sqlspec 0.68.0__py3-none-any.whl → 0.69.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/adapters/oracledb/__init__.py +6 -0
- sqlspec/adapters/oracledb/adk/store.py +92 -12
- sqlspec/adapters/oracledb/config.py +55 -11
- sqlspec/adapters/oracledb/core.py +943 -194
- sqlspec/adapters/oracledb/driver.py +89 -3
- sqlspec/builder/_vector_distance.py +21 -6
- {sqlspec-0.68.0.dist-info → sqlspec-0.69.0.dist-info}/METADATA +1 -1
- {sqlspec-0.68.0.dist-info → sqlspec-0.69.0.dist-info}/RECORD +11 -11
- {sqlspec-0.68.0.dist-info → sqlspec-0.69.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.68.0.dist-info → sqlspec-0.69.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.68.0.dist-info → sqlspec-0.69.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -17,6 +17,7 @@ from sqlspec.adapters.oracledb.core import (
|
|
|
17
17
|
OracleTopologyConfig,
|
|
18
18
|
build_arrow_fetch_kwargs,
|
|
19
19
|
build_connection_config,
|
|
20
|
+
build_dbms_cloud_copy_data_sql,
|
|
20
21
|
build_duality_etag_update_sql,
|
|
21
22
|
build_session_checkout_plsql,
|
|
22
23
|
build_session_context_plsql,
|
|
@@ -36,6 +37,8 @@ from sqlspec.adapters.oracledb.core import (
|
|
|
36
37
|
inject_duality_view_etag,
|
|
37
38
|
oracle_df_batches_to_arrow_reader,
|
|
38
39
|
oracle_df_to_arrow_table,
|
|
40
|
+
parse_sqlnet_ora,
|
|
41
|
+
parse_tnsnames_ora,
|
|
39
42
|
sanitize_oracle_text_query,
|
|
40
43
|
)
|
|
41
44
|
from sqlspec.adapters.oracledb.data_dictionary import (
|
|
@@ -74,6 +77,7 @@ __all__ = (
|
|
|
74
77
|
"OracledbSyncDataDictionary",
|
|
75
78
|
"build_arrow_fetch_kwargs",
|
|
76
79
|
"build_connection_config",
|
|
80
|
+
"build_dbms_cloud_copy_data_sql",
|
|
77
81
|
"build_duality_etag_update_sql",
|
|
78
82
|
"build_session_checkout_plsql",
|
|
79
83
|
"build_session_context_plsql",
|
|
@@ -93,5 +97,7 @@ __all__ = (
|
|
|
93
97
|
"inject_duality_view_etag",
|
|
94
98
|
"oracle_df_batches_to_arrow_reader",
|
|
95
99
|
"oracle_df_to_arrow_table",
|
|
100
|
+
"parse_sqlnet_ora",
|
|
101
|
+
"parse_tnsnames_ora",
|
|
96
102
|
"sanitize_oracle_text_query",
|
|
97
103
|
)
|
|
@@ -2382,13 +2382,33 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
2382
2382
|
class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
2383
2383
|
"""Oracle ADK memory store using async oracledb driver."""
|
|
2384
2384
|
|
|
2385
|
-
__slots__ = ("_in_memory"
|
|
2385
|
+
__slots__ = ("_approximate", "_fts_operator", "_in_memory")
|
|
2386
2386
|
|
|
2387
2387
|
def __init__(self, config: "OracleAsyncConfig") -> None:
|
|
2388
2388
|
super().__init__(config)
|
|
2389
2389
|
validate_oracle_identifier(self._memory_table, "table name")
|
|
2390
2390
|
adk_config = _adk_config(config)
|
|
2391
2391
|
self._in_memory: bool = bool(adk_config.get("in_memory", False))
|
|
2392
|
+
self._fts_operator: str = str(adk_config.get("fts_operator", "ACCUM"))
|
|
2393
|
+
self._approximate: bool = bool(adk_config.get("approximate", True))
|
|
2394
|
+
|
|
2395
|
+
@property
|
|
2396
|
+
def fts_operator(self) -> str:
|
|
2397
|
+
"""Return the default Oracle Text operator for hybrid search."""
|
|
2398
|
+
return self._fts_operator
|
|
2399
|
+
|
|
2400
|
+
@fts_operator.setter
|
|
2401
|
+
def fts_operator(self, value: str) -> None:
|
|
2402
|
+
self._fts_operator = value
|
|
2403
|
+
|
|
2404
|
+
@property
|
|
2405
|
+
def approximate(self) -> bool:
|
|
2406
|
+
"""Return whether approximate vector search is enabled."""
|
|
2407
|
+
return self._approximate
|
|
2408
|
+
|
|
2409
|
+
@approximate.setter
|
|
2410
|
+
def approximate(self, value: bool) -> None:
|
|
2411
|
+
self._approximate = bool(value)
|
|
2392
2412
|
|
|
2393
2413
|
async def create_tables(self) -> None:
|
|
2394
2414
|
if not self.create_schema_enabled:
|
|
@@ -2470,10 +2490,19 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2470
2490
|
try:
|
|
2471
2491
|
if embedding is not None and self._use_fts:
|
|
2472
2492
|
return await self._search_entries_hybrid_rrf(
|
|
2473
|
-
query,
|
|
2493
|
+
query,
|
|
2494
|
+
embedding,
|
|
2495
|
+
app_name,
|
|
2496
|
+
user_id,
|
|
2497
|
+
effective_limit,
|
|
2498
|
+
scope_filter,
|
|
2499
|
+
fts_operator=self._fts_operator,
|
|
2500
|
+
approximate=self._approximate,
|
|
2474
2501
|
)
|
|
2475
2502
|
if self._use_fts:
|
|
2476
|
-
return await self._search_entries_fts(
|
|
2503
|
+
return await self._search_entries_fts(
|
|
2504
|
+
query, app_name, user_id, effective_limit, scope_filter, fts_operator=self._fts_operator
|
|
2505
|
+
)
|
|
2477
2506
|
return await self._search_entries_simple(query, app_name, user_id, effective_limit, scope_filter)
|
|
2478
2507
|
except OracleDatabaseError as exc:
|
|
2479
2508
|
error_obj = exc.args[0] if exc.args else None
|
|
@@ -2666,9 +2695,16 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2666
2695
|
return True
|
|
2667
2696
|
|
|
2668
2697
|
async def _search_entries_fts(
|
|
2669
|
-
self,
|
|
2698
|
+
self,
|
|
2699
|
+
query: str,
|
|
2700
|
+
app_name: str,
|
|
2701
|
+
user_id: str,
|
|
2702
|
+
limit: int,
|
|
2703
|
+
scope_filter: Literal["all", "user", "app"] = "all",
|
|
2704
|
+
*,
|
|
2705
|
+
fts_operator: str = "ACCUM",
|
|
2670
2706
|
) -> "list[StoredMemory]":
|
|
2671
|
-
sanitized_query = sanitize_oracle_text_query(query)
|
|
2707
|
+
sanitized_query = sanitize_oracle_text_query(query, operator=fts_operator)
|
|
2672
2708
|
if not sanitized_query:
|
|
2673
2709
|
return []
|
|
2674
2710
|
where_scope, scope_params = _build_oracle_scope_where(app_name, user_id, scope_filter)
|
|
@@ -2705,8 +2741,10 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2705
2741
|
vector_column: str = "embedding",
|
|
2706
2742
|
rrf_k: int = 60,
|
|
2707
2743
|
metric: str = "COSINE",
|
|
2744
|
+
fts_operator: str = "ACCUM",
|
|
2745
|
+
approximate: bool = True,
|
|
2708
2746
|
) -> "list[StoredMemory]":
|
|
2709
|
-
sanitized_query = sanitize_oracle_text_query(query)
|
|
2747
|
+
sanitized_query = sanitize_oracle_text_query(query, operator=fts_operator)
|
|
2710
2748
|
where_scope, scope_params = _build_oracle_scope_where(app_name, user_id, scope_filter)
|
|
2711
2749
|
select_cols = (
|
|
2712
2750
|
"id",
|
|
@@ -2730,6 +2768,7 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2730
2768
|
vector_param="query_vector",
|
|
2731
2769
|
top_k_param="top_k",
|
|
2732
2770
|
metric=metric,
|
|
2771
|
+
approximate=approximate,
|
|
2733
2772
|
where_clause=where_scope,
|
|
2734
2773
|
)
|
|
2735
2774
|
params: dict[str, Any] = {**scope_params, "query_vector": embedding, "top_k": limit}
|
|
@@ -2745,6 +2784,7 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2745
2784
|
top_k_param="top_k",
|
|
2746
2785
|
rrf_k_param="rrf_k",
|
|
2747
2786
|
metric=metric,
|
|
2787
|
+
approximate=approximate,
|
|
2748
2788
|
where_clause=where_scope,
|
|
2749
2789
|
)
|
|
2750
2790
|
params = {
|
|
@@ -2814,13 +2854,33 @@ class OracleAsyncADKMemoryStore(BaseAsyncADKMemoryStore["OracleAsyncConfig"]):
|
|
|
2814
2854
|
class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
2815
2855
|
"""Oracle ADK memory store using sync oracledb driver."""
|
|
2816
2856
|
|
|
2817
|
-
__slots__ = ("_in_memory"
|
|
2857
|
+
__slots__ = ("_approximate", "_fts_operator", "_in_memory")
|
|
2818
2858
|
|
|
2819
2859
|
def __init__(self, config: "OracleSyncConfig") -> None:
|
|
2820
2860
|
super().__init__(config)
|
|
2821
2861
|
validate_oracle_identifier(self._memory_table, "table name")
|
|
2822
2862
|
adk_config = _adk_config(config)
|
|
2823
2863
|
self._in_memory = bool(adk_config.get("in_memory", False))
|
|
2864
|
+
self._fts_operator: str = str(adk_config.get("fts_operator", "ACCUM"))
|
|
2865
|
+
self._approximate: bool = bool(adk_config.get("approximate", True))
|
|
2866
|
+
|
|
2867
|
+
@property
|
|
2868
|
+
def fts_operator(self) -> str:
|
|
2869
|
+
"""Return the default Oracle Text operator for hybrid search."""
|
|
2870
|
+
return self._fts_operator
|
|
2871
|
+
|
|
2872
|
+
@fts_operator.setter
|
|
2873
|
+
def fts_operator(self, value: str) -> None:
|
|
2874
|
+
self._fts_operator = value
|
|
2875
|
+
|
|
2876
|
+
@property
|
|
2877
|
+
def approximate(self) -> bool:
|
|
2878
|
+
"""Return whether approximate vector search is enabled."""
|
|
2879
|
+
return self._approximate
|
|
2880
|
+
|
|
2881
|
+
@approximate.setter
|
|
2882
|
+
def approximate(self, value: bool) -> None:
|
|
2883
|
+
self._approximate = bool(value)
|
|
2824
2884
|
|
|
2825
2885
|
def create_tables(self) -> None:
|
|
2826
2886
|
"""Create the memory table when the data dictionary reports it missing."""
|
|
@@ -2904,10 +2964,19 @@ class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
|
2904
2964
|
try:
|
|
2905
2965
|
if embedding is not None and self._use_fts:
|
|
2906
2966
|
return self._search_entries_hybrid_rrf(
|
|
2907
|
-
query,
|
|
2967
|
+
query,
|
|
2968
|
+
embedding,
|
|
2969
|
+
app_name,
|
|
2970
|
+
user_id,
|
|
2971
|
+
effective_limit,
|
|
2972
|
+
scope_filter,
|
|
2973
|
+
fts_operator=self._fts_operator,
|
|
2974
|
+
approximate=self._approximate,
|
|
2908
2975
|
)
|
|
2909
2976
|
if self._use_fts:
|
|
2910
|
-
return self._search_entries_fts(
|
|
2977
|
+
return self._search_entries_fts(
|
|
2978
|
+
query, app_name, user_id, effective_limit, scope_filter, fts_operator=self._fts_operator
|
|
2979
|
+
)
|
|
2911
2980
|
return self._search_entries_simple(query, app_name, user_id, effective_limit, scope_filter)
|
|
2912
2981
|
except OracleDatabaseError as exc:
|
|
2913
2982
|
error_obj = exc.args[0] if exc.args else None
|
|
@@ -3098,9 +3167,16 @@ class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
|
3098
3167
|
return True
|
|
3099
3168
|
|
|
3100
3169
|
def _search_entries_fts(
|
|
3101
|
-
self,
|
|
3170
|
+
self,
|
|
3171
|
+
query: str,
|
|
3172
|
+
app_name: str,
|
|
3173
|
+
user_id: str,
|
|
3174
|
+
limit: int,
|
|
3175
|
+
scope_filter: Literal["all", "user", "app"] = "all",
|
|
3176
|
+
*,
|
|
3177
|
+
fts_operator: str = "ACCUM",
|
|
3102
3178
|
) -> "list[StoredMemory]":
|
|
3103
|
-
sanitized_query = sanitize_oracle_text_query(query)
|
|
3179
|
+
sanitized_query = sanitize_oracle_text_query(query, operator=fts_operator)
|
|
3104
3180
|
if not sanitized_query:
|
|
3105
3181
|
return []
|
|
3106
3182
|
where_scope, scope_params = _build_oracle_scope_where(app_name, user_id, scope_filter)
|
|
@@ -3136,8 +3212,10 @@ class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
|
3136
3212
|
vector_column: str = "embedding",
|
|
3137
3213
|
rrf_k: int = 60,
|
|
3138
3214
|
metric: str = "COSINE",
|
|
3215
|
+
fts_operator: str = "ACCUM",
|
|
3216
|
+
approximate: bool = True,
|
|
3139
3217
|
) -> "list[StoredMemory]":
|
|
3140
|
-
sanitized_query = sanitize_oracle_text_query(query)
|
|
3218
|
+
sanitized_query = sanitize_oracle_text_query(query, operator=fts_operator)
|
|
3141
3219
|
where_scope, scope_params = _build_oracle_scope_where(app_name, user_id, scope_filter)
|
|
3142
3220
|
select_cols = (
|
|
3143
3221
|
"id",
|
|
@@ -3161,6 +3239,7 @@ class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
|
3161
3239
|
vector_param="query_vector",
|
|
3162
3240
|
top_k_param="top_k",
|
|
3163
3241
|
metric=metric,
|
|
3242
|
+
approximate=approximate,
|
|
3164
3243
|
where_clause=where_scope,
|
|
3165
3244
|
)
|
|
3166
3245
|
params: dict[str, Any] = {**scope_params, "query_vector": embedding, "top_k": limit}
|
|
@@ -3176,6 +3255,7 @@ class OracleSyncADKMemoryStore(BaseSyncADKMemoryStore["OracleSyncConfig"]):
|
|
|
3176
3255
|
top_k_param="top_k",
|
|
3177
3256
|
rrf_k_param="rrf_k",
|
|
3178
3257
|
metric=metric,
|
|
3258
|
+
approximate=approximate,
|
|
3179
3259
|
where_clause=where_scope,
|
|
3180
3260
|
)
|
|
3181
3261
|
params = {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""OracleDB database configuration with direct field-based configuration."""
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
-
import shutil
|
|
5
4
|
from collections.abc import Awaitable, Callable, Mapping
|
|
6
5
|
from inspect import isawaitable
|
|
7
6
|
from pathlib import Path
|
|
@@ -38,6 +37,8 @@ from sqlspec.adapters.oracledb.core import (
|
|
|
38
37
|
execute_session_release_async,
|
|
39
38
|
execute_session_release_sync,
|
|
40
39
|
extract_oracle_wallet_zip,
|
|
40
|
+
release_extracted_wallet_dir,
|
|
41
|
+
retain_extracted_wallet_dir,
|
|
41
42
|
)
|
|
42
43
|
from sqlspec.adapters.oracledb.data_dictionary import (
|
|
43
44
|
OracleVersionCache,
|
|
@@ -154,6 +155,7 @@ class OracleConnectionParams(TypedDict):
|
|
|
154
155
|
extra: NotRequired[dict[str, Any]]
|
|
155
156
|
topology: NotRequired[OracleTopologyConfig | dict[str, Any]]
|
|
156
157
|
wallet_zip: NotRequired[str | Path | bytes]
|
|
158
|
+
tns_alias: NotRequired[str]
|
|
157
159
|
|
|
158
160
|
|
|
159
161
|
class OraclePoolParams(OracleConnectionParams):
|
|
@@ -240,6 +242,9 @@ class OracleDriverFeatures(TypedDict):
|
|
|
240
242
|
executemany.
|
|
241
243
|
session_context_hook: Optional declarative session context configuration
|
|
242
244
|
applied on checkout and cleared on release.
|
|
245
|
+
dbms_cloud_credential: Optional default ``DBMS_CLOUD`` credential name used
|
|
246
|
+
by ``load_from_storage`` for server-side object-store ingestion via
|
|
247
|
+
``DBMS_CLOUD.COPY_DATA``.
|
|
243
248
|
Native pipeline execution is runtime-gated by async Thin-mode driver API support,
|
|
244
249
|
Oracle Database 26ai or newer, and the SQLSPEC_ORACLE_DISABLE_PIPELINE environment
|
|
245
250
|
override; there is no adapter config switch that can force-enable unsupported
|
|
@@ -262,6 +267,7 @@ class OracleDriverFeatures(TypedDict):
|
|
|
262
267
|
enable_direct_path_load: NotRequired[bool]
|
|
263
268
|
session_context_hook: NotRequired[OracleSessionContextConfig | dict[str, Any]]
|
|
264
269
|
enable_session_tracing: NotRequired[bool]
|
|
270
|
+
dbms_cloud_credential: NotRequired[str | None]
|
|
265
271
|
|
|
266
272
|
|
|
267
273
|
class OracleSyncConnectionContext(SyncPoolConnectionContext):
|
|
@@ -390,9 +396,15 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
390
396
|
"""
|
|
391
397
|
normalized_conn = dict(normalize_connection_config(connection_config))
|
|
392
398
|
self._extracted_wallet_dir: Path | None = None
|
|
399
|
+
pending_wallet_dir: Path | None = None
|
|
393
400
|
if normalized_conn.get("wallet_zip") is not None:
|
|
394
|
-
|
|
395
|
-
|
|
401
|
+
raw_wallet_source = normalized_conn.pop("wallet_zip")
|
|
402
|
+
source_is_existing_dir = isinstance(raw_wallet_source, (str, Path)) and Path(raw_wallet_source).is_dir()
|
|
403
|
+
wallet_dir = extract_oracle_wallet_zip(
|
|
404
|
+
raw_wallet_source, wallet_password=normalized_conn.get("wallet_password")
|
|
405
|
+
)
|
|
406
|
+
if not source_is_existing_dir or wallet_dir.resolve() != Path(raw_wallet_source).resolve():
|
|
407
|
+
pending_wallet_dir = wallet_dir
|
|
396
408
|
normalized_conn.setdefault("config_dir", str(wallet_dir))
|
|
397
409
|
normalized_conn.setdefault("wallet_location", str(wallet_dir))
|
|
398
410
|
connection_config = build_connection_config(normalized_conn)
|
|
@@ -417,6 +429,9 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
417
429
|
extension_config=extension_config,
|
|
418
430
|
**kwargs,
|
|
419
431
|
)
|
|
432
|
+
if pending_wallet_dir is not None:
|
|
433
|
+
self._extracted_wallet_dir = pending_wallet_dir
|
|
434
|
+
retain_extracted_wallet_dir(pending_wallet_dir)
|
|
420
435
|
|
|
421
436
|
def create_connection(self) -> "OracleSyncConnection":
|
|
422
437
|
"""Create a single connection (not from pool).
|
|
@@ -466,7 +481,7 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
466
481
|
new_driver_features[key] = val
|
|
467
482
|
else:
|
|
468
483
|
new_conn_cfg[key] = val
|
|
469
|
-
|
|
484
|
+
derived = type(self)(
|
|
470
485
|
connection_config=new_conn_cfg,
|
|
471
486
|
connection_instance=None,
|
|
472
487
|
migration_config=cast("dict[str, Any] | None", dict(self.migration_config))
|
|
@@ -480,6 +495,15 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
480
495
|
else extension_config,
|
|
481
496
|
observability_config=self.observability_config if observability_config is None else observability_config,
|
|
482
497
|
)
|
|
498
|
+
if (
|
|
499
|
+
self._extracted_wallet_dir is not None
|
|
500
|
+
and derived._extracted_wallet_dir is None
|
|
501
|
+
and str(self._extracted_wallet_dir)
|
|
502
|
+
in {derived.connection_config.get("config_dir"), derived.connection_config.get("wallet_location")}
|
|
503
|
+
):
|
|
504
|
+
derived._extracted_wallet_dir = self._extracted_wallet_dir
|
|
505
|
+
retain_extracted_wallet_dir(self._extracted_wallet_dir)
|
|
506
|
+
return derived
|
|
483
507
|
|
|
484
508
|
def get_signature_namespace(self) -> "dict[str, Any]":
|
|
485
509
|
"""Get the signature namespace for OracleDB types.
|
|
@@ -578,7 +602,7 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
578
602
|
return driver
|
|
579
603
|
|
|
580
604
|
def _close_pool(self) -> None:
|
|
581
|
-
"""Close the actual connection pool and
|
|
605
|
+
"""Close the actual connection pool and release any extracted temporary wallet directory."""
|
|
582
606
|
try:
|
|
583
607
|
if self.connection_instance:
|
|
584
608
|
self.connection_instance.close()
|
|
@@ -586,8 +610,9 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
586
610
|
self._oracle_version_cache.reset()
|
|
587
611
|
finally:
|
|
588
612
|
if self._extracted_wallet_dir is not None:
|
|
589
|
-
|
|
613
|
+
wallet_dir = self._extracted_wallet_dir
|
|
590
614
|
self._extracted_wallet_dir = None
|
|
615
|
+
release_extracted_wallet_dir(wallet_dir)
|
|
591
616
|
|
|
592
617
|
|
|
593
618
|
class OracleAsyncConnectionContext(AsyncPoolConnectionContext):
|
|
@@ -695,9 +720,15 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
695
720
|
"""
|
|
696
721
|
normalized_conn = dict(normalize_connection_config(connection_config))
|
|
697
722
|
self._extracted_wallet_dir: Path | None = None
|
|
723
|
+
pending_wallet_dir: Path | None = None
|
|
698
724
|
if normalized_conn.get("wallet_zip") is not None:
|
|
699
|
-
|
|
700
|
-
|
|
725
|
+
raw_wallet_source = normalized_conn.pop("wallet_zip")
|
|
726
|
+
source_is_existing_dir = isinstance(raw_wallet_source, (str, Path)) and Path(raw_wallet_source).is_dir()
|
|
727
|
+
wallet_dir = extract_oracle_wallet_zip(
|
|
728
|
+
raw_wallet_source, wallet_password=normalized_conn.get("wallet_password")
|
|
729
|
+
)
|
|
730
|
+
if not source_is_existing_dir or wallet_dir.resolve() != Path(raw_wallet_source).resolve():
|
|
731
|
+
pending_wallet_dir = wallet_dir
|
|
701
732
|
normalized_conn.setdefault("config_dir", str(wallet_dir))
|
|
702
733
|
normalized_conn.setdefault("wallet_location", str(wallet_dir))
|
|
703
734
|
connection_config = build_connection_config(normalized_conn)
|
|
@@ -723,6 +754,9 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
723
754
|
extension_config=extension_config,
|
|
724
755
|
**kwargs,
|
|
725
756
|
)
|
|
757
|
+
if pending_wallet_dir is not None:
|
|
758
|
+
self._extracted_wallet_dir = pending_wallet_dir
|
|
759
|
+
retain_extracted_wallet_dir(pending_wallet_dir)
|
|
726
760
|
|
|
727
761
|
async def create_connection(self) -> OracleAsyncConnection:
|
|
728
762
|
"""Create a single async connection (not from pool).
|
|
@@ -772,7 +806,7 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
772
806
|
new_driver_features[key] = val
|
|
773
807
|
else:
|
|
774
808
|
new_conn_cfg[key] = val
|
|
775
|
-
|
|
809
|
+
derived = type(self)(
|
|
776
810
|
connection_config=new_conn_cfg,
|
|
777
811
|
connection_instance=None,
|
|
778
812
|
migration_config=cast("dict[str, Any] | None", dict(self.migration_config))
|
|
@@ -786,6 +820,15 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
786
820
|
else extension_config,
|
|
787
821
|
observability_config=self.observability_config if observability_config is None else observability_config,
|
|
788
822
|
)
|
|
823
|
+
if (
|
|
824
|
+
self._extracted_wallet_dir is not None
|
|
825
|
+
and derived._extracted_wallet_dir is None
|
|
826
|
+
and str(self._extracted_wallet_dir)
|
|
827
|
+
in {derived.connection_config.get("config_dir"), derived.connection_config.get("wallet_location")}
|
|
828
|
+
):
|
|
829
|
+
derived._extracted_wallet_dir = self._extracted_wallet_dir
|
|
830
|
+
retain_extracted_wallet_dir(self._extracted_wallet_dir)
|
|
831
|
+
return derived
|
|
789
832
|
|
|
790
833
|
def get_signature_namespace(self) -> "dict[str, Any]":
|
|
791
834
|
"""Get the signature namespace for OracleAsyncConfig types.
|
|
@@ -881,7 +924,7 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
881
924
|
return driver
|
|
882
925
|
|
|
883
926
|
async def _close_pool(self) -> None:
|
|
884
|
-
"""Close the actual async connection pool and
|
|
927
|
+
"""Close the actual async connection pool and release any extracted temporary wallet directory."""
|
|
885
928
|
try:
|
|
886
929
|
if self.connection_instance:
|
|
887
930
|
await self.connection_instance.close()
|
|
@@ -889,5 +932,6 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
889
932
|
self._oracle_version_cache.reset()
|
|
890
933
|
finally:
|
|
891
934
|
if self._extracted_wallet_dir is not None:
|
|
892
|
-
|
|
935
|
+
wallet_dir = self._extracted_wallet_dir
|
|
893
936
|
self._extracted_wallet_dir = None
|
|
937
|
+
release_extracted_wallet_dir(wallet_dir)
|