From a70b0cadc1142e92b2354a0ca3cd47aaeb0c148e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 4 Feb 2020 12:25:05 -0800
Subject: [PATCH v2 2/3] Using a Bitmapset of tags rather than a string array.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

EventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.  This
makes the code a little simpler and easier to read, in my opinion.  In
filter_event_trigger, rather than running bsearch through a sorted array
of strings, it just runs bms_is_member.
---
 src/backend/commands/event_trigger.c | 12 +++++-------
 src/backend/utils/cache/evtcache.c   | 27 +++++++++++++--------------
 src/include/commands/event_trigger.h |  2 +-
 src/include/utils/evtcache.h         |  3 +--
 src/pl/plpgsql/src/pl_exec.c         |  2 +-
 5 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 6cd9437367..96f3a06ff3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -820,7 +820,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
  * tags matching.
  */
 static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
 {
 	/*
 	 * Filter by session replication role, knowing that we never see disabled
@@ -838,9 +838,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
 	}
 
 	/* Filter by tags, if any were specified. */
-	if (item->ntags != 0 && bsearch(tag, item->tag,
-									item->ntags, sizeof(char *),
-									pg_qsort_strcmp) == NULL)
+	if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
 		return false;
 
 	/* if we reach that point, we're not filtering out this item */
@@ -857,7 +855,7 @@ EventTriggerCommonSetup(Node *parsetree,
 						EventTriggerEvent event, const char *eventstr,
 						EventTriggerData *trigdata)
 {
-	const char *tag;
+	CommandTag	tag;
 	List	   *cachelist;
 	ListCell   *lc;
 	List	   *runlist = NIL;
@@ -902,7 +900,7 @@ EventTriggerCommonSetup(Node *parsetree,
 		return NIL;
 
 	/* Get the command tag. */
-	tag = GetCommandTagName(CreateCommandTag(parsetree));
+	tag = CreateCommandTag(parsetree);
 
 	/*
 	 * Filter list of event triggers by command tag, and copy them into our
@@ -915,7 +913,7 @@ EventTriggerCommonSetup(Node *parsetree,
 	{
 		EventTriggerCacheItem *item = lfirst(lc);
 
-		if (filter_event_trigger(&tag, item))
+		if (filter_event_trigger(tag, item))
 		{
 			/* We must plan to fire this trigger. */
 			runlist = lappend_oid(runlist, item->fnoid);
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..a960d7ca80 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -51,7 +51,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
 static void BuildEventTriggerCache(void);
 static void InvalidateEventCacheCallback(Datum arg,
 										 int cacheid, uint32 hashvalue);
-static int	DecodeTextArrayToCString(Datum array, char ***cstringp);
+static void	DecodeTextArrayToBitmapset(Datum array, Bitmapset **bms);
 
 /*
  * Search the event cache by trigger event.
@@ -180,10 +180,7 @@ BuildEventTriggerCache(void)
 		evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
 							   RelationGetDescr(rel), &evttags_isnull);
 		if (!evttags_isnull)
-		{
-			item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
-			qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
-		}
+			DecodeTextArrayToBitmapset(evttags, &item->tagset);
 
 		/* Add to cache entry. */
 		entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +212,18 @@ BuildEventTriggerCache(void)
 }
 
 /*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
  *
  * We could avoid a bit of overhead here if we were willing to duplicate some
  * of the logic from deconstruct_array, but it doesn't seem worth the code
  * complexity.
  */
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static void
+DecodeTextArrayToBitmapset(Datum array, Bitmapset **tagset)
 {
 	ArrayType  *arr = DatumGetArrayTypeP(array);
 	Datum	   *elems;
-	char	  **cstring;
+	Bitmapset  *bms;
 	int			i;
 	int			nelems;
 
@@ -234,13 +231,15 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
 		elog(ERROR, "expected 1-D text array");
 	deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
 
-	cstring = palloc(nelems * sizeof(char *));
-	for (i = 0; i < nelems; ++i)
-		cstring[i] = TextDatumGetCString(elems[i]);
+	for (bms = NULL, i = 0; i < nelems; ++i)
+	{
+		char *str = TextDatumGetCString(elems[i]);
+		bms = bms_add_member(bms, GetCommandTagEnum(str));
+		pfree(str);
+	}
 
 	pfree(elems);
-	*cstringp = cstring;
-	return nelems;
+	*tagset = bms;
 }
 
 /*
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..5123c4b850 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -25,7 +25,7 @@ typedef struct EventTriggerData
 	NodeTag		type;
 	const char *event;			/* event name */
 	Node	   *parsetree;		/* parse tree */
-	const char *tag;			/* command tag */
+	CommandTag	tag;
 } EventTriggerData;
 
 #define AT_REWRITE_ALTER_PERSISTENCE	0x01
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
 {
 	Oid			fnoid;			/* function to be called */
 	char		enabled;		/* as SESSION_REPLICATION_ROLE_* */
-	int			ntags;			/* number of command tags */
-	char	  **tag;			/* command tags in SORTED order */
+	Bitmapset  *tagset;			/* command tags, or NULL if empty */
 } EventTriggerCacheItem;
 
 extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 8a0f31b4b8..8ac2b67417 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1473,7 +1473,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
 		case PLPGSQL_PROMISE_TG_TAG:
 			if (estate->evtrigdata == NULL)
 				elog(ERROR, "event trigger promise is not in an event trigger function");
-			assign_text_var(estate, var, estate->evtrigdata->tag);
+			assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
 			break;
 
 		default:
-- 
2.21.1 (Apple Git-122.3)

