sqlspec 0.24.1__py3-none-any.whl → 0.26.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/_serialization.py +223 -21
- sqlspec/_sql.py +20 -62
- sqlspec/_typing.py +11 -0
- sqlspec/adapters/adbc/config.py +8 -1
- sqlspec/adapters/adbc/data_dictionary.py +290 -0
- sqlspec/adapters/adbc/driver.py +129 -20
- sqlspec/adapters/adbc/type_converter.py +159 -0
- sqlspec/adapters/aiosqlite/config.py +3 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +117 -0
- sqlspec/adapters/aiosqlite/driver.py +17 -3
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/config.py +11 -8
- sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
- sqlspec/adapters/asyncmy/driver.py +31 -7
- sqlspec/adapters/asyncpg/config.py +3 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +134 -0
- sqlspec/adapters/asyncpg/driver.py +19 -4
- sqlspec/adapters/bigquery/config.py +3 -0
- sqlspec/adapters/bigquery/data_dictionary.py +109 -0
- sqlspec/adapters/bigquery/driver.py +21 -3
- sqlspec/adapters/bigquery/type_converter.py +93 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/config.py +2 -0
- sqlspec/adapters/duckdb/data_dictionary.py +124 -0
- sqlspec/adapters/duckdb/driver.py +32 -5
- sqlspec/adapters/duckdb/pool.py +1 -1
- sqlspec/adapters/duckdb/type_converter.py +103 -0
- sqlspec/adapters/oracledb/config.py +6 -0
- sqlspec/adapters/oracledb/data_dictionary.py +442 -0
- sqlspec/adapters/oracledb/driver.py +68 -9
- sqlspec/adapters/oracledb/migrations.py +51 -67
- sqlspec/adapters/oracledb/type_converter.py +132 -0
- sqlspec/adapters/psqlpy/config.py +3 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +133 -0
- sqlspec/adapters/psqlpy/driver.py +23 -179
- sqlspec/adapters/psqlpy/type_converter.py +73 -0
- sqlspec/adapters/psycopg/config.py +8 -4
- sqlspec/adapters/psycopg/data_dictionary.py +257 -0
- sqlspec/adapters/psycopg/driver.py +40 -5
- sqlspec/adapters/sqlite/config.py +3 -0
- sqlspec/adapters/sqlite/data_dictionary.py +117 -0
- sqlspec/adapters/sqlite/driver.py +18 -3
- sqlspec/adapters/sqlite/pool.py +13 -4
- sqlspec/base.py +3 -4
- sqlspec/builder/_base.py +130 -48
- sqlspec/builder/_column.py +66 -24
- sqlspec/builder/_ddl.py +91 -41
- sqlspec/builder/_insert.py +40 -58
- sqlspec/builder/_parsing_utils.py +127 -12
- sqlspec/builder/_select.py +147 -2
- sqlspec/builder/_update.py +1 -1
- sqlspec/builder/mixins/_cte_and_set_ops.py +31 -23
- sqlspec/builder/mixins/_delete_operations.py +12 -7
- sqlspec/builder/mixins/_insert_operations.py +50 -36
- sqlspec/builder/mixins/_join_operations.py +15 -30
- sqlspec/builder/mixins/_merge_operations.py +210 -78
- sqlspec/builder/mixins/_order_limit_operations.py +4 -10
- sqlspec/builder/mixins/_pivot_operations.py +1 -0
- sqlspec/builder/mixins/_select_operations.py +44 -22
- sqlspec/builder/mixins/_update_operations.py +30 -37
- sqlspec/builder/mixins/_where_clause.py +52 -70
- sqlspec/cli.py +246 -140
- sqlspec/config.py +33 -19
- sqlspec/core/__init__.py +3 -2
- sqlspec/core/cache.py +298 -352
- sqlspec/core/compiler.py +61 -4
- sqlspec/core/filters.py +246 -213
- sqlspec/core/hashing.py +9 -11
- sqlspec/core/parameters.py +27 -10
- sqlspec/core/statement.py +72 -12
- sqlspec/core/type_conversion.py +234 -0
- sqlspec/driver/__init__.py +6 -3
- sqlspec/driver/_async.py +108 -5
- sqlspec/driver/_common.py +186 -17
- sqlspec/driver/_sync.py +108 -5
- sqlspec/driver/mixins/_result_tools.py +60 -7
- sqlspec/exceptions.py +5 -0
- sqlspec/loader.py +8 -9
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +153 -14
- sqlspec/migrations/commands.py +34 -96
- sqlspec/migrations/context.py +145 -0
- sqlspec/migrations/loaders.py +25 -8
- sqlspec/migrations/runner.py +352 -82
- sqlspec/storage/backends/fsspec.py +1 -0
- sqlspec/typing.py +4 -0
- sqlspec/utils/config_resolver.py +153 -0
- sqlspec/utils/serializers.py +50 -2
- {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/METADATA +1 -1
- sqlspec-0.26.0.dist-info/RECORD +157 -0
- sqlspec-0.24.1.dist-info/RECORD +0 -139
- {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/core/compiler.py
CHANGED
|
@@ -72,6 +72,7 @@ class CompiledSQL:
|
|
|
72
72
|
"execution_parameters",
|
|
73
73
|
"expression",
|
|
74
74
|
"operation_type",
|
|
75
|
+
"parameter_casts",
|
|
75
76
|
"parameter_style",
|
|
76
77
|
"supports_many",
|
|
77
78
|
)
|
|
@@ -86,6 +87,7 @@ class CompiledSQL:
|
|
|
86
87
|
expression: Optional["exp.Expression"] = None,
|
|
87
88
|
parameter_style: Optional[str] = None,
|
|
88
89
|
supports_many: bool = False,
|
|
90
|
+
parameter_casts: Optional["dict[int, str]"] = None,
|
|
89
91
|
) -> None:
|
|
90
92
|
"""Initialize compiled result.
|
|
91
93
|
|
|
@@ -96,6 +98,7 @@ class CompiledSQL:
|
|
|
96
98
|
expression: SQLGlot AST expression
|
|
97
99
|
parameter_style: Parameter style used in compilation
|
|
98
100
|
supports_many: Whether this supports execute_many operations
|
|
101
|
+
parameter_casts: Mapping of parameter positions to cast types
|
|
99
102
|
"""
|
|
100
103
|
self.compiled_sql = compiled_sql
|
|
101
104
|
self.execution_parameters = execution_parameters
|
|
@@ -103,6 +106,7 @@ class CompiledSQL:
|
|
|
103
106
|
self.expression = expression
|
|
104
107
|
self.parameter_style = parameter_style
|
|
105
108
|
self.supports_many = supports_many
|
|
109
|
+
self.parameter_casts = parameter_casts or {}
|
|
106
110
|
self._hash: Optional[int] = None
|
|
107
111
|
|
|
108
112
|
def __hash__(self) -> int:
|
|
@@ -171,7 +175,7 @@ class SQLProcessor:
|
|
|
171
175
|
if not self._config.enable_caching:
|
|
172
176
|
return self._compile_uncached(sql, parameters, is_many)
|
|
173
177
|
|
|
174
|
-
cache_key = self._make_cache_key(sql, parameters)
|
|
178
|
+
cache_key = self._make_cache_key(sql, parameters, is_many)
|
|
175
179
|
|
|
176
180
|
if cache_key in self._cache:
|
|
177
181
|
result = self._cache[cache_key]
|
|
@@ -216,7 +220,7 @@ class SQLProcessor:
|
|
|
216
220
|
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
217
221
|
sqlglot_sql = processed_sql
|
|
218
222
|
else:
|
|
219
|
-
sqlglot_sql, _ = self._parameter_processor.
|
|
223
|
+
sqlglot_sql, _ = self._parameter_processor.get_sqlglot_compatible_sql(
|
|
220
224
|
sql, parameters, self._config.parameter_config, dialect_str
|
|
221
225
|
)
|
|
222
226
|
|
|
@@ -224,11 +228,13 @@ class SQLProcessor:
|
|
|
224
228
|
ast_was_transformed = False
|
|
225
229
|
expression = None
|
|
226
230
|
operation_type: OperationType = "EXECUTE"
|
|
231
|
+
parameter_casts: dict[int, str] = {}
|
|
227
232
|
|
|
228
233
|
if self._config.enable_parsing:
|
|
229
234
|
try:
|
|
230
235
|
expression = sqlglot.parse_one(sqlglot_sql, dialect=dialect_str)
|
|
231
236
|
operation_type = self._detect_operation_type(expression)
|
|
237
|
+
parameter_casts = self._detect_parameter_casts(expression)
|
|
232
238
|
|
|
233
239
|
ast_transformer = self._config.parameter_config.ast_transformer
|
|
234
240
|
if ast_transformer:
|
|
@@ -238,6 +244,7 @@ class SQLProcessor:
|
|
|
238
244
|
except ParseError:
|
|
239
245
|
expression = None
|
|
240
246
|
operation_type = "EXECUTE"
|
|
247
|
+
parameter_casts = {}
|
|
241
248
|
|
|
242
249
|
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
243
250
|
final_sql, final_params = processed_sql, processed_params
|
|
@@ -264,6 +271,7 @@ class SQLProcessor:
|
|
|
264
271
|
expression=expression,
|
|
265
272
|
parameter_style=self._config.parameter_config.default_parameter_style.value,
|
|
266
273
|
supports_many=isinstance(final_params, list) and len(final_params) > 0,
|
|
274
|
+
parameter_casts=parameter_casts,
|
|
267
275
|
)
|
|
268
276
|
|
|
269
277
|
except SQLSpecError:
|
|
@@ -271,14 +279,17 @@ class SQLProcessor:
|
|
|
271
279
|
raise
|
|
272
280
|
except Exception as e:
|
|
273
281
|
logger.warning("Compilation failed, using fallback: %s", e)
|
|
274
|
-
return CompiledSQL(
|
|
282
|
+
return CompiledSQL(
|
|
283
|
+
compiled_sql=sql, execution_parameters=parameters, operation_type="UNKNOWN", parameter_casts={}
|
|
284
|
+
)
|
|
275
285
|
|
|
276
|
-
def _make_cache_key(self, sql: str, parameters: Any) -> str:
|
|
286
|
+
def _make_cache_key(self, sql: str, parameters: Any, is_many: bool = False) -> str:
|
|
277
287
|
"""Generate cache key.
|
|
278
288
|
|
|
279
289
|
Args:
|
|
280
290
|
sql: SQL string
|
|
281
291
|
parameters: Parameter values
|
|
292
|
+
is_many: Whether this is for execute_many operation
|
|
282
293
|
|
|
283
294
|
Returns:
|
|
284
295
|
Cache key string
|
|
@@ -295,6 +306,7 @@ class SQLProcessor:
|
|
|
295
306
|
dialect_str,
|
|
296
307
|
self._config.enable_parsing,
|
|
297
308
|
self._config.enable_transformations,
|
|
309
|
+
is_many,
|
|
298
310
|
)
|
|
299
311
|
|
|
300
312
|
hash_str = hashlib.sha256(str(hash_data).encode("utf-8")).hexdigest()[:16]
|
|
@@ -324,6 +336,51 @@ class SQLProcessor:
|
|
|
324
336
|
|
|
325
337
|
return "UNKNOWN"
|
|
326
338
|
|
|
339
|
+
def _detect_parameter_casts(self, expression: Optional["exp.Expression"]) -> "dict[int, str]":
|
|
340
|
+
"""Detect explicit type casts on parameters in the AST.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
expression: SQLGlot AST expression to analyze
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Dict mapping parameter positions (1-based) to cast type names
|
|
347
|
+
"""
|
|
348
|
+
if not expression:
|
|
349
|
+
return {}
|
|
350
|
+
|
|
351
|
+
cast_positions = {}
|
|
352
|
+
|
|
353
|
+
# Walk all nodes in order to track parameter positions
|
|
354
|
+
for node in expression.walk():
|
|
355
|
+
# Check for cast nodes with parameter children
|
|
356
|
+
if isinstance(node, exp.Cast):
|
|
357
|
+
cast_target = node.this
|
|
358
|
+
position = None
|
|
359
|
+
|
|
360
|
+
if isinstance(cast_target, exp.Parameter):
|
|
361
|
+
# Handle $1, $2 style parameters
|
|
362
|
+
param_value = cast_target.this
|
|
363
|
+
if isinstance(param_value, exp.Literal):
|
|
364
|
+
position = int(param_value.this)
|
|
365
|
+
elif isinstance(cast_target, exp.Placeholder):
|
|
366
|
+
# For ? style, we need to count position (will implement if needed)
|
|
367
|
+
pass
|
|
368
|
+
elif isinstance(cast_target, exp.Column):
|
|
369
|
+
# Handle cases where $1 gets parsed as a column
|
|
370
|
+
column_name = str(cast_target.this) if cast_target.this else str(cast_target)
|
|
371
|
+
if column_name.startswith("$") and column_name[1:].isdigit():
|
|
372
|
+
position = int(column_name[1:])
|
|
373
|
+
|
|
374
|
+
if position is not None:
|
|
375
|
+
# Extract cast type
|
|
376
|
+
if isinstance(node.to, exp.DataType):
|
|
377
|
+
cast_type = node.to.this.value if hasattr(node.to.this, "value") else str(node.to.this)
|
|
378
|
+
else:
|
|
379
|
+
cast_type = str(node.to)
|
|
380
|
+
cast_positions[position] = cast_type.upper()
|
|
381
|
+
|
|
382
|
+
return cast_positions
|
|
383
|
+
|
|
327
384
|
def _apply_final_transformations(
|
|
328
385
|
self, expression: "Optional[exp.Expression]", sql: str, parameters: Any, dialect_str: "Optional[str]"
|
|
329
386
|
) -> "tuple[str, Any]":
|