annotate includes/database/query.inc @ 13:134d4b2e75f6

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