Code coverage for /20081101/includes/database/query.inc

Line #Times calledCode
1
<?php
2
3
/**
4
 * @ingroup database
5
 * @{
6
 */
7
8
/**
9
 * Interface for a conditional clause in a query.
10
 */
112361
interface QueryConditionInterface {
12
13
  /**
14
   * Helper function to build most common conditional clauses.
15
   *
16
   * This method can take a variable number of parameters.  If called with
two
17
   * parameters, they are taken as $field and $value with $operator having
a value
18
   * of =.
19
   *
20
   * @param $field
21
   *   The name of the field to check.
22
   * @param $value
23
   *   The value to test the field against.  In most cases, this is a
scalar. For more
24
   *   complex options, it is an array.  The meaning of each element in the
array is
25
   *   dependent on the $operator.
26
   * @param $operator
27
   *   The comparison operator, such as =, <, or >=.  It also accepts more
complex
28
   *   options such as IN, LIKE, or BETWEEN.
29
   * @param $num_args
30
   *   For internal use only.  This argument is used to track the recursive
calls when
31
   *   processing complex conditions.
32
   * @return
33
   *   The called object.
34
   */
35
  public function condition($field, $value = NULL, $operator = NULL);
36
37
  /**
38
   * Add an arbitrary WHERE clause to the query.
39
   *
40
   * @param $snippet
41
   *   A portion of a WHERE clause as a prepared statement.  It must use
named placeholders,
42
   *   not ? placeholders.
43
   * @param $args
44
   *   An associative array of arguments.
45
   * @return
46
   *   The called object.
47
   */
48
  public function where($snippet, $args = array());
49
50
  /**
51
   * Gets a complete list of all conditions in this conditional clause.
52
   *
53
   * This method returns by reference.  That allows alter hooks to access
the
54
   * data structure directly and manipulate it before it gets compiled.
55
   *
56
   * The data structure that is returned is an indexed array of entries,
where
57
   * each entry looks like the following:
58
   *
59
   * array(
60
   *   'field' => $field,
61
   *   'value' => $value,
62
   *   'operator' => $operator,
63
   * );
64
   *
65
   * In the special case that $operator is NULL, the $field is taken as a
raw
66
   * SQL snippet (possibly containing a function) and $value is an
associative
67
   * array of placeholders for the snippet.
68
   *
69
   * There will also be a single array entry of #conjunction, which is the
70
   * conjunction that will be applied to the array, such as AND.
71
   */
72
  public function &conditions();
73
74
  /**
75
   * Gets a complete list of all values to insert into the prepared
statement.
76
   *
77
   * @returns
78
   *   An associative array of placeholders and values.
79
   */
80
  public function arguments();
81
82
  /**
83
   * Compiles the saved conditions for later retrieval.
84
   *
85
   * This method does not return anything, but simply prepares data to be
86
   * retrieved via __toString() and arguments().
87
   *
88
   * @param $connection
89
   *   The database connection for which to compile the conditionals.
90
   */
91
  public function compile(DatabaseConnection $connection);
92
}
93
94
95
/**
96
 * Interface for a query that can be manipulated via an alter hook.
97
 */
982361
interface QueryAlterableInterface {
99
100
  /**
101
   * Adds a tag to a query.
102
   *
103
   * Tags are strings that identify a query.  A query may have any number
of
104
   * tags.  Tags are used to mark a query so that alter hooks may decide if
they
105
   * wish to take action.  Tags should be all lower-case and contain only
letters,
106
   * numbers, and underscore, and start with a letter.  That is, they
should
107
   * follow the same rules as PHP identifiers in general.
108
   *
109
   * @param $tag
110
   *   The tag to add.
111
   */
112
  public function addTag($tag);
113
114
  /**
115
   * Determines if a given query has a given tag.
116
   *
117
   * @param $tag
118
   *   The tag to check.
119
   * @return
120
   *   TRUE if this query has been marked with this tag, FALSE otherwise.
121
   */
122
  public function hasTag($tag);
123
124
  /**
125
   * Determines if a given query has all specified tags.
126
   *
127
   * @param $tags
128
   *   A variable number of arguments, one for each tag to check.
129
   * @return
130
   *   TRUE if this query has been marked with all specified tags, FALSE
otherwise.
131
   */
132
  public function hasAllTags();
133
134
  /**
135
   * Determines if a given query has any specified tag.
136
   *
137
   * @param $tags
138
   *   A variable number of arguments, one for each tag to check.
139
   * @return
140
   *   TRUE if this query has been marked with at least one of the
specified
141
   *   tags, FALSE otherwise.
142
   */
143
  public function hasAnyTag();
144
145
  /**
146
   * Adds additional metadata to the query.
147
   *
148
   * Often, a query may need to provide additional contextual data to alter
149
   * hooks.  Alter hooks may then use that information to decide if and how
150
   * to take action.
151
   *
152
   * @param $key
153
   *   The unique identifier for this piece of metadata.  Must be a string
that
154
   *   follows the same rules as any other PHP identifier.
155
   * @param $object
156
   *   The additional data to add to the query.  May be any valid PHP
variable.
157
   *
158
   */
159
  public function addMetaData($key, $object);
160
161
  /**
162
   * Retrieves a given piece of metadata.
163
   *
164
   * @param $key
165
   *   The unique identifier for the piece of metadata to retrieve.
166
   * @return
167
   *   The previously attached metadata object, or NULL if one doesn't
exist.
168
   */
169
  public function getMetaData($key);
170
}
171
172
/**
173
 * Base class for the query builders.
174
 *
175
 * All query builders inherit from a common base class.
176
 */
1772361
abstract class Query {
178
179
  /**
180
   * The connection object on which to run this query.
181
   *
182
   * @var DatabaseConnection
183
   */
184
  protected $connection;
185
186
  /**
187
   * The query options to pass on to the connection object.
188
   *
189
   * @var array
190
   */
191
  protected $queryOptions;
192
193
  public function __construct(DatabaseConnection $connection, $options) {
1942487
    $this->connection = $connection;
1952487
    $this->queryOptions = $options;
1962487
  }
197
198
  /**
199
   * Run the query against the database.
200
   */
201
  abstract protected function execute();
202
203
  /**
204
   * Returns the query as a prepared statement string.
205
   */
206
  abstract public function __toString();
207
}
208
209
/**
210
 * General class for an abstracted INSERT operation.
211
 */
2122361
class InsertQuery extends Query {
213
214
  /**
215
   * The table on which to insert.
216
   *
217
   * @var string
218
   */
219
  protected $table;
220
221
  /**
222
   * Whether or not this query is "delay-safe".  Different database drivers
223
   * may or may not implement this feature in their own ways.
224
   *
225
   * @var boolean
226
   */
227
  protected $delay;
228
229
  /**
230
   * An array of fields on which to insert.
231
   *
232
   * @var array
233
   */
234
  protected $insertFields = array();
235
236
  /**
237
   * An array of fields which should be set to their database-defined
defaults.
238
   *
239
   * @var array
240
   */
241
  protected $defaultFields = array();
242
243
  /**
244
   * A nested array of values to insert.
245
   *
246
   * $insertValues itself is an array of arrays.  Each sub-array is an
array of
247
   * field names to values to insert.  Whether multiple insert sets
248
   * will be run in a single query or multiple queries is left to
individual drivers
249
   * to implement in whatever manner is most efficient.  The order of
values in each
250
   * sub-array must match the order of fields in $insertFields.
251
   *
252
   * @var string
253
   */
254
  protected $insertValues = array();
255
256
  public function __construct($connection, $table, Array $options =
array()) {
257731
    $options['return'] = Database::RETURN_INSERT_ID;
258731
    $options += array('delay' => FALSE);
259731
    parent::__construct($connection, $options);
260731
    $this->table = $table;
261731
  }
262
263
  /**
264
   * Add a set of field->value pairs to be inserted.
265
   *
266
   * This method may only be called once.  Calling it a second time will be
267
   * ignored.  To queue up multiple sets of values to be inserted at once,
268
   * use the values() method.
269
   *
270
   * @param $fields
271
   *   An array of fields on which to insert.  This array may be indexed or
272
   *   associative.  If indexed, the array is taken to be the list of
fields.
273
   *   If associative, the keys of the array are taken to be the fields and
274
   *   the values are taken to be corresponding values to insert.  If a
275
   *   $values argument is provided, $fields must be indexed.
276
   * @param $values
277
   *   An array of fields to insert into the database.  The values must be
278
   *   specified in the same order as the $fields array.
279
   * @return
280
   *   The called object.
281
   */
282
  public function fields(Array $fields, Array $values = array()) {
283731
    if (empty($this->insertFields)) {
284731
      if (empty($values)) {
285731
        if (!is_numeric(key($fields))) {
286731
          $values = array_values($fields);
287731
          $fields = array_keys($fields);
288731
        }
289731
      }
290731
      $this->insertFields = $fields;
291731
      if (!empty($values)) {
292731
        $this->insertValues[] = $values;
293731
      }
294731
    }
295
296731
    return $this;
2970
  }
298
299
  /**
300
   * Add another set of values to the query to be inserted.
301
   *
302
   * If $values is a numeric array, it will be assumed to be in the same
303
   * order as the original fields() call.  If it is associative, it may be
304
   * in any order as long as the keys of the array match the names of the
305
   * fields.
306
   *
307
   * @param $values
308
   *   An array of values to add to the query.
309
   * @return
310
   *   The called object.
311
   */
312
  public function values(Array $values) {
31319
    if (is_numeric(key($values))) {
3141
      $this->insertValues[] = $values;
3151
    }
316
    else {
317
      // Reorder the submitted values to match the fields array.
31819
      foreach ($this->insertFields as $key) {
31919
        $insert_values[$key] = $values[$key];
32019
      }
321
      // For consistency, the values array is always numerically indexed.
32219
      $this->insertValues[] = array_values($insert_values);
323
    }
32419
    return $this;
3250
  }
326
327
  /**
328
   * Specify fields for which the database-defaults should be used.
329
   *
330
   * If you want to force a given field to use the database-defined
default,
331
   * not NULL or undefined, use this method to instruct the database to use
332
   * default values explicitly.  In most cases this will not be necessary
333
   * unless you are inserting a row that is all default values, as you
cannot
334
   * specify no values in an INSERT query.
335
   *
336
   * Specifying a field both in fields() and in useDefaults() is an error
337
   * and will not execute.
338
   *
339
   * @param $fields
340
   *   An array of values for which to use the default values
341
   *   specified in the table definition.
342
   * @return
343
   *   The called object.
344
   */
345
  public function useDefaults(Array $fields) {
3463
    $this->defaultFields = $fields;
3473
    return $this;
3480
  }
349
350
  /**
351
   * Flag this query as being delay-safe or not.
352
   *
353
   * If this method is never called, it is assumed that the query must be
354
   * executed immediately.  If delay is set to TRUE, then the query will be
355
   * flagged to run "delayed" or "low priority" on databases that support
such
356
   * capabilities.  In that case, the database will return immediately and
the
357
   * query will be run at some point in the future.  That makes it useful
for
358
   * logging-style queries.
359
   *
360
   * If the database does not support delayed INSERT queries, this method
361
   * has no effect.
362
   *
363
   * Note that for a delayed query there is no serial ID returned, as it
won't
364
   * be created until later when the query runs.  It should therefore not
be
365
   * used if the value of the ID is known.
366
   *
367
   * @param $delay
368
   *   If TRUE, this query is delay-safe and will run delayed on supported
databases.
369
   * @return
370
   *   The called object.
371
   */
372
  public function delay($delay = TRUE) {
3730
    $this->delay = $delay;
3740
    return $this;
3750
  }
376
377
  /**
378
   * Executes the insert query.
379
   *
380
   * @return
381
   *   The last insert ID of the query, if one exists.  If the query
382
   *   was given multiple sets of values to insert, the return value is
383
   *   undefined.  If the query is flagged "delayed", then the insert ID
384
   *   won't be created until later when the query actually runs so the
385
   *   return value is also undefined.
386
   */
387
  public function execute() {
388
3890
    $last_insert_id = 0;
390
391
    // Confirm that the user did not try to specify an identical
392
    //  field and default field.
3930
    if (array_intersect($this->insertFields, $this->defaultFields)) {
3940
      throw new PDOException('You may not specify the same field to have a
value and a schema-default value.');
3950
    }
396
397
    // Each insert happens in its own query in the degenerate case. 
However,
398
    // we wrap it in a transaction so that it is atomic where possible.  On
many
399
    // databases, such as SQLite, this is also a notable performance boost.
4000
    $transaction = $this->connection->startTransaction();
4010
    $sql = (string)$this;
4020
    foreach ($this->insertValues as $insert_values) {
4030
      $last_insert_id = $this->connection->query($sql, $insert_values,
$this->queryOptions);
4040
    }
4050
    $transaction->commit();
406
407
    // Re-initialize the values array so that we can re-use this query.
4080
    $this->insertValues = array();
409
4100
    return $last_insert_id;
4110
  }
412
413
  public function __toString() {
414
415
    // Default fields are always placed first for consistency.
4160
    $insert_fields = array_merge($this->defaultFields,
$this->insertFields);
417
418
    // For simplicity, we will use the $placeholders array to inject
419
    // default keywords even though they are not, strictly speaking,
420
    // placeholders for prepared statements.
4210
    $placeholders = array();
4220
    $placeholders = array_pad($placeholders, count($this->defaultFields),
'default');
4230
    $placeholders = array_pad($placeholders, count($this->insertFields),
'?');
424
4250
    return 'INSERT INTO {'. $this->table .'} ('. implode(', ',
$insert_fields) .') VALUES ('. implode(', ', $placeholders) .')';
4260
  }
427
}
428
429
/**
430
 * General class for an abstracted MERGE operation.
431
 */
4322361
class MergeQuery extends Query {
433
434
  /**
435
   * The table on which to insert.
436
   *
437
   * @var string
438
   */
439
  protected $table;
440
441
  /**
442
   * An array of fields on which to insert.
443
   *
444
   * @var array
445
   */
446
  protected $insertFields = array();
447
448
  /**
449
   * An array of fields to update instead of the values specified in
450
   * $insertFields;
451
   *
452
   * @var array
453
   */
454
  protected $updateFields = array();
455
456
  /**
457
   * An array of key fields for this query.
458
   *
459
   * @var array
460
   */
461
  protected $keyFields = array();
462
463
  /**
464
   * An array of fields to not update in case of a duplicate record.
465
   *
466
   * @var array
467
   */
468
  protected $excludeFields = array();
469
470
  /**
471
   * An array of fields to update to an expression in case of a duplicate
record.
472
   *
473
   * This variable is a nested array in the following format:
474
   * <some field> => array(
475
   *  'condition' => <condition to execute, as a string>
476
   *  'arguments' => <array of arguments for condition, or NULL for none>
477
   * );
478
   *
479
   * @var array
480
   */
481
  protected $expressionFields = array();
482
483
  public function __construct($connection, $table, Array $options =
array()) {
4841630
    $options['return'] = Database::RETURN_AFFECTED;
4851630
    parent::__construct($connection, $options);
4861630
    $this->table = $table;
4871630
  }
488
489
  /**
490
   * Set the field->value pairs to be merged into the table.
491
   *
492
   * This method should only be called once.  It may be called either
493
   * with a single associative array or two indexed arrays.  If called
494
   * with an associative array, the keys are taken to be the fields
495
   * and the values are taken to be the corresponding values to set.
496
   * If called with two arrays, the first array is taken as the fields
497
   * and the second array is taken as the corresponding values.
498
   *
499
   * @param $fields
500
   *   An array of fields to set.
501
   * @param $values
502
   *   An array of fields to set into the database.  The values must be
503
   *   specified in the same order as the $fields array.
504
   * @return
505
   *   The called object.
506
   */
507
  public function fields(Array $fields, Array $values = array()) {
5081630
    if (count($values) > 0) {
5090
      $fields = array_combine($fields, $values);
5100
    }
5111630
    $this->insertFields = $fields;
512
5131630
    return $this;
5140
  }
515
516
  /**
517
   * Set the key field(s) to be used to insert or update into the table.
518
   *
519
   * This method should only be called once.  It may be called either
520
   * with a single associative array or two indexed arrays.  If called
521
   * with an associative array, the keys are taken to be the fields
522
   * and the values are taken to be the corresponding values to set.
523
   * If called with two arrays, the first array is taken as the fields
524
   * and the second array is taken as the corresponding values.
525
   *
526
   * These fields are the "pivot" fields of the query.  Typically they
527
   * will be the fields of the primary key.  If the record does not
528
   * yet exist, they will be inserted into the table along with the
529
   * values set in the fields() method.  If the record does exist,
530
   * these fields will be used in the WHERE clause to select the
531
   * record to update.
532
   *
533
   * @param $fields
534
   *   An array of fields to set.
535
   * @param $values
536
   *   An array of fields to set into the database.  The values must be
537
   *   specified in the same order as the $fields array.
538
   * @return
539
   *   The called object.
540
   */
541
  public function key(Array $fields, Array $values = array()) {
5421629
    if ($values) {
5430
      $fields = array_combine($fields, $values);
5440
    }
5451629
    $this->keyFields = $fields;
546
5471629
    return $this;
5480
  }
549
550
  /**
551
   * Specify fields to update in case of a duplicate record.
552
   *
553
   * If a record with the values in keys() already exists, the fields and
values
554
   * specified here will be updated in that record.  If this method is not
called,
555
   * it defaults to the same values as were passed to the fields() method.
556
   *
557
   * @param $fields
558
   *   An array of fields to set.
559
   * @param $values
560
   *   An array of fields to set into the database.  The values must be
561
   *   specified in the same order as the $fields array.
562
   * @return
563
   *   The called object.
564
   */
565
  public function update(Array $fields, Array $values = array()) {
5661
   if ($values) {
5670
      $fields = array_combine($fields, $values);
5680
    }
5691
    $this->updateFields = $fields;
570
5711
    return $this;
5720
  }
573
574
  /**
575
   * Specify fields that should not be updated in case of a duplicate
record.
576
   *
577
   * If this method is called and a record with the values in keys()
already
578
   * exists, Drupal will instead update the record with the values passed
579
   * in the fields() method except for the fields specified in this method.
That
580
   * is, calling this method is equivalent to calling update() with
identical
581
   * parameters as fields() minus the keys specified here.
582
   *
583
   * The update() method takes precedent over this method.  If update() is
called,
584
   * this method has no effect.
585
   *
586
   * @param $exclude_fields
587
   *   An array of fields in the query that should not be updated to match
those
588
   *   specified by the fields() method.
589
   *   Alternatively, the fields may be specified as a variable number of
string
590
   *   parameters.
591
   * @return
592
   *   The called object.
593
   */
594
  public function updateExcept($exclude_fields) {
5951
    if (!is_array($exclude_fields)) {
5961
      $exclude_fields = func_get_args();
5971
    }
5981
    $this->excludeFields = $exclude_fields;
599
6001
    return $this;
6010
  }
602
603
  /**
604
   * Specify fields to be updated as an expression.
605
   *
606
   * Expression fields are cases such as counter=counter+1.  This method
only
607
   * applies if a duplicate key is detected.  This method takes precedent
over
608
   * both update() and updateExcept().
609
   *
610
   * @param $field
611
   *   The field to set.
612
   * @param $expression
613
   *   The field will be set to the value of this expression.  This
parameter
614
   *   may include named placeholders.
615
   * @param $arguments
616
   *   If specified, this is an array of key/value pairs for named
placeholders
617
   *   corresponding to the expression.
618
   * @return
619
   *   The called object.
620
   */
621
  public function expression($field, $expression, Array $arguments = NULL)
{
6229
    $this->expressionFields[$field] = array(
6239
      'expression' => $expression,
6249
      'arguments' => $arguments,
625
    );
626
6279
    return $this;
6280
  }
629
630
  public function execute() {
631
632
    // In the degenerate case of this query type, we have to run multiple
633
    // queries as there is no universal single-query mechanism that will
work.
634
    // Our degenerate case is not designed for performance efficiency but
635
    // for comprehensibility.  Any practical database driver will override
636
    // this method with database-specific logic, so this function serves
only
637
    // as a fallback to aid developers of new drivers.
638
639
    // Wrap multiple queries in a transaction, if the database supports it.
6400
    $transaction = $this->connection->startTransaction();
641
642
    // Manually check if the record already exists.
6430
    $select = $this->connection->select($this->table);
6440
    foreach ($this->keyFields as $field => $value) {
6450
      $select->condition($field, $value);
6460
    }
647
6480
    $select = $select->countQuery();
6490
    $sql = (string)$select;
6500
    $arguments = $select->getArguments();
6510
    $num_existing = db_query($sql, $arguments)->fetchField();
652
653
6540
    if ($num_existing) {
655
      // If there is already an existing record, run an update query.
656
6570
      if ($this->updateFields) {
6580
        $update_fields = $this->updateFields;
6590
      }
660
      else {
6610
        $update_fields = $this->insertFields;
662
        // If there are no exclude fields, this is a no-op.
6630
        foreach ($this->excludeFields as $exclude_field) {
6640
          unset($update_fields[$exclude_field]);
6650
        }
666
      }
6670
      $update = $this->connection->update($this->table,
$this->queryOptions)->fields($update_fields);
6680
      foreach ($this->keyFields as $field => $value) {
6690
        $update->condition($field, $value);
6700
      }
6710
      foreach ($this->expressionFields as $field => $expression) {
6720
        $update->expression($field, $expression['expression'],
$expression['arguments']);
6730
      }
6740
      $update->execute();
6750
    }
676
    else {
677
      // If there is no existing record, run an insert query.
6780
      $insert_fields = $this->insertFields + $this->keyFields;
6790
      $this->connection->insert($this->table,
$this->queryOptions)->fields($insert_fields)->execute();
680
    }
681
682
    // Commit the transaction.
6830
    $transaction->commit();
6840
  }
685
686
  public function __toString() {
687
    // In the degenerate case, there is no string-able query as this
operation
688
    // is potentially two queries.
6890
    return '';
6900
  }
691
}
692
693
694
/**
695
 * General class for an abstracted DELETE operation.
696
 *
697
 * The conditional WHERE handling of this class is all inherited from
Query.
698
 */
6992361
class DeleteQuery extends Query implements QueryConditionInterface {
700
701
  /**
702
   * The table from which to delete.
703
   *
704
   * @var string
705
   */
706
  protected $table;
707
708
  /**
709
   * The condition object for this query.  Condition handling is handled
via
710
   * composition.
711
   *
712
   * @var DatabaseCondition
713
   */
714
  protected $condition;
715
716
  public function __construct(DatabaseConnection $connection, $table, Array
$options = array()) {
717720
    $options['return'] = Database::RETURN_AFFECTED;
718720
    parent::__construct($connection, $options);
719720
    $this->table = $table;
720
721720
    $this->condition = new DatabaseCondition('AND');
722720
  }
723
724
  public function condition($field, $value = NULL, $operator = '=') {
725720
    if (!isset($num_args)) {
726720
      $num_args = func_num_args();
727720
    }
728720
    $this->condition->condition($field, $value, $operator, $num_args);
729720
    return $this;
7300
  }
731
732
  public function &conditions() {
7330
    return $this->condition->conditions();
7340
  }
735
736
  public function arguments() {
7370
    return $this->condition->arguments();
7380
  }
739
740
  public function where($snippet, $args = array()) {
7410
    $this->condition->where($snippet, $args);
7420
    return $this;
7430
  }
744
745
  public function compile(DatabaseConnection $connection) {
7460
    return $this->condition->compile($connection);
7470
  }
748
749
  public function execute() {
750720
    $values = array();
751720
    if (count($this->condition)) {
752720
      $this->condition->compile($this->connection);
753720
      $values = $this->condition->arguments();
754720
    }
755
756720
    return $this->connection->query((string)$this, $values,
$this->queryOptions);
7570
  }
758
759
  public function __toString() {
760720
    $query = 'DELETE FROM {' . $this->connection->escapeTable($this->table)
. '} ';
761
762720
    if (count($this->condition)) {
763720
      $this->condition->compile($this->connection);
764720
      $query .= "\nWHERE " . $this->condition;
765720
    }
766
767720
    return $query;
7680
  }
769
}
770
771
/**
772
 * General class for an abstracted UPDATE operation.
773
 *
774
 * The conditional WHERE handling of this class is all inherited from
Query.
775
 */
7762361
class UpdateQuery extends Query implements QueryConditionInterface {
777
778
  /**
779
   * The table to update.
780
   *
781
   * @var string
782
   */
783
  protected $table;
784
785
  /**
786
   * An array of fields that will be updated.
787
   *
788
   * @var array
789
   */
790
  protected $fields;
791
792
  /**
793
   * An array of values to update to.
794
   *
795
   * @var array
796
   */
797
  protected $arguments = array();
798
799
  /**
800
   * The condition object for this query.  Condition handling is handled
via
801
   * composition.
802
   *
803
   * @var DatabaseCondition
804
   */
805
  protected $condition;
806
807
  /**
808
   * An array of fields to update to an expression in case of a duplicate
record.
809
   *
810
   * This variable is a nested array in the following format:
811
   * <some field> => array(
812
   *  'condition' => <condition to execute, as a string>
813
   *  'arguments' => <array of arguments for condition, or NULL for none>
814
   * );
815
   *
816
   * @var array
817
   */
818
  protected $expressionFields = array();
819
820
821
  public function __construct(DatabaseConnection $connection, $table, Array
$options = array()) {
82267
    $options['return'] = Database::RETURN_AFFECTED;
82367
    parent::__construct($connection, $options);
82467
    $this->table = $table;
825
82667
    $this->condition = new DatabaseCondition('AND');
82767
  }
828
829
  public function condition($field, $value = NULL, $operator = '=') {
83067
    if (!isset($num_args)) {
83167
      $num_args = func_num_args();
83267
    }
83367
    $this->condition->condition($field, $value, $operator, $num_args);
83467
    return $this;
8350
  }
836
837
  public function &conditions() {
8380
    return $this->condition->conditions();
8390
  }
840
841
  public function arguments() {
8420
    return $this->condition->arguments();
8430
  }
844
845
  public function where($snippet, $args = array()) {
8461
    $this->condition->where($snippet, $args);
8471
    return $this;
8480
  }
849
850
  public function compile(DatabaseConnection $connection) {
8510
    return $this->condition->compile($connection);
8520
  }
853
854
  /**
855
   * Add a set of field->value pairs to be updated.
856
   *
857
   * @param $fields
858
   *   An associative array of fields to write into the database.  The
array keys
859
   *   are the field names while the values are the values to which to set
them.
860
   * @return
861
   *   The called object.
862
   */
863
  public function fields(Array $fields) {
86467
    $this->fields = $fields;
86567
    return $this;
8660
  }
867
868
  /**
869
   * Specify fields to be updated as an expression.
870
   *
871
   * Expression fields are cases such as counter=counter+1.  This method
takes
872
   * precedence over fields().
873
   *
874
   * @param $field
875
   *   The field to set.
876
   * @param $expression
877
   *   The field will be set to the value of this expression.  This
parameter
878
   *   may include named placeholders.
879
   * @param $arguments
880
   *   If specified, this is an array of key/value pairs for named
placeholders
881
   *   corresponding to the expression.
882
   * @return
883
   *   The called object.
884
   */
885
  public function expression($field, $expression, Array $arguments = NULL)
{
8861
    $this->expressionFields[$field] = array(
8871
      'expression' => $expression,
8881
      'arguments' => $arguments,
889
    );
890
8911
    return $this;
8920
  }
893
894
  public function execute() {
895
896
    // Expressions take priority over literal fields, so we process those
first
897
    // and remove any literal fields that conflict.
89867
    $fields = $this->fields;
89967
    $update_values = array();
90067
    foreach ($this->expressionFields as $field => $data) {
9011
      if (!empty($data['arguments'])) {
9021
        $update_values += $data['arguments'];
9031
      }
9041
      unset($fields[$field]);
9051
    }
906
907
    // Because we filter $fields the same way here and in __toString(), the
908
    // placeholders will all match up properly.
90967
    $max_placeholder = 0;
91067
    foreach ($fields as $field => $value) {
91167
      $update_values[':db_update_placeholder_' . ($max_placeholder++)] =
$value;
91267
    }
913
91467
    if (count($this->condition)) {
91567
      $this->condition->compile($this->connection);
91667
      $update_values = array_merge($update_values,
$this->condition->arguments());
91767
    }
918
91967
    return $this->connection->query((string)$this, $update_values,
$this->queryOptions);
9200
  }
921
922
  public function __toString() {
923
    // Expressions take priority over literal fields, so we process those
first
924
    // and remove any literal fields that conflict.
92567
    $fields = $this->fields;
92667
    $update_fields = array();
92767
    foreach ($this->expressionFields as $field => $data) {
9281
      $update_fields[] = $field . '=' . $data['expression'];
9291
      unset($fields[$field]);
9301
    }
931
93267
    $max_placeholder = 0;
93367
    foreach ($fields as $field => $value) {
93467
      $update_fields[] = $field . '=:db_update_placeholder_' .
($max_placeholder++);
93567
    }
936
93767
    $query = 'UPDATE {' . $this->connection->escapeTable($this->table) . '}
SET ' . implode(', ', $update_fields);
938
93967
    if (count($this->condition)) {
94067
      $this->condition->compile($this->connection);
941
      // There is an implicit string cast on $this->condition.
94267
      $query .= "\nWHERE " . $this->condition;
94367
    }
944
94567
    return $query;
9460
  }
947
948
}
949
950
/**
951
 * Generic class for a series of conditions in a query.
952
 */
9532361
class DatabaseCondition implements QueryConditionInterface, Countable {
954
955
  protected $conditions = array();
956
  protected $arguments = array();
957
958
  protected $changed = TRUE;
959
960
  public function __construct($conjunction) {
9612370
    $this->conditions['#conjunction'] = $conjunction;
9622370
  }
963
964
  /**
965
   * Return the size of this conditional.  This is part of the Countable
interface.
966
   *
967
   * The size of the conditional is the size of its conditional array minus
968
   * one, because one element is the the conjunction.
969
   */
970
  public function count() {
9712370
    return count($this->conditions) - 1;
9720
  }
973
974
  public function condition($field, $value = NULL, $operator = '=') {
9752370
    $this->conditions[] = array(
9762370
      'field' => $field,
9772370
      'value' => $value,
9782370
      'operator' => $operator,
979
    );
980
9812370
    $this->changed = TRUE;
982
9832370
    return $this;
9840
  }
985
986
  public function where($snippet, $args = array()) {
9871
    $this->conditions[] = array(
9881
      'field' => $snippet,
9891
      'value' => $args,
9901
      'operator' => NULL,
991
    );
9921
    $this->changed = TRUE;
993
9941
    return $this;
9950
  }
996
997
  public function &conditions() {
9981
    return $this->conditions;
9990
  }
1000
1001
  public function arguments() {
1002
    // If the caller forgot to call compile() first, refuse to run.
10032370
    if ($this->changed) {
10040
      return NULL;
10050
    }
10062370
    return $this->arguments;
10070
  }
1008
1009
  public function compile(DatabaseConnection $connection) {
1010
    // This value is static, so it will increment across the entire request
1011
    // rather than just this query.  That is OK, because we only need
definitive
1012
    // placeholder names if we're going to use them for _alter hooks, which
we
1013
    // are not.  The alter hook would intervene before compilation.
10142370
    static $next_placeholder = 1;
1015
10162370
    if ($this->changed) {
1017
10182370
      $condition_fragments = array();
10192370
      $arguments = array();
1020
10212370
      $conditions = $this->conditions;
10222370
      $conjunction = $conditions['#conjunction'];
10232370
      unset($conditions['#conjunction']);
10242370
      foreach ($conditions as $condition) {
10252370
        if (empty($condition['operator'])) {
1026
          // This condition is a literal string, so let it through as is.
10271
          $condition_fragments[] = ' (' . $condition['field'] . ') ';
10281
          $arguments += $condition['value'];
10291
        }
1030
        else {
1031
          // It's a structured condition, so parse it out accordingly.
10322370
          if ($condition['field'] instanceof QueryConditionInterface) {
1033
            // Compile the sub-condition recursively and add it to the
list.
10341835
            $condition['field']->compile($connection);
10351835
            $condition_fragments[] = (string)$condition['field'];
10361835
            $arguments += $condition['field']->arguments();
10371835
          }
1038
          else {
1039
            // For simplicity, we treat all operators as the same data
structure.
1040
            // In the typical degenerate case, this won't get changed.
1041
            $operator_defaults = array(
10422370
              'prefix' => '',
10432370
              'postfix' => '',
10442370
              'delimiter' => '',
10452370
              'operator' => $condition['operator'],
10462370
            );
10472370
            $operator =
$connection->mapConditionOperator($condition['operator']);
10482370
            if (!isset($operator)) {
10492370
              $operator =
$this->mapConditionOperator($condition['operator']);
10502370
            }
10512370
            $operator += $operator_defaults;
1052
10532370
            if ($condition['value'] instanceof SelectQuery) {
10540
              $placeholders[] = (string)$condition['value'];
10550
              $arguments += $condition['value']->arguments();
10560
            }
1057
            // We assume that if there is a delimiter, then the value is an
1058
            // array.  If not, it is a scalar.  For simplicity, we first
convert
1059
            // up to an array so that we can build the placeholders in the
same way.
10602370
            elseif (!$operator['delimiter']) {
10612370
              $condition['value'] = array($condition['value']);
10622370
            }
10632370
            $placeholders = array();
10642370
            foreach ($condition['value'] as $value) {
10652370
              $placeholder = ':db_condition_placeholder_' .
$next_placeholder++;
10662370
              $arguments[$placeholder] = $value;
10672370
              $placeholders[] = $placeholder;
10682370
            }
10692370
            $condition_fragments[] = ' (' . $condition['field'] . ' ' .
$operator['operator'] . ' ' . $operator['prefix'] .
implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ')
';
1070
1071
          }
1072
        }
10732370
      }
1074
10752370
      $this->changed = FALSE;
10762370
      $this->stringVersion = implode($conjunction, $condition_fragments);
10772370
      $this->arguments = $arguments;
10782370
    }
10792370
  }
1080
1081
  public function __toString() {
1082
    // If the caller forgot to call compile() first, refuse to run.
10832370
    if ($this->changed) {
10840
      return NULL;
10850
    }
10862370
    return $this->stringVersion;
10870
  }
1088
1089
  /**
1090
   * Gets any special processing requirements for the condition operator.
1091
   *
1092
   * Some condition types require special processing, such as IN, because
1093
   * the value data they pass in is not a simple value.  This is a simple
1094
   * overridable lookup function.
1095
   *
1096
   * @param $operator
1097
   *   The condition operator, such as "IN", "BETWEEN", etc. 
Case-sensitive.
1098
   * @return
1099
   *   The extra handling directives for the specified operator, or NULL.
1100
   */
1101
  protected function mapConditionOperator($operator) {
1102
    static $specials = array(
1103
      'BETWEEN' => array('delimiter' => ' AND '),
1104
      'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' =>
')'),
1105
      'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' =>
')'),
1106
      'LIKE' => array('operator' => 'LIKE'),
11072370
    );
1108
11092370
    $return = isset($specials[$operator]) ? $specials[$operator] : array();
11102370
    $return += array('operator' => $operator);
1111
11122370
    return $return;
11130
  }
1114
1115
}
1116
1117
/**
1118
 * Returns a new DatabaseCondition, set to "OR" all conditions together.
1119
 */
11202361
function db_or() {
11211835
  return new DatabaseCondition('OR');
11220
}
1123
1124
/**
1125
 * Returns a new DatabaseCondition, set to "AND" all conditions together.
1126
 */
11272361
function db_and() {
11281756
  return new DatabaseCondition('AND');
11290
}
1130
1131
/**
1132
 * Returns a new DatabaseCondition, set to "XOR" all conditions together.
1133
 */
11342361
function db_xor() {
11350
  return new DatabaseCondition('XOR');
11360
}
1137
1138
/**
1139
 * Returns a new DatabaseCondition, set to the specified conjunction.
1140
 *
1141
 * @param
1142
 *   The conjunction (AND, OR, XOR, etc.) to use on conditions.
1143
 */
11442361
function db_condition($conjunction) {
11450
  return new DatabaseCondition($conjunction);
11460
}
1147
1148
/**
1149
 * @} End of "ingroup database".
1150
 */
11512361