__dialect__ = "mysql"
transactional_ddl = False
- type_synonyms = DefaultImpl.type_synonyms + ({"BOOL", "TINYINT"},)
+ type_synonyms = DefaultImpl.type_synonyms + (
+ {"BOOL", "TINYINT"},
+ {"JSON", "LONGTEXT"},
+ )
type_arg_extract = [r"character set ([\w\-_]+)", r"collate ([\w\-_]+)"]
def alter_column( # type:ignore[override]
--- /dev/null
+.. change::
+ :tags: bug, mysql, autogenerate
+ :tickets: 968
+
+ Added a rule to the MySQL impl so that the translation between JSON /
+ LONGTEXT is accommodated by autogenerate, treating LONGTEXT from the server
+ as equivalent to an existing JSON in the model.
+from sqlalchemy import exc as sqla_exc
from sqlalchemy import text
from alembic.testing import exclusions
else:
return False
+ @property
+ def json_type(self):
+ return exclusions.only_on(
+ [
+ lambda config: exclusions.against(config, "mysql")
+ and (
+ (
+ not config.db.dialect._is_mariadb
+ and exclusions.against(config, "mysql >= 5.7")
+ )
+ or (
+ config.db.dialect._mariadb_normalized_version_info
+ >= (10, 2, 7)
+ )
+ ),
+ "mariadb>=10.2.7",
+ "postgresql >= 9.3",
+ self._sqlite_json,
+ self._mssql_json,
+ ]
+ )
+
+ def _mssql_json(self, config):
+ if not sqla_compat.sqla_14:
+ return False
+ else:
+ return exclusions.against(config, "mssql")
+
+ def _sqlite_json(self, config):
+ if not sqla_compat.sqla_14:
+ return False
+ elif not exclusions.against(config, "sqlite >= 3.9"):
+ return False
+ else:
+ with config.db.connect() as conn:
+ try:
+ return (
+ conn.execute(
+ text(
+ """select json_extract('{"foo": "bar"}', """
+ """'$."foo"')"""
+ )
+ ).scalar()
+ == "bar"
+ )
+ except sqla_exc.DBAPIError:
+ return False
+
@property
def identity_columns(self):
# TODO: in theory if these could come from SQLAlchemy dialects
from sqlalchemy import inspect
from sqlalchemy import INTEGER
from sqlalchemy import Integer
+from sqlalchemy import JSON
from sqlalchemy import LargeBinary
from sqlalchemy import MetaData
from sqlalchemy import Numeric
(String(32),),
(LargeBinary(),),
(Unicode(32),),
+ (JSON(), config.requirements.json_type),
+ (mysql.LONGTEXT(), config.requirements.mysql),
(Enum("one", "two", "three", name="the_enum"),),
)
def test_introspected_columns_match_metadata_columns(self, cola):
mysql.VARCHAR(200, charset="latin1"),
mysql.VARCHAR(200, charset="utf-8"),
True,
- config.requirements.mysql + config.requirements.sqlalchemy_13,
+ config.requirements.mysql,
),
(
String(255, collation="utf8_bin"),
String(255, collation="utf8_bin"),
String(255, collation="latin1_bin"),
True,
- config.requirements.mysql + config.requirements.sqlalchemy_13,
+ config.requirements.mysql,
),
)
def test_string_comparisons(self, cola, colb, expect_changes):