From 7f7d616d822b79c952cbd6a3046a6f6a8aa5a35e Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 25 Aug 2022 04:21:40 +0000 Subject: [PATCH] Query: Cache post ID database query within `WP_Query`. Add object caching to the first database query in `WP_Query`, ie the database query for post IDs that match the desired result. Randomly ordered queries remain uncached as they are not intended to return consistent results. Caching of ID queries is enabled by default, per the post object, term and meta caches. Props spacedmonkey, aaroncampbell, batmoo, chriscct7, costdev, dd32, drewapicture, johnbillion, mukesh27, nacin, ocean90, peterwilsoncc, ryan, scribu, sergeybiryukov, tillkruss. Fixes #22176. git-svn-id: https://develop.svn.wordpress.org/trunk@53941 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 136 +++- tests/phpunit/tests/query/cacheResults.php | 896 +++++++++++++++++++++ tests/phpunit/tests/query/commentFeed.php | 2 + 3 files changed, 1017 insertions(+), 17 deletions(-) create mode 100644 tests/phpunit/tests/query/cacheResults.php diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 3762802a72..214d18fda8 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1874,11 +1874,7 @@ class WP_Query { } if ( ! isset( $q['cache_results'] ) ) { - if ( wp_using_ext_object_cache() ) { - $q['cache_results'] = false; - } else { - $q['cache_results'] = true; - } + $q['cache_results'] = true; } if ( ! isset( $q['update_post_term_cache'] ) ) { @@ -3072,6 +3068,77 @@ class WP_Query { */ $this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) ); + /* + * Ensure the ID database query is able to be cached. + * + * Random queries are expected to have unpredictable results and + * cannot be cached. Note the space before `RAND` in the string + * search, that to ensure against a collision with another + * function. + */ + $id_query_is_cacheable = ! str_contains( strtoupper( $orderby ), ' RAND(' ); + if ( $q['cache_results'] && $id_query_is_cacheable ) { + $cache_args = $q; + + unset( + $cache_args['suppress_filters'], + $cache_args['cache_results'], + $cache_args['fields'], + $cache_args['update_post_meta_cache'], + $cache_args['update_post_term_cache'], + $cache_args['lazy_load_term_meta'], + $cache_args['update_menu_item_cache'] + ); + + $new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request ); + $key = md5( serialize( $cache_args ) . $new_request ); + + $last_changed = wp_cache_get_last_changed( 'posts' ); + if ( ! empty( $this->tax_query->queried_terms ) ) { + $last_changed .= wp_cache_get_last_changed( 'terms' ); + } + + $cache_key = "wp_query:$key:$last_changed"; + + if ( null === $this->posts ) { + $cached_results = wp_cache_get( $cache_key, 'posts' ); + + if ( $cached_results ) { + if ( 'ids' === $q['fields'] ) { + /** @var int[] */ + $this->posts = array_map( 'intval', $cached_results['posts'] ); + } else { + _prime_post_caches( $cached_results['posts'], $q['update_post_term_cache'], $q['update_post_meta_cache'] ); + /** @var WP_Post[] */ + $this->posts = array_map( 'get_post', $cached_results['posts'] ); + } + + $this->post_count = count( $this->posts ); + $this->found_posts = $cached_results['found_posts']; + $this->max_num_pages = $cached_results['max_num_pages']; + + if ( 'ids' === $q['fields'] ) { + return $this->posts; + } elseif ( 'id=>parent' === $q['fields'] ) { + /** @var int[] */ + $post_parents = array(); + + foreach ( $this->posts as $key => $post ) { + $obj = new stdClass(); + $obj->ID = (int) $post->ID; + $obj->post_parent = (int) $post->post_parent; + + $this->posts[ $key ] = $obj; + + $post_parents[ $obj->ID ] = $obj->post_parent; + } + + return $post_parents; + } + } + } + } + if ( 'ids' === $q['fields'] ) { if ( null === $this->posts ) { $this->posts = $wpdb->get_col( $this->request ); @@ -3082,6 +3149,16 @@ class WP_Query { $this->post_count = count( $this->posts ); $this->set_found_posts( $q, $limits ); + if ( $q['cache_results'] && $id_query_is_cacheable ) { + $cache_value = array( + 'posts' => $this->posts, + 'found_posts' => $this->found_posts, + 'max_num_pages' => $this->max_num_pages, + ); + + wp_cache_set( $cache_key, $cache_value, 'posts' ); + } + return $this->posts; } @@ -3094,15 +3171,28 @@ class WP_Query { $this->set_found_posts( $q, $limits ); /** @var int[] */ - $r = array(); + $post_parents = array(); + $post_ids = array(); + foreach ( $this->posts as $key => $post ) { $this->posts[ $key ]->ID = (int) $post->ID; $this->posts[ $key ]->post_parent = (int) $post->post_parent; - $r[ (int) $post->ID ] = (int) $post->post_parent; + $post_parents[ (int) $post->ID ] = (int) $post->post_parent; + $post_ids[] = (int) $post->ID; } - return $r; + if ( $q['cache_results'] && $id_query_is_cacheable ) { + $cache_value = array( + 'posts' => $post_ids, + 'found_posts' => $this->found_posts, + 'max_num_pages' => $this->max_num_pages, + ); + + wp_cache_set( $cache_key, $cache_value, 'posts' ); + } + + return $post_parents; } if ( null === $this->posts ) { @@ -3144,12 +3234,12 @@ class WP_Query { */ $this->request = apply_filters( 'posts_request_ids', $this->request, $this ); - $ids = $wpdb->get_col( $this->request ); + $post_ids = $wpdb->get_col( $this->request ); - if ( $ids ) { - $this->posts = $ids; + if ( $post_ids ) { + $this->posts = $post_ids; $this->set_found_posts( $q, $limits ); - _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] ); + _prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] ); } else { $this->posts = array(); } @@ -3165,6 +3255,18 @@ class WP_Query { $this->posts = array_map( 'get_post', $this->posts ); } + if ( $q['cache_results'] && $id_query_is_cacheable ) { + $post_ids = wp_list_pluck( $this->posts, 'ID' ); + + $cache_value = array( + 'posts' => $post_ids, + 'found_posts' => $this->found_posts, + 'max_num_pages' => $this->max_num_pages, + ); + + wp_cache_set( $cache_key, $cache_value, 'posts' ); + } + if ( ! empty( $this->posts ) && $q['update_menu_item_cache'] ) { update_menu_item_cache( $this->posts ); } @@ -3201,14 +3303,14 @@ class WP_Query { $comments_request = "SELECT {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits"; - $key = md5( $comments_request ); - $last_changed = wp_cache_get_last_changed( 'comment' ); + $comment_key = md5( $comments_request ); + $comment_last_changed = wp_cache_get_last_changed( 'comment' ); - $cache_key = "comment_feed:$key:$last_changed"; - $comment_ids = wp_cache_get( $cache_key, 'comment' ); + $comment_cache_key = "comment_feed:$comment_key:$comment_last_changed"; + $comment_ids = wp_cache_get( $comment_cache_key, 'comment' ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_add( $cache_key, $comment_ids, 'comment' ); + wp_cache_add( $comment_cache_key, $comment_ids, 'comment' ); } _prime_comment_caches( $comment_ids, false ); diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php new file mode 100644 index 0000000000..226e82aeca --- /dev/null +++ b/tests/phpunit/tests/query/cacheResults.php @@ -0,0 +1,896 @@ +post->create_many( 5 ); + self::$pages = $factory->post->create_many( 5, array( 'post_type' => 'page' ) ); + + self::$t1 = $factory->term->create( + array( + 'taxonomy' => 'category', + 'slug' => 'foo', + 'name' => 'Foo', + ) + ); + + wp_set_post_terms( self::$posts[0], self::$t1, 'category' ); + add_post_meta( self::$posts[0], 'color', '#000000' ); + + // Make a user. + self::$author_id = $factory->user->create( + array( + 'role' => 'author', + ) + ); + } + + /** + * @dataProvider data_query_cache + * @ticket 22176 + */ + public function test_query_cache( $args ) { + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + $queries_before = get_num_queries(); + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + $queries_after = get_num_queries(); + + add_filter( 'split_the_query', '__return_false' ); + $split_query = new WP_Query(); + $split_posts = $split_query->query( $args ); + remove_filter( 'split_the_query', '__return_false' ); + + if ( isset( $args['fields'] ) ) { + if ( 'all' !== $args['fields'] ) { + $this->assertSameSets( $posts1, $posts2, 'Second query produces different set of posts to first.' ); + $this->assertSameSets( $posts1, $split_posts, 'Split query produces different set of posts to first.' ); + } + if ( 'id=>parent' !== $args['fields'] ) { + $this->assertSame( $queries_after, $queries_before, 'Second query produces unexpected DB queries.' ); + } + } else { + $this->assertSame( $queries_after, $queries_before, 'Second query produces unexpected DB queries.' ); + } + $this->assertSame( $query1->found_posts, $query2->found_posts, 'Second query has a different number of found posts to first.' ); + $this->assertSame( $query1->found_posts, $split_query->found_posts, 'Split query has a different number of found posts to first.' ); + $this->assertSame( $query1->max_num_pages, $query2->max_num_pages, 'Second query has a different number of total to first.' ); + $this->assertSame( $query1->max_num_pages, $split_query->max_num_pages, 'Split query has a different number of total to first.' ); + + if ( ! $query1->query_vars['no_found_rows'] ) { + wp_delete_post( self::$posts[0], true ); + wp_delete_post( self::$pages[0], true ); + $query3 = new WP_Query(); + $query3->query( $args ); + + $this->assertNotSame( $query1->found_posts, $query3->found_posts ); + $this->assertNotSame( $queries_after, get_num_queries() ); + } + } + + /** + * Data provider. + * + * @return array[] Test parameters. + */ + public function data_query_cache() { + return array( + 'cache true' => array( + 'args' => array( + 'cache_results' => true, + ), + ), + 'cache true and pagination' => array( + 'args' => array( + 'cache_results' => true, + 'posts_per_page' => 3, + 'page' => 2, + ), + ), + 'cache true and no pagination' => array( + 'args' => array( + 'cache_results' => true, + 'nopaging' => true, + ), + ), + 'cache true and post type any' => array( + 'args' => array( + 'cache_results' => true, + 'nopaging' => true, + 'post_type' => 'any', + ), + ), + 'cache true and get all' => array( + 'args' => array( + 'cache_results' => true, + 'fields' => 'all', + 'posts_per_page' => -1, + 'post_status' => 'any', + 'post_type' => 'any', + ), + ), + 'cache true and page' => array( + 'args' => array( + 'cache_results' => true, + 'post_type' => 'page', + ), + ), + 'cache true and ids' => array( + 'args' => array( + 'cache_results' => true, + 'fields' => 'ids', + ), + ), + 'cache true and id=>parent and no found rows' => array( + 'args' => array( + 'cache_results' => true, + 'fields' => 'id=>parent', + ), + ), + 'cache true and ids and no found rows' => array( + 'args' => array( + 'no_found_rows' => true, + 'cache_results' => true, + 'fields' => 'ids', + ), + ), + 'cache true and id=>parent' => array( + 'args' => array( + 'no_found_rows' => true, + 'cache_results' => true, + 'fields' => 'id=>parent', + ), + ), + 'cache and ignore_sticky_posts' => array( + 'args' => array( + 'cache_results' => true, + 'ignore_sticky_posts' => true, + ), + ), + 'cache meta query' => array( + 'args' => array( + 'cache_results' => true, + 'meta_query' => array( + array( + 'key' => 'color', + ), + ), + ), + ), + 'cache comment_count' => array( + 'args' => array( + 'cache_results' => true, + 'comment_count' => 0, + ), + ), + 'cache term query' => array( + 'args' => array( + 'cache_results' => true, + 'tax_query' => array( + array( + 'taxonomy' => 'category', + 'terms' => array( 'foo' ), + 'field' => 'slug', + ), + ), + ), + ), + ); + } + + /** + * @ticket 22176 + */ + public function test_seeded_random_queries_only_cache_post_objects() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'orderby' => 'rand(6)', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + $queries_before = get_num_queries(); + + $query2 = new WP_Query(); + $query2->query( $args ); + + $queries_after = get_num_queries(); + + $this->assertNotSame( $queries_before, $queries_after ); + } + + /** + * @ticket 22176 + */ + public function test_unseeded_random_queries_only_cache_post_objects() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'orderby' => 'rand', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + $queries_before = get_num_queries(); + + $query2 = new WP_Query(); + $query2->query( $args ); + + $queries_after = get_num_queries(); + + $this->assertNotSame( $queries_before, $queries_after ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_filter_request() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + $queries_before = get_num_queries(); + + add_filter( 'posts_request', array( $this, 'filter_posts_request' ) ); + + $query2 = new WP_Query(); + $query2->query( $args ); + + $queries_after = get_num_queries(); + + $this->assertNotSame( $queries_before, $queries_after ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_no_caching() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + $queries_before = get_num_queries(); + + $query2 = new WP_Query(); + $args['cache_results'] = false; + $query2->query( $args ); + + $queries_after = get_num_queries(); + + $this->assertNotSame( $queries_before, $queries_after ); + } + + public function filter_posts_request( $request ) { + return $request . ' -- Add comment'; + } + + /** + * @ticket 22176 + */ + public function test_query_cache_new_post() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + $p1 = self::factory()->post->create(); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_main_query_sticky_posts_change() { + add_action( 'parse_query', array( $this, 'set_cache_results' ) ); + update_option( 'posts_per_page', 5 ); + + $old_date = date_create( '-25 hours' ); + $old_post = self::factory()->post->create( array( 'post_date' => $old_date->format( 'Y-m-d H:i:s' ) ) ); + + // Post is unstuck. + $this->go_to( '/' ); + $unstuck = $GLOBALS['wp_query']->posts; + $unstuck_ids = wp_list_pluck( $unstuck, 'ID' ); + + $expected = array_reverse( self::$posts ); + $this->assertSame( $expected, $unstuck_ids ); + + // Stick the post. + stick_post( $old_post ); + + $this->go_to( '/' ); + $stuck = $GLOBALS['wp_query']->posts; + $stuck_ids = wp_list_pluck( $stuck, 'ID' ); + + $expected = array_reverse( self::$posts ); + array_unshift( $expected, $old_post ); + + $this->assertSame( $expected, $stuck_ids ); + } + + /** + * @ticket 22176 + */ + public function test_main_query_in_query_sticky_posts_change() { + add_action( 'parse_query', array( $this, 'set_cache_results' ) ); + update_option( 'posts_per_page', 5 ); + + $middle_post = self::$posts[2]; + + // Post is unstuck. + $this->go_to( '/' ); + $unstuck = $GLOBALS['wp_query']->posts; + $unstuck_ids = wp_list_pluck( $unstuck, 'ID' ); + + $expected = array_reverse( self::$posts ); + $this->assertSame( $expected, $unstuck_ids ); + + // Stick the post. + stick_post( $middle_post ); + + $this->go_to( '/' ); + $stuck = $GLOBALS['wp_query']->posts; + $stuck_ids = wp_list_pluck( $stuck, 'ID' ); + + $expected = array_diff( array_reverse( self::$posts ), array( $middle_post ) ); + array_unshift( $expected, $middle_post ); + + $this->assertSame( $expected, $stuck_ids ); + } + + /** + * @ticket 22176 + */ + public function test_query_sticky_posts_change() { + add_action( 'parse_query', array( $this, 'set_cache_results' ) ); + + $old_date = date_create( '-25 hours' ); + $old_post = self::factory()->post->create( array( 'post_date' => $old_date->format( 'Y-m-d H:i:s' ) ) ); + + // Post is unstuck. + $unstuck = new WP_Query( array( 'posts_per_page' => 5 ) ); + $unstuck_ids = wp_list_pluck( $unstuck->posts, 'ID' ); + + $expected = array_reverse( self::$posts ); + + $this->assertSame( $expected, $unstuck_ids ); + + // Stick the post. + stick_post( $old_post ); + + $stuck = new WP_Query( array( 'posts_per_page' => 5 ) ); + $stuck_ids = wp_list_pluck( $stuck->posts, 'ID' ); + + $expected = array_reverse( self::$posts ); + array_unshift( $expected, $old_post ); + + $this->assertSame( $expected, $stuck_ids ); + + // Ignore sticky posts. + $ignore_stuck = new WP_Query( + array( + 'posts_per_page' => 5, + 'ignore_sticky_posts' => true, + ) + ); + $ignore_stuck_ids = wp_list_pluck( $ignore_stuck->posts, 'ID' ); + + $expected = array_reverse( self::$posts ); + + $this->assertSame( $expected, $ignore_stuck_ids ); + + // Just to make sure everything has changed. + $this->assertNotSame( $unstuck, $stuck ); + } + + /** + * @ticket 22176 + */ + public function test_query_in_query_sticky_posts_change() { + add_action( 'parse_query', array( $this, 'set_cache_results' ) ); + + $middle_post = self::$posts[2]; + + // Post is unstuck. + $unstuck = new WP_Query( array( 'posts_per_page' => 5 ) ); + $unstuck_ids = wp_list_pluck( $unstuck->posts, 'ID' ); + + $expected = array_reverse( self::$posts ); + + $this->assertSame( $expected, $unstuck_ids ); + + // Stick the post. + stick_post( $middle_post ); + + $stuck = new WP_Query( array( 'posts_per_page' => 5 ) ); + $stuck_ids = wp_list_pluck( $stuck->posts, 'ID' ); + + $expected = array_diff( array_reverse( self::$posts ), array( $middle_post ) ); + array_unshift( $expected, $middle_post ); + + $this->assertSame( $expected, $stuck_ids ); + + // Ignore sticky posts. + $ignore_stuck = new WP_Query( + array( + 'posts_per_page' => 5, + 'ignore_sticky_posts' => true, + ) + ); + $ignore_stuck_ids = wp_list_pluck( $ignore_stuck->posts, 'ID' ); + + $expected = array_reverse( self::$posts ); + + $this->assertSame( $expected, $ignore_stuck_ids ); + + // Just to make sure everything has changed. + $this->assertNotSame( $unstuck, $stuck ); + } + + public function set_cache_results( $q ) { + $q->set( 'cache_results', true ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_different_args() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'suppress_filters' => true, + 'cache_results' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'lazy_load_term_meta' => false, + ); + $queries_before = get_num_queries(); + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + $queries_after = get_num_queries(); + + $this->assertSame( $queries_before, $queries_after ); + $this->assertSame( $posts1, $posts2 ); + $this->assertSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_different_fields() { + $args = array( + 'cache_results' => true, + 'fields' => 'all', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + + $args = array( + 'cache_results' => true, + 'fields' => 'id=>parent', + ); + $queries_before = get_num_queries(); + $query2 = new WP_Query(); + $query2->query( $args ); + $queries_after = get_num_queries(); + + $this->assertSame( $queries_before, $queries_after ); + $this->assertCount( 5, $query1->posts ); + $this->assertCount( 5, $query2->posts ); + $this->assertSame( $query1->found_posts, $query2->found_posts ); + + /* + * Make sure the returned post objects differ due to the field argument. + * + * This uses assertNotEquals rather than assertNotSame as the former is + * agnostic to the instance ID of objects, whereas the latter will take + * it in to account. The test needs to discard the instance ID when + * confirming inequality. + */ + $this->assertNotEquals( $query1->posts, $query2->posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_logged_in() { + $user_id = self::$author_id; + + self::factory()->post->create( + array( + 'post_status' => 'private', + 'post_author' => $user_id, + ) + ); + + $args = array( + 'cache_results' => true, + 'author' => $user_id, + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_set_current_user( $user_id ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + $this->assertEmpty( $posts1 ); + $this->assertNotSame( $posts1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_logged_in_password() { + $user_id = self::$author_id; + self::factory()->post->create( + array( + 'post_title' => 'foo', + 'post_password' => 'password', + 'post_author' => $user_id, + ) + ); + + $args = array( + 'cache_results' => true, + 's' => 'foo', + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_set_current_user( $user_id ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + $this->assertEmpty( $posts1 ); + $this->assertNotSame( $posts1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_new_comment() { + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'comment_count' => 1, + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + self::factory()->comment->create( array( 'comment_post_ID' => self::$posts[0] ) ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( self::$posts[0], $posts2 ); + $this->assertNotEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_main_comments_feed_includes_attachment_comments() { + $attachment_id = self::factory()->post->create( array( 'post_type' => 'attachment' ) ); + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $attachment_id, + 'comment_approved' => '1', + ) + ); + + $args = array( + 'cache_results' => true, + 'withcomments' => 1, + 'feed' => 'feed', + ); + $query1 = new WP_Query(); + $query1->query( $args ); + + $query2 = new WP_Query(); + $query2->query( $args ); + + $this->assertTrue( $query1->have_comments() ); + $this->assertTrue( $query2->have_comments() ); + + $feed_comment = $query1->next_comment(); + $this->assertEquals( $comment_id, $feed_comment->comment_ID ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_delete_comment() { + $comment_id = self::factory()->comment->create( array( 'comment_post_ID' => self::$posts[0] ) ); + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'comment_count' => 1, + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_delete_comment( $comment_id, true ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_update_post() { + $p1 = self::$posts[0]; + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_update_post( + array( + 'ID' => $p1, + 'post_status' => 'draft', + ) + ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts1 ); + $this->assertNotContains( $p1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_new_meta() { + $p1 = self::$posts[1]; // Post 0 already has a color meta value. + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'meta_query' => array( + array( + 'key' => 'color', + ), + ), + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + add_post_meta( $p1, 'color', 'black' ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_update_meta() { + // Posts[0] already has a color meta value set to #000000. + $p1 = self::$posts[0]; + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'meta_query' => array( + array( + 'key' => 'color', + 'value' => '#000000', + ), + ), + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + update_post_meta( $p1, 'color', 'blue' ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts1 ); + $this->assertEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + + /** + * @ticket 22176 + */ + public function test_query_cache_delete_attachment() { + $p1 = self::factory()->post->create( + array( + 'post_type' => 'attachment', + 'post_status' => 'inherit', + ) + ); + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'post_type' => 'attachment', + 'post_status' => 'inherit', + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_delete_attachment( $p1 ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts1 ); + $this->assertEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_delete_meta() { + // Post 0 already has a color meta value. + $p1 = self::$posts[1]; + add_post_meta( $p1, 'color', 'black' ); + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'meta_query' => array( + array( + 'key' => 'color', + ), + ), + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + delete_post_meta( $p1, 'color' ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts1 ); + $this->assertNotEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_new_term() { + // Post 0 already has the category foo. + $p1 = self::$posts[1]; + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'tax_query' => array( + array( + 'taxonomy' => 'category', + 'terms' => array( 'foo' ), + 'field' => 'slug', + ), + ), + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_set_post_terms( $p1, array( self::$t1 ), 'category' ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_delete_term() { + // Post 0 already has the category foo. + $p1 = self::$posts[1]; + register_taxonomy( 'wptests_tax1', 'post' ); + + $t1 = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax1' ) ); + + wp_set_object_terms( $p1, array( $t1 ), 'wptests_tax1' ); + + $args = array( + 'cache_results' => true, + 'fields' => 'ids', + 'tax_query' => array( + array( + 'taxonomy' => 'wptests_tax1', + 'terms' => array( $t1 ), + 'field' => 'term_id', + ), + ), + ); + $query1 = new WP_Query(); + $posts1 = $query1->query( $args ); + + wp_delete_term( $t1, 'wptests_tax1' ); + + $query2 = new WP_Query(); + $posts2 = $query2->query( $args ); + + $this->assertNotSame( $posts1, $posts2 ); + $this->assertContains( $p1, $posts1 ); + $this->assertEmpty( $posts2 ); + $this->assertNotSame( $query1->found_posts, $query2->found_posts ); + } +} diff --git a/tests/phpunit/tests/query/commentFeed.php b/tests/phpunit/tests/query/commentFeed.php index 2deda2df40..35c4eb0b60 100644 --- a/tests/phpunit/tests/query/commentFeed.php +++ b/tests/phpunit/tests/query/commentFeed.php @@ -39,6 +39,7 @@ class Tests_Query_CommentFeed extends WP_UnitTestCase { 'update_post_term_cache' => false, 'ignore_sticky_posts' => false, 'no_found_rows' => true, + 'cache_results' => false, ); $q1->query( $args ); $num_queries = $wpdb->num_queries; @@ -98,6 +99,7 @@ class Tests_Query_CommentFeed extends WP_UnitTestCase { 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'ignore_sticky_posts' => false, + 'cache_results' => false, ); $q1->query( $args );