1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-22 12:22:45 +03:00

Add optional "validator" function to languages that can validate the

function body (and other properties) as a function in the language
is created.  This generalizes ad hoc code that already existed for
the built-in languages.

The validation now happens after the pg_proc tuple of the new function
is created, so it is possible to define recursive SQL functions.

Add some regression test cases that cover bogus function definition
attempts.
This commit is contained in:
Peter Eisentraut
2002-05-22 17:21:02 +00:00
parent df9c8e1a39
commit d60f10b0e7
18 changed files with 339 additions and 102 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.73 2002/05/21 22:05:54 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.74 2002/05/22 17:20:58 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "fmgr.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
@@ -32,6 +33,9 @@
static void checkretval(Oid rettype, List *queryTreeList);
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
/* ----------------------------------------------------------------
@@ -45,6 +49,7 @@ ProcedureCreate(const char *procedureName,
bool returnsSet,
Oid returnType,
Oid languageObjectId,
Oid languageValidator,
const char *prosrc,
const char *probin,
bool isAgg,
@@ -66,7 +71,6 @@ ProcedureCreate(const char *procedureName,
char nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
char replaces[Natts_pg_proc];
List *querytree_list;
Oid typev[FUNC_MAX_ARGS];
Oid relid;
NameData procname;
@@ -126,12 +130,6 @@ ProcedureCreate(const char *procedureName,
}
}
if (!OidIsValid(returnType))
{
if (languageObjectId == SQLlanguageId)
elog(ERROR, "SQL functions cannot return type \"opaque\"");
}
/*
* don't allow functions of complex types that have the same name as
* existing attributes of the type
@@ -142,65 +140,6 @@ ProcedureCreate(const char *procedureName,
elog(ERROR, "method %s already an attribute of type %s",
procedureName, format_type_be(typev[0]));
/*
* If this is a postquel procedure, we parse it here in order to be
* sure that it contains no syntax errors. We should store the plan
* in an Inversion file for use later, but for now, we just store the
* procedure's text in the prosrc attribute.
*/
if (languageObjectId == SQLlanguageId)
{
querytree_list = pg_parse_and_rewrite((char *) prosrc,
typev,
parameterCount);
/* typecheck return value */
checkretval(returnType, querytree_list);
}
/*
* If this is an internal procedure, check that the given internal
* function name (the 'prosrc' value) is a known builtin function.
*
* NOTE: in Postgres versions before 6.5, the SQL name of the created
* function could not be different from the internal name, and
* 'prosrc' wasn't used. So there is code out there that does CREATE
* FUNCTION xyz AS '' LANGUAGE 'internal'. To preserve some modicum
* of backwards compatibility, accept an empty 'prosrc' value as
* meaning the supplied SQL function name.
*/
if (languageObjectId == INTERNALlanguageId)
{
if (strlen(prosrc) == 0)
prosrc = procedureName;
if (fmgr_internal_function((char *) prosrc) == InvalidOid)
elog(ERROR,
"there is no built-in function named \"%s\"",
prosrc);
}
/*
* If this is a dynamically loadable procedure, make sure that the
* library file exists, is loadable, and contains the specified link
* symbol. Also check for a valid function information record.
*
* We used to perform these checks only when the function was first
* called, but it seems friendlier to verify the library's validity at
* CREATE FUNCTION time.
*/
if (languageObjectId == ClanguageId)
{
void *libraryhandle;
/* If link symbol is specified as "-", substitute procedure name */
if (strcmp(prosrc, "-") == 0)
prosrc = procedureName;
(void) load_external_function((char *) probin,
(char *) prosrc,
true,
&libraryhandle);
(void) fetch_finfo_record(libraryhandle, (char *) prosrc);
}
/*
* All seems OK; prepare the data to be inserted into pg_proc.
@@ -316,6 +255,14 @@ ProcedureCreate(const char *procedureName,
heap_close(rel, RowExclusiveLock);
/* Verify function body */
if (OidIsValid(languageValidator))
{
/* Advance command counter so recursive functions can be defined */
CommandCounterIncrement();
OidFunctionCall1(languageValidator, retval);
}
return retval;
}
@@ -454,3 +401,122 @@ checkretval(Oid rettype, List *queryTreeList)
heap_close(reln, AccessShareLock);
}
/*
* Validator for internal functions
*
* Check that the given internal function name (the "prosrc" value) is
* a known builtin function.
*/
Datum
fmgr_internal_validator(PG_FUNCTION_ARGS)
{
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc proc;
bool isnull;
Datum tmp;
char *prosrc;
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup of function %u failed", funcoid);
proc = (Form_pg_proc) GETSTRUCT(tuple);
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
if (fmgr_internal_function(prosrc) == InvalidOid)
elog(ERROR, "there is no built-in function named \"%s\"", prosrc);
ReleaseSysCache(tuple);
PG_RETURN_BOOL(true);
}
/*
* Validator for C language functions
*
* Make sure that the library file exists, is loadable, and contains
* the specified link symbol. Also check for a valid function
* information record.
*/
Datum
fmgr_c_validator(PG_FUNCTION_ARGS)
{
Oid funcoid = PG_GETARG_OID(0);
void *libraryhandle;
HeapTuple tuple;
Form_pg_proc proc;
bool isnull;
Datum tmp;
char *prosrc;
char *probin;
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup of function %u failed", funcoid);
proc = (Form_pg_proc) GETSTRUCT(tuple);
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
if (isnull)
elog(ERROR, "null probin");
probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
(void) load_external_function(probin, prosrc, true, &libraryhandle);
(void) fetch_finfo_record(libraryhandle, prosrc);
ReleaseSysCache(tuple);
PG_RETURN_BOOL(true);
}
/*
* Validator for SQL language functions
*
* Parse it here in order to be sure that it contains no syntax
* errors.
*/
Datum
fmgr_sql_validator(PG_FUNCTION_ARGS)
{
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc proc;
List *querytree_list;
bool isnull;
Datum tmp;
char *prosrc;
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup of function %u failed", funcoid);
proc = (Form_pg_proc) GETSTRUCT(tuple);
if (!OidIsValid(proc->prorettype))
elog(ERROR, "SQL functions cannot return type \"opaque\"");
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
checkretval(proc->prorettype, querytree_list);
ReleaseSysCache(tuple);
PG_RETURN_BOOL(true);
}