Review: Whole-row fixes for DROP COLUMN, SET COLUMN DATA TYPE (CF 61, Patch 6055)

code-review · generated from canonical Markdown

Type
code-review
Source
postgresql-master
Slug
commitfest-6055-review
Tags
code-reviewcommitfest-61patch-6055patch-reviewpostgresqlreview-report-v2
Updated
2026-08-14T16:53:08.915599+00:00
Verdict / Summaryneeds-review
Front matter
schema: review-report-v2
target: "CommitFest 61, Patch 6055: Whole-row fixes for DROP COLUMN, SET COLUMN DATA TYPE"
head: a82e33a83d7cd39f3abd18c7b941625ca78d4c35
base: a82e33a83d7cd39f3abd18c7b941625ca78d4c35
verdict: needs-review
blocker_counts:
  verified: 0
  unverified: 0
diff_sha: d98eecf92cd4c2bf764e33cc7d788cb453be2c4c8d4193157302ccd5d6807966
model: glm-5.2:cloud
date: 2026-08-14

Review: Whole-row fixes for DROP COLUMN, SET COLUMN DATA TYPE

CommitFest 61, Patch 6055 | Author: Jian He | Reviewer: pg-review-harness (glm-5.2:cloud) Thread: https://postgr.es/m/CACJufxGA6KVQy7DbHGLVw9s9KKmpGyZt5ME6C7kEfjDpr2wZCw@mail.gmail.com

Scope

The patch modifies ATExecDropColumn() in src/backend/commands/tablecmds.c to also drop CHECK constraints and indexes that contain whole-row Var references (attnum 0) when any column is dropped. The existing dependency machinery already drops objects that directly reference the dropped column; the patch closes a gap where objects referencing the whole row (e.g., CHECK (t IS NOT NULL), CREATE INDEX ON t ((t)) WHERE t IS NOT NULL) were orphaned with a stale rowtype after DROP COLUMN.

Changed files: src/backend/commands/tablecmds.c (+122 lines), src/test/regress/expected/constraints.out (+17), src/test/regress/expected/indexing.out (+25), src/test/regress/sql/constraints.sql (+11), src/test/regress/sql/indexing.sql (+9)

Changed function: ATExecDropColumn (lines 9425-9738)

Verdict: needs-review

No BLOCKERs. Three MAJOR findings (2 correctness, 1 test). Live thread objection from Matthias van de Meent (2026-08-05) regarding the scanning approach. The MAJ-1 else if bug must be fixed before commit. The MAJ-2 scope discrepancy should be clarified with the author. The TST-1 expected-output mismatch must be fixed for the test suite to pass.

Findings

MAJ-1

— MAJOR — Correctness — else if skips indexprs whole-row check when indpred is non-null

Location: src/backend/commands/tablecmds.c:9605

Mechanism: An index can have both indpred (partial-index predicate) AND indexprs (expression columns) non-null simultaneously (e.g., CREATE INDEX ON t ((t)) WHERE a > 0). The patch's else if at line 9605 means that when indpred is non-null, indexprs is never examined. If the whole-row reference is in the expression list but not the predicate, the index will NOT be dropped.

Failure: After ALTER TABLE t DROP COLUMN <col> where t has CREATE INDEX ON t ((t)) WHERE a > 0, the expression index ((t)) survives with a now-mismatched whole-row type, causing latent data corruption.

Verification: Confirmed by manual test. Before fix: index survives. After applying repair (else-if → if): index correctly dropped.

Repair: Change else if to independent if. Add bms_free, ReleaseSysCache, and continue after finding whole-row in indpred to avoid duplicate adds. Patch SHA-256: 938f4caf325cad65e37809e86d77081b4a8779a7833b6bed86c6984f1cbde7a2. Validation: passed (apply-check, diff-check, compile, targeted-test).

MAJ-2

— MAJOR — Correctness — Title claims SET DATA TYPE but diff only touches DROP COLUMN

Location: src/backend/commands/tablecmds.c:15395 (ATExecAlterColumnType, unmodified)

Mechanism: The patch title and v14 commit message claim to fix both DROP COLUMN and SET COLUMN DATA TYPE, but the frozen diff only modifies ATExecDropColumn. ATExecAlterColumnType is not modified. The SET DATA TYPE whole-row problem remains unfixed.

Failure: ALTER TABLE ALTER COLUMN SET DATA TYPE with a whole-row-referenced CHECK constraint silently corrupts the constraint.

Verification: Static analysis confirms no changes to ATExecAlterColumnType in the diff.

Repair: Add a whole-row check constraint error-out at the beginning of ATExecAlterColumnType. Patch SHA-256: 3aad5b13c2fd59036f97f28e977aed93d3a58bc5ac32088fc97880fe29de85a3. Validation: not-run (proposal for author).

TST-1

— MAJOR — Tests — Expected output has wrong index name

Location: src/test/regress/expected/indexing.out:776

Mechanism: The expected output file has idxpart_expr_idx but PostgreSQL generates idxpart_idxpart_idx for CREATE INDEX ON idxpart((idxpart is not null)).

Failure: The indexing regression test fails with a diff.

Verification: Confirmed by running make installcheck — test fails with expected-output mismatch.

Repair: Replace idxpart_expr_idx with idxpart_idxpart_idx. Patch SHA-256: ce04af6558a009ca59166dc4868b9d491465159aab18162f92ee6a503726f01b. Validation: passed (apply-check, diff-check, targeted-test).

MIN-1

— MINOR — Performance — Bitmapset *expr_attrs never freed

Location: src/backend/commands/tablecmds.c:9520

Already raised by Jinbinge (2026-01-19). Add bms_free(expr_attrs) in all three blocks. Patch SHA-256: 949c591769fdc7b5a1333f00eb5a021eb31f6ff4c3ad0b2fc213f4b0e3fc49e9.

MIN-2

— MINOR — Performance — Redundant per-constraint pg_constraint scan

Location: src/backend/commands/tablecmds.c:9536

Already raised by author in v5 (2025-09-23), but fix lost in v14. Restructure to single scan by conrelid. Patch SHA-256: 551d04483bddf06202878e1bbd241ba4d1c344a2b0db3cafb1e00b027cf30b49.

MIN-3

— MINOR — Performance — Redundant index lookups

Location: src/backend/commands/tablecmds.c:9571

Already raised by Kirill Reshke (2026-01-18). Replace RelationGetIndexList + SearchSysCache1 with single systable_beginscan on pg_index by indrelid. Patch SHA-256: cd3febc12233267224d72cc45416b2f61a2fe77e8d0c6991b2a11496b0da75f0.

NIT-1

— NIT — 0 vs InvalidAttrNumber inconsistency (lines 9528, 9597, 9621)

NIT-2

— NIT — Node *expr declared at function scope (line 9438)

NIT-3

— NIT — Comment grammar: "references" → "reference" (line 9570)

NIT-4

— NIT — TupleConstr *constr fetched at function top (line 9440)

All verified by static analysis. Concrete patches provided for each.

Verification Summary

FindingStatusMethodRepair Validation
MAJ-1verifiedrepropassed (apply-check, diff-check, compile, targeted-test)
MAJ-2verifiedstatic-checknot-run (proposal for author)
TST-1verifiedtargeted-testpassed (apply-check, diff-check, targeted-test)
MIN-1verifiedstatic-checknot-run (trivial fix)
MIN-2verifiedstatic-checknot-run (refactor proposal)
MIN-3verifiedstatic-checknot-run (refactor proposal)
NIT-1verifiedstatic-checknot-run (trivial fix)
NIT-2verifiedstatic-checknot-run (trivial fix)
NIT-3verifiedstatic-checknot-run (trivial fix)
NIT-4verifiedstatic-checknot-run (trivial fix)

Repair Validation Summary

Changed-Code Coverage

Execution lane: host-disposable (passed) Counter epoch: fresh (disposable build with -fprofile-arcs -ftest-coverage)

Metrics

MetricCount
Changed executable lines54
Covered changed lines52
Uncovered changed lines2
Changed branches30
Covered changed branches26
Uncovered branches4

Uncovered lines (2)

Both are defensive elog(ERROR) paths that should never execute in normal operation:

  1. Line 9555: elog(ERROR, "constraint \"%s\" of relation \"%s\" does not exist", ...) — infeasible (requires corrupted catalog state)
  2. Line 9580: elog(ERROR, "cache lookup failed for index %u", indexoid) — infeasible (requires corrupted catalog state)

Touched function

FunctionFileLinesEnteredLines CoveredBranches Covered
ATExecDropColumnsrc/backend/commands/tablecmds.c9425-9738yes74/11044/90

Non-instrumented changes

Thread Reconciliation

Thread: 621482 (17 messages, 2025-09-08 to 2026-08-05) Ledger report: commitfest-6055-review-thread-ledger

Key thread events:

Aggregated Test Plan

  1. Existing tests: constraints and indexing regress tests (indexing fails due to TST-1)
  2. MAJ-1 test: CREATE INDEX ON t ((t)) WHERE a > 0; ALTER TABLE t DROP COLUMN c; — verify index is dropped
  3. MAJ-2 test: CREATE TABLE t (... CONSTRAINT cc CHECK (t is not null)); ALTER TABLE t ALTER COLUMN c SET DATA TYPE bigint; — verify error is raised
  4. Coverage tests: CHECK constraint whole-row drop, index expression whole-row drop, index predicate whole-row drop

Provenance

Residual Risk