Projet

Général

Profil

Paste
Télécharger (56,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / includes / database / query.inc @ 6ae446a4

1
<?php
2

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

    
8
/**
9
 * @file
10
 * Non-specific Database query code. Used by all engines.
11
 */
12

    
13
/**
14
 * Interface for a conditional clause in a query.
15
 */
16
interface QueryConditionInterface {
17

    
18
  /**
19
   * Helper function: builds the most common conditional clauses.
20
   *
21
   * This method can take a variable number of parameters. If called with two
22
   * parameters, they are taken as $field and $value with $operator having a
23
   * value of IN if $value is an array and = otherwise.
24
   *
25
   * Do not use this method to test for NULL values. Instead, use
26
   * QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull().
27
   *
28
   * @param $field
29
   *   The name of the field to check. If you would like to add a more complex
30
   *   condition involving operators or functions, use where().
31
   * @param $value
32
   *   The value to test the field against. In most cases, this is a scalar.
33
   *   For more complex options, it is an array. The meaning of each element in
34
   *   the array is dependent on the $operator.
35
   * @param $operator
36
   *   The comparison operator, such as =, <, or >=. It also accepts more
37
   *   complex options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is
38
   *   an array, and = otherwise.
39
   *
40
   * @return QueryConditionInterface
41
   *   The called object.
42
   *
43
   * @see QueryConditionInterface::isNull()
44
   * @see QueryConditionInterface::isNotNull()
45
   */
46
  public function condition($field, $value = NULL, $operator = NULL);
47

    
48
  /**
49
   * Adds an arbitrary WHERE clause to the query.
50
   *
51
   * @param $snippet
52
   *   A portion of a WHERE clause as a prepared statement. It must use named
53
   *   placeholders, not ? placeholders.
54
   * @param $args
55
   *   An associative array of arguments.
56
   *
57
   * @return QueryConditionInterface
58
   *   The called object.
59
   */
60
  public function where($snippet, $args = array());
61

    
62
  /**
63
   * Sets a condition that the specified field be NULL.
64
   *
65
   * @param $field
66
   *   The name of the field to check.
67
   *
68
   * @return QueryConditionInterface
69
   *   The called object.
70
   */
71
  public function isNull($field);
72

    
73
  /**
74
   * Sets a condition that the specified field be NOT NULL.
75
   *
76
   * @param $field
77
   *   The name of the field to check.
78
   *
79
   * @return QueryConditionInterface
80
   *   The called object.
81
   */
82
  public function isNotNull($field);
83

    
84
  /**
85
   * Sets a condition that the specified subquery returns values.
86
   *
87
   * @param SelectQueryInterface $select
88
   *   The subquery that must contain results.
89
   *
90
   * @return QueryConditionInterface
91
   *   The called object.
92
   */
93
  public function exists(SelectQueryInterface $select);
94

    
95
  /**
96
   * Sets a condition that the specified subquery returns no values.
97
   *
98
   * @param SelectQueryInterface $select
99
   *   The subquery that must not contain results.
100
   *
101
   * @return QueryConditionInterface
102
   *   The called object.
103
   */
104
  public function notExists(SelectQueryInterface $select);
105

    
106
  /**
107
   * Gets a complete list of all conditions in this conditional clause.
108
   *
109
   * This method returns by reference. That allows alter hooks to access the
110
   * data structure directly and manipulate it before it gets compiled.
111
   *
112
   * The data structure that is returned is an indexed array of entries, where
113
   * each entry looks like the following:
114
   * @code
115
   * array(
116
   *   'field' => $field,
117
   *   'value' => $value,
118
   *   'operator' => $operator,
119
   * );
120
   * @endcode
121
   *
122
   * In the special case that $operator is NULL, the $field is taken as a raw
123
   * SQL snippet (possibly containing a function) and $value is an associative
124
   * array of placeholders for the snippet.
125
   *
126
   * There will also be a single array entry of #conjunction, which is the
127
   * conjunction that will be applied to the array, such as AND.
128
   */
129
  public function &conditions();
130

    
131
  /**
132
   * Gets a complete list of all values to insert into the prepared statement.
133
   *
134
   * @return
135
   *   An associative array of placeholders and values.
136
   */
137
  public function arguments();
138

    
139
  /**
140
   * Compiles the saved conditions for later retrieval.
141
   *
142
   * This method does not return anything, but simply prepares data to be
143
   * retrieved via __toString() and arguments().
144
   *
145
   * @param $connection
146
   *   The database connection for which to compile the conditionals.
147
   * @param $queryPlaceholder
148
   *   The query this condition belongs to. If not given, the current query is
149
   *   used.
150
   */
151
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder);
152

    
153
  /**
154
   * Check whether a condition has been previously compiled.
155
   *
156
   * @return
157
   *   TRUE if the condition has been previously compiled.
158
   */
159
  public function compiled();
160
}
161

    
162

    
163
/**
164
 * Interface for a query that can be manipulated via an alter hook.
165
 */
166
interface QueryAlterableInterface {
167

    
168
  /**
169
   * Adds a tag to a query.
170
   *
171
   * Tags are strings that identify a query. A query may have any number of
172
   * tags. Tags are used to mark a query so that alter hooks may decide if they
173
   * wish to take action. Tags should be all lower-case and contain only
174
   * letters, numbers, and underscore, and start with a letter. That is, they
175
   * should follow the same rules as PHP identifiers in general.
176
   *
177
   * @param $tag
178
   *   The tag to add.
179
   *
180
   * @return QueryAlterableInterface
181
   *   The called object.
182
   */
183
  public function addTag($tag);
184

    
185
  /**
186
   * Determines if a given query has a given tag.
187
   *
188
   * @param $tag
189
   *   The tag to check.
190
   *
191
   * @return
192
   *   TRUE if this query has been marked with this tag, FALSE otherwise.
193
   */
194
  public function hasTag($tag);
195

    
196
  /**
197
   * Determines if a given query has all specified tags.
198
   *
199
   * @param $tags
200
   *   A variable number of arguments, one for each tag to check.
201
   *
202
   * @return
203
   *   TRUE if this query has been marked with all specified tags, FALSE
204
   *   otherwise.
205
   */
206
  public function hasAllTags();
207

    
208
  /**
209
   * Determines if a given query has any specified tag.
210
   *
211
   * @param $tags
212
   *   A variable number of arguments, one for each tag to check.
213
   *
214
   * @return
215
   *   TRUE if this query has been marked with at least one of the specified
216
   *   tags, FALSE otherwise.
217
   */
218
  public function hasAnyTag();
219

    
220
  /**
221
   * Adds additional metadata to the query.
222
   *
223
   * Often, a query may need to provide additional contextual data to alter
224
   * hooks. Alter hooks may then use that information to decide if and how
225
   * to take action.
226
   *
227
   * @param $key
228
   *   The unique identifier for this piece of metadata. Must be a string that
229
   *   follows the same rules as any other PHP identifier.
230
   * @param $object
231
   *   The additional data to add to the query. May be any valid PHP variable.
232
   *
233
   * @return QueryAlterableInterface
234
   *   The called object.
235
   */
236
  public function addMetaData($key, $object);
237

    
238
  /**
239
   * Retrieves a given piece of metadata.
240
   *
241
   * @param $key
242
   *   The unique identifier for the piece of metadata to retrieve.
243
   *
244
   * @return
245
   *   The previously attached metadata object, or NULL if one doesn't exist.
246
   */
247
  public function getMetaData($key);
248
}
249

    
250
/**
251
 * Interface for a query that accepts placeholders.
252
 */
253
interface QueryPlaceholderInterface {
254

    
255
  /**
256
   * Returns a unique identifier for this object.
257
   */
258
  public function uniqueIdentifier();
259

    
260
  /**
261
   * Returns the next placeholder ID for the query.
262
   *
263
   * @return
264
   *   The next available placeholder ID as an integer.
265
   */
266
  public function nextPlaceholder();
267
}
268

    
269
/**
270
 * Base class for query builders.
271
 *
272
 * Note that query builders use PHP's magic __toString() method to compile the
273
 * query object into a prepared statement.
274
 */
275
abstract class Query implements QueryPlaceholderInterface {
276

    
277
  /**
278
   * The connection object on which to run this query.
279
   *
280
   * @var DatabaseConnection
281
   */
282
  protected $connection;
283

    
284
  /**
285
   * The target of the connection object.
286
   *
287
   * @var string
288
   */
289
  protected $connectionTarget;
290

    
291
  /**
292
   * The key of the connection object.
293
   *
294
   * @var string
295
   */
296
  protected $connectionKey;
297

    
298
  /**
299
   * The query options to pass on to the connection object.
300
   *
301
   * @var array
302
   */
303
  protected $queryOptions;
304

    
305
  /**
306
   * A unique identifier for this query object.
307
   */
308
  protected $uniqueIdentifier;
309

    
310
  /**
311
   * The placeholder counter.
312
   */
313
  protected $nextPlaceholder = 0;
314

    
315
  /**
316
   * An array of comments that can be prepended to a query.
317
   *
318
   * @var array
319
   */
320
  protected $comments = array();
321

    
322
  /**
323
   * Constructs a Query object.
324
   *
325
   * @param DatabaseConnection $connection
326
   *   Database connection object.
327
   * @param array $options
328
   *   Array of query options.
329
   */
330
  public function __construct(DatabaseConnection $connection, $options) {
331
    $this->uniqueIdentifier = uniqid('', TRUE);
332

    
333
    $this->connection = $connection;
334
    $this->connectionKey = $this->connection->getKey();
335
    $this->connectionTarget = $this->connection->getTarget();
336

    
337
    $this->queryOptions = $options;
338
  }
339

    
340
  /**
341
   * Implements the magic __sleep function to disconnect from the database.
342
   */
343
  public function __sleep() {
344
    $keys = get_object_vars($this);
345
    unset($keys['connection']);
346
    return array_keys($keys);
347
  }
348

    
349
  /**
350
   * Implements the magic __wakeup function to reconnect to the database.
351
   */
352
  public function __wakeup() {
353
    $this->connection = Database::getConnection($this->connectionTarget, $this->connectionKey);
354
  }
355

    
356
  /**
357
   * Implements the magic __clone function.
358
   */
359
  public function __clone() {
360
    $this->uniqueIdentifier = uniqid('', TRUE);
361
  }
362

    
363
  /**
364
   * Runs the query against the database.
365
   */
366
  abstract protected function execute();
367

    
368
  /**
369
   * Implements PHP magic __toString method to convert the query to a string.
370
   *
371
   * The toString operation is how we compile a query object to a prepared
372
   * statement.
373
   *
374
   * @return
375
   *   A prepared statement query string for this object.
376
   */
377
  abstract public function __toString();
378

    
379
  /**
380
   * Returns a unique identifier for this object.
381
   */
382
  public function uniqueIdentifier() {
383
    return $this->uniqueIdentifier;
384
  }
385

    
386
  /**
387
   * Gets the next placeholder value for this query object.
388
   *
389
   * @return int
390
   *   Next placeholder value.
391
   */
392
  public function nextPlaceholder() {
393
    return $this->nextPlaceholder++;
394
  }
395

    
396
  /**
397
   * Adds a comment to the query.
398
   *
399
   * By adding a comment to a query, you can more easily find it in your
400
   * query log or the list of active queries on an SQL server. This allows
401
   * for easier debugging and allows you to more easily find where a query
402
   * with a performance problem is being generated.
403
   *
404
   * The comment string will be sanitized to remove * / and other characters
405
   * that may terminate the string early so as to avoid SQL injection attacks.
406
   *
407
   * @param $comment
408
   *   The comment string to be inserted into the query.
409
   *
410
   * @return Query
411
   *   The called object.
412
   */
413
  public function comment($comment) {
414
    $this->comments[] = $comment;
415
    return $this;
416
  }
417

    
418
  /**
419
   * Returns a reference to the comments array for the query.
420
   *
421
   * Because this method returns by reference, alter hooks may edit the comments
422
   * array directly to make their changes. If just adding comments, however, the
423
   * use of comment() is preferred.
424
   *
425
   * Note that this method must be called by reference as well:
426
   * @code
427
   * $comments =& $query->getComments();
428
   * @endcode
429
   *
430
   * @return
431
   *   A reference to the comments array structure.
432
   */
433
  public function &getComments() {
434
    return $this->comments;
435
  }
436
}
437

    
438
/**
439
 * General class for an abstracted INSERT query.
440
 */
441
class InsertQuery extends Query {
442

    
443
  /**
444
   * The table on which to insert.
445
   *
446
   * @var string
447
   */
448
  protected $table;
449

    
450
  /**
451
   * An array of fields on which to insert.
452
   *
453
   * @var array
454
   */
455
  protected $insertFields = array();
456

    
457
  /**
458
   * An array of fields that should be set to their database-defined defaults.
459
   *
460
   * @var array
461
   */
462
  protected $defaultFields = array();
463

    
464
  /**
465
   * A nested array of values to insert.
466
   *
467
   * $insertValues is an array of arrays. Each sub-array is either an
468
   * associative array whose keys are field names and whose values are field
469
   * values to insert, or a non-associative array of values in the same order
470
   * as $insertFields.
471
   *
472
   * Whether multiple insert sets will be run in a single query or multiple
473
   * queries is left to individual drivers to implement in whatever manner is
474
   * most appropriate. The order of values in each sub-array must match the
475
   * order of fields in $insertFields.
476
   *
477
   * @var array
478
   */
479
  protected $insertValues = array();
480

    
481
  /**
482
   * A SelectQuery object to fetch the rows that should be inserted.
483
   *
484
   * @var SelectQueryInterface
485
   */
486
  protected $fromQuery;
487

    
488
  /**
489
   * Constructs an InsertQuery object.
490
   *
491
   * @param DatabaseConnection $connection
492
   *   A DatabaseConnection object.
493
   * @param string $table
494
   *   Name of the table to associate with this query.
495
   * @param array $options
496
   *   Array of database options.
497
   */
498
  public function __construct($connection, $table, array $options = array()) {
499
    if (!isset($options['return'])) {
500
      $options['return'] = Database::RETURN_INSERT_ID;
501
    }
502
    parent::__construct($connection, $options);
503
    $this->table = $table;
504
  }
505

    
506
  /**
507
   * Adds a set of field->value pairs to be inserted.
508
   *
509
   * This method may only be called once. Calling it a second time will be
510
   * ignored. To queue up multiple sets of values to be inserted at once,
511
   * use the values() method.
512
   *
513
   * @param $fields
514
   *   An array of fields on which to insert. This array may be indexed or
515
   *   associative. If indexed, the array is taken to be the list of fields.
516
   *   If associative, the keys of the array are taken to be the fields and
517
   *   the values are taken to be corresponding values to insert. If a
518
   *   $values argument is provided, $fields must be indexed.
519
   * @param $values
520
   *   An array of fields to insert into the database. The values must be
521
   *   specified in the same order as the $fields array.
522
   *
523
   * @return InsertQuery
524
   *   The called object.
525
   */
526
  public function fields(array $fields, array $values = array()) {
527
    if (empty($this->insertFields)) {
528
      if (empty($values)) {
529
        if (!is_numeric(key($fields))) {
530
          $values = array_values($fields);
531
          $fields = array_keys($fields);
532
        }
533
      }
534
      $this->insertFields = $fields;
535
      if (!empty($values)) {
536
        $this->insertValues[] = $values;
537
      }
538
    }
539

    
540
    return $this;
541
  }
542

    
543
  /**
544
   * Adds another set of values to the query to be inserted.
545
   *
546
   * If $values is a numeric-keyed array, it will be assumed to be in the same
547
   * order as the original fields() call. If it is associative, it may be
548
   * in any order as long as the keys of the array match the names of the
549
   * fields.
550
   *
551
   * @param $values
552
   *   An array of values to add to the query.
553
   *
554
   * @return InsertQuery
555
   *   The called object.
556
   */
557
  public function values(array $values) {
558
    if (is_numeric(key($values))) {
559
      $this->insertValues[] = $values;
560
    }
561
    else {
562
      // Reorder the submitted values to match the fields array.
563
      foreach ($this->insertFields as $key) {
564
        $insert_values[$key] = $values[$key];
565
      }
566
      // For consistency, the values array is always numerically indexed.
567
      $this->insertValues[] = array_values($insert_values);
568
    }
569
    return $this;
570
  }
571

    
572
  /**
573
   * Specifies fields for which the database defaults should be used.
574
   *
575
   * If you want to force a given field to use the database-defined default,
576
   * not NULL or undefined, use this method to instruct the database to use
577
   * default values explicitly. In most cases this will not be necessary
578
   * unless you are inserting a row that is all default values, as you cannot
579
   * specify no values in an INSERT query.
580
   *
581
   * Specifying a field both in fields() and in useDefaults() is an error
582
   * and will not execute.
583
   *
584
   * @param $fields
585
   *   An array of values for which to use the default values
586
   *   specified in the table definition.
587
   *
588
   * @return InsertQuery
589
   *   The called object.
590
   */
591
  public function useDefaults(array $fields) {
592
    $this->defaultFields = $fields;
593
    return $this;
594
  }
595

    
596
  /**
597
   * Sets the fromQuery on this InsertQuery object.
598
   *
599
   * @param SelectQueryInterface $query
600
   *   The query to fetch the rows that should be inserted.
601
   *
602
   * @return InsertQuery
603
   *   The called object.
604
   */
605
  public function from(SelectQueryInterface $query) {
606
    $this->fromQuery = $query;
607
    return $this;
608
  }
609

    
610
  /**
611
   * Executes the insert query.
612
   *
613
   * @return
614
   *   The last insert ID of the query, if one exists. If the query
615
   *   was given multiple sets of values to insert, the return value is
616
   *   undefined. If no fields are specified, this method will do nothing and
617
   *   return NULL. That makes it safe to use in multi-insert loops.
618
   */
619
  public function execute() {
620
    // If validation fails, simply return NULL. Note that validation routines
621
    // in preExecute() may throw exceptions instead.
622
    if (!$this->preExecute()) {
623
      return NULL;
624
    }
625

    
626
    // If we're selecting from a SelectQuery, finish building the query and
627
    // pass it back, as any remaining options are irrelevant.
628
    if (!empty($this->fromQuery)) {
629
      $sql = (string) $this;
630
      // The SelectQuery may contain arguments, load and pass them through.
631
      return $this->connection->query($sql, $this->fromQuery->getArguments(), $this->queryOptions);
632
    }
633

    
634
    $last_insert_id = 0;
635

    
636
    // Each insert happens in its own query in the degenerate case. However,
637
    // we wrap it in a transaction so that it is atomic where possible. On many
638
    // databases, such as SQLite, this is also a notable performance boost.
639
    $transaction = $this->connection->startTransaction();
640

    
641
    try {
642
      $sql = (string) $this;
643
      foreach ($this->insertValues as $insert_values) {
644
        $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions);
645
      }
646
    }
647
    catch (Exception $e) {
648
      // One of the INSERTs failed, rollback the whole batch.
649
      $transaction->rollback();
650
      // Rethrow the exception for the calling code.
651
      throw $e;
652
    }
653

    
654
    // Re-initialize the values array so that we can re-use this query.
655
    $this->insertValues = array();
656

    
657
    // Transaction commits here where $transaction looses scope.
658

    
659
    return $last_insert_id;
660
  }
661

    
662
  /**
663
   * Implements PHP magic __toString method to convert the query to a string.
664
   *
665
   * @return string
666
   *   The prepared statement.
667
   */
668
  public function __toString() {
669
    // Create a sanitized comment string to prepend to the query.
670
    $comments = $this->connection->makeComment($this->comments);
671

    
672
    // Default fields are always placed first for consistency.
673
    $insert_fields = array_merge($this->defaultFields, $this->insertFields);
674

    
675
    if (!empty($this->fromQuery)) {
676
      return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
677
    }
678

    
679
    // For simplicity, we will use the $placeholders array to inject
680
    // default keywords even though they are not, strictly speaking,
681
    // placeholders for prepared statements.
682
    $placeholders = array();
683
    $placeholders = array_pad($placeholders, count($this->defaultFields), 'default');
684
    $placeholders = array_pad($placeholders, count($this->insertFields), '?');
685

    
686
    return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')';
687
  }
688

    
689
  /**
690
   * Preprocesses and validates the query.
691
   *
692
   * @return
693
   *   TRUE if the validation was successful, FALSE if not.
694
   *
695
   * @throws FieldsOverlapException
696
   * @throws NoFieldsException
697
   */
698
  public function preExecute() {
699
    // Confirm that the user did not try to specify an identical
700
    // field and default field.
701
    if (array_intersect($this->insertFields, $this->defaultFields)) {
702
      throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
703
    }
704

    
705
    if (!empty($this->fromQuery)) {
706
      // We have to assume that the used aliases match the insert fields.
707
      // Regular fields are added to the query before expressions, maintain the
708
      // same order for the insert fields.
709
      // This behavior can be overridden by calling fields() manually as only the
710
      // first call to fields() does have an effect.
711
      $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions())));
712
    }
713
    else {
714
      // Don't execute query without fields.
715
      if (count($this->insertFields) + count($this->defaultFields) == 0) {
716
        throw new NoFieldsException('There are no fields available to insert with.');
717
      }
718
    }
719

    
720
    // If no values have been added, silently ignore this query. This can happen
721
    // if values are added conditionally, so we don't want to throw an
722
    // exception.
723
    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
724
      return FALSE;
725
    }
726
    return TRUE;
727
  }
728
}
729

    
730
/**
731
 * General class for an abstracted DELETE operation.
732
 */
733
class DeleteQuery extends Query implements QueryConditionInterface {
734

    
735
  /**
736
   * The table from which to delete.
737
   *
738
   * @var string
739
   */
740
  protected $table;
741

    
742
  /**
743
   * The condition object for this query.
744
   *
745
   * Condition handling is handled via composition.
746
   *
747
   * @var DatabaseCondition
748
   */
749
  protected $condition;
750

    
751
  /**
752
   * Constructs a DeleteQuery object.
753
   *
754
   * @param DatabaseConnection $connection
755
   *   A DatabaseConnection object.
756
   * @param string $table
757
   *   Name of the table to associate with this query.
758
   * @param array $options
759
   *   Array of database options.
760
   */
761
  public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
762
    $options['return'] = Database::RETURN_AFFECTED;
763
    parent::__construct($connection, $options);
764
    $this->table = $table;
765

    
766
    $this->condition = new DatabaseCondition('AND');
767
  }
768

    
769
  /**
770
   * Implements QueryConditionInterface::condition().
771
   */
772
  public function condition($field, $value = NULL, $operator = NULL) {
773
    $this->condition->condition($field, $value, $operator);
774
    return $this;
775
  }
776

    
777
  /**
778
   * Implements QueryConditionInterface::isNull().
779
   */
780
  public function isNull($field) {
781
    $this->condition->isNull($field);
782
    return $this;
783
  }
784

    
785
  /**
786
   * Implements QueryConditionInterface::isNotNull().
787
   */
788
  public function isNotNull($field) {
789
    $this->condition->isNotNull($field);
790
    return $this;
791
  }
792

    
793
  /**
794
   * Implements QueryConditionInterface::exists().
795
   */
796
  public function exists(SelectQueryInterface $select) {
797
    $this->condition->exists($select);
798
    return $this;
799
  }
800

    
801
  /**
802
   * Implements QueryConditionInterface::notExists().
803
   */
804
  public function notExists(SelectQueryInterface $select) {
805
    $this->condition->notExists($select);
806
    return $this;
807
  }
808

    
809
  /**
810
   * Implements QueryConditionInterface::conditions().
811
   */
812
  public function &conditions() {
813
    return $this->condition->conditions();
814
  }
815

    
816
  /**
817
   * Implements QueryConditionInterface::arguments().
818
   */
819
  public function arguments() {
820
    return $this->condition->arguments();
821
  }
822

    
823
  /**
824
   * Implements QueryConditionInterface::where().
825
   */
826
  public function where($snippet, $args = array()) {
827
    $this->condition->where($snippet, $args);
828
    return $this;
829
  }
830

    
831
  /**
832
   * Implements QueryConditionInterface::compile().
833
   */
834
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
835
    return $this->condition->compile($connection, $queryPlaceholder);
836
  }
837

    
838
  /**
839
   * Implements QueryConditionInterface::compiled().
840
   */
841
  public function compiled() {
842
    return $this->condition->compiled();
843
  }
844

    
845
  /**
846
   * Executes the DELETE query.
847
   *
848
   * @return int
849
   *   The number of rows affected by the delete query.
850
   */
851
  public function execute() {
852
    $values = array();
853
    if (count($this->condition)) {
854
      $this->condition->compile($this->connection, $this);
855
      $values = $this->condition->arguments();
856
    }
857

    
858
    return $this->connection->query((string) $this, $values, $this->queryOptions);
859
  }
860

    
861
  /**
862
   * Implements PHP magic __toString method to convert the query to a string.
863
   *
864
   * @return string
865
   *   The prepared statement.
866
   */
867
  public function __toString() {
868
    // Create a sanitized comment string to prepend to the query.
869
    $comments = $this->connection->makeComment($this->comments);
870

    
871
    $query = $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} ';
872

    
873
    if (count($this->condition)) {
874

    
875
      $this->condition->compile($this->connection, $this);
876
      $query .= "\nWHERE " . $this->condition;
877
    }
878

    
879
    return $query;
880
  }
881
}
882

    
883

    
884
/**
885
 * General class for an abstracted TRUNCATE operation.
886
 */
887
class TruncateQuery extends Query {
888

    
889
  /**
890
   * The table to truncate.
891
   *
892
   * @var string
893
   */
894
  protected $table;
895

    
896
  /**
897
   * Constructs a TruncateQuery object.
898
   *
899
   * @param DatabaseConnection $connection
900
   *   A DatabaseConnection object.
901
   * @param string $table
902
   *   Name of the table to associate with this query.
903
   * @param array $options
904
   *   Array of database options.
905
   */
906
  public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
907
    $options['return'] = Database::RETURN_AFFECTED;
908
    parent::__construct($connection, $options);
909
    $this->table = $table;
910
  }
911

    
912
  /**
913
   * Implements QueryConditionInterface::compile().
914
   */
915
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
916
    return $this->condition->compile($connection, $queryPlaceholder);
917
  }
918

    
919
  /**
920
   * Implements QueryConditionInterface::compiled().
921
   */
922
  public function compiled() {
923
    return $this->condition->compiled();
924
  }
925

    
926
  /**
927
   * Executes the TRUNCATE query.
928
   *
929
   * @return
930
   *   Return value is dependent on the database type.
931
   */
932
  public function execute() {
933
    return $this->connection->query((string) $this, array(), $this->queryOptions);
934
  }
935

    
936
  /**
937
   * Implements PHP magic __toString method to convert the query to a string.
938
   *
939
   * @return string
940
   *   The prepared statement.
941
   */
942
  public function __toString() {
943
    // Create a sanitized comment string to prepend to the query.
944
    $comments = $this->connection->makeComment($this->comments);
945

    
946
    // In most cases, TRUNCATE is not a transaction safe statement as it is a
947
    // DDL statement which results in an implicit COMMIT. When we are in a
948
    // transaction, fallback to the slower, but transactional, DELETE.
949
    // PostgreSQL also locks the entire table for a TRUNCATE strongly reducing
950
    // the concurrency with other transactions.
951
    if ($this->connection->inTransaction()) {
952
      return $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '}';
953
    }
954
    else {
955
      return $comments . 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} ';
956
    }
957
  }
958
}
959

    
960
/**
961
 * General class for an abstracted UPDATE operation.
962
 */
963
class UpdateQuery extends Query implements QueryConditionInterface {
964

    
965
  /**
966
   * The table to update.
967
   *
968
   * @var string
969
   */
970
  protected $table;
971

    
972
  /**
973
   * An array of fields that will be updated.
974
   *
975
   * @var array
976
   */
977
  protected $fields = array();
978

    
979
  /**
980
   * An array of values to update to.
981
   *
982
   * @var array
983
   */
984
  protected $arguments = array();
985

    
986
  /**
987
   * The condition object for this query.
988
   *
989
   * Condition handling is handled via composition.
990
   *
991
   * @var DatabaseCondition
992
   */
993
  protected $condition;
994

    
995
  /**
996
   * Array of fields to update to an expression in case of a duplicate record.
997
   *
998
   * This variable is a nested array in the following format:
999
   * @code
1000
   * <some field> => array(
1001
   *  'condition' => <condition to execute, as a string>,
1002
   *  'arguments' => <array of arguments for condition, or NULL for none>,
1003
   * );
1004
   * @endcode
1005
   *
1006
   * @var array
1007
   */
1008
  protected $expressionFields = array();
1009

    
1010
  /**
1011
   * Constructs an UpdateQuery object.
1012
   *
1013
   * @param DatabaseConnection $connection
1014
   *   A DatabaseConnection object.
1015
   * @param string $table
1016
   *   Name of the table to associate with this query.
1017
   * @param array $options
1018
   *   Array of database options.
1019
   */
1020
  public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
1021
    $options['return'] = Database::RETURN_AFFECTED;
1022
    parent::__construct($connection, $options);
1023
    $this->table = $table;
1024

    
1025
    $this->condition = new DatabaseCondition('AND');
1026
  }
1027

    
1028
  /**
1029
   * Implements QueryConditionInterface::condition().
1030
   */
1031
  public function condition($field, $value = NULL, $operator = NULL) {
1032
    $this->condition->condition($field, $value, $operator);
1033
    return $this;
1034
  }
1035

    
1036
  /**
1037
   * Implements QueryConditionInterface::isNull().
1038
   */
1039
  public function isNull($field) {
1040
    $this->condition->isNull($field);
1041
    return $this;
1042
  }
1043

    
1044
  /**
1045
   * Implements QueryConditionInterface::isNotNull().
1046
   */
1047
  public function isNotNull($field) {
1048
    $this->condition->isNotNull($field);
1049
    return $this;
1050
  }
1051

    
1052
  /**
1053
   * Implements QueryConditionInterface::exists().
1054
   */
1055
  public function exists(SelectQueryInterface $select) {
1056
    $this->condition->exists($select);
1057
    return $this;
1058
  }
1059

    
1060
  /**
1061
   * Implements QueryConditionInterface::notExists().
1062
   */
1063
  public function notExists(SelectQueryInterface $select) {
1064
    $this->condition->notExists($select);
1065
    return $this;
1066
  }
1067

    
1068
  /**
1069
   * Implements QueryConditionInterface::conditions().
1070
   */
1071
  public function &conditions() {
1072
    return $this->condition->conditions();
1073
  }
1074

    
1075
  /**
1076
   * Implements QueryConditionInterface::arguments().
1077
   */
1078
  public function arguments() {
1079
    return $this->condition->arguments();
1080
  }
1081

    
1082
  /**
1083
   * Implements QueryConditionInterface::where().
1084
   */
1085
  public function where($snippet, $args = array()) {
1086
    $this->condition->where($snippet, $args);
1087
    return $this;
1088
  }
1089

    
1090
  /**
1091
   * Implements QueryConditionInterface::compile().
1092
   */
1093
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1094
    return $this->condition->compile($connection, $queryPlaceholder);
1095
  }
1096

    
1097
  /**
1098
   * Implements QueryConditionInterface::compiled().
1099
   */
1100
  public function compiled() {
1101
    return $this->condition->compiled();
1102
  }
1103

    
1104
  /**
1105
   * Adds a set of field->value pairs to be updated.
1106
   *
1107
   * @param $fields
1108
   *   An associative array of fields to write into the database. The array keys
1109
   *   are the field names and the values are the values to which to set them.
1110
   *
1111
   * @return UpdateQuery
1112
   *   The called object.
1113
   */
1114
  public function fields(array $fields) {
1115
    $this->fields = $fields;
1116
    return $this;
1117
  }
1118

    
1119
  /**
1120
   * Specifies fields to be updated as an expression.
1121
   *
1122
   * Expression fields are cases such as counter=counter+1. This method takes
1123
   * precedence over fields().
1124
   *
1125
   * @param $field
1126
   *   The field to set.
1127
   * @param $expression
1128
   *   The field will be set to the value of this expression. This parameter
1129
   *   may include named placeholders.
1130
   * @param $arguments
1131
   *   If specified, this is an array of key/value pairs for named placeholders
1132
   *   corresponding to the expression.
1133
   *
1134
   * @return UpdateQuery
1135
   *   The called object.
1136
   */
1137
  public function expression($field, $expression, array $arguments = NULL) {
1138
    $this->expressionFields[$field] = array(
1139
      'expression' => $expression,
1140
      'arguments' => $arguments,
1141
    );
1142

    
1143
    return $this;
1144
  }
1145

    
1146
  /**
1147
   * Executes the UPDATE query.
1148
   *
1149
   * @return
1150
   *   The number of rows affected by the update.
1151
   */
1152
  public function execute() {
1153

    
1154
    // Expressions take priority over literal fields, so we process those first
1155
    // and remove any literal fields that conflict.
1156
    $fields = $this->fields;
1157
    $update_values = array();
1158
    foreach ($this->expressionFields as $field => $data) {
1159
      if (!empty($data['arguments'])) {
1160
        $update_values += $data['arguments'];
1161
      }
1162
      unset($fields[$field]);
1163
    }
1164

    
1165
    // Because we filter $fields the same way here and in __toString(), the
1166
    // placeholders will all match up properly.
1167
    $max_placeholder = 0;
1168
    foreach ($fields as $field => $value) {
1169
      $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value;
1170
    }
1171

    
1172
    if (count($this->condition)) {
1173
      $this->condition->compile($this->connection, $this);
1174
      $update_values = array_merge($update_values, $this->condition->arguments());
1175
    }
1176

    
1177
    return $this->connection->query((string) $this, $update_values, $this->queryOptions);
1178
  }
1179

    
1180
  /**
1181
   * Implements PHP magic __toString method to convert the query to a string.
1182
   *
1183
   * @return string
1184
   *   The prepared statement.
1185
   */
1186
  public function __toString() {
1187
    // Create a sanitized comment string to prepend to the query.
1188
    $comments = $this->connection->makeComment($this->comments);
1189

    
1190
    // Expressions take priority over literal fields, so we process those first
1191
    // and remove any literal fields that conflict.
1192
    $fields = $this->fields;
1193
    $update_fields = array();
1194
    foreach ($this->expressionFields as $field => $data) {
1195
      $update_fields[] = $field . '=' . $data['expression'];
1196
      unset($fields[$field]);
1197
    }
1198

    
1199
    $max_placeholder = 0;
1200
    foreach ($fields as $field => $value) {
1201
      $update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++);
1202
    }
1203

    
1204
    $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
1205

    
1206
    if (count($this->condition)) {
1207
      $this->condition->compile($this->connection, $this);
1208
      // There is an implicit string cast on $this->condition.
1209
      $query .= "\nWHERE " . $this->condition;
1210
    }
1211

    
1212
    return $query;
1213
  }
1214

    
1215
}
1216

    
1217
/**
1218
 * General class for an abstracted MERGE query operation.
1219
 *
1220
 * An ANSI SQL:2003 compatible database would run the following query:
1221
 *
1222
 * @code
1223
 * MERGE INTO table_name_1 USING table_name_2 ON (condition)
1224
 *   WHEN MATCHED THEN
1225
 *   UPDATE SET column1 = value1 [, column2 = value2 ...]
1226
 *   WHEN NOT MATCHED THEN
1227
 *   INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...
1228
 * @endcode
1229
 *
1230
 * Other databases (most notably MySQL, PostgreSQL and SQLite) will emulate
1231
 * this statement by running a SELECT and then INSERT or UPDATE.
1232
 *
1233
 * By default, the two table names are identical and they are passed into the
1234
 * the constructor. table_name_2 can be specified by the
1235
 * MergeQuery::conditionTable() method. It can be either a string or a
1236
 * subquery.
1237
 *
1238
 * The condition is built exactly like SelectQuery or UpdateQuery conditions,
1239
 * the UPDATE query part is built similarly like an UpdateQuery and finally the
1240
 * INSERT query part is built similarly like an InsertQuery. However, both
1241
 * UpdateQuery and InsertQuery has a fields method so
1242
 * MergeQuery::updateFields() and MergeQuery::insertFields() needs to be called
1243
 * instead. MergeQuery::fields() can also be called which calls both of these
1244
 * methods as the common case is to use the same column-value pairs for both
1245
 * INSERT and UPDATE. However, this is not mandatory. Another convenient
1246
 * wrapper is MergeQuery::key() which adds the same column-value pairs to the
1247
 * condition and the INSERT query part.
1248
 *
1249
 * Several methods (key(), fields(), insertFields()) can be called to set a
1250
 * key-value pair for the INSERT query part. Subsequent calls for the same
1251
 * fields override the earlier ones. The same is true for UPDATE and key(),
1252
 * fields() and updateFields().
1253
 */
1254
class MergeQuery extends Query implements QueryConditionInterface {
1255
  /**
1256
   * Returned by execute() if an INSERT query has been executed.
1257
   */
1258
  const STATUS_INSERT = 1;
1259

    
1260
  /**
1261
   * Returned by execute() if an UPDATE query has been executed.
1262
   */
1263
  const STATUS_UPDATE = 2;
1264

    
1265
  /**
1266
   * The table to be used for INSERT and UPDATE.
1267
   *
1268
   * @var string
1269
   */
1270
  protected $table;
1271

    
1272
  /**
1273
   * The table or subquery to be used for the condition.
1274
   */
1275
  protected $conditionTable;
1276

    
1277
  /**
1278
   * An array of fields on which to insert.
1279
   *
1280
   * @var array
1281
   */
1282
  protected $insertFields = array();
1283

    
1284
  /**
1285
   * An array of fields which should be set to their database-defined defaults.
1286
   *
1287
   * Used on INSERT.
1288
   *
1289
   * @var array
1290
   */
1291
  protected $defaultFields = array();
1292

    
1293
  /**
1294
   * An array of values to be inserted.
1295
   *
1296
   * @var string
1297
   */
1298
  protected $insertValues = array();
1299

    
1300
  /**
1301
   * An array of fields that will be updated.
1302
   *
1303
   * @var array
1304
   */
1305
  protected $updateFields = array();
1306

    
1307
  /**
1308
   * Array of fields to update to an expression in case of a duplicate record.
1309
   *
1310
   * This variable is a nested array in the following format:
1311
   * @code
1312
   * <some field> => array(
1313
   *  'condition' => <condition to execute, as a string>,
1314
   *  'arguments' => <array of arguments for condition, or NULL for none>,
1315
   * );
1316
   * @endcode
1317
   *
1318
   * @var array
1319
   */
1320
  protected $expressionFields = array();
1321

    
1322
  /**
1323
   * Flag indicating whether an UPDATE is necessary.
1324
   *
1325
   * @var boolean
1326
   */
1327
  protected $needsUpdate = FALSE;
1328

    
1329
  /**
1330
  * Constructs a MergeQuery object.
1331
  *
1332
  * @param DatabaseConnection $connection
1333
  *   A DatabaseConnection object.
1334
  * @param string $table
1335
  *   Name of the table to associate with this query.
1336
  * @param array $options
1337
  *   Array of database options.
1338
  */
1339
  public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
1340
    $options['return'] = Database::RETURN_AFFECTED;
1341
    parent::__construct($connection, $options);
1342
    $this->table = $table;
1343
    $this->conditionTable = $table;
1344
    $this->condition = new DatabaseCondition('AND');
1345
  }
1346

    
1347
  /**
1348
   * Sets the table or subquery to be used for the condition.
1349
   *
1350
   * @param $table
1351
   *   The table name or the subquery to be used. Use a SelectQuery object to
1352
   *   pass in a subquery.
1353
   *
1354
   * @return MergeQuery
1355
   *   The called object.
1356
   */
1357
  protected function conditionTable($table) {
1358
    $this->conditionTable = $table;
1359
    return $this;
1360
  }
1361

    
1362
  /**
1363
   * Adds a set of field->value pairs to be updated.
1364
   *
1365
   * @param $fields
1366
   *   An associative array of fields to write into the database. The array keys
1367
   *   are the field names and the values are the values to which to set them.
1368
   *
1369
   * @return MergeQuery
1370
   *   The called object.
1371
   */
1372
  public function updateFields(array $fields) {
1373
    $this->updateFields = $fields;
1374
    $this->needsUpdate = TRUE;
1375
    return $this;
1376
  }
1377

    
1378
  /**
1379
   * Specifies fields to be updated as an expression.
1380
   *
1381
   * Expression fields are cases such as counter = counter + 1. This method
1382
   * takes precedence over MergeQuery::updateFields() and it's wrappers,
1383
   * MergeQuery::key() and MergeQuery::fields().
1384
   *
1385
   * @param $field
1386
   *   The field to set.
1387
   * @param $expression
1388
   *   The field will be set to the value of this expression. This parameter
1389
   *   may include named placeholders.
1390
   * @param $arguments
1391
   *   If specified, this is an array of key/value pairs for named placeholders
1392
   *   corresponding to the expression.
1393
   *
1394
   * @return MergeQuery
1395
   *   The called object.
1396
   */
1397
  public function expression($field, $expression, array $arguments = NULL) {
1398
    $this->expressionFields[$field] = array(
1399
      'expression' => $expression,
1400
      'arguments' => $arguments,
1401
    );
1402
    $this->needsUpdate = TRUE;
1403
    return $this;
1404
  }
1405

    
1406
  /**
1407
   * Adds a set of field->value pairs to be inserted.
1408
   *
1409
   * @param $fields
1410
   *   An array of fields on which to insert. This array may be indexed or
1411
   *   associative. If indexed, the array is taken to be the list of fields.
1412
   *   If associative, the keys of the array are taken to be the fields and
1413
   *   the values are taken to be corresponding values to insert. If a
1414
   *   $values argument is provided, $fields must be indexed.
1415
   * @param $values
1416
   *   An array of fields to insert into the database. The values must be
1417
   *   specified in the same order as the $fields array.
1418
   *
1419
   * @return MergeQuery
1420
   *   The called object.
1421
   */
1422
  public function insertFields(array $fields, array $values = array()) {
1423
    if ($values) {
1424
      $fields = array_combine($fields, $values);
1425
    }
1426
    $this->insertFields = $fields;
1427
    return $this;
1428
  }
1429

    
1430
  /**
1431
   * Specifies fields for which the database-defaults should be used.
1432
   *
1433
   * If you want to force a given field to use the database-defined default,
1434
   * not NULL or undefined, use this method to instruct the database to use
1435
   * default values explicitly. In most cases this will not be necessary
1436
   * unless you are inserting a row that is all default values, as you cannot
1437
   * specify no values in an INSERT query.
1438
   *
1439
   * Specifying a field both in fields() and in useDefaults() is an error
1440
   * and will not execute.
1441
   *
1442
   * @param $fields
1443
   *   An array of values for which to use the default values
1444
   *   specified in the table definition.
1445
   *
1446
   * @return MergeQuery
1447
   *   The called object.
1448
   */
1449
  public function useDefaults(array $fields) {
1450
    $this->defaultFields = $fields;
1451
    return $this;
1452
  }
1453

    
1454
  /**
1455
   * Sets common field-value pairs in the INSERT and UPDATE query parts.
1456
   *
1457
   * This method should only be called once. It may be called either
1458
   * with a single associative array or two indexed arrays. If called
1459
   * with an associative array, the keys are taken to be the fields
1460
   * and the values are taken to be the corresponding values to set.
1461
   * If called with two arrays, the first array is taken as the fields
1462
   * and the second array is taken as the corresponding values.
1463
   *
1464
   * @param $fields
1465
   *   An array of fields to insert, or an associative array of fields and
1466
   *   values. The keys of the array are taken to be the fields and the values
1467
   *   are taken to be corresponding values to insert.
1468
   * @param $values
1469
   *   An array of values to set into the database. The values must be
1470
   *   specified in the same order as the $fields array.
1471
   *
1472
   * @return MergeQuery
1473
   *   The called object.
1474
   */
1475
  public function fields(array $fields, array $values = array()) {
1476
    if ($values) {
1477
      $fields = array_combine($fields, $values);
1478
    }
1479
    foreach ($fields as $key => $value) {
1480
      $this->insertFields[$key] = $value;
1481
      $this->updateFields[$key] = $value;
1482
    }
1483
    $this->needsUpdate = TRUE;
1484
    return $this;
1485
  }
1486

    
1487
  /**
1488
   * Sets the key field(s) to be used as conditions for this query.
1489
   *
1490
   * This method should only be called once. It may be called either
1491
   * with a single associative array or two indexed arrays. If called
1492
   * with an associative array, the keys are taken to be the fields
1493
   * and the values are taken to be the corresponding values to set.
1494
   * If called with two arrays, the first array is taken as the fields
1495
   * and the second array is taken as the corresponding values.
1496
   *
1497
   * The fields are copied to the condition of the query and the INSERT part.
1498
   * If no other method is called, the UPDATE will become a no-op.
1499
   *
1500
   * @param $fields
1501
   *   An array of fields to set, or an associative array of fields and values.
1502
   * @param $values
1503
   *   An array of values to set into the database. The values must be
1504
   *   specified in the same order as the $fields array.
1505
   *
1506
   * @return MergeQuery
1507
   *   The called object.
1508
   */
1509
  public function key(array $fields, array $values = array()) {
1510
    if ($values) {
1511
      $fields = array_combine($fields, $values);
1512
    }
1513
    foreach ($fields as $key => $value) {
1514
      $this->insertFields[$key] = $value;
1515
      $this->condition($key, $value);
1516
    }
1517
    return $this;
1518
  }
1519

    
1520
  /**
1521
   * Implements QueryConditionInterface::condition().
1522
   */
1523
  public function condition($field, $value = NULL, $operator = NULL) {
1524
    $this->condition->condition($field, $value, $operator);
1525
    return $this;
1526
  }
1527

    
1528
  /**
1529
   * Implements QueryConditionInterface::isNull().
1530
   */
1531
  public function isNull($field) {
1532
    $this->condition->isNull($field);
1533
    return $this;
1534
  }
1535

    
1536
  /**
1537
   * Implements QueryConditionInterface::isNotNull().
1538
   */
1539
  public function isNotNull($field) {
1540
    $this->condition->isNotNull($field);
1541
    return $this;
1542
  }
1543

    
1544
  /**
1545
   * Implements QueryConditionInterface::exists().
1546
   */
1547
  public function exists(SelectQueryInterface $select) {
1548
    $this->condition->exists($select);
1549
    return $this;
1550
  }
1551

    
1552
  /**
1553
   * Implements QueryConditionInterface::notExists().
1554
   */
1555
  public function notExists(SelectQueryInterface $select) {
1556
    $this->condition->notExists($select);
1557
    return $this;
1558
  }
1559

    
1560
  /**
1561
   * Implements QueryConditionInterface::conditions().
1562
   */
1563
  public function &conditions() {
1564
    return $this->condition->conditions();
1565
  }
1566

    
1567
  /**
1568
   * Implements QueryConditionInterface::arguments().
1569
   */
1570
  public function arguments() {
1571
    return $this->condition->arguments();
1572
  }
1573

    
1574
  /**
1575
   * Implements QueryConditionInterface::where().
1576
   */
1577
  public function where($snippet, $args = array()) {
1578
    $this->condition->where($snippet, $args);
1579
    return $this;
1580
  }
1581

    
1582
  /**
1583
   * Implements QueryConditionInterface::compile().
1584
   */
1585
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1586
    return $this->condition->compile($connection, $queryPlaceholder);
1587
  }
1588

    
1589
  /**
1590
   * Implements QueryConditionInterface::compiled().
1591
   */
1592
  public function compiled() {
1593
    return $this->condition->compiled();
1594
  }
1595

    
1596
  /**
1597
   * Implements PHP magic __toString method to convert the query to a string.
1598
   *
1599
   * In the degenerate case, there is no string-able query as this operation
1600
   * is potentially two queries.
1601
   *
1602
   * @return string
1603
   *   The prepared query statement.
1604
   */
1605
  public function __toString() {
1606
  }
1607

    
1608
  public function execute() {
1609
    if (!count($this->condition)) {
1610
      throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
1611
    }
1612
    $select = $this->connection->select($this->conditionTable)
1613
      ->condition($this->condition);
1614
    $select->addExpression('1');
1615
    if (!$select->execute()->fetchField()) {
1616
      try {
1617
        $insert = $this->connection->insert($this->table)->fields($this->insertFields);
1618
        if ($this->defaultFields) {
1619
          $insert->useDefaults($this->defaultFields);
1620
        }
1621
        $insert->execute();
1622
        return self::STATUS_INSERT;
1623
      }
1624
      catch (Exception $e) {
1625
        // The insert query failed, maybe it's because a racing insert query
1626
        // beat us in inserting the same row. Retry the select query, if it
1627
        // returns a row, ignore the error and continue with the update
1628
        // query below.
1629
        if (!$select->execute()->fetchField()) {
1630
          throw $e;
1631
        }
1632
      }
1633
    }
1634
    if ($this->needsUpdate) {
1635
      $update = $this->connection->update($this->table)
1636
        ->fields($this->updateFields)
1637
        ->condition($this->condition);
1638
      if ($this->expressionFields) {
1639
        foreach ($this->expressionFields as $field => $data) {
1640
          $update->expression($field, $data['expression'], $data['arguments']);
1641
        }
1642
      }
1643
      $update->execute();
1644
      return self::STATUS_UPDATE;
1645
     }
1646
  }
1647
}
1648

    
1649
/**
1650
 * Generic class for a series of conditions in a query.
1651
 */
1652
class DatabaseCondition implements QueryConditionInterface, Countable {
1653

    
1654
  /**
1655
   * Array of conditions.
1656
   *
1657
   * @var array
1658
   */
1659
  protected $conditions = array();
1660

    
1661
  /**
1662
   * Array of arguments.
1663
   *
1664
   * @var array
1665
   */
1666
  protected $arguments = array();
1667

    
1668
  /**
1669
   * Whether the conditions have been changed.
1670
   *
1671
   * TRUE if the condition has been changed since the last compile.
1672
   * FALSE if the condition has been compiled and not changed.
1673
   *
1674
   * @var bool
1675
   */
1676
  protected $changed = TRUE;
1677

    
1678
  /**
1679
   * The identifier of the query placeholder this condition has been compiled against.
1680
   */
1681
  protected $queryPlaceholderIdentifier;
1682

    
1683
  /**
1684
   * Constructs a DataBaseCondition object.
1685
   *
1686
   * @param string $conjunction
1687
   *   The operator to use to combine conditions: 'AND' or 'OR'.
1688
   */
1689
  public function __construct($conjunction) {
1690
    $this->conditions['#conjunction'] = $conjunction;
1691
  }
1692

    
1693
  /**
1694
   * Implements Countable::count().
1695
   *
1696
   * Returns the size of this conditional. The size of the conditional is the
1697
   * size of its conditional array minus one, because one element is the
1698
   * conjunction.
1699
   */
1700
  public function count() {
1701
    return count($this->conditions) - 1;
1702
  }
1703

    
1704
  /**
1705
   * Implements QueryConditionInterface::condition().
1706
   */
1707
  public function condition($field, $value = NULL, $operator = NULL) {
1708
    if (!isset($operator)) {
1709
      if (is_array($value)) {
1710
        $operator = 'IN';
1711
      }
1712
      elseif (!isset($value)) {
1713
        $operator = 'IS NULL';
1714
      }
1715
      else {
1716
        $operator = '=';
1717
      }
1718
    }
1719
    $this->conditions[] = array(
1720
      'field' => $field,
1721
      'value' => $value,
1722
      'operator' => $operator,
1723
    );
1724

    
1725
    $this->changed = TRUE;
1726

    
1727
    return $this;
1728
  }
1729

    
1730
  /**
1731
   * Implements QueryConditionInterface::where().
1732
   */
1733
  public function where($snippet, $args = array()) {
1734
    $this->conditions[] = array(
1735
      'field' => $snippet,
1736
      'value' => $args,
1737
      'operator' => NULL,
1738
    );
1739
    $this->changed = TRUE;
1740

    
1741
    return $this;
1742
  }
1743

    
1744
  /**
1745
   * Implements QueryConditionInterface::isNull().
1746
   */
1747
  public function isNull($field) {
1748
    return $this->condition($field);
1749
  }
1750

    
1751
  /**
1752
   * Implements QueryConditionInterface::isNotNull().
1753
   */
1754
  public function isNotNull($field) {
1755
    return $this->condition($field, NULL, 'IS NOT NULL');
1756
  }
1757

    
1758
  /**
1759
   * Implements QueryConditionInterface::exists().
1760
   */
1761
  public function exists(SelectQueryInterface $select) {
1762
    return $this->condition('', $select, 'EXISTS');
1763
  }
1764

    
1765
  /**
1766
   * Implements QueryConditionInterface::notExists().
1767
   */
1768
  public function notExists(SelectQueryInterface $select) {
1769
    return $this->condition('', $select, 'NOT EXISTS');
1770
  }
1771

    
1772
  /**
1773
   * Implements QueryConditionInterface::conditions().
1774
   */
1775
  public function &conditions() {
1776
    return $this->conditions;
1777
  }
1778

    
1779
  /**
1780
   * Implements QueryConditionInterface::arguments().
1781
   */
1782
  public function arguments() {
1783
    // If the caller forgot to call compile() first, refuse to run.
1784
    if ($this->changed) {
1785
      return NULL;
1786
    }
1787
    return $this->arguments;
1788
  }
1789

    
1790
  /**
1791
   * Implements QueryConditionInterface::compile().
1792
   */
1793
  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1794
    // Re-compile if this condition changed or if we are compiled against a
1795
    // different query placeholder object.
1796
    if ($this->changed || isset($this->queryPlaceholderIdentifier) && ($this->queryPlaceholderIdentifier != $queryPlaceholder->uniqueIdentifier())) {
1797
      $this->queryPlaceholderIdentifier = $queryPlaceholder->uniqueIdentifier();
1798

    
1799
      $condition_fragments = array();
1800
      $arguments = array();
1801

    
1802
      $conditions = $this->conditions;
1803
      $conjunction = $conditions['#conjunction'];
1804
      unset($conditions['#conjunction']);
1805
      foreach ($conditions as $condition) {
1806
        if (empty($condition['operator'])) {
1807
          // This condition is a literal string, so let it through as is.
1808
          $condition_fragments[] = ' (' . $condition['field'] . ') ';
1809
          $arguments += $condition['value'];
1810
        }
1811
        else {
1812
          // It's a structured condition, so parse it out accordingly.
1813
          // Note that $condition['field'] will only be an object for a dependent
1814
          // DatabaseCondition object, not for a dependent subquery.
1815
          if ($condition['field'] instanceof QueryConditionInterface) {
1816
            // Compile the sub-condition recursively and add it to the list.
1817
            $condition['field']->compile($connection, $queryPlaceholder);
1818
            $condition_fragments[] = '(' . (string) $condition['field'] . ')';
1819
            $arguments += $condition['field']->arguments();
1820
          }
1821
          else {
1822
            // For simplicity, we treat all operators as the same data structure.
1823
            // In the typical degenerate case, this won't get changed.
1824
            $operator_defaults = array(
1825
              'prefix' => '',
1826
              'postfix' => '',
1827
              'delimiter' => '',
1828
              'operator' => $condition['operator'],
1829
              'use_value' => TRUE,
1830
            );
1831
            $operator = $connection->mapConditionOperator($condition['operator']);
1832
            if (!isset($operator)) {
1833
              $operator = $this->mapConditionOperator($condition['operator']);
1834
            }
1835
            $operator += $operator_defaults;
1836

    
1837
            $placeholders = array();
1838
            if ($condition['value'] instanceof SelectQueryInterface) {
1839
              $condition['value']->compile($connection, $queryPlaceholder);
1840
              $placeholders[] = (string) $condition['value'];
1841
              $arguments += $condition['value']->arguments();
1842
              // Subqueries are the actual value of the operator, we don't
1843
              // need to add another below.
1844
              $operator['use_value'] = FALSE;
1845
            }
1846
            // We assume that if there is a delimiter, then the value is an
1847
            // array. If not, it is a scalar. For simplicity, we first convert
1848
            // up to an array so that we can build the placeholders in the same way.
1849
            elseif (!$operator['delimiter']) {
1850
              $condition['value'] = array($condition['value']);
1851
            }
1852
            if ($operator['use_value']) {
1853
              foreach ($condition['value'] as $value) {
1854
                $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
1855
                $arguments[$placeholder] = $value;
1856
                $placeholders[] = $placeholder;
1857
              }
1858
            }
1859
            $condition_fragments[] = ' (' . $connection->escapeField($condition['field']) . ' ' . $operator['operator'] . ' ' . $operator['prefix'] . implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ') ';
1860
          }
1861
        }
1862
      }
1863

    
1864
      $this->changed = FALSE;
1865
      $this->stringVersion = implode($conjunction, $condition_fragments);
1866
      $this->arguments = $arguments;
1867
    }
1868
  }
1869

    
1870
  /**
1871
   * Implements QueryConditionInterface::compiled().
1872
   */
1873
  public function compiled() {
1874
    return !$this->changed;
1875
  }
1876

    
1877
  /**
1878
   * Implements PHP magic __toString method to convert the conditions to string.
1879
   *
1880
   * @return string
1881
   *   A string version of the conditions.
1882
   */
1883
  public function __toString() {
1884
    // If the caller forgot to call compile() first, refuse to run.
1885
    if ($this->changed) {
1886
      return NULL;
1887
    }
1888
    return $this->stringVersion;
1889
  }
1890

    
1891
  /**
1892
   * PHP magic __clone() method.
1893
   *
1894
   * Only copies fields that implement QueryConditionInterface. Also sets
1895
   * $this->changed to TRUE.
1896
   */
1897
  function __clone() {
1898
    $this->changed = TRUE;
1899
    foreach ($this->conditions as $key => $condition) {
1900
      if ($key !== '#conjunction') {
1901
        if ($condition['field'] instanceOf QueryConditionInterface) {
1902
          $this->conditions[$key]['field'] = clone($condition['field']);
1903
        }
1904
        if ($condition['value'] instanceOf SelectQueryInterface) {
1905
          $this->conditions[$key]['value'] = clone($condition['value']);
1906
        }
1907
      }
1908
    }
1909
  }
1910

    
1911
  /**
1912
   * Gets any special processing requirements for the condition operator.
1913
   *
1914
   * Some condition types require special processing, such as IN, because
1915
   * the value data they pass in is not a simple value. This is a simple
1916
   * overridable lookup function.
1917
   *
1918
   * @param $operator
1919
   *   The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive.
1920
   *
1921
   * @return
1922
   *   The extra handling directives for the specified operator, or NULL.
1923
   */
1924
  protected function mapConditionOperator($operator) {
1925
    // $specials does not use drupal_static as its value never changes.
1926
    static $specials = array(
1927
      'BETWEEN' => array('delimiter' => ' AND '),
1928
      'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1929
      'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1930
      'EXISTS' => array('prefix' => ' (', 'postfix' => ')'),
1931
      'NOT EXISTS' => array('prefix' => ' (', 'postfix' => ')'),
1932
      'IS NULL' => array('use_value' => FALSE),
1933
      'IS NOT NULL' => array('use_value' => FALSE),
1934
      // Use backslash for escaping wildcard characters.
1935
      'LIKE' => array('postfix' => " ESCAPE '\\\\'"),
1936
      'NOT LIKE' => array('postfix' => " ESCAPE '\\\\'"),
1937
      // These ones are here for performance reasons.
1938
      '=' => array(),
1939
      '<' => array(),
1940
      '>' => array(),
1941
      '>=' => array(),
1942
      '<=' => array(),
1943
    );
1944
    if (isset($specials[$operator])) {
1945
      $return = $specials[$operator];
1946
    }
1947
    else {
1948
      // We need to upper case because PHP index matches are case sensitive but
1949
      // do not need the more expensive drupal_strtoupper because SQL statements are ASCII.
1950
      $operator = strtoupper($operator);
1951
      $return = isset($specials[$operator]) ? $specials[$operator] : array();
1952
    }
1953

    
1954
    $return += array('operator' => $operator);
1955

    
1956
    return $return;
1957
  }
1958

    
1959
}
1960

    
1961
/**
1962
 * @} End of "addtogroup database".
1963
 */