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,

        /* Wrap it in a resjunk TLE with the right name ... */
        attrname = "ctid";

        tle = makeTargetEntry((Expr *) var,
                              list_length(parsetree->targetList) + 1,

        /* ... 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?


Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:

Reply via email to