Skip to content

Add wpdb fallback for wp db query and wp db import when mysql/mariadb binary is unavailable - #320

Open
swissspidy with Copilot wants to merge 35 commits into
mainfrom
copilot/fix-wp-db-query-fallback
Open

Add wpdb fallback for wp db query and wp db import when mysql/mariadb binary is unavailable#320
swissspidy with Copilot wants to merge 35 commits into
mainfrom
copilot/fix-wp-db-query-fallback

Conversation

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

wp db query and wp db import currently require the mysql/mariadb CLI binary, causing failures with drop-in database engines (HyperDB, custom drivers) or environments where the binary isn't installed. This PR adds automatic fallback to WordPress's $wpdb for these commands when the binary is unavailable.

Changes

src/DB_Command.php

  • is_mysql_binary_available() — probes for the mysql/mariadb binary via Process::create(); short-circuits to false when Utils\get_mysql_binary_path() returns an empty string without spawning a subprocess; probes --version return code to reliably detect non-functional test shims across framework dependency versions.
  • Binary availability checks — added checks in cli(), export(), check(), optimize(), and repair() so missing binaries produce clean user-facing error messages instead of passing empty paths to shell commands (which caused /usr/bin/env: illegal option -- n).
  • maybe_load_wpdb() — loads minimal WordPress files needed for $wpdb (load.php, compat.php, functions.php, plugin.php, class-wp-error.php), includes any wp-content/db.php drop-in (e.g. HyperDB), and creates a plain wpdb instance with credentials (honouring --dbuser, --dbpass, --dbname, --dbhost overrides).
  • SQL Mode Compatibility — applies $this->get_sql_mode_compat_statement() to the $wpdb session prior to query/import execution so strict zero-date modes (e.g. NO_ZERO_DATE, STRICT_TRANS_TABLES) are stripped for compatibility with WordPress schema defaults.
  • query() & wpdb_query() — checks binary availability after SQLite handling; routes to wpdb_query() when absent. Suppresses $wpdb errors during execution, splits multi-statement SQL strings via split_sql_statements(), formats SELECT results as tab-separated values, and reports affected rows for DML operations.
  • import() & wpdb_import() — when the mysql binary is unavailable, reads SQL from file or STDIN and delegates to wpdb_import(). Executes statements with transaction & constraint optimizations (autocommit=0, unique_checks=0, foreign_key_checks=0), checks for COMMIT execution success, and ignores non-essential privilege errors on SET statements.
  • split_sql_statements() — state-machine SQL parser handling single/double-quoted strings, line comments (-- with space requirement, #), block comments (/* */), MySQL conditional comments (/*!...*/), and line-boundary DELIMITER directives.

Tests & Documentation

  • Acceptance Tests: Added fallback scenarios in features/db-query.feature and features/db-import.feature asserting fallback to $wpdb, handling multi-statement queries, zero-date imports (0000-00-00 00:00:00), and DELIMITER procedure routines.
  • Docblocks: Updated command inline documentation for wp db query and wp db import to document $wpdb fallback behavior.

Note: Server-level DDL commands (wp db create, wp db drop, wp db reset) are not covered by the $wpdb fallback because $wpdb operates within an established connection to a specific database context (DB_NAME).

Summary by CodeRabbit

  • New Features

    • Added automatic WordPress-based fallback execution for wp db query and wp db import when MySQL/MariaDB client tools are unavailable.
    • Query results, affected-row counts, import success messages, and relevant warnings are displayed as expected.
    • Fallback imports support legacy zero dates, stored procedures, multi-statement SQL, and input from files or STDIN.
  • Bug Fixes

    • Added clearer availability checks and errors for database commands requiring MySQL/MariaDB client tools.
    • Improved handling of SQL comments, delimiters, quoting, and compatible session settings during fallback execution.

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.06691% with 59 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/DB_Command.php 78.06% 59 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI changed the title [WIP] Fix wp db query to fallback using WPDB Add wpdb fallback for wp db query when mysql/mariadb binary is unavailable Apr 2, 2026
Copilot AI requested a review from swissspidy April 2, 2026 21:31
@swissspidy

This comment was marked as resolved.

This comment was marked as resolved.

Copilot AI changed the title Add wpdb fallback for wp db query when mysql/mariadb binary is unavailable Add wpdb fallback for wp db query, wp db import, wp db create, wp db drop, and wp db reset when mysql/mariadb binary is unavailable Apr 3, 2026
Comment thread features/db-query.feature Outdated
@swissspidy

This comment was marked as resolved.

This comment was marked as resolved.

Copilot AI changed the title Add wpdb fallback for wp db query, wp db import, wp db create, wp db drop, and wp db reset when mysql/mariadb binary is unavailable Add wpdb fallback for wp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable Apr 3, 2026
Copilot AI requested a review from swissspidy April 3, 2026 09:12
Comment thread features/db.feature Outdated

This comment was marked as resolved.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

wp db query and wp db import now detect unavailable MySQL/MariaDB binaries and execute through wpdb. Related database commands validate required client tools, while regression scenarios cover query, import, multi-statement, zero-date, and stored-procedure behavior.

Changes

MySQL CLI fallback

Layer / File(s) Summary
Binary availability guards
src/DB_Command.php
db check, db optimize, db repair, db cli, and db export now detect missing MySQL/MariaDB client tools and report errors before constructing commands.
Query fallback and output handling
src/DB_Command.php, features/db-query.feature
Queries fall back to wpdb, execute multiple statements, preserve tab-separated result output, and cover unavailable binaries.
SQL import fallback and statement parsing
src/DB_Command.php, features/db-import.feature
Imports read SQL from files or STDIN, split statements while respecting quotes, comments, conditional comments, and DELIMITER directives, execute through wpdb, handle optimization and errors, and validate standard, zero-date, and stored-procedure imports.
SQLite scenario annotation
features/db.feature
The Windows skip explanation now follows the SQLite scenario marker.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: command:db-import

Suggested reviewers: swissspidy

Sequence Diagram(s)

sequenceDiagram
  participant WPCLI
  participant DB_Command
  participant wpdb
  participant Database
  WPCLI->>DB_Command: run db query or db import
  DB_Command->>DB_Command: detect missing MySQL/MariaDB binary
  DB_Command->>wpdb: execute query or split import statements
  wpdb->>Database: send SQL
  Database-->>wpdb: return rows, status, or errors
  wpdb-->>DB_Command: return execution results
  DB_Command-->>WPCLI: print output and fallback warning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding wpdb fallback for db query and import when the MySQL/MariaDB binary is unavailable.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/fix-wp-db-query-fallback

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/DB_Command.php (1)

1977-1997: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Uses a fresh connection per DDL call — fine for single calls, costly in clean()'s loop.

run_query() now routes every run_query() caller through this method when the binary is unavailable, including clean(), which calls run_query() once per table (Lines 235-244). Each call opens and closes a brand-new mysqli connection just to run one DROP TABLE. For large installs with many tables this multiplies connection overhead unnecessarily.

♻️ Suggested direction

Accept an optional existing mysqli connection (or expose a connection-reuse variant) so callers that issue many DDL statements in a loop (like clean()) 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 win

Scenario 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 in wpdb_query()/wpdb_import() (src/DB_Command.php), consider adding a fallback scenario analogous to "wp db query adapts 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 win

Scenario 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 import loads 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

📥 Commits

Reviewing files that changed from the base of the PR and between 4f7635d and 6051f7f.

📒 Files selected for processing (4)
  • features/db-import.feature
  • features/db-query.feature
  • features/db.feature
  • src/DB_Command.php

Comment thread src/DB_Command.php Outdated
Comment thread src/DB_Command.php
Comment thread src/DB_Command.php
@swissspidy swissspidy changed the title Add wpdb fallback for wp db query, wp db import, wp db drop, and wp db reset when mysql/mariadb binary is unavailable Add wpdb fallback for wp db query and wp db import when mysql/mariadb binary is unavailable Jul 22, 2026
@swissspidy
swissspidy force-pushed the copilot/fix-wp-db-query-fallback branch from a7665f9 to 30dfd4f Compare July 22, 2026 14:12
@swissspidy
swissspidy marked this pull request as ready for review July 22, 2026 14:19
@swissspidy
swissspidy requested a review from a team as a code owner July 22, 2026 14:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/DB_Command.php (1)

602-619: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Document the wpdb-fallback behavior and its limitations in the query/import docblocks. Both commands gained a significant new code path (automatic fallback to $wpdb when the mysql/mariadb binary is unavailable) with real behavioral differences from the mysql-client path, but neither docblock mentions it.

  • src/DB_Command.php#L602-L619: add a note to query()'s docblock that when the mysql/mariadb binary is unavailable, the query runs through $wpdb instead, --dbuser/--dbpass/other mysql-specific options are not honored in that mode, and multi-statement input (e.g. wp db query < file.sql) is not currently supported via this fallback.
  • src/DB_Command.php#L955-L977: add a note to import()'s docblock that the wpdb fallback ignores --dbuser/--dbpass, and that dumps relying on custom DELIMITER blocks (stored routines/triggers) or server-level DDL are not supported by the fallback.

As per path instructions, **/*.php: "Update relevant inline code documentation when changes affect user-facing functionality."

🤖 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 602 - 619, Update the query() docblock at
src/DB_Command.php lines 602-619 to document that unavailable mysql/mariadb
binaries trigger execution through $wpdb, mysql-specific options including
--dbuser and --dbpass are ignored, and multi-statement input is unsupported.
Update the import() docblock at src/DB_Command.php lines 955-977 to document
that the fallback ignores --dbuser/--dbpass and does not support custom
DELIMITER blocks or server-level DDL.

Source: Coding guidelines

♻️ Duplicate comments (2)
src/DB_Command.php (2)

2501-2507: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

--dbuser/--dbpass (and other connection overrides) are silently ignored by the wpdb fallback.

maybe_load_wpdb() always instantiates wpdb with the raw DB_USER/DB_PASSWORD/DB_HOST constants, with no way to honor --dbuser/--dbpass passed to wp db query/wp db import. This is the same underlying gap flagged on the previous mysqli-based implementation (get_db_connection() reading DB_USER/DB_PASSWORD directly), just carried into the new wpdb-based fallback.

🛠️ Suggested direction
-	protected function maybe_load_wpdb() {
+	protected function maybe_load_wpdb( $assoc_args = [] ) {
 		...
-			$wpdb = new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
+			$dbuser = Utils\get_flag_value( $assoc_args, 'dbuser', DB_USER );
+			$dbpass = Utils\get_flag_value( $assoc_args, 'dbpass', DB_PASSWORD );
+			$wpdb   = new wpdb( $dbuser, $dbpass, DB_NAME, DB_HOST );

and pass $assoc_args through from wpdb_query()/wpdb_import().

🤖 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 2501 - 2507, Update maybe_load_wpdb() to
accept the connection override arguments and use them when constructing the
fallback wpdb instance instead of always using DB_USER, DB_PASSWORD, DB_NAME,
and DB_HOST directly. Pass $assoc_args through from wpdb_query() and
wpdb_import(), preserving the constants as defaults when overrides are absent.

2585-2606: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

SQL-mode compatibility statement is missing from both wpdb fallback executors. Neither wpdb_import() nor wpdb_query() applies get_sql_mode_compat_statement( $assoc_args ) on the fallback connection, unlike the CLI path's --init-command. This is the same root cause previously flagged, and matches the PR history's reported persisting "Invalid default value for 'comment_date'" import failure across WordPress versions.

  • src/DB_Command.php#L2585-L2606: in wpdb_import(), run $this->get_sql_mode_compat_statement( $assoc_args ) via $wpdb->query() before executing the split statements.
  • src/DB_Command.php#L2519-L2575: in wpdb_query(), run the same compatibility statement before executing $query.
🤖 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 2585 - 2606, Apply the SQL-mode
compatibility statement to both fallback executors before running SQL: in
src/DB_Command.php lines 2585-2606, update wpdb_import() to execute
get_sql_mode_compat_statement( $assoc_args ) through $wpdb->query() before the
split statements; likewise in src/DB_Command.php lines 2519-2575, update
wpdb_query() to execute the same statement before $query.
🧹 Nitpick comments (1)
src/DB_Command.php (1)

2656-2751: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Statement splitter doesn't handle custom DELIMITER blocks (stored routines/triggers).

split_sql_statements() correctly tracks quotes/comments/conditional comments, but has no concept of a custom DELIMITER (e.g. DELIMITER ;; used by dumps with --routines/--triggers). Any embedded ; inside such a routine body would be split as a statement boundary, corrupting the routine definition. Given --skip-optimization aside, this is a real gap for non-trivial dumps, though likely acceptable if this limitation is documented (see the docblock consolidation comment) rather than fixed outright, since the fallback already explicitly doesn't support server-level DDL.

🤖 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 2656 - 2751, Update split_sql_statements()
to recognize DELIMITER directives and use the configured delimiter instead of
treating every semicolon inside stored routine or trigger definitions as a
statement boundary. Preserve existing quote, comment, and conditional-comment
handling, and ensure DELIMITER directives themselves are not included in
returned SQL statements; if this scope is intentionally unsupported, document
that limitation in the associated method docblock instead.
🤖 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 2519-2575: Update wpdb_query() to pass the input through the
existing split_sql_statements() helper and execute each resulting statement
individually, matching the iteration behavior used by wpdb_import(). Preserve
per-statement handling for modifying queries, errors, affected-row success
messages, result headers, and rows, while ensuring all statements from piped SQL
are processed.
- Around line 2519-2526: Update wpdb_query() to suppress wpdb errors during
query execution, mirroring the behavior in wpdb_import(). Capture the previous
suppression state, call $wpdb->suppress_errors(true) before executing the query,
and restore the captured state before every return path and when the method
completes.
- Around line 2633-2645: Update the transaction cleanup in the import flow
around the $wpdb query calls and its enclosing method so failures from COMMIT or
any settings-restore query are detected and propagated to the caller. Ensure
import() does not print its success message when cleanup fails, while preserving
normal success behavior when all queries complete successfully.
- Around line 2480-2493: Update the wpdb bootstrap required-files list near
$required_files to include WordPress's wp-includes/functions.php before loading
dependent files. Keep the existing require_once loop and file existence checks
unchanged so wpdb::print_error() and get_caller() can safely resolve
wp_debug_backtrace_summary().

---

Outside diff comments:
In `@src/DB_Command.php`:
- Around line 602-619: Update the query() docblock at src/DB_Command.php lines
602-619 to document that unavailable mysql/mariadb binaries trigger execution
through $wpdb, mysql-specific options including --dbuser and --dbpass are
ignored, and multi-statement input is unsupported. Update the import() docblock
at src/DB_Command.php lines 955-977 to document that the fallback ignores
--dbuser/--dbpass and does not support custom DELIMITER blocks or server-level
DDL.

---

Duplicate comments:
In `@src/DB_Command.php`:
- Around line 2501-2507: Update maybe_load_wpdb() to accept the connection
override arguments and use them when constructing the fallback wpdb instance
instead of always using DB_USER, DB_PASSWORD, DB_NAME, and DB_HOST directly.
Pass $assoc_args through from wpdb_query() and wpdb_import(), preserving the
constants as defaults when overrides are absent.
- Around line 2585-2606: Apply the SQL-mode compatibility statement to both
fallback executors before running SQL: in src/DB_Command.php lines 2585-2606,
update wpdb_import() to execute get_sql_mode_compat_statement( $assoc_args )
through $wpdb->query() before the split statements; likewise in
src/DB_Command.php lines 2519-2575, update wpdb_query() to execute the same
statement before $query.

---

Nitpick comments:
In `@src/DB_Command.php`:
- Around line 2656-2751: Update split_sql_statements() to recognize DELIMITER
directives and use the configured delimiter instead of treating every semicolon
inside stored routine or trigger definitions as a statement boundary. Preserve
existing quote, comment, and conditional-comment handling, and ensure DELIMITER
directives themselves are not included in returned SQL statements; if this scope
is intentionally unsupported, document that limitation in the associated method
docblock instead.
🪄 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: 5a2840be-db42-4924-83dc-02975a8da892

📥 Commits

Reviewing files that changed from the base of the PR and between 6051f7f and 30dfd4f.

📒 Files selected for processing (3)
  • features/db-import.feature
  • features/db.feature
  • src/DB_Command.php
💤 Files with no reviewable changes (1)
  • features/db.feature
🚧 Files skipped from review as they are similar to previous changes (1)
  • features/db-import.feature

Comment thread src/DB_Command.php
Comment thread src/DB_Command.php
Comment thread src/DB_Command.php
Comment thread src/DB_Command.php
@apermo

apermo commented Jul 28, 2026

Copy link
Copy Markdown

Coming from #319, where I described our case: webserver image without the mysql/mariadb client binaries. So this PR would fix a real problem for us and I read through the diff properly.

The overall shape looks right to me. maybe_load_wpdb() loading just load.php, compat.php, plugin.php, class-wp-error.php and the wpdb class plus the db.php drop-in is the same trick as maybe_load_sqlite_dropin(), and it keeps the "works before WordPress is installed" property that the binaries were there for in the first place.

Five things I would want fixed before this lands. CodeRabbit flagged most of them, I verified them against the current branch (30dfd4fc):

  1. The SQL mode compat statement never reaches the fallback connection. The CLI path injects it as --init-command through apply_sql_mode_compat_init_command(), but neither wpdb_query() nor wpdb_import() runs get_sql_mode_compat_statement(). So importing a dump with legacy zero dates fails with Invalid default value for 'comment_date' on a strict server, which is exactly what Adapt SQL mode to account for WordPress incompatibilities #168 and SQL mode compat logic is missing for SQL from STDIN #171 were about. It needs to be the first statement on the connection in both methods.
  2. --dbuser and --dbpass are silently ignored. maybe_load_wpdb() hardcodes new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ). There is already a Behat scenario covering those flags for import, and silently connecting as a different user than asked is worse than erroring out, so either honour the flags or refuse when they are set.
  3. wp db query < file.sql with more than one statement only runs the first one. The import path splits, the query path does not, and split_sql_statements() is right there.
  4. DELIMITER blocks. The splitter has no concept of them, so a dump with triggers or routines gets cut apart mid-body. I think documenting it as unsupported is fine, silently corrupting a routine definition is not.
  5. The docblocks do not mention the fallback at all. Users need to find out about the different execution engine and its limitations somewhere.

Two smaller notes:

is_mysql_binary_available() spawns another process to run <binary> --version, but Utils\get_mysql_binary_path() already returns an empty string when it finds nothing. Short-circuiting on the empty path first would save a process spawn on exactly the systems this PR is for. I do see that the extra probe is what makes the fake-bin Behat scenarios work, since those shims exist on PATH and only fail on execution, so keep the probe, just skip it when the path is empty.

And independent of the fallback: the commands that still need a binary (export, check, optimize, repair) keep producing /usr/bin/env: illegal option -- n when nothing is installed, because the empty binary path is interpolated unchecked. A real error message there would be a small, separate, very welcome change.

@swissspidy happy to help move this along. I can test the branch against our setup, write the two missing Behat scenarios (zero-date dump through the fallback, multi-statement db query from STDIN), or take the branch into a fork and push the fixes as a follow-up PR. Whatever fits your workflow best.

- Short-circuit is_mysql_binary_available() when binary path is empty.
- Check binary availability before running commands without fallback (cli, export, check, optimize, repair) to avoid executing empty paths via /usr/bin/env.
- Apply SQL mode compatibility statement before running queries and imports under wpdb fallback.
- Pass custom dbuser, dbpass, dbname, and dbhost credentials to maybe_load_wpdb().
- Add support for multi-statement queries, -- space comment rules, # comments, and DELIMITER directives in split_sql_statements().
- Add error suppression to wpdb_query() and propagate COMMIT failures in wpdb_import().
- Update query and import docblocks to document wpdb fallback behavior.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@features/db-query.feature`:
- Around line 212-215: Update the “Database querying falls back to wpdb when
PATH contains no binaries” scenario so the wp command remains resolvable while
mysql and mariadb are hidden from PATH; use the runner’s absolute path or
construct PATH to retain wp, ensuring the test reaches the intended wpdb
fallback behavior.

In `@src/DB_Command.php`:
- Around line 2837-2846: Update the SQL parsing logic around the delimiter
handling in DB_Command to recognize DELIMITER directives when they occur at a
line boundary, before normal statement accumulation or semicolon processing.
Ensure newline-terminated directives update the active delimiter without
splitting procedure bodies, while preserving existing quoted-string handling and
statement collection. Add a procedure-based Behat acceptance scenario covering a
standard MySQL dump with a DELIMITER directive and verify the routine parses
correctly.
- Around line 289-296: Replace the unreachable empty-string guards with a shared
cached executable-validation helper that probes the selected binary using
--version and reports the existing client-tools error when unavailable. Apply
this validation before building the check command at src/DB_Command.php lines
289-296, before building the optimize command at lines 355-362, before building
the repair command at lines 421-428, and before probing options or exporting
with the dump binary at lines 798-803; reuse the helper across all four sites.
🪄 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: 994b2729-3667-4b32-b15c-20a6baeba4f2

📥 Commits

Reviewing files that changed from the base of the PR and between 30dfd4f and 1beefa6.

📒 Files selected for processing (3)
  • features/db-import.feature
  • features/db-query.feature
  • src/DB_Command.php

Comment thread features/db-query.feature Outdated
Comment thread src/DB_Command.php
Comment thread src/DB_Command.php
- Ensure is_mysql_binary_available() probes --version for non-empty binary paths so fake-bin test shims with non-zero exit codes correctly trigger wpdb fallback across all wp-cli dependency versions.
- Recognize DELIMITER directives at line boundaries before normal statement accumulation in split_sql_statements().
- Add acceptance test for DELIMITER procedure imports under wpdb fallback.
Ensure wp-includes/functions.php is loaded during wpdb bootstrap so helper functions like wp_debug_backtrace_summary() are available if wpdb::print_error() or get_caller() are invoked.
…wpdb fallback details

Document client binary requirements across create, drop, reset, check, optimize, repair, cli, and export subcommands, and clarify wpdb fallback behavior for query and import.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/DB_Command.php (1)

623-638: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Honor or reject MySQL connection options consistently in fallback mode. The binary paths accept options such as --host, while the wpdb fallback uses DB_HOST unless undocumented dbhost is supplied. Silent fallback can target the wrong database.

  • src/DB_Command.php#L623-L638: translate supported connection options before calling wpdb_query(), or error for unsupported options.
  • src/DB_Command.php#L982-L1002: apply the same behavior before calling wpdb_import().

As per coding guidelines, “Update relevant inline code documentation when changes affect user-facing functionality.”

🤖 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 623 - 638, Ensure MySQL connection options
are handled consistently when binaries are unavailable: in the fallback query
path around DB_Command’s wpdb_query call and the import path around wpdb_import,
translate every supported option such as --host into the fallback connection
configuration, or reject unsupported options with a clear WP_CLI error before
execution. Update the relevant inline documentation to describe this user-facing
fallback behavior; apply the change at both src/DB_Command.php ranges 623-638
and 982-1002.

Source: Coding guidelines

🤖 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 2774-2778: Update the DELIMITER parsing logic around
$delimiter_line and $delimiter in the SQL processing loop to validate that the
extracted delimiter is non-empty before accepting it. Reject or report malformed
empty DELIMITER directives and ensure the loop cannot continue with a
zero-length delimiter that leaves $i unchanged.

---

Outside diff comments:
In `@src/DB_Command.php`:
- Around line 623-638: Ensure MySQL connection options are handled consistently
when binaries are unavailable: in the fallback query path around DB_Command’s
wpdb_query call and the import path around wpdb_import, translate every
supported option such as --host into the fallback connection configuration, or
reject unsupported options with a clear WP_CLI error before execution. Update
the relevant inline documentation to describe this user-facing fallback
behavior; apply the change at both src/DB_Command.php ranges 623-638 and
982-1002.
🪄 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: d0d2d378-e606-40d7-be13-4f2109d55d36

📥 Commits

Reviewing files that changed from the base of the PR and between cc9efd3 and 3958766.

📒 Files selected for processing (2)
  • features/db-import.feature
  • src/DB_Command.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • features/db-import.feature

Comment thread src/DB_Command.php Outdated
Comment on lines +2774 to +2778
$delimiter_line = trim( substr( $sql, $i, $line_end - $i ) );
$delimiter = trim( substr( $delimiter_line, 10 ) );
$current = '';
$i = $line_end;
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Reject empty DELIMITER directives.

Line 2775 accepts an empty delimiter; the later zero-length match never advances $i, hanging wp db query or wp db import on malformed SQL.

Proposed fix
 $delimiter_line = trim( substr( $sql, $i, $line_end - $i ) );
 $delimiter      = trim( substr( $delimiter_line, 10 ) );
+if ( '' === $delimiter ) {
+	WP_CLI::error( 'Invalid DELIMITER directive.' );
+}
 $current        = '';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$delimiter_line = trim( substr( $sql, $i, $line_end - $i ) );
$delimiter = trim( substr( $delimiter_line, 10 ) );
$current = '';
$i = $line_end;
continue;
$delimiter_line = trim( substr( $sql, $i, $line_end - $i ) );
$delimiter = trim( substr( $delimiter_line, 10 ) );
if ( '' === $delimiter ) {
WP_CLI::error( 'Invalid DELIMITER directive.' );
}
$current = '';
$i = $line_end;
continue;
🤖 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 2774 - 2778, Update the DELIMITER parsing
logic around $delimiter_line and $delimiter in the SQL processing loop to
validate that the extracted delimiter is non-empty before accepting it. Reject
or report malformed empty DELIMITER directives and ensure the loop cannot
continue with a zero-length delimiter that leaves $i unchanged.

Ensure split_sql_statements() does not accept empty delimiter tokens on malformed DELIMITER lines, preventing zero-length delimiter matching and infinite loops.
…_load_wpdb()

Ensure all standard MySQL connection flags (user, password/pass, database, host) as well as dbuser/dbpass/dbname/dbhost are mapped to the fallback wpdb instance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

query shouldn't require mysql/mariadb cli tools

4 participants