diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 4c7b1e7bfd..cd984929a6 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -888,7 +888,6 @@ RemoveSocketFiles(void)
 		(void) unlink(sock_path);
 	}
 	/* Since we're about to exit, no need to reclaim storage */
-	sock_paths = NIL;
 }
 
 
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index d1a1f47a78..3cadfecfb0 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -22,6 +22,7 @@
 #include "utils/builtins.h"
 
 static shm_mq_handle *pq_mq_handle;
+static shm_mq_handle *volatile dummy_mq_handle = NULL;
 static bool pq_mq_busy = false;
 static pid_t pq_mq_parallel_leader_pid = 0;
 static pid_t pq_mq_parallel_leader_backend_id = InvalidBackendId;
@@ -64,6 +65,9 @@ pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh)
 static void
 pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg)
 {
+	/* fake out valgrind */
+	if (pq_mq_handle)
+		dummy_mq_handle = pq_mq_handle;
 	pq_mq_handle = NULL;
 	whereToSendOutput = DestNone;
 }
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 23ef23c13e..c4e2e1d228 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -850,6 +850,15 @@ AutoVacLauncherShutdown(void)
 			(errmsg_internal("autovacuum launcher shutting down")));
 	AutoVacuumShmem->av_launcherpid = 0;
 
+	/*
+	 * If valgrind'ing, free all the cruft in AutovacMemCxt, to avoid leak
+	 * complaints.
+	 */
+#ifdef USE_VALGRIND
+	MemoryContextSwitchTo(TopMemoryContext);
+	MemoryContextDelete(AutovacMemCxt);
+#endif
+
 	proc_exit(0);				/* done */
 }
 
@@ -2045,11 +2054,12 @@ do_autovacuum(void)
 	/* create hash table for toast <-> main relid mapping */
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(av_relation);
+	ctl.hcxt = AutovacMemCxt;
 
 	table_toast_map = hash_create("TOAST to main relid map",
 								  100,
 								  &ctl,
-								  HASH_ELEM | HASH_BLOBS);
+								  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/*
 	 * Scan pg_class to determine which tables to vacuum.
@@ -2596,11 +2606,6 @@ deleted:
 	}
 	LWLockRelease(AutovacuumLock);
 
-	/*
-	 * We leak table_toast_map here (among other things), but since we're
-	 * going away soon, it's not a problem.
-	 */
-
 	/*
 	 * Update pg_database.datfrozenxid, and truncate pg_xact if possible. We
 	 * only need to do this once, not after each table.
@@ -2626,6 +2631,14 @@ deleted:
 
 	/* Finally close out the last transaction. */
 	CommitTransactionCommand();
+
+	/*
+	 * If valgrind'ing, free all the cruft in AutovacMemCxt, to avoid leak
+	 * complaints.
+	 */
+#ifdef USE_VALGRIND
+	MemoryContextDelete(AutovacMemCxt);
+#endif
 }
 
 /*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e8af05c04e..2814ee7609 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -176,13 +176,13 @@
  */
 typedef struct bkend
 {
+	dlist_node	elem;			/* list link in BackendList */
 	pid_t		pid;			/* process id of backend */
 	int32		cancel_key;		/* cancel key for cancels for this backend */
 	int			child_slot;		/* PMChildSlot for this backend, if any */
 	int			bkend_type;		/* child process flavor, see above */
 	bool		dead_end;		/* is it going to send an error and quit? */
 	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
-	dlist_node	elem;			/* list link in BackendList */
 } Backend;
 
 static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
@@ -2011,6 +2011,7 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 	if (proto == CANCEL_REQUEST_CODE)
 	{
 		processCancelRequest(port, buf);
+		pfree(buf);
 		/* Not really an error, but we don't want to proceed further */
 		return STATUS_ERROR;
 	}
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index f74378110a..021c1b36f3 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -246,9 +246,15 @@ libpqrcv_check_conninfo(const char *conninfo)
 
 	opts = PQconninfoParse(conninfo, &err);
 	if (opts == NULL)
+	{
+		/* The error string is malloc'd, so we must free it explicitly */
+		char	   *errcopy = err ? pstrdup(err) : "out of memory";
+
+		PQfreemem(err);
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("invalid connection string syntax: %s", err)));
+				 errmsg("invalid connection string syntax: %s", errcopy)));
+	}
 
 	PQconninfoFree(opts);
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2b1b68109f..0ceeb8f911 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -176,7 +176,7 @@ static bool RecoveryConflictRetryable = true;
 static ProcSignalReason RecoveryConflictReason;
 
 /* reused buffer to pass to SendRowDescriptionMessage() */
-static MemoryContext row_description_context = NULL;
+static volatile MemoryContext row_description_context = NULL;
 static StringInfoData row_description_buf;
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index 9b9a9afaa8..0b9d8353a9 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -654,6 +654,17 @@ FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
 	return 0;
 }
 
+/*
+ * Context reset/delete callback for a regular-expression AFFIX
+ */
+static void
+regex_affix_deletion_callback(void *arg)
+{
+	aff_regex_struct *affregex = (aff_regex_struct *) arg;
+
+	pg_regfree(&(affregex->regex));
+}
+
 /*
  * Adds a new affix rule to the Affix field.
  *
@@ -716,6 +727,7 @@ NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
 		int			err;
 		pg_wchar   *wmask;
 		char	   *tmask;
+		aff_regex_struct *pregex;
 
 		Affix->issimple = 0;
 		Affix->isregis = 0;
@@ -729,18 +741,32 @@ NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
 		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,
+		/*
+		 * The regex engine stores its stuff using malloc not palloc, so we
+		 * must arrange to explicitly clean up the regex when the dictionary's
+		 * context is cleared.  That means the regex_t has to stay in a fixed
+		 * location within the context; we can't keep it directly in the AFFIX
+		 * struct, since we may sort and resize the array of AFFIXes.
+		 */
+		Affix->reg.pregex = pregex = palloc(sizeof(aff_regex_struct));
+
+		err = pg_regcomp(&(pregex->regex), wmask, wmasklen,
 						 REG_ADVANCED | REG_NOSUB,
 						 DEFAULT_COLLATION_OID);
 		if (err)
 		{
 			char		errstr[100];
 
-			pg_regerror(err, &(Affix->reg.regex), errstr, sizeof(errstr));
+			pg_regerror(err, &(pregex->regex), errstr, sizeof(errstr));
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
 					 errmsg("invalid regular expression: %s", errstr)));
 		}
+
+		pregex->mcallback.func = regex_affix_deletion_callback;
+		pregex->mcallback.arg = (void *) pregex;
+		MemoryContextRegisterResetCallback(CurrentMemoryContext,
+										   &pregex->mcallback);
 	}
 
 	Affix->flagflags = flagflags;
@@ -2133,7 +2159,7 @@ CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *neww
 		data = (pg_wchar *) palloc((newword_len + 1) * sizeof(pg_wchar));
 		data_len = pg_mb2wchar_with_len(newword, data, newword_len);
 
-		if (pg_regexec(&(Affix->reg.regex), data, data_len,
+		if (pg_regexec(&(Affix->reg.pregex->regex), data, data_len,
 					   0, NULL, 0, NULL, 0) == REG_OKAY)
 		{
 			pfree(data);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 55c9445898..2abdd44190 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -814,7 +814,7 @@ InitCatCache(int id,
 	 * Note: we rely on zeroing to initialize all the dlist headers correctly
 	 */
 	sz = sizeof(CatCache) + PG_CACHE_LINE_SIZE;
-	cp = (CatCache *) CACHELINEALIGN(palloc0(sz));
+	cp = (CatCache *) palloc0(sz);
 	cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));
 
 	/*
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 460b720a65..cf2c26a60f 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -111,9 +111,6 @@ BuildEventTriggerCache(void)
 									  (Datum) 0);
 	}
 
-	/* Switch to correct memory context. */
-	oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext);
-
 	/* Prevent the memory context from being nuked while we're rebuilding. */
 	EventTriggerCacheState = ETCS_REBUILD_STARTED;
 
@@ -170,6 +167,9 @@ BuildEventTriggerCache(void)
 		else
 			continue;
 
+		/* Switch to correct memory context. */
+		oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext);
+
 		/* Allocate new cache item. */
 		item = palloc0(sizeof(EventTriggerCacheItem));
 		item->fnoid = form->evtfoid;
@@ -187,6 +187,9 @@ BuildEventTriggerCache(void)
 			entry->triggerlist = lappend(entry->triggerlist, item);
 		else
 			entry->triggerlist = list_make1(item);
+
+		/* Restore previous memory context. */
+		MemoryContextSwitchTo(oldcontext);
 	}
 
 	/* Done with pg_event_trigger scan. */
@@ -194,9 +197,6 @@ BuildEventTriggerCache(void)
 	index_close(irel, AccessShareLock);
 	relation_close(rel, AccessShareLock);
 
-	/* Restore previous memory context. */
-	MemoryContextSwitchTo(oldcontext);
-
 	/* Install new cache. */
 	EventTriggerCache = cache;
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7ef510cd01..d90b2812af 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2392,6 +2392,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	FreeTriggerDesc(relation->trigdesc);
 	list_free_deep(relation->rd_fkeylist);
 	list_free(relation->rd_indexlist);
+	list_free(relation->rd_statlist);
 	bms_free(relation->rd_indexattr);
 	bms_free(relation->rd_keyattr);
 	bms_free(relation->rd_pkattr);
@@ -3542,6 +3543,13 @@ RelationBuildLocalRelation(const char *relname,
 
 	rel->rd_rel->relam = accessmtd;
 
+	/*
+	 * RelationInitTableAccessMethod will do syscache lookups, so we
+	 * mustn't run it in CacheMemoryContext.  Fortunately, the remaining
+	 * steps don't require a long-lived current context.
+	 */
+	MemoryContextSwitchTo(oldcxt);
+
 	if (relkind == RELKIND_RELATION ||
 		relkind == RELKIND_SEQUENCE ||
 		relkind == RELKIND_TOASTVALUE ||
@@ -3565,11 +3573,6 @@ RelationBuildLocalRelation(const char *relname,
 	 */
 	EOXactListAdd(rel);
 
-	/*
-	 * done building relcache entry.
-	 */
-	MemoryContextSwitchTo(oldcxt);
-
 	/* It's fully valid */
 	rel->rd_isvalid = true;
 
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 384107b6ba..a983478f69 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -314,11 +314,18 @@ lookup_ts_dictionary_cache(Oid dictId)
 
 		if (OidIsValid(template->tmplinit))
 		{
+			FmgrInfo	initflinfo;
 			List	   *dictoptions;
 			Datum		opt;
 			bool		isnull;
 			MemoryContext oldcontext;
 
+			/*
+			 * Lookup the init method while using caller's context, else we
+			 * might leak cruft in the private memory context.
+			 */
+			fmgr_info(template->tmplinit, &initflinfo);
+
 			/*
 			 * Init method runs in dictionary's private memory context, and we
 			 * make sure the options are stored there too
@@ -333,9 +340,17 @@ lookup_ts_dictionary_cache(Oid dictId)
 			else
 				dictoptions = deserialize_deflist(opt);
 
+			/*
+			 * We don't currently have any real use for the dictoptions list
+			 * after calling the init method, but the dictionary might
+			 * continue to reference parts of it, so we can't free it.  Keep a
+			 * pointer to it so valgrind doesn't complain that it's leaked.
+			 */
+			entry->dictOptions = dictoptions;
+
 			entry->dictData =
-				DatumGetPointer(OidFunctionCall1(template->tmplinit,
-												 PointerGetDatum(dictoptions)));
+				DatumGetPointer(FunctionCall1(&initflinfo,
+											  PointerGetDatum(dictoptions)));
 
 			MemoryContextSwitchTo(oldcontext);
 		}
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 4915ef5934..570f2986a7 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -1091,9 +1091,6 @@ load_domaintype_info(TypeCacheEntry *typentry)
 				dcc->dccRefCount = 0;
 			}
 
-			/* Create node trees in DomainConstraintCache's context */
-			oldcxt = MemoryContextSwitchTo(dcc->dccContext);
-
 			check_expr = (Expr *) stringToNode(constring);
 
 			/*
@@ -1108,10 +1105,13 @@ load_domaintype_info(TypeCacheEntry *typentry)
 			 */
 			check_expr = expression_planner(check_expr);
 
+			/* Create node trees in DomainConstraintCache's context */
+			oldcxt = MemoryContextSwitchTo(dcc->dccContext);
+
 			r = makeNode(DomainConstraintState);
 			r->constrainttype = DOM_CONSTRAINT_CHECK;
 			r->name = pstrdup(NameStr(c->conname));
-			r->check_expr = check_expr;
+			r->check_expr = copyObject(check_expr);
 			r->check_exprstate = NULL;
 
 			MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 6546e3c7c7..df39bc77df 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -1713,6 +1713,10 @@ element_alloc(HTAB *hashp, int nelem, int freelist_idx)
 	if (hashp->isfixed)
 		return false;
 
+	/* Force separate allocations to de-confuse valgrind */
+	if (!hashp->isshared)
+		nelem = 1;
+
 	/* Each element has a HASHELEMENT header plus user data. */
 	elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(hctl->entrysize);
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 8b73850d0d..be37e8b312 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -944,7 +944,6 @@ UnlinkLockFiles(int status, Datum arg)
 		/* Should we complain if the unlink fails? */
 	}
 	/* Since we're about to exit, no need to reclaim storage */
-	lock_files = NIL;
 
 	/*
 	 * Lock file removal should always be the last externally visible action
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 855076b1fd..c2e662a7b6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10639,10 +10639,12 @@ RestoreGUCState(void *gucstate)
 	int			i;
 	ErrorContextCallback error_context_callback;
 
+#if 0
 	/* See comment at can_skip_gucvar(). */
 	for (i = 0; i < num_guc_variables; i++)
 		if (!can_skip_gucvar(guc_variables[i]))
 			InitializeOneGUCOption(guc_variables[i]);
+#endif
 
 	/* First item is the length of the subsequent data */
 	memcpy(&len, gucstate, sizeof(len));
@@ -10754,6 +10756,8 @@ ProcessGUCArray(ArrayType *array,
 		char	   *s;
 		char	   *name;
 		char	   *value;
+		char	   *namecopy;
+		char	   *valuecopy;
 
 		d = array_ref(array, 1, &i,
 					  -1 /* varlenarray */ ,
@@ -10778,13 +10782,18 @@ ProcessGUCArray(ArrayType *array,
 			continue;
 		}
 
-		(void) set_config_option(name, value,
+		/* free strings immediately to avoid permanent leak if error */
+		namecopy = pstrdup(name);
+		free(name);
+		valuecopy = pstrdup(value);
+		free(value);
+
+		(void) set_config_option(namecopy, valuecopy,
 								 context, source,
 								 action, true, 0, false);
 
-		free(name);
-		if (value)
-			free(value);
+		pfree(namecopy);
+		pfree(valuecopy);
 		pfree(s);
 	}
 }
@@ -11216,34 +11225,50 @@ static bool
 call_string_check_hook(struct config_string *conf, char **newval, void **extra,
 					   GucSource source, int elevel)
 {
+	volatile bool result = true;
+
 	/* Quick success if no hook */
 	if (!conf->check_hook)
 		return true;
 
-	/* Reset variables that might be set by hook */
-	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
-	GUC_check_errmsg_string = NULL;
-	GUC_check_errdetail_string = NULL;
-	GUC_check_errhint_string = NULL;
+	/*
+	 * If elevel is ERROR, or if the check_hook itself throws an elog
+	 * (undesirable, but not always avoidable), make sure we don't leak the
+	 * already-malloc'd newval string.
+	 */
+	PG_TRY();
+	{
+		/* Reset variables that might be set by hook */
+		GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+		GUC_check_errmsg_string = NULL;
+		GUC_check_errdetail_string = NULL;
+		GUC_check_errhint_string = NULL;
 
-	if (!conf->check_hook(newval, extra, source))
+		if (!conf->check_hook(newval, extra, source))
+		{
+			ereport(elevel,
+					(errcode(GUC_check_errcode_value),
+					 GUC_check_errmsg_string ?
+					 errmsg_internal("%s", GUC_check_errmsg_string) :
+					 errmsg("invalid value for parameter \"%s\": \"%s\"",
+							conf->gen.name, *newval ? *newval : ""),
+					 GUC_check_errdetail_string ?
+					 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
+					 GUC_check_errhint_string ?
+					 errhint("%s", GUC_check_errhint_string) : 0));
+			/* Flush any strings created in ErrorContext */
+			FlushErrorState();
+			result = false;
+		}
+	}
+	PG_CATCH();
 	{
-		ereport(elevel,
-				(errcode(GUC_check_errcode_value),
-				 GUC_check_errmsg_string ?
-				 errmsg_internal("%s", GUC_check_errmsg_string) :
-				 errmsg("invalid value for parameter \"%s\": \"%s\"",
-						conf->gen.name, *newval ? *newval : ""),
-				 GUC_check_errdetail_string ?
-				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
-				 GUC_check_errhint_string ?
-				 errhint("%s", GUC_check_errhint_string) : 0));
-		/* Flush any strings created in ErrorContext */
-		FlushErrorState();
-		return false;
+		free(*newval);
+		PG_RE_THROW();
 	}
+	PG_END_TRY();
 
-	return true;
+	return result;
 }
 
 static bool
diff --git a/src/backend/utils/misc/ps_status.c b/src/backend/utils/misc/ps_status.c
index 5819faaf2d..2d0ef37f7f 100644
--- a/src/backend/utils/misc/ps_status.c
+++ b/src/backend/utils/misc/ps_status.c
@@ -113,6 +113,11 @@ static size_t ps_buffer_fixed_size; /* size of the constant prefix */
 static int	save_argc;
 static char **save_argv;
 
+/* We use this to convince Valgrind that replacement environ is referenced */
+#ifdef PS_USE_CLOBBER_ARGV
+static char ** volatile fake_environ;
+#endif
+
 
 /*
  * Call this early in startup to save the original argc/argv values.
@@ -192,6 +197,8 @@ save_ps_display_args(int argc, char **argv)
 		}
 		new_environ[i] = NULL;
 		environ = new_environ;
+		/* Valgrind tends to think this memory is leaked, so fool it */
+		fake_environ = new_environ;
 	}
 #endif							/* PS_USE_CLOBBER_ARGV */
 
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index fc7706314b..f32a13f786 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -32,6 +32,7 @@
  */
 typedef struct RegisteredBgWorker
 {
+	slist_node	rw_lnode;		/* list link (first to placate valgrind) */
 	BackgroundWorker rw_worker; /* its registry entry */
 	struct bkend *rw_backend;	/* its BackendList entry, or NULL */
 	pid_t		rw_pid;			/* 0 if not running */
@@ -39,7 +40,6 @@ typedef struct RegisteredBgWorker
 	TimestampTz rw_crashed_at;	/* if not 0, time it last crashed */
 	int			rw_shmem_slot;
 	bool		rw_terminate;
-	slist_node	rw_lnode;		/* list link */
 } RegisteredBgWorker;
 
 extern slist_head BackgroundWorkerList;
diff --git a/src/include/tsearch/dicts/spell.h b/src/include/tsearch/dicts/spell.h
index 9847e30208..e03ed42372 100644
--- a/src/include/tsearch/dicts/spell.h
+++ b/src/include/tsearch/dicts/spell.h
@@ -81,6 +81,17 @@ typedef struct spell_struct
 
 #define SPELLHDRSZ	(offsetof(SPELL, word))
 
+/*
+ * If an affix uses a regex, we have to store that separately in a struct
+ * that won't move around when arrays of affixes are enlarged or sorted.
+ * This is so that it can be found to be cleaned up at context destruction.
+ */
+typedef struct aff_regex_struct
+{
+	regex_t		regex;
+	MemoryContextCallback mcallback;
+} aff_regex_struct;
+
 /*
  * Represents an entry in an affix list.
  */
@@ -97,7 +108,7 @@ typedef struct aff_struct
 	char	   *repl;
 	union
 	{
-		regex_t		regex;
+		aff_regex_struct *pregex;
 		Regis		regis;
 	}			reg;
 } AFFIX;
diff --git a/src/include/tsearch/ts_cache.h b/src/include/tsearch/ts_cache.h
index 888f7028b1..c5617bab5b 100644
--- a/src/include/tsearch/ts_cache.h
+++ b/src/include/tsearch/ts_cache.h
@@ -59,6 +59,7 @@ typedef struct TSDictionaryCacheEntry
 	FmgrInfo	lexize;
 
 	MemoryContext dictCtx;		/* memory context to store private data */
+	List	   *dictOptions;
 	void	   *dictData;
 } TSDictionaryCacheEntry;
 
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..a304981467 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -85,6 +85,13 @@ typedef struct catcache
 
 typedef struct catctup
 {
+	/*
+	 * Each tuple in a cache is a member of a dlist that stores the elements
+	 * of its hash bucket.  We keep each dlist in LRU order to speed repeated
+	 * lookups.
+	 */
+	dlist_node	cache_elem;		/* list member of per-bucket list */
+
 	int			ct_magic;		/* for identifying CatCTup entries */
 #define CT_MAGIC   0x57261502
 
@@ -96,13 +103,6 @@ typedef struct catctup
 	 */
 	Datum		keys[CATCACHE_MAXKEYS];
 
-	/*
-	 * Each tuple in a cache is a member of a dlist that stores the elements
-	 * of its hash bucket.  We keep each dlist in LRU order to speed repeated
-	 * lookups.
-	 */
-	dlist_node	cache_elem;		/* list member of per-bucket list */
-
 	/*
 	 * A tuple marked "dead" must not be returned by subsequent searches.
 	 * However, it won't be physically deleted from the cache until its
@@ -156,13 +156,13 @@ typedef struct catctup
  */
 typedef struct catclist
 {
+	dlist_node	cache_elem;		/* list member of per-catcache list */
+
 	int			cl_magic;		/* for identifying CatCList entries */
 #define CL_MAGIC   0x52765103
 
 	uint32		hash_value;		/* hash value for lookup keys */
 
-	dlist_node	cache_elem;		/* list member of per-catcache list */
-
 	/*
 	 * Lookup keys for the entry, with the first nkeys elements being valid.
 	 * All by-reference are separately allocated.
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index ff09c63a02..afacd94924 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -95,6 +95,9 @@ extern int	plan_cache_mode;
  */
 typedef struct CachedPlanSource
 {
+	/* If CachedPlanSource has been saved, it is a member of a global list */
+	dlist_node	node;			/* list link, if is_saved */
+
 	int			magic;			/* should equal CACHEDPLANSOURCE_MAGIC */
 	struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
 	const char *query_string;	/* source text of query */
@@ -125,8 +128,6 @@ typedef struct CachedPlanSource
 	bool		is_saved;		/* has CachedPlanSource been "saved"? */
 	bool		is_valid;		/* is the query_list currently valid? */
 	int			generation;		/* increments each time we create a plan */
-	/* If CachedPlanSource has been saved, it is a member of a global list */
-	dlist_node	node;			/* list link, if is_saved */
 	/* State kept to help decide whether to use custom or generic plans: */
 	double		generic_cost;	/* cost of generic plan, or -1 if not known */
 	double		total_custom_cost;	/* total cost of custom plans so far */
@@ -174,6 +175,8 @@ typedef struct CachedPlan
  */
 typedef struct CachedExpression
 {
+	dlist_node	node;			/* link in global list of CachedExpressions */
+
 	int			magic;			/* should equal CACHEDEXPR_MAGIC */
 	Node	   *expr;			/* planned form of expression */
 	bool		is_valid;		/* is the expression still valid? */
@@ -181,7 +184,6 @@ typedef struct CachedExpression
 	List	   *relationOids;	/* OIDs of relations the expr depends on */
 	List	   *invalItems;		/* other dependencies, as PlanInvalItems */
 	MemoryContext context;		/* context containing this CachedExpression */
-	dlist_node	node;			/* link in global list of CachedExpressions */
 } CachedExpression;
 
 
diff --git a/src/tools/valgrind.supp b/src/tools/valgrind.supp
index e3a179d210..c399b787fa 100644
--- a/src/tools/valgrind.supp
+++ b/src/tools/valgrind.supp
@@ -198,3 +198,22 @@
    Memcheck:Cond
    fun:PyObject_Realloc
 }
+
+# Temporary hack to ignore leakage in pl/pgsql compiler
+{
+   ignore-plpgsql-leaks
+   Memcheck:Leak
+   ...
+   fun:do_compile
+}
+
+# Snowball likes to work with a pointer that doesn't point to the start
+# of its palloc chunk; see create_s/lose_s.
+{
+   ignore-snowball-weirdness
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:palloc
+   fun:create_s
+   fun:SN_create_env
+}
