Merging upstream version 25.20.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
d9afe6504e
commit
f9e1084cc5
119 changed files with 78094 additions and 71498 deletions
|
@ -98,6 +98,7 @@ class Generator(metaclass=_Generator):
|
|||
e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
|
||||
exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
|
||||
exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
|
||||
exp.Except: lambda self, e: self.set_operations(e),
|
||||
exp.ExternalProperty: lambda *_: "EXTERNAL",
|
||||
exp.GlobalProperty: lambda *_: "GLOBAL",
|
||||
exp.HeapProperty: lambda *_: "HEAP",
|
||||
|
@ -105,6 +106,7 @@ class Generator(metaclass=_Generator):
|
|||
exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
|
||||
exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
|
||||
exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
|
||||
exp.Intersect: lambda self, e: self.set_operations(e),
|
||||
exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
|
||||
exp.LanguageProperty: lambda self, e: self.naked_property(e),
|
||||
exp.LocationProperty: lambda self, e: self.naked_property(e),
|
||||
|
@ -131,6 +133,7 @@ class Generator(metaclass=_Generator):
|
|||
),
|
||||
exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
|
||||
exp.SecureProperty: lambda *_: "SECURE",
|
||||
exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
|
||||
exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
|
||||
exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
|
||||
exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
|
||||
|
@ -149,8 +152,9 @@ class Generator(metaclass=_Generator):
|
|||
exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
|
||||
exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
|
||||
exp.TransientProperty: lambda *_: "TRANSIENT",
|
||||
exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
|
||||
exp.Union: lambda self, e: self.set_operations(e),
|
||||
exp.UnloggedProperty: lambda *_: "UNLOGGED",
|
||||
exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
|
||||
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
|
||||
exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
|
||||
exp.VolatileProperty: lambda *_: "VOLATILE",
|
||||
|
@ -170,8 +174,8 @@ class Generator(metaclass=_Generator):
|
|||
# Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
|
||||
LOCKING_READS_SUPPORTED = False
|
||||
|
||||
# Always do <set op> distinct or <set op> all
|
||||
EXPLICIT_SET_OP = False
|
||||
# Whether the EXCEPT and INTERSECT operations can return duplicates
|
||||
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
|
||||
|
||||
# Wrap derived values in parens, usually standard but spark doesn't support it
|
||||
WRAP_DERIVED_VALUES = True
|
||||
|
@ -448,6 +452,8 @@ class Generator(metaclass=_Generator):
|
|||
exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.Cluster: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
|
||||
exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
|
||||
|
@ -495,6 +501,7 @@ class Generator(metaclass=_Generator):
|
|||
exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.SecureProperty: exp.Properties.Location.POST_CREATE,
|
||||
exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.Set: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
|
@ -534,6 +541,7 @@ class Generator(metaclass=_Generator):
|
|||
exp.From,
|
||||
exp.Insert,
|
||||
exp.Join,
|
||||
exp.MultitableInserts,
|
||||
exp.Select,
|
||||
exp.SetOperation,
|
||||
exp.Update,
|
||||
|
@ -584,6 +592,8 @@ class Generator(metaclass=_Generator):
|
|||
"_escaped_quote_end",
|
||||
"_escaped_identifier_end",
|
||||
"_next_name",
|
||||
"_identifier_start",
|
||||
"_identifier_end",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -631,6 +641,9 @@ class Generator(metaclass=_Generator):
|
|||
|
||||
self._next_name = name_sequence("_t")
|
||||
|
||||
self._identifier_start = self.dialect.IDENTIFIER_START
|
||||
self._identifier_end = self.dialect.IDENTIFIER_END
|
||||
|
||||
def generate(self, expression: exp.Expression, copy: bool = True) -> str:
|
||||
"""
|
||||
Generates the SQL string corresponding to the given syntax tree.
|
||||
|
@ -1284,6 +1297,7 @@ class Generator(metaclass=_Generator):
|
|||
kind = expression.args["kind"]
|
||||
kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
|
||||
exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
|
||||
concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
|
||||
on_cluster = self.sql(expression, "cluster")
|
||||
on_cluster = f" {on_cluster}" if on_cluster else ""
|
||||
temporary = " TEMPORARY" if expression.args.get("temporary") else ""
|
||||
|
@ -1291,13 +1305,69 @@ class Generator(metaclass=_Generator):
|
|||
cascade = " CASCADE" if expression.args.get("cascade") else ""
|
||||
constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
|
||||
purge = " PURGE" if expression.args.get("purge") else ""
|
||||
return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
|
||||
return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
|
||||
|
||||
def except_sql(self, expression: exp.Except) -> str:
|
||||
return self.set_operations(expression)
|
||||
def set_operation(self, expression: exp.SetOperation) -> str:
|
||||
op_type = type(expression)
|
||||
op_name = op_type.key.upper()
|
||||
|
||||
def except_op(self, expression: exp.Except) -> str:
|
||||
return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
|
||||
distinct = expression.args.get("distinct")
|
||||
if (
|
||||
distinct is False
|
||||
and op_type in (exp.Except, exp.Intersect)
|
||||
and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
|
||||
):
|
||||
self.unsupported(f"{op_name} ALL is not supported")
|
||||
|
||||
default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
|
||||
|
||||
if distinct is None:
|
||||
distinct = default_distinct
|
||||
if distinct is None:
|
||||
self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
|
||||
|
||||
if distinct is default_distinct:
|
||||
kind = ""
|
||||
else:
|
||||
kind = " DISTINCT" if distinct else " ALL"
|
||||
|
||||
by_name = " BY NAME" if expression.args.get("by_name") else ""
|
||||
return f"{op_name}{kind}{by_name}"
|
||||
|
||||
def set_operations(self, expression: exp.SetOperation) -> str:
|
||||
if not self.SET_OP_MODIFIERS:
|
||||
limit = expression.args.get("limit")
|
||||
order = expression.args.get("order")
|
||||
|
||||
if limit or order:
|
||||
select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
|
||||
|
||||
if limit:
|
||||
select = select.limit(limit.pop(), copy=False)
|
||||
if order:
|
||||
select = select.order_by(order.pop(), copy=False)
|
||||
return self.sql(select)
|
||||
|
||||
sqls: t.List[str] = []
|
||||
stack: t.List[t.Union[str, exp.Expression]] = [expression]
|
||||
|
||||
while stack:
|
||||
node = stack.pop()
|
||||
|
||||
if isinstance(node, exp.SetOperation):
|
||||
stack.append(node.expression)
|
||||
stack.append(
|
||||
self.maybe_comment(
|
||||
self.set_operation(node), comments=node.comments, separated=True
|
||||
)
|
||||
)
|
||||
stack.append(node.this)
|
||||
else:
|
||||
sqls.append(self.sql(node))
|
||||
|
||||
this = self.sep().join(sqls)
|
||||
this = self.query_modifiers(expression, this)
|
||||
return self.prepend_ctes(expression, this)
|
||||
|
||||
def fetch_sql(self, expression: exp.Fetch) -> str:
|
||||
direction = expression.args.get("direction")
|
||||
|
@ -1366,14 +1436,14 @@ class Generator(metaclass=_Generator):
|
|||
text = expression.name
|
||||
lower = text.lower()
|
||||
text = lower if self.normalize and not expression.quoted else text
|
||||
text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
|
||||
text = text.replace(self._identifier_end, self._escaped_identifier_end)
|
||||
if (
|
||||
expression.quoted
|
||||
or self.dialect.can_identify(text, self.identify)
|
||||
or lower in self.RESERVED_KEYWORDS
|
||||
or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
|
||||
):
|
||||
text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
|
||||
text = f"{self._identifier_start}{text}{self._identifier_end}"
|
||||
return text
|
||||
|
||||
def hex_sql(self, expression: exp.Hex) -> str:
|
||||
|
@ -1675,12 +1745,6 @@ class Generator(metaclass=_Generator):
|
|||
sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
|
||||
return self.prepend_ctes(expression, sql)
|
||||
|
||||
def intersect_sql(self, expression: exp.Intersect) -> str:
|
||||
return self.set_operations(expression)
|
||||
|
||||
def intersect_op(self, expression: exp.Intersect) -> str:
|
||||
return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
|
||||
|
||||
def introducer_sql(self, expression: exp.Introducer) -> str:
|
||||
return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
|
||||
|
||||
|
@ -2462,52 +2526,6 @@ class Generator(metaclass=_Generator):
|
|||
this = self.indent(self.sql(expression, "this"))
|
||||
return f"{self.seg('QUALIFY')}{self.sep()}{this}"
|
||||
|
||||
def set_operations(self, expression: exp.SetOperation) -> str:
|
||||
if not self.SET_OP_MODIFIERS:
|
||||
limit = expression.args.get("limit")
|
||||
order = expression.args.get("order")
|
||||
|
||||
if limit or order:
|
||||
select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
|
||||
|
||||
if limit:
|
||||
select = select.limit(limit.pop(), copy=False)
|
||||
if order:
|
||||
select = select.order_by(order.pop(), copy=False)
|
||||
return self.sql(select)
|
||||
|
||||
sqls: t.List[str] = []
|
||||
stack: t.List[t.Union[str, exp.Expression]] = [expression]
|
||||
|
||||
while stack:
|
||||
node = stack.pop()
|
||||
|
||||
if isinstance(node, exp.SetOperation):
|
||||
stack.append(node.expression)
|
||||
stack.append(
|
||||
self.maybe_comment(
|
||||
getattr(self, f"{node.key}_op")(node),
|
||||
comments=node.comments,
|
||||
separated=True,
|
||||
)
|
||||
)
|
||||
stack.append(node.this)
|
||||
else:
|
||||
sqls.append(self.sql(node))
|
||||
|
||||
this = self.sep().join(sqls)
|
||||
this = self.query_modifiers(expression, this)
|
||||
return self.prepend_ctes(expression, this)
|
||||
|
||||
def union_sql(self, expression: exp.Union) -> str:
|
||||
return self.set_operations(expression)
|
||||
|
||||
def union_op(self, expression: exp.SetOperation) -> str:
|
||||
kind = " DISTINCT" if self.EXPLICIT_SET_OP else ""
|
||||
kind = kind if expression.args.get("distinct") else " ALL"
|
||||
by_name = " BY NAME" if expression.args.get("by_name") else ""
|
||||
return f"UNION{kind}{by_name}"
|
||||
|
||||
def unnest_sql(self, expression: exp.Unnest) -> str:
|
||||
args = self.expressions(expression, flat=True)
|
||||
|
||||
|
@ -3177,7 +3195,9 @@ class Generator(metaclass=_Generator):
|
|||
options = self.expressions(expression, key="options")
|
||||
options = f", {options}" if options else ""
|
||||
kind = self.sql(expression, "kind")
|
||||
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"
|
||||
not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
|
||||
|
||||
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
|
||||
|
||||
def add_column_sql(self, expression: exp.Alter) -> str:
|
||||
if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
|
||||
|
@ -3490,6 +3510,7 @@ class Generator(metaclass=_Generator):
|
|||
result_sql = "\n".join(s.rstrip() for s in result_sqls)
|
||||
else:
|
||||
result_sql = "".join(result_sqls)
|
||||
|
||||
return (
|
||||
self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
|
||||
if indent
|
||||
|
@ -3605,6 +3626,19 @@ class Generator(metaclass=_Generator):
|
|||
def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
|
||||
return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
|
||||
|
||||
def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
|
||||
return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
|
||||
|
||||
# https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
|
||||
def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
|
||||
expressions = self.expressions(expression, flat=True)
|
||||
expressions = f" {self.wrap(expressions)}" if expressions else ""
|
||||
buckets = self.sql(expression, "buckets")
|
||||
kind = self.sql(expression, "kind")
|
||||
buckets = f" BUCKETS {buckets}" if buckets else ""
|
||||
order = self.sql(expression, "order")
|
||||
return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
|
||||
|
||||
def oncluster_sql(self, expression: exp.OnCluster) -> str:
|
||||
return ""
|
||||
|
||||
|
@ -4180,10 +4214,75 @@ class Generator(metaclass=_Generator):
|
|||
returning = self.sql(expression, "returning")
|
||||
returning = f" RETURNING {returning}" if returning else ""
|
||||
|
||||
on_empty = expression.args.get("on_empty")
|
||||
on_empty = f" {_generate_on_options(on_empty)} ON EMPTY" if on_empty else ""
|
||||
on_condition = self.sql(expression, "on_condition")
|
||||
on_condition = f" {on_condition}" if on_condition else ""
|
||||
|
||||
on_error = expression.args.get("on_error")
|
||||
on_error = f" {_generate_on_options(on_error)} ON ERROR" if on_error else ""
|
||||
return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
|
||||
|
||||
return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_empty}{on_error}")
|
||||
def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
|
||||
else_ = "ELSE " if expression.args.get("else_") else ""
|
||||
condition = self.sql(expression, "expression")
|
||||
condition = f"WHEN {condition} THEN " if condition else else_
|
||||
insert = self.sql(expression, "this")[len("INSERT") :].strip()
|
||||
return f"{condition}{insert}"
|
||||
|
||||
def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
|
||||
kind = self.sql(expression, "kind")
|
||||
expressions = self.seg(self.expressions(expression, sep=" "))
|
||||
res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
|
||||
return res
|
||||
|
||||
def oncondition_sql(self, expression: exp.OnCondition) -> str:
|
||||
# Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
|
||||
empty = expression.args.get("empty")
|
||||
empty = (
|
||||
f"DEFAULT {empty} ON EMPTY"
|
||||
if isinstance(empty, exp.Expression)
|
||||
else self.sql(expression, "empty")
|
||||
)
|
||||
|
||||
error = expression.args.get("error")
|
||||
error = (
|
||||
f"DEFAULT {error} ON ERROR"
|
||||
if isinstance(error, exp.Expression)
|
||||
else self.sql(expression, "error")
|
||||
)
|
||||
|
||||
if error and empty:
|
||||
error = (
|
||||
f"{empty} {error}"
|
||||
if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
|
||||
else f"{error} {empty}"
|
||||
)
|
||||
empty = ""
|
||||
|
||||
null = self.sql(expression, "null")
|
||||
|
||||
return f"{empty}{error}{null}"
|
||||
|
||||
def jsonexists_sql(self, expression: exp.JSONExists) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
path = self.sql(expression, "path")
|
||||
|
||||
passing = self.expressions(expression, "passing")
|
||||
passing = f" PASSING {passing}" if passing else ""
|
||||
|
||||
on_condition = self.sql(expression, "on_condition")
|
||||
on_condition = f" {on_condition}" if on_condition else ""
|
||||
|
||||
path = f"{path}{passing}{on_condition}"
|
||||
|
||||
return self.func("JSON_EXISTS", this, path)
|
||||
|
||||
def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
|
||||
array_agg = self.function_fallback_sql(expression)
|
||||
|
||||
if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
|
||||
parent = expression.parent
|
||||
if isinstance(parent, exp.Filter):
|
||||
parent_cond = parent.expression.this
|
||||
parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
|
||||
else:
|
||||
array_agg = f"{array_agg} FILTER(WHERE {self.sql(expression, 'this')} IS NOT NULL)"
|
||||
|
||||
return array_agg
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue