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,146 @@
1
+ # dialects/mysql/expression.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
+ from typing import Any
11
+
12
+ from ... import exc
13
+ from ... import util
14
+ from ...sql import coercions
15
+ from ...sql import elements
16
+ from ...sql import operators
17
+ from ...sql import roles
18
+ from ...sql.base import _generative
19
+ from ...sql.base import Generative
20
+ from ...util.typing import Self
21
+
22
+
23
+ class match(Generative, elements.BinaryExpression[Any]):
24
+ """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause.
25
+
26
+ E.g.::
27
+
28
+ from sqlalchemy import desc
29
+ from sqlalchemy.dialects.mysql import match
30
+
31
+ match_expr = match(
32
+ users_table.c.firstname,
33
+ users_table.c.lastname,
34
+ against="Firstname Lastname",
35
+ )
36
+
37
+ stmt = (
38
+ select(users_table)
39
+ .where(match_expr.in_boolean_mode())
40
+ .order_by(desc(match_expr))
41
+ )
42
+
43
+ Would produce SQL resembling:
44
+
45
+ .. sourcecode:: sql
46
+
47
+ SELECT id, firstname, lastname
48
+ FROM user
49
+ WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
50
+ ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC
51
+
52
+ The :func:`_mysql.match` function is a standalone version of the
53
+ :meth:`_sql.ColumnElement.match` method available on all
54
+ SQL expressions, as when :meth:`_expression.ColumnElement.match` is
55
+ used, but allows to pass multiple columns
56
+
57
+ :param cols: column expressions to match against
58
+
59
+ :param against: expression to be compared towards
60
+
61
+ :param in_boolean_mode: boolean, set "boolean mode" to true
62
+
63
+ :param in_natural_language_mode: boolean , set "natural language" to true
64
+
65
+ :param with_query_expansion: boolean, set "query expansion" to true
66
+
67
+ .. versionadded:: 1.4.19
68
+
69
+ .. seealso::
70
+
71
+ :meth:`_expression.ColumnElement.match`
72
+
73
+ """
74
+
75
+ __visit_name__ = "mysql_match"
76
+
77
+ inherit_cache = True
78
+ modifiers: util.immutabledict[str, Any]
79
+
80
+ def __init__(self, *cols: elements.ColumnElement[Any], **kw: Any):
81
+ if not cols:
82
+ raise exc.ArgumentError("columns are required")
83
+
84
+ against = kw.pop("against", None)
85
+
86
+ if against is None:
87
+ raise exc.ArgumentError("against is required")
88
+ against = coercions.expect(
89
+ roles.ExpressionElementRole,
90
+ against,
91
+ )
92
+
93
+ left = elements.BooleanClauseList._construct_raw(
94
+ operators.comma_op,
95
+ clauses=cols,
96
+ )
97
+ left.group = False
98
+
99
+ flags = util.immutabledict(
100
+ {
101
+ "mysql_boolean_mode": kw.pop("in_boolean_mode", False),
102
+ "mysql_natural_language": kw.pop(
103
+ "in_natural_language_mode", False
104
+ ),
105
+ "mysql_query_expansion": kw.pop("with_query_expansion", False),
106
+ }
107
+ )
108
+
109
+ if kw:
110
+ raise exc.ArgumentError("unknown arguments: %s" % (", ".join(kw)))
111
+
112
+ super().__init__(left, against, operators.match_op, modifiers=flags)
113
+
114
+ @_generative
115
+ def in_boolean_mode(self) -> Self:
116
+ """Apply the "IN BOOLEAN MODE" modifier to the MATCH expression.
117
+
118
+ :return: a new :class:`_mysql.match` instance with modifications
119
+ applied.
120
+ """
121
+
122
+ self.modifiers = self.modifiers.union({"mysql_boolean_mode": True})
123
+ return self
124
+
125
+ @_generative
126
+ def in_natural_language_mode(self) -> Self:
127
+ """Apply the "IN NATURAL LANGUAGE MODE" modifier to the MATCH
128
+ expression.
129
+
130
+ :return: a new :class:`_mysql.match` instance with modifications
131
+ applied.
132
+ """
133
+
134
+ self.modifiers = self.modifiers.union({"mysql_natural_language": True})
135
+ return self
136
+
137
+ @_generative
138
+ def with_query_expansion(self) -> Self:
139
+ """Apply the "WITH QUERY EXPANSION" modifier to the MATCH expression.
140
+
141
+ :return: a new :class:`_mysql.match` instance with modifications
142
+ applied.
143
+ """
144
+
145
+ self.modifiers = self.modifiers.union({"mysql_query_expansion": True})
146
+ return self
@@ -0,0 +1,91 @@
1
+ # dialects/mysql/json.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 TYPE_CHECKING
11
+
12
+ from ... import types as sqltypes
13
+
14
+ if TYPE_CHECKING:
15
+ from ...engine.interfaces import Dialect
16
+ from ...sql.type_api import _BindProcessorType
17
+ from ...sql.type_api import _LiteralProcessorType
18
+
19
+
20
+ class JSON(sqltypes.JSON):
21
+ """MySQL JSON type.
22
+
23
+ MySQL supports JSON as of version 5.7.
24
+ MariaDB supports JSON (as an alias for LONGTEXT) as of version 10.2.
25
+
26
+ :class:`_mysql.JSON` is used automatically whenever the base
27
+ :class:`_types.JSON` datatype is used against a MySQL or MariaDB backend.
28
+
29
+ .. seealso::
30
+
31
+ :class:`_types.JSON` - main documentation for the generic
32
+ cross-platform JSON datatype.
33
+
34
+ The :class:`.mysql.JSON` type supports persistence of JSON values
35
+ as well as the core index operations provided by :class:`_types.JSON`
36
+ datatype, by adapting the operations to render the ``JSON_EXTRACT``
37
+ function at the database level.
38
+
39
+ """
40
+
41
+ pass
42
+
43
+
44
+ class _FormatTypeMixin:
45
+ def _format_value(self, value: Any) -> str:
46
+ raise NotImplementedError()
47
+
48
+ def bind_processor(self, dialect: Dialect) -> _BindProcessorType[Any]:
49
+ super_proc = self.string_bind_processor(dialect) # type: ignore[attr-defined] # noqa: E501
50
+
51
+ def process(value: Any) -> Any:
52
+ value = self._format_value(value)
53
+ if super_proc:
54
+ value = super_proc(value)
55
+ return value
56
+
57
+ return process
58
+
59
+ def literal_processor(
60
+ self, dialect: Dialect
61
+ ) -> _LiteralProcessorType[Any]:
62
+ super_proc = self.string_literal_processor(dialect) # type: ignore[attr-defined] # noqa: E501
63
+
64
+ def process(value: Any) -> str:
65
+ value = self._format_value(value)
66
+ if super_proc:
67
+ value = super_proc(value)
68
+ return value # type: ignore[no-any-return]
69
+
70
+ return process
71
+
72
+
73
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
74
+ def _format_value(self, value: Any) -> str:
75
+ if isinstance(value, int):
76
+ formatted_value = "$[%s]" % value
77
+ else:
78
+ formatted_value = '$."%s"' % value
79
+ return formatted_value
80
+
81
+
82
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
83
+ def _format_value(self, value: Any) -> str:
84
+ return "$%s" % (
85
+ "".join(
86
+ [
87
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
88
+ for elem in value
89
+ ]
90
+ )
91
+ )
@@ -0,0 +1,72 @@
1
+ # dialects/mysql/mariadb.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
+ from typing import Any
11
+
12
+ from .base import MariaDBIdentifierPreparer
13
+ from .base import MySQLDialect
14
+ from .base import MySQLIdentifierPreparer
15
+ from .base import MySQLTypeCompiler
16
+ from ...sql import sqltypes
17
+
18
+
19
+ class INET4(sqltypes.TypeEngine[str]):
20
+ """INET4 column type for MariaDB
21
+
22
+ .. versionadded:: 2.0.37
23
+ """
24
+
25
+ __visit_name__ = "INET4"
26
+
27
+
28
+ class INET6(sqltypes.TypeEngine[str]):
29
+ """INET6 column type for MariaDB
30
+
31
+ .. versionadded:: 2.0.37
32
+ """
33
+
34
+ __visit_name__ = "INET6"
35
+
36
+
37
+ class MariaDBTypeCompiler(MySQLTypeCompiler):
38
+ def visit_INET4(self, type_: INET4, **kwargs: Any) -> str:
39
+ return "INET4"
40
+
41
+ def visit_INET6(self, type_: INET6, **kwargs: Any) -> str:
42
+ return "INET6"
43
+
44
+
45
+ class MariaDBDialect(MySQLDialect):
46
+ is_mariadb = True
47
+ supports_statement_cache = True
48
+ name = "mariadb"
49
+ preparer: type[MySQLIdentifierPreparer] = MariaDBIdentifierPreparer
50
+ type_compiler_cls = MariaDBTypeCompiler
51
+
52
+
53
+ def loader(driver: str) -> type[MariaDBDialect]:
54
+ dialect_mod = __import__(
55
+ "sqlalchemy.dialects.mysql.%s" % driver
56
+ ).dialects.mysql
57
+
58
+ driver_mod = getattr(dialect_mod, driver)
59
+ if hasattr(driver_mod, "mariadb_dialect"):
60
+ driver_cls = driver_mod.mariadb_dialect
61
+ return driver_cls # type: ignore[no-any-return]
62
+ else:
63
+ driver_cls = driver_mod.dialect
64
+
65
+ return type(
66
+ "MariaDBDialect_%s" % driver,
67
+ (
68
+ MariaDBDialect,
69
+ driver_cls,
70
+ ),
71
+ {"supports_statement_cache": True},
72
+ )
@@ -0,0 +1,322 @@
1
+ # dialects/mysql/mariadbconnector.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+
8
+ """
9
+
10
+ .. dialect:: mysql+mariadbconnector
11
+ :name: MariaDB Connector/Python
12
+ :dbapi: mariadb
13
+ :connectstring: mariadb+mariadbconnector://<user>:<password>@<host>[:<port>]/<dbname>
14
+ :url: https://pypi.org/project/mariadb/
15
+
16
+ Driver Status
17
+ -------------
18
+
19
+ MariaDB Connector/Python enables Python programs to access MariaDB and MySQL
20
+ databases using an API which is compliant with the Python DB API 2.0 (PEP-249).
21
+ It is written in C and uses MariaDB Connector/C client library for client server
22
+ communication.
23
+
24
+ Note that the default driver for a ``mariadb://`` connection URI continues to
25
+ be ``mysqldb``. ``mariadb+mariadbconnector://`` is required to use this driver.
26
+
27
+ .. mariadb: https://github.com/mariadb-corporation/mariadb-connector-python
28
+
29
+ """ # noqa
30
+ from __future__ import annotations
31
+
32
+ import re
33
+ from typing import Any
34
+ from typing import Optional
35
+ from typing import Sequence
36
+ from typing import Tuple
37
+ from typing import TYPE_CHECKING
38
+ from typing import Union
39
+ from uuid import UUID as _python_UUID
40
+
41
+ from .base import MySQLCompiler
42
+ from .base import MySQLDialect
43
+ from .base import MySQLExecutionContext
44
+ from ... import sql
45
+ from ... import util
46
+ from ...sql import sqltypes
47
+
48
+ if TYPE_CHECKING:
49
+ from ...engine.base import Connection
50
+ from ...engine.interfaces import ConnectArgsType
51
+ from ...engine.interfaces import DBAPIConnection
52
+ from ...engine.interfaces import DBAPICursor
53
+ from ...engine.interfaces import DBAPIModule
54
+ from ...engine.interfaces import Dialect
55
+ from ...engine.interfaces import IsolationLevel
56
+ from ...engine.interfaces import PoolProxiedConnection
57
+ from ...engine.url import URL
58
+ from ...sql.compiler import SQLCompiler
59
+ from ...sql.type_api import _ResultProcessorType
60
+
61
+
62
+ mariadb_cpy_minimum_version = (1, 0, 1)
63
+
64
+
65
+ class _MariaDBUUID(sqltypes.UUID[sqltypes._UUID_RETURN]):
66
+ # work around JIRA issue
67
+ # https://jira.mariadb.org/browse/CONPY-270. When that issue is fixed,
68
+ # this type can be removed.
69
+ def result_processor(
70
+ self, dialect: Dialect, coltype: object
71
+ ) -> Optional[_ResultProcessorType[Any]]:
72
+ if self.as_uuid:
73
+
74
+ def process(value: Any) -> Any:
75
+ if value is not None:
76
+ if hasattr(value, "decode"):
77
+ value = value.decode("ascii")
78
+ value = _python_UUID(value)
79
+ return value
80
+
81
+ return process
82
+ else:
83
+
84
+ def process(value: Any) -> Any:
85
+ if value is not None:
86
+ if hasattr(value, "decode"):
87
+ value = value.decode("ascii")
88
+ value = str(_python_UUID(value))
89
+ return value
90
+
91
+ return process
92
+
93
+
94
+ class MySQLExecutionContext_mariadbconnector(MySQLExecutionContext):
95
+ _lastrowid: Optional[int] = None
96
+
97
+ def create_server_side_cursor(self) -> DBAPICursor:
98
+ return self._dbapi_connection.cursor(buffered=False)
99
+
100
+ def create_default_cursor(self) -> DBAPICursor:
101
+ return self._dbapi_connection.cursor(buffered=True)
102
+
103
+ def post_exec(self) -> None:
104
+ super().post_exec()
105
+
106
+ self._rowcount = self.cursor.rowcount
107
+
108
+ if TYPE_CHECKING:
109
+ assert isinstance(self.compiled, SQLCompiler)
110
+ if self.isinsert and self.compiled.postfetch_lastrowid:
111
+ self._lastrowid = self.cursor.lastrowid
112
+
113
+ def get_lastrowid(self) -> int:
114
+ if TYPE_CHECKING:
115
+ assert self._lastrowid is not None
116
+ return self._lastrowid
117
+
118
+
119
+ class MySQLCompiler_mariadbconnector(MySQLCompiler):
120
+ pass
121
+
122
+
123
+ class MySQLDialect_mariadbconnector(MySQLDialect):
124
+ driver = "mariadbconnector"
125
+ supports_statement_cache = True
126
+
127
+ # set this to True at the module level to prevent the driver from running
128
+ # against a backend that server detects as MySQL. currently this appears to
129
+ # be unnecessary as MariaDB client libraries have always worked against
130
+ # MySQL databases. However, if this changes at some point, this can be
131
+ # adjusted, but PLEASE ADD A TEST in test/dialect/mysql/test_dialect.py if
132
+ # this change is made at some point to ensure the correct exception
133
+ # is raised at the correct point when running the driver against
134
+ # a MySQL backend.
135
+ # is_mariadb = True
136
+
137
+ supports_unicode_statements = True
138
+ encoding = "utf8mb4"
139
+ convert_unicode = True
140
+ supports_sane_rowcount = True
141
+ supports_sane_multi_rowcount = True
142
+ supports_native_decimal = True
143
+ default_paramstyle = "qmark"
144
+ execution_ctx_cls = MySQLExecutionContext_mariadbconnector
145
+ statement_compiler = MySQLCompiler_mariadbconnector
146
+
147
+ supports_server_side_cursors = True
148
+
149
+ colspecs = util.update_copy(
150
+ MySQLDialect.colspecs, {sqltypes.Uuid: _MariaDBUUID}
151
+ )
152
+
153
+ @util.memoized_property
154
+ def _dbapi_version(self) -> Tuple[int, ...]:
155
+ if self.dbapi and hasattr(self.dbapi, "__version__"):
156
+ return tuple(
157
+ [
158
+ int(x)
159
+ for x in re.findall(
160
+ r"(\d+)(?:[-\.]?|$)", self.dbapi.__version__
161
+ )
162
+ ]
163
+ )
164
+ else:
165
+ return (99, 99, 99)
166
+
167
+ def __init__(self, **kwargs: Any) -> None:
168
+ super().__init__(**kwargs)
169
+ self.paramstyle = "qmark"
170
+ if self.dbapi is not None:
171
+ if self._dbapi_version < mariadb_cpy_minimum_version:
172
+ raise NotImplementedError(
173
+ "The minimum required version for MariaDB "
174
+ "Connector/Python is %s"
175
+ % ".".join(str(x) for x in mariadb_cpy_minimum_version)
176
+ )
177
+
178
+ @classmethod
179
+ def import_dbapi(cls) -> DBAPIModule:
180
+ return __import__("mariadb")
181
+
182
+ def is_disconnect(
183
+ self,
184
+ e: DBAPIModule.Error,
185
+ connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
186
+ cursor: Optional[DBAPICursor],
187
+ ) -> bool:
188
+ if super().is_disconnect(e, connection, cursor):
189
+ return True
190
+ elif isinstance(e, self.loaded_dbapi.Error):
191
+ str_e = str(e).lower()
192
+ return "not connected" in str_e or "isn't valid" in str_e
193
+ else:
194
+ return False
195
+
196
+ def create_connect_args(self, url: URL) -> ConnectArgsType:
197
+ opts = url.translate_connect_args()
198
+ opts.update(url.query)
199
+
200
+ int_params = [
201
+ "connect_timeout",
202
+ "read_timeout",
203
+ "write_timeout",
204
+ "client_flag",
205
+ "port",
206
+ "pool_size",
207
+ ]
208
+ bool_params = [
209
+ "local_infile",
210
+ "ssl_verify_cert",
211
+ "ssl",
212
+ "pool_reset_connection",
213
+ "compress",
214
+ ]
215
+
216
+ for key in int_params:
217
+ util.coerce_kw_type(opts, key, int)
218
+ for key in bool_params:
219
+ util.coerce_kw_type(opts, key, bool)
220
+
221
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
222
+ # supports_sane_rowcount.
223
+ client_flag = opts.get("client_flag", 0)
224
+ if self.dbapi is not None:
225
+ try:
226
+ CLIENT_FLAGS = __import__(
227
+ self.dbapi.__name__ + ".constants.CLIENT"
228
+ ).constants.CLIENT
229
+ client_flag |= CLIENT_FLAGS.FOUND_ROWS
230
+ except (AttributeError, ImportError):
231
+ self.supports_sane_rowcount = False
232
+ opts["client_flag"] = client_flag
233
+ return [], opts
234
+
235
+ def _extract_error_code(self, exception: DBAPIModule.Error) -> int:
236
+ try:
237
+ rc: int = exception.errno
238
+ except:
239
+ rc = -1
240
+ return rc
241
+
242
+ def _detect_charset(self, connection: Connection) -> str:
243
+ return "utf8mb4"
244
+
245
+ def get_isolation_level_values(
246
+ self, dbapi_conn: DBAPIConnection
247
+ ) -> Sequence[IsolationLevel]:
248
+ return (
249
+ "SERIALIZABLE",
250
+ "READ UNCOMMITTED",
251
+ "READ COMMITTED",
252
+ "REPEATABLE READ",
253
+ "AUTOCOMMIT",
254
+ )
255
+
256
+ def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
257
+ return bool(dbapi_conn.autocommit)
258
+
259
+ def set_isolation_level(
260
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
261
+ ) -> None:
262
+ if level == "AUTOCOMMIT":
263
+ dbapi_connection.autocommit = True
264
+ else:
265
+ dbapi_connection.autocommit = False
266
+ super().set_isolation_level(dbapi_connection, level)
267
+
268
+ def do_begin_twophase(self, connection: Connection, xid: Any) -> None:
269
+ connection.execute(
270
+ sql.text("XA BEGIN :xid").bindparams(
271
+ sql.bindparam("xid", xid, literal_execute=True)
272
+ )
273
+ )
274
+
275
+ def do_prepare_twophase(self, connection: Connection, xid: Any) -> None:
276
+ connection.execute(
277
+ sql.text("XA END :xid").bindparams(
278
+ sql.bindparam("xid", xid, literal_execute=True)
279
+ )
280
+ )
281
+ connection.execute(
282
+ sql.text("XA PREPARE :xid").bindparams(
283
+ sql.bindparam("xid", xid, literal_execute=True)
284
+ )
285
+ )
286
+
287
+ def do_rollback_twophase(
288
+ self,
289
+ connection: Connection,
290
+ xid: Any,
291
+ is_prepared: bool = True,
292
+ recover: bool = False,
293
+ ) -> None:
294
+ if not is_prepared:
295
+ connection.execute(
296
+ sql.text("XA END :xid").bindparams(
297
+ sql.bindparam("xid", xid, literal_execute=True)
298
+ )
299
+ )
300
+ connection.execute(
301
+ sql.text("XA ROLLBACK :xid").bindparams(
302
+ sql.bindparam("xid", xid, literal_execute=True)
303
+ )
304
+ )
305
+
306
+ def do_commit_twophase(
307
+ self,
308
+ connection: Connection,
309
+ xid: Any,
310
+ is_prepared: bool = True,
311
+ recover: bool = False,
312
+ ) -> None:
313
+ if not is_prepared:
314
+ self.do_prepare_twophase(connection, xid)
315
+ connection.execute(
316
+ sql.text("XA COMMIT :xid").bindparams(
317
+ sql.bindparam("xid", xid, literal_execute=True)
318
+ )
319
+ )
320
+
321
+
322
+ dialect = MySQLDialect_mariadbconnector