diff --git a/features/db-import.feature b/features/db-import.feature index 462add04..b1d4ce07 100644 --- a/features/db-import.feature +++ b/features/db-import.feature @@ -374,3 +374,39 @@ Feature: Import a WordPress database """ Success: Imported from 'zerodate_compose.sql'. """ + + @require-mysql-or-mariadb + Scenario: `wp db import` handles filenames containing semicolons and client directives + Given a WP install + And a prefix.sql;\!touch_side_effect.sql#.sql file: + """ + CREATE TABLE wp_cli_meta_test (id int NOT NULL); + INSERT INTO wp_cli_meta_test (id) VALUES (42); + """ + + When I run `wp db import 'prefix.sql;\!touch_side_effect.sql#.sql'` + Then STDOUT should contain: + """ + Success: Imported from 'prefix.sql;\!touch_side_effect.sql#.sql'. + """ + And the side_effect.sql file should not exist + + When I run `wp db query 'SELECT id FROM wp_cli_meta_test;' --skip-column-names` + Then STDOUT should be: + """ + 42 + """ + + @require-mysql-or-mariadb + Scenario: `wp db import` does not execute embedded client meta-commands in dump content + Given a WP install + And a dump_with_command_injection.sql file: + """ + CREATE TABLE wp_cli_meta_content_test (id int NOT NULL); + INSERT INTO wp_cli_meta_content_test (id) VALUES (99); + \! touch side_effect_content.sql + """ + + When I try `wp db import dump_with_command_injection.sql` + Then the side_effect_content.sql file should not exist + diff --git a/src/DB_Command.php b/src/DB_Command.php index b6bb1376..be9f14fc 100644 --- a/src/DB_Command.php +++ b/src/DB_Command.php @@ -926,7 +926,7 @@ private function command_supports_option( $command, $option ) { * : Extra arguments to pass to mysql. [Refer to mysql binary docs](https://dev.mysql.com/doc/refman/8.0/en/mysql-command-options.html). * * [--skip-optimization] - * : When using an SQL file, do not include speed optimization such as disabling auto-commit and key checks. + * : Do not disable unique checks and foreign key checks during import. * * [--skip-sql-mode-compat] * : Do not adapt the session SQL mode for WordPress compatibility. By default, `wp db import` strips the SQL modes that WordPress Core disables (such as `STRICT_TRANS_TABLES` and `NO_ZERO_DATE`) so that dumps containing legacy values like `0000-00-00` import cleanly. Pass this flag to import under the server's own SQL modes instead. @@ -943,8 +943,10 @@ private function command_supports_option( $command, $option ) { public function import( $args, $assoc_args ) { $this->maybe_load_sqlite_dropin(); + $is_stdin = false; if ( ! empty( $args[0] ) ) { $result_file = $args[0]; + $is_stdin = '-' === $result_file; } else { $result_file = sprintf( '%s.sql', DB_NAME ); } @@ -956,7 +958,10 @@ public function import( $args, $assoc_args ) { // Process options to MySQL. $mysql_args = array_merge( - [ 'database' => DB_NAME ], + [ + 'database' => DB_NAME, + 'binary-mode' => true, + ], self::get_dbuser_dbpass_args( $assoc_args ), self::get_mysql_args( $assoc_args ) ); @@ -967,25 +972,33 @@ public function import( $args, $assoc_args ) { // no separate probe connection. $this->apply_sql_mode_compat_init_command( $mysql_args, $assoc_args ); - if ( '-' !== $result_file ) { + if ( ! $is_stdin ) { if ( ! is_readable( $result_file ) ) { WP_CLI::error( sprintf( 'Import file missing or not readable: %s', $result_file ) ); } - - $query = Utils\get_flag_value( $assoc_args, 'skip-optimization' ) - ? 'SOURCE %s;' - : 'SET autocommit = 0; SET unique_checks = 0; SET foreign_key_checks = 0; SOURCE %s; COMMIT;'; - - $mysql_args['execute'] = sprintf( $query, $result_file ); } else { $result_file = 'STDIN'; } + if ( ! Utils\get_flag_value( $assoc_args, 'skip-optimization' ) ) { + $optimization_sql = 'SET unique_checks = 0; SET foreign_key_checks = 0;'; + if ( isset( $mysql_args['init-command'] ) && '' !== trim( (string) $mysql_args['init-command'] ) ) { + $mysql_args['init-command'] .= '; ' . $optimization_sql; + } else { + $mysql_args['init-command'] = $optimization_sql; + } + } + $command = sprintf( '%s%s --no-auto-rehash', Utils\get_mysql_binary_path(), $this->get_defaults_flag_string( $assoc_args ) ); + + if ( ! $is_stdin ) { + $command .= ' < ' . escapeshellarg( $result_file ); + } + WP_CLI::debug( "Running shell command: {$command}", 'db' ); WP_CLI::debug( 'Associative arguments: ' . json_encode( $assoc_args ), 'db' );