Merging upstream version 17.2.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
06c5965633
commit
ff2afd7448
91 changed files with 42856 additions and 42624 deletions
|
@ -140,9 +140,21 @@ class Generator:
|
|||
# Whether or not table hints should be generated
|
||||
TABLE_HINTS = True
|
||||
|
||||
# Whether or not query hints should be generated
|
||||
QUERY_HINTS = True
|
||||
|
||||
# What kind of separator to use for query hints
|
||||
QUERY_HINT_SEP = ", "
|
||||
|
||||
# Whether or not comparing against booleans (e.g. x IS TRUE) is supported
|
||||
IS_BOOL_ALLOWED = True
|
||||
|
||||
# Whether or not to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
|
||||
DUPLICATE_KEY_UPDATE_WITH_SET = True
|
||||
|
||||
# Whether or not to generate the limit as TOP <value> instead of LIMIT <value>
|
||||
LIMIT_IS_TOP = False
|
||||
|
||||
# https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
|
||||
SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
|
||||
|
||||
|
@ -268,6 +280,7 @@ class Generator:
|
|||
STRICT_STRING_CONCAT = False
|
||||
NORMALIZE_FUNCTIONS: bool | str = "upper"
|
||||
NULL_ORDERING = "nulls_are_small"
|
||||
ESCAPE_LINE_BREAK = False
|
||||
|
||||
can_identify: t.Callable[[str, str | bool], bool]
|
||||
|
||||
|
@ -286,8 +299,6 @@ class Generator:
|
|||
HEX_END: t.Optional[str] = None
|
||||
BYTE_START: t.Optional[str] = None
|
||||
BYTE_END: t.Optional[str] = None
|
||||
RAW_START: t.Optional[str] = None
|
||||
RAW_END: t.Optional[str] = None
|
||||
|
||||
__slots__ = (
|
||||
"pretty",
|
||||
|
@ -486,7 +497,10 @@ class Generator:
|
|||
return expression
|
||||
|
||||
if key:
|
||||
return self.sql(expression.args.get(key))
|
||||
value = expression.args.get(key)
|
||||
if value:
|
||||
return self.sql(value)
|
||||
return ""
|
||||
|
||||
if self._cache is not None:
|
||||
expression_id = hash(expression)
|
||||
|
@ -779,10 +793,7 @@ class Generator:
|
|||
return this
|
||||
|
||||
def rawstring_sql(self, expression: exp.RawString) -> str:
|
||||
string = expression.this
|
||||
if self.RAW_START:
|
||||
return f"{self.RAW_START}{self.escape_str(expression.this)}{self.RAW_END}"
|
||||
string = self.escape_str(string.replace("\\", "\\\\"))
|
||||
string = self.escape_str(expression.this.replace("\\", "\\\\"))
|
||||
return f"{self.QUOTE_START}{string}{self.QUOTE_END}"
|
||||
|
||||
def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
|
||||
|
@ -818,15 +829,14 @@ class Generator:
|
|||
def delete_sql(self, expression: exp.Delete) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
this = f" FROM {this}" if this else ""
|
||||
using_sql = (
|
||||
f" USING {self.expressions(expression, key='using', sep=', USING ')}"
|
||||
if expression.args.get("using")
|
||||
else ""
|
||||
)
|
||||
where_sql = self.sql(expression, "where")
|
||||
using = self.sql(expression, "using")
|
||||
using = f" USING {using}" if using else ""
|
||||
where = self.sql(expression, "where")
|
||||
returning = self.sql(expression, "returning")
|
||||
limit = self.sql(expression, "limit")
|
||||
sql = f"DELETE{this}{using_sql}{where_sql}{returning}{limit}"
|
||||
tables = self.expressions(expression, key="tables")
|
||||
tables = f" {tables}" if tables else ""
|
||||
sql = f"DELETE{tables}{this}{using}{where}{returning}{limit}"
|
||||
return self.prepend_ctes(expression, sql)
|
||||
|
||||
def drop_sql(self, expression: exp.Drop) -> str:
|
||||
|
@ -867,9 +877,11 @@ class Generator:
|
|||
return f"{this} FILTER({where})"
|
||||
|
||||
def hint_sql(self, expression: exp.Hint) -> str:
|
||||
if self.sql(expression, "this"):
|
||||
if not self.QUERY_HINTS:
|
||||
self.unsupported("Hints are not supported")
|
||||
return ""
|
||||
return ""
|
||||
|
||||
return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
|
||||
|
||||
def index_sql(self, expression: exp.Index) -> str:
|
||||
unique = "UNIQUE " if expression.args.get("unique") else ""
|
||||
|
@ -1109,6 +1121,8 @@ class Generator:
|
|||
|
||||
alternative = expression.args.get("alternative")
|
||||
alternative = f" OR {alternative}" if alternative else ""
|
||||
ignore = " IGNORE" if expression.args.get("ignore") else ""
|
||||
|
||||
this = f"{this} {self.sql(expression, 'this')}"
|
||||
|
||||
exists = " IF EXISTS" if expression.args.get("exists") else ""
|
||||
|
@ -1120,7 +1134,7 @@ class Generator:
|
|||
expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
|
||||
conflict = self.sql(expression, "conflict")
|
||||
returning = self.sql(expression, "returning")
|
||||
sql = f"INSERT{alternative}{this}{exists}{partition_sql}{where}{expression_sql}{conflict}{returning}"
|
||||
sql = f"INSERT{alternative}{ignore}{this}{exists}{partition_sql}{where}{expression_sql}{conflict}{returning}"
|
||||
return self.prepend_ctes(expression, sql)
|
||||
|
||||
def intersect_sql(self, expression: exp.Intersect) -> str:
|
||||
|
@ -1147,8 +1161,9 @@ class Generator:
|
|||
do = "" if expression.args.get("duplicate") else " DO "
|
||||
nothing = "NOTHING" if expression.args.get("nothing") else ""
|
||||
expressions = self.expressions(expression, flat=True)
|
||||
set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
|
||||
if expressions:
|
||||
expressions = f"UPDATE SET {expressions}"
|
||||
expressions = f"UPDATE {set_keyword}{expressions}"
|
||||
return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
|
||||
|
||||
def returning_sql(self, expression: exp.Returning) -> str:
|
||||
|
@ -1195,7 +1210,7 @@ class Generator:
|
|||
hints = f" {hints}" if hints and self.TABLE_HINTS else ""
|
||||
pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
|
||||
pivots = f" {pivots}" if pivots else ""
|
||||
joins = self.expressions(expression, key="joins", sep="")
|
||||
joins = self.expressions(expression, key="joins", sep="", skip_first=True)
|
||||
laterals = self.expressions(expression, key="laterals", sep="")
|
||||
system_time = expression.args.get("system_time")
|
||||
system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
|
||||
|
@ -1287,6 +1302,10 @@ class Generator:
|
|||
|
||||
def group_sql(self, expression: exp.Group) -> str:
|
||||
group_by = self.op_expressions("GROUP BY", expression)
|
||||
|
||||
if expression.args.get("all"):
|
||||
return f"{group_by} ALL"
|
||||
|
||||
grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
|
||||
grouping_sets = (
|
||||
f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
|
||||
|
@ -1379,7 +1398,7 @@ class Generator:
|
|||
alias = f" AS {alias}" if alias else ""
|
||||
return f"LATERAL {this}{alias}"
|
||||
|
||||
def limit_sql(self, expression: exp.Limit) -> str:
|
||||
def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
args = ", ".join(
|
||||
sql
|
||||
|
@ -1389,7 +1408,7 @@ class Generator:
|
|||
)
|
||||
if sql
|
||||
)
|
||||
return f"{this}{self.seg('LIMIT')} {args}"
|
||||
return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args}"
|
||||
|
||||
def offset_sql(self, expression: exp.Offset) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
|
@ -1441,7 +1460,9 @@ class Generator:
|
|||
|
||||
def escape_str(self, text: str) -> str:
|
||||
text = text.replace(self.QUOTE_END, self._escaped_quote_end)
|
||||
if self.pretty:
|
||||
if self.ESCAPE_LINE_BREAK:
|
||||
text = text.replace("\n", "\\n")
|
||||
elif self.pretty:
|
||||
text = text.replace("\n", self.SENTINEL_LINE_BREAK)
|
||||
return text
|
||||
|
||||
|
@ -1544,6 +1565,9 @@ class Generator:
|
|||
def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
|
||||
limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
|
||||
|
||||
# If the limit is generated as TOP, we need to ensure it's not generated twice
|
||||
with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
|
||||
|
||||
if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
|
||||
limit = exp.Limit(expression=limit.args.get("count"))
|
||||
elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
|
||||
|
@ -1551,6 +1575,12 @@ class Generator:
|
|||
|
||||
fetch = isinstance(limit, exp.Fetch)
|
||||
|
||||
offset_limit_modifiers = (
|
||||
self.offset_limit_modifiers(expression, fetch, limit)
|
||||
if with_offset_limit_modifiers
|
||||
else []
|
||||
)
|
||||
|
||||
return csv(
|
||||
*sqls,
|
||||
*[self.sql(join) for join in expression.args.get("joins") or []],
|
||||
|
@ -1561,7 +1591,7 @@ class Generator:
|
|||
self.sql(expression, "having"),
|
||||
*self.after_having_modifiers(expression),
|
||||
self.sql(expression, "order"),
|
||||
*self.offset_limit_modifiers(expression, fetch, limit),
|
||||
*offset_limit_modifiers,
|
||||
*self.after_limit_modifiers(expression),
|
||||
sep="",
|
||||
)
|
||||
|
@ -1580,6 +1610,9 @@ class Generator:
|
|||
self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
|
||||
if expression.args.get("windows")
|
||||
else "",
|
||||
self.sql(expression, "distribute"),
|
||||
self.sql(expression, "sort"),
|
||||
self.sql(expression, "cluster"),
|
||||
]
|
||||
|
||||
def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
|
||||
|
@ -1592,6 +1625,13 @@ class Generator:
|
|||
distinct = self.sql(expression, "distinct")
|
||||
distinct = f" {distinct}" if distinct else ""
|
||||
kind = self.sql(expression, "kind").upper()
|
||||
limit = expression.args.get("limit")
|
||||
top = (
|
||||
self.limit_sql(limit, top=True)
|
||||
if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
|
||||
else ""
|
||||
)
|
||||
|
||||
expressions = self.expressions(expression)
|
||||
|
||||
if kind:
|
||||
|
@ -1618,7 +1658,7 @@ class Generator:
|
|||
expressions = f"{self.sep()}{expressions}" if expressions else expressions
|
||||
sql = self.query_modifiers(
|
||||
expression,
|
||||
f"SELECT{hint}{distinct}{kind}{expressions}",
|
||||
f"SELECT{top}{hint}{distinct}{kind}{expressions}",
|
||||
self.sql(expression, "into", comment=False),
|
||||
self.sql(expression, "from", comment=False),
|
||||
)
|
||||
|
@ -2288,6 +2328,7 @@ class Generator:
|
|||
sqls: t.Optional[t.List[str]] = None,
|
||||
flat: bool = False,
|
||||
indent: bool = True,
|
||||
skip_first: bool = False,
|
||||
sep: str = ", ",
|
||||
prefix: str = "",
|
||||
) -> str:
|
||||
|
@ -2321,7 +2362,7 @@ class Generator:
|
|||
result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
|
||||
|
||||
result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
|
||||
return self.indent(result_sql, skip_first=False) if indent else result_sql
|
||||
return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
|
||||
|
||||
def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
|
||||
flat = flat or isinstance(expression.parent, exp.Properties)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue