feat(pp,rtl): pre-release accuracy round (Wave 3)
Four audit findings around correctness/consistency in std.ch and the SORT/UPDATE/TOTAL handlers: * #13: TOTAL/UPDATE key idiom inconsistency documented as inherent. TOTAL evaluates `<key>` only in the source workarea so verbatim `<{key}>` (alias-qualified or `_FIELD->`-prefixed by the user) works. UPDATE evaluates the same block in BOTH master and detail context, so it must wrap as `_FIELD-><key>` to dispatch to whichever WA is selected at eval time. The two rules look alike but their evaluation contexts differ — also documented in std.ch alongside both rules so the asymmetry isn't a surprise. Plus: TOTAL TO and ON are now mandatory (matching the COUNT/ UPDATE pattern from Wave 1) — bare TOTAL would have produced broken syntax via the unconditional `<(f)>`/`<{key}>` template references. * #15/#16: SDF / DELIMITED variants of COPY and TO PRINTER / TO FILE variants of LIST / DISPLAY are now matched by stub rules (placed *before* the regular rules so they win) that expand to a new `__dbNotImpl(reason)` RTL primitive raising a clear `&hbrt.HbError`. BEGIN SEQUENCE / RECOVER catches the panic, so callers get a real error instead of the previous silent dispatch-to-regular-DBF-copy. * #19: SORT /C (case-insensitive) now actually folds case before the string compare, instead of being silently treated as ascending. Suffix parser also rebuilt as a multi-letter scanner so `name/CD`, `name/DC`, `name/C/D`, `name/D/C` all parse the same way — combine /C and /D freely. Unknown suffix letters (e.g., `name/X`) leave the suffix attached to the field name so a stray slash in user input doesn't get silently mangled into a broken field reference. * #27 SET DELETED: verified with a regression test that `SET DELETED ON` causes COUNT/COPY (and by extension SORT/TOTAL/JOIN/UPDATE — all of which iterate via Area.Skip) to skip rows marked deleted. The filtering is implemented at the workarea level (skipFilter in dbf.go honors hbrdd.IsSetDeleted) so no RTL changes were needed; this commit just adds the coverage so the behavior doesn't silently regress. Gates green: go test ./... : PASS FiveSql2 SQL:1999 : 43/43 Harbour compat : 56/56 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,8 +74,15 @@
|
||||
/* --- bulk record export ---
|
||||
COPY TO copies visible records of the current workarea into a fresh
|
||||
DBF. FIELDS/FOR/WHILE/NEXT/RECORD/REST work as in Harbour. SDF and
|
||||
DELIMITED variants stay as silent no-ops in the parser until their
|
||||
backends land. */
|
||||
DELIMITED variants are not implemented; the matching rules below
|
||||
raise a clear runtime error so callers don't quietly get a regular
|
||||
DBF copy when they asked for an SDF dump. Order matters: the SDF /
|
||||
DELIMITED rules must come before the regular COPY rule. */
|
||||
#command COPY [TO <(f)>] [FIELDS <fields,...>] SDF [<*tail*>] => ;
|
||||
__dbNotImpl("COPY TO ... SDF")
|
||||
#command COPY [TO <(f)>] [FIELDS <fields,...>] DELIMITED [<*tail*>] => ;
|
||||
__dbNotImpl("COPY TO ... DELIMITED")
|
||||
|
||||
#command COPY [TO <(f)>] [FIELDS <fields,...>] ;
|
||||
[FOR <for>] [WHILE <while>] [NEXT <next>] ;
|
||||
[RECORD <rec>] [<rest:REST>] [ALL] => ;
|
||||
@@ -94,8 +101,19 @@
|
||||
/* --- console output ---
|
||||
LIST emits every record matching the filter; DISPLAY without ALL
|
||||
shows just the current record. Both share __dbList — lAll
|
||||
distinguishes them. TO PRINTER / TO FILE accepted but unused;
|
||||
stdout is the only sink for now. */
|
||||
distinguishes them. TO PRINTER / TO FILE redirection is not yet
|
||||
implemented; the stub rules below surface a clear error rather
|
||||
than silently sending output to stdout when a printer/file was
|
||||
requested. Order matters: more specific rules first. */
|
||||
#command LIST [<v,...>] TO PRINTER [<*tail*>] => ;
|
||||
__dbNotImpl("LIST ... TO PRINTER")
|
||||
#command LIST [<v,...>] TO FILE <(f)> [<*tail*>] => ;
|
||||
__dbNotImpl("LIST ... TO FILE")
|
||||
#command DISPLAY [<v,...>] TO PRINTER [<*tail*>] => ;
|
||||
__dbNotImpl("DISPLAY ... TO PRINTER")
|
||||
#command DISPLAY [<v,...>] TO FILE <(f)> [<*tail*>] => ;
|
||||
__dbNotImpl("DISPLAY ... TO FILE")
|
||||
|
||||
#command LIST [<v,...>] [<off:OFF>] ;
|
||||
[FOR <for>] [WHILE <while>] [NEXT <next>] ;
|
||||
[RECORD <rec>] [<rest:REST>] [ALL] => ;
|
||||
@@ -114,10 +132,14 @@
|
||||
must already be sorted/indexed on the key for the grouping to
|
||||
produce one row per distinct value.
|
||||
|
||||
Note for callers: Five doesn't auto-resolve a bare identifier
|
||||
inside a code block to the current workarea's field. Write the
|
||||
key alias-qualified — `ON src->dept` rather than `ON dept`. */
|
||||
#command TOTAL [TO <(f)>] [ON <key>] [FIELDS <fields,...>] ;
|
||||
Note on key syntax — TOTAL evaluates `<key>` only in the source
|
||||
workarea, so `<{key}>` (verbatim blockify) is enough; user can
|
||||
write `ON src->dept` (alias-qualified) or `ON _FIELD->dept`
|
||||
(current-area). UPDATE FROM evaluates the key block in BOTH
|
||||
master and detail context and therefore needs `_FIELD->`-wrapped
|
||||
bare keys instead — the two rules look superficially similar but
|
||||
their evaluation contexts differ. */
|
||||
#command TOTAL TO <(f)> ON <key> [FIELDS <fields,...>] ;
|
||||
[FOR <for>] [WHILE <while>] [NEXT <next>] ;
|
||||
[RECORD <rec>] [<rest:REST>] [ALL] => ;
|
||||
__dbTotal( <(f)>, <{key}>, { <(fields)> }, ;
|
||||
|
||||
Reference in New Issue
Block a user