Projet

Général

Profil

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

root / htmltest / includes / database / select.inc @ 85ad3d82

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".
381
   * @return SelectQueryInterface
382
   *   The called object.
383
   */
384
  public function orderBy($field, $direction = 'ASC');
385

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
581
  /* Implementations of QueryAlterableInterface. */
582

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
663
  /* Implementations of QueryExtendableInterface. */
664

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
974
  /* Implementations of QueryAlterableInterface. */
975

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1044
    return $args;
1045
  }
1046

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

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

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

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

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

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

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

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

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

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

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

    
1109
    return TRUE;
1110
  }
1111

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

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

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

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

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

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

    
1136
  /* Implementations of QueryExtendableInterface. */
1137

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1231
    // Modules may alter all queries or only those having a particular tag.
1232
    if (isset($this->alterTags)) {
1233
      $hooks = array('query');
1234
      foreach ($this->alterTags as $tag => $value) {
1235
        $hooks[] = 'query_' . $tag;
1236
      }
1237
      drupal_alter($hooks, $query);
1238
    }
1239

    
1240
    $this->prepared = TRUE;
1241

    
1242
    // Now also prepare any sub-queries.
1243
    foreach ($this->tables as $table) {
1244
      if ($table['table'] instanceof SelectQueryInterface) {
1245
        $table['table']->preExecute();
1246
      }
1247
    }
1248

    
1249
    foreach ($this->union as $union) {
1250
      $union['query']->preExecute();
1251
    }
1252

    
1253
    return $this->prepared;
1254
  }
1255

    
1256
  public function execute() {
1257
    // If validation fails, simply return NULL.
1258
    // Note that validation routines in preExecute() may throw exceptions instead.
1259
    if (!$this->preExecute()) {
1260
      return NULL;
1261
    }
1262

    
1263
    $args = $this->getArguments();
1264
    return $this->connection->query((string) $this, $args, $this->queryOptions);
1265
  }
1266

    
1267
  public function distinct($distinct = TRUE) {
1268
    $this->distinct = $distinct;
1269
    return $this;
1270
  }
1271

    
1272
  public function addField($table_alias, $field, $alias = NULL) {
1273
    // If no alias is specified, first try the field name itself.
1274
    if (empty($alias)) {
1275
      $alias = $field;
1276
    }
1277

    
1278
    // If that's already in use, try the table name and field name.
1279
    if (!empty($this->fields[$alias])) {
1280
      $alias = $table_alias . '_' . $field;
1281
    }
1282

    
1283
    // If that is already used, just add a counter until we find an unused alias.
1284
    $alias_candidate = $alias;
1285
    $count = 2;
1286
    while (!empty($this->fields[$alias_candidate])) {
1287
      $alias_candidate = $alias . '_' . $count++;
1288
    }
1289
    $alias = $alias_candidate;
1290

    
1291
    $this->fields[$alias] = array(
1292
      'field' => $field,
1293
      'table' => $table_alias,
1294
      'alias' => $alias,
1295
    );
1296

    
1297
    return $alias;
1298
  }
1299

    
1300
  public function fields($table_alias, array $fields = array()) {
1301

    
1302
    if ($fields) {
1303
      foreach ($fields as $field) {
1304
        // We don't care what alias was assigned.
1305
        $this->addField($table_alias, $field);
1306
      }
1307
    }
1308
    else {
1309
      // We want all fields from this table.
1310
      $this->tables[$table_alias]['all_fields'] = TRUE;
1311
    }
1312

    
1313
    return $this;
1314
  }
1315

    
1316
  public function addExpression($expression, $alias = NULL, $arguments = array()) {
1317
    if (empty($alias)) {
1318
      $alias = 'expression';
1319
    }
1320

    
1321
    $alias_candidate = $alias;
1322
    $count = 2;
1323
    while (!empty($this->expressions[$alias_candidate])) {
1324
      $alias_candidate = $alias . '_' . $count++;
1325
    }
1326
    $alias = $alias_candidate;
1327

    
1328
    $this->expressions[$alias] = array(
1329
      'expression' => $expression,
1330
      'alias' => $alias,
1331
      'arguments' => $arguments,
1332
    );
1333

    
1334
    return $alias;
1335
  }
1336

    
1337
  public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1338
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1339
  }
1340

    
1341
  public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1342
    return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
1343
  }
1344

    
1345
  public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1346
    return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
1347
  }
1348

    
1349
  public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
1350
    return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
1351
  }
1352

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

    
1355
    if (empty($alias)) {
1356
      if ($table instanceof SelectQueryInterface) {
1357
        $alias = 'subquery';
1358
      }
1359
      else {
1360
        $alias = $table;
1361
      }
1362
    }
1363

    
1364
    $alias_candidate = $alias;
1365
    $count = 2;
1366
    while (!empty($this->tables[$alias_candidate])) {
1367
      $alias_candidate = $alias . '_' . $count++;
1368
    }
1369
    $alias = $alias_candidate;
1370

    
1371
    if (is_string($condition)) {
1372
      $condition = str_replace('%alias', $alias, $condition);
1373
    }
1374

    
1375
    $this->tables[$alias] = array(
1376
      'join type' => $type,
1377
      'table' => $table,
1378
      'alias' => $alias,
1379
      'condition' => $condition,
1380
      'arguments' => $arguments,
1381
    );
1382

    
1383
    return $alias;
1384
  }
1385

    
1386
  public function orderBy($field, $direction = 'ASC') {
1387
    $this->order[$field] = $direction;
1388
    return $this;
1389
  }
1390

    
1391
  public function orderRandom() {
1392
    $alias = $this->addExpression('RAND()', 'random_field');
1393
    $this->orderBy($alias);
1394
    return $this;
1395
  }
1396

    
1397
  public function range($start = NULL, $length = NULL) {
1398
    $this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array();
1399
    return $this;
1400
  }
1401

    
1402
  public function union(SelectQueryInterface $query, $type = '') {
1403
    // Handle UNION aliasing.
1404
    switch ($type) {
1405
      // Fold UNION DISTINCT to UNION for better cross database support.
1406
      case 'DISTINCT':
1407
      case '':
1408
        $type = 'UNION';
1409
        break;
1410

    
1411
      case 'ALL':
1412
        $type = 'UNION ALL';
1413
      default:
1414
    }
1415

    
1416
    $this->union[] = array(
1417
      'type' => $type,
1418
      'query' => $query,
1419
    );
1420

    
1421
    return $this;
1422
  }
1423

    
1424
  public function groupBy($field) {
1425
    $this->group[$field] = $field;
1426
    return $this;
1427
  }
1428

    
1429
  public function countQuery() {
1430
    // Create our new query object that we will mutate into a count query.
1431
    $count = clone($this);
1432

    
1433
    $group_by = $count->getGroupBy();
1434
    $having = $count->havingConditions();
1435

    
1436
    if (!$count->distinct && !isset($having[0])) {
1437
      // When not executing a distinct query, we can zero-out existing fields
1438
      // and expressions that are not used by a GROUP BY or HAVING. Fields
1439
      // listed in a GROUP BY or HAVING clause need to be present in the
1440
      // query.
1441
      $fields =& $count->getFields();
1442
      foreach (array_keys($fields) as $field) {
1443
        if (empty($group_by[$field])) {
1444
          unset($fields[$field]);
1445
        }
1446
      }
1447

    
1448
      $expressions =& $count->getExpressions();
1449
      foreach (array_keys($expressions) as $field) {
1450
        if (empty($group_by[$field])) {
1451
          unset($expressions[$field]);
1452
        }
1453
      }
1454

    
1455
      // Also remove 'all_fields' statements, which are expanded into tablename.*
1456
      // when the query is executed.
1457
      foreach ($count->tables as $alias => &$table) {
1458
        unset($table['all_fields']);
1459
      }
1460
    }
1461

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

    
1466
    // Ordering a count query is a waste of cycles, and breaks on some
1467
    // databases anyway.
1468
    $orders = &$count->getOrderBy();
1469
    $orders = array();
1470

    
1471
    if ($count->distinct && !empty($group_by)) {
1472
      // If the query is distinct and contains a GROUP BY, we need to remove the
1473
      // distinct because SQL99 does not support counting on distinct multiple fields.
1474
      $count->distinct = FALSE;
1475
    }
1476

    
1477
    $query = $this->connection->select($count);
1478
    $query->addExpression('COUNT(*)');
1479

    
1480
    return $query;
1481
  }
1482

    
1483
  public function __toString() {
1484
    // For convenience, we compile the query ourselves if the caller forgot
1485
    // to do it. This allows constructs like "(string) $query" to work. When
1486
    // the query will be executed, it will be recompiled using the proper
1487
    // placeholder generator anyway.
1488
    if (!$this->compiled()) {
1489
      $this->compile($this->connection, $this);
1490
    }
1491

    
1492
    // Create a sanitized comment string to prepend to the query.
1493
    $comments = $this->connection->makeComment($this->comments);
1494

    
1495
    // SELECT
1496
    $query = $comments . 'SELECT ';
1497
    if ($this->distinct) {
1498
      $query .= 'DISTINCT ';
1499
    }
1500

    
1501
    // FIELDS and EXPRESSIONS
1502
    $fields = array();
1503
    foreach ($this->tables as $alias => $table) {
1504
      if (!empty($table['all_fields'])) {
1505
        $fields[] = $this->connection->escapeTable($alias) . '.*';
1506
      }
1507
    }
1508
    foreach ($this->fields as $alias => $field) {
1509
      // Always use the AS keyword for field aliases, as some
1510
      // databases require it (e.g., PostgreSQL).
1511
      $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
1512
    }
1513
    foreach ($this->expressions as $alias => $expression) {
1514
      $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
1515
    }
1516
    $query .= implode(', ', $fields);
1517

    
1518

    
1519
    // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
1520
    $query .= "\nFROM ";
1521
    foreach ($this->tables as $alias => $table) {
1522
      $query .= "\n";
1523
      if (isset($table['join type'])) {
1524
        $query .= $table['join type'] . ' JOIN ';
1525
      }
1526

    
1527
      // If the table is a subquery, compile it and integrate it into this query.
1528
      if ($table['table'] instanceof SelectQueryInterface) {
1529
        // Run preparation steps on this sub-query before converting to string.
1530
        $subquery = $table['table'];
1531
        $subquery->preExecute();
1532
        $table_string = '(' . (string) $subquery . ')';
1533
      }
1534
      else {
1535
        $table_string = '{' . $this->connection->escapeTable($table['table']) . '}';
1536
      }
1537

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

    
1542
      if (!empty($table['condition'])) {
1543
        $query .= ' ON ' . $table['condition'];
1544
      }
1545
    }
1546

    
1547
    // WHERE
1548
    if (count($this->where)) {
1549
      // There is an implicit string cast on $this->condition.
1550
      $query .= "\nWHERE " . $this->where;
1551
    }
1552

    
1553
    // GROUP BY
1554
    if ($this->group) {
1555
      $query .= "\nGROUP BY " . implode(', ', $this->group);
1556
    }
1557

    
1558
    // HAVING
1559
    if (count($this->having)) {
1560
      // There is an implicit string cast on $this->having.
1561
      $query .= "\nHAVING " . $this->having;
1562
    }
1563

    
1564
    // ORDER BY
1565
    if ($this->order) {
1566
      $query .= "\nORDER BY ";
1567
      $fields = array();
1568
      foreach ($this->order as $field => $direction) {
1569
        $fields[] = $field . ' ' . $direction;
1570
      }
1571
      $query .= implode(', ', $fields);
1572
    }
1573

    
1574
    // RANGE
1575
    // There is no universal SQL standard for handling range or limit clauses.
1576
    // Fortunately, all core-supported databases use the same range syntax.
1577
    // Databases that need a different syntax can override this method and
1578
    // do whatever alternate logic they need to.
1579
    if (!empty($this->range)) {
1580
      $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
1581
    }
1582

    
1583
    // UNION is a little odd, as the select queries to combine are passed into
1584
    // this query, but syntactically they all end up on the same level.
1585
    if ($this->union) {
1586
      foreach ($this->union as $union) {
1587
        $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
1588
      }
1589
    }
1590

    
1591
    if ($this->forUpdate) {
1592
      $query .= ' FOR UPDATE';
1593
    }
1594

    
1595
    return $query;
1596
  }
1597

    
1598
  public function __clone() {
1599
    // On cloning, also clone the dependent objects. However, we do not
1600
    // want to clone the database connection object as that would duplicate the
1601
    // connection itself.
1602

    
1603
    $this->where = clone($this->where);
1604
    $this->having = clone($this->having);
1605
    foreach ($this->union as $key => $aggregate) {
1606
      $this->union[$key]['query'] = clone($aggregate['query']);
1607
    }
1608
  }
1609
}
1610

    
1611
/**
1612
 * @} End of "addtogroup database".
1613
 */