Add migration script to fix microchip UNIQUE constraint

This commit is contained in:
2026-03-08 23:40:02 -05:00
parent bb0f5dd9b8
commit f1e5b422b0

View File

@@ -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 };