On 01.10.2018 12:22, Arthur Zakirov wrote:
On Thu, Jun 14, 2018 at 11:40:17AM +0300, Arthur Zakirov wrote:
I attached new version of the patch.

The patch still applies to HEAD. I moved it to the next commitfest.


Here is the rebased patch. I also updated copyright in ts_shared.h and ts_shared.c.

--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index eb39466b22..eb8416ce7f 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -78,6 +78,8 @@
 #define tmpalloc(sz)  MemoryContextAlloc(Conf->buildCxt, (sz))
 #define tmpalloc0(sz)  MemoryContextAllocZero(Conf->buildCxt, (sz))
 
+#define tmpstrdup(str)	MemoryContextStrdup(Conf->buildCxt, (str))
+
 /*
  * Prepare for constructing an ISpell dictionary.
  *
@@ -498,7 +500,7 @@ NIAddSpell(IspellDict *Conf, const char *word, const char *flag)
 	Conf->Spell[Conf->nspell] = (SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1);
 	strcpy(Conf->Spell[Conf->nspell]->word, word);
 	Conf->Spell[Conf->nspell]->p.flag = (*flag != '\0')
-		? cpstrdup(Conf, flag) : VoidString;
+		? tmpstrdup(flag) : VoidString;
 	Conf->nspell++;
 }
 
@@ -1040,7 +1042,7 @@ setCompoundAffixFlagValue(IspellDict *Conf, CompoundAffixFlag *entry,
 		entry->flag.i = i;
 	}
 	else
-		entry->flag.s = cpstrdup(Conf, s);
+		entry->flag.s = tmpstrdup(s);
 
 	entry->flagMode = Conf->flagMode;
 	entry->value = val;
@@ -1541,6 +1543,9 @@ nextline:
 	return;
 
 isnewformat:
+	pfree(recoded);
+	pfree(pstr);
+
 	if (oldformat)
 		ereport(ERROR,
 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
diff --git a/contrib/dict_int/dict_int.c b/contrib/dict_int/dict_int.c
index 628b9769c3..ddde55eee4 100644
--- a/contrib/dict_int/dict_int.c
+++ b/contrib/dict_int/dict_int.c
@@ -30,7 +30,7 @@ PG_FUNCTION_INFO_V1(dintdict_lexize);
 Datum
 dintdict_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictInt    *d;
 	ListCell   *l;
 
@@ -38,7 +38,7 @@ dintdict_init(PG_FUNCTION_ARGS)
 	d->maxlen = 6;
 	d->rejectlong = false;
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/contrib/dict_xsyn/dict_xsyn.c b/contrib/dict_xsyn/dict_xsyn.c
index 509e14aee0..15b1a0033a 100644
--- a/contrib/dict_xsyn/dict_xsyn.c
+++ b/contrib/dict_xsyn/dict_xsyn.c
@@ -140,7 +140,7 @@ read_dictionary(DictSyn *d, const char *filename)
 Datum
 dxsyn_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictSyn    *d;
 	ListCell   *l;
 	char	   *filename = NULL;
@@ -153,7 +153,7 @@ dxsyn_init(PG_FUNCTION_ARGS)
 	d->matchsynonyms = false;
 	d->keepsynonyms = true;
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/contrib/unaccent/unaccent.c b/contrib/unaccent/unaccent.c
index fc5176e338..f3663cefd0 100644
--- a/contrib/unaccent/unaccent.c
+++ b/contrib/unaccent/unaccent.c
@@ -270,12 +270,12 @@ PG_FUNCTION_INFO_V1(unaccent_init);
 Datum
 unaccent_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	TrieChar   *rootTrie = NULL;
 	bool		fileloaded = false;
 	ListCell   *l;
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index cda21675f0..93a71adc5d 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -390,17 +390,25 @@ verify_dictoptions(Oid tmplId, List *dictoptions)
 	}
 	else
 	{
+		DictInitData init_data;
+
 		/*
 		 * Copy the options just in case init method thinks it can scribble on
 		 * them ...
 		 */
 		dictoptions = copyObject(dictoptions);
 
+		init_data.dict_options = dictoptions;
+		init_data.dict.id = InvalidOid;
+		init_data.dict.xmin = InvalidTransactionId;
+		init_data.dict.xmax = InvalidTransactionId;
+		ItemPointerSetInvalid(&init_data.dict.tid);
+
 		/*
 		 * Call the init method and see if it complains.  We don't worry about
 		 * it leaking memory, since our command will soon be over anyway.
 		 */
-		(void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
+		(void) OidFunctionCall1(initmethod, PointerGetDatum(&init_data));
 	}
 
 	ReleaseSysCache(tup);
diff --git a/src/backend/snowball/dict_snowball.c b/src/backend/snowball/dict_snowball.c
index 5166738310..f30f29865c 100644
--- a/src/backend/snowball/dict_snowball.c
+++ b/src/backend/snowball/dict_snowball.c
@@ -201,14 +201,14 @@ locate_stem_module(DictSnowball *d, const char *lang)
 Datum
 dsnowball_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictSnowball *d;
 	bool		stoploaded = false;
 	ListCell   *l;
 
 	d = (DictSnowball *) palloc0(sizeof(DictSnowball));
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/tsearch/dict_ispell.c b/src/backend/tsearch/dict_ispell.c
index 8b05a477f1..fc9a96abca 100644
--- a/src/backend/tsearch/dict_ispell.c
+++ b/src/backend/tsearch/dict_ispell.c
@@ -29,7 +29,7 @@ typedef struct
 Datum
 dispell_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictISpell *d;
 	bool		affloaded = false,
 				dictloaded = false,
@@ -40,7 +40,7 @@ dispell_init(PG_FUNCTION_ARGS)
 
 	NIStartBuild(&(d->obj));
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/tsearch/dict_simple.c b/src/backend/tsearch/dict_simple.c
index 2f62ef00c8..c92744641b 100644
--- a/src/backend/tsearch/dict_simple.c
+++ b/src/backend/tsearch/dict_simple.c
@@ -29,7 +29,7 @@ typedef struct
 Datum
 dsimple_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictSimple *d = (DictSimple *) palloc0(sizeof(DictSimple));
 	bool		stoploaded = false,
 				acceptloaded = false;
@@ -37,7 +37,7 @@ dsimple_init(PG_FUNCTION_ARGS)
 
 	d->accept = true;			/* default */
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/tsearch/dict_synonym.c b/src/backend/tsearch/dict_synonym.c
index b6226df940..d3f5f0da3f 100644
--- a/src/backend/tsearch/dict_synonym.c
+++ b/src/backend/tsearch/dict_synonym.c
@@ -91,7 +91,7 @@ compareSyn(const void *a, const void *b)
 Datum
 dsynonym_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictSyn    *d;
 	ListCell   *l;
 	char	   *filename = NULL;
@@ -104,7 +104,7 @@ dsynonym_init(PG_FUNCTION_ARGS)
 	char	   *line = NULL;
 	uint16		flags = 0;
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/tsearch/dict_thesaurus.c b/src/backend/tsearch/dict_thesaurus.c
index 75f8deef6a..8962e252e0 100644
--- a/src/backend/tsearch/dict_thesaurus.c
+++ b/src/backend/tsearch/dict_thesaurus.c
@@ -604,7 +604,7 @@ compileTheSubstitute(DictThesaurus *d)
 Datum
 thesaurus_init(PG_FUNCTION_ARGS)
 {
-	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
+	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictThesaurus *d;
 	char	   *subdictname = NULL;
 	bool		fileloaded = false;
@@ -612,7 +612,7 @@ thesaurus_init(PG_FUNCTION_ARGS)
 
 	d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
 
-	foreach(l, dictoptions)
+	foreach(l, init_data->dict_options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index f7fc6c1558..a90ee5c115 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -39,6 +39,7 @@
 #include "catalog/pg_ts_template.h"
 #include "commands/defrem.h"
 #include "tsearch/ts_cache.h"
+#include "tsearch/ts_public.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
 #include "utils/fmgroids.h"
@@ -75,6 +76,7 @@ static TSConfigCacheEntry *lastUsedConfig = NULL;
 char	   *TSCurrentConfig = NULL;
 
 static Oid	TSCurrentConfigCache = InvalidOid;
+static bool has_invalid_dictionary = false;
 
 
 /*
@@ -98,7 +100,12 @@ InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
 
 	hash_seq_init(&status, hash);
 	while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (cacheid == TSDICTOID)
+			has_invalid_dictionary = true;
+
 		entry->isvalid = false;
+	}
 
 	/* Also invalidate the current-config cache if it's pg_ts_config */
 	if (hash == TSConfigCacheHash)
@@ -312,11 +319,15 @@ lookup_ts_dictionary_cache(Oid dictId)
 		MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
 		entry->dictId = dictId;
 		entry->dictCtx = saveCtx;
+		entry->dict_xmin = HeapTupleHeaderGetRawXmin(tpdict->t_data);
+		entry->dict_xmax = HeapTupleHeaderGetRawXmax(tpdict->t_data);
+		entry->dict_tid = tpdict->t_self;
 
 		entry->lexizeOid = template->tmpllexize;
 
 		if (OidIsValid(template->tmplinit))
 		{
+			DictInitData init_data;
 			List	   *dictoptions;
 			Datum		opt;
 			bool		isnull;
@@ -336,9 +347,15 @@ lookup_ts_dictionary_cache(Oid dictId)
 			else
 				dictoptions = deserialize_deflist(opt);
 
+			init_data.dict_options = dictoptions;
+			init_data.dict.id = dictId;
+			init_data.dict.xmin = entry->dict_xmin;
+			init_data.dict.xmax = entry->dict_xmax;
+			init_data.dict.tid = entry->dict_tid;
+
 			entry->dictData =
 				DatumGetPointer(OidFunctionCall1(template->tmplinit,
-												 PointerGetDatum(dictoptions)));
+												 PointerGetDatum(&init_data)));
 
 			MemoryContextSwitchTo(oldcontext);
 		}
diff --git a/src/include/tsearch/ts_cache.h b/src/include/tsearch/ts_cache.h
index 77e325d101..cf3d870537 100644
--- a/src/include/tsearch/ts_cache.h
+++ b/src/include/tsearch/ts_cache.h
@@ -54,6 +54,10 @@ typedef struct TSDictionaryCacheEntry
 	Oid			dictId;
 	bool		isvalid;
 
+	TransactionId dict_xmin;		/* XMIN of the dictionary's tuple */
+	TransactionId dict_xmax;		/* XMAX of the dictionary's tuple */
+	ItemPointerData dict_tid;		/* TID of the dictionary's tuple */
+
 	/* most frequent fmgr call */
 	Oid			lexizeOid;
 	FmgrInfo	lexize;
diff --git a/src/include/tsearch/ts_public.h b/src/include/tsearch/ts_public.h
index b325fa122c..e169450de3 100644
--- a/src/include/tsearch/ts_public.h
+++ b/src/include/tsearch/ts_public.h
@@ -13,6 +13,8 @@
 #ifndef _PG_TS_PUBLIC_H_
 #define _PG_TS_PUBLIC_H_
 
+#include "nodes/pg_list.h"
+#include "storage/itemptr.h"
 #include "tsearch/ts_type.h"
 
 /*
@@ -81,10 +83,70 @@ extern void readstoplist(const char *fname, StopList *s,
 extern bool searchstoplist(StopList *s, char *key);
 
 /*
- * Interface with dictionaries
+ * API for text search dictionaries.
+ *
+ * API functions to handle a text search dictionary are defined by a text search
+ * template.  Currently an existing template cannot be altered in order to
+ * define another functions.  API consists of the following functions:
+ *	- init function - optional function which initializes internal structures of
+ *	  the dictionary.  It accepts DictInitData structure as an argument and must
+ *	  return a custom palloc'd structure which stores content of the processed
+ *	  dictionary and is used in lexize function.
+ *	- lexize function - normalizes a single word (token) using specific
+ *	  dictionary.  It must return a palloc'd array of TSLexeme the last entry of
+ *	  which is the terminating entry and accepts the following arguments:
+ *	  - dictData - pointer to a custom structure returned by init function or
+ *		NULL if init function wasn't defined by the template.
+ *	  - token - string which represents a token to normalize, isn't
+ *		null-terminated.
+ *	  - length - length of token.
+ *	  - dictState - pointer to a DictSubState structure which stores current
+ *		state of a set of tokens processing and allows to normalize phrases.
+ */
+
+/*
+ * A preprocessed dictionary can be stored in shared memory using DSM.  Does
+ * the dictionary want it decides init function.  A DSM segment is released if
+ * the dictionary was altered or droppped.  But still there is a situation when
+ * we haven't a way to prevent a segment leaking.  It may happen if the
+ * dictionary was dropped, some backend used the dictionary before dropping, the
+ * backend will hold its DSM segment till disconnecting or calling
+ * lookup_ts_dictionary_cache(), where invalid segment is unpinned.
+ *
+ * DictPointerData is a structure to search a dictionary's DSM segment.  We
+ * need xmin, xmax and tid to be sure that the content in the DSM segment still
+ * valid.
+ */
+typedef struct
+{
+	Oid			id;				/* OID of dictionary which is processed */
+	TransactionId xmin;			/* XMIN of the dictionary's tuple */
+	TransactionId xmax;			/* XMAX of the dictionary's tuple */
+	ItemPointerData tid;		/* TID of the dictionary's tuple */
+} DictPointerData;
+
+/*
+ * API structure for a dictionary initialization.  It is passed as an argument
+ * to a template's init function.
  */
+typedef struct
+{
+	/*
+	 * A dictionary option list for a template's init method. Should go first
+	 * for backward compatibility.
+	 */
+	List	   *dict_options;
+	/*
+	 * A dictionary information used to allocate, search and release its DSM
+	 * segment.
+	 */
+	DictPointerData dict;
+} DictInitData;
 
-/* return struct for any lexize function */
+/*
+ * Return struct for any lexize function.  They are combined into an array, the
+ * last entry is the terminating entry.
+ */
 typedef struct
 {
 	/*----------
@@ -108,7 +170,8 @@ typedef struct
 
 	uint16		flags;			/* See flag bits below */
 
-	char	   *lexeme;			/* C string */
+	char	   *lexeme;			/* C string or NULL if it is a terminating
+								 * entry */
 } TSLexeme;
 
 /* Flag bits that can appear in TSLexeme.flags */
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 93a71adc5d..6d2868ccb5 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -40,6 +40,7 @@
 #include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
 #include "tsearch/ts_cache.h"
+#include "tsearch/ts_shared.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -518,6 +519,7 @@ RemoveTSDictionaryById(Oid dictId)
 {
 	Relation	relation;
 	HeapTuple	tup;
+	DictPointerData dict;
 
 	relation = heap_open(TSDictionaryRelationId, RowExclusiveLock);
 
@@ -529,6 +531,18 @@ RemoveTSDictionaryById(Oid dictId)
 
 	CatalogTupleDelete(relation, &tup->t_self);
 
+	/*
+	 * We need to release the dictionary's DSM segment.  The segment still may
+	 * leak.  It may happen if some backend used the dictionary before dropping,
+	 * the backend will hold its DSM segment till disconnecting or calling
+	 * lookup_ts_dictionary_cache().
+	 */
+	dict.id = dictId;
+	dict.xmin = HeapTupleHeaderGetRawXmin(tup->t_data);
+	dict.xmax = HeapTupleHeaderGetRawXmax(tup->t_data);
+	dict.tid = tup->t_self;
+	ts_dict_shmem_release(&dict, true);
+
 	ReleaseSysCache(tup);
 
 	heap_close(relation, RowExclusiveLock);
@@ -552,6 +566,7 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 	bool		repl_null[Natts_pg_ts_dict];
 	bool		repl_repl[Natts_pg_ts_dict];
 	ObjectAddress address;
+	DictPointerData dict;
 
 	dictId = get_ts_dict_oid(stmt->dictname, false);
 
@@ -638,6 +653,18 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 
 	ObjectAddressSet(address, TSDictionaryRelationId, dictId);
 
+	/*
+	 * We need to release the dictionary's DSM segment.  The segment isn't valid
+	 * anymor.  The segment still may leak.  It may happen if some backend used
+	 * the dictionary before dropping, the backend will hold its DSM segment
+	 * till disconnecting or calling lookup_ts_dictionary_cache().
+	 */
+	dict.id = dictId;
+	dict.xmin = HeapTupleHeaderGetRawXmin(tup->t_data);
+	dict.xmax = HeapTupleHeaderGetRawXmax(tup->t_data);
+	dict.tid = tup->t_self;
+	ts_dict_shmem_release(&dict, true);
+
 	/*
 	 * NOTE: because we only support altering the options, not the template,
 	 * there is no need to update dependencies.  This might have to change if
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2849e47d99..a1af2b2692 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -44,6 +44,7 @@
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
+#include "tsearch/ts_shared.h"
 #include "utils/snapmgr.h"
 
 
@@ -148,6 +149,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, TsearchShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -268,6 +270,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	SyncScanShmemInit();
 	AsyncShmemInit();
 
+	/*
+	 * Set up shared memory to tsearch
+	 */
+	TsearchShmemInit();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile
index 62d8bb3254..0b25c20fb0 100644
--- a/src/backend/tsearch/Makefile
+++ b/src/backend/tsearch/Makefile
@@ -26,7 +26,7 @@ DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES))
 OBJS = ts_locale.o ts_parse.o wparser.o wparser_def.o dict.o \
 	dict_simple.o dict_synonym.o dict_thesaurus.o \
 	dict_ispell.o regis.o spell.o \
-	to_tsany.o ts_selfuncs.o ts_typanalyze.o ts_utils.o
+	to_tsany.o ts_selfuncs.o ts_shared.o ts_typanalyze.o ts_utils.o
 
 include $(top_srcdir)/src/backend/common.mk
 
diff --git a/src/backend/tsearch/ts_shared.c b/src/backend/tsearch/ts_shared.c
new file mode 100644
index 0000000000..748ab5a782
--- /dev/null
+++ b/src/backend/tsearch/ts_shared.c
@@ -0,0 +1,377 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.c
+ *	  tsearch shared memory management
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/tsearch/ts_shared.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "lib/dshash.h"
+#include "miscadmin.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "tsearch/ts_shared.h"
+#include "utils/hashutils.h"
+#include "utils/memutils.h"
+
+
+/*
+ * Hash table entries key.
+ */
+typedef struct
+{
+	Oid			db_id;
+	DictPointerData dict;
+} TsearchDictKey;
+
+/*
+ * Hash table entries representing shared dictionaries.
+ */
+typedef struct
+{
+	TsearchDictKey key;
+	dsm_handle	dict_dsm;
+
+	/*
+	 * We need a flag that the DSM segment is pinned/unpinned.  Otherwise we can
+	 * face double dsm_unpin_segment().
+	 */
+	bool		segment_ispinned;
+} TsearchDictEntry;
+
+static dshash_table *dict_table = NULL;
+
+/*
+ * Information about the main shmem segment, used to coordinate
+ * access to the hash table and dictionaries.
+ */
+typedef struct
+{
+	dsa_handle	area;
+	dshash_table_handle dict_table_handle;
+
+	LWLock		lock;
+} TsearchCtlData;
+
+static TsearchCtlData *tsearch_ctl;
+
+static int tsearch_dict_cmp(const void *a, const void *b, size_t size,
+							void *arg);
+static uint32 tsearch_dict_hash(const void *a, size_t size, void *arg);
+static void init_dict_table(void);
+
+/* Parameters for dict_table */
+static const dshash_parameters dict_table_params ={
+	sizeof(TsearchDictKey),
+	sizeof(TsearchDictEntry),
+	tsearch_dict_cmp,
+	tsearch_dict_hash,
+	LWTRANCHE_TSEARCH_TABLE
+};
+
+/*
+ * Build the dictionary using allocate_cb callback.
+ *
+ * Firstly try to find the dictionary in shared hash table. If it was built by
+ * someone earlier just return its location in DSM.
+ *
+ * init_data: an argument used within a template's init method.
+ * allocate_cb: function to build the dictionary, if it wasn't found in DSM.
+ *
+ * Returns address in the dynamic shared memory segment or in backend memory.
+ */
+void *
+ts_dict_shmem_location(DictInitData *init_data,
+					   ts_dict_build_callback allocate_cb)
+{
+	TsearchDictKey key;
+	TsearchDictEntry *entry;
+	bool		found;
+	dsm_segment *seg;
+	void	   *dict,
+			   *dict_location;
+	Size		dict_size;
+
+	init_dict_table();
+
+	/*
+	 * Build the dictionary in backend's memory if dictid is invalid (it may
+	 * happen if the dicionary's init method was called within
+	 * verify_dictoptions()).
+	 */
+	if (!OidIsValid(init_data->dict.id))
+	{
+		dict = allocate_cb(init_data->dict_options, &dict_size);
+
+		return dict;
+	}
+
+	/* Set up key for hashtable search */
+	key.db_id = MyDatabaseId;
+	key.dict = init_data->dict;
+
+	/* Try to find an entry in the hash table */
+	entry = (TsearchDictEntry *) dshash_find(dict_table, &key, false);
+
+	if (entry)
+	{
+		seg = dsm_find_mapping(entry->dict_dsm);
+		if (!seg)
+		{
+			seg = dsm_attach(entry->dict_dsm);
+			/* Remain attached until end of session */
+			dsm_pin_mapping(seg);
+		}
+
+		dshash_release_lock(dict_table, entry);
+
+		return dsm_segment_address(seg);
+	}
+
+	/* Dictionary haven't been loaded into memory yet */
+	entry = (TsearchDictEntry *) dshash_find_or_insert(dict_table, &key,
+													   &found);
+
+	if (found)
+	{
+		/*
+		 * Someone concurrently inserted a dictionary entry since the first time
+		 * we checked.
+		 */
+		seg = dsm_attach(entry->dict_dsm);
+
+		/* Remain attached until end of session */
+		dsm_pin_mapping(seg);
+
+		dshash_release_lock(dict_table, entry);
+
+		return dsm_segment_address(seg);
+	}
+
+	/* Build the dictionary */
+	dict = allocate_cb(init_data->dict_options, &dict_size);
+
+	/* At least, allocate a DSM segment for the compiled dictionary */
+	seg = dsm_create(dict_size, 0);
+	dict_location = dsm_segment_address(seg);
+	memcpy(dict_location, dict, dict_size);
+
+	pfree(dict);
+
+	entry->key = key;
+	entry->dict_dsm = dsm_segment_handle(seg);
+	entry->segment_ispinned = true;
+
+	/* Remain attached until end of postmaster */
+	dsm_pin_segment(seg);
+	/* Remain attached until end of session */
+	dsm_pin_mapping(seg);
+
+	dshash_release_lock(dict_table, entry);
+
+	return dsm_segment_address(seg);
+}
+
+/*
+ * Release memory occupied by the dictionary.  Function just unpins DSM mapping.
+ * If nobody else hasn't mapping to this DSM or the dictionary was dropped or
+ * altered then unpin the DSM segment.
+ *
+ * dict: key to search the dictionary's DSM segment.
+ * unpin_segment: true if we need to unpin the segment in case if the dictionary
+ *				  was dropped or altered.
+ */
+void
+ts_dict_shmem_release(DictPointerData *dict, bool unpin_segment)
+{
+	TsearchDictKey key;
+	TsearchDictEntry *entry;
+
+	/*
+	 * If we didn't attach to a hash table then do nothing.
+	 */
+	if (!dict_table && !unpin_segment)
+		return;
+	/*
+	 * But if we need to unpin the DSM segment to get of rid of the segment when
+	 * the last interested process disconnects we need the hash table to find
+	 * the dictionary's entry.
+	 */
+	else if (unpin_segment)
+		init_dict_table();
+
+	/* Set up key for hashtable search */
+	key.db_id = MyDatabaseId;
+	key.dict = *dict;
+
+	/* Try to find an entry in the hash table */
+	entry = (TsearchDictEntry *) dshash_find(dict_table, &key, true);
+
+	if (entry)
+	{
+		dsm_segment *seg;
+
+		seg = dsm_find_mapping(entry->dict_dsm);
+
+		if (seg)
+		{
+			dsm_unpin_mapping(seg);
+			dsm_detach(seg);
+		}
+
+		if (unpin_segment && entry->segment_ispinned)
+		{
+			dsm_unpin_segment(entry->dict_dsm);
+			entry->segment_ispinned = false;
+		}
+
+		dshash_release_lock(dict_table, entry);
+	}
+}
+
+/*
+ * Allocate and initialize tsearch-related shared memory.
+ */
+void
+TsearchShmemInit(void)
+{
+	bool		found;
+
+	tsearch_ctl = (TsearchCtlData *)
+		ShmemInitStruct("Full Text Search Ctl", sizeof(TsearchCtlData), &found);
+
+	if (!found)
+	{
+		LWLockRegisterTranche(LWTRANCHE_TSEARCH_DSA, "tsearch_dsa");
+		LWLockRegisterTranche(LWTRANCHE_TSEARCH_TABLE, "tsearch_table");
+
+		LWLockInitialize(&tsearch_ctl->lock, LWTRANCHE_TSEARCH_DSA);
+
+		tsearch_ctl->area = DSM_HANDLE_INVALID;
+		tsearch_ctl->dict_table_handle = InvalidDsaPointer;
+	}
+}
+
+/*
+ * Report shared memory space needed by TsearchShmemInit.
+ */
+Size
+TsearchShmemSize(void)
+{
+	Size		size = 0;
+
+	/* size of service structure */
+	size = add_size(size, MAXALIGN(sizeof(TsearchCtlData)));
+
+	return size;
+}
+
+/*
+ * A comparator function for TsearchDictKey.
+ *
+ * Returns 1 if keys are equal.
+ */
+static int
+tsearch_dict_cmp(const void *a, const void *b, size_t size, void *arg)
+{
+	TsearchDictKey *k1 = (TsearchDictKey *) a;
+	TsearchDictKey *k2 = (TsearchDictKey *) b;
+
+	if (k1->db_id == k2->db_id && k1->dict.id == k2->dict.id &&
+		k1->dict.xmin == k2->dict.xmin && k1->dict.xmax == k2->dict.xmax &&
+		ItemPointerEquals(&k1->dict.tid, &k2->dict.tid))
+		return 0;
+	else
+		return 1;
+}
+
+/*
+ * A hash function for TsearchDictKey.
+ */
+static uint32
+tsearch_dict_hash(const void *a, size_t size, void *arg)
+{
+	TsearchDictKey *k = (TsearchDictKey *) a;
+	uint32		s;
+
+	s = hash_combine(0, hash_uint32(k->db_id));
+	s = hash_combine(s, hash_uint32(k->dict.id));
+	s = hash_combine(s, hash_uint32(k->dict.xmin));
+	s = hash_combine(s, hash_uint32(k->dict.xmax));
+	s = hash_combine(s,
+					 hash_uint32(BlockIdGetBlockNumber(&k->dict.tid.ip_blkid)));
+	s = hash_combine(s, hash_uint32(k->dict.tid.ip_posid));
+
+	return s;
+}
+
+/*
+ * Initialize hash table located in DSM.
+ *
+ * The hash table should be created and initialized if it doesn't exist yet.
+ */
+static void
+init_dict_table(void)
+{
+	MemoryContext old_context;
+	dsa_area   *dsa;
+
+	/* Exit if hash table was initialized alread */
+	if (dict_table)
+		return;
+
+	old_context = MemoryContextSwitchTo(TopMemoryContext);
+
+recheck_table:
+	LWLockAcquire(&tsearch_ctl->lock, LW_SHARED);
+
+	/* Hash table have been created already by someone */
+	if (DsaPointerIsValid(tsearch_ctl->dict_table_handle))
+	{
+		Assert(tsearch_ctl->area != DSM_HANDLE_INVALID);
+
+		dsa = dsa_attach(tsearch_ctl->area);
+
+		dict_table = dshash_attach(dsa,
+								   &dict_table_params,
+								   tsearch_ctl->dict_table_handle,
+								   NULL);
+	}
+	else
+	{
+		/* Try to get exclusive lock */
+		LWLockRelease(&tsearch_ctl->lock);
+		if (!LWLockAcquireOrWait(&tsearch_ctl->lock, LW_EXCLUSIVE))
+		{
+			/*
+			 * The lock was released by another backend and other backend
+			 * has concurrently created the hash table already.
+			 */
+			goto recheck_table;
+		}
+
+		dsa = dsa_create(LWTRANCHE_TSEARCH_DSA);
+		tsearch_ctl->area = dsa_get_handle(dsa);
+
+		dict_table = dshash_create(dsa, &dict_table_params, NULL);
+		tsearch_ctl->dict_table_handle = dshash_get_hash_table_handle(dict_table);
+
+		/* Remain attached until end of postmaster */
+		dsa_pin(dsa);
+	}
+
+	LWLockRelease(&tsearch_ctl->lock);
+
+	/* Remain attached until end of session */
+	dsa_pin_mapping(dsa);
+
+	MemoryContextSwitchTo(old_context);
+}
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index a90ee5c115..7935667eba 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -40,6 +40,7 @@
 #include "commands/defrem.h"
 #include "tsearch/ts_cache.h"
 #include "tsearch/ts_public.h"
+#include "tsearch/ts_shared.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
 #include "utils/fmgroids.h"
@@ -88,6 +89,10 @@ static bool has_invalid_dictionary = false;
  * doesn't seem worth the trouble to determine that; we just flush all the
  * entries of the related hash table.
  *
+ * We set has_invalid_dictionary to true to unpin all used segments later on
+ * a first text search function usage.  It isn't safe to call
+ * ts_dict_shmem_release() here since it may call kernel functions.
+ *
  * We can use the same function for all TS caches by passing the hash
  * table address as the "arg".
  */
@@ -112,6 +117,33 @@ InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
 		TSCurrentConfigCache = InvalidOid;
 }
 
+/*
+ * Unpin shared segments of all dictionary entries.
+ */
+static void
+do_ts_dict_shmem_release(void)
+{
+	HASH_SEQ_STATUS status;
+	TSDictionaryCacheEntry *entry;
+
+	if (!has_invalid_dictionary)
+		return;
+
+	hash_seq_init(&status, TSDictionaryCacheHash);
+	while ((entry = (TSDictionaryCacheEntry *) hash_seq_search(&status)) != NULL)
+	{
+		DictPointerData dict_ptr;
+
+		dict_ptr.id = entry->dictId;
+		dict_ptr.xmin = entry->dict_xmin;
+		dict_ptr.xmax = entry->dict_xmax;
+		dict_ptr.tid = entry->dict_tid;
+		ts_dict_shmem_release(&dict_ptr, false);
+	}
+
+	has_invalid_dictionary = false;
+}
+
 /*
  * Fetch parser cache entry
  */
@@ -260,6 +292,13 @@ lookup_ts_dictionary_cache(Oid dictId)
 		Form_pg_ts_template template;
 		MemoryContext saveCtx;
 
+		/*
+		 * It is possible that some invalid entries hold a DSM mapping and we
+		 * need to unpin it to avoid memory leaking.  We will unpin segments of
+		 * all other invalid dictionaries.
+		 */
+		do_ts_dict_shmem_release();
+
 		tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
 		if (!HeapTupleIsValid(tpdict))
 			elog(ERROR, "cache lookup failed for text search dictionary %u",
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 96c7732006..49a3319a11 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -219,6 +219,8 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SHARED_TUPLESTORE,
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
+	LWTRANCHE_TSEARCH_DSA,
+	LWTRANCHE_TSEARCH_TABLE,
 	LWTRANCHE_FIRST_USER_DEFINED
 }			BuiltinTrancheIds;
 
diff --git a/src/include/tsearch/ts_shared.h b/src/include/tsearch/ts_shared.h
new file mode 100644
index 0000000000..4339dc6e1f
--- /dev/null
+++ b/src/include/tsearch/ts_shared.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.h
+ *	  tsearch shared memory management
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/tsearch/ts_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TS_SHARED_H
+#define TS_SHARED_H
+
+#include "tsearch/ts_public.h"
+
+typedef void *(*ts_dict_build_callback) (List *dictoptions, Size *size);
+
+extern void *ts_dict_shmem_location(DictInitData *init_data,
+									ts_dict_build_callback allocate_cb);
+extern void ts_dict_shmem_release(DictPointerData *dict, bool unpin_segment);
+
+extern void TsearchShmemInit(void);
+extern Size TsearchShmemSize(void);
+
+#endif							/* TS_SHARED_H */
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade767..0f172eda04 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3110,6 +3110,21 @@ CREATE TEXT SEARCH DICTIONARY english_stem (
 
   </sect2>
 
+  <sect2 id="textsearch-shared-dictionaries">
+   <title>Dictionaries in Shared Memory</title>
+
+   <para>
+    Some dictionaries, especially <application>Ispell</application>, consumes
+    a significant amount of memory, in some cases tens of megabytes.  Most of
+    them store the data in text files, and building the in-memory structure is
+    both CPU and time-consuming.  Instead of doing this in each backend when
+    it needs a dictionary for the first time, the compiled dictionary may be
+    stored in dynamic shared memory so that it may be reused by other backends.
+    Currently only <application>Ispell</application> supports loading into
+    dynamic shared memory.
+   </para>
+  </sect2>
+
  </sect1>
 
  <sect1 id="textsearch-configuration">
diff --git a/src/backend/tsearch/dict_ispell.c b/src/backend/tsearch/dict_ispell.c
index fc9a96abca..3c9dd78c56 100644
--- a/src/backend/tsearch/dict_ispell.c
+++ b/src/backend/tsearch/dict_ispell.c
@@ -5,6 +5,15 @@
  *
  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  *
+ * By default all Ispell dictionaries are stored in DSM. But if the amount
+ * of memory exceeds max_shared_dictionaries_size, then the dictionary will be
+ * allocated in private backend memory (in dictCtx context).
+ *
+ * All necessary data are built within dispell_build() function. But
+ * structures for regular expressions are compiled on first demand and
+ * stored using AffixReg array. It is because regex_t and Regis cannot be
+ * stored in shared memory easily.
+ *
  *
  * IDENTIFICATION
  *	  src/backend/tsearch/dict_ispell.c
@@ -14,8 +23,10 @@
 #include "postgres.h"
 
 #include "commands/defrem.h"
+#include "storage/dsm.h"
 #include "tsearch/dicts/spell.h"
 #include "tsearch/ts_locale.h"
+#include "tsearch/ts_shared.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 
@@ -26,54 +37,126 @@ typedef struct
 	IspellDict	obj;
 } DictISpell;
 
+static void parse_dictoptions(List *dictoptions,
+							  char **dictfile, char **afffile, char **stopfile);
+static void *dispell_build(List *dictoptions, Size *size);
+
 Datum
 dispell_init(PG_FUNCTION_ARGS)
 {
 	DictInitData *init_data = (DictInitData *) PG_GETARG_POINTER(0);
 	DictISpell *d;
-	bool		affloaded = false,
-				dictloaded = false,
-				stoploaded = false;
-	ListCell   *l;
+	void	   *dict_location;
+	char	   *stopfile;
 
 	d = (DictISpell *) palloc0(sizeof(DictISpell));
 
-	NIStartBuild(&(d->obj));
+	parse_dictoptions(init_data->dict_options, NULL, NULL, &stopfile);
+
+	if (stopfile)
+		readstoplist(stopfile, &(d->stoplist), lowerstr);
+
+	dict_location = ts_dict_shmem_location(init_data, dispell_build);
+	Assert(dict_location);
+
+	d->obj.dict = (IspellDictData *) dict_location;
+	d->obj.reg = (AffixReg *) palloc0(d->obj.dict->nAffix *
+									  sizeof(AffixReg));
+	/* Current memory context is dictionary's private memory context */
+	d->obj.dictCtx = CurrentMemoryContext;
+
+	PG_RETURN_POINTER(d);
+}
+
+Datum
+dispell_lexize(PG_FUNCTION_ARGS)
+{
+	DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
+	char	   *in = (char *) PG_GETARG_POINTER(1);
+	int32		len = PG_GETARG_INT32(2);
+	char	   *txt;
+	TSLexeme   *res;
+	TSLexeme   *ptr,
+			   *cptr;
+
+	if (len <= 0)
+		PG_RETURN_POINTER(NULL);
+
+	txt = lowerstr_with_len(in, len);
+	res = NINormalizeWord(&(d->obj), txt);
+
+	if (res == NULL)
+		PG_RETURN_POINTER(NULL);
+
+	cptr = res;
+	for (ptr = cptr; ptr->lexeme; ptr++)
+	{
+		if (searchstoplist(&(d->stoplist), ptr->lexeme))
+		{
+			pfree(ptr->lexeme);
+			ptr->lexeme = NULL;
+		}
+		else
+		{
+			if (cptr != ptr)
+				memcpy(cptr, ptr, sizeof(TSLexeme));
+			cptr++;
+		}
+	}
+	cptr->lexeme = NULL;
+
+	PG_RETURN_POINTER(res);
+}
+
+static void
+parse_dictoptions(List *dictoptions, char **dictfile, char **afffile,
+				  char **stopfile)
+{
+	ListCell   *l;
 
-	foreach(l, init_data->dict_options)
+	if (dictfile)
+		*dictfile = NULL;
+	if (afffile)
+		*afffile = NULL;
+	if (stopfile)
+		*stopfile = NULL;
+
+	foreach(l, dictoptions)
 	{
 		DefElem    *defel = (DefElem *) lfirst(l);
 
 		if (strcmp(defel->defname, "dictfile") == 0)
 		{
-			if (dictloaded)
+			if (!dictfile)
+				continue;
+
+			if (*dictfile)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("multiple DictFile parameters")));
-			NIImportDictionary(&(d->obj),
-							   get_tsearch_config_filename(defGetString(defel),
-														   "dict"));
-			dictloaded = true;
+			*dictfile = get_tsearch_config_filename(defGetString(defel), "dict");
 		}
 		else if (strcmp(defel->defname, "afffile") == 0)
 		{
-			if (affloaded)
+			if (!afffile)
+				continue;
+
+			if (*afffile)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("multiple AffFile parameters")));
-			NIImportAffixes(&(d->obj),
-							get_tsearch_config_filename(defGetString(defel),
-														"affix"));
-			affloaded = true;
+			*afffile = get_tsearch_config_filename(defGetString(defel), "affix");
 		}
 		else if (strcmp(defel->defname, "stopwords") == 0)
 		{
-			if (stoploaded)
+			if (!stopfile)
+				continue;
+
+			if (*stopfile)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("multiple StopWords parameters")));
-			readstoplist(defGetString(defel), &(d->stoplist), lowerstr);
-			stoploaded = true;
+			*stopfile = defGetString(defel);
 		}
 		else
 		{
@@ -83,66 +166,52 @@ dispell_init(PG_FUNCTION_ARGS)
 							defel->defname)));
 		}
 	}
+}
 
-	if (affloaded && dictloaded)
-	{
-		NISortDictionary(&(d->obj));
-		NISortAffixes(&(d->obj));
-	}
-	else if (!affloaded)
+/*
+ * Build the dictionary.
+ *
+ * Result is palloc'ed.
+ */
+static void *
+dispell_build(List *dictoptions, Size *size)
+{
+	IspellDictBuild build;
+	char	   *dictfile,
+			   *afffile;
+
+	parse_dictoptions(dictoptions, &dictfile, &afffile, NULL);
+
+	if (!afffile)
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("missing AffFile parameter")));
 	}
-	else
+	else if (!dictfile)
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("missing DictFile parameter")));
 	}
 
-	NIFinishBuild(&(d->obj));
+	MemSet(&build, 0, sizeof(build));
+	NIStartBuild(&build);
 
-	PG_RETURN_POINTER(d);
-}
+	/* Read files */
+	NIImportDictionary(&build, dictfile);
+	NIImportAffixes(&build, afffile);
 
-Datum
-dispell_lexize(PG_FUNCTION_ARGS)
-{
-	DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
-	char	   *in = (char *) PG_GETARG_POINTER(1);
-	int32		len = PG_GETARG_INT32(2);
-	char	   *txt;
-	TSLexeme   *res;
-	TSLexeme   *ptr,
-			   *cptr;
+	/* Build persistent data to use by backends */
+	NISortDictionary(&build);
+	NISortAffixes(&build);
 
-	if (len <= 0)
-		PG_RETURN_POINTER(NULL);
+	NICopyData(&build);
 
-	txt = lowerstr_with_len(in, len);
-	res = NINormalizeWord(&(d->obj), txt);
-
-	if (res == NULL)
-		PG_RETURN_POINTER(NULL);
+	/* Release temporary data */
+	NIFinishBuild(&build);
 
-	cptr = res;
-	for (ptr = cptr; ptr->lexeme; ptr++)
-	{
-		if (searchstoplist(&(d->stoplist), ptr->lexeme))
-		{
-			pfree(ptr->lexeme);
-			ptr->lexeme = NULL;
-		}
-		else
-		{
-			if (cptr != ptr)
-				memcpy(cptr, ptr, sizeof(TSLexeme));
-			cptr++;
-		}
-	}
-	cptr->lexeme = NULL;
-
-	PG_RETURN_POINTER(res);
+	/* Return the buffer and its size */
+	*size = build.dict_size;
+	return build.dict;
 }
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index eb8416ce7f..123fba7a11 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -23,33 +23,35 @@
  * Compilation of a dictionary
  * ---------------------------
  *
- * A compiled dictionary is stored in the IspellDict structure. Compilation of
- * a dictionary is divided into the several steps:
+ * A compiled dictionary is stored in the following structures:
+ *	- IspellDictBuild - stores temporary data and IspellDictData
+ *	- IspellDictData - stores permanent data used within NINormalizeWord()
+ * Compilation of the dictionary is divided into the several steps:
  *	- NIImportDictionary() - stores each word of a .dict file in the
  *	  temporary Spell field.
- *	- NIImportAffixes() - stores affix rules of an .affix file in the
- *	  Affix field (not temporary) if an .affix file has the Ispell format.
+ *	- NIImportAffixes() - stores affix rules of an .affix file in the temporary
+ *	  Affix field if an .affix file has the Ispell format.
  *	  -> NIImportOOAffixes() - stores affix rules if an .affix file has the
  *		 Hunspell format. The AffixData field is initialized if AF parameter
  *		 is defined.
  *	- NISortDictionary() - builds a prefix tree (Trie) from the words list
- *	  and stores it in the Dictionary field. The words list is got from the
+ *	  and stores it in the DictNodes field. The words list is got from the
  *	  Spell field. The AffixData field is initialized if AF parameter is not
  *	  defined.
  *	- NISortAffixes():
  *	  - builds a list of compound affixes from the affix list and stores it
  *		in the CompoundAffix.
  *	  - builds prefix trees (Trie) from the affix list for prefixes and suffixes
- *		and stores them in Suffix and Prefix fields.
+ *		and stores them in SuffixNodes and PrefixNodes fields.
  *	  The affix list is got from the Affix field.
+ * Persistent data of the dictionary is copied within NICopyData().
  *
  * Memory management
  * -----------------
  *
- * The IspellDict structure has the Spell field which is used only in compile
- * time. The Spell field stores a words list. It can take a lot of memory.
- * Therefore when a dictionary is compiled this field is cleared by
- * NIFinishBuild().
+ * The IspellDictBuild structure has the temporary data which is used only in
+ * compile time. It can take a lot of memory. Therefore after compiling the
+ * dictionary this data is cleared by NIFinishBuild().
  *
  * All resources which should cleared by NIFinishBuild() is initialized using
  * tmpalloc() and tmpalloc0().
@@ -73,112 +75,166 @@
  * after the initialization is done.  During initialization,
  * CurrentMemoryContext is the long-lived memory context associated
  * with the dictionary cache entry.  We keep the short-lived stuff
- * in the Conf->buildCxt context.
+ * in the ConfBuild->buildCxt context.
  */
-#define tmpalloc(sz)  MemoryContextAlloc(Conf->buildCxt, (sz))
-#define tmpalloc0(sz)  MemoryContextAllocZero(Conf->buildCxt, (sz))
+#define tmpalloc(sz)  MemoryContextAlloc(ConfBuild->buildCxt, (sz))
+#define tmpalloc0(sz)  MemoryContextAllocZero(ConfBuild->buildCxt, (sz))
 
-#define tmpstrdup(str)	MemoryContextStrdup(Conf->buildCxt, (str))
+#define tmpstrdup(str)	MemoryContextStrdup(ConfBuild->buildCxt, (str))
 
 /*
  * Prepare for constructing an ISpell dictionary.
  *
- * The IspellDict struct is assumed to be zeroed when allocated.
+ * The IspellDictBuild struct is assumed to be zeroed when allocated.
  */
 void
-NIStartBuild(IspellDict *Conf)
+NIStartBuild(IspellDictBuild *ConfBuild)
 {
+	uint32		dict_size;
+
 	/*
 	 * The temp context is a child of CurTransactionContext, so that it will
 	 * go away automatically on error.
 	 */
-	Conf->buildCxt = AllocSetContextCreate(CurTransactionContext,
-										   "Ispell dictionary init context",
-										   ALLOCSET_DEFAULT_SIZES);
+	ConfBuild->buildCxt = AllocSetContextCreate(CurTransactionContext,
+											   "Ispell dictionary init context",
+												ALLOCSET_DEFAULT_SIZES);
+
+	/*
+	 * Allocate buffer for the dictionary in current context not in buildCxt.
+	 */
+	dict_size = MAXALIGN(IspellDictDataHdrSize);
+	ConfBuild->dict = palloc0(dict_size);
+	ConfBuild->dict_size = dict_size;
 }
 
 /*
- * Clean up when dictionary construction is complete.
+ * Copy compiled and persistent data into IspellDictData.
  */
 void
-NIFinishBuild(IspellDict *Conf)
+NICopyData(IspellDictBuild *ConfBuild)
 {
-	/* Release no-longer-needed temp memory */
-	MemoryContextDelete(Conf->buildCxt);
-	/* Just for cleanliness, zero the now-dangling pointers */
-	Conf->buildCxt = NULL;
-	Conf->Spell = NULL;
-	Conf->firstfree = NULL;
-	Conf->CompoundAffixFlags = NULL;
-}
+	IspellDictData *dict;
+	uint32		size;
+	int			i;
+	uint32	   *offsets,
+				offset = 0;
+	SPNode	   *dict_node PG_USED_FOR_ASSERTS_ONLY;
+	AffixNode  *aff_node PG_USED_FOR_ASSERTS_ONLY;
 
+	/*
+	 * Calculate necessary space
+	 */
+	size = ConfBuild->nAffixData * sizeof(uint32);
+	size += ConfBuild->AffixDataEnd;
 
-/*
- * "Compact" palloc: allocate without extra palloc overhead.
- *
- * Since we have no need to free the ispell data items individually, there's
- * not much value in the per-chunk overhead normally consumed by palloc.
- * Getting rid of it is helpful since ispell can allocate a lot of small nodes.
- *
- * We currently pre-zero all data allocated this way, even though some of it
- * doesn't need that.  The cpalloc and cpalloc0 macros are just documentation
- * to indicate which allocations actually require zeroing.
- */
-#define COMPACT_ALLOC_CHUNK 8192	/* amount to get from palloc at once */
-#define COMPACT_MAX_REQ		1024	/* must be < COMPACT_ALLOC_CHUNK */
+	size += ConfBuild->nAffix * sizeof(uint32);
+	size += ConfBuild->AffixSize;
 
-static void *
-compact_palloc0(IspellDict *Conf, size_t size)
-{
-	void	   *result;
+	size += ConfBuild->DictNodes.NodesEnd;
+	size += ConfBuild->PrefixNodes.NodesEnd;
+	size += ConfBuild->SuffixNodes.NodesEnd;
 
-	/* Should only be called during init */
-	Assert(Conf->buildCxt != NULL);
+	size += sizeof(CMPDAffix) * ConfBuild->nCompoundAffix;
 
-	/* No point in this for large chunks */
-	if (size > COMPACT_MAX_REQ)
-		return palloc0(size);
+	/*
+	 * Copy data itself
+	 */
+	ConfBuild->dict_size = IspellDictDataHdrSize + size;
+	ConfBuild->dict = repalloc(ConfBuild->dict, ConfBuild->dict_size);
+
+	dict = ConfBuild->dict;
+
+	/* AffixData */
+	dict->nAffixData = ConfBuild->nAffixData;
+	dict->AffixDataStart = sizeof(uint32) * ConfBuild->nAffixData;
+	memcpy(DictAffixDataOffset(dict), ConfBuild->AffixDataOffset,
+		   sizeof(uint32) * ConfBuild->nAffixData);
+	memcpy(DictAffixData(dict), ConfBuild->AffixData, ConfBuild->AffixDataEnd);
+
+	/* Affix array */
+	dict->nAffix = ConfBuild->nAffix;
+	dict->AffixOffsetStart = dict->AffixDataStart + ConfBuild->AffixDataEnd;
+	dict->AffixStart = dict->AffixOffsetStart + sizeof(uint32) * ConfBuild->nAffix;
+	if (ConfBuild->nAffix > 0)
+	{
+		offsets = (uint32 *) DictAffixOffset(dict);
+		for (i = 0; i < ConfBuild->nAffix; i++)
+		{
+			AFFIX	   *affix;
+			uint32		size = AffixGetSize(ConfBuild->Affix[i]);
 
-	/* Keep everything maxaligned */
-	size = MAXALIGN(size);
+			offsets[i] = offset;
+			affix = (AFFIX *) DictAffixGet(dict, i);
+			Assert(affix);
 
-	/* Need more space? */
-	if (size > Conf->avail)
-	{
-		Conf->firstfree = palloc0(COMPACT_ALLOC_CHUNK);
-		Conf->avail = COMPACT_ALLOC_CHUNK;
-	}
+			memcpy(affix, ConfBuild->Affix[i], size);
 
-	result = (void *) Conf->firstfree;
-	Conf->firstfree += size;
-	Conf->avail -= size;
+			offset += size;
+		}
+	}
 
-	return result;
+	/* DictNodes prefix tree */
+	dict->DictNodesStart = dict->AffixStart + offset;
+	/* We have at least one root node even if dictionary list is empty */
+	dict_node = (SPNode *) NodeArrayGet(&ConfBuild->DictNodes, 0);
+	Assert(dict_node && dict_node->length > 0);
+	/* Copy dictionary nodes into persistent structure */
+	memcpy(DictDictNodes(dict), ConfBuild->DictNodes.Nodes,
+		   ConfBuild->DictNodes.NodesEnd);
+
+	/* PrefixNodes prefix tree */
+	dict->PrefixNodesStart = dict->DictNodesStart + ConfBuild->DictNodes.NodesEnd;
+	/* We have at least one root node even if prefix list is empty */
+	aff_node = (AffixNode *) NodeArrayGet(&ConfBuild->PrefixNodes, 0);
+	Assert(aff_node && aff_node->length > 0);
+	/* Copy prefix nodes into persistent structure */
+	memcpy(DictPrefixNodes(dict), ConfBuild->PrefixNodes.Nodes,
+		   ConfBuild->PrefixNodes.NodesEnd);
+
+	/* SuffixNodes prefix tree */
+	dict->SuffixNodesStart = dict->PrefixNodesStart + ConfBuild->PrefixNodes.NodesEnd;
+	/* We have at least one root node even if suffix list is empty */
+	aff_node = (AffixNode *) NodeArrayGet(&ConfBuild->SuffixNodes, 0);
+	Assert(aff_node && aff_node->length > 0);
+	/* Copy suffix nodes into persistent structure */
+	memcpy(DictSuffixNodes(dict), ConfBuild->SuffixNodes.Nodes,
+		   ConfBuild->SuffixNodes.NodesEnd);
+
+	/* CompoundAffix array */
+	dict->CompoundAffixStart = dict->SuffixNodesStart +
+		ConfBuild->SuffixNodes.NodesEnd;
+	/* We have at least one CompoundAffix terminating entry */
+	Assert(ConfBuild->nCompoundAffix > 0);
+	/* Copy array of compound affixes into persistent structure */
+	memcpy(DictCompoundAffix(dict), ConfBuild->CompoundAffix,
+		   sizeof(CMPDAffix) * ConfBuild->nCompoundAffix);
 }
 
-#define cpalloc(size) compact_palloc0(Conf, size)
-#define cpalloc0(size) compact_palloc0(Conf, size)
-
-static char *
-cpstrdup(IspellDict *Conf, const char *str)
+/*
+ * Clean up when dictionary construction is complete.
+ */
+void
+NIFinishBuild(IspellDictBuild *ConfBuild)
 {
-	char	   *res = cpalloc(strlen(str) + 1);
-
-	strcpy(res, str);
-	return res;
+	/* Release no-longer-needed temp memory */
+	MemoryContextDelete(ConfBuild->buildCxt);
+	/* Just for cleanliness, zero the now-dangling pointers */
+	ConfBuild->buildCxt = NULL;
+	ConfBuild->Spell = NULL;
+	ConfBuild->CompoundAffixFlags = NULL;
 }
 
-
 /*
  * Apply lowerstr(), producing a temporary result (in the buildCxt).
  */
 static char *
-lowerstr_ctx(IspellDict *Conf, const char *src)
+lowerstr_ctx(IspellDictBuild *ConfBuild, const char *src)
 {
 	MemoryContext saveCtx;
 	char	   *dst;
 
-	saveCtx = MemoryContextSwitchTo(Conf->buildCxt);
+	saveCtx = MemoryContextSwitchTo(ConfBuild->buildCxt);
 	dst = lowerstr(src);
 	MemoryContextSwitchTo(saveCtx);
 
@@ -190,7 +246,7 @@ lowerstr_ctx(IspellDict *Conf, const char *src)
 
 #define STRNCMP(s,p)	strncmp( (s), (p), strlen(p) )
 #define GETWCHAR(W,L,N,T) ( ((const uint8*)(W))[ ((T)==FF_PREFIX) ? (N) : ( (L) - 1 - (N) ) ] )
-#define GETCHAR(A,N,T)	  GETWCHAR( (A)->repl, (A)->replen, N, T )
+#define GETCHAR(A,N,T)	  GETWCHAR( AffixFieldRepl(A), (A)->replen, N, T )
 
 static char *VoidString = "";
 
@@ -311,18 +367,189 @@ strbncmp(const unsigned char *s1, const unsigned char *s2, size_t count)
 static int
 cmpaffix(const void *s1, const void *s2)
 {
-	const AFFIX *a1 = (const AFFIX *) s1;
-	const AFFIX *a2 = (const AFFIX *) s2;
+	const AFFIX *a1 = *((AFFIX *const *) s1);
+	const AFFIX *a2 = *((AFFIX *const *) s2);
 
 	if (a1->type < a2->type)
 		return -1;
 	if (a1->type > a2->type)
 		return 1;
 	if (a1->type == FF_PREFIX)
-		return strcmp(a1->repl, a2->repl);
+		return strcmp(AffixFieldRepl(a1), AffixFieldRepl(a2));
 	else
-		return strbcmp((const unsigned char *) a1->repl,
-					   (const unsigned char *) a2->repl);
+		return strbcmp((const unsigned char *) AffixFieldRepl(a1),
+					   (const unsigned char *) AffixFieldRepl(a2));
+}
+
+/*
+ * Allocate space for AffixData.
+ */
+static void
+InitAffixData(IspellDictBuild *ConfBuild, int numAffixData)
+{
+	uint32		size;
+
+	size = 8 * 1024 /* Reserve 8KB for data */;
+
+	ConfBuild->AffixData = (char *) tmpalloc(size);
+	ConfBuild->AffixDataSize = size;
+	ConfBuild->AffixDataOffset = (uint32 *) tmpalloc(numAffixData * sizeof(uint32));
+	ConfBuild->nAffixData = 0;
+	ConfBuild->mAffixData= numAffixData;
+
+	/* Save offset of the end of data */
+	ConfBuild->AffixDataEnd = 0;
+}
+
+/*
+ * Add affix set of affix flags into IspellDict struct.  If IspellDict doesn't
+ * fit new affix set then resize it.
+ *
+ * ConfBuild: building structure for the current dictionary.
+ * AffixSet: set of affix flags.
+ */
+static void
+AddAffixSet(IspellDictBuild *ConfBuild, const char *AffixSet,
+			uint32 AffixSetLen)
+{
+	/*
+	 * Check available space for AffixSet.
+	 */
+	if (ConfBuild->AffixDataEnd + AffixSetLen + 1 /* \0 */ >=
+		ConfBuild->AffixDataSize)
+	{
+		uint32		newsize = Max(ConfBuild->AffixDataSize + 8 * 1024 /* 8KB */,
+								  ConfBuild->AffixDataSize + AffixSetLen + 1);
+
+		ConfBuild->AffixData = (char *) repalloc(ConfBuild->AffixData, newsize);
+		ConfBuild->AffixDataSize = newsize;
+	}
+
+	/* Check available number of offsets */
+	if (ConfBuild->nAffixData >= ConfBuild->mAffixData)
+	{
+		ConfBuild->mAffixData *= 2;
+		ConfBuild->AffixDataOffset = (uint32 *) repalloc(ConfBuild->AffixDataOffset,
+														 sizeof(uint32) * ConfBuild->mAffixData);
+	}
+
+	ConfBuild->AffixDataOffset[ConfBuild->nAffixData] = ConfBuild->AffixDataEnd;
+	StrNCpy(AffixDataGet(ConfBuild, ConfBuild->nAffixData),
+			AffixSet, AffixSetLen + 1);
+
+	/* Save offset of the end of data */
+	ConfBuild->AffixDataEnd += AffixSetLen + 1;
+	ConfBuild->nAffixData++;
+}
+
+/*
+ * Allocate space for prefix tree node.
+ *
+ * ConfBuild: building structure for the current dictionary.
+ * array: NodeArray where to allocate new node.
+ * length: number of allocated NodeData.
+ * sizeNodeData: minimum size of each NodeData.
+ * sizeNodeHeader: size of header of new node.
+ *
+ * Returns an offset of new node in NodeArray->Nodes.
+ */
+static uint32
+AllocateNode(IspellDictBuild *ConfBuild, NodeArray *array, uint32 length,
+			 uint32 sizeNodeData, uint32 sizeNodeHeader)
+{
+	uint32		node_offset;
+	uint32		size;
+
+	size = sizeNodeHeader + length * sizeNodeData;
+	size = MAXALIGN(size);
+
+	if (array->NodesSize == 0)
+	{
+		array->NodesSize = size * 32;	/* Reserve space for next levels of the
+										 * prefix tree */
+		array->Nodes = (char *) tmpalloc(array->NodesSize);
+		array->NodesEnd = 0;
+	}
+	else if (array->NodesEnd + size >= array->NodesSize)
+	{
+		array->NodesSize = Max(array->NodesSize * 2, array->NodesSize + size);
+		array->Nodes = (char *) repalloc(array->Nodes, array->NodesSize);
+	}
+
+	node_offset = array->NodesEnd;
+	array->NodesEnd += size;
+
+	return node_offset;
+}
+
+/*
+ * Allocate space for SPNode.
+ *
+ * Returns an offset of new node in ConfBuild->DictNodes->Nodes.
+ */
+static uint32
+AllocateSPNode(IspellDictBuild *ConfBuild, uint32 length)
+{
+	uint32		offset;
+	SPNode	   *node;
+	SPNodeData *data;
+	uint32		i;
+
+	offset = AllocateNode(ConfBuild, &ConfBuild->DictNodes, length,
+						  sizeof(SPNodeData), SPNHDRSZ);
+	node = (SPNode *) NodeArrayGet(&ConfBuild->DictNodes, offset);
+	node->length = length;
+
+	/*
+	 * Initialize all SPNodeData with default values. We cannot use memset()
+	 * here because not all fields have 0 as default value.
+	 */
+	for (i = 0; i < length; i++)
+	{
+		data = &(node->data[i]);
+		data->val = 0;
+		data->affix = ISPELL_INVALID_INDEX;
+		data->compoundflag = 0;
+		data->isword = 0;
+		data->node_offset = ISPELL_INVALID_OFFSET;
+	}
+
+	return offset;
+}
+
+/*
+ * Allocate space for AffixNode.
+ *
+ * Returns an offset of new node in NodeArray->Nodes.
+ */
+static uint32
+AllocateAffixNode(IspellDictBuild *ConfBuild, NodeArray *array, uint32 length)
+{
+	uint32		offset;
+	AffixNode  *node;
+	AffixNodeData *data;
+	uint32		i;
+
+	offset = AllocateNode(ConfBuild, array, length, sizeof(AffixNodeData),
+						  ANHRDSZ);
+	node = (AffixNode *) NodeArrayGet(array, offset);
+	node->length = length;
+	node->isvoid = 0;
+
+	/*
+	 * Initialize all AffixNodeData with default values. We cannot use memset()
+	 * here because not all fields have 0 as default value.
+	 */
+	for (i = 0; i < length; i++)
+	{
+		data = &(node->data[i]);
+		data->val = 0;
+		data->affstart = ISPELL_INVALID_INDEX;
+		data->affend = ISPELL_INVALID_INDEX;
+		data->node_offset = ISPELL_INVALID_OFFSET;
+	}
+
+	return offset;
 }
 
 /*
@@ -333,7 +560,7 @@ cmpaffix(const void *s1, const void *s2)
  * - 2 characters (FM_LONG). A character may be Unicode.
  * - numbers from 1 to 65000 (FM_NUM).
  *
- * Depending on the flagMode an affix string can have the following format:
+ * Depending on the flagmode an affix string can have the following format:
  * - FM_CHAR: ABCD
  *	 Here we have 4 flags: A, B, C and D
  * - FM_LONG: ABCDE*
@@ -341,13 +568,13 @@ cmpaffix(const void *s1, const void *s2)
  * - FM_NUM: 200,205,50
  *	 Here we have 3 flags: 200, 205 and 50
  *
- * Conf: current dictionary.
+ * flagmode: flag mode of the dictionary
  * sflagset: the set of affix flags. Returns a reference to the start of a next
  *			 affix flag.
  * sflag: returns an affix flag from sflagset.
  */
 static void
-getNextFlagFromString(IspellDict *Conf, char **sflagset, char *sflag)
+getNextFlagFromString(FlagMode flagmode, char **sflagset, char *sflag)
 {
 	int32		s;
 	char	   *next,
@@ -356,11 +583,11 @@ getNextFlagFromString(IspellDict *Conf, char **sflagset, char *sflag)
 	bool		stop = false;
 	bool		met_comma = false;
 
-	maxstep = (Conf->flagMode == FM_LONG) ? 2 : 1;
+	maxstep = (flagmode == FM_LONG) ? 2 : 1;
 
 	while (**sflagset)
 	{
-		switch (Conf->flagMode)
+		switch (flagmode)
 		{
 			case FM_LONG:
 			case FM_CHAR:
@@ -422,15 +649,15 @@ getNextFlagFromString(IspellDict *Conf, char **sflagset, char *sflag)
 				stop = true;
 				break;
 			default:
-				elog(ERROR, "unrecognized type of Conf->flagMode: %d",
-					 Conf->flagMode);
+				elog(ERROR, "unrecognized type of flagmode: %d",
+					 flagmode);
 		}
 
 		if (stop)
 			break;
 	}
 
-	if (Conf->flagMode == FM_LONG && maxstep > 0)
+	if (flagmode == FM_LONG && maxstep > 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
 				 errmsg("invalid affix flag \"%s\" with \"long\" flag value",
@@ -440,31 +667,28 @@ getNextFlagFromString(IspellDict *Conf, char **sflagset, char *sflag)
 }
 
 /*
- * Checks if the affix set Conf->AffixData[affix] contains affixflag.
- * Conf->AffixData[affix] does not contain affixflag if this flag is not used
- * actually by the .dict file.
+ * Checks if the affix set from AffixData contains affixflag. Affix set does
+ * not contain affixflag if this flag is not used actually by the .dict file.
  *
- * Conf: current dictionary.
- * affix: index of the Conf->AffixData array.
+ * flagmode: flag mode of the dictionary.
+ * sflagset: the set of affix flags.
  * affixflag: the affix flag.
  *
- * Returns true if the string Conf->AffixData[affix] contains affixflag,
- * otherwise returns false.
+ * Returns true if the affix set string contains affixflag, otherwise returns
+ * false.
  */
 static bool
-IsAffixFlagInUse(IspellDict *Conf, int affix, const char *affixflag)
+IsAffixFlagInUse(FlagMode flagmode, char *sflagset, const char *affixflag)
 {
-	char	   *flagcur;
+	char	   *flagcur = sflagset;
 	char		flag[BUFSIZ];
 
 	if (*affixflag == 0)
 		return true;
 
-	flagcur = Conf->AffixData[affix];
-
 	while (*flagcur)
 	{
-		getNextFlagFromString(Conf, &flagcur, flag);
+		getNextFlagFromString(flagmode, &flagcur, flag);
 		/* Compare first affix flag in flagcur with affixflag */
 		if (strcmp(flag, affixflag) == 0)
 			return true;
@@ -477,31 +701,33 @@ IsAffixFlagInUse(IspellDict *Conf, int affix, const char *affixflag)
 /*
  * Adds the new word into the temporary array Spell.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * word: new word.
  * flag: set of affix flags. Single flag can be get by getNextFlagFromString().
  */
 static void
-NIAddSpell(IspellDict *Conf, const char *word, const char *flag)
+NIAddSpell(IspellDictBuild *ConfBuild, const char *word, const char *flag)
 {
-	if (Conf->nspell >= Conf->mspell)
+	if (ConfBuild->nSpell >= ConfBuild->mSpell)
 	{
-		if (Conf->mspell)
+		if (ConfBuild->mSpell)
 		{
-			Conf->mspell *= 2;
-			Conf->Spell = (SPELL **) repalloc(Conf->Spell, Conf->mspell * sizeof(SPELL *));
+			ConfBuild->mSpell *= 2;
+			ConfBuild->Spell = (SPELL **) repalloc(ConfBuild->Spell,
+												   ConfBuild->mSpell * sizeof(SPELL *));
 		}
 		else
 		{
-			Conf->mspell = 1024 * 20;
-			Conf->Spell = (SPELL **) tmpalloc(Conf->mspell * sizeof(SPELL *));
+			ConfBuild->mSpell = 1024 * 20;
+			ConfBuild->Spell = (SPELL **) tmpalloc(ConfBuild->mSpell * sizeof(SPELL *));
 		}
 	}
-	Conf->Spell[Conf->nspell] = (SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1);
-	strcpy(Conf->Spell[Conf->nspell]->word, word);
-	Conf->Spell[Conf->nspell]->p.flag = (*flag != '\0')
+	ConfBuild->Spell[ConfBuild->nSpell] =
+		(SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1);
+	strcpy(ConfBuild->Spell[ConfBuild->nSpell]->word, word);
+	ConfBuild->Spell[ConfBuild->nSpell]->p.flag = (*flag != '\0')
 		? tmpstrdup(flag) : VoidString;
-	Conf->nspell++;
+	ConfBuild->nSpell++;
 }
 
 /*
@@ -509,11 +735,11 @@ NIAddSpell(IspellDict *Conf, const char *word, const char *flag)
  *
  * Note caller must already have applied get_tsearch_config_filename.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * filename: path to the .dict file.
  */
 void
-NIImportDictionary(IspellDict *Conf, const char *filename)
+NIImportDictionary(IspellDictBuild *ConfBuild, const char *filename)
 {
 	tsearch_readline_state trst;
 	char	   *line;
@@ -564,9 +790,9 @@ NIImportDictionary(IspellDict *Conf, const char *filename)
 			}
 			s += pg_mblen(s);
 		}
-		pstr = lowerstr_ctx(Conf, line);
+		pstr = lowerstr_ctx(ConfBuild, line);
 
-		NIAddSpell(Conf, pstr, flag);
+		NIAddSpell(ConfBuild, pstr, flag);
 		pfree(pstr);
 
 		pfree(line);
@@ -590,7 +816,7 @@ NIImportDictionary(IspellDict *Conf, const char *filename)
  * SFX M   0	 's         .
  * is presented here.
  *
- * Conf: current dictionary.
+ * dict: current dictionary.
  * word: basic form of word.
  * affixflag: affix flag, by which a basic form of word was generated.
  * flag: compound flag used to compare with StopMiddle->compoundflag.
@@ -598,9 +824,9 @@ NIImportDictionary(IspellDict *Conf, const char *filename)
  * Returns 1 if the word was found in the prefix tree, else returns 0.
  */
 static int
-FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
+FindWord(IspellDictData *dict, const char *word, const char *affixflag, int flag)
 {
-	SPNode	   *node = Conf->Dictionary;
+	SPNode	   *node = (SPNode *) DictDictNodes(dict);
 	SPNodeData *StopLow,
 			   *StopHigh,
 			   *StopMiddle;
@@ -636,10 +862,14 @@ FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
 					 * Check if this affix rule is presented in the affix set
 					 * with index StopMiddle->affix.
 					 */
-					if (IsAffixFlagInUse(Conf, StopMiddle->affix, affixflag))
+					if (IsAffixFlagInUse(dict->flagMode,
+										 DictAffixDataGet(dict, StopMiddle->affix),
+										 affixflag))
 						return 1;
 				}
-				node = StopMiddle->node;
+				/* Retreive SPNode by the offset */
+				node = (SPNode *) DictNodeGet(DictDictNodes(dict),
+											  StopMiddle->node_offset);
 				ptr++;
 				break;
 			}
@@ -657,7 +887,8 @@ FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
 /*
  * Adds a new affix rule to the Affix field.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary, is used to allocate
+ *			  temporary data.
  * flag: affix flag ('\' in the below example).
  * flagflags: set of flags from the flagval field for this affix rule. This set
  *			  is listed after '/' character in the added string (repl).
@@ -673,26 +904,54 @@ FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
  * type: FF_SUFFIX or FF_PREFIX.
  */
 static void
-NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
-		   const char *find, const char *repl, int type)
+NIAddAffix(IspellDictBuild *ConfBuild, const char *flag, char flagflags,
+		   const char *mask, const char *find, const char *repl, int type)
 {
 	AFFIX	   *Affix;
+	uint32		size;
+	uint32		flaglen = strlen(flag),
+				findlen = strlen(find),
+				repllen = strlen(repl),
+				masklen = strlen(mask);
+
+	/* Sanity checks */
+	if (flaglen > AF_FLAG_MAXSIZE)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("affix flag \"%s\" too long", flag)));
+	if (findlen > AF_REPL_MAXSIZE)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("affix find field \"%s\" too long", find)));
+	if (repllen > AF_REPL_MAXSIZE)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("affix repl field \"%s\" too long", repl)));
+	if (masklen > AF_REPL_MAXSIZE)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("affix mask field \"%s\" too long", repl)));
 
-	if (Conf->naffixes >= Conf->maffixes)
+	if (ConfBuild->nAffix >= ConfBuild->mAffix)
 	{
-		if (Conf->maffixes)
+		if (ConfBuild->mAffix)
 		{
-			Conf->maffixes *= 2;
-			Conf->Affix = (AFFIX *) repalloc((void *) Conf->Affix, Conf->maffixes * sizeof(AFFIX));
+			ConfBuild->mAffix *= 2;
+			ConfBuild->Affix = (AFFIX **) repalloc(ConfBuild->Affix,
+												   ConfBuild->mAffix * sizeof(AFFIX *));
 		}
 		else
 		{
-			Conf->maffixes = 16;
-			Conf->Affix = (AFFIX *) palloc(Conf->maffixes * sizeof(AFFIX));
+			ConfBuild->mAffix = 255;
+			ConfBuild->Affix = (AFFIX **) tmpalloc(ConfBuild->mAffix * sizeof(AFFIX *));
 		}
 	}
 
-	Affix = Conf->Affix + Conf->naffixes;
+	size = AFFIXHDRSZ + flaglen + 1 /* \0 */ + findlen + 1 /* \0 */ +
+		repllen + 1 /* \0 */ + masklen + 1 /* \0 */;
+
+	Affix = (AFFIX *) tmpalloc(size);
+	ConfBuild->Affix[ConfBuild->nAffix] = Affix;
 
 	/* This affix rule can be applied for words with any ending */
 	if (strcmp(mask, ".") == 0 || *mask == '\0')
@@ -705,42 +964,12 @@ NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
 	{
 		Affix->issimple = 0;
 		Affix->isregis = 1;
-		RS_compile(&(Affix->reg.regis), (type == FF_SUFFIX),
-				   *mask ? mask : VoidString);
 	}
 	/* This affix rule will use regex_t to search word ending */
 	else
 	{
-		int			masklen;
-		int			wmasklen;
-		int			err;
-		pg_wchar   *wmask;
-		char	   *tmask;
-
 		Affix->issimple = 0;
 		Affix->isregis = 0;
-		tmask = (char *) tmpalloc(strlen(mask) + 3);
-		if (type == FF_SUFFIX)
-			sprintf(tmask, "%s$", mask);
-		else
-			sprintf(tmask, "^%s", mask);
-
-		masklen = strlen(tmask);
-		wmask = (pg_wchar *) tmpalloc((masklen + 1) * sizeof(pg_wchar));
-		wmasklen = pg_mb2wchar_with_len(tmask, wmask, masklen);
-
-		err = pg_regcomp(&(Affix->reg.regex), wmask, wmasklen,
-						 REG_ADVANCED | REG_NOSUB,
-						 DEFAULT_COLLATION_OID);
-		if (err)
-		{
-			char		errstr[100];
-
-			pg_regerror(err, &(Affix->reg.regex), errstr, sizeof(errstr));
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
-					 errmsg("invalid regular expression: %s", errstr)));
-		}
 	}
 
 	Affix->flagflags = flagflags;
@@ -749,15 +978,22 @@ NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
 		if ((Affix->flagflags & FF_COMPOUNDFLAG) == 0)
 			Affix->flagflags |= FF_COMPOUNDFLAG;
 	}
-	Affix->flag = cpstrdup(Conf, flag);
+
 	Affix->type = type;
 
-	Affix->find = (find && *find) ? cpstrdup(Conf, find) : VoidString;
-	if ((Affix->replen = strlen(repl)) > 0)
-		Affix->repl = cpstrdup(Conf, repl);
-	else
-		Affix->repl = VoidString;
-	Conf->naffixes++;
+	Affix->replen = repllen;
+	StrNCpy(AffixFieldRepl(Affix), repl, repllen + 1);
+
+	Affix->findlen = findlen;
+	StrNCpy(AffixFieldFind(Affix), find, findlen + 1);
+
+	Affix->masklen = masklen;
+	StrNCpy(AffixFieldMask(Affix), mask, masklen + 1);
+
+	StrNCpy(AffixFieldFlag(Affix), flag, flaglen + 1);
+
+	ConfBuild->nAffix++;
+	ConfBuild->AffixSize += size;
 }
 
 /* Parsing states for parse_affentry() and friends */
@@ -1021,10 +1257,10 @@ parse_affentry(char *str, char *mask, char *find, char *repl)
  * Sets a Hunspell options depending on flag type.
  */
 static void
-setCompoundAffixFlagValue(IspellDict *Conf, CompoundAffixFlag *entry,
+setCompoundAffixFlagValue(IspellDictBuild *ConfBuild, CompoundAffixFlag *entry,
 						  char *s, uint32 val)
 {
-	if (Conf->flagMode == FM_NUM)
+	if (ConfBuild->dict->flagMode == FM_NUM)
 	{
 		char	   *next;
 		int			i;
@@ -1044,19 +1280,19 @@ setCompoundAffixFlagValue(IspellDict *Conf, CompoundAffixFlag *entry,
 	else
 		entry->flag.s = tmpstrdup(s);
 
-	entry->flagMode = Conf->flagMode;
+	entry->flagMode = ConfBuild->dict->flagMode;
 	entry->value = val;
 }
 
 /*
  * Sets up a correspondence for the affix parameter with the affix flag.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * s: affix flag in string.
  * val: affix parameter.
  */
 static void
-addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val)
+addCompoundAffixFlagValue(IspellDictBuild *ConfBuild, char *s, uint32 val)
 {
 	CompoundAffixFlag *newValue;
 	char		sbuf[BUFSIZ];
@@ -1083,29 +1319,29 @@ addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val)
 	*sflag = '\0';
 
 	/* Resize array or allocate memory for array CompoundAffixFlag */
-	if (Conf->nCompoundAffixFlag >= Conf->mCompoundAffixFlag)
+	if (ConfBuild->nCompoundAffixFlag >= ConfBuild->mCompoundAffixFlag)
 	{
-		if (Conf->mCompoundAffixFlag)
+		if (ConfBuild->mCompoundAffixFlag)
 		{
-			Conf->mCompoundAffixFlag *= 2;
-			Conf->CompoundAffixFlags = (CompoundAffixFlag *)
-				repalloc((void *) Conf->CompoundAffixFlags,
-						 Conf->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
+			ConfBuild->mCompoundAffixFlag *= 2;
+			ConfBuild->CompoundAffixFlags = (CompoundAffixFlag *)
+				repalloc((void *) ConfBuild->CompoundAffixFlags,
+						 ConfBuild->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
 		}
 		else
 		{
-			Conf->mCompoundAffixFlag = 10;
-			Conf->CompoundAffixFlags = (CompoundAffixFlag *)
-				tmpalloc(Conf->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
+			ConfBuild->mCompoundAffixFlag = 10;
+			ConfBuild->CompoundAffixFlags = (CompoundAffixFlag *)
+				tmpalloc(ConfBuild->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
 		}
 	}
 
-	newValue = Conf->CompoundAffixFlags + Conf->nCompoundAffixFlag;
+	newValue = ConfBuild->CompoundAffixFlags + ConfBuild->nCompoundAffixFlag;
 
-	setCompoundAffixFlagValue(Conf, newValue, sbuf, val);
+	setCompoundAffixFlagValue(ConfBuild, newValue, sbuf, val);
 
-	Conf->usecompound = true;
-	Conf->nCompoundAffixFlag++;
+	ConfBuild->dict->usecompound = true;
+	ConfBuild->nCompoundAffixFlag++;
 }
 
 /*
@@ -1113,7 +1349,7 @@ addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val)
  * flags s.
  */
 static int
-getCompoundAffixFlagValue(IspellDict *Conf, char *s)
+getCompoundAffixFlagValue(IspellDictBuild *ConfBuild, char *s)
 {
 	uint32		flag = 0;
 	CompoundAffixFlag *found,
@@ -1121,18 +1357,18 @@ getCompoundAffixFlagValue(IspellDict *Conf, char *s)
 	char		sflag[BUFSIZ];
 	char	   *flagcur;
 
-	if (Conf->nCompoundAffixFlag == 0)
+	if (ConfBuild->nCompoundAffixFlag == 0)
 		return 0;
 
 	flagcur = s;
 	while (*flagcur)
 	{
-		getNextFlagFromString(Conf, &flagcur, sflag);
-		setCompoundAffixFlagValue(Conf, &key, sflag, 0);
+		getNextFlagFromString(ConfBuild->dict->flagMode, &flagcur, sflag);
+		setCompoundAffixFlagValue(ConfBuild, &key, sflag, 0);
 
 		found = (CompoundAffixFlag *)
-			bsearch(&key, (void *) Conf->CompoundAffixFlags,
-					Conf->nCompoundAffixFlag, sizeof(CompoundAffixFlag),
+			bsearch(&key, (void *) ConfBuild->CompoundAffixFlags,
+					ConfBuild->nCompoundAffixFlag, sizeof(CompoundAffixFlag),
 					cmpcmdflag);
 		if (found != NULL)
 			flag |= found->value;
@@ -1144,14 +1380,13 @@ getCompoundAffixFlagValue(IspellDict *Conf, char *s)
 /*
  * Returns a flag set using the s parameter.
  *
- * If Conf->useFlagAliases is true then the s parameter is index of the
- * Conf->AffixData array and function returns its entry.
- * Else function returns the s parameter.
+ * If useFlagAliases is true then the s parameter is index of the AffixData
+ * array and function returns its entry.  Else function returns the s parameter.
  */
 static char *
-getAffixFlagSet(IspellDict *Conf, char *s)
+getAffixFlagSet(IspellDictBuild *ConfBuild, char *s)
 {
-	if (Conf->useFlagAliases && *s != '\0')
+	if (ConfBuild->dict->useFlagAliases && *s != '\0')
 	{
 		int			curaffix;
 		char	   *end;
@@ -1162,13 +1397,13 @@ getAffixFlagSet(IspellDict *Conf, char *s)
 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
 					 errmsg("invalid affix alias \"%s\"", s)));
 
-		if (curaffix > 0 && curaffix <= Conf->nAffixData)
+		if (curaffix > 0 && curaffix <= ConfBuild->nAffixData)
 
 			/*
 			 * Do not subtract 1 from curaffix because empty string was added
 			 * in NIImportOOAffixes
 			 */
-			return Conf->AffixData[curaffix];
+			return AffixDataGet(ConfBuild, curaffix);
 		else
 			return VoidString;
 	}
@@ -1179,11 +1414,11 @@ getAffixFlagSet(IspellDict *Conf, char *s)
 /*
  * Import an affix file that follows MySpell or Hunspell format.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * filename: path to the .affix file.
  */
 static void
-NIImportOOAffixes(IspellDict *Conf, const char *filename)
+NIImportOOAffixes(IspellDictBuild *ConfBuild, const char *filename)
 {
 	char		type[BUFSIZ],
 			   *ptype = NULL;
@@ -1203,9 +1438,9 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 	char	   *recoded;
 
 	/* read file to find any flag */
-	Conf->usecompound = false;
-	Conf->useFlagAliases = false;
-	Conf->flagMode = FM_CHAR;
+	ConfBuild->dict->usecompound = false;
+	ConfBuild->dict->useFlagAliases = false;
+	ConfBuild->dict->flagMode = FM_CHAR;
 
 	if (!tsearch_readline_begin(&trst, filename))
 		ereport(ERROR,
@@ -1222,30 +1457,36 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 		}
 
 		if (STRNCMP(recoded, "COMPOUNDFLAG") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDFLAG"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("COMPOUNDFLAG"),
 									  FF_COMPOUNDFLAG);
 		else if (STRNCMP(recoded, "COMPOUNDBEGIN") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDBEGIN"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("COMPOUNDBEGIN"),
 									  FF_COMPOUNDBEGIN);
 		else if (STRNCMP(recoded, "COMPOUNDLAST") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDLAST"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("COMPOUNDLAST"),
 									  FF_COMPOUNDLAST);
 		/* COMPOUNDLAST and COMPOUNDEND are synonyms */
 		else if (STRNCMP(recoded, "COMPOUNDEND") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDEND"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("COMPOUNDEND"),
 									  FF_COMPOUNDLAST);
 		else if (STRNCMP(recoded, "COMPOUNDMIDDLE") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDMIDDLE"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("COMPOUNDMIDDLE"),
 									  FF_COMPOUNDMIDDLE);
 		else if (STRNCMP(recoded, "ONLYINCOMPOUND") == 0)
-			addCompoundAffixFlagValue(Conf, recoded + strlen("ONLYINCOMPOUND"),
+			addCompoundAffixFlagValue(ConfBuild,
+									  recoded + strlen("ONLYINCOMPOUND"),
 									  FF_COMPOUNDONLY);
 		else if (STRNCMP(recoded, "COMPOUNDPERMITFLAG") == 0)
-			addCompoundAffixFlagValue(Conf,
+			addCompoundAffixFlagValue(ConfBuild,
 									  recoded + strlen("COMPOUNDPERMITFLAG"),
 									  FF_COMPOUNDPERMITFLAG);
 		else if (STRNCMP(recoded, "COMPOUNDFORBIDFLAG") == 0)
-			addCompoundAffixFlagValue(Conf,
+			addCompoundAffixFlagValue(ConfBuild,
 									  recoded + strlen("COMPOUNDFORBIDFLAG"),
 									  FF_COMPOUNDFORBIDFLAG);
 		else if (STRNCMP(recoded, "FLAG") == 0)
@@ -1258,9 +1499,9 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 			if (*s)
 			{
 				if (STRNCMP(s, "long") == 0)
-					Conf->flagMode = FM_LONG;
+					ConfBuild->dict->flagMode = FM_LONG;
 				else if (STRNCMP(s, "num") == 0)
-					Conf->flagMode = FM_NUM;
+					ConfBuild->dict->flagMode = FM_NUM;
 				else if (STRNCMP(s, "default") != 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
@@ -1274,8 +1515,8 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 	}
 	tsearch_readline_end(&trst);
 
-	if (Conf->nCompoundAffixFlag > 1)
-		qsort((void *) Conf->CompoundAffixFlags, Conf->nCompoundAffixFlag,
+	if (ConfBuild->nCompoundAffixFlag > 1)
+		qsort((void *) ConfBuild->CompoundAffixFlags, ConfBuild->nCompoundAffixFlag,
 			  sizeof(CompoundAffixFlag), cmpcmdflag);
 
 	if (!tsearch_readline_begin(&trst, filename))
@@ -1295,15 +1536,15 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 
 		if (ptype)
 			pfree(ptype);
-		ptype = lowerstr_ctx(Conf, type);
+		ptype = lowerstr_ctx(ConfBuild, type);
 
 		/* First try to parse AF parameter (alias compression) */
 		if (STRNCMP(ptype, "af") == 0)
 		{
 			/* First line is the number of aliases */
-			if (!Conf->useFlagAliases)
+			if (!ConfBuild->dict->useFlagAliases)
 			{
-				Conf->useFlagAliases = true;
+				ConfBuild->dict->useFlagAliases = true;
 				naffix = atoi(sflag);
 				if (naffix <= 0)
 					ereport(ERROR,
@@ -1313,11 +1554,10 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 				/* Also reserve place for empty flag set */
 				naffix++;
 
-				Conf->AffixData = (char **) palloc0(naffix * sizeof(char *));
-				Conf->lenAffixData = Conf->nAffixData = naffix;
+				InitAffixData(ConfBuild, naffix);
 
 				/* Add empty flag set into AffixData */
-				Conf->AffixData[curaffix] = VoidString;
+				AddAffixSet(ConfBuild, VoidString, 0);
 				curaffix++;
 			}
 			/* Other lines are aliases */
@@ -1325,7 +1565,7 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 			{
 				if (curaffix < naffix)
 				{
-					Conf->AffixData[curaffix] = cpstrdup(Conf, sflag);
+					AddAffixSet(ConfBuild, sflag, strlen(sflag));
 					curaffix++;
 				}
 				else
@@ -1343,8 +1583,8 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 
 		sflaglen = strlen(sflag);
 		if (sflaglen == 0
-			|| (sflaglen > 1 && Conf->flagMode == FM_CHAR)
-			|| (sflaglen > 2 && Conf->flagMode == FM_LONG))
+			|| (sflaglen > 1 && ConfBuild->dict->flagMode == FM_CHAR)
+			|| (sflaglen > 2 && ConfBuild->dict->flagMode == FM_LONG))
 			goto nextline;
 
 		/*--------
@@ -1372,21 +1612,21 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 
 			/* Get flags after '/' (flags are case sensitive) */
 			if ((ptr = strchr(repl, '/')) != NULL)
-				aflg |= getCompoundAffixFlagValue(Conf,
-												  getAffixFlagSet(Conf,
+				aflg |= getCompoundAffixFlagValue(ConfBuild,
+												  getAffixFlagSet(ConfBuild,
 																  ptr + 1));
 			/* Get lowercased version of string before '/' */
-			prepl = lowerstr_ctx(Conf, repl);
+			prepl = lowerstr_ctx(ConfBuild, repl);
 			if ((ptr = strchr(prepl, '/')) != NULL)
 				*ptr = '\0';
-			pfind = lowerstr_ctx(Conf, find);
-			pmask = lowerstr_ctx(Conf, mask);
+			pfind = lowerstr_ctx(ConfBuild, find);
+			pmask = lowerstr_ctx(ConfBuild, mask);
 			if (t_iseq(find, '0'))
 				*pfind = '\0';
 			if (t_iseq(repl, '0'))
 				*prepl = '\0';
 
-			NIAddAffix(Conf, sflag, flagflags | aflg, pmask, pfind, prepl,
+			NIAddAffix(ConfBuild, sflag, flagflags | aflg, pmask, pfind, prepl,
 					   isSuffix ? FF_SUFFIX : FF_PREFIX);
 			pfree(prepl);
 			pfree(pfind);
@@ -1412,7 +1652,7 @@ nextline:
  * work to NIImportOOAffixes(), which will re-read the whole file.
  */
 void
-NIImportAffixes(IspellDict *Conf, const char *filename)
+NIImportAffixes(IspellDictBuild *ConfBuild, const char *filename)
 {
 	char	   *pstr = NULL;
 	char		flag[BUFSIZ];
@@ -1433,9 +1673,9 @@ NIImportAffixes(IspellDict *Conf, const char *filename)
 				 errmsg("could not open affix file \"%s\": %m",
 						filename)));
 
-	Conf->usecompound = false;
-	Conf->useFlagAliases = false;
-	Conf->flagMode = FM_CHAR;
+	ConfBuild->dict->usecompound = false;
+	ConfBuild->dict->useFlagAliases = false;
+	ConfBuild->dict->flagMode = FM_CHAR;
 
 	while ((recoded = tsearch_readline(&trst)) != NULL)
 	{
@@ -1457,10 +1697,8 @@ NIImportAffixes(IspellDict *Conf, const char *filename)
 					s += pg_mblen(s);
 
 				if (*s && pg_mblen(s) == 1)
-				{
-					addCompoundAffixFlagValue(Conf, s, FF_COMPOUNDFLAG);
-					Conf->usecompound = true;
-				}
+					addCompoundAffixFlagValue(ConfBuild, s, FF_COMPOUNDFLAG);
+
 				oldformat = true;
 				goto nextline;
 			}
@@ -1533,7 +1771,8 @@ NIImportAffixes(IspellDict *Conf, const char *filename)
 		if (!parse_affentry(pstr, mask, find, repl))
 			goto nextline;
 
-		NIAddAffix(Conf, flag, flagflags, mask, find, repl, suffixes ? FF_SUFFIX : FF_PREFIX);
+		NIAddAffix(ConfBuild, flag, flagflags, mask, find, repl,
+				   suffixes ? FF_SUFFIX : FF_PREFIX);
 
 nextline:
 		pfree(recoded);
@@ -1552,53 +1791,48 @@ isnewformat:
 				 errmsg("affix file contains both old-style and new-style commands")));
 	tsearch_readline_end(&trst);
 
-	NIImportOOAffixes(Conf, filename);
+	NIImportOOAffixes(ConfBuild, filename);
 }
 
 /*
  * Merges two affix flag sets and stores a new affix flag set into
- * Conf->AffixData.
+ * ConfBuild->AffixData.
  *
  * Returns index of a new affix flag set.
  */
 static int
-MergeAffix(IspellDict *Conf, int a1, int a2)
+MergeAffix(IspellDictBuild *ConfBuild, int a1, int a2)
 {
-	char	  **ptr;
+	char	   *ptr;
+	uint32		len;
 
 	/* Do not merge affix flags if one of affix flags is empty */
-	if (*Conf->AffixData[a1] == '\0')
+	if (*AffixDataGet(ConfBuild, a1) == '\0')
 		return a2;
-	else if (*Conf->AffixData[a2] == '\0')
+	else if (*AffixDataGet(ConfBuild, a2) == '\0')
 		return a1;
 
-	while (Conf->nAffixData + 1 >= Conf->lenAffixData)
-	{
-		Conf->lenAffixData *= 2;
-		Conf->AffixData = (char **) repalloc(Conf->AffixData,
-											 sizeof(char *) * Conf->lenAffixData);
-	}
-
-	ptr = Conf->AffixData + Conf->nAffixData;
-	if (Conf->flagMode == FM_NUM)
+	if (ConfBuild->dict->flagMode == FM_NUM)
 	{
-		*ptr = cpalloc(strlen(Conf->AffixData[a1]) +
-					   strlen(Conf->AffixData[a2]) +
-					   1 /* comma */ + 1 /* \0 */ );
-		sprintf(*ptr, "%s,%s", Conf->AffixData[a1], Conf->AffixData[a2]);
+		len = strlen(AffixDataGet(ConfBuild, a1)) + 1 /* comma */ +
+			  strlen(AffixDataGet(ConfBuild, a2));
+		ptr = tmpalloc(len + 1 /* \0 */);
+		sprintf(ptr, "%s,%s", AffixDataGet(ConfBuild, a1),
+				AffixDataGet(ConfBuild, a2));
 	}
 	else
 	{
-		*ptr = cpalloc(strlen(Conf->AffixData[a1]) +
-					   strlen(Conf->AffixData[a2]) +
-					   1 /* \0 */ );
-		sprintf(*ptr, "%s%s", Conf->AffixData[a1], Conf->AffixData[a2]);
+		len = strlen(AffixDataGet(ConfBuild, a1)) +
+			  strlen(AffixDataGet(ConfBuild, a2));
+		ptr = tmpalloc(len + 1 /* \0 */ );
+		sprintf(ptr, "%s%s", AffixDataGet(ConfBuild, a1),
+				AffixDataGet(ConfBuild, a2));
 	}
-	ptr++;
-	*ptr = NULL;
-	Conf->nAffixData++;
 
-	return Conf->nAffixData - 1;
+	AddAffixSet(ConfBuild, ptr, len);
+	pfree(ptr);
+
+	return ConfBuild->nAffixData - 1;
 }
 
 /*
@@ -1606,66 +1840,87 @@ MergeAffix(IspellDict *Conf, int a1, int a2)
  * flags with the given index.
  */
 static uint32
-makeCompoundFlags(IspellDict *Conf, int affix)
+makeCompoundFlags(IspellDictBuild *ConfBuild, int affix)
 {
-	char	   *str = Conf->AffixData[affix];
+	char	   *str = AffixDataGet(ConfBuild, affix);
 
-	return (getCompoundAffixFlagValue(Conf, str) & FF_COMPOUNDFLAGMASK);
+	return (getCompoundAffixFlagValue(ConfBuild, str) & FF_COMPOUNDFLAGMASK);
 }
 
 /*
  * Makes a prefix tree for the given level.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * low: lower index of the Conf->Spell array.
  * high: upper index of the Conf->Spell array.
  * level: current prefix tree level.
+ *
+ * Returns an offset of SPNode in DictNodes.
  */
-static SPNode *
-mkSPNode(IspellDict *Conf, int low, int high, int level)
+static uint32
+mkSPNode(IspellDictBuild *ConfBuild, int low, int high, int level)
 {
 	int			i;
 	int			nchar = 0;
 	char		lastchar = '\0';
+	uint32		rs_offset,
+				new_offset;
 	SPNode	   *rs;
 	SPNodeData *data;
+	int			data_index = 0;
 	int			lownew = low;
 
 	for (i = low; i < high; i++)
-		if (Conf->Spell[i]->p.d.len > level && lastchar != Conf->Spell[i]->word[level])
+		if (ConfBuild->Spell[i]->p.d.len > level &&
+			lastchar != ConfBuild->Spell[i]->word[level])
 		{
 			nchar++;
-			lastchar = Conf->Spell[i]->word[level];
+			lastchar = ConfBuild->Spell[i]->word[level];
 		}
 
 	if (!nchar)
-		return NULL;
+		return ISPELL_INVALID_OFFSET;
 
-	rs = (SPNode *) cpalloc0(SPNHDRSZ + nchar * sizeof(SPNodeData));
-	rs->length = nchar;
+	rs_offset = AllocateSPNode(ConfBuild, nchar);
+	rs = (SPNode *) NodeArrayGet(&ConfBuild->DictNodes, rs_offset);
 	data = rs->data;
 
 	lastchar = '\0';
 	for (i = low; i < high; i++)
-		if (Conf->Spell[i]->p.d.len > level)
+		if (ConfBuild->Spell[i]->p.d.len > level)
 		{
-			if (lastchar != Conf->Spell[i]->word[level])
+			if (lastchar != ConfBuild->Spell[i]->word[level])
 			{
 				if (lastchar)
 				{
 					/* Next level of the prefix tree */
-					data->node = mkSPNode(Conf, lownew, i, level + 1);
+					new_offset = mkSPNode(ConfBuild, lownew, i, level + 1);
+
+					/*
+					 * ConfBuild->DictNodes can be repalloc'ed within
+					 * mkSPNode(), so reinitialize pointers.
+					 */
+					rs = (SPNode *) NodeArrayGet(&ConfBuild->DictNodes, rs_offset);
+
+					/* First save offset of the new node */
+					data = &(rs->data[data_index]);
+					data->node_offset = new_offset;
+
+					/* Work with next node */
+					data_index++;
+					Assert(data_index < nchar);
+					data = &(rs->data[data_index]);
+
 					lownew = i;
-					data++;
 				}
-				lastchar = Conf->Spell[i]->word[level];
+				lastchar = ConfBuild->Spell[i]->word[level];
 			}
-			data->val = ((uint8 *) (Conf->Spell[i]->word))[level];
-			if (Conf->Spell[i]->p.d.len == level + 1)
+			data->val = ((uint8 *) (ConfBuild->Spell[i]->word))[level];
+			if (ConfBuild->Spell[i]->p.d.len == level + 1)
 			{
 				bool		clearCompoundOnly = false;
 
-				if (data->isword && data->affix != Conf->Spell[i]->p.d.affix)
+				if (data->isword && data->affix != ConfBuild->Spell[i]->p.d.affix)
 				{
 					/*
 					 * MergeAffix called a few times. If one of word is
@@ -1674,15 +1929,17 @@ mkSPNode(IspellDict *Conf, int low, int high, int level)
 					 */
 
 					clearCompoundOnly = (FF_COMPOUNDONLY & data->compoundflag
-										 & makeCompoundFlags(Conf, Conf->Spell[i]->p.d.affix))
+										 & makeCompoundFlags(ConfBuild,
+															 ConfBuild->Spell[i]->p.d.affix))
 						? false : true;
-					data->affix = MergeAffix(Conf, data->affix, Conf->Spell[i]->p.d.affix);
+					data->affix = MergeAffix(ConfBuild, data->affix,
+											 ConfBuild->Spell[i]->p.d.affix);
 				}
 				else
-					data->affix = Conf->Spell[i]->p.d.affix;
+					data->affix = ConfBuild->Spell[i]->p.d.affix;
 				data->isword = 1;
 
-				data->compoundflag = makeCompoundFlags(Conf, data->affix);
+				data->compoundflag = makeCompoundFlags(ConfBuild, data->affix);
 
 				if ((data->compoundflag & FF_COMPOUNDONLY) &&
 					(data->compoundflag & FF_COMPOUNDFLAG) == 0)
@@ -1694,9 +1951,19 @@ mkSPNode(IspellDict *Conf, int low, int high, int level)
 		}
 
 	/* Next level of the prefix tree */
-	data->node = mkSPNode(Conf, lownew, high, level + 1);
+	new_offset = mkSPNode(ConfBuild, lownew, high, level + 1);
 
-	return rs;
+	/*
+	 * ConfBuild->DictNodes can be repalloc'ed within mkSPNode(), so
+	 * reinitialize pointers.
+	 */
+	rs = (SPNode *) NodeArrayGet(&ConfBuild->DictNodes, rs_offset);
+
+	/* Save offset of the new node */
+	data = &(rs->data[data_index]);
+	data->node_offset = new_offset;
+
+	return rs_offset;
 }
 
 /*
@@ -1704,90 +1971,98 @@ mkSPNode(IspellDict *Conf, int low, int high, int level)
  * and affixes.
  */
 void
-NISortDictionary(IspellDict *Conf)
+NISortDictionary(IspellDictBuild *ConfBuild)
 {
 	int			i;
 	int			naffix = 0;
 	int			curaffix;
+	uint32		node_offset;
 
 	/* compress affixes */
 
 	/*
-	 * If we use flag aliases then we need to use Conf->AffixData filled in
+	 * If we use flag aliases then we need to use ConfBuild->AffixData filled in
 	 * the NIImportOOAffixes().
 	 */
-	if (Conf->useFlagAliases)
+	if (ConfBuild->dict->useFlagAliases)
 	{
-		for (i = 0; i < Conf->nspell; i++)
+		for (i = 0; i < ConfBuild->nSpell; i++)
 		{
 			char	   *end;
 
-			if (*Conf->Spell[i]->p.flag != '\0')
+			if (*ConfBuild->Spell[i]->p.flag != '\0')
 			{
-				curaffix = strtol(Conf->Spell[i]->p.flag, &end, 10);
-				if (Conf->Spell[i]->p.flag == end || errno == ERANGE)
+				curaffix = strtol(ConfBuild->Spell[i]->p.flag, &end, 10);
+				if (ConfBuild->Spell[i]->p.flag == end || errno == ERANGE)
 					ereport(ERROR,
 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
 							 errmsg("invalid affix alias \"%s\"",
-									Conf->Spell[i]->p.flag)));
+									ConfBuild->Spell[i]->p.flag)));
 			}
 			else
 			{
 				/*
-				 * If Conf->Spell[i]->p.flag is empty, then get empty value of
-				 * Conf->AffixData (0 index).
+				 * If ConfBuild->Spell[i]->p.flag is empty, then get empty
+				 * value of ConfBuild->AffixData (0 index).
 				 */
 				curaffix = 0;
 			}
 
-			Conf->Spell[i]->p.d.affix = curaffix;
-			Conf->Spell[i]->p.d.len = strlen(Conf->Spell[i]->word);
+			ConfBuild->Spell[i]->p.d.affix = curaffix;
+			ConfBuild->Spell[i]->p.d.len = strlen(ConfBuild->Spell[i]->word);
 		}
 	}
-	/* Otherwise fill Conf->AffixData here */
+	/* Otherwise fill ConfBuild->AffixData here */
 	else
 	{
 		/* Count the number of different flags used in the dictionary */
-		qsort((void *) Conf->Spell, Conf->nspell, sizeof(SPELL *),
+		qsort((void *) ConfBuild->Spell, ConfBuild->nSpell, sizeof(SPELL *),
 			  cmpspellaffix);
 
 		naffix = 0;
-		for (i = 0; i < Conf->nspell; i++)
+		for (i = 0; i < ConfBuild->nSpell; i++)
 		{
 			if (i == 0
-				|| strcmp(Conf->Spell[i]->p.flag, Conf->Spell[i - 1]->p.flag))
+				|| strcmp(ConfBuild->Spell[i]->p.flag,
+						  ConfBuild->Spell[i - 1]->p.flag))
 				naffix++;
 		}
 
 		/*
-		 * Fill in Conf->AffixData with the affixes that were used in the
-		 * dictionary. Replace textual flag-field of Conf->Spell entries with
-		 * indexes into Conf->AffixData array.
+		 * Fill in AffixData with the affixes that were used in the
+		 * dictionary. Replace textual flag-field of ConfBuild->Spell entries
+		 * with indexes into ConfBuild->AffixData array.
 		 */
-		Conf->AffixData = (char **) palloc0(naffix * sizeof(char *));
+		InitAffixData(ConfBuild, naffix);
 
 		curaffix = -1;
-		for (i = 0; i < Conf->nspell; i++)
+		for (i = 0; i < ConfBuild->nSpell; i++)
 		{
 			if (i == 0
-				|| strcmp(Conf->Spell[i]->p.flag, Conf->AffixData[curaffix]))
+				|| strcmp(ConfBuild->Spell[i]->p.flag,
+						  AffixDataGet(ConfBuild, curaffix)))
 			{
 				curaffix++;
 				Assert(curaffix < naffix);
-				Conf->AffixData[curaffix] = cpstrdup(Conf,
-													 Conf->Spell[i]->p.flag);
+				AddAffixSet(ConfBuild, ConfBuild->Spell[i]->p.flag,
+								   strlen(ConfBuild->Spell[i]->p.flag));
 			}
 
-			Conf->Spell[i]->p.d.affix = curaffix;
-			Conf->Spell[i]->p.d.len = strlen(Conf->Spell[i]->word);
+			ConfBuild->Spell[i]->p.d.affix = curaffix;
+			ConfBuild->Spell[i]->p.d.len = strlen(ConfBuild->Spell[i]->word);
 		}
-
-		Conf->lenAffixData = Conf->nAffixData = naffix;
 	}
 
 	/* Start build a prefix tree */
-	qsort((void *) Conf->Spell, Conf->nspell, sizeof(SPELL *), cmpspell);
-	Conf->Dictionary = mkSPNode(Conf, 0, Conf->nspell, 0);
+	qsort((void *) ConfBuild->Spell, ConfBuild->nSpell, sizeof(SPELL *), cmpspell);
+	node_offset = mkSPNode(ConfBuild, 0, ConfBuild->nSpell, 0);
+
+	/* Make void node only if the DictNodes is empty */
+	if (node_offset == ISPELL_INVALID_OFFSET)
+	{
+		/* AllocateSPNode() initializes root node data */
+		AllocateSPNode(ConfBuild, 1);
+	}
 }
 
 /*
@@ -1795,83 +2070,104 @@ NISortDictionary(IspellDict *Conf)
  * rule. Affixes with empty replace string do not include in the prefix tree.
  * This affixes are included by mkVoidAffix().
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * low: lower index of the Conf->Affix array.
  * high: upper index of the Conf->Affix array.
  * level: current prefix tree level.
  * type: FF_SUFFIX or FF_PREFIX.
+ *
+ * Returns an offset in nodes array.
  */
-static AffixNode *
-mkANode(IspellDict *Conf, int low, int high, int level, int type)
+static uint32
+mkANode(IspellDictBuild *ConfBuild, int low, int high, int level, int type)
 {
 	int			i;
 	int			nchar = 0;
 	uint8		lastchar = '\0';
+	NodeArray  *array;
+	uint32		rs_offset,
+				new_offset;
 	AffixNode  *rs;
 	AffixNodeData *data;
+	int			data_index = 0;
 	int			lownew = low;
-	int			naff;
-	AFFIX	  **aff;
 
 	for (i = low; i < high; i++)
-		if (Conf->Affix[i].replen > level && lastchar != GETCHAR(Conf->Affix + i, level, type))
+		if (ConfBuild->Affix[i]->replen > level &&
+			lastchar != GETCHAR(ConfBuild->Affix[i], level, type))
 		{
 			nchar++;
-			lastchar = GETCHAR(Conf->Affix + i, level, type);
+			lastchar = GETCHAR(ConfBuild->Affix[i], level, type);
 		}
 
 	if (!nchar)
-		return NULL;
+		return ISPELL_INVALID_OFFSET;
 
-	aff = (AFFIX **) tmpalloc(sizeof(AFFIX *) * (high - low + 1));
-	naff = 0;
+	if (type == FF_SUFFIX)
+		array = &ConfBuild->SuffixNodes;
+	else
+		array = &ConfBuild->PrefixNodes;
 
-	rs = (AffixNode *) cpalloc0(ANHRDSZ + nchar * sizeof(AffixNodeData));
-	rs->length = nchar;
-	data = rs->data;
+	rs_offset = AllocateAffixNode(ConfBuild, array, nchar);
+	rs = (AffixNode *) NodeArrayGet(array, rs_offset);
+	data = (AffixNodeData *) rs->data;
 
 	lastchar = '\0';
 	for (i = low; i < high; i++)
-		if (Conf->Affix[i].replen > level)
+		if (ConfBuild->Affix[i]->replen > level)
 		{
-			if (lastchar != GETCHAR(Conf->Affix + i, level, type))
+			if (lastchar != GETCHAR(ConfBuild->Affix[i], level, type))
 			{
 				if (lastchar)
 				{
 					/* Next level of the prefix tree */
-					data->node = mkANode(Conf, lownew, i, level + 1, type);
-					if (naff)
-					{
-						data->naff = naff;
-						data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * naff);
-						memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
-						naff = 0;
-					}
-					data++;
+					new_offset = mkANode(ConfBuild, lownew, i, level + 1, type);
+
+					/*
+					 * array can be repalloc'ed within mkANode(), so
+					 * reinitialize pointers.
+					 */
+					rs = (AffixNode *) NodeArrayGet(array, rs_offset);
+
+					/* First save offset of the new node */
+					data = &(rs->data[data_index]);
+					data->node_offset = new_offset;
+
+					/* Handle next data node */
+					data_index++;
+					Assert(data_index < nchar);
+					data = &(rs->data[data_index]);
+
 					lownew = i;
 				}
-				lastchar = GETCHAR(Conf->Affix + i, level, type);
+				lastchar = GETCHAR(ConfBuild->Affix[i], level, type);
 			}
-			data->val = GETCHAR(Conf->Affix + i, level, type);
-			if (Conf->Affix[i].replen == level + 1)
+			data->val = GETCHAR(ConfBuild->Affix[i], level, type);
+			if (ConfBuild->Affix[i]->replen == level + 1)
 			{					/* affix stopped */
-				aff[naff++] = Conf->Affix + i;
+				if (data->affstart == ISPELL_INVALID_INDEX)
+				{
+					data->affstart = i;
+					data->affend = i;
+				}
+				else
+					data->affend = i;
 			}
 		}
 
 	/* Next level of the prefix tree */
-	data->node = mkANode(Conf, lownew, high, level + 1, type);
-	if (naff)
-	{
-		data->naff = naff;
-		data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * naff);
-		memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
-		naff = 0;
-	}
+	new_offset = mkANode(ConfBuild, lownew, high, level + 1, type);
+
+	/*
+	 * array can be repalloc'ed within mkANode(), so reinitialize pointers.
+	 */
+	rs = (AffixNode *) NodeArrayGet(array, rs_offset);
 
-	pfree(aff);
+	/* Save offset of the new node */
+	data = &(rs->data[data_index]);
+	data->node_offset = new_offset;
 
-	return rs;
+	return rs_offset;
 }
 
 /*
@@ -1879,139 +2175,151 @@ mkANode(IspellDict *Conf, int low, int high, int level, int type)
  * for affixes which have empty replace string ("repl" field).
  */
 static void
-mkVoidAffix(IspellDict *Conf, bool issuffix, int startsuffix)
+mkVoidAffix(IspellDictBuild *ConfBuild, bool issuffix, int startsuffix)
 {
-	int			i,
-				cnt = 0;
+	int			i;
 	int			start = (issuffix) ? startsuffix : 0;
-	int			end = (issuffix) ? Conf->naffixes : startsuffix;
-	AffixNode  *Affix = (AffixNode *) palloc0(ANHRDSZ + sizeof(AffixNodeData));
-
-	Affix->length = 1;
-	Affix->isvoid = 1;
+	int			end = (issuffix) ? ConfBuild->nAffix : startsuffix;
+	uint32		node_offset;
+	NodeArray  *array;
+	AffixNode  *Affix;
+	AffixNodeData *AffixData;
 
 	if (issuffix)
-	{
-		Affix->data->node = Conf->Suffix;
-		Conf->Suffix = Affix;
-	}
+		array = &ConfBuild->SuffixNodes;
 	else
-	{
-		Affix->data->node = Conf->Prefix;
-		Conf->Prefix = Affix;
-	}
+		array = &ConfBuild->PrefixNodes;
 
-	/* Count affixes with empty replace string */
-	for (i = start; i < end; i++)
-		if (Conf->Affix[i].replen == 0)
-			cnt++;
-
-	/* There is not affixes with empty replace string */
-	if (cnt == 0)
-		return;
+	node_offset = AllocateAffixNode(ConfBuild, array, 1);
+	Affix = (AffixNode *) NodeArrayGet(array, node_offset);
 
-	Affix->data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * cnt);
-	Affix->data->naff = (uint32) cnt;
+	Affix->isvoid = 1;
+	AffixData = (AffixNodeData *) Affix->data;
 
-	cnt = 0;
 	for (i = start; i < end; i++)
-		if (Conf->Affix[i].replen == 0)
+		if (ConfBuild->Affix[i]->replen == 0)
 		{
-			Affix->data->aff[cnt] = Conf->Affix + i;
-			cnt++;
+			if (AffixData->affstart == ISPELL_INVALID_INDEX)
+			{
+				AffixData->affstart = i;
+				AffixData->affend = i;
+			}
+			else
+				AffixData->affend = i;
 		}
 }
 
 /*
- * Checks if the affixflag is used by dictionary. Conf->AffixData does not
+ * Checks if the affixflag is used by dictionary. AffixData does not
  * contain affixflag if this flag is not used actually by the .dict file.
  *
- * Conf: current dictionary.
+ * ConfBuild: building structure for the current dictionary.
  * affixflag: affix flag.
  *
- * Returns true if the Conf->AffixData array contains affixflag, otherwise
+ * Returns true if the ConfBuild->AffixData array contains affixflag, otherwise
  * returns false.
  */
 static bool
-isAffixInUse(IspellDict *Conf, char *affixflag)
+isAffixInUse(IspellDictBuild *ConfBuild, char *affixflag)
 {
 	int			i;
 
-	for (i = 0; i < Conf->nAffixData; i++)
-		if (IsAffixFlagInUse(Conf, i, affixflag))
+	for (i = 0; i < ConfBuild->nAffixData; i++)
+		if (IsAffixFlagInUse(ConfBuild->dict->flagMode,
+							 AffixDataGet(ConfBuild, i), affixflag))
 			return true;
 
 	return false;
 }
 
 /*
- * Builds Conf->Prefix and Conf->Suffix trees from the imported affixes.
+ * Builds Prefix and Suffix trees from the imported affixes.
  */
 void
-NISortAffixes(IspellDict *Conf)
+NISortAffixes(IspellDictBuild *ConfBuild)
 {
 	AFFIX	   *Affix;
+	AffixNode  *voidPrefix,
+			   *voidSuffix;
 	size_t		i;
 	CMPDAffix  *ptr;
-	int			firstsuffix = Conf->naffixes;
-
-	if (Conf->naffixes == 0)
-		return;
+	int			firstsuffix = ConfBuild->nAffix;
+	uint32		prefix_offset,
+				suffix_offset;
 
 	/* Store compound affixes in the Conf->CompoundAffix array */
-	if (Conf->naffixes > 1)
-		qsort((void *) Conf->Affix, Conf->naffixes, sizeof(AFFIX), cmpaffix);
-	Conf->CompoundAffix = ptr = (CMPDAffix *) palloc(sizeof(CMPDAffix) * Conf->naffixes);
-	ptr->affix = NULL;
-
-	for (i = 0; i < Conf->naffixes; i++)
+	if (ConfBuild->nAffix > 1)
+		qsort((void *) ConfBuild->Affix, ConfBuild->nAffix,
+			  sizeof(AFFIX *), cmpaffix);
+	ConfBuild->nCompoundAffix = ConfBuild->nAffix + 1 /* terminating entry */;
+	ConfBuild->CompoundAffix = ptr =
+		(CMPDAffix *) tmpalloc(sizeof(CMPDAffix) * ConfBuild->nCompoundAffix);
+	ptr->affix = ISPELL_INVALID_INDEX;
+
+	for (i = 0; i < ConfBuild->nAffix; i++)
 	{
-		Affix = &(((AFFIX *) Conf->Affix)[i]);
+		Affix = ConfBuild->Affix[i];
 		if (Affix->type == FF_SUFFIX && i < firstsuffix)
 			firstsuffix = i;
 
 		if ((Affix->flagflags & FF_COMPOUNDFLAG) && Affix->replen > 0 &&
-			isAffixInUse(Conf, Affix->flag))
+			isAffixInUse(ConfBuild, AffixFieldFlag(Affix)))
 		{
 			bool		issuffix = (Affix->type == FF_SUFFIX);
 
-			if (ptr == Conf->CompoundAffix ||
+			if (ptr == ConfBuild->CompoundAffix ||
 				issuffix != (ptr - 1)->issuffix ||
-				strbncmp((const unsigned char *) (ptr - 1)->affix,
-						 (const unsigned char *) Affix->repl,
+				strbncmp((const unsigned char *) AffixFieldRepl(ConfBuild->Affix[(ptr - 1)->affix]),
+						 (const unsigned char *) AffixFieldRepl(Affix),
 						 (ptr - 1)->len))
 			{
 				/* leave only unique and minimals suffixes */
-				ptr->affix = Affix->repl;
+				ptr->affix = i;
 				ptr->len = Affix->replen;
 				ptr->issuffix = issuffix;
 				ptr++;
 			}
 		}
 	}
-	ptr->affix = NULL;
-	Conf->CompoundAffix = (CMPDAffix *) repalloc(Conf->CompoundAffix, sizeof(CMPDAffix) * (ptr - Conf->CompoundAffix + 1));
+	ptr->affix = ISPELL_INVALID_INDEX;
+	ConfBuild->nCompoundAffix = ptr - ConfBuild->CompoundAffix + 1;
 
 	/* Start build a prefix tree */
-	Conf->Prefix = mkANode(Conf, 0, firstsuffix, 0, FF_PREFIX);
-	Conf->Suffix = mkANode(Conf, firstsuffix, Conf->naffixes, 0, FF_SUFFIX);
-	mkVoidAffix(Conf, true, firstsuffix);
-	mkVoidAffix(Conf, false, firstsuffix);
+	mkVoidAffix(ConfBuild, true, firstsuffix);
+	mkVoidAffix(ConfBuild, false, firstsuffix);
+
+	prefix_offset = mkANode(ConfBuild, 0, firstsuffix, 0, FF_PREFIX);
+	suffix_offset = mkANode(ConfBuild, firstsuffix, ConfBuild->nAffix, 0,
+							FF_SUFFIX);
+
+	/* Adjust offsets of new nodes for nodes of void affixes */
+	voidPrefix = (AffixNode *) NodeArrayGet(&ConfBuild->PrefixNodes, 0);
+	voidPrefix->data[0].node_offset = prefix_offset;
+
+	voidSuffix = (AffixNode *) NodeArrayGet(&ConfBuild->SuffixNodes, 0);
+	voidSuffix->data[0].node_offset = suffix_offset;
 }
 
 static AffixNodeData *
-FindAffixes(AffixNode *node, const char *word, int wrdlen, int *level, int type)
+FindAffixes(IspellDictData *dict, AffixNode *node, const char *word, int wrdlen,
+			int *level, int type)
 {
+	AffixNode  *nodes;
 	AffixNodeData *StopLow,
 			   *StopHigh,
 			   *StopMiddle;
 	uint8 symbol;
 
+	if (type == FF_PREFIX)
+		nodes = (AffixNode *) DictPrefixNodes(dict);
+	else
+		nodes = (AffixNode *) DictSuffixNodes(dict);
+
 	if (node->isvoid)
 	{							/* search void affixes */
-		if (node->data->naff)
+		if (node->data->affstart != ISPELL_INVALID_INDEX)
 			return node->data;
-		node = node->data->node;
+		node = (AffixNode *) DictNodeGet(nodes, node->data->node_offset);
 	}
 
 	while (node && *level < wrdlen)
@@ -2026,9 +2334,10 @@ FindAffixes(AffixNode *node, const char *word, int wrdlen, int *level, int type)
 			if (StopMiddle->val == symbol)
 			{
 				(*level)++;
-				if (StopMiddle->naff)
+				if (StopMiddle->affstart != ISPELL_INVALID_INDEX)
 					return StopMiddle;
-				node = StopMiddle->node;
+				node = (AffixNode *) DictNodeGet(nodes,
+												 StopMiddle->node_offset);
 				break;
 			}
 			else if (StopMiddle->val < symbol)
@@ -2042,8 +2351,67 @@ FindAffixes(AffixNode *node, const char *word, int wrdlen, int *level, int type)
 	return NULL;
 }
 
+/*
+ * Compile regular expression on first use and store it within reg.
+ */
+static void
+CompileAffixReg(AffixReg *reg, bool isregis, int type,
+				const char *mask, int masklen, MemoryContext dictCtx)
+{
+	MemoryContext oldcontext;
+
+	Assert(dictCtx);
+
+	/*
+	 * Switch to memory context of the dictionary, so compiled expression can be
+	 * used in other queries.
+	 */
+	oldcontext = MemoryContextSwitchTo(dictCtx);
+
+	if (isregis)
+		RS_compile(&reg->r.regis, (type == FF_SUFFIX), mask);
+	else
+	{
+		int			wmasklen;
+		int			err;
+		pg_wchar   *wmask;
+		char	   *tmask;
+
+		tmask = (char *) palloc(masklen + 3);
+		if (type == FF_SUFFIX)
+			sprintf(tmask, "%s$", mask);
+		else
+			sprintf(tmask, "^%s", mask);
+
+		masklen = strlen(tmask);
+		wmask = (pg_wchar *) palloc((masklen + 1) * sizeof(pg_wchar));
+		wmasklen = pg_mb2wchar_with_len(tmask, wmask, masklen);
+
+		err = pg_regcomp(&(reg->r.regex), wmask, wmasklen,
+						 REG_ADVANCED | REG_NOSUB,
+						 DEFAULT_COLLATION_OID);
+		if (err)
+		{
+			char		errstr[100];
+
+			pg_regerror(err, &(reg->r.regex), errstr, sizeof(errstr));
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+					 errmsg("invalid regular expression: %s", errstr)));
+		}
+
+		pfree(wmask);
+		pfree(tmask);
+	}
+
+	reg->iscompiled = true;
+
+	MemoryContextSwitchTo(oldcontext);
+}
+
 static char *
-CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *newword, int *baselen)
+CheckAffix(const char *word, size_t len, AFFIX *Affix, AffixReg *reg,
+		   int flagflags, char *newword, int *baselen, MemoryContext dictCtx)
 {
 	/*
 	 * Check compound allow flags
@@ -2083,7 +2451,7 @@ CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *neww
 	if (Affix->type == FF_SUFFIX)
 	{
 		strcpy(newword, word);
-		strcpy(newword + len - Affix->replen, Affix->find);
+		strcpy(newword + len - Affix->replen, AffixFieldFind(Affix));
 		if (baselen)			/* store length of non-changed part of word */
 			*baselen = len - Affix->replen;
 	}
@@ -2093,9 +2461,9 @@ CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *neww
 		 * if prefix is an all non-changed part's length then all word
 		 * contains only prefix and suffix, so out
 		 */
-		if (baselen && *baselen + strlen(Affix->find) <= Affix->replen)
+		if (baselen && *baselen + Affix->findlen <= Affix->replen)
 			return NULL;
-		strcpy(newword, Affix->find);
+		strcpy(newword, AffixFieldFind(Affix));
 		strcat(newword, word + Affix->replen);
 	}
 
@@ -2106,7 +2474,12 @@ CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *neww
 		return newword;
 	else if (Affix->isregis)
 	{
-		if (RS_execute(&(Affix->reg.regis), newword))
+		/* Compile the regular expression on first demand */
+		if (!reg->iscompiled)
+			CompileAffixReg(reg, Affix->isregis, Affix->type,
+							AffixFieldMask(Affix), Affix->masklen, dictCtx);
+
+		if (RS_execute(&(reg->r.regis), newword))
 			return newword;
 	}
 	else
@@ -2116,12 +2489,17 @@ CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *neww
 		size_t		data_len;
 		int			newword_len;
 
+		/* Compile the regular expression on first demand */
+		if (!reg->iscompiled)
+			CompileAffixReg(reg, Affix->isregis, Affix->type,
+							AffixFieldMask(Affix), Affix->masklen, dictCtx);
+
 		/* Convert data string to wide characters */
 		newword_len = strlen(newword);
 		data = (pg_wchar *) palloc((newword_len + 1) * sizeof(pg_wchar));
 		data_len = pg_mb2wchar_with_len(newword, data, newword_len);
 
-		if (!(err = pg_regexec(&(Affix->reg.regex), data, data_len, 0, NULL, 0, NULL, 0)))
+		if (!(err = pg_regexec(&(reg->r.regex), data, data_len, 0, NULL, 0, NULL, 0)))
 		{
 			pfree(data);
 			return newword;
@@ -2160,7 +2538,7 @@ NormalizeSubWord(IspellDict *Conf, char *word, int flag)
 	char	  **cur;
 	char		newword[2 * MAXNORMLEN] = "";
 	char		pnewword[2 * MAXNORMLEN] = "";
-	AffixNode  *snode = Conf->Suffix,
+	AffixNode  *snode = (AffixNode *) DictSuffixNodes(Conf->dict),
 			   *pnode;
 	int			i,
 				j;
@@ -2172,7 +2550,7 @@ NormalizeSubWord(IspellDict *Conf, char *word, int flag)
 
 
 	/* Check that the word itself is normal form */
-	if (FindWord(Conf, word, VoidString, flag))
+	if (FindWord(Conf->dict, word, VoidString, flag))
 	{
 		*cur = pstrdup(word);
 		cur++;
@@ -2180,23 +2558,29 @@ NormalizeSubWord(IspellDict *Conf, char *word, int flag)
 	}
 
 	/* Find all other NORMAL forms of the 'word' (check only prefix) */
-	pnode = Conf->Prefix;
+	pnode = (AffixNode *) DictPrefixNodes(Conf->dict);
 	plevel = 0;
 	while (pnode)
 	{
-		prefix = FindAffixes(pnode, word, wrdlen, &plevel, FF_PREFIX);
+		prefix = FindAffixes(Conf->dict, pnode, word, wrdlen, &plevel, FF_PREFIX);
 		if (!prefix)
 			break;
-		for (j = 0; j < prefix->naff; j++)
+		for (j = prefix->affstart; j <= prefix->affend; j++)
 		{
-			if (CheckAffix(word, wrdlen, prefix->aff[j], flag, newword, NULL))
+			AFFIX	   *affix = (AFFIX *) DictAffixGet(Conf->dict, j);
+			AffixReg   *reg = &(Conf->reg[j]);
+
+			if (affix &&
+				CheckAffix(word, wrdlen, affix, reg, flag, newword, NULL,
+						   Conf->dictCtx))
 			{
 				/* prefix success */
-				if (FindWord(Conf, newword, prefix->aff[j]->flag, flag))
+				if (FindWord(Conf->dict, newword, AffixFieldFlag(affix), flag))
 					cur += addToResult(forms, cur, newword);
 			}
 		}
-		pnode = prefix->node;
+		pnode = (AffixNode *) DictNodeGet(DictPrefixNodes(Conf->dict),
+										  prefix->node_offset);
 	}
 
 	/*
@@ -2208,45 +2592,59 @@ NormalizeSubWord(IspellDict *Conf, char *word, int flag)
 		int			baselen = 0;
 
 		/* find possible suffix */
-		suffix = FindAffixes(snode, word, wrdlen, &slevel, FF_SUFFIX);
+		suffix = FindAffixes(Conf->dict, snode, word, wrdlen, &slevel,
+							 FF_SUFFIX);
 		if (!suffix)
 			break;
 		/* foreach suffix check affix */
-		for (i = 0; i < suffix->naff; i++)
+		for (i = suffix->affstart; i <= suffix->affend; i++)
 		{
-			if (CheckAffix(word, wrdlen, suffix->aff[i], flag, newword, &baselen))
+			AFFIX	   *sufentry = (AFFIX *) DictAffixGet(Conf->dict, i);
+			AffixReg   *sufreg = &(Conf->reg[i]);
+
+			if (sufentry &&
+				CheckAffix(word, wrdlen, sufentry, sufreg, flag, newword, &baselen,
+						   Conf->dictCtx))
 			{
 				/* suffix success */
-				if (FindWord(Conf, newword, suffix->aff[i]->flag, flag))
+				if (FindWord(Conf->dict, newword, AffixFieldFlag(sufentry), flag))
 					cur += addToResult(forms, cur, newword);
 
 				/* now we will look changed word with prefixes */
-				pnode = Conf->Prefix;
+				pnode = (AffixNode *) DictPrefixNodes(Conf->dict);
 				plevel = 0;
 				swrdlen = strlen(newword);
 				while (pnode)
 				{
-					prefix = FindAffixes(pnode, newword, swrdlen, &plevel, FF_PREFIX);
+					prefix = FindAffixes(Conf->dict, pnode, newword, swrdlen,
+										 &plevel, FF_PREFIX);
 					if (!prefix)
 						break;
-					for (j = 0; j < prefix->naff; j++)
+					for (j = prefix->affstart; j <= prefix->affend; j++)
 					{
-						if (CheckAffix(newword, swrdlen, prefix->aff[j], flag, pnewword, &baselen))
+						AFFIX	   *prefentry = (AFFIX *) DictAffixGet(Conf->dict, j);
+						AffixReg   *prefreg = &(Conf->reg[j]);
+
+						if (prefentry &&
+							CheckAffix(newword, swrdlen, prefentry, prefreg,
+									   flag, pnewword, &baselen, Conf->dictCtx))
 						{
 							/* prefix success */
-							char	   *ff = (prefix->aff[j]->flagflags & suffix->aff[i]->flagflags & FF_CROSSPRODUCT) ?
-							VoidString : prefix->aff[j]->flag;
+							char	   *ff = (prefentry->flagflags & sufentry->flagflags & FF_CROSSPRODUCT) ?
+										VoidString : AffixFieldFlag(prefentry);
 
-							if (FindWord(Conf, pnewword, ff, flag))
+							if (FindWord(Conf->dict, pnewword, ff, flag))
 								cur += addToResult(forms, cur, pnewword);
 						}
 					}
-					pnode = prefix->node;
+					pnode = (AffixNode *) DictNodeGet(DictPrefixNodes(Conf->dict),
+													  prefix->node_offset);
 				}
 			}
 		}
 
-		snode = suffix->node;
+		snode = (AffixNode *) DictNodeGet(DictSuffixNodes(Conf->dict),
+										  suffix->node_offset);
 	}
 
 	if (cur == forms)
@@ -2266,7 +2664,8 @@ typedef struct SplitVar
 } SplitVar;
 
 static int
-CheckCompoundAffixes(CMPDAffix **ptr, char *word, int len, bool CheckInPlace)
+CheckCompoundAffixes(IspellDictData *dict, CMPDAffix **ptr,
+					 char *word, int len, bool CheckInPlace)
 {
 	bool		issuffix;
 
@@ -2276,9 +2675,12 @@ CheckCompoundAffixes(CMPDAffix **ptr, char *word, int len, bool CheckInPlace)
 
 	if (CheckInPlace)
 	{
-		while ((*ptr)->affix)
+		while ((*ptr)->affix != ISPELL_INVALID_INDEX)
 		{
-			if (len > (*ptr)->len && strncmp((*ptr)->affix, word, (*ptr)->len) == 0)
+			AFFIX	   *affix = (AFFIX *) DictAffixGet(dict, (*ptr)->affix);
+
+			if (len > (*ptr)->len &&
+				strncmp(AffixFieldRepl(affix), word, (*ptr)->len) == 0)
 			{
 				len = (*ptr)->len;
 				issuffix = (*ptr)->issuffix;
@@ -2292,9 +2694,12 @@ CheckCompoundAffixes(CMPDAffix **ptr, char *word, int len, bool CheckInPlace)
 	{
 		char	   *affbegin;
 
-		while ((*ptr)->affix)
+		while ((*ptr)->affix != ISPELL_INVALID_INDEX)
 		{
-			if (len > (*ptr)->len && (affbegin = strstr(word, (*ptr)->affix)) != NULL)
+			AFFIX	   *affix = (AFFIX *) DictAffixGet(dict, (*ptr)->affix);
+
+			if (len > (*ptr)->len &&
+				(affbegin = strstr(word, AffixFieldRepl(affix))) != NULL)
 			{
 				len = (*ptr)->len + (affbegin - word);
 				issuffix = (*ptr)->issuffix;
@@ -2346,13 +2751,14 @@ AddStem(SplitVar *v, char *word)
 }
 
 static SplitVar *
-SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, char *word, int wordlen, int startpos, int minpos)
+SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig,
+				char *word, int wordlen, int startpos, int minpos)
 {
 	SplitVar   *var = NULL;
 	SPNodeData *StopLow,
 			   *StopHigh,
 			   *StopMiddle = NULL;
-	SPNode	   *node = (snode) ? snode : Conf->Dictionary;
+	SPNode	   *node = (snode) ? snode : (SPNode *) DictDictNodes(Conf->dict);
 	int			level = (snode) ? minpos : startpos;	/* recursive
 														 * minpos==level */
 	int			lenaff;
@@ -2367,8 +2773,11 @@ SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, char *word, int
 	while (level < wordlen)
 	{
 		/* find word with epenthetic or/and compound affix */
-		caff = Conf->CompoundAffix;
-		while (level > startpos && (lenaff = CheckCompoundAffixes(&caff, word + level, wordlen - level, (node) ? true : false)) >= 0)
+		caff = (CMPDAffix *) DictCompoundAffix(Conf->dict);
+		while (level > startpos &&
+			   (lenaff = CheckCompoundAffixes(Conf->dict, &caff,
+											  word + level, wordlen - level,
+											  (node) ? true : false)) >= 0)
 		{
 			/*
 			 * there is one of compound affixes, so check word for existings
@@ -2415,7 +2824,8 @@ SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, char *word, int
 
 				while (ptr->next)
 					ptr = ptr->next;
-				ptr->next = SplitToVariants(Conf, NULL, new, word, wordlen, startpos + lenaff, startpos + lenaff);
+				ptr->next = SplitToVariants(Conf, NULL, new, word, wordlen,
+											startpos + lenaff, startpos + lenaff);
 
 				pfree(new->stem);
 				pfree(new);
@@ -2474,13 +2884,14 @@ SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, char *word, int
 						/* we can find next word */
 						level++;
 						AddStem(var, pnstrdup(word + startpos, level - startpos));
-						node = Conf->Dictionary;
+						node = (SPNode *) DictDictNodes(Conf->dict);
 						startpos = level;
 						continue;
 					}
 				}
 			}
-			node = StopMiddle->node;
+			node = (SPNode *) DictNodeGet(DictDictNodes(Conf->dict),
+										  StopMiddle->node_offset);
 		}
 		else
 			node = NULL;
@@ -2530,7 +2941,7 @@ NINormalizeWord(IspellDict *Conf, char *word)
 		pfree(res);
 	}
 
-	if (Conf->usecompound)
+	if (Conf->dict->usecompound)
 	{
 		int			wordlen = strlen(word);
 		SplitVar   *ptr,
diff --git a/src/include/tsearch/dicts/spell.h b/src/include/tsearch/dicts/spell.h
index 4cba578436..df0abd38ae 100644
--- a/src/include/tsearch/dicts/spell.h
+++ b/src/include/tsearch/dicts/spell.h
@@ -18,21 +18,23 @@
 #include "tsearch/dicts/regis.h"
 #include "tsearch/ts_public.h"
 
+#define ISPELL_INVALID_INDEX	(0x7FFFF)
+#define ISPELL_INVALID_OFFSET	(0xFFFFFFFF)
+
 /*
  * SPNode and SPNodeData are used to represent prefix tree (Trie) to store
  * a words list.
  */
-struct SPNode;
-
 typedef struct
 {
 	uint32		val:8,
 				isword:1,
 	/* Stores compound flags listed below */
 				compoundflag:4,
-	/* Reference to an entry of the AffixData field */
+	/* Index of an entry of the AffixData field */
 				affix:19;
-	struct SPNode *node;
+	/* Offset to a node of the DictNodes field */
+	uint32		node_offset;
 } SPNodeData;
 
 /*
@@ -86,21 +88,55 @@ typedef struct spell_struct
  */
 typedef struct aff_struct
 {
-	char	   *flag;
 	/* FF_SUFFIX or FF_PREFIX */
-	uint32		type:1,
+	uint16		type:1,
 				flagflags:7,
 				issimple:1,
 				isregis:1,
-				replen:14;
-	char	   *find;
-	char	   *repl;
+				flaglen:2;
+
+	/* 8 bytes could be too mach for repl, find and mask, but who knows */
+	uint8		replen;
+	uint8		findlen;
+	uint8		masklen;
+
+	/*
+	 * fields stores the following data (each ends with \0):
+	 * - repl
+	 * - find
+	 * - mask
+	 * - flag - one character (if FM_CHAR),
+	 *          two characters (if FM_LONG),
+	 *          number, >= 0 and < 65536 (if FM_NUM).
+	 */
+	char		fields[FLEXIBLE_ARRAY_MEMBER];
+} AFFIX;
+
+#define AF_FLAG_MAXSIZE		5		/* strlen(65536) */
+#define AF_REPL_MAXSIZE		255		/* 8 bytes */
+
+#define AFFIXHDRSZ	(offsetof(AFFIX, fields))
+
+#define AffixFieldRepl(af)	((af)->fields)
+#define AffixFieldFind(af)	((af)->fields + (af)->replen + 1)
+#define AffixFieldMask(af)	(AffixFieldFind(af) + (af)->findlen + 1)
+#define AffixFieldFlag(af)	(AffixFieldMask(af) + (af)->masklen + 1)
+#define AffixGetSize(af)	(AFFIXHDRSZ + (af)->replen + 1 + (af)->findlen + 1 \
+							 + (af)->masklen + 1 + strlen(AffixFieldFlag(af)) + 1)
+
+/*
+ * Stores compiled regular expression of affix. AffixReg uses mask field of
+ * AFFIX as a regular expression.
+ */
+typedef struct AffixReg
+{
+	bool		iscompiled;
 	union
 	{
 		regex_t		regex;
 		Regis		regis;
-	}			reg;
-} AFFIX;
+	}			r;
+} AffixReg;
 
 /*
  * affixes use dictionary flags too
@@ -120,14 +156,13 @@ typedef struct aff_struct
  * AffixNode and AffixNodeData are used to represent prefix tree (Trie) to store
  * an affix list.
  */
-struct AffixNode;
-
 typedef struct
 {
-	uint32		val:8,
-				naff:24;
-	AFFIX	  **aff;
-	struct AffixNode *node;
+	uint8		val;
+	uint32		affstart;
+	uint32		affend;
+	/* Offset to a node of the PrefixNodes or SuffixNodes field */
+	uint32		node_offset;
 } AffixNodeData;
 
 typedef struct AffixNode
@@ -139,9 +174,20 @@ typedef struct AffixNode
 
 #define ANHRDSZ		   (offsetof(AffixNode, data))
 
+typedef struct NodeArray
+{
+	char	   *Nodes;
+	uint32		NodesSize;	/* allocated size of Nodes */
+	uint32		NodesEnd;	/* end of data in Nodes */
+} NodeArray;
+
+#define NodeArrayGet(na, of) \
+	(((of) == ISPELL_INVALID_OFFSET) ? NULL : (na)->Nodes + (of))
+
 typedef struct
 {
-	char	   *affix;
+	/* Index of an affix of the Affix field */
+	uint32		affix;
 	int			len;
 	bool		issuffix;
 } CMPDAffix;
@@ -176,30 +222,75 @@ typedef struct CompoundAffixFlag
 
 #define FLAGNUM_MAXSIZE		(1 << 16)
 
-typedef struct
+typedef struct IspellDictData
 {
-	int			maffixes;
-	int			naffixes;
-	AFFIX	   *Affix;
-
-	AffixNode  *Suffix;
-	AffixNode  *Prefix;
+	FlagMode	flagMode;
+	bool		usecompound;
 
-	SPNode	   *Dictionary;
-	/* Array of sets of affixes */
-	char	  **AffixData;
-	int			lenAffixData;
-	int			nAffixData;
 	bool		useFlagAliases;
 
-	CMPDAffix  *CompoundAffix;
+	uint32		nAffixData;
+	uint32		AffixDataStart;
 
-	bool		usecompound;
-	FlagMode	flagMode;
+	uint32		AffixOffsetStart;
+	uint32		AffixStart;
+	uint32		nAffix;
+
+	uint32		DictNodesStart;
+	uint32		PrefixNodesStart;
+	uint32		SuffixNodesStart;
+
+	uint32		CompoundAffixStart;
 
 	/*
-	 * All follow fields are actually needed only for initialization
+	 * data stores:
+	 * - AffixData - array of affix sets
+	 * - Affix - sorted array of affixes
+	 * - DictNodes - prefix tree of a word list
+	 * - PrefixNodes - prefix tree of a prefix list
+	 * - SuffixNodes - prefix tree of a suffix list
+	 * - CompoundAffix - array of compound affixes
 	 */
+	char		data[FLEXIBLE_ARRAY_MEMBER];
+} IspellDictData;
+
+#define IspellDictDataHdrSize	(offsetof(IspellDictData, data))
+
+#define DictAffixDataOffset(d)	((d)->data)
+#define DictAffixData(d)		((d)->data + (d)->AffixDataStart)
+#define DictAffixDataGet(d, i) \
+	(((i) == ISPELL_INVALID_INDEX) ? NULL : \
+	 (AssertMacro(i < (d)->nAffixData), \
+	  DictAffixData(d) + ((uint32 *) DictAffixDataOffset(d))[i]))
+
+#define DictAffixOffset(d)		((d)->data + (d)->AffixOffsetStart)
+#define DictAffix(d)			((d)->data + (d)->AffixStart)
+#define DictAffixGet(d, i) \
+	(((i) == ISPELL_INVALID_INDEX) ? NULL : \
+	 (AssertMacro(i < (d)->nAffix), \
+	  DictAffix(d) + ((uint32 *) DictAffixOffset(d))[i]))
+
+#define DictDictNodes(d)		((d)->data + (d)->DictNodesStart)
+#define DictPrefixNodes(d)		((d)->data + (d)->PrefixNodesStart)
+#define DictSuffixNodes(d)		((d)->data + (d)->SuffixNodesStart)
+#define DictNodeGet(node_start, of) \
+	(((of) == ISPELL_INVALID_OFFSET) ? NULL : (char *) (node_start) + (of))
+
+#define DictCompoundAffix(d)	((d)->data + (d)->CompoundAffixStart)
+
+/*
+ * IspellDictBuild is used to initialize IspellDictData struct.  This is a
+ * temprorary structure which is setup by NIStartBuild() and released by
+ * NIFinishBuild().
+ */
+typedef struct IspellDictBuild
+{
+	MemoryContext buildCxt;		/* temp context for construction */
+
+	IspellDictData *dict;
+	uint32		dict_size;
+
+	/* Temporary data */
 
 	/* Array of Hunspell options in affix file */
 	CompoundAffixFlag *CompoundAffixFlags;
@@ -208,29 +299,73 @@ typedef struct
 	/* allocated length of CompoundAffixFlags array */
 	int			mCompoundAffixFlag;
 
-	/*
-	 * Remaining fields are only used during dictionary construction; they are
-	 * set up by NIStartBuild and cleared by NIFinishBuild.
-	 */
-	MemoryContext buildCxt;		/* temp context for construction */
-
-	/* Temporary array of all words in the dict file */
+	/* Array of all words in the dict file */
 	SPELL	  **Spell;
-	int			nspell;			/* number of valid entries in Spell array */
-	int			mspell;			/* allocated length of Spell array */
+	int			nSpell;			/* number of valid entries in Spell array */
+	int			mSpell;			/* allocated length of Spell array */
+
+	/* Data for IspellDictData */
+
+	/* Array of all affixes in the aff file */
+	AFFIX	  **Affix;
+	int			nAffix;			/* number of valid entries in Affix array */
+	int			mAffix;			/* allocated length of Affix array */
+	uint32		AffixSize;
+
+	/* Array of sets of affixes */
+	uint32	   *AffixDataOffset;
+	int			nAffixData;		/* number of affix sets */
+	int			mAffixData;		/* allocated number of affix sets */
+	char	   *AffixData;
+	uint32		AffixDataSize;	/* allocated size of AffixData */
+	uint32		AffixDataEnd;	/* end of data in AffixData */
+
+	/* Prefix tree which stores a word list */
+	NodeArray	DictNodes;
+
+	/* Prefix tree which stores a prefix list */
+	NodeArray	PrefixNodes;
+
+	/* Prefix tree which stores a suffix list */
+	NodeArray	SuffixNodes;
 
-	/* These are used to allocate "compact" data without palloc overhead */
-	char	   *firstfree;		/* first free address (always maxaligned) */
-	size_t		avail;			/* free space remaining at firstfree */
+	/* Array of compound affixes */
+	CMPDAffix  *CompoundAffix;
+	int			nCompoundAffix;	/* number of entries of CompoundAffix */
+} IspellDictBuild;
+
+#define AffixDataGet(d, i)		((d)->AffixData + (d)->AffixDataOffset[i])
+
+/*
+ * IspellDict is used within NINormalizeWord.
+ */
+typedef struct IspellDict
+{
+	/*
+	 * Pointer to a DSM location of IspellDictData. Should be retreived per
+	 * every dispell_lexize() call.
+	 */
+	IspellDictData *dict;
+	/*
+	 * Array of regular expression of affixes. Each regular expression is
+	 * compiled only on demand.
+	 */
+	AffixReg   *reg;
+	/*
+	 * Memory context for compiling regular expressions.
+	 */
+	MemoryContext dictCtx;
 } IspellDict;
 
 extern TSLexeme *NINormalizeWord(IspellDict *Conf, char *word);
 
-extern void NIStartBuild(IspellDict *Conf);
-extern void NIImportAffixes(IspellDict *Conf, const char *filename);
-extern void NIImportDictionary(IspellDict *Conf, const char *filename);
-extern void NISortDictionary(IspellDict *Conf);
-extern void NISortAffixes(IspellDict *Conf);
-extern void NIFinishBuild(IspellDict *Conf);
+extern void NIStartBuild(IspellDictBuild *ConfBuild);
+extern void NIImportAffixes(IspellDictBuild *ConfBuild, const char *filename);
+extern void NIImportDictionary(IspellDictBuild *ConfBuild,
+							   const char *filename);
+extern void NISortDictionary(IspellDictBuild *ConfBuild);
+extern void NISortAffixes(IspellDictBuild *ConfBuild);
+extern void NICopyData(IspellDictBuild *ConfBuild);
+extern void NIFinishBuild(IspellDictBuild *ConfBuild);
 
 #endif

Reply via email to