OSDN Git Service

Change error messages to oids come out as %u and not %d. Change has no
[pg-rex/syncrep.git] / src / backend / rewrite / rewriteDefine.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteDefine.c
4  *        routines for defining a rewrite rule
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.27 1999/05/10 00:45:30 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16
17 #include "postgres.h"
18
19 #include "access/heapam.h"              /* access methods like amopenr */
20 #include "nodes/parsenodes.h"
21 #include "nodes/pg_list.h"              /* for Lisp support */
22 #include "parser/parse_relation.h"
23 #include "rewrite/locks.h"
24 #include "rewrite/rewriteDefine.h"
25 #include "rewrite/rewriteRemove.h"
26 #include "rewrite/rewriteSupport.h"
27 #include "tcop/tcopprot.h"
28 #include "utils/builtins.h"
29 #include "utils/lsyscache.h"    /* for get_typlen */
30 #include "utils/rel.h"                  /* for Relation stuff */
31
32
33 Oid                     LastOidProcessed = InvalidOid;
34
35 /*
36  * This is too small for many rule plans, but it'll have to do for now.
37  * Rule plans, etc will eventually have to be large objects.
38  *
39  * should this be smaller?
40  */
41 #define RULE_PLAN_SIZE BLCKSZ
42
43 static void
44 strcpyq(char *dest, char *source)
45 {
46         char       *current = source,
47                            *destp = dest;
48
49         for (current = source; *current; current++)
50         {
51                 if (*current == '\"')
52                 {
53                         *destp = '\\';
54                         destp++;
55                 }
56                 *destp = *current;
57                 destp++;
58         }
59         *destp = '\0';
60 }
61
62 /*
63  * InsertRule -
64  *        takes the arguments and inserts them as attributes into the system
65  *        relation "pg_rewrite"
66  *
67  *              MODS :  changes the value of LastOidProcessed as a side
68  *                              effect of inserting the rule tuple
69  *
70  *              ARGS :  rulname                 -               name of the rule
71  *                              evtype                  -               one of RETRIEVE,REPLACE,DELETE,APPEND
72  *                              evobj                   -               name of relation
73  *                              evslot                  -               comma delimited list of slots
74  *                                                                              if null => multi-attr rule
75  *                              evinstead               -               is an instead rule
76  *                              actiontree              -               parsetree(s) of rule action
77  */
78 static Oid
79 InsertRule(char *rulname,
80                    int evtype,
81                    char *evobj,
82                    char *evslot,
83                    char *evqual,
84                    bool evinstead,
85                    char *actiontree)
86 {
87         static char rulebuf[RULE_PLAN_SIZE];
88         static char actionbuf[RULE_PLAN_SIZE];
89         static char qualbuf[RULE_PLAN_SIZE];
90         Oid                     eventrel_oid = InvalidOid;
91         AttrNumber      evslot_index = InvalidAttrNumber;
92         Relation        eventrel = NULL;
93         char       *is_instead = "f";
94         extern void eval_as_new_xact();
95         char       *template;
96
97         eventrel = heap_openr(evobj);
98         if (eventrel == NULL)
99                 elog(ERROR, "rules cannot be defined on relations not in schema");
100         eventrel_oid = RelationGetRelid(eventrel);
101
102         /*
103          * if the slotname is null, we know that this is a multi-attr rule
104          */
105         if (evslot == NULL)
106                 evslot_index = -1;
107         else
108                 evslot_index = attnameAttNum(eventrel, (char *) evslot);
109         heap_close(eventrel);
110
111         if (evinstead)
112                 is_instead = "t";
113
114         if (evqual == NULL)
115                 evqual = "<>";
116
117         if (IsDefinedRewriteRule(rulname))
118                 elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
119                          rulname);
120         strcpyq(actionbuf, actiontree);
121         strcpyq(qualbuf, evqual);
122
123         template = "INSERT INTO pg_rewrite \
124 (rulename, ev_type, ev_class, ev_attr, ev_action, ev_qual, is_instead) VALUES \
125 ('%s', %d::char, %u::oid, %d::int2, '%s'::text, '%s'::text, \
126  '%s'::bool);";
127         if (strlen(template) + strlen(rulname) + strlen(actionbuf) +
128                 strlen(qualbuf) + 20 /* fudge fac */ > RULE_PLAN_SIZE)
129                 elog(ERROR, "DefineQueryRewrite: rule plan string too big.");
130         sprintf(rulebuf, template,
131                         rulname, evtype, eventrel_oid, evslot_index, actionbuf,
132                         qualbuf, is_instead);
133
134         pg_exec_query_acl_override(rulebuf);
135
136         return LastOidProcessed;
137 }
138
139 /*
140  *              for now, event_object must be a single attribute
141  */
142 static void
143 ValidateRule(int event_type,
144                          char *eobj_string,
145                          char *eslot_string,
146                          Node *event_qual,
147                          List **action,
148                          int is_instead,
149                          Oid event_attype)
150 {
151         if (((event_type == CMD_INSERT) || (event_type == CMD_DELETE)) &&
152                 eslot_string)
153         {
154                 elog(ERROR,
155                 "rules not allowed for insert or delete events to an attribute");
156         }
157
158 #ifdef NOT_USED
159
160         /*
161          * on retrieve to class.attribute do instead nothing is converted to
162          * 'on retrieve to class.attribute do instead retrieve (attribute =
163          * NULL)' --- this is also a terrible hack that works well -- glass
164          */
165         if (is_instead && !*action && eslot_string && event_type == CMD_SELECT)
166         {
167                 char       *temp_buffer = (char *) palloc(strlen(template) + 80);
168
169                 sprintf(temp_buffer, template, event_attype,
170                                 get_typlen(event_attype), eslot_string,
171                                 event_attype);
172
173                 *action = (List *) stringToNode(temp_buffer);
174
175                 pfree(temp_buffer);
176         }
177 #endif
178 }
179
180 void
181 DefineQueryRewrite(RuleStmt *stmt)
182 {
183         CmdType         event_type = stmt->event;
184         Attr       *event_obj = stmt->object;
185         Node       *event_qual = stmt->whereClause;
186         bool            is_instead = stmt->instead;
187         List       *action = stmt->actions;
188         Relation        event_relation = NULL;
189         Oid                     ruleId;
190         Oid                     ev_relid = 0;
191         char       *eslot_string = NULL;
192         int                     event_attno = 0;
193         Oid                     event_attype = 0;
194         char       *actionP,
195                            *event_qualP;
196         List       *l;
197         Query      *query;
198
199         /* ----------
200          * The current rewrite handler is known to work on relation level
201          * rules only. And for SELECT events, it expects one non-nothing
202          * action that is instead and returns exactly a tuple of the
203          * rewritten relation. This restricts SELECT rules to views.
204          *
205          *         Jan
206          * ----------
207          */
208         if (event_obj->attrs)
209                 elog(ERROR, "attribute level rules currently not supported");
210
211         /*
212          * eslot_string = strVal(lfirst(event_obj->attrs));
213          */
214         else
215                 eslot_string = NULL;
216
217         /*
218          * No rule actions that modify OLD or NEW
219          */
220         if (action != NIL)
221                 foreach(l, action)
222         {
223                 query = (Query *) lfirst(l);
224                 if (query->resultRelation == 1)
225                 {
226                         elog(NOTICE, "rule actions on OLD currently not supported");
227                         elog(ERROR, " use views or triggers instead");
228                 }
229                 if (query->resultRelation == 2)
230                 {
231                         elog(NOTICE, "rule actions on NEW currently not supported");
232                         elog(ERROR, " use triggers instead");
233                 }
234         }
235
236         /*
237          * Rules ON SELECT are restricted to view definitions
238          */
239         if (event_type == CMD_SELECT)
240         {
241                 TargetEntry             *tle;
242                 Resdom                  *resdom;
243                 Form_pg_attribute       attr;
244                 char                    *attname;
245                 int                     i;
246                 char                    expected_name[NAMEDATALEN + 5];
247
248                 /*
249                  * So there cannot be INSTEAD NOTHING, ...
250                  */
251                 if (length(action) == 0)
252                 {
253                         elog(NOTICE, "instead nothing rules on select currently not supported");
254                         elog(ERROR, " use views instead");
255                 }
256
257                 /*
258                  * ... there cannot be multiple actions, ...
259                  */
260                 if (length(action) > 1)
261                         elog(ERROR, "multiple action rules on select currently not supported");
262                 /*
263                  * ... the one action must be a SELECT, ...
264                  */
265                 query = (Query *) lfirst(action);
266                 if (!is_instead || query->commandType != CMD_SELECT)
267                         elog(ERROR, "only instead-select rules currently supported on select");
268                 if (event_qual != NULL)
269                         elog(ERROR, "event qualifications not supported for rules on select");
270
271                 /*
272                  * ... the targetlist of the SELECT action must
273                  * exactly match the event relation, ...
274                  */
275                 event_relation = heap_openr(event_obj->relname);
276                 if (event_relation == NULL)
277                         elog(ERROR, "virtual relations not supported yet");
278
279                 if (event_relation->rd_att->natts != length(query->targetList))
280                         elog(ERROR, "select rules target list must match event relations structure");
281
282                 for (i = 1; i <= event_relation->rd_att->natts; i++) {
283                         tle = (TargetEntry *)nth(i - 1, query->targetList);
284                         resdom = tle->resdom;
285                         attr = event_relation->rd_att->attrs[i - 1];
286                         attname = nameout(&(attr->attname));
287
288                         if (strcmp(resdom->resname, attname) != 0)
289                                 elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
290
291                         if (attr->atttypid != resdom->restype)
292                                 elog(ERROR, "select rules target entry %d has different type from attribute %s", i,  attname);
293
294                         if (attr->atttypmod != resdom->restypmod)
295                                 elog(ERROR, "select rules target entry %d has different size from attribute %s", i,  attname);
296                 }
297
298                 /*
299                  * ... there must not be another ON SELECT
300                  * rule already ...
301                  */
302                 if (event_relation->rd_rules != NULL) {
303                         for (i = 0; i < event_relation->rd_rules->numLocks; i++) {
304                                 RewriteRule     *rule;
305
306                                 rule = event_relation->rd_rules->rules[i];
307                                 if (rule->event == CMD_SELECT)
308                                         elog(ERROR, "%s is already a view", nameout(&(event_relation->rd_rel->relname)));
309                         }
310                 }
311
312                 heap_close(event_relation);
313
314                 /*
315                  * LIMIT in view is not supported
316                  */
317                 if (query->limitOffset != NULL || query->limitCount != NULL)
318                         elog(ERROR, "LIMIT clause not supported in views");
319
320                 /*
321                  * ... and finally the rule must be named _RETviewname.
322                  */
323                 sprintf(expected_name, "_RET%s", event_obj->relname);
324                 if (strcmp(expected_name, stmt->rulename) != 0) {
325                         elog(ERROR, "view rule for %s must be named %s",
326                                 event_obj->relname, expected_name);
327                 }
328         }
329
330         /*
331          * This rule is allowed - install it.
332          */
333
334         event_relation = heap_openr(event_obj->relname);
335         if (event_relation == NULL)
336                 elog(ERROR, "virtual relations not supported yet");
337         ev_relid = RelationGetRelid(event_relation);
338
339         if (eslot_string == NULL)
340         {
341                 event_attno = -1;
342                 event_attype = -1;              /* XXX - don't care */
343         }
344         else
345         {
346                 event_attno = attnameAttNum(event_relation, eslot_string);
347                 event_attype = attnumTypeId(event_relation, event_attno);
348         }
349         heap_close(event_relation);
350
351         /* fix bug about instead nothing */
352         ValidateRule(event_type, event_obj->relname,
353                                  eslot_string, event_qual, &action,
354                                  is_instead, event_attype);
355
356         if (action == NULL)
357         {
358                 if (!is_instead)
359                         return;                         /* doesn't do anything */
360
361                 event_qualP = nodeToString(event_qual);
362
363                 ruleId = InsertRule(stmt->rulename,
364                                                         event_type,
365                                                         event_obj->relname,
366                                                         eslot_string,
367                                                         event_qualP,
368                                                         true,
369                                                         "<>");
370                 prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, TRUE,
371                                                    event_qual, NIL);
372
373         }
374         else
375         {
376                 event_qualP = nodeToString(event_qual);
377                 actionP = nodeToString(action);
378
379                 ruleId = InsertRule(stmt->rulename,
380                                                         event_type,
381                                                         event_obj->relname,
382                                                         eslot_string,
383                                                         event_qualP,
384                                                         is_instead,
385                                                         actionP);
386
387                 /* what is the max size of type text? XXX -- glass */
388                 if (length(action) > 15)
389                         elog(ERROR, "max # of actions exceeded");
390                 prs2_addToRelation(ev_relid, ruleId, event_type, event_attno,
391                                                    is_instead, event_qual, action);
392         }
393 }