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

flowchart TB subgraph Application A[TDatabase Object] end subgraph "Data Layer" B[cAlias Work Area] C[DBF File] D[CDX/NTX Index] E[FPT/DBT Memo] end subgraph "Optional" F[Replication Server
MySQL / MS SQL] G[SQL Query via oCn] end A --> B B --> C B --> D B --> E A -.-> F A -.-> G

Key DATA Members

DATATypeDefaultDescription
cAliasCharacter""Work area alias name (READONLY)
cFileCharacter""DBF file path (READONLY)
cDriverCharacterRddSetDefault()RDD driver name (e.g. "DBFCDX", "DBFNTX")
lReadOnlyLogical.F.Open in read-only mode
lSharedLogical!Set(_SET_EXCLUSIVE)Open in shared mode
lBufferLogical.F.Enable record buffering for optimistic locking
nAreaNumeric0Work area number (READONLY)
aStructArrayExtended field structure array (beyond standard 4-element)
hRecHashCurrent record as a hash (field name => value)
aBufferArrayRecord buffer for optimistic locking
nBufRecNoNumeric0Record number of the current buffer content
bBoFBlockCode block evaluated at beginning of file
bEoFBlockCode block evaluated at end of file
bNetErrorBlockCode block evaluated on network error
bTriggerBlockTrigger block evaluated on record changes
lADSLogical.F.True when using Advantage Database Server RDD
lHasMemosLogical.F.Whether the DBF has memo fields (READONLY)
oCnObjectConnection object for SQL queries (since FWH 13.03)
oReplServerObjectReplication 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.

MethodDescription
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

MethodDescription
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

MethodDescription
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

MethodDescription
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 / MethodDescription
lBufferSet to .T. to enable buffering. When on, field modifications are held in the buffer array rather than written directly to disk.
aBufferArray containing the buffered field values. The array is sized to match the field count.
nBufRecNoRecord 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

sequenceDiagram participant App as Application participant DB as TDatabase participant RDD as RDD Engine participant File as DBF File App->>DB: Open("customers.dbf") DB->>RDD: DbUseArea() RDD->>File: Open file handle RDD-->>DB: nArea, cAlias, aStruct App->>DB: SetOrder("NAME") DB->>RDD: OrdSetFocus("NAME") App->>DB: Seek("Smith") DB->>RDD: DbSeek("Smith") RDD->>File: Binary search in index RDD-->>DB: Found() = .T. App->>DB: FieldGet("PHONE") DB->>RDD: FieldGet(nPos) RDD-->>App: "555-1234" App->>DB: FieldPut("PHONE", "555-9999") DB->>RDD: RecLock + FieldPut + DbUnlock App->>DB: Close() DB->>RDD: DbCloseArea()

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 MethodTDatabase Equivalent
MoveFirst()GoTop()
MoveLast()GoBottom()
MoveNext()Skip(1)
MovePrevious()Skip(-1)
Move(n)Skip(n)

See Also