Ticket #29642: 29642.3.patch
File 29642.3.patch, 27.8 KB (added by , 11 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..43d5aae
- + 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 */ 8 abstract 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 { 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/meta.php
diff --git src/wp-includes/meta.php src/wp-includes/meta.php index e3c43cf..3b452fc 100644
function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $ 853 853 * 854 854 * @since 3.2.0 855 855 */ 856 class WP_Meta_Query {856 class WP_Meta_Query extends WP_Recursive_Query { 857 857 /** 858 858 * List of metadata queries. A single query is an associative array: 859 859 * - 'key' string The meta key … … class WP_Meta_Query { 882 882 public $relation; 883 883 884 884 /** 885 * Constructor885 * Database table to query for the metadata. 886 886 * 887 * @param array $meta_query (optional) A meta query 887 * @access public 888 * @var string 889 */ 890 public $meta_table; 891 892 /** 893 * Column in meta_table that represents the ID of the object the metadata belongs to. 894 * 895 * @access public 896 * @var string 897 */ 898 public $meta_id_column; 899 900 /** 901 * Database table that where the metadata's objects are stored (eg $wpdb->users). 902 * 903 * @access public 904 * @var string 905 */ 906 public $primary_table; 907 908 /** 909 * Column in primary_table that represents the ID of the object. 910 * 911 * @access public 912 * @var string 913 */ 914 public $primary_id_column; 915 916 /** 917 * Constructor. 918 * 919 * @param array $meta_query (optional) A meta query. 888 920 */ 889 921 public function __construct( $meta_query = false ) { 890 922 if ( !$meta_query ) … … class WP_Meta_Query { 896 928 $this->relation = 'AND'; 897 929 } 898 930 899 $this->queries = array(); 931 $this->queries = $this->sanitize_query( $meta_query ); 932 } 933 934 /** 935 * Ensure that the meta_query argument passed to the class constructor is well-formed. 936 * 937 * Eliminates empty items and ensures that a 'relation' is set. 938 * 939 * @param array $queries 940 * @return array 941 */ 942 public function sanitize_query( $queries ) { 943 $clean_queries = array(); 944 945 if ( ! is_array( $queries ) ) { 946 return $clean_queries; 947 } 948 949 foreach ( $queries as $key => $query ) { 950 if ( 'relation' === $key ) { 951 $relation = $query; 952 953 // First-order clause. 954 } else if ( $this->is_first_order_clause( $query ) ) { 955 $clean_queries[] = $query; 956 957 // Otherwise, it's a nested query. 958 } else { 959 $cleaned_query = $this->sanitize_query( $query ); 960 961 if ( ! empty( $cleaned_query ) ) { 962 $clean_queries[] = $cleaned_query; 963 } 964 } 965 } 966 967 if ( empty( $clean_queries ) ) { 968 return $clean_queries; 969 } 970 971 // Sanitize the 'relation' key provided in the query. 972 if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) { 973 $clean_queries['relation'] = 'OR'; 900 974 901 foreach ( $meta_query as $key => $query ) { 902 if ( ! is_array( $query ) ) 903 continue; 975 // If there is only a single clause, call the relation 'OR'. 976 // This value will not actually be used to join clauses, but it 977 // simplifies the logic around combining key-only queries. 978 } else if ( 1 === count( $clean_queries ) ) { 979 $clean_queries['relation'] = 'OR'; 904 980 905 $this->queries[] = $query; 981 // Default to AND. 982 } else { 983 $clean_queries['relation'] = 'AND'; 906 984 } 985 986 return $clean_queries; 987 } 988 989 protected function is_first_order_clause( $query ) { 990 return isset( $query['key'] ) || isset( $query['value'] ); 907 991 } 908 992 909 993 /** … … class WP_Meta_Query { 958 1042 } 959 1043 960 1044 /** 961 * Generate s SQL clauses to be appended to a main query.1045 * Generate SQL JOIN and WHERE clauses for a first-order query clause. 962 1046 * 963 * @since 3.2.0 964 * @access public 1047 * "First-order" means that it's an array with a 'key' or 'value'. 965 1048 * 966 * @param string $type Type of meta 967 * @param string $primary_table 968 * @param string $primary_id_column 969 * @param object $context (optional) The main query object 970 * @return array( 'join' => $join_sql, 'where' => $where_sql ) 1049 * @param array $clause Query clause. 1050 * @param array $parent_query Parent query array. 1051 * @return array 971 1052 */ 972 public function get_sql ( $type, $primary_table, $primary_id_column, $context = null) {1053 public function get_sql_for_clause( $clause, $parent_query ) { 973 1054 global $wpdb; 974 1055 975 if ( ! $meta_table = _get_meta_table( $type ) ) 976 return false; 977 978 $meta_id_column = sanitize_key( $type . '_id' ); 979 980 $join = array(); 981 $where = array(); 1056 $sql_chunks = array( 1057 'where' => array(), 1058 'join' => array(), 1059 ); 982 1060 983 $ key_only_queries = array();984 $ queries = array();1061 $i = count( $this->table_aliases ); 1062 $alias = $i ? 'mt' . $i : $this->meta_table; 985 1063 986 // Split out the queries with empty arrays as value 987 foreach ( $this->queries as $k => $q ) { 988 if ( isset( $q['value'] ) && is_array( $q['value'] ) && empty( $q['value'] ) ) { 989 $key_only_queries[$k] = $q; 990 unset( $this->queries[$k] ); 991 } 992 } 993 994 // Split out the meta_key only queries (we can only do this for OR) 995 if ( 'OR' == $this->relation ) { 996 foreach ( $this->queries as $k => $q ) { 997 if ( ( empty( $q['compare'] ) || 'NOT EXISTS' != $q['compare'] ) && ! array_key_exists( 'value', $q ) && ! empty( $q['key'] ) ) 998 $key_only_queries[$k] = $q; 999 else 1000 $queries[$k] = $q; 1001 } 1064 if ( isset( $clause['compare'] ) ) { 1065 $meta_compare = strtoupper( $clause['compare'] ); 1002 1066 } else { 1003 $ queries = $this->queries;1067 $meta_compare = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '='; 1004 1068 } 1005 1069 1006 // Specify all the meta_key only queries in one go 1007 if ( $key_only_queries ) { 1008 $join[] = "INNER JOIN $meta_table ON $primary_table.$primary_id_column = $meta_table.$meta_id_column"; 1009 1010 foreach ( $key_only_queries as $key => $q ) 1011 $where["key-only-$key"] = $wpdb->prepare( "$meta_table.meta_key = %s", trim( $q['key'] ) ); 1070 if ( ! in_array( $meta_compare, array( 1071 '=', '!=', '>', '>=', '<', '<=', 1072 'LIKE', 'NOT LIKE', 1073 'IN', 'NOT IN', 1074 'BETWEEN', 'NOT BETWEEN', 1075 'EXISTS', 'NOT EXISTS', 1076 'REGEXP', 'NOT REGEXP', 'RLIKE' 1077 ) ) ) { 1078 $meta_compare = '='; 1012 1079 } 1013 1080 1014 foreach ( $queries as $k => $q ) { 1015 $meta_key = isset( $q['key'] ) ? trim( $q['key'] ) : ''; 1016 $meta_type = $this->get_cast_for_type( isset( $q['type'] ) ? $q['type'] : '' ); 1017 1018 if ( array_key_exists( 'value', $q ) && is_null( $q['value'] ) ) 1019 $q['value'] = ''; 1020 1021 $meta_value = isset( $q['value'] ) ? $q['value'] : null; 1022 1023 if ( isset( $q['compare'] ) ) 1024 $meta_compare = strtoupper( $q['compare'] ); 1025 else 1026 $meta_compare = is_array( $meta_value ) ? 'IN' : '='; 1027 1028 if ( ! in_array( $meta_compare, array( 1029 '=', '!=', '>', '>=', '<', '<=', 1030 'LIKE', 'NOT LIKE', 1031 'IN', 'NOT IN', 1032 'BETWEEN', 'NOT BETWEEN', 1033 'NOT EXISTS', 1034 'REGEXP', 'NOT REGEXP', 'RLIKE' 1035 ) ) ) 1036 $meta_compare = '='; 1037 1038 $i = count( $join ); 1039 $alias = $i ? 'mt' . $i : $meta_table; 1040 1041 if ( 'NOT EXISTS' == $meta_compare ) { 1042 $join[$i] = "LEFT JOIN $meta_table"; 1043 $join[$i] .= $i ? " AS $alias" : ''; 1044 $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column AND $alias.meta_key = '$meta_key')"; 1045 1046 $where[$k] = ' ' . $alias . '.' . $meta_id_column . ' IS NULL'; 1047 1048 continue; 1081 // There are a number of different query structures that get 1082 // built in different ways. 1083 // 1. Key-only clauses - (a) clauses without a 'value' key that 1084 // appear in the context of an OR relation and do not use 1085 // 'NOT EXISTS' as the 'compare', or (b) clauses with an 1086 // empty array for 'value'. 1087 if ( ! empty( $clause['key'] ) && ( 1088 ( ! array_key_exists( 'value', $clause ) && 'NOT EXISTS' !== $meta_compare && 'OR' === $parent_query['relation'] ) || 1089 ( isset( $clause['value'] ) && is_array( $clause['value'] ) && empty( $clause['value'] ) ) 1090 ) ) { 1091 1092 $alias = $this->meta_table; 1093 $sql_chunks['join'][] = " INNER JOIN $this->meta_table ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)"; 1094 $sql_chunks['where'][] = $wpdb->prepare( "$this->meta_table.meta_key = %s", trim( $clause['key'] ) ); 1095 1096 // 2. NOT EXISTS. 1097 } else if ( 'NOT EXISTS' === $meta_compare ) { 1098 $join = " LEFT JOIN $this->meta_table"; 1099 $join .= $i ? " AS $alias" : ''; 1100 $join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] ); 1101 $sql_chunks['join'][] = $join; 1102 1103 $sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL'; 1104 1105 // 3. EXISTS and other key-only queries. 1106 } else if ( 'EXISTS' === $meta_compare || ( ! empty( $clause['key'] ) && ! array_key_exists( 'value', $clause ) ) ) { 1107 $join = " INNER JOIN $this->meta_table"; 1108 $join .= $i ? " AS $alias" : ''; 1109 $join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )"; 1110 $sql_chunks['join'][] = $join; 1111 1112 $sql_chunks['where'][] = $wpdb->prepare( $alias . '.meta_key = %s', trim( $clause['key'] ) ); 1113 1114 // 4. Clauses that have a value. 1115 } else if ( array_key_exists( 'value', $clause ) ) { 1116 $join = " INNER JOIN $this->meta_table"; 1117 $join .= $i ? " AS $alias" : ''; 1118 $join .= " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)"; 1119 $sql_chunks['join'][] = $join; 1120 1121 if ( ! empty( $clause['key'] ) ) { 1122 $sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) ); 1049 1123 } 1050 1124 1051 $join[$i] = "INNER JOIN $meta_table"; 1052 $join[$i] .= $i ? " AS $alias" : ''; 1053 $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column)"; 1054 1055 $where[$k] = ''; 1056 if ( !empty( $meta_key ) ) 1057 $where[$k] = $wpdb->prepare( "$alias.meta_key = %s", $meta_key ); 1125 $meta_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' ); 1058 1126 1059 if ( is_null( $meta_value ) ) { 1060 if ( empty( $where[$k] ) ) 1061 unset( $join[$i] ); 1062 continue; 1063 } 1127 $meta_value = isset( $clause['value'] ) ? $clause['value'] : ''; 1064 1128 1065 1129 if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) { 1066 if ( ! is_array( $meta_value ) ) 1130 if ( ! is_array( $meta_value ) ) { 1067 1131 $meta_value = preg_split( '/[,\s]+/', $meta_value ); 1068 1069 if ( empty( $meta_value ) ) {1070 unset( $join[$i] );1071 continue;1072 1132 } 1073 1133 } else { 1074 1134 $meta_value = trim( $meta_value ); 1075 1135 } 1076 1136 1077 if ( 'IN' == substr( $meta_compare, -2 ) ) {1137 if ( 'IN' == substr( $meta_compare, -2 ) ) { 1078 1138 $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')'; 1079 } elseif ( 'BETWEEN' == substr( $meta_compare, -7 ) ) {1139 } elseif ( 'BETWEEN' == substr( $meta_compare, -7 ) ) { 1080 1140 $meta_value = array_slice( $meta_value, 0, 2 ); 1081 1141 $meta_compare_string = '%s AND %s'; 1082 1142 } elseif ( 'LIKE' == $meta_compare || 'NOT LIKE' == $meta_compare ) { … … class WP_Meta_Query { 1086 1146 $meta_compare_string = '%s'; 1087 1147 } 1088 1148 1089 if ( ! empty( $where[$k] ) )1090 $where[$k] .= ' AND ';1149 $sql_chunks['where'][] = $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string}", $meta_value ); 1150 } 1091 1151 1092 $where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value ); 1152 // Multiple WHERE clauses (for meta_key and meta_value) should 1153 // be joined in parentheses. 1154 if ( 1 < count( $sql_chunks['where'] ) ) { 1155 $sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' ); 1093 1156 } 1094 1157 1095 $ where = array_filter( $where );1158 $this->table_aliases[] = $alias; 1096 1159 1097 if ( empty( $where ) ) 1098 $where = ''; 1099 else 1100 $where = ' AND (' . implode( "\n{$this->relation} ", $where ) . ' )'; 1160 return $sql_chunks; 1161 } 1162 1163 /** 1164 * Generates SQL clauses to be appended to a main query. 1165 * 1166 * @since 3.2.0 1167 * @access public 1168 * 1169 * @param string $type Type of meta, eg 'user', 'post'. 1170 * @param string $primary_table Database table where the object being 1171 * filtered is stored (eg wp_users). 1172 * @param string $primary_id_column ID column for the filtered object in $primary_table 1173 * @param object $context Optional. The main query object. 1174 * @return array { 1175 * @type string $join SQL fragment to append to the main JOIN clause. 1176 * @type string $where SQL fragment to append to the main WHERE clause. 1177 * } 1178 */ 1179 public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) { 1180 global $wpdb; 1181 1182 if ( ! $meta_table = _get_meta_table( $type ) ) { 1183 return false; 1184 } 1185 1186 $this->meta_table = $meta_table; 1187 $this->meta_id_column = sanitize_key( $type . '_id' ); 1188 1189 $this->primary_table = $primary_table; 1190 $this->primary_id_column = $primary_id_column; 1101 1191 1102 $join = implode( "\n", $join ); 1103 if ( ! empty( $join ) ) 1104 $join = ' ' . $join; 1192 $sql = $this->get_sql_clauses(); 1105 1193 1106 1194 /** 1107 1195 * Filter the meta query's generated SQL. … … class WP_Meta_Query { 1119 1207 * @type object $context The main query object. 1120 1208 * } 1121 1209 */ 1122 return apply_filters_ref_array( 'get_meta_sql', array( compact( 'join', 'where' ), $this->queries, $type, $primary_table, $primary_id_column, $context ) );1210 return apply_filters_ref_array( 'get_meta_sql', array( $sql, $this->queries, $type, $primary_table, $primary_id_column, $context ) ); 1123 1211 } 1124 1212 } 1125 1213 -
src/wp-settings.php
diff --git src/wp-settings.php src/wp-settings.php index 9795971..5dca996 100644
wp_not_installed(); 111 111 // Load most of WordPress. 112 112 require( ABSPATH . WPINC . '/class-wp-walker.php' ); 113 113 require( ABSPATH . WPINC . '/class-wp-ajax-response.php' ); 114 require( ABSPATH . WPINC . '/class-wp-recursive-query.php' ); 114 115 require( ABSPATH . WPINC . '/formatting.php' ); 115 116 require( ABSPATH . WPINC . '/capabilities.php' ); 116 117 require( ABSPATH . WPINC . '/query.php' ); -
tests/phpunit/tests/meta/query.php
diff --git tests/phpunit/tests/meta/query.php tests/phpunit/tests/meta/query.php index fb2a1cc..390ccc9 100644
class Tests_Meta_Query extends WP_UnitTestCase { 40 40 array(), 41 41 ) ); 42 42 43 $this->assertSame( array( array()), $query->queries );43 $this->assertSame( array(), $query->queries ); 44 44 } 45 45 46 46 /** … … class Tests_Meta_Query extends WP_UnitTestCase { 153 153 $query = new WP_Meta_Query(); 154 154 155 155 // just meta_value 156 $query->parse_query_vars( array( 'meta_key' => 'abc' ) ); 157 158 $this->assertEquals( array( array( 'key' => 'abc' ) ), $query->queries ); 156 $expected = array( 157 'relation' => 'OR', 158 array( 159 'key' => 'abc', 160 ), 161 ); 162 $query->parse_query_vars( array( 163 'meta_key' => 'abc', 164 ) ); 165 $this->assertEquals( $expected, $query->queries ); 159 166 160 167 // meta_key & meta_value 161 $query->parse_query_vars( array( 'meta_key' => 'abc', 'meta_value' => 'def' ) ); 162 163 $this->assertEquals( array( array( 'key' => 'abc', 'value' => 'def' ) ), $query->queries ); 168 $expected = array( 169 'relation' => 'OR', 170 array( 171 'key' => 'abc', 172 'value' => 'def', 173 ), 174 ); 175 $query->parse_query_vars( array( 176 'meta_key' => 'abc', 177 'meta_value' => 'def', 178 ) ); 179 $this->assertEquals( $expected, $query->queries ); 164 180 165 181 // meta_compare 166 $query->parse_query_vars( array( 'meta_key' => 'abc', 'meta_compare' => '=>' ) ); 167 168 $this->assertEquals( array( array( 'key' => 'abc', 'compare' => '=>' ) ), $query->queries ); 182 $expected = array( 183 'relation' => 'OR', 184 array( 185 'key' => 'abc', 186 'compare' => '=>', 187 ), 188 ); 189 $query->parse_query_vars( array( 190 'meta_key' => 'abc', 191 'meta_compare' => '=>', 192 ) ); 193 $this->assertEquals( $expected, $query->queries ); 169 194 } 170 195 171 196 /** … … class Tests_Meta_Query extends WP_UnitTestCase { 202 227 $this->assertEquals( 'CHAR', $query->get_cast_for_type( 'ANYTHING ELSE' ) ); 203 228 } 204 229 230 public function test_sanitize_query_single_query() { 231 $expected = array( 232 'relation' => 'OR', 233 array( 234 'key' => 'foo', 235 'value' => 'bar', 236 ), 237 ); 238 239 $q = new WP_Meta_Query(); 240 $found = $q->sanitize_query( array( 241 array( 242 'key' => 'foo', 243 'value' => 'bar', 244 ), 245 ) ); 246 247 $this->assertEquals( $expected, $found ); 248 } 249 250 public function test_sanitize_query_multiple_first_order_queries_relation_default() { 251 $expected = array( 252 'relation' => 'AND', 253 array( 254 'key' => 'foo', 255 'value' => 'bar', 256 ), 257 array( 258 'key' => 'foo2', 259 'value' => 'bar2', 260 ), 261 ); 262 263 $q = new WP_Meta_Query(); 264 $found = $q->sanitize_query( array( 265 array( 266 'key' => 'foo', 267 'value' => 'bar', 268 ), 269 array( 270 'key' => 'foo2', 271 'value' => 'bar2', 272 ), 273 ) ); 274 275 $this->assertEquals( $expected, $found ); 276 } 277 278 public function test_sanitize_query_multiple_first_order_queries_relation_or() { 279 $expected = array( 280 'relation' => 'OR', 281 array( 282 'key' => 'foo', 283 'value' => 'bar', 284 ), 285 array( 286 'key' => 'foo2', 287 'value' => 'bar2', 288 ), 289 ); 290 291 $q = new WP_Meta_Query(); 292 $found = $q->sanitize_query( array( 293 'relation' => 'OR', 294 array( 295 'key' => 'foo', 296 'value' => 'bar', 297 ), 298 array( 299 'key' => 'foo2', 300 'value' => 'bar2', 301 ), 302 ) ); 303 304 $this->assertEquals( $expected, $found ); 305 } 306 307 public function test_sanitize_query_multiple_first_order_queries_relation_or_lowercase() { 308 $expected = array( 309 'relation' => 'OR', 310 array( 311 'key' => 'foo', 312 'value' => 'bar', 313 ), 314 array( 315 'key' => 'foo2', 316 'value' => 'bar2', 317 ), 318 ); 319 320 $q = new WP_Meta_Query(); 321 $found = $q->sanitize_query( array( 322 'relation' => 'or', 323 array( 324 'key' => 'foo', 325 'value' => 'bar', 326 ), 327 array( 328 'key' => 'foo2', 329 'value' => 'bar2', 330 ), 331 ) ); 332 333 $this->assertEquals( $expected, $found ); 334 } 335 336 public function test_sanitize_query_multiple_first_order_queries_invalid_relation() { 337 $expected = array( 338 'relation' => 'AND', 339 array( 340 'key' => 'foo', 341 'value' => 'bar', 342 ), 343 array( 344 'key' => 'foo2', 345 'value' => 'bar2', 346 ), 347 ); 348 349 $q = new WP_Meta_Query(); 350 $found = $q->sanitize_query( array( 351 'relation' => 'FOO', 352 array( 353 'key' => 'foo', 354 'value' => 'bar', 355 ), 356 array( 357 'key' => 'foo2', 358 'value' => 'bar2', 359 ), 360 ) ); 361 362 $this->assertEquals( $expected, $found ); 363 } 364 365 public function test_sanitize_query_single_query_which_is_a_nested_query() { 366 $expected = array( 367 'relation' => 'OR', 368 array( 369 'relation' => 'AND', 370 array( 371 'key' => 'foo', 372 'value' => 'bar', 373 ), 374 array( 375 'key' => 'foo2', 376 'value' => 'bar2', 377 ), 378 ) 379 ); 380 381 $q = new WP_Meta_Query(); 382 $found = $q->sanitize_query( array( 383 array( 384 array( 385 'key' => 'foo', 386 'value' => 'bar', 387 ), 388 array( 389 'key' => 'foo2', 390 'value' => 'bar2', 391 ), 392 ), 393 ) ); 394 395 $this->assertEquals( $expected, $found ); 396 } 397 398 public function test_sanitize_query_multiple_nested_queries() { 399 $expected = array( 400 'relation' => 'OR', 401 array( 402 'relation' => 'AND', 403 array( 404 'key' => 'foo', 405 'value' => 'bar', 406 ), 407 array( 408 'key' => 'foo2', 409 'value' => 'bar2', 410 ), 411 ), 412 array( 413 'relation' => 'AND', 414 array( 415 'key' => 'foo3', 416 'value' => 'bar3', 417 ), 418 array( 419 'key' => 'foo4', 420 'value' => 'bar4', 421 ), 422 ), 423 ); 424 425 $q = new WP_Meta_Query(); 426 $found = $q->sanitize_query( array( 427 'relation' => 'OR', 428 array( 429 array( 430 'key' => 'foo', 431 'value' => 'bar', 432 ), 433 array( 434 'key' => 'foo2', 435 'value' => 'bar2', 436 ), 437 ), 438 array( 439 array( 440 'key' => 'foo3', 441 'value' => 'bar3', 442 ), 443 array( 444 'key' => 'foo4', 445 'value' => 'bar4', 446 ), 447 ), 448 ) ); 449 450 $this->assertEquals( $expected, $found ); 451 } 452 205 453 /** 206 454 * Invalid $type will fail to get a table from _get_meta_table() 207 455 */ … … class Tests_Meta_Query extends WP_UnitTestCase { 229 477 $sql = $query->get_sql( 'post', $wpdb->posts, 'ID', $this ); 230 478 231 479 // We should have 2 joins - one for my_first_key and one for my_second_key 232 $this->assertEquals( 2, substr_count( $sql['join'], ' INNERJOIN' ) );480 $this->assertEquals( 2, substr_count( $sql['join'], 'JOIN' ) ); 233 481 234 482 // The WHERE should check my_third_key against an unaliased table 235 483 $this->assertEquals( 1, substr_count( $sql['where'], "$wpdb->postmeta.meta_key = 'my_third_key'" ) ); … … class Tests_Meta_Query extends WP_UnitTestCase { 247 495 ) ); 248 496 $sql = $query->get_sql( 'post', $wpdb->posts, 'ID', $this ); 249 497 250 $this->assertEquals( 1, substr_count( $sql['where'], "CAST($wpdb->postmeta.meta_value AS CHAR) = '' )" ) );498 $this->assertEquals( 1, substr_count( $sql['where'], "CAST($wpdb->postmeta.meta_value AS CHAR) = ''" ) ); 251 499 } 252 500 253 501 /** … … class Tests_Meta_Query extends WP_UnitTestCase { 558 806 ) ); 559 807 560 808 $sql = $query->get_sql( 'post', $wpdb->posts, 'ID', $this ); 561 $this->assertContains( "{$wpdb->postmeta}.meta_key = 'exclude'\nOR", $sql['where'] ); 809 810 // Use regex because we don't care about the whitespace before OR. 811 $this->assertRegExp( "/{$wpdb->postmeta}\.meta_key = \'exclude\'\s+OR/", $sql['where'] ); 562 812 $this->assertNotContains( "{$wpdb->postmeta}.post_id IS NULL", $sql['where'] ); 563 813 } 564 814 } -
tests/phpunit/tests/post/query.php
diff --git tests/phpunit/tests/post/query.php tests/phpunit/tests/post/query.php index 45b1abc..03edcf9 100644
class Tests_Post_Query extends WP_UnitTestCase { 772 772 $this->assertEqualSets( array( $post_4, $post_3, $post_2, $post_1 ), $query->posts ); 773 773 } 774 774 775 public function test_meta_query_nested() { 776 $p1 = $this->factory->post->create(); 777 $p2 = $this->factory->post->create(); 778 $p3 = $this->factory->post->create(); 779 780 add_post_meta( $p1, 'foo', 'bar' ); 781 add_post_meta( $p2, 'foo2', 'bar' ); 782 add_post_meta( $p3, 'foo2', 'bar' ); 783 add_post_meta( $p3, 'foo3', 'bar' ); 784 785 $query = new WP_Query( array( 786 'update_post_meta_cache' => false, 787 'update_term_meta_cache' => false, 788 'fields' => 'ids', 789 'meta_query' => array( 790 'relation' => 'OR', 791 array( 792 'key' => 'foo', 793 'value' => 'bar', 794 ), 795 array( 796 'relation' => 'AND', 797 array( 798 'key' => 'foo2', 799 'value' => 'bar', 800 ), 801 array( 802 'key' => 'foo3', 803 'value' => 'bar', 804 ), 805 ), 806 ), 807 ) ); 808 809 $expected = array( $p1, $p3 ); 810 $this->assertEqualSets( $expected, $query->posts ); 811 } 812 813 public function test_meta_query_nested_two_levels_deep() { 814 $p1 = $this->factory->post->create(); 815 $p2 = $this->factory->post->create(); 816 $p3 = $this->factory->post->create(); 817 818 add_post_meta( $p1, 'foo', 'bar' ); 819 add_post_meta( $p3, 'foo2', 'bar' ); 820 add_post_meta( $p3, 'foo3', 'bar' ); 821 add_post_meta( $p3, 'foo4', 'bar' ); 822 823 $query = new WP_Query( array( 824 'update_post_meta_cache' => false, 825 'update_term_meta_cache' => false, 826 'fields' => 'ids', 827 'meta_query' => array( 828 'relation' => 'OR', 829 array( 830 'key' => 'foo', 831 'value' => 'bar', 832 ), 833 array( 834 'relation' => 'OR', 835 array( 836 'key' => 'foo2', 837 'value' => 'bar', 838 ), 839 array( 840 'relation' => 'AND', 841 array( 842 'key' => 'foo3', 843 'value' => 'bar', 844 ), 845 array( 846 'key' => 'foo4', 847 'value' => 'bar', 848 ), 849 ), 850 ), 851 ), 852 ) ); 853 854 $expected = array( $p1, $p3 ); 855 $this->assertEqualSets( $expected, $query->posts ); 856 } 857 775 858 /** 776 859 * @ticket 20604 777 860 */