CommitFest 7392 Review: amorderbyvalsexact — ORDER BY values from index scan to targetlist

code-review · generated from canonical Markdown

Type
code-review
Source
postgresql-master
Slug
commitfest-7392-review
Tags
code-reviewcoveragepatch-reviewpg-review-harness
Updated
2026-10-04T21:17:05.963756+00:00
Verdict / Summaryready-for-committer (test follow-up required)
Front matter
schema: review-report-v2
slug: commitfest-7392-review
source: postgresql-master
target: CommitFest 7392 - Let an ordering index scan hand its ORDER BY value to the target list (amorderbyvalsexact)
head: 3ff475ac12ec8d8d07cf3fd86144659285a59d83
base: 3ff475ac12ec8d8d07cf3fd86144659285a59d83
verdict: ready-for-committer (test follow-up required)
blocker_counts:
  verified: 0
  unverified: 0
coverage_status: measured
coverage_epoch: fresh
changed_executable_lines: 188
covered_changed_lines: 72
uncovered_changed_lines: 6
changed_branches: 56
covered_changed_branches: 43

CommitFest 7392 Review: amorderbyvalsexact — ORDER BY values from index scan to targetlist

Summary

This patch adds a new amorderbyvalsexact flag to IndexAmRoutine that lets an ordering index scan (GiST, SP-GiST) hand the AM's ORDER BY distance values directly to the query's target list, instead of re-evaluating the ORDER BY expression on the heap tuple. This is a performance optimization for AMs whose reported distance values are exact (not approximate), and a correctness fix for AMs (like BM25 extensions) where re-evaluating the operator would produce different values than what the index reported.

The patch modifies the planner (setrefs.c, createplan.c, plancat.c) to rewrite targetlist entries matching ORDER BY expressions into INNER_VAR references to a virtual slot filled by the executor, the executor (nodeIndexscan.c) to populate that slot with the AM's values, EXPLAIN and ruleutils.c to deparse the new Vars correctly, and adds comprehensive regression tests in gist.sql.

Verdict

ready-for-committer (test follow-up required)

No blockers, no correctness or security MAJORs. One test-only MAJOR (COV-1) for the uncovered EvalPlanQual recheck path in IndexRecheck.

Findings

COV-1

IndexRecheck EPQ path uncovered (MAJOR, tests)

Location: src/backend/executor/nodeIndexscan.c:438

Mechanism: The IndexRecheck function (the EvalPlanQual recheck callback) was modified by this patch to recompute ORDER BY values from the heap tuple via EvalOrderByExpressions and store them via IndexStoreOrderByValues when iss_OrderBySlot is non-NULL. However, no test in the regression suite exercises this path. IndexRecheck is only called during EvalPlanQual recheck, which fires when a concurrently-modified tuple is re-fetched under READ COMMITTED isolation with row locking.

Verification: verified-by-coverage. gcov shows lines 438, 446, 448, 449, 450 with zero hit count and branches at lines 438, 446 marked "never executed" after running postgresql:regress/regress (239 subtests), postgresql:btree_gist/regress, postgresql:spgist_name_ops/regress, and postgresql:cube/regress.

Repair: An isolation test spec (gist-orderby-epq.spec) was drafted and validated in a disposable scratch environment. The test creates a GiST-indexed table, runs a SELECT ... ORDER BY ... FOR UPDATE in one session and a concurrent UPDATE in another, triggering EPQ recheck. The test compiled and ran successfully via pg_isolation_regress, producing the expected concurrent behavior (the UPDATE blocks until the SELECT's transaction commits).

Validation: partial — the isolation test compiled and ran successfully, demonstrating the concurrent-update scenario. Coverage was not re-measured against the instrumented build because the isolation test used a separate temp instance.

Verification Summary

FindingSeverityClassStatusMethod
COV-1MAJORtestsverifiedverified-by-coverage

All findings verified. No refuted findings. No cannot-verify findings.

Repair Validation Summary

FindingPatch StatusValidation StatusMethods
COV-1providedpartialapply-check, diff-check, targeted-test

The isolation test spec was created, compiled, and run successfully against a temp PostgreSQL instance. The test exercises the concurrent-update scenario that triggers EvalPlanQual recheck through IndexRecheck. Full validation requires running the isolation test against the coverage-instrumented build.

Changed-Code Coverage

Measurement

Metrics

MetricCount
Changed executable lines188
Covered changed lines72
Compiler-uncovered changed lines116
Semantic uncovered lines6
Structural uncovered lines110
Changed branches56
Covered changed branches43

Uncovered Regions

  1. src/backend/executor/nodeIndexscan.c:438-450 (function: IndexRecheck)

modification under READ COMMITTED with row locking. Requires an isolation test.

  1. src/backend/utils/adt/ruleutils.c:8016 (function: resolve_special_varno)

(bogus varattno). replace_orderby_tlist_refs only emits valid varattno values; this branch is an assertion guard.

Non-instrumented Changes

pgsql-hackers Thread Reconciliation

Thread found: One thread on pgsql-hackers.

The patch author explains the motivation (BM25/full-text-search extensions where recomputing the operator gives incorrect results) and references prior discussion by Tom Lane about the subtlety of making operator values index-dependent. No review objections have been raised.

Review Analysis

Design

The patch introduces a new amorderbyvalsexact boolean on IndexAmRoutine, propagated through IndexOptInfo and IndexScan plan nodes. When set, the planner's setrefs.c replaces targetlist expressions that are equal() to ORDER BY expressions with Var(INNER_VAR, i+1) references. The executor fills a virtual slot (iss_OrderBySlot) with the AM's ORDER BY values and installs it as the projection's inner tuple.

This mirrors the existing INDEX_VAR mechanism for Index Only Scan, which is the correct design precedent. The use of INNER_VAR for a scan node with no inner plan is safe because INNER_VAR is otherwise unused for scan nodes.

Correctness

targetlist entries that are the same expression as an ORDER BY key. Only top-level expressions are matched (not subexpressions), which is conservative and correct.

only types index_store_float8_orderby_distances() can deliver.

heap tuple during EPQ, ensuring the displayed values match the operator's computation rather than stale index values.

entry's ORDER BY values before the pop frees them.

Security

No security issues identified. The patch does not introduce any new privilege-escalation paths, search-path vulnerabilities, or memory-safety concerns. The virtual slot is properly allocated and managed within the executor's tuple table.

Test Coverage

The patch includes comprehensive regression tests in gist.sql that verify:

(sequence counter stays at zero).

The only gap is the EPQ recheck path (COV-1), which requires an isolation test.

Aggregated Test Plan

  1. Existing: src/test/regress/sql/gist.sql — exercises exact, recheck,

reorder-queue, and FOR UPDATE paths (passing)

  1. Existing: src/test/regress/sql/box.sql, polygon.sql — EXPLAIN output

changes (passing)

  1. Proposed: src/test/isolation/specs/gist-orderby-epq.spec — isolation

test for EPQ recheck path (COV-1 repair, not yet applied)

Residual Risk

Low. The patch is well-structured, the design follows established patterns (INDEX_VAR for Index Only Scan), and the type restriction to float8/float4 limits the blast radius. The main risk is the uncovered EPQ path, which is addressed by the proposed isolation test.

Provenance