Pin critical auth indexes to COLLATE "C" (ICU-drift immune)
idx_users_email and idx_sessions_token_hash now use byte-order collation so a future ICU library bump cannot silently corrupt the indexes the way it did this week. The columns themselves retain their default collation; only the index comparison is C, which is safe for these because: - users.email is lowercased ASCII in practice - user_sessions.token_hash is SHA-256 hex (pure ASCII) Both are used for equality lookups only, never ORDER BY. Migration is idempotent, gated on app_settings.migration.text_indexes_c. Slug indexes on learning_* tables left at default for now — those are also ASCII in practice but under lighter load; the startup drift check + auto-REINDEX covers them.
This commit is contained in:
parent
cf50d8577e
commit
4f51f1efb5
1 changed files with 31 additions and 0 deletions
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue