WordPress.org

Make WordPress Core

Ticket #29822: 29822.patch

File 29822.patch, 26.1 KB (added by boonebgorges, 5 years ago)
  • new file src/wp-includes/class-wp-recursive-query.php

    diff --git src/wp-includes/class-wp-recursive-query.php src/wp-includes/class-wp-recursive-query.php
    new file mode 100644
    index 0000000..5fec3f5
    - +  
     1<?php
     2
     3/**
     4 * Base class for creating query classes that generate SQL fragments for filtering results based on recursive query params.
     5 *
     6 * @since 4.1.0
     7 */
     8abstract class WP_Recursive_Query {
     9
     10        /**
     11         * Query arguments passed to the constructor.
     12         *
     13         * @since 4.1.0
     14         * @access public
     15         * @var array
     16         */
     17        public $queries = array();
     18
     19        /**
     20         * A flat list of table aliases used in JOIN clauses.
     21         *
     22         * @since 4.1.0
     23         * @access protected
     24         * @var array
     25         */
     26        protected $table_aliases = array();
     27
     28        /**
     29         * Generate SQL clauses to be appended to a main query.
     30         *
     31         * Extending classes should call this method from within a publicly
     32         * accessible get_sql() method, and manipulate the SQL as necessary.
     33         * For example, {@link WP_Tax_Query::get_sql()} is merely a wrapper for
     34         * get_sql_clauses(), while {@link WP_Date_Query::get_sql()} discards
     35         * the empty 'join' clauses are discarded, and passes the 'where'
     36         * clause through apply_filters().
     37         *
     38         * @since 4.1.0
     39         * @access protected
     40         *
     41         * @param  string $primary_table
     42         * @param  string $primary_id_column
     43         * @return array
     44         */
     45        protected function get_sql_clauses() {
     46                $sql = $this->get_sql_for_query( $this->queries );
     47
     48                if ( ! empty( $sql['where'] ) ) {
     49                        $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n";
     50                }
     51
     52                return $sql;
     53        }
     54
     55        /**
     56         * Generate SQL clauses for a single query array.
     57         *
     58         * If nested subqueries are found, this method recurses the tree to
     59         * produce the properly nested SQL.
     60         *
     61         * Subclasses generally do not need to call this method. It is invoked
     62         * automatically from get_sql_clauses().
     63         *
     64         * @since 4.1.0
     65         * @access protected
     66         *
     67         * @param  array $query Query to parse.
     68         * @param  int   $depth Optional. Number of tree levels deep we
     69         *                      currently are. Used to calculate indentation.
     70         * @return array
     71         */
     72        protected function get_sql_for_query( $query, $depth = 0 ) {
     73                $sql_chunks = array(
     74                        'join'  => array(),
     75                        'where' => array(),
     76                );
     77
     78                $sql = array(
     79                        'join'  => '',
     80                        'where' => '',
     81                );
     82
     83                $indent = '';
     84                for ( $i = 0; $i < $depth; $i++ ) {
     85                        $indent .= "\t";
     86                }
     87
     88                foreach ( $query as $key => $clause ) {
     89                        if ( 'relation' === $key ) {
     90                                $relation = $query['relation'];
     91                        } else if ( is_array( $clause ) ) {
     92                                // This is a first-order clause
     93                                if ( $this->is_first_order_clause( $clause ) ) {
     94                                        $clause_sql = $this->get_sql_for_clause( $clause, $query );
     95
     96                                        $where_count = count( $clause_sql['where'] );
     97                                        if ( ! $where_count ) {
     98                                                $sql_chunks['where'][] = '';
     99                                        } else if ( 1 === $where_count ) {
     100                                                $sql_chunks['where'][] = $clause_sql['where'][0];
     101                                        } else {
     102                                                $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
     103                                        }
     104
     105                                        $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
     106                                // This is a subquery
     107                                } else {
     108                                        $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
     109
     110                                        $sql_chunks['where'][] = $clause_sql['where'];
     111                                        $sql_chunks['join'][]  = $clause_sql['join'];
     112
     113                                }
     114                        }
     115                }
     116
     117                // Filter empties
     118                $sql_chunks['join']  = array_filter( $sql_chunks['join'] );
     119                $sql_chunks['where'] = array_filter( $sql_chunks['where'] );
     120
     121                if ( empty( $relation ) ) {
     122                        $relation = 'AND';
     123                }
     124
     125                if ( ! empty( $sql_chunks['join'] ) ) {
     126                        $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
     127                }
     128
     129                if ( ! empty( $sql_chunks['where'] ) ) {
     130                        $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n";
     131                }
     132
     133                return $sql;
     134        }
     135
     136        /**
     137         * Generate JOIN and WHERE clauses for a first-order clause.
     138         *
     139         * Must be overridden in a subclass.
     140         *
     141         * @since 4.1.0
     142         * @access protected
     143         *
     144         * @param  array $clause       Array of arguments belonging to the clause.
     145         * @param  array $parent_query Parent query to which the clause belongs.
     146         * @return array {
     147         *     @type array $join  Array of subclauses for the JOIN statement.
     148         *     @type array $where Array of subclauses for the WHERE statement.
     149         * }
     150         */
     151        abstract protected function get_sql_for_clause( $clause, $parent_query );
     152
     153        /**
     154         * Determine whether a clause is first-order.
     155         *
     156         * Must be overridden in a subclass.
     157         *
     158         * @since 4.1.0
     159         * @access protected
     160         *
     161         * @param  array $q Clause to check.
     162         * @return bool
     163         */
     164        abstract protected function is_first_order_clause( $query );
     165}
  • src/wp-includes/date.php

    diff --git src/wp-includes/date.php src/wp-includes/date.php
    index 1cf4c6d..b3e3ffe 100644
     
    88 *
    99 * @since 3.7.0
    1010 */
    11 class WP_Date_Query {
    12         /**
    13          * List of date queries.
    14          *
    15          * @since 3.7.0
    16          * @access public
    17          * @var array
    18          */
    19         public $queries = array();
     11class WP_Date_Query extends WP_Recursive_Query {
    2012
    2113        /**
    22          * The relation between the queries. Can be either 'AND' or 'OR' and can be changed via the query arguments.
     14         * The column to query against. Can be changed via the query arguments.
    2315         *
    2416         * @since 3.7.0
    2517         * @access public
    2618         * @var string
    2719         */
    28         public $relation = 'AND';
     20        public $column = 'post_date';
    2921
    3022        /**
    31          * The column to query against. Can be changed via the query arguments.
     23         * The value comparison operator. Can be changed via the query arguments.
    3224         *
    3325         * @since 3.7.0
    3426         * @access public
    35          * @var string
     27         * @var array
    3628         */
    37         public $column = 'post_date';
     29        public $compare = '=';
    3830
    3931        /**
    40          * The value comparison operator. Can be changed via the query arguments.
     32         * Supported time-related parameter keys.
    4133         *
    42          * @since 3.7.0
     34         * @since 4.1.0
    4335         * @access public
    4436         * @var array
    4537         */
    46         public $compare = '=';
     38        public $time_keys = array( 'after', 'before', 'year', 'month', 'monthnum', 'week', 'w', 'dayofyear', 'day', 'dayofweek', 'hour', 'minute', 'second' );
     39
    4740
    4841        /**
    4942         * Constructor.
    class WP_Date_Query { 
    109102         *                              'comment_date', 'comment_date_gmt'.
    110103         */
    111104        public function __construct( $date_query, $default_column = 'post_date' ) {
    112                 if ( empty( $date_query ) || ! is_array( $date_query ) )
    113                         return;
    114105
    115                 if ( isset( $date_query['relation'] ) && strtoupper( $date_query['relation'] ) == 'OR' )
     106                if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) {
    116107                        $this->relation = 'OR';
    117                 else
     108                } else {
    118109                        $this->relation = 'AND';
     110                }
    119111
    120                 if ( ! empty( $date_query['column'] ) )
    121                         $this->column = esc_sql( $date_query['column'] );
    122                 else
    123                         $this->column = esc_sql( $default_column );
     112                if ( ! is_array( $date_query ) ) {
     113                        return;
     114                }
     115
     116                if ( ! isset( $date_query[0] ) && ! empty( $date_query ) ) {
     117                        $date_query = array( $date_query );
     118                }
     119
     120                if ( empty( $date_query ) ) {
     121                        return;
     122                }
     123
     124                if ( ! empty( $date_query['column'] ) ) {
     125                        $date_query['column'] = esc_sql( $date_query['column'] );
     126                } else {
     127                        $date_query['column'] = esc_sql( $default_column );
     128                }
    124129
    125130                $this->column = $this->validate_column( $this->column );
    126131
    127132                $this->compare = $this->get_compare( $date_query );
    128133
    129                 // If an array of arrays wasn't passed, fix it
    130                 if ( ! isset( $date_query[0] ) )
    131                         $date_query = array( $date_query );
     134                $this->queries = $this->sanitize_query( $date_query );
     135
     136                return;
     137        }
    132138
    133                 $this->queries = array();
    134                 foreach ( $date_query as $key => $query ) {
    135                         if ( ! is_array( $query ) )
     139        /**
     140         * Recursive-friendly query sanitizer.
     141         *
     142         * Ensures that each query-level clause has a 'relation' key, and that
     143         * each first-order clause contains all the necessary keys from
     144         * $defaults.
     145         *
     146         * @since 4.1.0
     147         *
     148         * @param  array $query A tax_query query clause.
     149         * @return array
     150         */
     151        protected function sanitize_query( $query, $parent_query = null ) {
     152                $sanitized_query = array();
     153
     154                $defaults = array(
     155                        'column' => 'post_date',
     156                        'compare' => '=',
     157                        'relation' => 'AND',
     158                );
     159
     160                // Numeric keys should always have array values.
     161                foreach ( $query as $qkey => $qvalue ) {
     162                        if ( is_numeric( $qkey ) && ! is_array( $qvalue ) ) {
     163                                unset( $query[ $qkey ] );
     164                        }
     165                }
     166
     167                // Each layer of query should have a value for each default key.
     168                foreach ( $defaults as $dkey => $dvalue ) {
     169                        if ( isset( $query[ $dkey ] ) ) {
    136170                                continue;
     171                        }
    137172
    138                         $this->queries[$key] = $query;
     173                        if ( isset( $parent_query[ $dkey ] ) ) {
     174                                $query[ $dkey ] = $parent_query[ $dkey ];
     175                        } else {
     176                                $query[ $dkey ] = $dvalue;
     177                        }
    139178                }
     179
     180                foreach ( $query as $key => $q ) {
     181                        if ( ! is_array( $q ) || in_array( $key, $this->time_keys, true ) ) {
     182                                $sanitized_query[ $key ] = $q;
     183                        } else {
     184                                $sanitized_query[] = $this->sanitize_query( $q, $query );
     185                        }
     186                }
     187
     188                return $sanitized_query;
    140189        }
    141190
    142191        /**
    class WP_Date_Query { 
    192241         * @return string MySQL WHERE parameters
    193242         */
    194243        public function get_sql() {
    195                 // The parts of the final query
    196                 $where = array();
    197 
    198                 foreach ( $this->queries as $key => $query ) {
    199                         $where_parts = $this->get_sql_for_subquery( $query );
    200                         if ( $where_parts ) {
    201                                 // Combine the parts of this subquery into a single string
    202                                 $where[ $key ] = '( ' . implode( ' AND ', $where_parts ) . ' )';
    203                         }
    204                 }
     244                $sql = $this->get_sql_clauses();
    205245
    206                 // Combine the subquery strings into a single string
    207                 if ( $where )
    208                         $where = ' AND ( ' . implode( " {$this->relation} ", $where ) . ' )';
    209                 else
    210                         $where = '';
     246                $where = $sql['where'];
    211247
    212248                /**
    213249                 * Filter the date query WHERE clause.
    class WP_Date_Query { 
    221257        }
    222258
    223259        /**
    224          * Turns a single date subquery into pieces for a WHERE clause.
     260         * Turns a single date clause into pieces for a WHERE clause.
    225261         *
    226          * @since 3.7.0
    227          * return array
     262         * A wrapper for get_sql_for_clause(), included here for backward
     263         * compatibility while retaining the naming convention across Query
     264         * classes.
     265         *
     266         * @since  3.7.0
     267         * @access protected
     268         *
     269         * @param  array $query Date query arguments.
     270         * @return array
    228271         */
    229272        protected function get_sql_for_subquery( $query ) {
     273                return $this->get_sql_for_clause( $query, '' );
     274        }
     275
     276        /**
     277         * Turns a single date clause into pieces for a WHERE clause.
     278         *
     279         * @since  4.1.0
     280         * @access protected
     281         *
     282         * @param  array $query        Date query clause.
     283         * @param  array $parent_query Parent query of the current date query.
     284         * @return array
     285         */
     286        protected function get_sql_for_clause( $query, $parent_query ) {
    230287                global $wpdb;
    231288
    232289                // The sub-parts of a $where part
    class WP_Date_Query { 
    293350                        }
    294351                }
    295352
    296                 return $where_parts;
     353                // Return an array of 'join' and 'where' for compatibility
     354                // with other query classes.
     355                return array(
     356                        'where' => $where_parts,
     357                        'join'  => array(),
     358                );
     359        }
     360
     361        /**
     362         * Determine whether this is a first-order clause.
     363         *
     364         * Essentially, this checks to see if the current clause has any time-
     365         * related keys. If so, it's first-order.
     366         *
     367         * @param  array $query Query clause.
     368         * @return bool
     369         */
     370        protected function is_first_order_clause( $query ) {
     371                return isset( $query['after'] ) || isset( $query['before'] ) || isset( $query['year'] ) || isset( $query['month'] ) || isset( $query['monthnum'] ) || isset( $query['week'] ) || isset( $query['w'] ) || isset( $query['dayofyear'] ) || isset( $query['day'] ) || isset( $query['dayofweek'] ) || isset( $query['hour'] ) || isset( $query['minute'] ) || isset( $query['second'] );
    297372        }
    298373
    299374        /**
  • src/wp-settings.php

    diff --git src/wp-settings.php src/wp-settings.php
    index 9795971..5dca996 100644
    wp_not_installed(); 
    111111// Load most of WordPress.
    112112require( ABSPATH . WPINC . '/class-wp-walker.php' );
    113113require( ABSPATH . WPINC . '/class-wp-ajax-response.php' );
     114require( ABSPATH . WPINC . '/class-wp-recursive-query.php' );
    114115require( ABSPATH . WPINC . '/formatting.php' );
    115116require( ABSPATH . WPINC . '/capabilities.php' );
    116117require( ABSPATH . WPINC . '/query.php' );
  • tests/phpunit/tests/date/query.php

    diff --git tests/phpunit/tests/date/query.php tests/phpunit/tests/date/query.php
    index e9dd191..e87e917 100644
    class Tests_WP_Date_Query extends WP_UnitTestCase { 
    5555                                        'year' => 2008,
    5656                                        'month' => 6,
    5757                                ),
     58                                'column' => 'post_date',
     59                                'compare' => '=',
     60                                'relation' => 'AND',
    5861                        ),
     62                        'column' => 'post_date',
     63                        'compare' => '=',
     64                        'relation' => 'AND',
    5965                );
    6066
    6167                $this->assertSame( $expected, $q->queries );
    class Tests_WP_Date_Query extends WP_UnitTestCase { 
    7379                        ),
    7480                ) );
    7581
    76                 // Note: WP_Date_Query does not reset indexes
    7782                $expected = array(
    78                         2 => array(
     83                        array(
    7984                                'before' => array(
    8085                                        'year' => 2008,
    8186                                        'month' => 6,
    8287                                ),
     88                                'column' => 'post_date',
     89                                'compare' => '=',
     90                                'relation' => 'AND',
    8391                        ),
     92                        'column' => 'post_date',
     93                        'compare' => '=',
     94                        'relation' => 'AND',
    8495                );
    8596
    86                 $this->assertSame( $expected, $q->queries );
     97                $this->assertEquals( $expected, $q->queries );
    8798        }
    8899
    89100        public function test_get_compare_empty() {
  • tests/phpunit/tests/query/dateQuery.php

    diff --git tests/phpunit/tests/query/dateQuery.php tests/phpunit/tests/query/dateQuery.php
    index 2f94c4a..0d519ce 100644
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    1515
    1616        public function setUp() {
    1717                parent::setUp();
    18 
    19                 // Be careful modifying this. Tests are coded to expect this exact sample data.
    20                 $post_dates = array(
    21                         '1972-05-24 14:53:45',
    22                         '1984-07-28 19:28:56',
    23                         '2003-05-27 22:45:07',
    24                         '2004-01-03 08:54:10',
    25                         '2004-05-22 12:34:12',
    26                         '2005-02-17 00:00:15',
    27                         '2005-12-31 23:59:20',
    28                         '2007-01-22 03:49:21',
    29                         '2007-05-16 17:32:22',
    30                         '2007-09-24 07:17:23',
    31                         '2008-03-29 09:04:25',
    32                         '2008-07-15 11:32:26',
    33                         '2008-12-10 13:06:27',
    34                         '2009-06-11 21:30:28',
    35                         '2009-12-18 10:42:29',
    36                         '2010-06-17 17:09:30',
    37                         '2011-02-23 12:12:31',
    38                         '2011-07-04 01:56:32',
    39                         '2011-12-12 16:39:33',
    40                         '2012-06-13 14:03:34',
    41                         '2025-04-20 10:13:00',
    42                         '2025-04-20 10:13:01',
    43                         '2025-05-20 10:13:01',
    44                 );
    45 
    46                 foreach ( $post_dates as $post_date ) {
    47                         $this->factory->post->create( array( 'post_date' => $post_date ) );
    48                 }
    49 
    5018                unset( $this->q );
    5119                $this->q = new WP_Query();
    5220        }
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    6533        }
    6634
    6735        public function test_date_query_before_array() {
     36                $this->create_posts();
     37
    6838                $posts = $this->_get_query_result( array(
    6939                        'date_query' => array(
    7040                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    9868         * their minimum values when being used with "before".
    9969         */
    10070        public function test_date_query_before_array_test_defaulting() {
     71                $this->create_posts();
     72
    10173                $posts = $this->_get_query_result( array(
    10274                        'date_query' => array(
    10375                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    12597        }
    12698
    12799        public function test_date_query_before_string() {
     100                $this->create_posts();
     101
    128102                $posts = $this->_get_query_result( array(
    129103                        'date_query' => array(
    130104                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    151125        }
    152126
    153127        public function test_date_query_after_array() {
     128                $this->create_posts();
     129
    154130                $posts = $this->_get_query_result( array(
    155131                        'date_query' => array(
    156132                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    182158         * their maximum values when being used with "after".
    183159         */
    184160        public function test_date_query_after_array_test_defaulting() {
     161                $this->create_posts();
     162
    185163                $posts = $this->_get_query_result( array(
    186164                        'date_query' => array(
    187165                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    209187        }
    210188
    211189        public function test_date_query_after_string() {
     190                $this->create_posts();
     191
    212192                $posts = $this->_get_query_result( array(
    213193                        'date_query' => array(
    214194                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    232212        }
    233213
    234214        public function test_date_query_after_string_inclusive() {
     215                $this->create_posts();
     216
    235217                $posts = $this->_get_query_result( array(
    236218                        'date_query' => array(
    237219                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    260242         * @ticket 26653
    261243         */
    262244        public function test_date_query_inclusive_between_dates() {
     245                $this->create_posts();
     246
    263247                $posts = $this->_get_query_result( array(
    264248                        'date_query' => array(
    265249                                'after' => array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    288272        }
    289273
    290274        public function test_date_query_year_expecting_results() {
     275                $this->create_posts();
     276
    291277                $posts = $this->_get_query_result( array(
    292278                        'date_query' => array(
    293279                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    305291        }
    306292
    307293        public function test_date_query_year_expecting_noresults() {
     294                $this->create_posts();
     295
    308296                $posts = $this->_get_query_result( array(
    309297                        'date_query' => array(
    310298                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    317305        }
    318306
    319307        public function test_date_query_month_expecting_results() {
     308                $this->create_posts();
     309
    320310                $posts = $this->_get_query_result( array(
    321311                        'date_query' => array(
    322312                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    336326        }
    337327
    338328        public function test_date_query_month_expecting_noresults() {
     329                $this->create_posts();
     330
    339331                $posts = $this->_get_query_result( array(
    340332                        'date_query' => array(
    341333                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    348340        }
    349341
    350342        public function test_date_query_week_expecting_results() {
     343                $this->create_posts();
     344
    351345                $posts = $this->_get_query_result( array(
    352346                        'date_query' => array(
    353347                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    364358        }
    365359
    366360        public function test_date_query_week_expecting_noresults() {
     361                $this->create_posts();
     362
    367363                $posts = $this->_get_query_result( array(
    368364                        'date_query' => array(
    369365                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    376372        }
    377373
    378374        public function test_date_query_day_expecting_results() {
     375                $this->create_posts();
     376
    379377                $posts = $this->_get_query_result( array(
    380378                        'date_query' => array(
    381379                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    393391        }
    394392
    395393        public function test_date_query_day_expecting_noresults() {
     394                $this->create_posts();
     395
    396396                $posts = $this->_get_query_result( array(
    397397                        'date_query' => array(
    398398                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    405405        }
    406406
    407407        public function test_date_query_dayofweek_expecting_results() {
     408                $this->create_posts();
     409
    408410                $posts = $this->_get_query_result( array(
    409411                        'date_query' => array(
    410412                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    425427        }
    426428
    427429        public function test_date_query_hour_expecting_results() {
     430                $this->create_posts();
     431
    428432                $posts = $this->_get_query_result( array(
    429433                        'date_query' => array(
    430434                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    441445        }
    442446
    443447        public function test_date_query_hour_expecting_noresults() {
     448                $this->create_posts();
     449
    444450                $posts = $this->_get_query_result( array(
    445451                        'date_query' => array(
    446452                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    453459        }
    454460
    455461        public function test_date_query_minute_expecting_results() {
     462                $this->create_posts();
     463
    456464                $posts = $this->_get_query_result( array(
    457465                        'date_query' => array(
    458466                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    469477        }
    470478
    471479        public function test_date_query_minute_expecting_noresults() {
     480                $this->create_posts();
     481
    472482                $posts = $this->_get_query_result( array(
    473483                        'date_query' => array(
    474484                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    481491        }
    482492
    483493        public function test_date_query_second_expecting_results() {
     494                $this->create_posts();
     495
    484496                $posts = $this->_get_query_result( array(
    485497                        'date_query' => array(
    486498                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    497509        }
    498510
    499511        public function test_date_query_second_expecting_noresults() {
     512                $this->create_posts();
     513
    500514                $posts = $this->_get_query_result( array(
    501515                        'date_query' => array(
    502516                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    509523        }
    510524
    511525        public function test_date_query_between_two_times() {
     526                $this->create_posts();
     527
    512528                $posts = $this->_get_query_result( array(
    513529                        'date_query' => array(
    514530                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    542558                $this->assertEquals( $expected_dates, wp_list_pluck( $posts, 'post_date' ) );
    543559        }
    544560
     561        public function test_date_query_one_nested_clause_one_unnested_clause() {
     562                $this->create_posts();
     563
     564                $posts = $this->_get_query_result( array(
     565                        'date_query' => array(
     566                                array(
     567                                        'hour'    => 9,
     568                                        'minute'  => 0,
     569                                        'compare' => '=',
     570                                ),
     571                                array(
     572                                        'hour'    => '17',
     573                                        'minute'  => '0',
     574                                        'compare' => '=',
     575                                ),
     576                        ),
     577                ) );
     578        }
     579
    545580        public function test_date_query_relation_or() {
     581                $this->create_posts();
     582
    546583                $posts = $this->_get_query_result( array(
    547584                        'date_query' => array(
    548585                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    564601                $this->assertEquals( $expected_dates, wp_list_pluck( $posts, 'post_date' ) );
    565602        }
    566603
     604        public function test_date_query_one_nested_query() {
     605                $this->create_posts();
     606
     607                $posts = $this->_get_query_result( array(
     608                        'date_query' => array(
     609                                'relation' => 'OR',
     610                                array(
     611                                        'relation' => 'AND',
     612                                        array(
     613                                                'year' => 2004,
     614                                        ),
     615                                        array(
     616                                                'month' => 1,
     617                                        ),
     618                                ),
     619                                array(
     620                                        'year' => 1984,
     621                                ),
     622                        ),
     623                ) );
     624
     625                $expected_dates = array(
     626                        '1984-07-28 19:28:56',
     627                        '2004-01-03 08:54:10',
     628                );
     629
     630                $this->assertEquals( $expected_dates, wp_list_pluck( $posts, 'post_date' ) );
     631        }
     632
     633        public function test_date_query_one_nested_query_multiple_columns_relation_and() {
     634                $p1 = $this->factory->post->create( array(
     635                        'post_date' => '2012-03-05 15:30:55',
     636                ) );
     637                $this->update_post_modified( $p1, '2014-11-03 14:43:00' );
     638
     639                $p2 = $this->factory->post->create( array(
     640                        'post_date' => '2012-05-05 15:30:55',
     641                ) );
     642                $this->update_post_modified( $p2, '2014-10-03 14:43:00' );
     643
     644                $p3 = $this->factory->post->create( array(
     645                        'post_date' => '2013-05-05 15:30:55',
     646                ) );
     647                $this->update_post_modified( $p3, '2014-10-03 14:43:00' );
     648
     649                $p4 = $this->factory->post->create( array(
     650                        'post_date' => '2012-02-05 15:30:55',
     651                ) );
     652                $this->update_post_modified( $p4, '2012-12-03 14:43:00' );
     653
     654                global $wpdb;
     655
     656                $q = new WP_Query( array(
     657                        'date_query' => array(
     658                                'relation' => 'AND',
     659                                array(
     660                                        'column' => 'post_date',
     661                                        array(
     662                                                'year' => 2012,
     663                                        ),
     664                                ),
     665                                array(
     666                                        'column' => 'post_modified',
     667                                        array(
     668                                                'year' => 2014,
     669                                        ),
     670                                ),
     671                        ),
     672                        'fields' => 'ids',
     673                        'update_post_meta_cache' => false,
     674                        'update_post_term_cache' => false,
     675                        'post_status' => 'publish',
     676                ) );
     677
     678                $expected = array(
     679                        $p1,
     680                        $p2,
     681                );
     682
     683                $this->assertEqualSets( $expected, $q->posts );
     684        }
     685
    567686        public function test_date_query_compare_greater_than_or_equal_to() {
     687                $this->create_posts();
     688
    568689                $posts = $this->_get_query_result( array(
    569690                        'date_query' => array(
    570691                                array(
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    590711        }
    591712
    592713        public function test_date_params_monthnum_m_duplicate() {
     714                $this->create_posts();
     715
    593716                $posts = $this->_get_query_result( array(
    594717                        'date_query' => array(
    595718                                'month' => 5,
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    607730
    608731                $this->assertEquals( $expected_dates, wp_list_pluck( $posts, 'post_date' ) );
    609732
    610                 $this->assertContains( "AND ( ( MONTH( post_date ) = 5 ) ) AND", $this->q->request );
     733                $this->assertContains( "MONTH( post_date ) = 5", $this->q->request );
    611734
    612                 $this->assertNotContains( "AND ( ( MONTH( post_date ) = 5 AND MONTH( post_date ) = 9 ) ) AND", $this->q->request );
     735                $this->assertNotContains( "MONTH( post_date ) = 5 AND MONTH( post_date ) = 9", $this->q->request );
    613736        }
    614737
    615738        public function test_date_params_week_w_duplicate() {
     739                $this->create_posts();
     740
    616741                $posts = $this->_get_query_result( array(
    617742                        'date_query' => array(
    618743                                'week' => 21,
    class Tests_Query_DateQuery extends WP_UnitTestCase { 
    629754
    630755                $this->assertEquals( $expected_dates, wp_list_pluck( $posts, 'post_date' ) );
    631756
    632                 $this->assertContains( "AND ( ( WEEK( post_date, 1 ) = 21 ) ) AND", $this->q->request );
     757                $this->assertContains( "WEEK( post_date, 1 ) = 21", $this->q->request );
     758
     759                $this->assertNotContains( "WEEK( post_date, 1 ) = 21 AND WEEK( post_date, 1 ) = 22", $this->q->request );
     760        }
     761
     762        /** Helpers **********************************************************/
     763
     764        protected function create_posts() {
     765                // Be careful modifying this. Tests are coded to expect this exact sample data.
     766                $post_dates = array(
     767                        '1972-05-24 14:53:45',
     768                        '1984-07-28 19:28:56',
     769                        '2003-05-27 22:45:07',
     770                        '2004-01-03 08:54:10',
     771                        '2004-05-22 12:34:12',
     772                        '2005-02-17 00:00:15',
     773                        '2005-12-31 23:59:20',
     774                        '2007-01-22 03:49:21',
     775                        '2007-05-16 17:32:22',
     776                        '2007-09-24 07:17:23',
     777                        '2008-03-29 09:04:25',
     778                        '2008-07-15 11:32:26',
     779                        '2008-12-10 13:06:27',
     780                        '2009-06-11 21:30:28',
     781                        '2009-12-18 10:42:29',
     782                        '2010-06-17 17:09:30',
     783                        '2011-02-23 12:12:31',
     784                        '2011-07-04 01:56:32',
     785                        '2011-12-12 16:39:33',
     786                        '2012-06-13 14:03:34',
     787                        '2025-04-20 10:13:00',
     788                        '2025-04-20 10:13:01',
     789                        '2025-05-20 10:13:01',
     790                );
     791
     792                foreach ( $post_dates as $post_date ) {
     793                        $this->factory->post->create( array( 'post_date' => $post_date ) );
     794                }
     795        }
    633796
    634                 $this->assertNotContains( "AND ( ( WEEK( post_date, 1 ) = 21 AND WEEK( post_date, 1 ) = 22 ) ) AND", $this->q->request );
     797        protected function update_post_modified( $post_id, $date ) {
     798                global $wpdb;
     799                return $wpdb->update(
     800                        $wpdb->posts,
     801                        array(
     802                                'post_modified' => $date,
     803                                'post_modified_gmt' => $date,
     804                        ),
     805                        array(
     806                                'ID' => $post_id,
     807                        ),
     808                        array(
     809                                '%s',
     810                                '%s',
     811                        ),
     812                        array(
     813                                '%d',
     814                        )
     815                );
    635816        }
    636 }
    637  No newline at end of file
     817}