<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_function.sgml,v 1.26 2001/09/03 12:57:49 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_function.sgml,v 1.27 2001/10/02 21:39:35 tgl Exp $
-->
<refentry id="SQL-CREATEFUNCTION">
<refsynopsisdiv>
<synopsis>
-CREATE FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+CREATE [ OR REPLACE ] FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
RETURNS <replaceable class="parameter">rettype</replaceable>
AS '<replaceable class="parameter">definition</replaceable>'
LANGUAGE <replaceable class="parameter">langname</replaceable>
[ WITH ( <replaceable class="parameter">attribute</replaceable> [, ...] ) ]
-CREATE FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+CREATE [ OR REPLACE ] FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
RETURNS <replaceable class="parameter">rettype</replaceable>
AS '<replaceable class="parameter">obj_file</replaceable>', '<replaceable class="parameter">link_symbol</replaceable>'
LANGUAGE <replaceable class="parameter">langname</replaceable>
<para>
<command>CREATE FUNCTION</command> defines a new function.
+ <command>CREATE OR REPLACE FUNCTION</command> will either create
+ a new function, or replace an existing definition.
<variablelist>
<title>Parameters</title>
</para>
<para>
- Use <command>DROP FUNCTION</command>
- to remove user-defined functions.
- </para>
-
- <para>
The full <acronym>SQL</acronym> type syntax is allowed for
input arguments and return value. However, some details of the
type specification (e.g., the precision field for
linkend="sql-load"> command.
</para>
+ <para>
+ Use <command>DROP FUNCTION</command>
+ to remove user-defined functions.
+ </para>
+
+ <para>
+ To update the definition of an existing function, use
+ <command>CREATE OR REPLACE FUNCTION</command>. Note that it is
+ not possible to change the name or argument types of a function
+ this way (if you tried, you'd just be creating a new, distinct
+ function). Also, <command>CREATE OR REPLACE FUNCTION</command>
+ will not let you change the return type of an existing function.
+ To do that, you must drop and re-create the function.
+ </para>
+
+ <para>
+ If you drop and then re-create a function, the new function is not
+ the same entity as the old; you will break existing rules, views,
+ triggers, etc that referred to the old function. Use
+ <command>CREATE OR REPLACE FUNCTION</command> to change a function
+ definition without breaking objects that refer to the function.
+ </para>
+
</refsect1>
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.59 2001/09/08 01:10:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.60 2001/10/02 21:39:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
Oid
ProcedureCreate(char *procedureName,
+ bool replace,
bool returnsSet,
char *returnTypeName,
char *languageName,
int i;
Relation rel;
HeapTuple tup;
+ HeapTuple oldtup;
bool defined;
uint16 parameterCount;
char nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
+ char replaces[Natts_pg_proc];
Oid languageObjectId;
Oid typeObjectId;
List *x;
typev[parameterCount++] = toid;
}
- /* Check for duplicate definition */
- if (SearchSysCacheExists(PROCNAME,
- PointerGetDatum(procedureName),
- UInt16GetDatum(parameterCount),
- PointerGetDatum(typev),
- 0))
- elog(ERROR, "function %s already exists with same argument types",
- procedureName);
-
if (languageObjectId == SQLlanguageId)
{
}
/*
- * All seems OK; prepare the tuple to be inserted into pg_proc.
+ * All seems OK; prepare the data to be inserted into pg_proc.
*/
for (i = 0; i < Natts_pg_proc; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
+ replaces[i] = 'r';
}
i = 0;
CStringGetDatum(probin));
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
tupDesc = rel->rd_att;
- tup = heap_formtuple(tupDesc,
- values,
- nulls);
- heap_insert(rel, tup);
+ /* Check for pre-existing definition */
+ oldtup = SearchSysCache(PROCNAME,
+ PointerGetDatum(procedureName),
+ UInt16GetDatum(parameterCount),
+ PointerGetDatum(typev),
+ 0);
+ if (HeapTupleIsValid(oldtup))
+ {
+ /* There is one; okay to replace it? */
+ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
+
+ if (!replace)
+ elog(ERROR, "function %s already exists with same argument types",
+ procedureName);
+ if (GetUserId() != oldproc->proowner && !superuser())
+ elog(ERROR, "ProcedureCreate: you do not have permission to replace function %s",
+ procedureName);
+ /*
+ * Not okay to change the return type of the existing proc, since
+ * existing rules, views, etc may depend on the return type.
+ */
+ if (typeObjectId != oldproc->prorettype ||
+ returnsSet != oldproc->proretset)
+ elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
+ "\n\tUse DROP FUNCTION first.");
+
+ /* Okay, do it... */
+ tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ ReleaseSysCache(oldtup);
+ }
+ else
+ {
+ /* Creating a new procedure */
+ tup = heap_formtuple(tupDesc, values, nulls);
+ heap_insert(rel, tup);
+ }
+
+ /* Need to update indices for either the insert or update case */
if (RelationGetForm(rel)->relhasindex)
{
Relation idescs[Num_pg_proc_indices];
CatalogIndexInsert(idescs, Num_pg_proc_indices, rel, tup);
CatalogCloseIndices(Num_pg_proc_indices, idescs);
}
- heap_close(rel, RowExclusiveLock);
+
retval = tup->t_data->t_oid;
heap_freetuple(tup);
+
+ heap_close(rel, RowExclusiveLock);
+
return retval;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.60 2001/09/08 01:10:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.61 2001/10/02 21:39:35 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
* to do so, go ahead and create the function.
*/
ProcedureCreate(stmt->funcname,
+ stmt->replace,
returnsSet,
prorettype,
languageName,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.156 2001/09/18 01:59:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.157 2001/10/02 21:39:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
ProcedureStmt *newnode = makeNode(ProcedureStmt);
+ newnode->replace = from->replace;
newnode->funcname = pstrdup(from->funcname);
Node_Copy(from, newnode, argTypes);
Node_Copy(from, newnode, returnType);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.104 2001/09/18 01:59:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.105 2001/10/02 21:39:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static bool
_equalProcedureStmt(ProcedureStmt *a, ProcedureStmt *b)
{
+ if (a->replace != b->replace)
+ return false;
if (!equalstr(a->funcname, b->funcname))
return false;
if (!equal(a->argTypes, b->argTypes))
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.255 2001/10/01 04:19:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.256 2001/10/02 21:39:35 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
%type <list> createdb_opt_list, createdb_opt_item
%type <ival> opt_lock, lock_type
-%type <boolean> opt_force
+%type <boolean> opt_force, opt_or_replace
%type <list> user_list
VALUES, VARCHAR, VARYING, VIEW,
WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
-/* Keywords (in SQL3 reserved words) */
+/* Keywords (in SQL99 reserved words) */
%token CHAIN, CHARACTERISTICS,
DEFERRABLE, DEFERRED,
IMMEDIATE, INITIALLY, INOUT,
OFF, OUT,
PATH_P, PENDANT,
- RESTRICT,
+ REPLACE, RESTRICT,
TRIGGER,
WITHOUT
/*****************************************************************************
*
* QUERY:
- * define function <fname>
+ * create [or replace] function <fname>
* [(<type-1> { , <type-n>})]
* returns <type-r>
* as <filename or code in language as appropriate>
- * language <lang> [with
- * [ arch_pct = <percentage | pre-defined>]
- * [, disk_pct = <percentage | pre-defined>]
- * [, byte_pct = <percentage | pre-defined>]
- * [, perbyte_cpu = <int | pre-defined>]
- * [, percall_cpu = <int | pre-defined>]
- * [, iscachable] ]
+ * language <lang> [with parameters]
*
*****************************************************************************/
-ProcedureStmt: CREATE FUNCTION func_name func_args
+ProcedureStmt: CREATE opt_or_replace FUNCTION func_name func_args
RETURNS func_return AS func_as LANGUAGE ColId_or_Sconst opt_with
{
ProcedureStmt *n = makeNode(ProcedureStmt);
- n->funcname = $3;
- n->argTypes = $4;
- n->returnType = (Node *)$6;
- n->withClause = $11;
- n->as = $8;
- n->language = $10;
+ n->replace = $2;
+ n->funcname = $4;
+ n->argTypes = $5;
+ n->returnType = (Node *) $7;
+ n->withClause = $12;
+ n->as = $9;
+ n->language = $11;
$$ = (Node *)n;
};
+opt_or_replace: OR REPLACE { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
opt_with: WITH definition { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
| REINDEX { $$ = "reindex"; }
| RELATIVE { $$ = "relative"; }
| RENAME { $$ = "rename"; }
+ | REPLACE { $$ = "replace"; }
| RESTRICT { $$ = "restrict"; }
| RETURNS { $$ = "returns"; }
| REVOKE { $$ = "revoke"; }
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.97 2001/08/26 16:56:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.98 2001/10/02 21:39:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{"reindex", REINDEX},
{"relative", RELATIVE},
{"rename", RENAME},
+ {"replace", REPLACE},
{"reset", RESET},
{"restrict", RESTRICT},
{"returns", RETURNS},
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.38 2001/09/08 01:10:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.39 2001/10/02 21:39:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char repl[Natts_pg_proc];
setoid = ProcedureCreate(procname, /* changed below, after oid known */
+ false, /* don't replace */
true, /* returnsSet */
typename, /* returnTypeName */
"sql", /* languageName */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.212 2001/09/28 08:09:13 thomas Exp $
+ * $Id: pg_proc.h,v 1.213 2001/10/02 21:39:36 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
* prototypes for functions pg_proc.c
*/
extern Oid ProcedureCreate(char *procedureName,
+ bool replace,
bool returnsSet,
char *returnTypeName,
char *languageName,
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.144 2001/09/18 01:59:06 tgl Exp $
+ * $Id: parsenodes.h,v 1.145 2001/10/02 21:39:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct ProcedureStmt
{
NodeTag type;
+ bool replace; /* T => replace if already exists */
char *funcname; /* name of function to create */
List *argTypes; /* list of argument types (TypeName nodes) */
Node *returnType; /* the return type (a TypeName node) */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.44 2001/09/19 14:09:32 meskes Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.45 2001/10/02 21:39:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{"force", FORCE},
{"foreign", FOREIGN},
{"forward", FORWARD},
+ {"freeze", FREEZE},
{"from", FROM},
{"full", FULL},
{"function", FUNCTION},
{"reindex", REINDEX},
{"relative", RELATIVE},
{"rename", RENAME},
+ {"replace", REPLACE},
{"reset", RESET},
{"restrict", RESTRICT},
{"returns", RETURNS},
{"truncate", TRUNCATE},
{"trusted", TRUSTED},
{"type", TYPE_P},
- {"unencrypted", UNENCRYPTED},
+ {"unencrypted", UNENCRYPTED},
{"union", UNION},
{"unique", UNIQUE},
{"unknown", UNKNOWN},
%token DEFERRABLE, DEFERRED,
IMMEDIATE, INITIALLY,
PENDANT,
- RESTRICT,
+ REPLACE, RESTRICT,
TRIGGER
/* Keywords (in SQL92 non-reserved words) */
%type <str> constraints_set_mode comment_type comment_cl comment_ag
%type <str> CreateGroupStmt AlterGroupStmt DropGroupStmt key_delete
%type <str> opt_force key_update CreateSchemaStmt PosIntStringConst
-%type <str> IntConst PosIntConst grantee_list func_type
+%type <str> IntConst PosIntConst grantee_list func_type opt_or_replace
%type <str> select_limit opt_for_update_clause CheckPointStmt
%type <str> ECPGWhenever ECPGConnect connection_target ECPGOpen
/*****************************************************************************
*
* QUERY:
- * define function <fname>
- * [(<type-1> { , <type-n>})]
- * returns <type-r>
- * as <filename or code in language as appropriate>
- * language <lang> [with
- * [ arch_pct = <percentage | pre-defined>]
- * [, disk_pct = <percentage | pre-defined>]
- * [, byte_pct = <percentage | pre-defined>]
- * [, perbyte_cpu = <int | pre-defined>]
- * [, percall_cpu = <int | pre-defined>]
- * [, iscachable]
+ * create [or replace] function <fname>
+ * [(<type-1> { , <type-n>})]
+ * returns <type-r>
+ * as <filename or code in language as appropriate>
+ * language <lang> [with parameters]
*
*****************************************************************************/
-ProcedureStmt: CREATE FUNCTION func_name func_args
+ProcedureStmt: CREATE opt_or_replace FUNCTION func_name func_args
RETURNS func_return AS func_as LANGUAGE ColId_or_Sconst opt_with
{
- $$ = cat_str(10, make_str("create function"), $3, $4, make_str("returns"), $6, make_str("as"), $8, make_str("language"), $10, $11);
+ $$ = cat_str(12, make_str("create"), $2, make_str("function"), $4, $5, make_str("returns"), $7, make_str("as"), $9, make_str("language"), $11, $12);
}
+opt_or_replace: OR REPLACE { $$ = make_str("or replace"); }
+ | /*EMPTY*/ { $$ = EMPTY; }
+ ;
+
opt_with: WITH definition { $$ = cat2_str(make_str("with"), $2); }
| /*EMPTY*/ { $$ = EMPTY; }
;
| REINDEX { $$ = make_str("reindex"); }
| RELATIVE { $$ = make_str("relative"); }
| RENAME { $$ = make_str("rename"); }
+ | REPLACE { $$ = make_str("replace"); }
| RESTRICT { $$ = make_str("restrict"); }
| RETURNS { $$ = make_str("returns"); }
| REVOKE { $$ = make_str("revoke"); }