2 // $Id: query.inc,v 1.42 2009/03/24 23:06:24 merlinofchaos Exp $
5 * Defines the query object which is the underlying layer in a View.
9 * Object used to create a SELECT query.
14 * A list of tables in the order they should be added, keyed by alias.
16 var $table_queue = array();
19 * Holds an array of tables and counts added so that we can create aliases
21 var $tables = array();
24 * Holds an array of relationships, which are aliases of the primary
25 * table that represent different ways to join the same table in.
27 var $relationships = array();
30 * An array of sections of the WHERE query. Each section is in itself
31 * an array of pieces and a flag as to whether or not it should be AND
36 * An array of sections of the HAVING query. Each section is in itself
37 * an array of pieces and a flag as to whether or not it should be AND
40 var $having = array();
42 * The default operator to use when connecting the WHERE groups. May be
45 var $group_operator = 'AND';
48 * A simple array of order by clauses.
50 var $orderby = array();
53 * A simple array of group by clauses.
55 var $groupby = array();
58 * The table header to use for tablesort. This matters because tablesort
59 * needs to modify the query and needs the header.
61 var $header = array();
64 * A flag as to whether or not to make the primary field distinct.
66 var $distinct = FALSE;
69 * Constructor; Create the basic query object and fill with default values.
71 function views_query($base_table = 'node', $base_field = 'nid') {
72 $this->base_table = $base_table; // Predefine these above, for clarity.
73 $this->base_field = $base_field;
74 $this->relationships[$base_table] = array(
76 'table' => $base_table,
77 'alias' => $base_table,
81 // init the table queue with our primary table.
82 $this->table_queue[$base_table] = array(
83 'alias' => $base_table,
84 'table' => $base_table,
85 'relationship' => $base_table,
89 // init the tables with our primary table
90 $this->tables[$base_table][$base_table] = array(
92 'alias' => $base_table,
96 $this->fields[$base_field] = array(
97 'table' => $base_table,
98 'field' => $base_field,
99 'alias' => $base_field,
103 $this->count_field = array(
104 'table' => $base_table,
105 'field' => $base_field,
106 'alias' => $base_field,
111 // ----------------------------------------------------------------
112 // Utility methods to set flags and data.
115 * Set the base field to be distinct.
117 function set_distinct($value = TRUE) {
118 if (!(isset($this->no_distinct) && $value)) {
119 $this->distinct = $value;
124 * Set what field the query will count() on for paging.
126 function set_count_field($table, $field, $alias = NULL) {
128 $alias = $table . '_' . $field;
130 $this->count_field = array(
139 * Set the table header; used for click-sorting because it's needed
140 * info to modify the ORDER BY clause.
142 function set_header($header) {
143 $this->header = $header;
146 // ----------------------------------------------------------------
150 * A relationship is an alternative endpoint to a series of table
151 * joins. Relationships must be aliases of the primary table and
152 * they must join either to the primary table or to a pre-existing
155 * An example of a relationship would be a nodereference table.
156 * If you have a nodereference named 'book_parent' which links to a
157 * parent node, you could set up a relationship 'node_book_parent'
158 * to 'node'. Then, anything that links to 'node' can link to
159 * 'node_book_parent' instead, thus allowing all properties of
160 * both nodes to be available in the query.
163 * What this relationship will be called, and is also the alias
166 * A views_join object (or derived object) to join the alias in.
168 * The name of the 'base' table this relationship represents; this
169 * tells the join search which path to attempt to use when finding
170 * the path to this relationship.
172 * If this relationship links to something other than the primary
173 * table, specify that table here. For example, a 'track' node
174 * might have a relationship to an 'album' node, which might
175 * have a relationship to an 'artist' node.
177 function add_relationship($alias, $join, $base, $link_point = NULL) {
178 if (empty($link_point)) {
179 $link_point = $this->base_table;
181 else if (!array_key_exists($link_point, $this->relationships)) {
185 // Make sure $alias isn't already used; if it, start adding stuff.
186 $alias_base = $alias;
188 while (!empty($this->relationships[$alias])) {
189 $alias = $alias_base . '_' . $count++;
192 // Add the table directly to the queue to avoid accidentally marking
194 $this->table_queue[$alias] = array(
195 'table' => $join->table,
199 'relationship' => $link_point,
202 $this->relationships[$alias] = array(
203 'link' => $link_point,
204 'table' => $join->table,
211 if ($alias = $this->add_table($join->table, $link_point, $join, $alias)) {
217 * Add a table to the query, ensuring the path exists.
219 * This function will test to ensure that the path back to the primary
220 * table is valid and exists; if you do not wish for this testing to
221 * occur, use $query->queue_table() instead.
224 * The name of the table to add. It needs to exist in the global table
226 * @param $relationship
227 * An alias of a table; if this is set, the path back to this table will
228 * be tested prior to adding the table, making sure that all intermediary
229 * tables exist and are properly aliased. If set to NULL the path to
230 * the primary table will be ensured. If the path cannot be made, the
231 * table will NOT be added.
233 * In some join configurations this table may actually join back through
234 * a different method; this is most likely to be used when tracing
235 * a hierarchy path. (node->parent->parent2->parent3). This parameter
236 * will specify how this table joins if it is not the default.
238 * A specific alias to use, rather than the default alias.
241 * The alias of the table; this alias can be used to access information
242 * about the table and should always be used to refer to the table when
243 * adding parts to the query. Or FALSE if the table was not able to be
246 function add_table($table, $relationship = NULL, $join = NULL, $alias = NULL) {
247 if (!$this->ensure_path($table, $relationship, $join)) {
251 return $this->queue_table($table, $relationship, $this->adjust_join($join, $relationship), $alias);
255 * Add a table to the query, without ensuring the path.
257 * This function will test to ensure that the path back to the primary
258 * table is valid and exists; if you do not wish for this testing to
259 * occur, use $query->queue_table() instead.
262 * The name of the table to add. It needs to exist in the global table
264 * @param $relationship
265 * The primary table alias this table is related to. If not set, the
266 * primary table will be used.
268 * In some join configurations this table may actually join back through
269 * a different method; this is most likely to be used when tracing
270 * a hierarchy path. (node->parent->parent2->parent3). This parameter
271 * will specify how this table joins if it is not the default.
273 * A specific alias to use, rather than the default alias.
276 * The alias of the table; this alias can be used to access information
277 * about the table and should always be used to refer to the table when
278 * adding parts to the query. Or FALSE if the table was not able to be
281 function queue_table($table, $relationship = NULL, $join = NULL, $alias = NULL) {
282 // If the alias is set, make sure it doesn't already exist.
283 if (isset($this->table_queue[$alias])) {
287 if (empty($relationship)) {
288 $relationship = $this->base_table;
291 if (!array_key_exists($relationship, $this->relationships)) {
295 if (!$alias && $join && $relationship && !empty($join->adjusted) && $table != $join->table) {
299 // Check this again to make sure we don't blow up existing aliases for already
301 if (isset($this->table_queue[$alias])) {
305 $alias = $this->mark_table($table, $relationship, $alias);
307 // If no alias is specified, give it the default.
308 if (!isset($alias)) {
309 $alias = $this->tables[$relationship][$table]['alias'] . $this->tables[$relationship][$table]['count'];
312 // If this is a relationship based table, add a marker with
313 // the relationship as a primary table for the alias.
314 if ($table != $alias) {
315 $this->mark_table($alias, $this->base_table, $alias);
318 // If no join is specified, pull it from the table data.
320 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
325 $join = $this->adjust_join($join, $relationship);
328 $this->table_queue[$alias] = array(
330 'num' => $this->tables[$relationship][$table]['count'],
333 'relationship' => $relationship,
339 function mark_table($table, $relationship, $alias) {
340 // Mark that this table has been added.
341 if (empty($this->tables[$relationship][$table])) {
342 if (!isset($alias)) {
344 if ($relationship != $this->base_table) {
345 // double underscore will help prevent accidental name
347 $alias = $relationship . '__';
351 $this->tables[$relationship][$table] = array(
357 $this->tables[$relationship][$table]['count']++;
364 * Ensure a table exists in the queue; if it already exists it won't
365 * do anything, but if it doesn't it will add the table queue. It will ensure
366 * a path leads back to the relationship table.
369 * The unaliased name of the table to ensure.
370 * @param $relationship
371 * The relationship to ensure the table links to. Each relationship will
372 * get a unique instance of the table being added. If not specified,
373 * will be the primary table.
375 * A views_join object (or derived object) to join the alias in.
378 * The alias used to refer to this specific table, or NULL if the table
381 function ensure_table($table, $relationship = NULL, $join = NULL) {
382 // ensure a relationship
383 if (empty($relationship)) {
384 $relationship = $this->base_table;
387 // If the relationship is the primary table, this actually be a relationship
388 // link back from an alias. We store all aliases along with the primary table
389 // to detect this state, because eventually it'll hit a table we already
390 // have and that's when we want to stop.
391 if ($relationship == $this->base_table && !empty($this->tables[$relationship][$table])) {
392 return $this->tables[$relationship][$table]['alias'];
395 if (!array_key_exists($relationship, $this->relationships)) {
399 if ($table == $this->relationships[$relationship]['base']) {
400 return $relationship;
403 // If we do not have join info, fetch it.
405 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
408 // If it can't be fetched, this won't work.
413 // Adjust this join for the relationship, which will ensure that the 'base'
414 // table it links to is correct. Tables adjoined to a relationship
415 // join to a link point, not the base table.
416 $join = $this->adjust_join($join, $relationship);
418 if ($this->ensure_path($table, $relationship, $join)) {
419 // Attempt to eliminate redundant joins. If this table's
420 // relationship and join exactly matches an existing table's
421 // relationship and join, we do not have to join to it again;
422 // just return the existing table's alias. See
423 // http://groups.drupal.org/node/11288 for details.
425 // This can be done safely here but not lower down in
426 // queue_table(), because queue_table() is also used by
427 // add_table() which requires the ability to intentionally add
428 // the same table with the same join multiple times. For
429 // example, a view that filters on 3 taxonomy terms using AND
430 // needs to join term_data 3 times with the same join.
432 // scan through the table queue to see if a matching join and
433 // relationship exists. If so, use it instead of this join.
435 // TODO: Scanning through $this->table_queue results in an
436 // O(N^2) algorithm, and this code runs every time the view is
437 // instantiated (Views 2 does not currently cache queries).
438 // There are a couple possible "improvements" but we should do
439 // some performance testing before picking one.
440 foreach ($this->table_queue as $queued_table) {
441 // In PHP 4 and 5, the == operation returns TRUE for two objects
442 // if they are instances of the same class and have the same
443 // attributes and values.
444 if ($queued_table['relationship'] == $relationship && $queued_table['join'] == $join) {
445 return $queued_table['alias'];
449 return $this->queue_table($table, $relationship, $join);
454 * Make sure that the specified table can be properly linked to the primary
455 * table in the JOINs. This function uses recursion. If the tables
456 * needed to complete the path back to the primary table are not in the
457 * query they will be added, but additional copies will NOT be added
458 * if the table is already there.
460 function ensure_path($table, $relationship = NULL, $join = NULL, $traced = array(), $add = array()) {
461 if (!isset($relationship)) {
462 $relationship = $this->base_table;
465 if (!array_key_exists($relationship, $this->relationships)) {
469 // If we do not have join info, fetch it.
471 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
474 // If it can't be fetched, this won't work.
479 // Does a table along this path exist?
480 if (isset($this->tables[$relationship][$table]) ||
481 ($join && $join->left_table == $relationship) ||
482 ($join && $join->left_table == $this->relationships[$relationship]['table'])) {
484 // Make sure that we're linking to the correct table for our relationship.
485 foreach (array_reverse($add) as $table => $path_join) {
486 $this->queue_table($table, $relationship, $this->adjust_join($path_join, $relationship));
491 // Have we been this way?
492 if (isset($traced[$join->left_table])) {
493 // we looped. Broked.
497 // Do we have to add this table?
498 $left_join = $this->get_join_data($join->left_table, $this->relationships[$relationship]['base']);
499 if (!isset($this->tables[$relationship][$join->left_table])) {
500 $add[$join->left_table] = $left_join;
504 $traced[$join->left_table] = TRUE;
505 return $this->ensure_path($join->left_table, $relationship, $left_join, $traced, $add);
509 * Fix a join to adhere to the proper relationship; the left table can vary
510 * based upon what relationship items are joined in on.
512 function adjust_join($join, $relationship) {
513 if (!empty($join->adjusted)) {
517 if (empty($relationship) || empty($this->relationships[$relationship])) {
521 // Adjusts the left table for our relationship.
522 if ($relationship != $this->base_table) {
523 // If we're linking to the primary table, the relationship to use will
524 // be the prior relationship. Unless it's a direct link.
526 // Safety! Don't modify an original here.
527 $join = drupal_clone($join);
529 // Do we need to try to ensure a path?
530 if ($join->left_table != $this->relationships[$relationship]['table'] &&
531 $join->left_table != $this->relationships[$relationship]['base'] &&
532 !isset($this->tables[$relationship][$join->left_table]['alias'])) {
533 $this->ensure_table($join->left_table, $relationship);
536 // First, if this is our link point/anchor table, just use the relationship
537 if ($join->left_table == $this->relationships[$relationship]['table']) {
538 $join->left_table = $relationship;
540 // then, try the base alias.
541 else if (isset($this->tables[$relationship][$join->left_table]['alias'])) {
542 $join->left_table = $this->tables[$relationship][$join->left_table]['alias'];
544 // But if we're already looking at an alias, use that instead.
545 else if (isset($this->table_queue[$relationship]['alias'])) {
546 $join->left_table = $this->table_queue[$relationship]['alias'];
550 $join->adjusted = TRUE;
555 * Retrieve join data from the larger join data cache.
558 * The table to get the join information for.
560 * The path we're following to get this join.
563 * A views_join object or child object, if one exists.
565 function get_join_data($table, $base_table) {
566 // Check to see if we're linking to a known alias. If so, get the real
567 // table's data instead.
568 if (!empty($this->table_queue[$table])) {
569 $table = $this->table_queue[$table]['table'];
571 return views_get_table_join($table, $base_table);
576 * Get the information associated with a table.
578 * If you need the alias of a table with a particular relationship, use
581 function get_table_info($table) {
582 if (!empty($this->table_queue[$table])) {
583 return $this->table_queue[$table];
586 // In rare cases we might *only* have aliased versions of the table.
587 if (!empty($this->tables[$this->base_table][$table])) {
588 $alias = $this->tables[$this->base_table][$table]['alias'];
589 if (!empty($this->table_queue[$alias])) {
590 return $this->table_queue[$alias];
596 * Add a field to the query table, possibly with an alias. This will
597 * automatically call ensure_table to make sure the required table
598 * exists, *unless* $table is unset.
601 * The table this field is attached to. If NULL, it is assumed this will
602 * be a formula; otherwise, ensure_table is used to make sure the
605 * The name of the field to add. This may be a real field or a formula.
607 * The alias to create. If not specified, the alias will be $table_$field
608 * unless $table is NULL. When adding formulae, it is recommended that an
612 * The name that this field can be referred to as. Usually this is the alias.
614 function add_field($table, $field, $alias = '', $params = NULL) {
615 // We check for this specifically because it gets a special alias.
616 if ($table == $this->base_table && $field == $this->base_field && empty($alias)) {
617 $alias = $this->base_field;
620 if ($table && empty($this->table_queue[$table])) {
621 $this->ensure_table($table);
624 if (!$alias && $table) {
625 $alias = $table . '_' . $field;
628 $name = $alias ? $alias : $field;
630 // @todo FIXME -- $alias, then $name is inconsistent
631 if (empty($this->fields[$alias])) {
632 $this->fields[$name] = array(
639 foreach ((array)$params as $key => $value) {
640 $this->fields[$name][$key] = $value;
647 * Remove all fields that may've been added; primarily used for summary
648 * mode where we're changing the query because we didn't get data we needed.
650 function clear_fields() {
651 $this->fields = array();
655 * Create a new grouping for the WHERE or HAVING clause.
658 * Either 'AND' or 'OR'. All items within this group will be added
659 * to the WHERE clause with this logical operator.
661 * An ID to use for this group. If unspecified, an ID will be generated.
663 * 'where' or 'having'.
666 * The group ID generated.
668 function set_where_group($type = 'AND', $group = NULL, $where = 'where') {
670 $groups = &$this->$where;
672 if (!isset($group)) {
673 $group = max(array_keys($groups)) + 1;
676 // Create an empty group
677 if (empty($groups[$group])) {
678 $groups[$group] = array('clauses' => array(), 'args' => array());
681 $groups[$group]['type'] = strtoupper($type);
686 * Control how all WHERE and HAVING groups are put together.
689 * Either 'AND' or 'OR'
691 function set_group_operator($type = 'AND') {
692 $this->group_operator = strtoupper($type);
696 * Add a simple WHERE clause to the query. The caller is responsible for
697 * ensuring that all fields are fully qualified (TABLE.FIELD) and that
698 * the table already exists in the query.
701 * The WHERE group to add these to; groups are used to create AND/OR
702 * sections. Groups cannot be nested. Use 0 as the default group.
703 * If the group does not yet exist it will be created as an AND group.
705 * The actual clause to add. When adding a where clause it is important
706 * that all tables are addressed by the alias provided by add_table or
707 * ensure_table and that all fields are addressed by their alias wehn
708 * possible. Please use %d and %s for arguments.
710 * A number of arguments as used in db_query(). May be many args or one
711 * array full of args.
713 function add_where($group, $clause) {
714 $args = func_get_args();
715 array_shift($args); // ditch $group
716 array_shift($args); // ditch $clause
718 // Expand an array of args if it came in.
719 if (count($args) == 1 && is_array(reset($args))) {
720 $args = current($args);
723 // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
724 // the default group.
729 // Check for a group.
730 if (!isset($this->where[$group])) {
731 $this->set_where_group('AND', $group);
734 // Add the clause and the args.
735 if (is_array($args)) {
736 $this->where[$group]['clauses'][] = $clause;
737 // we use array_values() here to prevent array_merge errors as keys from multiple
738 // sources occasionally collide.
739 $this->where[$group]['args'] = array_merge($this->where[$group]['args'], array_values($args));
744 * Add a simple HAVING clause to the query. The caller is responsible for
745 * ensuring that all fields are fully qualified (TABLE.FIELD) and that
746 * the table and an appropriate GROUP BY already exist in the query.
749 * The HAVING group to add these to; groups are used to create AND/OR
750 * sections. Groups cannot be nested. Use 0 as the default group.
751 * If the group does not yet exist it will be created as an AND group.
753 * The actual clause to add. When adding a having clause it is important
754 * that all tables are addressed by the alias provided by add_table or
755 * ensure_table and that all fields are addressed by their alias wehn
756 * possible. Please use %d and %s for arguments.
758 * A number of arguments as used in db_query(). May be many args or one
759 * array full of args.
761 function add_having($group, $clause) {
762 $args = func_get_args();
763 array_shift($args); // ditch $group
764 array_shift($args); // ditch $clause
766 // Expand an array of args if it came in.
767 if (count($args) == 1 && is_array(reset($args))) {
768 $args = current($args);
771 // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
772 // the default group.
777 // Check for a group.
778 if (!isset($this->having[$group])) {
779 $this->set_where_group('AND', $group, 'having');
782 // Add the clause and the args.
783 if (is_array($args)) {
784 $this->having[$group]['clauses'][] = $clause;
785 $this->having[$group]['args'] = array_merge($this->having[$group]['args'], array_values($args));
790 * Add an ORDER BY clause to the query.
793 * The table this field is part of. If a formula, enter NULL.
795 * The field or formula to sort on. If already a field, enter NULL
796 * and put in the alias.
798 * Either ASC or DESC.
800 * The alias to add the field as. In SQL, all fields in the order by
801 * must also be in the SELECT portion. If an $alias isn't specified
802 * one will be generated for from the $field; however, if the
803 * $field is a formula, this alias will likely fail.
805 function add_orderby($table, $field, $order, $alias = '') {
807 $this->ensure_table($table);
810 // Only fill out this aliasing if there is a table;
811 // otherwise we assume it is a formula.
812 if (!$alias && $table) {
813 $as = $table . '_' . $field;
820 $this->add_field($table, $field, $as);
823 $this->orderby[] = "$as " . strtoupper($order);
825 // If grouping, all items in the order by must also be in the
826 // group by clause. Check $table to ensure that this is not a
828 if ($this->groupby && $table) {
829 $this->add_groupby($as);
834 * Add a simple GROUP BY clause to the query. The caller is responsible
835 * for ensuring that the fields are fully qualified and the table is properly
838 function add_groupby($clause) {
839 // Only add it if it's not already in there.
840 if (!in_array($clause, $this->groupby)) {
841 $this->groupby[] = $clause;
846 * Construct the "WHERE" or "HAVING" part of the query.
849 * 'where' or 'having'.
851 function condition_sql($where = 'where') {
853 foreach ($this->$where as $group => $info) {
854 $clause = implode(") " . $info['type'] . " (", $info['clauses']);
855 if (count($info['clauses']) > 1) {
856 $clause = '(' . $clause . ')';
858 $clauses[] = $clause;
862 $keyword = drupal_strtoupper($where);
863 if (count($clauses) > 1) {
864 return "$keyword (" . implode(")\n " . $this->group_operator . ' (', $clauses) . ")\n";
867 return "$keyword " . array_shift($clauses) . "\n";
874 * Generate a query and a countquery from all of the information supplied
878 * Provide a countquery if this is true, otherwise provide a normal query.
880 function query($get_count = FALSE) {
881 // Check query distinct value.
882 if (empty($this->no_distinct) && $this->distinct && !empty($this->fields)) {
883 if (!empty($this->fields[$this->base_field])) {
884 $this->fields[$this->base_field]['distinct'] = TRUE;
889 * An optimized count query includes just the base field instead of all the fields.
890 * Determine of this query qualifies by checking for a groupby or distinct.
892 $fields_array = $this->fields;
893 if ($get_count && !$this->groupby) {
894 foreach ($fields_array as $field) {
895 if (!empty($field['distinct'])) {
896 $get_count_optimized = FALSE;
902 $get_count_optimized = FALSE;
904 if (!isset($get_count_optimized)) {
905 $get_count_optimized = TRUE;
908 $joins = $fields = $where = $having = $orderby = $groupby = '';
909 // Add all the tables to the query via joins. We assume all LEFT joins.
910 foreach ($this->table_queue as $table) {
911 if (is_object($table['join'])) {
912 $joins .= $table['join']->join($table, $this) . "\n";
916 $has_aggregate = FALSE;
917 $non_aggregates = array();
919 foreach ($fields_array as $field) {
924 if (!empty($field['table'])) {
925 $string .= $field['table'] . '.';
927 $string .= $field['field'];
929 // store for use with non-aggregates below
930 $fieldname = (!empty($field['alias']) ? $field['alias'] : $string);
932 if (!empty($field['distinct'])) {
933 $string = "DISTINCT($string)";
935 if (!empty($field['count'])) {
936 $string = "COUNT($string)";
937 $has_aggregate = TRUE;
939 else if (!empty($field['aggregate'])) {
940 $has_aggregate = TRUE;
943 $non_aggregates[] = $fieldname;
945 if ($field['alias']) {
946 $string .= " AS $field[alias]";
950 if ($get_count_optimized) {
951 // We only want the first field in this case.
956 if ($has_aggregate || $this->groupby) {
957 $groupby = "GROUP BY " . implode(', ', array_unique(array_merge($this->groupby, $non_aggregates))) . "\n";
959 $having = $this->condition_sql('having');
963 if (!$get_count_optimized) {
964 // we only add the groupby if we're not counting.
965 if ($this->orderby) {
966 $orderby = "ORDER BY " . implode(', ', $this->orderby) . "\n";
970 $where = $this->condition_sql();
972 $query = "SELECT $fields\n FROM {" . $this->base_table . "} $this->base_table \n$joins $where $groupby $having $orderby";
974 $replace = array('>' => '>', '<' => '<');
975 $query = strtr($query, $replace);
981 * Get the arguments attached to the WHERE and HAVING clauses of this query.
983 function get_where_args() {
985 foreach ($this->where as $group => $where) {
986 $args = array_merge($args, $where['args']);
988 foreach ($this->having as $group => $having) {
989 $args = array_merge($args, $having['args']);