sqlspec 0.7.1__py3-none-any.whl → 0.9.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 +15 -0
- sqlspec/_serialization.py +16 -2
- sqlspec/_typing.py +40 -7
- sqlspec/adapters/adbc/__init__.py +7 -0
- sqlspec/adapters/adbc/config.py +183 -17
- sqlspec/adapters/adbc/driver.py +392 -0
- sqlspec/adapters/aiosqlite/__init__.py +5 -1
- sqlspec/adapters/aiosqlite/config.py +24 -6
- sqlspec/adapters/aiosqlite/driver.py +264 -0
- sqlspec/adapters/asyncmy/__init__.py +7 -2
- sqlspec/adapters/asyncmy/config.py +71 -11
- sqlspec/adapters/asyncmy/driver.py +246 -0
- sqlspec/adapters/asyncpg/__init__.py +9 -0
- sqlspec/adapters/asyncpg/config.py +102 -25
- sqlspec/adapters/asyncpg/driver.py +444 -0
- sqlspec/adapters/duckdb/__init__.py +5 -1
- sqlspec/adapters/duckdb/config.py +194 -12
- sqlspec/adapters/duckdb/driver.py +225 -0
- sqlspec/adapters/oracledb/__init__.py +7 -4
- sqlspec/adapters/oracledb/config/__init__.py +4 -4
- sqlspec/adapters/oracledb/config/_asyncio.py +96 -12
- sqlspec/adapters/oracledb/config/_common.py +1 -1
- sqlspec/adapters/oracledb/config/_sync.py +96 -12
- sqlspec/adapters/oracledb/driver.py +571 -0
- sqlspec/adapters/psqlpy/__init__.py +0 -0
- sqlspec/adapters/psqlpy/config.py +258 -0
- sqlspec/adapters/psqlpy/driver.py +335 -0
- sqlspec/adapters/psycopg/__init__.py +16 -0
- sqlspec/adapters/psycopg/config/__init__.py +6 -6
- sqlspec/adapters/psycopg/config/_async.py +107 -15
- sqlspec/adapters/psycopg/config/_common.py +2 -2
- sqlspec/adapters/psycopg/config/_sync.py +107 -15
- sqlspec/adapters/psycopg/driver.py +578 -0
- sqlspec/adapters/sqlite/__init__.py +7 -0
- sqlspec/adapters/sqlite/config.py +24 -6
- sqlspec/adapters/sqlite/driver.py +305 -0
- sqlspec/base.py +565 -63
- sqlspec/exceptions.py +30 -0
- sqlspec/extensions/litestar/__init__.py +19 -0
- sqlspec/extensions/litestar/_utils.py +56 -0
- sqlspec/extensions/litestar/config.py +87 -0
- sqlspec/extensions/litestar/handlers.py +213 -0
- sqlspec/extensions/litestar/plugin.py +105 -11
- sqlspec/statement.py +373 -0
- sqlspec/typing.py +81 -17
- sqlspec/utils/__init__.py +3 -0
- sqlspec/utils/fixtures.py +4 -5
- sqlspec/utils/sync_tools.py +335 -0
- {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/METADATA +4 -1
- sqlspec-0.9.0.dist-info/RECORD +61 -0
- sqlspec-0.7.1.dist-info/RECORD +0 -46
- {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/base.py
CHANGED
|
@@ -1,36 +1,84 @@
|
|
|
1
|
+
# ruff: noqa: PLR6301
|
|
2
|
+
import re
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
|
|
4
|
+
from collections.abc import Awaitable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Annotated,
|
|
9
|
+
Any,
|
|
10
|
+
ClassVar,
|
|
11
|
+
Generic,
|
|
12
|
+
Optional,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Union,
|
|
15
|
+
cast,
|
|
16
|
+
overload,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from sqlspec.exceptions import NotFoundError
|
|
20
|
+
from sqlspec.statement import SQLStatement
|
|
21
|
+
from sqlspec.typing import ModelDTOT, StatementParameterType
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
25
|
+
|
|
26
|
+
from pyarrow import Table as ArrowTable
|
|
6
27
|
|
|
7
28
|
__all__ = (
|
|
29
|
+
"AsyncArrowBulkOperationsMixin",
|
|
8
30
|
"AsyncDatabaseConfig",
|
|
31
|
+
"AsyncDriverAdapterProtocol",
|
|
32
|
+
"CommonDriverAttributes",
|
|
9
33
|
"DatabaseConfigProtocol",
|
|
10
34
|
"GenericPoolConfig",
|
|
11
35
|
"NoPoolAsyncConfig",
|
|
12
36
|
"NoPoolSyncConfig",
|
|
37
|
+
"SQLSpec",
|
|
38
|
+
"SQLStatement",
|
|
39
|
+
"SyncArrowBulkOperationsMixin",
|
|
13
40
|
"SyncDatabaseConfig",
|
|
41
|
+
"SyncDriverAdapterProtocol",
|
|
14
42
|
)
|
|
15
43
|
|
|
44
|
+
T = TypeVar("T")
|
|
16
45
|
ConnectionT = TypeVar("ConnectionT")
|
|
17
46
|
PoolT = TypeVar("PoolT")
|
|
18
|
-
|
|
19
|
-
|
|
47
|
+
PoolT_co = TypeVar("PoolT_co", covariant=True)
|
|
48
|
+
AsyncConfigT = TypeVar("AsyncConfigT", bound="Union[AsyncDatabaseConfig[Any, Any, Any], NoPoolAsyncConfig[Any, Any]]")
|
|
49
|
+
SyncConfigT = TypeVar("SyncConfigT", bound="Union[SyncDatabaseConfig[Any, Any, Any], NoPoolSyncConfig[Any, Any]]")
|
|
50
|
+
ConfigT = TypeVar(
|
|
51
|
+
"ConfigT",
|
|
52
|
+
bound="Union[Union[AsyncDatabaseConfig[Any, Any, Any], NoPoolAsyncConfig[Any, Any]], SyncDatabaseConfig[Any, Any, Any], NoPoolSyncConfig[Any, Any]]",
|
|
53
|
+
)
|
|
54
|
+
DriverT = TypeVar("DriverT", bound="Union[SyncDriverAdapterProtocol[Any], AsyncDriverAdapterProtocol[Any]]")
|
|
55
|
+
# Regex to find :param or %(param)s style placeholders, skipping those inside quotes
|
|
56
|
+
PARAM_REGEX = re.compile(
|
|
57
|
+
r"""
|
|
58
|
+
(?P<dquote>"([^"]|\\")*") | # Double-quoted strings
|
|
59
|
+
(?P<squote>'([^']|\\')*') | # Single-quoted strings
|
|
60
|
+
: (?P<var_name_colon>[a-zA-Z_][a-zA-Z0-9_]*) | # :var_name
|
|
61
|
+
% \( (?P<var_name_perc>[a-zA-Z_][a-zA-Z0-9_]*) \) s # %(var_name)s
|
|
62
|
+
""",
|
|
63
|
+
re.VERBOSE,
|
|
64
|
+
)
|
|
20
65
|
|
|
21
66
|
|
|
22
67
|
@dataclass
|
|
23
|
-
class DatabaseConfigProtocol(Generic[ConnectionT, PoolT
|
|
68
|
+
class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
|
|
24
69
|
"""Protocol defining the interface for database configurations."""
|
|
25
70
|
|
|
26
|
-
|
|
27
|
-
|
|
71
|
+
connection_type: "type[ConnectionT]" = field(init=False)
|
|
72
|
+
driver_type: "type[DriverT]" = field(init=False)
|
|
73
|
+
pool_instance: "Optional[PoolT]" = field(default=None)
|
|
74
|
+
__is_async__: "ClassVar[bool]" = False
|
|
75
|
+
__supports_connection_pooling__: "ClassVar[bool]" = False
|
|
28
76
|
|
|
29
77
|
def __hash__(self) -> int:
|
|
30
78
|
return id(self)
|
|
31
79
|
|
|
32
80
|
@abstractmethod
|
|
33
|
-
def create_connection(self) -> Union[ConnectionT, Awaitable[ConnectionT]]:
|
|
81
|
+
def create_connection(self) -> "Union[ConnectionT, Awaitable[ConnectionT]]":
|
|
34
82
|
"""Create and return a new database connection."""
|
|
35
83
|
raise NotImplementedError
|
|
36
84
|
|
|
@@ -39,32 +87,41 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
39
87
|
self,
|
|
40
88
|
*args: Any,
|
|
41
89
|
**kwargs: Any,
|
|
42
|
-
) -> Union[
|
|
43
|
-
Generator[ConnectionT, None, None],
|
|
44
|
-
AsyncGenerator[ConnectionT, None],
|
|
45
|
-
AbstractContextManager[ConnectionT],
|
|
46
|
-
AbstractAsyncContextManager[ConnectionT],
|
|
47
|
-
]:
|
|
90
|
+
) -> "Union[AbstractContextManager[ConnectionT], AbstractAsyncContextManager[ConnectionT]]":
|
|
48
91
|
"""Provide a database connection context manager."""
|
|
49
92
|
raise NotImplementedError
|
|
50
93
|
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def provide_session(
|
|
96
|
+
self,
|
|
97
|
+
*args: Any,
|
|
98
|
+
**kwargs: Any,
|
|
99
|
+
) -> "Union[AbstractContextManager[DriverT], AbstractAsyncContextManager[DriverT]]":
|
|
100
|
+
"""Provide a database session context manager."""
|
|
101
|
+
raise NotImplementedError
|
|
102
|
+
|
|
51
103
|
@property
|
|
52
104
|
@abstractmethod
|
|
53
|
-
def connection_config_dict(self) -> dict[str, Any]:
|
|
105
|
+
def connection_config_dict(self) -> "dict[str, Any]":
|
|
54
106
|
"""Return the connection configuration as a dict."""
|
|
55
107
|
raise NotImplementedError
|
|
56
108
|
|
|
57
109
|
@abstractmethod
|
|
58
|
-
def create_pool(self) -> Union[PoolT, Awaitable[PoolT]]:
|
|
110
|
+
def create_pool(self) -> "Union[PoolT, Awaitable[PoolT]]":
|
|
59
111
|
"""Create and return connection pool."""
|
|
60
112
|
raise NotImplementedError
|
|
61
113
|
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def close_pool(self) -> "Optional[Awaitable[None]]":
|
|
116
|
+
"""Terminate the connection pool."""
|
|
117
|
+
raise NotImplementedError
|
|
118
|
+
|
|
62
119
|
@abstractmethod
|
|
63
120
|
def provide_pool(
|
|
64
121
|
self,
|
|
65
122
|
*args: Any,
|
|
66
123
|
**kwargs: Any,
|
|
67
|
-
) -> Union[PoolT, Awaitable[PoolT], AbstractContextManager[PoolT], AbstractAsyncContextManager[PoolT]]:
|
|
124
|
+
) -> "Union[PoolT, Awaitable[PoolT], AbstractContextManager[PoolT], AbstractAsyncContextManager[PoolT]]":
|
|
68
125
|
"""Provide pool instance."""
|
|
69
126
|
raise NotImplementedError
|
|
70
127
|
|
|
@@ -79,31 +136,39 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
79
136
|
return self.__supports_connection_pooling__
|
|
80
137
|
|
|
81
138
|
|
|
82
|
-
class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None]):
|
|
139
|
+
class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
|
|
83
140
|
"""Base class for a sync database configurations that do not implement a pool."""
|
|
84
141
|
|
|
85
142
|
__is_async__ = False
|
|
86
143
|
__supports_connection_pooling__ = False
|
|
144
|
+
pool_instance: None = None
|
|
87
145
|
|
|
88
146
|
def create_pool(self) -> None:
|
|
89
147
|
"""This database backend has not implemented the pooling configurations."""
|
|
90
148
|
return
|
|
91
149
|
|
|
150
|
+
def close_pool(self) -> None:
|
|
151
|
+
return
|
|
152
|
+
|
|
92
153
|
def provide_pool(self, *args: Any, **kwargs: Any) -> None:
|
|
93
154
|
"""This database backend has not implemented the pooling configurations."""
|
|
94
155
|
return
|
|
95
156
|
|
|
96
157
|
|
|
97
|
-
class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None]):
|
|
158
|
+
class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
|
|
98
159
|
"""Base class for an async database configurations that do not implement a pool."""
|
|
99
160
|
|
|
100
161
|
__is_async__ = True
|
|
101
162
|
__supports_connection_pooling__ = False
|
|
163
|
+
pool_instance: None = None
|
|
102
164
|
|
|
103
165
|
async def create_pool(self) -> None:
|
|
104
166
|
"""This database backend has not implemented the pooling configurations."""
|
|
105
167
|
return
|
|
106
168
|
|
|
169
|
+
async def close_pool(self) -> None:
|
|
170
|
+
return
|
|
171
|
+
|
|
107
172
|
def provide_pool(self, *args: Any, **kwargs: Any) -> None:
|
|
108
173
|
"""This database backend has not implemented the pooling configurations."""
|
|
109
174
|
return
|
|
@@ -115,7 +180,7 @@ class GenericPoolConfig:
|
|
|
115
180
|
|
|
116
181
|
|
|
117
182
|
@dataclass
|
|
118
|
-
class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT]):
|
|
183
|
+
class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
|
|
119
184
|
"""Generic Sync Database Configuration."""
|
|
120
185
|
|
|
121
186
|
__is_async__ = False
|
|
@@ -123,48 +188,58 @@ class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT]):
|
|
|
123
188
|
|
|
124
189
|
|
|
125
190
|
@dataclass
|
|
126
|
-
class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT]):
|
|
191
|
+
class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
|
|
127
192
|
"""Generic Async Database Configuration."""
|
|
128
193
|
|
|
129
194
|
__is_async__ = True
|
|
130
195
|
__supports_connection_pooling__ = True
|
|
131
196
|
|
|
132
197
|
|
|
133
|
-
class
|
|
134
|
-
"""Type-safe configuration manager
|
|
198
|
+
class SQLSpec:
|
|
199
|
+
"""Type-safe configuration manager and registry for database connections and pools."""
|
|
200
|
+
|
|
201
|
+
__slots__ = ("_configs",)
|
|
135
202
|
|
|
136
203
|
def __init__(self) -> None:
|
|
137
|
-
self._configs: dict[Any, DatabaseConfigProtocol[Any, Any]] = {}
|
|
204
|
+
self._configs: dict[Any, DatabaseConfigProtocol[Any, Any, Any]] = {}
|
|
138
205
|
|
|
139
206
|
@overload
|
|
140
|
-
def add_config(self, config: SyncConfigT) -> type[SyncConfigT]: ...
|
|
207
|
+
def add_config(self, config: "SyncConfigT") -> "type[SyncConfigT]": ...
|
|
141
208
|
|
|
142
209
|
@overload
|
|
143
|
-
def add_config(self, config: AsyncConfigT) -> type[AsyncConfigT]: ...
|
|
210
|
+
def add_config(self, config: "AsyncConfigT") -> "type[AsyncConfigT]": ...
|
|
144
211
|
|
|
145
212
|
def add_config(
|
|
146
213
|
self,
|
|
147
|
-
config: Union[
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
214
|
+
config: "Union[SyncConfigT, AsyncConfigT]",
|
|
215
|
+
) -> "Union[Annotated[type[SyncConfigT], int], Annotated[type[AsyncConfigT], int]]": # pyright: ignore[reportInvalidTypeVarUse]
|
|
216
|
+
"""Add a new configuration to the manager.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
A unique type key that can be used to retrieve the configuration later.
|
|
220
|
+
"""
|
|
153
221
|
key = Annotated[type(config), id(config)] # type: ignore[valid-type]
|
|
154
222
|
self._configs[key] = config
|
|
155
223
|
return key # type: ignore[return-value] # pyright: ignore[reportReturnType]
|
|
156
224
|
|
|
157
225
|
@overload
|
|
158
|
-
def get_config(self, name: type[SyncConfigT]) -> SyncConfigT: ...
|
|
226
|
+
def get_config(self, name: "type[SyncConfigT]") -> "SyncConfigT": ...
|
|
159
227
|
|
|
160
228
|
@overload
|
|
161
|
-
def get_config(self, name: type[AsyncConfigT]) -> AsyncConfigT: ...
|
|
229
|
+
def get_config(self, name: "type[AsyncConfigT]") -> "AsyncConfigT": ...
|
|
162
230
|
|
|
163
231
|
def get_config(
|
|
164
232
|
self,
|
|
165
|
-
name: Union[type[DatabaseConfigProtocol[ConnectionT, PoolT]], Any],
|
|
166
|
-
) -> DatabaseConfigProtocol[ConnectionT, PoolT]:
|
|
167
|
-
"""Retrieve a configuration by its type.
|
|
233
|
+
name: "Union[type[DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]], Any]",
|
|
234
|
+
) -> "DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]":
|
|
235
|
+
"""Retrieve a configuration by its type.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
DatabaseConfigProtocol: The configuration instance for the given type.
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
KeyError: If no configuration is found for the given type.
|
|
242
|
+
"""
|
|
168
243
|
config = self._configs.get(name)
|
|
169
244
|
if not config:
|
|
170
245
|
msg = f"No configuration found for {name}"
|
|
@@ -175,53 +250,480 @@ class ConfigManager:
|
|
|
175
250
|
def get_connection(
|
|
176
251
|
self,
|
|
177
252
|
name: Union[
|
|
178
|
-
type[NoPoolSyncConfig[ConnectionT]],
|
|
179
|
-
type[SyncDatabaseConfig[ConnectionT, PoolT]], # pyright: ignore[reportInvalidTypeVarUse]
|
|
253
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
254
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]", # pyright: ignore[reportInvalidTypeVarUse]
|
|
180
255
|
],
|
|
181
|
-
) -> ConnectionT: ...
|
|
256
|
+
) -> "ConnectionT": ...
|
|
182
257
|
|
|
183
258
|
@overload
|
|
184
259
|
def get_connection(
|
|
185
260
|
self,
|
|
186
261
|
name: Union[
|
|
187
|
-
type[NoPoolAsyncConfig[ConnectionT]],
|
|
188
|
-
type[AsyncDatabaseConfig[ConnectionT, PoolT]], # pyright: ignore[reportInvalidTypeVarUse]
|
|
262
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
263
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]", # pyright: ignore[reportInvalidTypeVarUse]
|
|
189
264
|
],
|
|
190
|
-
) -> Awaitable[ConnectionT]: ...
|
|
265
|
+
) -> "Awaitable[ConnectionT]": ...
|
|
191
266
|
|
|
192
267
|
def get_connection(
|
|
193
268
|
self,
|
|
194
269
|
name: Union[
|
|
195
|
-
type[NoPoolSyncConfig[ConnectionT]],
|
|
196
|
-
type[NoPoolAsyncConfig[ConnectionT]],
|
|
197
|
-
type[SyncDatabaseConfig[ConnectionT, PoolT]],
|
|
198
|
-
type[AsyncDatabaseConfig[ConnectionT, PoolT]],
|
|
270
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
271
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
272
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
273
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
199
274
|
],
|
|
200
|
-
) -> Union[ConnectionT, Awaitable[ConnectionT]]:
|
|
201
|
-
"""Create and return a connection from the specified configuration.
|
|
275
|
+
) -> "Union[ConnectionT, Awaitable[ConnectionT]]":
|
|
276
|
+
"""Create and return a new database connection from the specified configuration.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
name: The configuration type to use for creating the connection.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Either a connection instance or an awaitable that resolves to a connection instance.
|
|
283
|
+
"""
|
|
202
284
|
config = self.get_config(name)
|
|
203
285
|
return config.create_connection()
|
|
204
286
|
|
|
287
|
+
def get_session(
|
|
288
|
+
self,
|
|
289
|
+
name: Union[
|
|
290
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
291
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
292
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
293
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
294
|
+
],
|
|
295
|
+
) -> "Union[DriverT, Awaitable[DriverT]]":
|
|
296
|
+
"""Create and return a new database session from the specified configuration.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
name: The configuration type to use for creating the session.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Either a driver instance or an awaitable that resolves to a driver instance.
|
|
303
|
+
"""
|
|
304
|
+
config = self.get_config(name)
|
|
305
|
+
connection = self.get_connection(name)
|
|
306
|
+
if isinstance(connection, Awaitable):
|
|
307
|
+
|
|
308
|
+
async def _create_session() -> DriverT:
|
|
309
|
+
return cast("DriverT", config.driver_type(await connection)) # pyright: ignore
|
|
310
|
+
|
|
311
|
+
return _create_session()
|
|
312
|
+
return cast("DriverT", config.driver_type(connection)) # pyright: ignore
|
|
313
|
+
|
|
314
|
+
def provide_connection(
|
|
315
|
+
self,
|
|
316
|
+
name: Union[
|
|
317
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
318
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
319
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
320
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
321
|
+
],
|
|
322
|
+
*args: Any,
|
|
323
|
+
**kwargs: Any,
|
|
324
|
+
) -> "Union[AbstractContextManager[ConnectionT], AbstractAsyncContextManager[ConnectionT]]":
|
|
325
|
+
"""Create and provide a database connection from the specified configuration.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
name: The configuration type to use for creating the connection.
|
|
329
|
+
*args: Positional arguments to pass to the configuration's provide_connection method.
|
|
330
|
+
**kwargs: Keyword arguments to pass to the configuration's provide_connection method.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Either a synchronous or asynchronous context manager that provides a database connection.
|
|
334
|
+
"""
|
|
335
|
+
config = self.get_config(name)
|
|
336
|
+
return config.provide_connection(*args, **kwargs)
|
|
337
|
+
|
|
338
|
+
def provide_session(
|
|
339
|
+
self,
|
|
340
|
+
name: Union[
|
|
341
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
342
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
343
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
344
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
345
|
+
],
|
|
346
|
+
*args: Any,
|
|
347
|
+
**kwargs: Any,
|
|
348
|
+
) -> "Union[AbstractContextManager[DriverT], AbstractAsyncContextManager[DriverT]]":
|
|
349
|
+
"""Create and provide a database session from the specified configuration.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
name: The configuration type to use for creating the session.
|
|
353
|
+
*args: Positional arguments to pass to the configuration's provide_session method.
|
|
354
|
+
**kwargs: Keyword arguments to pass to the configuration's provide_session method.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
Either a synchronous or asynchronous context manager that provides a database session.
|
|
358
|
+
"""
|
|
359
|
+
config = self.get_config(name)
|
|
360
|
+
return config.provide_session(*args, **kwargs)
|
|
361
|
+
|
|
205
362
|
@overload
|
|
206
|
-
def get_pool(
|
|
363
|
+
def get_pool(
|
|
364
|
+
self, name: "type[Union[NoPoolSyncConfig[ConnectionT, DriverT], NoPoolAsyncConfig[ConnectionT, DriverT]]]"
|
|
365
|
+
) -> "None": ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
207
366
|
|
|
208
367
|
@overload
|
|
209
|
-
def get_pool(self, name: type[SyncDatabaseConfig[ConnectionT, PoolT]]) -> type[PoolT]: ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
368
|
+
def get_pool(self, name: "type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]") -> "type[PoolT]": ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
210
369
|
|
|
211
370
|
@overload
|
|
212
|
-
def get_pool(self, name: type[AsyncDatabaseConfig[ConnectionT, PoolT]]) -> Awaitable[type[PoolT]]: ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
371
|
+
def get_pool(self, name: "type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]") -> "Awaitable[type[PoolT]]": ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
213
372
|
|
|
214
373
|
def get_pool(
|
|
215
374
|
self,
|
|
216
375
|
name: Union[
|
|
217
|
-
type[NoPoolSyncConfig[ConnectionT]],
|
|
218
|
-
type[NoPoolAsyncConfig[ConnectionT]],
|
|
219
|
-
type[SyncDatabaseConfig[ConnectionT, PoolT]],
|
|
220
|
-
type[AsyncDatabaseConfig[ConnectionT, PoolT]],
|
|
376
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
377
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
378
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
379
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
221
380
|
],
|
|
222
|
-
) -> Union[type[PoolT], Awaitable[type[PoolT]], None]:
|
|
223
|
-
"""Create and return a connection pool from the specified configuration.
|
|
381
|
+
) -> "Union[type[PoolT], Awaitable[type[PoolT]], None]":
|
|
382
|
+
"""Create and return a connection pool from the specified configuration.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
name: The configuration type to use for creating the pool.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
Either a pool instance, an awaitable that resolves to a pool instance, or None
|
|
389
|
+
if the configuration does not support connection pooling.
|
|
390
|
+
"""
|
|
224
391
|
config = self.get_config(name)
|
|
225
|
-
if
|
|
226
|
-
return
|
|
227
|
-
return
|
|
392
|
+
if config.support_connection_pooling:
|
|
393
|
+
return cast("Union[type[PoolT], Awaitable[type[PoolT]]]", config.create_pool())
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
def close_pool(
|
|
397
|
+
self,
|
|
398
|
+
name: Union[
|
|
399
|
+
"type[NoPoolSyncConfig[ConnectionT, DriverT]]",
|
|
400
|
+
"type[NoPoolAsyncConfig[ConnectionT, DriverT]]",
|
|
401
|
+
"type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
402
|
+
"type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]]",
|
|
403
|
+
],
|
|
404
|
+
) -> "Optional[Awaitable[None]]":
|
|
405
|
+
"""Close the connection pool for the specified configuration.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
name: The configuration type whose pool to close.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
An awaitable if the configuration is async, otherwise None.
|
|
412
|
+
"""
|
|
413
|
+
config = self.get_config(name)
|
|
414
|
+
if config.support_connection_pooling:
|
|
415
|
+
return config.close_pool()
|
|
416
|
+
return None
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class CommonDriverAttributes(Generic[ConnectionT]):
|
|
420
|
+
"""Common attributes and methods for driver adapters."""
|
|
421
|
+
|
|
422
|
+
dialect: str
|
|
423
|
+
"""The SQL dialect supported by the underlying database driver (e.g., 'postgres', 'mysql')."""
|
|
424
|
+
connection: ConnectionT
|
|
425
|
+
"""The connection to the underlying database."""
|
|
426
|
+
__supports_arrow__: ClassVar[bool] = False
|
|
427
|
+
"""Indicates if the driver supports Apache Arrow operations."""
|
|
428
|
+
|
|
429
|
+
def _connection(self, connection: "Optional[ConnectionT]" = None) -> "ConnectionT":
|
|
430
|
+
return connection if connection is not None else self.connection
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def check_not_found(item_or_none: Optional[T] = None) -> T:
|
|
434
|
+
"""Raise :exc:`sqlspec.exceptions.NotFoundError` if ``item_or_none`` is ``None``.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
item_or_none: Item to be tested for existence.
|
|
438
|
+
|
|
439
|
+
Raises:
|
|
440
|
+
NotFoundError: If ``item_or_none`` is ``None``
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
The item, if it exists.
|
|
444
|
+
"""
|
|
445
|
+
if item_or_none is None:
|
|
446
|
+
msg = "No result found when one was expected"
|
|
447
|
+
raise NotFoundError(msg)
|
|
448
|
+
return item_or_none
|
|
449
|
+
|
|
450
|
+
def _process_sql_params(
|
|
451
|
+
self, sql: str, parameters: "Optional[StatementParameterType]" = None, /, **kwargs: Any
|
|
452
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
453
|
+
"""Process SQL query and parameters using SQLStatement for validation and formatting.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
sql: The SQL query string.
|
|
457
|
+
parameters: Parameters for the query.
|
|
458
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
A tuple containing the processed SQL query and parameters.
|
|
462
|
+
"""
|
|
463
|
+
# Instantiate SQLStatement with parameters and kwargs for internal merging
|
|
464
|
+
stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
|
|
465
|
+
# Process uses the merged parameters internally
|
|
466
|
+
return stmt.process()
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class SyncArrowBulkOperationsMixin(Generic[ConnectionT]):
|
|
470
|
+
"""Mixin for sync drivers supporting bulk Apache Arrow operations."""
|
|
471
|
+
|
|
472
|
+
__supports_arrow__: "ClassVar[bool]" = True
|
|
473
|
+
|
|
474
|
+
@abstractmethod
|
|
475
|
+
def select_arrow( # pyright: ignore[reportUnknownParameterType]
|
|
476
|
+
self,
|
|
477
|
+
sql: str,
|
|
478
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
479
|
+
/,
|
|
480
|
+
*,
|
|
481
|
+
connection: "Optional[ConnectionT]" = None,
|
|
482
|
+
**kwargs: Any,
|
|
483
|
+
) -> "ArrowTable": # pyright: ignore[reportUnknownReturnType]
|
|
484
|
+
"""Execute a SQL query and return results as an Apache Arrow Table.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
sql: The SQL query string.
|
|
488
|
+
parameters: Parameters for the query.
|
|
489
|
+
connection: Optional connection override.
|
|
490
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
An Apache Arrow Table containing the query results.
|
|
494
|
+
"""
|
|
495
|
+
raise NotImplementedError
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
class SyncDriverAdapterProtocol(CommonDriverAttributes[ConnectionT], ABC, Generic[ConnectionT]):
|
|
499
|
+
connection: "ConnectionT"
|
|
500
|
+
|
|
501
|
+
def __init__(self, connection: "ConnectionT", **kwargs: Any) -> None:
|
|
502
|
+
self.connection = connection
|
|
503
|
+
|
|
504
|
+
@abstractmethod
|
|
505
|
+
def select(
|
|
506
|
+
self,
|
|
507
|
+
sql: str,
|
|
508
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
509
|
+
/,
|
|
510
|
+
*,
|
|
511
|
+
connection: "Optional[ConnectionT]" = None,
|
|
512
|
+
schema_type: Optional[type[ModelDTOT]] = None,
|
|
513
|
+
**kwargs: Any,
|
|
514
|
+
) -> "list[Union[ModelDTOT, dict[str, Any]]]": ...
|
|
515
|
+
|
|
516
|
+
@abstractmethod
|
|
517
|
+
def select_one(
|
|
518
|
+
self,
|
|
519
|
+
sql: str,
|
|
520
|
+
parameters: Optional[StatementParameterType] = None,
|
|
521
|
+
/,
|
|
522
|
+
*,
|
|
523
|
+
connection: Optional[ConnectionT] = None,
|
|
524
|
+
schema_type: Optional[type[ModelDTOT]] = None,
|
|
525
|
+
**kwargs: Any,
|
|
526
|
+
) -> "Union[ModelDTOT, dict[str, Any]]": ...
|
|
527
|
+
|
|
528
|
+
@abstractmethod
|
|
529
|
+
def select_one_or_none(
|
|
530
|
+
self,
|
|
531
|
+
sql: str,
|
|
532
|
+
parameters: Optional[StatementParameterType] = None,
|
|
533
|
+
/,
|
|
534
|
+
*,
|
|
535
|
+
connection: Optional[ConnectionT] = None,
|
|
536
|
+
schema_type: Optional[type[ModelDTOT]] = None,
|
|
537
|
+
**kwargs: Any,
|
|
538
|
+
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]": ...
|
|
539
|
+
|
|
540
|
+
@abstractmethod
|
|
541
|
+
def select_value(
|
|
542
|
+
self,
|
|
543
|
+
sql: str,
|
|
544
|
+
parameters: Optional[StatementParameterType] = None,
|
|
545
|
+
/,
|
|
546
|
+
*,
|
|
547
|
+
connection: Optional[ConnectionT] = None,
|
|
548
|
+
schema_type: Optional[type[T]] = None,
|
|
549
|
+
**kwargs: Any,
|
|
550
|
+
) -> "Union[Any, T]": ...
|
|
551
|
+
|
|
552
|
+
@abstractmethod
|
|
553
|
+
def select_value_or_none(
|
|
554
|
+
self,
|
|
555
|
+
sql: str,
|
|
556
|
+
parameters: Optional[StatementParameterType] = None,
|
|
557
|
+
/,
|
|
558
|
+
*,
|
|
559
|
+
connection: Optional[ConnectionT] = None,
|
|
560
|
+
schema_type: Optional[type[T]] = None,
|
|
561
|
+
**kwargs: Any,
|
|
562
|
+
) -> "Optional[Union[Any, T]]": ...
|
|
563
|
+
|
|
564
|
+
@abstractmethod
|
|
565
|
+
def insert_update_delete(
|
|
566
|
+
self,
|
|
567
|
+
sql: str,
|
|
568
|
+
parameters: Optional[StatementParameterType] = None,
|
|
569
|
+
/,
|
|
570
|
+
*,
|
|
571
|
+
connection: Optional[ConnectionT] = None,
|
|
572
|
+
**kwargs: Any,
|
|
573
|
+
) -> int: ...
|
|
574
|
+
|
|
575
|
+
@abstractmethod
|
|
576
|
+
def insert_update_delete_returning(
|
|
577
|
+
self,
|
|
578
|
+
sql: str,
|
|
579
|
+
parameters: Optional[StatementParameterType] = None,
|
|
580
|
+
/,
|
|
581
|
+
*,
|
|
582
|
+
connection: Optional[ConnectionT] = None,
|
|
583
|
+
schema_type: Optional[type[ModelDTOT]] = None,
|
|
584
|
+
**kwargs: Any,
|
|
585
|
+
) -> "Optional[Union[dict[str, Any], ModelDTOT]]": ...
|
|
586
|
+
|
|
587
|
+
@abstractmethod
|
|
588
|
+
def execute_script(
|
|
589
|
+
self,
|
|
590
|
+
sql: str,
|
|
591
|
+
parameters: Optional[StatementParameterType] = None,
|
|
592
|
+
/,
|
|
593
|
+
*,
|
|
594
|
+
connection: Optional[ConnectionT] = None,
|
|
595
|
+
**kwargs: Any,
|
|
596
|
+
) -> str: ...
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class AsyncArrowBulkOperationsMixin(Generic[ConnectionT]):
|
|
600
|
+
"""Mixin for async drivers supporting bulk Apache Arrow operations."""
|
|
601
|
+
|
|
602
|
+
__supports_arrow__: "ClassVar[bool]" = True
|
|
603
|
+
|
|
604
|
+
@abstractmethod
|
|
605
|
+
async def select_arrow( # pyright: ignore[reportUnknownParameterType]
|
|
606
|
+
self,
|
|
607
|
+
sql: str,
|
|
608
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
609
|
+
/,
|
|
610
|
+
*,
|
|
611
|
+
connection: "Optional[ConnectionT]" = None,
|
|
612
|
+
**kwargs: Any,
|
|
613
|
+
) -> "ArrowTable": # pyright: ignore[reportUnknownReturnType]
|
|
614
|
+
"""Execute a SQL query and return results as an Apache Arrow Table.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
sql: The SQL query string.
|
|
618
|
+
parameters: Parameters for the query.
|
|
619
|
+
connection: Optional connection override.
|
|
620
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
An Apache Arrow Table containing the query results.
|
|
624
|
+
"""
|
|
625
|
+
raise NotImplementedError
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
class AsyncDriverAdapterProtocol(CommonDriverAttributes[ConnectionT], ABC, Generic[ConnectionT]):
|
|
629
|
+
connection: "ConnectionT"
|
|
630
|
+
|
|
631
|
+
def __init__(self, connection: "ConnectionT") -> None:
|
|
632
|
+
self.connection = connection
|
|
633
|
+
|
|
634
|
+
@abstractmethod
|
|
635
|
+
async def select(
|
|
636
|
+
self,
|
|
637
|
+
sql: str,
|
|
638
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
639
|
+
/,
|
|
640
|
+
*,
|
|
641
|
+
connection: "Optional[ConnectionT]" = None,
|
|
642
|
+
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
643
|
+
**kwargs: Any,
|
|
644
|
+
) -> "list[Union[ModelDTOT, dict[str, Any]]]": ...
|
|
645
|
+
|
|
646
|
+
@abstractmethod
|
|
647
|
+
async def select_one(
|
|
648
|
+
self,
|
|
649
|
+
sql: str,
|
|
650
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
651
|
+
/,
|
|
652
|
+
*,
|
|
653
|
+
connection: "Optional[ConnectionT]" = None,
|
|
654
|
+
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
655
|
+
**kwargs: Any,
|
|
656
|
+
) -> "Union[ModelDTOT, dict[str, Any]]": ...
|
|
657
|
+
|
|
658
|
+
@abstractmethod
|
|
659
|
+
async def select_one_or_none(
|
|
660
|
+
self,
|
|
661
|
+
sql: str,
|
|
662
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
663
|
+
/,
|
|
664
|
+
*,
|
|
665
|
+
connection: "Optional[ConnectionT]" = None,
|
|
666
|
+
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
667
|
+
**kwargs: Any,
|
|
668
|
+
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]": ...
|
|
669
|
+
|
|
670
|
+
@abstractmethod
|
|
671
|
+
async def select_value(
|
|
672
|
+
self,
|
|
673
|
+
sql: str,
|
|
674
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
675
|
+
/,
|
|
676
|
+
*,
|
|
677
|
+
connection: "Optional[ConnectionT]" = None,
|
|
678
|
+
schema_type: "Optional[type[T]]" = None,
|
|
679
|
+
**kwargs: Any,
|
|
680
|
+
) -> "Union[Any, T]": ...
|
|
681
|
+
|
|
682
|
+
@abstractmethod
|
|
683
|
+
async def select_value_or_none(
|
|
684
|
+
self,
|
|
685
|
+
sql: str,
|
|
686
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
687
|
+
/,
|
|
688
|
+
*,
|
|
689
|
+
connection: "Optional[ConnectionT]" = None,
|
|
690
|
+
schema_type: "Optional[type[T]]" = None,
|
|
691
|
+
**kwargs: Any,
|
|
692
|
+
) -> "Optional[Union[Any, T]]": ...
|
|
693
|
+
|
|
694
|
+
@abstractmethod
|
|
695
|
+
async def insert_update_delete(
|
|
696
|
+
self,
|
|
697
|
+
sql: str,
|
|
698
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
699
|
+
/,
|
|
700
|
+
*,
|
|
701
|
+
connection: "Optional[ConnectionT]" = None,
|
|
702
|
+
**kwargs: Any,
|
|
703
|
+
) -> int: ...
|
|
704
|
+
|
|
705
|
+
@abstractmethod
|
|
706
|
+
async def insert_update_delete_returning(
|
|
707
|
+
self,
|
|
708
|
+
sql: str,
|
|
709
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
710
|
+
/,
|
|
711
|
+
*,
|
|
712
|
+
connection: "Optional[ConnectionT]" = None,
|
|
713
|
+
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
714
|
+
**kwargs: Any,
|
|
715
|
+
) -> "Optional[Union[dict[str, Any], ModelDTOT]]": ...
|
|
716
|
+
|
|
717
|
+
@abstractmethod
|
|
718
|
+
async def execute_script(
|
|
719
|
+
self,
|
|
720
|
+
sql: str,
|
|
721
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
722
|
+
/,
|
|
723
|
+
*,
|
|
724
|
+
connection: "Optional[ConnectionT]" = None,
|
|
725
|
+
**kwargs: Any,
|
|
726
|
+
) -> str: ...
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
DriverAdapterProtocol = Union[SyncDriverAdapterProtocol[ConnectionT], AsyncDriverAdapterProtocol[ConnectionT]]
|