summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/private/AppFramework/Db/Db.php17
-rw-r--r--lib/private/DB/Adapter.php20
-rw-r--r--lib/private/DB/AdapterMySQL.php12
-rw-r--r--lib/private/DB/AdapterSqlite.php12
-rw-r--r--lib/private/DB/Connection.php32
-rw-r--r--lib/public/IDBConnection.php19
6 files changed, 110 insertions, 2 deletions
diff --git a/lib/private/AppFramework/Db/Db.php b/lib/private/AppFramework/Db/Db.php
index 0d17d7bc225..ab06d56cfd1 100644
--- a/lib/private/AppFramework/Db/Db.php
+++ b/lib/private/AppFramework/Db/Db.php
@@ -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,13 +158,27 @@ 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
*/
public function beginTransaction() {
diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php
index 9522f768c88..bcced395cb7 100644
--- a/lib/private/DB/Adapter.php
+++ b/lib/private/DB/Adapter.php
@@ -58,6 +58,26 @@ class Adapter {
}
/**
+ * 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.
*
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
diff --git a/lib/private/DB/AdapterMySQL.php b/lib/private/DB/AdapterMySQL.php
index ab87c589747..8504fc74e0f 100644
--- a/lib/private/DB/AdapterMySQL.php
+++ b/lib/private/DB/AdapterMySQL.php
@@ -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;
diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php
index 3466e0e1aac..cefb06ffac6 100644
--- a/lib/private/DB/AdapterSqlite.php
+++ b/lib/private/DB/AdapterSqlite.php
@@ -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);
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index 7cdc13a7c6d..5b7026db2f3 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -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(
@@ -302,6 +305,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
* @return string
diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php
index 780fcd26364..4ecf01ca27e 100644
--- a/lib/public/IDBConnection.php
+++ b/lib/public/IDBConnection.php
@@ -125,6 +125,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
*/