41 lines
1.8 KiB
Python
41 lines
1.8 KiB
Python
"""Force every table created on MySQL to utf8mb4 + DYNAMIC row format.
|
|
|
|
Importing this module installs a SQLAlchemy compiler hook. It is a side effect on
|
|
purpose: the hook has to be registered before any CreateTable is compiled, and
|
|
every place that creates tables needs it.
|
|
|
|
WHY. Without it a CREATE TABLE inherits the SERVER's default charset. A MySQL box
|
|
defaulting to latin1 - common on older installs, and the 5.6 server this was
|
|
ported from is one - silently builds a latin1 schema that drifts from the utf8mb4
|
|
production target. Nothing fails at create time; it surfaces later as mangled
|
|
characters, or as a join between a utf8mb4 and a latin1 column that cannot use an
|
|
index.
|
|
|
|
DYNAMIC row format is the other half: it keeps utf8mb4 indexes under the 767-byte
|
|
prefix limit on pre-5.7 InnoDB, which is what error 1071 ("key too long") is.
|
|
|
|
This lived inline in migrations/env.py, so it covered the CORE chain only. Plugin
|
|
chains (ADR-008) run through shopdb/plugins/alembic_template.py, which never
|
|
imported it - so a plugin's baseline tables were created at the server default
|
|
while core's were utf8mb4, on the same database. Both import this now.
|
|
|
|
Scoped to the mysql dialect, so the SQLite test database is untouched.
|
|
"""
|
|
from sqlalchemy.ext.compiler import compiles
|
|
from sqlalchemy.schema import CreateTable
|
|
|
|
TABLE_SUFFIX = (
|
|
" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
" COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC"
|
|
)
|
|
|
|
|
|
@compiles(CreateTable, "mysql")
|
|
def _mysql_create_table_utf8mb4(element, compiler, **kw):
|
|
sql = compiler.visit_create_table(element, **kw)
|
|
# An explicit CHARSET in the model's __table_args__ wins - this is a default,
|
|
# not an override.
|
|
if "CHARSET" not in sql.upper():
|
|
sql = sql.rstrip().rstrip(";") + TABLE_SUFFIX
|
|
return sql
|