I believe I fixed all flaws mentioned so far (see attachment).

Also I did a new benchmark to make sure that new patch makes PostgreSQL
work faster and doesn't cause performance degradation in some cases. 

"usual pgbench" row corresponds to `pgbench -j 8 -c 8 -T 30 pgbench`
performed on a 4-core PC.

"N partitions" rows correspond to a benchmark described in a first
message of this thread performed on the same PC. N is and argument
given to gen.pl script i.e. number of partitions in generated
partitioned table.

Here are results (3 tests, TPS excluding connections establishing):

Test            |  Before   |   After   |  Delta avg   
----------------|-----------|-----------|-------------
                |    295.7  |    295.0  |             
usual pgbench   |    303.1  |    299.6  |   ~ 0%          
                |    297.7  |    302.7  |             
----------------|-----------|-----------|-------------
                |  28022.3  |  27956.1  |             
10 partitions   |  27550.1  |  28916.9  |   ~ 0%      
                |  28617.0  |  28022.9  |             
----------------|-----------|-----------|-------------
                |   3021.4  |   3184.0  |             
100 partitions  |   2949.1  |   3120.1  | 3% more TPS      
                |   2870.6  |   2825.2  |             
----------------|-----------|-----------|-------------
                |    106.7  |    158.6  |             
1000 partitions |    105.2  |    168.4  | 53% more TPS 
                |    105.9  |    162.0  |             


On Fri, 18 Dec 2015 13:39:01 -0500
Tom Lane <t...@sss.pgh.pa.us> wrote:

> Robert Haas <robertmh...@gmail.com> writes:
> > I don't know, I'm still not very comfortable with this.  And Tom
> > didn't like dictating that hash_any() must be no-fail, though I'm
> > not sure why.
> 
> What I definitely didn't like was assuming at a distance that it would
> be no-fail.  If we're to depend on that, the patch had better attach
> a comment saying so to the header comments of the function(s) it's
> assuming that about.  Otherwise, somebody could hack up hashfunc.c
> in a way that breaks the assumption, without any clue that some code
> in a very-far-away module is critically reliant on it.
> 
> > Let's wait to see what others think.
> 
> A few observations:
> 
> * This bit is too cute by half, if not three-quarters:
> 
> +     uint32          itemsizelg:2;   /* sizeof one
> item log 2 */
> +     uint32          capacity:30;    /* capacity of
> array */
> 
> Is there a good reason to assume that the only things we'll ever store
> in these arrays are of size no more than 8 bytes?  Are we so desperate
> to save space that we cannot spare two separate words for itemsize and
> capacity?  (ISTM it's a good bet that the extra code for accessing
> these bitfields occupies more space than would be saved, considering
> how few ResourceOwners typically exist at one time.)  Let's just make
> it a couple of ints and be done.  Actually, maybe nitems and capacity
> should be size_t, just in case.
> 
> * An alternative design would be to forget itemsizelg altogether and
> insist that everything stored in the resource arrays be a Datum,
> which could then be coerced to/from some form of integer or some form
> of pointer as appropriate.  That would waste some space in the int
> case, but it would considerably simplify both the ResourceArray code
> and the APIs to it, which might be worth the price of assuming we'll
> never store anything bigger than 8 bytes.  It also would make this
> look more like some existing APIs such as the on_exit callbacks.
> 
> * A lot of the code churn comes from the insistence on defining
> callbacks, which I'm dubious that we need.  We could instead have a
> function that is "get any convenient one of the array elements" and
> revise the loops in ResourceOwnerReleaseInternal to be like
> 
>       while ((item = getconvenientitem(resourcearray)))
>       {
>               drop item in exactly the same way as before
>       }
> 
> I find that preferable to the proposed ResourceArrayRemoveAll
> 
> +     while (resarr->nitems > 0)
> +     {
> +             releasecb(resarr->itemsarr, isCommit);
> +     }
> 
> which certainly looks like it's an infinite loop; it's assuming (again
> with no documentation) that the callback function will cause the array
> to get smaller somehow.  With the existing coding, it's much more
> clear why we think the loops will terminate.
> 
> * The reason that ResourceOwnerReleaseInternal was not horribly
> inefficient was that its notion of "any convenient one" of the items
> to be deleted next was in fact the one that the corresponding Forget
> function would examine first, thus avoiding an O(N^2) cost to
> re-identify the item to be dropped.  I think we should make an effort
> to be more explicit about that connection in any rewrite.  In
> particular, it looks to me like when a hash array is in use, things
> will get slower not faster because we'll be adding a hash lookup step
> to each forget operation.  Maybe we should consider adjusting the
> APIs so that that can be avoided.  Or possibly we could have internal
> state in the ResourceArrays that says "we expect this item to be
> dropped in a moment, check that before going to the trouble of a hash
> lookup".
> 
> * Actually, I'm not convinced that the proposed reimplementation of
> ResourceArrayRemove isn't horribly slow much of the time.  It sure
> looks like it could degrade to a linear search very easily.
> 
> * I still say that the assumption embodied as
> RESOURCE_ARRAY_ZERO_ELEMENT (ie that no valid entry is all-zero-bits)
> is pretty unacceptable.  It might work for pointers, but I don't like
> it for resources represented by integer indexes.
> 
>                       regards, tom lane
> 
> 

diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 9ee654e..5a00a03 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..abed36c 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,219 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
+ */
+typedef struct ResourceArray
+{
+	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
+	uint32		capacity;		/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. 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.
+ *
+ */
+#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)
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
+{
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, Datum data)
+{
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
+	resarr->itemsarr[idx] = data;
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, Datum data)
+{
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
+	{
+		if (resarr->itemsarr[idx] == data)
+		{
+			resarr->itemsarr[idx] = resarr->invalidval;
+			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		i,
+				oldcap;
+	Datum	   *olditemsarr;
+
+	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 = (Datum *)
+		MemoryContextAlloc(TopMemoryContext,
+						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
+	resarr->nitems = 0;
+
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
+	if (olditemsarr != NULL)
+	{
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Get any convenient element.
+ *
+ * Returns true on success, false on failure.
+ */
+static bool
+ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
+{
+	uint32		mask;
+
+	if (resarr->nitems == 0)
+		return false;
+
+	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
+
+	*out = resarr->itemsarr[resarr->lastidx];
+	return true;
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+	resarr->maxitems = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +269,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* 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 */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local 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 +350,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), (Datum) (-1));
+
 	return owner;
 }
 
@@ -229,6 +421,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	ResourceOwner child;
 	ResourceOwner save;
 	ResourceReleaseCallbackItem *item;
+	Datum		foundres;
 
 	/* Recurse to handle descendants */
 	for (child = owner->firstchild; child != NULL; child = child->nextchild)
@@ -252,45 +445,34 @@ 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)
+		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
 		{
+			Buffer		res = (Buffer) foundres;
+
 			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
+				PrintBufferLeakWarning(res);
+			ReleaseBuffer(res);
 		}
 
-		/*
-		 * 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)
+		/* Ditto for relcache references. */
+		while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
 		{
+			Relation	res = (Relation) foundres;
+
 			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
+				PrintRelCacheLeakWarning(res);
+			RelationClose(res);
 		}
 
-		/*
-		 * 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)
+		/* Ditto for dynamic shared memory segments */
+		while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
 		{
+			dsm_segment *res = (dsm_segment *) foundres;
+
 			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
+				PrintDSMLeakWarning(res);
+			dsm_detach(res);
 		}
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
@@ -351,47 +533,63 @@ 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)
+		while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
 		{
+			HeapTuple	res = (HeapTuple) foundres;
+
 			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
+				PrintCatCacheLeakWarning(res);
+			ReleaseCatCache(res);
 		}
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
 		{
+			CatCList   *res = (CatCList *) foundres;
+
 			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
+				PrintCatCacheListLeakWarning(res);
+			ReleaseCatCacheList(res);
 		}
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
+		while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
 		{
+			CachedPlan *res = (CachedPlan *) foundres;
+
 			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
+				PrintPlanCacheLeakWarning(res);
+			ReleaseCachedPlan(res, true);
 		}
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
+		while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
 		{
+			TupleDesc	res = (TupleDesc) foundres;
+
 			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
+				PrintTupleDescLeakWarning(res);
+			DecrTupleDescRefCount(res);
 		}
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
+		while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
 		{
+			Snapshot	res = (Snapshot) foundres;
+
 			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
+				PrintSnapshotLeakWarning(res);
+			UnregisterSnapshot(res);
 		}
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
+		while (ResourceArrayGetAny(&(owner->filearr), &foundres))
 		{
+			File		res = (File) foundres;
+
 			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
+				PrintFileLeakWarning(res);
+			FileClose(res);
 		}
 
 		/* Clean up index scans too */
@@ -418,16 +616,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 +633,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 +754,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 +770,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), (Datum) buffer);
 }
 
 /*
@@ -625,33 +784,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), (Datum) buffer);
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +808,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 +857,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 +868,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), (Datum) tuple);
 }
 
 /*
@@ -754,25 +877,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (Datum) tuple);
 
-	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);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +895,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 +906,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), (Datum) list);
 }
 
 /*
@@ -825,25 +915,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (Datum) list);
 
-	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);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +933,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 +944,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), (Datum) rel);
 }
 
 /*
@@ -896,25 +953,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (Datum) rel);
 
-	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);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +981,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 +992,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), (Datum) plan);
 }
 
 /*
@@ -977,25 +1001,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (Datum) plan);
 
-	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);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1028,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 +1039,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), (Datum) tupdesc);
 }
 
 /*
@@ -1057,25 +1048,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (Datum) tupdesc);
 
-	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);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1077,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 +1088,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), (Datum) snapshot);
 }
 
 /*
@@ -1139,25 +1097,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (Datum) snapshot);
 
-	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);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1127,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 +1138,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), (Datum) file);
 }
 
 /*
@@ -1222,25 +1147,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  (Datum) file);
 
-	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);
+	if (!res)
+		elog(ERROR, "temporary file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1177,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 +1188,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), (Datum) seg);
 }
 
 /*
@@ -1306,26 +1197,12 @@ 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;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  (Datum) seg);
 
-	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);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 9ee654e..5a00a03 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 3330c8d..abed36c 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,29 +37,60 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. 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.
+ *
  */
 #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)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -70,11 +101,24 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -86,24 +130,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
-				j,
-				lastidx;
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
 
-	lastidx = ((int) resarr->nitems) - 1;
-
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -119,27 +163,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -152,12 +202,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -170,6 +232,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -287,15 +350,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), (Datum) (-1));
 
 	return owner;
 }
-- 
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