diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index de364bcf90addf3b969f72bfaf0478a46495eb92..04bdba93292591719d28de8a69cc26cc8eff7300 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.14 1999/02/13 23:20:09 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.15 1999/05/22 23:19:37 tgl Exp $
  *
  * NOTE:
  *	This is a new (Feb. 05, 1999) implementation of the allocation set
@@ -16,7 +16,7 @@
  *	many small allocations in a few bigger blocks. AllocSetFree() does
  *	never free() memory really. It just add's the free'd area to some
  *	list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
- *	on AllocSetReset() at once, what happens when the memory context gets
+ *	at once on AllocSetReset(), which happens when the memory context gets
  *	destroyed.
  *				Jan Wieck
  *-------------------------------------------------------------------------
@@ -38,8 +38,36 @@
 #undef realloc
 
 
-#define	ALLOC_BLOCK_SIZE	8192
-#define	ALLOC_CHUNK_LIMIT	512
+/*--------------------
+ * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
+ * for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
+ * The last freelist holds all larger chunks.
+ *
+ * CAUTION: ALLOC_MINBITS must be large enough so that
+ * 1<<ALLOC_MINBITS is at least MAXALIGN,
+ * or we may fail to align the smallest chunks adequately.
+ * 16-byte alignment is enough on all currently known machines.
+ *--------------------
+ */
+
+#define ALLOC_MINBITS		4	/* smallest chunk size is 16 bytes */
+#define	ALLOC_SMALLCHUNK_LIMIT	(1 << (ALLOCSET_NUM_FREELISTS-2+ALLOC_MINBITS))
+/* Size of largest chunk that we use a fixed size for */
+
+/*--------------------
+ * The first block allocated for an allocset has size ALLOC_MIN_BLOCK_SIZE.
+ * Each time we have to allocate another block, we double the block size
+ * (if possible, and without exceeding ALLOC_MAX_BLOCK_SIZE), so as to reduce
+ * the load on "malloc".
+ *
+ * Blocks allocated to hold oversize chunks do not follow this rule, however;
+ * they are just however big they need to be.
+ *--------------------
+ */
+
+#define	ALLOC_MIN_BLOCK_SIZE	8192
+#define	ALLOC_MAX_BLOCK_SIZE	(8 * 1024 * 1024)
+
 
 #define ALLOC_BLOCKHDRSZ	MAXALIGN(sizeof(AllocBlockData))
 #define ALLOC_CHUNKHDRSZ	MAXALIGN(sizeof(AllocChunkData))
@@ -65,11 +93,14 @@ AllocSetFreeIndex(Size size)
 {
 	int idx = 0;
 
-	size = (size - 1) >> 4;
-	while (size != 0 && idx < 7)
+	if (size > 0)
 	{
-		idx++;
-		size >>= 1;
+		size = (size - 1) >> ALLOC_MINBITS;
+		while (size != 0 && idx < ALLOCSET_NUM_FREELISTS-1)
+		{
+			idx++;
+			size >>= 1;
+		}
 	}
 
 	return idx;
@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
 	AllocChunk		freeref = NULL;
 	int				fidx;
 	Size			chunk_size;
+	Size			blksize;
 
 	AssertArg(AllocSetIsValid(set));
 
@@ -191,7 +223,7 @@ AllocSetAlloc(AllocSet set, Size size)
 	}
 
 	/*
-	 * If found, remove it from the free list, make it again
+	 * If one is found, remove it from the free list, make it again
 	 * a member of the alloc set and return it's data address.
 	 *
 	 */
@@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size)
 	}
 
 	/*
-	 * If requested size exceeds smallchunk limit, allocate a separate,
-	 * entire used block for this allocation
-	 *
+	 * Choose the actual chunk size to allocate.
 	 */
-	if (size > ALLOC_CHUNK_LIMIT)
+	if (size > ALLOC_SMALLCHUNK_LIMIT)
+		chunk_size = MAXALIGN(size);
+	else
+		chunk_size = 1 << (fidx + ALLOC_MINBITS);
+	Assert(chunk_size >= size);
+
+	/*
+	 * If there is enough room in the active allocation block,
+	 * always allocate the chunk there.
+	 */
+
+	if ((block = set->blocks) != NULL)
 	{
-		Size	blksize;
+		Size		have_free = block->endptr - block->freeptr;
 
-		chunk_size = MAXALIGN(size);
+		if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ))
+			block = NULL;
+	}
+
+	/*
+	 * Otherwise, if requested size exceeds smallchunk limit,
+	 * allocate an entire separate block for this allocation
+	 *
+	 */
+	if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT)
+	{
 		blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
 		block = (AllocBlock) malloc(blksize);
 		if (block == NULL)
 			elog(FATAL, "Memory exhausted in AllocSetAlloc()");
 		block->aset = set;
-		block->freeptr = block->endptr = ((char *)block) + ALLOC_BLOCKHDRSZ;
+		block->freeptr = block->endptr = ((char *)block) + blksize;
 
 		chunk = (AllocChunk) (((char *)block) + ALLOC_BLOCKHDRSZ);
 		chunk->aset = set;
 		chunk->size = chunk_size;
 
+		/*
+		 * Try to stick the block underneath the active allocation block,
+		 * so that we don't lose the use of the space remaining therein.
+		 */
 		if (set->blocks != NULL)
 		{
 			block->next = set->blocks->next;
@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
 		return AllocChunkGetPointer(chunk);
 	}
 
-	chunk_size = 16 << fidx;
-
-	if ((block = set->blocks) != NULL)
-	{
-		Size		have_free = block->endptr - block->freeptr;
-
-		if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ))
-			block = NULL;
-	}
-
+	/*
+	 * Time to create a new regular block?
+	 */
 	if (block == NULL)
 	{
-		block = (AllocBlock) malloc(ALLOC_BLOCK_SIZE);
+		if (set->blocks == NULL)
+		{
+			blksize = ALLOC_MIN_BLOCK_SIZE;
+			block = (AllocBlock) malloc(blksize);
+		}
+		else
+		{
+			/* Get size of prior block */
+			blksize = set->blocks->endptr - ((char *) set->blocks);
+			/* Special case: if very first allocation was for a large chunk,
+			 * could have a funny-sized top block.  Do something reasonable.
+			 */
+			if (blksize < ALLOC_MIN_BLOCK_SIZE)
+				blksize = ALLOC_MIN_BLOCK_SIZE;
+			/* Crank it up, but not past max */
+			blksize <<= 1;
+			if (blksize > ALLOC_MAX_BLOCK_SIZE)
+				blksize = ALLOC_MAX_BLOCK_SIZE;
+			/* Try to allocate it */
+			block = (AllocBlock) malloc(blksize);
+			/*
+			 * We could be asking for pretty big blocks here, so cope if
+			 * malloc fails.  But give up if there's less than a meg or so
+			 * available...
+			 */
+			while (block == NULL && blksize > 1024*1024)
+			{
+				blksize >>= 1;
+				block = (AllocBlock) malloc(blksize);
+			}
+		}
+
 		if (block == NULL)
 			elog(FATAL, "Memory exhausted in AllocSetAlloc()");
 		block->aset = set;
-		block->next = set->blocks;
 		block->freeptr = ((char *)block) + ALLOC_BLOCKHDRSZ;
-		block->endptr = ((char *)block) + ALLOC_BLOCK_SIZE;
+		block->endptr = ((char *)block) + blksize;
+		block->next = set->blocks;
 
 		set->blocks = block;
 	}
 
+	/*
+	 * OK, do the allocation
+	 */
 	chunk = (AllocChunk)(block->freeptr);
 	chunk->aset = (void *)set;
 	chunk->size = chunk_size;
 	block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
+	Assert(block->freeptr <= block->endptr);
 
 	return AllocChunkGetPointer(chunk);
 }
@@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
 	 * Maybe the allocated area already is >= the new size.
 	 *
 	 */
-	if (AllocPointerGetSize(pointer) >= size)
+	oldsize = AllocPointerGetSize(pointer);
+	if (oldsize >= size)
 		return pointer;
 
 	/* allocate new pointer */
 	newPointer = AllocSetAlloc(set, size);
 
 	/* fill new memory */
-	oldsize = AllocPointerGetSize(pointer);
 	memmove(newPointer, pointer, (oldsize < size) ? oldsize : size);
 
 	/* free old pointer */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 009d086311c0e6e94b753e93eb6455007f92091b..e95aec57c9014409c7d2fbadf820cc03f55e05a6 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -15,7 +15,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: memutils.h,v 1.23 1999/03/25 19:05:19 tgl Exp $
+ * $Id: memutils.h,v 1.24 1999/05/22 23:19:36 tgl Exp $
  *
  * NOTES
  *	  some of the information in this file will be moved to
@@ -96,7 +96,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
  *		an allocation is requested for a set, memory is allocated and a
  *		pointer is returned.  Subsequently, this memory may be freed or
  *		reallocated.  In addition, an allocation set may be reset which
- *		will cause all allocated memory to be freed.
+ *		will cause all memory allocated within it to be freed.
  *
  *		Allocations may occur in four different modes.	The mode of
  *		allocation does not affect the behavior of allocations except in
@@ -109,7 +109,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
  *		and freed very frequently.	This is a good choice when allocation
  *		characteristics are unknown.  This is the default mode.
  *
- *		"Static" mode attemts to allocate space as efficiently as possible
+ *		"Static" mode attempts to allocate space as efficiently as possible
  *		without regard to freeing memory.  This mode should be chosen only
  *		when it is known that many allocations will occur but that very
  *		little of the allocated memory will be explicitly freed.
@@ -129,7 +129,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
  *		Allocation sets are not automatically reset on a system reset.
  *		Higher level code is responsible for cleaning up.
  *
- *		There may other modes in the future.
+ *		There may be other modes in the future.
  */
 
 /*
@@ -176,7 +176,9 @@ typedef AllocBlockData *AllocBlock;
  *		The prefix of each piece of memory in an AllocBlock
  */
 typedef struct AllocChunkData {
+	/* aset is the owning aset if allocated, or the freelist link if free */
 	void						*aset;
+	/* size is always the chunk size */
 	Size						size;
 } AllocChunkData;
 
@@ -189,7 +191,8 @@ typedef AllocChunkData *AllocChunk;
 typedef struct AllocSetData
 {
 	struct AllocBlockData		*blocks;
-	struct AllocChunkData		*freelist[8];
+#define ALLOCSET_NUM_FREELISTS	8
+	struct AllocChunkData		*freelist[ALLOCSET_NUM_FREELISTS];
 	/* Note: this will change in the future to support other modes */
 } AllocSetData;