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