Adding upstream version 21.0.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
07f4660f31
commit
91f2cef5f0
115 changed files with 66603 additions and 60920 deletions
|
@ -9,6 +9,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.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
|
||||
from sqlglot.time import format_time
|
||||
from sqlglot.tokens import TokenType
|
||||
|
||||
|
@ -21,7 +22,18 @@ logger = logging.getLogger("sqlglot")
|
|||
ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
|
||||
|
||||
|
||||
class Generator:
|
||||
class _Generator(type):
|
||||
def __new__(cls, clsname, bases, attrs):
|
||||
klass = super().__new__(cls, clsname, bases, attrs)
|
||||
|
||||
# Remove transforms that correspond to unsupported JSONPathPart expressions
|
||||
for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
|
||||
klass.TRANSFORMS.pop(part, None)
|
||||
|
||||
return klass
|
||||
|
||||
|
||||
class Generator(metaclass=_Generator):
|
||||
"""
|
||||
Generator converts a given syntax tree to the corresponding SQL string.
|
||||
|
||||
|
@ -58,19 +70,23 @@ class Generator:
|
|||
Default: True
|
||||
"""
|
||||
|
||||
TRANSFORMS = {
|
||||
TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
|
||||
**JSON_PATH_PART_TRANSFORMS,
|
||||
exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
|
||||
exp.CaseSpecificColumnConstraint: lambda self,
|
||||
e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
|
||||
exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
|
||||
exp.CharacterSetProperty: lambda self,
|
||||
e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
|
||||
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.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
|
||||
exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS",
|
||||
exp.DateAdd: lambda self, e: self.func(
|
||||
"DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
|
||||
),
|
||||
exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
|
||||
exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
|
||||
exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
|
||||
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')}",
|
||||
exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
|
||||
exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
|
||||
|
@ -85,29 +101,33 @@ class Generator:
|
|||
exp.LocationProperty: lambda self, e: self.naked_property(e),
|
||||
exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
|
||||
exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
|
||||
exp.NonClusteredColumnConstraint: lambda self,
|
||||
e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
|
||||
exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
|
||||
exp.NonClusteredColumnConstraint: lambda self, e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
|
||||
exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
|
||||
exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
|
||||
exp.OnCommitProperty: lambda self,
|
||||
e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
|
||||
exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
|
||||
exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
|
||||
exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
|
||||
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.RemoteWithConnectionModelProperty: lambda self,
|
||||
e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
|
||||
exp.ReturnsProperty: lambda self, e: self.naked_property(e),
|
||||
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.SetConfigProperty: lambda self, e: 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.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",
|
||||
exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
|
||||
exp.TransientProperty: lambda self, e: "TRANSIENT",
|
||||
exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
|
||||
exp.TemporaryProperty: lambda self, e: "TEMPORARY",
|
||||
exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
|
||||
exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
|
||||
exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
|
||||
exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
|
||||
exp.TransientProperty: lambda self, e: "TRANSIENT",
|
||||
exp.UppercaseColumnConstraint: lambda self, e: "UPPERCASE",
|
||||
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
|
||||
exp.VolatileProperty: lambda self, e: "VOLATILE",
|
||||
exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
|
||||
|
@ -117,6 +137,10 @@ class Generator:
|
|||
# True: Full Support, None: No support, False: No support in window specifications
|
||||
NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
|
||||
|
||||
# Whether or not ignore nulls is inside the agg or outside.
|
||||
# FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
|
||||
IGNORE_NULLS_IN_FUNC = False
|
||||
|
||||
# Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
|
||||
LOCKING_READS_SUPPORTED = False
|
||||
|
||||
|
@ -266,6 +290,24 @@ class Generator:
|
|||
# Whether or not UNLOGGED tables can be created
|
||||
SUPPORTS_UNLOGGED_TABLES = False
|
||||
|
||||
# Whether or not the CREATE TABLE LIKE statement is supported
|
||||
SUPPORTS_CREATE_TABLE_LIKE = True
|
||||
|
||||
# Whether or not the LikeProperty needs to be specified inside of the schema clause
|
||||
LIKE_PROPERTY_INSIDE_SCHEMA = False
|
||||
|
||||
# Whether or not the JSON extraction operators expect a value of type JSON
|
||||
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
|
||||
|
||||
# Whether or not bracketed keys like ["foo"] are supported in JSON paths
|
||||
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
|
||||
|
||||
# Whether or not to escape keys using single quotes in JSON paths
|
||||
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
|
||||
|
||||
# The JSONPathPart expressions supported by this dialect
|
||||
SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
|
||||
|
||||
TYPE_MAPPING = {
|
||||
exp.DataType.Type.NCHAR: "CHAR",
|
||||
exp.DataType.Type.NVARCHAR: "VARCHAR",
|
||||
|
@ -641,8 +683,6 @@ class Generator:
|
|||
|
||||
if callable(transform):
|
||||
sql = transform(self, expression)
|
||||
elif transform:
|
||||
sql = transform
|
||||
elif isinstance(expression, exp.Expression):
|
||||
exp_handler_name = f"{expression.key}_sql"
|
||||
|
||||
|
@ -802,7 +842,7 @@ class Generator:
|
|||
desc = expression.args.get("desc")
|
||||
if desc is not None:
|
||||
return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
|
||||
return f"PRIMARY KEY"
|
||||
return "PRIMARY KEY"
|
||||
|
||||
def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
|
||||
this = self.sql(expression, "this")
|
||||
|
@ -1218,9 +1258,21 @@ class Generator:
|
|||
return f"{property_name}={self.sql(expression, 'this')}"
|
||||
|
||||
def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
|
||||
options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
|
||||
options = f" {options}" if options else ""
|
||||
return f"LIKE {self.sql(expression, 'this')}{options}"
|
||||
if self.SUPPORTS_CREATE_TABLE_LIKE:
|
||||
options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
|
||||
options = f" {options}" if options else ""
|
||||
|
||||
like = f"LIKE {self.sql(expression, 'this')}{options}"
|
||||
if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
|
||||
like = f"({like})"
|
||||
|
||||
return like
|
||||
|
||||
if expression.expressions:
|
||||
self.unsupported("Transpilation of LIKE property options is unsupported")
|
||||
|
||||
select = exp.select("*").from_(expression.this).limit(0)
|
||||
return f"AS {self.sql(select)}"
|
||||
|
||||
def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
|
||||
no = "NO " if expression.args.get("no") else ""
|
||||
|
@ -2367,6 +2419,31 @@ class Generator:
|
|||
def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
|
||||
return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
|
||||
|
||||
def jsonpath_sql(self, expression: exp.JSONPath) -> str:
|
||||
path = self.expressions(expression, sep="", flat=True).lstrip(".")
|
||||
return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
|
||||
|
||||
def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
|
||||
if isinstance(expression, exp.JSONPathPart):
|
||||
transform = self.TRANSFORMS.get(expression.__class__)
|
||||
if not callable(transform):
|
||||
self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
|
||||
return ""
|
||||
|
||||
return transform(self, expression)
|
||||
|
||||
if isinstance(expression, int):
|
||||
return str(expression)
|
||||
|
||||
if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
|
||||
escaped = expression.replace("'", "\\'")
|
||||
escaped = f"\\'{expression}\\'"
|
||||
else:
|
||||
escaped = expression.replace('"', '\\"')
|
||||
escaped = f'"{escaped}"'
|
||||
|
||||
return escaped
|
||||
|
||||
def formatjson_sql(self, expression: exp.FormatJson) -> str:
|
||||
return f"{self.sql(expression, 'this')} FORMAT JSON"
|
||||
|
||||
|
@ -2620,6 +2697,9 @@ class Generator:
|
|||
zone = self.sql(expression, "this")
|
||||
return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
|
||||
|
||||
def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
|
||||
return self.func("CURRENT_TIMESTAMP", expression.this)
|
||||
|
||||
def collate_sql(self, expression: exp.Collate) -> str:
|
||||
if self.COLLATE_IS_FUNC:
|
||||
return self.function_fallback_sql(expression)
|
||||
|
@ -2761,10 +2841,20 @@ class Generator:
|
|||
return f"DISTINCT{this}{on}"
|
||||
|
||||
def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
|
||||
return f"{self.sql(expression, 'this')} IGNORE NULLS"
|
||||
return self._embed_ignore_nulls(expression, "IGNORE NULLS")
|
||||
|
||||
def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
|
||||
return f"{self.sql(expression, 'this')} RESPECT NULLS"
|
||||
return self._embed_ignore_nulls(expression, "RESPECT NULLS")
|
||||
|
||||
def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
|
||||
if self.IGNORE_NULLS_IN_FUNC:
|
||||
this = expression.find(exp.AggFunc)
|
||||
if this:
|
||||
sql = self.sql(this)
|
||||
sql = sql[:-1] + f" {text})"
|
||||
return sql
|
||||
|
||||
return f"{self.sql(expression, 'this')} {text}"
|
||||
|
||||
def intdiv_sql(self, expression: exp.IntDiv) -> str:
|
||||
return self.sql(
|
||||
|
@ -2935,7 +3025,7 @@ class Generator:
|
|||
def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
|
||||
arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
|
||||
if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
|
||||
return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
|
||||
return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
|
||||
return ", ".join(arg_sqls)
|
||||
|
||||
def text_width(self, args: t.Iterable) -> int:
|
||||
|
@ -3279,6 +3369,22 @@ class Generator:
|
|||
|
||||
return self.func("LAST_DAY", expression.this)
|
||||
|
||||
def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
|
||||
this = expression.this
|
||||
if isinstance(this, exp.JSONPathWildcard):
|
||||
this = self.json_path_part(this)
|
||||
return f".{this}" if this else ""
|
||||
|
||||
if exp.SAFE_IDENTIFIER_RE.match(this):
|
||||
return f".{this}"
|
||||
|
||||
this = self.json_path_part(this)
|
||||
return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}"
|
||||
|
||||
def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
|
||||
this = self.json_path_part(expression.this)
|
||||
return f"[{this}]" if this else ""
|
||||
|
||||
def _simplify_unless_literal(self, expression: E) -> E:
|
||||
if not isinstance(expression, exp.Literal):
|
||||
from sqlglot.optimizer.simplify import simplify
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue