sqlglotc 30.15.0__tar.gz → 30.16.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.
- {sqlglotc-30.15.0/sqlglotc.egg-info → sqlglotc-30.16.0}/PKG-INFO +2 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/errors.py +15 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/array.py +6 -4
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/core.py +20 -14
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/ddl.py +1 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/json.py +4 -3
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/query.py +4 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/string.py +3 -3
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generator.py +57 -14
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/bigquery.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/clickhouse.py +3 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/doris.py +5 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/duckdb.py +24 -22
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/hive.py +7 -3
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/mysql.py +11 -6
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/postgres.py +8 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/singlestore.py +2 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/snowflake.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/spark2.py +5 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/sqlite.py +7 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/starrocks.py +3 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/trino.py +34 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/tsql.py +1 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/lineage.py +78 -33
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/qualify_columns.py +14 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/qualify_tables.py +9 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/scope.py +6 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/simplify.py +2 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parser.py +95 -19
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/clickhouse.py +24 -15
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/duckdb.py +11 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/mysql.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/postgres.py +16 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/prql.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/snowflake.py +11 -4
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/sqlite.py +79 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/teradata.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/trino.py +73 -9
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/tsql.py +8 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/tokenizer_core.py +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0/sqlglotc.egg-info}/PKG-INFO +2 -2
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglotc.egg-info/requires.txt +1 -1
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglotc.egg-info/scm_file_list.json +2 -2
- sqlglotc-30.16.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.15.0/sqlglotc.egg-info/scm_version.json +0 -8
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/MANIFEST.in +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/pyproject.toml +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/setup.cfg +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/setup.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/anonymize.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/builders.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/constraints.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/properties.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/databricks.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/dax.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/exasol.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/oracle.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/presto.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/annotate_types.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/optimizer/resolver.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/bigquery.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/databricks.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/dax.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/hive.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/oracle.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/spark2.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/starrocks.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.15.0 → sqlglotc-30.16.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.16.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.
|
|
10
|
+
Requires-Dist: sqlglot==30.16.0
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
13
13
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
@@ -72,7 +72,21 @@ class ParseError(SqlglotError):
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
class TokenError(SqlglotError):
|
|
75
|
-
|
|
75
|
+
"""Error raised when tokenizing fails.
|
|
76
|
+
|
|
77
|
+
When available, `start` and `end` are the offsets in the source SQL of the context
|
|
78
|
+
snippet quoted in the message, i.e. the snippet is `sql[start:end]`.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
message: str,
|
|
84
|
+
start: int | None = None,
|
|
85
|
+
end: int | None = None,
|
|
86
|
+
):
|
|
87
|
+
super().__init__(message)
|
|
88
|
+
self.start = start
|
|
89
|
+
self.end = end
|
|
76
90
|
|
|
77
91
|
|
|
78
92
|
class OptimizeError(SqlglotError):
|
|
@@ -8,6 +8,7 @@ from sqlglot.expressions.core import (
|
|
|
8
8
|
Expr,
|
|
9
9
|
Func,
|
|
10
10
|
Binary,
|
|
11
|
+
Predicate,
|
|
11
12
|
to_identifier,
|
|
12
13
|
)
|
|
13
14
|
from sqlglot.helper import trait
|
|
@@ -109,16 +110,16 @@ class ArrayAny(Expression, Func):
|
|
|
109
110
|
arg_types = {"this": True, "expression": True}
|
|
110
111
|
|
|
111
112
|
|
|
112
|
-
class ArrayContains(Expression, Binary, Func):
|
|
113
|
+
class ArrayContains(Expression, Binary, Predicate, Func):
|
|
113
114
|
arg_types = {"this": True, "expression": True, "ensure_variant": False, "check_null": False}
|
|
114
115
|
_sql_names = ["ARRAY_CONTAINS", "ARRAY_HAS"]
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
class ArrayContainsAll(Expression, Binary, Func):
|
|
118
|
+
class ArrayContainsAll(Expression, Binary, Predicate, Func):
|
|
118
119
|
_sql_names = ["ARRAY_CONTAINS_ALL", "ARRAY_HAS_ALL"]
|
|
119
120
|
|
|
120
121
|
|
|
121
|
-
class ArrayContainedBy(Expression, Binary, Func):
|
|
122
|
+
class ArrayContainedBy(Expression, Binary, Predicate, Func):
|
|
122
123
|
pass
|
|
123
124
|
|
|
124
125
|
|
|
@@ -132,7 +133,7 @@ class ArrayIntersect(Expression, Func):
|
|
|
132
133
|
_sql_names = ["ARRAY_INTERSECT", "ARRAY_INTERSECTION"]
|
|
133
134
|
|
|
134
135
|
|
|
135
|
-
class ArrayOverlaps(Expression, Binary, Func):
|
|
136
|
+
class ArrayOverlaps(Expression, Binary, Predicate, Func):
|
|
136
137
|
arg_types = {"this": True, "expression": True, "null_safe": False}
|
|
137
138
|
|
|
138
139
|
|
|
@@ -343,6 +344,7 @@ class ToMap(Expression, Func):
|
|
|
343
344
|
class VarMap(Expression, Func):
|
|
344
345
|
arg_types = {"keys": True, "values": True}
|
|
345
346
|
is_var_len_args = True
|
|
347
|
+
var_len_arg_key = "values"
|
|
346
348
|
|
|
347
349
|
@property
|
|
348
350
|
def keys(self) -> list[Expr]:
|
|
@@ -14,7 +14,7 @@ from builtins import type as Type
|
|
|
14
14
|
from collections import deque
|
|
15
15
|
from collections.abc import Collection, Iterator, Mapping, MutableMapping, Sequence
|
|
16
16
|
from copy import deepcopy
|
|
17
|
-
from decimal import Decimal
|
|
17
|
+
from decimal import Decimal, InvalidOperation
|
|
18
18
|
from functools import reduce
|
|
19
19
|
|
|
20
20
|
from sqlglot._typing import E, GeneratorNoDialectArgs, ParserNoDialectArgs, T
|
|
@@ -85,6 +85,7 @@ class Expr:
|
|
|
85
85
|
arg_types: t.ClassVar[dict[str, bool]] = {"this": True}
|
|
86
86
|
required_args: t.ClassVar[set[str]] = {"this"}
|
|
87
87
|
is_var_len_args: t.ClassVar[bool] = False
|
|
88
|
+
var_len_arg_key: t.ClassVar[str] = "expressions"
|
|
88
89
|
_hash_raw_args: t.ClassVar[bool] = False
|
|
89
90
|
is_subquery: t.ClassVar[bool] = False
|
|
90
91
|
is_cast: t.ClassVar[bool] = False
|
|
@@ -1577,7 +1578,7 @@ class Condition(Expr):
|
|
|
1577
1578
|
|
|
1578
1579
|
@trait
|
|
1579
1580
|
class Predicate(Condition):
|
|
1580
|
-
"""
|
|
1581
|
+
"""Any condition that evaluates to a boolean, e.g. x = y, x LIKE 'a%', a @> b."""
|
|
1581
1582
|
|
|
1582
1583
|
|
|
1583
1584
|
class Cache(Expression):
|
|
@@ -1643,8 +1644,11 @@ class Func(Condition):
|
|
|
1643
1644
|
The base class for all function expressions.
|
|
1644
1645
|
|
|
1645
1646
|
Attributes:
|
|
1646
|
-
is_var_len_args (bool): if set to True the
|
|
1647
|
+
is_var_len_args (bool): if set to True the argument identified by var_len_arg_key will be
|
|
1647
1648
|
treated as a variable length argument and the argument's value will be stored as a list.
|
|
1649
|
+
var_len_arg_key (str): the arg_types key that collects the variable length arguments.
|
|
1650
|
+
Arguments preceding it in arg_types are filled positionally; those following it (e.g.
|
|
1651
|
+
dialect flags) are never populated by from_arg_list.
|
|
1648
1652
|
_sql_names (list): the SQL name (1st item in the list) and aliases (subsequent items) for this
|
|
1649
1653
|
function expression. These values are used to map this node to a name during parsing as
|
|
1650
1654
|
well as to provide the function's name during SQL string generation. By default the SQL
|
|
@@ -1652,18 +1656,17 @@ class Func(Condition):
|
|
|
1652
1656
|
"""
|
|
1653
1657
|
|
|
1654
1658
|
is_var_len_args: t.ClassVar[bool] = False
|
|
1659
|
+
var_len_arg_key: t.ClassVar[str] = "expressions"
|
|
1655
1660
|
_sql_names: t.ClassVar[list[str]] = []
|
|
1656
1661
|
|
|
1657
1662
|
@classmethod
|
|
1658
1663
|
def from_arg_list(cls, args: Sequence[object]) -> Self:
|
|
1659
1664
|
if cls.is_var_len_args:
|
|
1660
1665
|
all_arg_keys = tuple(cls.arg_types)
|
|
1661
|
-
|
|
1662
|
-
non_var_len_arg_keys = all_arg_keys[:-1] if cls.is_var_len_args else all_arg_keys
|
|
1663
|
-
num_non_var = len(non_var_len_arg_keys)
|
|
1666
|
+
var_len_index = all_arg_keys.index(cls.var_len_arg_key)
|
|
1664
1667
|
|
|
1665
|
-
args_dict = {arg_key: arg for arg, arg_key in zip(args,
|
|
1666
|
-
args_dict[
|
|
1668
|
+
args_dict = {arg_key: arg for arg, arg_key in zip(args, all_arg_keys[:var_len_index])}
|
|
1669
|
+
args_dict[cls.var_len_arg_key] = args[var_len_index:]
|
|
1667
1670
|
else:
|
|
1668
1671
|
args_dict = {arg_key: arg for arg, arg_key in zip(args, cls.arg_types)}
|
|
1669
1672
|
|
|
@@ -1764,7 +1767,10 @@ class Literal(Expression, Condition):
|
|
|
1764
1767
|
try:
|
|
1765
1768
|
return int(self.this)
|
|
1766
1769
|
except ValueError:
|
|
1767
|
-
|
|
1770
|
+
try:
|
|
1771
|
+
return Decimal(self.this)
|
|
1772
|
+
except InvalidOperation as e:
|
|
1773
|
+
raise ValueError(f"Invalid numeric literal: {self.this!r}") from e
|
|
1768
1774
|
return self.this
|
|
1769
1775
|
|
|
1770
1776
|
|
|
@@ -2119,15 +2125,15 @@ class Div(Expression, Binary):
|
|
|
2119
2125
|
arg_types = {"this": True, "expression": True, "typed": False, "safe": False}
|
|
2120
2126
|
|
|
2121
2127
|
|
|
2122
|
-
class Overlaps(Expression, Binary):
|
|
2128
|
+
class Overlaps(Expression, Binary, Predicate):
|
|
2123
2129
|
pass
|
|
2124
2130
|
|
|
2125
2131
|
|
|
2126
|
-
class ExtendsLeft(Expression, Binary):
|
|
2132
|
+
class ExtendsLeft(Expression, Binary, Predicate):
|
|
2127
2133
|
pass
|
|
2128
2134
|
|
|
2129
2135
|
|
|
2130
|
-
class ExtendsRight(Expression, Binary):
|
|
2136
|
+
class ExtendsRight(Expression, Binary, Predicate):
|
|
2131
2137
|
pass
|
|
2132
2138
|
|
|
2133
2139
|
|
|
@@ -2231,7 +2237,7 @@ class Sub(Expression, Binary):
|
|
|
2231
2237
|
pass
|
|
2232
2238
|
|
|
2233
2239
|
|
|
2234
|
-
class Adjacent(Expression, Binary):
|
|
2240
|
+
class Adjacent(Expression, Binary, Predicate):
|
|
2235
2241
|
pass
|
|
2236
2242
|
|
|
2237
2243
|
|
|
@@ -2317,7 +2323,7 @@ class Pow(Expression, Binary, Func):
|
|
|
2317
2323
|
_sql_names = ["POWER", "POW"]
|
|
2318
2324
|
|
|
2319
2325
|
|
|
2320
|
-
class RegexpLike(Expression, Binary, Func):
|
|
2326
|
+
class RegexpLike(Expression, Binary, Predicate, Func):
|
|
2321
2327
|
arg_types = {"this": True, "expression": True, "flag": False, "full_match": False}
|
|
2322
2328
|
|
|
2323
2329
|
|
|
@@ -46,15 +46,15 @@ class JSONArrayInsert(Expression, Func):
|
|
|
46
46
|
_sql_names = ["JSON_ARRAY_INSERT"]
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
class JSONBContains(Expression, Binary, Func):
|
|
49
|
+
class JSONBContains(Expression, Binary, Predicate, Func):
|
|
50
50
|
_sql_names = ["JSONB_CONTAINS"]
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
class JSONBContainsAllTopKeys(Expression, Binary, Func):
|
|
53
|
+
class JSONBContainsAllTopKeys(Expression, Binary, Predicate, Func):
|
|
54
54
|
pass
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
class JSONBContainsAnyTopKeys(Expression, Binary, Func):
|
|
57
|
+
class JSONBContainsAnyTopKeys(Expression, Binary, Predicate, Func):
|
|
58
58
|
pass
|
|
59
59
|
|
|
60
60
|
|
|
@@ -133,6 +133,7 @@ class JSONExtractScalar(Expression, Binary, Func):
|
|
|
133
133
|
"expressions": False,
|
|
134
134
|
"json_type": False,
|
|
135
135
|
"scalar_only": False,
|
|
136
|
+
"json_subtype": False,
|
|
136
137
|
}
|
|
137
138
|
_sql_names = ["JSON_EXTRACT_SCALAR"]
|
|
138
139
|
is_var_len_args = True
|
|
@@ -2119,6 +2119,10 @@ class IfBlock(Expression):
|
|
|
2119
2119
|
arg_types = {"this": True, "true": True, "false": False}
|
|
2120
2120
|
|
|
2121
2121
|
|
|
2122
|
+
class CaseStatement(Expression):
|
|
2123
|
+
arg_types = {"this": False, "ifs": True, "default": False}
|
|
2124
|
+
|
|
2125
|
+
|
|
2122
2126
|
class WhileBlock(Expression):
|
|
2123
2127
|
arg_types = {"this": True, "body": True}
|
|
2124
2128
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from sqlglot.expressions.core import Expression, Func, Binary
|
|
5
|
+
from sqlglot.expressions.core import Expression, Func, Binary, Predicate
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
# String basics
|
|
@@ -445,11 +445,11 @@ class RegexpExtractAll(Expression, Func):
|
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
|
|
448
|
-
class RegexpFullMatch(Expression, Binary, Func):
|
|
448
|
+
class RegexpFullMatch(Expression, Binary, Predicate, Func):
|
|
449
449
|
arg_types = {"this": True, "expression": True, "options": False}
|
|
450
450
|
|
|
451
451
|
|
|
452
|
-
class RegexpILike(Expression, Binary, Func):
|
|
452
|
+
class RegexpILike(Expression, Binary, Predicate, Func):
|
|
453
453
|
arg_types = {"this": True, "expression": True, "flag": False}
|
|
454
454
|
|
|
455
455
|
|
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
import re
|
|
5
5
|
import typing as t
|
|
6
6
|
from collections import defaultdict
|
|
7
|
+
from decimal import Decimal
|
|
7
8
|
from functools import reduce, wraps
|
|
8
9
|
|
|
9
10
|
from sqlglot import exp
|
|
@@ -484,6 +485,12 @@ class Generator:
|
|
|
484
485
|
# Whether ALTER TABLE ... CHANGE COLUMN column-rename-and-redefine syntax is supported
|
|
485
486
|
SUPPORTS_CHANGE_COLUMN = False
|
|
486
487
|
|
|
488
|
+
# Whether ALTER COLUMN can set a column's nullability together with its type
|
|
489
|
+
SUPPORTS_ALTER_COLUMN_NULLABILITY = False
|
|
490
|
+
|
|
491
|
+
# Whether ALTER COLUMN IF EXISTS is supported
|
|
492
|
+
SUPPORTS_ALTER_COLUMN_IF_EXISTS = False
|
|
493
|
+
|
|
487
494
|
# Whether the LikeProperty needs to be specified inside of the schema clause
|
|
488
495
|
LIKE_PROPERTY_INSIDE_SCHEMA = False
|
|
489
496
|
|
|
@@ -1642,10 +1649,11 @@ class Generator:
|
|
|
1642
1649
|
def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
|
|
1643
1650
|
this = self.sql(expression, "this")
|
|
1644
1651
|
escape = expression.args.get("escape")
|
|
1652
|
+
unicode_start = self.dialect.UNICODE_START
|
|
1645
1653
|
|
|
1646
|
-
if
|
|
1654
|
+
if unicode_start:
|
|
1647
1655
|
escape_substitute = r"\\\1"
|
|
1648
|
-
left_quote, right_quote =
|
|
1656
|
+
left_quote, right_quote = unicode_start, self.dialect.UNICODE_END or ""
|
|
1649
1657
|
else:
|
|
1650
1658
|
escape_substitute = r"\\u\1"
|
|
1651
1659
|
left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
|
|
@@ -1657,9 +1665,16 @@ class Generator:
|
|
|
1657
1665
|
escape_pattern = ESCAPED_UNICODE_RE
|
|
1658
1666
|
escape_sql = ""
|
|
1659
1667
|
|
|
1660
|
-
if not
|
|
1668
|
+
if not unicode_start or (escape and not self.SUPPORTS_UESCAPE):
|
|
1661
1669
|
this = escape_pattern.sub(self.UNICODE_SUBSTITUTE or escape_substitute, this)
|
|
1662
1670
|
|
|
1671
|
+
if unicode_start:
|
|
1672
|
+
# A Unicode literal only escapes its delimiter by doubling it; the escape character
|
|
1673
|
+
# introduces a code point, so the dialect's ordinary string escapes don't apply here
|
|
1674
|
+
this = self._replace_line_breaks(this).replace(right_quote, right_quote * 2)
|
|
1675
|
+
else:
|
|
1676
|
+
this = self.escape_str(this, escape_backslash=False)
|
|
1677
|
+
|
|
1663
1678
|
return f"{left_quote}{this}{right_quote}{escape_sql}"
|
|
1664
1679
|
|
|
1665
1680
|
def rawstring_sql(self, expression: exp.RawString) -> str:
|
|
@@ -1702,11 +1717,12 @@ class Generator:
|
|
|
1702
1717
|
continue
|
|
1703
1718
|
|
|
1704
1719
|
param_value = param.this if isinstance(param, exp.DataTypeParam) else param
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
and param_value.is_number
|
|
1708
|
-
|
|
1709
|
-
)
|
|
1720
|
+
value = (
|
|
1721
|
+
param_value.to_py()
|
|
1722
|
+
if isinstance(param_value, exp.Literal) and param_value.is_number
|
|
1723
|
+
else None
|
|
1724
|
+
)
|
|
1725
|
+
if isinstance(value, (int, Decimal)) and value > bound:
|
|
1710
1726
|
self.unsupported(
|
|
1711
1727
|
f"{type_value.value} parameter {param_value.name} exceeds "
|
|
1712
1728
|
f"{self.dialect.__class__.__name__}'s maximum of {bound}; capping"
|
|
@@ -4198,6 +4214,13 @@ class Generator:
|
|
|
4198
4214
|
def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
|
|
4199
4215
|
this = self.sql(expression, "this")
|
|
4200
4216
|
|
|
4217
|
+
exists = ""
|
|
4218
|
+
if expression.args.get("exists"):
|
|
4219
|
+
if self.SUPPORTS_ALTER_COLUMN_IF_EXISTS:
|
|
4220
|
+
exists = " IF EXISTS"
|
|
4221
|
+
else:
|
|
4222
|
+
self.unsupported("ALTER COLUMN IF EXISTS is not supported by this dialect")
|
|
4223
|
+
|
|
4201
4224
|
dtype = self.sql(expression, "dtype")
|
|
4202
4225
|
if dtype:
|
|
4203
4226
|
collate = self.sql(expression, "collate")
|
|
@@ -4205,19 +4228,24 @@ class Generator:
|
|
|
4205
4228
|
using = self.sql(expression, "using")
|
|
4206
4229
|
using = f" USING {using}" if using else ""
|
|
4207
4230
|
alter_set_type = self.ALTER_SET_TYPE + " " if self.ALTER_SET_TYPE else ""
|
|
4208
|
-
|
|
4231
|
+
null_constraint = self._alter_column_null_constraint_sql(expression)
|
|
4232
|
+
|
|
4233
|
+
return (
|
|
4234
|
+
f"ALTER COLUMN{exists} {this} {alter_set_type}{dtype}"
|
|
4235
|
+
f"{collate}{using}{null_constraint}"
|
|
4236
|
+
)
|
|
4209
4237
|
|
|
4210
4238
|
default = self.sql(expression, "default")
|
|
4211
4239
|
if default:
|
|
4212
|
-
return f"ALTER COLUMN {this} SET DEFAULT {default}"
|
|
4240
|
+
return f"ALTER COLUMN{exists} {this} SET DEFAULT {default}"
|
|
4213
4241
|
|
|
4214
4242
|
comment = self.sql(expression, "comment")
|
|
4215
4243
|
if comment:
|
|
4216
|
-
return f"ALTER COLUMN {this} COMMENT {comment}"
|
|
4244
|
+
return f"ALTER COLUMN{exists} {this} COMMENT {comment}"
|
|
4217
4245
|
|
|
4218
4246
|
visible = expression.args.get("visible")
|
|
4219
4247
|
if visible:
|
|
4220
|
-
return f"ALTER COLUMN {this} SET {visible}"
|
|
4248
|
+
return f"ALTER COLUMN{exists} {this} SET {visible}"
|
|
4221
4249
|
|
|
4222
4250
|
allow_null = expression.args.get("allow_null")
|
|
4223
4251
|
drop = expression.args.get("drop")
|
|
@@ -4227,9 +4255,20 @@ class Generator:
|
|
|
4227
4255
|
|
|
4228
4256
|
if allow_null is not None:
|
|
4229
4257
|
keyword = "DROP" if drop else "SET"
|
|
4230
|
-
return f"ALTER COLUMN {this} {keyword} NOT NULL"
|
|
4258
|
+
return f"ALTER COLUMN{exists} {this} {keyword} NOT NULL"
|
|
4259
|
+
|
|
4260
|
+
return f"ALTER COLUMN{exists} {this} DROP DEFAULT"
|
|
4261
|
+
|
|
4262
|
+
def _alter_column_null_constraint_sql(self, expression: exp.AlterColumn) -> str:
|
|
4263
|
+
allow_null = expression.args.get("allow_null")
|
|
4264
|
+
if allow_null is None:
|
|
4265
|
+
return ""
|
|
4266
|
+
|
|
4267
|
+
if not self.SUPPORTS_ALTER_COLUMN_NULLABILITY:
|
|
4268
|
+
self.unsupported("ALTER COLUMN cannot set nullability along with a type")
|
|
4269
|
+
return ""
|
|
4231
4270
|
|
|
4232
|
-
return
|
|
4271
|
+
return " NULL" if allow_null else " NOT NULL"
|
|
4233
4272
|
|
|
4234
4273
|
def modifycolumn_sql(self, expression: exp.ModifyColumn) -> str:
|
|
4235
4274
|
this = self.sql(expression, "this")
|
|
@@ -6272,6 +6311,10 @@ class Generator:
|
|
|
6272
6311
|
self.unsupported("Unsupported If block syntax")
|
|
6273
6312
|
return ""
|
|
6274
6313
|
|
|
6314
|
+
def casestatement_sql(self, expression: exp.CaseStatement) -> str:
|
|
6315
|
+
self.unsupported("Unsupported Case statement syntax")
|
|
6316
|
+
return ""
|
|
6317
|
+
|
|
6275
6318
|
def whileblock_sql(self, expression: exp.WhileBlock) -> str:
|
|
6276
6319
|
self.unsupported("Unsupported While block syntax")
|
|
6277
6320
|
return ""
|
|
@@ -175,6 +175,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
175
175
|
STRUCT_DELIMITER = ("(", ")")
|
|
176
176
|
NVL2_SUPPORTED = False
|
|
177
177
|
ALTER_SET_TYPE = "TYPE"
|
|
178
|
+
SUPPORTS_ALTER_COLUMN_IF_EXISTS = True
|
|
178
179
|
TABLESAMPLE_REQUIRES_PARENS = False
|
|
179
180
|
TABLESAMPLE_SIZE_IS_ROWS = False
|
|
180
181
|
TABLESAMPLE_KEYWORDS = "SAMPLE"
|
|
@@ -398,7 +399,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
398
399
|
|
|
399
400
|
# There's no list in docs, but it can be found in Clickhouse code
|
|
400
401
|
# see `ClickHouse/src/Parsers/ParserCreate*.cpp`
|
|
401
|
-
ON_CLUSTER_TARGETS = {
|
|
402
|
+
ON_CLUSTER_TARGETS: t.ClassVar = {
|
|
402
403
|
"SCHEMA", # Transpiled CREATE SCHEMA may have OnCluster property set
|
|
403
404
|
"DATABASE",
|
|
404
405
|
"TABLE",
|
|
@@ -410,7 +411,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
410
411
|
}
|
|
411
412
|
|
|
412
413
|
# https://clickhouse.com/docs/en/sql-reference/data-types/nullable
|
|
413
|
-
NON_NULLABLE_TYPES = {
|
|
414
|
+
NON_NULLABLE_TYPES: t.ClassVar = {
|
|
414
415
|
exp.DType.ARRAY,
|
|
415
416
|
exp.DType.MAP,
|
|
416
417
|
exp.DType.STRUCT,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
3
5
|
from sqlglot import exp
|
|
4
6
|
from sqlglot.dialects.dialect import (
|
|
5
7
|
approx_count_distinct_sql,
|
|
@@ -22,6 +24,7 @@ def _lag_lead_sql(self, expression: exp.Lag | exp.Lead) -> str:
|
|
|
22
24
|
|
|
23
25
|
class DorisGenerator(MySQLGenerator):
|
|
24
26
|
LAST_DAY_SUPPORTS_DATE_PART = False
|
|
27
|
+
SUPPORTS_ALTER_COLUMN_NULLABILITY = False
|
|
25
28
|
VARCHAR_REQUIRES_SIZE = False
|
|
26
29
|
WITH_PROPERTIES_PREFIX = "PROPERTIES"
|
|
27
30
|
RENAME_TABLE_WITH_DB = False
|
|
@@ -41,8 +44,8 @@ class DorisGenerator(MySQLGenerator):
|
|
|
41
44
|
exp.BuildProperty: exp.Properties.Location.POST_SCHEMA,
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
CAST_MAPPING = {}
|
|
45
|
-
TIMESTAMP_FUNC_TYPES = set()
|
|
47
|
+
CAST_MAPPING: t.ClassVar[dict[exp.DType, str]] = {}
|
|
48
|
+
TIMESTAMP_FUNC_TYPES: t.ClassVar[set[exp.DType]] = set()
|
|
46
49
|
|
|
47
50
|
TRANSFORMS = {
|
|
48
51
|
**MySQLGenerator.TRANSFORMS,
|
|
@@ -1951,7 +1951,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1951
1951
|
IGNORE_RESPECT_NULLS_WINDOW_FUNCTIONS: t.ClassVar = _IGNORE_RESPECT_NULLS_WINDOW_FUNCTIONS
|
|
1952
1952
|
|
|
1953
1953
|
# Template for ZIPF transpilation - placeholders get replaced with actual parameters
|
|
1954
|
-
ZIPF_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
1954
|
+
ZIPF_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
1955
1955
|
"""
|
|
1956
1956
|
WITH rand AS (SELECT :random_expr AS r),
|
|
1957
1957
|
weights AS (
|
|
@@ -1970,22 +1970,24 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1970
1970
|
|
|
1971
1971
|
# Template for NORMAL transpilation using Box-Muller transform
|
|
1972
1972
|
# mean + (stddev * sqrt(-2 * ln(u1)) * cos(2 * pi * u2))
|
|
1973
|
-
NORMAL_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
1973
|
+
NORMAL_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
1974
1974
|
":mean + (:stddev * SQRT(-2 * LN(GREATEST(:u1, 1e-10))) * COS(2 * PI() * :u2))"
|
|
1975
1975
|
)
|
|
1976
1976
|
|
|
1977
1977
|
# Template for generating a seeded pseudo-random value in [0, 1) from a hash
|
|
1978
|
-
SEEDED_RANDOM_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
1978
|
+
SEEDED_RANDOM_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
1979
|
+
"(ABS(HASH(:seed)) % 1000000) / 1000000.0"
|
|
1980
|
+
)
|
|
1979
1981
|
|
|
1980
1982
|
# Template for generating signed and unsigned SEQ values within a specified range
|
|
1981
|
-
SEQ_UNSIGNED: exp.Expr = _SEQ_UNSIGNED
|
|
1982
|
-
SEQ_SIGNED: exp.Expr = _SEQ_SIGNED
|
|
1983
|
+
SEQ_UNSIGNED: t.ClassVar[exp.Expr] = _SEQ_UNSIGNED
|
|
1984
|
+
SEQ_SIGNED: t.ClassVar[exp.Expr] = _SEQ_SIGNED
|
|
1983
1985
|
|
|
1984
1986
|
# Template for MAP_CAT transpilation - Snowflake semantics:
|
|
1985
1987
|
# 1. Returns NULL if either input is NULL
|
|
1986
1988
|
# 2. For duplicate keys, prefers non-NULL value (COALESCE(m2[k], m1[k]))
|
|
1987
1989
|
# 3. Filters out entries with NULL values from the result
|
|
1988
|
-
MAPCAT_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
1990
|
+
MAPCAT_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
1989
1991
|
"""
|
|
1990
1992
|
CASE
|
|
1991
1993
|
WHEN :map1 IS NULL OR :map2 IS NULL THEN NULL
|
|
@@ -1999,7 +2001,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1999
2001
|
|
|
2000
2002
|
# Mappings for EXTRACT/DATE_PART transpilation
|
|
2001
2003
|
# Maps Snowflake specifiers unsupported in DuckDB to strftime format codes
|
|
2002
|
-
EXTRACT_STRFTIME_MAPPINGS: dict[str, tuple[str, str]] = {
|
|
2004
|
+
EXTRACT_STRFTIME_MAPPINGS: t.ClassVar[dict[str, tuple[str, str]]] = {
|
|
2003
2005
|
"WEEKISO": ("%V", "INTEGER"),
|
|
2004
2006
|
"YEAROFWEEK": ("%G", "INTEGER"),
|
|
2005
2007
|
"YEAROFWEEKISO": ("%G", "INTEGER"),
|
|
@@ -2007,7 +2009,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2007
2009
|
}
|
|
2008
2010
|
|
|
2009
2011
|
# Maps epoch-based specifiers to DuckDB epoch functions
|
|
2010
|
-
EXTRACT_EPOCH_MAPPINGS: dict[str, str] = {
|
|
2012
|
+
EXTRACT_EPOCH_MAPPINGS: t.ClassVar[dict[str, str]] = {
|
|
2011
2013
|
"EPOCH_SECOND": "EPOCH",
|
|
2012
2014
|
"EPOCH_MILLISECOND": "EPOCH_MS",
|
|
2013
2015
|
"EPOCH_MICROSECOND": "EPOCH_US",
|
|
@@ -2057,7 +2059,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2057
2059
|
# - Large format: Fixed 10-byte header + values (no padding needed)
|
|
2058
2060
|
# Result: Complete binary bitmap as BLOB
|
|
2059
2061
|
#
|
|
2060
|
-
BITMAP_CONSTRUCT_AGG_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2062
|
+
BITMAP_CONSTRUCT_AGG_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2061
2063
|
"""
|
|
2062
2064
|
SELECT CASE
|
|
2063
2065
|
WHEN l IS NULL OR LENGTH(l) = 0 THEN NULL
|
|
@@ -2076,7 +2078,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2076
2078
|
)
|
|
2077
2079
|
|
|
2078
2080
|
# Template for RANDSTR transpilation - placeholders get replaced with actual parameters
|
|
2079
|
-
RANDSTR_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2081
|
+
RANDSTR_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2080
2082
|
f"""
|
|
2081
2083
|
SELECT LISTAGG(
|
|
2082
2084
|
SUBSTRING(
|
|
@@ -2096,7 +2098,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2096
2098
|
# Template for MINHASH transpilation
|
|
2097
2099
|
# Computes k minimum hash values across aggregated data using DuckDB list functions
|
|
2098
2100
|
# Returns JSON matching Snowflake format: {"state": [...], "type": "minhash", "version": 1}
|
|
2099
|
-
MINHASH_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2101
|
+
MINHASH_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2100
2102
|
"""
|
|
2101
2103
|
SELECT JSON_OBJECT('state', LIST(min_h ORDER BY seed), 'type', 'minhash', 'version', 1)
|
|
2102
2104
|
FROM (
|
|
@@ -2108,7 +2110,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2108
2110
|
|
|
2109
2111
|
# Template for MINHASH_COMBINE transpilation
|
|
2110
2112
|
# Combines multiple minhash signatures by taking element-wise minimum
|
|
2111
|
-
MINHASH_COMBINE_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2113
|
+
MINHASH_COMBINE_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2112
2114
|
"""
|
|
2113
2115
|
SELECT JSON_OBJECT('state', LIST(min_h ORDER BY idx), 'type', 'minhash', 'version', 1)
|
|
2114
2116
|
FROM (
|
|
@@ -2125,7 +2127,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2125
2127
|
|
|
2126
2128
|
# Template for APPROXIMATE_SIMILARITY transpilation
|
|
2127
2129
|
# Computes multi-way Jaccard similarity: fraction of positions where ALL signatures agree
|
|
2128
|
-
APPROXIMATE_SIMILARITY_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2130
|
+
APPROXIMATE_SIMILARITY_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2129
2131
|
"""
|
|
2130
2132
|
SELECT CAST(SUM(CASE WHEN num_distinct = 1 THEN 1 ELSE 0 END) AS DOUBLE) / COUNT(*)
|
|
2131
2133
|
FROM (
|
|
@@ -2143,7 +2145,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2143
2145
|
# Template for ARRAYS_ZIP transpilation
|
|
2144
2146
|
# Snowflake pads to longest array; DuckDB LIST_ZIP truncates to shortest
|
|
2145
2147
|
# Uses RANGE + indexing to match Snowflake behavior
|
|
2146
|
-
ARRAYS_ZIP_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2148
|
+
ARRAYS_ZIP_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2147
2149
|
"""
|
|
2148
2150
|
CASE WHEN :null_check THEN NULL
|
|
2149
2151
|
WHEN :all_empty_check THEN [:empty_struct]
|
|
@@ -2152,7 +2154,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2152
2154
|
""",
|
|
2153
2155
|
)
|
|
2154
2156
|
|
|
2155
|
-
UUID_V5_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2157
|
+
UUID_V5_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2156
2158
|
"""
|
|
2157
2159
|
(SELECT
|
|
2158
2160
|
LOWER(
|
|
@@ -2176,7 +2178,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2176
2178
|
# INTERSECTION (<=): keep the N-th occurrence only if N <= count in arr2
|
|
2177
2179
|
# e.g. [2,2,2] INTERSECT [2,2] -> [2,2]
|
|
2178
2180
|
# IS NOT DISTINCT FROM is used for NULL-safe element comparison.
|
|
2179
|
-
ARRAY_BAG_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2181
|
+
ARRAY_BAG_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2180
2182
|
"""
|
|
2181
2183
|
CASE
|
|
2182
2184
|
WHEN :arr1 IS NULL OR :arr2 IS NULL THEN NULL
|
|
@@ -2191,12 +2193,12 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2191
2193
|
"""
|
|
2192
2194
|
)
|
|
2193
2195
|
|
|
2194
|
-
ARRAY_EXCEPT_CONDITION: exp.Expr = exp.maybe_parse(
|
|
2196
|
+
ARRAY_EXCEPT_CONDITION: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2195
2197
|
"LEN(LIST_FILTER(:arr1[1:pair[1]], e -> e IS NOT DISTINCT FROM pair[0]))"
|
|
2196
2198
|
" > LEN(LIST_FILTER(:arr2, e -> e IS NOT DISTINCT FROM pair[0]))"
|
|
2197
2199
|
)
|
|
2198
2200
|
|
|
2199
|
-
ARRAY_INTERSECTION_CONDITION: exp.Expr = exp.maybe_parse(
|
|
2201
|
+
ARRAY_INTERSECTION_CONDITION: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2200
2202
|
"LEN(LIST_FILTER(:arr1[1:pair[1]], e -> e IS NOT DISTINCT FROM pair[0]))"
|
|
2201
2203
|
" <= LEN(LIST_FILTER(:arr2, e -> e IS NOT DISTINCT FROM pair[0]))"
|
|
2202
2204
|
)
|
|
@@ -2205,7 +2207,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2205
2207
|
# filters out any element that appears at least once in arr2.
|
|
2206
2208
|
# e.g. [1,1,2,3] EXCEPT [1] -> [2,3]
|
|
2207
2209
|
# IS NOT DISTINCT FROM is used for NULL-safe element comparison.
|
|
2208
|
-
ARRAY_EXCEPT_SET_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2210
|
+
ARRAY_EXCEPT_SET_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2209
2211
|
"""
|
|
2210
2212
|
CASE
|
|
2211
2213
|
WHEN :arr1 IS NULL OR :arr2 IS NULL THEN NULL
|
|
@@ -2225,7 +2227,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2225
2227
|
# 1 IN UNNEST([]) -> FALSE
|
|
2226
2228
|
# The default `IN (SELECT UNNEST(...))` rewrite creates a correlated subquery
|
|
2227
2229
|
# that DuckDB rejects inside non-inner joins, so a CASE expression is used instead.
|
|
2228
|
-
IN_UNNEST_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2230
|
+
IN_UNNEST_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2229
2231
|
"""
|
|
2230
2232
|
CASE
|
|
2231
2233
|
WHEN :arr IS NULL OR ARRAY_LENGTH(:arr) = 0 THEN FALSE
|
|
@@ -2236,7 +2238,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2236
2238
|
"""
|
|
2237
2239
|
)
|
|
2238
2240
|
|
|
2239
|
-
STRTOK_TO_ARRAY_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2241
|
+
STRTOK_TO_ARRAY_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2240
2242
|
"""
|
|
2241
2243
|
CASE WHEN :delimiter IS NULL THEN NULL
|
|
2242
2244
|
ELSE LIST_FILTER(
|
|
@@ -2285,7 +2287,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2285
2287
|
# x -> NOT x = ''
|
|
2286
2288
|
# )[index]
|
|
2287
2289
|
# END
|
|
2288
|
-
STRTOK_TEMPLATE: exp.Expr = exp.maybe_parse(
|
|
2290
|
+
STRTOK_TEMPLATE: t.ClassVar[exp.Expr] = exp.maybe_parse(
|
|
2289
2291
|
"""
|
|
2290
2292
|
CASE
|
|
2291
2293
|
WHEN :delimiter = '' AND :string = '' THEN NULL
|