Skip to content

fix(snowflake): accept column list after CREATE DYNAMIC TABLE options#90

Merged
lustefaniak merged 1 commit into
mainfrom
lukasz-snowflake-dynamic-table-column-list
Jun 18, 2026
Merged

fix(snowflake): accept column list after CREATE DYNAMIC TABLE options#90
lustefaniak merged 1 commit into
mainfrom
lukasz-snowflake-dynamic-table-column-list

Conversation

@lustefaniak

Copy link
Copy Markdown
Collaborator

Summary

Snowflake permits the column-definition list to appear after the options header (TARGET_LAG / WAREHOUSE / REFRESH_MODE / INITIALIZE) rather than before it. This form is what generator-emitted dynamic-table DDL uses, and it previously failed to parse with Expected end of statement, found: <TYPE>.

CREATE OR REPLACE DYNAMIC TABLE t
    TARGET_LAG = '100000 Days'
    WAREHOUSE = COMPUTE_WH
    REFRESH_MODE = AUTO
    INITIALIZE = ON_CREATE
    ( "SYSTEM_CREATE_DATE" TIMESTAMP_NTZ(9), "ID" VARCHAR(256), ... )
    AS SELECT ...

Root cause

Two issues, both in parse_create_table:

  • The bare-word option value (e.g. INITIALIZE = ON_CREATE) was parsed with parse_expr, which greedily treated the trailing column-list ( as a function call on the value word — swallowing ON_CREATE ( "col" TYPE, ... ). Bare-word option values are now parsed as a plain identifier so the ( is left intact.
  • Nothing picked up a column list that appears after the options loop. The up-front parse_columns() runs before the options and saw the first option keyword, not (. A new arm in the options loop parses the column list when it follows the options.

Coverage

New tests cover: column list + AS SELECT, column-list-only (no AS), the no-whitespace "col"TYPE form, and the types TIMESTAMP_NTZ / TIMESTAMP_TZ / NUMBER / VARCHAR / DATE / BOOLEAN. Full suite green (1187 tests); corpus run shows 0 regressions.

https://claude.ai/code/session_012hHZLhjkLPneyPz7jpMWFS

…BLE options

Snowflake allows the column-definition list to appear after the options
header (TARGET_LAG / WAREHOUSE / REFRESH_MODE / INITIALIZE) rather than
before it:

    CREATE OR REPLACE DYNAMIC TABLE t
        TARGET_LAG = '...' WAREHOUSE = wh REFRESH_MODE = AUTO INITIALIZE = ON_CREATE
        ( "col" TIMESTAMP_NTZ(9), "id" VARCHAR(256), ... )
        AS SELECT ...

This previously failed with "Expected end of statement, found: <TYPE>"
for two reasons:

- The bare-word option value (e.g. INITIALIZE = ON_CREATE) was parsed
  with parse_expr, which greedily treated the following column-list `(`
  as a function call on the value word. Bare-word option values are now
  parsed as a plain identifier so the `(` is left for the column list.
- Nothing picked up a column list appearing after the options loop. The
  up-front parse_columns() ran before the options and saw the first
  option keyword, not `(`. A new arm in the options loop parses the
  column list when it follows the options.

Both the `( ... ) AS SELECT` and column-list-only forms are covered, as
is the no-whitespace `"col"TYPE` form generators emit.

Claude-Session: https://claude.ai/code/session_012hHZLhjkLPneyPz7jpMWFS
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

Corpus Parsing Report

Total: 191713 passed, 1920 failed (99.0% pass rate)

✨ No changes in test results

By Dialect

Dialect Passed Failed Total Pass Rate Delta
ansi 511 69 580 88.1% -
athena 37 1 38 97.4% -
bigquery 42324 113 42437 99.7% -
clickhouse 2723 129 2852 95.5% -
databricks 2965 184 3149 94.2% -
doris 28 12 40 70.0% -
dremio 27 0 27 100.0% -
duckdb 1124 45 1169 96.2% -
exasol 54 7 61 88.5% -
fabric 6 0 6 100.0% -
generic 17 38 55 30.9% -
hive 35 10 45 77.8% -
materialize 6 14 20 30.0% -
mssql 2276 407 2683 84.8% -
mysql 151 37 188 80.3% -
oracle 1046 352 1398 74.8% -
postgres 1172 111 1283 91.3% -
presto 55 8 63 87.3% -
redshift 40503 65 40568 99.8% -
singlestore 141 9 150 94.0% -
snowflake 94741 138 94879 99.9% +2
spark 90 20 110 81.8% -
sqlite 51 16 67 76.1% -
starrocks 29 4 33 87.9% -
teradata 23 20 43 53.5% -
trino 1413 77 1490 94.8% -
tsql 165 34 199 82.9% -

@lustefaniak lustefaniak merged commit e81eaa1 into main Jun 18, 2026
5 checks passed
@lustefaniak lustefaniak deleted the lukasz-snowflake-dynamic-table-column-list branch June 18, 2026 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants