293 lines
9.9 KiB
PL/PgSQL
293 lines
9.9 KiB
PL/PgSQL
CREATE EXTENSION pg_ivm;
|
|
|
|
-- create a table to use as a basis for views and materialized views in various combinations
|
|
CREATE TABLE mv_base_a (i int, j int);
|
|
INSERT INTO mv_base_a VALUES
|
|
(1,10),
|
|
(2,20),
|
|
(3,30),
|
|
(4,40),
|
|
(5,50);
|
|
CREATE TABLE mv_base_b (i int, k int);
|
|
INSERT INTO mv_base_b VALUES
|
|
(1,101),
|
|
(2,102),
|
|
(3,103),
|
|
(4,104);
|
|
|
|
-- CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA;
|
|
SELECT create_immv('mv_ivm_1', 'SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i)');
|
|
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|
|
|
-- immediate maintenance
|
|
BEGIN;
|
|
INSERT INTO mv_base_b VALUES(5,105);
|
|
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|
UPDATE mv_base_a SET j = 0 WHERE i = 1;
|
|
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|
DELETE FROM mv_base_b WHERE (i,k) = (5,105);
|
|
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|
ROLLBACK;
|
|
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|
|
|
-- some query syntax
|
|
BEGIN;
|
|
CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql'
|
|
AS 'SELECT 1' IMMUTABLE;
|
|
SELECT create_immv('mv_ivm_func', 'SELECT * FROM ivm_func()');
|
|
SELECT create_immv('mv_ivm_no_tbl', 'SELECT 1');
|
|
ROLLBACK;
|
|
|
|
-- result of materialized view have DISTINCT clause or the duplicate result.
|
|
BEGIN;
|
|
SELECT create_immv('mv_ivm_duplicate', 'SELECT j FROM mv_base_a');
|
|
SELECT create_immv('mv_ivm_distinct', 'SELECT DISTINCT j FROM mv_base_a');
|
|
INSERT INTO mv_base_a VALUES(6,20);
|
|
SELECT * FROM mv_ivm_duplicate ORDER BY 1;
|
|
SELECT * FROM mv_ivm_distinct ORDER BY 1;
|
|
DELETE FROM mv_base_a WHERE (i,j) = (2,20);
|
|
SELECT * FROM mv_ivm_duplicate ORDER BY 1;
|
|
SELECT * FROM mv_ivm_distinct ORDER BY 1;
|
|
ROLLBACK;
|
|
|
|
-- not support SUM(), COUNT() and AVG() aggregate functions
|
|
SELECT create_immv('mv_ivm_agg', 'SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i');
|
|
|
|
-- support self join view and multiple change on the same table
|
|
BEGIN;
|
|
CREATE TABLE base_t (i int, v int);
|
|
INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30);
|
|
SELECT create_immv('mv_self(v1, v2)',
|
|
'SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i');
|
|
SELECT * FROM mv_self ORDER BY v1;
|
|
INSERT INTO base_t VALUES (4,40);
|
|
DELETE FROM base_t WHERE i = 1;
|
|
UPDATE base_t SET v = v*10 WHERE i=2;
|
|
SELECT * FROM mv_self ORDER BY v1;
|
|
WITH
|
|
ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1),
|
|
ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1),
|
|
upd_t AS (UPDATE base_t SET v = v + 100 RETURNING 1),
|
|
dlt_t AS (DELETE FROM base_t WHERE i IN (4,5) RETURNING 1)
|
|
SELECT NULL;
|
|
SELECT * FROM mv_self ORDER BY v1;
|
|
ROLLBACK;
|
|
|
|
-- support simultaneous table changes
|
|
BEGIN;
|
|
CREATE TABLE base_r (i int, v int);
|
|
CREATE TABLE base_s (i int, v int);
|
|
INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30);
|
|
INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300);
|
|
SELECT create_immv('mv(v1, v2)', 'SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i)');;
|
|
SELECT * FROM mv ORDER BY v1;
|
|
WITH
|
|
ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1),
|
|
ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1),
|
|
ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1),
|
|
upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1),
|
|
dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1)
|
|
SELECT NULL;
|
|
SELECT * FROM mv ORDER BY v1;
|
|
|
|
-- support foreign reference constraints
|
|
BEGIN;
|
|
CREATE TABLE ri1 (i int PRIMARY KEY);
|
|
CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int);
|
|
INSERT INTO ri1 VALUES (1),(2),(3);
|
|
INSERT INTO ri2 VALUES (1),(2),(3);
|
|
SELECT create_immv('mv_ri(i1, i2)', 'SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i)');
|
|
SELECT * FROM mv_ri ORDER BY i1;
|
|
UPDATE ri1 SET i=10 where i=1;
|
|
DELETE FROM ri1 WHERE i=2;
|
|
SELECT * FROM mv_ri ORDER BY i2;
|
|
ROLLBACK;
|
|
|
|
-- views including NULL
|
|
BEGIN;
|
|
CREATE TABLE base_t (i int, v int);
|
|
INSERT INTO base_t VALUES (1,10),(2, NULL);
|
|
SELECT create_immv('mv', 'SELECT * FROM base_t');
|
|
SELECT * FROM mv ORDER BY i;
|
|
UPDATE base_t SET v = 20 WHERE i = 2;
|
|
SELECT * FROM mv ORDER BY i;
|
|
ROLLBACK;
|
|
|
|
BEGIN;
|
|
CREATE TABLE base_t (i int);
|
|
SELECT create_immv('mv', 'SELECT * FROM base_t');
|
|
SELECT * FROM mv ORDER BY i;
|
|
INSERT INTO base_t VALUES (1),(NULL);
|
|
SELECT * FROM mv ORDER BY i;
|
|
ROLLBACK;
|
|
|
|
-- IMMV containing user defined type
|
|
BEGIN;
|
|
|
|
CREATE TYPE mytype;
|
|
CREATE FUNCTION mytype_in(cstring)
|
|
RETURNS mytype AS 'int4in'
|
|
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
|
CREATE FUNCTION mytype_out(mytype)
|
|
RETURNS cstring AS 'int4out'
|
|
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
|
CREATE TYPE mytype (
|
|
LIKE = int4,
|
|
INPUT = mytype_in,
|
|
OUTPUT = mytype_out
|
|
);
|
|
|
|
CREATE FUNCTION mytype_eq(mytype, mytype)
|
|
RETURNS bool AS 'int4eq'
|
|
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
|
CREATE FUNCTION mytype_lt(mytype, mytype)
|
|
RETURNS bool AS 'int4lt'
|
|
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
|
CREATE FUNCTION mytype_cmp(mytype, mytype)
|
|
RETURNS integer AS 'btint4cmp'
|
|
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
|
|
|
CREATE OPERATOR = (
|
|
leftarg = mytype, rightarg = mytype,
|
|
procedure = mytype_eq);
|
|
CREATE OPERATOR < (
|
|
leftarg = mytype, rightarg = mytype,
|
|
procedure = mytype_lt);
|
|
|
|
CREATE OPERATOR CLASS mytype_ops
|
|
DEFAULT FOR TYPE mytype USING btree AS
|
|
OPERATOR 1 <,
|
|
OPERATOR 3 = ,
|
|
FUNCTION 1 mytype_cmp(mytype,mytype);
|
|
|
|
CREATE TABLE t_mytype (x mytype);
|
|
SELECT create_immv('mv_mytype',
|
|
'SELECT * FROM t_mytype');
|
|
INSERT INTO t_mytype VALUES ('1'::mytype);
|
|
SELECT * FROM mv_mytype;
|
|
|
|
ROLLBACK;
|
|
|
|
-- outer join is not supported
|
|
SELECT create_immv('mv(a,b)',
|
|
'SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i');
|
|
|
|
-- CTE is not supported
|
|
SELECT create_immv('mv',
|
|
'WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i;');
|
|
|
|
-- contain system column
|
|
SELECT create_immv('mv_ivm01', 'SELECT i,j,xmin FROM mv_base_a');
|
|
SELECT create_immv('mv_ivm02', 'SELECT i,j FROM mv_base_a WHERE xmin = ''610''');
|
|
SELECT create_immv('mv_ivm03', 'SELECT i,j,xmin::text AS x_min FROM mv_base_a');
|
|
SELECT create_immv('mv_ivm04', 'SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a');
|
|
|
|
-- contain subquery
|
|
SELECT create_immv('mv_ivm03', 'SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 )');
|
|
SELECT create_immv('mv_ivm04', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i');
|
|
SELECT create_immv('mv_ivm05', 'SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a');
|
|
-- contain ORDER BY
|
|
SELECT create_immv('mv_ivm07', 'SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k');
|
|
-- contain HAVING
|
|
SELECT create_immv('mv_ivm08', 'SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5');
|
|
|
|
-- contain view or materialized view
|
|
CREATE VIEW b_view AS SELECT i,k FROM mv_base_b;
|
|
CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b;
|
|
SELECT create_immv('mv_ivm07', 'SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i');
|
|
SELECT create_immv('mv_ivm08', 'SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i');
|
|
SELECT create_immv('mv_ivm09', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i');
|
|
|
|
-- contain mutable functions
|
|
SELECT create_immv('mv_ivm12', 'SELECT i,j FROM mv_base_a WHERE i = random()::int');
|
|
|
|
-- LIMIT/OFFSET is not supported
|
|
SELECT create_immv('mv_ivm13', 'SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5');
|
|
|
|
-- DISTINCT ON is not supported
|
|
SELECT create_immv('mv_ivm14', 'SELECT DISTINCT ON(i) i, j FROM mv_base_a');
|
|
|
|
-- TABLESAMPLE clause is not supported
|
|
SELECT create_immv('mv_ivm15', 'SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50)');
|
|
|
|
-- window functions are not supported
|
|
SELECT create_immv('mv_ivm16', 'SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a');
|
|
|
|
-- inheritance parent is not supported
|
|
BEGIN;
|
|
CREATE TABLE parent (i int, v int);
|
|
CREATE TABLE child_a(options text) INHERITS(parent);
|
|
SELECT create_immv('mv_ivm21', 'SELECT * FROM parent');
|
|
ROLLBACK;
|
|
|
|
-- UNION statement is not supported
|
|
SELECT create_immv('mv_ivm22', 'SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b');
|
|
|
|
-- empty target list is not allowed with IVM
|
|
SELECT create_immv('mv_ivm25', 'SELECT FROM mv_base_a');
|
|
|
|
-- FOR UPDATE/SHARE is not supported
|
|
SELECT create_immv('mv_ivm26', 'SELECT i,j FROM mv_base_a FOR UPDATE');
|
|
|
|
-- tartget list cannot contain ivm column that start with '__ivm'
|
|
SELECT create_immv('mv_ivm28', 'SELECT i AS "__ivm_count__" FROM mv_base_a');
|
|
|
|
-- VALUES is not supported
|
|
SELECT create_immv('mv_ivm_only_values1', 'values(1)');
|
|
|
|
|
|
-- base table which has row level security
|
|
DROP USER IF EXISTS ivm_admin;
|
|
DROP USER IF EXISTS ivm_user;
|
|
CREATE USER ivm_admin;
|
|
CREATE USER ivm_user;
|
|
SET SESSION AUTHORIZATION ivm_admin;
|
|
|
|
CREATE TABLE rls_tbl(id int, data text, owner name);
|
|
INSERT INTO rls_tbl VALUES
|
|
(1,'foo','ivm_user'),
|
|
(2,'bar','postgres');
|
|
CREATE TABLE num_tbl(id int, num text);
|
|
INSERT INTO num_tbl VALUES
|
|
(1,'one'),
|
|
(2,'two'),
|
|
(3,'three'),
|
|
(4,'four');
|
|
CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user);
|
|
CREATE POLICY rls_tbl_policy2 ON rls_tbl FOR INSERT TO PUBLIC WITH CHECK(current_user LIKE 'ivm_%');
|
|
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
|
|
GRANT ALL on rls_tbl TO PUBLIC;
|
|
GRANT ALL on num_tbl TO PUBLIC;
|
|
|
|
SET SESSION AUTHORIZATION ivm_user;
|
|
|
|
SELECT create_immv('ivm_rls', 'SELECT * FROM rls_tbl');
|
|
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
|
INSERT INTO rls_tbl VALUES
|
|
(3,'baz','ivm_user'),
|
|
(4,'qux','postgres');
|
|
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
|
SELECT create_immv('ivm_rls2', 'SELECT * FROM rls_tbl JOIN num_tbl USING(id)');
|
|
|
|
RESET SESSION AUTHORIZATION;
|
|
|
|
WITH
|
|
x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)),
|
|
y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4))
|
|
SELECT;
|
|
SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
|
|
|
|
DROP TABLE rls_tbl CASCADE;
|
|
DROP TABLE num_tbl CASCADE;
|
|
|
|
DROP USER ivm_user;
|
|
DROP USER ivm_admin;
|
|
|
|
-- prevent IMMV chanages
|
|
INSERT INTO mv_ivm_1 VALUES(1,1,1);
|
|
UPDATE mv_ivm_1 SET k = 1 WHERE i = 1;
|
|
DELETE FROM mv_ivm_1;
|
|
TRUNCATE mv_ivm_1;
|
|
|
|
DROP TABLE mv_base_b CASCADE;
|
|
DROP TABLE mv_base_a CASCADE;
|