Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<repair-steps>
<post-migration>
<step>OCA\Polls\Migration\RepairSteps\UpdateHashes</step>
<step>OCA\Polls\Migration\RepairSteps\MigratePublicToOpen</step>
</post-migration>
</repair-steps>
<commands>
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
"allow-plugins": {
"bamarni/composer-bin-plugin": true
}
},
},
"autoload": {
"psr-4": {
"OCA\\Polls\\": "lib/"
}
},
"autoload-dev": {
"psr-4": {
"OCA\\Polls\\Tests\\": "tests/"
"OCA\\Polls\\Tests\\": "tests/",
"OCP\\": "vendor/nextcloud/ocp/OCP/"
}
},
"require-dev": {
Expand Down Expand Up @@ -66,4 +67,4 @@
"league/commonmark": "^2.1",
"rlanvin/php-rrule": "^2.3"
}
}
}
73 changes: 61 additions & 12 deletions lib/Db/V4/DbManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@

use Doctrine\DBAL\Schema\Schema;
use Exception;
use OCA\Polls\Exceptions\InvalidClassException;
use OCA\Polls\Exceptions\PreconditionColumnIsMissingException;
use OCA\Polls\Exceptions\PreconditionException;
use OCA\Polls\Exceptions\PreconditionTableIsMissingException;
use OCA\Polls\Exceptions\SchemaMissmatchException;
use OCP\DB\ISchemaWrapper;
use OCP\IConfig;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;

abstract class DbManager {

Expand All @@ -24,6 +28,7 @@ abstract class DbManager {
public function __construct(
protected IConfig $config,
protected IDBConnection $connection,
protected LoggerInterface $logger,
) {
$this->dbPrefix = $this->config->getSystemValue('dbtableprefix', 'oc_');
}
Expand Down Expand Up @@ -56,12 +61,14 @@ public function createSchema(): void {
* This method is used to apply the schema changes to the database.
* It must be called after the schema is set.
*
* @throws InvalidClassException if the schema is not an instance of Schema class
* @throws SchemaMissmatchException if the schema is not an instance of Schema class
*/
public function migrateToSchema() : void {
// Schema must be of class Schema
$this->needsSchema(allowISchemWrapperClass: false);
$this->connection->migrateToSchema($this->schema);
if ($this->schema instanceof Schema) {
$this->connection->migrateToSchema($this->schema);
}
}

/**
Expand All @@ -80,9 +87,9 @@ public function setConnection(IDBConnection &$connection): void {
* ISchemaWrapper already uses the prefixed table name, but Schema does not.
*
* @param string $tableName without prefix
* @return string|null
* @return string tableName with prefix
*/
protected function getTableName(string $tableName): ?string {
protected function getTableName(string $tableName): string {
if ($this->schema instanceof Schema) {
// If the schema is an instance of Schema, we need to prefix the table name
return $this->dbPrefix . $tableName;
Expand All @@ -93,9 +100,10 @@ protected function getTableName(string $tableName): ?string {
/**
* Use this as a predetermined breaking point to ensure if a method needs a schema to be set.
*
* @param bool $allowSchemaClass allow schema to be an instance of Schema (default is true)
* @param bool $allowISchemWrapperClass allow schema to be an instance of ISchemaWrapper (default is true)
* @throws InvalidClassException if the schema is not set or not of a required class
* @param bool $allowSchemaClass allow schema to be an instance of Schema (default is true). table names are not prefixed
* @param bool $allowISchemWrapperClass allow schema to be an instance of ISchemaWrapper (default is true), tablen ames are prefixed
*
* @throws SchemaMissmatchException if the schema is not set or not of a required class
*/
protected function needsSchema(bool $allowSchemaClass = true, bool $allowISchemWrapperClass = true): void {
if (($this->schema instanceof Schema) && $allowSchemaClass) {
Expand All @@ -108,17 +116,58 @@ protected function needsSchema(bool $allowSchemaClass = true, bool $allowISchemW

if ($allowSchemaClass && $allowISchemWrapperClass) {
// If the schema is not set or not an instance of Schema or ISchemaWrapper, throw an exception
throw new InvalidClassException('Schema is not set or not an instance of Schema or ISchemaWrapper (caller: ' . self::formatCaller() . ')');
throw new SchemaMissmatchException('Schema is not set or not an instance of Schema or ISchemaWrapper (caller: ' . self::formatCaller() . ')');
}
if ($allowSchemaClass) {
// If the schema is not set or not an instance of Schema, throw an exception
throw new InvalidClassException('Schema is not set or not an instance of Schema (caller: ' . self::formatCaller() . ')');
throw new SchemaMissmatchException('Schema is not set or not an instance of Schema (caller: ' . self::formatCaller() . ')');
}
if ($allowISchemWrapperClass) {
// If the schema is not set or not an instance of ISchemaWrapper, throw an exception
throw new InvalidClassException('Schema is not set or not an instance of ISchemaWrapper(caller: ' . self::formatCaller() . ')');
throw new SchemaMissmatchException('Schema is not set or not an instance of ISchemaWrapper(caller: ' . self::formatCaller() . ')');
}
throw new SchemaMissmatchException('Unexpected. Schema is an instance of ' . get_class($this->schema) . '(caller: ' . self::formatCaller() . ')');
}

/**
* Check if the table and columns exist
* If columnNames is empty only the table is checked
*
* @param string $tableName Unprefixed tablename
* @param string[]|string $columnNames Column name or array of column names to check
*
* @throws PreconditionException on any precondition failure
* @throws PreconditionTableIsMissingException if the table does not exist
* @throws PreconditionColumnIsMissingException if a column does not exist
*
*/
protected function checkPrecondition(string $tableName, array|string $columnNames = []): void {
$prefixedTableName = $this->dbPrefix . $tableName;

if (!$this->connection->tableExists($tableName)) {
$this->logger->error('{db} is missing', [ 'db' => $prefixedTableName]);
throw new PreconditionTableIsMissingException('Table ' . $prefixedTableName . ' does not exist');
}

if (empty($columnNames)) {
return; // Only the table is checked
}

$schema = $this->connection->createSchema();
$table = $schema->getTable($prefixedTableName);

if (is_string($columnNames)) {
$columnNames = [$columnNames];
}
throw new InvalidClassException('Unexpected. Schema is an instance of ' . get_class($this->schema) . '(caller: ' . self::formatCaller() . ')');

foreach ($columnNames as $columnName) {
if (!$table->hasColumn($columnName)) {
$this->logger->error('{db} is missing column \'{column}\'', [ 'db' => $prefixedTableName, 'column' => $columnName]);
throw new PreconditionColumnIsMissingException('Column ' . $columnName . ' does not exist in ' . $prefixedTableName);
}
}

return;
}

private static function formatCaller(int $skip = 1): string {
Expand Down
8 changes: 3 additions & 5 deletions lib/Db/V4/IndexManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@
namespace OCA\Polls\Db\V4;

use Doctrine\DBAL\Schema\Exception\IndexDoesNotExist;
use Doctrine\DBAL\Schema\Schema;
use Exception;
use OCA\Polls\Migration\V4\TableSchema;
use OCP\DB\ISchemaWrapper;
use OCP\IConfig;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;

/** @psalm-suppress UnusedClass */
class IndexManager extends DbManager {

// private Schema|ISchemaWrapper $schema;

/** @psalm-suppress PossiblyUnusedMethod */
public function __construct(
protected IConfig $config,
protected IDBConnection $connection,
protected LoggerInterface $logger,
) {
parent::__construct($config, $connection);
parent::__construct($config, $connection, $logger);
}

/**
Expand Down
Loading