1 /*-------------------------------------------------------------------------
4 * routines for defining a rewrite rule
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.40 1999/11/18 13:56:27 wieck Exp $
12 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "utils/builtins.h"
19 #include "catalog/pg_rewrite.h"
20 #include "lib/stringinfo.h"
21 #include "parser/parse_relation.h"
22 #include "rewrite/rewriteDefine.h"
23 #include "rewrite/rewriteSupport.h"
24 #include "tcop/tcopprot.h"
26 Oid LastOidProcessed = InvalidOid;
30 * Convert given string to a suitably quoted string constant,
31 * and append it to the StringInfo buffer.
32 * XXX Any MULTIBYTE considerations here?
35 quoteString(StringInfo buf, char *source)
39 appendStringInfoChar(buf, '\'');
40 for (current = source; *current; current++)
43 if (ch == '\'' || ch == '\\')
45 appendStringInfoChar(buf, '\\');
46 appendStringInfoChar(buf, ch);
48 else if (ch >= 0 && ch < ' ')
49 appendStringInfo(buf, "\\%03o", (int) ch);
51 appendStringInfoChar(buf, ch);
53 appendStringInfoChar(buf, '\'');
58 * takes the arguments and inserts them as attributes into the system
59 * relation "pg_rewrite"
61 * MODS : changes the value of LastOidProcessed as a side
62 * effect of inserting the rule tuple
64 * ARGS : rulname - name of the rule
65 * evtype - one of RETRIEVE,REPLACE,DELETE,APPEND
66 * evobj - name of relation
67 * evslot - comma delimited list of slots
68 * if null => multi-attr rule
69 * evinstead - is an instead rule
70 * actiontree - parsetree(s) of rule action
73 InsertRule(char *rulname,
81 StringInfoData rulebuf;
84 AttrNumber evslot_index;
85 char *is_instead = "f";
87 eventrel = heap_openr(evobj, AccessShareLock);
88 eventrel_oid = RelationGetRelid(eventrel);
91 * if the slotname is null, we know that this is a multi-attr rule
96 evslot_index = attnameAttNum(eventrel, evslot);
97 heap_close(eventrel, AccessShareLock);
105 if (IsDefinedRewriteRule(rulname))
106 elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
109 initStringInfo(&rulebuf);
110 appendStringInfo(&rulebuf,
111 "INSERT INTO pg_rewrite (rulename, ev_type, ev_class, ev_attr, ev_action, ev_qual, is_instead) VALUES (");
112 quoteString(&rulebuf, rulname);
113 appendStringInfo(&rulebuf, ", %d::char, %u::oid, %d::int2, ",
114 evtype, eventrel_oid, evslot_index);
115 quoteString(&rulebuf, actiontree);
116 appendStringInfo(&rulebuf, "::text, ");
117 quoteString(&rulebuf, evqual);
118 appendStringInfo(&rulebuf, "::text, '%s'::bool);",
121 pg_exec_query_dest(NameStr(rulebuf), None, true);
123 pfree(NameStr(rulebuf));
125 return LastOidProcessed;
129 * for now, event_object must be a single attribute
132 ValidateRule(int event_type,
140 if (((event_type == CMD_INSERT) || (event_type == CMD_DELETE)) &&
144 "rules not allowed for insert or delete events to an attribute");
150 * on retrieve to class.attribute do instead nothing is converted to
151 * 'on retrieve to class.attribute do instead retrieve (attribute =
152 * NULL)' --- this is also a terrible hack that works well -- glass
154 if (is_instead && !*action && eslot_string && event_type == CMD_SELECT)
156 char *temp_buffer = (char *) palloc(strlen(template) + 80);
158 sprintf(temp_buffer, template, event_attype,
159 get_typlen(event_attype), eslot_string,
162 *action = (List *) stringToNode(temp_buffer);
170 DefineQueryRewrite(RuleStmt *stmt)
172 CmdType event_type = stmt->event;
173 Attr *event_obj = stmt->object;
174 Node *event_qual = stmt->whereClause;
175 bool is_instead = stmt->instead;
176 List *action = stmt->actions;
177 Relation event_relation = NULL;
180 char *eslot_string = NULL;
182 Oid event_attype = 0;
189 * The current rewrite handler is known to work on relation level
190 * rules only. And for SELECT events, it expects one non-nothing
191 * action that is instead and returns exactly a tuple of the
192 * rewritten relation. This restricts SELECT rules to views.
197 if (event_obj->attrs)
198 elog(ERROR, "attribute level rules currently not supported");
201 * eslot_string = strVal(lfirst(event_obj->attrs));
207 * No rule actions that modify OLD or NEW
212 query = (Query *) lfirst(l);
213 if (query->resultRelation == 1)
215 elog(NOTICE, "rule actions on OLD currently not supported");
216 elog(ERROR, " use views or triggers instead");
218 if (query->resultRelation == 2)
220 elog(NOTICE, "rule actions on NEW currently not supported");
221 elog(ERROR, " use triggers instead");
226 * Rules ON SELECT are restricted to view definitions
228 if (event_type == CMD_SELECT)
232 Form_pg_attribute attr;
235 char expected_name[NAMEDATALEN + 5];
238 * So there cannot be INSTEAD NOTHING, ...
240 if (length(action) == 0)
242 elog(NOTICE, "instead nothing rules on select currently not supported");
243 elog(ERROR, " use views instead");
247 * ... there cannot be multiple actions, ...
249 if (length(action) > 1)
250 elog(ERROR, "multiple action rules on select currently not supported");
253 * ... the one action must be a SELECT, ...
255 query = (Query *) lfirst(action);
256 if (!is_instead || query->commandType != CMD_SELECT)
257 elog(ERROR, "only instead-select rules currently supported on select");
258 if (event_qual != NULL)
259 elog(ERROR, "event qualifications not supported for rules on select");
262 * ... the targetlist of the SELECT action must exactly match the
263 * event relation, ...
265 event_relation = heap_openr(event_obj->relname, AccessShareLock);
267 if (event_relation->rd_att->natts != length(query->targetList))
268 elog(ERROR, "select rules target list must match event relations structure");
270 for (i = 1; i <= event_relation->rd_att->natts; i++)
272 tle = (TargetEntry *) nth(i - 1, query->targetList);
273 resdom = tle->resdom;
274 attr = event_relation->rd_att->attrs[i - 1];
275 attname = pstrdup(NameStr(attr->attname));
277 if (strcmp(resdom->resname, attname) != 0)
278 elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
280 if (attr->atttypid != resdom->restype)
281 elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
283 if (attr->atttypmod != resdom->restypmod)
284 elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
288 * ... there must not be another ON SELECT rule already ...
290 if (event_relation->rd_rules != NULL)
292 for (i = 0; i < event_relation->rd_rules->numLocks; i++)
296 rule = event_relation->rd_rules->rules[i];
297 if (rule->event == CMD_SELECT)
298 elog(ERROR, "%s is already a view",
299 RelationGetRelationName(event_relation));
303 heap_close(event_relation, AccessShareLock);
306 * LIMIT in view is not supported
308 if (query->limitOffset != NULL || query->limitCount != NULL)
309 elog(ERROR, "LIMIT clause not supported in views");
312 * DISTINCT on view is not supported
314 if (query->uniqueFlag != NULL)
315 elog(ERROR, "DISTINCT not supported in views");
318 * ORDER BY in view is not supported
320 if (query->sortClause != NIL)
321 elog(ERROR, "ORDER BY not supported in views");
324 * ... and finally the rule must be named _RETviewname.
326 sprintf(expected_name, "_RET%s", event_obj->relname);
327 if (strcmp(expected_name, stmt->rulename) != 0)
329 elog(ERROR, "view rule for %s must be named %s",
330 event_obj->relname, expected_name);
335 * This rule is allowed - install it.
338 event_relation = heap_openr(event_obj->relname, AccessShareLock);
339 ev_relid = RelationGetRelid(event_relation);
341 if (eslot_string == NULL)
344 event_attype = -1; /* XXX - don't care */
348 event_attno = attnameAttNum(event_relation, eslot_string);
349 event_attype = attnumTypeId(event_relation, event_attno);
351 heap_close(event_relation, AccessShareLock);
353 /* fix bug about instead nothing */
354 ValidateRule(event_type, event_obj->relname,
355 eslot_string, event_qual, &action,
356 is_instead, event_attype);
361 return; /* doesn't do anything */
363 event_qualP = nodeToString(event_qual);
365 ruleId = InsertRule(stmt->rulename,
372 prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, TRUE,
378 event_qualP = nodeToString(event_qual);
379 actionP = nodeToString(action);
381 ruleId = InsertRule(stmt->rulename,
389 /* what is the max size of type text? XXX -- glass */
390 if (length(action) > 15)
391 elog(ERROR, "max # of actions exceeded");
392 prs2_addToRelation(ev_relid, ruleId, event_type, event_attno,
393 is_instead, event_qual, action);