"Fossies" - the Fresh Open Source Software Archive

Member "drupal-8.9.10/core/lib/Drupal/Core/Database/Query/Select.php" (26 Nov 2020, 24349 Bytes) of package /linux/www/drupal-8.9.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Select.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 namespace Drupal\Core\Database\Query;
    4 
    5 use Drupal\Core\Database\Database;
    6 use Drupal\Core\Database\Connection;
    7 
    8 /**
    9  * Query builder for SELECT statements.
   10  *
   11  * @ingroup database
   12  */
   13 class Select extends Query implements SelectInterface {
   14 
   15   use QueryConditionTrait;
   16 
   17   /**
   18    * The fields to SELECT.
   19    *
   20    * @var array
   21    */
   22   protected $fields = [];
   23 
   24   /**
   25    * The expressions to SELECT as virtual fields.
   26    *
   27    * @var array
   28    */
   29   protected $expressions = [];
   30 
   31   /**
   32    * The tables against which to JOIN.
   33    *
   34    * This property is a nested array. Each entry is an array representing
   35    * a single table against which to join. The structure of each entry is:
   36    *
   37    * array(
   38    *   'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
   39    *   'table' => $table,
   40    *   'alias' => $alias_of_the_table,
   41    *   'condition' => $join_condition (string or Condition object),
   42    *   'arguments' => $array_of_arguments_for_placeholders_in_the condition.
   43    *   'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
   44    * )
   45    *
   46    * If $table is a string, it is taken as the name of a table. If it is
   47    * a Select query object, it is taken as a subquery.
   48    *
   49    * If $join_condition is a Condition object, any arguments should be
   50    * incorporated into the object; a separate array of arguments does not
   51    * need to be provided.
   52    *
   53    * @var array
   54    */
   55   protected $tables = [];
   56 
   57   /**
   58    * The fields by which to order this query.
   59    *
   60    * This is an associative array. The keys are the fields to order, and the value
   61    * is the direction to order, either ASC or DESC.
   62    *
   63    * @var array
   64    */
   65   protected $order = [];
   66 
   67   /**
   68    * The fields by which to group.
   69    *
   70    * @var array
   71    */
   72   protected $group = [];
   73 
   74   /**
   75    * The conditional object for the HAVING clause.
   76    *
   77    * @var \Drupal\Core\Database\Query\Condition
   78    */
   79   protected $having;
   80 
   81   /**
   82    * Whether or not this query should be DISTINCT
   83    *
   84    * @var bool
   85    */
   86   protected $distinct = FALSE;
   87 
   88   /**
   89    * The range limiters for this query.
   90    *
   91    * @var array
   92    */
   93   protected $range;
   94 
   95   /**
   96    * An array whose elements specify a query to UNION, and the UNION type. The
   97    * 'type' key may be '', 'ALL', or 'DISTINCT' to represent a 'UNION',
   98    * 'UNION ALL', or 'UNION DISTINCT' statement, respectively.
   99    *
  100    * All entries in this array will be applied from front to back, with the
  101    * first query to union on the right of the original query, the second union
  102    * to the right of the first, etc.
  103    *
  104    * @var array
  105    */
  106   protected $union = [];
  107 
  108   /**
  109    * Indicates if preExecute() has already been called.
  110    * @var bool
  111    */
  112   protected $prepared = FALSE;
  113 
  114   /**
  115    * The FOR UPDATE status
  116    *
  117    * @var bool
  118    */
  119   protected $forUpdate = FALSE;
  120 
  121   /**
  122    * Constructs a Select object.
  123    *
  124    * @param string $table
  125    *   The name of the table that is being queried.
  126    * @param string $alias
  127    *   The alias for the table.
  128    * @param \Drupal\Core\Database\Connection $connection
  129    *   Database connection object.
  130    * @param array $options
  131    *   Array of query options.
  132    */
  133   public function __construct($table, $alias, Connection $connection, $options = []) {
  134     $options['return'] = Database::RETURN_STATEMENT;
  135     parent::__construct($connection, $options);
  136     $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND';
  137     $this->condition = $this->connection->condition($conjunction);
  138     $this->having = $this->connection->condition($conjunction);
  139     $this->addJoin(NULL, $table, $alias);
  140   }
  141 
  142   /**
  143    * {@inheritdoc}
  144    */
  145   public function addTag($tag) {
  146     $this->alterTags[$tag] = 1;
  147     return $this;
  148   }
  149 
  150   /**
  151    * {@inheritdoc}
  152    */
  153   public function hasTag($tag) {
  154     return isset($this->alterTags[$tag]);
  155   }
  156 
  157   /**
  158    * {@inheritdoc}
  159    */
  160   public function hasAllTags() {
  161     return !(boolean) array_diff(func_get_args(), array_keys($this->alterTags));
  162   }
  163 
  164   /**
  165    * {@inheritdoc}
  166    */
  167   public function hasAnyTag() {
  168     return (boolean) array_intersect(func_get_args(), array_keys($this->alterTags));
  169   }
  170 
  171   /**
  172    * {@inheritdoc}
  173    */
  174   public function addMetaData($key, $object) {
  175     $this->alterMetaData[$key] = $object;
  176     return $this;
  177   }
  178 
  179   /**
  180    * {@inheritdoc}
  181    */
  182   public function getMetaData($key) {
  183     return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
  184   }
  185 
  186   /**
  187    * {@inheritdoc}
  188    */
  189   public function arguments() {
  190     if (!$this->compiled()) {
  191       return NULL;
  192     }
  193 
  194     $args = $this->condition->arguments() + $this->having->arguments();
  195 
  196     foreach ($this->tables as $table) {
  197       if ($table['arguments']) {
  198         $args += $table['arguments'];
  199       }
  200       // If this table is a subquery, grab its arguments recursively.
  201       if ($table['table'] instanceof SelectInterface) {
  202         $args += $table['table']->arguments();
  203       }
  204       // If the join condition is an object, grab its arguments recursively.
  205       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
  206         $args += $table['condition']->arguments();
  207       }
  208     }
  209 
  210     foreach ($this->expressions as $expression) {
  211       if ($expression['arguments']) {
  212         $args += $expression['arguments'];
  213       }
  214     }
  215 
  216     // If there are any dependent queries to UNION,
  217     // incorporate their arguments recursively.
  218     foreach ($this->union as $union) {
  219       $args += $union['query']->arguments();
  220     }
  221 
  222     return $args;
  223   }
  224 
  225   /**
  226    * {@inheritdoc}
  227    */
  228   public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
  229     $this->condition->compile($connection, $queryPlaceholder);
  230     $this->having->compile($connection, $queryPlaceholder);
  231 
  232     foreach ($this->tables as $table) {
  233       // If this table is a subquery, compile it recursively.
  234       if ($table['table'] instanceof SelectInterface) {
  235         $table['table']->compile($connection, $queryPlaceholder);
  236       }
  237       // Make sure join conditions are also compiled.
  238       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
  239         $table['condition']->compile($connection, $queryPlaceholder);
  240       }
  241     }
  242 
  243     // If there are any dependent queries to UNION, compile it recursively.
  244     foreach ($this->union as $union) {
  245       $union['query']->compile($connection, $queryPlaceholder);
  246     }
  247   }
  248 
  249   /**
  250    * {@inheritdoc}
  251    */
  252   public function compiled() {
  253     if (!$this->condition->compiled() || !$this->having->compiled()) {
  254       return FALSE;
  255     }
  256 
  257     foreach ($this->tables as $table) {
  258       // If this table is a subquery, check its status recursively.
  259       if ($table['table'] instanceof SelectInterface) {
  260         if (!$table['table']->compiled()) {
  261           return FALSE;
  262         }
  263       }
  264       if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
  265         if (!$table['condition']->compiled()) {
  266           return FALSE;
  267         }
  268       }
  269     }
  270 
  271     foreach ($this->union as $union) {
  272       if (!$union['query']->compiled()) {
  273         return FALSE;
  274       }
  275     }
  276 
  277     return TRUE;
  278   }
  279 
  280   /**
  281    * {@inheritdoc}
  282    */
  283   public function havingCondition($field, $value = NULL, $operator = NULL) {
  284     $this->having->condition($field, $value, $operator);
  285     return $this;
  286   }
  287 
  288   /**
  289    * {@inheritdoc}
  290    */
  291   public function &havingConditions() {
  292     return $this->having->conditions();
  293   }
  294 
  295   /**
  296    * {@inheritdoc}
  297    */
  298   public function havingArguments() {
  299     return $this->having->arguments();
  300   }
  301 
  302   /**
  303    * {@inheritdoc}
  304    */
  305   public function having($snippet, $args = []) {
  306     $this->having->where($snippet, $args);
  307     return $this;
  308   }
  309 
  310   /**
  311    * {@inheritdoc}
  312    */
  313   public function havingCompile(Connection $connection) {
  314     $this->having->compile($connection, $this);
  315   }
  316 
  317   /**
  318    * {@inheritdoc}
  319    */
  320   public function extend($extender_name) {
  321     $override_class = $extender_name . '_' . $this->connection->driver();
  322     if (class_exists($override_class)) {
  323       $extender_name = $override_class;
  324     }
  325     return new $extender_name($this, $this->connection);
  326   }
  327 
  328   /**
  329    * {@inheritdoc}
  330    */
  331   public function havingIsNull($field) {
  332     $this->having->isNull($field);
  333     return $this;
  334   }
  335 
  336   /**
  337    * {@inheritdoc}
  338    */
  339   public function havingIsNotNull($field) {
  340     $this->having->isNotNull($field);
  341     return $this;
  342   }
  343 
  344   /**
  345    * {@inheritdoc}
  346    */
  347   public function havingExists(SelectInterface $select) {
  348     $this->having->exists($select);
  349     return $this;
  350   }
  351 
  352   /**
  353    * {@inheritdoc}
  354    */
  355   public function havingNotExists(SelectInterface $select) {
  356     $this->having->notExists($select);
  357     return $this;
  358   }
  359 
  360   /**
  361    * {@inheritdoc}
  362    */
  363   public function forUpdate($set = TRUE) {
  364     if (isset($set)) {
  365       $this->forUpdate = $set;
  366     }
  367     return $this;
  368   }
  369 
  370   /**
  371    * {@inheritdoc}
  372    */
  373   public function &getFields() {
  374     return $this->fields;
  375   }
  376 
  377   /**
  378    * {@inheritdoc}
  379    */
  380   public function &getExpressions() {
  381     return $this->expressions;
  382   }
  383 
  384   /**
  385    * {@inheritdoc}
  386    */
  387   public function &getOrderBy() {
  388     return $this->order;
  389   }
  390 
  391   /**
  392    * {@inheritdoc}
  393    */
  394   public function &getGroupBy() {
  395     return $this->group;
  396   }
  397 
  398   /**
  399    * {@inheritdoc}
  400    */
  401   public function &getTables() {
  402     return $this->tables;
  403   }
  404 
  405   /**
  406    * {@inheritdoc}
  407    */
  408   public function &getUnion() {
  409     return $this->union;
  410   }
  411 
  412   /**
  413    * {@inheritdoc}
  414    */
  415   public function escapeLike($string) {
  416     return $this->connection->escapeLike($string);
  417   }
  418 
  419   /**
  420    * {@inheritdoc}
  421    */
  422   public function escapeField($string) {
  423     return $this->connection->escapeField($string);
  424   }
  425 
  426   /**
  427    * {@inheritdoc}
  428    */
  429   public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
  430     if (!isset($queryPlaceholder)) {
  431       $queryPlaceholder = $this;
  432     }
  433     $this->compile($this->connection, $queryPlaceholder);
  434     return $this->arguments();
  435   }
  436 
  437   /**
  438    * {@inheritdoc}
  439    */
  440   public function isPrepared() {
  441     return $this->prepared;
  442   }
  443 
  444   /**
  445    * {@inheritdoc}
  446    */
  447   public function preExecute(SelectInterface $query = NULL) {
  448     // If no query object is passed in, use $this.
  449     if (!isset($query)) {
  450       $query = $this;
  451     }
  452 
  453     // Only execute this once.
  454     if ($query->isPrepared()) {
  455       return TRUE;
  456     }
  457 
  458     // Modules may alter all queries or only those having a particular tag.
  459     if (isset($this->alterTags)) {
  460       // Many contrib modules as well as Entity Reference in core assume that
  461       // query tags used for access-checking purposes follow the pattern
  462       // $entity_type . '_access'. But this is not the case for taxonomy terms,
  463       // since the core Taxonomy module used to add term_access instead of
  464       // taxonomy_term_access to its queries. Provide backwards compatibility
  465       // by adding both tags here instead of attempting to fix all contrib
  466       // modules in a coordinated effort.
  467       // TODO:
  468       // - Extract this mechanism into a hook as part of a public (non-security)
  469       //   issue.
  470       // - Emit E_USER_DEPRECATED if term_access is used.
  471       //   https://www.drupal.org/node/2575081
  472       $term_access_tags = ['term_access' => 1, 'taxonomy_term_access' => 1];
  473       if (array_intersect_key($this->alterTags, $term_access_tags)) {
  474         $this->alterTags += $term_access_tags;
  475       }
  476       $hooks = ['query'];
  477       foreach ($this->alterTags as $tag => $value) {
  478         $hooks[] = 'query_' . $tag;
  479       }
  480       \Drupal::moduleHandler()->alter($hooks, $query);
  481     }
  482 
  483     $this->prepared = TRUE;
  484 
  485     // Now also prepare any sub-queries.
  486     foreach ($this->tables as $table) {
  487       if ($table['table'] instanceof SelectInterface) {
  488         $table['table']->preExecute();
  489       }
  490     }
  491 
  492     foreach ($this->union as $union) {
  493       $union['query']->preExecute();
  494     }
  495 
  496     return $this->prepared;
  497   }
  498 
  499   /**
  500    * {@inheritdoc}
  501    */
  502   public function execute() {
  503     // If validation fails, simply return NULL.
  504     // Note that validation routines in preExecute() may throw exceptions instead.
  505     if (!$this->preExecute()) {
  506       return NULL;
  507     }
  508 
  509     $args = $this->getArguments();
  510     return $this->connection->query((string) $this, $args, $this->queryOptions);
  511   }
  512 
  513   /**
  514    * {@inheritdoc}
  515    */
  516   public function distinct($distinct = TRUE) {
  517     $this->distinct = $distinct;
  518     return $this;
  519   }
  520 
  521   /**
  522    * {@inheritdoc}
  523    */
  524   public function addField($table_alias, $field, $alias = NULL) {
  525     // If no alias is specified, first try the field name itself.
  526     if (empty($alias)) {
  527       $alias = $field;
  528     }
  529 
  530     // If that's already in use, try the table name and field name.
  531     if (!empty($this->fields[$alias])) {
  532       $alias = $table_alias . '_' . $field;
  533     }
  534 
  535     // If that is already used, just add a counter until we find an unused alias.
  536     $alias_candidate = $alias;
  537     $count = 2;
  538     while (!empty($this->fields[$alias_candidate])) {
  539       $alias_candidate = $alias . '_' . $count++;
  540     }
  541     $alias = $alias_candidate;
  542 
  543     $this->fields[$alias] = [
  544       'field' => $field,
  545       'table' => $table_alias,
  546       'alias' => $alias,
  547     ];
  548 
  549     return $alias;
  550   }
  551 
  552   /**
  553    * {@inheritdoc}
  554    */
  555   public function fields($table_alias, array $fields = []) {
  556     if ($fields) {
  557       foreach ($fields as $field) {
  558         // We don't care what alias was assigned.
  559         $this->addField($table_alias, $field);
  560       }
  561     }
  562     else {
  563       // We want all fields from this table.
  564       $this->tables[$table_alias]['all_fields'] = TRUE;
  565     }
  566 
  567     return $this;
  568   }
  569 
  570   /**
  571    * {@inheritdoc}
  572    */
  573   public function addExpression($expression, $alias = NULL, $arguments = []) {
  574     if (empty($alias)) {
  575       $alias = 'expression';
  576     }
  577 
  578     $alias_candidate = $alias;
  579     $count = 2;
  580     while (!empty($this->expressions[$alias_candidate])) {
  581       $alias_candidate = $alias . '_' . $count++;
  582     }
  583     $alias = $alias_candidate;
  584 
  585     $this->expressions[$alias] = [
  586       'expression' => $expression,
  587       'alias' => $alias,
  588       'arguments' => $arguments,
  589     ];
  590 
  591     return $alias;
  592   }
  593 
  594   /**
  595    * {@inheritdoc}
  596    */
  597   public function join($table, $alias = NULL, $condition = NULL, $arguments = []) {
  598     return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
  599   }
  600 
  601   /**
  602    * {@inheritdoc}
  603    */
  604   public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
  605     return $this->addJoin('INNER', $table, $alias, $condition, $arguments);
  606   }
  607 
  608   /**
  609    * {@inheritdoc}
  610    */
  611   public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
  612     return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
  613   }
  614 
  615   /**
  616    * {@inheritdoc}
  617    */
  618   public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
  619     return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
  620   }
  621 
  622   /**
  623    * {@inheritdoc}
  624    */
  625   public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = []) {
  626     if (empty($alias)) {
  627       if ($table instanceof SelectInterface) {
  628         $alias = 'subquery';
  629       }
  630       else {
  631         $alias = $table;
  632       }
  633     }
  634 
  635     $alias_candidate = $alias;
  636     $count = 2;
  637     while (!empty($this->tables[$alias_candidate])) {
  638       $alias_candidate = $alias . '_' . $count++;
  639     }
  640     $alias = $alias_candidate;
  641 
  642     if (is_string($condition)) {
  643       $condition = str_replace('%alias', $alias, $condition);
  644     }
  645 
  646     $this->tables[$alias] = [
  647       'join type' => $type,
  648       'table' => $table,
  649       'alias' => $alias,
  650       'condition' => $condition,
  651       'arguments' => $arguments,
  652     ];
  653 
  654     return $alias;
  655   }
  656 
  657   /**
  658    * {@inheritdoc}
  659    */
  660   public function orderBy($field, $direction = 'ASC') {
  661     // Only allow ASC and DESC, default to ASC.
  662     $direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC';
  663     $this->order[$field] = $direction;
  664     return $this;
  665   }
  666 
  667   /**
  668    * {@inheritdoc}
  669    */
  670   public function orderRandom() {
  671     $alias = $this->addExpression('RAND()', 'random_field');
  672     $this->orderBy($alias);
  673     return $this;
  674   }
  675 
  676   /**
  677    * {@inheritdoc}
  678    */
  679   public function range($start = NULL, $length = NULL) {
  680     $this->range = $start !== NULL ? ['start' => $start, 'length' => $length] : [];
  681     return $this;
  682   }
  683 
  684   /**
  685    * {@inheritdoc}
  686    */
  687   public function union(SelectInterface $query, $type = '') {
  688     // Handle UNION aliasing.
  689     switch ($type) {
  690       // Fold UNION DISTINCT to UNION for better cross database support.
  691       case 'DISTINCT':
  692       case '':
  693         $type = 'UNION';
  694         break;
  695 
  696       case 'ALL':
  697         $type = 'UNION ALL';
  698       default:
  699     }
  700 
  701     $this->union[] = [
  702       'type' => $type,
  703       'query' => $query,
  704     ];
  705 
  706     return $this;
  707   }
  708 
  709   /**
  710    * {@inheritdoc}
  711    */
  712   public function groupBy($field) {
  713     $this->group[$field] = $field;
  714     return $this;
  715   }
  716 
  717   /**
  718    * {@inheritdoc}
  719    */
  720   public function countQuery() {
  721     $count = $this->prepareCountQuery();
  722 
  723     $query = $this->connection->select($count, NULL, $this->queryOptions);
  724     $query->addExpression('COUNT(*)');
  725 
  726     return $query;
  727   }
  728 
  729   /**
  730    * Prepares a count query from the current query object.
  731    *
  732    * @return \Drupal\Core\Database\Query\Select
  733    *   A new query object ready to have COUNT(*) performed on it.
  734    */
  735   protected function prepareCountQuery() {
  736     // Create our new query object that we will mutate into a count query.
  737     $count = clone($this);
  738 
  739     $group_by = $count->getGroupBy();
  740     $having = $count->havingConditions();
  741 
  742     if (!$count->distinct && !isset($having[0])) {
  743       // When not executing a distinct query, we can zero-out existing fields
  744       // and expressions that are not used by a GROUP BY or HAVING. Fields
  745       // listed in a GROUP BY or HAVING clause need to be present in the
  746       // query.
  747       $fields =& $count->getFields();
  748       foreach (array_keys($fields) as $field) {
  749         if (empty($group_by[$field])) {
  750           unset($fields[$field]);
  751         }
  752       }
  753 
  754       $expressions =& $count->getExpressions();
  755       foreach (array_keys($expressions) as $field) {
  756         if (empty($group_by[$field])) {
  757           unset($expressions[$field]);
  758         }
  759       }
  760 
  761       // Also remove 'all_fields' statements, which are expanded into tablename.*
  762       // when the query is executed.
  763       foreach ($count->tables as &$table) {
  764         unset($table['all_fields']);
  765       }
  766     }
  767 
  768     // If we've just removed all fields from the query, make sure there is at
  769     // least one so that the query still runs.
  770     $count->addExpression('1');
  771 
  772     // Ordering a count query is a waste of cycles, and breaks on some
  773     // databases anyway.
  774     $orders = &$count->getOrderBy();
  775     $orders = [];
  776 
  777     if ($count->distinct && !empty($group_by)) {
  778       // If the query is distinct and contains a GROUP BY, we need to remove the
  779       // distinct because SQL99 does not support counting on distinct multiple fields.
  780       $count->distinct = FALSE;
  781     }
  782 
  783     // If there are any dependent queries to UNION, prepare each of those for
  784     // the count query also.
  785     foreach ($count->union as &$union) {
  786       $union['query'] = $union['query']->prepareCountQuery();
  787     }
  788 
  789     return $count;
  790   }
  791 
  792   /**
  793    * {@inheritdoc}
  794    */
  795   public function __toString() {
  796     // For convenience, we compile the query ourselves if the caller forgot
  797     // to do it. This allows constructs like "(string) $query" to work. When
  798     // the query will be executed, it will be recompiled using the proper
  799     // placeholder generator anyway.
  800     if (!$this->compiled()) {
  801       $this->compile($this->connection, $this);
  802     }
  803 
  804     // Create a sanitized comment string to prepend to the query.
  805     $comments = $this->connection->makeComment($this->comments);
  806 
  807     // SELECT
  808     $query = $comments . 'SELECT ';
  809     if ($this->distinct) {
  810       $query .= 'DISTINCT ';
  811     }
  812 
  813     // FIELDS and EXPRESSIONS
  814     $fields = [];
  815     foreach ($this->tables as $alias => $table) {
  816       if (!empty($table['all_fields'])) {
  817         $fields[] = $this->connection->escapeAlias($alias) . '.*';
  818       }
  819     }
  820     foreach ($this->fields as $field) {
  821       // Note that $field['table'] holds the table alias.
  822       // @see \Drupal\Core\Database\Query\Select::addField
  823       $table = isset($field['table']) ? $this->connection->escapeAlias($field['table']) . '.' : '';
  824       // Always use the AS keyword for field aliases, as some
  825       // databases require it (e.g., PostgreSQL).
  826       $fields[] = $table . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
  827     }
  828     foreach ($this->expressions as $expression) {
  829       $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
  830     }
  831     $query .= implode(', ', $fields);
  832 
  833     // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
  834     $query .= "\nFROM";
  835     foreach ($this->tables as $table) {
  836       $query .= "\n";
  837       if (isset($table['join type'])) {
  838         $query .= $table['join type'] . ' JOIN ';
  839       }
  840 
  841       // If the table is a subquery, compile it and integrate it into this query.
  842       if ($table['table'] instanceof SelectInterface) {
  843         // Run preparation steps on this sub-query before converting to string.
  844         $subquery = $table['table'];
  845         $subquery->preExecute();
  846         $table_string = '(' . (string) $subquery . ')';
  847       }
  848       else {
  849         $table_string = $this->connection->escapeTable($table['table']);
  850         // Do not attempt prefixing cross database / schema queries.
  851         if (strpos($table_string, '.') === FALSE) {
  852           $table_string = '{' . $table_string . '}';
  853         }
  854       }
  855 
  856       // Don't use the AS keyword for table aliases, as some
  857       // databases don't support it (e.g., Oracle).
  858       $query .= $table_string . ' ' . $this->connection->escapeAlias($table['alias']);
  859 
  860       if (!empty($table['condition'])) {
  861         $query .= ' ON ' . (string) $table['condition'];
  862       }
  863     }
  864 
  865     // WHERE
  866     if (count($this->condition)) {
  867       // There is an implicit string cast on $this->condition.
  868       $query .= "\nWHERE " . $this->condition;
  869     }
  870 
  871     // GROUP BY
  872     if ($this->group) {
  873       $query .= "\nGROUP BY " . implode(', ', $this->group);
  874     }
  875 
  876     // HAVING
  877     if (count($this->having)) {
  878       // There is an implicit string cast on $this->having.
  879       $query .= "\nHAVING " . $this->having;
  880     }
  881 
  882     // UNION is a little odd, as the select queries to combine are passed into
  883     // this query, but syntactically they all end up on the same level.
  884     if ($this->union) {
  885       foreach ($this->union as $union) {
  886         $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
  887       }
  888     }
  889 
  890     // ORDER BY
  891     if ($this->order) {
  892       $query .= "\nORDER BY ";
  893       $fields = [];
  894       foreach ($this->order as $field => $direction) {
  895         $fields[] = $this->connection->escapeField($field) . ' ' . $direction;
  896       }
  897       $query .= implode(', ', $fields);
  898     }
  899 
  900     // RANGE
  901     // There is no universal SQL standard for handling range or limit clauses.
  902     // Fortunately, all core-supported databases use the same range syntax.
  903     // Databases that need a different syntax can override this method and
  904     // do whatever alternate logic they need to.
  905     if (!empty($this->range)) {
  906       $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
  907     }
  908 
  909     if ($this->forUpdate) {
  910       $query .= ' FOR UPDATE';
  911     }
  912 
  913     return $query;
  914   }
  915 
  916   /**
  917    * {@inheritdoc}
  918    */
  919   public function __clone() {
  920     parent::__clone();
  921 
  922     // On cloning, also clone the dependent objects. However, we do not
  923     // want to clone the database connection object as that would duplicate the
  924     // connection itself.
  925 
  926     $this->condition = clone($this->condition);
  927     $this->having = clone($this->having);
  928     foreach ($this->union as $key => $aggregate) {
  929       $this->union[$key]['query'] = clone($aggregate['query']);
  930     }
  931     foreach ($this->tables as $alias => $table) {
  932       if ($table['table'] instanceof SelectInterface) {
  933         $this->tables[$alias]['table'] = clone $table['table'];
  934       }
  935     }
  936   }
  937 
  938 }