Add migration script to fix microchip UNIQUE constraint
This commit is contained in:
138
server/db/migrate_microchip.js
Normal file
138
server/db/migrate_microchip.js
Normal 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 };
|
||||
Reference in New Issue
Block a user