Projet

Général

Profil

Paste
Télécharger (48,7 ko) Statistiques
| Branche: | Révision:

root / drupal7 / includes / database / select.inc @ 76597ebf

1 85ad3d82 Assos Assos
<?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 b4adf10d Assos Assos
   *   The direction to sort. Legal values are "ASC" and "DESC". Any other value
381
   *   will be converted to "ASC".
382 85ad3d82 Assos Assos
   * @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 4444412d Julien Enselme
    return call_user_func_array(array($this->query, 'hasAnyTag'), $args);
601 85ad3d82 Assos Assos
  }
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
      $hooks = array('query');
1235
      foreach ($this->alterTags as $tag => $value) {
1236
        $hooks[] = 'query_' . $tag;
1237
      }
1238
      drupal_alter($hooks, $query);
1239
    }
1240
1241
    $this->prepared = TRUE;
1242
1243
    // Now also prepare any sub-queries.
1244
    foreach ($this->tables as $table) {
1245
      if ($table['table'] instanceof SelectQueryInterface) {
1246
        $table['table']->preExecute();
1247
      }
1248
    }
1249
1250
    foreach ($this->union as $union) {
1251
      $union['query']->preExecute();
1252
    }
1253
1254
    return $this->prepared;
1255
  }
1256
1257
  public function execute() {
1258
    // If validation fails, simply return NULL.
1259
    // Note that validation routines in preExecute() may throw exceptions instead.
1260
    if (!$this->preExecute()) {
1261
      return NULL;
1262
    }
1263
1264
    $args = $this->getArguments();
1265
    return $this->connection->query((string) $this, $args, $this->queryOptions);
1266
  }
1267
1268
  public function distinct($distinct = TRUE) {
1269
    $this->distinct = $distinct;
1270
    return $this;
1271
  }
1272
1273
  public function addField($table_alias, $field, $alias = NULL) {
1274
    // If no alias is specified, first try the field name itself.
1275
    if (empty($alias)) {
1276
      $alias = $field;
1277
    }
1278
1279
    // If that's already in use, try the table name and field name.
1280
    if (!empty($this->fields[$alias])) {
1281
      $alias = $table_alias . '_' . $field;
1282
    }
1283
1284
    // If that is already used, just add a counter until we find an unused alias.
1285
    $alias_candidate = $alias;
1286
    $count = 2;
1287
    while (!empty($this->fields[$alias_candidate])) {
1288
      $alias_candidate = $alias . '_' . $count++;
1289
    }
1290
    $alias = $alias_candidate;
1291
1292
    $this->fields[$alias] = array(
1293
      'field' => $field,
1294
      'table' => $table_alias,
1295
      'alias' => $alias,
1296
    );
1297
1298
    return $alias;
1299
  }
1300
1301
  public function fields($table_alias, array $fields = array()) {
1302
1303
    if ($fields) {
1304
      foreach ($fields as $field) {
1305
        // We don't care what alias was assigned.
1306
        $this->addField($table_alias, $field);
1307
      }
1308
    }
1309
    else {
1310
      // We want all fields from this table.
1311
      $this->tables[$table_alias]['all_fields'] = TRUE;
1312
    }
1313
1314
    return $this;
1315
  }
1316
1317
  public function addExpression($expression, $alias = NULL, $arguments = array()) {
1318
    if (empty($alias)) {
1319
      $alias = 'expression';
1320
    }
1321
1322
    $alias_candidate = $alias;
1323
    $count = 2;
1324
    while (!empty($this->expressions[$alias_candidate])) {
1325
      $alias_candidate = $alias . '_' . $count++;
1326
    }
1327
    $alias = $alias_candidate;
1328
1329
    $this->expressions[$alias] = array(
1330
      'expression' => $expression,
1331
      'alias' => $alias,
1332
      'arguments' => $arguments,
1333
    );
1334
1335
    return $alias;
1336
  }
1337
1338
  public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1339
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1340
  }
1341
1342
  public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1343
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1344
  }
1345
1346
  public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1347
    return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
1348
  }
1349
1350
  public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1351
    return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
1352
  }
1353
1354
  public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
1355
1356
    if (empty($alias)) {
1357
      if ($table instanceof SelectQueryInterface) {
1358
        $alias = 'subquery';
1359
      }
1360
      else {
1361
        $alias = $table;
1362
      }
1363
    }
1364
1365
    $alias_candidate = $alias;
1366
    $count = 2;
1367
    while (!empty($this->tables[$alias_candidate])) {
1368
      $alias_candidate = $alias . '_' . $count++;
1369
    }
1370
    $alias = $alias_candidate;
1371
1372
    if (is_string($condition)) {
1373
      $condition = str_replace('%alias', $alias, $condition);
1374
    }
1375
1376
    $this->tables[$alias] = array(
1377
      'join type' => $type,
1378
      'table' => $table,
1379
      'alias' => $alias,
1380
      'condition' => $condition,
1381
      'arguments' => $arguments,
1382
    );
1383
1384
    return $alias;
1385
  }
1386
1387
  public function orderBy($field, $direction = 'ASC') {
1388 b4adf10d Assos Assos
    // Only allow ASC and DESC, default to ASC.
1389
    $direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC';
1390 85ad3d82 Assos Assos
    $this->order[$field] = $direction;
1391
    return $this;
1392
  }
1393
1394
  public function orderRandom() {
1395
    $alias = $this->addExpression('RAND()', 'random_field');
1396
    $this->orderBy($alias);
1397
    return $this;
1398
  }
1399
1400
  public function range($start = NULL, $length = NULL) {
1401
    $this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array();
1402
    return $this;
1403
  }
1404
1405
  public function union(SelectQueryInterface $query, $type = '') {
1406
    // Handle UNION aliasing.
1407
    switch ($type) {
1408
      // Fold UNION DISTINCT to UNION for better cross database support.
1409
      case 'DISTINCT':
1410
      case '':
1411
        $type = 'UNION';
1412
        break;
1413
1414
      case 'ALL':
1415
        $type = 'UNION ALL';
1416
      default:
1417
    }
1418
1419
    $this->union[] = array(
1420
      'type' => $type,
1421
      'query' => $query,
1422
    );
1423
1424
    return $this;
1425
  }
1426
1427
  public function groupBy($field) {
1428
    $this->group[$field] = $field;
1429
    return $this;
1430
  }
1431
1432
  public function countQuery() {
1433
    // Create our new query object that we will mutate into a count query.
1434
    $count = clone($this);
1435
1436
    $group_by = $count->getGroupBy();
1437
    $having = $count->havingConditions();
1438
1439
    if (!$count->distinct && !isset($having[0])) {
1440
      // When not executing a distinct query, we can zero-out existing fields
1441
      // and expressions that are not used by a GROUP BY or HAVING. Fields
1442
      // listed in a GROUP BY or HAVING clause need to be present in the
1443
      // query.
1444
      $fields =& $count->getFields();
1445
      foreach (array_keys($fields) as $field) {
1446
        if (empty($group_by[$field])) {
1447
          unset($fields[$field]);
1448
        }
1449
      }
1450
1451
      $expressions =& $count->getExpressions();
1452
      foreach (array_keys($expressions) as $field) {
1453
        if (empty($group_by[$field])) {
1454
          unset($expressions[$field]);
1455
        }
1456
      }
1457
1458
      // Also remove 'all_fields' statements, which are expanded into tablename.*
1459
      // when the query is executed.
1460
      foreach ($count->tables as $alias => &$table) {
1461
        unset($table['all_fields']);
1462
      }
1463
    }
1464
1465
    // If we've just removed all fields from the query, make sure there is at
1466
    // least one so that the query still runs.
1467
    $count->addExpression('1');
1468
1469
    // Ordering a count query is a waste of cycles, and breaks on some
1470
    // databases anyway.
1471
    $orders = &$count->getOrderBy();
1472
    $orders = array();
1473
1474
    if ($count->distinct && !empty($group_by)) {
1475
      // If the query is distinct and contains a GROUP BY, we need to remove the
1476
      // distinct because SQL99 does not support counting on distinct multiple fields.
1477
      $count->distinct = FALSE;
1478
    }
1479
1480
    $query = $this->connection->select($count);
1481
    $query->addExpression('COUNT(*)');
1482
1483
    return $query;
1484
  }
1485
1486
  public function __toString() {
1487
    // For convenience, we compile the query ourselves if the caller forgot
1488
    // to do it. This allows constructs like "(string) $query" to work. When
1489
    // the query will be executed, it will be recompiled using the proper
1490
    // placeholder generator anyway.
1491
    if (!$this->compiled()) {
1492
      $this->compile($this->connection, $this);
1493
    }
1494
1495
    // Create a sanitized comment string to prepend to the query.
1496
    $comments = $this->connection->makeComment($this->comments);
1497
1498
    // SELECT
1499
    $query = $comments . 'SELECT ';
1500
    if ($this->distinct) {
1501
      $query .= 'DISTINCT ';
1502
    }
1503
1504
    // FIELDS and EXPRESSIONS
1505
    $fields = array();
1506
    foreach ($this->tables as $alias => $table) {
1507
      if (!empty($table['all_fields'])) {
1508
        $fields[] = $this->connection->escapeTable($alias) . '.*';
1509
      }
1510
    }
1511
    foreach ($this->fields as $alias => $field) {
1512
      // Always use the AS keyword for field aliases, as some
1513
      // databases require it (e.g., PostgreSQL).
1514
      $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
1515
    }
1516
    foreach ($this->expressions as $alias => $expression) {
1517
      $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
1518
    }
1519
    $query .= implode(', ', $fields);
1520
1521
1522
    // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
1523
    $query .= "\nFROM ";
1524
    foreach ($this->tables as $alias => $table) {
1525
      $query .= "\n";
1526
      if (isset($table['join type'])) {
1527
        $query .= $table['join type'] . ' JOIN ';
1528
      }
1529
1530
      // If the table is a subquery, compile it and integrate it into this query.
1531
      if ($table['table'] instanceof SelectQueryInterface) {
1532
        // Run preparation steps on this sub-query before converting to string.
1533
        $subquery = $table['table'];
1534
        $subquery->preExecute();
1535
        $table_string = '(' . (string) $subquery . ')';
1536
      }
1537
      else {
1538
        $table_string = '{' . $this->connection->escapeTable($table['table']) . '}';
1539
      }
1540
1541
      // Don't use the AS keyword for table aliases, as some
1542
      // databases don't support it (e.g., Oracle).
1543
      $query .=  $table_string . ' ' . $this->connection->escapeTable($table['alias']);
1544
1545
      if (!empty($table['condition'])) {
1546
        $query .= ' ON ' . $table['condition'];
1547
      }
1548
    }
1549
1550
    // WHERE
1551
    if (count($this->where)) {
1552
      // There is an implicit string cast on $this->condition.
1553
      $query .= "\nWHERE " . $this->where;
1554
    }
1555
1556
    // GROUP BY
1557
    if ($this->group) {
1558
      $query .= "\nGROUP BY " . implode(', ', $this->group);
1559
    }
1560
1561
    // HAVING
1562
    if (count($this->having)) {
1563
      // There is an implicit string cast on $this->having.
1564
      $query .= "\nHAVING " . $this->having;
1565
    }
1566
1567
    // ORDER BY
1568
    if ($this->order) {
1569
      $query .= "\nORDER BY ";
1570
      $fields = array();
1571
      foreach ($this->order as $field => $direction) {
1572
        $fields[] = $field . ' ' . $direction;
1573
      }
1574
      $query .= implode(', ', $fields);
1575
    }
1576
1577
    // RANGE
1578
    // There is no universal SQL standard for handling range or limit clauses.
1579
    // Fortunately, all core-supported databases use the same range syntax.
1580
    // Databases that need a different syntax can override this method and
1581
    // do whatever alternate logic they need to.
1582
    if (!empty($this->range)) {
1583
      $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
1584
    }
1585
1586
    // UNION is a little odd, as the select queries to combine are passed into
1587
    // this query, but syntactically they all end up on the same level.
1588
    if ($this->union) {
1589
      foreach ($this->union as $union) {
1590
        $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
1591
      }
1592
    }
1593
1594
    if ($this->forUpdate) {
1595
      $query .= ' FOR UPDATE';
1596
    }
1597
1598
    return $query;
1599
  }
1600
1601
  public function __clone() {
1602
    // On cloning, also clone the dependent objects. However, we do not
1603
    // want to clone the database connection object as that would duplicate the
1604
    // connection itself.
1605
1606
    $this->where = clone($this->where);
1607
    $this->having = clone($this->having);
1608
    foreach ($this->union as $key => $aggregate) {
1609
      $this->union[$key]['query'] = clone($aggregate['query']);
1610
    }
1611
  }
1612
}
1613
1614
/**
1615
 * @} End of "addtogroup database".
1616
 */