sqlspec 0.17.0__py3-none-any.whl → 0.18.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 +1 -1
- sqlspec/_sql.py +188 -234
- sqlspec/adapters/adbc/config.py +24 -30
- sqlspec/adapters/adbc/driver.py +42 -61
- sqlspec/adapters/aiosqlite/config.py +5 -10
- sqlspec/adapters/aiosqlite/driver.py +9 -25
- sqlspec/adapters/aiosqlite/pool.py +43 -35
- sqlspec/adapters/asyncmy/config.py +10 -7
- sqlspec/adapters/asyncmy/driver.py +18 -39
- sqlspec/adapters/asyncpg/config.py +4 -0
- sqlspec/adapters/asyncpg/driver.py +32 -79
- sqlspec/adapters/bigquery/config.py +12 -65
- sqlspec/adapters/bigquery/driver.py +39 -133
- sqlspec/adapters/duckdb/config.py +11 -15
- sqlspec/adapters/duckdb/driver.py +61 -85
- sqlspec/adapters/duckdb/pool.py +2 -5
- sqlspec/adapters/oracledb/_types.py +8 -1
- sqlspec/adapters/oracledb/config.py +55 -38
- sqlspec/adapters/oracledb/driver.py +35 -92
- sqlspec/adapters/oracledb/migrations.py +257 -0
- sqlspec/adapters/psqlpy/config.py +13 -9
- sqlspec/adapters/psqlpy/driver.py +28 -103
- sqlspec/adapters/psycopg/config.py +9 -5
- sqlspec/adapters/psycopg/driver.py +107 -175
- sqlspec/adapters/sqlite/config.py +7 -5
- sqlspec/adapters/sqlite/driver.py +37 -73
- sqlspec/adapters/sqlite/pool.py +3 -12
- sqlspec/base.py +1 -8
- sqlspec/builder/__init__.py +1 -1
- sqlspec/builder/_base.py +34 -20
- sqlspec/builder/_column.py +5 -1
- sqlspec/builder/_ddl.py +407 -183
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_insert.py +2 -4
- sqlspec/builder/_update.py +5 -5
- sqlspec/builder/mixins/_insert_operations.py +26 -6
- sqlspec/builder/mixins/_merge_operations.py +1 -1
- sqlspec/builder/mixins/_order_limit_operations.py +16 -4
- sqlspec/builder/mixins/_select_operations.py +3 -7
- sqlspec/builder/mixins/_update_operations.py +4 -4
- sqlspec/config.py +32 -13
- sqlspec/core/__init__.py +89 -14
- sqlspec/core/cache.py +57 -104
- sqlspec/core/compiler.py +57 -112
- sqlspec/core/filters.py +1 -21
- sqlspec/core/hashing.py +13 -47
- sqlspec/core/parameters.py +272 -261
- sqlspec/core/result.py +12 -27
- sqlspec/core/splitter.py +17 -21
- sqlspec/core/statement.py +150 -159
- sqlspec/driver/_async.py +2 -15
- sqlspec/driver/_common.py +16 -95
- sqlspec/driver/_sync.py +2 -15
- sqlspec/driver/mixins/_result_tools.py +8 -29
- sqlspec/driver/mixins/_sql_translator.py +6 -8
- sqlspec/exceptions.py +1 -2
- sqlspec/loader.py +43 -115
- sqlspec/migrations/__init__.py +1 -1
- sqlspec/migrations/base.py +34 -45
- sqlspec/migrations/commands.py +34 -15
- sqlspec/migrations/loaders.py +1 -1
- sqlspec/migrations/runner.py +104 -19
- sqlspec/migrations/tracker.py +49 -2
- sqlspec/protocols.py +13 -6
- sqlspec/storage/__init__.py +4 -4
- sqlspec/storage/backends/fsspec.py +5 -6
- sqlspec/storage/backends/obstore.py +7 -8
- sqlspec/storage/registry.py +3 -3
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/logging.py +6 -10
- sqlspec/utils/sync_tools.py +27 -4
- sqlspec/utils/text.py +6 -1
- {sqlspec-0.17.0.dist-info → sqlspec-0.18.0.dist-info}/METADATA +1 -1
- sqlspec-0.18.0.dist-info/RECORD +138 -0
- sqlspec/builder/_ddl_utils.py +0 -103
- sqlspec-0.17.0.dist-info/RECORD +0 -137
- {sqlspec-0.17.0.dist-info → sqlspec-0.18.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.17.0.dist-info → sqlspec-0.18.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.17.0.dist-info → sqlspec-0.18.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.17.0.dist-info → sqlspec-0.18.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/core/compiler.py
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
"""SQL
|
|
2
|
-
|
|
3
|
-
This module implements the core compilation system for SQL statements with
|
|
4
|
-
integrated parameter processing and caching.
|
|
1
|
+
"""SQL compilation and caching.
|
|
5
2
|
|
|
6
3
|
Components:
|
|
7
|
-
- CompiledSQL: Immutable compilation result
|
|
8
|
-
- SQLProcessor:
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
Features:
|
|
12
|
-
- Single SQLGlot parse for efficient processing
|
|
13
|
-
- AST-based operation type detection
|
|
14
|
-
- Unified caching system with LRU eviction
|
|
15
|
-
- Complete StatementConfig support
|
|
4
|
+
- CompiledSQL: Immutable compilation result
|
|
5
|
+
- SQLProcessor: SQL compiler with caching
|
|
6
|
+
- Parameter processing via ParameterProcessor
|
|
16
7
|
"""
|
|
17
8
|
|
|
18
9
|
import hashlib
|
|
@@ -31,7 +22,7 @@ from sqlspec.utils.logging import get_logger
|
|
|
31
22
|
if TYPE_CHECKING:
|
|
32
23
|
from sqlspec.core.statement import StatementConfig
|
|
33
24
|
|
|
34
|
-
|
|
25
|
+
|
|
35
26
|
OperationType = Literal[
|
|
36
27
|
"SELECT",
|
|
37
28
|
"INSERT",
|
|
@@ -52,35 +43,24 @@ __all__ = ("CompiledSQL", "OperationType", "SQLProcessor")
|
|
|
52
43
|
|
|
53
44
|
logger = get_logger("sqlspec.core.compiler")
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"DDL": "DDL",
|
|
66
|
-
"PRAGMA": "PRAGMA",
|
|
67
|
-
"UNKNOWN": "UNKNOWN",
|
|
46
|
+
OPERATION_TYPE_MAP: "dict[type[exp.Expression], OperationType]" = {
|
|
47
|
+
exp.Select: "SELECT",
|
|
48
|
+
exp.Insert: "INSERT",
|
|
49
|
+
exp.Update: "UPDATE",
|
|
50
|
+
exp.Delete: "DELETE",
|
|
51
|
+
exp.Pragma: "PRAGMA",
|
|
52
|
+
exp.Command: "EXECUTE",
|
|
53
|
+
exp.Create: "DDL",
|
|
54
|
+
exp.Drop: "DDL",
|
|
55
|
+
exp.Alter: "DDL",
|
|
68
56
|
}
|
|
69
57
|
|
|
70
58
|
|
|
71
|
-
@mypyc_attr(allow_interpreted_subclasses=
|
|
59
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
72
60
|
class CompiledSQL:
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
This class represents the result of SQL compilation, containing all
|
|
76
|
-
information needed for execution.
|
|
61
|
+
"""Compiled SQL result.
|
|
77
62
|
|
|
78
|
-
|
|
79
|
-
- Immutable design for safe sharing
|
|
80
|
-
- Cached hash for efficient dictionary operations
|
|
81
|
-
- Complete operation type detection
|
|
82
|
-
- Parameter style and execution information
|
|
83
|
-
- Support for execute_many operations
|
|
63
|
+
Contains the result of SQL compilation with information needed for execution.
|
|
84
64
|
"""
|
|
85
65
|
|
|
86
66
|
__slots__ = (
|
|
@@ -93,21 +73,23 @@ class CompiledSQL:
|
|
|
93
73
|
"supports_many",
|
|
94
74
|
)
|
|
95
75
|
|
|
76
|
+
operation_type: "OperationType"
|
|
77
|
+
|
|
96
78
|
def __init__(
|
|
97
79
|
self,
|
|
98
80
|
compiled_sql: str,
|
|
99
81
|
execution_parameters: Any,
|
|
100
|
-
operation_type:
|
|
82
|
+
operation_type: "OperationType",
|
|
101
83
|
expression: Optional["exp.Expression"] = None,
|
|
102
84
|
parameter_style: Optional[str] = None,
|
|
103
85
|
supports_many: bool = False,
|
|
104
86
|
) -> None:
|
|
105
|
-
"""Initialize
|
|
87
|
+
"""Initialize compiled result.
|
|
106
88
|
|
|
107
89
|
Args:
|
|
108
|
-
compiled_sql:
|
|
90
|
+
compiled_sql: SQL string ready for execution
|
|
109
91
|
execution_parameters: Parameters in driver-specific format
|
|
110
|
-
operation_type:
|
|
92
|
+
operation_type: SQL operation type (SELECT, INSERT, etc.)
|
|
111
93
|
expression: SQLGlot AST expression
|
|
112
94
|
parameter_style: Parameter style used in compilation
|
|
113
95
|
supports_many: Whether this supports execute_many operations
|
|
@@ -121,15 +103,14 @@ class CompiledSQL:
|
|
|
121
103
|
self._hash: Optional[int] = None
|
|
122
104
|
|
|
123
105
|
def __hash__(self) -> int:
|
|
124
|
-
"""Cached hash value
|
|
106
|
+
"""Cached hash value."""
|
|
125
107
|
if self._hash is None:
|
|
126
|
-
# Optimize by avoiding str() conversion if possible
|
|
127
108
|
param_str = str(self.execution_parameters)
|
|
128
109
|
self._hash = hash((self.compiled_sql, param_str, self.operation_type, self.parameter_style))
|
|
129
110
|
return self._hash
|
|
130
111
|
|
|
131
112
|
def __eq__(self, other: object) -> bool:
|
|
132
|
-
"""Equality comparison
|
|
113
|
+
"""Equality comparison."""
|
|
133
114
|
if not isinstance(other, CompiledSQL):
|
|
134
115
|
return False
|
|
135
116
|
return (
|
|
@@ -140,7 +121,7 @@ class CompiledSQL:
|
|
|
140
121
|
)
|
|
141
122
|
|
|
142
123
|
def __repr__(self) -> str:
|
|
143
|
-
"""String representation
|
|
124
|
+
"""String representation."""
|
|
144
125
|
return (
|
|
145
126
|
f"CompiledSQL(sql={self.compiled_sql!r}, "
|
|
146
127
|
f"params={self.execution_parameters!r}, "
|
|
@@ -148,35 +129,21 @@ class CompiledSQL:
|
|
|
148
129
|
)
|
|
149
130
|
|
|
150
131
|
|
|
151
|
-
@mypyc_attr(allow_interpreted_subclasses=
|
|
132
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
152
133
|
class SQLProcessor:
|
|
153
|
-
"""SQL processor with
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
integrated parameter processing and caching.
|
|
157
|
-
|
|
158
|
-
Processing Flow:
|
|
159
|
-
1. Parameter detection and normalization (if needed)
|
|
160
|
-
2. Single SQLGlot parse
|
|
161
|
-
3. AST-based operation type detection
|
|
162
|
-
4. Parameter conversion (if needed)
|
|
163
|
-
5. Final SQL generation with execution parameters
|
|
164
|
-
|
|
165
|
-
Features:
|
|
166
|
-
- LRU cache with O(1) operations
|
|
167
|
-
- Integrated parameter processing
|
|
168
|
-
- Cached compilation results
|
|
169
|
-
- Complete StatementConfig support
|
|
134
|
+
"""SQL processor with compilation and caching.
|
|
135
|
+
|
|
136
|
+
Processes SQL statements with parameter processing and caching.
|
|
170
137
|
"""
|
|
171
138
|
|
|
172
139
|
__slots__ = ("_cache", "_cache_hits", "_cache_misses", "_config", "_max_cache_size", "_parameter_processor")
|
|
173
140
|
|
|
174
141
|
def __init__(self, config: "StatementConfig", max_cache_size: int = 1000) -> None:
|
|
175
|
-
"""Initialize processor
|
|
142
|
+
"""Initialize processor.
|
|
176
143
|
|
|
177
144
|
Args:
|
|
178
|
-
config: Statement configuration
|
|
179
|
-
max_cache_size: Maximum number of
|
|
145
|
+
config: Statement configuration
|
|
146
|
+
max_cache_size: Maximum number of compilation results to cache
|
|
180
147
|
"""
|
|
181
148
|
self._config = config
|
|
182
149
|
self._cache: OrderedDict[str, CompiledSQL] = OrderedDict()
|
|
@@ -186,15 +153,15 @@ class SQLProcessor:
|
|
|
186
153
|
self._cache_misses = 0
|
|
187
154
|
|
|
188
155
|
def compile(self, sql: str, parameters: Any = None, is_many: bool = False) -> CompiledSQL:
|
|
189
|
-
"""Compile SQL statement
|
|
156
|
+
"""Compile SQL statement.
|
|
190
157
|
|
|
191
158
|
Args:
|
|
192
|
-
sql:
|
|
193
|
-
parameters: Parameter values
|
|
159
|
+
sql: SQL string for compilation
|
|
160
|
+
parameters: Parameter values
|
|
194
161
|
is_many: Whether this is for execute_many operation
|
|
195
162
|
|
|
196
163
|
Returns:
|
|
197
|
-
CompiledSQL with
|
|
164
|
+
CompiledSQL with execution information
|
|
198
165
|
"""
|
|
199
166
|
if not self._config.enable_caching:
|
|
200
167
|
return self._compile_uncached(sql, parameters, is_many)
|
|
@@ -202,7 +169,6 @@ class SQLProcessor:
|
|
|
202
169
|
cache_key = self._make_cache_key(sql, parameters)
|
|
203
170
|
|
|
204
171
|
if cache_key in self._cache:
|
|
205
|
-
# Move to end for LRU behavior
|
|
206
172
|
result = self._cache[cache_key]
|
|
207
173
|
del self._cache[cache_key]
|
|
208
174
|
self._cache[cache_key] = result
|
|
@@ -222,7 +188,7 @@ class SQLProcessor:
|
|
|
222
188
|
"""Compile SQL without caching.
|
|
223
189
|
|
|
224
190
|
Args:
|
|
225
|
-
sql:
|
|
191
|
+
sql: SQL string
|
|
226
192
|
parameters: Parameter values
|
|
227
193
|
is_many: Whether this is for execute_many operation
|
|
228
194
|
|
|
@@ -230,10 +196,8 @@ class SQLProcessor:
|
|
|
230
196
|
CompiledSQL result
|
|
231
197
|
"""
|
|
232
198
|
try:
|
|
233
|
-
# Cache dialect string to avoid repeated conversions
|
|
234
199
|
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
235
200
|
|
|
236
|
-
# Process parameters in single call
|
|
237
201
|
processed_sql: str
|
|
238
202
|
processed_params: Any
|
|
239
203
|
processed_sql, processed_params = self._parameter_processor.process(
|
|
@@ -244,7 +208,6 @@ class SQLProcessor:
|
|
|
244
208
|
is_many=is_many,
|
|
245
209
|
)
|
|
246
210
|
|
|
247
|
-
# Optimize static compilation path
|
|
248
211
|
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
249
212
|
sqlglot_sql = processed_sql
|
|
250
213
|
else:
|
|
@@ -255,15 +218,13 @@ class SQLProcessor:
|
|
|
255
218
|
final_parameters = processed_params
|
|
256
219
|
ast_was_transformed = False
|
|
257
220
|
expression = None
|
|
258
|
-
operation_type = "EXECUTE"
|
|
221
|
+
operation_type: OperationType = "EXECUTE"
|
|
259
222
|
|
|
260
223
|
if self._config.enable_parsing:
|
|
261
224
|
try:
|
|
262
|
-
# Use copy=False for performance optimization
|
|
263
225
|
expression = sqlglot.parse_one(sqlglot_sql, dialect=dialect_str)
|
|
264
226
|
operation_type = self._detect_operation_type(expression)
|
|
265
227
|
|
|
266
|
-
# Handle AST transformation if configured
|
|
267
228
|
ast_transformer = self._config.parameter_config.ast_transformer
|
|
268
229
|
if ast_transformer:
|
|
269
230
|
expression, final_parameters = ast_transformer(expression, processed_params)
|
|
@@ -273,7 +234,6 @@ class SQLProcessor:
|
|
|
273
234
|
expression = None
|
|
274
235
|
operation_type = "EXECUTE"
|
|
275
236
|
|
|
276
|
-
# Optimize final SQL generation path
|
|
277
237
|
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
278
238
|
final_sql, final_params = processed_sql, processed_params
|
|
279
239
|
elif ast_was_transformed and expression is not None:
|
|
@@ -303,12 +263,10 @@ class SQLProcessor:
|
|
|
303
263
|
|
|
304
264
|
except Exception as e:
|
|
305
265
|
logger.warning("Compilation failed, using fallback: %s", e)
|
|
306
|
-
return CompiledSQL(
|
|
307
|
-
compiled_sql=sql, execution_parameters=parameters, operation_type=_OPERATION_TYPES["UNKNOWN"]
|
|
308
|
-
)
|
|
266
|
+
return CompiledSQL(compiled_sql=sql, execution_parameters=parameters, operation_type="UNKNOWN")
|
|
309
267
|
|
|
310
268
|
def _make_cache_key(self, sql: str, parameters: Any) -> str:
|
|
311
|
-
"""Generate cache key
|
|
269
|
+
"""Generate cache key.
|
|
312
270
|
|
|
313
271
|
Args:
|
|
314
272
|
sql: SQL string
|
|
@@ -317,12 +275,11 @@ class SQLProcessor:
|
|
|
317
275
|
Returns:
|
|
318
276
|
Cache key string
|
|
319
277
|
"""
|
|
320
|
-
|
|
278
|
+
|
|
321
279
|
param_repr = repr(parameters)
|
|
322
280
|
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
323
281
|
param_style = self._config.parameter_config.default_parameter_style.value
|
|
324
282
|
|
|
325
|
-
# Use direct tuple construction for better performance
|
|
326
283
|
hash_data = (
|
|
327
284
|
sql,
|
|
328
285
|
param_repr,
|
|
@@ -332,34 +289,23 @@ class SQLProcessor:
|
|
|
332
289
|
self._config.enable_transformations,
|
|
333
290
|
)
|
|
334
291
|
|
|
335
|
-
# Optimize hash computation
|
|
336
292
|
hash_str = hashlib.sha256(str(hash_data).encode("utf-8")).hexdigest()[:16]
|
|
337
293
|
return f"sql_{hash_str}"
|
|
338
294
|
|
|
339
|
-
def _detect_operation_type(self, expression: "exp.Expression") ->
|
|
295
|
+
def _detect_operation_type(self, expression: "exp.Expression") -> "OperationType":
|
|
340
296
|
"""Detect operation type from AST.
|
|
341
297
|
|
|
342
|
-
Uses SQLGlot AST structure to determine operation type.
|
|
343
|
-
|
|
344
298
|
Args:
|
|
345
|
-
expression:
|
|
299
|
+
expression: AST expression
|
|
346
300
|
|
|
347
301
|
Returns:
|
|
348
|
-
Operation type
|
|
302
|
+
Operation type literal
|
|
349
303
|
"""
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if isinstance(expression, exp.Update):
|
|
356
|
-
return "UPDATE"
|
|
357
|
-
if isinstance(expression, exp.Delete):
|
|
358
|
-
return "DELETE"
|
|
359
|
-
if isinstance(expression, exp.Pragma):
|
|
360
|
-
return "PRAGMA"
|
|
361
|
-
if isinstance(expression, exp.Command):
|
|
362
|
-
return "EXECUTE"
|
|
304
|
+
|
|
305
|
+
expr_type = type(expression)
|
|
306
|
+
if expr_type in OPERATION_TYPE_MAP:
|
|
307
|
+
return OPERATION_TYPE_MAP[expr_type] # pyright: ignore
|
|
308
|
+
|
|
363
309
|
if isinstance(expression, exp.Copy):
|
|
364
310
|
copy_kind = expression.args.get("kind")
|
|
365
311
|
if copy_kind is True:
|
|
@@ -367,8 +313,7 @@ class SQLProcessor:
|
|
|
367
313
|
if copy_kind is False:
|
|
368
314
|
return "COPY_TO"
|
|
369
315
|
return "COPY"
|
|
370
|
-
|
|
371
|
-
return "DDL"
|
|
316
|
+
|
|
372
317
|
return "UNKNOWN"
|
|
373
318
|
|
|
374
319
|
def _apply_final_transformations(
|
|
@@ -377,10 +322,10 @@ class SQLProcessor:
|
|
|
377
322
|
"""Apply final transformations.
|
|
378
323
|
|
|
379
324
|
Args:
|
|
380
|
-
expression: SQLGlot AST expression
|
|
381
|
-
sql:
|
|
325
|
+
expression: SQLGlot AST expression
|
|
326
|
+
sql: SQL string
|
|
382
327
|
parameters: Execution parameters
|
|
383
|
-
dialect_str: SQL dialect
|
|
328
|
+
dialect_str: SQL dialect
|
|
384
329
|
|
|
385
330
|
Returns:
|
|
386
331
|
Tuple of (final_sql, final_parameters)
|
|
@@ -395,7 +340,7 @@ class SQLProcessor:
|
|
|
395
340
|
return sql, parameters
|
|
396
341
|
|
|
397
342
|
def clear_cache(self) -> None:
|
|
398
|
-
"""Clear
|
|
343
|
+
"""Clear cache."""
|
|
399
344
|
self._cache.clear()
|
|
400
345
|
self._cache_hits = 0
|
|
401
346
|
self._cache_misses = 0
|
|
@@ -405,7 +350,7 @@ class SQLProcessor:
|
|
|
405
350
|
"""Get cache statistics.
|
|
406
351
|
|
|
407
352
|
Returns:
|
|
408
|
-
Dictionary with cache
|
|
353
|
+
Dictionary with cache statistics
|
|
409
354
|
"""
|
|
410
355
|
total_requests = self._cache_hits + self._cache_misses
|
|
411
356
|
hit_rate_pct = int((self._cache_hits / total_requests) * 100) if total_requests > 0 else 0
|
sqlspec/core/filters.py
CHANGED
|
@@ -25,6 +25,7 @@ from collections.abc import Sequence
|
|
|
25
25
|
from datetime import datetime
|
|
26
26
|
from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, Union
|
|
27
27
|
|
|
28
|
+
import sqlglot
|
|
28
29
|
from sqlglot import exp
|
|
29
30
|
from typing_extensions import TypeAlias, TypeVar
|
|
30
31
|
|
|
@@ -160,7 +161,6 @@ class BeforeAfterFilter(StatementFilter):
|
|
|
160
161
|
conditions: list[Condition] = []
|
|
161
162
|
col_expr = exp.column(self.field_name)
|
|
162
163
|
|
|
163
|
-
# Resolve parameter name conflicts
|
|
164
164
|
proposed_names = []
|
|
165
165
|
if self.before and self._param_name_before:
|
|
166
166
|
proposed_names.append(self._param_name_before)
|
|
@@ -238,7 +238,6 @@ class OnBeforeAfterFilter(StatementFilter):
|
|
|
238
238
|
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
239
239
|
conditions: list[Condition] = []
|
|
240
240
|
|
|
241
|
-
# Resolve parameter name conflicts
|
|
242
241
|
proposed_names = []
|
|
243
242
|
if self.on_or_before and self._param_name_on_or_before:
|
|
244
243
|
proposed_names.append(self._param_name_on_or_before)
|
|
@@ -328,7 +327,6 @@ class InCollectionFilter(InAnyFilter[T]):
|
|
|
328
327
|
if not self.values:
|
|
329
328
|
return statement.where(exp.false())
|
|
330
329
|
|
|
331
|
-
# Resolve parameter name conflicts
|
|
332
330
|
resolved_names = self._resolve_parameter_conflicts(statement, self._param_names)
|
|
333
331
|
|
|
334
332
|
placeholder_expressions: list[exp.Placeholder] = [
|
|
@@ -337,7 +335,6 @@ class InCollectionFilter(InAnyFilter[T]):
|
|
|
337
335
|
|
|
338
336
|
result = statement.where(exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
|
|
339
337
|
|
|
340
|
-
# Add parameters with resolved names
|
|
341
338
|
for resolved_name, value in zip(resolved_names, self.values):
|
|
342
339
|
result = result.add_named_parameter(resolved_name, value)
|
|
343
340
|
return result
|
|
@@ -383,7 +380,6 @@ class NotInCollectionFilter(InAnyFilter[T]):
|
|
|
383
380
|
if self.values is None or not self.values:
|
|
384
381
|
return statement
|
|
385
382
|
|
|
386
|
-
# Resolve parameter name conflicts
|
|
387
383
|
resolved_names = self._resolve_parameter_conflicts(statement, self._param_names)
|
|
388
384
|
|
|
389
385
|
placeholder_expressions: list[exp.Placeholder] = [
|
|
@@ -394,7 +390,6 @@ class NotInCollectionFilter(InAnyFilter[T]):
|
|
|
394
390
|
exp.Not(this=exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
|
|
395
391
|
)
|
|
396
392
|
|
|
397
|
-
# Add parameters with resolved names
|
|
398
393
|
for resolved_name, value in zip(resolved_names, self.values):
|
|
399
394
|
result = result.add_named_parameter(resolved_name, value)
|
|
400
395
|
return result
|
|
@@ -445,7 +440,6 @@ class AnyCollectionFilter(InAnyFilter[T]):
|
|
|
445
440
|
if not self.values:
|
|
446
441
|
return statement.where(exp.false())
|
|
447
442
|
|
|
448
|
-
# Resolve parameter name conflicts
|
|
449
443
|
resolved_names = self._resolve_parameter_conflicts(statement, self._param_names)
|
|
450
444
|
|
|
451
445
|
placeholder_expressions: list[exp.Expression] = [
|
|
@@ -455,7 +449,6 @@ class AnyCollectionFilter(InAnyFilter[T]):
|
|
|
455
449
|
array_expr = exp.Array(expressions=placeholder_expressions)
|
|
456
450
|
result = statement.where(exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr)))
|
|
457
451
|
|
|
458
|
-
# Add parameters with resolved names
|
|
459
452
|
for resolved_name, value in zip(resolved_names, self.values):
|
|
460
453
|
result = result.add_named_parameter(resolved_name, value)
|
|
461
454
|
return result
|
|
@@ -500,7 +493,6 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
|
|
|
500
493
|
if self.values is None or not self.values:
|
|
501
494
|
return statement
|
|
502
495
|
|
|
503
|
-
# Resolve parameter name conflicts
|
|
504
496
|
resolved_names = self._resolve_parameter_conflicts(statement, self._param_names)
|
|
505
497
|
|
|
506
498
|
placeholder_expressions: list[exp.Expression] = [
|
|
@@ -511,7 +503,6 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
|
|
|
511
503
|
condition = exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr))
|
|
512
504
|
result = statement.where(exp.Not(this=condition))
|
|
513
505
|
|
|
514
|
-
# Add parameters with resolved names
|
|
515
506
|
for resolved_name, value in zip(resolved_names, self.values):
|
|
516
507
|
result = result.add_named_parameter(resolved_name, value)
|
|
517
508
|
return result
|
|
@@ -558,27 +549,20 @@ class LimitOffsetFilter(PaginationFilter):
|
|
|
558
549
|
return [], {self._limit_param_name: self.limit, self._offset_param_name: self.offset}
|
|
559
550
|
|
|
560
551
|
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
561
|
-
import sqlglot
|
|
562
|
-
from sqlglot import exp
|
|
563
|
-
|
|
564
|
-
# Resolve parameter name conflicts
|
|
565
552
|
resolved_names = self._resolve_parameter_conflicts(statement, [self._limit_param_name, self._offset_param_name])
|
|
566
553
|
limit_param_name, offset_param_name = resolved_names
|
|
567
554
|
|
|
568
555
|
limit_placeholder = exp.Placeholder(this=limit_param_name)
|
|
569
556
|
offset_placeholder = exp.Placeholder(this=offset_param_name)
|
|
570
557
|
|
|
571
|
-
# Parse the current SQL to get the statement structure
|
|
572
558
|
try:
|
|
573
559
|
current_statement = sqlglot.parse_one(statement._raw_sql, dialect=getattr(statement, "_dialect", None))
|
|
574
560
|
except Exception:
|
|
575
|
-
# Fallback to wrapping in subquery if parsing fails
|
|
576
561
|
current_statement = exp.Select().from_(f"({statement._raw_sql})")
|
|
577
562
|
|
|
578
563
|
if isinstance(current_statement, exp.Select):
|
|
579
564
|
new_statement = current_statement.limit(limit_placeholder).offset(offset_placeholder)
|
|
580
565
|
else:
|
|
581
|
-
# Wrap non-SELECT statements in a subquery
|
|
582
566
|
new_statement = exp.Select().from_(current_statement).limit(limit_placeholder).offset(offset_placeholder)
|
|
583
567
|
|
|
584
568
|
result = statement.copy(statement=new_statement)
|
|
@@ -678,7 +662,6 @@ class SearchFilter(StatementFilter):
|
|
|
678
662
|
if not self.value or not self._param_name:
|
|
679
663
|
return statement
|
|
680
664
|
|
|
681
|
-
# Resolve parameter name conflicts
|
|
682
665
|
resolved_names = self._resolve_parameter_conflicts(statement, [self._param_name])
|
|
683
666
|
param_name = resolved_names[0]
|
|
684
667
|
|
|
@@ -701,7 +684,6 @@ class SearchFilter(StatementFilter):
|
|
|
701
684
|
else:
|
|
702
685
|
result = statement
|
|
703
686
|
|
|
704
|
-
# Add parameter with resolved name
|
|
705
687
|
search_value_with_wildcards = f"%{self.value}%"
|
|
706
688
|
return result.add_named_parameter(param_name, search_value_with_wildcards)
|
|
707
689
|
|
|
@@ -745,7 +727,6 @@ class NotInSearchFilter(SearchFilter):
|
|
|
745
727
|
if not self.value or not self._param_name:
|
|
746
728
|
return statement
|
|
747
729
|
|
|
748
|
-
# Resolve parameter name conflicts
|
|
749
730
|
resolved_names = self._resolve_parameter_conflicts(statement, [self._param_name])
|
|
750
731
|
param_name = resolved_names[0]
|
|
751
732
|
|
|
@@ -768,7 +749,6 @@ class NotInSearchFilter(SearchFilter):
|
|
|
768
749
|
final_condition = exp.And(this=final_condition, expression=cond)
|
|
769
750
|
result = statement.where(final_condition)
|
|
770
751
|
|
|
771
|
-
# Add parameter with resolved name
|
|
772
752
|
search_value_with_wildcards = f"%{self.value}%"
|
|
773
753
|
return result.add_named_parameter(param_name, search_value_with_wildcards)
|
|
774
754
|
|