Simpletest Coverage - includes/database/query.inc

1 <?php
2 // $Id: query.inc,v 1.27 2009/07/21 01:56:36 webchick Exp $
3
4 /**
5 * @ingroup database
6 * @{
7 */
8
9 /**
10 * @file
11 * Non-specific Database query code. Used by all engines.
12 */
13
14 /**
15 * Interface for a conditional clause in a query.
16 */
17 interface QueryConditionInterface {
18
19 /**
20 * Helper function to build most common conditional clauses.
21 *
22 * This method can take a variable number of parameters. If called with two
23 * parameters, they are taken as $field and $value with $operator having a value
24 * of =.
25 *
26 * @param $field
27 * The name of the field to check.
28 * @param $value
29 * The value to test the field against. In most cases, this is a scalar. For more
30 * complex options, it is an array. The meaning of each element in the array is
31 * dependent on the $operator.
32 * @param $operator
33 * The comparison operator, such as =, <, or >=. It also accepts more complex
34 * options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is an array
35 * = otherwise.
36 * @return
37 * The called object.
38 */
39 public function condition($field, $value = NULL, $operator = NULL);
40
41 /**
42 * Add an arbitrary WHERE clause to the query.
43 *
44 * @param $snippet
45 * A portion of a WHERE clause as a prepared statement. It must use named placeholders,
46 * not ? placeholders.
47 * @param $args
48 * An associative array of arguments.
49 * @return
50 * The called object.
51 */
52 public function where($snippet, $args = array());
53
54 /**
55 * Set a condition that the specified field be NULL.
56 *
57 * @param $field
58 * The name of the field to check.
59 * @return
60 * The called object.
61 */
62 public function isNull($field);
63
64 /**
65 * Set a condition that the specified field be NOT NULL.
66 *
67 * @param $field
68 * The name of the field to check.
69 * @return
70 * The called object.
71 */
72 public function isNotNull($field);
73
74 /**
75 * Gets a complete list of all conditions in this conditional clause.
76 *
77 * This method returns by reference. That allows alter hooks to access the
78 * data structure directly and manipulate it before it gets compiled.
79 *
80 * The data structure that is returned is an indexed array of entries, where
81 * each entry looks like the following:
82 *
83 * array(
84 * 'field' => $field,
85 * 'value' => $value,
86 * 'operator' => $operator,
87 * );
88 *
89 * In the special case that $operator is NULL, the $field is taken as a raw
90 * SQL snippet (possibly containing a function) and $value is an associative
91 * array of placeholders for the snippet.
92 *
93 * There will also be a single array entry of #conjunction, which is the
94 * conjunction that will be applied to the array, such as AND.
95 */
96 public function &conditions();
97
98 /**
99 * Gets a complete list of all values to insert into the prepared statement.
100 *
101 * @returns
102 * An associative array of placeholders and values.
103 */
104 public function arguments();
105
106 /**
107 * Compiles the saved conditions for later retrieval.
108 *
109 * This method does not return anything, but simply prepares data to be
110 * retrieved via __toString() and arguments().
111 *
112 * @param $connection
113 * The database connection for which to compile the conditionals.
114 */
115 public function compile(DatabaseConnection $connection);
116 }
117
118
119 /**
120 * Interface for a query that can be manipulated via an alter hook.
121 */
122 interface QueryAlterableInterface {
123
124 /**
125 * Adds a tag to a query.
126 *
127 * Tags are strings that identify a query. A query may have any number of
128 * tags. Tags are used to mark a query so that alter hooks may decide if they
129 * wish to take action. Tags should be all lower-case and contain only letters,
130 * numbers, and underscore, and start with a letter. That is, they should
131 * follow the same rules as PHP identifiers in general.
132 *
133 * @param $tag
134 * The tag to add.
135 * @return
136 * The called object.
137 */
138 public function addTag($tag);
139
140 /**
141 * Determines if a given query has a given tag.
142 *
143 * @param $tag
144 * The tag to check.
145 * @return
146 * TRUE if this query has been marked with this tag, FALSE otherwise.
147 */
148 public function hasTag($tag);
149
150 /**
151 * Determines if a given query has all specified tags.
152 *
153 * @param $tags
154 * A variable number of arguments, one for each tag to check.
155 * @return
156 * TRUE if this query has been marked with all specified tags, FALSE otherwise.
157 */
158 public function hasAllTags();
159
160 /**
161 * Determines if a given query has any specified tag.
162 *
163 * @param $tags
164 * A variable number of arguments, one for each tag to check.
165 * @return
166 * TRUE if this query has been marked with at least one of the specified
167 * tags, FALSE otherwise.
168 */
169 public function hasAnyTag();
170
171 /**
172 * Adds additional metadata to the query.
173 *
174 * Often, a query may need to provide additional contextual data to alter
175 * hooks. Alter hooks may then use that information to decide if and how
176 * to take action.
177 *
178 * @param $key
179 * The unique identifier for this piece of metadata. Must be a string that
180 * follows the same rules as any other PHP identifier.
181 * @param $object
182 * The additional data to add to the query. May be any valid PHP variable.
183 * @return
184 * The called object.
185 */
186 public function addMetaData($key, $object);
187
188 /**
189 * Retrieves a given piece of metadata.
190 *
191 * @param $key
192 * The unique identifier for the piece of metadata to retrieve.
193 * @return
194 * The previously attached metadata object, or NULL if one doesn't exist.
195 */
196 public function getMetaData($key);
197 }
198
199 /**
200 * Base class for the query builders.
201 *
202 * All query builders inherit from a common base class.
203 */
204 abstract class Query {
205
206 /**
207 * The connection object on which to run this query.
208 *
209 * @var DatabaseConnection
210 */
211 protected $connection;
212
213 /**
214 * The query options to pass on to the connection object.
215 *
216 * @var array
217 */
218 protected $queryOptions;
219
220 public function __construct(DatabaseConnection $connection, $options) {
221 $this->connection = $connection;
222 $this->queryOptions = $options;
223 }
224
225 /**
226 * Run the query against the database.
227 */
228 abstract protected function execute();
229
230 /**
231 * __toString() magic method.
232 *
233 * The toString operation is how we compile a query object to a prepared statement.
234 *
235 * @return
236 * A prepared statement query string for this object.
237 */
238 abstract public function __toString();
239 }
240
241 /**
242 * General class for an abstracted INSERT operation.
243 */
244 class InsertQuery extends Query {
245
246 /**
247 * The table on which to insert.
248 *
249 * @var string
250 */
251 protected $table;
252
253 /**
254 * Whether or not this query is "delay-safe". Different database drivers
255 * may or may not implement this feature in their own ways.
256 *
257 * @var boolean
258 */
259 protected $delay;
260
261 /**
262 * An array of fields on which to insert.
263 *
264 * @var array
265 */
266 protected $insertFields = array();
267
268 /**
269 * An array of fields which should be set to their database-defined defaults.
270 *
271 * @var array
272 */
273 protected $defaultFields = array();
274
275 /**
276 * A nested array of values to insert.
277 *
278 * $insertValues itself is an array of arrays. Each sub-array is an array of
279 * field names to values to insert. Whether multiple insert sets
280 * will be run in a single query or multiple queries is left to individual drivers
281 * to implement in whatever manner is most efficient. The order of values in each
282 * sub-array must match the order of fields in $insertFields.
283 *
284 * @var string
285 */
286 protected $insertValues = array();
287
288 /**
289 * A SelectQuery object to fetch the rows that should be inserted.
290 *
291 * @var SelectQueryInterface
292 */
293 protected $fromQuery;
294
295 public function __construct($connection, $table, array $options = array()) {
296 if (!isset($options['return'])) {
297 $options['return'] = Database::RETURN_INSERT_ID;
298 }
299 $options += array('delay' => FALSE);
300 parent::__construct($connection, $options);
301 $this->table = $table;
302 }
303
304 /**
305 * Add a set of field->value pairs to be inserted.
306 *
307 * This method may only be called once. Calling it a second time will be
308 * ignored. To queue up multiple sets of values to be inserted at once,
309 * use the values() method.
310 *
311 * @param $fields
312 * An array of fields on which to insert. This array may be indexed or
313 * associative. If indexed, the array is taken to be the list of fields.
314 * If associative, the keys of the array are taken to be the fields and
315 * the values are taken to be corresponding values to insert. If a
316 * $values argument is provided, $fields must be indexed.
317 * @param $values
318 * An array of fields to insert into the database. The values must be
319 * specified in the same order as the $fields array.
320 * @return
321 * The called object.
322 */
323 public function fields(array $fields, array $values = array()) {
324 if (empty($this->insertFields)) {
325 if (empty($values)) {
326 if (!is_numeric(key($fields))) {
327 $values = array_values($fields);
328 $fields = array_keys($fields);
329 }
330 }
331 $this->insertFields = $fields;
332 if (!empty($values)) {
333 $this->insertValues[] = $values;
334 }
335 }
336
337 return $this;
338 }
339
340 /**
341 * Add another set of values to the query to be inserted.
342 *
343 * If $values is a numeric array, it will be assumed to be in the same
344 * order as the original fields() call. If it is associative, it may be
345 * in any order as long as the keys of the array match the names of the
346 * fields.
347 *
348 * @param $values
349 * An array of values to add to the query.
350 * @return
351 * The called object.
352 */
353 public function values(array $values) {
354 if (is_numeric(key($values))) {
355 $this->insertValues[] = $values;
356 }
357 else {
358 // Reorder the submitted values to match the fields array.
359 foreach ($this->insertFields as $key) {
360 $insert_values[$key] = $values[$key];
361 }
362 // For consistency, the values array is always numerically indexed.
363 $this->insertValues[] = array_values($insert_values);
364 }
365 return $this;
366 }
367
368 /**
369 * Specify fields for which the database-defaults should be used.
370 *
371 * If you want to force a given field to use the database-defined default,
372 * not NULL or undefined, use this method to instruct the database to use
373 * default values explicitly. In most cases this will not be necessary
374 * unless you are inserting a row that is all default values, as you cannot
375 * specify no values in an INSERT query.
376 *
377 * Specifying a field both in fields() and in useDefaults() is an error
378 * and will not execute.
379 *
380 * @param $fields
381 * An array of values for which to use the default values
382 * specified in the table definition.
383 * @return
384 * The called object.
385 */
386 public function useDefaults(array $fields) {
387 $this->defaultFields = $fields;
388 return $this;
389 }
390
391 /**
392 * Flag this query as being delay-safe or not.
393 *
394 * If this method is never called, it is assumed that the query must be
395 * executed immediately. If delay is set to TRUE, then the query will be
396 * flagged to run "delayed" or "low priority" on databases that support such
397 * capabilities. In that case, the database will return immediately and the
398 * query will be run at some point in the future. That makes it useful for
399 * logging-style queries.
400 *
401 * If the database does not support delayed INSERT queries, this method
402 * has no effect.
403 *
404 * Note that for a delayed query there is no serial ID returned, as it won't
405 * be created until later when the query runs. It should therefore not be
406 * used if the value of the ID is known.
407 *
408 * @param $delay
409 * If TRUE, this query is delay-safe and will run delayed on supported databases.
410 * @return
411 * The called object.
412 */
413 public function delay($delay = TRUE) {
414 $this->delay = $delay;
415 return $this;
416 }
417
418 public function from(SelectQueryInterface $query) {
419 $this->fromQuery = $query;
420 return $this;
421 }
422
423 /**
424 * Executes the insert query.
425 *
426 * @return
427 * The last insert ID of the query, if one exists. If the query
428 * was given multiple sets of values to insert, the return value is
429 * undefined. If the query is flagged "delayed", then the insert ID
430 * won't be created until later when the query actually runs so the
431 * return value is also undefined. If no fields are specified, this
432 * method will do nothing and return NULL. That makes it safe to use
433 * in multi-insert loops.
434 */
435 public function execute() {
436 if (!$this->preExecute()) {
437 return NULL;
438 }
439
440 // If we're selecting from a SelectQuery, finish building the query and
441 // pass it back, as any remaining options are irrelevant.
442 if (!empty($this->fromQuery)) {
443 $sql = (string)$this;
444 // The SelectQuery may contain arguments, load and pass them through.
445 return $this->connection->query($sql, $this->fromQuery->getArguments(), $this->queryOptions);
446 }
447
448 $last_insert_id = 0;
449
450 // Each insert happens in its own query in the degenerate case. However,
451 // we wrap it in a transaction so that it is atomic where possible. On many
452 // databases, such as SQLite, this is also a notable performance boost.
453 $transaction = $this->connection->startTransaction();
454 $sql = (string)$this;
455 foreach ($this->insertValues as $insert_values) {
456 $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions);
457 }
458
459 // Re-initialize the values array so that we can re-use this query.
460 $this->insertValues = array();
461
462 // Transaction commits here where $transaction looses scope.
463
464 return $last_insert_id;
465 }
466
467 public function __toString() {
468
469 // Default fields are always placed first for consistency.
470 $insert_fields = array_merge($this->defaultFields, $this->insertFields);
471
472 if (!empty($this->fromQuery)) {
473 return "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
474 }
475
476 // For simplicity, we will use the $placeholders array to inject
477 // default keywords even though they are not, strictly speaking,
478 // placeholders for prepared statements.
479 $placeholders = array();
480 $placeholders = array_pad($placeholders, count($this->defaultFields), 'default');
481 $placeholders = array_pad($placeholders, count($this->insertFields), '?');
482
483 return 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')';
484 }
485
486 /**
487 * Generic preparation and validation for an INSERT query.
488 *
489 * @return
490 * TRUE if the validation was successful, FALSE if not.
491 */
492 protected function preExecute() {
493 // Confirm that the user did not try to specify an identical
494 // field and default field.
495 if (array_intersect($this->insertFields, $this->defaultFields)) {
496 throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
497 }
498
499 if (!empty($this->fromQuery)) {
500 // We have to assume that the used aliases match the insert fields.
501 // Regular fields are added to the query before expressions, maintain the
502 // same order for the insert fields.
503 // This behavior can be overriden by calling fields() manually as only the
504 // first call to fields() does have an effect.
505 $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions())));
506 }
507
508 // Don't execute query without fields.
509 if (count($this->insertFields) + count($this->defaultFields) == 0) {
510 throw new NoFieldsException('There are no fields available to insert with.');
511 }
512
513 // If no values have been added, silently ignore this query. This can happen
514 // if values are added conditionally, so we don't want to throw an
515 // exception.
516 if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
517 return FALSE;
518 }
519 return TRUE;
520 }
521 }
522
523 /**
524 * General class for an abstracted MERGE operation.
525 */
526 class MergeQuery extends Query {
527
528 /**
529 * The table on which to insert.
530 *
531 * @var string
532 */
533 protected $table;
534
535 /**
536 * An array of fields on which to insert.
537 *
538 * @var array
539 */
540 protected $insertFields = array();
541
542 /**
543 * An array of fields to update instead of the values specified in
544 * $insertFields;
545 *
546 * @var array
547 */
548 protected $updateFields = array();
549
550 /**
551 * An array of key fields for this query.
552 *
553 * @var array
554 */
555 protected $keyFields = array();
556
557 /**
558 * An array of fields to not update in case of a duplicate record.
559 *
560 * @var array
561 */
562 protected $excludeFields = array();
563
564 /**
565 * An array of fields to update to an expression in case of a duplicate record.
566 *
567 * This variable is a nested array in the following format:
568 * <some field> => array(
569 * 'condition' => <condition to execute, as a string>
570 * 'arguments' => <array of arguments for condition, or NULL for none>
571 * );
572 *
573 * @var array
574 */
575 protected $expressionFields = array();
576
577 public function __construct($connection, $table, array $options = array()) {
578 $options['return'] = Database::RETURN_AFFECTED;
579 parent::__construct($connection, $options);
580 $this->table = $table;
581 }
582
583 /**
584 * Set the field->value pairs to be merged into the table.
585 *
586 * This method should only be called once. It may be called either
587 * with a single associative array or two indexed arrays. If called
588 * with an associative array, the keys are taken to be the fields
589 * and the values are taken to be the corresponding values to set.
590 * If called with two arrays, the first array is taken as the fields
591 * and the second array is taken as the corresponding values.
592 *
593 * @param $fields
594 * An array of fields to set.
595 * @param $values
596 * An array of fields to set into the database. The values must be
597 * specified in the same order as the $fields array.
598 * @return
599 * The called object.
600 */
601 public function fields(array $fields, array $values = array()) {
602 if (count($values) > 0) {
603 $fields = array_combine($fields, $values);
604 }
605 $this->insertFields = $fields;
606
607 return $this;
608 }
609
610 /**
611 * Set the key field(s) to be used to insert or update into the table.
612 *
613 * This method should only be called once. It may be called either
614 * with a single associative array or two indexed arrays. If called
615 * with an associative array, the keys are taken to be the fields
616 * and the values are taken to be the corresponding values to set.
617 * If called with two arrays, the first array is taken as the fields
618 * and the second array is taken as the corresponding values.
619 *
620 * These fields are the "pivot" fields of the query. Typically they
621 * will be the fields of the primary key. If the record does not
622 * yet exist, they will be inserted into the table along with the
623 * values set in the fields() method. If the record does exist,
624 * these fields will be used in the WHERE clause to select the
625 * record to update.
626 *
627 * @param $fields
628 * An array of fields to set.
629 * @param $values
630 * An array of fields to set into the database. The values must be
631 * specified in the same order as the $fields array.
632 * @return
633 * The called object.
634 */
635 public function key(array $fields, array $values = array()) {
636 if ($values) {
637 $fields = array_combine($fields, $values);
638 }
639 $this->keyFields = $fields;
640
641 return $this;
642 }
643
644 /**
645 * Specify fields to update in case of a duplicate record.
646 *
647 * If a record with the values in keys() already exists, the fields and values
648 * specified here will be updated in that record. If this method is not called,
649 * it defaults to the same values as were passed to the fields() method.
650 *
651 * @param $fields
652 * An array of fields to set.
653 * @param $values
654 * An array of fields to set into the database. The values must be
655 * specified in the same order as the $fields array.
656 * @return
657 * The called object.
658 */
659 public function update(array $fields, array $values = array()) {
660 if ($values) {
661 $fields = array_combine($fields, $values);
662 }
663 $this->updateFields = $fields;
664
665 return $this;
666 }
667
668 /**
669 * Specify fields that should not be updated in case of a duplicate record.
670 *
671 * If this method is called and a record with the values in keys() already
672 * exists, Drupal will instead update the record with the values passed
673 * in the fields() method except for the fields specified in this method. That
674 * is, calling this method is equivalent to calling update() with identical
675 * parameters as fields() minus the keys specified here.
676 *
677 * The update() method takes precedent over this method. If update() is called,
678 * this method has no effect.
679 *
680 * @param $exclude_fields
681 * An array of fields in the query that should not be updated to match those
682 * specified by the fields() method.
683 * Alternatively, the fields may be specified as a variable number of string
684 * parameters.
685 * @return
686 * The called object.
687 */
688 public function updateExcept($exclude_fields) {
689 if (!is_array($exclude_fields)) {
690 $exclude_fields = func_get_args();
691 }
692 $this->excludeFields = $exclude_fields;
693
694 return $this;
695 }
696
697 /**
698 * Specify fields to be updated as an expression.
699 *
700 * Expression fields are cases such as counter=counter+1. This method only
701 * applies if a duplicate key is detected. This method takes precedent over
702 * both update() and updateExcept().
703 *
704 * @param $field
705 * The field to set.
706 * @param $expression
707 * The field will be set to the value of this expression. This parameter
708 * may include named placeholders.
709 * @param $arguments
710 * If specified, this is an array of key/value pairs for named placeholders
711 * corresponding to the expression.
712 * @return
713 * The called object.
714 */
715 public function expression($field, $expression, array $arguments = NULL) {
716 $this->expressionFields[$field] = array(
717 'expression' => $expression,
718 'arguments' => $arguments,
719 );
720
721 return $this;
722 }
723
724 public function execute() {
725
726 // A merge query without any key field is invalid.
727 if (count($this->keyFields) == 0) {
728 throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query");
729 }
730
731 // In the degenerate case of this query type, we have to run multiple
732 // queries as there is no universal single-query mechanism that will work.
733 // Our degenerate case is not designed for performance efficiency but
734 // for comprehensibility. Any practical database driver will override
735 // this method with database-specific logic, so this function serves only
736 // as a fallback to aid developers of new drivers.
737
738 // Wrap multiple queries in a transaction, if the database supports it.
739 $transaction = $this->connection->startTransaction();
740
741 // Manually check if the record already exists.
742 $select = $this->connection->select($this->table);
743 foreach ($this->keyFields as $field => $value) {
744 $select->condition($field, $value);
745 }
746
747 $select = $select->countQuery();
748 $sql = (string)$select;
749 $arguments = $select->getArguments();
750 $num_existing = db_query($sql, $arguments)->fetchField();
751
752
753 if ($num_existing) {
754 // If there is already an existing record, run an update query.
755
756 if ($this->updateFields) {
757 $update_fields = $this->updateFields;
758 }
759 else {
760 $update_fields = $this->insertFields;
761 // If there are no exclude fields, this is a no-op.
762 foreach ($this->excludeFields as $exclude_field) {
763 unset($update_fields[$exclude_field]);
764 }
765 }
766 if ($update_fields || $this->expressionFields) {
767 // Only run the update if there are no fields or expressions to update.
768 $update = $this->connection->update($this->table, $this->queryOptions)->fields($update_fields);
769 foreach ($this->keyFields as $field => $value) {
770 $update->condition($field, $value);
771 }
772 foreach ($this->expressionFields as $field => $expression) {
773 $update->expression($field, $expression['expression'], $expression['arguments']);
774 }
775 $update->execute();
776 }
777 }
778 else {
779 // If there is no existing record, run an insert query.
780 $insert_fields = $this->insertFields + $this->keyFields;
781 $this->connection->insert($this->table, $this->queryOptions)->fields($insert_fields)->execute();
782 }
783
784 // Transaction commits here where $transaction looses scope.
785 }
786
787 public function __toString() {
788 // In the degenerate case, there is no string-able query as this operation
789 // is potentially two queries.
790 return '';
791 }
792 }
793
794
795 /**
796 * General class for an abstracted DELETE operation.
797 */
798 class DeleteQuery extends Query implements QueryConditionInterface {
799
800 /**
801 * The table from which to delete.
802 *
803 * @var string
804 */
805 protected $table;
806
807 /**
808 * The condition object for this query. Condition handling is handled via
809 * composition.
810 *
811 * @var DatabaseCondition
812 */
813 protected $condition;
814
815 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
816 $options['return'] = Database::RETURN_AFFECTED;
817 parent::__construct($connection, $options);
818 $this->table = $table;
819
820 $this->condition = new DatabaseCondition('AND');
821 }
822
823 public function condition($field, $value = NULL, $operator = NULL) {
824 $this->condition->condition($field, $value, $operator);
825 return $this;
826 }
827
828 public function isNull($field) {
829 $this->condition->isNull($field);
830 return $this;
831 }
832
833 public function isNotNull($field) {
834 $this->condition->isNotNull($field);
835 return $this;
836 }
837
838 public function &conditions() {
839 return $this->condition->conditions();
840 }
841
842 public function arguments() {
843 return $this->condition->arguments();
844 }
845
846 public function where($snippet, $args = array()) {
847 $this->condition->where($snippet, $args);
848 return $this;
849 }
850
851 public function compile(DatabaseConnection $connection) {
852 return $this->condition->compile($connection);
853 }
854
855 public function execute() {
856 $values = array();
857 if (count($this->condition)) {
858 $this->condition->compile($this->connection);
859 $values = $this->condition->arguments();
860 }
861
862 return $this->connection->query((string)$this, $values, $this->queryOptions);
863 }
864
865 public function __toString() {
866 $query = 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} ';
867
868 if (count($this->condition)) {
869 $this->condition->compile($this->connection);
870 $query .= "\nWHERE " . $this->condition;
871 }
872
873 return $query;
874 }
875 }
876
877
878 /**
879 * General class for an abstracted TRUNCATE operation.
880 */
881 class TruncateQuery extends Query {
882
883 /**
884 * The table from which to delete.
885 *
886 * @var string
887 */
888 protected $table;
889
890 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
891 $options['return'] = Database::RETURN_AFFECTED;
892 parent::__construct($connection, $options);
893 $this->table = $table;
894 }
895
896 public function compile(DatabaseConnection $connection) {
897 return $this->condition->compile($connection);
898 }
899
900 public function execute() {
901 return $this->connection->query((string)$this, array(), $this->queryOptions);
902 }
903
904 public function __toString() {
905 return 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} ';
906 }
907 }
908
909 /**
910 * General class for an abstracted UPDATE operation.
911 */
912 class UpdateQuery extends Query implements QueryConditionInterface {
913
914 /**
915 * The table to update.
916 *
917 * @var string
918 */
919 protected $table;
920
921 /**
922 * An array of fields that will be updated.
923 *
924 * @var array
925 */
926 protected $fields = array();
927
928 /**
929 * An array of values to update to.
930 *
931 * @var array
932 */
933 protected $arguments = array();
934
935 /**
936 * The condition object for this query. Condition handling is handled via
937 * composition.
938 *
939 * @var DatabaseCondition
940 */
941 protected $condition;
942
943 /**
944 * An array of fields to update to an expression in case of a duplicate record.
945 *
946 * This variable is a nested array in the following format:
947 * <some field> => array(
948 * 'condition' => <condition to execute, as a string>
949 * 'arguments' => <array of arguments for condition, or NULL for none>
950 * );
951 *
952 * @var array
953 */
954 protected $expressionFields = array();
955
956
957 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
958 $options['return'] = Database::RETURN_AFFECTED;
959 parent::__construct($connection, $options);
960 $this->table = $table;
961
962 $this->condition = new DatabaseCondition('AND');
963 }
964
965 public function condition($field, $value = NULL, $operator = NULL) {
966 $this->condition->condition($field, $value, $operator);
967 return $this;
968 }
969
970 public function isNull($field) {
971 $this->condition->isNull($field);
972 return $this;
973 }
974
975 public function isNotNull($field) {
976 $this->condition->isNotNull($field);
977 return $this;
978 }
979
980 public function &conditions() {
981 return $this->condition->conditions();
982 }
983
984 public function arguments() {
985 return $this->condition->arguments();
986 }
987
988 public function where($snippet, $args = array()) {
989 $this->condition->where($snippet, $args);
990 return $this;
991 }
992
993 public function compile(DatabaseConnection $connection) {
994 return $this->condition->compile($connection);
995 }
996
997 /**
998 * Add a set of field->value pairs to be updated.
999 *
1000 * @param $fields
1001 * An associative array of fields to write into the database. The array keys
1002 * are the field names while the values are the values to which to set them.
1003 * @return
1004 * The called object.
1005 */
1006 public function fields(array $fields) {
1007 $this->fields = $fields;
1008 return $this;
1009 }
1010
1011 /**
1012 * Specify fields to be updated as an expression.
1013 *
1014 * Expression fields are cases such as counter=counter+1. This method takes
1015 * precedence over fields().
1016 *
1017 * @param $field
1018 * The field to set.
1019 * @param $expression
1020 * The field will be set to the value of this expression. This parameter
1021 * may include named placeholders.
1022 * @param $arguments
1023 * If specified, this is an array of key/value pairs for named placeholders
1024 * corresponding to the expression.
1025 * @return
1026 * The called object.
1027 */
1028 public function expression($field, $expression, array $arguments = NULL) {
1029 $this->expressionFields[$field] = array(
1030 'expression' => $expression,
1031 'arguments' => $arguments,
1032 );
1033
1034 return $this;
1035 }
1036
1037 public function execute() {
1038
1039 // Expressions take priority over literal fields, so we process those first
1040 // and remove any literal fields that conflict.
1041 $fields = $this->fields;
1042 $update_values = array();
1043 foreach ($this->expressionFields as $field => $data) {
1044 if (!empty($data['arguments'])) {
1045 $update_values += $data['arguments'];
1046 }
1047 unset($fields[$field]);
1048 }
1049
1050 // Because we filter $fields the same way here and in __toString(), the
1051 // placeholders will all match up properly.
1052 $max_placeholder = 0;
1053 foreach ($fields as $field => $value) {
1054 $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value;
1055 }
1056
1057 if (count($this->condition)) {
1058 $this->condition->compile($this->connection);
1059 $update_values = array_merge($update_values, $this->condition->arguments());
1060 }
1061
1062 return $this->connection->query((string)$this, $update_values, $this->queryOptions);
1063 }
1064
1065 public function __toString() {
1066 // Expressions take priority over literal fields, so we process those first
1067 // and remove any literal fields that conflict.
1068 $fields = $this->fields;
1069 $update_fields = array();
1070 foreach ($this->expressionFields as $field => $data) {
1071 $update_fields[] = $field . '=' . $data['expression'];
1072 unset($fields[$field]);
1073 }
1074
1075 $max_placeholder = 0;
1076 foreach ($fields as $field => $value) {
1077 $update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++);
1078 }
1079
1080 $query = 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
1081
1082 if (count($this->condition)) {
1083 $this->condition->compile($this->connection);
1084 // There is an implicit string cast on $this->condition.
1085 $query .= "\nWHERE " . $this->condition;
1086 }
1087
1088 return $query;
1089 }
1090
1091 }
1092
1093 /**
1094 * Generic class for a series of conditions in a query.
1095 */
1096 class DatabaseCondition implements QueryConditionInterface, Countable {
1097
1098 protected $conditions = array();
1099 protected $arguments = array();
1100
1101 protected $changed = TRUE;
1102
1103 public function __construct($conjunction) {
1104 $this->conditions['#conjunction'] = $conjunction;
1105 }
1106
1107 /**
1108 * Return the size of this conditional. This is part of the Countable interface.
1109 *
1110 * The size of the conditional is the size of its conditional array minus
1111 * one, because one element is the the conjunction.
1112 */
1113 public function count() {
1114 return count($this->conditions) - 1;
1115 }
1116
1117 public function condition($field, $value = NULL, $operator = NULL) {
1118 if (!isset($operator)) {
1119 $operator = is_array($value) ? 'IN' : '=';
1120 }
1121 $this->conditions[] = array(
1122 'field' => $field,
1123 'value' => $value,
1124 'operator' => $operator,
1125 );
1126
1127 $this->changed = TRUE;
1128
1129 return $this;
1130 }
1131
1132 public function where($snippet, $args = array()) {
1133 $this->conditions[] = array(
1134 'field' => $snippet,
1135 'value' => $args,
1136 'operator' => NULL,
1137 );
1138 $this->changed = TRUE;
1139
1140 return $this;
1141 }
1142
1143 public function isNull($field) {
1144 return $this->condition($field, NULL, 'IS NULL');
1145 }
1146
1147 public function isNotNull($field) {
1148 return $this->condition($field, NULL, 'IS NOT NULL');
1149 }
1150
1151 public function &conditions() {
1152 return $this->conditions;
1153 }
1154
1155 public function arguments() {
1156 // If the caller forgot to call compile() first, refuse to run.
1157 if ($this->changed) {
1158 return NULL;
1159 }
1160 return $this->arguments;
1161 }
1162
1163 public function compile(DatabaseConnection $connection) {
1164 // This value is static, so it will increment across the entire request
1165 // rather than just this query. That is OK, because we only need definitive
1166 // placeholder names if we're going to use them for _alter hooks, which we
1167 // are not. The alter hook would intervene before compilation.
1168 // $next_placeholder does not use drupal_static as it increments and should
1169 // never be reset during a request.
1170 static $next_placeholder = 1;
1171
1172 if ($this->changed) {
1173
1174 $condition_fragments = array();
1175 $arguments = array();
1176
1177 $conditions = $this->conditions;
1178 $conjunction = $conditions['#conjunction'];
1179 unset($conditions['#conjunction']);
1180 foreach ($conditions as $condition) {
1181 if (empty($condition['operator'])) {
1182 // This condition is a literal string, so let it through as is.
1183 $condition_fragments[] = ' (' . $condition['field'] . ') ';
1184 $arguments += $condition['value'];
1185 }
1186 else {
1187 // It's a structured condition, so parse it out accordingly.
1188 if ($condition['field'] instanceof QueryConditionInterface) {
1189 // Compile the sub-condition recursively and add it to the list.
1190 $condition['field']->compile($connection);
1191 $condition_fragments[] = '(' . (string)$condition['field'] . ')';
1192 $arguments += $condition['field']->arguments();
1193 }
1194 else {
1195 // For simplicity, we treat all operators as the same data structure.
1196 // In the typical degenerate case, this won't get changed.
1197 $operator_defaults = array(
1198 'prefix' => '',
1199 'postfix' => '',
1200 'delimiter' => '',
1201 'operator' => $condition['operator'],
1202 'use_value' => TRUE,
1203 );
1204 $operator = $connection->mapConditionOperator($condition['operator']);
1205 if (!isset($operator)) {
1206 $operator = $this->mapConditionOperator($condition['operator']);
1207 }
1208 $operator += $operator_defaults;
1209
1210 $placeholders = array();
1211 if ($condition['value'] instanceof SelectQuery) {
1212 $placeholders[] = (string)$condition['value'];
1213 $arguments += $condition['value']->arguments();
1214 }
1215 // We assume that if there is a delimiter, then the value is an
1216 // array. If not, it is a scalar. For simplicity, we first convert
1217 // up to an array so that we can build the placeholders in the same way.
1218 elseif (!$operator['delimiter']) {
1219 $condition['value'] = array($condition['value']);
1220 }
1221 if ($operator['use_value']) {
1222 foreach ($condition['value'] as $value) {
1223 $placeholder = ':db_condition_placeholder_' . $next_placeholder++;
1224 $arguments[$placeholder] = $value;
1225 $placeholders[] = $placeholder;
1226 }
1227 }
1228 $condition_fragments[] = ' (' . $condition['field'] . ' ' . $operator['operator'] . ' ' . $operator['prefix'] . implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ') ';
1229
1230 }
1231 }
1232 }
1233
1234 $this->changed = FALSE;
1235 $this->stringVersion = implode($conjunction, $condition_fragments);
1236 $this->arguments = $arguments;
1237 }
1238 }
1239
1240 public function __toString() {
1241 // If the caller forgot to call compile() first, refuse to run.
1242 if ($this->changed) {
1243 return NULL;
1244 }
1245 return $this->stringVersion;
1246 }
1247
1248 /**
1249 * Gets any special processing requirements for the condition operator.
1250 *
1251 * Some condition types require special processing, such as IN, because
1252 * the value data they pass in is not a simple value. This is a simple
1253 * overridable lookup function.
1254 *
1255 * @param $operator
1256 * The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive.
1257 * @return
1258 * The extra handling directives for the specified operator, or NULL.
1259 */
1260 protected function mapConditionOperator($operator) {
1261 // $specials does not use drupal_static as its value never changes.
1262 static $specials = array(
1263 'BETWEEN' => array('delimiter' => ' AND '),
1264 'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1265 'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1266 'LIKE' => array('operator' => 'LIKE'),
1267 'IS NULL' => array('use_value' => FALSE),
1268 'IS NOT NULL' => array('use_value' => FALSE),
1269 );
1270
1271 $return = isset($specials[$operator]) ? $specials[$operator] : array();
1272 $return += array('operator' => $operator);
1273
1274 return $return;
1275 }
1276
1277 }
1278
1279 /**
1280 * Returns a new DatabaseCondition, set to "OR" all conditions together.
1281 */
1282 function db_or() {
1283 return new DatabaseCondition('OR');
1284 }
1285
1286 /**
1287 * Returns a new DatabaseCondition, set to "AND" all conditions together.
1288 */
1289 function db_and() {
1290 return new DatabaseCondition('AND');
1291 }
1292
1293 /**
1294 * Returns a new DatabaseCondition, set to "XOR" all conditions together.
1295 */
1296 function db_xor() {
1297 return new DatabaseCondition('XOR');
1298 }
1299
1300 /**
1301 * Returns a new DatabaseCondition, set to the specified conjunction.
1302 *
1303 * @param
1304 * The conjunction (AND, OR, XOR, etc.) to use on conditions.
1305 */
1306 function db_condition($conjunction) {
1307 return new DatabaseCondition($conjunction);
1308 }
1309
1310 /**
1311 * @} End of "ingroup database".
1312 */
1313

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.