Add UUID column to pg_ivm_immv.

This commit is contained in:
Adam Guo 2025-05-19 16:22:52 +00:00
parent 38777f260a
commit 5a31d7a9ad
5 changed files with 169 additions and 54 deletions

View file

@ -62,8 +62,8 @@ typedef struct
} DR_intorel;
/* utility functions for IMMV definition creation */
static ObjectAddress create_immv_internal(List *attrList, IntoClause *into);
static ObjectAddress create_immv_nodata(List *tlist, IntoClause *into);
static ImmvAddress create_immv_internal(List *attrList, IntoClause *into);
static ImmvAddress create_immv_nodata(List *tlist, IntoClause *into);
typedef struct
{
@ -74,15 +74,15 @@ typedef struct
int sublevels_up; /* (current) nesting depth */
} check_ivm_restriction_context;
static void CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
static void CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, ImmvAddress immv_addr,
Relids *relids, bool ex_lock);
static void CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock);
static void CreateIvmTrigger(Oid relOid, ImmvAddress immv_addr, int16 type, int16 timing, bool ex_lock);
static void check_ivm_restriction(Node *node);
static bool check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context);
static Bitmapset *get_primary_key_attnos_from_query(Query *query, List **constraintList);
static bool check_aggregate_supports_ivm(Oid aggfnoid);
static void StoreImmvQuery(Oid viewOid, Query *viewQuery);
static void StoreImmvQuery(ImmvAddress immv_addr, Query *viewQuery);
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM < 140000)
static bool CreateTableAsRelExists(CreateTableAsStmt *ctas);
@ -96,14 +96,15 @@ static bool CreateTableAsRelExists(CreateTableAsStmt *ctas);
*
* This imitates PostgreSQL's create_ctas_internal().
*/
static ObjectAddress
static ImmvAddress
create_immv_internal(List *attrList, IntoClause *into)
{
CreateStmt *create = makeNode(CreateStmt);
char relkind;
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
ImmvAddress immv_addr;
pg_uuid_t *immv_uuid;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
/* relkind of IMMV must be RELKIND_RELATION */
@ -128,7 +129,12 @@ create_immv_internal(List *attrList, IntoClause *into)
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
immv_addr.address = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
/* Generate the IMMV UUID. */
// TODO: check for hash collision
immv_uuid = DatumGetUUIDP(DirectFunctionCall1(gen_random_uuid, (Datum) NULL));
memcpy(&immv_addr.immv_uuid, immv_uuid, sizeof(*immv_uuid));
pfree(immv_uuid);
/*
* If necessary, create a TOAST table for the target table. Note that
@ -146,13 +152,13 @@ create_immv_internal(List *attrList, IntoClause *into)
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
NewRelationCreateToastTable(immv_addr.address.objectId, toast_options);
/* Create the "view" part of an IMMV. */
StoreImmvQuery(intoRelationAddr.objectId, (Query *) into->viewQuery);
StoreImmvQuery(immv_addr, (Query *) into->viewQuery);
CommandCounterIncrement();
return intoRelationAddr;
return immv_addr;
}
/*
@ -163,7 +169,7 @@ create_immv_internal(List *attrList, IntoClause *into)
*
* This imitates PostgreSQL's create_ctas_nodata().
*/
static ObjectAddress
static ImmvAddress
create_immv_nodata(List *tlist, IntoClause *into)
{
List *attrList;
@ -240,7 +246,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
bool do_refresh = false;
ObjectAddress address;
ImmvAddress immv_addr;
/* Check if the relation exists or not */
if (CreateTableAsRelExists(stmt))
@ -275,7 +281,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
* similar to CREATE VIEW. This avoids dump/restore problems stemming
* from running the planner before all dependencies are set up.
*/
address = create_immv_nodata(query->targetList, into);
immv_addr = create_immv_nodata(query->targetList, into);
/*
* For materialized views, reuse the REFRESH logic, which locks down
@ -286,18 +292,18 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
{
Relation matviewRel;
RefreshImmvByOid(address.objectId, true, false, pstate->p_sourcetext, qc);
RefreshImmvByOid(immv_addr, true, false, pstate->p_sourcetext, qc);
if (qc)
qc->commandTag = CMDTAG_SELECT;
matviewRel = table_open(address.objectId, NoLock);
matviewRel = table_open(immv_addr.address.objectId, NoLock);
/* Create an index on incremental maintainable materialized view, if possible */
CreateIndexOnIMMV(query, matviewRel);
/* Create triggers to prevent IMMV from being changed */
CreateChangePreventTrigger(address.objectId);
CreateChangePreventTrigger(immv_addr.address.objectId);
table_close(matviewRel, NoLock);
@ -309,7 +315,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
"or execute refresh_immv to make sure the view is consistent.")));
}
return address;
return immv_addr.address;
}
/*
@ -548,7 +554,7 @@ makeIvmAggColumn(ParseState *pstate, Aggref *aggref, char *resname, AttrNumber *
* CreateIvmTriggersOnBaseTables -- create IVM triggers on all base tables
*/
void
CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid)
CreateIvmTriggersOnBaseTables(Query *qry, ImmvAddress immv_addr)
{
Relids relids = NULL;
bool ex_lock = false;
@ -582,13 +588,13 @@ CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid)
qry->distinctClause || (qry->hasAggs && qry->groupClause))
ex_lock = true;
CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *)qry, matviewOid, &relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *)qry, immv_addr, &relids, ex_lock);
bms_free(relids);
}
static void
CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, ImmvAddress immv_addr,
Relids *relids, bool ex_lock)
{
if (node == NULL)
@ -604,12 +610,12 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
Query *query = (Query *) node;
ListCell *lc;
CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *) query->jointree, matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *) query->jointree, immv_addr, relids, ex_lock);
foreach(lc, query->cteList)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
Assert(IsA(cte->ctequery, Query));
CreateIvmTriggersOnBaseTablesRecurse((Query *) cte->ctequery, cte->ctequery, matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse((Query *) cte->ctequery, cte->ctequery, immv_addr, relids, ex_lock);
}
}
break;
@ -621,14 +627,14 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
if (rte->rtekind == RTE_RELATION && !bms_is_member(rte->relid, *relids))
{
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_BEFORE, true);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_AFTER, true);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_BEFORE, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_BEFORE, true);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_AFTER, ex_lock);
CreateIvmTrigger(rte->relid, immv_addr, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_AFTER, true);
*relids = bms_add_member(*relids, rte->relid);
}
@ -636,7 +642,7 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
{
Query *subquery = rte->subquery;
Assert(rte->subquery != NULL);
CreateIvmTriggersOnBaseTablesRecurse(subquery, (Node *) subquery, matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(subquery, (Node *) subquery, immv_addr, relids, ex_lock);
}
}
break;
@ -647,7 +653,7 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
ListCell *l;
foreach(l, f->fromlist)
CreateIvmTriggersOnBaseTablesRecurse(qry, lfirst(l), matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, lfirst(l), immv_addr, relids, ex_lock);
}
break;
@ -655,8 +661,8 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
{
JoinExpr *j = (JoinExpr *) node;
CreateIvmTriggersOnBaseTablesRecurse(qry, j->larg, matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, j->rarg, matviewOid, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, j->larg, immv_addr, relids, ex_lock);
CreateIvmTriggersOnBaseTablesRecurse(qry, j->rarg, immv_addr, relids, ex_lock);
}
break;
@ -669,7 +675,7 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
* CreateIvmTrigger -- create IVM trigger on a base table
*/
static void
CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock)
CreateIvmTrigger(Oid relOid, ImmvAddress immv_addr, int16 type, int16 timing, bool ex_lock)
{
ObjectAddress refaddr;
ObjectAddress address;
@ -679,7 +685,7 @@ CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock
Assert(timing == TRIGGER_TYPE_BEFORE || timing == TRIGGER_TYPE_AFTER);
refaddr.classId = RelationRelationId;
refaddr.objectId = viewOid;
refaddr.objectId = immv_addr.address.objectId;
refaddr.objectSubId = 0;
ivm_trigger = makeNode(CreateTrigStmt);
@ -753,7 +759,7 @@ CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock
ivm_trigger->initdeferred = false;
ivm_trigger->constrrel = NULL;
ivm_trigger->args = list_make2(
makeString(DatumGetPointer(DirectFunctionCall1(oidout, ObjectIdGetDatum(viewOid)))),
makeString(DatumGetPointer(UUIDPGetDatum(&immv_addr.immv_uuid))),
makeString(DatumGetPointer(DirectFunctionCall1(boolout, BoolGetDatum(ex_lock))))
);
@ -1708,7 +1714,7 @@ get_primary_key_attnos_from_query(Query *query, List **constraintList)
* Store the query for the IMMV to pg_ivm_immv
*/
static void
StoreImmvQuery(Oid viewOid, Query *viewQuery)
StoreImmvQuery(ImmvAddress immv_addr, Query *viewQuery)
{
char *querytree = nodeToString((Node *) viewQuery);
char *querystring;
@ -1736,10 +1742,11 @@ StoreImmvQuery(Oid viewOid, Query *viewQuery)
memset(values, 0, sizeof(values));
memset(isNulls, false, sizeof(isNulls));
values[Anum_pg_ivm_immv_immvrelid -1 ] = ObjectIdGetDatum(viewOid);
values[Anum_pg_ivm_immv_immvrelid -1 ] = ObjectIdGetDatum(immv_addr.address.objectId);
values[Anum_pg_ivm_immv_ispopulated -1 ] = BoolGetDatum(false);
values[Anum_pg_ivm_immv_viewdef -1 ] = CStringGetTextDatum(querytree);
values[Anum_pg_ivm_immv_querystring - 1] = CStringGetTextDatum(querystring);
values[Anum_pg_ivm_immv_immvuuid -1 ] = UUIDPGetDatum(&immv_addr.immv_uuid);
pgIvmImmv = table_open(PgIvmImmvRelationId(), RowExclusiveLock);
@ -1749,7 +1756,7 @@ StoreImmvQuery(Oid viewOid, Query *viewQuery)
CatalogTupleInsert(pgIvmImmv, heapTuple);
address.classId = RelationRelationId;
address.objectId = viewOid;
address.objectId = immv_addr.address.objectId;
address.objectSubId = 0;
recordDependencyOnExpr(&address, (Node *) viewQuery, NIL,

View file

@ -50,6 +50,7 @@
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
#include "utils/uuid.h"
#include "utils/xid8.h"
#include "pg_ivm.h"
@ -251,6 +252,7 @@ ExecRefreshImmv(const RangeVar *relation, bool skipData,
{
Oid matviewOid;
LOCKMODE lockmode;
ImmvAddress immv_addr;
/* Determine strength of lock needed. */
//concurrent = stmt->concurrent;
@ -272,7 +274,12 @@ ExecRefreshImmv(const RangeVar *relation, bool skipData,
NULL);
#endif
return RefreshImmvByOid(matviewOid, false, skipData, queryString, qc);
immv_addr.address.classId = RelationRelationId;
immv_addr.address.objectId = matviewOid;
immv_addr.address.objectSubId = 0;
// TODO: get uuid
return RefreshImmvByOid(immv_addr, false, skipData, queryString, qc);
}
/*
@ -283,7 +290,7 @@ ExecRefreshImmv(const RangeVar *relation, bool skipData,
* This imitates PostgreSQL's RefreshMatViewByOid().
*/
ObjectAddress
RefreshImmvByOid(Oid matviewOid, bool is_create, bool skipData,
RefreshImmvByOid(ImmvAddress immv_addr, bool is_create, bool skipData,
const char *queryString, QueryCompletion *qc)
{
Relation matviewRel;
@ -300,6 +307,7 @@ RefreshImmvByOid(Oid matviewOid, bool is_create, bool skipData,
int save_nestlevel;
ObjectAddress address;
bool oldPopulated;
Oid matviewOid = immv_addr.address.objectId;
Relation pgIvmImmv;
TupleDesc tupdesc;
@ -486,10 +494,7 @@ RefreshImmvByOid(Oid matviewOid, bool is_create, bool skipData,
* is created.
*/
if (!skipData && !oldPopulated)
{
CreateIvmTriggersOnBaseTables(dataQuery, matviewOid);
CreateChangePreventTrigger(matviewOid);
}
CreateIvmTriggersOnBaseTables(dataQuery, immv_addr);
/*
* Create the transient table that will receive the regenerated data. Lock
@ -844,15 +849,17 @@ Datum
IVM_immediate_before(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
char *matviewOid_text = trigdata->tg_trigger->tgargs[0];
char *immv_uuid_text = trigdata->tg_trigger->tgargs[0];
char *ex_lock_text = trigdata->tg_trigger->tgargs[1];
pg_uuid_t *immv_uuid;
Oid matviewOid;
MV_TriggerHashEntry *entry;
bool found;
bool ex_lock;
matviewOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(matviewOid_text)));
immv_uuid = DatumGetUUIDP(CStringGetDatum(immv_uuid_text));
ex_lock = DatumGetBool(DirectFunctionCall1(boolin, CStringGetDatum(ex_lock_text)));
matviewOid = GetImmvRelid(immv_uuid);
/* If the view has more than one tables, we have to use an exclusive lock. */
if (ex_lock)
@ -967,10 +974,11 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
Oid matviewOid;
Query *query;
Query *rewritten = NULL;
char *matviewOid_text = trigdata->tg_trigger->tgargs[0];
char *immv_uuid_text = trigdata->tg_trigger->tgargs[0];
Relation matviewRel;
int old_depth = immv_maintenance_depth;
SubTransactionId subxid;
pg_uuid_t *immv_uuid;
Oid relowner;
Tuplestorestate *old_tuplestore = NULL;
@ -998,7 +1006,8 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
rel = trigdata->tg_relation;
relid = rel->rd_id;
matviewOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(matviewOid_text)));
immv_uuid = DatumGetUUIDP(CStringGetDatum(immv_uuid_text));
matviewOid = GetImmvRelid(immv_uuid);
/*
* On the first call initialize the hashtable

View file

@ -1,4 +1,6 @@
ALTER TABLE pgivm.pg_ivm_immv ADD COLUMN querystring text NOT NULL;
ALTER TABLE pgivm.pg_ivm_immv ADD COLUMN immvuuid uuid NOT NULL;
ALTER TABLE pgivm.pg_ivm_immv ADD CONSTRAINT pg_ivm_immv_uuid UNIQUE (immvuuid);
CREATE FUNCTION pgivm.recreate_all_immvs() RETURNS VOID LANGUAGE PLPGSQL AS
$$

View file

@ -32,6 +32,7 @@
#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/rel.h"
#include "utils/uuid.h"
#include "utils/varlena.h"
#include "pg_ivm.h"
@ -365,6 +366,17 @@ PgIvmImmvPrimaryKeyIndexId(void)
AccessShareLock, true);
}
/*
* Get relid of pg_ivm_immv's UUID unique key.
*/
Oid
PgIvmImmvUuidIndexId(void)
{
return RangeVarGetRelid(
makeRangeVar("pgivm", "pg_ivm_immv_uuid", -1),
AccessShareLock, true);
}
/*
* Return the SELECT part of an IMMV
*/
@ -488,3 +500,77 @@ RestrictSearchPath(void)
PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false);
}
#endif
/*
* GetImmvOid
*
* Look up the immvrelid of an IMMV from its immv_uuid.
*/
Oid
GetImmvRelid(pg_uuid_t *immv_uuid)
{
Datum datum;
HeapTuple tup;
Relation pgIvmImmv = table_open(PgIvmImmvRelationId(), AccessShareLock);
ScanKeyData key;
SysScanDesc scan;
TupleDesc tupdesc = RelationGetDescr(pgIvmImmv);
bool isnull;
ScanKeyInit(&key, Anum_pg_ivm_immv_immvuuid, BTEqualStrategyNumber,
F_UUID_EQ, UUIDPGetDatum(immv_uuid));
scan = systable_beginscan(pgIvmImmv, PgIvmImmvUuidIndexId(), true, NULL, 1,
&key);
tup = systable_getnext(scan);
if (!HeapTupleIsValid(tup))
{
systable_endscan(scan);
table_close(pgIvmImmv, NoLock);
return InvalidOid;
}
datum = heap_getattr(tup, Anum_pg_ivm_immv_immvrelid, tupdesc, &isnull);
Assert(!isnull);
systable_endscan(scan);
table_close(pgIvmImmv, NoLock);
return DatumGetObjectId(datum);
}
/*
* GetImmvUuid
*
* Look up the immv_uuid of an IMMV from its immvrelid.
*/
pg_uuid_t *
GetImmvUuid(Oid immvrelid)
{
Datum datum;
HeapTuple tup;
Relation pgIvmImmv = table_open(PgIvmImmvRelationId(), AccessShareLock);
ScanKeyData key;
SysScanDesc scan;
TupleDesc tupdesc = RelationGetDescr(pgIvmImmv);
bool isnull;
ScanKeyInit(&key, Anum_pg_ivm_immv_immvrelid, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(immvrelid));
scan = systable_beginscan(pgIvmImmv, PgIvmImmvPrimaryKeyIndexId(),
true, NULL, 1, &key);
tup = systable_getnext(scan);
if (!HeapTupleIsValid(tup))
{
systable_endscan(scan);
table_close(pgIvmImmv, NoLock);
return NULL;
}
datum = heap_getattr(tup, Anum_pg_ivm_immv_immvuuid, tupdesc, &isnull);
Assert(!isnull);
systable_endscan(scan);
table_close(pgIvmImmv, NoLock);
return DatumGetUUIDP(datum);
}

View file

@ -19,30 +19,41 @@
#include "parser/parse_node.h"
#include "tcop/dest.h"
#include "utils/queryenvironment.h"
#include "utils/uuid.h"
#define Natts_pg_ivm_immv 5
#define Natts_pg_ivm_immv 6
#define Anum_pg_ivm_immv_immvrelid 1
#define Anum_pg_ivm_immv_viewdef 2
#define Anum_pg_ivm_immv_ispopulated 3
#define Anum_pg_ivm_immv_lastivmupdate 4
#define Anum_pg_ivm_immv_querystring 5
#define Anum_pg_ivm_immv_immvuuid 6
typedef struct ImmvAddress
{
ObjectAddress address;
pg_uuid_t immv_uuid;
} ImmvAddress;
/* pg_ivm.c */
extern void CreateChangePreventTrigger(Oid matviewOid);
extern Oid PgIvmImmvRelationId(void);
extern Oid PgIvmImmvPrimaryKeyIndexId(void);
extern Oid PgIvmImmvUuidIndexId(void);
extern bool isImmv(Oid immv_oid);
extern List *PgIvmFuncName(char *name);
extern void parse_immv_query(const char *relname, const char *sql,
Query **query_ret, ParseState **pstate_ret);
extern Oid GetImmvRelid(pg_uuid_t *immv_uuid);
extern pg_uuid_t *GetImmvUuid(Oid immvrelid);
/* createas.c */
extern ObjectAddress ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
QueryCompletion *qc);
extern void CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid);
extern void CreateIvmTriggersOnBaseTables(Query *qry, ImmvAddress immv_addr);
extern void CreateIndexOnIMMV(Query *query, Relation matviewRel);
extern Query *rewriteQueryForIMMV(Query *query, List *colNames);
extern void makeIvmAggColumn(ParseState *pstate, Aggref *aggref, char *resname, AttrNumber *next_resno, List **aggs);
@ -52,7 +63,7 @@ extern void makeIvmAggColumn(ParseState *pstate, Aggref *aggref, char *resname,
extern Query *get_immv_query(Relation matviewRel);
extern ObjectAddress ExecRefreshImmv(const RangeVar *relation, bool skipData,
const char *queryString, QueryCompletion *qc);
extern ObjectAddress RefreshImmvByOid(Oid matviewOid, bool is_create, bool skipData,
extern ObjectAddress RefreshImmvByOid(ImmvAddress immv_addr, bool is_create, bool skipData,
const char *queryString, QueryCompletion *qc);
extern bool ImmvIncrementalMaintenanceIsEnabled(void);
extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);