Commit b67aaf21 authored by Andres Freund's avatar Andres Freund

Add CASCADE support for CREATE EXTENSION.

Without CASCADE, if an extension has an unfullfilled dependency on
another extension, CREATE EXTENSION ERRORs out with "required extension
... is not installed". That is annoying, especially when that dependency
is an implementation detail of the extension, rather than something the
extension's user can make sense of.

In addition to CASCADE this also includes a small set of regression
tests around CREATE EXTENSION.

Author: Petr Jelinek, editorialized by Michael Paquier, Andres Freund
Reviewed-By: Michael Paquier, Andres Freund, Jeff Janes
Discussion: 557E0520.3040800@2ndquadrant.com
parent bf686796
CREATE EXTENSION hstore; CREATE EXTENSION hstore_plperl CASCADE;
CREATE EXTENSION plperl; NOTICE: installing required extension "hstore"
CREATE EXTENSION hstore_plperl; NOTICE: installing required extension "plperl"
SELECT transforms.udt_schema, transforms.udt_name, SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name, routine_schema, routine_name,
group_name, transform_type group_name, transform_type
......
CREATE EXTENSION hstore; CREATE EXTENSION hstore_plperlu CASCADE;
CREATE EXTENSION plperlu; NOTICE: installing required extension "hstore"
CREATE EXTENSION hstore_plperlu; NOTICE: installing required extension "plperlu"
SELECT transforms.udt_schema, transforms.udt_name, SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name, routine_schema, routine_name,
group_name, transform_type group_name, transform_type
......
CREATE EXTENSION hstore; CREATE EXTENSION hstore_plperl CASCADE;
CREATE EXTENSION plperl;
CREATE EXTENSION hstore_plperl;
SELECT transforms.udt_schema, transforms.udt_name, SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name, routine_schema, routine_name,
......
CREATE EXTENSION hstore; CREATE EXTENSION hstore_plperlu CASCADE;
CREATE EXTENSION plperlu;
CREATE EXTENSION hstore_plperlu;
SELECT transforms.udt_schema, transforms.udt_name, SELECT transforms.udt_schema, transforms.udt_name,
routine_schema, routine_name, routine_schema, routine_name,
......
CREATE EXTENSION plpython2u; CREATE EXTENSION hstore_plpython2u CASCADE;
CREATE EXTENSION hstore_plpython2u; NOTICE: installing required extension "plpython2u"
-- test hstore -> python -- test hstore -> python
CREATE FUNCTION test1(val hstore) RETURNS int CREATE FUNCTION test1(val hstore) RETURNS int
LANGUAGE plpythonu LANGUAGE plpythonu
......
CREATE EXTENSION plpython2u; CREATE EXTENSION hstore_plpython2u CASCADE;
CREATE EXTENSION hstore_plpython2u;
-- test hstore -> python -- test hstore -> python
......
CREATE EXTENSION plpython2u; CREATE EXTENSION ltree_plpython2u CASCADE;
CREATE EXTENSION ltree_plpython2u; NOTICE: installing required extension "plpython2u"
CREATE FUNCTION test1(val ltree) RETURNS int CREATE FUNCTION test1(val ltree) RETURNS int
LANGUAGE plpythonu LANGUAGE plpythonu
TRANSFORM FOR TYPE ltree TRANSFORM FOR TYPE ltree
......
CREATE EXTENSION plpython2u; CREATE EXTENSION ltree_plpython2u CASCADE;
CREATE EXTENSION ltree_plpython2u;
CREATE FUNCTION test1(val ltree) RETURNS int CREATE FUNCTION test1(val ltree) RETURNS int
......
...@@ -25,6 +25,7 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name ...@@ -25,6 +25,7 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
[ WITH ] [ SCHEMA <replaceable class="parameter">schema_name</replaceable> ] [ WITH ] [ SCHEMA <replaceable class="parameter">schema_name</replaceable> ]
[ VERSION <replaceable class="parameter">version</replaceable> ] [ VERSION <replaceable class="parameter">version</replaceable> ]
[ FROM <replaceable class="parameter">old_version</replaceable> ] [ FROM <replaceable class="parameter">old_version</replaceable> ]
[ CASCADE ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -94,6 +95,35 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name ...@@ -94,6 +95,35 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
If not specified, and the extension's control file does not specify a If not specified, and the extension's control file does not specify a
schema either, the current default object creation schema is used. schema either, the current default object creation schema is used.
</para> </para>
<para>
If the extension specifies <literal>schema</> in its control file,
the schema cannot be overriden with <literal>SCHEMA</> clause.
The <literal>SCHEMA</> clause in this case works as follows:
<itemizedlist>
<listitem>
<para>
If <replaceable class="parameter">schema_name</replaceable> matches
the schema in control file, it will be used normally as there is no
conflict.
</para>
</listitem>
<listitem>
<para>
If the <literal>CASCADE</> clause is given, the
<replaceable class="parameter">schema_name</replaceable> will only
be used for the missing required extensions which do not specify
<literal>schema</> in their control files.
</para>
</listitem>
<listitem>
<para>
If <replaceable class="parameter">schema_name</replaceable> is not
the same as the one in extension's control file and the
<literal>CASCADE</> clause is not given, error will be thrown.
</para>
</listitem>
</itemizedlist>
</para>
<para> <para>
Remember that the extension itself is not considered to be within any Remember that the extension itself is not considered to be within any
schema: extensions have unqualified names that must be unique schema: extensions have unqualified names that must be unique
...@@ -139,6 +169,18 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name ...@@ -139,6 +169,18 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
</para> </para>
</listitem> </listitem>
</varlistentry> </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. Other options are not recursively
applied when using this clause.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
......
...@@ -43,11 +43,13 @@ ...@@ -43,11 +43,13 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/alter.h" #include "commands/alter.h"
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
#include "funcapi.h" #include "funcapi.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "tcop/utility.h" #include "tcop/utility.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -1165,18 +1167,25 @@ find_update_path(List *evi_list, ...@@ -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 static ObjectAddress
CreateExtension(CreateExtensionStmt *stmt) CreateExtensionInternal(CreateExtensionStmt *stmt, List *parents)
{ {
DefElem *d_schema = NULL; DefElem *d_schema = NULL;
DefElem *d_new_version = NULL; DefElem *d_new_version = NULL;
DefElem *d_old_version = NULL; DefElem *d_old_version = NULL;
char *schemaName; DefElem *d_cascade = NULL;
Oid schemaOid; char *schemaName = NULL;
Oid schemaOid = InvalidOid;
char *versionName; char *versionName;
char *oldVersionName; char *oldVersionName;
bool cascade = false;
Oid extowner = GetUserId(); Oid extowner = GetUserId();
ExtensionControlFile *pcontrol; ExtensionControlFile *pcontrol;
ExtensionControlFile *control; ExtensionControlFile *control;
...@@ -1187,41 +1196,6 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1187,41 +1196,6 @@ CreateExtension(CreateExtensionStmt *stmt)
ListCell *lc; ListCell *lc;
ObjectAddress address; 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 * 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 * any non-ASCII data, so there is no need to worry about encoding at this
...@@ -1260,6 +1234,15 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1260,6 +1234,15 @@ CreateExtension(CreateExtensionStmt *stmt)
errmsg("conflicting or redundant options"))); errmsg("conflicting or redundant options")));
d_old_version = defel; 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 else
elog(ERROR, "unrecognized option: %s", defel->defname); elog(ERROR, "unrecognized option: %s", defel->defname);
} }
...@@ -1337,33 +1320,37 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1337,33 +1320,37 @@ CreateExtension(CreateExtensionStmt *stmt)
{ {
/* /*
* User given schema, CREATE EXTENSION ... WITH SCHEMA ... * 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); schemaName = strVal(d_schema->arg);
if (control->schema != NULL && /* If the user is giving us the schema name, it must exist already. */
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 */
schemaOid = get_namespace_oid(schemaName, false); 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 * 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; schemaName = control->schema;
/* Find or create the schema in case it does not exist. */
schemaOid = get_namespace_oid(schemaName, true); schemaOid = get_namespace_oid(schemaName, true);
if (schemaOid == InvalidOid) if (!OidIsValid(schemaOid))
{ {
CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt); CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
...@@ -1375,16 +1362,17 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1375,16 +1362,17 @@ CreateExtension(CreateExtensionStmt *stmt)
/* /*
* CreateSchemaCommand includes CommandCounterIncrement, so new * CreateSchemaCommand includes CommandCounterIncrement, so new
* schema is now visible * schema is now visible.
*/ */
schemaOid = get_namespace_oid(schemaName, false); schemaOid = get_namespace_oid(schemaName, false);
} }
} }
else else if (!OidIsValid(schemaOid))
{ {
/* /*
* Else, use the current default creation namespace, which is the * Neither user nor author of the extension specified schema, use the
* first explicit entry in the search_path. * current default creation namespace, which is the first explicit
* entry in the search_path.
*/ */
List *search_path = fetch_search_path(false); List *search_path = fetch_search_path(false);
...@@ -1423,16 +1411,65 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1423,16 +1411,65 @@ CreateExtension(CreateExtensionStmt *stmt)
Oid reqext; Oid reqext;
Oid reqschema; 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); reqext = get_extension_oid(curreq, true);
if (!OidIsValid(reqext)) if (!OidIsValid(reqext))
ereport(ERROR, {
(errcode(ERRCODE_UNDEFINED_OBJECT), if (cascade)
errmsg("required extension \"%s\" is not installed", {
curreq))); 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); reqschema = get_extension_schema(reqext);
requiredExtensions = lappend_oid(requiredExtensions, reqext); requiredExtensions = lappend_oid(requiredExtensions, reqext);
requiredSchemas = lappend_oid(requiredSchemas, reqschema); requiredSchemas = lappend_oid(requiredSchemas, reqschema);
...@@ -1473,6 +1510,52 @@ CreateExtension(CreateExtensionStmt *stmt) ...@@ -1473,6 +1510,52 @@ CreateExtension(CreateExtensionStmt *stmt)
return address; return address;
} }
/*
* 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 * InsertExtensionTuple
* *
......
...@@ -3876,6 +3876,10 @@ create_extension_opt_item: ...@@ -3876,6 +3876,10 @@ create_extension_opt_item:
{ {
$$ = makeDefElem("old_version", (Node *)makeString($2)); $$ = makeDefElem("old_version", (Node *)makeString($2));
} }
| CASCADE
{
$$ = makeDefElem("cascade", (Node *)makeInteger(TRUE));
}
; ;
/***************************************************************************** /*****************************************************************************
......
...@@ -2264,7 +2264,12 @@ psql_completion(const char *text, int start, int end) ...@@ -2264,7 +2264,12 @@ psql_completion(const char *text, int start, int end)
/* CREATE EXTENSION <name> */ /* CREATE EXTENSION <name> */
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
pg_strcasecmp(prev2_wd, "EXTENSION") == 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 */ /* CREATE FOREIGN */
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
......
...@@ -9,6 +9,7 @@ SUBDIRS = \ ...@@ -9,6 +9,7 @@ SUBDIRS = \
commit_ts \ commit_ts \
dummy_seclabel \ dummy_seclabel \
test_ddl_deparse \ test_ddl_deparse \
test_extensions \
test_parser \ test_parser \
test_rls_hooks \ test_rls_hooks \
test_shm_mq \ test_shm_mq \
......
# Generated subdirectories
/log/
/results/
/tmp_check/
# 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
-- 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
-- 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;
/* 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
comment = 'Test extension 1'
default_version = '1.0'
schema = 'test_ext1'
relocatable = false
requires = 'test_ext2,test_ext4'
/* 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
comment = 'Test extension 2'
default_version = '1.0'
relocatable = true
requires = 'test_ext3,test_ext5'
/* 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
comment = 'Test extension 3'
default_version = '1.0'
relocatable = true
/* 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
comment = 'Test extension 4'
default_version = '1.0'
relocatable = true
requires = 'test_ext5'
/* 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
comment = 'Test extension 5'
default_version = '1.0'
relocatable = true
/* 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
comment = 'Test extension cyclic 1'
default_version = '1.0'
relocatable = true
requires = 'test_ext_cyclic2'
/* 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
comment = 'Test extension cyclic 2'
default_version = '1.0'
relocatable = true
requires = 'test_ext_cyclic1'
...@@ -41,7 +41,8 @@ my $contrib_extrasource = { ...@@ -41,7 +41,8 @@ my $contrib_extrasource = {
'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ], }; 'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ], };
my @contrib_excludes = ( my @contrib_excludes = (
'commit_ts', 'hstore_plperl', 'hstore_plpython', 'intagg', '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 # Set of variables for frontend modules
my $frontend_defines = { 'initdb' => 'FRONTEND' }; my $frontend_defines = { 'initdb' => 'FRONTEND' };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment