Milestones 8 and 9: normalize, fingerprint, and deparse — every corpus suite at 100% - #7
Merged
Merged
Conversation
Port pg_query_fingerprint.c against the protobuf tree directly: the per-node walk is protobuf-descriptor-driven (like the JSON emitter), with the upstream generator's special cases hand-written — skip nodes/fields, sorted-dedup list fields with the listsort cache, A_Expr kind folding, RangeVar digit stripping and temp-table elision, TypeCast-of-constant elision, and the pre-PG15 legacy value-node field names. Field elision streams tokens into a byte buffer and truncates, so internal/xxh3 stays one-shot. All 43,385 fingerprint corpus todos graduate, including the 1.1 MB stress insert.
Port pg_query_normalize.c: const_record_walker with its special cases (DefElem string arguments located by text scan, subscription conninfo matching, the SELECT GROUP BY-to-target-list fingerprint matching that reuses parameter numbers, and the utility-only gates), backed by a port of PostgreSQL 17's raw_expression_tree_walker whose unsupported node types stop the subtree walk the way the swallowed elog does. Constant lengths come from the milestone-3 lexer; the location sort is a port of pg_qsort itself, because the C sort is unstable and duplicate locations (MultiAssignRef sources walked once per column) take their parameter number from whichever record it leaves first. All 43,254 normalize and 25 normalize_utility corpus todos graduate; milestone 8 is complete.
Infer the suite from the .test file path so the same debug aid drives the normalize/fingerprint/deparse mismatch hunts; drop the Deparse entry from the not-implemented compat pins ahead of milestone 9.
… tree
Port all 293 deparse functions of postgres_deparse.c (12,107 lines at
the pin) into internal/deparse, targeting the protobuf structs
directly. The part/group/nesting-level machinery is ported exactly —
it shapes plain-mode output too (parts join with single spaces, no
space after "(" or before ")"/";", part groups fold their major
keyword into their first part, and the merge pass runs against the
default 80-column limit) — while the pretty-print and comment paths
its structure exists for are unreachable through pg_query_go's API
and are omitted. quote_identifier and quote_qualified_identifier are
ported from ruleutils.c on top of the milestone-3 keyword tables;
deparse errors surface as parser.Error via panic/recover, mirroring
the C ereport longjmp.
All 43,352 deparse corpus todos graduate; every suite's todo list is
now empty and go test -race ./... is green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands milestones 8 (Normalize + Fingerprint) and 9 (Deparse) from PLAN.md. With these, every suite's todo list is empty: all 217,229 corpus cases across the eight suites pass, and
go test -race ./...is green. The remaining not-implemented entry points areSummary(milestone 10) andParsePlPgSqlToJSON(milestone 11).Milestone 8a: Fingerprint (
internal/fingerprint)Port of
pg_query_fingerprint.c+ its generated defs/conds at the pin. Like the milestone-4 JSON emitter, the per-node walk is protobuf-descriptor-driven rather than generated per-node code — the upstream defs are generated from the same struct metadata the pinned proto came from, so the descriptor already carries the field set, kinds, and (after sorting) the alphabetical field order. The hand-written layer mirrors the upstream generator's special-case tables: skip-nodes/skip-fields, the six sorted-dedup list field names with the listsort cache (the exponential-blowup guard the 1.1 MB stress insert needs),AEXPR_OP_ANY/AEXPR_INfolding,RangeVardigit-stripping and temp-table elision, TypeCast-of-constant elision, theResTarget.name-under-SELECT-targetList rule, and the pre-PG15 legacy value-node field names.The C's per-field XXH3 state snapshot/restore reduces to "elide the field name if the subtree appended zero bytes", so tokens stream into a byte buffer and
internal/xxh3stays one-shot.Gate met: all 43,385 fingerprint todos graduated, including the 1.1 MB stress case.
Milestone 8b: Normalize (
internal/normalize)Port of
pg_query_normalize.c:const_record_walkerwith its hand-written cases (DefElem string arguments located by text scan, subscription conninfo matching, the SELECT GROUP BY-to-target-list fingerprint matching that reuses parameter numbers, the utility-only gates), backed by a port of PostgreSQL 17'sraw_expression_tree_walkerwhose unsupported node types stop the subtree walk the way the swallowedelogdoes upstream. Constant lengths come from the milestone-3 lexer.One bug-compatibility find: the C's location sort is an unstable
pg_qsort, and for duplicate constant locations (aMultiAssignRefsource walked once per target column) the parameter number depends on which duplicate the Bentley–McIlroy partition leaves first —UPDATE t SET (c,b,a) = ('x', b+1, DEFAULT) WHERE c = 'y'normalizes toSET (c,b,a) = ($1, b+$4, DEFAULT) WHERE c = $7.pg_qsortis therefore ported too (internal/normalize/qsort.go).Gate met: all 43,254 normalize and 25 normalize_utility todos graduated.
Milestone 9: Deparse (
internal/deparse)Port of all 293 functions of
postgres_deparse.c(12,107 lines at the pin), targeting the protobuf structs directly. The part/group/nesting-level machinery — built for the pretty-print mode pg_query_go's API never exposes — still shapes plain-mode byte output (parts join with single spaces except after(or before a part starting with)/;, part groups fold their major keyword into their first part, and the merge pass runs against the default 80-column limit), so it is ported exactly, while the pretty-print and comment paths themselves are omitted as unreachable.quote_identifier/quote_qualified_identifierare ported fromruleutils.con top of the milestone-3 keyword tables. Deparse errors surface asparser.Errorvia panic/recover standing in for the Cereportlongjmp; no corpus input triggers one.proto3 cannot represent C's NULL-vs-empty-string distinction; where the deparser branches on it, call sites decided the port (e.g.
deparseOptBooleanOrString's NULL guard is dead upstream — every caller passesstrVal— soSET x TO ''falls through to print'').Gate met: all 43,352 deparse todos graduated — byte-equality with the oracle's deparse output corpus-wide, which subsumes the reference's parse → deparse → reparse roundtrip criterion since the goldens are the oracle's own deparse output.
Also in this PR
Normalize,NormalizeUtility,Fingerprint,FingerprintToUInt64, andDeparse/DeparseFromProtobufare wired up in the public API and removed from the not-implemented compat test.cmd/difftodonow supports all corpus suites, not just parse.🤖 Generated with Claude Code
https://claude.ai/code/session_0114AafjcMa5K8ZJJAZdGwzC
Generated by Claude Code