Repository navigation
googlesql: Spanner schemas, arrays, and a function seed from Spanner - #4645
Open
kyleconroy wants to merge 4 commits into
Open
kyleconroy wants to merge 4 commits into
kyleconroy wants to merge 4 commits into
Conversation
ALTER TABLE was dropped as unsupported, so columns it added, dropped or retyped never reached the catalog. It now converts ADD/DROP COLUMN, Spanner's ALTER COLUMN (which restates the type and nullability), ALTER COLUMN ... DROP NOT NULL, RENAME COLUMN and RENAME TO; RENAME TABLE converts too. CREATE [OR REPLACE] VIEW and DROP VIEW convert, and a HIDDEN column is left out of SELECT * as Spanner does. The new googlesql cases' schemas were checked against Spanner Omni. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L8QoGi6VRtADkb4jL3BFHP
The converter now handles typed literals (DATE, TIMESTAMP, NUMERIC, JSON, bytes), array constructors, INTERVAL, the OFFSET/ORDINAL/SAFE_* subscripts and "x IN UNNEST(array)", which becomes "x = ANY(array)". In the analysis core: - A subscript is the array's element, nullable, with an integer index. - ARRAY(subquery) is an array of the column, not its first value. - "x = ANY(array)" types a placeholder as an array of x's type, as PostgreSQL does; sqlc.slice() still gets the element. - A polymorphic function taking anyarray and returning anyelement, as unnest does, returns the array's element, and a function in FROM takes that resolved type, with its alias naming the column. GoogleSQL gains an unnest seed. analyze_arrays is checked against Spanner Omni; the rest are in array_unnest, where Spanner's wire types lose the nullability and lengths the golden pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L8QoGi6VRtADkb4jL3BFHP
functions.jsonl held only count, so every other function typed as any. It now lists 291 signatures across aggregates, math, strings, dates and timestamps, JSON, arrays, hashing, search and vector distance. Their return types, and those of the operators, were read from Spanner Omni by compiling each call in PLAN mode; the search functions and PENDING_COMMIT_TIMESTAMP, which cannot be compiled in a select list, are typed from Spanner's reference. Spanner has no window functions, so none are listed. GoogleSQL now propagates NULL through function arguments, with the functions that return NULL from non-null input marked nullable. The converter maps COALESCE, IFNULL, NULLIF, IF, GREATEST and LEAST onto the nodes the core types them by, makes SAFE.f(...) nullable, passes date parts (DAY, WEEK(MONDAY)) as strings rather than columns, and converts EXTRACT. In the core, an operator a dialect lists now replaces the one its arithmetic rule generates for the same operands, which is how INT64 / INT64 is a FLOAT64; and a placeholder passed for a polymorphic parameter takes its type from the other arguments, so the value in array_includes(array, @v) is the array's element. analyze_functions is checked against Spanner Omni; func_nullability covers what Spanner's wire types cannot report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L8QoGi6VRtADkb4jL3BFHP
Spanner keeps no catalog of its library, so goldeneye now reads it by asking. spanner/library.jsonl lists the function signatures to try; each is compiled in PLAN mode against a probe database, and the ones Spanner accepts are written to functions.jsonl with the type it reports. A polymorphic signature is tried with INT64 and STRING and its result must hold for both. Calls a select list cannot make are compiled the way they can be: SEARCH in a WHERE clause, SCORE over a search index, PENDING_COMMIT_TIMESTAMP in an INSERT. operators.jsonl is every binary operator Spanner accepts over the operands tried, and types.jsonl the types the dialect lists, each one Spanner has checked by a cast; the GoogleSQL types only BigQuery has are kept as they are. The generated files hold the same records the seed already did; they are rewritten in goldeneye's compact form, and TestDialect now checks all four files against Spanner. The dialect package's Function gains never_null, which the seed already reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L8QoGi6VRtADkb4jL3BFHP
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.
With zetajones v0.2.0 parsing Spanner's DDL, this fills the GoogleSQL engine's remaining gaps for Spanner. A realistic Spanner schema and query set that used to fail or come back as
anynow generates fully typed code. The PR has four commits, one per area.1. Schema statements
ALTER TABLEused to be dropped as unsupported, so columns it added, dropped or retyped never reached the catalog. It now convertsADD/DROP COLUMN, Spanner'sALTER COLUMN(which restates type and nullability),ALTER COLUMN … DROP NOT NULL,RENAME COLUMNandRENAME TO.RENAME TABLE,CREATE [OR REPLACE] VIEWandDROP VIEWnow convert.HIDDENcolumn is left out ofSELECT *, as Spanner does.2. Arrays and literals
DATE,TIMESTAMP,NUMERIC,JSON, bytes), array constructors,INTERVAL, theOFFSET/ORDINAL/SAFE_*subscripts, andx IN UNNEST(arr), which becomesx = ANY(arr).ARRAY(subquery)is an array, no longer its first value.x = ANY(array)types a placeholder as an array of x's type, which is what PostgreSQL reports;sqlc.slice()still gets the element.unnest(anyarray) → anyelementresolves to the element, and a function inFROMtakes its resolved type, with its alias naming the column.3. Functions and operators
functions.jsonlhad onlycount. It now has 291 signatures across aggregates, math, strings, dates and timestamps, JSON, arrays, hashing, search and vector distance.operators.jsonlhas 83. Spanner has no window functions, so none are listed.JSON_VALUE,REGEXP_EXTRACT, theSAFE_*functions, …) are marked nullable.COALESCE/IFNULL/NULLIF/IF/GREATEST/LEASTonto the nodes the core types them by, makesSAFE.f(...)nullable, passes date parts (DAY,WEEK(MONDAY)) as strings rather than columns, and convertsEXTRACT.INT64 / INT64is FLOAT64. A placeholder passed for a polymorphic parameter takes its type from the other arguments, soARRAY_INCLUDES(arr, @v)types@vas the element.4. goldeneye generates the library
Spanner keeps no catalog of its functions, so goldeneye now reads them by asking.
goldeneye/spanner/library.jsonllists the signatures to try. Each is compiled in PLAN mode against a probe database, and the ones Spanner accepts are written tofunctions.jsonlwith the type Spanner reports. A polymorphic signature is tried with INT64 and STRING, and its result must hold for both.SEARCHin a WHERE clause,SCOREover a search index,PENDING_COMMIT_TIMESTAMPin an INSERT.operators.jsonlis every binary operator Spanner accepts over the operands tried.types.jsonlis the types the dialect lists, each checked by a cast. The GoogleSQL types only BigQuery has (bignumeric,datetime,time,geography,range,map, …) are kept as they are, since the engine is shared with BigQuery.library.jsonlstates it.TestDialectnow checks all four files against Spanner.Effect on other engines
The core changes reach every engine that goes through the analysis core. One existing golden changes: in
analyze_types/bigquery,[1, 2]is now typedARRAY<INT64>where it used to be untyped.Testing
googlesqlvariants ofcreate_view,ddl_alter_table_add_column,ddl_alter_table_alter_type,ddl_alter_table_drop_columnandddl_alter_table_rename.ddl_spanner, a realistic schema with change streams, roles, grants, identity columns and vector indexes.analyze_arrays,array_unnest,analyze_functionsandfunc_nullability.analyze_arraysandanalyze_functionscases pass goldeneye's check against live Spanner. Nullability, string lengths andUNNESTlineage, which Spanner's wire types can't report, are pinned in the generate cases.go test ./internal/endtoend -run TestReplay/basepasses in full.SQLC_TEST_CORE=1 … -run TestReplay/corehas no new failures against main's baseline. Theinternal/core,internal/compiler,internal/engineandinternal/cmdtests pass.spanner.TestDialectpasses against Omni.examples/authors/googlesqlexample passes against Spanner Omni, and its generated code is unchanged.analyze_types/googlesqlstill fails, as it does on main. sqlc names unattributed parameters after their@name, and goldeneye's Spanner answer leaves them blank.🤖 Generated with Claude Code
https://claude.ai/code/session_01L8QoGi6VRtADkb4jL3BFHP