Skip to content

Commit 3071bbf

Browse files
committed
Fix null-pointer crash in postgres_fdw's conversion_error_callback.
Commit c7b7311 adjusted conversion_error_callback to always use information from the query's rangetable, to avoid doing catalog lookups in an already-failed transaction. However, as a result of the utterly inadequate documentation for make_tuple_from_result_row, I failed to realize that fsstate could be NULL in some contexts. That led to a crash if we got a conversion error in such a context. Fix by falling back to the previous coding when fsstate is NULL. Improve the commentary, too. Per report from Andrey Borodin. Back-patch to 9.6, like the previous patch. Discussion: https://postgr.es/m/08916396-55E4-4D68-AB3A-BD6066F9E5C0@yandex-team.ru
1 parent db692b0 commit 3071bbf

File tree

3 files changed

+77
-35
lines changed

3 files changed

+77
-35
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4200,6 +4200,9 @@ CONTEXT: whole-row reference to foreign table "ftx"
42004200
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
42014201
ERROR: invalid input syntax for type integer: "foo"
42024202
CONTEXT: processing expression at position 2 in select list
4203+
ANALYZE ft1; -- ERROR
4204+
ERROR: invalid input syntax for type integer: "foo"
4205+
CONTEXT: column "c8" of foreign table "ft1"
42034206
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
42044207
-- ===================================================================
42054208
-- subtransaction

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ typedef struct
303303
typedef struct ConversionLocation
304304
{
305305
AttrNumber cur_attno; /* attribute number being processed, or 0 */
306-
ForeignScanState *fsstate; /* plan node being processed */
306+
Relation rel; /* foreign table being processed, or NULL */
307+
ForeignScanState *fsstate; /* plan node being processed, or NULL */
307308
} ConversionLocation;
308309

309310
/* Callback argument for ec_member_matches_foreign */
@@ -7113,7 +7114,12 @@ complete_pending_request(AsyncRequest *areq)
71137114
* rel is the local representation of the foreign table, attinmeta is
71147115
* conversion data for the rel's tupdesc, and retrieved_attrs is an
71157116
* integer list of the table column numbers present in the PGresult.
7117+
* fsstate is the ForeignScan plan node's execution state.
71167118
* temp_context is a working context that can be reset after each tuple.
7119+
*
7120+
* Note: either rel or fsstate, but not both, can be NULL. rel is NULL
7121+
* if we're processing a remote join, while fsstate is NULL in a non-query
7122+
* context such as ANALYZE, or if we're processing a non-scan query node.
71177123
*/
71187124
static HeapTuple
71197125
make_tuple_from_result_row(PGresult *res,
@@ -7144,6 +7150,10 @@ make_tuple_from_result_row(PGresult *res,
71447150
*/
71457151
oldcontext = MemoryContextSwitchTo(temp_context);
71467152

7153+
/*
7154+
* Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7155+
* provided, otherwise look to the scan node's ScanTupleSlot.
7156+
*/
71477157
if (rel)
71487158
tupdesc = RelationGetDescr(rel);
71497159
else
@@ -7161,6 +7171,7 @@ make_tuple_from_result_row(PGresult *res,
71617171
* Set up and install callback to report where conversion error occurs.
71627172
*/
71637173
errpos.cur_attno = 0;
7174+
errpos.rel = rel;
71647175
errpos.fsstate = fsstate;
71657176
errcallback.callback = conversion_error_callback;
71667177
errcallback.arg = (void *) &errpos;
@@ -7265,60 +7276,87 @@ make_tuple_from_result_row(PGresult *res,
72657276
*
72667277
* Note that this function mustn't do any catalog lookups, since we are in
72677278
* an already-failed transaction. Fortunately, we can get the needed info
7268-
* from the query's rangetable instead.
7279+
* from the relation or the query's rangetable instead.
72697280
*/
72707281
static void
72717282
conversion_error_callback(void *arg)
72727283
{
72737284
ConversionLocation *errpos = (ConversionLocation *) arg;
7285+
Relation rel = errpos->rel;
72747286
ForeignScanState *fsstate = errpos->fsstate;
7275-
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7276-
int varno = 0;
7277-
AttrNumber colno = 0;
72787287
const char *attname = NULL;
72797288
const char *relname = NULL;
72807289
bool is_wholerow = false;
72817290

7282-
if (fsplan->scan.scanrelid > 0)
7283-
{
7284-
/* error occurred in a scan against a foreign table */
7285-
varno = fsplan->scan.scanrelid;
7286-
colno = errpos->cur_attno;
7287-
}
7288-
else
7291+
/*
7292+
* If we're in a scan node, always use aliases from the rangetable, for
7293+
* consistency between the simple-relation and remote-join cases. Look at
7294+
* the relation's tupdesc only if we're not in a scan node.
7295+
*/
7296+
if (fsstate)
72897297
{
7290-
/* error occurred in a scan against a foreign join */
7291-
TargetEntry *tle;
7298+
/* ForeignScan case */
7299+
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7300+
int varno = 0;
7301+
AttrNumber colno = 0;
72927302

7293-
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7294-
errpos->cur_attno - 1);
7303+
if (fsplan->scan.scanrelid > 0)
7304+
{
7305+
/* error occurred in a scan against a foreign table */
7306+
varno = fsplan->scan.scanrelid;
7307+
colno = errpos->cur_attno;
7308+
}
7309+
else
7310+
{
7311+
/* error occurred in a scan against a foreign join */
7312+
TargetEntry *tle;
72957313

7296-
/*
7297-
* Target list can have Vars and expressions. For Vars, we can get
7298-
* some information, however for expressions we can't. Thus for
7299-
* expressions, just show generic context message.
7300-
*/
7301-
if (IsA(tle->expr, Var))
7314+
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7315+
errpos->cur_attno - 1);
7316+
7317+
/*
7318+
* Target list can have Vars and expressions. For Vars, we can
7319+
* get some information, however for expressions we can't. Thus
7320+
* for expressions, just show generic context message.
7321+
*/
7322+
if (IsA(tle->expr, Var))
7323+
{
7324+
Var *var = (Var *) tle->expr;
7325+
7326+
varno = var->varno;
7327+
colno = var->varattno;
7328+
}
7329+
}
7330+
7331+
if (varno > 0)
73027332
{
7303-
Var *var = (Var *) tle->expr;
7333+
EState *estate = fsstate->ss.ps.state;
7334+
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
73047335

7305-
varno = var->varno;
7306-
colno = var->varattno;
7336+
relname = rte->eref->aliasname;
7337+
7338+
if (colno == 0)
7339+
is_wholerow = true;
7340+
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7341+
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7342+
else if (colno == SelfItemPointerAttributeNumber)
7343+
attname = "ctid";
73077344
}
73087345
}
7309-
7310-
if (varno > 0)
7346+
else if (rel)
73117347
{
7312-
EState *estate = fsstate->ss.ps.state;
7313-
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7348+
/* Non-ForeignScan case (we should always have a rel here) */
7349+
TupleDesc tupdesc = RelationGetDescr(rel);
73147350

7315-
relname = rte->eref->aliasname;
7351+
relname = RelationGetRelationName(rel);
7352+
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7353+
{
7354+
Form_pg_attribute attr = TupleDescAttr(tupdesc,
7355+
errpos->cur_attno - 1);
73167356

7317-
if (colno == 0)
7318-
is_wholerow = true;
7319-
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7320-
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7321-
else if (colno == SelfItemPointerAttributeNumber)
7357+
attname = NameStr(attr->attname);
7358+
}
7359+
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
73227360
attname = "ctid";
73237361
}
73247362

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,7 @@ SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11641164
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11651165
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
11661166
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1167+
ANALYZE ft1; -- ERROR
11671168
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
11681169

11691170
-- ===================================================================

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