Browse Source

fix: simplify active_identity migration to resolve iOS SQLite failures

The complex table rewrite approach in migration 003_active_did_separation was
failing on iOS SQLite, causing "no such table: active_identity" errors. The
migration was being marked as applied despite validation failures.

Changes:
- Simplify migration SQL to only create active_identity table and migrate data
- Remove complex table rewrite that was failing on iOS SQLite versions
- Remove foreign key constraint that could cause compatibility issues
- Update validation logic to focus on active_identity table existence only
- Remove validation check for activeDid column removal from settings table

This approach is more reliable across different SQLite versions and platforms
while maintaining the core functionality of separating activeDid into its own
table for better database architecture.

Fixes iOS build database errors and ensures migration completes successfully.
pull/188/head
Jose Olarte III 2 weeks ago
parent
commit
578dbe6177
  1. 47
      src/db-sql/migration.ts
  2. 63
      src/services/migrationService.ts

47
src/db-sql/migration.ts

@ -133,16 +133,15 @@ const MIGRATIONS = [
{ {
name: "003_active_did_separation", name: "003_active_did_separation",
sql: ` sql: `
-- CONSOLIDATED MIGRATION: Combines original migrations 003, 004, 005, and 006 -- SIMPLIFIED MIGRATION: Create active_identity table and migrate data
-- This migration handles the complete separation of activeDid from settings -- This migration handles the separation of activeDid from settings
-- and establishes proper data integrity constraints -- using a simpler, more reliable approach that works on all SQLite versions
-- Create new active_identity table with proper constraints -- Create new active_identity table with proper constraints
CREATE TABLE IF NOT EXISTS active_identity ( CREATE TABLE IF NOT EXISTS active_identity (
id INTEGER PRIMARY KEY CHECK (id = 1), id INTEGER PRIMARY KEY CHECK (id = 1),
activeDid TEXT NOT NULL, activeDid TEXT NOT NULL DEFAULT '',
lastUpdated TEXT NOT NULL DEFAULT (datetime('now')), lastUpdated TEXT NOT NULL DEFAULT (datetime('now'))
FOREIGN KEY (activeDid) REFERENCES accounts(did) ON DELETE CASCADE
); );
-- Add performance indexes -- Add performance indexes
@ -154,36 +153,14 @@ const MIGRATIONS = [
-- MIGRATE EXISTING DATA: Copy activeDid from settings to active_identity -- MIGRATE EXISTING DATA: Copy activeDid from settings to active_identity
-- This prevents data loss when migration runs on existing databases -- This prevents data loss when migration runs on existing databases
-- Use a more robust approach that handles missing columns gracefully
UPDATE active_identity UPDATE active_identity
SET activeDid = (SELECT activeDid FROM settings WHERE id = 1), SET activeDid = COALESCE(
lastUpdated = datetime('now') (SELECT activeDid FROM settings WHERE id = 1 AND activeDid IS NOT NULL AND activeDid != ''),
WHERE id = 1 ''
AND EXISTS (SELECT 1 FROM settings WHERE id = 1 AND activeDid IS NOT NULL AND activeDid != ''); ),
lastUpdated = datetime('now')
-- Remove activeDid column from settings table (moved to active_identity) WHERE id = 1;
-- Note: SQLite doesn't support DROP COLUMN in older versions
-- This migration will be skipped if DROP COLUMN is not supported
-- The activeDid column will remain but won't be used by the application
ALTER TABLE settings DROP COLUMN activeDid;
-- Eliminate MASTER_SETTINGS_KEY concept - remove confusing id=1 row
-- This creates clean separation: active_identity for current identity, settings for identity config
DELETE FROM settings WHERE id = 1 AND accountDid IS NULL;
-- Reset auto-increment to start from 1 again
DELETE FROM sqlite_sequence WHERE name = 'settings';
-- Add unique constraint to prevent duplicate accountDid values
-- This ensures data integrity: each identity can only have one settings record
DELETE FROM settings
WHERE id NOT IN (
SELECT MAX(id)
FROM settings
WHERE accountDid IS NOT NULL
GROUP BY accountDid
) AND accountDid IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_settings_accountDid_unique ON settings(accountDid);
`, `,
}, },
]; ];

63
src/services/migrationService.ts

@ -280,6 +280,40 @@ async function validateMigrationApplication<T>(
error, error,
); );
} }
} else if (migration.name === "003_active_did_separation") {
// Validate active_identity table exists and has correct structure
try {
// Check that active_identity table exists
const activeIdentityResult = await sqlQuery(
`SELECT name FROM sqlite_master WHERE type='table' AND name='active_identity'`,
);
const hasActiveIdentityTable =
(activeIdentityResult as unknown as { values: unknown[][] })?.values?.length > 0 ||
(Array.isArray(activeIdentityResult) && activeIdentityResult.length > 0);
if (!hasActiveIdentityTable) {
validation.isValid = false;
validation.errors.push(`Table active_identity missing`);
}
// Check that active_identity has the expected structure
try {
await sqlQuery(`SELECT id, activeDid, lastUpdated FROM active_identity LIMIT 1`);
validation.hasExpectedColumns = true;
} catch (error) {
validation.isValid = false;
validation.errors.push(`active_identity table missing expected columns`);
}
validation.tableExists = hasActiveIdentityTable;
} catch (error) {
validation.isValid = false;
validation.errors.push(`Validation error for active_did_separation: ${error}`);
logger.error(
`❌ [Migration-Validation] Validation failed for ${migration.name}:`,
error,
);
}
} }
// Add validation for future migrations here // Add validation for future migrations here
@ -343,6 +377,35 @@ async function isSchemaAlreadyPresent<T>(
// Reduced logging - only log on error // Reduced logging - only log on error
return false; return false;
} }
} else if (migration.name === "003_active_did_separation") {
// Check if active_identity table exists and has correct structure
try {
// Check that active_identity table exists
const activeIdentityResult = await sqlQuery(
`SELECT name FROM sqlite_master WHERE type='table' AND name='active_identity'`,
);
const hasActiveIdentityTable =
(activeIdentityResult as unknown as { values: unknown[][] })?.values?.length > 0 ||
(Array.isArray(activeIdentityResult) && activeIdentityResult.length > 0);
if (!hasActiveIdentityTable) {
return false;
}
// Check that active_identity has the expected structure
try {
await sqlQuery(`SELECT id, activeDid, lastUpdated FROM active_identity LIMIT 1`);
return true;
} catch (error) {
return false;
}
} catch (error) {
logger.error(
`🔍 [Migration-Schema] Schema check failed for ${migration.name}, assuming not present:`,
error,
);
return false;
}
} }
// Add schema checks for future migrations here // Add schema checks for future migrations here

Loading…
Cancel
Save