pg_ivm/pg_ivm.c

206 lines
4.9 KiB
C
Raw Normal View History

2022-03-31 12:48:53 +00:00
/*-------------------------------------------------------------------------
*
* pg_ivm.c
* incremental view maintenance extension
* Routines for user interfaces and callback functions
2022-03-31 12:48:53 +00:00
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 2022, IVM Development Group
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xact.h"
2022-03-31 12:48:53 +00:00
#include "catalog/dependency.h"
#include "catalog/pg_namespace_d.h"
#include "catalog/pg_trigger_d.h"
#include "commands/trigger.h"
#include "parser/analyze.h"
#include "parser/parser.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "pg_ivm.h"
2022-03-31 12:48:53 +00:00
PG_MODULE_MAGIC;
static Oid pg_ivm_immv_id = InvalidOid;
static Oid pg_ivm_immv_pkey_id = InvalidOid;
2022-03-31 12:48:53 +00:00
void _PG_init(void);
static void IvmXactCallback(XactEvent event, void *arg);
static void IvmSubXactCallback(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg);
2022-03-31 12:48:53 +00:00
/* SQL callable functions */
PG_FUNCTION_INFO_V1(create_immv);
PG_FUNCTION_INFO_V1(IVM_prevent_immv_change);
2022-03-31 12:48:53 +00:00
/*
* Call back functions for cleaning up
2022-03-31 12:48:53 +00:00
*/
static void
IvmXactCallback(XactEvent event, void *arg)
2022-03-31 12:48:53 +00:00
{
if (event == XACT_EVENT_ABORT)
AtAbort_IVM();
}
2022-03-31 12:48:53 +00:00
static void
IvmSubXactCallback(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg)
{
if (event == SUBXACT_EVENT_ABORT_SUB)
AtAbort_IVM();
}
2022-03-31 12:48:53 +00:00
/*
* Module load callback
2022-03-31 12:48:53 +00:00
*/
void
_PG_init(void)
2022-03-31 12:48:53 +00:00
{
RegisterXactCallback(IvmXactCallback, NULL);
RegisterSubXactCallback(IvmSubXactCallback, NULL);
}
2022-03-31 12:48:53 +00:00
/*
* User inerface for creating an IMMV
*/
2022-03-31 12:48:53 +00:00
Datum
create_immv(PG_FUNCTION_ARGS)
{
text *t_relname = PG_GETARG_TEXT_PP(0);
text *t_sql = PG_GETARG_TEXT_PP(1);
char *relname = text_to_cstring(t_relname);
char *sql = text_to_cstring(t_sql);
List *parsetree_list;
RawStmt *parsetree;
QueryCompletion qc;
2022-03-31 12:48:53 +00:00
ParseState *pstate = make_parsestate(NULL);
Query *query;
CreateTableAsStmt *stmt;
StringInfoData command_buf;
initStringInfo(&command_buf);
appendStringInfo(&command_buf, "CREATE MATERIALIZED VIEW %s AS %s;", relname, sql);
parsetree_list = pg_parse_query(command_buf.data);
pstate->p_sourcetext = command_buf.data;
/* XXX: should we check t_sql before command_buf? */
if (list_length(parsetree_list) != 1)
elog(ERROR, "invalid view definition");
parsetree = linitial_node(RawStmt, parsetree_list);
query = transformStmt(pstate, parsetree->stmt);
Assert(query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt));
stmt = (CreateTableAsStmt*) query->utilityStmt;
query = castNode(Query, stmt->query);
ExecCreateImmv(pstate, stmt, NULL, NULL, &qc);
2022-03-31 12:48:53 +00:00
PG_RETURN_INT64(qc.nprocessed);
2022-03-31 12:48:53 +00:00
}
/*
* Trigger function to prevent IMMV from being changed
2022-03-31 12:48:53 +00:00
*/
Datum
IVM_prevent_immv_change(PG_FUNCTION_ARGS)
2022-03-31 12:48:53 +00:00
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
Relation rel = trigdata->tg_relation;
2022-03-31 12:48:53 +00:00
if (!ImmvIncrementalMaintenanceIsEnabled())
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change materialized view \"%s\"",
RelationGetRelationName(rel))));
2022-03-31 12:48:53 +00:00
return PointerGetDatum(NULL);
2022-03-31 12:48:53 +00:00
}
/*
* Create triggers to prevent IMMV from being changed
2022-03-31 12:48:53 +00:00
*/
void
CreateChangePreventTrigger(Oid matviewOid)
{
ObjectAddress refaddr;
ObjectAddress address;
CreateTrigStmt *ivm_trigger;
int16 types[4] = {TRIGGER_TYPE_INSERT, TRIGGER_TYPE_DELETE,
TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_TRUNCATE};
int i;
refaddr.classId = RelationRelationId;
refaddr.objectId = matviewOid;
refaddr.objectSubId = 0;
ivm_trigger = makeNode(CreateTrigStmt);
ivm_trigger->relation = NULL;
ivm_trigger->row = false;
ivm_trigger->timing = TRIGGER_TYPE_BEFORE;
ivm_trigger->trigname = "IVM_prevent_immv_change";
ivm_trigger->funcname = SystemFuncName("IVM_prevent_immv_change");
ivm_trigger->columns = NIL;
ivm_trigger->transitionRels = NIL;
ivm_trigger->whenClause = NULL;
ivm_trigger->isconstraint = false;
ivm_trigger->deferrable = false;
ivm_trigger->initdeferred = false;
ivm_trigger->constrrel = NULL;
ivm_trigger->args = NIL;
for (i = 0; i < 4; i++)
{
ivm_trigger->events = types[i];
address = CreateTrigger(ivm_trigger, NULL, matviewOid, InvalidOid, InvalidOid,
InvalidOid, InvalidOid, InvalidOid, NULL, true, false);
recordDependencyOn(&address, &refaddr, DEPENDENCY_AUTO);
}
/* Make changes-so-far visible */
CommandCounterIncrement();
}
/*
* Get relid of pg_ivm_immv
2022-03-31 12:48:53 +00:00
*/
Oid
PgIvmImmvRelationId(void)
2022-03-31 12:48:53 +00:00
{
if (!OidIsValid(pg_ivm_immv_id))
pg_ivm_immv_id = get_relname_relid("pg_ivm_immv", PG_CATALOG_NAMESPACE);
2022-03-31 12:48:53 +00:00
return pg_ivm_immv_id;
2022-03-31 12:48:53 +00:00
}
/*
* Get relid of pg_ivm_immv's primary key
2022-03-31 12:48:53 +00:00
*/
Oid
PgIvmImmvPrimaryKeyIndexId(void)
2022-03-31 12:48:53 +00:00
{
if (!OidIsValid(pg_ivm_immv_pkey_id))
pg_ivm_immv_pkey_id = get_relname_relid("pg_ivm_immv_pkey", PG_CATALOG_NAMESPACE);
2022-03-31 12:48:53 +00:00
return pg_ivm_immv_pkey_id;
}
2022-03-31 12:48:53 +00:00