Known problems:
The TDBF code lacked a maintainer for a long while. Currently (October 2013), both the Sourceforge upstream project (focused on Delphi) and FPC database developers are looking into fixing TDBF bugs again. The fixes in FPC trunk will need to be merged with the SourceForge version, and the other way around.

Limitations

TDBF sets database encoding based on OS language settings (you can choose your own by setting LanguageID; see documentation). Moving a DB to another computer that does not support the encoding may lead to a read-only database. Note: investigate whether this applies to Tablelevel 7 (Visual DBase) and 25/30 (Foxpro/Visual Foxpro)

None of the DBF formats used support Unicode encoding (whether UTF-8, UTF-16 or UTF-32). There may be a workaround using binary fields, but then you lose sorting abilities etc.

Indexing a DBase file with [ixCaseInsensitive] currently does not work.

No support yet for referential integrity in file formats that support it (DBase VII, Visual FoxPro).

No support (now or ever) for internally encrypted .dbf files: the DbaseIV encryption mechanism is quite weak anyway. Please use one of the encryption methods mentioned below.

FPC 2.6.x and lower:

On ARM processors, the TDBF code is known not to work due to bus alignment issues. This is being addressed in the FPC development/trunk version.

Choosing the FoxPro tablelevel (25) will produce files that cannot be read by Visual Foxpro drivers. This has been fixed in the FPC development/trunk version.

No need for installation

The DbfLaz package is installed by default in both FPC and Lazarus. The DBFLaz package uses the TDbf and associated units in the Free Pascal Free Component Library. In other words, no need to install anything if you have a fairly recent version of Lazarus.

What the TDbf package provides

The TDbf package provides access to dBase and FoxPro database tables for Lazarus (and others). It allows for reading, writing and creation of dBase III+, dBase IV, Visual dBase VII and FoxPro tables. It does all of this without the need for additional libraries or database engines. Simply drop the TDbf on your form and you have instant access to a cross-platform database environment. The TDbf works in both Windows and Linux using Lazarus.

What to put in your uses section

If you use the visual DBF components in Lazarus, Lazarus will put Dbf in your uses clauses. If you use FPC (console mode) only, you can put it there yourself.
Handy other units are db for DataSet support and Dbf_Common for things such as field type definitions.

So you'd get:

uses Dbf, db, Dbf_Common;

How to create a new database table

There is a "Database Desktop" application for Lazarus (open the project in your Lazarus directory/tools/LazDataDesktop). You could create a new data dictionary, then create code to set up DBF files based on the dictionary. As of now (July 2011), this process is still a bit convoluted.

Another option is to use OpenOffice or LibreOffice Calc and save your spreadsheet as a .dbf file.

In any event, we shall illustrate creating a new database in code below.

Setting the path

It's a good idea to give your application's database its own directory. This simplifies making backups of the data.

There are two ways to set the path:

you can set the full path using the FilePathFull property

you can set a path relative to the current application path with FilePath.

For instance, setting "FilePath" at runtime to "data/" would use a data subdirectory just below the executable file. Setting the "FilePathFull" property to "/var/data/" would place all dbfs, index files etc in that exact folder, whatever the application location is.

An example:

MyDbf.FilePathFull:='/location/to/my/data';//Directory where all .dbf files will be stored
MyDbf.TableName:='customers.dbf';// note: is the .dbf really required?

Choosing a TableLevel

By default, the TDbf components will create dBase IV tables. While this is very compatible, there are features you may wish to use that are not supported. E.g. to support auto-incrementing fields, you must use something newer. The table types are:

You choose a table type by setting the TableLevel property appropriately:

MyDbf.TableLevel:=7;//Visual dBase 7

Adding fields

Creating fields for your new table at runtime pretty much follows the old Delphi standard. An example:

With MyDbf.FieldDefsdobegin
Add('Id', ftAutoInc,0,True);//Autoincrement field called Id
Add('Name', ftString,80,True);//80 character string field called NameEnd;

List of field types - bold types are currently supported by the DBF code. See Database_field_type for a explanation of field types:

ftUnknown

ftString

ftSmallInt

ftInteger

ftWord

ftBoolean

ftFloat

ftCurrency (only supported in TableLevel 25)

ftBCD (only supported in TableLevel 25)

ftDate

ftTime

ftDateTime: probably the recommended one for storing ftDate or ftDateTime values. At least on FoxPro, seems to store milliseconds as well.

ftBytes (only supported in TableLevel 25)

ftVarBytes

ftAutoInc (only supported in TableLevel 7 or 30)

ftBlob

ftMemo

ftGraphic

ftFmtMemo

ftParadoxOle

ftDBaseOle

ftTypedBinary

ftCursor

ftFixedChar

ftWideString

ftLargeInt

ftADT

ftArray

ftReference

ftDataSet

ftOraBlob

ftOraClob

ftVariant

ftInterface

ftIDispatch

ftGuid

ftTimeStamp

ftFMTBcd

Creating the table

Once you have defined the fields you wish to use in your new table, you can go ahead and create it with:

MyDbf.CreateTable;

How to add indexes to a table

If your database is larger than a few records, chances are you will want to have indexes defined to make searching faster. To change the index structure of a table, we need to have exclusive access to the table - which we would have while creating it anyway.

Expressions

Index/filter on strings

Strings needs to be surrounded/delimited by double quotes (") or single quotes (').

Note: December 2013: In all FPC versions except the current development version (trunk): using the delimited quotes in your filter string does not work (see the example below). See FPC New Features Trunk page: [[9]]

// We have a field called CUSTOMER// let's delimit with double quotes// (of course, double up single ' in string to make FPC happy)
MyDBF.Filter:='CUSTOMER = "Felipe''s Bank" ';// another tricky one, let's delimit with single quotes. Of course,// we'll need to double these up for FPC to interpret these correctly
MyDBF.Filter:='CUSTOMER = ''Jim "Mighty" McLanahan'' ';// If we have both a ' and a " in our string, we're in trouble.// the example below only works on FPC 2.7.1+
MyDBF.Filter:='CUSTOMER = "Felipe''s ""Mighty"" Bank" ';
MyDBF.Filtered:=true;

Index 100 character string limit

Index expressions that return a string may be maximum 100 characters long; use SUBSTR to cut off extra characters, e.g.:

Index/filter on TDateTimes

To set an index on a date/time field, use an expression index and the DTOS function:

MyDbf.AddIndex('PUBLISHDATEIDX','DTOS(PUBLISHDATE)',[]);

Filtering is similar - note: apparently you need to surround the string value you are comparing against with double quotes. See below for details on the DTOS function; basically you need to format the date as YYYYMMDD:

// we have a field named MODIFIED here:
MyDBF.Filter:='DTOS(MODIFIED) > "20051231"';// if you want to compare datetime, that can be done:// everything later than 2 January 2006, 4:34PM
MyDBF.Filter:='DTOS(MODIFIED) > "20060102 163400"';
MyDBF.Filtered:=true;

For example, if you want to filter a tdbf on a date field using a value selected from a DBlookupComboBox

True if the first argument is smaller than or equal to the second argument

>

True if the first argument is greater than the second argument

>=

True if the first argument is greater than or equal to the second argument

NOT

Negate the boolean argument to the right

AND

Returns true if both the first and second boolean argument are true

OR

Returs true if the first or second boolean argument is true

STR(num[,size,precision])

Converts a number num to a string with optional size and precision

num: number to convert to string
size: the total number of characters to output
precision: the number of digits to the right of the decimal point

DTOS(date)

Converts date to a string with representation YYYYMMDD

SUBSTR(str,index,count)

Extracts a substring from string str from position index and length count

UPPER(str)

Returns the uppercase equivalent of string str

LOWER(str)

Returns the lowercase equivalent of string str

Code example: creating a table/DBF file

The following sample creates a new table "customers" in code. This of course only needs done once, and after that you just open the table, instad of creating it. ;-)

($MODE OBJFPC}{$H+}Program DatabaseTest;{ We need the following units be in the USES clause: }uses Dbf, db, Dbf_Common, SysUtils;{ The Dbf is put there e.g. when you drop a TDbf component on a form... }{ but you will need db for the DataSet object and Dbf_Common }{ for things such as the field type definitions }{ Finally, use SysUtils for ForceDirectories. }var
MyDbf: TDbf;begin
MyDbf := TDbf.Create(nil);try{ make sure the data directory exists - otherwise this may fail (e.g. on Windows XP): }ForceDirectories('data');{ use relative path to "data" directory }
MyDbf.FilePath:='data'+ DirectorySeparator;{ we want to use Visual dBase VII compatible tables }
MyDbf.TableLevel:=7;
MyDbf.Exclusive:=True;
MyDbf.TableName:='customers.dbf';With MyDbf.FieldDefsdobegin
Add('Id', ftAutoInc,0,True);
Add('Name', ftString,80,True);End;
MyDbf.CreateTable;
MyDbf.Open;
MyDbf.AddIndex('custid','Id',[ixPrimary, ixUnique]);{ add a secondary index }
MyDbf.AddIndex('custname','Name',[ixCaseInsensitive]);
MyDbf.Close;finally
MyDbf.Free;end;end;

External Index Files

The TDbf component also supports storing secondary indexes in a separate file. This might be helpful if the database is expected to be very large. Secondary index files are created almost identically to normal indexes, but with the addition of the '.ndx' file extension:

How to link the TDbf component to data-aware components

The above examples show how to create a new database table in code. Using that table is even more simple.

Data aware components in Lazarus (such as the TDbEdit control) link to a TDataSource component using their "DataSource" and "DataField" properties. The TDataSource component handles communication between the database engine and the data aware components. A TDataSource then links to the TDbf component using it's "DataSet" property. The connection looks like this:

TDbEdit-------
|
TDbEdit------|-->TDataSource-->TDbf
|
TDbNavigator--

Be sure to set the FilePath (or FilePathFulll), TableLevel, and TableName properties of your TDbf component before calling

TDbf.Active:=True;

Master table relations

Real power in database programming begins when you have multiple tables that reference each other. While TDbf does not yet support full referential integrity (so what is missing?), it does support a master / detail relationship between TDbf's.

If you wanted to display all invoices for a given customer, the detail table (invoices) can stay in sync with the master table (customers) automatically.

On the invoices TDbf component set the following:

InvDbf.IndexName:='idxcustid';// our field that will match the customers table ID
InvDbf.MasterSource:= dsCustomers;// datasource that is linked to the customers TDbf
InvDbf.MasterFields:='Id';// field on the customers table we are matching against our index

Searching and Displaying a data set by using filters

In this example I wanted to search a database of books for all the titles an author has listed
and then display the list in a memo box

Dbf1.FilePathFull:='/home/somelocatio/database_location/';// path to the database directory
Dbf1.TableName:='books.dbase';// database file (including extension)
DbF1.Open;
memo1.Clear;// clear the memo box
Dbf1.FilterOptions:=[foCaseInsensitive];
Df1.Filter:='AUTHOR='+QuotedStr('anauthor');// AUTHOR is the field name containing the authors// Note that we need to surround the value with double quotes: "James Joyce"
Dbf1.Filtered:=true;// This selects the filtered set
Dbf1.First;// moves the the first filtered datawhilenot dbf1.EOFdo// prints the titles that match the author to the memo boxbegin
memo1.Append(Dbf1.FieldByName('TITLE').AsString);// TITLE is the field name for titles
dbf1.next;// use .next here NOT .findnext!end;
Dbf1.Close;

Note that you can use Ddf1.FindFirst to get the first record in the filtered set, then use
Dbf1.Next to move though the data. I found that using Dbf1.FindNext just causes the program to hang.

Filters

There are two ways to use filters:

use filter expressions

write an event handler for OnFilterRecord; if you want to show the record, set Accept to true.

In both cases, set the Filtered property to true to enable filtering.

Example: imagine you have a database that identifies the products by a code, for example, B019, and you need to filter only those products that are of this code so that a report is generated with LazReport.
Let's assume your DBF object is called TDBF1 and the field with product codes is called "product_no".

Packing and rebuilding the tables

When a record is deleted, it's not truly removed from the physical table. Periodically you must "pack" a table to recover that lost space. This should be done with exclusive mode set. See above for remarks on packing external index files.

Encryption

Apparently at least DBase IV and 5 support some kind of encryption (there is an encryption flag in the file structure), but it may well be that the password/encryption key is in the DBF itself (see [11]), making it insecure.