Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Database / Query / Select.php
1 <?php
2
3 namespace Drupal\Core\Database\Query;
4
5 use Drupal\Core\Database\Database;
6 use Drupal\Core\Database\Connection;
7
8 /**
9  * Query builder for SELECT statements.
10  *
11  * @ingroup database
12  */
13 class Select extends Query implements SelectInterface {
14
15   use QueryConditionTrait;
16
17   /**
18    * The fields to SELECT.
19    *
20    * @var array
21    */
22   protected $fields = [];
23
24   /**
25    * The expressions to SELECT as virtual fields.
26    *
27    * @var array
28    */
29   protected $expressions = [];
30
31   /**
32    * The tables against which to JOIN.
33    *
34    * This property is a nested array. Each entry is an array representing
35    * a single table against which to join. The structure of each entry is:
36    *
37    * array(
38    *   'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
39    *   'table' => $table,
40    *   'alias' => $alias_of_the_table,
41    *   'condition' => $join_condition (string or Condition object),
42    *   'arguments' => $array_of_arguments_for_placeholders_in_the condition.
43    *   'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
44    * )
45    *
46    * If $table is a string, it is taken as the name of a table. If it is
47    * a Select query object, it is taken as a subquery.
48    *
49    * If $join_condition is a Condition object, any arguments should be
50    * incorporated into the object; a separate array of arguments does not
51    * need to be provided.
52    *
53    * @var array
54    */
55   protected $tables = [];
56
57   /**
58    * The fields by which to order this query.
59    *
60    * This is an associative array. The keys are the fields to order, and the value
61    * is the direction to order, either ASC or DESC.
62    *
63    * @var array
64    */
65   protected $order = [];
66
67   /**
68    * The fields by which to group.
69    *
70    * @var array
71    */
72   protected $group = [];
73
74   /**
75    * The conditional object for the HAVING clause.
76    *
77    * @var \Drupal\Core\Database\Query\Condition
78    */
79   protected $having;
80
81   /**
82    * Whether or not this query should be DISTINCT
83    *
84    * @var bool
85    */
86   protected $distinct = FALSE;
87
88   /**
89    * The range limiters for this query.
90    *
91    * @var array
92    */
93   protected $range;
94
95   /**
96    * An array whose elements specify a query to UNION, and the UNION type. The
97    * 'type' key may be '', 'ALL', or 'DISTINCT' to represent a 'UNION',
98    * 'UNION ALL', or 'UNION DISTINCT' statement, respectively.
99    *
100    * All entries in this array will be applied from front to back, with the
101    * first query to union on the right of the original query, the second union
102    * to the right of the first, etc.
103    *
104    * @var array
105    */
106   protected $union = [];
107
108   /**
109    * Indicates if preExecute() has already been called.
110    * @var bool
111    */
112   protected $prepared = FALSE;
113
114   /**
115    * The FOR UPDATE status
116    */
117   protected $forUpdate = FALSE;
118
119   /**
120    * Constructs a Select object.
121    *
122    * @param string $table
123    *   The name of the table that is being queried.
124    * @param string $alias
125    *   The alias for the table.
126    * @param \Drupal\Core\Database\Connection $connection
127    *   Database connection object.
128    * @param array $options
129    *   Array of query options.
130    */
131   public function __construct($table, $alias = NULL, Connection $connection, $options = []) {
132     $options['return'] = Database::RETURN_STATEMENT;
133     parent::__construct($connection, $options);
134     $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND';
135     $this->condition = new Condition($conjunction);
136     $this->having = new Condition($conjunction);
137     $this->addJoin(NULL, $table, $alias);
138   }
139
140   /**
141    * {@inheritdoc}
142    */
143   public function addTag($tag) {
144     $this->alterTags[$tag] = 1;
145     return $this;
146   }
147
148   /**
149    * {@inheritdoc}
150    */
151   public function hasTag($tag) {
152     return isset($this->alterTags[$tag]);
153   }
154
155   /**
156    * {@inheritdoc}
157    */
158   public function hasAllTags() {
159     return !(boolean)array_diff(func_get_args(), array_keys($this->alterTags));
160   }
161
162   /**
163    * {@inheritdoc}
164    */
165   public function hasAnyTag() {
166     return (boolean)array_intersect(func_get_args(), array_keys($this->alterTags));
167   }
168
169   /**
170    * {@inheritdoc}
171    */
172   public function addMetaData($key, $object) {
173     $this->alterMetaData[$key] = $object;
174     return $this;
175   }
176
177   /**
178    * {@inheritdoc}
179    */
180   public function getMetaData($key) {
181     return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
182   }
183
184   /**
185    * {@inheritdoc}
186    */
187   public function arguments() {
188     if (!$this->compiled()) {
189       return NULL;
190     }
191
192     $args = $this->condition->arguments() + $this->having->arguments();
193
194     foreach ($this->tables as $table) {
195       if ($table['arguments']) {
196         $args += $table['arguments'];
197       }
198       // If this table is a subquery, grab its arguments recursively.
199       if ($table['table'] instanceof SelectInterface) {
200         $args += $table['table']->arguments();
201       }
202       // If the join condition is an object, grab its arguments recursively.
203       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
204         $args += $table['condition']->arguments();
205       }
206     }
207
208     foreach ($this->expressions as $expression) {
209       if ($expression['arguments']) {
210         $args += $expression['arguments'];
211       }
212     }
213
214     // If there are any dependent queries to UNION,
215     // incorporate their arguments recursively.
216     foreach ($this->union as $union) {
217       $args += $union['query']->arguments();
218     }
219
220     return $args;
221   }
222
223   /**
224    * {@inheritdoc}
225    */
226   public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
227     $this->condition->compile($connection, $queryPlaceholder);
228     $this->having->compile($connection, $queryPlaceholder);
229
230     foreach ($this->tables as $table) {
231       // If this table is a subquery, compile it recursively.
232       if ($table['table'] instanceof SelectInterface) {
233         $table['table']->compile($connection, $queryPlaceholder);
234       }
235       // Make sure join conditions are also compiled.
236       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
237         $table['condition']->compile($connection, $queryPlaceholder);
238       }
239     }
240
241     // If there are any dependent queries to UNION, compile it recursively.
242     foreach ($this->union as $union) {
243       $union['query']->compile($connection, $queryPlaceholder);
244     }
245   }
246
247   /**
248    * {@inheritdoc}
249    */
250   public function compiled() {
251     if (!$this->condition->compiled() || !$this->having->compiled()) {
252       return FALSE;
253     }
254
255     foreach ($this->tables as $table) {
256       // If this table is a subquery, check its status recursively.
257       if ($table['table'] instanceof SelectInterface) {
258         if (!$table['table']->compiled()) {
259           return FALSE;
260         }
261       }
262       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
263         if (!$table['condition']->compiled()) {
264           return FALSE;
265         }
266       }
267     }
268
269     foreach ($this->union as $union) {
270       if (!$union['query']->compiled()) {
271         return FALSE;
272       }
273     }
274
275     return TRUE;
276   }
277
278   /**
279    * {@inheritdoc}
280    */
281   public function havingCondition($field, $value = NULL, $operator = NULL) {
282     $this->having->condition($field, $value, $operator);
283     return $this;
284   }
285
286   /**
287    * {@inheritdoc}
288    */
289   public function &havingConditions() {
290     return $this->having->conditions();
291   }
292
293   /**
294    * {@inheritdoc}
295    */
296   public function havingArguments() {
297     return $this->having->arguments();
298   }
299
300   /**
301    * {@inheritdoc}
302    */
303   public function having($snippet, $args = []) {
304     $this->having->where($snippet, $args);
305     return $this;
306   }
307
308   /**
309    * {@inheritdoc}
310    */
311   public function havingCompile(Connection $connection) {
312     $this->having->compile($connection, $this);
313   }
314
315   /**
316    * {@inheritdoc}
317    */
318   public function extend($extender_name) {
319     $override_class = $extender_name . '_' . $this->connection->driver();
320     if (class_exists($override_class)) {
321       $extender_name = $override_class;
322     }
323     return new $extender_name($this, $this->connection);
324   }
325
326   /**
327    * {@inheritdoc}
328    */
329   public function havingIsNull($field) {
330     $this->having->isNull($field);
331     return $this;
332   }
333
334   /**
335    * {@inheritdoc}
336    */
337   public function havingIsNotNull($field) {
338     $this->having->isNotNull($field);
339     return $this;
340   }
341
342   /**
343    * {@inheritdoc}
344    */
345   public function havingExists(SelectInterface $select) {
346     $this->having->exists($select);
347     return $this;
348   }
349
350   /**
351    * {@inheritdoc}
352    */
353   public function havingNotExists(SelectInterface $select) {
354     $this->having->notExists($select);
355     return $this;
356   }
357
358   /**
359    * {@inheritdoc}
360    */
361   public function forUpdate($set = TRUE) {
362     if (isset($set)) {
363       $this->forUpdate = $set;
364     }
365     return $this;
366   }
367
368   /**
369    * {@inheritdoc}
370    */
371   public function &getFields() {
372     return $this->fields;
373   }
374
375   /**
376    * {@inheritdoc}
377    */
378   public function &getExpressions() {
379     return $this->expressions;
380   }
381
382   /**
383    * {@inheritdoc}
384    */
385   public function &getOrderBy() {
386     return $this->order;
387   }
388
389   /**
390    * {@inheritdoc}
391    */
392   public function &getGroupBy() {
393     return $this->group;
394   }
395
396   /**
397    * {@inheritdoc}
398    */
399   public function &getTables() {
400     return $this->tables;
401   }
402
403   /**
404    * {@inheritdoc}
405    */
406   public function &getUnion() {
407     return $this->union;
408   }
409
410   /**
411    * {@inheritdoc}
412    */
413   public function escapeLike($string) {
414     return $this->connection->escapeLike($string);
415   }
416
417   /**
418    * {@inheritdoc}
419    */
420   public function escapeField($string) {
421     return $this->connection->escapeField($string);
422   }
423
424   /**
425    * {@inheritdoc}
426    */
427   public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
428     if (!isset($queryPlaceholder)) {
429       $queryPlaceholder = $this;
430     }
431     $this->compile($this->connection, $queryPlaceholder);
432     return $this->arguments();
433   }
434
435   /**
436    * {@inheritdoc}
437    */
438   public function isPrepared() {
439     return $this->prepared;
440   }
441
442   /**
443    * {@inheritdoc}
444    */
445   public function preExecute(SelectInterface $query = NULL) {
446     // If no query object is passed in, use $this.
447     if (!isset($query)) {
448       $query = $this;
449     }
450
451     // Only execute this once.
452     if ($query->isPrepared()) {
453       return TRUE;
454     }
455
456     // Modules may alter all queries or only those having a particular tag.
457     if (isset($this->alterTags)) {
458       // Many contrib modules as well as Entity Reference in core assume that
459       // query tags used for access-checking purposes follow the pattern
460       // $entity_type . '_access'. But this is not the case for taxonomy terms,
461       // since the core Taxonomy module used to add term_access instead of
462       // taxonomy_term_access to its queries. Provide backwards compatibility
463       // by adding both tags here instead of attempting to fix all contrib
464       // modules in a coordinated effort.
465       // TODO:
466       // - Extract this mechanism into a hook as part of a public (non-security)
467       //   issue.
468       // - Emit E_USER_DEPRECATED if term_access is used.
469       //   https://www.drupal.org/node/2575081
470       $term_access_tags = ['term_access' => 1, 'taxonomy_term_access' => 1];
471       if (array_intersect_key($this->alterTags, $term_access_tags)) {
472         $this->alterTags += $term_access_tags;
473       }
474       $hooks = ['query'];
475       foreach ($this->alterTags as $tag => $value) {
476         $hooks[] = 'query_' . $tag;
477       }
478       \Drupal::moduleHandler()->alter($hooks, $query);
479     }
480
481     $this->prepared = TRUE;
482
483     // Now also prepare any sub-queries.
484     foreach ($this->tables as $table) {
485       if ($table['table'] instanceof SelectInterface) {
486         $table['table']->preExecute();
487       }
488     }
489
490     foreach ($this->union as $union) {
491       $union['query']->preExecute();
492     }
493
494     return $this->prepared;
495   }
496
497   /**
498    * {@inheritdoc}
499    */
500   public function execute() {
501     // If validation fails, simply return NULL.
502     // Note that validation routines in preExecute() may throw exceptions instead.
503     if (!$this->preExecute()) {
504       return NULL;
505     }
506
507     $args = $this->getArguments();
508     return $this->connection->query((string) $this, $args, $this->queryOptions);
509   }
510
511   /**
512    * {@inheritdoc}
513    */
514   public function distinct($distinct = TRUE) {
515     $this->distinct = $distinct;
516     return $this;
517   }
518
519   /**
520    * {@inheritdoc}
521    */
522   public function addField($table_alias, $field, $alias = NULL) {
523     // If no alias is specified, first try the field name itself.
524     if (empty($alias)) {
525       $alias = $field;
526     }
527
528     // If that's already in use, try the table name and field name.
529     if (!empty($this->fields[$alias])) {
530       $alias = $table_alias . '_' . $field;
531     }
532
533     // If that is already used, just add a counter until we find an unused alias.
534     $alias_candidate = $alias;
535     $count = 2;
536     while (!empty($this->fields[$alias_candidate])) {
537       $alias_candidate = $alias . '_' . $count++;
538     }
539     $alias = $alias_candidate;
540
541     $this->fields[$alias] = [
542       'field' => $field,
543       'table' => $table_alias,
544       'alias' => $alias,
545     ];
546
547     return $alias;
548   }
549
550   /**
551    * {@inheritdoc}
552    */
553   public function fields($table_alias, array $fields = []) {
554     if ($fields) {
555       foreach ($fields as $field) {
556         // We don't care what alias was assigned.
557         $this->addField($table_alias, $field);
558       }
559     }
560     else {
561       // We want all fields from this table.
562       $this->tables[$table_alias]['all_fields'] = TRUE;
563     }
564
565     return $this;
566   }
567
568   /**
569    * {@inheritdoc}
570    */
571   public function addExpression($expression, $alias = NULL, $arguments = []) {
572     if (empty($alias)) {
573       $alias = 'expression';
574     }
575
576     $alias_candidate = $alias;
577     $count = 2;
578     while (!empty($this->expressions[$alias_candidate])) {
579       $alias_candidate = $alias . '_' . $count++;
580     }
581     $alias = $alias_candidate;
582
583     $this->expressions[$alias] = [
584       'expression' => $expression,
585       'alias' => $alias,
586       'arguments' => $arguments,
587     ];
588
589     return $alias;
590   }
591
592   /**
593    * {@inheritdoc}
594    */
595   public function join($table, $alias = NULL, $condition = NULL, $arguments = []) {
596     return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
597   }
598
599   /**
600    * {@inheritdoc}
601    */
602   public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
603     return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
604   }
605
606   /**
607    * {@inheritdoc}
608    */
609   public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
610     return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
611   }
612
613   /**
614    * {@inheritdoc}
615    */
616   public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
617     return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
618   }
619
620   /**
621    * {@inheritdoc}
622    */
623   public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = []) {
624     if (empty($alias)) {
625       if ($table instanceof SelectInterface) {
626         $alias = 'subquery';
627       }
628       else {
629         $alias = $table;
630       }
631     }
632
633     $alias_candidate = $alias;
634     $count = 2;
635     while (!empty($this->tables[$alias_candidate])) {
636       $alias_candidate = $alias . '_' . $count++;
637     }
638     $alias = $alias_candidate;
639
640     if (is_string($condition)) {
641       $condition = str_replace('%alias', $alias, $condition);
642     }
643
644     $this->tables[$alias] = [
645       'join type' => $type,
646       'table' => $table,
647       'alias' => $alias,
648       'condition' => $condition,
649       'arguments' => $arguments,
650     ];
651
652     return $alias;
653   }
654
655   /**
656    * {@inheritdoc}
657    */
658   public function orderBy($field, $direction = 'ASC') {
659     // Only allow ASC and DESC, default to ASC.
660     $direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC';
661     $this->order[$field] = $direction;
662     return $this;
663   }
664
665   /**
666    * {@inheritdoc}
667    */
668   public function orderRandom() {
669     $alias = $this->addExpression('RAND()', 'random_field');
670     $this->orderBy($alias);
671     return $this;
672   }
673
674   /**
675    * {@inheritdoc}
676    */
677   public function range($start = NULL, $length = NULL) {
678     $this->range = $start !== NULL ? ['start' => $start, 'length' => $length] : [];
679     return $this;
680   }
681
682   /**
683    * {@inheritdoc}
684    */
685   public function union(SelectInterface $query, $type = '') {
686     // Handle UNION aliasing.
687     switch ($type) {
688       // Fold UNION DISTINCT to UNION for better cross database support.
689       case 'DISTINCT':
690       case '':
691         $type = 'UNION';
692         break;
693
694       case 'ALL':
695         $type = 'UNION ALL';
696       default:
697     }
698
699     $this->union[] = [
700       'type' => $type,
701       'query' => $query,
702     ];
703
704     return $this;
705   }
706
707   /**
708    * {@inheritdoc}
709    */
710   public function groupBy($field) {
711     $this->group[$field] = $field;
712     return $this;
713   }
714
715   /**
716    * {@inheritdoc}
717    */
718   public function countQuery() {
719     $count = $this->prepareCountQuery();
720
721     $query = $this->connection->select($count, NULL, $this->queryOptions);
722     $query->addExpression('COUNT(*)');
723
724     return $query;
725   }
726
727   /**
728    * Prepares a count query from the current query object.
729    *
730    * @return \Drupal\Core\Database\Query\Select
731    *   A new query object ready to have COUNT(*) performed on it.
732    */
733   protected function prepareCountQuery() {
734     // Create our new query object that we will mutate into a count query.
735     $count = clone($this);
736
737     $group_by = $count->getGroupBy();
738     $having = $count->havingConditions();
739
740     if (!$count->distinct && !isset($having[0])) {
741       // When not executing a distinct query, we can zero-out existing fields
742       // and expressions that are not used by a GROUP BY or HAVING. Fields
743       // listed in a GROUP BY or HAVING clause need to be present in the
744       // query.
745       $fields =& $count->getFields();
746       foreach (array_keys($fields) as $field) {
747         if (empty($group_by[$field])) {
748           unset($fields[$field]);
749         }
750       }
751
752       $expressions =& $count->getExpressions();
753       foreach (array_keys($expressions) as $field) {
754         if (empty($group_by[$field])) {
755           unset($expressions[$field]);
756         }
757       }
758
759       // Also remove 'all_fields' statements, which are expanded into tablename.*
760       // when the query is executed.
761       foreach ($count->tables as &$table) {
762         unset($table['all_fields']);
763       }
764     }
765
766     // If we've just removed all fields from the query, make sure there is at
767     // least one so that the query still runs.
768     $count->addExpression('1');
769
770     // Ordering a count query is a waste of cycles, and breaks on some
771     // databases anyway.
772     $orders = &$count->getOrderBy();
773     $orders = [];
774
775     if ($count->distinct && !empty($group_by)) {
776       // If the query is distinct and contains a GROUP BY, we need to remove the
777       // distinct because SQL99 does not support counting on distinct multiple fields.
778       $count->distinct = FALSE;
779     }
780
781     // If there are any dependent queries to UNION, prepare each of those for
782     // the count query also.
783     foreach ($count->union as &$union) {
784       $union['query'] = $union['query']->prepareCountQuery();
785     }
786
787     return $count;
788   }
789
790   /**
791    * {@inheritdoc}
792    */
793   public function __toString() {
794     // For convenience, we compile the query ourselves if the caller forgot
795     // to do it. This allows constructs like "(string) $query" to work. When
796     // the query will be executed, it will be recompiled using the proper
797     // placeholder generator anyway.
798     if (!$this->compiled()) {
799       $this->compile($this->connection, $this);
800     }
801
802     // Create a sanitized comment string to prepend to the query.
803     $comments = $this->connection->makeComment($this->comments);
804
805     // SELECT
806     $query = $comments . 'SELECT ';
807     if ($this->distinct) {
808       $query .= 'DISTINCT ';
809     }
810
811     // FIELDS and EXPRESSIONS
812     $fields = [];
813     foreach ($this->tables as $alias => $table) {
814       if (!empty($table['all_fields'])) {
815         $fields[] = $this->connection->escapeTable($alias) . '.*';
816       }
817     }
818     foreach ($this->fields as $field) {
819       // Always use the AS keyword for field aliases, as some
820       // databases require it (e.g., PostgreSQL).
821       $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
822     }
823     foreach ($this->expressions as $expression) {
824       $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
825     }
826     $query .= implode(', ', $fields);
827
828
829     // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
830     $query .= "\nFROM ";
831     foreach ($this->tables as $table) {
832       $query .= "\n";
833       if (isset($table['join type'])) {
834         $query .= $table['join type'] . ' JOIN ';
835       }
836
837       // If the table is a subquery, compile it and integrate it into this query.
838       if ($table['table'] instanceof SelectInterface) {
839         // Run preparation steps on this sub-query before converting to string.
840         $subquery = $table['table'];
841         $subquery->preExecute();
842         $table_string = '(' . (string) $subquery . ')';
843       }
844       else {
845         $table_string = $this->connection->escapeTable($table['table']);
846         // Do not attempt prefixing cross database / schema queries.
847         if (strpos($table_string, '.') === FALSE) {
848           $table_string = '{' . $table_string . '}';
849         }
850       }
851
852       // Don't use the AS keyword for table aliases, as some
853       // databases don't support it (e.g., Oracle).
854       $query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']);
855
856       if (!empty($table['condition'])) {
857         $query .= ' ON ' . (string) $table['condition'];
858       }
859     }
860
861     // WHERE
862     if (count($this->condition)) {
863       // There is an implicit string cast on $this->condition.
864       $query .= "\nWHERE " . $this->condition;
865     }
866
867     // GROUP BY
868     if ($this->group) {
869       $query .= "\nGROUP BY " . implode(', ', $this->group);
870     }
871
872     // HAVING
873     if (count($this->having)) {
874       // There is an implicit string cast on $this->having.
875       $query .= "\nHAVING " . $this->having;
876     }
877
878     // UNION is a little odd, as the select queries to combine are passed into
879     // this query, but syntactically they all end up on the same level.
880     if ($this->union) {
881       foreach ($this->union as $union) {
882         $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
883       }
884     }
885
886     // ORDER BY
887     if ($this->order) {
888       $query .= "\nORDER BY ";
889       $fields = [];
890       foreach ($this->order as $field => $direction) {
891         $fields[] = $this->connection->escapeField($field) . ' ' . $direction;
892       }
893       $query .= implode(', ', $fields);
894     }
895
896     // RANGE
897     // There is no universal SQL standard for handling range or limit clauses.
898     // Fortunately, all core-supported databases use the same range syntax.
899     // Databases that need a different syntax can override this method and
900     // do whatever alternate logic they need to.
901     if (!empty($this->range)) {
902       $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
903     }
904
905     if ($this->forUpdate) {
906       $query .= ' FOR UPDATE';
907     }
908
909     return $query;
910   }
911
912   /**
913    * {@inheritdoc}
914    */
915   public function __clone() {
916     // On cloning, also clone the dependent objects. However, we do not
917     // want to clone the database connection object as that would duplicate the
918     // connection itself.
919
920     $this->condition = clone($this->condition);
921     $this->having = clone($this->having);
922     foreach ($this->union as $key => $aggregate) {
923       $this->union[$key]['query'] = clone($aggregate['query']);
924     }
925   }
926
927 }