OSDN Git Service

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