sqlglot 27.6.0__py3-none-any.whl → 27.7.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 CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '27.6.0'
21
- __version_tuple__ = version_tuple = (27, 6, 0)
20
+ __version__ = version = '27.7.0'
21
+ __version_tuple__ = version_tuple = (27, 7, 0)
@@ -481,10 +481,20 @@ class BigQuery(Dialect):
481
481
  exp.BitwiseOrAgg: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BIGINT),
482
482
  exp.BitwiseXorAgg: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BIGINT),
483
483
  exp.BitwiseCountAgg: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BIGINT),
484
+ exp.ByteLength: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BIGINT),
485
+ exp.ByteString: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BINARY),
486
+ exp.CodePointsToString: lambda self, e: self._annotate_with_type(
487
+ e, exp.DataType.Type.VARCHAR
488
+ ),
484
489
  exp.Concat: _annotate_concat,
485
490
  exp.Corr: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.DOUBLE),
486
491
  exp.CovarPop: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.DOUBLE),
487
492
  exp.CovarSamp: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.DOUBLE),
493
+ exp.DateFromUnixDate: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.DATE),
494
+ exp.DateTrunc: lambda self, e: self._annotate_by_args(e, "this"),
495
+ exp.GenerateTimestampArray: lambda self, e: self._annotate_with_type(
496
+ e, exp.DataType.build("ARRAY<TIMESTAMP>", dialect="bigquery")
497
+ ),
488
498
  exp.JSONArray: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.JSON),
489
499
  exp.JSONExtractScalar: lambda self, e: self._annotate_with_type(
490
500
  e, exp.DataType.Type.VARCHAR
@@ -494,6 +504,9 @@ class BigQuery(Dialect):
494
504
  ),
495
505
  exp.JSONType: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.VARCHAR),
496
506
  exp.Lag: lambda self, e: self._annotate_by_args(e, "this", "default"),
507
+ exp.ParseTime: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.TIME),
508
+ exp.ParseDatetime: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.DATETIME),
509
+ exp.Reverse: lambda self, e: self._annotate_by_args(e, "this"),
497
510
  exp.SHA: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BINARY),
498
511
  exp.SHA2: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BINARY),
499
512
  exp.Sign: lambda self, e: self._annotate_by_args(e, "this"),
@@ -501,6 +514,10 @@ class BigQuery(Dialect):
501
514
  exp.TimestampFromParts: lambda self, e: self._annotate_with_type(
502
515
  e, exp.DataType.Type.DATETIME
503
516
  ),
517
+ exp.TimestampTrunc: lambda self, e: self._annotate_by_args(e, "this"),
518
+ exp.TimeFromParts: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.TIME),
519
+ exp.TsOrDsToTime: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.TIME),
520
+ exp.TimeTrunc: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.TIME),
504
521
  exp.Unicode: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.BIGINT),
505
522
  }
506
523
 
@@ -621,7 +638,13 @@ class BigQuery(Dialect):
621
638
  "PARSE_DATE": lambda args: build_formatted_time(exp.StrToDate, "bigquery")(
622
639
  [seq_get(args, 1), seq_get(args, 0)]
623
640
  ),
641
+ "PARSE_TIME": lambda args: build_formatted_time(exp.ParseTime, "bigquery")(
642
+ [seq_get(args, 1), seq_get(args, 0)]
643
+ ),
624
644
  "PARSE_TIMESTAMP": _build_parse_timestamp,
645
+ "PARSE_DATETIME": lambda args: build_formatted_time(exp.ParseDatetime, "bigquery")(
646
+ [seq_get(args, 1), seq_get(args, 0)]
647
+ ),
625
648
  "REGEXP_CONTAINS": exp.RegexpLike.from_arg_list,
626
649
  "REGEXP_EXTRACT": _build_regexp_extract(exp.RegexpExtract),
627
650
  "REGEXP_SUBSTR": _build_regexp_extract(exp.RegexpExtract),
@@ -652,6 +675,8 @@ class BigQuery(Dialect):
652
675
  "TO_JSON_STRING": exp.JSONFormat.from_arg_list,
653
676
  "FORMAT_DATETIME": _build_format_time(exp.TsOrDsToDatetime),
654
677
  "FORMAT_TIMESTAMP": _build_format_time(exp.TsOrDsToTimestamp),
678
+ "FORMAT_TIME": _build_format_time(exp.TsOrDsToTime),
679
+ "WEEK": lambda args: exp.WeekStart(this=exp.var(seq_get(args, 0))),
655
680
  }
656
681
 
657
682
  FUNCTION_PARSERS = {
@@ -994,6 +1019,13 @@ class BigQuery(Dialect):
994
1019
  EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
995
1020
  SUPPORTS_UNIX_SECONDS = True
996
1021
 
1022
+ TS_OR_DS_TYPES = (
1023
+ exp.TsOrDsToDatetime,
1024
+ exp.TsOrDsToTimestamp,
1025
+ exp.TsOrDsToTime,
1026
+ exp.TsOrDsToDate,
1027
+ )
1028
+
997
1029
  TRANSFORMS = {
998
1030
  **generator.Generator.TRANSFORMS,
999
1031
  exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"),
@@ -1022,6 +1054,7 @@ class BigQuery(Dialect):
1022
1054
  exp.DateSub: date_add_interval_sql("DATE", "SUB"),
1023
1055
  exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"),
1024
1056
  exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"),
1057
+ exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
1025
1058
  exp.FromTimeZone: lambda self, e: self.func(
1026
1059
  "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'"
1027
1060
  ),
@@ -1059,6 +1092,10 @@ class BigQuery(Dialect):
1059
1092
  exp.RegexpLike: rename_func("REGEXP_CONTAINS"),
1060
1093
  exp.ReturnsProperty: _returnsproperty_sql,
1061
1094
  exp.Rollback: lambda *_: "ROLLBACK TRANSACTION",
1095
+ exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this),
1096
+ exp.ParseDatetime: lambda self, e: self.func(
1097
+ "PARSE_DATETIME", self.format_time(e), e.this
1098
+ ),
1062
1099
  exp.Select: transforms.preprocess(
1063
1100
  [
1064
1101
  transforms.explode_projection_to_unnest(),
@@ -1297,14 +1334,12 @@ class BigQuery(Dialect):
1297
1334
  func_name = "FORMAT_DATETIME"
1298
1335
  elif isinstance(this, exp.TsOrDsToTimestamp):
1299
1336
  func_name = "FORMAT_TIMESTAMP"
1337
+ elif isinstance(this, exp.TsOrDsToTime):
1338
+ func_name = "FORMAT_TIME"
1300
1339
  else:
1301
1340
  func_name = "FORMAT_DATE"
1302
1341
 
1303
- time_expr = (
1304
- this
1305
- if isinstance(this, (exp.TsOrDsToDatetime, exp.TsOrDsToTimestamp, exp.TsOrDsToDate))
1306
- else expression
1307
- )
1342
+ time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression
1308
1343
  return self.func(
1309
1344
  func_name, self.format_time(expression), time_expr.this, expression.args.get("zone")
1310
1345
  )
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  import typing as t
3
3
  import datetime
4
4
  from sqlglot import exp, generator, parser, tokens
5
+ from sqlglot._typing import E
5
6
  from sqlglot.dialects.dialect import (
6
7
  Dialect,
7
8
  NormalizationStrategy,
@@ -31,14 +32,19 @@ from sqlglot.generator import unsupported_args
31
32
  DATEΤΙΜΕ_DELTA = t.Union[exp.DateAdd, exp.DateDiff, exp.DateSub, exp.TimestampSub, exp.TimestampAdd]
32
33
 
33
34
 
34
- def _build_date_format(args: t.List) -> exp.TimeToStr:
35
- expr = build_formatted_time(exp.TimeToStr, "clickhouse")(args)
35
+ def _build_datetime_format(
36
+ expr_type: t.Type[E],
37
+ ) -> t.Callable[[t.List], E]:
38
+ def _builder(args: t.List) -> E:
39
+ expr = build_formatted_time(expr_type, "clickhouse")(args)
36
40
 
37
- timezone = seq_get(args, 2)
38
- if timezone:
39
- expr.set("zone", timezone)
41
+ timezone = seq_get(args, 2)
42
+ if timezone:
43
+ expr.set("zone", timezone)
40
44
 
41
- return expr
45
+ return expr
46
+
47
+ return _builder
42
48
 
43
49
 
44
50
  def _unix_to_time_sql(self: ClickHouse.Generator, expression: exp.UnixToTime) -> str:
@@ -310,16 +316,17 @@ class ClickHouse(Dialect):
310
316
  "DATEADD": build_date_delta(exp.DateAdd, default_unit=None),
311
317
  "DATE_DIFF": build_date_delta(exp.DateDiff, default_unit=None, supports_timezone=True),
312
318
  "DATEDIFF": build_date_delta(exp.DateDiff, default_unit=None, supports_timezone=True),
313
- "DATE_FORMAT": _build_date_format,
319
+ "DATE_FORMAT": _build_datetime_format(exp.TimeToStr),
314
320
  "DATE_SUB": build_date_delta(exp.DateSub, default_unit=None),
315
321
  "DATESUB": build_date_delta(exp.DateSub, default_unit=None),
316
- "FORMATDATETIME": _build_date_format,
322
+ "FORMATDATETIME": _build_datetime_format(exp.TimeToStr),
317
323
  "JSONEXTRACTSTRING": build_json_extract_path(
318
324
  exp.JSONExtractScalar, zero_based_indexing=False
319
325
  ),
320
326
  "LENGTH": lambda args: exp.Length(this=seq_get(args, 0), binary=True),
321
327
  "MAP": parser.build_var_map,
322
328
  "MATCH": exp.RegexpLike.from_arg_list,
329
+ "PARSEDATETIME": _build_datetime_format(exp.ParseDatetime),
323
330
  "RANDCANONICAL": exp.Rand.from_arg_list,
324
331
  "STR_TO_DATE": _build_str_to_date,
325
332
  "TUPLE": exp.Struct.from_arg_list,
@@ -1141,6 +1148,7 @@ class ClickHouse(Dialect):
1141
1148
  exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
1142
1149
  rename_func("editDistance")
1143
1150
  ),
1151
+ exp.ParseDatetime: rename_func("parseDateTime"),
1144
1152
  }
1145
1153
 
1146
1154
  PROPERTIES_LOCATION = {
@@ -1177,6 +1185,17 @@ class ClickHouse(Dialect):
1177
1185
  exp.DataType.Type.MULTIPOLYGON,
1178
1186
  }
1179
1187
 
1188
+ def offset_sql(self, expression: exp.Offset) -> str:
1189
+ offset = super().offset_sql(expression)
1190
+
1191
+ # OFFSET ... FETCH syntax requires a "ROW" or "ROWS" keyword
1192
+ # https://clickhouse.com/docs/sql-reference/statements/select/offset
1193
+ parent = expression.parent
1194
+ if isinstance(parent, exp.Select) and isinstance(parent.args.get("limit"), exp.Fetch):
1195
+ offset = f"{offset} ROWS"
1196
+
1197
+ return offset
1198
+
1180
1199
  def strtodate_sql(self, expression: exp.StrToDate) -> str:
1181
1200
  strtodate_sql = self.function_fallback_sql(expression)
1182
1201
 
@@ -654,6 +654,8 @@ class Dialect(metaclass=_Dialect):
654
654
  exp.Length,
655
655
  exp.UnixDate,
656
656
  exp.UnixSeconds,
657
+ exp.UnixMicros,
658
+ exp.UnixMillis,
657
659
  },
658
660
  exp.DataType.Type.BINARY: {
659
661
  exp.FromBase64,
@@ -674,6 +676,7 @@ class Dialect(metaclass=_Dialect):
674
676
  exp.DateFromParts,
675
677
  exp.DateStrToDate,
676
678
  exp.DiToDate,
679
+ exp.LastDay,
677
680
  exp.StrToDate,
678
681
  exp.TimeStrToDate,
679
682
  exp.TsOrDsToDate,
@@ -718,6 +721,9 @@ class Dialect(metaclass=_Dialect):
718
721
  },
719
722
  exp.DataType.Type.INTERVAL: {
720
723
  exp.Interval,
724
+ exp.JustifyDays,
725
+ exp.JustifyHours,
726
+ exp.JustifyInterval,
721
727
  exp.MakeInterval,
722
728
  },
723
729
  exp.DataType.Type.JSON: {
@@ -1650,9 +1656,11 @@ def unit_to_str(expression: exp.Expression, default: str = "DAY") -> t.Optional[
1650
1656
  def unit_to_var(expression: exp.Expression, default: str = "DAY") -> t.Optional[exp.Expression]:
1651
1657
  unit = expression.args.get("unit")
1652
1658
 
1653
- if isinstance(unit, (exp.Var, exp.Placeholder)):
1659
+ if isinstance(unit, (exp.Var, exp.Placeholder, exp.WeekStart)):
1654
1660
  return unit
1655
- return exp.Var(this=default) if default else None
1661
+
1662
+ value = unit.name if unit else default
1663
+ return exp.Var(this=value) if value else None
1656
1664
 
1657
1665
 
1658
1666
  @t.overload
sqlglot/dialects/doris.py CHANGED
@@ -1,15 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import typing as t
4
+
3
5
  from sqlglot import exp
4
6
  from sqlglot.dialects.dialect import (
5
7
  approx_count_distinct_sql,
6
- build_timestamp_trunc,
7
8
  property_sql,
8
9
  rename_func,
9
10
  time_format,
10
11
  unit_to_str,
11
12
  )
12
13
  from sqlglot.dialects.mysql import MySQL
14
+ from sqlglot.helper import seq_get
13
15
  from sqlglot.tokens import TokenType
14
16
 
15
17
 
@@ -22,6 +24,22 @@ def _lag_lead_sql(self, expression: exp.Lag | exp.Lead) -> str:
22
24
  )
23
25
 
24
26
 
27
+ # Accept both DATE_TRUNC(datetime, unit) and DATE_TRUNC(unit, datetime)
28
+ def _build_date_trunc(args: t.List[exp.Expression]) -> exp.Expression:
29
+ a0, a1 = seq_get(args, 0), seq_get(args, 1)
30
+
31
+ def _is_unit_like(e: exp.Expression | None) -> bool:
32
+ if not (isinstance(e, exp.Literal) and e.is_string):
33
+ return False
34
+ text = e.this
35
+ return not any(ch.isdigit() for ch in text)
36
+
37
+ # Determine which argument is the unit
38
+ unit, this = (a0, a1) if _is_unit_like(a0) else (a1, a0)
39
+
40
+ return exp.TimestampTrunc(this=this, unit=unit)
41
+
42
+
25
43
  class Doris(MySQL):
26
44
  DATE_FORMAT = "'yyyy-MM-dd'"
27
45
  DATEINT_FORMAT = "'yyyyMMdd'"
@@ -31,7 +49,7 @@ class Doris(MySQL):
31
49
  FUNCTIONS = {
32
50
  **MySQL.Parser.FUNCTIONS,
33
51
  "COLLECT_SET": exp.ArrayUniqueAgg.from_arg_list,
34
- "DATE_TRUNC": build_timestamp_trunc,
52
+ "DATE_TRUNC": _build_date_trunc,
35
53
  "MONTHS_ADD": exp.AddMonths.from_arg_list,
36
54
  "REGEXP": exp.RegexpLike.from_arg_list,
37
55
  "TO_DATE": exp.TsOrDsToDate.from_arg_list,
@@ -40,6 +58,9 @@ class Doris(MySQL):
40
58
  FUNCTION_PARSERS = MySQL.Parser.FUNCTION_PARSERS.copy()
41
59
  FUNCTION_PARSERS.pop("GROUP_CONCAT")
42
60
 
61
+ NO_PAREN_FUNCTIONS = MySQL.Parser.NO_PAREN_FUNCTIONS.copy()
62
+ NO_PAREN_FUNCTIONS.pop(TokenType.CURRENT_DATE)
63
+
43
64
  PROPERTY_PARSERS = {
44
65
  **MySQL.Parser.PROPERTY_PARSERS,
45
66
  "PROPERTIES": lambda self: self._parse_wrapped_properties(),
@@ -111,6 +132,7 @@ class Doris(MySQL):
111
132
  LAST_DAY_SUPPORTS_DATE_PART = False
112
133
  VARCHAR_REQUIRES_SIZE = False
113
134
  WITH_PROPERTIES_PREFIX = "PROPERTIES"
135
+ RENAME_TABLE_WITH_DB = False
114
136
 
115
137
  TYPE_MAPPING = {
116
138
  **MySQL.Generator.TYPE_MAPPING,
@@ -123,6 +145,7 @@ class Doris(MySQL):
123
145
  **MySQL.Generator.PROPERTIES_LOCATION,
124
146
  exp.UniqueKeyProperty: exp.Properties.Location.POST_SCHEMA,
125
147
  exp.PartitionByRangeProperty: exp.Properties.Location.POST_SCHEMA,
148
+ exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA,
126
149
  }
127
150
 
128
151
  CAST_MAPPING = {}
@@ -137,6 +160,7 @@ class Doris(MySQL):
137
160
  exp.ArrayAgg: rename_func("COLLECT_LIST"),
138
161
  exp.ArrayToString: rename_func("ARRAY_JOIN"),
139
162
  exp.ArrayUniqueAgg: rename_func("COLLECT_SET"),
163
+ exp.CurrentDate: lambda self, _: self.func("CURRENT_DATE"),
140
164
  exp.CurrentTimestamp: lambda self, _: self.func("NOW"),
141
165
  exp.DateTrunc: lambda self, e: self.func("DATE_TRUNC", e.this, unit_to_str(e)),
142
166
  exp.GroupConcat: lambda self, e: self.func(
@@ -683,3 +707,20 @@ class Doris(MySQL):
683
707
  # Handle both static and dynamic partition definitions
684
708
  create_sql = ", ".join(self.sql(e) for e in create_expressions)
685
709
  return f"PARTITION BY RANGE ({partition_expressions}) ({create_sql})"
710
+
711
+ def partitionedbyproperty_sql(self, expression: exp.PartitionedByProperty) -> str:
712
+ node = expression.this
713
+ if isinstance(node, exp.Schema):
714
+ parts = ", ".join(self.sql(e) for e in node.expressions)
715
+ return f"PARTITION BY ({parts})"
716
+ return f"PARTITION BY ({self.sql(node)})"
717
+
718
+ def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
719
+ """Override table_sql to avoid AS keyword in UPDATE and DELETE statements."""
720
+ ancestor = expression.find_ancestor(exp.Update, exp.Delete, exp.Select)
721
+ if not isinstance(ancestor, exp.Select):
722
+ sep = " "
723
+ return super().table_sql(expression, sep=sep)
724
+
725
+ def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
726
+ return super().alterrename_sql(expression, include_to=False)
@@ -42,30 +42,51 @@ class Dremio(Dialect):
42
42
  TIME_MAPPING = {
43
43
  # year
44
44
  "YYYY": "%Y",
45
+ "yyyy": "%Y",
45
46
  "YY": "%y",
47
+ "yy": "%y",
46
48
  # month / day
47
49
  "MM": "%m",
50
+ "mm": "%m",
48
51
  "MON": "%b",
52
+ "mon": "%b",
49
53
  "MONTH": "%B",
54
+ "month": "%B",
50
55
  "DDD": "%j",
56
+ "ddd": "%j",
51
57
  "DD": "%d",
58
+ "dd": "%d",
52
59
  "DY": "%a",
60
+ "dy": "%a",
53
61
  "DAY": "%A",
62
+ "day": "%A",
54
63
  # hours / minutes / seconds
55
64
  "HH24": "%H",
65
+ "hh24": "%H",
56
66
  "HH12": "%I",
57
- "HH": "%I", # 24- / 12-hour
67
+ "hh12": "%I",
68
+ "HH": "%I",
69
+ "hh": "%I", # 24- / 12-hour
58
70
  "MI": "%M",
71
+ "mi": "%M",
59
72
  "SS": "%S",
73
+ "ss": "%S",
60
74
  "FFF": "%f",
75
+ "fff": "%f",
61
76
  "AMPM": "%p",
77
+ "ampm": "%p",
62
78
  # ISO week / century etc.
63
79
  "WW": "%W",
80
+ "ww": "%W",
64
81
  "D": "%w",
82
+ "d": "%w",
65
83
  "CC": "%C",
84
+ "cc": "%C",
66
85
  # timezone
67
- "TZD": "%Z", # abbreviation (UTC, PST, ...)
68
- "TZO": "%z", # numeric offset (+0200)
86
+ "TZD": "%Z",
87
+ "tzd": "%Z", # abbreviation (UTC, PST, ...)
88
+ "TZO": "%z",
89
+ "tzo": "%z", # numeric offset (+0200)
69
90
  }
70
91
 
71
92
  class Parser(parser.Parser):
@@ -396,6 +396,7 @@ class DuckDB(Dialect):
396
396
 
397
397
  FUNCTIONS = {
398
398
  **parser.Parser.FUNCTIONS,
399
+ "ANY_VALUE": lambda args: exp.IgnoreNulls(this=exp.AnyValue.from_arg_list(args)),
399
400
  "ARRAY_REVERSE_SORT": _build_sort_array_desc,
400
401
  "ARRAY_SORT": exp.SortArray.from_arg_list,
401
402
  "DATEDIFF": _build_date_diff,
@@ -920,6 +921,7 @@ class DuckDB(Dialect):
920
921
  PROPERTIES_LOCATION[exp.LikeProperty] = exp.Properties.Location.POST_SCHEMA
921
922
  PROPERTIES_LOCATION[exp.TemporaryProperty] = exp.Properties.Location.POST_CREATE
922
923
  PROPERTIES_LOCATION[exp.ReturnsProperty] = exp.Properties.Location.POST_ALIAS
924
+ PROPERTIES_LOCATION[exp.SequenceProperties] = exp.Properties.Location.POST_EXPRESSION
923
925
 
924
926
  IGNORE_RESPECT_NULLS_WINDOW_FUNCTIONS = (
925
927
  exp.FirstValue,
@@ -1136,9 +1138,10 @@ class DuckDB(Dialect):
1136
1138
 
1137
1139
  # If BQ's UNNEST is aliased, we transform it from a column alias to a table alias in DDB
1138
1140
  alias = expression.args.get("alias")
1139
- if alias:
1141
+ if isinstance(alias, exp.TableAlias):
1140
1142
  expression.set("alias", None)
1141
- alias = exp.TableAlias(this=seq_get(alias.args.get("columns"), 0))
1143
+ if alias.columns:
1144
+ alias = exp.TableAlias(this=seq_get(alias.columns, 0))
1142
1145
 
1143
1146
  unnest_sql = super().unnest_sql(expression)
1144
1147
  select = exp.Select(expressions=[unnest_sql]).subquery(alias)
@@ -1152,7 +1155,9 @@ class DuckDB(Dialect):
1152
1155
  # window functions that accept it e.g. FIRST_VALUE(... IGNORE NULLS) OVER (...)
1153
1156
  return super().ignorenulls_sql(expression)
1154
1157
 
1155
- self.unsupported("IGNORE NULLS is not supported for non-window functions.")
1158
+ if not isinstance(expression.this, exp.AnyValue):
1159
+ self.unsupported("IGNORE NULLS is not supported for non-window functions.")
1160
+
1156
1161
  return self.sql(expression, "this")
1157
1162
 
1158
1163
  def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
@@ -1247,3 +1252,27 @@ class DuckDB(Dialect):
1247
1252
  return self.sql(exp.Subquery(this=exp.Select(expressions=[posexplode_sql])))
1248
1253
 
1249
1254
  return posexplode_sql
1255
+
1256
+ def addmonths_sql(self, expression: exp.AddMonths) -> str:
1257
+ this = expression.this
1258
+
1259
+ if not this.type:
1260
+ from sqlglot.optimizer.annotate_types import annotate_types
1261
+
1262
+ this = annotate_types(this, dialect=self.dialect)
1263
+
1264
+ if this.is_type(*exp.DataType.TEXT_TYPES):
1265
+ this = exp.Cast(this=this, to=exp.DataType(this=exp.DataType.Type.TIMESTAMP))
1266
+
1267
+ func = self.func(
1268
+ "DATE_ADD", this, exp.Interval(this=expression.expression, unit=exp.var("MONTH"))
1269
+ )
1270
+
1271
+ # DuckDB's DATE_ADD function returns TIMESTAMP/DATETIME by default, even when the input is DATE
1272
+ # To match for example Snowflake's ADD_MONTHS behavior (which preserves the input type)
1273
+ # We need to cast the result back to the original type when the input is DATE or TIMESTAMPTZ
1274
+ # Example: ADD_MONTHS('2023-01-31'::date, 1) should return DATE, not TIMESTAMP
1275
+ if this.is_type(exp.DataType.Type.DATE, exp.DataType.Type.TIMESTAMPTZ):
1276
+ return self.sql(exp.Cast(this=func, to=this.type))
1277
+
1278
+ return self.sql(func)
@@ -28,6 +28,16 @@ def _sha2_sql(self: Exasol.Generator, expression: exp.SHA2) -> str:
28
28
  return self.func(func_name, expression.this)
29
29
 
30
30
 
31
+ def _date_diff_sql(self: Exasol.Generator, expression: exp.DateDiff | exp.TsOrDsDiff) -> str:
32
+ unit = expression.text("unit").upper() or "DAY"
33
+
34
+ if unit not in DATE_UNITS:
35
+ self.unsupported(f"'{unit}' is not supported in Exasol.")
36
+ return self.function_fallback_sql(expression)
37
+
38
+ return self.func(f"{unit}S_BETWEEN", expression.this, expression.expression)
39
+
40
+
31
41
  # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/trunc%5Bate%5D%20(datetime).htm
32
42
  # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/trunc%5Bate%5D%20(number).htm
33
43
  def _build_trunc(args: t.List[exp.Expression], dialect: DialectType) -> exp.Expression:
@@ -59,6 +69,9 @@ def _build_nullifzero(args: t.List) -> exp.If:
59
69
  return exp.If(this=cond, true=exp.Null(), false=seq_get(args, 0))
60
70
 
61
71
 
72
+ DATE_UNITS = {"DAY", "WEEK", "MONTH", "YEAR", "HOUR", "MINUTE", "SECOND"}
73
+
74
+
62
75
  class Exasol(Dialect):
63
76
  TIME_MAPPING = {
64
77
  "yyyy": "%Y",
@@ -100,20 +113,14 @@ class Exasol(Dialect):
100
113
  class Parser(parser.Parser):
101
114
  FUNCTIONS = {
102
115
  **parser.Parser.FUNCTIONS,
103
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_days.htm
104
- "ADD_DAYS": build_date_delta(exp.DateAdd, default_unit="DAY"),
105
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_years.htm
106
- "ADD_YEARS": build_date_delta(exp.DateAdd, default_unit="YEAR"),
107
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_months.htm
108
- "ADD_MONTHS": build_date_delta(exp.DateAdd, default_unit="MONTH"),
109
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_weeks.htm
110
- "ADD_WEEKS": build_date_delta(exp.DateAdd, default_unit="WEEK"),
111
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_hour.htm
112
- "ADD_HOURS": build_date_delta(exp.DateAdd, default_unit="HOUR"),
113
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_minutes.htm
114
- "ADD_MINUTES": build_date_delta(exp.DateAdd, default_unit="MINUTE"),
115
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/add_seconds.htm
116
- "ADD_SECONDS": build_date_delta(exp.DateAdd, default_unit="SECOND"),
116
+ **{
117
+ f"ADD_{unit}S": build_date_delta(exp.DateAdd, default_unit=unit)
118
+ for unit in DATE_UNITS
119
+ },
120
+ **{
121
+ f"{unit}S_BETWEEN": build_date_delta(exp.DateDiff, default_unit=unit)
122
+ for unit in DATE_UNITS
123
+ },
117
124
  "BIT_AND": binary_from_function(exp.BitwiseAnd),
118
125
  "BIT_OR": binary_from_function(exp.BitwiseOr),
119
126
  "BIT_XOR": binary_from_function(exp.BitwiseXor),
@@ -196,16 +203,6 @@ class Exasol(Dialect):
196
203
  exp.DataType.Type.DATETIME: "TIMESTAMP",
197
204
  }
198
205
 
199
- DATE_ADD_FUNCTION_BY_UNIT = {
200
- "DAY": "ADD_DAYS",
201
- "WEEK": "ADD_WEEKS",
202
- "MONTH": "ADD_MONTHS",
203
- "YEAR": "ADD_YEARS",
204
- "HOUR": "ADD_HOURS",
205
- "MINUTE": "ADD_MINUTES",
206
- "SECOND": "ADD_SECONDS",
207
- }
208
-
209
206
  def datatype_sql(self, expression: exp.DataType) -> str:
210
207
  # Exasol supports a fixed default precision of 3 for TIMESTAMP WITH LOCAL TIME ZONE
211
208
  # and does not allow specifying a different custom precision
@@ -230,8 +227,8 @@ class Exasol(Dialect):
230
227
  exp.BitwiseRightShift: rename_func("BIT_RSHIFT"),
231
228
  # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/bit_xor.htm
232
229
  exp.BitwiseXor: rename_func("BIT_XOR"),
233
- # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/every.htm
234
- exp.All: rename_func("EVERY"),
230
+ exp.DateDiff: _date_diff_sql,
231
+ exp.TsOrDsDiff: _date_diff_sql,
235
232
  exp.DateTrunc: lambda self, e: self.func("TRUNC", e.this, unit_to_str(e)),
236
233
  exp.DatetimeTrunc: timestamptrunc_sql(),
237
234
  # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/edit_distance.htm#EDIT_DISTANCE
@@ -301,9 +298,8 @@ class Exasol(Dialect):
301
298
 
302
299
  def dateadd_sql(self, expression: exp.DateAdd) -> str:
303
300
  unit = expression.text("unit").upper() or "DAY"
304
- func_name = self.DATE_ADD_FUNCTION_BY_UNIT.get(unit)
305
- if not func_name:
301
+ if unit not in DATE_UNITS:
306
302
  self.unsupported(f"'{unit}' is not supported in Exasol.")
307
303
  return self.function_fallback_sql(expression)
308
304
 
309
- return self.func(func_name, expression.this, expression.expression)
305
+ return self.func(f"ADD_{unit}S", expression.this, expression.expression)
@@ -2,7 +2,7 @@ from sqlglot import TokenType
2
2
  import typing as t
3
3
 
4
4
  from sqlglot import exp
5
- from sqlglot.dialects.dialect import build_formatted_time
5
+ from sqlglot.dialects.dialect import build_formatted_time, rename_func
6
6
  from sqlglot.dialects.mysql import MySQL
7
7
  from sqlglot.generator import unsupported_args
8
8
  from sqlglot.helper import seq_get
@@ -64,6 +64,8 @@ class SingleStore(MySQL):
64
64
  ),
65
65
  format=MySQL.format_time(seq_get(args, 1)),
66
66
  ),
67
+ "UNIX_TIMESTAMP": exp.StrToUnix.from_arg_list,
68
+ "FROM_UNIXTIME": build_formatted_time(exp.UnixToTime, "mysql"),
67
69
  }
68
70
 
69
71
  CAST_COLUMN_OPERATORS = {TokenType.COLON_GT, TokenType.NCOLON_GT}
@@ -111,6 +113,31 @@ class SingleStore(MySQL):
111
113
  exp.TryCast: unsupported_args("format", "action", "default")(
112
114
  lambda self, e: f"{self.sql(e, 'this')} !:> {self.sql(e, 'to')}"
113
115
  ),
116
+ exp.StrToUnix: unsupported_args("format")(rename_func("UNIX_TIMESTAMP")),
117
+ exp.TimeToUnix: rename_func("UNIX_TIMESTAMP"),
118
+ exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"),
119
+ exp.UnixSeconds: rename_func("UNIX_TIMESTAMP"),
120
+ exp.UnixToStr: lambda self, e: self.func(
121
+ "FROM_UNIXTIME",
122
+ e.this,
123
+ self.format_time(
124
+ e,
125
+ inverse_time_mapping=MySQL.INVERSE_TIME_MAPPING,
126
+ inverse_time_trie=MySQL.INVERSE_TIME_TRIE,
127
+ ),
128
+ ),
129
+ exp.UnixToTime: unsupported_args("scale", "zone", "hours", "minutes")(
130
+ lambda self, e: self.func(
131
+ "FROM_UNIXTIME",
132
+ e.this,
133
+ self.format_time(
134
+ e,
135
+ inverse_time_mapping=MySQL.INVERSE_TIME_MAPPING,
136
+ inverse_time_trie=MySQL.INVERSE_TIME_TRIE,
137
+ ),
138
+ ),
139
+ ),
140
+ exp.UnixToTimeStr: lambda self, e: f"FROM_UNIXTIME({self.sql(e, 'this')}) :> TEXT",
114
141
  }
115
142
 
116
143
  # https://docs.singlestore.com/cloud/reference/sql-reference/restricted-keywords/list-of-restricted-keywords/
@@ -461,7 +461,7 @@ def _eliminate_dot_variant_lookup(expression: exp.Expression) -> exp.Expression:
461
461
  unnest_alias = unnest.args.get("alias")
462
462
  if (
463
463
  isinstance(unnest_alias, exp.TableAlias)
464
- and (unnest_alias.args.get("column_only") or not unnest_alias.this)
464
+ and not unnest_alias.this
465
465
  and len(unnest_alias.columns) == 1
466
466
  ):
467
467
  unnest_aliases.add(unnest_alias.columns[0].name)
sqlglot/dialects/spark.py CHANGED
@@ -194,6 +194,7 @@ class Spark(Spark2):
194
194
  move_partitioned_by_to_schema_columns,
195
195
  ]
196
196
  ),
197
+ exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
197
198
  exp.GroupConcat: _groupconcat_sql,
198
199
  exp.EndsWith: rename_func("ENDSWITH"),
199
200
  exp.PartitionedByProperty: lambda self,
@@ -90,6 +90,7 @@ class Teradata(Dialect):
90
90
  "HELP": TokenType.COMMAND,
91
91
  "INS": TokenType.INSERT,
92
92
  "LE": TokenType.LTE,
93
+ "LOCKING": TokenType.LOCK,
93
94
  "LT": TokenType.LT,
94
95
  "MINUS": TokenType.EXCEPT,
95
96
  "MOD": TokenType.MOD,
@@ -155,6 +156,26 @@ class Teradata(Dialect):
155
156
  exp.Use, this=self._parse_table(schema=False)
156
157
  ),
157
158
  TokenType.REPLACE: lambda self: self._parse_create(),
159
+ TokenType.LOCK: lambda self: self._parse_locking_statement(),
160
+ }
161
+
162
+ def _parse_locking_statement(self) -> exp.LockingStatement:
163
+ # Reuse exp.LockingProperty parsing for the lock kind, type etc
164
+ locking_property = self._parse_locking()
165
+ wrapped_query = self._parse_select()
166
+
167
+ if not wrapped_query:
168
+ self.raise_error("Expected SELECT statement after LOCKING clause")
169
+
170
+ return self.expression(
171
+ exp.LockingStatement,
172
+ this=locking_property,
173
+ expression=wrapped_query,
174
+ )
175
+
176
+ SET_PARSERS = {
177
+ **parser.Parser.SET_PARSERS,
178
+ "QUERY_BAND": lambda self: self._parse_query_band(),
158
179
  }
159
180
 
160
181
  FUNCTION_PARSERS = {
@@ -210,6 +231,36 @@ class Teradata(Dialect):
210
231
 
211
232
  return self.expression(exp.RangeN, this=this, expressions=expressions, each=each)
212
233
 
234
+ def _parse_query_band(self) -> exp.QueryBand:
235
+ # Parse: SET QUERY_BAND = 'key=value;key2=value2;' FOR SESSION|TRANSACTION
236
+ # Also supports: SET QUERY_BAND = 'key=value;' UPDATE FOR SESSION|TRANSACTION
237
+ # Also supports: SET QUERY_BAND = NONE FOR SESSION|TRANSACTION
238
+ self._match(TokenType.EQ)
239
+
240
+ # Handle both string literals and NONE keyword
241
+ if self._match_text_seq("NONE"):
242
+ query_band_string: t.Optional[exp.Expression] = exp.Var(this="NONE")
243
+ else:
244
+ query_band_string = self._parse_string()
245
+
246
+ update = self._match_text_seq("UPDATE")
247
+ self._match_text_seq("FOR")
248
+
249
+ # Handle scope - can be SESSION, TRANSACTION, VOLATILE, or SESSION VOLATILE
250
+ if self._match_text_seq("SESSION", "VOLATILE"):
251
+ scope = "SESSION VOLATILE"
252
+ elif self._match_texts(("SESSION", "TRANSACTION")):
253
+ scope = self._prev.text.upper()
254
+ else:
255
+ scope = None
256
+
257
+ return self.expression(
258
+ exp.QueryBand,
259
+ this=query_band_string,
260
+ scope=scope,
261
+ update=update,
262
+ )
263
+
213
264
  def _parse_index_params(self) -> exp.IndexParameters:
214
265
  this = super()._parse_index_params()
215
266
 
@@ -358,6 +409,13 @@ class Teradata(Dialect):
358
409
 
359
410
  return f"RANGE_N({this} BETWEEN {expressions_sql}{each_sql})"
360
411
 
412
+ def lockingstatement_sql(self, expression: exp.LockingStatement) -> str:
413
+ """Generate SQL for LOCKING statement"""
414
+ locking_clause = self.sql(expression, "this")
415
+ query_sql = self.sql(expression, "expression")
416
+
417
+ return f"{locking_clause} {query_sql}"
418
+
361
419
  def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
362
420
  kind = self.sql(expression, "kind").upper()
363
421
  if kind == "TABLE" and locations.get(exp.Properties.Location.POST_NAME):
sqlglot/expressions.py CHANGED
@@ -1458,6 +1458,11 @@ class DDL(Expression):
1458
1458
  return self.expression.named_selects if isinstance(self.expression, Query) else []
1459
1459
 
1460
1460
 
1461
+ # https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Manipulation-Language/Statement-Syntax/LOCKING-Request-Modifier/LOCKING-Request-Modifier-Syntax
1462
+ class LockingStatement(Expression):
1463
+ arg_types = {"this": True, "expression": True}
1464
+
1465
+
1461
1466
  class DML(Expression):
1462
1467
  def returning(
1463
1468
  self,
@@ -1613,6 +1618,10 @@ class SetItem(Expression):
1613
1618
  }
1614
1619
 
1615
1620
 
1621
+ class QueryBand(Expression):
1622
+ arg_types = {"this": True, "scope": False, "update": False}
1623
+
1624
+
1616
1625
  class Show(Expression):
1617
1626
  arg_types = {
1618
1627
  "this": True,
@@ -1680,7 +1689,7 @@ class ProjectionDef(Expression):
1680
1689
 
1681
1690
 
1682
1691
  class TableAlias(Expression):
1683
- arg_types = {"this": False, "columns": False, "column_only": False}
1692
+ arg_types = {"this": False, "columns": False}
1684
1693
 
1685
1694
  @property
1686
1695
  def columns(self):
@@ -5416,6 +5425,10 @@ class BitwiseCountAgg(AggFunc):
5416
5425
  _sql_names = ["BIT_COUNT"]
5417
5426
 
5418
5427
 
5428
+ class ByteLength(Func):
5429
+ pass
5430
+
5431
+
5419
5432
  class ArrayRemove(Func):
5420
5433
  arg_types = {"this": True, "expression": True}
5421
5434
 
@@ -5564,6 +5577,10 @@ class ConvertTimezone(Func):
5564
5577
  }
5565
5578
 
5566
5579
 
5580
+ class CodePointsToString(Func):
5581
+ pass
5582
+
5583
+
5567
5584
  class GenerateSeries(Func):
5568
5585
  arg_types = {"start": True, "end": True, "step": False, "is_end_exclusive": False}
5569
5586
 
@@ -5791,6 +5808,18 @@ class JSONCast(Cast):
5791
5808
  pass
5792
5809
 
5793
5810
 
5811
+ class JustifyDays(Func):
5812
+ pass
5813
+
5814
+
5815
+ class JustifyHours(Func):
5816
+ pass
5817
+
5818
+
5819
+ class JustifyInterval(Func):
5820
+ pass
5821
+
5822
+
5794
5823
  class Try(Func):
5795
5824
  pass
5796
5825
 
@@ -5947,6 +5976,10 @@ class DatetimeTrunc(Func, TimeUnit):
5947
5976
  arg_types = {"this": True, "unit": True, "zone": False}
5948
5977
 
5949
5978
 
5979
+ class DateFromUnixDate(Func):
5980
+ pass
5981
+
5982
+
5950
5983
  class DayOfWeek(Func):
5951
5984
  _sql_names = ["DAY_OF_WEEK", "DAYOFWEEK"]
5952
5985
 
@@ -6509,6 +6542,14 @@ class ParseJSON(Func):
6509
6542
  arg_types = {"this": True, "expression": False, "safe": False}
6510
6543
 
6511
6544
 
6545
+ class ParseTime(Func):
6546
+ arg_types = {"this": True, "format": True}
6547
+
6548
+
6549
+ class ParseDatetime(Func):
6550
+ arg_types = {"this": True, "format": False, "zone": False}
6551
+
6552
+
6512
6553
  class Least(Func):
6513
6554
  arg_types = {"this": True, "expressions": False}
6514
6555
  is_var_len_args = True
@@ -6522,6 +6563,10 @@ class Right(Func):
6522
6563
  arg_types = {"this": True, "expression": True}
6523
6564
 
6524
6565
 
6566
+ class Reverse(Func):
6567
+ pass
6568
+
6569
+
6525
6570
  class Length(Func):
6526
6571
  arg_types = {"this": True, "binary": False, "encoding": False}
6527
6572
  _sql_names = ["LENGTH", "LEN", "CHAR_LENGTH", "CHARACTER_LENGTH"]
@@ -7047,6 +7092,14 @@ class UnixSeconds(Func):
7047
7092
  pass
7048
7093
 
7049
7094
 
7095
+ class UnixMicros(Func):
7096
+ pass
7097
+
7098
+
7099
+ class UnixMillis(Func):
7100
+ pass
7101
+
7102
+
7050
7103
  class Uuid(Func):
7051
7104
  _sql_names = ["UUID", "GEN_RANDOM_UUID", "GENERATE_UUID", "UUID_STRING"]
7052
7105
 
@@ -7096,6 +7149,10 @@ class Week(Func):
7096
7149
  arg_types = {"this": True, "mode": False}
7097
7150
 
7098
7151
 
7152
+ class WeekStart(Expression):
7153
+ pass
7154
+
7155
+
7099
7156
  class XMLElement(Func):
7100
7157
  _sql_names = ["XMLELEMENT"]
7101
7158
  arg_types = {"this": True, "expressions": False}
sqlglot/generator.py CHANGED
@@ -219,6 +219,7 @@ class Generator(metaclass=_Generator):
219
219
  exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
220
220
  exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
221
221
  exp.VolatileProperty: lambda *_: "VOLATILE",
222
+ exp.WeekStart: lambda self, e: f"WEEK({self.sql(e, 'this')})",
222
223
  exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
223
224
  exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
224
225
  exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
@@ -2405,6 +2406,14 @@ class Generator(metaclass=_Generator):
2405
2406
  tag = " TAG" if expression.args.get("tag") else ""
2406
2407
  return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2407
2408
 
2409
+ def queryband_sql(self, expression: exp.QueryBand) -> str:
2410
+ this = self.sql(expression, "this")
2411
+ update = " UPDATE" if expression.args.get("update") else ""
2412
+ scope = self.sql(expression, "scope")
2413
+ scope = f" FOR {scope}" if scope else ""
2414
+
2415
+ return f"QUERY_BAND = {this}{update}{scope}"
2416
+
2408
2417
  def pragma_sql(self, expression: exp.Pragma) -> str:
2409
2418
  return f"PRAGMA {self.sql(expression, 'this')}"
2410
2419
 
@@ -3479,14 +3488,15 @@ class Generator(metaclass=_Generator):
3479
3488
  expressions = f"({expressions})" if expressions else ""
3480
3489
  return f"ALTER{compound} SORTKEY {this or expressions}"
3481
3490
 
3482
- def alterrename_sql(self, expression: exp.AlterRename) -> str:
3491
+ def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
3483
3492
  if not self.RENAME_TABLE_WITH_DB:
3484
3493
  # Remove db from tables
3485
3494
  expression = expression.transform(
3486
3495
  lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3487
3496
  ).assert_is(exp.AlterRename)
3488
3497
  this = self.sql(expression, "this")
3489
- return f"RENAME TO {this}"
3498
+ to_kw = " TO" if include_to else ""
3499
+ return f"RENAME{to_kw} {this}"
3490
3500
 
3491
3501
  def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3492
3502
  exists = " IF EXISTS" if expression.args.get("exists") else ""
@@ -5127,3 +5137,12 @@ class Generator(metaclass=_Generator):
5127
5137
  return self.sql(exp.Bracket(this=this, expressions=[expr]))
5128
5138
 
5129
5139
  return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))
5140
+
5141
+ def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
5142
+ return self.sql(
5143
+ exp.DateAdd(
5144
+ this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
5145
+ expression=expression.this,
5146
+ unit=exp.var("DAY"),
5147
+ )
5148
+ )
@@ -326,7 +326,10 @@ class TypeAnnotator(metaclass=_TypeAnnotator):
326
326
  struct_type = exp.DataType(
327
327
  this=exp.DataType.Type.STRUCT,
328
328
  expressions=[
329
- exp.ColumnDef(this=exp.to_identifier(select.output_name), kind=select.type)
329
+ exp.ColumnDef(
330
+ this=exp.to_identifier(select.output_name),
331
+ kind=select.type.copy() if select.type else None,
332
+ )
330
333
  for select in scope.expression.selects
331
334
  ],
332
335
  nested=True,
@@ -330,6 +330,10 @@ def _merge_expressions(outer_scope: Scope, inner_scope: Scope, alias: str) -> No
330
330
  if isinstance(column.parent, (exp.Unary, exp.Binary)) and must_wrap_expression:
331
331
  expression = exp.paren(expression, copy=False)
332
332
 
333
+ # make sure we do not accidentally change the name of the column
334
+ if isinstance(column.parent, exp.Select) and column.name != expression.name:
335
+ expression = exp.alias_(expression, column.name)
336
+
333
337
  column.replace(expression.copy())
334
338
 
335
339
 
@@ -128,14 +128,6 @@ def qualify_tables(
128
128
  table_alias = udtf.args.get("alias") or exp.TableAlias(
129
129
  this=exp.to_identifier(next_alias_name())
130
130
  )
131
- if (
132
- isinstance(udtf, exp.Unnest)
133
- and dialect.UNNEST_COLUMN_ONLY
134
- and not table_alias.columns
135
- ):
136
- table_alias.set("columns", [table_alias.this.copy()])
137
- table_alias.set("column_only", True)
138
-
139
131
  udtf.set("alias", table_alias)
140
132
 
141
133
  if not table_alias.name:
sqlglot/parser.py CHANGED
@@ -2084,7 +2084,24 @@ class Parser(metaclass=_Parser):
2084
2084
 
2085
2085
  if create_token.token_type == TokenType.SEQUENCE:
2086
2086
  expression = self._parse_types()
2087
- extend_props(self._parse_properties())
2087
+ props = self._parse_properties()
2088
+ if props:
2089
+ sequence_props = exp.SequenceProperties()
2090
+ options = []
2091
+ for prop in props:
2092
+ if isinstance(prop, exp.SequenceProperties):
2093
+ for arg, value in prop.args.items():
2094
+ if arg == "options":
2095
+ options.extend(value)
2096
+ else:
2097
+ sequence_props.set(arg, value)
2098
+ prop.pop()
2099
+
2100
+ if options:
2101
+ sequence_props.set("options", options)
2102
+
2103
+ props.append("expressions", sequence_props)
2104
+ extend_props(props)
2088
2105
  else:
2089
2106
  expression = self._parse_ddl_select()
2090
2107
 
@@ -2222,11 +2239,17 @@ class Parser(metaclass=_Parser):
2222
2239
  return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
2223
2240
 
2224
2241
  index = self._index
2242
+
2243
+ seq_props = self._parse_sequence_properties()
2244
+ if seq_props:
2245
+ return seq_props
2246
+
2247
+ self._retreat(index)
2225
2248
  key = self._parse_column()
2226
2249
 
2227
2250
  if not self._match(TokenType.EQ):
2228
2251
  self._retreat(index)
2229
- return self._parse_sequence_properties()
2252
+ return None
2230
2253
 
2231
2254
  # Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
2232
2255
  if isinstance(key, exp.Column):
@@ -3820,7 +3843,8 @@ class Parser(metaclass=_Parser):
3820
3843
  elif self._match(TokenType.USING):
3821
3844
  kwargs["using"] = self._parse_using_identifiers()
3822
3845
  elif (
3823
- not (outer_apply or cross_apply)
3846
+ not method
3847
+ and not (outer_apply or cross_apply)
3824
3848
  and not isinstance(kwargs["this"], exp.Unnest)
3825
3849
  and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
3826
3850
  ):
@@ -5255,7 +5279,7 @@ class Parser(metaclass=_Parser):
5255
5279
  while self._match(TokenType.DOT):
5256
5280
  type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
5257
5281
 
5258
- return exp.DataType.build(type_name, udt=True)
5282
+ return exp.DataType.build(type_name, dialect=self.dialect, udt=True)
5259
5283
 
5260
5284
  def _parse_types(
5261
5285
  self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
@@ -6556,7 +6580,7 @@ class Parser(metaclass=_Parser):
6556
6580
  elif not to:
6557
6581
  self.raise_error("Expected TYPE after CAST")
6558
6582
  elif isinstance(to, exp.Identifier):
6559
- to = exp.DataType.build(to.name, udt=True)
6583
+ to = exp.DataType.build(to.name, dialect=self.dialect, udt=True)
6560
6584
  elif to.this == exp.DataType.Type.CHAR:
6561
6585
  if self._match(TokenType.CHARACTER_SET):
6562
6586
  to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlglot
3
- Version: 27.6.0
3
+ Version: 27.7.0
4
4
  Summary: An easily customizable SQL parser and transpiler
5
5
  Author-email: Toby Mao <toby.mao@gmail.com>
6
6
  Project-URL: Homepage, https://sqlglot.com/
@@ -1,15 +1,15 @@
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=SV7KPP4heBRufVil8McVI2tIDBJQQilGKWsK6K0PWKg,513
4
+ sqlglot/_version.py,sha256=5PUg50O7n_si_pQEvZg7Hxhtah6hzWUX8K6MSHG_oxc,513
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=f_DU8WoCCqNxLFofmrU4p7tR3ckkzQF2eN10v4T956E,246275
8
- sqlglot/generator.py,sha256=Xfbpm-z9NdxZPWdaTeMRJ2ton0H_y5wfC6j3dwgouQQ,218351
7
+ sqlglot/expressions.py,sha256=Odk7emY-OtfIxyqDdhqgHM4m7AEvKGLo7nCDML2Y3Dw,247140
8
+ sqlglot/generator.py,sha256=hXFm8cL3QTVHrUKz0vNK0PnymYHWxHk4Gzy_4bepjxg,219142
9
9
  sqlglot/helper.py,sha256=9nZjFVRBtMKFC3EdzpDQ6jkazFO19po6BF8xHiNGZIo,15111
10
10
  sqlglot/jsonpath.py,sha256=jneO-A57n4ojVT2drCn2HBlx_Ka8wLcGpemW1JgvbjA,7666
11
11
  sqlglot/lineage.py,sha256=Qj5ykuDNcATppb9vOjoIKBqRVLbu3OMPiZk9f3iyv40,15312
12
- sqlglot/parser.py,sha256=FWLmqe0VfmZY623NGrEcNjM1sfNcWbFJt8beZOzUna8,327856
12
+ sqlglot/parser.py,sha256=Pt0uy-MgeTNYqwLMgiLhgB_hk2HlOJMFV1rDC-JrmlQ,328778
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
@@ -20,17 +20,17 @@ sqlglot/transforms.py,sha256=utNDsCBsA7hPUK3-aby3DDgiY_XVMAKQqeoLm1EyihI,41218
20
20
  sqlglot/trie.py,sha256=v27uXMrHfqrXlJ6GmeTSMovsB_3o0ctnlKhdNt7W6fI,2245
21
21
  sqlglot/dialects/__init__.py,sha256=BQUv9EuMmvhP_wVitGLo0PlCi15atvfXgvREpsTsxeQ,3799
22
22
  sqlglot/dialects/athena.py,sha256=ofArmayYLev4qZQ15GM8mevG04qqR5WGFb2ZcuYm6x4,10966
23
- sqlglot/dialects/bigquery.py,sha256=VeWS0UYOHKT9IaWXxKOU9A5cBIEBGrpNiUJ2VutU4Mo,56697
24
- sqlglot/dialects/clickhouse.py,sha256=UY1hFC83RMO2bum1UFfGBey_wmKPBKlsWD5nxbrqeyg,57000
23
+ sqlglot/dialects/bigquery.py,sha256=d9RdAVLTIgHaOYi6INMNQL38S_zagdl9vNbIL_Ds21c,58896
24
+ sqlglot/dialects/clickhouse.py,sha256=ygkVXh8fAxQ2sPvsDIP3y-1bUxV2PSLeS-983MI1uKs,57780
25
25
  sqlglot/dialects/databricks.py,sha256=mJN2lFpqgH95x3mtry3qWbuRf4q7NV5jbRAOspqclzY,4548
26
- sqlglot/dialects/dialect.py,sha256=O8fYmv1iFEfmXa1mUwEZ7GgPsfG51_VuHSv8E_zOw0k,71039
27
- sqlglot/dialects/doris.py,sha256=g6KGYu01nUsvz_wjZkZbI0tiPg-ZUKyr7a-yGaw5Kec,19899
28
- sqlglot/dialects/dremio.py,sha256=FbBTri3KPD4oJMKV9Qdnh7_EgLJUZZB8iDzUlDE26h4,4077
26
+ sqlglot/dialects/dialect.py,sha256=g6gUxzNFXJYFDYkDJY314pup63-2OvGek2kdufEn59g,71267
27
+ sqlglot/dialects/doris.py,sha256=HT3NyPORz8E1Kk6nNT00CEe4cwz1CfXGhhBcg8Hht1s,21709
28
+ sqlglot/dialects/dremio.py,sha256=vOVu-L9QYLMV8gkbY6vfDh_iffhw3D58IKk0yqvd0Lo,4512
29
29
  sqlglot/dialects/drill.py,sha256=FOh7_KjPx_77pv0DiHKZog0CcmzqeF9_PEmGnJ1ESSM,5825
30
30
  sqlglot/dialects/druid.py,sha256=kh3snZtneehNOWqs3XcPjsrhNaRbkCQ8E4hHbWJ1fHM,690
31
- sqlglot/dialects/duckdb.py,sha256=X4nY2ZjUSGZZ3pCqUraGxIYXVA-gzB134qV0TeyMmqQ,51418
31
+ sqlglot/dialects/duckdb.py,sha256=kZ1LkGEtztkYwxh8Oad2GHUpmeM226h_uxSoCQ114Bo,52892
32
32
  sqlglot/dialects/dune.py,sha256=gALut-fFfN2qMsr8LvZ1NQK3F3W9z2f4PwMvTMXVVVg,375
33
- sqlglot/dialects/exasol.py,sha256=94t8ijRnfghe4EEDehaA7Nk73aMUK6Q38NcKmITLKd4,15250
33
+ sqlglot/dialects/exasol.py,sha256=w73rFG7jQVkBm2plWR8I12LMkHQVPofsEgWRf6VmiI8,14269
34
34
  sqlglot/dialects/fabric.py,sha256=4Sng2ZhQSaf6eK3ituR9DqDZERaVwYS_UfdpusjsISg,10220
35
35
  sqlglot/dialects/hive.py,sha256=bAZz0qnaOH9f5FyIMkqBu3XB2Cj7y-xnCPbxPsk8U9I,31959
36
36
  sqlglot/dialects/materialize.py,sha256=LD2q1kTRrCwkIu1BfoBvnjTGbupDtoQ8JQMDCIYAXHg,3533
@@ -41,14 +41,14 @@ sqlglot/dialects/presto.py,sha256=Tm3Bx9AJilT1xlgunTpF0wUhIZBOPS-rB5Iwitnygxc,33
41
41
  sqlglot/dialects/prql.py,sha256=fwN-SPEGx-drwf1K0U2MByN-PkW3C_rOgQ3xeJeychg,7908
42
42
  sqlglot/dialects/redshift.py,sha256=MXI9W7CgKCtMNjNRPcZPxO8NBA9_PxZx14HB52o-aUc,15822
43
43
  sqlglot/dialects/risingwave.py,sha256=BqWwW1iT_OIVMwfRamaww79snnBwIgCfr22Go-ggO68,3289
44
- sqlglot/dialects/singlestore.py,sha256=w-fIrSyt_2r5SezGl7gUMyvvNmG4v2mBLTHLMhmlgD0,30065
45
- sqlglot/dialects/snowflake.py,sha256=cwSeyUdqwP4FmkNzqqc1DGmYaIqTAxecKJudJYII654,70584
46
- sqlglot/dialects/spark.py,sha256=hTumyd46Cc3HEl9KvlTla2eq_NKBI3w5Jis3FeMt_R8,8886
44
+ sqlglot/dialects/singlestore.py,sha256=dJp28vf7e9j87U7wnJF7cmXul9sypY1dU0vjZx63Fkc,31363
45
+ sqlglot/dialects/snowflake.py,sha256=XPsqYGBjn3dfddF2dcuM7Ur-4sYdthjW7cSPnptWq_s,70542
46
+ sqlglot/dialects/spark.py,sha256=qVZ96NqRnj2YhphWoJhRJTcwmrmNfXD2g442tL3MTrs,8956
47
47
  sqlglot/dialects/spark2.py,sha256=aCwPqLduLRSUSPtbI1VtBjydK6haKgEy3iahmueGRo4,14742
48
48
  sqlglot/dialects/sqlite.py,sha256=XIDmiNTswWcrDwlFm8gOODCrJ_rPmXQKkm9U_-YAlVs,13183
49
49
  sqlglot/dialects/starrocks.py,sha256=2gav0PSNgRdAGXzawdznZliBpglJoQ0wBxPI7ZIMsRw,11314
50
50
  sqlglot/dialects/tableau.py,sha256=oIawDzUITxGCWaEMB8OaNMPWhbC3U-2y09pYPm4eazc,2190
51
- sqlglot/dialects/teradata.py,sha256=qdV3wqh5NOOjGph5rj5qBXBSNDvCm4ByP0sonmurklc,15873
51
+ sqlglot/dialects/teradata.py,sha256=7LxCcRwP0Idd_OnCzA57NCdheVjHcKC2aFAKG5N49IU,18202
52
52
  sqlglot/dialects/trino.py,sha256=wgLsiX1NQvjGny_rgrU1e2r6kK1LD0KgaSdIDrYmjD0,4285
53
53
  sqlglot/dialects/tsql.py,sha256=sz1t79iCcsMXw8PKYnKldZJbAQ2iobNsqLPVAj-QTXk,54036
54
54
  sqlglot/executor/__init__.py,sha256=FslewzYQtQdDNg_0Ju2UaiP4vo4IMUgkfkmFsYUhcN0,2958
@@ -57,13 +57,13 @@ sqlglot/executor/env.py,sha256=tQhU5PpTBMcxgZIFddFqxWMNPtHN0vOOz72voncY3KY,8276
57
57
  sqlglot/executor/python.py,sha256=09GYRzrPn3lZGfDJY9pbONOvmYxsRyeSWjUiqkSRHGo,16661
58
58
  sqlglot/executor/table.py,sha256=xkuJlgLVNYUXsSUaX0zTcnFekldXLLU8LqDyjR5K9wY,4419
59
59
  sqlglot/optimizer/__init__.py,sha256=FdAvVz6rQLLkiiH21-SD4RxB5zS3WDeU-s03PZkJ-F4,343
60
- sqlglot/optimizer/annotate_types.py,sha256=mEhUmD6CBZ1HIgYJMvfMTIivoB-6nKHiDAvoktobocM,25025
60
+ sqlglot/optimizer/annotate_types.py,sha256=MfxXNzYgxaqhv1yBmzuigWjc1oIw1ikZ_lXjcdT3RDc,25128
61
61
  sqlglot/optimizer/canonicalize.py,sha256=RJpUbWDudjknRMtO_Kf8MGZ5Hv1twpPWac2u5kpV4Vw,7719
62
62
  sqlglot/optimizer/eliminate_ctes.py,sha256=fUBM0RUnPrm2sYptEWBux98B7fcx7W-BM1zVqfgDz9c,1448
63
63
  sqlglot/optimizer/eliminate_joins.py,sha256=2iYtG93aJGxvURqm1BVPosrnnnQ_IXI14RcD4pM8eHc,5942
64
64
  sqlglot/optimizer/eliminate_subqueries.py,sha256=sAB_Pk94_n2n1PIaZ2Mc3M-n2TV-JmjjaomaY14u0Og,6292
65
65
  sqlglot/optimizer/isolate_table_selects.py,sha256=_8rIKVMoL7eY3rrJsmgIdTRvfmBSLUxeHg42q1JW990,1464
66
- sqlglot/optimizer/merge_subqueries.py,sha256=lg6Is78nUM2MbqbRjE6xapgErIO-5pteBE74Qh3z4Zk,15211
66
+ sqlglot/optimizer/merge_subqueries.py,sha256=-4C80Hob7gqJDHkt3IeH0oExmqPuU9RGB7JC_hlqr7s,15443
67
67
  sqlglot/optimizer/normalize.py,sha256=wu3GeKY36PLyAb9f534jDDfzDwvZJpZ8g_H5QH6acZQ,6667
68
68
  sqlglot/optimizer/normalize_identifiers.py,sha256=uD4xICJAgj0X7EFc2LYcDWxAW2aTHANO2wy7kfn9gfY,2098
69
69
  sqlglot/optimizer/optimize_joins.py,sha256=tfEnTqBofveBXNKJ30GIvm2lyagAuD24bMNfu3iQi_k,3043
@@ -72,12 +72,12 @@ sqlglot/optimizer/pushdown_predicates.py,sha256=HGjs3Z4V3-X2d1VTfWhyByY3aL5SmKnV
72
72
  sqlglot/optimizer/pushdown_projections.py,sha256=7NoK5NAUVYVhs0YnYyo6WuXfaO-BShSwS6lA8Y-ATQ4,6668
73
73
  sqlglot/optimizer/qualify.py,sha256=oAPfwub7dEkrlCrsptcJWpLya4BgKhN6M5SwIs_86LY,4002
74
74
  sqlglot/optimizer/qualify_columns.py,sha256=hOfhyczK9zBbUuysKdOK36PJ44nCzzw9BivJh8U5RBI,43921
75
- sqlglot/optimizer/qualify_tables.py,sha256=rRo0rXMMDAloG_ut7nGPtIO3e__ooM2PqShxWECKQbo,6965
75
+ sqlglot/optimizer/qualify_tables.py,sha256=dA4ZazL7ShQh2JgBwpHuG-4c5lBw1TNzCnuN7m0iVTA,6645
76
76
  sqlglot/optimizer/scope.py,sha256=T6iVYnYwubt-WB1BOFsFYdJ-D7WtWZGL37SuCRQK23s,31154
77
77
  sqlglot/optimizer/simplify.py,sha256=-_yus42OYwqjQ9a2TSGhtG2G0pSkInUry1z7hEMz2pY,51062
78
78
  sqlglot/optimizer/unnest_subqueries.py,sha256=kzWUVDlxs8z9nmRx-8U-pHXPtVZhEIwkKqmKhr2QLvc,10908
79
- sqlglot-27.6.0.dist-info/licenses/LICENSE,sha256=p1Yk0B4oa0l8Rh-_dYyy75d8spjPd_vTloXfz4FWxys,1065
80
- sqlglot-27.6.0.dist-info/METADATA,sha256=hd368OeN534Qwf2WjbjUncYYPp250IifRKnJDBNnV4A,19437
81
- sqlglot-27.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
- sqlglot-27.6.0.dist-info/top_level.txt,sha256=5kRskCGA_gVADF9rSfSzPdLHXqvfMusDYeHePfNY2nQ,8
83
- sqlglot-27.6.0.dist-info/RECORD,,
79
+ sqlglot-27.7.0.dist-info/licenses/LICENSE,sha256=p1Yk0B4oa0l8Rh-_dYyy75d8spjPd_vTloXfz4FWxys,1065
80
+ sqlglot-27.7.0.dist-info/METADATA,sha256=hzW8lPG_bNI-kTX-iNRWj2ntlxyQRdHI-O3vUQU0_64,19437
81
+ sqlglot-27.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
+ sqlglot-27.7.0.dist-info/top_level.txt,sha256=5kRskCGA_gVADF9rSfSzPdLHXqvfMusDYeHePfNY2nQ,8
83
+ sqlglot-27.7.0.dist-info/RECORD,,