- Patch #557318 by cha0s, hass, Crell: Changed UNION support is missing in database API.
							parent
							
								
									a539b0e00d
								
							
						
					
					
						commit
						f73040e18b
					
				| 
						 | 
					@ -111,6 +111,25 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public function &getTables();
 | 
					  public function &getTables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns a reference to the union queries for this query. This include
 | 
				
			||||||
 | 
					   * queries for UNION, UNION ALL, and UNION DISTINCT.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Because this method returns by reference, alter hooks may edit the tables
 | 
				
			||||||
 | 
					   * array directly to make their changes. If just adding union queries,
 | 
				
			||||||
 | 
					   * however, the use of the union() method is preferred.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Note that this method must be called by reference as well:
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @code
 | 
				
			||||||
 | 
					   * $fields =& $query->getUnion();
 | 
				
			||||||
 | 
					   * @endcode
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @return
 | 
				
			||||||
 | 
					   *   A reference to the union query array structure.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public function &getUnion();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Compiles and returns an associative array of the arguments for this prepared statement.
 | 
					   * Compiles and returns an associative array of the arguments for this prepared statement.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
| 
						 | 
					@ -344,6 +363,31 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public function range($start = NULL, $length = NULL);
 | 
					  public function range($start = NULL, $length = NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Add another Select query to UNION to this one.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Union queries consist of two or more queries whose
 | 
				
			||||||
 | 
					   * results are effectively concatenated together. Queries
 | 
				
			||||||
 | 
					   * will be UNIONed in the order they are specified, with
 | 
				
			||||||
 | 
					   * this object's query coming first. Duplicate columns will
 | 
				
			||||||
 | 
					   * be discarded. All forms of UNION are supported, using
 | 
				
			||||||
 | 
					   * the second '$type' argument.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Note: All queries UNIONed together must have the same
 | 
				
			||||||
 | 
					   * field structure, in the same order. It is up to the
 | 
				
			||||||
 | 
					   * caller to ensure that they match properly. If they do
 | 
				
			||||||
 | 
					   * not, an SQL syntax error will result.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param $query
 | 
				
			||||||
 | 
					   *   The query to UNION to this query.
 | 
				
			||||||
 | 
					   * @param $type
 | 
				
			||||||
 | 
					   *   The type of UNION to add to the query. Defaults to plain
 | 
				
			||||||
 | 
					   *   UNION.
 | 
				
			||||||
 | 
					   * @return
 | 
				
			||||||
 | 
					   *   The called object.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public function union(SelectQueryInterface $query, $type = '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Groups the result set by the specified field.
 | 
					   * Groups the result set by the specified field.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
| 
						 | 
					@ -524,6 +568,10 @@ class SelectQueryExtender implements SelectQueryInterface {
 | 
				
			||||||
    return $this->query->getTables();
 | 
					    return $this->query->getTables();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function &getUnion() {
 | 
				
			||||||
 | 
					    return $this->query->getUnion();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
 | 
					  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
 | 
				
			||||||
    return $this->query->getArguments($queryPlaceholder);
 | 
					    return $this->query->getArguments($queryPlaceholder);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -600,6 +648,11 @@ class SelectQueryExtender implements SelectQueryInterface {
 | 
				
			||||||
    return $this;
 | 
					    return $this;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function union(SelectQueryInterface $query, $type = '') {
 | 
				
			||||||
 | 
					    $this->query->union($query, $type);
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public function groupBy($field) {
 | 
					  public function groupBy($field) {
 | 
				
			||||||
    $this->query->groupBy($field);
 | 
					    $this->query->groupBy($field);
 | 
				
			||||||
    return $this;
 | 
					    return $this;
 | 
				
			||||||
| 
						 | 
					@ -765,6 +818,19 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  protected $range;
 | 
					  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.
 | 
					   * Indicates if preExecute() has already been called.
 | 
				
			||||||
   * @var boolean
 | 
					   * @var boolean
 | 
				
			||||||
| 
						 | 
					@ -910,6 +976,10 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
    return $this->tables;
 | 
					    return $this->tables;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function &getUnion() {
 | 
				
			||||||
 | 
					    return $this->union;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
 | 
					  public function getArguments(QueryPlaceholderInterface $queryPlaceholder = NULL) {
 | 
				
			||||||
    if (!isset($queryPlaceholder)) {
 | 
					    if (!isset($queryPlaceholder)) {
 | 
				
			||||||
      $queryPlaceholder = $this;
 | 
					      $queryPlaceholder = $this;
 | 
				
			||||||
| 
						 | 
					@ -917,6 +987,7 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
    $this->where->compile($this->connection, $queryPlaceholder);
 | 
					    $this->where->compile($this->connection, $queryPlaceholder);
 | 
				
			||||||
    $this->having->compile($this->connection, $queryPlaceholder);
 | 
					    $this->having->compile($this->connection, $queryPlaceholder);
 | 
				
			||||||
    $args = $this->where->arguments() + $this->having->arguments();
 | 
					    $args = $this->where->arguments() + $this->having->arguments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    foreach ($this->tables as $table) {
 | 
					    foreach ($this->tables as $table) {
 | 
				
			||||||
      if ($table['arguments']) {
 | 
					      if ($table['arguments']) {
 | 
				
			||||||
        $args += $table['arguments'];
 | 
					        $args += $table['arguments'];
 | 
				
			||||||
| 
						 | 
					@ -926,12 +997,19 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
        $args += $table['table']->getArguments($queryPlaceholder);
 | 
					        $args += $table['table']->getArguments($queryPlaceholder);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    foreach ($this->expressions as $expression) {
 | 
					    foreach ($this->expressions as $expression) {
 | 
				
			||||||
      if ($expression['arguments']) {
 | 
					      if ($expression['arguments']) {
 | 
				
			||||||
        $args += $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']->getArguments($queryPlaceholder);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $args;
 | 
					    return $args;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1109,6 +1187,28 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
    return $this;
 | 
					    return $this;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function union(SelectQueryInterface $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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public function groupBy($field) {
 | 
					  public function groupBy($field) {
 | 
				
			||||||
    $this->group[] = $field;
 | 
					    $this->group[] = $field;
 | 
				
			||||||
    return $this;
 | 
					    return $this;
 | 
				
			||||||
| 
						 | 
					@ -1223,16 +1323,28 @@ class SelectQuery extends Query implements SelectQueryInterface {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // RANGE is database specific, so we can't do it here.
 | 
					    // RANGE is database specific, so we can't do it here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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'];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $query;
 | 
					    return $query;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public function __clone() {
 | 
					  public function __clone() {
 | 
				
			||||||
    // On cloning, also clone the conditional objects. However, we do not
 | 
					    // On cloning, also clone the dependent objects. However, we do not
 | 
				
			||||||
    // want to clone the database connection object as that would duplicate the
 | 
					    // want to clone the database connection object as that would duplicate the
 | 
				
			||||||
    // connection itself.
 | 
					    // connection itself.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $this->where = clone($this->where);
 | 
					    $this->where = clone($this->where);
 | 
				
			||||||
    $this->having = clone($this->having);
 | 
					    $this->having = clone($this->having);
 | 
				
			||||||
 | 
					    foreach ($this->union as $key => $aggregate) {
 | 
				
			||||||
 | 
					      $this->union[$key]['query'] = clone($aggregate['query']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1352,6 +1352,54 @@ class DatabaseSelectTestCase extends DatabaseTestCase {
 | 
				
			||||||
    $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.'));
 | 
					    $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.'));
 | 
				
			||||||
    $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.'));
 | 
					    $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.'));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Test that we can UNION multiple Select queries together. This is
 | 
				
			||||||
 | 
					   * semantically equal to UNION DISTINCT, so we don't explicity test that.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  function testUnion() {
 | 
				
			||||||
 | 
					    $query_1 = db_select('test', 't')
 | 
				
			||||||
 | 
					      ->fields('t', array('name'))
 | 
				
			||||||
 | 
					      ->condition('age', array(27, 28), 'IN');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $query_2 = db_select('test', 't')
 | 
				
			||||||
 | 
					      ->fields('t', array('name'))
 | 
				
			||||||
 | 
					      ->condition('age', 28);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $query_1->union($query_2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $names = $query_1->execute()->fetchCol();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Ensure we only get 2 records.
 | 
				
			||||||
 | 
					    $this->assertEqual(count($names), 2, t('UNION correctly discarded duplicates.'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $this->assertEqual($names[0], 'George', t('First query returned correct name.'));
 | 
				
			||||||
 | 
					    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct name.'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Test that we can UNION ALL multiple Select queries together.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  function testUnionAll() {
 | 
				
			||||||
 | 
					    $query_1 = db_select('test', 't')
 | 
				
			||||||
 | 
					      ->fields('t', array('name'))
 | 
				
			||||||
 | 
					      ->condition('age', array(27, 28), 'IN');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    $query_2 = db_select('test', 't')
 | 
				
			||||||
 | 
					      ->fields('t', array('name'))
 | 
				
			||||||
 | 
					      ->condition('age', 28);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $query_1->union($query_2, 'ALL');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $names = $query_1->execute()->fetchCol();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Ensure we get all 3 records.
 | 
				
			||||||
 | 
					    $this->assertEqual(count($names), 3, t('UNION ALL correctly preserved duplicates.'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $this->assertEqual($names[0], 'George', t('First query returned correct first name.'));
 | 
				
			||||||
 | 
					    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.'));
 | 
				
			||||||
 | 
					    $this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue