@@ -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 | |||
*/ |
@@ -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. | |||
* |
@@ -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; |
@@ -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); |
@@ -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 |
@@ -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 |