Thread: BUG #15556: Duplicate key violations even when using ON CONFLICT DOUPDATE

BUG #15556: Duplicate key violations even when using ON CONFLICT DOUPDATE

From
PG Bug reporting form
Date:
The following bug has been logged on the website:

Bug reference:      15556
Logged by:          Feike Steenbergen
Email address:      feikesteenbergen@gmail.com
PostgreSQL version: 11.1
Operating system:   CentOS Linux release 7.5.1804 (Core)
Description:

On a table that has a primary key and multiple unique indexes, we have
recently
seen a duplicate key violation, even though we use ON CONFLICT DO UPDATE.

The values for the separate columns are dependent on each other, therefore
a
unique violation on one of the columns should also violate the other
indexes.

Below a self reproducing testcase for this behaviour.

I would have expected the statement to either insert or update, but
receiving
the duplicate key violation is not something that I expected.

Should I expect a duplicate key violation for the given table and
statements?

regards,

Feike


Reproducing testcase



#!/bin/bash
# within 1 second, I have multiple of these errors:
# ERROR:  duplicate key value violates unique constraint "bugtest_pkey"
# DETAIL:  Key (i)=(76) already exists.

PARALLEL=10
TIMEOUT=1

TMPFILE=$(mktemp)
trap 'rm -f ${TMPFILE}' EXIT QUIT INT TERM

psql -AtXq <<__EOT__
CREATE OR REPLACE FUNCTION slow_integer(i integer)
RETURNS integer LANGUAGE sql
AS 'SELECT i FROM pg_sleep(random()/1000000);'
IMMUTABLE;

DROP TABLE IF EXISTS bugtest;
CREATE TABLE bugtest(
    i int primary key,
    j int not null,
    k int not null,
    counter bigint not null default 1
);
CREATE UNIQUE INDEX ON bugtest (slow_integer(j));
CREATE UNIQUE INDEX ON bugtest (slow_integer(k));
__EOT__


cat > "${TMPFILE}" <<__EOT__
INSERT INTO bugtest(i, j, k)
SELECT i, i, i
  FROM CAST(random()*100 AS int) AS sub(i)
ON CONFLICT (slow_integer(k))
DO UPDATE
SET counter=bugtest.counter+1;

\watch 0.0000001
__EOT__

seq ${PARALLEL} | xargs --max-args=1 --max-procs=${PARALLEL} \
    timeout ${TIMEOUT} psql -AtXq -f "${TMPFILE}" --set


Re: BUG #15556: Duplicate key violations even when using ON CONFLICTDO UPDATE

From
Peter Geoghegan
Date:
On Mon, Dec 17, 2018 at 1:55 AM PG Bug reporting form
<noreply@postgresql.org> wrote:
> Below a self reproducing testcase for this behaviour.
>
> I would have expected the statement to either insert or update, but
> receiving
> the duplicate key violation is not something that I expected.
>
> Should I expect a duplicate key violation for the given table and
> statements?

The problem is that unique index inference isn't sophisticated enough
to recognize that the primary key ought to be inferred alongside the
two other unique indexes, which are expression indexes. This is hardly
surprising -- why would an expression index need to be created that
was exactly equivalent to the primary key? Actually, it's impossible
to make this work in principle, because nothing obligates you to
insert the same thing into column i as column j or k -- you've merely
done it that way this one time.

Inference is clever enough to not differentiate based on irrelevant
factors like column ordering among available, equivalent unique
indexes. It's already very sophisticated for a mechanism that's just
there to handle edge cases. I don't see a need for it to become more
sophisticated.

-- 
Peter Geoghegan


Re: BUG #15556: Duplicate key violations even when using ON CONFLICTDO UPDATE

From
Peter Geoghegan
Date:
On Mon, Dec 17, 2018 at 11:08 AM Peter Geoghegan <pg@bowt.ie> wrote:
> The problem is that unique index inference isn't sophisticated enough
> to recognize that the primary key ought to be inferred alongside the
> two other unique indexes, which are expression indexes. This is hardly
> surprising -- why would an expression index need to be created that
> was exactly equivalent to the primary key?

By the way, EXPLAIN ANALYZE INSERT ... ON CONFLICT will actually show
you which unique indexes/constraints have been inferred from the
target columns/expressions that appear in parenthesis. If two unique
indexes use different columns, or are otherwise equivalent based on
convention rather than on the semantics, then they're definitely not
going to be recognized as equivalent by the inference process.

You're not supposed to be able to UPDATE on a conflict on more than
one unique index, really. The general idea with inference is to avoid
unpleasant surprises when there are two indexes that enforce basically
the same constraint, such as when a bloated unique index is replaced
by creating a new index with CREATE UNIQUE INDEX CONCURRENTLY, before
the original is dropped. That's what I meant about this being an edge
case -- this hardly ever happens. So, yes, you can have multiple
unique indexes inferred, but it doesn't matter which one you take the
alternative UPDATE path on, because the rules of inference ensure that
it cannot matter. We can inferred multiple indexes precisely because
they'll all have the same conflicts.

I got asked about multiple inference specifications in one statement
quite a few times back when ON CONFLICT originally went in. That's not
how it's supposed to be used -- what happens when *both* constraints
are violated at once, in different ways? Just use multiple INSERT ...
ON CONFLICT statements instead.

-- 
Peter Geoghegan