Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
35.29% |
18 / 51 |
CRAP | |
74.13% |
192 / 259 |
| Select | |
0.00% |
0 / 1 |
|
35.29% |
18 / 51 |
417.07 | |
74.13% |
192 / 259 |
| __construct | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
| addTag | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| hasTag | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| hasAllTags | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| hasAnyTag | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| addMetaData | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| getMetaData | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 1 |
|||
| arguments | |
0.00% |
0 / 1 |
8.19 | |
85.71% |
12 / 14 |
|||
| compile | |
0.00% |
0 / 1 |
4.03 | |
87.50% |
7 / 8 |
|||
| compiled | |
0.00% |
0 / 1 |
8.51 | |
80.00% |
8 / 10 |
|||
| havingCondition | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| havingConditions | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| havingArguments | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| having | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| havingCompile | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| extend | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| havingIsNull | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| havingIsNotNull | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| havingExists | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| havingNotExists | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| forUpdate | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
| getFields | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getExpressions | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getOrderBy | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getGroupBy | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getTables | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getUnion | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| escapeLike | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| escapeField | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getArguments | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| isPrepared | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| preExecute | |
0.00% |
0 / 1 |
8.02 | |
93.75% |
15 / 16 |
|||
| execute | |
0.00% |
0 / 1 |
2.06 | |
75.00% |
3 / 4 |
|||
| distinct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| addField | |
0.00% |
0 / 1 |
4.05 | |
85.71% |
12 / 14 |
|||
| fields | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| addExpression | |
0.00% |
0 / 1 |
3.01 | |
91.67% |
11 / 12 |
|||
| join | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| innerJoin | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| leftJoin | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| rightJoin | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| addJoin | |
0.00% |
0 / 1 |
5.00 | |
94.44% |
17 / 18 |
|||
| orderBy | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
| orderRandom | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| range | |
100.00% |
1 / 1 |
2 | |
100.00% |
2 / 2 |
|||
| union | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 10 |
|||
| groupBy | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| countQuery | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
| prepareCountQuery | |
0.00% |
0 / 1 |
11.73 | |
81.82% |
18 / 22 |
|||
| __toString | |
0.00% |
0 / 1 |
22.48 | |
90.00% |
45 / 50 |
|||
| __clone | |
0.00% |
0 / 1 |
2.03 | |
80.00% |
4 / 5 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Database\Query\Select. | |
| */ | |
| namespace Drupal\Core\Database\Query; | |
| use Drupal\Core\Database\Database; | |
| use Drupal\Core\Database\Connection; | |
| /** | |
| * Query builder for SELECT statements. | |
| * | |
| * @ingroup database | |
| */ | |
| class Select extends Query implements SelectInterface { | |
| use QueryConditionTrait; | |
| /** | |
| * The fields to SELECT. | |
| * | |
| * @var array | |
| */ | |
| protected $fields = array(); | |
| /** | |
| * The expressions to SELECT as virtual fields. | |
| * | |
| * @var array | |
| */ | |
| protected $expressions = array(); | |
| /** | |
| * The tables against which to JOIN. | |
| * | |
| * This property is a nested array. Each entry is an array representing | |
| * a single table against which to join. The structure of each entry is: | |
| * | |
| * array( | |
| * 'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER), | |
| * 'table' => $table, | |
| * 'alias' => $alias_of_the_table, | |
| * 'condition' => $condition_clause_on_which_to_join, | |
| * 'arguments' => $array_of_arguments_for_placeholders_in_the condition. | |
| * 'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise. | |
| * ) | |
| * | |
| * If $table is a string, it is taken as the name of a table. If it is | |
| * a Select query object, it is taken as a subquery. | |
| * | |
| * @var array | |
| */ | |
| protected $tables = array(); | |
| /** | |
| * The fields by which to order this query. | |
| * | |
| * This is an associative array. The keys are the fields to order, and the value | |
| * is the direction to order, either ASC or DESC. | |
| * | |
| * @var array | |
| */ | |
| protected $order = array(); | |
| /** | |
| * The fields by which to group. | |
| * | |
| * @var array | |
| */ | |
| protected $group = array(); | |
| /** | |
| * The conditional object for the HAVING clause. | |
| * | |
| * @var \Drupal\Core\Database\Query\Condition | |
| */ | |
| protected $having; | |
| /** | |
| * Whether or not this query should be DISTINCT | |
| * | |
| * @var bool | |
| */ | |
| protected $distinct = FALSE; | |
| /** | |
| * The range limiters for this query. | |
| * | |
| * @var array | |
| */ | |
| protected $range; | |
| /** | |
| * An array whose elements specify a query to UNION, and the UNION type. The | |
| * 'type' key may be '', 'ALL', or 'DISTINCT' to represent a 'UNION', | |
| * 'UNION ALL', or 'UNION DISTINCT' statement, respectively. | |
| * | |
| * All entries in this array will be applied from front to back, with the | |
| * first query to union on the right of the original query, the second union | |
| * to the right of the first, etc. | |
| * | |
| * @var array | |
| */ | |
| protected $union = array(); | |
| /** | |
| * Indicates if preExecute() has already been called. | |
| * @var bool | |
| */ | |
| protected $prepared = FALSE; | |
| /** | |
| * The FOR UPDATE status | |
| */ | |
| protected $forUpdate = FALSE; | |
| /** | |
| * Constructs a Select object. | |
| * | |
| * @param string $table | |
| * The name of the table that is being queried. | |
| * @param string $alias | |
| * The alias for the table. | |
| * @param \Drupal\Core\Database\Connection $connection | |
| * Database connection object. | |
| * @param array $options | |
| * Array of query options. | |
| */ | |
| public function __construct($table, $alias = NULL, Connection $connection, $options = array()) { | |
| $options['return'] = Database::RETURN_STATEMENT; | |
| parent::__construct($connection, $options); | |
| $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND'; | |
| $this->condition = new Condition($conjunction); | |
| $this->having = new Condition($conjunction); | |
| $this->addJoin(NULL, $table, $alias); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addTag($tag) { | |
| $this->alterTags[$tag] = 1; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function hasTag($tag) { | |
| return isset($this->alterTags[$tag]); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function hasAllTags() { | |
| return !(boolean)array_diff(func_get_args(), array_keys($this->alterTags)); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function hasAnyTag() { | |
| return (boolean)array_intersect(func_get_args(), array_keys($this->alterTags)); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addMetaData($key, $object) { | |
| $this->alterMetaData[$key] = $object; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getMetaData($key) { | |
| return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function arguments() { | |
| if (!$this->compiled()) { | |
| return NULL; | |
| } | |
| $args = $this->condition->arguments() + $this->having->arguments(); | |
| foreach ($this->tables as $table) { | |
| if ($table['arguments']) { | |
| $args += $table['arguments']; | |
| } | |
| // If this table is a subquery, grab its arguments recursively. | |
| if ($table['table'] instanceof SelectInterface) { | |
| $args += $table['table']->arguments(); | |
| } | |
| } | |
| foreach ($this->expressions as $expression) { | |
| if ($expression['arguments']) { | |
| $args += $expression['arguments']; | |
| } | |
| } | |
| // If there are any dependent queries to UNION, | |
| // incorporate their arguments recursively. | |
| foreach ($this->union as $union) { | |
| $args += $union['query']->arguments(); | |
| } | |
| return $args; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) { | |
| $this->condition->compile($connection, $queryPlaceholder); | |
| $this->having->compile($connection, $queryPlaceholder); | |
| foreach ($this->tables as $table) { | |
| // If this table is a subquery, compile it recursively. | |
| if ($table['table'] instanceof SelectInterface) { | |
| $table['table']->compile($connection, $queryPlaceholder); | |
| } | |
| } | |
| // If there are any dependent queries to UNION, compile it recursively. | |
| foreach ($this->union as $union) { | |
| $union['query']->compile($connection, $queryPlaceholder); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function compiled() { | |
| if (!$this->condition->compiled() || !$this->having->compiled()) { | |
| return FALSE; | |
| } | |
| foreach ($this->tables as $table) { | |
| // If this table is a subquery, check its status recursively. | |
| if ($table['table'] instanceof SelectInterface) { | |
| if (!$table['table']->compiled()) { | |
| return FALSE; | |
| } | |
| } | |
| } | |
| foreach ($this->union as $union) { | |
| if (!$union['query']->compiled()) { | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingCondition($field, $value = NULL, $operator = NULL) { | |
| $this->having->condition($field, $value, $operator); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &havingConditions() { | |
| return $this->having->conditions(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingArguments() { | |
| return $this->having->arguments(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function having($snippet, $args = array()) { | |
| $this->having->where($snippet, $args); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingCompile(Connection $connection) { | |
| $this->having->compile($connection, $this); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function extend($extender_name) { | |
| $override_class = $extender_name . '_' . $this->connection->driver(); | |
| if (class_exists($override_class)) { | |
| $extender_name = $override_class; | |
| } | |
| return new $extender_name($this, $this->connection); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingIsNull($field) { | |
| $this->having->isNull($field); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingIsNotNull($field) { | |
| $this->having->isNotNull($field); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingExists(SelectInterface $select) { | |
| $this->having->exists($select); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function havingNotExists(SelectInterface $select) { | |
| $this->having->notExists($select); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function forUpdate($set = TRUE) { | |
| if (isset($set)) { | |
| $this->forUpdate = $set; | |
| } | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getFields() { | |
| return $this->fields; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getExpressions() { | |
| return $this->expressions; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getOrderBy() { | |
| return $this->order; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getGroupBy() { | |
| return $this->group; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getTables() { | |
| return $this->tables; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function &getUnion() { | |
| return $this->union; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function escapeLike($string) { | |
| return $this->connection->escapeLike($string); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function escapeField($string) { | |
| return $this->connection->escapeField($string); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) { | |
| if (!isset($queryPlaceholder)) { | |
| $queryPlaceholder = $this; | |
| } | |
| $this->compile($this->connection, $queryPlaceholder); | |
| return $this->arguments(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function isPrepared() { | |
| return $this->prepared; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function preExecute(SelectInterface $query = NULL) { | |
| // If no query object is passed in, use $this. | |
| if (!isset($query)) { | |
| $query = $this; | |
| } | |
| // Only execute this once. | |
| if ($query->isPrepared()) { | |
| return TRUE; | |
| } | |
| // Modules may alter all queries or only those having a particular tag. | |
| if (isset($this->alterTags)) { | |
| $hooks = array('query'); | |
| foreach ($this->alterTags as $tag => $value) { | |
| $hooks[] = 'query_' . $tag; | |
| } | |
| \Drupal::moduleHandler()->alter($hooks, $query); | |
| } | |
| $this->prepared = TRUE; | |
| // Now also prepare any sub-queries. | |
| foreach ($this->tables as $table) { | |
| if ($table['table'] instanceof SelectInterface) { | |
| $table['table']->preExecute(); | |
| } | |
| } | |
| foreach ($this->union as $union) { | |
| $union['query']->preExecute(); | |
| } | |
| return $this->prepared; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function execute() { | |
| // If validation fails, simply return NULL. | |
| // Note that validation routines in preExecute() may throw exceptions instead. | |
| if (!$this->preExecute()) { | |
| return NULL; | |
| } | |
| $args = $this->getArguments(); | |
| return $this->connection->query((string) $this, $args, $this->queryOptions); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function distinct($distinct = TRUE) { | |
| $this->distinct = $distinct; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addField($table_alias, $field, $alias = NULL) { | |
| // If no alias is specified, first try the field name itself. | |
| if (empty($alias)) { | |
| $alias = $field; | |
| } | |
| // If that's already in use, try the table name and field name. | |
| if (!empty($this->fields[$alias])) { | |
| $alias = $table_alias . '_' . $field; | |
| } | |
| // If that is already used, just add a counter until we find an unused alias. | |
| $alias_candidate = $alias; | |
| $count = 2; | |
| while (!empty($this->fields[$alias_candidate])) { | |
| $alias_candidate = $alias . '_' . $count++; | |
| } | |
| $alias = $alias_candidate; | |
| $this->fields[$alias] = array( | |
| 'field' => $field, | |
| 'table' => $table_alias, | |
| 'alias' => $alias, | |
| ); | |
| return $alias; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fields($table_alias, array $fields = array()) { | |
| if ($fields) { | |
| foreach ($fields as $field) { | |
| // We don't care what alias was assigned. | |
| $this->addField($table_alias, $field); | |
| } | |
| } | |
| else { | |
| // We want all fields from this table. | |
| $this->tables[$table_alias]['all_fields'] = TRUE; | |
| } | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addExpression($expression, $alias = NULL, $arguments = array()) { | |
| if (empty($alias)) { | |
| $alias = 'expression'; | |
| } | |
| $alias_candidate = $alias; | |
| $count = 2; | |
| while (!empty($this->expressions[$alias_candidate])) { | |
| $alias_candidate = $alias . '_' . $count++; | |
| } | |
| $alias = $alias_candidate; | |
| $this->expressions[$alias] = array( | |
| 'expression' => $expression, | |
| 'alias' => $alias, | |
| 'arguments' => $arguments, | |
| ); | |
| return $alias; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) { | |
| return $this->addJoin('INNER', $table, $alias, $condition, $arguments); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) { | |
| return $this->addJoin('INNER', $table, $alias, $condition, $arguments); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) { | |
| return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) { | |
| return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) { | |
| if (empty($alias)) { | |
| if ($table instanceof SelectInterface) { | |
| $alias = 'subquery'; | |
| } | |
| else { | |
| $alias = $table; | |
| } | |
| } | |
| $alias_candidate = $alias; | |
| $count = 2; | |
| while (!empty($this->tables[$alias_candidate])) { | |
| $alias_candidate = $alias . '_' . $count++; | |
| } | |
| $alias = $alias_candidate; | |
| if (is_string($condition)) { | |
| $condition = str_replace('%alias', $alias, $condition); | |
| } | |
| $this->tables[$alias] = array( | |
| 'join type' => $type, | |
| 'table' => $table, | |
| 'alias' => $alias, | |
| 'condition' => $condition, | |
| 'arguments' => $arguments, | |
| ); | |
| return $alias; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function orderBy($field, $direction = 'ASC') { | |
| // Only allow ASC and DESC, default to ASC. | |
| $direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC'; | |
| $this->order[$field] = $direction; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function orderRandom() { | |
| $alias = $this->addExpression('RAND()', 'random_field'); | |
| $this->orderBy($alias); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function range($start = NULL, $length = NULL) { | |
| $this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array(); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function union(SelectInterface $query, $type = '') { | |
| // Handle UNION aliasing. | |
| switch ($type) { | |
| // Fold UNION DISTINCT to UNION for better cross database support. | |
| case 'DISTINCT': | |
| case '': | |
| $type = 'UNION'; | |
| break; | |
| case 'ALL': | |
| $type = 'UNION ALL'; | |
| default: | |
| } | |
| $this->union[] = array( | |
| 'type' => $type, | |
| 'query' => $query, | |
| ); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function groupBy($field) { | |
| $this->group[$field] = $field; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function countQuery() { | |
| $count = $this->prepareCountQuery(); | |
| $query = $this->connection->select($count, NULL, $this->queryOptions); | |
| $query->addExpression('COUNT(*)'); | |
| return $query; | |
| } | |
| /** | |
| * Prepares a count query from the current query object. | |
| * | |
| * @return \Drupal\Core\Database\Query\Select | |
| * A new query object ready to have COUNT(*) performed on it. | |
| */ | |
| protected function prepareCountQuery() { | |
| // Create our new query object that we will mutate into a count query. | |
| $count = clone($this); | |
| $group_by = $count->getGroupBy(); | |
| $having = $count->havingConditions(); | |
| if (!$count->distinct && !isset($having[0])) { | |
| // When not executing a distinct query, we can zero-out existing fields | |
| // and expressions that are not used by a GROUP BY or HAVING. Fields | |
| // listed in a GROUP BY or HAVING clause need to be present in the | |
| // query. | |
| $fields =& $count->getFields(); | |
| foreach (array_keys($fields) as $field) { | |
| if (empty($group_by[$field])) { | |
| unset($fields[$field]); | |
| } | |
| } | |
| $expressions =& $count->getExpressions(); | |
| foreach (array_keys($expressions) as $field) { | |
| if (empty($group_by[$field])) { | |
| unset($expressions[$field]); | |
| } | |
| } | |
| // Also remove 'all_fields' statements, which are expanded into tablename.* | |
| // when the query is executed. | |
| foreach ($count->tables as &$table) { | |
| unset($table['all_fields']); | |
| } | |
| } | |
| // If we've just removed all fields from the query, make sure there is at | |
| // least one so that the query still runs. | |
| $count->addExpression('1'); | |
| // Ordering a count query is a waste of cycles, and breaks on some | |
| // databases anyway. | |
| $orders = &$count->getOrderBy(); | |
| $orders = array(); | |
| if ($count->distinct && !empty($group_by)) { | |
| // If the query is distinct and contains a GROUP BY, we need to remove the | |
| // distinct because SQL99 does not support counting on distinct multiple fields. | |
| $count->distinct = FALSE; | |
| } | |
| // If there are any dependent queries to UNION, prepare each of those for | |
| // the count query also. | |
| foreach ($count->union as &$union) { | |
| $union['query'] = $union['query']->prepareCountQuery(); | |
| } | |
| return $count; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function __toString() { | |
| // For convenience, we compile the query ourselves if the caller forgot | |
| // to do it. This allows constructs like "(string) $query" to work. When | |
| // the query will be executed, it will be recompiled using the proper | |
| // placeholder generator anyway. | |
| if (!$this->compiled()) { | |
| $this->compile($this->connection, $this); | |
| } | |
| // Create a sanitized comment string to prepend to the query. | |
| $comments = $this->connection->makeComment($this->comments); | |
| // SELECT | |
| $query = $comments . 'SELECT '; | |
| if ($this->distinct) { | |
| $query .= 'DISTINCT '; | |
| } | |
| // FIELDS and EXPRESSIONS | |
| $fields = array(); | |
| foreach ($this->tables as $alias => $table) { | |
| if (!empty($table['all_fields'])) { | |
| $fields[] = $this->connection->escapeTable($alias) . '.*'; | |
| } | |
| } | |
| foreach ($this->fields as $field) { | |
| // Always use the AS keyword for field aliases, as some | |
| // databases require it (e.g., PostgreSQL). | |
| $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']); | |
| } | |
| foreach ($this->expressions as $expression) { | |
| $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']); | |
| } | |
| $query .= implode(', ', $fields); | |
| // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway. | |
| $query .= "\nFROM "; | |
| foreach ($this->tables as $table) { | |
| $query .= "\n"; | |
| if (isset($table['join type'])) { | |
| $query .= $table['join type'] . ' JOIN '; | |
| } | |
| // If the table is a subquery, compile it and integrate it into this query. | |
| if ($table['table'] instanceof SelectInterface) { | |
| // Run preparation steps on this sub-query before converting to string. | |
| $subquery = $table['table']; | |
| $subquery->preExecute(); | |
| $table_string = '(' . (string) $subquery . ')'; | |
| } | |
| else { | |
| $table_string = $this->connection->escapeTable($table['table']); | |
| // Do not attempt prefixing cross database / schema queries. | |
| if (strpos($table_string, '.') === FALSE) { | |
| $table_string = '{' . $table_string . '}'; | |
| } | |
| } | |
| // Don't use the AS keyword for table aliases, as some | |
| // databases don't support it (e.g., Oracle). | |
| $query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']); | |
| if (!empty($table['condition'])) { | |
| $query .= ' ON ' . $table['condition']; | |
| } | |
| } | |
| // WHERE | |
| if (count($this->condition)) { | |
| // There is an implicit string cast on $this->condition. | |
| $query .= "\nWHERE " . $this->condition; | |
| } | |
| // GROUP BY | |
| if ($this->group) { | |
| $query .= "\nGROUP BY " . implode(', ', $this->group); | |
| } | |
| // HAVING | |
| if (count($this->having)) { | |
| // There is an implicit string cast on $this->having. | |
| $query .= "\nHAVING " . $this->having; | |
| } | |
| // ORDER BY | |
| if ($this->order) { | |
| $query .= "\nORDER BY "; | |
| $fields = array(); | |
| foreach ($this->order as $field => $direction) { | |
| $fields[] = $this->connection->escapeField($field) . ' ' . $direction; | |
| } | |
| $query .= implode(', ', $fields); | |
| } | |
| // RANGE | |
| // There is no universal SQL standard for handling range or limit clauses. | |
| // Fortunately, all core-supported databases use the same range syntax. | |
| // Databases that need a different syntax can override this method and | |
| // do whatever alternate logic they need to. | |
| if (!empty($this->range)) { | |
| $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start']; | |
| } | |
| // UNION is a little odd, as the select queries to combine are passed into | |
| // this query, but syntactically they all end up on the same level. | |
| if ($this->union) { | |
| foreach ($this->union as $union) { | |
| $query .= ' ' . $union['type'] . ' ' . (string) $union['query']; | |
| } | |
| } | |
| if ($this->forUpdate) { | |
| $query .= ' FOR UPDATE'; | |
| } | |
| return $query; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function __clone() { | |
| // On cloning, also clone the dependent objects. However, we do not | |
| // want to clone the database connection object as that would duplicate the | |
| // connection itself. | |
| $this->condition = clone($this->condition); | |
| $this->having = clone($this->having); | |
| foreach ($this->union as $key => $aggregate) { | |
| $this->union[$key]['query'] = clone($aggregate['query']); | |
| } | |
| } | |
| } |