### Eclipse Workspace Patch 1.0
#P drupal_test_7
Index: modules/field/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.test,v
retrieving revision 1.6
diff -u -r1.6 field.test
--- modules/field/field.test	17 Mar 2009 03:46:51 -0000	1.6
+++ modules/field/field.test	19 Mar 2009 02:35:52 -0000
@@ -184,36 +184,72 @@
   // most part, these tests pass by not crashing or causing exceptions.
   function testFieldAttachSaveMissingData() {
     $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    $entity = field_test_create_stub_entity();
 
-    // Insert: Field is missing
+    // Insert: Field is missing.
     field_attach_insert($entity_type, $entity);
-    $count = db_result(db_query("SELECT COUNT(*) FROM {{$this->table}}"));
-    $this->assertEqual($count, 0, 'Missing field results in no inserts');
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), 'Missing field results in no inserts');
 
-    // Insert: Field is NULL
+    // Insert: Field is NULL.
+    field_cache_clear();
     $entity->{$this->field_name} = NULL;
     field_attach_insert($entity_type, $entity);
-    $count = db_result(db_query("SELECT COUNT(*) FROM {{$this->table}}"));
-    $this->assertEqual($count, 0, 'NULL field results in no inserts');
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), 'NULL field results in no inserts');
 
-    // Add some real data
-    $entity->{$this->field_name} = array(0 => array('value' => 1));
+    // Add some real data.
+    field_cache_clear();
+    $values = array(0 => array('value' => 1));
+    $entity->{$this->field_name} = $values;
     field_attach_insert($entity_type, $entity);
-    $count = db_result(db_query("SELECT COUNT(*) FROM {{$this->table}}"));
-    $this->assertEqual($count, 1, 'Field data saved');
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertEqual($entity->{$this->field_name}, $values, 'Field data saved');
 
     // Update: Field is missing. Data should survive.
+    field_cache_clear();
     unset($entity->{$this->field_name});
     field_attach_update($entity_type, $entity);
-    $count = db_result(db_query("SELECT COUNT(*) FROM {{$this->table}}"));
-    $this->assertEqual($count, 1, 'Missing field leaves data in table');
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertEqual($entity->{$this->field_name}, $values, 'Missing field leaves data in place');
 
     // Update: Field is NULL. Data should be wiped.
+    field_cache_clear();
     $entity->{$this->field_name} = NULL;
     field_attach_update($entity_type, $entity);
-    $count = db_result(db_query("SELECT COUNT(*) FROM {{$this->table}}"));
-    $this->assertEqual($count, 0, 'NULL field leaves no data in table');
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), 'NULL field leaves no data in table');
+  }
+
+  // Test insert with missing or invalid fields, with a field having
+  // default values set.
+  function testFieldAttachSaveMissingDataDefaultValue() {
+    // Add a default value.
+    $this->instance['default_value_function'] = 'field_test_default_value';
+    field_update_instance($this->instance);
+
+    $entity_type = 'test_entity';
+
+    // Insert: Field is NULL.
+    $entity = field_test_create_stub_entity();
+    $entity->{$this->field_name} = NULL;
+    field_attach_insert($entity_type, $entity);
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), 'NULL field results in no inserts');
+
+    // Insert: Field is missing.
+    field_cache_clear();
+    $entity = field_test_create_stub_entity();
+    field_attach_insert($entity_type, $entity);
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
+    $this->assertEqual($entity->{$this->field_name}, $values, 'Missing field results in default value being inserted');
   }
 
   function testFieldAttachViewAndPreprocess() {
Index: modules/field/field.attach.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v
retrieving revision 1.6
diff -u -r1.6 field.attach.inc
--- modules/field/field.attach.inc	17 Mar 2009 03:46:51 -0000	1.6
+++ modules/field/field.attach.inc	19 Mar 2009 02:35:51 -0000
@@ -32,7 +32,7 @@
 
 /**
  * Argument for an insert operation.
- * This is used in hook_field_storage_write when updating an 
+ * This is used in hook_field_storage_write when updating an
  * existing object.
  */
 define('FIELD_STORAGE_UPDATE', 'update');
@@ -383,22 +383,29 @@
 }
 
 /**
- * Save field data for a new object. The passed in object must
- * already contain its id and (if applicable) revision id attributes.
+ * Save field data for a new object.
+ *
+ * The passed in object must already contain its id and (if applicable)
+ * revision id attributes.
+ * Default values (if any) will be inserted for fields not present in the
+ * $object.
  *
  * @param $obj_type
  *   The type of $object; e.g. 'node' or 'user'.
  * @param $object
  *   The object with fields to save.
+ * @return
+ *   Default values (if any) will be added to the $object parameter for fields
+ *   it leaves unspecified.
  */
 function _field_attach_insert($obj_type, &$object) {
-
   // Let other modules act on inserting the object.
   foreach (module_implements('field_attach_insert') as $module) {
     $function = $module . '_field_attach_insert';
     $function($obj_type, $object);
   }
 
+  _field_invoke_default('insert', $obj_type, $object);
   _field_invoke('insert', $obj_type, $object);
   module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_INSERT);
 
Index: modules/field/field.default.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v
retrieving revision 1.4
diff -u -r1.4 field.default.inc
--- modules/field/field.default.inc	8 Mar 2009 04:25:04 -0000	1.4
+++ modules/field/field.default.inc	19 Mar 2009 02:35:51 -0000
@@ -50,6 +50,24 @@
   }
 }
 
+function field_default_insert($obj_type, &$object, $field, $instance, &$items) {
+  // Insert default value if no $object->$field_name entry was provided.
+  // This can happen with programmatic saves, or on form-based creation where
+  // the current user doesn't have 'edit' permission for the field.
+
+  // _field_invoke() populates $items with an empty array if the $object has no
+  // entry for the field, so we check on the $object itself.
+  if (!property_exists($object, $field['field_name']) && !empty($instance['default_value_function'])) {
+    $function = $instance['default_value_function'];
+    if (drupal_function_exists($function)) {
+      $items = $function($obj_type, $object, $field, $instance);
+      // _field_invoke() does not add back items for fields not present in the
+      // original $object, so add them manually.
+      $object->{$field['field_name']} = $items;
+    }
+  }
+}
+
 /**
  * The 'view' operation constructs the $object in a way that you can use
  * drupal_render() to display the formatted output for an individual field.
Index: modules/field/field.form.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.form.inc,v
retrieving revision 1.5
diff -u -r1.5 field.form.inc
--- modules/field/field.form.inc	10 Mar 2009 09:45:31 -0000	1.5
+++ modules/field/field.form.inc	19 Mar 2009 02:35:52 -0000
@@ -22,9 +22,8 @@
   $field_name = $field['field_name'];
 
   // If the field is not accessible, don't add anything. The field value will
-  // be left unchanged on update, or considered empty on insert.
-  // TODO : if/when field_attach_insert() takes care of default values,
-  // unaccessible fields will automatically get the default value on insert.
+  // be left unchanged on update, or considered empty on insert (default value
+  // will be inserted if applicable).
   if (!field_access('edit', $field)) {
     return $addition;
   }
Index: modules/simpletest/tests/field_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.module,v
retrieving revision 1.2
diff -u -r1.2 field_test.module
--- modules/simpletest/tests/field_test.module	5 Feb 2009 03:42:58 -0000	1.2
+++ modules/simpletest/tests/field_test.module	19 Mar 2009 02:35:53 -0000
@@ -511,4 +511,11 @@
   }
   $output = implode('|', $items);
   return $settings['test_formatter_setting_multiple'] . '|' . $output;
+}
+
+/**
+ * Sample function to test default value assignment.
+ */
+function field_test_default_value($obj_type, $object, $field, $instance) {
+  return array(array('value' => 99));
 }
\ No newline at end of file
