sqlglotc 30.19.0__tar.gz → 30.21.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlglotc-30.19.0/sqlglotc.egg-info → sqlglotc-30.21.0}/PKG-INFO +2 -2
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/json.py +1 -1
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/query.py +13 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generator.py +43 -3
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/athena.py +1 -1
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/bigquery.py +5 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/clickhouse.py +1 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/duckdb.py +7 -5
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/mysql.py +2 -2
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/singlestore.py +6 -1
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/spark.py +3 -3
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/spark2.py +10 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/sqlite.py +13 -6
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/starrocks.py +19 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/canonicalize_internal_names.py +6 -4
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/qualify_columns.py +40 -15
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/resolver.py +16 -12
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/simplify.py +5 -2
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parser.py +30 -11
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/athena.py +5 -2
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/clickhouse.py +9 -6
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/hive.py +37 -1
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/spark.py +2 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/spark2.py +2 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/teradata.py +2 -1
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/tokenizer_core.py +110 -4
- {sqlglotc-30.19.0 → sqlglotc-30.21.0/sqlglotc.egg-info}/PKG-INFO +2 -2
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglotc.egg-info/requires.txt +1 -1
- sqlglotc-30.21.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.19.0/sqlglotc.egg-info/scm_version.json +0 -8
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/MANIFEST.in +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/pyproject.toml +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/setup.cfg +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/setup.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/anonymize.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/array.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/builders.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/constraints.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/core.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/ddl.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/properties.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/string.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/databricks.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/dax.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/exasol.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/hive.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/oracle.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/postgres.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/presto.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/snowflake.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/trino.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/generators/tsql.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/annotate_types.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/qualify_tables.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/optimizer/scope.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/bigquery.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/databricks.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/dax.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/duckdb.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/mysql.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/oracle.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/postgres.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/prql.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/snowflake.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/sqlite.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/starrocks.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/trino.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/parsers/tsql.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
- {sqlglotc-30.19.0 → sqlglotc-30.21.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.21.0
|
|
4
4
|
Summary: mypyc-compiled extensions for sqlglot
|
|
5
5
|
Author-email: Toby Mao <toby.mao@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://sqlglot.com/
|
|
8
8
|
Project-URL: Repository, https://github.com/tobymao/sqlglot
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
|
-
Requires-Dist: sqlglot==30.
|
|
10
|
+
Requires-Dist: sqlglot==30.21.0
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
13
13
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
@@ -248,4 +248,4 @@ class ParseJSON(Expression, Func):
|
|
|
248
248
|
# BigQuery, Snowflake have PARSE_JSON, Presto has JSON_PARSE
|
|
249
249
|
# Snowflake also has TRY_PARSE_JSON, which is represented using `safe`
|
|
250
250
|
_sql_names = ["PARSE_JSON", "JSON_PARSE"]
|
|
251
|
-
arg_types = {"this": True, "expression": False, "safe": False}
|
|
251
|
+
arg_types = {"this": True, "expression": False, "safe": False, "is_literal": False}
|
|
@@ -804,6 +804,19 @@ class Lateral(Expression, UDTF):
|
|
|
804
804
|
"ordinality": False,
|
|
805
805
|
}
|
|
806
806
|
|
|
807
|
+
@property
|
|
808
|
+
def selects(self) -> list[Expr]:
|
|
809
|
+
from sqlglot.expressions.array import Unnest
|
|
810
|
+
|
|
811
|
+
columns = super().selects
|
|
812
|
+
|
|
813
|
+
# UNNEST ... WITH ORDINALITY stores the ordinality column's name in Unnest.offset
|
|
814
|
+
offset = self.this.args.get("offset") if isinstance(self.this, Unnest) else None
|
|
815
|
+
if isinstance(offset, Identifier):
|
|
816
|
+
columns = columns + [offset]
|
|
817
|
+
|
|
818
|
+
return columns
|
|
819
|
+
|
|
807
820
|
|
|
808
821
|
class TableFromRows(Expression, UDTF):
|
|
809
822
|
arg_types = {
|
|
@@ -2959,7 +2959,20 @@ class Generator:
|
|
|
2959
2959
|
op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
|
|
2960
2960
|
return f"{op_sql}{self.sep()}{this}{table}{columns}"
|
|
2961
2961
|
|
|
2962
|
-
|
|
2962
|
+
table_alias = expression.args.get("alias")
|
|
2963
|
+
offset = expression.this.args.get("offset")
|
|
2964
|
+
|
|
2965
|
+
if (
|
|
2966
|
+
self.UNNEST_WITH_ORDINALITY
|
|
2967
|
+
and table_alias
|
|
2968
|
+
and isinstance(expression.this, exp.Unnest)
|
|
2969
|
+
and isinstance(offset, exp.Identifier)
|
|
2970
|
+
):
|
|
2971
|
+
# UNNEST ... WITH ORDINALITY stores the ordinality column's name in Unnest.offset
|
|
2972
|
+
table_alias = table_alias.copy()
|
|
2973
|
+
table_alias.append("columns", offset.copy())
|
|
2974
|
+
|
|
2975
|
+
alias = self.sql(table_alias)
|
|
2963
2976
|
alias = f" AS {alias}" if alias else ""
|
|
2964
2977
|
|
|
2965
2978
|
ordinality = expression.args.get("ordinality") or ""
|
|
@@ -4942,15 +4955,20 @@ class Generator:
|
|
|
4942
4955
|
def tonumber_sql(self, expression: exp.ToNumber) -> str:
|
|
4943
4956
|
if not self.SUPPORTS_TO_NUMBER:
|
|
4944
4957
|
self.unsupported("Unsupported TO_NUMBER function")
|
|
4945
|
-
return self.
|
|
4958
|
+
return self._tonumber_cast_sql(expression)
|
|
4946
4959
|
|
|
4947
4960
|
fmt = expression.args.get("format")
|
|
4948
4961
|
if not fmt:
|
|
4949
4962
|
self.unsupported("Conversion format is required for TO_NUMBER")
|
|
4950
|
-
return self.
|
|
4963
|
+
return self._tonumber_cast_sql(expression)
|
|
4951
4964
|
|
|
4952
4965
|
return self.func("TO_NUMBER", expression.this, fmt)
|
|
4953
4966
|
|
|
4967
|
+
def _tonumber_cast_sql(self, expression: exp.ToNumber) -> str:
|
|
4968
|
+
if expression.args.get("safe"):
|
|
4969
|
+
return self.sql(exp.TryCast(this=expression.this, to=exp.DataType.build("DOUBLE")))
|
|
4970
|
+
return self.sql(exp.cast(expression.this, exp.DType.DOUBLE))
|
|
4971
|
+
|
|
4954
4972
|
def dictproperty_sql(self, expression: exp.DictProperty) -> str:
|
|
4955
4973
|
this = self.sql(expression, "this")
|
|
4956
4974
|
kind = self.sql(expression, "kind")
|
|
@@ -5304,6 +5322,28 @@ class Generator:
|
|
|
5304
5322
|
sqlglot.dialects.dialect.unit_to_str(expression),
|
|
5305
5323
|
)
|
|
5306
5324
|
|
|
5325
|
+
def arrayinsert_sql(self, expression: exp.ArrayInsert, index_offset: int = 0) -> str:
|
|
5326
|
+
this = expression.this
|
|
5327
|
+
position = expression.args["position"]
|
|
5328
|
+
offset = index_offset - (expression.args.get("offset") or 0)
|
|
5329
|
+
|
|
5330
|
+
if offset:
|
|
5331
|
+
if position.is_int:
|
|
5332
|
+
value = position.to_py()
|
|
5333
|
+
if value >= 0:
|
|
5334
|
+
position = exp.Literal.number(value + offset)
|
|
5335
|
+
elif offset < 0 and value == -1:
|
|
5336
|
+
# 1-based -1 appends, which a 0-based position can only express as the size
|
|
5337
|
+
position = exp.ArraySize(this=this.copy())
|
|
5338
|
+
else:
|
|
5339
|
+
# Negative positions count from the end, so they shift in the opposite
|
|
5340
|
+
# direction, e.g. 0-based -1 (before the last element) is 1-based -2
|
|
5341
|
+
position = exp.Literal.number(value - offset)
|
|
5342
|
+
else:
|
|
5343
|
+
self.unsupported("ARRAY_INSERT position can only be converted if it's a literal")
|
|
5344
|
+
|
|
5345
|
+
return self.func("ARRAY_INSERT", this, position, expression.expression)
|
|
5346
|
+
|
|
5307
5347
|
def arrayany_sql(self, expression: exp.ArrayAny) -> str:
|
|
5308
5348
|
if self.CAN_IMPLEMENT_ARRAY_ANY:
|
|
5309
5349
|
filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
|
|
@@ -713,6 +713,11 @@ class BigQueryGenerator(generator.Generator):
|
|
|
713
713
|
|
|
714
714
|
return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope"))
|
|
715
715
|
|
|
716
|
+
def parsejson_sql(self, expression: exp.ParseJSON) -> str:
|
|
717
|
+
if expression.args.get("is_literal"):
|
|
718
|
+
return f"JSON {self.sql(expression, 'this')}"
|
|
719
|
+
return super().parsejson_sql(expression)
|
|
720
|
+
|
|
716
721
|
def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
|
|
717
722
|
this = expression.this
|
|
718
723
|
|
|
@@ -370,6 +370,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
370
370
|
exp.UnixToTime: _unix_to_time_sql,
|
|
371
371
|
exp.Trim: lambda self, e: trim_sql(self, e, default_trim_type="BOTH"),
|
|
372
372
|
exp.Variance: rename_func("varSamp"),
|
|
373
|
+
exp.VariancePop: rename_func("varPop"),
|
|
373
374
|
exp.SchemaCommentProperty: lambda self, e: self.naked_property(e),
|
|
374
375
|
exp.Stddev: rename_func("stddevSamp"),
|
|
375
376
|
exp.Chr: rename_func("CHAR"),
|
|
@@ -2577,11 +2577,10 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2577
2577
|
scale = expression.args.get("scale")
|
|
2578
2578
|
|
|
2579
2579
|
if not fmt and precision and scale:
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
)
|
|
2580
|
+
to = exp.DataType.build(f"DECIMAL({precision.name}, {scale.name})", dialect="duckdb")
|
|
2581
|
+
if expression.args.get("safe"):
|
|
2582
|
+
return self.sql(exp.TryCast(this=expression.this, to=to))
|
|
2583
|
+
return self.sql(exp.cast(expression.this, to, dialect="duckdb"))
|
|
2585
2584
|
|
|
2586
2585
|
return super().tonumber_sql(expression)
|
|
2587
2586
|
|
|
@@ -3075,6 +3074,9 @@ class DuckDBGenerator(generator.Generator):
|
|
|
3075
3074
|
if milli or nano:
|
|
3076
3075
|
expression.set("sec", sec)
|
|
3077
3076
|
|
|
3077
|
+
if zone := expression.args.get("zone"):
|
|
3078
|
+
return self.sql(exp.AtTimeZone(this=expression, zone=zone.pop()))
|
|
3079
|
+
|
|
3078
3080
|
return rename_func("MAKE_TIMESTAMP")(self, expression)
|
|
3079
3081
|
|
|
3080
3082
|
@unsupported_args("nano")
|
|
@@ -154,9 +154,9 @@ class MySQLGenerator(generator.Generator):
|
|
|
154
154
|
exp.DateDiff: remove_ts_or_ds_to_date(
|
|
155
155
|
lambda self, e: self.func("DATEDIFF", e.this, e.expression), ("this", "expression")
|
|
156
156
|
),
|
|
157
|
-
exp.DateAdd:
|
|
157
|
+
exp.DateAdd: date_add_sql("ADD"),
|
|
158
158
|
exp.DateStrToDate: datestrtodate_sql,
|
|
159
|
-
exp.DateSub:
|
|
159
|
+
exp.DateSub: date_add_sql("SUB"),
|
|
160
160
|
exp.DateTrunc: _date_trunc_sql,
|
|
161
161
|
exp.Day: remove_ts_or_ds_to_date(),
|
|
162
162
|
exp.DayOfMonth: remove_ts_or_ds_to_date(rename_func("DAYOFMONTH")),
|
|
@@ -1529,7 +1529,12 @@ class SingleStoreGenerator(MySQLGenerator):
|
|
|
1529
1529
|
|
|
1530
1530
|
def collate_sql(self, expression: exp.Collate) -> str:
|
|
1531
1531
|
# SingleStore does not support setting a collation for column in the SELECT query,
|
|
1532
|
-
# so we cast column to a LONGTEXT type with specific collation
|
|
1532
|
+
# so we cast column to a LONGTEXT type with specific collation. If the cast is
|
|
1533
|
+
# already there (e.g. when regenerating previously generated SQL), don't add
|
|
1534
|
+
# another one, so that generation stays idempotent.
|
|
1535
|
+
this = expression.this
|
|
1536
|
+
if isinstance(this, exp.Cast) and this.to.this == exp.DType.LONGTEXT:
|
|
1537
|
+
return self.binary(expression, "COLLATE")
|
|
1533
1538
|
return self.binary(expression, ":> LONGTEXT COLLATE")
|
|
1534
1539
|
|
|
1535
1540
|
def currentdate_sql(self, expression: exp.CurrentDate) -> str:
|
|
@@ -95,9 +95,6 @@ class SparkGenerator(Spark2Generator):
|
|
|
95
95
|
exp.ArrayConstructCompact: lambda self, e: self.func(
|
|
96
96
|
"ARRAY_COMPACT", self.func("ARRAY", *e.expressions)
|
|
97
97
|
),
|
|
98
|
-
exp.ArrayInsert: lambda self, e: self.func(
|
|
99
|
-
"ARRAY_INSERT", e.this, e.args.get("position"), e.expression
|
|
100
|
-
),
|
|
101
98
|
exp.ArrayAppend: array_append_sql("ARRAY_APPEND"),
|
|
102
99
|
exp.ArrayPrepend: array_append_sql("ARRAY_PREPEND"),
|
|
103
100
|
exp.BitwiseAndAgg: rename_func("BIT_AND"),
|
|
@@ -152,6 +149,9 @@ class SparkGenerator(Spark2Generator):
|
|
|
152
149
|
def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
|
|
153
150
|
return generator.Generator.ignorenulls_sql(self, expression)
|
|
154
151
|
|
|
152
|
+
def arrayinsert_sql(self, expression: exp.ArrayInsert, index_offset: int = 0) -> str:
|
|
153
|
+
return super().arrayinsert_sql(expression, index_offset=1)
|
|
154
|
+
|
|
155
155
|
def bracket_sql(self, expression: exp.Bracket) -> str:
|
|
156
156
|
if expression.args.get("safe"):
|
|
157
157
|
key = seq_get(self.bracket_offset_expressions(expression, index_offset=1), 0)
|
|
@@ -220,6 +220,16 @@ class Spark2Generator(HiveGenerator):
|
|
|
220
220
|
|
|
221
221
|
def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
|
|
222
222
|
arg = expression.this
|
|
223
|
+
if (
|
|
224
|
+
isinstance(arg, exp.Literal)
|
|
225
|
+
and arg.is_number
|
|
226
|
+
and expression.to.is_type(exp.DType.DECIMAL)
|
|
227
|
+
and len(expression.to.expressions) == 2
|
|
228
|
+
and isinstance(expression.to.expressions[1].this, exp.Neg)
|
|
229
|
+
):
|
|
230
|
+
# Spark 2 supports negative scales in literals, but not in DECIMAL type syntax.
|
|
231
|
+
return f"{self.sql(arg)}BD"
|
|
232
|
+
|
|
223
233
|
is_json_extract = isinstance(
|
|
224
234
|
arg, (exp.JSONExtract, exp.JSONExtractScalar)
|
|
225
235
|
) and not arg.args.get("variant_extract")
|
|
@@ -20,7 +20,7 @@ from sqlglot.tokens import TokenType
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _transform_create(expression: exp.Expr) -> exp.Expr:
|
|
23
|
-
"""Move primary key to a column and
|
|
23
|
+
"""Move primary key to a column and place auto_increment after it."""
|
|
24
24
|
schema = expression.this
|
|
25
25
|
|
|
26
26
|
if isinstance(expression, exp.Create) and isinstance(schema, exp.Schema):
|
|
@@ -52,12 +52,9 @@ def _transform_create(expression: exp.Expr) -> exp.Expr:
|
|
|
52
52
|
auto_increment = constraint
|
|
53
53
|
auto_increment_index = i
|
|
54
54
|
|
|
55
|
-
if auto_increment is not None and
|
|
56
|
-
primary_key_index == -1 or auto_increment_index < primary_key_index
|
|
57
|
-
):
|
|
55
|
+
if auto_increment is not None and 0 <= auto_increment_index < primary_key_index:
|
|
58
56
|
column.constraints.remove(auto_increment)
|
|
59
|
-
|
|
60
|
-
column.constraints.insert(primary_key_index, auto_increment)
|
|
57
|
+
column.constraints.insert(primary_key_index, auto_increment)
|
|
61
58
|
|
|
62
59
|
return expression
|
|
63
60
|
|
|
@@ -206,6 +203,16 @@ class SQLiteGenerator(generator.Generator):
|
|
|
206
203
|
|
|
207
204
|
LIMIT_FETCH = "LIMIT"
|
|
208
205
|
|
|
206
|
+
def autoincrementcolumnconstraint_sql(
|
|
207
|
+
self, expression: exp.AutoIncrementColumnConstraint
|
|
208
|
+
) -> str:
|
|
209
|
+
column = expression.find_ancestor(exp.ColumnDef)
|
|
210
|
+
if column and column.find(exp.PrimaryKeyColumnConstraint):
|
|
211
|
+
return super().autoincrementcolumnconstraint_sql(expression)
|
|
212
|
+
|
|
213
|
+
self.unsupported("SQLite AUTOINCREMENT requires an INTEGER PRIMARY KEY")
|
|
214
|
+
return ""
|
|
215
|
+
|
|
209
216
|
def insert_sql(self, expression: exp.Insert) -> str:
|
|
210
217
|
if expression.args.get("ignore"):
|
|
211
218
|
expression.set("ignore", False)
|
|
@@ -12,6 +12,7 @@ from sqlglot.dialects.dialect import (
|
|
|
12
12
|
property_sql,
|
|
13
13
|
var_map_sql,
|
|
14
14
|
)
|
|
15
|
+
from sqlglot.generator import Generator
|
|
15
16
|
from sqlglot.generators.mysql import MySQLGenerator
|
|
16
17
|
|
|
17
18
|
|
|
@@ -292,6 +293,24 @@ class StarRocksGenerator(MySQLGenerator):
|
|
|
292
293
|
"with",
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
|
|
297
|
+
# IGNORE NULLS follows the first argument, e.g. LAG(x IGNORE NULLS, 1, 0)
|
|
298
|
+
# https://docs.starrocks.io/docs/sql-reference/sql-functions/Window_function/
|
|
299
|
+
func = expression.this
|
|
300
|
+
if isinstance(func, (exp.FirstValue, exp.LastValue, exp.Lag, exp.Lead)):
|
|
301
|
+
return self.func(
|
|
302
|
+
func.sql_name(),
|
|
303
|
+
Generator.ignorenulls_sql(self, exp.IgnoreNulls(this=func.this)),
|
|
304
|
+
func.args.get("offset"),
|
|
305
|
+
func.args.get("default"),
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return super().ignorenulls_sql(expression)
|
|
309
|
+
|
|
310
|
+
def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
|
|
311
|
+
# RESPECT NULLS is the default behavior, and StarRocks rejects the keyword
|
|
312
|
+
return self.sql(expression, "this")
|
|
313
|
+
|
|
295
314
|
def create_sql(self, expression: exp.Create) -> str:
|
|
296
315
|
# Starrocks' primary key is defined outside of the schema, so we need to move it there
|
|
297
316
|
schema = expression.this
|
|
@@ -205,10 +205,12 @@ def canonicalize_internal_names(expression: E) -> E:
|
|
|
205
205
|
],
|
|
206
206
|
)
|
|
207
207
|
|
|
208
|
-
#
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
# UNNEST stores its offset column outside the alias, including under LATERAL.
|
|
209
|
+
unnest = (
|
|
210
|
+
alias_holder.this if isinstance(alias_holder, exp.Lateral) else alias_holder
|
|
211
|
+
)
|
|
212
|
+
if isinstance(unnest, exp.Unnest):
|
|
213
|
+
offset_id = unnest.args.get("offset")
|
|
212
214
|
if isinstance(offset_id, exp.Identifier) and offset_id.name in name_map:
|
|
213
215
|
_canon(offset_id, name_map[offset_id.name])
|
|
214
216
|
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import itertools
|
|
4
4
|
import re
|
|
5
5
|
import typing as t
|
|
6
|
+
from collections import Counter
|
|
6
7
|
|
|
7
8
|
from sqlglot import alias, exp
|
|
8
9
|
from sqlglot.dialects.dialect import Dialect, DialectType
|
|
@@ -492,6 +493,8 @@ def _expand_order_by_and_distinct_on(scope: Scope, resolver: Resolver) -> None:
|
|
|
492
493
|
if not isinstance(expression, exp.Selectable):
|
|
493
494
|
return
|
|
494
495
|
|
|
496
|
+
alias_names = {name for name, count in Counter(expression.named_selects).items() if count == 1}
|
|
497
|
+
|
|
495
498
|
# TODO (mypyc): rebind to exp.Expr to avoid Selectable trait vtable dispatch for .args
|
|
496
499
|
expr: exp.Expr = expression
|
|
497
500
|
|
|
@@ -510,7 +513,7 @@ def _expand_order_by_and_distinct_on(scope: Scope, resolver: Resolver) -> None:
|
|
|
510
513
|
for original, expanded in zip(
|
|
511
514
|
modifier_expressions,
|
|
512
515
|
_expand_positional_references(
|
|
513
|
-
scope, modifier_expressions, resolver.dialect,
|
|
516
|
+
scope, modifier_expressions, resolver.dialect, alias_names=alias_names
|
|
514
517
|
),
|
|
515
518
|
):
|
|
516
519
|
for agg in original.find_all(exp.AggFunc):
|
|
@@ -521,18 +524,23 @@ def _expand_order_by_and_distinct_on(scope: Scope, resolver: Resolver) -> None:
|
|
|
521
524
|
original.replace(expanded)
|
|
522
525
|
|
|
523
526
|
if expr.args.get("group"):
|
|
524
|
-
selects = {
|
|
527
|
+
selects = {
|
|
528
|
+
s.this: exp.column(s.alias_or_name)
|
|
529
|
+
for s in expression.selects
|
|
530
|
+
if s.alias_or_name in alias_names
|
|
531
|
+
}
|
|
525
532
|
|
|
526
533
|
for node in modifier_expressions:
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
else selects.get(node, node)
|
|
531
|
-
)
|
|
534
|
+
# Remaining ordinals reference duplicate aliases and must stay positional.
|
|
535
|
+
if not node.is_int:
|
|
536
|
+
node.replace(selects.get(node, node))
|
|
532
537
|
|
|
533
538
|
|
|
534
539
|
def _expand_positional_references(
|
|
535
|
-
scope: Scope,
|
|
540
|
+
scope: Scope,
|
|
541
|
+
expressions: Iterable[exp.Expr],
|
|
542
|
+
dialect: Dialect,
|
|
543
|
+
alias_names: set[str] | None = None,
|
|
536
544
|
) -> list[exp.Expr]:
|
|
537
545
|
new_nodes: list[exp.Expr] = []
|
|
538
546
|
ambiguous_projections = None
|
|
@@ -546,8 +554,10 @@ def _expand_positional_references(
|
|
|
546
554
|
if node.is_int and isinstance(node, exp.Literal):
|
|
547
555
|
select = _select_by_pos(expression, node)
|
|
548
556
|
|
|
549
|
-
if
|
|
550
|
-
new_nodes.append(
|
|
557
|
+
if alias_names is not None:
|
|
558
|
+
new_nodes.append(
|
|
559
|
+
exp.column(select.args["alias"].copy()) if select.alias in alias_names else node
|
|
560
|
+
)
|
|
551
561
|
else:
|
|
552
562
|
# TODO (mypyc): use a separate variable to avoid reusing `select` (Alias) with a different type
|
|
553
563
|
select_expr: exp.Expr = select.this
|
|
@@ -606,11 +616,25 @@ def _convert_columns_to_dots(scope: Scope, resolver: Resolver) -> None:
|
|
|
606
616
|
is_star = isinstance(column.this, exp.Star)
|
|
607
617
|
column_table: str | exp.Identifier | None = column.table
|
|
608
618
|
dot_parts = column.meta.pop("dot_parts", [])
|
|
619
|
+
root_resolver: Resolver | None = None
|
|
620
|
+
if not is_star and column.db and column_table in scope.selected_sources:
|
|
621
|
+
root, field = column.parts[:2]
|
|
622
|
+
for candidate_resolver in itertools.chain((resolver,), resolver.outer_resolvers()):
|
|
623
|
+
if root.name not in candidate_resolver.scope.selected_sources:
|
|
624
|
+
continue
|
|
625
|
+
if field.name in candidate_resolver.get_source_columns(root.name):
|
|
626
|
+
root_resolver = candidate_resolver
|
|
627
|
+
break
|
|
609
628
|
if (
|
|
610
629
|
column_table
|
|
611
|
-
and (column_table not in scope.selected_sources or (is_star and column.db))
|
|
612
630
|
and (
|
|
613
|
-
|
|
631
|
+
column_table not in scope.selected_sources
|
|
632
|
+
or (is_star and column.db)
|
|
633
|
+
or root_resolver
|
|
634
|
+
)
|
|
635
|
+
and (
|
|
636
|
+
root_resolver
|
|
637
|
+
or is_star
|
|
614
638
|
or not scope.parent
|
|
615
639
|
or column_table not in scope.parent.sources
|
|
616
640
|
or not scope.is_correlated_subquery
|
|
@@ -619,10 +643,11 @@ def _convert_columns_to_dots(scope: Scope, resolver: Resolver) -> None:
|
|
|
619
643
|
root, *parts = column.parts
|
|
620
644
|
was_qualified = False
|
|
621
645
|
|
|
622
|
-
#
|
|
623
|
-
# must be expanded in this one, so they're resolved against those scopes as well
|
|
646
|
+
# Correlated stars cannot be deferred; qualified paths use their root source's resolver.
|
|
624
647
|
resolvers: t.Iterable[Resolver] = (
|
|
625
|
-
itertools.chain((resolver,), resolver.outer_resolvers())
|
|
648
|
+
itertools.chain((resolver,), resolver.outer_resolvers())
|
|
649
|
+
if is_star
|
|
650
|
+
else (root_resolver or resolver,)
|
|
626
651
|
)
|
|
627
652
|
for source_resolver in resolvers:
|
|
628
653
|
selected_sources = source_resolver.scope.selected_sources
|
|
@@ -43,22 +43,26 @@ class Resolver:
|
|
|
43
43
|
"""
|
|
44
44
|
column_name = column if isinstance(column, str) else column.name
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
join_context = (
|
|
47
|
+
self._get_column_join_context(column) if isinstance(column, exp.Column) else None
|
|
48
|
+
)
|
|
49
|
+
is_semi_or_anti_join = join_context is not None and join_context.is_semi_or_anti_join
|
|
50
|
+
|
|
51
|
+
# A SEMI/ANTI join's own table is excluded from the global lookup below, so skip it here
|
|
52
|
+
table_name = (
|
|
53
|
+
None if is_semi_or_anti_join else self._get_table_name_from_sources(column_name)
|
|
54
|
+
)
|
|
47
55
|
|
|
48
|
-
if not table_name and
|
|
56
|
+
if not table_name and join_context is not None:
|
|
49
57
|
# Fall-back case: If we couldn't find the `table_name` from ALL of the sources,
|
|
50
58
|
# attempt to disambiguate the column based on other characteristics e.g if this column is in a join condition,
|
|
51
59
|
# we may be able to disambiguate based on the source order.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
column_name, self._get_available_source_columns(join_context)
|
|
59
|
-
)
|
|
60
|
-
except OptimizeError:
|
|
61
|
-
pass
|
|
60
|
+
try:
|
|
61
|
+
table_name = self._get_table_name_from_sources(
|
|
62
|
+
column_name, self._get_available_source_columns(join_context)
|
|
63
|
+
)
|
|
64
|
+
except OptimizeError:
|
|
65
|
+
pass
|
|
62
66
|
|
|
63
67
|
if not table_name and self._infer_schema:
|
|
64
68
|
sources_without_schema = tuple(
|
|
@@ -707,7 +707,7 @@ class Simplifier:
|
|
|
707
707
|
joins.append(node)
|
|
708
708
|
|
|
709
709
|
for where in wheres:
|
|
710
|
-
if always_true(where.this):
|
|
710
|
+
if always_true(where.this) and not isinstance(where.parent, exp.Filter):
|
|
711
711
|
where.pop()
|
|
712
712
|
for join in joins:
|
|
713
713
|
# Only an inner join can become a CROSS JOIN: a cross join is empty as soon as either
|
|
@@ -841,7 +841,10 @@ class Simplifier:
|
|
|
841
841
|
if complement_subquery_predicate:
|
|
842
842
|
right = complement_subquery_predicate(this=right.this)
|
|
843
843
|
|
|
844
|
-
return
|
|
844
|
+
return exp.paren(
|
|
845
|
+
self.COMPLEMENT_COMPARISONS[this.__class__](this=this.this, expression=right),
|
|
846
|
+
copy=False,
|
|
847
|
+
)
|
|
845
848
|
if isinstance(this, exp.Paren):
|
|
846
849
|
condition = this.unnest()
|
|
847
850
|
if isinstance(condition, exp.And):
|
|
@@ -1633,7 +1633,9 @@ class Parser:
|
|
|
1633
1633
|
SHOW_PARSERS: t.ClassVar[dict[str, t.Callable]] = {}
|
|
1634
1634
|
|
|
1635
1635
|
TYPE_LITERAL_PARSERS: t.ClassVar = {
|
|
1636
|
-
exp.DType.JSON: lambda self, this, _: self.expression(
|
|
1636
|
+
exp.DType.JSON: lambda self, this, _: self.expression(
|
|
1637
|
+
exp.ParseJSON(this=this, is_literal=True)
|
|
1638
|
+
),
|
|
1637
1639
|
}
|
|
1638
1640
|
|
|
1639
1641
|
TYPE_CONVERTERS: t.ClassVar[dict[exp.DType, t.Callable[[exp.DataType], exp.DataType]]] = {}
|
|
@@ -3693,7 +3695,7 @@ class Parser:
|
|
|
3693
3695
|
settings=self._match_text_seq("SETTINGS") and self._parse_settings_property(),
|
|
3694
3696
|
default=self._match_text_seq("DEFAULT", "VALUES"),
|
|
3695
3697
|
expression=set_values
|
|
3696
|
-
or self._parse_derived_table_values()
|
|
3698
|
+
or self._parse_derived_table_values(allow_value_synonym=True)
|
|
3697
3699
|
or self._parse_ddl_select(),
|
|
3698
3700
|
conflict=self._parse_on_conflict(),
|
|
3699
3701
|
returning=returning or self._parse_returning(),
|
|
@@ -3738,7 +3740,7 @@ class Parser:
|
|
|
3738
3740
|
action = self._parse_var_from_options(self.CONFLICT_ACTIONS)
|
|
3739
3741
|
if self._prev.token_type == TokenType.UPDATE:
|
|
3740
3742
|
self._match(TokenType.SET)
|
|
3741
|
-
expressions = self._parse_csv(self.
|
|
3743
|
+
expressions = self._parse_csv(self._parse_update_assignment)
|
|
3742
3744
|
else:
|
|
3743
3745
|
expressions = None
|
|
3744
3746
|
|
|
@@ -3867,6 +3869,15 @@ class Parser:
|
|
|
3867
3869
|
)
|
|
3868
3870
|
)
|
|
3869
3871
|
|
|
3872
|
+
def _parse_update_assignment(self) -> exp.Expr | None:
|
|
3873
|
+
this = self._parse_comparison()
|
|
3874
|
+
if self._match(TokenType.EQ):
|
|
3875
|
+
comments = self._prev_comments
|
|
3876
|
+
this = self.expression(
|
|
3877
|
+
exp.EQ(this=this, expression=self._parse_disjunction()), comments=comments
|
|
3878
|
+
)
|
|
3879
|
+
return this
|
|
3880
|
+
|
|
3870
3881
|
def _parse_update(self) -> exp.Update:
|
|
3871
3882
|
hint = self._parse_hint()
|
|
3872
3883
|
kwargs: dict[str, object] = {
|
|
@@ -3875,7 +3886,7 @@ class Parser:
|
|
|
3875
3886
|
}
|
|
3876
3887
|
while self._curr:
|
|
3877
3888
|
if self._match(TokenType.SET):
|
|
3878
|
-
kwargs["expressions"] = self._parse_csv(self.
|
|
3889
|
+
kwargs["expressions"] = self._parse_csv(self._parse_update_assignment)
|
|
3879
3890
|
elif self._match(TokenType.RETURNING, advance=False):
|
|
3880
3891
|
kwargs["returning"] = self._parse_returning()
|
|
3881
3892
|
elif self._match(TokenType.FROM, advance=False):
|
|
@@ -5259,11 +5270,14 @@ class Parser:
|
|
|
5259
5270
|
|
|
5260
5271
|
return self.expression(exp.Unnest(expressions=expressions, alias=alias, offset=offset))
|
|
5261
5272
|
|
|
5262
|
-
def _parse_derived_table_values(self) -> exp.Values | None:
|
|
5273
|
+
def _parse_derived_table_values(self, allow_value_synonym: bool = False) -> exp.Values | None:
|
|
5263
5274
|
is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES)
|
|
5264
5275
|
if not is_derived and not (
|
|
5265
5276
|
# ClickHouse's `FORMAT Values` is equivalent to `VALUES`
|
|
5266
|
-
self._match_text_seq("VALUES")
|
|
5277
|
+
self._match_text_seq("VALUES")
|
|
5278
|
+
or self._match_text_seq("FORMAT", "VALUES")
|
|
5279
|
+
# MySQL accepts VALUE as a synonym for VALUES in INSERT statements.
|
|
5280
|
+
or (allow_value_synonym and self._match_text_seq("VALUE"))
|
|
5267
5281
|
):
|
|
5268
5282
|
return None
|
|
5269
5283
|
|
|
@@ -5766,7 +5780,7 @@ class Parser:
|
|
|
5766
5780
|
|
|
5767
5781
|
def _parse_limit_options(self) -> exp.LimitOptions | None:
|
|
5768
5782
|
percent = self._match_set((TokenType.PERCENT, TokenType.MOD))
|
|
5769
|
-
rows = self.
|
|
5783
|
+
rows = self._match_texts(("ROW", "ROWS"))
|
|
5770
5784
|
self._match_text_seq("ONLY")
|
|
5771
5785
|
with_ties = self._match_text_seq("WITH", "TIES")
|
|
5772
5786
|
|
|
@@ -5830,7 +5844,11 @@ class Parser:
|
|
|
5830
5844
|
else "FIRST"
|
|
5831
5845
|
)
|
|
5832
5846
|
|
|
5833
|
-
count =
|
|
5847
|
+
count = (
|
|
5848
|
+
None
|
|
5849
|
+
if self._match_texts(("ROW", "ROWS"), advance=False)
|
|
5850
|
+
else self._parse_field(tokens=self.FETCH_TOKENS)
|
|
5851
|
+
)
|
|
5834
5852
|
|
|
5835
5853
|
return self.expression(
|
|
5836
5854
|
exp.Fetch(
|
|
@@ -5845,7 +5863,7 @@ class Parser:
|
|
|
5845
5863
|
return this
|
|
5846
5864
|
|
|
5847
5865
|
count = self._parse_term()
|
|
5848
|
-
self.
|
|
5866
|
+
self._match_texts(("ROW", "ROWS"))
|
|
5849
5867
|
|
|
5850
5868
|
return self.expression(
|
|
5851
5869
|
exp.Offset(this=this, expression=count, expressions=self._parse_limit_by())
|
|
@@ -7782,10 +7800,11 @@ class Parser:
|
|
|
7782
7800
|
and self._next.text.upper() in self.PROCEDURE_OPTIONS
|
|
7783
7801
|
)
|
|
7784
7802
|
|
|
7803
|
+
index = self._index
|
|
7785
7804
|
if not procedure_option_follows and self._match_texts(self.CONSTRAINT_PARSERS):
|
|
7786
7805
|
constraint = self.CONSTRAINT_PARSERS[self._prev.text.upper()](self)
|
|
7787
7806
|
if not constraint:
|
|
7788
|
-
self._retreat(
|
|
7807
|
+
self._retreat(index)
|
|
7789
7808
|
return None
|
|
7790
7809
|
|
|
7791
7810
|
return self.expression(exp.ColumnConstraint(this=this, kind=constraint))
|
|
@@ -9577,7 +9596,7 @@ class Parser:
|
|
|
9577
9596
|
then = self.expression(
|
|
9578
9597
|
exp.Update(
|
|
9579
9598
|
expressions=self._match(TokenType.SET)
|
|
9580
|
-
and self._parse_csv(self.
|
|
9599
|
+
and self._parse_csv(self._parse_update_assignment),
|
|
9581
9600
|
where=self._parse_where(),
|
|
9582
9601
|
)
|
|
9583
9602
|
)
|