aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/extension.c217
-rw-r--r--src/backend/parser/gram.y4
-rw-r--r--src/bin/psql/tab-complete.c7
-rw-r--r--src/test/modules/Makefile1
-rw-r--r--src/test/modules/test_extensions/.gitignore4
-rw-r--r--src/test/modules/test_extensions/Makefile23
-rw-r--r--src/test/modules/test_extensions/expected/test_extensions.out37
-rw-r--r--src/test/modules/test_extensions/sql/test_extensions.sql15
-rw-r--r--src/test/modules/test_extensions/test_ext1--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext1.control5
-rw-r--r--src/test/modules/test_extensions/test_ext2--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext2.control4
-rw-r--r--src/test/modules/test_extensions/test_ext3--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext3.control3
-rw-r--r--src/test/modules/test_extensions/test_ext4--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext4.control4
-rw-r--r--src/test/modules/test_extensions/test_ext5--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext5.control3
-rw-r--r--src/test/modules/test_extensions/test_ext_cyclic1--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext_cyclic1.control4
-rw-r--r--src/test/modules/test_extensions/test_ext_cyclic2--1.0.sql3
-rw-r--r--src/test/modules/test_extensions/test_ext_cyclic2.control4
-rw-r--r--src/tools/msvc/Mkvcbuild.pm3
23 files changed, 290 insertions, 69 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 6b92bdc5e0e..67b16a7a68d 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -43,11 +43,13 @@
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/comment.h"
+#include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
#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"
@@ -1165,18 +1167,25 @@ find_update_path(List *evi_list,
}
/*
- * CREATE EXTENSION
+ * CREATE EXTENSION worker
+ *
+ * When CASCADE is specified CreateExtensionInternal() recurses if required
+ * extensions need to be installed. To sanely handle cyclic dependencies
+ * cascade_parent contains the dependency chain leading to the current
+ * invocation; thus allowing to error out if there's a cyclic dependency.
*/
-ObjectAddress
-CreateExtension(CreateExtensionStmt *stmt)
+static ObjectAddress
+CreateExtensionInternal(CreateExtensionStmt *stmt, List *parents)
{
DefElem *d_schema = NULL;
DefElem *d_new_version = NULL;
DefElem *d_old_version = NULL;
- char *schemaName;
- Oid schemaOid;
+ DefElem *d_cascade = NULL;
+ char *schemaName = NULL;
+ Oid schemaOid = InvalidOid;
char *versionName;
char *oldVersionName;
+ bool cascade = false;
Oid extowner = GetUserId();
ExtensionControlFile *pcontrol;
ExtensionControlFile *control;
@@ -1187,41 +1196,6 @@ CreateExtension(CreateExtensionStmt *stmt)
ListCell *lc;
ObjectAddress address;
- /* Check extension name validity before any filesystem access */
- check_valid_extension_name(stmt->extname);
-
- /*
- * Check for duplicate extension name. The unique index on
- * pg_extension.extname would catch this anyway, and serves as a backstop
- * in case of race conditions; but this is a friendlier error message, and
- * besides we need a check to support IF NOT EXISTS.
- */
- if (get_extension_oid(stmt->extname, true) != InvalidOid)
- {
- if (stmt->if_not_exists)
- {
- ereport(NOTICE,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("extension \"%s\" already exists, skipping",
- stmt->extname)));
- return InvalidObjectAddress;
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("extension \"%s\" already exists",
- stmt->extname)));
- }
-
- /*
- * We use global variables to track the extension being created, so we can
- * create only one extension at the same time.
- */
- if (creating_extension)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("nested CREATE EXTENSION is not supported")));
-
/*
* Read the primary control file. Note we assume that it does not contain
* any non-ASCII data, so there is no need to worry about encoding at this
@@ -1260,6 +1234,15 @@ 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;
+ cascade = defGetBoolean(d_cascade);
+ }
else
elog(ERROR, "unrecognized option: %s", defel->defname);
}
@@ -1337,33 +1320,37 @@ 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.
+ *
+ * Unless CASCADE parameter was given, it's an error to give a schema
+ * different from control->schema if control->schema is specified.
*/
+ if (schemaName && strcmp(control->schema, schemaName) != 0 &&
+ !cascade)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extension \"%s\" must be installed in schema \"%s\"",
+ control->name,
+ control->schema)));
+
+ /* Always use the schema from control file for current extension. */
schemaName = control->schema;
+
+ /* Find or create the schema in case it does not exist. */
schemaOid = get_namespace_oid(schemaName, true);
- if (schemaOid == InvalidOid)
+ if (!OidIsValid(schemaOid))
{
CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
@@ -1375,16 +1362,17 @@ CreateExtension(CreateExtensionStmt *stmt)
/*
* CreateSchemaCommand includes CommandCounterIncrement, so new
- * schema is now visible
+ * 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);
@@ -1423,16 +1411,65 @@ CreateExtension(CreateExtensionStmt *stmt)
Oid reqext;
Oid reqschema;
- /*
- * We intentionally don't use get_extension_oid's default error
- * message here, because it would be confusing in this context.
- */
reqext = get_extension_oid(curreq, true);
if (!OidIsValid(reqext))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("required extension \"%s\" is not installed",
- curreq)));
+ {
+ if (cascade)
+ {
+ CreateExtensionStmt *ces;
+ ListCell *lc;
+ ObjectAddress addr;
+ List *cascade_parents;
+
+ /* Check extension name validity before trying to cascade */
+ check_valid_extension_name(curreq);
+
+ /* Check for cyclic dependency between extensions. */
+ foreach(lc, 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)));
+ }
+
+ ereport(NOTICE,
+ (errmsg("installing required extension \"%s\"",
+ curreq)));
+
+ /* Create and execute new CREATE EXTENSION statement. */
+ ces = makeNode(CreateExtensionStmt);
+ ces->extname = curreq;
+
+ /* Propagate the CASCADE option */
+ ces->options = list_make1(d_cascade);
+
+ /* Propagate the SCHEMA option if given. */
+ if (d_schema && d_schema->arg)
+ ces->options = lappend(ces->options, d_schema);
+
+ /*
+ * Pass the current list of parents + the current extension to
+ * the "child" CreateExtensionInternal().
+ */
+ cascade_parents =
+ lappend(list_copy(parents), stmt->extname);
+
+ /* Create the required extension. */
+ addr = CreateExtensionInternal(ces, cascade_parents);
+ reqext = addr.objectId;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("required extension \"%s\" is not installed",
+ curreq),
+ errhint("Use CREATE EXTENSION CASCADE to install required extensions too.")));
+ }
+
reqschema = get_extension_schema(reqext);
requiredExtensions = lappend_oid(requiredExtensions, reqext);
requiredSchemas = lappend_oid(requiredSchemas, reqschema);
@@ -1474,6 +1511,52 @@ CreateExtension(CreateExtensionStmt *stmt)
}
/*
+ * CREATE EXTENSION
+ */
+ObjectAddress
+CreateExtension(CreateExtensionStmt *stmt)
+{
+ /* Check extension name validity before any filesystem access */
+ check_valid_extension_name(stmt->extname);
+
+ /*
+ * Check for duplicate extension name. The unique index on
+ * pg_extension.extname would catch this anyway, and serves as a backstop
+ * in case of race conditions; but this is a friendlier error message, and
+ * besides we need a check to support IF NOT EXISTS.
+ */
+ if (get_extension_oid(stmt->extname, true) != InvalidOid)
+ {
+ if (stmt->if_not_exists)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists, skipping",
+ stmt->extname)));
+ return InvalidObjectAddress;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists",
+ stmt->extname)));
+ }
+
+ /*
+ * We use global variables to track the extension being created, so we can
+ * create only one extension at the same time.
+ */
+ if (creating_extension)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("nested CREATE EXTENSION is not supported")));
+
+
+ /* Finally create the extension. */
+ return CreateExtensionInternal(stmt, NIL);
+}
+
+/*
* InsertExtensionTuple
*
* Insert the new pg_extension row, and create extension's dependency entries.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index fb84937b626..417fb55bc48 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3876,6 +3876,10 @@ create_extension_opt_item:
{
$$ = makeDefElem("old_version", (Node *)makeString($2));
}
+ | CASCADE
+ {
+ $$ = makeDefElem("cascade", (Node *)makeInteger(TRUE));
+ }
;
/*****************************************************************************
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 4294ffd0520..1619de52aea 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2264,7 +2264,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 9b966542793..6167ec13446 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -9,6 +9,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 00000000000..5dcb3ff9723
--- /dev/null
+++ b/src/test/modules/test_extensions/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
new file mode 100644
index 00000000000..5691357c070
--- /dev/null
+++ b/src/test/modules/test_extensions/Makefile
@@ -0,0 +1,23 @@
+# 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_ext4 test_ext5 \
+ test_ext_cyclic1 test_ext_cyclic2
+DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
+ test_ext4--1.0.sql test_ext5--1.0.sql test_ext_cyclic1--1.0.sql \
+ test_ext_cyclic2--1.0.sql
+
+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 00000000000..a57bb4b7ff0
--- /dev/null
+++ b/src/test/modules/test_extensions/expected/test_extensions.out
@@ -0,0 +1,37 @@
+-- test some errors
+CREATE EXTENSION test_ext1;
+ERROR: required extension "test_ext2" is not installed
+HINT: Use CREATE EXTENSION CASCADE to install required extensions too.
+CREATE EXTENSION test_ext1 SCHEMA test_ext1;
+ERROR: schema "test_ext1" does not exist
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+ERROR: schema "test_ext" does not exist
+CREATE SCHEMA test_ext;
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+ERROR: extension "test_ext1" must be installed in schema "test_ext1"
+-- finally success
+CREATE EXTENSION test_ext1 SCHEMA test_ext CASCADE;
+NOTICE: installing required extension "test_ext2"
+NOTICE: installing required extension "test_ext3"
+NOTICE: installing required extension "test_ext5"
+NOTICE: installing required extension "test_ext4"
+SELECT extname, nspname, extversion, extrelocatable FROM pg_extension e, pg_namespace n WHERE extname LIKE 'test_ext%' AND e.extnamespace = n.oid ORDER BY 1;
+ extname | nspname | extversion | extrelocatable
+-----------+-----------+------------+----------------
+ test_ext1 | test_ext1 | 1.0 | f
+ test_ext2 | test_ext | 1.0 | t
+ test_ext3 | test_ext | 1.0 | t
+ test_ext4 | test_ext | 1.0 | t
+ test_ext5 | test_ext | 1.0 | t
+(5 rows)
+
+CREATE EXTENSION test_ext_cyclic1 CASCADE;
+NOTICE: installing required extension "test_ext_cyclic2"
+ERROR: cyclic dependency detected between extensions "test_ext_cyclic1" and "test_ext_cyclic2"
+DROP SCHEMA test_ext CASCADE;
+NOTICE: drop cascades to 5 other objects
+DETAIL: drop cascades to extension test_ext3
+drop cascades to extension test_ext5
+drop cascades to extension test_ext2
+drop cascades to extension test_ext4
+drop cascades to extension test_ext1
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 00000000000..9076c02807c
--- /dev/null
+++ b/src/test/modules/test_extensions/sql/test_extensions.sql
@@ -0,0 +1,15 @@
+-- test some errors
+CREATE EXTENSION test_ext1;
+CREATE EXTENSION test_ext1 SCHEMA test_ext1;
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+CREATE SCHEMA test_ext;
+CREATE EXTENSION test_ext1 SCHEMA test_ext;
+
+-- finally success
+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 ORDER BY 1;
+
+CREATE EXTENSION test_ext_cyclic1 CASCADE;
+
+DROP SCHEMA test_ext CASCADE;
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 00000000000..9a4bb1be789
--- /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 00000000000..9c069dfcaf8
--- /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,test_ext4'
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 00000000000..0f6d4ec2fb4
--- /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 00000000000..946b7d58feb
--- /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,test_ext5'
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 00000000000..7dec684dcb5
--- /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 00000000000..5f1afe72fc3
--- /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_ext4--1.0.sql b/src/test/modules/test_extensions/test_ext4--1.0.sql
new file mode 100644
index 00000000000..19f051f6f78
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext4--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext4--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext4" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext4.control b/src/test/modules/test_extensions/test_ext4.control
new file mode 100644
index 00000000000..fc625919509
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext4.control
@@ -0,0 +1,4 @@
+comment = 'Test extension 4'
+default_version = '1.0'
+relocatable = true
+requires = 'test_ext5'
diff --git a/src/test/modules/test_extensions/test_ext5--1.0.sql b/src/test/modules/test_extensions/test_ext5--1.0.sql
new file mode 100644
index 00000000000..baf6ef85dd6
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext5--1.0.sql
@@ -0,0 +1,3 @@
+/* src/test/modules/test_extensions/test_ext5--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext5" to load this file. \quit
diff --git a/src/test/modules/test_extensions/test_ext5.control b/src/test/modules/test_extensions/test_ext5.control
new file mode 100644
index 00000000000..51bc57efeaa
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext5.control
@@ -0,0 +1,3 @@
+comment = 'Test extension 5'
+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 00000000000..81bdaf48d21
--- /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 00000000000..aaab4034636
--- /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 00000000000..ae2b3e9a967
--- /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 00000000000..1e28f96391c
--- /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 3abbb4caf40..c122f0259c9 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -41,7 +41,8 @@ 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', 'brin');
+ 'ltree_plpython', 'pgcrypto', 'sepgsql', 'brin',
+ 'test_extensions');
# Set of variables for frontend modules
my $frontend_defines = { 'initdb' => 'FRONTEND' };