@@ -495,10 +495,12 @@ private function generate_column_default( string $mysql_type, ?string $default_v
495
495
// Quoted string literal. E.g.: 'abc', "abc", `abc`
496
496
$ first_byte = $ default_value [0 ] ?? null ;
497
497
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 );
499
501
}
500
502
501
- // Normalize the default value to for easier comparison.
503
+ // Normalize the default value for easier comparison.
502
504
$ uppercase_default_value = strtoupper ( $ default_value );
503
505
504
506
// NULL, TRUE, FALSE.
@@ -542,7 +544,7 @@ private function generate_column_default( string $mysql_type, ?string $default_v
542
544
}
543
545
544
546
// Unquoted string literal. E.g.: abc
545
- return $ this ->escape_mysql_string_literal ( $ default_value );
547
+ return $ this ->format_mysql_string_literal ( $ default_value );
546
548
}
547
549
548
550
/**
@@ -642,14 +644,32 @@ private function get_mysql_column_type( string $column_type ): string {
642
644
}
643
645
644
646
/**
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.
646
666
*
647
667
* @param string $literal The string literal to escape.
648
668
* @return string The escaped string literal.
649
669
*/
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 ) . "' " ;
653
673
}
654
674
655
675
/**
0 commit comments