sqlspec 0.32.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.
- sqlspec/__init__.py +104 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +312 -0
- sqlspec/_typing.py +784 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +436 -0
- sqlspec/adapters/adbc/data_dictionary.py +537 -0
- sqlspec/adapters/adbc/driver.py +841 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- sqlspec/adapters/aiosqlite/__init__.py +29 -0
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +536 -0
- sqlspec/adapters/aiosqlite/config.py +310 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +260 -0
- sqlspec/adapters/aiosqlite/driver.py +463 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +500 -0
- sqlspec/adapters/asyncmy/__init__.py +25 -0
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +503 -0
- sqlspec/adapters/asyncmy/config.py +246 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +241 -0
- sqlspec/adapters/asyncmy/driver.py +632 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +23 -0
- sqlspec/adapters/asyncpg/_type_handlers.py +76 -0
- sqlspec/adapters/asyncpg/_types.py +23 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +460 -0
- sqlspec/adapters/asyncpg/config.py +464 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +321 -0
- sqlspec/adapters/asyncpg/driver.py +720 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/__init__.py +18 -0
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +585 -0
- sqlspec/adapters/bigquery/config.py +298 -0
- sqlspec/adapters/bigquery/data_dictionary.py +256 -0
- sqlspec/adapters/bigquery/driver.py +1073 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/__init__.py +24 -0
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +563 -0
- sqlspec/adapters/duckdb/config.py +396 -0
- sqlspec/adapters/duckdb/data_dictionary.py +264 -0
- sqlspec/adapters/duckdb/driver.py +604 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +273 -0
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- sqlspec/adapters/oracledb/__init__.py +32 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +39 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +130 -0
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1632 -0
- sqlspec/adapters/oracledb/config.py +469 -0
- sqlspec/adapters/oracledb/data_dictionary.py +717 -0
- sqlspec/adapters/oracledb/driver.py +1493 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -0
- sqlspec/adapters/oracledb/migrations.py +532 -0
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- sqlspec/adapters/psqlpy/__init__.py +16 -0
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +12 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +483 -0
- sqlspec/adapters/psqlpy/config.py +271 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +179 -0
- sqlspec/adapters/psqlpy/driver.py +892 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_type_handlers.py +90 -0
- sqlspec/adapters/psycopg/_types.py +18 -0
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +962 -0
- sqlspec/adapters/psycopg/config.py +487 -0
- sqlspec/adapters/psycopg/data_dictionary.py +630 -0
- sqlspec/adapters/psycopg/driver.py +1336 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/spanner/__init__.py +38 -0
- sqlspec/adapters/spanner/_type_handlers.py +186 -0
- sqlspec/adapters/spanner/_types.py +12 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +435 -0
- sqlspec/adapters/spanner/config.py +241 -0
- sqlspec/adapters/spanner/data_dictionary.py +95 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +52 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +123 -0
- sqlspec/adapters/spanner/driver.py +366 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +266 -0
- sqlspec/adapters/spanner/type_converter.py +46 -0
- sqlspec/adapters/sqlite/__init__.py +18 -0
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +582 -0
- sqlspec/adapters/sqlite/config.py +221 -0
- sqlspec/adapters/sqlite/data_dictionary.py +256 -0
- sqlspec/adapters/sqlite/driver.py +527 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +140 -0
- sqlspec/base.py +811 -0
- sqlspec/builder/__init__.py +146 -0
- sqlspec/builder/_base.py +900 -0
- sqlspec/builder/_column.py +517 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.py +84 -0
- sqlspec/builder/_dml.py +381 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.py +1537 -0
- sqlspec/builder/_insert.py +315 -0
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +848 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.py +1615 -0
- sqlspec/builder/_update.py +161 -0
- sqlspec/builder/_vector_expressions.py +259 -0
- sqlspec/cli.py +764 -0
- sqlspec/config.py +1540 -0
- sqlspec/core/__init__.py +305 -0
- sqlspec/core/cache.py +785 -0
- sqlspec/core/compiler.py +603 -0
- sqlspec/core/filters.py +872 -0
- sqlspec/core/hashing.py +274 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +64 -0
- sqlspec/core/parameters/_alignment.py +266 -0
- sqlspec/core/parameters/_converter.py +413 -0
- sqlspec/core/parameters/_processor.py +341 -0
- sqlspec/core/parameters/_registry.py +201 -0
- sqlspec/core/parameters/_transformers.py +226 -0
- sqlspec/core/parameters/_types.py +430 -0
- sqlspec/core/parameters/_validator.py +123 -0
- sqlspec/core/pipeline.py +187 -0
- sqlspec/core/result.py +1124 -0
- sqlspec/core/splitter.py +940 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.py +835 -0
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +36 -0
- sqlspec/driver/_async.py +1027 -0
- sqlspec/driver/_common.py +1236 -0
- sqlspec/driver/_sync.py +1025 -0
- sqlspec/driver/mixins/__init__.py +7 -0
- sqlspec/driver/mixins/_result_tools.py +61 -0
- sqlspec/driver/mixins/_sql_translator.py +122 -0
- sqlspec/driver/mixins/_storage.py +311 -0
- sqlspec/exceptions.py +321 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +471 -0
- sqlspec/extensions/fastapi/__init__.py +19 -0
- sqlspec/extensions/fastapi/extension.py +341 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +72 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +402 -0
- sqlspec/extensions/litestar/__init__.py +23 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/cli.py +92 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +638 -0
- sqlspec/extensions/litestar/providers.py +454 -0
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +107 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +26 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +257 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/loader.py +716 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +728 -0
- sqlspec/migrations/commands.py +1140 -0
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +203 -0
- sqlspec/migrations/loaders.py +450 -0
- sqlspec/migrations/runner.py +1024 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +403 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +203 -0
- sqlspec/observability/__init__.py +22 -0
- sqlspec/observability/_config.py +228 -0
- sqlspec/observability/_diagnostics.py +67 -0
- sqlspec/observability/_dispatcher.py +151 -0
- sqlspec/observability/_observer.py +180 -0
- sqlspec/observability/_runtime.py +381 -0
- sqlspec/observability/_spans.py +158 -0
- sqlspec/protocols.py +530 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +46 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +163 -0
- sqlspec/storage/backends/fsspec.py +398 -0
- sqlspec/storage/backends/local.py +377 -0
- sqlspec/storage/backends/obstore.py +580 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +604 -0
- sqlspec/storage/registry.py +289 -0
- sqlspec/typing.py +219 -0
- sqlspec/utils/__init__.py +31 -0
- sqlspec/utils/arrow_helpers.py +95 -0
- sqlspec/utils/config_resolver.py +153 -0
- sqlspec/utils/correlation.py +132 -0
- sqlspec/utils/data_transformation.py +114 -0
- sqlspec/utils/dependencies.py +79 -0
- sqlspec/utils/deprecation.py +113 -0
- sqlspec/utils/fixtures.py +250 -0
- sqlspec/utils/logging.py +172 -0
- sqlspec/utils/module_loader.py +273 -0
- sqlspec/utils/portal.py +325 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +396 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.py +277 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.py +99 -0
- sqlspec/utils/type_guards.py +1324 -0
- sqlspec/utils/version.py +444 -0
- sqlspec-0.32.0.dist-info/METADATA +202 -0
- sqlspec-0.32.0.dist-info/RECORD +262 -0
- sqlspec-0.32.0.dist-info/WHEEL +4 -0
- sqlspec-0.32.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.32.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Psqlpy database configuration."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict, cast
|
|
7
|
+
|
|
8
|
+
from psqlpy import ConnectionPool
|
|
9
|
+
from typing_extensions import NotRequired
|
|
10
|
+
|
|
11
|
+
from sqlspec.adapters.psqlpy._types import PsqlpyConnection
|
|
12
|
+
from sqlspec.adapters.psqlpy.driver import (
|
|
13
|
+
PsqlpyCursor,
|
|
14
|
+
PsqlpyDriver,
|
|
15
|
+
PsqlpyExceptionHandler,
|
|
16
|
+
build_psqlpy_statement_config,
|
|
17
|
+
)
|
|
18
|
+
from sqlspec.config import AsyncDatabaseConfig, ExtensionConfigs
|
|
19
|
+
from sqlspec.core import StatementConfig
|
|
20
|
+
from sqlspec.typing import PGVECTOR_INSTALLED
|
|
21
|
+
from sqlspec.utils.serializers import to_json
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Callable
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("sqlspec.adapters.psqlpy")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PsqlpyConnectionParams(TypedDict):
|
|
31
|
+
"""Psqlpy connection parameters."""
|
|
32
|
+
|
|
33
|
+
dsn: NotRequired[str]
|
|
34
|
+
username: NotRequired[str]
|
|
35
|
+
password: NotRequired[str]
|
|
36
|
+
db_name: NotRequired[str]
|
|
37
|
+
host: NotRequired[str]
|
|
38
|
+
port: NotRequired[int]
|
|
39
|
+
connect_timeout_sec: NotRequired[int]
|
|
40
|
+
connect_timeout_nanosec: NotRequired[int]
|
|
41
|
+
tcp_user_timeout_sec: NotRequired[int]
|
|
42
|
+
tcp_user_timeout_nanosec: NotRequired[int]
|
|
43
|
+
keepalives: NotRequired[bool]
|
|
44
|
+
keepalives_idle_sec: NotRequired[int]
|
|
45
|
+
keepalives_idle_nanosec: NotRequired[int]
|
|
46
|
+
keepalives_interval_sec: NotRequired[int]
|
|
47
|
+
keepalives_interval_nanosec: NotRequired[int]
|
|
48
|
+
keepalives_retries: NotRequired[int]
|
|
49
|
+
ssl_mode: NotRequired[str]
|
|
50
|
+
ca_file: NotRequired[str]
|
|
51
|
+
target_session_attrs: NotRequired[str]
|
|
52
|
+
options: NotRequired[str]
|
|
53
|
+
application_name: NotRequired[str]
|
|
54
|
+
client_encoding: NotRequired[str]
|
|
55
|
+
gssencmode: NotRequired[str]
|
|
56
|
+
sslnegotiation: NotRequired[str]
|
|
57
|
+
sslcompression: NotRequired[str]
|
|
58
|
+
sslcert: NotRequired[str]
|
|
59
|
+
sslkey: NotRequired[str]
|
|
60
|
+
sslpassword: NotRequired[str]
|
|
61
|
+
sslrootcert: NotRequired[str]
|
|
62
|
+
sslcrl: NotRequired[str]
|
|
63
|
+
require_auth: NotRequired[str]
|
|
64
|
+
channel_binding: NotRequired[str]
|
|
65
|
+
krbsrvname: NotRequired[str]
|
|
66
|
+
gsslib: NotRequired[str]
|
|
67
|
+
gssdelegation: NotRequired[str]
|
|
68
|
+
service: NotRequired[str]
|
|
69
|
+
load_balance_hosts: NotRequired[str]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PsqlpyPoolParams(PsqlpyConnectionParams):
|
|
73
|
+
"""Psqlpy pool parameters."""
|
|
74
|
+
|
|
75
|
+
hosts: NotRequired[list[str]]
|
|
76
|
+
ports: NotRequired[list[int]]
|
|
77
|
+
conn_recycling_method: NotRequired[str]
|
|
78
|
+
max_db_pool_size: NotRequired[int]
|
|
79
|
+
configure: NotRequired["Callable[..., Any]"]
|
|
80
|
+
extra: NotRequired[dict[str, Any]]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class PsqlpyDriverFeatures(TypedDict):
|
|
84
|
+
"""Psqlpy driver feature flags.
|
|
85
|
+
|
|
86
|
+
enable_pgvector: Enable automatic pgvector extension support for vector similarity search.
|
|
87
|
+
Requires pgvector-python package installed.
|
|
88
|
+
Defaults to True when pgvector is installed.
|
|
89
|
+
Provides automatic conversion between NumPy arrays and PostgreSQL vector types.
|
|
90
|
+
json_serializer: Custom JSON serializer applied to the statement configuration.
|
|
91
|
+
json_deserializer: Custom JSON deserializer retained alongside the serializer for parity with asyncpg.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
enable_pgvector: NotRequired[bool]
|
|
95
|
+
json_serializer: NotRequired["Callable[[Any], str]"]
|
|
96
|
+
json_deserializer: NotRequired["Callable[[str], Any]"]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
__all__ = ("PsqlpyConfig", "PsqlpyConnectionParams", "PsqlpyCursor", "PsqlpyDriverFeatures", "PsqlpyPoolParams")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyDriver]):
|
|
103
|
+
"""Configuration for Psqlpy asynchronous database connections."""
|
|
104
|
+
|
|
105
|
+
driver_type: ClassVar[type[PsqlpyDriver]] = PsqlpyDriver
|
|
106
|
+
connection_type: "ClassVar[type[PsqlpyConnection]]" = PsqlpyConnection
|
|
107
|
+
supports_transactional_ddl: "ClassVar[bool]" = True
|
|
108
|
+
supports_native_arrow_export: ClassVar[bool] = True
|
|
109
|
+
supports_native_arrow_import: ClassVar[bool] = True
|
|
110
|
+
supports_native_parquet_export: ClassVar[bool] = True
|
|
111
|
+
supports_native_parquet_import: ClassVar[bool] = True
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self,
|
|
115
|
+
*,
|
|
116
|
+
pool_config: PsqlpyPoolParams | dict[str, Any] | None = None,
|
|
117
|
+
pool_instance: ConnectionPool | None = None,
|
|
118
|
+
migration_config: dict[str, Any] | None = None,
|
|
119
|
+
statement_config: StatementConfig | None = None,
|
|
120
|
+
driver_features: "PsqlpyDriverFeatures | dict[str, Any] | None" = None,
|
|
121
|
+
bind_key: str | None = None,
|
|
122
|
+
extension_config: "ExtensionConfigs | None" = None,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Initialize Psqlpy configuration.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
pool_config: Pool configuration parameters.
|
|
128
|
+
pool_instance: Existing connection pool instance to use.
|
|
129
|
+
migration_config: Migration configuration.
|
|
130
|
+
statement_config: SQL statement configuration.
|
|
131
|
+
driver_features: Driver feature configuration (TypedDict or dict).
|
|
132
|
+
bind_key: Optional unique identifier for this configuration.
|
|
133
|
+
extension_config: Extension-specific configuration (e.g., Litestar plugin settings).
|
|
134
|
+
"""
|
|
135
|
+
processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
|
|
136
|
+
if "extra" in processed_pool_config:
|
|
137
|
+
extras = processed_pool_config.pop("extra")
|
|
138
|
+
processed_pool_config.update(extras)
|
|
139
|
+
|
|
140
|
+
processed_driver_features: dict[str, Any] = dict(driver_features) if driver_features else {}
|
|
141
|
+
serializer = processed_driver_features.get("json_serializer")
|
|
142
|
+
serializer_callable = to_json if serializer is None else cast("Callable[[Any], str]", serializer)
|
|
143
|
+
processed_driver_features.setdefault("json_serializer", serializer_callable)
|
|
144
|
+
processed_driver_features.setdefault("enable_pgvector", PGVECTOR_INSTALLED)
|
|
145
|
+
|
|
146
|
+
super().__init__(
|
|
147
|
+
pool_config=processed_pool_config,
|
|
148
|
+
pool_instance=pool_instance,
|
|
149
|
+
migration_config=migration_config,
|
|
150
|
+
statement_config=statement_config or build_psqlpy_statement_config(json_serializer=serializer_callable),
|
|
151
|
+
driver_features=processed_driver_features,
|
|
152
|
+
bind_key=bind_key,
|
|
153
|
+
extension_config=extension_config,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def _get_pool_config_dict(self) -> dict[str, Any]:
|
|
157
|
+
"""Get pool configuration as plain dict for external library.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Dictionary with pool parameters, filtering out None values.
|
|
161
|
+
"""
|
|
162
|
+
return {k: v for k, v in self.pool_config.items() if v is not None}
|
|
163
|
+
|
|
164
|
+
async def _create_pool(self) -> "ConnectionPool":
|
|
165
|
+
"""Create the actual async connection pool."""
|
|
166
|
+
logger.info("Creating psqlpy connection pool", extra={"adapter": "psqlpy"})
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
config = self._get_pool_config_dict()
|
|
170
|
+
|
|
171
|
+
pool = ConnectionPool(**config)
|
|
172
|
+
logger.info("Psqlpy connection pool created successfully", extra={"adapter": "psqlpy"})
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.exception("Failed to create psqlpy connection pool", extra={"adapter": "psqlpy", "error": str(e)})
|
|
175
|
+
raise
|
|
176
|
+
return pool
|
|
177
|
+
|
|
178
|
+
async def _close_pool(self) -> None:
|
|
179
|
+
"""Close the actual async connection pool."""
|
|
180
|
+
if not self.pool_instance:
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
logger.info("Closing psqlpy connection pool", extra={"adapter": "psqlpy"})
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
self.pool_instance.close()
|
|
187
|
+
logger.info("Psqlpy connection pool closed successfully", extra={"adapter": "psqlpy"})
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.exception("Failed to close psqlpy connection pool", extra={"adapter": "psqlpy", "error": str(e)})
|
|
190
|
+
raise
|
|
191
|
+
|
|
192
|
+
async def close_pool(self) -> None:
|
|
193
|
+
"""Close the connection pool."""
|
|
194
|
+
await self._close_pool()
|
|
195
|
+
|
|
196
|
+
async def create_connection(self) -> "PsqlpyConnection":
|
|
197
|
+
"""Create a single async connection (not from pool).
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
A psqlpy Connection instance.
|
|
201
|
+
"""
|
|
202
|
+
if not self.pool_instance:
|
|
203
|
+
self.pool_instance = await self._create_pool()
|
|
204
|
+
|
|
205
|
+
return await self.pool_instance.connection()
|
|
206
|
+
|
|
207
|
+
@asynccontextmanager
|
|
208
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[PsqlpyConnection, None]:
|
|
209
|
+
"""Provide an async connection context manager.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
*args: Additional arguments.
|
|
213
|
+
**kwargs: Additional keyword arguments.
|
|
214
|
+
|
|
215
|
+
Yields:
|
|
216
|
+
A psqlpy Connection instance.
|
|
217
|
+
"""
|
|
218
|
+
if not self.pool_instance:
|
|
219
|
+
self.pool_instance = await self._create_pool()
|
|
220
|
+
|
|
221
|
+
async with self.pool_instance.acquire() as conn:
|
|
222
|
+
yield conn
|
|
223
|
+
|
|
224
|
+
@asynccontextmanager
|
|
225
|
+
async def provide_session(
|
|
226
|
+
self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
|
|
227
|
+
) -> AsyncGenerator[PsqlpyDriver, None]:
|
|
228
|
+
"""Provide an async driver session context manager.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
*args: Additional arguments.
|
|
232
|
+
statement_config: Optional statement configuration override.
|
|
233
|
+
**kwargs: Additional keyword arguments.
|
|
234
|
+
|
|
235
|
+
Yields:
|
|
236
|
+
A PsqlpyDriver instance.
|
|
237
|
+
"""
|
|
238
|
+
async with self.provide_connection(*args, **kwargs) as conn:
|
|
239
|
+
driver = self.driver_type(
|
|
240
|
+
connection=conn,
|
|
241
|
+
statement_config=statement_config or self.statement_config,
|
|
242
|
+
driver_features=self.driver_features,
|
|
243
|
+
)
|
|
244
|
+
yield self._prepare_driver(driver)
|
|
245
|
+
|
|
246
|
+
async def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
|
|
247
|
+
"""Provide async pool instance.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
The async connection pool.
|
|
251
|
+
"""
|
|
252
|
+
if not self.pool_instance:
|
|
253
|
+
self.pool_instance = await self.create_pool()
|
|
254
|
+
return self.pool_instance
|
|
255
|
+
|
|
256
|
+
def get_signature_namespace(self) -> "dict[str, Any]":
|
|
257
|
+
"""Get the signature namespace for Psqlpy types.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Dictionary mapping type names to types.
|
|
261
|
+
"""
|
|
262
|
+
namespace = super().get_signature_namespace()
|
|
263
|
+
namespace.update({
|
|
264
|
+
"PsqlpyConnection": PsqlpyConnection,
|
|
265
|
+
"PsqlpyConnectionParams": PsqlpyConnectionParams,
|
|
266
|
+
"PsqlpyCursor": PsqlpyCursor,
|
|
267
|
+
"PsqlpyDriver": PsqlpyDriver,
|
|
268
|
+
"PsqlpyExceptionHandler": PsqlpyExceptionHandler,
|
|
269
|
+
"PsqlpyPoolParams": PsqlpyPoolParams,
|
|
270
|
+
})
|
|
271
|
+
return namespace
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""PostgreSQL-specific data dictionary for metadata queries via psqlpy."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
|
+
|
|
6
|
+
from sqlspec.driver import AsyncDataDictionaryBase, AsyncDriverAdapterBase, VersionInfo
|
|
7
|
+
from sqlspec.utils.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
|
|
12
|
+
from sqlspec.adapters.psqlpy.driver import PsqlpyDriver
|
|
13
|
+
|
|
14
|
+
logger = get_logger("adapters.psqlpy.data_dictionary")
|
|
15
|
+
|
|
16
|
+
# Compiled regex patterns
|
|
17
|
+
POSTGRES_VERSION_PATTERN = re.compile(r"PostgreSQL (\d+)\.(\d+)(?:\.(\d+))?")
|
|
18
|
+
|
|
19
|
+
__all__ = ("PsqlpyAsyncDataDictionary",)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PsqlpyAsyncDataDictionary(AsyncDataDictionaryBase):
|
|
23
|
+
"""PostgreSQL-specific async data dictionary via psqlpy."""
|
|
24
|
+
|
|
25
|
+
async def get_version(self, driver: AsyncDriverAdapterBase) -> "VersionInfo | None":
|
|
26
|
+
"""Get PostgreSQL database version information.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
driver: Async database driver instance
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
PostgreSQL version information or None if detection fails
|
|
33
|
+
"""
|
|
34
|
+
version_str = await cast("PsqlpyDriver", driver).select_value("SELECT version()")
|
|
35
|
+
if not version_str:
|
|
36
|
+
logger.warning("No PostgreSQL version information found")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
# Parse version like "PostgreSQL 15.3 on x86_64-pc-linux-gnu..."
|
|
40
|
+
version_match = POSTGRES_VERSION_PATTERN.search(str(version_str))
|
|
41
|
+
if not version_match:
|
|
42
|
+
logger.warning("Could not parse PostgreSQL version: %s", version_str)
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
major = int(version_match.group(1))
|
|
46
|
+
minor = int(version_match.group(2))
|
|
47
|
+
patch = int(version_match.group(3)) if version_match.group(3) else 0
|
|
48
|
+
|
|
49
|
+
version_info = VersionInfo(major, minor, patch)
|
|
50
|
+
logger.debug("Detected PostgreSQL version: %s", version_info)
|
|
51
|
+
return version_info
|
|
52
|
+
|
|
53
|
+
async def get_feature_flag(self, driver: AsyncDriverAdapterBase, feature: str) -> bool:
|
|
54
|
+
"""Check if PostgreSQL database supports a specific feature.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
driver: Async database driver instance
|
|
58
|
+
feature: Feature name to check
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
True if feature is supported, False otherwise
|
|
62
|
+
"""
|
|
63
|
+
version_info = await self.get_version(driver)
|
|
64
|
+
if not version_info:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
feature_checks: dict[str, Callable[[VersionInfo], bool]] = {
|
|
68
|
+
"supports_json": lambda v: v >= VersionInfo(9, 2, 0),
|
|
69
|
+
"supports_jsonb": lambda v: v >= VersionInfo(9, 4, 0),
|
|
70
|
+
"supports_uuid": lambda _: True, # UUID extension widely available
|
|
71
|
+
"supports_arrays": lambda _: True, # PostgreSQL has excellent array support
|
|
72
|
+
"supports_returning": lambda v: v >= VersionInfo(8, 2, 0),
|
|
73
|
+
"supports_upsert": lambda v: v >= VersionInfo(9, 5, 0), # ON CONFLICT
|
|
74
|
+
"supports_window_functions": lambda v: v >= VersionInfo(8, 4, 0),
|
|
75
|
+
"supports_cte": lambda v: v >= VersionInfo(8, 4, 0),
|
|
76
|
+
"supports_transactions": lambda _: True,
|
|
77
|
+
"supports_prepared_statements": lambda _: True,
|
|
78
|
+
"supports_schemas": lambda _: True,
|
|
79
|
+
"supports_partitioning": lambda v: v >= VersionInfo(10, 0, 0),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if feature in feature_checks:
|
|
83
|
+
return bool(feature_checks[feature](version_info))
|
|
84
|
+
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
async def get_optimal_type(self, driver: AsyncDriverAdapterBase, type_category: str) -> str:
|
|
88
|
+
"""Get optimal PostgreSQL type for a category.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
driver: Async database driver instance
|
|
92
|
+
type_category: Type category
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
PostgreSQL-specific type name
|
|
96
|
+
"""
|
|
97
|
+
version_info = await self.get_version(driver)
|
|
98
|
+
|
|
99
|
+
if type_category == "json":
|
|
100
|
+
if version_info and version_info >= VersionInfo(9, 4, 0):
|
|
101
|
+
return "JSONB" # Prefer JSONB over JSON
|
|
102
|
+
if version_info and version_info >= VersionInfo(9, 2, 0):
|
|
103
|
+
return "JSON"
|
|
104
|
+
return "TEXT"
|
|
105
|
+
|
|
106
|
+
type_map = {
|
|
107
|
+
"uuid": "UUID",
|
|
108
|
+
"boolean": "BOOLEAN",
|
|
109
|
+
"timestamp": "TIMESTAMP WITH TIME ZONE",
|
|
110
|
+
"text": "TEXT",
|
|
111
|
+
"blob": "BYTEA",
|
|
112
|
+
"array": "ARRAY",
|
|
113
|
+
}
|
|
114
|
+
return type_map.get(type_category, "TEXT")
|
|
115
|
+
|
|
116
|
+
async def get_columns(
|
|
117
|
+
self, driver: AsyncDriverAdapterBase, table: str, schema: "str | None" = None
|
|
118
|
+
) -> "list[dict[str, Any]]":
|
|
119
|
+
"""Get column information for a table using pg_catalog.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
driver: Psqlpy async driver instance
|
|
123
|
+
table: Table name to query columns for
|
|
124
|
+
schema: Schema name (None for default 'public')
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
List of column metadata dictionaries with keys:
|
|
128
|
+
- column_name: Name of the column
|
|
129
|
+
- data_type: PostgreSQL data type
|
|
130
|
+
- is_nullable: Whether column allows NULL (YES/NO)
|
|
131
|
+
- column_default: Default value if any
|
|
132
|
+
|
|
133
|
+
Notes:
|
|
134
|
+
Uses pg_catalog instead of information_schema to avoid psqlpy's
|
|
135
|
+
inability to handle the PostgreSQL 'name' type returned by information_schema.
|
|
136
|
+
"""
|
|
137
|
+
psqlpy_driver = cast("PsqlpyDriver", driver)
|
|
138
|
+
|
|
139
|
+
schema_name = schema or "public"
|
|
140
|
+
sql = """
|
|
141
|
+
SELECT
|
|
142
|
+
a.attname::text AS column_name,
|
|
143
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type,
|
|
144
|
+
CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable,
|
|
145
|
+
pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text AS column_default
|
|
146
|
+
FROM pg_catalog.pg_attribute a
|
|
147
|
+
JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
|
|
148
|
+
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
|
|
149
|
+
LEFT JOIN pg_catalog.pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
|
150
|
+
WHERE c.relname = $1
|
|
151
|
+
AND n.nspname = $2
|
|
152
|
+
AND a.attnum > 0
|
|
153
|
+
AND NOT a.attisdropped
|
|
154
|
+
ORDER BY a.attnum
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
result = await psqlpy_driver.execute(sql, (table, schema_name))
|
|
158
|
+
return result.data or []
|
|
159
|
+
|
|
160
|
+
def list_available_features(self) -> "list[str]":
|
|
161
|
+
"""List available PostgreSQL feature flags.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
List of supported feature names
|
|
165
|
+
"""
|
|
166
|
+
return [
|
|
167
|
+
"supports_json",
|
|
168
|
+
"supports_jsonb",
|
|
169
|
+
"supports_uuid",
|
|
170
|
+
"supports_arrays",
|
|
171
|
+
"supports_returning",
|
|
172
|
+
"supports_upsert",
|
|
173
|
+
"supports_window_functions",
|
|
174
|
+
"supports_cte",
|
|
175
|
+
"supports_transactions",
|
|
176
|
+
"supports_prepared_statements",
|
|
177
|
+
"supports_schemas",
|
|
178
|
+
"supports_partitioning",
|
|
179
|
+
]
|