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.

Files changed (54) hide show
  1. sqlspec/__init__.py +15 -0
  2. sqlspec/_serialization.py +16 -2
  3. sqlspec/_typing.py +40 -7
  4. sqlspec/adapters/adbc/__init__.py +7 -0
  5. sqlspec/adapters/adbc/config.py +183 -17
  6. sqlspec/adapters/adbc/driver.py +392 -0
  7. sqlspec/adapters/aiosqlite/__init__.py +5 -1
  8. sqlspec/adapters/aiosqlite/config.py +24 -6
  9. sqlspec/adapters/aiosqlite/driver.py +264 -0
  10. sqlspec/adapters/asyncmy/__init__.py +7 -2
  11. sqlspec/adapters/asyncmy/config.py +71 -11
  12. sqlspec/adapters/asyncmy/driver.py +246 -0
  13. sqlspec/adapters/asyncpg/__init__.py +9 -0
  14. sqlspec/adapters/asyncpg/config.py +102 -25
  15. sqlspec/adapters/asyncpg/driver.py +444 -0
  16. sqlspec/adapters/duckdb/__init__.py +5 -1
  17. sqlspec/adapters/duckdb/config.py +194 -12
  18. sqlspec/adapters/duckdb/driver.py +225 -0
  19. sqlspec/adapters/oracledb/__init__.py +7 -4
  20. sqlspec/adapters/oracledb/config/__init__.py +4 -4
  21. sqlspec/adapters/oracledb/config/_asyncio.py +96 -12
  22. sqlspec/adapters/oracledb/config/_common.py +1 -1
  23. sqlspec/adapters/oracledb/config/_sync.py +96 -12
  24. sqlspec/adapters/oracledb/driver.py +571 -0
  25. sqlspec/adapters/psqlpy/__init__.py +0 -0
  26. sqlspec/adapters/psqlpy/config.py +258 -0
  27. sqlspec/adapters/psqlpy/driver.py +335 -0
  28. sqlspec/adapters/psycopg/__init__.py +16 -0
  29. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  30. sqlspec/adapters/psycopg/config/_async.py +107 -15
  31. sqlspec/adapters/psycopg/config/_common.py +2 -2
  32. sqlspec/adapters/psycopg/config/_sync.py +107 -15
  33. sqlspec/adapters/psycopg/driver.py +578 -0
  34. sqlspec/adapters/sqlite/__init__.py +7 -0
  35. sqlspec/adapters/sqlite/config.py +24 -6
  36. sqlspec/adapters/sqlite/driver.py +305 -0
  37. sqlspec/base.py +565 -63
  38. sqlspec/exceptions.py +30 -0
  39. sqlspec/extensions/litestar/__init__.py +19 -0
  40. sqlspec/extensions/litestar/_utils.py +56 -0
  41. sqlspec/extensions/litestar/config.py +87 -0
  42. sqlspec/extensions/litestar/handlers.py +213 -0
  43. sqlspec/extensions/litestar/plugin.py +105 -11
  44. sqlspec/statement.py +373 -0
  45. sqlspec/typing.py +81 -17
  46. sqlspec/utils/__init__.py +3 -0
  47. sqlspec/utils/fixtures.py +4 -5
  48. sqlspec/utils/sync_tools.py +335 -0
  49. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/METADATA +4 -1
  50. sqlspec-0.9.0.dist-info/RECORD +61 -0
  51. sqlspec-0.7.1.dist-info/RECORD +0 -46
  52. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/WHEEL +0 -0
  53. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/licenses/LICENSE +0 -0
  54. {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 AsyncGenerator, Awaitable, Generator
3
- from contextlib import AbstractAsyncContextManager, AbstractContextManager
4
- from dataclasses import dataclass
5
- from typing import Annotated, Any, ClassVar, Generic, TypeVar, Union, cast, overload
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
- AsyncConfigT = TypeVar("AsyncConfigT", bound="Union[AsyncDatabaseConfig[Any, Any], NoPoolAsyncConfig[Any]]")
19
- SyncConfigT = TypeVar("SyncConfigT", bound="Union[SyncDatabaseConfig[Any, Any], NoPoolSyncConfig[Any]]")
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], ABC):
68
+ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
24
69
  """Protocol defining the interface for database configurations."""
25
70
 
26
- __is_async__: ClassVar[bool] = False
27
- __supports_connection_pooling__: ClassVar[bool] = False
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 ConfigManager:
134
- """Type-safe configuration manager with literal inference."""
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
- SyncConfigT,
149
- AsyncConfigT,
150
- ],
151
- ) -> Union[Annotated[type[SyncConfigT], int], Annotated[type[AsyncConfigT], int]]: # pyright: ignore[reportInvalidTypeVarUse]
152
- """Add a new configuration to the manager."""
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(self, name: type[Union[NoPoolSyncConfig[ConnectionT], NoPoolAsyncConfig[ConnectionT]]]) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
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 isinstance(config, (NoPoolSyncConfig, NoPoolAsyncConfig)):
226
- return None
227
- return cast("Union[type[PoolT], Awaitable[type[PoolT]]]", config.create_pool())
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]]