diff --git a/scripts/apply_hotgo_tenant_db.go b/scripts/apply_hotgo_tenant_db.go index c6a1cba..cbcd16e 100644 --- a/scripts/apply_hotgo_tenant_db.go +++ b/scripts/apply_hotgo_tenant_db.go @@ -566,6 +566,14 @@ func main() { "ALTER TABLE tinode.im_youth_mode_settings ADD COLUMN policy_version INT NOT NULL DEFAULT 1 AFTER enabled", "ALTER TABLE tinode.im_youth_mode_settings ADD COLUMN restrictions JSON AFTER policy_version", "ALTER TABLE tinode.im_youth_mode_settings ADD COLUMN enabled_at DATETIME(3) AFTER restrictions", + // auth/credentials: server/db/mysql/adapter.go's CreateDb never defined a + // tenant_id column here even on a fresh install, yet every auth/credentials + // query unconditionally filters/inserts by tenant_id - a latent bug in the + // fresh-schema definition, not something version-skip alone explains. Backfill + // existing rows to the default tenant (id=1); adapter.go's CREATE TABLE was + // fixed alongside this to include the column on future fresh installs too. + "ALTER TABLE tinode.auth ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 1 AFTER id", + "ALTER TABLE tinode.credentials ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 1 AFTER id", } { if _, err = db.Exec(stmt); err != nil && !strings.Contains(err.Error(), "Duplicate column name") { fatal(fmt.Errorf("exec failed: %s: %w", stmt, err)) @@ -673,10 +681,17 @@ func fatal(v any) { os.Exit(1) } +// keptTenantIDTables lists tables whose tenant_id column must survive +// dropTenantIDColumns: server/db/mysql/adapter.go queries auth and credentials +// by tenant_id unconditionally (not gated behind tenant_db_routing), so unlike +// the newer per-tenant-DB feature tables, these two are row-tagged by design. +var keptTenantIDTables = "'auth','credentials'" + func dropTenantIDColumns(db *sql.DB, schema string) error { fks, err := queryPairs(db, `SELECT DISTINCT table_name, constraint_name FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA=? AND COLUMN_NAME='tenant_id' AND REFERENCED_TABLE_NAME IS NOT NULL`, + WHERE TABLE_SCHEMA=? AND COLUMN_NAME='tenant_id' AND REFERENCED_TABLE_NAME IS NOT NULL + AND table_name NOT IN (`+keptTenantIDTables+`)`, schema) if err != nil { return err @@ -689,7 +704,8 @@ func dropTenantIDColumns(db *sql.DB, schema string) error { indexes, err := queryPairs(db, `SELECT DISTINCT table_name, index_name FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA=? AND COLUMN_NAME='tenant_id' AND INDEX_NAME<>'PRIMARY'`, + WHERE TABLE_SCHEMA=? AND COLUMN_NAME='tenant_id' AND INDEX_NAME<>'PRIMARY' + AND table_name NOT IN (`+keptTenantIDTables+`)`, schema) if err != nil { return err @@ -702,6 +718,7 @@ func dropTenantIDColumns(db *sql.DB, schema string) error { rows, err := db.Query(`SELECT table_name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=? AND COLUMN_NAME='tenant_id' + AND table_name NOT IN (`+keptTenantIDTables+`) ORDER BY table_name`, schema) if err != nil { return err diff --git a/server/db/mysql/adapter.go b/server/db/mysql/adapter.go index df7ccbc..b0c07e7 100644 --- a/server/db/mysql/adapter.go +++ b/server/db/mysql/adapter.go @@ -657,7 +657,8 @@ func (a *adapter) CreateDb(reset bool) error { // Authentication records for the basic authentication scheme. if _, err = tx.Exec( `CREATE TABLE auth( - id INT NOT NULL AUTO_INCREMENT, + id INT NOT NULL AUTO_INCREMENT, + tenant_id BIGINT NOT NULL, uname VARCHAR(32) NOT NULL, userid BIGINT NOT NULL, scheme VARCHAR(16) NOT NULL, @@ -835,6 +836,7 @@ func (a *adapter) CreateDb(reset bool) error { if _, err = tx.Exec( `CREATE TABLE credentials( id INT NOT NULL AUTO_INCREMENT, + tenant_id BIGINT NOT NULL, createdat DATETIME(3) NOT NULL, updatedat DATETIME(3) NOT NULL, deletedat DATETIME(3),