Index: wp-includes/taxonomy.php
===================================================================
--- wp-includes/taxonomy.php	(revision 0)
+++ wp-includes/taxonomy.php	(revision 0)
@@ -0,0 +1,195 @@
+<?php
+
+class WPTaxonomy {
+
+	// This will look like (1 => 'post_category', 2 => 'link_category') etc.
+	var $taxonomies;
+	
+	function WPTaxonomy() {
+	}
+
+	/**
+	 * Adds a new term to the database.  Optionally marks it as an alias of an existing term.
+	 * @param string $term The term to add.
+	 * @param int|string $alias_of The id or slug of the new term's alias.
+	 */
+	function add_term($term, $alias_of = '') {
+		global $wpdb;
+		$term_slug = sanitize_title($term);		
+		if ($alias_of) {
+			$alias = $wpdb->fetch_row("SELECT term_id, term_group FROM $wpdb->terms WHERE term_slug = '$alias_of'");
+			if ($alias->term_group) {
+				// The alias we want is already in a group, so let's use that one.
+				$term_group = $alias->term_group;
+			} else {
+				// The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
+				$term_group = $wpdb->get_var("SELECT MAX() term_group FROM $wpdb->terms GROUP BY term_group") + 1;
+				$wpdb->query("UPDATE $wpdb->terms SET term_group = $term_group WHERE term_id = $alias->term_id");
+			}
+		} else {
+			$term_group = 0;
+		}
+		// Now add or replace the term.  This works because we have a UNIQUE key on term_slug.
+		$wpdb->query("REPLACE INTO $wpdb->terms (term_name, term_slug, term_group) VALUES ($term, $term_slug, $term_group)");
+	}
+
+	/**
+	 * Removes a term from the database.
+	 */
+	function remove_term() {
+		
+	}
+	
+	
+	/**
+	 * Returns the index of a defined term, or 0 (false) if the term doesn't exist.
+	 */
+	function is_defined_term($term) {
+		global $wpdb;
+		return $wpdb->get_var("SELECT term_id FROM $wpdb->terms WHERE term_slug = '$term'");
+	}
+	
+	/**
+	 * Given an array of terms, returns those that are defined term slugs.  Ignores integers.
+	 * @param array $terms The term slugs to check for a definition.
+	 */
+	function get_defined_terms($terms) {
+		global $wpdb;
+		foreach ($terms as $term) {
+			if (!is_int($term)) {
+				$searches[] = $term;
+			}
+		}
+		$terms = "'" . implode("', '", $searches) . "'";
+		return $wpdb->get_col("SELECT term_slug FROM $wpdb->terms WHERE term_slug IN ($terms)");
+	}
+	
+	/**
+	 * Adds a term taxonomy entry or increments the count if it already exists.
+	 * @param int|string $term The term to add.
+	 * @param string $taxonomy The context.
+	 * @param string $description An optional description of the relationship.
+	 * @param int $parent The parent of the term in this context.
+	 */
+	function add_term_taxonomy($term, $taxonomy, $description = '', $parent = 0) {
+		$clause = (is_int($term)) ? "t.term_id = $term" : "t.term_slug = '$term'";
+		if ($count = $wpdb->query("SELECT tt.count FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = '$taxonomy' AND $clause")) {
+      // We'll be updating whatever's there, and incrementing the count.
+      $count++;
+			$wpdb->query("UPDATE $wpdb->term_taxonomy SET count = $count");
+		} else {
+      // There was no existing entry for this term and taxonomy type, so add a new one, using the supplied description and parent, with a count of 1.
+      $wpdb->query("INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, term_description, parent, count) SELECT term_id, '$taxonomy', '$description', $parent, 1 FROM $wpdb->terms AS t1 WHERE $clause");
+		}
+	}	
+
+	/**
+	 * Removes a term from a particular taxonomy or set of taxonomies.  Attaches orphaned children in one of various schemes.
+	 * @param int|string $term The slug or id of the term.
+	 * @param string|array $taxonomy The taxonomy or taxonomies to remove from.
+	 * @param string (delete|parent|root|ignore) $attach_children.  Where to attach orphaned children.	 	 
+	 */	 	
+	function remove_term_taxonomy($term, $taxonomy, $attach_children = 'root') {
+		
+	}
+	
+	/**
+	 * Relates an object (post, link etc) to a term and taxonomy type.  Creates the term and taxonomy
+	 * relationship if it doesn't already exist.  Creates a term if it doesn't exist (using the term_slug).
+	 * @param array|int|string $term The slug or id of the term.
+	 * @param int $object_id The object to relate to.
+	 * @param array|string $taxonomies The context(s) in which to relate the term to the object.
+	 */
+	function add_term_relationship($terms, $object_id, $taxonomies) {
+		global $wpdb;
+		
+		if (!is_array($taxonomies)) {
+			$taxonomies = array($taxonomies);
+		}
+		
+		if (!is_array($terms)) {
+			$terms = array($terms);
+		}
+		
+		$defined_terms = $this->get_defined_terms($terms);
+
+		foreach ($terms as $term) {
+			if (!is_int($term)) {
+				if (!isset($defined_terms[$term])) {
+					$new_terms[] = $term;
+				}
+				$term_slugs[] = $term;
+			} else {
+				$term_ids[] = $term;
+			}
+		}
+		
+		if ( isset($new_terms) ) {
+			// At least one of the terms specified doesn't exist, so add any new ones, using the term_slug as the name.
+			$this->add_terms($new_terms);
+		}
+
+		$term_clause = isset($term_ids) ? 'tt.term_id IN (' . implode(', ', $term_ids) . ')' : '';
+		if (isset($term_slugs)) {
+			if ($term_clause) {
+				$term_clause .= ' OR ';
+			}
+			$term_clause .= "t.term_slug IN ('" . implode("', '", $term_slugs) . "')";
+			$term_join = "INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id";
+		} else {
+			$term_join = '';
+		}
+		
+		// Now add or increment the term taxonomy relationships.  This is inefficient at the moment.
+		foreach ($taxonomies as $taxonomy) {
+			foreach ($terms as $term) {
+				$this->add_term_taxonomy($term, $taxonomy);
+			}
+		}
+		
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
+		
+		// Finally, relate the term and taxonomy to the object.
+		$wpdb->query("INSERT INTO $wpdb->term_relationships(object_id, term_taxonomy_id) SELECT $object_id, term_taxonomy_id FROM $wpdb->term_taxonomy AS tt $term_join WHERE ($term_clause) AND tt.taxonomy IN ($taxonomies)");
+	}
+	
+	/**
+	 * Returns the terms associated with the given object(s), in the supplied taxonomies.
+	 * @param int|array $object_id The id of the object(s)) to retrieve for.
+	 * @param string|array $taxonomies The taxonomies to retrieve terms from.
+	 * @return array The requested term data.	 	 	 
+	 */
+	function get_object_terms($object_id, $taxonomy) {
+		global $wpdb;
+		$taxonomies = ($single_taxonomy = !is_array($taxonomy)) ? array($taxonomy) : $taxonomy;
+		$object_ids = ($single_object = !is_array($object_id)) ? array($object_id) : $object_id;
+
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";		
+		$object_ids = implode(', ', $object_ids);		
+
+		if ( $taxonomy_data = $wpdb->get_results("SELECT tr.object_id, tt.taxonomy, t.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids)") ) {
+			if ($single_taxonomy && $single_object) {
+				// Just one kind of taxonomy for one object.
+				return $taxonomy_data;
+			} else {
+				foreach ($taxonomy_data as $data) {
+					if ($single_taxonomy) {
+						// Many objects, one taxonomy type.
+						$return[$data->object_id][] = $data;
+					} elseif ($single_object) {
+						// One object, many taxonomies.
+						$return[$data->taxonomy][] = $data;
+					} else {
+						// Many objects, many taxonomies.
+						$return[$data->object_id][$data->taxonomy][] = $data;
+					}
+				}
+				return $return;			
+			}
+		} else {
+			return array();
+		}		
+	}	
+}
+
+?>
Index: wp-admin/upgrade-schema.php
===================================================================
--- wp-admin/upgrade-schema.php	(revision 5485)
+++ wp-admin/upgrade-schema.php	(working copy)
@@ -10,21 +10,30 @@
 		$charset_collate .= " COLLATE $wpdb->collate";
 }
 
-$wp_queries="CREATE TABLE $wpdb->categories (
-  cat_ID bigint(20) NOT NULL auto_increment,
-  cat_name varchar(55) NOT NULL default '',
-  category_nicename varchar(200) NOT NULL default '',
-  category_description longtext NOT NULL,
-  category_parent bigint(20) NOT NULL default '0',
-  category_count bigint(20) NOT NULL default '0',
-  link_count bigint(20) NOT NULL default '0',
-  tag_count bigint(20) NOT NULL default '0',
-  posts_private tinyint(1) NOT NULL default '0',
-  links_private tinyint(1) NOT NULL default '0',
-  type tinyint NOT NULL default '1',
-  PRIMARY KEY  (cat_ID),
-  KEY category_nicename (category_nicename)
+$wp_queries="CREATE TABLE $wpdb->terms (
+ term_id bigint(20) NOT NULL auto_increment,
+ term_name varchar(55) NOT NULL default '',
+ term_slug varchar(200) NOT NULL default '',
+ term_group bigint(10) NOT NULL default 0
+ PRIMARY KEY  (term_id),
+ UNIQUE KEY term_slug (term_slug)
 ) $charset_collate;
+CREATE TABLE $wpdb->term_taxonomy (
+ term_taxonomy_id bigint(20) NOT NULL auto_increment,
+ term_id bigint(20) NOT NULL default 0,
+ taxonomy varchar(32) NOT NULL default '',
+ term_description longtext NOT NULL,
+ parent bigint(20) NOT NULL default 0,
+ count bigint(20) NOT NULL default 0,
+ PRIMARY KEY (term_taxonomy_id),
+ UNIQUE KEY (term_id, taxonomy)
+) $charset_collate;
+CREATE TABLE $wpdb->term_relationships (
+ object_id bigint(20) NOT NULL default 0,
+ term_taxonomy_id bigint(20) NOT NULL default 0,
+ PRIMARY KEY  (object_id),
+ KEY (term_taxonomy_id)
+) $charset_collate;
 CREATE TABLE $wpdb->comments (
   comment_ID bigint(20) unsigned NOT NULL auto_increment,
   comment_post_ID int(11) NOT NULL default '0',
@@ -45,13 +54,6 @@
   KEY comment_approved (comment_approved),
   KEY comment_post_ID (comment_post_ID)
 ) $charset_collate;
-CREATE TABLE $wpdb->link2cat (
-  rel_id bigint(20) NOT NULL auto_increment,
-  link_id bigint(20) NOT NULL default '0',
-  category_id bigint(20) NOT NULL default '0',
-  PRIMARY KEY  (rel_id),
-  KEY link_id (link_id,category_id)
-) $charset_collate;
 CREATE TABLE $wpdb->links (
   link_id bigint(20) NOT NULL auto_increment,
   link_url varchar(255) NOT NULL default '',
@@ -86,14 +88,6 @@
   PRIMARY KEY  (option_id,blog_id,option_name),
   KEY option_name (option_name)
 ) $charset_collate;
-CREATE TABLE $wpdb->post2cat (
-  rel_id bigint(20) NOT NULL auto_increment,
-  post_id bigint(20) NOT NULL default '0',
-  category_id bigint(20) NOT NULL default '0',
-  rel_type varchar(64) NOT NULL default 'category',
-  PRIMARY KEY  (rel_id),
-  KEY post_id (post_id,category_id)
-) $charset_collate;
 CREATE TABLE $wpdb->postmeta (
   meta_id bigint(20) NOT NULL auto_increment,
   post_id bigint(20) NOT NULL default '0',
@@ -404,4 +398,4 @@
 	}
 }
 
-?>
\ No newline at end of file
+?>
