From 516f799c70e3d6bdf123afc9ab7650bb0edc4de6 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 5 Dec 2025 13:42:13 -0500 Subject: [PATCH v26 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 52a74479502..c55d84f86f8 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -226,9 +226,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; @@ -237,9 +239,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++) @@ -275,11 +319,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