Skip to content

Fix SQLite compatibility for search-replace command#225

Open
dr5hn wants to merge 1 commit intowp-cli:mainfrom
dr5hn:fix/190-sqlite-compatibility
Open

Fix SQLite compatibility for search-replace command#225
dr5hn wants to merge 1 commit intowp-cli:mainfrom
dr5hn:fix/190-sqlite-compatibility

Conversation

@dr5hn
Copy link
Copy Markdown

@dr5hn dr5hn commented Apr 9, 2026

Summary

Fixes #190

Addresses all MySQL-specific SQL queries that prevent wp search-replace from working with the WordPress SQLite database integration plugin. This removes the 5 @skip-sqlite tags from the test suite.

Changes

MySQL-specific query SQLite fix Location
DESCRIBE table PRAGMA table_info(table) get_columns()
REGEXP '^[aiO]:[1-9]' (serialized data detection) Fall back to PHP processing path sql_handle_col() caller
LIKE BINARY %s (case-sensitive match) Plain LIKE via like_operator() helper sql_handle_col(), php_handle_col(), log_sql_diff()
SHOW CREATE TABLE SELECT sql FROM sqlite_master Export functionality

Technical details

  • get_columns(): Uses PRAGMA table_info() on SQLite which returns name, type, pk fields instead of MySQL's Field, Type, Key structure.

  • Serialized data detection: SQLite lacks native REGEXP support. When SQLite is detected, the command skips the REGEXP check and always uses the PHP processing path (which uses preg_replace). This is slightly slower but produces identical results.

  • like_operator() helper: Returns LIKE BINARY for MySQL and LIKE for SQLite. SQLite's LIKE is case-insensitive for ASCII, which may match extra rows, but the PHP replacer handles case-sensitivity correctly so end results are identical.

  • Export: SHOW CREATE TABLE is replaced with a query to sqlite_master which returns the CREATE TABLE statement in a single sql column (index [0]) vs MySQL's two-column result (index [1]).

  • Detection: Uses the existing Utils\get_db_type() framework utility which checks for the SQLITE_DB_DROPIN_VERSION constant.

Tests

Removed @skip-sqlite tags from all 5 previously-skipped regex test scenarios:

  • Regex search/replace
  • Logging with regex replace
  • Regex search/replace with --regex-limit=1
  • Regex search/replace with --regex-limit=2
  • Regex search/replace with incorrect or default --regex-limit

Test plan

  • Run Behat test suite with MySQL: vendor/bin/behat features/search-replace.feature
  • Run Behat test suite with SQLite integration plugin enabled
  • Verify the 5 previously-skipped regex scenarios now pass with SQLite
  • Test wp search-replace with --export flag on SQLite
  • Test basic search-replace on a site with serialized data in options table

Submitted during WCAsia 2026 Contributor Day — tracked in wp-cli/wp-cli#6295

@dr5hn dr5hn requested a review from a team as a code owner April 9, 2026 09:57
Copilot AI review requested due to automatic review settings April 9, 2026 09:57
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces SQLite support for the search-replace command by implementing database-specific logic for schema retrieval and string matching. Feedback suggests enabling case-sensitive LIKE for SQLite to ensure dry-run accuracy and refactoring the column metadata logic to reduce code duplication.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Improves wp search-replace compatibility with SQLite (via the WordPress SQLite integration plugin) by replacing MySQL-specific SQL with SQLite-safe alternatives and enabling previously skipped SQLite scenarios.

Changes:

  • Replace SHOW CREATE TABLE with sqlite_master lookup when exporting on SQLite.
  • Avoid MySQL-only REGEXP/LIKE BINARY usage by routing SQLite through PHP processing and introducing a DB-aware like_operator().
  • Update Behat feature coverage by removing @skip-sqlite tags from regex scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/Search_Replace_Command.php Adds SQLite-specific SQL branches (schema export, column introspection) and DB-aware LIKE operator handling.
features/search-replace.feature Re-enables regex scenarios on SQLite by removing @skip-sqlite tags.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Search_Replace_Command.php 80.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@swissspidy
Copy link
Copy Markdown
Member

Are these queries something the SQLite integration drop-in could handle for us?

@dr5hn
Copy link
Copy Markdown
Author

dr5hn commented Apr 9, 2026

Codecov coverage note

The 11 uncovered lines are all SQLite-specific code paths (gated behind if ( 'sqlite' === Utils\get_db_type() )). Coverage is only collected from the MySQL test run (PHP 8.5 | WP latest | MySQL (with coverage)), so SQLite branches are never exercised during coverage collection.

These lines are tested by the 18 SQLite test matrix jobs (PHP 7.2–nightly, WP latest + trunk, Linux/macOS/Windows). The patch coverage gap is a known limitation of the coverage setup, not a testing gap.

@dr5hn
Copy link
Copy Markdown
Author

dr5hn commented Apr 9, 2026

@swissspidy Good question, I think some of these could absolutely live in the drop-in, but not all of them cleanly.

Query Drop-in? Notes
DESCRIBE table Yes Map to PRAGMA table_info() and remap column names
SHOW CREATE TABLE Yes Translate to SELECT sql FROM sqlite_master
SHOW FULL TABLES WHERE Table_Type = "VIEW" Yes SHOW TABLES already works, FULL variant doesn't
LIKE BINARY Tricky Needs PRAGMA case_sensitive_like = ON or a custom collation
REGEXP Hard Needs a PHP preg_match callback registered as a user function

DESCRIBE and SHOW FULL TABLES ... VIEW are good candidates for the drop-in. REGEXP and LIKE BINARY are messier — one needs a custom function, the other flips a global pragma.

Happy to open issues on WordPress/sqlite-database-integration for the ones that make sense there. This PR works as-is and can be slimmed down later as the drop-in picks those up.

Would you rather wait for drop-in support, or land this and revisit?

@github-actions github-actions bot added command:search-replace Related to 'search-replace' command contributor-day scope:distribution Related to distribution scope:testing Related to testing labels Apr 9, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

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:

  • composer install: Install dependencies.
  • composer test: Run the full test suite.
  • composer phpcs: Check for code style violations.
  • composer phpcbf: Automatically fix code style violations.
  • composer phpunit: Run unit tests.
  • composer behat: Run behavior-driven tests.

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:123

You can find a list of all available Behat steps in our handbook.

@dr5hn dr5hn force-pushed the fix/190-sqlite-compatibility branch from aadf8ba to 755407c Compare April 9, 2026 11:14
Two targeted fixes for SQLite compatibility:

1. Skip views detection on SQLite — the drop-in does not support
   SHOW FULL TABLES, and WordPress on SQLite does not use views
2. Fall back to PHP processing on SQLite for serialized data
   detection — SQLite lacks native REGEXP support

All other MySQL queries (DESCRIBE, SHOW CREATE TABLE, LIKE BINARY)
are left unchanged as the SQLite integration drop-in translates
them automatically.

Remove @skip-sqlite from 4 of 5 regex test scenarios. One scenario
(Logging with regex replace) remains skipped due to a pre-existing
difference in regex backreference behavior on SQLite.

Fixes wp-cli#190
@dr5hn dr5hn force-pushed the fix/190-sqlite-compatibility branch from 755407c to df3309a Compare April 9, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command:search-replace Related to 'search-replace' command contributor-day scope:distribution Related to distribution scope:testing Related to testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite Compatibility

3 participants