Commit da7d44b6 authored by Robert Haas's avatar Robert Haas

postgres_fdw: Clean up handling of system columns.

Previously, querying the xmin column of a single postgres_fdw foreign
table fetched the tuple length, xmax the typmod, and cmin or cmax the
composite type OID of the tuple.  However, when you queried several
such tables and the join got shipped to the remote side, these columns
ended up containing the remote values of the corresponding columns.
Both behaviors are rather unprincipled, the former for obvious reasons
and the latter because the remote values of these columns don't have
any local significance; our transaction IDs are in a different space
than those of the remote machine.  Clean this up by setting all of
these fields to 0 in both cases.  Also fix the handling of tableoid
to be sane.

Robert Haas and Ashutosh Bapat, reviewed by Etsuro Fujita.
parent 5702277c
......@@ -1571,13 +1571,38 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, PlannerInfo *root,
{
RangeTblEntry *rte;
/* varattno can be a whole-row reference, ctid or a regular table column */
if (varattno == SelfItemPointerAttributeNumber)
{
/* We support fetching the remote side's CTID. */
if (qualify_col)
ADD_REL_QUALIFIER(buf, varno);
appendStringInfoString(buf, "ctid");
}
else if (varattno < 0)
{
/*
* All other system attributes are fetched as 0, except for table OID,
* which is fetched as the local table OID. However, we must be
* careful; the table could be beneath an outer join, in which case
* it must go to NULL whenever the rest of the row does.
*/
Oid fetchval = 0;
if (varattno == TableOidAttributeNumber)
{
rte = planner_rt_fetch(varno, root);
fetchval = rte->relid;
}
if (qualify_col)
{
appendStringInfoString(buf, "CASE WHEN ");
ADD_REL_QUALIFIER(buf, varno);
appendStringInfo(buf, "* IS NOT NULL THEN %u END", fetchval);
}
else
appendStringInfo(buf, "%u", fetchval);
}
else if (varattno == 0)
{
/* Whole row reference */
......@@ -1606,10 +1631,29 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, PlannerInfo *root,
*/
attrs_used = bms_add_member(NULL,
0 - FirstLowInvalidHeapAttributeNumber);
/*
* In case the whole-row reference is under an outer join then it has to
* go NULL whenver the rest of the row goes NULL. Deparsing a join query
* would always involve multiple relations, thus qualify_col would be
* true.
*/
if (qualify_col)
{
appendStringInfoString(buf, "CASE WHEN ");
ADD_REL_QUALIFIER(buf, varno);
appendStringInfo(buf, "* IS NOT NULL THEN ");
}
appendStringInfoString(buf, "ROW(");
deparseTargetList(buf, root, varno, rel, false, attrs_used, qualify_col,
&retrieved_attrs);
appendStringInfoString(buf, ")");
/* Complete the CASE WHEN statement started above. */
if (qualify_col)
appendStringInfo(buf," END");
heap_close(rel, NoLock);
bms_free(attrs_used);
}
......
......@@ -4410,6 +4410,18 @@ make_tuple_from_result_row(PGresult *res,
if (ctid)
tuple->t_self = tuple->t_data->t_ctid = *ctid;
/*
* Stomp on the xmin, xmax, and cmin fields from the tuple created by
* heap_form_tuple. heap_form_tuple actually creates the tuple with
* DatumTupleFields, not HeapTupleFields, but the executor expects
* HeapTupleFields and will happily extract system columns on that
* assumption. If we don't do this then, for example, the tuple length
* ends up in the xmin field, which isn't what we want.
*/
HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
HeapTupleHeaderSetXmin(tuple->t_data, InvalidTransactionId);
HeapTupleHeaderSetCmin(tuple->t_data, InvalidTransactionId);
/* Clean up */
MemoryContextReset(temp_context);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment