BUG: BDE Paradox driver - cannot create 0-size Blob field

Bug in        : BDE Paradox driver
Symptom       : Cannot create tables with zero-size Blob fields.
BDE versions  : BDE32 3.12, BDE32 3.5, BDE32 4.0 (beta), BDE16 2.51
Delphi ver.   : Delphi 1.0x, Delphi 2.0x
Found by      : Hallvard Vassbotn, hallv...@falcon.no
Found date    : 12.03.97

In the work of converting an update program from 16-bit protected mode
Paradox Engine for DOS to 32-bit BDE, I discovered a bug in the BDE
Paradox driver. It seems that because of a bug in the implementation
of the DbiTranslateRecordStructure function, you cannot create a
Paradox table with a Blob field of size 0 using the VCL TTable
component.

This is a major problem because I have to work with existing
applications that expect the table to have this specific format. I
mananged to come up with a workaround by modifying the implementation
of the TTable.CreateTable method.

The 16-bit version of BDE also has this same problem, but the TableType
property of TTable must be manually set to ttParadox for it to appear.
It seems to be another bug that causes GetDriverTypeName to return nil
instead of 'PARADOX'. This makes DbiTranslateRecordStructure to simply
copy
the logical record onto the physical record instead of actually
converting
it.

Re-creating the bug:

Create a new application. Drop a button and a memo on the
mainform. Add this code to the Click event of the button:

procedure TForm1.Button1Click(Sender: TObject);
var
  Table: TTable;
begin
  Table := TTable.Create(Self);
  try
    with Table do
    begin
      Table.DatabaseName := 'C:\';
      Table.TableType := ttParadox;
      Table.TableName := 'TestIt.DB';
      Table.FieldDefs.Add('Blob', ftBlob, 0, false);
      Table.CreateTable;
      Table.Open;
      Memo1.Lines.Add(Format('Blob.Size = %d',
[Table.FieldByName('Blob').Size]));
    end;
  finally
    Table.Free;
  end;
end;

Now run the application and press the button. You will
see in the memo that the size of the Blob field in the
table that was created is 1, not 0 as we intended.

This can also be verified by using the Database Explorer and see
that the size and physical size for the blob field is 1 and 11,
respectively (not 0 and 10 as we intended).

Workaround for the bug:

For 16-bit BDE:
You can get around the bug by relying on another bug... Simply
make sure TableType is ttDefault (the default). Then use '.DB'
as the extension of the TableName property to signal that this
should be a Paradox database. To solve the problem for all cases,
follow the guidelines below.

For 32-bit BDE:
The only way I have found to fix this (other than calling
the DBI-functions directly) is to modify the implementation
of the TTable.CreateTable method found in SOURCE\VCL directory:

At line 1790, insert the following code (line 1338 for Delphi 1.0):

      Check(DbiTranslateRecordStructure(nil, iFldCount, FieldDescs,
        GetDriverTypeName(DriverTypeName), PSQLLName, pFLDDesc, False));

{$DEFINE Fix_Paradox_ZeroSizeBlob_Bug}
{$IFDEF Fix_Paradox_ZeroSizeBlob_Bug}
    { Correct a bug in the Paradox driver.
      This enables us to have blob fields that use
      zero extra bytes in the record structure. }
    if StrComp(DriverTypeName, 'PARADOX') = 0 then
      for I := 0 to iFldCount-1 do
        with PFieldDescList(FieldDescs)^[I] do
          if (iFldType = fldBLOB) and (iUnits1 = 0) then
            with PFieldDescList(TableDesc.pFLDDesc)^[I] do
            begin
              { The Paradox driver has a bug in that it sets
                all Blob fields of size 0 to size 1 }
              Dec(iLen, iUnits1);
              iUnits1 := 0;
            end;
{$ENDIF}

      iIdxCount := IndexDefs.Count;

The first and the last statements are taken from the original
source so that you know where to insert the code.

This is a quick-and-dirty fix around a problem in the BDE
Paradox driver, but it does work. The code first check to
see if the we are in fact using the Paradox driver. Then
it loops through all the logical fields in the FieldDescs
array. If it finds a Blob field that has a logical size
of 0, it changes the correspondig physical field in the
TableDesc.pFLDDesc array so that the iLen and iUnits1
fields are set correctly.

If you do not want to modify the VCL source, you can create your
own TTable component that implements this corrected CreateTable.
Note that this would be a static replacement rather than a virtual
override. This means that any code that calls the old TTable.CreateTable
(like TBatchMove.Execute) would still call the old code.

--
Hallvard Vassbotn