Add wpdb fallback for wp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable#320
Add wpdb fallback for wp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable#320swissspidy with Copilot wants to merge 21 commits into
wp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable#320Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…lable Agent-Logs-Url: https://github.com/wp-cli/db-command/sessions/8996fc65-2840-4792-8cf4-ddc700a7c040 Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/wp-cli/db-command/sessions/8996fc65-2840-4792-8cf4-ddc700a7c040 Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
wp db query when mysql/mariadb binary is unavailable
This comment was marked as resolved.
This comment was marked as resolved.
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>
This comment was marked as resolved.
This comment was marked as resolved.
wp db query when mysql/mariadb binary is unavailablewp db query, wp db import, wp db create, wp db drop, and wp db reset when mysql/mariadb binary is unavailable
This comment was marked as resolved.
This comment was marked as resolved.
Agent-Logs-Url: https://github.com/wp-cli/db-command/sessions/fa41acc5-de2a-4dc3-9f7a-f5b282ed670a Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
wp db query, wp db import, wp db create, wp db drop, and wp db reset when mysql/mariadb binary is unavailablewp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
…query/import Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
…_import() Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
…y output Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
…...*/) Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
…p comments Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Pull request overview
This PR adds a fallback execution path for several wp db subcommands when the mysql/mariadb client binary is not available, and extends Behat coverage to validate the fallback behavior by shadowing the client binaries via PATH.
Changes:
- Added mysql/mariadb binary availability detection and routed
wp db query/wp db import(and DDL viarun_query()) through a PHP-based fallback when unavailable. - Implemented SQL execution/import helpers (including SQL dump statement splitting) for the fallback path.
- Added Behat scenarios that simulate missing client binaries and assert fallback success + debug output.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/DB_Command.php |
Adds mysql binary probing, mysqli-based fallback execution/import, and a SQL statement splitter for imports. |
features/db.feature |
Adds scenarios verifying wp db drop and wp db reset succeed when mysql/mariadb binaries are shadowed. |
features/db-query.feature |
Adds scenario verifying wp db query succeeds and logs fallback when mysql/mariadb binaries are shadowed. |
features/db-import.feature |
Adds scenario verifying wp db import succeeds and logs fallback when mysql/mariadb binaries are shadowed. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| WP_CLI::debug( 'MySQL/MariaDB binary not available, falling back to wpdb.', 'db' ); | ||
| $this->wpdb_query( $query, $assoc_args ); | ||
| return; |
| protected function wpdb_query( $query, $assoc_args = [] ) { | ||
| $conn = $this->get_db_connection(); | ||
|
|
||
| $skip_column_names = Utils\get_flag_value( $assoc_args, 'skip-column-names', false ); | ||
| $is_row_modifying_query = (bool) preg_match( '/\b(UPDATE|DELETE|INSERT|REPLACE(?!\s*\()|LOAD DATA)\b/i', $query ); |
| protected function wpdb_import( $sql_content, $assoc_args = [] ) { | ||
| $conn = $this->get_db_connection(); | ||
|
|
||
| $skip_optimization = Utils\get_flag_value( $assoc_args, 'skip-optimization', false ); |
| if ( '-' === $char && '-' === $next && ! $in_single_quote && ! $in_double_quote ) { | ||
| $in_line_comment = true; | ||
| continue; | ||
| } |
| if ( ! $this->is_mysql_binary_available() ) { | ||
| if ( '-' === $result_file ) { | ||
| $sql_content = stream_get_contents( STDIN ); | ||
| if ( false === $sql_content ) { | ||
| WP_CLI::error( 'Failed to read from STDIN.' ); | ||
| } | ||
| $result_file = 'STDIN'; | ||
| } else { | ||
| if ( ! is_readable( $result_file ) ) { | ||
| WP_CLI::error( sprintf( 'Import file missing or not readable: %s', $result_file ) ); | ||
| } | ||
| $sql_content = file_get_contents( $result_file ); | ||
| if ( false === $sql_content ) { | ||
| WP_CLI::error( sprintf( 'Could not read import file: %s', $result_file ) ); | ||
| } | ||
| } | ||
|
|
||
| WP_CLI::debug( 'MySQL/MariaDB binary not available, falling back to wpdb for import.', 'db' ); | ||
| $this->wpdb_import( (string) $sql_content, $assoc_args ); | ||
| WP_CLI::success( sprintf( "Imported from '%s'.", $result_file ) ); |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/DB_Command.php (1)
1977-1997: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winUses a fresh connection per DDL call — fine for single calls, costly in
clean()'s loop.
run_query()now routes everyrun_query()caller through this method when the binary is unavailable, includingclean(), which callsrun_query()once per table (Lines 235-244). Each call opens and closes a brand-new mysqli connection just to run oneDROP TABLE. For large installs with many tables this multiplies connection overhead unnecessarily.♻️ Suggested direction
Accept an optional existing
mysqliconnection (or expose a connection-reuse variant) so callers that issue many DDL statements in a loop (likeclean()) can reuse a single connection instead of opening one per statement.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/DB_Command.php` around lines 1977 - 1997, Update run_query_via_mysqli() to accept an optional existing mysqli connection, creating and closing its own connection only when one is not provided. Modify clean() to obtain one reusable connection before its table-drop loop and pass it to each run_query_via_mysqli() call, then close it after the loop while preserving existing error handling.features/db-query.feature (1)
158-183: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScenario correctly matches the fallback's debug message; consider adding SQL-mode-compat coverage.
This scenario is well-formed and the previously requested
env PATH=...fix is already applied. Given the critical SQL-mode-compatibility gap identified inwpdb_query()/wpdb_import()(src/DB_Command.php), consider adding a fallback scenario analogous to "wp db queryadapts the SQL mode by default without a separate mode probe" (Lines 100-113) but using the fake-bin technique, so a fix to that gap is actually exercised by CI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/db-query.feature` around lines 158 - 183, Add a fake-bin fallback scenario in features/db-query.feature that verifies wp db query adapts SQL mode by default without a separate mode probe, using the same PATH override and unavailable mysql/mariadb binaries as the current scenario. Ensure the assertions exercise the compatibility behavior implemented by DB_Command::wpdb_query() or DB_Command::wpdb_import(), including the expected query result and debug output.features/db-import.feature (1)
72-99: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScenario structure is correct but doesn't reproduce the known regression.
This test imports a plain export on what is likely a lenient-SQL-mode CI database, so it wouldn't fail even with the missing SQL-mode-compat statement in
wpdb_import()(see src/DB_Command.php). Consider mirroring "wp db importloads a dump containing legacy zero-date values" (Lines 323-344) but through the fake-bin/mysqli fallback path, since that's the exact scenario that previously surfaced "Invalid default value for 'comment_date'" per the PR history.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/db-import.feature` around lines 72 - 99, The fallback scenario should import a dump containing legacy zero-date values so it exercises the SQL-mode compatibility logic in DB_Command::wpdb_import(), rather than importing a plain export. Adapt the existing fake-bin/mysql and fake-bin/mariadb unavailable setup to use the zero-date dump content and retain assertions for successful import and the fallback warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/DB_Command.php`:
- Around line 2593-2629: Apply get_sql_mode_compat_statement( $assoc_args ) to
the fallback connection before executing SQL in both src/DB_Command.php lines
2593-2629 within wpdb_import() and lines 2535-2583 within wpdb_query(); each
site requires the same compatibility statement before the split statements or
query runs.
- Around line 2008-2013: Update the run_query() fallback around
is_mysql_binary_available() so database creation does not route through
run_query_via_mysqli() without a selected database. Exclude the create()
operation from this fallback, preserving the existing mysqli behavior for other
queries, or explicitly align the command documentation and design with
supporting create() through that path.
- Around line 2481-2524: Update get_db_connection() to use the command’s
effective CLI database username and password values, including --dbuser and
--dbpass overrides, instead of always reading DB_USER and DB_PASSWORD. Handle
mysqli connection exceptions so failures reach the existing WP_CLI::error()
path, and ensure fallback query operations similarly tolerate or catch mysqli
exceptions before relying on connect_errno/query checks.
---
Nitpick comments:
In `@features/db-import.feature`:
- Around line 72-99: The fallback scenario should import a dump containing
legacy zero-date values so it exercises the SQL-mode compatibility logic in
DB_Command::wpdb_import(), rather than importing a plain export. Adapt the
existing fake-bin/mysql and fake-bin/mariadb unavailable setup to use the
zero-date dump content and retain assertions for successful import and the
fallback warning.
In `@features/db-query.feature`:
- Around line 158-183: Add a fake-bin fallback scenario in
features/db-query.feature that verifies wp db query adapts SQL mode by default
without a separate mode probe, using the same PATH override and unavailable
mysql/mariadb binaries as the current scenario. Ensure the assertions exercise
the compatibility behavior implemented by DB_Command::wpdb_query() or
DB_Command::wpdb_import(), including the expected query result and debug output.
In `@src/DB_Command.php`:
- Around line 1977-1997: Update run_query_via_mysqli() to accept an optional
existing mysqli connection, creating and closing its own connection only when
one is not provided. Modify clean() to obtain one reusable connection before its
table-drop loop and pass it to each run_query_via_mysqli() call, then close it
after the loop while preserving existing error handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 147dfdef-c9e6-4d69-9dda-8223e5fcbc7f
📒 Files selected for processing (4)
features/db-import.featurefeatures/db-query.featurefeatures/db.featuresrc/DB_Command.php
| if ( ! $this->is_mysql_binary_available() ) { | ||
| WP_CLI::debug( "Query via mysqli: {$query}", 'db' ); | ||
| $this->run_query_via_mysqli( $query ); | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect relevant methods in src/DB_Command.php
grep -nE 'function (create|run_query|run_query_via_mysqli|get_db_connection|is_mysql_binary_available)' -n src/DB_Command.php
# Show the relevant sections with line numbers
sed -n '1,180p' src/DB_Command.php
echo '---'
sed -n '1960,2060p' src/DB_Command.php
echo '---'
sed -n '2060,2145p' src/DB_Command.phpRepository: wp-cli/db-command
Length of output: 11488
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the connection helper and surrounding logic.
sed -n '2470,2575p' src/DB_Command.php
# Show any references to create() / unsupported / fallback in this file.
grep -nE 'unsupported|fallback|mysqli|CREATE DATABASE|run_query_via_mysqli|maybe_load_sqlite_dropin' src/DB_Command.phpRepository: wp-cli/db-command
Length of output: 5535
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '2470,2575p' src/DB_Command.php
echo '---'
grep -nE 'unsupported|fallback|mysqli|CREATE DATABASE|run_query_via_mysqli|maybe_load_sqlite_dropin' src/DB_Command.phpRepository: wp-cli/db-command
Length of output: 5539
wp db create still goes through the mysqli fallback. create() calls run_query(), and the no-binary path uses run_query_via_mysqli() with no selected database, so CREATE DATABASE will succeed there. Either exclude create() from this fallback or update the command docs/design to say it’s supported.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/DB_Command.php` around lines 2008 - 2013, Update the run_query() fallback
around is_mysql_binary_available() so database creation does not route through
run_query_via_mysqli() without a selected database. Exclude the create()
operation from this fallback, preserving the existing mysqli behavior for other
queries, or explicitly align the command documentation and design with
supporting create() through that path.
| /** | ||
| * Open a mysqli connection using the WordPress database credentials. | ||
| * | ||
| * Used as a fallback when the mysql/mariadb binary is not available. | ||
| * Parses DB_HOST the same way WordPress does (host:port, host:/socket, :/socket). | ||
| * | ||
| * @param bool $select_db Whether to select DB_NAME on connect. Pass false for | ||
| * server-level DDL (DROP/CREATE DATABASE). | ||
| * @return mysqli Connected mysqli instance. Calls WP_CLI::error() on failure. | ||
| */ | ||
| protected function get_db_connection( $select_db = true ) { | ||
| $db_host = DB_HOST; | ||
| $port = null; | ||
| $socket = null; | ||
|
|
||
| if ( ':' === substr( $db_host, 0, 1 ) ) { | ||
| $socket = substr( $db_host, 1 ); | ||
| $db_host = 'localhost'; | ||
| } else { | ||
| $parts = explode( ':', $db_host, 2 ); | ||
| $db_host = $parts[0]; | ||
| if ( isset( $parts[1] ) ) { | ||
| $port_or_socket = trim( $parts[1] ); | ||
| if ( '' !== $port_or_socket && '/' === $port_or_socket[0] ) { | ||
| $socket = $port_or_socket; | ||
| } elseif ( '' !== $port_or_socket ) { | ||
| $port = (int) $port_or_socket; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| $port_value = null !== $port ? $port : 0; | ||
| $socket_value = null !== $socket ? $socket : ''; | ||
| $database = $select_db ? DB_NAME : ''; | ||
|
|
||
| // phpcs:ignore WordPress.DB.RestrictedClasses.mysql__mysqli -- direct mysqli required as fallback when mysql binary is unavailable. | ||
| $conn = new mysqli( $db_host, DB_USER, DB_PASSWORD, $database, $port_value, $socket_value ); | ||
|
|
||
| if ( $conn->connect_errno ) { | ||
| WP_CLI::error( sprintf( 'Failed to connect to the database: %s', $conn->connect_error ) ); | ||
| } | ||
|
|
||
| return $conn; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Repo root: '; pwd
printf '\nRelevant PHP files around DB_Command:\n'
git ls-files 'src/DB_Command.php' 'composer.json' 'phpcs.xml.dist' 'README*' 'docs/**' 'bin/**' | sed 's#^`#-` #'
printf '\nSearch for PHP version requirements:\n'
rg -n --hidden -S "PHP ?8\.1|minimum PHP|PHP_VERSION|Requires PHP|php >=|php:" composer.json README* docs src bin .github 2>/dev/null || true
printf '\nSearch for mysqli_report / get_db_connection / dbuser usage:\n'
rg -n -S "mysqli_report|get_db_connection\(|dbuser|dbpass|DB_USER|DB_PASSWORD" src/DB_Command.php src 2>/dev/null || trueRepository: wp-cli/db-command
Length of output: 12267
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'src/DB_Command.php outline:\n'
ast-grep outline src/DB_Command.php --view expanded | sed -n '1,260p'
printf '\nTargeted slices around relevant methods:\n'
sed -n '2400,2605p' src/DB_Command.php | cat -nRepository: wp-cli/db-command
Length of output: 11393
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'composer.json (version-related keys):\n'
if [ -f composer.json ]; then
python3 - <<'PY'
import json
from pathlib import Path
p = Path("composer.json")
data = json.loads(p.read_text())
for k in ["require", "require-dev", "config", "scripts"]:
if k in data:
print(f"\n[{k}]")
print(data[k])
PY
fi
printf '\nphpcs.xml.dist snippets:\n'
if [ -f phpcs.xml.dist ]; then
rg -n -S "php_version|PHP|testVersion|runtime" phpcs.xml.dist || true
fiRepository: wp-cli/db-command
Length of output: 1340
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'README and package metadata around requirements/support:\n'
rg -n -S "PHP 7|PHP 8|minimum supported|requires PHP|WP-CLI|require-php|testVersion|7\.2-|8\.1|mysqli" README.md composer.json phpcs.xml.dist src 2>/dev/null || true
printf '\nREADME top section:\n'
sed -n '1,120p' README.md | cat -nRepository: wp-cli/db-command
Length of output: 5985
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'README and package metadata around requirements/support:\n'
rg -n -S "PHP 7|PHP 8|minimum supported|requires PHP|WP-CLI|require-php|testVersion|7\\.2-|8\\.1|mysqli" README.md composer.json phpcs.xml.dist src 2>/dev/null || true
printf '\nREADME top section:\n'
sed -n '1,120p' README.md | cat -nRepository: wp-cli/db-command
Length of output: 5985
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1968,2615p' src/DB_Command.php | cat -n | sed -n '1,280p'Repository: wp-cli/db-command
Length of output: 11990
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path("src/DB_Command.php")
lines = p.read_text().splitlines()
for start,end in [(1978,2028),(2528,2610)]:
print(f"\n--- {start}-{end} ---")
for i in range(start-1, min(end, len(lines))):
print(f"{i+1:5d}: {lines[i]}")
PYRepository: wp-cli/db-command
Length of output: 5329
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show where the command-level assoc args are consumed for the mysql binary path.
python3 - <<'PY'
from pathlib import Path
lines = Path("src/DB_Command.php").read_text().splitlines()
for start,end in [(470,560),(575,650),(930,1015)]:
print(f"\n--- {start}-{end} ---")
for i in range(start-1, min(end, len(lines))):
print(f"{i+1:5d}: {lines[i]}")
PYRepository: wp-cli/db-command
Length of output: 11555
🌐 Web query:
WP-CLI minimum supported PHP version current release 2025
💡 Result:
As of July 21, 2026, the current version of WP-CLI is 2.12.0 [1][2]. The official minimum supported PHP version for WP-CLI is PHP 7.2.24 or later [2][3]. While WP-CLI v2.12.0 maintained compatibility with older PHP versions (including 5.6) through various workarounds [1], official documentation confirms the current requirement as PHP 7.2.24+ [3][4]. Developers have noted that future major releases (such as the planned 3.0.0) intend to formalize this 7.2.24+ requirement to remove legacy compatibility code [1][5].
Citations:
- 1: https://make.wordpress.org/cli/2025/05/07/wp-cli-v2-12-0-release-notes/
- 2: https://root.packagist.org/packages/wp-cli/wp-cli
- 3: https://make.wordpress.org/cli/handbook/guides/installing/?output_format=md
- 4: https://github.com/wp-cli/handbook/blob/097b463ba79782edb20860108acad450f35e42fc/guides/installing.md
- 5: WP-CLI 3.0.0 / PHP version requirement tracking issue wp-cli#6026
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Search for any mysqli_report or DB bootstrap tweaks in the repo:\n'
rg -n -S "mysqli_report|MYSQLI_REPORT|wpdb|wp-db.php|database.*bootstrap|bootstrap.*database" . 2>/dev/null || trueRepository: wp-cli/db-command
Length of output: 7349
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for p in Path('.').rglob('*'):
if p.is_file() and p.suffix in {'.php', '.md', '.json', '.dist', '.txt'}:
text = p.read_text(errors='ignore')
if 'mysqli_report' in text or 'MYSQLI_REPORT' in text or 'wp-db.php' in text:
print(p)
PYRepository: wp-cli/db-command
Length of output: 502
Handle mysqli exceptions and pass CLI credentials through the fallback.
- The package still supports PHP 7.2.24+, so PHP 8.1+ installs can hit
mysqli’s default strict mode here and throw before theconnect_errno/query()checks run. get_db_connection()also still readsDB_USER/DB_PASSWORDdirectly, so--dbuser/--dbpassare ignored when the binary fallback is used.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/DB_Command.php` around lines 2481 - 2524, Update get_db_connection() to
use the command’s effective CLI database username and password values, including
--dbuser and --dbpass overrides, instead of always reading DB_USER and
DB_PASSWORD. Handle mysqli connection exceptions so failures reach the existing
WP_CLI::error() path, and ensure fallback query operations similarly tolerate or
catch mysqli exceptions before relying on connect_errno/query checks.
| protected function wpdb_import( $sql_content, $assoc_args = [] ) { | ||
| $conn = $this->get_db_connection(); | ||
|
|
||
| $skip_optimization = Utils\get_flag_value( $assoc_args, 'skip-optimization', false ); | ||
|
|
||
| if ( ! $skip_optimization ) { | ||
| $conn->query( 'SET autocommit = 0' ); | ||
| $conn->query( 'SET unique_checks = 0' ); | ||
| $conn->query( 'SET foreign_key_checks = 0' ); | ||
| } | ||
|
|
||
| $statements = $this->split_sql_statements( $sql_content ); | ||
|
|
||
| foreach ( $statements as $statement ) { | ||
| $statement = trim( $statement ); | ||
| if ( '' === $statement ) { | ||
| continue; | ||
| } | ||
| if ( ! $conn->query( $statement ) ) { | ||
| $error = $conn->error; | ||
| if ( ! $skip_optimization ) { | ||
| $conn->query( 'ROLLBACK' ); | ||
| } | ||
| $conn->close(); | ||
| WP_CLI::error( "Import failed: {$error}" ); | ||
| } | ||
| } | ||
|
|
||
| if ( ! $skip_optimization ) { | ||
| $conn->query( 'COMMIT' ); | ||
| $conn->query( 'SET autocommit = 1' ); | ||
| $conn->query( 'SET unique_checks = 1' ); | ||
| $conn->query( 'SET foreign_key_checks = 1' ); | ||
| } | ||
|
|
||
| $conn->close(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift
Missing SQL-mode compatibility is one root cause shared by both mysqli fallback executors. Neither wpdb_import() nor wpdb_query() applies get_sql_mode_compat_statement() on the fallback connection, unlike the CLI path's --init-command. This is the specific regression the PR objectives describe ("Invalid default value for 'comment_date'" on WordPress 4.9, and continued failures across versions).
src/DB_Command.php#L2593-L2629: inwpdb_import(), run$this->get_sql_mode_compat_statement( $assoc_args )on the connection before executing the split statements.src/DB_Command.php#L2535-L2583: inwpdb_query(), run the same compatibility statement on the connection before executing$query.
🧰 Tools
🪛 ast-grep (0.44.1)
[error] 2598-2598: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET autocommit = 0' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2599-2599: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET unique_checks = 0' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2600-2600: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET foreign_key_checks = 0' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2610-2610: Prevent SQL queries built from unsanitized input
Context: $conn->query( $statement )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2613-2613: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'ROLLBACK' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2621-2621: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'COMMIT' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2622-2622: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET autocommit = 1' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2623-2623: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET unique_checks = 1' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
[error] 2624-2624: Prevent SQL queries built from unsanitized input
Context: $conn->query( 'SET foreign_key_checks = 1' )
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-php)
📍 Affects 1 file
src/DB_Command.php#L2593-L2629(this comment)src/DB_Command.php#L2535-L2583
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/DB_Command.php` around lines 2593 - 2629, Apply
get_sql_mode_compat_statement( $assoc_args ) to the fallback connection before
executing SQL in both src/DB_Command.php lines 2593-2629 within wpdb_import()
and lines 2535-2583 within wpdb_query(); each site requires the same
compatibility statement before the split statements or query runs.
Several
wp dbcommands currently require themysql/mariadbCLI binary, causing failures with drop-in database engines (HyperDB, custom drivers) or environments where the binary simply isn't installed. This PR adds automatic fallback to WordPress's$wpdbfor all relevant commands.Changes
src/DB_Command.phpis_mysql_binary_available()— probes for the mysql/mariadb binary via/usr/bin/env <binary> --version; result is statically cachedmaybe_load_wpdb()— loads the minimal WordPress files needed for$wpdb(load.php,compat.php,plugin.php,functions.php,class-wpdb.php), includes anywp-content/db.phpdrop-in (HyperDB et al.), and falls back to creating a plainwpdbinstance with wp-config.php credentials +$table_prefixrun_query()— now checks binary availability at the top and, when absent, routes throughmaybe_load_wpdb()+$wpdb->query()instead of invoking the mysql CLI. This coversdropandreset.query()— after the existing SQLite branch, checks binary availability and routes to the new wpdb path when absent viawpdb_query()wpdb_query()— executes arbitrary SQL via$wpdb; mirrors the mysql-path behaviour (row-count reporting for DML, formatted tabular output for SELECT,--skip-column-namessupport)import()— when the mysql binary is unavailable, reads the SQL file (or STDIN) and delegates towpdb_import()wpdb_import()— executes a SQL dump through$wpdbstatement-by-statement, applying the sameautocommit=0 / unique_checks=0 / foreign_key_checks=0 … COMMIToptimizations as the mysql path (unless--skip-optimizationis passed)split_sql_statements()— splits a SQL string into individual statements using a state-machine parser that correctly handles single-quoted strings, double-quoted strings,--line comments, and/* */block commentsfeatures/db-query.featureAdded a
@require-mysql-or-mariadbscenario that shadows the real binary with a fakeexit 127script viaPATHprepending, then asserts the query succeeds and the debug message confirms the fallback was taken.features/db.featureAdded
@require-mysql-or-mariadbscenarios forwp db dropandwp db resetthat shadow the binary with a fakeexit 127script and assert each command succeeds via the wpdb path (verified via theQuery via wpdb:debug line).features/db-import.featureAdded a
@require-mysql-or-mariadbscenario that exports a real SQL dump first, then shadows the binary and re-imports it, asserting success and theMySQL/MariaDB binary not available, falling back to wpdb for import.debug message.Summary by CodeRabbit
New Features
Bug Fixes