1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Allow ALTER FUNCTION to change a function's strictness, volatility, and

whether or not it is a security definer. Changing a function's strictness
is required by SQL2003, and the other capabilities make sense. Also, allow
an optional RESTRICT noise word to be specified, for SQL conformance.

Some trivial regression tests added and the documentation has been
updated.
This commit is contained in:
Neil Conway
2005-03-14 00:19:37 +00:00
parent 41e2a80f57
commit c069655441
12 changed files with 384 additions and 81 deletions

View File

@@ -3,14 +3,14 @@
* functioncmds.c
*
* Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
* CAST commands.
* CAST commands.
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.55 2005/03/13 05:19:26 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.56 2005/03/14 00:19:36 neilc Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -195,12 +195,75 @@ examine_parameter_list(List *parameter, Oid languageOid,
return parameterCount;
}
/*
* Recognize one of the options that can be passed to both CREATE
* FUNCTION and ALTER FUNCTION and return it via one of the out
* parameters. Returns true if the passed option was recognized. If
* the out parameter we were going to assign to points to non-NULL,
* raise a duplicate error.
*/
static bool
compute_common_attribute(DefElem *defel,
DefElem **volatility_item,
DefElem **strict_item,
DefElem **security_item)
{
if (strcmp(defel->defname, "volatility") == 0)
{
if (*volatility_item)
goto duplicate_error;
*volatility_item = defel;
}
else if (strcmp(defel->defname, "strict") == 0)
{
if (*strict_item)
goto duplicate_error;
*strict_item = defel;
}
else if (strcmp(defel->defname, "security") == 0)
{
if (*security_item)
goto duplicate_error;
*security_item = defel;
}
else
return false;
/* Recognized an option */
return true;
duplicate_error:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
return false; /* keep compiler quiet */
}
static char
interpret_func_volatility(DefElem *defel)
{
char *str = strVal(defel->arg);
if (strcmp(str, "immutable") == 0)
return PROVOLATILE_IMMUTABLE;
else if (strcmp(str, "stable") == 0)
return PROVOLATILE_STABLE;
else if (strcmp(str, "volatile") == 0)
return PROVOLATILE_VOLATILE;
else
{
elog(ERROR, "invalid volatility \"%s\"", str);
return 0; /* keep compiler quiet */
}
}
/*
* Dissect the list of options assembled in gram.y into function
* attributes.
*/
static void
compute_attributes_sql_style(List *options,
List **as,
@@ -236,29 +299,13 @@ compute_attributes_sql_style(List *options,
errmsg("conflicting or redundant options")));
language_item = defel;
}
else if (strcmp(defel->defname, "volatility") == 0)
else if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
&security_item))
{
if (volatility_item)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
volatility_item = defel;
}
else if (strcmp(defel->defname, "strict") == 0)
{
if (strict_item)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
strict_item = defel;
}
else if (strcmp(defel->defname, "security") == 0)
{
if (security_item)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
security_item = defel;
/* recognized common option */
continue;
}
else
elog(ERROR, "option \"%s\" not recognized",
@@ -280,18 +327,7 @@ compute_attributes_sql_style(List *options,
errmsg("no language specified")));
if (volatility_item)
{
if (strcmp(strVal(volatility_item->arg), "immutable") == 0)
*volatility_p = PROVOLATILE_IMMUTABLE;
else if (strcmp(strVal(volatility_item->arg), "stable") == 0)
*volatility_p = PROVOLATILE_STABLE;
else if (strcmp(strVal(volatility_item->arg), "volatile") == 0)
*volatility_p = PROVOLATILE_VOLATILE;
else
elog(ERROR, "invalid volatility \"%s\"",
strVal(volatility_item->arg));
}
*volatility_p = interpret_func_volatility(volatility_item);
if (strict_item)
*strict_p = intVal(strict_item->arg);
if (security_item)
@@ -301,7 +337,7 @@ compute_attributes_sql_style(List *options,
/*-------------
* Interpret the parameters *parameters and return their contents via
* out parameters *isStrict_p and *volatility_p.
* *isStrict_p and *volatility_p.
*
* These parameters supply optional information about a function.
* All have defaults if not specified. Parameters:
@@ -347,9 +383,7 @@ compute_attributes_with_style(List *parameters, bool *isStrict_p, char *volatili
* In all other cases
*
* AS <object reference, or sql code>
*
*/
static void
interpret_AS_clause(Oid languageOid, const char *languageName, List *as,
char **prosrc_str_p, char **probin_str_p)
@@ -799,7 +833,74 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId)
heap_close(rel, NoLock);
}
/*
* Implements the ALTER FUNCTION utility command (except for the
* RENAME and OWNER clauses, which are handled as part of the generic
* ALTER framework).
*/
void
AlterFunction(AlterFunctionStmt *stmt)
{
HeapTuple tup;
Oid funcOid;
Form_pg_proc procForm;
Relation rel;
ListCell *l;
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_def_item = NULL;
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
funcOid = LookupFuncNameTypeNames(stmt->func->funcname,
stmt->func->funcargs,
false);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
/* Permission check: must own function */
if (!pg_proc_ownercheck(funcOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(stmt->func->funcname));
if (procForm->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(stmt->func->funcname))));
/* Examine requested actions. */
foreach (l, stmt->actions)
{
DefElem *defel = (DefElem *) lfirst(l);
if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
&security_def_item) == false)
elog(ERROR, "option \"%s\" not recognized", defel->defname);
}
if (volatility_item)
procForm->provolatile = interpret_func_volatility(volatility_item);
if (strict_item)
procForm->proisstrict = intVal(strict_item->arg);
if (security_def_item)
procForm->prosecdef = intVal(security_def_item->arg);
/* Do the update */
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* SetFunctionReturnType - change declared return type of a function

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.297 2005/03/10 23:21:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.298 2005/03/14 00:19:36 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1892,6 +1892,17 @@ _copyFunctionParameter(FunctionParameter *from)
return newnode;
}
static AlterFunctionStmt *
_copyAlterFunctionStmt(AlterFunctionStmt *from)
{
AlterFunctionStmt *newnode = makeNode(AlterFunctionStmt);
COPY_NODE_FIELD(func);
COPY_NODE_FIELD(actions);
return newnode;
}
static RemoveAggrStmt *
_copyRemoveAggrStmt(RemoveAggrStmt *from)
{
@@ -2882,6 +2893,9 @@ copyObject(void *from)
case T_FunctionParameter:
retval = _copyFunctionParameter(from);
break;
case T_AlterFunctionStmt:
retval = _copyAlterFunctionStmt(from);
break;
case T_RemoveAggrStmt:
retval = _copyRemoveAggrStmt(from);
break;

View File

@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.236 2005/03/10 23:21:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.237 2005/03/14 00:19:36 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -953,6 +953,15 @@ _equalFunctionParameter(FunctionParameter *a, FunctionParameter *b)
return true;
}
static bool
_equalAlterFunctionStmt(AlterFunctionStmt *a, AlterFunctionStmt *b)
{
COMPARE_NODE_FIELD(func);
COMPARE_NODE_FIELD(actions);
return true;
}
static bool
_equalRemoveAggrStmt(RemoveAggrStmt *a, RemoveAggrStmt *b)
{
@@ -2014,6 +2023,9 @@ equal(void *a, void *b)
case T_FunctionParameter:
retval = _equalFunctionParameter(a, b);
break;
case T_AlterFunctionStmt:
retval = _equalAlterFunctionStmt(a, b);
break;
case T_RemoveAggrStmt:
retval = _equalRemoveAggrStmt(a, b);
break;

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.483 2005/02/02 06:36:01 neilc Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.484 2005/03/14 00:19:36 neilc Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -142,7 +142,7 @@ static void doNegateFloat(Value *v);
DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
GrantStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt ReindexStmt RemoveAggrStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
SelectStmt TransactionStmt TruncateStmt
@@ -213,7 +213,7 @@ static void doNegateFloat(Value *v);
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
opt_distinct opt_definition func_args
func_args_list func_as createfunc_opt_list
func_args_list func_as createfunc_opt_list alterfunc_opt_list
oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list
sort_clause opt_sort_clause sortby_list index_params
@@ -231,7 +231,7 @@ static void doNegateFloat(Value *v);
%type <range> into_clause OptTempTableName
%type <defelt> createfunc_opt_item
%type <defelt> createfunc_opt_item common_func_opt_item
%type <fun_param> func_arg
%type <typnam> func_return func_type aggr_argtype
@@ -486,6 +486,7 @@ stmtmulti: stmtmulti ';' stmt
stmt :
AlterDatabaseSetStmt
| AlterDomainStmt
| AlterFunctionStmt
| AlterGroupStmt
| AlterOwnerStmt
| AlterSeqStmt
@@ -3371,14 +3372,21 @@ createfunc_opt_list:
| createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); }
;
createfunc_opt_item:
AS func_as
/*
* Options common to both CREATE FUNCTION and ALTER FUNCTION
*/
common_func_opt_item:
CALLED ON NULL_P INPUT_P
{
$$ = makeDefElem("as", (Node *)$2);
$$ = makeDefElem("strict", (Node *)makeInteger(FALSE));
}
| LANGUAGE ColId_or_Sconst
| RETURNS NULL_P ON NULL_P INPUT_P
{
$$ = makeDefElem("language", (Node *)makeString($2));
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
}
| STRICT_P
{
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
}
| IMMUTABLE
{
@@ -3392,18 +3400,7 @@ createfunc_opt_item:
{
$$ = makeDefElem("volatility", (Node *)makeString("volatile"));
}
| CALLED ON NULL_P INPUT_P
{
$$ = makeDefElem("strict", (Node *)makeInteger(FALSE));
}
| RETURNS NULL_P ON NULL_P INPUT_P
{
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
}
| STRICT_P
{
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
}
| EXTERNAL SECURITY DEFINER
{
$$ = makeDefElem("security", (Node *)makeInteger(TRUE));
@@ -3422,6 +3419,21 @@ createfunc_opt_item:
}
;
createfunc_opt_item:
AS func_as
{
$$ = makeDefElem("as", (Node *)$2);
}
| LANGUAGE ColId_or_Sconst
{
$$ = makeDefElem("language", (Node *)makeString($2));
}
| common_func_opt_item
{
$$ = $1;
}
;
func_as: Sconst { $$ = list_make1(makeString($1)); }
| Sconst ',' Sconst
{
@@ -3434,6 +3446,36 @@ opt_definition:
| /*EMPTY*/ { $$ = NIL; }
;
/*****************************************************************************
* ALTER FUNCTION
*
* RENAME and OWNER subcommands are already provided by the generic
* ALTER infrastructure, here we just specify alterations that can
* only be applied to functions.
*
*****************************************************************************/
AlterFunctionStmt:
ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict
{
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
n->func = (FuncWithArgs *) $3;
n->actions = $4;
$$ = (Node *) n;
}
;
alterfunc_opt_list:
/* At least one option must be specified */
common_func_opt_item { $$ = list_make1($1); }
| alterfunc_opt_list common_func_opt_item { $$ = lappend($1, $2); }
;
/* Ignored, merely for SQL compliance */
opt_restrict:
RESTRICT
| /* EMPTY */
;
/*****************************************************************************
*

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.233 2005/01/27 03:18:10 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.234 2005/03/14 00:19:36 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -277,6 +277,7 @@ check_xact_readonly(Node *parsetree)
{
case T_AlterDatabaseSetStmt:
case T_AlterDomainStmt:
case T_AlterFunctionStmt:
case T_AlterGroupStmt:
case T_AlterOwnerStmt:
case T_AlterSeqStmt:
@@ -711,6 +712,10 @@ ProcessUtility(Node *parsetree,
CreateFunction((CreateFunctionStmt *) parsetree);
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
AlterFunction((AlterFunctionStmt *) parsetree);
break;
case T_IndexStmt: /* CREATE INDEX */
{
IndexStmt *stmt = (IndexStmt *) parsetree;
@@ -1394,10 +1399,15 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER TABLE";
}
break;
case T_AlterDomainStmt:
tag = "ALTER DOMAIN";
break;
case T_AlterFunctionStmt:
tag = "ALTER FUNCTION";
break;
case T_GrantStmt:
{
GrantStmt *stmt = (GrantStmt *) parsetree;