Adding upstream version 23.16.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
9d7e0ff7aa
commit
b6ae88ec81
93 changed files with 64106 additions and 59061 deletions
|
@ -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}"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue