Browse Source

Catch UniqueConstraintViolationException inside insertIfNotExist

This is the most common case for the usage of this method.

See also https://github.com/nextcloud/server/issues/12369 and the linked tickets.

Signed-off-by: Morris Jobke <hey@morrisjobke.de>
tags/v15.0.0beta2
Morris Jobke 5 years ago
parent
commit
230e93f575
No account linked to committer's email address

+ 14
- 2
lib/private/DB/Adapter.php View File

@@ -27,6 +27,8 @@

namespace OC\DB;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;

/**
* This handles the way we use to write queries, into something that can be
* handled by the database abstraction layer.
@@ -79,7 +81,9 @@ class Adapter {
}

/**
* Insert a row if the matching row does not exists.
* Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
* it is needed that there is also a unique constraint on the values. Then this method will
* catch the exception and return 0.
*
* @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)
@@ -111,6 +115,14 @@ class Adapter {
$query = substr($query, 0, -5);
$query .= ' HAVING COUNT(*) = 0';

return $this->conn->executeUpdate($query, $inserts);
try {
return $this->conn->executeUpdate($query, $inserts);
} catch (UniqueConstraintViolationException $e) {
// if this is thrown then a concurrent insert happened between the insert and the sub-select in the insert, that should have avoided it
// it's fine to ignore this then
//
// more discussions about this can be found at https://github.com/nextcloud/server/pull/12315
return 0;
}
}
}

+ 14
- 2
lib/private/DB/AdapterSqlite.php View File

@@ -27,6 +27,8 @@

namespace OC\DB;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;

class AdapterSqlite extends Adapter {

/**
@@ -50,7 +52,9 @@ class AdapterSqlite extends Adapter {
}

/**
* Insert a row if the matching row does not exists.
* Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
* it is needed that there is also a unique constraint on the values. Then this method will
* catch the exception and return 0.
*
* @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)
@@ -82,6 +86,14 @@ class AdapterSqlite extends Adapter {
$query = substr($query, 0, -5);
$query .= ')';

return $this->conn->executeUpdate($query, $inserts);
try {
return $this->conn->executeUpdate($query, $inserts);
} catch (UniqueConstraintViolationException $e) {
// if this is thrown then a concurrent insert happened between the insert and the sub-select in the insert, that should have avoided it
// it's fine to ignore this then
//
// more discussions about this can be found at https://github.com/nextcloud/server/pull/12315
return 0;
}
}
}

+ 3
- 1
lib/private/DB/Connection.php View File

@@ -240,7 +240,9 @@ class Connection extends ReconnectWrapper implements IDBConnection {
}

/**
* Insert a row if the matching row does not exists.
* Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
* it is needed that there is also a unique constraint on the values. Then this method will
* catch the exception and return 0.
*
* @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)

+ 3
- 1
lib/public/IDBConnection.php View File

@@ -104,7 +104,9 @@ interface IDBConnection {
public function lastInsertId($table = null);

/**
* Insert a row if the matching row does not exists.
* Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
* it is needed that there is also a unique constraint on the values. Then this method will
* catch the exception and return 0.
*
* @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)

Loading…
Cancel
Save