Hello all,

Current implementation of ResourceOwner uses arrays to store resources
like TupleDescs, Snapshots, etc. When we want to release one of these
resources ResourceOwner finds it with linear scan. Granted, resource
array are usually small so its not a problem most of the time. But it
appears to be a bottleneck when we are working with tables which have a
lot of partitions.

To reproduce this issue:
1. run `./gen.pl 10000 | psql my_database postgres`
2. run `pgbench -j 8 -c 8 -f q.sql -T 100 my_database`
3. in second terminal run `sudo perf top -u postgres`

Both gen.pl and q.sql are attached to this message.

You will see that postgres spends a lot of time in ResourceOwnerForget*
procedures:

 32.80%  postgres               [.] list_nth
 20.29%  postgres               [.] ResourceOwnerForgetRelationRef
 12.87%  postgres               [.] find_all_inheritors
  7.90%  postgres               [.] get_tabstat_entry
  6.68%  postgres               [.] ResourceOwnerForgetTupleDesc
  1.17%  postgres               [.] hash_search_with_hash_value
 ... < 1% ...

I would like to suggest a patch (see attachment) witch fixes this
bottleneck. Also I discovered that there is a lot of code duplication in
ResourceOwner. Patch fixes this too. The idea is quite simple. I just
replaced arrays with something that could be considered hash tables,
but with some specific optimizations. 

After applying this patch we can see that bottleneck is gone:

 42.89%  postgres               [.] list_nth
 18.30%  postgres               [.] find_all_inheritors
 10.97%  postgres               [.] get_tabstat_entry
  1.82%  postgres               [.] hash_search_with_hash_value
  1.21%  postgres               [.] SearchCatCache
 ... < 1% ...

For tables with thousands partitions we gain in average 32.5% more TPS.

As far as I can see in the same time patch doesn't make anything worse.
`make check` passes with asserts enabled and disabled. There is no
performance degradation according to both standard pgbench benchmark
and benchmark described above for tables with 10 and 100 partitions.

Best regards,
Aleksander

Attachment: gen.pl
Description: Perl program

Attachment: q.sql
Description: application/sql

diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..6abbfa5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -47,62 +47,296 @@
 #define MAX_RESOWNER_LOCKS 15
 
 /*
- * ResourceOwner objects look like this
+ * This number is used as initial size of resource arrays (like buffers,
+ * catrefs, etc) in ResourceOwnerData structure. If given number of items is
+ * not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize -1) as mask for hash value.
+ *
  */
-typedef struct ResourceOwnerData
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
+ * Common structure for storing different type of resources.
+ */
+typedef struct ResourceArray
+{
+	uint8*		itemsarr;		/* buffer for storing values */
+	uint32		itemsizelg:  2;	/* sizeof one item log 2 */
+	uint32		capacity:   30;	/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+} ResourceArray;
+
+/*
+ * Such type of callback function is called when resource stored in
+ * ResourceArray is released using ResourceArrayFree. Callback should
+ * _actually_ release a resource so nitems value will be decremented.
+ */
+typedef void (*ResourceArrayRemoveCallback)(const uint8* ref, bool isCommit);
+
+/* Used as argument to memcmp to determine if ResourceArray[i] is free. */
+static const uint8 RESOURCE_ARRAY_ZERO_ELEMENT[sizeof(void*)] = {0};
+
+/*
+ * Calculate hash_any of given data. For uint32 values use faster hash_uint32.
+ */
+static Datum
+ResourceArrayHash(const uint8* data, int size)
+{
+	uint32		tmp;
+
+	Assert(size == sizeof(uint32) || size == sizeof(void*));
+
+	if(size == sizeof(uint32))
+	{
+		tmp = *((const uint32*)data);
+		return hash_uint32(tmp);
+	}
+	else
+	{
+		return hash_any(data, size);
+	}
+}
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray *resarr, uint32 itemsize) {
+	Assert(itemsize == sizeof(int) || itemsize == sizeof(void*));
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+
+	resarr->itemsizelg = 0;
+	while(itemsize > 1)
+	{
+		resarr->itemsizelg++;
+		itemsize >>= 1;
+	}
+
+	Assert(resarr->itemsizelg == 2 || resarr->itemsizelg == 3);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray *resarr, const uint8 *dataref)
+{
+	uint8 		*itemptr;
+	Datum 		idx;
+	Datum 		mask = resarr->capacity - 1;
+	uint32 		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->maxitems > resarr->nitems);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we just use a linear scan.
+	 */
+	if(resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = resarr->nitems;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	while(true)
+	{
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
+	memcpy(itemptr, dataref, itemsize);
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray *resarr, const uint8 *dataref)
+{
+	uint8 		*itemptr;
+	Datum		idx;
+	uint32		i;
+	Datum		mask = resarr->capacity - 1;
+	uint32 		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we use a linear scan.
+	 */
+	if(resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = 0;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	for(i = 0; i < resarr->capacity; i++)
+	{
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, dataref, itemsize) == 0)
+		{
+			memset(itemptr, 0, itemsize);
+			resarr->nitems--;
+			return true;
+		}
+		idx = (idx + 1) & mask;
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray* resarr)
+{
+	uint32		oldcap;
+	uint8*		olditemsarr;
+	uint8*		olditemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->itemsizelg != 0);
+
+	if(resarr->nitems < resarr->maxitems)
+		return;	/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (uint8*)
+		MemoryContextAllocZero(TopMemoryContext,
+							   resarr->capacity * itemsize);
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
+	resarr->nitems = 0;
+
+	if(olditemsarr != NULL)
+	{
+		while(oldcap > 0)
+		{
+			oldcap--;
+			/*
+			 * Read next line as:
+			 *
+			 *   olditemptr = &(olditemsarr[oldcap]).
+			 *
+			 * We use binary shift since compiler don't know that itemsize
+			 * is always power of two. It would use multiplication instead of
+			 * efficient binary shift in code `oldcap * itemsize`.
+			 */
+			olditemptr = olditemsarr + (oldcap << resarr->itemsizelg);
+			if(memcmp(olditemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0)
+				ResourceArrayAdd(resarr, olditemptr);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Remove all resources in array and call a callback function for each release.
+ */
+static void
+ResourceArrayRemoveAll(ResourceArray *resarr,
+					   ResourceArrayRemoveCallback releasecb,
+					   bool isCommit)
+{
+	uint32		idx = 0;
+	uint8*		itemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	while(resarr->nitems > 0)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 *   itemptr = &(itemsarr[idx]).
+		 *
+		 * We use binary shift since compiler don't know that itemsize
+		 * is always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `oldcap * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+		{
+			idx++;
+			continue;
+		}
+		releasecb(itemptr, isCommit);
+	}
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray *resarr)
 {
-	ResourceOwner parent;		/* NULL if no parent (toplevel owner) */
-	ResourceOwner firstchild;	/* head of linked list of children */
-	ResourceOwner nextchild;	/* next child of same parent */
-	const char *name;			/* name (just for debugging) */
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
+	if(!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/* ResourceOwner objects look like this */
+typedef struct ResourceOwnerData
+{
+	ResourceOwner parent;			/* NULL if no parent (toplevel owner) */
+	ResourceOwner firstchild;		/* head of linked list of children */
+	ResourceOwner nextchild;		/* next child of same parent */
+	const char *name;				/* name (just for debugging) */
 
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
-	int			nlocks;			/* number of owned locks */
+	int			nlocks;				/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;		/* `HeapTuple`s */
+	ResourceArray catlistrefarr; 	/* `ResourceOwner`s */
+	ResourceArray relrefarr;		/* `Relation`s */
+	ResourceArray planrefarr;		/* `CachedPlan*`s */
+	ResourceArray tupdescarr;		/* `TupleDesc`s */
+	ResourceArray snapshotarr;		/* `Snapshot`s */
+	ResourceArray dsmarr;			/* `dsm_segment*`s */
+	ResourceArray bufferarr; 		/* `Buffer`s  */
+	ResourceArray filearr; 			/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +402,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), sizeof(HeapTuple));
+	ResourceArrayInit(&(owner->catlistrefarr), sizeof(ResourceOwner));
+	ResourceArrayInit(&(owner->relrefarr), sizeof(Relation));
+	ResourceArrayInit(&(owner->planrefarr), sizeof(CachedPlan*));
+	ResourceArrayInit(&(owner->tupdescarr), sizeof(TupleDesc));
+	ResourceArrayInit(&(owner->snapshotarr), sizeof(Snapshot));
+	ResourceArrayInit(&(owner->dsmarr), sizeof(dsm_segment*));
+	ResourceArrayInit(&(owner->bufferarr), sizeof(Buffer));
+	ResourceArrayInit(&(owner->filearr), sizeof(File));
+
 	return owner;
 }
 
@@ -221,6 +465,87 @@ ResourceOwnerRelease(ResourceOwner owner,
 }
 
 static void
+ReleaseCachedPlanCallback(const uint8* dataref, bool isCommit)
+{
+	CachedPlan* res = *((CachedPlan**)dataref);
+	if (isCommit)
+		PrintPlanCacheLeakWarning(res);
+	ReleaseCachedPlan(res, true);
+}
+
+static void
+ReleaseDSMCallback(const uint8* dataref, bool isCommit)
+{
+	dsm_segment* res = *((dsm_segment**)dataref);
+	if (isCommit)
+		PrintDSMLeakWarning(res);
+	dsm_detach(res);
+}
+
+static void
+ReleaseTupleDescCallback(const uint8* dataref, bool isCommit)
+{
+	TupleDesc 	res = *((TupleDesc*)dataref);
+	if(isCommit)
+		PrintTupleDescLeakWarning(res);
+	DecrTupleDescRefCount(res);
+}
+
+static void
+ReleaseSnapshotCallback(const uint8* dataref, bool isCommit)
+{
+	Snapshot 	res = *((Snapshot*)dataref);
+	if (isCommit)
+		PrintSnapshotLeakWarning(res);
+	UnregisterSnapshot(res);
+}
+
+static void
+ReleaseRelationCallback(const uint8* dataref, bool isCommit)
+{
+	Relation 	res = *((Relation*)dataref);
+	if (isCommit)
+		PrintRelCacheLeakWarning(res);
+	RelationClose(res);
+}
+
+static void
+ReleaseCatCacheListCallback(const uint8* dataref, bool isCommit)
+{
+	CatCList* 	res = *((CatCList**)dataref);
+	if (isCommit)
+		PrintCatCacheListLeakWarning(res);
+	ReleaseCatCacheList(res);
+}
+
+static void
+ReleaseCatCacheCallback(const uint8* dataref, bool isCommit)
+{
+	HeapTuple 	res = *((HeapTuple*)dataref);
+	if (isCommit)
+		PrintCatCacheLeakWarning(res);
+	ReleaseCatCache(res);
+}
+
+static void
+ReleaseBufferCallback(const uint8* dataref, bool isCommit)
+{
+	Buffer 		res = *((Buffer*)dataref);
+	if (isCommit)
+		PrintBufferLeakWarning(res);
+	ReleaseBuffer(res);
+}
+
+static void
+ReleaseFileCallback(const uint8* dataref, bool isCommit)
+{
+	File 		res = *((File*)dataref);
+	if (isCommit)
+		PrintFileLeakWarning(res);
+	FileClose(res);
+}
+
+static void
 ResourceOwnerReleaseInternal(ResourceOwner owner,
 							 ResourceReleasePhase phase,
 							 bool isCommit,
@@ -252,46 +577,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
-		{
-			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->bufferarr),
+							   ReleaseBufferCallback, isCommit);
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
-		{
-			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
-		}
+		/* Ditto for relcache references. */
+		ResourceArrayRemoveAll(&(owner->relrefarr),
+							   ReleaseRelationCallback, isCommit);
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
-		{
-			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
-		}
+		/* Ditto for dynamic shared memory segments */
+		ResourceArrayRemoveAll(&(owner->dsmarr),
+							   ReleaseDSMCallback, isCommit);
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -351,48 +647,28 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catrefarr),
+							   ReleaseCatCacheCallback, isCommit);
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catlistrefarr),
+							   ReleaseCatCacheListCallback, isCommit);
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
-		{
-			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
-		}
+		ResourceArrayRemoveAll(&(owner->planrefarr),
+							   ReleaseCachedPlanCallback, isCommit);
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
-		{
-			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->tupdescarr),
+							   ReleaseTupleDescCallback, isCommit);
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
-		{
-			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->snapshotarr),
+							   ReleaseSnapshotCallback, isCommit);
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
-		{
-			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->filearr),
+							   ReleaseFileCallback, isCommit);
 
 		/* Clean up index scans too */
 		ReleaseResources_hash();
@@ -418,16 +694,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +711,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +832,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +848,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (const uint8*)&buffer);
 }
 
 /*
@@ -625,33 +862,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (const uint8*)&buffer);
+	if(!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +886,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +935,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +946,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (const uint8*)&tuple);
 }
 
 /*
@@ -754,25 +955,11 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
-
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (const uint8*)&tuple);
+	if(!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +972,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +983,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (const uint8*)&list);
 }
 
 /*
@@ -825,25 +992,11 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
-
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (const uint8*)&list);
+	if(!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +1009,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +1020,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (const uint8*)&rel);
 }
 
 /*
@@ -896,25 +1029,11 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
-
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (const uint8*)&rel);
+	if(!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +1056,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +1067,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (const uint8*)&plan);
 }
 
 /*
@@ -977,25 +1076,11 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
-
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (const uint8*)&plan);
+	if(!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1102,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +1113,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (const uint8*)&tupdesc);
 }
 
 /*
@@ -1057,25 +1122,11 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
-
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (const uint8*)&tupdesc);
+	if(!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1150,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1161,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (const uint8*)&snapshot);
 }
 
 /*
@@ -1139,25 +1170,11 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (const uint8*)&snapshot);
+	if(!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1199,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1210,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (const uint8*)&file);
 }
 
 /*
@@ -1222,25 +1219,10 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	bool res = ResourceArrayRemove(&(owner->filearr), (const uint8*)&file);
+	if(!res)
+		elog(ERROR, "temporery file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1247,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1258,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (const uint8*)&seg);
 }
 
 /*
@@ -1306,26 +1267,10 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	bool res = ResourceArrayRemove(&(owner->dsmarr), (const uint8*)&seg);
+	if(!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to