annotate includes/database/select.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 require_once dirname(__FILE__) . '/query.inc';
danielebarchiesi@0 9
danielebarchiesi@0 10 /**
danielebarchiesi@0 11 * Interface for extendable query objects.
danielebarchiesi@0 12 *
danielebarchiesi@0 13 * "Extenders" follow the "Decorator" OOP design pattern. That is, they wrap
danielebarchiesi@0 14 * and "decorate" another object. In our case, they implement the same interface
danielebarchiesi@0 15 * as select queries and wrap a select query, to which they delegate almost all
danielebarchiesi@0 16 * operations. Subclasses of this class may implement additional methods or
danielebarchiesi@0 17 * override existing methods as appropriate. Extenders may also wrap other
danielebarchiesi@0 18 * extender objects, allowing for arbitrarily complex "enhanced" queries.
danielebarchiesi@0 19 */
danielebarchiesi@0 20 interface QueryExtendableInterface {
danielebarchiesi@0 21
danielebarchiesi@0 22 /**
danielebarchiesi@0 23 * Enhance this object by wrapping it in an extender object.
danielebarchiesi@0 24 *
danielebarchiesi@0 25 * @param $extender_name
danielebarchiesi@0 26 * The base name of the extending class. The base name will be checked
danielebarchiesi@0 27 * against the current database connection to allow driver-specific subclasses
danielebarchiesi@0 28 * as well, using the same logic as the query objects themselves. For example,
danielebarchiesi@0 29 * PagerDefault_mysql is the MySQL-specific override for PagerDefault.
danielebarchiesi@0 30 * @return QueryExtendableInterface
danielebarchiesi@0 31 * The extender object, which now contains a reference to this object.
danielebarchiesi@0 32 */
danielebarchiesi@0 33 public function extend($extender_name);
danielebarchiesi@0 34 }
danielebarchiesi@0 35
danielebarchiesi@0 36 /**
danielebarchiesi@0 37 * Interface definition for a Select Query object.
danielebarchiesi@0 38 */
danielebarchiesi@0 39 interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableInterface, QueryExtendableInterface, QueryPlaceholderInterface {
danielebarchiesi@0 40
danielebarchiesi@0 41 /* Alter accessors to expose the query data to alter hooks. */
danielebarchiesi@0 42
danielebarchiesi@0 43 /**
danielebarchiesi@0 44 * Returns a reference to the fields array for this query.
danielebarchiesi@0 45 *
danielebarchiesi@0 46 * Because this method returns by reference, alter hooks may edit the fields
danielebarchiesi@0 47 * array directly to make their changes. If just adding fields, however, the
danielebarchiesi@0 48 * use of addField() is preferred.
danielebarchiesi@0 49 *
danielebarchiesi@0 50 * Note that this method must be called by reference as well:
danielebarchiesi@0 51 *
danielebarchiesi@0 52 * @code
danielebarchiesi@0 53 * $fields =& $query->getFields();
danielebarchiesi@0 54 * @endcode
danielebarchiesi@0 55 *
danielebarchiesi@0 56 * @return
danielebarchiesi@0 57 * A reference to the fields array structure.
danielebarchiesi@0 58 */
danielebarchiesi@0 59 public function &getFields();
danielebarchiesi@0 60
danielebarchiesi@0 61 /**
danielebarchiesi@0 62 * Returns a reference to the expressions array for this query.
danielebarchiesi@0 63 *
danielebarchiesi@0 64 * Because this method returns by reference, alter hooks may edit the expressions
danielebarchiesi@0 65 * array directly to make their changes. If just adding expressions, however, the
danielebarchiesi@0 66 * use of addExpression() is preferred.
danielebarchiesi@0 67 *
danielebarchiesi@0 68 * Note that this method must be called by reference as well:
danielebarchiesi@0 69 *
danielebarchiesi@0 70 * @code
danielebarchiesi@0 71 * $fields =& $query->getExpressions();
danielebarchiesi@0 72 * @endcode
danielebarchiesi@0 73 *
danielebarchiesi@0 74 * @return
danielebarchiesi@0 75 * A reference to the expression array structure.
danielebarchiesi@0 76 */
danielebarchiesi@0 77 public function &getExpressions();
danielebarchiesi@0 78
danielebarchiesi@0 79 /**
danielebarchiesi@0 80 * Returns a reference to the order by array for this query.
danielebarchiesi@0 81 *
danielebarchiesi@0 82 * Because this method returns by reference, alter hooks may edit the order-by
danielebarchiesi@0 83 * array directly to make their changes. If just adding additional ordering
danielebarchiesi@0 84 * fields, however, the use of orderBy() is preferred.
danielebarchiesi@0 85 *
danielebarchiesi@0 86 * Note that this method must be called by reference as well:
danielebarchiesi@0 87 *
danielebarchiesi@0 88 * @code
danielebarchiesi@0 89 * $fields =& $query->getOrderBy();
danielebarchiesi@0 90 * @endcode
danielebarchiesi@0 91 *
danielebarchiesi@0 92 * @return
danielebarchiesi@0 93 * A reference to the expression array structure.
danielebarchiesi@0 94 */
danielebarchiesi@0 95 public function &getOrderBy();
danielebarchiesi@0 96
danielebarchiesi@0 97 /**
danielebarchiesi@0 98 * Returns a reference to the group-by array for this query.
danielebarchiesi@0 99 *
danielebarchiesi@0 100 * Because this method returns by reference, alter hooks may edit the group-by
danielebarchiesi@0 101 * array directly to make their changes. If just adding additional grouping
danielebarchiesi@0 102 * fields, however, the use of groupBy() is preferred.
danielebarchiesi@0 103 *
danielebarchiesi@0 104 * Note that this method must be called by reference as well:
danielebarchiesi@0 105 *
danielebarchiesi@0 106 * @code
danielebarchiesi@0 107 * $fields =& $query->getGroupBy();
danielebarchiesi@0 108 * @endcode
danielebarchiesi@0 109 *
danielebarchiesi@0 110 * @return
danielebarchiesi@0 111 * A reference to the group-by array structure.
danielebarchiesi@0 112 */
danielebarchiesi@0 113 public function &getGroupBy();
danielebarchiesi@0 114
danielebarchiesi@0 115 /**
danielebarchiesi@0 116 * Returns a reference to the tables array for this query.
danielebarchiesi@0 117 *
danielebarchiesi@0 118 * Because this method returns by reference, alter hooks may edit the tables
danielebarchiesi@0 119 * array directly to make their changes. If just adding tables, however, the
danielebarchiesi@0 120 * use of the join() methods is preferred.
danielebarchiesi@0 121 *
danielebarchiesi@0 122 * Note that this method must be called by reference as well:
danielebarchiesi@0 123 *
danielebarchiesi@0 124 * @code
danielebarchiesi@0 125 * $fields =& $query->getTables();
danielebarchiesi@0 126 * @endcode
danielebarchiesi@0 127 *
danielebarchiesi@0 128 * @return
danielebarchiesi@0 129 * A reference to the tables array structure.
danielebarchiesi@0 130 */
danielebarchiesi@0 131 public function &getTables();
danielebarchiesi@0 132
danielebarchiesi@0 133 /**
danielebarchiesi@0 134 * Returns a reference to the union queries for this query. This include
danielebarchiesi@0 135 * queries for UNION, UNION ALL, and UNION DISTINCT.
danielebarchiesi@0 136 *
danielebarchiesi@0 137 * Because this method returns by reference, alter hooks may edit the tables
danielebarchiesi@0 138 * array directly to make their changes. If just adding union queries,
danielebarchiesi@0 139 * however, the use of the union() method is preferred.
danielebarchiesi@0 140 *
danielebarchiesi@0 141 * Note that this method must be called by reference as well:
danielebarchiesi@0 142 *
danielebarchiesi@0 143 * @code
danielebarchiesi@0 144 * $fields =& $query->getUnion();
danielebarchiesi@0 145 * @endcode
danielebarchiesi@0 146 *
danielebarchiesi@0 147 * @return
danielebarchiesi@0 148 * A reference to the union query array structure.
danielebarchiesi@0 149 */
danielebarchiesi@0 150 public function &getUnion();
danielebarchiesi@0 151
danielebarchiesi@0 152 /**
danielebarchiesi@0 153 * Compiles and returns an associative array of the arguments for this prepared statement.
danielebarchiesi@0 154 *
danielebarchiesi@0 155 * @param $queryPlaceholder
danielebarchiesi@0 156 * When collecting the arguments of a subquery, the main placeholder
danielebarchiesi@0 157 * object should be passed as this parameter.
danielebarchiesi@0 158 *
danielebarchiesi@0 159 * @return
danielebarchiesi@0 160 * An associative array of all placeholder arguments for this query.
danielebarchiesi@0 161 */
danielebarchiesi@0 162 public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL);
danielebarchiesi@0 163
danielebarchiesi@0 164 /* Query building operations */
danielebarchiesi@0 165
danielebarchiesi@0 166 /**
danielebarchiesi@0 167 * Sets this query to be DISTINCT.
danielebarchiesi@0 168 *
danielebarchiesi@0 169 * @param $distinct
danielebarchiesi@0 170 * TRUE to flag this query DISTINCT, FALSE to disable it.
danielebarchiesi@0 171 * @return SelectQueryInterface
danielebarchiesi@0 172 * The called object.
danielebarchiesi@0 173 */
danielebarchiesi@0 174 public function distinct($distinct = TRUE);
danielebarchiesi@0 175
danielebarchiesi@0 176 /**
danielebarchiesi@0 177 * Adds a field to the list to be SELECTed.
danielebarchiesi@0 178 *
danielebarchiesi@0 179 * @param $table_alias
danielebarchiesi@0 180 * The name of the table from which the field comes, as an alias. Generally
danielebarchiesi@0 181 * you will want to use the return value of join() here to ensure that it is
danielebarchiesi@0 182 * valid.
danielebarchiesi@0 183 * @param $field
danielebarchiesi@0 184 * The name of the field.
danielebarchiesi@0 185 * @param $alias
danielebarchiesi@0 186 * The alias for this field. If not specified, one will be generated
danielebarchiesi@0 187 * automatically based on the $table_alias and $field. The alias will be
danielebarchiesi@0 188 * checked for uniqueness, so the requested alias may not be the alias
danielebarchiesi@0 189 * that is assigned in all cases.
danielebarchiesi@0 190 * @return
danielebarchiesi@0 191 * The unique alias that was assigned for this field.
danielebarchiesi@0 192 */
danielebarchiesi@0 193 public function addField($table_alias, $field, $alias = NULL);
danielebarchiesi@0 194
danielebarchiesi@0 195 /**
danielebarchiesi@0 196 * Add multiple fields from the same table to be SELECTed.
danielebarchiesi@0 197 *
danielebarchiesi@0 198 * This method does not return the aliases set for the passed fields. In the
danielebarchiesi@0 199 * majority of cases that is not a problem, as the alias will be the field
danielebarchiesi@0 200 * name. However, if you do need to know the alias you can call getFields()
danielebarchiesi@0 201 * and examine the result to determine what alias was created. Alternatively,
danielebarchiesi@0 202 * simply use addField() for the few fields you care about and this method for
danielebarchiesi@0 203 * the rest.
danielebarchiesi@0 204 *
danielebarchiesi@0 205 * @param $table_alias
danielebarchiesi@0 206 * The name of the table from which the field comes, as an alias. Generally
danielebarchiesi@0 207 * you will want to use the return value of join() here to ensure that it is
danielebarchiesi@0 208 * valid.
danielebarchiesi@0 209 * @param $fields
danielebarchiesi@0 210 * An indexed array of fields present in the specified table that should be
danielebarchiesi@0 211 * included in this query. If not specified, $table_alias.* will be generated
danielebarchiesi@0 212 * without any aliases.
danielebarchiesi@0 213 * @return SelectQueryInterface
danielebarchiesi@0 214 * The called object.
danielebarchiesi@0 215 */
danielebarchiesi@0 216 public function fields($table_alias, array $fields = array());
danielebarchiesi@0 217
danielebarchiesi@0 218 /**
danielebarchiesi@0 219 * Adds an expression to the list of "fields" to be SELECTed.
danielebarchiesi@0 220 *
danielebarchiesi@0 221 * An expression can be any arbitrary string that is valid SQL. That includes
danielebarchiesi@0 222 * various functions, which may in some cases be database-dependent. This
danielebarchiesi@0 223 * method makes no effort to correct for database-specific functions.
danielebarchiesi@0 224 *
danielebarchiesi@0 225 * @param $expression
danielebarchiesi@0 226 * The expression string. May contain placeholders.
danielebarchiesi@0 227 * @param $alias
danielebarchiesi@0 228 * The alias for this expression. If not specified, one will be generated
danielebarchiesi@0 229 * automatically in the form "expression_#". The alias will be checked for
danielebarchiesi@0 230 * uniqueness, so the requested alias may not be the alias that is assigned
danielebarchiesi@0 231 * in all cases.
danielebarchiesi@0 232 * @param $arguments
danielebarchiesi@0 233 * Any placeholder arguments needed for this expression.
danielebarchiesi@0 234 * @return
danielebarchiesi@0 235 * The unique alias that was assigned for this expression.
danielebarchiesi@0 236 */
danielebarchiesi@0 237 public function addExpression($expression, $alias = NULL, $arguments = array());
danielebarchiesi@0 238
danielebarchiesi@0 239 /**
danielebarchiesi@0 240 * Default Join against another table in the database.
danielebarchiesi@0 241 *
danielebarchiesi@0 242 * This method is a convenience method for innerJoin().
danielebarchiesi@0 243 *
danielebarchiesi@0 244 * @param $table
danielebarchiesi@0 245 * The table against which to join.
danielebarchiesi@0 246 * @param $alias
danielebarchiesi@0 247 * The alias for the table. In most cases this should be the first letter
danielebarchiesi@0 248 * of the table, or the first letter of each "word" in the table.
danielebarchiesi@0 249 * @param $condition
danielebarchiesi@0 250 * The condition on which to join this table. If the join requires values,
danielebarchiesi@0 251 * this clause should use a named placeholder and the value or values to
danielebarchiesi@0 252 * insert should be passed in the 4th parameter. For the first table joined
danielebarchiesi@0 253 * on a query, this value is ignored as the first table is taken as the base
danielebarchiesi@0 254 * table. The token %alias can be used in this string to be replaced with
danielebarchiesi@0 255 * the actual alias. This is useful when $alias is modified by the database
danielebarchiesi@0 256 * system, for example, when joining the same table more than once.
danielebarchiesi@0 257 * @param $arguments
danielebarchiesi@0 258 * An array of arguments to replace into the $condition of this join.
danielebarchiesi@0 259 * @return
danielebarchiesi@0 260 * The unique alias that was assigned for this table.
danielebarchiesi@0 261 */
danielebarchiesi@0 262 public function join($table, $alias = NULL, $condition = NULL, $arguments = array());
danielebarchiesi@0 263
danielebarchiesi@0 264 /**
danielebarchiesi@0 265 * Inner Join against another table in the database.
danielebarchiesi@0 266 *
danielebarchiesi@0 267 * @param $table
danielebarchiesi@0 268 * The table against which to join.
danielebarchiesi@0 269 * @param $alias
danielebarchiesi@0 270 * The alias for the table. In most cases this should be the first letter
danielebarchiesi@0 271 * of the table, or the first letter of each "word" in the table.
danielebarchiesi@0 272 * @param $condition
danielebarchiesi@0 273 * The condition on which to join this table. If the join requires values,
danielebarchiesi@0 274 * this clause should use a named placeholder and the value or values to
danielebarchiesi@0 275 * insert should be passed in the 4th parameter. For the first table joined
danielebarchiesi@0 276 * on a query, this value is ignored as the first table is taken as the base
danielebarchiesi@0 277 * table. The token %alias can be used in this string to be replaced with
danielebarchiesi@0 278 * the actual alias. This is useful when $alias is modified by the database
danielebarchiesi@0 279 * system, for example, when joining the same table more than once.
danielebarchiesi@0 280 * @param $arguments
danielebarchiesi@0 281 * An array of arguments to replace into the $condition of this join.
danielebarchiesi@0 282 * @return
danielebarchiesi@0 283 * The unique alias that was assigned for this table.
danielebarchiesi@0 284 */
danielebarchiesi@0 285 public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
danielebarchiesi@0 286
danielebarchiesi@0 287 /**
danielebarchiesi@0 288 * Left Outer Join against another table in the database.
danielebarchiesi@0 289 *
danielebarchiesi@0 290 * @param $table
danielebarchiesi@0 291 * The table against which to join.
danielebarchiesi@0 292 * @param $alias
danielebarchiesi@0 293 * The alias for the table. In most cases this should be the first letter
danielebarchiesi@0 294 * of the table, or the first letter of each "word" in the table.
danielebarchiesi@0 295 * @param $condition
danielebarchiesi@0 296 * The condition on which to join this table. If the join requires values,
danielebarchiesi@0 297 * this clause should use a named placeholder and the value or values to
danielebarchiesi@0 298 * insert should be passed in the 4th parameter. For the first table joined
danielebarchiesi@0 299 * on a query, this value is ignored as the first table is taken as the base
danielebarchiesi@0 300 * table. The token %alias can be used in this string to be replaced with
danielebarchiesi@0 301 * the actual alias. This is useful when $alias is modified by the database
danielebarchiesi@0 302 * system, for example, when joining the same table more than once.
danielebarchiesi@0 303 * @param $arguments
danielebarchiesi@0 304 * An array of arguments to replace into the $condition of this join.
danielebarchiesi@0 305 * @return
danielebarchiesi@0 306 * The unique alias that was assigned for this table.
danielebarchiesi@0 307 */
danielebarchiesi@0 308 public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
danielebarchiesi@0 309
danielebarchiesi@0 310 /**
danielebarchiesi@0 311 * Right Outer Join against another table in the database.
danielebarchiesi@0 312 *
danielebarchiesi@0 313 * @param $table
danielebarchiesi@0 314 * The table against which to join.
danielebarchiesi@0 315 * @param $alias
danielebarchiesi@0 316 * The alias for the table. In most cases this should be the first letter
danielebarchiesi@0 317 * of the table, or the first letter of each "word" in the table.
danielebarchiesi@0 318 * @param $condition
danielebarchiesi@0 319 * The condition on which to join this table. If the join requires values,
danielebarchiesi@0 320 * this clause should use a named placeholder and the value or values to
danielebarchiesi@0 321 * insert should be passed in the 4th parameter. For the first table joined
danielebarchiesi@0 322 * on a query, this value is ignored as the first table is taken as the base
danielebarchiesi@0 323 * table. The token %alias can be used in this string to be replaced with
danielebarchiesi@0 324 * the actual alias. This is useful when $alias is modified by the database
danielebarchiesi@0 325 * system, for example, when joining the same table more than once.
danielebarchiesi@0 326 * @param $arguments
danielebarchiesi@0 327 * An array of arguments to replace into the $condition of this join.
danielebarchiesi@0 328 * @return
danielebarchiesi@0 329 * The unique alias that was assigned for this table.
danielebarchiesi@0 330 */
danielebarchiesi@0 331 public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array());
danielebarchiesi@0 332
danielebarchiesi@0 333 /**
danielebarchiesi@0 334 * Join against another table in the database.
danielebarchiesi@0 335 *
danielebarchiesi@0 336 * This method does the "hard" work of queuing up a table to be joined against.
danielebarchiesi@0 337 * In some cases, that may include dipping into the Schema API to find the necessary
danielebarchiesi@0 338 * fields on which to join.
danielebarchiesi@0 339 *
danielebarchiesi@0 340 * @param $type
danielebarchiesi@0 341 * The type of join. Typically one one of INNER, LEFT OUTER, and RIGHT OUTER.
danielebarchiesi@0 342 * @param $table
danielebarchiesi@0 343 * The table against which to join. May be a string or another SelectQuery
danielebarchiesi@0 344 * object. If a query object is passed, it will be used as a subselect.
danielebarchiesi@0 345 * @param $alias
danielebarchiesi@0 346 * The alias for the table. In most cases this should be the first letter
danielebarchiesi@0 347 * of the table, or the first letter of each "word" in the table. If omitted,
danielebarchiesi@0 348 * one will be dynamically generated.
danielebarchiesi@0 349 * @param $condition
danielebarchiesi@0 350 * The condition on which to join this table. If the join requires values,
danielebarchiesi@0 351 * this clause should use a named placeholder and the value or values to
danielebarchiesi@0 352 * insert should be passed in the 4th parameter. For the first table joined
danielebarchiesi@0 353 * on a query, this value is ignored as the first table is taken as the base
danielebarchiesi@0 354 * table. The token %alias can be used in this string to be replaced with
danielebarchiesi@0 355 * the actual alias. This is useful when $alias is modified by the database
danielebarchiesi@0 356 * system, for example, when joining the same table more than once.
danielebarchiesi@0 357 * @param $arguments
danielebarchiesi@0 358 * An array of arguments to replace into the $condition of this join.
danielebarchiesi@0 359 * @return
danielebarchiesi@0 360 * The unique alias that was assigned for this table.
danielebarchiesi@0 361 */
danielebarchiesi@0 362 public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array());
danielebarchiesi@0 363
danielebarchiesi@0 364 /**
danielebarchiesi@0 365 * Orders the result set by a given field.
danielebarchiesi@0 366 *
danielebarchiesi@0 367 * If called multiple times, the query will order by each specified field in the
danielebarchiesi@0 368 * order this method is called.
danielebarchiesi@0 369 *
danielebarchiesi@0 370 * If the query uses DISTINCT or GROUP BY conditions, fields or expressions
danielebarchiesi@0 371 * that are used for the order must be selected to be compatible with some
danielebarchiesi@0 372 * databases like PostgreSQL. The PostgreSQL driver can handle simple cases
danielebarchiesi@0 373 * automatically but it is suggested to explicitly specify them. Additionally,
danielebarchiesi@0 374 * when ordering on an alias, the alias must be added before orderBy() is
danielebarchiesi@0 375 * called.
danielebarchiesi@0 376 *
danielebarchiesi@0 377 * @param $field
danielebarchiesi@0 378 * The field on which to order.
danielebarchiesi@0 379 * @param $direction
danielebarchiesi@0 380 * The direction to sort. Legal values are "ASC" and "DESC".
danielebarchiesi@0 381 * @return SelectQueryInterface
danielebarchiesi@0 382 * The called object.
danielebarchiesi@0 383 */
danielebarchiesi@0 384 public function orderBy($field, $direction = 'ASC');
danielebarchiesi@0 385
danielebarchiesi@0 386 /**
danielebarchiesi@0 387 * Orders the result set by a random value.
danielebarchiesi@0 388 *
danielebarchiesi@0 389 * This may be stacked with other orderBy() calls. If so, the query will order
danielebarchiesi@0 390 * by each specified field, including this one, in the order called. Although
danielebarchiesi@0 391 * this method may be called multiple times on the same query, doing so
danielebarchiesi@0 392 * is not particularly useful.
danielebarchiesi@0 393 *
danielebarchiesi@0 394 * Note: The method used by most drivers may not scale to very large result
danielebarchiesi@0 395 * sets. If you need to work with extremely large data sets, you may create
danielebarchiesi@0 396 * your own database driver by subclassing off of an existing driver and
danielebarchiesi@0 397 * implementing your own randomization mechanism. See
danielebarchiesi@0 398 *
danielebarchiesi@0 399 * http://jan.kneschke.de/projects/mysql/order-by-rand/
danielebarchiesi@0 400 *
danielebarchiesi@0 401 * for an example of such an alternate sorting mechanism.
danielebarchiesi@0 402 *
danielebarchiesi@0 403 * @return SelectQueryInterface
danielebarchiesi@0 404 * The called object
danielebarchiesi@0 405 */
danielebarchiesi@0 406 public function orderRandom();
danielebarchiesi@0 407
danielebarchiesi@0 408 /**
danielebarchiesi@0 409 * Restricts a query to a given range in the result set.
danielebarchiesi@0 410 *
danielebarchiesi@0 411 * If this method is called with no parameters, will remove any range
danielebarchiesi@0 412 * directives that have been set.
danielebarchiesi@0 413 *
danielebarchiesi@0 414 * @param $start
danielebarchiesi@0 415 * The first record from the result set to return. If NULL, removes any
danielebarchiesi@0 416 * range directives that are set.
danielebarchiesi@0 417 * @param $length
danielebarchiesi@0 418 * The number of records to return from the result set.
danielebarchiesi@0 419 * @return SelectQueryInterface
danielebarchiesi@0 420 * The called object.
danielebarchiesi@0 421 */
danielebarchiesi@0 422 public function range($start = NULL, $length = NULL);
danielebarchiesi@0 423
danielebarchiesi@0 424 /**
danielebarchiesi@0 425 * Add another Select query to UNION to this one.
danielebarchiesi@0 426 *
danielebarchiesi@0 427 * Union queries consist of two or more queries whose
danielebarchiesi@0 428 * results are effectively concatenated together. Queries
danielebarchiesi@0 429 * will be UNIONed in the order they are specified, with
danielebarchiesi@0 430 * this object's query coming first. Duplicate columns will
danielebarchiesi@0 431 * be discarded. All forms of UNION are supported, using
danielebarchiesi@0 432 * the second '$type' argument.
danielebarchiesi@0 433 *
danielebarchiesi@0 434 * Note: All queries UNIONed together must have the same
danielebarchiesi@0 435 * field structure, in the same order. It is up to the
danielebarchiesi@0 436 * caller to ensure that they match properly. If they do
danielebarchiesi@0 437 * not, an SQL syntax error will result.
danielebarchiesi@0 438 *
danielebarchiesi@0 439 * @param $query
danielebarchiesi@0 440 * The query to UNION to this query.
danielebarchiesi@0 441 * @param $type
danielebarchiesi@0 442 * The type of UNION to add to the query. Defaults to plain
danielebarchiesi@0 443 * UNION.
danielebarchiesi@0 444 * @return SelectQueryInterface
danielebarchiesi@0 445 * The called object.
danielebarchiesi@0 446 */
danielebarchiesi@0 447 public function union(SelectQueryInterface $query, $type = '');
danielebarchiesi@0 448
danielebarchiesi@0 449 /**
danielebarchiesi@0 450 * Groups the result set by the specified field.
danielebarchiesi@0 451 *
danielebarchiesi@0 452 * @param $field
danielebarchiesi@0 453 * The field on which to group. This should be the field as aliased.
danielebarchiesi@0 454 * @return SelectQueryInterface
danielebarchiesi@0 455 * The called object.
danielebarchiesi@0 456 */
danielebarchiesi@0 457 public function groupBy($field);
danielebarchiesi@0 458
danielebarchiesi@0 459 /**
danielebarchiesi@0 460 * Get the equivalent COUNT query of this query as a new query object.
danielebarchiesi@0 461 *
danielebarchiesi@0 462 * @return SelectQueryInterface
danielebarchiesi@0 463 * A new SelectQuery object with no fields or expressions besides COUNT(*).
danielebarchiesi@0 464 */
danielebarchiesi@0 465 public function countQuery();
danielebarchiesi@0 466
danielebarchiesi@0 467 /**
danielebarchiesi@0 468 * Indicates if preExecute() has already been called on that object.
danielebarchiesi@0 469 *
danielebarchiesi@0 470 * @return
danielebarchiesi@0 471 * TRUE is this query has already been prepared, FALSE otherwise.
danielebarchiesi@0 472 */
danielebarchiesi@0 473 public function isPrepared();
danielebarchiesi@0 474
danielebarchiesi@0 475 /**
danielebarchiesi@0 476 * Generic preparation and validation for a SELECT query.
danielebarchiesi@0 477 *
danielebarchiesi@0 478 * @return
danielebarchiesi@0 479 * TRUE if the validation was successful, FALSE if not.
danielebarchiesi@0 480 */
danielebarchiesi@0 481 public function preExecute(SelectQueryInterface $query = NULL);
danielebarchiesi@0 482
danielebarchiesi@0 483 /**
danielebarchiesi@0 484 * Helper function to build most common HAVING conditional clauses.
danielebarchiesi@0 485 *
danielebarchiesi@0 486 * This method can take a variable number of parameters. If called with two
danielebarchiesi@0 487 * parameters, they are taken as $field and $value with $operator having a value
danielebarchiesi@0 488 * of IN if $value is an array and = otherwise.
danielebarchiesi@0 489 *
danielebarchiesi@0 490 * @param $field
danielebarchiesi@0 491 * The name of the field to check. If you would like to add a more complex
danielebarchiesi@0 492 * condition involving operators or functions, use having().
danielebarchiesi@0 493 * @param $value
danielebarchiesi@0 494 * The value to test the field against. In most cases, this is a scalar. For more
danielebarchiesi@0 495 * complex options, it is an array. The meaning of each element in the array is
danielebarchiesi@0 496 * dependent on the $operator.
danielebarchiesi@0 497 * @param $operator
danielebarchiesi@0 498 * The comparison operator, such as =, <, or >=. It also accepts more complex
danielebarchiesi@0 499 * options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is an array
danielebarchiesi@0 500 * = otherwise.
danielebarchiesi@0 501 * @return QueryConditionInterface
danielebarchiesi@0 502 * The called object.
danielebarchiesi@0 503 */
danielebarchiesi@0 504 public function havingCondition($field, $value = NULL, $operator = NULL);
danielebarchiesi@0 505
danielebarchiesi@0 506 /**
danielebarchiesi@0 507 * Clone magic method.
danielebarchiesi@0 508 *
danielebarchiesi@0 509 * Select queries have dependent objects that must be deep-cloned. The
danielebarchiesi@0 510 * connection object itself, however, should not be cloned as that would
danielebarchiesi@0 511 * duplicate the connection itself.
danielebarchiesi@0 512 */
danielebarchiesi@0 513 public function __clone();
danielebarchiesi@0 514
danielebarchiesi@0 515 /**
danielebarchiesi@0 516 * Add FOR UPDATE to the query.
danielebarchiesi@0 517 *
danielebarchiesi@0 518 * FOR UPDATE prevents the rows retrieved by the SELECT statement from being
danielebarchiesi@0 519 * modified or deleted by other transactions until the current transaction
danielebarchiesi@0 520 * ends. Other transactions that attempt UPDATE, DELETE, or SELECT FOR UPDATE
danielebarchiesi@0 521 * of these rows will be blocked until the current transaction ends.
danielebarchiesi@0 522 *
danielebarchiesi@0 523 * @param $set
danielebarchiesi@0 524 * IF TRUE, FOR UPDATE will be added to the query, if FALSE then it won't.
danielebarchiesi@0 525 *
danielebarchiesi@0 526 * @return QueryConditionInterface
danielebarchiesi@0 527 * The called object.
danielebarchiesi@0 528 */
danielebarchiesi@0 529 public function forUpdate($set = TRUE);
danielebarchiesi@0 530 }
danielebarchiesi@0 531
danielebarchiesi@0 532 /**
danielebarchiesi@0 533 * The base extender class for Select queries.
danielebarchiesi@0 534 */
danielebarchiesi@0 535 class SelectQueryExtender implements SelectQueryInterface {
danielebarchiesi@0 536
danielebarchiesi@0 537 /**
danielebarchiesi@0 538 * The SelectQuery object we are extending/decorating.
danielebarchiesi@0 539 *
danielebarchiesi@0 540 * @var SelectQueryInterface
danielebarchiesi@0 541 */
danielebarchiesi@0 542 protected $query;
danielebarchiesi@0 543
danielebarchiesi@0 544 /**
danielebarchiesi@0 545 * The connection object on which to run this query.
danielebarchiesi@0 546 *
danielebarchiesi@0 547 * @var DatabaseConnection
danielebarchiesi@0 548 */
danielebarchiesi@0 549 protected $connection;
danielebarchiesi@0 550
danielebarchiesi@0 551 /**
danielebarchiesi@0 552 * A unique identifier for this query object.
danielebarchiesi@0 553 */
danielebarchiesi@0 554 protected $uniqueIdentifier;
danielebarchiesi@0 555
danielebarchiesi@0 556 /**
danielebarchiesi@0 557 * The placeholder counter.
danielebarchiesi@0 558 */
danielebarchiesi@0 559 protected $placeholder = 0;
danielebarchiesi@0 560
danielebarchiesi@0 561 public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
danielebarchiesi@0 562 $this->uniqueIdentifier = uniqid('', TRUE);
danielebarchiesi@0 563 $this->query = $query;
danielebarchiesi@0 564 $this->connection = $connection;
danielebarchiesi@0 565 }
danielebarchiesi@0 566
danielebarchiesi@0 567 /**
danielebarchiesi@0 568 * Implements QueryPlaceholderInterface::uniqueIdentifier().
danielebarchiesi@0 569 */
danielebarchiesi@0 570 public function uniqueIdentifier() {
danielebarchiesi@0 571 return $this->uniqueIdentifier;
danielebarchiesi@0 572 }
danielebarchiesi@0 573
danielebarchiesi@0 574 /**
danielebarchiesi@0 575 * Implements QueryPlaceholderInterface::nextPlaceholder().
danielebarchiesi@0 576 */
danielebarchiesi@0 577 public function nextPlaceholder() {
danielebarchiesi@0 578 return $this->placeholder++;
danielebarchiesi@0 579 }
danielebarchiesi@0 580
danielebarchiesi@0 581 /* Implementations of QueryAlterableInterface. */
danielebarchiesi@0 582
danielebarchiesi@0 583 public function addTag($tag) {
danielebarchiesi@0 584 $this->query->addTag($tag);
danielebarchiesi@0 585 return $this;
danielebarchiesi@0 586 }
danielebarchiesi@0 587
danielebarchiesi@0 588 public function hasTag($tag) {
danielebarchiesi@0 589 return $this->query->hasTag($tag);
danielebarchiesi@0 590 }
danielebarchiesi@0 591
danielebarchiesi@0 592 public function hasAllTags() {
danielebarchiesi@0 593 $args = func_get_args();
danielebarchiesi@0 594 return call_user_func_array(array($this->query, 'hasAllTags'), $args);
danielebarchiesi@0 595 }
danielebarchiesi@0 596
danielebarchiesi@0 597 public function hasAnyTag() {
danielebarchiesi@0 598 $args = func_get_args();
danielebarchiesi@0 599 return call_user_func_array(array($this->query, 'hasAnyTags'), $args);
danielebarchiesi@0 600 }
danielebarchiesi@0 601
danielebarchiesi@0 602 public function addMetaData($key, $object) {
danielebarchiesi@0 603 $this->query->addMetaData($key, $object);
danielebarchiesi@0 604 return $this;
danielebarchiesi@0 605 }
danielebarchiesi@0 606
danielebarchiesi@0 607 public function getMetaData($key) {
danielebarchiesi@0 608 return $this->query->getMetaData($key);
danielebarchiesi@0 609 }
danielebarchiesi@0 610
danielebarchiesi@0 611 /* Implementations of QueryConditionInterface for the WHERE clause. */
danielebarchiesi@0 612
danielebarchiesi@0 613 public function condition($field, $value = NULL, $operator = NULL) {
danielebarchiesi@0 614 $this->query->condition($field, $value, $operator);
danielebarchiesi@0 615 return $this;
danielebarchiesi@0 616 }
danielebarchiesi@0 617
danielebarchiesi@0 618 public function &conditions() {
danielebarchiesi@0 619 return $this->query->conditions();
danielebarchiesi@0 620 }
danielebarchiesi@0 621
danielebarchiesi@0 622 public function arguments() {
danielebarchiesi@0 623 return $this->query->arguments();
danielebarchiesi@0 624 }
danielebarchiesi@0 625
danielebarchiesi@0 626 public function where($snippet, $args = array()) {
danielebarchiesi@0 627 $this->query->where($snippet, $args);
danielebarchiesi@0 628 return $this;
danielebarchiesi@0 629 }
danielebarchiesi@0 630
danielebarchiesi@0 631 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
danielebarchiesi@0 632 return $this->query->compile($connection, $queryPlaceholder);
danielebarchiesi@0 633 }
danielebarchiesi@0 634
danielebarchiesi@0 635 public function compiled() {
danielebarchiesi@0 636 return $this->query->compiled();
danielebarchiesi@0 637 }
danielebarchiesi@0 638
danielebarchiesi@0 639 /* Implementations of QueryConditionInterface for the HAVING clause. */
danielebarchiesi@0 640
danielebarchiesi@0 641 public function havingCondition($field, $value = NULL, $operator = '=') {
danielebarchiesi@0 642 $this->query->havingCondition($field, $value, $operator);
danielebarchiesi@0 643 return $this;
danielebarchiesi@0 644 }
danielebarchiesi@0 645
danielebarchiesi@0 646 public function &havingConditions() {
danielebarchiesi@0 647 return $this->query->havingConditions();
danielebarchiesi@0 648 }
danielebarchiesi@0 649
danielebarchiesi@0 650 public function havingArguments() {
danielebarchiesi@0 651 return $this->query->havingArguments();
danielebarchiesi@0 652 }
danielebarchiesi@0 653
danielebarchiesi@0 654 public function having($snippet, $args = array()) {
danielebarchiesi@0 655 $this->query->having($snippet, $args);
danielebarchiesi@0 656 return $this;
danielebarchiesi@0 657 }
danielebarchiesi@0 658
danielebarchiesi@0 659 public function havingCompile(DatabaseConnection $connection) {
danielebarchiesi@0 660 return $this->query->havingCompile($connection);
danielebarchiesi@0 661 }
danielebarchiesi@0 662
danielebarchiesi@0 663 /* Implementations of QueryExtendableInterface. */
danielebarchiesi@0 664
danielebarchiesi@0 665 public function extend($extender_name) {
danielebarchiesi@0 666 // The extender can be anywhere so this needs to go to the registry, which
danielebarchiesi@0 667 // is surely loaded by now.
danielebarchiesi@0 668 $class = $this->connection->getDriverClass($extender_name, array(), TRUE);
danielebarchiesi@0 669 return new $class($this, $this->connection);
danielebarchiesi@0 670 }
danielebarchiesi@0 671
danielebarchiesi@0 672 /* Alter accessors to expose the query data to alter hooks. */
danielebarchiesi@0 673
danielebarchiesi@0 674 public function &getFields() {
danielebarchiesi@0 675 return $this->query->getFields();
danielebarchiesi@0 676 }
danielebarchiesi@0 677
danielebarchiesi@0 678 public function &getExpressions() {
danielebarchiesi@0 679 return $this->query->getExpressions();
danielebarchiesi@0 680 }
danielebarchiesi@0 681
danielebarchiesi@0 682 public function &getOrderBy() {
danielebarchiesi@0 683 return $this->query->getOrderBy();
danielebarchiesi@0 684 }
danielebarchiesi@0 685
danielebarchiesi@0 686 public function &getGroupBy() {
danielebarchiesi@0 687 return $this->query->getGroupBy();
danielebarchiesi@0 688 }
danielebarchiesi@0 689
danielebarchiesi@0 690 public function &getTables() {
danielebarchiesi@0 691 return $this->query->getTables();
danielebarchiesi@0 692 }
danielebarchiesi@0 693
danielebarchiesi@0 694 public function &getUnion() {
danielebarchiesi@0 695 return $this->query->getUnion();
danielebarchiesi@0 696 }
danielebarchiesi@0 697
danielebarchiesi@0 698 public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
danielebarchiesi@0 699 return $this->query->getArguments($queryPlaceholder);
danielebarchiesi@0 700 }
danielebarchiesi@0 701
danielebarchiesi@0 702 public function isPrepared() {
danielebarchiesi@0 703 return $this->query->isPrepared();
danielebarchiesi@0 704 }
danielebarchiesi@0 705
danielebarchiesi@0 706 public function preExecute(SelectQueryInterface $query = NULL) {
danielebarchiesi@0 707 // If no query object is passed in, use $this.
danielebarchiesi@0 708 if (!isset($query)) {
danielebarchiesi@0 709 $query = $this;
danielebarchiesi@0 710 }
danielebarchiesi@0 711
danielebarchiesi@0 712 return $this->query->preExecute($query);
danielebarchiesi@0 713 }
danielebarchiesi@0 714
danielebarchiesi@0 715 public function execute() {
danielebarchiesi@0 716 // By calling preExecute() here, we force it to preprocess the extender
danielebarchiesi@0 717 // object rather than just the base query object. That means
danielebarchiesi@0 718 // hook_query_alter() gets access to the extended object.
danielebarchiesi@0 719 if (!$this->preExecute($this)) {
danielebarchiesi@0 720 return NULL;
danielebarchiesi@0 721 }
danielebarchiesi@0 722
danielebarchiesi@0 723 return $this->query->execute();
danielebarchiesi@0 724 }
danielebarchiesi@0 725
danielebarchiesi@0 726 public function distinct($distinct = TRUE) {
danielebarchiesi@0 727 $this->query->distinct($distinct);
danielebarchiesi@0 728 return $this;
danielebarchiesi@0 729 }
danielebarchiesi@0 730
danielebarchiesi@0 731 public function addField($table_alias, $field, $alias = NULL) {
danielebarchiesi@0 732 return $this->query->addField($table_alias, $field, $alias);
danielebarchiesi@0 733 }
danielebarchiesi@0 734
danielebarchiesi@0 735 public function fields($table_alias, array $fields = array()) {
danielebarchiesi@0 736 $this->query->fields($table_alias, $fields);
danielebarchiesi@0 737 return $this;
danielebarchiesi@0 738 }
danielebarchiesi@0 739
danielebarchiesi@0 740 public function addExpression($expression, $alias = NULL, $arguments = array()) {
danielebarchiesi@0 741 return $this->query->addExpression($expression, $alias, $arguments);
danielebarchiesi@0 742 }
danielebarchiesi@0 743
danielebarchiesi@0 744 public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 745 return $this->query->join($table, $alias, $condition, $arguments);
danielebarchiesi@0 746 }
danielebarchiesi@0 747
danielebarchiesi@0 748 public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 749 return $this->query->innerJoin($table, $alias, $condition, $arguments);
danielebarchiesi@0 750 }
danielebarchiesi@0 751
danielebarchiesi@0 752 public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 753 return $this->query->leftJoin($table, $alias, $condition, $arguments);
danielebarchiesi@0 754 }
danielebarchiesi@0 755
danielebarchiesi@0 756 public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 757 return $this->query->rightJoin($table, $alias, $condition, $arguments);
danielebarchiesi@0 758 }
danielebarchiesi@0 759
danielebarchiesi@0 760 public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 761 return $this->query->addJoin($type, $table, $alias, $condition, $arguments);
danielebarchiesi@0 762 }
danielebarchiesi@0 763
danielebarchiesi@0 764 public function orderBy($field, $direction = 'ASC') {
danielebarchiesi@0 765 $this->query->orderBy($field, $direction);
danielebarchiesi@0 766 return $this;
danielebarchiesi@0 767 }
danielebarchiesi@0 768
danielebarchiesi@0 769 public function orderRandom() {
danielebarchiesi@0 770 $this->query->orderRandom();
danielebarchiesi@0 771 return $this;
danielebarchiesi@0 772 }
danielebarchiesi@0 773
danielebarchiesi@0 774 public function range($start = NULL, $length = NULL) {
danielebarchiesi@0 775 $this->query->range($start, $length);
danielebarchiesi@0 776 return $this;
danielebarchiesi@0 777 }
danielebarchiesi@0 778
danielebarchiesi@0 779 public function union(SelectQueryInterface $query, $type = '') {
danielebarchiesi@0 780 $this->query->union($query, $type);
danielebarchiesi@0 781 return $this;
danielebarchiesi@0 782 }
danielebarchiesi@0 783
danielebarchiesi@0 784 public function groupBy($field) {
danielebarchiesi@0 785 $this->query->groupBy($field);
danielebarchiesi@0 786 return $this;
danielebarchiesi@0 787 }
danielebarchiesi@0 788
danielebarchiesi@0 789 public function forUpdate($set = TRUE) {
danielebarchiesi@0 790 $this->query->forUpdate($set);
danielebarchiesi@0 791 return $this;
danielebarchiesi@0 792 }
danielebarchiesi@0 793
danielebarchiesi@0 794 public function countQuery() {
danielebarchiesi@0 795 return $this->query->countQuery();
danielebarchiesi@0 796 }
danielebarchiesi@0 797
danielebarchiesi@0 798 function isNull($field) {
danielebarchiesi@0 799 $this->query->isNull($field);
danielebarchiesi@0 800 return $this;
danielebarchiesi@0 801 }
danielebarchiesi@0 802
danielebarchiesi@0 803 function isNotNull($field) {
danielebarchiesi@0 804 $this->query->isNotNull($field);
danielebarchiesi@0 805 return $this;
danielebarchiesi@0 806 }
danielebarchiesi@0 807
danielebarchiesi@0 808 public function exists(SelectQueryInterface $select) {
danielebarchiesi@0 809 $this->query->exists($select);
danielebarchiesi@0 810 return $this;
danielebarchiesi@0 811 }
danielebarchiesi@0 812
danielebarchiesi@0 813 public function notExists(SelectQueryInterface $select) {
danielebarchiesi@0 814 $this->query->notExists($select);
danielebarchiesi@0 815 return $this;
danielebarchiesi@0 816 }
danielebarchiesi@0 817
danielebarchiesi@0 818 public function __toString() {
danielebarchiesi@0 819 return (string) $this->query;
danielebarchiesi@0 820 }
danielebarchiesi@0 821
danielebarchiesi@0 822 public function __clone() {
danielebarchiesi@0 823 $this->uniqueIdentifier = uniqid('', TRUE);
danielebarchiesi@0 824
danielebarchiesi@0 825 // We need to deep-clone the query we're wrapping, which in turn may
danielebarchiesi@0 826 // deep-clone other objects. Exciting!
danielebarchiesi@0 827 $this->query = clone($this->query);
danielebarchiesi@0 828 }
danielebarchiesi@0 829
danielebarchiesi@0 830 /**
danielebarchiesi@0 831 * Magic override for undefined methods.
danielebarchiesi@0 832 *
danielebarchiesi@0 833 * If one extender extends another extender, then methods in the inner extender
danielebarchiesi@0 834 * will not be exposed on the outer extender. That's because we cannot know
danielebarchiesi@0 835 * in advance what those methods will be, so we cannot provide wrapping
danielebarchiesi@0 836 * implementations as we do above. Instead, we use this slower catch-all method
danielebarchiesi@0 837 * to handle any additional methods.
danielebarchiesi@0 838 */
danielebarchiesi@0 839 public function __call($method, $args) {
danielebarchiesi@0 840 $return = call_user_func_array(array($this->query, $method), $args);
danielebarchiesi@0 841
danielebarchiesi@0 842 // Some methods will return the called object as part of a fluent interface.
danielebarchiesi@0 843 // Others will return some useful value. If it's a value, then the caller
danielebarchiesi@0 844 // probably wants that value. If it's the called object, then we instead
danielebarchiesi@0 845 // return this object. That way we don't "lose" an extender layer when
danielebarchiesi@0 846 // chaining methods together.
danielebarchiesi@0 847 if ($return instanceof SelectQueryInterface) {
danielebarchiesi@0 848 return $this;
danielebarchiesi@0 849 }
danielebarchiesi@0 850 else {
danielebarchiesi@0 851 return $return;
danielebarchiesi@0 852 }
danielebarchiesi@0 853 }
danielebarchiesi@0 854 }
danielebarchiesi@0 855
danielebarchiesi@0 856 /**
danielebarchiesi@0 857 * Query builder for SELECT statements.
danielebarchiesi@0 858 */
danielebarchiesi@0 859 class SelectQuery extends Query implements SelectQueryInterface {
danielebarchiesi@0 860
danielebarchiesi@0 861 /**
danielebarchiesi@0 862 * The fields to SELECT.
danielebarchiesi@0 863 *
danielebarchiesi@0 864 * @var array
danielebarchiesi@0 865 */
danielebarchiesi@0 866 protected $fields = array();
danielebarchiesi@0 867
danielebarchiesi@0 868 /**
danielebarchiesi@0 869 * The expressions to SELECT as virtual fields.
danielebarchiesi@0 870 *
danielebarchiesi@0 871 * @var array
danielebarchiesi@0 872 */
danielebarchiesi@0 873 protected $expressions = array();
danielebarchiesi@0 874
danielebarchiesi@0 875 /**
danielebarchiesi@0 876 * The tables against which to JOIN.
danielebarchiesi@0 877 *
danielebarchiesi@0 878 * This property is a nested array. Each entry is an array representing
danielebarchiesi@0 879 * a single table against which to join. The structure of each entry is:
danielebarchiesi@0 880 *
danielebarchiesi@0 881 * array(
danielebarchiesi@0 882 * 'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
danielebarchiesi@0 883 * 'table' => $table,
danielebarchiesi@0 884 * 'alias' => $alias_of_the_table,
danielebarchiesi@0 885 * 'condition' => $condition_clause_on_which_to_join,
danielebarchiesi@0 886 * 'arguments' => $array_of_arguments_for_placeholders_in_the condition.
danielebarchiesi@0 887 * 'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
danielebarchiesi@0 888 * )
danielebarchiesi@0 889 *
danielebarchiesi@0 890 * If $table is a string, it is taken as the name of a table. If it is
danielebarchiesi@0 891 * a SelectQuery object, it is taken as a subquery.
danielebarchiesi@0 892 *
danielebarchiesi@0 893 * @var array
danielebarchiesi@0 894 */
danielebarchiesi@0 895 protected $tables = array();
danielebarchiesi@0 896
danielebarchiesi@0 897 /**
danielebarchiesi@0 898 * The fields by which to order this query.
danielebarchiesi@0 899 *
danielebarchiesi@0 900 * This is an associative array. The keys are the fields to order, and the value
danielebarchiesi@0 901 * is the direction to order, either ASC or DESC.
danielebarchiesi@0 902 *
danielebarchiesi@0 903 * @var array
danielebarchiesi@0 904 */
danielebarchiesi@0 905 protected $order = array();
danielebarchiesi@0 906
danielebarchiesi@0 907 /**
danielebarchiesi@0 908 * The fields by which to group.
danielebarchiesi@0 909 *
danielebarchiesi@0 910 * @var array
danielebarchiesi@0 911 */
danielebarchiesi@0 912 protected $group = array();
danielebarchiesi@0 913
danielebarchiesi@0 914 /**
danielebarchiesi@0 915 * The conditional object for the WHERE clause.
danielebarchiesi@0 916 *
danielebarchiesi@0 917 * @var DatabaseCondition
danielebarchiesi@0 918 */
danielebarchiesi@0 919 protected $where;
danielebarchiesi@0 920
danielebarchiesi@0 921 /**
danielebarchiesi@0 922 * The conditional object for the HAVING clause.
danielebarchiesi@0 923 *
danielebarchiesi@0 924 * @var DatabaseCondition
danielebarchiesi@0 925 */
danielebarchiesi@0 926 protected $having;
danielebarchiesi@0 927
danielebarchiesi@0 928 /**
danielebarchiesi@0 929 * Whether or not this query should be DISTINCT
danielebarchiesi@0 930 *
danielebarchiesi@0 931 * @var boolean
danielebarchiesi@0 932 */
danielebarchiesi@0 933 protected $distinct = FALSE;
danielebarchiesi@0 934
danielebarchiesi@0 935 /**
danielebarchiesi@0 936 * The range limiters for this query.
danielebarchiesi@0 937 *
danielebarchiesi@0 938 * @var array
danielebarchiesi@0 939 */
danielebarchiesi@0 940 protected $range;
danielebarchiesi@0 941
danielebarchiesi@0 942 /**
danielebarchiesi@0 943 * An array whose elements specify a query to UNION, and the UNION type. The
danielebarchiesi@0 944 * 'type' key may be '', 'ALL', or 'DISTINCT' to represent a 'UNION',
danielebarchiesi@0 945 * 'UNION ALL', or 'UNION DISTINCT' statement, respectively.
danielebarchiesi@0 946 *
danielebarchiesi@0 947 * All entries in this array will be applied from front to back, with the
danielebarchiesi@0 948 * first query to union on the right of the original query, the second union
danielebarchiesi@0 949 * to the right of the first, etc.
danielebarchiesi@0 950 *
danielebarchiesi@0 951 * @var array
danielebarchiesi@0 952 */
danielebarchiesi@0 953 protected $union = array();
danielebarchiesi@0 954
danielebarchiesi@0 955 /**
danielebarchiesi@0 956 * Indicates if preExecute() has already been called.
danielebarchiesi@0 957 * @var boolean
danielebarchiesi@0 958 */
danielebarchiesi@0 959 protected $prepared = FALSE;
danielebarchiesi@0 960
danielebarchiesi@0 961 /**
danielebarchiesi@0 962 * The FOR UPDATE status
danielebarchiesi@0 963 */
danielebarchiesi@0 964 protected $forUpdate = FALSE;
danielebarchiesi@0 965
danielebarchiesi@0 966 public function __construct($table, $alias = NULL, DatabaseConnection $connection, $options = array()) {
danielebarchiesi@0 967 $options['return'] = Database::RETURN_STATEMENT;
danielebarchiesi@0 968 parent::__construct($connection, $options);
danielebarchiesi@0 969 $this->where = new DatabaseCondition('AND');
danielebarchiesi@0 970 $this->having = new DatabaseCondition('AND');
danielebarchiesi@0 971 $this->addJoin(NULL, $table, $alias);
danielebarchiesi@0 972 }
danielebarchiesi@0 973
danielebarchiesi@0 974 /* Implementations of QueryAlterableInterface. */
danielebarchiesi@0 975
danielebarchiesi@0 976 public function addTag($tag) {
danielebarchiesi@0 977 $this->alterTags[$tag] = 1;
danielebarchiesi@0 978 return $this;
danielebarchiesi@0 979 }
danielebarchiesi@0 980
danielebarchiesi@0 981 public function hasTag($tag) {
danielebarchiesi@0 982 return isset($this->alterTags[$tag]);
danielebarchiesi@0 983 }
danielebarchiesi@0 984
danielebarchiesi@0 985 public function hasAllTags() {
danielebarchiesi@0 986 $args = func_get_args();
danielebarchiesi@0 987 return !(boolean)array_diff($args, array_keys($this->alterTags));
danielebarchiesi@0 988 }
danielebarchiesi@0 989
danielebarchiesi@0 990 public function hasAnyTag() {
danielebarchiesi@0 991 $args = func_get_args();
danielebarchiesi@0 992 return (boolean)array_intersect($args, array_keys($this->alterTags));
danielebarchiesi@0 993 }
danielebarchiesi@0 994
danielebarchiesi@0 995 public function addMetaData($key, $object) {
danielebarchiesi@0 996 $this->alterMetaData[$key] = $object;
danielebarchiesi@0 997 return $this;
danielebarchiesi@0 998 }
danielebarchiesi@0 999
danielebarchiesi@0 1000 public function getMetaData($key) {
danielebarchiesi@0 1001 return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
danielebarchiesi@0 1002 }
danielebarchiesi@0 1003
danielebarchiesi@0 1004 /* Implementations of QueryConditionInterface for the WHERE clause. */
danielebarchiesi@0 1005
danielebarchiesi@0 1006 public function condition($field, $value = NULL, $operator = NULL) {
danielebarchiesi@0 1007 $this->where->condition($field, $value, $operator);
danielebarchiesi@0 1008 return $this;
danielebarchiesi@0 1009 }
danielebarchiesi@0 1010
danielebarchiesi@0 1011 public function &conditions() {
danielebarchiesi@0 1012 return $this->where->conditions();
danielebarchiesi@0 1013 }
danielebarchiesi@0 1014
danielebarchiesi@0 1015 public function arguments() {
danielebarchiesi@0 1016 if (!$this->compiled()) {
danielebarchiesi@0 1017 return NULL;
danielebarchiesi@0 1018 }
danielebarchiesi@0 1019
danielebarchiesi@0 1020 $args = $this->where->arguments() + $this->having->arguments();
danielebarchiesi@0 1021
danielebarchiesi@0 1022 foreach ($this->tables as $table) {
danielebarchiesi@0 1023 if ($table['arguments']) {
danielebarchiesi@0 1024 $args += $table['arguments'];
danielebarchiesi@0 1025 }
danielebarchiesi@0 1026 // If this table is a subquery, grab its arguments recursively.
danielebarchiesi@0 1027 if ($table['table'] instanceof SelectQueryInterface) {
danielebarchiesi@0 1028 $args += $table['table']->arguments();
danielebarchiesi@0 1029 }
danielebarchiesi@0 1030 }
danielebarchiesi@0 1031
danielebarchiesi@0 1032 foreach ($this->expressions as $expression) {
danielebarchiesi@0 1033 if ($expression['arguments']) {
danielebarchiesi@0 1034 $args += $expression['arguments'];
danielebarchiesi@0 1035 }
danielebarchiesi@0 1036 }
danielebarchiesi@0 1037
danielebarchiesi@0 1038 // If there are any dependent queries to UNION,
danielebarchiesi@0 1039 // incorporate their arguments recursively.
danielebarchiesi@0 1040 foreach ($this->union as $union) {
danielebarchiesi@0 1041 $args += $union['query']->arguments();
danielebarchiesi@0 1042 }
danielebarchiesi@0 1043
danielebarchiesi@0 1044 return $args;
danielebarchiesi@0 1045 }
danielebarchiesi@0 1046
danielebarchiesi@0 1047 public function where($snippet, $args = array()) {
danielebarchiesi@0 1048 $this->where->where($snippet, $args);
danielebarchiesi@0 1049 return $this;
danielebarchiesi@0 1050 }
danielebarchiesi@0 1051
danielebarchiesi@0 1052 public function isNull($field) {
danielebarchiesi@0 1053 $this->where->isNull($field);
danielebarchiesi@0 1054 return $this;
danielebarchiesi@0 1055 }
danielebarchiesi@0 1056
danielebarchiesi@0 1057 public function isNotNull($field) {
danielebarchiesi@0 1058 $this->where->isNotNull($field);
danielebarchiesi@0 1059 return $this;
danielebarchiesi@0 1060 }
danielebarchiesi@0 1061
danielebarchiesi@0 1062 public function exists(SelectQueryInterface $select) {
danielebarchiesi@0 1063 $this->where->exists($select);
danielebarchiesi@0 1064 return $this;
danielebarchiesi@0 1065 }
danielebarchiesi@0 1066
danielebarchiesi@0 1067 public function notExists(SelectQueryInterface $select) {
danielebarchiesi@0 1068 $this->where->notExists($select);
danielebarchiesi@0 1069 return $this;
danielebarchiesi@0 1070 }
danielebarchiesi@0 1071
danielebarchiesi@0 1072 public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
danielebarchiesi@0 1073 $this->where->compile($connection, $queryPlaceholder);
danielebarchiesi@0 1074 $this->having->compile($connection, $queryPlaceholder);
danielebarchiesi@0 1075
danielebarchiesi@0 1076 foreach ($this->tables as $table) {
danielebarchiesi@0 1077 // If this table is a subquery, compile it recursively.
danielebarchiesi@0 1078 if ($table['table'] instanceof SelectQueryInterface) {
danielebarchiesi@0 1079 $table['table']->compile($connection, $queryPlaceholder);
danielebarchiesi@0 1080 }
danielebarchiesi@0 1081 }
danielebarchiesi@0 1082
danielebarchiesi@0 1083 // If there are any dependent queries to UNION, compile it recursively.
danielebarchiesi@0 1084 foreach ($this->union as $union) {
danielebarchiesi@0 1085 $union['query']->compile($connection, $queryPlaceholder);
danielebarchiesi@0 1086 }
danielebarchiesi@0 1087 }
danielebarchiesi@0 1088
danielebarchiesi@0 1089 public function compiled() {
danielebarchiesi@0 1090 if (!$this->where->compiled() || !$this->having->compiled()) {
danielebarchiesi@0 1091 return FALSE;
danielebarchiesi@0 1092 }
danielebarchiesi@0 1093
danielebarchiesi@0 1094 foreach ($this->tables as $table) {
danielebarchiesi@0 1095 // If this table is a subquery, check its status recursively.
danielebarchiesi@0 1096 if ($table['table'] instanceof SelectQueryInterface) {
danielebarchiesi@0 1097 if (!$table['table']->compiled()) {
danielebarchiesi@0 1098 return FALSE;
danielebarchiesi@0 1099 }
danielebarchiesi@0 1100 }
danielebarchiesi@0 1101 }
danielebarchiesi@0 1102
danielebarchiesi@0 1103 foreach ($this->union as $union) {
danielebarchiesi@0 1104 if (!$union['query']->compiled()) {
danielebarchiesi@0 1105 return FALSE;
danielebarchiesi@0 1106 }
danielebarchiesi@0 1107 }
danielebarchiesi@0 1108
danielebarchiesi@0 1109 return TRUE;
danielebarchiesi@0 1110 }
danielebarchiesi@0 1111
danielebarchiesi@0 1112 /* Implementations of QueryConditionInterface for the HAVING clause. */
danielebarchiesi@0 1113
danielebarchiesi@0 1114 public function havingCondition($field, $value = NULL, $operator = NULL) {
danielebarchiesi@0 1115 $this->having->condition($field, $value, $operator);
danielebarchiesi@0 1116 return $this;
danielebarchiesi@0 1117 }
danielebarchiesi@0 1118
danielebarchiesi@0 1119 public function &havingConditions() {
danielebarchiesi@0 1120 return $this->having->conditions();
danielebarchiesi@0 1121 }
danielebarchiesi@0 1122
danielebarchiesi@0 1123 public function havingArguments() {
danielebarchiesi@0 1124 return $this->having->arguments();
danielebarchiesi@0 1125 }
danielebarchiesi@0 1126
danielebarchiesi@0 1127 public function having($snippet, $args = array()) {
danielebarchiesi@0 1128 $this->having->where($snippet, $args);
danielebarchiesi@0 1129 return $this;
danielebarchiesi@0 1130 }
danielebarchiesi@0 1131
danielebarchiesi@0 1132 public function havingCompile(DatabaseConnection $connection) {
danielebarchiesi@0 1133 return $this->having->compile($connection, $this);
danielebarchiesi@0 1134 }
danielebarchiesi@0 1135
danielebarchiesi@0 1136 /* Implementations of QueryExtendableInterface. */
danielebarchiesi@0 1137
danielebarchiesi@0 1138 public function extend($extender_name) {
danielebarchiesi@0 1139 $override_class = $extender_name . '_' . $this->connection->driver();
danielebarchiesi@0 1140 if (class_exists($override_class)) {
danielebarchiesi@0 1141 $extender_name = $override_class;
danielebarchiesi@0 1142 }
danielebarchiesi@0 1143 return new $extender_name($this, $this->connection);
danielebarchiesi@0 1144 }
danielebarchiesi@0 1145
danielebarchiesi@0 1146 public function havingIsNull($field) {
danielebarchiesi@0 1147 $this->having->isNull($field);
danielebarchiesi@0 1148 return $this;
danielebarchiesi@0 1149 }
danielebarchiesi@0 1150
danielebarchiesi@0 1151 public function havingIsNotNull($field) {
danielebarchiesi@0 1152 $this->having->isNotNull($field);
danielebarchiesi@0 1153 return $this;
danielebarchiesi@0 1154 }
danielebarchiesi@0 1155
danielebarchiesi@0 1156 public function havingExists(SelectQueryInterface $select) {
danielebarchiesi@0 1157 $this->having->exists($select);
danielebarchiesi@0 1158 return $this;
danielebarchiesi@0 1159 }
danielebarchiesi@0 1160
danielebarchiesi@0 1161 public function havingNotExists(SelectQueryInterface $select) {
danielebarchiesi@0 1162 $this->having->notExists($select);
danielebarchiesi@0 1163 return $this;
danielebarchiesi@0 1164 }
danielebarchiesi@0 1165
danielebarchiesi@0 1166 public function forUpdate($set = TRUE) {
danielebarchiesi@0 1167 if (isset($set)) {
danielebarchiesi@0 1168 $this->forUpdate = $set;
danielebarchiesi@0 1169 }
danielebarchiesi@0 1170 return $this;
danielebarchiesi@0 1171 }
danielebarchiesi@0 1172
danielebarchiesi@0 1173 /* Alter accessors to expose the query data to alter hooks. */
danielebarchiesi@0 1174
danielebarchiesi@0 1175 public function &getFields() {
danielebarchiesi@0 1176 return $this->fields;
danielebarchiesi@0 1177 }
danielebarchiesi@0 1178
danielebarchiesi@0 1179 public function &getExpressions() {
danielebarchiesi@0 1180 return $this->expressions;
danielebarchiesi@0 1181 }
danielebarchiesi@0 1182
danielebarchiesi@0 1183 public function &getOrderBy() {
danielebarchiesi@0 1184 return $this->order;
danielebarchiesi@0 1185 }
danielebarchiesi@0 1186
danielebarchiesi@0 1187 public function &getGroupBy() {
danielebarchiesi@0 1188 return $this->group;
danielebarchiesi@0 1189 }
danielebarchiesi@0 1190
danielebarchiesi@0 1191 public function &getTables() {
danielebarchiesi@0 1192 return $this->tables;
danielebarchiesi@0 1193 }
danielebarchiesi@0 1194
danielebarchiesi@0 1195 public function &getUnion() {
danielebarchiesi@0 1196 return $this->union;
danielebarchiesi@0 1197 }
danielebarchiesi@0 1198
danielebarchiesi@0 1199 public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
danielebarchiesi@0 1200 if (!isset($queryPlaceholder)) {
danielebarchiesi@0 1201 $queryPlaceholder = $this;
danielebarchiesi@0 1202 }
danielebarchiesi@0 1203 $this->compile($this->connection, $queryPlaceholder);
danielebarchiesi@0 1204 return $this->arguments();
danielebarchiesi@0 1205 }
danielebarchiesi@0 1206
danielebarchiesi@0 1207 /**
danielebarchiesi@0 1208 * Indicates if preExecute() has already been called on that object.
danielebarchiesi@0 1209 */
danielebarchiesi@0 1210 public function isPrepared() {
danielebarchiesi@0 1211 return $this->prepared;
danielebarchiesi@0 1212 }
danielebarchiesi@0 1213
danielebarchiesi@0 1214 /**
danielebarchiesi@0 1215 * Generic preparation and validation for a SELECT query.
danielebarchiesi@0 1216 *
danielebarchiesi@0 1217 * @return
danielebarchiesi@0 1218 * TRUE if the validation was successful, FALSE if not.
danielebarchiesi@0 1219 */
danielebarchiesi@0 1220 public function preExecute(SelectQueryInterface $query = NULL) {
danielebarchiesi@0 1221 // If no query object is passed in, use $this.
danielebarchiesi@0 1222 if (!isset($query)) {
danielebarchiesi@0 1223 $query = $this;
danielebarchiesi@0 1224 }
danielebarchiesi@0 1225
danielebarchiesi@0 1226 // Only execute this once.
danielebarchiesi@0 1227 if ($query->isPrepared()) {
danielebarchiesi@0 1228 return TRUE;
danielebarchiesi@0 1229 }
danielebarchiesi@0 1230
danielebarchiesi@0 1231 // Modules may alter all queries or only those having a particular tag.
danielebarchiesi@0 1232 if (isset($this->alterTags)) {
danielebarchiesi@0 1233 $hooks = array('query');
danielebarchiesi@0 1234 foreach ($this->alterTags as $tag => $value) {
danielebarchiesi@0 1235 $hooks[] = 'query_' . $tag;
danielebarchiesi@0 1236 }
danielebarchiesi@0 1237 drupal_alter($hooks, $query);
danielebarchiesi@0 1238 }
danielebarchiesi@0 1239
danielebarchiesi@0 1240 $this->prepared = TRUE;
danielebarchiesi@0 1241
danielebarchiesi@0 1242 // Now also prepare any sub-queries.
danielebarchiesi@0 1243 foreach ($this->tables as $table) {
danielebarchiesi@0 1244 if ($table['table'] instanceof SelectQueryInterface) {
danielebarchiesi@0 1245 $table['table']->preExecute();
danielebarchiesi@0 1246 }
danielebarchiesi@0 1247 }
danielebarchiesi@0 1248
danielebarchiesi@0 1249 foreach ($this->union as $union) {
danielebarchiesi@0 1250 $union['query']->preExecute();
danielebarchiesi@0 1251 }
danielebarchiesi@0 1252
danielebarchiesi@0 1253 return $this->prepared;
danielebarchiesi@0 1254 }
danielebarchiesi@0 1255
danielebarchiesi@0 1256 public function execute() {
danielebarchiesi@0 1257 // If validation fails, simply return NULL.
danielebarchiesi@0 1258 // Note that validation routines in preExecute() may throw exceptions instead.
danielebarchiesi@0 1259 if (!$this->preExecute()) {
danielebarchiesi@0 1260 return NULL;
danielebarchiesi@0 1261 }
danielebarchiesi@0 1262
danielebarchiesi@0 1263 $args = $this->getArguments();
danielebarchiesi@0 1264 return $this->connection->query((string) $this, $args, $this->queryOptions);
danielebarchiesi@0 1265 }
danielebarchiesi@0 1266
danielebarchiesi@0 1267 public function distinct($distinct = TRUE) {
danielebarchiesi@0 1268 $this->distinct = $distinct;
danielebarchiesi@0 1269 return $this;
danielebarchiesi@0 1270 }
danielebarchiesi@0 1271
danielebarchiesi@0 1272 public function addField($table_alias, $field, $alias = NULL) {
danielebarchiesi@0 1273 // If no alias is specified, first try the field name itself.
danielebarchiesi@0 1274 if (empty($alias)) {
danielebarchiesi@0 1275 $alias = $field;
danielebarchiesi@0 1276 }
danielebarchiesi@0 1277
danielebarchiesi@0 1278 // If that's already in use, try the table name and field name.
danielebarchiesi@0 1279 if (!empty($this->fields[$alias])) {
danielebarchiesi@0 1280 $alias = $table_alias . '_' . $field;
danielebarchiesi@0 1281 }
danielebarchiesi@0 1282
danielebarchiesi@0 1283 // If that is already used, just add a counter until we find an unused alias.
danielebarchiesi@0 1284 $alias_candidate = $alias;
danielebarchiesi@0 1285 $count = 2;
danielebarchiesi@0 1286 while (!empty($this->fields[$alias_candidate])) {
danielebarchiesi@0 1287 $alias_candidate = $alias . '_' . $count++;
danielebarchiesi@0 1288 }
danielebarchiesi@0 1289 $alias = $alias_candidate;
danielebarchiesi@0 1290
danielebarchiesi@0 1291 $this->fields[$alias] = array(
danielebarchiesi@0 1292 'field' => $field,
danielebarchiesi@0 1293 'table' => $table_alias,
danielebarchiesi@0 1294 'alias' => $alias,
danielebarchiesi@0 1295 );
danielebarchiesi@0 1296
danielebarchiesi@0 1297 return $alias;
danielebarchiesi@0 1298 }
danielebarchiesi@0 1299
danielebarchiesi@0 1300 public function fields($table_alias, array $fields = array()) {
danielebarchiesi@0 1301
danielebarchiesi@0 1302 if ($fields) {
danielebarchiesi@0 1303 foreach ($fields as $field) {
danielebarchiesi@0 1304 // We don't care what alias was assigned.
danielebarchiesi@0 1305 $this->addField($table_alias, $field);
danielebarchiesi@0 1306 }
danielebarchiesi@0 1307 }
danielebarchiesi@0 1308 else {
danielebarchiesi@0 1309 // We want all fields from this table.
danielebarchiesi@0 1310 $this->tables[$table_alias]['all_fields'] = TRUE;
danielebarchiesi@0 1311 }
danielebarchiesi@0 1312
danielebarchiesi@0 1313 return $this;
danielebarchiesi@0 1314 }
danielebarchiesi@0 1315
danielebarchiesi@0 1316 public function addExpression($expression, $alias = NULL, $arguments = array()) {
danielebarchiesi@0 1317 if (empty($alias)) {
danielebarchiesi@0 1318 $alias = 'expression';
danielebarchiesi@0 1319 }
danielebarchiesi@0 1320
danielebarchiesi@0 1321 $alias_candidate = $alias;
danielebarchiesi@0 1322 $count = 2;
danielebarchiesi@0 1323 while (!empty($this->expressions[$alias_candidate])) {
danielebarchiesi@0 1324 $alias_candidate = $alias . '_' . $count++;
danielebarchiesi@0 1325 }
danielebarchiesi@0 1326 $alias = $alias_candidate;
danielebarchiesi@0 1327
danielebarchiesi@0 1328 $this->expressions[$alias] = array(
danielebarchiesi@0 1329 'expression' => $expression,
danielebarchiesi@0 1330 'alias' => $alias,
danielebarchiesi@0 1331 'arguments' => $arguments,
danielebarchiesi@0 1332 );
danielebarchiesi@0 1333
danielebarchiesi@0 1334 return $alias;
danielebarchiesi@0 1335 }
danielebarchiesi@0 1336
danielebarchiesi@0 1337 public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 1338 return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
danielebarchiesi@0 1339 }
danielebarchiesi@0 1340
danielebarchiesi@0 1341 public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 1342 return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
danielebarchiesi@0 1343 }
danielebarchiesi@0 1344
danielebarchiesi@0 1345 public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 1346 return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
danielebarchiesi@0 1347 }
danielebarchiesi@0 1348
danielebarchiesi@0 1349 public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 1350 return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
danielebarchiesi@0 1351 }
danielebarchiesi@0 1352
danielebarchiesi@0 1353 public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
danielebarchiesi@0 1354
danielebarchiesi@0 1355 if (empty($alias)) {
danielebarchiesi@0 1356 if ($table instanceof SelectQueryInterface) {
danielebarchiesi@0 1357 $alias = 'subquery';
danielebarchiesi@0 1358 }
danielebarchiesi@0 1359 else {
danielebarchiesi@0 1360 $alias = $table;
danielebarchiesi@0 1361 }
danielebarchiesi@0 1362 }
danielebarchiesi@0 1363
danielebarchiesi@0 1364 $alias_candidate = $alias;
danielebarchiesi@0 1365 $count = 2;
danielebarchiesi@0 1366 while (!empty($this->tables[$alias_candidate])) {
danielebarchiesi@0 1367 $alias_candidate = $alias . '_' . $count++;
danielebarchiesi@0 1368 }
danielebarchiesi@0 1369 $alias = $alias_candidate;
danielebarchiesi@0 1370
danielebarchiesi@0 1371 if (is_string($condition)) {
danielebarchiesi@0 1372 $condition = str_replace('%alias', $alias, $condition);
danielebarchiesi@0 1373 }
danielebarchiesi@0 1374
danielebarchiesi@0 1375 $this->tables[$alias] = array(
danielebarchiesi@0 1376 'join type' => $type,
danielebarchiesi@0 1377 'table' => $table,
danielebarchiesi@0 1378 'alias' => $alias,
danielebarchiesi@0 1379 'condition' => $condition,
danielebarchiesi@0 1380 'arguments' => $arguments,
danielebarchiesi@0 1381 );
danielebarchiesi@0 1382
danielebarchiesi@0 1383 return $alias;
danielebarchiesi@0 1384 }
danielebarchiesi@0 1385
danielebarchiesi@0 1386 public function orderBy($field, $direction = 'ASC') {
danielebarchiesi@0 1387 $this->order[$field] = $direction;
danielebarchiesi@0 1388 return $this;
danielebarchiesi@0 1389 }
danielebarchiesi@0 1390
danielebarchiesi@0 1391 public function orderRandom() {
danielebarchiesi@0 1392 $alias = $this->addExpression('RAND()', 'random_field');
danielebarchiesi@0 1393 $this->orderBy($alias);
danielebarchiesi@0 1394 return $this;
danielebarchiesi@0 1395 }
danielebarchiesi@0 1396
danielebarchiesi@0 1397 public function range($start = NULL, $length = NULL) {
danielebarchiesi@0 1398 $this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array();
danielebarchiesi@0 1399 return $this;
danielebarchiesi@0 1400 }
danielebarchiesi@0 1401
danielebarchiesi@0 1402 public function union(SelectQueryInterface $query, $type = '') {
danielebarchiesi@0 1403 // Handle UNION aliasing.
danielebarchiesi@0 1404 switch ($type) {
danielebarchiesi@0 1405 // Fold UNION DISTINCT to UNION for better cross database support.
danielebarchiesi@0 1406 case 'DISTINCT':
danielebarchiesi@0 1407 case '':
danielebarchiesi@0 1408 $type = 'UNION';
danielebarchiesi@0 1409 break;
danielebarchiesi@0 1410
danielebarchiesi@0 1411 case 'ALL':
danielebarchiesi@0 1412 $type = 'UNION ALL';
danielebarchiesi@0 1413 default:
danielebarchiesi@0 1414 }
danielebarchiesi@0 1415
danielebarchiesi@0 1416 $this->union[] = array(
danielebarchiesi@0 1417 'type' => $type,
danielebarchiesi@0 1418 'query' => $query,
danielebarchiesi@0 1419 );
danielebarchiesi@0 1420
danielebarchiesi@0 1421 return $this;
danielebarchiesi@0 1422 }
danielebarchiesi@0 1423
danielebarchiesi@0 1424 public function groupBy($field) {
danielebarchiesi@0 1425 $this->group[$field] = $field;
danielebarchiesi@0 1426 return $this;
danielebarchiesi@0 1427 }
danielebarchiesi@0 1428
danielebarchiesi@0 1429 public function countQuery() {
danielebarchiesi@0 1430 // Create our new query object that we will mutate into a count query.
danielebarchiesi@0 1431 $count = clone($this);
danielebarchiesi@0 1432
danielebarchiesi@0 1433 $group_by = $count->getGroupBy();
danielebarchiesi@0 1434 $having = $count->havingConditions();
danielebarchiesi@0 1435
danielebarchiesi@0 1436 if (!$count->distinct && !isset($having[0])) {
danielebarchiesi@0 1437 // When not executing a distinct query, we can zero-out existing fields
danielebarchiesi@0 1438 // and expressions that are not used by a GROUP BY or HAVING. Fields
danielebarchiesi@0 1439 // listed in a GROUP BY or HAVING clause need to be present in the
danielebarchiesi@0 1440 // query.
danielebarchiesi@0 1441 $fields =& $count->getFields();
danielebarchiesi@0 1442 foreach (array_keys($fields) as $field) {
danielebarchiesi@0 1443 if (empty($group_by[$field])) {
danielebarchiesi@0 1444 unset($fields[$field]);
danielebarchiesi@0 1445 }
danielebarchiesi@0 1446 }
danielebarchiesi@0 1447
danielebarchiesi@0 1448 $expressions =& $count->getExpressions();
danielebarchiesi@0 1449 foreach (array_keys($expressions) as $field) {
danielebarchiesi@0 1450 if (empty($group_by[$field])) {
danielebarchiesi@0 1451 unset($expressions[$field]);
danielebarchiesi@0 1452 }
danielebarchiesi@0 1453 }
danielebarchiesi@0 1454
danielebarchiesi@0 1455 // Also remove 'all_fields' statements, which are expanded into tablename.*
danielebarchiesi@0 1456 // when the query is executed.
danielebarchiesi@0 1457 foreach ($count->tables as $alias => &$table) {
danielebarchiesi@0 1458 unset($table['all_fields']);
danielebarchiesi@0 1459 }
danielebarchiesi@0 1460 }
danielebarchiesi@0 1461
danielebarchiesi@0 1462 // If we've just removed all fields from the query, make sure there is at
danielebarchiesi@0 1463 // least one so that the query still runs.
danielebarchiesi@0 1464 $count->addExpression('1');
danielebarchiesi@0 1465
danielebarchiesi@0 1466 // Ordering a count query is a waste of cycles, and breaks on some
danielebarchiesi@0 1467 // databases anyway.
danielebarchiesi@0 1468 $orders = &$count->getOrderBy();
danielebarchiesi@0 1469 $orders = array();
danielebarchiesi@0 1470
danielebarchiesi@0 1471 if ($count->distinct && !empty($group_by)) {
danielebarchiesi@0 1472 // If the query is distinct and contains a GROUP BY, we need to remove the
danielebarchiesi@0 1473 // distinct because SQL99 does not support counting on distinct multiple fields.
danielebarchiesi@0 1474 $count->distinct = FALSE;
danielebarchiesi@0 1475 }
danielebarchiesi@0 1476
danielebarchiesi@0 1477 $query = $this->connection->select($count);
danielebarchiesi@0 1478 $query->addExpression('COUNT(*)');
danielebarchiesi@0 1479
danielebarchiesi@0 1480 return $query;
danielebarchiesi@0 1481 }
danielebarchiesi@0 1482
danielebarchiesi@0 1483 public function __toString() {
danielebarchiesi@0 1484 // For convenience, we compile the query ourselves if the caller forgot
danielebarchiesi@0 1485 // to do it. This allows constructs like "(string) $query" to work. When
danielebarchiesi@0 1486 // the query will be executed, it will be recompiled using the proper
danielebarchiesi@0 1487 // placeholder generator anyway.
danielebarchiesi@0 1488 if (!$this->compiled()) {
danielebarchiesi@0 1489 $this->compile($this->connection, $this);
danielebarchiesi@0 1490 }
danielebarchiesi@0 1491
danielebarchiesi@0 1492 // Create a sanitized comment string to prepend to the query.
danielebarchiesi@0 1493 $comments = $this->connection->makeComment($this->comments);
danielebarchiesi@0 1494
danielebarchiesi@0 1495 // SELECT
danielebarchiesi@0 1496 $query = $comments . 'SELECT ';
danielebarchiesi@0 1497 if ($this->distinct) {
danielebarchiesi@0 1498 $query .= 'DISTINCT ';
danielebarchiesi@0 1499 }
danielebarchiesi@0 1500
danielebarchiesi@0 1501 // FIELDS and EXPRESSIONS
danielebarchiesi@0 1502 $fields = array();
danielebarchiesi@0 1503 foreach ($this->tables as $alias => $table) {
danielebarchiesi@0 1504 if (!empty($table['all_fields'])) {
danielebarchiesi@0 1505 $fields[] = $this->connection->escapeTable($alias) . '.*';
danielebarchiesi@0 1506 }
danielebarchiesi@0 1507 }
danielebarchiesi@0 1508 foreach ($this->fields as $alias => $field) {
danielebarchiesi@0 1509 // Always use the AS keyword for field aliases, as some
danielebarchiesi@0 1510 // databases require it (e.g., PostgreSQL).
danielebarchiesi@0 1511 $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
danielebarchiesi@0 1512 }
danielebarchiesi@0 1513 foreach ($this->expressions as $alias => $expression) {
danielebarchiesi@0 1514 $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
danielebarchiesi@0 1515 }
danielebarchiesi@0 1516 $query .= implode(', ', $fields);
danielebarchiesi@0 1517
danielebarchiesi@0 1518
danielebarchiesi@0 1519 // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
danielebarchiesi@0 1520 $query .= "\nFROM ";
danielebarchiesi@0 1521 foreach ($this->tables as $alias => $table) {
danielebarchiesi@0 1522 $query .= "\n";
danielebarchiesi@0 1523 if (isset($table['join type'])) {
danielebarchiesi@0 1524 $query .= $table['join type'] . ' JOIN ';
danielebarchiesi@0 1525 }
danielebarchiesi@0 1526
danielebarchiesi@0 1527 // If the table is a subquery, compile it and integrate it into this query.
danielebarchiesi@0 1528 if ($table['table'] instanceof SelectQueryInterface) {
danielebarchiesi@0 1529 // Run preparation steps on this sub-query before converting to string.
danielebarchiesi@0 1530 $subquery = $table['table'];
danielebarchiesi@0 1531 $subquery->preExecute();
danielebarchiesi@0 1532 $table_string = '(' . (string) $subquery . ')';
danielebarchiesi@0 1533 }
danielebarchiesi@0 1534 else {
danielebarchiesi@0 1535 $table_string = '{' . $this->connection->escapeTable($table['table']) . '}';
danielebarchiesi@0 1536 }
danielebarchiesi@0 1537
danielebarchiesi@0 1538 // Don't use the AS keyword for table aliases, as some
danielebarchiesi@0 1539 // databases don't support it (e.g., Oracle).
danielebarchiesi@0 1540 $query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']);
danielebarchiesi@0 1541
danielebarchiesi@0 1542 if (!empty($table['condition'])) {
danielebarchiesi@0 1543 $query .= ' ON ' . $table['condition'];
danielebarchiesi@0 1544 }
danielebarchiesi@0 1545 }
danielebarchiesi@0 1546
danielebarchiesi@0 1547 // WHERE
danielebarchiesi@0 1548 if (count($this->where)) {
danielebarchiesi@0 1549 // There is an implicit string cast on $this->condition.
danielebarchiesi@0 1550 $query .= "\nWHERE " . $this->where;
danielebarchiesi@0 1551 }
danielebarchiesi@0 1552
danielebarchiesi@0 1553 // GROUP BY
danielebarchiesi@0 1554 if ($this->group) {
danielebarchiesi@0 1555 $query .= "\nGROUP BY " . implode(', ', $this->group);
danielebarchiesi@0 1556 }
danielebarchiesi@0 1557
danielebarchiesi@0 1558 // HAVING
danielebarchiesi@0 1559 if (count($this->having)) {
danielebarchiesi@0 1560 // There is an implicit string cast on $this->having.
danielebarchiesi@0 1561 $query .= "\nHAVING " . $this->having;
danielebarchiesi@0 1562 }
danielebarchiesi@0 1563
danielebarchiesi@0 1564 // ORDER BY
danielebarchiesi@0 1565 if ($this->order) {
danielebarchiesi@0 1566 $query .= "\nORDER BY ";
danielebarchiesi@0 1567 $fields = array();
danielebarchiesi@0 1568 foreach ($this->order as $field => $direction) {
danielebarchiesi@0 1569 $fields[] = $field . ' ' . $direction;
danielebarchiesi@0 1570 }
danielebarchiesi@0 1571 $query .= implode(', ', $fields);
danielebarchiesi@0 1572 }
danielebarchiesi@0 1573
danielebarchiesi@0 1574 // RANGE
danielebarchiesi@0 1575 // There is no universal SQL standard for handling range or limit clauses.
danielebarchiesi@0 1576 // Fortunately, all core-supported databases use the same range syntax.
danielebarchiesi@0 1577 // Databases that need a different syntax can override this method and
danielebarchiesi@0 1578 // do whatever alternate logic they need to.
danielebarchiesi@0 1579 if (!empty($this->range)) {
danielebarchiesi@0 1580 $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
danielebarchiesi@0 1581 }
danielebarchiesi@0 1582
danielebarchiesi@0 1583 // UNION is a little odd, as the select queries to combine are passed into
danielebarchiesi@0 1584 // this query, but syntactically they all end up on the same level.
danielebarchiesi@0 1585 if ($this->union) {
danielebarchiesi@0 1586 foreach ($this->union as $union) {
danielebarchiesi@0 1587 $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
danielebarchiesi@0 1588 }
danielebarchiesi@0 1589 }
danielebarchiesi@0 1590
danielebarchiesi@0 1591 if ($this->forUpdate) {
danielebarchiesi@0 1592 $query .= ' FOR UPDATE';
danielebarchiesi@0 1593 }
danielebarchiesi@0 1594
danielebarchiesi@0 1595 return $query;
danielebarchiesi@0 1596 }
danielebarchiesi@0 1597
danielebarchiesi@0 1598 public function __clone() {
danielebarchiesi@0 1599 // On cloning, also clone the dependent objects. However, we do not
danielebarchiesi@0 1600 // want to clone the database connection object as that would duplicate the
danielebarchiesi@0 1601 // connection itself.
danielebarchiesi@0 1602
danielebarchiesi@0 1603 $this->where = clone($this->where);
danielebarchiesi@0 1604 $this->having = clone($this->having);
danielebarchiesi@0 1605 foreach ($this->union as $key => $aggregate) {
danielebarchiesi@0 1606 $this->union[$key]['query'] = clone($aggregate['query']);
danielebarchiesi@0 1607 }
danielebarchiesi@0 1608 }
danielebarchiesi@0 1609 }
danielebarchiesi@0 1610
danielebarchiesi@0 1611 /**
danielebarchiesi@0 1612 * @} End of "addtogroup database".
danielebarchiesi@0 1613 */