On 2015-07-22 07:12, Michael Paquier wrote:
On Tue, Jul 21, 2015 at 11:20 PM, Tom Lane <t...@sss.pgh.pa.us> wrote:
Petr Jelinek <p...@2ndquadrant.com> writes:
... My main question is if we are
ok with SCHEMA having different behavior with CASCADE vs without
CASCADE. I went originally with "no" and added the DEFAULT flag to
SCHEMA. If the answer is "yes" then we don't need the flag (in that case
CASCADE acts as the flag).
Yeah, I was coming around to that position as well. Insisting that
SCHEMA throw an error if the extension isn't relocatable makes sense
as long as only one extension is being considered, but once you say
CASCADE it seems like mostly a usability fail. I think it's probably
OK if with CASCADE, SCHEMA is just "use if needed else ignore".
Here is a patch implementing that. Note that the checks are now done in
different order for non-relocatable extension when SCHEMA is specified
than previously. Before the patch, the SCHEMA was first checked for
conflict with the extension's schema and then there was check for schema
existence. This patch always checks for schema existence first, mainly
to keep code saner (to my eyes).
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
>From a5e59422c162818002f5946542ad47373bf3eb12 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmo...@pjmodos.net>
Date: Sat, 13 Jun 2015 19:49:10 +0200
Subject: [PATCH] CREATE EXTENSION CASCADE
---
contrib/hstore_plperl/expected/hstore_plperl.out | 6 +-
contrib/hstore_plperl/expected/hstore_plperlu.out | 6 +-
contrib/hstore_plperl/sql/hstore_plperl.sql | 4 +-
contrib/hstore_plperl/sql/hstore_plperlu.sql | 4 +-
.../hstore_plpython/expected/hstore_plpython.out | 4 +-
contrib/hstore_plpython/sql/hstore_plpython.sql | 3 +-
contrib/ltree_plpython/expected/ltree_plpython.out | 4 +-
contrib/ltree_plpython/sql/ltree_plpython.sql | 3 +-
doc/src/sgml/ref/create_extension.sgml | 40 ++++++
src/backend/commands/extension.c | 158 ++++++++++++++++-----
src/backend/parser/gram.y | 4 +
src/bin/psql/tab-complete.c | 7 +-
src/test/modules/Makefile | 1 +
src/test/modules/test_extensions/.gitignore | 3 +
src/test/modules/test_extensions/Makefile | 22 +++
.../test_extensions/expected/test_extensions.out | 24 ++++
.../test_extensions/sql/test_extensions.sql | 15 ++
.../modules/test_extensions/test_ext1--1.0.sql | 3 +
src/test/modules/test_extensions/test_ext1.control | 5 +
.../modules/test_extensions/test_ext2--1.0.sql | 3 +
src/test/modules/test_extensions/test_ext2.control | 4 +
.../modules/test_extensions/test_ext3--1.0.sql | 3 +
src/test/modules/test_extensions/test_ext3.control | 3 +
.../test_extensions/test_ext_cyclic1--1.0.sql | 3 +
.../test_extensions/test_ext_cyclic1.control | 4 +
.../test_extensions/test_ext_cyclic2--1.0.sql | 3 +
.../test_extensions/test_ext_cyclic2.control | 4 +
src/tools/msvc/Mkvcbuild.pm | 2 +-
28 files changed, 287 insertions(+), 58 deletions(-)
create mode 100644 src/test/modules/test_extensions/.gitignore
create mode 100644 src/test/modules/test_extensions/Makefile
create mode 100644 src/test/modules/test_extensions/expected/test_extensions.out
create mode 100644 src/test/modules/test_extensions/sql/test_extensions.sql
create mode 100644 src/test/modules/test_extensions/test_ext1--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext1.control
create mode 100644 src/test/modules/test_extensions/test_ext2--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext2.control
create mode 100644 src/test/modules/test_extensions/test_ext3--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext3.control
create mode 100644 src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext_cyclic1.control
create mode 100644 src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext_cyclic2.control
diff --git a/contrib/hstore_plperl/expected/hstore_plperl.out b/contrib/hstore_plperl/expected/hstore_plperl.out
index cf384eb..25fc506 100644
--- a/contrib/hstore_plperl/expected/hstore_plperl.out
+++ b/contrib/hstore_plperl/expected/hstore_plperl.out
@@ -1,6 +1,6 @@
-CREATE EXTENSION hstore;
-CREATE EXTENSION plperl;
-CREATE EXTENSION hstore_plperl;
+CREATE EXTENSION hstore_plperl CASCADE;
+NOTICE: installing required extension "hstore"
+NOTICE: installing required extension "plperl"
SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name,
group_name, transform_type
diff --git a/contrib/hstore_plperl/expected/hstore_plperlu.out b/contrib/hstore_plperl/expected/hstore_plperlu.out
index 8c689ad..3fc3201 100644
--- a/contrib/hstore_plperl/expected/hstore_plperlu.out
+++ b/contrib/hstore_plperl/expected/hstore_plperlu.out
@@ -1,6 +1,6 @@
-CREATE EXTENSION hstore;
-CREATE EXTENSION plperlu;
-CREATE EXTENSION hstore_plperlu;
+CREATE EXTENSION hstore_plperlu CASCADE;
+NOTICE: installing required extension "hstore"
+NOTICE: installing required extension "plperlu"
SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name,
group_name, transform_type
diff --git a/contrib/hstore_plperl/sql/hstore_plperl.sql b/contrib/hstore_plperl/sql/hstore_plperl.sql
index 0f70f14..9398aed 100644
--- a/contrib/hstore_plperl/sql/hstore_plperl.sql
+++ b/contrib/hstore_plperl/sql/hstore_plperl.sql
@@ -1,6 +1,4 @@
-CREATE EXTENSION hstore;
-CREATE EXTENSION plperl;
-CREATE EXTENSION hstore_plperl;
+CREATE EXTENSION hstore_plperl CASCADE;
SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name,
diff --git a/contrib/hstore_plperl/sql/hstore_plperlu.sql b/contrib/hstore_plperl/sql/hstore_plperlu.sql
index 3cfb2fd..8d8508c 100644
--- a/contrib/hstore_plperl/sql/hstore_plperlu.sql
+++ b/contrib/hstore_plperl/sql/hstore_plperlu.sql
@@ -1,6 +1,4 @@
-CREATE EXTENSION hstore;
-CREATE EXTENSION plperlu;
-CREATE EXTENSION hstore_plperlu;
+CREATE EXTENSION hstore_plperlu CASCADE;
SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name,
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index b7a6a92..55f4efe 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -1,5 +1,5 @@
-CREATE EXTENSION plpython2u;
-CREATE EXTENSION hstore_plpython2u;
+CREATE EXTENSION hstore_plpython2u CASCADE;
+NOTICE: installing required extension "plpython2u"
-- test hstore -> python
CREATE FUNCTION test1(val hstore) RETURNS int
LANGUAGE plpythonu
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index 9ff2ebc..d55beda 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -1,5 +1,4 @@
-CREATE EXTENSION plpython2u;
-CREATE EXTENSION hstore_plpython2u;
+CREATE EXTENSION hstore_plpython2u CASCADE;
-- test hstore -> python
diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out
index 934529e..9bee6be 100644
--- a/contrib/ltree_plpython/expected/ltree_plpython.out
+++ b/contrib/ltree_plpython/expected/ltree_plpython.out
@@ -1,5 +1,5 @@
-CREATE EXTENSION plpython2u;
-CREATE EXTENSION ltree_plpython2u;
+CREATE EXTENSION ltree_plpython2u CASCADE;
+NOTICE: installing required extension "plpython2u"
CREATE FUNCTION test1(val ltree) RETURNS int
LANGUAGE plpythonu
TRANSFORM FOR TYPE ltree
diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql
index f08ff6a..210f542 100644
--- a/contrib/ltree_plpython/sql/ltree_plpython.sql
+++ b/contrib/ltree_plpython/sql/ltree_plpython.sql
@@ -1,5 +1,4 @@
-CREATE EXTENSION plpython2u;
-CREATE EXTENSION ltree_plpython2u;
+CREATE EXTENSION ltree_plpython2u CASCADE;
CREATE FUNCTION test1(val ltree) RETURNS int
diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml
index a1e7e4f8..715dd23 100644
--- a/doc/src/sgml/ref/create_extension.sgml
+++ b/doc/src/sgml/ref/create_extension.sgml
@@ -25,6 +25,7 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
[ WITH ] [ SCHEMA <replaceable class="parameter">schema_name</replaceable> ]
[ VERSION <replaceable class="parameter">version</replaceable> ]
[ FROM <replaceable class="parameter">old_version</replaceable> ]
+ [ CASCADE ]
</synopsis>
</refsynopsisdiv>
@@ -95,6 +96,34 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
schema either, the current default object creation schema is used.
</para>
<para>
+ In case the extension specifies schema in its control file, the schema
+ can't be overriden using <literal>SCHEMA</> parameter. The actual
+ behavior of the <literal>SCHEMA</> parameter in this case will depend
+ on circumstances:
+ <itemizedlist>
+ <listitem>
+ <para>
+ If schema specified by <literal>SCHEMA</> parameter matches the one
+ in control file, it will be used normally as there is no conflict.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If <literal>CASCADE</> parameter is given, the <literal>SCHEMA</>
+ parameter will only be used by the missing required extensions which
+ do not specify the schema in their control files.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If schema is not same as the one in extension's control file and
+ the <literal>CASCADE</> parameter is not given, error will be
+ thrown.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
Remember that the extension itself is not considered to be within any
schema: extensions have unqualified names that must be unique
database-wide. But objects belonging to the extension can be within
@@ -139,6 +168,17 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>CASCADE</></term>
+ <listitem>
+ <para>
+ Try to install extension including the required dependencies
+ recursively. The <literal>SCHEMA</> option will be propagated
+ to the required extensions.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 2b1dcd0..f73a8ce 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -48,6 +48,7 @@
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/makefuncs.h"
#include "storage/fd.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
@@ -1173,10 +1174,12 @@ CreateExtension(CreateExtensionStmt *stmt)
DefElem *d_schema = NULL;
DefElem *d_new_version = NULL;
DefElem *d_old_version = NULL;
+ DefElem *d_cascade = NULL;
char *schemaName;
- Oid schemaOid;
+ Oid schemaOid = InvalidOid;
char *versionName;
char *oldVersionName;
+ List *cascade_parents = NIL;
Oid extowner = GetUserId();
ExtensionControlFile *pcontrol;
ExtensionControlFile *control;
@@ -1260,6 +1263,14 @@ CreateExtension(CreateExtensionStmt *stmt)
errmsg("conflicting or redundant options")));
d_old_version = defel;
}
+ else if (strcmp(defel->defname, "cascade") == 0)
+ {
+ if (d_cascade)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_cascade = defel;
+ }
else
elog(ERROR, "unrecognized option: %s", defel->defname);
}
@@ -1330,6 +1341,13 @@ CreateExtension(CreateExtensionStmt *stmt)
*/
control = read_extension_aux_control_file(pcontrol, versionName);
+ /* Handle the CASCADE option. */
+ if (d_cascade)
+ {
+ if (d_cascade->arg)
+ cascade_parents = (List *) d_cascade->arg;
+ }
+
/*
* Determine the target schema to install the extension into
*/
@@ -1337,54 +1355,72 @@ CreateExtension(CreateExtensionStmt *stmt)
{
/*
* User given schema, CREATE EXTENSION ... WITH SCHEMA ...
- *
- * It's an error to give a schema different from control->schema if
- * control->schema is specified.
*/
schemaName = strVal(d_schema->arg);
- if (control->schema != NULL &&
- strcmp(control->schema, schemaName) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("extension \"%s\" must be installed in schema \"%s\"",
- control->name,
- control->schema)));
-
- /* If the user is giving us the schema name, it must exist already */
+ /* If the user is giving us the schema name, it must exist already. */
schemaOid = get_namespace_oid(schemaName, false);
}
- else if (control->schema != NULL)
+
+ if (control->schema != NULL)
{
/*
* The extension is not relocatable and the author gave us a schema
- * for it. We create the schema here if it does not already exist.
+ * for it.
*/
- schemaName = control->schema;
- schemaOid = get_namespace_oid(schemaName, true);
- if (schemaOid == InvalidOid)
+ /*
+ * User given schema. Unless CASCADE is specified, it's an error to
+ * give schema different from the one specified by author. When
+ * CASCADE is specified, we just ignore the schema given by user and
+ * use the one from control file as if no schema was given by user.
+ */
+ if (OidIsValid(schemaOid))
{
- CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
+ if (d_cascade == NULL &&
+ strcmp(control->schema, schemaName) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extension \"%s\" must be installed in schema \"%s\"",
+ control->name,
+ control->schema)));
+ else
+ schemaOid = InvalidOid;
+ }
- csstmt->schemaname = schemaName;
- csstmt->authrole = NULL; /* will be created by current user */
- csstmt->schemaElts = NIL;
- csstmt->if_not_exists = false;
- CreateSchemaCommand(csstmt, NULL);
+ /*
+ * User didn't provide schema or we are not using it, create the
+ * schema specified by extension author if it does not exist yet.
+ */
+ if (!OidIsValid(schemaOid))
+ {
+ schemaName = control->schema;
+ schemaOid = get_namespace_oid(control->schema, true);
- /*
- * CreateSchemaCommand includes CommandCounterIncrement, so new
- * schema is now visible
- */
- schemaOid = get_namespace_oid(schemaName, false);
+ if (!OidIsValid(schemaOid))
+ {
+ CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
+
+ csstmt->schemaname = schemaName;
+ csstmt->authrole = NULL; /* will be created by current user */
+ csstmt->schemaElts = NIL;
+ csstmt->if_not_exists = false;
+ CreateSchemaCommand(csstmt, NULL);
+
+ /*
+ * CreateSchemaCommand includes CommandCounterIncrement, so new
+ * schema is now visible
+ */
+ schemaOid = get_namespace_oid(schemaName, false);
+ }
}
}
- else
+ else if (!OidIsValid(schemaOid))
{
/*
- * Else, use the current default creation namespace, which is the
- * first explicit entry in the search_path.
+ * Neither user nor author of the extension specified schema, use the
+ * current default creation namespace, which is the first explicit
+ * entry in the search_path.
*/
List *search_path = fetch_search_path(false);
@@ -1429,10 +1465,60 @@ CreateExtension(CreateExtensionStmt *stmt)
*/
reqext = get_extension_oid(curreq, true);
if (!OidIsValid(reqext))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("required extension \"%s\" is not installed",
- curreq)));
+ {
+ if (d_cascade)
+ {
+ CreateExtensionStmt *ces;
+ ListCell *lc;
+ ObjectAddress addr;
+
+ ereport(NOTICE,
+ (errmsg("installing required extension \"%s\"",
+ curreq)));
+
+ /* Check for cyclic dependency between extensions. */
+ foreach(lc, cascade_parents)
+ {
+ char *pname = (char *) lfirst(lc);
+
+ if (strcmp(pname, curreq) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_RECURSION),
+ errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
+ curreq, stmt->extname)));
+ }
+
+ /* Create and execute new CREATE EXTENSION statement. */
+ ces = makeNode(CreateExtensionStmt);
+ ces->extname = curreq;
+ ces->if_not_exists = false;
+
+ /*
+ * Propagate the CASCADE option and add current extension
+ * to the list for checking the cyclic dependencies.
+ */
+ cascade_parents = lappend(cascade_parents, stmt->extname);
+ ces->options = list_make1(makeDefElem("cascade",
+ (Node *) cascade_parents));
+
+ /* Propagate the SCHEMA option if given. */
+ if (d_schema && d_schema->arg)
+ ces->options = lappend(ces->options, d_schema);
+
+ addr = CreateExtension(ces);
+ reqext = addr.objectId;
+
+ /* We leak the DeflElems but probably not worth worrying about */
+ list_free(ces->options);
+ pfree(ces);
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("required extension \"%s\" is not installed",
+ curreq)));
+ }
+
reqschema = get_extension_schema(reqext);
requiredExtensions = lappend_oid(requiredExtensions, reqext);
requiredSchemas = lappend_oid(requiredSchemas, reqschema);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2b02a2e..44c8ed5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3857,6 +3857,10 @@ create_extension_opt_item:
{
$$ = makeDefElem("old_version", (Node *)makeString($2));
}
+ | CASCADE
+ {
+ $$ = makeDefElem("cascade", NULL);
+ }
;
/*****************************************************************************
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9596af6..89ab420 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2245,7 +2245,12 @@ psql_completion(const char *text, int start, int end)
/* CREATE EXTENSION <name> */
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
- COMPLETE_WITH_CONST("WITH SCHEMA");
+ {
+ static const char *const list_CREATE_EXTENSION[] =
+ {"WITH SCHEMA", "CASCADE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_EXTENSION);
+ }
/* CREATE FOREIGN */
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 8213e23..84b4423 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -8,6 +8,7 @@ SUBDIRS = \
commit_ts \
dummy_seclabel \
test_ddl_deparse \
+ test_extensions \
test_parser \
test_rls_hooks \
test_shm_mq \
diff --git a/src/test/modules/test_extensions/.gitignore b/src/test/modules/test_extensions/.gitignore
new file mode 100644
index 0000000..c08326f
--- /dev/null
+++ b/src/test/modules/test_extensions/.gitignore
@@ -0,0 +1,3 @@
+# Generated subdirectories
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
new file mode 100644
index 0000000..32f6aab
--- /dev/null
+++ b/src/test/modules/test_extensions/Makefile
@@ -0,0 +1,22 @@
+# src/test/modules/test_extensions/Makefile
+
+MODULE = test_extensions
+PGFILEDESC = "test_extensions - regression testing for EXTENSION support"
+
+EXTENSION = test_ext1 test_ext2 test_ext3 test_ext_cyclic1 test_ext_cyclic2
+DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
+ test_ext_cyclic1--1.0.sql test_ext_cyclic2--1.0.sql
+
+# test_ddl_deparse must be first
+REGRESS = test_extensions
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_extensions
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_extensions/expected/test_extensions.out b/src/test/modules/test_extensions/expected/test_extensions.out
new file mode 100644
index 0000000..b32ad0e
--- /dev/null
+++ b/src/test/modules/test_extensions/expected/test_extensions.out
@@ -0,0 +1,24 @@
+CREATE EXTENSION test_ext1;
+ERROR: required extension "test_ext2" is not installed
+CREATE SCHEMA test_ext;
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+ERROR: extension "test_ext1" must be installed in schema "test_ext1"
+CREATE EXTENSION test_ext1 SCHEMA test_ext CASCADE;
+NOTICE: installing required extension "test_ext2"
+NOTICE: installing required extension "test_ext3"
+SELECT extname, nspname, extversion, extrelocatable FROM pg_extension e, pg_namespace n WHERE extname LIKE 'test_ext%' AND e.extnamespace = n.oid;
+ extname | nspname | extversion | extrelocatable
+-----------+-----------+------------+----------------
+ test_ext2 | test_ext | 1.0 | t
+ test_ext3 | test_ext | 1.0 | t
+ test_ext1 | test_ext1 | 1.0 | f
+(3 rows)
+
+CREATE EXTENSION test_ext_cyclic1 CASCADE;
+NOTICE: installing required extension "test_ext_cyclic2"
+NOTICE: installing required extension "test_ext_cyclic1"
+ERROR: cyclic dependency detected between extensions "test_ext_cyclic1" and "test_ext_cyclic2"
+DROP EXTENSION test_ext1;
+DROP EXTENSION test_ext2;
+DROP EXTENSION test_ext3;
+DROP SCHEMA test_ext;
diff --git a/src/test/modules/test_extensions/sql/test_extensions.sql b/src/test/modules/test_extensions/sql/test_extensions.sql
new file mode 100644
index 0000000..9c2c040
--- /dev/null
+++ b/src/test/modules/test_extensions/sql/test_extensions.sql
@@ -0,0 +1,15 @@
+CREATE EXTENSION test_ext1;
+
+CREATE SCHEMA test_ext;
+
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+CREATE EXTENSION test_ext1 SCHEMA test_ext CASCADE;
+
+SELECT extname, nspname, extversion, extrelocatable FROM pg_extension e, pg_namespace n WHERE extname LIKE 'test_ext%' AND e.extnamespace = n.oid;
+
+CREATE EXTENSION test_ext_cyclic1 CASCADE;
+
+DROP EXTENSION test_ext1;
+DROP EXTENSION test_ext2;
+DROP EXTENSION test_ext3;
+DROP SCHEMA test_ext;
diff --git a/src/test/modules/test_extensions/test_ext1--1.0.sql b/src/test/modules/test_extensions/test_ext1--1.0.sql
new file mode 100644
index 0000000..9a4bb1b
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext1--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext1--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext1" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext1.control b/src/test/modules/test_extensions/test_ext1.control
new file mode 100644
index 0000000..ae50cd8
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext1.control
@@ -0,0 +1,5 @@
+comment = 'Test extension 1'
+default_version = '1.0'
+schema = 'test_ext1'
+relocatable = false
+requires = 'test_ext2'
diff --git a/src/test/modules/test_extensions/test_ext2--1.0.sql b/src/test/modules/test_extensions/test_ext2--1.0.sql
new file mode 100644
index 0000000..0f6d4ec
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext2--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext2--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext2" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext2.control b/src/test/modules/test_extensions/test_ext2.control
new file mode 100644
index 0000000..788337e
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext2.control
@@ -0,0 +1,4 @@
+comment = 'Test extension 2'
+default_version = '1.0'
+relocatable = true
+requires = 'test_ext3'
diff --git a/src/test/modules/test_extensions/test_ext3--1.0.sql b/src/test/modules/test_extensions/test_ext3--1.0.sql
new file mode 100644
index 0000000..7dec684
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext3--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext3--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext3" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext3.control b/src/test/modules/test_extensions/test_ext3.control
new file mode 100644
index 0000000..5f1afe7
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext3.control
@@ -0,0 +1,3 @@
+comment = 'Test extension 3'
+default_version = '1.0'
+relocatable = true
diff --git a/src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql b/src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql
new file mode 100644
index 0000000..81bdaf4
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext_cyclic1" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext_cyclic1.control b/src/test/modules/test_extensions/test_ext_cyclic1.control
new file mode 100644
index 0000000..aaab403
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cyclic1.control
@@ -0,0 +1,4 @@
+comment = 'Test extension cyclic 1'
+default_version = '1.0'
+relocatable = true
+requires = 'test_ext_cyclic2'
diff --git a/src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql b/src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql
new file mode 100644
index 0000000..ae2b3e9
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext_cyclic2" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext_cyclic2.control b/src/test/modules/test_extensions/test_ext_cyclic2.control
new file mode 100644
index 0000000..1e28f96
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cyclic2.control
@@ -0,0 +1,4 @@
+comment = 'Test extension cyclic 2'
+default_version = '1.0'
+relocatable = true
+requires = 'test_ext_cyclic1'
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index d70db40..9b3e31e 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -41,7 +41,7 @@ my $contrib_extrasource = {
'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ], };
my @contrib_excludes = (
'commit_ts', 'hstore_plperl', 'hstore_plpython', 'intagg',
- 'ltree_plpython', 'pgcrypto', 'sepgsql');
+ 'ltree_plpython', 'pgcrypto', 'sepgsql', 'test_extensions');
# Set of variables for frontend modules
my $frontend_defines = { 'initdb' => 'FRONTEND' };
--
1.9.1
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers