Melange v0.7.2
Melange v0.7.2 adds performance linting to the melange doctor command and fixes two bugs reported by community contributors.
melange migrate to pick up the list_objects fix for closure userset patterns.Highlights
Performance Linting in melange doctor
The doctor command now detects the #1 production performance issue: missing expression indexes for ::text casts in the melange_tuples view. In benchmarks, missing indexes cause 26x slower queries at 1M tuples — and previously, there was no way to catch this before it hit production.
New checks under the Performance category:
- View definition parsing — parses the output of
pg_get_viewdef()to detectUNIONvsUNION ALLusage, enumerate source tables, and map column expressions - Expression index analysis — detects missing
::textexpression indexes per source table, with severity based on row count (<10K= Warn,>=10K= Fail) and exactCREATE INDEXfix hints
$ melange doctor
Performance
✓ View definition parsed (3 branches)
✓ UNION ALL used (no duplicate elimination overhead)
✗ Missing expression index on orders.user_id::text (table has 1.2M rows)
Fix: CREATE INDEX idx_orders_user_id_text ON orders ((user_id::text));Performance checks only run when melange_tuples is a view (not a table or materialized view) and can be disabled via --skip-performance or the doctor.skip_performance config option.
Bug Fixes
Fix list_objects Missing Results for Closure Userset Patterns
Fixed a bug (#30) where list_*_objects functions only expanded group#member userset lookups for the first relation in a computed permission’s closure. When multiple relations shared the same userset pattern (e.g., both editor and manager accept group#member), only one got SQL expansion — causing missing results.
The root cause was the dedup key in ClosureUsersetPatterns using SubjectType#SubjectRelation alone. Adding the source relation to the key ensures each contributing relation gets its own expansion.
Thanks to @jtbeach for identifying and fixing this.
Fix Uppercase Relation Names Breaking Re-Migration
Fixed a bug (#26) where schemas with uppercase relation names (e.g., define Member: [user]) caused functions to be dropped on re-migration.
PostgreSQL folds unquoted identifiers to lowercase, so CREATE FUNCTION check_organization_Member(...) creates a function named check_organization_member in pg_proc. However, CollectFunctionNames returned names with the original casing, causing the orphan detection logic to see the real lowercase functions as unexpected and drop them. The fix folds Ident() output to lowercase, matching PostgreSQL’s identifier folding behavior.
Thanks to @Desuuuu for reporting this (#26) and submitting an initial fix (#27).
Contributors
Thanks to the contributors who made this release possible:
- @jtbeach — fixed
list_objectsmissing results for closure userset patterns (#30) - @Desuuuu — reported uppercase relation name re-migration bug (#26, #27)
Migration Notes
From v0.7.1
No breaking changes. Upgrade and run migrations to pick up the fixed list_objects SQL:
melange migrateTo use the new performance linting:
melange doctorTry It Out
# Install / upgrade CLI
brew install pthm/melange/melange
# Apply migrations
melange migrate
# Go runtime
go get github.com/pthm/melange/melange@v0.7.2
# TypeScript runtime
npm install @pthm/melangeFeedback
We welcome feedback and bug reports. Please open an issue with questions or feature requests.
