-- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0;
-- check that non-updatable views and columns are rejected with useful error -- messages
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW ro_view1 ASSELECTDISTINCT a, b FROM base_tbl; -- DISTINCT not supported CREATE VIEW ro_view2 ASSELECT a, b FROM base_tbl GROUPBY a, b; -- GROUP BY not supported CREATE VIEW ro_view3 ASSELECT1FROM base_tbl HAVING max(a) > 0; -- HAVING not supported CREATE VIEW ro_view4 ASSELECT count(*) FROM base_tbl; -- Aggregate functions not supported CREATE VIEW ro_view5 ASSELECT a, rank() OVER() FROM base_tbl; -- Window functions not supported CREATE VIEW ro_view6 ASSELECT a, b FROM base_tbl UNIONSELECT -a, b FROM base_tbl; -- Set ops not supported CREATE VIEW ro_view7 ASWITH t AS (SELECT a, b FROM base_tbl) SELECT * FROM t; -- WITH not supported CREATE VIEW ro_view8 ASSELECT a, b FROM base_tbl ORDERBY a OFFSET 1; -- OFFSET not supported CREATE VIEW ro_view9 ASSELECT a, b FROM base_tbl ORDERBY a LIMIT1; -- LIMIT not supported CREATE VIEW ro_view10 ASSELECT1AS a; -- No base relations CREATE VIEW ro_view11 ASSELECT b1.a, b2.b FROM base_tbl b1, base_tbl b2; -- Multiple base relations CREATE VIEW ro_view12 ASSELECT * FROM generate_series(1, 10) AS g(a); -- SRF in rangetable CREATE VIEW ro_view13 ASSELECT a, b FROM (SELECT * FROM base_tbl) AS t; -- Subselect in rangetable CREATE VIEW rw_view14 ASSELECT ctid, a, b FROM base_tbl; -- System columns may be part of an updatable view CREATE VIEW rw_view15 ASSELECT a, upper(b) FROM base_tbl; -- Expression/function may be part of an updatable view CREATE VIEW rw_view16 ASSELECT a, b, a AS aa FROM base_tbl; -- Repeated column may be part of an updatable view CREATE VIEW ro_view17 ASSELECT * FROM ro_view1; -- Base relation not updatable CREATE VIEW ro_view18 ASSELECT * FROM (VALUES(1)) AS tmp(a); -- VALUES in rangetable CREATE SEQUENCE uv_seq; CREATE VIEW ro_view19 ASSELECT * FROM uv_seq; -- View based on a sequence CREATE VIEW ro_view20 ASSELECT a, b, generate_series(1, a) g FROM base_tbl; -- SRF in targetlist not supported
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name, ordinal_position;
-- Read-only views DELETEFROM ro_view1; DELETEFROM ro_view2; DELETEFROM ro_view3; DELETEFROM ro_view4; DELETEFROM ro_view5; DELETEFROM ro_view6; UPDATE ro_view7 SET a=a+1; UPDATE ro_view8 SET a=a+1; UPDATE ro_view9 SET a=a+1; UPDATE ro_view10 SET a=a+1; UPDATE ro_view11 SET a=a+1; UPDATE ro_view12 SET a=a+1; INSERTINTO ro_view13 VALUES (3, 'Row 3');
MERGE INTO ro_view13 AS t USING (VALUES (1, 'Row 1')) AS v(a,b) ON t.a = v.a WHEN MATCHED THENDELETE;
MERGE INTO ro_view13 AS t USING (VALUES (2, 'Row 2')) AS v(a,b) ON t.a = v.a WHEN MATCHED THENUPDATESET b = v.b;
MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a, v.b);
MERGE INTO ro_view13 AS t USING (VALUES (2, 'Row 2')) AS v(a,b) ON t.a = v.a WHEN MATCHED THEN DO NOTHING WHENNOT MATCHED THEN DO NOTHING; -- should be OK to do nothing
MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a WHEN MATCHED THEN DO NOTHING WHENNOT MATCHED THEN DO NOTHING; -- should be OK to do nothing -- Partially updatable view INSERTINTO rw_view14 VALUES (null, 3, 'Row 3'); -- should fail INSERTINTO rw_view14 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view14 SET ctid=nullWHERE a=3; -- should fail UPDATE rw_view14 SET b='ROW 3'WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETEFROM rw_view14 WHERE a=3; -- should be OK
MERGE INTO rw_view14 AS t USING (VALUES (2, 'Merged row 2'), (3, 'Merged row 3')) AS v(a,b) ON t.a = v.a WHEN MATCHED THENUPDATESET b = v.b -- should be OK, except... WHENNOT MATCHED THENINSERTVALUES (null, v.a, v.b); -- should fail
MERGE INTO rw_view14 AS t USING (VALUES (2, 'Merged row 2'), (3, 'Merged row 3')) AS v(a,b) ON t.a = v.a WHEN MATCHED THENUPDATESET b = v.b -- should be OK WHENNOT MATCHED THENINSERT (a,b) VALUES (v.a, v.b); -- should be OK SELECT * FROM base_tbl ORDERBY a;
MERGE INTO rw_view14 AS t USING (VALUES (2, 'Row 2'), (3, 'Row 3')) AS v(a,b) ON t.a = v.a WHEN MATCHED AND t.a = 2THENUPDATESET b = v.b -- should be OK WHEN MATCHED AND t.a = 3THENDELETE; -- should be OK SELECT * FROM base_tbl ORDERBY a; -- Partially updatable view INSERTINTO rw_view15 VALUES (3, 'ROW 3'); -- should fail INSERTINTO rw_view15 (a) VALUES (3); -- should be OK INSERTINTO rw_view15 (a) VALUES (3) ON CONFLICT DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERTINTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERTINTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATEset a = excluded.a; -- succeeds SELECT * FROM rw_view15; INSERTINTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATEset upper = 'blarg'; -- fails SELECT * FROM rw_view15; SELECT * FROM rw_view15; ALTER VIEW rw_view15 ALTERCOLUMN upper SETDEFAULT'NOT SET'; INSERTINTO rw_view15 (a) VALUES (4); -- should fail UPDATE rw_view15 SET upper='ROW 3'WHERE a=3; -- should fail UPDATE rw_view15 SET upper=DEFAULTWHERE a=3; -- should fail UPDATE rw_view15 SET a=4WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETEFROM rw_view15 WHERE a=4; -- should be OK -- Partially updatable view INSERTINTO rw_view16 VALUES (3, 'Row 3', 3); -- should fail INSERTINTO rw_view16 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view16 SET a=3, aa=-3WHERE a=3; -- should fail UPDATE rw_view16 SET aa=-3WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETEFROM rw_view16 WHERE a=-3; -- should be OK -- Read-only views INSERTINTO ro_view17 VALUES (3, 'ROW 3'); DELETEFROM ro_view18;
MERGE INTO ro_view18 AS t USING (VALUES (1, 'Row 1')) AS v(a,b) ON t.a = v.a WHEN MATCHED THEN DO NOTHING; -- should be OK to do nothing UPDATE ro_view19 SET last_value=1000; UPDATE ro_view20 SET b=upper(b);
-- A view with a conditional INSTEAD rule but no unconditional INSTEAD rules -- or INSTEAD OF triggers should be non-updatable and generate useful error -- messages with appropriate detail CREATE RULE rw_view16_ins_rule ASONINSERTTO rw_view16 WHERE NEW.a > 0 DO INSTEAD INSERTINTO base_tbl VALUES (NEW.a, NEW.b); CREATE RULE rw_view16_upd_rule ASONUPDATETO rw_view16 WHERE OLD.a > 0 DO INSTEAD UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a; CREATE RULE rw_view16_del_rule ASONDELETETO rw_view16 WHERE OLD.a > 0 DO INSTEAD DELETEFROM base_tbl WHERE a=OLD.a;
INSERTINTO rw_view16 (a, b) VALUES (3, 'Row 3'); -- should fail UPDATE rw_view16 SET b='ROW 2'WHERE a=2; -- should fail DELETEFROM rw_view16 WHERE a=2; -- should fail
MERGE INTO rw_view16 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a, v.b); -- should fail
DROPTABLE base_tbl CASCADE; DROP VIEW ro_view10, ro_view12, ro_view18; DROP SEQUENCE uv_seq CASCADE;
-- simple updatable view
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW rw_view1 AS SELECT *, 'Const'AS c, (SELECT concat('b: ', b)) AS d FROM base_tbl WHERE a>0;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1';
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1';
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDERBY ordinal_position;
INSERTINTO rw_view1 VALUES (3, 'Row 3'); INSERTINTO rw_view1 (a) VALUES (4); UPDATE rw_view1 SET a=5WHERE a=4; DELETEFROM rw_view1 WHERE b='Row 2'; SELECT * FROM base_tbl;
SET jit_above_cost = 0;
MERGE INTO rw_view1 t USING (VALUES (0, 'ROW 0'), (1, 'ROW 1'),
(2, 'ROW 2'), (3, 'ROW 3')) AS v(a,b) ON t.a = v.a WHEN MATCHED AND t.a <= 1THENUPDATESET b = v.b WHEN MATCHED THENDELETE WHENNOT MATCHED AND a > 0THENINSERT (a) VALUES (v.a)
RETURNING merge_action(), v.*, old, new, old.*, new.*, t.*;
SET jit_above_cost TODEFAULT;
SELECT * FROM base_tbl ORDERBY a;
MERGE INTO rw_view1 t USING (VALUES (0, 'R0'), (1, 'R1'),
(2, 'R2'), (3, 'R3')) AS v(a,b) ON t.a = v.a WHEN MATCHED AND t.a <= 1THENUPDATESET b = v.b WHEN MATCHED THENDELETE WHENNOT MATCHED BY SOURCE THENDELETE WHENNOT MATCHED AND a > 0THENINSERT (a) VALUES (v.a)
RETURNING merge_action(), v.*, old, new, old.*, new.*, t.*; SELECT * FROM base_tbl ORDERBY a;
EXPLAIN (costs off) UPDATE rw_view1 SET a=6WHERE a=5; EXPLAIN (costs off) DELETEFROM rw_view1 WHERE a=5;
EXPLAIN (costs off)
MERGE INTO rw_view1 t USING (VALUES (5, 'X')) AS v(a,b) ON t.a = v.a WHEN MATCHED THENDELETE;
EXPLAIN (costs off)
MERGE INTO rw_view1 t USING (SELECT * FROM generate_series(1,5)) AS s(a) ON t.a = s.a WHEN MATCHED THENUPDATESET b = 'Updated';
EXPLAIN (costs off)
MERGE INTO rw_view1 t USING (SELECT * FROM generate_series(1,5)) AS s(a) ON t.a = s.a WHENNOT MATCHED BY SOURCE THENDELETE;
EXPLAIN (costs off)
MERGE INTO rw_view1 t USING (SELECT * FROM generate_series(1,5)) AS s(a) ON t.a = s.a WHENNOT MATCHED THENINSERT (a) VALUES (s.a);
-- it's still updatable if we add a DO ALSO rule
CREATETABLE base_tbl_hist(ts timestamptz default now(), a int, b text);
CREATE RULE base_tbl_log ASONINSERTTO rw_view1 DO ALSO INSERTINTO base_tbl_hist(a,b) VALUES(new.a, new.b);
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1';
-- Check behavior with DEFAULTs (bug #17633)
INSERTINTO rw_view1 VALUES (9, DEFAULT), (10, DEFAULT); SELECT a, b FROM base_tbl_hist;
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW rw_view1 AS SELECT b AS bb, a AS aa, 'Const1'AS c FROM base_tbl WHERE a>0; CREATE VIEW rw_view2 AS SELECT aa AS aaa, bb AS bbb, c AS c1, 'Const2'AS c2 FROM rw_view1 WHERE aa<10;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2';
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2';
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDERBY ordinal_position;
INSERTINTO rw_view2 VALUES (3, 'Row 3'); INSERTINTO rw_view2 (aaa) VALUES (4); SELECT * FROM rw_view2; UPDATE rw_view2 SET bbb='Row 4'WHERE aaa=4; DELETEFROM rw_view2 WHERE aaa=2; SELECT * FROM rw_view2;
MERGE INTO rw_view2 t USING (VALUES (3, 'R3'), (4, 'R4'), (5, 'R5')) AS v(a,b) ON aaa = v.a WHEN MATCHED AND aaa = 3THENDELETE WHEN MATCHED THENUPDATESET bbb = v.b WHENNOT MATCHED THENINSERT (aaa) VALUES (v.a)
RETURNING merge_action(), v.*, (SELECT old), (SELECT (SELECT new)), t.*; SELECT * FROM rw_view2 ORDERBY aaa;
MERGE INTO rw_view2 t USING (VALUES (4, 'r4'), (5, 'r5'), (6, 'r6')) AS v(a,b) ON aaa = v.a WHEN MATCHED AND aaa = 4THENDELETE WHEN MATCHED THENUPDATESET bbb = v.b WHENNOT MATCHED THENINSERT (aaa) VALUES (v.a) WHENNOT MATCHED BY SOURCE THENUPDATESET bbb = 'Not matched by source'
RETURNING merge_action(), v.*, old, (SELECT new FROM (VALUES ((SELECT new)))), t.*; SELECT * FROM rw_view2 ORDERBY aaa;
EXPLAIN (costs off) UPDATE rw_view2 SET aaa=5WHERE aaa=4; EXPLAIN (costs off) DELETEFROM rw_view2 WHERE aaa=4;
DROPTABLE base_tbl CASCADE;
-- view on top of view with rules
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW rw_view1 ASSELECT * FROM base_tbl WHERE a>0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 ASSELECT * FROM rw_view1 WHERE a<10;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
CREATE RULE rw_view1_upd_rule ASONUPDATETO rw_view1
DO INSTEAD UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a RETURNING NEW.*;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
CREATE RULE rw_view1_del_rule ASONDELETETO rw_view1
DO INSTEAD DELETEFROM base_tbl WHERE a=OLD.a RETURNING OLD.*;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
INSERTINTO rw_view2 VALUES (3, 'Row 3') RETURNING old.*, new.*; UPDATE rw_view2 SET b='R3'WHERE a=3 RETURNING old.*, new.*; -- rule returns NEW DROP RULE rw_view1_upd_rule ON rw_view1; CREATE RULE rw_view1_upd_rule ASONUPDATETO rw_view1
DO INSTEAD UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a RETURNING *; UPDATE rw_view2 SET b='Row three'WHERE a=3 RETURNING old.*, new.*; SELECT * FROM rw_view2; DELETEFROM rw_view2 WHERE a=3 RETURNING old.*, new.*; SELECT * FROM rw_view2;
MERGE INTO rw_view2 t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a, v.b); -- should fail
EXPLAIN (costs off) UPDATE rw_view2 SET a=3WHERE a=2; EXPLAIN (costs off) DELETEFROM rw_view2 WHERE a=2;
DROPTABLE base_tbl CASCADE;
-- view on top of view with triggers
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW rw_view1 AS SELECT *, 'Const1'AS c1 FROM base_tbl WHERE a>0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 AS SELECT *, 'Const2'AS c2 FROM rw_view1 WHERE a<10;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into,
is_trigger_updatable, is_trigger_deletable,
is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
CREATE FUNCTION rw_view1_trig_fn()
RETURNS triggerAS
$$
BEGIN IF TG_OP = 'INSERT'THEN INSERTINTO base_tbl VALUES (NEW.a, NEW.b);
NEW.c1 = 'Trigger Const1'; RETURN NEW;
ELSIF TG_OP = 'UPDATE'THEN UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a;
NEW.c1 = 'Trigger Const1'; RETURN NEW;
ELSIF TG_OP = 'DELETE'THEN DELETEFROM base_tbl WHERE a=OLD.a; RETURN OLD;
END IF;
END;
$$
LANGUAGE plpgsql;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into,
is_trigger_updatable, is_trigger_deletable,
is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE'rw_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE'rw_view%' ORDERBY table_name, ordinal_position;
INSERTINTO rw_view2 VALUES (3, 'Row 3') RETURNING old.*, new.*; UPDATE rw_view2 SET b='Row three'WHERE a=3 RETURNING old.*, new.*; SELECT * FROM rw_view2; DELETEFROM rw_view2 WHERE a=3 RETURNING old.*, new.*; SELECT * FROM rw_view2;
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED AND t.a <= 1THENDELETE WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b)
RETURNING merge_action(), s.*, old, new, t.*; SELECT * FROM base_tbl ORDERBY a;
MERGE INTO rw_view2 t USING (SELECT x, 'r'||x FROM generate_series(0,2) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b) WHENNOT MATCHED BY SOURCE THENUPDATESET b = 'Not matched by source'
RETURNING merge_action(), s.*, old, new, t.*; SELECT * FROM base_tbl ORDERBY a;
EXPLAIN (costs off) UPDATE rw_view2 SET a=3WHERE a=2; EXPLAIN (costs off) DELETEFROM rw_view2 WHERE a=2;
EXPLAIN (costs off)
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED AND t.a <= 1THENDELETE WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b);
-- MERGE with incomplete set of INSTEAD OF triggers DROPTRIGGER rw_view1_del_trig ON rw_view1;
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED AND t.a <= 1THENDELETE WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b); -- should fail
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b); -- ok
DROPTRIGGER rw_view1_ins_trig ON rw_view1;
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b); -- should fail
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b; -- ok
-- MERGE with INSTEAD OF triggers on auto-updatable view CREATETRIGGER rw_view2_upd_trig INSTEAD OF UPDATEON rw_view2 FOREACH ROW EXECUTE PROCEDURE rw_view1_trig_fn();
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b WHENNOT MATCHED AND s.a > 0THENINSERTVALUES (s.a, s.b); -- should fail
MERGE INTO rw_view2 t USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a WHEN MATCHED THENUPDATESET b = s.b; -- ok SELECT * FROM base_tbl ORDERBY a;
DROPTABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn();
-- update using whole row from view
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);
CREATE VIEW rw_view1 ASSELECT b AS bb, a AS aa FROM base_tbl;
CREATE FUNCTION rw_view1_aa(x rw_view1)
RETURNS intAS $$ SELECT x.aa $$ LANGUAGE sql;
UPDATE rw_view1 v SET bb='Updated row 2'WHERE rw_view1_aa(v)=2
RETURNING rw_view1_aa(v), v.bb; SELECT * FROM base_tbl;
EXPLAIN (costs off) UPDATE rw_view1 v SET bb='Updated row 2'WHERE rw_view1_aa(v)=2
RETURNING rw_view1_aa(v), v.bb;
DROPTABLE base_tbl CASCADE;
-- permissions checks
CREATE USER regress_view_user1; CREATE USER regress_view_user2; CREATE USER regress_view_user3;
SET SESSION AUTHORIZATION regress_view_user1; CREATETABLE base_tbl(a int, b text, c float); INSERTINTO base_tbl VALUES (1, 'Row 1', 1.0); CREATE VIEW rw_view1 ASSELECT b AS bb, c AS cc, a AS aa FROM base_tbl; INSERTINTO rw_view1 VALUES ('Row 2', 2.0, 2);
GRANTSELECTON base_tbl TO regress_view_user2; GRANTSELECTON rw_view1 TO regress_view_user2; GRANTUPDATE (a,c) ON base_tbl TO regress_view_user2; GRANTUPDATE (bb,cc) ON rw_view1 TO regress_view_user2;
RESET SESSION AUTHORIZATION;
SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 ASSELECT b AS bb, c AS cc, a AS aa FROM base_tbl; SELECT * FROM base_tbl; -- ok SELECT * FROM rw_view1; -- ok SELECT * FROM rw_view2; -- ok
INSERTINTO base_tbl VALUES (3, 'Row 3', 3.0); -- not allowed INSERTINTO rw_view1 VALUES ('Row 3', 3.0, 3); -- not allowed INSERTINTO rw_view2 VALUES ('Row 3', 3.0, 3); -- not allowed
MERGE INTO rw_view1 t USING (VALUES ('Row 3', 3.0, 3)) AS v(b,c,a) ON t.aa = v.a WHENNOT MATCHED THENINSERTVALUES (v.b, v.c, v.a); -- not allowed
MERGE INTO rw_view2 t USING (VALUES ('Row 3', 3.0, 3)) AS v(b,c,a) ON t.aa = v.a WHENNOT MATCHED THENINSERTVALUES (v.b, v.c, v.a); -- not allowed
UPDATE base_tbl SET a=a, c=c; -- ok UPDATE base_tbl SET b=b; -- not allowed UPDATE rw_view1 SET bb=bb, cc=cc; -- ok UPDATE rw_view1 SET aa=aa; -- not allowed UPDATE rw_view2 SET aa=aa, cc=cc; -- ok UPDATE rw_view2 SET bb=bb; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET bb = bb, cc = cc; -- ok
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET aa = aa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET aa = aa, cc = cc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET bb = bb; -- not allowed
DELETEFROM base_tbl; -- not allowed DELETEFROM rw_view1; -- not allowed DELETEFROM rw_view2; -- not allowed
RESET SESSION AUTHORIZATION;
SET SESSION AUTHORIZATION regress_view_user1; GRANTINSERT, DELETEON base_tbl TO regress_view_user2;
RESET SESSION AUTHORIZATION;
SET SESSION AUTHORIZATION regress_view_user2; INSERTINTO base_tbl VALUES (3, 'Row 3', 3.0); -- ok INSERTINTO rw_view1 VALUES ('Row 4', 4.0, 4); -- not allowed INSERTINTO rw_view2 VALUES ('Row 4', 4.0, 4); -- ok DELETEFROM base_tbl WHERE a=1; -- ok DELETEFROM rw_view1 WHERE aa=2; -- not allowed DELETEFROM rw_view2 WHERE aa=2; -- ok
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED AND bb = 'xxx'THENDELETE; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED AND bb = 'xxx'THENDELETE; -- ok SELECT * FROM base_tbl;
RESET SESSION AUTHORIZATION;
SET SESSION AUTHORIZATION regress_view_user1; REVOKEINSERT, DELETEON base_tbl FROM regress_view_user2; GRANTINSERT, DELETEON rw_view1 TO regress_view_user2;
RESET SESSION AUTHORIZATION;
SET SESSION AUTHORIZATION regress_view_user2; INSERTINTO base_tbl VALUES (5, 'Row 5', 5.0); -- not allowed INSERTINTO rw_view1 VALUES ('Row 5', 5.0, 5); -- ok INSERTINTO rw_view2 VALUES ('Row 6', 6.0, 6); -- not allowed DELETEFROM base_tbl WHERE a=3; -- not allowed DELETEFROM rw_view1 WHERE aa=3; -- ok DELETEFROM rw_view2 WHERE aa=4; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED AND bb = 'xxx'THENDELETE; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED AND bb = 'xxx'THENDELETE; -- not allowed SELECT * FROM base_tbl;
RESET SESSION AUTHORIZATION;
DROPTABLE base_tbl CASCADE;
-- nested-view permissions
CREATETABLE base_tbl(a int, b text, c float); INSERTINTO base_tbl VALUES (1, 'Row 1', 1.0);
SET SESSION AUTHORIZATION regress_view_user1; CREATE VIEW rw_view1 ASSELECT * FROM base_tbl; SELECT * FROM rw_view1; -- not allowed SELECT * FROM rw_view1 FORUPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo'WHERE a = 1; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 ASSELECT * FROM rw_view1; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FORUPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar'WHERE a = 1; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
RESET SESSION AUTHORIZATION; GRANTSELECTON base_tbl TO regress_view_user1;
SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FORUPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo'WHERE a = 1; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FORUPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar'WHERE a = 1; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTSELECTON rw_view1 TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FORUPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar'WHERE a = 1; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
RESET SESSION AUTHORIZATION; GRANTUPDATEON base_tbl TO regress_view_user1;
SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FORUPDATE; UPDATE rw_view1 SET b = 'foo'WHERE a = 1;
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo';
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FORUPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar'WHERE a = 1; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'bar'; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTUPDATEON rw_view1 TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FORUPDATE; UPDATE rw_view2 SET b = 'bar'WHERE a = 1;
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'fud';
RESET SESSION AUTHORIZATION; REVOKEUPDATEON base_tbl FROM regress_view_user1;
SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FORUPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo'WHERE a = 1; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FORUPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar'WHERE a = 1; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET b = 'foo'; -- not allowed
RESET SESSION AUTHORIZATION;
DROPTABLE base_tbl CASCADE;
-- security invoker view permissions
SET SESSION AUTHORIZATION regress_view_user1; CREATETABLE base_tbl(a int, b text, c float); INSERTINTO base_tbl VALUES (1, 'Row 1', 1.0); CREATE VIEW rw_view1 ASSELECT b AS bb, c AS cc, a AS aa FROM base_tbl; ALTER VIEW rw_view1 SET (security_invoker = true); INSERTINTO rw_view1 VALUES ('Row 2', 2.0, 2); GRANTSELECTON rw_view1 TO regress_view_user2; GRANTUPDATE (bb,cc) ON rw_view1 TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM base_tbl; -- not allowed SELECT * FROM rw_view1; -- not allowed INSERTINTO base_tbl VALUES (3, 'Row 3', 3.0); -- not allowed INSERTINTO rw_view1 VALUES ('Row 3', 3.0, 3); -- not allowed UPDATE base_tbl SET a=a; -- not allowed UPDATE rw_view1 SET bb=bb, cc=cc; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET bb = bb; -- not allowed DELETEFROM base_tbl; -- not allowed DELETEFROM rw_view1; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENDELETE; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTSELECTON base_tbl TO regress_view_user2; GRANTUPDATE (a,c) ON base_tbl TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM base_tbl; -- ok SELECT * FROM rw_view1; -- ok UPDATE base_tbl SET a=a, c=c; -- ok UPDATE base_tbl SET b=b; -- not allowed UPDATE rw_view1 SET cc=cc; -- ok
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET cc = cc; -- ok UPDATE rw_view1 SET aa=aa; -- not allowed UPDATE rw_view1 SET bb=bb; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET aa = aa; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET bb = bb; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTINSERT, DELETEON base_tbl TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; INSERTINTO base_tbl VALUES (3, 'Row 3', 3.0); -- ok INSERTINTO rw_view1 VALUES ('Row 4', 4.0, 4); -- not allowed DELETEFROM base_tbl WHERE a=1; -- ok DELETEFROM rw_view1 WHERE aa=2; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENDELETE; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; REVOKEINSERT, DELETEON base_tbl FROM regress_view_user2; GRANTINSERT, DELETEON rw_view1 TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; INSERTINTO rw_view1 VALUES ('Row 4', 4.0, 4); -- not allowed DELETEFROM rw_view1 WHERE aa=2; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENDELETE; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTINSERT, DELETEON base_tbl TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; INSERTINTO rw_view1 VALUES ('Row 4', 4.0, 4); -- ok DELETEFROM rw_view1 WHERE aa=2; -- ok
MERGE INTO rw_view1 t USING (VALUES (3)) AS v(a) ON t.aa = v.a WHEN MATCHED THENDELETE; -- ok SELECT * FROM base_tbl; -- ok
RESET SESSION AUTHORIZATION;
DROPTABLE base_tbl CASCADE;
-- ordinary view on top of security invoker view permissions
CREATETABLE base_tbl(a int, b text, c float); INSERTINTO base_tbl VALUES (1, 'Row 1', 1.0);
SET SESSION AUTHORIZATION regress_view_user1; CREATE VIEW rw_view1 ASSELECT b AS bb, c AS cc, a AS aa FROM base_tbl; ALTER VIEW rw_view1 SET (security_invoker = true); SELECT * FROM rw_view1; -- not allowed UPDATE rw_view1 SET aa=aa; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (2, 'Row 2', 2.0)) AS v(a,b,c) ON t.aa = v.a WHENNOT MATCHED THENINSERTVALUES (v.b, v.c, v.a); -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 ASSELECT cc AS ccc, aa AS aaa, bb AS bbb FROM rw_view1; GRANTSELECT, UPDATEON rw_view2 TO regress_view_user3; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET aaa=aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (2, 'Row 2', 2.0)) AS v(a,b,c) ON t.aaa = v.a WHENNOT MATCHED THENINSERTVALUES (v.c, v.a, v.b); -- not allowed
RESET SESSION AUTHORIZATION;
GRANTSELECTON base_tbl TO regress_view_user1; GRANTUPDATE (a, b) ON base_tbl TO regress_view_user1;
SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; -- ok UPDATE rw_view1 SET aa=aa, bb=bb; -- ok UPDATE rw_view1 SET cc=cc; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET aa = aa, bb = bb; -- ok
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET cc = cc; -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET aaa=aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET aaa=aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
SET SESSION AUTHORIZATION regress_view_user1; GRANTSELECTON rw_view1 TO regress_view_user2; GRANTUPDATE (bb, cc) ON rw_view1 TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
RESET SESSION AUTHORIZATION;
GRANTSELECTON base_tbl TO regress_view_user2; GRANTUPDATE (a, c) ON base_tbl TO regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- ok UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- ok
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- not allowed
RESET SESSION AUTHORIZATION;
GRANTSELECTON base_tbl TO regress_view_user3; GRANTUPDATE (a, c) ON base_tbl TO regress_view_user3;
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- ok UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- ok
RESET SESSION AUTHORIZATION;
REVOKESELECT, UPDATEON base_tbl FROM regress_view_user1;
SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; -- not allowed UPDATE rw_view1 SET aa=aa; -- not allowed
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.aa = v.a WHEN MATCHED THENUPDATESET aa = aa; -- not allowed
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- ok UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- ok
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- ok UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- ok
RESET SESSION AUTHORIZATION;
REVOKESELECT, UPDATEON base_tbl FROM regress_view_user2;
SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- not allowed
SET SESSION AUTHORIZATION regress_view_user3; SELECT * FROM rw_view2; -- ok UPDATE rw_view2 SET aaa=aaa; -- not allowed UPDATE rw_view2 SET bbb=bbb; -- not allowed UPDATE rw_view2 SET ccc=ccc; -- ok
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET aaa = aaa; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET bbb = bbb; -- not allowed
MERGE INTO rw_view2 t USING (VALUES (1)) AS v(a) ON t.aaa = v.a WHEN MATCHED THENUPDATESET ccc = ccc; -- ok
RESET SESSION AUTHORIZATION;
DROPTABLE base_tbl CASCADE;
DROP USER regress_view_user1; DROP USER regress_view_user2; DROP USER regress_view_user3;
-- column defaults
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified', c serial); INSERTINTO base_tbl VALUES (1, 'Row 1'); INSERTINTO base_tbl VALUES (2, 'Row 2'); INSERTINTO base_tbl VALUES (3);
CREATE VIEW rw_view1 ASSELECT a AS aa, b AS bb FROM base_tbl; ALTER VIEW rw_view1 ALTERCOLUMN bb SETDEFAULT'View default';
INSERTINTO rw_view1 VALUES (4, 'Row 4'); INSERTINTO rw_view1 (aa) VALUES (5);
MERGE INTO rw_view1 t USING (VALUES (6)) AS v(a) ON t.aa = v.a WHENNOT MATCHED THENINSERT (aa) VALUES (v.a);
SELECT * FROM base_tbl;
DROPTABLE base_tbl CASCADE;
-- Table having triggers
CREATETABLE base_tbl (a intPRIMARYKEY, b text DEFAULT'Unspecified'); INSERTINTO base_tbl VALUES (1, 'Row 1'); INSERTINTO base_tbl VALUES (2, 'Row 2');
CREATE FUNCTION rw_view1_trig_fn()
RETURNS triggerAS
$$
BEGIN IF TG_OP = 'INSERT'THEN UPDATE base_tbl SET b=NEW.b WHERE a=1; RETURNNULL;
END IF; RETURNNULL;
END;
$$
LANGUAGE plpgsql;
CREATETRIGGER rw_view1_ins_trig AFTER INSERTON base_tbl FOREACH ROW EXECUTE PROCEDURE rw_view1_trig_fn();
CREATE VIEW rw_view1 ASSELECT a AS aa, b AS bb FROM base_tbl;
INSERTINTO rw_view1 VALUES (3, 'Row 3'); select * from base_tbl;
DROP VIEW rw_view1; DROPTRIGGER rw_view1_ins_trig on base_tbl; DROP FUNCTION rw_view1_trig_fn(); DROPTABLE base_tbl;
-- view with ORDER BY
CREATETABLE base_tbl (a int, b int); INSERTINTO base_tbl VALUES (1,2), (4,5), (3,-3);
CREATE VIEW rw_view1 ASSELECT * FROM base_tbl ORDERBY a+b;
SELECT * FROM rw_view1;
INSERTINTO rw_view1 VALUES (7,-8); SELECT * FROM rw_view1;
EXPLAIN (verbose, costs off) UPDATE rw_view1 SET b = b + 1 RETURNING *; UPDATE rw_view1 SET b = b + 1 RETURNING *; SELECT * FROM rw_view1;
CREATE VIEW rw_view1 AS SELECT ctid, sin(a) s, a, cos(a) c FROM base_tbl WHERE a != 0 ORDERBY abs(a);
INSERTINTO rw_view1 VALUES (null, null, 1.1, null); -- should fail INSERTINTO rw_view1 (s, c, a) VALUES (null, null, 1.1); -- should fail INSERTINTO rw_view1 (s, c, a) VALUES (default, default, 1.1); -- should fail INSERTINTO rw_view1 (a) VALUES (1.1) RETURNING a, s, c; -- OK UPDATE rw_view1 SET s = s WHERE a = 1.1; -- should fail UPDATE rw_view1 SET a = 1.05WHERE a = 1.1 RETURNING s; -- OK DELETEFROM rw_view1 WHERE a = 1.05; -- OK
CREATE VIEW rw_view2 AS SELECT s, c, s/c t, a base_a, ctid FROM rw_view1;
INSERTINTO rw_view2 VALUES (null, null, null, 1.1, null); -- should fail INSERTINTO rw_view2(s, c, base_a) VALUES (null, null, 1.1); -- should fail INSERTINTO rw_view2(base_a) VALUES (1.1) RETURNING t; -- OK UPDATE rw_view2 SET s = s WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET t = t WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET base_a = 1.05WHERE base_a = 1.1; -- OK DELETEFROM rw_view2 WHERE base_a = 1.05 RETURNING base_a, s, c, t; -- OK
CREATE VIEW rw_view3 AS SELECT s, c, s/c t, ctid FROM rw_view1;
INSERTINTO rw_view3 VALUES (null, null, null, null); -- should fail INSERTINTO rw_view3(s) VALUES (null); -- should fail UPDATE rw_view3 SET s = s; -- should fail DELETEFROM rw_view3 WHERE s = sin(0.1); -- should be OK SELECT * FROM base_tbl ORDERBY a;
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name;
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name;
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDERBY table_name, ordinal_position;
UPDATE rw_view1 SET a = a*10WHERE a IN (-1, 1); -- Should produce -10 and 10 UPDATE ONLY rw_view1 SET a = a*10WHERE a IN (-2, 2); -- Should produce -20 and 20 UPDATE rw_view2 SET a = a*10WHERE a IN (-3, 3); -- Should produce -30 only UPDATE ONLY rw_view2 SET a = a*10WHERE a IN (-4, 4); -- Should produce -40 only
DELETEFROM rw_view1 WHERE a IN (-5, 5); -- Should delete -5 and 5 DELETEFROM ONLY rw_view1 WHERE a IN (-6, 6); -- Should delete -6 and 6 DELETEFROM rw_view2 WHERE a IN (-7, 7); -- Should delete -7 only DELETEFROM ONLY rw_view2 WHERE a IN (-8, 8); -- Should delete -8 only
SELECT * FROM ONLY base_tbl_parent ORDERBY a; SELECT * FROM base_tbl_child ORDERBY a;
MERGE INTO rw_view1 t USING (VALUES (-200), (10)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a+1; -- Should produce -199 and 11
MERGE INTO ONLY rw_view1 t USING (VALUES (-100), (20)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a+1; -- Should produce -99 and 21
MERGE INTO rw_view2 t USING (VALUES (-40), (3)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a+1; -- Should produce -39 only
MERGE INTO ONLY rw_view2 t USING (VALUES (-30), (4)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a+1; -- Should produce -29 only
SELECT * FROM ONLY base_tbl_parent ORDERBY a; SELECT * FROM base_tbl_child ORDERBY a;
EXPLAIN (costs off) UPDATE rw_view1 SET a = a + 1000FROM other_tbl_parent WHERE a = id; UPDATE rw_view1 SET a = a + 1000FROM other_tbl_parent WHERE a = id;
SELECT * FROM ONLY base_tbl_parent ORDERBY a; SELECT * FROM base_tbl_child ORDERBY a;
CREATETABLE base_tbl (a int, b intDEFAULT10); INSERTINTO base_tbl VALUES (1,2), (2,3), (1,-1);
CREATE VIEW rw_view1 ASSELECT * FROM base_tbl WHERE a < b WITH LOCAL CHECKOPTION;
\d+ rw_view1 SELECT * FROM information_schema.views WHERE table_name = 'rw_view1';
INSERTINTO rw_view1 VALUES(3,4); -- ok INSERTINTO rw_view1 VALUES(4,3); -- should fail INSERTINTO rw_view1 VALUES(5,null); -- should fail UPDATE rw_view1 SET b = 5WHERE a = 3; -- ok UPDATE rw_view1 SET b = -5WHERE a = 3; -- should fail INSERTINTO rw_view1(a) VALUES (9); -- ok INSERTINTO rw_view1(a) VALUES (10); -- should fail SELECT * FROM base_tbl ORDERBY a, b;
MERGE INTO rw_view1 t USING (VALUES (10)) AS v(a) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a, v.a + 1); -- ok
MERGE INTO rw_view1 t USING (VALUES (11)) AS v(a) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a, v.a - 1); -- should fail
MERGE INTO rw_view1 t USING (VALUES (1)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a - 1; -- ok
MERGE INTO rw_view1 t USING (VALUES (2)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a + 1; -- should fail SELECT * FROM base_tbl ORDERBY a, b;
DROPTABLE base_tbl CASCADE;
-- WITH LOCAL/CASCADED CHECK OPTION
CREATETABLE base_tbl (a int);
CREATE VIEW rw_view1 ASSELECT * FROM base_tbl WHERE a > 0; CREATE VIEW rw_view2 ASSELECT * FROM rw_view1 WHERE a < 10 WITHCHECKOPTION; -- implicitly cascaded
\d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2';
INSERTINTO rw_view2 VALUES (-5); -- should fail INSERTINTO rw_view2 VALUES (5); -- ok INSERTINTO rw_view2 VALUES (15); -- should fail SELECT * FROM base_tbl;
UPDATE rw_view2 SET a = a - 10; -- should fail UPDATE rw_view2 SET a = a + 10; -- should fail
CREATEORREPLACE VIEW rw_view2 ASSELECT * FROM rw_view1 WHERE a < 10 WITH LOCAL CHECKOPTION;
\d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2';
INSERTINTO rw_view2 VALUES (-10); -- ok, but not in view INSERTINTO rw_view2 VALUES (20); -- should fail SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid ALTER VIEW rw_view1 SET (check_option=local);
INSERTINTO rw_view2 VALUES (-20); -- should fail INSERTINTO rw_view2 VALUES (30); -- should fail
ALTER VIEW rw_view2 RESET (check_option);
\d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERTINTO rw_view2 VALUES (30); -- ok, but not in view SELECT * FROM base_tbl;
DROPTABLE base_tbl CASCADE;
-- WITH CHECK OPTION with no local view qual
CREATETABLE base_tbl (a int);
CREATE VIEW rw_view1 ASSELECT * FROM base_tbl WITHCHECKOPTION; CREATE VIEW rw_view2 ASSELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 ASSELECT * FROM rw_view2 WITHCHECKOPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_'ORDERBY table_name;
INSERTINTO rw_view1 VALUES (-1); -- ok INSERTINTO rw_view1 VALUES (1); -- ok INSERTINTO rw_view2 VALUES (-2); -- ok, but not in view INSERTINTO rw_view2 VALUES (2); -- ok INSERTINTO rw_view3 VALUES (-3); -- should fail INSERTINTO rw_view3 VALUES (3); -- ok
DROPTABLE base_tbl CASCADE;
-- WITH CHECK OPTION with scalar array ops
CREATETABLE base_tbl (a int, b int[]); CREATE VIEW rw_view1 ASSELECT * FROM base_tbl WHERE a = ANY (b) WITHCHECKOPTION;
INSERTINTO rw_view1 VALUES (1, ARRAY[1,2,3]); -- ok INSERTINTO rw_view1 VALUES (10, ARRAY[4,5]); -- should fail
UPDATE rw_view1 SET b[2] = -b[2] WHERE a = 1; -- ok UPDATE rw_view1 SET b[1] = -b[1] WHERE a = 1; -- should fail
CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0WITH LOCAL CHECKOPTION;
INSERTINTO rw_view2 VALUES (-5); -- should fail
MERGE INTO rw_view2 t USING (VALUES (-5)) AS v(a) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a); -- should fail INSERTINTO rw_view2 VALUES (5); -- ok
MERGE INTO rw_view2 t USING (VALUES (6)) AS v(a) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a); -- ok INSERTINTO rw_view2 VALUES (50); -- ok, but not in view
MERGE INTO rw_view2 t USING (VALUES (60)) AS v(a) ON t.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a); -- ok, but not in view UPDATE rw_view2 SET a = a - 10; -- should fail
MERGE INTO rw_view2 t USING (VALUES (6)) AS v(a) ON t.a = v.a WHEN MATCHED THENUPDATESET a = t.a - 10; -- should fail SELECT * FROM base_tbl;
-- Check option won't cascade down to base view with INSTEAD OF triggers
ALTER VIEW rw_view2 SET (check_option=cascaded); INSERTINTO rw_view2 VALUES (100); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 200WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) SELECT * FROM base_tbl;
-- Neither local nor cascaded check options work with INSTEAD rules
DROPTRIGGER rw_view1_trig ON rw_view1; CREATE RULE rw_view1_ins_rule ASONINSERTTO rw_view1
DO INSTEAD INSERTINTO base_tbl VALUES (NEW.a, 10); CREATE RULE rw_view1_upd_rule ASONUPDATETO rw_view1
DO INSTEAD UPDATE base_tbl SET a=NEW.a WHERE a=OLD.a; INSERTINTO rw_view2 VALUES (-10); -- ok, but not in view (doesn't fail rw_view2's check) INSERTINTO rw_view2 VALUES (5); -- ok INSERTINTO rw_view2 VALUES (20); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 30WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) INSERTINTO rw_view2 VALUES (5); -- ok UPDATE rw_view2 SET a = -5WHERE a = 5; -- ok, but not in view (doesn't fail rw_view2's check) SELECT * FROM base_tbl;
DROPTABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn();
CREATETABLE base_tbl (a int); CREATE VIEW rw_view1 ASSELECT a,10AS b FROM base_tbl; CREATE RULE rw_view1_ins_rule ASONINSERTTO rw_view1
DO INSTEAD INSERTINTO base_tbl VALUES (NEW.a); CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > b WITH LOCAL CHECKOPTION; INSERTINTO rw_view2 VALUES (2,3); -- ok, but not in view (doesn't fail rw_view2's check) DROPTABLE base_tbl CASCADE;
CREATE VIEW rw_view1 AS SELECT person FROM base_tbl WHERE visibility = 'public';
CREATE FUNCTION snoop(anyelement)
RETURNS boolean AS
$$
BEGIN
RAISE NOTICE 'snooped value: %', $1; RETURNtrue;
END;
$$
LANGUAGE plpgsql COST 0.000001;
CREATEORREPLACE FUNCTION leakproof(anyelement)
RETURNS boolean AS
$$
BEGIN RETURNtrue;
END;
$$
LANGUAGE plpgsql STRICT IMMUTABLE LEAKPROOF;
SELECT * FROM rw_view1 WHERE snoop(person); UPDATE rw_view1 SET person=person WHERE snoop(person); DELETEFROM rw_view1 WHERENOT snoop(person);
ALTER VIEW rw_view1 SET (security_barrier = true);
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1';
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1';
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDERBY ordinal_position;
SELECT * FROM rw_view1 WHERE snoop(person); UPDATE rw_view1 SET person=person WHERE snoop(person); DELETEFROM rw_view1 WHERENOT snoop(person);
MERGE INTO rw_view1 t USING (VALUES ('Tom'), ('Dick'), ('Harry')) AS v(person) ON t.person = v.person WHEN MATCHED AND snoop(t.person) THENUPDATESET person = v.person;
EXPLAIN (costs off) SELECT * FROM rw_view1 WHERE snoop(person); EXPLAIN (costs off) UPDATE rw_view1 SET person=person WHERE snoop(person); EXPLAIN (costs off) DELETEFROM rw_view1 WHERENOT snoop(person); EXPLAIN (costs off)
MERGE INTO rw_view1 t USING (VALUES ('Tom'), ('Dick'), ('Harry')) AS v(person) ON t.person = v.person WHEN MATCHED AND snoop(t.person) THENUPDATESET person = v.person;
-- security barrier view on top of security barrier view
CREATE VIEW rw_view2 WITH (security_barrier = true) AS SELECT * FROM rw_view1 WHERE snoop(person);
SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2';
SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2';
SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDERBY ordinal_position;
SELECT * FROM rw_view2 WHERE snoop(person); UPDATE rw_view2 SET person=person WHERE snoop(person); DELETEFROM rw_view2 WHERENOT snoop(person);
MERGE INTO rw_view2 t USING (VALUES ('Tom'), ('Dick'), ('Harry')) AS v(person) ON t.person = v.person WHEN MATCHED AND snoop(t.person) THENUPDATESET person = v.person;
EXPLAIN (costs off) SELECT * FROM rw_view2 WHERE snoop(person); EXPLAIN (costs off) UPDATE rw_view2 SET person=person WHERE snoop(person); EXPLAIN (costs off) DELETEFROM rw_view2 WHERENOT snoop(person); EXPLAIN (costs off)
MERGE INTO rw_view2 t USING (VALUES ('Tom'), ('Dick'), ('Harry')) AS v(person) ON t.person = v.person WHEN MATCHED AND snoop(t.person) THENUPDATESET person = v.person;
DROPTABLE base_tbl CASCADE;
-- security barrier view on top of table with rules
CREATE RULE base_tbl_ins_rule ASONINSERTTO base_tbl WHEREEXISTS (SELECT1FROM base_tbl t WHERE t.id = new.id)
DO INSTEAD UPDATE base_tbl SET data = new.data, deleted = falseWHERE id = new.id;
CREATE RULE base_tbl_del_rule ASONDELETETO base_tbl
DO INSTEAD UPDATE base_tbl SET deleted = trueWHERE id = old.id;
CREATE VIEW rw_view1 WITH (security_barrier=true) AS SELECT id, data FROM base_tbl WHERENOT deleted;
SELECT * FROM rw_view1;
EXPLAIN (costs off) DELETEFROM rw_view1 WHERE id = 1AND snoop(data); DELETEFROM rw_view1 WHERE id = 1AND snoop(data);
-- security barrier view based on inheritance set CREATETABLE t1 (a int, b float, c text); CREATEINDEX t1_a_idx ON t1(a); INSERTINTO t1 SELECT i,i,'t1'FROM generate_series(1,10) g(i); ANALYZE t1;
CREATETABLE t12 (e int[]) INHERITS (t1); CREATEINDEX t12_a_idx ON t12(a); INSERTINTO t12 SELECT i,i,'t12','{1,2}'::int[] FROM generate_series(1,10) g(i); ANALYZE t12;
CREATETABLE t111 () INHERITS (t11, t12); CREATEINDEX t111_a_idx ON t111(a); INSERTINTO t111 SELECT i,i,'t111','t111d','{1,1,1}'::int[] FROM generate_series(1,10) g(i); ANALYZE t111;
CREATE VIEW v1 WITH (security_barrier=true) AS SELECT *, (SELECT d FROM t11 WHERE t11.a = t1.a LIMIT1) AS d FROM t1 WHERE a > 5ANDEXISTS(SELECT1FROM t12 WHERE t12.a = t1.a);
SELECT * FROM v1 WHERE a=3; -- should not see anything SELECT * FROM v1 WHERE a=8;
EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=100WHERE snoop(a) AND leakproof(a) AND a < 7AND a != 6; UPDATE v1 SET a=100WHERE snoop(a) AND leakproof(a) AND a < 7AND a != 6;
SELECT * FROM v1 WHERE a=100; -- Nothing should have been changed to 100 SELECT * FROM t1 WHERE a=100; -- Nothing should have been changed to 100
EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=a+1WHERE snoop(a) AND leakproof(a) AND a = 8; UPDATE v1 SET a=a+1WHERE snoop(a) AND leakproof(a) AND a = 8;
SELECT * FROM v1 WHERE b=8;
DELETEFROM v1 WHERE snoop(a) AND leakproof(a); -- should not delete everything, just where a>5
TABLE t1; -- verify all a<=5 are intact
DROPTABLE t1, t11, t12, t111 CASCADE; DROP FUNCTION snoop(anyelement); DROP FUNCTION leakproof(anyelement);
CREATETABLE tx1 (a integer); CREATETABLE tx2 (b integer); CREATETABLE tx3 (c integer); CREATE VIEW vx1 ASSELECT a FROM tx1 WHEREEXISTS(SELECT1FROM tx2 JOIN tx3 ON b=c); INSERTINTO vx1 values (1); SELECT * FROM tx1; SELECT * FROM vx1;
DROP VIEW vx1; DROPTABLE tx1; DROPTABLE tx2; DROPTABLE tx3;
CREATETABLE tx1 (a integer); CREATETABLE tx2 (b integer); CREATETABLE tx3 (c integer); CREATE VIEW vx1 ASSELECT a FROM tx1 WHEREEXISTS(SELECT1FROM tx2 JOIN tx3 ON b=c); INSERTINTO vx1 VALUES (1); INSERTINTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1;
DROP VIEW vx1; DROPTABLE tx1; DROPTABLE tx2; DROPTABLE tx3;
CREATETABLE tx1 (a integer, b integer); CREATETABLE tx2 (b integer, c integer); CREATETABLE tx3 (c integer, d integer); ALTERTABLE tx1 DROPCOLUMN b; ALTERTABLE tx2 DROPCOLUMN c; ALTERTABLE tx3 DROPCOLUMN d; CREATE VIEW vx1 ASSELECT a FROM tx1 WHEREEXISTS(SELECT1FROM tx2 JOIN tx3 ON b=c); INSERTINTO vx1 VALUES (1); INSERTINTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1;
DROP VIEW vx1; DROPTABLE tx1; DROPTABLE tx2; DROPTABLE tx3;
-- -- Test handling of vars from correlated subqueries in quals from outer -- security barrier views, per bug #13988 -- CREATETABLE t1 (a int, b text, c int); INSERTINTO t1 VALUES (1, 'one', 10);
CREATE VIEW v1 WITH (security_barrier = true) AS SELECT * FROM t1 WHERE (a > 0) WITHCHECKOPTION;
CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHEREEXISTS (SELECT1FROM t2 WHERE t2.cc = v1.c) WITHCHECKOPTION;
INSERTINTO v2 VALUES (2, 'two', 20); -- ok INSERTINTO v2 VALUES (-2, 'minus two', 20); -- not allowed INSERTINTO v2 VALUES (3, 'three', 30); -- not allowed
UPDATE v2 SET b = 'ONE'WHERE a = 1; -- ok UPDATE v2 SET a = -1WHERE a = 1; -- not allowed UPDATE v2 SET c = 30WHERE a = 1; -- not allowed
DELETEFROM v2 WHERE a = 2; -- ok SELECT * FROM v2;
DROP VIEW v2; DROP VIEW v1; DROPTABLE t2; DROPTABLE t1;
-- -- Test sub-select in nested security barrier views, per bug #17972 -- CREATETABLE t1 (a int); CREATE VIEW v1 WITH (security_barrier = true) AS SELECT * FROM t1; CREATE RULE v1_upd_rule ASONUPDATETO v1 DO INSTEAD UPDATE t1 SET a = NEW.a WHERE a = OLD.a; CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHEREEXISTS (SELECT1);
EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1;
DROP VIEW v2; DROP VIEW v1; DROPTABLE t1;
-- -- Test CREATE OR REPLACE VIEW turning a non-updatable view into an -- auto-updatable view and adding check options in a single step -- CREATETABLE t1 (a int, b text); CREATE VIEW v1 ASSELECTnull::intAS a; CREATEORREPLACE VIEW v1 ASSELECT * FROM t1 WHERE a > 0WITHCHECKOPTION;
INSERTINTO v1 VALUES (1, 'ok'); -- ok INSERTINTO v1 VALUES (-1, 'invalid'); -- should fail
DROP VIEW v1; DROPTABLE t1;
-- check that an auto-updatable view on a partitioned table works correctly createtable uv_pt (a int, b int, v varchar) partition by range (a, b); createtable uv_pt1 (b intnotnull, v varchar, a intnotnull) partition by range (b); createtable uv_pt11 (like uv_pt1); altertable uv_pt11 drop a; altertable uv_pt11 add a int; altertable uv_pt11 drop a; altertable uv_pt11 add a intnotnull; altertable uv_pt1 attach partition uv_pt11 forvaluesfrom (2) to (5); altertable uv_pt attach partition uv_pt1 forvaluesfrom (1, 2) to (1, 10);
create view uv_ptv asselect * from uv_pt; select events & 4 != 0AS upd,
events & 8 != 0AS ins,
events & 16 != 0AS del from pg_catalog.pg_relation_is_updatable('uv_pt'::regclass, false) t(events); select pg_catalog.pg_column_is_updatable('uv_pt'::regclass, 1::smallint, false); select pg_catalog.pg_column_is_updatable('uv_pt'::regclass, 2::smallint, false); select table_name, is_updatable, is_insertable_into from information_schema.views where table_name = 'uv_ptv'; select table_name, column_name, is_updatable from information_schema.columns where table_name = 'uv_ptv'orderby column_name; insertinto uv_ptv values (1, 2); select tableoid::regclass, * from uv_pt; create view uv_ptv_wco asselect * from uv_pt where a = 0withcheckoption; insertinto uv_ptv_wco values (1, 2);
merge into uv_ptv t using (values (1,2), (1,4)) as v(a,b) on t.a = v.a -- fail: matches 2 src rows when matched thenupdateset b = t.b + 1 whennot matched theninsertvalues (v.a, v.b + 1);
merge into uv_ptv t using (values (1,2), (1,4)) as v(a,b) on t.a = v.a and t.b = v.b when matched thenupdateset b = t.b + 1 whennot matched theninsertvalues (v.a, v.b + 1); -- fail: no partition for b=5
merge into uv_ptv t using (values (1,2), (1,3)) as v(a,b) on t.a = v.a and t.b = v.b when matched thenupdateset b = t.b + 1 whennot matched theninsertvalues (v.a, v.b + 1); -- ok select tableoid::regclass, * from uv_pt orderby a, b; drop view uv_ptv, uv_ptv_wco; droptable uv_pt, uv_pt1, uv_pt11;
-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions -- work fine with partitioned tables createtable wcowrtest (a int) partition by list (a); createtable wcowrtest1 partition of wcowrtest forvaluesin (1); create view wcowrtest_v asselect * from wcowrtest where wcowrtest = '(2)'::wcowrtest withcheckoption; insertinto wcowrtest_v values (1);
altertable wcowrtest add b text; createtable wcowrtest2 (b text, c int, a int); altertable wcowrtest2 drop c; altertable wcowrtest attach partition wcowrtest2 forvaluesin (2);
createtable sometable (a int, b text); insertinto sometable values (1, 'a'), (2, 'b'); create view wcowrtest_v2 as select * from wcowrtest r where r in (select s from sometable s where r.a = s.a) withcheckoption;
-- WITH CHECK qual will be processed with wcowrtest2's -- rowtype after tuple-routing insertinto wcowrtest_v2 values (2, 'no such row in sometable');
drop view wcowrtest_v, wcowrtest_v2; droptable wcowrtest, sometable;
-- Check INSERT .. ON CONFLICT DO UPDATE works correctly when the view's -- columns are named and ordered differently than the underlying table's. createtable uv_iocu_tab (a text unique, b float); insertinto uv_iocu_tab values ('xyxyxy', 0); create view uv_iocu_view as select b, b+1as c, a, '2.0'::text as two from uv_iocu_tab;
insertinto uv_iocu_view (a, b) values ('xyxyxy', 1) on conflict (a) do updateset b = uv_iocu_view.b; select * from uv_iocu_tab; insertinto uv_iocu_view (a, b) values ('xyxyxy', 1) on conflict (a) do updateset b = excluded.b; select * from uv_iocu_tab;
-- OK to access view columns that are not present in underlying base -- relation in the ON CONFLICT portion of the query insertinto uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do updateset b = cast(excluded.two asfloat); select * from uv_iocu_tab;
explain (costs off) insertinto uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do updateset b = excluded.b where excluded.c > 0;
insertinto uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do updateset b = excluded.b where excluded.c > 0; select * from uv_iocu_tab;
drop view uv_iocu_view; droptable uv_iocu_tab;
-- Test whole-row references to the view createtable uv_iocu_tab (a intunique, b text); create view uv_iocu_view as select b as bb, a as aa, uv_iocu_tab::text as cc from uv_iocu_tab;
insertinto uv_iocu_view (aa,bb) values (1,'x'); explain (costs off) insertinto uv_iocu_view (aa,bb) values (1,'y') on conflict (aa) do updateset bb = 'Rejected: '||excluded.* where excluded.aa > 0 and excluded.bb != '' and excluded.cc isnotnull; insertinto uv_iocu_view (aa,bb) values (1,'y') on conflict (aa) do updateset bb = 'Rejected: '||excluded.* where excluded.aa > 0 and excluded.bb != '' and excluded.cc isnotnull; select * from uv_iocu_view;
-- Test omitting a column of the base relation deletefrom uv_iocu_view; insertinto uv_iocu_view (aa,bb) values (1,'x'); insertinto uv_iocu_view (aa) values (1) on conflict (aa) do updateset bb = 'Rejected: '||excluded.*; select * from uv_iocu_view;
altertable uv_iocu_tab altercolumn b setdefault'table default'; insertinto uv_iocu_view (aa) values (1) on conflict (aa) do updateset bb = 'Rejected: '||excluded.*; select * from uv_iocu_view;
alter view uv_iocu_view altercolumn bb setdefault'view default'; insertinto uv_iocu_view (aa) values (1) on conflict (aa) do updateset bb = 'Rejected: '||excluded.*; select * from uv_iocu_view;
-- Should fail to update non-updatable columns insertinto uv_iocu_view (aa) values (1) on conflict (aa) do updateset cc = 'XXX';
drop view uv_iocu_view; droptable uv_iocu_tab;
-- ON CONFLICT DO UPDATE permissions checks create user regress_view_user1; create user regress_view_user2;
set session authorization regress_view_user1; createtable base_tbl(a intunique, b text, c float); insertinto base_tbl values (1,'xxx',1.0); create view rw_view1 asselect b as bb, c as cc, a as aa from base_tbl;
grantselect (aa,bb) on rw_view1 to regress_view_user2; grantinserton rw_view1 to regress_view_user2; grantupdate (bb) on rw_view1 to regress_view_user2;
set session authorization regress_view_user2; insertinto rw_view1 values ('yyy',2.0,1) on conflict (aa) do updateset bb = excluded.cc; -- Not allowed insertinto rw_view1 values ('yyy',2.0,1) on conflict (aa) do updateset bb = rw_view1.cc; -- Not allowed insertinto rw_view1 values ('yyy',2.0,1) on conflict (aa) do updateset bb = excluded.bb; -- OK insertinto rw_view1 values ('zzz',2.0,1) on conflict (aa) do updateset bb = rw_view1.bb||'xxx'; -- OK insertinto rw_view1 values ('zzz',2.0,1) on conflict (aa) do updateset cc = 3.0; -- Not allowed
reset session authorization; select * from base_tbl;
set session authorization regress_view_user1; grantselect (a,b) on base_tbl to regress_view_user2; grantinsert (a,b) on base_tbl to regress_view_user2; grantupdate (a,b) on base_tbl to regress_view_user2;
set session authorization regress_view_user2; create view rw_view2 asselect b as bb, c as cc, a as aa from base_tbl; insertinto rw_view2 (aa,bb) values (1,'xxx') on conflict (aa) do updateset bb = excluded.bb; -- Not allowed create view rw_view3 asselect b as bb, a as aa from base_tbl; insertinto rw_view3 (aa,bb) values (1,'xxx') on conflict (aa) do updateset bb = excluded.bb; -- OK
reset session authorization; select * from base_tbl;
set session authorization regress_view_user2; create view rw_view4 asselect aa, bb, cc FROM rw_view1; insertinto rw_view4 (aa,bb) values (1,'yyy') on conflict (aa) do updateset bb = excluded.bb; -- Not allowed create view rw_view5 asselect aa, bb FROM rw_view1; insertinto rw_view5 (aa,bb) values (1,'yyy') on conflict (aa) do updateset bb = excluded.bb; -- OK
reset session authorization; select * from base_tbl;
drop view rw_view5; drop view rw_view4; drop view rw_view3; drop view rw_view2; drop view rw_view1; droptable base_tbl; drop user regress_view_user1; drop user regress_view_user2;
-- Test single- and multi-row inserts with table and view defaults. -- Table defaults should be used, unless overridden by view defaults. createtable base_tab_def (a int, b text default'Table default',
c text default'Table default', d text, e text); create view base_tab_def_view asselect * from base_tab_def; alter view base_tab_def_view alter b setdefault'View default'; alter view base_tab_def_view alter d setdefault'View default'; insertinto base_tab_def values (1); insertinto base_tab_def values (2), (3); insertinto base_tab_def values (4, default, default, default, default); insertinto base_tab_def values (5, default, default, default, default),
(6, default, default, default, default); insertinto base_tab_def_view values (11); insertinto base_tab_def_view values (12), (13); insertinto base_tab_def_view values (14, default, default, default, default); insertinto base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default); insertinto base_tab_def_view values (17), (default); select * from base_tab_def orderby a;
-- Adding an INSTEAD OF trigger should cause NULLs to be inserted instead of -- table defaults, where there are no view defaults. create function base_tab_def_view_instrig_func() returns trigger as
$$
begin insertinto base_tab_def values (new.a, new.b, new.c, new.d, new.e); return new;
end;
$$
language plpgsql; createtrigger base_tab_def_view_instrig instead of inserton base_tab_def_view foreach row execute function base_tab_def_view_instrig_func();
truncate base_tab_def; insertinto base_tab_def values (1); insertinto base_tab_def values (2), (3); insertinto base_tab_def values (4, default, default, default, default); insertinto base_tab_def values (5, default, default, default, default),
(6, default, default, default, default); insertinto base_tab_def_view values (11); insertinto base_tab_def_view values (12), (13); insertinto base_tab_def_view values (14, default, default, default, default); insertinto base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default); insertinto base_tab_def_view values (17), (default); select * from base_tab_def orderby a;
-- Using an unconditional DO INSTEAD rule should also cause NULLs to be -- inserted where there are no view defaults. droptrigger base_tab_def_view_instrig on base_tab_def_view; drop function base_tab_def_view_instrig_func; create rule base_tab_def_view_ins_rule asoninsertto base_tab_def_view
do instead insertinto base_tab_def values (new.a, new.b, new.c, new.d, new.e);
truncate base_tab_def; insertinto base_tab_def values (1); insertinto base_tab_def values (2), (3); insertinto base_tab_def values (4, default, default, default, default); insertinto base_tab_def values (5, default, default, default, default),
(6, default, default, default, default); insertinto base_tab_def_view values (11); insertinto base_tab_def_view values (12), (13); insertinto base_tab_def_view values (14, default, default, default, default); insertinto base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default); insertinto base_tab_def_view values (17), (default); select * from base_tab_def orderby a;
-- A DO ALSO rule should cause each row to be inserted twice. The first -- insert should behave the same as an auto-updatable view (using table -- defaults, unless overridden by view defaults). The second insert should -- behave the same as a rule-updatable view (inserting NULLs where there are -- no view defaults). drop rule base_tab_def_view_ins_rule on base_tab_def_view; create rule base_tab_def_view_ins_rule asoninsertto base_tab_def_view
do also insertinto base_tab_def values (new.a, new.b, new.c, new.d, new.e);
truncate base_tab_def; insertinto base_tab_def values (1); insertinto base_tab_def values (2), (3); insertinto base_tab_def values (4, default, default, default, default); insertinto base_tab_def values (5, default, default, default, default),
(6, default, default, default, default); insertinto base_tab_def_view values (11); insertinto base_tab_def_view values (12), (13); insertinto base_tab_def_view values (14, default, default, default, default); insertinto base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default); insertinto base_tab_def_view values (17), (default); select * from base_tab_def orderby a, c NULLS LAST;
-- Test a DO ALSO INSERT ... SELECT rule drop rule base_tab_def_view_ins_rule on base_tab_def_view; create rule base_tab_def_view_ins_rule asoninsertto base_tab_def_view
do also insertinto base_tab_def (a, b, e) select new.a, new.b, 'xxx';
truncate base_tab_def; insertinto base_tab_def_view values (1, default, default, default, default); insertinto base_tab_def_view values (2, default, default, default, default),
(3, default, default, default, default); select * from base_tab_def orderby a, e nulls first;
drop view base_tab_def_view; droptable base_tab_def;
-- Test defaults with array assignments createtable base_tab (a serial, b int[], c text, d text default'Table default'); create view base_tab_view asselect c, a, b from base_tab; alter view base_tab_view altercolumn c setdefault'View default'; insertinto base_tab_view (b[1], b[2], c, b[5], b[4], a, b[3]) values (1, 2, default, 5, 4, default, 3), (10, 11, 'C value', 14, 13, 100, 12); select * from base_tab orderby a; drop view base_tab_view; droptable base_tab;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.62 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.