Fix: CLI export silently drops _-prefixed options from wp_options (WordPress 6.4+)#157
Open
Striffly wants to merge 1 commit intodeliciousbrains:masterfrom
Open
Fix: CLI export silently drops _-prefixed options from wp_options (WordPress 6.4+)#157Striffly wants to merge 1 commit intodeliciousbrains:masterfrom
_-prefixed options from wp_options (WordPress 6.4+)#157Striffly wants to merge 1 commit intodeliciousbrains:masterfrom
Conversation
Table::$first_select was not reset in get_structure_info() when processing a new table. In CLI mode (single PHP process), the flag carried over from the previous table, causing the first SELECT query to include a stale WHERE primary_key > '0' clause. This had no visible effect on tables with integer primary keys (e.g. wp_posts.ID) since ID > '0' evaluates to ID > 0 which is always true for auto-increment values. However, WordPress 6.4 changed wp_options PRIMARY KEY from option_id (bigint) to option_name (varchar). With utf8mb4_unicode_520_ci collation, underscore '_' sorts before '0', so the clause option_name > '0' silently excluded all '_'-prefixed options (_options_*, _site_transient_*, _squidge_*, etc.). This broke ACF get_field() for options pages after a CLI export/pull, because ACF reference entries (e.g. _options_cookies_categories) were missing from the exported dump. The fix resets $this->first_select = null alongside the existing $this->primary_keys reset in get_structure_info(), ensuring each table's first query starts without a compound key WHERE clause.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When exporting via CLI (
wp migratedb export), allwp_optionsrows withoption_namestarting with_(underscore) are silently excluded from the dump. This does not occur when exporting via the admin UI.Exemple of affected rows:
_options_*) — breaksget_field()on options pages_wp_suggested_policy_text_has_changed)Root Cause
WP Migrate DB paginates results using the table's PRIMARY KEY:
The
Table::$first_selectflag controls whether theWHERE primary_key > valueclause is added:null→ first query for a table, no clause addedfalse→ subsequent queries, clause added with the last seen key valueIn UI mode (AJAX), each HTTP request instantiates a new
Tableobject, so$first_selectstarts atnullfor each table. This works correctly.In CLI mode, a single PHP process handles all tables sequentially. The
Tableobject persists across tables. Whenget_structure_info()is called for a new table, it resets$this->primary_keysto['column' => 0]but does not reset$this->first_select. The flag remainsfalsefrom the previous table.This causes the first query of every table (except the very first one processed) to include:
...where
'0'is the placeholder initialization value.Why This Only Manifests on WordPress 6.4+
WordPress 6.4 changed the
wp_optionsschema:wp_posts.ID,wp_postmeta.meta_id):WHERE ID > '0'is cast toID > 0, which isTRUEfor all auto-increment values ≥ 1. No data loss.wp_options.option_name):WHERE option_name > '0'uses string comparison underutf8mb4_unicode_520_cicollation. In this collation,_(underscore, U+005F) sorts before0(U+0030). So_options_xxx > '0'evaluates toFALSE, and all underscore-prefixed rows are excluded.Impact
In a test database with 608 non-transient options:
_)This directly breaks any functionality relying on
_-prefixed option entries, most notably ACF options pages whereget_field('field_name', 'options')returns empty/raw values because the field reference entries (_options_field_name → field_XXXXX) are missing.Fix
One line added in
Table::get_structure_info():This ensures
$first_selectis reset alongside$primary_keyswhen processing a new table, so the first SELECT for each table does not include a stale compound key clause.How to Reproduce
wp_optionsPRIMARY KEY isoption_name)wp migratedb export /tmp/dump.sqlgrep "_options_" /tmp/dump.sql→ 0 matchesgrep "_options_"→ matches foundHow to Verify the Fix