sqlglot 27.11.0__py3-none-any.whl → 27.13.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
 
@@ -20,11 +22,13 @@ from sqlglot.generator import unsupported_args
20
22
  from sqlglot.helper import seq_get
21
23
 
22
24
 
23
- def cast_to_time6(expression: t.Optional[exp.Expression]) -> exp.Cast:
25
+ def cast_to_time6(
26
+ expression: t.Optional[exp.Expression], time_type: DataType.Type = exp.DataType.Type.TIME
27
+ ) -> exp.Cast:
24
28
  return exp.Cast(
25
29
  this=expression,
26
30
  to=exp.DataType.build(
27
- exp.DataType.Type.TIME,
31
+ time_type,
28
32
  expressions=[exp.DataTypeParam(this=exp.Literal.number(6))],
29
33
  ),
30
34
  )
@@ -59,6 +63,9 @@ class SingleStore(MySQL):
59
63
  "BSON": TokenType.JSONB,
60
64
  "GEOGRAPHYPOINT": TokenType.GEOGRAPHYPOINT,
61
65
  "TIMESTAMP": TokenType.TIMESTAMP,
66
+ "UTC_DATE": TokenType.UTC_DATE,
67
+ "UTC_TIME": TokenType.UTC_TIME,
68
+ "UTC_TIMESTAMP": TokenType.UTC_TIMESTAMP,
62
69
  ":>": TokenType.COLON_GT,
63
70
  "!:>": TokenType.NCOLON_GT,
64
71
  "::$": TokenType.DCOLONDOLLAR,
@@ -158,6 +165,8 @@ class SingleStore(MySQL):
158
165
  json_type="JSON",
159
166
  ),
160
167
  "JSON_PRETTY": exp.JSONFormat.from_arg_list,
168
+ "JSON_BUILD_ARRAY": lambda args: exp.JSONArray(expressions=args),
169
+ "JSON_BUILD_OBJECT": lambda args: exp.JSONObject(expressions=args),
161
170
  "DATE": exp.Date.from_arg_list,
162
171
  "DAYNAME": lambda args: exp.TimeToStr(
163
172
  this=seq_get(args, 0),
@@ -195,6 +204,21 @@ class SingleStore(MySQL):
195
204
  ),
196
205
  }
197
206
 
207
+ FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
208
+ **MySQL.Parser.FUNCTION_PARSERS,
209
+ "JSON_AGG": lambda self: exp.JSONArrayAgg(
210
+ this=self._parse_term(),
211
+ order=self._parse_order(),
212
+ ),
213
+ }
214
+
215
+ NO_PAREN_FUNCTIONS = {
216
+ **MySQL.Parser.NO_PAREN_FUNCTIONS,
217
+ TokenType.UTC_DATE: exp.UtcDate,
218
+ TokenType.UTC_TIME: exp.UtcTime,
219
+ TokenType.UTC_TIMESTAMP: exp.UtcTimestamp,
220
+ }
221
+
198
222
  CAST_COLUMN_OPERATORS = {TokenType.COLON_GT, TokenType.NCOLON_GT}
199
223
 
200
224
  COLUMN_OPERATORS = {
@@ -226,6 +250,17 @@ class SingleStore(MySQL):
226
250
  COLUMN_OPERATORS.pop(TokenType.PLACEHOLDER)
227
251
 
228
252
  class Generator(MySQL.Generator):
253
+ SUPPORTS_UESCAPE = False
254
+ NULL_ORDERING_SUPPORTED = True
255
+ MATCH_AGAINST_TABLE_PREFIX = "TABLE "
256
+
257
+ @staticmethod
258
+ def _unicode_substitute(m: re.Match[str]) -> str:
259
+ # Interpret the number as hex and convert it to the Unicode string
260
+ return chr(int(m.group(1), 16))
261
+
262
+ UNICODE_SUBSTITUTE: t.Optional[t.Callable[[re.Match[str]], str]] = _unicode_substitute
263
+
229
264
  SUPPORTED_JSON_PATH_PARTS = {
230
265
  exp.JSONPathKey,
231
266
  exp.JSONPathRoot,
@@ -264,6 +299,9 @@ class SingleStore(MySQL):
264
299
  exp.TryCast: unsupported_args("format", "action", "default")(
265
300
  lambda self, e: f"{self.sql(e, 'this')} !:> {self.sql(e, 'to')}"
266
301
  ),
302
+ exp.CastToStrType: lambda self, e: self.sql(
303
+ exp.cast(e.this, DataType.build(e.args["to"].name))
304
+ ),
267
305
  exp.StrToUnix: unsupported_args("format")(rename_func("UNIX_TIMESTAMP")),
268
306
  exp.TimeToUnix: rename_func("UNIX_TIMESTAMP"),
269
307
  exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"),
@@ -313,7 +351,15 @@ class SingleStore(MySQL):
313
351
  if e.unit is not None
314
352
  else self.func("DATEDIFF", e.this, e.expression)
315
353
  ),
354
+ exp.TsOrDsDiff: lambda self, e: timestampdiff_sql(self, e)
355
+ if e.unit is not None
356
+ else self.func("DATEDIFF", e.this, e.expression),
316
357
  exp.TimestampTrunc: unsupported_args("zone")(timestamptrunc_sql()),
358
+ exp.CurrentDatetime: lambda self, e: self.sql(
359
+ cast_to_time6(
360
+ exp.CurrentTimestamp(this=exp.Literal.number(6)), exp.DataType.Type.DATETIME
361
+ )
362
+ ),
317
363
  exp.JSONExtract: unsupported_args(
318
364
  "only_json_types",
319
365
  "expressions",
@@ -329,6 +375,21 @@ class SingleStore(MySQL):
329
375
  exp.JSONPathSubscript: lambda self, e: self.json_path_part(e.this),
330
376
  exp.JSONPathRoot: lambda *_: "",
331
377
  exp.JSONFormat: unsupported_args("options", "is_json")(rename_func("JSON_PRETTY")),
378
+ exp.JSONArrayAgg: unsupported_args("null_handling", "return_type", "strict")(
379
+ lambda self, e: self.func("JSON_AGG", e.this, suffix=f"{self.sql(e, 'order')})")
380
+ ),
381
+ exp.JSONArray: unsupported_args("null_handling", "return_type", "strict")(
382
+ rename_func("JSON_BUILD_ARRAY")
383
+ ),
384
+ exp.JSONBExists: lambda self, e: self.func(
385
+ "BSON_MATCH_ANY_EXISTS", e.this, e.args.get("path")
386
+ ),
387
+ exp.JSONExists: unsupported_args("passing", "on_condition")(
388
+ lambda self, e: self.func("JSON_MATCH_ANY_EXISTS", e.this, e.args.get("path"))
389
+ ),
390
+ exp.JSONObject: unsupported_args(
391
+ "null_handling", "unique_keys", "return_type", "encoding"
392
+ )(rename_func("JSON_BUILD_OBJECT")),
332
393
  exp.DayOfWeekIso: lambda self, e: f"(({self.func('DAYOFWEEK', e.this)} % 7) + 1)",
333
394
  exp.DayOfMonth: rename_func("DAY"),
334
395
  exp.Hll: rename_func("APPROX_COUNT_DISTINCT"),
@@ -347,6 +408,9 @@ class SingleStore(MySQL):
347
408
  exp.Variance: rename_func("VAR_SAMP"),
348
409
  exp.VariancePop: rename_func("VAR_POP"),
349
410
  exp.Xor: bool_xor_sql,
411
+ exp.Cbrt: lambda self, e: self.sql(
412
+ exp.Pow(this=e.this, expression=exp.Literal.number(1) / exp.Literal.number(3))
413
+ ),
350
414
  exp.RegexpLike: lambda self, e: self.binary(e, "RLIKE"),
351
415
  exp.Repeat: lambda self, e: self.func(
352
416
  "LPAD",
@@ -382,13 +446,30 @@ class SingleStore(MySQL):
382
446
  exp.FromBase: lambda self, e: self.func(
383
447
  "CONV", e.this, e.expression, exp.Literal.number(10)
384
448
  ),
449
+ exp.RegexpILike: lambda self, e: self.binary(
450
+ exp.RegexpLike(
451
+ this=exp.Lower(this=e.this),
452
+ expression=exp.Lower(this=e.expression),
453
+ ),
454
+ "RLIKE",
455
+ ),
456
+ exp.Stuff: lambda self, e: self.func(
457
+ "CONCAT",
458
+ self.func("SUBSTRING", e.this, exp.Literal.number(1), e.args.get("start") - 1),
459
+ e.expression,
460
+ self.func("SUBSTRING", e.this, e.args.get("start") + e.args.get("length")),
461
+ ),
385
462
  exp.Reduce: unsupported_args("finish")(
386
463
  lambda self, e: self.func(
387
464
  "REDUCE", e.args.get("initial"), e.this, e.args.get("merge")
388
465
  )
389
466
  ),
467
+ exp.MatchAgainst: unsupported_args("modifier")(
468
+ lambda self, e: super().matchagainst_sql(e)
469
+ ),
390
470
  }
391
471
  TRANSFORMS.pop(exp.JSONExtractScalar)
472
+ TRANSFORMS.pop(exp.CurrentDate)
392
473
 
393
474
  UNSUPPORTED_TYPES = {
394
475
  exp.DataType.Type.ARRAY,
@@ -1606,3 +1687,57 @@ class SingleStore(MySQL):
1606
1687
  return f"DECIMAL({precision})"
1607
1688
 
1608
1689
  return super().datatype_sql(expression)
1690
+
1691
+ def collate_sql(self, expression: exp.Collate) -> str:
1692
+ # SingleStore does not support setting a collation for column in the SELECT query,
1693
+ # so we cast column to a LONGTEXT type with specific collation
1694
+ return self.binary(expression, ":> LONGTEXT COLLATE")
1695
+
1696
+ def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1697
+ timezone = expression.this
1698
+ if timezone:
1699
+ if isinstance(timezone, exp.Literal) and timezone.name.lower() == "utc":
1700
+ return self.func("UTC_DATE")
1701
+ self.unsupported("CurrentDate with timezone is not supported in SingleStore")
1702
+
1703
+ return self.func("CURRENT_DATE")
1704
+
1705
+ def currenttime_sql(self, expression: exp.CurrentTime) -> str:
1706
+ arg = expression.this
1707
+ if arg:
1708
+ if isinstance(arg, exp.Literal) and arg.name.lower() == "utc":
1709
+ return self.func("UTC_TIME")
1710
+ if isinstance(arg, exp.Literal) and arg.is_number:
1711
+ return self.func("CURRENT_TIME", arg)
1712
+ self.unsupported("CurrentTime with timezone is not supported in SingleStore")
1713
+
1714
+ return self.func("CURRENT_TIME")
1715
+
1716
+ def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
1717
+ arg = expression.this
1718
+ if arg:
1719
+ if isinstance(arg, exp.Literal) and arg.name.lower() == "utc":
1720
+ return self.func("UTC_TIMESTAMP")
1721
+ if isinstance(arg, exp.Literal) and arg.is_number:
1722
+ return self.func("CURRENT_TIMESTAMP", arg)
1723
+ self.unsupported("CurrentTimestamp with timezone is not supported in SingleStore")
1724
+
1725
+ return self.func("CURRENT_TIMESTAMP")
1726
+
1727
+ def standardhash_sql(self, expression: exp.StandardHash) -> str:
1728
+ hash_function = expression.expression
1729
+ if hash_function is None:
1730
+ return self.func("SHA", expression.this)
1731
+ if isinstance(hash_function, exp.Literal):
1732
+ if hash_function.name.lower() == "sha":
1733
+ return self.func("SHA", expression.this)
1734
+ if hash_function.name.lower() == "md5":
1735
+ return self.func("MD5", expression.this)
1736
+
1737
+ self.unsupported(
1738
+ f"{hash_function.this} hash method is not supported in SingleStore"
1739
+ )
1740
+ return self.func("SHA", expression.this)
1741
+
1742
+ self.unsupported("STANDARD_HASH function is not supported in SingleStore")
1743
+ return self.func("SHA", expression.this)
@@ -496,6 +496,15 @@ class Snowflake(Dialect):
496
496
  ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN = False
497
497
  TRY_CAST_REQUIRES_STRING = True
498
498
 
499
+ ANNOTATORS = {
500
+ **Dialect.ANNOTATORS,
501
+ **{
502
+ expr_type: lambda self, e: self._annotate_by_args(e, "this")
503
+ for expr_type in (exp.Reverse,)
504
+ },
505
+ exp.ConcatWs: lambda self, e: self._annotate_by_args(e, "expressions"),
506
+ }
507
+
499
508
  TIME_MAPPING = {
500
509
  "YYYY": "%Y",
501
510
  "yyyy": "%Y",
@@ -574,6 +583,7 @@ class Snowflake(Dialect):
574
583
  end=exp.Sub(this=seq_get(args, 1), expression=exp.Literal.number(1)),
575
584
  step=seq_get(args, 2),
576
585
  ),
586
+ "ARRAY_SORT": exp.SortArray.from_arg_list,
577
587
  "BITXOR": _build_bitwise(exp.BitwiseXor, "BITXOR"),
578
588
  "BIT_XOR": _build_bitwise(exp.BitwiseXor, "BITXOR"),
579
589
  "BITOR": _build_bitwise(exp.BitwiseOr, "BITOR"),
@@ -582,6 +592,18 @@ class Snowflake(Dialect):
582
592
  "BIT_SHIFTLEFT": _build_bitwise(exp.BitwiseLeftShift, "BIT_SHIFTLEFT"),
583
593
  "BITSHIFTRIGHT": _build_bitwise(exp.BitwiseRightShift, "BITSHIFTRIGHT"),
584
594
  "BIT_SHIFTRIGHT": _build_bitwise(exp.BitwiseRightShift, "BIT_SHIFTRIGHT"),
595
+ "BITANDAGG": exp.BitwiseAndAgg.from_arg_list,
596
+ "BITAND_AGG": exp.BitwiseAndAgg.from_arg_list,
597
+ "BIT_AND_AGG": exp.BitwiseAndAgg.from_arg_list,
598
+ "BIT_ANDAGG": exp.BitwiseAndAgg.from_arg_list,
599
+ "BITORAGG": exp.BitwiseOrAgg.from_arg_list,
600
+ "BITOR_AGG": exp.BitwiseOrAgg.from_arg_list,
601
+ "BIT_OR_AGG": exp.BitwiseOrAgg.from_arg_list,
602
+ "BIT_ORAGG": exp.BitwiseOrAgg.from_arg_list,
603
+ "BITXORAGG": exp.BitwiseXorAgg.from_arg_list,
604
+ "BITXOR_AGG": exp.BitwiseXorAgg.from_arg_list,
605
+ "BIT_XOR_AGG": exp.BitwiseXorAgg.from_arg_list,
606
+ "BIT_XORAGG": exp.BitwiseXorAgg.from_arg_list,
585
607
  "BOOLXOR": _build_bitwise(exp.Xor, "BOOLXOR"),
586
608
  "DATE": _build_datetime("DATE", exp.DataType.Type.DATE),
587
609
  "DATE_TRUNC": _date_trunc_to_time,
@@ -643,6 +665,7 @@ class Snowflake(Dialect):
643
665
  "TO_TIMESTAMP_NTZ": _build_datetime("TO_TIMESTAMP_NTZ", exp.DataType.Type.TIMESTAMP),
644
666
  "TO_TIMESTAMP_TZ": _build_datetime("TO_TIMESTAMP_TZ", exp.DataType.Type.TIMESTAMPTZ),
645
667
  "TO_VARCHAR": exp.ToChar.from_arg_list,
668
+ "VECTOR_L2_DISTANCE": exp.EuclideanDistance.from_arg_list,
646
669
  "ZEROIFNULL": _build_if_from_zeroifnull,
647
670
  }
648
671
 
@@ -659,6 +682,7 @@ class Snowflake(Dialect):
659
682
 
660
683
  ALTER_PARSERS = {
661
684
  **parser.Parser.ALTER_PARSERS,
685
+ "SESSION": lambda self: self._parse_alter_session(),
662
686
  "UNSET": lambda self: self.expression(
663
687
  exp.Set,
664
688
  tag=self._match_text_seq("TAG"),
@@ -1124,9 +1148,8 @@ class Snowflake(Dialect):
1124
1148
 
1125
1149
  KEYWORDS = {
1126
1150
  **tokens.Tokenizer.KEYWORDS,
1127
- "FILE://": TokenType.URI_START,
1128
1151
  "BYTEINT": TokenType.INT,
1129
- "EXCLUDE": TokenType.EXCEPT,
1152
+ "FILE://": TokenType.URI_START,
1130
1153
  "FILE FORMAT": TokenType.FILE_FORMAT,
1131
1154
  "GET": TokenType.GET,
1132
1155
  "MATCH_CONDITION": TokenType.MATCH_CONDITION,
@@ -1138,15 +1161,16 @@ class Snowflake(Dialect):
1138
1161
  "RM": TokenType.COMMAND,
1139
1162
  "SAMPLE": TokenType.TABLE_SAMPLE,
1140
1163
  "SEMANTIC VIEW": TokenType.SEMANTIC_VIEW,
1164
+ "SESSION": TokenType.SESSION,
1141
1165
  "SQL_DOUBLE": TokenType.DOUBLE,
1142
1166
  "SQL_VARCHAR": TokenType.VARCHAR,
1167
+ "STAGE": TokenType.STAGE,
1143
1168
  "STORAGE INTEGRATION": TokenType.STORAGE_INTEGRATION,
1169
+ "STREAMLIT": TokenType.STREAMLIT,
1144
1170
  "TAG": TokenType.TAG,
1145
1171
  "TIMESTAMP_TZ": TokenType.TIMESTAMPTZ,
1146
1172
  "TOP": TokenType.TOP,
1147
1173
  "WAREHOUSE": TokenType.WAREHOUSE,
1148
- "STAGE": TokenType.STAGE,
1149
- "STREAMLIT": TokenType.STREAMLIT,
1150
1174
  }
1151
1175
  KEYWORDS.pop("/*+")
1152
1176
 
@@ -1198,6 +1222,10 @@ class Snowflake(Dialect):
1198
1222
  ),
1199
1223
  exp.BitwiseOr: rename_func("BITOR"),
1200
1224
  exp.BitwiseXor: rename_func("BITXOR"),
1225
+ exp.BitwiseAnd: rename_func("BITAND"),
1226
+ exp.BitwiseAndAgg: rename_func("BITANDAGG"),
1227
+ exp.BitwiseOrAgg: rename_func("BITORAGG"),
1228
+ exp.BitwiseXorAgg: rename_func("BITXORAGG"),
1201
1229
  exp.BitwiseLeftShift: rename_func("BITSHIFTLEFT"),
1202
1230
  exp.BitwiseRightShift: rename_func("BITSHIFTRIGHT"),
1203
1231
  exp.Create: transforms.preprocess([_flatten_structured_types_unless_iceberg]),
@@ -1214,6 +1242,7 @@ class Snowflake(Dialect):
1214
1242
  exp.Extract: lambda self, e: self.func(
1215
1243
  "DATE_PART", map_date_part(e.this, self.dialect), e.expression
1216
1244
  ),
1245
+ exp.EuclideanDistance: rename_func("VECTOR_L2_DISTANCE"),
1217
1246
  exp.FileFormatProperty: lambda self,
1218
1247
  e: f"FILE_FORMAT=({self.expressions(e, 'expressions', sep=' ')})",
1219
1248
  exp.FromTimeZone: lambda self, e: self.func(
@@ -1269,6 +1298,7 @@ class Snowflake(Dialect):
1269
1298
  ]
1270
1299
  ),
1271
1300
  exp.SHA: rename_func("SHA1"),
1301
+ exp.SortArray: rename_func("ARRAY_SORT"),
1272
1302
  exp.StarMap: rename_func("OBJECT_CONSTRUCT"),
1273
1303
  exp.StartsWith: rename_func("STARTSWITH"),
1274
1304
  exp.EndsWith: rename_func("ENDSWITH"),
sqlglot/dialects/spark.py CHANGED
@@ -125,10 +125,17 @@ class Spark(Spark2):
125
125
  FUNCTIONS = {
126
126
  **Spark2.Parser.FUNCTIONS,
127
127
  "ANY_VALUE": _build_with_ignore_nulls(exp.AnyValue),
128
+ "BIT_AND": exp.BitwiseAndAgg.from_arg_list,
129
+ "BIT_OR": exp.BitwiseOrAgg.from_arg_list,
130
+ "BIT_XOR": exp.BitwiseXorAgg.from_arg_list,
131
+ "BIT_COUNT": exp.BitwiseCountAgg.from_arg_list,
128
132
  "DATE_ADD": _build_dateadd,
129
133
  "DATEADD": _build_dateadd,
130
134
  "TIMESTAMPADD": _build_dateadd,
131
135
  "TIMESTAMPDIFF": build_date_delta(exp.TimestampDiff),
136
+ "TRY_ADD": exp.SafeAdd.from_arg_list,
137
+ "TRY_MULTIPLY": exp.SafeMultiply.from_arg_list,
138
+ "TRY_SUBTRACT": exp.SafeSubtract.from_arg_list,
132
139
  "DATEDIFF": _build_datediff,
133
140
  "DATE_DIFF": _build_datediff,
134
141
  "LISTAGG": exp.GroupConcat.from_arg_list,
@@ -186,6 +193,10 @@ class Spark(Spark2):
186
193
  exp.ArrayConstructCompact: lambda self, e: self.func(
187
194
  "ARRAY_COMPACT", self.func("ARRAY", *e.expressions)
188
195
  ),
196
+ exp.BitwiseAndAgg: rename_func("BIT_AND"),
197
+ exp.BitwiseOrAgg: rename_func("BIT_OR"),
198
+ exp.BitwiseXorAgg: rename_func("BIT_XOR"),
199
+ exp.BitwiseCountAgg: rename_func("BIT_COUNT"),
189
200
  exp.Create: preprocess(
190
201
  [
191
202
  remove_unique_constraints,
@@ -202,6 +213,9 @@ class Spark(Spark2):
202
213
  exp.EndsWith: rename_func("ENDSWITH"),
203
214
  exp.PartitionedByProperty: lambda self,
204
215
  e: f"PARTITIONED BY {self.wrap(self.expressions(sqls=[_normalize_partition(e) for e in e.this.expressions], skip_first=True))}",
216
+ exp.SafeAdd: rename_func("TRY_ADD"),
217
+ exp.SafeMultiply: rename_func("TRY_MULTIPLY"),
218
+ exp.SafeSubtract: rename_func("TRY_SUBTRACT"),
205
219
  exp.StartsWith: rename_func("STARTSWITH"),
206
220
  exp.TimeAdd: date_delta_to_binary_interval_op(cast=False),
207
221
  exp.TimeSub: date_delta_to_binary_interval_op(cast=False),
sqlglot/expressions.py CHANGED
@@ -4905,7 +4905,7 @@ class Rollback(Expression):
4905
4905
 
4906
4906
  class Alter(Expression):
4907
4907
  arg_types = {
4908
- "this": True,
4908
+ "this": False,
4909
4909
  "kind": True,
4910
4910
  "actions": True,
4911
4911
  "exists": False,
@@ -4926,6 +4926,10 @@ class Alter(Expression):
4926
4926
  return self.args.get("actions") or []
4927
4927
 
4928
4928
 
4929
+ class AlterSession(Expression):
4930
+ arg_types = {"expressions": True, "unset": False}
4931
+
4932
+
4929
4933
  class Analyze(Expression):
4930
4934
  arg_types = {
4931
4935
  "kind": False,
@@ -5443,24 +5447,92 @@ class Typeof(Func):
5443
5447
  pass
5444
5448
 
5445
5449
 
5450
+ class Acos(Func):
5451
+ pass
5452
+
5453
+
5454
+ class Acosh(Func):
5455
+ pass
5456
+
5457
+
5458
+ class Asin(Func):
5459
+ pass
5460
+
5461
+
5462
+ class Asinh(Func):
5463
+ pass
5464
+
5465
+
5466
+ class Atan(Func):
5467
+ arg_types = {"this": True, "expression": False}
5468
+
5469
+
5470
+ class Atanh(Func):
5471
+ pass
5472
+
5473
+
5474
+ class Atan2(Func):
5475
+ arg_types = {"this": True, "expression": True}
5476
+
5477
+
5478
+ class Cot(Func):
5479
+ pass
5480
+
5481
+
5482
+ class Coth(Func):
5483
+ pass
5484
+
5485
+
5486
+ class Csc(Func):
5487
+ pass
5488
+
5489
+
5490
+ class Csch(Func):
5491
+ pass
5492
+
5493
+
5494
+ class Sec(Func):
5495
+ pass
5496
+
5497
+
5498
+ class Sech(Func):
5499
+ pass
5500
+
5501
+
5502
+ class Sin(Func):
5503
+ pass
5504
+
5505
+
5506
+ class Sinh(Func):
5507
+ pass
5508
+
5509
+
5510
+ class CosineDistance(Func):
5511
+ arg_types = {"this": True, "expression": True}
5512
+
5513
+
5514
+ class EuclideanDistance(Func):
5515
+ arg_types = {"this": True, "expression": True}
5516
+
5517
+
5446
5518
  class AggFunc(Func):
5447
5519
  pass
5448
5520
 
5449
5521
 
5450
5522
  class BitwiseAndAgg(AggFunc):
5451
- _sql_names = ["BIT_AND"]
5523
+ pass
5452
5524
 
5453
5525
 
5454
5526
  class BitwiseOrAgg(AggFunc):
5455
- _sql_names = ["BIT_OR"]
5527
+ pass
5456
5528
 
5457
5529
 
5458
5530
  class BitwiseXorAgg(AggFunc):
5459
- _sql_names = ["BIT_XOR"]
5531
+ pass
5460
5532
 
5461
5533
 
5462
5534
  class BitwiseCountAgg(AggFunc):
5463
- _sql_names = ["BIT_COUNT"]
5535
+ pass
5464
5536
 
5465
5537
 
5466
5538
  class ByteLength(Func):
@@ -5998,6 +6070,18 @@ class CurrentUser(Func):
5998
6070
  arg_types = {"this": False}
5999
6071
 
6000
6072
 
6073
+ class UtcDate(Func):
6074
+ arg_types = {}
6075
+
6076
+
6077
+ class UtcTime(Func):
6078
+ arg_types = {"this": False}
6079
+
6080
+
6081
+ class UtcTimestamp(Func):
6082
+ arg_types = {"this": False}
6083
+
6084
+
6001
6085
  class DateAdd(Func, IntervalOp):
6002
6086
  arg_types = {"this": True, "expression": True, "unit": False}
6003
6087
 
@@ -6675,7 +6759,7 @@ class JSONBExtractScalar(Binary, Func):
6675
6759
 
6676
6760
 
6677
6761
  class JSONFormat(Func):
6678
- arg_types = {"this": False, "options": False, "is_json": False}
6762
+ arg_types = {"this": False, "options": False, "is_json": False, "to_json": False}
6679
6763
  _sql_names = ["JSON_FORMAT"]
6680
6764
 
6681
6765
 
@@ -6952,6 +7036,10 @@ class RangeN(Func):
6952
7036
  arg_types = {"this": True, "expressions": True, "each": False}
6953
7037
 
6954
7038
 
7039
+ class RangeBucket(Func):
7040
+ arg_types = {"this": True, "expression": True}
7041
+
7042
+
6955
7043
  class Rank(AggFunc):
6956
7044
  arg_types = {"expressions": False}
6957
7045
  is_var_len_args = True
@@ -7045,10 +7133,26 @@ class RowNumber(Func):
7045
7133
  arg_types = {"this": False}
7046
7134
 
7047
7135
 
7136
+ class SafeAdd(Func):
7137
+ arg_types = {"this": True, "expression": True}
7138
+
7139
+
7048
7140
  class SafeDivide(Func):
7049
7141
  arg_types = {"this": True, "expression": True}
7050
7142
 
7051
7143
 
7144
+ class SafeMultiply(Func):
7145
+ arg_types = {"this": True, "expression": True}
7146
+
7147
+
7148
+ class SafeNegate(Func):
7149
+ pass
7150
+
7151
+
7152
+ class SafeSubtract(Func):
7153
+ arg_types = {"this": True, "expression": True}
7154
+
7155
+
7052
7156
  class SafeConvertBytesToString(Func):
7053
7157
  pass
7054
7158
 
@@ -7067,7 +7171,7 @@ class Sign(Func):
7067
7171
 
7068
7172
 
7069
7173
  class SortArray(Func):
7070
- arg_types = {"this": True, "asc": False}
7174
+ arg_types = {"this": True, "asc": False, "nulls_first": False}
7071
7175
 
7072
7176
 
7073
7177
  class Soundex(Func):
sqlglot/generator.py CHANGED
@@ -216,6 +216,11 @@ class Generator(metaclass=_Generator):
216
216
  exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
217
217
  exp.Uuid: lambda *_: "UUID()",
218
218
  exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
219
+ exp.UtcDate: lambda self, e: self.sql(exp.CurrentDate(this=exp.Literal.string("UTC"))),
220
+ exp.UtcTime: lambda self, e: self.sql(exp.CurrentTime(this=exp.Literal.string("UTC"))),
221
+ exp.UtcTimestamp: lambda self, e: self.sql(
222
+ exp.CurrentTimestamp(this=exp.Literal.string("UTC"))
223
+ ),
219
224
  exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
220
225
  exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
221
226
  exp.VolatileProperty: lambda *_: "VOLATILE",
@@ -433,6 +438,9 @@ class Generator(metaclass=_Generator):
433
438
  # Whether the UESCAPE syntax in unicode strings is supported
434
439
  SUPPORTS_UESCAPE = True
435
440
 
441
+ # Function used to replace escaped unicode codes in unicode strings
442
+ UNICODE_SUBSTITUTE: t.Optional[t.Callable[[re.Match[str]], str]] = None
443
+
436
444
  # The keyword to use when generating a star projection with excluded columns
437
445
  STAR_EXCEPT = "EXCEPT"
438
446
 
@@ -495,6 +503,9 @@ class Generator(metaclass=_Generator):
495
503
  # Whether LIKE and ILIKE support quantifiers such as LIKE ANY/ALL/SOME
496
504
  SUPPORTS_LIKE_QUANTIFIERS = True
497
505
 
506
+ # Prefix which is appended to exp.Table expressions in MATCH AGAINST
507
+ MATCH_AGAINST_TABLE_PREFIX: t.Optional[str] = None
508
+
498
509
  TYPE_MAPPING = {
499
510
  exp.DataType.Type.DATETIME2: "TIMESTAMP",
500
511
  exp.DataType.Type.NCHAR: "CHAR",
@@ -1384,7 +1395,7 @@ class Generator(metaclass=_Generator):
1384
1395
  escape_sql = ""
1385
1396
 
1386
1397
  if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1387
- this = escape_pattern.sub(escape_substitute, this)
1398
+ this = escape_pattern.sub(self.UNICODE_SUBSTITUTE or escape_substitute, this)
1388
1399
 
1389
1400
  return f"{left_quote}{this}{right_quote}{escape_sql}"
1390
1401
 
@@ -3063,9 +3074,21 @@ class Generator(metaclass=_Generator):
3063
3074
  return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
3064
3075
 
3065
3076
  def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
3077
+ if self.MATCH_AGAINST_TABLE_PREFIX:
3078
+ expressions = []
3079
+ for expr in expression.expressions:
3080
+ if isinstance(expr, exp.Table):
3081
+ expressions.append(f"TABLE {self.sql(expr)}")
3082
+ else:
3083
+ expressions.append(expr)
3084
+ else:
3085
+ expressions = expression.expressions
3086
+
3066
3087
  modifier = expression.args.get("modifier")
3067
3088
  modifier = f" {modifier}" if modifier else ""
3068
- return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
3089
+ return (
3090
+ f"{self.func('MATCH', *expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
3091
+ )
3069
3092
 
3070
3093
  def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
3071
3094
  return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
@@ -3444,7 +3467,9 @@ class Generator(metaclass=_Generator):
3444
3467
  return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3445
3468
 
3446
3469
  def transaction_sql(self, expression: exp.Transaction) -> str:
3447
- return "BEGIN"
3470
+ modes = self.expressions(expression, key="modes")
3471
+ modes = f" {modes}" if modes else ""
3472
+ return f"BEGIN{modes}"
3448
3473
 
3449
3474
  def commit_sql(self, expression: exp.Commit) -> str:
3450
3475
  chain = expression.args.get("chain")
@@ -3569,8 +3594,15 @@ class Generator(metaclass=_Generator):
3569
3594
  kind = self.sql(expression, "kind")
3570
3595
  not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3571
3596
  check = " WITH CHECK" if expression.args.get("check") else ""
3597
+ this = self.sql(expression, "this")
3598
+ this = f" {this}" if this else ""
3599
+
3600
+ return f"ALTER {kind}{exists}{only}{this}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
3572
3601
 
3573
- return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
3602
+ def altersession_sql(self, expression: exp.AlterSession) -> str:
3603
+ items_sql = self.expressions(expression, flat=True)
3604
+ keyword = "UNSET" if expression.args.get("unset") else "SET"
3605
+ return f"{keyword} {items_sql}"
3574
3606
 
3575
3607
  def add_column_sql(self, expression: exp.Expression) -> str:
3576
3608
  sql = self.sql(expression)