]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteHandler.c
Postgres95 1.01 Distribution - Virgin Sources
[postgresql] / src / backend / rewrite / rewriteHandler.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteHandler.c--
4  *
5  * Copyright (c) 1994, Regents of the University of California
6  *
7  *
8  * IDENTIFICATION
9  *    $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 #include "miscadmin.h"
15 #include "utils/palloc.h"
16 #include "utils/elog.h"
17 #include "utils/rel.h"
18 #include "nodes/pg_list.h"      
19 #include "nodes/primnodes.h"
20
21 #include "parser/parsetree.h"           /* for parsetree manipulation */
22 #include "nodes/parsenodes.h"
23
24 #include "rewrite/rewriteSupport.h"     
25 #include "rewrite/rewriteHandler.h"
26 #include "rewrite/rewriteManip.h"
27 #include "rewrite/locks.h"
28
29 #include "commands/creatinh.h"
30 #include "access/heapam.h"
31
32 static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, 
33                               int rt_index, int relation_level, int *modified);
34 static List *fireRules(Query *parsetree, int rt_index, CmdType event,
35                        bool *instead_flag, List *locks, List **qual_products);
36 static List *deepRewriteQuery(Query *parsetree);
37
38 /*
39  * gatherRewriteMeta -
40  *    Gather meta information about parsetree, and rule. Fix rule body
41  *    and qualifier so that they can be mixed with the parsetree and
42  *    maintain semantic validity
43  */
44 static RewriteInfo *
45 gatherRewriteMeta(Query *parsetree,
46                   Query *rule_action,
47                   Node *rule_qual,
48                   int rt_index,
49                   CmdType event,
50                   bool *instead_flag)
51 {
52     RewriteInfo *info;    
53     int rt_length;
54     int result_reln;
55     
56     info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
57     info->rt_index = rt_index;
58     info->event = event;
59     info->instead_flag = *instead_flag;
60 /*    info->rule_action = rule_action;  this needs to be a copy here, I think! - jolly*/
61     info->rule_action = (Query*)copyObject(rule_action);
62     info->rule_qual = (Node*)copyObject(rule_qual);
63     info->nothing = FALSE;
64     info->action = info->rule_action->commandType;
65     if (info->rule_action == NULL) info->nothing = TRUE;
66     if (info->nothing)
67         return info;
68
69     info->current_varno = rt_index;
70     info->rt = parsetree->rtable;
71     rt_length = length(info->rt);
72     info->rt = append(info->rt, info->rule_action->rtable); 
73
74
75     info->new_varno = PRS2_NEW_VARNO + rt_length;
76     OffsetVarNodes(info->rule_action->qual, rt_length);
77     OffsetVarNodes((Node*)info->rule_action->targetList, rt_length);
78     OffsetVarNodes(info->rule_qual, rt_length);
79     ChangeVarNodes((Node*)info->rule_action->qual,
80                    PRS2_CURRENT_VARNO+rt_length, rt_index);
81     ChangeVarNodes((Node*)info->rule_action->targetList,
82                    PRS2_CURRENT_VARNO+rt_length, rt_index);
83     ChangeVarNodes(info->rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
84
85     /*
86      * bug here about replace CURRENT  -- sort of
87      * replace current is deprecated now so this code shouldn't really
88      * need to be so clutzy but.....
89      */
90     if (info->action != CMD_SELECT) {   /* i.e update XXXXX */
91         int new_result_reln = 0;
92         result_reln = info->rule_action->resultRelation;
93         switch (result_reln) {
94         case PRS2_CURRENT_VARNO: new_result_reln = rt_index;
95             break; 
96         case PRS2_NEW_VARNO:    /* XXX */
97         default:
98             new_result_reln = result_reln + rt_length;
99             break;
100         }
101         info->rule_action->resultRelation = new_result_reln;
102     }
103     
104     return info;
105 }
106
107 static List *
108 OptimizeRIRRules(List *locks)
109 {
110     List *attr_level = NIL, *i;
111     List *relation_level = NIL;
112     
113     foreach (i, locks) {
114         RewriteRule *rule_lock  = lfirst(i);
115
116         if (rule_lock->attrno == -1) 
117             relation_level = lappend(relation_level, rule_lock);
118         else
119             attr_level = lappend(attr_level, rule_lock);
120     }
121     return nconc(relation_level, attr_level);
122 }
123
124 /*
125  * idea is to put instead rules before regular rules so that
126  * excess semantically queasy queries aren't processed
127  */
128 static List *
129 orderRules(List *locks)
130 {
131     List *regular = NIL, *i;
132     List *instead_rules = NIL;
133     
134     foreach (i, locks) {
135         RewriteRule *rule_lock  = (RewriteRule *)lfirst(i);
136
137         if (rule_lock->isInstead)
138             instead_rules = lappend(instead_rules, rule_lock);
139         else
140             regular = lappend(regular, rule_lock);
141     }
142     return nconc(regular, instead_rules);
143 }
144
145 static int
146 AllRetrieve(List *actions)
147 {
148     List *n;
149     
150     foreach(n, actions) {
151         Query *pt = lfirst(n);
152
153         /*
154          * in the old postgres code, we check whether command_type is
155          * a consp of '('*'.commandType). but we've never supported transitive
156          * closures. Hence removed    - ay 10/94.
157          */
158         if (pt->commandType != CMD_SELECT)
159             return false;
160     }
161     return true;
162 }
163
164 static List *
165 FireRetrieveRulesAtQuery(Query *parsetree,
166                          int rt_index,
167                          Relation relation,
168                          bool *instead_flag,
169                          int rule_flag)
170 {
171     List *i, *locks;
172     RuleLock *rt_entry_locks = NULL;
173     List *work = NIL;
174
175     if ((rt_entry_locks = relation->rd_rules) == NULL)
176         return NIL;
177     
178     locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
179
180     /* find all retrieve instead */
181     foreach (i, locks) {
182         RewriteRule *rule_lock  = (RewriteRule *)lfirst(i);
183         
184         if (!rule_lock->isInstead)
185             continue;
186         work = lappend(work, rule_lock);
187     }
188     if (work != NIL) {
189         work = OptimizeRIRRules(locks);
190         foreach (i, work) {
191             RewriteRule *rule_lock = lfirst(i);
192             int relation_level;
193             int modified = FALSE;
194
195             relation_level = (rule_lock->attrno == -1);
196             if (rule_lock->actions == NIL) {
197                 *instead_flag = TRUE;
198                 return NIL;
199             }
200             if (!rule_flag &&
201                 length(rule_lock->actions) >= 2 &&
202                 AllRetrieve(rule_lock->actions)) {
203                 *instead_flag = TRUE;
204                 return rule_lock->actions;
205             }
206             ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level,
207                               &modified);
208             if (modified) {
209                 *instead_flag = TRUE;
210                 FixResdomTypes(parsetree->targetList);
211                 return lcons(parsetree,NIL);
212             }
213         }
214     }
215     return NIL;
216 }       
217
218
219 /* Idea is like this:
220  *
221  * retrieve-instead-retrieve rules have different semantics than update nodes
222  * Separate RIR rules from others.  Pass others to FireRules.
223  * Order RIR rules and process.
224  *
225  * side effect: parsetree's rtable field might be changed
226  */
227 static void
228 ApplyRetrieveRule(Query *parsetree,
229                   RewriteRule *rule, 
230                   int rt_index,
231                   int relation_level,
232                   int *modified)
233 {
234     Query *rule_action = NULL;
235     Node *rule_qual;
236     List *rtable, *rt;
237     int nothing, rt_length;
238     int badsql= FALSE;
239
240     rule_qual = rule->qual;
241     if (rule->actions) {
242         if (length(rule->actions) > 1)  /* ??? because we don't handle rules
243                                            with more than one action? -ay */
244             return;
245         rule_action = copyObject(lfirst(rule->actions));
246         nothing = FALSE;
247     } else {
248         nothing = TRUE;
249     }
250
251     rtable = copyObject(parsetree->rtable);
252     foreach (rt, rtable) {
253         RangeTblEntry *rte = lfirst(rt);
254         /*
255          * this is to prevent add_missing_vars_to_base_rels() from
256          * adding a bogus entry to the new target list.
257          */
258         rte->inFromCl = false;
259     }
260     rt_length = length(rtable);
261     rtable = nconc(rtable, copyObject(rule_action->rtable)); 
262     parsetree->rtable = rtable;
263
264     rule_action->rtable = rtable;
265     OffsetVarNodes(rule_action->qual, rt_length);
266     OffsetVarNodes((Node*)rule_action->targetList, rt_length);
267     OffsetVarNodes(rule_qual, rt_length);
268     ChangeVarNodes(rule_action->qual,
269                    PRS2_CURRENT_VARNO+rt_length, rt_index);
270     ChangeVarNodes((Node*)rule_action->targetList,
271                    PRS2_CURRENT_VARNO+rt_length, rt_index);
272     ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
273     if (relation_level) {
274         HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
275                        modified);
276     } else {
277         HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
278                                rt_index, rule->attrno, modified, &badsql);
279     }
280     if (*modified && !badsql)
281         AddQual(parsetree, rule_action->qual);
282 }
283
284 static List *
285 ProcessRetrieveQuery(Query *parsetree,
286                      List *rtable,
287                      bool *instead_flag,
288                      bool rule)
289 {
290     List *rt;
291     List *product_queries = NIL;
292     int rt_index = 0;
293     
294     foreach (rt, rtable) {
295         RangeTblEntry *rt_entry = lfirst(rt);
296         Relation rt_entry_relation = NULL;
297         List *result = NIL;
298         
299         rt_index++;
300         rt_entry_relation = heap_openr(rt_entry->relname);
301
302         if (rt_entry_relation->rd_rules != NULL) {
303             result = 
304                 FireRetrieveRulesAtQuery(parsetree,
305                                          rt_index,
306                                          rt_entry_relation,
307                                          instead_flag,
308                                          rule);
309         }
310         heap_close(rt_entry_relation);
311         if (*instead_flag)
312             return result;
313     }
314     if (rule)
315         return NIL;
316
317     foreach (rt, rtable) {
318         RangeTblEntry *rt_entry = lfirst(rt);
319         Relation rt_entry_relation = NULL;
320         RuleLock *rt_entry_locks = NULL;
321         List *result = NIL;
322         List *locks = NIL;
323         List *dummy_products;
324
325         rt_index++;
326         rt_entry_relation = heap_openr(rt_entry->relname);
327         rt_entry_locks = rt_entry_relation->rd_rules;
328         heap_close(rt_entry_relation);
329
330         if (rt_entry_locks) {
331             locks =
332                 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
333         }
334         if (locks != NIL) {
335             result = fireRules(parsetree, rt_index, CMD_SELECT,
336                                instead_flag, locks, &dummy_products);
337             if (*instead_flag)
338                 return lappend(NIL, result);
339             if (result != NIL)
340                 product_queries = nconc(product_queries, result);
341         }
342     }
343     return product_queries;
344 }
345
346 static Query *
347 CopyAndAddQual(Query *parsetree,
348                List *actions,
349                Node *rule_qual,
350                int rt_index,
351                CmdType event)
352 {
353     Query *new_tree = (Query *) copyObject(parsetree);
354     Node *new_qual = NULL;
355     Query *rule_action = NULL;
356     
357     if (actions)
358         rule_action = lfirst(actions);
359     if (rule_qual != NULL)
360         new_qual = (Node *)copyObject(rule_qual);
361     if (rule_action != NULL) {
362         List *rtable;
363         int rt_length;
364         
365         rtable = new_tree->rtable;
366         rt_length = length(rtable);
367         rtable = append(rtable,listCopy(rule_action->rtable));
368         new_tree->rtable = rtable;
369         OffsetVarNodes(new_qual, rt_length);
370         ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
371     }
372     /* XXX -- where current doesn't work for instead nothing.... yet*/
373     AddNotQual(new_tree, new_qual);
374
375     return new_tree;
376 }
377
378
379 /*
380  *  fireRules -
381  *     Iterate through rule locks applying rules.  After an instead rule
382  *     rule has been applied, return just new parsetree and let RewriteQuery
383  *     start the process all over again.  The locks are reordered to maintain
384  *     sensible semantics.  remember: reality is for dead birds -- glass
385  *
386  */
387 static List *
388 fireRules(Query *parsetree,
389           int rt_index,
390           CmdType event,
391           bool *instead_flag,
392           List *locks,
393           List **qual_products)
394 {
395     RewriteInfo *info;
396     List *results = NIL;
397     List *i;
398     
399     /* choose rule to fire from list of rules */
400     if (locks == NIL) {
401         (void) ProcessRetrieveQuery(parsetree,
402                                     parsetree->rtable,
403                                     instead_flag, TRUE);
404         if (*instead_flag)
405             return lappend(NIL, parsetree);
406         else
407             return NIL;
408     }
409     
410     locks = orderRules(locks);  /* instead rules first */
411     foreach (i, locks) {
412         RewriteRule *rule_lock = (RewriteRule *)lfirst(i);
413         Node *qual, *event_qual;
414         List *actions;
415         List *r;
416         bool orig_instead_flag = *instead_flag;
417
418         /* multiple rule action time */
419         *instead_flag = rule_lock->isInstead;
420         event_qual = rule_lock->qual;
421         actions = rule_lock->actions;
422         if (event_qual != NULL && *instead_flag)
423             *qual_products =
424                 lappend(*qual_products,
425                          CopyAndAddQual(parsetree, actions, event_qual,
426                                         rt_index, event));
427         foreach (r, actions) {
428             Query *rule_action  = lfirst(r);
429             Node *rule_qual = copyObject(event_qual);
430             
431             /*--------------------------------------------------
432              * Step 1:
433              *    Rewrite current.attribute or current to tuple variable
434              *    this appears to be done in parser?
435              *--------------------------------------------------
436              */
437             info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
438                                      rt_index,event,instead_flag);
439             
440             /* handle escapable cases, or those handled by other code */
441             if (info->nothing) {
442                 if (*instead_flag)
443                     return NIL;
444                 else
445                     continue;
446             }
447
448             if (info->action == info->event &&
449                 info->event == CMD_SELECT)
450                 continue;
451
452             /*
453              * Event Qualification forces copying of parsetree --- XXX
454              * and splitting into two queries one w/rule_qual, one
455              * w/NOT rule_qual. Also add user query qual onto rule action
456              */
457             qual = parsetree->qual;
458             AddQual(info->rule_action, qual);
459             
460             if (info->rule_qual != NULL)
461                 AddQual(info->rule_action, info->rule_qual);
462             
463             /*--------------------------------------------------
464              * Step 2:
465              *    Rewrite new.attribute w/ right hand side of target-list
466              *    entry for appropriate field name in insert/update
467              *--------------------------------------------------
468              */  
469             if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE)) {
470                 FixNew(info, parsetree);
471             }
472             
473             /*--------------------------------------------------
474              * Step 3:
475              *    rewriting due to retrieve rules 
476              *--------------------------------------------------
477              */
478             info->rule_action->rtable = info->rt;                  
479             (void) ProcessRetrieveQuery(info->rule_action, info->rt,
480                                         &orig_instead_flag, TRUE);
481             
482             /*--------------------------------------------------  
483              * Step 4
484              *    Simplify? hey, no algorithm for simplification... let
485              *    the planner do it.
486              *--------------------------------------------------  
487              */
488             results = lappend(results, info->rule_action);
489
490             pfree(info);
491         }
492         if (*instead_flag) break;
493     }
494     return results;
495 }
496
497 static List *
498 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
499 {
500     CmdType event;
501     List *product_queries = NIL;
502     int result_relation =  0;
503     
504     Assert(parsetree != NULL);
505     
506     event = parsetree->commandType;
507
508     if (event == CMD_UTILITY)
509         return NIL;
510
511     /*
512      * only for a delete may the targetlist be NULL
513      */
514     if (event != CMD_DELETE) {
515         Assert(parsetree->targetList != NULL);
516     }
517     
518     result_relation = parsetree->resultRelation;
519
520     if (event != CMD_SELECT) {
521         /*
522          * the statement is an update, insert or delete
523          */
524         RangeTblEntry *rt_entry;
525         Relation rt_entry_relation = NULL;
526         RuleLock *rt_entry_locks = NULL;
527         
528         rt_entry = rt_fetch(result_relation, parsetree->rtable);
529         rt_entry_relation = heap_openr(rt_entry->relname);
530         rt_entry_locks = rt_entry_relation->rd_rules;
531         heap_close(rt_entry_relation);
532
533         if (rt_entry_locks != NULL) {
534             List *locks =
535                 matchLocks(event, rt_entry_locks, result_relation, parsetree);
536             
537             product_queries =
538                 fireRules(parsetree, 
539                           result_relation,
540                           event,
541                           instead_flag,
542                           locks,
543                           qual_products);
544         }
545         return product_queries;
546     }else {
547         /*
548          * the statement is a select
549          */
550         Query *other;
551
552         other = copyObject(parsetree);  /* ApplyRetrieveRule changes the
553                                            range table */
554         return
555             ProcessRetrieveQuery(other, parsetree->rtable,
556                                  instead_flag, FALSE);
557     }
558 }
559
560 /*
561  * to avoid infinite recursion, we restrict the number of times a query
562  * can be rewritten. Detecting cycles is left for the reader as an excercise.
563  */
564 #ifndef REWRITE_INVOKE_MAX
565 #define REWRITE_INVOKE_MAX      10
566 #endif
567
568 static int numQueryRewriteInvoked = 0;
569
570 /*
571  * QueryRewrite -
572  *    rewrite one query via QueryRewrite system, possibly returning 0, or many
573  *    queries
574  */  
575 List *
576 QueryRewrite(Query *parsetree)
577 {
578     numQueryRewriteInvoked = 0;
579
580     /*
581      * take a deep breath and apply all the rewrite rules - ay
582      */
583     return deepRewriteQuery(parsetree);
584 }
585
586 /*
587  * deepRewriteQuery -
588  *    rewrites the query and apply the rules again on the queries rewritten
589  */
590 static List *
591 deepRewriteQuery(Query *parsetree)
592 {
593     List *n;
594     List *rewritten = NIL;
595     List *result = NIL;
596     bool instead;
597     List *qual_products = NIL;
598
599     if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX) {
600         elog(WARN, "query rewritten %d times, may contain cycles",
601              numQueryRewriteInvoked-1);
602     }
603     
604     instead = FALSE;
605     result = RewriteQuery(parsetree, &instead, &qual_products);
606     if (!instead)
607         rewritten = lcons(parsetree, NIL);
608
609     foreach(n, result) {
610         Query *pt = lfirst(n);
611         List *newstuff = NIL;
612         
613         newstuff = deepRewriteQuery(pt);
614         if (newstuff != NIL)
615             rewritten = nconc(rewritten, newstuff);
616     }
617     if (qual_products != NIL)
618         rewritten = nconc(rewritten, qual_products);
619
620     return rewritten;
621 }
622