sqlspec 0.1.0__py3-none-any.whl → 0.3.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/__metadata__.py +1 -1
- sqlspec/_serialization.py +1 -1
- sqlspec/_typing.py +112 -0
- sqlspec/adapters/adbc/__init__.py +0 -0
- sqlspec/adapters/adbc/config.py +54 -0
- sqlspec/adapters/aiosqlite/__init__.py +3 -0
- sqlspec/adapters/aiosqlite/config.py +112 -0
- sqlspec/adapters/asyncmy/__init__.py +3 -0
- sqlspec/adapters/asyncmy/config.py +194 -0
- sqlspec/adapters/asyncpg/__init__.py +0 -0
- sqlspec/adapters/asyncpg/config.py +155 -0
- sqlspec/adapters/duckdb/__init__.py +0 -0
- sqlspec/adapters/duckdb/config.py +101 -0
- sqlspec/adapters/oracledb/__init__.py +13 -0
- sqlspec/adapters/oracledb/config/__init__.py +9 -0
- sqlspec/adapters/oracledb/config/_asyncio.py +98 -0
- sqlspec/adapters/oracledb/config/_common.py +151 -0
- sqlspec/adapters/oracledb/config/_sync.py +102 -0
- sqlspec/adapters/psycopg/__init__.py +0 -0
- sqlspec/adapters/psycopg/config/__init__.py +9 -0
- sqlspec/adapters/psycopg/config/_async.py +84 -0
- sqlspec/adapters/psycopg/config/_common.py +72 -0
- sqlspec/adapters/psycopg/config/_sync.py +84 -0
- sqlspec/adapters/sqlite/__init__.py +0 -0
- sqlspec/adapters/sqlite/config.py +109 -0
- sqlspec/config.py +16 -0
- sqlspec/exceptions.py +29 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/litestar/__init__.py +0 -0
- sqlspec/extensions/litestar/plugin.py +34 -0
- sqlspec/filters.py +33 -28
- sqlspec/typing.py +287 -0
- sqlspec/utils/dataclass.py +11 -3
- sqlspec/{types → utils}/empty.py +1 -1
- sqlspec-0.3.0.dist-info/METADATA +84 -0
- sqlspec-0.3.0.dist-info/RECORD +42 -0
- {sqlspec-0.1.0.dist-info → sqlspec-0.3.0.dist-info}/WHEEL +1 -1
- sqlspec-0.3.0.dist-info/licenses/NOTICE +29 -0
- sqlspec/types/protocols.py +0 -117
- sqlspec-0.1.0.dist-info/METADATA +0 -25
- sqlspec-0.1.0.dist-info/RECORD +0 -14
- /sqlspec/{types → adapters}/__init__.py +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from sqlspec.config import GenericDatabaseConfig
|
|
8
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
9
|
+
from sqlspec.utils.dataclass import simple_asdict
|
|
10
|
+
from sqlspec.utils.empty import Empty, EmptyType
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Generator
|
|
14
|
+
|
|
15
|
+
from duckdb import DuckDBPyConnection
|
|
16
|
+
|
|
17
|
+
__all__ = ("DuckDBConfig",)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class DuckDBConfig(GenericDatabaseConfig):
|
|
22
|
+
"""Configuration for DuckDB database connections.
|
|
23
|
+
|
|
24
|
+
This class provides configuration options for DuckDB database connections, wrapping all parameters
|
|
25
|
+
available to duckdb.connect().
|
|
26
|
+
|
|
27
|
+
For details see: https://duckdb.org/docs/api/python/overview#connection-options
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
database: str | EmptyType = Empty
|
|
31
|
+
"""The path to the database file to be opened. Pass ":memory:" to open a connection to a database that resides in RAM instead of on disk. If not specified, an in-memory database will be created."""
|
|
32
|
+
|
|
33
|
+
read_only: bool | EmptyType = Empty
|
|
34
|
+
"""If True, the database will be opened in read-only mode. This is required if multiple processes want to access the same database file at the same time."""
|
|
35
|
+
|
|
36
|
+
config: dict[str, Any] | EmptyType = Empty
|
|
37
|
+
"""A dictionary of configuration options to be passed to DuckDB. These can include settings like 'access_mode', 'max_memory', 'threads', etc.
|
|
38
|
+
|
|
39
|
+
For details see: https://duckdb.org/docs/api/python/overview#connection-options
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def connection_config_dict(self) -> dict[str, Any]:
|
|
44
|
+
"""Return the connection configuration as a dict.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
A string keyed dict of config kwargs for the duckdb.connect() function.
|
|
48
|
+
"""
|
|
49
|
+
config = simple_asdict(self, exclude_empty=True, convert_nested=False)
|
|
50
|
+
if not config.get("database"):
|
|
51
|
+
config["database"] = ":memory:"
|
|
52
|
+
return config
|
|
53
|
+
|
|
54
|
+
def create_connection(self) -> DuckDBPyConnection:
|
|
55
|
+
"""Create and return a new database connection.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A new DuckDB connection instance.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ImproperConfigurationError: If the connection could not be established.
|
|
62
|
+
"""
|
|
63
|
+
import duckdb
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
return duckdb.connect(**self.connection_config_dict)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
msg = f"Could not configure the DuckDB connection. Error: {e!s}"
|
|
69
|
+
raise ImproperConfigurationError(msg) from e
|
|
70
|
+
|
|
71
|
+
@contextmanager
|
|
72
|
+
def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
|
|
73
|
+
"""Manage the lifecycle of a database connection.
|
|
74
|
+
|
|
75
|
+
Yields:
|
|
76
|
+
None
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ImproperConfigurationError: If the connection could not be established.
|
|
80
|
+
"""
|
|
81
|
+
connection = self.create_connection()
|
|
82
|
+
try:
|
|
83
|
+
yield
|
|
84
|
+
finally:
|
|
85
|
+
connection.close()
|
|
86
|
+
|
|
87
|
+
@contextmanager
|
|
88
|
+
def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[DuckDBPyConnection, None, None]:
|
|
89
|
+
"""Create and provide a database connection.
|
|
90
|
+
|
|
91
|
+
Yields:
|
|
92
|
+
A DuckDB connection instance.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ImproperConfigurationError: If the connection could not be established.
|
|
96
|
+
"""
|
|
97
|
+
connection = self.create_connection()
|
|
98
|
+
try:
|
|
99
|
+
yield connection
|
|
100
|
+
finally:
|
|
101
|
+
connection.close()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .config import (
|
|
2
|
+
OracleAsyncDatabaseConfig,
|
|
3
|
+
OracleAsyncPoolConfig,
|
|
4
|
+
OracleSyncDatabaseConfig,
|
|
5
|
+
OracleSyncPoolConfig,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
"OracleAsyncDatabaseConfig",
|
|
10
|
+
"OracleAsyncPoolConfig",
|
|
11
|
+
"OracleSyncDatabaseConfig",
|
|
12
|
+
"OracleSyncPoolConfig",
|
|
13
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from sqlspec.adapters.oracledb.config._asyncio import OracleAsyncDatabaseConfig, OracleAsyncPoolConfig
|
|
2
|
+
from sqlspec.adapters.oracledb.config._sync import OracleSyncDatabaseConfig, OracleSyncPoolConfig
|
|
3
|
+
|
|
4
|
+
__all__ = (
|
|
5
|
+
"OracleAsyncDatabaseConfig",
|
|
6
|
+
"OracleAsyncPoolConfig",
|
|
7
|
+
"OracleSyncDatabaseConfig",
|
|
8
|
+
"OracleSyncPoolConfig",
|
|
9
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from oracledb import create_pool_async as oracledb_create_pool
|
|
8
|
+
from oracledb.connection import AsyncConnection
|
|
9
|
+
from oracledb.pool import AsyncConnectionPool
|
|
10
|
+
|
|
11
|
+
from sqlspec.adapters.oracledb.config._common import (
|
|
12
|
+
OracleGenericDatabaseConfig,
|
|
13
|
+
OracleGenericPoolConfig,
|
|
14
|
+
)
|
|
15
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
16
|
+
from sqlspec.utils.dataclass import simple_asdict
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import AsyncGenerator, Awaitable
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"OracleAsyncDatabaseConfig",
|
|
24
|
+
"OracleAsyncPoolConfig",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class OracleAsyncPoolConfig(OracleGenericPoolConfig[AsyncConnectionPool, AsyncConnection]):
|
|
30
|
+
"""Async Oracle Pool Config"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnectionPool, AsyncConnection]):
|
|
35
|
+
"""Async Oracle database Configuration."""
|
|
36
|
+
|
|
37
|
+
pool_config: OracleAsyncPoolConfig | None = None
|
|
38
|
+
"""Oracle Pool configuration"""
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
42
|
+
"""Return the pool configuration as a dict.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <oracledb.pool.create_pool>`
|
|
46
|
+
function.
|
|
47
|
+
"""
|
|
48
|
+
if self.pool_config is not None:
|
|
49
|
+
return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
|
|
50
|
+
msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
|
|
51
|
+
raise ImproperConfigurationError(msg)
|
|
52
|
+
|
|
53
|
+
async def create_pool(self) -> AsyncConnectionPool:
|
|
54
|
+
"""Return a pool. If none exists yet, create one.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Getter that returns the pool instance used by the plugin.
|
|
58
|
+
"""
|
|
59
|
+
if self.pool_instance is not None:
|
|
60
|
+
return self.pool_instance
|
|
61
|
+
|
|
62
|
+
if self.pool_config is None:
|
|
63
|
+
msg = "One of 'pool_config' or 'pool_instance' must be provided."
|
|
64
|
+
raise ImproperConfigurationError(msg)
|
|
65
|
+
|
|
66
|
+
pool_config = self.pool_config_dict
|
|
67
|
+
self.pool_instance = oracledb_create_pool(**pool_config)
|
|
68
|
+
if self.pool_instance is None:
|
|
69
|
+
msg = "Could not configure the 'pool_instance'. Please check your configuration."
|
|
70
|
+
raise ImproperConfigurationError(msg)
|
|
71
|
+
return self.pool_instance
|
|
72
|
+
|
|
73
|
+
@asynccontextmanager
|
|
74
|
+
async def lifespan(self, *args: Any, **kwargs) -> AsyncGenerator[None, None]:
|
|
75
|
+
db_pool = await self.create_pool()
|
|
76
|
+
try:
|
|
77
|
+
yield
|
|
78
|
+
finally:
|
|
79
|
+
await db_pool.close(force=True)
|
|
80
|
+
|
|
81
|
+
def provide_pool(self, *args: Any, **kwargs) -> Awaitable[AsyncConnectionPool]:
|
|
82
|
+
"""Create a pool instance.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
A Pool instance.
|
|
86
|
+
"""
|
|
87
|
+
return self.create_pool()
|
|
88
|
+
|
|
89
|
+
@asynccontextmanager
|
|
90
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncConnection, None]:
|
|
91
|
+
"""Create a connection instance.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
A connection instance.
|
|
95
|
+
"""
|
|
96
|
+
db_pool = await self.provide_pool(*args, **kwargs)
|
|
97
|
+
async with db_pool.acquire() as connection:
|
|
98
|
+
yield connection
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from oracledb import ConnectionPool
|
|
7
|
+
|
|
8
|
+
from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
|
|
9
|
+
from sqlspec.utils.empty import Empty
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
import ssl
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from oracledb import AuthMode, ConnectParams, Purity
|
|
17
|
+
from oracledb.connection import AsyncConnection, Connection
|
|
18
|
+
from oracledb.pool import AsyncConnectionPool, ConnectionPool
|
|
19
|
+
|
|
20
|
+
from sqlspec.utils.empty import EmptyType
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"OracleGenericDatabaseConfig",
|
|
24
|
+
"OracleGenericPoolConfig",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
T = TypeVar("T")
|
|
29
|
+
|
|
30
|
+
ConnectionT = TypeVar("ConnectionT", bound="Connection | AsyncConnection")
|
|
31
|
+
PoolT = TypeVar("PoolT", bound="ConnectionPool | AsyncConnectionPool")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class OracleGenericPoolConfig(Generic[PoolT, ConnectionT], GenericPoolConfig):
|
|
36
|
+
"""Configuration for Oracle database connection pools.
|
|
37
|
+
|
|
38
|
+
This class provides configuration options for both synchronous and asynchronous Oracle
|
|
39
|
+
database connection pools. It supports all standard Oracle connection parameters and pool-specific
|
|
40
|
+
settings.([1](https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html))
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
conn_class: type[ConnectionT] | EmptyType = Empty
|
|
44
|
+
"""The connection class to use (Connection or AsyncConnection)"""
|
|
45
|
+
dsn: str | EmptyType = Empty
|
|
46
|
+
"""Connection string for the database """
|
|
47
|
+
pool: PoolT | EmptyType = Empty
|
|
48
|
+
"""Existing pool instance to use"""
|
|
49
|
+
params: ConnectParams | EmptyType = Empty
|
|
50
|
+
"""Connection parameters object"""
|
|
51
|
+
user: str | EmptyType = Empty
|
|
52
|
+
"""Username for database authentication"""
|
|
53
|
+
proxy_user: str | EmptyType = Empty
|
|
54
|
+
"""Name of the proxy user to connect through"""
|
|
55
|
+
password: str | EmptyType = Empty
|
|
56
|
+
"""Password for database authentication"""
|
|
57
|
+
newpassword: str | EmptyType = Empty
|
|
58
|
+
"""New password for password change operations"""
|
|
59
|
+
wallet_password: str | EmptyType = Empty
|
|
60
|
+
"""Password for accessing Oracle Wallet"""
|
|
61
|
+
access_token: str | tuple | Callable | EmptyType = Empty
|
|
62
|
+
"""Token for token-based authentication"""
|
|
63
|
+
host: str | EmptyType = Empty
|
|
64
|
+
"""Database server hostname"""
|
|
65
|
+
port: int | EmptyType = Empty
|
|
66
|
+
"""Database server port number"""
|
|
67
|
+
protocol: str | EmptyType = Empty
|
|
68
|
+
"""Network protocol (TCP or TCPS)"""
|
|
69
|
+
https_proxy: str | EmptyType = Empty
|
|
70
|
+
"""HTTPS proxy server address"""
|
|
71
|
+
https_proxy_port: int | EmptyType = Empty
|
|
72
|
+
"""HTTPS proxy server port"""
|
|
73
|
+
service_name: str | EmptyType = Empty
|
|
74
|
+
"""Oracle service name"""
|
|
75
|
+
sid: str | EmptyType = Empty
|
|
76
|
+
"""Oracle System ID (SID)"""
|
|
77
|
+
server_type: str | EmptyType = Empty
|
|
78
|
+
"""Server type (dedicated, shared, pooled, or drcp)"""
|
|
79
|
+
cclass: str | EmptyType = Empty
|
|
80
|
+
"""Connection class for database resident connection pooling"""
|
|
81
|
+
purity: Purity | EmptyType = Empty
|
|
82
|
+
"""Session purity (NEW, SELF, or DEFAULT)"""
|
|
83
|
+
expire_time: int | EmptyType = Empty
|
|
84
|
+
"""Time in minutes after which idle connections are closed"""
|
|
85
|
+
retry_count: int | EmptyType = Empty
|
|
86
|
+
"""Number of attempts to connect"""
|
|
87
|
+
retry_delay: int | EmptyType = Empty
|
|
88
|
+
"""Time in seconds between connection attempts"""
|
|
89
|
+
tcp_connect_timeout: float | EmptyType = Empty
|
|
90
|
+
"""Timeout for establishing TCP connections"""
|
|
91
|
+
ssl_server_dn_match: bool | EmptyType = Empty
|
|
92
|
+
"""If True, verify server certificate DN"""
|
|
93
|
+
ssl_server_cert_dn: str | EmptyType = Empty
|
|
94
|
+
"""Expected server certificate DN"""
|
|
95
|
+
wallet_location: str | EmptyType = Empty
|
|
96
|
+
"""Location of Oracle Wallet"""
|
|
97
|
+
events: bool | EmptyType = Empty
|
|
98
|
+
"""If True, enables Oracle events for FAN and RLB"""
|
|
99
|
+
externalauth: bool | EmptyType = Empty
|
|
100
|
+
"""If True, uses external authentication"""
|
|
101
|
+
mode: AuthMode | EmptyType = Empty
|
|
102
|
+
"""Session mode (SYSDBA, SYSOPER, etc.)"""
|
|
103
|
+
disable_oob: bool | EmptyType = Empty
|
|
104
|
+
"""If True, disables Oracle out-of-band breaks"""
|
|
105
|
+
stmtcachesize: int | EmptyType = Empty
|
|
106
|
+
"""Size of the statement cache"""
|
|
107
|
+
edition: str | EmptyType = Empty
|
|
108
|
+
"""Edition name for edition-based redefinition"""
|
|
109
|
+
tag: str | EmptyType = Empty
|
|
110
|
+
"""Connection pool tag"""
|
|
111
|
+
matchanytag: bool | EmptyType = Empty
|
|
112
|
+
"""If True, allows connections with different tags"""
|
|
113
|
+
config_dir: str | EmptyType = Empty
|
|
114
|
+
"""Directory containing Oracle configuration files"""
|
|
115
|
+
appcontext: list | EmptyType = Empty
|
|
116
|
+
"""Application context list"""
|
|
117
|
+
shardingkey: list | EmptyType = Empty
|
|
118
|
+
"""Sharding key list"""
|
|
119
|
+
supershardingkey: list | EmptyType = Empty
|
|
120
|
+
"""Super sharding key list"""
|
|
121
|
+
debug_jdwp: str | EmptyType = Empty
|
|
122
|
+
"""JDWP debugging string"""
|
|
123
|
+
connection_id_prefix: str | EmptyType = Empty
|
|
124
|
+
"""Prefix for connection identifiers"""
|
|
125
|
+
ssl_context: Any | EmptyType = Empty
|
|
126
|
+
"""SSL context for TCPS connections"""
|
|
127
|
+
sdu: int | EmptyType = Empty
|
|
128
|
+
"""Session data unit size"""
|
|
129
|
+
pool_boundary: str | EmptyType = Empty
|
|
130
|
+
"""Connection pool boundary (statement or transaction)"""
|
|
131
|
+
use_tcp_fast_open: bool | EmptyType = Empty
|
|
132
|
+
"""If True, enables TCP Fast Open"""
|
|
133
|
+
ssl_version: ssl.TLSVersion | EmptyType = Empty
|
|
134
|
+
"""SSL/TLS protocol version"""
|
|
135
|
+
handle: int | EmptyType = Empty
|
|
136
|
+
"""Oracle service context handle"""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass
|
|
140
|
+
class OracleGenericDatabaseConfig(Generic[PoolT, ConnectionT], GenericDatabaseConfig):
|
|
141
|
+
"""Oracle database Configuration.
|
|
142
|
+
|
|
143
|
+
This class provides the base configuration for Oracle database connections, extending
|
|
144
|
+
the generic database configuration with Oracle-specific settings. It supports both
|
|
145
|
+
thin and thick modes of the python-oracledb driver.([1](https://python-oracledb.readthedocs.io/en/latest/index.html))
|
|
146
|
+
|
|
147
|
+
The configuration supports all standard Oracle connection parameters and can be used
|
|
148
|
+
with both synchronous and asynchronous connections. It includes support for features
|
|
149
|
+
like Oracle Wallet, external authentication, connection pooling, and advanced security
|
|
150
|
+
options.([2](https://python-oracledb.readthedocs.io/en/latest/user_guide/tuning.html))
|
|
151
|
+
"""
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from oracledb import create_pool as oracledb_create_pool
|
|
8
|
+
from oracledb.connection import Connection
|
|
9
|
+
from oracledb.pool import ConnectionPool
|
|
10
|
+
|
|
11
|
+
from sqlspec.adapters.oracledb.config._common import (
|
|
12
|
+
OracleGenericDatabaseConfig,
|
|
13
|
+
OracleGenericPoolConfig,
|
|
14
|
+
)
|
|
15
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
16
|
+
from sqlspec.utils.dataclass import simple_asdict
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Generator
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"OracleSyncDatabaseConfig",
|
|
24
|
+
"OracleSyncPoolConfig",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class OracleSyncPoolConfig(OracleGenericPoolConfig[ConnectionPool, Connection]):
|
|
30
|
+
"""Sync Oracle Pool Config"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class OracleSyncDatabaseConfig(OracleGenericDatabaseConfig[ConnectionPool, Connection]):
|
|
35
|
+
"""Oracle database Configuration."""
|
|
36
|
+
|
|
37
|
+
pool_config: OracleSyncPoolConfig | None = None
|
|
38
|
+
"""Oracle Pool configuration"""
|
|
39
|
+
pool_instance: ConnectionPool | None = None
|
|
40
|
+
"""Optional pool to use.
|
|
41
|
+
|
|
42
|
+
If set, the plugin will use the provided pool rather than instantiate one.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
47
|
+
"""Return the pool configuration as a dict.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <oracledb.pool.create_pool>`
|
|
51
|
+
function.
|
|
52
|
+
"""
|
|
53
|
+
if self.pool_config:
|
|
54
|
+
return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
|
|
55
|
+
msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
|
|
56
|
+
raise ImproperConfigurationError(msg)
|
|
57
|
+
|
|
58
|
+
def create_pool(self) -> ConnectionPool:
|
|
59
|
+
"""Return a pool. If none exists yet, create one.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Getter that returns the pool instance used by the plugin.
|
|
63
|
+
"""
|
|
64
|
+
if self.pool_instance is not None:
|
|
65
|
+
return self.pool_instance
|
|
66
|
+
|
|
67
|
+
if self.pool_config is None:
|
|
68
|
+
msg = "One of 'pool_config' or 'pool_instance' must be provided."
|
|
69
|
+
raise ImproperConfigurationError(msg)
|
|
70
|
+
|
|
71
|
+
pool_config = self.pool_config_dict
|
|
72
|
+
self.pool_instance = oracledb_create_pool(**pool_config)
|
|
73
|
+
if self.pool_instance is None:
|
|
74
|
+
msg = "Could not configure the 'pool_instance'. Please check your configuration."
|
|
75
|
+
raise ImproperConfigurationError(msg)
|
|
76
|
+
return self.pool_instance
|
|
77
|
+
|
|
78
|
+
def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
|
|
79
|
+
db_pool = self.create_pool()
|
|
80
|
+
try:
|
|
81
|
+
yield
|
|
82
|
+
finally:
|
|
83
|
+
db_pool.close()
|
|
84
|
+
|
|
85
|
+
def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
|
|
86
|
+
"""Create a pool instance.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
A Pool instance.
|
|
90
|
+
"""
|
|
91
|
+
return self.create_pool()
|
|
92
|
+
|
|
93
|
+
@contextmanager
|
|
94
|
+
def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[Connection, None, None]:
|
|
95
|
+
"""Create a connection instance.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
A connection instance.
|
|
99
|
+
"""
|
|
100
|
+
db_pool = self.provide_pool(*args, **kwargs)
|
|
101
|
+
with db_pool.acquire() as connection:
|
|
102
|
+
yield connection
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from ._async import PsycoPgAsyncDatabaseConfig, PsycoPgAsyncPoolConfig
|
|
2
|
+
from ._sync import PsycoPgSyncDatabaseConfig, PsycoPgSyncPoolConfig
|
|
3
|
+
|
|
4
|
+
__all__ = (
|
|
5
|
+
"PsycoPgAsyncDatabaseConfig",
|
|
6
|
+
"PsycoPgAsyncPoolConfig",
|
|
7
|
+
"PsycoPgSyncDatabaseConfig",
|
|
8
|
+
"PsycoPgSyncPoolConfig",
|
|
9
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from psycopg import AsyncConnection
|
|
8
|
+
from psycopg_pool import AsyncConnectionPool
|
|
9
|
+
|
|
10
|
+
from sqlspec.adapters.psycopg.config._common import (
|
|
11
|
+
PsycoPgGenericDatabaseConfig,
|
|
12
|
+
PsycoPgGenericPoolConfig,
|
|
13
|
+
)
|
|
14
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
15
|
+
from sqlspec.utils.dataclass import simple_asdict
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import AsyncGenerator, Awaitable
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"PsycoPgAsyncDatabaseConfig",
|
|
24
|
+
"PsycoPgAsyncPoolConfig",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class PsycoPgAsyncPoolConfig(PsycoPgGenericPoolConfig[AsyncConnectionPool, AsyncConnection]):
|
|
30
|
+
"""Async Psycopg Pool Config"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class PsycoPgAsyncDatabaseConfig(PsycoPgGenericDatabaseConfig[AsyncConnectionPool, AsyncConnection]):
|
|
35
|
+
"""Async Psycopg database Configuration."""
|
|
36
|
+
|
|
37
|
+
pool_config: PsycoPgAsyncPoolConfig | None = None
|
|
38
|
+
"""Psycopg Pool configuration"""
|
|
39
|
+
pool_instance: AsyncConnectionPool | None = None
|
|
40
|
+
"""Optional pool to use"""
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
44
|
+
"""Return the pool configuration as a dict."""
|
|
45
|
+
if self.pool_config:
|
|
46
|
+
return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
|
|
47
|
+
msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
|
|
48
|
+
raise ImproperConfigurationError(msg)
|
|
49
|
+
|
|
50
|
+
async def create_pool(self) -> AsyncConnectionPool:
|
|
51
|
+
"""Create and return a connection pool."""
|
|
52
|
+
if self.pool_instance is not None:
|
|
53
|
+
return self.pool_instance
|
|
54
|
+
|
|
55
|
+
if self.pool_config is None:
|
|
56
|
+
msg = "One of 'pool_config' or 'pool_instance' must be provided."
|
|
57
|
+
raise ImproperConfigurationError(msg)
|
|
58
|
+
|
|
59
|
+
pool_config = self.pool_config_dict
|
|
60
|
+
self.pool_instance = AsyncConnectionPool(**pool_config)
|
|
61
|
+
if self.pool_instance is None:
|
|
62
|
+
msg = "Could not configure the 'pool_instance'. Please check your configuration." # type: ignore[unreachable]
|
|
63
|
+
raise ImproperConfigurationError(msg)
|
|
64
|
+
return self.pool_instance
|
|
65
|
+
|
|
66
|
+
@asynccontextmanager
|
|
67
|
+
async def lifespan(self, *args: Any, **kwargs: Any) -> AsyncGenerator[None, None]:
|
|
68
|
+
"""Manage the lifecycle of the connection pool."""
|
|
69
|
+
pool = await self.create_pool()
|
|
70
|
+
try:
|
|
71
|
+
yield
|
|
72
|
+
finally:
|
|
73
|
+
await pool.close()
|
|
74
|
+
|
|
75
|
+
def provide_pool(self, *args: Any, **kwargs: Any) -> Awaitable[AsyncConnectionPool]:
|
|
76
|
+
"""Create and return a connection pool."""
|
|
77
|
+
return self.create_pool()
|
|
78
|
+
|
|
79
|
+
@asynccontextmanager
|
|
80
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncConnection, None]:
|
|
81
|
+
"""Create and provide a database connection."""
|
|
82
|
+
pool = await self.provide_pool(*args, **kwargs)
|
|
83
|
+
async with pool.connection() as connection:
|
|
84
|
+
yield connection
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
|
|
7
|
+
from sqlspec.utils.empty import Empty
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from psycopg import AsyncConnection, Connection
|
|
14
|
+
from psycopg_pool import AsyncConnectionPool, ConnectionPool
|
|
15
|
+
|
|
16
|
+
from sqlspec.utils.empty import EmptyType
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"PsycoPgGenericDatabaseConfig",
|
|
20
|
+
"PsycoPgGenericPoolConfig",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
ConnectionT = TypeVar("ConnectionT", bound="Connection | AsyncConnection")
|
|
25
|
+
PoolT = TypeVar("PoolT", bound="ConnectionPool | AsyncConnectionPool")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class PsycoPgGenericPoolConfig(Generic[PoolT, ConnectionT], GenericPoolConfig):
|
|
30
|
+
"""Configuration for Psycopg connection pools.
|
|
31
|
+
|
|
32
|
+
This class provides configuration options for both synchronous and asynchronous Psycopg
|
|
33
|
+
database connection pools. It supports all standard Psycopg connection parameters and pool-specific
|
|
34
|
+
settings.([1](https://www.psycopg.org/psycopg3/docs/api/pool.html))
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
conninfo: str | EmptyType = Empty
|
|
38
|
+
"""Connection string in libpq format"""
|
|
39
|
+
kwargs: dict[str, Any] | EmptyType = Empty
|
|
40
|
+
"""Additional connection parameters"""
|
|
41
|
+
min_size: int | EmptyType = Empty
|
|
42
|
+
"""Minimum number of connections in the pool"""
|
|
43
|
+
max_size: int | EmptyType = Empty
|
|
44
|
+
"""Maximum number of connections in the pool"""
|
|
45
|
+
name: str | EmptyType = Empty
|
|
46
|
+
"""Name of the connection pool"""
|
|
47
|
+
timeout: float | EmptyType = Empty
|
|
48
|
+
"""Timeout for acquiring connections"""
|
|
49
|
+
max_waiting: int | EmptyType = Empty
|
|
50
|
+
"""Maximum number of waiting clients"""
|
|
51
|
+
max_lifetime: float | EmptyType = Empty
|
|
52
|
+
"""Maximum connection lifetime"""
|
|
53
|
+
max_idle: float | EmptyType = Empty
|
|
54
|
+
"""Maximum idle time for connections"""
|
|
55
|
+
reconnect_timeout: float | EmptyType = Empty
|
|
56
|
+
"""Time between reconnection attempts"""
|
|
57
|
+
num_workers: int | EmptyType = Empty
|
|
58
|
+
"""Number of background workers"""
|
|
59
|
+
configure: Callable[[ConnectionT], None] | EmptyType = Empty
|
|
60
|
+
"""Callback to configure new connections"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class PsycoPgGenericDatabaseConfig(Generic[PoolT, ConnectionT], GenericDatabaseConfig):
|
|
65
|
+
"""Psycopg database Configuration.
|
|
66
|
+
|
|
67
|
+
This class provides the base configuration for Psycopg database connections, extending
|
|
68
|
+
the generic database configuration with Psycopg-specific settings.([1](https://www.psycopg.org/psycopg3/docs/api/connections.html))
|
|
69
|
+
|
|
70
|
+
The configuration supports all standard Psycopg connection parameters and can be used
|
|
71
|
+
with both synchronous and asynchronous connections.([2](https://www.psycopg.org/psycopg3/docs/api/connections.html))
|
|
72
|
+
"""
|