diff --git a/src/db/database.js b/src/db/database.js index b17ac18..30849f1 100644 --- a/src/db/database.js +++ b/src/db/database.js @@ -50,6 +50,37 @@ async function initDatabase() { console.warn('⚠️ Collation check skipped:', e.message); } + // Convert lookup-only text indexes to COLLATE "C" so a future ICU/glibc + // version bump cannot silently corrupt them. Idempotent — driven by a + // flag in app_settings so we only do the DROP/CREATE once. + try { + var done = await client.query( + "SELECT value FROM app_settings WHERE key = 'migration.text_indexes_c'" + ).catch(function() { return { rows: [] }; }); + if (!done.rows.length || done.rows[0].value !== 'true') { + console.log('Migrating text lookup indexes to COLLATE "C"...'); + var indexMigrations = [ + // users.email — primary auth lookup + 'DROP INDEX IF EXISTS idx_users_email', + 'CREATE INDEX idx_users_email ON users (email COLLATE "C")', + // user_sessions.token_hash — session auth + 'DROP INDEX IF EXISTS idx_sessions_token_hash', + 'CREATE INDEX idx_sessions_token_hash ON user_sessions (token_hash COLLATE "C")' + ]; + for (var i = 0; i < indexMigrations.length; i++) { + await client.query(indexMigrations[i]); + } + // Mark complete — create the setting row if the table exists. + await client.query( + "INSERT INTO app_settings (key, value) VALUES ('migration.text_indexes_c', 'true') " + + "ON CONFLICT (key) DO UPDATE SET value = 'true'" + ).catch(function() {}); + console.log('✅ Text indexes: migrated to COLLATE "C" (ICU-drift immune)'); + } + } catch (e) { + console.warn('⚠️ Index migration skipped:', e.message); + } + await client.query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY,