TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
			   Trigger *trigger, TriggerEvent event,
			   Bitmapset *modifiedCols,
			   HeapTuple oldtup, HeapTuple newtup)
{
	/* Check replication-role-dependent enable state */
	if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
	{
		if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
			trigger->tgenabled == TRIGGER_DISABLED)
			return false;
	}
	else	/* ORIGIN or LOCAL role */
	{
		if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
			trigger->tgenabled == TRIGGER_DISABLED)
			return false;
	}

	/*
	 * Check for column-specific trigger (only possible for UPDATE, and in
	 * fact we *must* ignore tgattr for other event types)
	 */
	if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event))
	{
		int			i;
		bool		modified;

		modified = false;
		for (i = 0; i < trigger->tgnattr; i++)
		{
			if (bms_is_member(trigger->tgattr[i] - FirstLowInvalidHeapAttributeNumber,
							  modifiedCols))
			{
				modified = true;
				break;
			}
		}
		if (!modified)
			return false;
	}

	/* Check for WHEN clause */
	if (trigger->tgqual)
	{
		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
		List	  **predicate;
		ExprContext *econtext;
		TupleTableSlot *oldslot = NULL;
		TupleTableSlot *newslot = NULL;
		MemoryContext oldContext;
		int			i;

		Assert(estate != NULL);

		/*
		 * trigger is an element of relinfo->ri_TrigDesc->triggers[]; find the
		 * matching element of relinfo->ri_TrigWhenExprs[]
		 */
		i = trigger - relinfo->ri_TrigDesc->triggers;
		predicate = &relinfo->ri_TrigWhenExprs[i];

		/*
		 * If first time through for this WHEN expression, build expression
		 * nodetrees for it.  Keep them in the per-query memory context so
		 * they'll survive throughout the query.
		 */
		if (*predicate == NIL)
		{
			Node	   *tgqual;

			oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
			tgqual = stringToNode(trigger->tgqual);
			/* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
			ChangeVarNodes(tgqual, PRS2_OLD_VARNO, INNER_VAR, 0);
			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
			/* ExecQual wants implicit-AND form */
			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
			MemoryContextSwitchTo(oldContext);
		}

		/*
		 * We will use the EState's per-tuple context for evaluating WHEN
		 * expressions (creating it if it's not already there).
		 */
		econtext = GetPerTupleExprContext(estate);

		/*
		 * Put OLD and NEW tuples into tupleslots for expression evaluation.
		 * These slots can be shared across the whole estate, but be careful
		 * that they have the current resultrel's tupdesc.
		 */
		if (HeapTupleIsValid(oldtup))
		{
			if (estate->es_trig_oldtup_slot == NULL)
			{
				oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
				estate->es_trig_oldtup_slot = ExecInitExtraTupleSlot(estate);
				MemoryContextSwitchTo(oldContext);
			}
			oldslot = estate->es_trig_oldtup_slot;
			if (oldslot->tts_tupleDescriptor != tupdesc)
				ExecSetSlotDescriptor(oldslot, tupdesc);
			ExecStoreTuple(oldtup, oldslot, InvalidBuffer, false);
		}
		if (HeapTupleIsValid(newtup))
		{
			if (estate->es_trig_newtup_slot == NULL)
			{
				oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
				estate->es_trig_newtup_slot = ExecInitExtraTupleSlot(estate);
				MemoryContextSwitchTo(oldContext);
			}
			newslot = estate->es_trig_newtup_slot;
			if (newslot->tts_tupleDescriptor != tupdesc)
				ExecSetSlotDescriptor(newslot, tupdesc);
			ExecStoreTuple(newtup, newslot, InvalidBuffer, false);
		}

		/*
		 * Finally evaluate the expression, making the old and/or new tuples
		 * available as INNER_VAR/OUTER_VAR respectively.
		 */
		econtext->ecxt_innertuple = oldslot;
		econtext->ecxt_outertuple = newslot;
		if (!ExecQual(*predicate, econtext, false))
			return false;
	}

	return true;
}