sqlglot 26.29.0__py3-none-any.whl → 26.30.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 = '26.29.0'
21
- __version_tuple__ = version_tuple = (26, 29, 0)
20
+ __version__ = version = '26.30.0'
21
+ __version_tuple__ = version_tuple = (26, 30, 0)
@@ -74,6 +74,7 @@ DIALECTS = [
74
74
  "Druid",
75
75
  "DuckDB",
76
76
  "Dune",
77
+ "Fabric",
77
78
  "Hive",
78
79
  "Materialize",
79
80
  "MySQL",
@@ -524,6 +524,7 @@ class BigQuery(Dialect):
524
524
  PREFIXED_PIVOT_COLUMNS = True
525
525
  LOG_DEFAULTS_TO_LN = True
526
526
  SUPPORTS_IMPLICIT_UNNEST = True
527
+ JOINS_HAVE_EQUAL_PRECEDENCE = True
527
528
 
528
529
  # BigQuery does not allow ASC/DESC to be used as an identifier
529
530
  ID_VAR_TOKENS = parser.Parser.ID_VAR_TOKENS - {TokenType.ASC, TokenType.DESC}
@@ -297,6 +297,7 @@ class ClickHouse(Dialect):
297
297
  MODIFIERS_ATTACHED_TO_SET_OP = False
298
298
  INTERVAL_SPANS = False
299
299
  OPTIONAL_ALIAS_TOKEN_CTE = False
300
+ JOINS_HAVE_EQUAL_PRECEDENCE = True
300
301
 
301
302
  FUNCTIONS = {
302
303
  **parser.Parser.FUNCTIONS,
@@ -691,6 +692,7 @@ class ClickHouse(Dialect):
691
692
  parse_bracket: bool = False,
692
693
  is_db_reference: bool = False,
693
694
  parse_partition: bool = False,
695
+ consume_pipe: bool = False,
694
696
  ) -> t.Optional[exp.Expression]:
695
697
  this = super()._parse_table(
696
698
  schema=schema,
@@ -77,6 +77,7 @@ class Dialects(str, Enum):
77
77
  DRUID = "druid"
78
78
  DUCKDB = "duckdb"
79
79
  DUNE = "dune"
80
+ FABRIC = "fabric"
80
81
  HIVE = "hive"
81
82
  MATERIALIZE = "materialize"
82
83
  MYSQL = "mysql"
@@ -1906,12 +1907,19 @@ def groupconcat_sql(
1906
1907
 
1907
1908
  def build_timetostr_or_tochar(args: t.List, dialect: Dialect) -> exp.TimeToStr | exp.ToChar:
1908
1909
  this = seq_get(args, 0)
1910
+ format = seq_get(args, 1)
1909
1911
 
1910
- if this and not this.type:
1911
- from sqlglot.optimizer.annotate_types import annotate_types
1912
+ if this:
1913
+ if not this.type:
1914
+ from sqlglot.optimizer.annotate_types import annotate_types
1915
+
1916
+ annotate_types(this, dialect=dialect)
1912
1917
 
1913
- annotate_types(this, dialect=dialect)
1914
- if this.is_type(*exp.DataType.TEMPORAL_TYPES):
1918
+ from sqlglot.dialects import Snowflake
1919
+
1920
+ if this.is_type(*exp.DataType.TEMPORAL_TYPES) or (
1921
+ isinstance(format, exp.Literal) and format.name in Snowflake.TIME_MAPPING
1922
+ ):
1915
1923
  dialect_name = dialect.__class__.__name__.lower()
1916
1924
  return build_formatted_time(exp.TimeToStr, dialect_name, default=True)(args)
1917
1925
 
@@ -508,6 +508,7 @@ class DuckDB(Dialect):
508
508
  parse_bracket: bool = False,
509
509
  is_db_reference: bool = False,
510
510
  parse_partition: bool = False,
511
+ consume_pipe: bool = False,
511
512
  ) -> t.Optional[exp.Expression]:
512
513
  # DuckDB supports prefix aliases, e.g. FROM foo: bar
513
514
  if self._next and self._next.token_type == TokenType.COLON:
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ from sqlglot import exp
4
+ from sqlglot.dialects.dialect import NormalizationStrategy
5
+ from sqlglot.dialects.tsql import TSQL
6
+
7
+
8
+ class Fabric(TSQL):
9
+ """
10
+ Microsoft Fabric Data Warehouse dialect that inherits from T-SQL.
11
+
12
+ Microsoft Fabric is a cloud-based analytics platform that provides a unified
13
+ data warehouse experience. While it shares much of T-SQL's syntax, it has
14
+ specific differences and limitations that this dialect addresses.
15
+
16
+ Key differences from T-SQL:
17
+ - Case-sensitive identifiers (unlike T-SQL which is case-insensitive)
18
+ - Limited data type support with mappings to supported alternatives
19
+ - Temporal types (DATETIME2, DATETIMEOFFSET, TIME) limited to 6 digits precision
20
+ - Certain legacy types (MONEY, SMALLMONEY, etc.) are not supported
21
+ - Unicode types (NCHAR, NVARCHAR) are mapped to non-unicode equivalents
22
+
23
+ References:
24
+ - Data Types: https://learn.microsoft.com/en-us/fabric/data-warehouse/data-types
25
+ - T-SQL Surface Area: https://learn.microsoft.com/en-us/fabric/data-warehouse/tsql-surface-area
26
+ """
27
+
28
+ # Fabric is case-sensitive unlike T-SQL which is case-insensitive
29
+ NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_SENSITIVE
30
+
31
+ class Generator(TSQL.Generator):
32
+ # Fabric-specific type mappings - override T-SQL types that aren't supported
33
+ # Reference: https://learn.microsoft.com/en-us/fabric/data-warehouse/data-types
34
+ TYPE_MAPPING = {
35
+ **TSQL.Generator.TYPE_MAPPING,
36
+ # Fabric doesn't support these types, map to alternatives
37
+ 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
+ exp.DataType.Type.NCHAR: "CHAR",
42
+ exp.DataType.Type.NVARCHAR: "VARCHAR",
43
+ exp.DataType.Type.TEXT: "VARCHAR(MAX)",
44
+ exp.DataType.Type.IMAGE: "VARBINARY",
45
+ exp.DataType.Type.TINYINT: "SMALLINT",
46
+ exp.DataType.Type.UTINYINT: "SMALLINT", # T-SQL parses TINYINT as UTINYINT
47
+ exp.DataType.Type.JSON: "VARCHAR",
48
+ 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
+ }
55
+
56
+ def datatype_sql(self, expression: exp.DataType) -> str:
57
+ """
58
+ Override datatype generation to handle Fabric-specific precision limitations.
59
+
60
+ Fabric limits temporal types (TIME, DATETIME2, DATETIMEOFFSET) to max 6 digits precision.
61
+ When no precision is specified, we default to 6 digits.
62
+ """
63
+ if expression.is_type(
64
+ exp.DataType.Type.TIME,
65
+ exp.DataType.Type.DATETIME2,
66
+ exp.DataType.Type.TIMESTAMPTZ, # DATETIMEOFFSET in Fabric
67
+ ):
68
+ # Get the current precision (first expression if it exists)
69
+ precision = expression.find(exp.DataTypeParam)
70
+
71
+ # Determine the target precision
72
+ if precision is None:
73
+ # No precision specified, default to 6
74
+ target_precision = 6
75
+ elif precision.this.is_int:
76
+ # Cap precision at 6
77
+ current_precision = precision.this.to_py()
78
+ target_precision = min(current_precision, 6)
79
+
80
+ # Create a new expression with the target precision
81
+ new_expression = exp.DataType(
82
+ this=expression.this,
83
+ expressions=[exp.DataTypeParam(this=exp.Literal.number(target_precision))],
84
+ )
85
+
86
+ return super().datatype_sql(new_expression)
87
+
88
+ return super().datatype_sql(expression)
sqlglot/dialects/hive.py CHANGED
@@ -305,6 +305,7 @@ class Hive(Dialect):
305
305
  LOG_DEFAULTS_TO_LN = True
306
306
  STRICT_CAST = False
307
307
  VALUES_FOLLOWED_BY_PAREN = False
308
+ JOINS_HAVE_EQUAL_PRECEDENCE = True
308
309
 
309
310
  FUNCTIONS = {
310
311
  **parser.Parser.FUNCTIONS,
@@ -128,6 +128,7 @@ class Oracle(Dialect):
128
128
  "NEXT": lambda self: self._parse_next_value_for(),
129
129
  "PRIOR": lambda self: self.expression(exp.Prior, this=self._parse_bitwise()),
130
130
  "SYSDATE": lambda self: self.expression(exp.CurrentTimestamp, sysdate=True),
131
+ "DBMS_RANDOM": lambda self: self._parse_dbms_random(),
131
132
  }
132
133
 
133
134
  FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
@@ -177,6 +178,19 @@ class Oracle(Dialect):
177
178
  ),
178
179
  }
179
180
 
181
+ def _parse_dbms_random(self) -> t.Optional[exp.Expression]:
182
+ if self._match_text_seq(".", "VALUE"):
183
+ lower, upper = None, None
184
+ if self._match(TokenType.L_PAREN, advance=False):
185
+ lower_upper = self._parse_wrapped_csv(self._parse_bitwise)
186
+ if len(lower_upper) == 2:
187
+ lower, upper = lower_upper
188
+
189
+ return exp.Rand(lower=lower, upper=upper)
190
+
191
+ self._retreat(self._index - 1)
192
+ return None
193
+
180
194
  def _parse_json_array(self, expr_type: t.Type[E], **kwargs) -> E:
181
195
  return self.expression(
182
196
  expr_type,
@@ -299,6 +313,7 @@ class Oracle(Dialect):
299
313
  exp.LogicalOr: rename_func("MAX"),
300
314
  exp.LogicalAnd: rename_func("MIN"),
301
315
  exp.Mod: rename_func("MOD"),
316
+ exp.Rand: rename_func("DBMS_RANDOM.VALUE"),
302
317
  exp.Select: transforms.preprocess(
303
318
  [
304
319
  transforms.eliminate_distinct_on,
@@ -315,6 +315,7 @@ class Presto(Dialect):
315
315
 
316
316
  class Parser(parser.Parser):
317
317
  VALUES_FOLLOWED_BY_PAREN = False
318
+ ZONE_AWARE_TIMESTAMP_CONSTRUCTOR = True
318
319
 
319
320
  FUNCTIONS = {
320
321
  **parser.Parser.FUNCTIONS,
sqlglot/dialects/prql.py CHANGED
@@ -189,11 +189,15 @@ class PRQL(Dialect):
189
189
  parse_bracket: bool = False,
190
190
  is_db_reference: bool = False,
191
191
  parse_partition: bool = False,
192
+ consume_pipe: bool = False,
192
193
  ) -> t.Optional[exp.Expression]:
193
194
  return self._parse_table_parts()
194
195
 
195
196
  def _parse_from(
196
- self, joins: bool = False, skip_from_token: bool = False
197
+ self,
198
+ joins: bool = False,
199
+ skip_from_token: bool = False,
200
+ consume_pipe: bool = False,
197
201
  ) -> t.Optional[exp.From]:
198
202
  if not skip_from_token and not self._match(TokenType.FROM):
199
203
  return None
@@ -90,6 +90,7 @@ class Redshift(Postgres):
90
90
  parse_bracket: bool = False,
91
91
  is_db_reference: bool = False,
92
92
  parse_partition: bool = False,
93
+ consume_pipe: bool = False,
93
94
  ) -> t.Optional[exp.Expression]:
94
95
  # Redshift supports UNPIVOTing SUPER objects, e.g. `UNPIVOT foo.obj[0] AS val AT attr`
95
96
  unpivot = self._match(TokenType.UNPIVOT)
@@ -799,6 +799,7 @@ class Snowflake(Dialect):
799
799
  parse_bracket: bool = False,
800
800
  is_db_reference: bool = False,
801
801
  parse_partition: bool = False,
802
+ consume_pipe: bool = False,
802
803
  ) -> t.Optional[exp.Expression]:
803
804
  table = super()._parse_table(
804
805
  schema=schema,
sqlglot/dialects/spark.py CHANGED
@@ -7,6 +7,7 @@ from sqlglot.dialects.dialect import rename_func, unit_to_var, timestampdiff_sql
7
7
  from sqlglot.dialects.hive import _build_with_ignore_nulls
8
8
  from sqlglot.dialects.spark2 import Spark2, temporary_storage_provider, _build_as_cast
9
9
  from sqlglot.helper import ensure_list, seq_get
10
+ from sqlglot.tokens import TokenType
10
11
  from sqlglot.transforms import (
11
12
  ctas_with_tmp_tables_to_create_tmp_view,
12
13
  remove_unique_constraints,
@@ -121,6 +122,16 @@ class Spark(Spark2):
121
122
  ),
122
123
  }
123
124
 
125
+ PLACEHOLDER_PARSERS = {
126
+ **Spark2.Parser.PLACEHOLDER_PARSERS,
127
+ TokenType.L_BRACE: lambda self: self._parse_query_parameter(),
128
+ }
129
+
130
+ def _parse_query_parameter(self) -> t.Optional[exp.Expression]:
131
+ this = self._parse_id_var()
132
+ self._match(TokenType.R_BRACE)
133
+ return self.expression(exp.Placeholder, this=this, widget=True)
134
+
124
135
  def _parse_generated_as_identity(
125
136
  self,
126
137
  ) -> (
@@ -200,3 +211,9 @@ class Spark(Spark2):
200
211
  return self.func("DATEDIFF", unit_to_var(expression), start, end)
201
212
 
202
213
  return self.func("DATEDIFF", end, start)
214
+
215
+ def placeholder_sql(self, expression: exp.Placeholder) -> str:
216
+ if not expression.args.get("widget"):
217
+ return super().placeholder_sql(expression)
218
+
219
+ return f"{{{expression.name}}}"
@@ -102,6 +102,10 @@ class SQLite(Dialect):
102
102
  COMMANDS = {*tokens.Tokenizer.COMMANDS, TokenType.REPLACE}
103
103
 
104
104
  class Parser(parser.Parser):
105
+ STRING_ALIASES = True
106
+ ALTER_RENAME_REQUIRES_COLUMN = False
107
+ JOINS_HAVE_EQUAL_PRECEDENCE = True
108
+
105
109
  FUNCTIONS = {
106
110
  **parser.Parser.FUNCTIONS,
107
111
  "EDITDIST3": exp.Levenshtein.from_arg_list,
@@ -110,9 +114,6 @@ class SQLite(Dialect):
110
114
  "TIME": lambda args: exp.Anonymous(this="TIME", expressions=args),
111
115
  }
112
116
 
113
- STRING_ALIASES = True
114
- ALTER_RENAME_REQUIRES_COLUMN = False
115
-
116
117
  def _parse_unique(self) -> exp.UniqueColumnConstraint:
117
118
  # Do not consume more tokens if UNIQUE is used as a standalone constraint, e.g:
118
119
  # CREATE TABLE foo (bar TEXT UNIQUE REFERENCES baz ...)
sqlglot/expressions.py CHANGED
@@ -4453,8 +4453,9 @@ class SessionParameter(Condition):
4453
4453
  arg_types = {"this": True, "kind": False}
4454
4454
 
4455
4455
 
4456
+ # https://www.databricks.com/blog/parameterized-queries-pyspark
4456
4457
  class Placeholder(Condition):
4457
- arg_types = {"this": False, "kind": False}
4458
+ arg_types = {"this": False, "kind": False, "widget": False}
4458
4459
 
4459
4460
  @property
4460
4461
  def name(self) -> str:
sqlglot/parser.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import re
4
5
  import typing as t
5
6
  import itertools
6
7
  from collections import defaultdict
@@ -23,6 +24,9 @@ logger = logging.getLogger("sqlglot")
23
24
 
24
25
  OPTIONS_TYPE = t.Dict[str, t.Sequence[t.Union[t.Sequence[str], str]]]
25
26
 
27
+ # Used to detect alphabetical characters and +/- in timestamp literals
28
+ TIME_ZONE_RE: t.Pattern[str] = re.compile(r":.*?[a-zA-Z\+\-]")
29
+
26
30
 
27
31
  def build_var_map(args: t.List) -> exp.StarMap | exp.VarMap:
28
32
  if len(args) == 1 and args[0].is_star:
@@ -935,7 +939,6 @@ class Parser(metaclass=_Parser):
935
939
  "AS": lambda self, query: self._build_pipe_cte(
936
940
  query, [exp.Star()], self._parse_table_alias()
937
941
  ),
938
- "DROP": lambda self, query: self._parse_pipe_syntax_drop(query),
939
942
  "EXTEND": lambda self, query: self._parse_pipe_syntax_extend(query),
940
943
  "LIMIT": lambda self, query: self._parse_pipe_syntax_limit(query),
941
944
  "ORDER BY": lambda self, query: query.order_by(
@@ -943,7 +946,6 @@ class Parser(metaclass=_Parser):
943
946
  ),
944
947
  "PIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
945
948
  "SELECT": lambda self, query: self._parse_pipe_syntax_select(query),
946
- "SET": lambda self, query: self._parse_pipe_syntax_set(query),
947
949
  "TABLESAMPLE": lambda self, query: self._parse_pipe_syntax_tablesample(query),
948
950
  "UNPIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
949
951
  "WHERE": lambda self, query: query.where(self._parse_where(), copy=False),
@@ -1518,6 +1520,15 @@ class Parser(metaclass=_Parser):
1518
1520
  # Whether renaming a column with an ALTER statement requires the presence of the COLUMN keyword
1519
1521
  ALTER_RENAME_REQUIRES_COLUMN = True
1520
1522
 
1523
+ # Whether all join types have the same precedence, i.e., they "naturally" produce a left-deep tree.
1524
+ # In standard SQL, joins that use the JOIN keyword take higher precedence than comma-joins. That is
1525
+ # to say, JOIN operators happen before comma operators. This is not the case in some dialects, such
1526
+ # as BigQuery, where all joins have the same precedence.
1527
+ JOINS_HAVE_EQUAL_PRECEDENCE = False
1528
+
1529
+ # Whether TIMESTAMP <literal> can produce a zone-aware timestamp
1530
+ ZONE_AWARE_TIMESTAMP_CONSTRUCTOR = False
1531
+
1521
1532
  __slots__ = (
1522
1533
  "error_level",
1523
1534
  "error_message_context",
@@ -3142,7 +3153,7 @@ class Parser(metaclass=_Parser):
3142
3153
  is_unpivot=self._prev.token_type == TokenType.UNPIVOT
3143
3154
  )
3144
3155
  elif self._match(TokenType.FROM):
3145
- from_ = self._parse_from(skip_from_token=True)
3156
+ from_ = self._parse_from(skip_from_token=True, consume_pipe=True)
3146
3157
  # Support parentheses for duckdb FROM-first syntax
3147
3158
  select = self._parse_select()
3148
3159
  if select:
@@ -3152,7 +3163,7 @@ class Parser(metaclass=_Parser):
3152
3163
  this = exp.select("*").from_(t.cast(exp.From, from_))
3153
3164
  else:
3154
3165
  this = (
3155
- self._parse_table()
3166
+ self._parse_table(consume_pipe=True)
3156
3167
  if table
3157
3168
  else self._parse_select(nested=True, parse_set_operation=False)
3158
3169
  )
@@ -3173,6 +3184,31 @@ class Parser(metaclass=_Parser):
3173
3184
  table: bool = False,
3174
3185
  parse_subquery_alias: bool = True,
3175
3186
  parse_set_operation: bool = True,
3187
+ consume_pipe: bool = True,
3188
+ ) -> t.Optional[exp.Expression]:
3189
+ query = self._parse_select_query(
3190
+ nested=nested,
3191
+ table=table,
3192
+ parse_subquery_alias=parse_subquery_alias,
3193
+ parse_set_operation=parse_set_operation,
3194
+ )
3195
+
3196
+ if (
3197
+ consume_pipe
3198
+ and self._match(TokenType.PIPE_GT, advance=False)
3199
+ and isinstance(query, exp.Query)
3200
+ ):
3201
+ query = self._parse_pipe_syntax_query(query)
3202
+ query = query.subquery(copy=False) if query and table else query
3203
+
3204
+ return query
3205
+
3206
+ def _parse_select_query(
3207
+ self,
3208
+ nested: bool = False,
3209
+ table: bool = False,
3210
+ parse_subquery_alias: bool = True,
3211
+ parse_set_operation: bool = True,
3176
3212
  ) -> t.Optional[exp.Expression]:
3177
3213
  cte = self._parse_with()
3178
3214
 
@@ -3192,7 +3228,11 @@ class Parser(metaclass=_Parser):
3192
3228
  return this
3193
3229
 
3194
3230
  # duckdb supports leading with FROM x
3195
- from_ = self._parse_from() if self._match(TokenType.FROM, advance=False) else None
3231
+ from_ = (
3232
+ self._parse_from(consume_pipe=True)
3233
+ if self._match(TokenType.FROM, advance=False)
3234
+ else None
3235
+ )
3196
3236
 
3197
3237
  if self._match(TokenType.SELECT):
3198
3238
  comments = self._prev_comments
@@ -3260,8 +3300,6 @@ class Parser(metaclass=_Parser):
3260
3300
  this = self._parse_derived_table_values()
3261
3301
  elif from_:
3262
3302
  this = exp.select("*").from_(from_.this, copy=False)
3263
- if self._match(TokenType.PIPE_GT, advance=False):
3264
- return self._parse_pipe_syntax_query(this)
3265
3303
  elif self._match(TokenType.SUMMARIZE):
3266
3304
  table = self._match(TokenType.TABLE)
3267
3305
  this = self._parse_select() or self._parse_string() or self._parse_table()
@@ -3521,13 +3559,18 @@ class Parser(metaclass=_Parser):
3521
3559
  )
3522
3560
 
3523
3561
  def _parse_from(
3524
- self, joins: bool = False, skip_from_token: bool = False
3562
+ self,
3563
+ joins: bool = False,
3564
+ skip_from_token: bool = False,
3565
+ consume_pipe: bool = False,
3525
3566
  ) -> t.Optional[exp.From]:
3526
3567
  if not skip_from_token and not self._match(TokenType.FROM):
3527
3568
  return None
3528
3569
 
3529
3570
  return self.expression(
3530
- exp.From, comments=self._prev_comments, this=self._parse_table(joins=joins)
3571
+ exp.From,
3572
+ comments=self._prev_comments,
3573
+ this=self._parse_table(joins=joins, consume_pipe=consume_pipe),
3531
3574
  )
3532
3575
 
3533
3576
  def _parse_match_recognize_measure(self) -> exp.MatchRecognizeMeasure:
@@ -3702,9 +3745,12 @@ class Parser(metaclass=_Parser):
3702
3745
  ) -> t.Optional[exp.Join]:
3703
3746
  if self._match(TokenType.COMMA):
3704
3747
  table = self._try_parse(self._parse_table)
3705
- if table:
3706
- return self.expression(exp.Join, this=table)
3707
- return None
3748
+ cross_join = self.expression(exp.Join, this=table) if table else None
3749
+
3750
+ if cross_join and self.JOINS_HAVE_EQUAL_PRECEDENCE:
3751
+ cross_join.set("kind", "CROSS")
3752
+
3753
+ return cross_join
3708
3754
 
3709
3755
  index = self._index
3710
3756
  method, side, kind = self._parse_join_parts()
@@ -3953,6 +3999,7 @@ class Parser(metaclass=_Parser):
3953
3999
  parse_bracket: bool = False,
3954
4000
  is_db_reference: bool = False,
3955
4001
  parse_partition: bool = False,
4002
+ consume_pipe: bool = False,
3956
4003
  ) -> t.Optional[exp.Expression]:
3957
4004
  lateral = self._parse_lateral()
3958
4005
  if lateral:
@@ -3966,7 +4013,7 @@ class Parser(metaclass=_Parser):
3966
4013
  if values:
3967
4014
  return values
3968
4015
 
3969
- subquery = self._parse_select(table=True)
4016
+ subquery = self._parse_select(table=True, consume_pipe=consume_pipe)
3970
4017
  if subquery:
3971
4018
  if not subquery.args.get("pivots"):
3972
4019
  subquery.set("pivots", self._parse_pivots())
@@ -4708,7 +4755,9 @@ class Parser(metaclass=_Parser):
4708
4755
 
4709
4756
  return locks
4710
4757
 
4711
- def parse_set_operation(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4758
+ def parse_set_operation(
4759
+ self, this: t.Optional[exp.Expression], consume_pipe: bool = False
4760
+ ) -> t.Optional[exp.Expression]:
4712
4761
  start = self._index
4713
4762
  _, side_token, kind_token = self._parse_join_parts()
4714
4763
 
@@ -4751,7 +4800,9 @@ class Parser(metaclass=_Parser):
4751
4800
  if by_name and self._match_texts(("ON", "BY")):
4752
4801
  on_column_list = self._parse_wrapped_csv(self._parse_column)
4753
4802
 
4754
- expression = self._parse_select(nested=True, parse_set_operation=False)
4803
+ expression = self._parse_select(
4804
+ nested=True, parse_set_operation=False, consume_pipe=consume_pipe
4805
+ )
4755
4806
 
4756
4807
  return self.expression(
4757
4808
  operation,
@@ -5082,12 +5133,20 @@ class Parser(metaclass=_Parser):
5082
5133
  this = self._parse_primary()
5083
5134
 
5084
5135
  if isinstance(this, exp.Literal):
5136
+ literal = this.name
5085
5137
  this = self._parse_column_ops(this)
5086
5138
 
5087
5139
  parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
5088
5140
  if parser:
5089
5141
  return parser(self, this, data_type)
5090
5142
 
5143
+ if (
5144
+ self.ZONE_AWARE_TIMESTAMP_CONSTRUCTOR
5145
+ and data_type.is_type(exp.DataType.Type.TIMESTAMP)
5146
+ and TIME_ZONE_RE.search(literal)
5147
+ ):
5148
+ data_type = exp.DataType.build("TIMESTAMPTZ")
5149
+
5091
5150
  return self.expression(exp.Cast, this=this, to=data_type)
5092
5151
 
5093
5152
  # The expressions arg gets set by the parser when we have something like DECIMAL(38, 0)
@@ -8361,34 +8420,14 @@ class Parser(metaclass=_Parser):
8361
8420
 
8362
8421
  return new_select.with_(new_cte, as_=query, copy=False)
8363
8422
 
8364
- def _build_pipe_ctes(
8365
- self,
8366
- query: exp.Select,
8367
- expressions: t.List[exp.Expression],
8368
- alias_cte: t.Optional[exp.TableAlias] = None,
8369
- ) -> exp.Select:
8370
- select = query.selects[0].assert_is(exp.Star)
8371
- if select.args.get("except") or select.args.get("replace"):
8372
- query = self._build_pipe_cte(
8373
- query=query.select(
8374
- *[expr for expr in expressions if not expr.is_star and expr.args.get("alias")],
8375
- copy=False,
8376
- ),
8377
- expressions=[
8378
- projection.args.get("alias", projection) for projection in expressions
8379
- ],
8380
- )
8381
- else:
8382
- query.select(*expressions, append=False, copy=False)
8383
-
8384
- return self._build_pipe_cte(query=query, expressions=[exp.Star()], alias_cte=alias_cte)
8385
-
8386
8423
  def _parse_pipe_syntax_select(self, query: exp.Select) -> exp.Select:
8387
- select = self._parse_select()
8424
+ select = self._parse_select(consume_pipe=False)
8388
8425
  if not select:
8389
8426
  return query
8390
8427
 
8391
- return self._build_pipe_ctes(query=query, expressions=select.expressions)
8428
+ return self._build_pipe_cte(
8429
+ query=query.select(*select.expressions, append=False), expressions=[exp.Star()]
8430
+ )
8392
8431
 
8393
8432
  def _parse_pipe_syntax_limit(self, query: exp.Select) -> exp.Select:
8394
8433
  limit = self._parse_limit()
@@ -8437,7 +8476,7 @@ class Parser(metaclass=_Parser):
8437
8476
  copy=False,
8438
8477
  )
8439
8478
  else:
8440
- query.select(*aggregates_or_groups, copy=False)
8479
+ query.select(*aggregates_or_groups, append=False, copy=False)
8441
8480
 
8442
8481
  if orders:
8443
8482
  return query.order_by(*orders, append=False, copy=False)
@@ -8453,11 +8492,9 @@ class Parser(metaclass=_Parser):
8453
8492
  ):
8454
8493
  query = self._parse_pipe_syntax_aggregate_group_order_by(query)
8455
8494
 
8456
- return self._build_pipe_ctes(
8457
- query=query, expressions=[expr for expr in query.selects if not expr.is_star]
8458
- )
8495
+ return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8459
8496
 
8460
- def _parse_pipe_syntax_set_operator(self, query: exp.Query) -> t.Optional[exp.Select]:
8497
+ def _parse_pipe_syntax_set_operator(self, query: exp.Query) -> t.Optional[exp.Query]:
8461
8498
  first_setop = self.parse_set_operation(this=query)
8462
8499
  if not first_setop:
8463
8500
  return None
@@ -8488,12 +8525,15 @@ class Parser(metaclass=_Parser):
8488
8525
 
8489
8526
  return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8490
8527
 
8491
- def _parse_pipe_syntax_join(self, query: exp.Select) -> t.Optional[exp.Select]:
8528
+ def _parse_pipe_syntax_join(self, query: exp.Query) -> t.Optional[exp.Query]:
8492
8529
  join = self._parse_join()
8493
8530
  if not join:
8494
8531
  return None
8495
8532
 
8496
- return query.join(join, copy=False)
8533
+ if isinstance(query, exp.Select):
8534
+ return query.join(join, copy=False)
8535
+
8536
+ return query
8497
8537
 
8498
8538
  def _parse_pipe_syntax_pivot(self, query: exp.Select) -> exp.Select:
8499
8539
  pivots = self._parse_pivots()
@@ -8504,37 +8544,12 @@ class Parser(metaclass=_Parser):
8504
8544
  if from_:
8505
8545
  from_.this.set("pivots", pivots)
8506
8546
 
8507
- return self._build_pipe_ctes(query=query, expressions=[exp.Star()])
8547
+ return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8508
8548
 
8509
8549
  def _parse_pipe_syntax_extend(self, query: exp.Select) -> exp.Select:
8510
8550
  self._match_text_seq("EXTEND")
8511
- return self._build_pipe_ctes(
8512
- query=query,
8513
- expressions=[query.selects[0].assert_is(exp.Star), *self._parse_expressions()],
8514
- )
8515
-
8516
- def _parse_pipe_syntax_drop(self, query: exp.Select) -> exp.Select:
8517
- self._match_text_seq("DROP")
8518
- dropped_columns = self._parse_csv(self._parse_assignment)
8519
-
8520
- select = query.selects[0].assert_is(exp.Star)
8521
- except_ = select.args.get("except") or []
8522
- select.set("except", [*except_, *dropped_columns])
8523
-
8524
- return query
8525
-
8526
- def _parse_pipe_syntax_set(self, query: exp.Select) -> exp.Select:
8527
- self._match_text_seq("SET")
8528
- replaced_columns = [
8529
- self.expression(exp.Alias, this=expr.expression, alias=expr.this)
8530
- for expr in self._parse_csv(self._parse_assignment)
8531
- ]
8532
-
8533
- select = query.selects[0].assert_is(exp.Star)
8534
- replace_ = select.args.get("replace") or []
8535
- select.set("replace", [*replace_, *replaced_columns])
8536
-
8537
- return query
8551
+ query.select(*[exp.Star(), *self._parse_expressions()], append=False, copy=False)
8552
+ return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8538
8553
 
8539
8554
  def _parse_pipe_syntax_tablesample(self, query: exp.Select) -> exp.Select:
8540
8555
  sample = self._parse_table_sample()
@@ -8547,7 +8562,13 @@ class Parser(metaclass=_Parser):
8547
8562
 
8548
8563
  return query
8549
8564
 
8550
- def _parse_pipe_syntax_query(self, query: exp.Select) -> t.Optional[exp.Select]:
8565
+ def _parse_pipe_syntax_query(self, query: exp.Query) -> t.Optional[exp.Query]:
8566
+ if isinstance(query, exp.Subquery):
8567
+ query = exp.select("*").from_(query, copy=False)
8568
+
8569
+ if not query.args.get("from"):
8570
+ query = exp.select("*").from_(query.subquery(copy=False), copy=False)
8571
+
8551
8572
  while self._match(TokenType.PIPE_GT):
8552
8573
  start = self._curr
8553
8574
  parser = self.PIPE_SYNTAX_TRANSFORM_PARSERS.get(self._curr.text.upper())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlglot
3
- Version: 26.29.0
3
+ Version: 26.30.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
@@ -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=gOnetX1YzVEd7bBCS3U4KJPt8DHhhNA_iqdIiN8DYk4,515
4
+ sqlglot/_version.py,sha256=FTiVUaTaSWoKVYxiizmUH00aN8nwszEzKZ3Dh2gUm7s,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=r3WkNufDInSqIoMasryY4W_XUV7DyIFU2G29jglFPqQ,243249
7
+ sqlglot/expressions.py,sha256=QTf40Yu04Ar6en5_Ncv0bpER5PRlNYtB7S7Ocr2vScw,243330
8
8
  sqlglot/generator.py,sha256=E1LjyN49nX9XfK-hysHWvpw7-qtws4xeb85sZi5x3M0,213345
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=IXOPic_GfVXDaNRna9JbxmG-l2FjxZTIlV0wtWvWnqM,323926
12
+ sqlglot/parser.py,sha256=xWm01SCq3tSHr7WIVz-h2taaf_JW5JvADsNufE8OAEw,324529
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
@@ -18,30 +18,31 @@ sqlglot/time.py,sha256=Q62gv6kL40OiRBF6BMESxKJcMVn7ZLNw7sv8H34z5FI,18400
18
18
  sqlglot/tokens.py,sha256=R0B8GQSbQ9GoDc0NlaT5Tc8RjgEOx2IYIkYU5rY8Rg8,48742
19
19
  sqlglot/transforms.py,sha256=3jpbHeVTLK9hmQi5f3_vmK-5jZB32_ittCkO7poxCs4,40631
20
20
  sqlglot/trie.py,sha256=v27uXMrHfqrXlJ6GmeTSMovsB_3o0ctnlKhdNt7W6fI,2245
21
- sqlglot/dialects/__init__.py,sha256=aZTLpe2SwgWqiVrRabmfV8TVLPVHFydGwb_zhcVhRss,3499
21
+ sqlglot/dialects/__init__.py,sha256=Rcmnv_D8xjCrEPObi4R2_cs3upbfc5OrDKMIJhQTt4k,3513
22
22
  sqlglot/dialects/athena.py,sha256=gPE9ybRcbd6dVa1mrTFB_eVjsjQG36hErq5EpHyQmXo,6344
23
- sqlglot/dialects/bigquery.py,sha256=PIRhlNIj6I5iXPxR2_9q1OWXvy4ovVB_ae5qe8SWV80,52713
24
- sqlglot/dialects/clickhouse.py,sha256=0ahX0zjIwN9-RzfNyITBHs9PsgQXjL0uMRlRgYz9crI,56520
23
+ sqlglot/dialects/bigquery.py,sha256=Dw_ZOCv_rCDiUqMBJbmkb6b_R2t9ZemiRmvqu9YLBmY,52756
24
+ sqlglot/dialects/clickhouse.py,sha256=Dc0aXwEgN8b6coXKM6P8zh3IsyrXjBajNGB-cVhnu1Y,56603
25
25
  sqlglot/dialects/databricks.py,sha256=8PoaiP8PfiBjpheRiua-rO_HzX2TRUXqc3DnlQ8zYrg,4481
26
- sqlglot/dialects/dialect.py,sha256=uiRHCJ2pjIea3EnRXhizNni1o-d31X02CRBuvXXne7U,68529
26
+ sqlglot/dialects/dialect.py,sha256=-u8403azEMX3F9KrLQnv7xOU6IaHpxL4pJH733oQlqs,68747
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
- sqlglot/dialects/duckdb.py,sha256=rARz845jDTzx8WUncAYHZeoBcVi7WvIJlGbjnNHaxZM,47965
30
+ sqlglot/dialects/duckdb.py,sha256=oGCgK0KjwJcCKy-YOZeiQnEo4v7Zc1r5AK0tCXO2VIc,48005
31
31
  sqlglot/dialects/dune.py,sha256=gALut-fFfN2qMsr8LvZ1NQK3F3W9z2f4PwMvTMXVVVg,375
32
- sqlglot/dialects/hive.py,sha256=PO6DLT1kHL-U2kFfV1CsNgQFT7A32LuGN71gnTXEOfY,31728
32
+ sqlglot/dialects/fabric.py,sha256=RfRvQq7AVcr7yT30rqsTk-QILmhTJHECXZXMOotmL6I,4104
33
+ sqlglot/dialects/hive.py,sha256=yKCsVN4R8pIB2Lmx1YGiSR9b8Me3li6rsGuZrKjHTo4,31771
33
34
  sqlglot/dialects/materialize.py,sha256=_DPLPt8YrdQIIXNrGJw1IMcGOoAEJ9NO9X9pDfy4hxs,3494
34
35
  sqlglot/dialects/mysql.py,sha256=prZecn3zeoifZX7l54UuLG64ar7I-or_z9lF-rT8bds,49233
35
- sqlglot/dialects/oracle.py,sha256=llxu2LzndrsGyceTod-Leh03vuPWEUKzVHB5gQY-tY8,15313
36
+ sqlglot/dialects/oracle.py,sha256=o6On1cYWFt6TpQYKuzo4kCz5vKb8jQr8WSwc619h3Lg,15967
36
37
  sqlglot/dialects/postgres.py,sha256=KUyMoLkm1_sZKUbdjn6bjXx9xz7sbEMKa-fl5Mzfrsk,31025
37
- sqlglot/dialects/presto.py,sha256=ltKbQ44efeq1HM0T8Qq0rsBSx6B6bF9RoKtUBVeoz70,33155
38
- sqlglot/dialects/prql.py,sha256=OF2LfDb4uzKIF7kpCfpL5G7VP1pnzLbjfW5QFUnuPvo,7803
39
- sqlglot/dialects/redshift.py,sha256=H8H8lGizHIAd4qLoPeFchyiGZKO1I8U_B058woukuGw,15366
38
+ sqlglot/dialects/presto.py,sha256=xsbYSc_1-z-jSOsG85z9Pw7pd_V_BX0Dila7KsMsS04,33203
39
+ sqlglot/dialects/prql.py,sha256=fwN-SPEGx-drwf1K0U2MByN-PkW3C_rOgQ3xeJeychg,7908
40
+ sqlglot/dialects/redshift.py,sha256=UwfntKCfPpX63G6ow4vjadFpfmfaKrmFOGLoOuWN8Yg,15406
40
41
  sqlglot/dialects/risingwave.py,sha256=hwEOPjMw0ZM_3fjQcBUE00oy6I8V6mzYOOYmcwwS8mw,2898
41
- sqlglot/dialects/snowflake.py,sha256=dP5o1sH0q5UDMxPoI5vYp1_2FQyBU7VbeYdxF1HVyEs,63398
42
- sqlglot/dialects/spark.py,sha256=fbmiTKAQiKqG9yE_HAxYGgQiOjdxB9tJyjOtgdqF100,7645
42
+ sqlglot/dialects/snowflake.py,sha256=kpoWQ_w3SJyb605QWSvr-BxBR3pP9tmlDbT4ix8p484,63438
43
+ sqlglot/dialects/spark.py,sha256=bOUSXUoWtLfWaQ9fIjWaw4zLBJY6N7vxajdMbAxLdOk,8307
43
44
  sqlglot/dialects/spark2.py,sha256=8er7nHDm5Wc57m9AOxKN0sd_DVzbhAL44H_udlFh9O8,14258
44
- sqlglot/dialects/sqlite.py,sha256=UzJwIdY1PsLArMxNt5lKvk8COHvXeo4FoqW41LqVmM8,12440
45
+ sqlglot/dialects/sqlite.py,sha256=fwqmopeuoupD_2dh2q6rT3UFxWtFHkskZ1OXAYnPT9Q,12483
45
46
  sqlglot/dialects/starrocks.py,sha256=fHNgvq5Nz7dI4QUWCTOO5VDOYjasBxRRlcg9TbY0UZE,11235
46
47
  sqlglot/dialects/tableau.py,sha256=oIawDzUITxGCWaEMB8OaNMPWhbC3U-2y09pYPm4eazc,2190
47
48
  sqlglot/dialects/teradata.py,sha256=xWa-9kSTsT-eM1NePi_oIM1dPHmXW89GLU5Uda3_6Ao,14036
@@ -72,8 +73,8 @@ sqlglot/optimizer/qualify_tables.py,sha256=5f5enBAh-bpNB9ewF97W9fx9h1TGXj1Ih5fnc
72
73
  sqlglot/optimizer/scope.py,sha256=r-2PaO7-woaIWaWrKC88J9eTgdQardNYQ1rIXXaPr1w,30501
73
74
  sqlglot/optimizer/simplify.py,sha256=S0Blqg5Mq2KRRWhWz-Eivch9sBjBhg9fRJA6EdBzj2g,50704
74
75
  sqlglot/optimizer/unnest_subqueries.py,sha256=kzWUVDlxs8z9nmRx-8U-pHXPtVZhEIwkKqmKhr2QLvc,10908
75
- sqlglot-26.29.0.dist-info/licenses/LICENSE,sha256=AI3__mHZfOtzY3EluR_pIYBm3_pE7TbVx7qaHxoZ114,1065
76
- sqlglot-26.29.0.dist-info/METADATA,sha256=rc1ouFaDp1lgiQ2W3jRFM2VNj7RUrf2drwij1_rajpg,20732
77
- sqlglot-26.29.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
- sqlglot-26.29.0.dist-info/top_level.txt,sha256=5kRskCGA_gVADF9rSfSzPdLHXqvfMusDYeHePfNY2nQ,8
79
- sqlglot-26.29.0.dist-info/RECORD,,
76
+ sqlglot-26.30.0.dist-info/licenses/LICENSE,sha256=AI3__mHZfOtzY3EluR_pIYBm3_pE7TbVx7qaHxoZ114,1065
77
+ sqlglot-26.30.0.dist-info/METADATA,sha256=rIvq32wg6apWdTgqTkYe5mYgGj5XTwMQ8rvqjoTQruI,20732
78
+ sqlglot-26.30.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ sqlglot-26.30.0.dist-info/top_level.txt,sha256=5kRskCGA_gVADF9rSfSzPdLHXqvfMusDYeHePfNY2nQ,8
80
+ sqlglot-26.30.0.dist-info/RECORD,,