-
Notifications
You must be signed in to change notification settings - Fork 6
/
init.c
196 lines (164 loc) · 4.52 KB
/
init.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
* ProcessUtility hook and associated functions to provide additional schema
* change events for the CREATE EVENT TRIGGER functionality.
*
*
* pg_schema_triggers/init.c
*/
#include "postgres.h"
#include "fmgr.h"
#include "access/hash.h"
#include "access/xact.h"
#include "catalog/objectaccess.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "parser/parse_func.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "events.h"
#include "hook_objacc.h"
#include "trigger_funcs.h"
/* PG_MODULE_MAGIC must appear exactly once in the entire module. */
PG_MODULE_MAGIC;
void _PG_init(void);
void _PG_fini(void);
static ProcessUtility_hook_type old_utility_hook = NULL;
static int stmt_createEventTrigger_before(CreateEventTrigStmt *stmt);
static void utility_hook(Node *parsetree,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag);
/*
* Install and uninstall the hook.
*
* We react poorly if we discover another module has also installed a
* ProcessUtility hook.
*/
void
_PG_init(void)
{
if (ProcessUtility_hook != NULL)
elog(FATAL, "a ProcessUtility hook is already installed.");
old_utility_hook = ProcessUtility_hook;
ProcessUtility_hook = utility_hook;
install_objacc_hook();
}
void
_PG_fini(void)
{
if (ProcessUtility_hook != utility_hook)
elog(FATAL, "hook conflict, our ProcessUtility hook has been removed.");
ProcessUtility_hook = old_utility_hook;
remove_objacc_hook();
}
/*
* The meat of the hook.
*/
static void
utility_hook(Node *parsetree,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
/* Intercept the CREATE EVENT TRIGGER command. */
if (nodeTag(parsetree) == T_CreateEventTrigStmt)
{
CreateEventTrigStmt *stmt = (CreateEventTrigStmt *) parsetree;
int suppress;
suppress = stmt_createEventTrigger_before(stmt);
if (suppress)
return;
}
/* Pass all other commands through to the default implementation. */
if (context != PROCESS_UTILITY_SUBCOMMAND)
StartNewEvent();
PG_TRY();
{
standard_ProcessUtility(parsetree, queryString, context, params, dest, completionTag);
}
PG_CATCH();
{
if (context != PROCESS_UTILITY_SUBCOMMAND)
EndEvent();
PG_RE_THROW();
}
PG_END_TRY();
if (context != PROCESS_UTILITY_SUBCOMMAND)
EndEvent();
}
/*
* List of events that we recognize, along with the number and types of
* arguments that are passed to the event trigger function.
*/
struct event {
char *eventname;
};
struct event supported_events[] = {
{"column_add"},
{"column_alter"},
{"column_drop"},
{"relation_create"},
{"relation_alter"},
{"relation_drop"},
{"trigger_create"},
{"trigger_adjust"},
{"trigger_rename"},
{"trigger_drop"},
/* end of list marker */
{NULL}
};
/*
* Intercept CREATE EVENT TRIGGER statements with event names that we
* recognize, and pass them to our own CreateEventTriggerEx() function.
* Pass everything else through to ProcessUtility_standard otherwise.
*/
static int
stmt_createEventTrigger_before(CreateEventTrigStmt *stmt)
{
Oid funcoid;
int recognized = 0;
struct event *evt;
/*
* If we recognize the event name, look up the corresponding function.
* Otherwise, fall through to the normal CreateEventTrigger() code.
*/
for (evt = supported_events; evt->eventname != NULL; evt++)
{
if (strcmp(stmt->eventname, evt->eventname) != 0)
continue;
/*
* LookupFuncName() will raise an error if it can't find a
* function with the appropriate signature.
*/
funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
recognized = 1;
break;
}
if (!recognized)
{
elog(INFO, "pg_schema_triggers: didn't recognize event name, ignoring.");
return 0;
}
/* Check the trigger function's return type. */
if (get_func_rettype(funcoid) != EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function \"%s\" must return type \"%s\"",
get_func_name(funcoid), format_type_be(EVTTRIGGEROID))));
/* None of our events support the WHEN clause, so ensure that it is empty. */
if (stmt->whenclause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("event \"%s\" cannot have a WHEN clause",
stmt->eventname)));
/* Create the event trigger. */
CreateEventTriggerEx(stmt->eventname, stmt->trigname, funcoid);
/* And skip the call to CreateEventTrigger(). */
return 1;
}