Skip to content

Snowflake: parse the EXTERNAL TABLE statement family - #20

Merged
wojpadlo merged 1 commit into
lav-1503-pipefrom
snowflake/external-tables
Aug 12, 2026
Merged

Snowflake: parse the EXTERNAL TABLE statement family#20
wojpadlo merged 1 commit into
lav-1503-pipefrom
snowflake/external-tables

Conversation

@wojpadlo

Copy link
Copy Markdown

Summary

Teaches the Snowflake dialect the full EXTERNAL TABLE statement family, which previously died in the parser (the fork's parse_create_external_table is Hive-shaped and the Snowflake dialect never intercepted CREATE EXTERNAL TABLE). Support for the LocalStack Snowflake emulator (snowflake-rs LAV-1574 / ADR 074 §6).

The Hive CREATE EXTERNAL TABLE … STORED AS … LOCATION '<path>' form — which other dialects, and Snowflake itself, rely on — is preserved: the Snowflake parser inspects the post-column tail and hands off to the Hive grammar when it is Hive-shaped.

What parses now (Snowflake dialect)

  • CREATE [OR REPLACE] EXTERNAL TABLE [IF NOT EXISTS] with virtual columns (<col> <type> AS <expr>, parenthesised or bare), LOCATION=@stage/…, FILE_FORMAT=(…) (inline and FORMAT_NAME), PATTERN, REFRESH_ON_CREATE, AUTO_REFRESH, PARTITION BY, PARTITION_TYPE, TABLE_FORMAT, AWS_SNS_TOPIC, COPY GRANTS, WITH TAG, WITH ROW ACCESS POLICY, COMMENT.
  • DROP EXTERNAL TABLE and DESC[RIBE] EXTERNAL TABLE — new ObjectType::ExternalTable / DescribeObjectType::ExternalTable.
  • ALTER EXTERNAL TABLE … ADD FILES / REMOVE FILES / SET AUTO_REFRESH / ADD PARTITION / DROP PARTITION, alongside the existing REFRESH ['subpath'].

Deferred/rejected members (TABLE_FORMAT = DELTA, PARTITION_TYPE = USER_SPECIFIED, ADD/DROP PARTITION) deliberately parse so the downstream consumer can reject them with a Snowflake-shaped error instead of aborting the rest of a batch in the parser.

Implementation notes

  • New CreateTable fields (pattern, refresh_on_create, partition_type, table_format, aws_sns_topic) and AlterTableOperation variants (AddFiles, RemoveFiles, SetAutoRefresh, AddExternalPartition, DropExternalPartition) carry the Snowflake-only clauses; Display round-trips every member back to the same AST.
  • New keywords: AWS_SNS_TOPIC, PARTITION_TYPE, REFRESH_ON_CREATE, USER_SPECIFIED.
  • Also fills in several stale full-CreateTable struct literals in the duckdb/mssql/postgres test suites so cargo test compiles on this base.

Tests

parse_snowflake_external_table_family_roundtrips exercises every member of the family through a Display → reparse round trip. Full cargo test is green.

🤖 Generated with Claude Code

Snowflake's `CREATE EXTERNAL TABLE` grammar did not parse: the fork's
`parse_create_external_table` is the Hive-shaped one and the Snowflake
dialect never intercepted `CREATE EXTERNAL TABLE`, so realistic DDL died
in the parser. This teaches the Snowflake dialect the whole external-table
statement family, while leaving the Hive path (which other dialects — and
Snowflake itself, for the `STORED AS … LOCATION '<path>'` form — rely on)
untouched via a tail-shape fallback.

Added under the Snowflake dialect:

* `CREATE [OR REPLACE] EXTERNAL TABLE [IF NOT EXISTS]` with virtual column
  definitions (`<col> <type> AS <expr>`, parenthesised or bare), `LOCATION`,
  `FILE_FORMAT` (named and inline), `PATTERN`, `REFRESH_ON_CREATE`,
  `AUTO_REFRESH`, `PARTITION BY`, `PARTITION_TYPE`, `TABLE_FORMAT`,
  `AWS_SNS_TOPIC`, `COPY GRANTS`, tags, row-access policy and `COMMENT`.
* `DROP EXTERNAL TABLE` and `DESC[RIBE] EXTERNAL TABLE` (new
  `ObjectType::ExternalTable` / `DescribeObjectType::ExternalTable`).
* `ALTER EXTERNAL TABLE … ADD FILES / REMOVE FILES / SET AUTO_REFRESH /
  ADD PARTITION / DROP PARTITION`, alongside the existing `REFRESH`.

New `CreateTable` fields (`pattern`, `refresh_on_create`, `partition_type`,
`table_format`, `aws_sns_topic`) and `AlterTableOperation` variants carry the
Snowflake-only clauses; `Display` round-trips every member back to the same
AST. The `ADD PARTITION` column/value pairs use a dedicated
`ExternalTablePartitionColumn` struct so the `visitor` derive is satisfied.
Deferred/rejected members (`TABLE_FORMAT = DELTA`,
`PARTITION_TYPE = USER_SPECIFIED`, `ADD`/`DROP PARTITION`) parse so they can be
rejected downstream rather than aborting a batch in the parser.

Also brings the branch to a green CI baseline: fills in several stale full
`CreateTable` struct literals in the duckdb/mssql/postgres tests, applies
`cargo fmt`, clears `clippy -D warnings`, and fixes two rustdoc errors.
@wojpadlo
wojpadlo force-pushed the snowflake/external-tables branch from 1df3bbd to 275d301 Compare August 12, 2026 12:45
@wojpadlo
wojpadlo merged commit 9554a18 into lav-1503-pipe Aug 12, 2026
19 checks passed
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