dict_preload.diff

application/octet-stream

Filename: dict_preload.diff
Type: application/octet-stream
Part: 0
Message: patch: preload dictionary - with mmap

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
contrib/dict_preload/dict_preload.c 183 0
contrib/dict_preload/dict_preload.sql.in 19 0
contrib/dict_preload/mmap_alloc.c 1300 0
contrib/dict_preload/uninstall_dict_preload.sql 10 0
src/backend/tsearch/spell.c 75 11
*** ./contrib/dict_preload/dict_preload.c.orig	2010-03-18 17:00:33.000000000 +0100
--- ./contrib/dict_preload/dict_preload.c	2010-03-31 19:45:51.123855008 +0200
***************
*** 0 ****
--- 1,183 ----
+ /*-------------------------------------------------------------------------
+  *
+  * dict_preload.c
+  *	  preloaded dictionary
+  *
+  * Copyright (c) 2007-2010, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  $PostgreSQL: pgsql/contrib/dict_preload/dict_preload.c,v 1.6 2010/01/02 16:57:32 momjian Exp $
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "commands/defrem.h"
+ #include "fmgr.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "nodes/value.h"
+ #include "tsearch/ts_public.h"
+ #include "tsearch/ts_utils.h"
+ #include "tsearch/dicts/spell.h"
+ #include "utils/guc.h"
+ #include "utils/memutils.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ char *preload_dictfile = NULL;
+ char *preload_afffile = NULL;
+ char *preload_stopwords = NULL;
+ 
+ typedef struct
+ {
+ 	StopList	stoplist;
+ 	IspellDict	obj;
+ } DictISpell;
+ 
+ MemoryContext		preload_ctx = NULL;
+ 
+ DictISpell		*preload_dict = NULL;
+ 
+ extern MemoryContext MMapAllocSetContextCreate(MemoryContext parent,
+ 							  const char *name,
+ 							  Size minContextSize,
+ 							  Size initBlockSize,
+ 							  Size maxBlockSize);
+ 
+ PG_FUNCTION_INFO_V1(dpreloaddict_init);
+ Datum		dpreloaddict_init(PG_FUNCTION_ARGS);
+ 
+ PG_FUNCTION_INFO_V1(dpreloaddict_lexize);
+ Datum		dpreloaddict_lexize(PG_FUNCTION_ARGS);
+ 
+ void _PG_init(void);
+ void _PG_fini(void);
+ 
+ static DictISpell *
+ load_dictionary(void)
+ {
+ 	List	*dictopt = NIL;
+ 	FunctionCallInfoData fcinfo;
+ 	DictISpell *result;
+ 
+ 	/*
+ 	 * read parameters for preloaded dictionary
+ 	 */
+ 	if (preload_dictfile != NULL)
+ 		dictopt = lappend(dictopt, makeDefElem("DictFile", 
+ 								    (Node *) makeString(preload_dictfile)));
+ 	if (preload_afffile != NULL)
+ 		dictopt = lappend(dictopt, makeDefElem("AffFile", 
+ 								    (Node *) makeString(preload_afffile)));
+ 	if (preload_stopwords != NULL)
+ 		dictopt = lappend(dictopt, makeDefElem("StopWords", 
+ 								    (Node *) makeString(preload_stopwords)));
+ 	/*
+ 	 * Initialise ispell dictionary
+ 	 */
+ 	InitFunctionCallInfoData(fcinfo, NULL, 1, NULL, NULL);
+ 	fcinfo.arg[0] = PointerGetDatum(dictopt);
+ 	fcinfo.argnull[0] = false;
+ 
+ 	result = (DictISpell *) DatumGetPointer(dispell_init(&fcinfo));
+ 
+ 	/* 
+ 	 * MemoryContextStats(CurrentMemoryContext);
+ 	 */
+ 
+ 	return result;
+ }
+ 
+ Datum
+ dpreloaddict_init(PG_FUNCTION_ARGS)
+ {
+ 	static bool firsttime = true;
+ 	
+ 	/*
+ 	 * dpreloaddict_init can be called more times:
+ 	 *  CREATE TEXT SEARCH DICTIONARY
+ 	 *  DROP TEXT SEARCH DICTIONARY
+ 	 *  CREATE TEXT SEARCH DICTIONARY
+ 	 *  ...
+ 	 */
+ 	if (firsttime)
+ 	{
+ 		/* In this moment, dictionary have to be loaded */
+ 		Assert(MemoryContextIsValid(preload_ctx));
+ 		Assert(preload_dict != NULL);
+ 	
+     		preload_ctx = NULL;
+ 		firsttime = false;
+ 		
+ 		return PointerGetDatum(preload_dict);
+ 	}
+ 	else
+ 		return PointerGetDatum(load_dictionary());
+ }
+ 
+ Datum
+ dpreloaddict_lexize(PG_FUNCTION_ARGS)
+ {
+ 	return dispell_lexize(fcinfo);
+ }
+ 
+ /*
+  * Module load callback
+  */
+ void
+ _PG_init()
+ {
+ 	MemoryContext	oldctx;
+ 	static bool inited = false;
+ 	GucContext	guc_ctx;
+ 
+ 	if (inited)
+ 		return;
+ 	else
+ 		inited = true;
+ 
+ 	guc_ctx = process_shared_preload_libraries_in_progress ? 
+ 								PGC_POSTMASTER : PGC_SUSET;
+ 
+ 	/* Define custom GUC variables. */
+ 	DefineCustomStringVariable("dict_preload.dictfile",
+ 		 "name of file of preloaded ispell dictionary",
+ 						 NULL,
+ 						&preload_dictfile,
+ 						NULL,
+ 						guc_ctx, 0,
+ 						NULL, NULL);
+ 
+ 	/* Define custom GUC variables. */
+ 	DefineCustomStringVariable("dict_preload.afffile",
+ 		 "name of file of preloaded ispell affix",
+ 						 NULL,
+ 						&preload_afffile,
+ 						NULL,
+ 						guc_ctx, 0,
+ 						NULL, NULL);
+ 
+ 	/* Define custom GUC variables. */
+ 	DefineCustomStringVariable("dict_preload.stopwords",
+ 		 "name of file of preloaded ispell stopwords",
+ 						 NULL,
+ 						&preload_stopwords,
+ 						NULL,
+ 						guc_ctx, 0,
+ 						NULL, NULL);
+ 
+ 	/* preload dictionary */
+ 	Assert(preload_ctx == NULL);
+ 	Assert(preload_dict == NULL);
+ 
+ 	preload_ctx = MMapAllocSetContextCreate(NULL, "Ispell dictionary preload context",
+ 											512 * 1024,
+ 											1024 * 1024 *4,
+ 											1024 * 1024 *8);
+ 	oldctx = MemoryContextSwitchTo(preload_ctx);
+ 
+ 	preload_dict = load_dictionary();
+ 
+ 	MemoryContextSwitchTo(oldctx);
+ }
*** ./contrib/dict_preload/dict_preload.sql.in.orig	2010-03-18 17:00:52.000000000 +0100
--- ./contrib/dict_preload/dict_preload.sql.in	2010-03-19 08:24:33.000000000 +0100
***************
*** 0 ****
--- 1,19 ----
+ /* $PostgreSQL: pgsql/contrib/dict_int/dict_int.sql.in,v 1.3 2007/11/13 04:24:27 momjian Exp $ */
+ 
+ -- Adjust this setting to control where the objects get created.
+ SET search_path = public;
+ 
+ CREATE OR REPLACE FUNCTION dpreloaddict_init(internal)
+         RETURNS internal
+         AS 'MODULE_PATHNAME'
+         LANGUAGE C STRICT;
+ 
+ CREATE OR REPLACE FUNCTION dpreloaddict_lexize(internal, internal, internal, internal)
+         RETURNS internal
+         AS 'MODULE_PATHNAME'
+         LANGUAGE C STRICT;
+ 
+ CREATE TEXT SEARCH TEMPLATE preloaddict(
+         LEXIZE = dpreloaddict_lexize,
+         INIT   = dpreloaddict_init
+ );
*** ./contrib/dict_preload/mmap_alloc.c.orig	2010-03-30 16:52:16.034361127 +0200
--- ./contrib/dict_preload/mmap_alloc.c	2010-03-31 19:44:56.428854686 +0200
***************
*** 0 ****
--- 1,1300 ----
+ /*-------------------------------------------------------------------------
+  *
+  * aset.c
+  *	  Allocation set definitions.
+  *
+  * AllocSet is our standard implementation of the abstract MemoryContext
+  * type.
+  *
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.83 2010/02/26 02:01:14 momjian Exp $
+  *
+  * NOTE:
+  *	This is a new (Feb. 05, 1999) implementation of the allocation set
+  *	routines. AllocSet...() does not use OrderedSet...() any more.
+  *	Instead it manages allocations in a block pool by itself, combining
+  *	many small allocations in a few bigger blocks. AllocSetFree() normally
+  *	doesn't 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
+  *	at once on AllocSetReset(), which happens when the memory context gets
+  *	destroyed.
+  *				Jan Wieck
+  *
+  *	Performance improvement from Tom Lane, 8/99: for extremely large request
+  *	sizes, we do want to be able to give the memory back to free() as soon
+  *	as it is pfree()'d.  Otherwise we risk tying up a lot of memory in
+  *	freelist entries that might never be usable.  This is specially needed
+  *	when the caller is repeatedly repalloc()'ing a block bigger and bigger;
+  *	the previous instances of the block were guaranteed to be wasted until
+  *	AllocSetReset() under the old way.
+  *
+  *	Further improvement 12/00: as the code stood, request sizes in the
+  *	midrange between "small" and "large" were handled very inefficiently,
+  *	because any sufficiently large free chunk would be used to satisfy a
+  *	request, even if it was much larger than necessary.  This led to more
+  *	and more wasted space in allocated chunks over time.  To fix, get rid
+  *	of the midrange behavior: we now handle only "small" power-of-2-size
+  *	chunks as chunks.  Anything "large" is passed off to malloc().	Change
+  *	the number of freelists to change the small/large boundary.
+  *
+  *
+  *	About CLOBBER_FREED_MEMORY:
+  *
+  *	If this symbol is defined, all freed memory is overwritten with 0x7F's.
+  *	This is useful for catching places that reference already-freed memory.
+  *
+  *	About MEMORY_CONTEXT_CHECKING:
+  *
+  *	Since we usually round request sizes up to the next power of 2, there
+  *	is often some unused space immediately after a requested data area.
+  *	Thus, if someone makes the common error of writing past what they've
+  *	requested, the problem is likely to go unnoticed ... until the day when
+  *	there *isn't* any wasted space, perhaps because of different memory
+  *	alignment on a new platform, or some other effect.	To catch this sort
+  *	of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
+  *	the requested space whenever the request is less than the actual chunk
+  *	size, and verifies that the byte is undamaged when the chunk is freed.
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include <sys/mman.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ 
+ #include "postgres.h"
+ 
+ #include "utils/memutils.h"
+ 
+ /* Define this to detail debug alloc information */
+ /* #define HAVE_ALLOCINFO */
+ 
+ /*--------------------
+  * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
+  * for k = 0 .. ALLOCSET_NUM_FREELISTS-1.
+  *
+  * Note that all chunks in the freelists have power-of-2 sizes.  This
+  * improves recyclability: we may waste some space, but the wasted space
+  * should stay pretty constant as requests are made and released.
+  *
+  * A request too large for the last freelist is handled by allocating a
+  * dedicated block from malloc().  The block still has a block header and
+  * chunk header, but when the chunk is freed we'll return the whole block
+  * to malloc(), not put it on our freelists.
+  *
+  * 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.
+  * 8-byte alignment is enough on all currently known machines.
+  *
+  * With the current parameters, request sizes up to 8K are treated as chunks,
+  * larger requests go into dedicated blocks.  Change ALLOCSET_NUM_FREELISTS
+  * to adjust the boundary point.
+  *--------------------
+  */
+ 
+ #define ALLOC_MINBITS		3	/* smallest chunk size is 8 bytes */
+ #define ALLOCSET_NUM_FREELISTS	11
+ #define ALLOC_CHUNK_LIMIT	(1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
+ /* Size of largest chunk that we use a fixed size for */
+ 
+ /*--------------------
+  * The first block allocated for an allocset has size initBlockSize.
+  * Each time we have to allocate another block, we double the block size
+  * (if possible, and without exceeding maxBlockSize), so as to reduce
+  * the bookkeeping load on malloc().
+  *
+  * Blocks allocated to hold oversize chunks do not follow this rule, however;
+  * they are just however big they need to be to hold that single chunk.
+  *--------------------
+  */
+ 
+ #define ALLOC_BLOCKHDRSZ	MAXALIGN(sizeof(AllocBlockData))
+ #define ALLOC_CHUNKHDRSZ	MAXALIGN(sizeof(AllocChunkData))
+ 
+ typedef struct AllocBlockData *AllocBlock;		/* forward reference */
+ typedef struct AllocChunkData *AllocChunk;
+ 
+ /*
+  * AllocPointer
+  *		Aligned pointer which may be a member of an allocation set.
+  */
+ typedef void *AllocPointer;
+ 
+ /*
+  * AllocSetContext is our standard implementation of MemoryContext.
+  *
+  * Note: isReset means there is nothing for AllocSetReset to do.  This is
+  * different from the aset being physically empty (empty blocks list) because
+  * we may still have a keeper block.  It's also different from the set being
+  * logically empty, because we don't attempt to detect pfree'ing the last
+  * active chunk.
+  */
+ typedef struct AllocSetContext
+ {
+ 	MemoryContextData header;	/* Standard memory-context fields */
+ 	/* Info about storage allocated in this context: */
+ 	AllocBlock	blocks;			/* head of list of blocks in this set */
+ 	AllocChunk	freelist[ALLOCSET_NUM_FREELISTS];		/* free chunk lists */
+ 	bool		isReset;		/* T = no space alloced since last reset */
+ 	/* Allocation parameters for this context: */
+ 	Size		initBlockSize;	/* initial block size */
+ 	Size		maxBlockSize;	/* maximum block size */
+ 	Size		nextBlockSize;	/* next block size to allocate */
+ 	Size		allocChunkLimit;	/* effective chunk size limit */
+ 	AllocBlock	keeper;			/* if not NULL, keep this block over resets */
+ 	void *ptr;				/* pointer to mmap header */
+ } AllocSetContext;
+ 
+ typedef AllocSetContext *AllocSet;
+ 
+ /*
+  * AllocBlock
+  *		An AllocBlock is the unit of memory that is obtained by aset.c
+  *		from malloc().	It contains one or more AllocChunks, which are
+  *		the units requested by palloc() and freed by pfree().  AllocChunks
+  *		cannot be returned to malloc() individually, instead they are put
+  *		on freelists by pfree() and re-used by the next palloc() that has
+  *		a matching request size.
+  *
+  *		AllocBlockData is the header data for a block --- the usable space
+  *		within the block begins at the next alignment boundary.
+  */
+ typedef struct AllocBlockData
+ {
+ 	AllocSet	aset;			/* aset that owns this block */
+ 	AllocBlock	next;			/* next block in aset's blocks list */
+ 	char	   *freeptr;		/* start of free space in this block */
+ 	char	   *endptr;			/* end of space in this block */
+ } AllocBlockData;
+ 
+ 
+ /*
+  * AllocChunk
+  *		The prefix of each piece of memory in an AllocBlock
+  *
+  * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
+  */
+ typedef struct AllocChunkData
+ {
+ 	/* aset is the owning aset if allocated, or the freelist link if free */
+ 	void	   *aset;
+ 	/* size is always the size of the usable space in the chunk */
+ 	Size		size;
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	/* when debugging memory usage, also store actual requested size */
+ 	/* this is zero in a free chunk */
+ 	Size		requested_size;
+ #endif
+ } AllocChunkData;
+ 
+ /*
+  * AllocPointerIsValid
+  *		True iff pointer is valid allocation pointer.
+  */
+ #define AllocPointerIsValid(pointer) PointerIsValid(pointer)
+ 
+ /*
+  * AllocSetIsValid
+  *		True iff set is valid allocation set.
+  */
+ #define AllocSetIsValid(set) PointerIsValid(set)
+ 
+ #define AllocPointerGetChunk(ptr)	\
+ 					((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
+ #define AllocChunkGetPointer(chk)	\
+ 					((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
+ 
+ 
+ MemoryContext MMapAllocSetContextCreate(MemoryContext parent,
+ 							  const char *name,
+ 							  Size minContextSize,
+ 							  Size initBlockSize,
+ 							  Size maxBlockSize);
+ 
+ /*
+  * These functions implement the MemoryContext API for AllocSet contexts.
+  */
+ static void *AllocSetAlloc(MemoryContext context, Size size);
+ static void AllocSetFree(MemoryContext context, void *pointer);
+ static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
+ static void AllocSetInit(MemoryContext context);
+ static void AllocSetReset(MemoryContext context);
+ static void AllocSetDelete(MemoryContext context);
+ static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
+ static bool AllocSetIsEmpty(MemoryContext context);
+ static void AllocSetStats(MemoryContext context, int level);
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ static void AllocSetCheck(MemoryContext context);
+ #endif
+ 
+ /*
+  * This is the virtual function table for AllocSet contexts.
+  */
+ static MemoryContextMethods AllocSetMethods = {
+ 	AllocSetAlloc,
+ 	AllocSetFree,
+ 	AllocSetRealloc,
+ 	AllocSetInit,
+ 	AllocSetReset,
+ 	AllocSetDelete,
+ 	AllocSetGetChunkSpace,
+ 	AllocSetIsEmpty,
+ 	AllocSetStats
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	,AllocSetCheck
+ #endif
+ };
+ 
+ /*
+  * Table for AllocSetFreeIndex
+  */
+ #define LT16(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
+ 
+ static const unsigned char LogTable256[256] =
+ {
+ 	0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 	LT16(5), LT16(6), LT16(6), LT16(7), LT16(7), LT16(7), LT16(7),
+ 	LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8)
+ };
+ 
+ /* ----------
+  * Debug macros
+  * ----------
+  */
+ #ifdef HAVE_ALLOCINFO
+ #define AllocFreeInfo(_cxt, _chunk) \
+ 			fprintf(stderr, "AllocFree: %s: %p, %d\n", \
+ 				(_cxt)->header.name, (_chunk), (_chunk)->size)
+ #define AllocAllocInfo(_cxt, _chunk) \
+ 			fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
+ 				(_cxt)->header.name, (_chunk), (_chunk)->size)
+ #else
+ #define AllocFreeInfo(_cxt, _chunk)
+ #define AllocAllocInfo(_cxt, _chunk)
+ #endif
+ 
+ #define PAGEALIGN(size)		(((size) / getpagesize() + 1) * getpagesize())
+ 
+ static void *
+ mmap_alloc(Size size)
+ {
+ 	void *ptr;
+ 
+ 	size = PAGEALIGN(MAXALIGN(size) + MAXALIGN(sizeof(Size)));
+ 
+ 	ptr = mmap(NULL, size,
+ 								    PROT_READ | PROT_WRITE,
+ 								    MAP_SHARED | MAP_ANON,
+ 								    -1, 0);
+ 	if (ptr == MAP_FAILED)
+ 		ereport(ERROR,
+ 			(errcode(ERRCODE_OUT_OF_MEMORY),
+ 			 errmsg("out of memory"),
+ 			 errdetail("Failed on request of size %lu.",
+ 					   (unsigned long) size)));
+ 
+ 	*((Size*) ptr) = size;
+ 	return (char *) ptr + MAXALIGN(sizeof(Size));
+ }
+ 
+ static void
+ mmap_free(void *ptr)
+ {
+ 	Size size;
+ 
+ 	ptr = (char *) ptr - MAXALIGN(sizeof(Size));
+ 	size = *((Size*) ptr);
+ 
+ 	munmap(ptr, size);
+ }
+ 
+ static void *
+ mmap_realloc(void *ptr, Size size)
+ {
+ 	Size oldsize;
+ 	void *result = ptr;
+ 	int	i;
+ 	char *x;
+ 
+ 	ptr = (char *) ptr - MAXALIGN(sizeof(Size));
+ 
+ 	oldsize = *((Size *) ptr);
+ 	size = PAGEALIGN(MAXALIGN(size) + MAXALIGN(sizeof(Size)));
+ 
+ 	/* 
+ 	 * Attention - on linux you cannot call mremap together with
+ 	 * shared
+ 	 *
+ 	 * ptr = mremap(ptr, oldsize,
+ 	 *			    size,
+ 	 *			    MREMAP_MAYMOVE);
+ 	 */
+ 	if (size > oldsize)
+ 	{
+ 		void *newptr;
+ 		Size  newsize = PAGEALIGN(2 * oldsize);
+ 
+ 		while (newsize < size)
+ 			newsize = PAGEALIGN(2 * newsize);
+ 
+ 		newptr = mmap(NULL, newsize,
+ 					    PROT_READ | PROT_WRITE,
+ 					    MAP_SHARED | MAP_ANON,
+ 					    -1, 0);
+ 
+ 		if (newptr == MAP_FAILED)
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_OUT_OF_MEMORY),
+ 				 errmsg("out of memory"),
+ 				 errdetail("Failed on request of size %lu.",
+ 						   (unsigned long) newsize)));
+ 		memcpy(newptr, ptr, oldsize);
+ 		*((Size*) newptr) = newsize;
+ 
+ 		munmap(ptr, oldsize);
+ 		result = (char *) newptr + MAXALIGN(sizeof(Size));
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /* ----------
+  * AllocSetFreeIndex -
+  *
+  *		Depending on the size of an allocation compute which freechunk
+  *		list of the alloc set it belongs to.  Caller must have verified
+  *		that size <= ALLOC_CHUNK_LIMIT.
+  * ----------
+  */
+ static inline int
+ AllocSetFreeIndex(Size size)
+ {
+ 	int			idx;
+ 	unsigned int t,
+ 				tsize;
+ 
+ 	if (size > (1 << ALLOC_MINBITS))
+ 	{
+ 		tsize = (size - 1) >> ALLOC_MINBITS;
+ 
+ 		/*
+ 		 * At this point we need to obtain log2(tsize)+1, ie, the number of
+ 		 * not-all-zero bits at the right.	We used to do this with a
+ 		 * shift-and-count loop, but this function is enough of a hotspot to
+ 		 * justify micro-optimization effort.  The best approach seems to be
+ 		 * to use a lookup table.  Note that this code assumes that
+ 		 * ALLOCSET_NUM_FREELISTS <= 17, since we only cope with two bytes of
+ 		 * the tsize value.
+ 		 */
+ 		t = tsize >> 8;
+ 		idx = t ? LogTable256[t] + 8 : LogTable256[tsize];
+ 
+ 		Assert(idx < ALLOCSET_NUM_FREELISTS);
+ 	}
+ 	else
+ 		idx = 0;
+ 
+ 	return idx;
+ }
+ 
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 
+ /*
+  * Fill a just-allocated piece of memory with "random" data.  It's not really
+  * very random, just a repeating sequence with a length that's prime.  What
+  * we mainly want out of it is to have a good probability that two palloc's
+  * of the same number of bytes start out containing different data.
+  */
+ static void
+ randomize_mem(char *ptr, size_t size)
+ {
+ 	static int	save_ctr = 1;
+ 	int			ctr;
+ 
+ 	ctr = save_ctr;
+ 	while (size-- > 0)
+ 	{
+ 		*ptr++ = ctr;
+ 		if (++ctr > 251)
+ 			ctr = 1;
+ 	}
+ 	save_ctr = ctr;
+ }
+ #endif   /* RANDOMIZE_ALLOCATED_MEMORY */
+ 
+ 
+ /*
+  * Public routines
+  */
+ 
+ 
+ /*
+  * AllocSetContextCreate
+  *		Create a new AllocSet context.
+  *
+  * parent: parent context, or NULL if top-level context
+  * name: name of context (for debugging --- string will be copied)
+  * minContextSize: minimum context size
+  * initBlockSize: initial allocation block size
+  * maxBlockSize: maximum allocation block size
+  */
+ MemoryContext
+ MMapAllocSetContextCreate(MemoryContext parent,
+ 					  const char *name,
+ 					  Size minContextSize,
+ 					  Size initBlockSize,
+ 					  Size maxBlockSize)
+ {
+ 	AllocSet	context;
+ 
+ 	/* Do the type-independent part of context creation */
+ 	context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
+ 											 sizeof(AllocSetContext),
+ 											 &AllocSetMethods,
+ 											 parent,
+ 											 name);
+ 
+ 	/*
+ 	 * Make sure alloc parameters are reasonable, and save them.
+ 	 *
+ 	 * We somewhat arbitrarily enforce a minimum 1K block size.
+ 	 */
+ 	initBlockSize = MAXALIGN(initBlockSize);
+ 	if (initBlockSize < 1024)
+ 		initBlockSize = 1024;
+ 	maxBlockSize = MAXALIGN(maxBlockSize);
+ 	if (maxBlockSize < initBlockSize)
+ 		maxBlockSize = initBlockSize;
+ 	context->initBlockSize = initBlockSize;
+ 	context->maxBlockSize = maxBlockSize;
+ 	context->nextBlockSize = initBlockSize;
+ 
+ 	/*
+ 	 * Compute the allocation chunk size limit for this context.  It can't be
+ 	 * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists.
+ 	 * If maxBlockSize is small then requests exceeding the maxBlockSize
+ 	 * should be treated as large chunks, too.	We have to have
+ 	 * allocChunkLimit a power of two, because the requested and
+ 	 * actually-allocated sizes of any chunk must be on the same side of the
+ 	 * limit, else we get confused about whether the chunk is "big".
+ 	 */
+ 	context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
+ 	while (context->allocChunkLimit >
+ 		   (Size) (maxBlockSize - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ))
+ 		context->allocChunkLimit >>= 1;
+ 
+ 	/*
+ 	 * Grab always-allocated space, if requested
+ 	 */
+ 	if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)
+ 	{
+ 		Size		blksize = MAXALIGN(minContextSize);
+ 		AllocBlock	block;
+ 
+ 		block = (AllocBlock) mmap_alloc(blksize);
+ 		if (block == NULL)
+ 		{
+ 			MemoryContextStats(TopMemoryContext);
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OUT_OF_MEMORY),
+ 					 errmsg("out of memory"),
+ 					 errdetail("Failed while creating memory context \"%s\".",
+ 							   name)));
+ 		}
+ 		block->aset = context;
+ 		block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
+ 		block->endptr = ((char *) block) + blksize;
+ 		block->next = context->blocks;
+ 		context->blocks = block;
+ 		/* Mark block as not to be released at reset time */
+ 		context->keeper = block;
+ 	}
+ 
+ 	context->isReset = true;
+ 
+ 	return (MemoryContext) context;
+ }
+ 
+ /*
+  * AllocSetInit                                
+  *		Context-type-specific initialization routine.
+  *
+  * This is called by MemoryContextCreate() after setting up the
+  * generic MemoryContext fields and before linking the new context
+  * into the context tree.  We must do whatever is needed to make the
+  * new context minimally valid for deletion.  We must *not* risk
+  * failure --- thus, for example, allocating more memory is not cool.
+  * (AllocSetContextCreate can allocate memory when it gets control
+  * back, however.)
+  */
+ static void
+ AllocSetInit(MemoryContext context)
+ {
+ 	/*
+ 	 * Since MemoryContextCreate already zeroed the context node, we don't
+ 	 * have to do anything here: it's already OK.
+ 	 */
+ }
+ 
+ /*
+  * AllocSetReset
+  *		Frees all memory which is allocated in the given set.
+  *
+  * Actually, this routine has some discretion about what to do.
+  * It should mark all allocated chunks freed, but it need not necessarily
+  * give back all the resources the set owns.  Our actual implementation is
+  * that we hang onto any "keeper" block specified for the set.	In this way,
+  * we don't thrash malloc() when a context is repeatedly reset after small
+  * allocations, which is typical behavior for per-tuple contexts.
+  */
+ static void
+ AllocSetReset(MemoryContext context)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	AllocBlock	block;
+ 
+ 	AssertArg(AllocSetIsValid(set));
+ 
+ 	/* Nothing to do if no pallocs since startup or last reset */
+ 	if (set->isReset)
+ 		return;
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	/* Check for corruption and leaks before freeing */
+ 	AllocSetCheck(context);
+ #endif
+ 
+ 	/* Clear chunk freelists */
+ 	MemSetAligned(set->freelist, 0, sizeof(set->freelist));
+ 
+ 	block = set->blocks;
+ 
+ 	/* New blocks list is either empty or just the keeper block */
+ 	set->blocks = set->keeper;
+ 
+ 	while (block != NULL)
+ 	{
+ 		AllocBlock	next = block->next;
+ 
+ 		if (block == set->keeper)
+ 		{
+ 			/* Reset the block, but don't return it to malloc */
+ 			char	   *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
+ 
+ #ifdef CLOBBER_FREED_MEMORY
+ 			/* Wipe freed memory for debugging purposes */
+ 			memset(datastart, 0x7F, block->freeptr - datastart);
+ #endif
+ 			block->freeptr = datastart;
+ 			block->next = NULL;
+ 		}
+ 		else
+ 		{
+ 			/* Normal case, release the block */
+ #ifdef CLOBBER_FREED_MEMORY
+ 			/* Wipe freed memory for debugging purposes */
+ 			memset(block, 0x7F, block->freeptr - ((char *) block));
+ #endif
+ 			mmap_free(block);
+ 		}
+ 		block = next;
+ 	}
+ 
+ 	/* Reset block size allocation sequence, too */
+ 	set->nextBlockSize = set->initBlockSize;
+ 
+ 	set->isReset = true;
+ }
+ 
+ /*
+  * AllocSetDelete
+  *		Frees all memory which is allocated in the given set,
+  *		in preparation for deletion of the set.
+  *
+  * Unlike AllocSetReset, this *must* free all resources of the set.
+  * But note we are not responsible for deleting the context node itself.
+  */
+ static void
+ AllocSetDelete(MemoryContext context)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	AllocBlock	block = set->blocks;
+ 
+ 	AssertArg(AllocSetIsValid(set));
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	/* Check for corruption and leaks before freeing */
+ 	AllocSetCheck(context);
+ #endif
+ 
+ 	/* Make it look empty, just in case... */
+ 	MemSetAligned(set->freelist, 0, sizeof(set->freelist));
+ 	set->blocks = NULL;
+ 	set->keeper = NULL;
+ 
+ 	while (block != NULL)
+ 	{
+ 		AllocBlock	next = block->next;
+ 
+ #ifdef CLOBBER_FREED_MEMORY
+ 		/* Wipe freed memory for debugging purposes */
+ 		memset(block, 0x7F, block->freeptr - ((char *) block));
+ #endif
+ 		mmap_free(block);
+ 		block = next;
+ 	}
+ }
+ 
+ /*
+  * AllocSetAlloc
+  *		Returns pointer to allocated memory of given size; memory is added
+  *		to the set.
+  */
+ static void *
+ AllocSetAlloc(MemoryContext context, Size size)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	AllocBlock	block;
+ 	AllocChunk	chunk;
+ 	int			fidx;
+ 	Size		chunk_size;
+ 	Size		blksize;
+ 
+ 	AssertArg(AllocSetIsValid(set));
+ 
+ 	/*
+ 	 * If requested size exceeds maximum for chunks, allocate an entire block
+ 	 * for this request.
+ 	 */
+ 	if (size > set->allocChunkLimit)
+ 	{
+ 		chunk_size = MAXALIGN(size);
+ 		blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
+ 		block = (AllocBlock) mmap_alloc(blksize);
+ 		if (block == NULL)
+ 		{
+ 			MemoryContextStats(TopMemoryContext);
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OUT_OF_MEMORY),
+ 					 errmsg("out of memory"),
+ 					 errdetail("Failed on request of size %lu.",
+ 							   (unsigned long) size)));
+ 		}
+ 		block->aset = set;
+ 		block->freeptr = block->endptr = ((char *) block) + blksize;
+ 
+ 		chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
+ 		chunk->aset = set;
+ 		chunk->size = chunk_size;
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 		chunk->requested_size = size;
+ 		/* set mark to catch clobber of "unused" space */
+ 		if (size < chunk_size)
+ 			((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
+ #endif
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 		/* fill the allocated space with junk */
+ 		randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+ #endif
+ 
+ 		/*
+ 		 * Stick the new 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;
+ 			set->blocks->next = block;
+ 		}
+ 		else
+ 		{
+ 			block->next = NULL;
+ 			set->blocks = block;
+ 		}
+ 
+ 		set->isReset = false;
+ 
+ 		AllocAllocInfo(set, chunk);
+ 		return AllocChunkGetPointer(chunk);
+ 	}
+ 
+ 	/*
+ 	 * Request is small enough to be treated as a chunk.  Look in the
+ 	 * corresponding free list to see if there is a free chunk we could reuse.
+ 	 * If one is found, remove it from the free list, make it again a member
+ 	 * of the alloc set and return its data address.
+ 	 */
+ 	fidx = AllocSetFreeIndex(size);
+ 	chunk = set->freelist[fidx];
+ 	if (chunk != NULL)
+ 	{
+ 		Assert(chunk->size >= size);
+ 
+ 		set->freelist[fidx] = (AllocChunk) chunk->aset;
+ 
+ 		chunk->aset = (void *) set;
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 		chunk->requested_size = size;
+ 		/* set mark to catch clobber of "unused" space */
+ 		if (size < chunk->size)
+ 			((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
+ #endif
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 		/* fill the allocated space with junk */
+ 		randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+ #endif
+ 
+ 		/* isReset must be false already */
+ 		Assert(!set->isReset);
+ 
+ 		AllocAllocInfo(set, chunk);
+ 		return AllocChunkGetPointer(chunk);
+ 	}
+ 
+ 	/*
+ 	 * Choose the actual chunk size to allocate.
+ 	 */
+ 	chunk_size = (1 << ALLOC_MINBITS) << fidx;
+ 	Assert(chunk_size >= size);
+ 
+ 	/*
+ 	 * If there is enough room in the active allocation block, we will put the
+ 	 * chunk into that block.  Else must start a new one.
+ 	 */
+ 	if ((block = set->blocks) != NULL)
+ 	{
+ 		Size		availspace = block->endptr - block->freeptr;
+ 
+ 		if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ))
+ 		{
+ 			/*
+ 			 * The existing active (top) block does not have enough room for
+ 			 * the requested allocation, but it might still have a useful
+ 			 * amount of space in it.  Once we push it down in the block list,
+ 			 * we'll never try to allocate more space from it. So, before we
+ 			 * do that, carve up its free space into chunks that we can put on
+ 			 * the set's freelists.
+ 			 *
+ 			 * Because we can only get here when there's less than
+ 			 * ALLOC_CHUNK_LIMIT left in the block, this loop cannot iterate
+ 			 * more than ALLOCSET_NUM_FREELISTS-1 times.
+ 			 */
+ 			while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
+ 			{
+ 				Size		availchunk = availspace - ALLOC_CHUNKHDRSZ;
+ 				int			a_fidx = AllocSetFreeIndex(availchunk);
+ 
+ 				/*
+ 				 * In most cases, we'll get back the index of the next larger
+ 				 * freelist than the one we need to put this chunk on.	The
+ 				 * exception is when availchunk is exactly a power of 2.
+ 				 */
+ 				if (availchunk != ((Size) 1 << (a_fidx + ALLOC_MINBITS)))
+ 				{
+ 					a_fidx--;
+ 					Assert(a_fidx >= 0);
+ 					availchunk = ((Size) 1 << (a_fidx + ALLOC_MINBITS));
+ 				}
+ 
+ 				chunk = (AllocChunk) (block->freeptr);
+ 
+ 				block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
+ 				availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
+ 
+ 				chunk->size = availchunk;
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 				chunk->requested_size = 0;		/* mark it free */
+ #endif
+ 				chunk->aset = (void *) set->freelist[a_fidx];
+ 				set->freelist[a_fidx] = chunk;
+ 			}
+ 
+ 			/* Mark that we need to create a new block */
+ 			block = NULL;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Time to create a new regular (multi-chunk) block?
+ 	 */
+ 	if (block == NULL)
+ 	{
+ 		Size		required_size;
+ 
+ 		/*
+ 		 * The first such block has size initBlockSize, and we double the
+ 		 * space in each succeeding block, but not more than maxBlockSize.
+ 		 */
+ 		blksize = set->nextBlockSize;
+ 		set->nextBlockSize <<= 1;
+ 		if (set->nextBlockSize > set->maxBlockSize)
+ 			set->nextBlockSize = set->maxBlockSize;
+ 
+ 		/*
+ 		 * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
+ 		 * space... but try to keep it a power of 2.
+ 		 */
+ 		required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
+ 		while (blksize < required_size)
+ 			blksize <<= 1;
+ 
+ 		/* Try to allocate it */
+ 		block = (AllocBlock) mmap_alloc(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;
+ 			if (blksize < required_size)
+ 				break;
+ 			block = (AllocBlock) mmap_alloc(blksize);
+ 		}
+ 
+ 		if (block == NULL)
+ 		{
+ 			MemoryContextStats(TopMemoryContext);
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OUT_OF_MEMORY),
+ 					 errmsg("out of memory"),
+ 					 errdetail("Failed on request of size %lu.",
+ 							   (unsigned long) size)));
+ 		}
+ 
+ 		block->aset = set;
+ 		block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
+ 		block->endptr = ((char *) block) + blksize;
+ 
+ 		/*
+ 		 * If this is the first block of the set, make it the "keeper" block.
+ 		 * Formerly, a keeper block could only be created during context
+ 		 * creation, but allowing it to happen here lets us have fast reset
+ 		 * cycling even for contexts created with minContextSize = 0; that way
+ 		 * we don't have to force space to be allocated in contexts that might
+ 		 * never need any space.  Don't mark an oversize block as a keeper,
+ 		 * however.
+ 		 */
+ 		if (set->keeper == NULL && blksize == set->initBlockSize)
+ 			set->keeper = block;
+ 
+ 		block->next = set->blocks;
+ 		set->blocks = block;
+ 	}
+ 
+ 	/*
+ 	 * OK, do the allocation
+ 	 */
+ 	chunk = (AllocChunk) (block->freeptr);
+ 
+ 	block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
+ 	Assert(block->freeptr <= block->endptr);
+ 
+ 	chunk->aset = (void *) set;
+ 	chunk->size = chunk_size;
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	chunk->requested_size = size;
+ 	/* set mark to catch clobber of "unused" space */
+ 	if (size < chunk->size)
+ 		((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
+ #endif
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 	/* fill the allocated space with junk */
+ 	randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+ #endif
+ 
+ 	set->isReset = false;
+ 
+ 	AllocAllocInfo(set, chunk);
+ 	return AllocChunkGetPointer(chunk);
+ }
+ 
+ /*
+  * AllocSetFree
+  *		Frees allocated memory; memory is removed from the set.
+  */
+ static void
+ AllocSetFree(MemoryContext context, void *pointer)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	AllocChunk	chunk = AllocPointerGetChunk(pointer);
+ 
+ 	AllocFreeInfo(set, chunk);
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	/* Test for someone scribbling on unused space in chunk */
+ 	if (chunk->requested_size < chunk->size)
+ 		if (((char *) pointer)[chunk->requested_size] != 0x7E)
+ 			elog(WARNING, "detected write past chunk end in %s %p",
+ 				 set->header.name, chunk);
+ #endif
+ 
+ 	if (chunk->size > set->allocChunkLimit)
+ 	{
+ 		/*
+ 		 * Big chunks are certain to have been allocated as single-chunk
+ 		 * blocks.	Find the containing block and return it to malloc().
+ 		 */
+ 		AllocBlock	block = set->blocks;
+ 		AllocBlock	prevblock = NULL;
+ 
+ 		while (block != NULL)
+ 		{
+ 			if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
+ 				break;
+ 			prevblock = block;
+ 			block = block->next;
+ 		}
+ 		if (block == NULL)
+ 			elog(ERROR, "could not find block containing chunk %p", chunk);
+ 		/* let's just make sure chunk is the only one in the block */
+ 		Assert(block->freeptr == ((char *) block) +
+ 			   (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
+ 
+ 		/* OK, remove block from aset's list and free it */
+ 		if (prevblock == NULL)
+ 			set->blocks = block->next;
+ 		else
+ 			prevblock->next = block->next;
+ #ifdef CLOBBER_FREED_MEMORY
+ 		/* Wipe freed memory for debugging purposes */
+ 		memset(block, 0x7F, block->freeptr - ((char *) block));
+ #endif
+ 		mmap_free(block);
+ 	}
+ 	else
+ 	{
+ 		/* Normal case, put the chunk into appropriate freelist */
+ 		int			fidx = AllocSetFreeIndex(chunk->size);
+ 
+ 		chunk->aset = (void *) set->freelist[fidx];
+ 
+ #ifdef CLOBBER_FREED_MEMORY
+ 		/* Wipe freed memory for debugging purposes */
+ 		memset(pointer, 0x7F, chunk->size);
+ #endif
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 		/* Reset requested_size to 0 in chunks that are on freelist */
+ 		chunk->requested_size = 0;
+ #endif
+ 		set->freelist[fidx] = chunk;
+ 	}
+ }
+ 
+ /*
+  * AllocSetRealloc
+  *		Returns new pointer to allocated memory of given size; this memory
+  *		is added to the set.  Memory associated with given pointer is copied
+  *		into the new memory, and the old memory is freed.
+  */
+ static void *
+ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	AllocChunk	chunk = AllocPointerGetChunk(pointer);
+ 	Size		oldsize = chunk->size;
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 	/* Test for someone scribbling on unused space in chunk */
+ 	if (chunk->requested_size < oldsize)
+ 		if (((char *) pointer)[chunk->requested_size] != 0x7E)
+ 			elog(WARNING, "detected write past chunk end in %s %p",
+ 				 set->header.name, chunk);
+ #endif
+ 
+ 	/* isReset must be false already */
+ 	Assert(!set->isReset);
+ 
+ 	/*
+ 	 * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
+ 	 * allocated area already is >= the new size.  (In particular, we always
+ 	 * fall out here if the requested size is a decrease.)
+ 	 */
+ 	if (oldsize >= size)
+ 	{
+ #ifdef MEMORY_CONTEXT_CHECKING
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 		/* We can only fill the extra space if we know the prior request */
+ 		if (size > chunk->requested_size)
+ 			randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
+ 						  size - chunk->requested_size);
+ #endif
+ 
+ 		chunk->requested_size = size;
+ 		/* set mark to catch clobber of "unused" space */
+ 		if (size < oldsize)
+ 			((char *) pointer)[size] = 0x7E;
+ #endif
+ 		return pointer;
+ 	}
+ 
+ 	if (oldsize > set->allocChunkLimit)
+ 	{
+ 		/*
+ 		 * The chunk must have been allocated as a single-chunk block.	Find
+ 		 * the containing block and use realloc() to make it bigger with
+ 		 * minimum space wastage.
+ 		 */
+ 		AllocBlock	block = set->blocks;
+ 		AllocBlock	prevblock = NULL;
+ 		Size		chksize;
+ 		Size		blksize;
+ 
+ 		while (block != NULL)
+ 		{
+ 			if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
+ 				break;
+ 			prevblock = block;
+ 			block = block->next;
+ 		}
+ 		if (block == NULL)
+ 			elog(ERROR, "could not find block containing chunk %p", chunk);
+ 		/* let's just make sure chunk is the only one in the block */
+ 		Assert(block->freeptr == ((char *) block) +
+ 			   (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
+ 
+ 		/* Do the realloc */
+ 		chksize = MAXALIGN(size);
+ 		blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
+ 
+ 		block = (AllocBlock) mmap_realloc(block, blksize);
+ 		if (block == NULL)
+ 		{
+ 			MemoryContextStats(TopMemoryContext);
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OUT_OF_MEMORY),
+ 					 errmsg("out of memory"),
+ 					 errdetail("Failed on request of size %lu.",
+ 							   (unsigned long) size)));
+ 		}
+ 		block->freeptr = block->endptr = ((char *) block) + blksize;
+ 
+ 		/* Update pointers since block has likely been moved */
+ 		chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
+ 		if (prevblock == NULL)
+ 			set->blocks = block;
+ 		else
+ 			prevblock->next = block;
+ 		chunk->size = chksize;
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ #ifdef RANDOMIZE_ALLOCATED_MEMORY
+ 		/* We can only fill the extra space if we know the prior request */
+ 		randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
+ 					  size - chunk->requested_size);
+ #endif
+ 
+ 		chunk->requested_size = size;
+ 		/* set mark to catch clobber of "unused" space */
+ 		if (size < chunk->size)
+ 			((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
+ #endif
+ 
+ 		return AllocChunkGetPointer(chunk);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * Small-chunk case.  We just do this by brute force, ie, allocate a
+ 		 * new chunk and copy the data.  Since we know the existing data isn't
+ 		 * huge, this won't involve any great memcpy expense, so it's not
+ 		 * worth being smarter.  (At one time we tried to avoid memcpy when it
+ 		 * was possible to enlarge the chunk in-place, but that turns out to
+ 		 * misbehave unpleasantly for repeated cycles of
+ 		 * palloc/repalloc/pfree: the eventually freed chunks go into the
+ 		 * wrong freelist for the next initial palloc request, and so we leak
+ 		 * memory indefinitely.  See pgsql-hackers archives for 2007-08-11.)
+ 		 */
+ 		AllocPointer newPointer;
+ 
+ 		/* allocate new chunk */
+ 		newPointer = AllocSetAlloc((MemoryContext) set, size);
+ 
+ 		/* transfer existing data (certain to fit) */
+ 		memcpy(newPointer, pointer, oldsize);
+ 
+ 		/* free old chunk */
+ 		AllocSetFree((MemoryContext) set, pointer);
+ 
+ 		return newPointer;
+ 	}
+ }
+ 
+ /*
+  * AllocSetGetChunkSpace
+  *		Given a currently-allocated chunk, determine the total space
+  *		it occupies (including all memory-allocation overhead).
+  */
+ static Size
+ AllocSetGetChunkSpace(MemoryContext context, void *pointer)
+ {
+ 	AllocChunk	chunk = AllocPointerGetChunk(pointer);
+ 
+ 	return chunk->size + ALLOC_CHUNKHDRSZ;
+ }
+ 
+ /*
+  * AllocSetIsEmpty
+  *		Is an allocset empty of any allocated space?
+  */
+ static bool
+ AllocSetIsEmpty(MemoryContext context)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 
+ 	/*
+ 	 * For now, we say "empty" only if the context is new or just reset. We
+ 	 * could examine the freelists to determine if all space has been freed,
+ 	 * but it's not really worth the trouble for present uses of this
+ 	 * functionality.
+ 	 */
+ 	if (set->isReset)
+ 		return true;
+ 	return false;
+ }
+ 
+ /*
+  * AllocSetStats
+  *		Displays stats about memory consumption of an allocset.
+  */
+ static void
+ AllocSetStats(MemoryContext context, int level)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	long		nblocks = 0;
+ 	long		nchunks = 0;
+ 	long		totalspace = 0;
+ 	long		freespace = 0;
+ 	AllocBlock	block;
+ 	AllocChunk	chunk;
+ 	int			fidx;
+ 	int			i;
+ 
+ 	for (block = set->blocks; block != NULL; block = block->next)
+ 	{
+ 		nblocks++;
+ 		totalspace += block->endptr - ((char *) block);
+ 		freespace += block->endptr - block->freeptr;
+ 	}
+ 	for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
+ 	{
+ 		for (chunk = set->freelist[fidx]; chunk != NULL;
+ 			 chunk = (AllocChunk) chunk->aset)
+ 		{
+ 			nchunks++;
+ 			freespace += chunk->size + ALLOC_CHUNKHDRSZ;
+ 		}
+ 	}
+ 
+ 	for (i = 0; i < level; i++)
+ 		fprintf(stderr, "  ");
+ 
+ 	fprintf(stderr,
+ 			"%s: %lu total in %ld blocks; %lu free (%ld chunks); %lu used\n",
+ 			set->header.name, totalspace, nblocks, freespace, nchunks,
+ 			totalspace - freespace);
+ }
+ 
+ 
+ #ifdef MEMORY_CONTEXT_CHECKING
+ 
+ /*
+  * AllocSetCheck
+  *		Walk through chunks and check consistency of memory.
+  *
+  * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+  * find yourself in an infinite loop when trouble occurs, because this
+  * routine will be entered again when elog cleanup tries to release memory!
+  */
+ static void
+ AllocSetCheck(MemoryContext context)
+ {
+ 	AllocSet	set = (AllocSet) context;
+ 	char	   *name = set->header.name;
+ 	AllocBlock	block;
+ 
+ 	for (block = set->blocks; block != NULL; block = block->next)
+ 	{
+ 		char	   *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
+ 		long		blk_used = block->freeptr - bpoz;
+ 		long		blk_data = 0;
+ 		long		nchunks = 0;
+ 
+ 		/*
+ 		 * Empty block - empty can be keeper-block only
+ 		 */
+ 		if (!blk_used)
+ 		{
+ 			if (set->keeper != block)
+ 				elog(WARNING, "problem in alloc set %s: empty block %p",
+ 					 name, block);
+ 		}
+ 
+ 		/*
+ 		 * Chunk walker
+ 		 */
+ 		while (bpoz < block->freeptr)
+ 		{
+ 			AllocChunk	chunk = (AllocChunk) bpoz;
+ 			Size		chsize,
+ 						dsize;
+ 			char	   *chdata_end;
+ 
+ 			chsize = chunk->size;		/* aligned chunk size */
+ 			dsize = chunk->requested_size;		/* real data */
+ 			chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize);
+ 
+ 			/*
+ 			 * Check chunk size
+ 			 */
+ 			if (dsize > chsize)
+ 				elog(WARNING, "problem in alloc set %s: req size > alloc size for chunk %p in block %p",
+ 					 name, chunk, block);
+ 			if (chsize < (1 << ALLOC_MINBITS))
+ 				elog(WARNING, "problem in alloc set %s: bad size %lu for chunk %p in block %p",
+ 					 name, (unsigned long) chsize, chunk, block);
+ 
+ 			/* single-chunk block? */
+ 			if (chsize > set->allocChunkLimit &&
+ 				chsize + ALLOC_CHUNKHDRSZ != blk_used)
+ 				elog(WARNING, "problem in alloc set %s: bad single-chunk %p in block %p",
+ 					 name, chunk, block);
+ 
+ 			/*
+ 			 * If chunk is allocated, check for correct aset pointer. (If it's
+ 			 * free, the aset is the freelist pointer, which we can't check as
+ 			 * easily...)
+ 			 */
+ 			if (dsize > 0 && chunk->aset != (void *) set)
+ 				elog(WARNING, "problem in alloc set %s: bogus aset link in block %p, chunk %p",
+ 					 name, block, chunk);
+ 
+ 			/*
+ 			 * Check for overwrite of "unallocated" space in chunk
+ 			 */
+ 			if (dsize > 0 && dsize < chsize && *chdata_end != 0x7E)
+ 				elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p",
+ 					 name, block, chunk);
+ 
+ 			blk_data += chsize;
+ 			nchunks++;
+ 
+ 			bpoz += ALLOC_CHUNKHDRSZ + chsize;
+ 		}
+ 
+ 		if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
+ 			elog(WARNING, "problem in alloc set %s: found inconsistent memory block %p",
+ 				 name, block);
+ 	}
+ }
+ 
+ #endif   /* MEMORY_CONTEXT_CHECKING */
*** ./contrib/dict_preload/uninstall_dict_preload.sql.orig	2010-03-18 17:00:58.000000000 +0100
--- ./contrib/dict_preload/uninstall_dict_preload.sql	2010-03-18 13:52:49.000000000 +0100
***************
*** 0 ****
--- 1,10 ----
+ /* $PostgreSQL: pgsql/contrib/dict_int/uninstall_dict_int.sql,v 1.3 2007/11/13 04:24:27 momjian Exp $ */
+ 
+ -- Adjust this setting to control where the objects get dropped.
+ SET search_path = public;
+ 
+ DROP TEXT SEARCH TEMPLATE preloaddict_template CASCADE;
+ 
+ DROP FUNCTION dpreloaddict_init(internal);
+ 
+ DROP FUNCTION dpreloaddict_lexize(internal,internal,internal,internal);
*** ./src/backend/tsearch/spell.c.orig	2010-01-02 17:57:53.000000000 +0100
--- ./src/backend/tsearch/spell.c	2010-03-31 14:41:27.412855204 +0200
***************
*** 31,36 ****
--- 31,41 ----
  #define tmpalloc(sz)  MemoryContextAlloc(tmpCtx, (sz))
  #define tmpalloc0(sz)  MemoryContextAllocZero(tmpCtx, (sz))
  
+ static void *salloc_ptr;
+ static Size salloc_free_size;
+ 
+ #define SALLOC_BLOCKSIZE	(1024 * 1024 * 2)
+ 
  static void
  checkTmpCtx(void)
  {
***************
*** 45,50 ****
--- 50,57 ----
  									   ALLOCSET_DEFAULT_MINSIZE,
  									   ALLOCSET_DEFAULT_INITSIZE,
  									   ALLOCSET_DEFAULT_MAXSIZE);
+ 		salloc_ptr = NULL;
+ 		salloc_free_size = 0;
  	}
  	else
  		tmpCtx = CurrentMemoryContext->firstchild;
***************
*** 63,68 ****
--- 70,133 ----
  	return dst;
  }
  
+ /*
+  * used for allocation lot of small memory blocks
+  */
+ static void *
+ simple_alloc(Size size)
+ {
+ 	void *result;
+ 	
+ 	size = MAXALIGN(size);
+ 	
+ 	if (size > salloc_free_size)
+ 	{
+ 		salloc_ptr = palloc(SALLOC_BLOCKSIZE);
+ 		salloc_free_size = SALLOC_BLOCKSIZE;
+ 	}
+ 	
+ 	result = salloc_ptr;
+ 	salloc_ptr += size;
+ 	salloc_free_size -= size;
+ 	
+ 	return result;
+ }
+ 
+ static void *
+ simple_alloc0(Size size)
+ {
+ 	void *result;
+ 	
+ 	size = MAXALIGN(size);
+ 	
+ 	if (size > salloc_free_size)
+ 	{
+ 		salloc_ptr = palloc(SALLOC_BLOCKSIZE);
+ 		salloc_free_size = SALLOC_BLOCKSIZE;
+ 	}
+ 	
+ 	result = salloc_ptr;
+ 	salloc_ptr += size;
+ 	salloc_free_size -= size;
+ 	
+ 	memset(result, 0, size);
+ 	
+ 	return result;
+ }
+ 
+ static char *
+ simple_strdup(char *str)
+ {
+ 	char *result;
+ 	int len;
+ 	
+ 	len = strlen(str);
+ 	result = simple_alloc(len + 1);
+ 	memcpy(result, str, len + 1);
+ 	
+ 	return result;
+ }
+ 
  #define MAX_NORM 1024
  #define MAXNORMLEN 256
  
***************
*** 375,383 ****
  	Affix->flag = flag;
  	Affix->type = type;
  
! 	Affix->find = (find && *find) ? pstrdup(find) : VoidString;
  	if ((Affix->replen = strlen(repl)) > 0)
! 		Affix->repl = pstrdup(repl);
  	else
  		Affix->repl = VoidString;
  	Conf->naffixes++;
--- 440,448 ----
  	Affix->flag = flag;
  	Affix->type = type;
  
! 	Affix->find = (find && *find) ? simple_strdup(find) : VoidString;
  	if ((Affix->replen = strlen(repl)) > 0)
! 		Affix->repl = simple_strdup(repl);
  	else
  		Affix->repl = VoidString;
  	Conf->naffixes++;
***************
*** 833,839 ****
  	}
  
  	ptr = Conf->AffixData + Conf->nAffixData;
! 	*ptr = palloc(strlen(Conf->AffixData[a1]) + strlen(Conf->AffixData[a2]) +
  				  1 /* space */ + 1 /* \0 */ );
  	sprintf(*ptr, "%s %s", Conf->AffixData[a1], Conf->AffixData[a2]);
  	ptr++;
--- 898,904 ----
  	}
  
  	ptr = Conf->AffixData + Conf->nAffixData;
! 	*ptr = simple_alloc(strlen(Conf->AffixData[a1]) + strlen(Conf->AffixData[a2]) +
  				  1 /* space */ + 1 /* \0 */ );
  	sprintf(*ptr, "%s %s", Conf->AffixData[a1], Conf->AffixData[a2]);
  	ptr++;
***************
*** 878,884 ****
  	if (!nchar)
  		return NULL;
  
! 	rs = (SPNode *) palloc0(SPNHDRSZ + nchar * sizeof(SPNodeData));
  	rs->length = nchar;
  	data = rs->data;
  
--- 943,949 ----
  	if (!nchar)
  		return NULL;
  
! 	rs = (SPNode *) simple_alloc0(SPNHDRSZ + nchar * sizeof(SPNodeData));
  	rs->length = nchar;
  	data = rs->data;
  
***************
*** 946,952 ****
  	int			curaffix;
  
  	checkTmpCtx();
- 
  	/* compress affixes */
  
  	/* Count the number of different flags used in the dictionary */
--- 1011,1016 ----
***************
*** 974,980 ****
  		{
  			curaffix++;
  			Assert(curaffix < naffix);
! 			Conf->AffixData[curaffix] = pstrdup(Conf->Spell[i]->p.flag);
  		}
  
  		Conf->Spell[i]->p.d.affix = curaffix;
--- 1038,1044 ----
  		{
  			curaffix++;
  			Assert(curaffix < naffix);
! 			Conf->AffixData[curaffix] = simple_strdup(Conf->Spell[i]->p.flag);
  		}
  
  		Conf->Spell[i]->p.d.affix = curaffix;
***************
*** 1014,1020 ****
  	aff = (AFFIX **) tmpalloc(sizeof(AFFIX *) * (high - low + 1));
  	naff = 0;
  
! 	rs = (AffixNode *) palloc0(ANHRDSZ + nchar * sizeof(AffixNodeData));
  	rs->length = nchar;
  	data = rs->data;
  
--- 1078,1084 ----
  	aff = (AFFIX **) tmpalloc(sizeof(AFFIX *) * (high - low + 1));
  	naff = 0;
  
! 	rs = (AffixNode *) simple_alloc0(ANHRDSZ + nchar * sizeof(AffixNodeData));
  	rs->length = nchar;
  	data = rs->data;
  
***************
*** 1030,1036 ****
  					if (naff)
  					{
  						data->naff = naff;
! 						data->aff = (AFFIX **) palloc(sizeof(AFFIX *) * naff);
  						memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
  						naff = 0;
  					}
--- 1094,1100 ----
  					if (naff)
  					{
  						data->naff = naff;
! 						data->aff = (AFFIX **) simple_alloc(sizeof(AFFIX *) * naff);
  						memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
  						naff = 0;
  					}
***************
*** 1050,1056 ****
  	if (naff)
  	{
  		data->naff = naff;
! 		data->aff = (AFFIX **) palloc(sizeof(AFFIX *) * naff);
  		memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
  		naff = 0;
  	}
--- 1114,1120 ----
  	if (naff)
  	{
  		data->naff = naff;
! 		data->aff = (AFFIX **) simple_alloc(sizeof(AFFIX *) * naff);
  		memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
  		naff = 0;
  	}
***************
*** 1067,1073 ****
  				cnt = 0;
  	int			start = (issuffix) ? startsuffix : 0;
  	int			end = (issuffix) ? Conf->naffixes : startsuffix;
! 	AffixNode  *Affix = (AffixNode *) palloc0(ANHRDSZ + sizeof(AffixNodeData));
  
  	Affix->length = 1;
  	Affix->isvoid = 1;
--- 1131,1137 ----
  				cnt = 0;
  	int			start = (issuffix) ? startsuffix : 0;
  	int			end = (issuffix) ? Conf->naffixes : startsuffix;
! 	AffixNode  *Affix = (AffixNode *) simple_alloc0(ANHRDSZ + sizeof(AffixNodeData));
  
  	Affix->length = 1;
  	Affix->isvoid = 1;
***************
*** 1091,1097 ****
  	if (cnt == 0)
  		return;
  
! 	Affix->data->aff = (AFFIX **) palloc(sizeof(AFFIX *) * cnt);
  	Affix->data->naff = (uint32) cnt;
  
  	cnt = 0;
--- 1155,1161 ----
  	if (cnt == 0)
  		return;
  
! 	Affix->data->aff = (AFFIX **) simple_alloc(sizeof(AFFIX *) * cnt);
  	Affix->data->naff = (uint32) cnt;
  
  	cnt = 0;