Commit 5b19de4034f for woocommerce
commit 5b19de4034f7252184e743714cd3a5f85f19f314
Author: Néstor Soriano <konamiman@konamiman.com>
Date: Tue May 19 17:25:31 2026 +0200
Restore the meta_value column in the meta_key_value index of the HPOS meta table (#65146)
diff --git a/plugins/woocommerce/changelog/fix-meta-key-value-index-removal b/plugins/woocommerce/changelog/fix-meta-key-value-index-removal
new file mode 100644
index 00000000000..86619e72bde
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-meta-key-value-index-removal
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Restore the meta_value column to the meta_key_value index on the wc_orders_meta table to fix a performance regression.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index a81552692bc..799d1d29bb3 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -46,7 +46,7 @@ class WC_Install {
* @var array
*/
private static $db_updates = array(
- '2.0.0' => array(
+ '2.0.0' => array(
'wc_update_200_file_paths',
'wc_update_200_permalinks',
'wc_update_200_subcat_display',
@@ -55,42 +55,42 @@ class WC_Install {
'wc_update_200_images',
'wc_update_200_db_version',
),
- '2.0.9' => array(
+ '2.0.9' => array(
'wc_update_209_brazillian_state',
'wc_update_209_db_version',
),
- '2.1.0' => array(
+ '2.1.0' => array(
'wc_update_210_remove_pages',
'wc_update_210_file_paths',
'wc_update_210_db_version',
),
- '2.2.0' => array(
+ '2.2.0' => array(
'wc_update_220_shipping',
'wc_update_220_order_status',
'wc_update_220_variations',
'wc_update_220_attributes',
'wc_update_220_db_version',
),
- '2.3.0' => array(
+ '2.3.0' => array(
'wc_update_230_options',
'wc_update_230_db_version',
),
- '2.4.0' => array(
+ '2.4.0' => array(
'wc_update_240_options',
'wc_update_240_shipping_methods',
'wc_update_240_api_keys',
'wc_update_240_refunds',
'wc_update_240_db_version',
),
- '2.4.1' => array(
+ '2.4.1' => array(
'wc_update_241_variations',
'wc_update_241_db_version',
),
- '2.5.0' => array(
+ '2.5.0' => array(
'wc_update_250_currency',
'wc_update_250_db_version',
),
- '2.6.0' => array(
+ '2.6.0' => array(
'wc_update_260_options',
'wc_update_260_termmeta',
'wc_update_260_zones',
@@ -98,26 +98,26 @@ class WC_Install {
'wc_update_260_refunds',
'wc_update_260_db_version',
),
- '3.0.0' => array(
+ '3.0.0' => array(
'wc_update_300_grouped_products',
'wc_update_300_settings',
'wc_update_300_product_visibility',
'wc_update_300_db_version',
),
- '3.1.0' => array(
+ '3.1.0' => array(
'wc_update_310_downloadable_products',
'wc_update_310_old_comments',
'wc_update_310_db_version',
),
- '3.1.2' => array(
+ '3.1.2' => array(
'wc_update_312_shop_manager_capabilities',
'wc_update_312_db_version',
),
- '3.2.0' => array(
+ '3.2.0' => array(
'wc_update_320_mexican_states',
'wc_update_320_db_version',
),
- '3.3.0' => array(
+ '3.3.0' => array(
'wc_update_330_image_options',
'wc_update_330_webhooks',
'wc_update_330_product_stock_status',
@@ -126,48 +126,48 @@ class WC_Install {
'wc_update_330_set_paypal_sandbox_credentials',
'wc_update_330_db_version',
),
- '3.4.0' => array(
+ '3.4.0' => array(
'wc_update_340_states',
'wc_update_340_state',
'wc_update_340_last_active',
'wc_update_340_db_version',
),
- '3.4.3' => array(
+ '3.4.3' => array(
'wc_update_343_cleanup_foreign_keys',
'wc_update_343_db_version',
),
- '3.4.4' => array(
+ '3.4.4' => array(
'wc_update_344_recreate_roles',
'wc_update_344_db_version',
),
- '3.5.0' => array(
+ '3.5.0' => array(
'wc_update_350_reviews_comment_type',
'wc_update_350_db_version',
),
- '3.5.2' => array(
+ '3.5.2' => array(
'wc_update_352_drop_download_log_fk',
),
- '3.5.4' => array(
+ '3.5.4' => array(
'wc_update_354_modify_shop_manager_caps',
'wc_update_354_db_version',
),
- '3.6.0' => array(
+ '3.6.0' => array(
'wc_update_360_product_lookup_tables',
'wc_update_360_term_meta',
'wc_update_360_downloadable_product_permissions_index',
'wc_update_360_db_version',
),
- '3.7.0' => array(
+ '3.7.0' => array(
'wc_update_370_tax_rate_classes',
'wc_update_370_mro_std_currency',
'wc_update_370_db_version',
),
- '3.9.0' => array(
+ '3.9.0' => array(
'wc_update_390_move_maxmind_database',
'wc_update_390_change_geolocation_database_update_cron',
'wc_update_390_db_version',
),
- '4.0.0' => array(
+ '4.0.0' => array(
'wc_update_product_lookup_tables',
'wc_update_400_increase_size_of_column',
'wc_update_400_reset_action_scheduler_migration_status',
@@ -176,27 +176,27 @@ class WC_Install {
'wc_admin_update_0251_remove_unsnooze_action',
'wc_update_400_db_version',
),
- '4.4.0' => array(
+ '4.4.0' => array(
'wc_update_440_insert_attribute_terms_for_variable_products',
'wc_admin_update_110_remove_facebook_note',
'wc_admin_update_130_remove_dismiss_action_from_tracking_opt_in_note',
'wc_update_440_db_version',
),
- '4.5.0' => array(
+ '4.5.0' => array(
'wc_update_450_sanitize_coupons_code',
'wc_update_450_db_version',
),
- '5.0.0' => array(
+ '5.0.0' => array(
'wc_update_500_fix_product_review_count',
'wc_admin_update_160_remove_facebook_note',
'wc_admin_update_170_homescreen_layout',
'wc_update_500_db_version',
),
- '5.6.0' => array(
+ '5.6.0' => array(
'wc_update_560_create_refund_returns_page',
'wc_update_560_db_version',
),
- '6.0.0' => array(
+ '6.0.0' => array(
'wc_update_600_migrate_rate_limit_options',
'wc_admin_update_270_delete_report_downloads',
'wc_admin_update_271_update_task_list_options',
@@ -205,130 +205,132 @@ class WC_Install {
'wc_admin_update_290_delete_default_homepage_layout_option',
'wc_update_600_db_version',
),
- '6.3.0' => array(
+ '6.3.0' => array(
'wc_update_630_create_product_attributes_lookup_table',
'wc_admin_update_300_update_is_read_from_last_read',
'wc_update_630_db_version',
),
- '6.4.0' => array(
+ '6.4.0' => array(
'wc_update_640_add_primary_key_to_product_attributes_lookup_table',
'wc_admin_update_340_remove_is_primary_from_note_action',
'wc_update_640_db_version',
),
- '6.5.0' => array(
+ '6.5.0' => array(
'wc_update_650_approved_download_directories',
),
- '6.5.1' => array(
+ '6.5.1' => array(
'wc_update_651_approved_download_directories',
),
- '6.7.0' => array(
+ '6.7.0' => array(
'wc_update_670_purge_comments_count_cache',
'wc_update_670_delete_deprecated_remote_inbox_notifications_option',
),
- '7.0.0' => array(
+ '7.0.0' => array(
'wc_update_700_remove_download_log_fk',
'wc_update_700_remove_recommended_marketing_plugins_transient',
),
- '7.2.1' => array(
+ '7.2.1' => array(
'wc_update_721_adjust_new_zealand_states',
'wc_update_721_adjust_ukraine_states',
),
- '7.2.2' => array(
+ '7.2.2' => array(
'wc_update_722_adjust_new_zealand_states',
'wc_update_722_adjust_ukraine_states',
),
- '7.5.0' => array(
+ '7.5.0' => array(
'wc_update_750_add_columns_to_order_stats_table',
'wc_update_750_disable_new_product_management_experience',
),
- '7.7.0' => array(
+ '7.7.0' => array(
'wc_update_770_remove_multichannel_marketing_feature_options',
),
- '7.9.0' => array(
+ '7.9.0' => array(
'wc_update_790_blockified_product_grid_block',
),
- '8.1.0' => array(
+ '8.1.0' => array(
'wc_update_810_migrate_transactional_metadata_for_hpos',
),
- '8.3.0' => array(
+ '8.3.0' => array(
'wc_update_830_rename_checkout_template',
'wc_update_830_rename_cart_template',
),
- '8.6.0' => array(
+ '8.6.0' => array(
'wc_update_860_remove_recommended_marketing_plugins_transient',
),
- '8.7.0' => array(
+ '8.7.0' => array(
'wc_update_870_prevent_listing_of_transient_files_directory',
),
- '8.9.0' => array(
+ '8.9.0' => array(
'wc_update_890_update_connect_to_woocommerce_note',
'wc_update_890_update_paypal_standard_load_eligibility',
),
- '8.9.1' => array(
+ '8.9.1' => array(
'wc_update_891_create_plugin_autoinstall_history_option',
),
- '9.1.0' => array(
+ '9.1.0' => array(
'wc_update_910_add_launch_your_store_tour_option',
'wc_update_910_remove_obsolete_user_meta',
),
- '9.2.0' => array(
+ '9.2.0' => array(
'wc_update_920_add_wc_hooked_blocks_version_option',
),
- '9.3.0' => array(
+ '9.3.0' => array(
'wc_update_930_add_woocommerce_coming_soon_option',
'wc_update_930_migrate_user_meta_for_launch_your_store_tour',
),
- '9.4.0' => array(
+ '9.4.0' => array(
'wc_update_940_add_phone_to_order_address_fts_index',
'wc_update_940_remove_help_panel_highlight_shown',
),
- '9.5.0' => array(
+ '9.5.0' => array(
'wc_update_950_tracking_option_autoload',
),
- '9.6.1' => array(
+ '9.6.1' => array(
'wc_update_961_migrate_default_email_base_color',
),
- '9.8.0' => array(
+ '9.8.0' => array(
'wc_update_980_remove_order_attribution_install_banner_dismissed_option',
),
- '9.8.5' => array(
+ '9.8.5' => array(
'wc_update_985_enable_new_payments_settings_page_feature',
),
- '9.9.0' => array(
+ '9.9.0' => array(
'wc_update_990_remove_wc_count_comments_transient',
'wc_update_990_remove_email_notes',
),
- '10.0.0' => array(
+ '10.0.0' => array(
'wc_update_1000_multisite_visibility_setting',
'wc_update_1000_remove_patterns_toolkit_transient',
),
- '10.2.0' => array(
+ '10.2.0' => array(
'wc_update_1020_add_old_refunded_order_items_to_product_lookup_table',
),
- '10.3.0' => array(
+ '10.3.0' => array(
'wc_update_1030_add_comments_date_type_index',
),
- '10.4.0' => array(
+ '10.4.0' => array(
'wc_update_1040_add_idx_date_paid_status_parent',
'wc_update_1040_cleanup_legacy_ptk_patterns_fetching',
),
- '10.5.0' => array(
+ '10.5.0' => array(
'wc_update_1050_migrate_brand_permalink_setting',
'wc_update_1050_enable_autoload_options',
'wc_update_1050_add_idx_user_email',
'wc_update_1050_remove_deprecated_marketplace_option',
),
- '10.6.0' => array(
+ '10.6.0' => array(
'wc_update_1060_add_woo_idx_comment_approved_type_index',
),
- '10.7.0' => array(
+ '10.7.0' => array(
'wc_update_1070_disable_hpos_sync_on_read',
),
- '10.8.0' => array(
+ '10.8.0' => array(
'wc_update_1080_migrate_analytics_import_option',
- 'wc_update_1080_slim_orders_meta_key_index',
'wc_update_1080_backfill_email_template_sync_meta',
),
+ '10.8.0-2' => array(
+ 'wc_update_10802_restore_orders_meta_key_value_index',
+ ),
);
/**
@@ -579,7 +581,8 @@ class WC_Install {
* @return void
*/
public static function install_actions() {
- if ( ! empty( $_GET['do_update_woocommerce'] ) ) { // WPCS: input var ok.
+ if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
+ // WPCS: input var ok.
check_admin_referer( 'wc_db_update', 'wc_db_update_nonce' );
wc_get_logger()->info( 'Manual database update triggered.', array( 'source' => 'wc-updater' ) );
self::update();
@@ -602,7 +605,8 @@ class WC_Install {
}
$return_url = esc_url_raw( wp_unslash( $return_url ) );
- wp_safe_redirect( $return_url ); // WPCS: input var ok.
+ wp_safe_redirect( $return_url );
+ // WPCS: input var ok.
exit;
}
}
diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php
index 3648be5bb37..a06659c8487 100644
--- a/plugins/woocommerce/includes/wc-update-functions.php
+++ b/plugins/woocommerce/includes/wc-update-functions.php
@@ -3463,39 +3463,62 @@ function wc_update_1080_migrate_analytics_import_option(): void {
}
/**
- * Slim the `meta_key_value` index on `wc_orders_meta` by removing the `meta_value` column.
- *
- * The original composite index `(meta_key(100), meta_value(82))` overlaps heavily with
- * `order_id_meta_key_meta_value` and the `meta_value` prefix adds significant storage
- * overhead with negligible selectivity benefit. All core queries that use this index
- * filter primarily by `meta_key`.
+ * Reshape the `meta_key_value` index on `wc_orders_meta` to `(meta_key(50), meta_value(20))`.
+ *
+ * This is labeled as a 10.8.0-2 migration because originally there was a 10.8 migration that modified the
+ * meta_key_value index to include only the meta_key column (see https://github.com/woocommerce/woocommerce/pull/63897),
+ * but later it was discovered that this caused performance issues (as commented in the same pull request),
+ * so this new migration was added. However sites where 10.8 beta was installed will already have run
+ * the original migration (the one that removed meta_value from the index) and will have their db
+ * version updated to 10.8, so this new migration wouldn't run. Hence the 10.8.0-2 marker, which
+ * sorts between 10.8.0 and 10.8.1 via version_compare() and leaves the 10.8.1 slot free for any
+ * future patch-release migrations. WooCommerce 10.8.0 beta 2 had a migration labeled as 10.8.0-1 already,
+ * hence the need to go with 10.8.0-2 for this one.
+ *
+ * Handles two starting states transparently:
+ * - Pre-10.8 sites: the index is the original `(meta_key(100), meta_value(82))`.
+ * - 10.8 beta sites: the index has been changed to `(meta_key(100))` only.
+ *
+ * The new prefixes are sized from profiling a production-scale table: all meta_keys fit
+ * within 47 chars (so `meta_key(50)` covers every existing key) and a 20-byte `meta_value`
+ * prefix preserves full selectivity for the affected query patterns, keeping most of the
+ * storage benefit of the slim attempt.
*
* @since 10.8.0
*
* @return void
*/
-function wc_update_1080_slim_orders_meta_key_index(): void {
+function wc_update_10802_restore_orders_meta_key_value_index(): void {
global $wpdb;
$table_name = $wpdb->prefix . 'wc_orders_meta';
$index_name = 'meta_key_value';
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
- $index = $wpdb->get_row(
+ $columns = $wpdb->get_results(
$wpdb->prepare(
- 'SHOW INDEX FROM ' . $table_name . ' WHERE Key_name = %s AND Column_name = %s',
- $index_name,
- 'meta_value'
+ 'SHOW INDEX FROM ' . $table_name . ' WHERE Key_name = %s',
+ $index_name
)
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
- if ( is_null( $index ) ) {
+ $already_correct =
+ is_array( $columns ) && 2 === count( $columns ) &&
+ 'meta_key' === $columns[0]->Column_name && 50 === (int) $columns[0]->Sub_part &&
+ 'meta_value' === $columns[1]->Column_name && 20 === (int) $columns[1]->Sub_part;
+
+ if ( $already_correct ) {
return;
}
- // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
- $wpdb->query( "ALTER TABLE {$table_name} DROP INDEX {$index_name}, ADD INDEX {$index_name} (meta_key(100))" );
+ if ( empty( $columns ) ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( "ALTER TABLE {$table_name} ADD INDEX {$index_name} (meta_key(50), meta_value(20))" );
+ } else {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( "ALTER TABLE {$table_name} DROP INDEX {$index_name}, ADD INDEX {$index_name} (meta_key(50), meta_value(20))" );
+ }
}
/**
diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
index ee7ef61dd38..f32af8c65ac 100644
--- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
+++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
@@ -3395,7 +3395,7 @@ CREATE TABLE $meta_table (
order_id bigint(20) unsigned null,
meta_key varchar(255),
meta_value text null,
- KEY meta_key_value (meta_key(100)),
+ KEY meta_key_value (meta_key(50), meta_value(20)),
KEY order_id_meta_key_meta_value (order_id, meta_key(100), meta_value($composite_meta_value_index_length))
) $collate;
";