sqlspec 0.16.1__cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- 51ff5a9eadfdefd49f98__mypyc.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +92 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +77 -0
- sqlspec/_sql.py +1780 -0
- sqlspec/_typing.py +680 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +361 -0
- sqlspec/adapters/adbc/driver.py +512 -0
- sqlspec/adapters/aiosqlite/__init__.py +19 -0
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +253 -0
- sqlspec/adapters/aiosqlite/driver.py +248 -0
- sqlspec/adapters/asyncmy/__init__.py +19 -0
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +180 -0
- sqlspec/adapters/asyncmy/driver.py +274 -0
- sqlspec/adapters/asyncpg/__init__.py +21 -0
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +229 -0
- sqlspec/adapters/asyncpg/driver.py +344 -0
- sqlspec/adapters/bigquery/__init__.py +18 -0
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +298 -0
- sqlspec/adapters/bigquery/driver.py +558 -0
- sqlspec/adapters/duckdb/__init__.py +22 -0
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +504 -0
- sqlspec/adapters/duckdb/driver.py +368 -0
- sqlspec/adapters/oracledb/__init__.py +32 -0
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +317 -0
- sqlspec/adapters/oracledb/driver.py +538 -0
- sqlspec/adapters/psqlpy/__init__.py +16 -0
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +214 -0
- sqlspec/adapters/psqlpy/driver.py +530 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +426 -0
- sqlspec/adapters/psycopg/driver.py +796 -0
- sqlspec/adapters/sqlite/__init__.py +15 -0
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/driver.py +294 -0
- sqlspec/base.py +571 -0
- sqlspec/builder/__init__.py +62 -0
- sqlspec/builder/_base.py +473 -0
- sqlspec/builder/_column.py +320 -0
- sqlspec/builder/_ddl.py +1346 -0
- sqlspec/builder/_ddl_utils.py +103 -0
- sqlspec/builder/_delete.py +76 -0
- sqlspec/builder/_insert.py +256 -0
- sqlspec/builder/_merge.py +71 -0
- sqlspec/builder/_parsing_utils.py +140 -0
- sqlspec/builder/_select.py +170 -0
- sqlspec/builder/_update.py +188 -0
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
- sqlspec/builder/mixins/_delete_operations.py +41 -0
- sqlspec/builder/mixins/_insert_operations.py +244 -0
- sqlspec/builder/mixins/_join_operations.py +122 -0
- sqlspec/builder/mixins/_merge_operations.py +476 -0
- sqlspec/builder/mixins/_order_limit_operations.py +135 -0
- sqlspec/builder/mixins/_pivot_operations.py +153 -0
- sqlspec/builder/mixins/_select_operations.py +603 -0
- sqlspec/builder/mixins/_update_operations.py +187 -0
- sqlspec/builder/mixins/_where_clause.py +621 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +395 -0
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result.py +677 -0
- sqlspec/core/splitter.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +819 -0
- sqlspec/core/statement.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +676 -0
- sqlspec/driver/__init__.py +19 -0
- sqlspec/driver/_async.py +502 -0
- sqlspec/driver/_common.py +631 -0
- sqlspec/driver/_sync.py +503 -0
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_result_tools.py +193 -0
- sqlspec/driver/mixins/_sql_translator.py +86 -0
- sqlspec/exceptions.py +193 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +461 -0
- sqlspec/extensions/litestar/__init__.py +6 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/config.py +92 -0
- sqlspec/extensions/litestar/handlers.py +260 -0
- sqlspec/extensions/litestar/plugin.py +145 -0
- sqlspec/extensions/litestar/providers.py +454 -0
- sqlspec/loader.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +760 -0
- sqlspec/migrations/__init__.py +35 -0
- sqlspec/migrations/base.py +414 -0
- sqlspec/migrations/commands.py +443 -0
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +213 -0
- sqlspec/migrations/tracker.py +140 -0
- sqlspec/migrations/utils.py +129 -0
- sqlspec/protocols.py +407 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +23 -0
- sqlspec/storage/backends/__init__.py +0 -0
- sqlspec/storage/backends/base.py +163 -0
- sqlspec/storage/backends/fsspec.py +386 -0
- sqlspec/storage/backends/obstore.py +459 -0
- sqlspec/storage/capabilities.py +102 -0
- sqlspec/storage/registry.py +239 -0
- sqlspec/typing.py +299 -0
- sqlspec/utils/__init__.py +3 -0
- sqlspec/utils/correlation.py +150 -0
- sqlspec/utils/deprecation.py +106 -0
- sqlspec/utils/fixtures.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +58 -0
- sqlspec/utils/logging.py +127 -0
- sqlspec/utils/module_loader.py +89 -0
- sqlspec/utils/serializers.py +4 -0
- sqlspec/utils/singleton.py +32 -0
- sqlspec/utils/sync_tools.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +237 -0
- sqlspec/utils/text.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +96 -0
- sqlspec/utils/type_guards.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1139 -0
- sqlspec-0.16.1.dist-info/METADATA +365 -0
- sqlspec-0.16.1.dist-info/RECORD +148 -0
- sqlspec-0.16.1.dist-info/WHEEL +7 -0
- sqlspec-0.16.1.dist-info/entry_points.txt +2 -0
- sqlspec-0.16.1.dist-info/licenses/LICENSE +21 -0
- sqlspec-0.16.1.dist-info/licenses/NOTICE +29 -0
|
Binary file
|
sqlspec/core/compiler.py
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"""SQL processor with integrated caching and compilation.
|
|
2
|
+
|
|
3
|
+
This module implements the core compilation system for SQL statements with
|
|
4
|
+
integrated parameter processing and caching.
|
|
5
|
+
|
|
6
|
+
Components:
|
|
7
|
+
- CompiledSQL: Immutable compilation result with complete information
|
|
8
|
+
- SQLProcessor: Single-pass compiler with integrated caching
|
|
9
|
+
- Integrated parameter processing via ParameterProcessor
|
|
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
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
from collections import OrderedDict
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
21
|
+
|
|
22
|
+
import sqlglot
|
|
23
|
+
from mypy_extensions import mypyc_attr
|
|
24
|
+
from sqlglot import expressions as exp
|
|
25
|
+
from sqlglot.errors import ParseError
|
|
26
|
+
from typing_extensions import Literal
|
|
27
|
+
|
|
28
|
+
from sqlspec.core.parameters import ParameterProcessor
|
|
29
|
+
from sqlspec.utils.logging import get_logger
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from sqlspec.core.statement import StatementConfig
|
|
33
|
+
|
|
34
|
+
# Define OperationType here to avoid circular import
|
|
35
|
+
OperationType = Literal[
|
|
36
|
+
"SELECT",
|
|
37
|
+
"INSERT",
|
|
38
|
+
"UPDATE",
|
|
39
|
+
"DELETE",
|
|
40
|
+
"COPY",
|
|
41
|
+
"COPY_FROM",
|
|
42
|
+
"COPY_TO",
|
|
43
|
+
"EXECUTE",
|
|
44
|
+
"SCRIPT",
|
|
45
|
+
"DDL",
|
|
46
|
+
"PRAGMA",
|
|
47
|
+
"UNKNOWN",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ("CompiledSQL", "OperationType", "SQLProcessor")
|
|
52
|
+
|
|
53
|
+
logger = get_logger("sqlspec.core.compiler")
|
|
54
|
+
|
|
55
|
+
_OPERATION_TYPES = {
|
|
56
|
+
"SELECT": "SELECT",
|
|
57
|
+
"INSERT": "INSERT",
|
|
58
|
+
"UPDATE": "UPDATE",
|
|
59
|
+
"DELETE": "DELETE",
|
|
60
|
+
"COPY": "COPY",
|
|
61
|
+
"COPY_FROM": "COPY_FROM",
|
|
62
|
+
"COPY_TO": "COPY_TO",
|
|
63
|
+
"EXECUTE": "EXECUTE",
|
|
64
|
+
"SCRIPT": "SCRIPT",
|
|
65
|
+
"DDL": "DDL",
|
|
66
|
+
"PRAGMA": "PRAGMA",
|
|
67
|
+
"UNKNOWN": "UNKNOWN",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
72
|
+
class CompiledSQL:
|
|
73
|
+
"""Immutable compiled SQL result with complete information.
|
|
74
|
+
|
|
75
|
+
This class represents the result of SQL compilation, containing all
|
|
76
|
+
information needed for execution.
|
|
77
|
+
|
|
78
|
+
Features:
|
|
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
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
__slots__ = (
|
|
87
|
+
"_hash",
|
|
88
|
+
"compiled_sql",
|
|
89
|
+
"execution_parameters",
|
|
90
|
+
"expression",
|
|
91
|
+
"operation_type",
|
|
92
|
+
"parameter_style",
|
|
93
|
+
"supports_many",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
compiled_sql: str,
|
|
99
|
+
execution_parameters: Any,
|
|
100
|
+
operation_type: str,
|
|
101
|
+
expression: Optional["exp.Expression"] = None,
|
|
102
|
+
parameter_style: Optional[str] = None,
|
|
103
|
+
supports_many: bool = False,
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Initialize immutable compiled result.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
compiled_sql: Final SQL string ready for execution
|
|
109
|
+
execution_parameters: Parameters in driver-specific format
|
|
110
|
+
operation_type: Detected SQL operation type (SELECT, INSERT, etc.)
|
|
111
|
+
expression: SQLGlot AST expression
|
|
112
|
+
parameter_style: Parameter style used in compilation
|
|
113
|
+
supports_many: Whether this supports execute_many operations
|
|
114
|
+
"""
|
|
115
|
+
self.compiled_sql = compiled_sql
|
|
116
|
+
self.execution_parameters = execution_parameters
|
|
117
|
+
self.operation_type = operation_type
|
|
118
|
+
self.expression = expression
|
|
119
|
+
self.parameter_style = parameter_style
|
|
120
|
+
self.supports_many = supports_many
|
|
121
|
+
self._hash: Optional[int] = None
|
|
122
|
+
|
|
123
|
+
def __hash__(self) -> int:
|
|
124
|
+
"""Cached hash value with optimization."""
|
|
125
|
+
if self._hash is None:
|
|
126
|
+
# Optimize by avoiding str() conversion if possible
|
|
127
|
+
param_str = str(self.execution_parameters)
|
|
128
|
+
self._hash = hash((self.compiled_sql, param_str, self.operation_type, self.parameter_style))
|
|
129
|
+
return self._hash
|
|
130
|
+
|
|
131
|
+
def __eq__(self, other: object) -> bool:
|
|
132
|
+
"""Equality comparison for compiled results."""
|
|
133
|
+
if not isinstance(other, CompiledSQL):
|
|
134
|
+
return False
|
|
135
|
+
return (
|
|
136
|
+
self.compiled_sql == other.compiled_sql
|
|
137
|
+
and self.execution_parameters == other.execution_parameters
|
|
138
|
+
and self.operation_type == other.operation_type
|
|
139
|
+
and self.parameter_style == other.parameter_style
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def __repr__(self) -> str:
|
|
143
|
+
"""String representation for debugging."""
|
|
144
|
+
return (
|
|
145
|
+
f"CompiledSQL(sql={self.compiled_sql!r}, "
|
|
146
|
+
f"params={self.execution_parameters!r}, "
|
|
147
|
+
f"type={self.operation_type!r})"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
152
|
+
class SQLProcessor:
|
|
153
|
+
"""SQL processor with integrated caching and compilation.
|
|
154
|
+
|
|
155
|
+
This is the core compilation engine that processes SQL statements with
|
|
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
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
__slots__ = ("_cache", "_cache_hits", "_cache_misses", "_config", "_max_cache_size", "_parameter_processor")
|
|
173
|
+
|
|
174
|
+
def __init__(self, config: "StatementConfig", max_cache_size: int = 1000) -> None:
|
|
175
|
+
"""Initialize processor with configuration and caching.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
config: Statement configuration with parameter processing settings
|
|
179
|
+
max_cache_size: Maximum number of cached compilation results
|
|
180
|
+
"""
|
|
181
|
+
self._config = config
|
|
182
|
+
self._cache: OrderedDict[str, CompiledSQL] = OrderedDict()
|
|
183
|
+
self._parameter_processor = ParameterProcessor()
|
|
184
|
+
self._max_cache_size = max_cache_size
|
|
185
|
+
self._cache_hits = 0
|
|
186
|
+
self._cache_misses = 0
|
|
187
|
+
|
|
188
|
+
def compile(self, sql: str, parameters: Any = None, is_many: bool = False) -> CompiledSQL:
|
|
189
|
+
"""Compile SQL statement with integrated caching.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
sql: Raw SQL string for compilation
|
|
193
|
+
parameters: Parameter values in any format
|
|
194
|
+
is_many: Whether this is for execute_many operation
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
CompiledSQL with all information for execution
|
|
198
|
+
"""
|
|
199
|
+
if not self._config.enable_caching:
|
|
200
|
+
return self._compile_uncached(sql, parameters, is_many)
|
|
201
|
+
|
|
202
|
+
cache_key = self._make_cache_key(sql, parameters)
|
|
203
|
+
|
|
204
|
+
if cache_key in self._cache:
|
|
205
|
+
# Move to end for LRU behavior
|
|
206
|
+
result = self._cache[cache_key]
|
|
207
|
+
del self._cache[cache_key]
|
|
208
|
+
self._cache[cache_key] = result
|
|
209
|
+
self._cache_hits += 1
|
|
210
|
+
return result
|
|
211
|
+
|
|
212
|
+
self._cache_misses += 1
|
|
213
|
+
result = self._compile_uncached(sql, parameters, is_many)
|
|
214
|
+
|
|
215
|
+
if len(self._cache) >= self._max_cache_size:
|
|
216
|
+
self._cache.popitem(last=False)
|
|
217
|
+
|
|
218
|
+
self._cache[cache_key] = result
|
|
219
|
+
return result
|
|
220
|
+
|
|
221
|
+
def _compile_uncached(self, sql: str, parameters: Any, is_many: bool = False) -> CompiledSQL:
|
|
222
|
+
"""Compile SQL without caching.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
sql: Raw SQL string
|
|
226
|
+
parameters: Parameter values
|
|
227
|
+
is_many: Whether this is for execute_many operation
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
CompiledSQL result
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
# Cache dialect string to avoid repeated conversions
|
|
234
|
+
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
235
|
+
|
|
236
|
+
# Process parameters in single call
|
|
237
|
+
processed_sql: str
|
|
238
|
+
processed_params: Any
|
|
239
|
+
processed_sql, processed_params = self._parameter_processor.process(
|
|
240
|
+
sql=sql,
|
|
241
|
+
parameters=parameters,
|
|
242
|
+
config=self._config.parameter_config,
|
|
243
|
+
dialect=dialect_str,
|
|
244
|
+
is_many=is_many,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Optimize static compilation path
|
|
248
|
+
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
249
|
+
sqlglot_sql = processed_sql
|
|
250
|
+
else:
|
|
251
|
+
sqlglot_sql, _ = self._parameter_processor._get_sqlglot_compatible_sql(
|
|
252
|
+
sql, parameters, self._config.parameter_config, dialect_str
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
final_parameters = processed_params
|
|
256
|
+
ast_was_transformed = False
|
|
257
|
+
expression = None
|
|
258
|
+
operation_type = "EXECUTE"
|
|
259
|
+
|
|
260
|
+
if self._config.enable_parsing:
|
|
261
|
+
try:
|
|
262
|
+
# Use copy=False for performance optimization
|
|
263
|
+
expression = sqlglot.parse_one(sqlglot_sql, dialect=dialect_str)
|
|
264
|
+
operation_type = self._detect_operation_type(expression)
|
|
265
|
+
|
|
266
|
+
# Handle AST transformation if configured
|
|
267
|
+
ast_transformer = self._config.parameter_config.ast_transformer
|
|
268
|
+
if ast_transformer:
|
|
269
|
+
expression, final_parameters = ast_transformer(expression, processed_params)
|
|
270
|
+
ast_was_transformed = True
|
|
271
|
+
|
|
272
|
+
except ParseError:
|
|
273
|
+
expression = None
|
|
274
|
+
operation_type = "EXECUTE"
|
|
275
|
+
|
|
276
|
+
# Optimize final SQL generation path
|
|
277
|
+
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
278
|
+
final_sql, final_params = processed_sql, processed_params
|
|
279
|
+
elif ast_was_transformed and expression is not None:
|
|
280
|
+
final_sql = expression.sql(dialect=dialect_str)
|
|
281
|
+
final_params = final_parameters
|
|
282
|
+
logger.debug("AST was transformed - final SQL: %s, final params: %s", final_sql, final_params)
|
|
283
|
+
|
|
284
|
+
# Apply output transformer if configured
|
|
285
|
+
output_transformer = self._config.output_transformer
|
|
286
|
+
if output_transformer:
|
|
287
|
+
final_sql, final_params = output_transformer(final_sql, final_params)
|
|
288
|
+
else:
|
|
289
|
+
final_sql, final_params = self._apply_final_transformations(
|
|
290
|
+
expression, processed_sql, final_parameters, dialect_str
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return CompiledSQL(
|
|
294
|
+
compiled_sql=final_sql,
|
|
295
|
+
execution_parameters=final_params,
|
|
296
|
+
operation_type=operation_type,
|
|
297
|
+
expression=expression,
|
|
298
|
+
parameter_style=self._config.parameter_config.default_parameter_style.value,
|
|
299
|
+
supports_many=isinstance(final_params, list) and len(final_params) > 0,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
logger.warning("Compilation failed, using fallback: %s", e)
|
|
304
|
+
return CompiledSQL(
|
|
305
|
+
compiled_sql=sql, execution_parameters=parameters, operation_type=_OPERATION_TYPES["UNKNOWN"]
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
def _make_cache_key(self, sql: str, parameters: Any) -> str:
|
|
309
|
+
"""Generate cache key for compilation result.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
sql: SQL string
|
|
313
|
+
parameters: Parameter values
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Cache key string
|
|
317
|
+
"""
|
|
318
|
+
# Optimize key generation by avoiding string conversion overhead
|
|
319
|
+
param_repr = repr(parameters)
|
|
320
|
+
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
321
|
+
param_style = self._config.parameter_config.default_parameter_style.value
|
|
322
|
+
|
|
323
|
+
# Use direct tuple construction for better performance
|
|
324
|
+
hash_data = (
|
|
325
|
+
sql,
|
|
326
|
+
param_repr,
|
|
327
|
+
param_style,
|
|
328
|
+
dialect_str,
|
|
329
|
+
self._config.enable_parsing,
|
|
330
|
+
self._config.enable_transformations,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Optimize hash computation
|
|
334
|
+
hash_str = hashlib.sha256(str(hash_data).encode("utf-8")).hexdigest()[:16]
|
|
335
|
+
return f"sql_{hash_str}"
|
|
336
|
+
|
|
337
|
+
def _detect_operation_type(self, expression: "exp.Expression") -> str:
|
|
338
|
+
"""Detect operation type from AST.
|
|
339
|
+
|
|
340
|
+
Uses SQLGlot AST structure to determine operation type.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
expression: SQLGlot AST expression
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Operation type string
|
|
347
|
+
"""
|
|
348
|
+
# Use isinstance for compatibility with mocks and inheritance
|
|
349
|
+
if isinstance(expression, exp.Select):
|
|
350
|
+
return "SELECT"
|
|
351
|
+
if isinstance(expression, exp.Insert):
|
|
352
|
+
return "INSERT"
|
|
353
|
+
if isinstance(expression, exp.Update):
|
|
354
|
+
return "UPDATE"
|
|
355
|
+
if isinstance(expression, exp.Delete):
|
|
356
|
+
return "DELETE"
|
|
357
|
+
if isinstance(expression, exp.Pragma):
|
|
358
|
+
return "PRAGMA"
|
|
359
|
+
if isinstance(expression, exp.Command):
|
|
360
|
+
return "EXECUTE"
|
|
361
|
+
if isinstance(expression, exp.Copy):
|
|
362
|
+
copy_kind = expression.args.get("kind")
|
|
363
|
+
if copy_kind is True:
|
|
364
|
+
return "COPY_FROM"
|
|
365
|
+
if copy_kind is False:
|
|
366
|
+
return "COPY_TO"
|
|
367
|
+
return "COPY"
|
|
368
|
+
if isinstance(expression, (exp.Create, exp.Drop, exp.Alter)):
|
|
369
|
+
return "DDL"
|
|
370
|
+
return "UNKNOWN"
|
|
371
|
+
|
|
372
|
+
def _apply_final_transformations(
|
|
373
|
+
self, expression: "Optional[exp.Expression]", sql: str, parameters: Any, dialect_str: "Optional[str]"
|
|
374
|
+
) -> "tuple[str, Any]":
|
|
375
|
+
"""Apply final transformations.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
expression: SQLGlot AST expression (if available)
|
|
379
|
+
sql: Compiled SQL string (fallback)
|
|
380
|
+
parameters: Execution parameters
|
|
381
|
+
dialect_str: SQL dialect for AST-to-SQL conversion
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Tuple of (final_sql, final_parameters)
|
|
385
|
+
"""
|
|
386
|
+
output_transformer = self._config.output_transformer
|
|
387
|
+
if output_transformer:
|
|
388
|
+
if expression is not None:
|
|
389
|
+
ast_sql = expression.sql(dialect=dialect_str)
|
|
390
|
+
return output_transformer(ast_sql, parameters)
|
|
391
|
+
return output_transformer(sql, parameters)
|
|
392
|
+
|
|
393
|
+
return sql, parameters
|
|
394
|
+
|
|
395
|
+
def clear_cache(self) -> None:
|
|
396
|
+
"""Clear compilation cache."""
|
|
397
|
+
self._cache.clear()
|
|
398
|
+
self._cache_hits = 0
|
|
399
|
+
self._cache_misses = 0
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def cache_stats(self) -> "dict[str, int]":
|
|
403
|
+
"""Get cache statistics.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Dictionary with cache hit/miss statistics
|
|
407
|
+
"""
|
|
408
|
+
total_requests = self._cache_hits + self._cache_misses
|
|
409
|
+
hit_rate_pct = int((self._cache_hits / total_requests) * 100) if total_requests > 0 else 0
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
"hits": self._cache_hits,
|
|
413
|
+
"misses": self._cache_misses,
|
|
414
|
+
"size": len(self._cache),
|
|
415
|
+
"max_size": self._max_cache_size,
|
|
416
|
+
"hit_rate_percent": hit_rate_pct,
|
|
417
|
+
}
|
|
Binary file
|