Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Must use parenthesis to params of SQL function in FunctionExpression #6733

Closed
@StewEucen

Description

@StewEucen

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions