diff --git a/features/utils.feature b/features/utils.feature index aac2114dca..921e9d4bc0 100644 --- a/features/utils.feature +++ b/features/utils.feature @@ -31,7 +31,7 @@ Feature: Utilities that do NOT depend on WordPress code @require-mysql Scenario: Check that `Utils\run_mysql_command()` uses STDOUT and STDERR by default - When I run `wp --skip-wordpress eval 'WP_CLI\Utils\run_mysql_command( "/usr/bin/env mysql --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "SHOW DATABASES;" ] );'` + When I run `wp --skip-wordpress eval 'WP_CLI\Utils\run_mysql_command( "{MYSQL_BINARY} --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "SHOW DATABASES;" ] );'` Then STDOUT should contain: """ Database @@ -42,7 +42,7 @@ Feature: Utilities that do NOT depend on WordPress code """ And STDERR should be empty - When I try `wp --skip-wordpress eval 'WP_CLI\Utils\run_mysql_command( "/usr/bin/env mysql --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "broken query" ]);'` + When I try `wp --skip-wordpress eval 'WP_CLI\Utils\run_mysql_command( "{MYSQL_BINARY} --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "broken query" ]);'` Then STDOUT should be empty And STDERR should contain: """ @@ -51,7 +51,7 @@ Feature: Utilities that do NOT depend on WordPress code @require-mysql Scenario: Check that `Utils\run_mysql_command()` can return data and errors if requested - When I run `wp --skip-wordpress eval 'list( $stdout, $stderr, $exit_code ) = WP_CLI\Utils\run_mysql_command( "/usr/bin/env mysql --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "SHOW DATABASES;" ], null, false ); fwrite( STDOUT, strtoupper( $stdout ) ); fwrite( STDERR, strtoupper( $stderr ) );'` + When I run `wp --skip-wordpress eval 'list( $stdout, $stderr, $exit_code ) = WP_CLI\Utils\run_mysql_command( "{MYSQL_BINARY} --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "SHOW DATABASES;" ], null, false ); fwrite( STDOUT, strtoupper( $stdout ) ); fwrite( STDERR, strtoupper( $stderr ) );'` Then STDOUT should not contain: """ Database @@ -70,7 +70,7 @@ Feature: Utilities that do NOT depend on WordPress code """ And STDERR should be empty - When I try `wp --skip-wordpress eval 'list( $stdout, $stderr, $exit_code ) = WP_CLI\Utils\run_mysql_command( "/usr/bin/env mysql --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "broken query" ], null, false ); fwrite( STDOUT, strtoupper( $stdout ) ); fwrite( STDERR, strtoupper( $stderr ) );'` + When I try `wp --skip-wordpress eval 'list( $stdout, $stderr, $exit_code ) = WP_CLI\Utils\run_mysql_command( "{MYSQL_BINARY} --no-defaults", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}", "execute" => "broken query" ], null, false ); fwrite( STDOUT, strtoupper( $stdout ) ); fwrite( STDERR, strtoupper( $stderr ) );'` Then STDOUT should be empty And STDERR should not contain: """ @@ -149,19 +149,19 @@ Feature: Utilities that do NOT depend on WordPress code """ And save STDOUT as {DB_HOST_STRING} - When I try `mysql --database={DB_NAME} --user={DB_ROOT_USER} --password={DB_ROOT_PASSWORD} {DB_HOST_STRING} -e "SET GLOBAL max_allowed_packet=64*1024*1024;"` + When I try `{MYSQL_BINARY} --database={DB_NAME} --user={DB_ROOT_USER} --password={DB_ROOT_PASSWORD} {DB_HOST_STRING} -e "SET GLOBAL max_allowed_packet=64*1024*1024;"` Then the return code should be 0 # This throws a warning because of the password. - When I try `mysql --database={DB_NAME} --user={DB_USER} --password={DB_PASSWORD} {DB_HOST_STRING} < test_db.sql` + When I try `{MYSQL_BINARY} --database={DB_NAME} --user={DB_USER} --password={DB_PASSWORD} {DB_HOST_STRING} < test_db.sql` Then the return code should be 0 # The --skip-column-statistics flag is not always present. - When I try `mysqldump --help | grep -q 'column-statistics' && echo '--skip-column-statistics'` + When I try `{SQL_DUMP_COMMAND} --help | grep -q 'column-statistics' && echo '--skip-column-statistics'` Then save STDOUT as {SKIP_COLUMN_STATISTICS_FLAG} # This throws a warning because of the password. - When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--dmemory_limit=50M -ddisable_functions=ini_set} eval '\WP_CLI\Utils\run_mysql_command("/usr/bin/env mysqldump {SKIP_COLUMN_STATISTICS_FLAG} --no-tablespaces {DB_NAME}", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}" ], null, true);'` + When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--dmemory_limit=50M -ddisable_functions=ini_set} eval '\WP_CLI\Utils\run_mysql_command("/usr/bin/env {SQL_DUMP_COMMAND} {SKIP_COLUMN_STATISTICS_FLAG} --no-tablespaces {DB_NAME}", [ "user" => "{DB_USER}", "pass" => "{DB_PASSWORD}", "host" => "{DB_HOST}" ], null, true);'` Then the return code should be 0 And STDOUT should not be empty And STDOUT should contain: diff --git a/php/utils.php b/php/utils.php index 1d937b7b7c..16462fed1e 100644 --- a/php/utils.php +++ b/php/utils.php @@ -1818,37 +1818,103 @@ function pluralize( $noun, $count = null ) { } /** - * Get the path to the mysql binary. + * Return the detected database type. * - * @return string Path to the mysql binary, or an empty string if not found. + * Can be either 'sqlite' (if in a WordPress installation with the SQLite drop-in), + * 'mysql', or 'mariadb'. + * + * @return string Database type. + */ +function get_db_type() { + static $db_type = null; + + if ( defined( 'SQLITE_DB_DROPIN_VERSION' ) ) { + return 'sqlite'; + } + + if ( null !== $db_type ) { + return $db_type; + } + + $db_type = 'mysql'; + + $binary = get_mysql_binary_path(); + + if ( '' !== $binary ) { + $result = Process::create( "$binary --version", null, null )->run(); + + if ( 0 === $result->return_code ) { + $db_type = ( false !== strpos( $result->stdout, 'MariaDB' ) ) ? 'mariadb' : 'mysql'; + } + } + + return $db_type; +} + +/** + * Get the path to the MySQL or MariaDB binary. + * + * If the MySQL binary is provided by MariaDB (as determined by the version string), + * prefers the actual MariaDB binary. + * + * @since 2.12.0 Now also checks for MariaDB. + * + * @return string Path to the MySQL/MariaDB binary, or an empty string if not found. */ function get_mysql_binary_path() { static $path = null; - if ( null === $path ) { - $result = Process::create( '/usr/bin/env which mysql', null, null )->run(); + if ( null !== $path ) { + return $path; + } - if ( 0 !== $result->return_code ) { - $path = ''; - } else { - $path = trim( $result->stdout ); + $path = ''; + $mysql = Process::create( '/usr/bin/env which mysql', null, null )->run(); + $mariadb = Process::create( '/usr/bin/env which mariadb', null, null )->run(); + + $mysql_binary = trim( $mysql->stdout ); + $mariadb_binary = trim( $mariadb->stdout ); + + if ( 0 === $mysql->return_code ) { + if ( '' !== $mysql_binary ) { + $path = $mysql_binary; + $result = Process::create( "$mysql_binary --version", null, null )->run(); + + // It's actually MariaDB disguised as MySQL. + if ( 0 === $result->return_code && false !== strpos( $result->stdout, 'MariaDB' ) && 0 === $mariadb->return_code ) { + $path = $mariadb_binary; + } } + } elseif ( 0 === $mariadb->return_code ) { + $path = $mariadb_binary; + } + + if ( '' === $path ) { + WP_CLI::Error( 'Could not find mysql binary' ); } return $path; } /** - * Get the version of the MySQL database. + * Get the version of the MySQL or MariaDB database. + * + * @since 2.12.0 Now also checks for MariaDB. * - * @return string Version of the MySQL database, or an empty string if not - * found. + * @return string Version of the MySQL/MariaDB database, + * or an empty string if not found. */ function get_mysql_version() { static $version = null; - if ( null === $version ) { - $result = Process::create( '/usr/bin/env mysql --version', null, null )->run(); + if ( null !== $version ) { + return $version; + } + + $db_type = get_db_type(); + + if ( 'sqlite' !== $db_type ) { + $result = Process::create( "/usr/bin/env $db_type --version", null, null )->run(); if ( 0 !== $result->return_code ) { $version = ''; @@ -1860,6 +1926,24 @@ function get_mysql_version() { return $version; } +/** + * Returns the correct `dump` command based on the detected database type. + * + * @return string The appropriate dump command. + */ +function get_sql_dump_command() { + return 'mariadb' === get_db_type() ? 'mariadb-dump' : 'mysqldump'; +} + +/** + * Returns the correct `check` command based on the detected database type. + * + * @return string The appropriate check command. + */ +function get_sql_check_command() { + return 'mariadb' === get_db_type() ? 'mariadb-check' : 'mysqlcheck'; +} + /** * Get the SQL modes of the MySQL session. * @@ -1869,8 +1953,16 @@ function get_mysql_version() { function get_sql_modes() { static $sql_modes = null; - if ( null === $sql_modes ) { - $result = Process::create( '/usr/bin/env mysql --no-auto-rehash --batch --skip-column-names --execute="SELECT @@SESSION.sql_mode"', null, null )->run(); + if ( null !== $sql_modes ) { + return $sql_modes; + } + + $binary = get_mysql_binary_path(); + + if ( '' === $binary ) { + $sql_modes = []; + } else { + $result = Process::create( "$binary --no-auto-rehash --batch --skip-column-names --execute=\"SELECT @@SESSION.sql_mode\"", null, null )->run(); if ( 0 !== $result->return_code ) { $sql_modes = [];