From fe7fd3690360f63ab3c749eb918df4889193b0d3 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 1 Sep 2025 10:51:14 +0300 Subject: [PATCH] Fix database command warnings --- app/src/Lib/Util/SchemaManager.php | 87 +++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/app/src/Lib/Util/SchemaManager.php b/app/src/Lib/Util/SchemaManager.php index 528ef2bd0..8405034bf 100644 --- a/app/src/Lib/Util/SchemaManager.php +++ b/app/src/Lib/Util/SchemaManager.php @@ -327,14 +327,16 @@ protected function processSchema( } // This is the SQL that represents the desired state of the database + // XXX This never used $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(); - + // This is the current database representation + $curSchema = $sm->introspectSchema(); + + // XXX This is never used $fromSql = $curSchema->toSql($this->conn->getDatabasePlatform()); try { @@ -359,12 +361,43 @@ protected function processSchema( // Remove the DROP SEQUENCE statements in $fromSql because they're Postgres automagic // being misinterpreted. (Note toSaveSql might mask this now.) // XXX Maybe debug and file a PR to not emit DROP SEQUENCE on PG for autoincrementesque fields? - if($this->io) $io->out("Skipping sequence drop"); - } else { + if($this->io) $this->io->out("Skipping sequence drop"); + continue; + } + + // PostgreSQL: translate DROP INDEX for names that are actually unique constraints + if($this->conn->driver == 'Cake\Database\Driver\Postgres' + && preg_match('/^DROP INDEX\s+"?([a-zA-Z0-9_\.]+)"?/i', $sql, $m)) { + $idxOrConstraint = $m[1]; + + // Resolve if this name is a constraint and on which table + $qualifiedTable = $this->resolvePgConstraintTable($idxOrConstraint); + + if($qualifiedTable !== null) { + // We will skip this index if it's bound to a constraint + if($this->io) $this->io->out('Skipping index drop since it is bound to a constraint'); + continue; + } + } + + // PostgreSQL: make index renames idempotent and tolerant using IF EXISTS + if($this->conn->driver == 'Cake\Database\Driver\Postgres' + && preg_match('/^ALTER INDEX\s+([^\s]+)\s+RENAME\s+TO\s+([^\s]+)$/i', trim($sql), $m)) { + $oldName = $m[1]; + $newName = $m[2]; + + // Use IF EXISTS to avoid error if the old index name doesn't exist in this environment + $translated = 'ALTER INDEX IF EXISTS ' . $oldName . ' RENAME TO ' . $newName; + if($this->io) $this->io->out($translated . ' -- added IF EXISTS for PostgreSQL resilience'); if(!$diffOnly) { - $stmt = $this->conn->executeQuery($sql); - // $stmt just returns the query string so we don't bother examining it + $this->conn->executeQuery($translated); } + continue; + } + + if(!$diffOnly) { + $stmt = $this->conn->executeQuery($sql); + // $stmt just returns the query string so we don't bother examining it } } @@ -380,4 +413,42 @@ protected function processSchema( // but so far we don't have an example indicating it's needed. } -} \ No newline at end of file + /** + * Resolve a PostgreSQL constraint's owning table (schema-qualified) by constraint name. + * Returns null if the given name is not found as a constraint. + * + * @param string $constraintName + * @return ?string Schema-qualified table name or null if constraint not found + * @since COmanage Registry v5.2.0 + */ + + protected function resolvePgConstraintTable(string $constraintName): ?string { + if($this->conn->driver !== 'Cake\\Database\\Driver\\Postgres') { + return null; + } + + // Query pg catalogs to find the table for this constraint name + // We return schema-qualified name: schema.table + $sql = <<conn->executeQuery($sql, ['name' => $constraintName]); + $row = $stmt->fetchAssociative(); + if(!$row || empty($row['schema']) || empty($row['table'])) { + return null; + } + // Quote/qualify as schema.table (matching how other statements look) + return $row['schema'] . '.' . $row['table']; + } catch(\Throwable $e) { + // On any error, fail open by returning null so caller uses original SQL + return null; + } + } +}