### Eclipse Workspace Patch 1.0 #P drupal_test_7 Index: modules/simpletest/tests/cache.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/cache.test,v retrieving revision 1.5 diff -u -r1.5 cache.test --- modules/simpletest/tests/cache.test 29 Apr 2009 12:08:28 -0000 1.5 +++ modules/simpletest/tests/cache.test 17 May 2009 14:16:33 -0000 @@ -162,6 +162,56 @@ } } +/** + * Test cache_get_multiple(). + */ +class CacheGetMultipleUnitTest extends CacheTestCase { + + public static function getInfo() { + return array( + 'name' => t('Fetching multiple cache items'), + 'description' => t('Confirm that multiple records are fetched correctly.'), + 'group' => t('Cache'), + ); + } + + function setUp() { + $this->default_table = 'cache_page'; + parent::setUp(); + } + + /** + * Test cache_get_multiple(). + */ + function testCacheMultiple() { + $item1 = $this->randomName(10); + $item2 = $this->randomName(10); + cache_set('item1', $item1, $this->default_table); + cache_set('item2', $item2, $this->default_table); + $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.')); + $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.')); + + // Fetch both records from the database with cache_get_multiple(). + $item_ids = array('item1', 'item2'); + $items = cache_get_multiple($item_ids, $this->default_table); + $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); + $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.')); + + // Remove one item from the cache. + cache_clear_all('item2', $this->default_table); + + // Confirm that only one item is returned by cache_get_multiple(). + $item_ids = array('item1', 'item2'); + $items = cache_get_multiple($item_ids, $this->default_table); + $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); + $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.')); + $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.')); + } +} + +/** + * Test cache clearing methods. + */ class CacheClearCase extends CacheTestCase { public static function getInfo() { return array( @@ -224,4 +274,41 @@ || $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two caches removed after clearing cid substring with wildcard true.')); } -} \ No newline at end of file + + /** + * Test clearing using an array. + */ + function testClearArray() { + // Create three cache entries. + cache_set('test_cid_clear1', $this->default_value, $this->default_table); + cache_set('test_cid_clear2', $this->default_value, $this->default_table); + cache_set('test_cid_clear3', $this->default_value, $this->default_table); + $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) + && $this->checkCacheExists('test_cid_clear2', $this->default_value) + && $this->checkCacheExists('test_cid_clear3', $this->default_value), + t('Three cache entries were created.')); + + // Clear two entries using an array. + cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_table); + $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) + || $this->checkCacheExists('test_cid_clear2', $this->default_value), + t('Two cache entries removed after clearing with an array.')); + + $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value), + t('Entry was not cleared from the cache')); + + // Set the cache clear threshold to 2 to confirm that the full table is cleared + // when the threshold is exceeded. + variable_set('cache_clear_threshold', 2); + cache_set('test_cid_clear1', $this->default_value, $this->default_table); + cache_set('test_cid_clear2', $this->default_value, $this->default_table); + $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) + && $this->checkCacheExists('test_cid_clear2', $this->default_value), + t('Two cache entries were created.')); + cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_table); + $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) + || $this->checkCacheExists('test_cid_clear2', $this->default_value) + || $this->checkCacheExists('test_cid_clear3', $this->default_value), + t('All cache entries removed when the array exceeded the cache clear threshold.')); + } +} Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.17 diff -u -r1.17 field.attach.inc --- modules/field/field.attach.inc 17 May 2009 03:12:17 -0000 1.17 +++ modules/field/field.attach.inc 17 May 2009 14:16:33 -0000 @@ -337,28 +337,36 @@ $info = field_info_fieldable_types($obj_type); $cacheable = $load_current && $info['cacheable']; - $queried_objects = array(); + if (empty($objects)) { + return; + } + + // Assume all objects will need to be queried. Objects found in the cache + // will be removed from the list. + $queried_objects = $objects; - // Fetch avaliable objects from cache. + // Fetch available objects from cache, if applicable. if ($cacheable) { + // Build the list of cache entries to retrieve. + $cids = array(); + foreach ($objects as $id => $object) { + $cids[] = "field:$obj_type:$id"; + } + $cache = cache_get_multiple($cids, 'cache_field'); + // Put the cached field values back into the objects and remove them from + // the list of objects to query. foreach ($objects as $id => $object) { $cid = "field:$obj_type:$id"; - if ($cached = cache_get($cid, 'cache_field')) { - foreach ($cached->data as $key => $value) { - $object->$key = $value; + if (isset($cache[$cid])) { + unset($queried_objects[$id]); + foreach ($cache[$cid]->data as $field_name => $values) { + $object->$field_name = $values; } } - else { - $queried_objects[$id] = $objects[$id]; - } } } - else { - $queried_objects = $objects; - } - - // Fetch other objects from the database. + // Fetch other objects from their storage location. if ($queried_objects) { // The invoke order is: // - hook_field_attach_pre_load() Index: includes/cache.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/cache.inc,v retrieving revision 1.32 diff -u -r1.32 cache.inc --- includes/cache.inc 16 May 2009 16:06:04 -0000 1.32 +++ includes/cache.inc 17 May 2009 14:16:32 -0000 @@ -9,12 +9,54 @@ * @param $cid * The cache ID of the data to retrieve. * @param $table - * The table $table to store the data in. Valid core values are - * 'cache_filter', 'cache_menu', 'cache_page', or 'cache' for - * the default cache. - * @return The cache or FALSE on failure. + * The cache table where the data is stored. + * @return + * The cache or FALSE on failure. */ function cache_get($cid, $table = 'cache') { + // Garbage collection necessary when enforcing a minimum cache lifetime. + _cache_garbage_collection($table); + $cache = db_query("SELECT data, created, headers, expire, serialized FROM {" . $table . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject(); + + return _cache_prepare_item($cache); +} + +/** + * Return data from the persistent cache when given an array of cache IDs. + * + * @param $cids + * An array of cache IDs for the data to retrieve. This is passed by + * reference, and will have the IDs successfully returned from cache removed. + * @param $table + * The cache table where the data is stored. + * @return + * An array of the items successfully returned from cache indexed by cid. + */ +function cache_get_multiple(array &$cids, $table = 'cache') { + // Garbage collection necessary when enforcing a minimum cache lifetime. + _cache_garbage_collection($table); + $query = db_select($table); + $query->fields($table, array('cid', 'data', 'created', 'headers', 'expire', 'serialized')); + $query->condition($table . '.cid', $cids, 'IN'); + $result = $query->execute(); + $cache = array(); + foreach ($result as $item) { + $item = _cache_prepare_item($item); + if ($item) { + $cache[$item->cid] = $item; + } + } + $cids = array_keys(array_diff_key(array_flip($cids), $cache)); + return $cache; +} + +/** + * Garbage collection for cache_get() and cache_get_multiple(). + * + * @param $table + * The table being requested. + */ +function _cache_garbage_collection($table) { global $user; // Garbage collection necessary when enforcing a minimum cache lifetime @@ -28,13 +70,31 @@ ->condition('expire', $cache_flush, '<=') ->execute(); } +} - $cache = db_query("SELECT data, created, headers, expire, serialized FROM {" . $table . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject(); - +/** + * Prepare a cached item. + * + * Checks that items are either permanent or did not expire, and unserializes + * data as appropriate. + * + * @param $cache + * An item loaded from cache_get() or cache_get_multiple(). + * @return + * The item with data unserialized as appropriate or FALSE if there is no + * valid item to load. + */ +function _cache_prepare_item($cache) { if (!isset($cache->data)) { return FALSE; } - + // If the data is permanent or we are not enforcing a minimum cache lifetime + // always return the cached data. + if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) { + if ($cache->serialized) { + $cache->data = unserialize($cache->data); + } + } // If enforcing a minimum cache lifetime, validate that the data is // currently valid for this user before we return it by making sure the cache // entry was created before the timestamp in the current session's cache @@ -46,9 +106,6 @@ return FALSE; } - if ($cache->serialized) { - $cache->data = unserialize($cache->data); - } if (isset($cache->headers)) { $cache->headers = unserialize($cache->headers); } @@ -129,8 +186,8 @@ * entries will be cleared from the cache_page and cache_block tables. * * @param $cid - * If set, the cache ID to delete. Otherwise, all cache entries that can - * expire are deleted. + * If set, the cache ID to delete, or an array of cache IDs to delete. + * Otherwise, all cache entries that can expire are deleted. * * @param $table * If set, the table $table to delete from. Mandatory @@ -196,6 +253,15 @@ ->execute(); } } + elseif (is_array($cid)) { + // Delete in chunks when a large array is passed. + do { + db_delete($table) + ->condition('cid', array_splice($cid, 0, 1000), 'IN') + ->execute(); + } + while (count($cid)); + } else { db_delete($table) ->condition('cid', $cid)