@@ -495,10 +495,12 @@ private function generate_column_default( string $mysql_type, ?string $default_v
495495 // Quoted string literal. E.g.: 'abc', "abc", `abc`
496496 $ first_byte = $ default_value [0 ] ?? null ;
497497 if ( '" ' === $ first_byte || "' " === $ first_byte || '` ' === $ first_byte ) {
498- return $ this ->escape_mysql_string_literal ( substr ( $ default_value , 1 , -1 ) );
498+ $ value = substr ( $ default_value , 1 , -1 );
499+ $ value = str_replace ( $ first_byte . $ first_byte , $ first_byte , $ value );
500+ return $ this ->format_mysql_string_literal ( $ value );
499501 }
500502
501- // Normalize the default value to for easier comparison.
503+ // Normalize the default value for easier comparison.
502504 $ uppercase_default_value = strtoupper ( $ default_value );
503505
504506 // NULL, TRUE, FALSE.
@@ -542,7 +544,7 @@ private function generate_column_default( string $mysql_type, ?string $default_v
542544 }
543545
544546 // Unquoted string literal. E.g.: abc
545- return $ this ->escape_mysql_string_literal ( $ default_value );
547+ return $ this ->format_mysql_string_literal ( $ default_value );
546548 }
547549
548550 /**
@@ -642,14 +644,32 @@ private function get_mysql_column_type( string $column_type ): string {
642644 }
643645
644646 /**
645- * Escape a string literal for MySQL DEFAULT values.
647+ * Format a MySQL string literal for output in a SHOW statement.
648+ *
649+ * We expect UTF-8 strings coming from SQLite. The only characters that need
650+ * to be escaped in a single-quoted string for a UTF-8 MySQL dump are ' and \.
651+ *
652+ * MySQL's SHOW command also escapes \0 (for the mysql CLI), \n (for logs and
653+ * readability), and \r (for readability). Let's these characters as well.
654+ *
655+ * See:
656+ * - https://github.com/mysql/mysql-server/blob/ff05628a530696bc6851ba6540ac250c7a059aa7/sql/sql_show.cc#L1799
657+ * - https://github.com/mysql/mysql-server/blob/ff05628a530696bc6851ba6540ac250c7a059aa7/sql/table.cc#L3525
658+ *
659+ * Unfortunately, SQLite doesn't validate the UTF-8 encoding of strings, so
660+ * other byte sequences may come from SQLite as well.
661+ *
662+ * See: https://www.sqlite.org/invalidutf.html
663+ *
664+ * TODO: We may consider stripping invalid UTF-8 characters, but that's likely
665+ * to be a bigger project, as these can appear also in other contexts.
646666 *
647667 * @param string $literal The string literal to escape.
648668 * @return string The escaped string literal.
649669 */
650- private function escape_mysql_string_literal ( string $ literal ): string {
651- // See: https://www.php.net/manual/en/mysqli.real-escape-string.php
652- return "' " . addcslashes ( $ literal , "\0\n\r ' \" \Z " ) . "' " ;
670+ private function format_mysql_string_literal ( string $ literal ): string {
671+ $ value = addcslashes ( $ literal , "\0\n\r\\" );
672+ return "' " . str_replace ( " ' " , "'' " , $ value ) . "' " ;
653673 }
654674
655675 /**
0 commit comments