sqlglot 26.30.0__py3-none-any.whl → 26.31.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 -0
- sqlglot/dialects/bigquery.py +6 -4
- sqlglot/dialects/databricks.py +2 -0
- sqlglot/dialects/dialect.py +15 -9
- sqlglot/dialects/exasol.py +46 -0
- sqlglot/dialects/fabric.py +60 -33
- sqlglot/dialects/presto.py +2 -0
- sqlglot/dialects/redshift.py +10 -2
- sqlglot/dialects/snowflake.py +3 -1
- sqlglot/dialects/tsql.py +7 -5
- sqlglot/expressions.py +9 -2
- sqlglot/generator.py +3 -3
- sqlglot/optimizer/scope.py +13 -3
- sqlglot/parser.py +3 -2
- sqlglot/transforms.py +15 -1
- {sqlglot-26.30.0.dist-info → sqlglot-26.31.0.dist-info}/METADATA +2 -2
- {sqlglot-26.30.0.dist-info → sqlglot-26.31.0.dist-info}/RECORD +21 -20
- {sqlglot-26.30.0.dist-info → sqlglot-26.31.0.dist-info}/WHEEL +0 -0
- {sqlglot-26.30.0.dist-info → sqlglot-26.31.0.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-26.30.0.dist-info → sqlglot-26.31.0.dist-info}/top_level.txt +0 -0
sqlglot/_version.py
CHANGED
sqlglot/dialects/__init__.py
CHANGED
sqlglot/dialects/bigquery.py
CHANGED
@@ -543,7 +543,7 @@ class BigQuery(Dialect):
|
|
543
543
|
"DATE_ADD": build_date_delta_with_interval(exp.DateAdd),
|
544
544
|
"DATE_SUB": build_date_delta_with_interval(exp.DateSub),
|
545
545
|
"DATE_TRUNC": lambda args: exp.DateTrunc(
|
546
|
-
unit=
|
546
|
+
unit=seq_get(args, 1),
|
547
547
|
this=seq_get(args, 0),
|
548
548
|
zone=seq_get(args, 2),
|
549
549
|
),
|
@@ -963,9 +963,6 @@ class BigQuery(Dialect):
|
|
963
963
|
exp.DateSub: date_add_interval_sql("DATE", "SUB"),
|
964
964
|
exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"),
|
965
965
|
exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"),
|
966
|
-
exp.DateTrunc: lambda self, e: self.func(
|
967
|
-
"DATE_TRUNC", e.this, e.text("unit"), e.args.get("zone")
|
968
|
-
),
|
969
966
|
exp.FromTimeZone: lambda self, e: self.func(
|
970
967
|
"DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'"
|
971
968
|
),
|
@@ -1195,6 +1192,11 @@ class BigQuery(Dialect):
|
|
1195
1192
|
"within",
|
1196
1193
|
}
|
1197
1194
|
|
1195
|
+
def datetrunc_sql(self, expression: exp.DateTrunc) -> str:
|
1196
|
+
unit = expression.unit
|
1197
|
+
unit_sql = unit.name if unit.is_string else self.sql(unit)
|
1198
|
+
return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone"))
|
1199
|
+
|
1198
1200
|
def mod_sql(self, expression: exp.Mod) -> str:
|
1199
1201
|
this = expression.this
|
1200
1202
|
expr = expression.expression
|
sqlglot/dialects/databricks.py
CHANGED
@@ -9,6 +9,7 @@ from sqlglot.dialects.dialect import (
|
|
9
9
|
build_date_delta,
|
10
10
|
timestamptrunc_sql,
|
11
11
|
build_formatted_time,
|
12
|
+
groupconcat_sql,
|
12
13
|
)
|
13
14
|
from sqlglot.dialects.spark import Spark
|
14
15
|
from sqlglot.tokens import TokenType
|
@@ -87,6 +88,7 @@ class Databricks(Spark):
|
|
87
88
|
e.this,
|
88
89
|
),
|
89
90
|
exp.DatetimeTrunc: timestamptrunc_sql(),
|
91
|
+
exp.GroupConcat: groupconcat_sql,
|
90
92
|
exp.Select: transforms.preprocess(
|
91
93
|
[
|
92
94
|
transforms.eliminate_distinct_on,
|
sqlglot/dialects/dialect.py
CHANGED
@@ -96,6 +96,7 @@ class Dialects(str, Enum):
|
|
96
96
|
TERADATA = "teradata"
|
97
97
|
TRINO = "trino"
|
98
98
|
TSQL = "tsql"
|
99
|
+
EXASOL = "exasol"
|
99
100
|
|
100
101
|
|
101
102
|
class NormalizationStrategy(str, AutoName):
|
@@ -700,6 +701,9 @@ class Dialect(metaclass=_Dialect):
|
|
700
701
|
exp.TimeAdd,
|
701
702
|
exp.TimeSub,
|
702
703
|
},
|
704
|
+
exp.DataType.Type.TIMESTAMPTZ: {
|
705
|
+
exp.CurrentTimestampLTZ,
|
706
|
+
},
|
703
707
|
exp.DataType.Type.TIMESTAMP: {
|
704
708
|
exp.CurrentTimestamp,
|
705
709
|
exp.StrToTime,
|
@@ -1906,21 +1910,23 @@ def groupconcat_sql(
|
|
1906
1910
|
|
1907
1911
|
|
1908
1912
|
def build_timetostr_or_tochar(args: t.List, dialect: Dialect) -> exp.TimeToStr | exp.ToChar:
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
if this:
|
1913
|
+
if len(args) == 2:
|
1914
|
+
this = args[0]
|
1913
1915
|
if not this.type:
|
1914
1916
|
from sqlglot.optimizer.annotate_types import annotate_types
|
1915
1917
|
|
1916
1918
|
annotate_types(this, dialect=dialect)
|
1917
1919
|
|
1918
|
-
|
1919
|
-
|
1920
|
-
if this.is_type(*exp.DataType.TEMPORAL_TYPES) or (
|
1921
|
-
isinstance(format, exp.Literal) and format.name in Snowflake.TIME_MAPPING
|
1922
|
-
):
|
1920
|
+
if this.is_type(*exp.DataType.TEMPORAL_TYPES):
|
1923
1921
|
dialect_name = dialect.__class__.__name__.lower()
|
1924
1922
|
return build_formatted_time(exp.TimeToStr, dialect_name, default=True)(args)
|
1925
1923
|
|
1926
1924
|
return exp.ToChar.from_arg_list(args)
|
1925
|
+
|
1926
|
+
|
1927
|
+
def build_replace_with_optional_replacement(args: t.List) -> exp.Replace:
|
1928
|
+
return exp.Replace(
|
1929
|
+
this=seq_get(args, 0),
|
1930
|
+
expression=seq_get(args, 1),
|
1931
|
+
replacement=seq_get(args, 2) or exp.Literal.string(""),
|
1932
|
+
)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from sqlglot import exp, generator
|
3
|
+
from sqlglot.dialects.dialect import Dialect, rename_func
|
4
|
+
|
5
|
+
|
6
|
+
class Exasol(Dialect):
|
7
|
+
class Generator(generator.Generator):
|
8
|
+
# https://docs.exasol.com/db/latest/sql_references/data_types/datatypedetails.htm#StringDataType
|
9
|
+
STRING_TYPE_MAPPING = {
|
10
|
+
exp.DataType.Type.BLOB: "VARCHAR",
|
11
|
+
exp.DataType.Type.LONGBLOB: "VARCHAR",
|
12
|
+
exp.DataType.Type.LONGTEXT: "VARCHAR",
|
13
|
+
exp.DataType.Type.MEDIUMBLOB: "VARCHAR",
|
14
|
+
exp.DataType.Type.MEDIUMTEXT: "VARCHAR",
|
15
|
+
exp.DataType.Type.TINYBLOB: "VARCHAR",
|
16
|
+
exp.DataType.Type.TINYTEXT: "VARCHAR",
|
17
|
+
exp.DataType.Type.TEXT: "VARCHAR",
|
18
|
+
exp.DataType.Type.VARBINARY: "VARCHAR",
|
19
|
+
}
|
20
|
+
|
21
|
+
# https://docs.exasol.com/db/latest/sql_references/data_types/datatypealiases.htm
|
22
|
+
TYPE_MAPPING = {
|
23
|
+
**generator.Generator.TYPE_MAPPING,
|
24
|
+
**STRING_TYPE_MAPPING,
|
25
|
+
exp.DataType.Type.TINYINT: "SMALLINT",
|
26
|
+
exp.DataType.Type.MEDIUMINT: "INT",
|
27
|
+
exp.DataType.Type.DECIMAL32: "DECIMAL",
|
28
|
+
exp.DataType.Type.DECIMAL64: "DECIMAL",
|
29
|
+
exp.DataType.Type.DECIMAL128: "DECIMAL",
|
30
|
+
exp.DataType.Type.DECIMAL256: "DECIMAL",
|
31
|
+
exp.DataType.Type.DATETIME: "TIMESTAMP",
|
32
|
+
}
|
33
|
+
|
34
|
+
def datatype_sql(self, expression: exp.DataType) -> str:
|
35
|
+
# Exasol supports a fixed default precision of 3 for TIMESTAMP WITH LOCAL TIME ZONE
|
36
|
+
# and does not allow specifying a different custom precision
|
37
|
+
if expression.is_type(exp.DataType.Type.TIMESTAMPLTZ):
|
38
|
+
return "TIMESTAMP WITH LOCAL TIME ZONE"
|
39
|
+
|
40
|
+
return super().datatype_sql(expression)
|
41
|
+
|
42
|
+
TRANSFORMS = {
|
43
|
+
**generator.Generator.TRANSFORMS,
|
44
|
+
# https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/mod.htm
|
45
|
+
exp.Mod: rename_func("MOD"),
|
46
|
+
}
|
sqlglot/dialects/fabric.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from sqlglot import exp
|
4
4
|
from sqlglot.dialects.dialect import NormalizationStrategy
|
5
5
|
from sqlglot.dialects.tsql import TSQL
|
6
|
+
from sqlglot.tokens import TokenType
|
6
7
|
|
7
8
|
|
8
9
|
class Fabric(TSQL):
|
@@ -28,61 +29,87 @@ class Fabric(TSQL):
|
|
28
29
|
# Fabric is case-sensitive unlike T-SQL which is case-insensitive
|
29
30
|
NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_SENSITIVE
|
30
31
|
|
32
|
+
class Tokenizer(TSQL.Tokenizer):
|
33
|
+
# Override T-SQL tokenizer to handle TIMESTAMP differently
|
34
|
+
# In T-SQL, TIMESTAMP is a synonym for ROWVERSION, but in Fabric we want it to be a datetime type
|
35
|
+
# Also add UTINYINT keyword mapping since T-SQL doesn't have it
|
36
|
+
KEYWORDS = {
|
37
|
+
**TSQL.Tokenizer.KEYWORDS,
|
38
|
+
"TIMESTAMP": TokenType.TIMESTAMP,
|
39
|
+
"UTINYINT": TokenType.UTINYINT,
|
40
|
+
}
|
41
|
+
|
31
42
|
class Generator(TSQL.Generator):
|
32
43
|
# Fabric-specific type mappings - override T-SQL types that aren't supported
|
33
44
|
# Reference: https://learn.microsoft.com/en-us/fabric/data-warehouse/data-types
|
34
45
|
TYPE_MAPPING = {
|
35
46
|
**TSQL.Generator.TYPE_MAPPING,
|
36
|
-
|
47
|
+
exp.DataType.Type.DATETIME: "DATETIME2",
|
48
|
+
exp.DataType.Type.DECIMAL: "DECIMAL",
|
49
|
+
exp.DataType.Type.IMAGE: "VARBINARY",
|
50
|
+
exp.DataType.Type.INT: "INT",
|
51
|
+
exp.DataType.Type.JSON: "VARCHAR",
|
37
52
|
exp.DataType.Type.MONEY: "DECIMAL",
|
38
|
-
exp.DataType.Type.SMALLMONEY: "DECIMAL",
|
39
|
-
exp.DataType.Type.DATETIME: "DATETIME2(6)",
|
40
|
-
exp.DataType.Type.SMALLDATETIME: "DATETIME2(6)",
|
41
53
|
exp.DataType.Type.NCHAR: "CHAR",
|
42
54
|
exp.DataType.Type.NVARCHAR: "VARCHAR",
|
43
|
-
exp.DataType.Type.
|
44
|
-
exp.DataType.Type.
|
55
|
+
exp.DataType.Type.ROWVERSION: "ROWVERSION",
|
56
|
+
exp.DataType.Type.SMALLDATETIME: "DATETIME2",
|
57
|
+
exp.DataType.Type.SMALLMONEY: "DECIMAL",
|
58
|
+
exp.DataType.Type.TIMESTAMP: "DATETIME2",
|
59
|
+
exp.DataType.Type.TIMESTAMPNTZ: "DATETIME2",
|
60
|
+
exp.DataType.Type.TIMESTAMPTZ: "DATETIMEOFFSET",
|
45
61
|
exp.DataType.Type.TINYINT: "SMALLINT",
|
46
|
-
exp.DataType.Type.UTINYINT: "SMALLINT",
|
47
|
-
exp.DataType.Type.
|
62
|
+
exp.DataType.Type.UTINYINT: "SMALLINT",
|
63
|
+
exp.DataType.Type.UUID: "VARBINARY(MAX)",
|
48
64
|
exp.DataType.Type.XML: "VARCHAR",
|
49
|
-
exp.DataType.Type.UUID: "VARBINARY(MAX)", # UNIQUEIDENTIFIER has limitations in Fabric
|
50
|
-
# Override T-SQL mappings that use different names in Fabric
|
51
|
-
exp.DataType.Type.DECIMAL: "DECIMAL", # T-SQL uses NUMERIC
|
52
|
-
exp.DataType.Type.DOUBLE: "FLOAT",
|
53
|
-
exp.DataType.Type.INT: "INT", # T-SQL uses INTEGER
|
54
65
|
}
|
55
66
|
|
56
67
|
def datatype_sql(self, expression: exp.DataType) -> str:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
"""
|
63
|
-
if expression.is_type(
|
64
|
-
exp.DataType.Type.TIME,
|
65
|
-
exp.DataType.Type.DATETIME2,
|
66
|
-
exp.DataType.Type.TIMESTAMPTZ, # DATETIMEOFFSET in Fabric
|
68
|
+
# Check if this is a temporal type that needs precision handling. Fabric limits temporal
|
69
|
+
# types to max 6 digits precision. When no precision is specified, we default to 6 digits.
|
70
|
+
if (
|
71
|
+
expression.is_type(*exp.DataType.TEMPORAL_TYPES)
|
72
|
+
and expression.this != exp.DataType.Type.DATE
|
67
73
|
):
|
68
74
|
# Get the current precision (first expression if it exists)
|
69
|
-
|
75
|
+
precision_param = expression.find(exp.DataTypeParam)
|
76
|
+
target_precision = 6
|
70
77
|
|
71
|
-
|
72
|
-
if precision is None:
|
73
|
-
# No precision specified, default to 6
|
74
|
-
target_precision = 6
|
75
|
-
elif precision.this.is_int:
|
78
|
+
if precision_param and precision_param.this.is_int:
|
76
79
|
# Cap precision at 6
|
77
|
-
current_precision =
|
80
|
+
current_precision = precision_param.this.to_py()
|
78
81
|
target_precision = min(current_precision, 6)
|
82
|
+
else:
|
83
|
+
# If precision exists but is not an integer, default to 6
|
84
|
+
target_precision = 6
|
79
85
|
|
80
86
|
# Create a new expression with the target precision
|
81
|
-
|
87
|
+
expression = exp.DataType(
|
82
88
|
this=expression.this,
|
83
89
|
expressions=[exp.DataTypeParam(this=exp.Literal.number(target_precision))],
|
84
90
|
)
|
85
91
|
|
86
|
-
return super().datatype_sql(new_expression)
|
87
|
-
|
88
92
|
return super().datatype_sql(expression)
|
93
|
+
|
94
|
+
def unixtotime_sql(self, expression: exp.UnixToTime) -> str:
|
95
|
+
scale = expression.args.get("scale")
|
96
|
+
timestamp = expression.this
|
97
|
+
|
98
|
+
if scale not in (None, exp.UnixToTime.SECONDS):
|
99
|
+
self.unsupported(f"UnixToTime scale {scale} is not supported by Fabric")
|
100
|
+
return ""
|
101
|
+
|
102
|
+
# Convert unix timestamp (seconds) to microseconds and round to avoid decimals
|
103
|
+
microseconds = timestamp * exp.Literal.number("1e6")
|
104
|
+
rounded = exp.func("round", microseconds, 0)
|
105
|
+
rounded_ms_as_bigint = exp.cast(rounded, exp.DataType.Type.BIGINT)
|
106
|
+
|
107
|
+
# Create the base datetime as '1970-01-01' cast to DATETIME2(6)
|
108
|
+
epoch_start = exp.cast("'1970-01-01'", "datetime2(6)", dialect="fabric")
|
109
|
+
|
110
|
+
dateadd = exp.DateAdd(
|
111
|
+
this=epoch_start,
|
112
|
+
expression=rounded_ms_as_bigint,
|
113
|
+
unit=exp.Literal.string("MICROSECONDS"),
|
114
|
+
)
|
115
|
+
return self.sql(dateadd)
|
sqlglot/dialects/presto.py
CHANGED
@@ -8,6 +8,7 @@ from sqlglot.dialects.dialect import (
|
|
8
8
|
NormalizationStrategy,
|
9
9
|
binary_from_function,
|
10
10
|
bool_xor_sql,
|
11
|
+
build_replace_with_optional_replacement,
|
11
12
|
date_trunc_to_time,
|
12
13
|
datestrtodate_sql,
|
13
14
|
encode_decode_sql,
|
@@ -360,6 +361,7 @@ class Presto(Dialect):
|
|
360
361
|
expression=seq_get(args, 1),
|
361
362
|
replacement=seq_get(args, 2) or exp.Literal.string(""),
|
362
363
|
),
|
364
|
+
"REPLACE": build_replace_with_optional_replacement,
|
363
365
|
"ROW": exp.Struct.from_arg_list,
|
364
366
|
"SEQUENCE": exp.GenerateSeries.from_arg_list,
|
365
367
|
"SET_AGG": exp.ArrayUniqueAgg.from_arg_list,
|
sqlglot/dialects/redshift.py
CHANGED
@@ -213,8 +213,7 @@ class Redshift(Postgres):
|
|
213
213
|
exp.TableSample: no_tablesample_sql,
|
214
214
|
exp.TsOrDsAdd: date_delta_sql("DATEADD"),
|
215
215
|
exp.TsOrDsDiff: date_delta_sql("DATEDIFF"),
|
216
|
-
exp.UnixToTime: lambda self,
|
217
|
-
e: f"(TIMESTAMP 'epoch' + {self.sql(e.this)} * INTERVAL '1 SECOND')",
|
216
|
+
exp.UnixToTime: lambda self, e: self._unix_to_time_sql(e),
|
218
217
|
}
|
219
218
|
|
220
219
|
# Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots
|
@@ -447,3 +446,12 @@ class Redshift(Postgres):
|
|
447
446
|
def explode_sql(self, expression: exp.Explode) -> str:
|
448
447
|
self.unsupported("Unsupported EXPLODE() function")
|
449
448
|
return ""
|
449
|
+
|
450
|
+
def _unix_to_time_sql(self, expression: exp.UnixToTime) -> str:
|
451
|
+
scale = expression.args.get("scale")
|
452
|
+
this = self.sql(expression.this)
|
453
|
+
|
454
|
+
if scale is not None and scale != exp.UnixToTime.SECONDS and scale.is_int:
|
455
|
+
this = f"({this} / POWER(10, {scale.to_py()}))"
|
456
|
+
|
457
|
+
return f"(TIMESTAMP 'epoch' + {this} * INTERVAL '1 SECOND')"
|
sqlglot/dialects/snowflake.py
CHANGED
@@ -9,6 +9,7 @@ from sqlglot.dialects.dialect import (
|
|
9
9
|
build_timetostr_or_tochar,
|
10
10
|
binary_from_function,
|
11
11
|
build_default_decimal_type,
|
12
|
+
build_replace_with_optional_replacement,
|
12
13
|
build_timestamp_from_parts,
|
13
14
|
date_delta_sql,
|
14
15
|
date_trunc_to_time,
|
@@ -484,6 +485,7 @@ class Snowflake(Dialect):
|
|
484
485
|
"REGEXP_REPLACE": _build_regexp_replace,
|
485
486
|
"REGEXP_SUBSTR": _build_regexp_extract(exp.RegexpExtract),
|
486
487
|
"REGEXP_SUBSTR_ALL": _build_regexp_extract(exp.RegexpExtractAll),
|
488
|
+
"REPLACE": build_replace_with_optional_replacement,
|
487
489
|
"RLIKE": exp.RegexpLike.from_arg_list,
|
488
490
|
"SQUARE": lambda args: exp.Pow(this=seq_get(args, 0), expression=exp.Literal.number(2)),
|
489
491
|
"TABLE": lambda args: exp.TableFromRows(this=seq_get(args, 0)),
|
@@ -1416,7 +1418,7 @@ class Snowflake(Dialect):
|
|
1416
1418
|
|
1417
1419
|
def timetostr_sql(self, expression: exp.TimeToStr) -> str:
|
1418
1420
|
this = expression.this
|
1419
|
-
if
|
1421
|
+
if this.is_string:
|
1420
1422
|
this = exp.cast(this, exp.DataType.Type.TIMESTAMP)
|
1421
1423
|
|
1422
1424
|
return self.func("TO_CHAR", this, self.format_time(expression))
|
sqlglot/dialects/tsql.py
CHANGED
@@ -612,6 +612,7 @@ class TSQL(Dialect):
|
|
612
612
|
"SYSDATETIME": exp.CurrentTimestamp.from_arg_list,
|
613
613
|
"SUSER_NAME": exp.CurrentUser.from_arg_list,
|
614
614
|
"SUSER_SNAME": exp.CurrentUser.from_arg_list,
|
615
|
+
"SYSDATETIMEOFFSET": exp.CurrentTimestampLTZ.from_arg_list,
|
615
616
|
"SYSTEM_USER": exp.CurrentUser.from_arg_list,
|
616
617
|
"TIMEFROMPARTS": _build_timefromparts,
|
617
618
|
"DATETRUNC": _build_datetrunc,
|
@@ -1020,6 +1021,7 @@ class TSQL(Dialect):
|
|
1020
1021
|
exp.CTE: transforms.preprocess([qualify_derived_table_outputs]),
|
1021
1022
|
exp.CurrentDate: rename_func("GETDATE"),
|
1022
1023
|
exp.CurrentTimestamp: rename_func("GETDATE"),
|
1024
|
+
exp.CurrentTimestampLTZ: rename_func("SYSDATETIMEOFFSET"),
|
1023
1025
|
exp.DateStrToDate: datestrtodate_sql,
|
1024
1026
|
exp.Extract: rename_func("DATEPART"),
|
1025
1027
|
exp.GeneratedAsIdentityColumnConstraint: generatedasidentitycolumnconstraint_sql,
|
@@ -1249,15 +1251,15 @@ class TSQL(Dialect):
|
|
1249
1251
|
sql_with_ctes = self.prepend_ctes(expression, sql)
|
1250
1252
|
sql_literal = self.sql(exp.Literal.string(sql_with_ctes))
|
1251
1253
|
if kind == "SCHEMA":
|
1252
|
-
return f"""IF NOT EXISTS (SELECT * FROM
|
1254
|
+
return f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = {identifier}) EXEC({sql_literal})"""
|
1253
1255
|
elif kind == "TABLE":
|
1254
1256
|
assert table
|
1255
1257
|
where = exp.and_(
|
1256
|
-
exp.column("
|
1257
|
-
exp.column("
|
1258
|
-
exp.column("
|
1258
|
+
exp.column("TABLE_NAME").eq(table.name),
|
1259
|
+
exp.column("TABLE_SCHEMA").eq(table.db) if table.db else None,
|
1260
|
+
exp.column("TABLE_CATALOG").eq(table.catalog) if table.catalog else None,
|
1259
1261
|
)
|
1260
|
-
return f"""IF NOT EXISTS (SELECT * FROM
|
1262
|
+
return f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE {where}) EXEC({sql_literal})"""
|
1261
1263
|
elif kind == "INDEX":
|
1262
1264
|
index = self.sql(exp.Literal.string(expression.this.text("this")))
|
1263
1265
|
return f"""IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = object_id({identifier}) AND name = {index}) EXEC({sql_literal})"""
|
sqlglot/expressions.py
CHANGED
@@ -5806,6 +5806,10 @@ class CurrentTimestamp(Func):
|
|
5806
5806
|
arg_types = {"this": False, "sysdate": False}
|
5807
5807
|
|
5808
5808
|
|
5809
|
+
class CurrentTimestampLTZ(Func):
|
5810
|
+
arg_types = {}
|
5811
|
+
|
5812
|
+
|
5809
5813
|
class CurrentSchema(Func):
|
5810
5814
|
arg_types = {"this": False}
|
5811
5815
|
|
@@ -5846,8 +5850,6 @@ class DateTrunc(Func):
|
|
5846
5850
|
unit_name = TimeUnit.UNABBREVIATED_UNIT_NAME[unit_name]
|
5847
5851
|
|
5848
5852
|
args["unit"] = Literal.string(unit_name)
|
5849
|
-
elif isinstance(unit, Week):
|
5850
|
-
unit.set("this", Literal.string(unit.this.name.upper()))
|
5851
5853
|
|
5852
5854
|
super().__init__(**args)
|
5853
5855
|
|
@@ -6669,6 +6671,11 @@ class Repeat(Func):
|
|
6669
6671
|
arg_types = {"this": True, "times": True}
|
6670
6672
|
|
6671
6673
|
|
6674
|
+
# Some dialects like Snowflake support two argument replace
|
6675
|
+
class Replace(Func):
|
6676
|
+
arg_types = {"this": True, "expression": True, "replacement": False}
|
6677
|
+
|
6678
|
+
|
6672
6679
|
# https://learn.microsoft.com/en-us/sql/t-sql/functions/round-transact-sql?view=sql-server-ver16
|
6673
6680
|
# tsql third argument function == trunctaion if not 0
|
6674
6681
|
class Round(Func):
|
sqlglot/generator.py
CHANGED
@@ -3480,7 +3480,7 @@ class Generator(metaclass=_Generator):
|
|
3480
3480
|
|
3481
3481
|
actions_list.append(action_sql)
|
3482
3482
|
|
3483
|
-
actions_sql = self.format_args(*actions_list)
|
3483
|
+
actions_sql = self.format_args(*actions_list).lstrip("\n")
|
3484
3484
|
|
3485
3485
|
exists = " IF EXISTS" if expression.args.get("exists") else ""
|
3486
3486
|
on_cluster = self.sql(expression, "cluster")
|
@@ -3491,7 +3491,7 @@ class Generator(metaclass=_Generator):
|
|
3491
3491
|
kind = self.sql(expression, "kind")
|
3492
3492
|
not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
|
3493
3493
|
|
3494
|
-
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}
|
3494
|
+
return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{self.sep()}{actions_sql}{not_valid}{options}"
|
3495
3495
|
|
3496
3496
|
def add_column_sql(self, expression: exp.Expression) -> str:
|
3497
3497
|
sql = self.sql(expression)
|
@@ -3510,7 +3510,7 @@ class Generator(metaclass=_Generator):
|
|
3510
3510
|
return f"DROP{exists}{expressions}"
|
3511
3511
|
|
3512
3512
|
def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
|
3513
|
-
return f"ADD {self.expressions(expression)}"
|
3513
|
+
return f"ADD {self.expressions(expression, indent=False)}"
|
3514
3514
|
|
3515
3515
|
def addpartition_sql(self, expression: exp.AddPartition) -> str:
|
3516
3516
|
exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
|
sqlglot/optimizer/scope.py
CHANGED
@@ -358,7 +358,7 @@ class Scope:
|
|
358
358
|
for expression in itertools.chain(self.derived_tables, self.udtfs):
|
359
359
|
self._references.append(
|
360
360
|
(
|
361
|
-
expression
|
361
|
+
_get_source_alias(expression),
|
362
362
|
expression if expression.args.get("pivots") else expression.unnest(),
|
363
363
|
)
|
364
364
|
)
|
@@ -785,7 +785,7 @@ def _traverse_tables(scope):
|
|
785
785
|
# This shouldn't be a problem once qualify_columns runs, as it adds aliases on everything.
|
786
786
|
# Until then, this means that only a single, unaliased derived table is allowed (rather,
|
787
787
|
# the latest one wins.
|
788
|
-
sources[expression
|
788
|
+
sources[_get_source_alias(expression)] = child_scope
|
789
789
|
|
790
790
|
# append the final child_scope yielded
|
791
791
|
if child_scope:
|
@@ -825,7 +825,7 @@ def _traverse_udtfs(scope):
|
|
825
825
|
):
|
826
826
|
yield child_scope
|
827
827
|
top = child_scope
|
828
|
-
sources[expression
|
828
|
+
sources[_get_source_alias(expression)] = child_scope
|
829
829
|
|
830
830
|
scope.subquery_scopes.append(top)
|
831
831
|
|
@@ -915,3 +915,13 @@ def find_in_scope(expression, expression_types, bfs=True):
|
|
915
915
|
the criteria was found.
|
916
916
|
"""
|
917
917
|
return next(find_all_in_scope(expression, expression_types, bfs=bfs), None)
|
918
|
+
|
919
|
+
|
920
|
+
def _get_source_alias(expression):
|
921
|
+
alias_arg = expression.args.get("alias")
|
922
|
+
alias_name = expression.alias
|
923
|
+
|
924
|
+
if not alias_name and isinstance(alias_arg, exp.TableAlias) and len(alias_arg.columns) == 1:
|
925
|
+
alias_name = alias_arg.columns[0].name
|
926
|
+
|
927
|
+
return alias_name
|
sqlglot/parser.py
CHANGED
@@ -7362,8 +7362,9 @@ class Parser(metaclass=_Parser):
|
|
7362
7362
|
|
7363
7363
|
return None
|
7364
7364
|
|
7365
|
-
if not self.
|
7366
|
-
|
7365
|
+
if not self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False) and (
|
7366
|
+
not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
|
7367
|
+
or self._match_text_seq("COLUMNS")
|
7367
7368
|
):
|
7368
7369
|
schema = self._parse_schema()
|
7369
7370
|
|
sqlglot/transforms.py
CHANGED
@@ -352,13 +352,20 @@ def unnest_to_explode(
|
|
352
352
|
has_multi_expr = len(exprs) > 1
|
353
353
|
this, *expressions = _unnest_zip_exprs(unnest, exprs, has_multi_expr)
|
354
354
|
|
355
|
+
columns = alias.columns if alias else []
|
356
|
+
offset = unnest.args.get("offset")
|
357
|
+
if offset:
|
358
|
+
columns.insert(
|
359
|
+
0, offset if isinstance(offset, exp.Identifier) else exp.to_identifier("pos")
|
360
|
+
)
|
361
|
+
|
355
362
|
unnest.replace(
|
356
363
|
exp.Table(
|
357
364
|
this=_udtf_type(unnest, has_multi_expr)(
|
358
365
|
this=this,
|
359
366
|
expressions=expressions,
|
360
367
|
),
|
361
|
-
alias=exp.TableAlias(this=alias.this, columns=
|
368
|
+
alias=exp.TableAlias(this=alias.this, columns=columns) if alias else None,
|
362
369
|
)
|
363
370
|
)
|
364
371
|
|
@@ -393,6 +400,13 @@ def unnest_to_explode(
|
|
393
400
|
"CROSS JOIN UNNEST to LATERAL VIEW EXPLODE transformation requires explicit column aliases"
|
394
401
|
)
|
395
402
|
|
403
|
+
offset = unnest.args.get("offset")
|
404
|
+
if offset:
|
405
|
+
alias_cols.insert(
|
406
|
+
0,
|
407
|
+
offset if isinstance(offset, exp.Identifier) else exp.to_identifier("pos"),
|
408
|
+
)
|
409
|
+
|
396
410
|
for e, column in zip(exprs, alias_cols):
|
397
411
|
expression.append(
|
398
412
|
"laterals",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sqlglot
|
3
|
-
Version: 26.
|
3
|
+
Version: 26.31.0
|
4
4
|
Summary: An easily customizable SQL parser and transpiler
|
5
5
|
Author-email: Toby Mao <toby.mao@gmail.com>
|
6
6
|
License: MIT License
|
@@ -61,7 +61,7 @@ Dynamic: provides-extra
|
|
61
61
|
|
62
62
|

|
63
63
|
|
64
|
-
SQLGlot is a no-dependency SQL parser, transpiler, optimizer, and engine. It can be used to format SQL or translate between [
|
64
|
+
SQLGlot is a no-dependency SQL parser, transpiler, optimizer, and engine. It can be used to format SQL or translate between [29 different dialects](https://github.com/tobymao/sqlglot/blob/main/sqlglot/dialects/__init__.py) like [DuckDB](https://duckdb.org/), [Presto](https://prestodb.io/) / [Trino](https://trino.io/), [Spark](https://spark.apache.org/) / [Databricks](https://www.databricks.com/), [Snowflake](https://www.snowflake.com/en/), and [BigQuery](https://cloud.google.com/bigquery/). It aims to read a wide variety of SQL inputs and output syntactically and semantically correct SQL in the targeted dialects.
|
65
65
|
|
66
66
|
It is a very comprehensive generic SQL parser with a robust [test suite](https://github.com/tobymao/sqlglot/blob/main/tests/). It is also quite [performant](#benchmarks), while being written purely in Python.
|
67
67
|
|
@@ -1,45 +1,46 @@
|
|
1
1
|
sqlglot/__init__.py,sha256=za08rtdPh2v7dOpGdNomttlIVGgTrKja7rPd6sQwaTg,5391
|
2
2
|
sqlglot/__main__.py,sha256=022c173KqxsiABWTEpUIq_tJUxuNiW7a7ABsxBXqvu8,2069
|
3
3
|
sqlglot/_typing.py,sha256=-1HPyr3w5COlSJWqlgt8jhFk2dyMvBuvVBqIX1wyVCM,642
|
4
|
-
sqlglot/_version.py,sha256=
|
4
|
+
sqlglot/_version.py,sha256=X5X34o5ymsD4ydxIloUOjJGcZ-0Zi6rgP-736DtnUZ8,515
|
5
5
|
sqlglot/diff.py,sha256=PtOllQMQa1Sw1-V2Y8eypmDqGujXYPaTOp_WLsWkAWk,17314
|
6
6
|
sqlglot/errors.py,sha256=QNKMr-pzLUDR-tuMmn_GK6iMHUIVdb_YSJ_BhGEvuso,2126
|
7
|
-
sqlglot/expressions.py,sha256=
|
8
|
-
sqlglot/generator.py,sha256=
|
7
|
+
sqlglot/expressions.py,sha256=rYPkorYfWlBzPxyaodGqIkW-x6RG1gSkVjBkOfkdZiI,243434
|
8
|
+
sqlglot/generator.py,sha256=Od0aBsKJph1wG_YhrknJAcAcVvuVIN823iyxA3KPi0Y,213383
|
9
9
|
sqlglot/helper.py,sha256=9nZjFVRBtMKFC3EdzpDQ6jkazFO19po6BF8xHiNGZIo,15111
|
10
10
|
sqlglot/jsonpath.py,sha256=dKdI3PNINNGimmSse2IIv-GbPN_3lXncXh_70QH7Lss,7664
|
11
11
|
sqlglot/lineage.py,sha256=kXBDSErmZZluZx_kkrMj4MPEOAbkvcbX1tbOW7Bpl-U,15303
|
12
|
-
sqlglot/parser.py,sha256=
|
12
|
+
sqlglot/parser.py,sha256=Mqm77jhuF0b3hyuFPgYtLAMPkuslF64Y8iHIOPw3ZWA,324610
|
13
13
|
sqlglot/planner.py,sha256=ql7Li-bWJRcyXzNaZy_n6bQ6B2ZfunEIB8Ztv2xaxq4,14634
|
14
14
|
sqlglot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
sqlglot/schema.py,sha256=13H2qKQs27EKdTpDLOvcNnSTDAUbYNKjWtJs4aQCSOA,20509
|
16
16
|
sqlglot/serde.py,sha256=DQVJ95WrIvhYfe02Ytb4NQug2aMwDCEwpMBW1LKDqzE,2031
|
17
17
|
sqlglot/time.py,sha256=Q62gv6kL40OiRBF6BMESxKJcMVn7ZLNw7sv8H34z5FI,18400
|
18
18
|
sqlglot/tokens.py,sha256=R0B8GQSbQ9GoDc0NlaT5Tc8RjgEOx2IYIkYU5rY8Rg8,48742
|
19
|
-
sqlglot/transforms.py,sha256=
|
19
|
+
sqlglot/transforms.py,sha256=s96QMtR7rJbcLAU1I_IF1xLNxno6yvEbhERgbS5xmJ4,41164
|
20
20
|
sqlglot/trie.py,sha256=v27uXMrHfqrXlJ6GmeTSMovsB_3o0ctnlKhdNt7W6fI,2245
|
21
|
-
sqlglot/dialects/__init__.py,sha256=
|
21
|
+
sqlglot/dialects/__init__.py,sha256=G-YO1_zIcONWb9LjTjHX_HGzGl9Rm0sA9MX4ok6tpns,3527
|
22
22
|
sqlglot/dialects/athena.py,sha256=gPE9ybRcbd6dVa1mrTFB_eVjsjQG36hErq5EpHyQmXo,6344
|
23
|
-
sqlglot/dialects/bigquery.py,sha256=
|
23
|
+
sqlglot/dialects/bigquery.py,sha256=5s4hSe-PXbjeIlKhAZon-rGq4ZIywYZj1kxx213V748,52862
|
24
24
|
sqlglot/dialects/clickhouse.py,sha256=Dc0aXwEgN8b6coXKM6P8zh3IsyrXjBajNGB-cVhnu1Y,56603
|
25
|
-
sqlglot/dialects/databricks.py,sha256=
|
26
|
-
sqlglot/dialects/dialect.py,sha256
|
25
|
+
sqlglot/dialects/databricks.py,sha256=mJN2lFpqgH95x3mtry3qWbuRf4q7NV5jbRAOspqclzY,4548
|
26
|
+
sqlglot/dialects/dialect.py,sha256=qcpaE4cYO3v2R1cQVonpbrJOybYspnEdXSkXWxDW6d4,68921
|
27
27
|
sqlglot/dialects/doris.py,sha256=eC7Ct-iz7p4Usz659NkelUFhm-GmVolIZy5uaBvgjaA,14397
|
28
28
|
sqlglot/dialects/drill.py,sha256=FOh7_KjPx_77pv0DiHKZog0CcmzqeF9_PEmGnJ1ESSM,5825
|
29
29
|
sqlglot/dialects/druid.py,sha256=kh3snZtneehNOWqs3XcPjsrhNaRbkCQ8E4hHbWJ1fHM,690
|
30
30
|
sqlglot/dialects/duckdb.py,sha256=oGCgK0KjwJcCKy-YOZeiQnEo4v7Zc1r5AK0tCXO2VIc,48005
|
31
31
|
sqlglot/dialects/dune.py,sha256=gALut-fFfN2qMsr8LvZ1NQK3F3W9z2f4PwMvTMXVVVg,375
|
32
|
-
sqlglot/dialects/
|
32
|
+
sqlglot/dialects/exasol.py,sha256=r2fO9FHfMV1_1M62wBGlNcQ6fHWikO4SBr8eCzxEYEY,2008
|
33
|
+
sqlglot/dialects/fabric.py,sha256=IU7aMh2yEuG8eVBAYzXO5pObZBZ4rZSd5UgvkwbCI-E,5277
|
33
34
|
sqlglot/dialects/hive.py,sha256=yKCsVN4R8pIB2Lmx1YGiSR9b8Me3li6rsGuZrKjHTo4,31771
|
34
35
|
sqlglot/dialects/materialize.py,sha256=_DPLPt8YrdQIIXNrGJw1IMcGOoAEJ9NO9X9pDfy4hxs,3494
|
35
36
|
sqlglot/dialects/mysql.py,sha256=prZecn3zeoifZX7l54UuLG64ar7I-or_z9lF-rT8bds,49233
|
36
37
|
sqlglot/dialects/oracle.py,sha256=o6On1cYWFt6TpQYKuzo4kCz5vKb8jQr8WSwc619h3Lg,15967
|
37
38
|
sqlglot/dialects/postgres.py,sha256=KUyMoLkm1_sZKUbdjn6bjXx9xz7sbEMKa-fl5Mzfrsk,31025
|
38
|
-
sqlglot/dialects/presto.py,sha256=
|
39
|
+
sqlglot/dialects/presto.py,sha256=dHdPv6tUO-7SAYUWnx5ftKzv6FcRvzBfiYDTlQvL2Cs,33312
|
39
40
|
sqlglot/dialects/prql.py,sha256=fwN-SPEGx-drwf1K0U2MByN-PkW3C_rOgQ3xeJeychg,7908
|
40
|
-
sqlglot/dialects/redshift.py,sha256=
|
41
|
+
sqlglot/dialects/redshift.py,sha256=sHhibn2g6_hVRd1XEe8HSQd_ofWkEpzld0odsNQ6X2g,15747
|
41
42
|
sqlglot/dialects/risingwave.py,sha256=hwEOPjMw0ZM_3fjQcBUE00oy6I8V6mzYOOYmcwwS8mw,2898
|
42
|
-
sqlglot/dialects/snowflake.py,sha256=
|
43
|
+
sqlglot/dialects/snowflake.py,sha256=68I7OjdWXSVnDxJ-ItmXnJd-A1nlND1T6aKNv0nkJlQ,63518
|
43
44
|
sqlglot/dialects/spark.py,sha256=bOUSXUoWtLfWaQ9fIjWaw4zLBJY6N7vxajdMbAxLdOk,8307
|
44
45
|
sqlglot/dialects/spark2.py,sha256=8er7nHDm5Wc57m9AOxKN0sd_DVzbhAL44H_udlFh9O8,14258
|
45
46
|
sqlglot/dialects/sqlite.py,sha256=fwqmopeuoupD_2dh2q6rT3UFxWtFHkskZ1OXAYnPT9Q,12483
|
@@ -47,7 +48,7 @@ sqlglot/dialects/starrocks.py,sha256=fHNgvq5Nz7dI4QUWCTOO5VDOYjasBxRRlcg9TbY0UZE
|
|
47
48
|
sqlglot/dialects/tableau.py,sha256=oIawDzUITxGCWaEMB8OaNMPWhbC3U-2y09pYPm4eazc,2190
|
48
49
|
sqlglot/dialects/teradata.py,sha256=xWa-9kSTsT-eM1NePi_oIM1dPHmXW89GLU5Uda3_6Ao,14036
|
49
50
|
sqlglot/dialects/trino.py,sha256=wgLsiX1NQvjGny_rgrU1e2r6kK1LD0KgaSdIDrYmjD0,4285
|
50
|
-
sqlglot/dialects/tsql.py,sha256=
|
51
|
+
sqlglot/dialects/tsql.py,sha256=dKlGmOmRFDx2MO5YebAAIK3FHorLZfzR0iqtK6xiiX4,54540
|
51
52
|
sqlglot/executor/__init__.py,sha256=FslewzYQtQdDNg_0Ju2UaiP4vo4IMUgkfkmFsYUhcN0,2958
|
52
53
|
sqlglot/executor/context.py,sha256=WJHJdYQCOeVXwLw0uSSrWSc25eBMn5Ix108RCvdsKRQ,3386
|
53
54
|
sqlglot/executor/env.py,sha256=tQhU5PpTBMcxgZIFddFqxWMNPtHN0vOOz72voncY3KY,8276
|
@@ -70,11 +71,11 @@ sqlglot/optimizer/pushdown_projections.py,sha256=7NoK5NAUVYVhs0YnYyo6WuXfaO-BShS
|
|
70
71
|
sqlglot/optimizer/qualify.py,sha256=oAPfwub7dEkrlCrsptcJWpLya4BgKhN6M5SwIs_86LY,4002
|
71
72
|
sqlglot/optimizer/qualify_columns.py,sha256=77aScPakXYaiagnoCWk2qwMxlKuRGsFTAK9sOQuR2vY,40872
|
72
73
|
sqlglot/optimizer/qualify_tables.py,sha256=5f5enBAh-bpNB9ewF97W9fx9h1TGXj1Ih5fncvH42sY,6486
|
73
|
-
sqlglot/optimizer/scope.py,sha256=
|
74
|
+
sqlglot/optimizer/scope.py,sha256=HI3TZ4VWTgM6_x8k5ClA0lA0xidaKv4xgn8iGERJRjk,30824
|
74
75
|
sqlglot/optimizer/simplify.py,sha256=S0Blqg5Mq2KRRWhWz-Eivch9sBjBhg9fRJA6EdBzj2g,50704
|
75
76
|
sqlglot/optimizer/unnest_subqueries.py,sha256=kzWUVDlxs8z9nmRx-8U-pHXPtVZhEIwkKqmKhr2QLvc,10908
|
76
|
-
sqlglot-26.
|
77
|
-
sqlglot-26.
|
78
|
-
sqlglot-26.
|
79
|
-
sqlglot-26.
|
80
|
-
sqlglot-26.
|
77
|
+
sqlglot-26.31.0.dist-info/licenses/LICENSE,sha256=AI3__mHZfOtzY3EluR_pIYBm3_pE7TbVx7qaHxoZ114,1065
|
78
|
+
sqlglot-26.31.0.dist-info/METADATA,sha256=OAEEcPh5a0gV2C4sacAbhuXg4cpNWPUXeGS0H6iAGgs,20732
|
79
|
+
sqlglot-26.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
80
|
+
sqlglot-26.31.0.dist-info/top_level.txt,sha256=5kRskCGA_gVADF9rSfSzPdLHXqvfMusDYeHePfNY2nQ,8
|
81
|
+
sqlglot-26.31.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|