Compare commits

...

16 commits

Author SHA1 Message Date
Yugo Nagata
132816cffe Prepare 1.12 2025-09-04 22:59:18 +09:00
Yugo Nagata
b926225089 Fix role names in regression test expected file with "regress_" for old versions
Commit 3dcf7db2cb added the prefix "regress_" to role names in the regression
tests, but the expected file for old versions was overlooked, since that
file was added after the pull request for this fix.
2025-09-04 22:23:59 +09:00
Michael Paquier
3dcf7db2cb
Prefix role names in regression tests with "regress_" (#146)
Building PostgreSQL with -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
causes the regression tests of pg_ivm to fail, because the role names
used in the tests are not compliant with what upstream expects.

Enforcing restrictions in the regression tests is a good practice to
have, as roles are shared objects and running the tests could manipulate
existing clusters in unwanted ways.
2025-09-04 18:26:54 +09:00
Michael Paquier
e1c26275ff
Rework .gitignore (#147)
This commit reworks .gitignore to be more portable depending on the
options used in the PostgreSQL build this module links to, in various
aspects:
- Ignore code generated for LLVM bitcode.
- Dependencies.
- Libraries.
- Coverage files.
- WIN32 and MacOS files.
- Ignore isolation test subdirectory.
- Do *not* ignore regression.diffs and regression.out.  This is more
useful to detect if something is broken in the tree, in line with
upstream core practices.

The contents of this file are updated to something closer to what
upstream uses.
2025-09-04 15:57:51 +09:00
Li
e92cbe543d
add missing ` for REFRESH MATERIALIZED VIEW (#148) 2025-08-06 16:09:58 +09:00
Yugo Nagata
96fdf6f789
Add support for PostgreSQL 18 (#145)
- Fixed compiler errors and warnings.
- Added expected/pg_ivm_0.out to accommodate message changes introduced in PostgreSQL 18.
2025-07-09 19:26:28 +09:00
Yugo Nagata
8232f7fc9b
Update README.md
In addition to recreation, refresh may be used to apply changes to row-level security policies.
2025-06-11 16:59:09 +09:00
Yugo Nagata
f4b40e93a6 Prepare 1.11 2025-05-26 17:07:09 +09:00
Yugo Nagata
f03a6a71da Merge branch 'pr123' 2025-05-26 15:58:01 +09:00
Yugo Nagata
deb2652335 Some fix
- Revert a typo fix on IMMV deletion

For consistency of the documents, dropped IMMV should be immv
rather than immv_agg

- Remove the description about the entry modification in the catalog

Actually, the catalog entry doesn't not change when an IMMV is renamed
since it just references IMMV's Oids.
2025-05-26 15:52:31 +09:00
Yugo Nagata
406381cc37
Add meson.build (#142)
It is experimental. The codes were partially copied from orafce.
2025-05-26 15:11:27 +09:00
Yugo Nagata
1d78c158a2
Fix potential segmentation fault in create_immv (#141)
Ensure that newly added column information is passed to heap_form_tuple().
Previously, omission of this data led to a segmentation fault when building
with MSVC, though the issue has not not observed on Linux.
2025-05-26 11:44:11 +09:00
Yuta MASANO
49b52bcd5e
Fix Windows linkage errors by adding PGDLLEXPORT to function declarations (#139)
The following functions in pg_ivm.h are now explicitly marked with
PGDLLEXPORT:

- IVM_immediate_before
- IVM_immediate_maintenance
- ivm_visible_in_prestate

This change resolves linkage mismatches between function declarations in
the header and their definitions using PG_FUNCTION_INFO_V1 in the
implementation file, which caused build failures on Windows
environments.
2025-05-26 09:41:06 +09:00
Yugo Nagata
3f33229efe
Use quoted column names in targetlist of subquery substituting modified table (#135)
Fix a bug introduced by 5b8b2f0, which built targetlist of subquery substituting
modified table. When a table has a column whose name includes a capital letter,
incremental view maintenance failed because crafted targetlist contains incorrect
column name which was parsed as lower case.

Isseu #124
2025-05-08 17:09:37 +09:00
Ivan Kozik
7966a92b8e
Fix typo in README (#134) 2025-05-07 09:38:38 +09:00
qmitchell-aa
c31d5ec9ab
README - detail on renaming IMMV + fix typo in drop IMMV 2025-03-12 09:15:29 -04:00
15 changed files with 2011 additions and 68 deletions

28
.gitignore vendored
View file

@ -1,8 +1,30 @@
# Global excludes across all subdirectories
*.bc
# Object files
*.o
# Coverage files
*.gcno
*.gcda
# Libraries
*.lib
*.a
# LLVM bitcode
*.bc
# Shared objects (inc. Windows DLLs)
*.dll
*.so
regression.*
*.so.*
*.dylib
# Executables
*.exe
*.app
# Dependencies
.deps
# Generated subdirectories
/results/
/output_iso/

View file

@ -18,7 +18,8 @@ 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.sql \
pg_ivm--1.10--1.11.sql pg_ivm--1.11--1.12.sql
REGRESS = pg_ivm create_immv refresh_immv

View file

@ -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, and 17.
The extension is compatible with PostgreSQL 13, 14, 15, 16, 17, and 18.
## Description
@ -101,7 +101,7 @@ When `pg_ivm` is installed, the 'pgivm' schema is created, along with the follow
Use `create_immv` function to create IMMV.
```
pgivim.create_immv(immv_name text, view_definition text) RETURNS bigint
pgivm.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 +120,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.
@ -300,7 +300,7 @@ test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM
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;
test=# DROP TABLE immv
DROP TABLE
test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM pgivm.pg_ivm_immv;
@ -316,6 +316,27 @@ test=# SELECT immvrelid AS immv, pgivm.get_immv_def(immvrelid) AS immv_def FROM
(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.
@ -397,7 +418,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 recreate 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 refresh or recreate the IMMV.
### How to Disable or Enable Immediate Maintenance

View file

@ -101,7 +101,11 @@ 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 */
@ -148,7 +152,12 @@ 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;
@ -1514,10 +1523,18 @@ 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);
@ -1723,6 +1740,7 @@ 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);

View file

@ -81,6 +81,21 @@ 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;
@ -1380,6 +1395,8 @@ 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,
@ -1516,17 +1533,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 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;
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;
--- create a table with RLS
SET SESSION AUTHORIZATION ivm_admin;
SET SESSION AUTHORIZATION regress_ivm_admin;
CREATE TABLE rls_tbl(id int, data text, owner name);
INSERT INTO rls_tbl VALUES
(1,'foo','ivm_user'),
(1,'foo','regress_ivm_user'),
(2,'bar','postgres');
CREATE TABLE num_tbl(id int, num text);
INSERT INTO num_tbl VALUES
@ -1541,8 +1558,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 ivm_user
SET SESSION AUTHORIZATION ivm_user;
--- create a view owned by regress_ivm_user
SET SESSION AUTHORIZATION regress_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.
@ -1553,42 +1570,42 @@ HINT: Create an index on the immv for efficient incremental maintenance.
(1 row)
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
id | data | owner
----+------+----------
1 | foo | ivm_user
id | data | owner
----+------+------------------
1 | foo | regress_ivm_user
(1 row)
RESET SESSION AUTHORIZATION;
--- inserts rows owned by different users
INSERT INTO rls_tbl VALUES
(3,'baz','ivm_user'),
(3,'baz','regress_ivm_user'),
(4,'qux','postgres');
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
id | data | owner
----+------+----------
1 | foo | ivm_user
3 | baz | ivm_user
id | data | owner
----+------+------------------
1 | foo | regress_ivm_user
3 | baz | regress_ivm_user
(2 rows)
--- combination of diffent kinds of commands
WITH
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','ivm_user')),
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2)
u2 AS (UPDATE rls_tbl SET owner = 'regress_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 | ivm_user
3 | baz | ivm_user
6 | corge | ivm_user
id | data | owner
----+-------+------------------
2 | bar | regress_ivm_user
3 | baz | regress_ivm_user
6 | corge | regress_ivm_user
(3 rows)
---
SET SESSION AUTHORIZATION ivm_user;
SET SESSION AUTHORIZATION regress_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.
@ -1607,11 +1624,11 @@ SELECT;
(1 row)
SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
id | data | owner | num
----+-------+----------+---------
2 | bar | ivm_user | two
3 | baz_2 | ivm_user | three_2
6 | corge | ivm_user | six
id | data | owner | num
----+-------+------------------+---------
2 | bar | regress_ivm_user | two
3 | baz_2 | regress_ivm_user | three_2
6 | corge | regress_ivm_user | six
(3 rows)
DROP TABLE rls_tbl CASCADE;
@ -1619,8 +1636,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 ivm_user;
DROP USER ivm_admin;
DROP USER regress_ivm_user;
DROP USER regress_ivm_admin;
-- automatic index creation
BEGIN;
CREATE TABLE base_a (i int primary key, j int);

1743
expected/pg_ivm_0.out Normal file

File diff suppressed because it is too large Load diff

View file

@ -617,7 +617,11 @@ 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;
@ -1612,7 +1616,7 @@ make_subquery_targetlist_from_table(MV_TriggerTable *table)
if (attr->attisdropped)
appendStringInfo(&str, "null");
else
appendStringInfo(&str, "%s", NameStr(attr->attname));
appendStringInfo(&str, "%s", quote_identifier(NameStr(attr->attname)));
}
return str.data;

101
meson.build Normal file
View file

@ -0,0 +1,101 @@
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,
)

0
pg_ivm--1.10--1.11.sql Normal file
View file

0
pg_ivm--1.11--1.12.sql Normal file
View file

View file

@ -224,10 +224,14 @@ 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);
query = transformStmt(pstate, (Node *) ctas);
Assert(query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt));
ExecCreateImmv(pstate, (CreateTableAsStmt *) query->utilityStmt, &qc);

View file

@ -1,6 +1,6 @@
# incremental view maintenance extension_
# incremental view maintenance extension
comment = 'incremental view maintenance on PostgreSQL'
default_version = '1.10'
default_version = '1.12'
module_pathname = '$libdir/pg_ivm'
relocatable = false
schema = pg_catalog

View file

@ -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 Datum IVM_immediate_before(PG_FUNCTION_ARGS);
extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
extern PGDLLEXPORT Datum IVM_immediate_before(PG_FUNCTION_ARGS);
extern PGDLLEXPORT Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
extern Query* rewrite_query_for_exists_subquery(Query *query);
extern Datum ivm_visible_in_prestate(PG_FUNCTION_ARGS);
extern PGDLLEXPORT 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);

View file

@ -1,6 +1,6 @@
# How to build RPM:
#
# rpmbuild -bb pg_ivm.spec --define "pgmajorversion 17" --define "pginstdir /usr/pgsql-17"
# rpmbuild -bb pg_ivm.spec --define "pgmajorversion 18" --define "pginstdir /usr/pgsql-18"
%global sname pg_ivm
@ -8,11 +8,11 @@
%global llvm 1
%endif
Summary: PostgreSQL-based distributed RDBMS
Summary: Incremental View Maintenance (IVM) feature for PostgreSQL.
Name: %{sname}_%{pgmajorversion}
Version: 1.10
Version: 1.12
Release: 1%{dist}
License: BSD
License: PostgreSQL
Vendor: IVM Development Group
URL: https://github.com/sraoss/%{sname}
Source0: https://github.com/sraoss/%{sname}/archive/v%{version}.tar.gz
@ -55,7 +55,11 @@ PATH=%{pginstdir}/bin:$PATH %{__make} %{?_smp_mflags} INSTALL_PREFIX=%{buildroot
%endif
%changelog
* xxxxx - Yugo Nagata <nagata@sraoss.co.jp> 1.10-1
* 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
- Update to 1.10
* Fri Jul 31 2024 - Yugo Nagata <nagata@sraoss.co.jp> 1.9-1
- Update to 1.9

View file

@ -37,6 +37,14 @@ 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;
@ -588,16 +596,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 ivm_admin;
DROP USER IF EXISTS ivm_user;
CREATE USER ivm_admin;
CREATE USER ivm_user;
DROP USER IF EXISTS regress_ivm_admin;
DROP USER IF EXISTS regress_ivm_user;
CREATE USER regress_ivm_admin;
CREATE USER regress_ivm_user;
--- create a table with RLS
SET SESSION AUTHORIZATION ivm_admin;
SET SESSION AUTHORIZATION regress_ivm_admin;
CREATE TABLE rls_tbl(id int, data text, owner name);
INSERT INTO rls_tbl VALUES
(1,'foo','ivm_user'),
(1,'foo','regress_ivm_user'),
(2,'bar','postgres');
CREATE TABLE num_tbl(id int, num text);
INSERT INTO num_tbl VALUES
@ -614,28 +622,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 ivm_user
SET SESSION AUTHORIZATION ivm_user;
--- create a view owned by regress_ivm_user
SET SESSION AUTHORIZATION regress_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','ivm_user'),
(3,'baz','regress_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','ivm_user')),
i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2)
u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2)
SELECT;
SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
---
SET SESSION AUTHORIZATION ivm_user;
SET SESSION AUTHORIZATION regress_ivm_user;
SELECT pgivm.create_immv('ivm_rls2', 'SELECT * FROM rls_tbl JOIN num_tbl USING(id)');
RESET SESSION AUTHORIZATION;
@ -648,8 +656,8 @@ 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;
DROP USER regress_ivm_user;
DROP USER regress_ivm_admin;
-- automatic index creation
BEGIN;