Skip to content

Commit 2e33fbd

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 4853baa commit 2e33fbd

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
@@ -4189,6 +4189,9 @@ CONTEXT: whole-row reference to foreign table "ftx"
41894189
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
41904190
ERROR: invalid input syntax for integer: "foo"
41914191
CONTEXT: processing expression at position 2 in select list
4192+
ANALYZE ft1; -- ERROR
4193+
ERROR: invalid input syntax for integer: "foo"
4194+
CONTEXT: column "c8" of foreign table "ft1"
41924195
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
41934196
-- ===================================================================
41944197
-- subtransaction

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ typedef struct PgFdwAnalyzeState
245245
typedef struct ConversionLocation
246246
{
247247
AttrNumber cur_attno; /* attribute number being processed, or 0 */
248-
ForeignScanState *fsstate; /* plan node being processed */
248+
Relation rel; /* foreign table being processed, or NULL */
249+
ForeignScanState *fsstate; /* plan node being processed, or NULL */
249250
} ConversionLocation;
250251

251252
/* Callback argument for ec_member_matches_foreign */
@@ -4967,7 +4968,12 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
49674968
* rel is the local representation of the foreign table, attinmeta is
49684969
* conversion data for the rel's tupdesc, and retrieved_attrs is an
49694970
* integer list of the table column numbers present in the PGresult.
4971+
* fsstate is the ForeignScan plan node's execution state.
49704972
* temp_context is a working context that can be reset after each tuple.
4973+
*
4974+
* Note: either rel or fsstate, but not both, can be NULL. rel is NULL
4975+
* if we're processing a remote join, while fsstate is NULL in a non-query
4976+
* context such as ANALYZE, or if we're processing a non-scan query node.
49714977
*/
49724978
static HeapTuple
49734979
make_tuple_from_result_row(PGresult *res,
@@ -4999,6 +5005,10 @@ make_tuple_from_result_row(PGresult *res,
49995005
*/
50005006
oldcontext = MemoryContextSwitchTo(temp_context);
50015007

5008+
/*
5009+
* Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
5010+
* provided, otherwise look to the scan node's ScanTupleSlot.
5011+
*/
50025012
if (rel)
50035013
tupdesc = RelationGetDescr(rel);
50045014
else
@@ -5019,6 +5029,7 @@ make_tuple_from_result_row(PGresult *res,
50195029
* Set up and install callback to report where conversion error occurs.
50205030
*/
50215031
errpos.cur_attno = 0;
5032+
errpos.rel = rel;
50225033
errpos.fsstate = fsstate;
50235034
errcallback.callback = conversion_error_callback;
50245035
errcallback.arg = (void *) &errpos;
@@ -5140,63 +5151,90 @@ make_tuple_from_result_row(PGresult *res,
51405151
*
51415152
* Note that this function mustn't do any catalog lookups, since we are in
51425153
* an already-failed transaction. Fortunately, we can get the needed info
5143-
* from the query's rangetable instead.
5154+
* from the relation or the query's rangetable instead.
51445155
*/
51455156
static void
51465157
conversion_error_callback(void *arg)
51475158
{
51485159
ConversionLocation *errpos = (ConversionLocation *) arg;
5160+
Relation rel = errpos->rel;
51495161
ForeignScanState *fsstate = errpos->fsstate;
5150-
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5151-
int varno = 0;
5152-
AttrNumber colno = 0;
51535162
const char *attname = NULL;
51545163
const char *relname = NULL;
51555164
bool is_wholerow = false;
51565165

5157-
if (fsplan->scan.scanrelid > 0)
5158-
{
5159-
/* error occurred in a scan against a foreign table */
5160-
varno = fsplan->scan.scanrelid;
5161-
colno = errpos->cur_attno;
5162-
}
5163-
else
5166+
/*
5167+
* If we're in a scan node, always use aliases from the rangetable, for
5168+
* consistency between the simple-relation and remote-join cases. Look at
5169+
* the relation's tupdesc only if we're not in a scan node.
5170+
*/
5171+
if (fsstate)
51645172
{
5165-
/* error occurred in a scan against a foreign join */
5166-
TargetEntry *tle;
5167-
5168-
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5169-
errpos->cur_attno - 1);
5173+
/* ForeignScan case */
5174+
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5175+
int varno = 0;
5176+
AttrNumber colno = 0;
51705177

5171-
/*
5172-
* Target list can have Vars and expressions. For Vars, we can get
5173-
* some information, however for expressions we can't. Thus for
5174-
* expressions, just show generic context message.
5175-
*/
5176-
if (IsA(tle->expr, Var))
5178+
if (fsplan->scan.scanrelid > 0)
5179+
{
5180+
/* error occurred in a scan against a foreign table */
5181+
varno = fsplan->scan.scanrelid;
5182+
colno = errpos->cur_attno;
5183+
}
5184+
else
51775185
{
5178-
Var *var = (Var *) tle->expr;
5186+
/* error occurred in a scan against a foreign join */
5187+
TargetEntry *tle;
5188+
5189+
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5190+
errpos->cur_attno - 1);
5191+
5192+
/*
5193+
* Target list can have Vars and expressions. For Vars, we can
5194+
* get some information, however for expressions we can't. Thus
5195+
* for expressions, just show generic context message.
5196+
*/
5197+
if (IsA(tle->expr, Var))
5198+
{
5199+
Var *var = (Var *) tle->expr;
5200+
5201+
varno = var->varno;
5202+
colno = var->varattno;
5203+
}
5204+
}
51795205

5180-
varno = var->varno;
5181-
colno = var->varattno;
5206+
if (varno > 0)
5207+
{
5208+
EState *estate = fsstate->ss.ps.state;
5209+
RangeTblEntry *rte = rt_fetch(varno, estate->es_range_table);
5210+
5211+
relname = rte->eref->aliasname;
5212+
5213+
if (colno == 0)
5214+
is_wholerow = true;
5215+
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
5216+
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
5217+
else if (colno == SelfItemPointerAttributeNumber)
5218+
attname = "ctid";
5219+
else if (colno == ObjectIdAttributeNumber)
5220+
attname = "oid";
51825221
}
51835222
}
5184-
5185-
if (varno > 0)
5223+
else if (rel)
51865224
{
5187-
EState *estate = fsstate->ss.ps.state;
5188-
RangeTblEntry *rte = rt_fetch(varno, estate->es_range_table);
5225+
/* Non-ForeignScan case (we should always have a rel here) */
5226+
TupleDesc tupdesc = RelationGetDescr(rel);
51895227

5190-
relname = rte->eref->aliasname;
5228+
relname = RelationGetRelationName(rel);
5229+
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5230+
{
5231+
Form_pg_attribute attr = TupleDescAttr(tupdesc,
5232+
errpos->cur_attno - 1);
51915233

5192-
if (colno == 0)
5193-
is_wholerow = true;
5194-
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
5195-
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
5196-
else if (colno == SelfItemPointerAttributeNumber)
5234+
attname = NameStr(attr->attname);
5235+
}
5236+
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
51975237
attname = "ctid";
5198-
else if (colno == ObjectIdAttributeNumber)
5199-
attname = "oid";
52005238
}
52015239

52025240
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
@@ -1061,6 +1061,7 @@ SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
10611061
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
10621062
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
10631063
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1064+
ANALYZE ft1; -- ERROR
10641065
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
10651066

10661067
-- ===================================================================

0 commit comments

Comments
 (0)