Skip to content

Commit

Permalink
Database: implemented correct support for empty array NOT operator [R…
Browse files Browse the repository at this point in the history
…efs nette/nette#667]

Removed old behavior, which could produce malformed SQL query.
Condition must have parentheses around all expressions with empty array.
  • Loading branch information
hrach authored and dg committed May 17, 2013
1 parent a0c88fe commit 35155e6
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 33 deletions.
26 changes: 8 additions & 18 deletions src/Database/Table/SqlBuilder.php
Expand Up @@ -273,24 +273,14 @@ public function addWhere($condition, $parameters = array())

if ($arg !== NULL) {
if (!$arg) {
$placholderPosition = $match[1][1] + strlen($match[1][0]);
$conditionPrefix = substr($condition, 0, $placholderPosition);
$conditionSuffix = substr($condition, $placholderPosition);
$conditionPrefix = preg_replace('#(?!OR|AND)([^\(]*)(NOT\s*)([^\(]*)$#s', '\1\3', $conditionPrefix, 1, $notCount);
if ($notCount) {
$match[1][1] -= strpos($match[1][0], 'NOT') !== FALSE ? 0 : $placholderPosition - strlen($conditionPrefix);
if (!isset($addedParentheses)) {
$addedParentheses = TRUE;
$conditionPrefixLen = strlen($conditionPrefix);
list($conditionPrefix, $conditionSuffix) = str_replace(array('(', ')'), array('((', '))'), array($conditionPrefix, $conditionSuffix));
list($conditionPrefix, $conditionSuffix) = preg_replace('#\s*(AND|OR)\s*#', ') \1 (', array($conditionPrefix, $conditionSuffix), -1, $andCount);
$condition = $conditionPrefix . $conditionSuffix;
if ($andCount) {
$match[1][1] += strlen($conditionPrefix) - $conditionPrefixLen + 1;
$condition = "($condition)";
}
$match[1][0] = '?';
}
$hasBrackets = strpos($condition, '(') !== FALSE;
$hasOperators = preg_match('#AND|OR#', $condition);
$hasNot = strpos($condition, 'NOT') !== FALSE;
$hasPrefixNot = strpos($match[2][0], 'NOT') !== FALSE;
if (!$hasBrackets && ($hasOperators || ($hasNot && !$hasPrefixNot))) {
throw new Nette\InvalidArgumentException('Possible SQL query corruption. Add parentheses around operators.');
}
if ($hasPrefixNot) {
$replace = 'IS NULL OR TRUE';
} else {
$replace = 'IS NULL AND FALSE';
Expand Down
24 changes: 9 additions & 15 deletions tests/Database/SqlBuilder.addWhere().phpt
Expand Up @@ -52,14 +52,14 @@ $sqlBuilder[5]->addWhere('id ? OR id ? OR id ?', 1, "test", array(1, 2));
$sqlBuilder[6] = new SqlBuilder('book', $connection, $reflection);
$sqlBuilder[6]->addWhere('id', array());
$sqlBuilder[6]->addWhere('id NOT', array());
$sqlBuilder[6]->addWhere('NOT id', array());
$sqlBuilder[6]->addWhere('TRUE AND NOT id ? AND TRUE', array());
$sqlBuilder[6]->addWhere('TRUE AND id NOT ? AND TRUE', array());
$sqlBuilder[6]->addWhere('NOT id ? AND id NOT ?', array(), array());
$sqlBuilder[6]->addWhere('id NOT ? OR id = 3', array());
$sqlBuilder[6]->addWhere('id = 3 OR id = 2 AND id NOT ?', array());
$sqlBuilder[6]->addWhere('NOT (id ? OR id)', array());
$sqlBuilder[6]->addWhere('NOT (id NOT ? OR id)', array());
$sqlBuilder[6]->addWhere('NOT (id ?)', array());

Assert::throws(function() use ($sqlBuilder) {
$sqlBuilder[6]->addWhere('TRUE AND id', array());
}, 'Nette\InvalidArgumentException', 'Possible SQL query corruption. Add parentheses around operators.');
Assert::throws(function() use ($sqlBuilder) {
$sqlBuilder[6]->addWhere('NOT id', array());
}, 'Nette\InvalidArgumentException', 'Possible SQL query corruption. Add parentheses around operators.');

// backward compatibility
$sqlBuilder[7] = new SqlBuilder('book', $connection, $reflection);
Expand Down Expand Up @@ -88,13 +88,7 @@ $sqlBuilder[10]->addWhere('id NOT', $dao->table('book')->select('id'));
Assert::same(reformat('SELECT * FROM [book] WHERE ([id] = ? OR [id] IS NULL)'), $sqlBuilder[0]->buildSelectQuery());
Assert::same(reformat('SELECT * FROM [book] WHERE ([id] IN (?))'), $sqlBuilder[4]->buildSelectQuery());
Assert::same(reformat('SELECT * FROM [book] WHERE ([id] = ? OR [id] = ? OR [id] IN (?))'), $sqlBuilder[5]->buildSelectQuery());
Assert::equal(reformat(
'SELECT * FROM [book] WHERE ([id] IS NULL AND FALSE) AND ([id] IS NULL OR TRUE) AND ([id] IS NULL OR TRUE) ' .
'AND ((TRUE) AND ([id] IS NULL OR TRUE) AND (TRUE)) AND ((TRUE) AND ([id] IS NULL OR TRUE) AND (TRUE)) ' .
'AND (([id] IS NULL OR TRUE) AND ([id] IS NULL OR TRUE)) AND (([id] IS NULL OR TRUE) OR ([id] = 3)) ' .
'AND (([id] = 3) OR ([id] = 2) AND ([id] IS NULL OR TRUE)) AND (NOT ([id] IS NULL AND FALSE OR [id])) ' .
'AND ((NOT (([id] IS NULL OR TRUE) OR ([id]))))'
), $sqlBuilder[6]->buildSelectQuery());
Assert::same(reformat('SELECT * FROM [book] WHERE ([id] IS NULL AND FALSE) AND ([id] IS NULL OR TRUE) AND (NOT ([id] IS NULL AND FALSE))'), $sqlBuilder[6]->buildSelectQuery());
Assert::same(reformat('SELECT * FROM [book] WHERE ([id] = ? OR [id] = ? OR [id] IN (?) OR [id] LIKE ? OR [id] > ?) AND ([name] = ?) AND (MAIN = ?)'), $sqlBuilder[7]->buildSelectQuery());
Assert::same(reformat('SELECT * FROM [book] WHERE (FOO(?)) AND (FOO([id], ?)) AND ([id] & ? = ?) AND (?) AND (NOT ? OR ?) AND (? + ? - ? / ? * ? % ?)'), $sqlBuilder[8]->buildSelectQuery());
Assert::same(reformat("SELECT * FROM [book] WHERE ([col1] = ?\nOR [col2] = ?)"), $sqlBuilder[9]->buildSelectQuery());
Expand Down

0 comments on commit 35155e6

Please sign in to comment.