Skip to content

Parse TRUNC/TRUNCATE's bare second argument as a column - #104

Merged
kvlonge merged 4 commits into
mainfrom
fix/trunc-scale-column
Nov 6, 2026
Merged

kvlonge merged 4 commits into
mainfrom
fix/trunc-scale-column

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Unblocks C's slice 1 (land/d151-s1). Coordinator priority: this lands before the slice. It touches crates/sqlquack-syntax, so it goes on the auditor's D118 re-read list.

Bug. SQLQuack rewrote TRUNC/TRUNCATE's bare second argument into a date part ({"var": …}) whenever its spelling named a date part. TRUNC(x, s) with a NUMBER column s became "second". Snowflake recorded numeric truncation for those cases, and Polyglot parses s as a column. This was the only Rust failure slice 1 causes (runs::test_exact, 8 differences in trunc.trunc / trunc.truncate t001_int, t005_null_x, t006_null_scale, t007_scale_past). It was also the cause of trunc.truncate's SQ0002 node:var refusal.

Fix. The parser keeps the identifier as a column, as Polyglot does. Snowflake decides by scope, which the parser can't see:

  • SELECT TRUNC ( 1 , DAY ) compiled with no table in scope (receipt sf.functions.trunc2.ka4427f986c78, accept, stage semantic).
  • TRUNC(x, s) with a column s truncated numerically.

This reverses the TRUNC part of d704778, an auditor tree finding. DATEDIFF, TIMEDIFF, TIMESTAMPDIFF, EXTRACT and DATE_TRUNC keep their date-part rewrite. The test_fixes pins now assert a column for TRUNC(x, DAY) and TRUNCATE(x, s).

Zero-fire evidence

  • Every recorded SQL text calling TRUNC or TRUNCATE on main was parsed with the old parser: 49 texts across cases/** and coverage/syntax/cases/*.jsonl. The rewrite fires on one, the trunc2 receipt above. Its verdict is unchanged, and sqlquack check still reports no problems for it.
  • No translation case on main has a bare-identifier TRUNC unit, so no translation verdict moves.
  • Syntax baseline, re-recorded with snapshot on the committed tree: the only change is sf.functions.trunc2 verified → unverified, because its auditor review fingerprints the old tree. Satisfied obligations (68,585), parsed and refused cases, and the 5,128 known receipt disagreements are identical. The auditor renews trunc2 under D118: 2,388 → 2,387 verified until then.
  • sqlquack check: no problems for TRUNC(1, DAY), TRUNC(x, s) FROM t or TRUNC(d, day) FROM t.
  • Polyglot directory check on those shapes: every entry identical, where all three differed before.

The converter is unchanged. A bare TRUNC unit was already refused (node:var), and a TRUNC scale from a column stays refused ("places other than a literal integer"). Resolving TRUNC(d, day) by type is a possible follow-up, which needs a receipt.

A bare identifier as TRUNC's second argument is a date part in
Snowflake only when no column of that name is in scope: TRUNC(1, DAY)
compiled with no table (receipt sf.functions.trunc2.ka4427f986c78),
while TRUNC(x, s) with a column s truncated numerically (C's slice 1
cases trunc.trunc/trunc.truncate t001, t005, t006, t007). The parser
cannot see scope, so it keeps the identifier as a column, as polyglot
does, instead of rewriting date-part spellings to a date part.
Only change: sf.functions.trunc2 verified -> unverified, because its
auditor review fingerprints the old tree of SELECT TRUNC ( 1 , DAY ).
Satisfied obligations 68,585, parsed/refused cases and the 5,128
known parser/receipt disagreements are unchanged; no receipt verdict
moves. The auditor renews the review under D118.
@kvlonge
kvlonge merged commit 219a540 into main Nov 6, 2026
1 check failed
@kvlonge
kvlonge deleted the fix/trunc-scale-column branch November 9, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant