Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c239495
chore(deps): Bump doctrine/dbal from 3.8.3 to 4.0.4
nickvergessen Jun 28, 2024
015a3e8
fix(db)!: Fix casing of doctrine's SQLitePlatform
nickvergessen Jun 28, 2024
a5432e3
fix(db): Adjust Connection signature to updated doctrine one
nickvergessen Jun 28, 2024
de0141d
fix(db)!: Empty and adjust query-part related methods
nickvergessen Jun 28, 2024
72886c0
fix(db): Make sure setComment() is only called with strings
nickvergessen Jun 28, 2024
6d0e8e4
fix(db): Use new function name to get the schema
nickvergessen Jun 28, 2024
76ef7dd
fix(db): Remove usage of dropped doctrine event manager
nickvergessen Jun 28, 2024
648b20c
fix(db)!: Manually track the query type and whether WHERE is empty
nickvergessen Jun 28, 2024
e6e1bd8
fix(db): Fix internal calls to doctrine's fetch functions
nickvergessen Jun 28, 2024
f6f53a8
fix(db)!: Doctrine\DBAL\Types\Type::getName() was removed
nickvergessen Jul 1, 2024
27c49e7
fix(db)!: Table::changeColumn() was renamed to Table::modifyColumn()
nickvergessen Jul 1, 2024
466f455
fix(db)!: `Doctrine\DBAL\Schema\Table::hasPrimaryKey() was removed, u…
nickvergessen Jul 1, 2024
287d527
fix(db)!: `Doctrine\DBAL\Connection::getSchemaManager` was removed, u…
nickvergessen Jul 1, 2024
3c5acbc
fix(db): `Doctrine\DBAL\Connection::executeUpdate()` was removed
nickvergessen Jul 1, 2024
f45ac5b
fix(db): `Doctrine\DBAL\Query\Expression\ExpressionBuilder::andX()` a…
nickvergessen Jul 1, 2024
72151b5
fix(db): `Table::getPrimaryKeyColumns()` was removed
nickvergessen Jul 1, 2024
a20110b
fix(db): UndefinedConstant: Constant Doctrine\DBAL\Connection::PARAM_…
nickvergessen Jul 1, 2024
c0d2f7e
fix(CI): Operand of type Doctrine\DBAL\Schema\Column is always truthy
nickvergessen Jul 1, 2024
fb3d4d5
fix(db): Don't use removed/deprecated methods in setup
nickvergessen Jul 1, 2024
2d27a32
fix(db): InaccessibleMethod: Cannot access private method Doctrine\DB…
nickvergessen Jul 1, 2024
d9e69e1
fix(db)!: Table alias for DELETE and UPDATE no longer supported
nickvergessen Jul 1, 2024
f6ac69a
fix(db)!: `Doctrine\DBAL\Platforms\PostgreSQL94Platform` was removed
nickvergessen Jul 1, 2024
c6cd20e
fix(db)!: `Doctrine\DBAL\FetchMode` was removed
nickvergessen Jul 1, 2024
1654430
fix(db): Interface `Doctrine\DBAL\Exception` cannot be instantiated
nickvergessen Jul 1, 2024
4c10031
fix(db): `Doctrine\DBAL\Schema\Schema::getTableNames()` was removed
nickvergessen Jul 1, 2024
ec1a3d6
fix(db): `Doctrine\DBAL\Query\QueryBuilder::getState` does not exist
nickvergessen Jul 1, 2024
6e90ddf
fix(db): `Doctrine\DBAL\Query\Expression\CompositeExpression::add*` d…
nickvergessen Jul 1, 2024
4bbaae5
fix(db): `ExpressionBuilder::like` expects string, but `OCP\DB\QueryB…
nickvergessen Jul 1, 2024
2b83016
fix(db): TooManyArguments: Too many arguments for method `Doctrine\DB…
nickvergessen Jul 1, 2024
55a8f33
fix(db): Doctrine Event Manager is removed
nickvergessen Jul 1, 2024
5823d58
fix(db)!: `Doctrine\DBAL\Platforms\MySQL80Platform` requires the leng…
nickvergessen Jul 2, 2024
e8ef23d
fix(db): Too few arguments for `Doctrine\DBAL\Driver::getDatabasePlat…
nickvergessen Jul 2, 2024
df7d088
fix(db)!: Deprecate getDatabasePlatform which leaks 3rdparty
nickvergessen Jul 2, 2024
60217aa
fix(db): No longer return result of `Connection::connect()`
nickvergessen Jul 2, 2024
926f2ab
fix(db): Too many arguments for method `\Doctrine\DBAL\Connection::qu…
nickvergessen Jul 2, 2024
bae31bf
fix(db): Method `\Doctrine\DBAL\Platforms\AbstractPlatform::getIdenti…
nickvergessen Jul 2, 2024
7156569
fix(db): Argument 2 of `\Doctrine\DBAL\Platforms\AbstractPlatform::ge…
nickvergessen Jul 2, 2024
66aac18
fix(db)!: `OCP\DB\IPreparedStatement::bindParam()` is deprecated and …
nickvergessen Jul 2, 2024
98b6c7c
fix(db): Add a hint that we return PHP_INT_MAX
nickvergessen Jul 2, 2024
6b2f136
fix(db): Map parameter types to doctrine's enums
nickvergessen Jul 2, 2024
1370693
fix(db): Remove usage of Platforms
nickvergessen Jul 2, 2024
de5a7c3
fix(db)!: Reimplement the query logger with a doctrine middleware
nickvergessen Jul 3, 2024
8153c38
fix(db): Don't set null as type
nickvergessen Jul 3, 2024
fc383a3
fix(tests): Adjust the migrator test
nickvergessen Jul 3, 2024
4c80fdd
fix(db): Adjust unit tests of query builder
nickvergessen Jul 3, 2024
5718e8f
fix(tests): Fix changes in doctrine's Exception handling
nickvergessen Jul 3, 2024
f0348be
fix(db): Replace more Platform usages
nickvergessen Jul 4, 2024
b2cf45c
fix(tests): Add length to test columns that bypass the migration service
nickvergessen Jul 4, 2024
72c8c3a
fix(tests): Adjust postgres schema diffing
nickvergessen Jul 4, 2024
8b7f8e1
fix(tests): Also handle the case where the schema is cached and the D…
nickvergessen Jul 4, 2024
5fdc5d5
fix(db)!: Too few arguments to function `Doctrine\DBAL\Query\Expressi…
nickvergessen Jul 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(db)!: Reimplement the query logger with a doctrine middleware
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Jul 4, 2024
commit de5a7c3c73e5f2f5dda5ec07333c8d3dd89f4540
5 changes: 4 additions & 1 deletion lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1321,12 +1321,15 @@
'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php',
'OC\\DB\\AdapterPgSql' => $baseDir . '/lib/private/DB/AdapterPgSql.php',
'OC\\DB\\AdapterSqlite' => $baseDir . '/lib/private/DB/AdapterSqlite.php',
'OC\\DB\\BacktraceDebugStack' => $baseDir . '/lib/private/DB/BacktraceDebugStack.php',
'OC\\DB\\Connection' => $baseDir . '/lib/private/DB/Connection.php',
'OC\\DB\\ConnectionAdapter' => $baseDir . '/lib/private/DB/ConnectionAdapter.php',
'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php',
'OC\\DB\\DbDataCollector' => $baseDir . '/lib/private/DB/DbDataCollector.php',
'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php',
'OC\\DB\\Logging\\Connection' => $baseDir . '/lib/private/DB/Logging/Connection.php',
'OC\\DB\\Logging\\Driver' => $baseDir . '/lib/private/DB/Logging/Driver.php',
'OC\\DB\\Logging\\Middleware' => $baseDir . '/lib/private/DB/Logging/Middleware.php',
'OC\\DB\\Logging\\Statement' => $baseDir . '/lib/private/DB/Logging/Statement.php',
'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => $baseDir . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php',
'OC\\DB\\Middlewares\\SQLiteJournalMode' => $baseDir . '/lib/private/DB/Middlewares/SQLiteJournalMode.php',
'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => $baseDir . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php',
Expand Down
5 changes: 4 additions & 1 deletion lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -1354,12 +1354,15 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php',
'OC\\DB\\AdapterPgSql' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterPgSql.php',
'OC\\DB\\AdapterSqlite' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterSqlite.php',
'OC\\DB\\BacktraceDebugStack' => __DIR__ . '/../../..' . '/lib/private/DB/BacktraceDebugStack.php',
'OC\\DB\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Connection.php',
'OC\\DB\\ConnectionAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionAdapter.php',
'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php',
'OC\\DB\\DbDataCollector' => __DIR__ . '/../../..' . '/lib/private/DB/DbDataCollector.php',
'OC\\DB\\Exceptions\\DbalException' => __DIR__ . '/../../..' . '/lib/private/DB/Exceptions/DbalException.php',
'OC\\DB\\Logging\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Connection.php',
'OC\\DB\\Logging\\Driver' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Driver.php',
'OC\\DB\\Logging\\Middleware' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Middleware.php',
'OC\\DB\\Logging\\Statement' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Statement.php',
'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php',
'OC\\DB\\Middlewares\\SQLiteJournalMode' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteJournalMode.php',
'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php',
Expand Down
20 changes: 0 additions & 20 deletions lib/private/DB/BacktraceDebugStack.php

This file was deleted.

8 changes: 3 additions & 5 deletions lib/private/DB/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OC\SystemConfig;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\Diagnostics\IQueryLogger;
use OCP\IRequestId;
use OCP\PreConditionNotMetException;
use OCP\Profiler\IProfiler;
Expand Down Expand Up @@ -99,14 +100,11 @@ public function __construct(
$this->logRequestId = $this->systemConfig->getValue('db.log_request_id', false);
$this->requestId = Server::get(IRequestId::class)->getId();

/** @var \OCP\Profiler\IProfiler */
/** @var IProfiler */
$profiler = Server::get(IProfiler::class);
if ($profiler->isEnabled()) {
$this->dbDataCollector = new DbDataCollector($this);
$this->dbDataCollector = new DbDataCollector(Server::get(IQueryLogger::class));
$profiler->add($this->dbDataCollector);
$debugStack = new BacktraceDebugStack();
$this->dbDataCollector->setDebugStack($debugStack);
// FIXME $this->_config->setSQLLogger($debugStack);
}

$this->setNestTransactionsWithSavepoints(true);
Expand Down
9 changes: 9 additions & 0 deletions lib/private/DB/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@

use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DriverManager;
use OC\DB\Logging\Middleware;
use OC\DB\Middlewares\SetTransactionIsolationLevel;
use OC\DB\Middlewares\SQLiteCaseSensitiveLike;
use OC\DB\Middlewares\SQLiteJournalMode;
use OC\SystemConfig;
use OCP\Diagnostics\IQueryLogger;
use OCP\Profiler\IProfiler;
use OCP\Server;

/**
* Takes care of creating and configuring Doctrine connections.
Expand Down Expand Up @@ -108,6 +112,11 @@ public function getConnection($type, $additionalConnectionParams) {

$doctrineConfiguration = new Configuration();
$doctrineMiddlewares = $doctrineConfiguration->getMiddlewares();
/** @var IProfiler */
$profiler = Server::get(IProfiler::class);
if ($profiler->isEnabled()) {
$doctrineMiddlewares[] = new Middleware(Server::get(IQueryLogger::class));
}
$doctrineMiddlewares[] = new SetTransactionIsolationLevel();

switch ($normalizedType) {
Expand Down
73 changes: 21 additions & 52 deletions lib/private/DB/DbDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,22 @@

namespace OC\DB;

use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
use OC\AppFramework\Http\Request;
use OC\Diagnostics\Query;
use OCP\AppFramework\Http\Response;
use OCP\Diagnostics\IQueryLogger;

class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector {
protected ?BacktraceDebugStack $debugStack = null;
private Connection $connection;

/**
* DbDataCollector constructor.
*/
public function __construct(Connection $connection) {
$this->connection = $connection;
}

public function setDebugStack(BacktraceDebugStack $debugStack, $name = 'default'): void {
$this->debugStack = $debugStack;
public function __construct(
private readonly IQueryLogger $queryLogger,
) {
}

/**
* @inheritDoc
*/
public function collect(Request $request, Response $response, ?\Throwable $exception = null): void {
$queries = $this->sanitizeQueries($this->debugStack->queries);
$queries = $this->sanitizeQueries($this->queryLogger->getQueries());

$this->data = [
'queries' => $queries,
Expand All @@ -55,37 +46,19 @@ private function sanitizeQueries(array $queries): array {
return $queries;
}

private function sanitizeQuery(array $query): array {
$query['explainable'] = true;
$query['runnable'] = true;
if ($query['params'] === null) {
$query['params'] = [];
}
if (!\is_array($query['params'])) {
$query['params'] = [$query['params']];
}
if (!\is_array($query['types'])) {
$query['types'] = [];
}
foreach ($query['params'] as $j => $param) {
$e = null;
if (isset($query['types'][$j])) {
// Transform the param according to the type
$type = $query['types'][$j];
if (\is_string($type)) {
$type = Type::getType($type);
}
if ($type instanceof Type) {
$query['types'][$j] = $type->getBindingType();
try {
$param = $type->convertToDatabaseValue($param, $this->connection->getDatabasePlatform());
} catch (\TypeError $e) {
} catch (ConversionException $e) {
}
}
}
private function sanitizeQuery(Query $queryObject): array {
$query = [
'sql' => $queryObject->getSql(),
'params' => $queryObject->getParams() ?? [],
'types' => $queryObject->getTypes() ?? [],
'executionMS' => $queryObject->getDuration(),
'backtrace' => $queryObject->getStacktrace(),
'explainable' => true,
'runnable' => true,
];

[$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param, $e);
foreach ($query['params'] as $j => $param) {
[$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param);
if (!$explainable) {
$query['explainable'] = false;
}
Expand All @@ -105,20 +78,16 @@ private function sanitizeQuery(array $query): array {
* indicating if the original value was kept (allowing to use the sanitized
* value to explain the query).
*/
private function sanitizeParam($var, ?\Throwable $error): array {
private function sanitizeParam($var): array {
if (\is_object($var)) {
return [$o = new ObjectParameter($var, $error), false, $o->isStringable() && !$error];
}

if ($error) {
return ['⚠ '.$error->getMessage(), false, false];
return [$o = new ObjectParameter($var, null), false, $o->isStringable()];
}

if (\is_array($var)) {
$a = [];
$explainable = $runnable = true;
foreach ($var as $k => $v) {
[$value, $e, $r] = $this->sanitizeParam($v, null);
[$value, $e, $r] = $this->sanitizeParam($v);
$explainable = $explainable && $e;
$runnable = $runnable && $r;
$a[$k] = $value;
Expand Down
49 changes: 49 additions & 0 deletions lib/private/DB/Logging/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Doctrine Project
* SPDX-License-Identifier: MIT
*/

namespace OC\DB\Logging;

use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use OCP\Diagnostics\IQueryLogger;

final class Connection extends AbstractConnectionMiddleware {
public function __construct(
ConnectionInterface $connection,
private readonly IQueryLogger $queryLogger,
) {
parent::__construct($connection);
}

public function prepare(string $sql): DriverStatement {
return new Statement(
parent::prepare($sql),
$this->queryLogger,
$sql,
);
}

public function query(string $sql): Result {
$this->queryLogger->startQuery($sql);
$result = parent::query($sql);
$this->queryLogger->stopQuery();

return $result;
}

public function exec(string $sql): int|string {
$this->queryLogger->startQuery($sql);
$result = parent::exec($sql);
$this->queryLogger->stopQuery();

return $result;
}
}
34 changes: 34 additions & 0 deletions lib/private/DB/Logging/Driver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Doctrine Project
* SPDX-License-Identifier: MIT
*/

namespace OC\DB\Logging;

use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use OCP\Diagnostics\IQueryLogger;
use SensitiveParameter;

final class Driver extends AbstractDriverMiddleware {
public function __construct(
DriverInterface $driver,
private readonly IQueryLogger $queryLogger,
) {
parent::__construct($driver);
}

public function connect(
#[SensitiveParameter]
array $params,
): Connection {
return new Connection(
parent::connect($params),
$this->queryLogger,
);
}
}
28 changes: 28 additions & 0 deletions lib/private/DB/Logging/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Doctrine Project
* SPDX-License-Identifier: MIT
*/

namespace OC\DB\Logging;

use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
use OCP\Diagnostics\IQueryLogger;

final class Middleware implements MiddlewareInterface {
public function __construct(
private readonly IQueryLogger $queryLogger,
) {
}

public function wrap(DriverInterface $driver): DriverInterface {
return new Driver(
$driver,
$this->queryLogger,
);
}
}
50 changes: 50 additions & 0 deletions lib/private/DB/Logging/Statement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Doctrine Project
* SPDX-License-Identifier: MIT
*/

namespace OC\DB\Logging;

use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use OC\DB\TDoctrineParameterTypeMap;
use OCP\Diagnostics\IQueryLogger;

final class Statement extends AbstractStatementMiddleware {
use TDoctrineParameterTypeMap;

/** @var array<int,mixed>|array<string,mixed> */
private array $params = [];

/** @var array<int,string>|array<string,string> */
private array $types = [];

public function __construct(
StatementInterface $statement,
private readonly IQueryLogger $queryLogger,
private readonly string $sql,
) {
parent::__construct($statement);
}

public function bindValue(int|string $param, mixed $value, ParameterType $type): void {
$this->params[$param] = $value;
$this->types[$param] = $this->convertParameterTypeToJsonSerializable($type);

parent::bindValue($param, $value, $type);
}

public function execute(): ResultInterface {
$this->queryLogger->startQuery($this->sql, $this->params, $this->types);
$result = parent::execute();
$this->queryLogger->stopQuery();

return $result;
}
}
Loading