Skip to content

Commit 523f7c2

Browse files
authored
feat(snowflake): support properties before column definitions in CREATE statements (#7787)
* feat(snowflake): support properties before the column definitions in CREATE * improve the test by covering two properties * key=value plus parantheses should not parse as function calls * refactor
1 parent 9f18b77 commit 523f7c2

3 files changed

Lines changed: 42 additions & 3 deletions

File tree

sqlglot/parser.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2812,6 +2812,12 @@ def _parse_property(self) -> exp.Expr | list[exp.Expr] | None:
28122812
return seq_props
28132813

28142814
self._retreat(index)
2815+
return self._parse_key_value_property()
2816+
2817+
def _parse_key_value_property(
2818+
self, parse_value: t.Callable[[], exp.Expr | None] | None = None
2819+
) -> exp.Property | None:
2820+
index = self._index
28152821
key = self._parse_column()
28162822

28172823
if not self._match(TokenType.EQ):
@@ -2822,7 +2828,11 @@ def _parse_property(self) -> exp.Expr | list[exp.Expr] | None:
28222828
if isinstance(key, exp.Column):
28232829
key = key.to_dot() if len(key.parts) > 1 else exp.var(key.name)
28242830

2825-
value = self._parse_bitwise() or self._parse_var(any_token=True)
2831+
value = (
2832+
parse_value()
2833+
if parse_value
2834+
else self._parse_bitwise() or self._parse_var(any_token=True)
2835+
)
28262836

28272837
# Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
28282838
if isinstance(value, exp.Column):

sqlglot/parsers/snowflake.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,25 @@
33
import typing as t
44

55
from sqlglot import exp, parser
6-
from sqlglot.trie import new_trie
76
from sqlglot.dialects.dialect import (
87
Dialect,
8+
binary_from_function,
99
build_default_decimal_type,
1010
build_formatted_time,
1111
build_like,
1212
build_replace_with_optional_replacement,
1313
build_timetostr_or_tochar,
1414
build_trunc,
15-
binary_from_function,
1615
date_trunc_to_time,
1716
map_date_part,
1817
)
1918
from sqlglot.helper import is_date_unit, is_int, seq_get
2019
from sqlglot.tokens import TokenType
20+
from sqlglot.trie import new_trie
2121

2222
if t.TYPE_CHECKING:
2323
from collections.abc import Collection
24+
2425
from sqlglot._typing import B, E
2526
from sqlglot.dialects.dialect import Dialect
2627

@@ -976,6 +977,18 @@ def _negate_range(self, this: exp.Expr | None = None) -> exp.Expr | None:
976977
def _parse_tag(self) -> exp.Tags:
977978
return self.expression(exp.Tags(expressions=self._parse_wrapped_csv(self._parse_property)))
978979

980+
def _parse_property_before(self) -> exp.Expr | list[exp.Expr] | None:
981+
prop = super()._parse_property_before()
982+
if prop:
983+
return prop
984+
985+
if not self._next or self._next.token_type != TokenType.EQ:
986+
return None
987+
988+
return self._parse_sequence_properties() or self._parse_key_value_property(
989+
self._parse_primary_or_var
990+
)
991+
979992
def _parse_with_constraint(self) -> exp.Expr | None:
980993
if self._prev.token_type != TokenType.WITH:
981994
self._retreat(self._index - 1)

tests/dialects/test_snowflake.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4264,6 +4264,11 @@ def test_ddl(self):
42644264
self.validate_identity("CREATE SECURE VIEW table1 AS (SELECT a FROM table2)")
42654265
self.validate_identity("CREATE OR REPLACE VIEW foo (uid) COPY GRANTS AS (SELECT 1)")
42664266
self.validate_identity("CREATE TABLE geospatial_table (id INT, g GEOGRAPHY)")
4267+
self.validate_identity("CREATE TABLE t (id INT) CHANGE_TRACKING=TRUE")
4268+
self.validate_identity(
4269+
"CREATE TABLE t CHANGE_TRACKING=TRUE DATA_RETENTION_TIME_IN_DAYS=1 (id INT)",
4270+
"CREATE TABLE t (id INT) CHANGE_TRACKING=TRUE DATA_RETENTION_TIME_IN_DAYS=1",
4271+
)
42674272
self.validate_identity("CREATE MATERIALIZED VIEW a COMMENT='...' AS SELECT 1 FROM x")
42684273
self.validate_identity("CREATE DATABASE mytestdb_clone CLONE mytestdb")
42694274
self.validate_identity("CREATE SCHEMA mytestschema_clone CLONE testschema")
@@ -4299,6 +4304,10 @@ def test_ddl(self):
42994304
self.validate_identity(
43004305
"CREATE DYNAMIC TABLE product (pre_tax_profit, taxes, after_tax_profit) TARGET_LAG='20 minutes' WAREHOUSE=mywh AS SELECT revenue - cost, (revenue - cost) * tax_rate, (revenue - cost) * (1.0 - tax_rate) FROM staging_table"
43014306
)
4307+
self.validate_identity(
4308+
"CREATE DYNAMIC TABLE dt TARGET_LAG='1 minute' WAREHOUSE=my_wh (id) AS SELECT * FROM bla",
4309+
"CREATE DYNAMIC TABLE dt (id) TARGET_LAG='1 minute' WAREHOUSE=my_wh AS SELECT * FROM bla",
4310+
)
43024311
self.validate_identity(
43034312
"ALTER TABLE db_name.schmaName.tblName ADD COLUMN_1 VARCHAR NOT NULL TAG (key1='value_1')"
43044313
)
@@ -5960,6 +5969,13 @@ def test_create_view_copy_grants(self):
59605969
"CREATE OR REPLACE MATERIALIZED VIEW FOO (A, B) AS SELECT A, B FROM TBL"
59615970
)
59625971

5972+
def test_create_view_change_tracking(self):
5973+
self.validate_identity("CREATE VIEW v (c) CHANGE_TRACKING=TRUE AS SELECT 1 AS c")
5974+
self.validate_identity(
5975+
"CREATE VIEW my_view CHANGE_TRACKING=TRUE (id) AS SELECT * FROM my_table",
5976+
"CREATE VIEW my_view (id) CHANGE_TRACKING=TRUE AS SELECT * FROM my_table",
5977+
)
5978+
59635979
def test_create_view_row_access_policy(self):
59645980
self.validate_identity(
59655981
"CREATE VIEW v WITH ROW ACCESS POLICY mypolicy ON (col1) AS SELECT col1 FROM t1"

0 commit comments

Comments
 (0)