|
1 <?php |
|
2 |
|
3 /** |
|
4 * @addtogroup database |
|
5 * @{ |
|
6 */ |
|
7 |
|
8 /** |
|
9 * @file |
|
10 * Non-specific Database query code. Used by all engines. |
|
11 */ |
|
12 |
|
13 /** |
|
14 * Interface for a conditional clause in a query. |
|
15 */ |
|
16 interface QueryConditionInterface { |
|
17 |
|
18 /** |
|
19 * Helper function: builds the most common conditional clauses. |
|
20 * |
|
21 * This method can take a variable number of parameters. If called with two |
|
22 * parameters, they are taken as $field and $value with $operator having a |
|
23 * value of IN if $value is an array and = otherwise. |
|
24 * |
|
25 * Do not use this method to test for NULL values. Instead, use |
|
26 * QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull(). |
|
27 * |
|
28 * @param $field |
|
29 * The name of the field to check. If you would like to add a more complex |
|
30 * condition involving operators or functions, use where(). |
|
31 * @param $value |
|
32 * The value to test the field against. In most cases, this is a scalar. |
|
33 * For more complex options, it is an array. The meaning of each element in |
|
34 * the array is dependent on the $operator. |
|
35 * @param $operator |
|
36 * The comparison operator, such as =, <, or >=. It also accepts more |
|
37 * complex options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is |
|
38 * an array, and = otherwise. |
|
39 * |
|
40 * @return QueryConditionInterface |
|
41 * The called object. |
|
42 * |
|
43 * @see QueryConditionInterface::isNull() |
|
44 * @see QueryConditionInterface::isNotNull() |
|
45 */ |
|
46 public function condition($field, $value = NULL, $operator = NULL); |
|
47 |
|
48 /** |
|
49 * Adds an arbitrary WHERE clause to the query. |
|
50 * |
|
51 * @param $snippet |
|
52 * A portion of a WHERE clause as a prepared statement. It must use named |
|
53 * placeholders, not ? placeholders. |
|
54 * @param $args |
|
55 * An associative array of arguments. |
|
56 * |
|
57 * @return QueryConditionInterface |
|
58 * The called object. |
|
59 */ |
|
60 public function where($snippet, $args = array()); |
|
61 |
|
62 /** |
|
63 * Sets a condition that the specified field be NULL. |
|
64 * |
|
65 * @param $field |
|
66 * The name of the field to check. |
|
67 * |
|
68 * @return QueryConditionInterface |
|
69 * The called object. |
|
70 */ |
|
71 public function isNull($field); |
|
72 |
|
73 /** |
|
74 * Sets a condition that the specified field be NOT NULL. |
|
75 * |
|
76 * @param $field |
|
77 * The name of the field to check. |
|
78 * |
|
79 * @return QueryConditionInterface |
|
80 * The called object. |
|
81 */ |
|
82 public function isNotNull($field); |
|
83 |
|
84 /** |
|
85 * Sets a condition that the specified subquery returns values. |
|
86 * |
|
87 * @param SelectQueryInterface $select |
|
88 * The subquery that must contain results. |
|
89 * |
|
90 * @return QueryConditionInterface |
|
91 * The called object. |
|
92 */ |
|
93 public function exists(SelectQueryInterface $select); |
|
94 |
|
95 /** |
|
96 * Sets a condition that the specified subquery returns no values. |
|
97 * |
|
98 * @param SelectQueryInterface $select |
|
99 * The subquery that must not contain results. |
|
100 * |
|
101 * @return QueryConditionInterface |
|
102 * The called object. |
|
103 */ |
|
104 public function notExists(SelectQueryInterface $select); |
|
105 |
|
106 /** |
|
107 * Gets a complete list of all conditions in this conditional clause. |
|
108 * |
|
109 * This method returns by reference. That allows alter hooks to access the |
|
110 * data structure directly and manipulate it before it gets compiled. |
|
111 * |
|
112 * The data structure that is returned is an indexed array of entries, where |
|
113 * each entry looks like the following: |
|
114 * @code |
|
115 * array( |
|
116 * 'field' => $field, |
|
117 * 'value' => $value, |
|
118 * 'operator' => $operator, |
|
119 * ); |
|
120 * @endcode |
|
121 * |
|
122 * In the special case that $operator is NULL, the $field is taken as a raw |
|
123 * SQL snippet (possibly containing a function) and $value is an associative |
|
124 * array of placeholders for the snippet. |
|
125 * |
|
126 * There will also be a single array entry of #conjunction, which is the |
|
127 * conjunction that will be applied to the array, such as AND. |
|
128 */ |
|
129 public function &conditions(); |
|
130 |
|
131 /** |
|
132 * Gets a complete list of all values to insert into the prepared statement. |
|
133 * |
|
134 * @return |
|
135 * An associative array of placeholders and values. |
|
136 */ |
|
137 public function arguments(); |
|
138 |
|
139 /** |
|
140 * Compiles the saved conditions for later retrieval. |
|
141 * |
|
142 * This method does not return anything, but simply prepares data to be |
|
143 * retrieved via __toString() and arguments(). |
|
144 * |
|
145 * @param $connection |
|
146 * The database connection for which to compile the conditionals. |
|
147 * @param $queryPlaceholder |
|
148 * The query this condition belongs to. If not given, the current query is |
|
149 * used. |
|
150 */ |
|
151 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder); |
|
152 |
|
153 /** |
|
154 * Check whether a condition has been previously compiled. |
|
155 * |
|
156 * @return |
|
157 * TRUE if the condition has been previously compiled. |
|
158 */ |
|
159 public function compiled(); |
|
160 } |
|
161 |
|
162 |
|
163 /** |
|
164 * Interface for a query that can be manipulated via an alter hook. |
|
165 */ |
|
166 interface QueryAlterableInterface { |
|
167 |
|
168 /** |
|
169 * Adds a tag to a query. |
|
170 * |
|
171 * Tags are strings that identify a query. A query may have any number of |
|
172 * tags. Tags are used to mark a query so that alter hooks may decide if they |
|
173 * wish to take action. Tags should be all lower-case and contain only |
|
174 * letters, numbers, and underscore, and start with a letter. That is, they |
|
175 * should follow the same rules as PHP identifiers in general. |
|
176 * |
|
177 * @param $tag |
|
178 * The tag to add. |
|
179 * |
|
180 * @return QueryAlterableInterface |
|
181 * The called object. |
|
182 */ |
|
183 public function addTag($tag); |
|
184 |
|
185 /** |
|
186 * Determines if a given query has a given tag. |
|
187 * |
|
188 * @param $tag |
|
189 * The tag to check. |
|
190 * |
|
191 * @return |
|
192 * TRUE if this query has been marked with this tag, FALSE otherwise. |
|
193 */ |
|
194 public function hasTag($tag); |
|
195 |
|
196 /** |
|
197 * Determines if a given query has all specified tags. |
|
198 * |
|
199 * @param $tags |
|
200 * A variable number of arguments, one for each tag to check. |
|
201 * |
|
202 * @return |
|
203 * TRUE if this query has been marked with all specified tags, FALSE |
|
204 * otherwise. |
|
205 */ |
|
206 public function hasAllTags(); |
|
207 |
|
208 /** |
|
209 * Determines if a given query has any specified tag. |
|
210 * |
|
211 * @param $tags |
|
212 * A variable number of arguments, one for each tag to check. |
|
213 * |
|
214 * @return |
|
215 * TRUE if this query has been marked with at least one of the specified |
|
216 * tags, FALSE otherwise. |
|
217 */ |
|
218 public function hasAnyTag(); |
|
219 |
|
220 /** |
|
221 * Adds additional metadata to the query. |
|
222 * |
|
223 * Often, a query may need to provide additional contextual data to alter |
|
224 * hooks. Alter hooks may then use that information to decide if and how |
|
225 * to take action. |
|
226 * |
|
227 * @param $key |
|
228 * The unique identifier for this piece of metadata. Must be a string that |
|
229 * follows the same rules as any other PHP identifier. |
|
230 * @param $object |
|
231 * The additional data to add to the query. May be any valid PHP variable. |
|
232 * |
|
233 * @return QueryAlterableInterface |
|
234 * The called object. |
|
235 */ |
|
236 public function addMetaData($key, $object); |
|
237 |
|
238 /** |
|
239 * Retrieves a given piece of metadata. |
|
240 * |
|
241 * @param $key |
|
242 * The unique identifier for the piece of metadata to retrieve. |
|
243 * |
|
244 * @return |
|
245 * The previously attached metadata object, or NULL if one doesn't exist. |
|
246 */ |
|
247 public function getMetaData($key); |
|
248 } |
|
249 |
|
250 /** |
|
251 * Interface for a query that accepts placeholders. |
|
252 */ |
|
253 interface QueryPlaceholderInterface { |
|
254 |
|
255 /** |
|
256 * Returns a unique identifier for this object. |
|
257 */ |
|
258 public function uniqueIdentifier(); |
|
259 |
|
260 /** |
|
261 * Returns the next placeholder ID for the query. |
|
262 * |
|
263 * @return |
|
264 * The next available placeholder ID as an integer. |
|
265 */ |
|
266 public function nextPlaceholder(); |
|
267 } |
|
268 |
|
269 /** |
|
270 * Base class for query builders. |
|
271 * |
|
272 * Note that query builders use PHP's magic __toString() method to compile the |
|
273 * query object into a prepared statement. |
|
274 */ |
|
275 abstract class Query implements QueryPlaceholderInterface { |
|
276 |
|
277 /** |
|
278 * The connection object on which to run this query. |
|
279 * |
|
280 * @var DatabaseConnection |
|
281 */ |
|
282 protected $connection; |
|
283 |
|
284 /** |
|
285 * The target of the connection object. |
|
286 * |
|
287 * @var string |
|
288 */ |
|
289 protected $connectionTarget; |
|
290 |
|
291 /** |
|
292 * The key of the connection object. |
|
293 * |
|
294 * @var string |
|
295 */ |
|
296 protected $connectionKey; |
|
297 |
|
298 /** |
|
299 * The query options to pass on to the connection object. |
|
300 * |
|
301 * @var array |
|
302 */ |
|
303 protected $queryOptions; |
|
304 |
|
305 /** |
|
306 * A unique identifier for this query object. |
|
307 */ |
|
308 protected $uniqueIdentifier; |
|
309 |
|
310 /** |
|
311 * The placeholder counter. |
|
312 */ |
|
313 protected $nextPlaceholder = 0; |
|
314 |
|
315 /** |
|
316 * An array of comments that can be prepended to a query. |
|
317 * |
|
318 * @var array |
|
319 */ |
|
320 protected $comments = array(); |
|
321 |
|
322 /** |
|
323 * Constructs a Query object. |
|
324 * |
|
325 * @param DatabaseConnection $connection |
|
326 * Database connection object. |
|
327 * @param array $options |
|
328 * Array of query options. |
|
329 */ |
|
330 public function __construct(DatabaseConnection $connection, $options) { |
|
331 $this->uniqueIdentifier = uniqid('', TRUE); |
|
332 |
|
333 $this->connection = $connection; |
|
334 $this->connectionKey = $this->connection->getKey(); |
|
335 $this->connectionTarget = $this->connection->getTarget(); |
|
336 |
|
337 $this->queryOptions = $options; |
|
338 } |
|
339 |
|
340 /** |
|
341 * Implements the magic __sleep function to disconnect from the database. |
|
342 */ |
|
343 public function __sleep() { |
|
344 $keys = get_object_vars($this); |
|
345 unset($keys['connection']); |
|
346 return array_keys($keys); |
|
347 } |
|
348 |
|
349 /** |
|
350 * Implements the magic __wakeup function to reconnect to the database. |
|
351 */ |
|
352 public function __wakeup() { |
|
353 $this->connection = Database::getConnection($this->connectionTarget, $this->connectionKey); |
|
354 } |
|
355 |
|
356 /** |
|
357 * Implements the magic __clone function. |
|
358 */ |
|
359 public function __clone() { |
|
360 $this->uniqueIdentifier = uniqid('', TRUE); |
|
361 } |
|
362 |
|
363 /** |
|
364 * Runs the query against the database. |
|
365 */ |
|
366 abstract protected function execute(); |
|
367 |
|
368 /** |
|
369 * Implements PHP magic __toString method to convert the query to a string. |
|
370 * |
|
371 * The toString operation is how we compile a query object to a prepared |
|
372 * statement. |
|
373 * |
|
374 * @return |
|
375 * A prepared statement query string for this object. |
|
376 */ |
|
377 abstract public function __toString(); |
|
378 |
|
379 /** |
|
380 * Returns a unique identifier for this object. |
|
381 */ |
|
382 public function uniqueIdentifier() { |
|
383 return $this->uniqueIdentifier; |
|
384 } |
|
385 |
|
386 /** |
|
387 * Gets the next placeholder value for this query object. |
|
388 * |
|
389 * @return int |
|
390 * Next placeholder value. |
|
391 */ |
|
392 public function nextPlaceholder() { |
|
393 return $this->nextPlaceholder++; |
|
394 } |
|
395 |
|
396 /** |
|
397 * Adds a comment to the query. |
|
398 * |
|
399 * By adding a comment to a query, you can more easily find it in your |
|
400 * query log or the list of active queries on an SQL server. This allows |
|
401 * for easier debugging and allows you to more easily find where a query |
|
402 * with a performance problem is being generated. |
|
403 * |
|
404 * The comment string will be sanitized to remove * / and other characters |
|
405 * that may terminate the string early so as to avoid SQL injection attacks. |
|
406 * |
|
407 * @param $comment |
|
408 * The comment string to be inserted into the query. |
|
409 * |
|
410 * @return Query |
|
411 * The called object. |
|
412 */ |
|
413 public function comment($comment) { |
|
414 $this->comments[] = $comment; |
|
415 return $this; |
|
416 } |
|
417 |
|
418 /** |
|
419 * Returns a reference to the comments array for the query. |
|
420 * |
|
421 * Because this method returns by reference, alter hooks may edit the comments |
|
422 * array directly to make their changes. If just adding comments, however, the |
|
423 * use of comment() is preferred. |
|
424 * |
|
425 * Note that this method must be called by reference as well: |
|
426 * @code |
|
427 * $comments =& $query->getComments(); |
|
428 * @endcode |
|
429 * |
|
430 * @return |
|
431 * A reference to the comments array structure. |
|
432 */ |
|
433 public function &getComments() { |
|
434 return $this->comments; |
|
435 } |
|
436 } |
|
437 |
|
438 /** |
|
439 * General class for an abstracted INSERT query. |
|
440 */ |
|
441 class InsertQuery extends Query { |
|
442 |
|
443 /** |
|
444 * The table on which to insert. |
|
445 * |
|
446 * @var string |
|
447 */ |
|
448 protected $table; |
|
449 |
|
450 /** |
|
451 * An array of fields on which to insert. |
|
452 * |
|
453 * @var array |
|
454 */ |
|
455 protected $insertFields = array(); |
|
456 |
|
457 /** |
|
458 * An array of fields that should be set to their database-defined defaults. |
|
459 * |
|
460 * @var array |
|
461 */ |
|
462 protected $defaultFields = array(); |
|
463 |
|
464 /** |
|
465 * A nested array of values to insert. |
|
466 * |
|
467 * $insertValues is an array of arrays. Each sub-array is either an |
|
468 * associative array whose keys are field names and whose values are field |
|
469 * values to insert, or a non-associative array of values in the same order |
|
470 * as $insertFields. |
|
471 * |
|
472 * Whether multiple insert sets will be run in a single query or multiple |
|
473 * queries is left to individual drivers to implement in whatever manner is |
|
474 * most appropriate. The order of values in each sub-array must match the |
|
475 * order of fields in $insertFields. |
|
476 * |
|
477 * @var array |
|
478 */ |
|
479 protected $insertValues = array(); |
|
480 |
|
481 /** |
|
482 * A SelectQuery object to fetch the rows that should be inserted. |
|
483 * |
|
484 * @var SelectQueryInterface |
|
485 */ |
|
486 protected $fromQuery; |
|
487 |
|
488 /** |
|
489 * Constructs an InsertQuery object. |
|
490 * |
|
491 * @param DatabaseConnection $connection |
|
492 * A DatabaseConnection object. |
|
493 * @param string $table |
|
494 * Name of the table to associate with this query. |
|
495 * @param array $options |
|
496 * Array of database options. |
|
497 */ |
|
498 public function __construct($connection, $table, array $options = array()) { |
|
499 if (!isset($options['return'])) { |
|
500 $options['return'] = Database::RETURN_INSERT_ID; |
|
501 } |
|
502 parent::__construct($connection, $options); |
|
503 $this->table = $table; |
|
504 } |
|
505 |
|
506 /** |
|
507 * Adds a set of field->value pairs to be inserted. |
|
508 * |
|
509 * This method may only be called once. Calling it a second time will be |
|
510 * ignored. To queue up multiple sets of values to be inserted at once, |
|
511 * use the values() method. |
|
512 * |
|
513 * @param $fields |
|
514 * An array of fields on which to insert. This array may be indexed or |
|
515 * associative. If indexed, the array is taken to be the list of fields. |
|
516 * If associative, the keys of the array are taken to be the fields and |
|
517 * the values are taken to be corresponding values to insert. If a |
|
518 * $values argument is provided, $fields must be indexed. |
|
519 * @param $values |
|
520 * An array of fields to insert into the database. The values must be |
|
521 * specified in the same order as the $fields array. |
|
522 * |
|
523 * @return InsertQuery |
|
524 * The called object. |
|
525 */ |
|
526 public function fields(array $fields, array $values = array()) { |
|
527 if (empty($this->insertFields)) { |
|
528 if (empty($values)) { |
|
529 if (!is_numeric(key($fields))) { |
|
530 $values = array_values($fields); |
|
531 $fields = array_keys($fields); |
|
532 } |
|
533 } |
|
534 $this->insertFields = $fields; |
|
535 if (!empty($values)) { |
|
536 $this->insertValues[] = $values; |
|
537 } |
|
538 } |
|
539 |
|
540 return $this; |
|
541 } |
|
542 |
|
543 /** |
|
544 * Adds another set of values to the query to be inserted. |
|
545 * |
|
546 * If $values is a numeric-keyed array, it will be assumed to be in the same |
|
547 * order as the original fields() call. If it is associative, it may be |
|
548 * in any order as long as the keys of the array match the names of the |
|
549 * fields. |
|
550 * |
|
551 * @param $values |
|
552 * An array of values to add to the query. |
|
553 * |
|
554 * @return InsertQuery |
|
555 * The called object. |
|
556 */ |
|
557 public function values(array $values) { |
|
558 if (is_numeric(key($values))) { |
|
559 $this->insertValues[] = $values; |
|
560 } |
|
561 else { |
|
562 // Reorder the submitted values to match the fields array. |
|
563 foreach ($this->insertFields as $key) { |
|
564 $insert_values[$key] = $values[$key]; |
|
565 } |
|
566 // For consistency, the values array is always numerically indexed. |
|
567 $this->insertValues[] = array_values($insert_values); |
|
568 } |
|
569 return $this; |
|
570 } |
|
571 |
|
572 /** |
|
573 * Specifies fields for which the database defaults should be used. |
|
574 * |
|
575 * If you want to force a given field to use the database-defined default, |
|
576 * not NULL or undefined, use this method to instruct the database to use |
|
577 * default values explicitly. In most cases this will not be necessary |
|
578 * unless you are inserting a row that is all default values, as you cannot |
|
579 * specify no values in an INSERT query. |
|
580 * |
|
581 * Specifying a field both in fields() and in useDefaults() is an error |
|
582 * and will not execute. |
|
583 * |
|
584 * @param $fields |
|
585 * An array of values for which to use the default values |
|
586 * specified in the table definition. |
|
587 * |
|
588 * @return InsertQuery |
|
589 * The called object. |
|
590 */ |
|
591 public function useDefaults(array $fields) { |
|
592 $this->defaultFields = $fields; |
|
593 return $this; |
|
594 } |
|
595 |
|
596 /** |
|
597 * Sets the fromQuery on this InsertQuery object. |
|
598 * |
|
599 * @param SelectQueryInterface $query |
|
600 * The query to fetch the rows that should be inserted. |
|
601 * |
|
602 * @return InsertQuery |
|
603 * The called object. |
|
604 */ |
|
605 public function from(SelectQueryInterface $query) { |
|
606 $this->fromQuery = $query; |
|
607 return $this; |
|
608 } |
|
609 |
|
610 /** |
|
611 * Executes the insert query. |
|
612 * |
|
613 * @return |
|
614 * The last insert ID of the query, if one exists. If the query |
|
615 * was given multiple sets of values to insert, the return value is |
|
616 * undefined. If no fields are specified, this method will do nothing and |
|
617 * return NULL. That makes it safe to use in multi-insert loops. |
|
618 */ |
|
619 public function execute() { |
|
620 // If validation fails, simply return NULL. Note that validation routines |
|
621 // in preExecute() may throw exceptions instead. |
|
622 if (!$this->preExecute()) { |
|
623 return NULL; |
|
624 } |
|
625 |
|
626 // If we're selecting from a SelectQuery, finish building the query and |
|
627 // pass it back, as any remaining options are irrelevant. |
|
628 if (!empty($this->fromQuery)) { |
|
629 $sql = (string) $this; |
|
630 // The SelectQuery may contain arguments, load and pass them through. |
|
631 return $this->connection->query($sql, $this->fromQuery->getArguments(), $this->queryOptions); |
|
632 } |
|
633 |
|
634 $last_insert_id = 0; |
|
635 |
|
636 // Each insert happens in its own query in the degenerate case. However, |
|
637 // we wrap it in a transaction so that it is atomic where possible. On many |
|
638 // databases, such as SQLite, this is also a notable performance boost. |
|
639 $transaction = $this->connection->startTransaction(); |
|
640 |
|
641 try { |
|
642 $sql = (string) $this; |
|
643 foreach ($this->insertValues as $insert_values) { |
|
644 $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions); |
|
645 } |
|
646 } |
|
647 catch (Exception $e) { |
|
648 // One of the INSERTs failed, rollback the whole batch. |
|
649 $transaction->rollback(); |
|
650 // Rethrow the exception for the calling code. |
|
651 throw $e; |
|
652 } |
|
653 |
|
654 // Re-initialize the values array so that we can re-use this query. |
|
655 $this->insertValues = array(); |
|
656 |
|
657 // Transaction commits here where $transaction looses scope. |
|
658 |
|
659 return $last_insert_id; |
|
660 } |
|
661 |
|
662 /** |
|
663 * Implements PHP magic __toString method to convert the query to a string. |
|
664 * |
|
665 * @return string |
|
666 * The prepared statement. |
|
667 */ |
|
668 public function __toString() { |
|
669 // Create a sanitized comment string to prepend to the query. |
|
670 $comments = $this->connection->makeComment($this->comments); |
|
671 |
|
672 // Default fields are always placed first for consistency. |
|
673 $insert_fields = array_merge($this->defaultFields, $this->insertFields); |
|
674 |
|
675 if (!empty($this->fromQuery)) { |
|
676 return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; |
|
677 } |
|
678 |
|
679 // For simplicity, we will use the $placeholders array to inject |
|
680 // default keywords even though they are not, strictly speaking, |
|
681 // placeholders for prepared statements. |
|
682 $placeholders = array(); |
|
683 $placeholders = array_pad($placeholders, count($this->defaultFields), 'default'); |
|
684 $placeholders = array_pad($placeholders, count($this->insertFields), '?'); |
|
685 |
|
686 return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')'; |
|
687 } |
|
688 |
|
689 /** |
|
690 * Preprocesses and validates the query. |
|
691 * |
|
692 * @return |
|
693 * TRUE if the validation was successful, FALSE if not. |
|
694 * |
|
695 * @throws FieldsOverlapException |
|
696 * @throws NoFieldsException |
|
697 */ |
|
698 public function preExecute() { |
|
699 // Confirm that the user did not try to specify an identical |
|
700 // field and default field. |
|
701 if (array_intersect($this->insertFields, $this->defaultFields)) { |
|
702 throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.'); |
|
703 } |
|
704 |
|
705 if (!empty($this->fromQuery)) { |
|
706 // We have to assume that the used aliases match the insert fields. |
|
707 // Regular fields are added to the query before expressions, maintain the |
|
708 // same order for the insert fields. |
|
709 // This behavior can be overridden by calling fields() manually as only the |
|
710 // first call to fields() does have an effect. |
|
711 $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions()))); |
|
712 } |
|
713 else { |
|
714 // Don't execute query without fields. |
|
715 if (count($this->insertFields) + count($this->defaultFields) == 0) { |
|
716 throw new NoFieldsException('There are no fields available to insert with.'); |
|
717 } |
|
718 } |
|
719 |
|
720 // If no values have been added, silently ignore this query. This can happen |
|
721 // if values are added conditionally, so we don't want to throw an |
|
722 // exception. |
|
723 if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) { |
|
724 return FALSE; |
|
725 } |
|
726 return TRUE; |
|
727 } |
|
728 } |
|
729 |
|
730 /** |
|
731 * General class for an abstracted DELETE operation. |
|
732 */ |
|
733 class DeleteQuery extends Query implements QueryConditionInterface { |
|
734 |
|
735 /** |
|
736 * The table from which to delete. |
|
737 * |
|
738 * @var string |
|
739 */ |
|
740 protected $table; |
|
741 |
|
742 /** |
|
743 * The condition object for this query. |
|
744 * |
|
745 * Condition handling is handled via composition. |
|
746 * |
|
747 * @var DatabaseCondition |
|
748 */ |
|
749 protected $condition; |
|
750 |
|
751 /** |
|
752 * Constructs a DeleteQuery object. |
|
753 * |
|
754 * @param DatabaseConnection $connection |
|
755 * A DatabaseConnection object. |
|
756 * @param string $table |
|
757 * Name of the table to associate with this query. |
|
758 * @param array $options |
|
759 * Array of database options. |
|
760 */ |
|
761 public function __construct(DatabaseConnection $connection, $table, array $options = array()) { |
|
762 $options['return'] = Database::RETURN_AFFECTED; |
|
763 parent::__construct($connection, $options); |
|
764 $this->table = $table; |
|
765 |
|
766 $this->condition = new DatabaseCondition('AND'); |
|
767 } |
|
768 |
|
769 /** |
|
770 * Implements QueryConditionInterface::condition(). |
|
771 */ |
|
772 public function condition($field, $value = NULL, $operator = NULL) { |
|
773 $this->condition->condition($field, $value, $operator); |
|
774 return $this; |
|
775 } |
|
776 |
|
777 /** |
|
778 * Implements QueryConditionInterface::isNull(). |
|
779 */ |
|
780 public function isNull($field) { |
|
781 $this->condition->isNull($field); |
|
782 return $this; |
|
783 } |
|
784 |
|
785 /** |
|
786 * Implements QueryConditionInterface::isNotNull(). |
|
787 */ |
|
788 public function isNotNull($field) { |
|
789 $this->condition->isNotNull($field); |
|
790 return $this; |
|
791 } |
|
792 |
|
793 /** |
|
794 * Implements QueryConditionInterface::exists(). |
|
795 */ |
|
796 public function exists(SelectQueryInterface $select) { |
|
797 $this->condition->exists($select); |
|
798 return $this; |
|
799 } |
|
800 |
|
801 /** |
|
802 * Implements QueryConditionInterface::notExists(). |
|
803 */ |
|
804 public function notExists(SelectQueryInterface $select) { |
|
805 $this->condition->notExists($select); |
|
806 return $this; |
|
807 } |
|
808 |
|
809 /** |
|
810 * Implements QueryConditionInterface::conditions(). |
|
811 */ |
|
812 public function &conditions() { |
|
813 return $this->condition->conditions(); |
|
814 } |
|
815 |
|
816 /** |
|
817 * Implements QueryConditionInterface::arguments(). |
|
818 */ |
|
819 public function arguments() { |
|
820 return $this->condition->arguments(); |
|
821 } |
|
822 |
|
823 /** |
|
824 * Implements QueryConditionInterface::where(). |
|
825 */ |
|
826 public function where($snippet, $args = array()) { |
|
827 $this->condition->where($snippet, $args); |
|
828 return $this; |
|
829 } |
|
830 |
|
831 /** |
|
832 * Implements QueryConditionInterface::compile(). |
|
833 */ |
|
834 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) { |
|
835 return $this->condition->compile($connection, $queryPlaceholder); |
|
836 } |
|
837 |
|
838 /** |
|
839 * Implements QueryConditionInterface::compiled(). |
|
840 */ |
|
841 public function compiled() { |
|
842 return $this->condition->compiled(); |
|
843 } |
|
844 |
|
845 /** |
|
846 * Executes the DELETE query. |
|
847 * |
|
848 * @return int |
|
849 * The number of rows affected by the delete query. |
|
850 */ |
|
851 public function execute() { |
|
852 $values = array(); |
|
853 if (count($this->condition)) { |
|
854 $this->condition->compile($this->connection, $this); |
|
855 $values = $this->condition->arguments(); |
|
856 } |
|
857 |
|
858 return $this->connection->query((string) $this, $values, $this->queryOptions); |
|
859 } |
|
860 |
|
861 /** |
|
862 * Implements PHP magic __toString method to convert the query to a string. |
|
863 * |
|
864 * @return string |
|
865 * The prepared statement. |
|
866 */ |
|
867 public function __toString() { |
|
868 // Create a sanitized comment string to prepend to the query. |
|
869 $comments = $this->connection->makeComment($this->comments); |
|
870 |
|
871 $query = $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} '; |
|
872 |
|
873 if (count($this->condition)) { |
|
874 |
|
875 $this->condition->compile($this->connection, $this); |
|
876 $query .= "\nWHERE " . $this->condition; |
|
877 } |
|
878 |
|
879 return $query; |
|
880 } |
|
881 } |
|
882 |
|
883 |
|
884 /** |
|
885 * General class for an abstracted TRUNCATE operation. |
|
886 */ |
|
887 class TruncateQuery extends Query { |
|
888 |
|
889 /** |
|
890 * The table to truncate. |
|
891 * |
|
892 * @var string |
|
893 */ |
|
894 protected $table; |
|
895 |
|
896 /** |
|
897 * Constructs a TruncateQuery object. |
|
898 * |
|
899 * @param DatabaseConnection $connection |
|
900 * A DatabaseConnection object. |
|
901 * @param string $table |
|
902 * Name of the table to associate with this query. |
|
903 * @param array $options |
|
904 * Array of database options. |
|
905 */ |
|
906 public function __construct(DatabaseConnection $connection, $table, array $options = array()) { |
|
907 $options['return'] = Database::RETURN_AFFECTED; |
|
908 parent::__construct($connection, $options); |
|
909 $this->table = $table; |
|
910 } |
|
911 |
|
912 /** |
|
913 * Implements QueryConditionInterface::compile(). |
|
914 */ |
|
915 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) { |
|
916 return $this->condition->compile($connection, $queryPlaceholder); |
|
917 } |
|
918 |
|
919 /** |
|
920 * Implements QueryConditionInterface::compiled(). |
|
921 */ |
|
922 public function compiled() { |
|
923 return $this->condition->compiled(); |
|
924 } |
|
925 |
|
926 /** |
|
927 * Executes the TRUNCATE query. |
|
928 * |
|
929 * @return |
|
930 * Return value is dependent on the database type. |
|
931 */ |
|
932 public function execute() { |
|
933 return $this->connection->query((string) $this, array(), $this->queryOptions); |
|
934 } |
|
935 |
|
936 /** |
|
937 * Implements PHP magic __toString method to convert the query to a string. |
|
938 * |
|
939 * @return string |
|
940 * The prepared statement. |
|
941 */ |
|
942 public function __toString() { |
|
943 // Create a sanitized comment string to prepend to the query. |
|
944 $comments = $this->connection->makeComment($this->comments); |
|
945 |
|
946 // In most cases, TRUNCATE is not a transaction safe statement as it is a |
|
947 // DDL statement which results in an implicit COMMIT. When we are in a |
|
948 // transaction, fallback to the slower, but transactional, DELETE. |
|
949 // PostgreSQL also locks the entire table for a TRUNCATE strongly reducing |
|
950 // the concurrency with other transactions. |
|
951 if ($this->connection->inTransaction()) { |
|
952 return $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '}'; |
|
953 } |
|
954 else { |
|
955 return $comments . 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} '; |
|
956 } |
|
957 } |
|
958 } |
|
959 |
|
960 /** |
|
961 * General class for an abstracted UPDATE operation. |
|
962 */ |
|
963 class UpdateQuery extends Query implements QueryConditionInterface { |
|
964 |
|
965 /** |
|
966 * The table to update. |
|
967 * |
|
968 * @var string |
|
969 */ |
|
970 protected $table; |
|
971 |
|
972 /** |
|
973 * An array of fields that will be updated. |
|
974 * |
|
975 * @var array |
|
976 */ |
|
977 protected $fields = array(); |
|
978 |
|
979 /** |
|
980 * An array of values to update to. |
|
981 * |
|
982 * @var array |
|
983 */ |
|
984 protected $arguments = array(); |
|
985 |
|
986 /** |
|
987 * The condition object for this query. |
|
988 * |
|
989 * Condition handling is handled via composition. |
|
990 * |
|
991 * @var DatabaseCondition |
|
992 */ |
|
993 protected $condition; |
|
994 |
|
995 /** |
|
996 * Array of fields to update to an expression in case of a duplicate record. |
|
997 * |
|
998 * This variable is a nested array in the following format: |
|
999 * @code |
|
1000 * <some field> => array( |
|
1001 * 'condition' => <condition to execute, as a string>, |
|
1002 * 'arguments' => <array of arguments for condition, or NULL for none>, |
|
1003 * ); |
|
1004 * @endcode |
|
1005 * |
|
1006 * @var array |
|
1007 */ |
|
1008 protected $expressionFields = array(); |
|
1009 |
|
1010 /** |
|
1011 * Constructs an UpdateQuery object. |
|
1012 * |
|
1013 * @param DatabaseConnection $connection |
|
1014 * A DatabaseConnection object. |
|
1015 * @param string $table |
|
1016 * Name of the table to associate with this query. |
|
1017 * @param array $options |
|
1018 * Array of database options. |
|
1019 */ |
|
1020 public function __construct(DatabaseConnection $connection, $table, array $options = array()) { |
|
1021 $options['return'] = Database::RETURN_AFFECTED; |
|
1022 parent::__construct($connection, $options); |
|
1023 $this->table = $table; |
|
1024 |
|
1025 $this->condition = new DatabaseCondition('AND'); |
|
1026 } |
|
1027 |
|
1028 /** |
|
1029 * Implements QueryConditionInterface::condition(). |
|
1030 */ |
|
1031 public function condition($field, $value = NULL, $operator = NULL) { |
|
1032 $this->condition->condition($field, $value, $operator); |
|
1033 return $this; |
|
1034 } |
|
1035 |
|
1036 /** |
|
1037 * Implements QueryConditionInterface::isNull(). |
|
1038 */ |
|
1039 public function isNull($field) { |
|
1040 $this->condition->isNull($field); |
|
1041 return $this; |
|
1042 } |
|
1043 |
|
1044 /** |
|
1045 * Implements QueryConditionInterface::isNotNull(). |
|
1046 */ |
|
1047 public function isNotNull($field) { |
|
1048 $this->condition->isNotNull($field); |
|
1049 return $this; |
|
1050 } |
|
1051 |
|
1052 /** |
|
1053 * Implements QueryConditionInterface::exists(). |
|
1054 */ |
|
1055 public function exists(SelectQueryInterface $select) { |
|
1056 $this->condition->exists($select); |
|
1057 return $this; |
|
1058 } |
|
1059 |
|
1060 /** |
|
1061 * Implements QueryConditionInterface::notExists(). |
|
1062 */ |
|
1063 public function notExists(SelectQueryInterface $select) { |
|
1064 $this->condition->notExists($select); |
|
1065 return $this; |
|
1066 } |
|
1067 |
|
1068 /** |
|
1069 * Implements QueryConditionInterface::conditions(). |
|
1070 */ |
|
1071 public function &conditions() { |
|
1072 return $this->condition->conditions(); |
|
1073 } |
|
1074 |
|
1075 /** |
|
1076 * Implements QueryConditionInterface::arguments(). |
|
1077 */ |
|
1078 public function arguments() { |
|
1079 return $this->condition->arguments(); |
|
1080 } |
|
1081 |
|
1082 /** |
|
1083 * Implements QueryConditionInterface::where(). |
|
1084 */ |
|
1085 public function where($snippet, $args = array()) { |
|
1086 $this->condition->where($snippet, $args); |
|
1087 return $this; |
|
1088 } |
|
1089 |
|
1090 /** |
|
1091 * Implements QueryConditionInterface::compile(). |
|
1092 */ |
|
1093 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) { |
|
1094 return $this->condition->compile($connection, $queryPlaceholder); |
|
1095 } |
|
1096 |
|
1097 /** |
|
1098 * Implements QueryConditionInterface::compiled(). |
|
1099 */ |
|
1100 public function compiled() { |
|
1101 return $this->condition->compiled(); |
|
1102 } |
|
1103 |
|
1104 /** |
|
1105 * Adds a set of field->value pairs to be updated. |
|
1106 * |
|
1107 * @param $fields |
|
1108 * An associative array of fields to write into the database. The array keys |
|
1109 * are the field names and the values are the values to which to set them. |
|
1110 * |
|
1111 * @return UpdateQuery |
|
1112 * The called object. |
|
1113 */ |
|
1114 public function fields(array $fields) { |
|
1115 $this->fields = $fields; |
|
1116 return $this; |
|
1117 } |
|
1118 |
|
1119 /** |
|
1120 * Specifies fields to be updated as an expression. |
|
1121 * |
|
1122 * Expression fields are cases such as counter=counter+1. This method takes |
|
1123 * precedence over fields(). |
|
1124 * |
|
1125 * @param $field |
|
1126 * The field to set. |
|
1127 * @param $expression |
|
1128 * The field will be set to the value of this expression. This parameter |
|
1129 * may include named placeholders. |
|
1130 * @param $arguments |
|
1131 * If specified, this is an array of key/value pairs for named placeholders |
|
1132 * corresponding to the expression. |
|
1133 * |
|
1134 * @return UpdateQuery |
|
1135 * The called object. |
|
1136 */ |
|
1137 public function expression($field, $expression, array $arguments = NULL) { |
|
1138 $this->expressionFields[$field] = array( |
|
1139 'expression' => $expression, |
|
1140 'arguments' => $arguments, |
|
1141 ); |
|
1142 |
|
1143 return $this; |
|
1144 } |
|
1145 |
|
1146 /** |
|
1147 * Executes the UPDATE query. |
|
1148 * |
|
1149 * @return |
|
1150 * The number of rows affected by the update. |
|
1151 */ |
|
1152 public function execute() { |
|
1153 |
|
1154 // Expressions take priority over literal fields, so we process those first |
|
1155 // and remove any literal fields that conflict. |
|
1156 $fields = $this->fields; |
|
1157 $update_values = array(); |
|
1158 foreach ($this->expressionFields as $field => $data) { |
|
1159 if (!empty($data['arguments'])) { |
|
1160 $update_values += $data['arguments']; |
|
1161 } |
|
1162 unset($fields[$field]); |
|
1163 } |
|
1164 |
|
1165 // Because we filter $fields the same way here and in __toString(), the |
|
1166 // placeholders will all match up properly. |
|
1167 $max_placeholder = 0; |
|
1168 foreach ($fields as $field => $value) { |
|
1169 $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value; |
|
1170 } |
|
1171 |
|
1172 if (count($this->condition)) { |
|
1173 $this->condition->compile($this->connection, $this); |
|
1174 $update_values = array_merge($update_values, $this->condition->arguments()); |
|
1175 } |
|
1176 |
|
1177 return $this->connection->query((string) $this, $update_values, $this->queryOptions); |
|
1178 } |
|
1179 |
|
1180 /** |
|
1181 * Implements PHP magic __toString method to convert the query to a string. |
|
1182 * |
|
1183 * @return string |
|
1184 * The prepared statement. |
|
1185 */ |
|
1186 public function __toString() { |
|
1187 // Create a sanitized comment string to prepend to the query. |
|
1188 $comments = $this->connection->makeComment($this->comments); |
|
1189 |
|
1190 // Expressions take priority over literal fields, so we process those first |
|
1191 // and remove any literal fields that conflict. |
|
1192 $fields = $this->fields; |
|
1193 $update_fields = array(); |
|
1194 foreach ($this->expressionFields as $field => $data) { |
|
1195 $update_fields[] = $field . '=' . $data['expression']; |
|
1196 unset($fields[$field]); |
|
1197 } |
|
1198 |
|
1199 $max_placeholder = 0; |
|
1200 foreach ($fields as $field => $value) { |
|
1201 $update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++); |
|
1202 } |
|
1203 |
|
1204 $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields); |
|
1205 |
|
1206 if (count($this->condition)) { |
|
1207 $this->condition->compile($this->connection, $this); |
|
1208 // There is an implicit string cast on $this->condition. |
|
1209 $query .= "\nWHERE " . $this->condition; |
|
1210 } |
|
1211 |
|
1212 return $query; |
|
1213 } |
|
1214 |
|
1215 } |
|
1216 |
|
1217 /** |
|
1218 * General class for an abstracted MERGE query operation. |
|
1219 * |
|
1220 * An ANSI SQL:2003 compatible database would run the following query: |
|
1221 * |
|
1222 * @code |
|
1223 * MERGE INTO table_name_1 USING table_name_2 ON (condition) |
|
1224 * WHEN MATCHED THEN |
|
1225 * UPDATE SET column1 = value1 [, column2 = value2 ...] |
|
1226 * WHEN NOT MATCHED THEN |
|
1227 * INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ... |
|
1228 * @endcode |
|
1229 * |
|
1230 * Other databases (most notably MySQL, PostgreSQL and SQLite) will emulate |
|
1231 * this statement by running a SELECT and then INSERT or UPDATE. |
|
1232 * |
|
1233 * By default, the two table names are identical and they are passed into the |
|
1234 * the constructor. table_name_2 can be specified by the |
|
1235 * MergeQuery::conditionTable() method. It can be either a string or a |
|
1236 * subquery. |
|
1237 * |
|
1238 * The condition is built exactly like SelectQuery or UpdateQuery conditions, |
|
1239 * the UPDATE query part is built similarly like an UpdateQuery and finally the |
|
1240 * INSERT query part is built similarly like an InsertQuery. However, both |
|
1241 * UpdateQuery and InsertQuery has a fields method so |
|
1242 * MergeQuery::updateFields() and MergeQuery::insertFields() needs to be called |
|
1243 * instead. MergeQuery::fields() can also be called which calls both of these |
|
1244 * methods as the common case is to use the same column-value pairs for both |
|
1245 * INSERT and UPDATE. However, this is not mandatory. Another convenient |
|
1246 * wrapper is MergeQuery::key() which adds the same column-value pairs to the |
|
1247 * condition and the INSERT query part. |
|
1248 * |
|
1249 * Several methods (key(), fields(), insertFields()) can be called to set a |
|
1250 * key-value pair for the INSERT query part. Subsequent calls for the same |
|
1251 * fields override the earlier ones. The same is true for UPDATE and key(), |
|
1252 * fields() and updateFields(). |
|
1253 */ |
|
1254 class MergeQuery extends Query implements QueryConditionInterface { |
|
1255 /** |
|
1256 * Returned by execute() if an INSERT query has been executed. |
|
1257 */ |
|
1258 const STATUS_INSERT = 1; |
|
1259 |
|
1260 /** |
|
1261 * Returned by execute() if an UPDATE query has been executed. |
|
1262 */ |
|
1263 const STATUS_UPDATE = 2; |
|
1264 |
|
1265 /** |
|
1266 * The table to be used for INSERT and UPDATE. |
|
1267 * |
|
1268 * @var string |
|
1269 */ |
|
1270 protected $table; |
|
1271 |
|
1272 /** |
|
1273 * The table or subquery to be used for the condition. |
|
1274 */ |
|
1275 protected $conditionTable; |
|
1276 |
|
1277 /** |
|
1278 * An array of fields on which to insert. |
|
1279 * |
|
1280 * @var array |
|
1281 */ |
|
1282 protected $insertFields = array(); |
|
1283 |
|
1284 /** |
|
1285 * An array of fields which should be set to their database-defined defaults. |
|
1286 * |
|
1287 * Used on INSERT. |
|
1288 * |
|
1289 * @var array |
|
1290 */ |
|
1291 protected $defaultFields = array(); |
|
1292 |
|
1293 /** |
|
1294 * An array of values to be inserted. |
|
1295 * |
|
1296 * @var string |
|
1297 */ |
|
1298 protected $insertValues = array(); |
|
1299 |
|
1300 /** |
|
1301 * An array of fields that will be updated. |
|
1302 * |
|
1303 * @var array |
|
1304 */ |
|
1305 protected $updateFields = array(); |
|
1306 |
|
1307 /** |
|
1308 * Array of fields to update to an expression in case of a duplicate record. |
|
1309 * |
|
1310 * This variable is a nested array in the following format: |
|
1311 * @code |
|
1312 * <some field> => array( |
|
1313 * 'condition' => <condition to execute, as a string>, |
|
1314 * 'arguments' => <array of arguments for condition, or NULL for none>, |
|
1315 * ); |
|
1316 * @endcode |
|
1317 * |
|
1318 * @var array |
|
1319 */ |
|
1320 protected $expressionFields = array(); |
|
1321 |
|
1322 /** |
|
1323 * Flag indicating whether an UPDATE is necessary. |
|
1324 * |
|
1325 * @var boolean |
|
1326 */ |
|
1327 protected $needsUpdate = FALSE; |
|
1328 |
|
1329 /** |
|
1330 * Constructs a MergeQuery object. |
|
1331 * |
|
1332 * @param DatabaseConnection $connection |
|
1333 * A DatabaseConnection object. |
|
1334 * @param string $table |
|
1335 * Name of the table to associate with this query. |
|
1336 * @param array $options |
|
1337 * Array of database options. |
|
1338 */ |
|
1339 public function __construct(DatabaseConnection $connection, $table, array $options = array()) { |
|
1340 $options['return'] = Database::RETURN_AFFECTED; |
|
1341 parent::__construct($connection, $options); |
|
1342 $this->table = $table; |
|
1343 $this->conditionTable = $table; |
|
1344 $this->condition = new DatabaseCondition('AND'); |
|
1345 } |
|
1346 |
|
1347 /** |
|
1348 * Sets the table or subquery to be used for the condition. |
|
1349 * |
|
1350 * @param $table |
|
1351 * The table name or the subquery to be used. Use a SelectQuery object to |
|
1352 * pass in a subquery. |
|
1353 * |
|
1354 * @return MergeQuery |
|
1355 * The called object. |
|
1356 */ |
|
1357 protected function conditionTable($table) { |
|
1358 $this->conditionTable = $table; |
|
1359 return $this; |
|
1360 } |
|
1361 |
|
1362 /** |
|
1363 * Adds a set of field->value pairs to be updated. |
|
1364 * |
|
1365 * @param $fields |
|
1366 * An associative array of fields to write into the database. The array keys |
|
1367 * are the field names and the values are the values to which to set them. |
|
1368 * |
|
1369 * @return MergeQuery |
|
1370 * The called object. |
|
1371 */ |
|
1372 public function updateFields(array $fields) { |
|
1373 $this->updateFields = $fields; |
|
1374 $this->needsUpdate = TRUE; |
|
1375 return $this; |
|
1376 } |
|
1377 |
|
1378 /** |
|
1379 * Specifies fields to be updated as an expression. |
|
1380 * |
|
1381 * Expression fields are cases such as counter = counter + 1. This method |
|
1382 * takes precedence over MergeQuery::updateFields() and it's wrappers, |
|
1383 * MergeQuery::key() and MergeQuery::fields(). |
|
1384 * |
|
1385 * @param $field |
|
1386 * The field to set. |
|
1387 * @param $expression |
|
1388 * The field will be set to the value of this expression. This parameter |
|
1389 * may include named placeholders. |
|
1390 * @param $arguments |
|
1391 * If specified, this is an array of key/value pairs for named placeholders |
|
1392 * corresponding to the expression. |
|
1393 * |
|
1394 * @return MergeQuery |
|
1395 * The called object. |
|
1396 */ |
|
1397 public function expression($field, $expression, array $arguments = NULL) { |
|
1398 $this->expressionFields[$field] = array( |
|
1399 'expression' => $expression, |
|
1400 'arguments' => $arguments, |
|
1401 ); |
|
1402 $this->needsUpdate = TRUE; |
|
1403 return $this; |
|
1404 } |
|
1405 |
|
1406 /** |
|
1407 * Adds a set of field->value pairs to be inserted. |
|
1408 * |
|
1409 * @param $fields |
|
1410 * An array of fields on which to insert. This array may be indexed or |
|
1411 * associative. If indexed, the array is taken to be the list of fields. |
|
1412 * If associative, the keys of the array are taken to be the fields and |
|
1413 * the values are taken to be corresponding values to insert. If a |
|
1414 * $values argument is provided, $fields must be indexed. |
|
1415 * @param $values |
|
1416 * An array of fields to insert into the database. The values must be |
|
1417 * specified in the same order as the $fields array. |
|
1418 * |
|
1419 * @return MergeQuery |
|
1420 * The called object. |
|
1421 */ |
|
1422 public function insertFields(array $fields, array $values = array()) { |
|
1423 if ($values) { |
|
1424 $fields = array_combine($fields, $values); |
|
1425 } |
|
1426 $this->insertFields = $fields; |
|
1427 return $this; |
|
1428 } |
|
1429 |
|
1430 /** |
|
1431 * Specifies fields for which the database-defaults should be used. |
|
1432 * |
|
1433 * If you want to force a given field to use the database-defined default, |
|
1434 * not NULL or undefined, use this method to instruct the database to use |
|
1435 * default values explicitly. In most cases this will not be necessary |
|
1436 * unless you are inserting a row that is all default values, as you cannot |
|
1437 * specify no values in an INSERT query. |
|
1438 * |
|
1439 * Specifying a field both in fields() and in useDefaults() is an error |
|
1440 * and will not execute. |
|
1441 * |
|
1442 * @param $fields |
|
1443 * An array of values for which to use the default values |
|
1444 * specified in the table definition. |
|
1445 * |
|
1446 * @return MergeQuery |
|
1447 * The called object. |
|
1448 */ |
|
1449 public function useDefaults(array $fields) { |
|
1450 $this->defaultFields = $fields; |
|
1451 return $this; |
|
1452 } |
|
1453 |
|
1454 /** |
|
1455 * Sets common field-value pairs in the INSERT and UPDATE query parts. |
|
1456 * |
|
1457 * This method should only be called once. It may be called either |
|
1458 * with a single associative array or two indexed arrays. If called |
|
1459 * with an associative array, the keys are taken to be the fields |
|
1460 * and the values are taken to be the corresponding values to set. |
|
1461 * If called with two arrays, the first array is taken as the fields |
|
1462 * and the second array is taken as the corresponding values. |
|
1463 * |
|
1464 * @param $fields |
|
1465 * An array of fields to insert, or an associative array of fields and |
|
1466 * values. The keys of the array are taken to be the fields and the values |
|
1467 * are taken to be corresponding values to insert. |
|
1468 * @param $values |
|
1469 * An array of values to set into the database. The values must be |
|
1470 * specified in the same order as the $fields array. |
|
1471 * |
|
1472 * @return MergeQuery |
|
1473 * The called object. |
|
1474 */ |
|
1475 public function fields(array $fields, array $values = array()) { |
|
1476 if ($values) { |
|
1477 $fields = array_combine($fields, $values); |
|
1478 } |
|
1479 foreach ($fields as $key => $value) { |
|
1480 $this->insertFields[$key] = $value; |
|
1481 $this->updateFields[$key] = $value; |
|
1482 } |
|
1483 $this->needsUpdate = TRUE; |
|
1484 return $this; |
|
1485 } |
|
1486 |
|
1487 /** |
|
1488 * Sets the key field(s) to be used as conditions for this query. |
|
1489 * |
|
1490 * This method should only be called once. It may be called either |
|
1491 * with a single associative array or two indexed arrays. If called |
|
1492 * with an associative array, the keys are taken to be the fields |
|
1493 * and the values are taken to be the corresponding values to set. |
|
1494 * If called with two arrays, the first array is taken as the fields |
|
1495 * and the second array is taken as the corresponding values. |
|
1496 * |
|
1497 * The fields are copied to the condition of the query and the INSERT part. |
|
1498 * If no other method is called, the UPDATE will become a no-op. |
|
1499 * |
|
1500 * @param $fields |
|
1501 * An array of fields to set, or an associative array of fields and values. |
|
1502 * @param $values |
|
1503 * An array of values to set into the database. The values must be |
|
1504 * specified in the same order as the $fields array. |
|
1505 * |
|
1506 * @return MergeQuery |
|
1507 * The called object. |
|
1508 */ |
|
1509 public function key(array $fields, array $values = array()) { |
|
1510 if ($values) { |
|
1511 $fields = array_combine($fields, $values); |
|
1512 } |
|
1513 foreach ($fields as $key => $value) { |
|
1514 $this->insertFields[$key] = $value; |
|
1515 $this->condition($key, $value); |
|
1516 } |
|
1517 return $this; |
|
1518 } |
|
1519 |
|
1520 /** |
|
1521 * Implements QueryConditionInterface::condition(). |
|
1522 */ |
|
1523 public function condition($field, $value = NULL, $operator = NULL) { |
|
1524 $this->condition->condition($field, $value, $operator); |
|
1525 return $this; |
|
1526 } |
|
1527 |
|
1528 /** |
|
1529 * Implements QueryConditionInterface::isNull(). |
|
1530 */ |
|
1531 public function isNull($field) { |
|
1532 $this->condition->isNull($field); |
|
1533 return $this; |
|
1534 } |
|
1535 |
|
1536 /** |
|
1537 * Implements QueryConditionInterface::isNotNull(). |
|
1538 */ |
|
1539 public function isNotNull($field) { |
|
1540 $this->condition->isNotNull($field); |
|
1541 return $this; |
|
1542 } |
|
1543 |
|
1544 /** |
|
1545 * Implements QueryConditionInterface::exists(). |
|
1546 */ |
|
1547 public function exists(SelectQueryInterface $select) { |
|
1548 $this->condition->exists($select); |
|
1549 return $this; |
|
1550 } |
|
1551 |
|
1552 /** |
|
1553 * Implements QueryConditionInterface::notExists(). |
|
1554 */ |
|
1555 public function notExists(SelectQueryInterface $select) { |
|
1556 $this->condition->notExists($select); |
|
1557 return $this; |
|
1558 } |
|
1559 |
|
1560 /** |
|
1561 * Implements QueryConditionInterface::conditions(). |
|
1562 */ |
|
1563 public function &conditions() { |
|
1564 return $this->condition->conditions(); |
|
1565 } |
|
1566 |
|
1567 /** |
|
1568 * Implements QueryConditionInterface::arguments(). |
|
1569 */ |
|
1570 public function arguments() { |
|
1571 return $this->condition->arguments(); |
|
1572 } |
|
1573 |
|
1574 /** |
|
1575 * Implements QueryConditionInterface::where(). |
|
1576 */ |
|
1577 public function where($snippet, $args = array()) { |
|
1578 $this->condition->where($snippet, $args); |
|
1579 return $this; |
|
1580 } |
|
1581 |
|
1582 /** |
|
1583 * Implements QueryConditionInterface::compile(). |
|
1584 */ |
|
1585 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) { |
|
1586 return $this->condition->compile($connection, $queryPlaceholder); |
|
1587 } |
|
1588 |
|
1589 /** |
|
1590 * Implements QueryConditionInterface::compiled(). |
|
1591 */ |
|
1592 public function compiled() { |
|
1593 return $this->condition->compiled(); |
|
1594 } |
|
1595 |
|
1596 /** |
|
1597 * Implements PHP magic __toString method to convert the query to a string. |
|
1598 * |
|
1599 * In the degenerate case, there is no string-able query as this operation |
|
1600 * is potentially two queries. |
|
1601 * |
|
1602 * @return string |
|
1603 * The prepared query statement. |
|
1604 */ |
|
1605 public function __toString() { |
|
1606 } |
|
1607 |
|
1608 public function execute() { |
|
1609 if (!count($this->condition)) { |
|
1610 throw new InvalidMergeQueryException(t('Invalid merge query: no conditions')); |
|
1611 } |
|
1612 $select = $this->connection->select($this->conditionTable) |
|
1613 ->condition($this->condition); |
|
1614 $select->addExpression('1'); |
|
1615 if (!$select->execute()->fetchField()) { |
|
1616 try { |
|
1617 $insert = $this->connection->insert($this->table)->fields($this->insertFields); |
|
1618 if ($this->defaultFields) { |
|
1619 $insert->useDefaults($this->defaultFields); |
|
1620 } |
|
1621 $insert->execute(); |
|
1622 return self::STATUS_INSERT; |
|
1623 } |
|
1624 catch (Exception $e) { |
|
1625 // The insert query failed, maybe it's because a racing insert query |
|
1626 // beat us in inserting the same row. Retry the select query, if it |
|
1627 // returns a row, ignore the error and continue with the update |
|
1628 // query below. |
|
1629 if (!$select->execute()->fetchField()) { |
|
1630 throw $e; |
|
1631 } |
|
1632 } |
|
1633 } |
|
1634 if ($this->needsUpdate) { |
|
1635 $update = $this->connection->update($this->table) |
|
1636 ->fields($this->updateFields) |
|
1637 ->condition($this->condition); |
|
1638 if ($this->expressionFields) { |
|
1639 foreach ($this->expressionFields as $field => $data) { |
|
1640 $update->expression($field, $data['expression'], $data['arguments']); |
|
1641 } |
|
1642 } |
|
1643 $update->execute(); |
|
1644 return self::STATUS_UPDATE; |
|
1645 } |
|
1646 } |
|
1647 } |
|
1648 |
|
1649 /** |
|
1650 * Generic class for a series of conditions in a query. |
|
1651 */ |
|
1652 class DatabaseCondition implements QueryConditionInterface, Countable { |
|
1653 |
|
1654 /** |
|
1655 * Array of conditions. |
|
1656 * |
|
1657 * @var array |
|
1658 */ |
|
1659 protected $conditions = array(); |
|
1660 |
|
1661 /** |
|
1662 * Array of arguments. |
|
1663 * |
|
1664 * @var array |
|
1665 */ |
|
1666 protected $arguments = array(); |
|
1667 |
|
1668 /** |
|
1669 * Whether the conditions have been changed. |
|
1670 * |
|
1671 * TRUE if the condition has been changed since the last compile. |
|
1672 * FALSE if the condition has been compiled and not changed. |
|
1673 * |
|
1674 * @var bool |
|
1675 */ |
|
1676 protected $changed = TRUE; |
|
1677 |
|
1678 /** |
|
1679 * The identifier of the query placeholder this condition has been compiled against. |
|
1680 */ |
|
1681 protected $queryPlaceholderIdentifier; |
|
1682 |
|
1683 /** |
|
1684 * Constructs a DataBaseCondition object. |
|
1685 * |
|
1686 * @param string $conjunction |
|
1687 * The operator to use to combine conditions: 'AND' or 'OR'. |
|
1688 */ |
|
1689 public function __construct($conjunction) { |
|
1690 $this->conditions['#conjunction'] = $conjunction; |
|
1691 } |
|
1692 |
|
1693 /** |
|
1694 * Implements Countable::count(). |
|
1695 * |
|
1696 * Returns the size of this conditional. The size of the conditional is the |
|
1697 * size of its conditional array minus one, because one element is the |
|
1698 * conjunction. |
|
1699 */ |
|
1700 public function count() { |
|
1701 return count($this->conditions) - 1; |
|
1702 } |
|
1703 |
|
1704 /** |
|
1705 * Implements QueryConditionInterface::condition(). |
|
1706 */ |
|
1707 public function condition($field, $value = NULL, $operator = NULL) { |
|
1708 if (!isset($operator)) { |
|
1709 if (is_array($value)) { |
|
1710 $operator = 'IN'; |
|
1711 } |
|
1712 elseif (!isset($value)) { |
|
1713 $operator = 'IS NULL'; |
|
1714 } |
|
1715 else { |
|
1716 $operator = '='; |
|
1717 } |
|
1718 } |
|
1719 $this->conditions[] = array( |
|
1720 'field' => $field, |
|
1721 'value' => $value, |
|
1722 'operator' => $operator, |
|
1723 ); |
|
1724 |
|
1725 $this->changed = TRUE; |
|
1726 |
|
1727 return $this; |
|
1728 } |
|
1729 |
|
1730 /** |
|
1731 * Implements QueryConditionInterface::where(). |
|
1732 */ |
|
1733 public function where($snippet, $args = array()) { |
|
1734 $this->conditions[] = array( |
|
1735 'field' => $snippet, |
|
1736 'value' => $args, |
|
1737 'operator' => NULL, |
|
1738 ); |
|
1739 $this->changed = TRUE; |
|
1740 |
|
1741 return $this; |
|
1742 } |
|
1743 |
|
1744 /** |
|
1745 * Implements QueryConditionInterface::isNull(). |
|
1746 */ |
|
1747 public function isNull($field) { |
|
1748 return $this->condition($field); |
|
1749 } |
|
1750 |
|
1751 /** |
|
1752 * Implements QueryConditionInterface::isNotNull(). |
|
1753 */ |
|
1754 public function isNotNull($field) { |
|
1755 return $this->condition($field, NULL, 'IS NOT NULL'); |
|
1756 } |
|
1757 |
|
1758 /** |
|
1759 * Implements QueryConditionInterface::exists(). |
|
1760 */ |
|
1761 public function exists(SelectQueryInterface $select) { |
|
1762 return $this->condition('', $select, 'EXISTS'); |
|
1763 } |
|
1764 |
|
1765 /** |
|
1766 * Implements QueryConditionInterface::notExists(). |
|
1767 */ |
|
1768 public function notExists(SelectQueryInterface $select) { |
|
1769 return $this->condition('', $select, 'NOT EXISTS'); |
|
1770 } |
|
1771 |
|
1772 /** |
|
1773 * Implements QueryConditionInterface::conditions(). |
|
1774 */ |
|
1775 public function &conditions() { |
|
1776 return $this->conditions; |
|
1777 } |
|
1778 |
|
1779 /** |
|
1780 * Implements QueryConditionInterface::arguments(). |
|
1781 */ |
|
1782 public function arguments() { |
|
1783 // If the caller forgot to call compile() first, refuse to run. |
|
1784 if ($this->changed) { |
|
1785 return NULL; |
|
1786 } |
|
1787 return $this->arguments; |
|
1788 } |
|
1789 |
|
1790 /** |
|
1791 * Implements QueryConditionInterface::compile(). |
|
1792 */ |
|
1793 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) { |
|
1794 // Re-compile if this condition changed or if we are compiled against a |
|
1795 // different query placeholder object. |
|
1796 if ($this->changed || isset($this->queryPlaceholderIdentifier) && ($this->queryPlaceholderIdentifier != $queryPlaceholder->uniqueIdentifier())) { |
|
1797 $this->queryPlaceholderIdentifier = $queryPlaceholder->uniqueIdentifier(); |
|
1798 |
|
1799 $condition_fragments = array(); |
|
1800 $arguments = array(); |
|
1801 |
|
1802 $conditions = $this->conditions; |
|
1803 $conjunction = $conditions['#conjunction']; |
|
1804 unset($conditions['#conjunction']); |
|
1805 foreach ($conditions as $condition) { |
|
1806 if (empty($condition['operator'])) { |
|
1807 // This condition is a literal string, so let it through as is. |
|
1808 $condition_fragments[] = ' (' . $condition['field'] . ') '; |
|
1809 $arguments += $condition['value']; |
|
1810 } |
|
1811 else { |
|
1812 // It's a structured condition, so parse it out accordingly. |
|
1813 // Note that $condition['field'] will only be an object for a dependent |
|
1814 // DatabaseCondition object, not for a dependent subquery. |
|
1815 if ($condition['field'] instanceof QueryConditionInterface) { |
|
1816 // Compile the sub-condition recursively and add it to the list. |
|
1817 $condition['field']->compile($connection, $queryPlaceholder); |
|
1818 $condition_fragments[] = '(' . (string) $condition['field'] . ')'; |
|
1819 $arguments += $condition['field']->arguments(); |
|
1820 } |
|
1821 else { |
|
1822 // For simplicity, we treat all operators as the same data structure. |
|
1823 // In the typical degenerate case, this won't get changed. |
|
1824 $operator_defaults = array( |
|
1825 'prefix' => '', |
|
1826 'postfix' => '', |
|
1827 'delimiter' => '', |
|
1828 'operator' => $condition['operator'], |
|
1829 'use_value' => TRUE, |
|
1830 ); |
|
1831 $operator = $connection->mapConditionOperator($condition['operator']); |
|
1832 if (!isset($operator)) { |
|
1833 $operator = $this->mapConditionOperator($condition['operator']); |
|
1834 } |
|
1835 $operator += $operator_defaults; |
|
1836 |
|
1837 $placeholders = array(); |
|
1838 if ($condition['value'] instanceof SelectQueryInterface) { |
|
1839 $condition['value']->compile($connection, $queryPlaceholder); |
|
1840 $placeholders[] = (string) $condition['value']; |
|
1841 $arguments += $condition['value']->arguments(); |
|
1842 // Subqueries are the actual value of the operator, we don't |
|
1843 // need to add another below. |
|
1844 $operator['use_value'] = FALSE; |
|
1845 } |
|
1846 // We assume that if there is a delimiter, then the value is an |
|
1847 // array. If not, it is a scalar. For simplicity, we first convert |
|
1848 // up to an array so that we can build the placeholders in the same way. |
|
1849 elseif (!$operator['delimiter']) { |
|
1850 $condition['value'] = array($condition['value']); |
|
1851 } |
|
1852 if ($operator['use_value']) { |
|
1853 foreach ($condition['value'] as $value) { |
|
1854 $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder(); |
|
1855 $arguments[$placeholder] = $value; |
|
1856 $placeholders[] = $placeholder; |
|
1857 } |
|
1858 } |
|
1859 $condition_fragments[] = ' (' . $connection->escapeField($condition['field']) . ' ' . $operator['operator'] . ' ' . $operator['prefix'] . implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ') '; |
|
1860 } |
|
1861 } |
|
1862 } |
|
1863 |
|
1864 $this->changed = FALSE; |
|
1865 $this->stringVersion = implode($conjunction, $condition_fragments); |
|
1866 $this->arguments = $arguments; |
|
1867 } |
|
1868 } |
|
1869 |
|
1870 /** |
|
1871 * Implements QueryConditionInterface::compiled(). |
|
1872 */ |
|
1873 public function compiled() { |
|
1874 return !$this->changed; |
|
1875 } |
|
1876 |
|
1877 /** |
|
1878 * Implements PHP magic __toString method to convert the conditions to string. |
|
1879 * |
|
1880 * @return string |
|
1881 * A string version of the conditions. |
|
1882 */ |
|
1883 public function __toString() { |
|
1884 // If the caller forgot to call compile() first, refuse to run. |
|
1885 if ($this->changed) { |
|
1886 return NULL; |
|
1887 } |
|
1888 return $this->stringVersion; |
|
1889 } |
|
1890 |
|
1891 /** |
|
1892 * PHP magic __clone() method. |
|
1893 * |
|
1894 * Only copies fields that implement QueryConditionInterface. Also sets |
|
1895 * $this->changed to TRUE. |
|
1896 */ |
|
1897 function __clone() { |
|
1898 $this->changed = TRUE; |
|
1899 foreach ($this->conditions as $key => $condition) { |
|
1900 if ($key !== '#conjunction') { |
|
1901 if ($condition['field'] instanceOf QueryConditionInterface) { |
|
1902 $this->conditions[$key]['field'] = clone($condition['field']); |
|
1903 } |
|
1904 if ($condition['value'] instanceOf SelectQueryInterface) { |
|
1905 $this->conditions[$key]['value'] = clone($condition['value']); |
|
1906 } |
|
1907 } |
|
1908 } |
|
1909 } |
|
1910 |
|
1911 /** |
|
1912 * Gets any special processing requirements for the condition operator. |
|
1913 * |
|
1914 * Some condition types require special processing, such as IN, because |
|
1915 * the value data they pass in is not a simple value. This is a simple |
|
1916 * overridable lookup function. |
|
1917 * |
|
1918 * @param $operator |
|
1919 * The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive. |
|
1920 * |
|
1921 * @return |
|
1922 * The extra handling directives for the specified operator, or NULL. |
|
1923 */ |
|
1924 protected function mapConditionOperator($operator) { |
|
1925 // $specials does not use drupal_static as its value never changes. |
|
1926 static $specials = array( |
|
1927 'BETWEEN' => array('delimiter' => ' AND '), |
|
1928 'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'), |
|
1929 'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'), |
|
1930 'EXISTS' => array('prefix' => ' (', 'postfix' => ')'), |
|
1931 'NOT EXISTS' => array('prefix' => ' (', 'postfix' => ')'), |
|
1932 'IS NULL' => array('use_value' => FALSE), |
|
1933 'IS NOT NULL' => array('use_value' => FALSE), |
|
1934 // Use backslash for escaping wildcard characters. |
|
1935 'LIKE' => array('postfix' => " ESCAPE '\\\\'"), |
|
1936 'NOT LIKE' => array('postfix' => " ESCAPE '\\\\'"), |
|
1937 // These ones are here for performance reasons. |
|
1938 '=' => array(), |
|
1939 '<' => array(), |
|
1940 '>' => array(), |
|
1941 '>=' => array(), |
|
1942 '<=' => array(), |
|
1943 ); |
|
1944 if (isset($specials[$operator])) { |
|
1945 $return = $specials[$operator]; |
|
1946 } |
|
1947 else { |
|
1948 // We need to upper case because PHP index matches are case sensitive but |
|
1949 // do not need the more expensive drupal_strtoupper because SQL statements are ASCII. |
|
1950 $operator = strtoupper($operator); |
|
1951 $return = isset($specials[$operator]) ? $specials[$operator] : array(); |
|
1952 } |
|
1953 |
|
1954 $return += array('operator' => $operator); |
|
1955 |
|
1956 return $return; |
|
1957 } |
|
1958 |
|
1959 } |
|
1960 |
|
1961 /** |
|
1962 * @} End of "addtogroup database". |
|
1963 */ |