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