Merging upstream version 18.4.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
b982664fe2
commit
d90681de49
92 changed files with 43076 additions and 40554 deletions
|
@ -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
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue