Skip to content

Commit

Permalink
Improve support for unioned queries on MySQL.
Browse files Browse the repository at this point in the history
By wrapping unioned queries with () each arm in an union can have an
order clause. I've created a specialized compiler for SQLite as it seems
to be the only rdbms that does not support () in UNION from what I could
see.

Refs #5962
  • Loading branch information
markstory committed Feb 25, 2015
1 parent 250d30d commit 8490c9d
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/Database/Dialect/SqliteDialectTrait.php
Expand Up @@ -18,6 +18,7 @@
use Cake\Database\ExpressionInterface;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\SqlDialectTrait;
use Cake\Database\SqliteCompiler;

/**
* SQLite dialect trait
Expand Down Expand Up @@ -185,4 +186,14 @@ public function enableForeignKeySQL()
{
return 'PRAGMA foreign_keys = ON';
}

/**
* {@inheritDoc}
*
* @return \Cake\Database\SqliteCompiler
*/
public function newCompiler()
{
return new SqliteCompiler();
}
}
3 changes: 2 additions & 1 deletion src/Database/QueryCompiler.php
Expand Up @@ -253,7 +253,8 @@ protected function _buildUnionPart($parts, $query, $generator)
$parts = array_map(function ($p) use ($generator) {
$p['query'] = $p['query']->sql($generator);
$p['query'] = $p['query'][0] === '(' ? trim($p['query'], '()') : $p['query'];
return $p['all'] ? 'ALL ' . $p['query'] : $p['query'];
$prefix = $p['all'] ? 'ALL' : '';
return sprintf('%s (%s)', $prefix, $p['query']);
}, $parts);
return sprintf("\nUNION %s", implode("\nUNION ", $parts));
}
Expand Down
48 changes: 48 additions & 0 deletions src/Database/SqliteCompiler.php
@@ -0,0 +1,48 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Database;

use Cake\Database\QueryCompiler;

/**
* Responsible for compiling a Query object into its SQL representation
* for SQLite
*
* @internal
*/
class SqliteCompiler extends QueryCompiler
{

/**
* Builds the SQL string for all the UNION clauses in this query, when dealing
* with query objects it will also transform them using their configured SQL
* dialect.
*
* @param array $parts list of queries to be operated with UNION
* @param \Cake\Database\Query $query The query that is being compiled
* @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
* @return string
*/
protected function _buildUnionPart($parts, $query, $generator)
{
$parts = array_map(function ($p) use ($generator) {
$p['query'] = $p['query']->sql($generator);
$p['query'] = $p['query'][0] === '(' ? trim($p['query'], '()') : $p['query'];
$prefix = $p['all'] ? 'ALL' : '';
return sprintf('%s %s', $prefix, $p['query']);
}, $parts);
return sprintf("\nUNION %s", implode("\nUNION ", $parts));
}
}
25 changes: 25 additions & 0 deletions tests/TestCase/Database/QueryTest.php
Expand Up @@ -1944,6 +1944,31 @@ public function testUnion()
$this->assertEquals($rows, $result->fetchAll());
}

/**
* Tests that it is possible to run unions with order statements
*
* @return void
*/
public function testUnionOrderBy()
{
$this->skipIf(
$this->connection->driver() instanceof \Cake\Database\Driver\Sqlite,
'SQLite does not support ORDER BY in UNIONed queries.'
);
$union = (new Query($this->connection))
->select(['id', 'title'])
->from(['a' => 'articles'])
->order(['a.id' => 'asc']);

$query = new Query($this->connection);
$result = $query->select(['id', 'comment'])
->from(['c' => 'comments'])
->order(['c.id' => 'asc'])
->union($union)
->execute();
$this->assertCount(self::COMMENT_COUNT + self::ARTICLE_COUNT, $result);
}

/**
* Tests that UNION ALL can be built by setting the second param of union() to true
*
Expand Down

0 comments on commit 8490c9d

Please sign in to comment.