feat(pp,rtl): Tier 2 audit followups — JOIN hash + PP validation + C heuristic
Three medium-priority audit items in one commit, each independently
revertible.
* **#18 JOIN hash-join fast path.** New std.ch shape:
JOIN WITH <alias> TO <file> [FIELDS ...] ON <mfield> = <dfield>
expands to a 6-arg __dbJoin call with the master/detail key
field names. Runtime detects the extra args, builds an O(M)
hash over the detail's key column, then probes per master row
for O(N+M) total — vs the FOR form's O(N*M). For 1k×1k that's
2k vs 1M operations; the gap widens with N. The original FOR
form is unchanged and stays the fallback for arbitrary
predicates. New helper dbHashKey type-tags the key string so
`1` (numeric), `"1"` (string), and `.T.` (logical) don't
collide in the bucket map.
* **#38 PP rule result-marker validation.** ParseRule now walks
the result template after parseMarkers and warns about every
`<name>` (or `<(name)>` / `<.name.>` / `<{name}>` / `#<name>`
/ `<"name">`) that doesn't match a pattern marker. Warnings
flow into pp.errors via handleDirective with the directive's
filename:line, so a typo'd `<NaMe>` in an `#xcommand`
case-sensitive rule fails the build with a clear diagnostic
instead of silently producing broken expansions.
* **#44 looksLikeInlineC heuristic strengthened.** Catches more
of the common Harbour-PRG-with-C-inline-block shapes that
used to fall through and produce cryptic Go-side errors:
function-like #define, `extern "C"` linkage blocks, C return-
type declarations (`int foo(`, `static char* bar(`), and the
hb_ret*() helper family used by Harbour's C FFI return
setters. Two small predicate helpers (allLetters,
allIdentChars) keep the C-vs-Go disambiguation tight enough
that legit Go code (`func name() int { ... }`) doesn't trip.
* **#28 LIST/DISPLAY pagination** — explicitly deferred. Proper
pagination requires interactive terminal handling (Inkey(0)
for the keypress) which would hang in CI / batch mode. Will
revisit when an interactive terminal layer needs it for
other reasons.
Test fixtures: tests/std_ch/test_join_hash.prg verifies the new
ON-form path produces the same output as the FOR form would.
std.ch runner now stands at 16/16.
Other gates green:
go test ./... : PASS
FiveSql2 SQL:1999 : 43/43
Harbour compat : 56/56
std.ch suite : 16/16
FRB suite : 7/7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ TESTS=(
|
||||
test_unsupported
|
||||
test_block_comma
|
||||
test_compound_lhs
|
||||
test_join_hash
|
||||
)
|
||||
|
||||
work="$(mktemp -d)"
|
||||
|
||||
50
tests/std_ch/test_join_hash.prg
Normal file
50
tests/std_ch/test_join_hash.prg
Normal file
@@ -0,0 +1,50 @@
|
||||
/* JOIN ... ON hash-join fast path. */
|
||||
|
||||
PROCEDURE Main()
|
||||
LOCAL n
|
||||
|
||||
FErase("cust.dbf"); FErase("ord.dbf"); FErase("out.dbf")
|
||||
|
||||
/* Customers (master) */
|
||||
dbCreate("cust.dbf", { {"CID","N",4,0}, {"CNAME","C",12,0} })
|
||||
USE cust.dbf NEW EXCLUSIVE ALIAS cu
|
||||
dbAppend(); FieldPut(1, 1); FieldPut(2, "Alice")
|
||||
dbAppend(); FieldPut(1, 2); FieldPut(2, "Bob")
|
||||
dbAppend(); FieldPut(1, 3); FieldPut(2, "Carol")
|
||||
dbCommit()
|
||||
dbCloseArea()
|
||||
|
||||
/* Orders (detail) */
|
||||
dbCreate("ord.dbf", { {"OID","N",4,0}, {"CID","N",4,0}, {"AMT","N",8,2} })
|
||||
USE ord.dbf NEW EXCLUSIVE ALIAS od
|
||||
dbAppend(); FieldPut(1, 100); FieldPut(2, 1); FieldPut(3, 50)
|
||||
dbAppend(); FieldPut(1, 101); FieldPut(2, 1); FieldPut(3, 30)
|
||||
dbAppend(); FieldPut(1, 102); FieldPut(2, 2); FieldPut(3, 200)
|
||||
dbAppend(); FieldPut(1, 103); FieldPut(2, 4); FieldPut(3, 99) /* no-match */
|
||||
dbCommit()
|
||||
dbCloseArea()
|
||||
|
||||
USE cust.dbf NEW EXCLUSIVE ALIAS cu
|
||||
USE ord.dbf NEW EXCLUSIVE ALIAS od
|
||||
SELECT cu
|
||||
|
||||
/* Hash-join form */
|
||||
JOIN WITH od TO out.dbf FIELDS cid, cname, oid, amt ON cid = cid
|
||||
|
||||
SELECT cu ; dbCloseArea()
|
||||
SELECT od ; dbCloseArea()
|
||||
|
||||
USE out.dbf NEW EXCLUSIVE
|
||||
COUNT TO n
|
||||
? "Hash-joined rows =", n, "(expect 3)"
|
||||
dbGoTop()
|
||||
DO WHILE !Eof()
|
||||
? " ", FieldGet(1), AllTrim(FieldGet(2)), FieldGet(3), FieldGet(4)
|
||||
dbSkip()
|
||||
ENDDO
|
||||
? "(expect three rows: Alice/100/50, Alice/101/30, Bob/102/200)"
|
||||
dbCloseArea()
|
||||
|
||||
FErase("cust.dbf"); FErase("ord.dbf"); FErase("out.dbf")
|
||||
? "DONE"
|
||||
RETURN
|
||||
Reference in New Issue
Block a user