OSDN Git Service

46cdf0aca0ba26717008e3b81163e7754b0b52bc
[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-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.69 2002/09/02 02:13:01 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 /*---------------------------------------------------------------------
33  * DefineVirtualRelation
34  *
35  * Create the "view" relation. `DefineRelation' does all the work,
36  * we just provide the correct arguments ... at least when we're
37  * creating a view.  If we're updating an existing view, we have to
38  * work harder.
39  *---------------------------------------------------------------------
40  */
41 static Oid
42 DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
43 {
44         Oid                     viewOid,
45                                 namespaceId;
46         CreateStmt *createStmt = makeNode(CreateStmt);
47         List       *attrList,
48                            *t;
49
50         /*
51          * create a list of ColumnDef nodes based on the names and types of
52          * the (non-junk) targetlist items from the view's SELECT list.
53          */
54         attrList = NIL;
55         foreach (t, tlist)
56         {
57                 TargetEntry *entry = lfirst(t);
58                 Resdom     *res = entry->resdom;
59
60                 if (!res->resjunk)
61                 {
62                         ColumnDef  *def = makeNode(ColumnDef);
63                         TypeName   *typename = makeNode(TypeName);
64
65                         def->colname = pstrdup(res->resname);
66
67                         typename->typeid = res->restype;
68                         typename->typmod = res->restypmod;
69                         def->typename = typename;
70
71                         def->is_inherited = false;
72                         def->is_not_null = false;
73                         def->raw_default = NULL;
74                         def->cooked_default = NULL;
75                         def->constraints = NIL;
76                         def->support = NULL;
77
78                         attrList = lappend(attrList, def);
79                 }
80         }
81
82         if (attrList == NIL)
83                 elog(ERROR, "attempted to define virtual relation with no attrs");
84
85         /*
86          * Check to see if we want to replace an existing view.
87          */
88         namespaceId = RangeVarGetCreationNamespace(relation);
89         viewOid = get_relname_relid(relation->relname, namespaceId);
90
91         if (OidIsValid(viewOid) && replace)
92         {
93                 Relation        rel;
94                 TupleDesc       descriptor;
95
96                 /*
97                  * Yes.  Get exclusive lock on the existing view ...
98                  */
99                 rel = relation_open(viewOid, AccessExclusiveLock);
100
101                 /*
102                  * Make sure it *is* a view, and do permissions checks.
103                  */
104                 if (rel->rd_rel->relkind != RELKIND_VIEW)
105                         elog(ERROR, "%s is not a view",
106                                  RelationGetRelationName(rel));
107
108                 if (!pg_class_ownercheck(viewOid, GetUserId()))
109                         aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
110
111                 /*
112                  * Create a tuple descriptor to compare against the existing view,
113                  * and verify it matches.
114                  *
115                  * XXX the error message is a bit cheesy here: would be useful to
116                  * give a more specific complaint about the difference in the
117                  * descriptors.  No time for it at the moment though.
118                  */
119             descriptor = BuildDescForRelation(attrList);
120                 if (!equalTupleDescs(descriptor, rel->rd_att))
121                         elog(ERROR, "Cannot change column set of existing view %s",
122                                  RelationGetRelationName(rel));
123
124                 /*
125                  * Seems okay, so return the OID of the pre-existing view.
126                  */
127                 relation_close(rel, NoLock); /* keep the lock! */
128
129                 return viewOid;
130         }
131         else
132         {
133                 /*
134                  * now create the parameters for keys/inheritance etc. All of them are
135                  * nil...
136                  */
137                 createStmt->relation = (RangeVar *) relation;
138                 createStmt->tableElts = attrList;
139                 createStmt->inhRelations = NIL;
140                 createStmt->constraints = NIL;
141                 createStmt->hasoids = false;
142         
143                 /*
144                  * finally create the relation (this will error out if there's
145                  * an existing view, so we don't need more code to complain
146                  * if "replace" is false).
147                  */
148                 return DefineRelation(createStmt, RELKIND_VIEW);
149         }
150 }
151
152 static RuleStmt *
153 FormViewRetrieveRule(const RangeVar *view, Query *viewParse, bool replace)
154 {
155         RuleStmt   *rule;
156
157         /*
158          * Create a RuleStmt that corresponds to the suitable rewrite rule
159          * args for DefineQueryRewrite();
160          */
161         rule = makeNode(RuleStmt);
162         rule->relation = copyObject((RangeVar *) view);
163         rule->rulename = pstrdup(ViewSelectRuleName);
164         rule->whereClause = NULL;
165         rule->event = CMD_SELECT;
166         rule->instead = true;
167         rule->actions = makeList1(viewParse);
168         rule->replace = replace;
169
170         return rule;
171 }
172
173 static void
174 DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
175 {
176         RuleStmt   *retrieve_rule;
177
178 #ifdef NOTYET
179         RuleStmt   *replace_rule;
180         RuleStmt   *append_rule;
181         RuleStmt   *delete_rule;
182 #endif
183
184         retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
185
186 #ifdef NOTYET
187         replace_rule = FormViewReplaceRule(view, viewParse);
188         append_rule = FormViewAppendRule(view, viewParse);
189         delete_rule = FormViewDeleteRule(view, viewParse);
190 #endif
191
192         DefineQueryRewrite(retrieve_rule);
193
194 #ifdef NOTYET
195         DefineQueryRewrite(replace_rule);
196         DefineQueryRewrite(append_rule);
197         DefineQueryRewrite(delete_rule);
198 #endif
199
200 }
201
202 /*---------------------------------------------------------------
203  * UpdateRangeTableOfViewParse
204  *
205  * Update the range table of the given parsetree.
206  * This update consists of adding two new entries IN THE BEGINNING
207  * of the range table (otherwise the rule system will die a slow,
208  * horrible and painful death, and we do not want that now, do we?)
209  * one for the OLD relation and one for the NEW one (both of
210  * them refer in fact to the "view" relation).
211  *
212  * Of course we must also increase the 'varnos' of all the Var nodes
213  * by 2...
214  *
215  * These extra RT entries are not actually used in the query,
216  * except for run-time permission checking.
217  *---------------------------------------------------------------
218  */
219 static Query *
220 UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
221 {
222         List       *new_rt;
223         RangeTblEntry *rt_entry1,
224                            *rt_entry2;
225
226         /*
227          * Make a copy of the given parsetree.  It's not so much that we don't
228          * want to scribble on our input, it's that the parser has a bad habit
229          * of outputting multiple links to the same subtree for constructs
230          * like BETWEEN, and we mustn't have OffsetVarNodes increment the
231          * varno of a Var node twice.  copyObject will expand any
232          * multiply-referenced subtree into multiple copies.
233          */
234         viewParse = (Query *) copyObject(viewParse);
235
236         /*
237          * Create the 2 new range table entries and form the new range
238          * table... OLD first, then NEW....
239          */
240         rt_entry1 = addRangeTableEntryForRelation(NULL, viewOid,
241                                                                                           makeAlias("*OLD*", NIL),
242                                                                                           false, false);
243         rt_entry2 = addRangeTableEntryForRelation(NULL, viewOid,
244                                                                                           makeAlias("*NEW*", NIL),
245                                                                                           false, false);
246         /* Must override addRangeTableEntry's default access-check flags */
247         rt_entry1->checkForRead = false;
248         rt_entry2->checkForRead = false;
249
250         new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
251
252         viewParse->rtable = new_rt;
253
254         /*
255          * Now offset all var nodes by 2, and jointree RT indexes too.
256          */
257         OffsetVarNodes((Node *) viewParse, 2, 0);
258
259         return viewParse;
260 }
261
262 /*-------------------------------------------------------------------
263  * DefineView
264  *
265  *              - takes a "viewname", "parsetree" pair and then
266  *              1)              construct the "virtual" relation
267  *              2)              commit the command but NOT the transaction,
268  *                              so that the relation exists
269  *                              before the rules are defined.
270  *              2)              define the "n" rules specified in the PRS2 paper
271  *                              over the "virtual" relation
272  *-------------------------------------------------------------------
273  */
274 void
275 DefineView(const RangeVar *view, Query *viewParse, bool replace)
276 {
277         Oid                     viewOid;
278
279         /*
280          * Create the view relation
281          *
282          * NOTE: if it already exists and replace is false, the xact will 
283          * be aborted.
284          */
285
286         viewOid = DefineVirtualRelation(view, viewParse->targetList, replace);
287
288         /*
289          * The relation we have just created is not visible to any other
290          * commands running with the same transaction & command id. So,
291          * increment the command id counter (but do NOT pfree any memory!!!!)
292          */
293         CommandCounterIncrement();
294
295         /*
296          * The range table of 'viewParse' does not contain entries for the
297          * "OLD" and "NEW" relations. So... add them!
298          */
299         viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
300
301         /*
302          * Now create the rules associated with the view.
303          */
304         DefineViewRules(view, viewParse, replace);
305 }
306
307 /*
308  * RemoveView
309  *
310  * Remove a view given its name
311  *
312  * We just have to drop the relation; the associated rules will be
313  * cleaned up automatically.
314  */
315 void
316 RemoveView(const RangeVar *view, DropBehavior behavior)
317 {
318         Oid                     viewOid;
319         ObjectAddress object;
320
321         viewOid = RangeVarGetRelid(view, false);
322
323         object.classId = RelOid_pg_class;
324         object.objectId = viewOid;
325         object.objectSubId = 0;
326
327         performDeletion(&object, behavior);
328 }