SQLAlchemy 2.0.36__cp313-cp313-win_amd64.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.
- SQLAlchemy-2.0.36.dist-info/LICENSE +19 -0
- SQLAlchemy-2.0.36.dist-info/METADATA +243 -0
- SQLAlchemy-2.0.36.dist-info/RECORD +273 -0
- SQLAlchemy-2.0.36.dist-info/WHEEL +5 -0
- SQLAlchemy-2.0.36.dist-info/top_level.txt +1 -0
- sqlalchemy/__init__.py +294 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +174 -0
- sqlalchemy/connectors/asyncio.py +213 -0
- sqlalchemy/connectors/pyodbc.py +249 -0
- sqlalchemy/cyextension/__init__.py +6 -0
- sqlalchemy/cyextension/collections.cp313-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/collections.pyx +409 -0
- sqlalchemy/cyextension/immutabledict.cp313-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/immutabledict.pxd +8 -0
- sqlalchemy/cyextension/immutabledict.pyx +133 -0
- sqlalchemy/cyextension/processors.cp313-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/processors.pyx +68 -0
- sqlalchemy/cyextension/resultproxy.cp313-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/resultproxy.pyx +102 -0
- sqlalchemy/cyextension/util.cp313-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/util.pyx +91 -0
- sqlalchemy/dialects/__init__.py +61 -0
- sqlalchemy/dialects/_typing.py +25 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +64 -0
- sqlalchemy/dialects/mssql/base.py +4010 -0
- sqlalchemy/dialects/mssql/information_schema.py +254 -0
- sqlalchemy/dialects/mssql/json.py +133 -0
- sqlalchemy/dialects/mssql/provision.py +162 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +745 -0
- sqlalchemy/dialects/mysql/__init__.py +101 -0
- sqlalchemy/dialects/mysql/aiomysql.py +333 -0
- sqlalchemy/dialects/mysql/asyncmy.py +337 -0
- sqlalchemy/dialects/mysql/base.py +3494 -0
- sqlalchemy/dialects/mysql/cymysql.py +84 -0
- sqlalchemy/dialects/mysql/dml.py +219 -0
- sqlalchemy/dialects/mysql/enumerated.py +244 -0
- sqlalchemy/dialects/mysql/expression.py +141 -0
- sqlalchemy/dialects/mysql/json.py +81 -0
- sqlalchemy/dialects/mysql/mariadb.py +32 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +277 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +180 -0
- sqlalchemy/dialects/mysql/mysqldb.py +303 -0
- sqlalchemy/dialects/mysql/provision.py +110 -0
- sqlalchemy/dialects/mysql/pymysql.py +137 -0
- sqlalchemy/dialects/mysql/pyodbc.py +138 -0
- sqlalchemy/dialects/mysql/reflection.py +677 -0
- sqlalchemy/dialects/mysql/reserved_words.py +571 -0
- sqlalchemy/dialects/mysql/types.py +774 -0
- sqlalchemy/dialects/oracle/__init__.py +67 -0
- sqlalchemy/dialects/oracle/base.py +3271 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1483 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/oracledb.py +431 -0
- sqlalchemy/dialects/oracle/provision.py +220 -0
- sqlalchemy/dialects/oracle/types.py +287 -0
- sqlalchemy/dialects/postgresql/__init__.py +167 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +187 -0
- sqlalchemy/dialects/postgresql/array.py +425 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1274 -0
- sqlalchemy/dialects/postgresql/base.py +5008 -0
- sqlalchemy/dialects/postgresql/dml.py +310 -0
- sqlalchemy/dialects/postgresql/ext.py +496 -0
- sqlalchemy/dialects/postgresql/hstore.py +397 -0
- sqlalchemy/dialects/postgresql/json.py +333 -0
- sqlalchemy/dialects/postgresql/named_types.py +509 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +662 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +300 -0
- sqlalchemy/dialects/postgresql/provision.py +175 -0
- sqlalchemy/dialects/postgresql/psycopg.py +772 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +886 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1029 -0
- sqlalchemy/dialects/postgresql/types.py +303 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +396 -0
- sqlalchemy/dialects/sqlite/base.py +2805 -0
- sqlalchemy/dialects/sqlite/dml.py +240 -0
- sqlalchemy/dialects/sqlite/json.py +92 -0
- sqlalchemy/dialects/sqlite/provision.py +198 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +155 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_py_processors.py +136 -0
- sqlalchemy/engine/_py_row.py +128 -0
- sqlalchemy/engine/_py_util.py +74 -0
- sqlalchemy/engine/base.py +3375 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +875 -0
- sqlalchemy/engine/cursor.py +2181 -0
- sqlalchemy/engine/default.py +2365 -0
- sqlalchemy/engine/events.py +951 -0
- sqlalchemy/engine/interfaces.py +3403 -0
- sqlalchemy/engine/mock.py +131 -0
- sqlalchemy/engine/processors.py +61 -0
- sqlalchemy/engine/reflection.py +2098 -0
- sqlalchemy/engine/result.py +2382 -0
- sqlalchemy/engine/row.py +401 -0
- sqlalchemy/engine/strategies.py +19 -0
- sqlalchemy/engine/url.py +910 -0
- sqlalchemy/engine/util.py +167 -0
- sqlalchemy/event/__init__.py +25 -0
- sqlalchemy/event/api.py +225 -0
- sqlalchemy/event/attr.py +655 -0
- sqlalchemy/event/base.py +470 -0
- sqlalchemy/event/legacy.py +246 -0
- sqlalchemy/event/registry.py +386 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +830 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2013 -0
- sqlalchemy/ext/asyncio/__init__.py +25 -0
- sqlalchemy/ext/asyncio/base.py +279 -0
- sqlalchemy/ext/asyncio/engine.py +1466 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +961 -0
- sqlalchemy/ext/asyncio/scoping.py +1614 -0
- sqlalchemy/ext/asyncio/session.py +1936 -0
- sqlalchemy/ext/automap.py +1691 -0
- sqlalchemy/ext/baked.py +574 -0
- sqlalchemy/ext/compiler.py +570 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +548 -0
- sqlalchemy/ext/horizontal_shard.py +481 -0
- sqlalchemy/ext/hybrid.py +1514 -0
- sqlalchemy/ext/indexable.py +341 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1073 -0
- sqlalchemy/ext/mypy/__init__.py +6 -0
- sqlalchemy/ext/mypy/apply.py +320 -0
- sqlalchemy/ext/mypy/decl_class.py +515 -0
- sqlalchemy/ext/mypy/infer.py +590 -0
- sqlalchemy/ext/mypy/names.py +335 -0
- sqlalchemy/ext/mypy/plugin.py +303 -0
- sqlalchemy/ext/mypy/util.py +357 -0
- sqlalchemy/ext/orderinglist.py +416 -0
- sqlalchemy/ext/serializer.py +181 -0
- sqlalchemy/future/__init__.py +16 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +174 -0
- sqlalchemy/log.py +288 -0
- sqlalchemy/orm/__init__.py +170 -0
- sqlalchemy/orm/_orm_constructors.py +2571 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2835 -0
- sqlalchemy/orm/base.py +973 -0
- sqlalchemy/orm/bulk_persistence.py +2123 -0
- sqlalchemy/orm/clsregistry.py +571 -0
- sqlalchemy/orm/collections.py +1620 -0
- sqlalchemy/orm/context.py +3268 -0
- sqlalchemy/orm/decl_api.py +1883 -0
- sqlalchemy/orm/decl_base.py +2190 -0
- sqlalchemy/orm/dependency.py +1304 -0
- sqlalchemy/orm/descriptor_props.py +1076 -0
- sqlalchemy/orm/dynamic.py +300 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3261 -0
- sqlalchemy/orm/exc.py +228 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +754 -0
- sqlalchemy/orm/interfaces.py +1474 -0
- sqlalchemy/orm/loading.py +1682 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4432 -0
- sqlalchemy/orm/path_registry.py +811 -0
- sqlalchemy/orm/persistence.py +1782 -0
- sqlalchemy/orm/properties.py +886 -0
- sqlalchemy/orm/query.py +3396 -0
- sqlalchemy/orm/relationships.py +3500 -0
- sqlalchemy/orm/scoping.py +2165 -0
- sqlalchemy/orm/session.py +5301 -0
- sqlalchemy/orm/state.py +1143 -0
- sqlalchemy/orm/state_changes.py +198 -0
- sqlalchemy/orm/strategies.py +3473 -0
- sqlalchemy/orm/strategy_options.py +2569 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +796 -0
- sqlalchemy/orm/util.py +2424 -0
- sqlalchemy/orm/writeonly.py +678 -0
- sqlalchemy/pool/__init__.py +44 -0
- sqlalchemy/pool/base.py +1515 -0
- sqlalchemy/pool/events.py +370 -0
- sqlalchemy/pool/impl.py +581 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +70 -0
- sqlalchemy/sql/__init__.py +145 -0
- sqlalchemy/sql/_dml_constructors.py +140 -0
- sqlalchemy/sql/_elements_constructors.py +1850 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_py_util.py +75 -0
- sqlalchemy/sql/_selectable_constructors.py +635 -0
- sqlalchemy/sql/_typing.py +460 -0
- sqlalchemy/sql/annotation.py +585 -0
- sqlalchemy/sql/base.py +2185 -0
- sqlalchemy/sql/cache_key.py +1057 -0
- sqlalchemy/sql/coercions.py +1405 -0
- sqlalchemy/sql/compiler.py +7818 -0
- sqlalchemy/sql/crud.py +1669 -0
- sqlalchemy/sql/ddl.py +1378 -0
- sqlalchemy/sql/default_comparator.py +552 -0
- sqlalchemy/sql/dml.py +1817 -0
- sqlalchemy/sql/elements.py +5499 -0
- sqlalchemy/sql/events.py +455 -0
- sqlalchemy/sql/expression.py +162 -0
- sqlalchemy/sql/functions.py +2055 -0
- sqlalchemy/sql/lambdas.py +1449 -0
- sqlalchemy/sql/naming.py +212 -0
- sqlalchemy/sql/operators.py +2579 -0
- sqlalchemy/sql/roles.py +323 -0
- sqlalchemy/sql/schema.py +6158 -0
- sqlalchemy/sql/selectable.py +7004 -0
- sqlalchemy/sql/sqltypes.py +3827 -0
- sqlalchemy/sql/traversals.py +1024 -0
- sqlalchemy/sql/type_api.py +2339 -0
- sqlalchemy/sql/util.py +1486 -0
- sqlalchemy/sql/visitors.py +1165 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +989 -0
- sqlalchemy/testing/assertsql.py +516 -0
- sqlalchemy/testing/asyncio.py +135 -0
- sqlalchemy/testing/config.py +427 -0
- sqlalchemy/testing/engines.py +472 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +435 -0
- sqlalchemy/testing/fixtures/__init__.py +28 -0
- sqlalchemy/testing/fixtures/base.py +366 -0
- sqlalchemy/testing/fixtures/mypy.py +312 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +503 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +51 -0
- sqlalchemy/testing/plugin/plugin_base.py +779 -0
- sqlalchemy/testing/plugin/pytestplugin.py +868 -0
- sqlalchemy/testing/profiling.py +324 -0
- sqlalchemy/testing/provision.py +496 -0
- sqlalchemy/testing/requirements.py +1818 -0
- sqlalchemy/testing/schema.py +224 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +211 -0
- sqlalchemy/testing/suite/test_ddl.py +389 -0
- sqlalchemy/testing/suite/test_deprecations.py +153 -0
- sqlalchemy/testing/suite/test_dialect.py +740 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3225 -0
- sqlalchemy/testing/suite/test_results.py +502 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +1999 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_types.py +2141 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +537 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +76 -0
- sqlalchemy/util/__init__.py +160 -0
- sqlalchemy/util/_collections.py +715 -0
- sqlalchemy/util/_concurrency_py3k.py +288 -0
- sqlalchemy/util/_has_cy.py +40 -0
- sqlalchemy/util/_py_collections.py +541 -0
- sqlalchemy/util/compat.py +301 -0
- sqlalchemy/util/concurrency.py +108 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2218 -0
- sqlalchemy/util/preloaded.py +150 -0
- sqlalchemy/util/queue.py +322 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- 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
|