Skip to content

Commit

Permalink
Add update with join support for Postgres.
Browse files Browse the repository at this point in the history
Postgres requires some query fudging to make joins with updates
syntactically valid. Add a query translator that moves joins around and
creates a 'from' clause.
  • Loading branch information
markstory committed Jun 20, 2013
1 parent 538840b commit 7f19f76
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
36 changes: 36 additions & 0 deletions lib/Cake/Database/Dialect/PostgresDialectTrait.php
Expand Up @@ -80,6 +80,42 @@ protected function _selectQueryTranslator($query) {
return $query;
}

/**
* Returns an update query that has been transformed for Postgres.
*
* Postgres requires joins to be defined in the FROM list instead of
* as standard joins. This translator will erase joins and replace them with
* an expression object in the FROM clause.
*
* @param Cake\Database\Query $query
* @return Cake\Database\Query
*/
protected function _updateQueryTranslator($query) {
$joins = $query->clause('join');
if ($joins) {
$sql = '';
foreach ($joins as $i => $join) {
if ($i == 0) {
$sql .= sprintf('%s %s', $join['table'], $join['alias']);
if (isset($join['conditions']) && count($join['conditions'])) {
$query->where($join['conditions']);
}
continue;
}
$sql .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']);
if (isset($join['conditions']) && count($join['conditions'])) {
$sql .= sprintf(' ON %s', $join['conditions']);
} else {
$sql .= ' ON 1 = 1';
}
}
$expr = $query->newExpr()->add($sql);
$query->join([], [], true);
$query->from([$expr]);
}
return $query;
}

/**
* Returns a function that will be used as a callback for a results decorator.
* this function is responsible for deleting the artificial column in results
Expand Down
8 changes: 6 additions & 2 deletions lib/Cake/Database/Query.php
Expand Up @@ -291,7 +291,7 @@ protected function _traverseDelete(callable $visitor) {
* @return void
*/
protected function _traverseUpdate(callable $visitor) {
$parts = ['update', 'join', 'set', 'where'];
$parts = ['update', 'join', 'set', 'from', 'where'];
foreach ($parts as $name) {
call_user_func($visitor, $this->_parts[$name], $name);
}
Expand Down Expand Up @@ -597,7 +597,6 @@ public function join($tables = null, $types = [], $overwrite = false) {
if (!($t['conditions']) instanceof ExpressionInterface) {
$t['conditions'] = $this->newExpr()->add($t['conditions'], $types);
}

$joins[] = $t + ['type' => 'INNER', 'alias' => is_string($alias) ? $alias : null];
}

Expand All @@ -623,6 +622,11 @@ public function join($tables = null, $types = [], $overwrite = false) {
protected function _buildJoinPart($parts) {
$joins = '';
foreach ($parts as $join) {
if ($join instanceof ExpressionInterface) {
$joins .= $join->sql();
continue;
}

$joins .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']);
if (isset($join['conditions']) && count($join['conditions'])) {
$joins .= sprintf(' ON %s', $join['conditions']);
Expand Down
25 changes: 25 additions & 0 deletions lib/Cake/Test/TestCase/Database/Driver/PostgresTest.php
Expand Up @@ -20,6 +20,7 @@
use Cake\Core\Configure;
use Cake\Database\Connection;
use Cake\Database\Driver\Postgres;
use Cake\Database\Query;
use \PDO;

/**
Expand Down Expand Up @@ -135,4 +136,28 @@ public function testConnectionConfigCustom() {
$driver->connect();
}

public function testUpdateWithJoin() {
$driver = $this->getMock('Cake\Database\driver\Postgres', ['_connect', 'connection']);
$connection = new Connection(Configure::read('Datasource.test'));
$connection->driver($driver);

$query = new Query($connection);

$query->update('articles')
->set('title', 'New title')
->join([
'table' => 'authors',
'alias' => 'a',
'conditions' => 'author_id = a.id'
])
->join('comments')
->where(['articles.id' => 1]);
$result = $query->sql(true);

$this->assertContains('UPDATE articles SET title = :', $result);
$this->assertContains('FROM authors a INNER JOIN comments ON 1 = 1', $result);
$this->assertContains('WHERE (articles.id = :', $result);
$this->assertContains('AND author_id = a.id)', $result);
}

}

0 comments on commit 7f19f76

Please sign in to comment.