fix(mysql): read and compare text as utf8mb4; pre-scan sees collation… · DBDiff/DBDiff@935aaaf · GitHub
Skip to content

Commit 935aaaf

Browse files
jasdeepkhalsaclaude
andcommitted
fix(mysql): read and compare text as utf8mb4; pre-scan sees collation, comment, generated
DBDiff connected to MySQL as utf8 (utf8mb3) and compared rows cast to it, so a four-byte character — an emoji, a script outside the Basic Multilingual Plane — read as '?', and a change between two of them was not seen. The schema pre-scan left out a column's character set, collation, comment and generated expression, so a table differing only there was skipped as unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 3aa5fab commit 935aaaf

4 files changed

Lines changed: 283 additions & 8 deletions

File tree

‎src/DB/Adapters/MySQLAdapter.php‎

Lines changed: 17 additions & 4 deletions

‎src/DB/Data/LocalTableData.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ private function skipIfIdentical(string $table): bool
9393
*
9494
* SQLite only supports cross-database queries when the second file is
9595
* attached to the same connection. MySQL-specific functions
96-
* (CONVERT … USING utf8, CAST … AS CHAR CHARACTER SET utf8, SHA2) are
96+
* (CONVERT … USING utf8mb4, CAST … AS CHAR CHARACTER SET utf8mb4, SHA2) are
9797
* replaced with SQLite-compatible equivalents.
9898
*/
9999
private function getDiffSQLite(string $table, array $key): array
@@ -326,7 +326,7 @@ public function getOldNewDiff($table, $key) {
326326
if (in_array($el, $binaryCols)) {
327327
return "HEX(`{$p}`.`{$el}`) as `{$el}`";
328328
}
329-
return "CONVERT(`{$p}`.`{$el}` USING utf8) as `{$el}`";
329+
return "CONVERT(`{$p}`.`{$el}` USING utf8mb4) as `{$el}`";
330330
}, $arr);
331331
};
332332

@@ -416,7 +416,7 @@ public function getChangeDiff($table, $key) {
416416
if (in_array($el, $binaryCols)) {
417417
return "HEX(IFNULL(`{$p}`.`{$el}`, ''))";
418418
}
419-
return "CAST(IFNULL(`{$p}`.`{$el}`, '\\0') AS CHAR CHARACTER SET utf8)";
419+
return "CAST(IFNULL(`{$p}`.`{$el}`, '\\0') AS CHAR CHARACTER SET utf8mb4)";
420420
}, $arr);
421421
};
422422

‎src/DB/Data/StreamingMergeDiff.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ public function buildHashExpression(array $columns): string
307307
}
308308

309309
return match ($this->driver) {
310-
'mysql' => 'SHA2(CONCAT(' . implode(", CHAR(31), ", array_map(fn($c) => "IF(`$c` IS NULL, 'N', CONCAT('V', CAST(`$c` AS CHAR CHARACTER SET utf8)))", $columns)) . '), 256)',
310+
'mysql' => 'SHA2(CONCAT(' . implode(", CHAR(31), ", array_map(fn($c) => "IF(`$c` IS NULL, 'N', CONCAT('V', CAST(`$c` AS CHAR CHARACTER SET utf8mb4)))", $columns)) . '), 256)',
311311
'pgsql' => "md5(" . implode(" || E'\\x1f' || ", array_map(fn($c) => "CASE WHEN \"$c\" IS NULL THEN 'N' ELSE 'V' || \"$c\"::text END", $columns)) . ")",
312312
'sqlite' => "hex(" . implode(" || X'1f' || ", array_map(fn($c) => "CASE WHEN \"$c\" IS NULL THEN 'N' ELSE 'V' || CAST(\"$c\" AS TEXT) END", $columns)) . ")",
313313
default => throw new DataException("Unsupported driver: {$this->driver}"),
Lines changed: 262 additions & 0 deletions

0 commit comments

Comments
 (0)