diff --git a/compiler/analyzer/analyzer.go b/compiler/analyzer/analyzer.go index 9706111..e3aa417 100644 --- a/compiler/analyzer/analyzer.go +++ b/compiler/analyzer/analyzer.go @@ -548,6 +548,9 @@ var rtlFunctions = map[string]bool{ "DBCREATE": true, "DBINFO": true, "DBORDERINFO": true, "DBSETINDEX": true, // FiveSql2 hybrid hot-path RTL (pcode + Go-native scan) "PCCOMPILE": true, "PCEVAL": true, "SQLSCAN": true, + // Field metadata + index creation + "FIELDTYPE": true, "FIELDLEN": true, "FIELDDEC": true, + "ORDCREATE": true, "DBCREATEINDEX": true, "DBCLEARINDEX": true, "RECALL": true, "PACK": true, "ZAP": true, "FLOCK": true, "DBUNLOCK": true, "__DBPACK": true, "__DBZAP": true, diff --git a/hbrtl/indexrtl.go b/hbrtl/indexrtl.go index 9fbbca9..839a4de 100644 --- a/hbrtl/indexrtl.go +++ b/hbrtl/indexrtl.go @@ -251,6 +251,191 @@ func RddSetDefault(t *hbrt.Thread) { t.RetString("DBFNTX") } +// FIELDTYPE(n) → cType — one-letter type ("C"/"N"/"L"/"D"/"M"/...) +// Harbour: field descriptor type byte from current workarea. +func FieldType(t *hbrt.Thread) { + t.Frame(1, 0) + defer t.EndProc() + wam := getWA(t) + if wam == nil { + t.RetString("") + return + } + area := wam.Current() + if area == nil { + t.RetString("") + return + } + n := t.Local(1).AsInt() - 1 + if n < 0 || n >= area.FieldCount() { + t.RetString("") + return + } + fi := area.GetFieldInfo(n) + t.RetString(string(fi.Type)) +} + +// FIELDLEN(n) → nLen — field length in bytes. +func FieldLen(t *hbrt.Thread) { + t.Frame(1, 0) + defer t.EndProc() + wam := getWA(t) + if wam == nil { + t.RetInt(0) + return + } + area := wam.Current() + if area == nil { + t.RetInt(0) + return + } + n := t.Local(1).AsInt() - 1 + if n < 0 || n >= area.FieldCount() { + t.RetInt(0) + return + } + fi := area.GetFieldInfo(n) + t.RetInt(int64(fi.Len)) +} + +// FIELDDEC(n) → nDecimals — field decimal places. +func FieldDec(t *hbrt.Thread) { + t.Frame(1, 0) + defer t.EndProc() + wam := getWA(t) + if wam == nil { + t.RetInt(0) + return + } + area := wam.Current() + if area == nil { + t.RetInt(0) + return + } + n := t.Local(1).AsInt() - 1 + if n < 0 || n >= area.FieldCount() { + t.RetInt(0) + return + } + fi := area.GetFieldInfo(n) + t.RetInt(int64(fi.Dec)) +} + +// ORDCREATE(cBagName, cTagName, cKeyExpr [, bKeyExpr] [, lUnique]) +// Creates a new index (CDX tag or NTX file). Uses MacroEval slow path +// for the key expression since Callers pass a string literal. +func OrdCreate(t *hbrt.Thread) { + nParams := t.ParamCount() + t.Frame(nParams, 0) + defer t.EndProc() + + wam := getWA(t) + if wam == nil { + t.RetNil() + return + } + area := wam.Current() + if area == nil { + t.RetNil() + return + } + idx, ok := area.(hbrdd.Indexer) + if !ok { + t.RetNil() + return + } + + cBag := "" + if nParams >= 1 && !t.Local(1).IsNil() { + cBag = t.Local(1).AsString() + } + cTag := "" + if nParams >= 2 && !t.Local(2).IsNil() { + cTag = t.Local(2).AsString() + } + cExpr := "" + if nParams >= 3 && !t.Local(3).IsNil() { + cExpr = t.Local(3).AsString() + } + lUnique := false + if nParams >= 5 && !t.Local(5).IsNil() { + lUnique = t.Local(5).AsBool() + } + + _ = idx.OrderCreate(hbrdd.OrderCreateParams{ + TagName: cTag, + KeyExpr: cExpr, + FilePath: cBag, + Unique: lUnique, + }) + t.RetNil() +} + +// DBCREATEINDEX(cFile, cKeyExpr [, bKeyExpr] [, lUnique]) +// Legacy (Clipper) single-tag NTX index creation. Tag name defaults +// to the bare filename. +func DbCreateIndex(t *hbrt.Thread) { + nParams := t.ParamCount() + t.Frame(nParams, 0) + defer t.EndProc() + + wam := getWA(t) + if wam == nil { + t.RetNil() + return + } + area := wam.Current() + if area == nil { + t.RetNil() + return + } + idx, ok := area.(hbrdd.Indexer) + if !ok { + t.RetNil() + return + } + + cFile := "" + if nParams >= 1 && !t.Local(1).IsNil() { + cFile = t.Local(1).AsString() + } + cExpr := "" + if nParams >= 2 && !t.Local(2).IsNil() { + cExpr = t.Local(2).AsString() + } + lUnique := false + if nParams >= 4 && !t.Local(4).IsNil() { + lUnique = t.Local(4).AsBool() + } + + _ = idx.OrderCreate(hbrdd.OrderCreateParams{ + KeyExpr: cExpr, + FilePath: cFile, + Unique: lUnique, + }) + t.RetNil() +} + +// DBCLEARINDEX() — close all open index bags on current workarea. +func DbClearIndex(t *hbrt.Thread) { + t.Frame(0, 0) + defer t.EndProc() + wam := getWA(t) + if wam == nil { + t.RetNil() + return + } + area := wam.Current() + if area == nil { + t.RetNil() + return + } + if idx, ok := area.(hbrdd.Indexer); ok { + _ = idx.OrderListClear() + } + t.RetNil() +} + // DBCREATE(cFile, aStruct [, cDriver]) → NIL func DbCreate(t *hbrt.Thread) { nParams := t.ParamCount() diff --git a/hbrtl/register.go b/hbrtl/register.go index c3ea205..086395e 100644 --- a/hbrtl/register.go +++ b/hbrtl/register.go @@ -422,6 +422,12 @@ func RegisterRTL(vm *hbrt.VM) { hbrt.Sym("INDEXORD", hbrt.FsPublic, IndexOrd), hbrt.Sym("INDEXKEY", hbrt.FsPublic, IndexKey), hbrt.Sym("ORDSETFOCUS", hbrt.FsPublic, OrdSetFocus), + hbrt.Sym("ORDCREATE", hbrt.FsPublic, OrdCreate), + hbrt.Sym("DBCREATEINDEX", hbrt.FsPublic, DbCreateIndex), + hbrt.Sym("DBCLEARINDEX", hbrt.FsPublic, DbClearIndex), + hbrt.Sym("FIELDTYPE", hbrt.FsPublic, FieldType), + hbrt.Sym("FIELDLEN", hbrt.FsPublic, FieldLen), + hbrt.Sym("FIELDDEC", hbrt.FsPublic, FieldDec), hbrt.Sym("ORDCOUNT", hbrt.FsPublic, OrdCount), hbrt.Sym("ORDNAME", hbrt.FsPublic, OrdName), hbrt.Sym("ORDKEY", hbrt.FsPublic, OrdKey), diff --git a/include/dbinfo.ch b/include/dbinfo.ch new file mode 100644 index 0000000..39c4715 --- /dev/null +++ b/include/dbinfo.ch @@ -0,0 +1,107 @@ +/* + * dbinfo.ch — Harbour-compatible DBI / DBOI constants + * + * Values mirror Harbour's include/dbinfo.ch so FiveSql2 and other + * downstream code using dbInfo() / dbOrderInfo() compile cleanly. + * + * Note: Five's hbrtl dbInfo / dbOrderInfo are currently stubbed + * (return NIL). These constants exist so identifier references + * resolve at compile time — runtime behavior depends on the stub. + * When the stubs are replaced with real implementations, these + * values become load-bearing. + */ + +#ifndef HB_DBINFO_CH_ +#define HB_DBINFO_CH_ + +/* DBI: database info types passed to dbInfo() */ +#define DBI_ISDBF 1 +#define DBI_CANPUTREC 2 +#define DBI_GETHEADERSIZE 3 +#define DBI_LASTUPDATE 4 +#define DBI_GETDELIMITER 5 +#define DBI_SETDELIMITER 6 +#define DBI_GETRECSIZE 7 +#define DBI_GETLOCKARRAY 8 +#define DBI_TABLEEXT 9 +#define DBI_FULLPATH 10 +#define DBI_MEMOEXT 11 +#define DBI_DB_VERSION 12 +#define DBI_RDD_VERSION 13 +#define DBI_LOCKSCHEME 14 +#define DBI_ISFLOCK 15 +#define DBI_ROLLBACK 16 +#define DBI_PASSWORD 17 +#define DBI_ISENCRYPTED 18 +#define DBI_MEMOTYPE 19 +#define DBI_SEPARATOR 20 +#define DBI_MEMOBLOCKSIZE 21 +#define DBI_MEMOVERSION 22 +#define DBI_TABLETYPE 23 +#define DBI_SCOPEDRELATION 24 +#define DBI_TRANSREC 25 +#define DBI_SHARED 42 +#define DBI_ISREADONLY 43 +#define DBI_VALIDBUFFER 44 +#define DBI_POSITIONED 45 +#define DBI_ISTEMPORARY 46 +#define DBI_LOCKOFFSET 47 +#define DBI_LOCKTEST 48 +#define DBI_LOCKCOUNT 49 +#define DBI_CHILDCOUNT 50 +#define DBI_BOF 51 +#define DBI_EOF 52 +#define DBI_DBFILTER 53 +#define DBI_FOUND 54 +#define DBI_FCOUNT 55 +#define DBI_ALIAS 56 +#define DBI_OPENINFO 57 +#define DBI_DIRTYREAD 58 +#define DBI_POSINBUFFER 59 +#define DBI_TRIGGER 60 + +/* DBOI: order info types passed to dbOrderInfo() / ordXxx() */ +#define DBOI_CONDITION 1 +#define DBOI_EXPRESSION 2 +#define DBOI_POSITION 3 +#define DBOI_RECNO 3 +#define DBOI_NAME 4 +#define DBOI_NUMBER 5 +#define DBOI_BAGNAME 6 +#define DBOI_BAGEXT 7 +#define DBOI_INDEXEXT 7 +#define DBOI_INDEXNAME 8 +#define DBOI_ORDERCOUNT 9 +#define DBOI_FILEHANDLE 10 +#define DBOI_ISCOND 11 +#define DBOI_ISDESC 12 +#define DBOI_UNIQUE 13 +#define DBOI_KEYTYPE 14 +#define DBOI_KEYSIZE 15 +#define DBOI_KEYDEC 16 +#define DBOI_KEYVAL 17 +#define DBOI_SCOPETOP 18 +#define DBOI_SCOPEBOTTOM 19 +#define DBOI_SCOPETOPCLEAR 20 +#define DBOI_SCOPEBOTTOMCLEAR 21 +#define DBOI_KEYCOUNT 22 +#define DBOI_NUMBER2 23 +#define DBOI_KEYSINCLUDED 24 +#define DBOI_HPLOCKING 25 +#define DBOI_CUSTOM 26 +#define DBOI_KEYADD 27 +#define DBOI_KEYDELETE 28 +#define DBOI_KEYGOTO 29 +#define DBOI_SKIPUNIQUE 30 +#define DBOI_ISREINDEX 31 +#define DBOI_USERKEEP 32 +#define DBOI_TEMPORARY 33 +#define DBOI_KEYCOUNTRAW 34 +#define DBOI_FINDREC 35 +#define DBOI_FINDRECCONT 36 + +/* Lock modes for hb_dbfLock */ +#define DBOI_LOCKMODE_FILE 1 +#define DBOI_LOCKMODE_RECORD 2 + +#endif /* HB_DBINFO_CH_ */ diff --git a/include/dbstruct.ch b/include/dbstruct.ch new file mode 100644 index 0000000..05c6841 --- /dev/null +++ b/include/dbstruct.ch @@ -0,0 +1,23 @@ +/* + * dbstruct.ch — DBS_xxx field descriptor indices + * + * Mirrors Harbour's include/dbstruct.ch. These index into the + * sub-array returned by dbStruct() for each field: + * { FIELD_NAME, FIELD_TYPE, FIELD_LEN, FIELD_DEC } + */ + +#ifndef HB_DBSTRUCT_CH_ +#define HB_DBSTRUCT_CH_ + +#define DBS_NAME 1 +#define DBS_TYPE 2 +#define DBS_LEN 3 +#define DBS_DEC 4 + +/* dbCreate() field descriptor indices — same as DBS_ */ +#define DB_FLD_NAME 1 +#define DB_FLD_TYPE 2 +#define DB_FLD_LEN 3 +#define DB_FLD_DEC 4 + +#endif /* HB_DBSTRUCT_CH_ */