Commit 07a48467 authored by Jonathan Vollebregt's avatar Jonathan Vollebregt

Connection: Support IS NULL in update/delete criteria

If you pass a null to the where array of update or delete it's
pretty obvious what you want.

Most (all?) databases won't match anything if you actually try
to compare with NULL making this a silent failure without it.
parent 638cb41b
......@@ -602,6 +602,36 @@ class Connection implements DriverConnection
return $this->_transactionNestingLevel > 0;
}
/**
* Gathers criteria for an update or delete call.
*
* @param array $identifiers Input array of columns to values
*
* @return string[][] a triplet with:
* - the first key being the column names
* - the second key being the values
* - the third key being the criteria strings
*/
private function gatherCriteria(array $identifiers)
{
$columns = [];
$values = [];
$criteria = [];
foreach ($identifiers as $columnName => $value) {
if (null === $value) {
$criteria[] = $this->getDatabasePlatform()->getIsNullExpression($columnName);
continue;
}
$columns[] = $columnName;
$values[] = $value;
$criteria[] = $columnName . ' = ?';
}
return [$columns, $values, $criteria];
}
/**
* Executes an SQL DELETE statement on a table.
*
......@@ -621,15 +651,7 @@ class Connection implements DriverConnection
throw InvalidArgumentException::fromEmptyCriteria();
}
$columnList = array();
$criteria = array();
$paramValues = array();
foreach ($identifier as $columnName => $value) {
$columnList[] = $columnName;
$criteria[] = $columnName . ' = ?';
$paramValues[] = $value;
}
list($columnList, $paramValues, $criteria) = $this->gatherCriteria($identifier);
return $this->executeUpdate(
'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $criteria),
......@@ -694,7 +716,6 @@ class Connection implements DriverConnection
{
$columnList = array();
$set = array();
$criteria = array();
$paramValues = array();
foreach ($data as $columnName => $value) {
......@@ -703,11 +724,9 @@ class Connection implements DriverConnection
$paramValues[] = $value;
}
foreach ($identifier as $columnName => $value) {
$columnList[] = $columnName;
$criteria[] = $columnName . ' = ?';
$paramValues[] = $value;
}
list($whereColumns, $whereValues, $criteria) = $this->gatherCriteria($identifier);
$columnList = array_merge($columnList, $whereColumns);
$paramValues = array_merge($paramValues, $whereValues);
if (is_string(key($types))) {
$types = $this->extractTypeValues($columnList, $types);
......
......@@ -408,6 +408,98 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
);
}
/**
* @group DBAL-2688
*/
public function testUpdateWithIsNull()
{
$driverMock = $this->createMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = $this->getMockBuilder('Doctrine\DBAL\Connection')
->setMethods(array('executeUpdate'))
->setConstructorArgs(array(array('platform' => new Mocks\MockPlatform()), $driverMock))
->getMock();
$conn->expects($this->once())
->method('executeUpdate')
->with(
'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?',
[
'some text',
null,
'foo',
],
[
'string',
'boolean',
'string',
]
);
$conn->update(
'TestTable',
[
'text' => 'some text',
'is_edited' => null,
],
[
'id' => null,
'name' => 'foo',
],
[
'text' => 'string',
'is_edited' => 'boolean',
'id' => 'integer',
'name' => 'string',
]
);
}
/**
* @group DBAL-2688
*/
public function testDeleteWithIsNull()
{
$driverMock = $this->createMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = $this->getMockBuilder('Doctrine\DBAL\Connection')
->setMethods(array('executeUpdate'))
->setConstructorArgs(array(array('platform' => new Mocks\MockPlatform()), $driverMock))
->getMock();
$conn->expects($this->once())
->method('executeUpdate')
->with(
'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
[
'foo',
],
[
'string',
]
);
$conn->delete(
'TestTable',
[
'id' => null,
'name' => 'foo',
],
[
'id' => 'integer',
'name' => 'string',
]
);
}
public function testFetchAssoc()
{
$statement = 'SELECT * FROM foo WHERE bar = ?';
......
......@@ -285,4 +285,44 @@ class WriteTest extends \Doctrine\Tests\DbalFunctionalTestCase
}
/**
* @group DBAL-2688
*/
public function testUpdateWhereIsNull()
{
$this->_conn->insert(
'write_table',
['test_int' => '30', 'test_string' => null],
['test_string' => 'string', 'test_int' => 'integer']
);
$data = $this->_conn->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
$this->assertCount(1, $data);
$this->_conn->update('write_table', ['test_int' => 10], ['test_string' => null], ['test_string' => 'string', 'test_int' => 'integer']);
$data = $this->_conn->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
$this->assertCount(0, $data);
}
public function testDeleteWhereIsNull()
{
$this->_conn->insert(
'write_table',
['test_int' => '30', 'test_string' => null],
['test_string' => 'string', 'test_int' => 'integer']
);
$data = $this->_conn->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
$this->assertCount(1, $data);
$this->_conn->delete('write_table', ['test_string' => null], ['test_string' => 'string']);
$data = $this->_conn->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
$this->assertCount(0, $data);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment