postgresql/src/test/regress/expected/insert_conflict.out

477 lines
25 KiB
Plaintext
Raw Normal View History

Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE. The newly added ON CONFLICT clause allows to specify an alternative to raising a unique or exclusion constraint violation error when inserting. ON CONFLICT refers to constraints that can either be specified using a inference clause (by specifying the columns of a unique constraint) or by naming a unique or exclusion constraint. DO NOTHING avoids the constraint violation, without touching the pre-existing row. DO UPDATE SET ... [WHERE ...] updates the pre-existing tuple, and has access to both the tuple proposed for insertion and the existing tuple; the optional WHERE clause can be used to prevent an update from being executed. The UPDATE SET and WHERE clauses have access to the tuple proposed for insertion using the "magic" EXCLUDED alias, and to the pre-existing tuple using the table name or its alias. This feature is often referred to as upsert. This is implemented using a new infrastructure called "speculative insertion". It is an optimistic variant of regular insertion that first does a pre-check for existing tuples and then attempts an insert. If a violating tuple was inserted concurrently, the speculatively inserted tuple is deleted and a new attempt is made. If the pre-check finds a matching tuple the alternative DO NOTHING or DO UPDATE action is taken. If the insertion succeeds without detecting a conflict, the tuple is deemed inserted. To handle the possible ambiguity between the excluded alias and a table named excluded, and for convenience with long relation names, INSERT INTO now can alias its target table. Bumps catversion as stored rules change. Author: Peter Geoghegan, with significant contributions from Heikki Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes. Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs, Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
--
-- insert...on conflict do unique index inference
--
create table insertconflicttest(key int4, fruit text);
--
-- Test unique index inference with operator class specifications and
-- named collations
--
create unique index op_index_key on insertconflicttest(key, fruit text_pattern_ops);
create unique index collation_index_key on insertconflicttest(key, fruit collate "C");
create unique index both_index_key on insertconflicttest(key, fruit collate "C" text_pattern_ops);
create unique index both_index_expr_key on insertconflicttest(key, lower(fruit) collate "C" text_pattern_ops);
-- fails
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) do nothing;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit) do nothing;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
-- succeeds
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do nothing;
QUERY PLAN
-------------------------------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key
-> Result
(4 rows)
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit, key) do nothing;
QUERY PLAN
-------------------------------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key
-> Result
(4 rows)
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing;
QUERY PLAN
-------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: both_index_expr_key
-> Result
(4 rows)
-- Neither collation nor operator class specifications are required --
-- supplying them merely *limits* matches to indexes with matching opclasses
-- used for relevant indexes
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit text_pattern_ops) do nothing;
QUERY PLAN
----------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: op_index_key, both_index_key
-> Result
(4 rows)
-- Okay, arbitrates using both index where text_pattern_ops opclass does and
-- does not appear.
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit collate "C") do nothing;
QUERY PLAN
-----------------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: collation_index_key, both_index_key
-> Result
(4 rows)
-- Okay, but only accepts the single index where both opclass and collation are
-- specified
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit collate "C" text_pattern_ops, key) do nothing;
QUERY PLAN
--------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: both_index_key
-> Result
(4 rows)
-- Okay, but only accepts the single index where both opclass and collation are
-- specified (plus expression variant)
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) collate "C", key, key) do nothing;
QUERY PLAN
-------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: both_index_expr_key
-> Result
(4 rows)
-- Attribute appears twice, while not all attributes/expressions on attributes
-- appearing within index definition match in terms of both opclass and
-- collation.
--
-- Works because every attribute in inference specification needs to be
-- satisfied once or more by cataloged index attribute, and as always when an
-- attribute in the cataloged definition has a non-default opclass/collation,
-- it still satisfied some inference attribute lacking any particular
-- opclass/collation specification.
--
-- The implementation is liberal in accepting inference specifications on the
-- assumption that multiple inferred unique indexes will prevent problematic
-- cases. It rolls with unique indexes where attributes redundantly appear
-- multiple times, too (which is not tested here).
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit text_pattern_ops, key) do nothing;
QUERY PLAN
----------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: op_index_key, both_index_key
-> Result
(4 rows)
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) collate "C" text_pattern_ops, key, key) do nothing;
QUERY PLAN
-------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: both_index_expr_key
-> Result
(4 rows)
drop index op_index_key;
drop index collation_index_key;
drop index both_index_key;
drop index both_index_expr_key;
--
-- Single key tests
--
create unique index key_index on insertconflicttest(key);
--
-- Explain tests
--
explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit;
QUERY PLAN
---------------------------------------
Insert on insertconflicttest
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: key_index
-> Result
(4 rows)
-- Should display qual actually attributable to internal sequential scan:
explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Cawesh';
QUERY PLAN
-----------------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: key_index
Conflict Filter: (insertconflicttest.fruit <> 'Cawesh'::text)
-> Result
(5 rows)
-- With EXCLUDED.* expression in scan node:
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) do update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
QUERY PLAN
-----------------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: key_index
Conflict Filter: (excluded.fruit <> 'Elderberry'::text)
-> Result
(5 rows)
-- Does the same, but JSON format shows "Conflict Arbiter Index" as JSON array:
explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Lime' returning *;
QUERY PLAN
------------------------------------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "ModifyTable", +
"Operation": "Insert", +
"Relation Name": "insertconflicttest", +
"Alias": "insertconflicttest", +
"Conflict Resolution": "UPDATE", +
"Conflict Arbiter Indexes": ["key_index"], +
"Conflict Filter": "(insertconflicttest.fruit <> 'Lime'::text)",+
"Plans": [ +
{ +
"Node Type": "Result", +
"Parent Relationship": "Member" +
} +
] +
} +
} +
]
(1 row)
-- Fails (no unique index inference specification, required for do update variant):
insert into insertconflicttest values (1, 'Apple') on conflict do update set fruit = excluded.fruit;
ERROR: ON CONFLICT DO UPDATE requires inference specification or constraint name
LINE 1: ...nsert into insertconflicttest values (1, 'Apple') on conflic...
^
HINT: For example, ON CONFLICT ON CONFLICT (<column>).
-- inference succeeds:
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit;
insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) do update set fruit = excluded.fruit;
-- Succeed, since multi-assignment does not involve subquery:
insert into insertconflicttest
values (1, 'Apple'), (2, 'Orange')
on conflict (key) do update set (fruit, key) = (excluded.fruit, excluded.key);
-- Give good diagnostic message when EXCLUDED.* spuriously referenced from
-- RETURNING:
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit RETURNING excluded.fruit;
ERROR: invalid reference to FROM-clause entry for table "excluded"
LINE 1: ...y) do update set fruit = excluded.fruit RETURNING excluded.f...
^
HINT: There is an entry for table "excluded", but it cannot be referenced from this part of the query.
-- Only suggest <table>.* column when inference element misspelled:
insert into insertconflicttest values (1, 'Apple') on conflict (keyy) do update set fruit = excluded.fruit;
ERROR: column "keyy" does not exist
LINE 1: ...nsertconflicttest values (1, 'Apple') on conflict (keyy) do ...
^
HINT: Perhaps you meant to reference the column "insertconflicttest"."key".
-- Have useful HINT for EXCLUDED.* RTE within UPDATE:
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruitt;
ERROR: column excluded.fruitt does not exist
LINE 1: ... 'Apple') on conflict (key) do update set fruit = excluded.f...
^
HINT: Perhaps you meant to reference the column "excluded"."fruit".
-- inference fails:
insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
-- Check the target relation can be aliased
insert into insertconflicttest values (6, 'Passionfruits') on conflict (key) do update set fruit = excluded.fruit;
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = excluded.fruit; -- ok, no reference to target table
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = ict.fruit; -- ok, alias
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = insertconflicttest.fruit; -- error, references aliased away name
ERROR: invalid reference to FROM-clause entry for table "insertconflicttest"
LINE 1: ...onfruit') on conflict (key) do update set fruit = insertconf...
^
HINT: Perhaps you meant to reference the table alias "ict".
drop index key_index;
--
-- Composite key tests
--
create unique index comp_key_index on insertconflicttest(key, fruit);
-- inference succeeds:
insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) do update set fruit = excluded.fruit;
insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) do update set fruit = excluded.fruit;
-- inference fails:
insert into insertconflicttest values (9, 'Banana') on conflict (key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
drop index comp_key_index;
--
-- Partial index tests, no inference predicate specificied
--
create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5;
-- inference fails:
insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (15, 'Cranberry') on conflict (key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
drop index part_comp_key_index;
drop index expr_part_comp_key_index;
--
-- Expression index tests
--
create unique index expr_key_index on insertconflicttest(lower(fruit));
-- inference succeeds:
insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) do update set fruit = excluded.fruit;
insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) do update set fruit = excluded.fruit;
-- inference fails:
insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
drop index expr_key_index;
--
-- Expression index tests (with regular column)
--
create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit));
create unique index tricky_expr_comp_key_index on insertconflicttest(key, lower(fruit), upper(fruit));
-- inference succeeds:
insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit;
insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) do update set fruit = excluded.fruit;
-- Should not infer "tricky_expr_comp_key_index" index:
explain (costs off) insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) do update set fruit = excluded.fruit;
QUERY PLAN
-------------------------------------------------
Insert on insertconflicttest
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: expr_comp_key_index
-> Result
(4 rows)
-- inference fails:
insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (29, 'Nectarine') on conflict (key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
drop index expr_comp_key_index;
drop index tricky_expr_comp_key_index;
--
-- Non-spurious duplicate violation tests
--
create unique index key_index on insertconflicttest(key);
create unique index fruit_index on insertconflicttest(fruit);
-- succeeds, since UPDATE happens to update "fruit" to existing value:
insert into insertconflicttest values (26, 'Fig') on conflict (key) do update set fruit = excluded.fruit;
-- fails, since UPDATE is to row with key value 26, and we're updating "fruit"
-- to a value that happens to exist in another row ('peach'):
insert into insertconflicttest values (26, 'Peach') on conflict (key) do update set fruit = excluded.fruit;
ERROR: duplicate key value violates unique constraint "fruit_index"
DETAIL: Key (fruit)=(Peach) already exists.
-- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit"
-- arbitrates that statement updates existing "Fig" row:
insert into insertconflicttest values (25, 'Fig') on conflict (fruit) do update set fruit = excluded.fruit;
drop index key_index;
drop index fruit_index;
--
-- Test partial unique index inference
--
create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
-- Succeeds
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' do update set fruit = excluded.fruit;
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and fruit = 'inconsequential' do nothing;
-- fails
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' or fruit = 'consequential' do nothing;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) where fruit like '%berry' do update set fruit = excluded.fruit;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
drop index partial_key_index;
-- Cleanup
drop table insertconflicttest;
-- ******************************************************************
-- * *
-- * Test inheritance (example taken from tutorial) *
-- * *
-- ******************************************************************
create table cities (
name text,
population float8,
altitude int -- (in ft)
);
create table capitals (
state char(2)
) inherits (cities);
-- Create unique indexes. Due to a general limitation of inheritance,
-- uniqueness is only enforced per-relation. Unique index inference
-- specification will do the right thing, though.
create unique index cities_names_unique on cities (name);
create unique index capitals_names_unique on capitals (name);
-- prepopulate the tables.
insert into cities values ('San Francisco', 7.24E+5, 63);
insert into cities values ('Las Vegas', 2.583E+5, 2174);
insert into cities values ('Mariposa', 1200, 1953);
insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
-- Tests proper for inheritance:
select * from capitals;
name | population | altitude | state
------------+------------+----------+-------
Sacramento | 369400 | 30 | CA
Madison | 191300 | 845 | WI
(2 rows)
-- Succeeds:
insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict do nothing;
insert into capitals values ('Sacramento', 4664.E+5, 30, 'CA') on conflict (name) do update set population = excluded.population;
-- Wrong "Sacramento", so do nothing:
insert into capitals values ('Sacramento', 50, 2267, 'NE') on conflict (name) do nothing;
select * from capitals;
name | population | altitude | state
------------+------------+----------+-------
Madison | 191300 | 845 | WI
Sacramento | 466400000 | 30 | CA
(2 rows)
insert into cities values ('Las Vegas', 5.83E+5, 2001) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude;
select tableoid::regclass, * from cities;
tableoid | name | population | altitude
----------+---------------+------------+----------
cities | San Francisco | 724000 | 63
cities | Mariposa | 1200 | 1953
cities | Las Vegas | 583000 | 2001
capitals | Madison | 191300 | 845
capitals | Sacramento | 466400000 | 30
(5 rows)
insert into capitals values ('Las Vegas', 5.83E+5, 2222, 'NV') on conflict (name) do update set population = excluded.population;
-- Capitals will contain new capital, Las Vegas:
select * from capitals;
name | population | altitude | state
------------+------------+----------+-------
Madison | 191300 | 845 | WI
Sacramento | 466400000 | 30 | CA
Las Vegas | 583000 | 2222 | NV
(3 rows)
-- Cities contains two instances of "Las Vegas", since unique constraints don't
-- work across inheritance:
select tableoid::regclass, * from cities;
tableoid | name | population | altitude
----------+---------------+------------+----------
cities | San Francisco | 724000 | 63
cities | Mariposa | 1200 | 1953
cities | Las Vegas | 583000 | 2001
capitals | Madison | 191300 | 845
capitals | Sacramento | 466400000 | 30
capitals | Las Vegas | 583000 | 2222
(6 rows)
-- This only affects "cities" version of "Las Vegas":
insert into cities values ('Las Vegas', 5.86E+5, 2223) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude;
select tableoid::regclass, * from cities;
tableoid | name | population | altitude
----------+---------------+------------+----------
cities | San Francisco | 724000 | 63
cities | Mariposa | 1200 | 1953
cities | Las Vegas | 586000 | 2223
capitals | Madison | 191300 | 845
capitals | Sacramento | 466400000 | 30
capitals | Las Vegas | 583000 | 2222
(6 rows)
-- clean up
drop table capitals;
drop table cities;
-- Make sure a table named excluded is handled properly
create table excluded(key int primary key, data text);
insert into excluded values(1, '1');
-- error, ambiguous
insert into excluded values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *;
ERROR: table reference "excluded" is ambiguous
LINE 1: ...es(1, '2') on conflict (key) do update set data = excluded.d...
^
-- ok, aliased
insert into excluded AS target values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *;
key | data
-----+------
1 | 2
(1 row)
-- ok, aliased
insert into excluded AS target values(1, '2') on conflict (key) do update set data = target.data RETURNING *;
key | data
-----+------
1 | 2
(1 row)
-- make sure excluded isn't a problem in returning clause
insert into excluded values(1, '2') on conflict (key) do update set data = 3 RETURNING excluded.*;
key | data
-----+------
1 | 3
(1 row)
-- clean up
drop table excluded;