Skip to content

Commit

Permalink
override index recovery for PostgreSQL databases
Browse files Browse the repository at this point in the history
  • Loading branch information
Ioannis Igoumenos committed Oct 22, 2023
1 parent 5c0deab commit c2b637c
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 4 deletions.
2 changes: 1 addition & 1 deletion app/config/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@
"identifiers_i2": { "columns": [ "identifier", "type_id", "external_identity_id" ] },
"identifiers_i3": { "columns": [ "type_id" ] },
"identifiers_i4": { "needed": false, "columns": [ "provisioning_target_id" ] },
"identifiers_i5": { "columns": [ "lower(identifier)", "type_id" ] }
"identifiers_i5": { "columns": [ "lower(identifier::text)", "type_id" ] }
},
"mvea": [ "person", "external_identity", "group" ],
"sourced": true
Expand Down
107 changes: 107 additions & 0 deletions app/src/Lib/Util/CmgPostgreSQLSchemaManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace App\Lib\Util;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\DBAL\Schema\PostgreSQLSchemaManager;

class CmgPostgreSQLSchemaManager extends PostgreSQLSchemaManager {
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
$buffer = [];
foreach ($tableIndexes as $row) {
$colNumbers = array_map('intval', explode(' ', $row['indkey']));
$colNames = explode("," , trim($row["indkey_names"], "{,}"));
$colNumName = array_combine($colNumbers, $colNames);

// required for getting the order of the columns right.
foreach ($colNumName as $colNum => $colName) {
$buffer[] = [
'key_name' => $row['relname'],
'column_name' => trim($colName),
'non_unique' => ! $row['indisunique'],
'primary' => $row['indisprimary'],
'where' => $row['where'],
];
}
}

return AbstractSchemaManager::_getPortableTableIndexesList($buffer, $tableName);
}


protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';

if ($tableName === null) {
$sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,';
}

$sql .= <<<'SQL'
quote_ident(ic.relname) AS relname,
i.indisunique,
i.indisprimary,
i.indkey,
i.indexprs,
i.indrelid,
ARRAY(
SELECT pg_get_indexdef(i.indexrelid, k + 1, true)
FROM generate_subscripts(i.indkey, 1) AS k
ORDER BY k
) AS indkey_names,
pg_get_expr(indpred, indrelid) AS "where"
FROM pg_index i
JOIN pg_class AS tc ON tc.oid = i.indrelid
JOIN pg_namespace tn ON tn.oid = tc.relnamespace
JOIN pg_class AS ic ON ic.oid = i.indexrelid
WHERE ic.oid IN (
SELECT indexrelid
FROM pg_index i, pg_class c, pg_namespace n
SQL;

$conditions = array_merge([
'c.oid = i.indrelid',
'c.relnamespace = n.oid',
], $this->buildQueryConditions($tableName));

$sql .= ' WHERE ' . implode(' AND ', $conditions) . ')';

return $this->_conn->executeQuery($sql);
}

/**
* @param string|null $tableName
*
* @return list<string>
*/
private function buildQueryConditions($tableName): array
{
$conditions = [];

if ($tableName !== null) {
if (strpos($tableName, '.') !== false) {
[$schemaName, $tableName] = explode('.', $tableName);
$conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName);
} else {
$conditions[] = 'n.nspname = ANY(current_schemas(false))';
}

$identifier = new Identifier($tableName);
$conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName());
}

$conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')";

return $conditions;
}
}

34 changes: 34 additions & 0 deletions app/src/Lib/Util/CmgSchemaManagerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace App\Lib\Util;

use App\Lib\Util\CmgPostgreSQLSchemaManager;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
use Doctrine\DBAL\Schema\SchemaManagerFactory;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;

final class CmgSchemaManagerFactory implements SchemaManagerFactory
{
private readonly SchemaManagerFactory $defaultFactory;

public function __construct()
{
$this->defaultFactory = new DefaultSchemaManagerFactory();
}

public function createSchemaManager(Connection $connection): AbstractSchemaManager
{
$platform = $connection->getDatabasePlatform();
if ($platform instanceof PostgreSQL1000Platform
|| $platform instanceof PostgreSQLPlatform) {
return new CmgPostgreSQLSchemaManager($connection, $platform);
}

return $this->defaultFactory->createSchemaManager($connection);
}
}
9 changes: 6 additions & 3 deletions app/src/Lib/Util/SchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Cake\Console\ConsoleIo;
use Cake\Datasource\ConnectionInterface;
use Cake\Datasource\ConnectionManager;
use App\Lib\Util\CmgSchemaManagerFactory;

use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Comparator;
Expand Down Expand Up @@ -79,7 +80,9 @@ public function __construct(?ConsoleIo $io=null, string $connection='default') {
$cfg = $db->config();

$config = new \Doctrine\DBAL\Configuration();

// Load the COmanage custom CmgSchemaManagerFactory
$config->setSchemaManagerFactory(new CmgSchemaManagerFactory());

$cfargs = [
'dbname' => $cfg['database'],
'user' => $cfg['username'],
Expand Down Expand Up @@ -325,12 +328,12 @@ protected function processSchema(

// This is the SQL that represents the desired state of the database
$toSql = $schema->toSql($this->conn->getDatabasePlatform());

// SchemaManager provides info about the database
$sm = $this->conn->createSchemaManager();

// The is the current database representation
$curSchema = $sm->createSchema();
$curSchema = $sm->introspectSchema();

$fromSql = $curSchema->toSql($this->conn->getDatabasePlatform());

Expand Down

0 comments on commit c2b637c

Please sign in to comment.