From 031e344fcb1b48e1061025f1d4a3ba6e9cbec453 Mon Sep 17 00:00:00 2001 From: jason Date: Tue, 10 Mar 2026 13:06:42 -0500 Subject: [PATCH] fix: Migration 003 - remove old record_type CHECK constraint from health_records --- server/db/migrations.js | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/server/db/migrations.js b/server/db/migrations.js index 5ff9c37..867c050 100644 --- a/server/db/migrations.js +++ b/server/db/migrations.js @@ -59,6 +59,20 @@ class MigrationRunner { return columns.some(col => col.name === 'litter_id'); } + // Check if health_records has the old restrictive CHECK constraint on record_type + healthRecordsHasOldConstraint() { + try { + const row = this.db.prepare( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='health_records'" + ).get(); + if (!row) return false; + // Old constraint lists only the 5 legacy types + return row.sql.includes("'test', 'vaccination', 'exam', 'treatment', 'certification'"); + } catch (_) { + return false; + } + } + // Migration 1: Remove sire/dam columns, use parents table migration001_removeOldParentColumns() { console.log('[Migration 001] Checking for old sire/dam columns...'); @@ -204,6 +218,87 @@ class MigrationRunner { } } + // Migration 3: Remove old restrictive CHECK constraint on health_records.record_type + // The old schema only allowed: 'test','vaccination','exam','treatment','certification' + // The new schema allows any free-form text (ofa_clearance, etc.) + migration003_removeHealthRecordTypeConstraint() { + console.log('[Migration 003] Checking health_records.record_type constraint...'); + + if (!this.healthRecordsHasOldConstraint()) { + console.log('[Migration 003] No old constraint found, skipping'); + return; + } + + console.log('[Migration 003] Rebuilding health_records table to remove old CHECK constraint...'); + + this.db.exec('BEGIN TRANSACTION'); + + try { + // Backup existing health records + this.db.exec('DROP TABLE IF EXISTS health_records_migration_backup'); + this.db.exec('CREATE TABLE health_records_migration_backup AS SELECT * FROM health_records'); + + const backupCount = this.db.prepare('SELECT COUNT(*) as count FROM health_records_migration_backup').get(); + console.log(`[Migration 003] Backed up ${backupCount.count} health records`); + + // Drop old table (constraint is baked in and cannot be altered) + this.db.exec('DROP TABLE health_records'); + + // Recreate WITHOUT the old CHECK constraint on record_type + this.db.exec(` + CREATE TABLE health_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + dog_id INTEGER NOT NULL, + record_type TEXT NOT NULL, + test_type TEXT, + test_name TEXT, + test_date TEXT NOT NULL, + ofa_result TEXT, + ofa_number TEXT, + performed_by TEXT, + expires_at TEXT, + document_url TEXT, + result TEXT, + vet_name TEXT, + next_due TEXT, + notes TEXT, + created_at TEXT DEFAULT (datetime('now')), + updated_at TEXT DEFAULT (datetime('now')), + FOREIGN KEY (dog_id) REFERENCES dogs(id) + ) + `); + + // Restore all existing records + this.db.exec(` + INSERT INTO health_records + (id, dog_id, record_type, test_type, test_name, test_date, + ofa_result, ofa_number, performed_by, expires_at, + document_url, result, vet_name, next_due, notes, + created_at, updated_at) + SELECT + id, dog_id, record_type, test_type, test_name, test_date, + ofa_result, ofa_number, performed_by, expires_at, + document_url, result, vet_name, next_due, notes, + created_at, updated_at + FROM health_records_migration_backup + `); + + const restoredCount = this.db.prepare('SELECT COUNT(*) as count FROM health_records').get(); + console.log(`[Migration 003] Restored ${restoredCount.count} health records`); + + // Clean up backup + this.db.exec('DROP TABLE health_records_migration_backup'); + + this.db.exec('COMMIT'); + console.log('[Migration 003] ✓ health_records constraint removed successfully!'); + + } catch (error) { + this.db.exec('ROLLBACK'); + console.error('[Migration 003] ✗ Migration failed:', error.message); + throw error; + } + } + // Validate final schema validateSchema() { console.log('[Validation] Checking database schema...'); @@ -240,6 +335,10 @@ class MigrationRunner { const tables = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='litters'").all(); return tables.length > 0; } + }, + { + name: 'health_records has no old record_type CHECK constraint', + test: () => !this.healthRecordsHasOldConstraint() } ]; @@ -284,6 +383,11 @@ class MigrationRunner { this.recordMigration(2, 'Add litter_id column to dogs table'); } + if (currentVersion < 3) { + this.migration003_removeHealthRecordTypeConstraint(); + this.recordMigration(3, 'Remove old record_type CHECK constraint from health_records'); + } + // Validate final schema console.log(''); const isValid = this.validateSchema();