OSDN Git Service

679712196992301c08fa5747ba0fbeff145f6524
[pg-rex/syncrep.git] / src / backend / commands / view.c
1 /*-------------------------------------------------------------------------
2  *
3  * view.c
4  *        use rewrite rules to construct views
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.83 2004/06/18 06:13:23 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/namespace.h"
20 #include "commands/tablecmds.h"
21 #include "commands/view.h"
22 #include "miscadmin.h"
23 #include "nodes/makefuncs.h"
24 #include "parser/parse_relation.h"
25 #include "rewrite/rewriteDefine.h"
26 #include "rewrite/rewriteManip.h"
27 #include "rewrite/rewriteSupport.h"
28 #include "utils/acl.h"
29 #include "utils/lsyscache.h"
30
31
32 static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
33
34
35 /*---------------------------------------------------------------------
36  * DefineVirtualRelation
37  *
38  * Create the "view" relation. `DefineRelation' does all the work,
39  * we just provide the correct arguments ... at least when we're
40  * creating a view.  If we're updating an existing view, we have to
41  * work harder.
42  *---------------------------------------------------------------------
43  */
44 static Oid
45 DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
46 {
47         Oid                     viewOid,
48                                 namespaceId;
49         CreateStmt *createStmt = makeNode(CreateStmt);
50         List       *attrList;
51         ListCell   *t;
52
53         /*
54          * create a list of ColumnDef nodes based on the names and types of
55          * the (non-junk) targetlist items from the view's SELECT list.
56          */
57         attrList = NIL;
58         foreach(t, tlist)
59         {
60                 TargetEntry *entry = lfirst(t);
61                 Resdom     *res = entry->resdom;
62
63                 if (!res->resjunk)
64                 {
65                         ColumnDef  *def = makeNode(ColumnDef);
66                         TypeName   *typename = makeNode(TypeName);
67
68                         def->colname = pstrdup(res->resname);
69
70                         typename->typeid = res->restype;
71                         typename->typmod = res->restypmod;
72                         def->typename = typename;
73
74                         def->inhcount = 0;
75                         def->is_local = true;
76                         def->is_not_null = false;
77                         def->raw_default = NULL;
78                         def->cooked_default = NULL;
79                         def->constraints = NIL;
80                         def->support = NULL;
81
82                         attrList = lappend(attrList, def);
83                 }
84         }
85
86         if (attrList == NIL)
87                 ereport(ERROR,
88                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
89                                  errmsg("view must have at least one column")));
90
91         /*
92          * Check to see if we want to replace an existing view.
93          */
94         namespaceId = RangeVarGetCreationNamespace(relation);
95         viewOid = get_relname_relid(relation->relname, namespaceId);
96
97         if (OidIsValid(viewOid) && replace)
98         {
99                 Relation        rel;
100                 TupleDesc       descriptor;
101
102                 /*
103                  * Yes.  Get exclusive lock on the existing view ...
104                  */
105                 rel = relation_open(viewOid, AccessExclusiveLock);
106
107                 /*
108                  * Make sure it *is* a view, and do permissions checks.
109                  */
110                 if (rel->rd_rel->relkind != RELKIND_VIEW)
111                         ereport(ERROR,
112                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
113                                          errmsg("\"%s\" is not a view",
114                                                         RelationGetRelationName(rel))));
115
116                 if (!pg_class_ownercheck(viewOid, GetUserId()))
117                         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
118                                                    RelationGetRelationName(rel));
119
120                 /*
121                  * Create a tuple descriptor to compare against the existing view,
122                  * and verify it matches.
123                  */
124                 descriptor = BuildDescForRelation(attrList);
125                 checkViewTupleDesc(descriptor, rel->rd_att);
126
127                 /*
128                  * Seems okay, so return the OID of the pre-existing view.
129                  */
130                 relation_close(rel, NoLock);    /* keep the lock! */
131
132                 return viewOid;
133         }
134         else
135         {
136                 /*
137                  * now set the parameters for keys/inheritance etc. All of these
138                  * are uninteresting for views...
139                  */
140                 createStmt->relation = (RangeVar *) relation;
141                 createStmt->tableElts = attrList;
142                 createStmt->inhRelations = NIL;
143                 createStmt->constraints = NIL;
144                 createStmt->hasoids = MUST_NOT_HAVE_OIDS;
145                 createStmt->oncommit = ONCOMMIT_NOOP;
146                 createStmt->tablespacename = NULL;
147
148                 /*
149                  * finally create the relation (this will error out if there's an
150                  * existing view, so we don't need more code to complain if
151                  * "replace" is false).
152                  */
153                 return DefineRelation(createStmt, RELKIND_VIEW);
154         }
155 }
156
157 /*
158  * Verify that tupledesc associated with proposed new view definition
159  * matches tupledesc of old view.  This is basically a cut-down version
160  * of equalTupleDescs(), with code added to generate specific complaints.
161  */
162 static void
163 checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
164 {
165         int                     i;
166
167         if (newdesc->natts != olddesc->natts)
168                 ereport(ERROR,
169                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
170                                  errmsg("cannot change number of columns in view")));
171         /* we can ignore tdhasoid */
172
173         for (i = 0; i < newdesc->natts; i++)
174         {
175                 Form_pg_attribute newattr = newdesc->attrs[i];
176                 Form_pg_attribute oldattr = olddesc->attrs[i];
177
178                 /* XXX not right, but we don't support DROP COL on view anyway */
179                 if (newattr->attisdropped != oldattr->attisdropped)
180                         ereport(ERROR,
181                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
182                                          errmsg("cannot change number of columns in view")));
183
184                 if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0)
185                         ereport(ERROR,
186                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
187                                          errmsg("cannot change name of view column \"%s\"",
188                                                         NameStr(oldattr->attname))));
189                 /* XXX would it be safe to allow atttypmod to change?  Not sure */
190                 if (newattr->atttypid != oldattr->atttypid ||
191                         newattr->atttypmod != oldattr->atttypmod)
192                         ereport(ERROR,
193                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
194                                    errmsg("cannot change data type of view column \"%s\"",
195                                                   NameStr(oldattr->attname))));
196                 /* We can ignore the remaining attributes of an attribute... */
197         }
198
199         /*
200          * We ignore the constraint fields.  The new view desc can't have any
201          * constraints, and the only ones that could be on the old view are
202          * defaults, which we are happy to leave in place.
203          */
204 }
205
206 static RuleStmt *
207 FormViewRetrieveRule(const RangeVar *view, Query *viewParse, bool replace)
208 {
209         RuleStmt   *rule;
210
211         /*
212          * Create a RuleStmt that corresponds to the suitable rewrite rule
213          * args for DefineQueryRewrite();
214          */
215         rule = makeNode(RuleStmt);
216         rule->relation = copyObject((RangeVar *) view);
217         rule->rulename = pstrdup(ViewSelectRuleName);
218         rule->whereClause = NULL;
219         rule->event = CMD_SELECT;
220         rule->instead = true;
221         rule->actions = list_make1(viewParse);
222         rule->replace = replace;
223
224         return rule;
225 }
226
227 static void
228 DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
229 {
230         RuleStmt   *retrieve_rule;
231
232 #ifdef NOTYET
233         RuleStmt   *replace_rule;
234         RuleStmt   *append_rule;
235         RuleStmt   *delete_rule;
236 #endif
237
238         retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
239
240 #ifdef NOTYET
241         replace_rule = FormViewReplaceRule(view, viewParse);
242         append_rule = FormViewAppendRule(view, viewParse);
243         delete_rule = FormViewDeleteRule(view, viewParse);
244 #endif
245
246         DefineQueryRewrite(retrieve_rule);
247
248 #ifdef NOTYET
249         DefineQueryRewrite(replace_rule);
250         DefineQueryRewrite(append_rule);
251         DefineQueryRewrite(delete_rule);
252 #endif
253
254 }
255
256 /*---------------------------------------------------------------
257  * UpdateRangeTableOfViewParse
258  *
259  * Update the range table of the given parsetree.
260  * This update consists of adding two new entries IN THE BEGINNING
261  * of the range table (otherwise the rule system will die a slow,
262  * horrible and painful death, and we do not want that now, do we?)
263  * one for the OLD relation and one for the NEW one (both of
264  * them refer in fact to the "view" relation).
265  *
266  * Of course we must also increase the 'varnos' of all the Var nodes
267  * by 2...
268  *
269  * These extra RT entries are not actually used in the query,
270  * except for run-time permission checking.
271  *---------------------------------------------------------------
272  */
273 static Query *
274 UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
275 {
276         List       *new_rt;
277         RangeTblEntry *rt_entry1,
278                            *rt_entry2;
279
280         /*
281          * Make a copy of the given parsetree.  It's not so much that we don't
282          * want to scribble on our input, it's that the parser has a bad habit
283          * of outputting multiple links to the same subtree for constructs
284          * like BETWEEN, and we mustn't have OffsetVarNodes increment the
285          * varno of a Var node twice.  copyObject will expand any
286          * multiply-referenced subtree into multiple copies.
287          */
288         viewParse = (Query *) copyObject(viewParse);
289
290         /*
291          * Create the 2 new range table entries and form the new range
292          * table... OLD first, then NEW....
293          */
294         rt_entry1 = addRangeTableEntryForRelation(NULL, viewOid,
295                                                                                           makeAlias("*OLD*", NIL),
296                                                                                           false, false);
297         rt_entry2 = addRangeTableEntryForRelation(NULL, viewOid,
298                                                                                           makeAlias("*NEW*", NIL),
299                                                                                           false, false);
300         /* Must override addRangeTableEntry's default access-check flags */
301         rt_entry1->requiredPerms = 0;
302         rt_entry2->requiredPerms = 0;
303
304         new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
305
306         viewParse->rtable = new_rt;
307
308         /*
309          * Now offset all var nodes by 2, and jointree RT indexes too.
310          */
311         OffsetVarNodes((Node *) viewParse, 2, 0);
312
313         return viewParse;
314 }
315
316 /*-------------------------------------------------------------------
317  * DefineView
318  *
319  *              - takes a "viewname", "parsetree" pair and then
320  *              1)              construct the "virtual" relation
321  *              2)              commit the command but NOT the transaction,
322  *                              so that the relation exists
323  *                              before the rules are defined.
324  *              2)              define the "n" rules specified in the PRS2 paper
325  *                              over the "virtual" relation
326  *-------------------------------------------------------------------
327  */
328 void
329 DefineView(const RangeVar *view, Query *viewParse, bool replace)
330 {
331         Oid                     viewOid;
332
333         /*
334          * Create the view relation
335          *
336          * NOTE: if it already exists and replace is false, the xact will be
337          * aborted.
338          */
339
340         viewOid = DefineVirtualRelation(view, viewParse->targetList, replace);
341
342         /*
343          * The relation we have just created is not visible to any other
344          * commands running with the same transaction & command id. So,
345          * increment the command id counter (but do NOT pfree any memory!!!!)
346          */
347         CommandCounterIncrement();
348
349         /*
350          * The range table of 'viewParse' does not contain entries for the
351          * "OLD" and "NEW" relations. So... add them!
352          */
353         viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
354
355         /*
356          * Now create the rules associated with the view.
357          */
358         DefineViewRules(view, viewParse, replace);
359 }
360
361 /*
362  * RemoveView
363  *
364  * Remove a view given its name
365  *
366  * We just have to drop the relation; the associated rules will be
367  * cleaned up automatically.
368  */
369 void
370 RemoveView(const RangeVar *view, DropBehavior behavior)
371 {
372         Oid                     viewOid;
373         ObjectAddress object;
374
375         viewOid = RangeVarGetRelid(view, false);
376
377         object.classId = RelOid_pg_class;
378         object.objectId = viewOid;
379         object.objectSubId = 0;
380
381         performDeletion(&object, behavior);
382 }