diff --git a/include/class.orm.php b/include/class.orm.php index c004ab455bd3c43252cdcb42947b78aaf2c1befe..1f3631db3bfc4a94a639ab4264782b6d39cfa646 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -728,8 +728,9 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl return $this; } - function order_by() { - $this->ordering = array_merge($this->ordering, func_get_args()); + function order_by($order) { + $this->ordering = array_merge($this->ordering, + is_array($order) ? $order : func_get_args()); return $this; } function getSortFields() { @@ -754,6 +755,10 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl return $this; } + function isWindowed() { + return $this->limit || $this->offset; + } + function select_related() { $this->related = array_merge($this->related, func_get_args()); return $this; @@ -945,6 +950,26 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl return $this->query; } + function asView() { + $unique = spl_object_hash($this); + $classname = "QueryView{$unique}"; + $class = <<<EOF +class {$classname} extends VerySimpleModel { + static \$meta = array( + 'view' => true, + ); + static \$queryset; + + static function getQuery(\$compiler) { + return ' ('.static::\$queryset->getQuery().') '; + } +} +EOF; + eval($class); // Ugh + $classname::$queryset = $this; + return $classname; + } + function serialize() { $info = get_object_vars($this); unset($info['query']); @@ -1681,6 +1706,14 @@ class MySqlCompiler extends SqlCompiler { $vals = array_map(array($this, 'input'), $b); $b = implode(', ', $vals); } + // MySQL doesn't support LIMIT or OFFSET in subqueries. Instead, add + // add the constraint to the join + elseif ($b instanceof QuerySet && $b->isWindowed()) { + $f1 = $b->values[0]; + $view = $b->asView(); + $alias = $this->pushJoin($view, $a, $view, array('constraint'=>array())); + return sprintf('%s = %s.%s', $a, $alias, $this->quote($f1)); + } else { $b = $this->input($b); } @@ -1744,12 +1777,16 @@ class MySqlCompiler extends SqlCompiler { if ($extra instanceof Q) { $constraints[] = $this->compileQ($extra, $model, self::SLOT_JOINS); } + if (!isset($rmodel)) + $rmodel = $model; // Support inline views $table = ($rmodel::$meta['view']) ? $rmodel::getQuery($this) : $this->quote($rmodel::$meta['table']); - return $join.$table - .' '.$alias.' ON ('.implode(' AND ', $constraints).')'; + $base = $join.$table.$alias; + if ($constraints) + $base .= ' ON ('.implode(' AND ', $constraints).')'; + return $base; } /** @@ -1992,7 +2029,7 @@ class MySqlCompiler extends SqlCompiler { foreach ($queryset->distinct as $d) list($group_by[]) = $this->getField($d, $model); } - $group_by = $group_by ? ' GROUP BY '.implode(',', $group_by) : ''; + $group_by = $group_by ? ' GROUP BY '.implode(', ', $group_by) : ''; $joins = $this->getJoins($queryset); diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index ca01e03ddb0383ba6451abeeffcf2a85dba5f0b5..45ee6847be6da3a2b4ea031731f12849712ed88e 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -111,18 +111,18 @@ if (!$view_all_tickets) { $tickets->filter(Q::any($visibility)); } -// Add in annotations -$tickets->annotate(array( - 'collab_count' => SqlAggregate::COUNT('thread__collaborators'), - 'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'), - 'thread_count' => SqlAggregate::COUNT('thread__entries'), -)); +// Apply requested quick filter -// Select pertinent columns -// ------------------------------------------------------------ -$tickets->values('lock__staff_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status_id', 'status__name', 'status__state', 'dept_id', 'dept__name', 'user__name', 'lastupdate'); +// Rewrite $tickets to use a nested query, which will include the LIMIT part +// in order to speed the result -// Apply requested quick filter +// Apply requested pagination +$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; +$pageNav = new Pagenate($tickets->count(), $page, PAGE_LIMIT); +$pageNav->setURL('tickets.php', $args); +$tickets = $pageNav->paginate($tickets); + +$tickets2 = TicketModel::objects(); // Apply requested sorting $queue_sort_key = sprintf(':Q:%s:sort', $queue_name); @@ -156,14 +156,26 @@ case 'updated': break; } -// Apply requested pagination -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($tickets->count(), $page, PAGE_LIMIT); -$pageNav->setURL('tickets.php', $args); -$tickets = $pageNav->paginate($tickets); +$tickets2 = TicketModel::objects(); +$tickets2->filter(array('ticket_id__in' => $tickets->values_flat('ticket_id'))); + +// Transfer the order_by from the original tickets +$tickets2->order_by($tickets->getSortFields()); +$tickets = $tickets2; TicketForm::ensureDynamicDataView(); +// Select pertinent columns +// ------------------------------------------------------------ +$tickets->values('lock__staff_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status_id', 'status__name', 'status__state', 'dept_id', 'dept__name', 'user__name', 'lastupdate'); + +// Add in annotations +$tickets->annotate(array( + 'collab_count' => SqlAggregate::COUNT('thread__collaborators'), + 'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'), + 'thread_count' => SqlAggregate::COUNT('thread__entries'), +)); + // Save the query to the session for exporting $_SESSION[':Q:tickets'] = $tickets;