-1

I wanted to create a new mdb file containing tables based on the structure of previously existing tables. I knew that I can use newTable.FieldDefs.Add() to recreate the fields one by one in a loop. But since there is already an oldTable fully stocked with the correct FieldDefs, that seemed terribly inelegant. I was looking for a single-statement solution!

I had found that newTable.FieldDefs.Assign(oldTable.FieldDefs) would compile (and run) without error but it left newTable with zero defined fields. This caused me to erroneously conclude that I didn't understand that statement's function. (Later I found that it failed only when oldTable.open had not occurred, which could not happen when the database was not available, even though the FieldDefs had been made Persistent and were clearly visible in the Object Inspector)

Here is my original code after some sleuthing:

procedure TForm2.Button1Click(Sender: TObject);
var
  fname: string;
  Table: TFDTable;
  FDConn: TFDConnection;

begin
  fname := 'C:\ProgramData\mymdb.mdb';
  if FileExists(fname) then DeleteFile(fname);

  { Make new file for Table }
  FDMSAccessService1.Database := fname;
  FDMSAccessService1.DBVersion := avAccess2000;
  FDMSAccessService1.CreateDB;

  { Connect to new file }
  FDConn := TFDConnection.Create(nil);
  FDConn.Params.Database := fname;
  FDConn.Params.DriverID := 'MSAcc';
  FDConn.Connected := true;

  { Set up new Table using old table's structure }
  Table := TFDTable.Create(nil);
  try
    { ADOTable1 has been linked to an existing table in a prior
      database with Field Defs made Persistent using the Fields
      Editor in the Object Inspector. That database will not be
      available in my actual use scenario }
    try
      ADOTable1.open; // Throws exception when database file not found
    except
    end;
    Table.Connection := FDConn;
    { specify table name }
    Table.TableName := ADOTable1.TableName;
    Table.FieldDefs.Assign(ADOTable1.FieldDefs);  // No errors reported
    ShowMessageFmt('New Table %s has %d fields',[Table.TableName,
      Table.FieldDefs.Count]);
    { Reports correct TableName but "0 fields" with table not open
      (i.e. file not found). Reports "23 fields" with table open }
    { Set Table definition into new mdb file }
    Table.CreateTable(False); // Throws exception when 0 fields found
  finally
    Table.Free;
  end;

end;

It turned out that the solution was to use a ClientDataSet originally linked to the same old database instead of the ADOTable. See the working solution below in my answer.

Edit: A final note. I had hoped to use this FireDAC approach, as indicated here, to get around the lack of a TADOTable.CreateTable method. Alas, although the "solutions" above and below do work to create a new TADOTable, that table's field definitions are not faithful replicas of the original table. There may be a combination of the myriad TFDTable options that would get around this, but I was not able to discover it so I reverted to creating my ADO tables with SQL.

Terry Peterson
  • 121
  • 1
  • 8
  • I've voted to close this question as unclear until you decide what you want to ask. After taking time to answer your question based on what you've posted, you then added information (not yet in your post) about errors you had made before posting (which would have been clear if you'd included your code) and also thrown a TClientDataset into it (which is also not in your post). I've deleted my answer to your post as a result. I'll be happy to reconsider my close vote whwn you've made an [edit] to include a [mcve] that demonstrates the issue you're having and clarified what you're asking. – Ken White Apr 13 '19 at 03:37
  • Yikes, Ken. Please put it back. I think that others will greatly benefit from your answer. I know that I did. (See my answer below) If you want to "verify" the problem that I was having, just comment out the line in your answer that opens the source table. That was the key to the solution. – Terry Peterson Apr 13 '19 at 03:56
  • Please add to your q the **exact** code (including surrounding context) which you believe "was incorrectly executed without compiler or runtime errors because I failed to "open" the source table." and what was incorrect about it, that fact that it failed silently to do what you expected, or what? – MartynA Apr 13 '19 at 19:23
  • @MartyA, here ya go. I've revised the question to show the exact code that fails. Yes, I believe that the Assign statement should either (preferably) produce the same results with the table open or closed (as is the case with ClientDataSets) or (less preferably) it should throw a compiler error when there is no table.open statement and a runtime error when the table.open has failed. The current behavior is a bug. – Terry Peterson Apr 13 '19 at 22:38

1 Answers1

1

Thanks to @Ken White's (unfortunately deleted) pointer, I now think that I have a solution to my original question about cloning the field defs from an old table into a newly created database. My original problem stemmed from the fact that the FieldDefs function for a table evidently does not return the actual stored field data if the table is not "open" (i.e., connected to the relevant database). Since my use scenario would not have a valid database available I could not "open" the table. However, ClientDataSets have an additional option to "StoreDefs" along with editor options to "Fetch Params" and "Assign Local Data". With those settings saved, the ClientDataSet renders its FieldDefs properties without being "open". Using that approach it seems that I can clone the stored field defs to a new table without needing a currently valid database to read them from. Thanks again, Ken, you saved a lot of my remaining hair! I sure wish that Embarcadero would do a better job of rationalizing their help files. They removed BDE from the default installation of Rio while still pointing in their help file discussion on creating Access tables to its TTable type as the way to create new tables and then never point to the equivalent capabilities in FireDAC (or elsewhere) which they continue to support. I wasted a lot of time because of this "oversight"!

Here is my working code after Ken's tip:

procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
fname: string;
Table: TFDTable;
FDConn: TFDConnection;

begin
  fname := 'C:\ProgramData\mymdb.mdb';
  if FileExists(fname) then DeleteFile(fname);
  FDMSAccessService1.Database := fname;
  FDMSAccessService1.DBVersion := avAccess2000;
  FDMSAccessService1.CreateDB;

  FDConn := TFDConnection.Create(nil);
  FDConn.Params.Database := fname;
  FDConn.Params.DriverID := 'MSAcc';
  FDConn.Connected := true;

  Table := TFDTable.Create(nil);
  try
    Table.Connection := FDConn;
    { specify table name }
    Table.TableName := 'ATable';
    { The existingClientDataSet has been linked to a table in the
      prior, no longer valid, database using StoreDefs, Fetch Params,
      and Assign Local Data in the Object Inspector }
    Table.FieldDefs.Assign(existingClientDataSet.FieldDefs);
    ShowMessageFmt('New Table has %d fields', [Table.FieldDefs.Count]);
    Table.CreateTable(False);
  finally
    Table.Free;
  end;
Terry Peterson
  • 121
  • 1
  • 8
  • 1
    Fwiw, I don't think there was any oversight. The BDE was obsolete for over a decade before Emb bought in the library they re-badged as FireDAC. As a result, the FireDAC docs have always been "self-contained" so I can't imagine them having any incentive to re-visit the BDE docs to improve the FD ones. Also it hasn't been necessary to use the BDE to work with Access files at least as far back as D5. – MartynA Apr 13 '19 at 10:38
  • @MartynA I chose the word "oversight" so as not to ascribe any negative motive to the fact that Embarcadero's own Rio help page on [Creating and Deleting Tables](http://docwiki.embarcadero.com/RADStudio/Rio/en/Creating_and_Deleting_Tables) pointed the unfortunate reader (me) to the deprecated (and not even installed) BDE component TTable without ever mentioning the currently supported alternatives, including but perhaps not limited to FireDAC. I expressed no opinion on the obsolescence of BDE. – Terry Peterson Apr 13 '19 at 17:51