diff -urp --strip-trailing-cr ../category/category_display/category_display.module ./category_display/category_display.module --- ../category/category_display/category_display.module 2009-01-03 12:47:00.000000000 +0100 +++ ./category_display/category_display.module 2009-06-24 03:22:03.000000000 +0200 @@ -283,7 +283,7 @@ function category_display_toc_recurse($p $cat = category_get_container($is_cat ? $category->cnid : $category->cid); unset($cat->cid); unset($cat->title); - foreach ($cat as $key => $value) { + foreach ((array)$cat as $key => $value) { $category->$key = $value; } diff -urp --strip-trailing-cr ../category/category.inc ./category.inc --- ../category/category.inc 2009-01-03 12:47:00.000000000 +0100 +++ ./category.inc 2009-06-24 00:15:07.000000000 +0200 @@ -26,9 +26,12 @@ function category_get_category($cid, $re if (!is_numeric($cid)) { return FALSE; } - $query = 'SELECT c.*, n.title FROM {category} c INNER JOIN {node} n ON c.cid = n.nid'; - - return category_get_cached_item('get_category', $cid, 'cid', $query, 'c.cid = %d', FALSE, $reset); + $category = category_cache_op('get', $cid, 'cgcat'); + if (!isset($category)) { + $category = db_fetch_object(db_query('SELECT c.*, n.title FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE c.cid = %d', $cid)); + category_cache_op('set', $cid, 'cgcat', $category); + } + return $category; } /** @@ -57,9 +60,9 @@ function category_get_category_by_name($ * Return the container object matching a container ID. */ function category_get_container($cnid) { - static $containers = array(); + $cont = category_cache_op('get', $cnid, 'cgcont'); + if (!isset($cont)) { - if (!array_key_exists($cnid, $containers)) { $result = db_query('SELECT cn.*, n.title, ct.type, cd.allowed_parent, c.description AS cont_description, c.weight AS cont_weight, c.depth AS cont_depth FROM {category} c INNER JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont_node_types} ct ON c.cid = ct.cid LEFT JOIN {category_cont_distant} cd ON c.cid = cd.cid WHERE c.cid = %d ORDER BY c.weight, n.title', $cnid); $node_types = array(); $node_distant = array(); @@ -85,11 +88,11 @@ function category_get_container($cnid) { else { $cont->has_admin_title = TRUE; } - $containers[$cnid] = $cont; } + category_cache_op('set', $cnid, 'cgcont', $cont); } - return (isset($containers[$cnid]) ? $containers[$cnid] : NULL); + return (isset($cont) ? $cont : NULL); } /** @@ -147,9 +150,15 @@ function category_is_cat_or_cont($nid, $ return FALSE; } - $query = 'SELECT n.nid FROM {node} n INNER JOIN {category} c ON n.nid = c.cid'; - $return = category_get_cached_item('is_cat_or_cont', $nid, 'nid', $query, 'n.nid = %d', FALSE, $reset); - return !empty($return->nid); + $is_coc = category_cache_op('get', $nid, 'cicoc'); + if (!isset($is_coc)) { + $object = db_fetch_object(db_query('SELECT n.nid FROM {node} n INNER JOIN {category} c ON n.nid = c.cid WHERE n.nid = %d', $nid)); + $is_coc = empty($object->nid) ? '0' : '1'; + + category_cache_op('set', $nid, 'cicoc', $is_coc); + } + + return !empty($is_coc); } /** @@ -193,35 +202,28 @@ function category_node_get_type($node, $ * This function will return the parents in an indexed array sorted by the parents' weight and title. */ function category_get_parents($cid, $key = 'cid', $distant = TRUE, $reset = FALSE) { - static $parents, $distant_parents; if (!$cid) { return array(); } - if (($distant && !isset($distant_parents)) || (!$distant && !isset($parents)) || $reset) { + $cache_key = $distant ? 'cgpdist' : 'cgparent'; + $parents = category_cache_op('get', 0, $cache_key); + if (!isset($parents)) { + $distant_sql = $distant ? '' : ' AND (c.cnid = cc.cnid OR c.cnid = 0)'; $result = db_query(db_rewrite_sql('SELECT c.*, h.cid AS child, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.parent INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {category} cc ON h.cid = cc.cid WHERE n.status = 1'. $distant_sql .' ORDER BY c.weight, n.title'), $cid); - if ($distant) { - $distant_parents = array(); - } - else { - $parents = array(); - } + $parents = array(); while ($parent = db_fetch_object($result)) { if (empty($parent->admin_title)) { $parent->admin_title = $parent->title; } - if ($distant) { - $distant_parents[$parent->child][] = $parent; - } - else { - $parents[$parent->child][] = $parent; - } + $parents[$parent->child][] = $parent; } + category_cache_op('set', 0, $cache_key, $parents); } - return $distant ? (isset($distant_parents[$cid]) ? $distant_parents[$cid] : array()) : (isset($parents[$cid]) ? $parents[$cid] : array()); + return (isset($parents[$cid]) ? $parents[$cid] : array()); } /** @@ -244,7 +246,6 @@ function category_get_parents_all($cid, * Find all children of a category ID. */ function category_get_children($cid, $cnid = 0, $key = 'cid', $reset = FALSE) { - static $children; if ($cnid) { $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE h.parent = %d AND n.status = 1 AND c.cnid = %d ORDER BY c.weight, n.title'), $cid, $cnid); @@ -256,13 +257,17 @@ function category_get_children($cid, $cn return $results; } - elseif (!isset($children) || $reset) { + + $children = category_cache_op('get', 0, 'cgch'. $key); + if (!isset($children)) { + $result = db_query(db_rewrite_sql('SELECT c.*, h.parent, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE n.status = 1 ORDER BY c.weight, n.title'), $cid); $children = array(); while ($category = db_fetch_object($result)) { $children[$category->parent][$category->$key] = $category; } + category_cache_op('set', 0, 'cgch'. $key, $children); } return isset($children[$cid]) ? $children[$cid] : array(); @@ -377,7 +382,6 @@ function category_location_down($node, $ * to have "depth" and "parents" attributes in addition to its normal ones. */ function category_get_tree($cnid, $parent = NULL, $depth = -1, $max_depth = NULL, $distant = FALSE) { - static $children, $parents, $categories; $tree = array(); if (empty($cnid) && $depth == -1) { @@ -391,10 +395,17 @@ function category_get_tree($cnid, $paren // We cache trees, so it's not CPU-intensive to call get_tree() on a category // and its children, too. - if (!isset($children[$cnid])) { + $cache_key = $distant ? 'cgtdist' : 'cgtree'; + $children = category_cache_op('get', $cnid, $cache_key .'ch'); + if (isset($children)) { + $parents = category_cache_op('get', $cnid, $cache_key .'pa'); + $categories = category_cache_op('get', $cnid, $cache_key .'ca'); + } + else { + $distant_sql = ($distant) ? '' : 'AND c.cnid = %d '; - $children[$cnid] = array(); + $children = $parents = $categories = array(); $result = db_query(db_rewrite_sql('SELECT c.cid, c.*, h.parent, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid LEFT JOIN {category} c2 ON h.parent = c2.cid WHERE (c2.cid IS NOT NULL OR h.parent = 0) AND n.status = 1 '. $distant_sql .'ORDER BY c.weight, n.title', 'n', 'nid'), $cnid); while ($category = db_fetch_object($result)) { @@ -405,28 +416,31 @@ function category_get_tree($cnid, $paren } } - $children[$cnid][$category->parent][] = $category->cid; - $categories[$cnid][$category->cid] = $category; - $parents[$cnid][$category->cid][] = $category->parent; + $children[$category->parent][] = $category->cid; + $categories[$category->cid] = $category; + $parents[$category->cid][] = $category->parent; } + category_cache_op('set', $cnid, $cache_key .'ch', $children); + category_cache_op('set', $cnid, $cache_key .'pa', $parents); + category_cache_op('set', $cnid, $cache_key .'ca', $categories); } - $max_depth = (is_null($max_depth)) ? count($children[$cnid]) : $max_depth; - if ($children[$cnid][$parent]) { - foreach ($children[$cnid][$parent] as $child) { + $max_depth = (is_null($max_depth)) ? count($children) : $max_depth; + if ($children[$parent]) { + foreach ($children[$parent] as $child) { if ($max_depth > $depth) { - $cat = drupal_clone($categories[$cnid][$child]); + $cat = drupal_clone($categories[$child]); $cat->depth = $depth; // The "parent" attribute is not useful, as it would show one parent only. unset($cat->parent); - $cat->parents = $parents[$cnid][$child]; + $cat->parents = $parents[$child]; if (!$cat->cnid && empty($cat->admin_title)) { $cat->admin_title = $cat->title; } $tree[] = $cat; - if ($children[$cnid][$child]) { + if ($children[$child]) { $tree = array_merge($tree, category_get_tree($cnid, $child, $depth, $max_depth, $distant)); } } @@ -593,12 +607,16 @@ function category_node_type($op, $info) */ function category_get_related($cid, $key = 'cid') { if ($cid) { + $related = category_cache_op('get', $cid, 'cgrel'. $key); + if (!isset($related)) { $result = db_query(db_rewrite_sql('SELECT c.*, n.title, cid1, cid2 FROM {category_relation}, {category} c, {node} n WHERE (c.cid = cid1 OR c.cid = cid2) AND (cid1 = %d OR cid2 = %d) AND c.cid = n.nid AND c.cid != %d AND n.status = 1 ORDER BY c.weight, n.title'), $cid, $cid, $cid); $related = array(); while ($category = db_fetch_object($result)) { $related[$category->$key] = $category; } - return $related; + category_cache_op('set', $cid, 'cgrel'. $key, $related); + } + return $related; } else { return array(); @@ -610,11 +628,15 @@ function category_get_related($cid, $key */ function category_get_synonyms($cid) { if ($cid) { + $synonyms = category_cache_op('get', $cid, 'cgsyn'); + if (!isset($synonyms)) { $result = db_query('SELECT name FROM {category_synonym} WHERE cid = %d', $cid); while ($synonym = db_fetch_array($result)) { $synonyms[] = $synonym['name']; } - return $synonyms ? $synonyms : array(); + category_cache_op('set', $cid, 'cgsyn', $synonyms); + } + return $synonyms ? $synonyms : array(); } else { return array(); @@ -803,9 +825,9 @@ function category_node_save(&$node, $is_ * Given a category id, count the number of published nodes in it. */ function category_category_count_nodes($cid, $type = 0) { - static $count; - if (!isset($count[$type])) { + $count = category_cache_op('get', 0, 'cccn'. $type); + if (!isset($count)) { // $type == 0 always evaluates true if $type is a string if (is_numeric($type)) { $result = db_query(db_rewrite_sql('SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 GROUP BY c.cid')); @@ -814,42 +836,51 @@ function category_category_count_nodes($ $result = db_query(db_rewrite_sql("SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY c.cid"), $type); } while ($category = db_fetch_object($result)) { - $count[$type][$category->cid] = $category->cnt; + $count[$category->cid] = $category->cnt; } + category_cache_op('set', 0, 'cccn'. $type, $count); } - return isset($count[$type][$cid]) ? $count[$type][$cid] : 0; + return isset($count[$cid]) ? $count[$cid] : 0; } /** * Find all categories associated to the given node, ordered by container and category weight. */ function category_node_get_categories($nid, $key = 'cid', $reset = FALSE) { - static $categories = array(); - if (!isset($categories[$nid]) || $reset) { - $categories[$nid] = array(); + $categories = category_cache_op('get', $nid, 'ngcat'. $key); + if (!isset($categories)) { + + $categories = array(); $result = db_query(db_rewrite_sql('SELECT n.nid, r.nid AS node_id, c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {category} cn ON c.cnid = cn.cid INNER JOIN {node} n ON c.cid = n.nid INNER JOIN {node} cnn ON cn.cid = cnn.nid WHERE n.status = 1 AND r.nid = %d ORDER BY cn.weight, cnn.title, c.weight, n.title'), $nid); while ($category = db_fetch_object($result)) { - $categories[$nid][$category->$key] = $category; + $categories[$category->$key] = $category; } + category_cache_op('set', $nid, 'ngcat'. $key, $categories); } - return $categories[$nid]; + return $categories; } /** * Find all categories associated to the given node, within one container. */ function category_node_get_categories_by_container($nid, $cnid, $key = 'cid') { + + $categories = category_cache_op('get', $nid, 'ngcbc'. $key . $cnid); + if (!isset($categories)) { + $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {node} n ON c.cid = n.nid WHERE c.cnid = %d AND r.nid = %d AND n.status = 1 ORDER BY c.weight'), $cnid, $nid); $categories = array(); while ($category = db_fetch_object($result)) { $categories[$category->$key] = $category; } - return $categories; + category_cache_op('set', $nid, 'ngcbc'. $key . $cnid, $categories); + } + return $categories; } /** @@ -861,6 +892,7 @@ function category_node_delete($nid, $is_ if (!$is_legacy || !isset($legacy_calls[$nid])) { db_query('DELETE FROM {category_node} WHERE nid = %d', $nid); + category_cache_op('flush', $nid); $legacy_calls[$nid] = TRUE; } } @@ -1424,14 +1456,19 @@ function category_get_wrapper_status($ty * containers are found. */ function category_get_distant_child_containers($cnid) { + + $containers = category_cache_op('get', $cnid, 'cgdcc'); + if (!isset($containers)) { + $containers = array(); $result = db_query('SELECT cid FROM {category_cont_distant} WHERE allowed_parent = \'%s\'', $cnid); while ($cont = db_fetch_object($result)) { $containers[$cont->cid] = $cont->cid; } - - return $containers; + category_cache_op('set', $cnid, 'cgdcc', $containers); + } + return $containers; } /** @@ -1509,8 +1546,12 @@ function category_get_cached_item($type, * does not exist or is not part of a category hierarchy. */ function _category_get_depth($cid) { - $depth = 0; if ($cid) { + + $depth = category_cache_op('get', $cid, 'cgdpth'); + if (!isset($depth)) { + + $depth = 0; while ($cid) { $result = db_query(db_rewrite_sql('SELECT h.parent FROM {category_hierarchy} h INNER JOIN {category} c ON h.cid = c.cid INNER JOIN {node} n ON h.cid = n.nid WHERE h.cid = %d ORDER BY c.weight, n.title'), $cid); $obj = db_fetch_object($result); @@ -1523,7 +1564,10 @@ function _category_get_depth($cid) { } $depth++; } - return $depth; + + category_cache_op('set', $cid, 'cgdpth', $depth); + } + return $depth; } else { return 0; @@ -1534,13 +1578,14 @@ function _category_get_depth($cid) { * Helper for category_category_count_nodes(). */ function _category_category_children($cid) { - static $children; + $children = category_cache_op('get', 0, 'cgchld'); if (!isset($children)) { $result = db_query('SELECT cid, parent FROM {category_hierarchy}'); while ($category = db_fetch_object($result)) { $children[$category->parent][] = $category->cid; } + category_cache_op('set', 0, 'cgchld', $children); } return $children[$cid] ? $children[$cid] : array(); } @@ -1551,3 +1596,70 @@ function _category_category_children($ci function _category_get_cid_from_category($category) { return $category->cid; } + +/** + * Central caching function for all kinds of node-related category data, + * so that all data for a given node may be loaded by single query from + * cache table into static variable, and then returned by various API + * functions without any queries needed. The cache is stored per-node, + * and flushed only when given node is saved or deleted. The key '0' + * is used for global summary-data, and flushed on every node save. + * While saving a category or container, we flush the whole cache, as + * this affects the whole hiearchy, and happens rarely. + * + * @param op + * Operation to be performed - either 'get', 'set', or 'flush' + * + * @param nid + * Node id to handle data for. 0 is general data, 'all' flushes all + * + * @param key + * Name of data subset (usually which API function use it) for the given node + * + * @param data + * Data for the 'set' operation (will get added to the existing per-node record) + */ +function category_cache_op($op, $nid, $key = '', $data = NULL) { + + static $cache; + if (!isset($cache)) { + $cache = array(); + } + + // Flush cache if requested + if ($op == 'flush') { + if ($nid === 'all') { + // Total flush: Empty {cache_category} entirely + cache_clear_all('*', 'cache_category', TRUE); + $cache = array(); + } + else { + // Node flush: Delete entries for given node, and for global data + cache_clear_all('c'. $nid, 'cache_category'); + unset($cache[$nid]); + cache_clear_all('c0', 'cache_category'); + unset($cache[0]); + } + return; + } + + // Load existing record for given node, if not available + if (!isset($cache[$nid])) { + $entry = cache_get('c'. $nid, 'cache_category'); + if ($entry) { + $cache[$nid] = unserialize($entry->data); + } + else { + $cache[$nid] = array(); + } + } + + // Save new data if required + if ($op == 'set') { + $cache[$nid][$key] = $data; + cache_set('c'. $nid, 'cache_category', serialize($cache[$nid])); + } + + // Return data for given node/key combination + return isset($cache[$nid][$key]) ? $cache[$nid][$key] : NULL; +} diff -urp --strip-trailing-cr ../category/category.module ./category.module --- ../category/category.module 2009-01-03 12:47:00.000000000 +0100 +++ ./category.module 2009-06-23 21:34:56.000000000 +0200 @@ -536,6 +536,7 @@ function category_update($node, $type = } } } + category_cache_op('flush', 'all'); } /** @@ -559,6 +560,7 @@ function category_delete(&$node, $type = db_query('DELETE FROM {category_relation} WHERE cid1 = %d OR cid2 = %d', $node->nid, $node->nid); db_query('DELETE FROM {category_synonym} WHERE cid = %d', $node->nid); } + category_cache_op('flush', 'all'); } /** diff -urp --strip-trailing-cr ../category/contrib/category_views/category_views.module ./contrib/category_views/category_views.module --- ../category/contrib/category_views/category_views.module 2009-01-03 12:48:00.000000000 +0100 +++ ./contrib/category_views/category_views.module 2009-06-23 23:03:19.000000000 +0200 @@ -143,6 +143,8 @@ function category_views_category_legacy( * The views settings for the specified node. */ function category_views_cont_load($nid) { + $container = category_cache_op('get', $nid, 'cvcld'); + if (!isset($container)) { $container = new stdClass(); $sql = db_query("SELECT * FROM {category_views} WHERE cid = %d", $nid); if ($result = db_fetch_object($sql)) { @@ -150,8 +152,9 @@ function category_views_cont_load($nid) if ($result->display_cont) $container->view_display['cont'] = 'cont'; if ($result->display_cat) $container->view_display['cat'] = 'cat'; } - - return $container; + category_cache_op('set', $nid, 'cvcld', $container); + } + return $container; } /**