· 7 years ago · Dec 13, 2018, 07:00 AM
1<?php
2
3/**
4 * @ingroup 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 */
16interface 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 */
166interface 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 */
253interface 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 */
275abstract 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 */
441class 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
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 // If no values have been added, silently ignore this query. This can happen
720 // if values are added conditionally, so we don't want to throw an
721 // exception.
722 if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
723 return FALSE;
724 }
725 return TRUE;
726 }
727}
728
729/**
730 * General class for an abstracted DELETE operation.
731 */
732class DeleteQuery extends Query implements QueryConditionInterface {
733
734 /**
735 * The table from which to delete.
736 *
737 * @var string
738 */
739 protected $table;
740
741 /**
742 * The condition object for this query.
743 *
744 * Condition handling is handled via composition.
745 *
746 * @var DatabaseCondition
747 */
748 protected $condition;
749
750 /**
751 * Constructs a DeleteQuery object.
752 *
753 * @param DatabaseConnection $connection
754 * A DatabaseConnection object.
755 * @param string $table
756 * Name of the table to associate with this query.
757 * @param array $options
758 * Array of database options.
759 */
760 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
761 $options['return'] = Database::RETURN_AFFECTED;
762 parent::__construct($connection, $options);
763 $this->table = $table;
764
765 $this->condition = new DatabaseCondition('AND');
766 }
767
768 /**
769 * Implements QueryConditionInterface::condition().
770 */
771 public function condition($field, $value = NULL, $operator = NULL) {
772 $this->condition->condition($field, $value, $operator);
773 return $this;
774 }
775
776 /**
777 * Implements QueryConditionInterface::isNull().
778 */
779 public function isNull($field) {
780 $this->condition->isNull($field);
781 return $this;
782 }
783
784 /**
785 * Implements QueryConditionInterface::isNotNull().
786 */
787 public function isNotNull($field) {
788 $this->condition->isNotNull($field);
789 return $this;
790 }
791
792 /**
793 * Implements QueryConditionInterface::exists().
794 */
795 public function exists(SelectQueryInterface $select) {
796 $this->condition->exists($select);
797 return $this;
798 }
799
800 /**
801 * Implements QueryConditionInterface::notExists().
802 */
803 public function notExists(SelectQueryInterface $select) {
804 $this->condition->notExists($select);
805 return $this;
806 }
807
808 /**
809 * Implements QueryConditionInterface::conditions().
810 */
811 public function &conditions() {
812 return $this->condition->conditions();
813 }
814
815 /**
816 * Implements QueryConditionInterface::arguments().
817 */
818 public function arguments() {
819 return $this->condition->arguments();
820 }
821
822 /**
823 * Implements QueryConditionInterface::where().
824 */
825 public function where($snippet, $args = array()) {
826 $this->condition->where($snippet, $args);
827 return $this;
828 }
829
830 /**
831 * Implements QueryConditionInterface::compile().
832 */
833 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
834 return $this->condition->compile($connection, $queryPlaceholder);
835 }
836
837 /**
838 * Implements QueryConditionInterface::compiled().
839 */
840 public function compiled() {
841 return $this->condition->compiled();
842 }
843
844 /**
845 * Executes the DELETE query.
846 *
847 * @return
848 * The return value is dependent on the database connection.
849 */
850 public function execute() {
851 $values = array();
852 if (count($this->condition)) {
853 $this->condition->compile($this->connection, $this);
854 $values = $this->condition->arguments();
855 }
856
857 return $this->connection->query((string) $this, $values, $this->queryOptions);
858 }
859
860 /**
861 * Implements PHP magic __toString method to convert the query to a string.
862 *
863 * @return string
864 * The prepared statement.
865 */
866 public function __toString() {
867 // Create a sanitized comment string to prepend to the query.
868 $comments = $this->connection->makeComment($this->comments);
869
870 $query = $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} ';
871
872 if (count($this->condition)) {
873
874 $this->condition->compile($this->connection, $this);
875 $query .= "\nWHERE " . $this->condition;
876 }
877
878 return $query;
879 }
880}
881
882
883/**
884 * General class for an abstracted TRUNCATE operation.
885 */
886class TruncateQuery extends Query {
887
888 /**
889 * The table to truncate.
890 *
891 * @var string
892 */
893 protected $table;
894
895 /**
896 * Constructs a TruncateQuery object.
897 *
898 * @param DatabaseConnection $connection
899 * A DatabaseConnection object.
900 * @param string $table
901 * Name of the table to associate with this query.
902 * @param array $options
903 * Array of database options.
904 */
905 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
906 $options['return'] = Database::RETURN_AFFECTED;
907 parent::__construct($connection, $options);
908 $this->table = $table;
909 }
910
911 /**
912 * Implements QueryConditionInterface::compile().
913 */
914 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
915 return $this->condition->compile($connection, $queryPlaceholder);
916 }
917
918 /**
919 * Implements QueryConditionInterface::compiled().
920 */
921 public function compiled() {
922 return $this->condition->compiled();
923 }
924
925 /**
926 * Executes the TRUNCATE query.
927 *
928 * @return
929 * Return value is dependent on the database type.
930 */
931 public function execute() {
932 return $this->connection->query((string) $this, array(), $this->queryOptions);
933 }
934
935 /**
936 * Implements PHP magic __toString method to convert the query to a string.
937 *
938 * @return string
939 * The prepared statement.
940 */
941 public function __toString() {
942 // Create a sanitized comment string to prepend to the query.
943 $comments = $this->connection->makeComment($this->comments);
944
945 return $comments . 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} ';
946 }
947}
948
949/**
950 * General class for an abstracted UPDATE operation.
951 */
952class UpdateQuery extends Query implements QueryConditionInterface {
953
954 /**
955 * The table to update.
956 *
957 * @var string
958 */
959 protected $table;
960
961 /**
962 * An array of fields that will be updated.
963 *
964 * @var array
965 */
966 protected $fields = array();
967
968 /**
969 * An array of values to update to.
970 *
971 * @var array
972 */
973 protected $arguments = array();
974
975 /**
976 * The condition object for this query.
977 *
978 * Condition handling is handled via composition.
979 *
980 * @var DatabaseCondition
981 */
982 protected $condition;
983
984 /**
985 * Array of fields to update to an expression in case of a duplicate record.
986 *
987 * This variable is a nested array in the following format:
988 * @code
989 * <some field> => array(
990 * 'condition' => <condition to execute, as a string>,
991 * 'arguments' => <array of arguments for condition, or NULL for none>,
992 * );
993 * @endcode
994 *
995 * @var array
996 */
997 protected $expressionFields = array();
998
999 /**
1000 * Constructs an UpdateQuery object.
1001 *
1002 * @param DatabaseConnection $connection
1003 * A DatabaseConnection object.
1004 * @param string $table
1005 * Name of the table to associate with this query.
1006 * @param array $options
1007 * Array of database options.
1008 */
1009 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
1010 $options['return'] = Database::RETURN_AFFECTED;
1011 parent::__construct($connection, $options);
1012 $this->table = $table;
1013
1014 $this->condition = new DatabaseCondition('AND');
1015 }
1016
1017 /**
1018 * Implements QueryConditionInterface::condition().
1019 */
1020 public function condition($field, $value = NULL, $operator = NULL) {
1021 $this->condition->condition($field, $value, $operator);
1022 return $this;
1023 }
1024
1025 /**
1026 * Implements QueryConditionInterface::isNull().
1027 */
1028 public function isNull($field) {
1029 $this->condition->isNull($field);
1030 return $this;
1031 }
1032
1033 /**
1034 * Implements QueryConditionInterface::isNotNull().
1035 */
1036 public function isNotNull($field) {
1037 $this->condition->isNotNull($field);
1038 return $this;
1039 }
1040
1041 /**
1042 * Implements QueryConditionInterface::exists().
1043 */
1044 public function exists(SelectQueryInterface $select) {
1045 $this->condition->exists($select);
1046 return $this;
1047 }
1048
1049 /**
1050 * Implements QueryConditionInterface::notExists().
1051 */
1052 public function notExists(SelectQueryInterface $select) {
1053 $this->condition->notExists($select);
1054 return $this;
1055 }
1056
1057 /**
1058 * Implements QueryConditionInterface::conditions().
1059 */
1060 public function &conditions() {
1061 return $this->condition->conditions();
1062 }
1063
1064 /**
1065 * Implements QueryConditionInterface::arguments().
1066 */
1067 public function arguments() {
1068 return $this->condition->arguments();
1069 }
1070
1071 /**
1072 * Implements QueryConditionInterface::where().
1073 */
1074 public function where($snippet, $args = array()) {
1075 $this->condition->where($snippet, $args);
1076 return $this;
1077 }
1078
1079 /**
1080 * Implements QueryConditionInterface::compile().
1081 */
1082 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1083 return $this->condition->compile($connection, $queryPlaceholder);
1084 }
1085
1086 /**
1087 * Implements QueryConditionInterface::compiled().
1088 */
1089 public function compiled() {
1090 return $this->condition->compiled();
1091 }
1092
1093 /**
1094 * Adds a set of field->value pairs to be updated.
1095 *
1096 * @param $fields
1097 * An associative array of fields to write into the database. The array keys
1098 * are the field names and the values are the values to which to set them.
1099 *
1100 * @return UpdateQuery
1101 * The called object.
1102 */
1103 public function fields(array $fields) {
1104 $this->fields = $fields;
1105 return $this;
1106 }
1107
1108 /**
1109 * Specifies fields to be updated as an expression.
1110 *
1111 * Expression fields are cases such as counter=counter+1. This method takes
1112 * precedence over fields().
1113 *
1114 * @param $field
1115 * The field to set.
1116 * @param $expression
1117 * The field will be set to the value of this expression. This parameter
1118 * may include named placeholders.
1119 * @param $arguments
1120 * If specified, this is an array of key/value pairs for named placeholders
1121 * corresponding to the expression.
1122 *
1123 * @return UpdateQuery
1124 * The called object.
1125 */
1126 public function expression($field, $expression, array $arguments = NULL) {
1127 $this->expressionFields[$field] = array(
1128 'expression' => $expression,
1129 'arguments' => $arguments,
1130 );
1131
1132 return $this;
1133 }
1134
1135 /**
1136 * Executes the UPDATE query.
1137 *
1138 * @return
1139 * The number of rows affected by the update.
1140 */
1141 public function execute() {
1142
1143 // Expressions take priority over literal fields, so we process those first
1144 // and remove any literal fields that conflict.
1145 $fields = $this->fields;
1146 $update_values = array();
1147 foreach ($this->expressionFields as $field => $data) {
1148 if (!empty($data['arguments'])) {
1149 $update_values += $data['arguments'];
1150 }
1151 unset($fields[$field]);
1152 }
1153
1154 // Because we filter $fields the same way here and in __toString(), the
1155 // placeholders will all match up properly.
1156 $max_placeholder = 0;
1157 foreach ($fields as $field => $value) {
1158 $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value;
1159 }
1160
1161 if (count($this->condition)) {
1162 $this->condition->compile($this->connection, $this);
1163 $update_values = array_merge($update_values, $this->condition->arguments());
1164 }
1165
1166 return $this->connection->query((string) $this, $update_values, $this->queryOptions);
1167 }
1168
1169 /**
1170 * Implements PHP magic __toString method to convert the query to a string.
1171 *
1172 * @return string
1173 * The prepared statement.
1174 */
1175 public function __toString() {
1176 // Create a sanitized comment string to prepend to the query.
1177 $comments = $this->connection->makeComment($this->comments);
1178
1179 // Expressions take priority over literal fields, so we process those first
1180 // and remove any literal fields that conflict.
1181 $fields = $this->fields;
1182 $update_fields = array();
1183 foreach ($this->expressionFields as $field => $data) {
1184 $update_fields[] = $field . '=' . $data['expression'];
1185 unset($fields[$field]);
1186 }
1187
1188 $max_placeholder = 0;
1189 foreach ($fields as $field => $value) {
1190 $update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++);
1191 }
1192
1193 $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
1194
1195 if (count($this->condition)) {
1196 $this->condition->compile($this->connection, $this);
1197 // There is an implicit string cast on $this->condition.
1198 $query .= "\nWHERE " . $this->condition;
1199 }
1200
1201 return $query;
1202 }
1203
1204}
1205
1206/**
1207 * General class for an abstracted MERGE query operation.
1208 *
1209 * An ANSI SQL:2003 compatible database would run the following query:
1210 *
1211 * @code
1212 * MERGE INTO table_name_1 USING table_name_2 ON (condition)
1213 * WHEN MATCHED THEN
1214 * UPDATE SET column1 = value1 [, column2 = value2 ...]
1215 * WHEN NOT MATCHED THEN
1216 * INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...
1217 * @endcode
1218 *
1219 * Other databases (most notably MySQL, PostgreSQL and SQLite) will emulate
1220 * this statement by running a SELECT and then INSERT or UPDATE.
1221 *
1222 * By default, the two table names are identical and they are passed into the
1223 * the constructor. table_name_2 can be specified by the
1224 * MergeQuery::conditionTable() method. It can be either a string or a
1225 * subquery.
1226 *
1227 * The condition is built exactly like SelectQuery or UpdateQuery conditions,
1228 * the UPDATE query part is built similarly like an UpdateQuery and finally the
1229 * INSERT query part is built similarly like an InsertQuery. However, both
1230 * UpdateQuery and InsertQuery has a fields method so
1231 * MergeQuery::updateFields() and MergeQuery::insertFields() needs to be called
1232 * instead. MergeQuery::fields() can also be called which calls both of these
1233 * methods as the common case is to use the same column-value pairs for both
1234 * INSERT and UPDATE. However, this is not mandatory. Another convinient
1235 * wrapper is MergeQuery::key() which adds the same column-value pairs to the
1236 * condition and the INSERT query part.
1237 *
1238 * Several methods (key(), fields(), insertFields()) can be called to set a
1239 * key-value pair for the INSERT query part. Subsequent calls for the same
1240 * fields override the earlier ones. The same is true for UPDATE and key(),
1241 * fields() and updateFields().
1242 */
1243class MergeQuery extends Query implements QueryConditionInterface {
1244 /**
1245 * Returned by execute() if an INSERT query has been executed.
1246 */
1247 const STATUS_INSERT = 1;
1248
1249 /**
1250 * Returned by execute() if an UPDATE query has been executed.
1251 */
1252 const STATUS_UPDATE = 2;
1253
1254 /**
1255 * The table to be used for INSERT and UPDATE.
1256 *
1257 * @var string
1258 */
1259 protected $table;
1260
1261 /**
1262 * The table or subquery to be used for the condition.
1263 */
1264 protected $conditionTable;
1265
1266 /**
1267 * An array of fields on which to insert.
1268 *
1269 * @var array
1270 */
1271 protected $insertFields = array();
1272
1273 /**
1274 * An array of fields which should be set to their database-defined defaults.
1275 *
1276 * Used on INSERT.
1277 *
1278 * @var array
1279 */
1280 protected $defaultFields = array();
1281
1282 /**
1283 * An array of values to be inserted.
1284 *
1285 * @var string
1286 */
1287 protected $insertValues = array();
1288
1289 /**
1290 * An array of fields that will be updated.
1291 *
1292 * @var array
1293 */
1294 protected $updateFields = array();
1295
1296 /**
1297 * Array of fields to update to an expression in case of a duplicate record.
1298 *
1299 * This variable is a nested array in the following format:
1300 * @code
1301 * <some field> => array(
1302 * 'condition' => <condition to execute, as a string>,
1303 * 'arguments' => <array of arguments for condition, or NULL for none>,
1304 * );
1305 * @endcode
1306 *
1307 * @var array
1308 */
1309 protected $expressionFields = array();
1310
1311 /**
1312 * Flag indicating whether an UPDATE is necessary.
1313 *
1314 * @var boolean
1315 */
1316 protected $needsUpdate = FALSE;
1317
1318 /**
1319 * Constructs a MergeQuery object.
1320 *
1321 * @param DatabaseConnection $connection
1322 * A DatabaseConnection object.
1323 * @param string $table
1324 * Name of the table to associate with this query.
1325 * @param array $options
1326 * Array of database options.
1327 */
1328 public function __construct(DatabaseConnection $connection, $table, array $options = array()) {
1329 $options['return'] = Database::RETURN_AFFECTED;
1330 parent::__construct($connection, $options);
1331 $this->table = $table;
1332 $this->conditionTable = $table;
1333 $this->condition = new DatabaseCondition('AND');
1334 }
1335
1336 /**
1337 * Sets the table or subquery to be used for the condition.
1338 *
1339 * @param $table
1340 * The table name or the subquery to be used. Use a SelectQuery object to
1341 * pass in a subquery.
1342 *
1343 * @return MergeQuery
1344 * The called object.
1345 */
1346 protected function conditionTable($table) {
1347 $this->conditionTable = $table;
1348 return $this;
1349 }
1350
1351 /**
1352 * Adds a set of field->value pairs to be updated.
1353 *
1354 * @param $fields
1355 * An associative array of fields to write into the database. The array keys
1356 * are the field names and the values are the values to which to set them.
1357 *
1358 * @return MergeQuery
1359 * The called object.
1360 */
1361 public function updateFields(array $fields) {
1362 $this->updateFields = $fields;
1363 $this->needsUpdate = TRUE;
1364 return $this;
1365 }
1366
1367 /**
1368 * Specifies fields to be updated as an expression.
1369 *
1370 * Expression fields are cases such as counter = counter + 1. This method
1371 * takes precedence over MergeQuery::updateFields() and it's wrappers,
1372 * MergeQuery::key() and MergeQuery::fields().
1373 *
1374 * @param $field
1375 * The field to set.
1376 * @param $expression
1377 * The field will be set to the value of this expression. This parameter
1378 * may include named placeholders.
1379 * @param $arguments
1380 * If specified, this is an array of key/value pairs for named placeholders
1381 * corresponding to the expression.
1382 *
1383 * @return MergeQuery
1384 * The called object.
1385 */
1386 public function expression($field, $expression, array $arguments = NULL) {
1387 $this->expressionFields[$field] = array(
1388 'expression' => $expression,
1389 'arguments' => $arguments,
1390 );
1391 $this->needsUpdate = TRUE;
1392 return $this;
1393 }
1394
1395 /**
1396 * Adds a set of field->value pairs to be inserted.
1397 *
1398 * @param $fields
1399 * An array of fields on which to insert. This array may be indexed or
1400 * associative. If indexed, the array is taken to be the list of fields.
1401 * If associative, the keys of the array are taken to be the fields and
1402 * the values are taken to be corresponding values to insert. If a
1403 * $values argument is provided, $fields must be indexed.
1404 * @param $values
1405 * An array of fields to insert into the database. The values must be
1406 * specified in the same order as the $fields array.
1407 *
1408 * @return MergeQuery
1409 * The called object.
1410 */
1411 public function insertFields(array $fields, array $values = array()) {
1412 if ($values) {
1413 $fields = array_combine($fields, $values);
1414 }
1415 $this->insertFields = $fields;
1416 return $this;
1417 }
1418
1419 /**
1420 * Specifies fields for which the database-defaults should be used.
1421 *
1422 * If you want to force a given field to use the database-defined default,
1423 * not NULL or undefined, use this method to instruct the database to use
1424 * default values explicitly. In most cases this will not be necessary
1425 * unless you are inserting a row that is all default values, as you cannot
1426 * specify no values in an INSERT query.
1427 *
1428 * Specifying a field both in fields() and in useDefaults() is an error
1429 * and will not execute.
1430 *
1431 * @param $fields
1432 * An array of values for which to use the default values
1433 * specified in the table definition.
1434 *
1435 * @return MergeQuery
1436 * The called object.
1437 */
1438 public function useDefaults(array $fields) {
1439 $this->defaultFields = $fields;
1440 return $this;
1441 }
1442
1443 /**
1444 * Sets common field-value pairs in the INSERT and UPDATE query parts.
1445 *
1446 * This method should only be called once. It may be called either
1447 * with a single associative array or two indexed arrays. If called
1448 * with an associative array, the keys are taken to be the fields
1449 * and the values are taken to be the corresponding values to set.
1450 * If called with two arrays, the first array is taken as the fields
1451 * and the second array is taken as the corresponding values.
1452 *
1453 * @param $fields
1454 * An array of fields to insert, or an associative array of fields and
1455 * values. The keys of the array are taken to be the fields and the values
1456 * are taken to be corresponding values to insert.
1457 * @param $values
1458 * An array of values to set into the database. The values must be
1459 * specified in the same order as the $fields array.
1460 *
1461 * @return MergeQuery
1462 * The called object.
1463 */
1464 public function fields(array $fields, array $values = array()) {
1465 if ($values) {
1466 $fields = array_combine($fields, $values);
1467 }
1468 foreach ($fields as $key => $value) {
1469 $this->insertFields[$key] = $value;
1470 $this->updateFields[$key] = $value;
1471 }
1472 $this->needsUpdate = TRUE;
1473 return $this;
1474 }
1475
1476 /**
1477 * Sets the key field(s) to be used as conditions for this query.
1478 *
1479 * This method should only be called once. It may be called either
1480 * with a single associative array or two indexed arrays. If called
1481 * with an associative array, the keys are taken to be the fields
1482 * and the values are taken to be the corresponding values to set.
1483 * If called with two arrays, the first array is taken as the fields
1484 * and the second array is taken as the corresponding values.
1485 *
1486 * The fields are copied to the condition of the query and the INSERT part.
1487 * If no other method is called, the UPDATE will become a no-op.
1488 *
1489 * @param $fields
1490 * An array of fields to set, or an associative array of fields and values.
1491 * @param $values
1492 * An array of values to set into the database. The values must be
1493 * specified in the same order as the $fields array.
1494 *
1495 * @return MergeQuery
1496 * The called object.
1497 */
1498 public function key(array $fields, array $values = array()) {
1499 if ($values) {
1500 $fields = array_combine($fields, $values);
1501 }
1502 foreach ($fields as $key => $value) {
1503 $this->insertFields[$key] = $value;
1504 $this->condition($key, $value);
1505 }
1506 return $this;
1507 }
1508
1509 /**
1510 * Implements QueryConditionInterface::condition().
1511 */
1512 public function condition($field, $value = NULL, $operator = NULL) {
1513 $this->condition->condition($field, $value, $operator);
1514 return $this;
1515 }
1516
1517 /**
1518 * Implements QueryConditionInterface::isNull().
1519 */
1520 public function isNull($field) {
1521 $this->condition->isNull($field);
1522 return $this;
1523 }
1524
1525 /**
1526 * Implements QueryConditionInterface::isNotNull().
1527 */
1528 public function isNotNull($field) {
1529 $this->condition->isNotNull($field);
1530 return $this;
1531 }
1532
1533 /**
1534 * Implements QueryConditionInterface::exists().
1535 */
1536 public function exists(SelectQueryInterface $select) {
1537 $this->condition->exists($select);
1538 return $this;
1539 }
1540
1541 /**
1542 * Implements QueryConditionInterface::notExists().
1543 */
1544 public function notExists(SelectQueryInterface $select) {
1545 $this->condition->notExists($select);
1546 return $this;
1547 }
1548
1549 /**
1550 * Implements QueryConditionInterface::conditions().
1551 */
1552 public function &conditions() {
1553 return $this->condition->conditions();
1554 }
1555
1556 /**
1557 * Implements QueryConditionInterface::arguments().
1558 */
1559 public function arguments() {
1560 return $this->condition->arguments();
1561 }
1562
1563 /**
1564 * Implements QueryConditionInterface::where().
1565 */
1566 public function where($snippet, $args = array()) {
1567 $this->condition->where($snippet, $args);
1568 return $this;
1569 }
1570
1571 /**
1572 * Implements QueryConditionInterface::compile().
1573 */
1574 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1575 return $this->condition->compile($connection, $queryPlaceholder);
1576 }
1577
1578 /**
1579 * Implements QueryConditionInterface::compiled().
1580 */
1581 public function compiled() {
1582 return $this->condition->compiled();
1583 }
1584
1585 /**
1586 * Implements PHP magic __toString method to convert the query to a string.
1587 *
1588 * In the degenerate case, there is no string-able query as this operation
1589 * is potentially two queries.
1590 *
1591 * @return string
1592 * The prepared query statement.
1593 */
1594 public function __toString() {
1595 }
1596
1597 public function execute() {
1598 // Wrap multiple queries in a transaction, if the database supports it.
1599 $transaction = $this->connection->startTransaction();
1600 try {
1601 if (!count($this->condition)) {
1602 throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
1603 }
1604 $select = $this->connection->select($this->conditionTable)
1605 ->condition($this->condition)
1606 ->forUpdate();
1607 $select->addExpression('1');
1608 if (!$select->execute()->fetchField()) {
1609 try {
1610 $insert = $this->connection->insert($this->table)->fields($this->insertFields);
1611 if ($this->defaultFields) {
1612 $insert->useDefaults($this->defaultFields);
1613 }
1614 $insert->execute();
1615 return MergeQuery::STATUS_INSERT;
1616 }
1617 catch (Exception $e) {
1618 // The insert query failed, maybe it's because a racing insert query
1619 // beat us in inserting the same row. Retry the select query, if it
1620 // returns a row, ignore the error and continue with the update
1621 // query below.
1622 if (!$select->execute()->fetchField()) {
1623 throw $e;
1624 }
1625 }
1626 }
1627 if ($this->needsUpdate) {
1628 $update = $this->connection->update($this->table)
1629 ->fields($this->updateFields)
1630 ->condition($this->condition);
1631 if ($this->expressionFields) {
1632 foreach ($this->expressionFields as $field => $data) {
1633 $update->expression($field, $data['expression'], $data['arguments']);
1634 }
1635 }
1636 $update->execute();
1637 return MergeQuery::STATUS_UPDATE;
1638 }
1639 }
1640 catch (Exception $e) {
1641 // Something really wrong happened here, bubble up the exception to the
1642 // caller.
1643 $transaction->rollback();
1644 throw $e;
1645 }
1646 // Transaction commits here where $transaction looses scope.
1647 }
1648}
1649
1650/**
1651 * Generic class for a series of conditions in a query.
1652 */
1653class DatabaseCondition implements QueryConditionInterface, Countable {
1654
1655 /**
1656 * Array of conditions.
1657 *
1658 * @var array
1659 */
1660 protected $conditions = array();
1661
1662 /**
1663 * Array of arguments.
1664 *
1665 * @var array
1666 */
1667 protected $arguments = array();
1668
1669 /**
1670 * Whether the conditions have been changed.
1671 *
1672 * TRUE if the condition has been changed since the last compile.
1673 * FALSE if the condition has been compiled and not changed.
1674 *
1675 * @var bool
1676 */
1677 protected $changed = TRUE;
1678
1679 /**
1680 * The identifier of the query placeholder this condition has been compiled against.
1681 */
1682 protected $queryPlaceholderIdentifier;
1683
1684 /**
1685 * Constructs a DataBaseCondition object.
1686 *
1687 * @param string $conjunction
1688 * The operator to use to combine conditions: 'AND' or 'OR'.
1689 */
1690 public function __construct($conjunction) {
1691 $this->conditions['#conjunction'] = $conjunction;
1692 }
1693
1694 /**
1695 * Implements Countable::count().
1696 *
1697 * Returns the size of this conditional. The size of the conditional is the
1698 * size of its conditional array minus one, because one element is the the
1699 * conjunction.
1700 */
1701 public function count() {
1702 return count($this->conditions) - 1;
1703 }
1704
1705 /**
1706 * Implements QueryConditionInterface::condition().
1707 */
1708 public function condition($field, $value = NULL, $operator = NULL) {
1709 if (!isset($operator)) {
1710 if (is_array($value)) {
1711 $operator = 'IN';
1712 }
1713 elseif (!isset($value)) {
1714 $operator = 'IS NULL';
1715 }
1716 else {
1717 $operator = '=';
1718 }
1719 }
1720 $this->conditions[] = array(
1721 'field' => $field,
1722 'value' => $value,
1723 'operator' => $operator,
1724 );
1725
1726 $this->changed = TRUE;
1727
1728 return $this;
1729 }
1730
1731 /**
1732 * Implements QueryConditionInterface::where().
1733 */
1734 public function where($snippet, $args = array()) {
1735 $this->conditions[] = array(
1736 'field' => $snippet,
1737 'value' => $args,
1738 'operator' => NULL,
1739 );
1740 $this->changed = TRUE;
1741
1742 return $this;
1743 }
1744
1745 /**
1746 * Implements QueryConditionInterface::isNull().
1747 */
1748 public function isNull($field) {
1749 return $this->condition($field);
1750 }
1751
1752 /**
1753 * Implements QueryConditionInterface::isNotNull().
1754 */
1755 public function isNotNull($field) {
1756 return $this->condition($field, NULL, 'IS NOT NULL');
1757 }
1758
1759 /**
1760 * Implements QueryConditionInterface::exists().
1761 */
1762 public function exists(SelectQueryInterface $select) {
1763 return $this->condition('', $select, 'EXISTS');
1764 }
1765
1766 /**
1767 * Implements QueryConditionInterface::notExists().
1768 */
1769 public function notExists(SelectQueryInterface $select) {
1770 return $this->condition('', $select, 'NOT EXISTS');
1771 }
1772
1773 /**
1774 * Implements QueryConditionInterface::conditions().
1775 */
1776 public function &conditions() {
1777 return $this->conditions;
1778 }
1779
1780 /**
1781 * Implements QueryConditionInterface::arguments().
1782 */
1783 public function arguments() {
1784 // If the caller forgot to call compile() first, refuse to run.
1785 if ($this->changed) {
1786 return NULL;
1787 }
1788 return $this->arguments;
1789 }
1790
1791 /**
1792 * Implements QueryConditionInterface::compile().
1793 */
1794 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
1795 // Re-compile if this condition changed or if we are compiled against a
1796 // different query placeholder object.
1797 if ($this->changed || isset($this->queryPlaceholderIdentifier) && ($this->queryPlaceholderIdentifier != $queryPlaceholder->uniqueIdentifier())) {
1798 $this->queryPlaceholderIdentifier = $queryPlaceholder->uniqueIdentifier();
1799
1800 $condition_fragments = array();
1801 $arguments = array();
1802
1803 $conditions = $this->conditions;
1804 $conjunction = $conditions['#conjunction'];
1805 unset($conditions['#conjunction']);
1806 foreach ($conditions as $condition) {
1807 if (empty($condition['operator'])) {
1808 // This condition is a literal string, so let it through as is.
1809 $condition_fragments[] = ' (' . $condition['field'] . ') ';
1810 $arguments += $condition['value'];
1811 }
1812 else {
1813 // It's a structured condition, so parse it out accordingly.
1814 // Note that $condition['field'] will only be an object for a dependent
1815 // DatabaseCondition object, not for a dependent subquery.
1816 if ($condition['field'] instanceof QueryConditionInterface) {
1817 // Compile the sub-condition recursively and add it to the list.
1818 $condition['field']->compile($connection, $queryPlaceholder);
1819 $condition_fragments[] = '(' . (string) $condition['field'] . ')';
1820 $arguments += $condition['field']->arguments();
1821 }
1822 else {
1823 // For simplicity, we treat all operators as the same data structure.
1824 // In the typical degenerate case, this won't get changed.
1825 $operator_defaults = array(
1826 'prefix' => '',
1827 'postfix' => '',
1828 'delimiter' => '',
1829 'operator' => $condition['operator'],
1830 'use_value' => TRUE,
1831 );
1832 $operator = $connection->mapConditionOperator($condition['operator']);
1833 if (!isset($operator)) {
1834 $operator = $this->mapConditionOperator($condition['operator']);
1835 }
1836 $operator += $operator_defaults;
1837
1838 $placeholders = array();
1839 if ($condition['value'] instanceof SelectQueryInterface) {
1840 $condition['value']->compile($connection, $queryPlaceholder);
1841 $placeholders[] = (string) $condition['value'];
1842 $arguments += $condition['value']->arguments();
1843 // Subqueries are the actual value of the operator, we don't
1844 // need to add another below.
1845 $operator['use_value'] = FALSE;
1846 }
1847 // We assume that if there is a delimiter, then the value is an
1848 // array. If not, it is a scalar. For simplicity, we first convert
1849 // up to an array so that we can build the placeholders in the same way.
1850 elseif (!$operator['delimiter']) {
1851 $condition['value'] = array($condition['value']);
1852 }
1853 if ($operator['use_value']) {
1854 foreach ($condition['value'] as $value) {
1855 $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
1856 $arguments[$placeholder] = $value;
1857 $placeholders[] = $placeholder;
1858 }
1859 }
1860 $condition_fragments[] = ' (' . $connection->escapeField($condition['field']) . ' ' . $operator['operator'] . ' ' . $operator['prefix'] . implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ') ';
1861 }
1862 }
1863 }
1864
1865 $this->changed = FALSE;
1866 $this->stringVersion = implode($conjunction, $condition_fragments);
1867 $this->arguments = $arguments;
1868 }
1869 }
1870
1871 /**
1872 * Implements QueryConditionInterface::compiled().
1873 */
1874 public function compiled() {
1875 return !$this->changed;
1876 }
1877
1878 /**
1879 * Implements PHP magic __toString method to convert the conditions to string.
1880 *
1881 * @return string
1882 * A string version of the conditions.
1883 */
1884 public function __toString() {
1885 // If the caller forgot to call compile() first, refuse to run.
1886 if ($this->changed) {
1887 return NULL;
1888 }
1889 return $this->stringVersion;
1890 }
1891
1892 /**
1893 * PHP magic __clone() method.
1894 *
1895 * Only copies fields that implement QueryConditionInterface. Also sets
1896 * $this->changed to TRUE.
1897 */
1898 function __clone() {
1899 $this->changed = TRUE;
1900 foreach ($this->conditions as $key => $condition) {
1901 if ($condition['field'] instanceOf QueryConditionInterface) {
1902 $this->conditions[$key]['field'] = clone($condition['field']);
1903 }
1904 }
1905 }
1906
1907 /**
1908 * Gets any special processing requirements for the condition operator.
1909 *
1910 * Some condition types require special processing, such as IN, because
1911 * the value data they pass in is not a simple value. This is a simple
1912 * overridable lookup function.
1913 *
1914 * @param $operator
1915 * The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive.
1916 *
1917 * @return
1918 * The extra handling directives for the specified operator, or NULL.
1919 */
1920 protected function mapConditionOperator($operator) {
1921 // $specials does not use drupal_static as its value never changes.
1922 static $specials = array(
1923 'BETWEEN' => array('delimiter' => ' AND '),
1924 'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1925 'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
1926 'EXISTS' => array('prefix' => ' (', 'postfix' => ')'),
1927 'NOT EXISTS' => array('prefix' => ' (', 'postfix' => ')'),
1928 'IS NULL' => array('use_value' => FALSE),
1929 'IS NOT NULL' => array('use_value' => FALSE),
1930 // Use backslash for escaping wildcard characters.
1931 'LIKE' => array('postfix' => " ESCAPE '\\\\'"),
1932 'NOT LIKE' => array('postfix' => " ESCAPE '\\\\'"),
1933 // These ones are here for performance reasons.
1934 '=' => array(),
1935 '<' => array(),
1936 '>' => array(),
1937 '>=' => array(),
1938 '<=' => array(),
1939 );
1940 if (isset($specials[$operator])) {
1941 $return = $specials[$operator];
1942 }
1943 else {
1944 // We need to upper case because PHP index matches are case sensitive but
1945 // do not need the more expensive drupal_strtoupper because SQL statements are ASCII.
1946 $operator = strtoupper($operator);
1947 $return = isset($specials[$operator]) ? $specials[$operator] : array();
1948 }
1949
1950 $return += array('operator' => $operator);
1951
1952 return $return;
1953 }
1954
1955}
1956
1957/**
1958 * @} End of "ingroup database".
1959 */