Big performance improvment by:

- using egw_link::set_cache() for entries read or searched
--> eliminates an excessive number of single entry reads, when the links
need titles or file_access permissions
- removing sub-query for number of sub-entries from search (Bug #1613)
- method to query the number of sub-entries of all displayed entries in
  one go
This commit is contained in:
Ralf Becker 2008-10-19 11:34:12 +00:00
parent 25b3c3a1f8
commit 876649fe6f
3 changed files with 57 additions and 41 deletions

View File

@ -415,6 +415,9 @@ class infolog_bo
{ {
if ($data[$time]) $data[$time] += $this->tz_offset_s; if ($data[$time]) $data[$time] += $this->tz_offset_s;
} }
// pre-cache title and file access
self::set_link_cache($data);
return $data; return $data;
} }
@ -654,6 +657,9 @@ class infolog_bo
// notify the link-class about the update, as other apps may be subscribt to it // notify the link-class about the update, as other apps may be subscribt to it
egw_link::notify_update('infolog',$info_id,$values); egw_link::notify_update('infolog',$info_id,$values);
// pre-cache the new values
self::set_link_cache($values);
// send email notifications and do the history logging // send email notifications and do the history logging
if (!is_object($this->tracking)) if (!is_object($this->tracking))
{ {
@ -667,10 +673,10 @@ class infolog_bo
} }
/** /**
* Query the number of children / subs * Query the number of children / subs for one or more info_id's
* *
* @param int $info_id id * @param int|array $info_id id
* @return int number of subs * @return int|array number of subs
*/ */
function anzSubs( $info_id ) function anzSubs( $info_id )
{ {
@ -696,21 +702,25 @@ class infolog_bo
$ret = $this->so->search($query); $ret = $this->so->search($query);
// convert system- to user-time // convert system- to user-time
if (is_array($ret) && $this->tz_offset_s) if (is_array($ret))
{ {
foreach($ret as $id => $data) foreach($ret as $id => &$data)
{ {
foreach($this->timestamps as $time) if($this->tz_offset_s)
{ {
if ($data[$time]) $ret[$id][$time] += $this->tz_offset_s; foreach($this->timestamps as $time)
{
if ($data[$time]) $data[$time] += $this->tz_offset_s;
}
} }
// pre-cache title and file access
self::set_link_cache($data);
} }
} }
//echo "<p>boinfolog::search(".print_r($query,True).")=<pre>".print_r($ret,True)."</pre>\n"; //echo "<p>boinfolog::search(".print_r($query,True).")=<pre>".print_r($ret,True)."</pre>\n";
return $ret; return $ret;
} }
/** /**
* imports a mail identified by uid as infolog * imports a mail identified by uid as infolog
* *
@ -868,15 +878,28 @@ class infolog_bo
/** /**
* Check access to the projects file store * Check access to the projects file store
* *
* @param int $id id of entry * @param int|array $id id of entry or entry array
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access * @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @return boolean true if access is granted or false otherwise * @return boolean true if access is granted or false otherwise
*/ */
function file_access($id,$check,$rel_path) function file_access($id,$check,$rel_path=null)
{ {
return $this->check_access($id,$check); return $this->check_access($id,$check);
} }
/**
* Set the cache of the link class (title, file_access) for the given infolog entry
*
* @param array $info
*/
function set_link_cache(array $info)
{
egw_link::set_cache('infolog',$info['info_id'],
$this->link_title($info),
$this->file_access($info,EGW_ACL_EDIT) ? EGW_ACL_READ|EGW_ACL_EDIT :
($this->file_access($info,EGW_ACL_READ) ? EGW_ACL_READ : 0));
}
/** /**
* hook called be calendar to include events or todos in the cal-dayview * hook called be calendar to include events or todos in the cal-dayview
* *

View File

@ -15,13 +15,8 @@
* *
* all values passed to this class are run either through intval or addslashes to prevent query-insertion * all values passed to this class are run either through intval or addslashes to prevent query-insertion
* and for pgSql 7.3 compatibility * and for pgSql 7.3 compatibility
*
* @package infolog
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) by Ralf Becker <RalfBecker@outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/ */
class infolog_so // DB-Layer class infolog_so
{ {
/** /**
* Instance of the db class * Instance of the db class
@ -213,9 +208,10 @@ class infolog_so // DB-Layer
} }
if (count($private_user_list)) if (count($private_user_list))
{ {
$has_private_access = 'info_owner IN ('.implode(',',$private_user_list).')'; $has_private_access = $this->db->expression($this->info_table,array('info_owner' => $private_user_list));
} }
} }
$public_access = $this->db->expression($this->info_table,array('info_owner' => $public_user_list));
// implicit read-rights for responsible user // implicit read-rights for responsible user
$filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')"; $filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')";
@ -224,7 +220,7 @@ class infolog_so // DB-Layer
{ {
$filtermethod .= " OR (".$this->responsible_filter($this->user). $filtermethod .= " OR (".$this->responsible_filter($this->user).
($filter == 'own' && count($public_user_list) ? // offer's should show up in own, eg. startpage, but need read-access ($filter == 'own' && count($public_user_list) ? // offer's should show up in own, eg. startpage, but need read-access
" OR info_status = 'offer' AND info_owner IN(" . implode(',',$public_user_list) . ')' : '').")". " OR info_status = 'offer' AND $public_access" : '').")".
" AND (info_access='public'".($has_private_access?" OR $has_private_access":'').')'; " AND (info_access='public'".($has_private_access?" OR $has_private_access":'').')';
} }
elseif ($filter != 'my' && $filter != 'responsible') // none --> all entrys user has rights to see elseif ($filter != 'my' && $filter != 'responsible') // none --> all entrys user has rights to see
@ -235,7 +231,7 @@ class infolog_so // DB-Layer
} }
if (count($public_user_list)) if (count($public_user_list))
{ {
$filtermethod .= " OR (info_access='public' AND info_owner IN(" . implode(',',$public_user_list) . '))'; $filtermethod .= " OR (info_access='public' AND $public_access)";
} }
} }
$filtermethod .= ') '; $filtermethod .= ') ';
@ -348,6 +344,7 @@ class infolog_so // DB-Layer
*/ */
function read($info_id) // did _not_ ensure ACL function read($info_id) // did _not_ ensure ACL
{ {
//echo "<p>read($info_id) ".function_backtrace()."</p>\n";
if ($info_id && ((int)$info_id == $this->data['info_id'] || $info_id == $this->data['info_uid'])) if ($info_id && ((int)$info_id == $this->data['info_id'] || $info_id == $this->data['info_uid']))
{ {
return $this->data; // return the already read entry return $this->data; // return the already read entry
@ -587,30 +584,28 @@ class infolog_so // DB-Layer
* *
* This is done now be search too (in key info_anz_subs), if DB can use sub-queries * This is done now be search too (in key info_anz_subs), if DB can use sub-queries
* *
* @param $info_id id of log-entry * @param int|array $info_id id(s) of log-entry
* @return int the number of sub-entries * @return int|array the number of sub-entries or indexed by info_id, if array as param given
*/ */
function anzSubs( $info_id ) function anzSubs( $info_id )
{ {
if (($info_id = intval($info_id)) <= 0) if (!is_array($info_id) || !$info_id)
{ {
return 0; if ((int)$info_id <= 0) return 0;
} }
$this->db->select($this->info_table,'count(*)',array( $counts = array();
'info_id_parent' => $info_id, foreach($this->db->select($this->info_table,'info_id_parent,COUNT(*) AS info_anz_subs',array('info_id_parent' => $info_id),__LINE__,__FILE__,
$this->aclFilter() false,'GROUP BY info_id_parent','infolog') as $row)
),__LINE__,__FILE__); {
$counts[$row['info_id_parent']] = (int)$row['info_anz_subs'];
$this->db->next_record(); }
//echo "<p>anzSubs($info_id) = ".$this->db->f(0)." ($sql)</p>\n"; //echo '<p>'.__METHOD__."($info_id) = ".array2string($counts)."</p>\n";
return $this->db->f(0); return is_array($info_id) ? $counts : (int)array_pop($counts);
} }
/** /**
* searches InfoLog for a certain pattern in $query * searches InfoLog for a certain pattern in $query
* *
* If DB can use sub-queries, the number of subs are under the key info_anz_subs.
*
* @param $query[order] column-name to sort after * @param $query[order] column-name to sort after
* @param $query[sort] sort-order DESC or ASC * @param $query[sort] sort-order DESC or ASC
* @param $query[filter] string with combination of acl-, date- and status-filters, eg. 'own-open-today' or '' * @param $query[filter] string with combination of acl-, date- and status-filters, eg. 'own-open-today' or ''
@ -716,7 +711,7 @@ class infolog_so // DB-Layer
$cats = $GLOBALS['egw']->categories->return_all_children((int)$query['cat_id']); $cats = $GLOBALS['egw']->categories->return_all_children((int)$query['cat_id']);
$filtermethod .= ' AND info_cat'.(count($cats)>1? ' IN ('.implode(',',$cats).') ' : '='.(int)$query['cat_id']); $filtermethod .= ' AND info_cat'.(count($cats)>1? ' IN ('.implode(',',$cats).') ' : '='.(int)$query['cat_id']);
} }
$join = $distinct = $count_subs = ''; $join = $distinct = '';
if ($query['query']) $query['search'] = $query['query']; // allow both names if ($query['query']) $query['search'] = $query['query']; // allow both names
if ($query['search']) // we search in _from, _subject, _des and _extra_value for $query if ($query['search']) // we search in _from, _subject, _des and _extra_value for $query
{ {
@ -771,23 +766,19 @@ class infolog_so // DB-Layer
{ {
$query['total'] = $this->db->query($sql="SELECT $distinct main.info_id ".$sql_query,__LINE__,__FILE__)->NumRows(); $query['total'] = $this->db->query($sql="SELECT $distinct main.info_id ".$sql_query,__LINE__,__FILE__)->NumRows();
} }
if ($this->db->capabilities['sub_queries'])
{
$count_subs = ",(SELECT COUNT(*) FROM $this->info_table sub WHERE sub.info_id_parent=main.info_id AND $acl_filter) AS info_anz_subs";
}
$info_customfield = ''; $info_customfield = '';
if ($sortbycf != '') if ($sortbycf != '')
{ {
$info_customfield = ", (SELECT DISTINCT info_extra_value FROM $this->extra_table sub2 where sub2.info_id=main.info_id AND info_extra_name=".$this->db->quote($sortbycf).") AS cfsortcrit "; $info_customfield = ", (SELECT DISTINCT info_extra_value FROM $this->extra_table sub2 where sub2.info_id=main.info_id AND info_extra_name=".$this->db->quote($sortbycf).") AS cfsortcrit ";
} }
//echo "SELECT $distinct main.* $count_subs $info_customfield $sql_query $ordermethod"."<br>"; //echo "SELECT $distinct main.* $info_customfield $sql_query $ordermethod"."<br>";
do do
{ {
if (isset($query['start']) && isset($query['total']) && $query['start'] > $query['total']) if (isset($query['start']) && isset($query['total']) && $query['start'] > $query['total'])
{ {
$query['start'] = 0; $query['start'] = 0;
} }
$rs = $this->db->query($sql="SELECT $mysql_calc_rows $distinct main.* $count_subs $info_customfield $sql_query $ordermethod",__LINE__,__FILE__, $rs = $this->db->query($sql="SELECT $mysql_calc_rows $distinct main.* $info_customfield $sql_query $ordermethod",__LINE__,__FILE__,
(int) $query['start'],isset($query['start']) ? (int) $query['num_rows'] : -1,false,egw_db::FETCH_ASSOC); (int) $query['start'],isset($query['start']) ? (int) $query['num_rows'] : -1,false,egw_db::FETCH_ASSOC);
//echo "<p>db::query('$sql',,,".(int)$query['start'].','.(isset($query['start']) ? (int) $query['num_rows'] : -1).")</p>\n"; //echo "<p>db::query('$sql',,,".(int)$query['start'].','.(isset($query['start']) ? (int) $query['num_rows'] : -1).")</p>\n";

View File

@ -355,10 +355,11 @@ class infolog_ui
// set old show_times pref, that get_info calculates the cumulated time of the timesheets // set old show_times pref, that get_info calculates the cumulated time of the timesheets
$this->prefs['show_times'] = strpos($this->prefs['nextmatch-'.$query['columnselection_pref']],'info_used_time_info_planned_time_info_replanned_time') !== false; $this->prefs['show_times'] = strpos($this->prefs['nextmatch-'.$query['columnselection_pref']],'info_used_time_info_planned_time_info_replanned_time') !== false;
// query all links in one go // query all links and sub counts in one go
if ($infos && !$query['csv_export']) if ($infos && !$query['csv_export'])
{ {
$links = bolink::get_links_multiple('infolog',array_keys($infos)); $links = bolink::get_links_multiple('infolog',array_keys($infos));
$anzSubs = $this->bo->anzSubs(array_keys($infos));
} }
$readonlys = $rows = array(); $readonlys = $rows = array();
foreach($infos as $id => $info) foreach($infos as $id => $info)
@ -366,6 +367,7 @@ class infolog_ui
if (!$query['csv_export']) if (!$query['csv_export'])
{ {
$info['links'] =& $links[$id]; $info['links'] =& $links[$id];
$info['info_anz_subs'] = (int)$anzSubs[$id];
$info = $this->get_info($info,$readonlys,$query['action'],$query['action_id'],$query['filter2'],$details); $info = $this->get_info($info,$readonlys,$query['action'],$query['action_id'],$query['filter2'],$details);
if (!$query['filter2'] && $this->prefs['show_links'] == 'no_describtion' || if (!$query['filter2'] && $this->prefs['show_links'] == 'no_describtion' ||