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