SQLAlchemy 2.1.0b2__cp313-cp313t-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 (270) hide show
  1. sqlalchemy/__init__.py +298 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -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 +89 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4166 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +698 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +226 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +214 -0
  21. sqlalchemy/dialects/mysql/base.py +3877 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +312 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +157 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +156 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3977 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +158 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +909 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +368 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +171 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
  50. sqlalchemy/dialects/postgresql/base.py +5789 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +593 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +423 -0
  55. sqlalchemy/dialects/postgresql/json.py +408 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +521 -0
  57. sqlalchemy/dialects/postgresql/operators.py +130 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +670 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
  60. sqlalchemy/dialects/postgresql/provision.py +184 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +799 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
  68. sqlalchemy/dialects/sqlite/base.py +3063 -0
  69. sqlalchemy/dialects/sqlite/dml.py +279 -0
  70. sqlalchemy/dialects/sqlite/json.py +100 -0
  71. sqlalchemy/dialects/sqlite/provision.py +229 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +633 -0
  80. sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3354 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2421 -0
  88. sqlalchemy/engine/default.py +2402 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3495 -0
  91. sqlalchemy/engine/mock.py +134 -0
  92. sqlalchemy/engine/processors.py +82 -0
  93. sqlalchemy/engine/reflection.py +2100 -0
  94. sqlalchemy/engine/result.py +1966 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +156 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +674 -0
  102. sqlalchemy/event/base.py +472 -0
  103. sqlalchemy/event/legacy.py +258 -0
  104. sqlalchemy/event/registry.py +390 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +922 -0
  107. sqlalchemy/ext/__init__.py +11 -0
  108. sqlalchemy/ext/associationproxy.py +2072 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2007 -0
  116. sqlalchemy/ext/automap.py +1701 -0
  117. sqlalchemy/ext/baked.py +559 -0
  118. sqlalchemy/ext/compiler.py +600 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +560 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +439 -0
  127. sqlalchemy/ext/serializer.py +185 -0
  128. sqlalchemy/future/__init__.py +16 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +174 -0
  131. sqlalchemy/log.py +283 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +179 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +976 -0
  137. sqlalchemy/orm/bulk_persistence.py +2152 -0
  138. sqlalchemy/orm/clsregistry.py +582 -0
  139. sqlalchemy/orm/collections.py +1568 -0
  140. sqlalchemy/orm/context.py +3471 -0
  141. sqlalchemy/orm/decl_api.py +2280 -0
  142. sqlalchemy/orm/decl_base.py +2309 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +307 -0
  146. sqlalchemy/orm/evaluator.py +379 -0
  147. sqlalchemy/orm/events.py +3386 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +746 -0
  151. sqlalchemy/orm/interfaces.py +1589 -0
  152. sqlalchemy/orm/loading.py +1684 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4411 -0
  155. sqlalchemy/orm/path_registry.py +829 -0
  156. sqlalchemy/orm/persistence.py +1789 -0
  157. sqlalchemy/orm/properties.py +973 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3570 -0
  160. sqlalchemy/orm/scoping.py +2232 -0
  161. sqlalchemy/orm/session.py +5403 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3492 -0
  165. sqlalchemy/orm/strategy_options.py +2562 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +798 -0
  168. sqlalchemy/orm/util.py +2438 -0
  169. sqlalchemy/orm/writeonly.py +694 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1522 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +582 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +74 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +397 -0
  178. sqlalchemy/sql/_dml_constructors.py +132 -0
  179. sqlalchemy/sql/_elements_constructors.py +2164 -0
  180. sqlalchemy/sql/_orm_types.py +20 -0
  181. sqlalchemy/sql/_selectable_constructors.py +840 -0
  182. sqlalchemy/sql/_typing.py +487 -0
  183. sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
  184. sqlalchemy/sql/_util_cy.py +127 -0
  185. sqlalchemy/sql/annotation.py +590 -0
  186. sqlalchemy/sql/base.py +2699 -0
  187. sqlalchemy/sql/cache_key.py +1066 -0
  188. sqlalchemy/sql/coercions.py +1373 -0
  189. sqlalchemy/sql/compiler.py +8327 -0
  190. sqlalchemy/sql/crud.py +1815 -0
  191. sqlalchemy/sql/ddl.py +1928 -0
  192. sqlalchemy/sql/default_comparator.py +654 -0
  193. sqlalchemy/sql/dml.py +1977 -0
  194. sqlalchemy/sql/elements.py +6033 -0
  195. sqlalchemy/sql/events.py +458 -0
  196. sqlalchemy/sql/expression.py +172 -0
  197. sqlalchemy/sql/functions.py +2305 -0
  198. sqlalchemy/sql/lambdas.py +1443 -0
  199. sqlalchemy/sql/naming.py +209 -0
  200. sqlalchemy/sql/operators.py +2897 -0
  201. sqlalchemy/sql/roles.py +332 -0
  202. sqlalchemy/sql/schema.py +6703 -0
  203. sqlalchemy/sql/selectable.py +7553 -0
  204. sqlalchemy/sql/sqltypes.py +4093 -0
  205. sqlalchemy/sql/traversals.py +1042 -0
  206. sqlalchemy/sql/type_api.py +2446 -0
  207. sqlalchemy/sql/util.py +1495 -0
  208. sqlalchemy/sql/visitors.py +1157 -0
  209. sqlalchemy/testing/__init__.py +96 -0
  210. sqlalchemy/testing/assertions.py +1007 -0
  211. sqlalchemy/testing/assertsql.py +519 -0
  212. sqlalchemy/testing/asyncio.py +128 -0
  213. sqlalchemy/testing/config.py +440 -0
  214. sqlalchemy/testing/engines.py +483 -0
  215. sqlalchemy/testing/entities.py +117 -0
  216. sqlalchemy/testing/exclusions.py +476 -0
  217. sqlalchemy/testing/fixtures/__init__.py +30 -0
  218. sqlalchemy/testing/fixtures/base.py +384 -0
  219. sqlalchemy/testing/fixtures/mypy.py +247 -0
  220. sqlalchemy/testing/fixtures/orm.py +227 -0
  221. sqlalchemy/testing/fixtures/sql.py +538 -0
  222. sqlalchemy/testing/pickleable.py +155 -0
  223. sqlalchemy/testing/plugin/__init__.py +6 -0
  224. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  225. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  226. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  227. sqlalchemy/testing/profiling.py +329 -0
  228. sqlalchemy/testing/provision.py +613 -0
  229. sqlalchemy/testing/requirements.py +1978 -0
  230. sqlalchemy/testing/schema.py +198 -0
  231. sqlalchemy/testing/suite/__init__.py +19 -0
  232. sqlalchemy/testing/suite/test_cte.py +237 -0
  233. sqlalchemy/testing/suite/test_ddl.py +420 -0
  234. sqlalchemy/testing/suite/test_dialect.py +776 -0
  235. sqlalchemy/testing/suite/test_insert.py +630 -0
  236. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  237. sqlalchemy/testing/suite/test_results.py +660 -0
  238. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  239. sqlalchemy/testing/suite/test_select.py +2112 -0
  240. sqlalchemy/testing/suite/test_sequence.py +317 -0
  241. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  242. sqlalchemy/testing/suite/test_types.py +2271 -0
  243. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  244. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  245. sqlalchemy/testing/util.py +535 -0
  246. sqlalchemy/testing/warnings.py +52 -0
  247. sqlalchemy/types.py +76 -0
  248. sqlalchemy/util/__init__.py +158 -0
  249. sqlalchemy/util/_collections.py +688 -0
  250. sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
  251. sqlalchemy/util/_collections_cy.pxd +8 -0
  252. sqlalchemy/util/_collections_cy.py +516 -0
  253. sqlalchemy/util/_has_cython.py +46 -0
  254. sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
  255. sqlalchemy/util/_immutabledict_cy.py +240 -0
  256. sqlalchemy/util/compat.py +299 -0
  257. sqlalchemy/util/concurrency.py +322 -0
  258. sqlalchemy/util/cython.py +79 -0
  259. sqlalchemy/util/deprecations.py +401 -0
  260. sqlalchemy/util/langhelpers.py +2320 -0
  261. sqlalchemy/util/preloaded.py +152 -0
  262. sqlalchemy/util/queue.py +304 -0
  263. sqlalchemy/util/tool_support.py +201 -0
  264. sqlalchemy/util/topological.py +120 -0
  265. sqlalchemy/util/typing.py +711 -0
  266. sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
  267. sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
  268. sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
  269. sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
  270. sqlalchemy-2.1.0b2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,296 @@
1
+ # dialects/mysql/mysqlconnector.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
+
8
+
9
+ r"""
10
+ .. dialect:: mysql+mysqlconnector
11
+ :name: MySQL Connector/Python
12
+ :dbapi: myconnpy
13
+ :connectstring: mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
14
+ :url: https://pypi.org/project/mysql-connector-python/
15
+
16
+ Driver Status
17
+ -------------
18
+
19
+ MySQL Connector/Python is supported as of SQLAlchemy 2.0.39 to the
20
+ degree which the driver is functional. There are still ongoing issues
21
+ with features such as server side cursors which remain disabled until
22
+ upstream issues are repaired.
23
+
24
+ .. warning:: The MySQL Connector/Python driver published by Oracle is subject
25
+ to frequent, major regressions of essential functionality such as being able
26
+ to correctly persist simple binary strings which indicate it is not well
27
+ tested. The SQLAlchemy project is not able to maintain this dialect fully as
28
+ regressions in the driver prevent it from being included in continuous
29
+ integration.
30
+
31
+ .. versionchanged:: 2.0.39
32
+
33
+ The MySQL Connector/Python dialect has been updated to support the
34
+ latest version of this DBAPI. Previously, MySQL Connector/Python
35
+ was not fully supported. However, support remains limited due to ongoing
36
+ regressions introduced in this driver.
37
+
38
+ Connecting to MariaDB with MySQL Connector/Python
39
+ --------------------------------------------------
40
+
41
+ MySQL Connector/Python may attempt to pass an incompatible collation to the
42
+ database when connecting to MariaDB. Experimentation has shown that using
43
+ ``?charset=utf8mb4&collation=utfmb4_general_ci`` or similar MariaDB-compatible
44
+ charset/collation will allow connectivity.
45
+
46
+
47
+ """ # noqa
48
+ from __future__ import annotations
49
+
50
+ import re
51
+ from typing import Any
52
+ from typing import cast
53
+ from typing import Optional
54
+ from typing import Sequence
55
+ from typing import TYPE_CHECKING
56
+ from typing import Union
57
+
58
+ from .base import MySQLCompiler
59
+ from .base import MySQLDialect
60
+ from .base import MySQLExecutionContext
61
+ from .base import MySQLIdentifierPreparer
62
+ from .mariadb import MariaDBDialect
63
+ from .types import BIT
64
+ from ... import util
65
+
66
+ if TYPE_CHECKING:
67
+
68
+ from ...engine.base import Connection
69
+ from ...engine.cursor import CursorResult
70
+ from ...engine.interfaces import ConnectArgsType
71
+ from ...engine.interfaces import DBAPIConnection
72
+ from ...engine.interfaces import DBAPICursor
73
+ from ...engine.interfaces import DBAPIModule
74
+ from ...engine.interfaces import IsolationLevel
75
+ from ...engine.interfaces import PoolProxiedConnection
76
+ from ...engine.row import Row
77
+ from ...engine.url import URL
78
+ from ...sql.elements import BinaryExpression
79
+ from ...util.typing import TupleAny
80
+ from ...util.typing import Unpack
81
+
82
+
83
+ class MySQLExecutionContext_mysqlconnector(MySQLExecutionContext):
84
+ def create_server_side_cursor(self) -> DBAPICursor:
85
+ return self._dbapi_connection.cursor(buffered=False)
86
+
87
+ def create_default_cursor(self) -> DBAPICursor:
88
+ return self._dbapi_connection.cursor(buffered=True)
89
+
90
+
91
+ class MySQLCompiler_mysqlconnector(MySQLCompiler):
92
+ def visit_mod_binary(
93
+ self, binary: BinaryExpression[Any], operator: Any, **kw: Any
94
+ ) -> str:
95
+ return (
96
+ self.process(binary.left, **kw)
97
+ + " % "
98
+ + self.process(binary.right, **kw)
99
+ )
100
+
101
+
102
+ class IdentifierPreparerCommon_mysqlconnector:
103
+ @property
104
+ def _double_percents(self) -> bool:
105
+ return False
106
+
107
+ @_double_percents.setter
108
+ def _double_percents(self, value: Any) -> None:
109
+ pass
110
+
111
+ def _escape_identifier(self, value: str) -> str:
112
+ value = value.replace(
113
+ self.escape_quote, # type:ignore[attr-defined]
114
+ self.escape_to_quote, # type:ignore[attr-defined]
115
+ )
116
+ return value
117
+
118
+
119
+ class MySQLIdentifierPreparer_mysqlconnector(
120
+ IdentifierPreparerCommon_mysqlconnector, MySQLIdentifierPreparer
121
+ ):
122
+ pass
123
+
124
+
125
+ class _myconnpyBIT(BIT):
126
+ def result_processor(self, dialect: Any, coltype: Any) -> None:
127
+ """MySQL-connector already converts mysql bits, so."""
128
+
129
+ return None
130
+
131
+
132
+ class MySQLDialect_mysqlconnector(MySQLDialect):
133
+ driver = "mysqlconnector"
134
+ supports_statement_cache = True
135
+
136
+ supports_sane_rowcount = True
137
+ supports_sane_multi_rowcount = True
138
+
139
+ supports_native_decimal = True
140
+
141
+ supports_native_bit = True
142
+
143
+ # not until https://bugs.mysql.com/bug.php?id=117548
144
+ supports_server_side_cursors = False
145
+
146
+ default_paramstyle = "format"
147
+ statement_compiler = MySQLCompiler_mysqlconnector
148
+
149
+ execution_ctx_cls = MySQLExecutionContext_mysqlconnector
150
+
151
+ preparer: type[MySQLIdentifierPreparer] = (
152
+ MySQLIdentifierPreparer_mysqlconnector
153
+ )
154
+
155
+ colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _myconnpyBIT})
156
+
157
+ @classmethod
158
+ def import_dbapi(cls) -> DBAPIModule:
159
+ return cast("DBAPIModule", __import__("mysql.connector").connector)
160
+
161
+ def do_ping(self, dbapi_connection: DBAPIConnection) -> bool:
162
+ dbapi_connection.ping(False)
163
+ return True
164
+
165
+ def create_connect_args(self, url: URL) -> ConnectArgsType:
166
+ opts = url.translate_connect_args(username="user")
167
+
168
+ opts.update(url.query)
169
+
170
+ util.coerce_kw_type(opts, "allow_local_infile", bool)
171
+ util.coerce_kw_type(opts, "autocommit", bool)
172
+ util.coerce_kw_type(opts, "buffered", bool)
173
+ util.coerce_kw_type(opts, "client_flag", int)
174
+ util.coerce_kw_type(opts, "compress", bool)
175
+ util.coerce_kw_type(opts, "connection_timeout", int)
176
+ util.coerce_kw_type(opts, "connect_timeout", int)
177
+ util.coerce_kw_type(opts, "consume_results", bool)
178
+ util.coerce_kw_type(opts, "force_ipv6", bool)
179
+ util.coerce_kw_type(opts, "get_warnings", bool)
180
+ util.coerce_kw_type(opts, "pool_reset_session", bool)
181
+ util.coerce_kw_type(opts, "pool_size", int)
182
+ util.coerce_kw_type(opts, "raise_on_warnings", bool)
183
+ util.coerce_kw_type(opts, "raw", bool)
184
+ util.coerce_kw_type(opts, "ssl_verify_cert", bool)
185
+ util.coerce_kw_type(opts, "use_pure", bool)
186
+ util.coerce_kw_type(opts, "use_unicode", bool)
187
+
188
+ # note that "buffered" is set to False by default in MySQL/connector
189
+ # python. If you set it to True, then there is no way to get a server
190
+ # side cursor because the logic is written to disallow that.
191
+
192
+ # leaving this at True until
193
+ # https://bugs.mysql.com/bug.php?id=117548 can be fixed
194
+ opts["buffered"] = True
195
+
196
+ # FOUND_ROWS must be set in ClientFlag to enable
197
+ # supports_sane_rowcount.
198
+ if self.dbapi is not None:
199
+ try:
200
+ from mysql.connector import constants # type: ignore
201
+
202
+ ClientFlag = constants.ClientFlag
203
+
204
+ client_flags = opts.get(
205
+ "client_flags", ClientFlag.get_default()
206
+ )
207
+ client_flags |= ClientFlag.FOUND_ROWS
208
+ opts["client_flags"] = client_flags
209
+ except Exception:
210
+ pass
211
+
212
+ return [], opts
213
+
214
+ @util.memoized_property
215
+ def _mysqlconnector_version_info(self) -> Optional[tuple[int, ...]]:
216
+ if self.dbapi and hasattr(self.dbapi, "__version__"):
217
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__)
218
+ if m:
219
+ return tuple(int(x) for x in m.group(1, 2, 3) if x is not None)
220
+ return None
221
+
222
+ def _detect_charset(self, connection: Connection) -> str:
223
+ return connection.connection.charset # type: ignore
224
+
225
+ def _extract_error_code(self, exception: BaseException) -> int:
226
+ return exception.errno # type: ignore
227
+
228
+ def is_disconnect(
229
+ self,
230
+ e: Exception,
231
+ connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
232
+ cursor: Optional[DBAPICursor],
233
+ ) -> bool:
234
+ errnos = (2006, 2013, 2014, 2045, 2055, 2048)
235
+ exceptions = (
236
+ self.loaded_dbapi.OperationalError, #
237
+ self.loaded_dbapi.InterfaceError,
238
+ self.loaded_dbapi.ProgrammingError,
239
+ )
240
+ if isinstance(e, exceptions):
241
+ return (
242
+ e.errno in errnos
243
+ or "MySQL Connection not available." in str(e)
244
+ or "Connection to MySQL is not available" in str(e)
245
+ )
246
+ else:
247
+ return False
248
+
249
+ def _compat_fetchall(
250
+ self,
251
+ rp: CursorResult[Unpack[TupleAny]],
252
+ charset: Optional[str] = None,
253
+ ) -> Sequence[Row[Unpack[TupleAny]]]:
254
+ return rp.fetchall()
255
+
256
+ def _compat_fetchone(
257
+ self,
258
+ rp: CursorResult[Unpack[TupleAny]],
259
+ charset: Optional[str] = None,
260
+ ) -> Optional[Row[Unpack[TupleAny]]]:
261
+ return rp.fetchone()
262
+
263
+ def get_isolation_level_values(
264
+ self, dbapi_conn: DBAPIConnection
265
+ ) -> Sequence[IsolationLevel]:
266
+ return (
267
+ "SERIALIZABLE",
268
+ "READ UNCOMMITTED",
269
+ "READ COMMITTED",
270
+ "REPEATABLE READ",
271
+ "AUTOCOMMIT",
272
+ )
273
+
274
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
275
+ return bool(dbapi_conn.autocommit)
276
+
277
+ def set_isolation_level(
278
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
279
+ ) -> None:
280
+ if level == "AUTOCOMMIT":
281
+ dbapi_connection.autocommit = True
282
+ else:
283
+ dbapi_connection.autocommit = False
284
+ super().set_isolation_level(dbapi_connection, level)
285
+
286
+
287
+ class MariaDBDialect_mysqlconnector(
288
+ MariaDBDialect, MySQLDialect_mysqlconnector
289
+ ):
290
+ supports_statement_cache = True
291
+ _allows_uuid_binds = False
292
+ preparer = MySQLIdentifierPreparer_mysqlconnector
293
+
294
+
295
+ dialect = MySQLDialect_mysqlconnector
296
+ mariadb_dialect = MariaDBDialect_mysqlconnector
@@ -0,0 +1,312 @@
1
+ # dialects/mysql/mysqldb.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
+
8
+ """
9
+
10
+ .. dialect:: mysql+mysqldb
11
+ :name: mysqlclient (maintained fork of MySQL-Python)
12
+ :dbapi: mysqldb
13
+ :connectstring: mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
14
+ :url: https://pypi.org/project/mysqlclient/
15
+
16
+ Driver Status
17
+ -------------
18
+
19
+ The mysqlclient DBAPI is a maintained fork of the
20
+ `MySQL-Python <https://sourceforge.net/projects/mysql-python>`_ DBAPI
21
+ that is no longer maintained. `mysqlclient`_ supports Python 2 and Python 3
22
+ and is very stable.
23
+
24
+ .. _mysqlclient: https://github.com/PyMySQL/mysqlclient-python
25
+
26
+ .. _mysqldb_unicode:
27
+
28
+ Unicode
29
+ -------
30
+
31
+ Please see :ref:`mysql_unicode` for current recommendations on unicode
32
+ handling.
33
+
34
+ .. _mysqldb_ssl:
35
+
36
+ SSL Connections
37
+ ----------------
38
+
39
+ The mysqlclient and PyMySQL DBAPIs accept an additional dictionary under the
40
+ key "ssl", which may be specified using the
41
+ :paramref:`_sa.create_engine.connect_args` dictionary::
42
+
43
+ engine = create_engine(
44
+ "mysql+mysqldb://scott:tiger@192.168.0.134/test",
45
+ connect_args={
46
+ "ssl": {
47
+ "ca": "/home/gord/client-ssl/ca.pem",
48
+ "cert": "/home/gord/client-ssl/client-cert.pem",
49
+ "key": "/home/gord/client-ssl/client-key.pem",
50
+ }
51
+ },
52
+ )
53
+
54
+ For convenience, the following keys may also be specified inline within the URL
55
+ where they will be interpreted into the "ssl" dictionary automatically:
56
+ "ssl_ca", "ssl_cert", "ssl_key", "ssl_capath", "ssl_cipher",
57
+ "ssl_check_hostname". An example is as follows::
58
+
59
+ connection_uri = (
60
+ "mysql+mysqldb://scott:tiger@192.168.0.134/test"
61
+ "?ssl_ca=/home/gord/client-ssl/ca.pem"
62
+ "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
63
+ "&ssl_key=/home/gord/client-ssl/client-key.pem"
64
+ )
65
+
66
+ .. seealso::
67
+
68
+ :ref:`pymysql_ssl` in the PyMySQL dialect
69
+
70
+
71
+ Using MySQLdb with Google Cloud SQL
72
+ -----------------------------------
73
+
74
+ Google Cloud SQL now recommends use of the MySQLdb dialect. Connect
75
+ using a URL like the following:
76
+
77
+ .. sourcecode:: text
78
+
79
+ mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>
80
+
81
+ Server Side Cursors
82
+ -------------------
83
+
84
+ The mysqldb dialect supports server-side cursors. See :ref:`mysql_ss_cursors`.
85
+
86
+ """
87
+ from __future__ import annotations
88
+
89
+ import re
90
+ from typing import Any
91
+ from typing import Callable
92
+ from typing import cast
93
+ from typing import Literal
94
+ from typing import Optional
95
+ from typing import TYPE_CHECKING
96
+
97
+ from .base import MySQLCompiler
98
+ from .base import MySQLDialect
99
+ from .base import MySQLExecutionContext
100
+ from .base import MySQLIdentifierPreparer
101
+ from ... import util
102
+
103
+ if TYPE_CHECKING:
104
+
105
+ from ...engine.base import Connection
106
+ from ...engine.interfaces import _DBAPIMultiExecuteParams
107
+ from ...engine.interfaces import ConnectArgsType
108
+ from ...engine.interfaces import DBAPIConnection
109
+ from ...engine.interfaces import DBAPICursor
110
+ from ...engine.interfaces import DBAPIModule
111
+ from ...engine.interfaces import ExecutionContext
112
+ from ...engine.interfaces import IsolationLevel
113
+ from ...engine.url import URL
114
+
115
+
116
+ class MySQLExecutionContext_mysqldb(MySQLExecutionContext):
117
+ pass
118
+
119
+
120
+ class MySQLCompiler_mysqldb(MySQLCompiler):
121
+ pass
122
+
123
+
124
+ class MySQLDialect_mysqldb(MySQLDialect):
125
+ driver = "mysqldb"
126
+ supports_statement_cache = True
127
+ supports_unicode_statements = True
128
+ supports_sane_rowcount = True
129
+ supports_sane_multi_rowcount = True
130
+
131
+ supports_native_decimal = True
132
+
133
+ default_paramstyle = "format"
134
+ execution_ctx_cls = MySQLExecutionContext_mysqldb
135
+ statement_compiler = MySQLCompiler_mysqldb
136
+ preparer = MySQLIdentifierPreparer
137
+ server_version_info: tuple[int, ...]
138
+
139
+ def __init__(self, **kwargs: Any):
140
+ super().__init__(**kwargs)
141
+ self._mysql_dbapi_version = (
142
+ self._parse_dbapi_version(self.dbapi.__version__)
143
+ if self.dbapi is not None and hasattr(self.dbapi, "__version__")
144
+ else (0, 0, 0)
145
+ )
146
+
147
+ def _parse_dbapi_version(self, version: str) -> tuple[int, ...]:
148
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", version)
149
+ if m:
150
+ return tuple(int(x) for x in m.group(1, 2, 3) if x is not None)
151
+ else:
152
+ return (0, 0, 0)
153
+
154
+ @util.langhelpers.memoized_property
155
+ def supports_server_side_cursors(self) -> bool:
156
+ try:
157
+ cursors = __import__("MySQLdb.cursors").cursors
158
+ self._sscursor = cursors.SSCursor
159
+ return True
160
+ except (ImportError, AttributeError):
161
+ return False
162
+
163
+ @classmethod
164
+ def import_dbapi(cls) -> DBAPIModule:
165
+ return __import__("MySQLdb")
166
+
167
+ def on_connect(self) -> Callable[[DBAPIConnection], None]:
168
+ super_ = super().on_connect()
169
+
170
+ def on_connect(conn: DBAPIConnection) -> None:
171
+ if super_ is not None:
172
+ super_(conn)
173
+
174
+ charset_name = conn.character_set_name()
175
+
176
+ if charset_name is not None:
177
+ cursor = conn.cursor()
178
+ cursor.execute("SET NAMES %s" % charset_name)
179
+ cursor.close()
180
+
181
+ return on_connect
182
+
183
+ def do_ping(self, dbapi_connection: DBAPIConnection) -> Literal[True]:
184
+ dbapi_connection.ping()
185
+ return True
186
+
187
+ def do_executemany(
188
+ self,
189
+ cursor: DBAPICursor,
190
+ statement: str,
191
+ parameters: _DBAPIMultiExecuteParams,
192
+ context: Optional[ExecutionContext] = None,
193
+ ) -> None:
194
+ rowcount = cursor.executemany(statement, parameters)
195
+ if context is not None:
196
+ cast(MySQLExecutionContext, context)._rowcount = rowcount
197
+
198
+ def create_connect_args(
199
+ self, url: URL, _translate_args: Optional[dict[str, Any]] = None
200
+ ) -> ConnectArgsType:
201
+ if _translate_args is None:
202
+ _translate_args = dict(
203
+ database="db", username="user", password="passwd"
204
+ )
205
+
206
+ opts = url.translate_connect_args(**_translate_args)
207
+ opts.update(url.query)
208
+
209
+ util.coerce_kw_type(opts, "compress", bool)
210
+ util.coerce_kw_type(opts, "connect_timeout", int)
211
+ util.coerce_kw_type(opts, "read_timeout", int)
212
+ util.coerce_kw_type(opts, "write_timeout", int)
213
+ util.coerce_kw_type(opts, "client_flag", int)
214
+ util.coerce_kw_type(opts, "local_infile", bool)
215
+ # Note: using either of the below will cause all strings to be
216
+ # returned as Unicode, both in raw SQL operations and with column
217
+ # types like String and MSString.
218
+ util.coerce_kw_type(opts, "use_unicode", bool)
219
+ util.coerce_kw_type(opts, "charset", str)
220
+
221
+ # Rich values 'cursorclass' and 'conv' are not supported via
222
+ # query string.
223
+
224
+ ssl = {}
225
+ keys = [
226
+ ("ssl_ca", str),
227
+ ("ssl_key", str),
228
+ ("ssl_cert", str),
229
+ ("ssl_capath", str),
230
+ ("ssl_cipher", str),
231
+ ("ssl_check_hostname", bool),
232
+ ]
233
+ for key, kw_type in keys:
234
+ if key in opts:
235
+ ssl[key[4:]] = opts[key]
236
+ util.coerce_kw_type(ssl, key[4:], kw_type)
237
+ del opts[key]
238
+ if ssl:
239
+ opts["ssl"] = ssl
240
+
241
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
242
+ # supports_sane_rowcount.
243
+ client_flag = opts.get("client_flag", 0)
244
+
245
+ client_flag_found_rows = self._found_rows_client_flag()
246
+ if client_flag_found_rows is not None:
247
+ client_flag |= client_flag_found_rows
248
+ opts["client_flag"] = client_flag
249
+ return [], opts
250
+
251
+ def _found_rows_client_flag(self) -> Optional[int]:
252
+ if self.dbapi is not None:
253
+ try:
254
+ CLIENT_FLAGS = __import__(
255
+ self.dbapi.__name__ + ".constants.CLIENT"
256
+ ).constants.CLIENT
257
+ except (AttributeError, ImportError):
258
+ return None
259
+ else:
260
+ return CLIENT_FLAGS.FOUND_ROWS # type: ignore
261
+ else:
262
+ return None
263
+
264
+ def _extract_error_code(self, exception: DBAPIModule.Error) -> int:
265
+ return exception.args[0] # type: ignore[no-any-return]
266
+
267
+ def _detect_charset(self, connection: Connection) -> str:
268
+ """Sniff out the character set in use for connection results."""
269
+
270
+ try:
271
+ # note: the SQL here would be
272
+ # "SHOW VARIABLES LIKE 'character_set%%'"
273
+
274
+ cset_name: Callable[[], str] = (
275
+ connection.connection.character_set_name
276
+ )
277
+ except AttributeError:
278
+ util.warn(
279
+ "No 'character_set_name' can be detected with "
280
+ "this MySQL-Python version; "
281
+ "please upgrade to a recent version of MySQL-Python. "
282
+ "Assuming latin1."
283
+ )
284
+ return "latin1"
285
+ else:
286
+ return cset_name()
287
+
288
+ def get_isolation_level_values(
289
+ self, dbapi_conn: DBAPIConnection
290
+ ) -> tuple[IsolationLevel, ...]:
291
+ return (
292
+ "SERIALIZABLE",
293
+ "READ UNCOMMITTED",
294
+ "READ COMMITTED",
295
+ "REPEATABLE READ",
296
+ "AUTOCOMMIT",
297
+ )
298
+
299
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
300
+ return dbapi_conn.get_autocommit() # type: ignore[no-any-return]
301
+
302
+ def set_isolation_level(
303
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
304
+ ) -> None:
305
+ if level == "AUTOCOMMIT":
306
+ dbapi_connection.autocommit(True)
307
+ else:
308
+ dbapi_connection.autocommit(False)
309
+ super().set_isolation_level(dbapi_connection, level)
310
+
311
+
312
+ dialect = MySQLDialect_mysqldb