sqlglot 27.10.0__py3-none-any.whl → 27.12.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.
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  from sqlglot import TokenType
2
4
  import typing as t
3
5
 
@@ -10,8 +12,11 @@ from sqlglot.dialects.dialect import (
10
12
  rename_func,
11
13
  bool_xor_sql,
12
14
  count_if_to_sum,
15
+ timestamptrunc_sql,
16
+ date_add_interval_sql,
17
+ timestampdiff_sql,
13
18
  )
14
- from sqlglot.dialects.mysql import MySQL
19
+ from sqlglot.dialects.mysql import MySQL, _remove_ts_or_ds_to_date, date_add_sql
15
20
  from sqlglot.expressions import DataType
16
21
  from sqlglot.generator import unsupported_args
17
22
  from sqlglot.helper import seq_get
@@ -55,6 +60,9 @@ class SingleStore(MySQL):
55
60
  **MySQL.Tokenizer.KEYWORDS,
56
61
  "BSON": TokenType.JSONB,
57
62
  "GEOGRAPHYPOINT": TokenType.GEOGRAPHYPOINT,
63
+ "TIMESTAMP": TokenType.TIMESTAMP,
64
+ "UTC_DATE": TokenType.UTC_DATE,
65
+ "UTC_TIME": TokenType.UTC_TIME,
58
66
  ":>": TokenType.COLON_GT,
59
67
  "!:>": TokenType.NCOLON_GT,
60
68
  "::$": TokenType.DCOLONDOLLAR,
@@ -159,6 +167,11 @@ class SingleStore(MySQL):
159
167
  this=seq_get(args, 0),
160
168
  format=MySQL.format_time(exp.Literal.string("%W")),
161
169
  ),
170
+ "TIMESTAMPDIFF": lambda args: exp.TimestampDiff(
171
+ this=seq_get(args, 2),
172
+ expression=seq_get(args, 1),
173
+ unit=seq_get(args, 0),
174
+ ),
162
175
  "APPROX_COUNT_DISTINCT": exp.Hll.from_arg_list,
163
176
  "APPROX_PERCENTILE": lambda args, dialect: exp.ApproxQuantile(
164
177
  this=seq_get(args, 0),
@@ -186,9 +199,16 @@ class SingleStore(MySQL):
186
199
  ),
187
200
  }
188
201
 
202
+ NO_PAREN_FUNCTIONS = {
203
+ **MySQL.Parser.NO_PAREN_FUNCTIONS,
204
+ TokenType.UTC_DATE: exp.UtcDate,
205
+ TokenType.UTC_TIME: exp.UtcTime,
206
+ }
207
+
189
208
  CAST_COLUMN_OPERATORS = {TokenType.COLON_GT, TokenType.NCOLON_GT}
190
209
 
191
210
  COLUMN_OPERATORS = {
211
+ **MySQL.Parser.COLUMN_OPERATORS,
192
212
  TokenType.COLON_GT: lambda self, this, to: self.expression(
193
213
  exp.Cast,
194
214
  this=this,
@@ -209,8 +229,22 @@ class SingleStore(MySQL):
209
229
  exp.JSONExtractScalar, json_type="DOUBLE"
210
230
  )([this, exp.Literal.string(path.name)]),
211
231
  }
232
+ COLUMN_OPERATORS.pop(TokenType.ARROW)
233
+ COLUMN_OPERATORS.pop(TokenType.DARROW)
234
+ COLUMN_OPERATORS.pop(TokenType.HASH_ARROW)
235
+ COLUMN_OPERATORS.pop(TokenType.DHASH_ARROW)
236
+ COLUMN_OPERATORS.pop(TokenType.PLACEHOLDER)
212
237
 
213
238
  class Generator(MySQL.Generator):
239
+ SUPPORTS_UESCAPE = False
240
+
241
+ @staticmethod
242
+ def _unicode_substitute(m: re.Match[str]) -> str:
243
+ # Interpret the number as hex and convert it to the Unicode string
244
+ return chr(int(m.group(1), 16))
245
+
246
+ UNICODE_SUBSTITUTE: t.Optional[t.Callable[[re.Match[str]], str]] = _unicode_substitute
247
+
214
248
  SUPPORTED_JSON_PATH_PARTS = {
215
249
  exp.JSONPathKey,
216
250
  exp.JSONPathRoot,
@@ -249,6 +283,9 @@ class SingleStore(MySQL):
249
283
  exp.TryCast: unsupported_args("format", "action", "default")(
250
284
  lambda self, e: f"{self.sql(e, 'this')} !:> {self.sql(e, 'to')}"
251
285
  ),
286
+ exp.CastToStrType: lambda self, e: self.sql(
287
+ exp.cast(e.this, DataType.build(e.args["to"].name))
288
+ ),
252
289
  exp.StrToUnix: unsupported_args("format")(rename_func("UNIX_TIMESTAMP")),
253
290
  exp.TimeToUnix: rename_func("UNIX_TIMESTAMP"),
254
291
  exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"),
@@ -277,6 +314,31 @@ class SingleStore(MySQL):
277
314
  exp.DateBin: unsupported_args("unit", "zone")(
278
315
  lambda self, e: self.func("TIME_BUCKET", e.this, e.expression, e.args.get("origin"))
279
316
  ),
317
+ exp.TimeStrToDate: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.DATE)),
318
+ exp.FromTimeZone: lambda self, e: self.func(
319
+ "CONVERT_TZ", e.this, e.args.get("zone"), "'UTC'"
320
+ ),
321
+ exp.DiToDate: lambda self,
322
+ e: f"STR_TO_DATE({self.sql(e, 'this')}, {SingleStore.DATEINT_FORMAT})",
323
+ exp.DateToDi: lambda self,
324
+ e: f"(DATE_FORMAT({self.sql(e, 'this')}, {SingleStore.DATEINT_FORMAT}) :> INT)",
325
+ exp.TsOrDiToDi: lambda self,
326
+ e: f"(DATE_FORMAT({self.sql(e, 'this')}, {SingleStore.DATEINT_FORMAT}) :> INT)",
327
+ exp.Time: unsupported_args("zone")(lambda self, e: f"{self.sql(e, 'this')} :> TIME"),
328
+ exp.DatetimeAdd: _remove_ts_or_ds_to_date(date_add_sql("ADD")),
329
+ exp.DatetimeTrunc: unsupported_args("zone")(timestamptrunc_sql()),
330
+ exp.DatetimeSub: date_add_interval_sql("DATE", "SUB"),
331
+ exp.DatetimeDiff: timestampdiff_sql,
332
+ exp.DateTrunc: unsupported_args("zone")(timestamptrunc_sql()),
333
+ exp.DateDiff: unsupported_args("zone")(
334
+ lambda self, e: timestampdiff_sql(self, e)
335
+ if e.unit is not None
336
+ else self.func("DATEDIFF", e.this, e.expression)
337
+ ),
338
+ exp.TsOrDsDiff: lambda self, e: timestampdiff_sql(self, e)
339
+ if e.unit is not None
340
+ else self.func("DATEDIFF", e.this, e.expression),
341
+ exp.TimestampTrunc: unsupported_args("zone")(timestamptrunc_sql()),
280
342
  exp.JSONExtract: unsupported_args(
281
343
  "only_json_types",
282
344
  "expressions",
@@ -310,6 +372,9 @@ class SingleStore(MySQL):
310
372
  exp.Variance: rename_func("VAR_SAMP"),
311
373
  exp.VariancePop: rename_func("VAR_POP"),
312
374
  exp.Xor: bool_xor_sql,
375
+ exp.Cbrt: lambda self, e: self.sql(
376
+ exp.Pow(this=e.this, expression=exp.Literal.number(1) / exp.Literal.number(3))
377
+ ),
313
378
  exp.RegexpLike: lambda self, e: self.binary(e, "RLIKE"),
314
379
  exp.Repeat: lambda self, e: self.func(
315
380
  "LPAD",
@@ -345,6 +410,13 @@ class SingleStore(MySQL):
345
410
  exp.FromBase: lambda self, e: self.func(
346
411
  "CONV", e.this, e.expression, exp.Literal.number(10)
347
412
  ),
413
+ exp.RegexpILike: lambda self, e: self.binary(
414
+ exp.RegexpLike(
415
+ this=exp.Lower(this=e.this),
416
+ expression=exp.Lower(this=e.expression),
417
+ ),
418
+ "RLIKE",
419
+ ),
348
420
  exp.Reduce: unsupported_args("finish")(
349
421
  lambda self, e: self.func(
350
422
  "REDUCE", e.args.get("initial"), e.this, e.args.get("merge")
@@ -352,6 +424,95 @@ class SingleStore(MySQL):
352
424
  ),
353
425
  }
354
426
  TRANSFORMS.pop(exp.JSONExtractScalar)
427
+ TRANSFORMS.pop(exp.CurrentDate)
428
+
429
+ UNSUPPORTED_TYPES = {
430
+ exp.DataType.Type.ARRAY,
431
+ exp.DataType.Type.AGGREGATEFUNCTION,
432
+ exp.DataType.Type.SIMPLEAGGREGATEFUNCTION,
433
+ exp.DataType.Type.BIGSERIAL,
434
+ exp.DataType.Type.BPCHAR,
435
+ exp.DataType.Type.DATEMULTIRANGE,
436
+ exp.DataType.Type.DATERANGE,
437
+ exp.DataType.Type.DYNAMIC,
438
+ exp.DataType.Type.HLLSKETCH,
439
+ exp.DataType.Type.HSTORE,
440
+ exp.DataType.Type.IMAGE,
441
+ exp.DataType.Type.INET,
442
+ exp.DataType.Type.INT128,
443
+ exp.DataType.Type.INT256,
444
+ exp.DataType.Type.INT4MULTIRANGE,
445
+ exp.DataType.Type.INT4RANGE,
446
+ exp.DataType.Type.INT8MULTIRANGE,
447
+ exp.DataType.Type.INT8RANGE,
448
+ exp.DataType.Type.INTERVAL,
449
+ exp.DataType.Type.IPADDRESS,
450
+ exp.DataType.Type.IPPREFIX,
451
+ exp.DataType.Type.IPV4,
452
+ exp.DataType.Type.IPV6,
453
+ exp.DataType.Type.LIST,
454
+ exp.DataType.Type.MAP,
455
+ exp.DataType.Type.LOWCARDINALITY,
456
+ exp.DataType.Type.MONEY,
457
+ exp.DataType.Type.MULTILINESTRING,
458
+ exp.DataType.Type.NAME,
459
+ exp.DataType.Type.NESTED,
460
+ exp.DataType.Type.NOTHING,
461
+ exp.DataType.Type.NULL,
462
+ exp.DataType.Type.NUMMULTIRANGE,
463
+ exp.DataType.Type.NUMRANGE,
464
+ exp.DataType.Type.OBJECT,
465
+ exp.DataType.Type.RANGE,
466
+ exp.DataType.Type.ROWVERSION,
467
+ exp.DataType.Type.SERIAL,
468
+ exp.DataType.Type.SMALLSERIAL,
469
+ exp.DataType.Type.SMALLMONEY,
470
+ exp.DataType.Type.STRUCT,
471
+ exp.DataType.Type.SUPER,
472
+ exp.DataType.Type.TIMETZ,
473
+ exp.DataType.Type.TIMESTAMPNTZ,
474
+ exp.DataType.Type.TIMESTAMPLTZ,
475
+ exp.DataType.Type.TIMESTAMPTZ,
476
+ exp.DataType.Type.TIMESTAMP_NS,
477
+ exp.DataType.Type.TSMULTIRANGE,
478
+ exp.DataType.Type.TSRANGE,
479
+ exp.DataType.Type.TSTZMULTIRANGE,
480
+ exp.DataType.Type.TSTZRANGE,
481
+ exp.DataType.Type.UINT128,
482
+ exp.DataType.Type.UINT256,
483
+ exp.DataType.Type.UNION,
484
+ exp.DataType.Type.UNKNOWN,
485
+ exp.DataType.Type.USERDEFINED,
486
+ exp.DataType.Type.UUID,
487
+ exp.DataType.Type.VARIANT,
488
+ exp.DataType.Type.XML,
489
+ exp.DataType.Type.TDIGEST,
490
+ }
491
+
492
+ TYPE_MAPPING = {
493
+ **MySQL.Generator.TYPE_MAPPING,
494
+ exp.DataType.Type.BIGDECIMAL: "DECIMAL",
495
+ exp.DataType.Type.BIT: "BOOLEAN",
496
+ exp.DataType.Type.DATE32: "DATE",
497
+ exp.DataType.Type.DATETIME64: "DATETIME",
498
+ exp.DataType.Type.DECIMAL32: "DECIMAL",
499
+ exp.DataType.Type.DECIMAL64: "DECIMAL",
500
+ exp.DataType.Type.DECIMAL128: "DECIMAL",
501
+ exp.DataType.Type.DECIMAL256: "DECIMAL",
502
+ exp.DataType.Type.ENUM8: "ENUM",
503
+ exp.DataType.Type.ENUM16: "ENUM",
504
+ exp.DataType.Type.FIXEDSTRING: "TEXT",
505
+ exp.DataType.Type.GEOMETRY: "GEOGRAPHY",
506
+ exp.DataType.Type.POINT: "GEOGRAPHYPOINT",
507
+ exp.DataType.Type.RING: "GEOGRAPHY",
508
+ exp.DataType.Type.LINESTRING: "GEOGRAPHY",
509
+ exp.DataType.Type.POLYGON: "GEOGRAPHY",
510
+ exp.DataType.Type.MULTIPOLYGON: "GEOGRAPHY",
511
+ exp.DataType.Type.JSONB: "BSON",
512
+ exp.DataType.Type.TIMESTAMP: "TIMESTAMP",
513
+ exp.DataType.Type.TIMESTAMP_S: "TIMESTAMP",
514
+ exp.DataType.Type.TIMESTAMP_MS: "TIMESTAMP(6)",
515
+ }
355
516
 
356
517
  # https://docs.singlestore.com/cloud/reference/sql-reference/restricted-keywords/list-of-restricted-keywords/
357
518
  RESERVED_KEYWORDS = {
@@ -1452,3 +1613,57 @@ class SingleStore(MySQL):
1452
1613
  expression.expression,
1453
1614
  self.func("TO_JSON", expression.this),
1454
1615
  )
1616
+
1617
+ @unsupported_args("kind", "nested", "values")
1618
+ def datatype_sql(self, expression: exp.DataType) -> str:
1619
+ if expression.is_type(exp.DataType.Type.VARBINARY) and not expression.expressions:
1620
+ # `VARBINARY` must always have a size - if it doesn't, we always generate `BLOB`
1621
+ return "BLOB"
1622
+ if expression.is_type(
1623
+ exp.DataType.Type.DECIMAL32,
1624
+ exp.DataType.Type.DECIMAL64,
1625
+ exp.DataType.Type.DECIMAL128,
1626
+ exp.DataType.Type.DECIMAL256,
1627
+ ):
1628
+ scale = self.expressions(expression, flat=True)
1629
+
1630
+ if expression.is_type(exp.DataType.Type.DECIMAL32):
1631
+ precision = "9"
1632
+ elif expression.is_type(exp.DataType.Type.DECIMAL64):
1633
+ precision = "18"
1634
+ elif expression.is_type(exp.DataType.Type.DECIMAL128):
1635
+ precision = "38"
1636
+ else:
1637
+ # 65 is a maximum precision supported in SingleStore
1638
+ precision = "65"
1639
+ if scale is not None:
1640
+ return f"DECIMAL({precision}, {scale[0]})"
1641
+ else:
1642
+ return f"DECIMAL({precision})"
1643
+
1644
+ return super().datatype_sql(expression)
1645
+
1646
+ def collate_sql(self, expression: exp.Collate) -> str:
1647
+ # SingleStore does not support setting a collation for column in the SELECT query,
1648
+ # so we cast column to a LONGTEXT type with specific collation
1649
+ return self.binary(expression, ":> LONGTEXT COLLATE")
1650
+
1651
+ def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1652
+ timezone = expression.this
1653
+ if timezone:
1654
+ if isinstance(timezone, exp.Literal) and timezone.name.lower() == "utc":
1655
+ return self.func("UTC_DATE")
1656
+ self.unsupported("CurrentDate with timezone is not supported in SingleStore")
1657
+
1658
+ return self.func("CURRENT_DATE")
1659
+
1660
+ def currenttime_sql(self, expression: exp.CurrentTime) -> str:
1661
+ arg = expression.this
1662
+ if arg:
1663
+ if isinstance(arg, exp.Literal) and arg.name.lower() == "utc":
1664
+ return self.func("UTC_TIME")
1665
+ if isinstance(arg, exp.Literal) and arg.is_number:
1666
+ return self.func("CURRENT_TIME", arg)
1667
+ self.unsupported("CurrentTime with timezone is not supported in SingleStore")
1668
+
1669
+ return self.func("CURRENT_TIME")
@@ -552,6 +552,7 @@ class Snowflake(Dialect):
552
552
 
553
553
  ID_VAR_TOKENS = {
554
554
  *parser.Parser.ID_VAR_TOKENS,
555
+ TokenType.EXCEPT,
555
556
  TokenType.MATCH_CONDITION,
556
557
  }
557
558
 
@@ -642,6 +643,7 @@ class Snowflake(Dialect):
642
643
  "TO_TIMESTAMP_NTZ": _build_datetime("TO_TIMESTAMP_NTZ", exp.DataType.Type.TIMESTAMP),
643
644
  "TO_TIMESTAMP_TZ": _build_datetime("TO_TIMESTAMP_TZ", exp.DataType.Type.TIMESTAMPTZ),
644
645
  "TO_VARCHAR": exp.ToChar.from_arg_list,
646
+ "VECTOR_L2_DISTANCE": exp.EuclideanDistance.from_arg_list,
645
647
  "ZEROIFNULL": _build_if_from_zeroifnull,
646
648
  }
647
649
 
@@ -658,6 +660,7 @@ class Snowflake(Dialect):
658
660
 
659
661
  ALTER_PARSERS = {
660
662
  **parser.Parser.ALTER_PARSERS,
663
+ "SESSION": lambda self: self._parse_alter_session(),
661
664
  "UNSET": lambda self: self.expression(
662
665
  exp.Set,
663
666
  tag=self._match_text_seq("TAG"),
@@ -1123,9 +1126,8 @@ class Snowflake(Dialect):
1123
1126
 
1124
1127
  KEYWORDS = {
1125
1128
  **tokens.Tokenizer.KEYWORDS,
1126
- "FILE://": TokenType.URI_START,
1127
1129
  "BYTEINT": TokenType.INT,
1128
- "EXCLUDE": TokenType.EXCEPT,
1130
+ "FILE://": TokenType.URI_START,
1129
1131
  "FILE FORMAT": TokenType.FILE_FORMAT,
1130
1132
  "GET": TokenType.GET,
1131
1133
  "MATCH_CONDITION": TokenType.MATCH_CONDITION,
@@ -1137,15 +1139,16 @@ class Snowflake(Dialect):
1137
1139
  "RM": TokenType.COMMAND,
1138
1140
  "SAMPLE": TokenType.TABLE_SAMPLE,
1139
1141
  "SEMANTIC VIEW": TokenType.SEMANTIC_VIEW,
1142
+ "SESSION": TokenType.SESSION,
1140
1143
  "SQL_DOUBLE": TokenType.DOUBLE,
1141
1144
  "SQL_VARCHAR": TokenType.VARCHAR,
1145
+ "STAGE": TokenType.STAGE,
1142
1146
  "STORAGE INTEGRATION": TokenType.STORAGE_INTEGRATION,
1147
+ "STREAMLIT": TokenType.STREAMLIT,
1143
1148
  "TAG": TokenType.TAG,
1144
1149
  "TIMESTAMP_TZ": TokenType.TIMESTAMPTZ,
1145
1150
  "TOP": TokenType.TOP,
1146
1151
  "WAREHOUSE": TokenType.WAREHOUSE,
1147
- "STAGE": TokenType.STAGE,
1148
- "STREAMLIT": TokenType.STREAMLIT,
1149
1152
  }
1150
1153
  KEYWORDS.pop("/*+")
1151
1154
 
@@ -1213,6 +1216,7 @@ class Snowflake(Dialect):
1213
1216
  exp.Extract: lambda self, e: self.func(
1214
1217
  "DATE_PART", map_date_part(e.this, self.dialect), e.expression
1215
1218
  ),
1219
+ exp.EuclideanDistance: rename_func("VECTOR_L2_DISTANCE"),
1216
1220
  exp.FileFormatProperty: lambda self,
1217
1221
  e: f"FILE_FORMAT=({self.expressions(e, 'expressions', sep=' ')})",
1218
1222
  exp.FromTimeZone: lambda self, e: self.func(
sqlglot/dialects/spark.py CHANGED
@@ -129,6 +129,9 @@ class Spark(Spark2):
129
129
  "DATEADD": _build_dateadd,
130
130
  "TIMESTAMPADD": _build_dateadd,
131
131
  "TIMESTAMPDIFF": build_date_delta(exp.TimestampDiff),
132
+ "TRY_ADD": exp.SafeAdd.from_arg_list,
133
+ "TRY_MULTIPLY": exp.SafeMultiply.from_arg_list,
134
+ "TRY_SUBTRACT": exp.SafeSubtract.from_arg_list,
132
135
  "DATEDIFF": _build_datediff,
133
136
  "DATE_DIFF": _build_datediff,
134
137
  "LISTAGG": exp.GroupConcat.from_arg_list,
@@ -202,6 +205,9 @@ class Spark(Spark2):
202
205
  exp.EndsWith: rename_func("ENDSWITH"),
203
206
  exp.PartitionedByProperty: lambda self,
204
207
  e: f"PARTITIONED BY {self.wrap(self.expressions(sqls=[_normalize_partition(e) for e in e.this.expressions], skip_first=True))}",
208
+ exp.SafeAdd: rename_func("TRY_ADD"),
209
+ exp.SafeMultiply: rename_func("TRY_MULTIPLY"),
210
+ exp.SafeSubtract: rename_func("TRY_SUBTRACT"),
205
211
  exp.StartsWith: rename_func("STARTSWITH"),
206
212
  exp.TimeAdd: date_delta_to_binary_interval_op(cast=False),
207
213
  exp.TimeSub: date_delta_to_binary_interval_op(cast=False),
@@ -187,6 +187,7 @@ class Spark2(Hive):
187
187
  "DAYOFYEAR": lambda args: exp.DayOfYear(this=exp.TsOrDsToDate(this=seq_get(args, 0))),
188
188
  "DOUBLE": _build_as_cast("double"),
189
189
  "FLOAT": _build_as_cast("float"),
190
+ "FORMAT_STRING": exp.Format.from_arg_list,
190
191
  "FROM_UTC_TIMESTAMP": lambda args, dialect: exp.AtTimeZone(
191
192
  this=exp.cast(
192
193
  seq_get(args, 0) or exp.Var(this=""),
@@ -292,6 +293,7 @@ class Spark2(Hive):
292
293
  # (DAY_OF_WEEK(datetime) % 7) + 1 is equivalent to DAYOFWEEK_ISO(datetime)
293
294
  exp.DayOfWeekIso: lambda self, e: f"(({self.func('DAYOFWEEK', e.this)} % 7) + 1)",
294
295
  exp.DayOfYear: rename_func("DAYOFYEAR"),
296
+ exp.Format: rename_func("FORMAT_STRING"),
295
297
  exp.From: transforms.preprocess([_unalias_pivot]),
296
298
  exp.FromTimeZone: lambda self, e: self.func(
297
299
  "TO_UTC_TIMESTAMP", e.this, e.args.get("zone")
@@ -156,6 +156,7 @@ class SQLite(Dialect):
156
156
  EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
157
157
  SUPPORTS_MEDIAN = False
158
158
  JSON_KEY_VALUE_PAIR_SEP = ","
159
+ PARSE_JSON_NAME: t.Optional[str] = None
159
160
 
160
161
  SUPPORTED_JSON_PATH_PARTS = {
161
162
  exp.JSONPathKey,