aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2002-05-22 17:21:02 +0000
committerPeter Eisentraut <peter_e@gmx.net>2002-05-22 17:21:02 +0000
commitd60f10b0e74173653d17c09750a791afe6f56404 (patch)
tree9803f14bc9ce71e3a056a447b6999847bb2a8dd6 /src/backend
parentdf9c8e1a39dc0502c62e164aa94e7e810bcd2009 (diff)
downloadpostgresql-d60f10b0e74173653d17c09750a791afe6f56404.tar.gz
postgresql-d60f10b0e74173653d17c09750a791afe6f56404.zip
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.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_aggregate.c3
-rw-r--r--src/backend/catalog/pg_proc.c200
-rw-r--r--src/backend/commands/functioncmds.c28
-rw-r--r--src/backend/commands/proclang.c20
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c4
-rw-r--r--src/backend/parser/gram.y16
-rw-r--r--src/backend/parser/keywords.c3
-rw-r--r--src/backend/utils/adt/sets.c3
9 files changed, 199 insertions, 81 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 1853acda73c..3a3d749e19d 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.47 2002/05/21 22:05:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.48 2002/05/22 17:20:58 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -136,6 +136,7 @@ AggregateCreate(const char *aggName,
false, /* doesn't return a set */
finaltype, /* returnType */
INTERNALlanguageId, /* languageObjectId */
+ 0,
"aggregate_dummy", /* placeholder proc */
"-", /* probin */
true, /* isAgg */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 4d656d98ba2..e7421ef2077 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -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);
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 06870b0d3d2..a619a71b435 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.5 2002/05/18 13:47:59 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.6 2002/05/22 17:20:58 petere Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -388,6 +388,7 @@ CreateFunction(CreateFunctionStmt *stmt)
char *language;
char languageName[NAMEDATALEN];
Oid languageOid;
+ Oid languageValidator;
char *funcname;
Oid namespaceId;
AclResult aclresult;
@@ -457,6 +458,8 @@ CreateFunction(CreateFunctionStmt *stmt)
aclcheck_error(ACLCHECK_NO_PRIV, NameStr(languageStruct->lanname));
}
+ languageValidator = languageStruct->lanvalidator;
+
ReleaseSysCache(languageTuple);
/*
@@ -477,6 +480,28 @@ CreateFunction(CreateFunctionStmt *stmt)
interpret_AS_clause(languageOid, languageName, as_clause,
&prosrc_str, &probin_str);
+ if (languageOid == INTERNALlanguageId)
+ {
+ /*
+ * In PostgreSQL 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 (strlen(prosrc_str) == 0)
+ prosrc_str = funcname;
+ }
+
+ if (languageOid == ClanguageId)
+ {
+ /* If link symbol is specified as "-", substitute procedure name */
+ if (strcmp(prosrc_str, "-") == 0)
+ prosrc_str = funcname;
+ }
+
/*
* And now that we have all the parameters, and know we're permitted
* to do so, go ahead and create the function.
@@ -487,6 +512,7 @@ CreateFunction(CreateFunctionStmt *stmt)
returnsSet,
prorettype,
languageOid,
+ languageValidator,
prosrc_str, /* converted to text later */
probin_str, /* converted to text later */
false, /* not an aggregate */
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 6cabbf0ec13..626c05f59cd 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -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.32 2002/05/21 22:05:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.33 2002/05/22 17:20:58 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
#include "commands/proclang.h"
#include "commands/defrem.h"
#include "fmgr.h"
@@ -39,7 +40,7 @@ void
CreateProceduralLanguage(CreatePLangStmt *stmt)
{
char languageName[NAMEDATALEN];
- Oid procOid;
+ Oid procOid, valProcOid;
Oid typev[FUNC_MAX_ARGS];
char nulls[Natts_pg_language];
Datum values[Natts_pg_language];
@@ -76,9 +77,21 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
elog(ERROR, "PL handler function %s() doesn't exist",
NameListToString(stmt->plhandler));
if (get_func_rettype(procOid) != InvalidOid)
- elog(ERROR, "PL handler function %s() isn't of return type Opaque",
+ elog(ERROR, "PL handler function %s() does not return type \"opaque\"",
NameListToString(stmt->plhandler));
+ /* validate the validator function */
+ if (stmt->plvalidator)
+ {
+ typev[0] = OIDOID;
+ valProcOid = LookupFuncName(stmt->plvalidator, 1, typev);
+ if (!OidIsValid(valProcOid))
+ elog(ERROR, "PL validator function %s(oid) doesn't exist",
+ NameListToString(stmt->plvalidator));
+ }
+ else
+ valProcOid = 0;
+
/*
* Insert the new language into pg_language
*/
@@ -93,6 +106,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
values[i++] = BoolGetDatum(true); /* lanispl */
values[i++] = BoolGetDatum(stmt->pltrusted);
values[i++] = ObjectIdGetDatum(procOid);
+ values[i++] = ObjectIdGetDatum(valProcOid);
values[i++] = DirectFunctionCall1(textin,
CStringGetDatum(stmt->plcompiler));
nulls[i] = 'n'; /* lanacl */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1f0cc11934f..5bc16dc9b82 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
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.187 2002/05/17 18:32:52 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.188 2002/05/22 17:20:58 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2418,6 +2418,7 @@ _copyCreatePLangStmt(CreatePLangStmt *from)
if (from->plname)
newnode->plname = pstrdup(from->plname);
Node_Copy(from, newnode, plhandler);
+ Node_Copy(from, newnode, plvalidator);
if (from->plcompiler)
newnode->plcompiler = pstrdup(from->plcompiler);
newnode->pltrusted = from->pltrusted;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f48d6d033f4..b4f576fc881 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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.134 2002/05/17 18:32:52 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.135 2002/05/22 17:20:59 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1252,6 +1252,8 @@ _equalCreatePLangStmt(CreatePLangStmt *a, CreatePLangStmt *b)
return false;
if (!equal(a->plhandler, b->plhandler))
return false;
+ if (!equal(a->plvalidator, b->plvalidator))
+ return false;
if (!equalstr(a->plcompiler, b->plcompiler))
return false;
if (a->pltrusted != b->pltrusted)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 999910161d0..d1109b58a7e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.318 2002/05/19 15:16:55 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.319 2002/05/22 17:20:59 petere Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -182,7 +182,7 @@ static void doNegateFloat(Value *v);
index_name, name, function_name, file_name
%type <list> func_name, handler_name, qual_Op, qual_all_Op, OptUseOp,
- opt_class
+ opt_class, opt_validator
%type <range> qualified_name, OptConstrFromTable
@@ -375,7 +375,7 @@ static void doNegateFloat(Value *v);
UNENCRYPTED, UNION, UNIQUE, UNKNOWN, UNLISTEN, UNTIL, UPDATE, USAGE,
USER, USING,
- VACUUM, VALID, VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW, VOLATILE,
+ VACUUM, VALID, VALIDATOR, VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW, VOLATILE,
WHEN, WHERE, WITH, WITHOUT, WORK,
YEAR_P,
ZONE
@@ -1835,12 +1835,13 @@ IntegerOnly: Iconst
*****************************************************************************/
CreatePLangStmt: CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
- HANDLER handler_name opt_lancompiler
+ HANDLER handler_name opt_validator opt_lancompiler
{
CreatePLangStmt *n = makeNode(CreatePLangStmt);
n->plname = $5;
n->plhandler = $7;
- n->plcompiler = $8;
+ n->plvalidator = $8;
+ n->plcompiler = $9;
n->pltrusted = $2;
$$ = (Node *)n;
}
@@ -1864,6 +1865,10 @@ opt_lancompiler: LANCOMPILER Sconst { $$ = $2; }
| /*EMPTY*/ { $$ = ""; }
;
+opt_validator: VALIDATOR handler_name { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
DropPLangStmt: DROP opt_procedural LANGUAGE ColId_or_Sconst
{
DropPLangStmt *n = makeNode(DropPLangStmt);
@@ -6357,6 +6362,7 @@ unreserved_keyword:
| USAGE
| VACUUM
| VALID
+ | VALIDATOR
| VALUES
| VARYING
| VERSION
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 36900127ea8..2cf91572a00 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.110 2002/05/17 18:32:52 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.111 2002/05/22 17:20:59 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -292,6 +292,7 @@ static const ScanKeyword ScanKeywords[] = {
{"using", USING},
{"vacuum", VACUUM},
{"valid", VALID},
+ {"validator", VALIDATOR},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
index 298bdbdec82..e93ed50ff0a 100644
--- a/src/backend/utils/adt/sets.c
+++ b/src/backend/utils/adt/sets.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.44 2002/05/18 13:47:59 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.45 2002/05/22 17:21:00 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,6 +58,7 @@ SetDefine(char *querystr, Oid elemType)
true, /* returnsSet */
elemType, /* returnType */
SQLlanguageId, /* language */
+ SQLvalidatorId,
querystr, /* prosrc */
fileName, /* probin */
false, /* not aggregate */