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,81 @@
1
+ # dialects/mysql/json.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
+ from ... import types as sqltypes
10
+
11
+
12
+ class JSON(sqltypes.JSON):
13
+ """MySQL JSON type.
14
+
15
+ MySQL supports JSON as of version 5.7.
16
+ MariaDB supports JSON (as an alias for LONGTEXT) as of version 10.2.
17
+
18
+ :class:`_mysql.JSON` is used automatically whenever the base
19
+ :class:`_types.JSON` datatype is used against a MySQL or MariaDB backend.
20
+
21
+ .. seealso::
22
+
23
+ :class:`_types.JSON` - main documentation for the generic
24
+ cross-platform JSON datatype.
25
+
26
+ The :class:`.mysql.JSON` type supports persistence of JSON values
27
+ as well as the core index operations provided by :class:`_types.JSON`
28
+ datatype, by adapting the operations to render the ``JSON_EXTRACT``
29
+ function at the database level.
30
+
31
+ """
32
+
33
+ pass
34
+
35
+
36
+ class _FormatTypeMixin:
37
+ def _format_value(self, value):
38
+ raise NotImplementedError()
39
+
40
+ def bind_processor(self, dialect):
41
+ super_proc = self.string_bind_processor(dialect)
42
+
43
+ def process(value):
44
+ value = self._format_value(value)
45
+ if super_proc:
46
+ value = super_proc(value)
47
+ return value
48
+
49
+ return process
50
+
51
+ def literal_processor(self, dialect):
52
+ super_proc = self.string_literal_processor(dialect)
53
+
54
+ def process(value):
55
+ value = self._format_value(value)
56
+ if super_proc:
57
+ value = super_proc(value)
58
+ return value
59
+
60
+ return process
61
+
62
+
63
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
64
+ def _format_value(self, value):
65
+ if isinstance(value, int):
66
+ value = "$[%s]" % value
67
+ else:
68
+ value = '$."%s"' % value
69
+ return value
70
+
71
+
72
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
73
+ def _format_value(self, value):
74
+ return "$%s" % (
75
+ "".join(
76
+ [
77
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
78
+ for elem in value
79
+ ]
80
+ )
81
+ )
@@ -0,0 +1,32 @@
1
+ # dialects/mysql/mariadb.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
+ from .base import MariaDBIdentifierPreparer
9
+ from .base import MySQLDialect
10
+
11
+
12
+ class MariaDBDialect(MySQLDialect):
13
+ is_mariadb = True
14
+ supports_statement_cache = True
15
+ name = "mariadb"
16
+ preparer = MariaDBIdentifierPreparer
17
+
18
+
19
+ def loader(driver):
20
+ driver_mod = __import__(
21
+ "sqlalchemy.dialects.mysql.%s" % driver
22
+ ).dialects.mysql
23
+ driver_cls = getattr(driver_mod, driver).dialect
24
+
25
+ return type(
26
+ "MariaDBDialect_%s" % driver,
27
+ (
28
+ MariaDBDialect,
29
+ driver_cls,
30
+ ),
31
+ {"supports_statement_cache": True},
32
+ )
@@ -0,0 +1,277 @@
1
+ # dialects/mysql/mariadbconnector.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
+ """
11
+
12
+ .. dialect:: mysql+mariadbconnector
13
+ :name: MariaDB Connector/Python
14
+ :dbapi: mariadb
15
+ :connectstring: mariadb+mariadbconnector://<user>:<password>@<host>[:<port>]/<dbname>
16
+ :url: https://pypi.org/project/mariadb/
17
+
18
+ Driver Status
19
+ -------------
20
+
21
+ MariaDB Connector/Python enables Python programs to access MariaDB and MySQL
22
+ databases using an API which is compliant with the Python DB API 2.0 (PEP-249).
23
+ It is written in C and uses MariaDB Connector/C client library for client server
24
+ communication.
25
+
26
+ Note that the default driver for a ``mariadb://`` connection URI continues to
27
+ be ``mysqldb``. ``mariadb+mariadbconnector://`` is required to use this driver.
28
+
29
+ .. mariadb: https://github.com/mariadb-corporation/mariadb-connector-python
30
+
31
+ """ # noqa
32
+ import re
33
+ from uuid import UUID as _python_UUID
34
+
35
+ from .base import MySQLCompiler
36
+ from .base import MySQLDialect
37
+ from .base import MySQLExecutionContext
38
+ from ... import sql
39
+ from ... import util
40
+ from ...sql import sqltypes
41
+
42
+
43
+ mariadb_cpy_minimum_version = (1, 0, 1)
44
+
45
+
46
+ class _MariaDBUUID(sqltypes.UUID[sqltypes._UUID_RETURN]):
47
+ # work around JIRA issue
48
+ # https://jira.mariadb.org/browse/CONPY-270. When that issue is fixed,
49
+ # this type can be removed.
50
+ def result_processor(self, dialect, coltype):
51
+ if self.as_uuid:
52
+
53
+ def process(value):
54
+ if value is not None:
55
+ if hasattr(value, "decode"):
56
+ value = value.decode("ascii")
57
+ value = _python_UUID(value)
58
+ return value
59
+
60
+ return process
61
+ else:
62
+
63
+ def process(value):
64
+ if value is not None:
65
+ if hasattr(value, "decode"):
66
+ value = value.decode("ascii")
67
+ value = str(_python_UUID(value))
68
+ return value
69
+
70
+ return process
71
+
72
+
73
+ class MySQLExecutionContext_mariadbconnector(MySQLExecutionContext):
74
+ _lastrowid = None
75
+
76
+ def create_server_side_cursor(self):
77
+ return self._dbapi_connection.cursor(buffered=False)
78
+
79
+ def create_default_cursor(self):
80
+ return self._dbapi_connection.cursor(buffered=True)
81
+
82
+ def post_exec(self):
83
+ super().post_exec()
84
+
85
+ self._rowcount = self.cursor.rowcount
86
+
87
+ if self.isinsert and self.compiled.postfetch_lastrowid:
88
+ self._lastrowid = self.cursor.lastrowid
89
+
90
+ def get_lastrowid(self):
91
+ return self._lastrowid
92
+
93
+
94
+ class MySQLCompiler_mariadbconnector(MySQLCompiler):
95
+ pass
96
+
97
+
98
+ class MySQLDialect_mariadbconnector(MySQLDialect):
99
+ driver = "mariadbconnector"
100
+ supports_statement_cache = True
101
+
102
+ # set this to True at the module level to prevent the driver from running
103
+ # against a backend that server detects as MySQL. currently this appears to
104
+ # be unnecessary as MariaDB client libraries have always worked against
105
+ # MySQL databases. However, if this changes at some point, this can be
106
+ # adjusted, but PLEASE ADD A TEST in test/dialect/mysql/test_dialect.py if
107
+ # this change is made at some point to ensure the correct exception
108
+ # is raised at the correct point when running the driver against
109
+ # a MySQL backend.
110
+ # is_mariadb = True
111
+
112
+ supports_unicode_statements = True
113
+ encoding = "utf8mb4"
114
+ convert_unicode = True
115
+ supports_sane_rowcount = True
116
+ supports_sane_multi_rowcount = True
117
+ supports_native_decimal = True
118
+ default_paramstyle = "qmark"
119
+ execution_ctx_cls = MySQLExecutionContext_mariadbconnector
120
+ statement_compiler = MySQLCompiler_mariadbconnector
121
+
122
+ supports_server_side_cursors = True
123
+
124
+ colspecs = util.update_copy(
125
+ MySQLDialect.colspecs, {sqltypes.Uuid: _MariaDBUUID}
126
+ )
127
+
128
+ @util.memoized_property
129
+ def _dbapi_version(self):
130
+ if self.dbapi and hasattr(self.dbapi, "__version__"):
131
+ return tuple(
132
+ [
133
+ int(x)
134
+ for x in re.findall(
135
+ r"(\d+)(?:[-\.]?|$)", self.dbapi.__version__
136
+ )
137
+ ]
138
+ )
139
+ else:
140
+ return (99, 99, 99)
141
+
142
+ def __init__(self, **kwargs):
143
+ super().__init__(**kwargs)
144
+ self.paramstyle = "qmark"
145
+ if self.dbapi is not None:
146
+ if self._dbapi_version < mariadb_cpy_minimum_version:
147
+ raise NotImplementedError(
148
+ "The minimum required version for MariaDB "
149
+ "Connector/Python is %s"
150
+ % ".".join(str(x) for x in mariadb_cpy_minimum_version)
151
+ )
152
+
153
+ @classmethod
154
+ def import_dbapi(cls):
155
+ return __import__("mariadb")
156
+
157
+ def is_disconnect(self, e, connection, cursor):
158
+ if super().is_disconnect(e, connection, cursor):
159
+ return True
160
+ elif isinstance(e, self.dbapi.Error):
161
+ str_e = str(e).lower()
162
+ return "not connected" in str_e or "isn't valid" in str_e
163
+ else:
164
+ return False
165
+
166
+ def create_connect_args(self, url):
167
+ opts = url.translate_connect_args()
168
+ opts.update(url.query)
169
+
170
+ int_params = [
171
+ "connect_timeout",
172
+ "read_timeout",
173
+ "write_timeout",
174
+ "client_flag",
175
+ "port",
176
+ "pool_size",
177
+ ]
178
+ bool_params = [
179
+ "local_infile",
180
+ "ssl_verify_cert",
181
+ "ssl",
182
+ "pool_reset_connection",
183
+ "compress",
184
+ ]
185
+
186
+ for key in int_params:
187
+ util.coerce_kw_type(opts, key, int)
188
+ for key in bool_params:
189
+ util.coerce_kw_type(opts, key, bool)
190
+
191
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
192
+ # supports_sane_rowcount.
193
+ client_flag = opts.get("client_flag", 0)
194
+ if self.dbapi is not None:
195
+ try:
196
+ CLIENT_FLAGS = __import__(
197
+ self.dbapi.__name__ + ".constants.CLIENT"
198
+ ).constants.CLIENT
199
+ client_flag |= CLIENT_FLAGS.FOUND_ROWS
200
+ except (AttributeError, ImportError):
201
+ self.supports_sane_rowcount = False
202
+ opts["client_flag"] = client_flag
203
+ return [[], opts]
204
+
205
+ def _extract_error_code(self, exception):
206
+ try:
207
+ rc = exception.errno
208
+ except:
209
+ rc = -1
210
+ return rc
211
+
212
+ def _detect_charset(self, connection):
213
+ return "utf8mb4"
214
+
215
+ def get_isolation_level_values(self, dbapi_connection):
216
+ return (
217
+ "SERIALIZABLE",
218
+ "READ UNCOMMITTED",
219
+ "READ COMMITTED",
220
+ "REPEATABLE READ",
221
+ "AUTOCOMMIT",
222
+ )
223
+
224
+ def set_isolation_level(self, connection, level):
225
+ if level == "AUTOCOMMIT":
226
+ connection.autocommit = True
227
+ else:
228
+ connection.autocommit = False
229
+ super().set_isolation_level(connection, level)
230
+
231
+ def do_begin_twophase(self, connection, xid):
232
+ connection.execute(
233
+ sql.text("XA BEGIN :xid").bindparams(
234
+ sql.bindparam("xid", xid, literal_execute=True)
235
+ )
236
+ )
237
+
238
+ def do_prepare_twophase(self, connection, xid):
239
+ connection.execute(
240
+ sql.text("XA END :xid").bindparams(
241
+ sql.bindparam("xid", xid, literal_execute=True)
242
+ )
243
+ )
244
+ connection.execute(
245
+ sql.text("XA PREPARE :xid").bindparams(
246
+ sql.bindparam("xid", xid, literal_execute=True)
247
+ )
248
+ )
249
+
250
+ def do_rollback_twophase(
251
+ self, connection, xid, is_prepared=True, recover=False
252
+ ):
253
+ if not is_prepared:
254
+ connection.execute(
255
+ sql.text("XA END :xid").bindparams(
256
+ sql.bindparam("xid", xid, literal_execute=True)
257
+ )
258
+ )
259
+ connection.execute(
260
+ sql.text("XA ROLLBACK :xid").bindparams(
261
+ sql.bindparam("xid", xid, literal_execute=True)
262
+ )
263
+ )
264
+
265
+ def do_commit_twophase(
266
+ self, connection, xid, is_prepared=True, recover=False
267
+ ):
268
+ if not is_prepared:
269
+ self.do_prepare_twophase(connection, xid)
270
+ connection.execute(
271
+ sql.text("XA COMMIT :xid").bindparams(
272
+ sql.bindparam("xid", xid, literal_execute=True)
273
+ )
274
+ )
275
+
276
+
277
+ dialect = MySQLDialect_mariadbconnector
@@ -0,0 +1,180 @@
1
+ # dialects/mysql/mysqlconnector.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
+ r"""
11
+ .. dialect:: mysql+mysqlconnector
12
+ :name: MySQL Connector/Python
13
+ :dbapi: myconnpy
14
+ :connectstring: mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
15
+ :url: https://pypi.org/project/mysql-connector-python/
16
+
17
+ .. note::
18
+
19
+ The MySQL Connector/Python DBAPI has had many issues since its release,
20
+ some of which may remain unresolved, and the mysqlconnector dialect is
21
+ **not tested as part of SQLAlchemy's continuous integration**.
22
+ The recommended MySQL dialects are mysqlclient and PyMySQL.
23
+
24
+ """ # noqa
25
+
26
+ import re
27
+
28
+ from .base import BIT
29
+ from .base import MySQLCompiler
30
+ from .base import MySQLDialect
31
+ from .base import MySQLIdentifierPreparer
32
+ from ... import util
33
+
34
+
35
+ class MySQLCompiler_mysqlconnector(MySQLCompiler):
36
+ def visit_mod_binary(self, binary, operator, **kw):
37
+ return (
38
+ self.process(binary.left, **kw)
39
+ + " % "
40
+ + self.process(binary.right, **kw)
41
+ )
42
+
43
+
44
+ class MySQLIdentifierPreparer_mysqlconnector(MySQLIdentifierPreparer):
45
+ @property
46
+ def _double_percents(self):
47
+ return False
48
+
49
+ @_double_percents.setter
50
+ def _double_percents(self, value):
51
+ pass
52
+
53
+ def _escape_identifier(self, value):
54
+ value = value.replace(self.escape_quote, self.escape_to_quote)
55
+ return value
56
+
57
+
58
+ class _myconnpyBIT(BIT):
59
+ def result_processor(self, dialect, coltype):
60
+ """MySQL-connector already converts mysql bits, so."""
61
+
62
+ return None
63
+
64
+
65
+ class MySQLDialect_mysqlconnector(MySQLDialect):
66
+ driver = "mysqlconnector"
67
+ supports_statement_cache = True
68
+
69
+ supports_sane_rowcount = True
70
+ supports_sane_multi_rowcount = True
71
+
72
+ supports_native_decimal = True
73
+
74
+ default_paramstyle = "format"
75
+ statement_compiler = MySQLCompiler_mysqlconnector
76
+
77
+ preparer = MySQLIdentifierPreparer_mysqlconnector
78
+
79
+ colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _myconnpyBIT})
80
+
81
+ @classmethod
82
+ def import_dbapi(cls):
83
+ from mysql import connector
84
+
85
+ return connector
86
+
87
+ def do_ping(self, dbapi_connection):
88
+ dbapi_connection.ping(False)
89
+ return True
90
+
91
+ def create_connect_args(self, url):
92
+ opts = url.translate_connect_args(username="user")
93
+
94
+ opts.update(url.query)
95
+
96
+ util.coerce_kw_type(opts, "allow_local_infile", bool)
97
+ util.coerce_kw_type(opts, "autocommit", bool)
98
+ util.coerce_kw_type(opts, "buffered", bool)
99
+ util.coerce_kw_type(opts, "client_flag", int)
100
+ util.coerce_kw_type(opts, "compress", bool)
101
+ util.coerce_kw_type(opts, "connection_timeout", int)
102
+ util.coerce_kw_type(opts, "connect_timeout", int)
103
+ util.coerce_kw_type(opts, "consume_results", bool)
104
+ util.coerce_kw_type(opts, "force_ipv6", bool)
105
+ util.coerce_kw_type(opts, "get_warnings", bool)
106
+ util.coerce_kw_type(opts, "pool_reset_session", bool)
107
+ util.coerce_kw_type(opts, "pool_size", int)
108
+ util.coerce_kw_type(opts, "raise_on_warnings", bool)
109
+ util.coerce_kw_type(opts, "raw", bool)
110
+ util.coerce_kw_type(opts, "ssl_verify_cert", bool)
111
+ util.coerce_kw_type(opts, "use_pure", bool)
112
+ util.coerce_kw_type(opts, "use_unicode", bool)
113
+
114
+ # unfortunately, MySQL/connector python refuses to release a
115
+ # cursor without reading fully, so non-buffered isn't an option
116
+ opts.setdefault("buffered", True)
117
+
118
+ # FOUND_ROWS must be set in ClientFlag to enable
119
+ # supports_sane_rowcount.
120
+ if self.dbapi is not None:
121
+ try:
122
+ from mysql.connector.constants import ClientFlag
123
+
124
+ client_flags = opts.get(
125
+ "client_flags", ClientFlag.get_default()
126
+ )
127
+ client_flags |= ClientFlag.FOUND_ROWS
128
+ opts["client_flags"] = client_flags
129
+ except Exception:
130
+ pass
131
+ return [[], opts]
132
+
133
+ @util.memoized_property
134
+ def _mysqlconnector_version_info(self):
135
+ if self.dbapi and hasattr(self.dbapi, "__version__"):
136
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__)
137
+ if m:
138
+ return tuple(int(x) for x in m.group(1, 2, 3) if x is not None)
139
+
140
+ def _detect_charset(self, connection):
141
+ return connection.connection.charset
142
+
143
+ def _extract_error_code(self, exception):
144
+ return exception.errno
145
+
146
+ def is_disconnect(self, e, connection, cursor):
147
+ errnos = (2006, 2013, 2014, 2045, 2055, 2048)
148
+ exceptions = (self.dbapi.OperationalError, self.dbapi.InterfaceError)
149
+ if isinstance(e, exceptions):
150
+ return (
151
+ e.errno in errnos
152
+ or "MySQL Connection not available." in str(e)
153
+ or "Connection to MySQL is not available" in str(e)
154
+ )
155
+ else:
156
+ return False
157
+
158
+ def _compat_fetchall(self, rp, charset=None):
159
+ return rp.fetchall()
160
+
161
+ def _compat_fetchone(self, rp, charset=None):
162
+ return rp.fetchone()
163
+
164
+ _isolation_lookup = {
165
+ "SERIALIZABLE",
166
+ "READ UNCOMMITTED",
167
+ "READ COMMITTED",
168
+ "REPEATABLE READ",
169
+ "AUTOCOMMIT",
170
+ }
171
+
172
+ def _set_isolation_level(self, connection, level):
173
+ if level == "AUTOCOMMIT":
174
+ connection.autocommit = True
175
+ else:
176
+ connection.autocommit = False
177
+ super()._set_isolation_level(connection, level)
178
+
179
+
180
+ dialect = MySQLDialect_mysqlconnector