Index: wp-includes/wp-db.php
===================================================================
--- wp-includes/wp-db.php	(revision 4846)
+++ wp-includes/wp-db.php	(working copy)
@@ -34,6 +34,8 @@
 	var $optiongroups;
 	var $optiongroup_options;
 	var $postmeta;
+	var $tags;
+	var $tagged;
 
 	/**
 	 * Connects to the database server and selects a database
Index: wp-includes/tag.php
===================================================================
--- wp-includes/tag.php	(revision 0)
+++ wp-includes/tag.php	(revision 0)
@@ -0,0 +1,156 @@
+<?php
+
+// TODO: Split these across tag.php, tag-template.php, and admin-db.php
+
+
+function get_post_tags ( $post_id = false ) {
+	global $post_tag_cache, $wpdb, $post;
+
+	if ( empty($post_id) )
+		$post_id = $post->ID;
+
+	if ( isset ($post_tag_cache[$post_id] ) )
+		return $post_tag_cache[$post_id];
+
+	$post_tag_cache[$post_id] = $wpdb->get_results("SELECT * FROM $wpdb->tagged RIGHT JOIN $wpdb->tags ON ($wpdb->tags.tag_id = $wpdb->tagged.tag_id) WHERE post_id = '$post_id'");
+	
+	return $post_tag_cache[$post_id];
+}
+
+function &get_tag(&$tag, $output = OBJECT) {
+	global $wpdb;
+
+	if ( empty($tag) )
+		return null;
+
+	if ( is_object($tag) ) {
+		wp_cache_add($tag->tag_id, $tag, 'tag');
+		$_tag = $tag;
+	} else {
+		if ( ! $_tag = wp_cache_get($tag, 'tag') ) {
+			$_tag = $wpdb->get_row("SELECT * FROM $wpdb->tags WHERE tag_id = '$tag' LIMIT 1");
+			wp_cache_set($tag, $_tag, 'tag');
+		}
+	}
+
+	$_tag = apply_filters('get_tag', $_tag);
+
+	if ( $output == OBJECT ) {
+		return $_tag;
+	} elseif ( $output == ARRAY_A ) {
+		return get_object_vars($_tag);
+	} elseif ( $output == ARRAY_N ) {
+		return array_values(get_object_vars($_tag));
+	} else {
+		return $_tag;
+	}
+}
+
+function get_tag_id($tag_name) {
+	global $wpdb;
+
+	$tag_id = $wpdb->get_var("SELECT tag_id FROM $wpdb->tags WHERE raw_tag = '$tag_name'");
+
+	return $tag_id;
+}
+
+function get_tag_by_name() {
+
+}
+
+function get_tag_name($tag_id) {
+	$tag_id = (int) $tag_id;
+	$tag = &get_tag($tag_id);
+	return $tag->raw_tag;
+}
+
+function get_tag_link() {
+	
+}
+
+// Template funcs
+
+function get_the_tag_list() {
+	
+}
+
+function the_tags() {
+	
+}
+
+function wp_list_tags() {
+	
+}
+
+// Admin funcs
+
+function wp_create_tag( $tag ) {
+	global $wpdb;
+	$raw_tag = $tag;
+	$tag = sanitize_title( $tag );
+	if ( empty( $tag ) )
+		return false;
+	if ( $exists = $wpdb->get_var("SELECT tag_id FROM $wpdb->tags WHERE tag = '$tag'") )
+		return $exists;
+
+	$wpdb->query("INSERT INTO $wpdb->tags ( tag, raw_tag ) VALUES ( '$tag', '$raw_tag' )");
+	$tag_id = $wpdb->insert_id;
+	do_action('wp_tag_created', $raw_tag, $tag_id);
+	return $tag_id;
+}
+
+function wp_set_post_tags($post_ID = 0, $post_tags = array()) {
+	global $wpdb;
+	if (!is_array($post_tags) || 0 == count($post_tags) || empty($post_tags))
+		$post_tags = array();
+
+	$post_tags = array_unique($post_tags);
+
+	// First the old tags
+	$old_tags = $wpdb->get_col("
+		SELECT tag_id
+		FROM $wpdb->tagged
+		WHERE post_id = $post_ID");
+
+	if (!$old_tags) {
+		$old_tags = array();
+	} else {
+		$old_tags = array_unique($old_tags);
+	}
+
+	// Delete any?
+	$delete_tags = array_diff($old_tags,$post_tags);
+
+	if ($delete_tags) {
+		foreach ($delete_tags as $del) {
+			$wpdb->query("
+				DELETE FROM $wpdb->tagged
+				WHERE tag_id = $del
+					AND post_id = $post_ID
+				");
+		}
+	}
+
+	// Add any?
+	$add_tags = array_diff($post_tags, $old_tags);
+
+	if ($add_tags) {
+		foreach ($add_tags as $new_tag) {
+			if ( !empty($new_tag) )
+				$wpdb->query("
+					INSERT INTO $wpdb->taggewd (post_id, tag_id) 
+					VALUES ($post_ID, $new_tag)");
+		}
+	}
+
+	// Update tag counts.
+	$all_affected_tags = array_unique(array_merge($post_tags, $old_tags));
+	foreach ( $all_affected_tags as $tag_id ) {
+		$count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->tagged, $wpdb->posts WHERE $wpdb->posts.ID=$wpdb->tagged.post_id AND post_status = 'publish' AND post_type = 'post' AND tag_id = '$tag_id'");
+		$wpdb->query("UPDATE $wpdb->tags SET tag_count = '$count' WHERE tag_id = '$tag_id'");
+		clean_tag_cache($tag_id);
+		do_action('edit_tag', $tag_id);
+	}
+}
+
+?>
Index: wp-includes/post.php
===================================================================
--- wp-includes/post.php	(revision 4846)
+++ wp-includes/post.php	(working copy)
@@ -522,6 +522,9 @@
 	}
 	$post_cat = $post_category[0];
 
+	if ( empty($post_tags) )
+		$post_tags = array();
+
 	if ( empty($post_author) )
 		$post_author = $user_ID;
 
@@ -646,6 +649,7 @@
 	}
 
 	wp_set_post_categories($post_ID, $post_category);
+	wp_set_post_tags($post_ID, $post_tags);
 
 	if ( 'page' == $post_type ) {
 		clean_page_cache($post_ID);
Index: wp-includes/version.php
===================================================================
--- wp-includes/version.php	(revision 4846)
+++ wp-includes/version.php	(working copy)
@@ -3,6 +3,6 @@
 // This holds the version number in a separate file so we can bump it without cluttering the SVN
 
 $wp_version = '2.2-bleeding';
-$wp_db_version = 4772;
+$wp_db_version = 4847;
 
 ?>
Index: wp-settings.php
===================================================================
--- wp-settings.php	(revision 4846)
+++ wp-settings.php	(working copy)
@@ -116,6 +116,8 @@
 $wpdb->options        = $wpdb->prefix . 'options';
 $wpdb->postmeta       = $wpdb->prefix . 'postmeta';
 $wpdb->usermeta       = $wpdb->prefix . 'usermeta';
+$wpdb->tags           = $wpdb->prefix . 'tags';
+$wpdb->tagged         = $wpdb->prefix . 'tagged';
 
 if ( defined('CUSTOM_USER_TABLE') )
 	$wpdb->users = CUSTOM_USER_TABLE;
Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php	(revision 4846)
+++ wp-admin/edit-form-advanced.php	(working copy)
@@ -75,6 +75,13 @@
 <ul id="categorychecklist"><?php dropdown_categories(); ?></ul></div>
 </fieldset>
 
+<fieldset id="tagdiv" class="dbx-box">
+<h3 class="dbx-handle"><?php _e('Tags') ?></h3>
+<div class="dbx-content">
+<p id="jaxtag"></p>
+<ul id="tagchecklist"></ul></div>
+</fieldset>
+
 <fieldset id="commentstatusdiv" class="dbx-box">
 <h3 class="dbx-handle"><?php _e('Discussion') ?></h3>
 <div class="dbx-content">
Index: wp-admin/cat-js.php
===================================================================
--- wp-admin/cat-js.php	(revision 4846)
+++ wp-admin/cat-js.php	(working copy)
@@ -12,3 +12,13 @@
 	$('newcat').onkeypress = function(e) { return killSubmit("catList.ajaxAdder('category','jaxcat');", e); };
 	$('catadd').onclick = function() { catList.ajaxAdder('category', 'jaxcat'); };
 }
+addLoadEvent(function(){tagList=new listMan('tagchecklist');tagList.ajaxRespEl='jaxtag';tagList.topAdder=1;tagList.alt=0;tagList.showLink=0;});
+addLoadEvent(newtagAddIn);
+function newtagAddIn() {
+	var jaxtag = $('jaxtag');
+	if ( !jaxtag )
+		return false;
+	Element.update(jaxtag,'<span id="ajaxtag"><input type="text" name="newtag" id="newtag" size="16" autocomplete="off"/><input type="button" name="Button" id="tagadd" value="<?php echo js_escape(__('Add')); ?>"/><span id="howto"><?php echo js_escape(__('Separate multiple tags with commas.')); ?></span></span>');
+	$('newtag').onkeypress = function(e) { return killSubmit("tagList.ajaxAdder('tag','jaxtag');", e); };
+	$('tagadd').onclick = function() { tagList.ajaxAdder('tag', 'jaxtag'); };
+}
Index: wp-admin/upgrade-schema.php
===================================================================
--- wp-admin/upgrade-schema.php	(revision 4846)
+++ wp-admin/upgrade-schema.php	(working copy)
@@ -120,6 +120,20 @@
   KEY post_name (post_name),
   KEY type_status_date (post_type,post_status,post_date,ID)
 );
+CREATE TABLE $wpdb->tags (
+  tag_id bigint(20) unsigned NOT NULL auto_increment,
+  tag varchar(30) NOT NULL default '',
+  raw_tag varchar(50) NOT NULL default '',
+  tag_count bigint(20) unsigned NOT NULL default '0',
+  PRIMARY KEY  (tag_id)
+);
+CREATE TABLE $wpdb->tagged (
+  tag_id bigint(20) unsigned NOT NULL default '0',
+  post_id bigint(20) unsigned NOT NULL default '0',
+  PRIMARY KEY  (tag_id,post_id),
+  KEY tag_id_index (tag_id),
+  KEY post_id_index (post_id)
+);
 CREATE TABLE $wpdb->users (
   ID bigint(20) unsigned NOT NULL auto_increment,
   user_login varchar(60) NOT NULL default '',
