Add experimental support for FTS indexes in HPOS. (#46130)
* Added key for sku * Add experimental support for FTS indexes in HPOS. Additionally, revert existing HPOS search queries to use post like structure. * Add unit tests * Escape the error messages as per new phpcs rules. * Fix query with index + added test. * remove uninteded change. * Unit test fixes. * Remove unit test since the commit command breaks other tests. Use message API instead of notices with defensive checks.
This commit is contained in:
parent
3d8e20c62c
commit
94e438f524
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: performance
|
||||
|
||||
Add experimental support for FTS indexes in HPOS. Additionally, revert existing HPOS search queries to use post like structure.
|
|
@ -10,6 +10,8 @@ use Automattic\WooCommerce\Caches\OrderCacheController;
|
|||
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
|
||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use Automattic\WooCommerce\Utilities\PluginUtil;
|
||||
use WC_Admin_Settings;
|
||||
|
||||
|
@ -46,6 +48,12 @@ class CustomOrdersTableController {
|
|||
|
||||
public const DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL = 'READ UNCOMMITTED';
|
||||
|
||||
public const HPOS_FTS_INDEX_OPTION = 'woocommerce_hpos_fts_index_enabled';
|
||||
|
||||
public const HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION = 'woocommerce_hpos_address_fts_index_created';
|
||||
|
||||
public const HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION = 'woocommerce_hpos_order_item_fts_index_created';
|
||||
|
||||
/**
|
||||
* The data store object to use.
|
||||
*
|
||||
|
@ -109,6 +117,13 @@ class CustomOrdersTableController {
|
|||
*/
|
||||
private $plugin_util;
|
||||
|
||||
/**
|
||||
* The db util object to use.
|
||||
*
|
||||
* @var DatabaseUtil;
|
||||
*/
|
||||
private $db_util;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
|
@ -124,6 +139,7 @@ class CustomOrdersTableController {
|
|||
self::add_filter( 'woocommerce_order-refund_data_store', array( $this, 'get_refunds_data_store' ), 999, 1 );
|
||||
self::add_filter( 'woocommerce_debug_tools', array( $this, 'add_hpos_tools' ), 999 );
|
||||
self::add_filter( 'updated_option', array( $this, 'process_updated_option' ), 999, 3 );
|
||||
self::add_filter( 'updated_option', array( $this, 'process_updated_option_fts_index' ), 999, 3 );
|
||||
self::add_filter( 'pre_update_option', array( $this, 'process_pre_update_option' ), 999, 3 );
|
||||
self::add_action( 'woocommerce_after_register_post_type', array( $this, 'register_post_type_for_order_placeholders' ), 10, 0 );
|
||||
self::add_action( 'woocommerce_sections_advanced', array( $this, 'sync_now' ) );
|
||||
|
@ -144,6 +160,7 @@ class CustomOrdersTableController {
|
|||
* @param OrderCache $order_cache The order cache engine to use.
|
||||
* @param OrderCacheController $order_cache_controller The order cache controller to use.
|
||||
* @param PluginUtil $plugin_util The plugin util to use.
|
||||
* @param DatabaseUtil $db_util The database util to use.
|
||||
*/
|
||||
final public function init(
|
||||
OrdersTableDataStore $data_store,
|
||||
|
@ -154,7 +171,8 @@ class CustomOrdersTableController {
|
|||
FeaturesController $features_controller,
|
||||
OrderCache $order_cache,
|
||||
OrderCacheController $order_cache_controller,
|
||||
PluginUtil $plugin_util
|
||||
PluginUtil $plugin_util,
|
||||
DatabaseUtil $db_util
|
||||
) {
|
||||
$this->data_store = $data_store;
|
||||
$this->data_synchronizer = $data_synchronizer;
|
||||
|
@ -165,6 +183,7 @@ class CustomOrdersTableController {
|
|||
$this->order_cache = $order_cache;
|
||||
$this->order_cache_controller = $order_cache_controller;
|
||||
$this->plugin_util = $plugin_util;
|
||||
$this->db_util = $db_util;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,6 +310,61 @@ class CustomOrdersTableController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process option that enables FTS index on orders table. Tries to create an FTS index when option is enabled.
|
||||
*
|
||||
* @param string $option Option name.
|
||||
* @param string $old_value Old value of the option.
|
||||
* @param string $value New value of the option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function process_updated_option_fts_index( $option, $old_value, $value ) {
|
||||
if ( self::HPOS_FTS_INDEX_OPTION !== $option ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'yes' !== $value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->custom_orders_table_usage_is_enabled() ) {
|
||||
update_option( self::HPOS_FTS_INDEX_OPTION, 'no', true );
|
||||
if ( class_exists( 'WC_Admin_Settings' ) ) {
|
||||
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on orders table. This feature is only available when High-performance order storage is enabled.', 'woocommerce' ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->db_util->fts_index_on_order_address_table_exists() ) {
|
||||
$this->db_util->create_fts_index_order_address_table();
|
||||
}
|
||||
|
||||
// Check again to see if index was actually created.
|
||||
if ( $this->db_util->fts_index_on_order_address_table_exists() ) {
|
||||
update_option( self::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION, 'yes', true );
|
||||
} else {
|
||||
update_option( self::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION, 'no', true );
|
||||
if ( class_exists( 'WC_Admin_Settings ' ) ) {
|
||||
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on address table', 'woocommerce' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $this->db_util->fts_index_on_order_item_table_exists() ) {
|
||||
$this->db_util->create_fts_index_order_item_table();
|
||||
}
|
||||
|
||||
// Check again to see if index was actually created.
|
||||
if ( $this->db_util->fts_index_on_order_item_table_exists() ) {
|
||||
update_option( self::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION, 'yes', true );
|
||||
} else {
|
||||
update_option( self::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION, 'no', true );
|
||||
if ( class_exists( 'WC_Admin_Settings ' ) ) {
|
||||
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on order item table', 'woocommerce' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the setting pre-update hook.
|
||||
* We use it to verify that authoritative orders table switch doesn't happen while sync is pending.
|
||||
|
|
|
@ -51,7 +51,7 @@ class OrdersTableSearchQuery {
|
|||
*
|
||||
* @return array Array of search filters.
|
||||
*/
|
||||
private function sanitize_search_filters( string $search_filter ) : array {
|
||||
private function sanitize_search_filters( string $search_filter ): array {
|
||||
$core_filters = array(
|
||||
'order_id',
|
||||
'transaction_id',
|
||||
|
@ -112,15 +112,7 @@ class OrdersTableSearchQuery {
|
|||
*
|
||||
* @return string JOIN clause.
|
||||
*/
|
||||
private function generate_join_for_search_filter( $search_filter ) : string {
|
||||
if ( 'products' === $search_filter ) {
|
||||
$orders_table = $this->query->get_table_name( 'orders' );
|
||||
$items_table = $this->query->get_table_name( 'items' );
|
||||
return "
|
||||
LEFT JOIN $items_table AS search_query_items ON search_query_items.order_id = $orders_table.id
|
||||
";
|
||||
}
|
||||
|
||||
private function generate_join_for_search_filter( $search_filter ): string {
|
||||
/**
|
||||
* Filter to support adding a custom order search filter.
|
||||
* Provide a JOIN clause for a new search filter. This should be used along with `woocommerce_hpos_admin_search_filters`
|
||||
|
@ -181,7 +173,7 @@ class OrdersTableSearchQuery {
|
|||
*
|
||||
* @return string WHERE clause.
|
||||
*/
|
||||
private function generate_where_for_search_filter( string $search_filter ) : string {
|
||||
private function generate_where_for_search_filter( string $search_filter ): string {
|
||||
global $wpdb;
|
||||
|
||||
$order_table = $this->query->get_table_name( 'orders' );
|
||||
|
@ -208,15 +200,11 @@ class OrdersTableSearchQuery {
|
|||
}
|
||||
|
||||
if ( 'products' === $search_filter ) {
|
||||
return $wpdb->prepare(
|
||||
'search_query_items.order_item_name LIKE %s',
|
||||
'%' . $wpdb->esc_like( $this->search_term ) . '%'
|
||||
);
|
||||
return $this->get_where_for_products();
|
||||
}
|
||||
|
||||
if ( 'customers' === $search_filter ) {
|
||||
$meta_sub_query = $this->generate_where_for_meta_table();
|
||||
return "`$order_table`.id IN ( $meta_sub_query ) ";
|
||||
return $this->get_where_for_customers();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,6 +231,74 @@ class OrdersTableSearchQuery {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate the WHERE clause for products search. Uses FTS when available.
|
||||
*
|
||||
* @return string|null WHERE clause for products search.
|
||||
*/
|
||||
private function get_where_for_products() {
|
||||
global $wpdb;
|
||||
$items_table = $this->query->get_table_name( 'items' );
|
||||
$orders_table = $this->query->get_table_name( 'orders' );
|
||||
$fts_enabled = get_option( CustomOrdersTableController::HPOS_FTS_INDEX_OPTION ) === 'yes' && get_option( CustomOrdersTableController::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION ) === 'yes';
|
||||
|
||||
if ( $fts_enabled ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orders_table and $items_table are hardcoded.
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
$orders_table.id in (
|
||||
SELECT order_id FROM $items_table search_query_items WHERE
|
||||
MATCH ( search_query_items.order_item_name ) AGAINST ( %s IN BOOLEAN MODE )
|
||||
)
|
||||
",
|
||||
'*' . $wpdb->esc_like( $this->search_term ) . '*'
|
||||
);
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orders_table and $items_table are hardcoded.
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
$orders_table.id in (
|
||||
SELECT order_id FROM $items_table search_query_items WHERE
|
||||
search_query_items.order_item_name LIKE %s
|
||||
)
|
||||
",
|
||||
'%' . $wpdb->esc_like( $this->search_term ) . '%'
|
||||
);
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate the WHERE clause for customers search. Uses FTS when available.
|
||||
*
|
||||
* @return string|null WHERE clause for customers search.
|
||||
*/
|
||||
private function get_where_for_customers() {
|
||||
global $wpdb;
|
||||
$order_table = $this->query->get_table_name( 'orders' );
|
||||
$address_table = $this->query->get_table_name( 'addresses' );
|
||||
|
||||
$fts_enabled = get_option( CustomOrdersTableController::HPOS_FTS_INDEX_OPTION ) === 'yes' && get_option( CustomOrdersTableController::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION ) === 'yes';
|
||||
|
||||
if ( $fts_enabled ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_table and $address_table are hardcoded.
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
$order_table.id IN (
|
||||
SELECT order_id FROM $address_table WHERE
|
||||
MATCH( $address_table.first_name, $address_table.last_name, $address_table.company, $address_table.address_1, $address_table.address_2, $address_table.city, $address_table.state, $address_table.postcode, $address_table.country, $address_table.email ) AGAINST ( %s IN BOOLEAN MODE )
|
||||
)
|
||||
",
|
||||
'*' . $wpdb->esc_like( $this->search_term ) . '*'
|
||||
);
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
$meta_sub_query = $this->generate_where_for_meta_table();
|
||||
return "`$order_table`.id IN ( $meta_sub_query ) ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates where clause for meta table.
|
||||
*
|
||||
|
|
|
@ -76,6 +76,7 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
|
|||
OrderCache::class,
|
||||
OrderCacheController::class,
|
||||
PluginUtil::class,
|
||||
DatabaseUtil::class,
|
||||
)
|
||||
);
|
||||
$this->share( OrderCache::class );
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Automattic\WooCommerce\Internal\Features;
|
|||
|
||||
use Automattic\WooCommerce\Internal\Admin\Analytics;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Init;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||
|
@ -232,6 +233,17 @@ class FeaturesController {
|
|||
'is_legacy' => true,
|
||||
'is_experimental' => false,
|
||||
),
|
||||
'hpos_fts_indexes' => array(
|
||||
'name' => __( 'HPOS Full text search indexes', 'woocommerce' ),
|
||||
'description' => __(
|
||||
'Create and use full text search indexes for orders. This feature only works with high-performance order storage.',
|
||||
'woocommerce'
|
||||
),
|
||||
'is_experimental' => true,
|
||||
'enabled_by_default' => false,
|
||||
'is_legacy' => true,
|
||||
'option_key' => CustomOrdersTableController::HPOS_FTS_INDEX_OPTION,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $legacy_features as $slug => $definition ) {
|
||||
|
@ -392,7 +404,7 @@ class FeaturesController {
|
|||
ArrayUtil::ensure_key_is_array( $this->compatibility_info_by_plugin[ $plugin_name ], $opposite_key );
|
||||
|
||||
if ( in_array( $feature_id, $this->compatibility_info_by_plugin[ $plugin_name ][ $opposite_key ], true ) ) {
|
||||
throw new \Exception( "Plugin $plugin_name is trying to declare itself as $key with the '$feature_id' feature, but it already declared itself as $opposite_key" );
|
||||
throw new \Exception( esc_html( "Plugin $plugin_name is trying to declare itself as $key with the '$feature_id' feature, but it already declared itself as $opposite_key" ) );
|
||||
}
|
||||
|
||||
if ( ! in_array( $feature_id, $this->compatibility_info_by_plugin[ $plugin_name ][ $key ], true ) ) {
|
||||
|
@ -487,14 +499,15 @@ class FeaturesController {
|
|||
/**
|
||||
* Check if the 'woocommerce_init' has run or is running, do a 'wc_doing_it_wrong' if not.
|
||||
*
|
||||
* @param string|null $function Name of the invoking method, if not null, 'wc_doing_it_wrong' will be invoked if 'woocommerce_init' has not run and is not running.
|
||||
* @param string|null $function_name Name of the invoking method, if not null, 'wc_doing_it_wrong' will be invoked if 'woocommerce_init' has not run and is not running.
|
||||
*
|
||||
* @return bool True if 'woocommerce_init' has run or is running, false otherwise.
|
||||
*/
|
||||
private function verify_did_woocommerce_init( string $function = null ): bool {
|
||||
private function verify_did_woocommerce_init( string $function_name = null ): bool {
|
||||
if ( ! $this->proxy->call_function( 'did_action', 'woocommerce_init' ) &&
|
||||
! $this->proxy->call_function( 'doing_action', 'woocommerce_init' ) ) {
|
||||
if ( ! is_null( $function ) ) {
|
||||
$class_and_method = ( new \ReflectionClass( $this ) )->getShortName() . '::' . $function;
|
||||
if ( ! is_null( $function_name ) ) {
|
||||
$class_and_method = ( new \ReflectionClass( $this ) )->getShortName() . '::' . $function_name;
|
||||
/* translators: 1: class::method 2: plugins_loaded */
|
||||
$this->proxy->call_function( 'wc_doing_it_wrong', $class_and_method, sprintf( __( '%1$s should not be called before the %2$s action.', 'woocommerce' ), $class_and_method, 'woocommerce_init' ), '7.0' );
|
||||
}
|
||||
|
@ -869,41 +882,41 @@ class FeaturesController {
|
|||
* if we are in the plugins page and the query string of the current request
|
||||
* looks like '?plugin_status=incompatible_with_feature&feature_id=<feature id>'.
|
||||
*
|
||||
* @param array $list The original list of plugins.
|
||||
* @param array $plugin_list The original list of plugins.
|
||||
*/
|
||||
private function filter_plugins_list( $list ): array {
|
||||
private function filter_plugins_list( $plugin_list ): array {
|
||||
if ( ! $this->verify_did_woocommerce_init() ) {
|
||||
return $list;
|
||||
return $plugin_list;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
|
||||
if ( ! function_exists( 'get_current_screen' ) || get_current_screen() && 'plugins' !== get_current_screen()->id || 'incompatible_with_feature' !== ArrayUtil::get_value_or_default( $_GET, 'plugin_status' ) ) {
|
||||
return $list;
|
||||
return $plugin_list;
|
||||
}
|
||||
|
||||
$feature_id = $_GET['feature_id'] ?? 'all';
|
||||
if ( 'all' !== $feature_id && ! $this->feature_exists( $feature_id ) ) {
|
||||
return $list;
|
||||
return $plugin_list;
|
||||
}
|
||||
|
||||
return $this->get_incompatible_plugins( $feature_id, $list );
|
||||
return $this->get_incompatible_plugins( $feature_id, $plugin_list );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of plugins incompatible with a given feature.
|
||||
*
|
||||
* @param string $feature_id ID of the feature. Can also be `all` to denote all features.
|
||||
* @param array $list List of plugins to filter.
|
||||
* @param array $plugin_list List of plugins to filter.
|
||||
*
|
||||
* @return array List of plugins incompatible with the given feature.
|
||||
*/
|
||||
private function get_incompatible_plugins( $feature_id, $list ) {
|
||||
private function get_incompatible_plugins( $feature_id, $plugin_list ) {
|
||||
$incompatibles = array();
|
||||
|
||||
$list = array_diff_key( $list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
|
||||
$plugin_list = array_diff_key( $plugin_list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
|
||||
|
||||
// phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
|
||||
foreach ( array_keys( $list ) as $plugin_name ) {
|
||||
foreach ( array_keys( $plugin_list ) as $plugin_name ) {
|
||||
if ( ! $this->plugin_util->is_woocommerce_aware_plugin( $plugin_name ) || ! $this->proxy->call_function( 'is_plugin_active', $plugin_name ) ) {
|
||||
continue;
|
||||
}
|
||||
|
@ -921,7 +934,7 @@ class FeaturesController {
|
|||
}
|
||||
}
|
||||
|
||||
return array_intersect_key( $list, array_flip( $incompatibles ) );
|
||||
return array_intersect_key( $plugin_list, array_flip( $incompatibles ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -170,7 +170,7 @@ class DatabaseUtil {
|
|||
$value = $value ? ( new DateTime( "@{$value}" ) )->format( 'Y-m-d H:i:s' ) : null;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception( 'Invalid type received: ' . $type );
|
||||
throw new \Exception( esc_html( 'Invalid type received: ' . $type ) );
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
@ -194,7 +194,7 @@ class DatabaseUtil {
|
|||
);
|
||||
|
||||
if ( ! isset( $wpdb_placeholder_for_type[ $type ] ) ) {
|
||||
throw new \Exception( 'Invalid column type: ' . $type );
|
||||
throw new \Exception( esc_html( 'Invalid column type: ' . $type ) );
|
||||
}
|
||||
|
||||
return $wpdb_placeholder_for_type[ $type ];
|
||||
|
@ -231,7 +231,7 @@ class DatabaseUtil {
|
|||
*
|
||||
* @return int Returns the value of DB's ON DUPLICATE KEY UPDATE clause.
|
||||
*/
|
||||
public function insert_on_duplicate_key_update( $table_name, $data, $format ) : int {
|
||||
public function insert_on_duplicate_key_update( $table_name, $data, $format ): int {
|
||||
global $wpdb;
|
||||
if ( empty( $data ) ) {
|
||||
return 0;
|
||||
|
@ -249,7 +249,7 @@ class DatabaseUtil {
|
|||
$values[] = $value;
|
||||
$value_format[] = $format[ $index ];
|
||||
}
|
||||
$index++;
|
||||
++$index;
|
||||
}
|
||||
$column_clause = '`' . implode( '`, `', $columns ) . '`';
|
||||
$value_format_clause = implode( ', ', $value_format );
|
||||
|
@ -273,7 +273,7 @@ $on_duplicate_clause
|
|||
*
|
||||
* @return int Max index length.
|
||||
*/
|
||||
public function get_max_index_length() : int {
|
||||
public function get_max_index_length(): int {
|
||||
/**
|
||||
* Filters the maximum index length in the database.
|
||||
*
|
||||
|
@ -291,4 +291,52 @@ $on_duplicate_clause
|
|||
// Index length cannot be more than 768, which is 3078 bytes in utf8mb4 and max allowed by InnoDB engine.
|
||||
return min( absint( $max_index_length ), 767 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fulltext index on order address table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create_fts_index_order_address_table(): void {
|
||||
global $wpdb;
|
||||
$address_table = $wpdb->prefix . 'wc_order_addresses';
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $address_table is hardcoded.
|
||||
$wpdb->query( "CREATE FULLTEXT INDEX order_addresses_fts ON $address_table (first_name, last_name, company, address_1, address_2, city, state, postcode, country, email)" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if fulltext index with key `order_addresses_fts` on order address table exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fts_index_on_order_address_table_exists(): bool {
|
||||
global $wpdb;
|
||||
$address_table = $wpdb->prefix . 'wc_order_addresses';
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $address_table is hardcoded.
|
||||
return ! empty( $wpdb->get_results( "SHOW INDEX FROM $address_table WHERE Key_name = 'order_addresses_fts'" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fulltext index on order item table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create_fts_index_order_item_table(): void {
|
||||
global $wpdb;
|
||||
$order_item_table = $wpdb->prefix . 'woocommerce_order_items';
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_item_table is hardcoded.
|
||||
$wpdb->query( "CREATE FULLTEXT INDEX order_item_fts ON $order_item_table (order_item_name)" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if fulltext index with key `order_item_fts` on order item table exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fts_index_on_order_item_table_exists(): bool {
|
||||
global $wpdb;
|
||||
$order_item_table = $wpdb->prefix . 'woocommerce_order_items';
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_item_table is hardcoded.
|
||||
return ! empty( $wpdb->get_results( "SHOW INDEX FROM $order_item_table WHERE Key_name = 'order_item_fts'" ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery;
|
||||
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
|
||||
use Automattic\WooCommerce\RestApi\UnitTests\HPOSToggleTrait;
|
||||
|
@ -24,8 +25,8 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
add_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
|
||||
$this->setup_cot();
|
||||
$this->cot_state = OrderUtil::custom_orders_table_usage_is_enabled();
|
||||
$this->setup_cot();
|
||||
$this->toggle_cot_feature_and_usage( true );
|
||||
}
|
||||
|
||||
|
@ -148,7 +149,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
|
||||
$filters_called = 0;
|
||||
$filter_callback = function ( $arg ) use ( &$filters_called ) {
|
||||
$filters_called++;
|
||||
++$filters_called;
|
||||
return $arg;
|
||||
};
|
||||
|
||||
|
@ -193,7 +194,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
$this->assertCount( 2, wc_get_orders( array() ) );
|
||||
|
||||
// Force a query that returns nothing.
|
||||
$filter_callback = function( $clauses ) {
|
||||
$filter_callback = function ( $clauses ) {
|
||||
$clauses['where'] .= ' AND 1=0 ';
|
||||
return $clauses;
|
||||
};
|
||||
|
@ -203,7 +204,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
remove_all_filters( 'woocommerce_orders_table_query_clauses' );
|
||||
|
||||
// Force a query that sorts orders by id ASC (as opposed to the default date DESC) if a query arg is present.
|
||||
$filter_callback = function( $clauses, $query, $query_args ) {
|
||||
$filter_callback = function ( $clauses, $query, $query_args ) {
|
||||
if ( ! empty( $query_args['my_custom_arg'] ) ) {
|
||||
$clauses['orderby'] = $query->get_table_name( 'orders' ) . '.id ASC';
|
||||
}
|
||||
|
@ -254,7 +255,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
$this->assertEquals( 2, $query->found_orders );
|
||||
$this->assertEquals( 0, $query->max_num_pages );
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$callback = function ( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$this->assertNull( $result );
|
||||
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
|
||||
$this->assertStringContainsString( 'SELECT ', $sql );
|
||||
|
@ -295,7 +296,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
$this->assertEquals( 2, $query->found_orders );
|
||||
$this->assertEquals( 0, $query->max_num_pages );
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$callback = function ( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$this->assertNull( $result );
|
||||
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
|
||||
$this->assertStringContainsString( 'SELECT ', $sql );
|
||||
|
@ -330,7 +331,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$callback = function () use ( $order1 ) {
|
||||
// Do not return found_orders or max_num_pages so as to provoke a warning.
|
||||
$order_ids = array( $order1->get_id() );
|
||||
return array( $order_ids, 10, null );
|
||||
|
@ -385,7 +386,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$callback = function () use ( $order1 ) {
|
||||
// Just return null.
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -402,7 +402,7 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
|
|||
$this->simulate_inside_before_woocommerce_init_hook();
|
||||
|
||||
$this->ExpectException( \Exception::class );
|
||||
$this->ExpectExceptionMessage( "Plugin the_plugin is trying to declare itself as incompatible with the 'mature1' feature, but it already declared itself as compatible" );
|
||||
$this->ExpectExceptionMessage( esc_html( "Plugin the_plugin is trying to declare itself as incompatible with the 'mature1' feature, but it already declared itself as compatible" ) );
|
||||
|
||||
$this->sut->declare_compatibility( 'mature1', 'the_plugin', true );
|
||||
$this->sut->declare_compatibility( 'mature1', 'the_plugin', false );
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Tests for the DatabaseUtil utility.
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
|
||||
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
||||
|
||||
/**
|
||||
|
@ -57,4 +58,32 @@ class DatabaseUtilTest extends WC_Unit_Test_Case {
|
|||
$this->sut->get_index_columns( $wpdb->prefix . 'wc_product_meta_lookup', 'invalid_index_name' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test Test that we are able to create FTS index on order address table.
|
||||
*/
|
||||
public function test_create_fts_index_order_address_table() {
|
||||
$db = wc_get_container()->get( DataSynchronizer::class );
|
||||
// Remove the Test Suite’s use of temporary tables https://wordpress.stackexchange.com/a/220308.
|
||||
remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
|
||||
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||
$db->create_database_tables();
|
||||
// Add back removed filter.
|
||||
add_filter( 'query', array( $this, '_create_temporary_tables' ) );
|
||||
add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||
if ( ! $this->sut->fts_index_on_order_item_table_exists() ) {
|
||||
$this->sut->create_fts_index_order_address_table();
|
||||
}
|
||||
$this->assertTrue( $this->sut->fts_index_on_order_address_table_exists() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @test Test that we are able to create FTS index on order item table.
|
||||
*/
|
||||
public function test_create_fts_index_order_item_table() {
|
||||
if ( ! $this->sut->fts_index_on_order_item_table_exists() ) {
|
||||
$this->sut->create_fts_index_order_item_table();
|
||||
}
|
||||
$this->assertTrue( $this->sut->fts_index_on_order_item_table_exists() );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue