Commit 03e17189 authored by Marco Pivetta's avatar Marco Pivetta Committed by GitHub

Merge pull request #2688 from jnvsor/isnull

Connection: Support IS NULL in update/delete criteria
parents 638cb41b f4437acb
......@@ -602,6 +602,36 @@ class Connection implements DriverConnection
return $this->_transactionNestingLevel > 0;
}
/**
* Gathers conditions 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 columns
* - the second key being the values
* - the third key being the conditions
*/
private function gatherConditions(array $identifiers)
{
$columns = [];
$values = [];
$conditions = [];
foreach ($identifiers as $columnName => $value) {
if (null === $value) {
$conditions[] = $this->getDatabasePlatform()->getIsNullExpression($columnName);
continue;
}
$columns[] = $columnName;
$values[] = $value;
$conditions[] = $columnName . ' = ?';
}
return [$columns, $values, $conditions];
}
/**
* Executes an SQL DELETE statement on a table.
*
......@@ -621,20 +651,12 @@ 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($columns, $values, $conditions) = $this->gatherConditions($identifier);
return $this->executeUpdate(
'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $criteria),
$paramValues,
is_string(key($types)) ? $this->extractTypeValues($columnList, $types) : $types
'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $conditions),
$values,
is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types
);
}
......@@ -692,31 +714,28 @@ class Connection implements DriverConnection
*/
public function update($tableExpression, array $data, array $identifier, array $types = array())
{
$columnList = array();
$setColumns = array();
$setValues = array();
$set = array();
$criteria = array();
$paramValues = array();
foreach ($data as $columnName => $value) {
$columnList[] = $columnName;
$setColumns[] = $columnName;
$setValues[] = $value;
$set[] = $columnName . ' = ?';
$paramValues[] = $value;
}
foreach ($identifier as $columnName => $value) {
$columnList[] = $columnName;
$criteria[] = $columnName . ' = ?';
$paramValues[] = $value;
}
list($conditionColumns, $conditionValues, $conditions) = $this->gatherConditions($identifier);
$columns = array_merge($setColumns, $conditionColumns);
$values = array_merge($setValues, $conditionValues);
if (is_string(key($types))) {
$types = $this->extractTypeValues($columnList, $types);
$types = $this->extractTypeValues($columns, $types);
}
$sql = 'UPDATE ' . $tableExpression . ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' AND ', $criteria);
. ' WHERE ' . implode(' AND ', $conditions);
return $this->executeUpdate($sql, $paramValues, $types);
return $this->executeUpdate($sql, $values, $types);
}
/**
......@@ -736,21 +755,21 @@ class Connection implements DriverConnection
return $this->executeUpdate('INSERT INTO ' . $tableExpression . ' ()' . ' VALUES ()');
}
$columnList = array();
$paramPlaceholders = array();
$paramValues = array();
$columns = array();
$values = array();
$set = array();
foreach ($data as $columnName => $value) {
$columnList[] = $columnName;
$paramPlaceholders[] = '?';
$paramValues[] = $value;
$columns[] = $columnName;
$values[] = $value;
$set[] = '?';
}
return $this->executeUpdate(
'INSERT INTO ' . $tableExpression . ' (' . implode(', ', $columnList) . ')' .
' VALUES (' . implode(', ', $paramPlaceholders) . ')',
$paramValues,
is_string(key($types)) ? $this->extractTypeValues($columnList, $types) : $types
'INSERT INTO ' . $tableExpression . ' (' . implode(', ', $columns) . ')' .
' VALUES (' . implode(', ', $set) . ')',
$values,
is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types
);
}
......
......@@ -34,6 +34,22 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
$this->_conn = \Doctrine\DBAL\DriverManager::getConnection($this->params);
}
public function getExecuteUpdateMockConnection()
{
$driverMock = $this->createMock(\Doctrine\DBAL\Driver::class);
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = $this->getMockBuilder(Connection::class)
->setMethods(['executeUpdate'])
->setConstructorArgs([['platform' => new Mocks\MockPlatform()], $driverMock])
->getMock();
return $conn;
}
public function testIsConnected()
{
$this->assertFalse($this->_conn->isConnected());
......@@ -285,16 +301,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
public function testEmptyInsert()
{
$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 = $this->getExecuteUpdateMockConnection();
$conn->expects($this->once())
->method('executeUpdate')
......@@ -308,16 +315,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
*/
public function testUpdateWithDifferentColumnsInDataAndIdentifiers()
{
$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 = $this->getExecuteUpdateMockConnection();
$conn->expects($this->once())
->method('executeUpdate')
......@@ -361,16 +359,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
*/
public function testUpdateWithSameColumnInDataAndIdentifiers()
{
$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 = $this->getExecuteUpdateMockConnection();
$conn->expects($this->once())
->method('executeUpdate')
......@@ -408,6 +397,80 @@ class ConnectionTest extends \Doctrine\Tests\DbalTestCase
);
}
/**
* @group DBAL-2688
*/
public function testUpdateWithIsNull()
{
$conn = $this->getExecuteUpdateMockConnection();
$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()
{
$conn = $this->getExecuteUpdateMockConnection();
$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