sqlspec 0.8.0__py3-none-any.whl → 0.9.1__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/_typing.py +39 -6
- sqlspec/adapters/adbc/__init__.py +2 -2
- sqlspec/adapters/adbc/config.py +34 -11
- sqlspec/adapters/adbc/driver.py +302 -111
- sqlspec/adapters/aiosqlite/__init__.py +2 -2
- sqlspec/adapters/aiosqlite/config.py +2 -2
- sqlspec/adapters/aiosqlite/driver.py +164 -42
- sqlspec/adapters/asyncmy/__init__.py +3 -3
- sqlspec/adapters/asyncmy/config.py +11 -12
- sqlspec/adapters/asyncmy/driver.py +161 -37
- sqlspec/adapters/asyncpg/__init__.py +5 -5
- sqlspec/adapters/asyncpg/config.py +17 -19
- sqlspec/adapters/asyncpg/driver.py +386 -96
- sqlspec/adapters/duckdb/__init__.py +2 -2
- sqlspec/adapters/duckdb/config.py +2 -2
- sqlspec/adapters/duckdb/driver.py +190 -60
- sqlspec/adapters/oracledb/__init__.py +8 -8
- sqlspec/adapters/oracledb/config/__init__.py +6 -6
- sqlspec/adapters/oracledb/config/_asyncio.py +9 -10
- sqlspec/adapters/oracledb/config/_sync.py +8 -9
- sqlspec/adapters/oracledb/driver.py +384 -45
- sqlspec/adapters/psqlpy/__init__.py +0 -0
- sqlspec/adapters/psqlpy/config.py +250 -0
- sqlspec/adapters/psqlpy/driver.py +481 -0
- sqlspec/adapters/psycopg/__init__.py +10 -5
- sqlspec/adapters/psycopg/config/__init__.py +6 -6
- sqlspec/adapters/psycopg/config/_async.py +12 -12
- sqlspec/adapters/psycopg/config/_sync.py +13 -13
- sqlspec/adapters/psycopg/driver.py +432 -222
- sqlspec/adapters/sqlite/__init__.py +2 -2
- sqlspec/adapters/sqlite/config.py +2 -2
- sqlspec/adapters/sqlite/driver.py +176 -72
- sqlspec/base.py +687 -161
- sqlspec/exceptions.py +30 -0
- sqlspec/extensions/litestar/config.py +6 -0
- sqlspec/extensions/litestar/handlers.py +25 -0
- sqlspec/extensions/litestar/plugin.py +8 -1
- sqlspec/statement.py +373 -0
- sqlspec/typing.py +10 -1
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/METADATA +144 -2
- sqlspec-0.9.1.dist-info/RECORD +61 -0
- sqlspec-0.8.0.dist-info/RECORD +0 -57
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/NOTICE +0 -0
|
File without changes
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Configuration for the psqlpy PostgreSQL adapter."""
|
|
2
|
+
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
6
|
+
|
|
7
|
+
from psqlpy import Connection, ConnectionPool
|
|
8
|
+
|
|
9
|
+
from sqlspec.adapters.psqlpy.driver import PsqlpyDriver
|
|
10
|
+
from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
|
|
11
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
12
|
+
from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import AsyncGenerator, Awaitable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"PsqlpyConfig",
|
|
20
|
+
"PsqlpyPoolConfig",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class PsqlpyPoolConfig(GenericPoolConfig):
|
|
26
|
+
"""Configuration for psqlpy connection pool.
|
|
27
|
+
|
|
28
|
+
Ref: https://psqlpy-python.github.io/components/connection_pool.html#all-available-connectionpool-parameters
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
dsn: Optional[Union[str, EmptyType]] = Empty
|
|
32
|
+
"""DSN of the PostgreSQL."""
|
|
33
|
+
# Required connection parameters
|
|
34
|
+
username: Optional[Union[str, EmptyType]] = Empty
|
|
35
|
+
"""Username of the user in the PostgreSQL."""
|
|
36
|
+
password: Optional[Union[str, EmptyType]] = Empty
|
|
37
|
+
"""Password of the user in the PostgreSQL."""
|
|
38
|
+
db_name: Optional[Union[str, EmptyType]] = Empty
|
|
39
|
+
"""Name of the database in PostgreSQL."""
|
|
40
|
+
|
|
41
|
+
# Single or Multi-host parameters (mutually exclusive)
|
|
42
|
+
host: Optional[Union[str, EmptyType]] = Empty
|
|
43
|
+
"""Host of the PostgreSQL (use for single host)."""
|
|
44
|
+
port: Optional[Union[int, EmptyType]] = Empty
|
|
45
|
+
"""Port of the PostgreSQL (use for single host)."""
|
|
46
|
+
hosts: Optional[Union[list[str], EmptyType]] = Empty
|
|
47
|
+
"""List of hosts of the PostgreSQL (use for multiple hosts)."""
|
|
48
|
+
ports: Optional[Union[list[int], EmptyType]] = Empty
|
|
49
|
+
"""List of ports of the PostgreSQL (use for multiple hosts)."""
|
|
50
|
+
|
|
51
|
+
# Pool size
|
|
52
|
+
max_db_pool_size: int = 10
|
|
53
|
+
"""Maximum size of the connection pool. Defaults to 10."""
|
|
54
|
+
|
|
55
|
+
# Optional timeouts
|
|
56
|
+
connect_timeout_sec: Optional[Union[int, EmptyType]] = Empty
|
|
57
|
+
"""The time limit in seconds applied to each socket-level connection attempt."""
|
|
58
|
+
connect_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
|
|
59
|
+
"""Nanoseconds for connection timeout, can be used only with `connect_timeout_sec`."""
|
|
60
|
+
tcp_user_timeout_sec: Optional[Union[int, EmptyType]] = Empty
|
|
61
|
+
"""The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed."""
|
|
62
|
+
tcp_user_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
|
|
63
|
+
"""Nanoseconds for tcp_user_timeout, can be used only with `tcp_user_timeout_sec`."""
|
|
64
|
+
|
|
65
|
+
# Optional keepalives
|
|
66
|
+
keepalives: bool = True
|
|
67
|
+
"""Controls the use of TCP keepalive. Defaults to True (on)."""
|
|
68
|
+
keepalives_idle_sec: Optional[Union[int, EmptyType]] = Empty
|
|
69
|
+
"""The number of seconds of inactivity after which a keepalive message is sent to the server."""
|
|
70
|
+
keepalives_idle_nanosec: Optional[Union[int, EmptyType]] = Empty
|
|
71
|
+
"""Nanoseconds for keepalives_idle_sec."""
|
|
72
|
+
keepalives_interval_sec: Optional[Union[int, EmptyType]] = Empty
|
|
73
|
+
"""The time interval between TCP keepalive probes."""
|
|
74
|
+
keepalives_interval_nanosec: Optional[Union[int, EmptyType]] = Empty
|
|
75
|
+
"""Nanoseconds for keepalives_interval_sec."""
|
|
76
|
+
keepalives_retries: Optional[Union[int, EmptyType]] = Empty
|
|
77
|
+
"""The maximum number of TCP keepalive probes that will be sent before dropping a connection."""
|
|
78
|
+
|
|
79
|
+
# Other optional parameters
|
|
80
|
+
load_balance_hosts: Optional[Union[str, EmptyType]] = Empty
|
|
81
|
+
"""Controls the order in which the client tries to connect to the available hosts and addresses ('disable' or 'random')."""
|
|
82
|
+
conn_recycling_method: Optional[Union[str, EmptyType]] = Empty
|
|
83
|
+
"""How a connection is recycled."""
|
|
84
|
+
ssl_mode: Optional[Union[str, EmptyType]] = Empty
|
|
85
|
+
"""SSL mode."""
|
|
86
|
+
ca_file: Optional[Union[str, EmptyType]] = Empty
|
|
87
|
+
"""Path to ca_file for SSL."""
|
|
88
|
+
target_session_attrs: Optional[Union[str, EmptyType]] = Empty
|
|
89
|
+
"""Specifies requirements of the session (e.g., 'read-write')."""
|
|
90
|
+
options: Optional[Union[str, EmptyType]] = Empty
|
|
91
|
+
"""Command line options used to configure the server."""
|
|
92
|
+
application_name: Optional[Union[str, EmptyType]] = Empty
|
|
93
|
+
"""Sets the application_name parameter on the server."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class PsqlpyConfig(AsyncDatabaseConfig[Connection, ConnectionPool, PsqlpyDriver]):
|
|
98
|
+
"""Configuration for psqlpy database connections, managing a connection pool.
|
|
99
|
+
|
|
100
|
+
This configuration class wraps `PsqlpyPoolConfig` and manages the lifecycle
|
|
101
|
+
of a `psqlpy.ConnectionPool`.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
pool_config: Optional[PsqlpyPoolConfig] = field(default=None)
|
|
105
|
+
"""Psqlpy Pool configuration"""
|
|
106
|
+
driver_type: type[PsqlpyDriver] = field(default=PsqlpyDriver, init=False, hash=False)
|
|
107
|
+
"""Type of the driver object"""
|
|
108
|
+
connection_type: type[Connection] = field(default=Connection, init=False, hash=False)
|
|
109
|
+
"""Type of the connection object"""
|
|
110
|
+
pool_instance: Optional[ConnectionPool] = field(default=None, hash=False)
|
|
111
|
+
"""The connection pool instance. If set, this will be used instead of creating a new pool."""
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def connection_config_dict(self) -> "dict[str, Any]":
|
|
115
|
+
"""Return the minimal connection configuration as a dict for standalone use.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
A string keyed dict of config kwargs for a psqlpy.Connection.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
ImproperConfigurationError: If essential connection parameters are missing.
|
|
122
|
+
"""
|
|
123
|
+
if self.pool_config:
|
|
124
|
+
# Exclude pool-specific keys and internal metadata
|
|
125
|
+
pool_specific_keys = {
|
|
126
|
+
"max_db_pool_size",
|
|
127
|
+
"load_balance_hosts",
|
|
128
|
+
"conn_recycling_method",
|
|
129
|
+
"pool_instance",
|
|
130
|
+
"connection_type",
|
|
131
|
+
"driver_type",
|
|
132
|
+
}
|
|
133
|
+
return dataclass_to_dict(
|
|
134
|
+
self.pool_config,
|
|
135
|
+
exclude_empty=True,
|
|
136
|
+
convert_nested=False,
|
|
137
|
+
exclude_none=True,
|
|
138
|
+
exclude=pool_specific_keys,
|
|
139
|
+
)
|
|
140
|
+
msg = "You must provide a 'pool_config' for this adapter."
|
|
141
|
+
raise ImproperConfigurationError(msg)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def pool_config_dict(self) -> "dict[str, Any]":
|
|
145
|
+
"""Return the pool configuration as a dict.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ImproperConfigurationError: If no pool_config is provided but a pool_instance
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
A string keyed dict of config kwargs for creating a psqlpy pool.
|
|
152
|
+
"""
|
|
153
|
+
if self.pool_config:
|
|
154
|
+
# Extract the config from the pool_config
|
|
155
|
+
return dataclass_to_dict(
|
|
156
|
+
self.pool_config,
|
|
157
|
+
exclude_empty=True,
|
|
158
|
+
convert_nested=False,
|
|
159
|
+
exclude_none=True,
|
|
160
|
+
exclude={"pool_instance", "connection_type", "driver_type"},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
|
|
164
|
+
raise ImproperConfigurationError(msg)
|
|
165
|
+
|
|
166
|
+
async def create_pool(self) -> "ConnectionPool":
|
|
167
|
+
"""Return a pool. If none exists yet, create one.
|
|
168
|
+
|
|
169
|
+
Ensures that the pool is initialized and returns the instance.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The pool instance used by the plugin.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
ImproperConfigurationError: If the pool could not be configured.
|
|
176
|
+
"""
|
|
177
|
+
if self.pool_instance is not None:
|
|
178
|
+
return self.pool_instance
|
|
179
|
+
|
|
180
|
+
if self.pool_config is None:
|
|
181
|
+
msg = "One of 'pool_config' or 'pool_instance' must be provided."
|
|
182
|
+
raise ImproperConfigurationError(msg)
|
|
183
|
+
|
|
184
|
+
# pool_config is guaranteed to exist due to __post_init__
|
|
185
|
+
try:
|
|
186
|
+
# psqlpy ConnectionPool doesn't have an explicit async connect/startup method
|
|
187
|
+
# It creates connections on demand.
|
|
188
|
+
self.pool_instance = ConnectionPool(**self.pool_config_dict)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
msg = f"Could not configure the 'pool_instance'. Error: {e!s}. Please check your configuration."
|
|
191
|
+
raise ImproperConfigurationError(msg) from e
|
|
192
|
+
|
|
193
|
+
return self.pool_instance
|
|
194
|
+
|
|
195
|
+
def provide_pool(self, *args: "Any", **kwargs: "Any") -> "Awaitable[ConnectionPool]":
|
|
196
|
+
"""Create or return the pool instance.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
An awaitable resolving to the Pool instance.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
async def _create() -> "ConnectionPool":
|
|
203
|
+
return await self.create_pool()
|
|
204
|
+
|
|
205
|
+
return _create()
|
|
206
|
+
|
|
207
|
+
def create_connection(self) -> "Awaitable[Connection]":
|
|
208
|
+
"""Create and return a new, standalone psqlpy connection using the configured parameters.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
An awaitable that resolves to a new Connection instance.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
async def _create() -> "Connection":
|
|
215
|
+
try:
|
|
216
|
+
async with self.provide_connection() as conn:
|
|
217
|
+
return conn
|
|
218
|
+
except Exception as e:
|
|
219
|
+
msg = f"Could not configure the psqlpy connection. Error: {e!s}"
|
|
220
|
+
raise ImproperConfigurationError(msg) from e
|
|
221
|
+
|
|
222
|
+
return _create()
|
|
223
|
+
|
|
224
|
+
@asynccontextmanager
|
|
225
|
+
async def provide_connection(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[Connection, None]":
|
|
226
|
+
"""Acquire a connection from the pool.
|
|
227
|
+
|
|
228
|
+
Yields:
|
|
229
|
+
A connection instance managed by the pool.
|
|
230
|
+
"""
|
|
231
|
+
db_pool = await self.provide_pool(*args, **kwargs)
|
|
232
|
+
async with db_pool.acquire() as conn:
|
|
233
|
+
yield conn
|
|
234
|
+
|
|
235
|
+
def close_pool(self) -> None:
|
|
236
|
+
"""Close the connection pool."""
|
|
237
|
+
if self.pool_instance is not None:
|
|
238
|
+
# psqlpy pool close is synchronous
|
|
239
|
+
self.pool_instance.close()
|
|
240
|
+
self.pool_instance = None
|
|
241
|
+
|
|
242
|
+
@asynccontextmanager
|
|
243
|
+
async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsqlpyDriver, None]":
|
|
244
|
+
"""Create and provide a database session using a pooled connection.
|
|
245
|
+
|
|
246
|
+
Yields:
|
|
247
|
+
A Psqlpy driver instance wrapping a pooled connection.
|
|
248
|
+
"""
|
|
249
|
+
async with self.provide_connection(*args, **kwargs) as connection:
|
|
250
|
+
yield self.driver_type(connection)
|