Skip to content

Commit

Permalink
Merge pull request #14766 from owncloud/fix-insertifnotexists-poc
Browse files Browse the repository at this point in the history
Allow specifying the compare-array for insertIfNotExists()
  • Loading branch information
MorrisJobke committed Mar 16, 2015
2 parents 1e0d8f1 + fefcbb9 commit 997a7a2
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 138 deletions.
15 changes: 11 additions & 4 deletions lib/private/allconfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,18 @@ public function setUserValue($userId, $appName, $key, $value, $preCondition = nu
return;
}

$data = array($value, $userId, $appName, $key);
$affectedRows = 0;
if (!$exists && $preCondition === null) {
$sql = 'INSERT INTO `*PREFIX*preferences` (`configvalue`, `userid`, `appid`, `configkey`)'.
'VALUES (?, ?, ?, ?)';
$this->connection->insertIfNotExist('*PREFIX*preferences', [
'configvalue' => $value,
'userid' => $userId,
'appid' => $appName,
'configkey' => $key,
], ['configvalue', 'userid', 'appid']);
$affectedRows = 1;
} elseif ($exists) {
$data = array($value, $userId, $appName, $key);

$sql = 'UPDATE `*PREFIX*preferences` SET `configvalue` = ? '.
'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? ';

Expand All @@ -206,8 +213,8 @@ public function setUserValue($userId, $appName, $key, $value, $preCondition = nu
}
$data[] = $preCondition;
}
$affectedRows = $this->connection->executeUpdate($sql, $data);
}
$affectedRows = $this->connection->executeUpdate($sql, $data);

// only add to the cache if we already loaded data for the user
if ($affectedRows > 0 && isset($this->userCache[$userId])) {
Expand Down
26 changes: 9 additions & 17 deletions lib/private/appframework/db/db.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,17 @@ public function lastInsertId($table = null) {
}

/**
* Insert a row if a matching row doesn't exists.
* @param string $table The table name (will replace *PREFIX*) to perform the replace on.
* @param array $input
* @throws \OC\HintException
*
* The input array if in the form:
*
* array ( 'id' => array ( 'value' => 6,
* 'key' => true
* ),
* 'name' => array ('value' => 'Stoyan'),
* 'family' => array ('value' => 'Stefanov'),
* 'birth_date' => array ('value' => '1975-06-20')
* );
* @return bool
* Insert a row if the matching row does not exists.
*
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*/
public function insertIfNotExist($table, $input) {
return $this->connection->insertIfNotExist($table, $input);
public function insertIfNotExist($table, $input, array $compare = null) {
return $this->connection->insertIfNotExist($table, $input, $compare);
}

/**
Expand Down
17 changes: 2 additions & 15 deletions lib/private/db.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
*
*/

define('MDB2_SCHEMA_DUMP_STRUCTURE', '1');

/**
* This class manages the access to the database. It basically is a wrapper for
* Doctrine with some adaptions.
Expand All @@ -40,8 +38,7 @@ static public function getConnection() {
*
* @return \OC\DB\MDB2SchemaManager
*/
private static function getMDB2SchemaManager()
{
private static function getMDB2SchemaManager() {
return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection());
}

Expand Down Expand Up @@ -166,16 +163,6 @@ public static function insertid($table=null) {
return \OC::$server->getDatabaseConnection()->lastInsertId($table);
}

/**
* Insert a row if a matching row doesn't exists.
* @param string $table The table to insert into in the form '*PREFIX*tableName'
* @param array $input An array of fieldname/value pairs
* @return boolean number of updated rows
*/
public static function insertIfNotExist($table, $input) {
return \OC::$server->getDatabaseConnection()->insertIfNotExist($table, $input);
}

/**
* Start a transaction
*/
Expand Down Expand Up @@ -205,7 +192,7 @@ public static function rollback() {
*
* TODO: write more documentation
*/
public static function getDbStructure( $file, $mode = 0) {
public static function getDbStructure($file) {
$schemaManager = self::getMDB2SchemaManager();
return $schemaManager->getDbStructure($file);
}
Expand Down
39 changes: 16 additions & 23 deletions lib/private/db/adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,44 +40,37 @@ public function fixupStatement($statement) {
}

/**
* insert the @input values when they do not exist yet
* @param string $table name
* @param array $input key->value pair, key has to be sanitized properly
* @throws \OC\HintException
* @return int count of inserted rows
* Insert a row if the matching row does not exists.
*
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*/
public function insertIfNotExist($table, $input) {
public function insertIfNotExist($table, $input, array $compare = null) {
if (empty($compare)) {
$compare = array_keys($input);
}
$query = 'INSERT INTO `' .$table . '` (`'
. implode('`,`', array_keys($input)) . '`) SELECT '
. str_repeat('?,', count($input)-1).'? ' // Is there a prettier alternative?
. 'FROM `' . $table . '` WHERE ';

$inserts = array_values($input);
foreach($input as $key => $value) {
foreach($compare as $key) {
$query .= '`' . $key . '`';
if (is_null($value)) {
if (is_null($input[$key])) {
$query .= ' IS NULL AND ';
} else {
$inserts[] = $value;
$inserts[] = $input[$key];
$query .= ' = ? AND ';
}
}
$query = substr($query, 0, strlen($query) - 5);
$query .= ' HAVING COUNT(*) = 0';

try {
return $this->conn->executeUpdate($query, $inserts);
} catch(\Doctrine\DBAL\DBALException $e) {
$entry = 'DB Error: "'.$e->getMessage() . '"<br />';
$entry .= 'Offending command was: ' . $query.'<br />';
\OC_Log::write('core', $entry, \OC_Log::FATAL);
$l = \OC::$server->getL10N('lib');
throw new \OC\HintException(
$l->t('Database Error'),
$l->t('Please contact your system administrator.'),
0,
$e
);
}
return $this->conn->executeUpdate($query, $inserts);
}
}
36 changes: 18 additions & 18 deletions lib/private/db/adaptersqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,38 @@ public function fixupStatement($statement) {
return $statement;
}

public function insertIfNotExist($table, $input) {
/**
* Insert a row if the matching row does not exists.
*
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*/
public function insertIfNotExist($table, $input, array $compare = null) {
if (empty($compare)) {
$compare = array_keys($input);
}
$fieldList = '`' . implode('`,`', array_keys($input)) . '`';
$query = "INSERT INTO `$table` ($fieldList) SELECT "
. str_repeat('?,', count($input)-1).'? '
. " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";

$inserts = array_values($input);
foreach($input as $key => $value) {
foreach($compare as $key) {
$query .= '`' . $key . '`';
if (is_null($value)) {
if (is_null($input[$key])) {
$query .= ' IS NULL AND ';
} else {
$inserts[] = $value;
$inserts[] = $input[$key];
$query .= ' = ? AND ';
}
}
$query = substr($query, 0, strlen($query) - 5);
$query .= ')';

try {
return $this->conn->executeUpdate($query, $inserts);
} catch(\Doctrine\DBAL\DBALException $e) {
$entry = 'DB Error: "'.$e->getMessage() . '"<br />';
$entry .= 'Offending command was: ' . $query.'<br />';
\OC_Log::write('core', $entry, \OC_Log::FATAL);
$l = \OC::$server->getL10N('lib');
throw new \OC\HintException(
$l->t('Database Error'),
$l->t('Please contact your system administrator.'),
0,
$e
);
}
return $this->conn->executeUpdate($query, $inserts);
}
}
20 changes: 11 additions & 9 deletions lib/private/db/connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,22 @@ public function lastInsertId($seqName = null)
}

// internal use
public function realLastInsertId($seqName = null)
{
public function realLastInsertId($seqName = null) {
return parent::lastInsertId($seqName);
}

/**
* Insert a row if a matching row doesn't exists.
* @param string $table. The table to insert into in the form '*PREFIX*tableName'
* @param array $input. An array of fieldname/value pairs
* @throws \OC\HintException
* @return bool The return value from execute()
* Insert a row if the matching row does not exists.
*
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*/
public function insertIfNotExist($table, $input) {
return $this->adapter->insertIfNotExist($table, $input);
public function insertIfNotExist($table, $input, array $compare = null) {
return $this->adapter->insertIfNotExist($table, $input, $compare);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/private/db/mdb2schemamanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct($conn) {
*
* TODO: write more documentation
*/
public function getDbStructure($file, $mode = MDB2_SCHEMA_DUMP_STRUCTURE) {
public function getDbStructure($file) {
return \OC_DB_MDB2SchemaWriter::saveSchemaToFile($file, $this->conn);
}

Expand Down
14 changes: 12 additions & 2 deletions lib/private/files/cache/cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ public function getFolderContentsById($fileId) {
* @param array $data
*
* @return int file id
* @throws \RuntimeException
*/
public function put($file, array $data) {
if (($id = $this->getId($file)) > -1) {
Expand Down Expand Up @@ -251,11 +252,20 @@ public function put($file, array $data) {
return trim($item, "`");
}, $queryParts);
$values = array_combine($queryParts, $params);
if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values)) {
if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [
'storage',
'path_hash',
])) {
return (int)\OC_DB::insertid('*PREFIX*filecache');
}

return $this->getId($file);
// The file was created in the mean time
if (($id = $this->getId($file)) > -1) {
$this->update($id, $data);
return $id;
} else {
throw new \RuntimeException('File entry exists when inserting and does not exist on select... go away');
}
}
}

Expand Down
15 changes: 12 additions & 3 deletions lib/private/files/cache/storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Storage {

/**
* @param \OC\Files\Storage\Storage|string $storage
* @throws \RuntimeException
*/
public function __construct($storage) {
if ($storage instanceof \OC\Files\Storage\Storage) {
Expand All @@ -35,9 +36,17 @@ public function __construct($storage) {
if ($row = $result->fetchRow()) {
$this->numericId = $row['numeric_id'];
} else {
$sql = 'INSERT INTO `*PREFIX*storages` (`id`) VALUES(?)';
\OC_DB::executeAudited($sql, array($this->storageId));
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
$connection = \OC_DB::getConnection();
if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId])) {
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
} else {
$result = \OC_DB::executeAudited($sql, array($this->storageId));
if ($row = $result->fetchRow()) {
$this->numericId = $row['numeric_id'];
} else {
throw new \RuntimeException('Storage exists when inserting and does not exist on select... go away');
}
}
}
}

Expand Down
24 changes: 9 additions & 15 deletions lib/public/db.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,18 @@ static public function prepare( $query, $limit=null, $offset=null ) {
}

/**
* Insert a row if a matching row doesn't exists.
* @param string $table The optional table name (will replace *PREFIX*) and add sequence suffix
* @param array $input
*
* The input array if in the form:
* Insert a row if the matching row does not exists.
*
* array ( 'id' => array ( 'value' => 6,
* 'key' => true
* ),
* 'name' => array ('value' => 'Stoyan'),
* 'family' => array ('value' => 'Stefanov'),
* 'birth_date' => array ('value' => '1975-06-20')
* );
* @return bool
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*
*/
public static function insertIfNotExist($table, $input) {
return(\OC_DB::insertIfNotExist($table, $input));
public static function insertIfNotExist($table, $input, array $compare = null) {
return \OC::$server->getDatabaseConnection()->insertIfNotExist($table, $input, $compare);
}

/**
Expand Down
26 changes: 9 additions & 17 deletions lib/public/idbconnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,16 @@ public function executeUpdate($query, array $params = array(), array $types = ar
public function lastInsertId($table = null);

/**
* Insert a row if a matching row doesn't exists.
* @param string $table The table name (will replace *PREFIX*) to perform the replace on.
* @param array $input
* @throws \OC\HintException
* Insert a row if the matching row does not exists.
*
* The input array if in the form:
*
* array ( 'id' => array ( 'value' => 6,
* 'key' => true
* ),
* 'name' => array ('value' => 'Stoyan'),
* 'family' => array ('value' => 'Stefanov'),
* 'birth_date' => array ('value' => '1975-06-20')
* );
* @return bool
*
*/
public function insertIfNotExist($table, $input);
* @param string $table The table name (will replace *PREFIX* with the actual prefix)
* @param array $input data that should be inserted into the table (column name => value)
* @param array|null $compare List of values that should be checked for "if not exists"
* If this is null or an empty array, all keys of $input will be compared
* @return int number of inserted rows
* @throws \Doctrine\DBAL\DBALException
*/
public function insertIfNotExist($table, $input, array $compare = null);

/**
* Start a transaction
Expand Down
Loading

0 comments on commit 997a7a2

Please sign in to comment.