Загрузить файлы в «venv/Lib/site-packages/sqlalchemy/dialects/oracle»
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
# dialects/oracle/__init__.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
# mypy: ignore-errors
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
from . import base # noqa
|
||||||
|
from . import cx_oracle # noqa
|
||||||
|
from . import oracledb # noqa
|
||||||
|
from .base import BFILE
|
||||||
|
from .base import BINARY_DOUBLE
|
||||||
|
from .base import BINARY_FLOAT
|
||||||
|
from .base import BLOB
|
||||||
|
from .base import CHAR
|
||||||
|
from .base import CLOB
|
||||||
|
from .base import DATE
|
||||||
|
from .base import DOUBLE_PRECISION
|
||||||
|
from .base import FLOAT
|
||||||
|
from .base import INTERVAL
|
||||||
|
from .base import LONG
|
||||||
|
from .base import NCHAR
|
||||||
|
from .base import NCLOB
|
||||||
|
from .base import NUMBER
|
||||||
|
from .base import NVARCHAR
|
||||||
|
from .base import NVARCHAR2
|
||||||
|
from .base import RAW
|
||||||
|
from .base import REAL
|
||||||
|
from .base import ROWID
|
||||||
|
from .base import TIMESTAMP
|
||||||
|
from .base import VARCHAR
|
||||||
|
from .base import VARCHAR2
|
||||||
|
from .base import VECTOR
|
||||||
|
from .base import VectorIndexConfig
|
||||||
|
from .base import VectorIndexType
|
||||||
|
from .vector import SparseVector
|
||||||
|
from .vector import VectorDistanceType
|
||||||
|
from .vector import VectorStorageFormat
|
||||||
|
from .vector import VectorStorageType
|
||||||
|
|
||||||
|
# Alias oracledb also as oracledb_async
|
||||||
|
oracledb_async = type(
|
||||||
|
"oracledb_async", (ModuleType,), {"dialect": oracledb.dialect_async}
|
||||||
|
)
|
||||||
|
|
||||||
|
base.dialect = dialect = cx_oracle.dialect
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"VARCHAR",
|
||||||
|
"NVARCHAR",
|
||||||
|
"CHAR",
|
||||||
|
"NCHAR",
|
||||||
|
"DATE",
|
||||||
|
"NUMBER",
|
||||||
|
"BLOB",
|
||||||
|
"BFILE",
|
||||||
|
"CLOB",
|
||||||
|
"NCLOB",
|
||||||
|
"TIMESTAMP",
|
||||||
|
"RAW",
|
||||||
|
"FLOAT",
|
||||||
|
"DOUBLE_PRECISION",
|
||||||
|
"BINARY_DOUBLE",
|
||||||
|
"BINARY_FLOAT",
|
||||||
|
"LONG",
|
||||||
|
"dialect",
|
||||||
|
"INTERVAL",
|
||||||
|
"VARCHAR2",
|
||||||
|
"NVARCHAR2",
|
||||||
|
"ROWID",
|
||||||
|
"REAL",
|
||||||
|
"VECTOR",
|
||||||
|
"VectorDistanceType",
|
||||||
|
"VectorIndexType",
|
||||||
|
"VectorIndexConfig",
|
||||||
|
"VectorStorageFormat",
|
||||||
|
"VectorStorageType",
|
||||||
|
"SparseVector",
|
||||||
|
)
|
||||||
3806
venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py
Normal file
3806
venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py
Normal file
File diff suppressed because it is too large
Load Diff
1556
venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py
Normal file
1556
venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py
Normal file
File diff suppressed because it is too large
Load Diff
507
venv/Lib/site-packages/sqlalchemy/dialects/oracle/dictionary.py
Normal file
507
venv/Lib/site-packages/sqlalchemy/dialects/oracle/dictionary.py
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
# dialects/oracle/dictionary.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
# mypy: ignore-errors
|
||||||
|
|
||||||
|
from .types import DATE
|
||||||
|
from .types import LONG
|
||||||
|
from .types import NUMBER
|
||||||
|
from .types import RAW
|
||||||
|
from .types import VARCHAR2
|
||||||
|
from ... import Column
|
||||||
|
from ... import MetaData
|
||||||
|
from ... import Table
|
||||||
|
from ... import table
|
||||||
|
from ...sql.sqltypes import CHAR
|
||||||
|
|
||||||
|
# constants
|
||||||
|
DB_LINK_PLACEHOLDER = "__$sa_dblink$__"
|
||||||
|
# tables
|
||||||
|
dual = table("dual")
|
||||||
|
dictionary_meta = MetaData()
|
||||||
|
|
||||||
|
# NOTE: all the dictionary_meta are aliases because oracle does not like
|
||||||
|
# using the full table@dblink for every column in query, and complains with
|
||||||
|
# ORA-00960: ambiguous column naming in select list
|
||||||
|
all_tables = Table(
|
||||||
|
"all_tables" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("tablespace_name", VARCHAR2(30)),
|
||||||
|
Column("cluster_name", VARCHAR2(128)),
|
||||||
|
Column("iot_name", VARCHAR2(128)),
|
||||||
|
Column("status", VARCHAR2(8)),
|
||||||
|
Column("pct_free", NUMBER),
|
||||||
|
Column("pct_used", NUMBER),
|
||||||
|
Column("ini_trans", NUMBER),
|
||||||
|
Column("max_trans", NUMBER),
|
||||||
|
Column("initial_extent", NUMBER),
|
||||||
|
Column("next_extent", NUMBER),
|
||||||
|
Column("min_extents", NUMBER),
|
||||||
|
Column("max_extents", NUMBER),
|
||||||
|
Column("pct_increase", NUMBER),
|
||||||
|
Column("freelists", NUMBER),
|
||||||
|
Column("freelist_groups", NUMBER),
|
||||||
|
Column("logging", VARCHAR2(3)),
|
||||||
|
Column("backed_up", VARCHAR2(1)),
|
||||||
|
Column("num_rows", NUMBER),
|
||||||
|
Column("blocks", NUMBER),
|
||||||
|
Column("empty_blocks", NUMBER),
|
||||||
|
Column("avg_space", NUMBER),
|
||||||
|
Column("chain_cnt", NUMBER),
|
||||||
|
Column("avg_row_len", NUMBER),
|
||||||
|
Column("avg_space_freelist_blocks", NUMBER),
|
||||||
|
Column("num_freelist_blocks", NUMBER),
|
||||||
|
Column("degree", VARCHAR2(10)),
|
||||||
|
Column("instances", VARCHAR2(10)),
|
||||||
|
Column("cache", VARCHAR2(5)),
|
||||||
|
Column("table_lock", VARCHAR2(8)),
|
||||||
|
Column("sample_size", NUMBER),
|
||||||
|
Column("last_analyzed", DATE),
|
||||||
|
Column("partitioned", VARCHAR2(3)),
|
||||||
|
Column("iot_type", VARCHAR2(12)),
|
||||||
|
Column("temporary", VARCHAR2(1)),
|
||||||
|
Column("secondary", VARCHAR2(1)),
|
||||||
|
Column("nested", VARCHAR2(3)),
|
||||||
|
Column("buffer_pool", VARCHAR2(7)),
|
||||||
|
Column("flash_cache", VARCHAR2(7)),
|
||||||
|
Column("cell_flash_cache", VARCHAR2(7)),
|
||||||
|
Column("row_movement", VARCHAR2(8)),
|
||||||
|
Column("global_stats", VARCHAR2(3)),
|
||||||
|
Column("user_stats", VARCHAR2(3)),
|
||||||
|
Column("duration", VARCHAR2(15)),
|
||||||
|
Column("skip_corrupt", VARCHAR2(8)),
|
||||||
|
Column("monitoring", VARCHAR2(3)),
|
||||||
|
Column("cluster_owner", VARCHAR2(128)),
|
||||||
|
Column("dependencies", VARCHAR2(8)),
|
||||||
|
Column("compression", VARCHAR2(8)),
|
||||||
|
Column("compress_for", VARCHAR2(30)),
|
||||||
|
Column("dropped", VARCHAR2(3)),
|
||||||
|
Column("read_only", VARCHAR2(3)),
|
||||||
|
Column("segment_created", VARCHAR2(3)),
|
||||||
|
Column("result_cache", VARCHAR2(7)),
|
||||||
|
Column("clustering", VARCHAR2(3)),
|
||||||
|
Column("activity_tracking", VARCHAR2(23)),
|
||||||
|
Column("dml_timestamp", VARCHAR2(25)),
|
||||||
|
Column("has_identity", VARCHAR2(3)),
|
||||||
|
Column("container_data", VARCHAR2(3)),
|
||||||
|
Column("inmemory", VARCHAR2(8)),
|
||||||
|
Column("inmemory_priority", VARCHAR2(8)),
|
||||||
|
Column("inmemory_distribute", VARCHAR2(15)),
|
||||||
|
Column("inmemory_compression", VARCHAR2(17)),
|
||||||
|
Column("inmemory_duplicate", VARCHAR2(13)),
|
||||||
|
Column("default_collation", VARCHAR2(100)),
|
||||||
|
Column("duplicated", VARCHAR2(1)),
|
||||||
|
Column("sharded", VARCHAR2(1)),
|
||||||
|
Column("externally_sharded", VARCHAR2(1)),
|
||||||
|
Column("externally_duplicated", VARCHAR2(1)),
|
||||||
|
Column("external", VARCHAR2(3)),
|
||||||
|
Column("hybrid", VARCHAR2(3)),
|
||||||
|
Column("cellmemory", VARCHAR2(24)),
|
||||||
|
Column("containers_default", VARCHAR2(3)),
|
||||||
|
Column("container_map", VARCHAR2(3)),
|
||||||
|
Column("extended_data_link", VARCHAR2(3)),
|
||||||
|
Column("extended_data_link_map", VARCHAR2(3)),
|
||||||
|
Column("inmemory_service", VARCHAR2(12)),
|
||||||
|
Column("inmemory_service_name", VARCHAR2(1000)),
|
||||||
|
Column("container_map_object", VARCHAR2(3)),
|
||||||
|
Column("memoptimize_read", VARCHAR2(8)),
|
||||||
|
Column("memoptimize_write", VARCHAR2(8)),
|
||||||
|
Column("has_sensitive_column", VARCHAR2(3)),
|
||||||
|
Column("admit_null", VARCHAR2(3)),
|
||||||
|
Column("data_link_dml_enabled", VARCHAR2(3)),
|
||||||
|
Column("logical_replication", VARCHAR2(8)),
|
||||||
|
).alias("a_tables")
|
||||||
|
|
||||||
|
all_views = Table(
|
||||||
|
"all_views" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("view_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("text_length", NUMBER),
|
||||||
|
Column("text", LONG),
|
||||||
|
Column("text_vc", VARCHAR2(4000)),
|
||||||
|
Column("type_text_length", NUMBER),
|
||||||
|
Column("type_text", VARCHAR2(4000)),
|
||||||
|
Column("oid_text_length", NUMBER),
|
||||||
|
Column("oid_text", VARCHAR2(4000)),
|
||||||
|
Column("view_type_owner", VARCHAR2(128)),
|
||||||
|
Column("view_type", VARCHAR2(128)),
|
||||||
|
Column("superview_name", VARCHAR2(128)),
|
||||||
|
Column("editioning_view", VARCHAR2(1)),
|
||||||
|
Column("read_only", VARCHAR2(1)),
|
||||||
|
Column("container_data", VARCHAR2(1)),
|
||||||
|
Column("bequeath", VARCHAR2(12)),
|
||||||
|
Column("origin_con_id", VARCHAR2(256)),
|
||||||
|
Column("default_collation", VARCHAR2(100)),
|
||||||
|
Column("containers_default", VARCHAR2(3)),
|
||||||
|
Column("container_map", VARCHAR2(3)),
|
||||||
|
Column("extended_data_link", VARCHAR2(3)),
|
||||||
|
Column("extended_data_link_map", VARCHAR2(3)),
|
||||||
|
Column("has_sensitive_column", VARCHAR2(3)),
|
||||||
|
Column("admit_null", VARCHAR2(3)),
|
||||||
|
Column("pdb_local_only", VARCHAR2(3)),
|
||||||
|
).alias("a_views")
|
||||||
|
|
||||||
|
all_sequences = Table(
|
||||||
|
"all_sequences" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("sequence_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("sequence_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("min_value", NUMBER),
|
||||||
|
Column("max_value", NUMBER),
|
||||||
|
Column("increment_by", NUMBER, nullable=False),
|
||||||
|
Column("cycle_flag", VARCHAR2(1)),
|
||||||
|
Column("order_flag", VARCHAR2(1)),
|
||||||
|
Column("cache_size", NUMBER, nullable=False),
|
||||||
|
Column("last_number", NUMBER, nullable=False),
|
||||||
|
Column("scale_flag", VARCHAR2(1)),
|
||||||
|
Column("extend_flag", VARCHAR2(1)),
|
||||||
|
Column("sharded_flag", VARCHAR2(1)),
|
||||||
|
Column("session_flag", VARCHAR2(1)),
|
||||||
|
Column("keep_value", VARCHAR2(1)),
|
||||||
|
).alias("a_sequences")
|
||||||
|
|
||||||
|
all_users = Table(
|
||||||
|
"all_users" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("username", VARCHAR2(128), nullable=False),
|
||||||
|
Column("user_id", NUMBER, nullable=False),
|
||||||
|
Column("created", DATE, nullable=False),
|
||||||
|
Column("common", VARCHAR2(3)),
|
||||||
|
Column("oracle_maintained", VARCHAR2(1)),
|
||||||
|
Column("inherited", VARCHAR2(3)),
|
||||||
|
Column("default_collation", VARCHAR2(100)),
|
||||||
|
Column("implicit", VARCHAR2(3)),
|
||||||
|
Column("all_shard", VARCHAR2(3)),
|
||||||
|
Column("external_shard", VARCHAR2(3)),
|
||||||
|
).alias("a_users")
|
||||||
|
|
||||||
|
all_mviews = Table(
|
||||||
|
"all_mviews" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("mview_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("container_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("query", LONG),
|
||||||
|
Column("query_len", NUMBER(38)),
|
||||||
|
Column("updatable", VARCHAR2(1)),
|
||||||
|
Column("update_log", VARCHAR2(128)),
|
||||||
|
Column("master_rollback_seg", VARCHAR2(128)),
|
||||||
|
Column("master_link", VARCHAR2(128)),
|
||||||
|
Column("rewrite_enabled", VARCHAR2(1)),
|
||||||
|
Column("rewrite_capability", VARCHAR2(9)),
|
||||||
|
Column("refresh_mode", VARCHAR2(6)),
|
||||||
|
Column("refresh_method", VARCHAR2(8)),
|
||||||
|
Column("build_mode", VARCHAR2(9)),
|
||||||
|
Column("fast_refreshable", VARCHAR2(18)),
|
||||||
|
Column("last_refresh_type", VARCHAR2(8)),
|
||||||
|
Column("last_refresh_date", DATE),
|
||||||
|
Column("last_refresh_end_time", DATE),
|
||||||
|
Column("staleness", VARCHAR2(19)),
|
||||||
|
Column("after_fast_refresh", VARCHAR2(19)),
|
||||||
|
Column("unknown_prebuilt", VARCHAR2(1)),
|
||||||
|
Column("unknown_plsql_func", VARCHAR2(1)),
|
||||||
|
Column("unknown_external_table", VARCHAR2(1)),
|
||||||
|
Column("unknown_consider_fresh", VARCHAR2(1)),
|
||||||
|
Column("unknown_import", VARCHAR2(1)),
|
||||||
|
Column("unknown_trusted_fd", VARCHAR2(1)),
|
||||||
|
Column("compile_state", VARCHAR2(19)),
|
||||||
|
Column("use_no_index", VARCHAR2(1)),
|
||||||
|
Column("stale_since", DATE),
|
||||||
|
Column("num_pct_tables", NUMBER),
|
||||||
|
Column("num_fresh_pct_regions", NUMBER),
|
||||||
|
Column("num_stale_pct_regions", NUMBER),
|
||||||
|
Column("segment_created", VARCHAR2(3)),
|
||||||
|
Column("evaluation_edition", VARCHAR2(128)),
|
||||||
|
Column("unusable_before", VARCHAR2(128)),
|
||||||
|
Column("unusable_beginning", VARCHAR2(128)),
|
||||||
|
Column("default_collation", VARCHAR2(100)),
|
||||||
|
Column("on_query_computation", VARCHAR2(1)),
|
||||||
|
Column("auto", VARCHAR2(3)),
|
||||||
|
).alias("a_mviews")
|
||||||
|
|
||||||
|
all_tab_identity_cols = Table(
|
||||||
|
"all_tab_identity_cols" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("generation_type", VARCHAR2(10)),
|
||||||
|
Column("sequence_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("identity_options", VARCHAR2(298)),
|
||||||
|
).alias("a_tab_identity_cols")
|
||||||
|
|
||||||
|
all_tab_cols = Table(
|
||||||
|
"all_tab_cols" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("data_type", VARCHAR2(128)),
|
||||||
|
Column("data_type_mod", VARCHAR2(3)),
|
||||||
|
Column("data_type_owner", VARCHAR2(128)),
|
||||||
|
Column("data_length", NUMBER, nullable=False),
|
||||||
|
Column("data_precision", NUMBER),
|
||||||
|
Column("data_scale", NUMBER),
|
||||||
|
Column("nullable", VARCHAR2(1)),
|
||||||
|
Column("column_id", NUMBER),
|
||||||
|
Column("default_length", NUMBER),
|
||||||
|
Column("data_default", LONG),
|
||||||
|
Column("num_distinct", NUMBER),
|
||||||
|
Column("low_value", RAW(1000)),
|
||||||
|
Column("high_value", RAW(1000)),
|
||||||
|
Column("density", NUMBER),
|
||||||
|
Column("num_nulls", NUMBER),
|
||||||
|
Column("num_buckets", NUMBER),
|
||||||
|
Column("last_analyzed", DATE),
|
||||||
|
Column("sample_size", NUMBER),
|
||||||
|
Column("character_set_name", VARCHAR2(44)),
|
||||||
|
Column("char_col_decl_length", NUMBER),
|
||||||
|
Column("global_stats", VARCHAR2(3)),
|
||||||
|
Column("user_stats", VARCHAR2(3)),
|
||||||
|
Column("avg_col_len", NUMBER),
|
||||||
|
Column("char_length", NUMBER),
|
||||||
|
Column("char_used", VARCHAR2(1)),
|
||||||
|
Column("v80_fmt_image", VARCHAR2(3)),
|
||||||
|
Column("data_upgraded", VARCHAR2(3)),
|
||||||
|
Column("hidden_column", VARCHAR2(3)),
|
||||||
|
Column("virtual_column", VARCHAR2(3)),
|
||||||
|
Column("segment_column_id", NUMBER),
|
||||||
|
Column("internal_column_id", NUMBER, nullable=False),
|
||||||
|
Column("histogram", VARCHAR2(15)),
|
||||||
|
Column("qualified_col_name", VARCHAR2(4000)),
|
||||||
|
Column("user_generated", VARCHAR2(3)),
|
||||||
|
Column("default_on_null", VARCHAR2(3)),
|
||||||
|
Column("identity_column", VARCHAR2(3)),
|
||||||
|
Column("evaluation_edition", VARCHAR2(128)),
|
||||||
|
Column("unusable_before", VARCHAR2(128)),
|
||||||
|
Column("unusable_beginning", VARCHAR2(128)),
|
||||||
|
Column("collation", VARCHAR2(100)),
|
||||||
|
Column("collated_column_id", NUMBER),
|
||||||
|
).alias("a_tab_cols")
|
||||||
|
|
||||||
|
all_tab_comments = Table(
|
||||||
|
"all_tab_comments" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_type", VARCHAR2(11)),
|
||||||
|
Column("comments", VARCHAR2(4000)),
|
||||||
|
Column("origin_con_id", NUMBER),
|
||||||
|
).alias("a_tab_comments")
|
||||||
|
|
||||||
|
all_col_comments = Table(
|
||||||
|
"all_col_comments" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("comments", VARCHAR2(4000)),
|
||||||
|
Column("origin_con_id", NUMBER),
|
||||||
|
).alias("a_col_comments")
|
||||||
|
|
||||||
|
all_mview_comments = Table(
|
||||||
|
"all_mview_comments" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("mview_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("comments", VARCHAR2(4000)),
|
||||||
|
).alias("a_mview_comments")
|
||||||
|
|
||||||
|
all_ind_columns = Table(
|
||||||
|
"all_ind_columns" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("index_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("index_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_name", VARCHAR2(4000)),
|
||||||
|
Column("column_position", NUMBER, nullable=False),
|
||||||
|
Column("column_length", NUMBER, nullable=False),
|
||||||
|
Column("char_length", NUMBER),
|
||||||
|
Column("descend", VARCHAR2(4)),
|
||||||
|
Column("collated_column_id", NUMBER),
|
||||||
|
).alias("a_ind_columns")
|
||||||
|
|
||||||
|
all_indexes = Table(
|
||||||
|
"all_indexes" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("index_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("index_type", VARCHAR2(27)),
|
||||||
|
Column("table_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_type", CHAR(11)),
|
||||||
|
Column("uniqueness", VARCHAR2(9)),
|
||||||
|
Column("compression", VARCHAR2(13)),
|
||||||
|
Column("prefix_length", NUMBER),
|
||||||
|
Column("tablespace_name", VARCHAR2(30)),
|
||||||
|
Column("ini_trans", NUMBER),
|
||||||
|
Column("max_trans", NUMBER),
|
||||||
|
Column("initial_extent", NUMBER),
|
||||||
|
Column("next_extent", NUMBER),
|
||||||
|
Column("min_extents", NUMBER),
|
||||||
|
Column("max_extents", NUMBER),
|
||||||
|
Column("pct_increase", NUMBER),
|
||||||
|
Column("pct_threshold", NUMBER),
|
||||||
|
Column("include_column", NUMBER),
|
||||||
|
Column("freelists", NUMBER),
|
||||||
|
Column("freelist_groups", NUMBER),
|
||||||
|
Column("pct_free", NUMBER),
|
||||||
|
Column("logging", VARCHAR2(3)),
|
||||||
|
Column("blevel", NUMBER),
|
||||||
|
Column("leaf_blocks", NUMBER),
|
||||||
|
Column("distinct_keys", NUMBER),
|
||||||
|
Column("avg_leaf_blocks_per_key", NUMBER),
|
||||||
|
Column("avg_data_blocks_per_key", NUMBER),
|
||||||
|
Column("clustering_factor", NUMBER),
|
||||||
|
Column("status", VARCHAR2(8)),
|
||||||
|
Column("num_rows", NUMBER),
|
||||||
|
Column("sample_size", NUMBER),
|
||||||
|
Column("last_analyzed", DATE),
|
||||||
|
Column("degree", VARCHAR2(40)),
|
||||||
|
Column("instances", VARCHAR2(40)),
|
||||||
|
Column("partitioned", VARCHAR2(3)),
|
||||||
|
Column("temporary", VARCHAR2(1)),
|
||||||
|
Column("generated", VARCHAR2(1)),
|
||||||
|
Column("secondary", VARCHAR2(1)),
|
||||||
|
Column("buffer_pool", VARCHAR2(7)),
|
||||||
|
Column("flash_cache", VARCHAR2(7)),
|
||||||
|
Column("cell_flash_cache", VARCHAR2(7)),
|
||||||
|
Column("user_stats", VARCHAR2(3)),
|
||||||
|
Column("duration", VARCHAR2(15)),
|
||||||
|
Column("pct_direct_access", NUMBER),
|
||||||
|
Column("ityp_owner", VARCHAR2(128)),
|
||||||
|
Column("ityp_name", VARCHAR2(128)),
|
||||||
|
Column("parameters", VARCHAR2(1000)),
|
||||||
|
Column("global_stats", VARCHAR2(3)),
|
||||||
|
Column("domidx_status", VARCHAR2(12)),
|
||||||
|
Column("domidx_opstatus", VARCHAR2(6)),
|
||||||
|
Column("funcidx_status", VARCHAR2(8)),
|
||||||
|
Column("join_index", VARCHAR2(3)),
|
||||||
|
Column("iot_redundant_pkey_elim", VARCHAR2(3)),
|
||||||
|
Column("dropped", VARCHAR2(3)),
|
||||||
|
Column("visibility", VARCHAR2(9)),
|
||||||
|
Column("domidx_management", VARCHAR2(14)),
|
||||||
|
Column("segment_created", VARCHAR2(3)),
|
||||||
|
Column("orphaned_entries", VARCHAR2(3)),
|
||||||
|
Column("indexing", VARCHAR2(7)),
|
||||||
|
Column("auto", VARCHAR2(3)),
|
||||||
|
).alias("a_indexes")
|
||||||
|
|
||||||
|
all_ind_expressions = Table(
|
||||||
|
"all_ind_expressions" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("index_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("index_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_expression", LONG),
|
||||||
|
Column("column_position", NUMBER, nullable=False),
|
||||||
|
).alias("a_ind_expressions")
|
||||||
|
|
||||||
|
all_constraints = Table(
|
||||||
|
"all_constraints" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128)),
|
||||||
|
Column("constraint_name", VARCHAR2(128)),
|
||||||
|
Column("constraint_type", VARCHAR2(1)),
|
||||||
|
Column("table_name", VARCHAR2(128)),
|
||||||
|
Column("search_condition", LONG),
|
||||||
|
Column("search_condition_vc", VARCHAR2(4000)),
|
||||||
|
Column("r_owner", VARCHAR2(128)),
|
||||||
|
Column("r_constraint_name", VARCHAR2(128)),
|
||||||
|
Column("delete_rule", VARCHAR2(9)),
|
||||||
|
Column("status", VARCHAR2(8)),
|
||||||
|
Column("deferrable", VARCHAR2(14)),
|
||||||
|
Column("deferred", VARCHAR2(9)),
|
||||||
|
Column("validated", VARCHAR2(13)),
|
||||||
|
Column("generated", VARCHAR2(14)),
|
||||||
|
Column("bad", VARCHAR2(3)),
|
||||||
|
Column("rely", VARCHAR2(4)),
|
||||||
|
Column("last_change", DATE),
|
||||||
|
Column("index_owner", VARCHAR2(128)),
|
||||||
|
Column("index_name", VARCHAR2(128)),
|
||||||
|
Column("invalid", VARCHAR2(7)),
|
||||||
|
Column("view_related", VARCHAR2(14)),
|
||||||
|
Column("origin_con_id", VARCHAR2(256)),
|
||||||
|
).alias("a_constraints")
|
||||||
|
|
||||||
|
all_cons_columns = Table(
|
||||||
|
"all_cons_columns" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("constraint_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("table_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("column_name", VARCHAR2(4000)),
|
||||||
|
Column("position", NUMBER),
|
||||||
|
).alias("a_cons_columns")
|
||||||
|
|
||||||
|
# TODO figure out if it's still relevant, since there is no mention from here
|
||||||
|
# https://docs.oracle.com/en/database/oracle/oracle-database/21/refrn/ALL_DB_LINKS.html
|
||||||
|
# original note:
|
||||||
|
# using user_db_links here since all_db_links appears
|
||||||
|
# to have more restricted permissions.
|
||||||
|
# https://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_admin005.htm
|
||||||
|
# will need to hear from more users if we are doing
|
||||||
|
# the right thing here. See [ticket:2619]
|
||||||
|
all_db_links = Table(
|
||||||
|
"all_db_links" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("db_link", VARCHAR2(128), nullable=False),
|
||||||
|
Column("username", VARCHAR2(128)),
|
||||||
|
Column("host", VARCHAR2(2000)),
|
||||||
|
Column("created", DATE, nullable=False),
|
||||||
|
Column("hidden", VARCHAR2(3)),
|
||||||
|
Column("shard_internal", VARCHAR2(3)),
|
||||||
|
Column("valid", VARCHAR2(3)),
|
||||||
|
Column("intra_cdb", VARCHAR2(3)),
|
||||||
|
).alias("a_db_links")
|
||||||
|
|
||||||
|
all_synonyms = Table(
|
||||||
|
"all_synonyms" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128)),
|
||||||
|
Column("synonym_name", VARCHAR2(128)),
|
||||||
|
Column("table_owner", VARCHAR2(128)),
|
||||||
|
Column("table_name", VARCHAR2(128)),
|
||||||
|
Column("db_link", VARCHAR2(128)),
|
||||||
|
Column("origin_con_id", VARCHAR2(256)),
|
||||||
|
).alias("a_synonyms")
|
||||||
|
|
||||||
|
all_objects = Table(
|
||||||
|
"all_objects" + DB_LINK_PLACEHOLDER,
|
||||||
|
dictionary_meta,
|
||||||
|
Column("owner", VARCHAR2(128), nullable=False),
|
||||||
|
Column("object_name", VARCHAR2(128), nullable=False),
|
||||||
|
Column("subobject_name", VARCHAR2(128)),
|
||||||
|
Column("object_id", NUMBER, nullable=False),
|
||||||
|
Column("data_object_id", NUMBER),
|
||||||
|
Column("object_type", VARCHAR2(23)),
|
||||||
|
Column("created", DATE, nullable=False),
|
||||||
|
Column("last_ddl_time", DATE, nullable=False),
|
||||||
|
Column("timestamp", VARCHAR2(19)),
|
||||||
|
Column("status", VARCHAR2(7)),
|
||||||
|
Column("temporary", VARCHAR2(1)),
|
||||||
|
Column("generated", VARCHAR2(1)),
|
||||||
|
Column("secondary", VARCHAR2(1)),
|
||||||
|
Column("namespace", NUMBER, nullable=False),
|
||||||
|
Column("edition_name", VARCHAR2(128)),
|
||||||
|
Column("sharing", VARCHAR2(13)),
|
||||||
|
Column("editionable", VARCHAR2(1)),
|
||||||
|
Column("oracle_maintained", VARCHAR2(1)),
|
||||||
|
Column("application", VARCHAR2(1)),
|
||||||
|
Column("default_collation", VARCHAR2(100)),
|
||||||
|
Column("duplicated", VARCHAR2(1)),
|
||||||
|
Column("sharded", VARCHAR2(1)),
|
||||||
|
Column("created_appid", NUMBER),
|
||||||
|
Column("created_vsnid", NUMBER),
|
||||||
|
Column("modified_appid", NUMBER),
|
||||||
|
Column("modified_vsnid", NUMBER),
|
||||||
|
).alias("a_objects")
|
||||||
942
venv/Lib/site-packages/sqlalchemy/dialects/oracle/oracledb.py
Normal file
942
venv/Lib/site-packages/sqlalchemy/dialects/oracle/oracledb.py
Normal file
@@ -0,0 +1,942 @@
|
|||||||
|
# dialects/oracle/oracledb.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
# mypy: ignore-errors
|
||||||
|
|
||||||
|
r""".. dialect:: oracle+oracledb
|
||||||
|
:name: python-oracledb
|
||||||
|
:dbapi: oracledb
|
||||||
|
:connectstring: oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]
|
||||||
|
:url: https://oracle.github.io/python-oracledb/
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Python-oracledb is the Oracle Database driver for Python. It features a default
|
||||||
|
"thin" client mode that requires no dependencies, and an optional "thick" mode
|
||||||
|
that uses Oracle Client libraries. It supports SQLAlchemy features including
|
||||||
|
two phase transactions and Asyncio.
|
||||||
|
|
||||||
|
Python-oracle is the renamed, updated cx_Oracle driver. Oracle is no longer
|
||||||
|
doing any releases in the cx_Oracle namespace.
|
||||||
|
|
||||||
|
The SQLAlchemy ``oracledb`` dialect provides both a sync and an async
|
||||||
|
implementation under the same dialect name. The proper version is
|
||||||
|
selected depending on how the engine is created:
|
||||||
|
|
||||||
|
* calling :func:`_sa.create_engine` with ``oracle+oracledb://...`` will
|
||||||
|
automatically select the sync version::
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
sync_engine = create_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@localhost?service_name=FREEPDB1"
|
||||||
|
)
|
||||||
|
|
||||||
|
* calling :func:`_asyncio.create_async_engine` with ``oracle+oracledb://...``
|
||||||
|
will automatically select the async version::
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
|
|
||||||
|
asyncio_engine = create_async_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@localhost?service_name=FREEPDB1"
|
||||||
|
)
|
||||||
|
|
||||||
|
The asyncio version of the dialect may also be specified explicitly using the
|
||||||
|
``oracledb_async`` suffix::
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
|
|
||||||
|
asyncio_engine = create_async_engine(
|
||||||
|
"oracle+oracledb_async://scott:tiger@localhost?service_name=FREEPDB1"
|
||||||
|
)
|
||||||
|
|
||||||
|
.. versionadded:: 2.0.25 added support for the async version of oracledb.
|
||||||
|
|
||||||
|
Thick mode support
|
||||||
|
------------------
|
||||||
|
|
||||||
|
By default, the python-oracledb driver runs in a "thin" mode that does not
|
||||||
|
require Oracle Client libraries to be installed. The driver also supports a
|
||||||
|
"thick" mode that uses Oracle Client libraries to get functionality such as
|
||||||
|
Oracle Application Continuity.
|
||||||
|
|
||||||
|
To enable thick mode, call `oracledb.init_oracle_client()
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.init_oracle_client>`_
|
||||||
|
explicitly, or pass the parameter ``thick_mode=True`` to
|
||||||
|
:func:`_sa.create_engine`. To pass custom arguments to
|
||||||
|
``init_oracle_client()``, like the ``lib_dir`` path, a dict may be passed, for
|
||||||
|
example::
|
||||||
|
|
||||||
|
engine = sa.create_engine(
|
||||||
|
"oracle+oracledb://...",
|
||||||
|
thick_mode={
|
||||||
|
"lib_dir": "/path/to/oracle/client/lib",
|
||||||
|
"config_dir": "/path/to/network_config_file_directory",
|
||||||
|
"driver_name": "my-app : 1.0.0",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Note that passing a ``lib_dir`` path should only be done on macOS or
|
||||||
|
Windows. On Linux it does not behave as you might expect.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
python-oracledb documentation `Enabling python-oracledb Thick mode
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/initialization.html#enabling-python-oracledb-thick-mode>`_
|
||||||
|
|
||||||
|
Connecting to Oracle Database
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
python-oracledb provides several methods of indicating the target database.
|
||||||
|
The dialect translates from a series of different URL forms.
|
||||||
|
|
||||||
|
Given the hostname, port and service name of the target database, you can
|
||||||
|
connect in SQLAlchemy using the ``service_name`` query string parameter::
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@hostname:port?service_name=myservice"
|
||||||
|
)
|
||||||
|
|
||||||
|
Connecting with Easy Connect strings
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
You can pass any valid python-oracledb connection string as the ``dsn`` key
|
||||||
|
value in a :paramref:`_sa.create_engine.connect_args` dictionary. See
|
||||||
|
python-oracledb documentation `Oracle Net Services Connection Strings
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#oracle-net-services-connection-strings>`_.
|
||||||
|
|
||||||
|
For example to use an `Easy Connect string
|
||||||
|
<https://download.oracle.com/ocomdocs/global/Oracle-Net-Easy-Connect-Plus.pdf>`_
|
||||||
|
with a timeout to prevent connection establishment from hanging if the network
|
||||||
|
transport to the database cannot be established in 30 seconds, and also setting
|
||||||
|
a keep-alive time of 60 seconds to stop idle network connections from being
|
||||||
|
terminated by a firewall::
|
||||||
|
|
||||||
|
e = create_engine(
|
||||||
|
"oracle+oracledb://@",
|
||||||
|
connect_args={
|
||||||
|
"user": "scott",
|
||||||
|
"password": "tiger",
|
||||||
|
"dsn": "hostname:port/myservice?transport_connect_timeout=30&expire_time=60",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
The Easy Connect syntax has been enhanced during the life of Oracle Database.
|
||||||
|
Review the documentation for your database version. The current documentation
|
||||||
|
is at `Understanding the Easy Connect Naming Method
|
||||||
|
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-B0437826-43C1-49EC-A94D-B650B6A4A6EE>`_.
|
||||||
|
|
||||||
|
The general syntax is similar to:
|
||||||
|
|
||||||
|
.. sourcecode:: text
|
||||||
|
|
||||||
|
[[protocol:]//]host[:port][/[service_name]][?parameter_name=value{¶meter_name=value}]
|
||||||
|
|
||||||
|
Note that although the SQLAlchemy URL syntax ``hostname:port/dbname`` looks
|
||||||
|
like Oracle's Easy Connect syntax, it is different. SQLAlchemy's URL requires a
|
||||||
|
system identifier (SID) for the ``dbname`` component::
|
||||||
|
|
||||||
|
engine = create_engine("oracle+oracledb://scott:tiger@hostname:port/sid")
|
||||||
|
|
||||||
|
Easy Connect syntax does not support SIDs. It uses services names, which are
|
||||||
|
the preferred choice for connecting to Oracle Database.
|
||||||
|
|
||||||
|
Passing python-oracledb connect arguments
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Other python-oracledb driver `connection options
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.connect>`_
|
||||||
|
can be passed in ``connect_args``. For example::
|
||||||
|
|
||||||
|
e = create_engine(
|
||||||
|
"oracle+oracledb://@",
|
||||||
|
connect_args={
|
||||||
|
"user": "scott",
|
||||||
|
"password": "tiger",
|
||||||
|
"dsn": "hostname:port/myservice",
|
||||||
|
"events": True,
|
||||||
|
"mode": oracledb.AUTH_MODE_SYSDBA,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Connecting with tnsnames.ora TNS aliases
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If no port, database name, or service name is provided, the dialect will use an
|
||||||
|
Oracle Database DSN "connection string". This takes the "hostname" portion of
|
||||||
|
the URL as the data source name. For example, if the ``tnsnames.ora`` file
|
||||||
|
contains a `TNS Alias
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#tns-aliases-for-connection-strings>`_
|
||||||
|
of ``myalias`` as below:
|
||||||
|
|
||||||
|
.. sourcecode:: text
|
||||||
|
|
||||||
|
myalias =
|
||||||
|
(DESCRIPTION =
|
||||||
|
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
|
||||||
|
(CONNECT_DATA =
|
||||||
|
(SERVER = DEDICATED)
|
||||||
|
(SERVICE_NAME = orclpdb1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
The python-oracledb dialect connects to this database service when ``myalias`` is the
|
||||||
|
hostname portion of the URL, without specifying a port, database name or
|
||||||
|
``service_name``::
|
||||||
|
|
||||||
|
engine = create_engine("oracle+oracledb://scott:tiger@myalias")
|
||||||
|
|
||||||
|
Connecting to Oracle Autonomous Database
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Users of Oracle Autonomous Database should use either use the TNS Alias URL
|
||||||
|
shown above, or pass the TNS Alias as the ``dsn`` key value in a
|
||||||
|
:paramref:`_sa.create_engine.connect_args` dictionary.
|
||||||
|
|
||||||
|
If Oracle Autonomous Database is configured for mutual TLS ("mTLS")
|
||||||
|
connections, then additional configuration is required as shown in `Connecting
|
||||||
|
to Oracle Cloud Autonomous Databases
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#connecting-to-oracle-cloud-autonomous-databases>`_. In
|
||||||
|
summary, Thick mode users should configure file locations and set the wallet
|
||||||
|
path in ``sqlnet.ora`` appropriately::
|
||||||
|
|
||||||
|
e = create_engine(
|
||||||
|
"oracle+oracledb://@",
|
||||||
|
thick_mode={
|
||||||
|
# directory containing tnsnames.ora and cwallet.so
|
||||||
|
"config_dir": "/opt/oracle/wallet_dir",
|
||||||
|
},
|
||||||
|
connect_args={
|
||||||
|
"user": "scott",
|
||||||
|
"password": "tiger",
|
||||||
|
"dsn": "mydb_high",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Thin mode users of mTLS should pass the appropriate directories and PEM wallet
|
||||||
|
password when creating the engine, similar to::
|
||||||
|
|
||||||
|
e = create_engine(
|
||||||
|
"oracle+oracledb://@",
|
||||||
|
connect_args={
|
||||||
|
"user": "scott",
|
||||||
|
"password": "tiger",
|
||||||
|
"dsn": "mydb_high",
|
||||||
|
"config_dir": "/opt/oracle/wallet_dir", # directory containing tnsnames.ora
|
||||||
|
"wallet_location": "/opt/oracle/wallet_dir", # directory containing ewallet.pem
|
||||||
|
"wallet_password": "top secret", # password for the PEM file
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Typically ``config_dir`` and ``wallet_location`` are the same directory, which
|
||||||
|
is where the Oracle Autonomous Database wallet zip file was extracted. Note
|
||||||
|
this directory should be protected.
|
||||||
|
|
||||||
|
Using python-oracledb Connection Pooling
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
The python-oracledb driver provides its own connection pool implementation that
|
||||||
|
may be used in place of SQLAlchemy's pooling functionality. The driver pool
|
||||||
|
gives support for high availability features such as dead connection detection,
|
||||||
|
connection draining for planned database downtime, support for Oracle
|
||||||
|
Application Continuity and Transparent Application Continuity, and gives
|
||||||
|
support for `Database Resident Connection Pooling (DRCP)
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#database-resident-connection-pooling-drcp>`_.
|
||||||
|
|
||||||
|
To take advantage of python-oracledb's pool, use the
|
||||||
|
:paramref:`_sa.create_engine.creator` parameter to provide a function that
|
||||||
|
returns a new connection, along with setting
|
||||||
|
:paramref:`_sa.create_engine.pool_class` to ``NullPool`` to disable
|
||||||
|
SQLAlchemy's pooling::
|
||||||
|
|
||||||
|
import oracledb
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import text
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
# Uncomment to use the optional python-oracledb Thick mode.
|
||||||
|
# Review the python-oracledb doc for the appropriate parameters
|
||||||
|
# oracledb.init_oracle_client(<your parameters>)
|
||||||
|
|
||||||
|
pool = oracledb.create_pool(
|
||||||
|
user="scott",
|
||||||
|
password="tiger",
|
||||||
|
dsn="localhost:1521/freepdb1",
|
||||||
|
min=1,
|
||||||
|
max=4,
|
||||||
|
increment=1,
|
||||||
|
)
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://", creator=pool.acquire, poolclass=NullPool
|
||||||
|
)
|
||||||
|
|
||||||
|
The above engine may then be used normally. Internally, python-oracledb handles
|
||||||
|
connection pooling::
|
||||||
|
|
||||||
|
with engine.connect() as conn:
|
||||||
|
print(conn.scalar(text("select 1 from dual")))
|
||||||
|
|
||||||
|
Refer to the python-oracledb documentation for `oracledb.create_pool()
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.create_pool>`_
|
||||||
|
for the arguments that can be used when creating a connection pool.
|
||||||
|
|
||||||
|
.. _drcp:
|
||||||
|
|
||||||
|
Using Oracle Database Resident Connection Pooling (DRCP)
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
When using Oracle Database's Database Resident Connection Pooling (DRCP), the
|
||||||
|
best practice is to specify a connection class and "purity". Refer to the
|
||||||
|
`python-oracledb documentation on DRCP
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#database-resident-connection-pooling-drcp>`_.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
import oracledb
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import text
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
# Uncomment to use the optional python-oracledb Thick mode.
|
||||||
|
# Review the python-oracledb doc for the appropriate parameters
|
||||||
|
# oracledb.init_oracle_client(<your parameters>)
|
||||||
|
|
||||||
|
pool = oracledb.create_pool(
|
||||||
|
user="scott",
|
||||||
|
password="tiger",
|
||||||
|
dsn="localhost:1521/freepdb1",
|
||||||
|
min=1,
|
||||||
|
max=4,
|
||||||
|
increment=1,
|
||||||
|
cclass="MYCLASS",
|
||||||
|
purity=oracledb.PURITY_SELF,
|
||||||
|
)
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://", creator=pool.acquire, poolclass=NullPool
|
||||||
|
)
|
||||||
|
|
||||||
|
The above engine may then be used normally where python-oracledb handles
|
||||||
|
application connection pooling and Oracle Database additionally uses DRCP::
|
||||||
|
|
||||||
|
with engine.connect() as conn:
|
||||||
|
print(conn.scalar(text("select 1 from dual")))
|
||||||
|
|
||||||
|
If you wish to use different connection classes or purities for different
|
||||||
|
connections, then wrap ``pool.acquire()``::
|
||||||
|
|
||||||
|
import oracledb
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import text
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
# Uncomment to use python-oracledb Thick mode.
|
||||||
|
# Review the python-oracledb doc for the appropriate parameters
|
||||||
|
# oracledb.init_oracle_client(<your parameters>)
|
||||||
|
|
||||||
|
pool = oracledb.create_pool(
|
||||||
|
user="scott",
|
||||||
|
password="tiger",
|
||||||
|
dsn="localhost:1521/freepdb1",
|
||||||
|
min=1,
|
||||||
|
max=4,
|
||||||
|
increment=1,
|
||||||
|
cclass="MYCLASS",
|
||||||
|
purity=oracledb.PURITY_SELF,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def creator():
|
||||||
|
return pool.acquire(cclass="MYOTHERCLASS", purity=oracledb.PURITY_NEW)
|
||||||
|
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://", creator=creator, poolclass=NullPool
|
||||||
|
)
|
||||||
|
|
||||||
|
Engine Options consumed by the SQLAlchemy oracledb dialect outside of the driver
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
There are also options that are consumed by the SQLAlchemy oracledb dialect
|
||||||
|
itself. These options are always passed directly to :func:`_sa.create_engine`,
|
||||||
|
such as::
|
||||||
|
|
||||||
|
e = create_engine("oracle+oracledb://user:pass@tnsalias", arraysize=500)
|
||||||
|
|
||||||
|
The parameters accepted by the oracledb dialect are as follows:
|
||||||
|
|
||||||
|
* ``arraysize`` - set the driver cursor.arraysize value. It defaults to
|
||||||
|
``None``, indicating that the driver default value of 100 should be used.
|
||||||
|
This setting controls how many rows are buffered when fetching rows, and can
|
||||||
|
have a significant effect on performance if increased for queries that return
|
||||||
|
large numbers of rows.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.26 - changed the default value from 50 to None,
|
||||||
|
to use the default value of the driver itself.
|
||||||
|
|
||||||
|
* ``auto_convert_lobs`` - defaults to True; See :ref:`oracledb_lob`.
|
||||||
|
|
||||||
|
* ``coerce_to_decimal`` - see :ref:`oracledb_numeric` for detail.
|
||||||
|
|
||||||
|
* ``encoding_errors`` - see :ref:`oracledb_unicode_encoding_errors` for detail.
|
||||||
|
|
||||||
|
.. _oracledb_unicode:
|
||||||
|
|
||||||
|
Unicode
|
||||||
|
-------
|
||||||
|
|
||||||
|
As is the case for all DBAPIs under Python 3, all strings are inherently
|
||||||
|
Unicode strings.
|
||||||
|
|
||||||
|
Ensuring the Correct Client Encoding
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In python-oracledb, the encoding used for all character data is "UTF-8".
|
||||||
|
|
||||||
|
Unicode-specific Column datatypes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Core expression language handles unicode data by use of the
|
||||||
|
:class:`.Unicode` and :class:`.UnicodeText` datatypes. These types correspond
|
||||||
|
to the VARCHAR2 and CLOB Oracle Database datatypes by default. When using
|
||||||
|
these datatypes with Unicode data, it is expected that the database is
|
||||||
|
configured with a Unicode-aware character set so that the VARCHAR2 and CLOB
|
||||||
|
datatypes can accommodate the data.
|
||||||
|
|
||||||
|
In the case that Oracle Database is not configured with a Unicode character
|
||||||
|
set, the two options are to use the :class:`_types.NCHAR` and
|
||||||
|
:class:`_oracle.NCLOB` datatypes explicitly, or to pass the flag
|
||||||
|
``use_nchar_for_unicode=True`` to :func:`_sa.create_engine`, which will cause
|
||||||
|
the SQLAlchemy dialect to use NCHAR/NCLOB for the :class:`.Unicode` /
|
||||||
|
:class:`.UnicodeText` datatypes instead of VARCHAR/CLOB.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.3 The :class:`.Unicode` and :class:`.UnicodeText`
|
||||||
|
datatypes now correspond to the ``VARCHAR2`` and ``CLOB`` Oracle Database
|
||||||
|
datatypes unless the ``use_nchar_for_unicode=True`` is passed to the dialect
|
||||||
|
when :func:`_sa.create_engine` is called.
|
||||||
|
|
||||||
|
|
||||||
|
.. _oracledb_unicode_encoding_errors:
|
||||||
|
|
||||||
|
Encoding Errors
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For the unusual case that data in Oracle Database is present with a broken
|
||||||
|
encoding, the dialect accepts a parameter ``encoding_errors`` which will be
|
||||||
|
passed to Unicode decoding functions in order to affect how decoding errors are
|
||||||
|
handled. The value is ultimately consumed by the Python `decode
|
||||||
|
<https://docs.python.org/3/library/stdtypes.html#bytes.decode>`_ function, and
|
||||||
|
is passed both via python-oracledb's ``encodingErrors`` parameter consumed by
|
||||||
|
``Cursor.var()``, as well as SQLAlchemy's own decoding function, as the
|
||||||
|
python-oracledb dialect makes use of both under different circumstances.
|
||||||
|
|
||||||
|
.. versionadded:: 1.3.11
|
||||||
|
|
||||||
|
|
||||||
|
.. _oracledb_setinputsizes:
|
||||||
|
|
||||||
|
Fine grained control over python-oracledb data binding with setinputsizes
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The python-oracle DBAPI has a deep and fundamental reliance upon the usage of
|
||||||
|
the DBAPI ``setinputsizes()`` call. The purpose of this call is to establish
|
||||||
|
the datatypes that are bound to a SQL statement for Python values being passed
|
||||||
|
as parameters. While virtually no other DBAPI assigns any use to the
|
||||||
|
``setinputsizes()`` call, the python-oracledb DBAPI relies upon it heavily in
|
||||||
|
its interactions with the Oracle Database, and in some scenarios it is not
|
||||||
|
possible for SQLAlchemy to know exactly how data should be bound, as some
|
||||||
|
settings can cause profoundly different performance characteristics, while
|
||||||
|
altering the type coercion behavior at the same time.
|
||||||
|
|
||||||
|
Users of the oracledb dialect are **strongly encouraged** to read through
|
||||||
|
python-oracledb's list of built-in datatype symbols at `Database Types
|
||||||
|
<https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html#database-types>`_
|
||||||
|
Note that in some cases, significant performance degradation can occur when
|
||||||
|
using these types vs. not.
|
||||||
|
|
||||||
|
On the SQLAlchemy side, the :meth:`.DialectEvents.do_setinputsizes` event can
|
||||||
|
be used both for runtime visibility (e.g. logging) of the setinputsizes step as
|
||||||
|
well as to fully control how ``setinputsizes()`` is used on a per-statement
|
||||||
|
basis.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.9 Added :meth:`.DialectEvents.setinputsizes`
|
||||||
|
|
||||||
|
|
||||||
|
Example 1 - logging all setinputsizes calls
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following example illustrates how to log the intermediary values from a
|
||||||
|
SQLAlchemy perspective before they are converted to the raw ``setinputsizes()``
|
||||||
|
parameter dictionary. The keys of the dictionary are :class:`.BindParameter`
|
||||||
|
objects which have a ``.key`` and a ``.type`` attribute::
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine, event
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(engine, "do_setinputsizes")
|
||||||
|
def _log_setinputsizes(inputsizes, cursor, statement, parameters, context):
|
||||||
|
for bindparam, dbapitype in inputsizes.items():
|
||||||
|
log.info(
|
||||||
|
"Bound parameter name: %s SQLAlchemy type: %r DBAPI object: %s",
|
||||||
|
bindparam.key,
|
||||||
|
bindparam.type,
|
||||||
|
dbapitype,
|
||||||
|
)
|
||||||
|
|
||||||
|
Example 2 - remove all bindings to CLOB
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For performance, fetching LOB datatypes from Oracle Database is set by default
|
||||||
|
for the ``Text`` type within SQLAlchemy. This setting can be modified as
|
||||||
|
follows::
|
||||||
|
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine, event
|
||||||
|
from oracledb import CLOB
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(engine, "do_setinputsizes")
|
||||||
|
def _remove_clob(inputsizes, cursor, statement, parameters, context):
|
||||||
|
for bindparam, dbapitype in list(inputsizes.items()):
|
||||||
|
if dbapitype is CLOB:
|
||||||
|
del inputsizes[bindparam]
|
||||||
|
|
||||||
|
.. _oracledb_lob:
|
||||||
|
|
||||||
|
LOB Datatypes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
LOB datatypes refer to the "large object" datatypes such as CLOB, NCLOB and
|
||||||
|
BLOB. Oracle Database can efficiently return these datatypes as a single
|
||||||
|
buffer. SQLAlchemy makes use of type handlers to do this by default.
|
||||||
|
|
||||||
|
To disable the use of the type handlers and deliver LOB objects as classic
|
||||||
|
buffered objects with a ``read()`` method, the parameter
|
||||||
|
``auto_convert_lobs=False`` may be passed to :func:`_sa.create_engine`.
|
||||||
|
|
||||||
|
.. _oracledb_returning:
|
||||||
|
|
||||||
|
RETURNING Support
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The oracledb dialect implements RETURNING using OUT parameters. The dialect
|
||||||
|
supports RETURNING fully.
|
||||||
|
|
||||||
|
Two Phase Transaction Support
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Two phase transactions are fully supported with python-oracledb. (Thin mode
|
||||||
|
requires python-oracledb 2.3). APIs for two phase transactions are provided at
|
||||||
|
the Core level via :meth:`_engine.Connection.begin_twophase` and
|
||||||
|
:paramref:`_orm.Session.twophase` for transparent ORM use.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.32 added support for two phase transactions
|
||||||
|
|
||||||
|
.. _oracledb_numeric:
|
||||||
|
|
||||||
|
Precision Numerics
|
||||||
|
------------------
|
||||||
|
|
||||||
|
SQLAlchemy's numeric types can handle receiving and returning values as Python
|
||||||
|
``Decimal`` objects or float objects. When a :class:`.Numeric` object, or a
|
||||||
|
subclass such as :class:`.Float`, :class:`_oracle.DOUBLE_PRECISION` etc. is in
|
||||||
|
use, the :paramref:`.Numeric.asdecimal` flag determines if values should be
|
||||||
|
coerced to ``Decimal`` upon return, or returned as float objects. To make
|
||||||
|
matters more complicated under Oracle Database, the ``NUMBER`` type can also
|
||||||
|
represent integer values if the "scale" is zero, so the Oracle
|
||||||
|
Database-specific :class:`_oracle.NUMBER` type takes this into account as well.
|
||||||
|
|
||||||
|
The oracledb dialect makes extensive use of connection- and cursor-level
|
||||||
|
"outputtypehandler" callables in order to coerce numeric values as requested.
|
||||||
|
These callables are specific to the specific flavor of :class:`.Numeric` in
|
||||||
|
use, as well as if no SQLAlchemy typing objects are present. There are
|
||||||
|
observed scenarios where Oracle Database may send incomplete or ambiguous
|
||||||
|
information about the numeric types being returned, such as a query where the
|
||||||
|
numeric types are buried under multiple levels of subquery. The type handlers
|
||||||
|
do their best to make the right decision in all cases, deferring to the
|
||||||
|
underlying python-oracledb DBAPI for all those cases where the driver can make
|
||||||
|
the best decision.
|
||||||
|
|
||||||
|
When no typing objects are present, as when executing plain SQL strings, a
|
||||||
|
default "outputtypehandler" is present which will generally return numeric
|
||||||
|
values which specify precision and scale as Python ``Decimal`` objects. To
|
||||||
|
disable this coercion to decimal for performance reasons, pass the flag
|
||||||
|
``coerce_to_decimal=False`` to :func:`_sa.create_engine`::
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"oracle+oracledb://scott:tiger@tnsalias", coerce_to_decimal=False
|
||||||
|
)
|
||||||
|
|
||||||
|
The ``coerce_to_decimal`` flag only impacts the results of plain string
|
||||||
|
SQL statements that are not otherwise associated with a :class:`.Numeric`
|
||||||
|
SQLAlchemy type (or a subclass of such).
|
||||||
|
|
||||||
|
.. versionchanged:: 1.2 The numeric handling system for the oracle dialects has
|
||||||
|
been reworked to take advantage of newer driver features as well as better
|
||||||
|
integration of outputtypehandlers.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0.0 added support for the python-oracledb driver.
|
||||||
|
|
||||||
|
""" # noqa
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from . import cx_oracle as _cx_oracle
|
||||||
|
from ... import exc
|
||||||
|
from ... import pool
|
||||||
|
from ...connectors.asyncio import AsyncAdapt_dbapi_connection
|
||||||
|
from ...connectors.asyncio import AsyncAdapt_dbapi_cursor
|
||||||
|
from ...connectors.asyncio import AsyncAdapt_dbapi_ss_cursor
|
||||||
|
from ...connectors.asyncio import AsyncAdaptFallback_dbapi_connection
|
||||||
|
from ...engine import default
|
||||||
|
from ...util import asbool
|
||||||
|
from ...util import await_fallback
|
||||||
|
from ...util import await_only
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from oracledb import AsyncConnection
|
||||||
|
from oracledb import AsyncCursor
|
||||||
|
|
||||||
|
|
||||||
|
class OracleExecutionContext_oracledb(
|
||||||
|
_cx_oracle.OracleExecutionContext_cx_oracle
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OracleDialect_oracledb(_cx_oracle.OracleDialect_cx_oracle):
|
||||||
|
supports_statement_cache = True
|
||||||
|
execution_ctx_cls = OracleExecutionContext_oracledb
|
||||||
|
|
||||||
|
driver = "oracledb"
|
||||||
|
_min_version = (1,)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
auto_convert_lobs=True,
|
||||||
|
coerce_to_decimal=True,
|
||||||
|
arraysize=None,
|
||||||
|
encoding_errors=None,
|
||||||
|
thick_mode=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
auto_convert_lobs,
|
||||||
|
coerce_to_decimal,
|
||||||
|
arraysize,
|
||||||
|
encoding_errors,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.dbapi is not None and (
|
||||||
|
thick_mode or isinstance(thick_mode, dict)
|
||||||
|
):
|
||||||
|
kw = thick_mode if isinstance(thick_mode, dict) else {}
|
||||||
|
self.dbapi.init_oracle_client(**kw)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_dbapi(cls):
|
||||||
|
import oracledb
|
||||||
|
|
||||||
|
return oracledb
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_thin_mode(cls, connection):
|
||||||
|
return connection.connection.dbapi_connection.thin
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_async_dialect_cls(cls, url):
|
||||||
|
return OracleDialectAsync_oracledb
|
||||||
|
|
||||||
|
def _load_version(self, dbapi_module):
|
||||||
|
version = (0, 0, 0)
|
||||||
|
if dbapi_module is not None:
|
||||||
|
m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", dbapi_module.version)
|
||||||
|
if m:
|
||||||
|
version = tuple(
|
||||||
|
int(x) for x in m.group(1, 2, 3) if x is not None
|
||||||
|
)
|
||||||
|
self.oracledb_ver = version
|
||||||
|
if (
|
||||||
|
self.oracledb_ver > (0, 0, 0)
|
||||||
|
and self.oracledb_ver < self._min_version
|
||||||
|
):
|
||||||
|
raise exc.InvalidRequestError(
|
||||||
|
f"oracledb version {self._min_version} and above are supported"
|
||||||
|
)
|
||||||
|
|
||||||
|
def do_begin_twophase(self, connection, xid):
|
||||||
|
conn_xis = connection.connection.xid(*xid)
|
||||||
|
connection.connection.tpc_begin(conn_xis)
|
||||||
|
connection.connection.info["oracledb_xid"] = conn_xis
|
||||||
|
|
||||||
|
def do_prepare_twophase(self, connection, xid):
|
||||||
|
should_commit = connection.connection.tpc_prepare()
|
||||||
|
connection.info["oracledb_should_commit"] = should_commit
|
||||||
|
|
||||||
|
def do_rollback_twophase(
|
||||||
|
self, connection, xid, is_prepared=True, recover=False
|
||||||
|
):
|
||||||
|
if recover:
|
||||||
|
conn_xid = connection.connection.xid(*xid)
|
||||||
|
else:
|
||||||
|
conn_xid = None
|
||||||
|
connection.connection.tpc_rollback(conn_xid)
|
||||||
|
|
||||||
|
def do_commit_twophase(
|
||||||
|
self, connection, xid, is_prepared=True, recover=False
|
||||||
|
):
|
||||||
|
conn_xid = None
|
||||||
|
if not is_prepared:
|
||||||
|
should_commit = connection.connection.tpc_prepare()
|
||||||
|
elif recover:
|
||||||
|
conn_xid = connection.connection.xid(*xid)
|
||||||
|
should_commit = True
|
||||||
|
else:
|
||||||
|
should_commit = connection.info["oracledb_should_commit"]
|
||||||
|
if should_commit:
|
||||||
|
connection.connection.tpc_commit(conn_xid)
|
||||||
|
|
||||||
|
def do_recover_twophase(self, connection):
|
||||||
|
return [
|
||||||
|
# oracledb seems to return bytes
|
||||||
|
(
|
||||||
|
fi,
|
||||||
|
gti.decode() if isinstance(gti, bytes) else gti,
|
||||||
|
bq.decode() if isinstance(bq, bytes) else bq,
|
||||||
|
)
|
||||||
|
for fi, gti, bq in connection.connection.tpc_recover()
|
||||||
|
]
|
||||||
|
|
||||||
|
def _check_max_identifier_length(self, connection):
|
||||||
|
if self.oracledb_ver >= (2, 5):
|
||||||
|
max_len = connection.connection.max_identifier_length
|
||||||
|
if max_len is not None:
|
||||||
|
return max_len
|
||||||
|
return super()._check_max_identifier_length(connection)
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncAdapt_oracledb_cursor(AsyncAdapt_dbapi_cursor):
|
||||||
|
_cursor: AsyncCursor
|
||||||
|
_awaitable_cursor_close: bool = False
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def outputtypehandler(self):
|
||||||
|
return self._cursor.outputtypehandler
|
||||||
|
|
||||||
|
@outputtypehandler.setter
|
||||||
|
def outputtypehandler(self, value):
|
||||||
|
self._cursor.outputtypehandler = value
|
||||||
|
|
||||||
|
def var(self, *args, **kwargs):
|
||||||
|
return self._cursor.var(*args, **kwargs)
|
||||||
|
|
||||||
|
def setinputsizes(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self._cursor.setinputsizes(*args, **kwargs)
|
||||||
|
|
||||||
|
def _aenter_cursor(self, cursor: AsyncCursor) -> AsyncCursor:
|
||||||
|
try:
|
||||||
|
return cursor.__enter__()
|
||||||
|
except Exception as error:
|
||||||
|
self._adapt_connection._handle_exception(error)
|
||||||
|
|
||||||
|
async def _execute_async(self, operation, parameters):
|
||||||
|
# override to not use mutex, oracledb already has a mutex
|
||||||
|
|
||||||
|
if parameters is None:
|
||||||
|
result = await self._cursor.execute(operation)
|
||||||
|
else:
|
||||||
|
result = await self._cursor.execute(operation, parameters)
|
||||||
|
|
||||||
|
if self._cursor.description and not self.server_side:
|
||||||
|
self._rows = collections.deque(await self._cursor.fetchall())
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def _executemany_async(
|
||||||
|
self,
|
||||||
|
operation,
|
||||||
|
seq_of_parameters,
|
||||||
|
):
|
||||||
|
# override to not use mutex, oracledb already has a mutex
|
||||||
|
return await self._cursor.executemany(operation, seq_of_parameters)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type_: Any, value: Any, traceback: Any) -> None:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncAdapt_oracledb_ss_cursor(
|
||||||
|
AsyncAdapt_dbapi_ss_cursor, AsyncAdapt_oracledb_cursor
|
||||||
|
):
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if self._cursor is not None:
|
||||||
|
self._cursor.close()
|
||||||
|
self._cursor = None # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncAdapt_oracledb_connection(AsyncAdapt_dbapi_connection):
|
||||||
|
_connection: AsyncConnection
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
thin = True
|
||||||
|
|
||||||
|
_cursor_cls = AsyncAdapt_oracledb_cursor
|
||||||
|
_ss_cursor_cls = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def autocommit(self):
|
||||||
|
return self._connection.autocommit
|
||||||
|
|
||||||
|
@autocommit.setter
|
||||||
|
def autocommit(self, value):
|
||||||
|
self._connection.autocommit = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def outputtypehandler(self):
|
||||||
|
return self._connection.outputtypehandler
|
||||||
|
|
||||||
|
@outputtypehandler.setter
|
||||||
|
def outputtypehandler(self, value):
|
||||||
|
self._connection.outputtypehandler = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self):
|
||||||
|
return self._connection.version
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stmtcachesize(self):
|
||||||
|
return self._connection.stmtcachesize
|
||||||
|
|
||||||
|
@stmtcachesize.setter
|
||||||
|
def stmtcachesize(self, value):
|
||||||
|
self._connection.stmtcachesize = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_identifier_length(self):
|
||||||
|
return self._connection.max_identifier_length
|
||||||
|
|
||||||
|
def cursor(self):
|
||||||
|
return AsyncAdapt_oracledb_cursor(self)
|
||||||
|
|
||||||
|
def ss_cursor(self):
|
||||||
|
return AsyncAdapt_oracledb_ss_cursor(self)
|
||||||
|
|
||||||
|
def xid(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self._connection.xid(*args, **kwargs)
|
||||||
|
|
||||||
|
def tpc_begin(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self.await_(self._connection.tpc_begin(*args, **kwargs))
|
||||||
|
|
||||||
|
def tpc_commit(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self.await_(self._connection.tpc_commit(*args, **kwargs))
|
||||||
|
|
||||||
|
def tpc_prepare(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self.await_(self._connection.tpc_prepare(*args, **kwargs))
|
||||||
|
|
||||||
|
def tpc_recover(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self.await_(self._connection.tpc_recover(*args, **kwargs))
|
||||||
|
|
||||||
|
def tpc_rollback(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return self.await_(self._connection.tpc_rollback(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncAdaptFallback_oracledb_connection(
|
||||||
|
AsyncAdaptFallback_dbapi_connection, AsyncAdapt_oracledb_connection
|
||||||
|
):
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
|
||||||
|
class OracledbAdaptDBAPI:
|
||||||
|
def __init__(self, oracledb) -> None:
|
||||||
|
self.oracledb = oracledb
|
||||||
|
|
||||||
|
for k, v in self.oracledb.__dict__.items():
|
||||||
|
if k != "connect":
|
||||||
|
self.__dict__[k] = v
|
||||||
|
|
||||||
|
def connect(self, *arg, **kw):
|
||||||
|
async_fallback = kw.pop("async_fallback", False)
|
||||||
|
creator_fn = kw.pop("async_creator_fn", self.oracledb.connect_async)
|
||||||
|
|
||||||
|
if asbool(async_fallback):
|
||||||
|
return AsyncAdaptFallback_oracledb_connection(
|
||||||
|
self, await_fallback(creator_fn(*arg, **kw))
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return AsyncAdapt_oracledb_connection(
|
||||||
|
self, await_only(creator_fn(*arg, **kw))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OracleExecutionContextAsync_oracledb(OracleExecutionContext_oracledb):
|
||||||
|
# restore default create cursor
|
||||||
|
create_cursor = default.DefaultExecutionContext.create_cursor
|
||||||
|
|
||||||
|
def create_default_cursor(self):
|
||||||
|
# copy of OracleExecutionContext_cx_oracle.create_cursor
|
||||||
|
c = self._dbapi_connection.cursor()
|
||||||
|
if self.dialect.arraysize:
|
||||||
|
c.arraysize = self.dialect.arraysize
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
def create_server_side_cursor(self):
|
||||||
|
c = self._dbapi_connection.ss_cursor()
|
||||||
|
if self.dialect.arraysize:
|
||||||
|
c.arraysize = self.dialect.arraysize
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
class OracleDialectAsync_oracledb(OracleDialect_oracledb):
|
||||||
|
is_async = True
|
||||||
|
supports_server_side_cursors = True
|
||||||
|
supports_statement_cache = True
|
||||||
|
execution_ctx_cls = OracleExecutionContextAsync_oracledb
|
||||||
|
|
||||||
|
_min_version = (2,)
|
||||||
|
|
||||||
|
# thick_mode mode is not supported by asyncio, oracledb will raise
|
||||||
|
@classmethod
|
||||||
|
def import_dbapi(cls):
|
||||||
|
import oracledb
|
||||||
|
|
||||||
|
return OracledbAdaptDBAPI(oracledb)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_pool_class(cls, url):
|
||||||
|
async_fallback = url.query.get("async_fallback", False)
|
||||||
|
|
||||||
|
if asbool(async_fallback):
|
||||||
|
return pool.FallbackAsyncAdaptedQueuePool
|
||||||
|
else:
|
||||||
|
return pool.AsyncAdaptedQueuePool
|
||||||
|
|
||||||
|
def get_driver_connection(self, connection):
|
||||||
|
return connection._connection
|
||||||
|
|
||||||
|
|
||||||
|
dialect = OracleDialect_oracledb
|
||||||
|
dialect_async = OracleDialectAsync_oracledb
|
||||||
Reference in New Issue
Block a user