On Sat, Nov 29, 2025 at 6:02 AM Viktor Holmberg <[email protected]> wrote:
>
> Attaching v18 with the above changes. Thanks your continued reviews Jian!
>

hi.
I had some minor comments with doc, comments.

+   expressions or <replaceable>condition</replaceable>. If using a
+   <literal>WHERE</literal> with <literal>ON CONFLICT DO UPDATE /
SELECT </literal>,
+   you must have <literal>SELECT</literal> privilege on the columns referenced
+   in the <literal>WHERE</literal> clause.
the extra white space is not necessary, it should be:
<literal>ON CONFLICT DO UPDATE / SELECT</literal>


-   list of <command>SELECT</command>.  Only rows that were successfully
+   list of <command>SELECT</command>.  If an <literal>ON CONFLICT DO
SELECT</literal>
+   clause is not present, only rows that were successfully
    inserted or updated will be returned.  For example, if a row was
    locked but not updated because an <literal>ON CONFLICT DO UPDATE
    ... WHERE</literal> clause <replaceable
    class="parameter">condition</replaceable> was not satisfied, the
-   row will not be returned.
+   row will not be returned.  <literal>ON CONFLICT DO SELECT</literal>
+   works similarly, except no update takes place.
I am not sure "except no update takes place." is appropriate here.


-   rows.  Similarly, if a <command>DELETE</command> is turned into an
+   rows.  Similarly, in an <command>INSERT</command> with an
+   <literal>ON CONFLICT DO SELECT</literal> clause, you can look at
the old values to determine if your query inserted a row or not. If a
<command>DELETE</command> is turned into an
    <command>UPDATE</command> by a <link
linkend="sql-createrule">rewrite rule</link>,
    the new values may be non-<literal>NULL</literal>.
182 characters, line too long, we can wrap it into several lines.

src/backend/executor/execIndexing.c top have bunch of comments for
INSERT ... ON CONFLICT DO UPDATE/NOTHING
maybe we also need to write something for INSERT ON CONFLICT DO SELECT too.


src/backend/optimizer/plan/setrefs.c, fix_join_expr comments:
 * 3) ON CONFLICT UPDATE SET/WHERE clauses.  Here references to EXCLUDED are
 *      to be replaced with INNER_VAR references, while leaving target Vars (the
 *      to-be-updated relation) alone. Correspondingly inner_itlist is to be
 *      EXCLUDED elements, outer_itlist = NULL and acceptable_rel the target
 *      relation.
This applies to INSERT ON CONFLICT DO SELECT.
comments need slightly adjusted?


src/test/regress/sql/triggers.sql:
--
-- Verify behavior of before and after triggers with INSERT...ON CONFLICT
-- DO UPDATE
--
the above comments need change.
we can also add a new tests like:
``insert into upsert values(9, 'orange') on conflict (key) do select
for update returning old.*, new.*, upsert.*;``
then we can see how triggers behave with or without conflict.


https://git.postgresql.org/cgit/postgresql.git/commit/?id=2bc7e886fc1baaeee3987a141bff3ac490037d12
so we also need change infer_arbiter_indexes accordingly.
seems we only need adjust:
```
        else if (indexOidFromConstraint != InvalidOid)
        {
            /*
             * In the case of "ON constraint_name DO UPDATE" we need to skip
             * non-unique candidates.
             */
            if (!idxForm->indisunique && onconflict->action ==
ONCONFLICT_UPDATE)
                continue;
        }
```

src/test/regress/sql/updatable_views.sql tests line too long too, we can make it
into separate lines.


+insert into parted_conflict_test (a, b) values (1, 'x'), (2, 'y'),
(4, 'z') on conflict (a) do select returning *;
+
we can change it to
+insert into parted_conflict_test (a, b) values (1, 'x'), (2, 'y'), (4, 'z')
+  on conflict (a) do select returning *, tableoid::regclass;
then we can see that tableoid system is computed correctly in
ExecOnConflictSelect.


+ if (lockStrength == LCS_NONE)
+ {
+ if (!table_tuple_fetch_row_version(relation, conflictTid,
SnapshotAny, existing))
+ /* The pre-existing tuple was deleted */
+ return false;
+ }
i think this part should be
```
    if (!table_tuple_fetch_row_version(rel, conflictTid, SnapshotAny, existing))
        elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
```

say we have a conflict for values (1)
``insert into tsa values (1,3) on conflict(a) do select returning *;``

set a GDB breakpoint at ExecOnConflictSelect, let another process do
``delete from tsa; vacuum tsa;``
then let GDB continue.

table_tuple_fetch_row_version can still fetch the tuple.
so I think this is an unlikely scenario.



--
jian
https://www.enterprisedb.com


Reply via email to