Illegal mix of collations
Auf dieser Seite
Bei Installationen mit MariaDB brechen einzelne Vorgänge mit dem Datenbankfehler 1267 ab. Häufig fällt das zuerst bei der Automatischen Geräteregistrierung (ADE, im Portal: DEP) auf: Die Synchronisierung der Geräte schlägt für alle Organisationen fehl, während der übrige Betrieb normal weiterläuft. Im Relution-Log erscheint bei jeder Synchronisierung eine Meldung wie die folgende:
Internal Exception: java.sql.SQLException: Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='
Error Code: 1267
Call: DELETE FROM `mdm_auto_enrllmnt_dflt_plcy` USING `mdm_auto_enrllmnt_dflt_plcy`, `TL_mdm_auto_enrllmnt` WHERE `mdm_auto_enrllmnt_dflt_plcy`.`auto_enrollment_uuid` = `TL_mdm_auto_enrllmnt`.`uuid`
Die genannten Collations können je nach Installation abweichen, z. B. utf8mb4_uca1400_ai_ci statt utf8mb4_unicode_ci. Kennzeichnend ist eine Tabelle mit dem Präfix TL_ in der Abfrage. Wo die Log-Dateien liegen, ist unter Relution Logs → beschrieben.
Ursache
Relution erwartet für alle Textspalten die Collation utf8mb4_general_ci. Die Tabellen der Datenbank sind entsprechend angelegt, und auch die Datenbankverbindung von Relution arbeitet mit dieser Collation.
Für Massenänderungen, etwa das Löschen mehrerer Geräteregistrierungen während der ADE-Synchronisierung, legt Relution kurzzeitig temporäre Hilfstabellen mit dem Präfix TL_ an. Für diese Tabellen wird keine Collation vorgegeben, sie übernehmen daher die Standard-Collation der Datenbank. Weicht diese Standard-Collation von utf8mb4_general_ci ab, kann MariaDB die Spalten der Hilfstabelle nicht mit den Spalten der eigentlichen Tabellen vergleichen und bricht die Abfrage ab. Die gespeicherten Daten sind davon nicht betroffen.
Eine abweichende Standard-Collation entsteht typischerweise in folgenden Fällen:
- Die Datenbank wurde auf einem MariaDB-Server angelegt, dessen
collation-servernicht aufutf8mb4_general_cigesetzt ist. Neuere MariaDB-Versionen verwenden ohne explizite Konfiguration eine andere Standard-Collation. - Die Datenbank wurde per Dump auf einen neuen Server oder in einen neuen Container übertragen. Der Dump stellt die Tabellen mit ihrer ursprünglichen Collation wieder her, die neu angelegte Datenbank selbst erhält jedoch die Vorgabe des neuen Servers.
Lösung
Die Standard-Collation der Datenbank wird an die Collation der Relution-Tabellen angeglichen. Dabei ändert sich ausschließlich die Vorgabe für neu angelegte Tabellen, bestehende Tabellen und Daten bleiben unverändert.
Erforderliche Schritte:
- Der Relution-Dienst wird gestoppt.
- Ein Backup der Datenbank wird erstellt.
- Das nachfolgende Skript wird als Datei
repair_temp_table_collation_mariadb.sqlgespeichert und auf der Relution-Datenbank ausgeführt. - Die Ausgabe des Skripts wird geprüft (siehe unten).
- Der Relution-Dienst wird gestartet.
Der Neustart ist zwingend erforderlich. Die temporären Hilfstabellen bestehen jeweils für die Dauer einer Datenbankverbindung. Bereits geöffnete Verbindungen verwenden sonst weiterhin die mit der falschen Collation angelegte Hilfstabelle.
Skript
Das Skript ermittelt die Collation der Relution-Tabellen, listet abweichende Spalten auf und setzt die Standard-Collation der Datenbank. Es kann gefahrlos mehrfach ausgeführt werden.
repair_temp_table_collation_mariadb.sql
-- Behebt "Illegal mix of collations" (Fehler 1267) bei temporären TL_*-Tabellen unter MariaDB.
-- Setzt nur die Standard-Collation der Datenbank, bestehende Tabellen und Daten bleiben unverändert.
-- Danach muss Relution neu gestartet werden.
-- 1) Diagnose
SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = DATABASE();
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME IN ('mdm_auto_enrllmnt', 'mdm_auto_enrllmnt_dflt_plcy')
AND COLLATION_NAME IS NOT NULL
ORDER BY TABLE_NAME, COLUMN_NAME;
SELECT CHARACTER_SET_NAME, COLLATION_NAME
INTO @ref_charset, @ref_collation
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'mdm_auto_enrllmnt'
AND COLUMN_NAME = 'uuid';
-- 2) Spalten, die von der Referenz-Collation abweichen
SELECT @ref_collation AS reference_collation;
SELECT COLLATION_NAME, COUNT(*) AS column_count, COUNT(DISTINCT TABLE_NAME) AS table_count
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND COLLATION_NAME IS NOT NULL
GROUP BY COLLATION_NAME
ORDER BY column_count DESC;
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND COLLATION_NAME IS NOT NULL
AND COLLATION_NAME <> @ref_collation
ORDER BY TABLE_NAME, COLUMN_NAME;
-- 3) Standard-Collation der Datenbank angleichen
SET @ddl := IF(
@ref_collation IS NULL,
'SELECT ''ERROR: mdm_auto_enrllmnt.uuid not found - wrong schema?'' AS notice',
CONCAT('ALTER DATABASE `', DATABASE(), '` CHARACTER SET ', @ref_charset, ' COLLATE ', @ref_collation)
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = DATABASE();
Die Platzhalter in spitzen Klammern werden in den folgenden Befehlen durch die Werte aus der application.yml bzw. docker-compose.yml ersetzt.
Docker
Die Befehle werden im Verzeichnis /opt/relution/ ausgeführt, in dem auch das Skript abgelegt ist.
docker compose stop relution
docker exec <containername> mariadb-dump -u <username> -p<password> --single-transaction <database> > relution_backup.sql
docker exec -i <containername> mariadb -t -u <username> -p<password> <database> < repair_temp_table_collation_mariadb.sql
docker compose start relution
Das Passwort wird hier direkt hinter -p angegeben, da die Eingabe des Skripts über die Standardeingabe erfolgt und eine interaktive Passwortabfrage dabei nicht möglich ist.
Nativ
Unter Linux:
sudo systemctl stop relution
mariadb-dump -u <username> -p --single-transaction <database> > relution_backup.sql
mariadb -t -u <username> -p <database> < repair_temp_table_collation_mariadb.sql
sudo systemctl start relution
Das Skript kann alternativ auch über ein Verwaltungstool wie HeidiSQL oder DBeaver auf der Relution-Datenbank ausgeführt werden.
Ausgabe prüfen
Die letzte Ausgabe des Skripts zeigt die neue Standard-Collation der Datenbank. Sie entspricht danach der Collation der Relution-Tabellen, in der Regel utf8mb4_general_ci.
Wird stattdessen die Meldung ERROR: mdm_auto_enrllmnt.uuid not found - wrong schema? ausgegeben, wurde das Skript nicht auf der Relution-Datenbank ausgeführt. In diesem Fall wurde nichts geändert.
Abschnitt 2 des Skripts listet alle Textspalten auf, deren Collation von der Referenz abweicht. Im Normalfall ist diese Liste leer. Enthält sie Einträge, wurden seit dem Anlegen der Datenbank Tabellen mit der falschen Collation erstellt, etwa durch Datenbank-Updates im Rahmen von Relution-Updates. Diese Tabellen können bei anderen Vorgängen denselben Fehler auslösen. Sie werden mit der Anleitung unter DB Konvertierung zu UTF8MB4 → vollständig auf utf8mb4_general_ci umgestellt.
Vorbeugung
Damit neu angelegte Datenbanken direkt die richtige Collation erhalten, wird der MariaDB-Server mit folgenden Einstellungen im Abschnitt [mysqld] betrieben:
[mysqld]
collation-server=utf8mb4_general_ci
character-set-server=utf8mb4
Die Prüfung der aktuellen Einstellungen erfolgt mit:
SHOW VARIABLES LIKE '%collation%';
Die Werte für collation_server und collation_database lauten dabei utf8mb4_general_ci. Die vollständige Konfiguration ist in der Installationsanleitung für Linux → beschrieben.