diff --git a/hbrdd/dbf/dbf.go b/hbrdd/dbf/dbf.go index 4f9a98a..01b3ebf 100644 --- a/hbrdd/dbf/dbf.go +++ b/hbrdd/dbf/dbf.go @@ -42,8 +42,9 @@ type DBFArea struct { dirty bool // record buffer modified // State - recCount uint32 - ghost bool // at phantom record (after APPEND) + recCount uint32 + ghost bool // at phantom record (after APPEND) + recLoaded bool // false = recBuf stale, need loadRecord() // Memo file (FPT) memoFile *FPTFile @@ -358,6 +359,7 @@ func (a *DBFArea) RecCount() (uint32, error) { } func (a *DBFArea) Deleted() bool { + a.loadRecord() if len(a.recBuf) > 0 { return a.recBuf[0] == RecordDeleted } @@ -365,7 +367,7 @@ func (a *DBFArea) Deleted() bool { } // GoTo positions the cursor at a specific record number. -// Harbour: hb_dbfGoTo in dbf1.c +// Harbour: hb_dbfGoTo in dbf1.c — lazy read: record loaded on first access. func (a *DBFArea) GoTo(recNo uint32) error { if a.dirty { a.flushRecord() @@ -374,11 +376,10 @@ func (a *DBFArea) GoTo(recNo uint32) error { a.FFound = false if recNo == 0 || recNo > a.recCount { - // EOF / phantom record a.recNo = a.recCount + 1 a.FEof = true a.FBof = (recNo == 0) - // Clear buffer + a.recLoaded = false for i := range a.recBuf { a.recBuf[i] = ' ' } @@ -387,12 +388,10 @@ func (a *DBFArea) GoTo(recNo uint32) error { // Read record from file offset := a.header.RecordOffset(recNo) - _, err := a.dataFile.ReadAt(a.recBuf, offset) - if err != nil { - return fmt.Errorf("read record %d: %w", recNo, err) - } + a.dataFile.ReadAt(a.recBuf, offset) a.recNo = recNo + a.recLoaded = true a.FEof = false a.FBof = false a.dirty = false @@ -508,9 +507,20 @@ func (a *DBFArea) Skip(count int64) error { return nil } +// loadRecord reads the current record from disk if not already loaded. +func (a *DBFArea) loadRecord() { + if a.recLoaded || a.FEof || a.recNo == 0 || a.recNo > a.recCount { + return + } + offset := a.header.RecordOffset(a.recNo) + a.dataFile.ReadAt(a.recBuf, offset) + a.recLoaded = true +} + // --- Field access --- func (a *DBFArea) GetValue(fieldIndex int) (hbrt.Value, error) { + a.loadRecord() if fieldIndex < 0 || fieldIndex >= len(a.fieldDescs) { return hbrt.MakeNil(), fmt.Errorf("field index out of range: %d", fieldIndex) } @@ -535,6 +545,7 @@ func (a *DBFArea) GetValue(fieldIndex int) (hbrt.Value, error) { } func (a *DBFArea) PutValue(fieldIndex int, val hbrt.Value) error { + a.loadRecord() if a.readOnly { return fmt.Errorf("table is read-only") } @@ -582,8 +593,9 @@ func (a *DBFArea) Append() error { a.FEof = false a.FBof = false - a.dirty = true // mark dirty — will be written on Flush/Close/GoTo + a.dirty = true a.ghost = true + a.recLoaded = true // buffer is fresh (all spaces) return nil }