From e31dc21eaa58356f351fbede5bc3ee6674b23e00 Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Fri, 30 Sep 2022 23:49:51 +0900 Subject: [PATCH 1/2] Use object_access_hook to drop an IMMV entry from pg_ivm_immv (#29) Previously, we used an event trigger and executed DELETE command, but it caused a privilege error when non-superuser dropped a table even if it is irrelevant to IMMV. To fix it, we now use object_access_hook and an entry is deleted via CatalogTupleDelete which doesn't need the superuser privilege. Issue #25 --- pg_ivm--1.2--1.3.sql | 5 +++++ pg_ivm.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/pg_ivm--1.2--1.3.sql b/pg_ivm--1.2--1.3.sql index 1555567..13e1e1d 100644 --- a/pg_ivm--1.2--1.3.sql +++ b/pg_ivm--1.2--1.3.sql @@ -11,3 +11,8 @@ RETURNS text STRICT AS 'MODULE_PATHNAME', 'get_immv_def' LANGUAGE C; + +-- event trigger + +DROP EVENT TRIGGER pg_ivm_sql_drop_trigger; +DROP FUNCTION pg_ivm_sql_drop_trigger_func; diff --git a/pg_ivm.c b/pg_ivm.c index 8db5ac0..a9f6d3b 100644 --- a/pg_ivm.c +++ b/pg_ivm.c @@ -11,10 +11,13 @@ */ #include "postgres.h" +#include "access/genam.h" #include "access/table.h" #include "access/xact.h" #include "catalog/dependency.h" +#include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_namespace_d.h" #include "catalog/pg_trigger_d.h" #include "commands/trigger.h" @@ -23,6 +26,7 @@ #include "parser/scansup.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/regproc.h" #include "utils/rel.h" @@ -35,6 +39,8 @@ PG_MODULE_MAGIC; static Oid pg_ivm_immv_id = InvalidOid; static Oid pg_ivm_immv_pkey_id = InvalidOid; +static object_access_hook_type PrevObjectAccessHook = NULL; + void _PG_init(void); static void IvmXactCallback(XactEvent event, void *arg); @@ -42,6 +48,9 @@ static void IvmSubXactCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg); static void parseNameAndColumns(const char *string, List **names, List **colNames); +static void PgIvmObjectAccessHook(ObjectAccessType access, Oid classId, + Oid objectId, int subId, void *arg); + /* SQL callable functions */ PG_FUNCTION_INFO_V1(create_immv); PG_FUNCTION_INFO_V1(refresh_immv); @@ -75,6 +84,9 @@ _PG_init(void) { RegisterXactCallback(IvmXactCallback, NULL); RegisterSubXactCallback(IvmSubXactCallback, NULL); + + PrevObjectAccessHook = object_access_hook; + object_access_hook = PgIvmObjectAccessHook; } /* @@ -350,3 +362,37 @@ get_immv_def(PG_FUNCTION_ARGS) table_close(matviewRel, NoLock); PG_RETURN_TEXT_P(cstring_to_text(querystring)); } + +/* + * object_access_hook function for dropping an IMMV + */ +static void +PgIvmObjectAccessHook(ObjectAccessType access, Oid classId, + Oid objectId, int subId, void *arg) +{ + if (PrevObjectAccessHook) + PrevObjectAccessHook(access, classId, objectId, subId, arg); + + if (access == OAT_DROP && classId == RelationRelationId && !OidIsValid(subId)) + { + Relation pgIvmImmv = table_open(PgIvmImmvRelationId(), AccessShareLock); + SysScanDesc scan; + ScanKeyData key; + HeapTuple tup; + + ScanKeyInit(&key, + Anum_pg_ivm_immv_immvrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + scan = systable_beginscan(pgIvmImmv, PgIvmImmvPrimaryKeyIndexId(), + true, NULL, 1, &key); + + tup = systable_getnext(scan); + + if (HeapTupleIsValid(tup)) + CatalogTupleDelete(pgIvmImmv, &tup->t_self); + + systable_endscan(scan); + table_close(pgIvmImmv, NoLock); + } +} From 78b8ac2e68f3bc8061164d026f6a596d0e0177aa Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Fri, 30 Sep 2022 23:55:01 +0900 Subject: [PATCH 2/2] Add description of get_immv_def to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 3f327be..4324954 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,12 @@ refresh_immv(immv_name text, with_data bool) RETURNS bigint 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. +#### get_immv_def + +`get_immv_def` reconstructs the underlying SELECT command for an IMMV. (This is a decompiled reconstruction, not the original text of the command.) +``` +get_immv_def(immv regclass) RETURNS text +``` ### IMMV metadata catalog