mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Add CREATE OR REPLACE FUNCTION syntax to allow replacing a function
definition without changing the function's OID, thereby not breaking rules, views, triggers, etc that depend on it. From Gavin Sherry.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -41,6 +41,7 @@ static void checkretval(Oid rettype, List *queryTreeList);
|
||||
*/
|
||||
Oid
|
||||
ProcedureCreate(char *procedureName,
|
||||
bool replace,
|
||||
bool returnsSet,
|
||||
char *returnTypeName,
|
||||
char *languageName,
|
||||
@@ -58,10 +59,12 @@ ProcedureCreate(char *procedureName,
|
||||
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;
|
||||
@@ -120,15 +123,6 @@ ProcedureCreate(char *procedureName,
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -260,13 +254,14 @@ ProcedureCreate(char *procedureName,
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
@@ -293,14 +288,49 @@ ProcedureCreate(char *procedureName,
|
||||
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];
|
||||
@@ -309,9 +339,12 @@ ProcedureCreate(char *procedureName,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -324,6 +324,7 @@ CreateFunction(ProcedureStmt *stmt)
|
||||
* to do so, go ahead and create the function.
|
||||
*/
|
||||
ProcedureCreate(stmt->funcname,
|
||||
stmt->replace,
|
||||
returnsSet,
|
||||
prorettype,
|
||||
languageName,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2071,6 +2071,7 @@ _copyProcedureStmt(ProcedureStmt *from)
|
||||
{
|
||||
ProcedureStmt *newnode = makeNode(ProcedureStmt);
|
||||
|
||||
newnode->replace = from->replace;
|
||||
newnode->funcname = pstrdup(from->funcname);
|
||||
Node_Copy(from, newnode, argTypes);
|
||||
Node_Copy(from, newnode, returnType);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -941,6 +941,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
|
||||
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))
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -151,7 +151,7 @@ static void doNegateFloat(Value *v);
|
||||
%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
|
||||
|
||||
@@ -321,13 +321,13 @@ static void doNegateFloat(Value *v);
|
||||
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
|
||||
|
||||
@@ -2497,33 +2497,32 @@ RecipeStmt: EXECUTE RECIPE recipe_name
|
||||
/*****************************************************************************
|
||||
*
|
||||
* 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; }
|
||||
;
|
||||
@@ -5682,6 +5681,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; }
|
||||
| REINDEX { $$ = "reindex"; }
|
||||
| RELATIVE { $$ = "relative"; }
|
||||
| RENAME { $$ = "rename"; }
|
||||
| REPLACE { $$ = "replace"; }
|
||||
| RESTRICT { $$ = "restrict"; }
|
||||
| RETURNS { $$ = "returns"; }
|
||||
| REVOKE { $$ = "revoke"; }
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -216,6 +216,7 @@ static ScanKeyword ScanKeywords[] = {
|
||||
{"reindex", REINDEX},
|
||||
{"relative", RELATIVE},
|
||||
{"rename", RENAME},
|
||||
{"replace", REPLACE},
|
||||
{"reset", RESET},
|
||||
{"restrict", RESTRICT},
|
||||
{"returns", RETURNS},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -51,6 +51,7 @@ SetDefine(char *querystr, char *typename)
|
||||
char repl[Natts_pg_proc];
|
||||
|
||||
setoid = ProcedureCreate(procname, /* changed below, after oid known */
|
||||
false, /* don't replace */
|
||||
true, /* returnsSet */
|
||||
typename, /* returnTypeName */
|
||||
"sql", /* languageName */
|
||||
|
||||
Reference in New Issue
Block a user