Skip to content

Commit 8880775

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 b2a0f16 commit 8880775

File tree

3 files changed

+80
-38
lines changed

3 files changed

+80
-38
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4204,6 +4204,9 @@ CONTEXT: whole-row reference to foreign table "ftx"
42044204
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
42054205
ERROR: invalid input syntax for integer: "foo"
42064206
CONTEXT: processing expression at position 2 in select list
4207+
ANALYZE ft1; -- ERROR
4208+
ERROR: invalid input syntax for integer: "foo"
4209+
CONTEXT: column "c8" of foreign table "ft1"
42074210
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
42084211
-- ===================================================================
42094212
-- subtransaction

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ typedef struct PgFdwAnalyzeState
254254
typedef struct ConversionLocation
255255
{
256256
AttrNumber cur_attno; /* attribute number being processed, or 0 */
257-
ForeignScanState *fsstate; /* plan node being processed */
257+
Relation rel; /* foreign table being processed, or NULL */
258+
ForeignScanState *fsstate; /* plan node being processed, or NULL */
258259
} ConversionLocation;
259260

260261
/* Callback argument for ec_member_matches_foreign */
@@ -5639,7 +5640,12 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
56395640
* rel is the local representation of the foreign table, attinmeta is
56405641
* conversion data for the rel's tupdesc, and retrieved_attrs is an
56415642
* integer list of the table column numbers present in the PGresult.
5643+
* fsstate is the ForeignScan plan node's execution state.
56425644
* temp_context is a working context that can be reset after each tuple.
5645+
*
5646+
* Note: either rel or fsstate, but not both, can be NULL. rel is NULL
5647+
* if we're processing a remote join, while fsstate is NULL in a non-query
5648+
* context such as ANALYZE, or if we're processing a non-scan query node.
56435649
*/
56445650
static HeapTuple
56455651
make_tuple_from_result_row(PGresult *res,
@@ -5671,6 +5677,10 @@ make_tuple_from_result_row(PGresult *res,
56715677
*/
56725678
oldcontext = MemoryContextSwitchTo(temp_context);
56735679

5680+
/*
5681+
* Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
5682+
* provided, otherwise look to the scan node's ScanTupleSlot.
5683+
*/
56745684
if (rel)
56755685
tupdesc = RelationGetDescr(rel);
56765686
else
@@ -5688,6 +5698,7 @@ make_tuple_from_result_row(PGresult *res,
56885698
* Set up and install callback to report where conversion error occurs.
56895699
*/
56905700
errpos.cur_attno = 0;
5701+
errpos.rel = rel;
56915702
errpos.fsstate = fsstate;
56925703
errcallback.callback = conversion_error_callback;
56935704
errcallback.arg = (void *) &errpos;
@@ -5809,63 +5820,90 @@ make_tuple_from_result_row(PGresult *res,
58095820
*
58105821
* Note that this function mustn't do any catalog lookups, since we are in
58115822
* an already-failed transaction. Fortunately, we can get the needed info
5812-
* from the query's rangetable instead.
5823+
* from the relation or the query's rangetable instead.
58135824
*/
58145825
static void
58155826
conversion_error_callback(void *arg)
58165827
{
58175828
ConversionLocation *errpos = (ConversionLocation *) arg;
5829+
Relation rel = errpos->rel;
58185830
ForeignScanState *fsstate = errpos->fsstate;
5819-
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5820-
int varno = 0;
5821-
AttrNumber colno = 0;
58225831
const char *attname = NULL;
58235832
const char *relname = NULL;
58245833
bool is_wholerow = false;
58255834

5826-
if (fsplan->scan.scanrelid > 0)
5827-
{
5828-
/* error occurred in a scan against a foreign table */
5829-
varno = fsplan->scan.scanrelid;
5830-
colno = errpos->cur_attno;
5831-
}
5832-
else
5835+
/*
5836+
* If we're in a scan node, always use aliases from the rangetable, for
5837+
* consistency between the simple-relation and remote-join cases. Look at
5838+
* the relation's tupdesc only if we're not in a scan node.
5839+
*/
5840+
if (fsstate)
58335841
{
5834-
/* error occurred in a scan against a foreign join */
5835-
TargetEntry *tle;
5836-
5837-
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5838-
errpos->cur_attno - 1);
5842+
/* ForeignScan case */
5843+
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5844+
int varno = 0;
5845+
AttrNumber colno = 0;
58395846

5840-
/*
5841-
* Target list can have Vars and expressions. For Vars, we can get
5842-
* some information, however for expressions we can't. Thus for
5843-
* expressions, just show generic context message.
5844-
*/
5845-
if (IsA(tle->expr, Var))
5847+
if (fsplan->scan.scanrelid > 0)
5848+
{
5849+
/* error occurred in a scan against a foreign table */
5850+
varno = fsplan->scan.scanrelid;
5851+
colno = errpos->cur_attno;
5852+
}
5853+
else
58465854
{
5847-
Var *var = (Var *) tle->expr;
5855+
/* error occurred in a scan against a foreign join */
5856+
TargetEntry *tle;
5857+
5858+
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5859+
errpos->cur_attno - 1);
5860+
5861+
/*
5862+
* Target list can have Vars and expressions. For Vars, we can
5863+
* get some information, however for expressions we can't. Thus
5864+
* for expressions, just show generic context message.
5865+
*/
5866+
if (IsA(tle->expr, Var))
5867+
{
5868+
Var *var = (Var *) tle->expr;
5869+
5870+
varno = var->varno;
5871+
colno = var->varattno;
5872+
}
5873+
}
58485874

5849-
varno = var->varno;
5850-
colno = var->varattno;
5875+
if (varno > 0)
5876+
{
5877+
EState *estate = fsstate->ss.ps.state;
5878+
RangeTblEntry *rte = rt_fetch(varno, estate->es_range_table);
5879+
5880+
relname = rte->eref->aliasname;
5881+
5882+
if (colno == 0)
5883+
is_wholerow = true;
5884+
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
5885+
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
5886+
else if (colno == SelfItemPointerAttributeNumber)
5887+
attname = "ctid";
5888+
else if (colno == ObjectIdAttributeNumber)
5889+
attname = "oid";
58515890
}
58525891
}
5853-
5854-
if (varno > 0)
5892+
else if (rel)
58555893
{
5856-
EState *estate = fsstate->ss.ps.state;
5857-
RangeTblEntry *rte = rt_fetch(varno, estate->es_range_table);
5894+
/* Non-ForeignScan case (we should always have a rel here) */
5895+
TupleDesc tupdesc = RelationGetDescr(rel);
58585896

5859-
relname = rte->eref->aliasname;
5897+
relname = RelationGetRelationName(rel);
5898+
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5899+
{
5900+
Form_pg_attribute attr = TupleDescAttr(tupdesc,
5901+
errpos->cur_attno - 1);
58605902

5861-
if (colno == 0)
5862-
is_wholerow = true;
5863-
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
5864-
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
5865-
else if (colno == SelfItemPointerAttributeNumber)
5903+
attname = NameStr(attr->attname);
5904+
}
5905+
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
58665906
attname = "ctid";
5867-
else if (colno == ObjectIdAttributeNumber)
5868-
attname = "oid";
58695907
}
58705908

58715909
if (relname && is_wholerow)

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
10671067
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
10681068
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
10691069
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1070+
ANALYZE ft1; -- ERROR
10701071
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
10711072

10721073
-- ===================================================================

0 commit comments

Comments
 (0)