SQLAlchemy 2.1.0b1__cp313-cp313-win_arm64.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 (267) hide show
  1. sqlalchemy/__init__.py +295 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +161 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +30 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4110 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  12. sqlalchemy/dialects/mssql/json.py +129 -0
  13. sqlalchemy/dialects/mssql/provision.py +185 -0
  14. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  15. sqlalchemy/dialects/mssql/pyodbc.py +758 -0
  16. sqlalchemy/dialects/mysql/__init__.py +106 -0
  17. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  18. sqlalchemy/dialects/mysql/aiomysql.py +226 -0
  19. sqlalchemy/dialects/mysql/asyncmy.py +214 -0
  20. sqlalchemy/dialects/mysql/base.py +3870 -0
  21. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  22. sqlalchemy/dialects/mysql/dml.py +279 -0
  23. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  24. sqlalchemy/dialects/mysql/expression.py +146 -0
  25. sqlalchemy/dialects/mysql/json.py +91 -0
  26. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  27. sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
  28. sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
  29. sqlalchemy/dialects/mysql/mysqldb.py +312 -0
  30. sqlalchemy/dialects/mysql/provision.py +147 -0
  31. sqlalchemy/dialects/mysql/pymysql.py +157 -0
  32. sqlalchemy/dialects/mysql/pyodbc.py +156 -0
  33. sqlalchemy/dialects/mysql/reflection.py +724 -0
  34. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  35. sqlalchemy/dialects/mysql/types.py +845 -0
  36. sqlalchemy/dialects/oracle/__init__.py +83 -0
  37. sqlalchemy/dialects/oracle/base.py +3871 -0
  38. sqlalchemy/dialects/oracle/cx_oracle.py +1522 -0
  39. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  40. sqlalchemy/dialects/oracle/oracledb.py +894 -0
  41. sqlalchemy/dialects/oracle/provision.py +288 -0
  42. sqlalchemy/dialects/oracle/types.py +350 -0
  43. sqlalchemy/dialects/oracle/vector.py +368 -0
  44. sqlalchemy/dialects/postgresql/__init__.py +171 -0
  45. sqlalchemy/dialects/postgresql/_psycopg_common.py +193 -0
  46. sqlalchemy/dialects/postgresql/array.py +534 -0
  47. sqlalchemy/dialects/postgresql/asyncpg.py +1331 -0
  48. sqlalchemy/dialects/postgresql/base.py +5729 -0
  49. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  50. sqlalchemy/dialects/postgresql/dml.py +360 -0
  51. sqlalchemy/dialects/postgresql/ext.py +593 -0
  52. sqlalchemy/dialects/postgresql/hstore.py +413 -0
  53. sqlalchemy/dialects/postgresql/json.py +407 -0
  54. sqlalchemy/dialects/postgresql/named_types.py +521 -0
  55. sqlalchemy/dialects/postgresql/operators.py +130 -0
  56. sqlalchemy/dialects/postgresql/pg8000.py +672 -0
  57. sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
  58. sqlalchemy/dialects/postgresql/provision.py +175 -0
  59. sqlalchemy/dialects/postgresql/psycopg.py +815 -0
  60. sqlalchemy/dialects/postgresql/psycopg2.py +887 -0
  61. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  62. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  63. sqlalchemy/dialects/postgresql/types.py +388 -0
  64. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  65. sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
  66. sqlalchemy/dialects/sqlite/base.py +3050 -0
  67. sqlalchemy/dialects/sqlite/dml.py +279 -0
  68. sqlalchemy/dialects/sqlite/json.py +89 -0
  69. sqlalchemy/dialects/sqlite/provision.py +223 -0
  70. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  71. sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
  72. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  73. sqlalchemy/engine/__init__.py +62 -0
  74. sqlalchemy/engine/_processors_cy.cp313-win_arm64.pyd +0 -0
  75. sqlalchemy/engine/_processors_cy.py +92 -0
  76. sqlalchemy/engine/_result_cy.cp313-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_result_cy.py +633 -0
  78. sqlalchemy/engine/_row_cy.cp313-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_row_cy.py +232 -0
  80. sqlalchemy/engine/_util_cy.cp313-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_util_cy.py +136 -0
  82. sqlalchemy/engine/base.py +3334 -0
  83. sqlalchemy/engine/characteristics.py +155 -0
  84. sqlalchemy/engine/create.py +869 -0
  85. sqlalchemy/engine/cursor.py +2416 -0
  86. sqlalchemy/engine/default.py +2393 -0
  87. sqlalchemy/engine/events.py +965 -0
  88. sqlalchemy/engine/interfaces.py +3465 -0
  89. sqlalchemy/engine/mock.py +134 -0
  90. sqlalchemy/engine/processors.py +82 -0
  91. sqlalchemy/engine/reflection.py +2100 -0
  92. sqlalchemy/engine/result.py +1932 -0
  93. sqlalchemy/engine/row.py +397 -0
  94. sqlalchemy/engine/strategies.py +16 -0
  95. sqlalchemy/engine/url.py +922 -0
  96. sqlalchemy/engine/util.py +156 -0
  97. sqlalchemy/event/__init__.py +26 -0
  98. sqlalchemy/event/api.py +220 -0
  99. sqlalchemy/event/attr.py +674 -0
  100. sqlalchemy/event/base.py +472 -0
  101. sqlalchemy/event/legacy.py +258 -0
  102. sqlalchemy/event/registry.py +390 -0
  103. sqlalchemy/events.py +17 -0
  104. sqlalchemy/exc.py +922 -0
  105. sqlalchemy/ext/__init__.py +11 -0
  106. sqlalchemy/ext/associationproxy.py +2072 -0
  107. sqlalchemy/ext/asyncio/__init__.py +29 -0
  108. sqlalchemy/ext/asyncio/base.py +281 -0
  109. sqlalchemy/ext/asyncio/engine.py +1475 -0
  110. sqlalchemy/ext/asyncio/exc.py +21 -0
  111. sqlalchemy/ext/asyncio/result.py +994 -0
  112. sqlalchemy/ext/asyncio/scoping.py +1667 -0
  113. sqlalchemy/ext/asyncio/session.py +1993 -0
  114. sqlalchemy/ext/automap.py +1701 -0
  115. sqlalchemy/ext/baked.py +559 -0
  116. sqlalchemy/ext/compiler.py +600 -0
  117. sqlalchemy/ext/declarative/__init__.py +65 -0
  118. sqlalchemy/ext/declarative/extensions.py +560 -0
  119. sqlalchemy/ext/horizontal_shard.py +481 -0
  120. sqlalchemy/ext/hybrid.py +1877 -0
  121. sqlalchemy/ext/indexable.py +364 -0
  122. sqlalchemy/ext/instrumentation.py +450 -0
  123. sqlalchemy/ext/mutable.py +1081 -0
  124. sqlalchemy/ext/orderinglist.py +439 -0
  125. sqlalchemy/ext/serializer.py +185 -0
  126. sqlalchemy/future/__init__.py +16 -0
  127. sqlalchemy/future/engine.py +15 -0
  128. sqlalchemy/inspection.py +174 -0
  129. sqlalchemy/log.py +283 -0
  130. sqlalchemy/orm/__init__.py +175 -0
  131. sqlalchemy/orm/_orm_constructors.py +2694 -0
  132. sqlalchemy/orm/_typing.py +179 -0
  133. sqlalchemy/orm/attributes.py +2868 -0
  134. sqlalchemy/orm/base.py +970 -0
  135. sqlalchemy/orm/bulk_persistence.py +2152 -0
  136. sqlalchemy/orm/clsregistry.py +582 -0
  137. sqlalchemy/orm/collections.py +1568 -0
  138. sqlalchemy/orm/context.py +3471 -0
  139. sqlalchemy/orm/decl_api.py +2257 -0
  140. sqlalchemy/orm/decl_base.py +2304 -0
  141. sqlalchemy/orm/dependency.py +1306 -0
  142. sqlalchemy/orm/descriptor_props.py +1183 -0
  143. sqlalchemy/orm/dynamic.py +300 -0
  144. sqlalchemy/orm/evaluator.py +379 -0
  145. sqlalchemy/orm/events.py +3386 -0
  146. sqlalchemy/orm/exc.py +237 -0
  147. sqlalchemy/orm/identity.py +302 -0
  148. sqlalchemy/orm/instrumentation.py +746 -0
  149. sqlalchemy/orm/interfaces.py +1589 -0
  150. sqlalchemy/orm/loading.py +1684 -0
  151. sqlalchemy/orm/mapped_collection.py +557 -0
  152. sqlalchemy/orm/mapper.py +4406 -0
  153. sqlalchemy/orm/path_registry.py +814 -0
  154. sqlalchemy/orm/persistence.py +1789 -0
  155. sqlalchemy/orm/properties.py +973 -0
  156. sqlalchemy/orm/query.py +3521 -0
  157. sqlalchemy/orm/relationships.py +3570 -0
  158. sqlalchemy/orm/scoping.py +2220 -0
  159. sqlalchemy/orm/session.py +5389 -0
  160. sqlalchemy/orm/state.py +1175 -0
  161. sqlalchemy/orm/state_changes.py +196 -0
  162. sqlalchemy/orm/strategies.py +3480 -0
  163. sqlalchemy/orm/strategy_options.py +2544 -0
  164. sqlalchemy/orm/sync.py +164 -0
  165. sqlalchemy/orm/unitofwork.py +798 -0
  166. sqlalchemy/orm/util.py +2435 -0
  167. sqlalchemy/orm/writeonly.py +694 -0
  168. sqlalchemy/pool/__init__.py +41 -0
  169. sqlalchemy/pool/base.py +1514 -0
  170. sqlalchemy/pool/events.py +372 -0
  171. sqlalchemy/pool/impl.py +582 -0
  172. sqlalchemy/py.typed +0 -0
  173. sqlalchemy/schema.py +72 -0
  174. sqlalchemy/sql/__init__.py +153 -0
  175. sqlalchemy/sql/_dml_constructors.py +132 -0
  176. sqlalchemy/sql/_elements_constructors.py +2147 -0
  177. sqlalchemy/sql/_orm_types.py +20 -0
  178. sqlalchemy/sql/_selectable_constructors.py +773 -0
  179. sqlalchemy/sql/_typing.py +486 -0
  180. sqlalchemy/sql/_util_cy.cp313-win_arm64.pyd +0 -0
  181. sqlalchemy/sql/_util_cy.py +127 -0
  182. sqlalchemy/sql/annotation.py +590 -0
  183. sqlalchemy/sql/base.py +2602 -0
  184. sqlalchemy/sql/cache_key.py +1066 -0
  185. sqlalchemy/sql/coercions.py +1373 -0
  186. sqlalchemy/sql/compiler.py +8259 -0
  187. sqlalchemy/sql/crud.py +1807 -0
  188. sqlalchemy/sql/ddl.py +1928 -0
  189. sqlalchemy/sql/default_comparator.py +654 -0
  190. sqlalchemy/sql/dml.py +1974 -0
  191. sqlalchemy/sql/elements.py +6016 -0
  192. sqlalchemy/sql/events.py +458 -0
  193. sqlalchemy/sql/expression.py +170 -0
  194. sqlalchemy/sql/functions.py +2257 -0
  195. sqlalchemy/sql/lambdas.py +1443 -0
  196. sqlalchemy/sql/naming.py +209 -0
  197. sqlalchemy/sql/operators.py +2897 -0
  198. sqlalchemy/sql/roles.py +332 -0
  199. sqlalchemy/sql/schema.py +6560 -0
  200. sqlalchemy/sql/selectable.py +7497 -0
  201. sqlalchemy/sql/sqltypes.py +4050 -0
  202. sqlalchemy/sql/traversals.py +1042 -0
  203. sqlalchemy/sql/type_api.py +2425 -0
  204. sqlalchemy/sql/util.py +1495 -0
  205. sqlalchemy/sql/visitors.py +1157 -0
  206. sqlalchemy/testing/__init__.py +96 -0
  207. sqlalchemy/testing/assertions.py +1007 -0
  208. sqlalchemy/testing/assertsql.py +519 -0
  209. sqlalchemy/testing/asyncio.py +128 -0
  210. sqlalchemy/testing/config.py +440 -0
  211. sqlalchemy/testing/engines.py +478 -0
  212. sqlalchemy/testing/entities.py +117 -0
  213. sqlalchemy/testing/exclusions.py +476 -0
  214. sqlalchemy/testing/fixtures/__init__.py +30 -0
  215. sqlalchemy/testing/fixtures/base.py +366 -0
  216. sqlalchemy/testing/fixtures/mypy.py +247 -0
  217. sqlalchemy/testing/fixtures/orm.py +227 -0
  218. sqlalchemy/testing/fixtures/sql.py +538 -0
  219. sqlalchemy/testing/pickleable.py +155 -0
  220. sqlalchemy/testing/plugin/__init__.py +6 -0
  221. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  222. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  223. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  224. sqlalchemy/testing/profiling.py +329 -0
  225. sqlalchemy/testing/provision.py +596 -0
  226. sqlalchemy/testing/requirements.py +1973 -0
  227. sqlalchemy/testing/schema.py +198 -0
  228. sqlalchemy/testing/suite/__init__.py +19 -0
  229. sqlalchemy/testing/suite/test_cte.py +237 -0
  230. sqlalchemy/testing/suite/test_ddl.py +420 -0
  231. sqlalchemy/testing/suite/test_dialect.py +776 -0
  232. sqlalchemy/testing/suite/test_insert.py +630 -0
  233. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  234. sqlalchemy/testing/suite/test_results.py +660 -0
  235. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  236. sqlalchemy/testing/suite/test_select.py +2112 -0
  237. sqlalchemy/testing/suite/test_sequence.py +317 -0
  238. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  239. sqlalchemy/testing/suite/test_types.py +2253 -0
  240. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  241. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  242. sqlalchemy/testing/util.py +535 -0
  243. sqlalchemy/testing/warnings.py +52 -0
  244. sqlalchemy/types.py +76 -0
  245. sqlalchemy/util/__init__.py +157 -0
  246. sqlalchemy/util/_collections.py +693 -0
  247. sqlalchemy/util/_collections_cy.cp313-win_arm64.pyd +0 -0
  248. sqlalchemy/util/_collections_cy.pxd +8 -0
  249. sqlalchemy/util/_collections_cy.py +516 -0
  250. sqlalchemy/util/_has_cython.py +46 -0
  251. sqlalchemy/util/_immutabledict_cy.cp313-win_arm64.pyd +0 -0
  252. sqlalchemy/util/_immutabledict_cy.py +240 -0
  253. sqlalchemy/util/compat.py +287 -0
  254. sqlalchemy/util/concurrency.py +322 -0
  255. sqlalchemy/util/cython.py +79 -0
  256. sqlalchemy/util/deprecations.py +401 -0
  257. sqlalchemy/util/langhelpers.py +2256 -0
  258. sqlalchemy/util/preloaded.py +152 -0
  259. sqlalchemy/util/queue.py +304 -0
  260. sqlalchemy/util/tool_support.py +201 -0
  261. sqlalchemy/util/topological.py +120 -0
  262. sqlalchemy/util/typing.py +711 -0
  263. sqlalchemy-2.1.0b1.dist-info/METADATA +267 -0
  264. sqlalchemy-2.1.0b1.dist-info/RECORD +267 -0
  265. sqlalchemy-2.1.0b1.dist-info/WHEEL +5 -0
  266. sqlalchemy-2.1.0b1.dist-info/licenses/LICENSE +19 -0
  267. sqlalchemy-2.1.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,288 @@
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 log
23
+ from ...testing.provision import post_configure_engine
24
+ from ...testing.provision import post_configure_testing_engine
25
+ from ...testing.provision import run_reap_dbs
26
+ from ...testing.provision import set_default_schema_on_connection
27
+ from ...testing.provision import stop_test_class_outside_fixtures
28
+ from ...testing.provision import temp_table_keyword_args
29
+ from ...testing.provision import update_db_opts
30
+ from ...testing.warnings import warn_test_suite
31
+
32
+
33
+ @generate_driver_url.for_db("oracle")
34
+ def _oracle_generate_driver_url(url, driver, query_str):
35
+
36
+ backend = url.get_backend_name()
37
+
38
+ new_url = url.set(
39
+ drivername="%s+%s" % (backend, driver),
40
+ )
41
+
42
+ # use oracledb's retry feature, which is essential for oracle 23c
43
+ # which otherwise frequently rejects connections under load
44
+ # for cx_oracle we have a connect event instead
45
+ if driver in ("oracledb", "oracledb_async"):
46
+ # oracledb is even nice enough to convert from string to int
47
+ # for these opts, apparently
48
+ new_url = new_url.update_query_pairs(
49
+ [("retry_count", "5"), ("retry_delay", "2")]
50
+ )
51
+ else:
52
+ # remove these params for cx_oracle if we received an
53
+ # already-modified URL
54
+ new_url = new_url.difference_update_query(
55
+ ["retry_count", "retry_delay"]
56
+ )
57
+
58
+ try:
59
+ new_url.get_dialect()
60
+ except exc.NoSuchModuleError:
61
+ return None
62
+ else:
63
+ return new_url
64
+
65
+
66
+ @create_db.for_db("oracle")
67
+ def _oracle_create_db(cfg, eng, ident):
68
+ # NOTE: make sure you've run "ALTER DATABASE default tablespace users" or
69
+ # similar, so that the default tablespace is not "system"; reflection will
70
+ # fail otherwise
71
+ with eng.begin() as conn:
72
+ conn.exec_driver_sql("create user %s identified by xe" % ident)
73
+ conn.exec_driver_sql("create user %s_ts1 identified by xe" % ident)
74
+ conn.exec_driver_sql("create user %s_ts2 identified by xe" % ident)
75
+ conn.exec_driver_sql("grant dba to %s" % (ident,))
76
+ conn.exec_driver_sql("grant unlimited tablespace to %s" % ident)
77
+ conn.exec_driver_sql("grant unlimited tablespace to %s_ts1" % ident)
78
+ conn.exec_driver_sql("grant unlimited tablespace to %s_ts2" % ident)
79
+ # these are needed to create materialized views
80
+ conn.exec_driver_sql("grant create table to %s" % ident)
81
+ conn.exec_driver_sql("grant create table to %s_ts1" % ident)
82
+ conn.exec_driver_sql("grant create table to %s_ts2" % ident)
83
+
84
+
85
+ @configure_follower.for_db("oracle")
86
+ def _oracle_configure_follower(config, ident):
87
+ config.test_schema = "%s_ts1" % ident
88
+ config.test_schema_2 = "%s_ts2" % ident
89
+
90
+
91
+ def _ora_drop_ignore(conn, dbname):
92
+ try:
93
+ conn.exec_driver_sql("drop user %s cascade" % dbname)
94
+ log.info("Reaped db: %s", dbname)
95
+ return True
96
+ except exc.DatabaseError as err:
97
+ log.warning("couldn't drop db: %s", err)
98
+ return False
99
+
100
+
101
+ @drop_all_schema_objects_pre_tables.for_db("oracle")
102
+ def _ora_drop_all_schema_objects_pre_tables(cfg, eng):
103
+ _purge_recyclebin(eng)
104
+ _purge_recyclebin(eng, cfg.test_schema)
105
+
106
+
107
+ @drop_all_schema_objects_post_tables.for_db("oracle")
108
+ def _ora_drop_all_schema_objects_post_tables(cfg, eng):
109
+ with eng.begin() as conn:
110
+ for syn in conn.dialect._get_synonyms(conn, None, None, None):
111
+ conn.exec_driver_sql(f"drop synonym {syn['synonym_name']}")
112
+
113
+ for syn in conn.dialect._get_synonyms(
114
+ conn, cfg.test_schema, None, None
115
+ ):
116
+ conn.exec_driver_sql(
117
+ f"drop synonym {cfg.test_schema}.{syn['synonym_name']}"
118
+ )
119
+
120
+ for tmp_table in inspect(conn).get_temp_table_names():
121
+ conn.exec_driver_sql(f"drop table {tmp_table}")
122
+
123
+
124
+ @drop_db.for_db("oracle")
125
+ def _oracle_drop_db(cfg, eng, ident):
126
+ with eng.begin() as conn:
127
+ # cx_Oracle seems to occasionally leak open connections when a large
128
+ # suite it run, even if we confirm we have zero references to
129
+ # connection objects.
130
+ # while there is a "kill session" command in Oracle Database,
131
+ # it unfortunately does not release the connection sufficiently.
132
+ _ora_drop_ignore(conn, ident)
133
+ _ora_drop_ignore(conn, "%s_ts1" % ident)
134
+ _ora_drop_ignore(conn, "%s_ts2" % ident)
135
+
136
+
137
+ @stop_test_class_outside_fixtures.for_db("oracle")
138
+ def _ora_stop_test_class_outside_fixtures(config, db, cls):
139
+ try:
140
+ _purge_recyclebin(db)
141
+ except exc.DatabaseError as err:
142
+ log.warning("purge recyclebin command failed: %s", err)
143
+
144
+
145
+ def _purge_recyclebin(eng, schema=None):
146
+ with eng.begin() as conn:
147
+ if schema is None:
148
+ # run magic command to get rid of identity sequences
149
+ # https://floo.bar/2019/11/29/drop-the-underlying-sequence-of-an-identity-column/ # noqa: E501
150
+ conn.exec_driver_sql("purge recyclebin")
151
+ else:
152
+ # per user: https://community.oracle.com/tech/developers/discussion/2255402/how-to-clear-dba-recyclebin-for-a-particular-user # noqa: E501
153
+ for owner, object_name, type_ in conn.exec_driver_sql(
154
+ "select owner, object_name,type from "
155
+ "dba_recyclebin where owner=:schema and type='TABLE'",
156
+ {"schema": conn.dialect.denormalize_name(schema)},
157
+ ).all():
158
+ conn.exec_driver_sql(f'purge {type_} {owner}."{object_name}"')
159
+
160
+
161
+ def _connect_with_retry(dialect, conn_rec, cargs, cparams):
162
+ assert dialect.driver == "cx_oracle"
163
+
164
+ def _is_couldnt_connect(err):
165
+ return "DPY-6005" in str(err) or "ORA-12516" in str(err)
166
+
167
+ err_ = None
168
+ for _ in range(5):
169
+ try:
170
+ return dialect.loaded_dbapi.connect(*cargs, **cparams)
171
+ except (
172
+ dialect.loaded_dbapi.DatabaseError,
173
+ dialect.loaded_dbapi.OperationalError,
174
+ ) as err:
175
+ err_ = err
176
+ if _is_couldnt_connect(err):
177
+ warn_test_suite("Oracle database reconnecting...")
178
+ time.sleep(2)
179
+ continue
180
+ else:
181
+ raise
182
+ if err_ is not None:
183
+ raise Exception("connect failed after five attempts") from err_
184
+
185
+
186
+ @post_configure_testing_engine.for_db("oracle")
187
+ def _oracle_post_configure_testing_engine(url, engine, options, scope):
188
+ from ... import event
189
+
190
+ if engine.dialect.driver == "cx_oracle":
191
+ event.listen(engine, "do_connect", _connect_with_retry)
192
+
193
+
194
+ @post_configure_engine.for_db("oracle")
195
+ def _oracle_post_configure_engine(url, engine, follower_ident):
196
+
197
+ from ... import event
198
+
199
+ @event.listens_for(engine, "checkin")
200
+ def checkin(dbapi_connection, connection_record):
201
+ # this was meant to work around this issue:
202
+ # https://github.com/oracle/python-cx_Oracle/issues/530
203
+ # invalidate oracle connections that had 2pc set up
204
+ # however things are too complex with some of the 2pc tests,
205
+ # so just block cx_oracle from being used in 2pc tests (use oracledb
206
+ # instead)
207
+ # if "cx_oracle_xid" in connection_record.info:
208
+ # connection_record.invalidate()
209
+
210
+ # clear statement cache on all connections that were used
211
+ # https://github.com/oracle/python-cx_Oracle/issues/519
212
+ # TODO: oracledb claims to have this feature built in somehow,
213
+ # see if that's in use and/or if it needs to be enabled
214
+ # (or if this doesn't even apply to the newer oracle's we're using)
215
+ try:
216
+ sc = dbapi_connection.stmtcachesize
217
+ except:
218
+ # connection closed
219
+ pass
220
+ else:
221
+ dbapi_connection.stmtcachesize = 0
222
+ dbapi_connection.stmtcachesize = sc
223
+
224
+
225
+ @run_reap_dbs.for_db("oracle")
226
+ def _reap_oracle_dbs(url, idents):
227
+ log.info("db reaper connecting to %r", url)
228
+ eng = create_engine(url)
229
+ with eng.begin() as conn:
230
+ log.info("identifiers in file: %s", ", ".join(idents))
231
+
232
+ to_reap = conn.exec_driver_sql(
233
+ "select u.username from all_users u where username "
234
+ "like 'TEST_%' and not exists (select username "
235
+ "from v$session where username=u.username)"
236
+ )
237
+ all_names = {username.lower() for (username,) in to_reap}
238
+ to_drop = set()
239
+ for name in all_names:
240
+ if name.endswith("_ts1") or name.endswith("_ts2"):
241
+ continue
242
+ elif name in idents:
243
+ to_drop.add(name)
244
+ if "%s_ts1" % name in all_names:
245
+ to_drop.add("%s_ts1" % name)
246
+ if "%s_ts2" % name in all_names:
247
+ to_drop.add("%s_ts2" % name)
248
+
249
+ dropped = total = 0
250
+ for total, username in enumerate(to_drop, 1):
251
+ if _ora_drop_ignore(conn, username):
252
+ dropped += 1
253
+ log.info(
254
+ "Dropped %d out of %d stale databases detected", dropped, total
255
+ )
256
+
257
+
258
+ @follower_url_from_main.for_db("oracle")
259
+ def _oracle_follower_url_from_main(url, ident):
260
+ url = sa_url.make_url(url)
261
+ return url.set(username=ident, password="xe")
262
+
263
+
264
+ @temp_table_keyword_args.for_db("oracle")
265
+ def _oracle_temp_table_keyword_args(cfg, eng):
266
+ return {
267
+ "prefixes": ["GLOBAL TEMPORARY"],
268
+ "oracle_on_commit": "PRESERVE ROWS",
269
+ }
270
+
271
+
272
+ @set_default_schema_on_connection.for_db("oracle")
273
+ def _oracle_set_default_schema_on_connection(
274
+ cfg, dbapi_connection, schema_name
275
+ ):
276
+ cursor = dbapi_connection.cursor()
277
+ cursor.execute("ALTER SESSION SET CURRENT_SCHEMA=%s" % schema_name)
278
+ cursor.close()
279
+
280
+
281
+ @update_db_opts.for_db("oracle")
282
+ def _update_db_opts(db_url, db_opts, options):
283
+ """Set database options (db_opts) for a test database that we created."""
284
+ if (
285
+ options.oracledb_thick_mode
286
+ and sa_url.make_url(db_url).get_driver_name() == "oracledb"
287
+ ):
288
+ db_opts["thick_mode"] = True
@@ -0,0 +1,350 @@
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 operators
17
+ from ...sql import sqltypes
18
+ from ...types import NVARCHAR
19
+ from ...types import VARCHAR
20
+
21
+ if TYPE_CHECKING:
22
+ from ...engine.interfaces import Dialect
23
+ from ...sql.type_api import _LiteralProcessorType
24
+
25
+
26
+ BOOLEAN = sqltypes.BOOLEAN
27
+
28
+
29
+ class RAW(sqltypes._Binary):
30
+ __visit_name__ = "RAW"
31
+
32
+
33
+ OracleRaw = RAW
34
+
35
+
36
+ class NCLOB(sqltypes.Text):
37
+ __visit_name__ = "NCLOB"
38
+
39
+
40
+ class VARCHAR2(VARCHAR):
41
+ __visit_name__ = "VARCHAR2"
42
+
43
+
44
+ NVARCHAR2 = NVARCHAR
45
+
46
+
47
+ class NUMBER(sqltypes.Numeric, sqltypes.Integer):
48
+ __visit_name__ = "NUMBER"
49
+
50
+ def __init__(self, precision=None, scale=None, asdecimal=None):
51
+ if asdecimal is None:
52
+ asdecimal = bool(scale and scale > 0)
53
+
54
+ super().__init__(precision=precision, scale=scale, asdecimal=asdecimal)
55
+
56
+ def adapt(self, impltype):
57
+ ret = super().adapt(impltype)
58
+ # leave a hint for the DBAPI handler
59
+ ret._is_oracle_number = True
60
+ return ret
61
+
62
+ @property
63
+ def _type_affinity(self):
64
+ if bool(self.scale and self.scale > 0):
65
+ return sqltypes.Numeric
66
+ else:
67
+ return sqltypes.Integer
68
+
69
+
70
+ class FLOAT(sqltypes.FLOAT):
71
+ """Oracle Database FLOAT.
72
+
73
+ This is the same as :class:`_sqltypes.FLOAT` except that
74
+ an Oracle Database -specific :paramref:`_oracle.FLOAT.binary_precision`
75
+ parameter is accepted, and
76
+ the :paramref:`_sqltypes.Float.precision` parameter is not accepted.
77
+
78
+ Oracle Database FLOAT types indicate precision in terms of "binary
79
+ precision", which defaults to 126. For a REAL type, the value is 63. This
80
+ parameter does not cleanly map to a specific number of decimal places but
81
+ is roughly equivalent to the desired number of decimal places divided by
82
+ 0.3103.
83
+
84
+ .. versionadded:: 2.0
85
+
86
+ """
87
+
88
+ __visit_name__ = "FLOAT"
89
+
90
+ def __init__(
91
+ self,
92
+ binary_precision=None,
93
+ asdecimal=False,
94
+ decimal_return_scale=None,
95
+ ):
96
+ r"""
97
+ Construct a FLOAT
98
+
99
+ :param binary_precision: Oracle Database binary precision value to be
100
+ rendered in DDL. This may be approximated to the number of decimal
101
+ characters using the formula "decimal precision = 0.30103 * binary
102
+ precision". The default value used by Oracle Database for FLOAT /
103
+ DOUBLE PRECISION is 126.
104
+
105
+ :param asdecimal: See :paramref:`_sqltypes.Float.asdecimal`
106
+
107
+ :param decimal_return_scale: See
108
+ :paramref:`_sqltypes.Float.decimal_return_scale`
109
+
110
+ """
111
+ super().__init__(
112
+ asdecimal=asdecimal, decimal_return_scale=decimal_return_scale
113
+ )
114
+ self.binary_precision = binary_precision
115
+
116
+
117
+ class BINARY_DOUBLE(sqltypes.Double):
118
+ """Implement the Oracle ``BINARY_DOUBLE`` datatype.
119
+
120
+ This datatype differs from the Oracle ``DOUBLE`` datatype in that it
121
+ delivers a true 8-byte FP value. The datatype may be combined with a
122
+ generic :class:`.Double` datatype using :meth:`.TypeEngine.with_variant`.
123
+
124
+ .. seealso::
125
+
126
+ :ref:`oracle_float_support`
127
+
128
+
129
+ """
130
+
131
+ __visit_name__ = "BINARY_DOUBLE"
132
+
133
+
134
+ class BINARY_FLOAT(sqltypes.Float):
135
+ """Implement the Oracle ``BINARY_FLOAT`` datatype.
136
+
137
+ This datatype differs from the Oracle ``FLOAT`` datatype in that it
138
+ delivers a true 4-byte FP value. The datatype may be combined with a
139
+ generic :class:`.Float` datatype using :meth:`.TypeEngine.with_variant`.
140
+
141
+ .. seealso::
142
+
143
+ :ref:`oracle_float_support`
144
+
145
+
146
+ """
147
+
148
+ __visit_name__ = "BINARY_FLOAT"
149
+
150
+
151
+ class BFILE(sqltypes.LargeBinary):
152
+ __visit_name__ = "BFILE"
153
+
154
+
155
+ class LONG(sqltypes.Text):
156
+ __visit_name__ = "LONG"
157
+
158
+
159
+ class _OracleDateLiteralRender:
160
+ def _literal_processor_datetime(self, dialect):
161
+ def process(value):
162
+ if getattr(value, "microsecond", None):
163
+ value = (
164
+ f"""TO_TIMESTAMP"""
165
+ f"""('{value.isoformat().replace("T", " ")}', """
166
+ """'YYYY-MM-DD HH24:MI:SS.FF')"""
167
+ )
168
+ else:
169
+ value = (
170
+ f"""TO_DATE"""
171
+ f"""('{value.isoformat().replace("T", " ")}', """
172
+ """'YYYY-MM-DD HH24:MI:SS')"""
173
+ )
174
+ return value
175
+
176
+ return process
177
+
178
+ def _literal_processor_date(self, dialect):
179
+ def process(value):
180
+ if getattr(value, "microsecond", None):
181
+ value = (
182
+ f"""TO_TIMESTAMP"""
183
+ f"""('{value.isoformat().split("T")[0]}', """
184
+ """'YYYY-MM-DD')"""
185
+ )
186
+ else:
187
+ value = (
188
+ f"""TO_DATE"""
189
+ f"""('{value.isoformat().split("T")[0]}', """
190
+ """'YYYY-MM-DD')"""
191
+ )
192
+ return value
193
+
194
+ return process
195
+
196
+
197
+ class DATE(_OracleDateLiteralRender, sqltypes.DateTime):
198
+ """Provide the Oracle Database DATE type.
199
+
200
+ This type has no special Python behavior, except that it subclasses
201
+ :class:`_types.DateTime`; this is to suit the fact that the Oracle Database
202
+ ``DATE`` type supports a time value.
203
+
204
+ """
205
+
206
+ __visit_name__ = "DATE"
207
+
208
+ def literal_processor(self, dialect):
209
+ return self._literal_processor_datetime(dialect)
210
+
211
+ def _compare_type_affinity(self, other):
212
+ return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
213
+
214
+
215
+ class _OracleDate(_OracleDateLiteralRender, sqltypes.Date):
216
+ def literal_processor(self, dialect):
217
+ return self._literal_processor_date(dialect)
218
+
219
+
220
+ class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval):
221
+ __visit_name__ = "INTERVAL"
222
+
223
+ def __init__(self, day_precision=None, second_precision=None):
224
+ """Construct an INTERVAL.
225
+
226
+ Note that only DAY TO SECOND intervals are currently supported.
227
+ This is due to a lack of support for YEAR TO MONTH intervals
228
+ within available DBAPIs.
229
+
230
+ :param day_precision: the day precision value. this is the number of
231
+ digits to store for the day field. Defaults to "2"
232
+ :param second_precision: the second precision value. this is the
233
+ number of digits to store for the fractional seconds field.
234
+ Defaults to "6".
235
+
236
+ """
237
+ self.day_precision = day_precision
238
+ self.second_precision = second_precision
239
+
240
+ @classmethod
241
+ def _adapt_from_generic_interval(cls, interval):
242
+ return INTERVAL(
243
+ day_precision=interval.day_precision,
244
+ second_precision=interval.second_precision,
245
+ )
246
+
247
+ @classmethod
248
+ def adapt_emulated_to_native(
249
+ cls, interval: sqltypes.Interval, **kw # type: ignore[override]
250
+ ):
251
+ return INTERVAL(
252
+ day_precision=interval.day_precision,
253
+ second_precision=interval.second_precision,
254
+ )
255
+
256
+ @property
257
+ def _type_affinity(self):
258
+ return sqltypes.Interval
259
+
260
+ def as_generic(self, allow_nulltype=False):
261
+ return sqltypes.Interval(
262
+ native=True,
263
+ second_precision=self.second_precision,
264
+ day_precision=self.day_precision,
265
+ )
266
+
267
+ @property
268
+ def python_type(self) -> Type[dt.timedelta]:
269
+ return dt.timedelta
270
+
271
+ def literal_processor(
272
+ self, dialect: Dialect
273
+ ) -> Optional[_LiteralProcessorType[dt.timedelta]]:
274
+ def process(value: dt.timedelta) -> str:
275
+ return f"NUMTODSINTERVAL({value.total_seconds()}, 'SECOND')"
276
+
277
+ return process
278
+
279
+
280
+ class TIMESTAMP(sqltypes.TIMESTAMP):
281
+ """Oracle Database implementation of ``TIMESTAMP``, which supports
282
+ additional Oracle Database-specific modes
283
+
284
+ .. versionadded:: 2.0
285
+
286
+ """
287
+
288
+ def __init__(self, timezone: bool = False, local_timezone: bool = False):
289
+ """Construct a new :class:`_oracle.TIMESTAMP`.
290
+
291
+ :param timezone: boolean. Indicates that the TIMESTAMP type should
292
+ use Oracle Database's ``TIMESTAMP WITH TIME ZONE`` datatype.
293
+
294
+ :param local_timezone: boolean. Indicates that the TIMESTAMP type
295
+ should use Oracle Database's ``TIMESTAMP WITH LOCAL TIME ZONE``
296
+ datatype.
297
+
298
+
299
+ """
300
+ if timezone and local_timezone:
301
+ raise exc.ArgumentError(
302
+ "timezone and local_timezone are mutually exclusive"
303
+ )
304
+ super().__init__(timezone=timezone)
305
+ self.local_timezone = local_timezone
306
+
307
+
308
+ class ROWID(sqltypes.TypeEngine):
309
+ """Oracle Database ROWID type.
310
+
311
+ When used in a cast() or similar, generates ROWID.
312
+
313
+ """
314
+
315
+ __visit_name__ = "ROWID"
316
+ operator_classes = operators.OperatorClass.ANY
317
+
318
+
319
+ class _OracleBoolean(sqltypes.Boolean):
320
+
321
+ def get_dbapi_type(self, dbapi):
322
+ # this can probably be dbapi.BOOLEAN (including for older versions),
323
+ # however sticking with NUMBER to avoid any surprises with older
324
+ # versions or non-bool values
325
+ return dbapi.NUMBER
326
+
327
+ def result_processor(self, dialect, coltype):
328
+ # we dont need a result processor even if we are not native
329
+ # boolean because we use an outputtypehandler
330
+ return None
331
+
332
+ def _cx_oracle_outputtypehandler(self, dialect):
333
+ cx_Oracle = dialect.dbapi
334
+
335
+ def handler(cursor, name, default_type, size, precision, scale):
336
+ # if native boolean no handler needed
337
+ if default_type is cx_Oracle.BOOLEAN:
338
+ return None
339
+
340
+ # OTOH if we are getting a number back and we are either
341
+ # native boolean pulling from a smallint, or non native
342
+ # boolean pulling from a smallint that's emulated, use bool
343
+ return cursor.var(
344
+ cx_Oracle.NUMBER,
345
+ 255,
346
+ arraysize=cursor.arraysize,
347
+ outconverter=bool,
348
+ )
349
+
350
+ return handler