mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-17 19:13:16 +08:00
Previously we used postgres dependency tracking to ensure consistent deletes between various continuous agg postgres objects (views and tables). This does not work with dump/restore and thus this PR removes that dependency tracking in favor of handling these deps ourselves in various drop hooks. This PR also adds logic for deleting rows in the continuous_agg metadata table when the view is dropped. It does not yet handle deleting associated threshold metadata, that's left for a future PR. Most of this logic is in the apache-licensed code and not in the TSL since we want people who downgraded from TSL to apache to still be able to drop continuous views.
301 lines
7.9 KiB
C
301 lines
7.9 KiB
C
/*
|
|
* This file and its contents are licensed under the Apache License 2.0.
|
|
* Please see the included NOTICE for copyright information and
|
|
* LICENSE-APACHE for a copy of the license.
|
|
*/
|
|
#include <postgres.h>
|
|
#include <commands/event_trigger.h>
|
|
#include <utils/builtins.h>
|
|
#include <executor/executor.h>
|
|
#include <access/htup_details.h>
|
|
#include <catalog/pg_type.h>
|
|
#include <catalog/pg_constraint.h>
|
|
#include <catalog/pg_class.h>
|
|
#include <catalog/pg_namespace.h>
|
|
#include <catalog/pg_trigger.h>
|
|
|
|
#include "event_trigger.h"
|
|
|
|
#define DDL_INFO_NATTS 9
|
|
#define DROPPED_OBJECTS_NATTS 12
|
|
|
|
/* Function manager info for the event "pg_event_trigger_ddl_commands", which is
|
|
* used to retrieve information on executed DDL commands in an event
|
|
* trigger. The function manager info is initialized on extension load. */
|
|
static FmgrInfo ddl_commands_fmgrinfo;
|
|
static FmgrInfo dropped_objects_fmgrinfo;
|
|
|
|
/*
|
|
* Get a list of executed DDL commands in an event trigger.
|
|
*
|
|
* This function calls the function pg_ts_event_trigger_ddl_commands(), which is
|
|
* part of the event trigger API, and retrieves the DDL commands executed in
|
|
* relation to the event trigger. It is only valid to call this function from
|
|
* within an event trigger.
|
|
*/
|
|
List *
|
|
ts_event_trigger_ddl_commands(void)
|
|
{
|
|
ReturnSetInfo rsinfo;
|
|
FunctionCallInfoData fcinfo;
|
|
TupleTableSlot *slot;
|
|
EState *estate = CreateExecutorState();
|
|
List *objects = NIL;
|
|
|
|
InitFunctionCallInfoData(fcinfo, &ddl_commands_fmgrinfo, 1, InvalidOid, NULL, NULL);
|
|
MemSet(&rsinfo, 0, sizeof(rsinfo));
|
|
rsinfo.type = T_ReturnSetInfo;
|
|
rsinfo.allowedModes = SFRM_Materialize;
|
|
rsinfo.econtext = CreateExprContext(estate);
|
|
fcinfo.resultinfo = (fmNodePtr) &rsinfo;
|
|
|
|
FunctionCallInvoke(&fcinfo);
|
|
|
|
slot = MakeSingleTupleTableSlot(rsinfo.setDesc);
|
|
|
|
while (tuplestore_gettupleslot(rsinfo.setResult, true, false, slot))
|
|
{
|
|
HeapTuple tuple = ExecFetchSlotTuple(slot);
|
|
CollectedCommand *cmd;
|
|
Datum values[DDL_INFO_NATTS];
|
|
bool nulls[DDL_INFO_NATTS];
|
|
|
|
heap_deform_tuple(tuple, rsinfo.setDesc, values, nulls);
|
|
|
|
if (rsinfo.setDesc->natts > 8 && !nulls[8])
|
|
{
|
|
cmd = (CollectedCommand *) DatumGetPointer(values[8]);
|
|
objects = lappend(objects, cmd);
|
|
}
|
|
}
|
|
|
|
FreeExprContext(rsinfo.econtext, false);
|
|
FreeExecutorState(estate);
|
|
ExecDropSingleTupleTableSlot(slot);
|
|
|
|
return objects;
|
|
}
|
|
|
|
/* Given a TEXT[] of addrnames return a list of heap allocated char *
|
|
*
|
|
* similar to textarray_to_strvaluelist */
|
|
static List *
|
|
extract_addrnames(ArrayType *arr)
|
|
{
|
|
Datum *elems;
|
|
bool *nulls;
|
|
int nelems;
|
|
List *list = NIL;
|
|
int i;
|
|
|
|
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, &nulls, &nelems);
|
|
|
|
for (i = 0; i < nelems; i++)
|
|
{
|
|
if (nulls[i])
|
|
elog(ERROR, "unexpected NULL in name list");
|
|
|
|
/* TextDatumGetCString heap allocates the string */
|
|
list = lappend(list, TextDatumGetCString(elems[i]));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
static EventTriggerDropTableConstraint *
|
|
make_event_trigger_drop_table_constraint(char *constraint_name, char *schema, char *table)
|
|
{
|
|
EventTriggerDropTableConstraint *obj = palloc(sizeof(EventTriggerDropTableConstraint));
|
|
|
|
*obj =
|
|
(EventTriggerDropTableConstraint){ .obj = { .type = EVENT_TRIGGER_DROP_TABLE_CONSTRAINT },
|
|
.constraint_name = constraint_name,
|
|
.schema = schema,
|
|
.table = table };
|
|
|
|
return obj;
|
|
}
|
|
|
|
static EventTriggerDropIndex *
|
|
make_event_trigger_drop_index(char *index_name, char *schema)
|
|
{
|
|
EventTriggerDropIndex *obj = palloc(sizeof(EventTriggerDropIndex));
|
|
|
|
*obj = (EventTriggerDropIndex){
|
|
.obj = { .type = EVENT_TRIGGER_DROP_INDEX },
|
|
.index_name = index_name,
|
|
.schema = schema,
|
|
};
|
|
return obj;
|
|
}
|
|
|
|
static EventTriggerDropTable *
|
|
make_event_trigger_drop_table(char *table_name, char *schema)
|
|
{
|
|
EventTriggerDropTable *obj = palloc(sizeof(EventTriggerDropTable));
|
|
|
|
*obj = (EventTriggerDropTable){
|
|
.obj = { .type = EVENT_TRIGGER_DROP_TABLE },
|
|
.table_name = table_name,
|
|
.schema = schema,
|
|
};
|
|
return obj;
|
|
}
|
|
|
|
static EventTriggerDropView *
|
|
make_event_trigger_drop_view(char *view_name, char *schema)
|
|
{
|
|
EventTriggerDropView *obj = palloc(sizeof(*obj));
|
|
|
|
*obj = (EventTriggerDropView){
|
|
.obj = { .type = EVENT_TRIGGER_DROP_VIEW },
|
|
.view_name = view_name,
|
|
.schema = schema,
|
|
};
|
|
return obj;
|
|
}
|
|
|
|
static EventTriggerDropSchema *
|
|
make_event_trigger_drop_schema(char *schema)
|
|
{
|
|
EventTriggerDropSchema *obj = palloc(sizeof(EventTriggerDropSchema));
|
|
|
|
*obj = (EventTriggerDropSchema){
|
|
.obj = { .type = EVENT_TRIGGER_DROP_SCHEMA },
|
|
.schema = schema,
|
|
};
|
|
return obj;
|
|
}
|
|
|
|
static EventTriggerDropTrigger *
|
|
make_event_trigger_drop_trigger(char *trigger_name, char *schema, char *table)
|
|
{
|
|
EventTriggerDropTrigger *obj = palloc(sizeof(EventTriggerDropTrigger));
|
|
|
|
*obj = (EventTriggerDropTrigger){ .obj = { .type = EVENT_TRIGGER_DROP_TRIGGER },
|
|
.trigger_name = trigger_name,
|
|
.schema = schema,
|
|
.table = table };
|
|
|
|
return obj;
|
|
}
|
|
|
|
List *
|
|
ts_event_trigger_dropped_objects(void)
|
|
{
|
|
ReturnSetInfo rsinfo;
|
|
FunctionCallInfoData fcinfo;
|
|
TupleTableSlot *slot;
|
|
EState *estate = CreateExecutorState();
|
|
List *objects = NIL;
|
|
|
|
InitFunctionCallInfoData(fcinfo, &dropped_objects_fmgrinfo, 0, InvalidOid, NULL, NULL);
|
|
MemSet(&rsinfo, 0, sizeof(rsinfo));
|
|
rsinfo.type = T_ReturnSetInfo;
|
|
rsinfo.allowedModes = SFRM_Materialize;
|
|
rsinfo.econtext = CreateExprContext(estate);
|
|
fcinfo.resultinfo = (fmNodePtr) &rsinfo;
|
|
|
|
FunctionCallInvoke(&fcinfo);
|
|
|
|
slot = MakeSingleTupleTableSlot(rsinfo.setDesc);
|
|
|
|
while (tuplestore_gettupleslot(rsinfo.setResult, true, false, slot))
|
|
{
|
|
HeapTuple tuple = ExecFetchSlotTuple(slot);
|
|
Datum values[DROPPED_OBJECTS_NATTS];
|
|
bool nulls[DROPPED_OBJECTS_NATTS];
|
|
Oid class_id;
|
|
char *objtype;
|
|
|
|
heap_deform_tuple(tuple, rsinfo.setDesc, values, nulls);
|
|
|
|
class_id = DatumGetObjectId(values[0]);
|
|
|
|
switch (class_id)
|
|
{
|
|
case ConstraintRelationId:
|
|
objtype = TextDatumGetCString(values[6]);
|
|
if (objtype != NULL && strcmp(objtype, "table constraint") == 0)
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects,
|
|
make_event_trigger_drop_table_constraint(lthird(addrnames),
|
|
linitial(addrnames),
|
|
lsecond(addrnames)));
|
|
}
|
|
break;
|
|
case RelationRelationId:
|
|
objtype = TextDatumGetCString(values[6]);
|
|
if (objtype == NULL)
|
|
break;
|
|
if (strcmp(objtype, "index") == 0)
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects,
|
|
make_event_trigger_drop_index(lsecond(addrnames),
|
|
linitial(addrnames)));
|
|
}
|
|
else if (strcmp(objtype, "table") == 0)
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects,
|
|
make_event_trigger_drop_table(lsecond(addrnames),
|
|
linitial(addrnames)));
|
|
}
|
|
else if (strcmp(objtype, "view") == 0)
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects,
|
|
make_event_trigger_drop_view(lsecond(addrnames),
|
|
linitial(addrnames)));
|
|
}
|
|
break;
|
|
case NamespaceRelationId:
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects, make_event_trigger_drop_schema(linitial(addrnames)));
|
|
}
|
|
break;
|
|
case TriggerRelationId:
|
|
{
|
|
List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10]));
|
|
|
|
objects = lappend(objects,
|
|
make_event_trigger_drop_trigger(lthird(addrnames),
|
|
linitial(addrnames),
|
|
lsecond(addrnames)));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreeExprContext(rsinfo.econtext, false);
|
|
FreeExecutorState(estate);
|
|
ExecDropSingleTupleTableSlot(slot);
|
|
|
|
return objects;
|
|
}
|
|
|
|
void
|
|
_event_trigger_init(void)
|
|
{
|
|
fmgr_info(fmgr_internal_function("pg_event_trigger_ddl_commands"), &ddl_commands_fmgrinfo);
|
|
fmgr_info(fmgr_internal_function("pg_event_trigger_dropped_objects"),
|
|
&dropped_objects_fmgrinfo);
|
|
}
|
|
|
|
void
|
|
_event_trigger_fini(void)
|
|
{
|
|
/* Nothing to do */
|
|
}
|