SQLAlchemy 2.0.47__cp313-cp313t-win_amd64.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.
- sqlalchemy/__init__.py +283 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +184 -0
- sqlalchemy/connectors/asyncio.py +429 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/cyextension/__init__.py +6 -0
- sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/collections.pyx +409 -0
- sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/immutabledict.pxd +8 -0
- sqlalchemy/cyextension/immutabledict.pyx +133 -0
- sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/processors.pyx +68 -0
- sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/resultproxy.pyx +102 -0
- sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/util.pyx +90 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4093 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +129 -0
- sqlalchemy/dialects/mssql/provision.py +185 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +760 -0
- sqlalchemy/dialects/mysql/__init__.py +104 -0
- sqlalchemy/dialects/mysql/aiomysql.py +250 -0
- sqlalchemy/dialects/mysql/asyncmy.py +231 -0
- sqlalchemy/dialects/mysql/base.py +3949 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +225 -0
- sqlalchemy/dialects/mysql/enumerated.py +282 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +91 -0
- sqlalchemy/dialects/mysql/mariadb.py +72 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
- sqlalchemy/dialects/mysql/mysqldb.py +314 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +158 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +727 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +835 -0
- sqlalchemy/dialects/oracle/__init__.py +81 -0
- sqlalchemy/dialects/oracle/base.py +3802 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/oracledb.py +941 -0
- sqlalchemy/dialects/oracle/provision.py +297 -0
- sqlalchemy/dialects/oracle/types.py +316 -0
- sqlalchemy/dialects/oracle/vector.py +365 -0
- sqlalchemy/dialects/postgresql/__init__.py +167 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
- sqlalchemy/dialects/postgresql/array.py +519 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
- sqlalchemy/dialects/postgresql/base.py +5378 -0
- sqlalchemy/dialects/postgresql/dml.py +339 -0
- sqlalchemy/dialects/postgresql/ext.py +540 -0
- sqlalchemy/dialects/postgresql/hstore.py +406 -0
- sqlalchemy/dialects/postgresql/json.py +404 -0
- sqlalchemy/dialects/postgresql/named_types.py +524 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +669 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
- sqlalchemy/dialects/postgresql/provision.py +183 -0
- sqlalchemy/dialects/postgresql/psycopg.py +862 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1031 -0
- sqlalchemy/dialects/postgresql/types.py +313 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
- sqlalchemy/dialects/sqlite/base.py +3056 -0
- sqlalchemy/dialects/sqlite/dml.py +263 -0
- sqlalchemy/dialects/sqlite/json.py +92 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_py_processors.py +136 -0
- sqlalchemy/engine/_py_row.py +128 -0
- sqlalchemy/engine/_py_util.py +74 -0
- sqlalchemy/engine/base.py +3390 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +893 -0
- sqlalchemy/engine/cursor.py +2298 -0
- sqlalchemy/engine/default.py +2394 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3471 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +61 -0
- sqlalchemy/engine/reflection.py +2102 -0
- sqlalchemy/engine/result.py +2399 -0
- sqlalchemy/engine/row.py +400 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +924 -0
- sqlalchemy/engine/util.py +167 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +676 -0
- sqlalchemy/event/base.py +472 -0
- sqlalchemy/event/legacy.py +258 -0
- sqlalchemy/event/registry.py +390 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +832 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2027 -0
- sqlalchemy/ext/asyncio/__init__.py +25 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1471 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +965 -0
- sqlalchemy/ext/asyncio/scoping.py +1599 -0
- sqlalchemy/ext/asyncio/session.py +1947 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +570 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +564 -0
- sqlalchemy/ext/horizontal_shard.py +478 -0
- sqlalchemy/ext/hybrid.py +1535 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1085 -0
- sqlalchemy/ext/mypy/__init__.py +6 -0
- sqlalchemy/ext/mypy/apply.py +324 -0
- sqlalchemy/ext/mypy/decl_class.py +515 -0
- sqlalchemy/ext/mypy/infer.py +590 -0
- sqlalchemy/ext/mypy/names.py +335 -0
- sqlalchemy/ext/mypy/plugin.py +303 -0
- sqlalchemy/ext/mypy/util.py +357 -0
- sqlalchemy/ext/orderinglist.py +439 -0
- sqlalchemy/ext/serializer.py +185 -0
- sqlalchemy/future/__init__.py +16 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +174 -0
- sqlalchemy/log.py +288 -0
- sqlalchemy/orm/__init__.py +171 -0
- sqlalchemy/orm/_orm_constructors.py +2661 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2845 -0
- sqlalchemy/orm/base.py +971 -0
- sqlalchemy/orm/bulk_persistence.py +2135 -0
- sqlalchemy/orm/clsregistry.py +571 -0
- sqlalchemy/orm/collections.py +1627 -0
- sqlalchemy/orm/context.py +3334 -0
- sqlalchemy/orm/decl_api.py +2004 -0
- sqlalchemy/orm/decl_base.py +2192 -0
- sqlalchemy/orm/dependency.py +1302 -0
- sqlalchemy/orm/descriptor_props.py +1092 -0
- sqlalchemy/orm/dynamic.py +300 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3252 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +754 -0
- sqlalchemy/orm/interfaces.py +1496 -0
- sqlalchemy/orm/loading.py +1686 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4444 -0
- sqlalchemy/orm/path_registry.py +809 -0
- sqlalchemy/orm/persistence.py +1788 -0
- sqlalchemy/orm/properties.py +935 -0
- sqlalchemy/orm/query.py +3459 -0
- sqlalchemy/orm/relationships.py +3508 -0
- sqlalchemy/orm/scoping.py +2148 -0
- sqlalchemy/orm/session.py +5280 -0
- sqlalchemy/orm/state.py +1168 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3470 -0
- sqlalchemy/orm/strategy_options.py +2568 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +796 -0
- sqlalchemy/orm/util.py +2403 -0
- sqlalchemy/orm/writeonly.py +674 -0
- sqlalchemy/pool/__init__.py +44 -0
- sqlalchemy/pool/base.py +1524 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +588 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +69 -0
- sqlalchemy/sql/__init__.py +145 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +1872 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_py_util.py +75 -0
- sqlalchemy/sql/_selectable_constructors.py +763 -0
- sqlalchemy/sql/_typing.py +482 -0
- sqlalchemy/sql/annotation.py +587 -0
- sqlalchemy/sql/base.py +2293 -0
- sqlalchemy/sql/cache_key.py +1057 -0
- sqlalchemy/sql/coercions.py +1404 -0
- sqlalchemy/sql/compiler.py +8081 -0
- sqlalchemy/sql/crud.py +1752 -0
- sqlalchemy/sql/ddl.py +1444 -0
- sqlalchemy/sql/default_comparator.py +551 -0
- sqlalchemy/sql/dml.py +1850 -0
- sqlalchemy/sql/elements.py +5589 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +159 -0
- sqlalchemy/sql/functions.py +2158 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2623 -0
- sqlalchemy/sql/roles.py +323 -0
- sqlalchemy/sql/schema.py +6222 -0
- sqlalchemy/sql/selectable.py +7265 -0
- sqlalchemy/sql/sqltypes.py +3930 -0
- sqlalchemy/sql/traversals.py +1024 -0
- sqlalchemy/sql/type_api.py +2368 -0
- sqlalchemy/sql/util.py +1485 -0
- sqlalchemy/sql/visitors.py +1164 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +994 -0
- sqlalchemy/testing/assertsql.py +520 -0
- sqlalchemy/testing/asyncio.py +135 -0
- sqlalchemy/testing/config.py +434 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +28 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +332 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +482 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +51 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +892 -0
- sqlalchemy/testing/profiling.py +329 -0
- sqlalchemy/testing/provision.py +603 -0
- sqlalchemy/testing/requirements.py +1945 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +389 -0
- sqlalchemy/testing/suite/test_deprecations.py +153 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3557 -0
- sqlalchemy/testing/suite/test_results.py +504 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2010 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_types.py +2147 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +535 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +74 -0
- sqlalchemy/util/__init__.py +162 -0
- sqlalchemy/util/_collections.py +712 -0
- sqlalchemy/util/_concurrency_py3k.py +288 -0
- sqlalchemy/util/_has_cy.py +40 -0
- sqlalchemy/util/_py_collections.py +541 -0
- sqlalchemy/util/compat.py +421 -0
- sqlalchemy/util/concurrency.py +110 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2203 -0
- sqlalchemy/util/preloaded.py +150 -0
- sqlalchemy/util/queue.py +322 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +734 -0
- sqlalchemy-2.0.47.dist-info/METADATA +243 -0
- sqlalchemy-2.0.47.dist-info/RECORD +274 -0
- sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
- sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# dialects/oracle/provision.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: ignore-errors
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from ... import create_engine
|
|
12
|
+
from ... import exc
|
|
13
|
+
from ... import inspect
|
|
14
|
+
from ...engine import url as sa_url
|
|
15
|
+
from ...testing.provision import configure_follower
|
|
16
|
+
from ...testing.provision import create_db
|
|
17
|
+
from ...testing.provision import drop_all_schema_objects_post_tables
|
|
18
|
+
from ...testing.provision import drop_all_schema_objects_pre_tables
|
|
19
|
+
from ...testing.provision import drop_db
|
|
20
|
+
from ...testing.provision import follower_url_from_main
|
|
21
|
+
from ...testing.provision import generate_driver_url
|
|
22
|
+
from ...testing.provision import is_preferred_driver
|
|
23
|
+
from ...testing.provision import log
|
|
24
|
+
from ...testing.provision import post_configure_engine
|
|
25
|
+
from ...testing.provision import post_configure_testing_engine
|
|
26
|
+
from ...testing.provision import run_reap_dbs
|
|
27
|
+
from ...testing.provision import set_default_schema_on_connection
|
|
28
|
+
from ...testing.provision import stop_test_class_outside_fixtures
|
|
29
|
+
from ...testing.provision import temp_table_keyword_args
|
|
30
|
+
from ...testing.provision import update_db_opts
|
|
31
|
+
from ...testing.warnings import warn_test_suite
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@generate_driver_url.for_db("oracle")
|
|
35
|
+
def _oracle_generate_driver_url(url, driver, query_str):
|
|
36
|
+
|
|
37
|
+
backend = url.get_backend_name()
|
|
38
|
+
|
|
39
|
+
new_url = url.set(
|
|
40
|
+
drivername="%s+%s" % (backend, driver),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# use oracledb's retry feature, which is essential for oracle 23c
|
|
44
|
+
# which otherwise frequently rejects connections under load
|
|
45
|
+
# for cx_oracle we have a connect event instead
|
|
46
|
+
if driver in ("oracledb", "oracledb_async"):
|
|
47
|
+
# oracledb is even nice enough to convert from string to int
|
|
48
|
+
# for these opts, apparently
|
|
49
|
+
new_url = new_url.update_query_pairs(
|
|
50
|
+
[("retry_count", "5"), ("retry_delay", "2")]
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
# remove these params for cx_oracle if we received an
|
|
54
|
+
# already-modified URL
|
|
55
|
+
new_url = new_url.difference_update_query(
|
|
56
|
+
["retry_count", "retry_delay"]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
new_url.get_dialect()
|
|
61
|
+
except exc.NoSuchModuleError:
|
|
62
|
+
return None
|
|
63
|
+
else:
|
|
64
|
+
return new_url
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@create_db.for_db("oracle")
|
|
68
|
+
def _oracle_create_db(cfg, eng, ident):
|
|
69
|
+
# NOTE: make sure you've run "ALTER DATABASE default tablespace users" or
|
|
70
|
+
# similar, so that the default tablespace is not "system"; reflection will
|
|
71
|
+
# fail otherwise
|
|
72
|
+
with eng.begin() as conn:
|
|
73
|
+
conn.exec_driver_sql("create user %s identified by xe" % ident)
|
|
74
|
+
conn.exec_driver_sql("create user %s_ts1 identified by xe" % ident)
|
|
75
|
+
conn.exec_driver_sql("create user %s_ts2 identified by xe" % ident)
|
|
76
|
+
conn.exec_driver_sql("grant dba to %s" % (ident,))
|
|
77
|
+
conn.exec_driver_sql("grant unlimited tablespace to %s" % ident)
|
|
78
|
+
conn.exec_driver_sql("grant unlimited tablespace to %s_ts1" % ident)
|
|
79
|
+
conn.exec_driver_sql("grant unlimited tablespace to %s_ts2" % ident)
|
|
80
|
+
# these are needed to create materialized views
|
|
81
|
+
conn.exec_driver_sql("grant create table to %s" % ident)
|
|
82
|
+
conn.exec_driver_sql("grant create table to %s_ts1" % ident)
|
|
83
|
+
conn.exec_driver_sql("grant create table to %s_ts2" % ident)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@configure_follower.for_db("oracle")
|
|
87
|
+
def _oracle_configure_follower(config, ident):
|
|
88
|
+
config.test_schema = "%s_ts1" % ident
|
|
89
|
+
config.test_schema_2 = "%s_ts2" % ident
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _ora_drop_ignore(conn, dbname):
|
|
93
|
+
try:
|
|
94
|
+
conn.exec_driver_sql("drop user %s cascade" % dbname)
|
|
95
|
+
log.info("Reaped db: %s", dbname)
|
|
96
|
+
return True
|
|
97
|
+
except exc.DatabaseError as err:
|
|
98
|
+
log.warning("couldn't drop db: %s", err)
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@drop_all_schema_objects_pre_tables.for_db("oracle")
|
|
103
|
+
def _ora_drop_all_schema_objects_pre_tables(cfg, eng):
|
|
104
|
+
_purge_recyclebin(eng)
|
|
105
|
+
_purge_recyclebin(eng, cfg.test_schema)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@drop_all_schema_objects_post_tables.for_db("oracle")
|
|
109
|
+
def _ora_drop_all_schema_objects_post_tables(cfg, eng):
|
|
110
|
+
with eng.begin() as conn:
|
|
111
|
+
for syn in conn.dialect._get_synonyms(conn, None, None, None):
|
|
112
|
+
conn.exec_driver_sql(f"drop synonym {syn['synonym_name']}")
|
|
113
|
+
|
|
114
|
+
for syn in conn.dialect._get_synonyms(
|
|
115
|
+
conn, cfg.test_schema, None, None
|
|
116
|
+
):
|
|
117
|
+
conn.exec_driver_sql(
|
|
118
|
+
f"drop synonym {cfg.test_schema}.{syn['synonym_name']}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
for tmp_table in inspect(conn).get_temp_table_names():
|
|
122
|
+
conn.exec_driver_sql(f"drop table {tmp_table}")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@drop_db.for_db("oracle")
|
|
126
|
+
def _oracle_drop_db(cfg, eng, ident):
|
|
127
|
+
with eng.begin() as conn:
|
|
128
|
+
# cx_Oracle seems to occasionally leak open connections when a large
|
|
129
|
+
# suite it run, even if we confirm we have zero references to
|
|
130
|
+
# connection objects.
|
|
131
|
+
# while there is a "kill session" command in Oracle Database,
|
|
132
|
+
# it unfortunately does not release the connection sufficiently.
|
|
133
|
+
_ora_drop_ignore(conn, ident)
|
|
134
|
+
_ora_drop_ignore(conn, "%s_ts1" % ident)
|
|
135
|
+
_ora_drop_ignore(conn, "%s_ts2" % ident)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@stop_test_class_outside_fixtures.for_db("oracle")
|
|
139
|
+
def _ora_stop_test_class_outside_fixtures(config, db, cls):
|
|
140
|
+
try:
|
|
141
|
+
_purge_recyclebin(db)
|
|
142
|
+
except exc.DatabaseError as err:
|
|
143
|
+
log.warning("purge recyclebin command failed: %s", err)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _purge_recyclebin(eng, schema=None):
|
|
147
|
+
with eng.begin() as conn:
|
|
148
|
+
if schema is None:
|
|
149
|
+
# run magic command to get rid of identity sequences
|
|
150
|
+
# https://floo.bar/2019/11/29/drop-the-underlying-sequence-of-an-identity-column/ # noqa: E501
|
|
151
|
+
conn.exec_driver_sql("purge recyclebin")
|
|
152
|
+
else:
|
|
153
|
+
# per user: https://community.oracle.com/tech/developers/discussion/2255402/how-to-clear-dba-recyclebin-for-a-particular-user # noqa: E501
|
|
154
|
+
for owner, object_name, type_ in conn.exec_driver_sql(
|
|
155
|
+
"select owner, object_name,type from "
|
|
156
|
+
"dba_recyclebin where owner=:schema and type='TABLE'",
|
|
157
|
+
{"schema": conn.dialect.denormalize_name(schema)},
|
|
158
|
+
).all():
|
|
159
|
+
conn.exec_driver_sql(f'purge {type_} {owner}."{object_name}"')
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@is_preferred_driver.for_db("oracle")
|
|
163
|
+
def _oracle_is_preferred_driver(cfg, engine):
|
|
164
|
+
"""establish oracledb as the preferred driver to use for tests, even
|
|
165
|
+
though cx_Oracle is still the "default" driver"""
|
|
166
|
+
|
|
167
|
+
return engine.dialect.driver == "oracledb" and not engine.dialect.is_async
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _connect_with_retry(dialect, conn_rec, cargs, cparams):
|
|
171
|
+
assert dialect.driver == "cx_oracle"
|
|
172
|
+
|
|
173
|
+
def _is_couldnt_connect(err):
|
|
174
|
+
return "DPY-6005" in str(err) or "ORA-12516" in str(err)
|
|
175
|
+
|
|
176
|
+
err_ = None
|
|
177
|
+
for _ in range(5):
|
|
178
|
+
try:
|
|
179
|
+
return dialect.loaded_dbapi.connect(*cargs, **cparams)
|
|
180
|
+
except (
|
|
181
|
+
dialect.loaded_dbapi.DatabaseError,
|
|
182
|
+
dialect.loaded_dbapi.OperationalError,
|
|
183
|
+
) as err:
|
|
184
|
+
err_ = err
|
|
185
|
+
if _is_couldnt_connect(err):
|
|
186
|
+
warn_test_suite("Oracle database reconnecting...")
|
|
187
|
+
time.sleep(2)
|
|
188
|
+
continue
|
|
189
|
+
else:
|
|
190
|
+
raise
|
|
191
|
+
if err_ is not None:
|
|
192
|
+
raise Exception("connect failed after five attempts") from err_
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@post_configure_testing_engine.for_db("oracle")
|
|
196
|
+
def _oracle_post_configure_testing_engine(url, engine, options, scope):
|
|
197
|
+
from ... import event
|
|
198
|
+
|
|
199
|
+
if engine.dialect.driver == "cx_oracle":
|
|
200
|
+
event.listen(engine, "do_connect", _connect_with_retry)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@post_configure_engine.for_db("oracle")
|
|
204
|
+
def _oracle_post_configure_engine(url, engine, follower_ident):
|
|
205
|
+
|
|
206
|
+
from ... import event
|
|
207
|
+
|
|
208
|
+
@event.listens_for(engine, "checkin")
|
|
209
|
+
def checkin(dbapi_connection, connection_record):
|
|
210
|
+
# this was meant to work around this issue:
|
|
211
|
+
# https://github.com/oracle/python-cx_Oracle/issues/530
|
|
212
|
+
# invalidate oracle connections that had 2pc set up
|
|
213
|
+
# however things are too complex with some of the 2pc tests,
|
|
214
|
+
# so just block cx_oracle from being used in 2pc tests (use oracledb
|
|
215
|
+
# instead)
|
|
216
|
+
# if "cx_oracle_xid" in connection_record.info:
|
|
217
|
+
# connection_record.invalidate()
|
|
218
|
+
|
|
219
|
+
# clear statement cache on all connections that were used
|
|
220
|
+
# https://github.com/oracle/python-cx_Oracle/issues/519
|
|
221
|
+
# TODO: oracledb claims to have this feature built in somehow,
|
|
222
|
+
# see if that's in use and/or if it needs to be enabled
|
|
223
|
+
# (or if this doesn't even apply to the newer oracle's we're using)
|
|
224
|
+
try:
|
|
225
|
+
sc = dbapi_connection.stmtcachesize
|
|
226
|
+
except:
|
|
227
|
+
# connection closed
|
|
228
|
+
pass
|
|
229
|
+
else:
|
|
230
|
+
dbapi_connection.stmtcachesize = 0
|
|
231
|
+
dbapi_connection.stmtcachesize = sc
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@run_reap_dbs.for_db("oracle")
|
|
235
|
+
def _reap_oracle_dbs(url, idents):
|
|
236
|
+
log.info("db reaper connecting to %r", url)
|
|
237
|
+
eng = create_engine(url)
|
|
238
|
+
with eng.begin() as conn:
|
|
239
|
+
log.info("identifiers in file: %s", ", ".join(idents))
|
|
240
|
+
|
|
241
|
+
to_reap = conn.exec_driver_sql(
|
|
242
|
+
"select u.username from all_users u where username "
|
|
243
|
+
"like 'TEST_%' and not exists (select username "
|
|
244
|
+
"from v$session where username=u.username)"
|
|
245
|
+
)
|
|
246
|
+
all_names = {username.lower() for (username,) in to_reap}
|
|
247
|
+
to_drop = set()
|
|
248
|
+
for name in all_names:
|
|
249
|
+
if name.endswith("_ts1") or name.endswith("_ts2"):
|
|
250
|
+
continue
|
|
251
|
+
elif name in idents:
|
|
252
|
+
to_drop.add(name)
|
|
253
|
+
if "%s_ts1" % name in all_names:
|
|
254
|
+
to_drop.add("%s_ts1" % name)
|
|
255
|
+
if "%s_ts2" % name in all_names:
|
|
256
|
+
to_drop.add("%s_ts2" % name)
|
|
257
|
+
|
|
258
|
+
dropped = total = 0
|
|
259
|
+
for total, username in enumerate(to_drop, 1):
|
|
260
|
+
if _ora_drop_ignore(conn, username):
|
|
261
|
+
dropped += 1
|
|
262
|
+
log.info(
|
|
263
|
+
"Dropped %d out of %d stale databases detected", dropped, total
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@follower_url_from_main.for_db("oracle")
|
|
268
|
+
def _oracle_follower_url_from_main(url, ident):
|
|
269
|
+
url = sa_url.make_url(url)
|
|
270
|
+
return url.set(username=ident, password="xe")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@temp_table_keyword_args.for_db("oracle")
|
|
274
|
+
def _oracle_temp_table_keyword_args(cfg, eng):
|
|
275
|
+
return {
|
|
276
|
+
"prefixes": ["GLOBAL TEMPORARY"],
|
|
277
|
+
"oracle_on_commit": "PRESERVE ROWS",
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@set_default_schema_on_connection.for_db("oracle")
|
|
282
|
+
def _oracle_set_default_schema_on_connection(
|
|
283
|
+
cfg, dbapi_connection, schema_name
|
|
284
|
+
):
|
|
285
|
+
cursor = dbapi_connection.cursor()
|
|
286
|
+
cursor.execute("ALTER SESSION SET CURRENT_SCHEMA=%s" % schema_name)
|
|
287
|
+
cursor.close()
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@update_db_opts.for_db("oracle")
|
|
291
|
+
def _update_db_opts(db_url, db_opts, options):
|
|
292
|
+
"""Set database options (db_opts) for a test database that we created."""
|
|
293
|
+
if (
|
|
294
|
+
options.oracledb_thick_mode
|
|
295
|
+
and sa_url.make_url(db_url).get_driver_name() == "oracledb"
|
|
296
|
+
):
|
|
297
|
+
db_opts["thick_mode"] = True
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# dialects/oracle/types.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: ignore-errors
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import datetime as dt
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from typing import Type
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from ... import exc
|
|
16
|
+
from ...sql import sqltypes
|
|
17
|
+
from ...types import NVARCHAR
|
|
18
|
+
from ...types import VARCHAR
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from ...engine.interfaces import Dialect
|
|
22
|
+
from ...sql.type_api import _LiteralProcessorType
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RAW(sqltypes._Binary):
|
|
26
|
+
__visit_name__ = "RAW"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
OracleRaw = RAW
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class NCLOB(sqltypes.Text):
|
|
33
|
+
__visit_name__ = "NCLOB"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class VARCHAR2(VARCHAR):
|
|
37
|
+
__visit_name__ = "VARCHAR2"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
NVARCHAR2 = NVARCHAR
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class NUMBER(sqltypes.Numeric, sqltypes.Integer):
|
|
44
|
+
__visit_name__ = "NUMBER"
|
|
45
|
+
|
|
46
|
+
def __init__(self, precision=None, scale=None, asdecimal=None):
|
|
47
|
+
if asdecimal is None:
|
|
48
|
+
asdecimal = bool(scale and scale > 0)
|
|
49
|
+
|
|
50
|
+
super().__init__(precision=precision, scale=scale, asdecimal=asdecimal)
|
|
51
|
+
|
|
52
|
+
def adapt(self, impltype):
|
|
53
|
+
ret = super().adapt(impltype)
|
|
54
|
+
# leave a hint for the DBAPI handler
|
|
55
|
+
ret._is_oracle_number = True
|
|
56
|
+
return ret
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def _type_affinity(self):
|
|
60
|
+
if bool(self.scale and self.scale > 0):
|
|
61
|
+
return sqltypes.Numeric
|
|
62
|
+
else:
|
|
63
|
+
return sqltypes.Integer
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FLOAT(sqltypes.FLOAT):
|
|
67
|
+
"""Oracle Database FLOAT.
|
|
68
|
+
|
|
69
|
+
This is the same as :class:`_sqltypes.FLOAT` except that
|
|
70
|
+
an Oracle Database -specific :paramref:`_oracle.FLOAT.binary_precision`
|
|
71
|
+
parameter is accepted, and
|
|
72
|
+
the :paramref:`_sqltypes.Float.precision` parameter is not accepted.
|
|
73
|
+
|
|
74
|
+
Oracle Database FLOAT types indicate precision in terms of "binary
|
|
75
|
+
precision", which defaults to 126. For a REAL type, the value is 63. This
|
|
76
|
+
parameter does not cleanly map to a specific number of decimal places but
|
|
77
|
+
is roughly equivalent to the desired number of decimal places divided by
|
|
78
|
+
0.3103.
|
|
79
|
+
|
|
80
|
+
.. versionadded:: 2.0
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
__visit_name__ = "FLOAT"
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
binary_precision=None,
|
|
89
|
+
asdecimal=False,
|
|
90
|
+
decimal_return_scale=None,
|
|
91
|
+
):
|
|
92
|
+
r"""
|
|
93
|
+
Construct a FLOAT
|
|
94
|
+
|
|
95
|
+
:param binary_precision: Oracle Database binary precision value to be
|
|
96
|
+
rendered in DDL. This may be approximated to the number of decimal
|
|
97
|
+
characters using the formula "decimal precision = 0.30103 * binary
|
|
98
|
+
precision". The default value used by Oracle Database for FLOAT /
|
|
99
|
+
DOUBLE PRECISION is 126.
|
|
100
|
+
|
|
101
|
+
:param asdecimal: See :paramref:`_sqltypes.Float.asdecimal`
|
|
102
|
+
|
|
103
|
+
:param decimal_return_scale: See
|
|
104
|
+
:paramref:`_sqltypes.Float.decimal_return_scale`
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
super().__init__(
|
|
108
|
+
asdecimal=asdecimal, decimal_return_scale=decimal_return_scale
|
|
109
|
+
)
|
|
110
|
+
self.binary_precision = binary_precision
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BINARY_DOUBLE(sqltypes.Double):
|
|
114
|
+
"""Implement the Oracle ``BINARY_DOUBLE`` datatype.
|
|
115
|
+
|
|
116
|
+
This datatype differs from the Oracle ``DOUBLE`` datatype in that it
|
|
117
|
+
delivers a true 8-byte FP value. The datatype may be combined with a
|
|
118
|
+
generic :class:`.Double` datatype using :meth:`.TypeEngine.with_variant`.
|
|
119
|
+
|
|
120
|
+
.. seealso::
|
|
121
|
+
|
|
122
|
+
:ref:`oracle_float_support`
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
__visit_name__ = "BINARY_DOUBLE"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class BINARY_FLOAT(sqltypes.Float):
|
|
131
|
+
"""Implement the Oracle ``BINARY_FLOAT`` datatype.
|
|
132
|
+
|
|
133
|
+
This datatype differs from the Oracle ``FLOAT`` datatype in that it
|
|
134
|
+
delivers a true 4-byte FP value. The datatype may be combined with a
|
|
135
|
+
generic :class:`.Float` datatype using :meth:`.TypeEngine.with_variant`.
|
|
136
|
+
|
|
137
|
+
.. seealso::
|
|
138
|
+
|
|
139
|
+
:ref:`oracle_float_support`
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
__visit_name__ = "BINARY_FLOAT"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class BFILE(sqltypes.LargeBinary):
|
|
148
|
+
__visit_name__ = "BFILE"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class LONG(sqltypes.Text):
|
|
152
|
+
__visit_name__ = "LONG"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class _OracleDateLiteralRender:
|
|
156
|
+
def _literal_processor_datetime(self, dialect):
|
|
157
|
+
def process(value):
|
|
158
|
+
if getattr(value, "microsecond", None):
|
|
159
|
+
value = (
|
|
160
|
+
f"""TO_TIMESTAMP"""
|
|
161
|
+
f"""('{value.isoformat().replace("T", " ")}', """
|
|
162
|
+
"""'YYYY-MM-DD HH24:MI:SS.FF')"""
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
value = (
|
|
166
|
+
f"""TO_DATE"""
|
|
167
|
+
f"""('{value.isoformat().replace("T", " ")}', """
|
|
168
|
+
"""'YYYY-MM-DD HH24:MI:SS')"""
|
|
169
|
+
)
|
|
170
|
+
return value
|
|
171
|
+
|
|
172
|
+
return process
|
|
173
|
+
|
|
174
|
+
def _literal_processor_date(self, dialect):
|
|
175
|
+
def process(value):
|
|
176
|
+
if getattr(value, "microsecond", None):
|
|
177
|
+
value = (
|
|
178
|
+
f"""TO_TIMESTAMP"""
|
|
179
|
+
f"""('{value.isoformat().split("T")[0]}', """
|
|
180
|
+
"""'YYYY-MM-DD')"""
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
value = (
|
|
184
|
+
f"""TO_DATE"""
|
|
185
|
+
f"""('{value.isoformat().split("T")[0]}', """
|
|
186
|
+
"""'YYYY-MM-DD')"""
|
|
187
|
+
)
|
|
188
|
+
return value
|
|
189
|
+
|
|
190
|
+
return process
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class DATE(_OracleDateLiteralRender, sqltypes.DateTime):
|
|
194
|
+
"""Provide the Oracle Database DATE type.
|
|
195
|
+
|
|
196
|
+
This type has no special Python behavior, except that it subclasses
|
|
197
|
+
:class:`_types.DateTime`; this is to suit the fact that the Oracle Database
|
|
198
|
+
``DATE`` type supports a time value.
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
__visit_name__ = "DATE"
|
|
203
|
+
|
|
204
|
+
def literal_processor(self, dialect):
|
|
205
|
+
return self._literal_processor_datetime(dialect)
|
|
206
|
+
|
|
207
|
+
def _compare_type_affinity(self, other):
|
|
208
|
+
return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class _OracleDate(_OracleDateLiteralRender, sqltypes.Date):
|
|
212
|
+
def literal_processor(self, dialect):
|
|
213
|
+
return self._literal_processor_date(dialect)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval):
|
|
217
|
+
__visit_name__ = "INTERVAL"
|
|
218
|
+
|
|
219
|
+
def __init__(self, day_precision=None, second_precision=None):
|
|
220
|
+
"""Construct an INTERVAL.
|
|
221
|
+
|
|
222
|
+
Note that only DAY TO SECOND intervals are currently supported.
|
|
223
|
+
This is due to a lack of support for YEAR TO MONTH intervals
|
|
224
|
+
within available DBAPIs.
|
|
225
|
+
|
|
226
|
+
:param day_precision: the day precision value. this is the number of
|
|
227
|
+
digits to store for the day field. Defaults to "2"
|
|
228
|
+
:param second_precision: the second precision value. this is the
|
|
229
|
+
number of digits to store for the fractional seconds field.
|
|
230
|
+
Defaults to "6".
|
|
231
|
+
|
|
232
|
+
"""
|
|
233
|
+
self.day_precision = day_precision
|
|
234
|
+
self.second_precision = second_precision
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def _adapt_from_generic_interval(cls, interval):
|
|
238
|
+
return INTERVAL(
|
|
239
|
+
day_precision=interval.day_precision,
|
|
240
|
+
second_precision=interval.second_precision,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
@classmethod
|
|
244
|
+
def adapt_emulated_to_native(
|
|
245
|
+
cls, interval: sqltypes.Interval, **kw # type: ignore[override]
|
|
246
|
+
):
|
|
247
|
+
return INTERVAL(
|
|
248
|
+
day_precision=interval.day_precision,
|
|
249
|
+
second_precision=interval.second_precision,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def _type_affinity(self):
|
|
254
|
+
return sqltypes.Interval
|
|
255
|
+
|
|
256
|
+
def as_generic(self, allow_nulltype=False):
|
|
257
|
+
return sqltypes.Interval(
|
|
258
|
+
native=True,
|
|
259
|
+
second_precision=self.second_precision,
|
|
260
|
+
day_precision=self.day_precision,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def python_type(self) -> Type[dt.timedelta]:
|
|
265
|
+
return dt.timedelta
|
|
266
|
+
|
|
267
|
+
def literal_processor(
|
|
268
|
+
self, dialect: Dialect
|
|
269
|
+
) -> Optional[_LiteralProcessorType[dt.timedelta]]:
|
|
270
|
+
def process(value: dt.timedelta) -> str:
|
|
271
|
+
return f"NUMTODSINTERVAL({value.total_seconds()}, 'SECOND')"
|
|
272
|
+
|
|
273
|
+
return process
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class TIMESTAMP(sqltypes.TIMESTAMP):
|
|
277
|
+
"""Oracle Database implementation of ``TIMESTAMP``, which supports
|
|
278
|
+
additional Oracle Database-specific modes
|
|
279
|
+
|
|
280
|
+
.. versionadded:: 2.0
|
|
281
|
+
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
def __init__(self, timezone: bool = False, local_timezone: bool = False):
|
|
285
|
+
"""Construct a new :class:`_oracle.TIMESTAMP`.
|
|
286
|
+
|
|
287
|
+
:param timezone: boolean. Indicates that the TIMESTAMP type should
|
|
288
|
+
use Oracle Database's ``TIMESTAMP WITH TIME ZONE`` datatype.
|
|
289
|
+
|
|
290
|
+
:param local_timezone: boolean. Indicates that the TIMESTAMP type
|
|
291
|
+
should use Oracle Database's ``TIMESTAMP WITH LOCAL TIME ZONE``
|
|
292
|
+
datatype.
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
"""
|
|
296
|
+
if timezone and local_timezone:
|
|
297
|
+
raise exc.ArgumentError(
|
|
298
|
+
"timezone and local_timezone are mutually exclusive"
|
|
299
|
+
)
|
|
300
|
+
super().__init__(timezone=timezone)
|
|
301
|
+
self.local_timezone = local_timezone
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class ROWID(sqltypes.TypeEngine):
|
|
305
|
+
"""Oracle Database ROWID type.
|
|
306
|
+
|
|
307
|
+
When used in a cast() or similar, generates ROWID.
|
|
308
|
+
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
__visit_name__ = "ROWID"
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class _OracleBoolean(sqltypes.Boolean):
|
|
315
|
+
def get_dbapi_type(self, dbapi):
|
|
316
|
+
return dbapi.NUMBER
|