From f1e5b422b01dc24a8e56e4f063fe67f291d2ed38 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:40:02 -0500 Subject: [PATCH] Add migration script to fix microchip UNIQUE constraint --- server/db/migrate_microchip.js | 138 +++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 server/db/migrate_microchip.js diff --git a/server/db/migrate_microchip.js b/server/db/migrate_microchip.js new file mode 100644 index 0000000..58c24af --- /dev/null +++ b/server/db/migrate_microchip.js @@ -0,0 +1,138 @@ +#!/usr/bin/env node + +/** + * Migration: Fix microchip UNIQUE constraint + * + * Problem: The microchip field had a UNIQUE constraint which prevents multiple NULL values. + * Solution: Remove the constraint and create a partial unique index that only applies to non-NULL values. + * + * This script can be run safely multiple times. + */ + +const Database = require('better-sqlite3'); +const path = require('path'); +const fs = require('fs'); + +function migrateMicrochip() { + const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/breedr.db'); + + if (!fs.existsSync(dbPath)) { + console.log('No database found at', dbPath); + console.log('Skipping migration - database will be created with correct schema.'); + return; + } + + console.log('Migrating database:', dbPath); + const db = new Database(dbPath); + db.pragma('foreign_keys = OFF'); // Temporarily disable for migration + + try { + console.log('Starting microchip field migration...'); + + // Check if the old unique constraint exists + const tableInfo = db.pragma('table_info(dogs)'); + const microchipField = tableInfo.find(col => col.name === 'microchip'); + + if (!microchipField) { + console.log('Microchip field not found. Skipping migration.'); + return; + } + + // SQLite doesn't support ALTER COLUMN, so we need to recreate the table + console.log('Step 1: Creating new dogs table with correct schema...'); + + db.exec(` + -- Create new table with correct schema + CREATE TABLE IF NOT EXISTS dogs_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + registration_number TEXT UNIQUE, + breed TEXT NOT NULL, + sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), + birth_date DATE, + color TEXT, + microchip TEXT, + photo_urls TEXT, + notes TEXT, + is_active INTEGER DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + `); + + console.log('Step 2: Copying data from old table...'); + + // Copy all data from old table to new table + db.exec(` + INSERT INTO dogs_new + SELECT * FROM dogs; + `); + + console.log('Step 3: Dropping old table and renaming new table...'); + + // Drop old table and rename new table + db.exec(` + DROP TABLE dogs; + ALTER TABLE dogs_new RENAME TO dogs; + `); + + console.log('Step 4: Creating partial unique index for microchip...'); + + // Create partial unique index (only applies to non-NULL values) + db.exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_dogs_microchip + ON dogs(microchip) + WHERE microchip IS NOT NULL; + `); + + console.log('Step 5: Recreating other indexes...'); + + // Recreate other indexes + db.exec(` + CREATE INDEX IF NOT EXISTS idx_dogs_name ON dogs(name); + CREATE INDEX IF NOT EXISTS idx_dogs_registration ON dogs(registration_number); + `); + + console.log('Step 6: Recreating triggers...'); + + // Recreate the updated_at trigger + db.exec(` + DROP TRIGGER IF EXISTS update_dogs_timestamp; + CREATE TRIGGER update_dogs_timestamp + AFTER UPDATE ON dogs + FOR EACH ROW + BEGIN + UPDATE dogs SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; + END; + `); + + console.log('✓ Migration completed successfully!'); + console.log('✓ Microchip field is now optional and can be left empty.'); + console.log('✓ Multiple dogs can have no microchip (NULL value).'); + console.log('✓ Unique constraint still prevents duplicate microchip numbers.'); + + } catch (error) { + console.error('Migration failed:', error.message); + console.error('\nYou may need to restore from backup or manually fix the database.'); + throw error; + } finally { + db.pragma('foreign_keys = ON'); // Re-enable foreign keys + db.close(); + } +} + +if (require.main === module) { + console.log('='.repeat(60)); + console.log('BREEDR Database Migration: Microchip Field Fix'); + console.log('='.repeat(60)); + console.log(''); + + migrateMicrochip(); + + console.log(''); + console.log('='.repeat(60)); + console.log('Migration Complete'); + console.log('='.repeat(60)); +} + +module.exports = { migrateMicrochip }; \ No newline at end of file