Index: wp-includes/taxonomy.php
===================================================================
--- wp-includes/taxonomy.php	(revision 16400)
+++ wp-includes/taxonomy.php	(working copy)
@@ -554,61 +554,6 @@
 	return $do_query ? $wpdb->get_col( $sql ) : $sql;
 }
 
-/*
- * Given a meta query, generates SQL to be appended to a main query
- *
- * @since 3.1.0
- *
- * @param array $tax_query List of taxonomy queries. A single taxonomy query is an associative array:
- * - 'taxonomy' string|array The taxonomy being queried
- * - 'terms' string|array The list of terms
- * - 'field' string (optional) Which term field is being used.
- *		Possible values: 'term_id', 'slug' or 'name'
- *		Default: 'term_id'
- * - 'operator' string (optional)
- *		Possible values: 'IN' and 'NOT IN'.
- *		Default: 'IN'
- * - 'include_children' bool (optional) Whether to include child terms.
- *		Default: true
- *
- * @param string $object_id_column
- * @return string
- */
-function get_tax_sql( $tax_query, $object_id_column ) {
-	global $wpdb;
-
-	$sql = array();
-	foreach ( $tax_query as $query ) {
-		if ( !isset( $query['include_children'] ) )
-			$query['include_children'] = true;
-
-		$query['do_query'] = false;
-
-		$sql_single = get_objects_in_term( $query['terms'], $query['taxonomy'], $query );
-
-		if ( empty( $sql_single ) || is_wp_error( $sql_single ) )
-			return ' AND 0 = 1';
-
-		$sql[] = $sql_single;
-	}
-
-	if ( 1 == count( $sql ) ) {
-		$ids = $wpdb->get_col( $sql[0] );
-	} else {
-		$r = "SELECT object_id FROM $wpdb->term_relationships WHERE 1=1";
-		foreach ( $sql as $query )
-			$r .= " AND object_id IN ($query)";
-
-		$ids = $wpdb->get_col( $r );
-	}
-
-	if ( !empty( $ids ) )
-		return " AND $object_id_column IN(" . implode( ', ', $ids ) . ")";
-	else
-		return ' AND 0 = 1';
-}
-
-
 /**
  * Get all Term data from database by Term ID.
  *
Index: wp-includes/query.php
===================================================================
--- wp-includes/query.php	(revision 16400)
+++ wp-includes/query.php	(working copy)
@@ -1345,8 +1345,95 @@
 				$this->is_date = true;
 			}
 
-			$this->parse_tax_query( $qv );
+			if ( empty($qv['cat']) || ($qv['cat'] == '0') ) {
+				$this->is_category = false;
+			} else {
+				if ( strpos($qv['cat'], '-') !== false ) {
+					$this->is_category = false;
+				} else {
+					$this->is_category = true;
+				}
+			}
 
+			if ( '' != $qv['category_name'] ) {
+				$this->is_category = true;
+			}
+
+			if ( !is_array($qv['category__in']) || empty($qv['category__in']) ) {
+				$qv['category__in'] = array();
+			} else {
+				$qv['category__in'] = array_map('absint', $qv['category__in']);
+				$this->is_category = true;
+			}
+
+			if ( !is_array($qv['category__not_in']) || empty($qv['category__not_in']) ) {
+				$qv['category__not_in'] = array();
+			} else {
+				$qv['category__not_in'] = array_map('absint', $qv['category__not_in']);
+			}
+
+			if ( !is_array($qv['category__and']) || empty($qv['category__and']) ) {
+				$qv['category__and'] = array();
+			} else {
+				$qv['category__and'] = array_map('absint', $qv['category__and']);
+				$this->is_category = true;
+			}
+
+			if (  '' != $qv['tag'] )
+				$this->is_tag = true;
+
+			$qv['tag_id'] = absint($qv['tag_id']);
+			if (  !empty($qv['tag_id']) )
+				$this->is_tag = true;
+
+			if ( !is_array($qv['tag__in']) || empty($qv['tag__in']) ) {
+				$qv['tag__in'] = array();
+			} else {
+				$qv['tag__in'] = array_map('absint', $qv['tag__in']);
+				$this->is_tag = true;
+			}
+
+			if ( !is_array($qv['tag__not_in']) || empty($qv['tag__not_in']) ) {
+				$qv['tag__not_in'] = array();
+			} else {
+				$qv['tag__not_in'] = array_map('absint', $qv['tag__not_in']);
+			}
+
+			if ( !is_array($qv['tag__and']) || empty($qv['tag__and']) ) {
+				$qv['tag__and'] = array();
+			} else {
+				$qv['tag__and'] = array_map('absint', $qv['tag__and']);
+				$this->is_category = true;
+			}
+
+			if ( !is_array($qv['tag_slug__in']) || empty($qv['tag_slug__in']) ) {
+				$qv['tag_slug__in'] = array();
+			} else {
+				$qv['tag_slug__in'] = array_map('sanitize_title', $qv['tag_slug__in']);
+				$this->is_tag = true;
+			}
+
+			if ( !is_array($qv['tag_slug__and']) || empty($qv['tag_slug__and']) ) {
+				$qv['tag_slug__and'] = array();
+			} else {
+				$qv['tag_slug__and'] = array_map('sanitize_title', $qv['tag_slug__and']);
+				$this->is_tag = true;
+			}
+
+			if ( empty($qv['taxonomy']) || empty($qv['term']) ) {
+				$this->is_tax = false;
+				foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
+					if ( $t->query_var && isset($qv[$t->query_var]) && '' != $qv[$t->query_var] ) {
+						$qv['taxonomy'] = $taxonomy;
+						$qv['term'] = $qv[$t->query_var];
+						$this->is_tax = true;
+						break;
+					}
+				}
+			} else {
+				$this->is_tax = true;
+			}
+
 			_parse_meta_query( $qv );
 
 			if ( empty($qv['author']) || ($qv['author'] == '0') ) {
@@ -1460,145 +1547,6 @@
 			do_action_ref_array('parse_query', array(&$this));
 	}
 
-	/*
-	 * Parses various taxonomy related query vars and sets the appropriate query flags
-	 *
-	 * @access protected
-	 * @since 3.1.0
-	 *
-	 * @param array &$q The query variables
-	 * @return array tax query
-	 */
-	function parse_tax_query( &$q ) {
-		if ( ! empty( $q['tax_query'] ) && is_array( $q['tax_query'] ) ) {
-			$tax_query = $q['tax_query'];
-		} else {
-			$tax_query = array();
-		}
-
-		if ( !empty($q['taxonomy']) && !empty($q['term']) ) {
-			$tax_query[] = array(
-				'taxonomy' => $q['taxonomy'],
-				'terms' => array( $q['term'] ),
-				'field' => 'slug',
-				'operator' => 'IN',
-			);
-		}
-
-		foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
-			if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
-				$tax_query_defaults = array(
-					'taxonomy' => $taxonomy,
-					'field' => 'slug',
-					'operator' => 'IN'
-				);
-
-				if ( $t->rewrite['hierarchical'] ) {
-					$q[$t->query_var] = wp_basename( $q[$t->query_var] );
-				}
-
-				$term = str_replace( ' ', '+', $q[$t->query_var] );
-
-				if ( strpos($term, '+') !== false ) {
-					$terms = preg_split( '/[+]+/', $term );
-					foreach ( $terms as $term ) {
-						$tax_query[] = array_merge( $tax_query_defaults, array(
-							'terms' => array( $term )
-						) );
-					}
-				} else {
-					$tax_query[] = array_merge( $tax_query_defaults, array(
-						'terms' => preg_split( '/[,]+/', $term )
-					) );
-				}
-			}
-		}
-
-		// Category stuff
-		if ( !empty($q['cat']) && '0' != $q['cat'] && !$this->is_singular ) {
-			$q['cat'] = ''.urldecode($q['cat']).'';
-			$q['cat'] = addslashes_gpc($q['cat']);
-			$cat_array = preg_split('/[,\s]+/', $q['cat']);
-			$q['cat'] = '';
-			$req_cats = array();
-			foreach ( (array) $cat_array as $cat ) {
-				$cat = intval($cat);
-				$req_cats[] = $cat;
-				$in = ($cat > 0);
-				$cat = abs($cat);
-				if ( $in ) {
-					$q['category__in'][] = $cat;
-				} else {
-					$q['category__not_in'][] = $cat;
-				}
-			}
-			$q['cat'] = implode(',', $req_cats);
-		}
-
-		if ( !empty($q['category__in']) ) {
-			$tax_query[] = array(
-				'taxonomy' => 'category',
-				'terms' => $q['category__in'],
-				'operator' => 'IN',
-				'field' => 'term_id'
-			);
-		}
-
-		if ( !empty($q['category__not_in']) ) {
-			$tax_query[] = array(
-				'taxonomy' => 'category',
-				'terms' => $q['category__not_in'],
-				'operator' => 'NOT IN',
-				'field' => 'term_id'
-			);
-		}
-
-		// Tag stuff
-		if ( !empty($qv['tag_id']) ) {
-			$tax_query[] = array(
-				'taxonomy' => 'post_tag',
-				'terms' => $qv['tag_id'],
-				'operator' => 'IN',
-				'field' => 'term_id'
-			);
-		}
-
-		if ( !empty($q['tag__in']) ) {
-			$tax_query[] = array(
-				'taxonomy' => 'post_tag',
-				'terms' => $q['tag__in'],
-				'operator' => 'IN',
-				'field' => 'term_id'
-			);
-		}
-
-		if ( !empty($q['tag__not_in']) ) {
-			$tax_query[] = array(
-				'taxonomy' => 'post_tag',
-				'terms' => $q['tag__not_in'],
-				'operator' => 'NOT IN',
-				'field' => 'term_id'
-			);
-		}
-
-		foreach ( $tax_query as $query ) {
-			if ( 'IN' == $query['operator'] ) {
-				switch ( $query['taxonomy'] ) {
-					case 'category':
-						$this->is_category = true;
-						break;
-					case 'post_tag':
-						$this->is_tag = true;
-						break;
-					default:
-						$this->is_tax = true;
-				}
-			}
-		}
-
-		return $tax_query;
-	}
-
 	/**
 	 * Sets the 404 property and saves whether query is feed.
 	 *
@@ -1667,6 +1615,7 @@
 
 		// First let's clear some variables
 		$distinct = '';
+		$whichcat = '';
 		$whichauthor = '';
 		$whichmimetype = '';
 		$where = '';
@@ -1930,44 +1879,202 @@
 		// Allow plugins to contextually add/remove/modify the search section of the database query
 		$search = apply_filters_ref_array('posts_search', array( $search, &$this ) );
 
-		// Taxonomies
-		$q['tax_query'] = $this->parse_tax_query( $q );
-		if ( !empty( $q['tax_query'] ) ) {
-			if ( empty($post_type) ) {
-				$post_type = 'any';
-				$post_status_join = true;
-			} elseif ( in_array('attachment', (array) $post_type) ) {
-				$post_status_join = true;
+		// Category stuff
+
+		if ( empty($q['cat']) || ($q['cat'] == '0') ||
+				// Bypass cat checks if fetching specific posts
+				$this->is_singular ) {
+			$whichcat = '';
+		} else {
+			$q['cat'] = ''.urldecode($q['cat']).'';
+			$q['cat'] = addslashes_gpc($q['cat']);
+			$cat_array = preg_split('/[,\s]+/', $q['cat']);
+			$q['cat'] = '';
+			$req_cats = array();
+			foreach ( (array) $cat_array as $cat ) {
+				$cat = intval($cat);
+				$req_cats[] = $cat;
+				$in = ($cat > 0);
+				$cat = abs($cat);
+				if ( $in ) {
+					$q['category__in'][] = $cat;
+					$q['category__in'] = array_merge($q['category__in'], get_term_children($cat, 'category'));
+				} else {
+					$q['category__not_in'][] = $cat;
+					$q['category__not_in'] = array_merge($q['category__not_in'], get_term_children($cat, 'category'));
+				}
 			}
+			$q['cat'] = implode(',', $req_cats);
+		}
 
-			$where .= get_tax_sql( $q['tax_query'], "$wpdb->posts.ID" );
+		if ( !empty($q['category__in']) ) {
+			$join = " INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) ";
+			$whichcat .= " AND $wpdb->term_taxonomy.taxonomy = 'category' ";
+			$include_cats = "'" . implode("', '", $q['category__in']) . "'";
+			$whichcat .= " AND $wpdb->term_taxonomy.term_id IN ($include_cats) ";
+		}
 
-			// Back-compat
-			$tax_query_in = wp_list_filter( $q['tax_query'], array( 'operator' => 'IN' ) );
-			if ( !empty( $tax_query_in ) ) {
-				if ( !isset( $q['taxonomy'] ) ) {
-					foreach ( $tax_query_in as $a_tax_query ) {
-						if ( !in_array( $a_tax_query['taxonomy'], array( 'category', 'post_tag' ) ) ) {
-							$q['taxonomy'] = $a_tax_query['taxonomy'];
-							if ( 'slug' == $a_tax_query['field'] )
-								$q['term'] = $a_tax_query['terms'][0];
-							else
-								$q['term_id'] = $a_tax_query['terms'][0];
+		if ( !empty($q['category__not_in']) ) {
+			$cat_string = "'" . implode("', '", $q['category__not_in']) . "'";
+			$whichcat .= " AND $wpdb->posts.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'category' AND tt.term_id IN ($cat_string) )";
+		}
 
-							break;
-						}
+		// Category stuff for nice URLs
+		if ( '' != $q['category_name'] && !$this->is_singular ) {
+			$q['category_name'] = implode('/', array_map('sanitize_title', explode('/', $q['category_name'])));
+			$reqcat = get_category_by_path($q['category_name']);
+			$q['category_name'] = str_replace('%2F', '/', urlencode(urldecode($q['category_name'])));
+			$cat_paths = '/' . trim($q['category_name'], '/');
+			$q['category_name'] = sanitize_title(basename($cat_paths));
+
+			$cat_paths = '/' . trim(urldecode($q['category_name']), '/');
+			$q['category_name'] = sanitize_title(basename($cat_paths));
+			$cat_paths = explode('/', $cat_paths);
+			$cat_path = '';
+			foreach ( (array) $cat_paths as $pathdir )
+				$cat_path .= ( $pathdir != '' ? '/' : '' ) . sanitize_title($pathdir);
+
+			//if we don't match the entire hierarchy fallback on just matching the nicename
+			if ( empty($reqcat) )
+				$reqcat = get_category_by_path($q['category_name'], false);
+
+			if ( !empty($reqcat) )
+				$reqcat = $reqcat->term_id;
+			else
+				$reqcat = 0;
+
+			$q['cat'] = $reqcat;
+
+			$join = " INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) ";
+			$whichcat = " AND $wpdb->term_taxonomy.taxonomy = 'category' ";
+			$in_cats = array($q['cat']);
+			$in_cats = array_merge($in_cats, get_term_children($q['cat'], 'category'));
+			$in_cats = "'" . implode("', '", $in_cats) . "'";
+			$whichcat .= "AND $wpdb->term_taxonomy.term_id IN ($in_cats)";
+			$groupby = "{$wpdb->posts}.ID";
+		}
+
+		// Tags
+		if ( '' != $q['tag'] ) {
+			if ( strpos($q['tag'], ',') !== false ) {
+				$tags = preg_split('/[,\s]+/', $q['tag']);
+				foreach ( (array) $tags as $tag ) {
+					$tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
+					$q['tag_slug__in'][] = $tag;
+				}
+			} else if ( preg_match('/[+\s]+/', $q['tag']) || !empty($q['cat']) ) {
+				$tags = preg_split('/[+\s]+/', $q['tag']);
+				foreach ( (array) $tags as $tag ) {
+					$tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
+					$q['tag_slug__and'][] = $tag;
+				}
+			} else {
+				$q['tag'] = sanitize_term_field('slug', $q['tag'], 0, 'post_tag', 'db');
+				$q['tag_slug__in'][] = $q['tag'];
+			}
+		}
+
+		if ( !empty($q['category__in']) || !empty($q['meta_key']) || !empty($q['tag__in']) || !empty($q['tag_slug__in']) ) {
+			$groupby = "{$wpdb->posts}.ID";
+		}
+
+		if ( !empty($q['tag__in']) && empty($q['cat']) ) {
+			$join = " INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) ";
+			$whichcat .= " AND $wpdb->term_taxonomy.taxonomy = 'post_tag' ";
+			$include_tags = "'" . implode("', '", $q['tag__in']) . "'";
+			$whichcat .= " AND $wpdb->term_taxonomy.term_id IN ($include_tags) ";
+			$reqtag = term_exists( $q['tag__in'][0], 'post_tag' );
+			if ( !empty($reqtag) )
+				$q['tag_id'] = $reqtag['term_id'];
+		}
+
+		if ( !empty($q['tag_slug__in']) && empty($q['cat']) ) {
+			$join = " INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) INNER JOIN $wpdb->terms ON ($wpdb->term_taxonomy.term_id = $wpdb->terms.term_id) ";
+			$whichcat .= " AND $wpdb->term_taxonomy.taxonomy = 'post_tag' ";
+			$include_tags = "'" . implode("', '", $q['tag_slug__in']) . "'";
+			$whichcat .= " AND $wpdb->terms.slug IN ($include_tags) ";
+			$reqtag = get_term_by( 'slug', $q['tag_slug__in'][0], 'post_tag' );
+			if ( !empty($reqtag) )
+				$q['tag_id'] = $reqtag->term_id;
+		}
+
+		if ( !empty($q['tag__not_in']) ) {
+			$tag_string = "'" . implode("', '", $q['tag__not_in']) . "'";
+			$whichcat .= " AND $wpdb->posts.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'post_tag' AND tt.term_id IN ($tag_string) )";
+		}
+
+		// Tag and slug intersections.
+		$intersections = array('category__and' => 'category', 'tag__and' => 'post_tag', 'tag_slug__and' => 'post_tag', 'tag__in' => 'post_tag', 'tag_slug__in' => 'post_tag');
+		$tagin = array('tag__in', 'tag_slug__in'); // These are used to make some exceptions below
+		foreach ( $intersections as $item => $taxonomy ) {
+			if ( empty($q[$item]) ) continue;
+			if ( in_array($item, $tagin) && empty($q['cat']) ) continue; // We should already have what we need if categories aren't being used
+
+			if ( $item != 'category__and' ) {
+				$reqtag = term_exists( $q[$item][0], 'post_tag' );
+				if ( !empty($reqtag) )
+					$q['tag_id'] = $reqtag['term_id'];
+			}
+
+			if ( in_array( $item, array('tag_slug__and', 'tag_slug__in' ) ) )
+				$taxonomy_field = 'slug';
+			else
+				$taxonomy_field = 'term_id';
+
+			$q[$item] = array_unique($q[$item]);
+			$tsql = "SELECT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->term_relationships tr ON (p.ID = tr.object_id) INNER JOIN $wpdb->term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) INNER JOIN $wpdb->terms t ON (tt.term_id = t.term_id)";
+			$tsql .= " WHERE tt.taxonomy = '$taxonomy' AND t.$taxonomy_field IN ('" . implode("', '", $q[$item]) . "')";
+			if ( !in_array($item, $tagin) ) { // This next line is only helpful if we are doing an and relationship
+				$tsql .= " GROUP BY p.ID HAVING count(p.ID) = " . count($q[$item]);
+			}
+			$post_ids = $wpdb->get_col($tsql);
+
+			if ( count($post_ids) )
+				$whichcat .= " AND $wpdb->posts.ID IN (" . implode(', ', $post_ids) . ") ";
+			else {
+				$whichcat = " AND 0 = 1";
+				break;
+			}
+		}
+
+		// Taxonomies
+		if ( $this->is_tax ) {
+			if ( '' != $q['taxonomy'] ) {
+				$taxonomy = $q['taxonomy'];
+				$tt[$taxonomy] = $q['term'];
+			} else {
+				foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
+					if ( $t->query_var && '' != $q[$t->query_var] ) {
+						$tt[$taxonomy] = $q[$t->query_var];
+						break;
 					}
 				}
+			}
 
-				$cat_query = wp_list_filter( $tax_query_in, array( 'taxonomy' => 'category' ) );
-				if ( !empty( $cat_query ) ) {
-					$cat_query = reset( $cat_query );
-					$cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
-					if ( $cat ) {
-						$this->set( 'cat', $cat->term_id );
-						$this->set( 'category_name', $cat->slug );
+			$terms = get_terms($taxonomy, array('slug' => $tt[$taxonomy], 'hide_empty' => !is_taxonomy_hierarchical($taxonomy)));
+
+			if ( is_wp_error($terms) || empty($terms) ) {
+				$whichcat = " AND 0 ";
+			} else {
+				foreach ( $terms as $term ) {
+					$term_ids[] = $term->term_id;
+					if ( is_taxonomy_hierarchical($taxonomy) ) {
+						$children = get_term_children($term->term_id, $taxonomy);
+						$term_ids = array_merge($term_ids, $children);
 					}
 				}
+				$post_ids = get_objects_in_term($term_ids, $taxonomy);
+				if ( !is_wp_error($post_ids) && !empty($post_ids) ) {
+					$whichcat .= " AND $wpdb->posts.ID IN (" . implode(', ', $post_ids) . ") ";
+					if ( empty($post_type) ) {
+						$post_type = 'any';
+						$post_status_join = true;
+					} elseif ( in_array('attachment', (array)$post_type) ) {
+						$post_status_join = true;
+					}
+				} else {
+					$whichcat = " AND 0 ";
+				}
 			}
 		}
 
@@ -2024,7 +2131,7 @@
 			$whichmimetype = wp_post_mime_type_where($q['post_mime_type'], $table_alias);
 		}
 
-		$where .= $search . $whichauthor . $whichmimetype;
+		$where .= $search . $whichcat . $whichauthor . $whichmimetype;
 
 		if ( empty($q['order']) || ((strtoupper($q['order']) != 'ASC') && (strtoupper($q['order']) != 'DESC')) )
 			$q['order'] = 'DESC';
