Skip to content
Snippets Groups Projects
Unverified Commit 7e3ce835 authored by Joas Schilling's avatar Joas Schilling
Browse files

Add a method to lock a table

parent 59a85a4c
No related branches found
No related tags found
No related merge requests found
...@@ -29,6 +29,7 @@ namespace OC\AppFramework\Db; ...@@ -29,6 +29,7 @@ namespace OC\AppFramework\Db;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDb; use OCP\IDb;
use OCP\IDBConnection; use OCP\IDBConnection;
use OCP\PreConditionNotMetException;
/** /**
* @deprecated use IDBConnection directly, will be removed in ownCloud 10 * @deprecated use IDBConnection directly, will be removed in ownCloud 10
...@@ -157,12 +158,26 @@ class Db implements IDb { ...@@ -157,12 +158,26 @@ class Db implements IDb {
* @param array $updatePreconditionValues ensure values match preconditions (column name => value) * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
* @return int number of new rows * @return int number of new rows
* @throws \Doctrine\DBAL\DBALException * @throws \Doctrine\DBAL\DBALException
* @throws PreconditionNotMetException * @throws PreConditionNotMetException
*/ */
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
return $this->connection->setValues($table, $keys, $values, $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 * Start a transaction
*/ */
......
...@@ -57,6 +57,26 @@ class Adapter { ...@@ -57,6 +57,26 @@ class Adapter {
return $statement; 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. * Insert a row if the matching row does not exists.
* *
......
...@@ -24,6 +24,18 @@ ...@@ -24,6 +24,18 @@
namespace OC\DB; namespace OC\DB;
class AdapterMySQL extends Adapter { 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) { public function fixupStatement($statement) {
$statement = str_replace(' ILIKE ', ' COLLATE utf8_general_ci LIKE ', $statement); $statement = str_replace(' ILIKE ', ' COLLATE utf8_general_ci LIKE ', $statement);
return $statement; return $statement;
......
...@@ -27,6 +27,18 @@ ...@@ -27,6 +27,18 @@
namespace OC\DB; namespace OC\DB;
class AdapterSqlite extends Adapter { 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) { public function fixupStatement($statement) {
$statement = preg_replace('( I?LIKE \?)', '$0 ESCAPE \'\\\'', $statement); $statement = preg_replace('( I?LIKE \?)', '$0 ESCAPE \'\\\'', $statement);
$statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement); $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
*/ */
namespace OC\DB; namespace OC\DB;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Configuration;
...@@ -46,6 +47,8 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { ...@@ -46,6 +47,8 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
*/ */
protected $adapter; protected $adapter;
protected $lockedTable = null;
public function connect() { public function connect() {
try { try {
return parent::connect(); return parent::connect();
...@@ -281,7 +284,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { ...@@ -281,7 +284,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
foreach ($values as $name => $value) { foreach ($values as $name => $value) {
$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value))); $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
} }
$where = $updateQb->expr()->andx(); $where = $updateQb->expr()->andX();
$whereValues = array_merge($keys, $updatePreconditionValues); $whereValues = array_merge($keys, $updatePreconditionValues);
foreach ($whereValues as $name => $value) { foreach ($whereValues as $name => $value) {
$where->add($updateQb->expr()->eq( $where->add($updateQb->expr()->eq(
...@@ -301,6 +304,33 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { ...@@ -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 * returns the error code and message as a string for logging
* works with DoctrineException * works with DoctrineException
......
...@@ -124,6 +124,25 @@ interface IDBConnection { ...@@ -124,6 +124,25 @@ interface IDBConnection {
*/ */
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []); 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 * Start a transaction
* @since 6.0.0 * @since 6.0.0
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment