-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.
CREATE TABLE upsert_test(time timestamp PRIMARY KEY, temp float, color text);
SELECT create_hypertable('upsert_test', 'time');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
    create_hypertable     
--------------------------
 (1,public,upsert_test,t)

INSERT INTO upsert_test VALUES ('2017-01-20T09:00:01', 22.5, 'yellow') RETURNING *;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 22.5 | yellow

INSERT INTO upsert_test VALUES ('2017-01-20T09:00:01', 23.8, 'yellow') ON CONFLICT (time)
DO UPDATE SET temp = 23.8 RETURNING *;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 23.8 | yellow

INSERT INTO upsert_test VALUES ('2017-01-20T09:00:01', 78.4, 'yellow') ON CONFLICT DO NOTHING;
SELECT * FROM upsert_test;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 23.8 | yellow

-- Test 'Tuples Inserted' and 'Conflicting Tuples' values in EXPLAIN ANALYZE
EXPLAIN (VERBOSE, ANALYZE, BUFFERS FALSE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE)
  INSERT INTO upsert_test VALUES
    ('2017-01-20T09:00:01', 28.5, 'blue'),
    ('2017-01-20T09:00:01', 21.9, 'red'),
    ('2017-01-20T10:00:01', 2.4, 'pink') ON CONFLICT DO NOTHING;
--- QUERY PLAN ---
 Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1)
   ->  Insert on public.upsert_test (actual rows=0.00 loops=1)
         Conflict Resolution: NOTHING
         Tuples Inserted: 1
         Conflicting Tuples: 2
         ->  Values Scan on "*VALUES*" (actual rows=3.00 loops=1)
               Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3

-- Test ON CONFLICT ON CONSTRAINT
INSERT INTO upsert_test VALUES ('2017-01-20T09:00:01', 12.3, 'yellow') ON CONFLICT ON CONSTRAINT upsert_test_pkey
DO UPDATE SET temp = 12.3 RETURNING time, temp, color;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 12.3 | yellow

-- Test that update generates error on conflicts
\set ON_ERROR_STOP 0
INSERT INTO upsert_test VALUES ('2017-01-21T09:00:01', 22.5, 'yellow') RETURNING *;
           time           | temp | color  
--------------------------+------+--------
 Sat Jan 21 09:00:01 2017 | 22.5 | yellow

UPDATE upsert_test SET time = '2017-01-20T09:00:01';
ERROR:  duplicate key value violates unique constraint "1_1_upsert_test_pkey"
\set ON_ERROR_STOP 1
-- Test with UNIQUE index on multiple columns instead of PRIMARY KEY constraint
CREATE TABLE upsert_test_unique(time timestamp, temp float, color text);
SELECT create_hypertable('upsert_test_unique', 'time');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
        create_hypertable        
---------------------------------
 (2,public,upsert_test_unique,t)

CREATE UNIQUE INDEX time_color_idx ON upsert_test_unique (time, color);
INSERT INTO upsert_test_unique VALUES ('2017-01-20T09:00:01', 22.5, 'yellow') RETURNING *;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 22.5 | yellow

INSERT INTO upsert_test_unique VALUES ('2017-01-20T09:00:01', 21.2, 'brown');
SELECT * FROM upsert_test_unique ORDER BY time, color DESC;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 22.5 | yellow
 Fri Jan 20 09:00:01 2017 | 21.2 | brown

INSERT INTO upsert_test_unique VALUES ('2017-01-20T09:00:01', 31.8, 'yellow') ON CONFLICT (time, color)
DO UPDATE SET temp = 31.8;
INSERT INTO upsert_test_unique VALUES ('2017-01-20T09:00:01', 54.3, 'yellow') ON CONFLICT DO NOTHING;
SELECT * FROM upsert_test_unique ORDER BY time, color DESC;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 31.8 | yellow
 Fri Jan 20 09:00:01 2017 | 21.2 | brown

-- Test with multiple UNIQUE indexes
CREATE TABLE upsert_test_multi_unique(time timestamp, temp float, color text);
SELECT create_hypertable('upsert_test_multi_unique', 'time');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
           create_hypertable           
---------------------------------------
 (3,public,upsert_test_multi_unique,t)

ALTER TABLE upsert_test_multi_unique ADD CONSTRAINT multi_time_temp UNIQUE (time, temp);
CREATE UNIQUE INDEX multi_time_color_idx ON upsert_test_multi_unique (time, color);
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 25.9, 'yellow');
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-21T09:00:01', 25.9, 'yellow');
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 23.5, 'brown');
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 25.9, 'purple') ON CONFLICT DO NOTHING;
SELECT * FROM upsert_test_multi_unique ORDER BY time, color DESC;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 25.9 | yellow
 Fri Jan 20 09:00:01 2017 | 23.5 | brown
 Sat Jan 21 09:00:01 2017 | 25.9 | yellow

INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 25.9, 'blue') ON CONFLICT (time, temp)
DO UPDATE SET color = 'blue';
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 23.5, 'orange') ON CONFLICT ON CONSTRAINT multi_time_temp
DO UPDATE SET color = excluded.color;
SELECT * FROM upsert_test_multi_unique ORDER BY time, color DESC;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange
 Fri Jan 20 09:00:01 2017 | 25.9 | blue
 Sat Jan 21 09:00:01 2017 | 25.9 | yellow

INSERT INTO upsert_test_multi_unique VALUES ('2017-01-21T09:00:01', 45.7, 'yellow') ON CONFLICT (time, color)
DO UPDATE SET temp = 45.7;
SELECT * FROM upsert_test_multi_unique ORDER BY time, color DESC;
           time           | temp | color  
--------------------------+------+--------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange
 Fri Jan 20 09:00:01 2017 | 25.9 | blue
 Sat Jan 21 09:00:01 2017 | 45.7 | yellow

\set ON_ERROR_STOP 0
-- Here the constraint in the ON CONFLICT clause is not the one that is
-- actually violated by the INSERT, so it should still fail.
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 23.5, 'purple') ON CONFLICT (time, color)
DO UPDATE set temp = 23.5;
ERROR:  duplicate key value violates unique constraint "3_2_multi_time_temp"
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 22.5, 'orange') ON CONFLICT ON CONSTRAINT multi_time_temp
DO UPDATE set color = 'orange';
ERROR:  duplicate key value violates unique constraint "_hyper_3_3_chunk_multi_time_color_idx"
\set ON_ERROR_STOP 1
CREATE TABLE upsert_test_space(time timestamp, device_id_1 char(20), to_drop int, temp float, color text);
--drop two columns; create one.
ALTER TABLE upsert_test_space DROP to_drop;
ALTER TABLE upsert_test_space DROP device_id_1, ADD device_id char(20);
ALTER TABLE upsert_test_space ADD CONSTRAINT time_space_constraint UNIQUE (time, device_id);
SELECT create_hypertable('upsert_test_space', 'time', 'device_id', 2, partitioning_func=>'_timescaledb_functions.get_partition_for_key'::regproc);
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
       create_hypertable        
--------------------------------
 (4,public,upsert_test_space,t)

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev1', 25.9, 'yellow') RETURNING *;
           time           | temp | color  |      device_id       
--------------------------+------+--------+----------------------
 Fri Jan 20 09:00:01 2017 | 25.9 | yellow | dev1                

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev2', 25.9, 'yellow');
INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev1', 23.5, 'green') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color;
INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev1', 23.5, 'orange') ON CONFLICT ON CONSTRAINT time_space_constraint
DO UPDATE SET color = excluded.color;
INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev2', 23.5, 'orange3') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color||' (originally '|| upsert_test_space.color ||')' RETURNING *;
           time           | temp |            color            |      device_id       
--------------------------+------+-----------------------------+----------------------
 Fri Jan 20 09:00:01 2017 | 25.9 | orange3 (originally yellow) | dev2                

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev3', 23.5, 'orange3.1') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color||' (originally '|| upsert_test_space.color ||')' RETURNING *;
           time           | temp |   color   |      device_id       
--------------------------+------+-----------+----------------------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange3.1 | dev3                

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev2', 23.5, 'orange4') ON CONFLICT (time, device_id)
DO NOTHING RETURNING *;
 time | temp | color | device_id 
------+------+-------+-----------

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev4', 23.5, 'orange5') ON CONFLICT (time, device_id)
DO NOTHING RETURNING *;
           time           | temp |  color  |      device_id       
--------------------------+------+---------+----------------------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange5 | dev4                

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 23.5, 'orange5') ON CONFLICT (time, device_id)
DO NOTHING RETURNING *;
           time           | temp |  color  |      device_id       
--------------------------+------+---------+----------------------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange5 | dev5                

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 23.5, 'orange6') ON CONFLICT ON CONSTRAINT time_space_constraint
DO NOTHING RETURNING *;
 time | temp | color | device_id 
------+------+-------+-----------

--restore a column with the same name as a previously deleted one;
ALTER TABLE upsert_test_space ADD device_id_1 char(20);
INSERT INTO upsert_test_space (time, device_id, temp, color, device_id_1) VALUES ('2017-01-20T09:00:01', 'dev4', 23.5, 'orange5.1', 'dev-id-1') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color||' (originally '|| upsert_test_space.color ||')' RETURNING *;
           time           | temp |             color              |      device_id       | device_id_1 
--------------------------+------+--------------------------------+----------------------+-------------
 Fri Jan 20 09:00:01 2017 | 23.5 | orange5.1 (originally orange5) | dev4                 | 

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 23.5, 'orange6') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color WHERE upsert_test_space.temp < 20 RETURNING *;
 time | temp | color | device_id | device_id_1 
------+------+-------+-----------+-------------

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 23.5, 'orange7') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color WHERE excluded.temp < 20 RETURNING *;
 time | temp | color | device_id | device_id_1 
------+------+-------+-----------+-------------

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 3.5, 'orange7') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color, temp=excluded.temp WHERE excluded.temp < 20 RETURNING *;
           time           | temp |  color  |      device_id       | device_id_1 
--------------------------+------+---------+----------------------+-------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange7 | dev5                 | 

INSERT INTO upsert_test_space (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev5', 43.5, 'orange8') ON CONFLICT (time, device_id)
DO UPDATE SET color = excluded.color WHERE upsert_test_space.temp < 20 RETURNING *;
           time           | temp |  color  |      device_id       | device_id_1 
--------------------------+------+---------+----------------------+-------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange8 | dev5                 | 

INSERT INTO upsert_test_space (time, device_id, temp, color, device_id_1) VALUES ('2017-01-20T09:00:01', 'dev5', 43.5, 'orange8', 'device-id-1-new') ON CONFLICT (time, device_id)
DO UPDATE SET device_id_1 = excluded.device_id_1 RETURNING *;
           time           | temp |  color  |      device_id       |     device_id_1      
--------------------------+------+---------+----------------------+----------------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange8 | dev5                 | device-id-1-new     

INSERT INTO upsert_test_space (time, device_id, temp, color, device_id_1) VALUES ('2017-01-20T09:00:01', 'dev5', 43.5, 'orange8', 'device-id-1-new') ON CONFLICT (time, device_id)
DO UPDATE SET device_id_1 = 'device-id-1-new-2', color = 'orange9'  RETURNING *;
           time           | temp |  color  |      device_id       |     device_id_1      
--------------------------+------+---------+----------------------+----------------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange9 | dev5                 | device-id-1-new-2   

SELECT * FROM upsert_test_space;
           time           | temp |             color              |      device_id       |     device_id_1      
--------------------------+------+--------------------------------+----------------------+----------------------
 Fri Jan 20 09:00:01 2017 | 25.9 | orange                         | dev1                 | 
 Fri Jan 20 09:00:01 2017 | 25.9 | orange3 (originally yellow)    | dev2                 | 
 Fri Jan 20 09:00:01 2017 | 23.5 | orange3.1                      | dev3                 | 
 Fri Jan 20 09:00:01 2017 | 23.5 | orange5.1 (originally orange5) | dev4                 | 
 Fri Jan 20 09:00:01 2017 |  3.5 | orange9                        | dev5                 | device-id-1-new-2   

ALTER TABLE upsert_test_space DROP device_id_1, ADD device_id_2 char(20);
INSERT INTO upsert_test_space (time, device_id, temp, color, device_id_2) VALUES ('2017-01-20T09:00:01', 'dev5', 43.5, 'orange8', 'device-id-2')
ON CONFLICT (time, device_id)
DO UPDATE SET device_id_2 = 'device-id-2-new', color = 'orange10' RETURNING *;
           time           | temp |  color   |      device_id       |     device_id_2      
--------------------------+------+----------+----------------------+----------------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange10 | dev5                 | device-id-2-new     

--test inserting to to a chunk already in the chunk dispatch cache again.
INSERT INTO upsert_test_space as current (time, device_id, temp, color, device_id_2) VALUES ('2017-01-20T09:00:01', 'dev5', 43.5, 'orange8', 'device-id-2'),
('2018-01-20T09:00:01', 'dev5', 43.5, 'orange8', 'device-id-2'),
('2017-01-20T09:00:01', 'dev3', 43.5, 'orange7', 'device-id-2'),
('2018-01-21T09:00:01', 'dev5', 43.5, 'orange9', 'device-id-2')
ON CONFLICT (time, device_id)
DO UPDATE SET device_id_2 = coalesce(excluded.device_id_2,current.device_id_2), color = coalesce(excluded.color,current.color) RETURNING *;
           time           | temp |  color  |      device_id       |     device_id_2      
--------------------------+------+---------+----------------------+----------------------
 Fri Jan 20 09:00:01 2017 |  3.5 | orange8 | dev5                 | device-id-2         
 Sat Jan 20 09:00:01 2018 | 43.5 | orange8 | dev5                 | device-id-2         
 Fri Jan 20 09:00:01 2017 | 23.5 | orange7 | dev3                 | device-id-2         
 Sun Jan 21 09:00:01 2018 | 43.5 | orange9 | dev5                 | device-id-2         

WITH CTE AS (
    INSERT INTO upsert_test_multi_unique
    VALUES ('2017-01-20T09:00:01', 25.9, 'purple')
    ON CONFLICT DO NOTHING
    RETURNING *
) SELECT 1;
 ?column? 
----------
        1

WITH CTE AS (
    INSERT INTO upsert_test_multi_unique
    VALUES ('2017-01-20T09:00:01', 25.9, 'purple'),
    ('2017-01-20T09:00:01', 29.9, 'purple1')
    ON CONFLICT DO NOTHING
    RETURNING *
) SELECT * FROM CTE;
           time           | temp |  color  
--------------------------+------+---------
 Fri Jan 20 09:00:01 2017 | 29.9 | purple1

WITH CTE AS (
    INSERT INTO upsert_test_multi_unique
    VALUES ('2017-01-20T09:00:01', 25.9, 'blue')
    ON CONFLICT (time, temp) DO UPDATE SET color = 'blue'
    RETURNING *
)
SELECT * FROM CTE;
           time           | temp | color 
--------------------------+------+-------
 Fri Jan 20 09:00:01 2017 | 25.9 | blue

--test error conditions when an index is dropped on a chunk
DROP INDEX _timescaledb_internal._hyper_3_3_chunk_multi_time_color_idx;
--everything is ok if not used as an arbiter index
INSERT INTO upsert_test_multi_unique
VALUES ('2017-01-20T09:00:01', 25.9, 'purple')
ON CONFLICT DO NOTHING
RETURNING *;
 time | temp | color 
------+------+-------

--errors out if used as an arbiter index
\set ON_ERROR_STOP 0
INSERT INTO upsert_test_multi_unique
VALUES ('2017-01-20T09:00:01', 25.9, 'purple')
ON CONFLICT (time, color) DO NOTHING
RETURNING *;
ERROR:  could not find arbiter index for hypertable index "multi_time_color_idx" on chunk "_hyper_3_3_chunk"
\set ON_ERROR_STOP 1
--create table with one chunk that has a tup_conv_map and one that does not
--to ensure this, create a chunk before altering the table this chunk will not have a tup_conv_map
CREATE TABLE upsert_test_diffchunk(time timestamp, device_id char(20), to_drop int, temp float, color text);
SELECT create_hypertable('upsert_test_diffchunk', 'time', chunk_time_interval=> interval '1 month');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
         create_hypertable          
------------------------------------
 (5,public,upsert_test_diffchunk,t)

CREATE UNIQUE INDEX time_device_idx ON upsert_test_diffchunk (time, device_id);
--this is the chunk with no tup_conv_map
INSERT INTO upsert_test_diffchunk (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev1', 25.9, 'yellow') RETURNING *;
           time           |      device_id       | to_drop | temp | color  
--------------------------+----------------------+---------+------+--------
 Fri Jan 20 09:00:01 2017 | dev1                 |         | 25.9 | yellow

INSERT INTO upsert_test_diffchunk (time, device_id, temp, color) VALUES ('2017-01-20T09:00:01', 'dev2', 25.9, 'yellow') RETURNING *;
           time           |      device_id       | to_drop | temp | color  
--------------------------+----------------------+---------+------+--------
 Fri Jan 20 09:00:01 2017 | dev2                 |         | 25.9 | yellow

--alter the table
ALTER TABLE upsert_test_diffchunk DROP to_drop;
ALTER TABLE upsert_test_diffchunk ADD device_id_2 char(20);
--new chunk that does have a tup conv map
INSERT INTO upsert_test_diffchunk (time, device_id, temp, color) VALUES ('2019-01-20T09:00:01', 'dev1', 23.5, 'orange') ;
INSERT INTO upsert_test_diffchunk (time, device_id, temp, color) VALUES ('2019-01-20T09:00:01', 'dev2', 23.5, 'orange') ;
select * from upsert_test_diffchunk order by time, device_id;
           time           |      device_id       | temp | color  | device_id_2 
--------------------------+----------------------+------+--------+-------------
 Fri Jan 20 09:00:01 2017 | dev1                 | 25.9 | yellow | 
 Fri Jan 20 09:00:01 2017 | dev2                 | 25.9 | yellow | 
 Sun Jan 20 09:00:01 2019 | dev1                 | 23.5 | orange | 
 Sun Jan 20 09:00:01 2019 | dev2                 | 23.5 | orange | 

--make sure current works
INSERT INTO upsert_test_diffchunk as current (time, device_id, temp, color, device_id_2) VALUES
('2019-01-20T09:00:01', 'dev1', 43.5, 'orange2', 'device-id-2'),
('2017-01-20T09:00:01', 'dev1', 43.5, 'yellow2', 'device-id-2'),
('2019-01-20T09:00:01', 'dev2', 43.5, 'orange2', 'device-id-2')
ON CONFLICT (time, device_id)
DO UPDATE SET
device_id_2 = coalesce(excluded.device_id_2,current.device_id_2),
temp = coalesce(excluded.temp,current.temp) ,
color = coalesce(excluded.color,current.color);
select * from upsert_test_diffchunk order by time, device_id;
           time           |      device_id       | temp |  color  |     device_id_2      
--------------------------+----------------------+------+---------+----------------------
 Fri Jan 20 09:00:01 2017 | dev1                 | 43.5 | yellow2 | device-id-2         
 Fri Jan 20 09:00:01 2017 | dev2                 | 25.9 | yellow  | 
 Sun Jan 20 09:00:01 2019 | dev1                 | 43.5 | orange2 | device-id-2         
 Sun Jan 20 09:00:01 2019 | dev2                 | 43.5 | orange2 | device-id-2         

--arbiter index tests
CREATE TABLE upsert_test_arbiter(time timestamp, to_drop int);
SELECT create_hypertable('upsert_test_arbiter', 'time', chunk_time_interval=> interval '1 month');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
        create_hypertable         
----------------------------------
 (6,public,upsert_test_arbiter,t)

--this is the chunk with no tup_conv_map
INSERT INTO upsert_test_arbiter (time, to_drop) VALUES ('2017-01-20T09:00:01', 1) RETURNING *;
           time           | to_drop 
--------------------------+---------
 Fri Jan 20 09:00:01 2017 |       1

INSERT INTO upsert_test_arbiter (time, to_drop) VALUES ('2017-01-21T09:00:01', 2) RETURNING *;
           time           | to_drop 
--------------------------+---------
 Sat Jan 21 09:00:01 2017 |       2

INSERT INTO upsert_test_arbiter (time, to_drop) VALUES ('2017-03-20T09:00:01', 3) RETURNING *;
           time           | to_drop 
--------------------------+---------
 Mon Mar 20 09:00:01 2017 |       3

--alter the table
ALTER TABLE upsert_test_arbiter DROP to_drop;
ALTER TABLE upsert_test_arbiter ADD device_id char(20) DEFAULT 'dev1';
CREATE UNIQUE INDEX arbiter_time_device_idx ON upsert_test_arbiter (time, device_id);
INSERT INTO upsert_test_arbiter as current (time, device_id) VALUES
    ('2018-01-21T09:00:01', 'dev1'),
    ('2017-01-20T09:00:01', 'dev1'),
    ('2017-01-21T09:00:01', 'dev2'),
    ('2018-01-21T09:00:01', 'dev2')
 ON CONFLICT (time, device_id) DO UPDATE SET device_id = coalesce(excluded.device_id,current.device_id)
RETURNING *;
           time           |      device_id       
--------------------------+----------------------
 Sun Jan 21 09:00:01 2018 | dev1                
 Fri Jan 20 09:00:01 2017 | dev1                
 Sat Jan 21 09:00:01 2017 | dev2                
 Sun Jan 21 09:00:01 2018 | dev2                

with cte as (
INSERT INTO upsert_test_arbiter (time, device_id) VALUES
    ('2017-01-21T09:00:01', 'dev2'),
    ('2018-01-21T09:00:01', 'dev2')
 ON CONFLICT (time, device_id) DO UPDATE SET device_id = 'dev3'
RETURNING *)
select * from cte;
           time           |      device_id       
--------------------------+----------------------
 Sat Jan 21 09:00:01 2017 | dev3                
 Sun Jan 21 09:00:01 2018 | dev3                

-- test ON CONFLICT with prepared statements
CREATE TABLE prepared_test(time timestamptz PRIMARY KEY, value float CHECK(value > 0));
SELECT create_hypertable('prepared_test','time');
     create_hypertable      
----------------------------
 (7,public,prepared_test,t)

CREATE TABLE source_data(time timestamptz PRIMARY KEY, value float);
INSERT INTO source_data VALUES('2000-01-01',0.5), ('2001-01-01',0.5);
-- at some point PostgreSQL will turn the plan into a generic plan
-- so we execute the prepared statement 10 times
-- check that an error in the prepared statement does not lead to the plan becoming unusable
PREPARE prep_insert_select AS INSERT INTO prepared_test select * from source_data ON CONFLICT (time) DO UPDATE SET value = EXCLUDED.value;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
EXECUTE prep_insert_select;
--this insert will create an invalid tuple in source_data
--so that future calls to prep_insert_select will fail
INSERT INTO source_data VALUES('2000-01-02',-0.5);
\set ON_ERROR_STOP 0
EXECUTE prep_insert_select;
ERROR:  new row for relation "_hyper_7_11_chunk" violates check constraint "prepared_test_value_check"
EXECUTE prep_insert_select;
ERROR:  new row for relation "_hyper_7_11_chunk" violates check constraint "prepared_test_value_check"
\set ON_ERROR_STOP 1
DELETE FROM source_data WHERE value <= 0;
EXECUTE prep_insert_select;
PREPARE prep_insert AS INSERT INTO prepared_test VALUES('2000-01-01',0.5) ON CONFLICT (time) DO UPDATE SET value = EXCLUDED.value;
-- at some point PostgreSQL will turn the plan into a generic plan
-- so we execute the prepared statement 10 times
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
EXECUTE prep_insert;
SELECT * FROM prepared_test;
             time             | value 
------------------------------+-------
 Sat Jan 01 00:00:00 2000 PST |   0.5
 Mon Jan 01 00:00:00 2001 PST |   0.5

DELETE FROM prepared_test;
-- test ON CONFLICT with functions
CREATE OR REPLACE FUNCTION test_upsert(t timestamptz, v float) RETURNS VOID AS $sql$
BEGIN
INSERT INTO prepared_test VALUES(t,v) ON CONFLICT (time) DO UPDATE SET value = EXCLUDED.value;
END;
$sql$ LANGUAGE PLPGSQL;
-- at some point PostgreSQL will turn the plan into a generic plan
-- so we execute the function 10 times
SELECT counter,test_upsert('2000-01-01',0.5) FROM generate_series(1,10) AS g(counter);
 counter | test_upsert 
---------+-------------
       1 | 
       2 | 
       3 | 
       4 | 
       5 | 
       6 | 
       7 | 
       8 | 
       9 | 
      10 | 

SELECT * FROM prepared_test;
             time             | value 
------------------------------+-------
 Sat Jan 01 00:00:00 2000 PST |   0.5

DELETE FROM prepared_test;
-- at some point PostgreSQL will turn the plan into a generic plan
-- so we execute the function 10 times
SELECT counter,test_upsert('2000-01-01',0.5) FROM generate_series(1,10) AS g(counter);
 counter | test_upsert 
---------+-------------
       1 | 
       2 | 
       3 | 
       4 | 
       5 | 
       6 | 
       7 | 
       8 | 
       9 | 
      10 | 

SELECT * FROM prepared_test;
             time             | value 
------------------------------+-------
 Sat Jan 01 00:00:00 2000 PST |   0.5

DELETE FROM prepared_test;
-- run it again to ensure INSERT path is still working as well
SELECT counter,test_upsert('2000-01-01',0.5) FROM generate_series(1,10) AS g(counter);
 counter | test_upsert 
---------+-------------
       1 | 
       2 | 
       3 | 
       4 | 
       5 | 
       6 | 
       7 | 
       8 | 
       9 | 
      10 | 

SELECT * FROM prepared_test;
             time             | value 
------------------------------+-------
 Sat Jan 01 00:00:00 2000 PST |   0.5

DELETE FROM prepared_test;
-- test ON CONFLICT with functions
CREATE OR REPLACE FUNCTION test_upsert2(t timestamptz, v float) RETURNS VOID AS $sql$
BEGIN
INSERT INTO prepared_test VALUES(t,v) ON CONFLICT (time) DO UPDATE SET value = prepared_test.value + 1.0;
END;
$sql$ LANGUAGE PLPGSQL;
-- at some point PostgreSQL will turn the plan into a generic plan
-- so we execute the function 10 times
SELECT counter,test_upsert2('2000-01-01',1.0) FROM generate_series(1,10) AS g(counter);
 counter | test_upsert2 
---------+--------------
       1 | 
       2 | 
       3 | 
       4 | 
       5 | 
       6 | 
       7 | 
       8 | 
       9 | 
      10 | 

SELECT * FROM prepared_test;
             time             | value 
------------------------------+-------
 Sat Jan 01 00:00:00 2000 PST |    10

