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,302 @@
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 Tuple
56
+ from typing import TYPE_CHECKING
57
+ from typing import Union
58
+
59
+ from .base import MariaDBIdentifierPreparer
60
+ from .base import MySQLCompiler
61
+ from .base import MySQLDialect
62
+ from .base import MySQLExecutionContext
63
+ from .base import MySQLIdentifierPreparer
64
+ from .mariadb import MariaDBDialect
65
+ from .types import BIT
66
+ from ... import util
67
+
68
+ if TYPE_CHECKING:
69
+
70
+ from ...engine.base import Connection
71
+ from ...engine.cursor import CursorResult
72
+ from ...engine.interfaces import ConnectArgsType
73
+ from ...engine.interfaces import DBAPIConnection
74
+ from ...engine.interfaces import DBAPICursor
75
+ from ...engine.interfaces import DBAPIModule
76
+ from ...engine.interfaces import IsolationLevel
77
+ from ...engine.interfaces import PoolProxiedConnection
78
+ from ...engine.row import Row
79
+ from ...engine.url import URL
80
+ from ...sql.elements import BinaryExpression
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 MariaDBIdentifierPreparer_mysqlconnector(
126
+ IdentifierPreparerCommon_mysqlconnector, MariaDBIdentifierPreparer
127
+ ):
128
+ pass
129
+
130
+
131
+ class _myconnpyBIT(BIT):
132
+ def result_processor(self, dialect: Any, coltype: Any) -> None:
133
+ """MySQL-connector already converts mysql bits, so."""
134
+
135
+ return None
136
+
137
+
138
+ class MySQLDialect_mysqlconnector(MySQLDialect):
139
+ driver = "mysqlconnector"
140
+ supports_statement_cache = True
141
+
142
+ supports_sane_rowcount = True
143
+ supports_sane_multi_rowcount = True
144
+
145
+ supports_native_decimal = True
146
+
147
+ supports_native_bit = True
148
+
149
+ # not until https://bugs.mysql.com/bug.php?id=117548
150
+ supports_server_side_cursors = False
151
+
152
+ default_paramstyle = "format"
153
+ statement_compiler = MySQLCompiler_mysqlconnector
154
+
155
+ execution_ctx_cls = MySQLExecutionContext_mysqlconnector
156
+
157
+ preparer: type[MySQLIdentifierPreparer] = (
158
+ MySQLIdentifierPreparer_mysqlconnector
159
+ )
160
+
161
+ colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _myconnpyBIT})
162
+
163
+ @classmethod
164
+ def import_dbapi(cls) -> DBAPIModule:
165
+ return cast("DBAPIModule", __import__("mysql.connector").connector)
166
+
167
+ def do_ping(self, dbapi_connection: DBAPIConnection) -> bool:
168
+ dbapi_connection.ping(False)
169
+ return True
170
+
171
+ def create_connect_args(self, url: URL) -> ConnectArgsType:
172
+ opts = url.translate_connect_args(username="user")
173
+
174
+ opts.update(url.query)
175
+
176
+ util.coerce_kw_type(opts, "allow_local_infile", bool)
177
+ util.coerce_kw_type(opts, "autocommit", bool)
178
+ util.coerce_kw_type(opts, "buffered", bool)
179
+ util.coerce_kw_type(opts, "client_flag", int)
180
+ util.coerce_kw_type(opts, "compress", bool)
181
+ util.coerce_kw_type(opts, "connection_timeout", int)
182
+ util.coerce_kw_type(opts, "connect_timeout", int)
183
+ util.coerce_kw_type(opts, "consume_results", bool)
184
+ util.coerce_kw_type(opts, "force_ipv6", bool)
185
+ util.coerce_kw_type(opts, "get_warnings", bool)
186
+ util.coerce_kw_type(opts, "pool_reset_session", bool)
187
+ util.coerce_kw_type(opts, "pool_size", int)
188
+ util.coerce_kw_type(opts, "raise_on_warnings", bool)
189
+ util.coerce_kw_type(opts, "raw", bool)
190
+ util.coerce_kw_type(opts, "ssl_verify_cert", bool)
191
+ util.coerce_kw_type(opts, "use_pure", bool)
192
+ util.coerce_kw_type(opts, "use_unicode", bool)
193
+
194
+ # note that "buffered" is set to False by default in MySQL/connector
195
+ # python. If you set it to True, then there is no way to get a server
196
+ # side cursor because the logic is written to disallow that.
197
+
198
+ # leaving this at True until
199
+ # https://bugs.mysql.com/bug.php?id=117548 can be fixed
200
+ opts["buffered"] = True
201
+
202
+ # FOUND_ROWS must be set in ClientFlag to enable
203
+ # supports_sane_rowcount.
204
+ if self.dbapi is not None:
205
+ try:
206
+ from mysql.connector import constants # type: ignore
207
+
208
+ ClientFlag = constants.ClientFlag
209
+
210
+ client_flags = opts.get(
211
+ "client_flags", ClientFlag.get_default()
212
+ )
213
+ client_flags |= ClientFlag.FOUND_ROWS
214
+ opts["client_flags"] = client_flags
215
+ except Exception:
216
+ pass
217
+
218
+ return [], opts
219
+
220
+ @util.memoized_property
221
+ def _mysqlconnector_version_info(self) -> Optional[Tuple[int, ...]]:
222
+ if self.dbapi and hasattr(self.dbapi, "__version__"):
223
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__)
224
+ if m:
225
+ return tuple(int(x) for x in m.group(1, 2, 3) if x is not None)
226
+ return None
227
+
228
+ def _detect_charset(self, connection: Connection) -> str:
229
+ return connection.connection.charset # type: ignore
230
+
231
+ def _extract_error_code(self, exception: BaseException) -> int:
232
+ return exception.errno # type: ignore
233
+
234
+ def is_disconnect(
235
+ self,
236
+ e: Exception,
237
+ connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
238
+ cursor: Optional[DBAPICursor],
239
+ ) -> bool:
240
+ errnos = (2006, 2013, 2014, 2045, 2055, 2048)
241
+ exceptions = (
242
+ self.loaded_dbapi.OperationalError, #
243
+ self.loaded_dbapi.InterfaceError,
244
+ self.loaded_dbapi.ProgrammingError,
245
+ )
246
+ if isinstance(e, exceptions):
247
+ return (
248
+ e.errno in errnos
249
+ or "MySQL Connection not available." in str(e)
250
+ or "Connection to MySQL is not available" in str(e)
251
+ )
252
+ else:
253
+ return False
254
+
255
+ def _compat_fetchall(
256
+ self,
257
+ rp: CursorResult[Tuple[Any, ...]],
258
+ charset: Optional[str] = None,
259
+ ) -> Sequence[Row[Tuple[Any, ...]]]:
260
+ return rp.fetchall()
261
+
262
+ def _compat_fetchone(
263
+ self,
264
+ rp: CursorResult[Tuple[Any, ...]],
265
+ charset: Optional[str] = None,
266
+ ) -> Optional[Row[Tuple[Any, ...]]]:
267
+ return rp.fetchone()
268
+
269
+ def get_isolation_level_values(
270
+ self, dbapi_conn: DBAPIConnection
271
+ ) -> Sequence[IsolationLevel]:
272
+ return (
273
+ "SERIALIZABLE",
274
+ "READ UNCOMMITTED",
275
+ "READ COMMITTED",
276
+ "REPEATABLE READ",
277
+ "AUTOCOMMIT",
278
+ )
279
+
280
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
281
+ return bool(dbapi_conn.autocommit)
282
+
283
+ def set_isolation_level(
284
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
285
+ ) -> None:
286
+ if level == "AUTOCOMMIT":
287
+ dbapi_connection.autocommit = True
288
+ else:
289
+ dbapi_connection.autocommit = False
290
+ super().set_isolation_level(dbapi_connection, level)
291
+
292
+
293
+ class MariaDBDialect_mysqlconnector(
294
+ MariaDBDialect, MySQLDialect_mysqlconnector
295
+ ):
296
+ supports_statement_cache = True
297
+ _allows_uuid_binds = False
298
+ preparer = MariaDBIdentifierPreparer_mysqlconnector
299
+
300
+
301
+ dialect = MySQLDialect_mysqlconnector
302
+ mariadb_dialect = MariaDBDialect_mysqlconnector
@@ -0,0 +1,314 @@
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 Dict
94
+ from typing import Optional
95
+ from typing import Tuple
96
+ from typing import TYPE_CHECKING
97
+
98
+ from .base import MySQLCompiler
99
+ from .base import MySQLDialect
100
+ from .base import MySQLExecutionContext
101
+ from .base import MySQLIdentifierPreparer
102
+ from ... import util
103
+ from ...util.typing import Literal
104
+
105
+ if TYPE_CHECKING:
106
+
107
+ from ...engine.base import Connection
108
+ from ...engine.interfaces import _DBAPIMultiExecuteParams
109
+ from ...engine.interfaces import ConnectArgsType
110
+ from ...engine.interfaces import DBAPIConnection
111
+ from ...engine.interfaces import DBAPICursor
112
+ from ...engine.interfaces import DBAPIModule
113
+ from ...engine.interfaces import ExecutionContext
114
+ from ...engine.interfaces import IsolationLevel
115
+ from ...engine.url import URL
116
+
117
+
118
+ class MySQLExecutionContext_mysqldb(MySQLExecutionContext):
119
+ pass
120
+
121
+
122
+ class MySQLCompiler_mysqldb(MySQLCompiler):
123
+ pass
124
+
125
+
126
+ class MySQLDialect_mysqldb(MySQLDialect):
127
+ driver = "mysqldb"
128
+ supports_statement_cache = True
129
+ supports_unicode_statements = True
130
+ supports_sane_rowcount = True
131
+ supports_sane_multi_rowcount = True
132
+
133
+ supports_native_decimal = True
134
+
135
+ default_paramstyle = "format"
136
+ execution_ctx_cls = MySQLExecutionContext_mysqldb
137
+ statement_compiler = MySQLCompiler_mysqldb
138
+ preparer = MySQLIdentifierPreparer
139
+ server_version_info: Tuple[int, ...]
140
+
141
+ def __init__(self, **kwargs: Any):
142
+ super().__init__(**kwargs)
143
+ self._mysql_dbapi_version = (
144
+ self._parse_dbapi_version(self.dbapi.__version__)
145
+ if self.dbapi is not None and hasattr(self.dbapi, "__version__")
146
+ else (0, 0, 0)
147
+ )
148
+
149
+ def _parse_dbapi_version(self, version: str) -> Tuple[int, ...]:
150
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", version)
151
+ if m:
152
+ return tuple(int(x) for x in m.group(1, 2, 3) if x is not None)
153
+ else:
154
+ return (0, 0, 0)
155
+
156
+ @util.langhelpers.memoized_property
157
+ def supports_server_side_cursors(self) -> bool:
158
+ try:
159
+ cursors = __import__("MySQLdb.cursors").cursors
160
+ self._sscursor = cursors.SSCursor
161
+ return True
162
+ except (ImportError, AttributeError):
163
+ return False
164
+
165
+ @classmethod
166
+ def import_dbapi(cls) -> DBAPIModule:
167
+ return __import__("MySQLdb")
168
+
169
+ def on_connect(self) -> Callable[[DBAPIConnection], None]:
170
+ super_ = super().on_connect()
171
+
172
+ def on_connect(conn: DBAPIConnection) -> None:
173
+ if super_ is not None:
174
+ super_(conn)
175
+
176
+ charset_name = conn.character_set_name()
177
+
178
+ if charset_name is not None:
179
+ cursor = conn.cursor()
180
+ cursor.execute("SET NAMES %s" % charset_name)
181
+ cursor.close()
182
+
183
+ return on_connect
184
+
185
+ def do_ping(self, dbapi_connection: DBAPIConnection) -> Literal[True]:
186
+ dbapi_connection.ping()
187
+ return True
188
+
189
+ def do_executemany(
190
+ self,
191
+ cursor: DBAPICursor,
192
+ statement: str,
193
+ parameters: _DBAPIMultiExecuteParams,
194
+ context: Optional[ExecutionContext] = None,
195
+ ) -> None:
196
+ rowcount = cursor.executemany(statement, parameters)
197
+ if context is not None:
198
+ cast(MySQLExecutionContext, context)._rowcount = rowcount
199
+
200
+ def create_connect_args(
201
+ self, url: URL, _translate_args: Optional[Dict[str, Any]] = None
202
+ ) -> ConnectArgsType:
203
+ if _translate_args is None:
204
+ _translate_args = dict(
205
+ database="db", username="user", password="passwd"
206
+ )
207
+
208
+ opts = url.translate_connect_args(**_translate_args)
209
+ opts.update(url.query)
210
+
211
+ util.coerce_kw_type(opts, "compress", bool)
212
+ util.coerce_kw_type(opts, "connect_timeout", int)
213
+ util.coerce_kw_type(opts, "read_timeout", int)
214
+ util.coerce_kw_type(opts, "write_timeout", int)
215
+ util.coerce_kw_type(opts, "client_flag", int)
216
+ util.coerce_kw_type(opts, "local_infile", bool)
217
+ # Note: using either of the below will cause all strings to be
218
+ # returned as Unicode, both in raw SQL operations and with column
219
+ # types like String and MSString.
220
+ util.coerce_kw_type(opts, "use_unicode", bool)
221
+ util.coerce_kw_type(opts, "charset", str)
222
+
223
+ # Rich values 'cursorclass' and 'conv' are not supported via
224
+ # query string.
225
+
226
+ ssl = {}
227
+ keys = [
228
+ ("ssl_ca", str),
229
+ ("ssl_key", str),
230
+ ("ssl_cert", str),
231
+ ("ssl_capath", str),
232
+ ("ssl_cipher", str),
233
+ ("ssl_check_hostname", bool),
234
+ ]
235
+ for key, kw_type in keys:
236
+ if key in opts:
237
+ ssl[key[4:]] = opts[key]
238
+ util.coerce_kw_type(ssl, key[4:], kw_type)
239
+ del opts[key]
240
+ if ssl:
241
+ opts["ssl"] = ssl
242
+
243
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
244
+ # supports_sane_rowcount.
245
+ client_flag = opts.get("client_flag", 0)
246
+
247
+ client_flag_found_rows = self._found_rows_client_flag()
248
+ if client_flag_found_rows is not None:
249
+ client_flag |= client_flag_found_rows
250
+ opts["client_flag"] = client_flag
251
+ return [], opts
252
+
253
+ def _found_rows_client_flag(self) -> Optional[int]:
254
+ if self.dbapi is not None:
255
+ try:
256
+ CLIENT_FLAGS = __import__(
257
+ self.dbapi.__name__ + ".constants.CLIENT"
258
+ ).constants.CLIENT
259
+ except (AttributeError, ImportError):
260
+ return None
261
+ else:
262
+ return CLIENT_FLAGS.FOUND_ROWS # type: ignore
263
+ else:
264
+ return None
265
+
266
+ def _extract_error_code(self, exception: DBAPIModule.Error) -> int:
267
+ return exception.args[0] # type: ignore[no-any-return]
268
+
269
+ def _detect_charset(self, connection: Connection) -> str:
270
+ """Sniff out the character set in use for connection results."""
271
+
272
+ try:
273
+ # note: the SQL here would be
274
+ # "SHOW VARIABLES LIKE 'character_set%%'"
275
+
276
+ cset_name: Callable[[], str] = (
277
+ connection.connection.character_set_name
278
+ )
279
+ except AttributeError:
280
+ util.warn(
281
+ "No 'character_set_name' can be detected with "
282
+ "this MySQL-Python version; "
283
+ "please upgrade to a recent version of MySQL-Python. "
284
+ "Assuming latin1."
285
+ )
286
+ return "latin1"
287
+ else:
288
+ return cset_name()
289
+
290
+ def get_isolation_level_values(
291
+ self, dbapi_conn: DBAPIConnection
292
+ ) -> Tuple[IsolationLevel, ...]:
293
+ return (
294
+ "SERIALIZABLE",
295
+ "READ UNCOMMITTED",
296
+ "READ COMMITTED",
297
+ "REPEATABLE READ",
298
+ "AUTOCOMMIT",
299
+ )
300
+
301
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
302
+ return dbapi_conn.get_autocommit() # type: ignore[no-any-return]
303
+
304
+ def set_isolation_level(
305
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
306
+ ) -> None:
307
+ if level == "AUTOCOMMIT":
308
+ dbapi_connection.autocommit(True)
309
+ else:
310
+ dbapi_connection.autocommit(False)
311
+ super().set_isolation_level(dbapi_connection, level)
312
+
313
+
314
+ dialect = MySQLDialect_mysqldb