Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions sqlglot/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import logging
import re
import typing as t
from builtins import type as Type
from collections import defaultdict
from collections.abc import Sequence

from sqlglot import exp
from sqlglot.errors import (
Expand All @@ -17,20 +19,17 @@
)
from sqlglot.expressions import apply_index_offset
from sqlglot.helper import ensure_list, i64, seq_get
from sqlglot.trie import new_trie
from sqlglot.time import format_time
from sqlglot.tokens import Token, Tokenizer, TokenType
from sqlglot.trie import TrieResult, in_trie
from collections.abc import Sequence
from builtins import type as Type
from sqlglot.trie import TrieResult, in_trie, new_trie

if t.TYPE_CHECKING:
from sqlglot.expressions import ExpOrStr
from sqlglot._typing import E, BuilderArgs
from sqlglot.dialects.dialect import Dialect, DialectType

from re import Pattern

from sqlglot._typing import BuilderArgs, E
from sqlglot.dialects.dialect import Dialect, DialectType
from sqlglot.expressions import ExpOrStr

T = t.TypeVar("T")
TCeilFloor = t.TypeVar("TCeilFloor", exp.Ceil, exp.Floor)

Expand Down Expand Up @@ -2812,6 +2811,12 @@ def _parse_property(self) -> exp.Expr | list[exp.Expr] | None:
return seq_props

self._retreat(index)
return self._parse_key_value_property()

def _parse_key_value_property(
self, parse_value: t.Callable[[], exp.Expr | None] | None = None
) -> exp.Property | None:
index = self._index
key = self._parse_column()

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

value = self._parse_bitwise() or self._parse_var(any_token=True)
value = (
parse_value()
if parse_value
else self._parse_bitwise() or self._parse_var(any_token=True)
)

# Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
if isinstance(value, exp.Column):
Expand Down
17 changes: 15 additions & 2 deletions sqlglot/parsers/snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@
import typing as t

from sqlglot import exp, parser
from sqlglot.trie import new_trie
from sqlglot.dialects.dialect import (
Dialect,
binary_from_function,
build_default_decimal_type,
build_formatted_time,
build_like,
build_replace_with_optional_replacement,
build_timetostr_or_tochar,
build_trunc,
binary_from_function,
date_trunc_to_time,
map_date_part,
)
from sqlglot.helper import is_date_unit, is_int, seq_get
from sqlglot.tokens import TokenType
from sqlglot.trie import new_trie

if t.TYPE_CHECKING:
from collections.abc import Collection

from sqlglot._typing import B, E
from sqlglot.dialects.dialect import Dialect

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

def _parse_property_before(self) -> exp.Expr | list[exp.Expr] | None:
prop = super()._parse_property_before()
if prop:
return prop

if not self._next or self._next.token_type != TokenType.EQ:
return None

return self._parse_sequence_properties() or self._parse_key_value_property(
self._parse_primary_or_var
)

def _parse_with_constraint(self) -> exp.Expr | None:
if self._prev.token_type != TokenType.WITH:
self._retreat(self._index - 1)
Expand Down
16 changes: 16 additions & 0 deletions tests/dialects/test_snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -4264,6 +4264,11 @@ def test_ddl(self):
self.validate_identity("CREATE SECURE VIEW table1 AS (SELECT a FROM table2)")
self.validate_identity("CREATE OR REPLACE VIEW foo (uid) COPY GRANTS AS (SELECT 1)")
self.validate_identity("CREATE TABLE geospatial_table (id INT, g GEOGRAPHY)")
self.validate_identity("CREATE TABLE t (id INT) CHANGE_TRACKING=TRUE")
self.validate_identity(
"CREATE TABLE t CHANGE_TRACKING=TRUE DATA_RETENTION_TIME_IN_DAYS=1 (id INT)",
"CREATE TABLE t (id INT) CHANGE_TRACKING=TRUE DATA_RETENTION_TIME_IN_DAYS=1",
)
self.validate_identity("CREATE MATERIALIZED VIEW a COMMENT='...' AS SELECT 1 FROM x")
self.validate_identity("CREATE DATABASE mytestdb_clone CLONE mytestdb")
self.validate_identity("CREATE SCHEMA mytestschema_clone CLONE testschema")
Expand Down Expand Up @@ -4299,6 +4304,10 @@ def test_ddl(self):
self.validate_identity(
"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"
)
self.validate_identity(
"CREATE DYNAMIC TABLE dt TARGET_LAG='1 minute' WAREHOUSE=my_wh (id) AS SELECT * FROM bla",
"CREATE DYNAMIC TABLE dt (id) TARGET_LAG='1 minute' WAREHOUSE=my_wh AS SELECT * FROM bla",
)
self.validate_identity(
"ALTER TABLE db_name.schmaName.tblName ADD COLUMN_1 VARCHAR NOT NULL TAG (key1='value_1')"
)
Expand Down Expand Up @@ -5958,6 +5967,13 @@ def test_create_view_copy_grants(self):
"CREATE OR REPLACE MATERIALIZED VIEW FOO (A, B) AS SELECT A, B FROM TBL"
)

def test_create_view_change_tracking(self):
self.validate_identity("CREATE VIEW v (c) CHANGE_TRACKING=TRUE AS SELECT 1 AS c")
self.validate_identity(
"CREATE VIEW my_view CHANGE_TRACKING=TRUE (id) AS SELECT * FROM my_table",
"CREATE VIEW my_view (id) CHANGE_TRACKING=TRUE AS SELECT * FROM my_table",
)

def test_create_view_row_access_policy(self):
self.validate_identity(
"CREATE VIEW v WITH ROW ACCESS POLICY mypolicy ON (col1) AS SELECT col1 FROM t1"
Expand Down
Loading