writable FDWs / update targets confusion - Mailing list pgsql-hackers
From | Tomas Vondra |
---|---|
Subject | writable FDWs / update targets confusion |
Date | |
Msg-id | 8b848b463a71b7a905bc5ef18b95528e.squirrel@sq.gransy.com Whole thread Raw |
Responses |
Re: writable FDWs / update targets confusion
|
List | pgsql-hackers |
Hi, I'm working on adding write support to one of my FDWs. Adding INSERT went pretty fine, but when adding DELETE/UPDATE I got really confused about how the update targets are supposed to work. My understanding of how it's supposed to work is this: (1) AddForeignUpdateTargets adds columns that serve as ID of the record (e.g. postgres_fdw adds 'ctid') (2) planning the inner foreign scan handles the new column appropriately (e.g. scans system columns, as in case of 'ctid'etc.) (3) IterateForeignScan will see the column in the tuple descriptor, will set it just like any other column, etc. (4) ExecForeignDelete will fetch the new column and do something with it However no matter what I do, I can't get the steps (3) and (4) working this way. This is what I do in AddForeignUpdateTargets (pretty much a 1:1 copy from postgres_fdw, except that I'm using INT8OID instead of TIDOID): static void myAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) { Var *var; const char *attrname; TargetEntry *tle; /* Make a Var representing the desired value */ var = makeVar(parsetree->resultRelation, SelfItemPointerAttributeNumber, INT8OID, -1, InvalidOid, 0); /* Wrap it in a resjunk TLE with the right name ... */ attrname = "ctid"; tle = makeTargetEntry((Expr *) var, list_length(parsetree->targetList) + 1, pstrdup(attrname), true); /* ... and add it to the query's targetlist */ parsetree->targetList = lappend(parsetree->targetList, tle); } Then in GetForeignPlan I collect all the attnums (including the new one), and in BeginForeignScan decide which columns to actually fetch. So if any attnum is (attnum < 0) I know I need to fetch the new 'ctid' column. However in IterateForeignScan, the tuple descriptor does not change. It still has only the columns of the foreign table, so I have no place to set the ctid to. So even though ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid"); return "1" I can't really set any of the column to the CTID, because the column might be used in a WHERE condition. And looking at postgres_fdw it seems to me it does not really set the ctid into the tuple as a column, but just does this: if (ctid) tuple->t_self = *ctid; which I can't really do because I need to use INT8 and not TID. But even if I do this, Interestingly, if I do this in ExecForeignDelete static TupleTableSlot * myExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot) { bool isNull; MyModifyState state = (MyModifyState)resultRelInfo->ri_FdwState; int64 ctid; Datum datum = ExecGetJunkAttribute(planSlot, state->ctidAttno, &isNull); ctid = DatumGetInt64(datum); elog(WARNING, "ID = %ld", ctid); if (isNull) elog(ERROR, "ctid is NULL"); /* FIXME not yet implemented */ return NULL; } I do get (isNull=FALSE) but the ctid evaluates to some random number, e.g. WARNING: ID = 44384788 (44384788) WARNING: ID = 44392980 (44392980) and so on. So what did I get wrong? Is it possible to use arbitrary hidden column as "junk" columns (documentation seems to suggest that)? What is the right way to do that / whad did I get wrong? regards Tomas
pgsql-hackers by date: