sqlspec 0.17.1__py3-none-any.whl → 0.19.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 (77) hide show
  1. sqlspec/__init__.py +1 -1
  2. sqlspec/_sql.py +54 -159
  3. sqlspec/adapters/adbc/config.py +24 -30
  4. sqlspec/adapters/adbc/driver.py +42 -61
  5. sqlspec/adapters/aiosqlite/config.py +5 -10
  6. sqlspec/adapters/aiosqlite/driver.py +9 -25
  7. sqlspec/adapters/aiosqlite/pool.py +43 -35
  8. sqlspec/adapters/asyncmy/config.py +10 -7
  9. sqlspec/adapters/asyncmy/driver.py +18 -39
  10. sqlspec/adapters/asyncpg/config.py +4 -0
  11. sqlspec/adapters/asyncpg/driver.py +32 -79
  12. sqlspec/adapters/bigquery/config.py +12 -65
  13. sqlspec/adapters/bigquery/driver.py +39 -133
  14. sqlspec/adapters/duckdb/config.py +11 -15
  15. sqlspec/adapters/duckdb/driver.py +61 -85
  16. sqlspec/adapters/duckdb/pool.py +2 -5
  17. sqlspec/adapters/oracledb/_types.py +8 -1
  18. sqlspec/adapters/oracledb/config.py +55 -38
  19. sqlspec/adapters/oracledb/driver.py +35 -92
  20. sqlspec/adapters/oracledb/migrations.py +257 -0
  21. sqlspec/adapters/psqlpy/config.py +13 -9
  22. sqlspec/adapters/psqlpy/driver.py +28 -103
  23. sqlspec/adapters/psycopg/config.py +9 -5
  24. sqlspec/adapters/psycopg/driver.py +107 -175
  25. sqlspec/adapters/sqlite/config.py +7 -5
  26. sqlspec/adapters/sqlite/driver.py +37 -73
  27. sqlspec/adapters/sqlite/pool.py +3 -12
  28. sqlspec/base.py +19 -22
  29. sqlspec/builder/__init__.py +1 -1
  30. sqlspec/builder/_base.py +34 -20
  31. sqlspec/builder/_ddl.py +407 -183
  32. sqlspec/builder/_insert.py +1 -1
  33. sqlspec/builder/mixins/_insert_operations.py +26 -6
  34. sqlspec/builder/mixins/_merge_operations.py +1 -1
  35. sqlspec/builder/mixins/_select_operations.py +1 -5
  36. sqlspec/cli.py +281 -33
  37. sqlspec/config.py +183 -14
  38. sqlspec/core/__init__.py +89 -14
  39. sqlspec/core/cache.py +57 -104
  40. sqlspec/core/compiler.py +57 -112
  41. sqlspec/core/filters.py +1 -21
  42. sqlspec/core/hashing.py +13 -47
  43. sqlspec/core/parameters.py +272 -261
  44. sqlspec/core/result.py +12 -27
  45. sqlspec/core/splitter.py +17 -21
  46. sqlspec/core/statement.py +150 -159
  47. sqlspec/driver/_async.py +2 -15
  48. sqlspec/driver/_common.py +16 -95
  49. sqlspec/driver/_sync.py +2 -15
  50. sqlspec/driver/mixins/_result_tools.py +8 -29
  51. sqlspec/driver/mixins/_sql_translator.py +6 -8
  52. sqlspec/exceptions.py +1 -2
  53. sqlspec/extensions/litestar/plugin.py +15 -8
  54. sqlspec/loader.py +43 -115
  55. sqlspec/migrations/__init__.py +1 -1
  56. sqlspec/migrations/base.py +34 -45
  57. sqlspec/migrations/commands.py +34 -15
  58. sqlspec/migrations/loaders.py +1 -1
  59. sqlspec/migrations/runner.py +104 -19
  60. sqlspec/migrations/tracker.py +49 -2
  61. sqlspec/protocols.py +3 -6
  62. sqlspec/storage/__init__.py +4 -4
  63. sqlspec/storage/backends/fsspec.py +5 -6
  64. sqlspec/storage/backends/obstore.py +7 -8
  65. sqlspec/storage/registry.py +3 -3
  66. sqlspec/utils/__init__.py +2 -2
  67. sqlspec/utils/logging.py +6 -10
  68. sqlspec/utils/sync_tools.py +27 -4
  69. sqlspec/utils/text.py +6 -1
  70. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/METADATA +1 -1
  71. sqlspec-0.19.0.dist-info/RECORD +138 -0
  72. sqlspec/builder/_ddl_utils.py +0 -103
  73. sqlspec-0.17.1.dist-info/RECORD +0 -138
  74. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/WHEEL +0 -0
  75. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/entry_points.txt +0 -0
  76. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/LICENSE +0 -0
  77. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/core/cache.py CHANGED
@@ -1,25 +1,19 @@
1
- """Caching system with unified cache management.
1
+ """Caching system for SQL statement processing.
2
2
 
3
3
  This module provides a caching system with LRU eviction and TTL support for
4
4
  SQL statement processing, parameter processing, and expression caching.
5
5
 
6
6
  Components:
7
- - CacheKey: Immutable cache key with optimized hashing
8
- - UnifiedCache: Main cache implementation with LRU eviction and TTL
9
- - StatementCache: Specialized cache for compiled SQL statements
10
- - ExpressionCache: Specialized cache for parsed SQLGlot expressions
11
- - ParameterCache: Specialized cache for processed parameters
12
-
13
- Features:
14
- - LRU caching with configurable size and TTL
15
- - Thread-safe cache operations for concurrent access
16
- - Cached hash values to avoid recomputation
17
- - O(1) cache lookup and insertion operations
7
+ - CacheKey: Immutable cache key
8
+ - UnifiedCache: Cache implementation with LRU eviction and TTL
9
+ - StatementCache: Cache for compiled SQL statements
10
+ - ExpressionCache: Cache for parsed expressions
11
+ - ParameterCache: Cache for processed parameters
18
12
  """
19
13
 
20
14
  import threading
21
15
  import time
22
- from typing import TYPE_CHECKING, Any, Generic, Optional
16
+ from typing import TYPE_CHECKING, Any, Final, Optional
23
17
 
24
18
  from mypy_extensions import mypyc_attr
25
19
  from typing_extensions import TypeVar
@@ -49,29 +43,21 @@ __all__ = (
49
43
  T = TypeVar("T")
50
44
  CacheValueT = TypeVar("CacheValueT")
51
45
 
52
- # Cache configuration constants
53
- DEFAULT_MAX_SIZE = 10000 # LRU cache size limit
54
- DEFAULT_TTL_SECONDS = 3600 # 1 hour default TTL
55
- CACHE_STATS_UPDATE_INTERVAL = 100 # Update stats every N operations
56
46
 
57
- # Cache slots - optimized structure
58
- CACHE_KEY_SLOTS = ("_hash", "_key_data")
59
- CACHE_NODE_SLOTS = ("key", "value", "prev", "next", "timestamp", "access_count")
60
- UNIFIED_CACHE_SLOTS = ("_cache", "_lock", "_max_size", "_ttl", "_head", "_tail", "_stats")
61
- CACHE_STATS_SLOTS = ("hits", "misses", "evictions", "total_operations", "memory_usage")
47
+ DEFAULT_MAX_SIZE: Final = 10000
48
+ DEFAULT_TTL_SECONDS: Final = 3600
49
+ CACHE_STATS_UPDATE_INTERVAL: Final = 100
62
50
 
63
51
 
64
- @mypyc_attr(allow_interpreted_subclasses=True)
65
- class CacheKey:
66
- """Immutable cache key with optimized hashing.
52
+ CACHE_KEY_SLOTS: Final = ("_hash", "_key_data")
53
+ CACHE_NODE_SLOTS: Final = ("key", "value", "prev", "next", "timestamp", "access_count")
54
+ UNIFIED_CACHE_SLOTS: Final = ("_cache", "_lock", "_max_size", "_ttl", "_head", "_tail", "_stats")
55
+ CACHE_STATS_SLOTS: Final = ("hits", "misses", "evictions", "total_operations", "memory_usage")
67
56
 
68
- This class provides an immutable cache key for consistent cache operations
69
- across all cache types.
70
57
 
71
- Features:
72
- - Cached hash value to avoid recomputation
73
- - Immutable design for safe sharing across threads
74
- - Fast equality comparison with short-circuit evaluation
58
+ @mypyc_attr(allow_interpreted_subclasses=False)
59
+ class CacheKey:
60
+ """Immutable cache key.
75
61
 
76
62
  Args:
77
63
  key_data: Tuple of hashable values that uniquely identify the cached item
@@ -98,10 +84,10 @@ class CacheKey:
98
84
  return self._hash
99
85
 
100
86
  def __eq__(self, other: object) -> bool:
101
- """Equality comparison with short-circuit evaluation."""
87
+ """Equality comparison."""
102
88
  if type(other) is not CacheKey:
103
89
  return False
104
- other_key = other # type: CacheKey
90
+ other_key = other
105
91
  if self._hash != other_key._hash:
106
92
  return False
107
93
  return self._key_data == other_key._key_data
@@ -111,12 +97,11 @@ class CacheKey:
111
97
  return f"CacheKey({self._key_data!r})"
112
98
 
113
99
 
114
- @mypyc_attr(allow_interpreted_subclasses=True)
100
+ @mypyc_attr(allow_interpreted_subclasses=False)
115
101
  class CacheStats:
116
102
  """Cache statistics tracking.
117
103
 
118
- Tracks cache performance metrics including hit rates, evictions,
119
- and memory usage.
104
+ Tracks cache metrics including hit rates, evictions, and memory usage.
120
105
  """
121
106
 
122
107
  __slots__ = CACHE_STATS_SLOTS
@@ -171,13 +156,9 @@ class CacheStats:
171
156
  )
172
157
 
173
158
 
174
- @mypyc_attr(allow_interpreted_subclasses=True)
159
+ @mypyc_attr(allow_interpreted_subclasses=False)
175
160
  class CacheNode:
176
- """Internal cache node for LRU linked list implementation.
177
-
178
- This class represents a node in the doubly-linked list used for
179
- LRU cache implementation with O(1) operations.
180
- """
161
+ """Internal cache node for LRU linked list implementation."""
181
162
 
182
163
  __slots__ = CACHE_NODE_SLOTS
183
164
 
@@ -196,20 +177,10 @@ class CacheNode:
196
177
  self.access_count = 1
197
178
 
198
179
 
199
- @mypyc_attr(allow_interpreted_subclasses=True)
200
- class UnifiedCache(Generic[CacheValueT]):
180
+ @mypyc_attr(allow_interpreted_subclasses=False)
181
+ class UnifiedCache:
201
182
  """Cache with LRU eviction and TTL support.
202
183
 
203
- This class provides a thread-safe cache implementation with LRU eviction
204
- and time-based expiration.
205
-
206
- Features:
207
- - O(1) cache lookup, insertion, and deletion operations
208
- - LRU eviction policy with configurable size limits
209
- - TTL-based expiration for cache entries
210
- - Thread-safe operations
211
- - Statistics tracking
212
-
213
184
  Args:
214
185
  max_size: Maximum number of items to cache (LRU eviction when exceeded)
215
186
  ttl_seconds: Time-to-live in seconds (None for no expiration)
@@ -235,8 +206,8 @@ class UnifiedCache(Generic[CacheValueT]):
235
206
  self._head.next = self._tail
236
207
  self._tail.prev = self._head
237
208
 
238
- def get(self, key: CacheKey) -> Optional[CacheValueT]:
239
- """Get value from cache with LRU update.
209
+ def get(self, key: CacheKey) -> Optional[Any]:
210
+ """Get value from cache.
240
211
 
241
212
  Args:
242
213
  key: Cache key to lookup
@@ -250,7 +221,6 @@ class UnifiedCache(Generic[CacheValueT]):
250
221
  self._stats.record_miss()
251
222
  return None
252
223
 
253
- # Optimize TTL check with early variable assignment
254
224
  ttl = self._ttl
255
225
  if ttl is not None:
256
226
  current_time = time.time()
@@ -264,10 +234,10 @@ class UnifiedCache(Generic[CacheValueT]):
264
234
  self._move_to_head(node)
265
235
  node.access_count += 1
266
236
  self._stats.record_hit()
267
- return node.value # type: ignore[no-any-return]
237
+ return node.value
268
238
 
269
- def put(self, key: CacheKey, value: CacheValueT) -> None:
270
- """Put value in cache with LRU management.
239
+ def put(self, key: CacheKey, value: Any) -> None:
240
+ """Put value in cache.
271
241
 
272
242
  Args:
273
243
  key: Cache key
@@ -286,7 +256,6 @@ class UnifiedCache(Generic[CacheValueT]):
286
256
  self._cache[key] = new_node
287
257
  self._add_to_head(new_node)
288
258
 
289
- # Optimize size check with cached length
290
259
  if len(self._cache) > self._max_size:
291
260
  tail_node = self._tail.prev
292
261
  if tail_node is not None and tail_node is not self._head:
@@ -326,14 +295,14 @@ class UnifiedCache(Generic[CacheValueT]):
326
295
 
327
296
  def is_empty(self) -> bool:
328
297
  """Check if cache is empty."""
329
- return len(self._cache) == 0
298
+ return not self._cache
330
299
 
331
300
  def get_stats(self) -> CacheStats:
332
301
  """Get cache statistics."""
333
302
  return self._stats
334
303
 
335
304
  def _add_to_head(self, node: CacheNode) -> None:
336
- """Add node after head."""
305
+ """Add node to head of list."""
337
306
  node.prev = self._head
338
307
  head_next: Optional[CacheNode] = self._head.next
339
308
  node.next = head_next
@@ -351,7 +320,7 @@ class UnifiedCache(Generic[CacheValueT]):
351
320
  node_next.prev = node_prev
352
321
 
353
322
  def _move_to_head(self, node: CacheNode) -> None:
354
- """Move existing node to head."""
323
+ """Move node to head of list."""
355
324
  self._remove_node(node)
356
325
  self._add_to_head(node)
357
326
 
@@ -366,17 +335,13 @@ class UnifiedCache(Generic[CacheValueT]):
366
335
  if node is None:
367
336
  return False
368
337
 
369
- # Optimize TTL check
370
338
  ttl = self._ttl
371
339
  return not (ttl is not None and time.time() - node.timestamp > ttl)
372
340
 
373
341
 
374
342
  @mypyc_attr(allow_interpreted_subclasses=False)
375
343
  class StatementCache:
376
- """Specialized cache for compiled SQL statements.
377
-
378
- Caches compiled SQL statements and their execution parameters.
379
- """
344
+ """Cache for compiled SQL statements."""
380
345
 
381
346
  def __init__(self, max_size: int = DEFAULT_MAX_SIZE) -> None:
382
347
  """Initialize statement cache.
@@ -384,7 +349,7 @@ class StatementCache:
384
349
  Args:
385
350
  max_size: Maximum number of statements to cache
386
351
  """
387
- self._cache: UnifiedCache[tuple[str, Any]] = UnifiedCache(max_size)
352
+ self._cache: UnifiedCache = UnifiedCache(max_size)
388
353
 
389
354
  def get_compiled(self, statement: "SQL") -> Optional[tuple[str, Any]]:
390
355
  """Get compiled SQL and parameters from cache.
@@ -393,7 +358,7 @@ class StatementCache:
393
358
  statement: SQL statement to lookup
394
359
 
395
360
  Returns:
396
- Tuple of (compiled_sql, parameters) or None if not cached
361
+ Tuple of (compiled_sql, parameters) or None if not found
397
362
  """
398
363
  cache_key = self._create_statement_key(statement)
399
364
  return self._cache.get(cache_key)
@@ -418,11 +383,11 @@ class StatementCache:
418
383
  Returns:
419
384
  Cache key for the statement
420
385
  """
421
- # Create key from SQL text, parameters, and configuration
386
+
422
387
  key_data = (
423
388
  "statement",
424
389
  statement._raw_sql,
425
- hash(statement), # Includes parameters and flags
390
+ hash(statement),
426
391
  str(statement.dialect) if statement.dialect else None,
427
392
  statement.is_many,
428
393
  statement.is_script,
@@ -440,10 +405,7 @@ class StatementCache:
440
405
 
441
406
  @mypyc_attr(allow_interpreted_subclasses=False)
442
407
  class ExpressionCache:
443
- """Specialized cache for parsed SQLGlot expressions.
444
-
445
- Caches parsed SQLGlot expressions to avoid redundant parsing operations.
446
- """
408
+ """Cache for parsed expressions."""
447
409
 
448
410
  def __init__(self, max_size: int = DEFAULT_MAX_SIZE) -> None:
449
411
  """Initialize expression cache.
@@ -451,7 +413,7 @@ class ExpressionCache:
451
413
  Args:
452
414
  max_size: Maximum number of expressions to cache
453
415
  """
454
- self._cache: UnifiedCache[exp.Expression] = UnifiedCache(max_size)
416
+ self._cache: UnifiedCache = UnifiedCache(max_size)
455
417
 
456
418
  def get_expression(self, sql: str, dialect: Optional[str] = None) -> "Optional[exp.Expression]":
457
419
  """Get parsed expression from cache.
@@ -461,7 +423,7 @@ class ExpressionCache:
461
423
  dialect: SQL dialect
462
424
 
463
425
  Returns:
464
- Parsed expression or None if not cached
426
+ Parsed expression or None if not found
465
427
  """
466
428
  cache_key = self._create_expression_key(sql, dialect)
467
429
  return self._cache.get(cache_key)
@@ -501,10 +463,7 @@ class ExpressionCache:
501
463
 
502
464
  @mypyc_attr(allow_interpreted_subclasses=False)
503
465
  class ParameterCache:
504
- """Specialized cache for processed parameters.
505
-
506
- Caches processed parameter transformations.
507
- """
466
+ """Cache for processed parameters."""
508
467
 
509
468
  def __init__(self, max_size: int = DEFAULT_MAX_SIZE) -> None:
510
469
  """Initialize parameter cache.
@@ -512,7 +471,7 @@ class ParameterCache:
512
471
  Args:
513
472
  max_size: Maximum number of parameter sets to cache
514
473
  """
515
- self._cache: UnifiedCache[Any] = UnifiedCache(max_size)
474
+ self._cache: UnifiedCache = UnifiedCache(max_size)
516
475
 
517
476
  def get_parameters(self, original_params: Any, config_hash: int) -> Optional[Any]:
518
477
  """Get processed parameters from cache.
@@ -522,7 +481,7 @@ class ParameterCache:
522
481
  config_hash: Hash of parameter processing configuration
523
482
 
524
483
  Returns:
525
- Processed parameters or None if not cached
484
+ Processed parameters or None if not found
526
485
  """
527
486
  cache_key = self._create_parameter_key(original_params, config_hash)
528
487
  return self._cache.get(cache_key)
@@ -548,9 +507,8 @@ class ParameterCache:
548
507
  Returns:
549
508
  Cache key for the parameters
550
509
  """
551
- # Create stable key from parameters and configuration
510
+
552
511
  try:
553
- # Optimize type checking order
554
512
  param_key: tuple[Any, ...]
555
513
  if isinstance(params, dict):
556
514
  param_key = tuple(sorted(params.items()))
@@ -561,7 +519,6 @@ class ParameterCache:
561
519
 
562
520
  return CacheKey(("parameters", param_key, config_hash))
563
521
  except (TypeError, ValueError):
564
- # Fallback for unhashable types
565
522
  param_key_fallback = (str(params), type(params).__name__)
566
523
  return CacheKey(("parameters", param_key_fallback, config_hash))
567
524
 
@@ -574,14 +531,14 @@ class ParameterCache:
574
531
  return self._cache.get_stats()
575
532
 
576
533
 
577
- _default_cache: Optional[UnifiedCache[Any]] = None
534
+ _default_cache: Optional[UnifiedCache] = None
578
535
  _statement_cache: Optional[StatementCache] = None
579
536
  _expression_cache: Optional[ExpressionCache] = None
580
537
  _parameter_cache: Optional[ParameterCache] = None
581
538
  _cache_lock = threading.Lock()
582
539
 
583
540
 
584
- def get_default_cache() -> UnifiedCache[Any]:
541
+ def get_default_cache() -> UnifiedCache:
585
542
  """Get the default unified cache instance.
586
543
 
587
544
  Returns:
@@ -591,7 +548,7 @@ def get_default_cache() -> UnifiedCache[Any]:
591
548
  if _default_cache is None:
592
549
  with _cache_lock:
593
550
  if _default_cache is None:
594
- _default_cache = UnifiedCache[Any]()
551
+ _default_cache = UnifiedCache()
595
552
  return _default_cache
596
553
 
597
554
 
@@ -670,12 +627,9 @@ def get_cache_statistics() -> dict[str, CacheStats]:
670
627
  _global_cache_config: "Optional[CacheConfig]" = None
671
628
 
672
629
 
673
- @mypyc_attr(allow_interpreted_subclasses=True)
630
+ @mypyc_attr(allow_interpreted_subclasses=False)
674
631
  class CacheConfig:
675
- """Global cache configuration for SQLSpec.
676
-
677
- Controls caching behavior across the SQLSpec system.
678
- """
632
+ """Global cache configuration for SQLSpec."""
679
633
 
680
634
  def __init__(
681
635
  self,
@@ -723,8 +677,7 @@ def get_cache_config() -> CacheConfig:
723
677
  def update_cache_config(config: CacheConfig) -> None:
724
678
  """Update the global cache configuration.
725
679
 
726
- This clears all existing caches when configuration changes to ensure
727
- consistency with the new settings.
680
+ Clears all existing caches when configuration changes.
728
681
 
729
682
  Args:
730
683
  config: New cache configuration to apply globally
@@ -752,9 +705,9 @@ def update_cache_config(config: CacheConfig) -> None:
752
705
  )
753
706
 
754
707
 
755
- @mypyc_attr(allow_interpreted_subclasses=True)
708
+ @mypyc_attr(allow_interpreted_subclasses=False)
756
709
  class CacheStatsAggregate:
757
- """Aggregated cache statistics from all cache instances."""
710
+ """Cache statistics from all cache instances."""
758
711
 
759
712
  __slots__ = (
760
713
  "fragment_capacity",
@@ -775,7 +728,7 @@ class CacheStatsAggregate:
775
728
  )
776
729
 
777
730
  def __init__(self) -> None:
778
- """Initialize aggregated cache statistics."""
731
+ """Initialize cache statistics."""
779
732
  self.sql_hit_rate = 0.0
780
733
  self.fragment_hit_rate = 0.0
781
734
  self.optimized_hit_rate = 0.0
@@ -794,10 +747,10 @@ class CacheStatsAggregate:
794
747
 
795
748
 
796
749
  def get_cache_stats() -> CacheStatsAggregate:
797
- """Get current cache statistics from all caches.
750
+ """Get cache statistics from all caches.
798
751
 
799
752
  Returns:
800
- Combined cache statistics object
753
+ Cache statistics object
801
754
  """
802
755
  stats_dict = get_cache_statistics()
803
756
  stats = CacheStatsAggregate()
@@ -841,7 +794,7 @@ def reset_cache_stats() -> None:
841
794
 
842
795
 
843
796
  def log_cache_stats() -> None:
844
- """Log current cache statistics using the configured logger."""
797
+ """Log cache statistics."""
845
798
  logger = get_logger("sqlspec.cache")
846
799
  stats = get_cache_stats()
847
800
  logger.info("Cache Statistics: %s", stats)