Skip to content

Commit 3a624e9

Browse files
committed
Revise plpgsql's scanner to process comments and string literals in a way
more nearly matching the core SQL scanner. The user-visible effects are: * Block comments (slash-star comments) now nest, as per SQL spec. * In standard_conforming_strings mode, backslash as the last character of a non-E string literal is now correctly taken as an ordinary character; formerly it was misinterpreted as escaping the ending quote. (Since the string also had to pass through the core scanner, this invariably led to syntax errors.) * Formerly, backslashes in the format string of RAISE were always treated as quoting the next character, regardless of mode. Now, they are ordinary characters with standard_conforming_strings on, while with it off, they introduce the same set of escapes as in the core SQL scanner. Also, escape_string_warning is now effective for RAISE format strings. These changes make RAISE format strings work just like any other string literal. This is implemented by copying and pasting a lot of logic from the core scanner. It would be a good idea to look into getting rid of plpgsql's scanner entirely in favor of using the core scanner. However, that involves more change than I can justify making during beta --- in particular, the core scanner would have to become re-entrant. In passing, remove the kluge that made the plpgsql scanner emit T_FUNCTION or T_TRIGGER as a made-up first token. That presumably had some value once upon a time, but now it's just useless complication for both the scanner and the grammar.
1 parent 7f2f798 commit 3a624e9

File tree

8 files changed

+398
-236
lines changed

8 files changed

+398
-236
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.139 2009/04/02 19:20:45 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.140 2009/04/19 18:52:56 tgl Exp $ -->
22

33
<chapter id="plpgsql">
44
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -220,10 +220,8 @@ END <optional> <replaceable>label</replaceable> </optional>;
220220
There are two types of comments in <application>PL/pgSQL</>. A double
221221
dash (<literal>--</literal>) starts a comment that extends to the end of
222222
the line. A <literal>/*</literal> starts a block comment that extends to
223-
the next occurrence of <literal>*/</literal>. Block comments cannot be
224-
nested, but double dash comments can be enclosed into a block comment and
225-
a double dash can hide the block comment delimiters <literal>/*</literal>
226-
and <literal>*/</literal>.
223+
the next occurrence of <literal>*/</literal>. Block comments nest,
224+
just as in ordinary SQL.
227225
</para>
228226

229227
<para>

src/pl/plpgsql/src/gram.y

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.121 2009/02/18 11:33:04 petere Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.122 2009/04/19 18:52:57 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -62,6 +62,8 @@ static PLpgSQL_row *make_scalar_list1(const char *initial_name,
6262
int lineno);
6363
static void check_sql_expr(const char *stmt);
6464
static void plpgsql_sql_error_callback(void *arg);
65+
static char *parse_string_token(const char *token);
66+
static void plpgsql_string_error_callback(void *arg);
6567
static char *check_label(const char *yytxt);
6668
static void check_labels(const char *start_label,
6769
const char *end_label);
@@ -228,8 +230,6 @@ static List *read_raise_options(void);
228230
/*
229231
* Other tokens
230232
*/
231-
%token T_FUNCTION
232-
%token T_TRIGGER
233233
%token T_STRING
234234
%token T_NUMBER
235235
%token T_SCALAR /* a VAR, RECFIELD, or TRIGARG */
@@ -244,13 +244,9 @@ static List *read_raise_options(void);
244244

245245
%%
246246

247-
pl_function : T_FUNCTION comp_optsect pl_block opt_semi
247+
pl_function : comp_optsect pl_block opt_semi
248248
{
249-
yylval.program = (PLpgSQL_stmt_block *)$3;
250-
}
251-
| T_TRIGGER comp_optsect pl_block opt_semi
252-
{
253-
yylval.program = (PLpgSQL_stmt_block *)$3;
249+
yylval.program = (PLpgSQL_stmt_block *) $2;
254250
}
255251
;
256252

@@ -1403,7 +1399,7 @@ stmt_raise : K_RAISE lno
14031399
if (tok == T_STRING)
14041400
{
14051401
/* old style message and parameters */
1406-
new->message = plpgsql_get_string_value();
1402+
new->message = parse_string_token(yytext);
14071403
/*
14081404
* We expect either a semi-colon, which
14091405
* indicates no parameters, or a comma that
@@ -1435,7 +1431,7 @@ stmt_raise : K_RAISE lno
14351431

14361432
if (yylex() != T_STRING)
14371433
yyerror("syntax error");
1438-
sqlstatestr = plpgsql_get_string_value();
1434+
sqlstatestr = parse_string_token(yytext);
14391435

14401436
if (strlen(sqlstatestr) != 5)
14411437
yyerror("invalid SQLSTATE code");
@@ -1778,7 +1774,7 @@ proc_condition : opt_lblname
17781774
/* next token should be a string literal */
17791775
if (yylex() != T_STRING)
17801776
yyerror("syntax error");
1781-
sqlstatestr = plpgsql_get_string_value();
1777+
sqlstatestr = parse_string_token(yytext);
17821778

17831779
if (strlen(sqlstatestr) != 5)
17841780
yyerror("invalid SQLSTATE code");
@@ -2738,6 +2734,49 @@ plpgsql_sql_error_callback(void *arg)
27382734
errposition(0);
27392735
}
27402736

2737+
/*
2738+
* Convert a string-literal token to the represented string value.
2739+
*
2740+
* To do this, we need to invoke the core lexer. To avoid confusion between
2741+
* the core bison/flex definitions and our own, the actual invocation is in
2742+
* pl_funcs.c. Here we are only concerned with setting up the right errcontext
2743+
* state, which is handled the same as in check_sql_expr().
2744+
*/
2745+
static char *
2746+
parse_string_token(const char *token)
2747+
{
2748+
char *result;
2749+
ErrorContextCallback syntax_errcontext;
2750+
ErrorContextCallback *previous_errcontext;
2751+
2752+
/* See comments in check_sql_expr() */
2753+
Assert(error_context_stack->callback == plpgsql_compile_error_callback);
2754+
2755+
previous_errcontext = error_context_stack;
2756+
syntax_errcontext.callback = plpgsql_string_error_callback;
2757+
syntax_errcontext.arg = (char *) token;
2758+
syntax_errcontext.previous = error_context_stack->previous;
2759+
error_context_stack = &syntax_errcontext;
2760+
2761+
result = plpgsql_parse_string_token(token);
2762+
2763+
/* Restore former ereport callback */
2764+
error_context_stack = previous_errcontext;
2765+
2766+
return result;
2767+
}
2768+
2769+
static void
2770+
plpgsql_string_error_callback(void *arg)
2771+
{
2772+
Assert(plpgsql_error_funcname);
2773+
2774+
errcontext("string literal in PL/PgSQL function \"%s\" near line %d",
2775+
plpgsql_error_funcname, plpgsql_error_lineno);
2776+
/* representing the string literal as internalquery seems overkill */
2777+
errposition(0);
2778+
}
2779+
27412780
static char *
27422781
check_label(const char *yytxt)
27432782
{

src/pl/plpgsql/src/pl_comp.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.134 2009/02/18 11:33:04 petere Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.135 2009/04/19 18:52:57 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -261,7 +261,7 @@ do_compile(FunctionCallInfo fcinfo,
261261
bool forValidator)
262262
{
263263
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
264-
int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
264+
bool is_trigger = CALLED_AS_TRIGGER(fcinfo);
265265
Datum prosrcdatum;
266266
bool isnull;
267267
char *proc_source;
@@ -293,7 +293,7 @@ do_compile(FunctionCallInfo fcinfo,
293293
if (isnull)
294294
elog(ERROR, "null prosrc");
295295
proc_source = TextDatumGetCString(prosrcdatum);
296-
plpgsql_scanner_init(proc_source, functype);
296+
plpgsql_scanner_init(proc_source);
297297

298298
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
299299
plpgsql_error_lineno = 0;
@@ -359,13 +359,13 @@ do_compile(FunctionCallInfo fcinfo,
359359
function->fn_oid = fcinfo->flinfo->fn_oid;
360360
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
361361
function->fn_tid = procTup->t_self;
362-
function->fn_functype = functype;
362+
function->fn_is_trigger = is_trigger;
363363
function->fn_cxt = func_cxt;
364364
function->out_param_varno = -1; /* set up for no OUT param */
365365

366-
switch (functype)
366+
switch (is_trigger)
367367
{
368-
case T_FUNCTION:
368+
case false:
369369

370370
/*
371371
* Fetch info about the procedure's parameters. Allocations aren't
@@ -564,7 +564,7 @@ do_compile(FunctionCallInfo fcinfo,
564564
ReleaseSysCache(typeTup);
565565
break;
566566

567-
case T_TRIGGER:
567+
case true:
568568
/* Trigger procedure's return type is unknown yet */
569569
function->fn_rettype = InvalidOid;
570570
function->fn_retbyval = false;
@@ -645,7 +645,7 @@ do_compile(FunctionCallInfo fcinfo,
645645
break;
646646

647647
default:
648-
elog(ERROR, "unrecognized function typecode: %u", functype);
648+
elog(ERROR, "unrecognized function typecode: %d", (int) is_trigger);
649649
break;
650650
}
651651

@@ -790,7 +790,7 @@ plpgsql_parse_word(const char *word)
790790
* Recognize tg_argv when compiling triggers
791791
* (XXX this sucks, it should be a regular variable in the namestack)
792792
*/
793-
if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
793+
if (plpgsql_curr_compile->fn_is_trigger)
794794
{
795795
if (strcmp(cp[0], "tg_argv") == 0)
796796
{

src/pl/plpgsql/src/pl_funcs.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.76 2009/02/18 11:33:04 petere Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.77 2009/04/19 18:52:57 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -17,6 +17,8 @@
1717

1818
#include <ctype.h>
1919

20+
#include "parser/gramparse.h"
21+
#include "parser/gram.h"
2022
#include "parser/scansup.h"
2123

2224

@@ -459,6 +461,41 @@ plpgsql_convert_ident(const char *s, char **output, int numidents)
459461
}
460462

461463

464+
/*
465+
* plpgsql_parse_string_token - get the value represented by a string literal
466+
*
467+
* We do not make plpgsql's lexer produce the represented value, because
468+
* in many cases we don't need it. Instead this function is invoked when
469+
* we do need it. The input is the T_STRING token as identified by the lexer.
470+
*
471+
* The result is a palloc'd string.
472+
*
473+
* Note: this is called only from plpgsql's gram.y, but we can't just put it
474+
* there because including parser/gram.h there would cause confusion.
475+
*/
476+
char *
477+
plpgsql_parse_string_token(const char *token)
478+
{
479+
int ctoken;
480+
481+
/*
482+
* We use the core lexer to do the dirty work. Aside from getting the
483+
* right results for escape sequences and so on, this helps us produce
484+
* appropriate warnings for escape_string_warning etc.
485+
*/
486+
scanner_init(token);
487+
488+
ctoken = base_yylex();
489+
490+
if (ctoken != SCONST)
491+
elog(ERROR, "unexpected result from base lexer: %d", ctoken);
492+
493+
scanner_finish();
494+
495+
return base_yylval.str;
496+
}
497+
498+
462499
/*
463500
* Statement type as a string, for use in error messages etc.
464501
*/

src/pl/plpgsql/src/plpgsql.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.110 2009/04/09 02:57:53 tgl Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.111 2009/04/19 18:52:57 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -650,7 +650,7 @@ typedef struct PLpgSQL_function
650650
Oid fn_oid;
651651
TransactionId fn_xmin;
652652
ItemPointerData fn_tid;
653-
int fn_functype;
653+
bool fn_is_trigger;
654654
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
655655
MemoryContext fn_cxt;
656656

@@ -880,6 +880,7 @@ extern void plpgsql_ns_rename(char *oldname, char *newname);
880880
* ----------
881881
*/
882882
extern void plpgsql_convert_ident(const char *s, char **output, int numidents);
883+
extern char *plpgsql_parse_string_token(const char *token);
883884
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
884885
extern void plpgsql_dumptree(PLpgSQL_function *func);
885886

@@ -894,8 +895,7 @@ extern int plpgsql_yylex(void);
894895
extern void plpgsql_push_back_token(int token);
895896
extern void plpgsql_yyerror(const char *message);
896897
extern int plpgsql_scanner_lineno(void);
897-
extern void plpgsql_scanner_init(const char *str, int functype);
898+
extern void plpgsql_scanner_init(const char *str);
898899
extern void plpgsql_scanner_finish(void);
899-
extern char *plpgsql_get_string_value(void);
900900

901901
#endif /* PLPGSQL_H */

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