Skip to content

Commit 01f7d29

Browse files
committed
Improve plpgsql's handling of record field references by forcing all potential
field references in SQL expressions to have RECFIELD datum-array entries at parse time. If it turns out that the reference is actually to a SQL column, the RECFIELD entry is useless, but it costs little. This allows us to get rid of the previous use of FieldSelect applied to a whole-row Param for the record variable; which was not only slower than a direct RECFIELD reference, but failed for references to system columns of a trigger's NEW or OLD record. Per report and fix suggestion from Dean Rasheed.
1 parent f537e7d commit 01f7d29

File tree

4 files changed

+77
-74
lines changed

4 files changed

+77
-74
lines changed

src/pl/plpgsql/src/gram.y

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.137 2010/01/02 16:58:12 momjian Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.138 2010/01/10 17:15:18 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -369,21 +369,21 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
369369
decl_sect : opt_block_label
370370
{
371371
/* done with decls, so resume identifier lookup */
372-
plpgsql_LookupIdentifiers = true;
372+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
373373
$$.label = $1;
374374
$$.n_initvars = 0;
375375
$$.initvarnos = NULL;
376376
}
377377
| opt_block_label decl_start
378378
{
379-
plpgsql_LookupIdentifiers = true;
379+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
380380
$$.label = $1;
381381
$$.n_initvars = 0;
382382
$$.initvarnos = NULL;
383383
}
384384
| opt_block_label decl_start decl_stmts
385385
{
386-
plpgsql_LookupIdentifiers = true;
386+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
387387
if ($3 != NULL)
388388
$$.label = $3;
389389
else
@@ -401,7 +401,7 @@ decl_start : K_DECLARE
401401
* Disable scanner lookup of identifiers while
402402
* we process the decl_stmts
403403
*/
404-
plpgsql_LookupIdentifiers = false;
404+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
405405
}
406406
;
407407

@@ -2121,17 +2121,17 @@ read_sql_construct(int until,
21212121
{
21222122
int tok;
21232123
StringInfoData ds;
2124-
bool save_LookupIdentifiers;
2124+
IdentifierLookup save_IdentifierLookup;
21252125
int startlocation = -1;
21262126
int parenlevel = 0;
21272127
PLpgSQL_expr *expr;
21282128

21292129
initStringInfo(&ds);
21302130
appendStringInfoString(&ds, sqlstart);
21312131

2132-
/* no need to lookup identifiers within the SQL text */
2133-
save_LookupIdentifiers = plpgsql_LookupIdentifiers;
2134-
plpgsql_LookupIdentifiers = false;
2132+
/* special lookup mode for identifiers within the SQL text */
2133+
save_IdentifierLookup = plpgsql_IdentifierLookup;
2134+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
21352135

21362136
for (;;)
21372137
{
@@ -2176,7 +2176,7 @@ read_sql_construct(int until,
21762176
}
21772177
}
21782178

2179-
plpgsql_LookupIdentifiers = save_LookupIdentifiers;
2179+
plpgsql_IdentifierLookup = save_IdentifierLookup;
21802180

21812181
if (startloc)
21822182
*startloc = startlocation;
@@ -2221,8 +2221,8 @@ read_datatype(int tok)
22212221
PLpgSQL_type *result;
22222222
int parenlevel = 0;
22232223

2224-
/* Should always be called with LookupIdentifiers off */
2225-
Assert(!plpgsql_LookupIdentifiers);
2224+
/* Should only be called while parsing DECLARE sections */
2225+
Assert(plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_DECLARE);
22262226

22272227
/* Often there will be a lookahead token, but if not, get one */
22282228
if (tok == YYEMPTY)
@@ -2327,7 +2327,7 @@ static PLpgSQL_stmt *
23272327
make_execsql_stmt(int firsttoken, int location)
23282328
{
23292329
StringInfoData ds;
2330-
bool save_LookupIdentifiers;
2330+
IdentifierLookup save_IdentifierLookup;
23312331
PLpgSQL_stmt_execsql *execsql;
23322332
PLpgSQL_expr *expr;
23332333
PLpgSQL_row *row = NULL;
@@ -2341,9 +2341,9 @@ make_execsql_stmt(int firsttoken, int location)
23412341

23422342
initStringInfo(&ds);
23432343

2344-
/* no need to lookup identifiers within the SQL text */
2345-
save_LookupIdentifiers = plpgsql_LookupIdentifiers;
2346-
plpgsql_LookupIdentifiers = false;
2344+
/* special lookup mode for identifiers within the SQL text */
2345+
save_IdentifierLookup = plpgsql_IdentifierLookup;
2346+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
23472347

23482348
/*
23492349
* We have to special-case the sequence INSERT INTO, because we don't want
@@ -2371,13 +2371,13 @@ make_execsql_stmt(int firsttoken, int location)
23712371
yyerror("INTO specified more than once");
23722372
have_into = true;
23732373
into_start_loc = yylloc;
2374-
plpgsql_LookupIdentifiers = true;
2374+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
23752375
read_into_target(&rec, &row, &have_strict);
2376-
plpgsql_LookupIdentifiers = false;
2376+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
23772377
}
23782378
}
23792379

2380-
plpgsql_LookupIdentifiers = save_LookupIdentifiers;
2380+
plpgsql_IdentifierLookup = save_IdentifierLookup;
23812381

23822382
if (have_into)
23832383
{

src/pl/plpgsql/src/pl_comp.c

Lines changed: 41 additions & 45 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.147 2010/01/02 16:58:12 momjian Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.148 2010/01/10 17:15:18 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1132,11 +1132,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref)
11321132
return make_datum_param(expr, nse->itemno, cref->location);
11331133
if (nnames == nnames_field)
11341134
{
1135-
/* colname must be a field in this record */
1136-
PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[nse->itemno];
1137-
FieldSelect *fselect;
1138-
Oid fldtype;
1139-
int fldno;
1135+
/* colname could be a field in this record */
11401136
int i;
11411137

11421138
/* search for a datum referencing this field */
@@ -1153,28 +1149,19 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref)
11531149
}
11541150

11551151
/*
1156-
* We can't readily add a recfield datum at runtime, so
1157-
* instead build a whole-row Param and a FieldSelect node.
1158-
* This is a bit less efficient, so we prefer the recfield
1159-
* way when possible.
1152+
* We should not get here, because a RECFIELD datum should
1153+
* have been built at parse time for every possible qualified
1154+
* reference to fields of this record. But if we do, fall
1155+
* out and return NULL.
11601156
*/
1161-
fldtype = exec_get_rec_fieldtype(rec, colname,
1162-
&fldno);
1163-
fselect = makeNode(FieldSelect);
1164-
fselect->arg = (Expr *) make_datum_param(expr, nse->itemno,
1165-
cref->location);
1166-
fselect->fieldnum = fldno;
1167-
fselect->resulttype = fldtype;
1168-
fselect->resulttypmod = -1;
1169-
return (Node *) fselect;
11701157
}
11711158
break;
11721159
case PLPGSQL_NSTYPE_ROW:
11731160
if (nnames == nnames_wholerow)
11741161
return make_datum_param(expr, nse->itemno, cref->location);
11751162
if (nnames == nnames_field)
11761163
{
1177-
/* colname must be a field in this row */
1164+
/* colname could be a field in this row */
11781165
PLpgSQL_row *row = (PLpgSQL_row *) estate->datums[nse->itemno];
11791166
int i;
11801167

@@ -1187,10 +1174,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref)
11871174
cref->location);
11881175
}
11891176
}
1190-
ereport(ERROR,
1191-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1192-
errmsg("row \"%s\" has no field \"%s\"",
1193-
row->refname, colname)));
1177+
/* Not found, so return NULL */
11941178
}
11951179
break;
11961180
default:
@@ -1257,8 +1241,12 @@ plpgsql_parse_word(char *word1, const char *yytxt,
12571241
{
12581242
PLpgSQL_nsitem *ns;
12591243

1260-
/* No lookup if disabled */
1261-
if (plpgsql_LookupIdentifiers)
1244+
/*
1245+
* We should do nothing in DECLARE sections. In SQL expressions, there's
1246+
* no need to do anything either --- lookup will happen when the expression
1247+
* is compiled.
1248+
*/
1249+
if (plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_NORMAL)
12621250
{
12631251
/*
12641252
* Do a lookup in the current namespace stack
@@ -1281,6 +1269,7 @@ plpgsql_parse_word(char *word1, const char *yytxt,
12811269
return true;
12821270

12831271
default:
1272+
/* plpgsql_ns_lookup should never return anything else */
12841273
elog(ERROR, "unrecognized plpgsql itemtype: %d",
12851274
ns->itemtype);
12861275
}
@@ -1313,8 +1302,12 @@ plpgsql_parse_dblword(char *word1, char *word2,
13131302
idents = list_make2(makeString(word1),
13141303
makeString(word2));
13151304

1316-
/* No lookup if disabled */
1317-
if (plpgsql_LookupIdentifiers)
1305+
/*
1306+
* We should do nothing in DECLARE sections. In SQL expressions,
1307+
* we really only need to make sure that RECFIELD datums are created
1308+
* when needed.
1309+
*/
1310+
if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE)
13181311
{
13191312
/*
13201313
* Do a lookup in the current namespace stack
@@ -1338,8 +1331,10 @@ plpgsql_parse_dblword(char *word1, char *word2,
13381331
if (nnames == 1)
13391332
{
13401333
/*
1341-
* First word is a record name, so second word must be
1342-
* a field in this record.
1334+
* First word is a record name, so second word could
1335+
* be a field in this record. We build a RECFIELD
1336+
* datum whether it is or not --- any error will be
1337+
* detected later.
13431338
*/
13441339
PLpgSQL_recfield *new;
13451340

@@ -1366,8 +1361,9 @@ plpgsql_parse_dblword(char *word1, char *word2,
13661361
if (nnames == 1)
13671362
{
13681363
/*
1369-
* First word is a row name, so second word must be a
1370-
* field in this row.
1364+
* First word is a row name, so second word could be
1365+
* a field in this row. Again, no error now if it
1366+
* isn't.
13711367
*/
13721368
PLpgSQL_row *row;
13731369
int i;
@@ -1385,10 +1381,7 @@ plpgsql_parse_dblword(char *word1, char *word2,
13851381
return true;
13861382
}
13871383
}
1388-
ereport(ERROR,
1389-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1390-
errmsg("row \"%s\" has no field \"%s\"",
1391-
word1, word2)));
1384+
/* fall through to return CWORD */
13921385
}
13931386
else
13941387
{
@@ -1399,6 +1392,7 @@ plpgsql_parse_dblword(char *word1, char *word2,
13991392
wdatum->idents = idents;
14001393
return true;
14011394
}
1395+
break;
14021396

14031397
default:
14041398
break;
@@ -1429,8 +1423,12 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
14291423
makeString(word2),
14301424
makeString(word3));
14311425

1432-
/* No lookup if disabled */
1433-
if (plpgsql_LookupIdentifiers)
1426+
/*
1427+
* We should do nothing in DECLARE sections. In SQL expressions,
1428+
* we really only need to make sure that RECFIELD datums are created
1429+
* when needed.
1430+
*/
1431+
if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE)
14341432
{
14351433
/*
14361434
* Do a lookup in the current namespace stack. Must find a qualified
@@ -1446,7 +1444,7 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
14461444
case PLPGSQL_NSTYPE_REC:
14471445
{
14481446
/*
1449-
* words 1/2 are a record name, so third word must be a
1447+
* words 1/2 are a record name, so third word could be a
14501448
* field in this record.
14511449
*/
14521450
PLpgSQL_recfield *new;
@@ -1468,8 +1466,8 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
14681466
case PLPGSQL_NSTYPE_ROW:
14691467
{
14701468
/*
1471-
* words 1/2 are a row name, so third word must be a field
1472-
* in this row.
1469+
* words 1/2 are a row name, so third word could be a
1470+
* field in this row.
14731471
*/
14741472
PLpgSQL_row *row;
14751473
int i;
@@ -1487,10 +1485,8 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
14871485
return true;
14881486
}
14891487
}
1490-
ereport(ERROR,
1491-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1492-
errmsg("row \"%s.%s\" has no field \"%s\"",
1493-
word1, word2, word3)));
1488+
/* fall through to return CWORD */
1489+
break;
14941490
}
14951491

14961492
default:

src/pl/plpgsql/src/pl_scanner.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_scanner.c,v 1.3 2010/01/02 16:58:13 momjian Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_scanner.c,v 1.4 2010/01/10 17:15:18 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -23,8 +23,8 @@
2323
#define PG_KEYWORD(a,b,c) {a,b,c},
2424

2525

26-
/* Klugy flag to tell scanner whether to lookup identifiers */
27-
bool plpgsql_LookupIdentifiers = true;
26+
/* Klugy flag to tell scanner how to look up identifiers */
27+
IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
2828

2929
/*
3030
* A word about keywords:
@@ -33,11 +33,10 @@ bool plpgsql_LookupIdentifiers = true;
3333
* reserved keywords are passed to the core scanner, so they will be
3434
* recognized before (and instead of) any variable name. Unreserved
3535
* words are checked for separately, after determining that the identifier
36-
* isn't a known variable name. If plpgsql_LookupIdentifiers is off then
36+
* isn't a known variable name. If plpgsql_IdentifierLookup is DECLARE then
3737
* no variable names will be recognized, so the unreserved words always work.
3838
* (Note in particular that this helps us avoid reserving keywords that are
39-
* only needed in DECLARE sections, since we scan those sections with
40-
* plpgsql_LookupIdentifiers off.)
39+
* only needed in DECLARE sections.)
4140
*
4241
* In certain contexts it is desirable to prefer recognizing an unreserved
4342
* keyword over recognizing a variable name. Those cases are handled in
@@ -193,7 +192,7 @@ static void location_lineno_init(void);
193192
* It is a wrapper around the core lexer, with the ability to recognize
194193
* PL/pgSQL variables and return them as special T_DATUM tokens. If a
195194
* word or compound word does not match any variable name, or if matching
196-
* is turned off by plpgsql_LookupIdentifiers, it is returned as
195+
* is turned off by plpgsql_IdentifierLookup, it is returned as
197196
* T_WORD or T_CWORD respectively, or as an unreserved keyword if it
198197
* matches one of those.
199198
*/
@@ -567,7 +566,7 @@ plpgsql_scanner_init(const char *str)
567566
scanorig = str;
568567

569568
/* Other setup */
570-
plpgsql_LookupIdentifiers = true;
569+
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
571570

572571
num_pushbacks = 0;
573572

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