Merging upstream version 22.2.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
b13ba670fd
commit
2c28c49d7e
148 changed files with 68457 additions and 63176 deletions
|
@ -73,17 +73,16 @@ class Generator(metaclass=_Generator):
|
|||
TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
|
||||
**JSON_PATH_PART_TRANSFORMS,
|
||||
exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
|
||||
exp.CaseSpecificColumnConstraint: lambda self,
|
||||
exp.CaseSpecificColumnConstraint: lambda _,
|
||||
e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
|
||||
exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
|
||||
exp.CharacterSetProperty: lambda self,
|
||||
e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
|
||||
exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
|
||||
exp.ClusteredColumnConstraint: lambda self,
|
||||
e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
|
||||
exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
|
||||
exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
|
||||
exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS",
|
||||
exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
|
||||
exp.DateAdd: lambda self, e: self.func(
|
||||
"DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
|
||||
),
|
||||
|
@ -91,8 +90,8 @@ class Generator(metaclass=_Generator):
|
|||
exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
|
||||
exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
|
||||
exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
|
||||
exp.ExternalProperty: lambda self, e: "EXTERNAL",
|
||||
exp.HeapProperty: lambda self, e: "HEAP",
|
||||
exp.ExternalProperty: lambda *_: "EXTERNAL",
|
||||
exp.HeapProperty: lambda *_: "HEAP",
|
||||
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')}",
|
||||
|
@ -105,13 +104,13 @@ class Generator(metaclass=_Generator):
|
|||
),
|
||||
exp.LanguageProperty: lambda self, e: self.naked_property(e),
|
||||
exp.LocationProperty: lambda self, e: self.naked_property(e),
|
||||
exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
|
||||
exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
|
||||
exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
|
||||
exp.MaterializedProperty: lambda *_: "MATERIALIZED",
|
||||
exp.NonClusteredColumnConstraint: lambda self,
|
||||
e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
|
||||
exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
|
||||
exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
|
||||
exp.OnCommitProperty: lambda self,
|
||||
exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
|
||||
exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
|
||||
exp.OnCommitProperty: lambda _,
|
||||
e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
|
||||
exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
|
||||
exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
|
||||
|
@ -122,21 +121,21 @@ class Generator(metaclass=_Generator):
|
|||
exp.ReturnsProperty: lambda self, e: self.naked_property(e),
|
||||
exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
|
||||
exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
|
||||
exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
|
||||
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))}",
|
||||
exp.SqlReadWriteProperty: lambda self, e: e.name,
|
||||
exp.SqlSecurityProperty: lambda self,
|
||||
exp.SqlReadWriteProperty: lambda _, e: e.name,
|
||||
exp.SqlSecurityProperty: lambda _,
|
||||
e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
|
||||
exp.StabilityProperty: lambda self, e: e.name,
|
||||
exp.TemporaryProperty: lambda self, e: "TEMPORARY",
|
||||
exp.StabilityProperty: lambda _, e: e.name,
|
||||
exp.TemporaryProperty: lambda *_: "TEMPORARY",
|
||||
exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
|
||||
exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression),
|
||||
exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
|
||||
exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
|
||||
exp.TransientProperty: lambda self, e: "TRANSIENT",
|
||||
exp.UppercaseColumnConstraint: lambda self, e: "UPPERCASE",
|
||||
exp.TransientProperty: lambda *_: "TRANSIENT",
|
||||
exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
|
||||
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
|
||||
exp.VolatileProperty: lambda self, e: "VOLATILE",
|
||||
exp.VolatileProperty: lambda *_: "VOLATILE",
|
||||
exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
|
||||
}
|
||||
|
||||
|
@ -356,6 +355,7 @@ class Generator(metaclass=_Generator):
|
|||
STRUCT_DELIMITER = ("<", ">")
|
||||
|
||||
PARAMETER_TOKEN = "@"
|
||||
NAMED_PLACEHOLDER_TOKEN = ":"
|
||||
|
||||
PROPERTIES_LOCATION = {
|
||||
exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
|
||||
|
@ -388,6 +388,7 @@ class Generator(metaclass=_Generator):
|
|||
exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
|
||||
exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
|
||||
exp.LogProperty: exp.Properties.Location.POST_NAME,
|
||||
exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
|
||||
|
@ -459,11 +460,16 @@ class Generator(metaclass=_Generator):
|
|||
exp.Paren,
|
||||
)
|
||||
|
||||
PARAMETERIZABLE_TEXT_TYPES = {
|
||||
exp.DataType.Type.NVARCHAR,
|
||||
exp.DataType.Type.VARCHAR,
|
||||
exp.DataType.Type.CHAR,
|
||||
exp.DataType.Type.NCHAR,
|
||||
}
|
||||
|
||||
# Expressions that need to have all CTEs under them bubbled up to them
|
||||
EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
|
||||
|
||||
KEY_VALUE_DEFINITIONS = (exp.EQ, exp.PropertyEQ, exp.Slice)
|
||||
|
||||
SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
|
||||
|
||||
__slots__ = (
|
||||
|
@ -630,7 +636,7 @@ class Generator(metaclass=_Generator):
|
|||
this_sql = self.indent(
|
||||
(
|
||||
self.sql(expression)
|
||||
if isinstance(expression, (exp.Select, exp.Union))
|
||||
if isinstance(expression, exp.UNWRAPPED_QUERIES)
|
||||
else self.sql(expression, "this")
|
||||
),
|
||||
level=1,
|
||||
|
@ -1535,8 +1541,8 @@ class Generator(metaclass=_Generator):
|
|||
expr = self.sql(expression, "expression")
|
||||
return f"{this} ({kind} => {expr})"
|
||||
|
||||
def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
|
||||
table = ".".join(
|
||||
def table_parts(self, expression: exp.Table) -> str:
|
||||
return ".".join(
|
||||
self.sql(part)
|
||||
for part in (
|
||||
expression.args.get("catalog"),
|
||||
|
@ -1546,6 +1552,9 @@ class Generator(metaclass=_Generator):
|
|||
if part is not None
|
||||
)
|
||||
|
||||
def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
|
||||
table = self.table_parts(expression)
|
||||
only = "ONLY " if expression.args.get("only") else ""
|
||||
version = self.sql(expression, "version")
|
||||
version = f" {version}" if version else ""
|
||||
alias = self.sql(expression, "alias")
|
||||
|
@ -1572,7 +1581,7 @@ class Generator(metaclass=_Generator):
|
|||
if when:
|
||||
table = f"{table} {when}"
|
||||
|
||||
return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
|
||||
return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
|
||||
|
||||
def tablesample_sql(
|
||||
self,
|
||||
|
@ -1681,7 +1690,7 @@ class Generator(metaclass=_Generator):
|
|||
alias_node = expression.args.get("alias")
|
||||
column_names = alias_node and alias_node.columns
|
||||
|
||||
selects: t.List[exp.Subqueryable] = []
|
||||
selects: t.List[exp.Query] = []
|
||||
|
||||
for i, tup in enumerate(expression.expressions):
|
||||
row = tup.expressions
|
||||
|
@ -1697,10 +1706,8 @@ class Generator(metaclass=_Generator):
|
|||
# This may result in poor performance for large-cardinality `VALUES` tables, due to
|
||||
# the deep nesting of the resulting exp.Unions. If this is a problem, either increase
|
||||
# `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
|
||||
subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
|
||||
return self.subquery_sql(
|
||||
subqueryable.subquery(alias_node and alias_node.this, copy=False)
|
||||
)
|
||||
query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
|
||||
return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
|
||||
|
||||
alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
|
||||
unions = " UNION ALL ".join(self.sql(select) for select in selects)
|
||||
|
@ -1854,7 +1861,7 @@ class Generator(metaclass=_Generator):
|
|||
]
|
||||
|
||||
args_sql = ", ".join(self.sql(e) for e in args)
|
||||
args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
|
||||
args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
|
||||
expressions = self.expressions(expression, flat=True)
|
||||
expressions = f" BY {expressions}" if expressions else ""
|
||||
|
||||
|
@ -2070,12 +2077,17 @@ class Generator(metaclass=_Generator):
|
|||
else []
|
||||
)
|
||||
|
||||
options = self.expressions(expression, key="options")
|
||||
if options:
|
||||
options = f" OPTION{self.wrap(options)}"
|
||||
|
||||
return csv(
|
||||
*sqls,
|
||||
*[self.sql(join) for join in expression.args.get("joins") or []],
|
||||
self.sql(expression, "connect"),
|
||||
self.sql(expression, "match"),
|
||||
*[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
|
||||
self.sql(expression, "prewhere"),
|
||||
self.sql(expression, "where"),
|
||||
self.sql(expression, "group"),
|
||||
self.sql(expression, "having"),
|
||||
|
@ -2083,9 +2095,13 @@ class Generator(metaclass=_Generator):
|
|||
self.sql(expression, "order"),
|
||||
*offset_limit_modifiers,
|
||||
*self.after_limit_modifiers(expression),
|
||||
options,
|
||||
sep="",
|
||||
)
|
||||
|
||||
def queryoption_sql(self, expression: exp.QueryOption) -> str:
|
||||
return ""
|
||||
|
||||
def offset_limit_modifiers(
|
||||
self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
|
||||
) -> t.List[str]:
|
||||
|
@ -2140,9 +2156,9 @@ class Generator(metaclass=_Generator):
|
|||
self.sql(
|
||||
exp.Struct(
|
||||
expressions=[
|
||||
exp.column(e.output_name).eq(
|
||||
e.this if isinstance(e, exp.Alias) else e
|
||||
)
|
||||
exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
|
||||
if isinstance(e, exp.Alias)
|
||||
else e
|
||||
for e in expression.expressions
|
||||
]
|
||||
)
|
||||
|
@ -2204,7 +2220,7 @@ class Generator(metaclass=_Generator):
|
|||
return f"@@{kind}{this}"
|
||||
|
||||
def placeholder_sql(self, expression: exp.Placeholder) -> str:
|
||||
return f":{expression.name}" if expression.name else "?"
|
||||
return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
|
||||
|
||||
def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
|
||||
alias = self.sql(expression, "alias")
|
||||
|
@ -2261,6 +2277,9 @@ class Generator(metaclass=_Generator):
|
|||
|
||||
return f"UNNEST({args}){suffix}"
|
||||
|
||||
def prewhere_sql(self, expression: exp.PreWhere) -> str:
|
||||
return ""
|
||||
|
||||
def where_sql(self, expression: exp.Where) -> str:
|
||||
this = self.indent(self.sql(expression, "this"))
|
||||
return f"{self.seg('WHERE')}{self.sep()}{this}"
|
||||
|
@ -2326,7 +2345,7 @@ class Generator(metaclass=_Generator):
|
|||
|
||||
def any_sql(self, expression: exp.Any) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
if isinstance(expression.this, exp.Subqueryable):
|
||||
if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
|
||||
this = self.wrap(this)
|
||||
return f"ANY {this}"
|
||||
|
||||
|
@ -2568,7 +2587,7 @@ class Generator(metaclass=_Generator):
|
|||
is_global = " GLOBAL" if expression.args.get("is_global") else ""
|
||||
|
||||
if query:
|
||||
in_sql = self.wrap(query)
|
||||
in_sql = self.wrap(self.sql(query))
|
||||
elif unnest:
|
||||
in_sql = self.in_unnest_op(unnest)
|
||||
elif field:
|
||||
|
@ -2610,7 +2629,7 @@ class Generator(metaclass=_Generator):
|
|||
return f"REFERENCES {this}{expressions}{options}"
|
||||
|
||||
def anonymous_sql(self, expression: exp.Anonymous) -> str:
|
||||
return self.func(expression.name, *expression.expressions)
|
||||
return self.func(self.sql(expression, "this"), *expression.expressions)
|
||||
|
||||
def paren_sql(self, expression: exp.Paren) -> str:
|
||||
if isinstance(expression.unnest(), exp.Select):
|
||||
|
@ -2822,7 +2841,9 @@ class Generator(metaclass=_Generator):
|
|||
|
||||
exists = " IF EXISTS" if expression.args.get("exists") else ""
|
||||
only = " ONLY" if expression.args.get("only") else ""
|
||||
return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
|
||||
options = self.expressions(expression, key="options")
|
||||
options = f", {options}" if options else ""
|
||||
return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
|
||||
|
||||
def add_column_sql(self, expression: exp.AlterTable) -> str:
|
||||
if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
|
||||
|
@ -2839,15 +2860,7 @@ class Generator(metaclass=_Generator):
|
|||
return f"DROP{exists}{expressions}"
|
||||
|
||||
def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
expression_ = self.sql(expression, "expression")
|
||||
add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
|
||||
|
||||
enforced = expression.args.get("enforced")
|
||||
if enforced is not None:
|
||||
return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
|
||||
|
||||
return f"{add_constraint} {expression_}"
|
||||
return f"ADD {self.expressions(expression)}"
|
||||
|
||||
def distinct_sql(self, expression: exp.Distinct) -> str:
|
||||
this = self.expressions(expression, flat=True)
|
||||
|
@ -3296,6 +3309,10 @@ class Generator(metaclass=_Generator):
|
|||
self.unsupported("Unsupported index constraint option.")
|
||||
return ""
|
||||
|
||||
def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
|
||||
enforced = " ENFORCED" if expression.args.get("enforced") else ""
|
||||
return f"CHECK ({self.sql(expression, 'this')}){enforced}"
|
||||
|
||||
def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
|
||||
kind = self.sql(expression, "kind")
|
||||
kind = f"{kind} INDEX" if kind else "INDEX"
|
||||
|
@ -3452,9 +3469,87 @@ class Generator(metaclass=_Generator):
|
|||
|
||||
return expression
|
||||
|
||||
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
|
||||
]
|
||||
def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
|
||||
expression.set("is_end_exclusive", None)
|
||||
return self.function_fallback_sql(expression)
|
||||
|
||||
def struct_sql(self, expression: exp.Struct) -> str:
|
||||
expression.set(
|
||||
"expressions",
|
||||
[
|
||||
exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
|
||||
for e in expression.expressions
|
||||
],
|
||||
)
|
||||
|
||||
return self.function_fallback_sql(expression)
|
||||
|
||||
def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
|
||||
low = self.sql(expression, "this")
|
||||
high = self.sql(expression, "expression")
|
||||
|
||||
return f"{low} TO {high}"
|
||||
|
||||
def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
|
||||
target = "DATABASE" if expression.args.get("is_database") else "TABLE"
|
||||
tables = f" {self.expressions(expression)}"
|
||||
|
||||
exists = " IF EXISTS" if expression.args.get("exists") else ""
|
||||
|
||||
on_cluster = self.sql(expression, "cluster")
|
||||
on_cluster = f" {on_cluster}" if on_cluster else ""
|
||||
|
||||
identity = self.sql(expression, "identity")
|
||||
identity = f" {identity} IDENTITY" if identity else ""
|
||||
|
||||
option = self.sql(expression, "option")
|
||||
option = f" {option}" if option else ""
|
||||
|
||||
partition = self.sql(expression, "partition")
|
||||
partition = f" {partition}" if partition else ""
|
||||
|
||||
return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
|
||||
|
||||
# This transpiles T-SQL's CONVERT function
|
||||
# https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
|
||||
def convert_sql(self, expression: exp.Convert) -> str:
|
||||
to = expression.this
|
||||
value = expression.expression
|
||||
style = expression.args.get("style")
|
||||
safe = expression.args.get("safe")
|
||||
strict = expression.args.get("strict")
|
||||
|
||||
if not to or not value:
|
||||
return ""
|
||||
|
||||
# Retrieve length of datatype and override to default if not specified
|
||||
if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
|
||||
to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
|
||||
|
||||
transformed: t.Optional[exp.Expression] = None
|
||||
cast = exp.Cast if strict else exp.TryCast
|
||||
|
||||
# Check whether a conversion with format (T-SQL calls this 'style') is applicable
|
||||
if isinstance(style, exp.Literal) and style.is_int:
|
||||
from sqlglot.dialects.tsql import TSQL
|
||||
|
||||
style_value = style.name
|
||||
converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
|
||||
if not converted_style:
|
||||
self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
|
||||
|
||||
fmt = exp.Literal.string(converted_style)
|
||||
|
||||
if to.this == exp.DataType.Type.DATE:
|
||||
transformed = exp.StrToDate(this=value, format=fmt)
|
||||
elif to.this == exp.DataType.Type.DATETIME:
|
||||
transformed = exp.StrToTime(this=value, format=fmt)
|
||||
elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
|
||||
transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
|
||||
elif to.this == exp.DataType.Type.TEXT:
|
||||
transformed = exp.TimeToStr(this=value, format=fmt)
|
||||
|
||||
if not transformed:
|
||||
transformed = cast(this=value, to=to, safe=safe)
|
||||
|
||||
return self.sql(transformed)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue