Packing a Paradox or dBase Table

How do I pack a Paradox (or dBase) table to optimize its indexes and space used?

Wouldn't it be great for the TTable component to have a method that does this? Just open up a TTable, connected it to a table on disk, call the method, and wham! The table's packed (Hmm.... I just might have to look into that). But short of that, you have to make direct calls to the BDE to accomplish this task. For dBase tables, it's easy. There's a single function called dbiPackTable that'll pack any dBase file. For Paradox files, you have to jump through a couple of hoops first, then call dbiDoRestructure because Paradox tables can only be packed within the context of a table restructure (Note: If you've restructured a table in Paradox or the Database Desktop, you'll notice a checkbox at the bottom of the restructure dialog called "Pack Table").

Below is a simple procedure for packing tables. I took most of the code right out of the online help (yes, there's really good stuff in there if you know where look), and made some alterations. The difference between what I did and what the help file lists is that instead of the formal parameter being a TTable, I require a fully qualified file name (path/name). This allows for greater flexibility - the procedure opens up its own TTable and works on it instead you having to create one yourself. I guess it might all boil down to semantics, but I still like my way better (so there!). Check out the code below:

procedure PackTable(TblName : String);
  tbl     : TTable;
  cProps  : CURProps;
  hDb     : hDBIDb;
  TblDesc : CRTblDesc;

  tbl := TTable.Create(nil);
  with tbl do begin
    Active := False;
    DatabaseName := ExtractFilePath(TblName);
    TableName := ExtractFileName(TblName);
    Exclusive := True;

  // Added 23/7/2000 to make sure that the current path is the same as the table
  //see note below
  // Make sure the table is open exclusively so we can get the db handle...
  if not tbl.Active then
    raise EDatabaseError.Create('Table must be opened to pack');

  if not tbl.Exclusive then
    raise EDatabaseError.Create('Table must be opened exclusively to pack');

  // Get the table properties to determine table type...
  Check(DbiGetCursorProps(tbl.Handle, cProps));

  // If the table is a Paradox table, you must call DbiDoRestructure...
  if (cProps.szTableType = szPARADOX) then
      // Blank out the structure...
      FillChar(TblDesc, sizeof(TblDesc), 0);
      // Get the database handle from the table's cursor handle...
      Check(DbiGetObjFromObj(hDBIObj(tbl.Handle), objDATABASE, hDBIObj(hDb)));
      // Put the table name in the table descriptor...
      StrPCopy(TblDesc.szTblName, tbl.TableName);
      // Put the table type in the table descriptor...
      StrPCopy(TblDesc.szTblType, cProps.szTableType);
      // Set the Pack option in the table descriptor to TRUE...
      TblDesc.bPack := True;
      // Close the table so the restructure can complete...
      // Call DbiDoRestructure...
      Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
    // If the table is a dBASE table, simply call DbiPackTable...
    if (cProps.szTableType = szDBASE) then
      Check(DbiPackTable(tbl.DBHandle, tbl.Handle, nil, szDBASE, True))
      // Pack only works on Paradox or dBASE; nothing else...
      raise EDatabaseError.Create('You can only pack Paradox or dBase tables!');

  with tbl do begin
    if Active then

See? Nothing fancy. What you should know is that all operations involving dbiDoRestructure revolve around a table descriptor type CRTblDesc. With this record type, you can set various field values, then execute dbiDoRestructure to make your changes. That's kind of the trick to making BDE calls in general. You typically work with some sort of record structure, then use that structure in one of the calls. I know I'm probably oversimplifying, but that's it in a nutshell. The point? Don't be scared of the BDE. More later!

I encourage you to look at the BDE online help under any dbi- subject. There are lots of code examples that will get you on your way.

NOTE:  I received an email from Stewart Nightingale who said "When running PackTable straight after saving something to floppy it became obvious that packing a table must use the current directory for temporary tables and things - the current directory was "a:" and it came up with an error saying "No Disk in Drive a:". I put in the line "SetCurrentDir(ExtractFilePath(TblName));" at the top of the procedure so it would work properly ."  I have added this line to the code sample shown above - use it if you wish, leave it out if you do not...........