From 4e190dc7ec35e640e3096bc22474147c0f1bbbac Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 22 Apr 2026 21:34:48 +0100 Subject: [PATCH] fix(brain): Postgres portability for brain connection + migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three related fixes so the brain DB works on Postgres, not just MariaDB: 1. config.php — brain charset/collation was hardcoded to utf8mb4 which Postgres rejects as client_encoding. Now driver-aware: utf8 for pgsql, utf8mb4 otherwise. Override via BRAIN_DB_CHARSET env var. 2. Migration 000008 (create_brain_memories) — self-referential FK on supersedes_id was declared inside Schema::create{}, causing Postgres to evaluate it before the PK index existed ('no unique constraint matching given keys'). Split into Schema::create + separate Schema::table to guarantee PK is in place when FK is added. 3. Migration 000009 (drop workspace FK) — try/catch inside the Blueprint closure couldn't catch deferred SQL failures. Replaced with a constraint-exists pre-query against information_schema, supporting both pgsql and mariadb/mysql drivers. Fresh installs no longer fail trying to drop a constraint that was never created. Co-Authored-By: Virgil --- ..._01_000008_create_brain_memories_table.php | 8 +++++ ...00009_drop_brain_memories_workspace_fk.php | 33 +++++++++++++++---- php/config.php | 7 ++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/php/Migrations/0001_01_01_000008_create_brain_memories_table.php b/php/Migrations/0001_01_01_000008_create_brain_memories_table.php index e6c680d..509291b 100644 --- a/php/Migrations/0001_01_01_000008_create_brain_memories_table.php +++ b/php/Migrations/0001_01_01_000008_create_brain_memories_table.php @@ -43,7 +43,15 @@ return new class extends Migration $table->index('agent_id'); $table->index(['workspace_id', 'type']); $table->index(['workspace_id', 'project']); + }); + // Self-referential FK added AFTER create so Postgres sees the + // primary key on `id` when evaluating the constraint. Adding it + // inside the create{} block ordered the FK before the PK index + // on some Postgres versions, breaking with: + // SQLSTATE[42830]: there is no unique constraint matching + // given keys for referenced table "brain_memories" + $schema->table('brain_memories', function (Blueprint $table) { $table->foreign('supersedes_id') ->references('id') ->on('brain_memories') diff --git a/php/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php b/php/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php index 3f8ee38..105f79f 100644 --- a/php/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php +++ b/php/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php @@ -25,13 +25,34 @@ return new class extends Migration return; } - $schema->table('brain_memories', function (Blueprint $table) { - try { + // Laravel Blueprint defers statement execution until the closure + // returns, so a try/catch INSIDE the closure doesn't catch the + // deferred SQL's failure. Instead, check the constraint exists + // before issuing the drop, across both pgsql + mariadb backends. + $conn = $schema->getConnection(); + $driver = $conn->getDriverName(); + $constraint = 'brain_memories_workspace_id_foreign'; + + $exists = match ($driver) { + 'pgsql' => (bool) $conn->selectOne( + "SELECT 1 FROM information_schema.table_constraints + WHERE table_name = 'brain_memories' AND constraint_name = ?", + [$constraint], + ), + 'mariadb', 'mysql' => (bool) $conn->selectOne( + "SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = DATABASE() AND table_name = 'brain_memories' + AND constraint_name = ?", + [$constraint], + ), + default => false, // unknown driver — don't attempt the drop + }; + + if ($exists) { + $schema->table('brain_memories', function (Blueprint $table) { $table->dropForeign(['workspace_id']); - } catch (\Throwable) { - // FK doesn't exist — fresh install, nothing to drop. - } - }); + }); + } } public function down(): void diff --git a/php/config.php b/php/config.php index 61d8b84..1599522 100644 --- a/php/config.php +++ b/php/config.php @@ -95,8 +95,11 @@ return [ 'database' => env('BRAIN_DB_DATABASE', env('DB_DATABASE', 'forge')), 'username' => env('BRAIN_DB_USERNAME', env('DB_USERNAME', 'forge')), 'password' => env('BRAIN_DB_PASSWORD', env('DB_PASSWORD', '')), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', + // charset defaults: utf8 for pgsql (Postgres rejects 'utf8mb4'), + // utf8mb4 for everything else (MariaDB/MySQL). Override with + // BRAIN_DB_CHARSET if your instance needs something specific. + 'charset' => env('BRAIN_DB_CHARSET', env('BRAIN_DB_DRIVER', env('DB_CONNECTION', 'mariadb')) === 'pgsql' ? 'utf8' : 'utf8mb4'), + 'collation' => env('BRAIN_DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => '', ], ],