Skip to content

Snowflake: ORDER/NOORDER sequence property + CREATE/ALTER SEQUENCE grammar - #25

Open
hovaesco wants to merge 79 commits into
mainfrom
lav-1644-sequence-order
Open

Snowflake: ORDER/NOORDER sequence property + CREATE/ALTER SEQUENCE grammar#25
hovaesco wants to merge 79 commits into
mainfrom
lav-1644-sequence-order

Conversation

@hovaesco

Copy link
Copy Markdown

Surfaces ORDER/NOORDER on CREATE SEQUENCE (was accept-and-ignore), accepts the leading-WITH / comma-separated CREATE option form, and adds ALTER SEQUENCE SET ORDER|NOORDER. Backs localstack/snowflake-rs LAV-1644.

sabir-akhadov-localstack and others added 30 commits June 23, 2026 02:57
Thread or_replace through parse_create_role and CreateRole AST node so
CREATE OR REPLACE ROLE parses and round-trips.
The quote-escaper kept an 'already escaped' peek-ahead heuristic that left
an adjacent quote pair untouched. A decoded value containing an embedded
empty-string literal ('') is indistinguishable from a pre-escaped quote, so
it was under-escaped. Quoted-string AST nodes hold the decoded value, so
escaping is the pure inverse: double every quote unconditionally.

Updates the with_unescape(false) round-trip tests, which relied on the
heuristic, to assert the new quote-doubled serialization.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add CATALOG_TABLE_NAME and AUTO_REFRESH to CreateTable for
externally-managed Iceberg tables, accept comma- or space-separated
Iceberg options in free order, relax the BASE_LOCATION requirement for
externally-managed tables, and parse DROP ICEBERG TABLE [IF EXISTS]
<name> [PURGE].
Add a parser test covering the FILE_FORMAT = <name> COPY INTO shorthand
(unquoted ident, quoted string, and the unaffected parenthesized form).

Also reflow a pre-existing unformatted block in parser/mod.rs so
'cargo fmt --all -- --check' (CI codestyle) passes.
…ORMAT objects, WRITE + CREATE TABLE privileges (#12)
parse_literal_string now consumes Token::DollarQuotedString in the
Snowflake dialect, so $$...$$ is accepted anywhere a string literal
is expected (e.g. COMMENT ON ... IS $$...$$).
Give ShowProcedures and ShowFunctions the shared ShowStatementOptions so
they accept the IN { ACCOUNT | DATABASE | SCHEMA } scope clause (and the
bare IN <db>.<schema> form) in addition to the LIKE filter, matching the
other scoped SHOW variants.
A wire placeholder (?, $n, ?name) in a COPY INTO stage-name position is
carried through as a single identifier part so the bound stage reference
can be resolved server-side.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inside a BEGIN … END body the surrounding statement list expects to
consume the trailing ';' itself; leave it via prev_token().
Recognise PUT/GET file-transfer statements inside a scripting BEGIN...END
block as Statement::PutGetFiles so the block parses. Operands are not
modelled: an unquoted file:// path triggers the // single-line-comment
lexer rule, so the statement tail is scanned at the raw-token level up to
its terminator rather than parsed.
Allow a CREATE TABLE column list to name columns without a data type
(e.g. CREATE TABLE t(id) AS SELECT 123), deferring type inference to the
AS query. Gated behind a new Dialect::supports_create_table_optional_column_type
hook, enabled only for SnowflakeDialect; a bare name is accepted only when
immediately followed by ',' or ')' so malformed data types still error.
hovaesco and others added 30 commits July 22, 2026 20:46
Add an additive Expr::LikeAnyAll variant modelling
<subj> [NOT] {LIKE|ILIKE} {ANY|ALL} (p1, ..., pN) [ESCAPE e],
leaving the single-pattern Expr::Like / Expr::ILike shape untouched.
The LIKE/ILIKE infix parser now routes a parenthesized {ANY|ALL} (...)
list into the new variant.
Accept `@stage (FILE_FORMAT => …, PATTERN => …)` table-function args on the
FROM stage of a `COPY INTO <table> FROM (SELECT … FROM @stage (…))` load, and
carry them on a new `CopyIntoSnowflake::from_obj_args` field.
…nator

Accept CREATE [OR REPLACE] STREAM [IF NOT EXISTS] <name> ON VIEW <name> in
addition to ON TABLE <name>, and carry the source kind (Table vs View) on the
CreateStream AST node via a new StreamSourceKind enum. Display round-trips both
forms.
…/RELY)

Extend ConstraintCharacteristics with the remaining three Snowflake constraint
properties — { ENABLE | DISABLE }, { VALIDATE | NOVALIDATE } and
{ RELY | NORELY } — so they can be given in any order alongside DEFERRABLE /
INITIALLY / ENFORCED on inline column constraints, out-of-line table
constraints and ALTER TABLE ADD CONSTRAINT. Parsing is gated on a new
Dialect::supports_informational_constraint_properties hook (Snowflake only),
since ENABLE, DISABLE and VALIDATE are keywords used elsewhere. Display
round-trips the full set.

Also add TableConstraint::clear_characteristics(), for consumers whose target
dialect has no grammar for the characteristics Snowflake accepts.
Counterpart to TableConstraint::clear_characteristics for inline column
constraints, so a consumer targeting a dialect without the constraint
characteristics grammar can drop them from a column list in one call
instead of hand-matching the constraint-bearing ColumnOption variants.
Decode \ooo, \xhh and \uhhhh as Unicode code points, keep \0 as NUL,
collapse unrecognized letters (\a, \Z) to the bare letter, and raise a
tokenizer error on malformed \x / \u — gated behind a new
supports_snowflake_string_literal_escapes dialect capability so other
backslash-escaping dialects keep MySQL/BigQuery semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ame>

Add first-class UNDROP grammar for the statement family. VIEW parses so
the consumer can reject it with Snowflake's unsupported-feature error
rather than a parse error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Token::Placeholder arm to parse_key_value_option so a wire bind
(?, :N, :name) parses as an option value, and accept placeholders in the
special-cased COPY INTO PATTERN / FILES positions and CREATE FILE FORMAT
TYPE. FILES entries and PATTERN now carry ValueWithSpan so a bound ? is
distinguishable from a string literal downstream.
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.

Co-authored-by: Wojciech Padło <wojciech.padlo@localstack.cloud>
…T/UNSET TAG, CREATE WAREHOUSE ... WITH TAG (#21)

Route ALTER WAREHOUSE <name> { SET | UNSET } TAG through parse_alter_object_set_tags
(mirroring the ALTER SCHEMA maybe_parse form) so the tag form requires a name and
non-tag forms keep falling through to parse_alter_warehouse. Add if_exists to
Statement::SetTags (also enables ALTER DATABASE|SCHEMA IF EXISTS ... SET TAG). Add
a trailing-only WITH TAG (...) clause to CREATE WAREHOUSE via a new with_tags field.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…G, CREATE ROLE ... WITH TAG

Route ALTER ROLE <name> { SET | UNSET } TAG through parse_alter_object_set_tags
(mirroring the ALTER SCHEMA maybe_parse form) so the tag form requires a name and
non-tag forms keep falling through. Add if_exists to Statement::SetTags (also
enables ALTER DATABASE|SCHEMA|ROLE IF EXISTS ... SET TAG). Add a trailing-only
WITH TAG (...) clause to CREATE ROLE via a new with_tags field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pinned fork routed any WITH after CREATE SCHEMA into the Trino
option list, so CREATE SCHEMA s WITH TAG (t='v') failed with
Expected: (, found: TAG. Intercept the inline [WITH] TAG (...) clause
before the Trino WITH branch and record it on CreateSchema.with_tags,
mirroring CREATE DATABASE. WITH MANAGED ACCESS and WITH (k='v') are
unaffected.
…TAG, CREATE STAGE ... WITH TAG (#24)

Route ALTER STAGE <name> { SET | UNSET } TAG through parse_alter_object_set_tags
(mirroring the ALTER WAREHOUSE maybe_parse form) so the tag form requires a name
and non-tag forms keep falling through to parse_alter_stage. Add a trailing-only
WITH TAG (...) clause to CREATE STAGE via a new with_tags field.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	src/dialect/snowflake.rs
…ting bare assignment

A bare scripting assignment (var := expr) parsed its RHS with parse_expr,
which rejects a parenthesized EXECUTE IMMEDIATE / SHOW statement. Route it
through the same payload parser a RESULTSET declaration initializer uses so
r := (EXECUTE IMMEDIATE '<sql>') parses into an Expr::Subquery whose body is
SetExpr::Execute, matching the declaration-initializer shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parse_returns_table_columns now uses parse_comma_separated0 so an empty
(or whitespace-only) parenthesized column list parses to
Table(Some(vec![])) instead of erroring. Bare RETURNS TABLE without
parentheses is unchanged (Table(None)).
… WITH/comma options; ALTER SEQUENCE SET ORDER|NOORDER
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.

4 participants