fix: 5 seek/dbf bugs — 77/77 thorough Harbour compatibility
1. SOFTSEEK: use idx.CurRecNo() for positioning (was checking recNo > 0) - SEEK with SET SOFTSEEK ON now positions at next higher key - SEEK command reads SET SOFTSEEK at runtime (was compile-time only) - rtlDbSeek defaults to GetSetSoftSeek() when no explicit param 2. SET DELETED ON + INDEX: SkipIndexed skips deleted records - GoTopIndexed: skip deleted record at top position - SkipIndexed: inner loop continues past deleted records 3. Compound key (CITY+NAME): field name TrimSpace before lookup - evalKeyExprInner: TrimSpace on fieldName after FIELD-> strip - Fixed "CITY " != "CITY" mismatch from + operator splitting 4. SET INDEX TO filename: treated as string, not variable - gengo uses exprToString for SET INDEX TO (was emitExpr) - Prevents identifier being resolved as local variable 5. hasXBaseCommands: recursive scan into nested blocks - BEGIN SEQUENCE, IF, FOR, DO WHILE, SWITCH bodies now scanned - Fixes missing hbrdd import for DB commands inside blocks Thorough test: 77 items (14 sections) covering exact/partial/soft seek, SET DELETED, duplicate keys, numeric keys, compound keys, empty/single table, state consistency, order switching, full traversal — all identical. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -98,7 +98,12 @@ func (g *Generator) emitSeekCmd(s *ast.SeekCmd, locals localMap) {
|
||||
g.writeln("_key := t.Pop2()")
|
||||
g.writeln("if _idx, ok := area.(hbrdd.Indexer); ok {")
|
||||
g.indent++
|
||||
g.writeln(fmt.Sprintf("_found, _ := _idx.Seek(_key, %v, false)", s.SoftSeek))
|
||||
// SoftSeek: from SEEK SOFT keyword OR runtime SET SOFTSEEK
|
||||
if s.SoftSeek {
|
||||
g.writeln("_found, _ := _idx.Seek(_key, true, false)")
|
||||
} else {
|
||||
g.writeln("_found, _ := _idx.Seek(_key, hbrtl.GetSetSoftSeek(), false)")
|
||||
}
|
||||
g.writeln("_ = _found")
|
||||
g.indent--
|
||||
g.writeln("}")
|
||||
|
||||
@@ -12,13 +12,51 @@ func hasXBaseCommands(file *ast.File) bool {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, s := range fn.Body {
|
||||
switch s.(type) {
|
||||
case *ast.UseCmd, *ast.GoCmd, *ast.SkipCmd, *ast.SeekCmd,
|
||||
*ast.ReplaceCmd, *ast.AppendCmd, *ast.DeleteCmd,
|
||||
*ast.SelectCmd, *ast.IndexCmd, *ast.SetCmd:
|
||||
if scanStmtsForXBase(fn.Body) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func scanStmtsForXBase(stmts []ast.Stmt) bool {
|
||||
for _, s := range stmts {
|
||||
switch v := s.(type) {
|
||||
case *ast.UseCmd, *ast.GoCmd, *ast.SkipCmd, *ast.SeekCmd,
|
||||
*ast.ReplaceCmd, *ast.AppendCmd, *ast.DeleteCmd,
|
||||
*ast.SelectCmd, *ast.IndexCmd, *ast.SetCmd:
|
||||
return true
|
||||
case *ast.IfStmt:
|
||||
if scanStmtsForXBase(v.Body) || scanStmtsForXBase(v.ElseBody) {
|
||||
return true
|
||||
}
|
||||
for _, ei := range v.ElseIfs {
|
||||
if scanStmtsForXBase(ei.Body) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case *ast.ForStmt:
|
||||
if scanStmtsForXBase(v.Body) {
|
||||
return true
|
||||
}
|
||||
case *ast.ForEachStmt:
|
||||
if scanStmtsForXBase(v.Body) {
|
||||
return true
|
||||
}
|
||||
case *ast.DoWhileStmt:
|
||||
if scanStmtsForXBase(v.Body) {
|
||||
return true
|
||||
}
|
||||
case *ast.SeqStmt:
|
||||
if scanStmtsForXBase(v.Body) || scanStmtsForXBase(v.RecoverBody) {
|
||||
return true
|
||||
}
|
||||
case *ast.SwitchStmt:
|
||||
for _, c := range v.Cases {
|
||||
if scanStmtsForXBase(c.Body) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -564,10 +564,15 @@ func (g *Generator) emitStmt(stmt ast.Stmt, locals localMap) {
|
||||
}
|
||||
case "INDEX":
|
||||
if s.Expr != nil {
|
||||
fileStr := exprToString(s.Expr)
|
||||
g.writeln("if idx, ok := area.(hbrdd.Indexer); ok {")
|
||||
g.indent++
|
||||
g.emitExpr(s.Expr)
|
||||
g.writeln(`idx.OrderListAdd(t.Pop2().AsString())`)
|
||||
if fileStr != "" {
|
||||
g.writeln(fmt.Sprintf(`idx.OrderListAdd(%q)`, fileStr))
|
||||
} else {
|
||||
g.emitExpr(s.Expr)
|
||||
g.writeln(`idx.OrderListAdd(t.Pop2().AsString())`)
|
||||
}
|
||||
g.indent--
|
||||
g.writeln("}")
|
||||
} else {
|
||||
|
||||
297
examples/seek_thorough.prg
Normal file
297
examples/seek_thorough.prg
Normal file
@@ -0,0 +1,297 @@
|
||||
// Thorough DBF + SEEK compatibility test — Harbour vs Five
|
||||
PROCEDURE Main()
|
||||
LOCAL i, aStruct, aCities, nIdx, nCount
|
||||
|
||||
ErrorBlock({|e| Break(e)})
|
||||
|
||||
aStruct := { ;
|
||||
{"ID","N",6,0}, {"NAME","C",20,0}, {"CITY","C",15,0}, ;
|
||||
{"CODE","C",5,0}, {"SALARY","N",10,2} ;
|
||||
}
|
||||
|
||||
BEGIN SEQUENCE
|
||||
|
||||
dbCreate("seek_test", aStruct)
|
||||
USE "seek_test" NEW
|
||||
|
||||
aCities := {"Seoul","Tokyo","Beijing","London","NYC"}
|
||||
FOR i := 1 TO 30
|
||||
APPEND BLANK
|
||||
REPLACE ID WITH i
|
||||
REPLACE NAME WITH PadR("Name_" + PadL(LTrim(Str(i)), 3, "0"), 20)
|
||||
nIdx := ((i-1) % 5) + 1
|
||||
REPLACE CITY WITH PadR(aCities[nIdx], 15)
|
||||
REPLACE CODE WITH PadR(LTrim(Str(Int((i-1)/10)+1)), 5)
|
||||
REPLACE SALARY WITH 20000 + i * 500.00
|
||||
NEXT
|
||||
R("SETUP", LTrim(Str(RecCount())))
|
||||
|
||||
// S1: Basic field access
|
||||
GO 1
|
||||
R("S1_REC1_ID", LTrim(Str(FieldGet(1))))
|
||||
R("S1_REC1_NAME", RTrim(FieldGet(2)))
|
||||
R("S1_REC1_CITY", RTrim(FieldGet(3)))
|
||||
GO 15
|
||||
R("S1_REC15_ID", LTrim(Str(FieldGet(1))))
|
||||
R("S1_REC15_NAME", RTrim(FieldGet(2)))
|
||||
GO 30
|
||||
R("S1_REC30_ID", LTrim(Str(FieldGet(1))))
|
||||
|
||||
// S2: NAME index — exact seek
|
||||
INDEX ON FIELD->NAME TO seek_name
|
||||
R("S2_IDX", "OK")
|
||||
GO TOP
|
||||
R("S2_TOP_NAME", RTrim(FieldGet(2)))
|
||||
R("S2_TOP_RECNO", LTrim(Str(RecNo())))
|
||||
GO BOTTOM
|
||||
R("S2_BOT_NAME", RTrim(FieldGet(2)))
|
||||
R("S2_BOT_RECNO", LTrim(Str(RecNo())))
|
||||
|
||||
SEEK PadR("Name_001", 20)
|
||||
R("S2_SEEK_001", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
SEEK PadR("Name_015", 20)
|
||||
R("S2_SEEK_015", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
SEEK PadR("Name_030", 20)
|
||||
R("S2_SEEK_030", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
|
||||
SEEK PadR("Name_000", 20)
|
||||
R("S2_MISS_000", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
SEEK PadR("Name_031", 20)
|
||||
R("S2_MISS_031", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
SEEK PadR("Name_999", 20)
|
||||
R("S2_MISS_999", B(Found()) + " " + LTrim(Str(RecNo())) + " " + B(EOF()))
|
||||
SEEK PadR("ZZZZZ", 20)
|
||||
R("S2_MISS_ZZZ", B(Found()) + " " + B(EOF()))
|
||||
|
||||
// S3: Partial key
|
||||
SEEK "Name_0"
|
||||
R("S3_PART_0", B(Found()) + " " + LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SEEK "Name_01"
|
||||
R("S3_PART_01", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK "Name_1"
|
||||
R("S3_PART_1", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK "Name_3"
|
||||
R("S3_PART_3", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK "N"
|
||||
R("S3_PART_N", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK "Z"
|
||||
R("S3_PART_Z", B(Found()) + " " + B(EOF()))
|
||||
|
||||
// S4: SOFTSEEK
|
||||
SET SOFTSEEK ON
|
||||
SEEK PadR("Name_001", 20)
|
||||
R("S4_SOFT_001", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Name_000", 20)
|
||||
R("S4_SOFT_000", B(Found()) + " " + LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SEEK PadR("Name_015X", 20)
|
||||
R("S4_SOFT_15X", B(Found()) + " " + LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SEEK PadR("Name_031", 20)
|
||||
R("S4_SOFT_031", B(Found()) + " " + B(EOF()))
|
||||
SEEK PadR("ZZZZZ", 20)
|
||||
R("S4_SOFT_ZZZ", B(Found()) + " " + B(EOF()))
|
||||
SEEK "A"
|
||||
R("S4_SOFT_A", B(Found()) + " " + LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SET SOFTSEEK OFF
|
||||
|
||||
// S5: Seek + navigation
|
||||
SEEK PadR("Name_010", 20)
|
||||
R("S5_SEEK_010", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SKIP
|
||||
R("S5_SKIP1", LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SKIP -1
|
||||
R("S5_SKIPM1", LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
SKIP 5
|
||||
R("S5_SKIP5", LTrim(Str(RecNo())) + " " + RTrim(FieldGet(2)))
|
||||
|
||||
// S6: Duplicate key (CITY)
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
INDEX ON FIELD->CITY TO seek_city
|
||||
R("S6_IDX", "OK")
|
||||
GO TOP
|
||||
R("S6_TOP", RTrim(FieldGet(3)) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
SEEK PadR("Seoul", 15)
|
||||
R("S6_SEOUL", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
nCount := 0
|
||||
DO WHILE !EOF() .AND. RTrim(FieldGet(3)) == "Seoul"
|
||||
nCount++
|
||||
SKIP
|
||||
ENDDO
|
||||
R("S6_SEOUL_CNT", LTrim(Str(nCount)))
|
||||
|
||||
SEEK PadR("Tokyo", 15)
|
||||
R("S6_TOKYO", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Beijing", 15)
|
||||
R("S6_BEIJING", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("London", 15)
|
||||
R("S6_LONDON", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("NYC", 15)
|
||||
R("S6_NYC", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Paris", 15)
|
||||
R("S6_PARIS", B(Found()) + " " + B(EOF()))
|
||||
|
||||
// S7: SET DELETED + SEEK
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
INDEX ON FIELD->NAME TO seek_name
|
||||
SET ORDER TO 0
|
||||
FOR i := 1 TO 30
|
||||
GO i
|
||||
IF i % 5 == 0
|
||||
DELETE
|
||||
ENDIF
|
||||
NEXT
|
||||
SET ORDER TO 1
|
||||
|
||||
SET DELETED OFF
|
||||
SEEK PadR("Name_005", 20)
|
||||
R("S7_DELOFF_005", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Name_010", 20)
|
||||
R("S7_DELOFF_010", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
SET DELETED ON
|
||||
GO TOP
|
||||
nCount := 0
|
||||
DO WHILE !EOF()
|
||||
nCount++
|
||||
SKIP
|
||||
ENDDO
|
||||
R("S7_DELON_CNT", LTrim(Str(nCount)))
|
||||
|
||||
SET DELETED OFF
|
||||
SET ORDER TO 0
|
||||
FOR i := 1 TO 30
|
||||
GO i
|
||||
IF Deleted()
|
||||
RECALL
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
// S8: Numeric key
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
INDEX ON Str(FIELD->ID, 6) TO seek_id
|
||||
R("S8_IDX", "OK")
|
||||
SEEK Str(1, 6)
|
||||
R("S8_SEEK_1", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK Str(15, 6)
|
||||
R("S8_SEEK_15", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK Str(30, 6)
|
||||
R("S8_SEEK_30", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK Str(31, 6)
|
||||
R("S8_SEEK_31", B(Found()) + " " + B(EOF()))
|
||||
GO TOP
|
||||
R("S8_TOP_ID", LTrim(Str(FieldGet(1))))
|
||||
GO BOTTOM
|
||||
R("S8_BOT_ID", LTrim(Str(FieldGet(1))))
|
||||
|
||||
// S9: Compound key
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
INDEX ON FIELD->CITY + FIELD->NAME TO seek_compound
|
||||
R("S9_IDX", "OK")
|
||||
GO TOP
|
||||
R("S9_TOP", RTrim(FieldGet(3)) + " " + RTrim(FieldGet(2)) + " " + LTrim(Str(RecNo())))
|
||||
GO BOTTOM
|
||||
R("S9_BOT", RTrim(FieldGet(3)) + " " + RTrim(FieldGet(2)) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Seoul", 15) + PadR("Name_001", 20)
|
||||
R("S9_SEEK_S001", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Seoul", 15)
|
||||
R("S9_PART_SEOUL", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Tokyo", 15)
|
||||
R("S9_PART_TOKYO", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
// S10: Empty table
|
||||
CLOSE ALL
|
||||
dbCreate("seek_empty", {{"NAME","C",10,0}})
|
||||
USE "seek_empty" NEW
|
||||
INDEX ON FIELD->NAME TO seek_empty_idx
|
||||
R("S10_RC", LTrim(Str(RecCount())))
|
||||
SEEK "Test"
|
||||
R("S10_SEEK", B(Found()) + " " + B(EOF()) + " " + LTrim(Str(RecNo())))
|
||||
GO TOP
|
||||
R("S10_TOP", B(EOF()) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
// S11: Single record
|
||||
APPEND BLANK
|
||||
REPLACE NAME WITH "OnlyOne"
|
||||
CLOSE ALL
|
||||
USE "seek_empty" NEW
|
||||
INDEX ON FIELD->NAME TO seek_empty_idx
|
||||
SEEK PadR("OnlyOne", 10)
|
||||
R("S11_FOUND", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
SEEK PadR("Other", 10)
|
||||
R("S11_MISS", B(Found()) + " " + B(EOF()))
|
||||
SEEK "Only"
|
||||
R("S11_PARTIAL", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
// S12: State after seek
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
SET INDEX TO seek_name
|
||||
SEEK PadR("Name_020", 20)
|
||||
R("S12_SEEK", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
R("S12_BOF", B(BOF()))
|
||||
R("S12_EOF", B(EOF()))
|
||||
SEEK PadR("Name_999", 20)
|
||||
R("S12_MISS_FOUND", B(Found()))
|
||||
R("S12_MISS_EOF", B(EOF()))
|
||||
GO TOP
|
||||
R("S12_GOTOP", LTrim(Str(RecNo())) + " " + B(Found()))
|
||||
|
||||
// S13: Order switch + seek
|
||||
SET INDEX TO seek_name
|
||||
SEEK PadR("Name_001", 20)
|
||||
R("S13_ORD1", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
SET INDEX TO seek_city
|
||||
SEEK PadR("Tokyo", 15)
|
||||
R("S13_ORD2", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
SET INDEX TO seek_id
|
||||
SEEK Str(25, 6)
|
||||
R("S13_ORD3", B(Found()) + " " + LTrim(Str(RecNo())))
|
||||
|
||||
// S14: Full traversal count
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
SET INDEX TO seek_name
|
||||
GO TOP
|
||||
nCount := 0
|
||||
DO WHILE !EOF()
|
||||
nCount++
|
||||
SKIP
|
||||
ENDDO
|
||||
R("S14_NAME_CNT", LTrim(Str(nCount)))
|
||||
|
||||
CLOSE ALL
|
||||
USE "seek_test" NEW
|
||||
SET INDEX TO seek_city
|
||||
GO TOP
|
||||
nCount := 0
|
||||
DO WHILE !EOF()
|
||||
nCount++
|
||||
SKIP
|
||||
ENDDO
|
||||
R("S14_CITY_CNT", LTrim(Str(nCount)))
|
||||
|
||||
CLOSE ALL
|
||||
|
||||
RECOVER
|
||||
? "ERROR"
|
||||
END SEQUENCE
|
||||
|
||||
RETURN
|
||||
|
||||
FUNCTION B(lVal)
|
||||
IF lVal
|
||||
RETURN ".T."
|
||||
ENDIF
|
||||
RETURN ".F."
|
||||
|
||||
PROCEDURE R(cKey, cVal)
|
||||
? cKey + "=" + cVal
|
||||
RETURN
|
||||
@@ -416,11 +416,15 @@ func (a *DBFArea) Seek(key hbrt.Value, softSeek bool, findLast bool) (bool, erro
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if softSeek && recNo > 0 && !idx.IsEOF() {
|
||||
a.GoTo(recNo)
|
||||
a.FEof = false
|
||||
a.SetFound(false)
|
||||
return false, nil
|
||||
if softSeek && !idx.IsEOF() {
|
||||
// Softseek: position at the next higher key
|
||||
posRecNo := idx.CurRecNo()
|
||||
if posRecNo > 0 {
|
||||
a.GoTo(posRecNo)
|
||||
a.FEof = false
|
||||
a.SetFound(false)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Not found — go to EOF
|
||||
@@ -464,7 +468,12 @@ func (a *DBFArea) GoTopIndexed() error {
|
||||
a.FEof = true
|
||||
return a.GoTo(rc + 1)
|
||||
}
|
||||
return a.GoTo(idx.CurRecNo())
|
||||
a.GoTo(idx.CurRecNo())
|
||||
// Skip deleted records at top
|
||||
if hbrdd.IsSetDeleted != nil && hbrdd.IsSetDeleted() && a.Deleted() {
|
||||
return a.SkipIndexed(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoBottomIndexed positions at the last key in the active index.
|
||||
@@ -525,23 +534,35 @@ func (a *DBFArea) SkipIndexed(count int64) error {
|
||||
idx := a.idxState.indexes[a.idxState.current]
|
||||
hasScope := a.idxState.scopeTop != nil || a.idxState.scopeBottom != nil
|
||||
|
||||
setDel := hbrdd.IsSetDeleted != nil && hbrdd.IsSetDeleted()
|
||||
|
||||
if count > 0 {
|
||||
for i := int64(0); i < count; i++ {
|
||||
idx.SkipNext()
|
||||
if idx.IsEOF() || idx.CurRecNo() == 0 {
|
||||
rc, _ := a.RecCount()
|
||||
a.GoTo(rc + 1)
|
||||
a.FEof = true
|
||||
return nil
|
||||
}
|
||||
// Check bottom scope
|
||||
if hasScope && a.idxState.scopeBottom != nil {
|
||||
if bytes.Compare(idx.CurKey(), a.idxState.scopeBottom) > 0 {
|
||||
for {
|
||||
idx.SkipNext()
|
||||
if idx.IsEOF() || idx.CurRecNo() == 0 {
|
||||
rc, _ := a.RecCount()
|
||||
a.GoTo(rc + 1)
|
||||
a.FEof = true
|
||||
return nil
|
||||
}
|
||||
// Check bottom scope
|
||||
if hasScope && a.idxState.scopeBottom != nil {
|
||||
if bytes.Compare(idx.CurKey(), a.idxState.scopeBottom) > 0 {
|
||||
rc, _ := a.RecCount()
|
||||
a.GoTo(rc + 1)
|
||||
a.FEof = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Skip deleted records
|
||||
if setDel {
|
||||
a.GoTo(idx.CurRecNo())
|
||||
if a.Deleted() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if count < 0 {
|
||||
@@ -737,9 +758,9 @@ func (a *DBFArea) evalKeyExprInner(expr string) []byte {
|
||||
}
|
||||
|
||||
// Strip FIELD-> or _FIELD-> or alias-> prefix (Harbour: M->var, FIELD->var)
|
||||
fieldName := upper
|
||||
fieldName := strings.TrimSpace(upper)
|
||||
if idx := strings.Index(fieldName, "->"); idx >= 0 {
|
||||
fieldName = fieldName[idx+2:]
|
||||
fieldName = strings.TrimSpace(fieldName[idx+2:])
|
||||
}
|
||||
|
||||
// Simple field name
|
||||
|
||||
@@ -334,7 +334,7 @@ func rtlDbSeek(t *hbrt.Thread) {
|
||||
return
|
||||
}
|
||||
val := t.Local(1)
|
||||
softSeek := false
|
||||
softSeek := GetSetSoftSeek() // default: check SET SOFTSEEK
|
||||
findLast := false
|
||||
if nParams >= 2 && !t.Local(2).IsNil() {
|
||||
softSeek = t.Local(2).AsBool()
|
||||
|
||||
Reference in New Issue
Block a user