TDatabase
Source: source/classes/database.prg
TDatabase is FiveWin's object-oriented wrapper around Harbour's DBF/RDD (Replaceable Database Driver) system. It encapsulates all common database operations -- opening, navigating, editing, filtering, indexing, and more -- into a clean class interface. TDatabase can work with any RDD supported by Harbour, including DBFCDX, DBFNTX, DBFNSX, and ADS (Advantage Database Server).
Architecture
MySQL / MS SQL] G[SQL Query via oCn] end A --> B B --> C B --> D B --> E A -.-> F A -.-> G
Key DATA Members
| DATA | Type | Default | Description |
|---|---|---|---|
cAlias | Character | "" | Work area alias name (READONLY) |
cFile | Character | "" | DBF file path (READONLY) |
cDriver | Character | RddSetDefault() | RDD driver name (e.g. "DBFCDX", "DBFNTX") |
lReadOnly | Logical | .F. | Open in read-only mode |
lShared | Logical | !Set(_SET_EXCLUSIVE) | Open in shared mode |
lBuffer | Logical | .F. | Enable record buffering for optimistic locking |
nArea | Numeric | 0 | Work area number (READONLY) |
aStruct | Array | Extended field structure array (beyond standard 4-element) | |
hRec | Hash | Current record as a hash (field name => value) | |
aBuffer | Array | Record buffer for optimistic locking | |
nBufRecNo | Numeric | 0 | Record number of the current buffer content |
bBoF | Block | Code block evaluated at beginning of file | |
bEoF | Block | Code block evaluated at end of file | |
bNetError | Block | Code block evaluated on network error | |
bTrigger | Block | Trigger block evaluated on record changes | |
lADS | Logical | .F. | True when using Advantage Database Server RDD |
lHasMemos | Logical | .F. | Whether the DBF has memo fields (READONLY) |
oCn | Object | Connection object for SQL queries (since FWH 13.03) | |
oReplServer | Object | Replication server (MySQL/MS SQL, since FWH 18.11) |
Constructors
New()
METHOD New( ncArea, cFile, cDriver, lShared, lReadOnly, cPassword )
Creates a TDatabase object. If ncArea is a number or alias of an already-open work area,
TDatabase wraps the existing area. If ncArea is a file path string, it prepares to open that file.
If only one parameter is passed, it auto-detects whether it is an area number, alias, or filename.
Open()
METHOD Open( cAlias, cFile, cDriver, lShared, lReadOnly, cPassword )
Opens a DBF file with explicit alias. This is the preferred constructor for opening new files.
Create()
METHOD Create( cFile, aStruct, cDriver, cIndexFields, cPassword )
Creates a new DBF file with the specified structure and optionally creates an index.
The aStruct parameter is an array of field definitions, where each element contains
{ cFieldName, cType, nLen, nDec }. The optional cIndexFields parameter
specifies a comma-separated list of field names for an automatic index. After creation,
the table is opened and the work area is ready for use.
Navigation Methods
| Method | Description |
|---|---|
GoTop() | Move to first record, reload buffer |
GoBottom() | Move to last record, reload buffer |
GoTo( nRecNo ) | Go to specific record number |
Skip( nRecords ) | Skip forward/backward by n records |
Skipper( nRecords ) | Skipper for XBrowse (internal use) |
Bof() | Returns .T. if at beginning of file |
Eof() | Returns .T. if at end of file |
RecNo() | Current record number |
RecCount() | Total number of records |
LastRec() | Last record number (same as RecCount) |
KeyNo() | Current key position in active index |
KeyCount() | Total keys in active index |
KeyGoTo( nKeyNo ) | Go to specific key position in index |
Seeking & Filtering
METHOD Seek( uExpr, lSoft, lWildSeek, lCurRec )
METHOD SetFilter( cFilter, aParams )
METHOD ClearFilter()
METHOD SetScope( uTop, uBot )
METHOD SetOrder( cnTag, cFile )
METHOD OrdDescend( cnTag, cFile, lDesc )
Seek() searches the active index for the specified key expression. lSoft enables
soft-seek (nearest match). SetFilter() accepts a filter expression string with optional parameter
substitution via aParams. SetScope() sets top and bottom scope values to restrict
navigation within an index range.
Editing Methods
| Method | Description |
|---|---|
FieldGet( cnCol ) | Get field value by name or position |
FieldPut( nField, uVal ) | Set field value by position |
FieldPos( cField ) | Get the ordinal position of a field by name |
Append( aFlds, aVals ) | Append blank record, optionally fill fields |
Blank() | Create a blank buffer (no physical append yet) |
Save() | Save buffered changes to disk |
SaveBuff() | Save buffer without re-reading |
Delete() | Mark current record as deleted |
Recall() | Undelete current record |
Update( aFlds, aVals ) | Update multiple fields at once |
Record( cFieldList, lBlank ) | Get a TDataRow object for the current record |
Load( lReload ) | Reload the hRec hash from disk |
RollBack() | Discard buffered changes |
Locking Methods
| Method | Description |
|---|---|
Lock() | File-level lock (FLock) |
RecLock( nRecNo, @lLocked ) | Record-level lock |
UnLock() | Release all locks |
IsRecLocked( nRecNo ) | Check if specific record is locked |
IsFileLocked() | Check if entire file is locked |
Indexing Methods
| Method | Description |
|---|---|
AddIndex( cFile, cTag ) | Add an existing index file to the order list |
CreateIndex( cFile, cTag, cKey, lUnique, lDescend, lMemory ) | Create a new index |
DeleteIndex( cTag, cFile ) | Delete an index tag |
CloseIndex() | Clear all indexes from the order list |
ReIndex() | Rebuild all indexes |
IndexKey( ncTag, cFile ) | Get index key expression |
IndexName( nTag, cFile ) | Get index tag name |
Buffering
TDatabase supports optional record buffering for optimistic concurrency control. When buffering
is enabled, changes are held in memory and written to disk only when Save() is called.
| DATA / Method | Description |
|---|---|
lBuffer | Set to .T. to enable buffering. When on, field modifications are held in the buffer array rather than written directly to disk. |
aBuffer | Array containing the buffered field values. The array is sized to match the field count. |
nBufRecNo | Record number corresponding to the content currently in aBuffer. Used to detect if the buffer is stale. |
Blank() | Create a blank buffer for appending a new record without writing to disk. Call Save() to commit. |
Load( lReload ) | Fill the buffer from the current record on disk. When lReload is .T., re-reads even if the buffer already contains data. |
Save() | Commit buffered changes to disk. For new records (created with Blank()), performs an append. For existing records, writes field values. |
SaveBuff() | Save the buffer to disk without re-reading the current record first. Used for performance when the record is known to be current. |
RollBack() | Discard buffered changes and restore the buffer to the original disk state. |
Editing with TDataRow
The Record() method provides a shortcut to create a TDataRow object for the current
record, enabling rich editing dialogs:
METHOD Record( cFieldList, lBlank ) -> oDataRow
Pass a comma-separated field list to limit the visible fields, or lBlank=.T. to
start with a blank record. The returned TDataRow can be used with Edit() to display
an interactive editing dialog:
oDb:GoTop()
oRow := oDb:Record()
if oRow:Edit( , , "Edit Customer" )
MsgInfo( "Changes saved" )
endif
See TDataRow for full documentation.
Browsing & XBrowse
METHOD Browse( cTitle, acFields )
METHOD SetXBrowse( oBrw, aCols, lAutoSort, lAutocols )
METHOD xBrowse( oWnd, aCols, lAutoSort, bSetUp )
xBrowse() is the quickest way to display database contents in an XBrowse grid. It automatically
configures the browse with column headers, data types, and sorting.
Data Flow
Example: Basic DBF Operations
#include "FiveWin.ch"
function Main()
local oDb, oWnd
// Open a DBF file with DBFCDX driver
DATABASE oDb FILE "customers.dbf" DRIVER "DBFCDX" SHARED
// Equivalent to:
// oDb := TDatabase():Open( "CUST", "customers.dbf", "DBFCDX", .T. )
// Navigate
oDb:GoTop()
? oDb:FieldGet( "FIRST" ) // "John"
? oDb:FieldGet( "LAST" ) // "Smith"
? oDb:hRec[ "FIRST" ] // "John" (hash access)
// Set an index order and seek
oDb:SetOrder( "LAST" )
if oDb:Seek( "Jones" )
? "Found:", oDb:FieldGet( "FIRST" ), oDb:FieldGet( "LAST" )
endif
// Edit a record
if oDb:RecLock()
oDb:FieldPut( "PHONE", "555-1234" )
oDb:UnLock()
endif
// Append a new record
oDb:Append()
oDb:FieldPut( "FIRST", "Jane" )
oDb:FieldPut( "LAST", "Doe" )
oDb:FieldPut( "PHONE", "555-5678" )
oDb:Save()
// Apply a filter
oDb:SetFilter( "STATE = 'CA'" )
oDb:GoTop()
do while ! oDb:Eof()
? oDb:FieldGet( "FIRST" ), oDb:FieldGet( "LAST" )
oDb:Skip()
enddo
oDb:ClearFilter()
oDb:Close()
return nil
Example: Browse with XBrowse
#include "FiveWin.ch"
function Main()
local oDb, oWnd, oBrw
DATABASE oDb FILE "customers.dbf" DRIVER "DBFCDX" SHARED
DEFINE WINDOW oWnd TITLE "Customer Browser"
oDb:xBrowse( oWnd,, .T., { |oBrw|
oBrw:cHeaders := { "First Name", "Last Name", "Phone", "State" }
oBrw:lRecordSelector := .F.
} )
ACTIVATE WINDOW oWnd MAXIMIZED
oDb:Close()
return nil
Example: Buffered Editing
#include "FiveWin.ch"
function Main()
local oDb
DATABASE oDb FILE "customers.dbf" DRIVER "DBFCDX" SHARED
// Enable buffering for optimistic locking
oDb:SetBuffer( .T. )
oDb:GoTop()
// Changes go to buffer, not disk
oDb:FieldPut( "PHONE", "555-0000" )
if MsgYesNo( "Save changes?" )
oDb:Save() // writes buffer to disk
else
oDb:RollBack() // discard changes
endif
oDb:Close()
return nil
Example: Creating Indexes
#include "FiveWin.ch"
function Main()
local oDb
DATABASE oDb FILE "customers.dbf" DRIVER "DBFCDX" SHARED
// Create a compound index with multiple tags
oDb:CreateIndex( "customers.cdx", "NAME", "UPPER(LAST)+UPPER(FIRST)" )
oDb:CreateIndex( "customers.cdx", "STATE", "STATE" )
oDb:CreateIndex( "customers.cdx", "PHONE", "PHONE", .T. ) // unique
// Use the NAME order
oDb:SetOrder( "NAME" )
oDb:GoTop()
// Scoped navigation: only records where STATE = "CA"
oDb:SetScope( "CA", "CA" )
oDb:GoTop()
do while ! oDb:Eof()
? oDb:FieldGet( "LAST" )
oDb:Skip()
enddo
oDb:Close()
return nil
RecordSet-Compatible Syntax
TDatabase provides ADO RecordSet-compatible method names for easier migration between local DBF and remote data:
| RecordSet Method | TDatabase Equivalent |
|---|---|
MoveFirst() | GoTop() |
MoveLast() | GoBottom() |
MoveNext() | Skip(1) |
MovePrevious() | Skip(-1) |
Move(n) | Skip(n) |