Hi,
I am getting tired installing manually required extensions manually. I
was wondering if we might want to add option to CREATE SEQUENCE that
would allow automatic creation of the extensions required by the
extension that is being installed by the user.
I also wrote some prototype patch that implements this.
I call it prototype mainly because the syntax (CREATE EXTENSION ...
RECURSIVE) could be improved, I originally wanted to do something like
INCLUDING DEPENDENCIES but that need news (unreserved) keyword and I
don't think it's worth it, plus it's wordy.
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
>From 7d5db7faae0b30a70ceae020638fa7779810339d 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 RECURSIVE
---
doc/src/sgml/ref/create_extension.sgml | 11 ++++
src/backend/commands/extension.c | 61 ++++++++++++++++++++--
src/backend/parser/gram.y | 4 ++
src/test/modules/test_extensions/.gitignore | 3 ++
src/test/modules/test_extensions/Makefile | 22 ++++++++
.../test_extensions/expected/test_extensions.out | 16 ++++++
.../test_extensions/sql/test_extensions.sql | 11 ++++
.../modules/test_extensions/test_ext1--1.0.sql | 0
src/test/modules/test_extensions/test_ext1.control | 4 ++
.../modules/test_extensions/test_ext2--1.0.sql | 0
src/test/modules/test_extensions/test_ext2.control | 4 ++
.../modules/test_extensions/test_ext3--1.0.sql | 0
src/test/modules/test_extensions/test_ext3.control | 3 ++
.../test_extensions/test_ext_cyclic1--1.0.sql | 0
.../test_extensions/test_ext_cyclic1.control | 4 ++
.../test_extensions/test_ext_cyclic2--1.0.sql | 0
.../test_extensions/test_ext_cyclic2.control | 4 ++
17 files changed, 143 insertions(+), 4 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/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml
index a1e7e4f8..d151fd6 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> ]
+ [ RECURSIVE ]
</synopsis>
</refsynopsisdiv>
@@ -139,6 +140,16 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>RECURSIVE</></term>
+ <listitem>
+ <para>
+ Try to install extension including the required dependencies
+ recursively.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 5cc74d0..4395519 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -46,6 +46,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"
@@ -1176,10 +1177,13 @@ CreateExtension(CreateExtensionStmt *stmt)
DefElem *d_schema = NULL;
DefElem *d_new_version = NULL;
DefElem *d_old_version = NULL;
+ DefElem *d_recursive = NULL;
char *schemaName;
Oid schemaOid;
char *versionName;
char *oldVersionName;
+ bool recursive = false;
+ List *recursive_parents = NIL;
Oid extowner = GetUserId();
ExtensionControlFile *pcontrol;
ExtensionControlFile *control;
@@ -1263,6 +1267,14 @@ CreateExtension(CreateExtensionStmt *stmt)
errmsg("conflicting or redundant options")));
d_old_version = defel;
}
+ else if (strcmp(defel->defname, "recursive") == 0)
+ {
+ if (d_recursive)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_recursive = defel;
+ }
else
elog(ERROR, "unrecognized option: %s", defel->defname);
}
@@ -1405,6 +1417,15 @@ CreateExtension(CreateExtensionStmt *stmt)
list_free(search_path);
}
+ /* Validate the RECURSIVE option. */
+ if (d_recursive)
+ {
+ recursive = true;
+ if (d_recursive->arg)
+ recursive_parents = (List *) d_recursive->arg;
+ }
+
+
/*
* We don't check creation rights on the target namespace here. If the
* extension script actually creates any objects there, it will fail if
@@ -1432,10 +1453,42 @@ 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 (recursive)
+ {
+ CreateExtensionStmt *ces;
+ List *parents;
+ ListCell *lc;
+
+ /* Check for cyclic dependency between extension. */
+ foreach(lc, recursive_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\"",
+ pname, stmt->extname)));
+ }
+
+ /* Create and execute new CREATE EXTENSION statement. */
+ ces = makeNode(CreateExtensionStmt);
+ ces->extname = curreq;
+ ces->if_not_exists = false;
+ parents = lappend(list_copy(recursive_parents), stmt->extname);
+ ces->options = list_make1(makeDefElem("recursive",
+ (Node *) parents));
+ CreateExtension(ces);
+ list_free(parents);
+ }
+ 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 e0ff6f1..7eaf080 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3856,6 +3856,10 @@ create_extension_opt_item:
{
$$ = makeDefElem("old_version", (Node *)makeString($2));
}
+ | RECURSIVE
+ {
+ $$ = makeDefElem("recursive", NULL);
+ }
;
/*****************************************************************************
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..ba90eea
--- /dev/null
+++ b/src/test/modules/test_extensions/expected/test_extensions.out
@@ -0,0 +1,16 @@
+CREATE EXTENSION test_ext1;
+ERROR: required extension "test_ext2" is not installed
+CREATE EXTENSION test_ext1 RECURSIVE;
+SELECT * FROM pg_extension WHERE extname LIKE 'test_ext%';
+ extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
+-----------+----------+--------------+----------------+------------+-----------+--------------
+ test_ext3 | 10 | 2200 | t | 1.0 | |
+ test_ext2 | 10 | 2200 | t | 1.0 | |
+ test_ext1 | 10 | 2200 | t | 1.0 | |
+(3 rows)
+
+CREATE EXTENSION test_ext_cyclic1 RECURSIVE;
+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;
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..7c0c542
--- /dev/null
+++ b/src/test/modules/test_extensions/sql/test_extensions.sql
@@ -0,0 +1,11 @@
+CREATE EXTENSION test_ext1;
+
+
+CREATE EXTENSION test_ext1 RECURSIVE;
+SELECT * FROM pg_extension WHERE extname LIKE 'test_ext%';
+
+CREATE EXTENSION test_ext_cyclic1 RECURSIVE;
+
+DROP EXTENSION test_ext1;
+DROP EXTENSION test_ext2;
+DROP EXTENSION test_ext3;
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..e69de29
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..6b1049b
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext1.control
@@ -0,0 +1,4 @@
+comment = 'Test extension 1'
+default_version = '1.0'
+relocatable = true
+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..e69de29
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..e69de29
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..e69de29
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..e69de29
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'
--
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