sqlglot 27.8.0__py3-none-any.whl → 27.10.0__py3-none-any.whl
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.
- sqlglot/_version.py +2 -2
- sqlglot/dialects/__init__.py +1 -1
- sqlglot/dialects/bigquery.py +127 -12
- sqlglot/dialects/clickhouse.py +11 -0
- sqlglot/dialects/dialect.py +2 -0
- sqlglot/dialects/doris.py +77 -9
- sqlglot/dialects/dremio.py +75 -15
- sqlglot/dialects/duckdb.py +13 -3
- sqlglot/dialects/exasol.py +23 -0
- sqlglot/dialects/mysql.py +0 -33
- sqlglot/dialects/postgres.py +0 -1
- sqlglot/dialects/redshift.py +1 -0
- sqlglot/dialects/singlestore.py +185 -19
- sqlglot/dialects/tsql.py +2 -0
- sqlglot/expressions.py +123 -7
- sqlglot/generator.py +123 -29
- sqlglot/optimizer/qualify_columns.py +1 -1
- sqlglot/optimizer/scope.py +1 -0
- sqlglot/parser.py +83 -19
- sqlglot/tokens.py +2 -0
- {sqlglot-27.8.0.dist-info → sqlglot-27.10.0.dist-info}/METADATA +41 -1
- {sqlglot-27.8.0.dist-info → sqlglot-27.10.0.dist-info}/RECORD +25 -25
- {sqlglot-27.8.0.dist-info → sqlglot-27.10.0.dist-info}/WHEEL +0 -0
- {sqlglot-27.8.0.dist-info → sqlglot-27.10.0.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-27.8.0.dist-info → sqlglot-27.10.0.dist-info}/top_level.txt +0 -0
sqlglot/expressions.py
CHANGED
|
@@ -2468,6 +2468,10 @@ class Grant(Expression):
|
|
|
2468
2468
|
}
|
|
2469
2469
|
|
|
2470
2470
|
|
|
2471
|
+
class Revoke(Expression):
|
|
2472
|
+
arg_types = {**Grant.arg_types, "cascade": False}
|
|
2473
|
+
|
|
2474
|
+
|
|
2471
2475
|
class Group(Expression):
|
|
2472
2476
|
arg_types = {
|
|
2473
2477
|
"expressions": False,
|
|
@@ -2790,6 +2794,11 @@ class BackupProperty(Property):
|
|
|
2790
2794
|
arg_types = {"this": True}
|
|
2791
2795
|
|
|
2792
2796
|
|
|
2797
|
+
# https://doris.apache.org/docs/sql-manual/sql-statements/table-and-view/async-materialized-view/CREATE-ASYNC-MATERIALIZED-VIEW/
|
|
2798
|
+
class BuildProperty(Property):
|
|
2799
|
+
arg_types = {"this": True}
|
|
2800
|
+
|
|
2801
|
+
|
|
2793
2802
|
class BlockCompressionProperty(Property):
|
|
2794
2803
|
arg_types = {
|
|
2795
2804
|
"autotemp": False,
|
|
@@ -3031,6 +3040,27 @@ class PartitionByRangePropertyDynamic(Expression):
|
|
|
3031
3040
|
arg_types = {"this": False, "start": True, "end": True, "every": True}
|
|
3032
3041
|
|
|
3033
3042
|
|
|
3043
|
+
# https://doris.apache.org/docs/table-design/data-partitioning/manual-partitioning
|
|
3044
|
+
class PartitionByListProperty(Property):
|
|
3045
|
+
arg_types = {"partition_expressions": True, "create_expressions": True}
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
# https://doris.apache.org/docs/table-design/data-partitioning/manual-partitioning
|
|
3049
|
+
class PartitionList(Expression):
|
|
3050
|
+
arg_types = {"this": True, "expressions": True}
|
|
3051
|
+
|
|
3052
|
+
|
|
3053
|
+
# https://doris.apache.org/docs/sql-manual/sql-statements/table-and-view/async-materialized-view/CREATE-ASYNC-MATERIALIZED-VIEW
|
|
3054
|
+
class RefreshTriggerProperty(Property):
|
|
3055
|
+
arg_types = {
|
|
3056
|
+
"method": True,
|
|
3057
|
+
"kind": False,
|
|
3058
|
+
"every": False,
|
|
3059
|
+
"unit": False,
|
|
3060
|
+
"starts": False,
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
|
|
3034
3064
|
# https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
|
|
3035
3065
|
class UniqueKeyProperty(Property):
|
|
3036
3066
|
arg_types = {"expressions": True}
|
|
@@ -4304,7 +4334,14 @@ class Select(Query):
|
|
|
4304
4334
|
|
|
4305
4335
|
@property
|
|
4306
4336
|
def named_selects(self) -> t.List[str]:
|
|
4307
|
-
|
|
4337
|
+
selects = []
|
|
4338
|
+
|
|
4339
|
+
for e in self.expressions:
|
|
4340
|
+
if e.alias_or_name:
|
|
4341
|
+
selects.append(e.output_name)
|
|
4342
|
+
elif isinstance(e, Aliases):
|
|
4343
|
+
selects.extend([a.name for a in e.aliases])
|
|
4344
|
+
return selects
|
|
4308
4345
|
|
|
4309
4346
|
@property
|
|
4310
4347
|
def is_star(self) -> bool:
|
|
@@ -4876,6 +4913,7 @@ class Alter(Expression):
|
|
|
4876
4913
|
"options": False,
|
|
4877
4914
|
"cluster": False,
|
|
4878
4915
|
"not_valid": False,
|
|
4916
|
+
"check": False,
|
|
4879
4917
|
}
|
|
4880
4918
|
|
|
4881
4919
|
@property
|
|
@@ -5429,6 +5467,11 @@ class ByteLength(Func):
|
|
|
5429
5467
|
pass
|
|
5430
5468
|
|
|
5431
5469
|
|
|
5470
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions#bool_for_json
|
|
5471
|
+
class JSONBool(Func):
|
|
5472
|
+
pass
|
|
5473
|
+
|
|
5474
|
+
|
|
5432
5475
|
class ArrayRemove(Func):
|
|
5433
5476
|
arg_types = {"this": True, "expression": True}
|
|
5434
5477
|
|
|
@@ -5455,10 +5498,28 @@ class ApproxTopK(AggFunc):
|
|
|
5455
5498
|
arg_types = {"this": True, "expression": False, "counters": False}
|
|
5456
5499
|
|
|
5457
5500
|
|
|
5501
|
+
class ApproxTopSum(AggFunc):
|
|
5502
|
+
arg_types = {"this": True, "expression": True, "count": True}
|
|
5503
|
+
|
|
5504
|
+
|
|
5505
|
+
class ApproxQuantiles(AggFunc):
|
|
5506
|
+
arg_types = {"this": True, "expression": False}
|
|
5507
|
+
|
|
5508
|
+
|
|
5509
|
+
class FarmFingerprint(Func):
|
|
5510
|
+
arg_types = {"expressions": True}
|
|
5511
|
+
is_var_len_args = True
|
|
5512
|
+
_sql_names = ["FARM_FINGERPRINT", "FARMFINGERPRINT64"]
|
|
5513
|
+
|
|
5514
|
+
|
|
5458
5515
|
class Flatten(Func):
|
|
5459
5516
|
pass
|
|
5460
5517
|
|
|
5461
5518
|
|
|
5519
|
+
class Float64(Func):
|
|
5520
|
+
arg_types = {"this": True, "expression": False}
|
|
5521
|
+
|
|
5522
|
+
|
|
5462
5523
|
# https://spark.apache.org/docs/latest/api/sql/index.html#transform
|
|
5463
5524
|
class Transform(Func):
|
|
5464
5525
|
arg_types = {"this": True, "expression": True}
|
|
@@ -5548,6 +5609,10 @@ class ToChar(Func):
|
|
|
5548
5609
|
}
|
|
5549
5610
|
|
|
5550
5611
|
|
|
5612
|
+
class ToCodePoints(Func):
|
|
5613
|
+
pass
|
|
5614
|
+
|
|
5615
|
+
|
|
5551
5616
|
# https://docs.snowflake.com/en/sql-reference/functions/to_decimal
|
|
5552
5617
|
# https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/TO_NUMBER.html
|
|
5553
5618
|
class ToNumber(Func):
|
|
@@ -5568,6 +5633,10 @@ class ToDouble(Func):
|
|
|
5568
5633
|
}
|
|
5569
5634
|
|
|
5570
5635
|
|
|
5636
|
+
class CodePointsToBytes(Func):
|
|
5637
|
+
pass
|
|
5638
|
+
|
|
5639
|
+
|
|
5571
5640
|
class Columns(Func):
|
|
5572
5641
|
arg_types = {"this": True, "unpack": False}
|
|
5573
5642
|
|
|
@@ -5877,8 +5946,9 @@ class ConcatWs(Concat):
|
|
|
5877
5946
|
_sql_names = ["CONCAT_WS"]
|
|
5878
5947
|
|
|
5879
5948
|
|
|
5949
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/string_functions#contains_substr
|
|
5880
5950
|
class Contains(Func):
|
|
5881
|
-
arg_types = {"this": True, "expression": True}
|
|
5951
|
+
arg_types = {"this": True, "expression": True, "json_scope": False}
|
|
5882
5952
|
|
|
5883
5953
|
|
|
5884
5954
|
# https://docs.oracle.com/cd/B13789_01/server.101/b10759/operators004.htm#i1035022
|
|
@@ -5933,7 +6003,7 @@ class DateAdd(Func, IntervalOp):
|
|
|
5933
6003
|
|
|
5934
6004
|
|
|
5935
6005
|
class DateBin(Func, IntervalOp):
|
|
5936
|
-
arg_types = {"this": True, "expression": True, "unit": False, "zone": False}
|
|
6006
|
+
arg_types = {"this": True, "expression": True, "unit": False, "zone": False, "origin": False}
|
|
5937
6007
|
|
|
5938
6008
|
|
|
5939
6009
|
class DateSub(Func, IntervalOp):
|
|
@@ -6193,12 +6263,16 @@ class Floor(Func):
|
|
|
6193
6263
|
arg_types = {"this": True, "decimals": False, "to": False}
|
|
6194
6264
|
|
|
6195
6265
|
|
|
6266
|
+
class FromBase32(Func):
|
|
6267
|
+
pass
|
|
6268
|
+
|
|
6269
|
+
|
|
6196
6270
|
class FromBase64(Func):
|
|
6197
6271
|
pass
|
|
6198
6272
|
|
|
6199
6273
|
|
|
6200
|
-
class
|
|
6201
|
-
|
|
6274
|
+
class ToBase32(Func):
|
|
6275
|
+
pass
|
|
6202
6276
|
|
|
6203
6277
|
|
|
6204
6278
|
class ToBase64(Func):
|
|
@@ -6553,9 +6627,18 @@ class JSONFormat(Func):
|
|
|
6553
6627
|
|
|
6554
6628
|
# https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_member-of
|
|
6555
6629
|
class JSONArrayContains(Binary, Predicate, Func):
|
|
6630
|
+
arg_types = {"this": True, "expression": True, "json_type": False}
|
|
6556
6631
|
_sql_names = ["JSON_ARRAY_CONTAINS"]
|
|
6557
6632
|
|
|
6558
6633
|
|
|
6634
|
+
class ParseBignumeric(Func):
|
|
6635
|
+
pass
|
|
6636
|
+
|
|
6637
|
+
|
|
6638
|
+
class ParseNumeric(Func):
|
|
6639
|
+
pass
|
|
6640
|
+
|
|
6641
|
+
|
|
6559
6642
|
class ParseJSON(Func):
|
|
6560
6643
|
# BigQuery, Snowflake have PARSE_JSON, Presto has JSON_PARSE
|
|
6561
6644
|
# Snowflake also has TRY_PARSE_JSON, which is represented using `safe`
|
|
@@ -6714,7 +6797,7 @@ class Nvl2(Func):
|
|
|
6714
6797
|
|
|
6715
6798
|
|
|
6716
6799
|
class Normalize(Func):
|
|
6717
|
-
arg_types = {"this": True, "form": False}
|
|
6800
|
+
arg_types = {"this": True, "form": False, "is_casefold": False}
|
|
6718
6801
|
|
|
6719
6802
|
|
|
6720
6803
|
class Overlay(Func):
|
|
@@ -6726,6 +6809,29 @@ class Predict(Func):
|
|
|
6726
6809
|
arg_types = {"this": True, "expression": True, "params_struct": False}
|
|
6727
6810
|
|
|
6728
6811
|
|
|
6812
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-feature-time
|
|
6813
|
+
class FeaturesAtTime(Func):
|
|
6814
|
+
arg_types = {"this": True, "time": False, "num_rows": False, "ignore_feature_nulls": False}
|
|
6815
|
+
|
|
6816
|
+
|
|
6817
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-generate-embedding
|
|
6818
|
+
class GenerateEmbedding(Func):
|
|
6819
|
+
arg_types = {"this": True, "expression": True, "params_struct": False}
|
|
6820
|
+
|
|
6821
|
+
|
|
6822
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/search_functions#vector_search
|
|
6823
|
+
class VectorSearch(Func):
|
|
6824
|
+
arg_types = {
|
|
6825
|
+
"this": True,
|
|
6826
|
+
"column_to_search": True,
|
|
6827
|
+
"query_table": True,
|
|
6828
|
+
"query_column_to_search": False,
|
|
6829
|
+
"top_k": False,
|
|
6830
|
+
"distance_type": False,
|
|
6831
|
+
"options": False,
|
|
6832
|
+
}
|
|
6833
|
+
|
|
6834
|
+
|
|
6729
6835
|
class Pow(Binary, Func):
|
|
6730
6836
|
_sql_names = ["POWER", "POW"]
|
|
6731
6837
|
|
|
@@ -6743,7 +6849,13 @@ class Quantile(AggFunc):
|
|
|
6743
6849
|
|
|
6744
6850
|
|
|
6745
6851
|
class ApproxQuantile(Quantile):
|
|
6746
|
-
arg_types = {
|
|
6852
|
+
arg_types = {
|
|
6853
|
+
"this": True,
|
|
6854
|
+
"quantile": True,
|
|
6855
|
+
"accuracy": False,
|
|
6856
|
+
"weight": False,
|
|
6857
|
+
"error_tolerance": False,
|
|
6858
|
+
}
|
|
6747
6859
|
|
|
6748
6860
|
|
|
6749
6861
|
class Quarter(Func):
|
|
@@ -6845,6 +6957,10 @@ class SafeDivide(Func):
|
|
|
6845
6957
|
arg_types = {"this": True, "expression": True}
|
|
6846
6958
|
|
|
6847
6959
|
|
|
6960
|
+
class SafeConvertBytesToString(Func):
|
|
6961
|
+
pass
|
|
6962
|
+
|
|
6963
|
+
|
|
6848
6964
|
class SHA(Func):
|
|
6849
6965
|
_sql_names = ["SHA", "SHA1"]
|
|
6850
6966
|
|
sqlglot/generator.py
CHANGED
|
@@ -1137,14 +1137,14 @@ class Generator(metaclass=_Generator):
|
|
|
1137
1137
|
if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
|
|
1138
1138
|
exp.Properties.Location.POST_WITH
|
|
1139
1139
|
):
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
]
|
|
1146
|
-
)
|
|
1140
|
+
props_ast = exp.Properties(
|
|
1141
|
+
expressions=[
|
|
1142
|
+
*properties_locs[exp.Properties.Location.POST_SCHEMA],
|
|
1143
|
+
*properties_locs[exp.Properties.Location.POST_WITH],
|
|
1144
|
+
]
|
|
1147
1145
|
)
|
|
1146
|
+
props_ast.parent = expression
|
|
1147
|
+
properties_sql = self.sql(props_ast)
|
|
1148
1148
|
|
|
1149
1149
|
if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
|
|
1150
1150
|
properties_sql = self.sep() + properties_sql
|
|
@@ -1670,8 +1670,14 @@ class Generator(metaclass=_Generator):
|
|
|
1670
1670
|
elif p_loc == exp.Properties.Location.POST_SCHEMA:
|
|
1671
1671
|
root_properties.append(p)
|
|
1672
1672
|
|
|
1673
|
-
|
|
1674
|
-
|
|
1673
|
+
root_props_ast = exp.Properties(expressions=root_properties)
|
|
1674
|
+
root_props_ast.parent = expression.parent
|
|
1675
|
+
|
|
1676
|
+
with_props_ast = exp.Properties(expressions=with_properties)
|
|
1677
|
+
with_props_ast.parent = expression.parent
|
|
1678
|
+
|
|
1679
|
+
root_props = self.root_properties(root_props_ast)
|
|
1680
|
+
with_props = self.with_properties(with_props_ast)
|
|
1675
1681
|
|
|
1676
1682
|
if root_props and with_props and not self.pretty:
|
|
1677
1683
|
with_props = " " + with_props
|
|
@@ -2992,7 +2998,19 @@ class Generator(metaclass=_Generator):
|
|
|
2992
2998
|
args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
|
|
2993
2999
|
|
|
2994
3000
|
if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
|
|
2995
|
-
|
|
3001
|
+
|
|
3002
|
+
def _wrap_with_coalesce(e: exp.Expression) -> exp.Expression:
|
|
3003
|
+
if not e.type:
|
|
3004
|
+
from sqlglot.optimizer.annotate_types import annotate_types
|
|
3005
|
+
|
|
3006
|
+
e = annotate_types(e, dialect=self.dialect)
|
|
3007
|
+
|
|
3008
|
+
if e.is_string or e.is_type(exp.DataType.Type.ARRAY):
|
|
3009
|
+
return e
|
|
3010
|
+
|
|
3011
|
+
return exp.func("coalesce", e, exp.Literal.string(""))
|
|
3012
|
+
|
|
3013
|
+
args = [_wrap_with_coalesce(e) for e in args]
|
|
2996
3014
|
|
|
2997
3015
|
return args
|
|
2998
3016
|
|
|
@@ -3543,8 +3561,9 @@ class Generator(metaclass=_Generator):
|
|
|
3543
3561
|
options = f", {options}" if options else ""
|
|
3544
3562
|
kind = self.sql(expression, "kind")
|
|
3545
3563
|
not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
|
|
3564
|
+
check = " WITH CHECK" if expression.args.get("check") else ""
|
|
3546
3565
|
|
|
3547
|
-
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{self.sep()}{actions_sql}{not_valid}{options}"
|
|
3566
|
+
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
|
|
3548
3567
|
|
|
3549
3568
|
def add_column_sql(self, expression: exp.Expression) -> str:
|
|
3550
3569
|
sql = self.sql(expression)
|
|
@@ -4029,8 +4048,10 @@ class Generator(metaclass=_Generator):
|
|
|
4029
4048
|
return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
|
|
4030
4049
|
|
|
4031
4050
|
# https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
|
|
4032
|
-
def uniquekeyproperty_sql(
|
|
4033
|
-
|
|
4051
|
+
def uniquekeyproperty_sql(
|
|
4052
|
+
self, expression: exp.UniqueKeyProperty, prefix: str = "UNIQUE KEY"
|
|
4053
|
+
) -> str:
|
|
4054
|
+
return f"{prefix} ({self.expressions(expression, flat=True)})"
|
|
4034
4055
|
|
|
4035
4056
|
# https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
|
|
4036
4057
|
def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
|
|
@@ -4162,6 +4183,47 @@ class Generator(metaclass=_Generator):
|
|
|
4162
4183
|
parameters = self.sql(expression, "params_struct")
|
|
4163
4184
|
return self.func("PREDICT", model, table, parameters or None)
|
|
4164
4185
|
|
|
4186
|
+
def generateembedding_sql(self, expression: exp.GenerateEmbedding) -> str:
|
|
4187
|
+
model = self.sql(expression, "this")
|
|
4188
|
+
model = f"MODEL {model}"
|
|
4189
|
+
table = self.sql(expression, "expression")
|
|
4190
|
+
table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
|
|
4191
|
+
parameters = self.sql(expression, "params_struct")
|
|
4192
|
+
return self.func("GENERATE_EMBEDDING", model, table, parameters or None)
|
|
4193
|
+
|
|
4194
|
+
def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
|
|
4195
|
+
this_sql = self.sql(expression, "this")
|
|
4196
|
+
if isinstance(expression.this, exp.Table):
|
|
4197
|
+
this_sql = f"TABLE {this_sql}"
|
|
4198
|
+
|
|
4199
|
+
return self.func(
|
|
4200
|
+
"FEATURES_AT_TIME",
|
|
4201
|
+
this_sql,
|
|
4202
|
+
expression.args.get("time"),
|
|
4203
|
+
expression.args.get("num_rows"),
|
|
4204
|
+
expression.args.get("ignore_feature_nulls"),
|
|
4205
|
+
)
|
|
4206
|
+
|
|
4207
|
+
def vectorsearch_sql(self, expression: exp.VectorSearch) -> str:
|
|
4208
|
+
this_sql = self.sql(expression, "this")
|
|
4209
|
+
if isinstance(expression.this, exp.Table):
|
|
4210
|
+
this_sql = f"TABLE {this_sql}"
|
|
4211
|
+
|
|
4212
|
+
query_table = self.sql(expression, "query_table")
|
|
4213
|
+
if isinstance(expression.args["query_table"], exp.Table):
|
|
4214
|
+
query_table = f"TABLE {query_table}"
|
|
4215
|
+
|
|
4216
|
+
return self.func(
|
|
4217
|
+
"VECTOR_SEARCH",
|
|
4218
|
+
this_sql,
|
|
4219
|
+
expression.args.get("column_to_search"),
|
|
4220
|
+
query_table,
|
|
4221
|
+
expression.args.get("query_column_to_search"),
|
|
4222
|
+
expression.args.get("top_k"),
|
|
4223
|
+
expression.args.get("distance_type"),
|
|
4224
|
+
expression.args.get("options"),
|
|
4225
|
+
)
|
|
4226
|
+
|
|
4165
4227
|
def forin_sql(self, expression: exp.ForIn) -> str:
|
|
4166
4228
|
this = self.sql(expression, "this")
|
|
4167
4229
|
expression_sql = self.sql(expression, "expression")
|
|
@@ -4741,7 +4803,14 @@ class Generator(metaclass=_Generator):
|
|
|
4741
4803
|
|
|
4742
4804
|
return f"{this} APPLY({expr})"
|
|
4743
4805
|
|
|
4744
|
-
def
|
|
4806
|
+
def _grant_or_revoke_sql(
|
|
4807
|
+
self,
|
|
4808
|
+
expression: exp.Grant | exp.Revoke,
|
|
4809
|
+
keyword: str,
|
|
4810
|
+
preposition: str,
|
|
4811
|
+
grant_option_prefix: str = "",
|
|
4812
|
+
grant_option_suffix: str = "",
|
|
4813
|
+
) -> str:
|
|
4745
4814
|
privileges_sql = self.expressions(expression, key="privileges", flat=True)
|
|
4746
4815
|
|
|
4747
4816
|
kind = self.sql(expression, "kind")
|
|
@@ -4752,9 +4821,30 @@ class Generator(metaclass=_Generator):
|
|
|
4752
4821
|
|
|
4753
4822
|
principals = self.expressions(expression, key="principals", flat=True)
|
|
4754
4823
|
|
|
4755
|
-
|
|
4824
|
+
if not expression.args.get("grant_option"):
|
|
4825
|
+
grant_option_prefix = grant_option_suffix = ""
|
|
4826
|
+
|
|
4827
|
+
# cascade for revoke only
|
|
4828
|
+
cascade = self.sql(expression, "cascade")
|
|
4829
|
+
cascade = f" {cascade}" if cascade else ""
|
|
4830
|
+
|
|
4831
|
+
return f"{keyword} {grant_option_prefix}{privileges_sql} ON{kind}{securable} {preposition} {principals}{grant_option_suffix}{cascade}"
|
|
4756
4832
|
|
|
4757
|
-
|
|
4833
|
+
def grant_sql(self, expression: exp.Grant) -> str:
|
|
4834
|
+
return self._grant_or_revoke_sql(
|
|
4835
|
+
expression,
|
|
4836
|
+
keyword="GRANT",
|
|
4837
|
+
preposition="TO",
|
|
4838
|
+
grant_option_suffix=" WITH GRANT OPTION",
|
|
4839
|
+
)
|
|
4840
|
+
|
|
4841
|
+
def revoke_sql(self, expression: exp.Revoke) -> str:
|
|
4842
|
+
return self._grant_or_revoke_sql(
|
|
4843
|
+
expression,
|
|
4844
|
+
keyword="REVOKE",
|
|
4845
|
+
preposition="FROM",
|
|
4846
|
+
grant_option_prefix="GRANT OPTION FOR ",
|
|
4847
|
+
)
|
|
4758
4848
|
|
|
4759
4849
|
def grantprivilege_sql(self, expression: exp.GrantPrivilege):
|
|
4760
4850
|
this = self.sql(expression, "this")
|
|
@@ -4869,19 +4959,6 @@ class Generator(metaclass=_Generator):
|
|
|
4869
4959
|
value = f" {value}" if value else ""
|
|
4870
4960
|
return f"{this}{value}"
|
|
4871
4961
|
|
|
4872
|
-
def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
|
|
4873
|
-
this_sql = self.sql(expression, "this")
|
|
4874
|
-
if isinstance(expression.this, exp.Table):
|
|
4875
|
-
this_sql = f"TABLE {this_sql}"
|
|
4876
|
-
|
|
4877
|
-
return self.func(
|
|
4878
|
-
"FEATURES_AT_TIME",
|
|
4879
|
-
this_sql,
|
|
4880
|
-
expression.args.get("time"),
|
|
4881
|
-
expression.args.get("num_rows"),
|
|
4882
|
-
expression.args.get("ignore_feature_nulls"),
|
|
4883
|
-
)
|
|
4884
|
-
|
|
4885
4962
|
def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
|
|
4886
4963
|
return (
|
|
4887
4964
|
f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
|
|
@@ -5151,3 +5228,20 @@ class Generator(metaclass=_Generator):
|
|
|
5151
5228
|
|
|
5152
5229
|
def space_sql(self: Generator, expression: exp.Space) -> str:
|
|
5153
5230
|
return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))
|
|
5231
|
+
|
|
5232
|
+
def buildproperty_sql(self, expression: exp.BuildProperty) -> str:
|
|
5233
|
+
return f"BUILD {self.sql(expression, 'this')}"
|
|
5234
|
+
|
|
5235
|
+
def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
|
|
5236
|
+
method = self.sql(expression, "method")
|
|
5237
|
+
kind = expression.args.get("kind")
|
|
5238
|
+
if not kind:
|
|
5239
|
+
return f"REFRESH {method}"
|
|
5240
|
+
|
|
5241
|
+
every = self.sql(expression, "every")
|
|
5242
|
+
unit = self.sql(expression, "unit")
|
|
5243
|
+
every = f" EVERY {every} {unit}" if every else ""
|
|
5244
|
+
starts = self.sql(expression, "starts")
|
|
5245
|
+
starts = f" STARTS {starts}" if starts else ""
|
|
5246
|
+
|
|
5247
|
+
return f"REFRESH {method} ON {kind}{every}{starts}"
|
|
@@ -853,7 +853,7 @@ def qualify_outputs(scope_or_expression: Scope | exp.Expression) -> None:
|
|
|
853
853
|
if isinstance(selection, exp.Subquery):
|
|
854
854
|
if not selection.output_name:
|
|
855
855
|
selection.set("alias", exp.TableAlias(this=exp.to_identifier(f"_col_{i}")))
|
|
856
|
-
elif not isinstance(selection, exp.Alias) and not selection.is_star:
|
|
856
|
+
elif not isinstance(selection, (exp.Alias, exp.Aliases)) and not selection.is_star:
|
|
857
857
|
selection = alias(
|
|
858
858
|
selection,
|
|
859
859
|
alias=selection.output_name or f"_col_{i}",
|
sqlglot/optimizer/scope.py
CHANGED
sqlglot/parser.py
CHANGED
|
@@ -844,6 +844,7 @@ class Parser(metaclass=_Parser):
|
|
|
844
844
|
TokenType.DESCRIBE: lambda self: self._parse_describe(),
|
|
845
845
|
TokenType.DROP: lambda self: self._parse_drop(),
|
|
846
846
|
TokenType.GRANT: lambda self: self._parse_grant(),
|
|
847
|
+
TokenType.REVOKE: lambda self: self._parse_revoke(),
|
|
847
848
|
TokenType.INSERT: lambda self: self._parse_insert(),
|
|
848
849
|
TokenType.KILL: lambda self: self._parse_kill(),
|
|
849
850
|
TokenType.LOAD: lambda self: self._parse_load(),
|
|
@@ -1244,7 +1245,6 @@ class Parser(metaclass=_Parser):
|
|
|
1244
1245
|
"OPENJSON": lambda self: self._parse_open_json(),
|
|
1245
1246
|
"OVERLAY": lambda self: self._parse_overlay(),
|
|
1246
1247
|
"POSITION": lambda self: self._parse_position(),
|
|
1247
|
-
"PREDICT": lambda self: self._parse_predict(),
|
|
1248
1248
|
"SAFE_CAST": lambda self: self._parse_cast(False, safe=True),
|
|
1249
1249
|
"STRING_AGG": lambda self: self._parse_string_agg(),
|
|
1250
1250
|
"SUBSTRING": lambda self: self._parse_substring(),
|
|
@@ -3533,10 +3533,17 @@ class Parser(metaclass=_Parser):
|
|
|
3533
3533
|
|
|
3534
3534
|
while True:
|
|
3535
3535
|
if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False):
|
|
3536
|
-
|
|
3536
|
+
modifier_token = self._curr
|
|
3537
|
+
parser = self.QUERY_MODIFIER_PARSERS[modifier_token.token_type]
|
|
3537
3538
|
key, expression = parser(self)
|
|
3538
3539
|
|
|
3539
3540
|
if expression:
|
|
3541
|
+
if this.args.get(key):
|
|
3542
|
+
self.raise_error(
|
|
3543
|
+
f"Found multiple '{modifier_token.text.upper()}' clauses",
|
|
3544
|
+
token=modifier_token,
|
|
3545
|
+
)
|
|
3546
|
+
|
|
3540
3547
|
this.set(key, expression)
|
|
3541
3548
|
if key == "limit":
|
|
3542
3549
|
offset = expression.args.pop("offset", None)
|
|
@@ -6914,20 +6921,6 @@ class Parser(metaclass=_Parser):
|
|
|
6914
6921
|
exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2)
|
|
6915
6922
|
)
|
|
6916
6923
|
|
|
6917
|
-
def _parse_predict(self) -> exp.Predict:
|
|
6918
|
-
self._match_text_seq("MODEL")
|
|
6919
|
-
this = self._parse_table()
|
|
6920
|
-
|
|
6921
|
-
self._match(TokenType.COMMA)
|
|
6922
|
-
self._match_text_seq("TABLE")
|
|
6923
|
-
|
|
6924
|
-
return self.expression(
|
|
6925
|
-
exp.Predict,
|
|
6926
|
-
this=this,
|
|
6927
|
-
expression=self._parse_table(),
|
|
6928
|
-
params_struct=self._match(TokenType.COMMA) and self._parse_bitwise(),
|
|
6929
|
-
)
|
|
6930
|
-
|
|
6931
6924
|
def _parse_join_hint(self, func_name: str) -> exp.JoinHint:
|
|
6932
6925
|
args = self._parse_csv(self._parse_table)
|
|
6933
6926
|
return exp.JoinHint(this=func_name.upper(), expressions=args)
|
|
@@ -7597,6 +7590,7 @@ class Parser(metaclass=_Parser):
|
|
|
7597
7590
|
exists = self._parse_exists()
|
|
7598
7591
|
only = self._match_text_seq("ONLY")
|
|
7599
7592
|
this = self._parse_table(schema=True)
|
|
7593
|
+
check = self._match_text_seq("WITH", "CHECK")
|
|
7600
7594
|
cluster = self._parse_on_property() if self._match(TokenType.ON) else None
|
|
7601
7595
|
|
|
7602
7596
|
if self._next:
|
|
@@ -7619,6 +7613,7 @@ class Parser(metaclass=_Parser):
|
|
|
7619
7613
|
options=options,
|
|
7620
7614
|
cluster=cluster,
|
|
7621
7615
|
not_valid=not_valid,
|
|
7616
|
+
check=check,
|
|
7622
7617
|
)
|
|
7623
7618
|
|
|
7624
7619
|
return self._parse_as_command(start)
|
|
@@ -8416,9 +8411,9 @@ class Parser(metaclass=_Parser):
|
|
|
8416
8411
|
|
|
8417
8412
|
return self.expression(exp.GrantPrincipal, this=principal, kind=kind)
|
|
8418
8413
|
|
|
8419
|
-
def
|
|
8420
|
-
|
|
8421
|
-
|
|
8414
|
+
def _parse_grant_revoke_common(
|
|
8415
|
+
self,
|
|
8416
|
+
) -> t.Tuple[t.Optional[t.List], t.Optional[str], t.Optional[exp.Expression]]:
|
|
8422
8417
|
privileges = self._parse_csv(self._parse_grant_privilege)
|
|
8423
8418
|
|
|
8424
8419
|
self._match(TokenType.ON)
|
|
@@ -8428,6 +8423,13 @@ class Parser(metaclass=_Parser):
|
|
|
8428
8423
|
# such as "foo.*", "*.*" which are not easily parseable yet
|
|
8429
8424
|
securable = self._try_parse(self._parse_table_parts)
|
|
8430
8425
|
|
|
8426
|
+
return privileges, kind, securable
|
|
8427
|
+
|
|
8428
|
+
def _parse_grant(self) -> exp.Grant | exp.Command:
|
|
8429
|
+
start = self._prev
|
|
8430
|
+
|
|
8431
|
+
privileges, kind, securable = self._parse_grant_revoke_common()
|
|
8432
|
+
|
|
8431
8433
|
if not securable or not self._match_text_seq("TO"):
|
|
8432
8434
|
return self._parse_as_command(start)
|
|
8433
8435
|
|
|
@@ -8447,6 +8449,35 @@ class Parser(metaclass=_Parser):
|
|
|
8447
8449
|
grant_option=grant_option,
|
|
8448
8450
|
)
|
|
8449
8451
|
|
|
8452
|
+
def _parse_revoke(self) -> exp.Revoke | exp.Command:
|
|
8453
|
+
start = self._prev
|
|
8454
|
+
|
|
8455
|
+
grant_option = self._match_text_seq("GRANT", "OPTION", "FOR")
|
|
8456
|
+
|
|
8457
|
+
privileges, kind, securable = self._parse_grant_revoke_common()
|
|
8458
|
+
|
|
8459
|
+
if not securable or not self._match_text_seq("FROM"):
|
|
8460
|
+
return self._parse_as_command(start)
|
|
8461
|
+
|
|
8462
|
+
principals = self._parse_csv(self._parse_grant_principal)
|
|
8463
|
+
|
|
8464
|
+
cascade = None
|
|
8465
|
+
if self._match_texts(("CASCADE", "RESTRICT")):
|
|
8466
|
+
cascade = self._prev.text.upper()
|
|
8467
|
+
|
|
8468
|
+
if self._curr:
|
|
8469
|
+
return self._parse_as_command(start)
|
|
8470
|
+
|
|
8471
|
+
return self.expression(
|
|
8472
|
+
exp.Revoke,
|
|
8473
|
+
privileges=privileges,
|
|
8474
|
+
kind=kind,
|
|
8475
|
+
securable=securable,
|
|
8476
|
+
principals=principals,
|
|
8477
|
+
grant_option=grant_option,
|
|
8478
|
+
cascade=cascade,
|
|
8479
|
+
)
|
|
8480
|
+
|
|
8450
8481
|
def _parse_overlay(self) -> exp.Overlay:
|
|
8451
8482
|
return self.expression(
|
|
8452
8483
|
exp.Overlay,
|
|
@@ -8722,3 +8753,36 @@ class Parser(metaclass=_Parser):
|
|
|
8722
8753
|
returning=returning,
|
|
8723
8754
|
on_condition=self._parse_on_condition(),
|
|
8724
8755
|
)
|
|
8756
|
+
|
|
8757
|
+
def _parse_group_concat(self) -> t.Optional[exp.Expression]:
|
|
8758
|
+
def concat_exprs(
|
|
8759
|
+
node: t.Optional[exp.Expression], exprs: t.List[exp.Expression]
|
|
8760
|
+
) -> exp.Expression:
|
|
8761
|
+
if isinstance(node, exp.Distinct) and len(node.expressions) > 1:
|
|
8762
|
+
concat_exprs = [
|
|
8763
|
+
self.expression(exp.Concat, expressions=node.expressions, safe=True)
|
|
8764
|
+
]
|
|
8765
|
+
node.set("expressions", concat_exprs)
|
|
8766
|
+
return node
|
|
8767
|
+
if len(exprs) == 1:
|
|
8768
|
+
return exprs[0]
|
|
8769
|
+
return self.expression(exp.Concat, expressions=args, safe=True)
|
|
8770
|
+
|
|
8771
|
+
args = self._parse_csv(self._parse_lambda)
|
|
8772
|
+
|
|
8773
|
+
if args:
|
|
8774
|
+
order = args[-1] if isinstance(args[-1], exp.Order) else None
|
|
8775
|
+
|
|
8776
|
+
if order:
|
|
8777
|
+
# Order By is the last (or only) expression in the list and has consumed the 'expr' before it,
|
|
8778
|
+
# remove 'expr' from exp.Order and add it back to args
|
|
8779
|
+
args[-1] = order.this
|
|
8780
|
+
order.set("this", concat_exprs(order.this, args))
|
|
8781
|
+
|
|
8782
|
+
this = order or concat_exprs(args[0], args)
|
|
8783
|
+
else:
|
|
8784
|
+
this = None
|
|
8785
|
+
|
|
8786
|
+
separator = self._parse_field() if self._match(TokenType.SEPARATOR) else None
|
|
8787
|
+
|
|
8788
|
+
return self.expression(exp.GroupConcat, this=this, separator=separator)
|
sqlglot/tokens.py
CHANGED
|
@@ -376,6 +376,7 @@ class TokenType(AutoName):
|
|
|
376
376
|
RENAME = auto()
|
|
377
377
|
REPLACE = auto()
|
|
378
378
|
RETURNING = auto()
|
|
379
|
+
REVOKE = auto()
|
|
379
380
|
REFERENCES = auto()
|
|
380
381
|
RIGHT = auto()
|
|
381
382
|
RLIKE = auto()
|
|
@@ -972,6 +973,7 @@ class Tokenizer(metaclass=_Tokenizer):
|
|
|
972
973
|
"COMMENT": TokenType.COMMENT,
|
|
973
974
|
"EXPLAIN": TokenType.COMMAND,
|
|
974
975
|
"GRANT": TokenType.GRANT,
|
|
976
|
+
"REVOKE": TokenType.REVOKE,
|
|
975
977
|
"OPTIMIZE": TokenType.COMMAND,
|
|
976
978
|
"PREPARE": TokenType.COMMAND,
|
|
977
979
|
"VACUUM": TokenType.COMMAND,
|