--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -443,6 +443,7 @@ char	   *ConfigFileName;
 char	   *HbaFileName;
 char	   *IdentFileName;
 char	   *external_pid_file;
+char	   *extension_destdir;
 
 char	   *pgstat_temp_directory;
 
@@ -3438,6 +3439,17 @@ static struct config_string ConfigureNam
 	},
 
 	{
+		{"extension_destdir", PGC_SUSET, FILE_LOCATIONS,
+			gettext_noop("Path to prepend for extension loading"),
+			gettext_noop("This directory is prepended to paths when loading extensions (control and SQL files), and to the '$libdir' directive when loading modules that back functions. The location is made configurable to allow build-time testing of extensions that do not have been installed to their proper location yet."),
+			GUC_SUPERUSER_ONLY
+		},
+		&extension_destdir,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
 		{"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
 			gettext_noop("Location of the SSL server certificate file."),
 			NULL
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -110,6 +110,8 @@ static void ApplyExtensionUpdates(Oid ex
 					  const char *initialVersion,
 					  List *updateVersions);
 static char *read_whole_file(const char *filename, int *length);
+static bool file_exists(const char *name);
+static bool directory_exists(const char *dir);
 
 
 /*
@@ -370,6 +372,16 @@ get_extension_control_filename(const cha
 
 	get_share_path(my_exec_path, sharepath);
 	result = (char *) palloc(MAXPGPATH);
+	/*
+	 * If extension_destdir is set, try to find the file there first
+	 */
+	if (*extension_destdir != '\0')
+	{
+		snprintf(result, MAXPGPATH, "%s%s/extension/%s.control",
+				 extension_destdir, sharepath, extname);
+		if (file_exists(result))
+			return result;
+	}
 	snprintf(result, MAXPGPATH, "%s/extension/%s.control",
 			 sharepath, extname);
 
@@ -409,6 +421,16 @@ get_extension_aux_control_filename(Exten
 	scriptdir = get_extension_script_directory(control);
 
 	result = (char *) palloc(MAXPGPATH);
+	/*
+	 * If extension_destdir is set, try to find the file there first
+	 */
+	if (*extension_destdir != '\0')
+	{
+		snprintf(result, MAXPGPATH, "%s%s/%s--%s.control",
+				 extension_destdir, scriptdir, control->name, version);
+		if (file_exists(result))
+			return result;
+	}
 	snprintf(result, MAXPGPATH, "%s/%s--%s.control",
 			 scriptdir, control->name, version);
 
@@ -427,6 +449,23 @@ get_extension_script_filename(ExtensionC
 	scriptdir = get_extension_script_directory(control);
 
 	result = (char *) palloc(MAXPGPATH);
+	/*
+	 * If extension_destdir is set, try to find the file there first
+	 */
+	if (*extension_destdir != '\0')
+	{
+		if (from_version)
+			snprintf(result, MAXPGPATH, "%s%s/%s--%s--%s.sql",
+					 extension_destdir, scriptdir, control->name, from_version, version);
+		else
+			snprintf(result, MAXPGPATH, "%s%s/%s--%s.sql",
+					 extension_destdir, scriptdir, control->name, version);
+		if (file_exists(result))
+		{
+			pfree(scriptdir);
+			return result;
+		}
+	}
 	if (from_version)
 		snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
 				 scriptdir, control->name, from_version, version);
@@ -1017,6 +1056,59 @@ get_ext_ver_list(ExtensionControlFile *c
 	DIR		   *dir;
 	struct dirent *de;
 
+	/*
+	 * If extension_destdir is set, try to find the files there first
+	 */
+	if (*extension_destdir != '\0')
+	{
+		char		location[MAXPGPATH];
+
+		snprintf(location, MAXPGPATH, "%s%s", extension_destdir,
+				get_extension_script_directory(control));
+		dir = AllocateDir(location);
+		while ((de = ReadDir(dir, location)) != NULL)
+		{
+			char	   *vername;
+			char	   *vername2;
+			ExtensionVersionInfo *evi;
+			ExtensionVersionInfo *evi2;
+
+			/* must be a .sql file ... */
+			if (!is_extension_script_filename(de->d_name))
+				continue;
+
+			/* ... matching extension name followed by separator */
+			if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
+				de->d_name[extnamelen] != '-' ||
+				de->d_name[extnamelen + 1] != '-')
+				continue;
+
+			/* extract version name(s) from 'extname--something.sql' filename */
+			vername = pstrdup(de->d_name + extnamelen + 2);
+			*strrchr(vername, '.') = '\0';
+			vername2 = strstr(vername, "--");
+			if (!vername2)
+			{
+				/* It's an install, not update, script; record its version name */
+				evi = get_ext_ver_info(vername, &evi_list);
+				evi->installable = true;
+				continue;
+			}
+			*vername2 = '\0';		/* terminate first version */
+			vername2 += 2;			/* and point to second */
+
+			/* if there's a third --, it's bogus, ignore it */
+			if (strstr(vername2, "--"))
+				continue;
+
+			/* Create ExtensionVersionInfos and link them together */
+			evi = get_ext_ver_info(vername, &evi_list);
+			evi2 = get_ext_ver_info(vername2, &evi_list);
+			evi->reachable = lappend(evi->reachable, evi2);
+		}
+		FreeDir(dir);
+	}
+
 	location = get_extension_script_directory(control);
 	dir = AllocateDir(location);
 	while ((de = ReadDir(dir, location)) != NULL)
@@ -3171,3 +3263,32 @@ read_whole_file(const char *filename, in
 	buf[*length] = '\0';
 	return buf;
 }
+
+static bool
+file_exists(const char *name)
+{
+	struct stat st;
+
+	AssertArg(name != NULL);
+
+	if (stat(name, &st) == 0)
+		return S_ISDIR(st.st_mode) ? false : true;
+	else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not access file \"%s\": %m", name)));
+
+	return false;
+}
+
+static bool
+directory_exists(const char *dir)
+{
+	struct stat st;
+
+	if (stat(dir, &st) != 0)
+		return false;
+	if (S_ISDIR(st.st_mode))
+		return true;
+	return false;
+}
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -262,6 +262,7 @@ extern PGDLLIMPORT char *ConfigFileName;
 extern char *HbaFileName;
 extern char *IdentFileName;
 extern char *external_pid_file;
+extern char *extension_destdir;
 
 extern PGDLLIMPORT char *application_name;
 
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -21,6 +21,7 @@
 #include "miscadmin.h"
 #include "storage/shmem.h"
 #include "utils/dynamic_loader.h"
+#include "utils/guc.h"
 #include "utils/hsearch.h"
 
 
@@ -475,7 +476,7 @@ expand_dynamic_library_name(const char *
 {
 	bool		have_slash;
 	char	   *new;
-	char	   *full;
+	char	   *full, *full2;
 
 	AssertArg(name);
 
@@ -490,6 +491,19 @@ expand_dynamic_library_name(const char *
 	else
 	{
 		full = substitute_libpath_macro(name);
+		/*
+		 * If extension_destdir is set, try to find the file there first
+		 */
+		if (*extension_destdir != '\0')
+		{
+			full2 = psprintf("%s%s", extension_destdir, full);
+			if (file_exists(full2))
+			{
+				pfree(full);
+				return full2;
+			}
+			pfree(full2);
+		}
 		if (file_exists(full))
 			return full;
 		pfree(full);
@@ -508,6 +522,19 @@ expand_dynamic_library_name(const char *
 	{
 		full = substitute_libpath_macro(new);
 		pfree(new);
+		/*
+		 * If extension_destdir is set, try to find the file there first
+		 */
+		if (*extension_destdir != '\0')
+		{
+			full2 = psprintf("%s%s", extension_destdir, full);
+			if (file_exists(full2))
+			{
+				pfree(full);
+				return full2;
+			}
+			pfree(full2);
+		}
 		if (file_exists(full))
 			return full;
 		pfree(full);
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -577,6 +577,8 @@
 # - Other Defaults -
 
 #dynamic_library_path = '$libdir'
+#extension_destdir = ''			# prepend path when loading extensions
+					# and shared objects (added by Debian)
 #local_preload_libraries = ''
 #session_preload_libraries = ''
 
