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.
Files changed (274) hide show
  1. sqlalchemy/__init__.py +283 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +184 -0
  4. sqlalchemy/connectors/asyncio.py +429 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/cyextension/__init__.py +6 -0
  7. sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
  17. sqlalchemy/cyextension/util.pyx +90 -0
  18. sqlalchemy/dialects/__init__.py +62 -0
  19. sqlalchemy/dialects/_typing.py +30 -0
  20. sqlalchemy/dialects/mssql/__init__.py +88 -0
  21. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  22. sqlalchemy/dialects/mssql/base.py +4093 -0
  23. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  24. sqlalchemy/dialects/mssql/json.py +129 -0
  25. sqlalchemy/dialects/mssql/provision.py +185 -0
  26. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  27. sqlalchemy/dialects/mssql/pyodbc.py +760 -0
  28. sqlalchemy/dialects/mysql/__init__.py +104 -0
  29. sqlalchemy/dialects/mysql/aiomysql.py +250 -0
  30. sqlalchemy/dialects/mysql/asyncmy.py +231 -0
  31. sqlalchemy/dialects/mysql/base.py +3949 -0
  32. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  33. sqlalchemy/dialects/mysql/dml.py +225 -0
  34. sqlalchemy/dialects/mysql/enumerated.py +282 -0
  35. sqlalchemy/dialects/mysql/expression.py +146 -0
  36. sqlalchemy/dialects/mysql/json.py +91 -0
  37. sqlalchemy/dialects/mysql/mariadb.py +72 -0
  38. sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
  39. sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
  40. sqlalchemy/dialects/mysql/mysqldb.py +314 -0
  41. sqlalchemy/dialects/mysql/provision.py +153 -0
  42. sqlalchemy/dialects/mysql/pymysql.py +158 -0
  43. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  44. sqlalchemy/dialects/mysql/reflection.py +727 -0
  45. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  46. sqlalchemy/dialects/mysql/types.py +835 -0
  47. sqlalchemy/dialects/oracle/__init__.py +81 -0
  48. sqlalchemy/dialects/oracle/base.py +3802 -0
  49. sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
  50. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  51. sqlalchemy/dialects/oracle/oracledb.py +941 -0
  52. sqlalchemy/dialects/oracle/provision.py +297 -0
  53. sqlalchemy/dialects/oracle/types.py +316 -0
  54. sqlalchemy/dialects/oracle/vector.py +365 -0
  55. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  56. sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
  57. sqlalchemy/dialects/postgresql/array.py +519 -0
  58. sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
  59. sqlalchemy/dialects/postgresql/base.py +5378 -0
  60. sqlalchemy/dialects/postgresql/dml.py +339 -0
  61. sqlalchemy/dialects/postgresql/ext.py +540 -0
  62. sqlalchemy/dialects/postgresql/hstore.py +406 -0
  63. sqlalchemy/dialects/postgresql/json.py +404 -0
  64. sqlalchemy/dialects/postgresql/named_types.py +524 -0
  65. sqlalchemy/dialects/postgresql/operators.py +129 -0
  66. sqlalchemy/dialects/postgresql/pg8000.py +669 -0
  67. sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
  68. sqlalchemy/dialects/postgresql/provision.py +183 -0
  69. sqlalchemy/dialects/postgresql/psycopg.py +862 -0
  70. sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
  71. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  72. sqlalchemy/dialects/postgresql/ranges.py +1031 -0
  73. sqlalchemy/dialects/postgresql/types.py +313 -0
  74. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  75. sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
  76. sqlalchemy/dialects/sqlite/base.py +3056 -0
  77. sqlalchemy/dialects/sqlite/dml.py +263 -0
  78. sqlalchemy/dialects/sqlite/json.py +92 -0
  79. sqlalchemy/dialects/sqlite/provision.py +229 -0
  80. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  81. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  82. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  83. sqlalchemy/engine/__init__.py +62 -0
  84. sqlalchemy/engine/_py_processors.py +136 -0
  85. sqlalchemy/engine/_py_row.py +128 -0
  86. sqlalchemy/engine/_py_util.py +74 -0
  87. sqlalchemy/engine/base.py +3390 -0
  88. sqlalchemy/engine/characteristics.py +155 -0
  89. sqlalchemy/engine/create.py +893 -0
  90. sqlalchemy/engine/cursor.py +2298 -0
  91. sqlalchemy/engine/default.py +2394 -0
  92. sqlalchemy/engine/events.py +965 -0
  93. sqlalchemy/engine/interfaces.py +3471 -0
  94. sqlalchemy/engine/mock.py +134 -0
  95. sqlalchemy/engine/processors.py +61 -0
  96. sqlalchemy/engine/reflection.py +2102 -0
  97. sqlalchemy/engine/result.py +2399 -0
  98. sqlalchemy/engine/row.py +400 -0
  99. sqlalchemy/engine/strategies.py +16 -0
  100. sqlalchemy/engine/url.py +924 -0
  101. sqlalchemy/engine/util.py +167 -0
  102. sqlalchemy/event/__init__.py +26 -0
  103. sqlalchemy/event/api.py +220 -0
  104. sqlalchemy/event/attr.py +676 -0
  105. sqlalchemy/event/base.py +472 -0
  106. sqlalchemy/event/legacy.py +258 -0
  107. sqlalchemy/event/registry.py +390 -0
  108. sqlalchemy/events.py +17 -0
  109. sqlalchemy/exc.py +832 -0
  110. sqlalchemy/ext/__init__.py +11 -0
  111. sqlalchemy/ext/associationproxy.py +2027 -0
  112. sqlalchemy/ext/asyncio/__init__.py +25 -0
  113. sqlalchemy/ext/asyncio/base.py +281 -0
  114. sqlalchemy/ext/asyncio/engine.py +1471 -0
  115. sqlalchemy/ext/asyncio/exc.py +21 -0
  116. sqlalchemy/ext/asyncio/result.py +965 -0
  117. sqlalchemy/ext/asyncio/scoping.py +1599 -0
  118. sqlalchemy/ext/asyncio/session.py +1947 -0
  119. sqlalchemy/ext/automap.py +1701 -0
  120. sqlalchemy/ext/baked.py +570 -0
  121. sqlalchemy/ext/compiler.py +600 -0
  122. sqlalchemy/ext/declarative/__init__.py +65 -0
  123. sqlalchemy/ext/declarative/extensions.py +564 -0
  124. sqlalchemy/ext/horizontal_shard.py +478 -0
  125. sqlalchemy/ext/hybrid.py +1535 -0
  126. sqlalchemy/ext/indexable.py +364 -0
  127. sqlalchemy/ext/instrumentation.py +450 -0
  128. sqlalchemy/ext/mutable.py +1085 -0
  129. sqlalchemy/ext/mypy/__init__.py +6 -0
  130. sqlalchemy/ext/mypy/apply.py +324 -0
  131. sqlalchemy/ext/mypy/decl_class.py +515 -0
  132. sqlalchemy/ext/mypy/infer.py +590 -0
  133. sqlalchemy/ext/mypy/names.py +335 -0
  134. sqlalchemy/ext/mypy/plugin.py +303 -0
  135. sqlalchemy/ext/mypy/util.py +357 -0
  136. sqlalchemy/ext/orderinglist.py +439 -0
  137. sqlalchemy/ext/serializer.py +185 -0
  138. sqlalchemy/future/__init__.py +16 -0
  139. sqlalchemy/future/engine.py +15 -0
  140. sqlalchemy/inspection.py +174 -0
  141. sqlalchemy/log.py +288 -0
  142. sqlalchemy/orm/__init__.py +171 -0
  143. sqlalchemy/orm/_orm_constructors.py +2661 -0
  144. sqlalchemy/orm/_typing.py +179 -0
  145. sqlalchemy/orm/attributes.py +2845 -0
  146. sqlalchemy/orm/base.py +971 -0
  147. sqlalchemy/orm/bulk_persistence.py +2135 -0
  148. sqlalchemy/orm/clsregistry.py +571 -0
  149. sqlalchemy/orm/collections.py +1627 -0
  150. sqlalchemy/orm/context.py +3334 -0
  151. sqlalchemy/orm/decl_api.py +2004 -0
  152. sqlalchemy/orm/decl_base.py +2192 -0
  153. sqlalchemy/orm/dependency.py +1302 -0
  154. sqlalchemy/orm/descriptor_props.py +1092 -0
  155. sqlalchemy/orm/dynamic.py +300 -0
  156. sqlalchemy/orm/evaluator.py +379 -0
  157. sqlalchemy/orm/events.py +3252 -0
  158. sqlalchemy/orm/exc.py +237 -0
  159. sqlalchemy/orm/identity.py +302 -0
  160. sqlalchemy/orm/instrumentation.py +754 -0
  161. sqlalchemy/orm/interfaces.py +1496 -0
  162. sqlalchemy/orm/loading.py +1686 -0
  163. sqlalchemy/orm/mapped_collection.py +557 -0
  164. sqlalchemy/orm/mapper.py +4444 -0
  165. sqlalchemy/orm/path_registry.py +809 -0
  166. sqlalchemy/orm/persistence.py +1788 -0
  167. sqlalchemy/orm/properties.py +935 -0
  168. sqlalchemy/orm/query.py +3459 -0
  169. sqlalchemy/orm/relationships.py +3508 -0
  170. sqlalchemy/orm/scoping.py +2148 -0
  171. sqlalchemy/orm/session.py +5280 -0
  172. sqlalchemy/orm/state.py +1168 -0
  173. sqlalchemy/orm/state_changes.py +196 -0
  174. sqlalchemy/orm/strategies.py +3470 -0
  175. sqlalchemy/orm/strategy_options.py +2568 -0
  176. sqlalchemy/orm/sync.py +164 -0
  177. sqlalchemy/orm/unitofwork.py +796 -0
  178. sqlalchemy/orm/util.py +2403 -0
  179. sqlalchemy/orm/writeonly.py +674 -0
  180. sqlalchemy/pool/__init__.py +44 -0
  181. sqlalchemy/pool/base.py +1524 -0
  182. sqlalchemy/pool/events.py +375 -0
  183. sqlalchemy/pool/impl.py +588 -0
  184. sqlalchemy/py.typed +0 -0
  185. sqlalchemy/schema.py +69 -0
  186. sqlalchemy/sql/__init__.py +145 -0
  187. sqlalchemy/sql/_dml_constructors.py +132 -0
  188. sqlalchemy/sql/_elements_constructors.py +1872 -0
  189. sqlalchemy/sql/_orm_types.py +20 -0
  190. sqlalchemy/sql/_py_util.py +75 -0
  191. sqlalchemy/sql/_selectable_constructors.py +763 -0
  192. sqlalchemy/sql/_typing.py +482 -0
  193. sqlalchemy/sql/annotation.py +587 -0
  194. sqlalchemy/sql/base.py +2293 -0
  195. sqlalchemy/sql/cache_key.py +1057 -0
  196. sqlalchemy/sql/coercions.py +1404 -0
  197. sqlalchemy/sql/compiler.py +8081 -0
  198. sqlalchemy/sql/crud.py +1752 -0
  199. sqlalchemy/sql/ddl.py +1444 -0
  200. sqlalchemy/sql/default_comparator.py +551 -0
  201. sqlalchemy/sql/dml.py +1850 -0
  202. sqlalchemy/sql/elements.py +5589 -0
  203. sqlalchemy/sql/events.py +458 -0
  204. sqlalchemy/sql/expression.py +159 -0
  205. sqlalchemy/sql/functions.py +2158 -0
  206. sqlalchemy/sql/lambdas.py +1442 -0
  207. sqlalchemy/sql/naming.py +209 -0
  208. sqlalchemy/sql/operators.py +2623 -0
  209. sqlalchemy/sql/roles.py +323 -0
  210. sqlalchemy/sql/schema.py +6222 -0
  211. sqlalchemy/sql/selectable.py +7265 -0
  212. sqlalchemy/sql/sqltypes.py +3930 -0
  213. sqlalchemy/sql/traversals.py +1024 -0
  214. sqlalchemy/sql/type_api.py +2368 -0
  215. sqlalchemy/sql/util.py +1485 -0
  216. sqlalchemy/sql/visitors.py +1164 -0
  217. sqlalchemy/testing/__init__.py +96 -0
  218. sqlalchemy/testing/assertions.py +994 -0
  219. sqlalchemy/testing/assertsql.py +520 -0
  220. sqlalchemy/testing/asyncio.py +135 -0
  221. sqlalchemy/testing/config.py +434 -0
  222. sqlalchemy/testing/engines.py +483 -0
  223. sqlalchemy/testing/entities.py +117 -0
  224. sqlalchemy/testing/exclusions.py +476 -0
  225. sqlalchemy/testing/fixtures/__init__.py +28 -0
  226. sqlalchemy/testing/fixtures/base.py +384 -0
  227. sqlalchemy/testing/fixtures/mypy.py +332 -0
  228. sqlalchemy/testing/fixtures/orm.py +227 -0
  229. sqlalchemy/testing/fixtures/sql.py +482 -0
  230. sqlalchemy/testing/pickleable.py +155 -0
  231. sqlalchemy/testing/plugin/__init__.py +6 -0
  232. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  233. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  234. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  235. sqlalchemy/testing/profiling.py +329 -0
  236. sqlalchemy/testing/provision.py +603 -0
  237. sqlalchemy/testing/requirements.py +1945 -0
  238. sqlalchemy/testing/schema.py +198 -0
  239. sqlalchemy/testing/suite/__init__.py +19 -0
  240. sqlalchemy/testing/suite/test_cte.py +237 -0
  241. sqlalchemy/testing/suite/test_ddl.py +389 -0
  242. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  243. sqlalchemy/testing/suite/test_dialect.py +776 -0
  244. sqlalchemy/testing/suite/test_insert.py +630 -0
  245. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  246. sqlalchemy/testing/suite/test_results.py +504 -0
  247. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  248. sqlalchemy/testing/suite/test_select.py +2010 -0
  249. sqlalchemy/testing/suite/test_sequence.py +317 -0
  250. sqlalchemy/testing/suite/test_types.py +2147 -0
  251. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  252. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  253. sqlalchemy/testing/util.py +535 -0
  254. sqlalchemy/testing/warnings.py +52 -0
  255. sqlalchemy/types.py +74 -0
  256. sqlalchemy/util/__init__.py +162 -0
  257. sqlalchemy/util/_collections.py +712 -0
  258. sqlalchemy/util/_concurrency_py3k.py +288 -0
  259. sqlalchemy/util/_has_cy.py +40 -0
  260. sqlalchemy/util/_py_collections.py +541 -0
  261. sqlalchemy/util/compat.py +421 -0
  262. sqlalchemy/util/concurrency.py +110 -0
  263. sqlalchemy/util/deprecations.py +401 -0
  264. sqlalchemy/util/langhelpers.py +2203 -0
  265. sqlalchemy/util/preloaded.py +150 -0
  266. sqlalchemy/util/queue.py +322 -0
  267. sqlalchemy/util/tool_support.py +201 -0
  268. sqlalchemy/util/topological.py +120 -0
  269. sqlalchemy/util/typing.py +734 -0
  270. sqlalchemy-2.0.47.dist-info/METADATA +243 -0
  271. sqlalchemy-2.0.47.dist-info/RECORD +274 -0
  272. sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
  273. sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
  274. 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