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/hashing.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"""Statement hashing utilities for cache key generation.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Also supports fine-grained AST sub-expression caching.
|
|
3
|
+
Provides hashing functions for SQL statements, expressions, parameters,
|
|
4
|
+
filters, and AST sub-expressions.
|
|
7
5
|
"""
|
|
8
6
|
|
|
9
7
|
from typing import TYPE_CHECKING, Any, Optional
|
|
@@ -26,14 +24,14 @@ __all__ = (
|
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
def hash_expression(expr: Optional[exp.Expression], _seen: Optional[set[int]] = None) -> int:
|
|
29
|
-
"""Generate
|
|
27
|
+
"""Generate hash from AST structure.
|
|
30
28
|
|
|
31
29
|
Args:
|
|
32
30
|
expr: SQLGlot Expression to hash
|
|
33
31
|
_seen: Set of seen object IDs to handle circular references
|
|
34
32
|
|
|
35
33
|
Returns:
|
|
36
|
-
|
|
34
|
+
Hash of the AST structure
|
|
37
35
|
"""
|
|
38
36
|
if expr is None:
|
|
39
37
|
return hash(None)
|
|
@@ -47,7 +45,6 @@ def hash_expression(expr: Optional[exp.Expression], _seen: Optional[set[int]] =
|
|
|
47
45
|
|
|
48
46
|
_seen.add(expr_id)
|
|
49
47
|
|
|
50
|
-
# Build hash from type and args
|
|
51
48
|
components: list[Any] = [type(expr).__name__]
|
|
52
49
|
|
|
53
50
|
for key, value in sorted(expr.args.items()):
|
|
@@ -57,14 +54,14 @@ def hash_expression(expr: Optional[exp.Expression], _seen: Optional[set[int]] =
|
|
|
57
54
|
|
|
58
55
|
|
|
59
56
|
def _hash_value(value: Any, _seen: set[int]) -> int:
|
|
60
|
-
"""Hash different value types
|
|
57
|
+
"""Hash different value types.
|
|
61
58
|
|
|
62
59
|
Args:
|
|
63
60
|
value: Value to hash (can be Expression, list, dict, or primitive)
|
|
64
61
|
_seen: Set of seen object IDs to handle circular references
|
|
65
62
|
|
|
66
63
|
Returns:
|
|
67
|
-
|
|
64
|
+
Hash of the value
|
|
68
65
|
"""
|
|
69
66
|
if isinstance(value, exp.Expression):
|
|
70
67
|
return hash_expression(value, _seen)
|
|
@@ -75,7 +72,7 @@ def _hash_value(value: Any, _seen: set[int]) -> int:
|
|
|
75
72
|
return hash(tuple(items))
|
|
76
73
|
if isinstance(value, tuple):
|
|
77
74
|
return hash(tuple(_hash_value(v, _seen) for v in value))
|
|
78
|
-
|
|
75
|
+
|
|
79
76
|
return hash(value)
|
|
80
77
|
|
|
81
78
|
|
|
@@ -96,7 +93,6 @@ def hash_parameters(
|
|
|
96
93
|
"""
|
|
97
94
|
param_hash = 0
|
|
98
95
|
|
|
99
|
-
# Hash positional parameters
|
|
100
96
|
if positional_parameters:
|
|
101
97
|
from sqlspec.core.parameters import TypedParameter
|
|
102
98
|
|
|
@@ -108,25 +104,20 @@ def hash_parameters(
|
|
|
108
104
|
else:
|
|
109
105
|
hashable_parameters.append((param.value, param.original_type))
|
|
110
106
|
elif isinstance(param, (list, dict)):
|
|
111
|
-
# Convert unhashable types to hashable representations
|
|
112
107
|
hashable_parameters.append((repr(param), "unhashable"))
|
|
113
108
|
else:
|
|
114
|
-
# Check if param itself contains unhashable types
|
|
115
109
|
try:
|
|
116
110
|
hash(param)
|
|
117
111
|
hashable_parameters.append((param, "primitive"))
|
|
118
112
|
except TypeError:
|
|
119
|
-
# If unhashable, convert to string representation
|
|
120
113
|
hashable_parameters.append((repr(param), "unhashable_repr"))
|
|
121
114
|
|
|
122
115
|
param_hash ^= hash(tuple(hashable_parameters))
|
|
123
116
|
|
|
124
117
|
if named_parameters:
|
|
125
|
-
# Handle unhashable types in named parameters
|
|
126
118
|
hashable_items = []
|
|
127
119
|
for key, value in sorted(named_parameters.items()):
|
|
128
120
|
if is_typed_parameter(value):
|
|
129
|
-
# For TypedParameter, hash its value with type info
|
|
130
121
|
if isinstance(value.value, (list, dict)):
|
|
131
122
|
hashable_items.append((key, (repr(value.value), value.original_type)))
|
|
132
123
|
else:
|
|
@@ -137,13 +128,10 @@ def hash_parameters(
|
|
|
137
128
|
hashable_items.append((key, (value, "primitive")))
|
|
138
129
|
param_hash ^= hash(tuple(hashable_items))
|
|
139
130
|
|
|
140
|
-
# Hash original parameters (important for execute_many)
|
|
141
131
|
if original_parameters is not None:
|
|
142
132
|
if isinstance(original_parameters, list):
|
|
143
|
-
# For execute_many, hash the count and first few items to avoid
|
|
144
133
|
param_hash ^= hash(("original_count", len(original_parameters)))
|
|
145
134
|
if original_parameters:
|
|
146
|
-
# Hash first 3 items as representatives
|
|
147
135
|
sample_size = min(3, len(original_parameters))
|
|
148
136
|
sample_hash = hash(repr(original_parameters[:sample_size]))
|
|
149
137
|
param_hash ^= hash(("original_sample", sample_hash))
|
|
@@ -172,14 +160,10 @@ def hash_filters(filters: Optional[list["StatementFilter"]] = None) -> int:
|
|
|
172
160
|
if not filters:
|
|
173
161
|
return 0
|
|
174
162
|
|
|
175
|
-
# Use class names and any hashable attributes
|
|
176
163
|
filter_components = []
|
|
177
164
|
for f in filters:
|
|
178
|
-
# Use class name as primary identifier
|
|
179
165
|
components: list[Any] = [f.__class__.__name__]
|
|
180
166
|
|
|
181
|
-
# Add any hashable attributes if available
|
|
182
|
-
# Use getattr with default instead of hasattr for mypyc compatibility
|
|
183
167
|
filter_dict = getattr(f, "__dict__", None)
|
|
184
168
|
if filter_dict is not None:
|
|
185
169
|
for key, value in sorted(filter_dict.items()):
|
|
@@ -191,10 +175,7 @@ def hash_filters(filters: Optional[list["StatementFilter"]] = None) -> int:
|
|
|
191
175
|
|
|
192
176
|
|
|
193
177
|
def hash_sql_statement(statement: "SQL") -> str:
|
|
194
|
-
"""Generate
|
|
195
|
-
|
|
196
|
-
This centralizes all the complex hashing logic that was previously
|
|
197
|
-
scattered across different parts of the codebase.
|
|
178
|
+
"""Generate cache key for a SQL statement.
|
|
198
179
|
|
|
199
180
|
Args:
|
|
200
181
|
statement: SQL statement object
|
|
@@ -204,23 +185,19 @@ def hash_sql_statement(statement: "SQL") -> str:
|
|
|
204
185
|
"""
|
|
205
186
|
from sqlspec.utils.type_guards import is_expression
|
|
206
187
|
|
|
207
|
-
# Hash the expression or raw SQL
|
|
208
188
|
if is_expression(statement._statement):
|
|
209
189
|
expr_hash = hash_expression(statement._statement)
|
|
210
190
|
else:
|
|
211
191
|
expr_hash = hash(statement._raw_sql)
|
|
212
192
|
|
|
213
|
-
# Hash all parameters
|
|
214
193
|
param_hash = hash_parameters(
|
|
215
194
|
positional_parameters=statement._positional_parameters,
|
|
216
195
|
named_parameters=statement._named_parameters,
|
|
217
196
|
original_parameters=statement._original_parameters,
|
|
218
197
|
)
|
|
219
198
|
|
|
220
|
-
# Hash filters
|
|
221
199
|
filter_hash = hash_filters(statement._filters)
|
|
222
200
|
|
|
223
|
-
# Combine with other state
|
|
224
201
|
state_components = [
|
|
225
202
|
expr_hash,
|
|
226
203
|
param_hash,
|
|
@@ -234,10 +211,7 @@ def hash_sql_statement(statement: "SQL") -> str:
|
|
|
234
211
|
|
|
235
212
|
|
|
236
213
|
def hash_expression_node(node: exp.Expression, include_children: bool = True, dialect: Optional[str] = None) -> str:
|
|
237
|
-
"""Generate
|
|
238
|
-
|
|
239
|
-
This is used for sub-expression caching where we want to cache
|
|
240
|
-
frequently used SQL fragments like complex JOIN clauses or WHERE conditions.
|
|
214
|
+
"""Generate cache key for an expression node.
|
|
241
215
|
|
|
242
216
|
Args:
|
|
243
217
|
node: The expression node to hash
|
|
@@ -248,14 +222,11 @@ def hash_expression_node(node: exp.Expression, include_children: bool = True, di
|
|
|
248
222
|
Cache key string for the expression node
|
|
249
223
|
"""
|
|
250
224
|
if include_children:
|
|
251
|
-
# Full structural hash including all children
|
|
252
225
|
node_hash = hash_expression(node)
|
|
253
226
|
else:
|
|
254
|
-
# Shallow hash - just the node type and immediate args
|
|
255
227
|
components: list[Any] = [type(node).__name__]
|
|
256
228
|
for key, value in sorted(node.args.items()):
|
|
257
229
|
if not isinstance(value, (list, exp.Expression)):
|
|
258
|
-
# Only include primitive values, not child expressions
|
|
259
230
|
components.extend((key, hash(value)))
|
|
260
231
|
node_hash = hash(tuple(components))
|
|
261
232
|
|
|
@@ -269,11 +240,10 @@ def hash_optimized_expression(
|
|
|
269
240
|
schema: Optional[dict[str, Any]] = None,
|
|
270
241
|
optimizer_settings: Optional[dict[str, Any]] = None,
|
|
271
242
|
) -> str:
|
|
272
|
-
"""Generate
|
|
243
|
+
"""Generate cache key for optimized expressions.
|
|
273
244
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
reuse optimized expressions when all context matches.
|
|
245
|
+
Creates a key that includes expression structure, dialect, schema
|
|
246
|
+
context, and optimizer settings.
|
|
277
247
|
|
|
278
248
|
Args:
|
|
279
249
|
expr: The unoptimized expression
|
|
@@ -284,13 +254,11 @@ def hash_optimized_expression(
|
|
|
284
254
|
Returns:
|
|
285
255
|
Cache key string for the optimized expression
|
|
286
256
|
"""
|
|
287
|
-
|
|
257
|
+
|
|
288
258
|
expr_hash = hash_expression(expr)
|
|
289
259
|
|
|
290
|
-
# Schema hash - simplified representation
|
|
291
260
|
schema_hash = 0
|
|
292
261
|
if schema:
|
|
293
|
-
# Hash table names and column counts to avoid deep schema hashing
|
|
294
262
|
schema_items = []
|
|
295
263
|
for table_name, table_schema in sorted(schema.items()):
|
|
296
264
|
if isinstance(table_schema, dict):
|
|
@@ -299,12 +267,10 @@ def hash_optimized_expression(
|
|
|
299
267
|
schema_items.append((table_name, hash("unknown")))
|
|
300
268
|
schema_hash = hash(tuple(schema_items))
|
|
301
269
|
|
|
302
|
-
# Optimizer settings hash
|
|
303
270
|
settings_hash = 0
|
|
304
271
|
if optimizer_settings:
|
|
305
272
|
settings_items = sorted(optimizer_settings.items())
|
|
306
273
|
settings_hash = hash(tuple(settings_items))
|
|
307
274
|
|
|
308
|
-
# Combine all components
|
|
309
275
|
components = (expr_hash, dialect, schema_hash, settings_hash)
|
|
310
276
|
return f"opt:{hash(components)}"
|