From b8e1dc6e625102c9f25c2019e4fc900d1125540f Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 5 Dec 2025 13:42:13 -0500 Subject: [PATCH v28 4/4] Identify if partial indexes are impacted by an update. The executor now determines which, if any, attributes that are indexed are both modified and force new index tuples to be inserted ahead of calling into the table AM update function. Prior to this commit the test for partial indexes happened after table update, this changes that to before so that in cases where the before and after tuples both lie outside the predicate the attributes for the predicate are not included in the "modified indexed attributes" bitmapset. --- src/backend/executor/nodeModifyTable.c | 53 ++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index ab0a343bcf6..fee3d75fa36 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -227,9 +227,11 @@ ExecCheckIndexedAttrsForChanges(ResultRelInfo *relinfo, Bitmapset *m_attrs = NULL; /* (possibly) modified indexed attrs */ Bitmapset *p_attrs = NULL; /* (possibly) modified predicate attrs */ Bitmapset *u_attrs = NULL; /* unmodified indexed attrs */ + Bitmapset *pre_attrs = indexInfo->ii_PredicateAttrs; bool has_am_compare = (amroutine->amcomparedatums != NULL); bool supports_ios = (amroutine->amcanreturn != NULL); bool is_partial = (indexInfo->ii_Predicate != NIL); + TupleTableSlot *save_scantuple; ExprContext *econtext = GetPerTupleExprContext(estate); int num_datums = supports_ios ? indexInfo->ii_NumIndexAttrs : indexInfo->ii_NumIndexKeyAttrs; @@ -238,9 +240,51 @@ ExecCheckIndexedAttrsForChanges(ResultRelInfo *relinfo, if (bms_is_subset(indexInfo->ii_IndexedAttrs, mix_attrs)) continue; - /* Add partial index attributes */ - if (is_partial) - p_attrs = bms_add_members(p_attrs, indexInfo->ii_PredicateAttrs); + /* Checking partial at this point isn't viable when we're serializable */ + if (is_partial && IsolationIsSerializable()) + { + p_attrs = bms_add_members(p_attrs, pre_attrs); + } + /* Check partial index predicate */ + else if (is_partial) + { + ExprState *pstate; + bool old_qualifies, + new_qualifies; + + + if (!indexInfo->ii_CheckedPredicate) + pstate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + else + pstate = indexInfo->ii_PredicateState; + + save_scantuple = econtext->ecxt_scantuple; + + econtext->ecxt_scantuple = old_tts; + old_qualifies = ExecQual(pstate, econtext); + + econtext->ecxt_scantuple = new_tts; + new_qualifies = ExecQual(pstate, econtext); + + econtext->ecxt_scantuple = save_scantuple; + + indexInfo->ii_CheckedPredicate = true; + indexInfo->ii_PredicateState = pstate; + indexInfo->ii_PredicateSatisfied = new_qualifies; + + /* Both outside predicate, index doesn't need update */ + if (!old_qualifies && !new_qualifies) + continue; + + /* A transition means we need to update the index */ + if (old_qualifies != new_qualifies) + p_attrs = bms_copy(pre_attrs); + + /* + * When both are within the predicate we must update this index, + * but only if one of the index key attributes changed. + */ + } /* Compare the index datums for equality */ for (int j = 0; j < num_datums; j++) @@ -276,11 +320,12 @@ ExecCheckIndexedAttrsForChanges(ResultRelInfo *relinfo, */ else if (rel_attrnum == 0) { - TupleTableSlot *save_scantuple = econtext->ecxt_scantuple; Oid expr_type_oid; Expr *expr = (Expr *) list_nth(indexInfo->ii_Expressions, nth_expr); ExprState *state; + save_scantuple = econtext->ecxt_scantuple; + if (indexInfo->ii_ExpressionsState == NIL) { /* First time through, set up expression evaluation state */ -- 2.51.2