From 63b656b7bf39beb5b6095ab2b5e1e4e9d8c6a965 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 4 Mar 2011 21:51:14 -0500
Subject: [PATCH] Create extension infrastructure for the core procedural
 languages.

This mostly just involves creating control, install, and
update-from-unpackaged scripts for them.  However, I had to adjust plperl
and plpython to not share the same support functions between variants,
because we can't put the same function into multiple extensions.

catversion bump forced due to new contents of pg_pltemplate, and because
initdb now installs plpgsql as an extension not a bare language.

Add support for regression testing these as extensions not bare
languages.

Fix a couple of other issues that popped up while testing this: my initial
hack at pg_dump binary-upgrade support didn't work right, and we don't want
an extra schema permissions test after all.

Documentation changes still to come, but I'm committing now to see
whether the MSVC build scripts need work (likely they do).
---
 src/backend/commands/extension.c              | 13 +++---
 src/bin/initdb/initdb.c                       |  2 +-
 src/bin/pg_dump/pg_dump.c                     | 44 +++++++++++++------
 src/include/catalog/catversion.h              |  2 +-
 src/include/catalog/pg_pltemplate.h           |  4 +-
 src/pl/plperl/GNUmakefile                     | 23 ++++++++--
 src/pl/plperl/plperl--1.0.sql                 |  9 ++++
 src/pl/plperl/plperl--unpackaged--1.0.sql     |  7 +++
 src/pl/plperl/plperl.c                        | 36 +++++++++++++++
 src/pl/plperl/plperl.control                  |  7 +++
 src/pl/plperl/plperlu--1.0.sql                |  9 ++++
 src/pl/plperl/plperlu--unpackaged--1.0.sql    |  7 +++
 src/pl/plperl/plperlu.control                 |  7 +++
 src/pl/plpgsql/src/Makefile                   | 20 +++++++--
 src/pl/plpgsql/src/plpgsql--1.0.sql           |  9 ++++
 .../plpgsql/src/plpgsql--unpackaged--1.0.sql  |  7 +++
 src/pl/plpgsql/src/plpgsql.control            |  7 +++
 src/pl/plpython/Makefile                      | 27 ++++++++++--
 src/pl/plpython/expected/plpython_drop.out    |  3 +-
 src/pl/plpython/expected/plpython_test.out    |  2 +-
 src/pl/plpython/plpython.c                    | 33 ++++++++++++++
 src/pl/plpython/plpython2u--1.0.sql           |  9 ++++
 .../plpython/plpython2u--unpackaged--1.0.sql  |  7 +++
 src/pl/plpython/plpython2u.control            |  7 +++
 src/pl/plpython/plpython3u--1.0.sql           |  9 ++++
 .../plpython/plpython3u--unpackaged--1.0.sql  |  7 +++
 src/pl/plpython/plpython3u.control            |  7 +++
 src/pl/plpython/plpythonu--1.0.sql            |  9 ++++
 .../plpython/plpythonu--unpackaged--1.0.sql   |  7 +++
 src/pl/plpython/plpythonu.control             |  7 +++
 src/pl/plpython/sql/plpython_drop.sql         |  4 +-
 src/pl/plpython/sql/plpython_test.sql         |  2 +-
 src/pl/tcl/Makefile                           | 26 +++++++++--
 src/pl/tcl/pltcl--1.0.sql                     |  9 ++++
 src/pl/tcl/pltcl--unpackaged--1.0.sql         |  5 +++
 src/pl/tcl/pltcl.control                      |  7 +++
 src/pl/tcl/pltclu--1.0.sql                    |  9 ++++
 src/pl/tcl/pltclu--unpackaged--1.0.sql        |  5 +++
 src/pl/tcl/pltclu.control                     |  7 +++
 src/test/regress/pg_regress.c                 | 17 +++++++
 src/tools/msvc/vcregress.pl                   |  4 +-
 41 files changed, 399 insertions(+), 43 deletions(-)
 create mode 100644 src/pl/plperl/plperl--1.0.sql
 create mode 100644 src/pl/plperl/plperl--unpackaged--1.0.sql
 create mode 100644 src/pl/plperl/plperl.control
 create mode 100644 src/pl/plperl/plperlu--1.0.sql
 create mode 100644 src/pl/plperl/plperlu--unpackaged--1.0.sql
 create mode 100644 src/pl/plperl/plperlu.control
 create mode 100644 src/pl/plpgsql/src/plpgsql--1.0.sql
 create mode 100644 src/pl/plpgsql/src/plpgsql--unpackaged--1.0.sql
 create mode 100644 src/pl/plpgsql/src/plpgsql.control
 create mode 100644 src/pl/plpython/plpython2u--1.0.sql
 create mode 100644 src/pl/plpython/plpython2u--unpackaged--1.0.sql
 create mode 100644 src/pl/plpython/plpython2u.control
 create mode 100644 src/pl/plpython/plpython3u--1.0.sql
 create mode 100644 src/pl/plpython/plpython3u--unpackaged--1.0.sql
 create mode 100644 src/pl/plpython/plpython3u.control
 create mode 100644 src/pl/plpython/plpythonu--1.0.sql
 create mode 100644 src/pl/plpython/plpythonu--unpackaged--1.0.sql
 create mode 100644 src/pl/plpython/plpythonu.control
 create mode 100644 src/pl/tcl/pltcl--1.0.sql
 create mode 100644 src/pl/tcl/pltcl--unpackaged--1.0.sql
 create mode 100644 src/pl/tcl/pltcl.control
 create mode 100644 src/pl/tcl/pltclu--1.0.sql
 create mode 100644 src/pl/tcl/pltclu--unpackaged--1.0.sql
 create mode 100644 src/pl/tcl/pltclu.control

diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 6ff30ddc0ae..b1673e6549a 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1188,7 +1188,6 @@ CreateExtension(CreateExtensionStmt *stmt)
 	List	   *requiredExtensions;
 	List	   *requiredSchemas;
 	Oid			extensionOid;
-	AclResult	aclresult;
 	ListCell   *lc;
 
 	/* Check extension name validity before any filesystem access */
@@ -1393,13 +1392,13 @@ CreateExtension(CreateExtensionStmt *stmt)
 	}
 
 	/*
-	 * Check we have creation rights in target namespace.  Although strictly
-	 * speaking the extension itself isn't in the schema, it will almost
-	 * certainly want to create objects therein, so let's just check now.
+	 * We don't check creation rights on the target namespace here.  If the
+	 * extension script actually creates any objects there, it will fail if
+	 * the user doesn't have such permissions.  But there are cases such as
+	 * procedural languages where it's convenient to set schema = pg_catalog
+	 * yet we don't want to restrict the command to users with ACL_CREATE
+	 * for pg_catalog.
 	 */
-	aclresult = pg_namespace_aclcheck(schemaOid, extowner, ACL_CREATE);
-	if (aclresult != ACLCHECK_OK)
-		aclcheck_error(aclresult, ACL_KIND_NAMESPACE, schemaName);
 
 	/*
 	 * Look up the prerequisite extensions, and build lists of their OIDs
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 98915c4b891..ce24d6101d1 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1912,7 +1912,7 @@ load_plpgsql(void)
 
 	PG_CMD_OPEN;
 
-	PG_CMD_PUTS("CREATE LANGUAGE plpgsql;\n");
+	PG_CMD_PUTS("CREATE EXTENSION plpgsql;\n");
 
 	PG_CMD_CLOSE;
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ea1818b1d57..dfbdcadd145 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1171,6 +1171,24 @@ selectDumpableDefaultACL(DefaultACLInfo *dinfo)
 		dinfo->dobj.dump = include_everything;
 }
 
+/*
+ * selectDumpableExtension: policy-setting subroutine
+ *		Mark an extension as to be dumped or not
+ *
+ * Normally, we just dump all extensions.  However, in binary-upgrade mode
+ * it's necessary to skip built-in extensions, since we assume those will
+ * already be installed in the target database.  We identify such extensions
+ * by their having OIDs in the range reserved for initdb.
+ */
+static void
+selectDumpableExtension(ExtensionInfo *extinfo)
+{
+	if (binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
+		extinfo->dobj.dump = false;
+	else
+		extinfo->dobj.dump = true;
+}
+
 /*
  * selectDumpableObject: policy-setting subroutine
  *		Mark a generic dumpable object as to be dumped or not
@@ -2730,6 +2748,9 @@ getExtensions(int *numExtensions)
 		extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
 		extinfo[i].extconfig = strdup(PQgetvalue(res, i, i_extconfig));
 		extinfo[i].extcondition = strdup(PQgetvalue(res, i, i_extcondition));
+
+		/* Decide whether we want to dump it */
+		selectDumpableExtension(&(extinfo[i]));
 	}
 
 	PQclear(res);
@@ -7042,19 +7063,6 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo)
 	if (!extinfo->dobj.dump || dataOnly)
 		return;
 
-	/*
-	 * In a regular dump, we use IF NOT EXISTS so that there isn't a problem
-	 * if the extension already exists in the target database; this is
-	 * essential for installed-by-default extensions such as plpgsql.
-	 *
-	 * In binary-upgrade mode, that doesn't work well, so instead we skip
-	 * extensions with OIDs less than FirstNormalObjectId; those were
-	 * presumably installed by initdb, and we assume they'll exist in the
-	 * target installation too.
-	 */
-	if (binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
-		return;
-
 	q = createPQExpBuffer();
 	delq = createPQExpBuffer();
 	labelq = createPQExpBuffer();
@@ -7065,6 +7073,16 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo)
 
 	if (!binary_upgrade)
 	{
+		/*
+		 * In a regular dump, we use IF NOT EXISTS so that there isn't a
+		 * problem if the extension already exists in the target database;
+		 * this is essential for installed-by-default extensions such as
+		 * plpgsql.
+		 *
+		 * In binary-upgrade mode, that doesn't work well, so instead we skip
+		 * built-in extensions based on their OIDs; see
+		 * selectDumpableExtension.
+		 */
 		appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
 						  qextname, fmtId(extinfo->namespace));
 	}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index fe7ccf4ba0b..56fb4931ba7 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201103041
+#define CATALOG_VERSION_NO	201103042
 
 #endif
diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h
index c0578f92c09..fa086525e7f 100644
--- a/src/include/catalog/pg_pltemplate.h
+++ b/src/include/catalog/pg_pltemplate.h
@@ -71,9 +71,9 @@ DATA(insert ( "plpgsql"		t t "plpgsql_call_handler" "plpgsql_inline_handler" "pl
 DATA(insert ( "pltcl"		t t "pltcl_call_handler" _null_ _null_ "$libdir/pltcl" _null_ ));
 DATA(insert ( "pltclu"		f f "pltclu_call_handler" _null_ _null_ "$libdir/pltcl" _null_ ));
 DATA(insert ( "plperl"		t t "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
-DATA(insert ( "plperlu"		f f "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
+DATA(insert ( "plperlu"		f f "plperlu_call_handler" "plperlu_inline_handler" "plperlu_validator" "$libdir/plperl" _null_ ));
 DATA(insert ( "plpythonu"	f f "plpython_call_handler" "plpython_inline_handler" "plpython_validator" "$libdir/plpython" _null_ ));
-DATA(insert ( "plpython2u"	f f "plpython_call_handler" "plpython_inline_handler" "plpython_validator" "$libdir/plpython2" _null_ ));
+DATA(insert ( "plpython2u"	f f "plpython2_call_handler" "plpython2_inline_handler" "plpython2_validator" "$libdir/plpython2" _null_ ));
 DATA(insert ( "plpython3u"	f f "plpython3_call_handler" "plpython3_inline_handler" "plpython3_validator" "$libdir/plpython3" _null_ ));
 
 #endif   /* PG_PLTEMPLATE_H */
diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile
index e86cb84dba2..71e2cef4c5e 100644
--- a/src/pl/plperl/GNUmakefile
+++ b/src/pl/plperl/GNUmakefile
@@ -36,11 +36,14 @@ NAME = plperl
 
 OBJS = plperl.o SPI.o Util.o
 
+DATA = plperl.control plperl--1.0.sql plperl--unpackaged--1.0.sql \
+       plperlu.control plperlu--1.0.sql plperlu--unpackaged--1.0.sql
+
 PERLCHUNKS = plc_perlboot.pl plc_trusted.pl
 
 SHLIB_LINK = $(perl_embed_ldflags)
 
-REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plperl  --load-language=plperlu
+REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=plperl  --load-extension=plperlu
 REGRESS = plperl plperl_trigger plperl_shared plperl_elog plperl_util plperl_init plperlu plperl_array
 # if Perl can support two interpreters in one backend,
 # test plperl-and-plperlu cases
@@ -70,11 +73,25 @@ SPI.c: SPI.xs
 Util.c: Util.xs
 	$(PERL) $(perl_privlibexp)/ExtUtils/xsubpp -typemap $(perl_privlibexp)/ExtUtils/typemap $< >$@
 
-install: all installdirs install-lib
+
+install: all installdirs install-lib install-data
 
 installdirs: installdirs-lib
+	$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
+
+uninstall: uninstall-lib uninstall-data
+
+install-data:
+	@for file in $(addprefix $(srcdir)/, $(DATA)); do \
+	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'"; \
+	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'; \
+	done
+
+uninstall-data:
+	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
+
+.PHONY: install-data uninstall-data
 
-uninstall: uninstall-lib
 
 check: submake
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
diff --git a/src/pl/plperl/plperl--1.0.sql b/src/pl/plperl/plperl--1.0.sql
new file mode 100644
index 00000000000..befd8827455
--- /dev/null
+++ b/src/pl/plperl/plperl--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plperl/plperl--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plperl;
diff --git a/src/pl/plperl/plperl--unpackaged--1.0.sql b/src/pl/plperl/plperl--unpackaged--1.0.sql
new file mode 100644
index 00000000000..b062bd5d9b3
--- /dev/null
+++ b/src/pl/plperl/plperl--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plperl/plperl--unpackaged--1.0.sql */
+
+ALTER EXTENSION plperl ADD PROCEDURAL LANGUAGE plperl;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plperl ADD FUNCTION plperl_call_handler();
+ALTER EXTENSION plperl ADD FUNCTION plperl_inline_handler(internal);
+ALTER EXTENSION plperl ADD FUNCTION plperl_validator(oid);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 72e1e5106a6..9a94b3f0852 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -222,6 +222,9 @@ static plperl_call_data *current_call_data = NULL;
 Datum		plperl_call_handler(PG_FUNCTION_ARGS);
 Datum		plperl_inline_handler(PG_FUNCTION_ARGS);
 Datum		plperl_validator(PG_FUNCTION_ARGS);
+Datum		plperlu_call_handler(PG_FUNCTION_ARGS);
+Datum		plperlu_inline_handler(PG_FUNCTION_ARGS);
+Datum		plperlu_validator(PG_FUNCTION_ARGS);
 void		_PG_init(void);
 
 static PerlInterpreter *plperl_init_interp(void);
@@ -1758,6 +1761,39 @@ plperl_validator(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * plperlu likewise requires three externally visible functions:
+ * plperlu_call_handler, plperlu_inline_handler, and plperlu_validator.
+ * These are currently just aliases that send control to the plperl
+ * handler functions, and we decide whether a particular function is
+ * trusted or not by inspecting the actual pg_language tuple.
+ */
+
+PG_FUNCTION_INFO_V1(plperlu_call_handler);
+
+Datum
+plperlu_call_handler(PG_FUNCTION_ARGS)
+{
+	return plperl_call_handler(fcinfo);
+}
+
+PG_FUNCTION_INFO_V1(plperlu_inline_handler);
+
+Datum
+plperlu_inline_handler(PG_FUNCTION_ARGS)
+{
+	return plperl_inline_handler(fcinfo);
+}
+
+PG_FUNCTION_INFO_V1(plperlu_validator);
+
+Datum
+plperlu_validator(PG_FUNCTION_ARGS)
+{
+	return plperl_validator(fcinfo);
+}
+
+
 /*
  * Uses mksafefunc/mkunsafefunc to create a subroutine whose text is
  * supplied in s, and returns a reference to it
diff --git a/src/pl/plperl/plperl.control b/src/pl/plperl/plperl.control
new file mode 100644
index 00000000000..6faace12fa5
--- /dev/null
+++ b/src/pl/plperl/plperl.control
@@ -0,0 +1,7 @@
+# plperl extension
+comment = 'PL/Perl procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plperl'
+relocatable = false
+schema = pg_catalog
+superuser = false
diff --git a/src/pl/plperl/plperlu--1.0.sql b/src/pl/plperl/plperlu--1.0.sql
new file mode 100644
index 00000000000..025f7957c4d
--- /dev/null
+++ b/src/pl/plperl/plperlu--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plperl/plperlu--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plperlu;
diff --git a/src/pl/plperl/plperlu--unpackaged--1.0.sql b/src/pl/plperl/plperlu--unpackaged--1.0.sql
new file mode 100644
index 00000000000..bc62d36a3d8
--- /dev/null
+++ b/src/pl/plperl/plperlu--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plperl/plperlu--unpackaged--1.0.sql */
+
+ALTER EXTENSION plperlu ADD PROCEDURAL LANGUAGE plperlu;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plperlu ADD FUNCTION plperlu_call_handler();
+ALTER EXTENSION plperlu ADD FUNCTION plperlu_inline_handler(internal);
+ALTER EXTENSION plperlu ADD FUNCTION plperlu_validator(oid);
diff --git a/src/pl/plperl/plperlu.control b/src/pl/plperl/plperlu.control
new file mode 100644
index 00000000000..69473caed47
--- /dev/null
+++ b/src/pl/plperl/plperlu.control
@@ -0,0 +1,7 @@
+# plperlu extension
+comment = 'PL/PerlU untrusted procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plperl'
+relocatable = false
+schema = pg_catalog
+superuser = true
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile
index a5f161dc62d..d748ef68262 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -1,6 +1,6 @@
 #-------------------------------------------------------------------------
 #
-# Makefile for the plpgsql shared object
+# Makefile for the pl/pgsql procedural language
 #
 # src/pl/plpgsql/src/Makefile
 #
@@ -19,17 +19,31 @@ rpath =
 
 OBJS = pl_gram.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o pl_scanner.o
 
+DATA = plpgsql.control plpgsql--1.0.sql plpgsql--unpackaged--1.0.sql
+
 all: all-lib
 
 # Shared library stuff
 include $(top_srcdir)/src/Makefile.shlib
 
 
-install: installdirs all install-lib
+install: all installdirs install-lib install-data
 
 installdirs: installdirs-lib
+	$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
+
+uninstall: uninstall-lib uninstall-data
+
+install-data:
+	@for file in $(addprefix $(srcdir)/, $(DATA)); do \
+	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'"; \
+	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'; \
+	done
+
+uninstall-data:
+	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
 
-uninstall: uninstall-lib
+.PHONY: install-data uninstall-data
 
 
 # Force these dependencies to be known even without dependency info built:
diff --git a/src/pl/plpgsql/src/plpgsql--1.0.sql b/src/pl/plpgsql/src/plpgsql--1.0.sql
new file mode 100644
index 00000000000..514562d70fe
--- /dev/null
+++ b/src/pl/plpgsql/src/plpgsql--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plpgsql/src/plpgsql--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plpgsql;
diff --git a/src/pl/plpgsql/src/plpgsql--unpackaged--1.0.sql b/src/pl/plpgsql/src/plpgsql--unpackaged--1.0.sql
new file mode 100644
index 00000000000..9de7e8392aa
--- /dev/null
+++ b/src/pl/plpgsql/src/plpgsql--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plpgsql/src/plpgsql--unpackaged--1.0.sql */
+
+ALTER EXTENSION plpgsql ADD PROCEDURAL LANGUAGE plpgsql;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plpgsql ADD FUNCTION plpgsql_call_handler();
+ALTER EXTENSION plpgsql ADD FUNCTION plpgsql_inline_handler(internal);
+ALTER EXTENSION plpgsql ADD FUNCTION plpgsql_validator(oid);
diff --git a/src/pl/plpgsql/src/plpgsql.control b/src/pl/plpgsql/src/plpgsql.control
new file mode 100644
index 00000000000..b320227b120
--- /dev/null
+++ b/src/pl/plpgsql/src/plpgsql.control
@@ -0,0 +1,7 @@
+# plpgsql extension
+comment = 'PL/pgSQL procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plpgsql'
+relocatable = false
+schema = pg_catalog
+superuser = false
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index e6b6ed3ca52..46e01951424 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -37,8 +37,13 @@ override CPPFLAGS := -I. -I$(srcdir) $(python_includespec) $(CPPFLAGS)
 rpathdir = $(python_libdir)
 
 NAME = plpython$(python_majorversion)
+
 OBJS = plpython.o
 
+DATA = plpythonu.control plpythonu--1.0.sql plpythonu--unpackaged--1.0.sql \
+       plpython2u.control plpython2u--1.0.sql plpython2u--unpackaged--1.0.sql \
+       plpython3u.control plpython3u--1.0.sql plpython3u--unpackaged--1.0.sql
+
 
 # Python on win32 ships with import libraries only for Microsoft Visual C++,
 # which are not compatible with mingw gcc. Therefore we need to build a
@@ -60,7 +65,7 @@ REGRESS_OPTS = --dbname=$(PL_TESTDB)
 # Only load plpythonu with Python 2.  The test files themselves load
 # the versioned language plpython(2|3)u.
 ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-language=plpythonu
+REGRESS_OPTS += --load-extension=plpythonu
 endif
 REGRESS = \
 	plpython_schema \
@@ -98,18 +103,32 @@ all: all-lib
 
 distprep: spiexceptions.h
 
-install: all installdirs install-lib
+
+install: all installdirs install-lib install-data
 ifeq ($(python_majorversion),2)
 	cd '$(DESTDIR)$(pkglibdir)' && rm -f plpython$(DLSUFFIX) && $(LN_S) $(shlib) plpython$(DLSUFFIX)
 endif
 
 installdirs: installdirs-lib
+	$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
 
-uninstall: uninstall-lib
+uninstall: uninstall-lib uninstall-data
 ifeq ($(python_majorversion),2)
 	rm -f '$(DESTDIR)$(pkglibdir)/plpython$(DLSUFFIX)'
 endif
 
+install-data:
+	@for file in $(addprefix $(srcdir)/, $(DATA)); do \
+	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'"; \
+	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'; \
+	done
+
+uninstall-data:
+	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
+
+.PHONY: install-data uninstall-data
+
+
 ifeq ($(python_majorversion),3)
 # Adjust regression tests for Python 3 compatibility
 prep3:
@@ -124,6 +143,8 @@ prep3:
 	      -e "s/def next/def __next__/g" \
 	      -e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
 	      -e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
+	      -e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
+	      -e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
 	    $$file >`echo $$file | sed 's,$(srcdir),python3,'`; \
 	done
 
diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out
index fef642f7c06..a0e3b5c4ef6 100644
--- a/src/pl/plpython/expected/plpython_drop.out
+++ b/src/pl/plpython/expected/plpython_drop.out
@@ -2,4 +2,5 @@
 -- For paranoia's sake, don't leave an untrusted language sitting around
 --
 SET client_min_messages = WARNING;
-DROP PROCEDURAL LANGUAGE plpythonu CASCADE;
+DROP EXTENSION plpythonu CASCADE;
+DROP EXTENSION IF EXISTS plpython2u CASCADE;
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 7b2e1703f4b..c2358b452d9 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -1,5 +1,5 @@
 -- first some tests of basic functionality
-CREATE LANGUAGE plpython2u;
+CREATE EXTENSION plpython2u;
 -- really stupid function just to get the module loaded
 CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
 select stupid();
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 75f7b5cf57f..e415aa36f41 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -4652,3 +4652,36 @@ PLyUnicode_FromString(const char *s)
 }
 
 #endif   /* PY_MAJOR_VERSION >= 3 */
+
+#if PY_MAJOR_VERSION < 3
+
+/* Define aliases plpython2_call_handler etc */
+Datum		plpython2_call_handler(PG_FUNCTION_ARGS);
+Datum		plpython2_inline_handler(PG_FUNCTION_ARGS);
+Datum		plpython2_validator(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(plpython2_call_handler);
+
+Datum
+plpython2_call_handler(PG_FUNCTION_ARGS)
+{
+	return plpython_call_handler(fcinfo);
+}
+
+PG_FUNCTION_INFO_V1(plpython2_inline_handler);
+
+Datum
+plpython2_inline_handler(PG_FUNCTION_ARGS)
+{
+	return plpython_inline_handler(fcinfo);
+}
+
+PG_FUNCTION_INFO_V1(plpython2_validator);
+
+Datum
+plpython2_validator(PG_FUNCTION_ARGS)
+{
+	return plpython_validator(fcinfo);
+}
+
+#endif   /* PY_MAJOR_VERSION < 3 */
diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql
new file mode 100644
index 00000000000..0e478765020
--- /dev/null
+++ b/src/pl/plpython/plpython2u--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plpython/plpython2u--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plpython2u;
diff --git a/src/pl/plpython/plpython2u--unpackaged--1.0.sql b/src/pl/plpython/plpython2u--unpackaged--1.0.sql
new file mode 100644
index 00000000000..a89d8b4d09e
--- /dev/null
+++ b/src/pl/plpython/plpython2u--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plpython/plpython2u--unpackaged--1.0.sql */
+
+ALTER EXTENSION plpython2u ADD PROCEDURAL LANGUAGE plpython2u;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plpython2u ADD FUNCTION plpython2_call_handler();
+ALTER EXTENSION plpython2u ADD FUNCTION plpython2_inline_handler(internal);
+ALTER EXTENSION plpython2u ADD FUNCTION plpython2_validator(oid);
diff --git a/src/pl/plpython/plpython2u.control b/src/pl/plpython/plpython2u.control
new file mode 100644
index 00000000000..39c2b791efe
--- /dev/null
+++ b/src/pl/plpython/plpython2u.control
@@ -0,0 +1,7 @@
+# plpython2u extension
+comment = 'PL/Python2U untrusted procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plpython2'
+relocatable = false
+schema = pg_catalog
+superuser = true
diff --git a/src/pl/plpython/plpython3u--1.0.sql b/src/pl/plpython/plpython3u--1.0.sql
new file mode 100644
index 00000000000..d5c6e5ab96a
--- /dev/null
+++ b/src/pl/plpython/plpython3u--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plpython/plpython3u--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plpython3u;
diff --git a/src/pl/plpython/plpython3u--unpackaged--1.0.sql b/src/pl/plpython/plpython3u--unpackaged--1.0.sql
new file mode 100644
index 00000000000..b1c0d03a304
--- /dev/null
+++ b/src/pl/plpython/plpython3u--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plpython/plpython3u--unpackaged--1.0.sql */
+
+ALTER EXTENSION plpython3u ADD PROCEDURAL LANGUAGE plpython3u;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plpython3u ADD FUNCTION plpython3_call_handler();
+ALTER EXTENSION plpython3u ADD FUNCTION plpython3_inline_handler(internal);
+ALTER EXTENSION plpython3u ADD FUNCTION plpython3_validator(oid);
diff --git a/src/pl/plpython/plpython3u.control b/src/pl/plpython/plpython3u.control
new file mode 100644
index 00000000000..01905ef398b
--- /dev/null
+++ b/src/pl/plpython/plpython3u.control
@@ -0,0 +1,7 @@
+# plpython3u extension
+comment = 'PL/Python3U untrusted procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plpython3'
+relocatable = false
+schema = pg_catalog
+superuser = true
diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql
new file mode 100644
index 00000000000..beb0aa16457
--- /dev/null
+++ b/src/pl/plpython/plpythonu--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/plpython/plpythonu--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE plpythonu;
diff --git a/src/pl/plpython/plpythonu--unpackaged--1.0.sql b/src/pl/plpython/plpythonu--unpackaged--1.0.sql
new file mode 100644
index 00000000000..79262332508
--- /dev/null
+++ b/src/pl/plpython/plpythonu--unpackaged--1.0.sql
@@ -0,0 +1,7 @@
+/* src/pl/plpython/plpythonu--unpackaged--1.0.sql */
+
+ALTER EXTENSION plpythonu ADD PROCEDURAL LANGUAGE plpythonu;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION plpythonu ADD FUNCTION plpython_call_handler();
+ALTER EXTENSION plpythonu ADD FUNCTION plpython_inline_handler(internal);
+ALTER EXTENSION plpythonu ADD FUNCTION plpython_validator(oid);
diff --git a/src/pl/plpython/plpythonu.control b/src/pl/plpython/plpythonu.control
new file mode 100644
index 00000000000..ae91b1c255c
--- /dev/null
+++ b/src/pl/plpython/plpythonu.control
@@ -0,0 +1,7 @@
+# plpythonu extension
+comment = 'PL/PythonU untrusted procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/plpython2'
+relocatable = false
+schema = pg_catalog
+superuser = true
diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql
index 319d5e0925d..72d5d657ec3 100644
--- a/src/pl/plpython/sql/plpython_drop.sql
+++ b/src/pl/plpython/sql/plpython_drop.sql
@@ -3,4 +3,6 @@
 --
 SET client_min_messages = WARNING;
 
-DROP PROCEDURAL LANGUAGE plpythonu CASCADE;
+DROP EXTENSION plpythonu CASCADE;
+
+DROP EXTENSION IF EXISTS plpython2u CASCADE;
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 915189847a7..c8d5ef5f534 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -1,5 +1,5 @@
 -- first some tests of basic functionality
-CREATE LANGUAGE plpython2u;
+CREATE EXTENSION plpython2u;
 
 -- really stupid function just to get the module loaded
 CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile
index b29478dd6fd..c7797c61f5e 100644
--- a/src/pl/tcl/Makefile
+++ b/src/pl/tcl/Makefile
@@ -1,6 +1,6 @@
 #-------------------------------------------------------------------------
 #
-# Makefile for the pltcl shared object
+# Makefile for the pl/tcl procedural language
 #
 # src/pl/tcl/Makefile
 #
@@ -35,9 +35,13 @@ SHLIB_LINK += $(TCL_LIBS) -lc
 endif
 
 NAME = pltcl
+
 OBJS = pltcl.o
 
-REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=pltcl
+DATA = pltcl.control pltcl--1.0.sql pltcl--unpackaged--1.0.sql \
+       pltclu.control pltclu--1.0.sql pltclu--unpackaged--1.0.sql
+
+REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
 REGRESS = pltcl_setup pltcl_queries
 # where to find psql for running the tests
 PSQLDIR = $(bindir)
@@ -49,15 +53,29 @@ ifeq ($(TCL_SHARED_BUILD), 1)
 all: all-lib
 	$(MAKE) -C modules $@
 
-install: all installdirs install-lib
+
+install: all installdirs install-lib install-data
 	$(MAKE) -C modules $@
 
 installdirs: installdirs-lib
+	$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
 	$(MAKE) -C modules $@
 
-uninstall: uninstall-lib
+uninstall: uninstall-lib uninstall-data
 	$(MAKE) -C modules $@
 
+install-data:
+	@for file in $(addprefix $(srcdir)/, $(DATA)); do \
+	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'"; \
+	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'; \
+	done
+
+uninstall-data:
+	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
+
+.PHONY: install-data uninstall-data
+
+
 check: submake
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
 
diff --git a/src/pl/tcl/pltcl--1.0.sql b/src/pl/tcl/pltcl--1.0.sql
new file mode 100644
index 00000000000..897e1a1fe92
--- /dev/null
+++ b/src/pl/tcl/pltcl--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/tcl/pltcl--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE pltcl;
diff --git a/src/pl/tcl/pltcl--unpackaged--1.0.sql b/src/pl/tcl/pltcl--unpackaged--1.0.sql
new file mode 100644
index 00000000000..dfad66c268b
--- /dev/null
+++ b/src/pl/tcl/pltcl--unpackaged--1.0.sql
@@ -0,0 +1,5 @@
+/* src/pl/tcl/pltcl--unpackaged--1.0.sql */
+
+ALTER EXTENSION pltcl ADD PROCEDURAL LANGUAGE pltcl;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION pltcl ADD FUNCTION pltcl_call_handler();
diff --git a/src/pl/tcl/pltcl.control b/src/pl/tcl/pltcl.control
new file mode 100644
index 00000000000..b9dc1b8a138
--- /dev/null
+++ b/src/pl/tcl/pltcl.control
@@ -0,0 +1,7 @@
+# pltcl extension
+comment = 'PL/Tcl procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/pltcl'
+relocatable = false
+schema = pg_catalog
+superuser = false
diff --git a/src/pl/tcl/pltclu--1.0.sql b/src/pl/tcl/pltclu--1.0.sql
new file mode 100644
index 00000000000..e53bb04e6d3
--- /dev/null
+++ b/src/pl/tcl/pltclu--1.0.sql
@@ -0,0 +1,9 @@
+/* src/pl/tcl/pltclu--1.0.sql */
+
+/*
+ * Currently, all the interesting stuff is done by CREATE LANGUAGE.
+ * Later we will probably "dumb down" that command and put more of the
+ * knowledge into this script.
+ */
+
+CREATE PROCEDURAL LANGUAGE pltclu;
diff --git a/src/pl/tcl/pltclu--unpackaged--1.0.sql b/src/pl/tcl/pltclu--unpackaged--1.0.sql
new file mode 100644
index 00000000000..a5d359fc047
--- /dev/null
+++ b/src/pl/tcl/pltclu--unpackaged--1.0.sql
@@ -0,0 +1,5 @@
+/* src/pl/tcl/pltclu--unpackaged--1.0.sql */
+
+ALTER EXTENSION pltclu ADD PROCEDURAL LANGUAGE pltclu;
+-- ALTER ADD LANGUAGE doesn't pick up the support functions, so we have to.
+ALTER EXTENSION pltclu ADD FUNCTION pltclu_call_handler();
diff --git a/src/pl/tcl/pltclu.control b/src/pl/tcl/pltclu.control
new file mode 100644
index 00000000000..1418dc5a9ef
--- /dev/null
+++ b/src/pl/tcl/pltclu.control
@@ -0,0 +1,7 @@
+# pltclu extension
+comment = 'PL/TclU untrusted procedural language'
+default_version = '1.0'
+module_pathname = '$libdir/pltcl'
+relocatable = false
+schema = pg_catalog
+superuser = true
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index a6160298efb..6ecf78488e0 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -86,6 +86,7 @@ char	   *outputdir = ".";
 char	   *psqldir = PGBINDIR;
 char	   *launcher = NULL;
 static _stringlist *loadlanguage = NULL;
+static _stringlist *loadextension = NULL;
 static int	max_connections = 0;
 static char *encoding = NULL;
 static _stringlist *schedulelist = NULL;
@@ -1800,6 +1801,16 @@ create_database(const char *dbname)
 		header(_("installing %s"), sl->str);
 		psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
 	}
+
+	/*
+	 * Install any requested extensions.  We use CREATE IF NOT EXISTS
+	 * so that this will work whether or not the extension is preinstalled.
+	 */
+	for (sl = loadextension; sl != NULL; sl = sl->next)
+	{
+		header(_("installing %s"), sl->str);
+		psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
+	}
 }
 
 static void
@@ -1862,6 +1873,8 @@ help(void)
 	printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
 	printf(_("  --load-language=lang      load the named language before running the\n"));
 	printf(_("                            tests; can appear multiple times\n"));
+	printf(_("  --load-extension=ext      load the named extension before running the\n"));
+	printf(_("                            tests; can appear multiple times\n"));
 	printf(_("  --create-role=ROLE        create the specified role before testing\n"));
 	printf(_("  --max-connections=N       maximum number of concurrent connections\n"));
 	printf(_("                            (default is 0 meaning unlimited)\n"));
@@ -1925,6 +1938,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 		{"temp-config", required_argument, NULL, 19},
 		{"use-existing", no_argument, NULL, 20},
 		{"launcher", required_argument, NULL, 21},
+		{"load-extension", required_argument, NULL, 22},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -2021,6 +2035,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 			case 21:
 				launcher = strdup(optarg);
 				break;
+			case 22:
+				add_stringlist_item(&loadextension, optarg);
+				break;
 			default:
 				/* getopt_long already emitted a complaint */
 				fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index f3024f26ca6..2fe6b8a05be 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -146,14 +146,14 @@ sub plcheck
         my $lang = $pl eq 'tcl' ? 'pltcl' : $pl;
         next unless -d "../../$Config/$lang";
         $lang = 'plpythonu' if $lang eq 'plpython';
-        my @lang_args = ("--load-language=$lang");
+        my @lang_args = ("--load-extension=$lang");
         chdir $pl;
         my @tests = fetchTests();
         if ($lang eq 'plperl')
         {
 
             # run both trusted and untrusted perl tests
-            push(@lang_args, "--load-language=plperlu");
+            push(@lang_args, "--load-extension=plperlu");
 
             # assume we're using this perl to built postgres
             # test if we can run two interpreters in one backend, and if so
-- 
GitLab