Buffer vmbuffer = InvalidBuffer;
bool all_visible_cleared = false;
+ /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
+ Assert(HeapTupleHeaderGetNatts(tup->t_data) <=
+ RelationGetNumberOfAttributes(relation));
+
/*
* Fill in tuple header fields, assign an OID, and toast the tuple if
* necessary.
Assert(ItemPointerIsValid(otid));
+ /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
+ Assert(HeapTupleHeaderGetNatts(newtup->t_data) <=
+ RelationGetNumberOfAttributes(relation));
+
/*
* Forbid this during a parallel operation, lest it allocate a combocid.
* Other workers might need that combocid for visibility checks, and we
/* Project the new tuple version */
ExecProject(resultRelInfo->ri_onConflictSetProj, NULL);
+ if (mtstate->mt_confljunk)
+ (void) ExecFilterJunk(mtstate->mt_confljunk,
+ resultRelInfo->ri_onConflictSetProj->pi_slot);
+
/*
* Note that it is possible that the target tuple has been modified in
* this session, after the above heap_lock_tuple. We choose to not error
{
ExprContext *econtext;
ExprState *setexpr;
- TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
Assert(nplans == 1);
mtstate->mt_excludedtlist = node->exclRelTlist;
/* create target slot for UPDATE SET projection */
- tupDesc = ExecTypeFromTL((List *) node->onConflictSet,
- resultRelInfo->ri_RelationDesc->rd_rel->relhasoids);
mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
- ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
+ ExecSetSlotDescriptor(mtstate->mt_conflproj,
+ resultRelInfo->ri_RelationDesc->rd_att);
- /* build UPDATE SET expression and projection state */
+ /* initialize UPDATE SET tlist expressions */
setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
- resultRelInfo->ri_onConflictSetProj =
- ExecBuildProjectionInfo((List *) setexpr, econtext,
- mtstate->mt_conflproj,
- resultRelInfo->ri_RelationDesc->rd_att);
+
+ /*
+ * The onConflictSet tlist should already have been adjusted to emit
+ * the table's exact column list.
+ */
+ ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
+ node->onConflictSet);
+
+ /*
+ * However, it might also contain resjunk columns, in which case we'll
+ * need a junkfilter to get rid of those.
+ */
+ if (ExecCleanTargetListLength(node->onConflictSet) ==
+ list_length(node->onConflictSet))
+ {
+ /* No junk columns, so we'll just project into mt_conflproj. */
+ resultRelInfo->ri_onConflictSetProj =
+ ExecBuildProjectionInfo((List *) setexpr, econtext,
+ mtstate->mt_conflproj,
+ resultRelInfo->ri_RelationDesc->rd_att);
+ mtstate->mt_confljunk = NULL;
+ }
+ else
+ {
+ /*
+ * Project into a slot matching the tlist's output rowtype, then
+ * apply a junkfilter.
+ */
+ TupleDesc tupDesc = ExecTypeFromTL(node->onConflictSet, false);
+ TupleTableSlot *ocsSlot;
+
+ ocsSlot = ExecInitExtraTupleSlot(mtstate->ps.state);
+ ExecSetSlotDescriptor(ocsSlot, tupDesc);
+ resultRelInfo->ri_onConflictSetProj =
+ ExecBuildProjectionInfo((List *) setexpr, econtext,
+ ocsSlot,
+ resultRelInfo->ri_RelationDesc->rd_att);
+ mtstate->mt_confljunk =
+ ExecInitJunkFilter(node->onConflictSet,
+ resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
+ mtstate->mt_conflproj);
+ }
/* build DO UPDATE WHERE clause expression */
if (node->onConflictWhere)
* tlist */
TupleTableSlot *mt_conflproj; /* CONFLICT ... SET ... projection
* target */
+ JunkFilter *mt_confljunk; /* CONFLICT ... SET might need a junkfilter */
} ModifyTableState;
/* ----------------
(4 rows)
-- Test ON CONFLICT DO UPDATE
-INSERT INTO upsert_test VALUES(1, 'Boo');
+INSERT INTO upsert_test VALUES(1, 'Boo'), (3, 'Zoo');
-- uncorrelated sub-select:
WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test
VALUES (1, 'Bar') ON CONFLICT(a)
(1 row)
-- correlated sub-select:
-INSERT INTO upsert_test VALUES (1, 'Baz') ON CONFLICT(a)
+INSERT INTO upsert_test VALUES (1, 'Baz'), (3, 'Zaz') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Correlated', a from upsert_test i WHERE i.a = upsert_test.a)
RETURNING *;
a | b
---+-----------------
1 | Foo, Correlated
-(1 row)
+ 3 | Zoo, Correlated
+(2 rows)
-- correlated sub-select (EXCLUDED.* alias):
-INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
+INSERT INTO upsert_test VALUES (1, 'Bat'), (3, 'Zot') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING *;
a | b
---+---------------------------
1 | Foo, Correlated, Excluded
-(1 row)
+ 3 | Zoo, Correlated, Excluded
+(2 rows)
DROP TABLE update_test;
DROP TABLE upsert_test;
SELECT a, b, char_length(c) FROM update_test;
-- Test ON CONFLICT DO UPDATE
-INSERT INTO upsert_test VALUES(1, 'Boo');
+INSERT INTO upsert_test VALUES(1, 'Boo'), (3, 'Zoo');
-- uncorrelated sub-select:
WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test
VALUES (1, 'Bar') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *;
-- correlated sub-select:
-INSERT INTO upsert_test VALUES (1, 'Baz') ON CONFLICT(a)
+INSERT INTO upsert_test VALUES (1, 'Baz'), (3, 'Zaz') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Correlated', a from upsert_test i WHERE i.a = upsert_test.a)
RETURNING *;
-- correlated sub-select (EXCLUDED.* alias):
-INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
+INSERT INTO upsert_test VALUES (1, 'Bat'), (3, 'Zot') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING *;