mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-17 19:30:00 +08:00
Create a validator for plpgsql, so that some minimal syntax checking
is done at creation time for plpgsql functions. Improve createlang and droplang to support adding/dropping validators for PLs. Initial steps towards producing a syntax error position from plpgsql syntax errors (this part is a work in progress, and will change depending on outcome of current discussions).
This commit is contained in:
parent
74ffc77279
commit
0fdc6c4cc0
src
bin/scripts
include/catalog
pl/plpgsql/src
@ -5,7 +5,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.7 2003/11/29 19:52:07 pgsql Exp $
|
* $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.8 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -49,8 +49,10 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
char *p;
|
char *p;
|
||||||
bool handlerexists;
|
bool handlerexists;
|
||||||
|
bool validatorexists;
|
||||||
bool trusted;
|
bool trusted;
|
||||||
char *handler;
|
char *handler;
|
||||||
|
char *validator = NULL;
|
||||||
char *object;
|
char *object;
|
||||||
|
|
||||||
PQExpBufferData sql;
|
PQExpBufferData sql;
|
||||||
@ -169,6 +171,7 @@ main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
trusted = true;
|
trusted = true;
|
||||||
handler = "plpgsql_call_handler";
|
handler = "plpgsql_call_handler";
|
||||||
|
validator = "plpgsql_validator";
|
||||||
object = "plpgsql";
|
object = "plpgsql";
|
||||||
}
|
}
|
||||||
else if (strcmp(langname, "pltcl") == 0)
|
else if (strcmp(langname, "pltcl") == 0)
|
||||||
@ -229,13 +232,26 @@ main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Check whether the call handler exists
|
* Check whether the call handler exists
|
||||||
*/
|
*/
|
||||||
printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND prorettype = (SELECT oid FROM pg_type WHERE typname = 'language_handler') AND pronargs = 0;", handler);
|
printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND prorettype = 'pg_catalog.language_handler'::regtype AND pronargs = 0;", handler);
|
||||||
result = executeQuery(conn, sql.data, progname, echo);
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
handlerexists = (PQntuples(result) > 0);
|
handlerexists = (PQntuples(result) > 0);
|
||||||
PQclear(result);
|
PQclear(result);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the call handler and the language
|
* Check whether the validator exists
|
||||||
|
*/
|
||||||
|
if (validator)
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND proargtypes[0] = 'pg_catalog.oid'::regtype AND pronargs = 1;", validator);
|
||||||
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
|
validatorexists = (PQntuples(result) > 0);
|
||||||
|
PQclear(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
validatorexists = true; /* don't try to create it */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the function(s) and the language
|
||||||
*/
|
*/
|
||||||
resetPQExpBuffer(&sql);
|
resetPQExpBuffer(&sql);
|
||||||
|
|
||||||
@ -244,10 +260,20 @@ main(int argc, char *argv[])
|
|||||||
"CREATE FUNCTION \"%s\" () RETURNS language_handler AS '%s/%s' LANGUAGE C;\n",
|
"CREATE FUNCTION \"%s\" () RETURNS language_handler AS '%s/%s' LANGUAGE C;\n",
|
||||||
handler, pglib, object);
|
handler, pglib, object);
|
||||||
|
|
||||||
|
if (!validatorexists)
|
||||||
|
appendPQExpBuffer(&sql,
|
||||||
|
"CREATE FUNCTION \"%s\" (oid) RETURNS void AS '%s/%s' LANGUAGE C;\n",
|
||||||
|
validator, pglib, object);
|
||||||
|
|
||||||
appendPQExpBuffer(&sql,
|
appendPQExpBuffer(&sql,
|
||||||
"CREATE %sLANGUAGE \"%s\" HANDLER \"%s\";\n",
|
"CREATE %sLANGUAGE \"%s\" HANDLER \"%s\"",
|
||||||
(trusted ? "TRUSTED " : ""), langname, handler);
|
(trusted ? "TRUSTED " : ""), langname, handler);
|
||||||
|
|
||||||
|
if (validator)
|
||||||
|
appendPQExpBuffer(&sql, " VALIDATOR \"%s\"", validator);
|
||||||
|
|
||||||
|
appendPQExpBuffer(&sql, ";\n");
|
||||||
|
|
||||||
if (echo)
|
if (echo)
|
||||||
printf("%s", sql.data);
|
printf("%s", sql.data);
|
||||||
result = PQexec(conn, sql.data);
|
result = PQexec(conn, sql.data);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.6 2003/11/29 19:52:07 pgsql Exp $
|
* $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.7 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -14,6 +14,8 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
|
||||||
|
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
||||||
|
|
||||||
|
|
||||||
static void help(const char *progname);
|
static void help(const char *progname);
|
||||||
|
|
||||||
@ -46,9 +48,12 @@ main(int argc, char *argv[])
|
|||||||
char *langname = NULL;
|
char *langname = NULL;
|
||||||
|
|
||||||
char *p;
|
char *p;
|
||||||
char *lanplcallfoid;
|
Oid lanplcallfoid;
|
||||||
|
Oid lanvalidator;
|
||||||
char *handler;
|
char *handler;
|
||||||
|
char *validator;
|
||||||
bool keephandler;
|
bool keephandler;
|
||||||
|
bool keepvalidator;
|
||||||
|
|
||||||
PQExpBufferData sql;
|
PQExpBufferData sql;
|
||||||
|
|
||||||
@ -159,10 +164,10 @@ main(int argc, char *argv[])
|
|||||||
conn = connectDatabase(dbname, host, port, username, password, progname);
|
conn = connectDatabase(dbname, host, port, username, password, progname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the language is installed and find the OID of the handler
|
* Make sure the language is installed and find the OIDs of the handler
|
||||||
* function
|
* and validator functions
|
||||||
*/
|
*/
|
||||||
printfPQExpBuffer(&sql, "SELECT lanplcallfoid FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
|
printfPQExpBuffer(&sql, "SELECT lanplcallfoid, lanvalidator FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
|
||||||
result = executeQuery(conn, sql.data, progname, echo);
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
if (PQntuples(result) == 0)
|
if (PQntuples(result) == 0)
|
||||||
{
|
{
|
||||||
@ -171,8 +176,9 @@ main(int argc, char *argv[])
|
|||||||
progname, langname, dbname);
|
progname, langname, dbname);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
lanplcallfoid = PQgetvalue(result, 0, 0);
|
lanplcallfoid = atooid(PQgetvalue(result, 0, 0));
|
||||||
/* result not cleared! */
|
lanvalidator = atooid(PQgetvalue(result, 0, 1));
|
||||||
|
PQclear(result);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that there are no functions left defined in that language
|
* Check that there are no functions left defined in that language
|
||||||
@ -192,7 +198,7 @@ main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Check that the handler function isn't used by some other language
|
* Check that the handler function isn't used by some other language
|
||||||
*/
|
*/
|
||||||
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %s AND lanname <> '%s';", lanplcallfoid, langname);
|
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %u AND lanname <> '%s';", lanplcallfoid, langname);
|
||||||
result = executeQuery(conn, sql.data, progname, echo);
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
|
if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
|
||||||
keephandler = false;
|
keephandler = false;
|
||||||
@ -205,20 +211,51 @@ main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
if (!keephandler)
|
if (!keephandler)
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %s;", lanplcallfoid);
|
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanplcallfoid);
|
||||||
result = executeQuery(conn, sql.data, progname, echo);
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
handler = PQgetvalue(result, 0, 0);
|
handler = strdup(PQgetvalue(result, 0, 0));
|
||||||
/* result not cleared! */
|
PQclear(result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
handler = NULL;
|
handler = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drop the language
|
* Check that the validator function isn't used by some other language
|
||||||
|
*/
|
||||||
|
if (OidIsValid(lanvalidator))
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanvalidator = %u AND lanname <> '%s';", lanvalidator, langname);
|
||||||
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
|
if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
|
||||||
|
keepvalidator = false;
|
||||||
|
else
|
||||||
|
keepvalidator = true;
|
||||||
|
PQclear(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
keepvalidator = true; /* don't try to delete it */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the validator name
|
||||||
|
*/
|
||||||
|
if (!keepvalidator)
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanvalidator);
|
||||||
|
result = executeQuery(conn, sql.data, progname, echo);
|
||||||
|
validator = strdup(PQgetvalue(result, 0, 0));
|
||||||
|
PQclear(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
validator = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drop the language and the functions
|
||||||
*/
|
*/
|
||||||
printfPQExpBuffer(&sql, "DROP LANGUAGE \"%s\";\n", langname);
|
printfPQExpBuffer(&sql, "DROP LANGUAGE \"%s\";\n", langname);
|
||||||
if (!keephandler)
|
if (!keephandler)
|
||||||
appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" ();\n", handler);
|
appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" ();\n", handler);
|
||||||
|
if (!keepvalidator)
|
||||||
|
appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" (oid);\n", validator);
|
||||||
if (echo)
|
if (echo)
|
||||||
printf("%s", sql.data);
|
printf("%s", sql.data);
|
||||||
result = PQexec(conn, sql.data);
|
result = PQexec(conn, sql.data);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.150 2004/02/24 22:59:10 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.151 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
@ -402,6 +402,7 @@ DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b t \054 0 19 array_in array_
|
|||||||
DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b t \054 0 21 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b t \054 0 21 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b t \054 0 22 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b t \054 0 22 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b t \054 0 23 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b t \054 0 23 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
|
#define INT4ARRAYOID 1007
|
||||||
DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b t \054 0 24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b t \054 0 24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b t \054 0 25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b t \054 0 25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b t \054 0 26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b t \054 0 26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.73 2004/01/07 18:56:30 neilc Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.74 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -101,13 +101,15 @@ typedef struct plpgsql_hashent
|
|||||||
*/
|
*/
|
||||||
static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
|
static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
|
||||||
HeapTuple procTup,
|
HeapTuple procTup,
|
||||||
PLpgSQL_func_hashkey *hashkey);
|
PLpgSQL_func_hashkey *hashkey,
|
||||||
|
bool forValidator);
|
||||||
static void plpgsql_compile_error_callback(void *arg);
|
static void plpgsql_compile_error_callback(void *arg);
|
||||||
static char **fetchArgNames(HeapTuple procTup, int nargs);
|
static char **fetchArgNames(HeapTuple procTup, int nargs);
|
||||||
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
|
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
|
||||||
static void compute_function_hashkey(FunctionCallInfo fcinfo,
|
static void compute_function_hashkey(FunctionCallInfo fcinfo,
|
||||||
Form_pg_proc procStruct,
|
Form_pg_proc procStruct,
|
||||||
PLpgSQL_func_hashkey *hashkey);
|
PLpgSQL_func_hashkey *hashkey,
|
||||||
|
bool forValidator);
|
||||||
static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
|
static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
|
||||||
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
|
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
|
||||||
PLpgSQL_func_hashkey *func_key);
|
PLpgSQL_func_hashkey *func_key);
|
||||||
@ -134,12 +136,15 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
|
|||||||
/* ----------
|
/* ----------
|
||||||
* plpgsql_compile Make an execution tree for a PL/pgSQL function.
|
* plpgsql_compile Make an execution tree for a PL/pgSQL function.
|
||||||
*
|
*
|
||||||
|
* If forValidator is true, we're only compiling for validation purposes,
|
||||||
|
* and so some checks are skipped.
|
||||||
|
*
|
||||||
* Note: it's important for this to fall through quickly if the function
|
* Note: it's important for this to fall through quickly if the function
|
||||||
* has already been compiled.
|
* has already been compiled.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
PLpgSQL_function *
|
PLpgSQL_function *
|
||||||
plpgsql_compile(FunctionCallInfo fcinfo)
|
plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
|
||||||
{
|
{
|
||||||
Oid funcOid = fcinfo->flinfo->fn_oid;
|
Oid funcOid = fcinfo->flinfo->fn_oid;
|
||||||
HeapTuple procTup;
|
HeapTuple procTup;
|
||||||
@ -171,7 +176,7 @@ plpgsql_compile(FunctionCallInfo fcinfo)
|
|||||||
plpgsql_HashTableInit();
|
plpgsql_HashTableInit();
|
||||||
|
|
||||||
/* Compute hashkey using function signature and actual arg types */
|
/* Compute hashkey using function signature and actual arg types */
|
||||||
compute_function_hashkey(fcinfo, procStruct, &hashkey);
|
compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
|
||||||
hashkey_valid = true;
|
hashkey_valid = true;
|
||||||
|
|
||||||
/* And do the lookup */
|
/* And do the lookup */
|
||||||
@ -205,12 +210,13 @@ plpgsql_compile(FunctionCallInfo fcinfo)
|
|||||||
* the completed function.
|
* the completed function.
|
||||||
*/
|
*/
|
||||||
if (!hashkey_valid)
|
if (!hashkey_valid)
|
||||||
compute_function_hashkey(fcinfo, procStruct, &hashkey);
|
compute_function_hashkey(fcinfo, procStruct, &hashkey,
|
||||||
|
forValidator);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the hard part.
|
* Do the hard part.
|
||||||
*/
|
*/
|
||||||
function = do_compile(fcinfo, procTup, &hashkey);
|
function = do_compile(fcinfo, procTup, &hashkey, forValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseSysCache(procTup);
|
ReleaseSysCache(procTup);
|
||||||
@ -232,7 +238,8 @@ plpgsql_compile(FunctionCallInfo fcinfo)
|
|||||||
static PLpgSQL_function *
|
static PLpgSQL_function *
|
||||||
do_compile(FunctionCallInfo fcinfo,
|
do_compile(FunctionCallInfo fcinfo,
|
||||||
HeapTuple procTup,
|
HeapTuple procTup,
|
||||||
PLpgSQL_func_hashkey *hashkey)
|
PLpgSQL_func_hashkey *hashkey,
|
||||||
|
bool forValidator)
|
||||||
{
|
{
|
||||||
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||||
int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
|
int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
|
||||||
@ -308,7 +315,8 @@ do_compile(FunctionCallInfo fcinfo,
|
|||||||
/*
|
/*
|
||||||
* Check for a polymorphic returntype. If found, use the
|
* Check for a polymorphic returntype. If found, use the
|
||||||
* actual returntype type from the caller's FuncExpr node, if
|
* actual returntype type from the caller's FuncExpr node, if
|
||||||
* we have one.
|
* we have one. (In validation mode we arbitrarily assume we
|
||||||
|
* are dealing with integers.)
|
||||||
*
|
*
|
||||||
* Note: errcode is FEATURE_NOT_SUPPORTED because it should
|
* Note: errcode is FEATURE_NOT_SUPPORTED because it should
|
||||||
* always work; if it doesn't we're in some context that fails
|
* always work; if it doesn't we're in some context that fails
|
||||||
@ -317,7 +325,15 @@ do_compile(FunctionCallInfo fcinfo,
|
|||||||
rettypeid = procStruct->prorettype;
|
rettypeid = procStruct->prorettype;
|
||||||
if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
|
if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
|
||||||
{
|
{
|
||||||
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
|
if (forValidator)
|
||||||
|
{
|
||||||
|
if (rettypeid == ANYARRAYOID)
|
||||||
|
rettypeid = INT4ARRAYOID;
|
||||||
|
else
|
||||||
|
rettypeid = INT4OID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
|
||||||
if (!OidIsValid(rettypeid))
|
if (!OidIsValid(rettypeid))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
@ -1758,22 +1774,6 @@ plpgsql_add_initdatums(int **varnos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------
|
|
||||||
* plpgsql_yyerror Handle parser error
|
|
||||||
* ---------
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
plpgsql_yyerror(const char *s)
|
|
||||||
{
|
|
||||||
plpgsql_error_lineno = plpgsql_scanner_lineno();
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
/* translator: first %s is a phrase like "syntax error" */
|
|
||||||
errmsg("%s at or near \"%s\"", s, plpgsql_yytext)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute the hashkey for a given function invocation
|
* Compute the hashkey for a given function invocation
|
||||||
*
|
*
|
||||||
@ -1782,7 +1782,8 @@ plpgsql_yyerror(const char *s)
|
|||||||
static void
|
static void
|
||||||
compute_function_hashkey(FunctionCallInfo fcinfo,
|
compute_function_hashkey(FunctionCallInfo fcinfo,
|
||||||
Form_pg_proc procStruct,
|
Form_pg_proc procStruct,
|
||||||
PLpgSQL_func_hashkey *hashkey)
|
PLpgSQL_func_hashkey *hashkey,
|
||||||
|
bool forValidator)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -1792,8 +1793,12 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
|||||||
/* get function OID */
|
/* get function OID */
|
||||||
hashkey->funcOid = fcinfo->flinfo->fn_oid;
|
hashkey->funcOid = fcinfo->flinfo->fn_oid;
|
||||||
|
|
||||||
/* if trigger, get relation OID */
|
/*
|
||||||
if (CALLED_AS_TRIGGER(fcinfo))
|
* if trigger, get relation OID. In validation mode we do not know what
|
||||||
|
* relation is intended to be used, so we leave trigrelOid zero; the
|
||||||
|
* hash entry built in this case will never really be used.
|
||||||
|
*/
|
||||||
|
if (CALLED_AS_TRIGGER(fcinfo) && !forValidator)
|
||||||
{
|
{
|
||||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||||
|
|
||||||
@ -1808,6 +1813,9 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
|||||||
/*
|
/*
|
||||||
* Check for polymorphic arguments. If found, use the actual
|
* Check for polymorphic arguments. If found, use the actual
|
||||||
* parameter type from the caller's FuncExpr node, if we have one.
|
* parameter type from the caller's FuncExpr node, if we have one.
|
||||||
|
* (In validation mode we arbitrarily assume we are dealing with
|
||||||
|
* integers. This lets us build a valid, if possibly useless,
|
||||||
|
* function hashtable entry.)
|
||||||
*
|
*
|
||||||
* We can support arguments of type ANY the same way as normal
|
* We can support arguments of type ANY the same way as normal
|
||||||
* polymorphic arguments.
|
* polymorphic arguments.
|
||||||
@ -1815,7 +1823,15 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
|||||||
if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
|
if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
|
||||||
argtypeid == ANYOID)
|
argtypeid == ANYOID)
|
||||||
{
|
{
|
||||||
argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
|
if (forValidator)
|
||||||
|
{
|
||||||
|
if (argtypeid == ANYARRAYOID)
|
||||||
|
argtypeid = INT4ARRAYOID;
|
||||||
|
else
|
||||||
|
argtypeid = INT4OID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
|
||||||
if (!OidIsValid(argtypeid))
|
if (!OidIsValid(argtypeid))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.19 2003/11/29 19:52:12 pgsql Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.20 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -42,8 +42,11 @@
|
|||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
extern bool check_function_bodies;
|
||||||
|
|
||||||
static int plpgsql_firstcall = 1;
|
static int plpgsql_firstcall = 1;
|
||||||
|
|
||||||
static void plpgsql_init_all(void);
|
static void plpgsql_init_all(void);
|
||||||
@ -88,7 +91,6 @@ plpgsql_init_all(void)
|
|||||||
/* ----------
|
/* ----------
|
||||||
* plpgsql_call_handler
|
* plpgsql_call_handler
|
||||||
*
|
*
|
||||||
* This is the only visible function of the PL interpreter.
|
|
||||||
* The PostgreSQL function manager and trigger manager
|
* The PostgreSQL function manager and trigger manager
|
||||||
* call this function for execution of PL/pgSQL procedures.
|
* call this function for execution of PL/pgSQL procedures.
|
||||||
* ----------
|
* ----------
|
||||||
@ -111,7 +113,7 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
|||||||
elog(ERROR, "SPI_connect failed");
|
elog(ERROR, "SPI_connect failed");
|
||||||
|
|
||||||
/* Find or compile the function */
|
/* Find or compile the function */
|
||||||
func = plpgsql_compile(fcinfo);
|
func = plpgsql_compile(fcinfo, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine if called as function or trigger and call appropriate
|
* Determine if called as function or trigger and call appropriate
|
||||||
@ -131,3 +133,118 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------
|
||||||
|
* plpgsql_validator
|
||||||
|
*
|
||||||
|
* This function attempts to validate a PL/pgSQL function at
|
||||||
|
* CREATE FUNCTION time.
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
PG_FUNCTION_INFO_V1(plpgsql_validator);
|
||||||
|
|
||||||
|
Datum
|
||||||
|
plpgsql_validator(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Oid funcoid = PG_GETARG_OID(0);
|
||||||
|
HeapTuple tuple;
|
||||||
|
Form_pg_proc proc;
|
||||||
|
char functyptype;
|
||||||
|
bool istrigger = false;
|
||||||
|
bool haspolyresult;
|
||||||
|
bool haspolyarg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* perform initialization */
|
||||||
|
plpgsql_init_all();
|
||||||
|
|
||||||
|
/* Get the new function's pg_proc entry */
|
||||||
|
tuple = SearchSysCache(PROCOID,
|
||||||
|
ObjectIdGetDatum(funcoid),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "cache lookup failed for function %u", funcoid);
|
||||||
|
proc = (Form_pg_proc) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
functyptype = get_typtype(proc->prorettype);
|
||||||
|
|
||||||
|
/* Disallow pseudotype result */
|
||||||
|
/* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
|
||||||
|
if (functyptype == 'p')
|
||||||
|
{
|
||||||
|
/* we assume OPAQUE with no arguments means a trigger */
|
||||||
|
if (proc->prorettype == TRIGGEROID ||
|
||||||
|
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
|
||||||
|
istrigger = true;
|
||||||
|
else if (proc->prorettype == ANYARRAYOID ||
|
||||||
|
proc->prorettype == ANYELEMENTOID)
|
||||||
|
haspolyresult = true;
|
||||||
|
else if (proc->prorettype != RECORDOID &&
|
||||||
|
proc->prorettype != VOIDOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("plpgsql functions cannot return type %s",
|
||||||
|
format_type_be(proc->prorettype))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disallow pseudotypes in arguments */
|
||||||
|
/* except for ANYARRAY or ANYELEMENT */
|
||||||
|
haspolyarg = false;
|
||||||
|
for (i = 0; i < proc->pronargs; i++)
|
||||||
|
{
|
||||||
|
if (get_typtype(proc->proargtypes[i]) == 'p')
|
||||||
|
{
|
||||||
|
if (proc->proargtypes[i] == ANYARRAYOID ||
|
||||||
|
proc->proargtypes[i] == ANYELEMENTOID)
|
||||||
|
haspolyarg = true;
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("plpgsql functions cannot take type %s",
|
||||||
|
format_type_be(proc->proargtypes[i]))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Postpone body checks if !check_function_bodies */
|
||||||
|
if (check_function_bodies)
|
||||||
|
{
|
||||||
|
FunctionCallInfoData fake_fcinfo;
|
||||||
|
FmgrInfo flinfo;
|
||||||
|
TriggerData trigdata;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to SPI manager (is this needed for compilation?)
|
||||||
|
*/
|
||||||
|
if (SPI_connect() != SPI_OK_CONNECT)
|
||||||
|
elog(ERROR, "SPI_connect failed");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up a fake fcinfo with just enough info to satisfy
|
||||||
|
* plpgsql_compile().
|
||||||
|
*/
|
||||||
|
MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
|
||||||
|
MemSet(&flinfo, 0, sizeof(flinfo));
|
||||||
|
fake_fcinfo.flinfo = &flinfo;
|
||||||
|
flinfo.fn_oid = funcoid;
|
||||||
|
flinfo.fn_mcxt = CurrentMemoryContext;
|
||||||
|
if (istrigger)
|
||||||
|
{
|
||||||
|
MemSet(&trigdata, 0, sizeof(trigdata));
|
||||||
|
trigdata.type = T_TriggerData;
|
||||||
|
fake_fcinfo.context = (Node *) &trigdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test-compile the function */
|
||||||
|
plpgsql_compile(&fake_fcinfo, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disconnect from SPI manager
|
||||||
|
*/
|
||||||
|
if (SPI_finish() != SPI_OK_FINISH)
|
||||||
|
elog(ERROR, "SPI_finish failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.44 2004/02/25 18:10:51 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.45 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -620,7 +620,8 @@ extern PLpgSQL_function *plpgsql_curr_compile;
|
|||||||
* Functions in pl_comp.c
|
* Functions in pl_comp.c
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo);
|
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
|
||||||
|
bool forValidator);
|
||||||
extern int plpgsql_parse_word(char *word);
|
extern int plpgsql_parse_word(char *word);
|
||||||
extern int plpgsql_parse_dblword(char *word);
|
extern int plpgsql_parse_dblword(char *word);
|
||||||
extern int plpgsql_parse_tripword(char *word);
|
extern int plpgsql_parse_tripword(char *word);
|
||||||
@ -633,7 +634,6 @@ extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
|
|||||||
extern PLpgSQL_row *plpgsql_build_rowtype(Oid classOid);
|
extern PLpgSQL_row *plpgsql_build_rowtype(Oid classOid);
|
||||||
extern void plpgsql_adddatum(PLpgSQL_datum * new);
|
extern void plpgsql_adddatum(PLpgSQL_datum * new);
|
||||||
extern int plpgsql_add_initdatums(int **varnos);
|
extern int plpgsql_add_initdatums(int **varnos);
|
||||||
extern void plpgsql_yyerror(const char *s);
|
|
||||||
extern void plpgsql_HashTableInit(void);
|
extern void plpgsql_HashTableInit(void);
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -642,6 +642,7 @@ extern void plpgsql_HashTableInit(void);
|
|||||||
*/
|
*/
|
||||||
extern void plpgsql_init(void);
|
extern void plpgsql_init(void);
|
||||||
extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS);
|
extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum plpgsql_validator(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Functions in pl_exec.c
|
* Functions in pl_exec.c
|
||||||
@ -691,6 +692,7 @@ extern int plpgsql_yyparse(void);
|
|||||||
extern int plpgsql_base_yylex(void);
|
extern int plpgsql_base_yylex(void);
|
||||||
extern int plpgsql_yylex(void);
|
extern int plpgsql_yylex(void);
|
||||||
extern void plpgsql_push_back_token(int token);
|
extern void plpgsql_push_back_token(int token);
|
||||||
|
extern void plpgsql_yyerror(const char *message);
|
||||||
extern int plpgsql_scanner_lineno(void);
|
extern int plpgsql_scanner_lineno(void);
|
||||||
extern void plpgsql_scanner_init(const char *str, int functype);
|
extern void plpgsql_scanner_init(const char *str, int functype);
|
||||||
extern void plpgsql_scanner_finish(void);
|
extern void plpgsql_scanner_finish(void);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.32 2004/02/25 18:10:51 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.33 2004/03/19 18:58:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -38,6 +38,8 @@
|
|||||||
|
|
||||||
#include "plpgsql.h"
|
#include "plpgsql.h"
|
||||||
|
|
||||||
|
#include "mb/pg_wchar.h"
|
||||||
|
|
||||||
|
|
||||||
/* No reason to constrain amount of data slurped */
|
/* No reason to constrain amount of data slurped */
|
||||||
#define YY_READ_BUF_SIZE 16777216
|
#define YY_READ_BUF_SIZE 16777216
|
||||||
@ -409,6 +411,38 @@ plpgsql_push_back_token(int token)
|
|||||||
have_pushback_token = true;
|
have_pushback_token = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report a syntax error.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
plpgsql_yyerror(const char *message)
|
||||||
|
{
|
||||||
|
const char *loc = yytext;
|
||||||
|
int cursorpos;
|
||||||
|
|
||||||
|
plpgsql_error_lineno = plpgsql_scanner_lineno();
|
||||||
|
|
||||||
|
/* in multibyte encodings, return index in characters not bytes */
|
||||||
|
cursorpos = pg_mbstrlen_with_len(scanbuf, loc - scanbuf) + 1;
|
||||||
|
|
||||||
|
if (*loc == YY_END_OF_BUFFER_CHAR)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
/* translator: %s is typically "syntax error" */
|
||||||
|
errmsg("%s at end of input", message),
|
||||||
|
errposition(cursorpos)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
/* translator: first %s is typically "syntax error" */
|
||||||
|
errmsg("%s at or near \"%s\"", message, loc),
|
||||||
|
errposition(cursorpos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the line number at which the current token ends. This substitutes
|
* Get the line number at which the current token ends. This substitutes
|
||||||
* for flex's very poorly implemented yylineno facility.
|
* for flex's very poorly implemented yylineno facility.
|
||||||
@ -439,19 +473,6 @@ plpgsql_scanner_init(const char *str, int functype)
|
|||||||
{
|
{
|
||||||
Size slen;
|
Size slen;
|
||||||
|
|
||||||
/*----------
|
|
||||||
* Hack: skip any initial newline, so that in the common coding layout
|
|
||||||
* CREATE FUNCTION ... AS '
|
|
||||||
* code body
|
|
||||||
* ' LANGUAGE 'plpgsql';
|
|
||||||
* we will think "line 1" is what the programmer thinks of as line 1.
|
|
||||||
*----------
|
|
||||||
*/
|
|
||||||
if (*str == '\r')
|
|
||||||
str++;
|
|
||||||
if (*str == '\n')
|
|
||||||
str++;
|
|
||||||
|
|
||||||
slen = strlen(str);
|
slen = strlen(str);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -478,6 +499,19 @@ plpgsql_scanner_init(const char *str, int functype)
|
|||||||
cur_line_start = scanbuf;
|
cur_line_start = scanbuf;
|
||||||
cur_line_num = 1;
|
cur_line_num = 1;
|
||||||
|
|
||||||
|
/*----------
|
||||||
|
* Hack: skip any initial newline, so that in the common coding layout
|
||||||
|
* CREATE FUNCTION ... AS '
|
||||||
|
* code body
|
||||||
|
* ' LANGUAGE 'plpgsql';
|
||||||
|
* we will think "line 1" is what the programmer thinks of as line 1.
|
||||||
|
*----------
|
||||||
|
*/
|
||||||
|
if (*cur_line_start == '\r')
|
||||||
|
cur_line_start++;
|
||||||
|
if (*cur_line_start == '\n')
|
||||||
|
cur_line_start++;
|
||||||
|
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user