diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0c442608426d2bef489d4361b5540634e47e9662..4f7123b84f478da6f5c4df00038cd06cb3e1e7de 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -2964,8 +2964,14 @@ make_tuple_from_result_row(PGresult *res, tuple = heap_form_tuple(tupdesc, values, nulls); + /* + * If we have a CTID to return, install it in both t_self and t_ctid. + * t_self is the normal place, but if the tuple is converted to a + * composite Datum, t_self will be lost; setting t_ctid allows CTID to be + * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code). + */ if (ctid) - tuple->t_self = *ctid; + tuple->t_self = tuple->t_data->t_ctid = *ctid; /* Clean up */ MemoryContextReset(temp_context); diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 6cd4e8e11ae97a855972c02b085367deadd93e69..f58f81e1ed7bf5b7f16a35c2b751b9fcb7253700 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -727,6 +727,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, HeapTupleHeaderSetDatumLength(td, len); HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid); HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod); + /* We also make sure that t_ctid is invalid unless explicitly set */ + ItemPointerSetInvalid(&(td->t_ctid)); HeapTupleHeaderSetNatts(td, numberOfAttributes); td->t_hoff = hoff; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 43d3c44c82795dd9f2fe257bf3080db415f4a71c..7c29b4b42aec439072c81e8ed95e0de3a5b7e9a7 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2613,10 +2613,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) /* build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(td); - ItemPointerSetInvalid(&(tuple.t_self)); + tuple.t_data = td; /* relation might be a foreign table, if so provide tableoid */ tuple.t_tableOid = erm->relid; - tuple.t_data = td; + /* also copy t_ctid in case there's valid data there */ + tuple.t_self = td->t_ctid; /* copy and store tuple */ EvalPlanQualSetTuple(epqstate, erm->rti,