### 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.30
diff -u -r1.30 field.test
--- modules/field/field.test	7 Jul 2009 09:28:07 -0000	1.30
+++ modules/field/field.test	7 Jul 2009 23:06:00 -0000
@@ -54,213 +54,221 @@
    * Works independently of the underlying field storage backend. Inserts or
    * updates random field data and then loads and verifies the data.
    */
-  function testFieldAttachSaveLoad() {
-    // Configure the instance so that we test hook_field_load() (see
-    // field_test_field_load() in field_test.module).
-    $this->instance['settings']['test_hook_field_load'] = TRUE;
-    field_update_instance($this->instance);
-
-    $entity_type = 'test_entity';
-    $values = array();
-
-    // TODO : test empty values filtering and "compression" (store consecutive deltas).
-
-    // Preparation: create three revisions and store them in $revision array.
-    for ($revision_id = 0; $revision_id < 3; $revision_id++) {
-      $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
-      // Note: we try to insert one extra value.
-      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
-      $current_revision = $revision_id;
-      // If this is the first revision do an insert.
-      if (!$revision_id) {
-        $revision[$revision_id]->{$this->field_name} = $values[$revision_id];
-        field_attach_insert($entity_type, $revision[$revision_id]);
-      }
-      else {
-        // Otherwise do an update.
-        $revision[$revision_id]->{$this->field_name} = $values[$revision_id];
-        field_attach_update($entity_type, $revision[$revision_id]);
-      }
-    }
-
-    // Confirm current revision loads the correct data.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    // Number of values per field loaded equals the field cardinality.
-    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], t('Currrent revision: expected number of values'));
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      // The field value loaded matches the one inserted or updated.
-      $this->assertEqual($entity->{$this->field_name}[$delta]['value'] , $values[$current_revision][$delta]['value'], t('Currrent revision: expected value %delta was found.', array('%delta' => $delta)));
-      // The value added in hook_field_load() is found.
-      $this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Currrent revision: extra information for value %delta was found', array('%delta' => $delta)));
-    }
-
-    // Confirm each revision loads the correct data.
-    foreach (array_keys($revision) as $revision_id) {
-      $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $entity));
-      // Number of values per field loaded equals the field cardinality.
-      $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
-      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-        // The field value loaded matches the one inserted or updated.
-        $this->assertEqual($entity->{$this->field_name}[$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
-        // The value added in hook_field_load() is found.
-        $this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
-      }
-    }
-  }
-
-  /**
-   * Test the 'multiple' load feature.
-   */
-  function testFieldAttachLoadMultiple() {
-    $entity_type = 'test_entity';
-
-    // Define 2 bundles.
-    $bundles = array(
-      1 => 'test_bundle_1',
-      2 => 'test_bundle_2',
-    );
-    field_test_create_bundle($bundles[1]);
-    field_test_create_bundle($bundles[2]);
-    // Define 3 fields:
-    // - field_1 is in bundle_1 and bundle_2,
-    // - field_2 is in bundle_1,
-    // - field_3 is in bundle_2.
-    $field_bundles_map = array(
-      1 => array(1, 2),
-      2 => array(1),
-      3 => array(2),
-    );
-    for ($i = 1; $i <= 3; $i++) {
-      $field_names[$i] = 'field_' . $i;
-      $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
-      field_create_field($field);
-      foreach ($field_bundles_map[$i] as $bundle) {
-        $instance = array(
-          'field_name' => $field_names[$i],
-          'bundle' => $bundles[$bundle],
-          'settings' => array(
-            // Configure the instance so that we test hook_field_load()
-            // (see field_test_field_load() in field_test.module).
-            'test_hook_field_load' => TRUE,
-          ),
-        );
-        field_create_instance($instance);
-      }
-    }
-
-    // Create one test entity per bundle, with random values.
-    foreach ($bundles as $index => $bundle) {
-      $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
-      $entity = clone($entities[$index]);
-      $instances = field_info_instances($bundle);
-      foreach ($instances as $field_name => $instance) {
-        $values[$index][$field_name] = mt_rand(1, 127);
-        $entity->$field_name = array(array('value' => $values[$index][$field_name]));
-      }
-      field_attach_insert($entity_type, $entity);
-    }
-
-    // Check that a single load correctly loads field values for both entities.
-    field_attach_load($entity_type, $entities);
-    foreach ($entities as $index => $entity) {
-      $instances = field_info_instances($bundles[$index]);
-      foreach ($instances as $field_name => $instance) {
-        // The field value loaded matches the one inserted.
-        $this->assertEqual($entity->{$field_name}[0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
-        // The value added in hook_field_load() is found.
-        $this->assertEqual($entity->{$field_name}[0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
-      }
-    }
-  }
-
-  /**
-   * Tests insert and update with missing or NULL fields.
-   */
-  function testFieldAttachSaveMissingData() {
-    $entity_type = 'test_entity';
-    $entity_init = field_test_create_stub_entity();
-
-    // Insert: Field is missing.
-    $entity = clone($entity_init);
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
-
-    // Insert: Field is NULL.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
-
-    // Add some real data.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $values = $this->_generateTestFieldValues(1);
-    $entity->{$this->field_name} = $values;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertEqual($entity->{$this->field_name}, $values, t('Field data saved'));
-
-    // Update: Field is missing. Data should survive.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertEqual($entity->{$this->field_name}, $values, t('Update: missing field leaves existing values in place'));
-
-    // Update: Field is NULL. Data should be wiped.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = NULL;
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
-  }
-
-  /**
-   * Test insert with missing or NULL fields, with default value.
-   */
-  function testFieldAttachSaveMissingDataDefaultValue() {
-    // Add a default value.
-    $this->instance['default_value_function'] = 'field_test_default_value';
-    field_update_instance($this->instance);
-
-    $entity_type = 'test_entity';
-    $entity_init = field_test_create_stub_entity();
-
-    // Insert: Field is NULL.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
-
-    // Insert: Field is missing.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    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, t('Insert: missing field results in default value saved'));
-  }
+//  function testFieldAttachSaveLoad() {
+//    // Configure the instance so that we test hook_field_load() (see
+//    // field_test_field_load() in field_test.module).
+//    $this->instance['settings']['test_hook_field_load'] = TRUE;
+//    field_update_instance($this->instance);
+//
+//    $entity_type = 'test_entity';
+//    $values = array();
+//
+//    // TODO : test empty values filtering and "compression" (store consecutive deltas).
+//
+//    // Preparation: create three revisions and store them in $revision array.
+//    for ($revision_id = 0; $revision_id < 3; $revision_id++) {
+//      $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+//      // Note: we try to insert one extra value.
+//      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+//      $current_revision = $revision_id;
+//      // If this is the first revision do an insert.
+//      if (!$revision_id) {
+//        $revision[$revision_id]->{$this->field_name} = $values[$revision_id];
+//        field_attach_insert($entity_type, $revision[$revision_id]);
+//      }
+//      else {
+//        // Otherwise do an update.
+//        $revision[$revision_id]->{$this->field_name} = $values[$revision_id];
+//        field_attach_update($entity_type, $revision[$revision_id]);
+//      }
+//    }
+//
+//    // Confirm current revision loads the correct data.
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $entity));
+//    // Number of values per field loaded equals the field cardinality.
+//    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], t('Currrent revision: expected number of values'));
+//    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+//      // The field value loaded matches the one inserted or updated.
+//      $this->assertEqual($entity->{$this->field_name}[$delta]['value'] , $values[$current_revision][$delta]['value'], t('Currrent revision: expected value %delta was found.', array('%delta' => $delta)));
+//      // The value added in hook_field_load() is found.
+//      $this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Currrent revision: extra information for value %delta was found', array('%delta' => $delta)));
+//    }
+//
+//    // Confirm each revision loads the correct data.
+//    foreach (array_keys($revision) as $revision_id) {
+//      $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+//      field_attach_load_revision($entity_type, array(0 => $entity));
+//      // Number of values per field loaded equals the field cardinality.
+//      $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
+//      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+//        // The field value loaded matches the one inserted or updated.
+//        $this->assertEqual($entity->{$this->field_name}[$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
+//        // The value added in hook_field_load() is found.
+//        $this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
+//      }
+//    }
+//  }
+//
+//  /**
+//   * Test the 'multiple' load feature.
+//   */
+//  function testFieldAttachLoadMultiple() {
+//    $entity_type = 'test_entity';
+//
+//    // Define 2 bundles.
+//    $bundles = array(
+//      1 => 'test_bundle_1',
+//      2 => 'test_bundle_2',
+//    );
+//    field_test_create_bundle($bundles[1]);
+//    field_test_create_bundle($bundles[2]);
+//    // Define 3 fields:
+//    // - field_1 is in bundle_1 and bundle_2,
+//    // - field_2 is in bundle_1,
+//    // - field_3 is in bundle_2.
+//    $field_bundles_map = array(
+//      1 => array(1, 2),
+//      2 => array(1),
+//      3 => array(2),
+//    );
+//    for ($i = 1; $i <= 3; $i++) {
+//      $field_names[$i] = 'field_' . $i;
+//      $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
+//      field_create_field($field);
+//      foreach ($field_bundles_map[$i] as $bundle) {
+//        $instance = array(
+//          'field_name' => $field_names[$i],
+//          'bundle' => $bundles[$bundle],
+//          'settings' => array(
+//            // Configure the instance so that we test hook_field_load()
+//            // (see field_test_field_load() in field_test.module).
+//            'test_hook_field_load' => TRUE,
+//          ),
+//        );
+//        field_create_instance($instance);
+//      }
+//    }
+//
+//    // Create one test entity per bundle, with random values.
+//    foreach ($bundles as $index => $bundle) {
+//      $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
+//      $entity = clone($entities[$index]);
+//      $instances = field_info_instances($bundle);
+//      foreach ($instances as $field_name => $instance) {
+//        $values[$index][$field_name] = mt_rand(1, 127);
+//        $entity->$field_name = array(array('value' => $values[$index][$field_name]));
+//      }
+//      field_attach_insert($entity_type, $entity);
+//    }
+//
+//    // Check that a single load correctly loads field values for both entities.
+//    field_attach_load($entity_type, $entities);
+//    foreach ($entities as $index => $entity) {
+//      $instances = field_info_instances($bundles[$index]);
+//      foreach ($instances as $field_name => $instance) {
+//        // The field value loaded matches the one inserted.
+//        $this->assertEqual($entity->{$field_name}[0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
+//        // The value added in hook_field_load() is found.
+//        $this->assertEqual($entity->{$field_name}[0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
+//      }
+//    }
+//
+//    // Check that the single-field load option works.
+//    $entity = field_test_create_stub_entity(1, 1, $bundles[1]);
+//    field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_name' => $field_names[1]));
+//    $this->assertEqual($entity->{$field_names[1]}[0]['value'], $values[1][$field_names[1]], t('Entity %index: expected value was found.', array('%index' => 1)));
+//    $this->assertEqual($entity->{$field_names[1]}[0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => 1)));
+//    $this->assert(!isset($entity->{$field_names[2]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
+//    $this->assert(!isset($entity->{$field_names[3]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
+//  }
+//
+//  /**
+//   * Tests insert and update with missing or NULL fields.
+//   */
+//  function testFieldAttachSaveMissingData() {
+//    $entity_type = 'test_entity';
+//    $entity_init = field_test_create_stub_entity();
+//
+//    // Insert: Field is missing.
+//    $entity = clone($entity_init);
+//    field_attach_insert($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
+//
+//    // Insert: Field is NULL.
+//    field_cache_clear();
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = NULL;
+//    field_attach_insert($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
+//
+//    // Add some real data.
+//    field_cache_clear();
+//    $entity = clone($entity_init);
+//    $values = $this->_generateTestFieldValues(1);
+//    $entity->{$this->field_name} = $values;
+//    field_attach_insert($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertEqual($entity->{$this->field_name}, $values, t('Field data saved'));
+//
+//    // Update: Field is missing. Data should survive.
+//    field_cache_clear();
+//    $entity = clone($entity_init);
+//    field_attach_update($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertEqual($entity->{$this->field_name}, $values, t('Update: missing field leaves existing values in place'));
+//
+//    // Update: Field is NULL. Data should be wiped.
+//    field_cache_clear();
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = NULL;
+//    field_attach_update($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
+//  }
+//
+//  /**
+//   * Test insert with missing or NULL fields, with default value.
+//   */
+//  function testFieldAttachSaveMissingDataDefaultValue() {
+//    // Add a default value.
+//    $this->instance['default_value_function'] = 'field_test_default_value';
+//    field_update_instance($this->instance);
+//
+//    $entity_type = 'test_entity';
+//    $entity_init = field_test_create_stub_entity();
+//
+//    // Insert: Field is NULL.
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = NULL;
+//    field_attach_insert($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    field_attach_load($entity_type, array($entity->ftid => $entity));
+//    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
+//
+//    // Insert: Field is missing.
+//    field_cache_clear();
+//    $entity = clone($entity_init);
+//    field_attach_insert($entity_type, $entity);
+//
+//    $entity = clone($entity_init);
+//    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, t('Insert: missing field results in default value saved'));
+//  }
 
   /**
    * Test field_attach_query().
@@ -328,7 +336,7 @@
     $result = field_attach_query($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
     $this->assertTrue(isset($result[$entity_types[1]][1]) && !isset($result[$entity_types[2]][2]), t("Query on a value common to both objects and an 'entity_id' condition only returns the relevant object"));
 
-    // Test FIELD_QUERY_RETURN_IDS result format.
+    // Test result format.
     $conditions = array(array('value', $values[0]));
     $result = field_attach_query($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
     $expected = array(
@@ -336,460 +344,500 @@
         $entities[1]->ftid => field_test_create_stub_entity($entities[1]->ftid, $entities[1]->ftvid),
       )
     );
-    $this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result'));
-  }
-
-  /**
-   * Test field_attach_query_revisions().
-   */
-  function testFieldAttachQueryRevisions() {
-    $cardinality = $this->field['cardinality'];
-
-    // Create first object revision with random (distinct) values.
-    $entity_type = 'test_entity';
-    $entities = array(1 => field_test_create_stub_entity(1, 1), 2 => field_test_create_stub_entity(1, 2));
-    $values = array();
-    for ($delta = 0; $delta < $cardinality; $delta++) {
-      do {
-        $value = mt_rand(1, 127);
-      } while (in_array($value, $values));
-      $values[$delta] = $value;
-      $entities[1]->{$this->field_name}[$delta] = array('value' => $values[$delta]);
-    }
-    field_attach_insert($entity_type, $entities[1]);
+    $this->assertEqual($result, $expected, t('Result format is correct.'));
 
-    // Create second object revision, sharing a value with the first one.
-    $common_value = $values[$cardinality - 1];
-    $entities[2]->{$this->field_name}[0] = array('value' => $common_value);
-    field_attach_update($entity_type, $entities[2]);
+    // Now test the count/offset paging capability.
 
-    // Query on the object's values.
-    for ($delta = 0; $delta < $cardinality; $delta++) {
-      $conditions = array(array('value', $values[$delta]));
-      $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
-      $this->assertTrue(isset($result[$entity_type][1]), t('Query on value %delta returns the object', array('%delta' => $delta)));
-    }
-
-    // Query on a value that is not in the object.
-    do {
-      $different_value = mt_rand(1, 127);
-    } while (in_array($different_value, $values));
-    $conditions = array(array('value', $different_value));
-    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
-    $this->assertFalse(isset($result[$entity_type][1]), t("Query on a value that is not in the object doesn't return the object"));
-
-    // Query on the value shared by both objects, and discriminate using
-    // additional conditions.
-
-    $conditions = array(array('value', $common_value));
-    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
-    $this->assertTrue(isset($result[$entity_type][1]) && isset($result[$entity_type][2]), t('Query on a value common to both objects returns both objects'));
-
-    $conditions = array(array('revision_id', $entities[1]->ftvid), array('value', $common_value));
-    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
-    $this->assertTrue(isset($result[$entity_type][1]) && !isset($result[$entity_type][2]), t("Query on a value common to both objects and a 'revision_id' condition only returns the relevant object"));
-
-    // Test FIELD_QUERY_RETURN_IDS result format.
-    $conditions = array(array('value', $values[0]));
-    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
-    $expected = array(
-      $entity_type => array(
-        $entities[1]->ftid => field_test_create_stub_entity($entities[1]->ftid, $entities[1]->ftvid),
-      )
-    );
-    $this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result'));
-  }
-
-  function testFieldAttachViewAndPreprocess() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Populate values to be displayed.
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity->{$this->field_name} = $values;
-
-    // Simple formatter, label displayed.
-    $formatter_setting = $this->randomName();
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'field_test_default',
-        'settings' => array(
-          'test_formatter_setting' => $formatter_setting,
-        )
-      ),
-    );
-    field_update_instance($this->instance);
-    $entity->content = field_attach_view($entity_type, $entity);
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertRaw($this->instance['label'], "Label is displayed.");
-    foreach ($values as $delta => $value) {
-      $this->content = $output;
-      $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
-    }
-
-    // Label hidden.
-    $this->instance['display']['full']['label'] = 'hidden';
-    field_update_instance($this->instance);
-    $entity->content = field_attach_view($entity_type, $entity);
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
-
-    // Field hidden.
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'hidden',
-      ),
-    );
-    field_update_instance($this->instance);
-    $entity->content = field_attach_view($entity_type, $entity);
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
-    foreach ($values as $delta => $value) {
-      $this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
-    }
-
-    // Multiple formatter.
-    $formatter_setting = $this->randomName();
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'field_test_multiple',
-        'settings' => array(
-          'test_formatter_setting_multiple' => $formatter_setting,
-        )
-      ),
-    );
-    field_update_instance($this->instance);
-    $entity->content = field_attach_view($entity_type, $entity);
-    $output = drupal_render($entity->content);
-    $display = $formatter_setting;
-    foreach ($values as $delta => $value) {
-      $display .= "|$delta:{$value['value']}";
-    }
-    $this->content = $output;
-    $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
-
-    // TODO:
-    // - check display order with several fields
-  }
-
-  function testFieldAttachDelete() {
-    $entity_type = 'test_entity';
-    $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Create revision 0
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $rev[0]->{$this->field_name} = $values;
-    field_attach_insert($entity_type, $rev[0]);
-
-    // Create revision 1
-    $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
-    $rev[1]->{$this->field_name} = $values;
-    field_attach_update($entity_type, $rev[1]);
-
-    // Create revision 2
-    $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    $rev[2]->{$this->field_name} = $values;
-    field_attach_update($entity_type, $rev[2]);
-
-    // Confirm each revision loads
-    foreach (array_keys($rev) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values.");
-    }
-
-    // Delete revision 1, confirm the other two still load.
-    field_attach_delete_revision($entity_type, $rev[1]);
-    foreach (array(0, 2) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values.");
-    }
-
-    // Confirm the current revision still loads
-    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object current revision has {$this->field['cardinality']} values.");
-
-    // Delete all field data, confirm nothing loads
-    field_attach_delete($entity_type, $rev[2]);
-    foreach (array(0, 1, 2) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertIdentical($read->{$this->field_name}, array(), "The test object revision $vid is deleted.");
-    }
-    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertIdentical($read->{$this->field_name}, array(), t('The test object current revision is deleted.'));
-  }
-
-  function testFieldAttachCreateRenameBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_fieldable_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_create_bundle($new_bundle);
-
-    // Add an instance to that bundle.
-    $this->instance['bundle'] = $new_bundle;
-    field_create_instance($this->instance);
-
-    // Save an object with data in the field.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity->{$this->field_name} = $values;
-    $entity_type = 'test_entity';
-    field_attach_insert($entity_type, $entity);
-
-    // Verify the field data is present on load.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data are retrieved for the new bundle");
-
-    // Rename the bundle. This has to be initiated by the module so that its
-    // hook_fieldable_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_rename_bundle($this->instance['bundle'], $new_bundle);
-
-    // Check that the instance definition has been updated.
-    $this->instance = field_info_instance($this->field_name, $new_bundle);
-    $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
-
-    // Verify the field data is present on load.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Bundle name has been updated in the field storage");
-  }
-
-  function testFieldAttachDeleteBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_fieldable_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_create_bundle($new_bundle);
-
-    // Add an instance to that bundle.
-    $this->instance['bundle'] = $new_bundle;
-    field_create_instance($this->instance);
-
-    // Create a second field for the test bundle
-    $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
-    field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'bundle' => $this->instance['bundle'],
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      // test_field has no instance settings
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'settings' => array(
-          'size' => mt_rand(0, 255))));
-    field_create_instance($instance);
-
-    // Save an object with data for both fields
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity->{$this->field_name} = $values;
-    $entity->{$field_name} = $this->_generateTestFieldValues(1);
-    $entity_type = 'test_entity';
-    field_attach_insert($entity_type, $entity);
-
-    // Verify the fields are present on load
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}), 4, "First field got loaded");
-    $this->assertEqual(count($entity->{$field_name}), 1, "Second field got loaded");
-
-    // Delete the bundle. This has to be initiated by the module so that its
-    // hook_fieldable_info() is consistent.
-    field_test_delete_bundle($this->instance['bundle']);
-
-    // Verify no data gets loaded
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertFalse(isset($entity->{$this->field_name}), "No data for first field");
-    $this->assertFalse(isset($entity->{$field_name}), "No data for second field");
-
-    // Verify that the instances are gone
-    $this->assertFalse(field_read_instance($this->field_name, $this->instance['bundle']), "First field is deleted");
-    $this->assertFalse(field_read_instance($field_name, $instance['bundle']), "Second field is deleted");
-  }
-
-  /**
-   * Test field cache.
-   */
-  function testFieldAttachCache() {
-    // Initialize random values and a test entity.
-    $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-
-    $noncached_type = 'test_entity';
-    $cached_type = 'test_cacheable_entity';
-
-
-    // Non-cacheable entity type.
-    $cid = "field:$noncached_type:{$entity_init->ftid}";
-
-    // Check that no initial cache entry is present.
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
-
-    // Save, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_insert($noncached_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
-
-    // Load, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    field_attach_load($noncached_type, array($entity->ftid => $entity));
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
-
-
-    // Cacheable entity type.
-    $cid = "field:$cached_type:{$entity_init->ftid}";
-
-    // Check that no initial cache entry is present.
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
-
-    // Save, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_insert($cached_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($cached_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
-
-    // Update with different values, and check that the cache entry is wiped.
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_update($cached_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($cached_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
-
-    // Create a new revision, and check that the cache entry is wiped.
-    $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_update($cached_type, $entity);
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($cached_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
-
-    // Delete, and check that the cache entry is wiped.
-    field_attach_delete($cached_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
-  }
-
-  // Verify that field_attach_validate() invokes the correct
-  // hook_field_validate.
-  function testFieldAttachValidate() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Set up values to generate errors
-    $values = array();
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = -1;
-      $values[$delta]['_error_element'] = 'field_error_' . $delta;
-    }
-    // Arrange for item 1 not to generate an error
-    $values[1]['value'] = 1;
-    $entity->{$this->field_name} = $values;
-
-    try {
-      field_attach_validate($entity_type, $entity);
-    }
-    catch (FieldValidationException $e) {
-      $errors = $e->errors;
-    }
+    // Create a new bundle with an instance of the field.
+    field_test_create_bundle('offset_bundle', 'Offset Test Bundle');
+    $this->instance2 = $this->instance;
+    $this->instance2['bundle'] = 'offset_bundle';
+    field_create_instance($this->instance2);
 
-    foreach ($values as $delta => $value) {
-      if ($value['value'] != 1) {
-        $this->assertIdentical($errors[$this->field_name][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
-        $this->assertEqual(count($errors[$this->field_name][$delta]), 1, "Only one error set on value $delta");
-        unset($errors[$this->field_name][$delta]);
+    // Create 20 test objects, using the new bundle, but with
+    // non-sequential ids so we can tell we are getting the right ones
+    // back. We do not need unique values since field_attach_query()
+    // won't return them anyway.
+    $offset_entities = array();
+    $offset_id = mt_rand(1, 3);
+    for ($i = 0; $i < 20; ++$i) {
+      $offset_id += mt_rand(2, 5);
+      $offset_entities[$offset_id] = field_test_create_stub_entity($offset_id, $offset_id, 'offset_bundle');
+      $offset_entities[$offset_id]->{$this->field_name}[0] = array('value' => $offset_id);
+      field_attach_insert('test_entity', $offset_entities[$offset_id]);
+    }
+
+    // Query for the offset entities in batches, making sure we get
+    // back the right ones.
+    $cursor = 0;
+    foreach (array(1 => 1, 3 => 3, 5 => 5, 8 => 8, 13 => 3) as $count => $expect) {
+      $found = field_attach_query($this->field_name, array(array('bundle', 'offset_bundle')), $count, $cursor);
+      if (isset($found['test_entity'])) {
+        $this->assertEqual(count($found['test_entity']), $expect, t('Requested @count, expected @expect, got @found, cursor @cursor', array('@count' => $count, '@expect' => $expect, '@found' => count($found['test_entity']), '@cursor' => $cursor)));
+        foreach ($found['test_entity'] as $id => $entity) {
+          $this->assert(isset($offset_entities[$id]), "Entity $id found");
+          unset($offset_entities[$id]);
+        }
       }
       else {
-        $this->assertFalse(isset($errors[$this->field_name][$delta]), "No error set on value $delta");
+        $this->assertEqual(0, $expect, t('Requested @count, expected @expect, got @found, cursor @cursor', array('@count' => $count, '@expect' => $expect, '@found' => 0, '@cursor' => $cursor)));
       }
     }
-    $this->assertEqual(count($errors[$this->field_name]), 0, 'No extraneous errors set');
-  }
-
-  // Validate that FAPI elements are generated. This could be much
-  // more thorough, but it does verify that the correct widgets show up.
-  function testFieldAttachForm() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    $form = $form_state = array();
-    field_attach_form($entity_type, $entity, $form, $form_state);
-
-    $this->assertEqual($form[$this->field_name]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      // field_test_widget uses 'textfield'
-      $this->assertEqual($form[$this->field_name][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
-    }
+    $this->assertEqual(count($offset_entities), 0, "All entities found");
+    $this->assertEqual($cursor, FIELD_QUERY_COMPLETE, "Cursor is FIELD_QUERY_COMPLETE");
   }
 
-  function testFieldAttachSubmit() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Build the form.
-    $form = $form_state = array();
-    field_attach_form($entity_type, $entity, $form, $form_state);
-
-    // Simulate incoming values.
-    $values = array();
-    $weights = array();
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = mt_rand(1, 127);
-      // Assign random weight.
-      do {
-        $weight = mt_rand(0, $this->field['cardinality']);
-      } while (in_array($weight, $weights));
-      $weights[$delta] = $weight;
-      $values[$delta]['_weight'] = $weight;
-    }
-    // Leave an empty value. 'field_test' fields are empty if empty().
-    $values[1]['value'] = 0;
-
-    $form_state['values'] = array($this->field_name => $values);
-    field_attach_submit($entity_type, $entity, $form, $form_state);
-
-    asort($weights);
-    $expected_values = array();
-    foreach ($weights as $key => $value) {
-      if ($key != 1) {
-        $expected_values[] = array('value' => $values[$key]['value']);
-      }
-    }
-    $this->assertIdentical($entity->{$this->field_name}, $expected_values, 'Submit filters empty values');
-  }
+  /**
+   * Test field_attach_query_revisions().
+   */
+//  function testFieldAttachQueryRevisions() {
+//    $cardinality = $this->field['cardinality'];
+//
+//    // Create first object revision with random (distinct) values.
+//    $entity_type = 'test_entity';
+//    $entities = array(1 => field_test_create_stub_entity(1, 1), 2 => field_test_create_stub_entity(1, 2));
+//    $values = array();
+//    for ($delta = 0; $delta < $cardinality; $delta++) {
+//      do {
+//        $value = mt_rand(1, 127);
+//      } while (in_array($value, $values));
+//      $values[$delta] = $value;
+//      $entities[1]->{$this->field_name}[$delta] = array('value' => $values[$delta]);
+//    }
+//    field_attach_insert($entity_type, $entities[1]);
+//
+//    // Create second object revision, sharing a value with the first one.
+//    $common_value = $values[$cardinality - 1];
+//    $entities[2]->{$this->field_name}[0] = array('value' => $common_value);
+//    field_attach_update($entity_type, $entities[2]);
+//
+//    // Query on the object's values.
+//    for ($delta = 0; $delta < $cardinality; $delta++) {
+//      $conditions = array(array('value', $values[$delta]));
+//      $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
+//      $this->assertTrue(isset($result[$entity_type][1]), t('Query on value %delta returns the object', array('%delta' => $delta)));
+//    }
+//
+//    // Query on a value that is not in the object.
+//    do {
+//      $different_value = mt_rand(1, 127);
+//    } while (in_array($different_value, $values));
+//    $conditions = array(array('value', $different_value));
+//    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
+//    $this->assertFalse(isset($result[$entity_type][1]), t("Query on a value that is not in the object doesn't return the object"));
+//
+//    // Query on the value shared by both objects, and discriminate using
+//    // additional conditions.
+//
+//    $conditions = array(array('value', $common_value));
+//    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
+//    $this->assertTrue(isset($result[$entity_type][1]) && isset($result[$entity_type][2]), t('Query on a value common to both objects returns both objects'));
+//
+//    $conditions = array(array('revision_id', $entities[1]->ftvid), array('value', $common_value));
+//    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
+//    $this->assertTrue(isset($result[$entity_type][1]) && !isset($result[$entity_type][2]), t("Query on a value common to both objects and a 'revision_id' condition only returns the relevant object"));
+//
+//    // Test FIELD_QUERY_RETURN_IDS result format.
+//    $conditions = array(array('value', $values[0]));
+//    $result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_NO_LIMIT);
+//    $expected = array(
+//      $entity_type => array(
+//        $entities[1]->ftid => field_test_create_stub_entity($entities[1]->ftid, $entities[1]->ftvid),
+//      )
+//    );
+//    $this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result'));
+//  }
+//
+//  function testFieldAttachViewAndPreprocess() {
+//    $entity_type = 'test_entity';
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//
+//    // Populate values to be displayed.
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $entity->{$this->field_name} = $values;
+//
+//    // Simple formatter, label displayed.
+//    $formatter_setting = $this->randomName();
+//    $this->instance['display'] = array(
+//      'full' => array(
+//        'label' => 'above',
+//        'type' => 'field_test_default',
+//        'settings' => array(
+//          'test_formatter_setting' => $formatter_setting,
+//        )
+//      ),
+//    );
+//    field_update_instance($this->instance);
+//    $entity->content = field_attach_view($entity_type, $entity);
+//    $output = drupal_render($entity->content);
+//    $this->content = $output;
+//    $this->assertRaw($this->instance['label'], "Label is displayed.");
+//    foreach ($values as $delta => $value) {
+//      $this->content = $output;
+//      $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
+//    }
+//
+//    // Label hidden.
+//    $this->instance['display']['full']['label'] = 'hidden';
+//    field_update_instance($this->instance);
+//    $entity->content = field_attach_view($entity_type, $entity);
+//    $output = drupal_render($entity->content);
+//    $this->content = $output;
+//    $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
+//
+//    // Field hidden.
+//    $this->instance['display'] = array(
+//      'full' => array(
+//        'label' => 'above',
+//        'type' => 'hidden',
+//      ),
+//    );
+//    field_update_instance($this->instance);
+//    $entity->content = field_attach_view($entity_type, $entity);
+//    $output = drupal_render($entity->content);
+//    $this->content = $output;
+//    $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
+//    foreach ($values as $delta => $value) {
+//      $this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
+//    }
+//
+//    // Multiple formatter.
+//    $formatter_setting = $this->randomName();
+//    $this->instance['display'] = array(
+//      'full' => array(
+//        'label' => 'above',
+//        'type' => 'field_test_multiple',
+//        'settings' => array(
+//          'test_formatter_setting_multiple' => $formatter_setting,
+//        )
+//      ),
+//    );
+//    field_update_instance($this->instance);
+//    $entity->content = field_attach_view($entity_type, $entity);
+//    $output = drupal_render($entity->content);
+//    $display = $formatter_setting;
+//    foreach ($values as $delta => $value) {
+//      $display .= "|$delta:{$value['value']}";
+//    }
+//    $this->content = $output;
+//    $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
+//
+//    // TODO:
+//    // - check display order with several fields
+//  }
+//
+//  function testFieldAttachDelete() {
+//    $entity_type = 'test_entity';
+//    $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//
+//    // Create revision 0
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $rev[0]->{$this->field_name} = $values;
+//    field_attach_insert($entity_type, $rev[0]);
+//
+//    // Create revision 1
+//    $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
+//    $rev[1]->{$this->field_name} = $values;
+//    field_attach_update($entity_type, $rev[1]);
+//
+//    // Create revision 2
+//    $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+//    $rev[2]->{$this->field_name} = $values;
+//    field_attach_update($entity_type, $rev[2]);
+//
+//    // Confirm each revision loads
+//    foreach (array_keys($rev) as $vid) {
+//      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+//      field_attach_load_revision($entity_type, array(0 => $read));
+//      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values.");
+//    }
+//
+//    // Delete revision 1, confirm the other two still load.
+//    field_attach_delete_revision($entity_type, $rev[1]);
+//    foreach (array(0, 2) as $vid) {
+//      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+//      field_attach_load_revision($entity_type, array(0 => $read));
+//      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values.");
+//    }
+//
+//    // Confirm the current revision still loads
+//    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $read));
+//    $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test object current revision has {$this->field['cardinality']} values.");
+//
+//    // Delete all field data, confirm nothing loads
+//    field_attach_delete($entity_type, $rev[2]);
+//    foreach (array(0, 1, 2) as $vid) {
+//      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+//      field_attach_load_revision($entity_type, array(0 => $read));
+//      $this->assertIdentical($read->{$this->field_name}, array(), "The test object revision $vid is deleted.");
+//    }
+//    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $read));
+//    $this->assertIdentical($read->{$this->field_name}, array(), t('The test object current revision is deleted.'));
+//  }
+//
+//  function testFieldAttachCreateRenameBundle() {
+//    // Create a new bundle. This has to be initiated by the module so that its
+//    // hook_fieldable_info() is consistent.
+//    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+//    field_test_create_bundle($new_bundle);
+//
+//    // Add an instance to that bundle.
+//    $this->instance['bundle'] = $new_bundle;
+//    field_create_instance($this->instance);
+//
+//    // Save an object with data in the field.
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $entity->{$this->field_name} = $values;
+//    $entity_type = 'test_entity';
+//    field_attach_insert($entity_type, $entity);
+//
+//    // Verify the field data is present on load.
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $entity));
+//    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data are retrieved for the new bundle");
+//
+//    // Rename the bundle. This has to be initiated by the module so that its
+//    // hook_fieldable_info() is consistent.
+//    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+//    field_test_rename_bundle($this->instance['bundle'], $new_bundle);
+//
+//    // Check that the instance definition has been updated.
+//    $this->instance = field_info_instance($this->field_name, $new_bundle);
+//    $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
+//
+//    // Verify the field data is present on load.
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $entity));
+//    $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Bundle name has been updated in the field storage");
+//  }
+//
+//  function testFieldAttachDeleteBundle() {
+//    // Create a new bundle. This has to be initiated by the module so that its
+//    // hook_fieldable_info() is consistent.
+//    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+//    field_test_create_bundle($new_bundle);
+//
+//    // Add an instance to that bundle.
+//    $this->instance['bundle'] = $new_bundle;
+//    field_create_instance($this->instance);
+//
+//    // Create a second field for the test bundle
+//    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+//    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
+//    field_create_field($field);
+//    $instance = array(
+//      'field_name' => $field_name,
+//      'bundle' => $this->instance['bundle'],
+//      'label' => $this->randomName() . '_label',
+//      'description' => $this->randomName() . '_description',
+//      'weight' => mt_rand(0, 127),
+//      // test_field has no instance settings
+//      'widget' => array(
+//        'type' => 'test_field_widget',
+//        'settings' => array(
+//          'size' => mt_rand(0, 255))));
+//    field_create_instance($instance);
+//
+//    // Save an object with data for both fields
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $entity->{$this->field_name} = $values;
+//    $entity->{$field_name} = $this->_generateTestFieldValues(1);
+//    $entity_type = 'test_entity';
+//    field_attach_insert($entity_type, $entity);
+//
+//    // Verify the fields are present on load
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $entity));
+//    $this->assertEqual(count($entity->{$this->field_name}), 4, "First field got loaded");
+//    $this->assertEqual(count($entity->{$field_name}), 1, "Second field got loaded");
+//
+//    // Delete the bundle. This has to be initiated by the module so that its
+//    // hook_fieldable_info() is consistent.
+//    field_test_delete_bundle($this->instance['bundle']);
+//
+//    // Verify no data gets loaded
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//    field_attach_load($entity_type, array(0 => $entity));
+//    $this->assertFalse(isset($entity->{$this->field_name}), "No data for first field");
+//    $this->assertFalse(isset($entity->{$field_name}), "No data for second field");
+//
+//    // Verify that the instances are gone
+//    $this->assertFalse(field_read_instance($this->field_name, $this->instance['bundle']), "First field is deleted");
+//    $this->assertFalse(field_read_instance($field_name, $instance['bundle']), "Second field is deleted");
+//  }
+//
+//  /**
+//   * Test field cache.
+//   */
+//  function testFieldAttachCache() {
+//    // Initialize random values and a test entity.
+//    $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//
+//    $noncached_type = 'test_entity';
+//    $cached_type = 'test_cacheable_entity';
+//
+//
+//    // Non-cacheable entity type.
+//    $cid = "field:$noncached_type:{$entity_init->ftid}";
+//
+//    // Check that no initial cache entry is present.
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
+//
+//    // Save, and check that no cache entry is present.
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = $values;
+//    field_attach_insert($noncached_type, $entity);
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
+//
+//    // Load, and check that no cache entry is present.
+//    $entity = clone($entity_init);
+//    field_attach_load($noncached_type, array($entity->ftid => $entity));
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
+//
+//
+//    // Cacheable entity type.
+//    $cid = "field:$cached_type:{$entity_init->ftid}";
+//
+//    // Check that no initial cache entry is present.
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
+//
+//    // Save, and check that no cache entry is present.
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = $values;
+//    field_attach_insert($cached_type, $entity);
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
+//
+//    // Load, and check that a cache entry is present with the expected values.
+//    $entity = clone($entity_init);
+//    field_attach_load($cached_type, array($entity->ftid => $entity));
+//    $cache = cache_get($cid, 'cache_field');
+//    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
+//
+//    // Update with different values, and check that the cache entry is wiped.
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = $values;
+//    field_attach_update($cached_type, $entity);
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
+//
+//    // Load, and check that a cache entry is present with the expected values.
+//    $entity = clone($entity_init);
+//    field_attach_load($cached_type, array($entity->ftid => $entity));
+//    $cache = cache_get($cid, 'cache_field');
+//    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
+//
+//    // Create a new revision, and check that the cache entry is wiped.
+//    $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
+//    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+//    $entity = clone($entity_init);
+//    $entity->{$this->field_name} = $values;
+//    field_attach_update($cached_type, $entity);
+//    $cache = cache_get($cid, 'cache_field');
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
+//
+//    // Load, and check that a cache entry is present with the expected values.
+//    $entity = clone($entity_init);
+//    field_attach_load($cached_type, array($entity->ftid => $entity));
+//    $cache = cache_get($cid, 'cache_field');
+//    $this->assertEqual($cache->data[$this->field_name], $values, t('Cached: correct cache entry on load'));
+//
+//    // Delete, and check that the cache entry is wiped.
+//    field_attach_delete($cached_type, $entity);
+//    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
+//  }
+//
+//  // Verify that field_attach_validate() invokes the correct
+//  // hook_field_validate.
+//  function testFieldAttachValidate() {
+//    $entity_type = 'test_entity';
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//
+//    // Set up values to generate errors
+//    $values = array();
+//    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+//      $values[$delta]['value'] = -1;
+//      $values[$delta]['_error_element'] = 'field_error_' . $delta;
+//    }
+//    // Arrange for item 1 not to generate an error
+//    $values[1]['value'] = 1;
+//    $entity->{$this->field_name} = $values;
+//
+//    try {
+//      field_attach_validate($entity_type, $entity);
+//    }
+//    catch (FieldValidationException $e) {
+//      $errors = $e->errors;
+//    }
+//
+//    foreach ($values as $delta => $value) {
+//      if ($value['value'] != 1) {
+//        $this->assertIdentical($errors[$this->field_name][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
+//        $this->assertEqual(count($errors[$this->field_name][$delta]), 1, "Only one error set on value $delta");
+//        unset($errors[$this->field_name][$delta]);
+//      }
+//      else {
+//        $this->assertFalse(isset($errors[$this->field_name][$delta]), "No error set on value $delta");
+//      }
+//    }
+//    $this->assertEqual(count($errors[$this->field_name]), 0, 'No extraneous errors set');
+//  }
+//
+//  // Validate that FAPI elements are generated. This could be much
+//  // more thorough, but it does verify that the correct widgets show up.
+//  function testFieldAttachForm() {
+//    $entity_type = 'test_entity';
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//
+//    $form = $form_state = array();
+//    field_attach_form($entity_type, $entity, $form, $form_state);
+//
+//    $this->assertEqual($form[$this->field_name]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
+//    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+//      // field_test_widget uses 'textfield'
+//      $this->assertEqual($form[$this->field_name][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
+//    }
+//  }
+//
+//  function testFieldAttachSubmit() {
+//    $entity_type = 'test_entity';
+//    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+//
+//    // Build the form.
+//    $form = $form_state = array();
+//    field_attach_form($entity_type, $entity, $form, $form_state);
+//
+//    // Simulate incoming values.
+//    $values = array();
+//    $weights = array();
+//    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+//      $values[$delta]['value'] = mt_rand(1, 127);
+//      // Assign random weight.
+//      do {
+//        $weight = mt_rand(0, $this->field['cardinality']);
+//      } while (in_array($weight, $weights));
+//      $weights[$delta] = $weight;
+//      $values[$delta]['_weight'] = $weight;
+//    }
+//    // Leave an empty value. 'field_test' fields are empty if empty().
+//    $values[1]['value'] = 0;
+//
+//    $form_state['values'] = array($this->field_name => $values);
+//    field_attach_submit($entity_type, $entity, $form, $form_state);
+//
+//    asort($weights);
+//    $expected_values = array();
+//    foreach ($weights as $key => $value) {
+//      if ($key != 1) {
+//        $expected_values[] = array('value' => $values[$key]['value']);
+//      }
+//    }
+//    $this->assertIdentical($entity->{$this->field_name}, $expected_values, 'Submit filters empty values');
+//  }
 
   /**
    * Generate random values for a field_test field.
Index: modules/field/field.attach.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v
retrieving revision 1.29
diff -u -r1.29 field.attach.inc
--- modules/field/field.attach.inc	7 Jul 2009 09:28:07 -0000	1.29
+++ modules/field/field.attach.inc	7 Jul 2009 23:05:58 -0000
@@ -25,7 +25,7 @@
   *   An array of field validation errors, keyed by field name and
   *   delta that contains two keys:
   *   - 'error': A machine-readable error code string, prefixed by
-  *     the field module name.  A field widget may use this code to decide
+  *     the field module name. A field widget may use this code to decide
   *     how to report the error.
   *   - 'message': A human-readable error message such as to be
   *     passed to form_error() for the appropriate form element.
@@ -112,11 +112,11 @@
  *
  * field_attach_load(), field_attach_insert(), and
  * field_attach_update() also define pre-operation hooks,
- * e.g. hook_field_attach_pre_load().  These hooks run before the
+ * e.g. hook_field_attach_pre_load(). These hooks run before the
  * corresponding Field Storage API and Field Type API operations.
  * They allow modules to define additional storage locations
  * (e.g. denormalizing, mirroring) for field data on a per-field
- * basis.  They also allow modules to take over field storage
+ * basis. They also allow modules to take over field storage
  * completely by instructing other implementations of the same hook
  * and the Field Storage API itself not to operate on specified
  * fields.
@@ -389,15 +389,24 @@
  *   fields, or FIELD_LOAD_REVISION to load the version indicated by
  *   each object. Defaults to FIELD_LOAD_CURRENT; use
  *   field_attach_load_revision() instead of passing FIELD_LOAD_REVISION.
+ * @param $options
+ *   An associative array of additional options, with the following keys:
+ *  - 'field_name'
+ *    The field name that should be loaded, instead of
+ *    loading all fields, for each object. Note that returned objects
+ *    may contain data for other fields, for example if they are read
+ *    from a cache.
  * @returns
  *   Loaded field values are added to $objects. Fields with no values should be
  *   set as an empty array.
  */
-function field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) {
+function field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT, $options = array()) {
   $load_current = $age == FIELD_LOAD_CURRENT;
 
+  // Only the most current revision of cacheable fieldable types can
+  // be cached, and only if all fields are being loaded.
   $info = field_info_fieldable_types($obj_type);
-  $cacheable = $load_current && $info['cacheable'];
+  $cacheable = $load_current && $info['cacheable'] && !isset($options['field_name']);
 
   if (empty($objects)) {
     return;
@@ -441,21 +450,21 @@
     $skip_fields = array();
     foreach (module_implements('field_attach_pre_load') as $module) {
       $function = $module . '_field_attach_pre_load';
-      $function($obj_type, $queried_objects, $age, $skip_fields);
+      $function($obj_type, $queried_objects, $age, $skip_fields, $options);
     }
 
     // Invoke the storage engine's hook_field_storage_load(): the field storage
     // engine loads the rest.
-    module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields);
+    module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields, $options);
 
     // Invoke field-type module's hook_field_load().
-    _field_invoke_multiple('load', $obj_type, $queried_objects, $age);
+    _field_invoke_multiple('load', $obj_type, $queried_objects, $age, $options);
 
     // Invoke hook_field_attach_load(): let other modules act on loading the
     // object.
     foreach (module_implements('field_attach_load') as $module) {
       $function = $module . '_field_attach_load';
-      $function($obj_type, $queried_objects, $age);
+      $function($obj_type, $queried_objects, $age, $options);
     }
 
     // Build cache data.
@@ -487,12 +496,19 @@
  *   An array of objects for which to load fields, keyed by object id.
  *   Each object needs to have its 'bundle key', 'id key' and 'revision key'
  *   filled.
+ * @param $options
+ *   An associative array of additional options, with the following keys:
+ *  - 'field_name'
+ *    The field name that should be loaded, instead of
+ *    loading all fields, for each object. Note that returned objects
+ *    may contain data for other fields, for example if they are read
+ *    from a cache.
  * @returns
  *   On return, the objects in $objects are modified by having the
  *   appropriate set of fields added.
  */
-function field_attach_load_revision($obj_type, $objects) {
-  return field_attach_load($obj_type, $objects, FIELD_LOAD_REVISION);
+function field_attach_load_revision($obj_type, $objects, $options = array()) {
+  return field_attach_load($obj_type, $objects, FIELD_LOAD_REVISION, $options);
 }
 
 /**
@@ -788,7 +804,9 @@
  * @param &$cursor
  *   An opaque cursor that allows a caller to iterate through multiple
  *   result sets. On the first call, pass 0; the correct value to pass
- *   on the next call will be written into $cursor on return. If NULL,
+ *   on the next call will be written into $cursor on return. When
+ *   there is no more query data available, $cursor will be filled in
+ *   with FIELD_QUERY_COMPLETE. If $cursor is passed as NULL,
  *   the first result set is returned and no next cursor is returned.
  * @param $age
  *   Internal use only. Use field_attach_query_revisions() instead of passing
@@ -824,7 +842,8 @@
   }
   // If the request hasn't been handled, let the storage engine handle it.
   if (!$skip_field) {
-    $results = module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_query', $field_name, $conditions, $count, $cursor, $age);
+    $function = variable_get('field_storage_module', 'field_sql_storage') . '_field_storage_query';
+    $results = $function($field_name, $conditions, $count, $cursor, $age);
   }
 
   return $results;
Index: modules/field/field.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v
retrieving revision 1.17
diff -u -r1.17 field.api.php
--- modules/field/field.api.php	30 Jun 2009 03:12:03 -0000	1.17
+++ modules/field/field.api.php	7 Jul 2009 23:05:58 -0000
@@ -671,7 +671,9 @@
  *   A storage module that doesn't support querying a given column should raise
  *   a FieldQueryException. Incompatibilities should be mentioned on the module
  *   project page.
- * @param $result_format
+ * @param $count
+ *   See field_attach_query().
+ * @param $cursor
  *   See field_attach_query().
  * @param $age
  *   - FIELD_LOAD_CURRENT: query the most recent revisions for all
@@ -685,7 +687,7 @@
  *   The $skip_field parameter should be set to TRUE if the query has been
  *   handled.
  */
-function hook_field_attach_pre_query($field_name, $conditions, $result_format, $age, &$skip_field) {
+function hook_field_attach_pre_query($field_name, $conditions, $count, &$cursor = NULL, $age, &$skip_field) {
 }
 
 /**
@@ -849,14 +851,16 @@
  *   A storage module that doesn't support querying a given column should raise
  *   a FieldQueryException. Incompatibilities should be mentioned on the module
  *   project page.
- * @param $result_format
+ * @param $count
+ *   See field_attach_query().
+ * @param $cursor
  *   See field_attach_query().
  * @param $age
  *   See field_attach_query().
  * @return
  *   See field_attach_query().
  */
-function hook_field_storage_query($field_name, $conditions, $result_format, $age) {
+function hook_field_storage_query($field_name, $conditions, $count, &$cursor = NULL, $age) {
 }
 
 /**
Index: modules/field/field.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.module,v
retrieving revision 1.17
diff -u -r1.17 field.module
--- modules/field/field.module	7 Jul 2009 09:28:07 -0000	1.17
+++ modules/field/field.module	7 Jul 2009 23:05:59 -0000
@@ -100,14 +100,10 @@
 define('FIELD_QUERY_NO_LIMIT', 'FIELD_QUERY_NO_LIMIT');
 
 /**
- * Result format argument for field_attach_query().
+ * Cursor return value for field_attach_query() to indicate that no
+ * more data is available.
  */
-define('FIELD_QUERY_RETURN_VALUES', 'FIELD_QUERY_RETURN_VALUES');
-
-/**
- * Result format argument for field_attach_query().
- */
-define('FIELD_QUERY_RETURN_IDS', 'FIELD_QUERY_RETURN_IDS');
+define('FIELD_QUERY_COMPLETE', 'FIELD_QUERY_COMPLETE');
 
 /**
  * @} End of "Field query flags".
Index: modules/field/modules/field_sql_storage/field_sql_storage.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v
retrieving revision 1.15
diff -u -r1.15 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	7 Jul 2009 09:28:07 -0000	1.15
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	7 Jul 2009 23:06:00 -0000
@@ -202,7 +202,7 @@
 /**
  * Implement hook_field_storage_load().
  */
-function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_fields = array()) {
+function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_fields, $options) {
   $etid = _field_sql_storage_etid($obj_type);
   $load_current = $age == FIELD_LOAD_CURRENT;
 
@@ -212,7 +212,7 @@
   foreach ($objects as $obj) {
     list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $obj);
     foreach (field_info_instances($bundle) as $field_name => $instance) {
-      if (!isset($skip_fields[$field_name])) {
+      if (!isset($skip_fields[$field_name]) && (!isset($options['field_name']) || $options['field_name'] == $instance['field_name'])) {
         $objects[$id]->{$field_name} = array();
         $field_ids[$field_name][] = $load_current ? $id : $vid;
         $delta_count[$id][$field_name] = 0;
@@ -224,13 +224,14 @@
     $field = field_info_field($field_name);
     $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
 
-    $results = db_select($table, 't')
+    $query = db_select($table, 't')
       ->fields('t')
       ->condition('etid', $etid)
       ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
       ->condition('deleted', 0)
-      ->orderBy('delta')
-      ->execute();
+      ->orderBy('delta');
+
+    $results = $query->execute();
 
     foreach ($results as $row) {
       if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$field_name] < $field['cardinality']) {
@@ -404,35 +405,40 @@
   // Initialize results array
   $return = array();
 
-  // Query for batches of rows until we've read $count objects or
-  // until we get no new rows.
-  $limit = $count;
+  // TODO
+  // Getting $count objects possibly requires reading more than $count rows
+  // since fields with multiple values span over several rows. We query for
+  // batches of $count rows until we've either read $count objects or receive
+  // less rows than asked for.
+  $obj_count = 0;
   do {
-    if ($limit != FIELD_QUERY_NO_LIMIT) {
-      $query->range($cursor, $limit);
+    if ($count != FIELD_QUERY_NO_LIMIT) {
+      $query->range($cursor, $count);
     }
     $results = $query->execute();
-    
-    $found = FALSE;
+
+    $row_count = 0;
     foreach ($results as $row) {
-      $found = TRUE;
-      ++$cursor;
-      
-      // If querying all revisions and the entity type has revisions, we need to
-      // key the results by revision_ids.
+      $row_count++;
+      $cursor++;
+
+      // If querying all revisions and the entity type has revisions, we need
+      // to key the results by revision_ids.
       $entity_type = field_info_fieldable_types($row->type);
       $id = ($load_current || empty($entity_type['revision key'])) ? $row->entity_id : $row->revision_id;
 
-      // We get multiple rows if the field has multiple deltas.  Only
-      // count the first one.
-      if (isset($return[$row->type][$id])) {
-        continue;
+      if (!isset($return[$row->type][$id])) {
+        $return[$row->type][$id] = field_attach_create_stub_object($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
+        $obj_count++;
       }
-      
-      $return[$row->type][$id] = field_attach_create_stub_object($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
-      --$count;
     }
-  } while ($found && ($limit != FIELD_QUERY_NO_LIMIT || $count > 0));
+  } while ($count != FIELD_QUERY_NO_LIMIT && $row_count == $count && $obj_count < $count);
+
+  // The query is complete when the last batch returns less rows than asked
+  // for.
+  if ($row_count < $count) {
+    $cursor = FIELD_QUERY_COMPLETE;
+  }
 
   return $return;
 }
