Skip to content

Commit

Permalink
Merge pull request #3844 from cakephp/3.0-fix-notnull
Browse files Browse the repository at this point in the history
3.0 fix notnull missing identifier quoting
  • Loading branch information
markstory committed Jul 1, 2014
2 parents c4c1329 + 9b60b7d commit 0a566bd
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/Database/Dialect/SqlserverDialectTrait.php
Expand Up @@ -99,7 +99,7 @@ protected function _pagingSubquery($original, $limit, $offset) {
$query = clone $original;
$order = $query->clause('order') ?: new OrderByExpression('NULL');
$query->select([
'_cake_page_rownum_' => new UnaryExpression($order, [], 'ROW_NUMBER() OVER')
'_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order)
])->limit(null)
->offset(null)
->order([], true);
Expand Down
19 changes: 14 additions & 5 deletions src/Database/Expression/QueryExpression.php
Expand Up @@ -15,6 +15,7 @@
namespace Cake\Database\Expression;

use Cake\Database\ExpressionInterface;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\TypeMapTrait;
use Cake\Database\ValueBinder;
use \Countable;
Expand Down Expand Up @@ -201,21 +202,29 @@ public function lte($field, $value, $type = null) {
/**
* Adds a new condition to the expression object in the form "field IS NULL".
*
* @param string $field database field to be tested for null
* @param string|\Cake\Database\ExpressionInteface $field database field to be
* tested for null
* @return QueryExpression
*/
public function isNull($field) {
return $this->add($field . ' IS NULL');
if (!($field instanceof ExpressionInterface)) {
$field = new IdentifierExpression($field);
}
return $this->add(new UnaryExpression('IS NULL', $field, UnaryExpression::POSTFIX));
}

/**
* Adds a new condition to the expression object in the form "field IS NOT NULL".
*
* @param string $field database field to be tested for not null
* @param string|\Cake\Database\ExpressionInteface $field database field to be
* tested for not null
* @return QueryExpression
*/
public function isNotNull($field) {
return $this->add($field . ' IS NOT NULL');
if (!($field instanceof ExpressionInterface)) {
$field = new IdentifierExpression($field);
}
return $this->add(new UnaryExpression('IS NOT NULL', $field, UnaryExpression::POSTFIX));
}

/**
Expand Down Expand Up @@ -437,7 +446,7 @@ protected function _addConditions(array $conditions, array $types) {
}

if (strtolower($k) === 'not') {
$this->_conditions[] = new UnaryExpression(new self($c, $typeMap), [], 'NOT');
$this->_conditions[] = new UnaryExpression('NOT', new self($c, $typeMap));
continue;
}

Expand Down
73 changes: 66 additions & 7 deletions src/Database/Expression/UnaryExpression.php
Expand Up @@ -22,7 +22,53 @@
*
* @internal
*/
class UnaryExpression extends QueryExpression {
class UnaryExpression implements ExpressionInterface {

/**
* Indicates that the operation is in pre-order
*
*/
const PREFIX = 0;

/**
* Indicates that the operation is in post-order
*
*/
const POSTFIX = 1;

/**
* The operator this unary expression represents
*
* @var string
*/
protected $_operator;

/**
* Holds the value which the unary expression operates
*
* @var mixed
*/
protected $_value;

/**
* Where to place the operator
*
* @var int
*/
protected $_mode;

/**
* Constructor
*
* @param string $operator The operator to used for the expression
* @param mixed $value the value to use as the operand for the expression
* @param int $mode either UnaryExpression::PREFIX or UnaryExpression::POSTFIX
*/
public function __construct($operator, $value, $mode = self::PREFIX) {
$this->_operator = $operator;
$this->_value = $value;
$this->_mode = $mode;
}

/**
* Converts the expression to its string representation
Expand All @@ -31,12 +77,25 @@ class UnaryExpression extends QueryExpression {
* @return string
*/
public function sql(ValueBinder $generator) {
foreach ($this->_conditions as $condition) {
if ($condition instanceof ExpressionInterface) {
$condition = $condition->sql($generator);
}
// We only use the first (and only) condition
return $this->_conjunction . ' (' . $condition . ')';
$operand = $this->_value;
if ($operand instanceof ExpressionInterface) {
$operand = $operand->sql($generator);
}

if ($this->_mode === self::POSTFIX) {
return '(' . $operand . ') ' . $this->_operator;
}

return $this->_operator . ' (' . $operand . ')';
}

/**
* {@inheritDoc}
*
*/
public function traverse(callable $callable) {
if ($this->_value instanceof ExpressionInterface) {
$callable($this->_value);
}
}

Expand Down
52 changes: 52 additions & 0 deletions tests/TestCase/Database/QueryTest.php
Expand Up @@ -2484,6 +2484,58 @@ public function testDebugInfo() {
$result = $query->__debugInfo();
}

/**
* Tests that it is possible to pass ExpressionInterface to isNull and isNotNull
*
* @return void
*/
public function testIsNullWithExpressions() {
$query = new Query($this->connection);
$subquery = (new Query($this->connection))
->select(['id'])
->from('authors')
->where(['id' => 1]);

$result = $query
->select(['name'])
->from(['authors'])
->where(function($exp) use ($subquery) {
return $exp->isNotNull($subquery);
})
->execute();
$this->assertNotEmpty($result->fetchAll('assoc'));

$result = (new Query($this->connection))
->select(['name'])
->from(['authors'])
->where(function($exp) use ($subquery) {
return $exp->isNull($subquery);
})
->execute();
$this->assertEmpty($result->fetchAll('assoc'));
}

/**
* Tests that strings passed to isNull and isNotNull will be treated as identifiers
* when using autoQuoting
*
* @return void
*/
public function testIsNullAutoQuoting() {
$this->connection->driver()->autoQuoting(true);
$query = new Query($this->connection);
$query->select('*')->from('things')->where(function($exp) {
return $exp->isNull('field');
});
$this->assertQuotedQuery('WHERE \(<field>\) IS NULL', $query->sql());

$query = new Query($this->connection);
$query->select('*')->from('things')->where(function($exp) {
return $exp->isNotNull('field');
});
$this->assertQuotedQuery('WHERE \(<field>\) IS NOT NULL', $query->sql());
}

/**
* Assertion for comparing a table's contents with what is in it.
*
Expand Down

0 comments on commit 0a566bd

Please sign in to comment.