Add a method to lock a table

This commit is contained in:
Joas Schilling 2016-05-19 12:34:40 +02:00
parent 59a85a4c76
commit 7e3ce83526
No known key found for this signature in database
GPG Key ID: 70A0B324C41C0946
6 changed files with 110 additions and 2 deletions

View File

@ -29,6 +29,7 @@ namespace OC\AppFramework\Db;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDb;
use OCP\IDBConnection;
use OCP\PreConditionNotMetException;
/**
* @deprecated use IDBConnection directly, will be removed in ownCloud 10
@ -157,12 +158,26 @@ class Db implements IDb {
* @param array $updatePreconditionValues ensure values match preconditions (column name => value)
* @return int number of new rows
* @throws \Doctrine\DBAL\DBALException
* @throws PreconditionNotMetException
* @throws PreConditionNotMetException
*/
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
return $this->connection->setValues($table, $keys, $values, $updatePreconditionValues);
}
/**
* @inheritdoc
*/
public function lockTable($tableName) {
$this->connection->lockTable($tableName);
}
/**
* @inheritdoc
*/
public function unlockTable() {
$this->connection->unlockTable();
}
/**
* Start a transaction
*/

View File

@ -57,6 +57,26 @@ class Adapter {
return $statement;
}
/**
* Create an exclusive read+write lock on a table
*
* @param string $tableName
* @since 9.1.0
*/
public function lockTable($tableName) {
$this->conn->beginTransaction();
$this->conn->executeUpdate('LOCK TABLE `' .$tableName . '` IN EXCLUSIVE MODE');
}
/**
* Release a previous acquired lock again
*
* @since 9.1.0
*/
public function unlockTable() {
$this->conn->commit();
}
/**
* Insert a row if the matching row does not exists.
*

View File

@ -24,6 +24,18 @@
namespace OC\DB;
class AdapterMySQL extends Adapter {
/**
* @param string $tableName
*/
public function lockTable($tableName) {
$this->conn->executeUpdate('LOCK TABLES `' .$tableName . '` WRITE');
}
public function unlockTable() {
$this->conn->executeUpdate('UNLOCK TABLES');
}
public function fixupStatement($statement) {
$statement = str_replace(' ILIKE ', ' COLLATE utf8_general_ci LIKE ', $statement);
return $statement;

View File

@ -27,6 +27,18 @@
namespace OC\DB;
class AdapterSqlite extends Adapter {
/**
* @param string $tableName
*/
public function lockTable($tableName) {
$this->conn->executeUpdate('BEGIN EXCLUSIVE TRANSACTION');
}
public function unlockTable() {
$this->conn->executeUpdate('COMMIT TRANSACTION');
}
public function fixupStatement($statement) {
$statement = preg_replace('( I?LIKE \?)', '$0 ESCAPE \'\\\'', $statement);
$statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement);

View File

@ -25,6 +25,7 @@
*/
namespace OC\DB;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Configuration;
@ -46,6 +47,8 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
*/
protected $adapter;
protected $lockedTable = null;
public function connect() {
try {
return parent::connect();
@ -281,7 +284,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
foreach ($values as $name => $value) {
$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
}
$where = $updateQb->expr()->andx();
$where = $updateQb->expr()->andX();
$whereValues = array_merge($keys, $updatePreconditionValues);
foreach ($whereValues as $name => $value) {
$where->add($updateQb->expr()->eq(
@ -301,6 +304,33 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
}
}
/**
* Create an exclusive read+write lock on a table
*
* @param string $tableName
* @throws \BadMethodCallException When trying to acquire a second lock
* @since 9.1.0
*/
public function lockTable($tableName) {
if ($this->lockedTable !== null) {
throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
}
$tableName = $this->tablePrefix . $tableName;
$this->lockedTable = $tableName;
$this->adapter->lockTable($tableName);
}
/**
* Release a previous acquired lock again
*
* @since 9.1.0
*/
public function unlockTable() {
$this->adapter->unlockTable();
$this->lockedTable = null;
}
/**
* returns the error code and message as a string for logging
* works with DoctrineException

View File

@ -124,6 +124,25 @@ interface IDBConnection {
*/
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []);
/**
* Create an exclusive read+write lock on a table
*
* Important Note: Due to the nature how locks work on different DBs, it is
* only possible to lock one table at a time. You should also NOT start a
* transaction while holding a lock.
*
* @param string $tableName
* @since 9.1.0
*/
public function lockTable($tableName);
/**
* Release a previous acquired lock again
*
* @since 9.1.0
*/
public function unlockTable();
/**
* Start a transaction
* @since 6.0.0