-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Bug Report
wp db query silently swallows SELECT results when the query contains the MySQL REPLACE() string function. The command outputs Success: Query succeeded. Rows affected: -1 instead of the query results.
This is because the write-query detection regex on line 541 of DB_Command.php matches the word REPLACE without distinguishing between:
REPLACE()— a MySQL string function used in SELECT statementsREPLACE INTO— a DML write statement
Reproduction
# This works (no REPLACE keyword):
$ wp db query "SELECT CONCAT('a', 'b') as result"
result
ab
# This silently fails (REPLACE string function triggers write path):
$ wp db query "SELECT REPLACE('hello world', 'world', 'there') as result"
Success: Query succeeded. Rows affected: -1
# Piping through db cli works fine (bypasses the regex):
$ echo "SELECT REPLACE('hello world', 'world', 'there') as result;" | wp db cli
result
hello thereRoot Cause
The regex at line 541:
$is_row_modifying_query = isset( $assoc_args['execute'] )
&& preg_match( '/\b(UPDATE|DELETE|INSERT|REPLACE|LOAD DATA)\b/i', $assoc_args['execute'] );\bREPLACE\b matches the REPLACE in REPLACE('hello world', ...) because \b treats the transition from E to ( as a word boundary.
When the write path is taken, the code:
- Appends
; SELECT ROW_COUNT();to the query - Captures stdout internally (the
falsethird arg toself::run) - Extracts only the last line as the affected row count (
-1because the prior statement was a SELECT) - Prints the success message and discards the actual query results
Suggested Fix
The MySQL REPLACE write statement syntax is always REPLACE INTO. Changing the regex to match REPLACE\s+INTO instead of bare REPLACE would correctly distinguish the two:
preg_match( '/\b(UPDATE|DELETE|INSERT|REPLACE\s+INTO|LOAD DATA)\b/i', $assoc_args['execute'] );Note: MySQL does technically allow REPLACE without INTO (the INTO keyword is optional), but this mirrors how the regex already handles INSERT — bare INSERT matches, which works because INSERT has no equivalent string function collision. For REPLACE, the collision with the string function makes the stricter match necessary.
Introduced in PR #277 (merged March 10, 2025).
Environment
WP-CLI version: 2.12.0
PHP version: 8.4
MySQL: MariaDB
OS: Linux (Debian)
- Yes, I reviewed the contribution guidelines.
- Yes, more specifically, I reviewed the guidelines on how to write clear bug reports.