sqlspec 0.25.0__py3-none-any.whl → 0.27.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 +256 -24
- sqlspec/_typing.py +71 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- 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/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- 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 +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- 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 +450 -0
- sqlspec/adapters/asyncpg/config.py +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- 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 +576 -0
- sqlspec/adapters/bigquery/config.py +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- 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/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- 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 +1745 -0
- sqlspec/adapters/oracledb/config.py +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- 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 +482 -0
- sqlspec/adapters/psqlpy/config.py +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- 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/_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 +944 -0
- sqlspec/adapters/psycopg/config.py +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- 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 +572 -0
- sqlspec/adapters/sqlite/config.py +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -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 +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -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 +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- 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 +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +50 -2
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
"""Oracle Driver"""
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import logging
|
|
4
|
-
|
|
5
|
+
import re
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Final
|
|
5
7
|
|
|
6
8
|
import oracledb
|
|
7
9
|
from oracledb import AsyncCursor, Cursor
|
|
8
10
|
|
|
9
11
|
from sqlspec.adapters.oracledb._types import OracleAsyncConnection, OracleSyncConnection
|
|
12
|
+
from sqlspec.adapters.oracledb.data_dictionary import OracleAsyncDataDictionary, OracleSyncDataDictionary
|
|
13
|
+
from sqlspec.adapters.oracledb.type_converter import OracleTypeConverter
|
|
10
14
|
from sqlspec.core.cache import get_cache_config
|
|
11
15
|
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
12
16
|
from sqlspec.core.statement import StatementConfig
|
|
13
|
-
from sqlspec.driver import
|
|
14
|
-
|
|
17
|
+
from sqlspec.driver import (
|
|
18
|
+
AsyncDataDictionaryBase,
|
|
19
|
+
AsyncDriverAdapterBase,
|
|
20
|
+
SyncDataDictionaryBase,
|
|
21
|
+
SyncDriverAdapterBase,
|
|
22
|
+
)
|
|
23
|
+
from sqlspec.exceptions import (
|
|
24
|
+
CheckViolationError,
|
|
25
|
+
DatabaseConnectionError,
|
|
26
|
+
DataError,
|
|
27
|
+
ForeignKeyViolationError,
|
|
28
|
+
IntegrityError,
|
|
29
|
+
NotNullViolationError,
|
|
30
|
+
OperationalError,
|
|
31
|
+
SQLParsingError,
|
|
32
|
+
SQLSpecError,
|
|
33
|
+
TransactionError,
|
|
34
|
+
UniqueViolationError,
|
|
35
|
+
)
|
|
36
|
+
from sqlspec.utils.serializers import to_json
|
|
15
37
|
|
|
16
38
|
if TYPE_CHECKING:
|
|
17
39
|
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
@@ -22,6 +44,14 @@ if TYPE_CHECKING:
|
|
|
22
44
|
|
|
23
45
|
logger = logging.getLogger(__name__)
|
|
24
46
|
|
|
47
|
+
# Oracle-specific constants
|
|
48
|
+
LARGE_STRING_THRESHOLD = 3000 # Threshold for large string parameters to avoid ORA-01704
|
|
49
|
+
|
|
50
|
+
_type_converter = OracleTypeConverter()
|
|
51
|
+
|
|
52
|
+
IMPLICIT_UPPER_COLUMN_PATTERN: Final[re.Pattern[str]] = re.compile(r"^(?!\d)(?:[A-Z0-9_]+)$")
|
|
53
|
+
|
|
54
|
+
|
|
25
55
|
__all__ = (
|
|
26
56
|
"OracleAsyncDriver",
|
|
27
57
|
"OracleAsyncExceptionHandler",
|
|
@@ -31,16 +61,93 @@ __all__ = (
|
|
|
31
61
|
)
|
|
32
62
|
|
|
33
63
|
|
|
64
|
+
def _normalize_column_names(column_names: "list[str]", driver_features: "dict[str, Any]") -> "list[str]":
|
|
65
|
+
should_lowercase = driver_features.get("enable_lowercase_column_names", False)
|
|
66
|
+
if not should_lowercase:
|
|
67
|
+
return column_names
|
|
68
|
+
normalized: list[str] = []
|
|
69
|
+
for name in column_names:
|
|
70
|
+
if name and IMPLICIT_UPPER_COLUMN_PATTERN.fullmatch(name):
|
|
71
|
+
normalized.append(name.lower())
|
|
72
|
+
else:
|
|
73
|
+
normalized.append(name)
|
|
74
|
+
return normalized
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _coerce_sync_row_values(row: "tuple[Any, ...]") -> "list[Any]":
|
|
78
|
+
"""Coerce LOB handles to concrete values for synchronous execution.
|
|
79
|
+
|
|
80
|
+
Processes each value in the row, reading LOB objects and applying
|
|
81
|
+
type detection for JSON values stored in CLOBs.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
row: Tuple of column values from database fetch.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
List of coerced values with LOBs read to strings/bytes.
|
|
88
|
+
"""
|
|
89
|
+
coerced_values: list[Any] = []
|
|
90
|
+
for value in row:
|
|
91
|
+
if hasattr(value, "read"):
|
|
92
|
+
try:
|
|
93
|
+
processed_value = value.read()
|
|
94
|
+
except Exception:
|
|
95
|
+
coerced_values.append(value)
|
|
96
|
+
continue
|
|
97
|
+
if isinstance(processed_value, str):
|
|
98
|
+
processed_value = _type_converter.convert_if_detected(processed_value)
|
|
99
|
+
coerced_values.append(processed_value)
|
|
100
|
+
else:
|
|
101
|
+
coerced_values.append(value)
|
|
102
|
+
return coerced_values
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
async def _coerce_async_row_values(row: "tuple[Any, ...]") -> "list[Any]":
|
|
106
|
+
"""Coerce LOB handles to concrete values for asynchronous execution.
|
|
107
|
+
|
|
108
|
+
Processes each value in the row, reading LOB objects asynchronously
|
|
109
|
+
and applying type detection for JSON values stored in CLOBs.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
row: Tuple of column values from database fetch.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of coerced values with LOBs read to strings/bytes.
|
|
116
|
+
"""
|
|
117
|
+
coerced_values: list[Any] = []
|
|
118
|
+
for value in row:
|
|
119
|
+
if hasattr(value, "read"):
|
|
120
|
+
try:
|
|
121
|
+
processed_value = await _type_converter.process_lob(value)
|
|
122
|
+
except Exception:
|
|
123
|
+
coerced_values.append(value)
|
|
124
|
+
continue
|
|
125
|
+
if isinstance(processed_value, str):
|
|
126
|
+
processed_value = _type_converter.convert_if_detected(processed_value)
|
|
127
|
+
coerced_values.append(processed_value)
|
|
128
|
+
else:
|
|
129
|
+
coerced_values.append(value)
|
|
130
|
+
return coerced_values
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
ORA_CHECK_CONSTRAINT = 2290
|
|
134
|
+
ORA_INTEGRITY_RANGE_START = 2200
|
|
135
|
+
ORA_INTEGRITY_RANGE_END = 2300
|
|
136
|
+
ORA_PARSING_RANGE_START = 900
|
|
137
|
+
ORA_PARSING_RANGE_END = 1000
|
|
138
|
+
ORA_TABLESPACE_FULL = 1652
|
|
139
|
+
|
|
140
|
+
|
|
34
141
|
oracledb_statement_config = StatementConfig(
|
|
35
142
|
dialect="oracle",
|
|
36
143
|
parameter_config=ParameterStyleConfig(
|
|
37
144
|
default_parameter_style=ParameterStyle.POSITIONAL_COLON,
|
|
38
145
|
supported_parameter_styles={ParameterStyle.NAMED_COLON, ParameterStyle.POSITIONAL_COLON, ParameterStyle.QMARK},
|
|
39
|
-
default_execution_parameter_style=ParameterStyle.
|
|
146
|
+
default_execution_parameter_style=ParameterStyle.NAMED_COLON,
|
|
40
147
|
supported_execution_parameter_styles={ParameterStyle.NAMED_COLON, ParameterStyle.POSITIONAL_COLON},
|
|
41
|
-
type_coercion_map={},
|
|
148
|
+
type_coercion_map={dict: to_json, list: to_json},
|
|
42
149
|
has_native_list_expansion=False,
|
|
43
|
-
needs_static_script_compilation=
|
|
150
|
+
needs_static_script_compilation=False,
|
|
44
151
|
preserve_parameter_format=True,
|
|
45
152
|
),
|
|
46
153
|
enable_parsing=True,
|
|
@@ -57,14 +164,13 @@ class OracleSyncCursor:
|
|
|
57
164
|
|
|
58
165
|
def __init__(self, connection: OracleSyncConnection) -> None:
|
|
59
166
|
self.connection = connection
|
|
60
|
-
self.cursor:
|
|
167
|
+
self.cursor: Cursor | None = None
|
|
61
168
|
|
|
62
169
|
def __enter__(self) -> Cursor:
|
|
63
170
|
self.cursor = self.connection.cursor()
|
|
64
171
|
return self.cursor
|
|
65
172
|
|
|
66
|
-
def __exit__(self,
|
|
67
|
-
_ = (exc_type, exc_val, exc_tb) # Mark as intentionally unused
|
|
173
|
+
def __exit__(self, *_: Any) -> None:
|
|
68
174
|
if self.cursor is not None:
|
|
69
175
|
self.cursor.close()
|
|
70
176
|
|
|
@@ -76,7 +182,7 @@ class OracleAsyncCursor:
|
|
|
76
182
|
|
|
77
183
|
def __init__(self, connection: OracleAsyncConnection) -> None:
|
|
78
184
|
self.connection = connection
|
|
79
|
-
self.cursor:
|
|
185
|
+
self.cursor: AsyncCursor | None = None
|
|
80
186
|
|
|
81
187
|
async def __aenter__(self) -> AsyncCursor:
|
|
82
188
|
self.cursor = self.connection.cursor()
|
|
@@ -85,11 +191,18 @@ class OracleAsyncCursor:
|
|
|
85
191
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
86
192
|
_ = (exc_type, exc_val, exc_tb) # Mark as intentionally unused
|
|
87
193
|
if self.cursor is not None:
|
|
88
|
-
|
|
194
|
+
with contextlib.suppress(Exception):
|
|
195
|
+
# Oracle async cursors have a synchronous close method
|
|
196
|
+
# but we need to ensure proper cleanup in the event loop context
|
|
197
|
+
self.cursor.close()
|
|
89
198
|
|
|
90
199
|
|
|
91
200
|
class OracleSyncExceptionHandler:
|
|
92
|
-
"""Context manager for handling Oracle database exceptions
|
|
201
|
+
"""Context manager for handling Oracle database exceptions.
|
|
202
|
+
|
|
203
|
+
Maps Oracle ORA-XXXXX error codes to specific SQLSpec exceptions
|
|
204
|
+
for better error handling in application code.
|
|
205
|
+
"""
|
|
93
206
|
|
|
94
207
|
__slots__ = ()
|
|
95
208
|
|
|
@@ -97,46 +210,101 @@ class OracleSyncExceptionHandler:
|
|
|
97
210
|
return None
|
|
98
211
|
|
|
99
212
|
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
100
|
-
_ = exc_tb
|
|
213
|
+
_ = exc_tb
|
|
101
214
|
if exc_type is None:
|
|
102
215
|
return
|
|
103
|
-
|
|
104
|
-
if issubclass(exc_type, oracledb.IntegrityError):
|
|
105
|
-
e = exc_val
|
|
106
|
-
msg = f"Oracle integrity constraint violation: {e}"
|
|
107
|
-
raise SQLSpecError(msg) from e
|
|
108
|
-
if issubclass(exc_type, oracledb.ProgrammingError):
|
|
109
|
-
e = exc_val
|
|
110
|
-
error_msg = str(e).lower()
|
|
111
|
-
if "syntax" in error_msg or "parse" in error_msg:
|
|
112
|
-
msg = f"Oracle SQL syntax error: {e}"
|
|
113
|
-
raise SQLParsingError(msg) from e
|
|
114
|
-
msg = f"Oracle programming error: {e}"
|
|
115
|
-
raise SQLSpecError(msg) from e
|
|
116
|
-
if issubclass(exc_type, oracledb.OperationalError):
|
|
117
|
-
e = exc_val
|
|
118
|
-
msg = f"Oracle operational error: {e}"
|
|
119
|
-
raise SQLSpecError(msg) from e
|
|
120
216
|
if issubclass(exc_type, oracledb.DatabaseError):
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
217
|
+
self._map_oracle_exception(exc_val)
|
|
218
|
+
|
|
219
|
+
def _map_oracle_exception(self, e: Any) -> None:
|
|
220
|
+
"""Map Oracle exception to SQLSpec exception.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
e: oracledb.DatabaseError instance
|
|
224
|
+
"""
|
|
225
|
+
error_obj = e.args[0] if e.args else None
|
|
226
|
+
if not error_obj:
|
|
227
|
+
self._raise_generic_error(e, None)
|
|
228
|
+
|
|
229
|
+
error_code = getattr(error_obj, "code", None)
|
|
230
|
+
|
|
231
|
+
if not error_code:
|
|
232
|
+
self._raise_generic_error(e, None)
|
|
233
|
+
|
|
234
|
+
if error_code == 1:
|
|
235
|
+
self._raise_unique_violation(e, error_code)
|
|
236
|
+
elif error_code in {2291, 2292}:
|
|
237
|
+
self._raise_foreign_key_violation(e, error_code)
|
|
238
|
+
elif error_code == ORA_CHECK_CONSTRAINT:
|
|
239
|
+
self._raise_check_violation(e, error_code)
|
|
240
|
+
elif error_code in {1400, 1407}:
|
|
241
|
+
self._raise_not_null_violation(e, error_code)
|
|
242
|
+
elif error_code and ORA_INTEGRITY_RANGE_START <= error_code < ORA_INTEGRITY_RANGE_END:
|
|
243
|
+
self._raise_integrity_error(e, error_code)
|
|
244
|
+
elif error_code in {1017, 12154, 12541, 12545, 12514, 12505}:
|
|
245
|
+
self._raise_connection_error(e, error_code)
|
|
246
|
+
elif error_code in {60, 8176}:
|
|
247
|
+
self._raise_transaction_error(e, error_code)
|
|
248
|
+
elif error_code in {1722, 1858, 1840}:
|
|
249
|
+
self._raise_data_error(e, error_code)
|
|
250
|
+
elif error_code and ORA_PARSING_RANGE_START <= error_code < ORA_PARSING_RANGE_END:
|
|
251
|
+
self._raise_parsing_error(e, error_code)
|
|
252
|
+
elif error_code == ORA_TABLESPACE_FULL:
|
|
253
|
+
self._raise_operational_error(e, error_code)
|
|
254
|
+
else:
|
|
255
|
+
self._raise_generic_error(e, error_code)
|
|
256
|
+
|
|
257
|
+
def _raise_unique_violation(self, e: Any, code: int) -> None:
|
|
258
|
+
msg = f"Oracle unique constraint violation [ORA-{code:05d}]: {e}"
|
|
259
|
+
raise UniqueViolationError(msg) from e
|
|
260
|
+
|
|
261
|
+
def _raise_foreign_key_violation(self, e: Any, code: int) -> None:
|
|
262
|
+
msg = f"Oracle foreign key constraint violation [ORA-{code:05d}]: {e}"
|
|
263
|
+
raise ForeignKeyViolationError(msg) from e
|
|
264
|
+
|
|
265
|
+
def _raise_check_violation(self, e: Any, code: int) -> None:
|
|
266
|
+
msg = f"Oracle check constraint violation [ORA-{code:05d}]: {e}"
|
|
267
|
+
raise CheckViolationError(msg) from e
|
|
268
|
+
|
|
269
|
+
def _raise_not_null_violation(self, e: Any, code: int) -> None:
|
|
270
|
+
msg = f"Oracle not-null constraint violation [ORA-{code:05d}]: {e}"
|
|
271
|
+
raise NotNullViolationError(msg) from e
|
|
272
|
+
|
|
273
|
+
def _raise_integrity_error(self, e: Any, code: int) -> None:
|
|
274
|
+
msg = f"Oracle integrity constraint violation [ORA-{code:05d}]: {e}"
|
|
275
|
+
raise IntegrityError(msg) from e
|
|
276
|
+
|
|
277
|
+
def _raise_parsing_error(self, e: Any, code: int) -> None:
|
|
278
|
+
msg = f"Oracle SQL syntax error [ORA-{code:05d}]: {e}"
|
|
279
|
+
raise SQLParsingError(msg) from e
|
|
280
|
+
|
|
281
|
+
def _raise_connection_error(self, e: Any, code: int) -> None:
|
|
282
|
+
msg = f"Oracle connection error [ORA-{code:05d}]: {e}"
|
|
283
|
+
raise DatabaseConnectionError(msg) from e
|
|
284
|
+
|
|
285
|
+
def _raise_transaction_error(self, e: Any, code: int) -> None:
|
|
286
|
+
msg = f"Oracle transaction error [ORA-{code:05d}]: {e}"
|
|
287
|
+
raise TransactionError(msg) from e
|
|
288
|
+
|
|
289
|
+
def _raise_data_error(self, e: Any, code: int) -> None:
|
|
290
|
+
msg = f"Oracle data error [ORA-{code:05d}]: {e}"
|
|
291
|
+
raise DataError(msg) from e
|
|
292
|
+
|
|
293
|
+
def _raise_operational_error(self, e: Any, code: int) -> None:
|
|
294
|
+
msg = f"Oracle operational error [ORA-{code:05d}]: {e}"
|
|
295
|
+
raise OperationalError(msg) from e
|
|
296
|
+
|
|
297
|
+
def _raise_generic_error(self, e: Any, code: "int | None") -> None:
|
|
298
|
+
msg = f"Oracle database error [ORA-{code:05d}]: {e}" if code else f"Oracle database error: {e}"
|
|
299
|
+
raise SQLSpecError(msg) from e
|
|
136
300
|
|
|
137
301
|
|
|
138
302
|
class OracleAsyncExceptionHandler:
|
|
139
|
-
"""
|
|
303
|
+
"""Async context manager for handling Oracle database exceptions.
|
|
304
|
+
|
|
305
|
+
Maps Oracle ORA-XXXXX error codes to specific SQLSpec exceptions
|
|
306
|
+
for better error handling in application code.
|
|
307
|
+
"""
|
|
140
308
|
|
|
141
309
|
__slots__ = ()
|
|
142
310
|
|
|
@@ -144,42 +312,93 @@ class OracleAsyncExceptionHandler:
|
|
|
144
312
|
return None
|
|
145
313
|
|
|
146
314
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
147
|
-
_ = exc_tb
|
|
315
|
+
_ = exc_tb
|
|
148
316
|
if exc_type is None:
|
|
149
317
|
return
|
|
150
|
-
|
|
151
|
-
if issubclass(exc_type, oracledb.IntegrityError):
|
|
152
|
-
e = exc_val
|
|
153
|
-
msg = f"Oracle integrity constraint violation: {e}"
|
|
154
|
-
raise SQLSpecError(msg) from e
|
|
155
|
-
if issubclass(exc_type, oracledb.ProgrammingError):
|
|
156
|
-
e = exc_val
|
|
157
|
-
error_msg = str(e).lower()
|
|
158
|
-
if "syntax" in error_msg or "parse" in error_msg:
|
|
159
|
-
msg = f"Oracle SQL syntax error: {e}"
|
|
160
|
-
raise SQLParsingError(msg) from e
|
|
161
|
-
msg = f"Oracle programming error: {e}"
|
|
162
|
-
raise SQLSpecError(msg) from e
|
|
163
|
-
if issubclass(exc_type, oracledb.OperationalError):
|
|
164
|
-
e = exc_val
|
|
165
|
-
msg = f"Oracle operational error: {e}"
|
|
166
|
-
raise SQLSpecError(msg) from e
|
|
167
318
|
if issubclass(exc_type, oracledb.DatabaseError):
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
319
|
+
self._map_oracle_exception(exc_val)
|
|
320
|
+
|
|
321
|
+
def _map_oracle_exception(self, e: Any) -> None:
|
|
322
|
+
"""Map Oracle exception to SQLSpec exception.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
e: oracledb.DatabaseError instance
|
|
326
|
+
"""
|
|
327
|
+
error_obj = e.args[0] if e.args else None
|
|
328
|
+
if not error_obj:
|
|
329
|
+
self._raise_generic_error(e, None)
|
|
330
|
+
|
|
331
|
+
error_code = getattr(error_obj, "code", None)
|
|
332
|
+
|
|
333
|
+
if not error_code:
|
|
334
|
+
self._raise_generic_error(e, None)
|
|
335
|
+
|
|
336
|
+
if error_code == 1:
|
|
337
|
+
self._raise_unique_violation(e, error_code)
|
|
338
|
+
elif error_code in {2291, 2292}:
|
|
339
|
+
self._raise_foreign_key_violation(e, error_code)
|
|
340
|
+
elif error_code == ORA_CHECK_CONSTRAINT:
|
|
341
|
+
self._raise_check_violation(e, error_code)
|
|
342
|
+
elif error_code in {1400, 1407}:
|
|
343
|
+
self._raise_not_null_violation(e, error_code)
|
|
344
|
+
elif error_code and ORA_INTEGRITY_RANGE_START <= error_code < ORA_INTEGRITY_RANGE_END:
|
|
345
|
+
self._raise_integrity_error(e, error_code)
|
|
346
|
+
elif error_code in {1017, 12154, 12541, 12545, 12514, 12505}:
|
|
347
|
+
self._raise_connection_error(e, error_code)
|
|
348
|
+
elif error_code in {60, 8176}:
|
|
349
|
+
self._raise_transaction_error(e, error_code)
|
|
350
|
+
elif error_code in {1722, 1858, 1840}:
|
|
351
|
+
self._raise_data_error(e, error_code)
|
|
352
|
+
elif error_code and ORA_PARSING_RANGE_START <= error_code < ORA_PARSING_RANGE_END:
|
|
353
|
+
self._raise_parsing_error(e, error_code)
|
|
354
|
+
elif error_code == ORA_TABLESPACE_FULL:
|
|
355
|
+
self._raise_operational_error(e, error_code)
|
|
356
|
+
else:
|
|
357
|
+
self._raise_generic_error(e, error_code)
|
|
358
|
+
|
|
359
|
+
def _raise_unique_violation(self, e: Any, code: int) -> None:
|
|
360
|
+
msg = f"Oracle unique constraint violation [ORA-{code:05d}]: {e}"
|
|
361
|
+
raise UniqueViolationError(msg) from e
|
|
362
|
+
|
|
363
|
+
def _raise_foreign_key_violation(self, e: Any, code: int) -> None:
|
|
364
|
+
msg = f"Oracle foreign key constraint violation [ORA-{code:05d}]: {e}"
|
|
365
|
+
raise ForeignKeyViolationError(msg) from e
|
|
366
|
+
|
|
367
|
+
def _raise_check_violation(self, e: Any, code: int) -> None:
|
|
368
|
+
msg = f"Oracle check constraint violation [ORA-{code:05d}]: {e}"
|
|
369
|
+
raise CheckViolationError(msg) from e
|
|
370
|
+
|
|
371
|
+
def _raise_not_null_violation(self, e: Any, code: int) -> None:
|
|
372
|
+
msg = f"Oracle not-null constraint violation [ORA-{code:05d}]: {e}"
|
|
373
|
+
raise NotNullViolationError(msg) from e
|
|
374
|
+
|
|
375
|
+
def _raise_integrity_error(self, e: Any, code: int) -> None:
|
|
376
|
+
msg = f"Oracle integrity constraint violation [ORA-{code:05d}]: {e}"
|
|
377
|
+
raise IntegrityError(msg) from e
|
|
378
|
+
|
|
379
|
+
def _raise_parsing_error(self, e: Any, code: int) -> None:
|
|
380
|
+
msg = f"Oracle SQL syntax error [ORA-{code:05d}]: {e}"
|
|
381
|
+
raise SQLParsingError(msg) from e
|
|
382
|
+
|
|
383
|
+
def _raise_connection_error(self, e: Any, code: int) -> None:
|
|
384
|
+
msg = f"Oracle connection error [ORA-{code:05d}]: {e}"
|
|
385
|
+
raise DatabaseConnectionError(msg) from e
|
|
386
|
+
|
|
387
|
+
def _raise_transaction_error(self, e: Any, code: int) -> None:
|
|
388
|
+
msg = f"Oracle transaction error [ORA-{code:05d}]: {e}"
|
|
389
|
+
raise TransactionError(msg) from e
|
|
390
|
+
|
|
391
|
+
def _raise_data_error(self, e: Any, code: int) -> None:
|
|
392
|
+
msg = f"Oracle data error [ORA-{code:05d}]: {e}"
|
|
393
|
+
raise DataError(msg) from e
|
|
394
|
+
|
|
395
|
+
def _raise_operational_error(self, e: Any, code: int) -> None:
|
|
396
|
+
msg = f"Oracle operational error [ORA-{code:05d}]: {e}"
|
|
397
|
+
raise OperationalError(msg) from e
|
|
398
|
+
|
|
399
|
+
def _raise_generic_error(self, e: Any, code: "int | None") -> None:
|
|
400
|
+
msg = f"Oracle database error [ORA-{code:05d}]: {e}" if code else f"Oracle database error: {e}"
|
|
401
|
+
raise SQLSpecError(msg) from e
|
|
183
402
|
|
|
184
403
|
|
|
185
404
|
class OracleSyncDriver(SyncDriverAdapterBase):
|
|
@@ -189,14 +408,14 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
189
408
|
error handling, and transaction management.
|
|
190
409
|
"""
|
|
191
410
|
|
|
192
|
-
__slots__ = ()
|
|
411
|
+
__slots__ = ("_data_dictionary",)
|
|
193
412
|
dialect = "oracle"
|
|
194
413
|
|
|
195
414
|
def __init__(
|
|
196
415
|
self,
|
|
197
416
|
connection: OracleSyncConnection,
|
|
198
|
-
statement_config: "
|
|
199
|
-
driver_features: "
|
|
417
|
+
statement_config: "StatementConfig | None" = None,
|
|
418
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
200
419
|
) -> None:
|
|
201
420
|
if statement_config is None:
|
|
202
421
|
cache_config = get_cache_config()
|
|
@@ -208,6 +427,7 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
208
427
|
)
|
|
209
428
|
|
|
210
429
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
430
|
+
self._data_dictionary: SyncDataDictionaryBase | None = None
|
|
211
431
|
|
|
212
432
|
def with_cursor(self, connection: OracleSyncConnection) -> OracleSyncCursor:
|
|
213
433
|
"""Create context manager for Oracle cursor.
|
|
@@ -224,7 +444,7 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
224
444
|
"""Handle database-specific exceptions and wrap them appropriately."""
|
|
225
445
|
return OracleSyncExceptionHandler()
|
|
226
446
|
|
|
227
|
-
def _try_special_handling(self, cursor: Any, statement: "SQL") -> "
|
|
447
|
+
def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
228
448
|
"""Hook for Oracle-specific special operations.
|
|
229
449
|
|
|
230
450
|
Oracle doesn't have complex special operations like PostgreSQL COPY,
|
|
@@ -309,15 +529,23 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
309
529
|
Execution result containing data for SELECT statements or row count for others
|
|
310
530
|
"""
|
|
311
531
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
532
|
+
|
|
533
|
+
# Oracle-specific: Use setinputsizes for large string parameters to avoid ORA-01704
|
|
534
|
+
if prepared_parameters and isinstance(prepared_parameters, dict):
|
|
535
|
+
for param_name, param_value in prepared_parameters.items():
|
|
536
|
+
if isinstance(param_value, str) and len(param_value) > LARGE_STRING_THRESHOLD:
|
|
537
|
+
cursor.setinputsizes(**{param_name: len(param_value)})
|
|
538
|
+
|
|
312
539
|
cursor.execute(sql, prepared_parameters or {})
|
|
313
540
|
|
|
314
541
|
# SELECT result processing for Oracle
|
|
315
542
|
if statement.returns_rows():
|
|
316
543
|
fetched_data = cursor.fetchall()
|
|
317
544
|
column_names = [col[0] for col in cursor.description or []]
|
|
545
|
+
column_names = _normalize_column_names(column_names, self.driver_features)
|
|
318
546
|
|
|
319
|
-
# Oracle returns tuples - convert to consistent dict format
|
|
320
|
-
data = [dict(zip(column_names, row)) for row in fetched_data]
|
|
547
|
+
# Oracle returns tuples - convert to consistent dict format after LOB hydration
|
|
548
|
+
data = [dict(zip(column_names, _coerce_sync_row_values(row), strict=False)) for row in fetched_data]
|
|
321
549
|
|
|
322
550
|
return self.create_execution_result(
|
|
323
551
|
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
@@ -359,6 +587,17 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
359
587
|
msg = f"Failed to commit Oracle transaction: {e}"
|
|
360
588
|
raise SQLSpecError(msg) from e
|
|
361
589
|
|
|
590
|
+
@property
|
|
591
|
+
def data_dictionary(self) -> "SyncDataDictionaryBase":
|
|
592
|
+
"""Get the data dictionary for this driver.
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
Data dictionary instance for metadata queries
|
|
596
|
+
"""
|
|
597
|
+
if self._data_dictionary is None:
|
|
598
|
+
self._data_dictionary = OracleSyncDataDictionary()
|
|
599
|
+
return self._data_dictionary
|
|
600
|
+
|
|
362
601
|
|
|
363
602
|
class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
364
603
|
"""Asynchronous Oracle Database driver.
|
|
@@ -367,14 +606,14 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
367
606
|
error handling, and transaction management for async operations.
|
|
368
607
|
"""
|
|
369
608
|
|
|
370
|
-
__slots__ = ()
|
|
609
|
+
__slots__ = ("_data_dictionary",)
|
|
371
610
|
dialect = "oracle"
|
|
372
611
|
|
|
373
612
|
def __init__(
|
|
374
613
|
self,
|
|
375
614
|
connection: OracleAsyncConnection,
|
|
376
|
-
statement_config: "
|
|
377
|
-
driver_features: "
|
|
615
|
+
statement_config: "StatementConfig | None" = None,
|
|
616
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
378
617
|
) -> None:
|
|
379
618
|
if statement_config is None:
|
|
380
619
|
cache_config = get_cache_config()
|
|
@@ -386,6 +625,7 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
386
625
|
)
|
|
387
626
|
|
|
388
627
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
628
|
+
self._data_dictionary: AsyncDataDictionaryBase | None = None
|
|
389
629
|
|
|
390
630
|
def with_cursor(self, connection: OracleAsyncConnection) -> OracleAsyncCursor:
|
|
391
631
|
"""Create context manager for Oracle cursor.
|
|
@@ -402,7 +642,7 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
402
642
|
"""Handle database-specific exceptions and wrap them appropriately."""
|
|
403
643
|
return OracleAsyncExceptionHandler()
|
|
404
644
|
|
|
405
|
-
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "
|
|
645
|
+
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
406
646
|
"""Hook for Oracle-specific special operations.
|
|
407
647
|
|
|
408
648
|
Oracle doesn't have complex special operations like PostgreSQL COPY,
|
|
@@ -482,15 +722,26 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
482
722
|
Execution result containing data for SELECT statements or row count for others
|
|
483
723
|
"""
|
|
484
724
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
725
|
+
|
|
726
|
+
# Oracle-specific: Use setinputsizes for large string parameters to avoid ORA-01704
|
|
727
|
+
if prepared_parameters and isinstance(prepared_parameters, dict):
|
|
728
|
+
for param_name, param_value in prepared_parameters.items():
|
|
729
|
+
if isinstance(param_value, str) and len(param_value) > LARGE_STRING_THRESHOLD:
|
|
730
|
+
cursor.setinputsizes(**{param_name: len(param_value)})
|
|
731
|
+
|
|
485
732
|
await cursor.execute(sql, prepared_parameters or {})
|
|
486
733
|
|
|
487
734
|
# SELECT result processing for Oracle
|
|
488
735
|
if statement.returns_rows():
|
|
489
736
|
fetched_data = await cursor.fetchall()
|
|
490
737
|
column_names = [col[0] for col in cursor.description or []]
|
|
738
|
+
column_names = _normalize_column_names(column_names, self.driver_features)
|
|
491
739
|
|
|
492
|
-
# Oracle returns tuples - convert to consistent dict format
|
|
493
|
-
data = [
|
|
740
|
+
# Oracle returns tuples - convert to consistent dict format after LOB hydration
|
|
741
|
+
data = []
|
|
742
|
+
for row in fetched_data:
|
|
743
|
+
coerced_row = await _coerce_async_row_values(row)
|
|
744
|
+
data.append(dict(zip(column_names, coerced_row, strict=False)))
|
|
494
745
|
|
|
495
746
|
return self.create_execution_result(
|
|
496
747
|
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
@@ -531,3 +782,14 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
531
782
|
except oracledb.Error as e:
|
|
532
783
|
msg = f"Failed to commit Oracle transaction: {e}"
|
|
533
784
|
raise SQLSpecError(msg) from e
|
|
785
|
+
|
|
786
|
+
@property
|
|
787
|
+
def data_dictionary(self) -> "AsyncDataDictionaryBase":
|
|
788
|
+
"""Get the data dictionary for this driver.
|
|
789
|
+
|
|
790
|
+
Returns:
|
|
791
|
+
Data dictionary instance for metadata queries
|
|
792
|
+
"""
|
|
793
|
+
if self._data_dictionary is None:
|
|
794
|
+
self._data_dictionary = OracleAsyncDataDictionary()
|
|
795
|
+
return self._data_dictionary
|