sqlspec 0.14.0__py3-none-any.whl → 0.15.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 (158) hide show
  1. sqlspec/__init__.py +50 -25
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +1 -3
  4. sqlspec/_serialization.py +1 -2
  5. sqlspec/_sql.py +256 -120
  6. sqlspec/_typing.py +278 -142
  7. sqlspec/adapters/adbc/__init__.py +4 -3
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/config.py +115 -248
  10. sqlspec/adapters/adbc/driver.py +462 -353
  11. sqlspec/adapters/aiosqlite/__init__.py +18 -3
  12. sqlspec/adapters/aiosqlite/_types.py +13 -0
  13. sqlspec/adapters/aiosqlite/config.py +199 -129
  14. sqlspec/adapters/aiosqlite/driver.py +230 -269
  15. sqlspec/adapters/asyncmy/__init__.py +18 -3
  16. sqlspec/adapters/asyncmy/_types.py +12 -0
  17. sqlspec/adapters/asyncmy/config.py +80 -168
  18. sqlspec/adapters/asyncmy/driver.py +260 -225
  19. sqlspec/adapters/asyncpg/__init__.py +19 -4
  20. sqlspec/adapters/asyncpg/_types.py +17 -0
  21. sqlspec/adapters/asyncpg/config.py +82 -181
  22. sqlspec/adapters/asyncpg/driver.py +285 -383
  23. sqlspec/adapters/bigquery/__init__.py +17 -3
  24. sqlspec/adapters/bigquery/_types.py +12 -0
  25. sqlspec/adapters/bigquery/config.py +191 -258
  26. sqlspec/adapters/bigquery/driver.py +474 -646
  27. sqlspec/adapters/duckdb/__init__.py +14 -3
  28. sqlspec/adapters/duckdb/_types.py +12 -0
  29. sqlspec/adapters/duckdb/config.py +415 -351
  30. sqlspec/adapters/duckdb/driver.py +343 -413
  31. sqlspec/adapters/oracledb/__init__.py +19 -5
  32. sqlspec/adapters/oracledb/_types.py +14 -0
  33. sqlspec/adapters/oracledb/config.py +123 -379
  34. sqlspec/adapters/oracledb/driver.py +507 -560
  35. sqlspec/adapters/psqlpy/__init__.py +13 -3
  36. sqlspec/adapters/psqlpy/_types.py +11 -0
  37. sqlspec/adapters/psqlpy/config.py +93 -254
  38. sqlspec/adapters/psqlpy/driver.py +505 -234
  39. sqlspec/adapters/psycopg/__init__.py +19 -5
  40. sqlspec/adapters/psycopg/_types.py +17 -0
  41. sqlspec/adapters/psycopg/config.py +143 -403
  42. sqlspec/adapters/psycopg/driver.py +706 -872
  43. sqlspec/adapters/sqlite/__init__.py +14 -3
  44. sqlspec/adapters/sqlite/_types.py +11 -0
  45. sqlspec/adapters/sqlite/config.py +202 -118
  46. sqlspec/adapters/sqlite/driver.py +264 -303
  47. sqlspec/base.py +105 -9
  48. sqlspec/{statement/builder → builder}/__init__.py +12 -14
  49. sqlspec/{statement/builder → builder}/_base.py +120 -55
  50. sqlspec/{statement/builder → builder}/_column.py +17 -6
  51. sqlspec/{statement/builder → builder}/_ddl.py +46 -79
  52. sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
  53. sqlspec/{statement/builder → builder}/_delete.py +6 -25
  54. sqlspec/{statement/builder → builder}/_insert.py +6 -64
  55. sqlspec/builder/_merge.py +56 -0
  56. sqlspec/{statement/builder → builder}/_parsing_utils.py +3 -10
  57. sqlspec/{statement/builder → builder}/_select.py +11 -56
  58. sqlspec/{statement/builder → builder}/_update.py +12 -18
  59. sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
  60. sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
  61. sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +22 -16
  62. sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
  63. sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +3 -5
  64. sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
  65. sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
  66. sqlspec/{statement/builder → builder}/mixins/_select_operations.py +21 -36
  67. sqlspec/{statement/builder → builder}/mixins/_update_operations.py +3 -14
  68. sqlspec/{statement/builder → builder}/mixins/_where_clause.py +52 -79
  69. sqlspec/cli.py +4 -5
  70. sqlspec/config.py +180 -133
  71. sqlspec/core/__init__.py +63 -0
  72. sqlspec/core/cache.py +873 -0
  73. sqlspec/core/compiler.py +396 -0
  74. sqlspec/core/filters.py +828 -0
  75. sqlspec/core/hashing.py +310 -0
  76. sqlspec/core/parameters.py +1209 -0
  77. sqlspec/core/result.py +664 -0
  78. sqlspec/{statement → core}/splitter.py +321 -191
  79. sqlspec/core/statement.py +651 -0
  80. sqlspec/driver/__init__.py +7 -10
  81. sqlspec/driver/_async.py +387 -176
  82. sqlspec/driver/_common.py +527 -289
  83. sqlspec/driver/_sync.py +390 -172
  84. sqlspec/driver/mixins/__init__.py +2 -19
  85. sqlspec/driver/mixins/_result_tools.py +168 -0
  86. sqlspec/driver/mixins/_sql_translator.py +6 -3
  87. sqlspec/exceptions.py +5 -252
  88. sqlspec/extensions/aiosql/adapter.py +93 -96
  89. sqlspec/extensions/litestar/config.py +0 -1
  90. sqlspec/extensions/litestar/handlers.py +15 -26
  91. sqlspec/extensions/litestar/plugin.py +16 -14
  92. sqlspec/extensions/litestar/providers.py +17 -52
  93. sqlspec/loader.py +424 -105
  94. sqlspec/migrations/__init__.py +12 -0
  95. sqlspec/migrations/base.py +92 -68
  96. sqlspec/migrations/commands.py +24 -106
  97. sqlspec/migrations/loaders.py +402 -0
  98. sqlspec/migrations/runner.py +49 -51
  99. sqlspec/migrations/tracker.py +31 -44
  100. sqlspec/migrations/utils.py +64 -24
  101. sqlspec/protocols.py +7 -183
  102. sqlspec/storage/__init__.py +1 -1
  103. sqlspec/storage/backends/base.py +37 -40
  104. sqlspec/storage/backends/fsspec.py +136 -112
  105. sqlspec/storage/backends/obstore.py +138 -160
  106. sqlspec/storage/capabilities.py +5 -4
  107. sqlspec/storage/registry.py +57 -106
  108. sqlspec/typing.py +136 -115
  109. sqlspec/utils/__init__.py +2 -3
  110. sqlspec/utils/correlation.py +0 -3
  111. sqlspec/utils/deprecation.py +6 -6
  112. sqlspec/utils/fixtures.py +6 -6
  113. sqlspec/utils/logging.py +0 -2
  114. sqlspec/utils/module_loader.py +7 -12
  115. sqlspec/utils/singleton.py +0 -1
  116. sqlspec/utils/sync_tools.py +16 -37
  117. sqlspec/utils/text.py +12 -51
  118. sqlspec/utils/type_guards.py +443 -232
  119. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/METADATA +7 -2
  120. sqlspec-0.15.0.dist-info/RECORD +134 -0
  121. sqlspec-0.15.0.dist-info/entry_points.txt +2 -0
  122. sqlspec/driver/connection.py +0 -207
  123. sqlspec/driver/mixins/_cache.py +0 -114
  124. sqlspec/driver/mixins/_csv_writer.py +0 -91
  125. sqlspec/driver/mixins/_pipeline.py +0 -508
  126. sqlspec/driver/mixins/_query_tools.py +0 -796
  127. sqlspec/driver/mixins/_result_utils.py +0 -138
  128. sqlspec/driver/mixins/_storage.py +0 -912
  129. sqlspec/driver/mixins/_type_coercion.py +0 -128
  130. sqlspec/driver/parameters.py +0 -138
  131. sqlspec/statement/__init__.py +0 -21
  132. sqlspec/statement/builder/_merge.py +0 -95
  133. sqlspec/statement/cache.py +0 -50
  134. sqlspec/statement/filters.py +0 -625
  135. sqlspec/statement/parameters.py +0 -996
  136. sqlspec/statement/pipelines/__init__.py +0 -210
  137. sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
  138. sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
  139. sqlspec/statement/pipelines/context.py +0 -115
  140. sqlspec/statement/pipelines/transformers/__init__.py +0 -7
  141. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
  142. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
  143. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
  144. sqlspec/statement/pipelines/validators/__init__.py +0 -23
  145. sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
  146. sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
  147. sqlspec/statement/pipelines/validators/_performance.py +0 -714
  148. sqlspec/statement/pipelines/validators/_security.py +0 -967
  149. sqlspec/statement/result.py +0 -435
  150. sqlspec/statement/sql.py +0 -1774
  151. sqlspec/utils/cached_property.py +0 -25
  152. sqlspec/utils/statement_hashing.py +0 -203
  153. sqlspec-0.14.0.dist-info/RECORD +0 -143
  154. sqlspec-0.14.0.dist-info/entry_points.txt +0 -2
  155. /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
  156. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/WHEEL +0 -0
  157. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/LICENSE +0 -0
  158. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,396 @@
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."""
125
+ if self._hash is None:
126
+ hash_data = (self.compiled_sql, str(self.execution_parameters), self.operation_type, self.parameter_style)
127
+ self._hash = hash(hash_data)
128
+ return self._hash
129
+
130
+ def __eq__(self, other: object) -> bool:
131
+ """Equality comparison for compiled results."""
132
+ if not isinstance(other, CompiledSQL):
133
+ return False
134
+ return (
135
+ self.compiled_sql == other.compiled_sql
136
+ and self.execution_parameters == other.execution_parameters
137
+ and self.operation_type == other.operation_type
138
+ and self.parameter_style == other.parameter_style
139
+ )
140
+
141
+ def __repr__(self) -> str:
142
+ """String representation for debugging."""
143
+ return (
144
+ f"CompiledSQL(sql={self.compiled_sql!r}, "
145
+ f"params={self.execution_parameters!r}, "
146
+ f"type={self.operation_type!r})"
147
+ )
148
+
149
+
150
+ @mypyc_attr(allow_interpreted_subclasses=True)
151
+ class SQLProcessor:
152
+ """SQL processor with integrated caching and compilation.
153
+
154
+ This is the core compilation engine that processes SQL statements with
155
+ integrated parameter processing and caching.
156
+
157
+ Processing Flow:
158
+ 1. Parameter detection and normalization (if needed)
159
+ 2. Single SQLGlot parse
160
+ 3. AST-based operation type detection
161
+ 4. Parameter conversion (if needed)
162
+ 5. Final SQL generation with execution parameters
163
+
164
+ Features:
165
+ - LRU cache with O(1) operations
166
+ - Integrated parameter processing
167
+ - Cached compilation results
168
+ - Complete StatementConfig support
169
+ """
170
+
171
+ __slots__ = ("_cache", "_cache_hits", "_cache_misses", "_config", "_max_cache_size", "_parameter_processor")
172
+
173
+ def __init__(self, config: "StatementConfig", max_cache_size: int = 1000) -> None:
174
+ """Initialize processor with configuration and caching.
175
+
176
+ Args:
177
+ config: Statement configuration with parameter processing settings
178
+ max_cache_size: Maximum number of cached compilation results
179
+ """
180
+ self._config = config
181
+ self._cache: OrderedDict[str, CompiledSQL] = OrderedDict()
182
+ self._parameter_processor = ParameterProcessor()
183
+ self._max_cache_size = max_cache_size
184
+ self._cache_hits = 0
185
+ self._cache_misses = 0
186
+
187
+ def compile(self, sql: str, parameters: Any = None, is_many: bool = False) -> CompiledSQL:
188
+ """Compile SQL statement with integrated caching.
189
+
190
+ Args:
191
+ sql: Raw SQL string for compilation
192
+ parameters: Parameter values in any format
193
+ is_many: Whether this is for execute_many operation
194
+
195
+ Returns:
196
+ CompiledSQL with all information for execution
197
+ """
198
+ if not self._config.enable_caching:
199
+ return self._compile_uncached(sql, parameters, is_many)
200
+
201
+ cache_key = self._make_cache_key(sql, parameters)
202
+
203
+ if cache_key in self._cache:
204
+ # Move to end for LRU behavior
205
+ result = self._cache[cache_key]
206
+ del self._cache[cache_key]
207
+ self._cache[cache_key] = result
208
+ self._cache_hits += 1
209
+ return result
210
+
211
+ self._cache_misses += 1
212
+ result = self._compile_uncached(sql, parameters, is_many)
213
+
214
+ if len(self._cache) >= self._max_cache_size:
215
+ self._cache.popitem(last=False)
216
+
217
+ self._cache[cache_key] = result
218
+ return result
219
+
220
+ def _compile_uncached(self, sql: str, parameters: Any, is_many: bool = False) -> CompiledSQL:
221
+ """Compile SQL without caching.
222
+
223
+ Args:
224
+ sql: Raw SQL string
225
+ parameters: Parameter values
226
+ is_many: Whether this is for execute_many operation
227
+
228
+ Returns:
229
+ CompiledSQL result
230
+ """
231
+ try:
232
+ dialect_str = str(self._config.dialect) if self._config.dialect else None
233
+ processed_sql, processed_params_tuple = self._parameter_processor.process(
234
+ sql=sql,
235
+ parameters=parameters,
236
+ config=self._config.parameter_config,
237
+ dialect=dialect_str,
238
+ is_many=is_many,
239
+ )
240
+ processed_params: Any = processed_params_tuple
241
+
242
+ if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
243
+ sqlglot_sql = processed_sql
244
+ else:
245
+ sqlglot_sql, _ = self._parameter_processor._get_sqlglot_compatible_sql(
246
+ sql, parameters, self._config.parameter_config, dialect_str
247
+ )
248
+
249
+ final_parameters: Any = processed_params
250
+ ast_was_transformed = False
251
+
252
+ if self._config.enable_parsing:
253
+ try:
254
+ expression = sqlglot.parse_one(sqlglot_sql, dialect=dialect_str)
255
+ operation_type = self._detect_operation_type(expression)
256
+
257
+ if self._config.parameter_config.ast_transformer:
258
+ expression, final_parameters = self._config.parameter_config.ast_transformer(
259
+ expression, processed_params
260
+ )
261
+ ast_was_transformed = True
262
+
263
+ except ParseError:
264
+ expression = None
265
+ operation_type = "EXECUTE"
266
+ else:
267
+ expression = None
268
+ operation_type = "EXECUTE"
269
+
270
+ if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
271
+ final_sql, final_params = processed_sql, processed_params
272
+ elif ast_was_transformed and expression is not None:
273
+ final_sql = expression.sql(dialect=dialect_str)
274
+ final_params = final_parameters
275
+ logger.debug("AST was transformed - final SQL: %s, final params: %s", final_sql, final_params)
276
+ if self._config.output_transformer:
277
+ final_sql, final_params = self._config.output_transformer(final_sql, final_params)
278
+ else:
279
+ final_sql, final_params = self._apply_final_transformations(
280
+ expression, processed_sql, final_parameters, dialect_str
281
+ )
282
+
283
+ return CompiledSQL(
284
+ compiled_sql=final_sql,
285
+ execution_parameters=final_params,
286
+ operation_type=operation_type,
287
+ expression=expression,
288
+ parameter_style=self._config.parameter_config.default_parameter_style.value,
289
+ supports_many=isinstance(final_params, list) and len(final_params) > 0,
290
+ )
291
+
292
+ except Exception as e:
293
+ logger.warning("Compilation failed, using fallback: %s", e)
294
+ return CompiledSQL(
295
+ compiled_sql=sql, execution_parameters=parameters, operation_type=_OPERATION_TYPES["UNKNOWN"]
296
+ )
297
+
298
+ def _make_cache_key(self, sql: str, parameters: Any) -> str:
299
+ """Generate cache key for compilation result.
300
+
301
+ Args:
302
+ sql: SQL string
303
+ parameters: Parameter values
304
+
305
+ Returns:
306
+ Cache key string
307
+ """
308
+ hash_data = (
309
+ sql,
310
+ repr(parameters),
311
+ self._config.parameter_config.default_parameter_style.value,
312
+ str(self._config.dialect),
313
+ self._config.enable_parsing,
314
+ self._config.enable_transformations,
315
+ )
316
+ hash_str = hashlib.sha256(str(hash_data).encode()).hexdigest()[:16]
317
+ return f"sql_{hash_str}"
318
+
319
+ def _detect_operation_type(self, expression: "exp.Expression") -> str:
320
+ """Detect operation type from AST.
321
+
322
+ Uses SQLGlot AST structure to determine operation type.
323
+
324
+ Args:
325
+ expression: SQLGlot AST expression
326
+
327
+ Returns:
328
+ Operation type string
329
+ """
330
+ if isinstance(expression, exp.Select):
331
+ return _OPERATION_TYPES["SELECT"]
332
+ if isinstance(expression, exp.Insert):
333
+ return _OPERATION_TYPES["INSERT"]
334
+ if isinstance(expression, exp.Update):
335
+ return _OPERATION_TYPES["UPDATE"]
336
+ if isinstance(expression, exp.Delete):
337
+ return _OPERATION_TYPES["DELETE"]
338
+ if isinstance(expression, (exp.Create, exp.Drop, exp.Alter)):
339
+ return _OPERATION_TYPES["DDL"]
340
+ if isinstance(expression, exp.Copy):
341
+ if expression.args["kind"] is True:
342
+ return _OPERATION_TYPES["COPY_FROM"]
343
+ if expression.args["kind"] is False:
344
+ return _OPERATION_TYPES["COPY_TO"]
345
+ return _OPERATION_TYPES["COPY"]
346
+ if isinstance(expression, exp.Pragma):
347
+ return _OPERATION_TYPES["PRAGMA"]
348
+ if isinstance(expression, exp.Command):
349
+ return _OPERATION_TYPES["EXECUTE"]
350
+ return _OPERATION_TYPES["UNKNOWN"]
351
+
352
+ def _apply_final_transformations(
353
+ self, expression: "Optional[exp.Expression]", sql: str, parameters: Any, dialect_str: "Optional[str]"
354
+ ) -> "tuple[str, Any]":
355
+ """Apply final transformations.
356
+
357
+ Args:
358
+ expression: SQLGlot AST expression (if available)
359
+ sql: Compiled SQL string (fallback)
360
+ parameters: Execution parameters
361
+ dialect_str: SQL dialect for AST-to-SQL conversion
362
+
363
+ Returns:
364
+ Tuple of (final_sql, final_parameters)
365
+ """
366
+ if self._config.output_transformer:
367
+ if expression is not None:
368
+ ast_sql = expression.sql(dialect=dialect_str)
369
+ return self._config.output_transformer(ast_sql, parameters)
370
+ return self._config.output_transformer(sql, parameters)
371
+
372
+ return sql, parameters
373
+
374
+ def clear_cache(self) -> None:
375
+ """Clear compilation cache."""
376
+ self._cache.clear()
377
+ self._cache_hits = 0
378
+ self._cache_misses = 0
379
+
380
+ @property
381
+ def cache_stats(self) -> "dict[str, int]":
382
+ """Get cache statistics.
383
+
384
+ Returns:
385
+ Dictionary with cache hit/miss statistics
386
+ """
387
+ total_requests = self._cache_hits + self._cache_misses
388
+ hit_rate_pct = int((self._cache_hits / total_requests) * 100) if total_requests > 0 else 0
389
+
390
+ return {
391
+ "hits": self._cache_hits,
392
+ "misses": self._cache_misses,
393
+ "size": len(self._cache),
394
+ "max_size": self._max_cache_size,
395
+ "hit_rate_percent": hit_rate_pct,
396
+ }