diff --git a/content.module b/content.module
index 7e6d120..bc2c84c 100644
--- a/content.module
+++ b/content.module
@@ -511,6 +511,217 @@ function content_field($op, &$node, $field, &$node_field, $teaser, $page) {
 }
 
 /**
+ *
+ */
+function content_storage($op, $node) {
+  $type_name = $node->type;
+  $type = content_types($type_name);
+
+  $return = array();
+
+  if (empty($type['fields'])) {
+    return;
+  }
+  switch ($op) {
+    case 'load':
+      $selects = array();
+      foreach ($type['fields'] as $field) {
+        $db_info = content_database_info($field);
+        if ($field['multiple']) {
+          $selects[$db_info['table']]['multiple'] = TRUE;
+        }
+        foreach ($db_info['columns'] as $column => $attributes) {
+          $selects[$db_info['table']]['columns'][] = $attributes['column'];
+          $field['columns'][$column] = $attributes['column'];
+        }
+        $selects[$db_info['table']]['fields'][] = $field;
+      }
+      
+      foreach ($selects as $table => $select) {
+        if ($select['multiple']) {
+          $select['columns'][] = 'delta';
+        }
+        $sql = 'SELECT '. implode(', ', $select['columns']) .' FROM {'. $table .'} WHERE vid = %d';
+        if ($select['multiple']) {
+          $result = db_query($sql .' ORDER BY delta', $node->vid);
+        }
+        else {
+          $result = db_query_range($sql, $node->vid, 0, 1);
+        }
+
+        while ($row = db_fetch_array($result)) {
+          foreach ($select['fields'] as $field) {
+            if ($select['multiple']) {
+              foreach ($field['columns'] as $column => $dbcol) {
+                $return[$field['field_name']][$row['delta']][$column] = $row[$dbcol];
+              }
+            }
+            else {
+              foreach ($field['columns'] as $column => $dbcol) {
+                $return[$field['field_name']][0][$column] = $row[$dbcol];
+              }
+            }
+          }
+        }
+      }
+      break;
+
+    case 'update':
+      $insert_only = $deletes = $updates = $args = array();
+      foreach ($type['fields'] as $field) {
+        $db_info = content_database_info($field);
+
+        if ($field['multiple']) {
+          $deletes[] = $db_info['table'];
+          $insert_only[] = $db_info['table'];
+        }
+        else {
+          foreach ($db_info['columns'] as $column => $attributes) {
+            $placeholder = content_type_placeholder($attributes['type']);
+            if (isset($node->{$field['field_name']})) {
+              $updates[$db_info['table']]['fields'][] = $attributes['column'] .' = '. $placeholder;
+              $updates[$db_info['table']]['values'][] = $node->{$field['field_name']}[0][$column];
+            }
+            else {
+              $updates[$db_info['table']]['fields'][] = $attributes['column'] .' = NULL';
+            }
+          }
+        }
+      }
+      if (!empty($updates)) {
+        foreach ($updates as $table => $update) {
+          $update['values'][] = $node->vid;
+
+          $sql = 'UPDATE {'. $table .'} SET '. implode(',', $update['fields']) .' WHERE vid = %d';
+          db_query($sql, $update['values']);
+          if (!db_affected_rows()) {
+            $insert_only[] = $table;
+            $op = 'insert';
+          }
+        }
+      }
+
+      if (!empty($deletes)) {
+        foreach (array_unique($deletes) as $table) {
+          db_query('DELETE FROM {'. $table .'} WHERE vid = %d', $node->vid);
+        }
+        $op = 'insert';
+      }
+      if ($op != 'insert') {
+        break;
+      }
+
+    case 'insert':
+      $inserts = array();
+      foreach ($type['fields'] as $field) {
+        $db_info = content_database_info($field);
+        if (!empty($insert_only) && !in_array($db_info['table'], $insert_only)) {
+          continue;
+        }
+        if ($field['multiple']) {
+          $inserts[$db_info['table']]['multiple'] = TRUE;
+        }
+        foreach ($db_info['columns'] as $column => $attributes) {
+          $inserts[$db_info['table']]['columns'][] = $attributes['column'];
+        }
+        if (!empty($node->{$field['field_name']})) {
+          foreach ($node->{$field['field_name']} as $delta => $value) {
+            foreach ($db_info['columns'] as $column => $attributes) {
+              if (isset($value[$column])) {
+                $placeholder = content_type_placeholder($attributes['type']); 
+                $inserts[$db_info['table']]['placeholders'][$delta][] = $placeholder;
+                $inserts[$db_info['table']]['values'][$delta][] = $value[$column];
+              }
+              else {
+                $inserts[$db_info['table']]['placeholders'][$delta][] = 'NULL';
+              }
+            }
+          }
+        }
+      }
+
+      foreach ($inserts as $table => $insert) {
+        $args = array();
+        $insert['columns'][] = 'nid';
+        $insert['columns'][] = 'vid';
+        if ($insert['multiple']) {
+          $insert['columns'][] = 'delta';
+          $insert['placeholders'][] = '%d';
+        }
+        $sql = 'INSERT INTO {'. $table .'} ('. implode(',', $insert['columns']) .') VALUES ';
+        $sql_values = array();
+        foreach ($insert['values'] as $delta => $value) {
+          $insert['placeholders'][$delta][] = '%d';
+          $insert['placeholders'][$delta][] = '%d';
+          if ($insert['multiple']) {
+            $insert['placeholders'][$delta][] = '%d';
+          }
+          $sql_values[] = '('. implode(', ', $insert['placeholders'][$delta]) .')';
+          $args = array_merge($args, $value);
+          $args[] = $node->nid;
+          $args[] = $node->vid;
+          if ($insert['multiple']) {
+            $args[] = $delta;
+          }
+        }
+        $sql.= implode(',', $sql_values);
+
+        db_query($sql, $args);
+      }
+      break;
+
+    case 'delete':
+      $tables = array('content_type_'. $node->type);
+      foreach ($type['fields'] as $field) {
+        $db_info = content_database_info($field);
+        $tables[] = $db_info['table'];
+      }
+      foreach (array_unique($tables) as $table) {
+        db_query('DELETE FROM {'. $table .'} WHERE nid = %d', $node->nid);
+      }
+      break;
+
+    case 'delete revision':
+      $tables = array('content_type_'. $node->type);
+      foreach ($type['fields'] as $field) {
+        $db_info = content_database_info($field);
+        $tables[] = $db_info['table'];
+      }
+      foreach (array_unique($tables) as $table) {
+        db_query('DELETE FROM {'. $table .'} WHERE vid = %d', $node->vid);
+      }
+      break;
+  }
+
+  return $return;
+}
+
+/**
+ *
+ */
+function content_type_placeholder($type) {
+  switch ($attributes['type']) {
+    case 'int':
+    case 'mediumint':
+    case 'tinyint':
+    case 'bigint':
+      return '%d';
+      break;
+
+    case 'float':
+      return '%f';
+      break;
+
+    case 'numeric':
+      return '%s';
+      break;
+
+    default:
+      return '\'%s\'';
+  }
+}
+
+/**
  * Invoke a field hook.
  *
  * For each operation, both this function and _content_field_invoke_default() are
@@ -554,26 +765,25 @@ function _content_field_invoke_default($op, &$node, $teaser = NULL, $page = NULL
   $type = content_types($type_name);
 
   $return = array();
-  if (count($type['fields'])) {
+  
+  if (in_array($op, array('load', 'insert', 'update', 'delete', 'delete revision'))) {
+    return content_storage($op, $node);
+  }
+  else {
     foreach ($type['fields'] as $field) {
-      $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
-
-      $db_info = content_database_info($field);
-      if (count($db_info['columns'])) {
-        $result = content_field($op, $node, $field, $node_field, $teaser, $page);
-        if (is_array($result)) {
-          $return = array_merge($return, $result);
-        }
-        else if (isset($result)) {
-          $return[] = $result;
-        }
+      $items = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
+      $result = content_field($op, $node, $field, $items, $teaser, $page);
+      if (is_array($result)) {
+        $return = array_merge($return, $result);
+      }
+      else if (isset($result)) {
+        $return[] = $result;
       }
       if (isset($node->$field['field_name'])) {
-        $node->$field['field_name'] = $node_field;
+        $node->$field['field_name'] = $items;
       }
     }
   }
-
   return $return;
 }
 
@@ -922,6 +1132,23 @@ function content_database_info($field) {
 }
 
 /**
+ * Helper function for identifying the storage type for a field.
+ */
+function content_storage_type($field) {
+  if ($field['multiple'] > 0) {
+    return CONTENT_DB_STORAGE_PER_FIELD;
+  }
+  else {
+    require_once drupal_get_path('module', 'content') ."/content_crud.inc";
+    $instances = content_field_instance_read(array('field_name' => $field['field_name']));
+    if (count($instances) > 1) {
+      return CONTENT_DB_STORAGE_PER_FIELD;
+    }
+  }
+  return CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
+}
+
+/**
  * Manipulate a 2D array to reverse rows and columns.
  *
  * The default data storage for fields is delta first, column names second.
diff --git a/content_admin.inc b/content_admin.inc
index 4d63e15..74de648 100644
--- a/content_admin.inc
+++ b/content_admin.inc
@@ -575,35 +575,12 @@ function theme_content_admin_field_add_new_field_widget_type($form) {
  * Add an existing field to a content type.
  */
 function _content_admin_field_add_existing_submit($form_id, $form_values) {
+  require_once './'. drupal_get_path('module', 'content') .'/content_crud.inc';
   $type  = content_types($form_values['type_name']);
-  $field = content_fields($form_values['field_name']);
-  $field_types = _content_field_types();
-  $field_type = $field_types[$field['type']];
-  $columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $field);
-
-  if (is_array($columns) && count($columns)) {
-    if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE) {
-      $new_field = $field;
-      $new_field['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
-      db_query("UPDATE {node_field} SET db_storage = %d WHERE field_name = '%s'", CONTENT_DB_STORAGE_PER_FIELD, $form_values['field_name']);
-      content_alter_db_field($field, $columns, $new_field, $columns);
-    }
-  }
 
-  $prior_instance = db_fetch_array(db_query("SELECT * FROM {node_field_instance} WHERE field_name = '%s'", $form_values['field_name']));
-  if (!$prior_instance) {
-    $prior_instance = array();
-    $prior_instance['weight'] = 0;
-    $prior_instance['label'] = $form_values['field_name'];
-    $prior_instance['widget_type'] = '';
-    $prior_instance['widget_settings'] = '';
-    $prior_instance['display_settings'] = '';
-    $prior_instance['description'] = '';
-  }
-  db_query("INSERT INTO {node_field_instance} (field_name, type_name, weight, label, widget_type, widget_settings, display_settings, description) VALUES ('%s', '%s', %d, '%s', '%s', '%s', '%s', '%s')", $form_values['field_name'], $form_values['type_name'], $prior_instance['weight'], $prior_instance['label'], $prior_instance['widget_type'], $prior_instance['widget_settings'], $prior_instance['display_settings'], $prior_instance['description']);
+  content_field_instance_create($form_values);
 
   drupal_set_message(t('Added field %label.', array('%label' => $prior_instance['label'])));
-  content_clear_type_cache();
   return 'admin/content/types/'. $type['url_str'] .'/fields';
 }
 
@@ -628,16 +605,14 @@ function _content_admin_field_add_new_validate($form_id, $form_values) {
  * Create a new field for a content type.
  */
 function _content_admin_field_add_new_submit($form_id, $form_values) {
-  // Find a valid, computer-friendly field name.
-
-  $fields = content_fields();
+  require_once './'. drupal_get_path('module', 'content') .'/content_crud.inc';
   $type   = content_types($form_values['type_name']);
-
-  // Accept field name from programmed submissions if valid and it doesn't already exist.
-  if ($form_values['field_name']) {
-    $field_name = $form_values['field_name'];
-  }
-  else {
+  $field = array(
+    'field_name' => $form_values['field_name'],
+    'label' => $form_values['label'],
+    'type_name' => $form_values['type_name'],
+  );
+  if (empty($form_values['field_name'])) {
     $field_name = trim($form_values['label']);
     $field_name = drupal_strtolower($field_name);
     $field_name = str_replace(array(' ', '-'), '_', $field_name);
@@ -651,24 +626,15 @@ function _content_admin_field_add_new_submit($form_id, $form_values) {
       }   while (isset($fields[$new_name]));
       $field_name = $new_name;
     }
+    $field['field_name'] = $field_name;
   }
+  $widget = explode('-', $form_values['field_widget_type']);
+  $field['type'] = $widget[0];
+  $field['widget_type'] = $widget[1];
 
-  $field_widget_type = explode('-', $form_values['field_widget_type']);
-  db_query("INSERT INTO {node_field} (field_name, type, global_settings, required, multiple, db_storage) VALUES ('%s', '%s', '%s', %d, %d, %d)", $field_name, $field_widget_type[0], serialize(array()), 0, 0, CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
-  db_query("INSERT INTO {node_field_instance} (field_name, type_name, weight, label, widget_type, widget_settings, display_settings, description) VALUES ('%s', '%s', %d, '%s', '%s', '%s', '%s', '%s')", $field_name, $form_values['type_name'], 0, $form_values['label'], $field_widget_type[1], serialize(array()), serialize(array()), '');
-
+  content_field_instance_create($field);
   content_clear_type_cache();
 
-  // Create new database columns as necessary.
-  $field_types = _content_field_types();
-  $field_type = $field_types[$field_widget_type[0]];
-  $field = content_fields($field_name);
-
-  $columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $field);
-  if (is_array($columns) && count($columns)) {
-    content_alter_db_field(array(), array(), $field, $columns);
-  }
-
   drupal_set_message(t('Created field %label.', array('%label' => $form_values['label'])));
   return 'admin/content/types/'. $type['url_str'] .'/fields/'. $field_name;
 }
@@ -710,7 +676,7 @@ function _content_admin_field_remove_submit($form_id, $form_values) {
 
   if ($type && $field && $form_values['confirm']) {
     include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc');
-    content_field_instance_delete($form_values);
+    content_field_instance_delete($field['field_name'], $field['type_name']);
 
     drupal_set_message(t('Removed field %field from %type.', array('%field' => $field['widget']['label'], '%type' => $type['name'])));
     content_clear_type_cache();
@@ -973,67 +939,45 @@ function _content_admin_field_validate($form_id, $form_values, $form) {
  * Save a field's settings after editing.
  */
 function _content_admin_field_submit($form_id, $form_values) {
+  require_once './'. drupal_get_path('module', 'content') .'/content_crud.inc';
   $type = content_types($form_values['type_name']);
-  $field = $type['fields'][$form_values['field_name']];
-  $field_types = _content_field_types();
-  $field_type = $field_types[$field['type']];
-  $widget_types = _content_widget_types();
-  $widget_type = $widget_types[$form_values['widget_type']];
-
-  // If content.module is handling the default value,
-  // initialize $widget_settings with default values,
-  if (content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) {
-    $widget_settings = array(
-      'default_value' => $form_values['default_value'],
-      'default_value_php' => $form_values['default_value_php'],
-      );
-  }
-
-  $setting_names = module_invoke($widget_type['module'], 'widget_settings', 'save', $field);
-  if (is_array($setting_names)) {
-    foreach ($setting_names as $setting) {
-      $widget_settings[$setting] = $form_values[$setting];
-    }
-  }
-  $field_settings = array();
-  $setting_names = module_invoke($field_type['module'], 'field_settings', 'save', $field);
-  if (is_array($setting_names)) {
-    foreach ($setting_names as $setting) {
-      $field_settings[$setting] = $form_values[$setting];
-    }
-  }
 
-  $prev_field = $field;
-  $prev_columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $field);
+  content_field_instance_update($form_values);
 
-  db_query("UPDATE {node_field_instance} SET weight = %d, label = '%s', widget_type = '%s', widget_settings = '%s', description = '%s' WHERE type_name = '%s' AND field_name = '%s'", $form_values['weight'], $form_values['label'], $form_values['widget_type'], serialize($widget_settings), $form_values['description'], $form_values['type_name'], $form_values['field_name']);
+  drupal_set_message(t('Saved field %field.', array('%field' => $form_values['label'])));
+  return 'admin/content/types/'. $type['url_str'] .'/fields';
+}
 
-  if ($form_values['multiple']) {
-    $field['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
+/**
+ * Content Schema Alter
+ *
+ * Alter the database schema.
+ *
+ * TODO figure out an API-safe way to use batching to update the nodes that
+ * will be affected by this change so the node_save() hooks will fire.
+ *
+ */
+function content_alter_schema($previous_field, $new_field) {
+  $type = !empty($new_field) ? $new_field['type'] : $previous_field['type'];
+  $field_types = _content_field_types();
+  $field_type = $field_types[$type];
+  if (empty($previous_field)) {
+    $previous_columns = array();
   }
   else {
-    $instances = db_result(db_query("SELECT COUNT(*) FROM {node_field_instance} WHERE field_name = '%s'", $form_values['field_name']));
-    if ($instances == 1) {
-      $field['db_storage'] = CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
-    }
+    $previous_columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $previous_field);
   }
-  db_query("UPDATE {node_field} SET required = %d, multiple = %d, global_settings = '%s', db_storage = %d WHERE field_name = '%s'", $form_values['required'], $form_values['multiple'], serialize($field_settings), $field['db_storage'], $form_values['field_name']);
 
-  drupal_set_message(t('Saved field %field.', array('%field' => $form_values['label'])));
   content_clear_type_cache();
 
-  $new_field = content_fields($form_values['field_name']);
-  $new_columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $new_field);
-
-  if (!isset($prev_columns)) {
-    $prev_columns = array();
-  }
-  if (!isset($new_columns)) {
+  if (empty($new_field)) {
     $new_columns = array();
   }
-  content_alter_db_field($prev_field, $prev_columns, $new_field, $new_columns);
+  else {
+    $new_columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $new_field);
+  }
 
-  return 'admin/content/types/'. $type['url_str'] .'/fields';
+  content_alter_db_field($previous_field, $previous_columns, $new_field, $new_columns);
 }
 
 /**
@@ -1474,4 +1418,4 @@ function content_db_change_column($table, $column, $column_new, $type, $attribut
       db_query('ALTER TABLE {'. $table .'} CHANGE '. $column .' '. $column_new .' '. $type .' '. $not_null .' '. $default);
       break;
   }
-}
\ No newline at end of file
+}
diff --git a/content_crud.inc b/content_crud.inc
index d23a571..952be49 100644
--- a/content_crud.inc
+++ b/content_crud.inc
@@ -4,267 +4,623 @@
 /**
  * @file
  * Create/Read/Update/Delete functions for CCK-defined object types.
+ *
+ * The content module field API will allow $field arguments to
+ * be input either in the field => widget nested array that is used
+ * by the content module, or in flattened $form_values arrays, by
+ * converting flattened arrays to the nested format.
+ *
+ * A hook_content_fieldapi() is available for each field instance action,
+ * and each hook receives the nested field => widget array as an argument.
+ *
+ * The hook_content_fieldapi() $ops include:
+ *
+ *   - create instance
+ *   - read instance
+ *   - update instance
+ *   - delete instance
+ *
+ * Another function, content_module_delete($module) will clean up
+ * after a module that has been deleted by removing all data and
+ * settings information that was created by that module.
  */
 
 /**
- * Rebuild content type information from node tables.
- *
- *  Used to update CCK tables that might have missed changes made when CCK was disabled.
- *  Called by hook_form_alter() on system modules page whenever CCK is enabled.
+ * Create an array of default values for a field type.
  */
-function content_types_rebuild() {
-  $db_types = content_types();
+function content_field_default_values($field_type) {
+  $field_types = _content_field_types();
+  $module = $field_types[$field_type]['module'];
+
+  $field = array(
+    'module' => $module,
+    'type' => $field_type,
+    'active' => 0,
+  );
 
-  $result = db_query("SELECT type_name FROM {node_field_instance}");
-  while ($type = db_fetch_array($result)) {
-    $field_types[] = $type['type_name'];
+  if (module_exists($module)) {
+    $field['active'] = 1;
   }
 
-  foreach ($db_types as $content_type) {
-    // Find content types that are missing the content table and add it
-    content_type_create((object)array('type' => $content_type['type']));
+  $field['columns'] = module_invoke($module, 'field_settings', 'database columns', $field);
+  // Ensure columns always default to NULL values.
+  foreach ((array) $field['columns'] as $column_name => $column) {
+    $field['columns'][$column_name]['not null'] = FALSE;
   }
-  content_clear_type_cache();
-}
 
-/**
- * Make changes needed when a content type is created.
- *
- * @param $info
- *   value supplied by hook_node_type()
- */
-function content_type_create($info) {
-  content_clear_type_cache();
-  $type = content_types($info->type);
-  $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
-  if (!db_table_exists($table)) {
-    switch ($GLOBALS['db_type']) {
-      case 'mysql':
-      case 'mysqli':
-        db_query("CREATE TABLE {". $table ."} (
-          vid int unsigned NOT NULL default '0',
-          nid int unsigned NOT NULL default '0',
-          PRIMARY KEY (vid)
-        ) /*!40100 DEFAULT CHARACTER SET utf8 */");
-      break;
+  $field['required'] = 0;
+  $field['multiple'] = 0;
+  $field['db_storage'] = CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
 
-      case 'pgsql':
-        db_query("CREATE TABLE {". $table ."} (
-          vid int_unsigned NOT NULL default '0',
-          nid int_unsigned NOT NULL default '0',
-          PRIMARY KEY (vid)
-        )");
-      break;
+  // Make sure field settings all have an index in the array.
+  $setting_names = module_invoke($module, 'field_settings', 'save', $field);
+  if (is_array($setting_names)) {
+    foreach ($setting_names as $setting) {
+      $field[$setting] = NULL;
     }
-    drupal_set_message(t('The content fields table %name has been created.', array('%name' => $table)));
   }
+  return $field;
 }
 
 /**
- * Make changes needed when an existing content type is updated.
- *
- * @param $info
- *   value supplied by hook_node_type()
+ * Create an array of default values for a field instance.
  */
-function content_type_update($info) {
-  if (!empty($info->old_type) && $info->old_type != $info->type) {
-    // rename the content type in all fields that use changed content type.
-    db_query("UPDATE {node_field_instance} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type));
+function content_instance_default_values($field_name, $type_name, $widget_type) {
+  $widget_types = _content_widget_types();
+  $module = $widget_types[$widget_type]['module'];
 
-    // Rename the content fields table to match new content type name.
-    $old_type = content_types($info->old_type);
-    $old_name = _content_tablename($old_type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
-    $new_name = _content_tablename($info->type, CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
-    if (db_table_exists($old_name)) {
-      switch ($GLOBALS['db_type']) {
-        case 'mysql':
-        case 'mysqli':
-          db_query("RENAME TABLE {". $old_name ."} TO {". $new_name ."}");
-          break;
-
-        case 'pgsql':
-          db_query("ALTER TABLE {". $old_name ."} RENAME TO {". $new_name ."}");
-          break;
-      }
-      drupal_set_message(t('Content fields table %old_name has been renamed to %new_name and field instances have been updated.', array('%old_name' => $old_name, '%new_name' => $new_name)));
-    }
+  $widget = array(
+    'field_name' => $field_name,
+    'type_name' => $type_name,
+    'weight' => 0,
+    'label' => $field_name,
+    'description' => '',
+    'widget_type' => $widget_type,
+    'widget_module' => $module,
+    'display_settings' => array(),
+    'widget_settings' => array(),
+  );
+
+  if (module_exists($module)) {
+    $widget['widget_active'] = 1;
   }
-  // reset all content type info
-  content_clear_type_cache();
+
+  $settings_names = array_merge(array('label'), array_keys(_content_admin_display_contexts()));
+  $widget['display_settings'] = array();
+  foreach ($settings_names as $name) {
+    $widget['display_settings'][$name]['format'] = ($name == 'label') ? 'above' : 'default';
+  }
+
+  // Make sure widget settings all have an index in the array.
+  $settings_names = module_invoke($module, 'widget_settings', 'save', $widget);
+  $widget['widget_settings'] = array();
+  foreach ((array) $settings_names as $name) {
+    $widget['widget_settings'][$name] = NULL;
+  }
+  return $widget;
 }
 
 /**
- * Make changes needed when a content type is deleted.
- *
- * @param $info
- *   value supplied by hook_node_type()
+ * Expand field info to create field => widget info.
  */
-function content_type_delete($info) {
-  $type = content_types($info->type);
+function content_field_instance_expand($field) {
+  if (isset($field['widget'])) {
+    return $field;
+  }
+  $field['widget'] = !empty($field['widget_settings']) ? $field['widget_settings'] : array();
+  $field['widget']['label'] = !empty($field['label']) ? $field['label'] : $field['field_name'];
+  $field['widget']['weight'] = !empty($field['weight']) ? $field['weight'] : NULL;
+  $field['widget']['description'] = !empty($field['description']) ? $field['description'] : NULL;
 
-  foreach ($type['fields'] as $field) {
-    content_field_instance_delete(array('type_name' => $info->type, 'field_name' => $field['field_name']));
+  if (!empty($field['widget_type'])) {
+    $field['widget']['type'] = $field['widget_type'];
+    $widget_types = _content_widget_types();
+    $field['widget']['module'] = $widget_types[$field['widget_type']]['module'];
   }
-  $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
-  if (db_table_exists($table)) {
-    db_query('DROP TABLE {'. $table .'}');
-    drupal_set_message(t('The content fields table %name has been deleted.', array('%name' => $table)));
+  elseif (!empty($field['widget_module'])) {
+    $field['widget']['module'] = $field['widget_module'];
   }
 
-  // reset all content type info
-  content_clear_type_cache();
-}
+  unset($field['widget_type']);
+  unset($field['weight']);
+  unset($field['label']);
+  unset($field['description']);
+  unset($field['widget_module']);
+  unset($field['widget_settings']);
 
+  // If content.module is handling the default value,
+  // initialize $widget_settings with default values,
+  if (isset($field['default_value']) && isset($field['default_value_php']) &&
+      content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) {
+    $field['widget']['default_value'] = !empty($field['default_value']) ? $field['default_value']  : NULL;
+    $field['widget']['default_value_php'] = !empty($field['default_value_php']) ? $field['default_value_php'] : NULL;
+    unset($field['default_value']);
+    unset($field['default_value_php']);
+  }
+  return $field;
+}
 
 /**
- * Create a new field.
- *
- * Any call to this function *must* be immediately followed by a call to
- * content_field_instance_create(), or the database could be left in an
- * inconsistent state.
- *
- * @param $properties
- *   An array of properties to load the field with. Valid keys:
- *   - '' -
- * @return
- *   The ID of the newly-created field.
+ * Collapse field info from field => widget to flattened form values.
  */
-function content_field_create($properties) {
-  // TODO
+function content_field_instance_collapse($field) {
+  if (!isset($field['widget'])) {
+    return $field;
+  }
+  $field['widget_settings'] = !empty($field['widget']) ? $field['widget'] : array();
+  $field['widget_type'] = !empty($field['widget']['type']) ? $field['widget']['type'] : '';
+  $field['weight'] = !empty($field['widget']['weight']) ? $field['widget']['weight'] : 0;
+  $field['label'] = !empty($field['widget']['label']) ? $field['widget']['label'] : $field['field_name'];
+  $field['description'] = !empty($field['widget']['description']) ? $field['widget']['description'] : '';
+  $field['type_name'] = !empty($field['type_name']) ? $field['type_name'] : '';
+
+  if (!empty($field['widget']['module'])) {
+    $widget_module = $field['widget']['module'];
+  }
+  elseif (!empty($field['widget']['type'])) {
+    $widget_types = _content_widget_types();
+    $widget_module = $widget_types[$field['widget']['type']]['module'];
+  }
+  else {
+    $widget_module = '';
+  }
+  $field['widget_module'] = $widget_module;
+  unset($field['widget_settings']['type']);
+  unset($field['widget_settings']['weight']);
+  unset($field['widget_settings']['label']);
+  unset($field['widget_settings']['description']);
+  unset($field['widget_settings']['module']);
+  unset($field['widget']);
+  return $field;
 }
 
 /**
- * Load a field.
+ * Create a new field instance.
  *
- * @param $properties
- *   An array of properties to use in selecting a field. Valid keys:
- *   - '' -
- * @return
- *   The field array.
+ * @param $field
+ *   An array of properties to create the field with, input either in
+ *   the field => widget format used by the content module or as an
+ *   array of form values.
+ *
+ *   Required values:
+ *   - field_name, the name of the field to be created
+ *   - type_name, the content type of the instance to be created
+ *
+ *   If there is no prior instance to create this from, we also need:
+ *   - type, the type of field to create
+ *   - widget_type, the type of widget to use
  */
-function content_field_read($properties) {
-  // TODO
+function content_field_instance_create($field) {
+  include_once('./'. drupal_get_path('module', 'content') .'/content_admin.inc');
+
+  $form_values = $field;
+  $field = content_field_instance_expand($field);
+
+  // If there are prior instances, fill out missing values from the prior values,
+  // otherwise get missing values from default values.
+  $prior_instances = content_field_instance_read(array('field_name' => $field['field_name']));
+  if (!empty($prior_instances) && is_array($prior_instances)) {
+    $prev_field = content_field_instance_expand($prior_instances[0]);
+
+    // Weight, label, and description may have been forced into the $field
+    // by content_field_instance_expand(). If there is a previous instance to
+    // get these values from and there was no value supplied originally, use
+    // the previous value.
+    $field['widget']['weight'] = isset($form_values['weight']) ? $form_values['weight'] : $prev_field['widget']['weight'];
+    $field['widget']['label']  = isset($form_values['label']) ? $form_values['label'] : $prev_field['widget']['label'];
+    $field['widget']['description'] = isset($form_values['description']) ? $form_values['description'] : $prev_field['widget']['description'];
+  }
+  else {
+    $prev_field = array('widget' => array());
+  }
+
+  // If we have a field type, we can build default values for this field type.
+  $default_values = array('widget' => array());
+  if (isset($field['type'])) {
+    $default_values = content_field_default_values($field['type']);
+    $default_instance_values = content_instance_default_values($field['field_name'], $field['type_name'], $field['widget']['type']);
+    $default_values = content_field_instance_expand(array_merge($default_values, $default_instance_values));
+  }
+
+  // Merge default values, previous values, and current values to create
+  // a complete field array.
+  $widget = array_merge($default_values['widget'], $prev_field['widget'], $field['widget']);
+  $field = array_merge($default_values, $prev_field, $field);
+  $field['widget'] = $widget;
+
+  // Make sure we know what module to invoke for field info.
+  if (empty($field['module']) && !empty($field['type'])) {
+    $field_types = _content_field_types();
+    $field['module'] = $field_types[$field['type']]['module'];
+  }
+
+  // The storage type may need to be updated.
+  $field['db_storage'] = content_storage_type($field);
+
+  // Get a fresh copy of the column information whenever a field is created.
+  $field['columns'] = (array) module_invoke($field['module'], 'field_settings', 'database columns', $field);
+
+  if (empty($prev_field['widget']) || $prior_instances < 1) {
+    // If this is the first instance, create the field.
+    $field['db_storage'] = $field['multiple'] > 0 ? CONTENT_DB_STORAGE_PER_FIELD : CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
+    _content_field_write($field, 'create');
+  }
+  elseif (!empty($prev_field['widget']) && $prev_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE && count($prior_instances) > 0) {
+    // If the database storage has changed, update the field and previous instances.
+    $field['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
+
+    foreach ($prior_instances as $instance) {
+      $new_instance = $instance;
+      $new_instance['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
+
+      // Invoke hook_content_fieldapi().
+      module_invoke_all('content_fieldapi', 'update instance', $new_instance);
+
+      content_alter_schema($instance, $new_instance);
+    }
+  }
+
+  $previous_fields = content_field_instance_read(array('field_name' => $field['field_name']));
+  $previous_field = array_pop($previous_fields);
+  $previous_field['db_storage'] = $field['db_storage'];
+
+  // Invoke hook_content_fieldapi().
+  module_invoke_all('content_fieldapi', 'create instance', $field);
+
+  // Update the field and the instance with the latest values.
+  _content_field_write($field, 'update');
+  _content_field_instance_write($field, 'create');
+
+  content_alter_schema(!empty($previous_field) ? $previous_field : array(), $field);
+
+  content_clear_type_cache(TRUE);
+  menu_rebuild(TRUE);
+
+  return $field;
 }
 
 /**
- * Update an existing field.
+ * Update an existing field instance.
  *
- * @param $properties
- *   An array of properties to set in the field. Valid keys:
- *   - '' -
- * @return
- *   The number of fields updated.
+ * @param $field
+ *   An array of properties to update the field with, input either in
+ *   the field => widget format used by the content module or as an
+ *   array of form values.
  */
-function content_field_update($properties) {
-  // TODO
+function content_field_instance_update($field) {
+  include_once('./'. drupal_get_path('module', 'content') .'/content_admin.inc');
+
+  // Get the previous value from the table.
+  $previous = content_field_instance_read(array('field_name' => $field['field_name'], 'type_name' => $field['type_name']));
+  $prev_field = array_pop($previous);
+
+  $field = content_field_instance_expand($field);
+
+  // Create a complete field array by merging the previous and current values,
+  // letting the current values overwrite the previous ones.
+  $widget = array_merge($prev_field['widget'], $field['widget']);
+  $field = array_merge($prev_field, $field);
+  $field['widget'] = $widget;
+
+  // Make sure we know what module to invoke for field info.
+  if (empty($field['module']) && !empty($field['type'])) {
+    $field_types = _content_field_types();
+    $field['module'] = $field_types[$field['type']]['module'];
+  }
+
+  // The storage type may need to be updated.
+  $field['db_storage'] = content_storage_type($field);
+
+  // Changes in field values may affect columns, or column
+  // information may have changed, get a fresh copy.
+  $field['columns'] = (array) module_invoke($field['module'], 'field_settings', 'database columns', $field);
+
+  // If the database storage has changed, update the field and previous instances.
+  $prior_instances = content_field_instance_read(array('field_name' => $field['field_name']));
+
+  if ($prev_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE && count($prior_instances) > 1) {
+
+    // Update the field's data storage.
+    $field['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
+
+    // Update the schema for prior instances to adapt to the change in db storage.
+    foreach ($prior_instances as $instance) {
+      if ($instance['type_name'] != $field['type_name']) {
+        $new_instance = $instance;
+        $new_instance['db_storage'] = CONTENT_DB_STORAGE_PER_FIELD;
+
+        // Invoke hook_content_fieldapi().
+        module_invoke_all('content_fieldapi', 'update instance', $new_instance);
+
+        content_alter_schema($instance, $new_instance);
+      }
+    }
+  }
+
+  // Invoke hook_content_fieldapi().
+  module_invoke_all('content_fieldapi', 'update instance', $field);
+
+  // Update the field and the instance with the latest values.
+  _content_field_write($field, 'update');
+  _content_field_instance_write($field, 'update');
+
+  content_alter_schema($prev_field, $field);
+
+  content_clear_type_cache(TRUE);
+
+  return $field;
 }
 
 /**
- * Delete an existing field.
+ * Write a field record.
  *
- * @param $properties
- *   An array of properties to use in selecting a field. Valid keys:
- *   - 'field_name' - The name of the field to be deleted.
- * @return
- *   The number of fields deleted.
+ * @param $field
+ *   The field array to process.
  */
-function content_field_delete($properties) {
-  $result = db_query("SELECT type_name FROM {node_field_instance} WHERE field_name = '%s'", $properties['field_name']);
-  $type_names = array();
-  while ($type = db_fetch_array($result)) {
-    $type_names[] = $type['type_name'];
+function _content_field_write($field, $op = 'update') {
+  // Rearrange the data to create the global_settings array.
+  $field['global_settings'] = array();
+  $setting_names = module_invoke($field['module'], 'field_settings', 'save', $field);
+  if (is_array($setting_names)) {
+    foreach ($setting_names as $setting) {
+      $field['global_settings'][$setting] = isset($field[$setting]) ? $field[$setting] : '';
+      unset($field[$setting]);
+    }
   }
-  foreach ($type_names as $type_name) {
-    content_field_instance_delete(array('type_name' => $type_name, 'field_name' => $properties['field_name']));
+  // 'columns' is a reserved word in MySQL4, so our column is named 'db_columns'.
+  $field['db_columns'] = $field['columns'];
+
+  switch ($op) {
+    case 'create':
+      db_query("INSERT INTO {node_field} (field_name, type, global_settings, required, multiple, db_storage) VALUES ('%s', '%s', '%s', %d, %d, %d)", $field['field_name'], $field['type'], serialize($field['global_settings']), $field['required'], $field['multiple'], $field['db_storage']);
+      break;
+    case 'update':
+      db_query("UPDATE {node_field} SET type = '%s', global_settings = '%s',
+        required = %d, multiple = %d, db_storage = %d WHERE field_name = '%s'", $field['type'], serialize($field['global_settings']), $field['required'], $field['multiple'], $field['db_storage'], $field['field_name']);
+      break;
   }
-  return (count($type_names) ? 1 : 0);
+  unset($field['db_columns']);
+  return $field;
 }
 
-
 /**
- * Create a new field instance.
+ * Write a field instance record.
  *
- * @param $properties
- *   An array of properties to load the field instance with. Valid keys:
- *   - '' -
- * @return
- *   The ID of the newly-created field instance.
+ * @param $field
+ *   The field array to process.
  */
-function content_field_instance_create($properties) {
-  // TODO
+function _content_field_instance_write($field, $op = 'update') {
+  // Collapse the field => widget format.
+  $field = content_field_instance_collapse($field);
+
+  // Rearrange the data to create the widget_settings array.
+  $setting_names = module_invoke($field['widget_module'], 'widget_settings', 'save', $field);
+  if (is_array($setting_names)) {
+    foreach ($setting_names as $setting) {
+      $field['widget_settings'][$setting] = isset($field[$setting]) ? $field[$setting] : '';
+      unset($field[$setting]);
+    }
+  }
+
+  switch ($op) {
+    case 'create':
+      db_query("INSERT INTO {node_field_instance} (field_name, type_name, weight, label, widget_type, widget_settings, display_settings, description) VALUES ('%s','%s',%d,'%s','%s','%s','%s','%s')", $field['field_name'], $field['type_name'], $field['weight'], $field['label'], $field['widget_type'], serialize($field['widget_settings']), serialize($field['display_settings']), $field['description']);
+      break;
+    case 'update':
+      db_query("UPDATE {node_field_instance} SET weight = %d, label = '%s', widget_type = '%s', widget_settings = '%s', display_settings = '%s', description = '%s' WHERE field_name = '%s' AND type_name = '%s'", $field['weight'], $field['label'], $field['widget_type'], serialize($field['widget_settings']), serialize($field['display_settings']), $field['description'], $field['field_name'], $field['type_name']);
+      break;
+  }
+  return $field;
 }
 
 /**
  * Load a field instance.
  *
- * @param $properties
+ * @param $param
  *   An array of properties to use in selecting a field instance. Valid keys:
  *   - 'type_name' - The name of the content type in which the instance exists.
  *   - 'field_name' - The name of the field whose instance is to be loaded.
+ *   if NULL, all instances will be returned.
  * @return
- *   The field instance array.
+ *   The field arrays.
  */
-function content_field_instance_read($properties) {
-  // TODO
+function content_field_instance_read($param = NULL) {
+  if (is_array($param)) {
+    // Turn the conditions into a query.
+    foreach ($param as $key => $value) {
+      $cond[] = 'nfi.'. db_escape_string($key) ." = '%s'";
+      $args[] = $value;
+    }
+  }
+
+  if (count($cond)) {
+    $cond = implode(' AND ', $cond);
+  }
+  $db_result = db_query("SELECT * FROM {node_field_instance} nfi ".
+    " JOIN {node_field} nf ON nfi.field_name = nf.field_name ".
+    " WHERE ". $cond ." ORDER BY nfi.weight ASC, nfi.label ASC", $args);
+
+  $fields = array();
+  while ($instance = db_fetch_array($db_result)) {
+    // 'columns' is a reserved word in MySQL4, so our column is named 'db_columns'.
+    $instance['columns'] = (array) (!empty($instance['db_columns']) ? unserialize($instance['db_columns']) : array());
+    unset($instance['db_columns']);
+    $instance['global_settings'] = (array) (!empty($instance['global_settings']) ? unserialize($instance['global_settings']) : array());
+
+    foreach ($instance['global_settings'] as $key => $value) {
+      $instance[$key] = $value;
+    }
+    unset($instance['global_settings']);
+
+    if (!empty($instance['widget_settings'])) {
+      $instance['widget_settings'] = (array) unserialize($instance['widget_settings']);
+    }
+    else {
+       $instance['widget_settings'] = array();
+    }
+    if (!empty($instance['display_settings'])) {
+      $instance['display_settings'] = (array) unserialize($instance['display_settings']);
+    }
+    else {
+      $instance['display_settings'] = array();
+    }
+
+    $field = content_field_instance_expand($instance);
+
+    // Invoke hook_content_fieldapi().
+    module_invoke_all('content_fieldapi', 'read instance', $field);
+    $fields[] = $field;
+  }
+  return $fields;
 }
 
 /**
- * Update an existing field instance.
- *
- * @param $properties
- *   An array of properties to set in the field instance. Valid keys:
- *   - '' -
- * @return
- *   The number of field instance updated.
+ * Delete an existing field instance.
  */
-function content_field_instance_update($properties) {
-  // TODO
+function content_field_instance_delete($field_name, $type_name) {
+  include_once('./'. drupal_get_path('module', 'content') .'/content_admin.inc');
+
+  // Get the previous field value.
+  $field = array_pop(content_field_instance_read(array('field_name' => $field_name, 'type_name' => $type_name)));
+
+  // Invoke hook_content_fieldapi().
+  module_invoke_all('content_fieldapi', 'delete instance', $field);
+
+  db_query("DELETE FROM {node_field_instance} WHERE field_name = '%s' AND type_name = '%s'", $field['field_name'], $field['type_name']);
+
+  // If no instances remain, delete the field entirely.
+  $instances = content_field_instance_read(array('field_name' => $field_name));
+  if (sizeof($instances) < 1) {
+    // Remove the fields from the database
+    content_alter_schema($field, array());
+
+    db_query("DELETE FROM {node_field} WHERE field_name = '%s'", $field['field_name']);
+
+    // Deleted fields require a menu rebuild to remove all the field paths.
+    menu_rebuild(TRUE);
+  }
+
+  // If only one instance remains, we may need to change the database
+  // representation for this field.
+  if (sizeof($instances) == 1 && !($field['multiple'])) {
+
+    // Multiple-valued fields are always stored per-field-type.
+    $instance = $instances[0];
+    $new_instance = $instance;
+    $new_instance['db_storage'] = CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
+    _content_field_write($new_instance, 'update');
+
+    content_alter_schema($instance, $new_instance);
+  }
+
+  content_clear_type_cache(TRUE);
+  return $field;
 }
 
 /**
- * Delete an existing field instance.
+ * Delete all data related to a module.
  *
- * @param $properties
- *   An array of properties to use in selecting a field instance. Valid keys:
- *   - 'type_name' - The name of the content type in which the instance exists.
- *   - 'field_name' - The name of the field whose instance is to be deleted.
- * @return
- *   The number of field instances deleted.
+ * @param string $module
  */
-function content_field_instance_delete($properties) {
-  $number_deleted = db_query("DELETE FROM {node_field_instance} WHERE type_name = '%s' AND field_name = '%s'", $properties['type_name'], $properties['field_name']);
+function content_module_delete($module) {
+  // Delete the field data.
+  $results = db_query("SELECT field_name, type_name FROM {node_field_instance} WHERE widget_module = '%s'", $module);
+  while ($field = db_fetch_array($results)) {
+    content_field_instance_delete($field['field_name'], $field['type_name']);
+  }
+  // Force the caches and static arrays to update to the new info.
+  _content_type_info(TRUE, TRUE);
 
-  $type = content_types($properties['type_name']);
-  $field = $type['fields'][$properties['field_name']];
-  $field_types = _content_field_types();
-  $field_type = $field_types[$field['type']];
-  $columns = module_invoke($field_type['module'], 'field_settings', 'database columns', $field);
+}
 
-  $instances = db_result(db_query("SELECT COUNT(*) FROM {node_field_instance} WHERE field_name = '%s'", $properties['field_name']));
+/**
+ * Make changes needed when a content type is created.
+ *
+ * @param $info
+ *   value supplied by hook_node_type()
+ *
+ * node_get_types() is still missing the new type at this point, so no
+ * use to call content_clear_type_cache() or menu_rebuild() here. Instead
+ * we set a flag to make sure it gets rebuilt on next page load.
+ */
+function content_type_create($info) {
+  content_clear_type_cache();
+  $type = content_types($info->type);
+  $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
+  if (!db_table_exists($table)) {
+    switch ($GLOBALS['db_type']) {
+      case 'mysql':
+      case 'mysqli':
+        db_query("CREATE TABLE {". $table ."} (
+          vid int unsigned NOT NULL default '0',
+          nid int unsigned NOT NULL default '0',
+          PRIMARY KEY (vid)
+        ) /*!40100 DEFAULT CHARACTER SET utf8 */");
+      break;
 
-  // If only one instance remains, we may need to change the database
-  // representation for this field.
-  if ($instances == 1) {
-    if (!($field['multiple'])) {
-      // Multiple-valued fields are always stored per-content-type.
-      if (is_array($columns) && count($columns)) {
-        $new_field = $field;
-        $new_field['db_storage'] = CONTENT_DB_STORAGE_PER_CONTENT_TYPE;
-        db_query("UPDATE {node_field} SET db_storage = %d WHERE field_name = '%s'", CONTENT_DB_STORAGE_PER_CONTENT_TYPE, $properties['field_name']);
-        content_alter_db_field($field, $columns, $new_field, $columns);
-      }
+      case 'pgsql':
+        db_query("CREATE TABLE {". $table ."} (
+          vid int_unsigned NOT NULL default '0',
+          nid int_unsigned NOT NULL default '0',
+          PRIMARY KEY (vid)
+        )");
+      break;
     }
+    drupal_set_message(t('The content fields table %name has been created.', array('%name' => $table)));
   }
+}
 
-  // If no instances remain, delete the field entirely.
-  elseif ($instances == 0) {
-    if (is_array($columns) && count($columns)) {
-      content_alter_db_field($field, $columns, array(), array());
+/**
+ * Make changes needed when an existing content type is updated.
+ *
+ * @param $info
+ *   value supplied by hook_node_type()
+ */
+function content_type_update($info) {
+  if (!empty($info->old_type) && $info->old_type != $info->type) {
+    // Rename the content type in all fields that use changed content type.
+    db_query("UPDATE {node_field_instance} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type));
+
+    // Rename the content fields table to match new content type name.
+    $old_type = content_types($info->old_type);
+    $old_name = _content_tablename($old_type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
+    $new_name = _content_tablename($info->type, CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
+    if (db_table_exists($old_name)) {
+      $ret = array();
+      db_rename_table($ret, $old_name, $new_name);
+      watchdog('content', 'Content fields table %old_name has been renamed to %new_name and field instances have been updated.', array(
+        '%old_name' => $old_name, '%new_name' => $new_name));
+    }
+
+    // Rename the variable storing weights for non-CCK fields.
+    if ($extra = variable_get('content_extra_weights_'. $info->old_type, array())) {
+      variable_set('content_extra_weights_'. $info->type, $extra);
+      variable_del('content_extra_weights_'. $info->old_type);
     }
-    db_query("DELETE FROM {node_field} WHERE field_name = '%s'", $properties['field_name']);
   }
 
+  // Reset all content type info and alter the menu paths.
   content_clear_type_cache();
+  menu_rebuild();
+}
+
+/**
+ * Make changes needed when a content type is deleted.
+ *
+ * @param $info
+ *   value supplied by hook_node_type()
+ */
+function content_type_delete($info) {
+  $fields = content_field_instance_read(array('type_name' => $info->type));
+  foreach ($fields as $field) {
+    content_field_instance_delete($field['field_name'], $info->type);
+  }
+  $table = _content_tablename($info->type, CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
+  if (db_table_exists($table)) {
+    $ret = array();
+    db_query('DROP TABLE {'. $table .'}');
+    watchdog('content', 'The content fields table %name has been deleted.', array('%name' => $table));
+  }
 
-  return $number_deleted;
+  menu_rebuild(TRUE);
 }
