1
0
Fork 0

Adding upstream version 20.9.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 21:18:57 +01:00
parent 943dfc0887
commit 8a068da99c
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
144 changed files with 78309 additions and 59609 deletions

View file

@ -68,6 +68,7 @@ class Generator:
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.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS",
exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
@ -96,6 +97,7 @@ class Generator:
exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
exp.SetProperty: lambda self, 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, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
exp.StabilityProperty: lambda self, e: e.name,
exp.TemporaryProperty: lambda self, e: f"TEMPORARY",
@ -110,7 +112,8 @@ class Generator:
}
# Whether or not null ordering is supported in order by
NULL_ORDERING_SUPPORTED = True
# True: Full Support, None: No support, False: No support in window specifications
NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
# Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
LOCKING_READS_SUPPORTED = False
@ -133,12 +136,6 @@ class Generator:
# Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs
INTERVAL_ALLOWS_PLURAL_FORM = True
# Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
TABLESAMPLE_WITH_METHOD = True
# Whether or not to treat the number in TABLESAMPLE (50) as a percentage
TABLESAMPLE_SIZE_IS_PERCENT = False
# Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
LIMIT_FETCH = "ALL"
@ -219,6 +216,18 @@ class Generator:
# Whether or not parentheses are required around the table sample's expression
TABLESAMPLE_REQUIRES_PARENS = True
# Whether or not a table sample clause's size needs to be followed by the ROWS keyword
TABLESAMPLE_SIZE_IS_ROWS = True
# The keyword(s) to use when generating a sample clause
TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
# Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
TABLESAMPLE_WITH_METHOD = True
# The keyword to use when specifying the seed of a sample clause
TABLESAMPLE_SEED_KEYWORD = "SEED"
# Whether or not COLLATE is a function instead of a binary operator
COLLATE_IS_FUNC = False
@ -234,6 +243,27 @@ class Generator:
# Whether or not CONCAT requires >1 arguments
SUPPORTS_SINGLE_ARG_CONCAT = True
# Whether or not LAST_DAY function supports a date part argument
LAST_DAY_SUPPORTS_DATE_PART = True
# Whether or not named columns are allowed in table aliases
SUPPORTS_TABLE_ALIAS_COLUMNS = True
# Whether or not UNPIVOT aliases are Identifiers (False means they're Literals)
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
# What delimiter to use for separating JSON key/value pairs
JSON_KEY_VALUE_PAIR_SEP = ":"
# INSERT OVERWRITE TABLE x override
INSERT_OVERWRITE = " OVERWRITE TABLE"
# Whether or not the SELECT .. INTO syntax is used instead of CTAS
SUPPORTS_SELECT_INTO = False
# Whether or not UNLOGGED tables can be created
SUPPORTS_UNLOGGED_TABLES = False
TYPE_MAPPING = {
exp.DataType.Type.NCHAR: "CHAR",
exp.DataType.Type.NVARCHAR: "VARCHAR",
@ -252,15 +282,15 @@ class Generator:
}
TIME_PART_SINGULARS = {
"microseconds": "microsecond",
"seconds": "second",
"minutes": "minute",
"hours": "hour",
"days": "day",
"weeks": "week",
"months": "month",
"quarters": "quarter",
"years": "year",
"MICROSECONDS": "MICROSECOND",
"SECONDS": "SECOND",
"MINUTES": "MINUTE",
"HOURS": "HOUR",
"DAYS": "DAY",
"WEEKS": "WEEK",
"MONTHS": "MONTH",
"QUARTERS": "QUARTER",
"YEARS": "YEAR",
}
TOKEN_MAPPING: t.Dict[TokenType, str] = {}
@ -272,6 +302,7 @@ class Generator:
PROPERTIES_LOCATION = {
exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
@ -323,6 +354,7 @@ class Generator:
exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
exp.SetProperty: exp.Properties.Location.POST_CREATE,
exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
@ -370,7 +402,7 @@ class Generator:
# 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_DEFINITONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice)
KEY_VALUE_DEFINITIONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice)
SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
@ -775,7 +807,7 @@ class Generator:
return self.sql(expression, "this")
def create_sql(self, expression: exp.Create) -> str:
kind = self.sql(expression, "kind").upper()
kind = self.sql(expression, "kind")
properties = expression.args.get("properties")
properties_locs = self.locate_properties(properties) if properties else defaultdict()
@ -868,7 +900,12 @@ class Generator:
return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: exp.Describe) -> str:
return f"DESCRIBE {self.sql(expression, 'this')}"
extended = " EXTENDED" if expression.args.get("extended") else ""
return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
def heredoc_sql(self, expression: exp.Heredoc) -> str:
tag = self.sql(expression, "tag")
return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
with_ = self.sql(expression, "with")
@ -895,6 +932,10 @@ class Generator:
columns = self.expressions(expression, key="columns", flat=True)
columns = f"({columns})" if columns else ""
if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
columns = ""
self.unsupported("Named columns are not supported in table alias.")
if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
alias = "_t"
@ -1027,7 +1068,7 @@ class Generator:
def fetch_sql(self, expression: exp.Fetch) -> str:
direction = expression.args.get("direction")
direction = f" {direction.upper()}" if direction else ""
direction = f" {direction}" if direction else ""
count = expression.args.get("count")
count = f" {count}" if count else ""
if expression.args.get("percent"):
@ -1318,7 +1359,7 @@ class Generator:
if isinstance(expression.this, exp.Directory):
this = " OVERWRITE" if overwrite else " INTO"
else:
this = " OVERWRITE TABLE" if overwrite else " INTO"
this = self.INSERT_OVERWRITE if overwrite else " INTO"
alternative = expression.args.get("alternative")
alternative = f" OR {alternative}" if alternative else ""
@ -1365,10 +1406,10 @@ class Generator:
return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: exp.PseudoType) -> str:
return expression.name.upper()
return expression.name
def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
return expression.name.upper()
return expression.name
def onconflict_sql(self, expression: exp.OnConflict) -> str:
conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
@ -1445,9 +1486,6 @@ class Generator:
pattern = f", PATTERN => {pattern}" if pattern else ""
file_format = f" (FILE_FORMAT => {file_format}{pattern})"
index = self.sql(expression, "index")
index = f" AT {index}" if index else ""
ordinality = expression.args.get("ordinality") or ""
if ordinality:
ordinality = f" WITH ORDINALITY{alias}"
@ -1457,10 +1495,13 @@ class Generator:
if when:
table = f"{table} {when}"
return f"{table}{version}{file_format}{alias}{index}{hints}{pivots}{joins}{laterals}{ordinality}"
return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
def tablesample_sql(
self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
self,
expression: exp.TableSample,
sep: str = " AS ",
tablesample_keyword: t.Optional[str] = None,
) -> str:
if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
table = expression.this.copy()
@ -1472,30 +1513,30 @@ class Generator:
alias = ""
method = self.sql(expression, "method")
method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
numerator = self.sql(expression, "bucket_numerator")
denominator = self.sql(expression, "bucket_denominator")
field = self.sql(expression, "bucket_field")
field = f" ON {field}" if field else ""
bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
percent = self.sql(expression, "percent")
percent = f"{percent} PERCENT" if percent else ""
rows = self.sql(expression, "rows")
rows = f"{rows} ROWS" if rows else ""
seed = self.sql(expression, "seed")
seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
size = self.sql(expression, "size")
if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
size = f"{size} PERCENT"
if size and self.TABLESAMPLE_SIZE_IS_ROWS:
size = f"{size} ROWS"
seed = self.sql(expression, "seed")
seed = f" {seed_prefix} ({seed})" if seed else ""
kind = expression.args.get("kind", "TABLESAMPLE")
percent = self.sql(expression, "percent")
if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
percent = f"{percent} PERCENT"
expr = f"{bucket}{percent}{rows}{size}"
expr = f"{bucket}{percent}{size}"
if self.TABLESAMPLE_REQUIRES_PARENS:
expr = f"({expr})"
return f"{this} {kind} {method}{expr}{seed}{alias}"
return (
f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
)
def pivot_sql(self, expression: exp.Pivot) -> str:
expressions = self.expressions(expression, flat=True)
@ -1513,8 +1554,7 @@ class Generator:
alias = self.sql(expression, "alias")
alias = f" AS {alias}" if alias else ""
unpivot = expression.args.get("unpivot")
direction = "UNPIVOT" if unpivot else "PIVOT"
direction = "UNPIVOT" if expression.unpivot else "PIVOT"
field = self.sql(expression, "field")
include_nulls = expression.args.get("include_nulls")
if include_nulls is not None:
@ -1675,7 +1715,8 @@ class Generator:
if not on_sql and using:
on_sql = csv(*(self.sql(column) for column in using))
this_sql = self.sql(expression, "this")
this = expression.this
this_sql = self.sql(this)
if on_sql:
on_sql = self.indent(on_sql, skip_first=True)
@ -1685,6 +1726,9 @@ class Generator:
else:
on_sql = f"{space}ON {on_sql}"
elif not op_sql:
if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
return f" {this_sql}"
return f", {this_sql}"
op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
@ -1695,6 +1739,19 @@ class Generator:
args = f"({args})" if len(args.split(",")) > 1 else args
return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_op(self, expression: exp.Lateral) -> str:
cross_apply = expression.args.get("cross_apply")
# https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
if cross_apply is True:
op = "INNER JOIN "
elif cross_apply is False:
op = "LEFT JOIN "
else:
op = ""
return f"{op}LATERAL"
def lateral_sql(self, expression: exp.Lateral) -> str:
this = self.sql(expression, "this")
@ -1708,7 +1765,7 @@ class Generator:
alias = self.sql(expression, "alias")
alias = f" AS {alias}" if alias else ""
return f"LATERAL {this}{alias}"
return f"{self.lateral_op(expression)} {this}{alias}"
def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
this = self.sql(expression, "this")
@ -1805,7 +1862,8 @@ class Generator:
def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
this = self.sql(expression, "this")
this = f"{this} " if this else this
order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore
siblings = "SIBLINGS " if expression.args.get("siblings") else ""
order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore
interpolated_values = [
f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
for named_expression in expression.args.get("interpolate") or []
@ -1860,9 +1918,21 @@ class Generator:
# If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
nulls_sort_change = ""
window = expression.find_ancestor(exp.Window, exp.Select)
if isinstance(window, exp.Window) and window.args.get("spec"):
self.unsupported(
f"'{nulls_sort_change.strip()}' translation not supported in window functions"
)
nulls_sort_change = ""
elif self.NULL_ORDERING_SUPPORTED is None:
if expression.this.is_int:
self.unsupported(
f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
)
else:
null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
nulls_sort_change = ""
with_fill = self.sql(expression, "with_fill")
with_fill = f" {with_fill}" if with_fill else ""
@ -1961,10 +2031,14 @@ class Generator:
return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: exp.Select) -> str:
into = expression.args.get("into")
if not self.SUPPORTS_SELECT_INTO and into:
into.pop()
hint = self.sql(expression, "hint")
distinct = self.sql(expression, "distinct")
distinct = f" {distinct}" if distinct else ""
kind = self.sql(expression, "kind").upper()
kind = self.sql(expression, "kind")
limit = expression.args.get("limit")
top = (
self.limit_sql(limit, top=True)
@ -2005,7 +2079,19 @@ class Generator:
self.sql(expression, "into", comment=False),
self.sql(expression, "from", comment=False),
)
return self.prepend_ctes(expression, sql)
sql = self.prepend_ctes(expression, sql)
if not self.SUPPORTS_SELECT_INTO and into:
if into.args.get("temporary"):
table_kind = " TEMPORARY"
elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
table_kind = " UNLOGGED"
else:
table_kind = ""
sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
return sql
def schema_sql(self, expression: exp.Schema) -> str:
this = self.sql(expression, "this")
@ -2266,29 +2352,35 @@ class Generator:
return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
def formatjson_sql(self, expression: exp.FormatJson) -> str:
return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql(self, expression: exp.JSONObject) -> str:
def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
null_handling = expression.args.get("null_handling")
null_handling = f" {null_handling}" if null_handling else ""
unique_keys = expression.args.get("unique_keys")
if unique_keys is not None:
unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
else:
unique_keys = ""
return_type = self.sql(expression, "return_type")
return_type = f" RETURNING {return_type}" if return_type else ""
encoding = self.sql(expression, "encoding")
encoding = f" ENCODING {encoding}" if encoding else ""
return self.func(
"JSON_OBJECT",
"JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
*expression.expressions,
suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
)
def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: exp.JSONArray) -> str:
null_handling = expression.args.get("null_handling")
null_handling = f" {null_handling}" if null_handling else ""
@ -2385,7 +2477,7 @@ class Generator:
def interval_sql(self, expression: exp.Interval) -> str:
unit = self.sql(expression, "unit")
if not self.INTERVAL_ALLOWS_PLURAL_FORM:
unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
unit = self.TIME_PART_SINGULARS.get(unit, unit)
unit = f" {unit}" if unit else ""
if self.SINGLE_STRING_INTERVAL:
@ -2436,9 +2528,25 @@ class Generator:
alias = f" AS {alias}" if alias else ""
return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
alias = expression.args["alias"]
identifier_alias = isinstance(alias, exp.Identifier)
if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
alias.replace(exp.Literal.string(alias.output_name))
elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
alias.replace(exp.to_identifier(alias.output_name))
return self.alias_sql(expression)
def aliases_sql(self, expression: exp.Aliases) -> str:
return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: exp.AtTimeZone) -> str:
this = self.sql(expression, "this")
index = self.sql(expression, "expression")
return f"{this} AT {index}"
def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
this = self.sql(expression, "this")
zone = self.sql(expression, "zone")
@ -2500,7 +2608,7 @@ class Generator:
return self.binary(expression, "COLLATE")
def command_sql(self, expression: exp.Command) -> str:
return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: exp.Comment) -> str:
this = self.sql(expression, "this")
@ -3102,6 +3210,47 @@ class Generator:
cond_for_null = arg.is_(exp.null())
return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg])))
def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
this = expression.this
if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
return self.sql(this)
return self.sql(exp.cast(this, "time"))
def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
this = expression.this
time_format = self.format_time(expression)
if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
return self.sql(
exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
)
if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
return self.sql(this)
return self.sql(exp.cast(this, "date"))
def unixdate_sql(self, expression: exp.UnixDate) -> str:
return self.sql(
exp.func(
"DATEDIFF",
expression.this,
exp.cast(exp.Literal.string("1970-01-01"), "date"),
"day",
)
)
def lastday_sql(self, expression: exp.LastDay) -> str:
if self.LAST_DAY_SUPPORTS_DATE_PART:
return self.function_fallback_sql(expression)
unit = expression.text("unit")
if unit and unit != "MONTH":
self.unsupported("Date parts are not supported in LAST_DAY.")
return self.func("LAST_DAY", expression.this)
def _simplify_unless_literal(self, expression: E) -> E:
if not isinstance(expression, exp.Literal):
from sqlglot.optimizer.simplify import simplify