From 78b40243b7d3d0f90ed8a12868c1195d1b8f5020 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 3 Aug 2011 16:53:22 +0000 Subject: [PATCH] * CalDAV/calendar backported iterator from trunk to minimize resource usage - move all filtering into SQL query in calendar_so, to be able to correctly return N rows starting from row M - re-enabling propfind iterator again for calendar (fetching events in chunks of 500), to lower memory footprint Please note: changed SQL queries used for CalDAV do not take changed participants (or status) in exceptions into account - merged: r34529, r34584, r34592, r34594, r35948 --- calendar/inc/class.calendar_bo.inc.php | 50 ++---- calendar/inc/class.calendar_groupdav.inc.php | 25 ++- calendar/inc/class.calendar_so.inc.php | 169 +++++++++++-------- phpgwapi/inc/class.groupdav_handler.inc.php | 22 +-- 4 files changed, 131 insertions(+), 135 deletions(-) diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 8beee061df..c7eaec2766 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -155,6 +155,10 @@ class calendar_bo * @var array $cached_holidays holidays plus birthdays (gets cached in the session for performance reasons) */ var $cached_holidays; + /** + * @var boholiday + */ + var $holidays; /** * Instance of the socal class * @@ -445,10 +449,17 @@ class calendar_bo $this->check_move_horizont($end); } $daywise = !isset($params['daywise']) ? False : !!$params['daywise']; - $enum_recuring = $daywise || !isset($params['enum_recuring']) || !!$params['enum_recuring']; + $params['enum_recuring'] = $enum_recuring = $daywise || !isset($params['enum_recuring']) || !!$params['enum_recuring']; $cat_id = isset($params['cat_id']) ? $params['cat_id'] : 0; $filter = isset($params['filter']) ? $params['filter'] : 'all'; $offset = isset($params['offset']) && $params['offset'] !== false ? (int) $params['offset'] : false; + // socal::search() returns rejected group-invitations, as only the user not also the group is rejected + // as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed + $users_in = (array)$params_in['users']; + $remove_rejected_by_user = !in_array($filter,array('all','rejected')) && + count($users_in) == 1 && $users_in[0] > 0 ? $users_in[0] : null; + //error_log(__METHOD__.'('.array2string($params_in).", $sql_filter) params[users]=".array2string($params['users']).' --> remove_rejected_by_user='.array2string($remove_rejected_by_user)); + if ($this->debug && ($this->debug > 1 || $this->debug == 'search')) { $this->debug_message('bocal::search(%1) start=%2, end=%3, daywise=%4, cat_id=%5, filter=%6, query=%7, offset=%8, num_rows=%9, order=%10, sql_filter=%11)', @@ -456,7 +467,7 @@ class calendar_bo } // date2ts(,true) converts to server time, db2data converts again to user-time $events =& $this->so->search(isset($start) ? $this->date2ts($start,true) : null,isset($end) ? $this->date2ts($end,true) : null, - $users,$cat_id,$filter,$offset,(int)$params['num_rows'],$params); + $users,$cat_id,$filter,$offset,(int)$params['num_rows'],$params,$remove_rejected_by_user); if (isset($params['cols'])) { @@ -465,24 +476,9 @@ class calendar_bo $this->total = $this->so->total; $this->db2data($events,isset($params['date_format']) ? $params['date_format'] : 'ts'); - // socal::search() returns rejected group-invitations, as only the user not also the group is rejected - // as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed - $remove_rejected_by_user = !in_array($filter,array('all','rejected','owner')) && count($params['users']) == 1 ? $params['users'][0] : false; //echo "

remove_rejected_by_user=$remove_rejected_by_user, filter=$filter, params[users]=".print_r($param['users'])."

\n"; foreach($events as $id => $event) { - if (isset($start) && $event['end'] < $start) - { - unset($events[$id]); // remove former events (e.g. whole day) - $this->total--; - continue; - } - if ($remove_rejected_by_user && $event['participants'][$remove_rejected_by_user] == 'R') - { - unset($events[$id]); // remove the rejected event - $this->total--; - continue; - } if ($params['enum_groups'] && $this->enum_groups($event)) { $events[$id] = $event; @@ -538,29 +534,11 @@ class calendar_bo $this->debug_message('socalendar::search daywise events=%1',False,$events); } } - elseif(!$enum_recuring) - { - $recur_ids = array(); - foreach($events as $k => $event) - { - if ($event['recur_type'] != MCAL_RECUR_NONE) - { - if (!in_array($event['id'],$recur_ids)) - { - $recur_ids[] = $event['id']; - } - unset($events[$k]); - } - } - if (count($recur_ids)) - { - $events = array_merge($this->read($recur_ids,null,false,$params['date_format']),$events); - } - } if ($this->debug && ($this->debug > 0 || $this->debug == 'search')) { $this->debug_message('bocal::search(%1)=%2',True,$params,$events); } + //error_log(__METHOD__."() returning ".count($events)." entries, total=$this->total ".function_backtrace()); return $events; } diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index a71532709a..14a407fc39 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -122,7 +122,12 @@ class calendar_groupdav extends groupdav_handler 'enum_recuring' => false, 'daywise' => false, 'date_format' => 'server', + 'no_total' => true, // we need no total number of rows (saves extra query) ); + if ($this->client_shared_uid_exceptions) // do NOT return (non-virtual) exceptions + { + $filter['query'] = array('cal_reference' => 0); + } if ($path == '/calendar/') { @@ -140,8 +145,7 @@ class calendar_groupdav extends groupdav_handler } if ($this->debug > 1) { - error_log(__METHOD__."($path,,,$user,$id) filter=". - array2string($filter)); + error_log(__METHOD__."($path,,,$user,$id) filter=".array2string($filter)); } // check if we have to return the full calendar data or just the etag's @@ -157,12 +161,12 @@ class calendar_groupdav extends groupdav_handler } } } -/* disabling not working iterator // return iterator, calling ourself to return result in chunks $files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']); + return true; } -*/ + /** * Callback for profind interator * @@ -171,15 +175,13 @@ class calendar_groupdav extends groupdav_handler * @param array|boolean $start=false false=return all or array(start,num) * @return array with "files" array with values for keys path and props */ -/* disabling not working iterator function propfind_callback($path,array $filter,$start=false) { -*/ if ($this->debug) $starttime = microtime(true); $calendar_data = $filter['calendar_data']; unset($filter['calendar_data']); -/* disabling not working iterator + $files = array(); if (is_array($start)) @@ -187,8 +189,6 @@ class calendar_groupdav extends groupdav_handler $filter['offset'] = $start[0]; $filter['num_rows'] = $start[1]; } -error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($filter)); -*/ $events =& $this->bo->search($filter); if ($events) { @@ -197,6 +197,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f { if ($this->client_shared_uid_exceptions && $event['reference']) { + throw new egw_exception_assertion_failed(__METHOD__."() event=".array2string($event)); // this exception will be handled with the series master unset($events[$k]); continue; @@ -228,10 +229,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f { $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it } -/* disabling not working iterator $files[] = array( -*/ - $files['files'][] = array( 'path' => $path.$this->get_path($event), 'props' => $props, ); @@ -242,10 +240,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f error_log(__METHOD__."($path) took ".(microtime(true) - $starttime). ' to return '.count($files['files']).' items'); } -/* disabling not working iterator return $files; -*/ - return true; } /** diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 066c92795d..6563fc3fdd 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -98,7 +98,6 @@ class calendar_so */ protected static $tz_cache = array(); - /** * Constructor of the socal class */ @@ -373,17 +372,18 @@ class calendar_so * @param string $params['append'] SQL to append to the query before $order, eg. for a GROUP BY clause * @param array $params['cfs'] custom fields to query, null = none, array() = all, or array with cfs names * @param array $params['users'] raw parameter as passed to calendar_bo::search() no memberships resolved! + * @param int $remove_rejected_by_user=null add join to remove entry, if given user has rejected it * @return array of cal_ids, or false if error in the parameters * * ToDo: search custom-fields too */ - function &search($start,$end,$users,$cat_id=0,$filter='all',$offset=False,$num_rows=0,array $params=array()) + function &search($start,$end,$users,$cat_id=0,$filter='all',$offset=False,$num_rows=0,array $params=array(),$remove_rejected_by_user=null) { - //error_log('*** '.__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($offset).",$num_rows,".array2string($params).') '.function_backtrace()); + //error_log(__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($offset).",$num_rows,".array2string($params).') '.function_backtrace()); $cols = self::get_columns('calendar', $this->cal_table); $cols[0] = $this->db->to_varchar($this->cal_table.'.cal_id'); - $cols = isset($params['cols']) ? $params['cols'] : "$this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".implode(',',$cols).",cal_start,cal_end,cal_recur_date"; + $cols = isset($params['cols']) ? $params['cols'] : "$this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".implode(',',$cols).",cal_start,cal_end,$this->user_table.cal_recur_date"; $where = array(); if (is_array($params['query'])) @@ -423,7 +423,8 @@ class calendar_so $users_by_type[$user[0]][] = (int) substr($user,1); } } - $to_or = $user_or = $owner_or = array(); + $to_or = $user_or = array(); + $owner_or = null; $useUnionQuery = $this->db->capabilities['distinct_on_text'] && $this->db->capabilities['union']; $table_def = $this->db->get_table_definitions('calendar',$this->user_table); foreach($users_by_type as $type => $ids) @@ -431,20 +432,22 @@ class calendar_so // when we are able to use Union Querys, we do not OR our query, we save the needed parts for later construction of the union if ($useUnionQuery) { - $user_or[] = $this->db->expression($table_def,array( + $user_or[] = $this->db->expression($table_def,$this->user_table.'.',array( 'cal_user_type' => $type, + ),' AND '.$this->user_table.'.',array( 'cal_user_id' => $ids, )); - if ($type == 'u' && ($filter == 'owner')) + if ($type == 'u' && $filter == 'owner') { $cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table); - $owner_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids)); + $owner_or = $this->db->expression($cal_table_def,array('cal_owner' => $ids)); } } else { - $to_or[] = $this->db->expression($table_def,array( + $to_or[] = $this->db->expression($table_def,$this->user_table.'.',array( 'cal_user_type' => $type, + ),' AND '.$this->user_table.'.',array( 'cal_user_id' => $ids, )); if ($type == 'u' && ($filter == 'owner')) @@ -465,24 +468,24 @@ class calendar_so { case 'showonlypublic': $where['cal_public'] = 1; - $where[] = "cal_status != 'R'"; break; + $where[] = "$this->user_table.cal_status != 'R'"; break; case 'deleted': $where['cal_deleted'] = true; break; case 'unknown': - $where[] = "cal_status='U'"; break; + $where[] = "$this->user_table.cal_status='U'"; break; case 'accepted': - $where[] = "cal_status='A'"; break; + $where[] = "$this->user_table.cal_status='A'"; break; case 'tentative': - $where[] = "cal_status='T'"; break; + $where[] = "$this->user_table.cal_status='T'"; break; case 'rejected': - $where[] = "cal_status='R'"; break; + $where[] = "$this->user_table.cal_status='R'"; break; case 'delegated': - $where[] = "cal_status='D'"; break; + $where[] = "$this->user_table.cal_status='D'"; break; case 'all': case 'owner': break; default: - $where[] = "cal_status != 'R'"; + $where[] = "$this->user_table.cal_status != 'R'"; break; } } @@ -490,11 +493,44 @@ class calendar_so { $where[] = $this->cat_filter($cat_id); } - if ($start) $where[] = (int)$start.' < cal_end'; + if ($start) + { + if ($params['enum_recuring']) + { + $where[] = (int)$start.' < cal_end'; + } + else + { + // we check recur_endate!=0, because it can be NULL, 0 or !=0 !!! + $where[] = (int)$start.' < (CASE WHEN recur_type IS NULL THEN cal_end ELSE (CASE WHEN recur_enddate!=0 THEN recur_enddate ELSE 9999999999 END) END)'; + } + } + // if not enum recuring events, we have to use minimum start- AND end-dates, otherwise we get more then one event per cal_id! + if (!$params['enum_recuring']) + { + $where[] = "$this->user_table.cal_recur_date=0"; + $group_by = 'GROUP BY '.str_replace(array('cal_start,','cal_end,'),'',implode(', ',(array)$cols)); + $cols = str_replace(array('cal_start','cal_end'),array('MIN(cal_start) AS cal_start','MIN(cal_end) AS cal_end'),$cols); + } if ($end) $where[] = 'cal_start < '.(int)$end; - if (!preg_match('/^[a-z_ ,]+$/i',$params['order'])) $params['order'] = 'cal_start'; // gard against SQL injection + if (!preg_match('/^[a-z_ ,c]+$/i',$params['order'])) $params['order'] = 'cal_start'; // gard against SQL injection + if ($remove_rejected_by_user) + { + $rejected_by_user_join = "LEFT JOIN $this->user_table rejected_by_user". + " ON $this->cal_table.cal_id=rejected_by_user.cal_id". + " AND rejected_by_user.cal_user_type='u'". + " AND rejected_by_user.cal_user_id=".$this->db->quote($remove_rejected_by_user). + " AND (recur_type IS NULL AND rejected_by_user.cal_recur_date=0 OR cal_start=rejected_by_user.cal_recur_date)"; + $or_required = array( + 'rejected_by_user.cal_status IS NULL', + "rejected_by_user.cal_status!='R'", + ); + if ($filter == 'owner') $or_required[] = 'cal_owner='.(int)$remove_rejected_by_user; + $where[] = '('.implode(' OR ',$or_required).')'; + } + //$starttime = microtime(true); if ($useUnionQuery) { // allow apps to supply participants and/or icons @@ -503,89 +539,72 @@ class calendar_so // changed the original OR in the query into a union, to speed up the query execution under MySQL 5 $select = array( 'table' => $this->cal_table, - 'join' => "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id", + 'join' => "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id $rejected_by_user_join", 'cols' => $cols, 'where' => $where, 'app' => 'calendar', - 'append'=> $params['append'], + 'append'=> $params['append'].' '.$group_by, ); + $selects = array(); // we check if there are parts to use for the construction of our UNION query, // as replace the OR by construction of a suitable UNION for performance reasons - if (!empty($owner_or)||!empty($user_or)) + if ($owner_or || $user_or) { - if (!empty($owner_or) && !empty($user_or)) + foreach($user_or as $user_sql) { - // if the query is to be filtered by owner OR user we need 4 selects for the union - //_debug_array($owner_or); - $selects = array(); - foreach(array_keys($user_or) as $key) + $selects[] = $select; + $selects[count($selects)-1]['where'][] = $user_sql; + if ($params['enum_recuring']) { + $selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0"; $selects[] = $select; - $selects[count($selects)-1]['where'][] = $user_or[$key]; - $selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0'; - $selects[] = $select; - $selects[count($selects)-1]['where'][] = $user_or[$key]; - $selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start'; + $selects[count($selects)-1]['where'][] = $user_sql; + $selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start"; } - $selects[] = $select; - $selects[count($selects)-1]['where'][] = $owner_or; - $selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0'; - $selects[] = $select; - $selects[count($selects)-1]['where'][] = $owner_or; - $selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start'; } - else + // if the query is to be filtered by owner we need to add more selects for the union + if ($owner_or) { - // if the query is to be filtered only by user we need 2 selects for the union - $selects = array(); - foreach(array_keys($user_or) as $key) + $selects[] = $select; + $selects[count($selects)-1]['where'][] = $owner_or; + if ($params['enum_recuring']) { + $selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0"; $selects[] = $select; - $selects[count($selects)-1]['where'][] = $user_or[$key]; - $selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0'; - $selects[] = $select; - $selects[count($selects)-1]['where'][] = $user_or[$key]; - $selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start'; + $selects[count($selects)-1]['where'][] = $owner_or; + $selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start"; } } } else { // if the query is to be filtered by neither by user nor owner (should not happen?) we need 2 selects for the union - $selects = array($select,$select); - $selects[0]['where'][] = 'recur_type IS NULL AND cal_recur_date=0'; - $selects[1]['where'][] = 'cal_recur_date=cal_start'; + $selects[] = $select; + if ($params['enum_recuring']) + { + $selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0"; + $selects[] = $select; + $selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start"; + } } - if (is_numeric($offset)) // get the total too + if (is_numeric($offset) && !$params['no_total']) // get the total too { + $save_selects = $selects; // we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns) - $countSelects = count($selects); foreach(array_keys($selects) as $key) { - $selects[$key]['cols'] = "DISTINCT $this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".$this->db->to_varchar($this->cal_table.'.cal_id').",cal_start,cal_end,cal_recur_date"; - //$selects[0]['cols'] = $selects[1]['cols'] = "DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date"; + $selects[$key]['cols'] = "DISTINCT $this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".$this->db->to_varchar($this->cal_table.'.cal_id').",cal_start,cal_end,$this->user_table.cal_recur_date"; + if (!$params['enum_recuring']) + { + $selects[$key]['cols'] = str_replace('cal_start','MIN(cal_start) AS cal_start',$selects[$key]['cols']); + } } if (!isset($param['cols'])) self::get_union_selects($selects,$start,$end,$users,$cat_id,$filter,$params['query'],$params['users']); $this->total = $this->db->union($selects,__LINE__,__FILE__)->NumRows(); - $i = 0; - foreach(array_keys($selects) as $key) - { - if ($i >= $countSelects) continue; - $i++; - $selects[$key]['cols'] = $select['cols']; // restore the original cols - //$selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols - } - $i = 0; - $selections = array(); - foreach(array_keys($selects) as $key) - { - if ($i >= $countSelects) continue; - $i++; - $selections[] = $selects[$key]; - } - $selects = $selections; + // restore original cols / selects + $selects = $save_selects; unset($save_selects); } if (!isset($param['cols'])) self::get_union_selects($selects,$start,$end,$users,$cat_id,$filter,$params['query'],$params['users']); @@ -593,7 +612,7 @@ class calendar_so } else // MsSQL oder MySQL 3.23 { - $where[] = '(recur_type IS NULL AND cal_recur_date=0 OR cal_recur_date=cal_start)'; + $where[] = "(recur_type IS NULL AND $this->user_table.cal_recur_date=0 OR $this->user_table.cal_recur_date=cal_start)"; //_debug_array($where); if (is_numeric($offset)) // get the total too @@ -601,12 +620,13 @@ class calendar_so // we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns) $this->total = $this->db->select($this->cal_table,"DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date", $where,__LINE__,__FILE__,false,'','calendar',0, - "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id")->NumRows(); + "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id")->NumRows(); } $rs = $this->db->select($this->cal_table,($this->db->capabilities['distinct_on_text'] ? 'DISTINCT ' : '').$cols, $where,__LINE__,__FILE__,$offset,$params['append'].' ORDER BY '.$params['order'],'calendar',$num_rows, - "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id"); + "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id"); } + //error_log(__METHOD__."() useUnionQuery=$useUnionQuery --> query took ".(microtime(true)-$starttime)); if (isset($params['cols'])) { return $rs; // if colums are specified we return the recordset / iterator @@ -651,7 +671,7 @@ class calendar_so foreach($this->db->select($utcal_id_view,'*',array( //'cal_id' => array_unique($ids), 'cal_recur_date' => $recur_dates, - ),__LINE__,__FILE__,false,'ORDER BY cal_id,cal_user_type DESC,'.self::STATUS_SORT,'calendar',$num_rows=0,$join='', + ),__LINE__,__FILE__,false,'ORDER BY cal_id,cal_user_type DESC,'.self::STATUS_SORT,'calendar',$num_rows,$join='', $this->db->get_table_definitions('calendar',$this->user_table)) as $row) // DESC puts users before resources and contacts { $id = $row['cal_id']; @@ -715,6 +735,7 @@ class calendar_so } } //echo "

socal::search\n"; _debug_array($events); + //error_log(__METHOD__."(,filter=".array2string($params['query']).",offset=$offset, num_rows=$num_rows) returning ".count($events)." entries".($offset!==false?" total=$this->total":'').' '.function_backtrace()); return $events; } diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php index 18cdb50b04..48fc4dfd29 100644 --- a/phpgwapi/inc/class.groupdav_handler.inc.php +++ b/phpgwapi/inc/class.groupdav_handler.inc.php @@ -460,16 +460,15 @@ class groupdav_propfind_iterator implements Iterator * * @param groupdav_handler $handler * @param array $filter filter for propfind call - * @param array $files=null extra files/responses to return too + * @param array $files=array() extra files/responses to return too */ - public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=null) + public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=array()) { if ($this->debug) error_log(__METHOD__."('$path', ".array2string($filter).",)"); $this->path = $path; $this->handler = $handler; $this->filter = $filter; - $this->files = $files; - $this->common_files = $files; + $this->files = $this->common_files = $files; reset($this->files); } @@ -507,6 +506,12 @@ class groupdav_propfind_iterator implements Iterator if ($this->debug) error_log(__METHOD__."() returning TRUE"); return true; } + // check if previous query gave less then CHUNK_SIZE entries --> we're done + if ($this->start && count($this->files) < self::CHUNK_SIZE) + { + if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)"); + return false; + } // try query further files via propfind callback of handler and store result in $this->files $this->files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE)); if (!is_array($this->files) || !($entries = count($this->files))) @@ -514,10 +519,10 @@ class groupdav_propfind_iterator implements Iterator if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)"); return false; // no further entries } - $this->start += $entries; + $this->start += self::CHUNK_SIZE; reset($this->files); - if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false)); + if ($this->debug) error_log(__METHOD__."() this->start=$this->start, entries=$entries, count(this->files)=".count($this->files)." returning ".array2string(current($this->files) !== false)); return current($this->files) !== false; } @@ -529,11 +534,8 @@ class groupdav_propfind_iterator implements Iterator { if ($this->debug) error_log(__METHOD__."()"); - // query first set of files via propfind callback of handler and store result in $this->files $this->start = 0; - $files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE)); - $this->files = array_merge($this->common_files, $files); - $this->start += self::CHUNK_SIZE; + $this->files = $this->common_files; reset($this->files); }