1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-08 11:42:09 +03:00

Provide an upgrade strategy for dump files containing functions declared

with OPAQUE.  CREATE LANGUAGE, CREATE TRIGGER, and CREATE TYPE will all
accept references to functions declared with OPAQUE --- but they will
issue a NOTICE, and will modify the function entries in pg_proc to have
the preferred type-safe argument or result types instead of OPAQUE.
Per recent pghackers discussions.
This commit is contained in:
Tom Lane
2002-09-21 18:39:26 +00:00
parent bc49968764
commit eb3adab568
12 changed files with 239 additions and 93 deletions

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.80 2002/09/04 20:31:15 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.81 2002/09/21 18:39:25 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -44,17 +44,17 @@
/*
* Translate the input language name to lower case.
*
* Output buffer should be NAMEDATALEN long.
* Output buffer must be NAMEDATALEN long.
*/
void
case_translate_language_name(const char *input, char *output)
{
int i;
MemSet(output, 0, NAMEDATALEN); /* ensure result Name is zero-filled */
for (i = 0; i < NAMEDATALEN - 1 && input[i]; ++i)
output[i] = tolower((unsigned char) input[i]);
output[i] = '\0';
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.21 2002/09/18 21:35:20 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.22 2002/09/21 18:39:25 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@ -591,6 +591,85 @@ RemoveFunctionById(Oid funcOid)
}
/*
* SetFunctionReturnType - change declared return type of a function
*
* This is presently only used for adjusting legacy functions that return
* OPAQUE to return whatever we find their correct definition should be.
* The caller should emit a suitable NOTICE explaining what we did.
*/
void
SetFunctionReturnType(Oid funcOid, Oid newRetType)
{
Relation pg_proc_rel;
HeapTuple tup;
Form_pg_proc procForm;
pg_proc_rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "SetFunctionReturnType: couldn't find tuple for function %u",
funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
if (procForm->prorettype != OPAQUEOID)
elog(ERROR, "SetFunctionReturnType: function %u doesn't return OPAQUE",
funcOid);
/* okay to overwrite copied tuple */
procForm->prorettype = newRetType;
/* update the catalog and its indexes */
simple_heap_update(pg_proc_rel, &tup->t_self, tup);
CatalogUpdateIndexes(pg_proc_rel, tup);
heap_close(pg_proc_rel, RowExclusiveLock);
}
/*
* SetFunctionArgType - change declared argument type of a function
*
* As above, but change an argument's type.
*/
void
SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType)
{
Relation pg_proc_rel;
HeapTuple tup;
Form_pg_proc procForm;
pg_proc_rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "SetFunctionArgType: couldn't find tuple for function %u",
funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
if (argIndex < 0 || argIndex >= procForm->pronargs ||
procForm->proargtypes[argIndex] != OPAQUEOID)
elog(ERROR, "SetFunctionArgType: function %u doesn't take OPAQUE",
funcOid);
/* okay to overwrite copied tuple */
procForm->proargtypes[argIndex] = newArgType;
/* update the catalog and its indexes */
simple_heap_update(pg_proc_rel, &tup->t_self, tup);
CatalogUpdateIndexes(pg_proc_rel, tup);
heap_close(pg_proc_rel, RowExclusiveLock);
}
/*
* CREATE CAST

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.42 2002/09/04 20:31:15 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.43 2002/09/21 18:39:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,6 +43,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
char languageName[NAMEDATALEN];
Oid procOid,
valProcOid;
Oid funcrettype;
Oid typev[FUNC_MAX_ARGS];
char nulls[Natts_pg_language];
Datum values[Natts_pg_language];
@ -80,10 +81,24 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (!OidIsValid(procOid))
elog(ERROR, "function %s() doesn't exist",
NameListToString(stmt->plhandler));
if (get_func_rettype(procOid) != LANGUAGE_HANDLEROID)
elog(ERROR, "function %s() does not return type %s",
NameListToString(stmt->plhandler),
format_type_be(LANGUAGE_HANDLEROID));
funcrettype = get_func_rettype(procOid);
if (funcrettype != LANGUAGE_HANDLEROID)
{
/*
* We allow OPAQUE just so we can load old dump files. When we
* see a handler function declared OPAQUE, change it to
* LANGUAGE_HANDLER.
*/
if (funcrettype == OPAQUEOID)
{
elog(NOTICE, "CreateProceduralLanguage: changing return type of function %s() from OPAQUE to LANGUAGE_HANDLER",
NameListToString(stmt->plhandler));
SetFunctionReturnType(procOid, LANGUAGE_HANDLEROID);
}
else
elog(ERROR, "CreateProceduralLanguage: function %s() must return LANGUAGE_HANDLER",
NameListToString(stmt->plhandler));
}
/* validate the validator function */
if (stmt->plvalidator)

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.131 2002/09/04 20:31:15 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.132 2002/09/21 18:39:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,6 +24,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
@ -75,6 +76,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
HeapTuple tuple;
Oid fargtypes[FUNC_MAX_ARGS];
Oid funcoid;
Oid funcrettype;
Oid trigoid;
int found = 0;
int i;
@ -217,22 +219,23 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
if (!OidIsValid(funcoid))
elog(ERROR, "CreateTrigger: function %s() does not exist",
NameListToString(stmt->funcname));
tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcoid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "CreateTrigger: function %s() does not exist",
NameListToString(stmt->funcname));
if (((Form_pg_proc) GETSTRUCT(tuple))->prorettype != TRIGGEROID)
funcrettype = get_func_rettype(funcoid);
if (funcrettype != TRIGGEROID)
{
/* OPAQUE is deprecated, but allowed for backwards compatibility */
if (((Form_pg_proc) GETSTRUCT(tuple))->prorettype == OPAQUEOID)
elog(NOTICE, "CreateTrigger: OPAQUE is deprecated, use type TRIGGER instead to define trigger functions");
/*
* We allow OPAQUE just so we can load old dump files. When we
* see a trigger function declared OPAQUE, change it to TRIGGER.
*/
if (funcrettype == OPAQUEOID)
{
elog(NOTICE, "CreateTrigger: changing return type of function %s() from OPAQUE to TRIGGER",
NameListToString(stmt->funcname));
SetFunctionReturnType(funcoid, TRIGGEROID);
}
else
elog(ERROR, "CreateTrigger: function %s() must return TRIGGER",
NameListToString(stmt->funcname));
}
ReleaseSysCache(tuple);
/*
* Build the new pg_trigger tuple.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.14 2002/09/19 22:48:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.15 2002/09/21 18:39:25 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -188,13 +188,19 @@ DefineType(List *names, List *parameters)
/*
* Look to see if type already exists (presumably as a shell; if not,
* TypeCreate will complain). If it does then the declarations of the
* I/O functions might use it.
* TypeCreate will complain). If it doesn't, create it as a shell,
* so that the OID is known for use in the I/O function definitions.
*/
typoid = GetSysCacheOid(TYPENAMENSP,
CStringGetDatum(typeName),
ObjectIdGetDatum(typeNamespace),
0, 0);
if (!OidIsValid(typoid))
{
typoid = TypeShellMake(typeName, typeNamespace);
/* Make new shell type visible for modification below */
CommandCounterIncrement();
}
/*
* Convert I/O proc names to OIDs
@ -203,15 +209,18 @@ DefineType(List *names, List *parameters)
outputOid = findTypeIOFunction(outputName, typoid, true);
/*
* Verify that I/O procs return the expected thing. OPAQUE is an
* allowed, but deprecated, alternative to the fully type-safe
* choices.
* Verify that I/O procs return the expected thing. If we see OPAQUE,
* complain and change it to the correct type-safe choice.
*/
resulttype = get_func_rettype(inputOid);
if (!(OidIsValid(typoid) && resulttype == typoid))
if (resulttype != typoid)
{
if (resulttype == OPAQUEOID)
elog(NOTICE, "DefineType: OPAQUE is deprecated, instead declare I/O functions using their true datatypes");
{
elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to %s",
NameListToString(inputName), typeName);
SetFunctionReturnType(inputOid, typoid);
}
else
elog(ERROR, "Type input function %s must return %s",
NameListToString(inputName), typeName);
@ -220,7 +229,11 @@ DefineType(List *names, List *parameters)
if (resulttype != CSTRINGOID)
{
if (resulttype == OPAQUEOID)
elog(NOTICE, "DefineType: OPAQUE is deprecated, instead declare I/O functions using their true datatypes");
{
elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to CSTRING",
NameListToString(outputName));
SetFunctionReturnType(outputOid, CSTRINGOID);
}
else
elog(ERROR, "Type output function %s must return cstring",
NameListToString(outputName));
@ -670,8 +683,8 @@ RemoveDomain(List *names, DropBehavior behavior)
/*
* Find a suitable I/O function for a type.
*
* typeOid is the type's OID, if it already exists as a shell type,
* otherwise InvalidOid.
* typeOid is the type's OID (which will already exist, if only as a shell
* type).
*/
static Oid
findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
@ -683,35 +696,15 @@ findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
{
/*
* Output functions can take a single argument of the type, or two
* arguments (data value, element OID). The signature may use
* OPAQUE in place of the actual type name; this is the only
* possibility if the type doesn't yet exist as a shell.
* arguments (data value, element OID).
*
* Note: although we could throw a NOTICE in this routine if OPAQUE
* is used, we do not because of the probability that it'd be
* duplicate with a notice issued in DefineType.
* For backwards compatibility we allow OPAQUE in place of the actual
* type name; if we see this, we issue a NOTICE and fix up the
* pg_proc entry.
*/
if (OidIsValid(typeOid))
{
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
argList[0] = typeOid;
procOid = LookupFuncName(procname, 1, argList);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
procOid = LookupFuncName(procname, 2, argList);
if (OidIsValid(procOid))
return procOid;
}
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
argList[0] = OPAQUEOID;
argList[0] = typeOid;
procOid = LookupFuncName(procname, 1, argList);
if (OidIsValid(procOid))
@ -723,9 +716,37 @@ findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
if (OidIsValid(procOid))
return procOid;
/* Prefer type name over OPAQUE in the failure message. */
if (OidIsValid(typeOid))
argList[0] = typeOid;
/* No luck, try it with OPAQUE */
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
argList[0] = OPAQUEOID;
procOid = LookupFuncName(procname, 1, argList);
if (!OidIsValid(procOid))
{
argList[1] = OIDOID;
procOid = LookupFuncName(procname, 2, argList);
}
if (OidIsValid(procOid))
{
/* Found, but must complain and fix the pg_proc entry */
elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to %s",
NameListToString(procname), format_type_be(typeOid));
SetFunctionArgType(procOid, 0, typeOid);
/*
* Need CommandCounterIncrement since DefineType will likely
* try to alter the pg_proc tuple again.
*/
CommandCounterIncrement();
return procOid;
}
/* Use type name, not OPAQUE, in the failure message. */
argList[0] = typeOid;
func_error("TypeCreate", procname, 1, argList, NULL);
}
@ -733,8 +754,10 @@ findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
{
/*
* Input functions can take a single argument of type CSTRING, or
* three arguments (string, element OID, typmod). The signature
* may use OPAQUE in place of CSTRING.
* three arguments (string, element OID, typmod).
*
* For backwards compatibility we allow OPAQUE in place of CSTRING;
* if we see this, we issue a NOTICE and fix up the pg_proc entry.
*/
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
@ -751,20 +774,35 @@ findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
if (OidIsValid(procOid))
return procOid;
/* No luck, try it with OPAQUE */
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
argList[0] = OPAQUEOID;
procOid = LookupFuncName(procname, 1, argList);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
if (!OidIsValid(procOid))
{
argList[1] = OIDOID;
argList[2] = INT4OID;
procOid = LookupFuncName(procname, 3, argList);
}
procOid = LookupFuncName(procname, 3, argList);
if (OidIsValid(procOid))
{
/* Found, but must complain and fix the pg_proc entry */
elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to CSTRING",
NameListToString(procname));
SetFunctionArgType(procOid, 0, CSTRINGOID);
/*
* Need CommandCounterIncrement since DefineType will likely
* try to alter the pg_proc tuple again.
*/
CommandCounterIncrement();
return procOid;
}
/* Use CSTRING (preferred) in the error message */
argList[0] = CSTRINGOID;