SQLAlchemy 2.1.0rc1__cp315-cp315-win32.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 (276) hide show
  1. sqlalchemy/__init__.py +299 -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 +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -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 +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -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 +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win32.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win32.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win32.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win32.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -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 +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -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 +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -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 +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win32.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win32.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win32.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win32.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,291 @@
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
+
49
+ from __future__ import annotations
50
+
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[import-not-found] # noqa: E501
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
+ def retrieve_dbapi_version(self, dbapi: DBAPIModule) -> util.VersionInfo:
215
+ return util.parse_version_string(getattr(dbapi, "__version__", None))
216
+
217
+ def _detect_charset(self, connection: Connection) -> str:
218
+ return connection.connection.charset # type: ignore[no-any-return]
219
+
220
+ def _extract_error_code(self, exception: BaseException) -> int:
221
+ return exception.errno # type: ignore[attr-defined, no-any-return]
222
+
223
+ def is_disconnect(
224
+ self,
225
+ e: Exception,
226
+ connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
227
+ cursor: Optional[DBAPICursor],
228
+ ) -> bool:
229
+ errnos = (2006, 2013, 2014, 2045, 2055, 2048)
230
+ exceptions = (
231
+ self.loaded_dbapi.OperationalError, #
232
+ self.loaded_dbapi.InterfaceError,
233
+ self.loaded_dbapi.ProgrammingError,
234
+ )
235
+ if isinstance(e, exceptions):
236
+ return (
237
+ e.errno in errnos
238
+ or "MySQL Connection not available." in str(e)
239
+ or "Connection to MySQL is not available" in str(e)
240
+ )
241
+ else:
242
+ return False
243
+
244
+ def _compat_fetchall(
245
+ self,
246
+ rp: CursorResult[Unpack[TupleAny]],
247
+ charset: Optional[str] = None,
248
+ ) -> Sequence[Row[Unpack[TupleAny]]]:
249
+ return rp.fetchall()
250
+
251
+ def _compat_fetchone(
252
+ self,
253
+ rp: CursorResult[Unpack[TupleAny]],
254
+ charset: Optional[str] = None,
255
+ ) -> Optional[Row[Unpack[TupleAny]]]:
256
+ return rp.fetchone()
257
+
258
+ def get_isolation_level_values(
259
+ self, dbapi_conn: DBAPIConnection
260
+ ) -> Sequence[IsolationLevel]:
261
+ return (
262
+ "SERIALIZABLE",
263
+ "READ UNCOMMITTED",
264
+ "READ COMMITTED",
265
+ "REPEATABLE READ",
266
+ "AUTOCOMMIT",
267
+ )
268
+
269
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
270
+ return bool(dbapi_conn.autocommit)
271
+
272
+ def set_isolation_level(
273
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
274
+ ) -> None:
275
+ if level == "AUTOCOMMIT":
276
+ dbapi_connection.autocommit = True
277
+ else:
278
+ dbapi_connection.autocommit = False
279
+ super().set_isolation_level(dbapi_connection, level)
280
+
281
+
282
+ class MariaDBDialect_mysqlconnector(
283
+ MariaDBDialect, MySQLDialect_mysqlconnector
284
+ ):
285
+ supports_statement_cache = True
286
+ _allows_uuid_binds = False
287
+ preparer = MySQLIdentifierPreparer_mysqlconnector
288
+
289
+
290
+ dialect = MySQLDialect_mysqlconnector
291
+ mariadb_dialect = MariaDBDialect_mysqlconnector
@@ -0,0 +1,318 @@
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
+
88
+ from __future__ import annotations
89
+
90
+ import itertools
91
+ from typing import Any
92
+ from typing import Callable
93
+ from typing import cast
94
+ from typing import Literal
95
+ from typing import Optional
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
+
104
+ if TYPE_CHECKING:
105
+
106
+ from ...engine.base import Connection
107
+ from ...engine.interfaces import _DBAPIMultiExecuteParams
108
+ from ...engine.interfaces import ConnectArgsType
109
+ from ...engine.interfaces import DBAPIConnection
110
+ from ...engine.interfaces import DBAPICursor
111
+ from ...engine.interfaces import DBAPIModule
112
+ from ...engine.interfaces import ExecutionContext
113
+ from ...engine.interfaces import IsolationLevel
114
+ from ...engine.url import URL
115
+
116
+
117
+ class MySQLExecutionContext_mysqldb(MySQLExecutionContext):
118
+ pass
119
+
120
+
121
+ class MySQLCompiler_mysqldb(MySQLCompiler):
122
+ pass
123
+
124
+
125
+ class MySQLDialect_mysqldb(MySQLDialect):
126
+ driver = "mysqldb"
127
+ supports_statement_cache = True
128
+ supports_unicode_statements = True
129
+ supports_sane_rowcount = True
130
+ supports_sane_multi_rowcount = True
131
+
132
+ supports_native_decimal = True
133
+
134
+ default_paramstyle = "format"
135
+ execution_ctx_cls = MySQLExecutionContext_mysqldb
136
+ statement_compiler = MySQLCompiler_mysqldb
137
+ preparer = MySQLIdentifierPreparer
138
+ server_version_info: tuple[int, ...]
139
+
140
+ def retrieve_dbapi_version(self, dbapi: DBAPIModule) -> util.VersionInfo:
141
+ # mysqlclient publishes ``version_info``, a tuple in the style of
142
+ # ``sys.version_info`` such as ``(2, 2, 7, "final", 0)``, and no
143
+ # version string of its own; MySQL-python published
144
+ # ``__version__``. cymysql, which subclasses this dialect, also
145
+ # publishes ``__version__``. pymysql publishes both of these as
146
+ # mysqlclient compatibility values rather than as its own version,
147
+ # so that dialect overrides this method; the asyncio dialects
148
+ # likewise override as their DBAPI is a wrapper module.
149
+ version_info = getattr(dbapi, "version_info", None)
150
+ if version_info is not None:
151
+ return util.VersionInfo(
152
+ tuple(
153
+ itertools.takewhile(
154
+ lambda part: isinstance(part, int), version_info
155
+ )
156
+ )
157
+ )
158
+ return util.parse_version_string(getattr(dbapi, "__version__", None))
159
+
160
+ @util.langhelpers.memoized_property
161
+ def supports_server_side_cursors(self) -> bool:
162
+ try:
163
+ cursors = __import__("MySQLdb.cursors").cursors
164
+ self._sscursor = cursors.SSCursor
165
+ return True
166
+ except (ImportError, AttributeError):
167
+ return False
168
+
169
+ @classmethod
170
+ def import_dbapi(cls) -> DBAPIModule:
171
+ return __import__("MySQLdb")
172
+
173
+ def on_connect(self) -> Callable[[DBAPIConnection], None]:
174
+ super_ = super().on_connect()
175
+
176
+ def on_connect(conn: DBAPIConnection) -> None:
177
+ if super_ is not None:
178
+ super_(conn)
179
+
180
+ charset_name = conn.character_set_name()
181
+
182
+ if charset_name is not None:
183
+ cursor = conn.cursor()
184
+ cursor.execute("SET NAMES %s" % charset_name)
185
+ cursor.close()
186
+
187
+ return on_connect
188
+
189
+ def do_ping(self, dbapi_connection: DBAPIConnection) -> Literal[True]:
190
+ dbapi_connection.ping()
191
+ return True
192
+
193
+ def do_executemany(
194
+ self,
195
+ cursor: DBAPICursor,
196
+ statement: str,
197
+ parameters: _DBAPIMultiExecuteParams,
198
+ context: Optional[ExecutionContext] = None,
199
+ ) -> None:
200
+ rowcount = cursor.executemany(statement, parameters)
201
+ if context is not None:
202
+ cast(MySQLExecutionContext, context)._rowcount = rowcount
203
+
204
+ def create_connect_args(
205
+ self, url: URL, _translate_args: Optional[dict[str, Any]] = None
206
+ ) -> ConnectArgsType:
207
+ if _translate_args is None:
208
+ _translate_args = dict(
209
+ database="db", username="user", password="passwd"
210
+ )
211
+
212
+ opts = url.translate_connect_args(**_translate_args)
213
+ opts.update(url.query)
214
+
215
+ util.coerce_kw_type(opts, "compress", bool)
216
+ util.coerce_kw_type(opts, "connect_timeout", int)
217
+ util.coerce_kw_type(opts, "read_timeout", int)
218
+ util.coerce_kw_type(opts, "write_timeout", int)
219
+ util.coerce_kw_type(opts, "client_flag", int)
220
+ util.coerce_kw_type(opts, "local_infile", bool)
221
+ # Note: using either of the below will cause all strings to be
222
+ # returned as Unicode, both in raw SQL operations and with column
223
+ # types like String and MSString.
224
+ util.coerce_kw_type(opts, "use_unicode", bool)
225
+ util.coerce_kw_type(opts, "charset", str)
226
+
227
+ # Rich values 'cursorclass' and 'conv' are not supported via
228
+ # query string.
229
+
230
+ ssl = {}
231
+ keys = [
232
+ ("ssl_ca", str),
233
+ ("ssl_key", str),
234
+ ("ssl_cert", str),
235
+ ("ssl_capath", str),
236
+ ("ssl_cipher", str),
237
+ ("ssl_check_hostname", bool),
238
+ ]
239
+ for key, kw_type in keys:
240
+ if key in opts:
241
+ ssl[key[4:]] = opts[key]
242
+ util.coerce_kw_type(ssl, key[4:], kw_type)
243
+ del opts[key]
244
+ if ssl:
245
+ opts["ssl"] = ssl
246
+
247
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
248
+ # supports_sane_rowcount.
249
+ client_flag = opts.get("client_flag", 0)
250
+
251
+ client_flag_found_rows = self._found_rows_client_flag()
252
+ if client_flag_found_rows is not None:
253
+ client_flag |= client_flag_found_rows
254
+ opts["client_flag"] = client_flag
255
+ return [], opts
256
+
257
+ def _found_rows_client_flag(self) -> Optional[int]:
258
+ if self.dbapi is not None:
259
+ try:
260
+ CLIENT_FLAGS = __import__(
261
+ self.dbapi.__name__ + ".constants.CLIENT"
262
+ ).constants.CLIENT
263
+ except (AttributeError, ImportError):
264
+ return None
265
+ else:
266
+ return CLIENT_FLAGS.FOUND_ROWS # type: ignore[no-any-return]
267
+ else:
268
+ return None
269
+
270
+ def _extract_error_code(self, exception: DBAPIModule.Error) -> int:
271
+ return exception.args[0] # type: ignore[no-any-return]
272
+
273
+ def _detect_charset(self, connection: Connection) -> str:
274
+ """Sniff out the character set in use for connection results."""
275
+
276
+ try:
277
+ # note: the SQL here would be
278
+ # "SHOW VARIABLES LIKE 'character_set%%'"
279
+
280
+ cset_name: Callable[[], str] = (
281
+ connection.connection.character_set_name
282
+ )
283
+ except AttributeError:
284
+ util.warn(
285
+ "No 'character_set_name' can be detected with "
286
+ "this MySQL-Python version; "
287
+ "please upgrade to a recent version of MySQL-Python. "
288
+ "Assuming latin1."
289
+ )
290
+ return "latin1"
291
+ else:
292
+ return cset_name()
293
+
294
+ def get_isolation_level_values(
295
+ self, dbapi_conn: DBAPIConnection
296
+ ) -> tuple[IsolationLevel, ...]:
297
+ return (
298
+ "SERIALIZABLE",
299
+ "READ UNCOMMITTED",
300
+ "READ COMMITTED",
301
+ "REPEATABLE READ",
302
+ "AUTOCOMMIT",
303
+ )
304
+
305
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
306
+ return dbapi_conn.get_autocommit() # type: ignore[no-any-return]
307
+
308
+ def set_isolation_level(
309
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
310
+ ) -> None:
311
+ if level == "AUTOCOMMIT":
312
+ dbapi_connection.autocommit(True)
313
+ else:
314
+ dbapi_connection.autocommit(False)
315
+ super().set_isolation_level(dbapi_connection, level)
316
+
317
+
318
+ dialect = MySQLDialect_mysqldb