SQLAlchemy 2.0.36__cp313-cp313-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 (273) hide show
  1. SQLAlchemy-2.0.36.dist-info/LICENSE +19 -0
  2. SQLAlchemy-2.0.36.dist-info/METADATA +243 -0
  3. SQLAlchemy-2.0.36.dist-info/RECORD +273 -0
  4. SQLAlchemy-2.0.36.dist-info/WHEEL +5 -0
  5. SQLAlchemy-2.0.36.dist-info/top_level.txt +1 -0
  6. sqlalchemy/__init__.py +294 -0
  7. sqlalchemy/connectors/__init__.py +18 -0
  8. sqlalchemy/connectors/aioodbc.py +174 -0
  9. sqlalchemy/connectors/asyncio.py +213 -0
  10. sqlalchemy/connectors/pyodbc.py +249 -0
  11. sqlalchemy/cyextension/__init__.py +6 -0
  12. sqlalchemy/cyextension/collections.cp313-win32.pyd +0 -0
  13. sqlalchemy/cyextension/collections.pyx +409 -0
  14. sqlalchemy/cyextension/immutabledict.cp313-win32.pyd +0 -0
  15. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  16. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  17. sqlalchemy/cyextension/processors.cp313-win32.pyd +0 -0
  18. sqlalchemy/cyextension/processors.pyx +68 -0
  19. sqlalchemy/cyextension/resultproxy.cp313-win32.pyd +0 -0
  20. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  21. sqlalchemy/cyextension/util.cp313-win32.pyd +0 -0
  22. sqlalchemy/cyextension/util.pyx +91 -0
  23. sqlalchemy/dialects/__init__.py +61 -0
  24. sqlalchemy/dialects/_typing.py +25 -0
  25. sqlalchemy/dialects/mssql/__init__.py +88 -0
  26. sqlalchemy/dialects/mssql/aioodbc.py +64 -0
  27. sqlalchemy/dialects/mssql/base.py +4010 -0
  28. sqlalchemy/dialects/mssql/information_schema.py +254 -0
  29. sqlalchemy/dialects/mssql/json.py +133 -0
  30. sqlalchemy/dialects/mssql/provision.py +162 -0
  31. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  32. sqlalchemy/dialects/mssql/pyodbc.py +745 -0
  33. sqlalchemy/dialects/mysql/__init__.py +101 -0
  34. sqlalchemy/dialects/mysql/aiomysql.py +333 -0
  35. sqlalchemy/dialects/mysql/asyncmy.py +337 -0
  36. sqlalchemy/dialects/mysql/base.py +3494 -0
  37. sqlalchemy/dialects/mysql/cymysql.py +84 -0
  38. sqlalchemy/dialects/mysql/dml.py +219 -0
  39. sqlalchemy/dialects/mysql/enumerated.py +244 -0
  40. sqlalchemy/dialects/mysql/expression.py +141 -0
  41. sqlalchemy/dialects/mysql/json.py +81 -0
  42. sqlalchemy/dialects/mysql/mariadb.py +32 -0
  43. sqlalchemy/dialects/mysql/mariadbconnector.py +277 -0
  44. sqlalchemy/dialects/mysql/mysqlconnector.py +180 -0
  45. sqlalchemy/dialects/mysql/mysqldb.py +303 -0
  46. sqlalchemy/dialects/mysql/provision.py +110 -0
  47. sqlalchemy/dialects/mysql/pymysql.py +137 -0
  48. sqlalchemy/dialects/mysql/pyodbc.py +138 -0
  49. sqlalchemy/dialects/mysql/reflection.py +677 -0
  50. sqlalchemy/dialects/mysql/reserved_words.py +571 -0
  51. sqlalchemy/dialects/mysql/types.py +774 -0
  52. sqlalchemy/dialects/oracle/__init__.py +67 -0
  53. sqlalchemy/dialects/oracle/base.py +3271 -0
  54. sqlalchemy/dialects/oracle/cx_oracle.py +1483 -0
  55. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  56. sqlalchemy/dialects/oracle/oracledb.py +431 -0
  57. sqlalchemy/dialects/oracle/provision.py +220 -0
  58. sqlalchemy/dialects/oracle/types.py +287 -0
  59. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  60. sqlalchemy/dialects/postgresql/_psycopg_common.py +187 -0
  61. sqlalchemy/dialects/postgresql/array.py +425 -0
  62. sqlalchemy/dialects/postgresql/asyncpg.py +1274 -0
  63. sqlalchemy/dialects/postgresql/base.py +5008 -0
  64. sqlalchemy/dialects/postgresql/dml.py +310 -0
  65. sqlalchemy/dialects/postgresql/ext.py +496 -0
  66. sqlalchemy/dialects/postgresql/hstore.py +397 -0
  67. sqlalchemy/dialects/postgresql/json.py +333 -0
  68. sqlalchemy/dialects/postgresql/named_types.py +509 -0
  69. sqlalchemy/dialects/postgresql/operators.py +129 -0
  70. sqlalchemy/dialects/postgresql/pg8000.py +662 -0
  71. sqlalchemy/dialects/postgresql/pg_catalog.py +300 -0
  72. sqlalchemy/dialects/postgresql/provision.py +175 -0
  73. sqlalchemy/dialects/postgresql/psycopg.py +772 -0
  74. sqlalchemy/dialects/postgresql/psycopg2.py +886 -0
  75. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  76. sqlalchemy/dialects/postgresql/ranges.py +1029 -0
  77. sqlalchemy/dialects/postgresql/types.py +303 -0
  78. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  79. sqlalchemy/dialects/sqlite/aiosqlite.py +396 -0
  80. sqlalchemy/dialects/sqlite/base.py +2805 -0
  81. sqlalchemy/dialects/sqlite/dml.py +240 -0
  82. sqlalchemy/dialects/sqlite/json.py +92 -0
  83. sqlalchemy/dialects/sqlite/provision.py +198 -0
  84. sqlalchemy/dialects/sqlite/pysqlcipher.py +155 -0
  85. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  86. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  87. sqlalchemy/engine/__init__.py +62 -0
  88. sqlalchemy/engine/_py_processors.py +136 -0
  89. sqlalchemy/engine/_py_row.py +128 -0
  90. sqlalchemy/engine/_py_util.py +74 -0
  91. sqlalchemy/engine/base.py +3375 -0
  92. sqlalchemy/engine/characteristics.py +155 -0
  93. sqlalchemy/engine/create.py +875 -0
  94. sqlalchemy/engine/cursor.py +2181 -0
  95. sqlalchemy/engine/default.py +2365 -0
  96. sqlalchemy/engine/events.py +951 -0
  97. sqlalchemy/engine/interfaces.py +3403 -0
  98. sqlalchemy/engine/mock.py +131 -0
  99. sqlalchemy/engine/processors.py +61 -0
  100. sqlalchemy/engine/reflection.py +2098 -0
  101. sqlalchemy/engine/result.py +2382 -0
  102. sqlalchemy/engine/row.py +401 -0
  103. sqlalchemy/engine/strategies.py +19 -0
  104. sqlalchemy/engine/url.py +910 -0
  105. sqlalchemy/engine/util.py +167 -0
  106. sqlalchemy/event/__init__.py +25 -0
  107. sqlalchemy/event/api.py +225 -0
  108. sqlalchemy/event/attr.py +655 -0
  109. sqlalchemy/event/base.py +470 -0
  110. sqlalchemy/event/legacy.py +246 -0
  111. sqlalchemy/event/registry.py +386 -0
  112. sqlalchemy/events.py +17 -0
  113. sqlalchemy/exc.py +830 -0
  114. sqlalchemy/ext/__init__.py +11 -0
  115. sqlalchemy/ext/associationproxy.py +2013 -0
  116. sqlalchemy/ext/asyncio/__init__.py +25 -0
  117. sqlalchemy/ext/asyncio/base.py +279 -0
  118. sqlalchemy/ext/asyncio/engine.py +1466 -0
  119. sqlalchemy/ext/asyncio/exc.py +21 -0
  120. sqlalchemy/ext/asyncio/result.py +961 -0
  121. sqlalchemy/ext/asyncio/scoping.py +1614 -0
  122. sqlalchemy/ext/asyncio/session.py +1936 -0
  123. sqlalchemy/ext/automap.py +1691 -0
  124. sqlalchemy/ext/baked.py +574 -0
  125. sqlalchemy/ext/compiler.py +570 -0
  126. sqlalchemy/ext/declarative/__init__.py +65 -0
  127. sqlalchemy/ext/declarative/extensions.py +548 -0
  128. sqlalchemy/ext/horizontal_shard.py +481 -0
  129. sqlalchemy/ext/hybrid.py +1514 -0
  130. sqlalchemy/ext/indexable.py +341 -0
  131. sqlalchemy/ext/instrumentation.py +450 -0
  132. sqlalchemy/ext/mutable.py +1073 -0
  133. sqlalchemy/ext/mypy/__init__.py +6 -0
  134. sqlalchemy/ext/mypy/apply.py +320 -0
  135. sqlalchemy/ext/mypy/decl_class.py +515 -0
  136. sqlalchemy/ext/mypy/infer.py +590 -0
  137. sqlalchemy/ext/mypy/names.py +335 -0
  138. sqlalchemy/ext/mypy/plugin.py +303 -0
  139. sqlalchemy/ext/mypy/util.py +357 -0
  140. sqlalchemy/ext/orderinglist.py +416 -0
  141. sqlalchemy/ext/serializer.py +181 -0
  142. sqlalchemy/future/__init__.py +16 -0
  143. sqlalchemy/future/engine.py +15 -0
  144. sqlalchemy/inspection.py +174 -0
  145. sqlalchemy/log.py +288 -0
  146. sqlalchemy/orm/__init__.py +170 -0
  147. sqlalchemy/orm/_orm_constructors.py +2571 -0
  148. sqlalchemy/orm/_typing.py +179 -0
  149. sqlalchemy/orm/attributes.py +2835 -0
  150. sqlalchemy/orm/base.py +973 -0
  151. sqlalchemy/orm/bulk_persistence.py +2123 -0
  152. sqlalchemy/orm/clsregistry.py +571 -0
  153. sqlalchemy/orm/collections.py +1620 -0
  154. sqlalchemy/orm/context.py +3268 -0
  155. sqlalchemy/orm/decl_api.py +1883 -0
  156. sqlalchemy/orm/decl_base.py +2190 -0
  157. sqlalchemy/orm/dependency.py +1304 -0
  158. sqlalchemy/orm/descriptor_props.py +1076 -0
  159. sqlalchemy/orm/dynamic.py +300 -0
  160. sqlalchemy/orm/evaluator.py +379 -0
  161. sqlalchemy/orm/events.py +3261 -0
  162. sqlalchemy/orm/exc.py +228 -0
  163. sqlalchemy/orm/identity.py +302 -0
  164. sqlalchemy/orm/instrumentation.py +754 -0
  165. sqlalchemy/orm/interfaces.py +1474 -0
  166. sqlalchemy/orm/loading.py +1682 -0
  167. sqlalchemy/orm/mapped_collection.py +557 -0
  168. sqlalchemy/orm/mapper.py +4432 -0
  169. sqlalchemy/orm/path_registry.py +811 -0
  170. sqlalchemy/orm/persistence.py +1782 -0
  171. sqlalchemy/orm/properties.py +886 -0
  172. sqlalchemy/orm/query.py +3396 -0
  173. sqlalchemy/orm/relationships.py +3500 -0
  174. sqlalchemy/orm/scoping.py +2165 -0
  175. sqlalchemy/orm/session.py +5301 -0
  176. sqlalchemy/orm/state.py +1143 -0
  177. sqlalchemy/orm/state_changes.py +198 -0
  178. sqlalchemy/orm/strategies.py +3473 -0
  179. sqlalchemy/orm/strategy_options.py +2569 -0
  180. sqlalchemy/orm/sync.py +164 -0
  181. sqlalchemy/orm/unitofwork.py +796 -0
  182. sqlalchemy/orm/util.py +2424 -0
  183. sqlalchemy/orm/writeonly.py +678 -0
  184. sqlalchemy/pool/__init__.py +44 -0
  185. sqlalchemy/pool/base.py +1515 -0
  186. sqlalchemy/pool/events.py +370 -0
  187. sqlalchemy/pool/impl.py +581 -0
  188. sqlalchemy/py.typed +0 -0
  189. sqlalchemy/schema.py +70 -0
  190. sqlalchemy/sql/__init__.py +145 -0
  191. sqlalchemy/sql/_dml_constructors.py +140 -0
  192. sqlalchemy/sql/_elements_constructors.py +1850 -0
  193. sqlalchemy/sql/_orm_types.py +20 -0
  194. sqlalchemy/sql/_py_util.py +75 -0
  195. sqlalchemy/sql/_selectable_constructors.py +635 -0
  196. sqlalchemy/sql/_typing.py +460 -0
  197. sqlalchemy/sql/annotation.py +585 -0
  198. sqlalchemy/sql/base.py +2185 -0
  199. sqlalchemy/sql/cache_key.py +1057 -0
  200. sqlalchemy/sql/coercions.py +1405 -0
  201. sqlalchemy/sql/compiler.py +7818 -0
  202. sqlalchemy/sql/crud.py +1669 -0
  203. sqlalchemy/sql/ddl.py +1378 -0
  204. sqlalchemy/sql/default_comparator.py +552 -0
  205. sqlalchemy/sql/dml.py +1817 -0
  206. sqlalchemy/sql/elements.py +5499 -0
  207. sqlalchemy/sql/events.py +455 -0
  208. sqlalchemy/sql/expression.py +162 -0
  209. sqlalchemy/sql/functions.py +2055 -0
  210. sqlalchemy/sql/lambdas.py +1449 -0
  211. sqlalchemy/sql/naming.py +212 -0
  212. sqlalchemy/sql/operators.py +2579 -0
  213. sqlalchemy/sql/roles.py +323 -0
  214. sqlalchemy/sql/schema.py +6158 -0
  215. sqlalchemy/sql/selectable.py +7004 -0
  216. sqlalchemy/sql/sqltypes.py +3827 -0
  217. sqlalchemy/sql/traversals.py +1024 -0
  218. sqlalchemy/sql/type_api.py +2339 -0
  219. sqlalchemy/sql/util.py +1486 -0
  220. sqlalchemy/sql/visitors.py +1165 -0
  221. sqlalchemy/testing/__init__.py +96 -0
  222. sqlalchemy/testing/assertions.py +989 -0
  223. sqlalchemy/testing/assertsql.py +516 -0
  224. sqlalchemy/testing/asyncio.py +135 -0
  225. sqlalchemy/testing/config.py +427 -0
  226. sqlalchemy/testing/engines.py +472 -0
  227. sqlalchemy/testing/entities.py +117 -0
  228. sqlalchemy/testing/exclusions.py +435 -0
  229. sqlalchemy/testing/fixtures/__init__.py +28 -0
  230. sqlalchemy/testing/fixtures/base.py +366 -0
  231. sqlalchemy/testing/fixtures/mypy.py +312 -0
  232. sqlalchemy/testing/fixtures/orm.py +227 -0
  233. sqlalchemy/testing/fixtures/sql.py +503 -0
  234. sqlalchemy/testing/pickleable.py +155 -0
  235. sqlalchemy/testing/plugin/__init__.py +6 -0
  236. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  237. sqlalchemy/testing/plugin/plugin_base.py +779 -0
  238. sqlalchemy/testing/plugin/pytestplugin.py +868 -0
  239. sqlalchemy/testing/profiling.py +324 -0
  240. sqlalchemy/testing/provision.py +496 -0
  241. sqlalchemy/testing/requirements.py +1818 -0
  242. sqlalchemy/testing/schema.py +224 -0
  243. sqlalchemy/testing/suite/__init__.py +19 -0
  244. sqlalchemy/testing/suite/test_cte.py +211 -0
  245. sqlalchemy/testing/suite/test_ddl.py +389 -0
  246. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  247. sqlalchemy/testing/suite/test_dialect.py +740 -0
  248. sqlalchemy/testing/suite/test_insert.py +630 -0
  249. sqlalchemy/testing/suite/test_reflection.py +3225 -0
  250. sqlalchemy/testing/suite/test_results.py +502 -0
  251. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  252. sqlalchemy/testing/suite/test_select.py +1999 -0
  253. sqlalchemy/testing/suite/test_sequence.py +317 -0
  254. sqlalchemy/testing/suite/test_types.py +2141 -0
  255. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  256. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  257. sqlalchemy/testing/util.py +537 -0
  258. sqlalchemy/testing/warnings.py +52 -0
  259. sqlalchemy/types.py +76 -0
  260. sqlalchemy/util/__init__.py +160 -0
  261. sqlalchemy/util/_collections.py +715 -0
  262. sqlalchemy/util/_concurrency_py3k.py +288 -0
  263. sqlalchemy/util/_has_cy.py +40 -0
  264. sqlalchemy/util/_py_collections.py +541 -0
  265. sqlalchemy/util/compat.py +301 -0
  266. sqlalchemy/util/concurrency.py +108 -0
  267. sqlalchemy/util/deprecations.py +401 -0
  268. sqlalchemy/util/langhelpers.py +2218 -0
  269. sqlalchemy/util/preloaded.py +150 -0
  270. sqlalchemy/util/queue.py +322 -0
  271. sqlalchemy/util/tool_support.py +201 -0
  272. sqlalchemy/util/topological.py +120 -0
  273. sqlalchemy/util/typing.py +629 -0
@@ -0,0 +1,84 @@
1
+ # dialects/mysql/cymysql.py
2
+ # Copyright (C) 2005-2024 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
+ # mypy: ignore-errors
8
+
9
+ r"""
10
+
11
+ .. dialect:: mysql+cymysql
12
+ :name: CyMySQL
13
+ :dbapi: cymysql
14
+ :connectstring: mysql+cymysql://<username>:<password>@<host>/<dbname>[?<options>]
15
+ :url: https://github.com/nakagami/CyMySQL
16
+
17
+ .. note::
18
+
19
+ The CyMySQL dialect is **not tested as part of SQLAlchemy's continuous
20
+ integration** and may have unresolved issues. The recommended MySQL
21
+ dialects are mysqlclient and PyMySQL.
22
+
23
+ """ # noqa
24
+
25
+ from .base import BIT
26
+ from .base import MySQLDialect
27
+ from .mysqldb import MySQLDialect_mysqldb
28
+ from ... import util
29
+
30
+
31
+ class _cymysqlBIT(BIT):
32
+ def result_processor(self, dialect, coltype):
33
+ """Convert MySQL's 64 bit, variable length binary string to a long."""
34
+
35
+ def process(value):
36
+ if value is not None:
37
+ v = 0
38
+ for i in iter(value):
39
+ v = v << 8 | i
40
+ return v
41
+ return value
42
+
43
+ return process
44
+
45
+
46
+ class MySQLDialect_cymysql(MySQLDialect_mysqldb):
47
+ driver = "cymysql"
48
+ supports_statement_cache = True
49
+
50
+ description_encoding = None
51
+ supports_sane_rowcount = True
52
+ supports_sane_multi_rowcount = False
53
+ supports_unicode_statements = True
54
+
55
+ colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _cymysqlBIT})
56
+
57
+ @classmethod
58
+ def import_dbapi(cls):
59
+ return __import__("cymysql")
60
+
61
+ def _detect_charset(self, connection):
62
+ return connection.connection.charset
63
+
64
+ def _extract_error_code(self, exception):
65
+ return exception.errno
66
+
67
+ def is_disconnect(self, e, connection, cursor):
68
+ if isinstance(e, self.dbapi.OperationalError):
69
+ return self._extract_error_code(e) in (
70
+ 2006,
71
+ 2013,
72
+ 2014,
73
+ 2045,
74
+ 2055,
75
+ )
76
+ elif isinstance(e, self.dbapi.InterfaceError):
77
+ # if underlying connection is closed,
78
+ # this is the error you get
79
+ return True
80
+ else:
81
+ return False
82
+
83
+
84
+ dialect = MySQLDialect_cymysql
@@ -0,0 +1,219 @@
1
+ # dialects/mysql/dml.py
2
+ # Copyright (C) 2005-2024 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 List
11
+ from typing import Mapping
12
+ from typing import Optional
13
+ from typing import Tuple
14
+ from typing import Union
15
+
16
+ from ... import exc
17
+ from ... import util
18
+ from ...sql._typing import _DMLTableArgument
19
+ from ...sql.base import _exclusive_against
20
+ from ...sql.base import _generative
21
+ from ...sql.base import ColumnCollection
22
+ from ...sql.base import ReadOnlyColumnCollection
23
+ from ...sql.dml import Insert as StandardInsert
24
+ from ...sql.elements import ClauseElement
25
+ from ...sql.elements import KeyedColumnElement
26
+ from ...sql.expression import alias
27
+ from ...sql.selectable import NamedFromClause
28
+ from ...util.typing import Self
29
+
30
+
31
+ __all__ = ("Insert", "insert")
32
+
33
+
34
+ def insert(table: _DMLTableArgument) -> Insert:
35
+ """Construct a MySQL/MariaDB-specific variant :class:`_mysql.Insert`
36
+ construct.
37
+
38
+ .. container:: inherited_member
39
+
40
+ The :func:`sqlalchemy.dialects.mysql.insert` function creates
41
+ a :class:`sqlalchemy.dialects.mysql.Insert`. This class is based
42
+ on the dialect-agnostic :class:`_sql.Insert` construct which may
43
+ be constructed using the :func:`_sql.insert` function in
44
+ SQLAlchemy Core.
45
+
46
+ The :class:`_mysql.Insert` construct includes additional methods
47
+ :meth:`_mysql.Insert.on_duplicate_key_update`.
48
+
49
+ """
50
+ return Insert(table)
51
+
52
+
53
+ class Insert(StandardInsert):
54
+ """MySQL-specific implementation of INSERT.
55
+
56
+ Adds methods for MySQL-specific syntaxes such as ON DUPLICATE KEY UPDATE.
57
+
58
+ The :class:`~.mysql.Insert` object is created using the
59
+ :func:`sqlalchemy.dialects.mysql.insert` function.
60
+
61
+ .. versionadded:: 1.2
62
+
63
+ """
64
+
65
+ stringify_dialect = "mysql"
66
+ inherit_cache = False
67
+
68
+ @property
69
+ def inserted(
70
+ self,
71
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
72
+ """Provide the "inserted" namespace for an ON DUPLICATE KEY UPDATE
73
+ statement
74
+
75
+ MySQL's ON DUPLICATE KEY UPDATE clause allows reference to the row
76
+ that would be inserted, via a special function called ``VALUES()``.
77
+ This attribute provides all columns in this row to be referenceable
78
+ such that they will render within a ``VALUES()`` function inside the
79
+ ON DUPLICATE KEY UPDATE clause. The attribute is named ``.inserted``
80
+ so as not to conflict with the existing
81
+ :meth:`_expression.Insert.values` method.
82
+
83
+ .. tip:: The :attr:`_mysql.Insert.inserted` attribute is an instance
84
+ of :class:`_expression.ColumnCollection`, which provides an
85
+ interface the same as that of the :attr:`_schema.Table.c`
86
+ collection described at :ref:`metadata_tables_and_columns`.
87
+ With this collection, ordinary names are accessible like attributes
88
+ (e.g. ``stmt.inserted.some_column``), but special names and
89
+ dictionary method names should be accessed using indexed access,
90
+ such as ``stmt.inserted["column name"]`` or
91
+ ``stmt.inserted["values"]``. See the docstring for
92
+ :class:`_expression.ColumnCollection` for further examples.
93
+
94
+ .. seealso::
95
+
96
+ :ref:`mysql_insert_on_duplicate_key_update` - example of how
97
+ to use :attr:`_expression.Insert.inserted`
98
+
99
+ """
100
+ return self.inserted_alias.columns
101
+
102
+ @util.memoized_property
103
+ def inserted_alias(self) -> NamedFromClause:
104
+ return alias(self.table, name="inserted")
105
+
106
+ @_generative
107
+ @_exclusive_against(
108
+ "_post_values_clause",
109
+ msgs={
110
+ "_post_values_clause": "This Insert construct already "
111
+ "has an ON DUPLICATE KEY clause present"
112
+ },
113
+ )
114
+ def on_duplicate_key_update(self, *args: _UpdateArg, **kw: Any) -> Self:
115
+ r"""
116
+ Specifies the ON DUPLICATE KEY UPDATE clause.
117
+
118
+ :param \**kw: Column keys linked to UPDATE values. The
119
+ values may be any SQL expression or supported literal Python
120
+ values.
121
+
122
+ .. warning:: This dictionary does **not** take into account
123
+ Python-specified default UPDATE values or generation functions,
124
+ e.g. those specified using :paramref:`_schema.Column.onupdate`.
125
+ These values will not be exercised for an ON DUPLICATE KEY UPDATE
126
+ style of UPDATE, unless values are manually specified here.
127
+
128
+ :param \*args: As an alternative to passing key/value parameters,
129
+ a dictionary or list of 2-tuples can be passed as a single positional
130
+ argument.
131
+
132
+ Passing a single dictionary is equivalent to the keyword argument
133
+ form::
134
+
135
+ insert().on_duplicate_key_update({"name": "some name"})
136
+
137
+ Passing a list of 2-tuples indicates that the parameter assignments
138
+ in the UPDATE clause should be ordered as sent, in a manner similar
139
+ to that described for the :class:`_expression.Update`
140
+ construct overall
141
+ in :ref:`tutorial_parameter_ordered_updates`::
142
+
143
+ insert().on_duplicate_key_update(
144
+ [("name", "some name"), ("value", "some value")])
145
+
146
+ .. versionchanged:: 1.3 parameters can be specified as a dictionary
147
+ or list of 2-tuples; the latter form provides for parameter
148
+ ordering.
149
+
150
+
151
+ .. versionadded:: 1.2
152
+
153
+ .. seealso::
154
+
155
+ :ref:`mysql_insert_on_duplicate_key_update`
156
+
157
+ """
158
+ if args and kw:
159
+ raise exc.ArgumentError(
160
+ "Can't pass kwargs and positional arguments simultaneously"
161
+ )
162
+
163
+ if args:
164
+ if len(args) > 1:
165
+ raise exc.ArgumentError(
166
+ "Only a single dictionary or list of tuples "
167
+ "is accepted positionally."
168
+ )
169
+ values = args[0]
170
+ else:
171
+ values = kw
172
+
173
+ self._post_values_clause = OnDuplicateClause(
174
+ self.inserted_alias, values
175
+ )
176
+ return self
177
+
178
+
179
+ class OnDuplicateClause(ClauseElement):
180
+ __visit_name__ = "on_duplicate_key_update"
181
+
182
+ _parameter_ordering: Optional[List[str]] = None
183
+
184
+ stringify_dialect = "mysql"
185
+
186
+ def __init__(
187
+ self, inserted_alias: NamedFromClause, update: _UpdateArg
188
+ ) -> None:
189
+ self.inserted_alias = inserted_alias
190
+
191
+ # auto-detect that parameters should be ordered. This is copied from
192
+ # Update._proces_colparams(), however we don't look for a special flag
193
+ # in this case since we are not disambiguating from other use cases as
194
+ # we are in Update.values().
195
+ if isinstance(update, list) and (
196
+ update and isinstance(update[0], tuple)
197
+ ):
198
+ self._parameter_ordering = [key for key, value in update]
199
+ update = dict(update)
200
+
201
+ if isinstance(update, dict):
202
+ if not update:
203
+ raise ValueError(
204
+ "update parameter dictionary must not be empty"
205
+ )
206
+ elif isinstance(update, ColumnCollection):
207
+ update = dict(update)
208
+ else:
209
+ raise ValueError(
210
+ "update parameter must be a non-empty dictionary "
211
+ "or a ColumnCollection such as the `.c.` collection "
212
+ "of a Table object"
213
+ )
214
+ self.update = update
215
+
216
+
217
+ _UpdateArg = Union[
218
+ Mapping[Any, Any], List[Tuple[str, Any]], ColumnCollection[Any, Any]
219
+ ]
@@ -0,0 +1,244 @@
1
+ # dialects/mysql/enumerated.py
2
+ # Copyright (C) 2005-2024 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
+ # mypy: ignore-errors
8
+
9
+
10
+ import re
11
+
12
+ from .types import _StringType
13
+ from ... import exc
14
+ from ... import sql
15
+ from ... import util
16
+ from ...sql import sqltypes
17
+
18
+
19
+ class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _StringType):
20
+ """MySQL ENUM type."""
21
+
22
+ __visit_name__ = "ENUM"
23
+
24
+ native_enum = True
25
+
26
+ def __init__(self, *enums, **kw):
27
+ """Construct an ENUM.
28
+
29
+ E.g.::
30
+
31
+ Column('myenum', ENUM("foo", "bar", "baz"))
32
+
33
+ :param enums: The range of valid values for this ENUM. Values in
34
+ enums are not quoted, they will be escaped and surrounded by single
35
+ quotes when generating the schema. This object may also be a
36
+ PEP-435-compliant enumerated type.
37
+
38
+ .. versionadded: 1.1 added support for PEP-435-compliant enumerated
39
+ types.
40
+
41
+ :param strict: This flag has no effect.
42
+
43
+ .. versionchanged:: The MySQL ENUM type as well as the base Enum
44
+ type now validates all Python data values.
45
+
46
+ :param charset: Optional, a column-level character set for this string
47
+ value. Takes precedence to 'ascii' or 'unicode' short-hand.
48
+
49
+ :param collation: Optional, a column-level collation for this string
50
+ value. Takes precedence to 'binary' short-hand.
51
+
52
+ :param ascii: Defaults to False: short-hand for the ``latin1``
53
+ character set, generates ASCII in schema.
54
+
55
+ :param unicode: Defaults to False: short-hand for the ``ucs2``
56
+ character set, generates UNICODE in schema.
57
+
58
+ :param binary: Defaults to False: short-hand, pick the binary
59
+ collation type that matches the column's character set. Generates
60
+ BINARY in schema. This does not affect the type of data stored,
61
+ only the collation of character data.
62
+
63
+ """
64
+ kw.pop("strict", None)
65
+ self._enum_init(enums, kw)
66
+ _StringType.__init__(self, length=self.length, **kw)
67
+
68
+ @classmethod
69
+ def adapt_emulated_to_native(cls, impl, **kw):
70
+ """Produce a MySQL native :class:`.mysql.ENUM` from plain
71
+ :class:`.Enum`.
72
+
73
+ """
74
+ kw.setdefault("validate_strings", impl.validate_strings)
75
+ kw.setdefault("values_callable", impl.values_callable)
76
+ kw.setdefault("omit_aliases", impl._omit_aliases)
77
+ return cls(**kw)
78
+
79
+ def _object_value_for_elem(self, elem):
80
+ # mysql sends back a blank string for any value that
81
+ # was persisted that was not in the enums; that is, it does no
82
+ # validation on the incoming data, it "truncates" it to be
83
+ # the blank string. Return it straight.
84
+ if elem == "":
85
+ return elem
86
+ else:
87
+ return super()._object_value_for_elem(elem)
88
+
89
+ def __repr__(self):
90
+ return util.generic_repr(
91
+ self, to_inspect=[ENUM, _StringType, sqltypes.Enum]
92
+ )
93
+
94
+
95
+ class SET(_StringType):
96
+ """MySQL SET type."""
97
+
98
+ __visit_name__ = "SET"
99
+
100
+ def __init__(self, *values, **kw):
101
+ """Construct a SET.
102
+
103
+ E.g.::
104
+
105
+ Column('myset', SET("foo", "bar", "baz"))
106
+
107
+
108
+ The list of potential values is required in the case that this
109
+ set will be used to generate DDL for a table, or if the
110
+ :paramref:`.SET.retrieve_as_bitwise` flag is set to True.
111
+
112
+ :param values: The range of valid values for this SET. The values
113
+ are not quoted, they will be escaped and surrounded by single
114
+ quotes when generating the schema.
115
+
116
+ :param convert_unicode: Same flag as that of
117
+ :paramref:`.String.convert_unicode`.
118
+
119
+ :param collation: same as that of :paramref:`.String.collation`
120
+
121
+ :param charset: same as that of :paramref:`.VARCHAR.charset`.
122
+
123
+ :param ascii: same as that of :paramref:`.VARCHAR.ascii`.
124
+
125
+ :param unicode: same as that of :paramref:`.VARCHAR.unicode`.
126
+
127
+ :param binary: same as that of :paramref:`.VARCHAR.binary`.
128
+
129
+ :param retrieve_as_bitwise: if True, the data for the set type will be
130
+ persisted and selected using an integer value, where a set is coerced
131
+ into a bitwise mask for persistence. MySQL allows this mode which
132
+ has the advantage of being able to store values unambiguously,
133
+ such as the blank string ``''``. The datatype will appear
134
+ as the expression ``col + 0`` in a SELECT statement, so that the
135
+ value is coerced into an integer value in result sets.
136
+ This flag is required if one wishes
137
+ to persist a set that can store the blank string ``''`` as a value.
138
+
139
+ .. warning::
140
+
141
+ When using :paramref:`.mysql.SET.retrieve_as_bitwise`, it is
142
+ essential that the list of set values is expressed in the
143
+ **exact same order** as exists on the MySQL database.
144
+
145
+ """
146
+ self.retrieve_as_bitwise = kw.pop("retrieve_as_bitwise", False)
147
+ self.values = tuple(values)
148
+ if not self.retrieve_as_bitwise and "" in values:
149
+ raise exc.ArgumentError(
150
+ "Can't use the blank value '' in a SET without "
151
+ "setting retrieve_as_bitwise=True"
152
+ )
153
+ if self.retrieve_as_bitwise:
154
+ self._bitmap = {
155
+ value: 2**idx for idx, value in enumerate(self.values)
156
+ }
157
+ self._bitmap.update(
158
+ (2**idx, value) for idx, value in enumerate(self.values)
159
+ )
160
+ length = max([len(v) for v in values] + [0])
161
+ kw.setdefault("length", length)
162
+ super().__init__(**kw)
163
+
164
+ def column_expression(self, colexpr):
165
+ if self.retrieve_as_bitwise:
166
+ return sql.type_coerce(
167
+ sql.type_coerce(colexpr, sqltypes.Integer) + 0, self
168
+ )
169
+ else:
170
+ return colexpr
171
+
172
+ def result_processor(self, dialect, coltype):
173
+ if self.retrieve_as_bitwise:
174
+
175
+ def process(value):
176
+ if value is not None:
177
+ value = int(value)
178
+
179
+ return set(util.map_bits(self._bitmap.__getitem__, value))
180
+ else:
181
+ return None
182
+
183
+ else:
184
+ super_convert = super().result_processor(dialect, coltype)
185
+
186
+ def process(value):
187
+ if isinstance(value, str):
188
+ # MySQLdb returns a string, let's parse
189
+ if super_convert:
190
+ value = super_convert(value)
191
+ return set(re.findall(r"[^,]+", value))
192
+ else:
193
+ # mysql-connector-python does a naive
194
+ # split(",") which throws in an empty string
195
+ if value is not None:
196
+ value.discard("")
197
+ return value
198
+
199
+ return process
200
+
201
+ def bind_processor(self, dialect):
202
+ super_convert = super().bind_processor(dialect)
203
+ if self.retrieve_as_bitwise:
204
+
205
+ def process(value):
206
+ if value is None:
207
+ return None
208
+ elif isinstance(value, (int, str)):
209
+ if super_convert:
210
+ return super_convert(value)
211
+ else:
212
+ return value
213
+ else:
214
+ int_value = 0
215
+ for v in value:
216
+ int_value |= self._bitmap[v]
217
+ return int_value
218
+
219
+ else:
220
+
221
+ def process(value):
222
+ # accept strings and int (actually bitflag) values directly
223
+ if value is not None and not isinstance(value, (int, str)):
224
+ value = ",".join(value)
225
+
226
+ if super_convert:
227
+ return super_convert(value)
228
+ else:
229
+ return value
230
+
231
+ return process
232
+
233
+ def adapt(self, impltype, **kw):
234
+ kw["retrieve_as_bitwise"] = self.retrieve_as_bitwise
235
+ return util.constructor_copy(self, impltype, *self.values, **kw)
236
+
237
+ def __repr__(self):
238
+ return util.generic_repr(
239
+ self,
240
+ to_inspect=[SET, _StringType],
241
+ additional_kw=[
242
+ ("retrieve_as_bitwise", False),
243
+ ],
244
+ )
@@ -0,0 +1,141 @@
1
+ # dialects/mysql/expression.py
2
+ # Copyright (C) 2005-2024 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
+ # mypy: ignore-errors
8
+
9
+
10
+ from ... import exc
11
+ from ... import util
12
+ from ...sql import coercions
13
+ from ...sql import elements
14
+ from ...sql import operators
15
+ from ...sql import roles
16
+ from ...sql.base import _generative
17
+ from ...sql.base import Generative
18
+ from ...util.typing import Self
19
+
20
+
21
+ class match(Generative, elements.BinaryExpression):
22
+ """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause.
23
+
24
+ E.g.::
25
+
26
+ from sqlalchemy import desc
27
+ from sqlalchemy.dialects.mysql import match
28
+
29
+ match_expr = match(
30
+ users_table.c.firstname,
31
+ users_table.c.lastname,
32
+ against="Firstname Lastname",
33
+ )
34
+
35
+ stmt = (
36
+ select(users_table)
37
+ .where(match_expr.in_boolean_mode())
38
+ .order_by(desc(match_expr))
39
+ )
40
+
41
+ Would produce SQL resembling::
42
+
43
+ SELECT id, firstname, lastname
44
+ FROM user
45
+ WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
46
+ ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC
47
+
48
+ The :func:`_mysql.match` function is a standalone version of the
49
+ :meth:`_sql.ColumnElement.match` method available on all
50
+ SQL expressions, as when :meth:`_expression.ColumnElement.match` is
51
+ used, but allows to pass multiple columns
52
+
53
+ :param cols: column expressions to match against
54
+
55
+ :param against: expression to be compared towards
56
+
57
+ :param in_boolean_mode: boolean, set "boolean mode" to true
58
+
59
+ :param in_natural_language_mode: boolean , set "natural language" to true
60
+
61
+ :param with_query_expansion: boolean, set "query expansion" to true
62
+
63
+ .. versionadded:: 1.4.19
64
+
65
+ .. seealso::
66
+
67
+ :meth:`_expression.ColumnElement.match`
68
+
69
+ """
70
+
71
+ __visit_name__ = "mysql_match"
72
+
73
+ inherit_cache = True
74
+
75
+ def __init__(self, *cols, **kw):
76
+ if not cols:
77
+ raise exc.ArgumentError("columns are required")
78
+
79
+ against = kw.pop("against", None)
80
+
81
+ if against is None:
82
+ raise exc.ArgumentError("against is required")
83
+ against = coercions.expect(
84
+ roles.ExpressionElementRole,
85
+ against,
86
+ )
87
+
88
+ left = elements.BooleanClauseList._construct_raw(
89
+ operators.comma_op,
90
+ clauses=cols,
91
+ )
92
+ left.group = False
93
+
94
+ flags = util.immutabledict(
95
+ {
96
+ "mysql_boolean_mode": kw.pop("in_boolean_mode", False),
97
+ "mysql_natural_language": kw.pop(
98
+ "in_natural_language_mode", False
99
+ ),
100
+ "mysql_query_expansion": kw.pop("with_query_expansion", False),
101
+ }
102
+ )
103
+
104
+ if kw:
105
+ raise exc.ArgumentError("unknown arguments: %s" % (", ".join(kw)))
106
+
107
+ super().__init__(left, against, operators.match_op, modifiers=flags)
108
+
109
+ @_generative
110
+ def in_boolean_mode(self) -> Self:
111
+ """Apply the "IN BOOLEAN MODE" modifier to the MATCH expression.
112
+
113
+ :return: a new :class:`_mysql.match` instance with modifications
114
+ applied.
115
+ """
116
+
117
+ self.modifiers = self.modifiers.union({"mysql_boolean_mode": True})
118
+ return self
119
+
120
+ @_generative
121
+ def in_natural_language_mode(self) -> Self:
122
+ """Apply the "IN NATURAL LANGUAGE MODE" modifier to the MATCH
123
+ expression.
124
+
125
+ :return: a new :class:`_mysql.match` instance with modifications
126
+ applied.
127
+ """
128
+
129
+ self.modifiers = self.modifiers.union({"mysql_natural_language": True})
130
+ return self
131
+
132
+ @_generative
133
+ def with_query_expansion(self) -> Self:
134
+ """Apply the "WITH QUERY EXPANSION" modifier to the MATCH expression.
135
+
136
+ :return: a new :class:`_mysql.match` instance with modifications
137
+ applied.
138
+ """
139
+
140
+ self.modifiers = self.modifiers.union({"mysql_query_expansion": True})
141
+ return self