devxlogo

How do I get the field structures from a table, then load them into a list?

How do I get the field structures from a table, then load them into a list?

Question:
How do I get the field structures from a table, then load them intoa list of some sort?

Answer:

Accessing and Using the TFieldType

While this isn’t the hardest thing to do because you can get allthe field information you need from the TTable component’s defaultproperties, there’s more to it than meets the eye.

This is becauseit’s not intuitively obvious how to communicate field types in ameaningful fashion because the field types are defined as elements inan enumerated type called TFieldType. Enumerated types aremerely ordinal values, so they’re meaningless when you convert them tostrings. For example, a String type field is defined in theTFieldType enumerated type as ftString and has anordinal value of 1.

It’s easy to create an array of type Stringwhose indexes are the same as the ordinal positions of the TFieldTypeelements and whose associated element values hold the text describingthe field type. But, there is one bigproblem if you want to write a generic utility to list fieldstructures: With the arrival of Delphi 2.0 came the support for moredata types, so the TFieldType has more elements. To remedythis situation, you have two options:

  1. Write two procedures: one for Delphi 1.0 and the other forDelphi 2.0, writing the appropriate string arrays to handle bothtypes.
  2. Write just one procedure, but add conditional compiler directivesto conditionally compile the array definition source code based onwhether Delphi 2.0 is the compiler.

There are a few good reasons not to use the first option. To begin with, there is the issue of code redundancy. How you extract fielddefinitions from a TTable is exactly the same in Delphi 2.0 as inDelphi 1.0. The only differences that could be present in the codeare the array definitions, so your procedures would pretty muchbe equivalent.

The second reason is not as clear, but just asimportant. When you set up the index range in the array, byconvention you would use the minimum and maximum ordinal values fromthe TFieldType enumerated type list. For example, for 16-bit code,you’d make your declaration as follows const FieldTypeNames:array[ftUnknown..ftGraphic] of string = . However, since the 32-bit version of Delphi hasmore field types, the declaration would read constFieldTypeNames: array[ftUnknown..ftTypedBinary] of string = .

You could argue that we should just use the32-bit declaration even in 16-bit, or just use discreet numbers inthe range definition of the array, and access the appropriateelement when you need it. That won’t work either, and I’ll showyou why. Look at the TFieldType definitions below for 16-bit and32-bit Delphi.

{16-bit TFieldType definition}TFieldType = (ftUnknown, ftString,   ftSmallint, ftInteger,               ftWord,    ftBoolean,  ftFloat,    ftCurrency,               ftBCD,     ftDate,     ftTime,     ftDateTime,               ftBytes,   ftVarBytes, ftBlob,     ftMemo,               ftGraphic);{32-bit TFieldType definition}TFieldType = (ftUnknown, ftString,   ftSmallint, ftInteger,               ftWord,    ftBoolean,  ftFloat,    ftCurrency,               ftBCD,     ftDate,     ftTime,     ftDateTime,               ftBytes,   ftVarBytes, ftAutoInc,  ftBlob,               ftMemo,    ftGraphic,  ftFmtMemo,  ftParadoxOle,               ftDBaseOle, ftTypedBinary);

Notice that I set up the definitions above in sort of a tabularfashion. The idea of just defining the range values of our array withdiscrete numbers is a good one. But if you compare the fourth linesof both definitions, you’ll see that in the 32-bit definition,ftAutoInc was inserted right afterftVarBytes. That makesusing a single array definition impossible. We could write thesecond procedure with no problem, but if we want to follow conventionin our declaration, we’d have to put the second procedure intoanother file. Otherwise, the 16-bit compiler will produce an errorwhen it reaches ftTypedBinary.In fact, if you haveprocedures built specifically for 32-bit Delphi, you may just have todo that (especially if you include the procedure toextract a table’s field structure in a general purpose utility unit.)

The better way to approach this is to use conditional compilerdirectives to compile the correct code depending upon the version ofDelphi you’re using.

Conditional Compiler Directives

The Delphi help file defines a conditional directive as thefollowing: “Conditional directives control compilation of partsof the source text, based on evaluation of a symbol following thedirective. You can define your own symbols or you can use the ObjectPascal predefined symbols.” I won’t go into detail about themhere because I could probably write another entire article on usingthem in your code. Basically, what happens with conditionaldirectives is this: When the compiler sees a conditional directivestatement, it evaluates the conditional logic, which is defined by aconditional symbol. If the logic is true, it compiles one piece ofcode; if it’s false, it compiles another piece of code. For ourpurposes, we’ll be using the predefined conditional symbolWin32, which indicates whether the system is runningunder Win32.

This ties in nicely with the string arrays we’ll be using toassociate with the TFieldTypes. Let’s take a look at them now:

  {16-bit array constant}  const FieldTypeNames: array[ftUnknown..ftGraphic] of string =      (‘Unknown’, ‘String’,   ‘Smallint’, ‘Integer’,       ‘Word’,    ‘Boolean’,  ‘Float’,    ‘Currency’,       ‘BCD’,     ‘Date’,     ‘Time’,     ‘DateTime’,        ‘Bytes’,   ‘VarBytes’, ‘Blob’,     ‘Memo’,        ‘Graphic’);  {32-bit array constant}  const FieldTypeNames: array[ftUnknown..ftTypedBinary] of string =      (‘Unknown’, ‘String’,   ‘Smallint’, ‘Integer’,       ‘Word’,    ‘Boolean’,  ‘Float’,    ‘Currency’,       ‘BCD’,     ‘Date’,     ‘Time’,     ‘DateTime’,        ‘Bytes’,   ‘VarBytes’, ‘AutoInc’,  ‘Blob’,        ‘Memo’,    ‘Graphic’,  ‘FmtMemo’,  ‘ParadoxOle’,        ‘DBaseOle’, ‘TypedBinary’);

Nothing strange in these declarations, though I did make themconstants. If we want to conditionallycompile them, we’d write out the following declaration:

{$IFNDEF Win32}  {16-bit array constant}  const FieldTypeNames: array[ftUnknown..ftGraphic] of string =      (‘Unknown’, ‘String’,   ‘Smallint’, ‘Integer’,       ‘Word’,    ‘Boolean’,  ‘Float’,    ‘Currency’,       ‘BCD’,     ‘Date’,     ‘Time’,     ‘DateTime’,        ‘Bytes’,   ‘VarBytes’, ‘Blob’,     ‘Memo’,        ‘Graphic’);{$ELSE}  {32-bit array constant}  const FieldTypeNames: array[ftUnknown..ftTypedBinary] of string =      (‘Unknown’, ‘String’,   ‘Smallint’, ‘Integer’,       ‘Word’,    ‘Boolean’,  ‘Float’,    ‘Currency’,       ‘BCD’,     ‘Date’,     ‘Time’,     ‘DateTime’,        ‘Bytes’,   ‘VarBytes’, ‘AutoInc’,  ‘Blob’,        ‘Memo’,    ‘Graphic’,  ‘FmtMemo’,  ‘ParadoxOle’,        ‘DBaseOle’, ‘TypedBinary’);{$ENDIF}

The {$IFNDEF Win32} conditional directive asks this:”Is Win32 not defined?” If it’s not defined, the 16-bitarray constant is compiled. Otherwise, the 32-bit array constant iscompiled. That’s pretty straightforward, and allows us to keep asingle procedure in one file. And now that we’ve established amethodology for including both 16- and 32-bit versions of our arrayin a single procedure, let’s jump into the original subject of thisarticle: getting the field structure from a table.

Getting a Table’s Field Structure

As I statedabove, translating the field types into a recognizable form isn’tintuitively obvious. Besides, getting table structure is really easy, just a matter of perusing through a TTable’s Fieldsproperty to get the field information you need.

For our discussion,all we’ll get are the field names, types, sizes and list identity ofthe key fields. If you want to extend the functionality of theprocedure below, look in the help file for a listing of all theTField’s properties. Let’s look at the code:

{=============================================================================== Procedure that extracts a table’s structure and loads it into a list. Works in either 16-bit or 32-bit Delphi. ===============================================================================}procedure GetTableStruct(dbName, tblName : String; const FldList : TStrings);{Conditional compiler directive. If Win32 is not defined as it is inDelphi 2.0,compile the 16-bit constant. Otherwise, compile the 32-bitarray constant.}{$IFNDEF Win32}  {16-bit array constant}  const FieldTypeNames: array[ftUnknown..ftGraphic] of string =      (‘Unknown’, ‘String’, ‘Smallint’, ‘Integer’,       ‘Word’, ‘Boolean’, ‘Float’, ‘Currency’,       ‘BCD’, ‘Date’, ‘Time’, ‘DateTime’, ‘Bytes’,       ‘VarBytes’, ‘Blob’, ‘Memo’, ‘Graphic’);{$ELSE}  {32-bit array constant}  const FieldTypeNames: array[ftUnknown..ftTypedBinary] of string =      (‘Unknown’, ‘String’, ‘Smallint’, ‘Integer’,      ‘Word’, ‘Boolean’, ‘Float’, ‘Currency’,      ‘BCD’, ‘Date’, ‘Time’, ‘DateTime’, ‘Bytes’,      ‘VarBytes’, ‘AutoInc’, ‘Blob’, ‘Memo’, ‘Graphic’,      ‘FmtMemo’, ‘ParadoxOle’, ‘DBaseOle’, ‘TypedBinary’);{$ENDIF}var  I, Idx: Integer;  FldDef: String;  Tbl   : TTable;begin  {Initialize vars}  FldDef := ”;  Tbl := TTable.Create(Application);  {Now search through the field defs.}  with tbl do begin    Active        := False;    DatabaseName  := dbName;    TableName     := tblName;    Open;    for I := 0 to FieldCount – 1 do begin        {Get the properties we want to use for the listing}      FldDef := Fields[i].DisplayLabel + ‘ ‘ +                 FieldTypeNames[Fields[I].DataType] + ‘ ‘ +                 IntToStr(Fields[I].Size);        {Now, get the index definitions for the table to determine if a        field is a primary key or not.}      IndexDefs.Update;      if Fields[I].IsIndexField then begin        Idx := IndexDefs.Indexof(Fields[I].Name);        if (Idx > -1) then          if ixPrimary in IndexDefs[Idx].Options then            FldDef := FldDef + ‘ *’;      end;      FldList.Add(FldDef);    end;    Free;  end;end;

See anything familiar? It’s the conditional compiler directivestatement that we took a lot of time discussing above! As you cansee, what gets compiled is based on the Win32 conditional symbol.

Sowhat else is going in the code after the conditional directivestatement? The basic functionality of this procedure is to extract atable’s structure, then write it to a list. With this, I wanted to beable to write to any type of TStrings type list so that I couldwrite to a TMemo, TListBox, another TStrings or even a TComboBoxcomponent. The idea was to able to place the field definitions whereI felt they’d be appropriate.

Essentially what’s happening in thecode is that we step through the TTable’s Fields property and use theFldDef String var as a temporary holding buffer to load the variousTField property values we want to include. After that we seeif the field is a primary key field; if it is, we append a ‘*’ tothe end of FldDef, which is then written into the list using the Addmethod. I told you it was easy — that was the easy part.

Conclusion

We covered a lot of ground with this tip. Actually, I wasjust going to include two procedures and basically say: Use thisprocedure in Delphi 1.0, and use this procedure in Delphi 2.0.But I would have been supporting a practice that I really abhor: code redundancy. So I decided to introduce compiler directives as away of using your code in either version of Delphi. It may make someprocedures notationally longer, but think of the time you’ll save nothaving to keep two libraries up to date.

See also  Professionalism Starts in Your Inbox: Keys to Presenting Your Best Self in Email
devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist