aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2024-04-11 17:27:20 +0200
committerRobin Appelman <robin@icewind.nl>2024-07-24 15:15:22 +0200
commit3af3610963ac1fdc14770b2a4b9e0585b765e4f4 (patch)
tree1be0324f0f5d247b6cc58962cfc502c8cdc6b6c6
parent06fb25b6a2220b6b2402e16c034c0f83e5c980ee (diff)
downloadnextcloud-server-db-error-logging-28.tar.gz
nextcloud-server-db-error-logging-28.zip
feat: add additional logging for database errorsdb-error-logging-28
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r--lib/private/DB/Connection.php68
-rw-r--r--lib/private/DB/ConnectionAdapter.php4
-rw-r--r--lib/private/DB/QueryBuilder/QueryBuilder.php7
3 files changed, 73 insertions, 6 deletions
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index ab0b35c11b0..cd0606ca135 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -79,6 +79,8 @@ class Connection extends \Doctrine\DBAL\Connection {
/** @var DbDataCollector|null */
protected $dbDataCollector = null;
+ private ?array $transactionBacktrace = null;
+
protected bool $logRequestId;
protected string $requestId;
@@ -263,7 +265,12 @@ class Connection extends \Doctrine\DBAL\Connection {
$sql = $this->finishQuery($sql);
$this->queriesExecuted++;
$this->logQueryToFile($sql);
- return parent::executeQuery($sql, $params, $types, $qcp);
+ try {
+ return parent::executeQuery($sql, $params, $types, $qcp);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
/**
@@ -273,7 +280,12 @@ class Connection extends \Doctrine\DBAL\Connection {
$sql = $this->finishQuery($sql);
$this->queriesExecuted++;
$this->logQueryToFile($sql);
- return parent::executeUpdate($sql, $params, $types);
+ try {
+ return parent::executeUpdate($sql, $params, $types);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
/**
@@ -294,7 +306,12 @@ class Connection extends \Doctrine\DBAL\Connection {
$sql = $this->finishQuery($sql);
$this->queriesExecuted++;
$this->logQueryToFile($sql);
- return (int)parent::executeStatement($sql, $params, $types);
+ try {
+ return (int)parent::executeStatement($sql, $params, $types);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
protected function logQueryToFile(string $sql): void {
@@ -356,11 +373,21 @@ class Connection extends \Doctrine\DBAL\Connection {
* @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
*/
public function insertIfNotExist($table, $input, array $compare = null) {
- return $this->adapter->insertIfNotExist($table, $input, $compare);
+ try {
+ return $this->adapter->insertIfNotExist($table, $input, $compare);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
public function insertIgnoreConflict(string $table, array $values) : int {
- return $this->adapter->insertIgnoreConflict($table, $values);
+ try {
+ return $this->adapter->insertIgnoreConflict($table, $values);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
private function getType($value) {
@@ -616,4 +643,35 @@ class Connection extends \Doctrine\DBAL\Connection {
return new Migrator($this, $config, $dispatcher);
}
}
+
+ public function beginTransaction() {
+ if (!$this->inTransaction()) {
+ $this->transactionBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ }
+ return parent::beginTransaction();
+ }
+
+ public function commit() {
+ $result = parent::commit();
+ if ($this->getTransactionNestingLevel() === 0) {
+ $this->transactionBacktrace = null;
+ }
+ return $result;
+ }
+
+ public function rollBack() {
+ $result = parent::rollBack();
+ if ($this->getTransactionNestingLevel() === 0) {
+ $this->transactionBacktrace = null;
+ }
+ return $result;
+ }
+
+ public function logDatabaseException(\Exception $exception): void {
+ if ($exception instanceof Exception\UniqueConstraintViolationException) {
+ $this->logger->info($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
+ } else {
+ $this->logger->error($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
+ }
+ }
}
diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php
index e27c98194fb..71be59c4d35 100644
--- a/lib/private/DB/ConnectionAdapter.php
+++ b/lib/private/DB/ConnectionAdapter.php
@@ -261,4 +261,8 @@ class ConnectionAdapter implements IDBConnection {
throw new \Exception('Database ' . $platform::class . ' not supported');
}
}
+
+ public function logDatabaseException(\Exception $exception) {
+ $this->inner->logDatabaseException($exception);
+ }
}
diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php
index d78cf50e319..e9594369a71 100644
--- a/lib/private/DB/QueryBuilder/QueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/QueryBuilder.php
@@ -277,7 +277,12 @@ class QueryBuilder implements IQueryBuilder {
]);
}
- $result = $this->queryBuilder->execute();
+ try {
+ $result = $this->queryBuilder->execute();
+ } catch (\Exception $e) {
+ $this->connection->logDatabaseException($e);
+ throw $e;
+ }
if (is_int($result)) {
return $result;
}