Introduce IDBConnection::setValues()
setValues() attempts to insert a new row, or failing that, update an existing row. The ability to set preconditions is also available.
This commit is contained in:
parent
e4d5229940
commit
88cd615214
|
@ -205,57 +205,28 @@ class AllConfig implements \OCP\IConfig {
|
|||
// TODO - FIXME
|
||||
$this->fixDIInit();
|
||||
|
||||
// Check if the key does exist
|
||||
$sql = 'SELECT `configvalue` FROM `*PREFIX*preferences` '.
|
||||
'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
|
||||
$result = $this->connection->executeQuery($sql, array($userId, $appName, $key));
|
||||
$oldValue = $result->fetchColumn();
|
||||
$result->closeCursor();
|
||||
$exists = $oldValue !== false;
|
||||
|
||||
if($oldValue === strval($value)) {
|
||||
// no changes
|
||||
return;
|
||||
$preconditionArray = [];
|
||||
if (isset($preCondition)) {
|
||||
$preconditionArray = [
|
||||
'configvalue' => $preCondition,
|
||||
];
|
||||
}
|
||||
|
||||
$affectedRows = 0;
|
||||
if (!$exists && $preCondition === null) {
|
||||
$this->connection->insertIfNotExist('*PREFIX*preferences', [
|
||||
'configvalue' => $value,
|
||||
'userid' => $userId,
|
||||
'appid' => $appName,
|
||||
'configkey' => $key,
|
||||
], ['configkey', 'userid', 'appid']);
|
||||
$affectedRows = 1;
|
||||
} elseif ($exists) {
|
||||
$data = array($value, $userId, $appName, $key);
|
||||
|
||||
$sql = 'UPDATE `*PREFIX*preferences` SET `configvalue` = ? '.
|
||||
'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? ';
|
||||
|
||||
if($preCondition !== null) {
|
||||
if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
//oracle hack: need to explicitly cast CLOB to CHAR for comparison
|
||||
$sql .= 'AND to_char(`configvalue`) = ?';
|
||||
} else {
|
||||
$sql .= 'AND `configvalue` = ?';
|
||||
}
|
||||
$data[] = $preCondition;
|
||||
}
|
||||
$affectedRows = $this->connection->executeUpdate($sql, $data);
|
||||
}
|
||||
$this->connection->setValues('preferences', [
|
||||
'userid' => $userId,
|
||||
'appid' => $appName,
|
||||
'configkey' => $key,
|
||||
], [
|
||||
'configvalue' => $value,
|
||||
], $preconditionArray);
|
||||
|
||||
// only add to the cache if we already loaded data for the user
|
||||
if ($affectedRows > 0 && isset($this->userCache[$userId])) {
|
||||
if (isset($this->userCache[$userId])) {
|
||||
if (!isset($this->userCache[$userId][$appName])) {
|
||||
$this->userCache[$userId][$appName] = array();
|
||||
}
|
||||
$this->userCache[$userId][$appName][$key] = $value;
|
||||
}
|
||||
|
||||
if ($preCondition !== null && $affectedRows === 0) {
|
||||
throw new PreConditionNotMetException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -146,6 +146,21 @@ class Db implements IDb {
|
|||
return $this->connection->insertIfNotExist($table, $input, $compare);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a row value
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $keys (column name => value)
|
||||
* @param array $values (column name => value)
|
||||
* @param array $updatePreconditionValues ensure values match preconditions (column name => value)
|
||||
* @return int number of new rows
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws PreconditionNotMetException
|
||||
*/
|
||||
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
|
||||
return $this->connection->setValues($table, $keys, $values, $updatePreconditionValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a transaction
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ use Doctrine\Common\EventManager;
|
|||
use OC\DB\QueryBuilder\ExpressionBuilder;
|
||||
use OC\DB\QueryBuilder\QueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\PreconditionNotMetException;
|
||||
|
||||
class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
|
||||
/**
|
||||
|
@ -241,6 +242,52 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
|
|||
return $this->adapter->insertIfNotExist($table, $input, $compare);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a row value
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $keys (column name => value)
|
||||
* @param array $values (column name => value)
|
||||
* @param array $updatePreconditionValues ensure values match preconditions (column name => value)
|
||||
* @return int number of new rows
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws PreconditionNotMetException
|
||||
*/
|
||||
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
|
||||
try {
|
||||
$insertQb = $this->getQueryBuilder();
|
||||
$insertQb->insert($table)
|
||||
->values(
|
||||
array_map(function($value) use ($insertQb) {
|
||||
return $insertQb->createNamedParameter($value);
|
||||
}, array_merge($keys, $values))
|
||||
);
|
||||
return $insertQb->execute();
|
||||
} catch (\Doctrine\DBAL\Exception\UniqueConstraintViolationException $e) {
|
||||
// value already exists, try update
|
||||
$updateQb = $this->getQueryBuilder();
|
||||
$updateQb->update($table);
|
||||
foreach ($values as $name => $value) {
|
||||
$updateQb->set($name, $updateQb->createNamedParameter($value));
|
||||
}
|
||||
$where = $updateQb->expr()->andx();
|
||||
$whereValues = array_merge($keys, $updatePreconditionValues);
|
||||
foreach ($whereValues as $name => $value) {
|
||||
$where->add($updateQb->expr()->eq(
|
||||
$name, $updateQb->createNamedParameter($value)
|
||||
));
|
||||
}
|
||||
$updateQb->where($where);
|
||||
$affected = $updateQb->execute();
|
||||
|
||||
if ($affected === 0) {
|
||||
throw new PreconditionNotMetException();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the error code and message as a string for logging
|
||||
* works with DoctrineException
|
||||
|
|
|
@ -108,6 +108,20 @@ interface IDBConnection {
|
|||
*/
|
||||
public function insertIfNotExist($table, $input, array $compare = null);
|
||||
|
||||
/**
|
||||
* Insert or update a row value
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $keys (column name => value)
|
||||
* @param array $values (column name => value)
|
||||
* @param array $updatePreconditionValues ensure values match preconditions (column name => value)
|
||||
* @return int number of new rows
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws PreconditionNotMetException
|
||||
* @since 9.0.0
|
||||
*/
|
||||
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []);
|
||||
|
||||
/**
|
||||
* Start a transaction
|
||||
* @since 6.0.0
|
||||
|
|
|
@ -90,16 +90,7 @@ class TestAllConfig extends \Test\TestCase {
|
|||
}
|
||||
|
||||
public function testSetUserValueWithPreCondition() {
|
||||
// mock the check for the database to run the correct SQL statements for each database type
|
||||
$systemConfig = $this->getMockBuilder('\OC\SystemConfig')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$systemConfig->expects($this->once())
|
||||
->method('getValue')
|
||||
->with($this->equalTo('dbtype'),
|
||||
$this->equalTo('sqlite'))
|
||||
->will($this->returnValue(\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite')));
|
||||
$config = $this->getConfig($systemConfig);
|
||||
$config = $this->getConfig();
|
||||
|
||||
$selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
|
||||
|
||||
|
@ -136,16 +127,7 @@ class TestAllConfig extends \Test\TestCase {
|
|||
* @expectedException \OCP\PreConditionNotMetException
|
||||
*/
|
||||
public function testSetUserValueWithPreConditionFailure() {
|
||||
// mock the check for the database to run the correct SQL statements for each database type
|
||||
$systemConfig = $this->getMockBuilder('\OC\SystemConfig')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$systemConfig->expects($this->once())
|
||||
->method('getValue')
|
||||
->with($this->equalTo('dbtype'),
|
||||
$this->equalTo('sqlite'))
|
||||
->will($this->returnValue(\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite')));
|
||||
$config = $this->getConfig($systemConfig);
|
||||
$config = $this->getConfig();
|
||||
|
||||
$selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
|
||||
|
||||
|
|
|
@ -25,20 +25,17 @@ class Connection extends \Test\TestCase {
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
public static function setUpBeforeClass() {
|
||||
self::dropTestTable();
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
public static function tearDownAfterClass() {
|
||||
self::dropTestTable();
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
protected static function dropTestTable()
|
||||
{
|
||||
protected static function dropTestTable() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') !== 'oci') {
|
||||
\OC::$server->getDatabaseConnection()->dropTable('table');
|
||||
}
|
||||
|
@ -92,4 +89,89 @@ class Connection extends \Test\TestCase {
|
|||
$this->connection->dropTable('table');
|
||||
$this->assertTableNotExist('table');
|
||||
}
|
||||
|
||||
private function getTextValueByIntergerField($integerField) {
|
||||
$builder = $this->connection->getQueryBuilder();
|
||||
$query = $builder->select('textfield')
|
||||
->from('table')
|
||||
->where($builder->expr()->eq('integerfield', $builder->createNamedParameter($integerField, \PDO::PARAM_INT)));
|
||||
|
||||
$result = $query->execute();
|
||||
return $result->fetchColumn();
|
||||
}
|
||||
|
||||
public function testSetValues() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo'
|
||||
]);
|
||||
|
||||
$this->assertEquals('foo', $this->getTextValueByIntergerField(1));
|
||||
|
||||
$this->connection->dropTable('table');
|
||||
}
|
||||
|
||||
public function testSetValuesOverWrite() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo'
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
]);
|
||||
|
||||
$this->assertEquals('bar', $this->getTextValueByIntergerField(1));
|
||||
|
||||
$this->connection->dropTable('table');
|
||||
}
|
||||
|
||||
public function testSetValuesOverWritePrecondition() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'booleanfield' => true
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
], [
|
||||
'booleanfield' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals('bar', $this->getTextValueByIntergerField(1));
|
||||
|
||||
$this->connection->dropTable('table');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\PreConditionNotMetException
|
||||
*/
|
||||
public function testSetValuesOverWritePreconditionFailed() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'booleanfield' => true
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
], [
|
||||
'booleanfield' => false
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue