sqlglotc 30.21.0__tar.gz → 30.22.0__tar.gz

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.
Files changed (119) hide show
  1. {sqlglotc-30.21.0/sqlglotc.egg-info → sqlglotc-30.22.0}/PKG-INFO +3 -3
  2. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/setup.py +0 -1
  4. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/array.py +15 -1
  5. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/builders.py +1 -1
  6. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/ddl.py +1 -1
  7. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/properties.py +3 -5
  8. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/query.py +28 -3
  9. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/string.py +4 -0
  10. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generator.py +114 -33
  11. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/bigquery.py +4 -2
  12. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/clickhouse.py +20 -0
  13. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/doris.py +1 -0
  14. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/dremio.py +1 -0
  15. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/drill.py +7 -0
  16. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/duckdb.py +76 -9
  17. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/hive.py +1 -0
  18. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/materialize.py +6 -1
  19. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/mysql.py +25 -1
  20. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/oracle.py +1 -0
  21. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/postgres.py +15 -1
  22. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/presto.py +9 -0
  23. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/redshift.py +8 -0
  24. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/risingwave.py +2 -1
  25. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/singlestore.py +5 -1
  26. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/snowflake.py +5 -48
  27. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/sqlite.py +2 -1
  28. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/starrocks.py +8 -0
  29. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/trino.py +1 -0
  30. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/tsql.py +12 -4
  31. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/lineage.py +19 -6
  32. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/qualify.py +0 -3
  33. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/qualify_columns.py +93 -22
  34. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/qualify_tables.py +32 -8
  35. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/resolver.py +29 -25
  36. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/scope.py +31 -13
  37. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/simplify.py +9 -2
  38. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parser.py +243 -75
  39. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/clickhouse.py +14 -0
  40. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/doris.py +3 -0
  41. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/duckdb.py +3 -2
  42. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/mysql.py +30 -5
  43. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/postgres.py +3 -0
  44. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/snowflake.py +11 -0
  45. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/spark.py +4 -0
  46. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/spark2.py +6 -1
  47. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/starrocks.py +3 -0
  48. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/trino.py +25 -0
  49. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/tsql.py +72 -2
  50. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/tokenizer_core.py +99 -20
  51. {sqlglotc-30.21.0 → sqlglotc-30.22.0/sqlglotc.egg-info}/PKG-INFO +3 -3
  52. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglotc.egg-info/SOURCES.txt +0 -1
  53. sqlglotc-30.22.0/sqlglotc.egg-info/requires.txt +6 -0
  54. sqlglotc-30.22.0/sqlglotc.egg-info/scm_version.json +8 -0
  55. sqlglotc-30.21.0/sqlglot/schema.py +0 -813
  56. sqlglotc-30.21.0/sqlglotc.egg-info/requires.txt +0 -6
  57. sqlglotc-30.21.0/sqlglotc.egg-info/scm_version.json +0 -8
  58. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/MANIFEST.in +0 -0
  59. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/setup.cfg +0 -0
  60. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/anonymize.py +0 -0
  61. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/errors.py +0 -0
  62. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/executor/table.py +0 -0
  63. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/aggregate.py +0 -0
  64. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/constraints.py +0 -0
  65. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/core.py +0 -0
  66. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/datatypes.py +0 -0
  67. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/dml.py +0 -0
  68. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/functions.py +0 -0
  69. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/json.py +0 -0
  70. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/math.py +0 -0
  71. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/expressions/temporal.py +0 -0
  72. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/athena.py +0 -0
  73. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/databricks.py +0 -0
  74. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/dax.py +0 -0
  75. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/druid.py +0 -0
  76. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/dune.py +0 -0
  77. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/exasol.py +0 -0
  78. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/fabric.py +0 -0
  79. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/prql.py +0 -0
  80. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/python.py +0 -0
  81. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/solr.py +0 -0
  82. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/spark.py +0 -0
  83. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/spark2.py +0 -0
  84. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/tableau.py +0 -0
  85. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/generators/teradata.py +0 -0
  86. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/helper.py +0 -0
  87. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/annotate_types.py +0 -0
  88. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
  89. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  90. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  91. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/athena.py +0 -0
  92. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/base.py +0 -0
  93. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/bigquery.py +0 -0
  94. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/databricks.py +0 -0
  95. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/dax.py +0 -0
  96. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/dremio.py +0 -0
  97. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/drill.py +0 -0
  98. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/druid.py +0 -0
  99. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/dune.py +0 -0
  100. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/exasol.py +0 -0
  101. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/fabric.py +0 -0
  102. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/hive.py +0 -0
  103. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/materialize.py +0 -0
  104. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/oracle.py +0 -0
  105. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/presto.py +0 -0
  106. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/prql.py +0 -0
  107. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/redshift.py +0 -0
  108. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/risingwave.py +0 -0
  109. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/singlestore.py +0 -0
  110. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/solr.py +0 -0
  111. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/sqlite.py +0 -0
  112. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/tableau.py +0 -0
  113. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/parsers/teradata.py +0 -0
  114. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/serde.py +0 -0
  115. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/time.py +0 -0
  116. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglot/trie.py +0 -0
  117. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  118. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
  119. {sqlglotc-30.21.0 → sqlglotc-30.22.0}/sqlglotc.egg-info/top_level.txt +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlglotc
3
- Version: 30.21.0
3
+ Version: 30.22.0
4
4
  Summary: mypyc-compiled extensions for sqlglot
5
5
  Author-email: Toby Mao <toby.mao@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://sqlglot.com/
8
8
  Project-URL: Repository, https://github.com/tobymao/sqlglot
9
9
  Requires-Python: >=3.10
10
- Requires-Dist: sqlglot==30.21.0
10
+ Requires-Dist: sqlglot==30.22.0
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: setuptools>=61.0; extra == "dev"
13
13
  Requires-Dist: setuptools_scm; extra == "dev"
14
- Requires-Dist: sqlglot-mypy>=2.3.0.post2; extra == "dev"
14
+ Requires-Dist: mypy>=2.4.0; extra == "dev"
15
15
  Dynamic: requires-dist
@@ -7,7 +7,7 @@ license = "MIT"
7
7
  requires-python = ">= 3.10"
8
8
 
9
9
  [project.optional-dependencies]
10
- dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.3.0.post2"]
10
+ dev = ["setuptools >= 61.0", "setuptools_scm", "mypy >= 2.4.0"]
11
11
 
12
12
  [project.urls]
13
13
  Homepage = "https://sqlglot.com/"
@@ -17,7 +17,7 @@ Repository = "https://github.com/tobymao/sqlglot"
17
17
  requires = [
18
18
  "setuptools >= 61.0",
19
19
  "setuptools_scm",
20
- "sqlglot-mypy >= 2.3.0.post2",
20
+ "mypy >= 2.4.0",
21
21
  "types-python-dateutil",
22
22
  "sqlglot",
23
23
  ]
@@ -49,7 +49,6 @@ def _source_files(src_dir):
49
49
  "helper.py",
50
50
  "lineage.py",
51
51
  "parser.py",
52
- "schema.py",
53
52
  "serde.py",
54
53
  "time.py",
55
54
  "tokenizer_core.py",
@@ -23,6 +23,7 @@ class Array(Expression, Func):
23
23
  "expressions": False,
24
24
  "bracket_notation": False,
25
25
  "struct_name_inheritance": False,
26
+ "value_constructor": False,
26
27
  }
27
28
  is_var_len_args = True
28
29
 
@@ -84,7 +85,16 @@ class ArrayReverse(Expression, Func):
84
85
 
85
86
 
86
87
  class ArraySlice(Expression, Func):
87
- arg_types = {"this": True, "start": True, "end": False, "step": False, "zero_based": False}
88
+ """Slice by end index, or by length when is_length is set."""
89
+
90
+ arg_types = {
91
+ "this": True,
92
+ "start": True,
93
+ "end": False,
94
+ "step": False,
95
+ "zero_based": False,
96
+ "is_length": False,
97
+ }
88
98
 
89
99
 
90
100
  class ArraySort(Expression, Func):
@@ -244,6 +254,10 @@ class Inline(Expression, Func, UDTF):
244
254
  pass
245
255
 
246
256
 
257
+ class SplitToTable(Expression, Func, UDTF):
258
+ arg_types = {"this": True, "expression": True}
259
+
260
+
247
261
  class Stack(Expression, Func, UDTF):
248
262
  arg_types = {"this": True, "expressions": True}
249
263
  is_var_len_args = True
@@ -516,7 +516,7 @@ def cast(
516
516
 
517
517
 
518
518
  def table_(
519
- table: Identifier | str,
519
+ table: Identifier | str | None,
520
520
  db: Identifier | str | None = None,
521
521
  catalog: Identifier | str | None = None,
522
522
  quoted: bool | None = None,
@@ -400,7 +400,7 @@ class Alter(Expression):
400
400
  arg_types = {
401
401
  "this": False,
402
402
  "kind": True,
403
- "actions": True,
403
+ "actions": False,
404
404
  "exists": False,
405
405
  "only": False,
406
406
  "options": False,
@@ -624,7 +624,9 @@ class Properties(Expression):
624
624
  "INCLUDE": IncludeProperty,
625
625
  }
626
626
 
627
- PROPERTY_TO_NAME: t.ClassVar[dict[type[Property], str]] = {}
627
+ PROPERTY_TO_NAME: t.ClassVar[dict[type[Property], str]] = {
628
+ v: k for k, v in NAME_TO_PROPERTY.items()
629
+ }
628
630
 
629
631
  # CREATE property locations
630
632
  # Form: schema specified
@@ -652,7 +654,3 @@ class Properties(Expression):
652
654
  expressions.append(Property(this=Literal.string(key), value=convert(value)))
653
655
 
654
656
  return cls(expressions=expressions)
655
-
656
-
657
- # TODO (mypyc)
658
- Properties.PROPERTY_TO_NAME = {v: k for k, v in Properties.NAME_TO_PROPERTY.items()}
@@ -19,6 +19,7 @@ from sqlglot.expressions.core import (
19
19
  Hint,
20
20
  Identifier,
21
21
  In,
22
+ Var,
22
23
  _apply_builder,
23
24
  _apply_child_list_builder,
24
25
  _apply_list_builder,
@@ -441,11 +442,24 @@ class QueryBand(Expression):
441
442
 
442
443
 
443
444
  class RecursiveWithSearch(Expression):
444
- arg_types = {"kind": True, "this": True, "expression": True, "using": False}
445
+ arg_types = {
446
+ "kind": True,
447
+ "this": True,
448
+ "expression": True,
449
+ "to": False,
450
+ "default": False,
451
+ "using": False,
452
+ }
445
453
 
446
454
 
447
455
  class With(Expression):
448
- arg_types = {"expressions": False, "recursive": False, "search": False, "udfs": False}
456
+ arg_types = {
457
+ "expressions": False,
458
+ "recursive": False,
459
+ "search": False,
460
+ "cycle": False,
461
+ "udfs": False,
462
+ }
449
463
 
450
464
  @property
451
465
  def recursive(self) -> bool:
@@ -572,7 +586,7 @@ class ConditionalInsert(Expression):
572
586
 
573
587
 
574
588
  class MultitableInserts(Expression):
575
- arg_types = {"expressions": True, "kind": True, "source": True}
589
+ arg_types = {"with_": False, "expressions": True, "kind": False, "source": True}
576
590
 
577
591
 
578
592
  class OnCondition(Expression):
@@ -657,6 +671,11 @@ class Limit(Expression):
657
671
  "expressions": False,
658
672
  }
659
673
 
674
+ @property
675
+ def is_limit_all(self) -> bool:
676
+ limit_expression = self.args.get("expression")
677
+ return isinstance(limit_expression, Var) and limit_expression.name.upper() == "ALL"
678
+
660
679
 
661
680
  class LimitOptions(Expression):
662
681
  arg_types = {
@@ -954,6 +973,12 @@ class Get(Expression):
954
973
  arg_types = {"this": True, "target": True, "properties": False}
955
974
 
956
975
 
976
+ class ListStage(Expression):
977
+ """Lists files in a Snowflake stage or Git repository clone."""
978
+
979
+ arg_types = {"this": True, "pattern": False}
980
+
981
+
957
982
  class Table(Expression, Selectable):
958
983
  arg_types = {
959
984
  "this": False,
@@ -260,6 +260,10 @@ class Upper(Expression, Func):
260
260
  _sql_names = ["UPPER", "UCASE"]
261
261
 
262
262
 
263
+ class WeightString(Expression, Func):
264
+ arg_types = {"this": True, "to": False}
265
+
266
+
263
267
  # Encoding / base conversion
264
268
 
265
269
 
@@ -25,6 +25,8 @@ if t.TYPE_CHECKING:
25
25
 
26
26
  logger = logging.getLogger("sqlglot")
27
27
 
28
+ _HIGH_BYTE_RE = re.compile(r"[\x80-\xff]")
29
+
28
30
  ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
29
31
  UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
30
32
 
@@ -380,6 +382,12 @@ class Generator:
380
382
  # Whether comparing against booleans (e.g. x IS TRUE) is supported
381
383
  IS_BOOL_ALLOWED = True
382
384
 
385
+ # Whether casting a fractional number to an integer rounds it instead of truncating it
386
+ CAST_TO_INT_ROUNDS = False
387
+
388
+ # Whether the native integer division operator returns NULL for a zero divisor instead of raising
389
+ SAFE_INT_DIVISION = False
390
+
383
391
  # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
384
392
  DUPLICATE_KEY_UPDATE_WITH_SET = True
385
393
 
@@ -565,6 +573,9 @@ class Generator:
565
573
  # The HEX function name
566
574
  HEX_FUNC = "HEX"
567
575
 
576
+ # Function that constructs a binary value from a hex-encoded byte string.
577
+ BYTE_STRING_FUNCTION = ""
578
+
568
579
  # The keywords to use when prefixing & separating WITH based properties
569
580
  WITH_PROPERTIES_PREFIX = "WITH"
570
581
 
@@ -871,6 +882,10 @@ class Generator:
871
882
  exp.Mod,
872
883
  )
873
884
 
885
+ # ALTER options that follow the others without a comma, e.g. MySQL's
886
+ # ENGINE=InnoDB PARTITION BY LIST (...) or StarRocks' PARTITION BY ... DISTRIBUTED BY ...
887
+ ALTER_PARTITION_OPTIONS: t.ClassVar[tuple[type[exp.Expr], ...]] = ()
888
+
874
889
  SAFE_JSON_PATH_KEY_RE: t.ClassVar = exp.SAFE_IDENTIFIER_RE
875
890
 
876
891
  SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
@@ -892,6 +907,7 @@ class Generator:
892
907
  "_escaped_quote_end",
893
908
  "_escaped_byte_quote_end",
894
909
  "_escaped_identifier_end",
910
+ "_identifier_escaped_sequences",
895
911
  "_next_name",
896
912
  "_identifier_start",
897
913
  "_identifier_end",
@@ -944,6 +960,7 @@ class Generator:
944
960
  else ""
945
961
  )
946
962
  self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
963
+ self._identifier_escaped_sequences = self.dialect.IDENTIFIER_ESCAPED_SEQUENCES
947
964
 
948
965
  self._next_name = name_sequence("_t")
949
966
 
@@ -1569,8 +1586,10 @@ class Generator:
1569
1586
  )
1570
1587
  search = self.sql(expression, "search")
1571
1588
  search = f" {search}" if search else ""
1589
+ cycle = self.sql(expression, "cycle")
1590
+ cycle = f" {cycle}" if cycle else ""
1572
1591
 
1573
- sql = f"WITH {recursive}{sql}{search}" if sql else ""
1592
+ sql = f"WITH {recursive}{sql}{search}{cycle}" if sql else ""
1574
1593
  return f"{udfs} {sql}" if udfs and sql else f"{udfs}{sql}"
1575
1594
 
1576
1595
  def cte_sql(self, expression: exp.CTE) -> str:
@@ -1642,15 +1661,23 @@ class Generator:
1642
1661
 
1643
1662
  def bytestring_sql(self, expression: exp.ByteString) -> str:
1644
1663
  this = self.sql(expression, "this")
1664
+ is_bytes = bool(expression.args.get("is_bytes"))
1665
+ if is_bytes and self.BYTE_STRING_FUNCTION:
1666
+ # Hex digits can be quoted directly without escaping.
1667
+ return self.func(self.BYTE_STRING_FUNCTION, f"'{this.encode('latin-1').hex()}'")
1668
+
1645
1669
  if self.dialect.BYTE_START:
1646
1670
  escaped_byte_string = self.escape_str(
1647
1671
  this,
1648
- escape_backslash=False,
1672
+ escape_backslash=bool(
1673
+ self.dialect.tokenizer_class.NUMERIC_ESCAPES
1674
+ or self.dialect.tokenizer_class.DROP_UNKNOWN_ESCAPES
1675
+ ),
1649
1676
  delimiter=self.dialect.BYTE_END,
1650
1677
  escaped_delimiter=self._escaped_byte_quote_end,
1651
1678
  is_byte_string=True,
1679
+ is_bytes=is_bytes,
1652
1680
  )
1653
- is_bytes = expression.args.get("is_bytes", False)
1654
1681
  delimited_byte_string = (
1655
1682
  f"{self.dialect.BYTE_START}{escaped_byte_string}{self.dialect.BYTE_END}"
1656
1683
  )
@@ -1787,7 +1814,8 @@ class Generator:
1787
1814
  if type_value == exp.DType.USERDEFINED and expression.args.get("kind"):
1788
1815
  type_sql = self.sql(expression, "kind")
1789
1816
  elif type_value == exp.DType.CHARACTER_SET:
1790
- return f"CHAR CHARACTER SET {self.sql(expression, 'kind')}"
1817
+ char = f"CHAR({interior})" if interior else "CHAR"
1818
+ return f"{char} CHARACTER SET {self.sql(expression, 'kind')}"
1791
1819
  else:
1792
1820
  type_sql = (
1793
1821
  self.TYPE_MAPPING.get(type_value, type_value.value)
@@ -2031,6 +2059,9 @@ class Generator:
2031
2059
  lower = text.lower()
2032
2060
  quoted = expression.quoted
2033
2061
  text = lower if self.normalize and not quoted else text
2062
+ if self._identifier_escaped_sequences:
2063
+ for char, escaped in self._identifier_escaped_sequences.items():
2064
+ text = text.replace(char, escaped)
2034
2065
  text = text.replace(self._identifier_end, self._escaped_identifier_end)
2035
2066
  if (
2036
2067
  quoted
@@ -2985,6 +3016,9 @@ class Generator:
2985
3016
  def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2986
3017
  this = self.sql(expression, "this")
2987
3018
 
3019
+ if expression.is_limit_all and not self.dialect.SUPPORTS_LIMIT_ALL:
3020
+ return this
3021
+
2988
3022
  args = [
2989
3023
  self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2990
3024
  for e in (expression.args.get(k) for k in ("offset", "expression"))
@@ -3072,6 +3106,7 @@ class Generator:
3072
3106
  delimiter: str | None = None,
3073
3107
  escaped_delimiter: str | None = None,
3074
3108
  is_byte_string: bool = False,
3109
+ is_bytes: bool = False,
3075
3110
  ) -> str:
3076
3111
  if is_byte_string:
3077
3112
  supports_escape_sequences = self.dialect.BYTE_STRINGS_SUPPORT_ESCAPED_SEQUENCES
@@ -3084,6 +3119,10 @@ class Generator:
3084
3119
  for ch in text
3085
3120
  )
3086
3121
 
3122
+ if is_bytes and "x" in self.dialect.tokenizer_class.NUMERIC_ESCAPES:
3123
+ # Escape high bytes so they aren't re-encoded as UTF-8 characters.
3124
+ text = _HIGH_BYTE_RE.sub(lambda m: f"\\x{ord(m.group()):02x}", text)
3125
+
3087
3126
  delimiter = delimiter or self.dialect.QUOTE_END
3088
3127
  escaped_delimiter = escaped_delimiter or self._escaped_quote_end
3089
3128
 
@@ -3184,7 +3223,9 @@ class Generator:
3184
3223
  if not (isinstance(this, exp.Column) and not this.table):
3185
3224
  return None
3186
3225
 
3187
- ancestor = expression.find_ancestor(exp.Select, exp.Window)
3226
+ # Only a query's own ORDER BY can reference its projection aliases
3227
+ order = expression.parent
3228
+ ancestor = order.parent if isinstance(order, exp.Order) else None
3188
3229
  if not isinstance(ancestor, exp.Select):
3189
3230
  return None
3190
3231
 
@@ -3334,7 +3375,9 @@ class Generator:
3334
3375
  limit = exp.Limit(
3335
3376
  expression=exp.maybe_copy(count) if count is not None else exp.Literal.number(1)
3336
3377
  )
3337
- elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
3378
+ elif (
3379
+ self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit) and not limit.is_limit_all
3380
+ ):
3338
3381
  limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
3339
3382
 
3340
3383
  return csv(
@@ -4384,10 +4427,12 @@ class Generator:
4384
4427
  return f"SET {exprs}"
4385
4428
 
4386
4429
  def alter_sql(self, expression: exp.Alter) -> str:
4387
- actions = expression.args["actions"]
4430
+ actions = expression.actions
4388
4431
 
4389
- if not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and isinstance(
4390
- actions[0], exp.ColumnDef
4432
+ if (
4433
+ not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
4434
+ and actions
4435
+ and isinstance(actions[0], exp.ColumnDef)
4391
4436
  ):
4392
4437
  actions_sql = self.expressions(expression, key="actions", flat=True)
4393
4438
  actions_sql = f"ADD {actions_sql}"
@@ -4414,8 +4459,18 @@ class Generator:
4414
4459
  on_cluster = self.sql(expression, "cluster")
4415
4460
  on_cluster = f" {on_cluster}" if on_cluster else ""
4416
4461
  only = " ONLY" if expression.args.get("only") else ""
4417
- options = self.expressions(expression, key="options")
4418
- options = f", {options}" if options else ""
4462
+ options = expression.args.get("options") or []
4463
+ options_sql = self.expressions(
4464
+ sqls=[o for o in options if not isinstance(o, self.ALTER_PARTITION_OPTIONS)]
4465
+ )
4466
+ options_sql = f", {options_sql}" if options_sql and actions_sql else options_sql
4467
+ partitions_sql = self.expressions(
4468
+ sqls=[o for o in options if isinstance(o, self.ALTER_PARTITION_OPTIONS)],
4469
+ sep=" ",
4470
+ flat=True,
4471
+ )
4472
+ if partitions_sql and (actions_sql or options_sql):
4473
+ partitions_sql = f" {partitions_sql}"
4419
4474
  kind = self.sql(expression, "kind")
4420
4475
  not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
4421
4476
  check = " WITH CHECK" if expression.args.get("check") else ""
@@ -4427,7 +4482,7 @@ class Generator:
4427
4482
  this = self.sql(expression, "this")
4428
4483
  this = f" {this}" if this else ""
4429
4484
 
4430
- return f"ALTER {iceberg}{kind}{exists}{only}{this}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}{cascade}"
4485
+ return f"ALTER {iceberg}{kind}{exists}{only}{this}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options_sql}{partitions_sql}{cascade}"
4431
4486
 
4432
4487
  def altersession_sql(self, expression: exp.AlterSession) -> str:
4433
4488
  items_sql = self.expressions(expression, flat=True)
@@ -4490,12 +4545,14 @@ class Generator:
4490
4545
  return f"{this_sql} HAVING {kind} {expression_sql}"
4491
4546
 
4492
4547
  def intdiv_sql(self, expression: exp.IntDiv) -> str:
4493
- return self.sql(
4494
- exp.Cast(
4495
- this=exp.Div(this=expression.this, expression=expression.expression),
4496
- to=exp.DataType(this=exp.DType.INT),
4497
- )
4498
- )
4548
+ return self._truncated_div_sql(expression.this, expression.expression)
4549
+
4550
+ def _truncated_div_sql(self, this: exp.Expr, divisor: exp.Expr) -> str:
4551
+ div: exp.Expr = exp.Div(this=this, expression=divisor)
4552
+ if self.CAST_TO_INT_ROUNDS:
4553
+ div = exp.Trunc(this=div)
4554
+
4555
+ return self.sql(exp.Cast(this=div, to=exp.DataType(this=exp.DType.BIGINT)))
4499
4556
 
4500
4557
  def dpipe_sql(self, expression: exp.DPipe) -> str:
4501
4558
  if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
@@ -4514,12 +4571,14 @@ class Generator:
4514
4571
 
4515
4572
  elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
4516
4573
  if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
4517
- return self.sql(
4518
- exp.cast(
4519
- l / r,
4520
- to=exp.DType.BIGINT,
4521
- )
4522
- )
4574
+ # Native integer division depends on the target's operand types, which may differ
4575
+ # from the source's (e.g. FLOOR(int) is DOUBLE in DuckDB), unless the operands are
4576
+ # integer literals or casts
4577
+ if all(e.is_int or isinstance(e, exp.Cast) for e in (l, r)) and (
4578
+ expression.args.get("safe") or not self.SAFE_INT_DIVISION
4579
+ ):
4580
+ return self.sql(exp.IntDiv(this=l, expression=r))
4581
+ return self._truncated_div_sql(l, r)
4523
4582
 
4524
4583
  return self.binary(expression, "/")
4525
4584
 
@@ -4740,12 +4799,11 @@ class Generator:
4740
4799
  elif arg_value is not None:
4741
4800
  args.append(arg_value)
4742
4801
 
4743
- if self.dialect.PRESERVE_ORIGINAL_NAMES:
4744
- name = expression.meta_get("name") or expression.sql_name()
4745
- else:
4746
- name = expression.sql_name()
4802
+ return self.func(self.function_name(expression), *args)
4747
4803
 
4748
- return self.func(name, *args)
4804
+ def function_name(self, expression: exp.Func) -> str:
4805
+ name_key = self.dialect.ORIGINAL_NAME_META_KEY
4806
+ return (expression.meta_get(name_key) if name_key else None) or expression.sql_name()
4749
4807
 
4750
4808
  def func(
4751
4809
  self,
@@ -5745,9 +5803,18 @@ class Generator:
5745
5803
 
5746
5804
  def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
5747
5805
  kind = self.sql(expression, "kind")
5748
- expressions = self.seg(self.expressions(expression, sep=" "))
5749
- res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
5750
- return res
5806
+ source = self.sql(expression, "source")
5807
+
5808
+ if kind:
5809
+ expressions = self.seg(self.expressions(expression, sep=" "))
5810
+ sql = f"INSERT {kind}{expressions}{self.seg(source)}"
5811
+ else:
5812
+ # If kind isn't present, we have Hive's FROM-first form:
5813
+ # FROM x INSERT ... SELECT ... [INSERT ... SELECT ...]
5814
+ inserts = self.sep().join(self.sql(e) for e in expression.expressions)
5815
+ sql = f"FROM {source}{self.seg(inserts)}"
5816
+
5817
+ return self.prepend_ctes(expression, sql)
5751
5818
 
5752
5819
  def oncondition_sql(self, expression: exp.OnCondition) -> str:
5753
5820
  # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
@@ -6183,12 +6250,16 @@ class Generator:
6183
6250
  kind = self.sql(expression, "kind")
6184
6251
  this = self.sql(expression, "this")
6185
6252
  set = self.sql(expression, "expression")
6253
+ to = self.sql(expression, "to")
6254
+ to = f" TO {to}" if to else ""
6255
+ default = self.sql(expression, "default")
6256
+ default = f" DEFAULT {default}" if default else ""
6186
6257
  using = self.sql(expression, "using")
6187
6258
  using = f" USING {using}" if using else ""
6188
6259
 
6189
6260
  kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
6190
6261
 
6191
- return f"{kind_sql} {this} SET {set}{using}"
6262
+ return f"{kind_sql} {this} SET {set}{to}{default}{using}"
6192
6263
 
6193
6264
  def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
6194
6265
  params = self.expressions(expression, key="params", flat=True)
@@ -6211,6 +6282,11 @@ class Generator:
6211
6282
  self.unsupported("Unsupported INSTALL statement")
6212
6283
  return ""
6213
6284
 
6285
+ def liststage_sql(self, expression: exp.ListStage) -> str:
6286
+ pattern = self.sql(expression, "pattern")
6287
+ pattern = f" PATTERN = {pattern}" if pattern else ""
6288
+ return f"LIST {self.sql(expression, 'this')}{pattern}"
6289
+
6214
6290
  def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
6215
6291
  # Snowflake GET/PUT statements:
6216
6292
  # PUT <file> <internalStage> <properties>
@@ -6389,6 +6465,11 @@ class Generator:
6389
6465
  using = f" USING {charset}" if charset else ""
6390
6466
  return self.func(name, this + using)
6391
6467
 
6468
+ def weightstring_sql(self, expression: exp.WeightString) -> str:
6469
+ to = self.sql(expression, "to")
6470
+ to = f" AS {to}" if to else ""
6471
+ return self.func("WEIGHT_STRING", f"{self.sql(expression, 'this')}{to}")
6472
+
6392
6473
  def block_sql(self, expression: exp.Block) -> str:
6393
6474
  expressions = self.expressions(expression, sep="; ", flat=True)
6394
6475
  begin = "BEGIN " if expression.args.get("begin") else ""
@@ -15,6 +15,7 @@ from sqlglot.dialects.dialect import (
15
15
  inline_array_unless_query,
16
16
  max_or_greatest,
17
17
  min_or_least,
18
+ no_filter_sql,
18
19
  no_ilike_sql,
19
20
  regexp_replace_sql,
20
21
  rename_func,
@@ -229,8 +230,7 @@ def _levenshtein_sql(self: BigQueryGenerator, expression: exp.Levenshtein) -> st
229
230
 
230
231
 
231
232
  def _json_extract_sql(self: BigQueryGenerator, expression: JSON_EXTRACT_TYPE) -> str:
232
- name = expression.meta_get("name") or expression.sql_name()
233
- upper = name.upper()
233
+ upper = self.function_name(expression).upper()
234
234
 
235
235
  dquote_escaping = upper in DQUOTES_ESCAPING_JSON_FUNCTIONS
236
236
 
@@ -257,6 +257,7 @@ class BigQueryGenerator(generator.Generator):
257
257
  RENAME_TABLE_WITH_DB = False
258
258
  NVL2_SUPPORTED = False
259
259
  UNNEST_WITH_ORDINALITY = False
260
+ CAST_TO_INT_ROUNDS = True
260
261
  COLLATE_IS_FUNC = True
261
262
  LIMIT_ONLY_LITERALS = True
262
263
  SUPPORTS_TABLE_ALIAS_COLUMNS = False
@@ -335,6 +336,7 @@ class BigQueryGenerator(generator.Generator):
335
336
  exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"),
336
337
  exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"),
337
338
  exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
339
+ exp.Filter: no_filter_sql,
338
340
  exp.FromTimeZone: lambda self, e: self.func(
339
341
  "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'"
340
342
  ),
@@ -164,6 +164,7 @@ def _json_cast_sql(self: ClickHouseGenerator, expression: exp.JSONCast) -> str:
164
164
 
165
165
 
166
166
  class ClickHouseGenerator(generator.Generator):
167
+ BYTE_STRING_FUNCTION = "unhex"
167
168
  SELECT_KINDS: tuple[str, ...] = ()
168
169
  TRY_SUPPORTED = False
169
170
  SUPPORTS_UESCAPE = False
@@ -294,6 +295,9 @@ class ClickHouseGenerator(generator.Generator):
294
295
  exp.ArgMax: arg_max_or_min_no_count("argMax"),
295
296
  exp.ArgMin: arg_max_or_min_no_count("argMin"),
296
297
  exp.Array: inline_array_sql,
298
+ exp.BitwiseAndAgg: rename_func("groupBitAnd"),
299
+ exp.BitwiseOrAgg: rename_func("groupBitOr"),
300
+ exp.BitwiseXorAgg: rename_func("groupBitXor"),
297
301
  exp.CityHash64: rename_func("cityHash64"),
298
302
  exp.CastToStrType: rename_func("CAST"),
299
303
  exp.CurrentDatabase: rename_func("CURRENT_DATABASE"),
@@ -312,9 +316,13 @@ class ClickHouseGenerator(generator.Generator):
312
316
  exp.DateDiff: _datetime_delta_sql("DATE_DIFF"),
313
317
  exp.DateStrToDate: rename_func("toDate"),
314
318
  exp.DateSub: _datetime_delta_sql("DATE_SUB"),
319
+ exp.DayOfMonth: rename_func("toDayOfMonth"),
320
+ exp.DayOfWeekIso: rename_func("toDayOfWeek"),
321
+ exp.DayOfYear: rename_func("toDayOfYear"),
315
322
  exp.Explode: rename_func("arrayJoin"),
316
323
  exp.FarmFingerprint: rename_func("farmFingerprint64"),
317
324
  exp.Final: lambda self, e: f"{self.sql(e, 'this')} FINAL",
325
+ exp.IsInf: rename_func("isInfinite"),
318
326
  exp.IsNan: rename_func("isNaN"),
319
327
  exp.JarowinklerSimilarity: jarowinkler_similarity("jaroWinklerSimilarity"),
320
328
  exp.JSONCast: _json_cast_sql,
@@ -371,6 +379,8 @@ class ClickHouseGenerator(generator.Generator):
371
379
  exp.Trim: lambda self, e: trim_sql(self, e, default_trim_type="BOTH"),
372
380
  exp.Variance: rename_func("varSamp"),
373
381
  exp.VariancePop: rename_func("varPop"),
382
+ exp.WeekOfYear: rename_func("toISOWeek"),
383
+ exp.YearOfWeekIso: rename_func("toISOYear"),
374
384
  exp.SchemaCommentProperty: lambda self, e: self.naked_property(e),
375
385
  exp.Stddev: rename_func("stddevSamp"),
376
386
  exp.Chr: rename_func("CHAR"),
@@ -421,6 +431,16 @@ class ClickHouseGenerator(generator.Generator):
421
431
  exp.DType.MULTIPOLYGON,
422
432
  }
423
433
 
434
+ @unsupported_args("json_scope")
435
+ def contains_sql(self, expression: exp.Contains) -> str:
436
+ # ClickHouse has no CONTAINS; the comparison needs parentheses inside another operator
437
+ result = exp.StrPosition(this=expression.this, substr=expression.expression) > 0
438
+ if isinstance(expression.parent, exp.Binary) and not isinstance(
439
+ expression.parent, exp.Connector
440
+ ):
441
+ return self.sql(exp.paren(result, copy=False))
442
+ return self.sql(result)
443
+
424
444
  def groupconcat_sql(self, expression: exp.GroupConcat) -> str:
425
445
  this = expression.this
426
446
  separator = expression.args.get("separator")
@@ -42,6 +42,7 @@ class DorisGenerator(MySQLGenerator):
42
42
  exp.UniqueKeyProperty: exp.Properties.Location.POST_SCHEMA,
43
43
  exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA,
44
44
  exp.BuildProperty: exp.Properties.Location.POST_SCHEMA,
45
+ exp.Property: exp.Properties.Location.POST_WITH,
45
46
  }
46
47
 
47
48
  CAST_MAPPING: t.ClassVar[dict[exp.DType, str]] = {}
@@ -34,6 +34,7 @@ class DremioGenerator(generator.Generator):
34
34
  TRY_SUPPORTED = False
35
35
  SUPPORTS_UESCAPE = False
36
36
  SUPPORTS_DECODE_CASE = False
37
+ CAST_TO_INT_ROUNDS = True
37
38
 
38
39
  AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
39
40
 
@@ -25,10 +25,12 @@ def _str_to_date(self: DrillGenerator, expression: exp.StrToDate) -> str:
25
25
 
26
26
 
27
27
  class DrillGenerator(generator.Generator):
28
+ BYTE_STRING_FUNCTION = "UNHEX"
28
29
  SELECT_KINDS: tuple[str, ...] = ()
29
30
  TRY_SUPPORTED = False
30
31
  SUPPORTS_UESCAPE = False
31
32
  SUPPORTS_DECODE_CASE = False
33
+ CAST_TO_INT_ROUNDS = True
32
34
  SUPPORTS_LIKE_QUANTIFIERS = False
33
35
 
34
36
  AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
@@ -104,6 +106,11 @@ class DrillGenerator(generator.Generator):
104
106
  ),
105
107
  }
106
108
 
109
+ def bytestring_sql(self, expression: exp.ByteString) -> str:
110
+ if expression.args.get("is_bytes"):
111
+ return super().bytestring_sql(expression)
112
+ return self.sql(exp.Literal.string(expression.this))
113
+
107
114
  def ilike_sql(self, expression: exp.ILike) -> str:
108
115
  if isinstance(expression.expression, (exp.All, exp.Any)):
109
116
  return super().ilike_sql(expression)