package store import ( "database/sql" "errors" "fmt" ) // bindMQScope checks the configured database owner and the immutable tenant // mapping inside the caller's transaction, before committing any input ACK. func bindMQScope(tx *sql.Tx, dispatcherID, tenantID, tenantKey string) error { var owner string if err := tx.QueryRow(`SELECT dispatcher_id FROM dispatcher_identity WHERE singleton=1`).Scan(&owner); err != nil { return fmt.Errorf("read MQ owner: %w", err) } if owner != dispatcherID { return ErrMessageScope } var conflicting int if err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM inbox WHERE (tenant_id=? AND tenant_key<>?) OR (tenant_key=? AND tenant_id<>?))`, tenantID, tenantKey, tenantKey, tenantID).Scan(&conflicting); err != nil { return err } if conflicting != 0 { return ErrMessageScope } if _, err := tx.Exec(`INSERT INTO tenant_bindings(tenant_id,tenant_key) VALUES(?,?) ON CONFLICT DO NOTHING`, tenantID, tenantKey); err != nil { return err } var key string if err := tx.QueryRow(`SELECT tenant_key FROM tenant_bindings WHERE tenant_id=?`, tenantID).Scan(&key); err != nil { if errors.Is(err, sql.ErrNoRows) { return ErrMessageScope } return err } if key != tenantKey { return ErrMessageScope } return nil }