Skip to content

Commit d60f10b

Browse files
committed
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.
1 parent df9c8e1 commit d60f10b

File tree

18 files changed

+339
-102
lines changed

18 files changed

+339
-102
lines changed

doc/src/sgml/ref/create_language.sgml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_language.sgml,v 1.23 2002/05/18 15:44:47 petere Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_language.sgml,v 1.24 2002/05/22 17:20:58 petere Exp $
33
PostgreSQL documentation
44
-->
55

@@ -17,7 +17,7 @@ PostgreSQL documentation
1717
<refsynopsisdiv>
1818
<synopsis>
1919
CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">langname</replaceable>
20-
HANDLER <replaceable class="parameter">call_handler</replaceable>
20+
HANDLER <replaceable class="parameter">call_handler</replaceable> [ VALIDATOR <replaceable>valfunction</replaceable> ]
2121
</synopsis>
2222
</refsynopsisdiv>
2323

@@ -113,6 +113,32 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">langna
113113
</para>
114114
</listitem>
115115
</varlistentry>
116+
117+
<varlistentry>
118+
<term><literal>VALIDATOR</literal> <replaceable class="parameter">valfunction</replaceable></term>
119+
120+
<listitem>
121+
<para>
122+
<replaceable class="parameter">valfunction</replaceable> is the
123+
name of a previously registered function that will be called
124+
when a new function in the language is created, to validate the
125+
new function. The validator function must take one argument of
126+
type <type>oid</type>, which will be the OID of the
127+
to-be-created function, and can have any return type. If no
128+
validator function is specified, then a new function will not
129+
be checked when it is created.
130+
</para>
131+
132+
<para>
133+
A validator function would typically inspect the function body
134+
for syntactical correctness, but it can also look at other
135+
properties of the function, for example if the language cannot
136+
handle certain argument types. To signal an error, the
137+
validator function should use the <function>elog()</function>
138+
function. The return value of the function is ignored.
139+
</para>
140+
</listitem>
141+
</varlistentry>
116142
</variablelist>
117143
</refsect1>
118144

doc/src/sgml/release.sgml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.137 2002/05/18 13:47:59 petere Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.138 2002/05/22 17:20:58 petere Exp $
33
-->
44

55
<appendix id="release">
@@ -24,6 +24,8 @@ CDATA means the content is "SGML-free", so you can write without
2424
worries about funny characters.
2525
-->
2626
<literallayout><![CDATA[
27+
Recursive SQL functions can be defined
28+
User-defined procedural languages can register a validator function to check new functions as they are created
2729
Functions can be executed with the privileges of the owner
2830
Syntax of CREATE FUNCTION has been extended to resemble SQL99
2931
Effects of SET within a transaction block now roll back if transaction aborts
@@ -72,7 +74,7 @@ Database and user-specific session defaults for run-time configuration variables
7274
<title>Changes</title>
7375

7476
<para>
75-
<programlisting>
77+
<literallayout>
7678
Ensure that sequence counters do not go backwards after a crash (Tom)
7779
Fix pgaccess kanji-coversion key binding (Tatsuo)
7880
Optimizer improvements (Tom)
@@ -90,7 +92,7 @@ contrib/tsearch dictionary improvements, see README.tsearch for
9092
an additional installation step (Thomas T. Thai, Teodor Sigaev)
9193
Fix for array subscripts handling (Tom)
9294
Allow EXECUTE of "CREATE TABLE AS ... SELECT" in PL/PgSQL (Tom)
93-
</programlisting>
95+
</literallayout>
9496
</para>
9597
</sect2>
9698
</sect1>

src/backend/catalog/pg_aggregate.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.47 2002/05/21 22:05:54 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.48 2002/05/22 17:20:58 petere Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -136,6 +136,7 @@ AggregateCreate(const char *aggName,
136136
false, /* doesn't return a set */
137137
finaltype, /* returnType */
138138
INTERNALlanguageId, /* languageObjectId */
139+
0,
139140
"aggregate_dummy", /* placeholder proc */
140141
"-", /* probin */
141142
true, /* isAgg */

src/backend/catalog/pg_proc.c

Lines changed: 133 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.73 2002/05/21 22:05:54 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.74 2002/05/22 17:20:58 petere Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -20,6 +20,7 @@
2020
#include "catalog/pg_language.h"
2121
#include "catalog/pg_proc.h"
2222
#include "executor/executor.h"
23+
#include "fmgr.h"
2324
#include "miscadmin.h"
2425
#include "parser/parse_coerce.h"
2526
#include "parser/parse_expr.h"
@@ -32,6 +33,9 @@
3233

3334

3435
static void checkretval(Oid rettype, List *queryTreeList);
36+
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
37+
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
38+
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
3539

3640

3741
/* ----------------------------------------------------------------
@@ -45,6 +49,7 @@ ProcedureCreate(const char *procedureName,
4549
bool returnsSet,
4650
Oid returnType,
4751
Oid languageObjectId,
52+
Oid languageValidator,
4853
const char *prosrc,
4954
const char *probin,
5055
bool isAgg,
@@ -66,7 +71,6 @@ ProcedureCreate(const char *procedureName,
6671
char nulls[Natts_pg_proc];
6772
Datum values[Natts_pg_proc];
6873
char replaces[Natts_pg_proc];
69-
List *querytree_list;
7074
Oid typev[FUNC_MAX_ARGS];
7175
Oid relid;
7276
NameData procname;
@@ -126,12 +130,6 @@ ProcedureCreate(const char *procedureName,
126130
}
127131
}
128132

129-
if (!OidIsValid(returnType))
130-
{
131-
if (languageObjectId == SQLlanguageId)
132-
elog(ERROR, "SQL functions cannot return type \"opaque\"");
133-
}
134-
135133
/*
136134
* don't allow functions of complex types that have the same name as
137135
* existing attributes of the type
@@ -142,65 +140,6 @@ ProcedureCreate(const char *procedureName,
142140
elog(ERROR, "method %s already an attribute of type %s",
143141
procedureName, format_type_be(typev[0]));
144142

145-
/*
146-
* If this is a postquel procedure, we parse it here in order to be
147-
* sure that it contains no syntax errors. We should store the plan
148-
* in an Inversion file for use later, but for now, we just store the
149-
* procedure's text in the prosrc attribute.
150-
*/
151-
152-
if (languageObjectId == SQLlanguageId)
153-
{
154-
querytree_list = pg_parse_and_rewrite((char *) prosrc,
155-
typev,
156-
parameterCount);
157-
/* typecheck return value */
158-
checkretval(returnType, querytree_list);
159-
}
160-
161-
/*
162-
* If this is an internal procedure, check that the given internal
163-
* function name (the 'prosrc' value) is a known builtin function.
164-
*
165-
* NOTE: in Postgres versions before 6.5, the SQL name of the created
166-
* function could not be different from the internal name, and
167-
* 'prosrc' wasn't used. So there is code out there that does CREATE
168-
* FUNCTION xyz AS '' LANGUAGE 'internal'. To preserve some modicum
169-
* of backwards compatibility, accept an empty 'prosrc' value as
170-
* meaning the supplied SQL function name.
171-
*/
172-
if (languageObjectId == INTERNALlanguageId)
173-
{
174-
if (strlen(prosrc) == 0)
175-
prosrc = procedureName;
176-
if (fmgr_internal_function((char *) prosrc) == InvalidOid)
177-
elog(ERROR,
178-
"there is no built-in function named \"%s\"",
179-
prosrc);
180-
}
181-
182-
/*
183-
* If this is a dynamically loadable procedure, make sure that the
184-
* library file exists, is loadable, and contains the specified link
185-
* symbol. Also check for a valid function information record.
186-
*
187-
* We used to perform these checks only when the function was first
188-
* called, but it seems friendlier to verify the library's validity at
189-
* CREATE FUNCTION time.
190-
*/
191-
if (languageObjectId == ClanguageId)
192-
{
193-
void *libraryhandle;
194-
195-
/* If link symbol is specified as "-", substitute procedure name */
196-
if (strcmp(prosrc, "-") == 0)
197-
prosrc = procedureName;
198-
(void) load_external_function((char *) probin,
199-
(char *) prosrc,
200-
true,
201-
&libraryhandle);
202-
(void) fetch_finfo_record(libraryhandle, (char *) prosrc);
203-
}
204143

205144
/*
206145
* All seems OK; prepare the data to be inserted into pg_proc.
@@ -316,6 +255,14 @@ ProcedureCreate(const char *procedureName,
316255

317256
heap_close(rel, RowExclusiveLock);
318257

258+
/* Verify function body */
259+
if (OidIsValid(languageValidator))
260+
{
261+
/* Advance command counter so recursive functions can be defined */
262+
CommandCounterIncrement();
263+
OidFunctionCall1(languageValidator, retval);
264+
}
265+
319266
return retval;
320267
}
321268

@@ -454,3 +401,122 @@ checkretval(Oid rettype, List *queryTreeList)
454401

455402
heap_close(reln, AccessShareLock);
456403
}
404+
405+
406+
407+
/*
408+
* Validator for internal functions
409+
*
410+
* Check that the given internal function name (the "prosrc" value) is
411+
* a known builtin function.
412+
*/
413+
Datum
414+
fmgr_internal_validator(PG_FUNCTION_ARGS)
415+
{
416+
Oid funcoid = PG_GETARG_OID(0);
417+
HeapTuple tuple;
418+
Form_pg_proc proc;
419+
bool isnull;
420+
Datum tmp;
421+
char *prosrc;
422+
423+
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
424+
if (!HeapTupleIsValid(tuple))
425+
elog(ERROR, "cache lookup of function %u failed", funcoid);
426+
proc = (Form_pg_proc) GETSTRUCT(tuple);
427+
428+
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
429+
if (isnull)
430+
elog(ERROR, "null prosrc");
431+
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
432+
433+
if (fmgr_internal_function(prosrc) == InvalidOid)
434+
elog(ERROR, "there is no built-in function named \"%s\"", prosrc);
435+
436+
ReleaseSysCache(tuple);
437+
PG_RETURN_BOOL(true);
438+
}
439+
440+
441+
442+
/*
443+
* Validator for C language functions
444+
*
445+
* Make sure that the library file exists, is loadable, and contains
446+
* the specified link symbol. Also check for a valid function
447+
* information record.
448+
*/
449+
Datum
450+
fmgr_c_validator(PG_FUNCTION_ARGS)
451+
{
452+
Oid funcoid = PG_GETARG_OID(0);
453+
void *libraryhandle;
454+
HeapTuple tuple;
455+
Form_pg_proc proc;
456+
bool isnull;
457+
Datum tmp;
458+
char *prosrc;
459+
char *probin;
460+
461+
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
462+
if (!HeapTupleIsValid(tuple))
463+
elog(ERROR, "cache lookup of function %u failed", funcoid);
464+
proc = (Form_pg_proc) GETSTRUCT(tuple);
465+
466+
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
467+
if (isnull)
468+
elog(ERROR, "null prosrc");
469+
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
470+
471+
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
472+
if (isnull)
473+
elog(ERROR, "null probin");
474+
probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
475+
476+
(void) load_external_function(probin, prosrc, true, &libraryhandle);
477+
(void) fetch_finfo_record(libraryhandle, prosrc);
478+
479+
ReleaseSysCache(tuple);
480+
PG_RETURN_BOOL(true);
481+
}
482+
483+
484+
485+
/*
486+
* Validator for SQL language functions
487+
*
488+
* Parse it here in order to be sure that it contains no syntax
489+
* errors.
490+
*/
491+
Datum
492+
fmgr_sql_validator(PG_FUNCTION_ARGS)
493+
{
494+
Oid funcoid = PG_GETARG_OID(0);
495+
HeapTuple tuple;
496+
Form_pg_proc proc;
497+
List *querytree_list;
498+
bool isnull;
499+
Datum tmp;
500+
char *prosrc;
501+
502+
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
503+
if (!HeapTupleIsValid(tuple))
504+
elog(ERROR, "cache lookup of function %u failed", funcoid);
505+
506+
proc = (Form_pg_proc) GETSTRUCT(tuple);
507+
508+
if (!OidIsValid(proc->prorettype))
509+
elog(ERROR, "SQL functions cannot return type \"opaque\"");
510+
511+
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
512+
if (isnull)
513+
elog(ERROR, "null prosrc");
514+
515+
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
516+
517+
querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
518+
checkretval(proc->prorettype, querytree_list);
519+
520+
ReleaseSysCache(tuple);
521+
PG_RETURN_BOOL(true);
522+
}

0 commit comments

Comments
 (0)