blob: f475ee479793d1b0d4e8725ec35c6f95ec6c1f93 [file] [log] [blame]
--
-- Tests for updatable cursors
--
-- Deterministic test runs will be a little tricky in the case of updatable
-- cursors. By definition, these cursors cannot be ordered, yet we will be
-- issuing UPDATE and DELETE based exclusively on cursor position.
--
CREATE TEMP TABLE uctest(f1 int, f2 int, f3 text) DISTRIBUTED BY (f1);
CREATE TEMP TABLE uctest2(f1 int, f2 int, f3 text) DISTRIBUTED BY (f1);
INSERT INTO uctest VALUES (1, 1, 'one'), (1, 1, 'one'), (2, 2, 'two'), (3, 3, 'three');
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
1 | 1 | one
2 | 2 | two
3 | 3 | three
(4 rows)
--
-- DELETE ... WHERE CURRENT
---
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM uctest WHERE f1 = 1;
FETCH 1 FROM c1;
f1 | f2 | f3
----+----+-----
1 | 1 | one
(1 row)
DELETE FROM uctest WHERE CURRENT OF c1;
-- should show deletion
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 2 | two
3 | 3 | three
(3 rows)
-- cursor did not move
FETCH ALL FROM c1;
f1 | f2 | f3
----+----+-----
1 | 1 | one
(1 row)
COMMIT;
-- should still see deletion
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 2 | two
3 | 3 | three
(3 rows)
--
-- UPDATE ... WHERE CURRENT against SELECT ... FOR UPDATE
--
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM uctest WHERE f1 = 2 FOR UPDATE;
FETCH 1 FROM c1;
f1 | f2 | f3
----+----+-----
2 | 2 | two
(1 row)
UPDATE uctest SET f2 = 8 WHERE CURRENT OF c1;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 3 | three
(3 rows)
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 3 | three
(3 rows)
--
-- UPDATE ... WHERE CURRENT against SELECT ... FOR SHARE
--
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM uctest WHERE f1 = 3 FOR SHARE;
FETCH 1 FROM c1;
f1 | f2 | f3
----+----+-------
3 | 3 | three
(1 row)
UPDATE uctest SET f2 = 9 WHERE CURRENT OF c1;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 9 | three
(3 rows)
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 9 | three
(3 rows)
--
-- Scan paths
--
CREATE INDEX uctest_index ON uctest USING btree(f1);
SET enable_tidscan=on; SET enable_seqscan=off; SET enable_indexscan=off;
BEGIN;
DECLARE a CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM a;
f1 | f2 | f3
----+----+-------
3 | 9 | three
(1 row)
UPDATE uctest SET f2 = 10 WHERE CURRENT OF a;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 10 | three
(3 rows)
SET enable_tidscan=off; SET enable_seqscan=on; SET enable_indexscan=off;
BEGIN;
DECLARE a CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM a;
f1 | f2 | f3
----+----+-------
3 | 10 | three
(1 row)
UPDATE uctest SET f2 = 11 WHERE CURRENT OF a;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 11 | three
(3 rows)
SET enable_tidscan=off; SET enable_seqscan=off; SET enable_indexscan=on;
BEGIN;
DECLARE a CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM a;
f1 | f2 | f3
----+----+-------
3 | 11 | three
(1 row)
UPDATE uctest SET f2 = 12 WHERE CURRENT OF a;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 12 | three
(3 rows)
RESET enable_tidscan; RESET enable_seqscan; RESET enable_indexscan;
--
-- System attributes already residing in targetlist
--
BEGIN;
DECLARE c1 CURSOR FOR SELECT f1 as gp_segment_id, * FROM uctest WHERE f1 = 3;
FETCH 1 FROM c1;
gp_segment_id | f1 | f2 | f3
---------------+----+----+-------
3 | 3 | 12 | three
(1 row)
UPDATE uctest SET f2 = -1 * f2 WHERE CURRENT OF c1;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+-----+-------
1 | 1 | one
2 | 8 | two
3 | -12 | three
(3 rows)
BEGIN;
DECLARE c1 CURSOR FOR SELECT 100 as ctid, * FROM uctest WHERE f1 = 3;
FETCH 1 FROM c1;
ctid | f1 | f2 | f3
------+----+-----+-------
100 | 3 | -12 | three
(1 row)
UPDATE uctest SET f2 = -1 * f2 WHERE CURRENT OF c1;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 12 | three
(3 rows)
--
-- Repeated updates
--
BEGIN;
DECLARE c CURSOR FOR SELECT * from uctest WHERE f1 = 3;
FETCH 1 from c;
f1 | f2 | f3
----+----+-------
3 | 12 | three
(1 row)
UPDATE uctest SET f2 = f2 + 10 WHERE CURRENT of c;
SELECT * from uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 22 | three
(3 rows)
UPDATE uctest SET f2 = f2 + 10 WHERE CURRENT of c;
SELECT * from uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 32 | three
(3 rows)
COMMIT;
SELECT * from uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 32 | three
(3 rows)
--
-- UPDATE with FROM and subqueries
--
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM c;
f1 | f2 | f3
----+----+-------
3 | 32 | three
(1 row)
UPDATE uctest SET f2 = other.f2 + 10 FROM
(SELECT * FROM uctest WHERE f1 = 3) AS other
WHERE CURRENT OF c;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 42 | three
(3 rows)
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM c;
f1 | f2 | f3
----+----+-------
3 | 42 | three
(1 row)
UPDATE uctest SET f2 = (SELECT f2 - 10 FROM uctest WHERE f1 = 3) WHERE CURRENT OF c;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 32 | three
(3 rows)
--
-- Partitioning
--
-- Partitioning: ensure each part has overlapping gp_segment_id/ctid, to test the
-- use of tableoid as qual
CREATE TABLE portals_updatable_rank(id int, rank int, f int)
DISTRIBUTED BY (id)
PARTITION BY RANGE (rank)
(START (0) END (10) EVERY (1),
DEFAULT PARTITION extra );
INSERT INTO portals_updatable_rank (SELECT x, x, x FROM generate_series(0, 10) x);
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE id = 1;
FETCH 1 FROM c;
id | rank | f
----+------+---
1 | 1 | 1
(1 row)
UPDATE portals_updatable_rank SET f = f * -1 WHERE CURRENT OF c;
SELECT * FROM portals_updatable_rank ORDER BY 1, 2, 3;
id | rank | f
----+------+----
0 | 0 | 0
1 | 1 | -1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
9 | 9 | 9
10 | 10 | 10
(11 rows)
COMMIT;
SELECT * FROM portals_updatable_rank ORDER BY 1, 2, 3;
id | rank | f
----+------+----
0 | 0 | 0
1 | 1 | -1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
9 | 9 | 9
10 | 10 | 10
(11 rows)
-- Partitioning: what if range tables of DECLARE CURSOR and CURRENT OF differ? with one
-- pointing at logical table and the other pointing merely at a part
-- Partitioning: DECLARE CURSOR against portals_updatable_rank_1_prt_extra, CURRENT OF against portals_updatable_rank_1_prt_extra
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank_1_prt_extra;
FETCH 1 FROM c;
id | rank | f
----+------+----
10 | 10 | 10
(1 row)
UPDATE portals_updatable_rank_1_prt_extra set f = f * -1 WHERE CURRENT OF c;
COMMIT;
SELECT * FROM portals_updatable_rank_1_prt_extra ORDER BY 1, 2, 3;
id | rank | f
----+------+-----
10 | 10 | -10
(1 row)
-- Partitioning: DECLARE CURSOR against portals_updatable_rank, CURRENT OF against portals_updatable_rank_1_prt_extra
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 10;
DELETE FROM portals_updatable_rank_1_prt_extra WHERE CURRENT OF c; -- error out on wrong table
ERROR: cursor "c" is not a simply updatable scan of table "portals_updatable_rank_1_prt_extra"
ROLLBACK;
-- Partitioning: DECLARE CURSOR against portals_updatable_rank_1_prt_extra, CURRENT OF against portals_updatable_rank
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank_1_prt_extra WHERE rank = 10;
DELETE FROM portals_updatable_rank WHERE CURRENT OF c; -- error out on wrong table
ERROR: cursor "c" is not a simply updatable scan of table "portals_updatable_rank"
ROLLBACK;
-- Partitioning, cursor-agnostic: move tuple across partitions
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 1;
FETCH 1 FROM c;
id | rank | f
----+------+----
1 | 1 | -1
(1 row)
UPDATE portals_updatable_rank SET rank = rank + 1 WHERE CURRENT OF c;
SELECT tableoid::regclass, * FROM portals_updatable_rank;
tableoid | id | rank | f
------------------------------------+----+------+-----
portals_updatable_rank_1_prt_7 | 5 | 5 | 5
portals_updatable_rank_1_prt_8 | 6 | 6 | 6
portals_updatable_rank_1_prt_11 | 9 | 9 | 9
portals_updatable_rank_1_prt_extra | 10 | 10 | -10
portals_updatable_rank_1_prt_4 | 2 | 2 | 2
portals_updatable_rank_1_prt_5 | 3 | 3 | 3
portals_updatable_rank_1_prt_6 | 4 | 4 | 4
portals_updatable_rank_1_prt_9 | 7 | 7 | 7
portals_updatable_rank_1_prt_10 | 8 | 8 | 8
portals_updatable_rank_1_prt_2 | 0 | 0 | 0
portals_updatable_rank_1_prt_4 | 1 | 2 | -1
(11 rows)
ROLLBACK;
-- Partitioning: AO part
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_2;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (0) WITH TABLE aopart;
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 0;
FETCH 1 FROM c;
id | rank | f
----+------+---
0 | 0 | 0
(1 row)
DELETE FROM portals_updatable_rank WHERE CURRENT OF c;
ERROR: "portals_updatable_rank_1_prt_2" is not simply updatable
ROLLBACK;
-- Partitioning: AO/CO part
CREATE TABLE aocopart (LIKE portals_updatable_rank) WITH (appendonly=true, orientation=column)
DISTRIBUTED BY (id);
INSERT INTO aocopart SELECT * FROM portals_updatable_rank_1_prt_2;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (0) WITH TABLE aocopart;
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 0;
FETCH 1 FROM c;
id | rank | f
----+------+---
0 | 0 | 0
(1 row)
DELETE FROM portals_updatable_rank WHERE CURRENT OF c;
ERROR: "portals_updatable_rank_1_prt_2" is not simply updatable
ROLLBACK;
-- Partitioning: mostly AO parts
-- Despite some non-determinism, this should cover the case in which AO parts
-- are scanned by the segments. Despite our grand attempts to disallow AO/CO tables,
-- the executor will need to cope with AO tuples, for cases just like this.
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_2;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (0) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_3;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (1) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_4;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (2) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_5;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (3) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_6;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (4) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_7;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (5) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_8;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (6) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_9;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (7) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_10;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (8) WITH TABLE aopart;
DROP TABLE aopart;
CREATE TABLE aopart (LIKE portals_updatable_rank) WITH (appendonly=true) DISTRIBUTED BY (id);
INSERT INTO aopart SELECT * FROM portals_updatable_rank_1_prt_11;
ALTER TABLE portals_updatable_rank EXCHANGE PARTITION FOR (9) WITH TABLE aopart;
ANALYZE portals_updatable_rank;
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 10; -- isolate the remaining heap part
FETCH 1 FROM c;
id | rank | f
----+------+-----
10 | 10 | -10
(1 row)
UPDATE portals_updatable_rank SET f = f * -1 WHERE CURRENT OF c;
COMMIT;
SELECT * FROM portals_updatable_rank ORDER BY 1, 2, 3;
id | rank | f
----+------+----
0 | 0 | 0
1 | 1 | -1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
9 | 9 | 9
10 | 10 | 10
(11 rows)
-- Partitioning, sub-partitioning
CREATE TABLE bar (a int, b int, c int, d int)
DISTRIBUTED BY (a)
PARTITION BY RANGE(b)
SUBPARTITION BY LIST( c)
SUBPARTITION TEMPLATE (
DEFAULT SUBPARTITION subothers,
SUBPARTITION s1 VALUES(0,1,2),
SUBPARTITION s2 VALUES(3,4,5)
)
( DEFAULT PARTITION others, START(0) END(6) EVERY(1) );
INSERT INTO bar (SELECT x, x % 6, x % 6, x FROM generate_series(0, 11) x);
BEGIN;
DECLARE a CURSOR FOR SELECT * FROM bar WHERE a = 0;
FETCH 1 FROM a;
a | b | c | d
---+---+---+---
0 | 0 | 0 | 0
(1 row)
UPDATE bar SET d = -1000 WHERE CURRENT OF a;
COMMIT;
SELECT * FROM bar ORDER BY 1, 2, 3, 4;
a | b | c | d
----+---+---+-------
0 | 0 | 0 | -1000
1 | 1 | 1 | 1
2 | 2 | 2 | 2
3 | 3 | 3 | 3
4 | 4 | 4 | 4
5 | 5 | 5 | 5
6 | 0 | 0 | 6
7 | 1 | 1 | 7
8 | 2 | 2 | 8
9 | 3 | 3 | 9
10 | 4 | 4 | 10
11 | 5 | 5 | 11
(12 rows)
-- Partitioning, update distribution key
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM portals_updatable_rank WHERE rank = 10;
FETCH 1 FROM c;
id | rank | f
----+------+----
10 | 10 | 10
(1 row)
UPDATE portals_updatable_rank SET id = id + 1 WHERE CURRENT OF c;
COMMIT;
SELECT * FROM portals_updatable_rank ORDER BY 1, 2, 3;
id | rank | f
----+------+----
0 | 0 | 0
1 | 1 | -1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
9 | 9 | 9
11 | 10 | 10
(11 rows)
--
-- Expected Failure
--
-- WHERE CURRENT OF against SELECT ... READ ONLY
-- UPDATE succeeds despite READ ONLY designation
--
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest WHERE f1 = 3 FOR READ ONLY;
FETCH 1 FROM c;
f1 | f2 | f3
----+----+-------
3 | 32 | three
(1 row)
UPDATE uctest SET f2 = f2 + 10 WHERE CURRENT OF c;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 42 | three
(3 rows)
-- gp_dist_random, edge case treated as special RTE_RELATION
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM gp_dist_random('uctest') WHERE f1 = 3;
FETCH 1 FROM c;
f1 | f2 | f3
----+----+-------
3 | 42 | three
(1 row)
UPDATE uctest SET f2 = f2 + 10 WHERE CURRENT OF c;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+----+-------
1 | 1 | one
2 | 8 | two
3 | 52 | three
(3 rows)
-- naive PREPARE usage
PREPARE ucplan AS UPDATE uctest SET f2 = f2 * -1 WHERE CURRENT OF a;
BEGIN;
DECLARE a CURSOR FOR SELECT * FROM uctest WHERE f1 = 3;
FETCH 1 FROM a;
f1 | f2 | f3
----+----+-------
3 | 52 | three
(1 row)
EXECUTE ucplan;
COMMIT;
SELECT * FROM uctest ORDER BY 1, 2, 3;
f1 | f2 | f3
----+-----+-------
1 | 1 | one
2 | 8 | two
3 | -52 | three
(3 rows)
--
-- Negative
--
-- Negative: no such cursor
DELETE FROM uctest WHERE CURRENT OF c1;
ERROR: cursor "c1" does not exist
-- Negative: wrong UPDATE syntax
UPDATE uctest SET f2 = 5 WHERE CURRENT OF c1 AND f3 = 10;
ERROR: syntax error at or near "AND"
LINE 1: UPDATE uctest SET f2 = 5 WHERE CURRENT OF c1 AND f3 = 10;
^
-- Negative: can't use held cursor
DECLARE cx CURSOR WITH HOLD FOR SELECT * FROM uctest;
DELETE FROM uctest WHERE CURRENT OF cx;
ERROR: cursor "cx" is held from a previous transaction
-- Negative: cursor on wrong table
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest where f1 = 3;
FETCH 1 from c;
f1 | f2 | f3
----+-----+-------
3 | -52 | three
(1 row)
DELETE FROM uctest2 WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest2"
ROLLBACK;
-- Negative: cursor is ordered
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest ORDER BY f1;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor is on a join
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest JOIN uctest2 USING (f1);
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor with subquery
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest WHERE f1 in (SELECT f1 FROM uctest);
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor is on aggregation
BEGIN;
DECLARE c CURSOR FOR SELECT count(*) FROM uctest;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
BEGIN;
DECLARE c CURSOR FOR SELECT f1,count(*) FROM uctest GROUP BY f1;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
BEGIN;
DECLARE c CURSOR FOR SELECT f1,count(*) FROM uctest GROUP BY f1 HAVING count(*) = 1;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor with distinct clause
BEGIN;
DECLARE c CURSOR FOR SELECT DISTINCT f1, f2 FROM uctest;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor with limit/offset
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest LIMIT 1;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM uctest OFFSET 1;
DELETE FROM uctest WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor with set operations
-- MPP-16559 - DECLARE CURSOR with set operations will hit assertion
-- BEGIN;
-- DECLARE c CURSOR FOR SELECT f1 FROM uctest UNION SELECT f2 FROM uctest2;
-- DELETE FROM uctest WHERE CURRENT OF c;
-- ROLLBACK;
-- BEGIN;
-- DECLARE c CURSOR FOR SELECT f1 FROM uctest UNION ALL SELECT f2 FROM uctest2;
-- DELETE FROM uctest WHERE CURRENT OF c;
-- ROLLBACK;
-- BEGIN;
-- DECLARE c CURSOR FOR SELECT f1 FROM uctest EXCEPT SELECT f2 FROM uctest2;
-- DELETE FROM uctest WHERE CURRENT OF c;
-- ROLLBACK;
-- Negative: cursor with window clauses
BEGIN;
DECLARE c1 CURSOR FOR SELECT f1, rank() OVER (ORDER BY f1 DESC) FROM uctest ORDER BY f1;
DELETE FROM uctest WHERE CURRENT OF c1;
ERROR: cursor "c1" is not a simply updatable scan of table "uctest"
ROLLBACK;
-- Negative: cursor is not positioned
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM uctest;
DELETE FROM uctest WHERE CURRENT OF c1;
ERROR: cursor "c1" is not positioned on a row
ROLLBACK;
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM uctest;
FETCH ALL FROM c1;
f1 | f2 | f3
----+-----+-------
2 | 8 | two
3 | -52 | three
1 | 1 | one
(3 rows)
FETCH 1 FROM c1;
f1 | f2 | f3
----+----+----
(0 rows)
DELETE FROM uctest WHERE CURRENT OF c1;
ERROR: cursor "c1" is not positioned on a row
ROLLBACK;
-- Negative: cursor on views
CREATE TEMP VIEW ucview AS SELECT * FROM uctest;
CREATE RULE ucrule AS ON DELETE TO ucview DO INSTEAD
DELETE FROM uctest WHERE f2 = OLD.f2;
BEGIN;
DECLARE c1 CURSOR FOR SELECT * FROM ucview WHERE f1 = 3;
FETCH 1 FROM c1;
f1 | f2 | f3
----+-----+-------
3 | -52 | three
(1 row)
DELETE FROM ucview WHERE CURRENT OF c1;
ERROR: WHERE CURRENT OF on a view is not implemented
ROLLBACK;
-- Negative, cursor-agnostic: cannot update external tables
CREATE EXTERNAL WEB TABLE ucexttest (x text) EXECUTE 'echo "foo";' FORMAT 'TEXT';
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM ucexttest;
FETCH 1 from c;
x
-----
foo
(1 row)
UPDATE ucexttest SET x = 'bar' WHERE CURRENT OF c;
ERROR: "ucexttest" is not simply updatable
ROLLBACK;
DROP EXTERNAL TABLE ucexttest;
-- Negative, cursor-agnostic: cannot update AO
CREATE TEMP TABLE aotest (a int, b text)
WITH (appendonly=true);
NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table.
HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM aotest;
DELETE FROM aotest WHERE CURRENT OF c;
ERROR: "aotest" is not simply updatable
ROLLBACK;
-- Negative, cursor-agnostic: cannot update AO/CO
CREATE TEMP TABLE aocotest (a int, b text)
WITH (appendonly=true, orientation=column);
NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table.
HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM aocotest;
DELETE FROM aocotest WHERE CURRENT OF c;
ERROR: "aocotest" is not simply updatable
ROLLBACK;
-- Negative: cursor with limit/offset against ordinary table
-- The issue: https://github.com/greenplum-db/gpdb/issues/9838
CREATE TABLE tidscan_9838(id integer);
INSERT INTO tidscan_9838 (id) VALUES (1), (2), (3);
BEGIN;
DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan_9838 LIMIT 1;
UPDATE tidscan_9838 SET id = -id WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "tidscan_9838"
ROLLBACK;
BEGIN;
DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan_9838 OFFSET 1;
UPDATE tidscan_9838 SET id = -id WHERE CURRENT OF c;
ERROR: cursor "c" is not a simply updatable scan of table "tidscan_9838"
ROLLBACK;
--
-- PL/pgSQL cursors
--
-- Test that cursors opened in PL/pgSQL can also be updated.
-- (Not supported by ORCA, as of this writing.)
create temp table uctest3 as
select n as i, n as j from generate_series(1, 5) n distributed randomly;
create or replace function plpgsql_uc_test() returns void as $$
declare
c cursor for select * from uctest3 where i = 3;
r record;
begin
open c;
fetch c into r;
raise notice '%, %', r.i, r.j;
update uctest3 set i = i * 100, j = r.j * 2 where current of c;
end;
$$ language plpgsql;
select plpgsql_uc_test();
NOTICE: 3, 3
plpgsql_uc_test
-----------------
(1 row)
select * from uctest3;
i | j
-----+---
5 | 5
1 | 1
2 | 2
4 | 4
300 | 6
(5 rows)