Skip to content

Commit 0fdc6c4

Browse files
committed
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).
1 parent 74ffc77 commit 0fdc6c4

File tree

7 files changed

+300
-67
lines changed

7 files changed

+300
-67
lines changed

src/bin/scripts/createlang.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
66
* Portions Copyright (c) 1994, Regents of the University of California
77
*
8-
* $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.7 2003/11/29 19:52:07 pgsql Exp $
8+
* $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.8 2004/03/19 18:58:07 tgl Exp $
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -49,8 +49,10 @@ main(int argc, char *argv[])
4949

5050
char *p;
5151
bool handlerexists;
52+
bool validatorexists;
5253
bool trusted;
5354
char *handler;
55+
char *validator = NULL;
5456
char *object;
5557

5658
PQExpBufferData sql;
@@ -169,6 +171,7 @@ main(int argc, char *argv[])
169171
{
170172
trusted = true;
171173
handler = "plpgsql_call_handler";
174+
validator = "plpgsql_validator";
172175
object = "plpgsql";
173176
}
174177
else if (strcmp(langname, "pltcl") == 0)
@@ -229,13 +232,26 @@ main(int argc, char *argv[])
229232
/*
230233
* Check whether the call handler exists
231234
*/
232-
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);
235+
printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND prorettype = 'pg_catalog.language_handler'::regtype AND pronargs = 0;", handler);
233236
result = executeQuery(conn, sql.data, progname, echo);
234237
handlerexists = (PQntuples(result) > 0);
235238
PQclear(result);
236239

237240
/*
238-
* Create the call handler and the language
241+
* Check whether the validator exists
242+
*/
243+
if (validator)
244+
{
245+
printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND proargtypes[0] = 'pg_catalog.oid'::regtype AND pronargs = 1;", validator);
246+
result = executeQuery(conn, sql.data, progname, echo);
247+
validatorexists = (PQntuples(result) > 0);
248+
PQclear(result);
249+
}
250+
else
251+
validatorexists = true; /* don't try to create it */
252+
253+
/*
254+
* Create the function(s) and the language
239255
*/
240256
resetPQExpBuffer(&sql);
241257

@@ -244,10 +260,20 @@ main(int argc, char *argv[])
244260
"CREATE FUNCTION \"%s\" () RETURNS language_handler AS '%s/%s' LANGUAGE C;\n",
245261
handler, pglib, object);
246262

263+
if (!validatorexists)
264+
appendPQExpBuffer(&sql,
265+
"CREATE FUNCTION \"%s\" (oid) RETURNS void AS '%s/%s' LANGUAGE C;\n",
266+
validator, pglib, object);
267+
247268
appendPQExpBuffer(&sql,
248-
"CREATE %sLANGUAGE \"%s\" HANDLER \"%s\";\n",
269+
"CREATE %sLANGUAGE \"%s\" HANDLER \"%s\"",
249270
(trusted ? "TRUSTED " : ""), langname, handler);
250271

272+
if (validator)
273+
appendPQExpBuffer(&sql, " VALIDATOR \"%s\"", validator);
274+
275+
appendPQExpBuffer(&sql, ";\n");
276+
251277
if (echo)
252278
printf("%s", sql.data);
253279
result = PQexec(conn, sql.data);

src/bin/scripts/droplang.c

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
66
* Portions Copyright (c) 1994, Regents of the University of California
77
*
8-
* $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.6 2003/11/29 19:52:07 pgsql Exp $
8+
* $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.7 2004/03/19 18:58:07 tgl Exp $
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -14,6 +14,8 @@
1414
#include "common.h"
1515
#include "print.h"
1616

17+
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
18+
1719

1820
static void help(const char *progname);
1921

@@ -46,9 +48,12 @@ main(int argc, char *argv[])
4648
char *langname = NULL;
4749

4850
char *p;
49-
char *lanplcallfoid;
51+
Oid lanplcallfoid;
52+
Oid lanvalidator;
5053
char *handler;
54+
char *validator;
5155
bool keephandler;
56+
bool keepvalidator;
5257

5358
PQExpBufferData sql;
5459

@@ -159,10 +164,10 @@ main(int argc, char *argv[])
159164
conn = connectDatabase(dbname, host, port, username, password, progname);
160165

161166
/*
162-
* Make sure the language is installed and find the OID of the handler
163-
* function
167+
* Make sure the language is installed and find the OIDs of the handler
168+
* and validator functions
164169
*/
165-
printfPQExpBuffer(&sql, "SELECT lanplcallfoid FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
170+
printfPQExpBuffer(&sql, "SELECT lanplcallfoid, lanvalidator FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
166171
result = executeQuery(conn, sql.data, progname, echo);
167172
if (PQntuples(result) == 0)
168173
{
@@ -171,8 +176,9 @@ main(int argc, char *argv[])
171176
progname, langname, dbname);
172177
exit(1);
173178
}
174-
lanplcallfoid = PQgetvalue(result, 0, 0);
175-
/* result not cleared! */
179+
lanplcallfoid = atooid(PQgetvalue(result, 0, 0));
180+
lanvalidator = atooid(PQgetvalue(result, 0, 1));
181+
PQclear(result);
176182

177183
/*
178184
* Check that there are no functions left defined in that language
@@ -192,7 +198,7 @@ main(int argc, char *argv[])
192198
/*
193199
* Check that the handler function isn't used by some other language
194200
*/
195-
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %s AND lanname <> '%s';", lanplcallfoid, langname);
201+
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %u AND lanname <> '%s';", lanplcallfoid, langname);
196202
result = executeQuery(conn, sql.data, progname, echo);
197203
if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
198204
keephandler = false;
@@ -205,20 +211,51 @@ main(int argc, char *argv[])
205211
*/
206212
if (!keephandler)
207213
{
208-
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %s;", lanplcallfoid);
214+
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanplcallfoid);
209215
result = executeQuery(conn, sql.data, progname, echo);
210-
handler = PQgetvalue(result, 0, 0);
211-
/* result not cleared! */
216+
handler = strdup(PQgetvalue(result, 0, 0));
217+
PQclear(result);
212218
}
213219
else
214220
handler = NULL;
215221

216222
/*
217-
* Drop the language
223+
* Check that the validator function isn't used by some other language
224+
*/
225+
if (OidIsValid(lanvalidator))
226+
{
227+
printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanvalidator = %u AND lanname <> '%s';", lanvalidator, langname);
228+
result = executeQuery(conn, sql.data, progname, echo);
229+
if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
230+
keepvalidator = false;
231+
else
232+
keepvalidator = true;
233+
PQclear(result);
234+
}
235+
else
236+
keepvalidator = true; /* don't try to delete it */
237+
238+
/*
239+
* Find the validator name
240+
*/
241+
if (!keepvalidator)
242+
{
243+
printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanvalidator);
244+
result = executeQuery(conn, sql.data, progname, echo);
245+
validator = strdup(PQgetvalue(result, 0, 0));
246+
PQclear(result);
247+
}
248+
else
249+
validator = NULL;
250+
251+
/*
252+
* Drop the language and the functions
218253
*/
219254
printfPQExpBuffer(&sql, "DROP LANGUAGE \"%s\";\n", langname);
220255
if (!keephandler)
221256
appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" ();\n", handler);
257+
if (!keepvalidator)
258+
appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" (oid);\n", validator);
222259
if (echo)
223260
printf("%s", sql.data);
224261
result = PQexec(conn, sql.data);

src/include/catalog/pg_type.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.150 2004/02/24 22:59:10 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.151 2004/03/19 18:58:07 tgl Exp $
1212
*
1313
* NOTES
1414
* 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_
402402
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_ ));
403403
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_ ));
404404
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_ ));
405+
#define INT4ARRAYOID 1007
405406
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_ ));
406407
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_ ));
407408
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_ ));

src/pl/plpgsql/src/pl_comp.c

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.73 2004/01/07 18:56:30 neilc Exp $
6+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.74 2004/03/19 18:58:07 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -101,13 +101,15 @@ typedef struct plpgsql_hashent
101101
*/
102102
static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
103103
HeapTuple procTup,
104-
PLpgSQL_func_hashkey *hashkey);
104+
PLpgSQL_func_hashkey *hashkey,
105+
bool forValidator);
105106
static void plpgsql_compile_error_callback(void *arg);
106107
static char **fetchArgNames(HeapTuple procTup, int nargs);
107108
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
108109
static void compute_function_hashkey(FunctionCallInfo fcinfo,
109110
Form_pg_proc procStruct,
110-
PLpgSQL_func_hashkey *hashkey);
111+
PLpgSQL_func_hashkey *hashkey,
112+
bool forValidator);
111113
static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
112114
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
113115
PLpgSQL_func_hashkey *func_key);
@@ -134,12 +136,15 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
134136
/* ----------
135137
* plpgsql_compile Make an execution tree for a PL/pgSQL function.
136138
*
139+
* If forValidator is true, we're only compiling for validation purposes,
140+
* and so some checks are skipped.
141+
*
137142
* Note: it's important for this to fall through quickly if the function
138143
* has already been compiled.
139144
* ----------
140145
*/
141146
PLpgSQL_function *
142-
plpgsql_compile(FunctionCallInfo fcinfo)
147+
plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
143148
{
144149
Oid funcOid = fcinfo->flinfo->fn_oid;
145150
HeapTuple procTup;
@@ -171,7 +176,7 @@ plpgsql_compile(FunctionCallInfo fcinfo)
171176
plpgsql_HashTableInit();
172177

173178
/* Compute hashkey using function signature and actual arg types */
174-
compute_function_hashkey(fcinfo, procStruct, &hashkey);
179+
compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
175180
hashkey_valid = true;
176181

177182
/* And do the lookup */
@@ -205,12 +210,13 @@ plpgsql_compile(FunctionCallInfo fcinfo)
205210
* the completed function.
206211
*/
207212
if (!hashkey_valid)
208-
compute_function_hashkey(fcinfo, procStruct, &hashkey);
213+
compute_function_hashkey(fcinfo, procStruct, &hashkey,
214+
forValidator);
209215

210216
/*
211217
* Do the hard part.
212218
*/
213-
function = do_compile(fcinfo, procTup, &hashkey);
219+
function = do_compile(fcinfo, procTup, &hashkey, forValidator);
214220
}
215221

216222
ReleaseSysCache(procTup);
@@ -232,7 +238,8 @@ plpgsql_compile(FunctionCallInfo fcinfo)
232238
static PLpgSQL_function *
233239
do_compile(FunctionCallInfo fcinfo,
234240
HeapTuple procTup,
235-
PLpgSQL_func_hashkey *hashkey)
241+
PLpgSQL_func_hashkey *hashkey,
242+
bool forValidator)
236243
{
237244
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
238245
int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
@@ -308,7 +315,8 @@ do_compile(FunctionCallInfo fcinfo,
308315
/*
309316
* Check for a polymorphic returntype. If found, use the
310317
* actual returntype type from the caller's FuncExpr node, if
311-
* we have one.
318+
* we have one. (In validation mode we arbitrarily assume we
319+
* are dealing with integers.)
312320
*
313321
* Note: errcode is FEATURE_NOT_SUPPORTED because it should
314322
* always work; if it doesn't we're in some context that fails
@@ -317,7 +325,15 @@ do_compile(FunctionCallInfo fcinfo,
317325
rettypeid = procStruct->prorettype;
318326
if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
319327
{
320-
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
328+
if (forValidator)
329+
{
330+
if (rettypeid == ANYARRAYOID)
331+
rettypeid = INT4ARRAYOID;
332+
else
333+
rettypeid = INT4OID;
334+
}
335+
else
336+
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
321337
if (!OidIsValid(rettypeid))
322338
ereport(ERROR,
323339
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1758,22 +1774,6 @@ plpgsql_add_initdatums(int **varnos)
17581774
}
17591775

17601776

1761-
/* ---------
1762-
* plpgsql_yyerror Handle parser error
1763-
* ---------
1764-
*/
1765-
1766-
void
1767-
plpgsql_yyerror(const char *s)
1768-
{
1769-
plpgsql_error_lineno = plpgsql_scanner_lineno();
1770-
ereport(ERROR,
1771-
(errcode(ERRCODE_SYNTAX_ERROR),
1772-
/* translator: first %s is a phrase like "syntax error" */
1773-
errmsg("%s at or near \"%s\"", s, plpgsql_yytext)));
1774-
}
1775-
1776-
17771777
/*
17781778
* Compute the hashkey for a given function invocation
17791779
*
@@ -1782,7 +1782,8 @@ plpgsql_yyerror(const char *s)
17821782
static void
17831783
compute_function_hashkey(FunctionCallInfo fcinfo,
17841784
Form_pg_proc procStruct,
1785-
PLpgSQL_func_hashkey *hashkey)
1785+
PLpgSQL_func_hashkey *hashkey,
1786+
bool forValidator)
17861787
{
17871788
int i;
17881789

@@ -1792,8 +1793,12 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
17921793
/* get function OID */
17931794
hashkey->funcOid = fcinfo->flinfo->fn_oid;
17941795

1795-
/* if trigger, get relation OID */
1796-
if (CALLED_AS_TRIGGER(fcinfo))
1796+
/*
1797+
* if trigger, get relation OID. In validation mode we do not know what
1798+
* relation is intended to be used, so we leave trigrelOid zero; the
1799+
* hash entry built in this case will never really be used.
1800+
*/
1801+
if (CALLED_AS_TRIGGER(fcinfo) && !forValidator)
17971802
{
17981803
TriggerData *trigdata = (TriggerData *) fcinfo->context;
17991804

@@ -1808,14 +1813,25 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
18081813
/*
18091814
* Check for polymorphic arguments. If found, use the actual
18101815
* parameter type from the caller's FuncExpr node, if we have one.
1816+
* (In validation mode we arbitrarily assume we are dealing with
1817+
* integers. This lets us build a valid, if possibly useless,
1818+
* function hashtable entry.)
18111819
*
18121820
* We can support arguments of type ANY the same way as normal
18131821
* polymorphic arguments.
18141822
*/
18151823
if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
18161824
argtypeid == ANYOID)
18171825
{
1818-
argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
1826+
if (forValidator)
1827+
{
1828+
if (argtypeid == ANYARRAYOID)
1829+
argtypeid = INT4ARRAYOID;
1830+
else
1831+
argtypeid = INT4OID;
1832+
}
1833+
else
1834+
argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
18191835
if (!OidIsValid(argtypeid))
18201836
ereport(ERROR,
18211837
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy