From 0599a547173c4781c3edc447af4e1adc06e52973 Mon Sep 17 00:00:00 2001
From: erthalion <9erthalion6@gmail.com>
Date: Fri, 15 Sep 2017 21:33:07 +0200
Subject: [PATCH 5/5] Function depends on
---
doc/src/sgml/ref/create_function.sgml | 12 +++
src/backend/commands/functioncmds.c | 119 ++++++++++++++++++------
src/backend/parser/gram.y | 23 ++++-
src/test/regress/expected/create_function_3.out | 15 +++
src/test/regress/sql/create_function_3.sql | 12 +++
5 files changed, 151 insertions(+), 30 deletions(-)
diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml
index 072e033687..7996a02322 100644
--- a/doc/src/sgml/ref/create_function.sgml
+++ b/doc/src/sgml/ref/create_function.sgml
@@ -27,6 +27,7 @@ CREATE [ OR REPLACE ] FUNCTION
{ LANGUAGE lang_name
| TRANSFORM { FOR TYPE type_name } [, ... ]
| WINDOW
+ | DEPENDS ON func_name [, ... ]
| IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
@@ -293,6 +294,17 @@ CREATE [ OR REPLACE ] FUNCTION
+ DEPENDS ON
+
+
+ DEPENDS ON indicates that the function depends
+ on other functions. This is useful when you want to prevent this
+ function to be invalid because some of its dependencies were deleted.
+
+
+
+
+
IMMUTABLE
STABLE
VOLATILE
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 7de844b2ca..dff9d59822 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -64,6 +64,7 @@
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
+#include "utils/regproc.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -608,6 +609,7 @@ compute_attributes_sql_style(ParseState *pstate,
char **language,
Node **transform,
bool *windowfunc_p,
+ Node **depends_on,
char *volatility_p,
bool *strict_p,
bool *security_definer,
@@ -622,6 +624,7 @@ compute_attributes_sql_style(ParseState *pstate,
DefElem *language_item = NULL;
DefElem *transform_item = NULL;
DefElem *windowfunc_item = NULL;
+ DefElem *depends_item = NULL;
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_item = NULL;
@@ -685,6 +688,15 @@ compute_attributes_sql_style(ParseState *pstate,
/* recognized common option */
continue;
}
+ else if (strcmp(defel->defname, "depends") == 0)
+ {
+ if (depends_item)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+ depends_item = defel;
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
@@ -716,6 +728,8 @@ compute_attributes_sql_style(ParseState *pstate,
*transform = transform_item->arg;
if (windowfunc_item)
*windowfunc_p = intVal(windowfunc_item->arg);
+ if (depends_item)
+ *depends_on = depends_item->arg;
if (volatility_item)
*volatility_p = interpret_func_volatility(volatility_item);
if (strict_item)
@@ -868,6 +882,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
Oid languageOid;
Oid languageValidator;
Node *transformDefElem = NULL;
+ Node *dependsOnDefElem = NULL;
char *funcname;
Oid namespaceId;
AclResult aclresult;
@@ -876,6 +891,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
ArrayType *parameterModes;
ArrayType *parameterNames;
List *parameterDefaults;
+ List *dependencies = NIL;
Oid variadicArgType;
List *trftypes_list = NIL;
ArrayType *trftypes;
@@ -892,6 +908,8 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
Form_pg_language languageStruct;
List *as_clause;
char parallel;
+ ObjectAddress result;
+ ListCell *lc;
/* Convert list of names to a name and namespace */
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
@@ -918,7 +936,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
compute_attributes_sql_style(pstate,
stmt->options,
&as_clause, &language, &transformDefElem,
- &isWindowFunc, &volatility,
+ &isWindowFunc, &dependsOnDefElem, &volatility,
&isStrict, &security, &isLeakProof,
&proconfig, &procost, &prorows, ¶llel);
@@ -983,6 +1001,33 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
}
}
+ if (dependsOnDefElem)
+ {
+ ListCell *lc;
+
+ foreach(lc, castNode(List, dependsOnDefElem))
+ {
+ DefElem *defel = (DefElem *) lfirst(lc);
+ Value *func = (Value *) defel->arg;
+ List *names;
+ FuncCandidateList clist;
+
+ /*
+ * Parse the name into components and see if it matches any pg_proc
+ * entries in the current search path.
+ */
+ names = stringToQualifiedNameList(strVal(func));
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false, true);
+
+ if (clist == NULL || clist->next != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("Function %s does not exist", strVal(func))));
+
+ dependencies = lappend_oid(dependencies, clist->oid);
+ }
+ }
+
/*
* Convert remaining parameters of CREATE to form wanted by
* ProcedureCreate.
@@ -1080,32 +1125,52 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
- return ProcedureCreate(funcname,
- namespaceId,
- stmt->replace,
- returnsSet,
- prorettype,
- GetUserId(),
- languageOid,
- languageValidator,
- prosrc_str, /* converted to text later */
- probin_str, /* converted to text later */
- false, /* not an aggregate */
- isWindowFunc,
- security,
- isLeakProof,
- isStrict,
- volatility,
- parallel,
- parameterTypes,
- PointerGetDatum(allParameterTypes),
- PointerGetDatum(parameterModes),
- PointerGetDatum(parameterNames),
- parameterDefaults,
- PointerGetDatum(trftypes),
- PointerGetDatum(proconfig),
- procost,
- prorows);
+ result = ProcedureCreate(funcname,
+ namespaceId,
+ stmt->replace,
+ returnsSet,
+ prorettype,
+ GetUserId(),
+ languageOid,
+ languageValidator,
+ prosrc_str, /* converted to text later */
+ probin_str, /* converted to text later */
+ false, /* not an aggregate */
+ isWindowFunc,
+ security,
+ isLeakProof,
+ isStrict,
+ volatility,
+ parallel,
+ parameterTypes,
+ PointerGetDatum(allParameterTypes),
+ PointerGetDatum(parameterModes),
+ PointerGetDatum(parameterNames),
+ parameterDefaults,
+ PointerGetDatum(trftypes),
+ PointerGetDatum(proconfig),
+ procost,
+ prorows);
+
+
+ /*
+ * Dependencies already verified, so for every dependent function
+ * we can create an internal dependency record to prevent them
+ * from being deleted.
+ */
+ foreach(lc, dependencies)
+ {
+ ObjectAddress dependencyAddress;
+
+ dependencyAddress.classId = ProcedureRelationId;
+ dependencyAddress.objectId = lfirst_oid(lc);
+ /* no subitems, since it's a function */
+ dependencyAddress.objectSubId = 0;
+
+ recordDependencyOn(&dependencyAddress, &result, DEPENDENCY_INTERNAL);
+ }
+
+ return result;
}
/*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5eb398118e..411cce0855 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -395,7 +395,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
relation_expr_list dostmt_opt_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
- publication_name_list
+ publication_name_list depends_on_func_list
+
+%type func_elem
%type group_by_list
%type group_by_item empty_grouping_set rollup_clause cube_clause
@@ -7697,6 +7699,10 @@ createfunc_opt_item:
{
$$ = makeDefElem("window", (Node *)makeInteger(TRUE), @1);
}
+ | DEPENDS depends_on_func_list
+ {
+ $$ = makeDefElem("depends", (Node *)$2, @1);
+ }
| common_func_opt_item
{
$$ = $1;
@@ -7711,8 +7717,19 @@ func_as: Sconst { $$ = list_make1(makeString($1)); }
;
transform_type_list:
- FOR TYPE_P Typename { $$ = list_make1($3); }
- | transform_type_list ',' FOR TYPE_P Typename { $$ = lappend($1, $5); }
+ FOR TYPE_P Typename { $$ = list_make1($3); }
+ | transform_type_list ',' FOR TYPE_P Typename { $$ = lappend($1, $5); }
+ ;
+
+depends_on_func_list: ON func_elem { $$ = list_make1($2); }
+ | depends_on_func_list ',' func_elem { $$ = lappend($1, $3); }
+ ;
+
+func_elem:
+ type_function_name
+ {
+ $$ = makeDefElem("func_name", (Node *)makeString($1), @1);
+ }
;
opt_definition:
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index b5e19485e5..15e0022ae7 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -248,3 +248,18 @@ drop cascades to function functext_f_4(integer)
drop cascades to function functest_b_2(bigint)
DROP USER regress_unpriv_user;
RESET search_path;
+-- Depends on
+CREATE FUNCTION dependency_func1() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION dependency_func2() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION dependency_func3() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION complex_func1() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL DEPENDS ON dependency_func1;
+CREATE FUNCTION complex_func2() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL DEPENDS ON dependency_func2, dependency_func3;
+DROP FUNCTION dependency_func1;
+ERROR: cannot drop function dependency_func1() because function complex_func1() requires it
+HINT: You can drop function complex_func1() instead.
+DROP FUNCTION dependency_func2;
+ERROR: cannot drop function dependency_func2() because function complex_func2() requires it
+HINT: You can drop function complex_func2() instead.
+DROP FUNCTION dependency_func3;
+ERROR: cannot drop function dependency_func3() because function complex_func2() requires it
+HINT: You can drop function complex_func2() instead.
diff --git a/src/test/regress/sql/create_function_3.sql b/src/test/regress/sql/create_function_3.sql
index 0a0e407aab..363cefe62e 100644
--- a/src/test/regress/sql/create_function_3.sql
+++ b/src/test/regress/sql/create_function_3.sql
@@ -171,3 +171,15 @@ DROP FUNCTION functest_b_2; -- error, ambiguous
DROP SCHEMA temp_func_test CASCADE;
DROP USER regress_unpriv_user;
RESET search_path;
+
+
+-- Depends on
+CREATE FUNCTION dependency_func1() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION dependency_func2() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION dependency_func3() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL;
+CREATE FUNCTION complex_func1() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL DEPENDS ON dependency_func1;
+CREATE FUNCTION complex_func2() RETURNS INTEGER AS 'SELECT 1;' LANGUAGE SQL DEPENDS ON dependency_func2, dependency_func3;
+
+DROP FUNCTION dependency_func1;
+DROP FUNCTION dependency_func2;
+DROP FUNCTION dependency_func3;
--
2.13.0