Closed
Description
I found a problem in FunctionExpression.
Steps to reproduce
- Overview
Use subquery in SQL function. - MySQL table
CREATE TABLE `samples` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`level` int(11),
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
;
- Code
$theTable = $this->Samples;
$theQuery = $theTable->query();
$subquery = $theQuery
->select('level')
->where($theQuery->newExpr()->eq('id', 28, 'integer'))
;
$condition = $theQuery->newExpr()->gt(
'level',
$theQuery->func()->coalesce(
[$subquery, 10],
['literal', 'integer']
),
'literal'
);
$res = $theTable->find()
->select('id')
->where($condition)
->all()
;
Error
Got error as follow.
Error: SQLSTATE[42000]:
Syntax error or access violation: 1064 You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near
'SELECT Samples.level AS `Samples__level` FROM samples Samples WHERE id' at line 1
SQL query
SELECT
Samples.id AS `Samples__id`
FROM
samples Samples
WHERE
level > (COALESCE(
SELECT Samples.level AS `Samples__level` FROM samples Samples WHERE id = :c0,
:c1
))
Cause
When write subquery in SQL functions (ex: COALESCE()), must use parenthesis to the subquery expression.
error : COALESCE(subquery, 9)
correct: COALESCE((subquery), 9)
Fix
Use parenthesis to inner expression in FunctionExpression.
file: Cake\Database\Expression\FunctionExpression.php
line: 130
around the code
public function sql(ValueBinder $generator)
{
$parts = [];
foreach ($this->_conditions as $condition) {
if ($condition instanceof ExpressionInterface) {
++ $condition = sprintf('(%s)', $condition->sql($generator));
-- $condition = $condition->sql($generator);
} elseif (is_array($condition)) {
$p = $generator->placeholder('param');
$generator->bind($p, $condition['value'], $condition['type']);
$condition = $p;
}
$parts[] = $condition;
}
return $this->_name . sprintf('(%s)', implode(
$this->_conjunction . ' ',
$parts
));
}
Result
Got SQL as follow
SELECT
Samples.id AS `Samples__id`
FROM
samples Samples
WHERE
level > (COALESCE(
(SELECT Samples.level AS `Samples__level` FROM samples Samples WHERE id = 28),
'10'
))
Side effect
Maybe none, because to use parenthesis in parameters of SQL functions only.