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:
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
|
||||
Reference in New Issue
Block a user