Melange v0.7.2

Melange v0.7.2

March 15, 2026·pthm
pthm

Melange v0.7.2 adds performance linting to the melange doctor command and fixes two bugs reported by community contributors.

No breaking changes from v0.7.1. Upgrade and run 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 detect UNION vs UNION ALL usage, enumerate source tables, and map column expressions
  • Expression index analysis — detects missing ::text expression indexes per source table, with severity based on row count (<10K = Warn, >=10K = Fail) and exact CREATE INDEX fix 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_objects missing 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 migrate

To use the new performance linting:

melange doctor

Try 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/melange

Feedback

We welcome feedback and bug reports. Please open an issue with questions or feature requests.