1
0
Fork 0

Adding upstream version 23.16.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 21:34:56 +01:00
parent 9d7e0ff7aa
commit b6ae88ec81
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
93 changed files with 64106 additions and 59061 deletions

View file

@ -8,7 +8,7 @@ from functools import reduce
from sqlglot import exp
from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
from sqlglot.helper import apply_index_offset, csv, seq_get
from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get
from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
from sqlglot.time import format_time
from sqlglot.tokens import TokenType
@ -74,6 +74,8 @@ class Generator(metaclass=_Generator):
TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
**JSON_PATH_PART_TRANSFORMS,
exp.AllowedValuesProperty: lambda self,
e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
exp.CaseSpecificColumnConstraint: lambda _,
@ -123,7 +125,9 @@ class Generator(metaclass=_Generator):
exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
exp.RemoteWithConnectionModelProperty: lambda self,
e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
exp.ReturnsProperty: lambda self, e: self.naked_property(e),
exp.ReturnsProperty: lambda self, e: (
"RETURNS NULL ON NULL INPUT" if e.args.get("null") else 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 _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
@ -133,6 +137,7 @@ class Generator(metaclass=_Generator):
exp.SqlSecurityProperty: lambda _,
e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
exp.StabilityProperty: lambda _, e: e.name,
exp.StrictProperty: lambda *_: "STRICT",
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),
@ -351,6 +356,15 @@ class Generator(metaclass=_Generator):
# Whether the conditional TRY(expression) function is supported
TRY_SUPPORTED = True
# The keyword to use when generating a star projection with excluded columns
STAR_EXCEPT = "EXCEPT"
# The HEX function name
HEX_FUNC = "HEX"
# The keywords to use when prefixing & separating WITH based properties
WITH_PROPERTIES_PREFIX = "WITH"
TYPE_MAPPING = {
exp.DataType.Type.NCHAR: "CHAR",
exp.DataType.Type.NVARCHAR: "VARCHAR",
@ -364,11 +378,6 @@ class Generator(metaclass=_Generator):
exp.DataType.Type.ROWVERSION: "VARBINARY",
}
STAR_MAPPING = {
"except": "EXCEPT",
"replace": "REPLACE",
}
TIME_PART_SINGULARS = {
"MICROSECONDS": "MICROSECOND",
"SECONDS": "SECOND",
@ -401,6 +410,7 @@ class Generator(metaclass=_Generator):
NAMED_PLACEHOLDER_TOKEN = ":"
PROPERTIES_LOCATION = {
exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
@ -413,6 +423,7 @@ class Generator(metaclass=_Generator):
exp.Cluster: exp.Properties.Location.POST_SCHEMA,
exp.ClusteredByProperty: 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,
exp.DictRange: exp.Properties.Location.POST_SCHEMA,
exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
@ -466,6 +477,7 @@ class Generator(metaclass=_Generator):
exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
exp.TransientProperty: exp.Properties.Location.POST_CREATE,
@ -539,6 +551,7 @@ class Generator(metaclass=_Generator):
"unsupported_messages",
"_escaped_quote_end",
"_escaped_identifier_end",
"_next_name",
)
def __init__(
@ -584,6 +597,8 @@ class Generator(metaclass=_Generator):
self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
)
self._next_name = name_sequence("_t")
def generate(self, expression: exp.Expression, copy: bool = True) -> str:
"""
Generates the SQL string corresponding to the given syntax tree.
@ -687,15 +702,15 @@ class Generator(metaclass=_Generator):
return f"{sql} {comments_sql}"
def wrap(self, expression: exp.Expression | str) -> str:
this_sql = self.indent(
(
self.sql(expression)
if isinstance(expression, exp.UNWRAPPED_QUERIES)
else self.sql(expression, "this")
),
level=1,
pad=0,
this_sql = (
self.sql(expression)
if isinstance(expression, exp.UNWRAPPED_QUERIES)
else self.sql(expression, "this")
)
if not this_sql:
return "()"
this_sql = self.indent(this_sql, level=1, pad=0)
return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
@ -720,7 +735,7 @@ class Generator(metaclass=_Generator):
skip_first: bool = False,
skip_last: bool = False,
) -> str:
if not self.pretty:
if not self.pretty or not sql:
return sql
pad = self.pad if pad is None else pad
@ -951,6 +966,12 @@ class Generator(metaclass=_Generator):
)
)
if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
properties_sql = self.sep() + properties_sql
elif not self.pretty:
# Standalone POST_WITH properties need a leading whitespace in non-pretty mode
properties_sql = f" {properties_sql}"
begin = " BEGIN" if expression.args.get("begin") else ""
end = " END" if expression.args.get("end") else ""
@ -1095,7 +1116,7 @@ class Generator(metaclass=_Generator):
self.unsupported("Named columns are not supported in table alias.")
if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
alias = "_t"
alias = self._next_name()
return f"{alias}{columns}"
@ -1208,12 +1229,14 @@ class Generator(metaclass=_Generator):
expressions = f" ({expressions})" if expressions else ""
kind = expression.args["kind"]
exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
on_cluster = self.sql(expression, "cluster")
on_cluster = f" {on_cluster}" if on_cluster else ""
temporary = " TEMPORARY" if expression.args.get("temporary") else ""
materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
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}{expressions}{cascade}{constraints}{purge}"
return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
def except_sql(self, expression: exp.Except) -> str:
return self.set_operations(expression)
@ -1296,6 +1319,19 @@ class Generator(metaclass=_Generator):
text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
return text
def hex_sql(self, expression: exp.Hex) -> str:
text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
if self.dialect.HEX_LOWERCASE:
text = self.func("LOWER", text)
return text
def lowerhex_sql(self, expression: exp.LowerHex) -> str:
text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
if not self.dialect.HEX_LOWERCASE:
text = self.func("LOWER", text)
return text
def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
input_format = self.sql(expression, "input_format")
input_format = f"INPUTFORMAT {input_format}" if input_format else ""
@ -1321,13 +1357,17 @@ class Generator(metaclass=_Generator):
elif p_loc == exp.Properties.Location.POST_SCHEMA:
root_properties.append(p)
return self.root_properties(
exp.Properties(expressions=root_properties)
) + self.with_properties(exp.Properties(expressions=with_properties))
root_props = self.root_properties(exp.Properties(expressions=root_properties))
with_props = self.with_properties(exp.Properties(expressions=with_properties))
if root_props and with_props and not self.pretty:
with_props = " " + with_props
return root_props + with_props
def root_properties(self, properties: exp.Properties) -> str:
if properties.expressions:
return self.sep() + self.expressions(properties, indent=False, sep=" ")
return self.expressions(properties, indent=False, sep=" ")
return ""
def properties(
@ -1346,7 +1386,7 @@ class Generator(metaclass=_Generator):
return ""
def with_properties(self, properties: exp.Properties) -> str:
return self.properties(properties, prefix=self.seg("WITH"))
return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
properties_locs = defaultdict(list)
@ -1514,19 +1554,25 @@ class Generator(metaclass=_Generator):
return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
sql = "WITH(SYSTEM_VERSIONING=ON"
this = self.sql(expression, "this")
this = f"HISTORY_TABLE={this}" if this else ""
data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
data_consistency = (
f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
)
retention_period: t.Optional[str] = self.sql(expression, "retention_period")
retention_period = (
f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
)
if expression.this:
history_table = self.sql(expression, "this")
sql = f"{sql}(HISTORY_TABLE={history_table}"
if this:
on_sql = self.func("ON", this, data_consistency, retention_period)
else:
on_sql = "ON" if expression.args.get("on") else "OFF"
if expression.expression:
data_consistency_check = self.sql(expression, "expression")
sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
sql = f"SYSTEM_VERSIONING={on_sql}"
sql = f"{sql})"
return f"{sql})"
return f"WITH({sql})" if expression.args.get("with") else sql
def insert_sql(self, expression: exp.Insert) -> str:
hint = self.sql(expression, "hint")
@ -2300,10 +2346,12 @@ class Generator(metaclass=_Generator):
def star_sql(self, expression: exp.Star) -> str:
except_ = self.expressions(expression, key="except", flat=True)
except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
replace = self.expressions(expression, key="replace", flat=True)
replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
return f"*{except_}{replace}"
replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
rename = self.expressions(expression, key="rename", flat=True)
rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
return f"*{except_}{replace}{rename}"
def parameter_sql(self, expression: exp.Parameter) -> str:
this = self.sql(expression, "this")
@ -2843,9 +2891,10 @@ class Generator(metaclass=_Generator):
stack.append(self.expressions(expression, sep=f" {op} "))
else:
stack.append(expression.right)
if expression.comments:
if expression.comments and self.comments:
for comment in expression.comments:
op += f" /*{self.pad_comment(comment)}*/"
if comment:
op += f" /*{self.pad_comment(comment)}*/"
stack.extend((op, expression.left))
return op
@ -2978,6 +3027,19 @@ class Generator(metaclass=_Generator):
return f"ALTER COLUMN {this} DROP DEFAULT"
def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
this = self.sql(expression, "this")
if not isinstance(expression.this, exp.Var):
this = f"KEY DISTKEY {this}"
return f"ALTER DISTSTYLE {this}"
def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
compound = " COMPOUND" if expression.args.get("compound") else ""
this = self.sql(expression, "this")
expressions = self.expressions(expression, flat=True)
expressions = f"({expressions})" if expressions else ""
return f"ALTER{compound} SORTKEY {this or expressions}"
def renametable_sql(self, expression: exp.RenameTable) -> str:
if not self.RENAME_TABLE_WITH_DB:
# Remove db from tables
@ -2993,6 +3055,10 @@ class Generator(metaclass=_Generator):
new_column = self.sql(expression, "to")
return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def alterset_sql(self, expression: exp.AlterSet) -> str:
exprs = self.expressions(expression, flat=True)
return f"SET {exprs}"
def altertable_sql(self, expression: exp.AlterTable) -> str:
actions = expression.args["actions"]
@ -3006,10 +3072,12 @@ class Generator(metaclass=_Generator):
actions = self.expressions(expression, key="actions", flat=True)
exists = " IF EXISTS" if expression.args.get("exists") else ""
on_cluster = self.sql(expression, "cluster")
on_cluster = f" {on_cluster}" if on_cluster else ""
only = " ONLY" if expression.args.get("only") else ""
options = self.expressions(expression, key="options")
options = f", {options}" if options else ""
return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"
def add_column_sql(self, expression: exp.AlterTable) -> str:
if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
@ -3781,6 +3849,11 @@ class Generator(metaclass=_Generator):
def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
option = self.sql(expression, "this")
if option.upper() == "FILE_FORMAT":
values = self.expressions(expression, key="expression", flat=True, sep=" ")
return f"{option} = ({values})"
value = self.sql(expression, "expression")
if not value:
@ -3802,7 +3875,6 @@ class Generator(metaclass=_Generator):
credentials = f"CREDENTIALS = ({credentials})" if credentials else ""
storage = self.sql(expression, "storage")
storage = f" {storage}" if storage else ""
encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
@ -3820,13 +3892,40 @@ class Generator(metaclass=_Generator):
this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
credentials = self.sql(expression, "credentials")
credentials = f" {credentials}" if credentials else ""
kind = " FROM " if expression.args.get("kind") else " TO "
credentials = self.seg(credentials) if credentials else ""
kind = self.seg("FROM" if expression.args.get("kind") else "TO")
files = self.expressions(expression, key="files", flat=True)
sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
params = self.expressions(expression, key="params", flat=True, sep=sep)
if params:
params = f" WITH ({params})" if self.COPY_PARAMS_ARE_WRAPPED else f" {params}"
params = self.expressions(
expression,
key="params",
sep=sep,
new_line=True,
skip_last=True,
skip_first=True,
indent=self.COPY_PARAMS_ARE_WRAPPED,
)
return f"COPY{this}{kind}{files}{credentials}{params}"
if params:
if self.COPY_PARAMS_ARE_WRAPPED:
params = f" WITH ({params})"
elif not self.pretty:
params = f" {params}"
return f"COPY{this}{kind} {files}{credentials}{params}"
def semicolon_sql(self, expression: exp.Semicolon) -> str:
return ""
def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
on_sql = "ON" if expression.args.get("on") else "OFF"
filter_col: t.Optional[str] = self.sql(expression, "filter_column")
filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
retention_period: t.Optional[str] = self.sql(expression, "retention_period")
retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
if filter_col or retention_period:
on_sql = self.func("ON", filter_col, retention_period)
return f"DATA_DELETION={on_sql}"