ZF-5372: Wrong SQL generation in Zend_Db_Select::joinUsing()
Description
The SQL generated by Zend_Db_Select::joinUsing() when joining by an array of columns is totally broken.
Sample code to reproduce:
$db = Zend_Db::factory('Pdo_Mysql', array('dbname' => 'test', 'username' => 'root', 'password' => ''));
$select = new Zend_Db_Select($db);
$select->from('table_A')->joinUsing('tableB', array('col_one', 'col_two'));
// Outputs:
// SELECT `table_A`.*, `tableB`.* FROM `table_A` INNER JOIN `tableB` ON `tableB`.Array = `table_A`.Array
echo (string) $select;
Notice the columns being used on the ON clause: ON tableB.{color:red}Array{color} = table_A.{color:red} Array{color}
This is a quick patch that solves the issue:
Index: Zend/Db/Select.php
===================================================================
--- Zend/Db/Select.php (revision 13462)
+++ Zend/Db/Select.php (working copy)
@@ -809,9 +809,17 @@
$join = $this->_adapter->quoteIdentifier(key($this->_parts[self::FROM]), true);
$from = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($name), true);
- $cond1 = $from . '.' . $cond;
- $cond2 = $join . '.' . $cond;
- $cond = $cond1 . ' = ' . $cond2;
+ if (is_scalar($cond)) {
+ $cond1 = $from . '.' . $cond;
+ $cond2 = $join . '.' . $cond;
+ $cond = $cond1 . ' = ' . $cond2;
+ } else {
+ $conds = array();
+ foreach ($cond as $c) {
+ $conds[] = $from . '.' . $c . ' = ' . $join . '.' . $c;
+ }
+ $cond = implode(' AND ', $conds);
+ }
return $this->_join($type, $name, $cond, $cols, $schema);
}
Sample code output after applying above patch::
// Outputs:
// SELECT `table_A`.*, `tableB`.* FROM `table_A` INNER JOIN `tableB` ON `tableB`.col_one = `table_A`.col_one AND `tableB`.col_two = `table_A`.col_two
It would be better if the generated SQL was using USING though.
Comments
Posted by Mickael Perraud (mikaelkael) on 2008-12-29T05:41:26.000+0000
It isn't a bug but an improvement because joinUsing wasn't designed to function with an array.
Posted by Daniel Berstein (danielb) on 2008-12-29T06:43:43.000+0000
So may I ask what's the purpose of the joinUsing family of methods? I fail to see their usefulness.
Well then I would suggest to complete remove it, it serves no purpose: a) It does not generate a USING clause (see [http://framework.zend.com/issues/browse/ZF-3792]). b) It does not simplify code by mapping a column-list array into the proper AND'ed expressions (like what this issue is about).
I would then label this bug as a design bug, but a bug nevertheless.
Posted by Mickael Perraud (mikaelkael) on 2008-12-29T07:08:28.000+0000
I understand what you want, but this is the phpDoc associated to _joinUsing:
Perhaps, joinUsing isn't a good name for this function ;).
Posted by Daniel Berstein (danielb) on 2008-12-29T10:07:55.000+0000
Thanks for showing me the "fine print" ;)
How can I help to fix it? The patch in the issue (massaging parameter $cond) at least gives some utility to the method(s) IMO. Don't you agree?
After further checking, the implementation of joinUsing() is broken in another way too: table aliases are not being respected!
You would assume the generated SQL to be:
But you would be wrong, the actual SQL generated is:
Here is the less intrusive patch I could think of that solves this second problem:
Posted by Niels Lensink (nielslensink) on 2009-03-10T02:23:20.000+0000
The following join method in 1.7.5 still generates wrong SQL code
->joinUsing(array('b' => 'table_b'), 'col_X');
Posted by Sonntag (sunday) on 2010-07-09T08:09:25.000+0000
Any news here?
Posted by Kim Blomqvist (kblomqvist) on 2011-08-20T17:46:14.000+0000
bq. Any news here?
Yes there are - fixed in trunk, see ZF-3309
Posted by Adam Lundrigan (adamlundrigan) on 2011-10-17T01:28:06.000+0000
@[~danielb] The table alias problem is being worked on in ZF-3309. The currently-agreed-upon solution is very similar to what you have proposed.
However, the original issue reported in this ticket has not been resolved. @[~ralph]: Is this something we want to add in ZFv1 at this stage? ie:
Posted by Adam Lundrigan (adamlundrigan) on 2012-05-05T02:36:40.000+0000
This will be addressed in ZF2 Zend\Db