OSDN Git Service

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