1
0
Fork 0

Merging upstream version 25.21.3.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 21:54:47 +01:00
parent 80aea2cba8
commit 34f177b0b3
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
105 changed files with 50314 additions and 49385 deletions

View file

@ -4,7 +4,7 @@ import logging
import re
import typing as t
from collections import defaultdict
from functools import reduce
from functools import reduce, wraps
from sqlglot import exp
from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
@ -17,9 +17,47 @@ if t.TYPE_CHECKING:
from sqlglot._typing import E
from sqlglot.dialects.dialect import DialectType
G = t.TypeVar("G", bound="Generator")
GeneratorMethod = t.Callable[[G, E], str]
logger = logging.getLogger("sqlglot")
ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
def unsupported_args(
*args: t.Union[str, t.Tuple[str, str]],
) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
"""
Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
"""
diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
for arg in args:
if isinstance(arg, str):
diagnostic_by_arg[arg] = None
else:
diagnostic_by_arg[arg[0]] = arg[1]
def decorator(func: GeneratorMethod) -> GeneratorMethod:
@wraps(func)
def _func(generator: G, expression: E) -> str:
expression_name = expression.__class__.__name__
dialect_name = generator.dialect.__class__.__name__
for arg_name, diagnostic in diagnostic_by_arg.items():
if expression.args.get(arg_name):
diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
arg_name, expression_name, dialect_name
)
generator.unsupported(diagnostic)
return func(generator, expression)
return _func
return decorator
class _Generator(type):
@ -154,6 +192,8 @@ class Generator(metaclass=_Generator):
exp.TransientProperty: lambda *_: "TRANSIENT",
exp.Union: lambda self, e: self.set_operations(e),
exp.UnloggedProperty: lambda *_: "UNLOGGED",
exp.UnpackColumns: lambda self, e: f"*{self.sql(e.this)}",
exp.Uuid: lambda *_: "UUID()",
exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
@ -389,9 +429,6 @@ class Generator(metaclass=_Generator):
# Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
SUPPORTS_CONVERT_TIMEZONE = False
# Whether nullable types can be constructed, e.g. `Nullable(Int64)`
SUPPORTS_NULLABLE_TYPES = True
# The name to generate for the JSONPath expression. If `None`, only `this` will be generated
PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
@ -1238,14 +1275,12 @@ class Generator(metaclass=_Generator):
type_value = expression.this
if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
type_sql = self.sql(expression, "kind")
elif type_value != exp.DataType.Type.NULLABLE or self.SUPPORTS_NULLABLE_TYPES:
else:
type_sql = (
self.TYPE_MAPPING.get(type_value, type_value.value)
if isinstance(type_value, exp.DataType.Type)
else type_value
)
else:
return interior
if interior:
if expression.args.get("nested"):
@ -1279,15 +1314,17 @@ class Generator(metaclass=_Generator):
this = f" FROM {this}" if this else ""
using = self.sql(expression, "using")
using = f" USING {using}" if using else ""
cluster = self.sql(expression, "cluster")
cluster = f" {cluster}" if cluster else ""
where = self.sql(expression, "where")
returning = self.sql(expression, "returning")
limit = self.sql(expression, "limit")
tables = self.expressions(expression, key="tables")
tables = f" {tables}" if tables else ""
if self.RETURNING_END:
expression_sql = f"{this}{using}{where}{returning}{limit}"
expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
else:
expression_sql = f"{returning}{this}{using}{where}{limit}"
expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: exp.Drop) -> str:
@ -1920,8 +1957,6 @@ class Generator(metaclass=_Generator):
direction = self.seg("UNPIVOT" if expression.unpivot else "PIVOT")
field = self.sql(expression, "field")
if field and isinstance(expression.args.get("field"), exp.PivotAny):
field = f"IN ({field})"
include_nulls = expression.args.get("include_nulls")
if include_nulls is not None:
@ -2742,6 +2777,10 @@ class Generator(metaclass=_Generator):
def jsonpath_sql(self, expression: exp.JSONPath) -> str:
path = self.expressions(expression, sep="", flat=True).lstrip(".")
if expression.args.get("escape"):
path = self.escape_str(path)
if self.QUOTE_JSON_PATH:
path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
@ -3587,15 +3626,16 @@ class Generator(metaclass=_Generator):
on = f"ON {self.sql(expression, 'on')}"
expressions = self.expressions(expression, sep=" ", indent=False)
sep = self.sep()
returning = self.expressions(expression, key="returning", indent=False)
returning = f"RETURNING {returning}" if returning else ""
return self.prepend_ctes(
expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}"
expression,
f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}{sep}{returning}",
)
@unsupported_args("format")
def tochar_sql(self, expression: exp.ToChar) -> str:
if expression.args.get("format"):
self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
def tonumber_sql(self, expression: exp.ToNumber) -> str: