# allow for the "true" and "false" keywords, however
supports_native_boolean = False
+ # support for BIT type; mysqlconnector coerces result values automatically,
+ # all other MySQL DBAPIs require a conversion routine
+ supports_native_bit = False
+
# identifiers are 64, however aliases can be 255...
max_identifier_length = 255
max_index_name_length = 64
% (".".join(map(str, server_version_info)),)
)
if is_mariadb:
- self.preparer = MariaDBIdentifierPreparer
- # this would have been set by the default dialect already,
- # so set it again
- self.identifier_preparer = self.preparer(self)
+
+ if not issubclass(self.preparer, MariaDBIdentifierPreparer):
+ self.preparer = MariaDBIdentifierPreparer
+ # this would have been set by the default dialect already,
+ # so set it again
+ self.identifier_preparer = self.preparer(self)
# this will be updated on first connect in initialize()
# if using older mariadb version
:connectstring: mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
:url: https://pypi.org/project/mysql-connector-python/
-.. note::
+Driver Status
+-------------
+
+MySQL Connector/Python is supported as of SQLAlchemy 2.0.39 to the
+degree which the driver is functional. There are still ongoing issues
+with features such as server side cursors which remain disabled until
+upstream issues are repaired.
+
+.. versionchanged:: 2.0.39
+
+ The MySQL Connector/Python dialect has been updated to support the
+ latest version of this DBAPI. Previously, MySQL Connector/Python
+ was not fully supported.
+
+Connecting to MariaDB with MySQL Connector/Python
+--------------------------------------------------
+
+MySQL Connector/Python may attempt to pass an incompatible collation to the
+database when connecting to MariaDB. Experimentation has shown that using
+``?charset=utf8mb4&collation=utfmb4_general_ci`` or similar MariaDB-compatible
+charset/collation will allow connectivity.
- The MySQL Connector/Python DBAPI has had many issues since its release,
- some of which may remain unresolved, and the mysqlconnector dialect is
- **not tested as part of SQLAlchemy's continuous integration**.
- The recommended MySQL dialects are mysqlclient and PyMySQL.
""" # noqa
import re
from .base import BIT
+from .base import MariaDBIdentifierPreparer
from .base import MySQLCompiler
from .base import MySQLDialect
+from .base import MySQLExecutionContext
from .base import MySQLIdentifierPreparer
from .mariadb import MariaDBDialect
from ... import util
+class MySQLExecutionContext_mysqlconnector(MySQLExecutionContext):
+ def create_server_side_cursor(self):
+ return self._dbapi_connection.cursor(buffered=False)
+
+ def create_default_cursor(self):
+ return self._dbapi_connection.cursor(buffered=True)
+
+
class MySQLCompiler_mysqlconnector(MySQLCompiler):
def visit_mod_binary(self, binary, operator, **kw):
return (
)
-class MySQLIdentifierPreparer_mysqlconnector(MySQLIdentifierPreparer):
+class IdentifierPreparerCommon_mysqlconnector:
@property
def _double_percents(self):
return False
return value
+class MySQLIdentifierPreparer_mysqlconnector(
+ IdentifierPreparerCommon_mysqlconnector, MySQLIdentifierPreparer
+):
+ pass
+
+
+class MariaDBIdentifierPreparer_mysqlconnector(
+ IdentifierPreparerCommon_mysqlconnector, MariaDBIdentifierPreparer
+):
+ pass
+
+
class _myconnpyBIT(BIT):
def result_processor(self, dialect, coltype):
"""MySQL-connector already converts mysql bits, so."""
supports_native_decimal = True
+ supports_native_bit = True
+
+ # not until https://bugs.mysql.com/bug.php?id=117548
+ supports_server_side_cursors = False
+
default_paramstyle = "format"
statement_compiler = MySQLCompiler_mysqlconnector
+ execution_ctx_cls = MySQLExecutionContext_mysqlconnector
+
preparer = MySQLIdentifierPreparer_mysqlconnector
colspecs = util.update_copy(MySQLDialect.colspecs, {BIT: _myconnpyBIT})
util.coerce_kw_type(opts, "use_pure", bool)
util.coerce_kw_type(opts, "use_unicode", bool)
- # unfortunately, MySQL/connector python refuses to release a
- # cursor without reading fully, so non-buffered isn't an option
- opts.setdefault("buffered", True)
+ # note that "buffered" is set to False by default in MySQL/connector
+ # python. If you set it to True, then there is no way to get a server
+ # side cursor because the logic is written to disallow that.
+
+ # leaving this at True until
+ # https://bugs.mysql.com/bug.php?id=117548 can be fixed
+ opts["buffered"] = True
# FOUND_ROWS must be set in ClientFlag to enable
# supports_sane_rowcount.
opts["client_flags"] = client_flags
except Exception:
pass
+
return [[], opts]
@util.memoized_property
def is_disconnect(self, e, connection, cursor):
errnos = (2006, 2013, 2014, 2045, 2055, 2048)
- exceptions = (self.dbapi.OperationalError, self.dbapi.InterfaceError)
+ exceptions = (
+ self.dbapi.OperationalError,
+ self.dbapi.InterfaceError,
+ self.dbapi.ProgrammingError,
+ )
if isinstance(e, exceptions):
return (
e.errno in errnos
def _compat_fetchone(self, rp, charset=None):
return rp.fetchone()
- _isolation_lookup = {
- "SERIALIZABLE",
- "READ UNCOMMITTED",
- "READ COMMITTED",
- "REPEATABLE READ",
- "AUTOCOMMIT",
- }
+ def get_isolation_level_values(self, dbapi_connection):
+ return (
+ "SERIALIZABLE",
+ "READ UNCOMMITTED",
+ "READ COMMITTED",
+ "REPEATABLE READ",
+ "AUTOCOMMIT",
+ )
- def _set_isolation_level(self, connection, level):
+ def set_isolation_level(self, connection, level):
if level == "AUTOCOMMIT":
connection.autocommit = True
else:
connection.autocommit = False
- super()._set_isolation_level(connection, level)
+ super().set_isolation_level(connection, level)
class MariaDBDialect_mysqlconnector(
):
supports_statement_cache = True
_allows_uuid_binds = False
+ preparer = MariaDBIdentifierPreparer_mysqlconnector
dialect = MySQLDialect_mysqlconnector
)[1]
eq_(kw["buffered"], True)
- kw = dialect.create_connect_args(
- make_url("mysql+mysqlconnector://u:p@host/db?buffered=false")
- )[1]
- eq_(kw["buffered"], False)
-
- kw = dialect.create_connect_args(
- make_url("mysql+mysqlconnector://u:p@host/db")
- )[1]
- eq_(kw["buffered"], True)
+ # this is turned off for now due to
+ # https://bugs.mysql.com/bug.php?id=117548
+ if dialect.supports_server_side_cursors:
+ kw = dialect.create_connect_args(
+ make_url("mysql+mysqlconnector://u:p@host/db?buffered=false")
+ )[1]
+ eq_(kw["buffered"], False)
+
+ kw = dialect.create_connect_args(
+ make_url("mysql+mysqlconnector://u:p@host/db")
+ )[1]
+ # defaults to False as of 2.0.39
+ eq_(kw.get("buffered"), None)
def test_mysqlconnector_raise_on_warnings_arg(self):
from sqlalchemy.dialects.mysql import mysqlconnector