Skip to content

Commit 5375be8

Browse files
Copilotswissspidy
andauthored
Add load.php, extend wpdb fallback to run_query() and import command
Agent-Logs-Url: https://github.com/wp-cli/db-command/sessions/2fe9988f-095f-4c61-b162-5bf4f8956cca Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
1 parent ea5dea1 commit 5375be8

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

src/DB_Command.php

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,30 @@ public function import( $args, $assoc_args ) {
929929
return;
930930
}
931931

932+
if ( ! $this->is_mysql_binary_available() ) {
933+
if ( '-' === $result_file ) {
934+
$sql_content = stream_get_contents( STDIN );
935+
if ( false === $sql_content ) {
936+
WP_CLI::error( 'Failed to read from STDIN.' );
937+
}
938+
$result_file = 'STDIN';
939+
} else {
940+
if ( ! is_readable( $result_file ) ) {
941+
WP_CLI::error( sprintf( 'Import file missing or not readable: %s', $result_file ) );
942+
}
943+
$sql_content = file_get_contents( $result_file );
944+
if ( false === $sql_content ) {
945+
WP_CLI::error( sprintf( 'Could not read import file: %s', $result_file ) );
946+
}
947+
}
948+
949+
WP_CLI::debug( 'MySQL/MariaDB binary not available, falling back to wpdb for import.', 'db' );
950+
$this->maybe_load_wpdb();
951+
$this->wpdb_import( (string) $sql_content, $assoc_args );
952+
WP_CLI::success( sprintf( "Imported from '%s'.", $result_file ) );
953+
return;
954+
}
955+
932956
// Process options to MySQL.
933957
$mysql_args = array_merge(
934958
[ 'database' => DB_NAME ],
@@ -1919,6 +1943,19 @@ private static function get_create_query() {
19191943
* @param array $assoc_args Optional. Associative array of arguments.
19201944
*/
19211945
protected function run_query( $query, $assoc_args = [] ) {
1946+
if ( ! $this->is_mysql_binary_available() ) {
1947+
WP_CLI::debug( "Query via wpdb: {$query}", 'db' );
1948+
$this->maybe_load_wpdb();
1949+
global $wpdb;
1950+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1951+
$result = $wpdb->query( $query );
1952+
if ( false === $result ) {
1953+
// phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags
1954+
WP_CLI::error( 'Query failed: ' . strip_tags( $wpdb->last_error ) );
1955+
}
1956+
return;
1957+
}
1958+
19221959
// Ensure that the SQL mode is compatible with WPDB.
19231960
$query = $this->get_sql_mode_query( $assoc_args ) . $query;
19241961

@@ -2410,6 +2447,7 @@ protected function maybe_load_wpdb() {
24102447
// Load required WordPress files if not already loaded.
24112448
if ( ! function_exists( 'add_action' ) ) {
24122449
$required_files = [
2450+
ABSPATH . WPINC . '/load.php',
24132451
ABSPATH . WPINC . '/compat.php',
24142452
ABSPATH . WPINC . '/plugin.php',
24152453
// Defines `wp_debug_backtrace_summary()` as used by wpdb.
@@ -2484,4 +2522,130 @@ protected function wpdb_query( $query, $assoc_args = [] ) {
24842522
$this->display_query_results( $headers, $results, $skip_column_names );
24852523
}
24862524
}
2525+
2526+
/**
2527+
* Import SQL content into the database using wpdb.
2528+
*
2529+
* Used as a fallback when the mysql/mariadb binary is not available.
2530+
*
2531+
* @param string $sql_content SQL content to import.
2532+
* @param array $assoc_args Associative arguments.
2533+
*/
2534+
protected function wpdb_import( $sql_content, $assoc_args = [] ) {
2535+
global $wpdb;
2536+
2537+
if ( ! isset( $wpdb ) || ! ( $wpdb instanceof wpdb ) ) {
2538+
WP_CLI::error( 'WordPress database (wpdb) is not available. Please install MySQL or MariaDB client tools.' );
2539+
}
2540+
2541+
$skip_optimization = Utils\get_flag_value( $assoc_args, 'skip-optimization', false );
2542+
2543+
if ( ! $skip_optimization ) {
2544+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2545+
$wpdb->query( 'SET autocommit = 0' );
2546+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2547+
$wpdb->query( 'SET unique_checks = 0' );
2548+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2549+
$wpdb->query( 'SET foreign_key_checks = 0' );
2550+
}
2551+
2552+
$statements = $this->split_sql_statements( $sql_content );
2553+
2554+
foreach ( $statements as $statement ) {
2555+
$statement = trim( $statement );
2556+
if ( '' === $statement ) {
2557+
continue;
2558+
}
2559+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2560+
$result = $wpdb->query( $statement );
2561+
if ( false === $result ) {
2562+
if ( ! $skip_optimization ) {
2563+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2564+
$wpdb->query( 'ROLLBACK' );
2565+
}
2566+
// phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags
2567+
WP_CLI::error( 'Import failed: ' . strip_tags( $wpdb->last_error ) );
2568+
}
2569+
}
2570+
2571+
if ( ! $skip_optimization ) {
2572+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2573+
$wpdb->query( 'COMMIT' );
2574+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2575+
$wpdb->query( 'SET autocommit = 1' );
2576+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2577+
$wpdb->query( 'SET unique_checks = 1' );
2578+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2579+
$wpdb->query( 'SET foreign_key_checks = 1' );
2580+
}
2581+
}
2582+
2583+
/**
2584+
* Split a SQL string into individual statements.
2585+
*
2586+
* Handles single-quoted strings, double-quoted strings, and comments
2587+
* so that semicolons inside them are not treated as statement delimiters.
2588+
*
2589+
* @param string $sql SQL string to split.
2590+
* @return string[] Array of individual SQL statements.
2591+
*/
2592+
private function split_sql_statements( $sql ) {
2593+
$statements = [];
2594+
$current = '';
2595+
$in_single_quote = false;
2596+
$in_double_quote = false;
2597+
$in_comment = false;
2598+
$in_line_comment = false;
2599+
$length = strlen( $sql );
2600+
2601+
for ( $i = 0; $i < $length; $i++ ) {
2602+
$char = $sql[ $i ];
2603+
$next = ( $i + 1 < $length ) ? $sql[ $i + 1 ] : '';
2604+
2605+
if ( $in_line_comment ) {
2606+
if ( "\n" === $char ) {
2607+
$in_line_comment = false;
2608+
}
2609+
continue;
2610+
}
2611+
2612+
if ( $in_comment ) {
2613+
if ( '*' === $char && '/' === $next ) {
2614+
$in_comment = false;
2615+
++$i;
2616+
}
2617+
continue;
2618+
}
2619+
2620+
if ( '/' === $char && '*' === $next && ! $in_single_quote && ! $in_double_quote ) {
2621+
$in_comment = true;
2622+
++$i;
2623+
continue;
2624+
}
2625+
2626+
if ( '-' === $char && '-' === $next && ! $in_single_quote && ! $in_double_quote ) {
2627+
$in_line_comment = true;
2628+
continue;
2629+
}
2630+
2631+
if ( "'" === $char && ! $in_double_quote ) {
2632+
$in_single_quote = ! $in_single_quote;
2633+
} elseif ( '"' === $char && ! $in_single_quote ) {
2634+
$in_double_quote = ! $in_double_quote;
2635+
}
2636+
2637+
if ( ';' === $char && ! $in_single_quote && ! $in_double_quote ) {
2638+
$statements[] = $current;
2639+
$current = '';
2640+
} else {
2641+
$current .= $char;
2642+
}
2643+
}
2644+
2645+
if ( '' !== trim( $current ) ) {
2646+
$statements[] = $current;
2647+
}
2648+
2649+
return $statements;
2650+
}
24872651
}

0 commit comments

Comments
 (0)