sqlspec 0.17.1__py3-none-any.whl → 0.19.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 +54 -159
- 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 +19 -22
- sqlspec/builder/__init__.py +1 -1
- sqlspec/builder/_base.py +34 -20
- sqlspec/builder/_ddl.py +407 -183
- sqlspec/builder/_insert.py +1 -1
- sqlspec/builder/mixins/_insert_operations.py +26 -6
- sqlspec/builder/mixins/_merge_operations.py +1 -1
- sqlspec/builder/mixins/_select_operations.py +1 -5
- sqlspec/cli.py +281 -33
- sqlspec/config.py +183 -14
- 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/extensions/litestar/plugin.py +15 -8
- 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 +3 -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.1.dist-info → sqlspec-0.19.0.dist-info}/METADATA +1 -1
- sqlspec-0.19.0.dist-info/RECORD +138 -0
- sqlspec/builder/_ddl_utils.py +0 -103
- sqlspec-0.17.1.dist-info/RECORD +0 -138
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/core/statement.py
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
"""SQL statement
|
|
1
|
+
"""SQL statement and configuration management."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
backward compatibility while using an optimized processing pipeline.
|
|
5
|
-
|
|
6
|
-
Components:
|
|
7
|
-
- SQL class: SQL statement with identical external interface
|
|
8
|
-
- StatementConfig: Complete backward compatibility for all driver requirements
|
|
9
|
-
- ProcessedState: Cached processing results
|
|
10
|
-
|
|
11
|
-
Features:
|
|
12
|
-
- Lazy compilation: Only compile when needed
|
|
13
|
-
- Cached properties: Avoid redundant computation
|
|
14
|
-
- Complete StatementConfig compatibility
|
|
15
|
-
- Integrated parameter processing and compilation caching
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import contextlib
|
|
19
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Final, Optional, Union
|
|
20
4
|
|
|
21
5
|
import sqlglot
|
|
22
6
|
from mypy_extensions import mypyc_attr
|
|
@@ -24,7 +8,7 @@ from sqlglot import exp
|
|
|
24
8
|
from sqlglot.errors import ParseError
|
|
25
9
|
from typing_extensions import TypeAlias
|
|
26
10
|
|
|
27
|
-
from sqlspec.core.compiler import SQLProcessor
|
|
11
|
+
from sqlspec.core.compiler import OperationType, SQLProcessor
|
|
28
12
|
from sqlspec.core.parameters import ParameterConverter, ParameterStyle, ParameterStyleConfig, ParameterValidator
|
|
29
13
|
from sqlspec.typing import Empty, EmptyEnum
|
|
30
14
|
from sqlspec.utils.logging import get_logger
|
|
@@ -46,7 +30,10 @@ __all__ = (
|
|
|
46
30
|
)
|
|
47
31
|
logger = get_logger("sqlspec.core.statement")
|
|
48
32
|
|
|
49
|
-
|
|
33
|
+
RETURNS_ROWS_OPERATIONS: Final = {"SELECT", "WITH", "VALUES", "TABLE", "SHOW", "DESCRIBE", "PRAGMA"}
|
|
34
|
+
MODIFYING_OPERATIONS: Final = {"INSERT", "UPDATE", "DELETE", "MERGE", "UPSERT"}
|
|
35
|
+
|
|
36
|
+
SQL_CONFIG_SLOTS: Final = (
|
|
50
37
|
"pre_process_steps",
|
|
51
38
|
"post_process_steps",
|
|
52
39
|
"dialect",
|
|
@@ -65,7 +52,7 @@ SQL_CONFIG_SLOTS = (
|
|
|
65
52
|
"parameter_validator",
|
|
66
53
|
)
|
|
67
54
|
|
|
68
|
-
PROCESSED_STATE_SLOTS = (
|
|
55
|
+
PROCESSED_STATE_SLOTS: Final = (
|
|
69
56
|
"compiled_sql",
|
|
70
57
|
"execution_parameters",
|
|
71
58
|
"parsed_expression",
|
|
@@ -77,20 +64,17 @@ PROCESSED_STATE_SLOTS = (
|
|
|
77
64
|
|
|
78
65
|
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
79
66
|
class ProcessedState:
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
Stores the results of processing to avoid redundant compilation,
|
|
83
|
-
parsing, and parameter processing.
|
|
84
|
-
"""
|
|
67
|
+
"""Processing results for SQL statements."""
|
|
85
68
|
|
|
86
69
|
__slots__ = PROCESSED_STATE_SLOTS
|
|
70
|
+
operation_type: "OperationType"
|
|
87
71
|
|
|
88
72
|
def __init__(
|
|
89
73
|
self,
|
|
90
74
|
compiled_sql: str,
|
|
91
75
|
execution_parameters: Any,
|
|
92
76
|
parsed_expression: "Optional[exp.Expression]" = None,
|
|
93
|
-
operation_type:
|
|
77
|
+
operation_type: "OperationType" = "UNKNOWN",
|
|
94
78
|
validation_errors: "Optional[list[str]]" = None,
|
|
95
79
|
is_many: bool = False,
|
|
96
80
|
) -> None:
|
|
@@ -105,20 +89,9 @@ class ProcessedState:
|
|
|
105
89
|
return hash((self.compiled_sql, str(self.execution_parameters), self.operation_type))
|
|
106
90
|
|
|
107
91
|
|
|
108
|
-
@mypyc_attr(allow_interpreted_subclasses=
|
|
92
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
109
93
|
class SQL:
|
|
110
|
-
"""SQL statement with
|
|
111
|
-
|
|
112
|
-
Provides 100% backward compatibility while using an optimized
|
|
113
|
-
core processing pipeline.
|
|
114
|
-
|
|
115
|
-
Features:
|
|
116
|
-
- Lazy evaluation with cached properties
|
|
117
|
-
- Integrated parameter processing pipeline
|
|
118
|
-
- Complete StatementFilter and execution mode support
|
|
119
|
-
- Same parameter processing behavior
|
|
120
|
-
- Same result types and interfaces
|
|
121
|
-
"""
|
|
94
|
+
"""SQL statement with parameter and filter support."""
|
|
122
95
|
|
|
123
96
|
__slots__ = (
|
|
124
97
|
"_dialect",
|
|
@@ -151,9 +124,9 @@ class SQL:
|
|
|
151
124
|
is_many: Mark as execute_many operation
|
|
152
125
|
**kwargs: Additional parameters
|
|
153
126
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
self._dialect = self._normalize_dialect(
|
|
127
|
+
config = statement_config or self._create_auto_config(statement, parameters, kwargs)
|
|
128
|
+
self._statement_config = config
|
|
129
|
+
self._dialect = self._normalize_dialect(config.dialect)
|
|
157
130
|
self._processed_state: Union[EmptyEnum, ProcessedState] = Empty
|
|
158
131
|
self._hash: Optional[int] = None
|
|
159
132
|
self._filters: list[StatementFilter] = []
|
|
@@ -169,7 +142,8 @@ class SQL:
|
|
|
169
142
|
if isinstance(statement, str):
|
|
170
143
|
self._raw_sql = statement
|
|
171
144
|
else:
|
|
172
|
-
|
|
145
|
+
dialect = self._dialect
|
|
146
|
+
self._raw_sql = statement.sql(dialect=str(dialect) if dialect else None)
|
|
173
147
|
|
|
174
148
|
self._is_many = is_many if is_many is not None else self._should_auto_detect_many(parameters)
|
|
175
149
|
|
|
@@ -188,10 +162,7 @@ class SQL:
|
|
|
188
162
|
return None
|
|
189
163
|
if isinstance(dialect, str):
|
|
190
164
|
return dialect
|
|
191
|
-
|
|
192
|
-
return dialect.__class__.__name__.lower()
|
|
193
|
-
except AttributeError:
|
|
194
|
-
return str(dialect)
|
|
165
|
+
return dialect.__class__.__name__.lower()
|
|
195
166
|
|
|
196
167
|
def _init_from_sql_object(self, sql_obj: "SQL") -> None:
|
|
197
168
|
"""Initialize from existing SQL object."""
|
|
@@ -208,19 +179,18 @@ class SQL:
|
|
|
208
179
|
"""Auto-detect execute_many from parameter structure."""
|
|
209
180
|
if len(parameters) == 1 and isinstance(parameters[0], list):
|
|
210
181
|
param_list = parameters[0]
|
|
211
|
-
if
|
|
212
|
-
return
|
|
182
|
+
if param_list and all(isinstance(item, (tuple, list)) for item in param_list):
|
|
183
|
+
return len(param_list) > 1
|
|
213
184
|
return False
|
|
214
185
|
|
|
215
186
|
def _process_parameters(self, *parameters: Any, dialect: Optional[str] = None, **kwargs: Any) -> None:
|
|
216
|
-
"""Process parameters
|
|
187
|
+
"""Process parameters and filters."""
|
|
217
188
|
if dialect is not None:
|
|
218
189
|
self._dialect = self._normalize_dialect(dialect)
|
|
219
190
|
|
|
220
191
|
if "is_script" in kwargs:
|
|
221
192
|
self._is_script = bool(kwargs.pop("is_script"))
|
|
222
193
|
|
|
223
|
-
# Optimize parameter filtering with direct iteration
|
|
224
194
|
filters: list[StatementFilter] = []
|
|
225
195
|
actual_params: list[Any] = []
|
|
226
196
|
for p in parameters:
|
|
@@ -249,36 +219,33 @@ class SQL:
|
|
|
249
219
|
|
|
250
220
|
self._named_parameters.update(kwargs)
|
|
251
221
|
|
|
252
|
-
# PRESERVED PROPERTIES - Exact same interface as existing SQL class
|
|
253
222
|
@property
|
|
254
223
|
def sql(self) -> str:
|
|
255
|
-
"""Get the raw SQL string
|
|
224
|
+
"""Get the raw SQL string."""
|
|
256
225
|
return self._raw_sql
|
|
257
226
|
|
|
258
227
|
@property
|
|
259
228
|
def parameters(self) -> Any:
|
|
260
|
-
"""Get the original parameters
|
|
229
|
+
"""Get the original parameters."""
|
|
261
230
|
if self._named_parameters:
|
|
262
231
|
return self._named_parameters
|
|
263
232
|
return self._positional_parameters or []
|
|
264
233
|
|
|
265
234
|
@property
|
|
266
|
-
def operation_type(self) ->
|
|
267
|
-
"""SQL operation type
|
|
235
|
+
def operation_type(self) -> "OperationType":
|
|
236
|
+
"""SQL operation type."""
|
|
268
237
|
if self._processed_state is Empty:
|
|
269
238
|
return "UNKNOWN"
|
|
270
239
|
return self._processed_state.operation_type
|
|
271
240
|
|
|
272
241
|
@property
|
|
273
242
|
def statement_config(self) -> "StatementConfig":
|
|
274
|
-
"""Statement configuration
|
|
243
|
+
"""Statement configuration."""
|
|
275
244
|
return self._statement_config
|
|
276
245
|
|
|
277
246
|
@property
|
|
278
247
|
def expression(self) -> "Optional[exp.Expression]":
|
|
279
|
-
"""SQLGlot expression
|
|
280
|
-
# This property should only be accessed after compilation
|
|
281
|
-
# If not compiled yet, return None
|
|
248
|
+
"""SQLGlot expression."""
|
|
282
249
|
if self._processed_state is not Empty:
|
|
283
250
|
return self._processed_state.parsed_expression
|
|
284
251
|
return None
|
|
@@ -310,7 +277,7 @@ class SQL:
|
|
|
310
277
|
|
|
311
278
|
@property
|
|
312
279
|
def validation_errors(self) -> "list[str]":
|
|
313
|
-
"""Validation errors
|
|
280
|
+
"""Validation errors."""
|
|
314
281
|
if self._processed_state is Empty:
|
|
315
282
|
return []
|
|
316
283
|
return self._processed_state.validation_errors.copy()
|
|
@@ -322,11 +289,21 @@ class SQL:
|
|
|
322
289
|
|
|
323
290
|
def returns_rows(self) -> bool:
|
|
324
291
|
"""Check if statement returns rows."""
|
|
325
|
-
|
|
326
|
-
|
|
292
|
+
if self._processed_state is Empty:
|
|
293
|
+
self.compile()
|
|
294
|
+
if self._processed_state is Empty:
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
op_type = self._processed_state.operation_type
|
|
298
|
+
if op_type in RETURNS_ROWS_OPERATIONS:
|
|
327
299
|
return True
|
|
328
300
|
|
|
329
|
-
|
|
301
|
+
if self._processed_state.parsed_expression:
|
|
302
|
+
expr = self._processed_state.parsed_expression
|
|
303
|
+
if isinstance(expr, (exp.Insert, exp.Update, exp.Delete)) and expr.args.get("returning"):
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
return False
|
|
330
307
|
|
|
331
308
|
def is_modifying_operation(self) -> bool:
|
|
332
309
|
"""Check if the SQL statement is a modifying operation.
|
|
@@ -334,23 +311,27 @@ class SQL:
|
|
|
334
311
|
Returns:
|
|
335
312
|
True if the operation modifies data (INSERT/UPDATE/DELETE)
|
|
336
313
|
"""
|
|
337
|
-
|
|
338
|
-
|
|
314
|
+
if self._processed_state is Empty:
|
|
315
|
+
return False
|
|
316
|
+
|
|
317
|
+
op_type = self._processed_state.operation_type
|
|
318
|
+
if op_type in MODIFYING_OPERATIONS:
|
|
339
319
|
return True
|
|
340
320
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
321
|
+
if self._processed_state.parsed_expression:
|
|
322
|
+
return isinstance(self._processed_state.parsed_expression, (exp.Insert, exp.Update, exp.Delete, exp.Merge))
|
|
323
|
+
|
|
324
|
+
return False
|
|
344
325
|
|
|
345
326
|
def compile(self) -> tuple[str, Any]:
|
|
346
|
-
"""
|
|
327
|
+
"""Compile the SQL statement."""
|
|
347
328
|
if self._processed_state is Empty:
|
|
348
329
|
try:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
)
|
|
330
|
+
config = self._statement_config
|
|
331
|
+
raw_sql = self._raw_sql
|
|
332
|
+
params = self._named_parameters or self._positional_parameters
|
|
333
|
+
is_many = self._is_many
|
|
334
|
+
compiled_result = SQLProcessor(config).compile(raw_sql, params, is_many=is_many)
|
|
354
335
|
|
|
355
336
|
self._processed_state = ProcessedState(
|
|
356
337
|
compiled_sql=compiled_result.compiled_sql,
|
|
@@ -373,10 +354,10 @@ class SQL:
|
|
|
373
354
|
|
|
374
355
|
def as_script(self) -> "SQL":
|
|
375
356
|
"""Mark as script execution."""
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
357
|
+
original_params = self._original_parameters
|
|
358
|
+
config = self._statement_config
|
|
359
|
+
is_many = self._is_many
|
|
360
|
+
new_sql = SQL(self._raw_sql, *original_params, statement_config=config, is_many=is_many)
|
|
380
361
|
new_sql._named_parameters.update(self._named_parameters)
|
|
381
362
|
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
382
363
|
new_sql._filters = self._filters.copy()
|
|
@@ -394,7 +375,6 @@ class SQL:
|
|
|
394
375
|
is_many=self._is_many,
|
|
395
376
|
**kwargs,
|
|
396
377
|
)
|
|
397
|
-
# Only preserve accumulated parameters when no explicit parameters are provided
|
|
398
378
|
if parameters is None:
|
|
399
379
|
new_sql._named_parameters.update(self._named_parameters)
|
|
400
380
|
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
@@ -411,9 +391,10 @@ class SQL:
|
|
|
411
391
|
Returns:
|
|
412
392
|
New SQL instance with the added parameter
|
|
413
393
|
"""
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
394
|
+
original_params = self._original_parameters
|
|
395
|
+
config = self._statement_config
|
|
396
|
+
is_many = self._is_many
|
|
397
|
+
new_sql = SQL(self._raw_sql, *original_params, statement_config=config, is_many=is_many)
|
|
417
398
|
new_sql._named_parameters.update(self._named_parameters)
|
|
418
399
|
new_sql._named_parameters[name] = value
|
|
419
400
|
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
@@ -429,20 +410,12 @@ class SQL:
|
|
|
429
410
|
Returns:
|
|
430
411
|
New SQL instance with the WHERE condition applied
|
|
431
412
|
"""
|
|
432
|
-
|
|
433
|
-
current_expr = None
|
|
434
|
-
with contextlib.suppress(ParseError):
|
|
413
|
+
try:
|
|
435
414
|
current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
|
|
415
|
+
except ParseError:
|
|
416
|
+
subquery_sql = f"SELECT * FROM ({self._raw_sql}) AS subquery"
|
|
417
|
+
current_expr = sqlglot.parse_one(subquery_sql, dialect=self._dialect)
|
|
436
418
|
|
|
437
|
-
if current_expr is None:
|
|
438
|
-
try:
|
|
439
|
-
current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
|
|
440
|
-
except ParseError:
|
|
441
|
-
# Use f-string optimization and copy=False
|
|
442
|
-
subquery_sql = f"SELECT * FROM ({self._raw_sql}) AS subquery"
|
|
443
|
-
current_expr = sqlglot.parse_one(subquery_sql, dialect=self._dialect)
|
|
444
|
-
|
|
445
|
-
# Parse condition with copy=False optimization
|
|
446
419
|
condition_expr: exp.Expression
|
|
447
420
|
if isinstance(condition, str):
|
|
448
421
|
try:
|
|
@@ -452,32 +425,30 @@ class SQL:
|
|
|
452
425
|
else:
|
|
453
426
|
condition_expr = condition
|
|
454
427
|
|
|
455
|
-
# Apply WHERE clause
|
|
456
428
|
if isinstance(current_expr, exp.Select) or supports_where(current_expr):
|
|
457
429
|
new_expr = current_expr.where(condition_expr, copy=False)
|
|
458
430
|
else:
|
|
459
431
|
new_expr = exp.Select().from_(current_expr).where(condition_expr, copy=False)
|
|
460
432
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
)
|
|
433
|
+
original_params = self._original_parameters
|
|
434
|
+
config = self._statement_config
|
|
435
|
+
is_many = self._is_many
|
|
436
|
+
new_sql = SQL(new_expr, *original_params, statement_config=config, is_many=is_many)
|
|
466
437
|
|
|
467
|
-
# Preserve state efficiently
|
|
468
438
|
new_sql._named_parameters.update(self._named_parameters)
|
|
469
439
|
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
470
440
|
new_sql._filters = self._filters.copy()
|
|
471
441
|
return new_sql
|
|
472
442
|
|
|
473
443
|
def __hash__(self) -> int:
|
|
474
|
-
"""Hash value
|
|
444
|
+
"""Hash value computation."""
|
|
475
445
|
if self._hash is None:
|
|
476
|
-
# Pre-compute tuple components to avoid multiple tuple() calls
|
|
477
446
|
positional_tuple = tuple(self._positional_parameters)
|
|
478
447
|
named_tuple = tuple(sorted(self._named_parameters.items())) if self._named_parameters else ()
|
|
479
|
-
|
|
480
|
-
|
|
448
|
+
raw_sql = self._raw_sql
|
|
449
|
+
is_many = self._is_many
|
|
450
|
+
is_script = self._is_script
|
|
451
|
+
self._hash = hash((raw_sql, positional_tuple, named_tuple, is_many, is_script))
|
|
481
452
|
return self._hash
|
|
482
453
|
|
|
483
454
|
def __eq__(self, other: object) -> bool:
|
|
@@ -494,11 +465,12 @@ class SQL:
|
|
|
494
465
|
|
|
495
466
|
def __repr__(self) -> str:
|
|
496
467
|
"""String representation."""
|
|
497
|
-
|
|
468
|
+
params_parts = []
|
|
469
|
+
if self._positional_parameters:
|
|
470
|
+
params_parts.append(f"params={self._positional_parameters}")
|
|
498
471
|
if self._named_parameters:
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
params_str = f", params={self._positional_parameters}"
|
|
472
|
+
params_parts.append(f"named_params={self._named_parameters}")
|
|
473
|
+
params_str = f", {', '.join(params_parts)}" if params_parts else ""
|
|
502
474
|
|
|
503
475
|
flags = []
|
|
504
476
|
if self._is_many:
|
|
@@ -510,18 +482,9 @@ class SQL:
|
|
|
510
482
|
return f"SQL({self._raw_sql!r}{params_str}{flags_str})"
|
|
511
483
|
|
|
512
484
|
|
|
513
|
-
@mypyc_attr(allow_interpreted_subclasses=
|
|
485
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
514
486
|
class StatementConfig:
|
|
515
|
-
"""Configuration for SQL statement processing.
|
|
516
|
-
|
|
517
|
-
Provides all attributes that drivers expect for SQL processing.
|
|
518
|
-
|
|
519
|
-
Features:
|
|
520
|
-
- Complete parameter processing configuration
|
|
521
|
-
- Caching and execution mode interfaces
|
|
522
|
-
- Support for various database-specific operations
|
|
523
|
-
- Immutable updates via replace() method
|
|
524
|
-
"""
|
|
487
|
+
"""Configuration for SQL statement processing."""
|
|
525
488
|
|
|
526
489
|
__slots__ = SQL_CONFIG_SLOTS
|
|
527
490
|
|
|
@@ -548,16 +511,16 @@ class StatementConfig:
|
|
|
548
511
|
|
|
549
512
|
Args:
|
|
550
513
|
parameter_config: Parameter style configuration
|
|
551
|
-
enable_parsing: Enable SQL parsing
|
|
552
|
-
enable_validation: Run SQL validators
|
|
514
|
+
enable_parsing: Enable SQL parsing
|
|
515
|
+
enable_validation: Run SQL validators
|
|
553
516
|
enable_transformations: Apply SQL transformers
|
|
554
|
-
enable_analysis: Run SQL analyzers
|
|
517
|
+
enable_analysis: Run SQL analyzers
|
|
555
518
|
enable_expression_simplification: Apply expression simplification
|
|
556
519
|
enable_parameter_type_wrapping: Wrap parameters with type information
|
|
557
520
|
enable_caching: Cache processed SQL statements
|
|
558
521
|
parameter_converter: Handles parameter style conversions
|
|
559
522
|
parameter_validator: Validates parameter usage and styles
|
|
560
|
-
dialect: SQL dialect
|
|
523
|
+
dialect: SQL dialect
|
|
561
524
|
pre_process_steps: Optional list of preprocessing steps
|
|
562
525
|
post_process_steps: Optional list of postprocessing steps
|
|
563
526
|
execution_mode: Special execution mode
|
|
@@ -598,12 +561,29 @@ class StatementConfig:
|
|
|
598
561
|
msg = f"{key!r} is not a field in {type(self).__name__}"
|
|
599
562
|
raise TypeError(msg)
|
|
600
563
|
|
|
601
|
-
current_kwargs
|
|
564
|
+
current_kwargs: dict[str, Any] = {
|
|
565
|
+
"parameter_config": self.parameter_config,
|
|
566
|
+
"enable_parsing": self.enable_parsing,
|
|
567
|
+
"enable_validation": self.enable_validation,
|
|
568
|
+
"enable_transformations": self.enable_transformations,
|
|
569
|
+
"enable_analysis": self.enable_analysis,
|
|
570
|
+
"enable_expression_simplification": self.enable_expression_simplification,
|
|
571
|
+
"enable_parameter_type_wrapping": self.enable_parameter_type_wrapping,
|
|
572
|
+
"enable_caching": self.enable_caching,
|
|
573
|
+
"parameter_converter": self.parameter_converter,
|
|
574
|
+
"parameter_validator": self.parameter_validator,
|
|
575
|
+
"dialect": self.dialect,
|
|
576
|
+
"pre_process_steps": self.pre_process_steps,
|
|
577
|
+
"post_process_steps": self.post_process_steps,
|
|
578
|
+
"execution_mode": self.execution_mode,
|
|
579
|
+
"execution_args": self.execution_args,
|
|
580
|
+
"output_transformer": self.output_transformer,
|
|
581
|
+
}
|
|
602
582
|
current_kwargs.update(kwargs)
|
|
603
583
|
return type(self)(**current_kwargs)
|
|
604
584
|
|
|
605
585
|
def __hash__(self) -> int:
|
|
606
|
-
"""Hash based on
|
|
586
|
+
"""Hash based on configuration settings."""
|
|
607
587
|
return hash(
|
|
608
588
|
(
|
|
609
589
|
self.enable_parsing,
|
|
@@ -619,10 +599,24 @@ class StatementConfig:
|
|
|
619
599
|
|
|
620
600
|
def __repr__(self) -> str:
|
|
621
601
|
"""String representation of the StatementConfig instance."""
|
|
622
|
-
field_strs = [
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
602
|
+
field_strs = [
|
|
603
|
+
f"parameter_config={self.parameter_config!r}",
|
|
604
|
+
f"enable_parsing={self.enable_parsing!r}",
|
|
605
|
+
f"enable_validation={self.enable_validation!r}",
|
|
606
|
+
f"enable_transformations={self.enable_transformations!r}",
|
|
607
|
+
f"enable_analysis={self.enable_analysis!r}",
|
|
608
|
+
f"enable_expression_simplification={self.enable_expression_simplification!r}",
|
|
609
|
+
f"enable_parameter_type_wrapping={self.enable_parameter_type_wrapping!r}",
|
|
610
|
+
f"enable_caching={self.enable_caching!r}",
|
|
611
|
+
f"parameter_converter={self.parameter_converter!r}",
|
|
612
|
+
f"parameter_validator={self.parameter_validator!r}",
|
|
613
|
+
f"dialect={self.dialect!r}",
|
|
614
|
+
f"pre_process_steps={self.pre_process_steps!r}",
|
|
615
|
+
f"post_process_steps={self.post_process_steps!r}",
|
|
616
|
+
f"execution_mode={self.execution_mode!r}",
|
|
617
|
+
f"execution_args={self.execution_args!r}",
|
|
618
|
+
f"output_transformer={self.output_transformer!r}",
|
|
619
|
+
]
|
|
626
620
|
return f"{self.__class__.__name__}({', '.join(field_strs)})"
|
|
627
621
|
|
|
628
622
|
def __eq__(self, other: object) -> bool:
|
|
@@ -630,35 +624,32 @@ class StatementConfig:
|
|
|
630
624
|
if not isinstance(other, type(self)):
|
|
631
625
|
return False
|
|
632
626
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
627
|
+
if not self._compare_parameter_configs(self.parameter_config, other.parameter_config):
|
|
628
|
+
return False
|
|
629
|
+
|
|
630
|
+
return (
|
|
631
|
+
self.enable_parsing == other.enable_parsing
|
|
632
|
+
and self.enable_validation == other.enable_validation
|
|
633
|
+
and self.enable_transformations == other.enable_transformations
|
|
634
|
+
and self.enable_analysis == other.enable_analysis
|
|
635
|
+
and self.enable_expression_simplification == other.enable_expression_simplification
|
|
636
|
+
and self.enable_parameter_type_wrapping == other.enable_parameter_type_wrapping
|
|
637
|
+
and self.enable_caching == other.enable_caching
|
|
638
|
+
and self.dialect == other.dialect
|
|
639
|
+
and self.pre_process_steps == other.pre_process_steps
|
|
640
|
+
and self.post_process_steps == other.post_process_steps
|
|
641
|
+
and self.execution_mode == other.execution_mode
|
|
642
|
+
and self.execution_args == other.execution_args
|
|
643
|
+
and self.output_transformer == other.output_transformer
|
|
644
|
+
)
|
|
650
645
|
|
|
651
646
|
def _compare_parameter_configs(self, config1: Any, config2: Any) -> bool:
|
|
652
|
-
"""Compare parameter configs
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
== getattr(config2, "supported_execution_parameter_styles", None)
|
|
659
|
-
)
|
|
660
|
-
except AttributeError:
|
|
661
|
-
return False
|
|
647
|
+
"""Compare parameter configs."""
|
|
648
|
+
return bool(
|
|
649
|
+
config1.default_parameter_style == config2.default_parameter_style
|
|
650
|
+
and config1.supported_parameter_styles == config2.supported_parameter_styles
|
|
651
|
+
and config1.supported_execution_parameter_styles == config2.supported_execution_parameter_styles
|
|
652
|
+
)
|
|
662
653
|
|
|
663
654
|
|
|
664
655
|
def get_default_config() -> StatementConfig:
|
sqlspec/driver/_async.py
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
"""Asynchronous driver protocol implementation.
|
|
2
|
-
|
|
3
|
-
This module provides the async driver infrastructure for database adapters,
|
|
4
|
-
including connection management, transaction support, and result processing.
|
|
5
|
-
"""
|
|
1
|
+
"""Asynchronous driver protocol implementation."""
|
|
6
2
|
|
|
7
3
|
from abc import abstractmethod
|
|
8
4
|
from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, Union, cast, overload
|
|
@@ -32,22 +28,13 @@ EMPTY_FILTERS: Final["list[StatementFilter]"] = []
|
|
|
32
28
|
|
|
33
29
|
|
|
34
30
|
class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, ToSchemaMixin):
|
|
35
|
-
"""Base class for asynchronous database drivers.
|
|
36
|
-
|
|
37
|
-
Provides the foundation for async database adapters, including connection management,
|
|
38
|
-
transaction support, and SQL execution methods. All database operations are performed
|
|
39
|
-
asynchronously and support context manager patterns for proper resource cleanup.
|
|
40
|
-
"""
|
|
31
|
+
"""Base class for asynchronous database drivers."""
|
|
41
32
|
|
|
42
33
|
__slots__ = ()
|
|
43
34
|
|
|
44
35
|
async def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult":
|
|
45
36
|
"""Central execution dispatcher using the Template Method Pattern.
|
|
46
37
|
|
|
47
|
-
Orchestrates the common execution flow, delegating database-specific steps
|
|
48
|
-
to abstract methods that concrete adapters must implement.
|
|
49
|
-
All database operations are wrapped in exception handling.
|
|
50
|
-
|
|
51
38
|
Args:
|
|
52
39
|
statement: The SQL statement to execute
|
|
53
40
|
connection: The database connection to use
|