Bug in ExecModifyTable function and trigger issues for foreign tables

Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru>

From: Ildus Kurbangaliev <i.kurbangaliev@postgrespro.ru>
To: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Date: 2017-05-14T12:05:25Z
Lists: pgsql-hackers

Attachments

Hello hackers,
i was experimenting with fdw tables recently, and discovered two bugs
in postgres core code (tested on stable 9.6 and master).

Steps to reproduce:

1) create parent table
2) create child local table
3) create child foreign table
4) create 'before row update` trigger at foreign table
5) make update query on parent table.

I attached sql file with these steps. At the end postgres will show an
error like:

ERROR:  could not open file "base/12410/33037": No such file or
directory

33037 is relid of the foreign table.  Bug is related with the fact that
postgres will try use latest scanned tupleid from local table to try
get an old tuple for trigger of foreign table.

It should be fixed with the patch like:

--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1324,7 +1324,6 @@ ExecModifyTable(ModifyTableState *node)
        JunkFilter *junkfilter;
        TupleTableSlot *slot;
        TupleTableSlot *planSlot;
-       ItemPointer tupleid = NULL;
        ItemPointerData tuple_ctid;
        HeapTupleData oldtupdata;
        HeapTuple       oldtuple;
@@ -1381,6 +1380,8 @@ ExecModifyTable(ModifyTableState *node)
         */
        for (;;)
        {
+               ItemPointer tupleid = NULL;
+

After this patch the second bug will appear:

TRAP: FailedAssertion("!(((const void*)(fdw_trigtuple) != ((void *)0))
^ ((bool) (((const void*)(tupleid) != ((void *)0)) &&
((tupleid)->ip_posid != 0))))", File: "trigger.c", Line: 2428)

I'm not sure how it should be fixed, because as I see `oldtuple` will
be set only for AFTER ROW triggers by `wholerow` junk attribute.

Regards,
Ildus Kurbangaliev

Commits

  1. Fix creation of resjunk tlist entries for inherited mixed UPDATE/DELETE.