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,106 @@
1
+ # dialects/mysql/cymysql.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
+ r"""
9
+
10
+ .. dialect:: mysql+cymysql
11
+ :name: CyMySQL
12
+ :dbapi: cymysql
13
+ :connectstring: mysql+cymysql://<username>:<password>@<host>/<dbname>[?<options>]
14
+ :url: https://github.com/nakagami/CyMySQL
15
+
16
+ .. note::
17
+
18
+ The CyMySQL dialect is **not tested as part of SQLAlchemy's continuous
19
+ integration** and may have unresolved issues. The recommended MySQL
20
+ dialects are mysqlclient and PyMySQL.
21
+
22
+ """ # noqa
23
+ from __future__ import annotations
24
+
25
+ from typing import Any
26
+ from typing import Iterable
27
+ from typing import Optional
28
+ from typing import TYPE_CHECKING
29
+ from typing import Union
30
+
31
+ from .base import MySQLDialect
32
+ from .mysqldb import MySQLDialect_mysqldb
33
+ from .types import BIT
34
+ from ... import util
35
+
36
+ if TYPE_CHECKING:
37
+ from ...engine.base import Connection
38
+ from ...engine.interfaces import DBAPIConnection
39
+ from ...engine.interfaces import DBAPICursor
40
+ from ...engine.interfaces import DBAPIModule
41
+ from ...engine.interfaces import Dialect
42
+ from ...engine.interfaces import PoolProxiedConnection
43
+ from ...sql.type_api import _ResultProcessorType
44
+
45
+
46
+ class _cymysqlBIT(BIT):
47
+ def result_processor(
48
+ self, dialect: Dialect, coltype: object
49
+ ) -> Optional[_ResultProcessorType[Any]]:
50
+ """Convert MySQL's 64 bit, variable length binary string to a long."""
51
+
52
+ def process(value: Optional[Iterable[int]]) -> Optional[int]:
53
+ if value is not None:
54
+ v = 0
55
+ for i in iter(value):
56
+ v = v << 8 | i
57
+ return v
58
+ return value
59
+
60
+ return process
61
+
62
+
63
+ class MySQLDialect_cymysql(MySQLDialect_mysqldb):
64
+ driver = "cymysql"
65
+ supports_statement_cache = True
66
+
67
+ description_encoding = None
68
+ supports_sane_rowcount = True
69
+ supports_sane_multi_rowcount = False
70
+ supports_unicode_statements = True
71
+
72
+ colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _cymysqlBIT})
73
+
74
+ @classmethod
75
+ def import_dbapi(cls) -> DBAPIModule:
76
+ return __import__("cymysql")
77
+
78
+ def _detect_charset(self, connection: Connection) -> str:
79
+ return connection.connection.charset # type: ignore[no-any-return]
80
+
81
+ def _extract_error_code(self, exception: DBAPIModule.Error) -> int:
82
+ return exception.errno # type: ignore[no-any-return]
83
+
84
+ def is_disconnect(
85
+ self,
86
+ e: DBAPIModule.Error,
87
+ connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
88
+ cursor: Optional[DBAPICursor],
89
+ ) -> bool:
90
+ if isinstance(e, self.loaded_dbapi.OperationalError):
91
+ return self._extract_error_code(e) in (
92
+ 2006,
93
+ 2013,
94
+ 2014,
95
+ 2045,
96
+ 2055,
97
+ )
98
+ elif isinstance(e, self.loaded_dbapi.InterfaceError):
99
+ # if underlying connection is closed,
100
+ # this is the error you get
101
+ return True
102
+ else:
103
+ return False
104
+
105
+
106
+ dialect = MySQLDialect_cymysql
@@ -0,0 +1,279 @@
1
+ # dialects/mysql/dml.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
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+ from typing import Dict
11
+ from typing import List
12
+ from typing import Mapping
13
+ from typing import Optional
14
+ from typing import Tuple
15
+ from typing import TYPE_CHECKING
16
+ from typing import Union
17
+
18
+ from ... import exc
19
+ from ... import util
20
+ from ...sql import coercions
21
+ from ...sql import roles
22
+ from ...sql._typing import _DMLTableArgument
23
+ from ...sql.base import _exclusive_against
24
+ from ...sql.base import ColumnCollection
25
+ from ...sql.base import ReadOnlyColumnCollection
26
+ from ...sql.base import SyntaxExtension
27
+ from ...sql.dml import Insert as StandardInsert
28
+ from ...sql.elements import ClauseElement
29
+ from ...sql.elements import KeyedColumnElement
30
+ from ...sql.expression import alias
31
+ from ...sql.selectable import NamedFromClause
32
+ from ...sql.sqltypes import NULLTYPE
33
+ from ...sql.visitors import InternalTraversal
34
+ from ...util.typing import Self
35
+
36
+ if TYPE_CHECKING:
37
+ from ...sql._typing import _LimitOffsetType
38
+ from ...sql.dml import Delete
39
+ from ...sql.dml import Update
40
+ from ...sql.elements import ColumnElement
41
+ from ...sql.visitors import _TraverseInternalsType
42
+
43
+ __all__ = ("Insert", "insert")
44
+
45
+
46
+ def limit(limit: _LimitOffsetType) -> DMLLimitClause:
47
+ """apply a LIMIT to an UPDATE or DELETE statement
48
+
49
+ e.g.::
50
+
51
+ stmt = t.update().values(q="hi").ext(limit(5))
52
+
53
+ this supersedes the previous approach of using ``mysql_limit`` for
54
+ update/delete statements.
55
+
56
+ .. versionadded:: 2.1
57
+
58
+ """
59
+ return DMLLimitClause(limit)
60
+
61
+
62
+ class DMLLimitClause(SyntaxExtension, ClauseElement):
63
+ stringify_dialect = "mysql"
64
+ __visit_name__ = "mysql_dml_limit_clause"
65
+
66
+ _traverse_internals: _TraverseInternalsType = [
67
+ ("_limit_clause", InternalTraversal.dp_clauseelement),
68
+ ]
69
+
70
+ def __init__(self, limit: _LimitOffsetType):
71
+ self._limit_clause = coercions.expect(
72
+ roles.LimitOffsetRole, limit, name=None, type_=None
73
+ )
74
+
75
+ def apply_to_update(self, update_stmt: Update) -> None:
76
+ update_stmt.apply_syntax_extension_point(
77
+ self.append_replacing_same_type, "post_criteria"
78
+ )
79
+
80
+ def apply_to_delete(self, delete_stmt: Delete) -> None:
81
+ delete_stmt.apply_syntax_extension_point(
82
+ self.append_replacing_same_type, "post_criteria"
83
+ )
84
+
85
+
86
+ def insert(table: _DMLTableArgument) -> Insert:
87
+ """Construct a MySQL/MariaDB-specific variant :class:`_mysql.Insert`
88
+ construct.
89
+
90
+ .. container:: inherited_member
91
+
92
+ The :func:`sqlalchemy.dialects.mysql.insert` function creates
93
+ a :class:`sqlalchemy.dialects.mysql.Insert`. This class is based
94
+ on the dialect-agnostic :class:`_sql.Insert` construct which may
95
+ be constructed using the :func:`_sql.insert` function in
96
+ SQLAlchemy Core.
97
+
98
+ The :class:`_mysql.Insert` construct includes additional methods
99
+ :meth:`_mysql.Insert.on_duplicate_key_update`.
100
+
101
+ """
102
+ return Insert(table)
103
+
104
+
105
+ class Insert(StandardInsert):
106
+ """MySQL-specific implementation of INSERT.
107
+
108
+ Adds methods for MySQL-specific syntaxes such as ON DUPLICATE KEY UPDATE.
109
+
110
+ The :class:`~.mysql.Insert` object is created using the
111
+ :func:`sqlalchemy.dialects.mysql.insert` function.
112
+
113
+ """
114
+
115
+ stringify_dialect = "mysql"
116
+ inherit_cache = True
117
+
118
+ @property
119
+ def inserted(
120
+ self,
121
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
122
+ """Provide the "inserted" namespace for an ON DUPLICATE KEY UPDATE
123
+ statement
124
+
125
+ MySQL's ON DUPLICATE KEY UPDATE clause allows reference to the row
126
+ that would be inserted, via a special function called ``VALUES()``.
127
+ This attribute provides all columns in this row to be referenceable
128
+ such that they will render within a ``VALUES()`` function inside the
129
+ ON DUPLICATE KEY UPDATE clause. The attribute is named ``.inserted``
130
+ so as not to conflict with the existing
131
+ :meth:`_expression.Insert.values` method.
132
+
133
+ .. tip:: The :attr:`_mysql.Insert.inserted` attribute is an instance
134
+ of :class:`_expression.ColumnCollection`, which provides an
135
+ interface the same as that of the :attr:`_schema.Table.c`
136
+ collection described at :ref:`metadata_tables_and_columns`.
137
+ With this collection, ordinary names are accessible like attributes
138
+ (e.g. ``stmt.inserted.some_column``), but special names and
139
+ dictionary method names should be accessed using indexed access,
140
+ such as ``stmt.inserted["column name"]`` or
141
+ ``stmt.inserted["values"]``. See the docstring for
142
+ :class:`_expression.ColumnCollection` for further examples.
143
+
144
+ .. seealso::
145
+
146
+ :ref:`mysql_insert_on_duplicate_key_update` - example of how
147
+ to use :attr:`_expression.Insert.inserted`
148
+
149
+ """
150
+ return self.inserted_alias.columns
151
+
152
+ @util.memoized_property
153
+ def inserted_alias(self) -> NamedFromClause:
154
+ return alias(self.table, name="inserted")
155
+
156
+ @_exclusive_against(
157
+ "_post_values_clause",
158
+ msgs={
159
+ "_post_values_clause": "This Insert construct already "
160
+ "has an ON DUPLICATE KEY clause present"
161
+ },
162
+ )
163
+ def on_duplicate_key_update(self, *args: _UpdateArg, **kw: Any) -> Self:
164
+ r"""
165
+ Specifies the ON DUPLICATE KEY UPDATE clause.
166
+
167
+ :param \**kw: Column keys linked to UPDATE values. The
168
+ values may be any SQL expression or supported literal Python
169
+ values.
170
+
171
+ .. warning:: This dictionary does **not** take into account
172
+ Python-specified default UPDATE values or generation functions,
173
+ e.g. those specified using :paramref:`_schema.Column.onupdate`.
174
+ These values will not be exercised for an ON DUPLICATE KEY UPDATE
175
+ style of UPDATE, unless values are manually specified here.
176
+
177
+ :param \*args: As an alternative to passing key/value parameters,
178
+ a dictionary or list of 2-tuples can be passed as a single positional
179
+ argument.
180
+
181
+ Passing a single dictionary is equivalent to the keyword argument
182
+ form::
183
+
184
+ insert().on_duplicate_key_update({"name": "some name"})
185
+
186
+ Passing a list of 2-tuples indicates that the parameter assignments
187
+ in the UPDATE clause should be ordered as sent, in a manner similar
188
+ to that described for the :class:`_expression.Update`
189
+ construct overall
190
+ in :ref:`tutorial_parameter_ordered_updates`::
191
+
192
+ insert().on_duplicate_key_update(
193
+ [
194
+ ("name", "some name"),
195
+ ("value", "some value"),
196
+ ]
197
+ )
198
+
199
+ .. seealso::
200
+
201
+ :ref:`mysql_insert_on_duplicate_key_update`
202
+
203
+ """
204
+ if args and kw:
205
+ raise exc.ArgumentError(
206
+ "Can't pass kwargs and positional arguments simultaneously"
207
+ )
208
+
209
+ if args:
210
+ if len(args) > 1:
211
+ raise exc.ArgumentError(
212
+ "Only a single dictionary or list of tuples "
213
+ "is accepted positionally."
214
+ )
215
+ values = args[0]
216
+ else:
217
+ values = kw
218
+
219
+ return self.ext(OnDuplicateClause(self.inserted_alias, values))
220
+
221
+
222
+ class OnDuplicateClause(SyntaxExtension, ClauseElement):
223
+ __visit_name__ = "on_duplicate_key_update"
224
+
225
+ _parameter_ordering: Optional[List[str]] = None
226
+
227
+ update: Dict[str, ColumnElement[Any]]
228
+ stringify_dialect = "mysql"
229
+
230
+ _traverse_internals = [
231
+ ("_parameter_ordering", InternalTraversal.dp_string_list),
232
+ ("update", InternalTraversal.dp_dml_values),
233
+ ]
234
+
235
+ def __init__(
236
+ self, inserted_alias: NamedFromClause, update: _UpdateArg
237
+ ) -> None:
238
+ self.inserted_alias = inserted_alias
239
+
240
+ # auto-detect that parameters should be ordered. This is copied from
241
+ # Update._proces_colparams(), however we don't look for a special flag
242
+ # in this case since we are not disambiguating from other use cases as
243
+ # we are in Update.values().
244
+ if isinstance(update, list) and (
245
+ update and isinstance(update[0], tuple)
246
+ ):
247
+ self._parameter_ordering = [key for key, value in update]
248
+ update = dict(update)
249
+
250
+ if isinstance(update, dict):
251
+ if not update:
252
+ raise ValueError(
253
+ "update parameter dictionary must not be empty"
254
+ )
255
+ elif isinstance(update, ColumnCollection):
256
+ update = dict(update)
257
+ else:
258
+ raise ValueError(
259
+ "update parameter must be a non-empty dictionary "
260
+ "or a ColumnCollection such as the `.c.` collection "
261
+ "of a Table object"
262
+ )
263
+
264
+ self.update = {
265
+ k: coercions.expect(
266
+ roles.ExpressionElementRole, v, type_=NULLTYPE, is_crud=True
267
+ )
268
+ for k, v in update.items()
269
+ }
270
+
271
+ def apply_to_insert(self, insert_stmt: StandardInsert) -> None:
272
+ insert_stmt.apply_syntax_extension_point(
273
+ self.append_replacing_same_type, "post_values"
274
+ )
275
+
276
+
277
+ _UpdateArg = Union[
278
+ Mapping[Any, Any], List[Tuple[str, Any]], ColumnCollection[Any, Any]
279
+ ]
@@ -0,0 +1,277 @@
1
+ # dialects/mysql/enumerated.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
+ from __future__ import annotations
9
+
10
+ import enum
11
+ import re
12
+ from typing import Any
13
+ from typing import Optional
14
+ from typing import Type
15
+ from typing import TYPE_CHECKING
16
+ from typing import Union
17
+
18
+ from .types import _StringType
19
+ from ... import exc
20
+ from ... import sql
21
+ from ... import util
22
+ from ...sql import sqltypes
23
+ from ...sql import type_api
24
+
25
+ if TYPE_CHECKING:
26
+ from ...engine.interfaces import Dialect
27
+ from ...sql.elements import ColumnElement
28
+ from ...sql.type_api import _BindProcessorType
29
+ from ...sql.type_api import _ResultProcessorType
30
+ from ...sql.type_api import TypeEngine
31
+ from ...sql.type_api import TypeEngineMixin
32
+
33
+
34
+ class ENUM(type_api.NativeForEmulated, sqltypes.Enum, _StringType):
35
+ """MySQL ENUM type."""
36
+
37
+ __visit_name__ = "ENUM"
38
+
39
+ native_enum = True
40
+
41
+ def __init__(self, *enums: Union[str, Type[enum.Enum]], **kw: Any) -> None:
42
+ """Construct an ENUM.
43
+
44
+ E.g.::
45
+
46
+ Column("myenum", ENUM("foo", "bar", "baz"))
47
+
48
+ :param enums: The range of valid values for this ENUM. Values in
49
+ enums are not quoted, they will be escaped and surrounded by single
50
+ quotes when generating the schema. This object may also be a
51
+ PEP-435-compliant enumerated type.
52
+
53
+ :param strict: This flag has no effect.
54
+
55
+ .. versionchanged:: The MySQL ENUM type as well as the base Enum
56
+ type now validates all Python data values.
57
+
58
+ :param charset: Optional, a column-level character set for this string
59
+ value. Takes precedence to 'ascii' or 'unicode' short-hand.
60
+
61
+ :param collation: Optional, a column-level collation for this string
62
+ value. Takes precedence to 'binary' short-hand.
63
+
64
+ :param ascii: Defaults to False: short-hand for the ``latin1``
65
+ character set, generates ASCII in schema.
66
+
67
+ :param unicode: Defaults to False: short-hand for the ``ucs2``
68
+ character set, generates UNICODE in schema.
69
+
70
+ :param binary: Defaults to False: short-hand, pick the binary
71
+ collation type that matches the column's character set. Generates
72
+ BINARY in schema. This does not affect the type of data stored,
73
+ only the collation of character data.
74
+
75
+ """
76
+ kw.pop("strict", None)
77
+ self._enum_init(enums, kw) # type: ignore[arg-type]
78
+ _StringType.__init__(self, length=self.length, **kw)
79
+
80
+ @classmethod
81
+ def adapt_emulated_to_native(
82
+ cls,
83
+ impl: Union[TypeEngine[Any], TypeEngineMixin],
84
+ **kw: Any,
85
+ ) -> ENUM:
86
+ """Produce a MySQL native :class:`.mysql.ENUM` from plain
87
+ :class:`.Enum`.
88
+
89
+ """
90
+ if TYPE_CHECKING:
91
+ assert isinstance(impl, ENUM)
92
+ kw.setdefault("validate_strings", impl.validate_strings)
93
+ kw.setdefault("values_callable", impl.values_callable)
94
+ kw.setdefault("omit_aliases", impl._omit_aliases)
95
+ return cls(**kw)
96
+
97
+ def _object_value_for_elem(self, elem: str) -> Union[str, enum.Enum]:
98
+ # mysql sends back a blank string for any value that
99
+ # was persisted that was not in the enums; that is, it does no
100
+ # validation on the incoming data, it "truncates" it to be
101
+ # the blank string. Return it straight.
102
+ if elem == "":
103
+ return elem
104
+ else:
105
+ return super()._object_value_for_elem(elem)
106
+
107
+ def repr_struct(self) -> util.GenericRepr:
108
+ return util.GenericRepr(
109
+ self, to_inspect=[ENUM, _StringType, sqltypes.Enum]
110
+ )
111
+
112
+
113
+ # TODO: SET is a string as far as configuration but does not act like
114
+ # a string at the python level. We either need to make a py-type agnostic
115
+ # version of String as a base to be used for this, make this some kind of
116
+ # TypeDecorator, or just vendor it out as its own type.
117
+ class SET(_StringType):
118
+ """MySQL SET type."""
119
+
120
+ __visit_name__ = "SET"
121
+
122
+ def __init__(self, *values: str, **kw: Any):
123
+ """Construct a SET.
124
+
125
+ E.g.::
126
+
127
+ Column("myset", SET("foo", "bar", "baz"))
128
+
129
+ The list of potential values is required in the case that this
130
+ set will be used to generate DDL for a table, or if the
131
+ :paramref:`.SET.retrieve_as_bitwise` flag is set to True.
132
+
133
+ :param values: The range of valid values for this SET. The values
134
+ are not quoted, they will be escaped and surrounded by single
135
+ quotes when generating the schema.
136
+
137
+ :param convert_unicode: Same flag as that of
138
+ :paramref:`.String.convert_unicode`.
139
+
140
+ :param collation: same as that of :paramref:`.String.collation`
141
+
142
+ :param charset: same as that of :paramref:`.VARCHAR.charset`.
143
+
144
+ :param ascii: same as that of :paramref:`.VARCHAR.ascii`.
145
+
146
+ :param unicode: same as that of :paramref:`.VARCHAR.unicode`.
147
+
148
+ :param binary: same as that of :paramref:`.VARCHAR.binary`.
149
+
150
+ :param retrieve_as_bitwise: if True, the data for the set type will be
151
+ persisted and selected using an integer value, where a set is coerced
152
+ into a bitwise mask for persistence. MySQL allows this mode which
153
+ has the advantage of being able to store values unambiguously,
154
+ such as the blank string ``''``. The datatype will appear
155
+ as the expression ``col + 0`` in a SELECT statement, so that the
156
+ value is coerced into an integer value in result sets.
157
+ This flag is required if one wishes
158
+ to persist a set that can store the blank string ``''`` as a value.
159
+
160
+ .. warning::
161
+
162
+ When using :paramref:`.mysql.SET.retrieve_as_bitwise`, it is
163
+ essential that the list of set values is expressed in the
164
+ **exact same order** as exists on the MySQL database.
165
+
166
+ """
167
+ self.retrieve_as_bitwise = kw.pop("retrieve_as_bitwise", False)
168
+ self.values = tuple(values)
169
+ if not self.retrieve_as_bitwise and "" in values:
170
+ raise exc.ArgumentError(
171
+ "Can't use the blank value '' in a SET without "
172
+ "setting retrieve_as_bitwise=True"
173
+ )
174
+ if self.retrieve_as_bitwise:
175
+ self._inversed_bitmap: dict[str, int] = {
176
+ value: 2**idx for idx, value in enumerate(self.values)
177
+ }
178
+ self._bitmap: dict[int, str] = {
179
+ 2**idx: value for idx, value in enumerate(self.values)
180
+ }
181
+ length = max([len(v) for v in values] + [0])
182
+ kw.setdefault("length", length)
183
+ super().__init__(**kw)
184
+
185
+ def column_expression(
186
+ self, colexpr: ColumnElement[Any]
187
+ ) -> ColumnElement[Any]:
188
+ if self.retrieve_as_bitwise:
189
+ return sql.type_coerce(
190
+ sql.type_coerce(colexpr, sqltypes.Integer) + 0, self
191
+ )
192
+ else:
193
+ return colexpr
194
+
195
+ def result_processor(
196
+ self, dialect: Dialect, coltype: Any
197
+ ) -> Optional[_ResultProcessorType[Any]]:
198
+ if self.retrieve_as_bitwise:
199
+
200
+ def process(value: Union[str, int, None]) -> Optional[set[str]]:
201
+ if value is not None:
202
+ value = int(value)
203
+
204
+ return set(util.map_bits(self._bitmap.__getitem__, value))
205
+ else:
206
+ return None
207
+
208
+ else:
209
+ super_convert = super().result_processor(dialect, coltype)
210
+
211
+ def process(value: Union[str, set[str], None]) -> Optional[set[str]]: # type: ignore[misc] # noqa: E501
212
+ if isinstance(value, str):
213
+ # MySQLdb returns a string, let's parse
214
+ if super_convert:
215
+ value = super_convert(value)
216
+ assert value is not None
217
+ if TYPE_CHECKING:
218
+ assert isinstance(value, str)
219
+ return set(re.findall(r"[^,]+", value))
220
+ else:
221
+ # mysql-connector-python does a naive
222
+ # split(",") which throws in an empty string
223
+ if value is not None:
224
+ value.discard("")
225
+ return value
226
+
227
+ return process
228
+
229
+ def bind_processor(
230
+ self, dialect: Dialect
231
+ ) -> _BindProcessorType[Union[str, int]]:
232
+ super_convert = super().bind_processor(dialect)
233
+ if self.retrieve_as_bitwise:
234
+
235
+ def process(
236
+ value: Union[str, int, set[str], None],
237
+ ) -> Union[str, int, None]:
238
+ if value is None:
239
+ return None
240
+ elif isinstance(value, (int, str)):
241
+ if super_convert:
242
+ return super_convert(value) # type: ignore[arg-type, no-any-return] # noqa: E501
243
+ else:
244
+ return value
245
+ else:
246
+ int_value = 0
247
+ for v in value:
248
+ int_value |= self._inversed_bitmap[v]
249
+ return int_value
250
+
251
+ else:
252
+
253
+ def process(
254
+ value: Union[str, int, set[str], None],
255
+ ) -> Union[str, int, None]:
256
+ # accept strings and int (actually bitflag) values directly
257
+ if value is not None and not isinstance(value, (int, str)):
258
+ value = ",".join(value)
259
+ if super_convert:
260
+ return super_convert(value) # type: ignore
261
+ else:
262
+ return value
263
+
264
+ return process
265
+
266
+ def adapt(self, cls: type, **kw: Any) -> Any:
267
+ kw["retrieve_as_bitwise"] = self.retrieve_as_bitwise
268
+ return util.constructor_copy(self, cls, *self.values, **kw)
269
+
270
+ def repr_struct(self) -> util.GenericRepr:
271
+ return util.GenericRepr(
272
+ self,
273
+ to_inspect=[SET, _StringType],
274
+ additional_kw=[
275
+ ("retrieve_as_bitwise", False),
276
+ ],
277
+ )