/[drupal]/contributions/modules/views/includes/query.inc
ViewVC logotype

Contents of /contributions/modules/views/includes/query.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.50 - (show annotations) (download) (as text)
Mon Oct 5 22:49:03 2009 UTC (7 weeks, 4 days ago) by merlinofchaos
Branch: MAIN
CVS Tags: DRUPAL-6--2-7, HEAD
Branch point for: DRUPAL-6--2, DRUPAL-7--3
Changes since 1.49: +1 -2 lines
File MIME type: text/x-php
Oops, committed debugging message.
1 <?php
2 // $Id: query.inc,v 1.49 2009/10/05 22:31:09 merlinofchaos Exp $
3 /**
4 * @file query.inc
5 * Defines the query object which is the underlying layer in a View.
6 */
7
8 /**
9 * Object used to create a SELECT query.
10 */
11 class views_query {
12
13 /**
14 * A list of tables in the order they should be added, keyed by alias.
15 */
16 var $table_queue = array();
17
18 /**
19 * Holds an array of tables and counts added so that we can create aliases
20 */
21 var $tables = array();
22
23 /**
24 * Holds an array of relationships, which are aliases of the primary
25 * table that represent different ways to join the same table in.
26 */
27 var $relationships = array();
28
29 /**
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
32 * or OR.
33 */
34 var $where = array();
35 /**
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
38 * or OR.
39 */
40 var $having = array();
41 /**
42 * The default operator to use when connecting the WHERE groups. May be
43 * AND or OR.
44 */
45 var $group_operator = 'AND';
46
47 /**
48 * A simple array of order by clauses.
49 */
50 var $orderby = array();
51
52 /**
53 * A simple array of group by clauses.
54 */
55 var $groupby = array();
56
57 /**
58 * The table header to use for tablesort. This matters because tablesort
59 * needs to modify the query and needs the header.
60 */
61 var $header = array();
62
63 /**
64 * A flag as to whether or not to make the primary field distinct.
65 */
66 var $distinct = FALSE;
67
68 /**
69 * Constructor; Create the basic query object and fill with default values.
70 */
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(
75 'link' => NULL,
76 'table' => $base_table,
77 'alias' => $base_table,
78 'base' => $base_table
79 );
80
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,
86 'join' => NULL,
87 );
88
89 // init the tables with our primary table
90 $this->tables[$base_table][$base_table] = array(
91 'count' => 1,
92 'alias' => $base_table,
93 );
94
95 if ($base_field) {
96 $this->fields[$base_field] = array(
97 'table' => $base_table,
98 'field' => $base_field,
99 'alias' => $base_field,
100 );
101 }
102
103 $this->count_field = array(
104 'table' => $base_table,
105 'field' => $base_field,
106 'alias' => $base_field,
107 'count' => TRUE,
108 );
109 }
110
111 // ----------------------------------------------------------------
112 // Utility methods to set flags and data.
113
114 /**
115 * Set the base field to be distinct.
116 */
117 function set_distinct($value = TRUE) {
118 if (!(isset($this->no_distinct) && $value)) {
119 // Hack in a check to see if node_access is going to add its own DISTINCT
120 // and break this query.
121 // See http://drupal.org/node/284392 for the core bug.
122 //
123 // We want to bypass views distinct code if this bug will be triggered so
124 // if the base table is node and there is a node access module, bypass
125 // views distinct code.
126 if ($this->base_table != 'node' || !node_access_view_all_nodes()) {
127 $this->distinct = FALSE;
128 }
129 else {
130 $this->distinct = $value;
131 }
132 }
133 }
134
135 /**
136 * Set what field the query will count() on for paging.
137 */
138 function set_count_field($table, $field, $alias = NULL) {
139 if (empty($alias)) {
140 $alias = $table . '_' . $field;
141 }
142 $this->count_field = array(
143 'table' => $table,
144 'field' => $field,
145 'alias' => $alias,
146 'count' => TRUE,
147 );
148 }
149
150 /**
151 * Set the table header; used for click-sorting because it's needed
152 * info to modify the ORDER BY clause.
153 */
154 function set_header($header) {
155 $this->header = $header;
156 }
157
158 // ----------------------------------------------------------------
159 // Table/join adding
160
161 /**
162 * A relationship is an alternative endpoint to a series of table
163 * joins. Relationships must be aliases of the primary table and
164 * they must join either to the primary table or to a pre-existing
165 * relationship.
166 *
167 * An example of a relationship would be a nodereference table.
168 * If you have a nodereference named 'book_parent' which links to a
169 * parent node, you could set up a relationship 'node_book_parent'
170 * to 'node'. Then, anything that links to 'node' can link to
171 * 'node_book_parent' instead, thus allowing all properties of
172 * both nodes to be available in the query.
173 *
174 * @param $alias
175 * What this relationship will be called, and is also the alias
176 * for the table.
177 * @param $join
178 * A views_join object (or derived object) to join the alias in.
179 * @param $base
180 * The name of the 'base' table this relationship represents; this
181 * tells the join search which path to attempt to use when finding
182 * the path to this relationship.
183 * @param $link_point
184 * If this relationship links to something other than the primary
185 * table, specify that table here. For example, a 'track' node
186 * might have a relationship to an 'album' node, which might
187 * have a relationship to an 'artist' node.
188 */
189 function add_relationship($alias, $join, $base, $link_point = NULL) {
190 if (empty($link_point)) {
191 $link_point = $this->base_table;
192 }
193 else if (!array_key_exists($link_point, $this->relationships)) {
194 return FALSE;
195 }
196
197 // Make sure $alias isn't already used; if it, start adding stuff.
198 $alias_base = $alias;
199 $count = 1;
200 while (!empty($this->relationships[$alias])) {
201 $alias = $alias_base . '_' . $count++;
202 }
203
204 // Make sure this join is adjusted for our relationship.
205 if ($link_point && isset($this->relationships[$link_point])) {
206 $join = $this->adjust_join($join, $link_point);
207 }
208
209 // Add the table directly to the queue to avoid accidentally marking
210 // it.
211 $this->table_queue[$alias] = array(
212 'table' => $join->table,
213 'num' => 1,
214 'alias' => $alias,
215 'join' => $join,
216 'relationship' => $link_point,
217 );
218
219 $this->relationships[$alias] = array(
220 'link' => $link_point,
221 'table' => $join->table,
222 'base' => $base,
223 );
224 return $alias;
225 }
226
227 /**
228 * Add a table to the query, ensuring the path exists.
229 *
230 * This function will test to ensure that the path back to the primary
231 * table is valid and exists; if you do not wish for this testing to
232 * occur, use $query->queue_table() instead.
233 *
234 * @param $table
235 * The name of the table to add. It needs to exist in the global table
236 * array.
237 * @param $relationship
238 * An alias of a table; if this is set, the path back to this table will
239 * be tested prior to adding the table, making sure that all intermediary
240 * tables exist and are properly aliased. If set to NULL the path to
241 * the primary table will be ensured. If the path cannot be made, the
242 * table will NOT be added.
243 * @param $join
244 * In some join configurations this table may actually join back through
245 * a different method; this is most likely to be used when tracing
246 * a hierarchy path. (node->parent->parent2->parent3). This parameter
247 * will specify how this table joins if it is not the default.
248 * @param $alias
249 * A specific alias to use, rather than the default alias.
250 *
251 * @return $alias
252 * The alias of the table; this alias can be used to access information
253 * about the table and should always be used to refer to the table when
254 * adding parts to the query. Or FALSE if the table was not able to be
255 * added.
256 */
257 function add_table($table, $relationship = NULL, $join = NULL, $alias = NULL) {
258 if (!$this->ensure_path($table, $relationship, $join)) {
259 return FALSE;
260 }
261
262 if (!$join) {
263 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
264 }
265
266 $join = $this->adjust_join($join, $relationship);
267
268 return $this->queue_table($table, $relationship, $join, $alias);
269 }
270
271 /**
272 * Add a table to the query, without ensuring the path.
273 *
274 * This function will test to ensure that the path back to the primary
275 * table is valid and exists; if you do not wish for this testing to
276 * occur, use $query->queue_table() instead.
277 *
278 * @param $table
279 * The name of the table to add. It needs to exist in the global table
280 * array.
281 * @param $relationship
282 * The primary table alias this table is related to. If not set, the
283 * primary table will be used.
284 * @param $join
285 * In some join configurations this table may actually join back through
286 * a different method; this is most likely to be used when tracing
287 * a hierarchy path. (node->parent->parent2->parent3). This parameter
288 * will specify how this table joins if it is not the default.
289 * @param $alias
290 * A specific alias to use, rather than the default alias.
291 *
292 * @return $alias
293 * The alias of the table; this alias can be used to access information
294 * about the table and should always be used to refer to the table when
295 * adding parts to the query. Or FALSE if the table was not able to be
296 * added.
297 */
298 function queue_table($table, $relationship = NULL, $join = NULL, $alias = NULL) {
299 // If the alias is set, make sure it doesn't already exist.
300 if (isset($this->table_queue[$alias])) {
301 return $alias;
302 }
303
304 if (empty($relationship)) {
305 $relationship = $this->base_table;
306 }
307
308 if (!array_key_exists($relationship, $this->relationships)) {
309 return FALSE;
310 }
311
312 if (!$alias && $join && $relationship && !empty($join->adjusted) && $table != $join->table) {
313 if ($relationship == $this->base_table) {
314 $alias = $table;
315 }
316 else {
317 $alias = $relationship . '_' . $table;
318 }
319 }
320
321 // Check this again to make sure we don't blow up existing aliases for already
322 // adjusted joins.
323 if (isset($this->table_queue[$alias])) {
324 return $alias;
325 }
326
327 $alias = $this->mark_table($table, $relationship, $alias);
328
329 // If no alias is specified, give it the default.
330 if (!isset($alias)) {
331 $alias = $this->tables[$relationship][$table]['alias'] . $this->tables[$relationship][$table]['count'];
332 }
333
334 // If this is a relationship based table, add a marker with
335 // the relationship as a primary table for the alias.
336 if ($table != $alias) {
337 $this->mark_table($alias, $this->base_table, $alias);
338 }
339
340 // If no join is specified, pull it from the table data.
341 if (!isset($join)) {
342 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
343 if (empty($join)) {
344 return FALSE;
345 }
346
347 $join = $this->adjust_join($join, $relationship);
348 }
349
350 $this->table_queue[$alias] = array(
351 'table' => $table,
352 'num' => $this->tables[$relationship][$table]['count'],
353 'alias' => $alias,
354 'join' => $join,
355 'relationship' => $relationship,
356 );
357
358 return $alias;
359 }
360
361 function mark_table($table, $relationship, $alias) {
362 // Mark that this table has been added.
363 if (empty($this->tables[$relationship][$table])) {
364 if (!isset($alias)) {
365 $alias = '';
366 if ($relationship != $this->base_table) {
367 // double underscore will help prevent accidental name
368 // space collisions.
369 $alias = $relationship . '__';
370 }
371 $alias .= $table;
372 }
373 $this->tables[$relationship][$table] = array(
374 'count' => 1,
375 'alias' => $alias,
376 );
377 }
378 else {
379 $this->tables[$relationship][$table]['count']++;
380 }
381
382 return $alias;
383 }
384
385 /**
386 * Ensure a table exists in the queue; if it already exists it won't
387 * do anything, but if it doesn't it will add the table queue. It will ensure
388 * a path leads back to the relationship table.
389 *
390 * @param $table
391 * The unaliased name of the table to ensure.
392 * @param $relationship
393 * The relationship to ensure the table links to. Each relationship will
394 * get a unique instance of the table being added. If not specified,
395 * will be the primary table.
396 * @param $join
397 * A views_join object (or derived object) to join the alias in.
398 *
399 * @return
400 * The alias used to refer to this specific table, or NULL if the table
401 * cannot be ensured.
402 */
403 function ensure_table($table, $relationship = NULL, $join = NULL) {
404 // ensure a relationship
405 if (empty($relationship)) {
406 $relationship = $this->base_table;
407 }
408
409 // If the relationship is the primary table, this actually be a relationship
410 // link back from an alias. We store all aliases along with the primary table
411 // to detect this state, because eventually it'll hit a table we already
412 // have and that's when we want to stop.
413 if ($relationship == $this->base_table && !empty($this->tables[$relationship][$table])) {
414 return $this->tables[$relationship][$table]['alias'];
415 }
416
417 if (!array_key_exists($relationship, $this->relationships)) {
418 return FALSE;
419 }
420
421 if ($table == $this->relationships[$relationship]['base']) {
422 return $relationship;
423 }
424
425 // If we do not have join info, fetch it.
426 if (!isset($join)) {
427 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
428 }
429
430 // If it can't be fetched, this won't work.
431 if (empty($join)) {
432 return;
433 }
434
435 // Adjust this join for the relationship, which will ensure that the 'base'
436 // table it links to is correct. Tables adjoined to a relationship
437 // join to a link point, not the base table.
438 $join = $this->adjust_join($join, $relationship);
439
440 if ($this->ensure_path($table, $relationship, $join)) {
441 // Attempt to eliminate redundant joins. If this table's
442 // relationship and join exactly matches an existing table's
443 // relationship and join, we do not have to join to it again;
444 // just return the existing table's alias. See
445 // http://groups.drupal.org/node/11288 for details.
446 //
447 // This can be done safely here but not lower down in
448 // queue_table(), because queue_table() is also used by
449 // add_table() which requires the ability to intentionally add
450 // the same table with the same join multiple times. For
451 // example, a view that filters on 3 taxonomy terms using AND
452 // needs to join term_data 3 times with the same join.
453
454 // scan through the table queue to see if a matching join and
455 // relationship exists. If so, use it instead of this join.
456
457 // TODO: Scanning through $this->table_queue results in an
458 // O(N^2) algorithm, and this code runs every time the view is
459 // instantiated (Views 2 does not currently cache queries).
460 // There are a couple possible "improvements" but we should do
461 // some performance testing before picking one.
462 foreach ($this->table_queue as $queued_table) {
463 // In PHP 4 and 5, the == operation returns TRUE for two objects
464 // if they are instances of the same class and have the same
465 // attributes and values.
466 if ($queued_table['relationship'] == $relationship && $queued_table['join'] == $join) {
467 return $queued_table['alias'];
468 }
469 }
470
471 return $this->queue_table($table, $relationship, $join);
472 }
473 }
474
475 /**
476 * Make sure that the specified table can be properly linked to the primary
477 * table in the JOINs. This function uses recursion. If the tables
478 * needed to complete the path back to the primary table are not in the
479 * query they will be added, but additional copies will NOT be added
480 * if the table is already there.
481 */
482 function ensure_path($table, $relationship = NULL, $join = NULL, $traced = array(), $add = array()) {
483 if (!isset($relationship)) {
484 $relationship = $this->base_table;
485 }
486
487 if (!array_key_exists($relationship, $this->relationships)) {
488 return FALSE;
489 }
490
491 // If we do not have join info, fetch it.
492 if (!isset($join)) {
493 $join = $this->get_join_data($table, $this->relationships[$relationship]['base']);
494 }
495
496 // If it can't be fetched, this won't work.
497 if (empty($join)) {
498 return FALSE;
499 }
500
501 // Does a table along this path exist?
502 if (isset($this->tables[$relationship][$table]) ||
503 ($join && $join->left_table == $relationship) ||
504 ($join && $join->left_table == $this->relationships[$relationship]['table'])) {
505
506 // Make sure that we're linking to the correct table for our relationship.
507 foreach (array_reverse($add) as $table => $path_join) {
508 $this->queue_table($table, $relationship, $this->adjust_join($path_join, $relationship));
509 }
510 return TRUE;
511 }
512
513 // Have we been this way?
514 if (isset($traced[$join->left_table])) {
515 // we looped. Broked.
516 return FALSE;
517 }
518
519 // Do we have to add this table?
520 $left_join = $this->get_join_data($join->left_table, $this->relationships[$relationship]['base']);
521 if (!isset($this->tables[$relationship][$join->left_table])) {
522 $add[$join->left_table] = $left_join;
523 }
524
525 // Keep looking.
526 $traced[$join->left_table] = TRUE;
527 return $this->ensure_path($join->left_table, $relationship, $left_join, $traced, $add);
528 }
529
530 /**
531 * Fix a join to adhere to the proper relationship; the left table can vary
532 * based upon what relationship items are joined in on.
533 */
534 function adjust_join($join, $relationship) {
535 if (!empty($join->adjusted)) {
536 return $join;
537 }
538
539 if (empty($relationship) || empty($this->relationships[$relationship])) {
540 return $join;
541 }
542
543 // Adjusts the left table for our relationship.
544 if ($relationship != $this->base_table) {
545 // If we're linking to the primary table, the relationship to use will
546 // be the prior relationship. Unless it's a direct link.
547
548 // Safety! Don't modify an original here.
549 $join = drupal_clone($join);
550
551 // Do we need to try to ensure a path?
552 if ($join->left_table != $this->relationships[$relationship]['table'] &&
553 $join->left_table != $this->relationships[$relationship]['base'] &&
554 !isset($this->tables[$relationship][$join->left_table]['alias'])) {
555 $this->ensure_table($join->left_table, $relationship);
556 }
557
558 // First, if this is our link point/anchor table, just use the relationship
559 if ($join->left_table == $this->relationships[$relationship]['table']) {
560 $join->left_table = $relationship;
561 }
562 // then, try the base alias.
563 else if (isset($this->tables[$relationship][$join->left_table]['alias'])) {
564 $join->left_table = $this->tables[$relationship][$join->left_table]['alias'];
565 }
566 // But if we're already looking at an alias, use that instead.
567 else if (isset($this->table_queue[$relationship]['alias'])) {
568 $join->left_table = $this->table_queue[$relationship]['alias'];
569 }
570 }
571
572 $join->adjusted = TRUE;
573 return $join;
574 }
575
576 /**
577 * Retrieve join data from the larger join data cache.
578 *
579 * @param $table
580 * The table to get the join information for.
581 * @param $base_table
582 * The path we're following to get this join.
583 *
584 * @return
585 * A views_join object or child object, if one exists.
586 */
587 function get_join_data($table, $base_table) {
588 // Check to see if we're linking to a known alias. If so, get the real
589 // table's data instead.
590 if (!empty($this->table_queue[$table])) {
591 $table = $this->table_queue[$table]['table'];
592 }
593 return views_get_table_join($table, $base_table);
594
595 }
596
597 /**
598 * Get the information associated with a table.
599 *
600 * If you need the alias of a table with a particular relationship, use
601 * ensure_table().
602 */
603 function get_table_info($table) {
604 if (!empty($this->table_queue[$table])) {
605 return $this->table_queue[$table];
606 }
607
608 // In rare cases we might *only* have aliased versions of the table.
609 if (!empty($this->tables[$this->base_table][$table])) {
610 $alias = $this->tables[$this->base_table][$table]['alias'];
611 if (!empty($this->table_queue[$alias])) {
612 return $this->table_queue[$alias];
613 }
614 }
615 }
616
617 /**
618 * Add a field to the query table, possibly with an alias. This will
619 * automatically call ensure_table to make sure the required table
620 * exists, *unless* $table is unset.
621 *
622 * @param $table
623 * The table this field is attached to. If NULL, it is assumed this will
624 * be a formula; otherwise, ensure_table is used to make sure the
625 * table exists.
626 * @param $field
627 * The name of the field to add. This may be a real field or a formula.
628 * @param $alias
629 * The alias to create. If not specified, the alias will be $table_$field
630 * unless $table is NULL. When adding formulae, it is recommended that an
631 * alias be used.
632 *
633 * @return $name
634 * The name that this field can be referred to as. Usually this is the alias.
635 */
636 function add_field($table, $field, $alias = '', $params = NULL) {
637 // We check for this specifically because it gets a special alias.
638 if ($table == $this->base_table && $field == $this->base_field && empty($alias)) {
639 $alias = $this->base_field;
640 }
641
642 if ($table && empty($this->table_queue[$table])) {
643 $this->ensure_table($table);
644 }
645
646 if (!$alias && $table) {
647 $alias = $table . '_' . $field;
648 }
649
650 $name = $alias ? $alias : $field;
651
652 // @todo FIXME -- $alias, then $name is inconsistent
653 if (empty($this->fields[$alias])) {
654 $this->fields[$name] = array(
655 'field' => $field,
656 'table' => $table,
657 'alias' => $alias,
658 );
659 }
660
661 foreach ((array)$params as $key => $value) {
662 $this->fields[$name][$key] = $value;
663 }
664
665 return $name;
666 }
667
668 /**
669 * Remove all fields that may've been added; primarily used for summary
670 * mode where we're changing the query because we didn't get data we needed.
671 */
672 function clear_fields() {
673 $this->fields = array();
674 }
675
676 /**
677 * Create a new grouping for the WHERE or HAVING clause.
678 *
679 * @param $type
680 * Either 'AND' or 'OR'. All items within this group will be added
681 * to the WHERE clause with this logical operator.
682 * @param $group
683 * An ID to use for this group. If unspecified, an ID will be generated.
684 * @param $where
685 * 'where' or 'having'.
686 *
687 * @return $group
688 * The group ID generated.
689 */
690 function set_where_group($type = 'AND', $group = NULL, $where = 'where') {
691 // Set an alias.
692 $groups = &$this->$where;
693
694 if (!isset($group)) {
695 $group = empty($groups) ? 1 : max(array_keys($groups)) + 1;
696 }
697
698 // Create an empty group
699 if (empty($groups[$group])) {
700 $groups[$group] = array('clauses' => array(), 'args' => array());
701 }
702
703 $groups[$group]['type'] = strtoupper($type);
704 return $group;
705 }
706
707 /**
708 * Control how all WHERE and HAVING groups are put together.
709 *
710 * @param $type
711 * Either 'AND' or 'OR'
712 */
713 function set_group_operator($type = 'AND') {
714 $this->group_operator = strtoupper($type);
715 }
716
717 /**
718 * Add a simple WHERE clause to the query. The caller is responsible for
719 * ensuring that all fields are fully qualified (TABLE.FIELD) and that
720 * the table already exists in the query.
721 *
722 * @param $group
723 * The WHERE group to add these to; groups are used to create AND/OR
724 * sections. Groups cannot be nested. Use 0 as the default group.
725 * If the group does not yet exist it will be created as an AND group.
726 * @param $clause
727 * The actual clause to add. When adding a where clause it is important
728 * that all tables are addressed by the alias provided by add_table or
729 * ensure_table and that all fields are addressed by their alias wehn
730 * possible. Please use %d and %s for arguments.
731 * @param ...
732 * A number of arguments as used in db_query(). May be many args or one
733 * array full of args.
734 */
735 function add_where($group, $clause) {
736 $args = func_get_args();
737 array_shift($args); // ditch $group
738 array_shift($args); // ditch $clause
739
740 // Expand an array of args if it came in.
741 if (count($args) == 1 && is_array(reset($args))) {
742 $args = current($args);
743 }
744
745 // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
746 // the default group.
747 if (empty($group)) {
748 $group = 0;
749 }
750
751 // Check for a group.
752 if (!isset($this->where[$group])) {
753 $this->set_where_group('AND', $group);
754 }
755
756 // Add the clause and the args.
757 if (is_array($args)) {
758 $this->where[$group]['clauses'][] = $clause;
759 // we use array_values() here to prevent array_merge errors as keys from multiple
760 // sources occasionally collide.
761 $this->where[$group]['args'] = array_merge($this->where[$group]['args'], array_values($args));
762 }
763 }
764
765 /**
766 * Add a simple HAVING clause to the query. The caller is responsible for
767 * ensuring that all fields are fully qualified (TABLE.FIELD) and that
768 * the table and an appropriate GROUP BY already exist in the query.
769 *
770 * @param $group
771 * The HAVING group to add these to; groups are used to create AND/OR
772 * sections. Groups cannot be nested. Use 0 as the default group.
773 * If the group does not yet exist it will be created as an AND group.
774 * @param $clause
775 * The actual clause to add. When adding a having clause it is important
776 * that all tables are addressed by the alias provided by add_table or
777 * ensure_table and that all fields are addressed by their alias wehn
778 * possible. Please use %d and %s for arguments.
779 * @param ...
780 * A number of arguments as used in db_query(). May be many args or one
781 * array full of args.
782 */
783 function add_having($group, $clause) {
784 $args = func_get_args();
785 array_shift($args); // ditch $group
786 array_shift($args); // ditch $clause
787
788 // Expand an array of args if it came in.
789 if (count($args) == 1 && is_array(reset($args))) {
790 $args = current($args);
791 }
792
793 // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
794 // the default group.
795 if (empty($group)) {
796 $group = 0;
797 }
798
799 // Check for a group.
800 if (!isset($this->having[$group])) {
801 $this->set_where_group('AND', $group, 'having');
802 }
803
804 // Add the clause and the args.
805 if (is_array($args)) {
806 $this->having[$group]['clauses'][] = $clause;
807 $this->having[$group]['args'] = array_merge($this->having[$group]['args'], array_values($args));
808 }
809 }
810
811 /**
812 * Add an ORDER BY clause to the query.
813 *
814 * @param $table
815 * The table this field is part of. If a formula, enter NULL.
816 * @param $field
817 * The field or formula to sort on. If already a field, enter NULL
818 * and put in the alias.
819 * @param $order
820 * Either ASC or DESC.
821 * @param $alias
822 * The alias to add the field as. In SQL, all fields in the order by
823 * must also be in the SELECT portion. If an $alias isn't specified
824 * one will be generated for from the $field; however, if the
825 * $field is a formula, this alias will likely fail.
826 */
827 function add_orderby($table, $field, $order, $alias = '') {
828 if ($table) {
829 $this->ensure_table($table);
830 }
831
832 // Only fill out this aliasing if there is a table;
833 // otherwise we assume it is a formula.
834 if (!$alias && $table) {
835 $as = $table . '_' . $field;
836 }
837 else {
838 $as = $alias;
839 }
840
841 if ($field) {
842 $this->add_field($table, $field, $as);
843 }
844
845 $this->orderby[] = "$as " . strtoupper($order);
846
847 // If grouping, all items in the order by must also be in the
848 // group by clause. Check $table to ensure that this is not a
849 // formula.
850 if ($this->groupby && $table) {
851 $this->add_groupby($as);
852 }
853 }
854
855 /**
856 * Add a simple GROUP BY clause to the query. The caller is responsible
857 * for ensuring that the fields are fully qualified and the table is properly
858 * added.
859 */
860 function add_groupby($clause) {
861 // Only add it if it's not already in there.
862 if (!in_array($clause, $this->groupby)) {
863 $this->groupby[] = $clause;
864 }
865 }
866
867 /**
868 * Construct the "WHERE" or "HAVING" part of the query.
869 *
870 * @param $where
871 * 'where' or 'having'.
872 */
873 function condition_sql($where = 'where') {
874 $clauses = array();
875 foreach ($this->$where as $group => $info) {
876 $clause = implode(") " . $info['type'] . " (", $info['clauses']);
877 if (count($info['clauses']) > 1) {
878 $clause = '(' . $clause . ')';
879 }
880 $clauses[] = $clause;
881 }
882
883 if ($clauses) {
884 $keyword = drupal_strtoupper($where);
885 if (count($clauses) > 1) {
886 return "$keyword (" . implode(")\n " . $this->group_operator . ' (', $clauses) . ")\n";
887 }
888 else {
889 return "$keyword " . array_shift($clauses) . "\n";
890 }
891 }
892 return "";
893 }
894
895 /**
896 * Generate a query and a countquery from all of the information supplied
897 * to the object.
898 *
899 * @param $get_count
900 * Provide a countquery if this is true, otherwise provide a normal query.
901 */
902 function query($get_count = FALSE) {
903 // Check query distinct value.
904 if (empty($this->no_distinct) && $this->distinct && !empty($this->fields)) {
905 if (!empty($this->fields[$this->base_field])) {
906 $this->fields[$this->base_field]['distinct'] = TRUE;
907 $this->add_groupby($this->base_field);
908 }
909 }
910
911 /**
912 * An optimized count query includes just the base field instead of all the fields.
913 * Determine of this query qualifies by checking for a groupby or distinct.
914 */
915 $fields_array = $this->fields;
916 if ($get_count && !$this->groupby) {
917 foreach ($fields_array as $field) {
918 if (!empty($field['distinct'])) {
919 $get_count_optimized = FALSE;
920 break;
921 }
922 }
923 }
924 else {
925 $get_count_optimized = FALSE;
926 }
927 if (!isset($get_count_optimized)) {
928 $get_count_optimized = TRUE;
929 }
930
931 $joins = $fields = $where = $having = $orderby = $groupby = '';
932 // Add all the tables to the query via joins. We assume all LEFT joins.
933 foreach ($this->table_queue as $table) {
934 if (is_object($table['join'])) {
935 $joins .= $table['join']->join($table, $this) . "\n";
936 }
937 }
938
939 $has_aggregate = FALSE;
940 $non_aggregates = array();
941
942 foreach ($fields_array as $field) {
943 if ($fields) {
944 $fields .= ",\n ";
945 }
946 $string = '';
947 if (!empty($field['table'])) {
948 $string .= $field['table'] . '.';
949 }
950 $string .= $field['field'];
951
952 // store for use with non-aggregates below
953 $fieldname = (!empty($field['alias']) ? $field['alias'] : $string);
954
955 if (!empty($field['distinct'])) {
956 $string = "DISTINCT($string)";
957 }
958 if (!empty($field['count'])) {
959 $string = "COUNT($string)";
960 $has_aggregate = TRUE;
961 }
962 else if (!empty($field['aggregate'])) {
963 $has_aggregate = TRUE;
964 }
965 elseif ($this->distinct && !in_array($fieldname, $this->groupby)) {
966 $string = $GLOBALS['db_type'] == 'pgsql' ? "FIRST($string)" : $string;
967 }
968 else {
969 $non_aggregates[] = $fieldname;
970 }
971 if ($field['alias']) {
972 $string .= " AS $field[alias]";
973 }
974 $fields .= $string;
975
976 if ($get_count_optimized) {
977 // We only want the first field in this case.
978 break;
979 }
980 }
981
982 if ($has_aggregate || $this->groupby) {
983 $groupby = "GROUP BY " . implode(', ', array_unique(array_merge($this->groupby, $non_aggregates))) . "\n";
984 if ($this->having) {
985 $having = $this->condition_sql('having');
986 }
987 }
988
989 if (!$get_count_optimized) {
990 // we only add the groupby if we're not counting.
991 if ($this->orderby) {
992 $orderby = "ORDER BY " . implode(', ', $this->orderby) . "\n";
993 }
994 }
995
996 $where = $this->condition_sql();
997
998 $query = "SELECT $fields\n FROM {" . $this->base_table . "} $this->base_table \n$joins $where $groupby $having $orderby";
999
1000 $replace = array('&gt;' => '>', '&lt;' => '<');
1001 $query = strtr($query, $replace);
1002
1003 return $query;
1004 }
1005
1006 /**
1007 * Get the arguments attached to the WHERE and HAVING clauses of this query.
1008 */
1009 function get_where_args() {
1010 $args = array();
1011 foreach ($this->where as $group => $where) {
1012 $args = array_merge($args, $where['args']);
1013 }
1014 foreach ($this->having as $group => $having) {
1015 $args = array_merge($args, $having['args']);
1016 }
1017 return $args;
1018 }
1019 }
1020

  ViewVC Help
Powered by ViewVC 1.1.2