>From 7d5db7faae0b30a70ceae020638fa7779810339d Mon Sep 17 00:00:00 2001 From: Petr Jelinek 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 ] extension_name [ WITH ] [ SCHEMA schema_name ] [ VERSION version ] [ FROM old_version ] + [ RECURSIVE ] @@ -139,6 +140,16 @@ CREATE EXTENSION [ IF NOT EXISTS ] extension_name + + + RECURSIVE + + + Try to install extension including the required dependencies + recursively. + + + 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