Here is an example using postgres_fdw.

[Terminal 1]
postgres=# create table t (a int, b int);
CREATE TABLE
postgres=# insert into t values (1, 1);
INSERT 0 1
postgres=# begin;
BEGIN
postgres=# update t set b = b * 2;
UPDATE 1

[Terminal 2]
postgres=# create foreign table ft (a int) server loopback options
(table_name 'lbt');
CREATE FOREIGN TABLE
postgres=# insert into ft values (1);
INSERT 0 1
postgres=# select tableoid, ctid, * from ft;
 tableoid | ctid  | a
----------+-------+---
    25092 | (0,1) | 1
(1 row)

postgres=# select ft.tableoid, ft.ctid, ft.* from t, ft where t.a = ft.a
for update;

[Terminal 1]
postgres=# commit;
COMMIT

[Terminal 2]
postgres=# select ft.tableoid, ft.ctid, ft.* from t, ft where t.a = ft.a
for update;
 tableoid |      ctid      | a
----------+----------------+---
        0 | (4294967295,0) | 1
(1 row)

Note that tableoid and ctid have been changed!

I think the reason for that is because EvalPlanQualFetchRowMarks doesn't
properly set tableoid and ctid for foreign tables, IIUC.  I think this
should be fixed.  Please find attached a patch.  The patch slightly
relates to [1], so if it is reasonable, I'll update [1] on top of this.

[1] https://commitfest.postgresql.org/action/patch_view?id=1386

Best regards,
Etsuro Fujita
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 2947,2953 **** make_tuple_from_result_row(PGresult *res,
  	tuple = heap_form_tuple(tupdesc, values, nulls);
  
  	if (ctid)
! 		tuple->t_self = *ctid;
  
  	/* Clean up */
  	MemoryContextReset(temp_context);
--- 2947,2953 ----
  	tuple = heap_form_tuple(tupdesc, values, nulls);
  
  	if (ctid)
! 		tuple->t_self = tuple->t_data->t_ctid = *ctid;
  
  	/* Clean up */
  	MemoryContextReset(temp_context);
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 795,800 **** InitPlan(QueryDesc *queryDesc, int eflags)
--- 795,801 ----
  	{
  		PlanRowMark *rc = (PlanRowMark *) lfirst(l);
  		Oid			relid;
+ 		char		relkind;
  		Relation	relation;
  		ExecRowMark *erm;
  
***************
*** 817,826 **** InitPlan(QueryDesc *queryDesc, int eflags)
--- 818,833 ----
  				break;
  			case ROW_MARK_COPY:
  				/* there's no real table here ... */
+ 				relkind = rt_fetch(rc->rti, rangeTable)->relkind;
+ 				if (relkind == RELKIND_FOREIGN_TABLE)
+ 					relid = getrelid(rc->rti, rangeTable);
+ 				else
+ 					relid = InvalidOid;
  				relation = NULL;
  				break;
  			default:
  				elog(ERROR, "unrecognized markType: %d", rc->markType);
+ 				relid = InvalidOid;
  				relation = NULL;	/* keep compiler quiet */
  				break;
  		}
***************
*** 831,836 **** InitPlan(QueryDesc *queryDesc, int eflags)
--- 838,844 ----
  
  		erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
  		erm->relation = relation;
+ 		erm->relid = relid;
  		erm->rti = rc->rti;
  		erm->prti = rc->prti;
  		erm->rowmarkId = rc->rowmarkId;
***************
*** 2318,2325 **** EvalPlanQualFetchRowMarks(EPQState *epqstate)
  
  			/* build a temporary HeapTuple control structure */
  			tuple.t_len = HeapTupleHeaderGetDatumLength(td);
! 			ItemPointerSetInvalid(&(tuple.t_self));
! 			tuple.t_tableOid = InvalidOid;
  			tuple.t_data = td;
  
  			/* copy and store tuple */
--- 2326,2342 ----
  
  			/* build a temporary HeapTuple control structure */
  			tuple.t_len = HeapTupleHeaderGetDatumLength(td);
! 			/* if relid is valid, rel is a foreign table; set system columns */
! 			if (OidIsValid(erm->relid))
! 			{
! 				tuple.t_self = td->t_ctid;
! 				tuple.t_tableOid = erm->relid;
! 			}
! 			else
! 			{
! 				ItemPointerSetInvalid(&(tuple.t_self));
! 				tuple.t_tableOid = InvalidOid;
! 			}
  			tuple.t_data = td;
  
  			/* copy and store tuple */
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
***************
*** 22,27 ****
--- 22,28 ----
   */
  #include "postgres.h"
  
+ #include "access/htup_details.h"
  #include "executor/executor.h"
  #include "executor/nodeForeignscan.h"
  #include "foreign/fdwapi.h"
***************
*** 53,65 **** ForeignNext(ForeignScanState *node)
  	/*
  	 * If any system columns are requested, we have to force the tuple into
  	 * physical-tuple form to avoid "cannot extract system attribute from
! 	 * virtual tuple" errors later.  We also insert a valid value for
! 	 * tableoid, which is the only actually-useful system column.
  	 */
  	if (plan->fsSystemCol && !TupIsNull(slot))
  	{
  		HeapTuple	tup = ExecMaterializeSlot(slot);
  
  		tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
  	}
  
--- 54,69 ----
  	/*
  	 * If any system columns are requested, we have to force the tuple into
  	 * physical-tuple form to avoid "cannot extract system attribute from
! 	 * virtual tuple" errors later.  We also insert a valid value for TID
! 	 * and tableoid, which are the only actually-useful system columns.
  	 */
  	if (plan->fsSystemCol && !TupIsNull(slot))
  	{
  		HeapTuple	tup = ExecMaterializeSlot(slot);
  
+ 		/* We assume that t_ctid is initialized with its own TID */
+ 		tup->t_self = tup->t_data->t_ctid;
+ 
  		tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
  	}
  
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 425,430 **** typedef struct EState
--- 425,431 ----
  typedef struct ExecRowMark
  {
  	Relation	relation;		/* opened and suitably locked relation */
+ 	Oid			relid;			/* its relation Oid */
  	Index		rti;			/* its range table index */
  	Index		prti;			/* parent range table index, if child */
  	Index		rowmarkId;		/* unique identifier for resjunk columns */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to