Files
five/hbrdd/driver.go
Charles KWON OhJun 21fd9dc65c feat: SET DELETED filtering, SEEK/LOCATE/CONTINUE, SET command codegen
- skipFilter: skip deleted records in GoTop/GoBottom/Skip when SET DELETED ON
- hbrdd.IsSetDeleted callback: avoids circular import hbrdd→hbrtl
- Parser: capture ON/OFF for boolean SET commands (DELETED, EXACT, SOFTSEEK, etc.)
- Parser: capture TO expr for SET DATE/DECIMALS/EPOCH
- Gengo: emit proper t.Do() calls for 11 SET toggles + 3 value SETs
- stmtSet: was stub (skipToEOL), now calls parseSet()
- RTL: register 11 SET toggle functions (SETDELETED, SETEXACT, etc.)
- RTL: DBLOCATE/DBCONTINUE for sequential search
- RTL: DBSETFILTER/DBCLEARFILTER/DBFILTER
- PadL/PadR: support 3rd param fill character
- Area interface: added SetFound, SetLocate, LocateBlock, filter methods
- MemRDD: implements new Area interface methods
- Comprehensive PRG test: test_search.prg (7 test suites all pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:33:59 +09:00

213 lines
5.4 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// RDD (Replaceable Database Driver) interface definitions for Five.
//
// Design: Harbour's 101-method RDDFUNCS vtable → Go interface composition.
// Each interface is small and focused (Go philosophy).
// Drivers implement only what they need; BaseArea provides defaults.
//
// Inheritance via Go embedding:
// BaseArea (WAAREA) → DBFArea → NTXArea / CDXArea
//
// Reference:
// /mnt/d/harbour-core/include/hbapirdd.h (lines 640-816)
// docs/rdd-architecture-spec.md
package hbrdd
import (
"five/hbrt"
"fmt"
"strings"
"sync"
)
// --- Driver registry ---
var (
driversMu sync.RWMutex
drivers = map[string]Driver{}
)
// RegisterDriver registers an RDD driver by name.
func RegisterDriver(d Driver) {
driversMu.Lock()
drivers[strings.ToUpper(d.Name())] = d
driversMu.Unlock()
}
// GetDriver returns a registered driver by name.
func GetDriver(name string) (Driver, error) {
driversMu.RLock()
d, ok := drivers[strings.ToUpper(name)]
driversMu.RUnlock()
if !ok {
return nil, fmt.Errorf("unknown RDD driver: %s", name)
}
return d, nil
}
// --- Core interfaces ---
// Driver creates and opens work areas.
// Harbour: RDD node with RDDFUNCS table.
type Driver interface {
Name() string
Open(params OpenParams) (Area, error)
Create(params CreateParams) (Area, error)
}
// OpenParams for opening an existing table.
type OpenParams struct {
Path string // file path (without extension)
Alias string // workarea alias
Shared bool // shared access mode
ReadOnly bool // read-only mode
CodePage string // code page name
}
// CreateParams for creating a new table.
type CreateParams struct {
Path string
Alias string
Fields []FieldInfo
CodePage string
}
// FieldInfo describes a database field.
// Harbour: DBFFIELD (32 bytes in file, this is the runtime representation)
type FieldInfo struct {
Name string // up to 10 chars (Harbour limit)
Type byte // 'C', 'N', 'L', 'D', 'M', 'I', 'B', '@', '+', '=', '^', 'Y', etc.
Len int // field length
Dec int // decimal places
Flags byte // 0x01=system, 0x02=nullable, 0x04=binary
}
// --- Area interface (WAAREA + DBF core) ---
// Area is the primary interface for accessing a database table.
// Harbour: AREAP with SELF_* macro dispatch.
type Area interface {
// Identity
Driver() Driver
Alias() string
// Lifecycle
Close() error
Flush() error
// Movement — Harbour: hb_waBof, hb_waEof, hb_waFound, hb_waGoTo, etc.
BOF() bool
EOF() bool
Found() bool
GoTo(recNo uint32) error
GoTop() error
GoBottom() error
Skip(count int64) error
// Record info
RecNo() uint32
RecCount() (uint32, error)
Deleted() bool
// Field access — Harbour: SELF_FIELDCOUNT, SELF_GETVALUE, SELF_PUTVALUE
FieldCount() int
GetFieldInfo(index int) FieldInfo
GetValue(fieldIndex int) (hbrt.Value, error)
PutValue(fieldIndex int, val hbrt.Value) error
// Record operations — Harbour: SELF_APPEND, SELF_DELETE, SELF_RECALL
Append() error
Delete() error
Recall() error
// Bulk operations
Pack() error
Zap() error
// State — Harbour: hb_waSetFound, locate support
SetFound(b bool)
SetLocate(expr string, block func(*hbrt.Thread) bool)
LocateBlock() func(*hbrt.Thread) bool
// Filter
SetFilter(expr string, block func(*hbrt.Thread) bool) error
ClearFilter() error
HasFilter() bool
}
// --- Optional interfaces (drivers implement as needed) ---
// Indexer provides index management and key-based seeking.
// Harbour: order* methods in RDDFUNCS (9 methods).
// Only DBFNTX and DBFCDX implement this.
type Indexer interface {
// Order management
OrderCreate(params OrderCreateParams) error
OrderListAdd(path string) error
OrderListClear() error
OrderListFocus(tagName string) error
OrderListRebuild() error
OrderDestroy(tagName string) error
OrderInfo(ordNo int) (*OrderInfo, error)
// Key-based seeking — Harbour: SELF_SEEK
Seek(key hbrt.Value, softSeek bool, findLast bool) (bool, error)
}
// OrderCreateParams for INDEX ON.
type OrderCreateParams struct {
TagName string // index tag name
KeyExpr string // key expression (e.g., "UPPER(lastname+firstname)")
ForExpr string // FOR condition (e.g., "active = .T.")
FilePath string // index file path
Unique bool
Descending bool
}
// OrderInfo holds information about an index order.
type OrderInfo struct {
Name string
KeyExpr string
ForExpr string
Unique bool
Descending bool
KeyCount uint32
Custom bool
}
// Locker provides record and file locking.
// Harbour: SELF_LOCK, SELF_UNLOCK, SELF_RAWLOCK
type Locker interface {
LockRecord(recNo uint32) (bool, error)
UnlockRecord(recNo uint32) error
LockFile() (bool, error)
UnlockFile() error
IsLocked(recNo uint32) bool
}
// Relater provides SET RELATION support.
// Harbour: SELF_SETREL, SELF_CLEARREL, SELF_FORCEREL
type Relater interface {
SetRelation(child Area, keyExpr func(*hbrt.Thread) hbrt.Value, scoped bool) error
ClearRelation() error
ForceRel() error
SyncChildren() error
}
// MemoHandler provides memo field read/write.
// Harbour: SELF_OPENMEMFILE, SELF_CLOSEMEMFILE, SELF_GETVALUEFILE, SELF_PUTVALUEFILE
type MemoHandler interface {
OpenMemo(path string) error
CloseMemo() error
ReadMemo(blockNo uint32) ([]byte, error)
WriteMemo(data []byte) (uint32, error)
}
// Scoper provides SET SCOPE support for index-based range queries.
type Scoper interface {
SetScope(top, bottom hbrt.Value) error
ClearScope() error
}