Catalog · How the catalog works
Firedac-BDE
FireDAC physical driver for the BDE: read and write legacy dBase (DBF) and Paradox tables with modern FireDAC code (Delphi, Win32).
- ★ 1 on GitHub
Anyone may press Interesting, signed in or not. It helps others find this project.
README
FireDAC-BDE
FireDAC driver for the BDE
A FireDAC physical driver (DriverID=BDE) that lets modern FireDAC code
read and write legacy dBase III/IV (.DBF) and Paradox 7+ (.DB)
tables through the Borland Database Engine (BDE).
You keep using TFDConnection, TFDQuery, TFDTable, transactions and
parameters as with any other FireDAC database; the driver takes care of
talking to the BDE underneath.
Unofficial, community project. Not affiliated with or endorsed by Embarcadero. Developed and tested with Delphi 13.2, Windows 32-bit only (the BDE is 32-bit). 🇮🇹 Leggi in italiano
Why
Migrating an application from BDE to FireDAC is easy; migrating the data usually isn't (shared DBF/Paradox files used by other legacy programs). This driver lets new FireDAC code work directly on the legacy files while the old programs keep running, with no hybrid infrastructure and no code that is specific to a database.
Features
- Connection through a BDE alias or a plain folder path (no alias needed).
- Metadata: table list, fields, indexes, primary key (
TFDMetaInfoQuery,GetTableNames,GetFieldNames, ...). TFDQuery/TFDTable/TFDCommandwith named parameters, fetch of all field types including memo and BLOB.- Updates through
Edit/Post,Append,Delete, and directUPDATE/INSERT/DELETE. - Transactions (
StartTransaction/Commit/Rollback) – tested with rollback on both DBF and Paradox. - Shared access with other applications, record-lock errors mapped to
ekRecordLocked, configurable lock wait (LockRetry). - DBF and Paradox files with the same base name coexisting in one folder.
- Design-time package (component
TFDPhysBDEDriverLink).
Requirements
- Delphi with FireDAC (developed on 13.2; other versions untested – reports welcome).
- A working BDE installation (
bdertlpackage, Borland Database Engine on the machine). The BDE is a legacy Embarcadero product; obtain it according to your own license. Nothing from the BDE or FireDAC sources is included here. - Build target: Windows 32-bit.
Installation
- Clone the repository (
git clone https://github.com/<your-user>/Firedac-BDE.git) and openFDBDE_D132.groupproj(target platform Win32). - Build
FDBDERun(runtime package, requiresbdertl). - Build and Install
FDBDEDesign(design-time package). - In your projects either add
FDBDERunto the runtime packages, or just addFireDAC.Phys.BDEDefandFireDAC.Phys.BDEto ausesclause: the driver registers itself in theinitializationsection.
Command line (Windows, Delphi 13): build.bat [Debug|Release] builds the packages and
BDETest; test.bat runs BDETest on a copy of the synthetic sample data;
install-ide.bat registers the design-time package in the IDE (close RAD Studio first);
clean.bat removes generated files. Other Delphi versions: set BDSVER=<n> or set RSVARS=<path to rsvars.bat>.
If the compiler cannot find FireDAC.inc, add $(BDS)\source\data\firedac to
the library/search path (do not copy that file into the repository, it belongs to Embarcadero).
Usage
FDConnection1.DriverName := 'BDE';
FDConnection1.Params.Database := 'C:\Data\Legacy'; // folder with .DBF/.DB, or a BDE alias
FDConnection1.Params.Values['LockRetry'] := '1'; // optional
FDConnection1.Open;
// Table names must carry the extension and be double-quoted in SQL
FDQuery1.Open('select * from "CUSTOMERS.DBF" where ID = :id', [42]);
// Updating a DBF needs a UNIQUE key field (DBF has no primary key)
FDQuery1.UpdateOptions.UpdateTableName := 'CUSTOMERS.DBF';
FDQuery1.UpdateOptions.KeyFields := 'ID';
FDQuery1.Edit;
FDQuery1.FieldByName('NAME').AsString := 'New name';
FDQuery1.Post;Connection parameters
| Parameter | Meaning |
|---|---|
DriverID |
BDE |
Database |
BDE alias, or folder containing the DBF/Paradox files |
BDEDriver |
BDE driver used with a folder path. Default STANDARD (DBF + Paradox) |
NetFileDir |
Folder of PDOXUSRS.NET for shared Paradox access (must be the same for all users) |
PrivateDir |
Temporary folder of the BDE session. Default: an automatic temp folder removed on disconnect |
LockRetry |
Seconds to wait on a locked record. 0 = don't wait. Not set = BDE default (about 6 s) |
TableCursor (see docs/TABLECURSOR.md) |
True = a plain select * from TAB / select f1, f2 from TAB (no WHERE, ORDER BY, join, parameters) is read through a BDE TTable instead of a TQuery: it opens at once even on very large tables (a TQuery copies the whole result on open). Also ... ORDER BY f1[, f2] (ascending), optionally with a WHERE on the same leading fields: equalities on the first fields and at most one range (>, >=, <, <=) on the next one, numeric fields only, joined by AND, value as a parameter or number (it becomes a SetRange on the TTable; a NULL or non-numeric parameter falls back to TQuery at open time) when the table has an index on exactly those leading fields (plain fields, not an expression, not descending, not case-insensitive). Default False. The data is a live cursor, and rows with equal keys may come in a different order than with TQuery; any other SELECT still uses TQuery. Because the TTable is used, a field that is a Local SQL reserved word (e.g. AUTOINC) also works here while the TQuery rejects it The optional events OnBDEIndexNeeded and OnBDESetRange let the program choose the index and the range (see the guide). |
Password |
Paradox table password (AddPassword) |
Things to know (limitations)
- Windows 32-bit only. The BDE does not exist for 64-bit.
- SQL dialect = BDE Local SQL: no
LIMIT/TOP,CASE,COALESCE,CURRENT_DATE,LENGTH, few functions, limited joins/subqueries;LIKEis case-sensitive; use parameters for dates. FireDAC does skip/limit on the client side. See docs/LOCAL_SQL.md. ForCASE,COALESCE,LIMIT, CTEs or joins across sources you can run FireDAC's own Local SQL (SQLite virtual tables) on top of the driver: docs/LOCAL_SQL_ON_BDE.md (experimental). - File extensions. A folder can hold
CLIENTS.DBandCLIENTS.DBF. Always use the extension and double quotes in SQL ("CLIENTS.DBF"); without it the BDE picks Paradox. - DBF has no primary key. Set
UpdateOptions.KeyFieldsto a field that is really unique, otherwise FireDAC reports "Exact update affected [N] rows". - Memo parameters. To write a memo with hand-written SQL, give the parameter a memo type
(
DataType := ftWideMemo; AsWideMemo := ...). A plain string parameter on a memo column raises no error on Paradox/dBase but writes nothing.Edit/Postis not affected. - Rollback of an empty memo (dBase III). Rolling back a transaction that gave a value to a memo that was previously empty can leave the BDE in an inconsistent state: the next query on that row fails with "BLOB already opened". Rows with an existing memo are not affected. Close and reopen the connection after such a rollback. (BDE behaviour, seen on dBase III; not seen on dBase IV.)
- Locks. Waiting time on a locked record is governed by the BDE; use
LockRetry. In the tests a row modified inside an open transaction stayed locked for other connections until commit/rollback, on both DBF and Paradox. - "Insufficient disk space" at random. A BDE bug (free space near a multiple of 4 GB), not a driver one.
Fix: put
Demos\FixBDE4GbBug.pasfirst in theusesof your program. See docs/BDE_4GB_BUG.md. - One BDE session per connection; the BDE is not designed for heavy multi-threading.
- Strings are exposed as Unicode; the conversion uses the table's language driver, so characters outside the table's code page may not survive a round trip.
- Memo text is decoded with the system ANSI code page. If an application stored UTF-8 inside a memo
(for example XML with a BOM), you get the UTF-8 bytes as ANSI characters (
<?xml ...). The conversion is lossless: re-decode in your code withTEncoding.UTF8.GetString(TEncoding.ANSI.GetBytes(S)). - Not implemented: DDL helpers (create/alter table through FireDAC), stored procedures, event alerts, array DML optimisation (arrays are executed row by row).
What has been tested
Real-world dBase IV (≈3,600 rows, 119 fields, memo) and Paradox tables, Delphi 13.2, with
Demos\BDETest:
| Area | DBF | Paradox |
|---|---|---|
| Connect, tables, fields, indexes | ✅ | ✅ |
Select, count, fetch, parameters, Locate |
✅ | ✅ |
| Update / insert / delete with rollback | ✅ | ✅ |
| Memo read/write (dataset and parameter) | ✅ | ✅ |
| Concurrent access from two applications | ✅ | ✅ |
| Record lock between two connections | ✅ | ✅ |
| Table password | – | not tested |
| BLOB / OLE / graphic fields | not tested | not tested |
| Other Delphi versions | not tested | not tested |
Trying it
python Tools/make_sample_dbf.py # creates Demos/Data/SAMPLE.DBF + .DBT (synthetic data)
Demos\BDETest.exe Demos\Data SAMPLE.DBF ID NAME NOTESBDETest <folder|alias> <TABLE.DB|DBF> [key field] [text field] [memo field] [row 1-5] [date field]
With a key, text and memo field the test also performs update, insert, delete and memo writes
inside a transaction that is rolled back, and a lock test with a second connection.
Run the write tests on a copy of your data.
BDETest takes an optional 6th parameter (row 1-5 of the sample used as key); row 3 of SAMPLE.DBF
has a memo, row 1 has none.
A Paradox sample can be created with Tools\MakeSampleParadox.dpr (untested).
Repository layout
Source/ driver units (BDEDef, BDEMeta, BDE) + design-time registration (BDEReg)
Packages/ Delphi packages (d132 = Delphi 13.2)
Demos/ BDETest console program + synthetic sample data, FixBDE4GbBug unit
Tools/ sample data generators
docs/ architecture notesSource comments and the test program messages are in Italian.
Contributing
Bug reports and test results on other Delphi versions or table types are very welcome – see CONTRIBUTING.md.
License
MIT. FireDAC, the BDE and Delphi are products of Embarcadero Technologies.

