OSDN Git Service

Fix longstanding bug with VIEW using BETWEEN: OffsetVarNodes would get
[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-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *      $Id: view.c,v 1.51 2000/12/21 17:36:15 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "access/xact.h"
17 #include "catalog/heap.h"
18 #include "commands/creatinh.h"
19 #include "commands/view.h"
20 #include "miscadmin.h"
21 #include "nodes/makefuncs.h"
22 #include "parser/parse_relation.h"
23 #include "parser/parse_type.h"
24 #include "rewrite/rewriteDefine.h"
25 #include "rewrite/rewriteManip.h"
26 #include "rewrite/rewriteRemove.h"
27
28 #ifdef MULTIBYTE
29 #include "mb/pg_wchar.h"
30 #endif
31
32 /*---------------------------------------------------------------------
33  * DefineVirtualRelation
34  *
35  * Create the "view" relation.
36  * `DefineRelation' does all the work, we just provide the correct
37  * arguments!
38  *
39  * If the relation already exists, then 'DefineRelation' will abort
40  * the xact...
41  *---------------------------------------------------------------------
42  */
43 static void
44 DefineVirtualRelation(char *relname, List *tlist)
45 {
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                         char       *resname = res->resname;
63                         char       *restypename = typeidTypeName(res->restype);
64                         ColumnDef  *def = makeNode(ColumnDef);
65                         TypeName   *typename = makeNode(TypeName);
66
67                         def->colname = pstrdup(resname);
68
69                         typename->name = pstrdup(restypename);
70                         typename->typmod = res->restypmod;
71                         def->typename = typename;
72
73                         def->is_not_null = false;
74                         def->is_sequence = false;
75                         def->raw_default = NULL;
76                         def->cooked_default = NULL;
77                         def->constraints = NIL;
78
79                         attrList = lappend(attrList, def);
80                 }
81         }
82
83         if (attrList == NIL)
84                 elog(ERROR, "attempted to define virtual relation with no attrs");
85
86         /*
87          * now create the parameters for keys/inheritance etc. All of them are
88          * nil...
89          */
90         createStmt->relname = relname;
91         createStmt->istemp = false;
92         createStmt->tableElts = attrList;
93         createStmt->inhRelnames = NIL;
94         createStmt->constraints = NIL;
95
96         /*
97          * finally create the relation...
98          */
99         DefineRelation(createStmt, RELKIND_VIEW);
100 }
101
102 /*------------------------------------------------------------------
103  * makeViewRetrieveRuleName
104  *
105  * Given a view name, returns the name for the 'on retrieve to "view"'
106  * rule.
107  *------------------------------------------------------------------
108  */
109 char *
110 MakeRetrieveViewRuleName(char *viewName)
111 {
112         char       *buf;
113 #ifdef MULTIBYTE
114         int                     len;
115 #endif
116
117         buf = palloc(strlen(viewName) + 5);
118         snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
119
120 #ifdef MULTIBYTE
121         len = pg_mbcliplen(buf,strlen(buf),NAMEDATALEN-1);
122         buf[len] = '\0';
123 #else
124         buf[NAMEDATALEN-1] = '\0';
125 #endif
126
127         return buf;
128 }
129
130 static RuleStmt *
131 FormViewRetrieveRule(char *viewName, Query *viewParse)
132 {
133         RuleStmt   *rule;
134         char       *rname;
135         Attr       *attr;
136
137         /*
138          * Create a RuleStmt that corresponds to the suitable rewrite rule
139          * args for DefineQueryRewrite();
140          */
141         rule = makeNode(RuleStmt);
142         rname = MakeRetrieveViewRuleName(viewName);
143
144         attr = makeNode(Attr);
145         attr->relname = pstrdup(viewName);
146         rule->rulename = pstrdup(rname);
147         rule->whereClause = NULL;
148         rule->event = CMD_SELECT;
149         rule->object = attr;
150         rule->instead = true;
151         rule->actions = makeList1(viewParse);
152
153         return rule;
154 }
155
156 static void
157 DefineViewRules(char *viewName, Query *viewParse)
158 {
159         RuleStmt   *retrieve_rule;
160
161 #ifdef NOTYET
162         RuleStmt   *replace_rule;
163         RuleStmt   *append_rule;
164         RuleStmt   *delete_rule;
165
166 #endif
167
168         retrieve_rule = FormViewRetrieveRule(viewName, viewParse);
169
170 #ifdef NOTYET
171
172         replace_rule = FormViewReplaceRule(viewName, viewParse);
173         append_rule = FormViewAppendRule(viewName, viewParse);
174         delete_rule = FormViewDeleteRule(viewName, viewParse);
175
176 #endif
177
178         DefineQueryRewrite(retrieve_rule);
179
180 #ifdef NOTYET
181         DefineQueryRewrite(replace_rule);
182         DefineQueryRewrite(append_rule);
183         DefineQueryRewrite(delete_rule);
184 #endif
185
186 }
187
188 /*---------------------------------------------------------------
189  * UpdateRangeTableOfViewParse
190  *
191  * Update the range table of the given parsetree.
192  * This update consists of adding two new entries IN THE BEGINNING
193  * of the range table (otherwise the rule system will die a slow,
194  * horrible and painful death, and we do not want that now, do we?)
195  * one for the OLD relation and one for the NEW one (both of
196  * them refer in fact to the "view" relation).
197  *
198  * Of course we must also increase the 'varnos' of all the Var nodes
199  * by 2...
200  *
201  * These extra RT entries are not actually used in the query,
202  * except for run-time permission checking.
203  *---------------------------------------------------------------
204  */
205 static Query *
206 UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
207 {
208         List       *new_rt;
209         RangeTblEntry *rt_entry1,
210                            *rt_entry2;
211
212         /*
213          * Make a copy of the given parsetree.  It's not so much that we
214          * don't want to scribble on our input, it's that the parser has
215          * a bad habit of outputting multiple links to the same subtree
216          * for constructs like BETWEEN, and we mustn't have OffsetVarNodes
217          * increment the varno of a Var node twice.  copyObject will expand
218          * any multiply-referenced subtree into multiple copies.
219          */
220         viewParse = (Query *) copyObject(viewParse);
221
222         /*
223          * Create the 2 new range table entries and form the new range
224          * table... OLD first, then NEW....
225          */
226         rt_entry1 = addRangeTableEntry(NULL, viewName,
227                                                                    makeAttr("*OLD*", NULL),
228                                                                    false, false);
229         rt_entry2 = addRangeTableEntry(NULL, viewName,
230                                                                    makeAttr("*NEW*", NULL),
231                                                                    false, false);
232         /* Must override addRangeTableEntry's default access-check flags */
233         rt_entry1->checkForRead = false;
234         rt_entry2->checkForRead = false;
235
236         new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
237
238         viewParse->rtable = new_rt;
239
240         /*
241          * Now offset all var nodes by 2, and jointree RT indexes too.
242          */
243         OffsetVarNodes((Node *) viewParse, 2, 0);
244
245         return viewParse;
246 }
247
248 /*-------------------------------------------------------------------
249  * DefineView
250  *
251  *              - takes a "viewname", "parsetree" pair and then
252  *              1)              construct the "virtual" relation
253  *              2)              commit the command but NOT the transaction,
254  *                              so that the relation exists
255  *                              before the rules are defined.
256  *              2)              define the "n" rules specified in the PRS2 paper
257  *                              over the "virtual" relation
258  *-------------------------------------------------------------------
259  */
260 void
261 DefineView(char *viewName, Query *viewParse)
262 {
263         /*
264          * Create the "view" relation NOTE: if it already exists, the xact
265          * will be aborted.
266          */
267         DefineVirtualRelation(viewName, viewParse->targetList);
268
269         /*
270          * The relation we have just created is not visible to any other
271          * commands running with the same transaction & command id. So,
272          * increment the command id counter (but do NOT pfree any memory!!!!)
273          */
274         CommandCounterIncrement();
275
276         /*
277          * The range table of 'viewParse' does not contain entries for the
278          * "OLD" and "NEW" relations. So... add them!
279          */
280         viewParse = UpdateRangeTableOfViewParse(viewName, viewParse);
281
282         /*
283          * Now create the rules associated with the view.
284          */
285         DefineViewRules(viewName, viewParse);
286 }
287
288 /*------------------------------------------------------------------
289  * RemoveView
290  *
291  * Remove a view given its name
292  *------------------------------------------------------------------
293  */
294 void
295 RemoveView(char *viewName)
296 {
297         /*
298          * We just have to drop the relation; the associated rules will
299          * be cleaned up automatically.
300          */
301         heap_drop_with_catalog(viewName, allowSystemTableMods);
302 }