Compare commits
1 commit
main
...
104-create
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67be9048ae |
27 changed files with 118 additions and 2202 deletions
28
.gitignore
vendored
28
.gitignore
vendored
|
|
@ -1,30 +1,8 @@
|
|||
# Object files
|
||||
*.o
|
||||
|
||||
# Coverage files
|
||||
*.gcno
|
||||
*.gcda
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
|
||||
# LLVM bitcode
|
||||
# Global excludes across all subdirectories
|
||||
*.bc
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.o
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.app
|
||||
|
||||
# Dependencies
|
||||
.deps
|
||||
regression.*
|
||||
|
||||
# Generated subdirectories
|
||||
/results/
|
||||
/output_iso/
|
||||
|
|
|
|||
13
Makefile
13
Makefile
|
|
@ -1,7 +1,5 @@
|
|||
# contrib/pg_ivm/Makefile
|
||||
|
||||
PG_CONFIG ?= pg_config
|
||||
|
||||
MODULE_big = pg_ivm
|
||||
OBJS = \
|
||||
$(WIN32RES) \
|
||||
|
|
@ -18,22 +16,15 @@ DATA = pg_ivm--1.0.sql \
|
|||
pg_ivm--1.3--1.4.sql pg_ivm--1.4--1.5.sql pg_ivm--1.5--1.6.sql \
|
||||
pg_ivm--1.6--1.7.sql pg_ivm--1.7--1.8.sql pg_ivm--1.8--1.9.sql \
|
||||
pg_ivm--1.9--1.10.sql \
|
||||
pg_ivm--1.10.sql \
|
||||
pg_ivm--1.10--1.11.sql pg_ivm--1.11--1.12.sql
|
||||
pg_ivm--1.10.sql
|
||||
|
||||
REGRESS = pg_ivm create_immv refresh_immv
|
||||
|
||||
PGVER = $(shell $(PG_CONFIG) --version | sed "s/^[^ ]* \([0-9]*\).*$$/\1/" 2>/dev/null)
|
||||
|
||||
# We assume PG13 is the only version that is supported by pg_ivm but
|
||||
# missing pg_isolation_regress.
|
||||
|
||||
ifneq ($(PGVER),13)
|
||||
ISOLATION = create_insert refresh_insert insert_insert \
|
||||
create_insert2 refresh_insert2 insert_insert2 \
|
||||
create_insert3 refresh_insert3 insert_insert3
|
||||
ISOLATION_OPTS = --load-extension=pg_ivm
|
||||
endif
|
||||
|
||||
PG_CONFIG ?= pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
|
|
|
|||
157
README.md
157
README.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
The `pg_ivm` module provides Incremental View Maintenance (IVM) feature for PostgreSQL.
|
||||
|
||||
The extension is compatible with PostgreSQL 13, 14, 15, 16, 17, and 18.
|
||||
The extension is compatible with PostgreSQL 13, 14, 15, 16, and 17.
|
||||
|
||||
## Description
|
||||
|
||||
|
|
@ -68,32 +68,19 @@ If you installed PostgreSQL from rpm or deb, you will need the devel package (fo
|
|||
|
||||
> **Important:** Don't forget to set the `PG_CONFIG` variable (`make PG_CONFIG=...`) or the `PATH` to the `pg_config` command in case you want to use `pg_ivm` on a non-default or custom build of PostgreSQL. Read more [here](https://wiki.postgresql.org/wiki/Building_and_Installing_PostgreSQL_Extension_Modules).
|
||||
|
||||
Once installed, execute `CREATE EXTENSION` comand.
|
||||
And, execute CREATE EXTENSION comand.
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION pg_ivm;
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
To ensure `pg_ivm` maintains IMMVs correctly, add it to either shared_preload_libraries or session_preload_libraries in postgresql.conf:
|
||||
|
||||
```
|
||||
# Add pg_ivm to preload libraries
|
||||
shared_preload_libraries = 'pg_ivm'
|
||||
# OR
|
||||
session_preload_libraries = 'pg_ivm'
|
||||
```
|
||||
|
||||
After making this change, restart PostgreSQL for the configuration to take effect.
|
||||
|
||||
### RPM packages and yum repository
|
||||
|
||||
RPM packages of pg_ivm are available from the [PostgreSQL yum repository](https://yum.postgresql.org/). See the [instruction](https://yum.postgresql.org/howto/) for details. Note that we are not the maintainer of this yum repository and RPMs for pg_ivm in it may be not always latest.
|
||||
|
||||
## Objects
|
||||
|
||||
When `pg_ivm` is installed, the 'pgivm' schema is created, along with the following objects within this schema.
|
||||
When `pg_ivm` is installed, the following objects are created in the schema `pgivm`.
|
||||
|
||||
### Functions
|
||||
|
||||
|
|
@ -101,7 +88,7 @@ When `pg_ivm` is installed, the 'pgivm' schema is created, along with the follow
|
|||
|
||||
Use `create_immv` function to create IMMV.
|
||||
```
|
||||
pgivm.create_immv(immv_name text, view_definition text) RETURNS bigint
|
||||
pgivim.create_immv(immv_name text, view_definition text) RETURNS bigint
|
||||
```
|
||||
`create_immv` defines a new IMMV of a query. A table of the name `immv_name` is created and a query specified by `view_definition` is executed and used to populate the IMMV. The query is stored in `pg_ivm_immv`, so that it can be refreshed later upon incremental view maintenance. `create_immv` returns the number of rows in the created IMMV.
|
||||
|
||||
|
|
@ -120,7 +107,7 @@ pgivm.refresh_immv(immv_name text, with_data bool) RETURNS bigint
|
|||
|
||||
`refresh_immv` completely replaces the contents of an IMMV as `REFRESH MATERIALIZED VIEW` command does for a materialized view. To execute this function you must be the owner of the IMMV (with PostgreSQL 16 or earlier) or have the `MAINTAIN` privilege on the IMMV (with PostgreSQL 17 or later). The old contents are discarded.
|
||||
|
||||
The with_data flag is corresponding to `WITH [NO] DATA` option of `REFRESH MATERIALIZED VIEW` command. If with_data is true, the backing query is executed to provide the new data, and if the IMMV is unpopulated, triggers for maintaining the view are created. Also, a unique index is created for IMMV if it is possible and the view doesn't have that yet. If with_data is false, no new data is generated and the IMMV become unpopulated, and the triggers are dropped from the IMMV. Note that unpopulated IMMV is still scannable although the result is empty. This behaviour may be changed in future to raise an error when an unpopulated IMMV is scanned.
|
||||
The with_data flag is corresponding to `WITH [NO] DATA` option of REFRESH MATERIALIZED VIEW` command. If with_data is true, the backing query is executed to provide the new data, and if the IMMV is unpopulated, triggers for maintaining the view are created. Also, a unique index is created for IMMV if it is possible and the view doesn't have that yet. If with_data is false, no new data is generated and the IMMV become unpopulated, and the triggers are dropped from the IMMV. Note that unpopulated IMMV is still scannable although the result is empty. This behaviour may be changed in future to raise an error when an unpopulated IMMV is scanned.
|
||||
|
||||
`refresh_immv` acquires `AccessExclusiveLock` on the view. However, even if we are able to acquire the lock, a concurrent transaction may have already incrementally updated and committed the view before we can acquire the lock. In `REPEATABLE READ` or `SERIALIZABLE` isolation level, this could lead to an inconsistent state of the view. Therefore, an error is raised to prevent anomalies when this situation is detected.
|
||||
|
||||
|
|
@ -142,13 +129,10 @@ The catalog `pgivm.pg_ivm_immv` stores IMMV information.
|
|||
|immvrelid|regclass|The OID of the IMMV|
|
||||
|viewdef|text|Query tree (in the form of a nodeToString() representation) for the view definition|
|
||||
|ispopulated|bool|True if IMMV is currently populated|
|
||||
|lastivmupdate|xid8|The transaction ID of the most recent incremental update to the view|
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
### `CREATE MATERIALIZED VIEW` and `REFRESH MATERIALIZED VIEW`
|
||||
|
||||
In general, IMMVs allow faster updates than `REFRESH MATERIALIZED VIEW` at the price of slower updates to their base tables. Update of base tables is slower because triggers will be invoked and the IMMV is updated in triggers per modification statement.
|
||||
|
||||
For example, suppose a normal materialized view defined as below:
|
||||
|
|
@ -172,8 +156,6 @@ REFRESH MATERIALIZED VIEW
|
|||
Time: 20575.721 ms (00:20.576)
|
||||
```
|
||||
|
||||
### Creating an IMMV
|
||||
|
||||
On the other hand, after creating IMMV with the same view definition as below:
|
||||
|
||||
```
|
||||
|
|
@ -187,7 +169,7 @@ NOTICE: created index "immv_index" on immv "immv"
|
|||
(1 row)
|
||||
```
|
||||
|
||||
Updating a tuple in a base table takes more than the normal view, but its content is updated automatically and this is faster than the `REFRESH MATERIALIZED VIEW` command.
|
||||
updating a tuple in a base table takes more than the normal view, but its content is updated automatically and this is faster than the `REFRESH MATERIALIZED VIEW` command.
|
||||
|
||||
```sql
|
||||
test=# UPDATE pgbench_accounts SET abalance = 1234 WHERE aid = 1;
|
||||
|
|
@ -216,131 +198,6 @@ UPDATE 1
|
|||
Time: 3224.741 ms (00:03.225)
|
||||
```
|
||||
|
||||
### IMMV with Aggregate Functions
|
||||
|
||||
You can create an IMMV that includes aggregate functions.
|
||||
|
||||
```sql
|
||||
test=# SELECT pgivm.create_immv('immv_agg',
|
||||
'SELECT bid, count(*), sum(abalance), avg(abalance)
|
||||
FROM pgbench_accounts JOIN pgbench_branches USING(bid) GROUP BY bid');
|
||||
NOTICE: created index "immv_agg_index" on immv "immv_agg"
|
||||
create_immv
|
||||
-------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
Time: 5772.625 ms (00:05.773)
|
||||
```
|
||||
|
||||
Creating this view takes about five seconds, and the normal refresh operation requires a similar amount of time. The following example demonstrates refreshing the IMMV using `refresh_immv`. The execution time would be approximately the same if you used `REFRESH MATERIALIZED VIEW` on a regular materialized view with the same definition.
|
||||
|
||||
```sql
|
||||
test=# SELECT pgivm.refresh_immv('immv_agg',true);
|
||||
refresh_immv
|
||||
--------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
Time: 5766.292 ms (00:05.766)
|
||||
```
|
||||
|
||||
When a base table is updated, the IMMV is also automatically updated incrementally, as expected.
|
||||
|
||||
```sql
|
||||
test=# SELECT bid, count, sum, avg FROM immv_agg WHERE bid = 42;
|
||||
bid | count | sum | avg
|
||||
-----+--------+-------+------------------------
|
||||
42 | 100000 | 38774 | 0.38774000000000000000
|
||||
(1 row)
|
||||
|
||||
Time: 3.123 ms
|
||||
|
||||
test=# UPDATE pgbench_accounts SET abalance = abalance + 1000 WHERE aid = 4112345 AND bid = 42;
|
||||
UPDATE 1
|
||||
Time: 16.616 ms
|
||||
|
||||
test=# SELECT bid, count, sum, avg FROM immv_agg WHERE bid = 42;
|
||||
bid | count | sum | avg
|
||||
-----+--------+-------+------------------------
|
||||
42 | 100000 | 39774 | 0.39774000000000000000
|
||||
(1 row)
|
||||
|
||||
Time: 1.987 ms
|
||||
```
|
||||
|
||||
After updating a row in the `pgbench_accounts` table, you can see that the aggregated values in `immv_agg` are updated automatically.
|
||||
|
||||
### Listing IMMVs and Viewing Their Definitions
|
||||
|
||||
You can find all IMMVs in the `pg_ivm_immv` catalog and view their definition queries by executing the `get_immv_def` function."
|
||||
|
||||
```sql
|
||||
test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM pgivm.pg_ivm_immv;
|
||||
immv | immv_def
|
||||
----------+--------------------------------------------
|
||||
immv_agg | SELECT pgbench_accounts.bid, +
|
||||
| count(*) AS count, +
|
||||
| sum(pgbench_accounts.abalance) AS sum,+
|
||||
| avg(pgbench_accounts.abalance) AS avg +
|
||||
| FROM (pgbench_accounts +
|
||||
| JOIN pgbench_branches USING (bid)) +
|
||||
| GROUP BY pgbench_accounts.bid
|
||||
immv | SELECT a.aid, +
|
||||
| b.bid, +
|
||||
| a.abalance, +
|
||||
| b.bbalance +
|
||||
| FROM (pgbench_accounts a +
|
||||
| JOIN pgbench_branches b USING (bid))
|
||||
(2 rows)
|
||||
```
|
||||
|
||||
### Dropping an IMMV
|
||||
|
||||
An IMMV can be dropped using the `DROP TABLE` command. Once an IMMV is dropped, its entry is automatically removed from the `pg_ivm_immv` catalog.
|
||||
|
||||
```sql
|
||||
test=# DROP TABLE immv
|
||||
DROP TABLE
|
||||
|
||||
test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM pgivm.pg_ivm_immv;
|
||||
immv | immv_def
|
||||
----------+--------------------------------------------
|
||||
immv_agg | SELECT pgbench_accounts.bid, +
|
||||
| count(*) AS count, +
|
||||
| sum(pgbench_accounts.abalance) AS sum,+
|
||||
| avg(pgbench_accounts.abalance) AS avg +
|
||||
| FROM (pgbench_accounts +
|
||||
| JOIN pgbench_branches USING (bid)) +
|
||||
| GROUP BY pgbench_accounts.bid
|
||||
(1 row)
|
||||
```
|
||||
|
||||
### Renaming an IMMV
|
||||
|
||||
An IMMV can be renamed using the `ALTER TABLE` command.
|
||||
|
||||
```sql
|
||||
test=# ALTER TABLE immv_agg RENAME TO immv_agg2;
|
||||
ALTER TABLE
|
||||
|
||||
test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM pgivm.pg_ivm_immv;
|
||||
immv | immv_def
|
||||
-----------+--------------------------------------------
|
||||
immv_agg2 | SELECT pgbench_accounts.bid, +
|
||||
| count(*) AS count, +
|
||||
| sum(pgbench_accounts.abalance) AS sum,+
|
||||
| avg(pgbench_accounts.abalance) AS avg +
|
||||
| FROM (pgbench_accounts +
|
||||
| JOIN pgbench_branches USING (bid)) +
|
||||
| GROUP BY pgbench_accounts.bid
|
||||
(1 row)
|
||||
```
|
||||
|
||||
## `pg_dump` and `pg_upgrade`
|
||||
|
||||
After restoring data from a `pg_dump` backup or upgrading `PostgreSQL` using `pg_upgrade`, all IMMVs must be manually dropped and recreated.
|
||||
|
||||
## Supported View Definitions and Restriction
|
||||
|
||||
Currently, IMMV's view definition can contain inner joins, DISTINCT clause, some built-in aggregate functions, simple sub-queries in `FROM` clause, EXISTS sub-queries, and simple CTE (`WITH` query). Inner joins including self-join are supported, but outer joins are not supported. Supported aggregate functions are count, sum, avg, min and max. Other aggregates, sub-queries which contain an aggregate or `DISTINCT` clause, sub-queries in other than `FROM` clause, window functions, `HAVING`, `ORDER BY`, `LIMIT`/`OFFSET`, `UNION`/`INTERSECT`/`EXCEPT`, `DISTINCT ON`, `TABLESAMPLE`, `VALUES`, and `FOR UPDATE`/`SHARE` can not be used in view definition.
|
||||
|
|
@ -418,7 +275,7 @@ Even if we are able to acquire a lock, a concurrent transaction may have already
|
|||
|
||||
### Row Level Security
|
||||
|
||||
If some base tables have row level security policy, rows that are not visible to the materialized view's owner are excluded from the result. In addition, such rows are excluded as well when views are incrementally maintained. However, if a new policy is defined or policies are changed after the materialized view was created, the new policy will not be applied to the view contents. To apply the new policy, you need to refresh or recreate the IMMV.
|
||||
If some base tables have row level security policy, rows that are not visible to the materialized view's owner are excluded from the result. In addition, such rows are excluded as well when views are incrementally maintained. However, if a new policy is defined or policies are changed after the materialized view was created, the new policy will not be applied to the view contents. To apply the new policy, you need to recreate IMMV.
|
||||
|
||||
### How to Disable or Enable Immediate Maintenance
|
||||
|
||||
|
|
|
|||
36
createas.c
36
createas.c
|
|
@ -101,11 +101,7 @@ create_immv_internal(List *attrList, IntoClause *into)
|
|||
CreateStmt *create = makeNode(CreateStmt);
|
||||
char relkind;
|
||||
Datum toast_options;
|
||||
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 180000)
|
||||
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
|
||||
#else
|
||||
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
|
||||
#endif
|
||||
ObjectAddress intoRelationAddr;
|
||||
|
||||
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
|
||||
|
|
@ -152,12 +148,7 @@ create_immv_internal(List *attrList, IntoClause *into)
|
|||
NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
|
||||
|
||||
/* Create the "view" part of an IMMV. */
|
||||
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 180000)
|
||||
StoreImmvQuery(intoRelationAddr.objectId, into->viewQuery);
|
||||
#else
|
||||
StoreImmvQuery(intoRelationAddr.objectId, (Query *) into->viewQuery);
|
||||
#endif
|
||||
|
||||
CommandCounterIncrement();
|
||||
|
||||
return intoRelationAddr;
|
||||
|
|
@ -304,7 +295,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
|
|||
/* Create an index on incremental maintainable materialized view, if possible */
|
||||
CreateIndexOnIMMV(query, matviewRel);
|
||||
|
||||
/* Create triggers to prevent IMMV from being changed */
|
||||
/* Create triggers to prevent IMMV from beeing changed */
|
||||
CreateChangePreventTrigger(address.objectId);
|
||||
|
||||
table_close(matviewRel, NoLock);
|
||||
|
|
@ -341,7 +332,7 @@ rewriteQueryForIMMV(Query *query, List *colNames)
|
|||
FuncCall *fn;
|
||||
|
||||
/*
|
||||
* Check the length of column name list not to override names of
|
||||
* Check the length of colunm name list not to override names of
|
||||
* additional columns
|
||||
*/
|
||||
if (list_length(colNames) > list_length(query->targetList))
|
||||
|
|
@ -1010,7 +1001,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
|||
errmsg("WITH query name %s is not supported on incrementally maintainable materialized view", cte->ctename)));
|
||||
|
||||
/*
|
||||
* When a table in an unreferenced CTE is TRUNCATEd, the contents of the
|
||||
* When a table in a unreferenced CTE is TRUNCATEd, the contents of the
|
||||
* IMMV is not affected so it must not be truncated. For confirming it
|
||||
* at the maintenance time, we have to check if the modified table used
|
||||
* in a CTE is actually referenced. Although it would be possible, we
|
||||
|
|
@ -1052,7 +1043,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
|||
check_ivm_restriction_walker((Node *) from->fromlist, context);
|
||||
|
||||
/*
|
||||
* EXIST is allowed directly under FROM clause
|
||||
* EXIEST is allowed directly under FROM clause
|
||||
*/
|
||||
context->allow_exists = true;
|
||||
check_ivm_restriction_walker(from->quals, context);
|
||||
|
|
@ -1124,7 +1115,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
|||
Node *opnode = (Node *) lfirst(lc);
|
||||
|
||||
/*
|
||||
* EXIST is allowed under AND expression only if it is
|
||||
* EXIEST is allowed under AND expression only if it is
|
||||
* directly under WHERE.
|
||||
*/
|
||||
if (allow_exists)
|
||||
|
|
@ -1363,8 +1354,8 @@ check_aggregate_supports_ivm(Oid aggfnoid)
|
|||
* Create a unique index on incremental maintainable materialized view.
|
||||
* If the view definition query has a GROUP BY clause, the index is created
|
||||
* on the columns of GROUP BY expressions. Otherwise, if the view contains
|
||||
* all primary key attributes of its base tables in the target list, the index
|
||||
* is created on these attributes. In other cases, no index is created.
|
||||
* all primary key attritubes of its base tables in the target list, the index
|
||||
* is created on these attritubes. In other cases, no index is created.
|
||||
*/
|
||||
void
|
||||
CreateIndexOnIMMV(Query *query, Relation matviewRel)
|
||||
|
|
@ -1523,18 +1514,10 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
|
|||
|
||||
indexRel = index_open(indexoid, AccessShareLock);
|
||||
|
||||
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 180000)
|
||||
if (CheckIndexCompatible(indexRel->rd_id,
|
||||
index->accessMethod,
|
||||
index->indexParams,
|
||||
index->excludeOpNames,
|
||||
false))
|
||||
#else
|
||||
if (CheckIndexCompatible(indexRel->rd_id,
|
||||
index->accessMethod,
|
||||
index->indexParams,
|
||||
index->excludeOpNames))
|
||||
#endif
|
||||
hasCompatibleIndex = true;
|
||||
|
||||
index_close(indexRel, AccessShareLock);
|
||||
|
|
@ -1558,7 +1541,7 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
|
|||
idxname, RelationGetRelationName(matviewRel))));
|
||||
|
||||
/*
|
||||
* Make dependencies so that the index is dropped if any base tables'
|
||||
* Make dependencies so that the index is dropped if any base tables's
|
||||
* primary key is dropped.
|
||||
*/
|
||||
foreach(lc, constraintList)
|
||||
|
|
@ -1721,7 +1704,7 @@ get_primary_key_attnos_from_query(Query *query, List **constraintList)
|
|||
}
|
||||
|
||||
/*
|
||||
* Store the query for the IMMV to pg_ivm_immv
|
||||
* Store the query for the IMMV to pg_ivwm_immv
|
||||
*/
|
||||
static void
|
||||
StoreImmvQuery(Oid viewOid, Query *viewQuery)
|
||||
|
|
@ -1740,7 +1723,6 @@ StoreImmvQuery(Oid viewOid, Query *viewQuery)
|
|||
values[Anum_pg_ivm_immv_immvrelid -1 ] = ObjectIdGetDatum(viewOid);
|
||||
values[Anum_pg_ivm_immv_ispopulated -1 ] = BoolGetDatum(false);
|
||||
values[Anum_pg_ivm_immv_viewdef -1 ] = CStringGetTextDatum(querytree);
|
||||
isNulls[Anum_pg_ivm_immv_lastivmupdate -1 ] = true;
|
||||
|
||||
pgIvmImmv = table_open(PgIvmImmvRelationId(), RowExclusiveLock);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -51,7 +51,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -92,7 +92,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -131,7 +131,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -174,7 +174,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -212,7 +212,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -257,7 +257,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -297,7 +297,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -57,7 +57,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -101,7 +101,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -140,7 +140,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -188,7 +188,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -226,7 +226,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -276,7 +276,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -318,7 +318,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -53,7 +53,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -93,7 +93,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -128,7 +128,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -176,7 +176,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -210,7 +210,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
tx1: NOTICE: could not create an index on immv "mv" automatically
|
||||
|
|
@ -260,7 +260,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
@ -302,7 +302,7 @@ step create:
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
|
||||
create_immv
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -81,21 +81,6 @@ SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|||
4 | 40 | 104
|
||||
(4 rows)
|
||||
|
||||
-- test for renaming column name to camel style
|
||||
BEGIN;
|
||||
ALTER TABLE mv_base_a RENAME i TO "I";
|
||||
ALTER TABLE mv_base_a RENAME j TO "J";
|
||||
UPDATE mv_base_a SET "J" = 0 WHERE "I" = 1;
|
||||
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
||||
i | j | k
|
||||
---+----+-----
|
||||
1 | 0 | 101
|
||||
2 | 20 | 102
|
||||
3 | 30 | 103
|
||||
4 | 40 | 104
|
||||
(4 rows)
|
||||
|
||||
ROLLBACK;
|
||||
-- TRUNCATE a base table in join views
|
||||
BEGIN;
|
||||
TRUNCATE mv_base_a;
|
||||
|
|
@ -1395,8 +1380,6 @@ CREATE FUNCTION mytype_out(mytype)
|
|||
RETURNS cstring AS 'int4out'
|
||||
LANGUAGE INTERNAL STRICT IMMUTABLE;
|
||||
NOTICE: argument type mytype is only a shell
|
||||
LINE 1: CREATE FUNCTION mytype_out(mytype)
|
||||
^
|
||||
CREATE TYPE mytype (
|
||||
LIKE = int4,
|
||||
INPUT = mytype_in,
|
||||
|
|
@ -1533,17 +1516,17 @@ ERROR: VALUES is not supported on incrementally maintainable materialized view
|
|||
SELECT pgivm.create_immv('mv_ivm_only_values2', 'SELECT * FROM (values(1)) AS tmp');
|
||||
ERROR: VALUES is not supported on incrementally maintainable materialized view
|
||||
-- views containing base tables with Row Level Security
|
||||
DROP USER IF EXISTS regress_ivm_admin;
|
||||
NOTICE: role "regress_ivm_admin" does not exist, skipping
|
||||
DROP USER IF EXISTS regress_ivm_user;
|
||||
NOTICE: role "regress_ivm_user" does not exist, skipping
|
||||
CREATE USER regress_ivm_admin;
|
||||
CREATE USER regress_ivm_user;
|
||||
DROP USER IF EXISTS ivm_admin;
|
||||
NOTICE: role "ivm_admin" does not exist, skipping
|
||||
DROP USER IF EXISTS ivm_user;
|
||||
NOTICE: role "ivm_user" does not exist, skipping
|
||||
CREATE USER ivm_admin;
|
||||
CREATE USER ivm_user;
|
||||
--- create a table with RLS
|
||||
SET SESSION AUTHORIZATION regress_ivm_admin;
|
||||
SET SESSION AUTHORIZATION ivm_admin;
|
||||
CREATE TABLE rls_tbl(id int, data text, owner name);
|
||||
INSERT INTO rls_tbl VALUES
|
||||
(1,'foo','regress_ivm_user'),
|
||||
(1,'foo','ivm_user'),
|
||||
(2,'bar','postgres');
|
||||
CREATE TABLE num_tbl(id int, num text);
|
||||
INSERT INTO num_tbl VALUES
|
||||
|
|
@ -1558,8 +1541,8 @@ CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = curre
|
|||
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
|
||||
GRANT ALL on rls_tbl TO PUBLIC;
|
||||
GRANT ALL on num_tbl TO PUBLIC;
|
||||
--- create a view owned by regress_ivm_user
|
||||
SET SESSION AUTHORIZATION regress_ivm_user;
|
||||
--- create a view owned by ivm_user
|
||||
SET SESSION AUTHORIZATION ivm_user;
|
||||
SELECT pgivm.create_immv('ivm_rls', 'SELECT * FROM rls_tbl');
|
||||
NOTICE: could not create an index on immv "ivm_rls" automatically
|
||||
DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
|
||||
|
|
@ -1571,41 +1554,41 @@ HINT: Create an index on the immv for efficient incremental maintenance.
|
|||
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
id | data | owner
|
||||
----+------+------------------
|
||||
1 | foo | regress_ivm_user
|
||||
----+------+----------
|
||||
1 | foo | ivm_user
|
||||
(1 row)
|
||||
|
||||
RESET SESSION AUTHORIZATION;
|
||||
--- inserts rows owned by different users
|
||||
INSERT INTO rls_tbl VALUES
|
||||
(3,'baz','regress_ivm_user'),
|
||||
(3,'baz','ivm_user'),
|
||||
(4,'qux','postgres');
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
id | data | owner
|
||||
----+------+------------------
|
||||
1 | foo | regress_ivm_user
|
||||
3 | baz | regress_ivm_user
|
||||
----+------+----------
|
||||
1 | foo | ivm_user
|
||||
3 | baz | ivm_user
|
||||
(2 rows)
|
||||
|
||||
--- combination of diffent kinds of commands
|
||||
WITH
|
||||
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
|
||||
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','ivm_user')),
|
||||
u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
|
||||
u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2)
|
||||
u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2)
|
||||
SELECT;
|
||||
--
|
||||
(1 row)
|
||||
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
id | data | owner
|
||||
----+-------+------------------
|
||||
2 | bar | regress_ivm_user
|
||||
3 | baz | regress_ivm_user
|
||||
6 | corge | regress_ivm_user
|
||||
----+-------+----------
|
||||
2 | bar | ivm_user
|
||||
3 | baz | ivm_user
|
||||
6 | corge | ivm_user
|
||||
(3 rows)
|
||||
|
||||
---
|
||||
SET SESSION AUTHORIZATION regress_ivm_user;
|
||||
SET SESSION AUTHORIZATION ivm_user;
|
||||
SELECT pgivm.create_immv('ivm_rls2', 'SELECT * FROM rls_tbl JOIN num_tbl USING(id)');
|
||||
NOTICE: could not create an index on immv "ivm_rls2" automatically
|
||||
DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
|
||||
|
|
@ -1625,10 +1608,10 @@ SELECT;
|
|||
|
||||
SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
|
||||
id | data | owner | num
|
||||
----+-------+------------------+---------
|
||||
2 | bar | regress_ivm_user | two
|
||||
3 | baz_2 | regress_ivm_user | three_2
|
||||
6 | corge | regress_ivm_user | six
|
||||
----+-------+----------+---------
|
||||
2 | bar | ivm_user | two
|
||||
3 | baz_2 | ivm_user | three_2
|
||||
6 | corge | ivm_user | six
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE rls_tbl CASCADE;
|
||||
|
|
@ -1636,8 +1619,8 @@ NOTICE: drop cascades to 2 other objects
|
|||
DETAIL: drop cascades to table ivm_rls
|
||||
drop cascades to table ivm_rls2
|
||||
DROP TABLE num_tbl CASCADE;
|
||||
DROP USER regress_ivm_user;
|
||||
DROP USER regress_ivm_admin;
|
||||
DROP USER ivm_user;
|
||||
DROP USER ivm_admin;
|
||||
-- automatic index creation
|
||||
BEGIN;
|
||||
CREATE TABLE base_a (i int primary key, j int);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
25
matview.c
25
matview.c
|
|
@ -617,11 +617,7 @@ refresh_immv_datafill(DestReceiver *dest, Query *query,
|
|||
ExecutorStart(queryDesc, 0);
|
||||
|
||||
/* run the plan */
|
||||
#if PG_VERSION_NUM < 180000
|
||||
ExecutorRun(queryDesc, ForwardScanDirection, 0, true);
|
||||
#else
|
||||
ExecutorRun(queryDesc, ForwardScanDirection, 0);
|
||||
#endif
|
||||
|
||||
processed = queryDesc->estate->es_processed;
|
||||
|
||||
|
|
@ -974,19 +970,12 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
|
|||
* If this is the last AFTER trigger call, continue and update the view.
|
||||
*/
|
||||
|
||||
/*
|
||||
* record the subxid that updated the view incrementally
|
||||
*
|
||||
* Note:
|
||||
* PG16 or later has list_member_xid and lappend_xid. It would be better
|
||||
* to use them, but we use integer for supporting older PGs since there
|
||||
* is no problem or now.
|
||||
*/
|
||||
/* record the subxid that updated the view incrementally */
|
||||
subxid = GetCurrentSubTransactionId();
|
||||
if (!list_member_int(entry->subxids, subxid))
|
||||
if (!list_member_xid(entry->subxids, subxid))
|
||||
{
|
||||
oldcxt = MemoryContextSwitchTo(TopTransactionContext);
|
||||
entry->subxids = lappend_int(entry->subxids, subxid);
|
||||
entry->subxids = lappend_xid(entry->subxids, subxid);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
||||
|
|
@ -1616,7 +1605,7 @@ make_subquery_targetlist_from_table(MV_TriggerTable *table)
|
|||
if (attr->attisdropped)
|
||||
appendStringInfo(&str, "null");
|
||||
else
|
||||
appendStringInfo(&str, "%s", quote_identifier(NameStr(attr->attname)));
|
||||
appendStringInfo(&str, "%s", NameStr(attr->attname));
|
||||
}
|
||||
|
||||
return str.data;
|
||||
|
|
@ -3435,11 +3424,7 @@ clean_up_IVM_hash_entry(MV_TriggerHashEntry *entry, bool is_abort,
|
|||
{
|
||||
foreach(lc, entry->subxids)
|
||||
{
|
||||
/* Note:
|
||||
* PG16 or later has lfirst_xid, but we use lfirst_int for
|
||||
* supporting older PGs since there is no problem or now.
|
||||
*/
|
||||
if (lfirst_int(lc) == subxid)
|
||||
if (lfirst_xid(lc) == subxid)
|
||||
{
|
||||
entry->subxids = list_delete_cell(entry->subxids, lc);
|
||||
break;
|
||||
|
|
|
|||
101
meson.build
101
meson.build
|
|
@ -1,101 +0,0 @@
|
|||
project('pg_ivm', ['c'])
|
||||
|
||||
pg_config = find_program('pg_config')
|
||||
|
||||
bindir = run_command(pg_config, '--bindir', check: true).stdout().strip()
|
||||
includedir_server = run_command(pg_config, '--includedir-server', check: true).stdout().strip()
|
||||
includedir = run_command(pg_config, '--includedir', check: true).stdout().strip()
|
||||
pkglibdir = run_command(pg_config, '--pkglibdir', check: true).stdout().strip()
|
||||
sharedir = run_command(pg_config, '--sharedir', check: true).stdout().strip()
|
||||
libdir = run_command(pg_config, '--libdir', check: true).stdout().strip()
|
||||
|
||||
module_name = meson.project_name()
|
||||
|
||||
# ruleutils.c includes ruleutils_13.c or ruleutils_14.c based on PostgreSQL version
|
||||
# Note: We don't need to explicitly add these files since they're included by ruleutils.c
|
||||
pg_ivm_sources = files(
|
||||
'createas.c',
|
||||
'matview.c',
|
||||
'pg_ivm.c',
|
||||
'ruleutils.c',
|
||||
'subselect.c',
|
||||
)
|
||||
|
||||
if meson.get_compiler('c').get_id() == 'msvc'
|
||||
incdir = [includedir_server / 'port/win32_msvc',
|
||||
includedir_server / 'port/win32',
|
||||
includedir_server,
|
||||
includedir]
|
||||
postgres_lib = meson.get_compiler('c').find_library(
|
||||
'postgres',
|
||||
dirs: libdir,
|
||||
static: true,
|
||||
required: true
|
||||
)
|
||||
else
|
||||
incdir = [ includedir_server ]
|
||||
postgres_lib = []
|
||||
endif
|
||||
|
||||
shared_module(module_name,
|
||||
pg_ivm_sources,
|
||||
include_directories: incdir,
|
||||
install: true,
|
||||
install_dir: pkglibdir,
|
||||
name_prefix: '',
|
||||
dependencies: postgres_lib,
|
||||
)
|
||||
|
||||
install_data(
|
||||
'pg_ivm--1.0.sql',
|
||||
'pg_ivm--1.0--1.1.sql',
|
||||
'pg_ivm--1.1--1.2.sql',
|
||||
'pg_ivm--1.2--1.3.sql',
|
||||
'pg_ivm--1.3--1.4.sql',
|
||||
'pg_ivm--1.4--1.5.sql',
|
||||
'pg_ivm--1.5--1.6.sql',
|
||||
'pg_ivm--1.6--1.7.sql',
|
||||
'pg_ivm--1.7--1.8.sql',
|
||||
'pg_ivm--1.8--1.9.sql',
|
||||
'pg_ivm--1.9--1.10.sql',
|
||||
'pg_ivm--1.10.sql',
|
||||
'pg_ivm--1.10--1.11.sql',
|
||||
'pg_ivm--1.11--1.12.sql',
|
||||
'pg_ivm.control',
|
||||
install_dir: sharedir / 'extension',
|
||||
)
|
||||
|
||||
pg_regress = find_program('pg_regress',
|
||||
dirs: [pkglibdir / 'pgxs/src/test/regress']
|
||||
)
|
||||
|
||||
regress_tests = ['pg_ivm', 'create_immv', 'refresh_immv']
|
||||
|
||||
test('regress',
|
||||
pg_regress,
|
||||
args: ['--bindir', bindir,
|
||||
'--inputdir', meson.current_source_dir(),
|
||||
] + regress_tests,
|
||||
)
|
||||
|
||||
pg_isolation_regress = find_program('pg_isolation_regress',
|
||||
dirs: [pkglibdir / 'pgxs/src/test/isolation']
|
||||
)
|
||||
|
||||
isolation_tests = [
|
||||
'create_insert', 'refresh_insert', 'insert_insert',
|
||||
'create_insert2', 'refresh_insert2', 'insert_insert2',
|
||||
'create_insert3', 'refresh_insert3', 'insert_insert3'
|
||||
]
|
||||
|
||||
isolation_opts = [
|
||||
'--load-extension','pg_ivm',
|
||||
]
|
||||
|
||||
test('isolation',
|
||||
pg_isolation_regress,
|
||||
args: ['--bindir', bindir,
|
||||
'--inputdir', meson.current_source_dir(),
|
||||
'--outputdir', 'output_iso',
|
||||
] + isolation_opts + isolation_tests,
|
||||
)
|
||||
4
pg_ivm.c
4
pg_ivm.c
|
|
@ -224,11 +224,7 @@ create_immv(PG_FUNCTION_ARGS)
|
|||
ctas->into->options = NIL;
|
||||
ctas->into->onCommit = ONCOMMIT_NOOP;
|
||||
ctas->into->tableSpaceName = NULL;
|
||||
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 180000)
|
||||
ctas->into->viewQuery = (Query *) parsetree->stmt;
|
||||
#else
|
||||
ctas->into->viewQuery = parsetree->stmt;
|
||||
#endif
|
||||
ctas->into->skipData = false;
|
||||
|
||||
query = transformStmt(pstate, (Node *)ctas);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# incremental view maintenance extension
|
||||
# incremental view maintenance extension_
|
||||
comment = 'incremental view maintenance on PostgreSQL'
|
||||
default_version = '1.12'
|
||||
default_version = '1.10'
|
||||
module_pathname = '$libdir/pg_ivm'
|
||||
relocatable = false
|
||||
schema = pg_catalog
|
||||
|
|
|
|||
6
pg_ivm.h
6
pg_ivm.h
|
|
@ -52,10 +52,10 @@ extern ObjectAddress ExecRefreshImmv(const RangeVar *relation, bool skipData,
|
|||
extern ObjectAddress RefreshImmvByOid(Oid matviewOid, bool is_create, bool skipData,
|
||||
const char *queryString, QueryCompletion *qc);
|
||||
extern bool ImmvIncrementalMaintenanceIsEnabled(void);
|
||||
extern PGDLLEXPORT Datum IVM_immediate_before(PG_FUNCTION_ARGS);
|
||||
extern PGDLLEXPORT Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
|
||||
extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);
|
||||
extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
|
||||
extern Query* rewrite_query_for_exists_subquery(Query *query);
|
||||
extern PGDLLEXPORT Datum ivm_visible_in_prestate(PG_FUNCTION_ARGS);
|
||||
extern Datum ivm_visible_in_prestate(PG_FUNCTION_ARGS);
|
||||
extern void AtAbort_IVM(SubTransactionId subtxid);
|
||||
extern void AtPreCommit_IVM(void);
|
||||
extern char *getColumnNameStartWith(RangeTblEntry *rte, char *str, int *attnum);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# How to build RPM:
|
||||
#
|
||||
# rpmbuild -bb pg_ivm.spec --define "pgmajorversion 18" --define "pginstdir /usr/pgsql-18"
|
||||
# rpmbuild -bb pg_ivm.spec --define "pgmajorversion 17" --define "pginstdir /usr/pgsql-17"
|
||||
|
||||
%global sname pg_ivm
|
||||
|
||||
|
|
@ -8,11 +8,11 @@
|
|||
%global llvm 1
|
||||
%endif
|
||||
|
||||
Summary: Incremental View Maintenance (IVM) feature for PostgreSQL.
|
||||
Summary: PostgreSQL-based distributed RDBMS
|
||||
Name: %{sname}_%{pgmajorversion}
|
||||
Version: 1.12
|
||||
Version: 1.10
|
||||
Release: 1%{dist}
|
||||
License: PostgreSQL
|
||||
License: BSD
|
||||
Vendor: IVM Development Group
|
||||
URL: https://github.com/sraoss/%{sname}
|
||||
Source0: https://github.com/sraoss/%{sname}/archive/v%{version}.tar.gz
|
||||
|
|
@ -55,11 +55,7 @@ PATH=%{pginstdir}/bin:$PATH %{__make} %{?_smp_mflags} INSTALL_PREFIX=%{buildroot
|
|||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Sep 4 2025 - Yugo Nagata <nagata@sraoss.co.jp> 1.12-1
|
||||
- Update to 1.12
|
||||
* Mon May 25 2025 - Yugo Nagata <nagata@sraoss.co.jp> 1.11-1
|
||||
- Update to 1.11
|
||||
* Tue Mar 11 2025 - Yugo Nagata <nagata@sraoss.co.jp> 1.10-1
|
||||
* xxxxx - Yugo Nagata <nagata@sraoss.co.jp> 1.10-1
|
||||
- Update to 1.10
|
||||
* Fri Jul 31 2024 - Yugo Nagata <nagata@sraoss.co.jp> 1.9-1
|
||||
- Update to 1.9
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ step create {
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
step check1 {SELECT check_mv();}
|
||||
step c1 { COMMIT; }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ step create {
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
step check1 {SELECT check_mv();}
|
||||
step c1 { COMMIT; }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ step create {
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
step check1 {SELECT check_mv();}
|
||||
step c1 { COMMIT; }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ setup
|
|||
CREATE FUNCTION check_mv() RETURNS text AS
|
||||
$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
|
||||
FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
|
||||
(SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) $$ LANGUAGE sql;
|
||||
}
|
||||
|
||||
teardown
|
||||
|
|
|
|||
|
|
@ -37,14 +37,6 @@ SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
|||
ROLLBACK;
|
||||
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
||||
|
||||
-- test for renaming column name to camel style
|
||||
BEGIN;
|
||||
ALTER TABLE mv_base_a RENAME i TO "I";
|
||||
ALTER TABLE mv_base_a RENAME j TO "J";
|
||||
UPDATE mv_base_a SET "J" = 0 WHERE "I" = 1;
|
||||
SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
|
||||
ROLLBACK;
|
||||
|
||||
-- TRUNCATE a base table in join views
|
||||
BEGIN;
|
||||
TRUNCATE mv_base_a;
|
||||
|
|
@ -596,16 +588,16 @@ SELECT pgivm.create_immv('mv_ivm_only_values2', 'SELECT * FROM (values(1)) AS t
|
|||
|
||||
|
||||
-- views containing base tables with Row Level Security
|
||||
DROP USER IF EXISTS regress_ivm_admin;
|
||||
DROP USER IF EXISTS regress_ivm_user;
|
||||
CREATE USER regress_ivm_admin;
|
||||
CREATE USER regress_ivm_user;
|
||||
DROP USER IF EXISTS ivm_admin;
|
||||
DROP USER IF EXISTS ivm_user;
|
||||
CREATE USER ivm_admin;
|
||||
CREATE USER ivm_user;
|
||||
|
||||
--- create a table with RLS
|
||||
SET SESSION AUTHORIZATION regress_ivm_admin;
|
||||
SET SESSION AUTHORIZATION ivm_admin;
|
||||
CREATE TABLE rls_tbl(id int, data text, owner name);
|
||||
INSERT INTO rls_tbl VALUES
|
||||
(1,'foo','regress_ivm_user'),
|
||||
(1,'foo','ivm_user'),
|
||||
(2,'bar','postgres');
|
||||
CREATE TABLE num_tbl(id int, num text);
|
||||
INSERT INTO num_tbl VALUES
|
||||
|
|
@ -622,28 +614,28 @@ ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
|
|||
GRANT ALL on rls_tbl TO PUBLIC;
|
||||
GRANT ALL on num_tbl TO PUBLIC;
|
||||
|
||||
--- create a view owned by regress_ivm_user
|
||||
SET SESSION AUTHORIZATION regress_ivm_user;
|
||||
--- create a view owned by ivm_user
|
||||
SET SESSION AUTHORIZATION ivm_user;
|
||||
SELECT pgivm.create_immv('ivm_rls', 'SELECT * FROM rls_tbl');
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
RESET SESSION AUTHORIZATION;
|
||||
|
||||
--- inserts rows owned by different users
|
||||
INSERT INTO rls_tbl VALUES
|
||||
(3,'baz','regress_ivm_user'),
|
||||
(3,'baz','ivm_user'),
|
||||
(4,'qux','postgres');
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
|
||||
--- combination of diffent kinds of commands
|
||||
WITH
|
||||
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
|
||||
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','ivm_user')),
|
||||
u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
|
||||
u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2)
|
||||
u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2)
|
||||
SELECT;
|
||||
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
|
||||
|
||||
---
|
||||
SET SESSION AUTHORIZATION regress_ivm_user;
|
||||
SET SESSION AUTHORIZATION ivm_user;
|
||||
SELECT pgivm.create_immv('ivm_rls2', 'SELECT * FROM rls_tbl JOIN num_tbl USING(id)');
|
||||
RESET SESSION AUTHORIZATION;
|
||||
|
||||
|
|
@ -656,8 +648,8 @@ SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
|
|||
DROP TABLE rls_tbl CASCADE;
|
||||
DROP TABLE num_tbl CASCADE;
|
||||
|
||||
DROP USER regress_ivm_user;
|
||||
DROP USER regress_ivm_admin;
|
||||
DROP USER ivm_user;
|
||||
DROP USER ivm_admin;
|
||||
|
||||
-- automatic index creation
|
||||
BEGIN;
|
||||
|
|
|
|||
Loading…
Reference in a new issue