Fix: REPLACE() string function misidentified as write query#314
Conversation
Use negative lookahead REPLACE(?!\s*\() instead of bare REPLACE to distinguish the MySQL REPLACE() string function from the REPLACE INTO (or bare REPLACE) DML write statement. This avoids a false positive where SELECT queries containing REPLACE() are routed through the write-query code path, causing their results to be silently swallowed. Fixes wp-cli#313
|
Hello! 👋 Thanks for opening this pull request! Please check out our contributing guidelines. We appreciate you taking the initiative to contribute to this project. Contributing isn't limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation. Here are some useful Composer commands to get you started:
To run a single Behat test, you can use the following command: # Run all tests in a single file
composer behat features/some-feature.feature
# Run only a specific scenario (where 123 is the line number of the "Scenario:" title)
composer behat features/some-feature.feature:123You can find a list of all available Behat steps in our handbook. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request resolves a bug where Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request provides a good fix for an issue where REPLACE() string functions in SELECT queries were misidentified as write operations. The change to the regex is logical and the pull request description is very clear and includes thorough testing details. I've found one edge case related to SQL comments that the new regex does not handle, which I've detailed in a specific comment. Overall, this is a solid improvement.
| } | ||
|
|
||
| $is_row_modifying_query = isset( $assoc_args['execute'] ) && preg_match( '/\b(UPDATE|DELETE|INSERT|REPLACE|LOAD DATA)\b/i', $assoc_args['execute'] ); | ||
| $is_row_modifying_query = isset( $assoc_args['execute'] ) && preg_match( '/\b(UPDATE|DELETE|INSERT|REPLACE(?!\s*\()|LOAD DATA)\b/i', $assoc_args['execute'] ); |
There was a problem hiding this comment.
This is a good fix for the common case. However, this regex can still misidentify a query if there is a SQL comment between REPLACE and the opening parenthesis.
For example, a query like SELECT * FROM my_table WHERE col = REPLACE/* a comment */('a', 'b'); would be incorrectly flagged as a write query.
The negative lookahead (?!\s*\() doesn't account for comments. As a result, REPLACE is matched, and the bug's symptom (swallowing SELECT output) would persist for this edge case.
Handling all SQL comment styles (--, #, /* ... */) could make the regex very complex. It's a trade-off between correctness for all edge cases and regex simplicity. Given the context, you might decide the current level of fix is sufficient, but I wanted to point out this remaining scenario.
Fixes #313
Description
wp db querysilently swallows SELECT results when the query contains the MySQLREPLACE()string function. The command outputsSuccess: Query succeeded. Rows affected: -1instead of the actual query results.The write-query detection regex added in #277 matches bare
REPLACEwithout distinguishing between:REPLACE()— a MySQL string function used in SELECT statementsREPLACE INTO/REPLACE— a DML write statementChange
One-line regex fix — adds a negative lookahead
(?!\s*\()so thatREPLACEonly matches when it's not followed by an opening parenthesis:Testing
Tested against a live WordPress multisite (WP 6.9.1, MariaDB, PHP 8.4) by extracting the 2.12.0 phar, patching DB_Command.php, and running from source. Full results:
SELECT queries with REPLACE() — all correctly return results:
SELECT REPLACE('hello world', 'world', 'there')hello thereREPLACE(REPLACE(REPLACE(...)))Write queries — all correctly show rows affected:
The negative lookahead handles every combination — including the rare bare
REPLACE table (...)syntax (without INTO) which MySQL/MariaDB accept as valid DML.Discovery Context
Found this bug while running automated audit queries against a WordPress multisite events calendar — using
REPLACE()to strip suffixes from taxonomy term names for comparison. The query returned nothing, forcing a workaround throughwp db clipiping.AI disclosure: This PR was prepared with AI assistance (Claude Code). The diagnosis, fix, and tests were reviewed and validated by the submitter against a live WordPress multisite (WP 6.9.1, MariaDB 10.11, PHP 8.4).