Skip to content

Commit

Permalink
Fix database command warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
Ioannis committed Sep 1, 2025
1 parent 25b3e17 commit a398624
Showing 1 changed file with 73 additions and 4 deletions.
77 changes: 73 additions & 4 deletions app/src/Lib/Util/SchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,40 @@
class SchemaManager {
use \App\Lib\Traits\LabeledLogTrait;

/**
* Resolve a PostgreSQL constraint's owning table (schema-qualified) by constraint name.
* Returns null if the given name is not found as a constraint.
*/
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 = <<<CATALOG
SELECT n.nspname AS schema, c.relname AS table
FROM pg_constraint co
JOIN pg_class c ON c.oid = co.conrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE co.conname = :name
LIMIT 1
CATALOG;

try {
$stmt = $this->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;
}
}

// Console for output
protected $io = null;

Expand Down Expand Up @@ -359,12 +393,47 @@ 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) {
// Replace with DROP CONSTRAINT on the owning table
$translated = 'ALTER TABLE ' . $qualifiedTable . ' DROP CONSTRAINT ' . $idxOrConstraint;
if($this->io) $this->io->out($translated . ' -- translated from DROP INDEX for PostgreSQL');
if(!$diffOnly) {
$this->conn->executeQuery($translated);
}
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
}
}

Expand Down

0 comments on commit a398624

Please sign in to comment.