diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 05:35:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 05:35:50 +0000 |
commit | 8f1b330983bddb35e2ec61a5667a84318bad88ef (patch) | |
tree | 32b74e964839c34014d31ec828fc6521323b9edc /sqlglot/parser.py | |
parent | Adding upstream version 23.13.7. (diff) | |
download | sqlglot-8f1b330983bddb35e2ec61a5667a84318bad88ef.tar.xz sqlglot-8f1b330983bddb35e2ec61a5667a84318bad88ef.zip |
Adding upstream version 23.16.0.upstream/23.16.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sqlglot/parser.py')
-rw-r--r-- | sqlglot/parser.py | 314 |
1 files changed, 257 insertions, 57 deletions
diff --git a/sqlglot/parser.py b/sqlglot/parser.py index b0a2516..67ffd8f 100644 --- a/sqlglot/parser.py +++ b/sqlglot/parser.py @@ -61,6 +61,23 @@ def build_logarithm(args: t.List, dialect: Dialect) -> exp.Func: return (exp.Ln if dialect.parser_class.LOG_DEFAULTS_TO_LN else exp.Log)(this=this) +def build_hex(args: t.List, dialect: Dialect) -> exp.Hex | exp.LowerHex: + arg = seq_get(args, 0) + return exp.LowerHex(this=arg) if dialect.HEX_LOWERCASE else exp.Hex(this=arg) + + +def build_lower(args: t.List) -> exp.Lower | exp.Hex: + # LOWER(HEX(..)) can be simplified to LowerHex to simplify its transpilation + arg = seq_get(args, 0) + return exp.LowerHex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Lower(this=arg) + + +def build_upper(args: t.List) -> exp.Upper | exp.Hex: + # UPPER(HEX(..)) can be simplified to Hex to simplify its transpilation + arg = seq_get(args, 0) + return exp.Hex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Upper(this=arg) + + def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]: def _builder(args: t.List, dialect: Dialect) -> E: expression = expr_type( @@ -74,6 +91,17 @@ def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Di return _builder +def build_mod(args: t.List) -> exp.Mod: + this = seq_get(args, 0) + expression = seq_get(args, 1) + + # Wrap the operands if they are binary nodes, e.g. MOD(a + 1, 7) -> (a + 1) % 7 + this = exp.Paren(this=this) if isinstance(this, exp.Binary) else this + expression = exp.Paren(this=expression) if isinstance(expression, exp.Binary) else expression + + return exp.Mod(this=this, expression=expression) + + class _Parser(type): def __new__(cls, clsname, bases, attrs): klass = super().__new__(cls, clsname, bases, attrs) @@ -123,7 +151,7 @@ class Parser(metaclass=_Parser): "LOG": build_logarithm, "LOG2": lambda args: exp.Log(this=exp.Literal.number(2), expression=seq_get(args, 0)), "LOG10": lambda args: exp.Log(this=exp.Literal.number(10), expression=seq_get(args, 0)), - "MOD": lambda args: exp.Mod(this=seq_get(args, 0), expression=seq_get(args, 1)), + "MOD": build_mod, "TIME_TO_TIME_STR": lambda args: exp.Cast( this=seq_get(args, 0), to=exp.DataType(this=exp.DataType.Type.TEXT), @@ -137,6 +165,10 @@ class Parser(metaclass=_Parser): length=exp.Literal.number(10), ), "VAR_MAP": build_var_map, + "LOWER": build_lower, + "UPPER": build_upper, + "HEX": build_hex, + "TO_HEX": build_hex, } NO_PAREN_FUNCTIONS = { @@ -295,22 +327,23 @@ class Parser(metaclass=_Parser): DB_CREATABLES = { TokenType.DATABASE, - TokenType.SCHEMA, - TokenType.TABLE, - TokenType.VIEW, - TokenType.MODEL, TokenType.DICTIONARY, + TokenType.MODEL, + TokenType.SCHEMA, TokenType.SEQUENCE, TokenType.STORAGE_INTEGRATION, + TokenType.TABLE, + TokenType.TAG, + TokenType.VIEW, } CREATABLES = { TokenType.COLUMN, TokenType.CONSTRAINT, + TokenType.FOREIGN_KEY, TokenType.FUNCTION, TokenType.INDEX, TokenType.PROCEDURE, - TokenType.FOREIGN_KEY, *DB_CREATABLES, } @@ -373,6 +406,7 @@ class Parser(metaclass=_Parser): TokenType.REFRESH, TokenType.REPLACE, TokenType.RIGHT, + TokenType.ROLLUP, TokenType.ROW, TokenType.ROWS, TokenType.SEMI, @@ -467,7 +501,6 @@ class Parser(metaclass=_Parser): } EQUALITY = { - TokenType.COLON_EQ: exp.PropertyEQ, TokenType.EQ: exp.EQ, TokenType.NEQ: exp.NEQ, TokenType.NULLSAFE_EQ: exp.NullSafeEQ, @@ -653,6 +686,7 @@ class Parser(metaclass=_Parser): kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False), this=self._parse_table(schema=False), ), + TokenType.SEMICOLON: lambda self: self.expression(exp.Semicolon), } UNARY_PARSERS = { @@ -700,7 +734,12 @@ class Parser(metaclass=_Parser): TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False), TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(), TokenType.STAR: lambda self, _: self.expression( - exp.Star, **{"except": self._parse_except(), "replace": self._parse_replace()} + exp.Star, + **{ + "except": self._parse_star_op("EXCEPT", "EXCLUDE"), + "replace": self._parse_star_op("REPLACE"), + "rename": self._parse_star_op("RENAME"), + }, ), } @@ -729,6 +768,9 @@ class Parser(metaclass=_Parser): } PROPERTY_PARSERS: t.Dict[str, t.Callable] = { + "ALLOWED_VALUES": lambda self: self.expression( + exp.AllowedValuesProperty, expressions=self._parse_csv(self._parse_primary) + ), "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty), "AUTO": lambda self: self._parse_auto_property(), "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty), @@ -748,6 +790,7 @@ class Parser(metaclass=_Parser): "CONTAINS": lambda self: self._parse_contains_property(), "COPY": lambda self: self._parse_copy_property(), "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs), + "DATA_DELETION": lambda self: self._parse_data_deletion_property(), "DEFINER": lambda self: self._parse_definer(), "DETERMINISTIC": lambda self: self.expression( exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE") @@ -796,6 +839,7 @@ class Parser(metaclass=_Parser): "READS": lambda self: self._parse_reads_property(), "REMOTE": lambda self: self._parse_remote_with_connection(), "RETURNS": lambda self: self._parse_returns(), + "STRICT": lambda self: self.expression(exp.StrictProperty), "ROW": lambda self: self._parse_row(), "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty), "SAMPLE": lambda self: self.expression( @@ -900,6 +944,14 @@ class Parser(metaclass=_Parser): "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()), "DROP": lambda self: self._parse_alter_table_drop(), "RENAME": lambda self: self._parse_alter_table_rename(), + "SET": lambda self: self._parse_alter_table_set(), + } + + ALTER_ALTER_PARSERS = { + "DISTKEY": lambda self: self._parse_alter_diststyle(), + "DISTSTYLE": lambda self: self._parse_alter_diststyle(), + "SORTKEY": lambda self: self._parse_alter_sortkey(), + "COMPOUND": lambda self: self._parse_alter_sortkey(compound=True), } SCHEMA_UNNAMED_CONSTRAINTS = { @@ -990,6 +1042,8 @@ class Parser(metaclass=_Parser): exp.DataType.Type.JSON: lambda self, this, _: self.expression(exp.ParseJSON, this=this), } + TYPE_CONVERTER: t.Dict[exp.DataType.Type, t.Callable[[exp.DataType], exp.DataType]] = {} + DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN} PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE} @@ -1084,6 +1138,9 @@ class Parser(metaclass=_Parser): # Whether the table sample clause expects CSV syntax TABLESAMPLE_CSV = False + # The default method used for table sampling + DEFAULT_SAMPLING_METHOD: t.Optional[str] = None + # Whether the SET command needs a delimiter (e.g. "=") for assignments SET_REQUIRES_ASSIGNMENT_DELIMITER = True @@ -1228,6 +1285,9 @@ class Parser(metaclass=_Parser): for i, token in enumerate(raw_tokens): if token.token_type == TokenType.SEMICOLON: + if token.comments: + chunks.append([token]) + if i < total - 1: chunks.append([]) else: @@ -1471,7 +1531,7 @@ class Parser(metaclass=_Parser): if self._match_set(self.STATEMENT_PARSERS): return self.STATEMENT_PARSERS[self._prev.token_type](self) - if self._match_set(Tokenizer.COMMANDS): + if self._match_set(self.dialect.tokenizer.COMMANDS): return self._parse_command() expression = self._parse_expression() @@ -1492,6 +1552,8 @@ class Parser(metaclass=_Parser): schema=True, is_db_reference=self._prev.token_type == TokenType.SCHEMA ) + cluster = self._parse_on_property() if self._match(TokenType.ON) else None + if self._match(TokenType.L_PAREN, advance=False): expressions = self._parse_wrapped_csv(self._parse_types) else: @@ -1503,12 +1565,13 @@ class Parser(metaclass=_Parser): exists=if_exists, this=table, expressions=expressions, - kind=kind, + kind=kind.upper(), temporary=temporary, materialized=materialized, cascade=self._match_text_seq("CASCADE"), constraints=self._match_text_seq("CONSTRAINTS"), purge=self._match_text_seq("PURGE"), + cluster=cluster, ) def _parse_exists(self, not_: bool = False) -> t.Optional[bool]: @@ -1651,7 +1714,7 @@ class Parser(metaclass=_Parser): exp.Clone, this=self._parse_table(schema=True), shallow=shallow, copy=copy ) - if self._curr: + if self._curr and not self._match_set((TokenType.R_PAREN, TokenType.COMMA), advance=False): return self._parse_as_command(start) return self.expression( @@ -1678,6 +1741,7 @@ class Parser(metaclass=_Parser): index = self._index while self._curr: + self._match(TokenType.COMMA) if self._match_text_seq("INCREMENT"): self._match_text_seq("BY") self._match_text_seq("=") @@ -1822,23 +1886,65 @@ class Parser(metaclass=_Parser): return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE")) - def _parse_system_versioning_property(self) -> exp.WithSystemVersioningProperty: - self._match_pair(TokenType.EQ, TokenType.ON) + def _parse_retention_period(self) -> exp.Var: + # Parse TSQL's HISTORY_RETENTION_PERIOD: {INFINITE | <number> DAY | DAYS | MONTH ...} + number = self._parse_number() + number_str = f"{number} " if number else "" + unit = self._parse_var(any_token=True) + return exp.var(f"{number_str}{unit}") + + def _parse_system_versioning_property( + self, with_: bool = False + ) -> exp.WithSystemVersioningProperty: + self._match(TokenType.EQ) + prop = self.expression( + exp.WithSystemVersioningProperty, + **{ # type: ignore + "on": True, + "with": with_, + }, + ) + + if self._match_text_seq("OFF"): + prop.set("on", False) + return prop - prop = self.expression(exp.WithSystemVersioningProperty) + self._match(TokenType.ON) if self._match(TokenType.L_PAREN): - self._match_text_seq("HISTORY_TABLE", "=") - prop.set("this", self._parse_table_parts()) + while self._curr and not self._match(TokenType.R_PAREN): + if self._match_text_seq("HISTORY_TABLE", "="): + prop.set("this", self._parse_table_parts()) + elif self._match_text_seq("DATA_CONSISTENCY_CHECK", "="): + prop.set("data_consistency", self._advance_any() and self._prev.text.upper()) + elif self._match_text_seq("HISTORY_RETENTION_PERIOD", "="): + prop.set("retention_period", self._parse_retention_period()) - if self._match(TokenType.COMMA): - self._match_text_seq("DATA_CONSISTENCY_CHECK", "=") - prop.set("expression", self._advance_any() and self._prev.text.upper()) + self._match(TokenType.COMMA) - self._match_r_paren() + return prop + + def _parse_data_deletion_property(self) -> exp.DataDeletionProperty: + self._match(TokenType.EQ) + on = self._match_text_seq("ON") or not self._match_text_seq("OFF") + prop = self.expression(exp.DataDeletionProperty, on=on) + + if self._match(TokenType.L_PAREN): + while self._curr and not self._match(TokenType.R_PAREN): + if self._match_text_seq("FILTER_COLUMN", "="): + prop.set("filter_column", self._parse_column()) + elif self._match_text_seq("RETENTION_PERIOD", "="): + prop.set("retention_period", self._parse_retention_period()) + + self._match(TokenType.COMMA) return prop def _parse_with_property(self) -> t.Optional[exp.Expression] | t.List[exp.Expression]: + if self._match_text_seq("(", "SYSTEM_VERSIONING"): + prop = self._parse_system_versioning_property(with_=True) + self._match_r_paren() + return prop + if self._match(TokenType.L_PAREN, advance=False): return self._parse_wrapped_properties() @@ -1853,6 +1959,9 @@ class Parser(metaclass=_Parser): elif self._match_text_seq("NO", "DATA"): return self._parse_withdata(no=True) + if self._match(TokenType.SERDE_PROPERTIES, advance=False): + return self._parse_serde_properties(with_=True) + if not self._next: return None @@ -2201,6 +2310,7 @@ class Parser(metaclass=_Parser): def _parse_returns(self) -> exp.ReturnsProperty: value: t.Optional[exp.Expression] + null = None is_table = self._match(TokenType.TABLE) if is_table: @@ -2214,10 +2324,13 @@ class Parser(metaclass=_Parser): self.raise_error("Expecting >") else: value = self._parse_schema(exp.var("TABLE")) + elif self._match_text_seq("NULL", "ON", "NULL", "INPUT"): + null = True + value = None else: value = self._parse_types() - return self.expression(exp.ReturnsProperty, this=value, is_table=is_table) + return self.expression(exp.ReturnsProperty, this=value, is_table=is_table, null=null) def _parse_describe(self) -> exp.Describe: kind = self._match_set(self.CREATABLES) and self._prev.text @@ -2340,6 +2453,21 @@ class Parser(metaclass=_Parser): return None return self._parse_row_format() + def _parse_serde_properties(self, with_: bool = False) -> t.Optional[exp.SerdeProperties]: + index = self._index + with_ = with_ or self._match_text_seq("WITH") + + if not self._match(TokenType.SERDE_PROPERTIES): + self._retreat(index) + return None + return self.expression( + exp.SerdeProperties, + **{ # type: ignore + "expressions": self._parse_wrapped_properties(), + "with": with_, + }, + ) + def _parse_row_format( self, match_row: bool = False ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]: @@ -2349,11 +2477,7 @@ class Parser(metaclass=_Parser): if self._match_text_seq("SERDE"): this = self._parse_string() - serde_properties = None - if self._match(TokenType.SERDE_PROPERTIES): - serde_properties = self.expression( - exp.SerdeProperties, expressions=self._parse_wrapped_properties() - ) + serde_properties = self._parse_serde_properties() return self.expression( exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties @@ -2672,9 +2796,7 @@ class Parser(metaclass=_Parser): ) def _implicit_unnests_to_explicit(self, this: E) -> E: - from sqlglot.optimizer.normalize_identifiers import ( - normalize_identifiers as _norm, - ) + from sqlglot.optimizer.normalize_identifiers import normalize_identifiers as _norm refs = {_norm(this.args["from"].this.copy(), dialect=self.dialect).alias_or_name} for i, join in enumerate(this.args.get("joins") or []): @@ -3366,6 +3488,9 @@ class Parser(metaclass=_Parser): elif self._match_texts(("SEED", "REPEATABLE")): seed = self._parse_wrapped(self._parse_number) + if not method and self.DEFAULT_SAMPLING_METHOD: + method = exp.var(self.DEFAULT_SAMPLING_METHOD) + return self.expression( exp.TableSample, expressions=expressions, @@ -3519,7 +3644,11 @@ class Parser(metaclass=_Parser): elements["all"] = False while True: - expressions = self._parse_csv(self._parse_conjunction) + expressions = self._parse_csv( + lambda: None + if self._match(TokenType.ROLLUP, advance=False) + else self._parse_conjunction() + ) if expressions: elements["expressions"].extend(expressions) @@ -3817,7 +3946,24 @@ class Parser(metaclass=_Parser): return self._parse_alias(self._parse_conjunction()) def _parse_conjunction(self) -> t.Optional[exp.Expression]: - return self._parse_tokens(self._parse_equality, self.CONJUNCTION) + this = self._parse_equality() + + if self._match(TokenType.COLON_EQ): + this = self.expression( + exp.PropertyEQ, + this=this, + comments=self._prev_comments, + expression=self._parse_conjunction(), + ) + + while self._match_set(self.CONJUNCTION): + this = self.expression( + self.CONJUNCTION[self._prev.token_type], + this=this, + comments=self._prev_comments, + expression=self._parse_equality(), + ) + return this def _parse_equality(self) -> t.Optional[exp.Expression]: return self._parse_tokens(self._parse_comparison, self.EQUALITY) @@ -4061,6 +4207,7 @@ class Parser(metaclass=_Parser): ) -> t.Optional[exp.Expression]: index = self._index + this: t.Optional[exp.Expression] = None prefix = self._match_text_seq("SYSUDTLIB", ".") if not self._match_set(self.TYPE_TOKENS): @@ -4081,7 +4228,7 @@ class Parser(metaclass=_Parser): while self._match(TokenType.DOT): type_name = f"{type_name}.{self._advance_any() and self._prev.text}" - return exp.DataType.build(type_name, udt=True) + this = exp.DataType.build(type_name, udt=True) else: self._retreat(self._index - 1) return None @@ -4134,7 +4281,6 @@ class Parser(metaclass=_Parser): maybe_func = True - this: t.Optional[exp.Expression] = None values: t.Optional[t.List[exp.Expression]] = None if nested and self._match(TokenType.LT): @@ -4203,10 +4349,17 @@ class Parser(metaclass=_Parser): values=values, prefix=prefix, ) + elif expressions: + this.set("expressions", expressions) while self._match_pair(TokenType.L_BRACKET, TokenType.R_BRACKET): this = exp.DataType(this=exp.DataType.Type.ARRAY, expressions=[this], nested=True) + if self.TYPE_CONVERTER and isinstance(this.this, exp.DataType.Type): + converter = self.TYPE_CONVERTER.get(this.this) + if converter: + this = converter(t.cast(exp.DataType, this)) + return this def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]: @@ -4326,9 +4479,7 @@ class Parser(metaclass=_Parser): if not this and self._match(TokenType.R_PAREN, advance=False): this = self.expression(exp.Tuple) elif isinstance(this, exp.UNWRAPPED_QUERIES): - this = self._parse_set_operations( - self._parse_subquery(this=this, parse_alias=False) - ) + this = self._parse_subquery(this=this, parse_alias=False) elif isinstance(this, exp.Subquery): this = self._parse_subquery( this=self._parse_set_operations(this), parse_alias=False @@ -5625,13 +5776,8 @@ class Parser(metaclass=_Parser): return self._parse_placeholder() def _parse_parameter(self) -> exp.Parameter: - self._match(TokenType.L_BRACE) this = self._parse_identifier() or self._parse_primary_or_var() - expression = self._match(TokenType.COLON) and ( - self._parse_identifier() or self._parse_primary_or_var() - ) - self._match(TokenType.R_BRACE) - return self.expression(exp.Parameter, this=this, expression=expression) + return self.expression(exp.Parameter, this=this) def _parse_placeholder(self) -> t.Optional[exp.Expression]: if self._match_set(self.PLACEHOLDER_PARSERS): @@ -5641,23 +5787,14 @@ class Parser(metaclass=_Parser): self._advance(-1) return None - def _parse_except(self) -> t.Optional[t.List[exp.Expression]]: - if not self._match(TokenType.EXCEPT): - return None - if self._match(TokenType.L_PAREN, advance=False): - return self._parse_wrapped_csv(self._parse_column) - - except_column = self._parse_column() - return [except_column] if except_column else None - - def _parse_replace(self) -> t.Optional[t.List[exp.Expression]]: - if not self._match(TokenType.REPLACE): + def _parse_star_op(self, *keywords: str) -> t.Optional[t.List[exp.Expression]]: + if not self._match_texts(keywords): return None if self._match(TokenType.L_PAREN, advance=False): return self._parse_wrapped_csv(self._parse_expression) - replace_expression = self._parse_expression() - return [replace_expression] if replace_expression else None + expression = self._parse_expression() + return [expression] if expression else None def _parse_csv( self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA @@ -5812,7 +5949,12 @@ class Parser(metaclass=_Parser): return self._parse_wrapped_csv(self._parse_field_def, optional=True) return self._parse_wrapped_csv(self._parse_add_column, optional=True) - def _parse_alter_table_alter(self) -> exp.AlterColumn: + def _parse_alter_table_alter(self) -> t.Optional[exp.Expression]: + if self._match_texts(self.ALTER_ALTER_PARSERS): + return self.ALTER_ALTER_PARSERS[self._prev.text.upper()](self) + + # Many dialects support the ALTER [COLUMN] syntax, so if there is no + # keyword after ALTER we default to parsing this statement self._match(TokenType.COLUMN) column = self._parse_field(any_token=True) @@ -5833,6 +5975,27 @@ class Parser(metaclass=_Parser): using=self._match(TokenType.USING) and self._parse_conjunction(), ) + def _parse_alter_diststyle(self) -> exp.AlterDistStyle: + if self._match_texts(("ALL", "EVEN", "AUTO")): + return self.expression(exp.AlterDistStyle, this=exp.var(self._prev.text.upper())) + + self._match_text_seq("KEY", "DISTKEY") + return self.expression(exp.AlterDistStyle, this=self._parse_column()) + + def _parse_alter_sortkey(self, compound: t.Optional[bool] = None) -> exp.AlterSortKey: + if compound: + self._match_text_seq("SORTKEY") + + if self._match(TokenType.L_PAREN, advance=False): + return self.expression( + exp.AlterSortKey, expressions=self._parse_wrapped_id_vars(), compound=compound + ) + + self._match_texts(("AUTO", "NONE")) + return self.expression( + exp.AlterSortKey, this=exp.var(self._prev.text.upper()), compound=compound + ) + def _parse_alter_table_drop(self) -> t.List[exp.Expression]: index = self._index - 1 @@ -5858,6 +6021,41 @@ class Parser(metaclass=_Parser): self._match_text_seq("TO") return self.expression(exp.RenameTable, this=self._parse_table(schema=True)) + def _parse_alter_table_set(self) -> exp.AlterSet: + alter_set = self.expression(exp.AlterSet) + + if self._match(TokenType.L_PAREN, advance=False) or self._match_text_seq( + "TABLE", "PROPERTIES" + ): + alter_set.set("expressions", self._parse_wrapped_csv(self._parse_conjunction)) + elif self._match_text_seq("FILESTREAM_ON", advance=False): + alter_set.set("expressions", [self._parse_conjunction()]) + elif self._match_texts(("LOGGED", "UNLOGGED")): + alter_set.set("option", exp.var(self._prev.text.upper())) + elif self._match_text_seq("WITHOUT") and self._match_texts(("CLUSTER", "OIDS")): + alter_set.set("option", exp.var(f"WITHOUT {self._prev.text.upper()}")) + elif self._match_text_seq("LOCATION"): + alter_set.set("location", self._parse_field()) + elif self._match_text_seq("ACCESS", "METHOD"): + alter_set.set("access_method", self._parse_field()) + elif self._match_text_seq("TABLESPACE"): + alter_set.set("tablespace", self._parse_field()) + elif self._match_text_seq("FILE", "FORMAT") or self._match_text_seq("FILEFORMAT"): + alter_set.set("file_format", [self._parse_field()]) + elif self._match_text_seq("STAGE_FILE_FORMAT"): + alter_set.set("file_format", self._parse_wrapped_options()) + elif self._match_text_seq("STAGE_COPY_OPTIONS"): + alter_set.set("copy_options", self._parse_wrapped_options()) + elif self._match_text_seq("TAG") or self._match_text_seq("TAGS"): + alter_set.set("tag", self._parse_csv(self._parse_conjunction)) + else: + if self._match_text_seq("SERDE"): + alter_set.set("serde", self._parse_field()) + + alter_set.set("expressions", [self._parse_properties()]) + + return alter_set + def _parse_alter(self) -> exp.AlterTable | exp.Command: start = self._prev @@ -5867,6 +6065,7 @@ class Parser(metaclass=_Parser): exists = self._parse_exists() only = self._match_text_seq("ONLY") this = self._parse_table(schema=True) + cluster = self._parse_on_property() if self._match(TokenType.ON) else None if self._next: self._advance() @@ -5884,6 +6083,7 @@ class Parser(metaclass=_Parser): actions=actions, only=only, options=options, + cluster=cluster, ) return self._parse_as_command(start) @@ -5974,7 +6174,7 @@ class Parser(metaclass=_Parser): if kind in ("GLOBAL", "SESSION") and self._match_text_seq("TRANSACTION"): return self._parse_set_transaction(global_=kind == "GLOBAL") - left = self._parse_primary() or self._parse_id_var() + left = self._parse_primary() or self._parse_column() assignment_delimiter = self._match_texts(("=", "TO")) if not left or (self.SET_REQUIRES_ASSIGNMENT_DELIMITER and not assignment_delimiter): |