sqlspec 0.26.0__py3-none-any.whl → 0.28.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 (212) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +155 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +880 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +74 -2
  9. sqlspec/adapters/adbc/driver.py +226 -58
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +585 -0
  44. sqlspec/adapters/bigquery/config.py +36 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +489 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +563 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +225 -44
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1628 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +475 -86
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
  77. sqlspec/adapters/psqlpy/driver.py +108 -41
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +962 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +91 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +582 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +331 -62
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +55 -47
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +234 -47
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +183 -160
  129. sqlspec/driver/_common.py +197 -109
  130. sqlspec/driver/_sync.py +189 -161
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +69 -61
  142. sqlspec/extensions/fastapi/__init__.py +21 -0
  143. sqlspec/extensions/fastapi/extension.py +331 -0
  144. sqlspec/extensions/fastapi/providers.py +543 -0
  145. sqlspec/extensions/flask/__init__.py +36 -0
  146. sqlspec/extensions/flask/_state.py +71 -0
  147. sqlspec/extensions/flask/_utils.py +40 -0
  148. sqlspec/extensions/flask/extension.py +389 -0
  149. sqlspec/extensions/litestar/__init__.py +21 -4
  150. sqlspec/extensions/litestar/cli.py +54 -10
  151. sqlspec/extensions/litestar/config.py +56 -266
  152. sqlspec/extensions/litestar/handlers.py +46 -17
  153. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  154. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  155. sqlspec/extensions/litestar/plugin.py +349 -224
  156. sqlspec/extensions/litestar/providers.py +25 -25
  157. sqlspec/extensions/litestar/store.py +265 -0
  158. sqlspec/extensions/starlette/__init__.py +10 -0
  159. sqlspec/extensions/starlette/_state.py +25 -0
  160. sqlspec/extensions/starlette/_utils.py +52 -0
  161. sqlspec/extensions/starlette/extension.py +254 -0
  162. sqlspec/extensions/starlette/middleware.py +154 -0
  163. sqlspec/loader.py +30 -49
  164. sqlspec/migrations/base.py +200 -76
  165. sqlspec/migrations/commands.py +591 -62
  166. sqlspec/migrations/context.py +6 -9
  167. sqlspec/migrations/fix.py +199 -0
  168. sqlspec/migrations/loaders.py +47 -19
  169. sqlspec/migrations/runner.py +241 -75
  170. sqlspec/migrations/tracker.py +237 -21
  171. sqlspec/migrations/utils.py +51 -3
  172. sqlspec/migrations/validation.py +177 -0
  173. sqlspec/protocols.py +106 -36
  174. sqlspec/storage/_utils.py +85 -0
  175. sqlspec/storage/backends/fsspec.py +133 -107
  176. sqlspec/storage/backends/local.py +78 -51
  177. sqlspec/storage/backends/obstore.py +276 -168
  178. sqlspec/storage/registry.py +75 -39
  179. sqlspec/typing.py +30 -84
  180. sqlspec/utils/__init__.py +25 -4
  181. sqlspec/utils/arrow_helpers.py +81 -0
  182. sqlspec/utils/config_resolver.py +6 -6
  183. sqlspec/utils/correlation.py +4 -5
  184. sqlspec/utils/data_transformation.py +3 -2
  185. sqlspec/utils/deprecation.py +9 -8
  186. sqlspec/utils/fixtures.py +4 -4
  187. sqlspec/utils/logging.py +46 -6
  188. sqlspec/utils/module_loader.py +205 -5
  189. sqlspec/utils/portal.py +311 -0
  190. sqlspec/utils/schema.py +288 -0
  191. sqlspec/utils/serializers.py +113 -4
  192. sqlspec/utils/sync_tools.py +36 -22
  193. sqlspec/utils/text.py +1 -2
  194. sqlspec/utils/type_guards.py +136 -20
  195. sqlspec/utils/version.py +433 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
  197. sqlspec-0.28.0.dist-info/RECORD +221 -0
  198. sqlspec/builder/mixins/__init__.py +0 -55
  199. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  200. sqlspec/builder/mixins/_delete_operations.py +0 -50
  201. sqlspec/builder/mixins/_insert_operations.py +0 -282
  202. sqlspec/builder/mixins/_merge_operations.py +0 -698
  203. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  204. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  205. sqlspec/builder/mixins/_select_operations.py +0 -930
  206. sqlspec/builder/mixins/_update_operations.py +0 -199
  207. sqlspec/builder/mixins/_where_clause.py +0 -1298
  208. sqlspec-0.26.0.dist-info/RECORD +0 -157
  209. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  210. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
  211. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
  212. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/core/__init__.py CHANGED
@@ -1,9 +1,9 @@
1
- """SQLSpec Core Module - High-Performance SQL Processing System.
1
+ """SQLSpec Core Module - SQL Processing System.
2
2
 
3
3
  This module provides the core SQL processing infrastructure for SQLSpec, implementing
4
4
  a complete pipeline for SQL statement compilation, parameter processing, caching,
5
- and result management. All components are optimized for MyPyC compilation and
6
- designed for maximum performance with minimal overhead.
5
+ and result management. All components are optimized for MyPyC compilation to
6
+ reduce overhead.
7
7
 
8
8
  Architecture Overview:
9
9
  The core module implements a single-pass processing pipeline where SQL statements
@@ -90,7 +90,7 @@ Example Usage:
90
90
  """
91
91
 
92
92
  from sqlspec.core import filters
93
- from sqlspec.core.cache import CacheConfig, CacheStats, MultiLevelCache, UnifiedCache, get_cache
93
+ from sqlspec.core.cache import CacheConfig, CacheStats, MultiLevelCache, UnifiedCache, get_cache, get_cache_config
94
94
  from sqlspec.core.compiler import OperationType, SQLProcessor
95
95
  from sqlspec.core.filters import StatementFilter
96
96
  from sqlspec.core.hashing import (
@@ -131,6 +131,7 @@ __all__ = (
131
131
  "UnifiedCache",
132
132
  "filters",
133
133
  "get_cache",
134
+ "get_cache_config",
134
135
  "hash_expression",
135
136
  "hash_expression_node",
136
137
  "hash_optimized_expression",
sqlspec/core/cache.py CHANGED
@@ -14,7 +14,7 @@ Components:
14
14
  import threading
15
15
  import time
16
16
  from dataclasses import dataclass
17
- from typing import TYPE_CHECKING, Any, Final, Optional, Union
17
+ from typing import TYPE_CHECKING, Any, Final, Optional
18
18
 
19
19
  from mypy_extensions import mypyc_attr
20
20
  from typing_extensions import TypeVar
@@ -173,8 +173,8 @@ class CacheNode:
173
173
  """
174
174
  self.key = key
175
175
  self.value = value
176
- self.prev: Optional[CacheNode] = None
177
- self.next: Optional[CacheNode] = None
176
+ self.prev: CacheNode | None = None
177
+ self.next: CacheNode | None = None
178
178
  self.timestamp = time.time()
179
179
  self.access_count = 1
180
180
 
@@ -190,7 +190,7 @@ class UnifiedCache:
190
190
 
191
191
  __slots__ = UNIFIED_CACHE_SLOTS
192
192
 
193
- def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: Optional[int] = DEFAULT_TTL_SECONDS) -> None:
193
+ def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: int | None = DEFAULT_TTL_SECONDS) -> None:
194
194
  """Initialize unified cache.
195
195
 
196
196
  Args:
@@ -208,7 +208,7 @@ class UnifiedCache:
208
208
  self._head.next = self._tail
209
209
  self._tail.prev = self._head
210
210
 
211
- def get(self, key: CacheKey) -> Optional[Any]:
211
+ def get(self, key: CacheKey) -> Any | None:
212
212
  """Get value from cache.
213
213
 
214
214
  Args:
@@ -275,7 +275,7 @@ class UnifiedCache:
275
275
  True if key was found and deleted, False otherwise
276
276
  """
277
277
  with self._lock:
278
- node: Optional[CacheNode] = self._cache.get(key)
278
+ node: CacheNode | None = self._cache.get(key)
279
279
  if node is None:
280
280
  return False
281
281
 
@@ -306,7 +306,7 @@ class UnifiedCache:
306
306
  def _add_to_head(self, node: CacheNode) -> None:
307
307
  """Add node to head of list."""
308
308
  node.prev = self._head
309
- head_next: Optional[CacheNode] = self._head.next
309
+ head_next: CacheNode | None = self._head.next
310
310
  node.next = head_next
311
311
  if head_next is not None:
312
312
  head_next.prev = node
@@ -314,8 +314,8 @@ class UnifiedCache:
314
314
 
315
315
  def _remove_node(self, node: CacheNode) -> None:
316
316
  """Remove node from linked list."""
317
- node_prev: Optional[CacheNode] = node.prev
318
- node_next: Optional[CacheNode] = node.next
317
+ node_prev: CacheNode | None = node.prev
318
+ node_next: CacheNode | None = node.next
319
319
  if node_prev is not None:
320
320
  node_prev.next = node_next
321
321
  if node_next is not None:
@@ -341,7 +341,7 @@ class UnifiedCache:
341
341
  return not (ttl is not None and time.time() - node.timestamp > ttl)
342
342
 
343
343
 
344
- _default_cache: Optional[UnifiedCache] = None
344
+ _default_cache: UnifiedCache | None = None
345
345
  _cache_lock = threading.Lock()
346
346
 
347
347
 
@@ -381,7 +381,7 @@ def get_cache_statistics() -> dict[str, CacheStats]:
381
381
  return stats
382
382
 
383
383
 
384
- _global_cache_config: "Optional[CacheConfig]" = None
384
+ _global_cache_config: "CacheConfig | None" = None
385
385
 
386
386
 
387
387
  @mypyc_attr(allow_interpreted_subclasses=False)
@@ -558,7 +558,7 @@ class CachedStatement:
558
558
  """
559
559
 
560
560
  compiled_sql: str
561
- parameters: Optional[Union[tuple[Any, ...], dict[str, Any]]] # None allowed for static script compilation
561
+ parameters: tuple[Any, ...] | dict[str, Any] | None # None allowed for static script compilation
562
562
  expression: Optional["exp.Expression"]
563
563
 
564
564
  def get_parameters_view(self) -> "ParametersView":
@@ -572,7 +572,7 @@ class CachedStatement:
572
572
  return ParametersView(list(self.parameters), {})
573
573
 
574
574
 
575
- def create_cache_key(level: str, key: str, dialect: Optional[str] = None) -> str:
575
+ def create_cache_key(level: str, key: str, dialect: str | None = None) -> str:
576
576
  """Create optimized cache key using string concatenation.
577
577
 
578
578
  Args:
@@ -592,7 +592,7 @@ class MultiLevelCache:
592
592
 
593
593
  __slots__ = ("_cache",)
594
594
 
595
- def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: Optional[int] = DEFAULT_TTL_SECONDS) -> None:
595
+ def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: int | None = DEFAULT_TTL_SECONDS) -> None:
596
596
  """Initialize multi-level cache.
597
597
 
598
598
  Args:
@@ -601,7 +601,7 @@ class MultiLevelCache:
601
601
  """
602
602
  self._cache = UnifiedCache(max_size, ttl_seconds)
603
603
 
604
- def get(self, level: str, key: str, dialect: Optional[str] = None) -> Optional[Any]:
604
+ def get(self, level: str, key: str, dialect: str | None = None) -> Any | None:
605
605
  """Get value from cache with level and dialect namespace.
606
606
 
607
607
  Args:
@@ -616,7 +616,7 @@ class MultiLevelCache:
616
616
  cache_key = CacheKey((full_key,))
617
617
  return self._cache.get(cache_key)
618
618
 
619
- def put(self, level: str, key: str, value: Any, dialect: Optional[str] = None) -> None:
619
+ def put(self, level: str, key: str, value: Any, dialect: str | None = None) -> None:
620
620
  """Put value in cache with level and dialect namespace.
621
621
 
622
622
  Args:
@@ -629,7 +629,7 @@ class MultiLevelCache:
629
629
  cache_key = CacheKey((full_key,))
630
630
  self._cache.put(cache_key, value)
631
631
 
632
- def delete(self, level: str, key: str, dialect: Optional[str] = None) -> bool:
632
+ def delete(self, level: str, key: str, dialect: str | None = None) -> bool:
633
633
  """Delete entry from cache.
634
634
 
635
635
  Args:
@@ -653,7 +653,7 @@ class MultiLevelCache:
653
653
  return self._cache.get_stats()
654
654
 
655
655
 
656
- _multi_level_cache: Optional[MultiLevelCache] = None
656
+ _multi_level_cache: MultiLevelCache | None = None
657
657
 
658
658
 
659
659
  def get_cache() -> MultiLevelCache:
sqlspec/core/compiler.py CHANGED
@@ -8,16 +8,15 @@ Components:
8
8
 
9
9
  import hashlib
10
10
  from collections import OrderedDict
11
- from typing import TYPE_CHECKING, Any, Optional
11
+ from typing import TYPE_CHECKING, Any, Literal, Optional
12
12
 
13
13
  import sqlglot
14
14
  from mypy_extensions import mypyc_attr
15
15
  from sqlglot import expressions as exp
16
16
  from sqlglot.errors import ParseError
17
- from typing_extensions import Literal
18
17
 
18
+ import sqlspec.exceptions
19
19
  from sqlspec.core.parameters import ParameterProcessor
20
- from sqlspec.exceptions import SQLSpecError
21
20
  from sqlspec.utils.logging import get_logger
22
21
 
23
22
  if TYPE_CHECKING:
@@ -85,7 +84,7 @@ class CompiledSQL:
85
84
  execution_parameters: Any,
86
85
  operation_type: "OperationType",
87
86
  expression: Optional["exp.Expression"] = None,
88
- parameter_style: Optional[str] = None,
87
+ parameter_style: str | None = None,
89
88
  supports_many: bool = False,
90
89
  parameter_casts: Optional["dict[int, str]"] = None,
91
90
  ) -> None:
@@ -107,7 +106,7 @@ class CompiledSQL:
107
106
  self.parameter_style = parameter_style
108
107
  self.supports_many = supports_many
109
108
  self.parameter_casts = parameter_casts or {}
110
- self._hash: Optional[int] = None
109
+ self._hash: int | None = None
111
110
 
112
111
  def __hash__(self) -> int:
113
112
  """Cached hash value."""
@@ -274,8 +273,7 @@ class SQLProcessor:
274
273
  parameter_casts=parameter_casts,
275
274
  )
276
275
 
277
- except SQLSpecError:
278
- # Re-raise SQLSpecError (validation errors, parameter mismatches) - these should fail hard
276
+ except sqlspec.exceptions.SQLSpecError:
279
277
  raise
280
278
  except Exception as e:
281
279
  logger.warning("Compilation failed, using fallback: %s", e)
@@ -382,7 +380,7 @@ class SQLProcessor:
382
380
  return cast_positions
383
381
 
384
382
  def _apply_final_transformations(
385
- self, expression: "Optional[exp.Expression]", sql: str, parameters: Any, dialect_str: "Optional[str]"
383
+ self, expression: "exp.Expression | None", sql: str, parameters: Any, dialect_str: "str | None"
386
384
  ) -> "tuple[str, Any]":
387
385
  """Apply final transformations.
388
386
 
sqlspec/core/filters.py CHANGED
@@ -23,11 +23,11 @@ from abc import ABC, abstractmethod
23
23
  from collections import abc
24
24
  from collections.abc import Sequence
25
25
  from datetime import datetime
26
- from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, Union
26
+ from typing import TYPE_CHECKING, Any, Generic, Literal, TypeAlias
27
27
 
28
28
  import sqlglot
29
29
  from sqlglot import exp
30
- from typing_extensions import TypeAlias, TypeVar
30
+ from typing_extensions import TypeVar
31
31
 
32
32
  if TYPE_CHECKING:
33
33
  from sqlglot.expressions import Condition
@@ -125,7 +125,7 @@ class BeforeAfterFilter(StatementFilter):
125
125
 
126
126
  __slots__ = ("_after", "_before", "_field_name")
127
127
 
128
- def __init__(self, field_name: str, before: Optional[datetime] = None, after: Optional[datetime] = None) -> None:
128
+ def __init__(self, field_name: str, before: datetime | None = None, after: datetime | None = None) -> None:
129
129
  self._field_name = field_name
130
130
  self._before = before
131
131
  self._after = after
@@ -135,11 +135,11 @@ class BeforeAfterFilter(StatementFilter):
135
135
  return self._field_name
136
136
 
137
137
  @property
138
- def before(self) -> Optional[datetime]:
138
+ def before(self) -> datetime | None:
139
139
  return self._before
140
140
 
141
141
  @property
142
- def after(self) -> Optional[datetime]:
142
+ def after(self) -> datetime | None:
143
143
  return self._after
144
144
 
145
145
  def get_param_names(self) -> list[str]:
@@ -206,7 +206,7 @@ class OnBeforeAfterFilter(StatementFilter):
206
206
  __slots__ = ("_field_name", "_on_or_after", "_on_or_before")
207
207
 
208
208
  def __init__(
209
- self, field_name: str, on_or_before: Optional[datetime] = None, on_or_after: Optional[datetime] = None
209
+ self, field_name: str, on_or_before: datetime | None = None, on_or_after: datetime | None = None
210
210
  ) -> None:
211
211
  self._field_name = field_name
212
212
  self._on_or_before = on_or_before
@@ -217,11 +217,11 @@ class OnBeforeAfterFilter(StatementFilter):
217
217
  return self._field_name
218
218
 
219
219
  @property
220
- def on_or_before(self) -> Optional[datetime]:
220
+ def on_or_before(self) -> datetime | None:
221
221
  return self._on_or_before
222
222
 
223
223
  @property
224
- def on_or_after(self) -> Optional[datetime]:
224
+ def on_or_after(self) -> datetime | None:
225
225
  return self._on_or_after
226
226
 
227
227
  def get_param_names(self) -> list[str]:
@@ -298,7 +298,7 @@ class InCollectionFilter(InAnyFilter[T]):
298
298
 
299
299
  __slots__ = ("_field_name", "_values")
300
300
 
301
- def __init__(self, field_name: str, values: Optional[abc.Collection[T]] = None) -> None:
301
+ def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
302
302
  self._field_name = field_name
303
303
  self._values = values
304
304
 
@@ -307,7 +307,7 @@ class InCollectionFilter(InAnyFilter[T]):
307
307
  return self._field_name
308
308
 
309
309
  @property
310
- def values(self) -> Optional[abc.Collection[T]]:
310
+ def values(self) -> abc.Collection[T] | None:
311
311
  return self._values
312
312
 
313
313
  def get_param_names(self) -> list[str]:
@@ -340,7 +340,7 @@ class InCollectionFilter(InAnyFilter[T]):
340
340
 
341
341
  result = statement.where(exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
342
342
 
343
- for resolved_name, value in zip(resolved_names, self.values):
343
+ for resolved_name, value in zip(resolved_names, self.values, strict=False):
344
344
  result = result.add_named_parameter(resolved_name, value)
345
345
  return result
346
346
 
@@ -358,7 +358,7 @@ class NotInCollectionFilter(InAnyFilter[T]):
358
358
 
359
359
  __slots__ = ("_field_name", "_values")
360
360
 
361
- def __init__(self, field_name: str, values: Optional[abc.Collection[T]] = None) -> None:
361
+ def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
362
362
  self._field_name = field_name
363
363
  self._values = values
364
364
 
@@ -367,7 +367,7 @@ class NotInCollectionFilter(InAnyFilter[T]):
367
367
  return self._field_name
368
368
 
369
369
  @property
370
- def values(self) -> Optional[abc.Collection[T]]:
370
+ def values(self) -> abc.Collection[T] | None:
371
371
  return self._values
372
372
 
373
373
  def get_param_names(self) -> list[str]:
@@ -400,7 +400,7 @@ class NotInCollectionFilter(InAnyFilter[T]):
400
400
  exp.Not(this=exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
401
401
  )
402
402
 
403
- for resolved_name, value in zip(resolved_names, self.values):
403
+ for resolved_name, value in zip(resolved_names, self.values, strict=False):
404
404
  result = result.add_named_parameter(resolved_name, value)
405
405
  return result
406
406
 
@@ -418,7 +418,7 @@ class AnyCollectionFilter(InAnyFilter[T]):
418
418
 
419
419
  __slots__ = ("_field_name", "_values")
420
420
 
421
- def __init__(self, field_name: str, values: Optional[abc.Collection[T]] = None) -> None:
421
+ def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
422
422
  self._field_name = field_name
423
423
  self._values = values
424
424
 
@@ -427,7 +427,7 @@ class AnyCollectionFilter(InAnyFilter[T]):
427
427
  return self._field_name
428
428
 
429
429
  @property
430
- def values(self) -> Optional[abc.Collection[T]]:
430
+ def values(self) -> abc.Collection[T] | None:
431
431
  return self._values
432
432
 
433
433
  def get_param_names(self) -> list[str]:
@@ -461,7 +461,7 @@ class AnyCollectionFilter(InAnyFilter[T]):
461
461
  array_expr = exp.Array(expressions=placeholder_expressions)
462
462
  result = statement.where(exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr)))
463
463
 
464
- for resolved_name, value in zip(resolved_names, self.values):
464
+ for resolved_name, value in zip(resolved_names, self.values, strict=False):
465
465
  result = result.add_named_parameter(resolved_name, value)
466
466
  return result
467
467
 
@@ -479,7 +479,7 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
479
479
 
480
480
  __slots__ = ("_field_name", "_values")
481
481
 
482
- def __init__(self, field_name: str, values: Optional[abc.Collection[T]] = None) -> None:
482
+ def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
483
483
  self._field_name = field_name
484
484
  self._values = values
485
485
 
@@ -488,7 +488,7 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
488
488
  return self._field_name
489
489
 
490
490
  @property
491
- def values(self) -> Optional[abc.Collection[T]]:
491
+ def values(self) -> abc.Collection[T] | None:
492
492
  return self._values
493
493
 
494
494
  def get_param_names(self) -> list[str]:
@@ -520,7 +520,7 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
520
520
  condition = exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr))
521
521
  result = statement.where(exp.Not(this=condition))
522
522
 
523
- for resolved_name, value in zip(resolved_names, self.values):
523
+ for resolved_name, value in zip(resolved_names, self.values, strict=False):
524
524
  result = result.add_named_parameter(resolved_name, value)
525
525
  return result
526
526
 
@@ -576,10 +576,13 @@ class LimitOffsetFilter(PaginationFilter):
576
576
  limit_placeholder = exp.Placeholder(this=limit_param_name)
577
577
  offset_placeholder = exp.Placeholder(this=offset_param_name)
578
578
 
579
- try:
580
- current_statement = sqlglot.parse_one(statement.raw_sql, dialect=statement.dialect)
581
- except Exception:
582
- current_statement = exp.Select().from_(f"({statement.raw_sql})")
579
+ if statement.statement_expression is not None:
580
+ current_statement = statement.statement_expression
581
+ else:
582
+ try:
583
+ current_statement = sqlglot.parse_one(statement.raw_sql, dialect=statement.dialect)
584
+ except Exception:
585
+ current_statement = exp.Select().from_(f"({statement.raw_sql})")
583
586
 
584
587
  if isinstance(current_statement, exp.Select):
585
588
  new_statement = current_statement.limit(limit_placeholder).offset(offset_placeholder)
@@ -587,7 +590,6 @@ class LimitOffsetFilter(PaginationFilter):
587
590
  new_statement = exp.Select().from_(current_statement).limit(limit_placeholder).offset(offset_placeholder)
588
591
 
589
592
  result = statement.copy(statement=new_statement)
590
-
591
593
  result = result.add_named_parameter(limit_param_name, self.limit)
592
594
  return result.add_named_parameter(offset_param_name, self.offset)
593
595
 
@@ -628,12 +630,18 @@ class OrderByFilter(StatementFilter):
628
630
  col_expr = exp.column(self.field_name)
629
631
  order_expr = col_expr.desc() if converted_sort_order == "desc" else col_expr.asc()
630
632
 
631
- if statement.statement_expression is None:
632
- new_statement = exp.Select().order_by(order_expr)
633
- elif isinstance(statement.statement_expression, exp.Select):
634
- new_statement = statement.statement_expression.order_by(order_expr)
633
+ if statement.statement_expression is not None:
634
+ current_statement = statement.statement_expression
635
+ else:
636
+ try:
637
+ current_statement = sqlglot.parse_one(statement.raw_sql, dialect=statement.dialect)
638
+ except Exception:
639
+ current_statement = exp.Select().from_(f"({statement.raw_sql})")
640
+
641
+ if isinstance(current_statement, exp.Select):
642
+ new_statement = current_statement.order_by(order_expr)
635
643
  else:
636
- new_statement = exp.Select().from_(statement.statement_expression).order_by(order_expr)
644
+ new_statement = exp.Select().from_(current_statement).order_by(order_expr)
637
645
 
638
646
  return statement.copy(statement=new_statement)
639
647
 
@@ -650,13 +658,13 @@ class SearchFilter(StatementFilter):
650
658
 
651
659
  __slots__ = ("_field_name", "_ignore_case", "_value")
652
660
 
653
- def __init__(self, field_name: Union[str, set[str]], value: str, ignore_case: Optional[bool] = False) -> None:
661
+ def __init__(self, field_name: str | set[str], value: str, ignore_case: bool | None = False) -> None:
654
662
  self._field_name = field_name
655
663
  self._value = value
656
664
  self._ignore_case = ignore_case
657
665
 
658
666
  @property
659
- def field_name(self) -> Union[str, set[str]]:
667
+ def field_name(self) -> str | set[str]:
660
668
  return self._field_name
661
669
 
662
670
  @property
@@ -664,10 +672,10 @@ class SearchFilter(StatementFilter):
664
672
  return self._value
665
673
 
666
674
  @property
667
- def ignore_case(self) -> Optional[bool]:
675
+ def ignore_case(self) -> bool | None:
668
676
  return self._ignore_case
669
677
 
670
- def get_param_name(self) -> Optional[str]:
678
+ def get_param_name(self) -> str | None:
671
679
  """Get parameter name without storing it."""
672
680
  if not self.value:
673
681
  return None
@@ -726,7 +734,7 @@ class NotInSearchFilter(SearchFilter):
726
734
  Constructs WHERE field_name NOT LIKE '%value%' clauses.
727
735
  """
728
736
 
729
- def get_param_name(self) -> Optional[str]:
737
+ def get_param_name(self) -> str | None:
730
738
  """Get parameter name without storing it."""
731
739
  if not self.value:
732
740
  return None
@@ -817,18 +825,18 @@ def apply_filter(statement: "SQL", filter_obj: StatementFilter) -> "SQL":
817
825
  return filter_obj.append_to_statement(statement)
818
826
 
819
827
 
820
- FilterTypes: TypeAlias = Union[
821
- BeforeAfterFilter,
822
- OnBeforeAfterFilter,
823
- InCollectionFilter[Any],
824
- LimitOffsetFilter,
825
- OrderByFilter,
826
- SearchFilter,
827
- NotInCollectionFilter[Any],
828
- NotInSearchFilter,
829
- AnyCollectionFilter[Any],
830
- NotAnyCollectionFilter[Any],
831
- ]
828
+ FilterTypes: TypeAlias = (
829
+ BeforeAfterFilter
830
+ | OnBeforeAfterFilter
831
+ | InCollectionFilter[Any]
832
+ | LimitOffsetFilter
833
+ | OrderByFilter
834
+ | SearchFilter
835
+ | NotInCollectionFilter[Any]
836
+ | NotInSearchFilter
837
+ | AnyCollectionFilter[Any]
838
+ | NotAnyCollectionFilter[Any]
839
+ )
832
840
 
833
841
 
834
842
  def create_filters(filters: "list[StatementFilter]") -> tuple["StatementFilter", ...]:
sqlspec/core/hashing.py CHANGED
@@ -4,7 +4,7 @@ Provides hashing functions for SQL statements, expressions, parameters,
4
4
  filters, and AST sub-expressions.
5
5
  """
6
6
 
7
- from typing import TYPE_CHECKING, Any, Optional
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from sqlglot import exp
10
10
 
@@ -23,7 +23,7 @@ __all__ = (
23
23
  )
24
24
 
25
25
 
26
- def hash_expression(expr: Optional[exp.Expression], _seen: Optional[set[int]] = None) -> int:
26
+ def hash_expression(expr: exp.Expression | None, _seen: set[int] | None = None) -> int:
27
27
  """Generate hash from AST structure.
28
28
 
29
29
  Args:
@@ -77,9 +77,9 @@ def _hash_value(value: Any, _seen: set[int]) -> int:
77
77
 
78
78
 
79
79
  def hash_parameters(
80
- positional_parameters: Optional[list[Any]] = None,
81
- named_parameters: Optional[dict[str, Any]] = None,
82
- original_parameters: Optional[Any] = None,
80
+ positional_parameters: list[Any] | None = None,
81
+ named_parameters: dict[str, Any] | None = None,
82
+ original_parameters: Any | None = None,
83
83
  ) -> int:
84
84
  """Generate hash for SQL parameters.
85
85
 
@@ -148,7 +148,7 @@ def _hash_filter_value(value: Any) -> int:
148
148
  return hash(repr(value))
149
149
 
150
150
 
151
- def hash_filters(filters: Optional[list["StatementFilter"]] = None) -> int:
151
+ def hash_filters(filters: list["StatementFilter"] | None = None) -> int:
152
152
  """Generate hash for statement filters.
153
153
 
154
154
  Args:
@@ -208,7 +208,7 @@ def hash_sql_statement(statement: "SQL") -> str:
208
208
  return f"sql:{hash(tuple(state_components))}"
209
209
 
210
210
 
211
- def hash_expression_node(node: exp.Expression, include_children: bool = True, dialect: Optional[str] = None) -> str:
211
+ def hash_expression_node(node: exp.Expression, include_children: bool = True, dialect: str | None = None) -> str:
212
212
  """Generate cache key for an expression node.
213
213
 
214
214
  Args:
@@ -235,8 +235,8 @@ def hash_expression_node(node: exp.Expression, include_children: bool = True, di
235
235
  def hash_optimized_expression(
236
236
  expr: exp.Expression,
237
237
  dialect: str,
238
- schema: Optional[dict[str, Any]] = None,
239
- optimizer_settings: Optional[dict[str, Any]] = None,
238
+ schema: dict[str, Any] | None = None,
239
+ optimizer_settings: dict[str, Any] | None = None,
240
240
  ) -> str:
241
241
  """Generate cache key for optimized expressions.
242
242