//go:build mysql // +build mysql package mysql import ( "context" "database/sql" "errors" "github.com/jmoiron/sqlx" ) const userPrivacyMigrationLock = "CONCAT('im_user_privacy:', LEFT(SHA2(DATABASE(), 256), 40))" // ensureUserPrivacySchema adds columns of im_user_privacy_settings that were // introduced after the table's initial release. Databases created fresh by // createIMUserProductTables already have them; legacy databases are upgraded // in place. A missing table is skipped: it is created by the fresh-install // path and there is nothing to upgrade yet. func ensureUserPrivacySchema(db *sqlx.DB) error { ctx := context.Background() conn, err := db.Connx(ctx) if err != nil { return err } defer conn.Close() var locked sql.NullInt64 if err = conn.QueryRowContext(ctx, "SELECT GET_LOCK("+userPrivacyMigrationLock+", 60)").Scan(&locked); err != nil { return err } if !locked.Valid || locked.Int64 != 1 { return errors.New("mysql: timed out waiting for user privacy migration lock") } defer conn.ExecContext(ctx, "SELECT RELEASE_LOCK("+userPrivacyMigrationLock+")") tableExists, err := identityTableExists(ctx, conn, "im_user_privacy_settings") if err != nil || !tableExists { return err } // Column definitions mirror createIMUserProductTables; the SELECT in // userPrivacyByUID references every one of them. columns := []struct{ name, ddl string }{ {"profile_visibility", "VARCHAR(32) NOT NULL DEFAULT 'friends'"}, {"last_seen_visibility", "VARCHAR(32) NOT NULL DEFAULT 'friends'"}, {"read_receipts", "TINYINT NOT NULL DEFAULT 1"}, {"friend_request_verification", "TINYINT NOT NULL DEFAULT 1"}, {"group_invite_verification", "TINYINT NOT NULL DEFAULT 0"}, } for _, col := range columns { exists, err := identityColumnExists(ctx, conn, "im_user_privacy_settings", col.name) if err != nil { return err } if !exists { if _, err = conn.ExecContext(ctx, "ALTER TABLE im_user_privacy_settings ADD COLUMN "+col.name+" "+col.ddl); err != nil { return err } } } return nil }