Project

General

Profile

Paste
Download (49.6 KB) Statistics
| Branch: | Revision:

root / drupal7 / includes / database / select.inc @ b0dc3a2e

1
<?php
2

    
3
/**
4
 * @addtogroup database
5
 * @{
6
 */
7

    
8
require_once dirname(__FILE__) . '/query.inc';
9

    
10
/**
11
 * Interface for extendable query objects.
12
 *
13
 * "Extenders" follow the "Decorator" OOP design pattern.  That is, they wrap
14
 * and "decorate" another object.  In our case, they implement the same interface
15
 * as select queries and wrap a select query, to which they delegate almost all
16
 * operations.  Subclasses of this class may implement additional methods or
17
 * override existing methods as appropriate.  Extenders may also wrap other
18
 * extender objects, allowing for arbitrarily complex "enhanced" queries.
19
 */
20
interface QueryExtendableInterface {
21

    
22
  /**
23
   * Enhance this object by wrapping it in an extender object.
24
   *
25
   * @param $extender_name
26
   *   The base name of the extending class.  The base name will be checked
27
   *   against the current database connection to allow driver-specific subclasses
28
   *   as well, using the same logic as the query objects themselves.  For example,
29
   *   PagerDefault_mysql is the MySQL-specific override for PagerDefault.
30
   * @return QueryExtendableInterface
31
   *   The extender object, which now contains a reference to this object.
32
   */
33
  public function extend($extender_name);
34
}
35

    
36
/**
37
 * Interface definition for a Select Query object.
38
 */
39
interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableInterface, QueryExtendableInterface, QueryPlaceholderInterface {
40

    
41
  /* Alter accessors to expose the query data to alter hooks. */
42

    
43
  /**
44
   * Returns a reference to the fields array for this query.
45
   *
46
   * Because this method returns by reference, alter hooks may edit the fields
47
   * array directly to make their changes. If just adding fields, however, the
48
   * use of addField() is preferred.
49
   *
50
   * Note that this method must be called by reference as well:
51
   *
52
   * @code
53
   * $fields =& $query->getFields();
54
   * @endcode
55
   *
56
   * @return
57
   *   A reference to the fields array structure.
58
   */
59
  public function &getFields();
60

    
61
  /**
62
   * Returns a reference to the expressions array for this query.
63
   *
64
   * Because this method returns by reference, alter hooks may edit the expressions
65
   * array directly to make their changes. If just adding expressions, however, the
66
   * use of addExpression() is preferred.
67
   *
68
   * Note that this method must be called by reference as well:
69
   *
70
   * @code
71
   * $fields =& $query->getExpressions();
72
   * @endcode
73
   *
74
   * @return
75
   *   A reference to the expression array structure.
76
   */
77
  public function &getExpressions();
78

    
79
  /**
80
   * Returns a reference to the order by array for this query.
81
   *
82
   * Because this method returns by reference, alter hooks may edit the order-by
83
   * array directly to make their changes. If just adding additional ordering
84
   * fields, however, the use of orderBy() is preferred.
85
   *
86
   * Note that this method must be called by reference as well:
87
   *
88
   * @code
89
   * $fields =& $query->getOrderBy();
90
   * @endcode
91
   *
92
   * @return
93
   *   A reference to the expression array structure.
94
   */
95
  public function &getOrderBy();
96

    
97
  /**
98
   * Returns a reference to the group-by array for this query.
99
   *
100
   * Because this method returns by reference, alter hooks may edit the group-by
101
   * array directly to make their changes. If just adding additional grouping
102
   * fields, however, the use of groupBy() is preferred.
103
   *
104
   * Note that this method must be called by reference as well:
105
   *
106
   * @code
107
   * $fields =& $query->getGroupBy();
108
   * @endcode
109
   *
110
   * @return
111
   *   A reference to the group-by array structure.
112
   */
113
  public function &getGroupBy();
114

    
115
  /**
116
   * Returns a reference to the tables array for this query.
117
   *
118
   * Because this method returns by reference, alter hooks may edit the tables
119
   * array directly to make their changes. If just adding tables, however, the
120
   * use of the join() methods is preferred.
121
   *
122
   * Note that this method must be called by reference as well:
123
   *
124
   * @code
125
   * $fields =& $query->getTables();
126
   * @endcode
127
   *
128
   * @return
129
   *   A reference to the tables array structure.
130
   */
131
  public function &getTables();
132

    
133
  /**
134
   * Returns a reference to the union queries for this query. This include
135
   * queries for UNION, UNION ALL, and UNION DISTINCT.
136
   *
137
   * Because this method returns by reference, alter hooks may edit the tables
138
   * array directly to make their changes. If just adding union queries,
139
   * however, the use of the union() method is preferred.
140
   *
141
   * Note that this method must be called by reference as well:
142
   *
143
   * @code
144
   * $fields =& $query->getUnion();
145
   * @endcode
146
   *
147
   * @return
148
   *   A reference to the union query array structure.
149
   */
150
  public function &getUnion();
151

    
152
  /**
153
   * Compiles and returns an associative array of the arguments for this prepared statement.
154
   *
155
   * @param $queryPlaceholder
156
   *   When collecting the arguments of a subquery, the main placeholder
157
   *   object should be passed as this parameter.
158
   *
159
   * @return
160
   *   An associative array of all placeholder arguments for this query.
161
   */
162
  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL);
163

    
164
  /* Query building operations */
165

    
166
  /**
167
   * Sets this query to be DISTINCT.
168
   *
169
   * @param $distinct
170
   *   TRUE to flag this query DISTINCT, FALSE to disable it.
171
   * @return SelectQueryInterface
172
   *   The called object.
173
   */
174
  public function distinct($distinct = TRUE);
175

    
176
  /**
177
   * Adds a field to the list to be SELECTed.
178
   *
179
   * @param $table_alias
180
   *   The name of the table from which the field comes, as an alias. Generally
181
   *   you will want to use the return value of join() here to ensure that it is
182
   *   valid.
183
   * @param $field
184
   *   The name of the field.
185
   * @param $alias
186
   *   The alias for this field. If not specified, one will be generated
187
   *   automatically based on the $table_alias and $field. The alias will be
188
   *   checked for uniqueness, so the requested alias may not be the alias
189
   *   that is assigned in all cases.
190
   * @return
191
   *   The unique alias that was assigned for this field.
192
   */
193
  public function addField($table_alias, $field, $alias = NULL);
194

    
195
  /**
196
   * Add multiple fields from the same table to be SELECTed.
197
   *
198
   * This method does not return the aliases set for the passed fields. In the
199
   * majority of cases that is not a problem, as the alias will be the field
200
   * name. However, if you do need to know the alias you can call getFields()
201
   * and examine the result to determine what alias was created. Alternatively,
202
   * simply use addField() for the few fields you care about and this method for
203
   * the rest.
204
   *
205
   * @param $table_alias
206
   *   The name of the table from which the field comes, as an alias. Generally
207
   *   you will want to use the return value of join() here to ensure that it is
208
   *   valid.
209
   * @param $fields
210
   *   An indexed array of fields present in the specified table that should be
211
   *   included in this query. If not specified, $table_alias.* will be generated
212
   *   without any aliases.
213
   * @return SelectQueryInterface
214
   *   The called object.
215
   */
216
  public function fields($table_alias, array $fields = array());
217

    
218
  /**
219
   * Adds an expression to the list of "fields" to be SELECTed.
220
   *
221
   * An expression can be any arbitrary string that is valid SQL. That includes
222
   * various functions, which may in some cases be database-dependent. This
223
   * method makes no effort to correct for database-specific functions.
224
   *
225
   * @param $expression
226
   *   The expression string. May contain placeholders.
227
   * @param $alias
228
   *   The alias for this expression. If not specified, one will be generated
229
   *   automatically in the form "expression_#". The alias will be checked for
230
   *   uniqueness, so the requested alias may not be the alias that is assigned
231
   *   in all cases.
232
   * @param $arguments
233
   *   Any placeholder arguments needed for this expression.
234
   * @return
235
   *   The unique alias that was assigned for this expression.
236
   */
237
  public function addExpression($expression, $alias = NULL, $arguments = array());
238

    
239
  /**
240
   * Default Join against another table in the database.
241
   *
242
   * This method is a convenience method for innerJoin().
243
   *
244
   * @param $table
245
   *   The table against which to join.
246
   * @param $alias
247
   *   The alias for the table. In most cases this should be the first letter
248
   *   of the table, or the first letter of each "word" in the table.
249
   * @param $condition
250
   *   The condition on which to join this table. If the join requires values,
251
   *   this clause should use a named placeholder and the value or values to
252
   *   insert should be passed in the 4th parameter. For the first table joined
253
   *   on a query, this value is ignored as the first table is taken as the base
254
   *   table. The token %alias can be used in this string to be replaced with
255
   *   the actual alias. This is useful when $alias is modified by the database
256
   *   system, for example, when joining the same table more than once.
257
   * @param $arguments
258
   *   An array of arguments to replace into the $condition of this join.
259
   * @return
260
   *   The unique alias that was assigned for this table.
261
   */
262
  public function join($table, $alias = NULL, $condition = NULL, $arguments = array());
263

    
264
  /**
265
   * Inner Join against another table in the database.
266
   *
267
   * @param $table
268
   *   The table against which to join.
269
   * @param $alias
270
   *   The alias for the table. In most cases this should be the first letter
271
   *   of the table, or the first letter of each "word" in the table.
272
   * @param $condition
273
   *   The condition on which to join this table. If the join requires values,
274
   *   this clause should use a named placeholder and the value or values to
275
   *   insert should be passed in the 4th parameter. For the first table joined
276
   *   on a query, this value is ignored as the first table is taken as the base
277
   *   table. The token %alias can be used in this string to be replaced with
278
   *   the actual alias. This is useful when $alias is modified by the database
279
   *   system, for example, when joining the same table more than once.
280
   * @param $arguments
281
   *   An array of arguments to replace into the $condition of this join.
282
   * @return
283
   *   The unique alias that was assigned for this table.
284
   */
285
  public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
286

    
287
  /**
288
   * Left Outer Join against another table in the database.
289
   *
290
   * @param $table
291
   *   The table against which to join.
292
   * @param $alias
293
   *   The alias for the table. In most cases this should be the first letter
294
   *   of the table, or the first letter of each "word" in the table.
295
   * @param $condition
296
   *   The condition on which to join this table. If the join requires values,
297
   *   this clause should use a named placeholder and the value or values to
298
   *   insert should be passed in the 4th parameter. For the first table joined
299
   *   on a query, this value is ignored as the first table is taken as the base
300
   *   table. The token %alias can be used in this string to be replaced with
301
   *   the actual alias. This is useful when $alias is modified by the database
302
   *   system, for example, when joining the same table more than once.
303
   * @param $arguments
304
   *   An array of arguments to replace into the $condition of this join.
305
   * @return
306
   *   The unique alias that was assigned for this table.
307
   */
308
  public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
309

    
310
  /**
311
   * Right Outer Join against another table in the database.
312
   *
313
   * @param $table
314
   *   The table against which to join.
315
   * @param $alias
316
   *   The alias for the table. In most cases this should be the first letter
317
   *   of the table, or the first letter of each "word" in the table.
318
   * @param $condition
319
   *   The condition on which to join this table. If the join requires values,
320
   *   this clause should use a named placeholder and the value or values to
321
   *   insert should be passed in the 4th parameter. For the first table joined
322
   *   on a query, this value is ignored as the first table is taken as the base
323
   *   table. The token %alias can be used in this string to be replaced with
324
   *   the actual alias. This is useful when $alias is modified by the database
325
   *   system, for example, when joining the same table more than once.
326
   * @param $arguments
327
   *   An array of arguments to replace into the $condition of this join.
328
   * @return
329
   *   The unique alias that was assigned for this table.
330
   */
331
  public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
332

    
333
  /**
334
   * Join against another table in the database.
335
   *
336
   * This method does the "hard" work of queuing up a table to be joined against.
337
   * In some cases, that may include dipping into the Schema API to find the necessary
338
   * fields on which to join.
339
   *
340
   * @param $type
341
   *   The type of join. Typically one one of INNER, LEFT OUTER, and RIGHT OUTER.
342
   * @param $table
343
   *   The table against which to join. May be a string or another SelectQuery
344
   *   object. If a query object is passed, it will be used as a subselect.
345
   * @param $alias
346
   *   The alias for the table. In most cases this should be the first letter
347
   *   of the table, or the first letter of each "word" in the table. If omitted,
348
   *   one will be dynamically generated.
349
   * @param $condition
350
   *   The condition on which to join this table. If the join requires values,
351
   *   this clause should use a named placeholder and the value or values to
352
   *   insert should be passed in the 4th parameter. For the first table joined
353
   *   on a query, this value is ignored as the first table is taken as the base
354
   *   table. The token %alias can be used in this string to be replaced with
355
   *   the actual alias. This is useful when $alias is modified by the database
356
   *   system, for example, when joining the same table more than once.
357
   * @param $arguments
358
   *   An array of arguments to replace into the $condition of this join.
359
   * @return
360
   *   The unique alias that was assigned for this table.
361
   */
362
  public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array());
363

    
364
  /**
365
   * Orders the result set by a given field.
366
   *
367
   * If called multiple times, the query will order by each specified field in the
368
   * order this method is called.
369
   *
370
   * If the query uses DISTINCT or GROUP BY conditions, fields or expressions
371
   * that are used for the order must be selected to be compatible with some
372
   * databases like PostgreSQL. The PostgreSQL driver can handle simple cases
373
   * automatically but it is suggested to explicitly specify them. Additionally,
374
   * when ordering on an alias, the alias must be added before orderBy() is
375
   * called.
376
   *
377
   * @param $field
378
   *   The field on which to order.
379
   * @param $direction
380
   *   The direction to sort. Legal values are "ASC" and "DESC". Any other value
381
   *   will be converted to "ASC".
382
   * @return SelectQueryInterface
383
   *   The called object.
384
   */
385
  public function orderBy($field, $direction = 'ASC');
386

    
387
  /**
388
   * Orders the result set by a random value.
389
   *
390
   * This may be stacked with other orderBy() calls. If so, the query will order
391
   * by each specified field, including this one, in the order called. Although
392
   * this method may be called multiple times on the same query, doing so
393
   * is not particularly useful.
394
   *
395
   * Note: The method used by most drivers may not scale to very large result
396
   * sets. If you need to work with extremely large data sets, you may create
397
   * your own database driver by subclassing off of an existing driver and
398
   * implementing your own randomization mechanism. See
399
   *
400
   * http://jan.kneschke.de/projects/mysql/order-by-rand/
401
   *
402
   * for an example of such an alternate sorting mechanism.
403
   *
404
   * @return SelectQueryInterface
405
   *   The called object
406
   */
407
  public function orderRandom();
408

    
409
  /**
410
   * Restricts a query to a given range in the result set.
411
   *
412
   * If this method is called with no parameters, will remove any range
413
   * directives that have been set.
414
   *
415
   * @param $start
416
   *   The first record from the result set to return. If NULL, removes any
417
   *   range directives that are set.
418
   * @param $length
419
   *   The number of records to return from the result set.
420
   * @return SelectQueryInterface
421
   *   The called object.
422
   */
423
  public function range($start = NULL, $length = NULL);
424

    
425
  /**
426
   * Add another Select query to UNION to this one.
427
   *
428
   * Union queries consist of two or more queries whose
429
   * results are effectively concatenated together. Queries
430
   * will be UNIONed in the order they are specified, with
431
   * this object's query coming first. Duplicate columns will
432
   * be discarded. All forms of UNION are supported, using
433
   * the second '$type' argument.
434
   *
435
   * Note: All queries UNIONed together must have the same
436
   * field structure, in the same order. It is up to the
437
   * caller to ensure that they match properly. If they do
438
   * not, an SQL syntax error will result.
439
   *
440
   * @param $query
441
   *   The query to UNION to this query.
442
   * @param $type
443
   *   The type of UNION to add to the query. Defaults to plain
444
   *   UNION.
445
   * @return SelectQueryInterface
446
   *   The called object.
447
   */
448
  public function union(SelectQueryInterface $query, $type = '');
449

    
450
  /**
451
   * Groups the result set by the specified field.
452
   *
453
   * @param $field
454
   *   The field on which to group. This should be the field as aliased.
455
   * @return SelectQueryInterface
456
   *   The called object.
457
   */
458
  public function groupBy($field);
459

    
460
  /**
461
   * Get the equivalent COUNT query of this query as a new query object.
462
   *
463
   * @return SelectQueryInterface
464
   *   A new SelectQuery object with no fields or expressions besides COUNT(*).
465
   */
466
  public function countQuery();
467

    
468
  /**
469
   * Indicates if preExecute() has already been called on that object.
470
   *
471
   * @return
472
   *   TRUE is this query has already been prepared, FALSE otherwise.
473
   */
474
  public function isPrepared();
475

    
476
  /**
477
   * Generic preparation and validation for a SELECT query.
478
   *
479
   * @return
480
   *   TRUE if the validation was successful, FALSE if not.
481
   */
482
  public function preExecute(SelectQueryInterface $query = NULL);
483

    
484
  /**
485
   * Helper function to build most common HAVING conditional clauses.
486
   *
487
   * This method can take a variable number of parameters. If called with two
488
   * parameters, they are taken as $field and $value with $operator having a value
489
   * of IN if $value is an array and = otherwise.
490
   *
491
   * @param $field
492
   *   The name of the field to check. If you would like to add a more complex
493
   *   condition involving operators or functions, use having().
494
   * @param $value
495
   *   The value to test the field against. In most cases, this is a scalar. For more
496
   *   complex options, it is an array. The meaning of each element in the array is
497
   *   dependent on the $operator.
498
   * @param $operator
499
   *   The comparison operator, such as =, <, or >=. It also accepts more complex
500
   *   options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is an array
501
   *   = otherwise.
502
   * @return QueryConditionInterface
503
   *   The called object.
504
   */
505
  public function havingCondition($field, $value = NULL, $operator = NULL);
506

    
507
  /**
508
   * Clone magic method.
509
   *
510
   * Select queries have dependent objects that must be deep-cloned.  The
511
   * connection object itself, however, should not be cloned as that would
512
   * duplicate the connection itself.
513
   */
514
  public function __clone();
515

    
516
  /**
517
   * Add FOR UPDATE to the query.
518
   *
519
   * FOR UPDATE prevents the rows retrieved by the SELECT statement from being
520
   * modified or deleted by other transactions until the current transaction
521
   * ends. Other transactions that attempt UPDATE, DELETE, or SELECT FOR UPDATE
522
   * of these rows will be blocked until the current transaction ends.
523
   *
524
   * @param $set
525
   *   IF TRUE, FOR UPDATE will be added to the query, if FALSE then it won't.
526
   *
527
   * @return QueryConditionInterface
528
   *   The called object.
529
   */
530
  public function forUpdate($set = TRUE);
531
}
532

    
533
/**
534
 * The base extender class for Select queries.
535
 */
536
class SelectQueryExtender implements SelectQueryInterface {
537

    
538
  /**
539
   * The SelectQuery object we are extending/decorating.
540
   *
541
   * @var SelectQueryInterface
542
   */
543
  protected $query;
544

    
545
  /**
546
   * The connection object on which to run this query.
547
   *
548
   * @var DatabaseConnection
549
   */
550
  protected $connection;
551

    
552
  /**
553
   * A unique identifier for this query object.
554
   */
555
  protected $uniqueIdentifier;
556

    
557
  /**
558
   * The placeholder counter.
559
   */
560
  protected $placeholder = 0;
561

    
562
  public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
563
    $this->uniqueIdentifier = uniqid('', TRUE);
564
    $this->query = $query;
565
    $this->connection = $connection;
566
  }
567

    
568
  /**
569
   * Implements QueryPlaceholderInterface::uniqueIdentifier().
570
   */
571
  public function uniqueIdentifier() {
572
    return $this->uniqueIdentifier;
573
  }
574

    
575
  /**
576
   * Implements QueryPlaceholderInterface::nextPlaceholder().
577
   */
578
  public function nextPlaceholder() {
579
    return $this->placeholder++;
580
  }
581

    
582
  /* Implementations of QueryAlterableInterface. */
583

    
584
  public function addTag($tag) {
585
    $this->query->addTag($tag);
586
    return $this;
587
  }
588

    
589
  public function hasTag($tag) {
590
    return $this->query->hasTag($tag);
591
  }
592

    
593
  public function hasAllTags() {
594
    $args = func_get_args();
595
    return call_user_func_array(array($this->query, 'hasAllTags'), $args);
596
  }
597

    
598
  public function hasAnyTag() {
599
    $args = func_get_args();
600
    return call_user_func_array(array($this->query, 'hasAnyTag'), $args);
601
  }
602

    
603
  public function addMetaData($key, $object) {
604
    $this->query->addMetaData($key, $object);
605
    return $this;
606
  }
607

    
608
  public function getMetaData($key) {
609
    return $this->query->getMetaData($key);
610
  }
611

    
612
  /* Implementations of QueryConditionInterface for the WHERE clause. */
613

    
614
  public function condition($field, $value = NULL, $operator = NULL) {
615
    $this->query->condition($field, $value, $operator);
616
    return $this;
617
  }
618

    
619
  public function &conditions() {
620
    return $this->query->conditions();
621
  }
622

    
623
  public function arguments() {
624
    return $this->query->arguments();
625
  }
626

    
627
  public function where($snippet, $args = array()) {
628
    $this->query->where($snippet, $args);
629
    return $this;
630
  }
631

    
632
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
633
    return $this->query->compile($connection, $queryPlaceholder);
634
  }
635

    
636
  public function compiled() {
637
    return $this->query->compiled();
638
  }
639

    
640
  /* Implementations of QueryConditionInterface for the HAVING clause. */
641

    
642
  public function havingCondition($field, $value = NULL, $operator = '=') {
643
    $this->query->havingCondition($field, $value, $operator);
644
    return $this;
645
  }
646

    
647
  public function &havingConditions() {
648
    return $this->query->havingConditions();
649
  }
650

    
651
  public function havingArguments() {
652
    return $this->query->havingArguments();
653
  }
654

    
655
  public function having($snippet, $args = array()) {
656
    $this->query->having($snippet, $args);
657
    return $this;
658
  }
659

    
660
  public function havingCompile(DatabaseConnection $connection) {
661
    return $this->query->havingCompile($connection);
662
  }
663

    
664
  /* Implementations of QueryExtendableInterface. */
665

    
666
  public function extend($extender_name) {
667
    // The extender can be anywhere so this needs to go to the registry, which
668
    // is surely loaded by now.
669
    $class = $this->connection->getDriverClass($extender_name, array(), TRUE);
670
    return new $class($this, $this->connection);
671
  }
672

    
673
  /* Alter accessors to expose the query data to alter hooks. */
674

    
675
  public function &getFields() {
676
    return $this->query->getFields();
677
  }
678

    
679
  public function &getExpressions() {
680
    return $this->query->getExpressions();
681
  }
682

    
683
  public function &getOrderBy() {
684
    return $this->query->getOrderBy();
685
  }
686

    
687
  public function &getGroupBy() {
688
    return $this->query->getGroupBy();
689
  }
690

    
691
  public function &getTables() {
692
    return $this->query->getTables();
693
  }
694

    
695
  public function &getUnion() {
696
    return $this->query->getUnion();
697
  }
698

    
699
  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
700
    return $this->query->getArguments($queryPlaceholder);
701
  }
702

    
703
  public function isPrepared() {
704
    return $this->query->isPrepared();
705
  }
706

    
707
  public function preExecute(SelectQueryInterface $query = NULL) {
708
    // If no query object is passed in, use $this.
709
    if (!isset($query)) {
710
      $query = $this;
711
    }
712

    
713
    return $this->query->preExecute($query);
714
  }
715

    
716
  public function execute() {
717
    // By calling preExecute() here, we force it to preprocess the extender
718
    // object rather than just the base query object.  That means
719
    // hook_query_alter() gets access to the extended object.
720
    if (!$this->preExecute($this)) {
721
      return NULL;
722
    }
723

    
724
    return $this->query->execute();
725
  }
726

    
727
  public function distinct($distinct = TRUE) {
728
    $this->query->distinct($distinct);
729
    return $this;
730
  }
731

    
732
  public function addField($table_alias, $field, $alias = NULL) {
733
    return $this->query->addField($table_alias, $field, $alias);
734
  }
735

    
736
  public function fields($table_alias, array $fields = array()) {
737
    $this->query->fields($table_alias, $fields);
738
    return $this;
739
  }
740

    
741
  public function addExpression($expression, $alias = NULL, $arguments = array()) {
742
    return $this->query->addExpression($expression, $alias, $arguments);
743
  }
744

    
745
  public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
746
    return $this->query->join($table, $alias, $condition, $arguments);
747
  }
748

    
749
  public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
750
    return $this->query->innerJoin($table, $alias, $condition, $arguments);
751
  }
752

    
753
  public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
754
    return $this->query->leftJoin($table, $alias, $condition, $arguments);
755
  }
756

    
757
  public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
758
    return $this->query->rightJoin($table, $alias, $condition, $arguments);
759
  }
760

    
761
  public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
762
    return $this->query->addJoin($type, $table, $alias, $condition, $arguments);
763
  }
764

    
765
  public function orderBy($field, $direction = 'ASC') {
766
    $this->query->orderBy($field, $direction);
767
    return $this;
768
  }
769

    
770
  public function orderRandom() {
771
    $this->query->orderRandom();
772
    return $this;
773
  }
774

    
775
  public function range($start = NULL, $length = NULL) {
776
    $this->query->range($start, $length);
777
    return $this;
778
  }
779

    
780
  public function union(SelectQueryInterface $query, $type = '') {
781
    $this->query->union($query, $type);
782
    return $this;
783
  }
784

    
785
  public function groupBy($field) {
786
    $this->query->groupBy($field);
787
    return $this;
788
  }
789

    
790
  public function forUpdate($set = TRUE) {
791
    $this->query->forUpdate($set);
792
    return $this;
793
  }
794

    
795
  public function countQuery() {
796
    return $this->query->countQuery();
797
  }
798

    
799
  function isNull($field) {
800
    $this->query->isNull($field);
801
    return $this;
802
  }
803

    
804
  function isNotNull($field) {
805
    $this->query->isNotNull($field);
806
    return $this;
807
  }
808

    
809
  public function exists(SelectQueryInterface $select) {
810
    $this->query->exists($select);
811
    return $this;
812
  }
813

    
814
  public function notExists(SelectQueryInterface $select) {
815
    $this->query->notExists($select);
816
    return $this;
817
  }
818

    
819
  public function __toString() {
820
    return (string) $this->query;
821
  }
822

    
823
  public function __clone() {
824
    $this->uniqueIdentifier = uniqid('', TRUE);
825

    
826
    // We need to deep-clone the query we're wrapping, which in turn may
827
    // deep-clone other objects.  Exciting!
828
    $this->query = clone($this->query);
829
  }
830

    
831
  /**
832
   * Magic override for undefined methods.
833
   *
834
   * If one extender extends another extender, then methods in the inner extender
835
   * will not be exposed on the outer extender.  That's because we cannot know
836
   * in advance what those methods will be, so we cannot provide wrapping
837
   * implementations as we do above.  Instead, we use this slower catch-all method
838
   * to handle any additional methods.
839
   */
840
  public function __call($method, $args) {
841
    $return = call_user_func_array(array($this->query, $method), $args);
842

    
843
    // Some methods will return the called object as part of a fluent interface.
844
    // Others will return some useful value.  If it's a value, then the caller
845
    // probably wants that value.  If it's the called object, then we instead
846
    // return this object.  That way we don't "lose" an extender layer when
847
    // chaining methods together.
848
    if ($return instanceof SelectQueryInterface) {
849
      return $this;
850
    }
851
    else {
852
      return $return;
853
    }
854
  }
855
}
856

    
857
/**
858
 * Query builder for SELECT statements.
859
 */
860
class SelectQuery extends Query implements SelectQueryInterface {
861

    
862
  /**
863
   * The fields to SELECT.
864
   *
865
   * @var array
866
   */
867
  protected $fields = array();
868

    
869
  /**
870
   * The expressions to SELECT as virtual fields.
871
   *
872
   * @var array
873
   */
874
  protected $expressions = array();
875

    
876
  /**
877
   * The tables against which to JOIN.
878
   *
879
   * This property is a nested array. Each entry is an array representing
880
   * a single table against which to join. The structure of each entry is:
881
   *
882
   * array(
883
   *   'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
884
   *   'table' => $table,
885
   *   'alias' => $alias_of_the_table,
886
   *   'condition' => $condition_clause_on_which_to_join,
887
   *   'arguments' => $array_of_arguments_for_placeholders_in_the condition.
888
   *   'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
889
   * )
890
   *
891
   * If $table is a string, it is taken as the name of a table. If it is
892
   * a SelectQuery object, it is taken as a subquery.
893
   *
894
   * @var array
895
   */
896
  protected $tables = array();
897

    
898
  /**
899
   * The fields by which to order this query.
900
   *
901
   * This is an associative array. The keys are the fields to order, and the value
902
   * is the direction to order, either ASC or DESC.
903
   *
904
   * @var array
905
   */
906
  protected $order = array();
907

    
908
  /**
909
   * The fields by which to group.
910
   *
911
   * @var array
912
   */
913
  protected $group = array();
914

    
915
  /**
916
   * The conditional object for the WHERE clause.
917
   *
918
   * @var DatabaseCondition
919
   */
920
  protected $where;
921

    
922
  /**
923
   * The conditional object for the HAVING clause.
924
   *
925
   * @var DatabaseCondition
926
   */
927
  protected $having;
928

    
929
  /**
930
   * Whether or not this query should be DISTINCT
931
   *
932
   * @var boolean
933
   */
934
  protected $distinct = FALSE;
935

    
936
  /**
937
   * The range limiters for this query.
938
   *
939
   * @var array
940
   */
941
  protected $range;
942

    
943
  /**
944
   * An array whose elements specify a query to UNION, and the UNION type. The
945
   * 'type' key may be '', 'ALL', or 'DISTINCT' to represent a 'UNION',
946
   * 'UNION ALL', or 'UNION DISTINCT' statement, respectively.
947
   *
948
   * All entries in this array will be applied from front to back, with the
949
   * first query to union on the right of the original query, the second union
950
   * to the right of the first, etc.
951
   *
952
   * @var array
953
   */
954
  protected $union = array();
955

    
956
  /**
957
   * Indicates if preExecute() has already been called.
958
   * @var boolean
959
   */
960
  protected $prepared = FALSE;
961

    
962
  /**
963
   * The FOR UPDATE status
964
   */
965
  protected $forUpdate = FALSE;
966

    
967
  public function __construct($table, $alias = NULL, DatabaseConnection $connection, $options = array()) {
968
    $options['return'] = Database::RETURN_STATEMENT;
969
    parent::__construct($connection, $options);
970
    $this->where = new DatabaseCondition('AND');
971
    $this->having = new DatabaseCondition('AND');
972
    $this->addJoin(NULL, $table, $alias);
973
  }
974

    
975
  /* Implementations of QueryAlterableInterface. */
976

    
977
  public function addTag($tag) {
978
    $this->alterTags[$tag] = 1;
979
    return $this;
980
  }
981

    
982
  public function hasTag($tag) {
983
    return isset($this->alterTags[$tag]);
984
  }
985

    
986
  public function hasAllTags() {
987
    $args = func_get_args();
988
    return !(boolean)array_diff($args, array_keys($this->alterTags));
989
  }
990

    
991
  public function hasAnyTag() {
992
    $args = func_get_args();
993
    return (boolean)array_intersect($args, array_keys($this->alterTags));
994
  }
995

    
996
  public function addMetaData($key, $object) {
997
    $this->alterMetaData[$key] = $object;
998
    return $this;
999
  }
1000

    
1001
  public function getMetaData($key) {
1002
    return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
1003
  }
1004

    
1005
  /* Implementations of QueryConditionInterface for the WHERE clause. */
1006

    
1007
  public function condition($field, $value = NULL, $operator = NULL) {
1008
    $this->where->condition($field, $value, $operator);
1009
    return $this;
1010
  }
1011

    
1012
  public function &conditions() {
1013
    return $this->where->conditions();
1014
  }
1015

    
1016
  public function arguments() {
1017
    if (!$this->compiled()) {
1018
      return NULL;
1019
    }
1020

    
1021
    $args = $this->where->arguments() + $this->having->arguments();
1022

    
1023
    foreach ($this->tables as $table) {
1024
      if ($table['arguments']) {
1025
        $args += $table['arguments'];
1026
      }
1027
      // If this table is a subquery, grab its arguments recursively.
1028
      if ($table['table'] instanceof SelectQueryInterface) {
1029
        $args += $table['table']->arguments();
1030
      }
1031
    }
1032

    
1033
    foreach ($this->expressions as $expression) {
1034
      if ($expression['arguments']) {
1035
        $args += $expression['arguments'];
1036
      }
1037
    }
1038

    
1039
    // If there are any dependent queries to UNION,
1040
    // incorporate their arguments recursively.
1041
    foreach ($this->union as $union) {
1042
      $args += $union['query']->arguments();
1043
    }
1044

    
1045
    return $args;
1046
  }
1047

    
1048
  public function where($snippet, $args = array()) {
1049
    $this->where->where($snippet, $args);
1050
    return $this;
1051
  }
1052

    
1053
  public function isNull($field) {
1054
    $this->where->isNull($field);
1055
    return $this;
1056
  }
1057

    
1058
  public function isNotNull($field) {
1059
    $this->where->isNotNull($field);
1060
    return $this;
1061
  }
1062

    
1063
  public function exists(SelectQueryInterface $select) {
1064
    $this->where->exists($select);
1065
    return $this;
1066
  }
1067

    
1068
  public function notExists(SelectQueryInterface $select) {
1069
    $this->where->notExists($select);
1070
    return $this;
1071
  }
1072

    
1073
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1074
    $this->where->compile($connection, $queryPlaceholder);
1075
    $this->having->compile($connection, $queryPlaceholder);
1076

    
1077
    foreach ($this->tables as $table) {
1078
      // If this table is a subquery, compile it recursively.
1079
      if ($table['table'] instanceof SelectQueryInterface) {
1080
        $table['table']->compile($connection, $queryPlaceholder);
1081
      }
1082
    }
1083

    
1084
    // If there are any dependent queries to UNION, compile it recursively.
1085
    foreach ($this->union as $union) {
1086
      $union['query']->compile($connection, $queryPlaceholder);
1087
    }
1088
  }
1089

    
1090
  public function compiled() {
1091
    if (!$this->where->compiled() || !$this->having->compiled()) {
1092
      return FALSE;
1093
    }
1094

    
1095
    foreach ($this->tables as $table) {
1096
      // If this table is a subquery, check its status recursively.
1097
      if ($table['table'] instanceof SelectQueryInterface) {
1098
        if (!$table['table']->compiled()) {
1099
          return FALSE;
1100
        }
1101
      }
1102
    }
1103

    
1104
    foreach ($this->union as $union) {
1105
      if (!$union['query']->compiled()) {
1106
        return FALSE;
1107
      }
1108
    }
1109

    
1110
    return TRUE;
1111
  }
1112

    
1113
  /* Implementations of QueryConditionInterface for the HAVING clause. */
1114

    
1115
  public function havingCondition($field, $value = NULL, $operator = NULL) {
1116
    $this->having->condition($field, $value, $operator);
1117
    return $this;
1118
  }
1119

    
1120
  public function &havingConditions() {
1121
    return $this->having->conditions();
1122
  }
1123

    
1124
  public function havingArguments() {
1125
    return $this->having->arguments();
1126
  }
1127

    
1128
  public function having($snippet, $args = array()) {
1129
    $this->having->where($snippet, $args);
1130
    return $this;
1131
  }
1132

    
1133
  public function havingCompile(DatabaseConnection $connection) {
1134
    return $this->having->compile($connection, $this);
1135
  }
1136

    
1137
  /* Implementations of QueryExtendableInterface. */
1138

    
1139
  public function extend($extender_name) {
1140
    $override_class = $extender_name . '_' . $this->connection->driver();
1141
    if (class_exists($override_class)) {
1142
      $extender_name = $override_class;
1143
    }
1144
    return new $extender_name($this, $this->connection);
1145
  }
1146

    
1147
  public function havingIsNull($field) {
1148
    $this->having->isNull($field);
1149
    return $this;
1150
  }
1151

    
1152
  public function havingIsNotNull($field) {
1153
    $this->having->isNotNull($field);
1154
    return $this;
1155
  }
1156

    
1157
  public function havingExists(SelectQueryInterface $select) {
1158
    $this->having->exists($select);
1159
    return $this;
1160
  }
1161

    
1162
  public function havingNotExists(SelectQueryInterface $select) {
1163
    $this->having->notExists($select);
1164
    return $this;
1165
  }
1166

    
1167
  public function forUpdate($set = TRUE) {
1168
    if (isset($set)) {
1169
      $this->forUpdate = $set;
1170
    }
1171
    return $this;
1172
  }
1173

    
1174
  /* Alter accessors to expose the query data to alter hooks. */
1175

    
1176
  public function &getFields() {
1177
    return $this->fields;
1178
  }
1179

    
1180
  public function &getExpressions() {
1181
    return $this->expressions;
1182
  }
1183

    
1184
  public function &getOrderBy() {
1185
    return $this->order;
1186
  }
1187

    
1188
  public function &getGroupBy() {
1189
    return $this->group;
1190
  }
1191

    
1192
  public function &getTables() {
1193
    return $this->tables;
1194
  }
1195

    
1196
  public function &getUnion() {
1197
    return $this->union;
1198
  }
1199

    
1200
  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
1201
    if (!isset($queryPlaceholder)) {
1202
      $queryPlaceholder = $this;
1203
    }
1204
    $this->compile($this->connection, $queryPlaceholder);
1205
    return $this->arguments();
1206
  }
1207

    
1208
  /**
1209
   * Indicates if preExecute() has already been called on that object.
1210
   */
1211
  public function isPrepared() {
1212
    return $this->prepared;
1213
  }
1214

    
1215
  /**
1216
   * Generic preparation and validation for a SELECT query.
1217
   *
1218
   * @return
1219
   *   TRUE if the validation was successful, FALSE if not.
1220
   */
1221
  public function preExecute(SelectQueryInterface $query = NULL) {
1222
    // If no query object is passed in, use $this.
1223
    if (!isset($query)) {
1224
      $query = $this;
1225
    }
1226

    
1227
    // Only execute this once.
1228
    if ($query->isPrepared()) {
1229
      return TRUE;
1230
    }
1231

    
1232
    // Modules may alter all queries or only those having a particular tag.
1233
    if (isset($this->alterTags)) {
1234
      // Many contrib modules assume that query tags used for access-checking
1235
      // purposes follow the pattern $entity_type . '_access'. But this is
1236
      // not the case for taxonomy terms, since core used to add term_access
1237
      // instead of taxonomy_term_access to its queries. Provide backwards
1238
      // compatibility by adding both tags here instead of attempting to fix
1239
      // all contrib modules in a coordinated effort.
1240
      // TODO:
1241
      // - Extract this mechanism into a hook as part of a public (non-security)
1242
      //   issue.
1243
      // - Emit E_USER_DEPRECATED if term_access is used.
1244
      //   https://www.drupal.org/node/2575081
1245
      $term_access_tags = array('term_access' => 1, 'taxonomy_term_access' => 1);
1246
      if (array_intersect_key($this->alterTags, $term_access_tags)) {
1247
        $this->alterTags += $term_access_tags;
1248
      }
1249
      $hooks = array('query');
1250
      foreach ($this->alterTags as $tag => $value) {
1251
        $hooks[] = 'query_' . $tag;
1252
      }
1253
      drupal_alter($hooks, $query);
1254
    }
1255

    
1256
    $this->prepared = TRUE;
1257

    
1258
    // Now also prepare any sub-queries.
1259
    foreach ($this->tables as $table) {
1260
      if ($table['table'] instanceof SelectQueryInterface) {
1261
        $table['table']->preExecute();
1262
      }
1263
    }
1264

    
1265
    foreach ($this->union as $union) {
1266
      $union['query']->preExecute();
1267
    }
1268

    
1269
    return $this->prepared;
1270
  }
1271

    
1272
  public function execute() {
1273
    // If validation fails, simply return NULL.
1274
    // Note that validation routines in preExecute() may throw exceptions instead.
1275
    if (!$this->preExecute()) {
1276
      return NULL;
1277
    }
1278

    
1279
    $args = $this->getArguments();
1280
    return $this->connection->query((string) $this, $args, $this->queryOptions);
1281
  }
1282

    
1283
  public function distinct($distinct = TRUE) {
1284
    $this->distinct = $distinct;
1285
    return $this;
1286
  }
1287

    
1288
  public function addField($table_alias, $field, $alias = NULL) {
1289
    // If no alias is specified, first try the field name itself.
1290
    if (empty($alias)) {
1291
      $alias = $field;
1292
    }
1293

    
1294
    // If that's already in use, try the table name and field name.
1295
    if (!empty($this->fields[$alias])) {
1296
      $alias = $table_alias . '_' . $field;
1297
    }
1298

    
1299
    // If that is already used, just add a counter until we find an unused alias.
1300
    $alias_candidate = $alias;
1301
    $count = 2;
1302
    while (!empty($this->fields[$alias_candidate])) {
1303
      $alias_candidate = $alias . '_' . $count++;
1304
    }
1305
    $alias = $alias_candidate;
1306

    
1307
    $this->fields[$alias] = array(
1308
      'field' => $field,
1309
      'table' => $table_alias,
1310
      'alias' => $alias,
1311
    );
1312

    
1313
    return $alias;
1314
  }
1315

    
1316
  public function fields($table_alias, array $fields = array()) {
1317

    
1318
    if ($fields) {
1319
      foreach ($fields as $field) {
1320
        // We don't care what alias was assigned.
1321
        $this->addField($table_alias, $field);
1322
      }
1323
    }
1324
    else {
1325
      // We want all fields from this table.
1326
      $this->tables[$table_alias]['all_fields'] = TRUE;
1327
    }
1328

    
1329
    return $this;
1330
  }
1331

    
1332
  public function addExpression($expression, $alias = NULL, $arguments = array()) {
1333
    if (empty($alias)) {
1334
      $alias = 'expression';
1335
    }
1336

    
1337
    $alias_candidate = $alias;
1338
    $count = 2;
1339
    while (!empty($this->expressions[$alias_candidate])) {
1340
      $alias_candidate = $alias . '_' . $count++;
1341
    }
1342
    $alias = $alias_candidate;
1343

    
1344
    $this->expressions[$alias] = array(
1345
      'expression' => $expression,
1346
      'alias' => $alias,
1347
      'arguments' => $arguments,
1348
    );
1349

    
1350
    return $alias;
1351
  }
1352

    
1353
  public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1354
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1355
  }
1356

    
1357
  public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1358
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1359
  }
1360

    
1361
  public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1362
    return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
1363
  }
1364

    
1365
  public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1366
    return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
1367
  }
1368

    
1369
  public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
1370

    
1371
    if (empty($alias)) {
1372
      if ($table instanceof SelectQueryInterface) {
1373
        $alias = 'subquery';
1374
      }
1375
      else {
1376
        $alias = $table;
1377
      }
1378
    }
1379

    
1380
    $alias_candidate = $alias;
1381
    $count = 2;
1382
    while (!empty($this->tables[$alias_candidate])) {
1383
      $alias_candidate = $alias . '_' . $count++;
1384
    }
1385
    $alias = $alias_candidate;
1386

    
1387
    if (is_string($condition)) {
1388
      $condition = str_replace('%alias', $alias, $condition);
1389
    }
1390

    
1391
    $this->tables[$alias] = array(
1392
      'join type' => $type,
1393
      'table' => $table,
1394
      'alias' => $alias,
1395
      'condition' => $condition,
1396
      'arguments' => $arguments,
1397
    );
1398

    
1399
    return $alias;
1400
  }
1401

    
1402
  public function orderBy($field, $direction = 'ASC') {
1403
    // Only allow ASC and DESC, default to ASC.
1404
    $direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC';
1405
    $this->order[$field] = $direction;
1406
    return $this;
1407
  }
1408

    
1409
  public function orderRandom() {
1410
    $alias = $this->addExpression('RAND()', 'random_field');
1411
    $this->orderBy($alias);
1412
    return $this;
1413
  }
1414

    
1415
  public function range($start = NULL, $length = NULL) {
1416
    $this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array();
1417
    return $this;
1418
  }
1419

    
1420
  public function union(SelectQueryInterface $query, $type = '') {
1421
    // Handle UNION aliasing.
1422
    switch ($type) {
1423
      // Fold UNION DISTINCT to UNION for better cross database support.
1424
      case 'DISTINCT':
1425
      case '':
1426
        $type = 'UNION';
1427
        break;
1428

    
1429
      case 'ALL':
1430
        $type = 'UNION ALL';
1431
      default:
1432
    }
1433

    
1434
    $this->union[] = array(
1435
      'type' => $type,
1436
      'query' => $query,
1437
    );
1438

    
1439
    return $this;
1440
  }
1441

    
1442
  public function groupBy($field) {
1443
    $this->group[$field] = $field;
1444
    return $this;
1445
  }
1446

    
1447
  public function countQuery() {
1448
    // Create our new query object that we will mutate into a count query.
1449
    $count = clone($this);
1450

    
1451
    $group_by = $count->getGroupBy();
1452
    $having = $count->havingConditions();
1453

    
1454
    if (!$count->distinct && !isset($having[0])) {
1455
      // When not executing a distinct query, we can zero-out existing fields
1456
      // and expressions that are not used by a GROUP BY or HAVING. Fields
1457
      // listed in a GROUP BY or HAVING clause need to be present in the
1458
      // query.
1459
      $fields =& $count->getFields();
1460
      foreach (array_keys($fields) as $field) {
1461
        if (empty($group_by[$field])) {
1462
          unset($fields[$field]);
1463
        }
1464
      }
1465

    
1466
      $expressions =& $count->getExpressions();
1467
      foreach (array_keys($expressions) as $field) {
1468
        if (empty($group_by[$field])) {
1469
          unset($expressions[$field]);
1470
        }
1471
      }
1472

    
1473
      // Also remove 'all_fields' statements, which are expanded into tablename.*
1474
      // when the query is executed.
1475
      foreach ($count->tables as $alias => &$table) {
1476
        unset($table['all_fields']);
1477
      }
1478
    }
1479

    
1480
    // If we've just removed all fields from the query, make sure there is at
1481
    // least one so that the query still runs.
1482
    $count->addExpression('1');
1483

    
1484
    // Ordering a count query is a waste of cycles, and breaks on some
1485
    // databases anyway.
1486
    $orders = &$count->getOrderBy();
1487
    $orders = array();
1488

    
1489
    if ($count->distinct && !empty($group_by)) {
1490
      // If the query is distinct and contains a GROUP BY, we need to remove the
1491
      // distinct because SQL99 does not support counting on distinct multiple fields.
1492
      $count->distinct = FALSE;
1493
    }
1494

    
1495
    $query = $this->connection->select($count);
1496
    $query->addExpression('COUNT(*)');
1497

    
1498
    return $query;
1499
  }
1500

    
1501
  public function __toString() {
1502
    // For convenience, we compile the query ourselves if the caller forgot
1503
    // to do it. This allows constructs like "(string) $query" to work. When
1504
    // the query will be executed, it will be recompiled using the proper
1505
    // placeholder generator anyway.
1506
    if (!$this->compiled()) {
1507
      $this->compile($this->connection, $this);
1508
    }
1509

    
1510
    // Create a sanitized comment string to prepend to the query.
1511
    $comments = $this->connection->makeComment($this->comments);
1512

    
1513
    // SELECT
1514
    $query = $comments . 'SELECT ';
1515
    if ($this->distinct) {
1516
      $query .= 'DISTINCT ';
1517
    }
1518

    
1519
    // FIELDS and EXPRESSIONS
1520
    $fields = array();
1521
    foreach ($this->tables as $alias => $table) {
1522
      if (!empty($table['all_fields'])) {
1523
        $fields[] = $this->connection->escapeTable($alias) . '.*';
1524
      }
1525
    }
1526
    foreach ($this->fields as $alias => $field) {
1527
      // Always use the AS keyword for field aliases, as some
1528
      // databases require it (e.g., PostgreSQL).
1529
      $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
1530
    }
1531
    foreach ($this->expressions as $alias => $expression) {
1532
      $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
1533
    }
1534
    $query .= implode(', ', $fields);
1535

    
1536

    
1537
    // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
1538
    $query .= "\nFROM ";
1539
    foreach ($this->tables as $alias => $table) {
1540
      $query .= "\n";
1541
      if (isset($table['join type'])) {
1542
        $query .= $table['join type'] . ' JOIN ';
1543
      }
1544

    
1545
      // If the table is a subquery, compile it and integrate it into this query.
1546
      if ($table['table'] instanceof SelectQueryInterface) {
1547
        // Run preparation steps on this sub-query before converting to string.
1548
        $subquery = $table['table'];
1549
        $subquery->preExecute();
1550
        $table_string = '(' . (string) $subquery . ')';
1551
      }
1552
      else {
1553
        $table_string = '{' . $this->connection->escapeTable($table['table']) . '}';
1554
      }
1555

    
1556
      // Don't use the AS keyword for table aliases, as some
1557
      // databases don't support it (e.g., Oracle).
1558
      $query .=  $table_string . ' ' . $this->connection->escapeTable($table['alias']);
1559

    
1560
      if (!empty($table['condition'])) {
1561
        $query .= ' ON ' . $table['condition'];
1562
      }
1563
    }
1564

    
1565
    // WHERE
1566
    if (count($this->where)) {
1567
      // There is an implicit string cast on $this->condition.
1568
      $query .= "\nWHERE " . $this->where;
1569
    }
1570

    
1571
    // GROUP BY
1572
    if ($this->group) {
1573
      $query .= "\nGROUP BY " . implode(', ', $this->group);
1574
    }
1575

    
1576
    // HAVING
1577
    if (count($this->having)) {
1578
      // There is an implicit string cast on $this->having.
1579
      $query .= "\nHAVING " . $this->having;
1580
    }
1581

    
1582
    // ORDER BY
1583
    if ($this->order) {
1584
      $query .= "\nORDER BY ";
1585
      $fields = array();
1586
      foreach ($this->order as $field => $direction) {
1587
        $fields[] = $field . ' ' . $direction;
1588
      }
1589
      $query .= implode(', ', $fields);
1590
    }
1591

    
1592
    // RANGE
1593
    // There is no universal SQL standard for handling range or limit clauses.
1594
    // Fortunately, all core-supported databases use the same range syntax.
1595
    // Databases that need a different syntax can override this method and
1596
    // do whatever alternate logic they need to.
1597
    if (!empty($this->range)) {
1598
      $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
1599
    }
1600

    
1601
    // UNION is a little odd, as the select queries to combine are passed into
1602
    // this query, but syntactically they all end up on the same level.
1603
    if ($this->union) {
1604
      foreach ($this->union as $union) {
1605
        $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
1606
      }
1607
    }
1608

    
1609
    if ($this->forUpdate) {
1610
      $query .= ' FOR UPDATE';
1611
    }
1612

    
1613
    return $query;
1614
  }
1615

    
1616
  public function __clone() {
1617
    // On cloning, also clone the dependent objects. However, we do not
1618
    // want to clone the database connection object as that would duplicate the
1619
    // connection itself.
1620

    
1621
    $this->where = clone($this->where);
1622
    $this->having = clone($this->having);
1623
    foreach ($this->union as $key => $aggregate) {
1624
      $this->union[$key]['query'] = clone($aggregate['query']);
1625
    }
1626
  }
1627
}
1628

    
1629
/**
1630
 * @} End of "addtogroup database".
1631
 */