Commit e8732426f5b for woocommerce

commit e8732426f5bf8ab7679437dc0a1162ec363d868e
Author: Taha Paksu <3295+tpaksu@users.noreply.github.com>
Date:   Fri Oct 23 18:25:18 2026 +0300

    Filter the orders list by shipping provider with a subquery instead of a materialized ID list (#69938)

    * [WOOPLUG-6444] Filter orders by shipping provider with a subquery instead of a materialized ID list

    * Add changefile(s) from automation for the following project(s): woocommerce

    * [WOOPLUG-6444] Fall back to an id list when the orders query skips the clause filter

    * [WOOPLUG-6444] Update the new docblock version tags to 11.4.0

    * [WOOPLUG-6444] Drop the redundant unset in the orders clause filter

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/69938-wooplug-6444-shipping-provider-filter b/plugins/woocommerce/changelog/69938-wooplug-6444-shipping-provider-filter
new file mode 100644
index 00000000000..1e1189c8600
--- /dev/null
+++ b/plugins/woocommerce/changelog/69938-wooplug-6444-shipping-provider-filter
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Improve the orders list performance when filtering by shipping provider on stores with many orders.
diff --git a/plugins/woocommerce/src/Admin/Features/Fulfillments/FulfillmentsRenderer.php b/plugins/woocommerce/src/Admin/Features/Fulfillments/FulfillmentsRenderer.php
index e014468bd71..c72ebd50b98 100644
--- a/plugins/woocommerce/src/Admin/Features/Fulfillments/FulfillmentsRenderer.php
+++ b/plugins/woocommerce/src/Admin/Features/Fulfillments/FulfillmentsRenderer.php
@@ -8,6 +8,7 @@ declare( strict_types=1 );
 namespace Automattic\WooCommerce\Admin\Features\Fulfillments;

 use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
+use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
 use Automattic\WooCommerce\Utilities\OrderUtil;
 use WC_Order;

@@ -16,6 +17,13 @@ use WC_Order;
  */
 class FulfillmentsRenderer {

+	/**
+	 * Query argument that carries the selected shipping provider through to the clause filters.
+	 *
+	 * @var string
+	 */
+	private const SHIPPING_PROVIDER_FILTER_ARG = 'fulfillment_shipping_provider';
+
 	/**
 	 * Fulfillments cache, that holds the fulfillments for each order to eliminate
 	 * fetching fulfillment records of an order on each column render.
@@ -67,6 +75,7 @@ class FulfillmentsRenderer {
 			add_action( 'woocommerce_order_list_table_restrict_manage_orders', array( $this, 'render_shipping_provider_filter' ) );
 			add_filter( 'woocommerce_order_query_args', array( $this, 'filter_orders_list_table_query' ), 10, 1 );
 			add_filter( 'woocommerce_order_list_table_prepare_items_query_args', array( $this, 'filter_orders_by_shipping_provider' ), 10, 1 );
+			add_filter( 'woocommerce_orders_table_query_clauses', array( $this, 'filter_orders_table_clauses_by_shipping_provider' ), 10, 3 );
 		} else {
 			// For legacy orders table, we need to add the bulk actions to the legacy orders table.
 			add_filter( 'bulk_actions-edit-shop_order', array( $this, 'define_fulfillment_bulk_actions' ) );
@@ -76,6 +85,7 @@ class FulfillmentsRenderer {
 			add_action( 'restrict_manage_posts', array( $this, 'render_shipping_provider_filter_legacy' ) );
 			add_action( 'pre_get_posts', array( $this, 'filter_legacy_orders_list_query' ) );
 			add_action( 'pre_get_posts', array( $this, 'filter_legacy_orders_by_shipping_provider' ) );
+			add_filter( 'posts_clauses', array( $this, 'filter_legacy_orders_clauses_by_shipping_provider' ), 10, 2 );
 		}
 	}

@@ -606,24 +616,58 @@ class FulfillmentsRenderer {

 		// phpcs:ignore WordPress.Security.NonceVerification
 		$shipping_provider = sanitize_text_field( wp_unslash( $_GET['shipping_provider'] ) );
-		$order_ids         = $this->get_order_ids_by_shipping_provider( $shipping_provider );
-
-		if ( empty( $order_ids ) ) {
-			$args['post__in'] = array( 0 );
-		} elseif ( isset( $args['post__in'] ) && is_array( $args['post__in'] ) ) {
-			$args['post__in'] = array_intersect( $args['post__in'], $order_ids );
-			if ( empty( $args['post__in'] ) ) {
-				$args['post__in'] = array( 0 );
-			}
-		} else {
-			$args['post__in'] = $order_ids;
+
+		// A `woocommerce_hpos_pre_query` callback may answer the query from its own source, and
+		// `suppress_filters` skips the clause filter, so fall back to an id list the query carries.
+		if ( has_filter( 'woocommerce_hpos_pre_query' ) || ! empty( $args['suppress_filters'] ) ) {
+			// `p` and `post__in` are both remapped onto `id`, so narrow every restriction the
+			// query already carries rather than overwriting any of them.
+			$args['post__in'] = $this->restrict_ids_to_shipping_provider(
+				array( $args['post__in'] ?? null, $args['id'] ?? null, $args['p'] ?? null ),
+				$shipping_provider
+			);
+
+			return $args;
 		}

+		// Carry the provider through as a marker; the subquery is injected later in the clause filter
+		// so high-volume stores never materialize every matching order id into PHP.
+		$args[ self::SHIPPING_PROVIDER_FILTER_ARG ] = $shipping_provider;
+
 		return $args;
 	}

 	/**
-	 * Filter legacy orders by shipping provider.
+	 * Inject the shipping provider filter into the HPOS orders query clauses.
+	 *
+	 * Restricts the orders to those with a matching fulfillment via an `IN ( subquery )`
+	 * condition, instead of a materialized `post__in` id list.
+	 *
+	 * @since 11.4.0
+	 *
+	 * @param array  $clauses The query clauses (fields, join, where, etc).
+	 * @param object $query   The OrdersTableQuery instance.
+	 * @param array  $args    The query args.
+	 * @return array The modified clauses.
+	 */
+	public function filter_orders_table_clauses_by_shipping_provider( $clauses, $query, $args ): array {
+		$shipping_provider = $args[ self::SHIPPING_PROVIDER_FILTER_ARG ] ?? '';
+		if ( ! is_string( $shipping_provider ) || '' === $shipping_provider ) {
+			return $clauses;
+		}
+
+		$orders_table = OrdersTableDataStore::get_orders_table_name();
+		$subquery     = $this->get_shipping_provider_order_ids_subquery( $shipping_provider );
+
+		// The table name comes from the data store and the subquery is already prepared.
+		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+		$clauses['where'] = ( $clauses['where'] ?? '' ) . " AND {$orders_table}.id IN ( {$subquery} )";
+
+		return $clauses;
+	}
+
+	/**
+	 * Mark the legacy orders query with the selected shipping provider.
 	 *
 	 * @since 10.7.0
 	 *
@@ -645,31 +689,88 @@ class FulfillmentsRenderer {

 		// phpcs:ignore WordPress.Security.NonceVerification
 		$shipping_provider = sanitize_text_field( wp_unslash( $_GET['shipping_provider'] ) );
-		$order_ids         = $this->get_order_ids_by_shipping_provider( $shipping_provider );

-		if ( empty( $order_ids ) ) {
-			$query->set( 'post__in', array( 0 ) );
-		} else {
-			$existing = $query->get( 'post__in' );
-			if ( ! empty( $existing ) && is_array( $existing ) ) {
-				$order_ids = array_intersect( $existing, $order_ids );
-				if ( empty( $order_ids ) ) {
-					$order_ids = array( 0 );
-				}
+		// A `posts_pre_query` callback may answer the query from its own source, and `suppress_filters`
+		// skips the clause filter, so fall back to an id list the query carries.
+		if ( has_filter( 'posts_pre_query' ) || $query->get( 'suppress_filters' ) ) {
+			$query->set(
+				'post__in',
+				$this->restrict_ids_to_shipping_provider( array( $query->get( 'post__in' ) ), $shipping_provider )
+			);
+
+			return;
+		}
+
+		$query->set( self::SHIPPING_PROVIDER_FILTER_ARG, $shipping_provider );
+	}
+
+	/**
+	 * Inject the shipping provider filter into the legacy orders query clauses.
+	 *
+	 * @since 11.4.0
+	 *
+	 * @param array     $clauses The query clauses (join, where, etc).
+	 * @param \WP_Query $query   The WP_Query being filtered.
+	 * @return array The modified clauses.
+	 */
+	public function filter_legacy_orders_clauses_by_shipping_provider( $clauses, $query ): array {
+		$shipping_provider = $query->get( self::SHIPPING_PROVIDER_FILTER_ARG );
+		if ( ! is_string( $shipping_provider ) || '' === $shipping_provider ) {
+			return $clauses;
+		}
+
+		global $wpdb;
+		$subquery = $this->get_shipping_provider_order_ids_subquery( $shipping_provider );
+
+		// The subquery is already prepared and the table name comes from $wpdb.
+		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+		$clauses['where'] = ( $clauses['where'] ?? '' ) . " AND {$wpdb->posts}.ID IN ( {$subquery} )";
+
+		return $clauses;
+	}
+
+	/**
+	 * Narrow a set of order ids to those whose fulfillments use a shipping provider.
+	 *
+	 * Only used when the orders query can be answered without the SQL the clause filters build.
+	 *
+	 * @since 11.4.0
+	 *
+	 * @param array  $existing_restrictions The id restrictions the query already carries, if any.
+	 * @param string $shipping_provider     The shipping provider key, or the `__other__` sentinel.
+	 * @return array<int> The ids to restrict the query to, or `array( 0 )` when nothing matches.
+	 */
+	private function restrict_ids_to_shipping_provider( array $existing_restrictions, string $shipping_provider ): array {
+		global $wpdb;
+
+		// The subquery is prepared by get_shipping_provider_order_ids_subquery().
+		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+		$results   = $wpdb->get_col( $this->get_shipping_provider_order_ids_subquery( $shipping_provider ) );
+		$order_ids = array_map( 'absint', $results );
+
+		foreach ( $existing_restrictions as $restriction ) {
+			if ( empty( $restriction ) ) {
+				continue;
 			}
-			$query->set( 'post__in', $order_ids );
+
+			$order_ids = array_intersect( array_map( 'absint', (array) $restriction ), $order_ids );
 		}
+
+		// An empty list drops the restriction instead of applying it, so use an id that cannot match.
+		return empty( $order_ids ) ? array( 0 ) : array_values( $order_ids );
 	}

 	/**
-	 * Get order IDs that have fulfillments with a specific shipping provider.
+	 * Build the prepared subquery selecting order ids whose fulfillments use a shipping provider.
 	 *
-	 * @since 10.7.0
+	 * The returned SQL is fully prepared and safe to embed as an `IN ( ... )` subquery.
+	 *
+	 * @since 11.4.0
 	 *
-	 * @param string $shipping_provider The shipping provider key to filter by.
-	 * @return array Array of order IDs.
+	 * @param string $shipping_provider The shipping provider key, or the `__other__` sentinel.
+	 * @return string The prepared SELECT statement returning matching order ids.
 	 */
-	private function get_order_ids_by_shipping_provider( string $shipping_provider ): array {
+	private function get_shipping_provider_order_ids_subquery( string $shipping_provider ): string {
 		global $wpdb;

 		$fulfillments_table = $wpdb->prefix . 'wc_order_fulfillments';
@@ -681,58 +782,51 @@ class FulfillmentsRenderer {
 			$known_keys      = array_keys( $known_providers );

 			if ( empty( $known_keys ) ) {
-				$results = $wpdb->get_col(
-					$wpdb->prepare(
-						"SELECT DISTINCT f.entity_id
-						FROM {$fulfillments_table} f
-						INNER JOIN {$meta_table} m ON f.fulfillment_id = m.fulfillment_id
-						WHERE m.meta_key = %s
-						AND m.meta_value IS NOT NULL
-						AND m.meta_value NOT IN ( '', %s )
-						AND f.date_deleted IS NULL
-						AND m.date_deleted IS NULL",
-						'_shipment_provider',
-						wp_json_encode( '' )
-					)
-				);
-			} else {
-				// Exclude known providers and both empty string and the JSON-encoded one the data store writes.
-				$excluded     = array_merge(
-					array( '', wp_json_encode( '' ) ),
-					array_map( 'wp_json_encode', $known_keys )
-				);
-				$placeholders = implode( ',', array_fill( 0, count( $excluded ), '%s' ) );
-				$results      = $wpdb->get_col(
-					$wpdb->prepare(
-						"SELECT DISTINCT f.entity_id
-						FROM {$fulfillments_table} f
-						INNER JOIN {$meta_table} m ON f.fulfillment_id = m.fulfillment_id
-						WHERE m.meta_key = '_shipment_provider'
-						AND m.meta_value NOT IN ({$placeholders})
-						AND m.meta_value IS NOT NULL
-						AND f.date_deleted IS NULL
-						AND m.date_deleted IS NULL",
-						...$excluded
-					)
-				);
-			}
-		} else {
-			$results = $wpdb->get_col(
-				$wpdb->prepare(
+				return $wpdb->prepare(
 					"SELECT DISTINCT f.entity_id
 					FROM {$fulfillments_table} f
 					INNER JOIN {$meta_table} m ON f.fulfillment_id = m.fulfillment_id
-					WHERE m.meta_key = '_shipment_provider'
-					AND m.meta_value = %s
+					WHERE m.meta_key = %s
+					AND m.meta_value IS NOT NULL
+					AND m.meta_value NOT IN ( '', %s )
 					AND f.date_deleted IS NULL
 					AND m.date_deleted IS NULL",
-					wp_json_encode( $shipping_provider )
-				)
+					'_shipment_provider',
+					wp_json_encode( '' )
+				);
+			}
+
+			// Exclude known providers and both empty string and the JSON-encoded one the data store writes.
+			$excluded     = array_merge(
+				array( '', wp_json_encode( '' ) ),
+				array_map( 'wp_json_encode', $known_keys )
+			);
+			$placeholders = implode( ',', array_fill( 0, count( $excluded ), '%s' ) );
+
+			return $wpdb->prepare(
+				"SELECT DISTINCT f.entity_id
+				FROM {$fulfillments_table} f
+				INNER JOIN {$meta_table} m ON f.fulfillment_id = m.fulfillment_id
+				WHERE m.meta_key = '_shipment_provider'
+				AND m.meta_value NOT IN ({$placeholders})
+				AND m.meta_value IS NOT NULL
+				AND f.date_deleted IS NULL
+				AND m.date_deleted IS NULL",
+				...$excluded
 			);
 		}
-		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare

-		return array_map( 'absint', $results );
+		return $wpdb->prepare(
+			"SELECT DISTINCT f.entity_id
+			FROM {$fulfillments_table} f
+			INNER JOIN {$meta_table} m ON f.fulfillment_id = m.fulfillment_id
+			WHERE m.meta_key = '_shipment_provider'
+			AND m.meta_value = %s
+			AND f.date_deleted IS NULL
+			AND m.date_deleted IS NULL",
+			wp_json_encode( $shipping_provider )
+		);
+		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Admin/Features/Fulfillments/FulfillmentsRendererTest.php b/plugins/woocommerce/tests/php/src/Admin/Features/Fulfillments/FulfillmentsRendererTest.php
index e1548e50699..ccde9391b21 100644
--- a/plugins/woocommerce/tests/php/src/Admin/Features/Fulfillments/FulfillmentsRendererTest.php
+++ b/plugins/woocommerce/tests/php/src/Admin/Features/Fulfillments/FulfillmentsRendererTest.php
@@ -4,6 +4,7 @@ namespace Automattic\WooCommerce\Tests\Admin\Features\Fulfillments;

 use Automattic\WooCommerce\Admin\Features\Fulfillments\DataStore\FulfillmentsDataStore;
 use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
+use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
 use Automattic\WooCommerce\Admin\Features\Fulfillments\Fulfillment;
 use Automattic\WooCommerce\Admin\Features\Fulfillments\FulfillmentsRenderer;
 use Automattic\WooCommerce\Admin\Features\Fulfillments\Providers\AmazonLogisticsShippingProvider;
@@ -417,31 +418,29 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 		$empty_provider_order_id   = $this->create_order_with_shipment_provider( '' );
 		$known_provider_order_id   = $this->create_order_with_shipment_provider( 'dhl' );

-		$_GET['shipping_provider'] = '__other__';
-		$args                      = $this->renderer->filter_orders_by_shipping_provider( array() );
-		unset( $_GET['shipping_provider'] );
+		$order_ids = $this->get_order_ids( '__other__' );

 		$this->assertContains(
 			$unknown_provider_order_id,
-			$args['post__in'],
+			$order_ids,
 			'A provider outside the known list should match the "Other" filter'
 		);
 		$this->assertNotContains(
 			$empty_provider_order_id,
-			$args['post__in'],
+			$order_ids,
 			'A fulfillment saved without a shipping provider should not match the "Other" filter'
 		);

 		if ( $is_known_providers ) {
 			$this->assertNotContains(
 				$known_provider_order_id,
-				$args['post__in'],
+				$order_ids,
 				'A known provider should not match the "Other" filter'
 			);
 		} else {
 			$this->assertContains(
 				$known_provider_order_id,
-				$args['post__in'],
+				$order_ids,
 				'With no known providers, any non-empty provider should match the "Other" filter'
 			);
 		}
@@ -559,45 +558,29 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox The HPOS orders query is filtered to the matching order ids when no post__in exists yet.
+	 * @testdox The HPOS filter carries the requested provider through as a query marker.
 	 */
-	public function test_filter_orders_sets_post_in_when_absent(): void {
-		$this->seed_fulfillment( 301, 'acme-couriers' );
-
+	public function test_filter_orders_sets_provider_marker(): void {
 		$this->with_provider_param(
 			'acme-couriers',
 			function () {
 				$args = $this->renderer->filter_orders_by_shipping_provider( array() );
-				$this->assertSame( array( 301 ), $args['post__in'] );
+				$this->assertSame( 'acme-couriers', $args['fulfillment_shipping_provider'] );
+				$this->assertArrayNotHasKey( 'post__in', $args, 'The optimized filter must not materialize a post__in id list.' );
 			}
 		);
 	}

 	/**
-	 * @testdox The HPOS filter intersects an existing post__in with the provider matches.
+	 * @testdox The HPOS filter leaves any existing query arguments in place while adding the marker.
 	 */
-	public function test_filter_orders_intersects_existing_post_in(): void {
-		$this->seed_fulfillment( 401, 'acme-couriers' );
-		$this->seed_fulfillment( 402, 'acme-couriers' );
-
+	public function test_filter_orders_preserves_existing_args(): void {
 		$this->with_provider_param(
 			'acme-couriers',
 			function () {
 				$args = $this->renderer->filter_orders_by_shipping_provider( array( 'post__in' => array( 401, 999 ) ) );
-				$this->assertSame( array( 401 ), array_values( $args['post__in'] ), 'Only the id present in both sets should remain.' );
-			}
-		);
-	}
-
-	/**
-	 * @testdox The HPOS filter returns a no-match sentinel when the provider has no orders.
-	 */
-	public function test_filter_orders_returns_zero_when_no_match(): void {
-		$this->with_provider_param(
-			'provider-with-no-orders',
-			function () {
-				$args = $this->renderer->filter_orders_by_shipping_provider( array() );
-				$this->assertSame( array( 0 ), $args['post__in'], 'An unmatched provider should force an empty result set.' );
+				$this->assertSame( array( 401, 999 ), $args['post__in'], 'An existing post__in must be preserved, not replaced.' );
+				$this->assertSame( 'acme-couriers', $args['fulfillment_shipping_provider'] );
 			}
 		);
 	}
@@ -612,11 +595,35 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox The legacy orders query is filtered to the matching order ids.
+	 * @testdox The HPOS clause filter restricts the orders table to the provider subquery.
+	 */
+	public function test_filter_orders_table_clauses_injects_subquery(): void {
+		$orders_table = OrdersTableDataStore::get_orders_table_name();
+
+		$clauses = $this->renderer->filter_orders_table_clauses_by_shipping_provider(
+			array( 'where' => '1=1' ),
+			null,
+			array( 'fulfillment_shipping_provider' => 'acme-couriers' )
+		);
+
+		$this->assertStringContainsString( "{$orders_table}.id IN (", $clauses['where'] );
+		$this->assertStringContainsString( $GLOBALS['wpdb']->prefix . 'wc_order_fulfillments', $clauses['where'] );
+		$this->assertStringStartsWith( '1=1 AND ', $clauses['where'], 'The existing WHERE must be preserved.' );
+	}
+
+	/**
+	 * @testdox The HPOS clause filter is a no-op when the provider marker is absent.
 	 */
-	public function test_filter_legacy_orders_sets_post_in(): void {
-		$this->seed_fulfillment( 501, 'acme-couriers' );
+	public function test_filter_orders_table_clauses_noop_without_marker(): void {
+		$clauses = array( 'where' => '1=1' );

+		$this->assertSame( $clauses, $this->renderer->filter_orders_table_clauses_by_shipping_provider( $clauses, null, array() ) );
+	}
+
+	/**
+	 * @testdox The legacy filter marks the main orders query with the requested provider.
+	 */
+	public function test_filter_legacy_orders_sets_provider_marker(): void {
 		$query = new WP_Query();
 		$query->set( 'post_type', 'shop_order' );

@@ -627,7 +634,8 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 					'acme-couriers',
 					function () use ( $query ) {
 						$this->renderer->filter_legacy_orders_by_shipping_provider( $query );
-						$this->assertSame( array( 501 ), $query->get( 'post__in' ) );
+						$this->assertSame( 'acme-couriers', $query->get( 'fulfillment_shipping_provider' ) );
+						$this->assertEmpty( $query->get( 'post__in' ), 'The optimized filter must not materialize a post__in id list.' );
 					}
 				);
 			}
@@ -638,8 +646,6 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 	 * @testdox The legacy filter does nothing when the query is not the admin main orders query.
 	 */
 	public function test_filter_legacy_orders_skips_non_main_query(): void {
-		$this->seed_fulfillment( 601, 'acme-couriers' );
-
 		// A secondary query: not registered as the main query, so the guard must bail.
 		$query = new WP_Query();
 		$query->set( 'post_type', 'shop_order' );
@@ -653,7 +659,7 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 				'acme-couriers',
 				function () use ( $query ) {
 					$this->renderer->filter_legacy_orders_by_shipping_provider( $query );
-					$this->assertEmpty( $query->get( 'post__in' ), 'A non-main query should not be filtered.' );
+					$this->assertEmpty( $query->get( 'fulfillment_shipping_provider' ), 'A non-main query should not be marked.' );
 				}
 			);
 		} finally {
@@ -666,16 +672,327 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Invoke the private provider-to-order-ids query.
+	 * @testdox The legacy clause filter restricts the posts table to the provider subquery.
+	 */
+	public function test_filter_legacy_orders_clauses_injects_subquery(): void {
+		global $wpdb;
+
+		$query = new WP_Query();
+		$query->set( 'fulfillment_shipping_provider', 'acme-couriers' );
+
+		$clauses = $this->renderer->filter_legacy_orders_clauses_by_shipping_provider( array( 'where' => '' ), $query );
+
+		$this->assertStringContainsString( "{$wpdb->posts}.ID IN (", $clauses['where'] );
+		$this->assertStringContainsString( $wpdb->prefix . 'wc_order_fulfillments', $clauses['where'] );
+	}
+
+	/**
+	 * @testdox The legacy clause filter is a no-op when the provider marker is absent.
+	 */
+	public function test_filter_legacy_orders_clauses_noop_without_marker(): void {
+		$clauses = array( 'where' => " AND {$GLOBALS['wpdb']->posts}.post_status = 'wc-completed'" );
+
+		$query = new WP_Query();
+
+		$this->assertSame( $clauses, $this->renderer->filter_legacy_orders_clauses_by_shipping_provider( $clauses, $query ) );
+	}
+
+	/**
+	 * @testdox Running a legacy orders query with the provider marker returns only the matching orders and count.
+	 */
+	public function test_legacy_query_returns_only_matching_orders(): void {
+		$match_a   = $this->create_shop_order_post();
+		$match_b   = $this->create_shop_order_post();
+		$other     = $this->create_shop_order_post();
+		$no_filled = $this->create_shop_order_post();
+
+		$this->seed_fulfillment( $match_a, 'acme-couriers' );
+		$this->seed_fulfillment( $match_b, 'acme-couriers' );
+		$this->seed_fulfillment( $other, 'other-co' );
+
+		add_filter( 'posts_clauses', array( $this->renderer, 'filter_legacy_orders_clauses_by_shipping_provider' ), 10, 2 );
+		try {
+			$query = new WP_Query(
+				array(
+					'post_type'                     => 'shop_order',
+					'post_status'                   => 'wc-processing',
+					'fields'                        => 'ids',
+					'posts_per_page'                => -1,
+					'fulfillment_shipping_provider' => 'acme-couriers',
+				)
+			);
+		} finally {
+			remove_filter( 'posts_clauses', array( $this->renderer, 'filter_legacy_orders_clauses_by_shipping_provider' ), 10 );
+		}
+
+		$ids = array_map( 'absint', $query->posts );
+		sort( $ids );
+		$expected = array( $match_a, $match_b );
+		sort( $expected );
+
+		$this->assertSame( $expected, $ids, 'Only orders whose fulfillment uses the provider should be returned.' );
+		$this->assertSame( 2, (int) $query->found_posts, 'The found-rows count must match the filtered result set.' );
+		$this->assertNotContains( $other, $ids );
+		$this->assertNotContains( $no_filled, $ids );
+	}
+
+	/**
+	 * @testdox The HPOS id-list fallback narrows a single-order `p` restriction instead of widening it.
+	 */
+	public function test_filter_orders_fallback_intersects_existing_p_arg(): void {
+		$this->seed_fulfillment( 971, 'acme-couriers' );
+		$this->seed_fulfillment( 972, 'acme-couriers' );
+
+		add_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		try {
+			$this->with_provider_param(
+				'acme-couriers',
+				function () {
+					// The query remaps p onto id, so a single-order query must not widen to the whole set.
+					$args = $this->renderer->filter_orders_by_shipping_provider( array( 'p' => 972 ) );
+
+					$this->assertSame( array( 972 ), $args['post__in'] );
+				}
+			);
+		} finally {
+			remove_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The legacy count query applies the provider filter when the result set is paginated.
+	 */
+	public function test_legacy_paginated_query_counts_only_matching_orders(): void {
+		$match_a = $this->create_shop_order_post();
+		$match_b = $this->create_shop_order_post();
+		$other   = $this->create_shop_order_post();
+
+		$this->seed_fulfillment( $match_a, 'acme-couriers' );
+		$this->seed_fulfillment( $match_b, 'acme-couriers' );
+		$this->seed_fulfillment( $other, 'other-co' );
+
+		add_filter( 'posts_clauses', array( $this->renderer, 'filter_legacy_orders_clauses_by_shipping_provider' ), 10, 2 );
+		try {
+			// A positive posts_per_page makes WP run the count query the pager reads, which an
+			// unlimited query skips entirely.
+			$query = new WP_Query(
+				array(
+					'post_type'                     => 'shop_order',
+					'post_status'                   => 'wc-processing',
+					'fields'                        => 'ids',
+					'posts_per_page'                => 1,
+					'fulfillment_shipping_provider' => 'acme-couriers',
+				)
+			);
+		} finally {
+			remove_filter( 'posts_clauses', array( $this->renderer, 'filter_legacy_orders_clauses_by_shipping_provider' ), 10 );
+		}
+
+		$this->assertCount( 1, $query->posts, 'The page should hold a single order.' );
+		$this->assertSame( 2, (int) $query->found_posts, 'The count query must apply the provider filter too.' );
+		$this->assertSame( 2, (int) $query->max_num_pages );
+		$this->assertNotContains( $other, array_map( 'absint', $query->posts ) );
+	}
+
+	/**
+	 * @testdox The HPOS filter restricts by id list when a callback can answer the query without the built SQL.
+	 */
+	public function test_filter_orders_falls_back_to_id_list_for_short_circuited_queries(): void {
+		$this->seed_fulfillment( 901, 'acme-couriers' );
+		$this->seed_fulfillment( 902, 'acme-couriers' );
+		$this->seed_fulfillment( 903, 'other-co' );
+
+		// Only the callback's presence matters here, not what it returns.
+		add_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		try {
+			$this->with_provider_param(
+				'acme-couriers',
+				function () {
+					$args = $this->renderer->filter_orders_by_shipping_provider( array() );
+
+					$this->assertArrayHasKey( 'post__in', $args );
+					$this->assertEqualsCanonicalizing( array( 901, 902 ), $args['post__in'] );
+					$this->assertArrayNotHasKey(
+						'fulfillment_shipping_provider',
+						$args,
+						'The marker would be ignored by a callback that discards the built SQL.'
+					);
+				}
+			);
+		} finally {
+			remove_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The HPOS id-list fallback narrows an existing post__in rather than replacing it.
+	 */
+	public function test_filter_orders_fallback_intersects_existing_ids(): void {
+		$this->seed_fulfillment( 911, 'acme-couriers' );
+		$this->seed_fulfillment( 912, 'acme-couriers' );
+
+		add_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		try {
+			$this->with_provider_param(
+				'acme-couriers',
+				function () {
+					$args = $this->renderer->filter_orders_by_shipping_provider( array( 'post__in' => array( 911, 999 ) ) );
+
+					$this->assertSame( array( 911 ), $args['post__in'] );
+				}
+			);
+		} finally {
+			remove_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The HPOS id-list fallback narrows an existing id restriction instead of replacing it.
+	 */
+	public function test_filter_orders_fallback_intersects_existing_id_arg(): void {
+		$this->seed_fulfillment( 961, 'acme-couriers' );
+		$this->seed_fulfillment( 962, 'acme-couriers' );
+
+		add_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		try {
+			$this->with_provider_param(
+				'acme-couriers',
+				function () {
+					// The query remaps post__in onto id, so an existing id must survive the fallback.
+					$args = $this->renderer->filter_orders_by_shipping_provider( array( 'id' => array( 962, 999 ) ) );
+
+					$this->assertSame( array( 962 ), $args['post__in'] );
+				}
+			);
+		} finally {
+			remove_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The HPOS id-list fallback matches no orders when the provider has none.
+	 */
+	public function test_filter_orders_fallback_matches_nothing_for_unused_provider(): void {
+		$this->seed_fulfillment( 921, 'acme-couriers' );
+
+		add_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		try {
+			$this->with_provider_param(
+				'nobody-ships-with-this',
+				function () {
+					$args = $this->renderer->filter_orders_by_shipping_provider( array() );
+
+					$this->assertSame( array( 0 ), $args['post__in'], 'An empty post__in would drop the filter instead of applying it.' );
+				}
+			);
+		} finally {
+			remove_filter( 'woocommerce_hpos_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The legacy filter restricts by id list when a callback can answer the query without the built SQL.
+	 */
+	public function test_filter_legacy_orders_falls_back_to_id_list_for_short_circuited_queries(): void {
+		$this->seed_fulfillment( 931, 'acme-couriers' );
+		$this->seed_fulfillment( 932, 'other-co' );
+
+		$query = new WP_Query();
+		$query->set( 'post_type', 'shop_order' );
+
+		// Only the callback's presence matters here, not what it returns.
+		add_filter( 'posts_pre_query', '__return_null' );
+		try {
+			$this->with_main_query(
+				$query,
+				function () use ( $query ) {
+					$this->with_provider_param(
+						'acme-couriers',
+						function () use ( $query ) {
+							$this->renderer->filter_legacy_orders_by_shipping_provider( $query );
+
+							$this->assertSame( array( 931 ), $query->get( 'post__in' ) );
+							$this->assertEmpty(
+								$query->get( 'fulfillment_shipping_provider' ),
+								'The marker would be ignored by a callback that discards the built SQL.'
+							);
+						}
+					);
+				}
+			);
+		} finally {
+			remove_filter( 'posts_pre_query', '__return_null' );
+		}
+	}
+
+	/**
+	 * @testdox The HPOS filter restricts by id list when the query suppresses the clause filters.
+	 */
+	public function test_filter_orders_falls_back_to_id_list_when_filters_are_suppressed(): void {
+		$this->seed_fulfillment( 941, 'acme-couriers' );
+		$this->seed_fulfillment( 942, 'other-co' );
+
+		$this->with_provider_param(
+			'acme-couriers',
+			function () {
+				$args = $this->renderer->filter_orders_by_shipping_provider( array( 'suppress_filters' => true ) );
+
+				$this->assertSame( array( 941 ), $args['post__in'] );
+				$this->assertArrayNotHasKey(
+					'fulfillment_shipping_provider',
+					$args,
+					'A suppressed query never reaches the clause filter that reads the marker.'
+				);
+			}
+		);
+	}
+
+	/**
+	 * @testdox The legacy filter restricts by id list when the query suppresses the clause filters.
+	 */
+	public function test_filter_legacy_orders_falls_back_to_id_list_when_filters_are_suppressed(): void {
+		$this->seed_fulfillment( 951, 'acme-couriers' );
+		$this->seed_fulfillment( 952, 'other-co' );
+
+		$query = new WP_Query();
+		$query->set( 'post_type', 'shop_order' );
+		$query->set( 'suppress_filters', true );
+
+		$this->with_main_query(
+			$query,
+			function () use ( $query ) {
+				$this->with_provider_param(
+					'acme-couriers',
+					function () use ( $query ) {
+						$this->renderer->filter_legacy_orders_by_shipping_provider( $query );
+
+						$this->assertSame( array( 951 ), $query->get( 'post__in' ) );
+						$this->assertEmpty(
+							$query->get( 'fulfillment_shipping_provider' ),
+							'A suppressed query never reaches the clause filter that reads the marker.'
+						);
+					}
+				);
+			}
+		);
+	}
+
+	/**
+	 * Build the provider subquery and run it, returning the matching order ids.
 	 *
 	 * @param string $provider The provider key or the __other__ sentinel.
 	 * @return array<int> The matching order ids.
 	 */
 	private function get_order_ids( string $provider ): array {
-		$method = new \ReflectionMethod( FulfillmentsRenderer::class, 'get_order_ids_by_shipping_provider' );
+		global $wpdb;
+
+		$method = new \ReflectionMethod( FulfillmentsRenderer::class, 'get_shipping_provider_order_ids_subquery' );
 		$method->setAccessible( true );
+		$subquery = $method->invoke( $this->renderer, $provider );

-		return $method->invoke( $this->renderer, $provider );
+		// The subquery is already prepared inside the method under test.
+		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+		return array_map( 'absint', $wpdb->get_col( $subquery ) );
 	}

 	/**
@@ -719,6 +1036,20 @@ class FulfillmentsRendererTest extends \WC_Unit_Test_Case {
 		}
 	}

+	/**
+	 * Insert a shop_order post and return its id.
+	 *
+	 * @return int The created order post id.
+	 */
+	private function create_shop_order_post(): int {
+		return (int) wp_insert_post(
+			array(
+				'post_type'   => 'shop_order',
+				'post_status' => 'wc-processing',
+			)
+		);
+	}
+
 	/**
 	 * Insert a fulfillment referencing a provider slug, as the filter query expects.
 	 *