@@ -31,21 +31,28 @@ IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
31
31
*
32
32
* We keep reserved and unreserved keywords in separate arrays. The
33
33
* reserved keywords are passed to the core scanner, so they will be
34
- * recognized before (and instead of) any variable name. Unreserved
35
- * words are checked for separately, after determining that the identifier
34
+ * recognized before (and instead of) any variable name. Unreserved words
35
+ * are checked for separately, usually after determining that the identifier
36
36
* isn't a known variable name. If plpgsql_IdentifierLookup is DECLARE then
37
37
* no variable names will be recognized, so the unreserved words always work.
38
38
* (Note in particular that this helps us avoid reserving keywords that are
39
39
* only needed in DECLARE sections.)
40
40
*
41
41
* In certain contexts it is desirable to prefer recognizing an unreserved
42
- * keyword over recognizing a variable name. Those cases are handled in
43
- * pl_gram.y using tok_is_keyword().
42
+ * keyword over recognizing a variable name. In particular, at the start
43
+ * of a statement we should prefer unreserved keywords unless the statement
44
+ * looks like an assignment (i.e., first token is followed by ':=' or '[').
45
+ * This rule allows most statement-introducing keywords to be kept unreserved.
46
+ * (We still have to reserve initial keywords that might follow a block
47
+ * label, unfortunately, since the method used to determine if we are at
48
+ * start of statement doesn't recognize such cases. We'd also have to
49
+ * reserve any keyword that could legitimately be followed by ':=' or '['.)
50
+ * Some additional cases are handled in pl_gram.y using tok_is_keyword().
44
51
*
45
- * For the most part, the reserved keywords are those that start a PL/pgSQL
46
- * statement (and so would conflict with an assignment to a variable of the
47
- * same name). We also don't sweat it much about reserving keywords that
48
- * are reserved in the core grammar. Try to avoid reserving other words.
52
+ * We try to avoid reserving more keywords than we have to; but there's
53
+ * little point in not reserving a word if it's reserved in the core grammar.
54
+ * Currently, the following words are reserved here but not in the core:
55
+ * BEGIN BY DECLARE EXECUTE FOREACH IF LOOP STRICT WHILE
49
56
*/
50
57
51
58
/*
@@ -63,37 +70,20 @@ static const ScanKeyword reserved_keywords[] = {
63
70
PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD)
64
71
PG_KEYWORD("by", K_BY, RESERVED_KEYWORD)
65
72
PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD)
66
- PG_KEYWORD("close", K_CLOSE, RESERVED_KEYWORD)
67
- PG_KEYWORD("collate", K_COLLATE, RESERVED_KEYWORD)
68
- PG_KEYWORD("continue", K_CONTINUE, RESERVED_KEYWORD)
69
73
PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD)
70
- PG_KEYWORD("default", K_DEFAULT, RESERVED_KEYWORD)
71
- PG_KEYWORD("diagnostics", K_DIAGNOSTICS, RESERVED_KEYWORD)
72
74
PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD)
73
- PG_KEYWORD("elseif", K_ELSIF, RESERVED_KEYWORD)
74
- PG_KEYWORD("elsif", K_ELSIF, RESERVED_KEYWORD)
75
75
PG_KEYWORD("end", K_END, RESERVED_KEYWORD)
76
- PG_KEYWORD("exception", K_EXCEPTION, RESERVED_KEYWORD)
77
76
PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD)
78
- PG_KEYWORD("exit", K_EXIT, RESERVED_KEYWORD)
79
- PG_KEYWORD("fetch", K_FETCH, RESERVED_KEYWORD)
80
77
PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD)
81
78
PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD)
82
79
PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD)
83
- PG_KEYWORD("get", K_GET, RESERVED_KEYWORD)
84
80
PG_KEYWORD("if", K_IF, RESERVED_KEYWORD)
85
81
PG_KEYWORD("in", K_IN, RESERVED_KEYWORD)
86
- PG_KEYWORD("insert", K_INSERT, RESERVED_KEYWORD)
87
82
PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD)
88
83
PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD)
89
- PG_KEYWORD("move", K_MOVE, RESERVED_KEYWORD)
90
84
PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD)
91
85
PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD)
92
- PG_KEYWORD("open", K_OPEN, RESERVED_KEYWORD)
93
86
PG_KEYWORD("or", K_OR, RESERVED_KEYWORD)
94
- PG_KEYWORD("perform", K_PERFORM, RESERVED_KEYWORD)
95
- PG_KEYWORD("raise", K_RAISE, RESERVED_KEYWORD)
96
- PG_KEYWORD("return", K_RETURN, RESERVED_KEYWORD)
97
87
PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD)
98
88
PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD)
99
89
PG_KEYWORD("to", K_TO, RESERVED_KEYWORD)
@@ -109,32 +99,47 @@ static const ScanKeyword unreserved_keywords[] = {
109
99
PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
110
100
PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
111
101
PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
102
+ PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD)
103
+ PG_KEYWORD("collate", K_COLLATE, UNRESERVED_KEYWORD)
112
104
PG_KEYWORD("column", K_COLUMN, UNRESERVED_KEYWORD)
113
105
PG_KEYWORD("column_name", K_COLUMN_NAME, UNRESERVED_KEYWORD)
114
106
PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
115
107
PG_KEYWORD("constraint", K_CONSTRAINT, UNRESERVED_KEYWORD)
116
108
PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME, UNRESERVED_KEYWORD)
109
+ PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD)
117
110
PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
118
111
PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
119
112
PG_KEYWORD("datatype", K_DATATYPE, UNRESERVED_KEYWORD)
120
113
PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
114
+ PG_KEYWORD("default", K_DEFAULT, UNRESERVED_KEYWORD)
121
115
PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
116
+ PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD)
122
117
PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
118
+ PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD)
119
+ PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD)
123
120
PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
124
121
PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
122
+ PG_KEYWORD("exception", K_EXCEPTION, UNRESERVED_KEYWORD)
123
+ PG_KEYWORD("exit", K_EXIT, UNRESERVED_KEYWORD)
124
+ PG_KEYWORD("fetch", K_FETCH, UNRESERVED_KEYWORD)
125
125
PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
126
126
PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
127
+ PG_KEYWORD("get", K_GET, UNRESERVED_KEYWORD)
127
128
PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
128
129
PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD)
130
+ PG_KEYWORD("insert", K_INSERT, UNRESERVED_KEYWORD)
129
131
PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD)
130
132
PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
131
133
PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
132
134
PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
133
135
PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
136
+ PG_KEYWORD("move", K_MOVE, UNRESERVED_KEYWORD)
134
137
PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
135
138
PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
136
139
PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
140
+ PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD)
137
141
PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
142
+ PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
138
143
PG_KEYWORD("pg_context", K_PG_CONTEXT, UNRESERVED_KEYWORD)
139
144
PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME, UNRESERVED_KEYWORD)
140
145
PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
@@ -143,8 +148,10 @@ static const ScanKeyword unreserved_keywords[] = {
143
148
PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS, UNRESERVED_KEYWORD)
144
149
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
145
150
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
151
+ PG_KEYWORD("raise", K_RAISE, UNRESERVED_KEYWORD)
146
152
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
147
153
PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
154
+ PG_KEYWORD("return", K_RETURN, UNRESERVED_KEYWORD)
148
155
PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
149
156
PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD)
150
157
PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
@@ -166,6 +173,19 @@ static const ScanKeyword unreserved_keywords[] = {
166
173
167
174
static const int num_unreserved_keywords = lengthof(unreserved_keywords);
168
175
176
+ /*
177
+ * This macro must recognize all tokens that can immediately precede a
178
+ * PL/pgSQL executable statement (that is, proc_sect or proc_stmt in the
179
+ * grammar). Fortunately, there are not very many, so hard-coding in this
180
+ * fashion seems sufficient.
181
+ */
182
+ #define AT_STMT_START(prev_token) \
183
+ ((prev_token) == ';' || \
184
+ (prev_token) == K_BEGIN || \
185
+ (prev_token) == K_THEN || \
186
+ (prev_token) == K_ELSE || \
187
+ (prev_token) == K_LOOP)
188
+
169
189
170
190
/* Auxiliary data about a token (other than the token type) */
171
191
typedef struct
@@ -192,6 +212,9 @@ static const char *scanorig;
192
212
/* Current token's length (corresponds to plpgsql_yylval and plpgsql_yylloc) */
193
213
static int plpgsql_yyleng;
194
214
215
+ /* Current token's code (corresponds to plpgsql_yylval and plpgsql_yylloc) */
216
+ static int plpgsql_yytoken;
217
+
195
218
/* Token pushback stack */
196
219
#define MAX_PUSHBACKS 4
197
220
@@ -315,31 +338,75 @@ plpgsql_yylex(void)
315
338
{
316
339
/* not A.B, so just process A */
317
340
push_back_token(tok2, &aux2);
318
- if (plpgsql_parse_word(aux1.lval.str,
319
- core_yy.scanbuf + aux1.lloc,
320
- &aux1.lval.wdatum,
321
- &aux1.lval.word))
322
- tok1 = T_DATUM;
323
- else if (!aux1.lval.word.quoted &&
324
- (kw = ScanKeywordLookup(aux1.lval.word.ident,
325
- unreserved_keywords,
326
- num_unreserved_keywords)))
341
+
342
+ /*
343
+ * If we are at start of statement, prefer unreserved keywords
344
+ * over variable names, unless the next token is assignment or
345
+ * '[', in which case prefer variable names. (Note we need not
346
+ * consider '.' as the next token; that case was handled above,
347
+ * and we always prefer variable names in that case.) If we are
348
+ * not at start of statement, always prefer variable names over
349
+ * unreserved keywords.
350
+ */
351
+ if (AT_STMT_START(plpgsql_yytoken) &&
352
+ !(tok2 == '=' || tok2 == COLON_EQUALS || tok2 == '['))
327
353
{
328
- aux1.lval.keyword = kw->name;
329
- tok1 = kw->value;
354
+ /* try for unreserved keyword, then for variable name */
355
+ if (core_yy.scanbuf[aux1.lloc] != '"' &&
356
+ (kw = ScanKeywordLookup(aux1.lval.str,
357
+ unreserved_keywords,
358
+ num_unreserved_keywords)))
359
+ {
360
+ aux1.lval.keyword = kw->name;
361
+ tok1 = kw->value;
362
+ }
363
+ else if (plpgsql_parse_word(aux1.lval.str,
364
+ core_yy.scanbuf + aux1.lloc,
365
+ &aux1.lval.wdatum,
366
+ &aux1.lval.word))
367
+ tok1 = T_DATUM;
368
+ else
369
+ tok1 = T_WORD;
330
370
}
331
371
else
332
- tok1 = T_WORD;
372
+ {
373
+ /* try for variable name, then for unreserved keyword */
374
+ if (plpgsql_parse_word(aux1.lval.str,
375
+ core_yy.scanbuf + aux1.lloc,
376
+ &aux1.lval.wdatum,
377
+ &aux1.lval.word))
378
+ tok1 = T_DATUM;
379
+ else if (!aux1.lval.word.quoted &&
380
+ (kw = ScanKeywordLookup(aux1.lval.word.ident,
381
+ unreserved_keywords,
382
+ num_unreserved_keywords)))
383
+ {
384
+ aux1.lval.keyword = kw->name;
385
+ tok1 = kw->value;
386
+ }
387
+ else
388
+ tok1 = T_WORD;
389
+ }
333
390
}
334
391
}
335
392
else
336
393
{
337
- /* Not a potential plpgsql variable name, just return the data */
394
+ /*
395
+ * Not a potential plpgsql variable name, just return the data.
396
+ *
397
+ * Note that we also come through here if the grammar pushed back a
398
+ * T_DATUM, T_CWORD, T_WORD, or unreserved-keyword token returned by a
399
+ * previous lookup cycle; thus, pushbacks do not incur extra lookup
400
+ * work, since we'll never do the above code twice for the same token.
401
+ * This property also makes it safe to rely on the old value of
402
+ * plpgsql_yytoken in the is-this-start-of-statement test above.
403
+ */
338
404
}
339
405
340
406
plpgsql_yylval = aux1.lval;
341
407
plpgsql_yylloc = aux1.lloc;
342
408
plpgsql_yyleng = aux1.leng;
409
+ plpgsql_yytoken = tok1;
343
410
return tok1;
344
411
}
345
412
@@ -645,6 +712,7 @@ plpgsql_scanner_init(const char *str)
645
712
646
713
/* Other setup */
647
714
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
715
+ plpgsql_yytoken = 0;
648
716
649
717
num_pushbacks = 0;
650
718
0 commit comments