sqlspec 0.26.0__py3-none-any.whl → 0.28.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/__init__.py +7 -15
- sqlspec/_serialization.py +55 -25
- sqlspec/_typing.py +155 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +74 -2
- sqlspec/adapters/adbc/driver.py +226 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +536 -0
- sqlspec/adapters/aiosqlite/config.py +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +503 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +460 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +585 -0
- sqlspec/adapters/bigquery/config.py +36 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +489 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +563 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +225 -44
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1628 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +475 -86
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +483 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
- sqlspec/adapters/psqlpy/driver.py +108 -41
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +962 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +91 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +582 -0
- sqlspec/adapters/sqlite/config.py +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +331 -62
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +55 -47
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +234 -47
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +183 -160
- sqlspec/driver/_common.py +197 -109
- sqlspec/driver/_sync.py +189 -161
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -7
- 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/adapter.py +69 -61
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +56 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +349 -224
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +106 -36
- sqlspec/storage/_utils.py +85 -0
- sqlspec/storage/backends/fsspec.py +133 -107
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +276 -168
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +30 -84
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- sqlspec/utils/config_resolver.py +6 -6
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +205 -5
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +113 -4
- sqlspec/utils/sync_tools.py +36 -22
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +136 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
- sqlspec-0.28.0.dist-info/RECORD +221 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import logging
|
|
5
5
|
from contextlib import asynccontextmanager
|
|
6
|
-
from typing import TYPE_CHECKING, Any, ClassVar,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict, cast
|
|
7
7
|
|
|
8
8
|
import oracledb
|
|
9
9
|
from typing_extensions import NotRequired
|
|
10
10
|
|
|
11
|
+
from sqlspec.adapters.oracledb._numpy_handlers import register_numpy_handlers
|
|
11
12
|
from sqlspec.adapters.oracledb._types import (
|
|
12
13
|
OracleAsyncConnection,
|
|
13
14
|
OracleAsyncConnectionPool,
|
|
@@ -23,6 +24,7 @@ from sqlspec.adapters.oracledb.driver import (
|
|
|
23
24
|
)
|
|
24
25
|
from sqlspec.adapters.oracledb.migrations import OracleAsyncMigrationTracker, OracleSyncMigrationTracker
|
|
25
26
|
from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
|
|
27
|
+
from sqlspec.typing import NUMPY_INSTALLED
|
|
26
28
|
|
|
27
29
|
if TYPE_CHECKING:
|
|
28
30
|
from collections.abc import AsyncGenerator, Callable, Generator
|
|
@@ -32,12 +34,18 @@ if TYPE_CHECKING:
|
|
|
32
34
|
from sqlspec.core.statement import StatementConfig
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
__all__ = (
|
|
37
|
+
__all__ = (
|
|
38
|
+
"OracleAsyncConfig",
|
|
39
|
+
"OracleConnectionParams",
|
|
40
|
+
"OracleDriverFeatures",
|
|
41
|
+
"OraclePoolParams",
|
|
42
|
+
"OracleSyncConfig",
|
|
43
|
+
)
|
|
36
44
|
|
|
37
45
|
logger = logging.getLogger(__name__)
|
|
38
46
|
|
|
39
47
|
|
|
40
|
-
class OracleConnectionParams(TypedDict
|
|
48
|
+
class OracleConnectionParams(TypedDict):
|
|
41
49
|
"""OracleDB connection parameters."""
|
|
42
50
|
|
|
43
51
|
dsn: NotRequired[str]
|
|
@@ -58,7 +66,7 @@ class OracleConnectionParams(TypedDict, total=False):
|
|
|
58
66
|
edition: NotRequired[str]
|
|
59
67
|
|
|
60
68
|
|
|
61
|
-
class OraclePoolParams(OracleConnectionParams
|
|
69
|
+
class OraclePoolParams(OracleConnectionParams):
|
|
62
70
|
"""OracleDB pool parameters."""
|
|
63
71
|
|
|
64
72
|
min: NotRequired[int]
|
|
@@ -77,6 +85,23 @@ class OraclePoolParams(OracleConnectionParams, total=False):
|
|
|
77
85
|
extra: NotRequired[dict[str, Any]]
|
|
78
86
|
|
|
79
87
|
|
|
88
|
+
class OracleDriverFeatures(TypedDict):
|
|
89
|
+
"""Oracle driver feature flags.
|
|
90
|
+
|
|
91
|
+
enable_numpy_vectors: Enable automatic NumPy array ↔ Oracle VECTOR conversion.
|
|
92
|
+
Requires NumPy and Oracle Database 23ai or higher with VECTOR data type support.
|
|
93
|
+
Defaults to True when NumPy is installed.
|
|
94
|
+
Provides automatic bidirectional conversion between NumPy ndarrays and Oracle VECTOR columns.
|
|
95
|
+
Supports float32, float64, int8, and uint8 dtypes.
|
|
96
|
+
enable_lowercase_column_names: Normalize implicit Oracle uppercase column names to lowercase.
|
|
97
|
+
Targets unquoted Oracle identifiers that default to uppercase while preserving quoted case-sensitive aliases.
|
|
98
|
+
Defaults to True for compatibility with schema libraries expecting snake_case fields.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
enable_numpy_vectors: NotRequired[bool]
|
|
102
|
+
enable_lowercase_column_names: NotRequired[bool]
|
|
103
|
+
|
|
104
|
+
|
|
80
105
|
class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConnectionPool", OracleSyncDriver]):
|
|
81
106
|
"""Configuration for Oracle synchronous database connections."""
|
|
82
107
|
|
|
@@ -85,26 +110,29 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
85
110
|
driver_type: ClassVar[type[OracleSyncDriver]] = OracleSyncDriver
|
|
86
111
|
connection_type: "ClassVar[type[OracleSyncConnection]]" = OracleSyncConnection
|
|
87
112
|
migration_tracker_type: "ClassVar[type[OracleSyncMigrationTracker]]" = OracleSyncMigrationTracker
|
|
113
|
+
supports_transactional_ddl: ClassVar[bool] = False
|
|
88
114
|
|
|
89
115
|
def __init__(
|
|
90
116
|
self,
|
|
91
117
|
*,
|
|
92
|
-
pool_config: "
|
|
93
|
-
pool_instance: "
|
|
94
|
-
migration_config:
|
|
95
|
-
statement_config: "
|
|
96
|
-
driver_features: "
|
|
97
|
-
bind_key: "
|
|
118
|
+
pool_config: "OraclePoolParams | dict[str, Any] | None" = None,
|
|
119
|
+
pool_instance: "OracleSyncConnectionPool | None" = None,
|
|
120
|
+
migration_config: dict[str, Any] | None = None,
|
|
121
|
+
statement_config: "StatementConfig | None" = None,
|
|
122
|
+
driver_features: "OracleDriverFeatures | dict[str, Any] | None" = None,
|
|
123
|
+
bind_key: "str | None" = None,
|
|
124
|
+
extension_config: "dict[str, dict[str, Any]] | None" = None,
|
|
98
125
|
) -> None:
|
|
99
126
|
"""Initialize Oracle synchronous configuration.
|
|
100
127
|
|
|
101
128
|
Args:
|
|
102
|
-
pool_config: Pool configuration parameters
|
|
103
|
-
pool_instance: Existing pool instance to use
|
|
104
|
-
migration_config: Migration configuration
|
|
105
|
-
statement_config: Default SQL statement configuration
|
|
106
|
-
driver_features: Optional driver feature configuration
|
|
107
|
-
bind_key: Optional unique identifier for this configuration
|
|
129
|
+
pool_config: Pool configuration parameters.
|
|
130
|
+
pool_instance: Existing pool instance to use.
|
|
131
|
+
migration_config: Migration configuration.
|
|
132
|
+
statement_config: Default SQL statement configuration.
|
|
133
|
+
driver_features: Optional driver feature configuration (TypedDict or dict).
|
|
134
|
+
bind_key: Optional unique identifier for this configuration.
|
|
135
|
+
extension_config: Extension-specific configuration (e.g., Litestar plugin settings).
|
|
108
136
|
"""
|
|
109
137
|
|
|
110
138
|
processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
|
|
@@ -112,19 +140,43 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
112
140
|
extras = processed_pool_config.pop("extra")
|
|
113
141
|
processed_pool_config.update(extras)
|
|
114
142
|
statement_config = statement_config or oracledb_statement_config
|
|
143
|
+
|
|
144
|
+
processed_driver_features: dict[str, Any] = dict(driver_features) if driver_features else {}
|
|
145
|
+
if "enable_numpy_vectors" not in processed_driver_features:
|
|
146
|
+
processed_driver_features["enable_numpy_vectors"] = NUMPY_INSTALLED
|
|
147
|
+
if "enable_lowercase_column_names" not in processed_driver_features:
|
|
148
|
+
processed_driver_features["enable_lowercase_column_names"] = True
|
|
149
|
+
|
|
115
150
|
super().__init__(
|
|
116
151
|
pool_config=processed_pool_config,
|
|
117
152
|
pool_instance=pool_instance,
|
|
118
153
|
migration_config=migration_config,
|
|
119
154
|
statement_config=statement_config,
|
|
120
|
-
driver_features=
|
|
155
|
+
driver_features=processed_driver_features,
|
|
121
156
|
bind_key=bind_key,
|
|
157
|
+
extension_config=extension_config,
|
|
122
158
|
)
|
|
123
159
|
|
|
124
160
|
def _create_pool(self) -> "OracleSyncConnectionPool":
|
|
125
161
|
"""Create the actual connection pool."""
|
|
162
|
+
config = dict(self.pool_config)
|
|
163
|
+
|
|
164
|
+
if self.driver_features.get("enable_numpy_vectors", False):
|
|
165
|
+
config["session_callback"] = self._init_connection
|
|
126
166
|
|
|
127
|
-
return oracledb.create_pool(**
|
|
167
|
+
return oracledb.create_pool(**config)
|
|
168
|
+
|
|
169
|
+
def _init_connection(self, connection: "OracleSyncConnection", tag: str) -> None:
|
|
170
|
+
"""Initialize connection with optional NumPy vector support.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
connection: Oracle connection to initialize.
|
|
174
|
+
tag: Connection tag for session state (unused).
|
|
175
|
+
"""
|
|
176
|
+
if self.driver_features.get("enable_numpy_vectors", False):
|
|
177
|
+
from sqlspec.adapters.oracledb._numpy_handlers import register_numpy_handlers
|
|
178
|
+
|
|
179
|
+
register_numpy_handlers(connection)
|
|
128
180
|
|
|
129
181
|
def _close_pool(self) -> None:
|
|
130
182
|
"""Close the actual connection pool."""
|
|
@@ -158,7 +210,7 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
158
210
|
|
|
159
211
|
@contextlib.contextmanager
|
|
160
212
|
def provide_session(
|
|
161
|
-
self, *args: Any, statement_config: "
|
|
213
|
+
self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
|
|
162
214
|
) -> "Generator[OracleSyncDriver, None, None]":
|
|
163
215
|
"""Provide a driver session context manager.
|
|
164
216
|
|
|
@@ -172,7 +224,11 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
172
224
|
"""
|
|
173
225
|
_ = (args, kwargs) # Mark as intentionally unused
|
|
174
226
|
with self.provide_connection() as conn:
|
|
175
|
-
yield self.driver_type(
|
|
227
|
+
yield self.driver_type(
|
|
228
|
+
connection=conn,
|
|
229
|
+
statement_config=statement_config or self.statement_config,
|
|
230
|
+
driver_features=self.driver_features,
|
|
231
|
+
)
|
|
176
232
|
|
|
177
233
|
def provide_pool(self) -> "OracleSyncConnectionPool":
|
|
178
234
|
"""Provide pool instance.
|
|
@@ -214,26 +270,29 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
214
270
|
connection_type: "ClassVar[type[OracleAsyncConnection]]" = OracleAsyncConnection
|
|
215
271
|
driver_type: ClassVar[type[OracleAsyncDriver]] = OracleAsyncDriver
|
|
216
272
|
migration_tracker_type: "ClassVar[type[OracleAsyncMigrationTracker]]" = OracleAsyncMigrationTracker
|
|
273
|
+
supports_transactional_ddl: ClassVar[bool] = False
|
|
217
274
|
|
|
218
275
|
def __init__(
|
|
219
276
|
self,
|
|
220
277
|
*,
|
|
221
|
-
pool_config: "
|
|
222
|
-
pool_instance: "
|
|
223
|
-
migration_config:
|
|
224
|
-
statement_config: "
|
|
225
|
-
driver_features: "
|
|
226
|
-
bind_key: "
|
|
278
|
+
pool_config: "OraclePoolParams | dict[str, Any] | None" = None,
|
|
279
|
+
pool_instance: "OracleAsyncConnectionPool | None" = None,
|
|
280
|
+
migration_config: dict[str, Any] | None = None,
|
|
281
|
+
statement_config: "StatementConfig | None" = None,
|
|
282
|
+
driver_features: "OracleDriverFeatures | dict[str, Any] | None" = None,
|
|
283
|
+
bind_key: "str | None" = None,
|
|
284
|
+
extension_config: "dict[str, dict[str, Any]] | None" = None,
|
|
227
285
|
) -> None:
|
|
228
286
|
"""Initialize Oracle asynchronous configuration.
|
|
229
287
|
|
|
230
288
|
Args:
|
|
231
|
-
pool_config: Pool configuration parameters
|
|
232
|
-
pool_instance: Existing pool instance to use
|
|
233
|
-
migration_config: Migration configuration
|
|
234
|
-
statement_config: Default SQL statement configuration
|
|
235
|
-
driver_features: Optional driver feature configuration
|
|
236
|
-
bind_key: Optional unique identifier for this configuration
|
|
289
|
+
pool_config: Pool configuration parameters.
|
|
290
|
+
pool_instance: Existing pool instance to use.
|
|
291
|
+
migration_config: Migration configuration.
|
|
292
|
+
statement_config: Default SQL statement configuration.
|
|
293
|
+
driver_features: Optional driver feature configuration (TypedDict or dict).
|
|
294
|
+
bind_key: Optional unique identifier for this configuration.
|
|
295
|
+
extension_config: Extension-specific configuration (e.g., Litestar plugin settings).
|
|
237
296
|
"""
|
|
238
297
|
|
|
239
298
|
processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
|
|
@@ -241,19 +300,40 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
241
300
|
extras = processed_pool_config.pop("extra")
|
|
242
301
|
processed_pool_config.update(extras)
|
|
243
302
|
|
|
303
|
+
processed_driver_features: dict[str, Any] = dict(driver_features) if driver_features else {}
|
|
304
|
+
if "enable_numpy_vectors" not in processed_driver_features:
|
|
305
|
+
processed_driver_features["enable_numpy_vectors"] = NUMPY_INSTALLED
|
|
306
|
+
if "enable_lowercase_column_names" not in processed_driver_features:
|
|
307
|
+
processed_driver_features["enable_lowercase_column_names"] = True
|
|
308
|
+
|
|
244
309
|
super().__init__(
|
|
245
310
|
pool_config=processed_pool_config,
|
|
246
311
|
pool_instance=pool_instance,
|
|
247
312
|
migration_config=migration_config,
|
|
248
313
|
statement_config=statement_config or oracledb_statement_config,
|
|
249
|
-
driver_features=
|
|
314
|
+
driver_features=processed_driver_features,
|
|
250
315
|
bind_key=bind_key,
|
|
316
|
+
extension_config=extension_config,
|
|
251
317
|
)
|
|
252
318
|
|
|
253
319
|
async def _create_pool(self) -> "OracleAsyncConnectionPool":
|
|
254
320
|
"""Create the actual async connection pool."""
|
|
321
|
+
config = dict(self.pool_config)
|
|
322
|
+
|
|
323
|
+
if self.driver_features.get("enable_numpy_vectors", False):
|
|
324
|
+
config["session_callback"] = self._init_connection
|
|
255
325
|
|
|
256
|
-
return oracledb.create_pool_async(**
|
|
326
|
+
return oracledb.create_pool_async(**config)
|
|
327
|
+
|
|
328
|
+
async def _init_connection(self, connection: "OracleAsyncConnection", tag: str) -> None:
|
|
329
|
+
"""Initialize async connection with optional NumPy vector support.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
connection: Oracle async connection to initialize.
|
|
333
|
+
tag: Connection tag for session state (unused).
|
|
334
|
+
"""
|
|
335
|
+
if self.driver_features.get("enable_numpy_vectors", False):
|
|
336
|
+
register_numpy_handlers(connection)
|
|
257
337
|
|
|
258
338
|
async def _close_pool(self) -> None:
|
|
259
339
|
"""Close the actual async connection pool."""
|
|
@@ -291,7 +371,7 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
291
371
|
|
|
292
372
|
@asynccontextmanager
|
|
293
373
|
async def provide_session(
|
|
294
|
-
self, *args: Any, statement_config: "
|
|
374
|
+
self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
|
|
295
375
|
) -> "AsyncGenerator[OracleAsyncDriver, None]":
|
|
296
376
|
"""Provide an async driver session context manager.
|
|
297
377
|
|
|
@@ -305,7 +385,11 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
305
385
|
"""
|
|
306
386
|
_ = (args, kwargs) # Mark as intentionally unused
|
|
307
387
|
async with self.provide_connection() as conn:
|
|
308
|
-
yield self.driver_type(
|
|
388
|
+
yield self.driver_type(
|
|
389
|
+
connection=conn,
|
|
390
|
+
statement_config=statement_config or self.statement_config,
|
|
391
|
+
driver_features=self.driver_features,
|
|
392
|
+
)
|
|
309
393
|
|
|
310
394
|
async def provide_pool(self) -> "OracleAsyncConnectionPool":
|
|
311
395
|
"""Provide async pool instance.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import re
|
|
5
5
|
from contextlib import suppress
|
|
6
|
-
from typing import TYPE_CHECKING,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
7
7
|
|
|
8
8
|
from sqlspec.driver import (
|
|
9
9
|
AsyncDataDictionaryBase,
|
|
@@ -15,6 +15,8 @@ from sqlspec.driver import (
|
|
|
15
15
|
from sqlspec.utils.logging import get_logger
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
|
|
18
20
|
from sqlspec.adapters.oracledb.driver import OracleAsyncDriver, OracleSyncDriver
|
|
19
21
|
|
|
20
22
|
logger = get_logger("adapters.oracledb.data_dictionary")
|
|
@@ -35,12 +37,7 @@ class OracleVersionInfo(VersionInfo):
|
|
|
35
37
|
"""Oracle database version information."""
|
|
36
38
|
|
|
37
39
|
def __init__(
|
|
38
|
-
self,
|
|
39
|
-
major: int,
|
|
40
|
-
minor: int = 0,
|
|
41
|
-
patch: int = 0,
|
|
42
|
-
compatible: "Optional[str]" = None,
|
|
43
|
-
is_autonomous: bool = False,
|
|
40
|
+
self, major: int, minor: int = 0, patch: int = 0, compatible: "str | None" = None, is_autonomous: bool = False
|
|
44
41
|
) -> None:
|
|
45
42
|
"""Initialize Oracle version info.
|
|
46
43
|
|
|
@@ -56,7 +53,7 @@ class OracleVersionInfo(VersionInfo):
|
|
|
56
53
|
self.is_autonomous = is_autonomous
|
|
57
54
|
|
|
58
55
|
@property
|
|
59
|
-
def compatible_major(self) -> "
|
|
56
|
+
def compatible_major(self) -> "int | None":
|
|
60
57
|
"""Get major version from compatible parameter."""
|
|
61
58
|
if not self.compatible:
|
|
62
59
|
return None
|
|
@@ -109,7 +106,31 @@ class OracleDataDictionaryMixin:
|
|
|
109
106
|
|
|
110
107
|
__slots__ = ()
|
|
111
108
|
|
|
112
|
-
def
|
|
109
|
+
def _get_columns_sql(self, table: str, schema: "str | None" = None) -> str:
|
|
110
|
+
"""Get SQL to query column metadata from Oracle data dictionary.
|
|
111
|
+
|
|
112
|
+
Uses USER_TAB_COLUMNS which returns column names in UPPERCASE.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
table: Table name to query columns for
|
|
116
|
+
schema: Schema name (unused for USER_TAB_COLUMNS)
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
SQL string for Oracle's USER_TAB_COLUMNS query
|
|
120
|
+
"""
|
|
121
|
+
_ = schema
|
|
122
|
+
return f"""
|
|
123
|
+
SELECT
|
|
124
|
+
column_name AS "column_name",
|
|
125
|
+
data_type AS "data_type",
|
|
126
|
+
data_length AS "data_length",
|
|
127
|
+
nullable AS "nullable"
|
|
128
|
+
FROM user_tab_columns
|
|
129
|
+
WHERE table_name = '{table.upper()}'
|
|
130
|
+
ORDER BY column_id
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def _get_oracle_version(self, driver: "OracleAsyncDriver | OracleSyncDriver") -> "OracleVersionInfo | None":
|
|
113
134
|
"""Get Oracle database version information.
|
|
114
135
|
|
|
115
136
|
Args:
|
|
@@ -118,7 +139,7 @@ class OracleDataDictionaryMixin:
|
|
|
118
139
|
Returns:
|
|
119
140
|
Oracle version information or None if detection fails
|
|
120
141
|
"""
|
|
121
|
-
banner = driver.select_value("SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'")
|
|
142
|
+
banner = driver.select_value("SELECT banner AS \"banner\" FROM v$version WHERE banner LIKE 'Oracle%'")
|
|
122
143
|
|
|
123
144
|
# Parse version from banner like "Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production"
|
|
124
145
|
# or "Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production"
|
|
@@ -143,7 +164,7 @@ class OracleDataDictionaryMixin:
|
|
|
143
164
|
logger.debug("Detected Oracle version: %s", version_info)
|
|
144
165
|
return version_info
|
|
145
166
|
|
|
146
|
-
def _get_oracle_compatible(self, driver: "OracleAsyncDriver | OracleSyncDriver") -> "
|
|
167
|
+
def _get_oracle_compatible(self, driver: "OracleAsyncDriver | OracleSyncDriver") -> "str | None":
|
|
147
168
|
"""Get Oracle compatible parameter value.
|
|
148
169
|
|
|
149
170
|
Args:
|
|
@@ -153,14 +174,14 @@ class OracleDataDictionaryMixin:
|
|
|
153
174
|
Compatible parameter value or None if detection fails
|
|
154
175
|
"""
|
|
155
176
|
try:
|
|
156
|
-
compatible = driver.select_value("SELECT value FROM v$parameter WHERE name = 'compatible'")
|
|
177
|
+
compatible = driver.select_value("SELECT value AS \"value\" FROM v$parameter WHERE name = 'compatible'")
|
|
157
178
|
logger.debug("Detected Oracle compatible parameter: %s", compatible)
|
|
158
179
|
return str(compatible)
|
|
159
180
|
except Exception:
|
|
160
181
|
logger.warning("Compatible parameter not found")
|
|
161
182
|
return None
|
|
162
183
|
|
|
163
|
-
def _get_oracle_json_type(self, version_info: "
|
|
184
|
+
def _get_oracle_json_type(self, version_info: "OracleVersionInfo | None") -> str:
|
|
164
185
|
"""Determine the appropriate JSON column type for Oracle.
|
|
165
186
|
|
|
166
187
|
Args:
|
|
@@ -199,10 +220,10 @@ class OracleSyncDataDictionary(OracleDataDictionaryMixin, SyncDataDictionaryBase
|
|
|
199
220
|
Returns:
|
|
200
221
|
True if this is an Autonomous Database, False otherwise
|
|
201
222
|
"""
|
|
202
|
-
result = driver.select_value_or_none(
|
|
223
|
+
result = driver.select_value_or_none('SELECT COUNT(1) AS "cnt" FROM v$pdbs WHERE cloud_identity IS NOT NULL')
|
|
203
224
|
return bool(result and int(result) > 0)
|
|
204
225
|
|
|
205
|
-
def get_version(self, driver: SyncDriverAdapterBase) -> "
|
|
226
|
+
def get_version(self, driver: SyncDriverAdapterBase) -> "OracleVersionInfo | None":
|
|
206
227
|
"""Get Oracle database version information.
|
|
207
228
|
|
|
208
229
|
Args:
|
|
@@ -275,6 +296,28 @@ class OracleSyncDataDictionary(OracleDataDictionaryMixin, SyncDataDictionaryBase
|
|
|
275
296
|
}
|
|
276
297
|
return type_map.get(type_category, "VARCHAR2(255)")
|
|
277
298
|
|
|
299
|
+
def get_columns(
|
|
300
|
+
self, driver: SyncDriverAdapterBase, table: str, schema: "str | None" = None
|
|
301
|
+
) -> "list[dict[str, Any]]":
|
|
302
|
+
"""Get column information for a table from Oracle data dictionary.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
driver: Database driver instance
|
|
306
|
+
table: Table name to query columns for
|
|
307
|
+
schema: Schema name (ignored for USER_TAB_COLUMNS)
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
List of column metadata dictionaries with keys:
|
|
311
|
+
- column_name: Name of the column
|
|
312
|
+
- data_type: Oracle data type
|
|
313
|
+
- data_length: Maximum length (for character types)
|
|
314
|
+
- nullable: 'Y' or 'N'
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
oracle_driver = cast("OracleSyncDriver", driver)
|
|
318
|
+
result = oracle_driver.execute(self._get_columns_sql(table, schema))
|
|
319
|
+
return result.get_data()
|
|
320
|
+
|
|
278
321
|
def list_available_features(self) -> "list[str]":
|
|
279
322
|
"""List available Oracle feature flags.
|
|
280
323
|
|
|
@@ -296,7 +339,7 @@ class OracleSyncDataDictionary(OracleDataDictionaryMixin, SyncDataDictionaryBase
|
|
|
296
339
|
class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBase):
|
|
297
340
|
"""Oracle-specific async data dictionary."""
|
|
298
341
|
|
|
299
|
-
async def get_version(self, driver: AsyncDriverAdapterBase) -> "
|
|
342
|
+
async def get_version(self, driver: AsyncDriverAdapterBase) -> "OracleVersionInfo | None":
|
|
300
343
|
"""Get Oracle database version information.
|
|
301
344
|
|
|
302
345
|
Args:
|
|
@@ -306,7 +349,7 @@ class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBa
|
|
|
306
349
|
Oracle version information or None if detection fails
|
|
307
350
|
"""
|
|
308
351
|
banner = await cast("OracleAsyncDriver", driver).select_value(
|
|
309
|
-
"SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'"
|
|
352
|
+
"SELECT banner AS \"banner\" FROM v$version WHERE banner LIKE 'Oracle%'"
|
|
310
353
|
)
|
|
311
354
|
|
|
312
355
|
version_match = ORACLE_VERSION_PATTERN.search(str(banner))
|
|
@@ -336,7 +379,7 @@ class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBa
|
|
|
336
379
|
logger.debug("Detected Oracle version: %s", version_info)
|
|
337
380
|
return version_info
|
|
338
381
|
|
|
339
|
-
async def _get_oracle_compatible_async(self, driver: "OracleAsyncDriver") -> "
|
|
382
|
+
async def _get_oracle_compatible_async(self, driver: "OracleAsyncDriver") -> "str | None":
|
|
340
383
|
"""Get Oracle compatible parameter value (async version).
|
|
341
384
|
|
|
342
385
|
Args:
|
|
@@ -346,7 +389,9 @@ class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBa
|
|
|
346
389
|
Compatible parameter value or None if detection fails
|
|
347
390
|
"""
|
|
348
391
|
try:
|
|
349
|
-
compatible = await driver.select_value(
|
|
392
|
+
compatible = await driver.select_value(
|
|
393
|
+
"SELECT value AS \"value\" FROM v$parameter WHERE name = 'compatible'"
|
|
394
|
+
)
|
|
350
395
|
logger.debug("Detected Oracle compatible parameter: %s", compatible)
|
|
351
396
|
return str(compatible)
|
|
352
397
|
except Exception:
|
|
@@ -364,7 +409,7 @@ class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBa
|
|
|
364
409
|
"""
|
|
365
410
|
# Check for cloud_identity in v$pdbs (most reliable for Autonomous)
|
|
366
411
|
with suppress(Exception):
|
|
367
|
-
result = await driver.execute(
|
|
412
|
+
result = await driver.execute('SELECT COUNT(1) AS "cnt" FROM v$pdbs WHERE cloud_identity IS NOT NULL')
|
|
368
413
|
if result.data:
|
|
369
414
|
count = result.data[0]["cnt"] if isinstance(result.data[0], dict) else result.data[0][0]
|
|
370
415
|
if int(count) > 0:
|
|
@@ -424,6 +469,28 @@ class OracleAsyncDataDictionary(OracleDataDictionaryMixin, AsyncDataDictionaryBa
|
|
|
424
469
|
type_map = {"uuid": "RAW(16)", "boolean": "NUMBER(1)", "timestamp": "TIMESTAMP", "text": "CLOB", "blob": "BLOB"}
|
|
425
470
|
return type_map.get(type_category, "VARCHAR2(255)")
|
|
426
471
|
|
|
472
|
+
async def get_columns(
|
|
473
|
+
self, driver: AsyncDriverAdapterBase, table: str, schema: "str | None" = None
|
|
474
|
+
) -> "list[dict[str, Any]]":
|
|
475
|
+
"""Get column information for a table from Oracle data dictionary.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
driver: Async database driver instance
|
|
479
|
+
table: Table name to query columns for
|
|
480
|
+
schema: Schema name (ignored for USER_TAB_COLUMNS)
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
List of column metadata dictionaries with keys:
|
|
484
|
+
- column_name: Name of the column
|
|
485
|
+
- data_type: Oracle data type
|
|
486
|
+
- data_length: Maximum length (for character types)
|
|
487
|
+
- nullable: 'Y' or 'N'
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
oracle_driver = cast("OracleAsyncDriver", driver)
|
|
491
|
+
result = await oracle_driver.execute(self._get_columns_sql(table, schema))
|
|
492
|
+
return result.get_data()
|
|
493
|
+
|
|
427
494
|
def list_available_features(self) -> "list[str]":
|
|
428
495
|
"""List available Oracle feature flags.
|
|
429
496
|
|