Skip to content

Commit 12ff678

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 8e26b86 commit 12ff678

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
@@ -4110,6 +4110,9 @@ CONTEXT: whole-row reference to foreign table "ftx"
41104110
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
41114111
ERROR: invalid input syntax for type integer: "foo"
41124112
CONTEXT: processing expression at position 2 in select list
4113+
ANALYZE ft1; -- ERROR
4114+
ERROR: invalid input syntax for type integer: "foo"
4115+
CONTEXT: column "c8" of foreign table "ft1"
41134116
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
41144117
-- ===================================================================
41154118
-- 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 */
@@ -7117,7 +7118,12 @@ complete_pending_request(AsyncRequest *areq)
71177118
* rel is the local representation of the foreign table, attinmeta is
71187119
* conversion data for the rel's tupdesc, and retrieved_attrs is an
71197120
* integer list of the table column numbers present in the PGresult.
7121+
* fsstate is the ForeignScan plan node's execution state.
71207122
* temp_context is a working context that can be reset after each tuple.
7123+
*
7124+
* Note: either rel or fsstate, but not both, can be NULL. rel is NULL
7125+
* if we're processing a remote join, while fsstate is NULL in a non-query
7126+
* context such as ANALYZE, or if we're processing a non-scan query node.
71217127
*/
71227128
static HeapTuple
71237129
make_tuple_from_result_row(PGresult *res,
@@ -7148,6 +7154,10 @@ make_tuple_from_result_row(PGresult *res,
71487154
*/
71497155
oldcontext = MemoryContextSwitchTo(temp_context);
71507156

7157+
/*
7158+
* Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7159+
* provided, otherwise look to the scan node's ScanTupleSlot.
7160+
*/
71517161
if (rel)
71527162
tupdesc = RelationGetDescr(rel);
71537163
else
@@ -7165,6 +7175,7 @@ make_tuple_from_result_row(PGresult *res,
71657175
* Set up and install callback to report where conversion error occurs.
71667176
*/
71677177
errpos.cur_attno = 0;
7178+
errpos.rel = rel;
71687179
errpos.fsstate = fsstate;
71697180
errcallback.callback = conversion_error_callback;
71707181
errcallback.arg = (void *) &errpos;
@@ -7269,60 +7280,87 @@ make_tuple_from_result_row(PGresult *res,
72697280
*
72707281
* Note that this function mustn't do any catalog lookups, since we are in
72717282
* an already-failed transaction. Fortunately, we can get the needed info
7272-
* from the query's rangetable instead.
7283+
* from the relation or the query's rangetable instead.
72737284
*/
72747285
static void
72757286
conversion_error_callback(void *arg)
72767287
{
72777288
ConversionLocation *errpos = (ConversionLocation *) arg;
7289+
Relation rel = errpos->rel;
72787290
ForeignScanState *fsstate = errpos->fsstate;
7279-
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7280-
int varno = 0;
7281-
AttrNumber colno = 0;
72827291
const char *attname = NULL;
72837292
const char *relname = NULL;
72847293
bool is_wholerow = false;
72857294

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

7297-
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7298-
errpos->cur_attno - 1);
7307+
if (fsplan->scan.scanrelid > 0)
7308+
{
7309+
/* error occurred in a scan against a foreign table */
7310+
varno = fsplan->scan.scanrelid;
7311+
colno = errpos->cur_attno;
7312+
}
7313+
else
7314+
{
7315+
/* error occurred in a scan against a foreign join */
7316+
TargetEntry *tle;
72997317

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

7309-
varno = var->varno;
7310-
colno = var->varattno;
7340+
relname = rte->eref->aliasname;
7341+
7342+
if (colno == 0)
7343+
is_wholerow = true;
7344+
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7345+
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7346+
else if (colno == SelfItemPointerAttributeNumber)
7347+
attname = "ctid";
73117348
}
73127349
}
7313-
7314-
if (varno > 0)
7350+
else if (rel)
73157351
{
7316-
EState *estate = fsstate->ss.ps.state;
7317-
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7352+
/* Non-ForeignScan case (we should always have a rel here) */
7353+
TupleDesc tupdesc = RelationGetDescr(rel);
73187354

7319-
relname = rte->eref->aliasname;
7355+
relname = RelationGetRelationName(rel);
7356+
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7357+
{
7358+
Form_pg_attribute attr = TupleDescAttr(tupdesc,
7359+
errpos->cur_attno - 1);
73207360

7321-
if (colno == 0)
7322-
is_wholerow = true;
7323-
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7324-
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7325-
else if (colno == SelfItemPointerAttributeNumber)
7361+
attname = NameStr(attr->attname);
7362+
}
7363+
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
73267364
attname = "ctid";
73277365
}
73287366

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,7 @@ SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11351135
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
11361136
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
11371137
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1138+
ANALYZE ft1; -- ERROR
11381139
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
11391140

11401141
-- ===================================================================

0 commit comments

Comments
 (0)