diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-09-13 09:17:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-09-13 09:17:37 +0000 |
commit | 22342ec7693d09ebf96484be7c3cd5d8b506f38e (patch) | |
tree | b8fe2ebfb290eb425dcee4f15fa8cab46e74b40f /sqlglot/parser.py | |
parent | Adding upstream version 18.3.0. (diff) | |
download | sqlglot-22342ec7693d09ebf96484be7c3cd5d8b506f38e.tar.xz sqlglot-22342ec7693d09ebf96484be7c3cd5d8b506f38e.zip |
Adding upstream version 18.4.1.upstream/18.4.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sqlglot/parser.py')
-rw-r--r-- | sqlglot/parser.py | 77 |
1 files changed, 57 insertions, 20 deletions
diff --git a/sqlglot/parser.py b/sqlglot/parser.py index 939303f..f721582 100644 --- a/sqlglot/parser.py +++ b/sqlglot/parser.py @@ -155,6 +155,8 @@ class Parser(metaclass=_Parser): TokenType.JSON, TokenType.JSONB, TokenType.INTERVAL, + TokenType.TINYBLOB, + TokenType.TINYTEXT, TokenType.TIME, TokenType.TIMETZ, TokenType.TIMESTAMP, @@ -764,6 +766,7 @@ class Parser(metaclass=_Parser): "ANY_VALUE": lambda self: self._parse_any_value(), "CAST": lambda self: self._parse_cast(self.STRICT_CAST), "CONCAT": lambda self: self._parse_concat(), + "CONCAT_WS": lambda self: self._parse_concat_ws(), "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST), "DECODE": lambda self: self._parse_decode(), "EXTRACT": lambda self: self._parse_extract(), @@ -1942,7 +1945,7 @@ class Parser(metaclass=_Parser): def _parse_update(self) -> exp.Update: comments = self._prev_comments - this = self._parse_table(alias_tokens=self.UPDATE_ALIAS_TOKENS) + this = self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS) expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality) returning = self._parse_returning() return self.expression( @@ -3269,7 +3272,7 @@ class Parser(metaclass=_Parser): if tokens[0].token_type in self.TYPE_TOKENS: self._prev = tokens[0] elif self.SUPPORTS_USER_DEFINED_TYPES: - return identifier + return exp.DataType.build(identifier.name, udt=True) else: return None else: @@ -3888,6 +3891,9 @@ class Parser(metaclass=_Parser): exp.ForeignKey, expressions=expressions, reference=reference, **options # type: ignore ) + def _parse_primary_key_part(self) -> t.Optional[exp.Expression]: + return self._parse_field() + def _parse_primary_key( self, wrapped_optional: bool = False, in_props: bool = False ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey: @@ -3899,7 +3905,9 @@ class Parser(metaclass=_Parser): if not in_props and not self._match(TokenType.L_PAREN, advance=False): return self.expression(exp.PrimaryKeyColumnConstraint, desc=desc) - expressions = self._parse_wrapped_csv(self._parse_field, optional=wrapped_optional) + expressions = self._parse_wrapped_csv( + self._parse_primary_key_part, optional=wrapped_optional + ) options = self._parse_key_constraint_options() return self.expression(exp.PrimaryKey, expressions=expressions, options=options) @@ -4066,11 +4074,7 @@ class Parser(metaclass=_Parser): def _parse_concat(self) -> t.Optional[exp.Expression]: args = self._parse_csv(self._parse_conjunction) if self.CONCAT_NULL_OUTPUTS_STRING: - args = [ - exp.func("COALESCE", exp.cast(arg, "text"), exp.Literal.string("")) - for arg in args - if arg - ] + args = self._ensure_string_if_null(args) # Some dialects (e.g. Trino) don't allow a single-argument CONCAT call, so when # we find such a call we replace it with its argument. @@ -4081,6 +4085,16 @@ class Parser(metaclass=_Parser): exp.Concat if self.STRICT_STRING_CONCAT else exp.SafeConcat, expressions=args ) + def _parse_concat_ws(self) -> t.Optional[exp.Expression]: + args = self._parse_csv(self._parse_conjunction) + if len(args) < 2: + return self.expression(exp.ConcatWs, expressions=args) + delim, *values = args + if self.CONCAT_NULL_OUTPUTS_STRING: + values = self._ensure_string_if_null(values) + + return self.expression(exp.ConcatWs, expressions=[delim] + values) + def _parse_string_agg(self) -> exp.Expression: if self._match(TokenType.DISTINCT): args: t.List[t.Optional[exp.Expression]] = [ @@ -4181,15 +4195,28 @@ class Parser(metaclass=_Parser): return None return self.expression(exp.JSONKeyValue, this=key, expression=value) + def _parse_format_json(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: + if not this or not self._match_text_seq("FORMAT", "JSON"): + return this + + return self.expression(exp.FormatJson, this=this) + + def _parse_on_handling(self, on: str, *values: str) -> t.Optional[str]: + # Parses the "X ON Y" syntax, i.e. NULL ON NULL (Oracle, T-SQL) + for value in values: + if self._match_text_seq(value, "ON", on): + return f"{value} ON {on}" + + return None + def _parse_json_object(self) -> exp.JSONObject: star = self._parse_star() - expressions = [star] if star else self._parse_csv(self._parse_json_key_value) - - null_handling = None - if self._match_text_seq("NULL", "ON", "NULL"): - null_handling = "NULL ON NULL" - elif self._match_text_seq("ABSENT", "ON", "NULL"): - null_handling = "ABSENT ON NULL" + expressions = ( + [star] + if star + else self._parse_csv(lambda: self._parse_format_json(self._parse_json_key_value())) + ) + null_handling = self._parse_on_handling("NULL", "NULL", "ABSENT") unique_keys = None if self._match_text_seq("WITH", "UNIQUE"): @@ -4199,8 +4226,9 @@ class Parser(metaclass=_Parser): self._match_text_seq("KEYS") - return_type = self._match_text_seq("RETURNING") and self._parse_type() - format_json = self._match_text_seq("FORMAT", "JSON") + return_type = self._match_text_seq("RETURNING") and self._parse_format_json( + self._parse_type() + ) encoding = self._match_text_seq("ENCODING") and self._parse_var() return self.expression( @@ -4209,7 +4237,6 @@ class Parser(metaclass=_Parser): null_handling=null_handling, unique_keys=unique_keys, return_type=return_type, - format_json=format_json, encoding=encoding, ) @@ -4979,9 +5006,12 @@ class Parser(metaclass=_Parser): self._match_r_paren() return self.expression(exp.DictRange, this=this, min=min, max=max) - def _parse_comprehension(self, this: exp.Expression) -> exp.Comprehension: + def _parse_comprehension(self, this: exp.Expression) -> t.Optional[exp.Comprehension]: + index = self._index expression = self._parse_column() - self._match(TokenType.IN) + if not self._match(TokenType.IN): + self._retreat(index - 1) + return None iterator = self._parse_column() condition = self._parse_conjunction() if self._match_text_seq("IF") else None return self.expression( @@ -5125,3 +5155,10 @@ class Parser(metaclass=_Parser): else: column.replace(dot_or_id) return node + + def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]: + return [ + exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string("")) + for value in values + if value + ] |