Support pg_ivm extension, which provides the incremental view maintenance (IVM) function for RDS for PostgreSQL.
Background information
Incremental view maintenance (IVM) is a method to keep materialized views up to date by computing and applying only incremental changes to the views, rather than recalculating the entire content from scratch like REFRESH MATERIALIZED VIEW. IVM can update materialized views more efficiently than recalculation when only a small part of the view is changed.
Precautions
The pg_ivm extension is only supported on instances with PostgreSQL version 13 or above. To use this extension, make sure to select the appropriate instance version when creating an instance.
Create and delete extension
tip
A super account is required to create the extension.
CREATE EXTENSION pg_ivm;
Usage
We support the incremental maintainable materialized view (immv) for ivm materialized views. To create immv, call the create_immv function with the table name and view definition statement. For example:
testdb=> CREATE TABLE mv_base_a (i int, j int);
testdb=> INSERT INTO mv_base_a VALUES
testdb=> CREATE TABLE mv_base_b (i int, k int);
testdb=> INSERT INTO mv_base_b VALUES
testdb=> SELECT create_immv('mv_ivm_1', 'SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i)');
NOTICE: could not create an index on immv "mv_ivm_1" automatically
DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
HINT: Create an index on the immv for efficient incremental maintenance.
testdb=> SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
testdb=*> INSERT INTO mv_base_b VALUES(5,105);
testdb=*> SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
testdb=*> UPDATE mv_base_a SET j = 0 WHERE i = 1;
testdb=*> SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
testdb=*> DELETE FROM mv_base_b WHERE (i,k) = (5,105);
testdb=*> SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
Extension metadata
pg_ivm_immv stores immv information.
testdb=> SELECT immvrelid,get_immv_def(immvrelid),ispopulated FROM pg_ivm_immv ORDER BY 1;
immvrelid | get_immv_def | ispopulated
mv_ivm_1 | SELECT a.i, +| t
| JOIN mv_base_b b USING (i)) |
Extension functions
Use the create_immv function to create immv. Usage example of create_immv function:
create_immv(immv_name text, view_definition text) RETURNS bigint
create_immv defines a new IMMV for querying. Create a table named immv_name, execute the query specified by view_definition, and use it to populate the IMMV. The query is stored in pg_ivm_immv for later refresh during incremental view maintenance. create_immv returns the number of rows in the created IMMV.
testdb=> CREATE TABLE t (i int PRIMARY KEY);
testdb=> INSERT INTO t SELECT generate_series(1, 100);
testdb=> SELECT create_immv('mv', 'SELECT * FROM t');
NOTICE: created index "mv_index" on immv "mv"
Use the refresh_immv function to refresh the IMMV. Usage example of refresh_immv function:
refresh_immv(immv_name text, with_data bool) RETURNS bigint
The refresh_immv function can completely replace the content of the IMMV, similar to what the REFRESH MATERIALIZED VIEW command does for materialized views. To execute this function, you must be the owner of the IMMV (using PostgreSQL 16 or earlier) or have MAINTAIN privileges on the IMMV (using PostgreSQL 17 or later). After executing the refresh_immv function, the old content in IMMV will be discarded. The with_data flag corresponds to the WITH [NO] DATA option of the REFRESH MATERIALIZED VIEW command.
warning
If you use PostgreSQL 17 or later, search_path temporarily changes to pg_catalog, pg_temp when running refresh_immv.
testdb=> SELECT immvrelid, ispopulated FROM pg_ivm_immv where immvrelid='mv'::regclass;
immvrelid | ispopulated
testdb=> SELECT refresh_immv('mv', true);
testdb=> SELECT immvrelid, ispopulated FROM pg_ivm_immv where immvrelid='mv'::regclass;
testdb=> INSERT INTO t VALUES(101);
testdb=> SELECT i FROM mv ORDER BY 1 desc limit 1;
testdb=> SELECT refresh_immv('mv', false);
testdb=> SELECT immvrelid, ispopulated FROM pg_ivm_immv where immvrelid='mv'::regclass;
testdb=> SELECT i FROM mv ORDER BY 1 desc limit 1;
testdb=> INSERT INTO t VALUES(102);
testdb=> SELECT i FROM mv ORDER BY 1 desc limit 1;
testdb=> SELECT refresh_immv('mv', true);
testdb=> SELECT i FROM mv ORDER BY 1 desc limit 1;
testdb=> INSERT INTO t VALUES(103);
testdb=> SELECT i FROM mv ORDER BY 1 desc limit 1;
the get_immv_def function is used to reconstruct the underlying SELECT command of IMMV. An example of using the get_immv_def function is as follows:
get_immv_def(immv regclass) RETURNS text
testdb=> SELECT immvrelid, get_immv_def(immvrelid) FROM pg_ivm_immv ORDER BY 1;
immvrelid | get_immv_def
mv_ivm_1 | SELECT a.i, +
| JOIN mv_base_b b USING (i))
Constraints
Aggregate
Supported aggregate functions include count, sum, avg, min, and max. Currently only built-in aggregate functions are supported; user-defined aggregates are not available.
Precautions:
- For min or max, when tuples containing the current minimum or maximum are deleted from the base table, new values may be recalculated from the base table based on the affected groups. Therefore, updating an IMMV containing these functions may take a long time.
- Using sum or avg on real (float4) or double precision (float8) types in an IMMV is unsafe because the aggregate values in the IMMV may differ from those computed from the base table due to the limited precision of these types. To avoid this issue, use the numeric type instead.
If there is a GROUP BY clause, the expressions specified in the GROUP BY must appear in the target list. This is how tuples to update in the IMMV are identified. These attributes are used as scan keys to search tuples in the IMMV and therefore must be indexed for efficient IVM.
Subquery
Subqueries with EXISTS in the FROM clause and simple subqueries are supported. EXISTS subqueries that are not combined with AND and those in the target list are not supported. EXISTS subqueries are only supported in WHERE clauses and not in target lists.
If EXISTS references columns from tables in the outer query, these columns must be included in the target list. Subqueries containing aggregate functions or DISTINCT are not supported.
Simple CTEs (WITH queries) are supported; WITH queries containing aggregate functions or DISTINCT are not supported.
Recursive queries (WITH RECURSIVE) are not allowed. Unreferenced CTEs are also not allowed; in view definition queries, CTEs must be referenced at least once.
DISTINCT is allowed in the definition query of IMMV. Assuming IMMV is defined using DISTINCT on base tables containing duplicate tuples. When tuples are deleted from the base table, tuples in the view are deleted if and only if the tuple count becomes zero. Additionally, when tuples are inserted into the base table, tuples are inserted into the view only if the same tuples do not exist in the view.
When the base table is truncated, if the view definition query does not include aggregation without a GROUP BY clause, the IMMV is also truncated, and its contents become empty. Aggregated views without a GROUP BY clause always have only one row. Therefore, in this case, if the base table is truncated, the IMMV is only refreshed and not truncated.
Others