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.
- sqlspec/__init__.py +1 -1
- sqlspec/_sql.py +54 -159
- sqlspec/adapters/adbc/config.py +24 -30
- sqlspec/adapters/adbc/driver.py +42 -61
- sqlspec/adapters/aiosqlite/config.py +5 -10
- sqlspec/adapters/aiosqlite/driver.py +9 -25
- sqlspec/adapters/aiosqlite/pool.py +43 -35
- sqlspec/adapters/asyncmy/config.py +10 -7
- sqlspec/adapters/asyncmy/driver.py +18 -39
- sqlspec/adapters/asyncpg/config.py +4 -0
- sqlspec/adapters/asyncpg/driver.py +32 -79
- sqlspec/adapters/bigquery/config.py +12 -65
- sqlspec/adapters/bigquery/driver.py +39 -133
- sqlspec/adapters/duckdb/config.py +11 -15
- sqlspec/adapters/duckdb/driver.py +61 -85
- sqlspec/adapters/duckdb/pool.py +2 -5
- sqlspec/adapters/oracledb/_types.py +8 -1
- sqlspec/adapters/oracledb/config.py +55 -38
- sqlspec/adapters/oracledb/driver.py +35 -92
- sqlspec/adapters/oracledb/migrations.py +257 -0
- sqlspec/adapters/psqlpy/config.py +13 -9
- sqlspec/adapters/psqlpy/driver.py +28 -103
- sqlspec/adapters/psycopg/config.py +9 -5
- sqlspec/adapters/psycopg/driver.py +107 -175
- sqlspec/adapters/sqlite/config.py +7 -5
- sqlspec/adapters/sqlite/driver.py +37 -73
- sqlspec/adapters/sqlite/pool.py +3 -12
- sqlspec/base.py +19 -22
- sqlspec/builder/__init__.py +1 -1
- sqlspec/builder/_base.py +34 -20
- sqlspec/builder/_ddl.py +407 -183
- sqlspec/builder/_insert.py +1 -1
- sqlspec/builder/mixins/_insert_operations.py +26 -6
- sqlspec/builder/mixins/_merge_operations.py +1 -1
- sqlspec/builder/mixins/_select_operations.py +1 -5
- sqlspec/cli.py +281 -33
- sqlspec/config.py +183 -14
- sqlspec/core/__init__.py +89 -14
- sqlspec/core/cache.py +57 -104
- sqlspec/core/compiler.py +57 -112
- sqlspec/core/filters.py +1 -21
- sqlspec/core/hashing.py +13 -47
- sqlspec/core/parameters.py +272 -261
- sqlspec/core/result.py +12 -27
- sqlspec/core/splitter.py +17 -21
- sqlspec/core/statement.py +150 -159
- sqlspec/driver/_async.py +2 -15
- sqlspec/driver/_common.py +16 -95
- sqlspec/driver/_sync.py +2 -15
- sqlspec/driver/mixins/_result_tools.py +8 -29
- sqlspec/driver/mixins/_sql_translator.py +6 -8
- sqlspec/exceptions.py +1 -2
- sqlspec/extensions/litestar/plugin.py +15 -8
- sqlspec/loader.py +43 -115
- sqlspec/migrations/__init__.py +1 -1
- sqlspec/migrations/base.py +34 -45
- sqlspec/migrations/commands.py +34 -15
- sqlspec/migrations/loaders.py +1 -1
- sqlspec/migrations/runner.py +104 -19
- sqlspec/migrations/tracker.py +49 -2
- sqlspec/protocols.py +3 -6
- sqlspec/storage/__init__.py +4 -4
- sqlspec/storage/backends/fsspec.py +5 -6
- sqlspec/storage/backends/obstore.py +7 -8
- sqlspec/storage/registry.py +3 -3
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/logging.py +6 -10
- sqlspec/utils/sync_tools.py +27 -4
- sqlspec/utils/text.py +6 -1
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/METADATA +1 -1
- sqlspec-0.19.0.dist-info/RECORD +138 -0
- sqlspec/builder/_ddl_utils.py +0 -103
- sqlspec-0.17.1.dist-info/RECORD +0 -138
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/config.py
CHANGED
|
@@ -5,13 +5,17 @@ from typing_extensions import NotRequired, TypedDict
|
|
|
5
5
|
|
|
6
6
|
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
7
7
|
from sqlspec.core.statement import StatementConfig
|
|
8
|
+
from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
|
|
8
9
|
from sqlspec.utils.logging import get_logger
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from collections.abc import Awaitable
|
|
12
13
|
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
14
|
+
from pathlib import Path
|
|
13
15
|
|
|
14
16
|
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
17
|
+
from sqlspec.loader import SQLFileLoader
|
|
18
|
+
from sqlspec.migrations.commands import MigrationCommands
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
__all__ = (
|
|
@@ -21,6 +25,7 @@ __all__ = (
|
|
|
21
25
|
"DatabaseConfigProtocol",
|
|
22
26
|
"DriverT",
|
|
23
27
|
"LifecycleConfig",
|
|
28
|
+
"MigrationConfig",
|
|
24
29
|
"NoPoolAsyncConfig",
|
|
25
30
|
"NoPoolSyncConfig",
|
|
26
31
|
"SyncConfigT",
|
|
@@ -43,7 +48,7 @@ logger = get_logger("config")
|
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
class LifecycleConfig(TypedDict, total=False):
|
|
46
|
-
"""
|
|
51
|
+
"""Lifecycle hooks for all adapters.
|
|
47
52
|
|
|
48
53
|
Each hook accepts a list of callables to support multiple handlers.
|
|
49
54
|
"""
|
|
@@ -59,10 +64,39 @@ class LifecycleConfig(TypedDict, total=False):
|
|
|
59
64
|
on_error: NotRequired[list[Callable[[Exception, str, dict], None]]]
|
|
60
65
|
|
|
61
66
|
|
|
67
|
+
class MigrationConfig(TypedDict, total=False):
|
|
68
|
+
"""Configuration options for database migrations.
|
|
69
|
+
|
|
70
|
+
All fields are optional with sensible defaults.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
script_location: NotRequired[str]
|
|
74
|
+
"""Path to the migrations directory. Defaults to 'migrations'."""
|
|
75
|
+
|
|
76
|
+
version_table_name: NotRequired[str]
|
|
77
|
+
"""Name of the table used to track applied migrations. Defaults to 'sqlspec_migrations'."""
|
|
78
|
+
|
|
79
|
+
project_root: NotRequired[str]
|
|
80
|
+
"""Path to the project root directory. Used for relative path resolution."""
|
|
81
|
+
|
|
82
|
+
enabled: NotRequired[bool]
|
|
83
|
+
"""Whether this configuration should be included in CLI operations. Defaults to True."""
|
|
84
|
+
|
|
85
|
+
|
|
62
86
|
class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
|
|
63
87
|
"""Protocol defining the interface for database configurations."""
|
|
64
88
|
|
|
65
|
-
__slots__ = (
|
|
89
|
+
__slots__ = (
|
|
90
|
+
"_migration_commands",
|
|
91
|
+
"_migration_loader",
|
|
92
|
+
"driver_features",
|
|
93
|
+
"migration_config",
|
|
94
|
+
"pool_instance",
|
|
95
|
+
"statement_config",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
_migration_loader: "SQLFileLoader"
|
|
99
|
+
_migration_commands: "MigrationCommands"
|
|
66
100
|
driver_type: "ClassVar[type[Any]]"
|
|
67
101
|
connection_type: "ClassVar[type[Any]]"
|
|
68
102
|
is_async: "ClassVar[bool]" = False
|
|
@@ -73,7 +107,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
|
|
|
73
107
|
supports_native_parquet_export: "ClassVar[bool]" = False
|
|
74
108
|
statement_config: "StatementConfig"
|
|
75
109
|
pool_instance: "Optional[PoolT]"
|
|
76
|
-
migration_config: "dict[str, Any]"
|
|
110
|
+
migration_config: "Union[dict[str, Any], MigrationConfig]"
|
|
77
111
|
|
|
78
112
|
def __hash__(self) -> int:
|
|
79
113
|
return id(self)
|
|
@@ -135,6 +169,136 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
|
|
|
135
169
|
"""
|
|
136
170
|
return {}
|
|
137
171
|
|
|
172
|
+
def _initialize_migration_components(self) -> None:
|
|
173
|
+
"""Initialize migration loader and commands with necessary imports.
|
|
174
|
+
|
|
175
|
+
This method handles the circular import between config and commands
|
|
176
|
+
by importing at runtime when needed.
|
|
177
|
+
"""
|
|
178
|
+
from sqlspec.loader import SQLFileLoader
|
|
179
|
+
from sqlspec.migrations.commands import MigrationCommands
|
|
180
|
+
|
|
181
|
+
self._migration_loader = SQLFileLoader()
|
|
182
|
+
self._migration_commands = MigrationCommands(self) # type: ignore[arg-type]
|
|
183
|
+
|
|
184
|
+
def _ensure_migration_loader(self) -> "SQLFileLoader":
|
|
185
|
+
"""Get the migration SQL loader and auto-load files if needed.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
The SQLFileLoader instance for migration files.
|
|
189
|
+
"""
|
|
190
|
+
# Auto-load migration files from configured migration path if it exists
|
|
191
|
+
migration_config = self.migration_config or {}
|
|
192
|
+
script_location = migration_config.get("script_location", "migrations")
|
|
193
|
+
|
|
194
|
+
from pathlib import Path
|
|
195
|
+
|
|
196
|
+
migration_path = Path(script_location)
|
|
197
|
+
if migration_path.exists() and not self._migration_loader.list_files():
|
|
198
|
+
self._migration_loader.load_sql(migration_path)
|
|
199
|
+
logger.debug("Auto-loaded migration SQL files from %s", migration_path)
|
|
200
|
+
|
|
201
|
+
return self._migration_loader
|
|
202
|
+
|
|
203
|
+
def _ensure_migration_commands(self) -> "MigrationCommands":
|
|
204
|
+
"""Get the migration commands instance.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The MigrationCommands instance for this config.
|
|
208
|
+
"""
|
|
209
|
+
return self._migration_commands
|
|
210
|
+
|
|
211
|
+
def get_migration_loader(self) -> "SQLFileLoader":
|
|
212
|
+
"""Get the SQL loader for migration files.
|
|
213
|
+
|
|
214
|
+
This provides access to migration SQL files loaded from the configured
|
|
215
|
+
script_location directory. Files are loaded lazily on first access.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
SQLFileLoader instance with migration files loaded.
|
|
219
|
+
"""
|
|
220
|
+
return self._ensure_migration_loader()
|
|
221
|
+
|
|
222
|
+
def load_migration_sql_files(self, *paths: "Union[str, Path]") -> None:
|
|
223
|
+
"""Load additional migration SQL files from specified paths.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
*paths: One or more file paths or directory paths to load migration SQL files from.
|
|
227
|
+
"""
|
|
228
|
+
from pathlib import Path
|
|
229
|
+
|
|
230
|
+
loader = self._ensure_migration_loader()
|
|
231
|
+
for path in paths:
|
|
232
|
+
path_obj = Path(path)
|
|
233
|
+
if path_obj.exists():
|
|
234
|
+
loader.load_sql(path_obj)
|
|
235
|
+
logger.debug("Loaded migration SQL files from %s", path_obj)
|
|
236
|
+
else:
|
|
237
|
+
logger.warning("Migration path does not exist: %s", path_obj)
|
|
238
|
+
|
|
239
|
+
def get_migration_commands(self) -> "MigrationCommands":
|
|
240
|
+
"""Get migration commands for this configuration.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
MigrationCommands instance configured for this database.
|
|
244
|
+
"""
|
|
245
|
+
return self._ensure_migration_commands()
|
|
246
|
+
|
|
247
|
+
def migrate_up(self, revision: str = "head") -> None:
|
|
248
|
+
"""Apply migrations up to the specified revision.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
revision: Target revision or "head" for latest. Defaults to "head".
|
|
252
|
+
"""
|
|
253
|
+
commands = self._ensure_migration_commands()
|
|
254
|
+
commands.upgrade(revision)
|
|
255
|
+
|
|
256
|
+
def migrate_down(self, revision: str = "-1") -> None:
|
|
257
|
+
"""Apply migrations down to the specified revision.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
revision: Target revision, "-1" for one step back, or "base" for all migrations. Defaults to "-1".
|
|
261
|
+
"""
|
|
262
|
+
commands = self._ensure_migration_commands()
|
|
263
|
+
commands.downgrade(revision)
|
|
264
|
+
|
|
265
|
+
def get_current_migration(self, verbose: bool = False) -> "Optional[str]":
|
|
266
|
+
"""Get the current migration version.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
verbose: Whether to show detailed migration history.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
The current migration version or None if no migrations applied.
|
|
273
|
+
"""
|
|
274
|
+
commands = self._ensure_migration_commands()
|
|
275
|
+
return commands.current(verbose=verbose)
|
|
276
|
+
|
|
277
|
+
def create_migration(self, message: str, file_type: str = "sql") -> None:
|
|
278
|
+
"""Create a new migration file.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
message: Description for the migration.
|
|
282
|
+
file_type: Type of migration file to create ('sql' or 'py'). Defaults to 'sql'.
|
|
283
|
+
"""
|
|
284
|
+
commands = self._ensure_migration_commands()
|
|
285
|
+
commands.revision(message, file_type)
|
|
286
|
+
|
|
287
|
+
def init_migrations(self, directory: "Optional[str]" = None, package: bool = True) -> None:
|
|
288
|
+
"""Initialize migration directory structure.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
directory: Directory to initialize migrations in. Uses script_location from migration_config if not provided.
|
|
292
|
+
package: Whether to create __init__.py file. Defaults to True.
|
|
293
|
+
"""
|
|
294
|
+
if directory is None:
|
|
295
|
+
migration_config = self.migration_config or {}
|
|
296
|
+
directory = migration_config.get("script_location") or "migrations"
|
|
297
|
+
|
|
298
|
+
commands = self._ensure_migration_commands()
|
|
299
|
+
assert directory is not None
|
|
300
|
+
commands.init(directory, package)
|
|
301
|
+
|
|
138
302
|
|
|
139
303
|
class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
|
|
140
304
|
"""Base class for a sync database configurations that do not implement a pool."""
|
|
@@ -142,18 +306,20 @@ class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
|
|
|
142
306
|
__slots__ = ("connection_config",)
|
|
143
307
|
is_async: "ClassVar[bool]" = False
|
|
144
308
|
supports_connection_pooling: "ClassVar[bool]" = False
|
|
309
|
+
migration_tracker_type: "ClassVar[type[Any]]" = SyncMigrationTracker
|
|
145
310
|
|
|
146
311
|
def __init__(
|
|
147
312
|
self,
|
|
148
313
|
*,
|
|
149
314
|
connection_config: Optional[dict[str, Any]] = None,
|
|
150
|
-
migration_config: "Optional[dict[str, Any]]" = None,
|
|
315
|
+
migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
|
|
151
316
|
statement_config: "Optional[StatementConfig]" = None,
|
|
152
317
|
driver_features: "Optional[dict[str, Any]]" = None,
|
|
153
318
|
) -> None:
|
|
154
319
|
self.pool_instance = None
|
|
155
320
|
self.connection_config = connection_config or {}
|
|
156
|
-
self.migration_config: dict[str, Any] = migration_config
|
|
321
|
+
self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
|
|
322
|
+
self._initialize_migration_components()
|
|
157
323
|
|
|
158
324
|
if statement_config is None:
|
|
159
325
|
default_parameter_config = ParameterStyleConfig(
|
|
@@ -192,21 +358,22 @@ class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
|
|
|
192
358
|
"""Base class for an async database configurations that do not implement a pool."""
|
|
193
359
|
|
|
194
360
|
__slots__ = ("connection_config",)
|
|
195
|
-
|
|
196
361
|
is_async: "ClassVar[bool]" = True
|
|
197
362
|
supports_connection_pooling: "ClassVar[bool]" = False
|
|
363
|
+
migration_tracker_type: "ClassVar[type[Any]]" = AsyncMigrationTracker
|
|
198
364
|
|
|
199
365
|
def __init__(
|
|
200
366
|
self,
|
|
201
367
|
*,
|
|
202
368
|
connection_config: "Optional[dict[str, Any]]" = None,
|
|
203
|
-
migration_config: "Optional[dict[str, Any]]" = None,
|
|
369
|
+
migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
|
|
204
370
|
statement_config: "Optional[StatementConfig]" = None,
|
|
205
371
|
driver_features: "Optional[dict[str, Any]]" = None,
|
|
206
372
|
) -> None:
|
|
207
373
|
self.pool_instance = None
|
|
208
374
|
self.connection_config = connection_config or {}
|
|
209
|
-
self.migration_config: dict[str, Any] = migration_config
|
|
375
|
+
self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
|
|
376
|
+
self._initialize_migration_components()
|
|
210
377
|
|
|
211
378
|
if statement_config is None:
|
|
212
379
|
default_parameter_config = ParameterStyleConfig(
|
|
@@ -245,22 +412,23 @@ class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
|
|
|
245
412
|
"""Generic Sync Database Configuration."""
|
|
246
413
|
|
|
247
414
|
__slots__ = ("pool_config",)
|
|
248
|
-
|
|
249
415
|
is_async: "ClassVar[bool]" = False
|
|
250
416
|
supports_connection_pooling: "ClassVar[bool]" = True
|
|
417
|
+
migration_tracker_type: "ClassVar[type[Any]]" = SyncMigrationTracker
|
|
251
418
|
|
|
252
419
|
def __init__(
|
|
253
420
|
self,
|
|
254
421
|
*,
|
|
255
422
|
pool_config: "Optional[dict[str, Any]]" = None,
|
|
256
423
|
pool_instance: "Optional[PoolT]" = None,
|
|
257
|
-
migration_config: "Optional[dict[str, Any]]" = None,
|
|
424
|
+
migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
|
|
258
425
|
statement_config: "Optional[StatementConfig]" = None,
|
|
259
426
|
driver_features: "Optional[dict[str, Any]]" = None,
|
|
260
427
|
) -> None:
|
|
261
428
|
self.pool_instance = pool_instance
|
|
262
429
|
self.pool_config = pool_config or {}
|
|
263
|
-
self.migration_config: dict[str, Any] = migration_config
|
|
430
|
+
self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
|
|
431
|
+
self._initialize_migration_components()
|
|
264
432
|
|
|
265
433
|
if statement_config is None:
|
|
266
434
|
default_parameter_config = ParameterStyleConfig(
|
|
@@ -321,22 +489,23 @@ class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
|
|
|
321
489
|
"""Generic Async Database Configuration."""
|
|
322
490
|
|
|
323
491
|
__slots__ = ("pool_config",)
|
|
324
|
-
|
|
325
492
|
is_async: "ClassVar[bool]" = True
|
|
326
493
|
supports_connection_pooling: "ClassVar[bool]" = True
|
|
494
|
+
migration_tracker_type: "ClassVar[type[Any]]" = AsyncMigrationTracker
|
|
327
495
|
|
|
328
496
|
def __init__(
|
|
329
497
|
self,
|
|
330
498
|
*,
|
|
331
499
|
pool_config: "Optional[dict[str, Any]]" = None,
|
|
332
500
|
pool_instance: "Optional[PoolT]" = None,
|
|
333
|
-
migration_config: "Optional[dict[str, Any]]" = None,
|
|
501
|
+
migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
|
|
334
502
|
statement_config: "Optional[StatementConfig]" = None,
|
|
335
503
|
driver_features: "Optional[dict[str, Any]]" = None,
|
|
336
504
|
) -> None:
|
|
337
505
|
self.pool_instance = pool_instance
|
|
338
506
|
self.pool_config = pool_config or {}
|
|
339
|
-
self.migration_config: dict[str, Any] = migration_config
|
|
507
|
+
self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
|
|
508
|
+
self._initialize_migration_components()
|
|
340
509
|
|
|
341
510
|
if statement_config is None:
|
|
342
511
|
self.statement_config = StatementConfig(
|
sqlspec/core/__init__.py
CHANGED
|
@@ -1,17 +1,92 @@
|
|
|
1
|
-
"""SQLSpec Core Module - SQL Processing System.
|
|
2
|
-
|
|
3
|
-
This module provides the core SQL processing
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
"""SQLSpec Core Module - High-Performance SQL Processing System.
|
|
2
|
+
|
|
3
|
+
This module provides the core SQL processing infrastructure for SQLSpec, implementing
|
|
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.
|
|
7
|
+
|
|
8
|
+
Architecture Overview:
|
|
9
|
+
The core module implements a single-pass processing pipeline where SQL statements
|
|
10
|
+
are parsed once, transformed once, and validated once. The SQL object serves as
|
|
11
|
+
the single source of truth throughout the system.
|
|
12
|
+
|
|
13
|
+
Key Components:
|
|
14
|
+
statement.py: SQL statement representation and configuration management
|
|
15
|
+
- SQL class for statement encapsulation with lazy compilation
|
|
16
|
+
- StatementConfig for processing pipeline configuration
|
|
17
|
+
- ProcessedState for cached compilation results
|
|
18
|
+
- Support for execute_many and script execution modes
|
|
19
|
+
|
|
20
|
+
parameters.py: Type-safe parameter processing and style conversion
|
|
21
|
+
- Automatic parameter style detection and conversion
|
|
22
|
+
- Support for QMARK (?), NAMED (:name), NUMERIC ($1), FORMAT (%s) styles
|
|
23
|
+
- Parameter validation and type coercion
|
|
24
|
+
- Batch parameter handling for execute_many operations
|
|
25
|
+
|
|
26
|
+
compiler.py: SQL compilation with validation and optimization
|
|
27
|
+
- SQLProcessor for statement compilation and validation
|
|
28
|
+
- Operation type detection (SELECT, INSERT, UPDATE, DELETE, etc.)
|
|
29
|
+
- AST-based SQL analysis using SQLGlot
|
|
30
|
+
- Support for multiple SQL dialects
|
|
31
|
+
- Compiled result caching for performance
|
|
32
|
+
|
|
33
|
+
result.py: Comprehensive result handling for all SQL operations
|
|
34
|
+
- SQLResult for standard query results with metadata
|
|
35
|
+
- ArrowResult for Apache Arrow format integration
|
|
36
|
+
- Support for DML operations with RETURNING clauses
|
|
37
|
+
- Script execution result aggregation
|
|
38
|
+
- Iterator protocol support for result rows
|
|
39
|
+
|
|
40
|
+
filters.py: Composable SQL statement filters
|
|
41
|
+
- BeforeAfterFilter for date range filtering
|
|
42
|
+
- InCollectionFilter for IN clause generation
|
|
43
|
+
- LimitOffsetFilter for pagination
|
|
44
|
+
- OrderByFilter for dynamic sorting
|
|
45
|
+
- SearchFilter for text search operations
|
|
46
|
+
- Parameter conflict resolution
|
|
47
|
+
|
|
48
|
+
cache.py: Unified caching system with LRU eviction
|
|
49
|
+
- UnifiedCache with configurable TTL and size limits
|
|
50
|
+
- StatementCache for compiled SQL statements
|
|
51
|
+
- ExpressionCache for parsed SQLGlot expressions
|
|
52
|
+
- ParameterCache for processed parameters
|
|
53
|
+
- Thread-safe operations with fine-grained locking
|
|
54
|
+
- Cache statistics and monitoring
|
|
55
|
+
|
|
56
|
+
splitter.py: Dialect-aware SQL script splitting
|
|
57
|
+
- Support for Oracle PL/SQL, T-SQL, PostgreSQL, MySQL
|
|
58
|
+
- Proper handling of block structures (BEGIN/END)
|
|
59
|
+
- Dollar-quoted string support for PostgreSQL
|
|
60
|
+
- Batch separator recognition (GO for T-SQL)
|
|
61
|
+
- Comment and string literal preservation
|
|
62
|
+
|
|
63
|
+
hashing.py: Efficient cache key generation
|
|
64
|
+
- SQL statement hashing with parameter consideration
|
|
65
|
+
- Expression tree hashing for AST caching
|
|
66
|
+
- Parameter set hashing for batch operations
|
|
67
|
+
- Optimized hash computation with caching
|
|
68
|
+
|
|
69
|
+
Performance Optimizations:
|
|
70
|
+
- MyPyC compilation support with proper annotations
|
|
71
|
+
- __slots__ usage for memory efficiency
|
|
72
|
+
- Final annotations for constant folding
|
|
73
|
+
- Lazy evaluation and compilation
|
|
74
|
+
- Comprehensive result caching
|
|
75
|
+
- Minimal object allocation in hot paths
|
|
76
|
+
|
|
77
|
+
Thread Safety:
|
|
78
|
+
All caching components are thread-safe with RLock protection.
|
|
79
|
+
The processing pipeline is stateless and safe for concurrent use.
|
|
80
|
+
|
|
81
|
+
Example Usage:
|
|
82
|
+
>>> from sqlspec.core import SQL, StatementConfig
|
|
83
|
+
>>> config = StatementConfig(dialect="postgresql")
|
|
84
|
+
>>> stmt = SQL(
|
|
85
|
+
... "SELECT * FROM users WHERE id = ?",
|
|
86
|
+
... 1,
|
|
87
|
+
... statement_config=config,
|
|
88
|
+
... )
|
|
89
|
+
>>> compiled_sql, params = stmt.compile()
|
|
15
90
|
"""
|
|
16
91
|
|
|
17
92
|
from sqlspec.core import filters
|