use UNIONs instead of OR for calendar filter "owner too"

This commit is contained in:
Klaus Leithoff 2010-04-15 14:50:54 +00:00
parent 5d6a7ede84
commit 0efd1057ea

View File

@ -364,21 +364,39 @@ class calendar_so
$users_by_type[$user[0]][] = (int) substr($user,1);
}
}
$to_or = array();
$to_or = $user_or = $owner_or = array();
$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)
{
$to_or[] = $this->db->expression($table_def,array(
'cal_user_type' => $type,
'cal_user_id' => $ids,
));
if ($type == 'u' && ($filter == 'owner'))
// 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)
{
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
$to_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
$user_or[] = $this->db->expression($table_def,array(
'cal_user_type' => $type,
'cal_user_id' => $ids,
));
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));
}
}
else
{
$to_or[] = $this->db->expression($table_def,array(
'cal_user_type' => $type,
'cal_user_id' => $ids,
));
if ($type == 'u' && ($filter == 'owner'))
{
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
$to_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
}
}
}
$where[] = '('.implode(' OR ',$to_or).')';
// this is only used, when we cannot use UNIONS
if (!$useUnionQuery) $where[] = '('.implode(' OR ',$to_or).')';
switch($filter)
{
@ -410,7 +428,7 @@ class calendar_so
if (!preg_match('/^[a-z_ ,]+$/i',$order)) $order = 'cal_start'; // gard against SQL injection
if ($this->db->capabilities['distinct_on_text'] && $this->db->capabilities['union'])
if ($useUnionQuery)
{
// changed the original OR in the query into a union, to speed up the query execution under MySQL 5
$select = array(
@ -421,18 +439,64 @@ class calendar_so
'app' => 'calendar',
'append'=> $append,
);
$selects = array($select,$select);
$selects[0]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
$selects[1]['where'][] = 'cal_recur_date=cal_start';
// 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 (!empty($owner_or) && !empty($user_or))
{
// 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)
{
array_push($selects,$select);
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
array_push($selects,$select);
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start';
}
array_push($selects,$select);
$selects[count($selects)-1]['where'][] = $owner_or;
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
array_push($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 only by user we need 2 selects for the union
$selects = array();
foreach(array_keys($user_or) as $key)
{
array_push($selects,$select);
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
array_push($selects,$select);
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = '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';
}
if (is_numeric($offset)) // get the total too
{
// 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)
$selects[0]['cols'] = $selects[1]['cols'] = "DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date";
foreach(array_keys($selects) as $key) $selects[$key]['cols'] = "DISTINCT $this->repeats_table.*,$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";
$this->total = $this->db->union($selects,__LINE__,__FILE__)->NumRows();
$selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols
foreach(array_keys($selects) as $key) $selects[$key]['cols'] = $select['cols']; // restore the original cols
//$selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols
//$selects = array($selects[0],$selects[1]); // what is this one used for?
}
// error_log("calendar_so_search:\n" . print_r($selects, true));
$rs = $this->db->union($selects,__LINE__,__FILE__,$order,$offset,$num_rows);