To find the next parser test case to work on (files closest to fully passing first), run from the repository root:
go run ./cmd/next-testThis prints the case's SQL, the expected parse tree, and overall progress.
Always run parser tests with a 60 second timeout:
go test ./... -timeout 60sThe tests are fast. If a test times out, it almost certainly indicates an infinite loop in the parser.
IMPORTANT: After implementing parser/dump changes, ALWAYS run check-parse to update metadata files:
go test ./parser -check-parse -v 2>&1 | grep "PARSE PASSES NOW"This command:
- Runs all cases listed in
parse_todoin the metadata files - Automatically removes now-passing cases from
parse_todo - Reports which cases now pass
You must run this after every change to parser, lexer, ast, or dump code,
then commit the updated .metadata.json files along with your code changes.
parser/testdata/*.test files are vendored unmodified from
google/googlesql release 2026.01.1
(googlesql/parser/testdata/). Each file contains cases separated by ==
lines; each case has the SQL input, the expected parse tree (or ERROR:
message), and the expected unparse output, separated by -- lines. See
internal/testfile for the format parser.
Each <name>.metadata.json sidecar (ours, not vendored) tracks:
parse_todo— cases whose output doesn't match yet. Keys arecase_Nfor ordinary cases andcase_N.alt_Kfor the K-th expansion of an alternation case ({{a|b}}groups; see below).alternations— alternation cases the harness cannot confidently expand and therefore skips entirely (currently none; kept for safety). Expandable alternation cases are tracked per-expansion inparse_todoinstead.skip— reason string to skip the whole file
A .test file with no sidecar passes completely.
Cases whose SQL or option lines contain {{a|b|c}} groups expand into the
cartesian product of the groups' alternatives (last group varies fastest),
matching file_based_test_driver. internal/testfile substitutes each
combination and matches it to its expected parse tree by the driver's
ALTERNATION GROUP label; each expansion runs as its own sub-test
(case_N/alt_K) with independent parse_todo tracking. Byte offsets in an
expansion's expected tree are relative to that expansion's substituted SQL.
Regenerate per-expansion todos the same way as ordinary cases, with
go test ./parser -check-parse (it re-triages every expansion, harvesting
ones that now pass and recording ones that still fail).
- NEVER modify
.testfiles — they are vendored golden files from the reference implementation. If output doesn't match, fix the Go code. - NEVER hand-edit expected output into metadata — metadata only marks what is not implemented yet.
- The dump format must match ZetaSQL's
ASTNode::DebugString(googlesql/parser/parse_tree.cc) byte-for-byte, including the summarized source text (seeGetSummaryStringin googlesql/common/utf_util.cc, ported ininternal/dump). - Parse error output must match ZetaSQL's error format:
Syntax error: <message> [at line:col]plus the source line and a caret. - When porting logic or tables (keywords, precedence) from googlesql source, keep the attribution note in the file header. GoogleSQL is Apache 2.0.
The grammar and AST are defined in google/googlesql:
googlesql/parser/parse_tree.h— AST node definitions and detailsgooglesql/parser/parse_tree.cc—SingleNodeDebugStringper-node outputgooglesql/parser/keywords.cc— keyword and reserved word tablesgooglesql/parser/*.tm/ grammar files — syntax reference
For new test queries, golden output comes from the prebuilt reference binary
(pinned in cmd/regenerate-parse/main.go, must match the vendored testdata
release):
go run ./cmd/regenerate-parse -sql "select 1 + 2"