Index: content.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/content.module,v
retrieving revision 1.261
diff -u -r1.261 content.module
--- content.module	24 Feb 2008 22:02:05 -0000	1.261
+++ content.module	25 Feb 2008 21:40:22 -0000
@@ -797,10 +797,10 @@
           // or the default value? May need to offer the user an option of
           // how to handle that.
           if (db_result(db_query("SELECT COUNT(*) FROM {". $table ."} WHERE vid = %d", $node->vid))) {
-            drupal_write_record($table, $record, array('vid'));
+            content_write_record($table, $record, array('vid'));
           }
           else {
-            drupal_write_record($table, $record);
+            content_write_record($table, $record);
           }
         }
       }
@@ -821,7 +821,7 @@
             $record['nid'] = $node->nid;
             $record['vid'] = $node->vid;
             $record['delta'] = $delta;
-            drupal_write_record($db_info['table'], $record);
+            content_write_record($db_info['table'], $record);
           }
         }
       }
@@ -842,6 +842,150 @@
 }
 
 /**
+ * Save a record to the database based upon the schema.
+ *
+ * This is a modified version of drupal_write_record(), needed to manage
+ * NULL values. Should be removed when core updates its function.
+ *
+ * Default values are filled in for missing items, and 'serial' (auto increment)
+ * types are filled in with IDs.
+ *
+ * @param $table
+ *   The name of the table; this must exist in schema API.
+ * @param $object
+ *   The object to write. This is a reference, as defaults according to
+ *   the schema may be filled in on the object, as well as ID on the serial
+ *   type(s). Both array an object types may be passed.
+ * @param $update
+ *   If this is an update, specify the primary keys' field names. It is the
+ *   caller's responsibility to know if a record for this object already
+ *   exists in the database. If there is only 1 key, you may pass a simple string.
+ * @return
+ *   Failure to write a record will return FALSE. Otherwise SAVED_NEW or
+ *   SAVED_UPDATED is returned depending on the operation performed. The
+ *   $object parameter contains values for any serial fields defined by
+ *   the $table. For example, $object->nid will be populated after inserting
+ *   a new node.
+ */
+function content_write_record($table, &$object, $update = array()) {
+  // Standardize $update to an array.
+  if (is_string($update)) {
+    $update = array($update);
+  }
+
+  // Convert to an object if needed.
+  if (is_array($object)) {
+    $object = (object) $object;
+    $array = TRUE;
+  }
+  else {
+    $array = FALSE;
+  }
+
+  $schema = drupal_get_schema($table);
+  if (empty($schema)) {
+    return FALSE;
+  }
+
+  $fields = $defs = $values = $serials = $placeholders = array();
+
+  // Go through our schema, build SQL, and when inserting, fill in defaults for
+  // fields that are not set.
+  foreach ($schema['fields'] as $field => $info) {
+    // Special case -- skip serial types if we are updating.
+    if ($info['type'] == 'serial' && count($update)) {
+      continue;
+    }
+
+    // For inserts, populate defaults from Schema if not already provided
+    if (!isset($object->$field) && !count($update) && isset($info['default'])) {
+      $object->$field = $info['default'];
+    }
+
+    // Track serial fields so we can helpfully populate them after the query.
+    if ($info['type'] == 'serial') {
+      $serials[] = $field;
+      // Ignore values for serials when inserting data. Unsupported.
+      unset($object->$field);
+    }
+
+    // Build arrays for the fields, placeholders, and values in our query.
+    if (isset($object->$field)) {
+      $fields[] = $field;
+      $placeholders[] = db_type_placeholder($info['type']);
+
+      if (empty($info['serialize'])) {
+        $values[] = $object->$field;
+      }
+      else if (!empty($object->$field)) {
+        $values[] = serialize($object->$field);
+      }
+      else {
+        $values[] = '';
+      }
+    }
+    else {
+      if (!$info['not null']) {
+        // On update, sets that field to NULL, but just ignore on insertion
+        if (count($update)) {
+          $fields[] = $field;
+          $placeholders[] = 'NULL';
+        }
+      }
+      else {
+        // This is a NULL value in a NON NULL field
+        $fields[] = $field;
+        $placeholders[] = db_type_placeholder($info['type']);
+        $values[] = '';
+      }
+    }
+  }
+
+  // Build the SQL.
+  $query = '';
+  if (!count($update)) {
+    $query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .') VALUES ('. implode(', ', $placeholders) .')';
+    $return = SAVED_NEW;
+  }
+  else {
+    $query = '';
+    foreach ($fields as $id => $field) {
+      if ($query) {
+        $query .= ', ';
+      }
+      $query .= $field .' = '. $placeholders[$id];
+    }
+
+    foreach ($update as $key) {
+      $conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']);
+      $values[] = $object->$key;
+    }
+
+    $query = "UPDATE {". $table ."} SET $query WHERE ". implode(' AND ', $conditions);
+    $return = SAVED_UPDATED;
+  }
+
+  // Execute the SQL.
+  if (db_query($query, $values)) {
+    if ($serials) {
+      // Get last insert ids and fill them in.
+      foreach ($serials as $field) {
+        $object->$field = db_last_insert_id($table, $field);
+      }
+    }
+
+    // If we began with an array, convert back so we don't surprise the caller.
+    if ($array) {
+      $object = (array) $object;
+    }
+
+    return $return;
+  }
+
+  return FALSE;
+}
+
+/**
  * Invoke a field hook.
  *
  * For each operation, both this function and _content_field_invoke_default() are
