Skip to content

Detect sorts and joins that want an index, not just filters #4

Description

@Truta446

Two plan shapes are worth reporting and are currently ignored.

Sort without an index. A Sort node under a Seq Scan means PostgreSQL read the table and then sorted it in memory (or spilled to disk). An index on the ORDER BY columns removes both steps. This is very common with ORDER BY created_at DESC LIMIT 20 — the pagination query on almost every list endpoint.

Hash Join on an unindexed foreign key. A join whose inner side is a sequential scan usually means the foreign key column has no index.

Where to look

  • src/plan.tswalk() already traverses the tree; Sort Key is typed
  • src/analyze.tsanalyzePlan currently matches only Node Type === "Seq Scan" with a Filter
  • test/analyze.test.ts — fixtures make this easy to test without a database

Acceptance

  • Suggestion.reason distinguishes the cases (it is already a union with one member)
  • ORDER BY suggestions respect direction — ORDER BY a DESC, b ASC needs (a DESC, b ASC), and getting this wrong produces an index the planner will not use
  • The existing sequential-scan tests still pass
  • Each new reason has a fixture test

Watch out for

A sort of 20 rows is not worth an index. Whatever threshold you choose, apply it to the rows being sorted, not the rows returned — LIMIT 20 after sorting 60,000 rows is precisely the case worth reporting, and looking at the wrong number would filter it out. That exact mistake already happened once in this codebase; see the note about Plan Rows in src/analyze.ts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is wanted

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions