* used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters. * * Additionally, MyISAM engine also limits the index size to 1000 bytes. We add this filter so that interested folks on InnoDB engine can increase the size till allowed 3071 bytes. * * @param int $max_index_length Maximum index length. Default 191. * * @since 8.0.0 */ $max_index_length = apply_filters( 'woocommerce_database_max_index_length', 191 ); // 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, phone)" ); } /** * Helper method to drop the fulltext index on order address table. * * @since 9.4.0 * * @return void */ public function drop_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( "ALTER TABLE $address_table DROP INDEX order_addresses_fts;" ); } /** * Sanitize FTS Search params to remove relevancy operators for performance, and add partial matches. Useful when the sorting is already happening based on some other conditions, so relevancy calculation is not needed. * * @since 9.4.0 * * @param string $param Search term. * * @return string Sanitized search term. */ public function sanitise_boolean_fts_search_term( string $param ): string { // Remove any operator to prevent incorrect query and fatals, such as search starting with `++`. We can allow this in the future if we have proper validation for FTS search operators. // Space is allowed to provide multiple words. $sanitized_param = preg_replace( '/[^\p{L}\p{N}_]+/u', ' ', $param ); if ( $sanitized_param !== $param ) { $param = str_replace( '"', '', $param ); return '"' . $param . '"'; } // Split the search phrase into words so that we can add operators when needed. $words = explode( ' ', $param ); $sanitized_words = array(); foreach ( $words as $word ) { // Add `*` as suffix to every term so that partial matches happens. $word = $word . '*'; $sanitized_words[] = $word; } return implode( ' ', $sanitized_words ); } /** * 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'" ) ); } } * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters. * * Additionally, MyISAM engine also limits the index size to 1000 bytes. We add this filter so that interested folks on InnoDB engine can increase the size till allowed 3071 bytes. * * @param int $max_index_length Maximum index length. Default 191. * * @since 8.0.0 */ $max_index_length = apply_filters( 'woocommerce_database_max_index_length', 191 ); // 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, phone)" ); } /** * Helper method to drop the fulltext index on order address table. * * @since 9.4.0 * * @return void */ public function drop_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( "ALTER TABLE $address_table DROP INDEX order_addresses_fts;" ); } /** * Sanitize FTS Search params to remove relevancy operators for performance, and add partial matches. Useful when the sorting is already happening based on some other conditions, so relevancy calculation is not needed. * * @since 9.4.0 * * @param string $param Search term. * * @return string Sanitized search term. */ public function sanitise_boolean_fts_search_term( string $param ): string { // Remove any operator to prevent incorrect query and fatals, such as search starting with `++`. We can allow this in the future if we have proper validation for FTS search operators. // Space is allowed to provide multiple words. $sanitized_param = preg_replace( '/[^\p{L}\p{N}_]+/u', ' ', $param ); if ( $sanitized_param !== $param ) { $param = str_replace( '"', '', $param ); return '"' . $param . '"'; } // Split the search phrase into words so that we can add operators when needed. $words = explode( ' ', $param ); $sanitized_words = array(); foreach ( $words as $word ) { // Add `*` as suffix to every term so that partial matches happens. $word = $word . '*'; $sanitized_words[] = $word; } return implode( ' ', $sanitized_words ); } /** * 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'" ) ); } }