Skip to content

feat(syntax): check Snowflake views and streams against their grammar - #199

Merged
kvlonge merged 9 commits into
mainfrom
feat/syntax-ddl-views-streams
Nov 12, 2026
Merged

kvlonge merged 9 commits into
mainfrom
feat/syntax-ddl-views-streams

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Why

After the table grammar, the view, materialized view, stream and semantic view forms were the next ddl gap: Raw fallbacks, false rejects of ALTER VIEW properties, and stream options tied to sources Snowflake does not tie them to. This replaces them with checked grammars that match the receipts: where the documentation shows a shape Snowflake refuses with a syntax error, the grammar refuses it too.

Changes

  • Streams, views, materialized views and semantic views build nodes through checked grammars. A stream takes any option, AT or BEFORE (with VERSION) on every source, as Snowflake parses them.
  • ALTER VIEW reads SET and UNSET of properties, IF EXISTS and the name SET may omit; ALTER MATERIALIZED VIEW reads RECLUSTER. An ALTER VIEW without an action and a CREATE missing STREAM are refused.
  • ALTER VIEW SET or UNSET SECURE combined with another property, and RECURSIVE after a temporary scope, are refused: Snowflake's receipts for those cases are syntax errors.
  • A view's column list may follow its properties; a view COMMENT without = is refused.
  • Re-records baseline.json and corpus-verdicts.json on the pinned evidence: seven non-Snowflake view clauses (SECURITY DEFINER/INVOKER, SQL SECURITY, LOCKING, COMMENT without =) are now refused.
  • Root .gitattributes marks tests, scripts and dev-only tools as linguist-documentation, so the language bar counts shipped code.

Verification

Snapshots on the pinned evidence, main's parser → this head:

before after
satisfied obligations 78,979 80,235
syntax errors accepted (false accepts) 2,519 2,359
receipt contradictions 3,526 3,199

New false accepts against receipts: 0. No obligation or parsed case is lost. In the view and stream forms, false accepts go 201 → 42 and false rejects of cases Snowflake parsed 186 → 34.

make check-ci and make verify-recorded pass; crate tests cover each root cause, including the three refused view shapes.

CREATE STREAM kept everything after STREAM as raw text, so malformed
statements were accepted. A stream on a table, view, external, dynamic
or event table or a stage now builds a CreateObject: its tags and COPY
GRANTS before ON, the source, AT or BEFORE where the source takes it,
and the properties that source documents. CREATE OR ALTER STREAM and
DEFINE STREAM read a table only. A time travel point may name a STREAM.
CREATE SEMANTIC VIEW was checked against its grammar and then kept as
raw text, and DEFINE SEMANTIC VIEW was refused. Both now build a
CreateObject whose clauses are properties: tables, relationships,
facts, dimensions, metrics and verified queries as lists of their
parts, with the item expressions parsed.
…eir grammar

Recursive, volatile, secure materialized and interactive materialized
views were kept as raw text, and view-level clauses such as COPY = ( ),
JOIN POLICY, AGGREGATION POLICY and WITH CONTACT were refused. A
Snowflake view now reads its scope, column options, view clauses and
query against the grammar of its kind: a materialized view takes
CLUSTER BY and no CHANGE_TRACKING, and CREATE OR ALTER VIEW takes only
CHANGE_TRACKING and COMMENT. Column policies after the column list,
empty column lists and string column names are refused, as Snowflake
refuses them.
…mmar

ALTER VIEW now reads Snowflake's SET and UNSET of view properties,
IF EXISTS, and the name Snowflake lets SET omit. An ALTER VIEW with no
action is refused. ALTER MATERIALIZED VIEW reads RECLUSTER and a SET
with the name omitted. A CREATE whose name is followed by ON, a stream
missing its STREAM keyword, is refused.
Snowflake parses any option, and AT or BEFORE, on every stream source
and refuses a misplaced one only after parsing, so the grammar no
longer ties options to sources. AT and BEFORE take VERSION, a tag value
may be a number, and CREATE OR ALTER STREAM takes tags.
Snowflake parses a CREATE VIEW whose column list follows a view
property, such as CHANGE_TRACKING = TRUE ( id ), and refuses a view
COMMENT written without =.
Test suites, development scripts and development-only tools (the
coverage and format checks, the fuzz targets and the Snowflake
recorder) are marked linguist-documentation, so the language bar
reflects the code SQLQuack ships.
Snowflake rejects a view's SECURE together with any other property in
one ALTER VIEW SET or UNSET, and RECURSIVE after a temporary scope in
CREATE, CREATE OR ALTER and DEFINE VIEW, although the documented
grammar shows both. Every receipt with either shape is a syntax error,
and every single-property SET and UNSET and every plain RECURSIVE view
parses, so the parser now refuses exactly these shapes.
…and stream grammar

On the pinned evidence the view, stream and semantic view grammar
satisfies 1,256 more obligations and accepts 160 fewer
statements Snowflake rejects as syntax errors, with no regression; re-record
the baseline so check-syntax holds the gain.

Seven corpus statements are now refused, as Snowflake's view grammar
has no such clause: SECURITY DEFINER or INVOKER and SQL SECURITY, a
LOCKING clause, and a view COMMENT without =.
@kvlonge
kvlonge added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit c50747f Nov 12, 2026
13 checks passed
@kvlonge
kvlonge deleted the feat/syntax-ddl-views-streams branch November 12, 2026 10:53
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