Skip to content

Commit aee83f3

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 9ab94cc commit aee83f3

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
@@ -4079,6 +4079,9 @@ CONTEXT: whole-row reference to foreign table "ftx"
40794079
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
40804080
ERROR: invalid input syntax for type integer: "foo"
40814081
CONTEXT: processing expression at position 2 in select list
4082+
ANALYZE ft1; -- ERROR
4083+
ERROR: invalid input syntax for type integer: "foo"
4084+
CONTEXT: column "c8" of foreign table "ft1"
40824085
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
40834086
-- ===================================================================
40844087
-- subtransaction

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ typedef struct
286286
typedef struct ConversionLocation
287287
{
288288
AttrNumber cur_attno; /* attribute number being processed, or 0 */
289-
ForeignScanState *fsstate; /* plan node being processed */
289+
Relation rel; /* foreign table being processed, or NULL */
290+
ForeignScanState *fsstate; /* plan node being processed, or NULL */
290291
} ConversionLocation;
291292

292293
/* Callback argument for ec_member_matches_foreign */
@@ -6345,7 +6346,12 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
63456346
* rel is the local representation of the foreign table, attinmeta is
63466347
* conversion data for the rel's tupdesc, and retrieved_attrs is an
63476348
* integer list of the table column numbers present in the PGresult.
6349+
* fsstate is the ForeignScan plan node's execution state.
63486350
* temp_context is a working context that can be reset after each tuple.
6351+
*
6352+
* Note: either rel or fsstate, but not both, can be NULL. rel is NULL
6353+
* if we're processing a remote join, while fsstate is NULL in a non-query
6354+
* context such as ANALYZE, or if we're processing a non-scan query node.
63496355
*/
63506356
static HeapTuple
63516357
make_tuple_from_result_row(PGresult *res,
@@ -6376,6 +6382,10 @@ make_tuple_from_result_row(PGresult *res,
63766382
*/
63776383
oldcontext = MemoryContextSwitchTo(temp_context);
63786384

6385+
/*
6386+
* Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
6387+
* provided, otherwise look to the scan node's ScanTupleSlot.
6388+
*/
63796389
if (rel)
63806390
tupdesc = RelationGetDescr(rel);
63816391
else
@@ -6393,6 +6403,7 @@ make_tuple_from_result_row(PGresult *res,
63936403
* Set up and install callback to report where conversion error occurs.
63946404
*/
63956405
errpos.cur_attno = 0;
6406+
errpos.rel = rel;
63966407
errpos.fsstate = fsstate;
63976408
errcallback.callback = conversion_error_callback;
63986409
errcallback.arg = (void *) &errpos;
@@ -6497,60 +6508,87 @@ make_tuple_from_result_row(PGresult *res,
64976508
*
64986509
* Note that this function mustn't do any catalog lookups, since we are in
64996510
* an already-failed transaction. Fortunately, we can get the needed info
6500-
* from the query's rangetable instead.
6511+
* from the relation or the query's rangetable instead.
65016512
*/
65026513
static void
65036514
conversion_error_callback(void *arg)
65046515
{
65056516
ConversionLocation *errpos = (ConversionLocation *) arg;
6517+
Relation rel = errpos->rel;
65066518
ForeignScanState *fsstate = errpos->fsstate;
6507-
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
6508-
int varno = 0;
6509-
AttrNumber colno = 0;
65106519
const char *attname = NULL;
65116520
const char *relname = NULL;
65126521
bool is_wholerow = false;
65136522

6514-
if (fsplan->scan.scanrelid > 0)
6515-
{
6516-
/* error occurred in a scan against a foreign table */
6517-
varno = fsplan->scan.scanrelid;
6518-
colno = errpos->cur_attno;
6519-
}
6520-
else
6523+
/*
6524+
* If we're in a scan node, always use aliases from the rangetable, for
6525+
* consistency between the simple-relation and remote-join cases. Look at
6526+
* the relation's tupdesc only if we're not in a scan node.
6527+
*/
6528+
if (fsstate)
65216529
{
6522-
/* error occurred in a scan against a foreign join */
6523-
TargetEntry *tle;
6530+
/* ForeignScan case */
6531+
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
6532+
int varno = 0;
6533+
AttrNumber colno = 0;
65246534

6525-
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
6526-
errpos->cur_attno - 1);
6535+
if (fsplan->scan.scanrelid > 0)
6536+
{
6537+
/* error occurred in a scan against a foreign table */
6538+
varno = fsplan->scan.scanrelid;
6539+
colno = errpos->cur_attno;
6540+
}
6541+
else
6542+
{
6543+
/* error occurred in a scan against a foreign join */
6544+
TargetEntry *tle;
65276545

6528-
/*
6529-
* Target list can have Vars and expressions. For Vars, we can get
6530-
* some information, however for expressions we can't. Thus for
6531-
* expressions, just show generic context message.
6532-
*/
6533-
if (IsA(tle->expr, Var))
6546+
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
6547+
errpos->cur_attno - 1);
6548+
6549+
/*
6550+
* Target list can have Vars and expressions. For Vars, we can
6551+
* get some information, however for expressions we can't. Thus
6552+
* for expressions, just show generic context message.
6553+
*/
6554+
if (IsA(tle->expr, Var))
6555+
{
6556+
Var *var = (Var *) tle->expr;
6557+
6558+
varno = var->varno;
6559+
colno = var->varattno;
6560+
}
6561+
}
6562+
6563+
if (varno > 0)
65346564
{
6535-
Var *var = (Var *) tle->expr;
6565+
EState *estate = fsstate->ss.ps.state;
6566+
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
65366567

6537-
varno = var->varno;
6538-
colno = var->varattno;
6568+
relname = rte->eref->aliasname;
6569+
6570+
if (colno == 0)
6571+
is_wholerow = true;
6572+
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
6573+
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
6574+
else if (colno == SelfItemPointerAttributeNumber)
6575+
attname = "ctid";
65396576
}
65406577
}
6541-
6542-
if (varno > 0)
6578+
else if (rel)
65436579
{
6544-
EState *estate = fsstate->ss.ps.state;
6545-
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
6580+
/* Non-ForeignScan case (we should always have a rel here) */
6581+
TupleDesc tupdesc = RelationGetDescr(rel);
65466582

6547-
relname = rte->eref->aliasname;
6583+
relname = RelationGetRelationName(rel);
6584+
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
6585+
{
6586+
Form_pg_attribute attr = TupleDescAttr(tupdesc,
6587+
errpos->cur_attno - 1);
65486588

6549-
if (colno == 0)
6550-
is_wholerow = true;
6551-
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
6552-
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
6553-
else if (colno == SelfItemPointerAttributeNumber)
6589+
attname = NameStr(attr->attname);
6590+
}
6591+
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
65546592
attname = "ctid";
65556593
}
65566594

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11031103
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11041104
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
11051105
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1106+
ANALYZE ft1; -- ERROR
11061107
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
11071108

11081109
-- ===================================================================

0 commit comments

Comments
 (0)