aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/functioncmds.c181
-rw-r--r--src/backend/nodes/copyfuncs.c16
-rw-r--r--src/backend/nodes/equalfuncs.c14
-rw-r--r--src/backend/parser/gram.y84
-rw-r--r--src/backend/tcop/utility.c12
-rw-r--r--src/include/commands/defrem.h3
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/parsenodes.h9
-rw-r--r--src/test/regress/expected/alter_table.out35
-rw-r--r--src/test/regress/sql/alter_table.sql17
10 files changed, 307 insertions, 67 deletions
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index e2afbce1029..cbe14d130be 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -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
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4acf02908b6..92f7168ae9f 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f80da7572b5..cbd99dab720 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -954,6 +954,15 @@ _equalFunctionParameter(FunctionParameter *a, FunctionParameter *b)
}
static bool
+_equalAlterFunctionStmt(AlterFunctionStmt *a, AlterFunctionStmt *b)
+{
+ COMPARE_NODE_FIELD(func);
+ COMPARE_NODE_FIELD(actions);
+
+ return true;
+}
+
+static bool
_equalRemoveAggrStmt(RemoveAggrStmt *a, RemoveAggrStmt *b)
{
COMPARE_NODE_FIELD(aggname);
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 69e70821970..a88262d4328 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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 */
+ ;
+
/*****************************************************************************
*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 006f904f72e..a3b946647ac 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 30f576d7811..546e9f8dc0d 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.62 2004/12/31 22:03:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.63 2005/03/14 00:19:37 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,6 +49,7 @@ extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
extern void RenameFunction(List *name, List *argtypes, const char *newname);
extern void AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId);
+extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCast(DropCastStmt *stmt);
extern void DropCastById(Oid castOid);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 291b1f281f2..5d70180a0c5 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.163 2004/12/31 22:03:34 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.164 2005/03/14 00:19:37 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -223,6 +223,7 @@ typedef enum NodeTag
T_FetchStmt,
T_IndexStmt,
T_CreateFunctionStmt,
+ T_AlterFunctionStmt,
T_RemoveAggrStmt,
T_RemoveFuncStmt,
T_RemoveOperStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cd3eb634d4a..8cf274b14ce 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.273 2005/03/10 23:21:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.274 2005/03/14 00:19:37 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1397,6 +1397,13 @@ typedef struct FunctionParameter
/* someday add IN/OUT/INOUT indicator here */
} FunctionParameter;
+typedef struct AlterFunctionStmt
+{
+ NodeTag type;
+ FuncWithArgs *func; /* name and args of function */
+ List *actions; /* list of DefElem */
+} AlterFunctionStmt;
+
/* ----------------------
* Drop Aggregate Statement
* ----------------------
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index fbb23a4b127..b9edbc649d4 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -1235,3 +1235,38 @@ select * from another;
(3 rows)
drop table another;
+--
+-- alter function
+--
+create function test_strict(text) returns text as
+ 'select coalesce($1, ''got passed a null'');'
+ language sql returns null on null input;
+select test_strict(NULL);
+ test_strict
+-------------
+
+(1 row)
+
+alter function test_strict(text) called on null input;
+select test_strict(NULL);
+ test_strict
+-------------------
+ got passed a null
+(1 row)
+
+create function non_strict(text) returns text as
+ 'select coalesce($1, ''got passed a null'');'
+ language sql called on null input;
+select non_strict(NULL);
+ non_strict
+-------------------
+ got passed a null
+(1 row)
+
+alter function non_strict(text) returns null on null input;
+select non_strict(NULL);
+ non_strict
+------------
+
+(1 row)
+
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index e6be119ff7d..445fabf7e09 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -975,3 +975,20 @@ alter table another
select * from another;
drop table another;
+
+--
+-- alter function
+--
+create function test_strict(text) returns text as
+ 'select coalesce($1, ''got passed a null'');'
+ language sql returns null on null input;
+select test_strict(NULL);
+alter function test_strict(text) called on null input;
+select test_strict(NULL);
+
+create function non_strict(text) returns text as
+ 'select coalesce($1, ''got passed a null'');'
+ language sql called on null input;
+select non_strict(NULL);
+alter function non_strict(text) returns null on null input;
+select non_strict(NULL); \ No newline at end of file