On Sat, Mar 9, 2024 at 3:53 AM Dean Rasheed <dean.a.rash...@gmail.com> wrote: > > > Attached is a new patch, now with docs (no other code changes). >
Hi, some issues I found, while playing around with support-returning-old-new-v2.patch doc/src/sgml/ref/update.sgml: [ RETURNING [ WITH ( { OLD | NEW } AS <replaceable class="parameter">output_alias</replaceable> [, ...] ) ] * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ] </synopsis> There is no parameter explanation for `*`. so, I think the synopsis may not cover cases like: ` update foo set f3 = 443 RETURNING new.*; ` I saw the explanation at output_alias, though. ----------------------------------------------------------------------------- insert into foo select 1, 2 RETURNING old.*, new.f2, old.f1(); ERROR: function old.f1() does not exist LINE 1: ...sert into foo select 1, 2 RETURNING old.*, new.f2, old.f1(); ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. I guess that's ok, slightly different context evaluation. if you say "old.f1", old refers to the virtual table "old", but "old.f1()", the "old" , reevaluate to the schema "old". you need privilege to schema "old", you also need execution privilege to function "old.f1()" to execute the above query. so seems no security issue after all. ----------------------------------------------------------------------------- I found a fancy expression: ` CREATE TABLE foo (f1 serial, f2 text, f3 int default 42); insert into foo select 1, 2 union select 11, 22 RETURNING old.*, new.f2, (select sum(new.f1) over()); ` is this ok? also the following works on PG16, not sure it's a bug. ` insert into foo select 1, 2 union select 11, 22 RETURNING (select count(*)); ` but not these ` insert into foo select 1, 2 union select 11, 22 RETURNING (select count(old.*)); insert into foo select 1, 2 union select 11, 22 RETURNING (select sum(f1)); ` ----------------------------------------------------------------------------- I found another interesting case, while trying to add some tests on for new code in createplan.c. in postgres_fdw.sql, right after line `MERGE ought to fail cleanly` --this will work insert into itrtest select 1, 'foo' returning new.*,old.*; --these two will fail insert into remp1 select 1, 'foo' returning new.*; insert into remp1 select 1, 'foo' returning old.*; itrtest is the partitioned non-foreign table. remp1 is the partition of itrtest, foreign table. ------------------------------------------------------------------------------------------ I did find a segment fault bug. insert into foo select 1, 2 RETURNING (select sum(old.f1) over()); This information is set in a subplan node. /* update the ExprState's flags if Var refers to OLD/NEW */ if (variable->varreturningtype == VAR_RETURNING_OLD) state->flags |= EEO_FLAG_HAS_OLD; else if (variable->varreturningtype == VAR_RETURNING_NEW) state->flags |= EEO_FLAG_HAS_NEW; but in ExecInsert: ` else if (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) { oldSlot = ExecGetReturningSlot(estate, resultRelInfo); ExecStoreAllNullTuple(oldSlot); oldSlot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); } ` it didn't use subplan node state->flags information. so the ExecInsert above code, never called, and should be executed. however ` insert into foo select 1, 2 RETURNING (select sum(new.f1)over());` works Similarly this ` delete from foo RETURNING (select sum(new.f1) over()); ` also causes segmentation fault. ------------------------------------------------------------------------------------------ diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h new file mode 100644 index 6133dbc..c9d3661 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -411,12 +411,21 @@ slot_getsysattr(TupleTableSlot *slot, in { Assert(attnum < 0); /* caller error */ + /* + * If the tid is not valid, there is no physical row, and all system + * attributes are deemed to be NULL, except for the tableoid. + */ if (attnum == TableOidAttributeNumber) { *isnull = false; return ObjectIdGetDatum(slot->tts_tableOid); } - else if (attnum == SelfItemPointerAttributeNumber) + if (!ItemPointerIsValid(&slot->tts_tid)) + { + *isnull = true; + return PointerGetDatum(NULL); + } + if (attnum == SelfItemPointerAttributeNumber) { *isnull = false; return PointerGetDatum(&slot->tts_tid); These changes is slot_getsysattr is somehow independ of this feature?