views:

424

answers:

6

I am familiar with Jeff Atwood's article about how errors are always the programmer's fault, but I believe I have really and truly found a bug in a Delphi .pas file.

Specifically, I'm using Delphi 2007, and the error is on line 955 of the DBCommon.pas file, which on my machine is located here:

C:\program files\codegear\rad studio\5.0\source\Win32\db\DBCommon.pas

And the code is this:

...
  FieldIndex := StrToInt(Token);
  if DataSet.FieldCount >= FieldIndex then
    LastField := DataSet.Fields[FieldIndex-1].FieldName else
...

If "Token" has a value of zero, then we try to access index -1 of DataSet.Fields, resulting in a list index out of bounds error.

This error is not raised to the user, because it is handled before it gets that high up, but it is enormously irritating to have the debugger break in every time this happens.

I could "Ignore this exception type" but Index out of bounds errors are common enough that I don't want to universally ignore them.

The situation that causes FieldIndex to be zero is when you have a SELECT statement whose ORDER BY contains a function, as in:

ORDER BY
  CASE WHEN FIELD1 = FIELD3 THEN 1 ELSE 2 END
 ,CASE WHEN FIELD2 = FIELD4 THEN 1 ELSE 2 END

I can fix the bug in DBCommon.pas, but Delphi will not recompile itself, and my change does not take effect. If I rename the .DCU file, then it just complains that "DBCommon.dcu" cannot be found.

So (finally) my question is: Can I recompile DBCommon.pas with my fix, and if so, how?

+9  A: 

You can probably put dbcommon.pas in youre project directory. It will then be compiled along with the rest of the project.

Tom
Note that this won't work unless all other units which are based on dbcommon.pas are copied to that directory as well, or the unmodified VCL sources are on the source path.
mghie
It should work. The interface version of the unit shouldn't change, so any units that use it should accept the recompiled version.
Rob Kennedy
@Rob: You are right, I tried and it does indeed work. I remember problems while trying to change strings in the implementation section of a Third Party library, so what was different there? Is there any documentation on what does and does not change the interface version?
mghie
This will not work if runtime packages are used in the application.
Lars Truijens
Mghie, I have no idea what might have changed. Barry Kelly summarized the rules last month (http://stackoverflow.com/questions/1482311/how-to-patch-a-method-in-classes-pas), but it's always been very fragile, in my experience, so I go to great lengths to avoid recompiling anything. That's why my source and DCU folders are always separate, and it's also the motivation behind my answer here. We've talked about patching before: http://stackoverflow.com/questions/469237/replace-function-units
Rob Kennedy
+1  A: 

We have a folder under our project source tree called VCL, into which we place copies of the VCL source that we wish to modify slightly. Your example is a good candidate for doing the same thing. You will need to modify the search path for your project so that "your" VCL folder is earlier on your path than the "Source" folders under your Delphi installation. You may also find that if you copy one VCL source unit out and modify it, you will have to also copy other VCL source units out into "your" folder which may be dependencies.

Our reason for doing this is that we want our builds to have zero compiler hints and warnings. There are some parts of the VCL source that are not hint/warning free.

Conor Boyd
+1  A: 

Simply - Yes. Using one of the above answers [By Tom or Connor]. Copy DBCommon.pas to your project folder rather than edit the original. This leaves other projects and compilations unaffected because it won't be on the path.

Despatcher
As you might have noticed, the default order Stack Overflow uses for displaying answers is to sort by vote count. Please be more specific when referring to other answers. "The above answers" doesn't really tell us which answers you're really talking about. You can link directly to them by copying the "link" address below each answer.
Rob Kennedy
Sorry, of course - there were only 2 saying basically the same thing when I replied :) So for clarity I will edit the answer.
Despatcher
+1  A: 

See previous answers for how to create the situation where you can recompile modified VCL source. I would however add that you seriously consider managing your changes in your change control system, using the "Vendor Branch" SCM pattern.

In simple terms (using SVN as a reference):

  • Create a "vendor source" copy of the original vendor supplied files. This is your "pristine" reference copy.

  • Create a branch representing that pristine copy (e.g. "2009" for the Delphi 2009 version of the VCL)

  • create a further branch into a separate "vendor library" folder. THIS is the copy of the library that you should reference in your projects

  • any modifications to the vendor source are made in the "vendor library" branch.

  • when the vendor provides a new version of the library you check the new version in to the "vendor source" project and create a new branch for the new version.

you can then easily diff the vendor revisions. But more importantly (with SubVersion, and possibly othe SCM systems) you should also be able to simply merge (i.e. automatically) the new vendor source with your "vendor library" branch to easily incorporate vendor changes with your own modifications.

This is all described far better than I just did in the excellent O'Reilly book: "Version Control with SubVersion"

NOTE however that the "loaddirs" utility mentioned in that book is no longer supported due to copyright issues, so updating "vendor drops" is currently a manual exercise, but this occurs only infrequently and is not a major burden.

We are using this pattern ourselves, although in the case of the VCL we do not maintain a complete copy of the entire VCL source tree in our "vendor source" or "vendor library", but instead only track changed and dependent units. For other libraries managed under a vendor branch we typically do maintain complete copies but decided this wasn't necessary for the VCL.

We've only just implemented this pattern however, so we may yet decide that we need to take a more comprehensive approach with the VCL too.

ymmv

Deltics
+3  A: 

You can, but often you don't have to. Recompiling a VCL unit sometime means recompiling all the rest of the VCL units either because you've changed the interface of a unit or because the compiler gets confused and thinks you've changed the interface. Recompiling a VCL unit also rules out the possibility of using most run-time packages because you can't recompile Delphi's run-time packages.

Instead of recompiling, you can use run-time patching. I've used the method in the TNT Unicode controls, but Madshi also provides a way to replace a function with your own implementation. If you copy the implementation of DBCommon.GetIndexForOrderBy into your own unit and make your fixes, you can use this command to patch the VCL version with your own:

var
  Old_GetIndexForOrderBy: Pointer;

HookCode(@DBCommon.GetIndexForOrderBy,
         @Fixed_GetIndexForOrderBy,
         Old_GetIndexForOrderBy,
         0);

With the Tnt Unicode library, find the OverwriteProcedure routine in the TntSystem unit. It's not public, so you'll need to either declare it in the unit interface or copy it into your own unit. Then you can call it much like the Madshi code above:

var
  Old_GetIndexForOrderBy_Data: TOverwrittenData;

OverwriteProcedure(@DBCommon.GetIndexForOrderBy,
                   @Fixed_GetIndexForOrderBy,
                   @Old_GetIndexForOrderBy_Data);
Rob Kennedy
A: 

"I am familiar with Jeff Atwood's article about how errors are always the programmer's fault, but I believe I have really and truly found a bug in a Delphi .pas file"

Are you joking me? With Delphi you always blame Borland first :) Something is weird, go Google it and see if it is a Delphi bug. Only if cannot find any similar reports you site down and check your code line by line.

After I reinstall Delphi I have to patch the original PAS files in 6 (six) places. There are tons of bugs that appear on a fresh Delphi installation and can be easily reproduced. Delphi (the one that we all love) is full of bugs. There is an entire history created around this. There are so many people releasing external patches (such as http://andy.jgknet.de/blog/?page_id=288) and Borland/Imprise/GoGear/Embarcadero keep ignoring them. It is a true wonder they included FastMM in their release.

Anyway, I have recompiled those PAS files and now I replace the original DCUs with the patched ones.

Altar