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.

Files changed (95) hide show
  1. sqlspec/_serialization.py +223 -21
  2. sqlspec/_sql.py +20 -62
  3. sqlspec/_typing.py +11 -0
  4. sqlspec/adapters/adbc/config.py +8 -1
  5. sqlspec/adapters/adbc/data_dictionary.py +290 -0
  6. sqlspec/adapters/adbc/driver.py +129 -20
  7. sqlspec/adapters/adbc/type_converter.py +159 -0
  8. sqlspec/adapters/aiosqlite/config.py +3 -0
  9. sqlspec/adapters/aiosqlite/data_dictionary.py +117 -0
  10. sqlspec/adapters/aiosqlite/driver.py +17 -3
  11. sqlspec/adapters/asyncmy/_types.py +1 -1
  12. sqlspec/adapters/asyncmy/config.py +11 -8
  13. sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
  14. sqlspec/adapters/asyncmy/driver.py +31 -7
  15. sqlspec/adapters/asyncpg/config.py +3 -0
  16. sqlspec/adapters/asyncpg/data_dictionary.py +134 -0
  17. sqlspec/adapters/asyncpg/driver.py +19 -4
  18. sqlspec/adapters/bigquery/config.py +3 -0
  19. sqlspec/adapters/bigquery/data_dictionary.py +109 -0
  20. sqlspec/adapters/bigquery/driver.py +21 -3
  21. sqlspec/adapters/bigquery/type_converter.py +93 -0
  22. sqlspec/adapters/duckdb/_types.py +1 -1
  23. sqlspec/adapters/duckdb/config.py +2 -0
  24. sqlspec/adapters/duckdb/data_dictionary.py +124 -0
  25. sqlspec/adapters/duckdb/driver.py +32 -5
  26. sqlspec/adapters/duckdb/pool.py +1 -1
  27. sqlspec/adapters/duckdb/type_converter.py +103 -0
  28. sqlspec/adapters/oracledb/config.py +6 -0
  29. sqlspec/adapters/oracledb/data_dictionary.py +442 -0
  30. sqlspec/adapters/oracledb/driver.py +68 -9
  31. sqlspec/adapters/oracledb/migrations.py +51 -67
  32. sqlspec/adapters/oracledb/type_converter.py +132 -0
  33. sqlspec/adapters/psqlpy/config.py +3 -0
  34. sqlspec/adapters/psqlpy/data_dictionary.py +133 -0
  35. sqlspec/adapters/psqlpy/driver.py +23 -179
  36. sqlspec/adapters/psqlpy/type_converter.py +73 -0
  37. sqlspec/adapters/psycopg/config.py +8 -4
  38. sqlspec/adapters/psycopg/data_dictionary.py +257 -0
  39. sqlspec/adapters/psycopg/driver.py +40 -5
  40. sqlspec/adapters/sqlite/config.py +3 -0
  41. sqlspec/adapters/sqlite/data_dictionary.py +117 -0
  42. sqlspec/adapters/sqlite/driver.py +18 -3
  43. sqlspec/adapters/sqlite/pool.py +13 -4
  44. sqlspec/base.py +3 -4
  45. sqlspec/builder/_base.py +130 -48
  46. sqlspec/builder/_column.py +66 -24
  47. sqlspec/builder/_ddl.py +91 -41
  48. sqlspec/builder/_insert.py +40 -58
  49. sqlspec/builder/_parsing_utils.py +127 -12
  50. sqlspec/builder/_select.py +147 -2
  51. sqlspec/builder/_update.py +1 -1
  52. sqlspec/builder/mixins/_cte_and_set_ops.py +31 -23
  53. sqlspec/builder/mixins/_delete_operations.py +12 -7
  54. sqlspec/builder/mixins/_insert_operations.py +50 -36
  55. sqlspec/builder/mixins/_join_operations.py +15 -30
  56. sqlspec/builder/mixins/_merge_operations.py +210 -78
  57. sqlspec/builder/mixins/_order_limit_operations.py +4 -10
  58. sqlspec/builder/mixins/_pivot_operations.py +1 -0
  59. sqlspec/builder/mixins/_select_operations.py +44 -22
  60. sqlspec/builder/mixins/_update_operations.py +30 -37
  61. sqlspec/builder/mixins/_where_clause.py +52 -70
  62. sqlspec/cli.py +246 -140
  63. sqlspec/config.py +33 -19
  64. sqlspec/core/__init__.py +3 -2
  65. sqlspec/core/cache.py +298 -352
  66. sqlspec/core/compiler.py +61 -4
  67. sqlspec/core/filters.py +246 -213
  68. sqlspec/core/hashing.py +9 -11
  69. sqlspec/core/parameters.py +27 -10
  70. sqlspec/core/statement.py +72 -12
  71. sqlspec/core/type_conversion.py +234 -0
  72. sqlspec/driver/__init__.py +6 -3
  73. sqlspec/driver/_async.py +108 -5
  74. sqlspec/driver/_common.py +186 -17
  75. sqlspec/driver/_sync.py +108 -5
  76. sqlspec/driver/mixins/_result_tools.py +60 -7
  77. sqlspec/exceptions.py +5 -0
  78. sqlspec/loader.py +8 -9
  79. sqlspec/migrations/__init__.py +4 -3
  80. sqlspec/migrations/base.py +153 -14
  81. sqlspec/migrations/commands.py +34 -96
  82. sqlspec/migrations/context.py +145 -0
  83. sqlspec/migrations/loaders.py +25 -8
  84. sqlspec/migrations/runner.py +352 -82
  85. sqlspec/storage/backends/fsspec.py +1 -0
  86. sqlspec/typing.py +4 -0
  87. sqlspec/utils/config_resolver.py +153 -0
  88. sqlspec/utils/serializers.py +50 -2
  89. {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/METADATA +1 -1
  90. sqlspec-0.26.0.dist-info/RECORD +157 -0
  91. sqlspec-0.24.1.dist-info/RECORD +0 -139
  92. {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/WHEEL +0 -0
  93. {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/entry_points.txt +0 -0
  94. {sqlspec-0.24.1.dist-info → sqlspec-0.26.0.dist-info}/licenses/LICENSE +0 -0
  95. {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._get_sqlglot_compatible_sql(
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(compiled_sql=sql, execution_parameters=parameters, operation_type="UNKNOWN")
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]":