sqlspec 0.14.1__py3-none-any.whl → 0.16.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.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/__init__.py +50 -25
- sqlspec/__main__.py +1 -1
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +480 -121
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +115 -260
- sqlspec/adapters/adbc/driver.py +462 -367
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +199 -129
- sqlspec/adapters/aiosqlite/driver.py +230 -269
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -168
- sqlspec/adapters/asyncmy/driver.py +260 -225
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +82 -181
- sqlspec/adapters/asyncpg/driver.py +285 -383
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -258
- sqlspec/adapters/bigquery/driver.py +474 -646
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +415 -351
- sqlspec/adapters/duckdb/driver.py +343 -413
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -379
- sqlspec/adapters/oracledb/driver.py +507 -560
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -254
- sqlspec/adapters/psqlpy/driver.py +505 -234
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -403
- sqlspec/adapters/psycopg/driver.py +706 -872
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +202 -118
- sqlspec/adapters/sqlite/driver.py +264 -303
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder → builder}/_base.py +120 -55
- sqlspec/{statement/builder → builder}/_column.py +17 -6
- sqlspec/{statement/builder → builder}/_ddl.py +46 -79
- sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
- sqlspec/{statement/builder → builder}/_delete.py +6 -25
- sqlspec/{statement/builder → builder}/_insert.py +18 -65
- sqlspec/builder/_merge.py +56 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +8 -11
- sqlspec/{statement/builder → builder}/_select.py +11 -56
- sqlspec/{statement/builder → builder}/_update.py +12 -18
- sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
- sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
- sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +34 -18
- sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
- sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +19 -9
- sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
- sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
- sqlspec/{statement/builder → builder}/mixins/_select_operations.py +25 -38
- sqlspec/{statement/builder → builder}/mixins/_update_operations.py +15 -16
- sqlspec/{statement/builder → builder}/mixins/_where_clause.py +210 -137
- sqlspec/cli.py +4 -5
- sqlspec/config.py +180 -133
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.py +664 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +666 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +387 -176
- sqlspec/driver/_common.py +527 -289
- sqlspec/driver/_sync.py +390 -172
- sqlspec/driver/mixins/__init__.py +2 -19
- sqlspec/driver/mixins/_result_tools.py +164 -0
- sqlspec/driver/mixins/_sql_translator.py +6 -3
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/cli.py +1 -1
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +18 -16
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +424 -105
- sqlspec/migrations/__init__.py +12 -0
- sqlspec/migrations/base.py +92 -68
- sqlspec/migrations/commands.py +24 -106
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +49 -51
- sqlspec/migrations/tracker.py +31 -44
- sqlspec/migrations/utils.py +64 -24
- sqlspec/protocols.py +7 -183
- sqlspec/storage/__init__.py +1 -1
- sqlspec/storage/backends/base.py +37 -40
- sqlspec/storage/backends/fsspec.py +136 -112
- sqlspec/storage/backends/obstore.py +138 -160
- sqlspec/storage/capabilities.py +5 -4
- sqlspec/storage/registry.py +57 -106
- sqlspec/typing.py +136 -115
- sqlspec/utils/__init__.py +2 -3
- sqlspec/utils/correlation.py +0 -3
- sqlspec/utils/deprecation.py +6 -6
- sqlspec/utils/fixtures.py +6 -6
- sqlspec/utils/logging.py +0 -2
- sqlspec/utils/module_loader.py +7 -12
- sqlspec/utils/singleton.py +0 -1
- sqlspec/utils/sync_tools.py +17 -38
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +443 -232
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/METADATA +7 -2
- sqlspec-0.16.0.dist-info/RECORD +134 -0
- sqlspec/adapters/adbc/transformers.py +0 -108
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_cache.py +0 -114
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -508
- sqlspec/driver/mixins/_query_tools.py +0 -796
- sqlspec/driver/mixins/_result_utils.py +0 -138
- sqlspec/driver/mixins/_storage.py +0 -912
- sqlspec/driver/mixins/_type_coercion.py +0 -128
- sqlspec/driver/parameters.py +0 -138
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/_merge.py +0 -95
- sqlspec/statement/cache.py +0 -50
- sqlspec/statement/filters.py +0 -625
- sqlspec/statement/parameters.py +0 -956
- sqlspec/statement/pipelines/__init__.py +0 -210
- sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
- sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
- sqlspec/statement/pipelines/context.py +0 -109
- sqlspec/statement/pipelines/transformers/__init__.py +0 -7
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
- sqlspec/statement/pipelines/validators/__init__.py +0 -23
- sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
- sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
- sqlspec/statement/pipelines/validators/_performance.py +0 -714
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1774
- sqlspec/utils/cached_property.py +0 -25
- sqlspec/utils/statement_hashing.py +0 -203
- sqlspec-0.14.1.dist-info/RECORD +0 -145
- /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,74 +1,84 @@
|
|
|
1
1
|
# ruff: noqa: PLR2004
|
|
2
2
|
"""Consolidated WHERE and HAVING clause mixins."""
|
|
3
3
|
|
|
4
|
-
from typing import TYPE_CHECKING, Any,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
5
5
|
|
|
6
6
|
from sqlglot import exp
|
|
7
7
|
from typing_extensions import Self
|
|
8
8
|
|
|
9
|
+
from sqlspec.builder._parsing_utils import parse_column_expression, parse_condition_expression
|
|
9
10
|
from sqlspec.exceptions import SQLBuilderError
|
|
10
|
-
from sqlspec.statement.builder._parsing_utils import parse_column_expression, parse_condition_expression
|
|
11
11
|
from sqlspec.utils.type_guards import has_query_builder_parameters, has_sqlglot_expression, is_iterable_parameters
|
|
12
12
|
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from sqlspec.protocols import SQLBuilderProtocol
|
|
15
|
-
from sqlspec.statement.builder._column import ColumnExpression
|
|
16
|
-
|
|
17
|
-
__all__ = ("HavingClauseMixin", "WhereClauseMixin")
|
|
18
13
|
|
|
14
|
+
def _extract_column_name(column: Union[str, exp.Column]) -> str:
|
|
15
|
+
"""Extract column name from column expression for parameter naming.
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
Args:
|
|
18
|
+
column: Column expression (string or SQLGlot Column)
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
Returns:
|
|
21
|
+
Column name as string for use as parameter name
|
|
22
|
+
"""
|
|
23
|
+
if isinstance(column, str):
|
|
24
|
+
# Handle simple column names and table.column references
|
|
25
|
+
if "." in column:
|
|
26
|
+
return column.split(".")[-1] # Return just the column part
|
|
27
|
+
return column
|
|
28
|
+
if isinstance(column, exp.Column):
|
|
29
|
+
# Extract the column name from SQLGlot Column expression
|
|
30
|
+
if column.this and hasattr(column.this, "this"):
|
|
31
|
+
return str(column.this.this)
|
|
32
|
+
return str(column.this) if column.this else "column"
|
|
33
|
+
return "column"
|
|
25
34
|
|
|
26
|
-
def handler(self: "SQLBuilderProtocol", column_exp: exp.Expression, value: Any) -> exp.Expression:
|
|
27
|
-
_, param_name = self.add_parameter(value)
|
|
28
|
-
return operator_class(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"""Create LIKE handler."""
|
|
34
|
-
|
|
35
|
-
def handler(self: "SQLBuilderProtocol", column_exp: exp.Expression, value: Any) -> exp.Expression:
|
|
36
|
-
_, param_name = self.add_parameter(value)
|
|
37
|
-
return exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
38
|
-
|
|
39
|
-
return handler
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from sqlspec.builder._column import ColumnExpression
|
|
38
|
+
from sqlspec.protocols import SQLBuilderProtocol
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
"""Create NOT LIKE handler."""
|
|
40
|
+
__all__ = ("HavingClauseMixin", "WhereClauseMixin")
|
|
43
41
|
|
|
44
|
-
def handler(self: "SQLBuilderProtocol", column_exp: exp.Expression, value: Any) -> exp.Expression:
|
|
45
|
-
_, param_name = self.add_parameter(value)
|
|
46
|
-
return exp.Not(this=exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name)))
|
|
47
42
|
|
|
48
|
-
|
|
43
|
+
class WhereClauseMixin:
|
|
44
|
+
"""Mixin providing WHERE clause methods for SELECT, UPDATE, and DELETE builders."""
|
|
49
45
|
|
|
50
|
-
def _handle_in_operator(
|
|
46
|
+
def _handle_in_operator(
|
|
47
|
+
self, column_exp: exp.Expression, value: Any, column_name: str = "column"
|
|
48
|
+
) -> exp.Expression:
|
|
51
49
|
"""Handle IN operator."""
|
|
52
50
|
builder = cast("SQLBuilderProtocol", self)
|
|
53
51
|
if is_iterable_parameters(value):
|
|
54
52
|
placeholders = []
|
|
55
|
-
for v in value:
|
|
56
|
-
|
|
53
|
+
for i, v in enumerate(value):
|
|
54
|
+
if len(value) == 1:
|
|
55
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
56
|
+
else:
|
|
57
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
|
|
58
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
57
59
|
placeholders.append(exp.Placeholder(this=param_name))
|
|
58
60
|
return exp.In(this=column_exp, expressions=placeholders)
|
|
59
|
-
|
|
61
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
62
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
60
63
|
return exp.In(this=column_exp, expressions=[exp.Placeholder(this=param_name)])
|
|
61
64
|
|
|
62
|
-
def _handle_not_in_operator(
|
|
65
|
+
def _handle_not_in_operator(
|
|
66
|
+
self, column_exp: exp.Expression, value: Any, column_name: str = "column"
|
|
67
|
+
) -> exp.Expression:
|
|
63
68
|
"""Handle NOT IN operator."""
|
|
64
69
|
builder = cast("SQLBuilderProtocol", self)
|
|
65
70
|
if is_iterable_parameters(value):
|
|
66
71
|
placeholders = []
|
|
67
|
-
for v in value:
|
|
68
|
-
|
|
72
|
+
for i, v in enumerate(value):
|
|
73
|
+
if len(value) == 1:
|
|
74
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
75
|
+
else:
|
|
76
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
|
|
77
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
69
78
|
placeholders.append(exp.Placeholder(this=param_name))
|
|
70
79
|
return exp.Not(this=exp.In(this=column_exp, expressions=placeholders))
|
|
71
|
-
|
|
80
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
81
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
72
82
|
return exp.Not(this=exp.In(this=column_exp, expressions=[exp.Placeholder(this=param_name)]))
|
|
73
83
|
|
|
74
84
|
def _handle_is_operator(self, column_exp: exp.Expression, value: Any) -> exp.Expression:
|
|
@@ -81,26 +91,34 @@ class WhereClauseMixin:
|
|
|
81
91
|
value_expr = exp.Null() if value is None else exp.convert(value)
|
|
82
92
|
return exp.Not(this=exp.Is(this=column_exp, expression=value_expr))
|
|
83
93
|
|
|
84
|
-
def _handle_between_operator(
|
|
94
|
+
def _handle_between_operator(
|
|
95
|
+
self, column_exp: exp.Expression, value: Any, column_name: str = "column"
|
|
96
|
+
) -> exp.Expression:
|
|
85
97
|
"""Handle BETWEEN operator."""
|
|
86
98
|
if is_iterable_parameters(value) and len(value) == 2:
|
|
87
99
|
builder = cast("SQLBuilderProtocol", self)
|
|
88
100
|
low, high = value
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
|
|
102
|
+
high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
|
|
103
|
+
_, low_param = builder.add_parameter(low, name=low_param)
|
|
104
|
+
_, high_param = builder.add_parameter(high, name=high_param)
|
|
91
105
|
return exp.Between(
|
|
92
106
|
this=column_exp, low=exp.Placeholder(this=low_param), high=exp.Placeholder(this=high_param)
|
|
93
107
|
)
|
|
94
108
|
msg = f"BETWEEN operator requires a tuple of two values, got {type(value).__name__}"
|
|
95
109
|
raise SQLBuilderError(msg)
|
|
96
110
|
|
|
97
|
-
def _handle_not_between_operator(
|
|
111
|
+
def _handle_not_between_operator(
|
|
112
|
+
self, column_exp: exp.Expression, value: Any, column_name: str = "column"
|
|
113
|
+
) -> exp.Expression:
|
|
98
114
|
"""Handle NOT BETWEEN operator."""
|
|
99
115
|
if is_iterable_parameters(value) and len(value) == 2:
|
|
100
116
|
builder = cast("SQLBuilderProtocol", self)
|
|
101
117
|
low, high = value
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
|
|
119
|
+
high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
|
|
120
|
+
_, low_param = builder.add_parameter(low, name=low_param)
|
|
121
|
+
_, high_param = builder.add_parameter(high, name=high_param)
|
|
104
122
|
return exp.Not(
|
|
105
123
|
this=exp.Between(
|
|
106
124
|
this=column_exp, low=exp.Placeholder(this=low_param), high=exp.Placeholder(this=high_param)
|
|
@@ -112,13 +130,15 @@ class WhereClauseMixin:
|
|
|
112
130
|
def _process_tuple_condition(self, condition: tuple) -> exp.Expression:
|
|
113
131
|
"""Process tuple-based WHERE conditions."""
|
|
114
132
|
builder = cast("SQLBuilderProtocol", self)
|
|
115
|
-
|
|
116
|
-
column_exp = parse_column_expression(
|
|
133
|
+
column_name_raw = str(condition[0])
|
|
134
|
+
column_exp = parse_column_expression(column_name_raw)
|
|
135
|
+
column_name = _extract_column_name(column_name_raw)
|
|
117
136
|
|
|
118
137
|
if len(condition) == 2:
|
|
119
138
|
# (column, value) tuple for equality
|
|
120
139
|
value = condition[1]
|
|
121
|
-
|
|
140
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
141
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
122
142
|
return exp.EQ(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
123
143
|
|
|
124
144
|
if len(condition) == 3:
|
|
@@ -126,45 +146,51 @@ class WhereClauseMixin:
|
|
|
126
146
|
operator = str(condition[1]).upper()
|
|
127
147
|
value = condition[2]
|
|
128
148
|
|
|
129
|
-
# Handle simple operators
|
|
130
149
|
if operator == "=":
|
|
131
|
-
|
|
150
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
151
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
132
152
|
return exp.EQ(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
133
153
|
if operator in {"!=", "<>"}:
|
|
134
|
-
|
|
154
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
155
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
135
156
|
return exp.NEQ(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
136
157
|
if operator == ">":
|
|
137
|
-
|
|
158
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
159
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
138
160
|
return exp.GT(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
139
161
|
if operator == ">=":
|
|
140
|
-
|
|
162
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
163
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
141
164
|
return exp.GTE(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
142
165
|
if operator == "<":
|
|
143
|
-
|
|
166
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
167
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
144
168
|
return exp.LT(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
145
169
|
if operator == "<=":
|
|
146
|
-
|
|
170
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
171
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
147
172
|
return exp.LTE(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
148
173
|
if operator == "LIKE":
|
|
149
|
-
|
|
174
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
175
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
150
176
|
return exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name))
|
|
151
177
|
if operator == "NOT LIKE":
|
|
152
|
-
|
|
178
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
179
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
153
180
|
return exp.Not(this=exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name)))
|
|
154
181
|
|
|
155
|
-
# Handle complex operators
|
|
156
182
|
if operator == "IN":
|
|
157
|
-
return self._handle_in_operator(column_exp, value)
|
|
183
|
+
return self._handle_in_operator(column_exp, value, column_name)
|
|
158
184
|
if operator == "NOT IN":
|
|
159
|
-
return self._handle_not_in_operator(column_exp, value)
|
|
185
|
+
return self._handle_not_in_operator(column_exp, value, column_name)
|
|
160
186
|
if operator == "IS":
|
|
161
187
|
return self._handle_is_operator(column_exp, value)
|
|
162
188
|
if operator == "IS NOT":
|
|
163
189
|
return self._handle_is_not_operator(column_exp, value)
|
|
164
190
|
if operator == "BETWEEN":
|
|
165
|
-
return self._handle_between_operator(column_exp, value)
|
|
191
|
+
return self._handle_between_operator(column_exp, value, column_name)
|
|
166
192
|
if operator == "NOT BETWEEN":
|
|
167
|
-
return self._handle_not_between_operator(column_exp, value)
|
|
193
|
+
return self._handle_not_between_operator(column_exp, value, column_name)
|
|
168
194
|
|
|
169
195
|
msg = f"Unsupported operator: {operator}"
|
|
170
196
|
raise SQLBuilderError(msg)
|
|
@@ -175,15 +201,20 @@ class WhereClauseMixin:
|
|
|
175
201
|
def where(
|
|
176
202
|
self,
|
|
177
203
|
condition: Union[str, exp.Expression, exp.Condition, tuple[str, Any], tuple[str, str, Any], "ColumnExpression"],
|
|
204
|
+
value: Optional[Any] = None,
|
|
205
|
+
operator: Optional[str] = None,
|
|
178
206
|
) -> Self:
|
|
179
207
|
"""Add a WHERE clause to the statement.
|
|
180
208
|
|
|
181
209
|
Args:
|
|
182
210
|
condition: The condition for the WHERE clause. Can be:
|
|
183
|
-
- A string condition
|
|
211
|
+
- A string condition (when value is None)
|
|
212
|
+
- A string column name (when value is provided)
|
|
184
213
|
- A sqlglot Expression or Condition
|
|
185
214
|
- A 2-tuple (column, value) for equality comparison
|
|
186
215
|
- A 3-tuple (column, operator, value) for custom comparison
|
|
216
|
+
value: Value for comparison (when condition is a column name)
|
|
217
|
+
operator: Operator for comparison (when both condition and value provided)
|
|
187
218
|
|
|
188
219
|
Raises:
|
|
189
220
|
SQLBuilderError: If the current expression is not a supported statement type.
|
|
@@ -191,10 +222,7 @@ class WhereClauseMixin:
|
|
|
191
222
|
Returns:
|
|
192
223
|
The current builder instance for method chaining.
|
|
193
224
|
"""
|
|
194
|
-
|
|
195
|
-
if self.__class__.__name__ == "Update" and not (
|
|
196
|
-
hasattr(self, "_expression") and isinstance(getattr(self, "_expression", None), exp.Update)
|
|
197
|
-
):
|
|
225
|
+
if self.__class__.__name__ == "Update" and not isinstance(self._expression, exp.Update): # type: ignore[attr-defined]
|
|
198
226
|
msg = "Cannot add WHERE clause to non-UPDATE expression"
|
|
199
227
|
raise SQLBuilderError(msg)
|
|
200
228
|
|
|
@@ -203,35 +231,38 @@ class WhereClauseMixin:
|
|
|
203
231
|
msg = "Cannot add WHERE clause: expression is not initialized."
|
|
204
232
|
raise SQLBuilderError(msg)
|
|
205
233
|
|
|
206
|
-
# Check if DELETE has a table set
|
|
207
234
|
if isinstance(builder._expression, exp.Delete) and not builder._expression.args.get("this"):
|
|
208
235
|
msg = "WHERE clause requires a table to be set. Use from() to set the table first."
|
|
209
236
|
raise SQLBuilderError(msg)
|
|
210
237
|
|
|
211
|
-
|
|
212
|
-
|
|
238
|
+
if value is not None:
|
|
239
|
+
if not isinstance(condition, str):
|
|
240
|
+
msg = "When value is provided, condition must be a column name (string)"
|
|
241
|
+
raise SQLBuilderError(msg)
|
|
242
|
+
|
|
243
|
+
if operator is not None:
|
|
244
|
+
where_expr = self._process_tuple_condition((condition, operator, value))
|
|
245
|
+
else:
|
|
246
|
+
where_expr = self._process_tuple_condition((condition, value))
|
|
247
|
+
elif isinstance(condition, str):
|
|
213
248
|
where_expr = parse_condition_expression(condition)
|
|
214
249
|
elif isinstance(condition, (exp.Expression, exp.Condition)):
|
|
215
250
|
where_expr = condition
|
|
216
251
|
elif isinstance(condition, tuple):
|
|
217
252
|
where_expr = self._process_tuple_condition(condition)
|
|
218
253
|
elif has_query_builder_parameters(condition):
|
|
219
|
-
# Handle ColumnExpression objects
|
|
220
254
|
column_expr_obj = cast("ColumnExpression", condition)
|
|
221
255
|
where_expr = column_expr_obj._expression # pyright: ignore
|
|
222
256
|
elif has_sqlglot_expression(condition):
|
|
223
|
-
|
|
224
|
-
raw_expr = getattr(condition, "sqlglot_expression", None)
|
|
257
|
+
raw_expr = condition.sqlglot_expression # pyright: ignore[attr-defined]
|
|
225
258
|
if raw_expr is not None:
|
|
226
259
|
where_expr = builder._parameterize_expression(raw_expr)
|
|
227
260
|
else:
|
|
228
|
-
# Fallback if attribute exists but is None
|
|
229
261
|
where_expr = parse_condition_expression(str(condition))
|
|
230
262
|
else:
|
|
231
263
|
msg = f"Unsupported condition type: {type(condition).__name__}"
|
|
232
264
|
raise SQLBuilderError(msg)
|
|
233
265
|
|
|
234
|
-
# Apply WHERE clause based on statement type
|
|
235
266
|
if isinstance(builder._expression, (exp.Select, exp.Update, exp.Delete)):
|
|
236
267
|
builder._expression = builder._expression.where(where_expr, copy=False)
|
|
237
268
|
else:
|
|
@@ -242,86 +273,107 @@ class WhereClauseMixin:
|
|
|
242
273
|
def where_eq(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
243
274
|
"""Add WHERE column = value clause."""
|
|
244
275
|
builder = cast("SQLBuilderProtocol", self)
|
|
245
|
-
|
|
276
|
+
column_name = _extract_column_name(column)
|
|
277
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
278
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
246
279
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
247
|
-
condition: exp.Expression = col_expr.eq(exp.
|
|
280
|
+
condition: exp.Expression = col_expr.eq(exp.Placeholder(this=param_name))
|
|
248
281
|
return self.where(condition)
|
|
249
282
|
|
|
250
283
|
def where_neq(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
251
284
|
"""Add WHERE column != value clause."""
|
|
252
285
|
builder = cast("SQLBuilderProtocol", self)
|
|
253
|
-
|
|
286
|
+
column_name = _extract_column_name(column)
|
|
287
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
288
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
254
289
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
255
|
-
condition: exp.Expression = col_expr.neq(exp.
|
|
290
|
+
condition: exp.Expression = col_expr.neq(exp.Placeholder(this=param_name))
|
|
256
291
|
return self.where(condition)
|
|
257
292
|
|
|
258
293
|
def where_lt(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
259
294
|
"""Add WHERE column < value clause."""
|
|
260
295
|
builder = cast("SQLBuilderProtocol", self)
|
|
261
|
-
|
|
296
|
+
column_name = _extract_column_name(column)
|
|
297
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
298
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
262
299
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
263
|
-
condition: exp.Expression = exp.LT(this=col_expr, expression=exp.
|
|
300
|
+
condition: exp.Expression = exp.LT(this=col_expr, expression=exp.Placeholder(this=param_name))
|
|
264
301
|
return self.where(condition)
|
|
265
302
|
|
|
266
303
|
def where_lte(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
267
304
|
"""Add WHERE column <= value clause."""
|
|
268
305
|
builder = cast("SQLBuilderProtocol", self)
|
|
269
|
-
|
|
306
|
+
column_name = _extract_column_name(column)
|
|
307
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
308
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
270
309
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
271
|
-
condition: exp.Expression = exp.LTE(this=col_expr, expression=exp.
|
|
310
|
+
condition: exp.Expression = exp.LTE(this=col_expr, expression=exp.Placeholder(this=param_name))
|
|
272
311
|
return self.where(condition)
|
|
273
312
|
|
|
274
313
|
def where_gt(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
275
314
|
"""Add WHERE column > value clause."""
|
|
276
315
|
builder = cast("SQLBuilderProtocol", self)
|
|
277
|
-
|
|
316
|
+
column_name = _extract_column_name(column)
|
|
317
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
318
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
278
319
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
279
|
-
condition: exp.Expression = exp.GT(this=col_expr, expression=exp.
|
|
320
|
+
condition: exp.Expression = exp.GT(this=col_expr, expression=exp.Placeholder(this=param_name))
|
|
280
321
|
return self.where(condition)
|
|
281
322
|
|
|
282
323
|
def where_gte(self, column: Union[str, exp.Column], value: Any) -> Self:
|
|
283
324
|
"""Add WHERE column >= value clause."""
|
|
284
325
|
builder = cast("SQLBuilderProtocol", self)
|
|
285
|
-
|
|
326
|
+
column_name = _extract_column_name(column)
|
|
327
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
328
|
+
_, param_name = builder.add_parameter(value, name=param_name)
|
|
286
329
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
287
|
-
condition: exp.Expression = exp.GTE(this=col_expr, expression=exp.
|
|
330
|
+
condition: exp.Expression = exp.GTE(this=col_expr, expression=exp.Placeholder(this=param_name))
|
|
288
331
|
return self.where(condition)
|
|
289
332
|
|
|
290
333
|
def where_between(self, column: Union[str, exp.Column], low: Any, high: Any) -> Self:
|
|
291
334
|
"""Add WHERE column BETWEEN low AND high clause."""
|
|
292
335
|
builder = cast("SQLBuilderProtocol", self)
|
|
293
|
-
|
|
294
|
-
|
|
336
|
+
column_name = _extract_column_name(column)
|
|
337
|
+
low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
|
|
338
|
+
high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
|
|
339
|
+
_, low_param = builder.add_parameter(low, name=low_param)
|
|
340
|
+
_, high_param = builder.add_parameter(high, name=high_param)
|
|
295
341
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
296
|
-
condition: exp.Expression = col_expr.between(exp.
|
|
342
|
+
condition: exp.Expression = col_expr.between(exp.Placeholder(this=low_param), exp.Placeholder(this=high_param))
|
|
297
343
|
return self.where(condition)
|
|
298
344
|
|
|
299
345
|
def where_like(self, column: Union[str, exp.Column], pattern: str, escape: Optional[str] = None) -> Self:
|
|
300
346
|
"""Add WHERE column LIKE pattern clause."""
|
|
301
347
|
builder = cast("SQLBuilderProtocol", self)
|
|
302
|
-
|
|
348
|
+
column_name = _extract_column_name(column)
|
|
349
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
350
|
+
_, param_name = builder.add_parameter(pattern, name=param_name)
|
|
303
351
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
304
352
|
if escape is not None:
|
|
305
|
-
cond = exp.Like(this=col_expr, expression=exp.
|
|
353
|
+
cond = exp.Like(this=col_expr, expression=exp.Placeholder(this=param_name), escape=exp.convert(str(escape)))
|
|
306
354
|
else:
|
|
307
|
-
cond = col_expr.like(exp.
|
|
355
|
+
cond = col_expr.like(exp.Placeholder(this=param_name))
|
|
308
356
|
condition: exp.Expression = cond
|
|
309
357
|
return self.where(condition)
|
|
310
358
|
|
|
311
359
|
def where_not_like(self, column: Union[str, exp.Column], pattern: str) -> Self:
|
|
312
360
|
"""Add WHERE column NOT LIKE pattern clause."""
|
|
313
361
|
builder = cast("SQLBuilderProtocol", self)
|
|
314
|
-
|
|
362
|
+
column_name = _extract_column_name(column)
|
|
363
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
364
|
+
_, param_name = builder.add_parameter(pattern, name=param_name)
|
|
315
365
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
316
|
-
condition: exp.Expression = col_expr.like(exp.
|
|
366
|
+
condition: exp.Expression = col_expr.like(exp.Placeholder(this=param_name)).not_()
|
|
317
367
|
return self.where(condition)
|
|
318
368
|
|
|
319
369
|
def where_ilike(self, column: Union[str, exp.Column], pattern: str) -> Self:
|
|
320
370
|
"""Add WHERE column ILIKE pattern clause."""
|
|
321
371
|
builder = cast("SQLBuilderProtocol", self)
|
|
322
|
-
|
|
372
|
+
column_name = _extract_column_name(column)
|
|
373
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
374
|
+
_, param_name = builder.add_parameter(pattern, name=param_name)
|
|
323
375
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
324
|
-
condition: exp.Expression = col_expr.ilike(exp.
|
|
376
|
+
condition: exp.Expression = col_expr.ilike(exp.Placeholder(this=param_name))
|
|
325
377
|
return self.where(condition)
|
|
326
378
|
|
|
327
379
|
def where_is_null(self, column: Union[str, exp.Column]) -> Self:
|
|
@@ -344,8 +396,12 @@ class WhereClauseMixin:
|
|
|
344
396
|
subquery_exp: exp.Expression
|
|
345
397
|
if has_query_builder_parameters(values):
|
|
346
398
|
subquery = values.build() # pyright: ignore
|
|
347
|
-
sql_str =
|
|
348
|
-
subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=
|
|
399
|
+
sql_str = subquery.sql
|
|
400
|
+
subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=builder.dialect_name)) # pyright: ignore
|
|
401
|
+
# Merge subquery parameters into parent builder
|
|
402
|
+
if hasattr(subquery, "parameters"):
|
|
403
|
+
for param_name, param_value in subquery.parameters.items(): # pyright: ignore[reportAttributeAccessIssue]
|
|
404
|
+
builder.add_parameter(param_value, name=param_name)
|
|
349
405
|
else:
|
|
350
406
|
subquery_exp = values # type: ignore[assignment]
|
|
351
407
|
condition = col_expr.isin(subquery_exp)
|
|
@@ -353,11 +409,16 @@ class WhereClauseMixin:
|
|
|
353
409
|
if not is_iterable_parameters(values) or isinstance(values, (str, bytes)):
|
|
354
410
|
msg = "Unsupported type for 'values' in WHERE IN"
|
|
355
411
|
raise SQLBuilderError(msg)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
412
|
+
column_name = _extract_column_name(column)
|
|
413
|
+
parameters = []
|
|
414
|
+
for i, v in enumerate(values):
|
|
415
|
+
if len(values) == 1:
|
|
416
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
417
|
+
else:
|
|
418
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
|
|
419
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
420
|
+
parameters.append(exp.Placeholder(this=param_name))
|
|
421
|
+
condition = col_expr.isin(*parameters)
|
|
361
422
|
return self.where(condition)
|
|
362
423
|
|
|
363
424
|
def where_not_in(self, column: Union[str, exp.Column], values: Any) -> Self:
|
|
@@ -368,8 +429,8 @@ class WhereClauseMixin:
|
|
|
368
429
|
subquery_exp: exp.Expression
|
|
369
430
|
if has_query_builder_parameters(values):
|
|
370
431
|
subquery = values.build() # pyright: ignore
|
|
371
|
-
|
|
372
|
-
subquery_exp = exp.paren(exp.maybe_parse(
|
|
432
|
+
|
|
433
|
+
subquery_exp = exp.paren(exp.maybe_parse(subquery.sql, dialect=builder.dialect_name)) # pyright: ignore
|
|
373
434
|
else:
|
|
374
435
|
subquery_exp = values # type: ignore[assignment]
|
|
375
436
|
condition = exp.Not(this=col_expr.isin(subquery_exp))
|
|
@@ -377,11 +438,16 @@ class WhereClauseMixin:
|
|
|
377
438
|
if not is_iterable_parameters(values) or isinstance(values, (str, bytes)):
|
|
378
439
|
msg = "Values for where_not_in must be a non-string iterable or subquery."
|
|
379
440
|
raise SQLBuilderError(msg)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
441
|
+
column_name = _extract_column_name(column)
|
|
442
|
+
parameters = []
|
|
443
|
+
for i, v in enumerate(values):
|
|
444
|
+
if len(values) == 1:
|
|
445
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
446
|
+
else:
|
|
447
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
|
|
448
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
449
|
+
parameters.append(exp.Placeholder(this=param_name))
|
|
450
|
+
condition = exp.Not(this=col_expr.isin(*parameters))
|
|
385
451
|
return self.where(condition)
|
|
386
452
|
|
|
387
453
|
def where_null(self, column: Union[str, exp.Column]) -> Self:
|
|
@@ -397,15 +463,15 @@ class WhereClauseMixin:
|
|
|
397
463
|
builder = cast("SQLBuilderProtocol", self)
|
|
398
464
|
sub_expr: exp.Expression
|
|
399
465
|
if has_query_builder_parameters(subquery):
|
|
400
|
-
|
|
401
|
-
if
|
|
402
|
-
for p_name, p_value in
|
|
466
|
+
subquery_builder_parameters: dict[str, Any] = subquery.parameters
|
|
467
|
+
if subquery_builder_parameters:
|
|
468
|
+
for p_name, p_value in subquery_builder_parameters.items():
|
|
403
469
|
builder.add_parameter(p_value, name=p_name)
|
|
404
470
|
sub_sql_obj = subquery.build() # pyright: ignore
|
|
405
|
-
|
|
406
|
-
sub_expr = exp.maybe_parse(
|
|
471
|
+
|
|
472
|
+
sub_expr = exp.maybe_parse(sub_sql_obj.sql, dialect=builder.dialect_name) # pyright: ignore
|
|
407
473
|
else:
|
|
408
|
-
sub_expr = exp.maybe_parse(str(subquery), dialect=
|
|
474
|
+
sub_expr = exp.maybe_parse(str(subquery), dialect=builder.dialect_name)
|
|
409
475
|
|
|
410
476
|
if sub_expr is None:
|
|
411
477
|
msg = "Could not parse subquery for EXISTS"
|
|
@@ -419,15 +485,14 @@ class WhereClauseMixin:
|
|
|
419
485
|
builder = cast("SQLBuilderProtocol", self)
|
|
420
486
|
sub_expr: exp.Expression
|
|
421
487
|
if has_query_builder_parameters(subquery):
|
|
422
|
-
|
|
423
|
-
if
|
|
424
|
-
for p_name, p_value in
|
|
488
|
+
subquery_builder_parameters: dict[str, Any] = subquery.parameters
|
|
489
|
+
if subquery_builder_parameters:
|
|
490
|
+
for p_name, p_value in subquery_builder_parameters.items():
|
|
425
491
|
builder.add_parameter(p_value, name=p_name)
|
|
426
492
|
sub_sql_obj = subquery.build() # pyright: ignore
|
|
427
|
-
|
|
428
|
-
sub_expr = exp.maybe_parse(sql_str, dialect=getattr(builder, "dialect_name", None))
|
|
493
|
+
sub_expr = exp.maybe_parse(sub_sql_obj.sql, dialect=builder.dialect_name) # pyright: ignore
|
|
429
494
|
else:
|
|
430
|
-
sub_expr = exp.maybe_parse(str(subquery), dialect=
|
|
495
|
+
sub_expr = exp.maybe_parse(str(subquery), dialect=builder.dialect_name)
|
|
431
496
|
|
|
432
497
|
if sub_expr is None:
|
|
433
498
|
msg = "Could not parse subquery for NOT EXISTS"
|
|
@@ -444,8 +509,7 @@ class WhereClauseMixin:
|
|
|
444
509
|
subquery_exp: exp.Expression
|
|
445
510
|
if has_query_builder_parameters(values):
|
|
446
511
|
subquery = values.build() # pyright: ignore
|
|
447
|
-
|
|
448
|
-
subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(builder, "dialect_name", None)))
|
|
512
|
+
subquery_exp = exp.paren(exp.maybe_parse(subquery.sql, dialect=builder.dialect_name)) # pyright: ignore
|
|
449
513
|
else:
|
|
450
514
|
subquery_exp = values # type: ignore[assignment]
|
|
451
515
|
condition = exp.EQ(this=col_expr, expression=exp.Any(this=subquery_exp))
|
|
@@ -464,24 +528,28 @@ class WhereClauseMixin:
|
|
|
464
528
|
if not is_iterable_parameters(values) or isinstance(values, bytes):
|
|
465
529
|
msg = "Unsupported type for 'values' in WHERE ANY"
|
|
466
530
|
raise SQLBuilderError(msg)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
531
|
+
column_name = _extract_column_name(column)
|
|
532
|
+
parameters = []
|
|
533
|
+
for i, v in enumerate(values):
|
|
534
|
+
if len(values) == 1:
|
|
535
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
536
|
+
else:
|
|
537
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_any_{i + 1}") # type: ignore[attr-defined]
|
|
538
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
539
|
+
parameters.append(exp.Placeholder(this=param_name))
|
|
540
|
+
tuple_expr = exp.Tuple(expressions=parameters)
|
|
472
541
|
condition = exp.EQ(this=col_expr, expression=exp.Any(this=tuple_expr))
|
|
473
542
|
return self.where(condition)
|
|
474
543
|
|
|
475
544
|
def where_not_any(self, column: Union[str, exp.Column], values: Any) -> Self:
|
|
476
|
-
"""Add WHERE column
|
|
545
|
+
"""Add WHERE column <> ANY(values) clause."""
|
|
477
546
|
builder = cast("SQLBuilderProtocol", self)
|
|
478
547
|
col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
|
|
479
548
|
if has_query_builder_parameters(values) or isinstance(values, exp.Expression):
|
|
480
549
|
subquery_exp: exp.Expression
|
|
481
550
|
if has_query_builder_parameters(values):
|
|
482
551
|
subquery = values.build() # pyright: ignore
|
|
483
|
-
|
|
484
|
-
subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(builder, "dialect_name", None)))
|
|
552
|
+
subquery_exp = exp.paren(exp.maybe_parse(subquery.sql, dialect=builder.dialect_name)) # pyright: ignore
|
|
485
553
|
else:
|
|
486
554
|
subquery_exp = values # type: ignore[assignment]
|
|
487
555
|
condition = exp.NEQ(this=col_expr, expression=exp.Any(this=subquery_exp))
|
|
@@ -500,11 +568,16 @@ class WhereClauseMixin:
|
|
|
500
568
|
if not is_iterable_parameters(values) or isinstance(values, bytes):
|
|
501
569
|
msg = "Unsupported type for 'values' in WHERE NOT ANY"
|
|
502
570
|
raise SQLBuilderError(msg)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
571
|
+
column_name = _extract_column_name(column)
|
|
572
|
+
parameters = []
|
|
573
|
+
for i, v in enumerate(values):
|
|
574
|
+
if len(values) == 1:
|
|
575
|
+
param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
|
|
576
|
+
else:
|
|
577
|
+
param_name = builder._generate_unique_parameter_name(f"{column_name}_not_any_{i + 1}") # type: ignore[attr-defined]
|
|
578
|
+
_, param_name = builder.add_parameter(v, name=param_name)
|
|
579
|
+
parameters.append(exp.Placeholder(this=param_name))
|
|
580
|
+
tuple_expr = exp.Tuple(expressions=parameters)
|
|
508
581
|
condition = exp.NEQ(this=col_expr, expression=exp.Any(this=tuple_expr))
|
|
509
582
|
return self.where(condition)
|
|
510
583
|
|