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.
@@ -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, embedding, app_name, user_id, effective_limit, scope_filter
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(query, app_name, user_id, effective_limit, scope_filter)
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, query: str, app_name: str, user_id: str, limit: int, scope_filter: Literal["all", "user", "app"] = "all"
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, embedding, app_name, user_id, effective_limit, scope_filter
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(query, app_name, user_id, effective_limit, scope_filter)
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, query: str, app_name: str, user_id: str, limit: int, scope_filter: Literal["all", "user", "app"] = "all"
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
- wallet_dir = extract_oracle_wallet_zip(normalized_conn.pop("wallet_zip"))
395
- self._extracted_wallet_dir = wallet_dir
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
- return type(self)(
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 remove any extracted temporary wallet directory."""
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
- shutil.rmtree(self._extracted_wallet_dir, ignore_errors=True)
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
- wallet_dir = extract_oracle_wallet_zip(normalized_conn.pop("wallet_zip"))
700
- self._extracted_wallet_dir = wallet_dir
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
- return type(self)(
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 remove any extracted temporary wallet directory."""
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
- shutil.rmtree(self._extracted_wallet_dir, ignore_errors=True)
935
+ wallet_dir = self._extracted_wallet_dir
893
936
  self._extracted_wallet_dir = None
937
+ release_extracted_wallet_dir(wallet_dir)