Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Annotation of pgsql/src/backend/rewrite/rewriteHandler.c, revision 1.155

1.1       scrappy     1: /*-------------------------------------------------------------------------
                      2:  *
1.35      momjian     3:  * rewriteHandler.c
1.95      tgl         4:  *             Primary module of query rewriter.
1.1       scrappy     5:  *
1.147     pgsql       6:  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
1.66      momjian     7:  * Portions Copyright (c) 1994, Regents of the University of California
1.1       scrappy     8:  *
                      9:  * IDENTIFICATION
1.155   ! tgl        10:  *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.154 2005/06/04 19:19:42 tgl Exp $
1.1       scrappy    11:  *
                     12:  *-------------------------------------------------------------------------
                     13:  */
                     14: #include "postgres.h"
1.53      momjian    15: 
                     16: #include "access/heapam.h"
1.55      tgl        17: #include "catalog/pg_operator.h"
1.53      momjian    18: #include "catalog/pg_type.h"
                     19: #include "miscadmin.h"
1.55      tgl        20: #include "nodes/makefuncs.h"
1.47      tgl        21: #include "optimizer/clauses.h"
1.28      momjian    22: #include "optimizer/prep.h"
1.70      tgl        23: #include "optimizer/var.h"
1.53      momjian    24: #include "parser/analyze.h"
1.101     tgl        25: #include "parser/parse_coerce.h"
1.55      tgl        26: #include "parser/parse_expr.h"
                     27: #include "parser/parse_oper.h"
1.101     tgl        28: #include "parser/parse_type.h"
1.53      momjian    29: #include "parser/parsetree.h"
1.95      tgl        30: #include "rewrite/rewriteHandler.h"
1.1       scrappy    31: #include "rewrite/rewriteManip.h"
1.101     tgl        32: #include "utils/builtins.h"
1.22      momjian    33: #include "utils/lsyscache.h"
                     34: 
                     35: 
1.118     tgl        36: /* We use a list of these to detect recursion in RewriteQuery */
1.126     momjian    37: typedef struct rewrite_event
                     38: {
1.118     tgl        39:        Oid                     relation;               /* OID of relation having rules */
                     40:        CmdType         event;                  /* type of rule being fired */
1.128     momjian    41: } rewrite_event;
1.118     tgl        42: 
1.153     tgl        43: static bool acquireLocksOnSubLinks(Node *node, void *context);
1.95      tgl        44: static Query *rewriteRuleAction(Query *parsetree,
1.98      momjian    45:                                  Query *rule_action,
                     46:                                  Node *rule_qual,
                     47:                                  int rt_index,
                     48:                                  CmdType event);
1.89      tgl        49: static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
1.101     tgl        50: static void rewriteTargetList(Query *parsetree, Relation target_relation);
                     51: static TargetEntry *process_matched_tle(TargetEntry *src_tle,
1.144     momjian    52:                                        TargetEntry *prior_tle,
                     53:                                        const char *attrName);
1.139     tgl        54: static Node *get_assignment_input(Node *node);
1.151     tgl        55: static void markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew);
1.81      tgl        56: static List *matchLocks(CmdType event, RuleLock *rulelocks,
1.90      momjian    57:                   int varno, Query *parsetree);
1.118     tgl        58: static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
1.82      tgl        59: 
1.1       scrappy    60: 
                     61: /*
1.153     tgl        62:  * AcquireRewriteLocks -
                     63:  *       Acquire suitable locks on all the relations mentioned in the Query.
                     64:  *       These locks will ensure that the relation schemas don't change under us
                     65:  *       while we are rewriting and planning the query.
                     66:  *
                     67:  * A secondary purpose of this routine is to fix up JOIN RTE references to
                     68:  * dropped columns (see details below).  Because the RTEs are modified in
                     69:  * place, it is generally appropriate for the caller of this routine to have
                     70:  * first done a copyObject() to make a writable copy of the querytree in the
                     71:  * current memory context.
                     72:  *
                     73:  * This processing can, and for efficiency's sake should, be skipped when the
                     74:  * querytree has just been built by the parser: parse analysis already got
                     75:  * all the same locks we'd get here, and the parser will have omitted dropped
                     76:  * columns from JOINs to begin with.  But we must do this whenever we are
                     77:  * dealing with a querytree produced earlier than the current command.
                     78:  *
                     79:  * About JOINs and dropped columns: although the parser never includes an
                     80:  * already-dropped column in a JOIN RTE's alias var list, it is possible for
                     81:  * such a list in a stored rule to include references to dropped columns.
                     82:  * (If the column is not explicitly referenced anywhere else in the query,
                     83:  * the dependency mechanism won't consider it used by the rule and so won't
                     84:  * prevent the column drop.)  To support get_rte_attribute_is_dropped(),
                     85:  * we replace join alias vars that reference dropped columns with NULL Const
                     86:  * nodes.
                     87:  *
                     88:  * (In PostgreSQL 8.0, we did not do this processing but instead had
                     89:  * get_rte_attribute_is_dropped() recurse to detect dropped columns in joins.
                     90:  * That approach had horrible performance unfortunately; in particular
                     91:  * construction of a nested join was O(N^2) in the nesting depth.)
                     92:  */
                     93: void
                     94: AcquireRewriteLocks(Query *parsetree)
                     95: {
                     96:        ListCell   *l;
                     97:        int                     rt_index;
                     98: 
                     99:        /*
                    100:         * First, process RTEs of the current query level.
                    101:         */
                    102:        rt_index = 0;
                    103:        foreach(l, parsetree->rtable)
                    104:        {
                    105:                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
                    106:                Relation        rel;
                    107:                LOCKMODE        lockmode;
                    108:                List       *newaliasvars;
1.154     tgl       109:                Index           curinputvarno;
                    110:                RangeTblEntry *curinputrte;
1.153     tgl       111:                ListCell   *ll;
                    112: 
                    113:                ++rt_index;
                    114:                switch (rte->rtekind)
                    115:                {
                    116:                        case RTE_RELATION:
                    117:                                /*
                    118:                                 * Grab the appropriate lock type for the relation, and
                    119:                                 * do not release it until end of transaction. This protects
                    120:                                 * the rewriter and planner against schema changes mid-query.
                    121:                                 *
                    122:                                 * If the relation is the query's result relation, then we
                    123:                                 * need RowExclusiveLock.  Otherwise, check to see if the
                    124:                                 * relation is accessed FOR UPDATE/SHARE or not.  We can't
                    125:                                 * just grab AccessShareLock because then the executor
                    126:                                 * would be trying to upgrade the lock, leading to possible
                    127:                                 * deadlocks.
                    128:                                 */
                    129:                                if (rt_index == parsetree->resultRelation)
                    130:                                        lockmode = RowExclusiveLock;
                    131:                                else if (list_member_int(parsetree->rowMarks, rt_index))
                    132:                                        lockmode = RowShareLock;
                    133:                                else
                    134:                                        lockmode = AccessShareLock;
                    135: 
                    136:                                rel = heap_open(rte->relid, lockmode);
                    137:                                heap_close(rel, NoLock);
                    138:                                break;
                    139: 
                    140:                        case RTE_JOIN:
                    141:                                /*
                    142:                                 * Scan the join's alias var list to see if any columns
                    143:                                 * have been dropped, and if so replace those Vars with
                    144:                                 * NULL Consts.
1.154     tgl       145:                                 *
                    146:                                 * Since a join has only two inputs, we can expect to
                    147:                                 * see multiple references to the same input RTE; optimize
                    148:                                 * away multiple fetches.
1.153     tgl       149:                                 */
                    150:                                newaliasvars = NIL;
1.154     tgl       151:                                curinputvarno = 0;
                    152:                                curinputrte = NULL;
1.153     tgl       153:                                foreach(ll, rte->joinaliasvars)
                    154:                                {
                    155:                                        Var                *aliasvar = (Var *) lfirst(ll);
                    156: 
                    157:                                        /*
                    158:                                         * If the list item isn't a simple Var, then it must
                    159:                                         * represent a merged column, ie a USING column, and so it
                    160:                                         * couldn't possibly be dropped, since it's referenced in
                    161:                                         * the join clause.  (Conceivably it could also be a
                    162:                                         * NULL constant already?  But that's OK too.)
                    163:                                         */
                    164:                                        if (IsA(aliasvar, Var))
                    165:                                        {
                    166:                                                /*
                    167:                                                 * The elements of an alias list have to refer to
                    168:                                                 * earlier RTEs of the same rtable, because that's
                    169:                                                 * the order the planner builds things in.  So we
                    170:                                                 * already processed the referenced RTE, and so it's
                    171:                                                 * safe to use get_rte_attribute_is_dropped on it.
                    172:                                                 * (This might not hold after rewriting or planning,
                    173:                                                 * but it's OK to assume here.)
                    174:                                                 */
                    175:                                                Assert(aliasvar->varlevelsup == 0);
1.154     tgl       176:                                                if (aliasvar->varno != curinputvarno)
                    177:                                                {
                    178:                                                        curinputvarno = aliasvar->varno;
                    179:                                                        if (curinputvarno >= rt_index)
                    180:                                                                elog(ERROR, "unexpected varno %d in JOIN RTE %d",
                    181:                                                                         curinputvarno, rt_index);
                    182:                                                        curinputrte = rt_fetch(curinputvarno,
                    183:                                                                                                   parsetree->rtable);
                    184:                                                }
                    185:                                                if (get_rte_attribute_is_dropped(curinputrte,
                    186:                                                                                                                 aliasvar->varattno))
1.153     tgl       187:                                                {
                    188:                                                        /*
                    189:                                                         * can't use vartype here, since that might be a
                    190:                                                         * now-dropped type OID, but it doesn't really
                    191:                                                         * matter what type the Const claims to be.
                    192:                                                         */
                    193:                                                        aliasvar = (Var *) makeNullConst(INT4OID);
                    194:                                                }
                    195:                                        }
                    196:                                        newaliasvars = lappend(newaliasvars, aliasvar);
                    197:                                }
                    198:                                rte->joinaliasvars = newaliasvars;
                    199:                                break;
                    200: 
                    201:                        case RTE_SUBQUERY:
                    202:                                /*
                    203:                                 * The subquery RTE itself is all right, but we have to
                    204:                                 * recurse to process the represented subquery.
                    205:                                 */
                    206:                                AcquireRewriteLocks(rte->subquery);
                    207:                                break;
                    208: 
                    209:                        default:
                    210:                                /* ignore other types of RTEs */
                    211:                                break;
                    212:                }
                    213:        }
                    214: 
                    215:        /*
                    216:         * Recurse into sublink subqueries, too.  But we already did the ones
                    217:         * in the rtable.
                    218:         */
                    219:        if (parsetree->hasSubLinks)
                    220:                query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
                    221:                                                  QTW_IGNORE_RT_SUBQUERIES);
                    222: }
                    223: 
                    224: /*
                    225:  * Walker to find sublink subqueries for AcquireRewriteLocks
                    226:  */
                    227: static bool
                    228: acquireLocksOnSubLinks(Node *node, void *context)
                    229: {
                    230:        if (node == NULL)
                    231:                return false;
                    232:        if (IsA(node, SubLink))
                    233:        {
                    234:                SubLink    *sub = (SubLink *) node;
                    235: 
                    236:                /* Do what we came for */
                    237:                AcquireRewriteLocks((Query *) sub->subselect);
                    238:                /* Fall through to process lefthand args of SubLink */
                    239:        }
                    240: 
                    241:        /*
                    242:         * Do NOT recurse into Query nodes, because AcquireRewriteLocks already
                    243:         * processed subselects of subselects for us.
                    244:         */
                    245:        return expression_tree_walker(node, acquireLocksOnSubLinks, context);
                    246: }
                    247: 
                    248: 
                    249: /*
1.95      tgl       250:  * rewriteRuleAction -
                    251:  *       Rewrite the rule action with appropriate qualifiers (taken from
                    252:  *       the triggering query).
1.1       scrappy   253:  */
1.95      tgl       254: static Query *
                    255: rewriteRuleAction(Query *parsetree,
1.6       momjian   256:                                  Query *rule_action,
                    257:                                  Node *rule_qual,
1.4       momjian   258:                                  int rt_index,
1.95      tgl       259:                                  CmdType event)
1.4       momjian   260: {
1.95      tgl       261:        int                     current_varno,
                    262:                                new_varno;
                    263:        int                     rt_length;
1.84      tgl       264:        Query      *sub_action;
                    265:        Query     **sub_action_ptr;
1.4       momjian   266: 
1.95      tgl       267:        /*
                    268:         * Make modifiable copies of rule action and qual (what we're passed
                    269:         * are the stored versions in the relcache; don't touch 'em!).
                    270:         */
                    271:        rule_action = (Query *) copyObject(rule_action);
                    272:        rule_qual = (Node *) copyObject(rule_qual);
                    273: 
1.153     tgl       274:        /*
                    275:         * Acquire necessary locks and fix any deleted JOIN RTE entries.
                    276:         */
                    277:        AcquireRewriteLocks(rule_action);
                    278:        (void) acquireLocksOnSubLinks(rule_qual, NULL);
                    279: 
1.95      tgl       280:        current_varno = rt_index;
1.138     neilc     281:        rt_length = list_length(parsetree->rtable);
1.95      tgl       282:        new_varno = PRS2_NEW_VARNO + rt_length;
1.84      tgl       283: 
                    284:        /*
                    285:         * Adjust rule action and qual to offset its varnos, so that we can
1.94      tgl       286:         * merge its rtable with the main parsetree's rtable.
1.84      tgl       287:         *
1.90      momjian   288:         * If the rule action is an INSERT...SELECT, the OLD/NEW rtable entries
                    289:         * will be in the SELECT part, and we have to modify that rather than
                    290:         * the top-level INSERT (kluge!).
1.84      tgl       291:         */
1.95      tgl       292:        sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr);
1.84      tgl       293: 
                    294:        OffsetVarNodes((Node *) sub_action, rt_length, 0);
1.95      tgl       295:        OffsetVarNodes(rule_qual, rt_length, 0);
1.84      tgl       296:        /* but references to *OLD* should point at original rt_index */
                    297:        ChangeVarNodes((Node *) sub_action,
                    298:                                   PRS2_OLD_VARNO + rt_length, rt_index, 0);
1.95      tgl       299:        ChangeVarNodes(rule_qual,
1.84      tgl       300:                                   PRS2_OLD_VARNO + rt_length, rt_index, 0);
                    301: 
                    302:        /*
1.98      momjian   303:         * Generate expanded rtable consisting of main parsetree's rtable plus
                    304:         * rule action's rtable; this becomes the complete rtable for the rule
                    305:         * action.      Some of the entries may be unused after we finish
1.132     tgl       306:         * rewriting, but we leave them all in place for two reasons:
                    307:         *
1.144     momjian   308:         * We'd have a much harder job to adjust the query's varnos if we
                    309:         * selectively removed RT entries.
1.132     tgl       310:         *
1.144     momjian   311:         * If the rule is INSTEAD, then the original query won't be executed at
                    312:         * all, and so its rtable must be preserved so that the executor will
                    313:         * do the correct permissions checks on it.
1.132     tgl       314:         *
                    315:         * RT entries that are not referenced in the completed jointree will be
                    316:         * ignored by the planner, so they do not affect query semantics.  But
                    317:         * any permissions checks specified in them will be applied during
1.144     momjian   318:         * executor startup (see ExecCheckRTEPerms()).  This allows us to
                    319:         * check that the caller has, say, insert-permission on a view, when
                    320:         * the view is not semantically referenced at all in the resulting
                    321:         * query.
1.132     tgl       322:         *
                    323:         * When a rule is not INSTEAD, the permissions checks done on its copied
1.144     momjian   324:         * RT entries will be redundant with those done during execution of
                    325:         * the original query, but we don't bother to treat that case
                    326:         * differently.
1.84      tgl       327:         *
1.94      tgl       328:         * NOTE: because planner will destructively alter rtable, we must ensure
1.98      momjian   329:         * that rule action's rtable is separate and shares no substructure
                    330:         * with the main rtable.  Hence do a deep copy here.
1.94      tgl       331:         */
1.138     neilc     332:        sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),
1.144     momjian   333:                                                                         sub_action->rtable);
1.84      tgl       334: 
                    335:        /*
                    336:         * Each rule action's jointree should be the main parsetree's jointree
1.90      momjian   337:         * plus that rule's jointree, but usually *without* the original
                    338:         * rtindex that we're replacing (if present, which it won't be for
                    339:         * INSERT). Note that if the rule action refers to OLD, its jointree
                    340:         * will add a reference to rt_index.  If the rule action doesn't refer
                    341:         * to OLD, but either the rule_qual or the user query quals do, then
                    342:         * we need to keep the original rtindex in the jointree to provide
                    343:         * data for the quals.  We don't want the original rtindex to be
                    344:         * joined twice, however, so avoid keeping it if the rule action
                    345:         * mentions it.
1.94      tgl       346:         *
1.98      momjian   347:         * As above, the action's jointree must not share substructure with the
                    348:         * main parsetree's.
1.84      tgl       349:         */
1.123     tgl       350:        if (sub_action->commandType != CMD_UTILITY)
1.84      tgl       351:        {
1.90      momjian   352:                bool            keeporig;
                    353:                List       *newjointree;
1.84      tgl       354: 
1.123     tgl       355:                Assert(sub_action->jointree != NULL);
1.90      momjian   356:                keeporig = (!rangeTableEntry_used((Node *) sub_action->jointree,
                    357:                                                                                  rt_index, 0)) &&
1.95      tgl       358:                        (rangeTableEntry_used(rule_qual, rt_index, 0) ||
1.98      momjian   359:                  rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0));
1.89      tgl       360:                newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index);
1.123     tgl       361:                if (newjointree != NIL)
                    362:                {
                    363:                        /*
                    364:                         * If sub_action is a setop, manipulating its jointree will do
                    365:                         * no good at all, because the jointree is dummy.  (Perhaps
                    366:                         * someday we could push the joining and quals down to the
                    367:                         * member statements of the setop?)
                    368:                         */
                    369:                        if (sub_action->setOperations != NULL)
1.124     tgl       370:                                ereport(ERROR,
                    371:                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    372:                                                 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1.123     tgl       373: 
                    374:                        sub_action->jointree->fromlist =
1.138     neilc     375:                                list_concat(newjointree, sub_action->jointree->fromlist);
1.123     tgl       376:                }
1.84      tgl       377:        }
                    378: 
                    379:        /*
1.90      momjian   380:         * Event Qualification forces copying of parsetree and splitting into
                    381:         * two queries one w/rule_qual, one w/NOT rule_qual. Also add user
                    382:         * query qual onto rule action
1.84      tgl       383:         */
1.95      tgl       384:        AddQual(sub_action, rule_qual);
1.84      tgl       385: 
                    386:        AddQual(sub_action, parsetree->jointree->quals);
1.4       momjian   387: 
1.84      tgl       388:        /*
1.90      momjian   389:         * Rewrite new.attribute w/ right hand side of target-list entry for
                    390:         * appropriate field name in insert/update.
1.84      tgl       391:         *
                    392:         * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
                    393:         * apply it to sub_action; we have to remember to update the sublink
1.95      tgl       394:         * inside rule_action, too.
1.84      tgl       395:         */
1.141     tgl       396:        if ((event == CMD_INSERT || event == CMD_UPDATE) &&
                    397:                sub_action->commandType != CMD_UTILITY)
1.84      tgl       398:        {
                    399:                sub_action = (Query *) ResolveNew((Node *) sub_action,
1.95      tgl       400:                                                                                  new_varno,
1.84      tgl       401:                                                                                  0,
1.154     tgl       402:                                                                                  rt_fetch(new_varno,
                    403:                                                                                                   sub_action->rtable),
1.84      tgl       404:                                                                                  parsetree->targetList,
1.95      tgl       405:                                                                                  event,
                    406:                                                                                  current_varno);
1.84      tgl       407:                if (sub_action_ptr)
                    408:                        *sub_action_ptr = sub_action;
                    409:                else
1.95      tgl       410:                        rule_action = sub_action;
1.4       momjian   411:        }
1.84      tgl       412: 
1.95      tgl       413:        return rule_action;
1.4       momjian   414: }
                    415: 
1.22      momjian   416: /*
1.89      tgl       417:  * Copy the query's jointree list, and optionally attempt to remove any
                    418:  * occurrence of the given rt_index as a top-level join item (we do not look
                    419:  * for it within join items; this is OK because we are only expecting to find
                    420:  * it as an UPDATE or DELETE target relation, which will be at the top level
1.94      tgl       421:  * of the join).  Returns modified jointree list --- this is a separate copy
                    422:  * sharing no nodes with the original.
1.22      momjian   423:  */
1.80      tgl       424: static List *
1.89      tgl       425: adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
1.71      momjian   426: {
1.94      tgl       427:        List       *newjointree = copyObject(parsetree->jointree->fromlist);
1.136     neilc     428:        ListCell   *l;
1.58      tgl       429: 
1.89      tgl       430:        if (removert)
1.58      tgl       431:        {
1.136     neilc     432:                foreach(l, newjointree)
1.89      tgl       433:                {
1.136     neilc     434:                        RangeTblRef *rtr = lfirst(l);
1.22      momjian   435: 
1.115     tgl       436:                        if (IsA(rtr, RangeTblRef) &&
                    437:                                rtr->rtindex == rt_index)
1.89      tgl       438:                        {
1.138     neilc     439:                                newjointree = list_delete_ptr(newjointree, rtr);
1.144     momjian   440: 
                    441:                                /*
                    442:                                 * foreach is safe because we exit loop after
                    443:                                 * list_delete...
                    444:                                 */
1.89      tgl       445:                                break;
                    446:                        }
1.80      tgl       447:                }
1.58      tgl       448:        }
1.80      tgl       449:        return newjointree;
1.22      momjian   450: }
1.20      momjian   451: 
1.4       momjian   452: 
1.22      momjian   453: /*
1.101     tgl       454:  * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form
                    455:  *
                    456:  * This has the following responsibilities:
                    457:  *
                    458:  * 1. For an INSERT, add tlist entries to compute default values for any
                    459:  * attributes that have defaults and are not assigned to in the given tlist.
                    460:  * (We do not insert anything for default-less attributes, however.  The
                    461:  * planner will later insert NULLs for them, but there's no reason to slow
1.122     tgl       462:  * down rewriter processing with extra tlist nodes.)  Also, for both INSERT
                    463:  * and UPDATE, replace explicit DEFAULT specifications with column default
                    464:  * expressions.
1.101     tgl       465:  *
                    466:  * 2. Merge multiple entries for the same target attribute, or declare error
1.139     tgl       467:  * if we can't.  Multiple entries are only allowed for INSERT/UPDATE of
                    468:  * portions of an array or record field, for example
                    469:  *                     UPDATE table SET foo[2] = 42, foo[4] = 43;
1.101     tgl       470:  * We can merge such operations into a single assignment op.  Essentially,
                    471:  * the expression we want to produce in this case is like
                    472:  *             foo = array_set(array_set(foo, 2, 42), 4, 43)
                    473:  *
                    474:  * 3. Sort the tlist into standard order: non-junk fields in order by resno,
                    475:  * then junk fields (these in no particular order).
                    476:  *
                    477:  * We must do items 1 and 2 before firing rewrite rules, else rewritten
1.108     momjian   478:  * references to NEW.foo will produce wrong or incomplete results.     Item 3
1.101     tgl       479:  * is not needed for rewriting, but will be needed by the planner, and we
                    480:  * can do it essentially for free while handling items 1 and 2.
                    481:  */
                    482: static void
                    483: rewriteTargetList(Query *parsetree, Relation target_relation)
                    484: {
                    485:        CmdType         commandType = parsetree->commandType;
1.149     tgl       486:        TargetEntry **new_tles;
1.101     tgl       487:        List       *new_tlist = NIL;
1.149     tgl       488:        List       *junk_tlist = NIL;
                    489:        Form_pg_attribute att_tup;
1.101     tgl       490:        int                     attrno,
1.149     tgl       491:                                next_junk_attrno,
1.101     tgl       492:                                numattrs;
1.136     neilc     493:        ListCell   *temp;
1.101     tgl       494: 
                    495:        /*
1.149     tgl       496:         * We process the normal (non-junk) attributes by scanning the input
                    497:         * tlist once and transferring TLEs into an array, then scanning the
                    498:         * array to build an output tlist.  This avoids O(N^2) behavior for
                    499:         * large numbers of attributes.
                    500:         *
                    501:         * Junk attributes are tossed into a separate list during the same
                    502:         * tlist scan, then appended to the reconstructed tlist.
1.101     tgl       503:         */
                    504:        numattrs = RelationGetNumberOfAttributes(target_relation);
1.149     tgl       505:        new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *));
                    506:        next_junk_attrno = numattrs + 1;
1.101     tgl       507: 
1.149     tgl       508:        foreach(temp, parsetree->targetList)
1.101     tgl       509:        {
1.149     tgl       510:                TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
                    511: 
1.150     tgl       512:                if (!old_tle->resjunk)
1.149     tgl       513:                {
                    514:                        /* Normal attr: stash it into new_tles[] */
1.150     tgl       515:                        attrno = old_tle->resno;
1.149     tgl       516:                        if (attrno < 1 || attrno > numattrs)
                    517:                                elog(ERROR, "bogus resno %d in targetlist", attrno);
                    518:                        att_tup = target_relation->rd_att->attrs[attrno - 1];
1.105     tgl       519: 
1.149     tgl       520:                        /* We can (and must) ignore deleted attributes */
                    521:                        if (att_tup->attisdropped)
                    522:                                continue;
1.101     tgl       523: 
1.149     tgl       524:                        /* Merge with any prior assignment to same attribute */
                    525:                        new_tles[attrno - 1] =
                    526:                                process_matched_tle(old_tle,
                    527:                                                                        new_tles[attrno - 1],
                    528:                                                                        NameStr(att_tup->attname));
                    529:                }
                    530:                else
1.101     tgl       531:                {
1.149     tgl       532:                        /*
                    533:                         * Copy all resjunk tlist entries to junk_tlist, and
                    534:                         * assign them resnos above the last real resno.
                    535:                         *
                    536:                         * Typical junk entries include ORDER BY or GROUP BY expressions
                    537:                         * (are these actually possible in an INSERT or UPDATE?), system
                    538:                         * attribute references, etc.
                    539:                         */
1.101     tgl       540: 
1.149     tgl       541:                        /* Get the resno right, but don't copy unnecessarily */
1.150     tgl       542:                        if (old_tle->resno != next_junk_attrno)
1.101     tgl       543:                        {
1.150     tgl       544:                                old_tle = flatCopyTargetEntry(old_tle);
                    545:                                old_tle->resno = next_junk_attrno;
1.101     tgl       546:                        }
1.149     tgl       547:                        junk_tlist = lappend(junk_tlist, old_tle);
                    548:                        next_junk_attrno++;
1.101     tgl       549:                }
1.149     tgl       550:        }
                    551: 
                    552:        for (attrno = 1; attrno <= numattrs; attrno++)
                    553:        {
                    554:                TargetEntry *new_tle = new_tles[attrno - 1];
                    555: 
                    556:                att_tup = target_relation->rd_att->attrs[attrno - 1];
                    557: 
                    558:                /* We can (and must) ignore deleted attributes */
                    559:                if (att_tup->attisdropped)
                    560:                        continue;
1.101     tgl       561: 
1.122     tgl       562:                /*
1.126     momjian   563:                 * Handle the two cases where we need to insert a default
                    564:                 * expression: it's an INSERT and there's no tlist entry for the
                    565:                 * column, or the tlist entry is a DEFAULT placeholder node.
1.122     tgl       566:                 */
                    567:                if ((new_tle == NULL && commandType == CMD_INSERT) ||
1.149     tgl       568:                        (new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
1.101     tgl       569:                {
                    570:                        Node       *new_expr;
                    571: 
                    572:                        new_expr = build_column_default(target_relation, attrno);
1.122     tgl       573: 
                    574:                        /*
                    575:                         * If there is no default (ie, default is effectively NULL),
                    576:                         * we can omit the tlist entry in the INSERT case, since the
                    577:                         * planner can insert a NULL for itself, and there's no point
1.126     momjian   578:                         * in spending any more rewriter cycles on the entry.  But in
                    579:                         * the UPDATE case we've got to explicitly set the column to
                    580:                         * NULL.
1.122     tgl       581:                         */
                    582:                        if (!new_expr)
                    583:                        {
                    584:                                if (commandType == CMD_INSERT)
                    585:                                        new_tle = NULL;
                    586:                                else
                    587:                                {
                    588:                                        new_expr = (Node *) makeConst(att_tup->atttypid,
                    589:                                                                                                  att_tup->attlen,
                    590:                                                                                                  (Datum) 0,
                    591:                                                                                                  true, /* isnull */
                    592:                                                                                                  att_tup->attbyval);
                    593:                                        /* this is to catch a NOT NULL domain constraint */
                    594:                                        new_expr = coerce_to_domain(new_expr,
                    595:                                                                                                InvalidOid,
                    596:                                                                                                att_tup->atttypid,
1.140     tgl       597:                                                                                                COERCE_IMPLICIT_CAST,
1.145     tgl       598:                                                                                                false,
1.140     tgl       599:                                                                                                false);
1.122     tgl       600:                                }
                    601:                        }
1.101     tgl       602: 
                    603:                        if (new_expr)
1.150     tgl       604:                                new_tle = makeTargetEntry((Expr *) new_expr,
                    605:                                                                                  attrno,
                    606:                                                                                  pstrdup(NameStr(att_tup->attname)),
                    607:                                                                                  false);
1.101     tgl       608:                }
                    609: 
                    610:                if (new_tle)
                    611:                        new_tlist = lappend(new_tlist, new_tle);
                    612:        }
                    613: 
1.149     tgl       614:        pfree(new_tles);
1.101     tgl       615: 
1.149     tgl       616:        parsetree->targetList = list_concat(new_tlist, junk_tlist);
1.101     tgl       617: }
                    618: 
                    619: 
                    620: /*
                    621:  * Convert a matched TLE from the original tlist into a correct new TLE.
                    622:  *
                    623:  * This routine detects and handles multiple assignments to the same target
1.129     tgl       624:  * attribute.  (The attribute name is needed only for error messages.)
1.101     tgl       625:  */
                    626: static TargetEntry *
                    627: process_matched_tle(TargetEntry *src_tle,
1.129     tgl       628:                                        TargetEntry *prior_tle,
                    629:                                        const char *attrName)
1.101     tgl       630: {
1.150     tgl       631:        TargetEntry *result;
1.139     tgl       632:        Node       *src_expr;
                    633:        Node       *prior_expr;
                    634:        Node       *src_input;
                    635:        Node       *prior_input;
1.101     tgl       636:        Node       *priorbottom;
1.139     tgl       637:        Node       *newexpr;
1.101     tgl       638: 
                    639:        if (prior_tle == NULL)
                    640:        {
                    641:                /*
                    642:                 * Normal case where this is the first assignment to the
                    643:                 * attribute.
                    644:                 */
                    645:                return src_tle;
                    646:        }
                    647: 
1.139     tgl       648:        /*----------
1.101     tgl       649:         * Multiple assignments to same attribute.      Allow only if all are
1.139     tgl       650:         * FieldStore or ArrayRef assignment operations.  This is a bit
                    651:         * tricky because what we may actually be looking at is a nest of
                    652:         * such nodes; consider
                    653:         *              UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
                    654:         * The two expressions produced by the parser will look like
                    655:         *              FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
                    656:         *              FieldStore(col, fld2, FieldStore(placeholder, subfld2, x))
                    657:         * However, we can ignore the substructure and just consider the top
                    658:         * FieldStore or ArrayRef from each assignment, because it works to
                    659:         * combine these as
                    660:         *              FieldStore(FieldStore(col, fld1,
                    661:         *                                                        FieldStore(placeholder, subfld1, x)),
                    662:         *                                 fld2, FieldStore(placeholder, subfld2, x))
                    663:         * Note the leftmost expression goes on the inside so that the
                    664:         * assignments appear to occur left-to-right.
                    665:         *
                    666:         * For FieldStore, instead of nesting we can generate a single
1.144     momjian   667:         * FieldStore with multiple target fields.      We must nest when
1.139     tgl       668:         * ArrayRefs are involved though.
                    669:         *----------
                    670:         */
                    671:        src_expr = (Node *) src_tle->expr;
                    672:        prior_expr = (Node *) prior_tle->expr;
                    673:        src_input = get_assignment_input(src_expr);
                    674:        prior_input = get_assignment_input(prior_expr);
                    675:        if (src_input == NULL ||
                    676:                prior_input == NULL ||
                    677:                exprType(src_expr) != exprType(prior_expr))
1.124     tgl       678:                ereport(ERROR,
                    679:                                (errcode(ERRCODE_SYNTAX_ERROR),
1.130     petere    680:                                 errmsg("multiple assignments to same column \"%s\"",
1.129     tgl       681:                                                attrName)));
1.101     tgl       682: 
                    683:        /*
1.139     tgl       684:         * Prior TLE could be a nest of assignments if we do this more than
1.101     tgl       685:         * once.
                    686:         */
1.139     tgl       687:        priorbottom = prior_input;
                    688:        for (;;)
                    689:        {
1.144     momjian   690:                Node       *newbottom = get_assignment_input(priorbottom);
1.139     tgl       691: 
                    692:                if (newbottom == NULL)
                    693:                        break;                          /* found the original Var reference */
                    694:                priorbottom = newbottom;
                    695:        }
                    696:        if (!equal(priorbottom, src_input))
1.124     tgl       697:                ereport(ERROR,
                    698:                                (errcode(ERRCODE_SYNTAX_ERROR),
1.130     petere    699:                                 errmsg("multiple assignments to same column \"%s\"",
1.129     tgl       700:                                                attrName)));
1.101     tgl       701: 
                    702:        /*
                    703:         * Looks OK to nest 'em.
                    704:         */
1.139     tgl       705:        if (IsA(src_expr, FieldStore))
                    706:        {
1.144     momjian   707:                FieldStore *fstore = makeNode(FieldStore);
1.139     tgl       708: 
                    709:                if (IsA(prior_expr, FieldStore))
                    710:                {
                    711:                        /* combine the two */
                    712:                        memcpy(fstore, prior_expr, sizeof(FieldStore));
                    713:                        fstore->newvals =
                    714:                                list_concat(list_copy(((FieldStore *) prior_expr)->newvals),
1.144     momjian   715:                                                  list_copy(((FieldStore *) src_expr)->newvals));
1.139     tgl       716:                        fstore->fieldnums =
                    717:                                list_concat(list_copy(((FieldStore *) prior_expr)->fieldnums),
1.144     momjian   718:                                                list_copy(((FieldStore *) src_expr)->fieldnums));
1.139     tgl       719:                }
                    720:                else
                    721:                {
                    722:                        /* general case, just nest 'em */
                    723:                        memcpy(fstore, src_expr, sizeof(FieldStore));
                    724:                        fstore->arg = (Expr *) prior_expr;
                    725:                }
                    726:                newexpr = (Node *) fstore;
                    727:        }
                    728:        else if (IsA(src_expr, ArrayRef))
                    729:        {
                    730:                ArrayRef   *aref = makeNode(ArrayRef);
                    731: 
                    732:                memcpy(aref, src_expr, sizeof(ArrayRef));
                    733:                aref->refexpr = (Expr *) prior_expr;
                    734:                newexpr = (Node *) aref;
                    735:        }
                    736:        else
                    737:        {
                    738:                elog(ERROR, "can't happen");
                    739:                newexpr = NULL;
                    740:        }
1.101     tgl       741: 
1.150     tgl       742:        result = flatCopyTargetEntry(src_tle);
                    743:        result->expr = (Expr *) newexpr;
                    744:        return result;
1.101     tgl       745: }
                    746: 
1.139     tgl       747: /*
                    748:  * If node is an assignment node, return its input; else return NULL
                    749:  */
                    750: static Node *
                    751: get_assignment_input(Node *node)
                    752: {
                    753:        if (node == NULL)
                    754:                return NULL;
                    755:        if (IsA(node, FieldStore))
                    756:        {
                    757:                FieldStore *fstore = (FieldStore *) node;
                    758: 
                    759:                return (Node *) fstore->arg;
                    760:        }
                    761:        else if (IsA(node, ArrayRef))
                    762:        {
                    763:                ArrayRef   *aref = (ArrayRef *) node;
                    764: 
                    765:                if (aref->refassgnexpr == NULL)
                    766:                        return NULL;
                    767:                return (Node *) aref->refexpr;
                    768:        }
                    769:        return NULL;
                    770: }
1.101     tgl       771: 
                    772: /*
                    773:  * Make an expression tree for the default value for a column.
                    774:  *
                    775:  * If there is no default, return a NULL instead.
                    776:  */
1.104     momjian   777: Node *
1.101     tgl       778: build_column_default(Relation rel, int attrno)
                    779: {
                    780:        TupleDesc       rd_att = rel->rd_att;
                    781:        Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
                    782:        Oid                     atttype = att_tup->atttypid;
                    783:        int32           atttypmod = att_tup->atttypmod;
                    784:        Node       *expr = NULL;
                    785:        Oid                     exprtype;
                    786: 
                    787:        /*
                    788:         * Scan to see if relation has a default for this column.
                    789:         */
                    790:        if (rd_att->constr && rd_att->constr->num_defval > 0)
                    791:        {
                    792:                AttrDefault *defval = rd_att->constr->defval;
                    793:                int                     ndef = rd_att->constr->num_defval;
                    794: 
                    795:                while (--ndef >= 0)
                    796:                {
                    797:                        if (attrno == defval[ndef].adnum)
                    798:                        {
                    799:                                /*
                    800:                                 * Found it, convert string representation to node tree.
                    801:                                 */
                    802:                                expr = stringToNode(defval[ndef].adbin);
                    803:                                break;
                    804:                        }
                    805:                }
                    806:        }
                    807: 
                    808:        if (expr == NULL)
                    809:        {
                    810:                /*
1.108     momjian   811:                 * No per-column default, so look for a default for the type
                    812:                 * itself.
1.101     tgl       813:                 */
1.134     tgl       814:                expr = get_typdefault(atttype);
1.101     tgl       815:        }
                    816: 
                    817:        if (expr == NULL)
                    818:                return NULL;                    /* No default anywhere */
                    819: 
                    820:        /*
1.125     tgl       821:         * Make sure the value is coerced to the target column type; this will
                    822:         * generally be true already, but there seem to be some corner cases
1.126     momjian   823:         * involving domain defaults where it might not be true. This should
                    824:         * match the parser's processing of non-defaulted expressions --- see
                    825:         * updateTargetListEntry().
1.101     tgl       826:         */
                    827:        exprtype = exprType(expr);
                    828: 
1.126     momjian   829:        expr = coerce_to_target_type(NULL,      /* no UNKNOWN params here */
1.119     tgl       830:                                                                 expr, exprtype,
1.110     tgl       831:                                                                 atttype, atttypmod,
                    832:                                                                 COERCION_ASSIGNMENT,
                    833:                                                                 COERCE_IMPLICIT_CAST);
                    834:        if (expr == NULL)
1.124     tgl       835:                ereport(ERROR,
                    836:                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    837:                                 errmsg("column \"%s\" is of type %s"
                    838:                                                " but default expression is of type %s",
                    839:                                                NameStr(att_tup->attname),
                    840:                                                format_type_be(atttype),
                    841:                                                format_type_be(exprtype)),
1.126     momjian   842:                   errhint("You will need to rewrite or cast the expression.")));
1.101     tgl       843: 
                    844:        return expr;
                    845: }
                    846: 
                    847: 
                    848: /*
1.81      tgl       849:  * matchLocks -
                    850:  *       match the list of locks and returns the matching rules
1.22      momjian   851:  */
1.81      tgl       852: static List *
                    853: matchLocks(CmdType event,
                    854:                   RuleLock *rulelocks,
                    855:                   int varno,
                    856:                   Query *parsetree)
1.71      momjian   857: {
1.112     tgl       858:        List       *matching_locks = NIL;
1.81      tgl       859:        int                     nlocks;
                    860:        int                     i;
1.58      tgl       861: 
1.118     tgl       862:        if (rulelocks == NULL)
                    863:                return NIL;
1.22      momjian   864: 
1.81      tgl       865:        if (parsetree->commandType != CMD_SELECT)
1.58      tgl       866:        {
1.81      tgl       867:                if (parsetree->resultRelation != varno)
                    868:                        return NIL;
1.58      tgl       869:        }
1.22      momjian   870: 
1.81      tgl       871:        nlocks = rulelocks->numLocks;
1.22      momjian   872: 
1.81      tgl       873:        for (i = 0; i < nlocks; i++)
1.45      momjian   874:        {
1.81      tgl       875:                RewriteRule *oneLock = rulelocks->rules[i];
1.22      momjian   876: 
1.81      tgl       877:                if (oneLock->event == event)
1.63      tgl       878:                {
1.81      tgl       879:                        if (parsetree->commandType != CMD_SELECT ||
                    880:                                (oneLock->attrno == -1 ?
                    881:                                 rangeTableEntry_used((Node *) parsetree, varno, 0) :
                    882:                                 attribute_used((Node *) parsetree,
                    883:                                                                varno, oneLock->attrno, 0)))
1.112     tgl       884:                                matching_locks = lappend(matching_locks, oneLock);
1.63      tgl       885:                }
1.22      momjian   886:        }
1.45      momjian   887: 
1.112     tgl       888:        return matching_locks;
1.58      tgl       889: }
1.22      momjian   890: 
                    891: 
1.153     tgl       892: /*
                    893:  * ApplyRetrieveRule - expand an ON SELECT rule
                    894:  */
1.81      tgl       895: static Query *
                    896: ApplyRetrieveRule(Query *parsetree,
                    897:                                  RewriteRule *rule,
                    898:                                  int rt_index,
                    899:                                  bool relation_level,
                    900:                                  Relation relation,
1.118     tgl       901:                                  List *activeRIRs)
1.58      tgl       902: {
1.81      tgl       903:        Query      *rule_action;
                    904:        RangeTblEntry *rte,
                    905:                           *subrte;
1.22      momjian   906: 
1.138     neilc     907:        if (list_length(rule->actions) != 1)
1.124     tgl       908:                elog(ERROR, "expected just one rule action");
1.81      tgl       909:        if (rule->qual != NULL)
1.124     tgl       910:                elog(ERROR, "cannot handle qualified ON SELECT rule");
1.90      momjian   911:        if (!relation_level)
1.124     tgl       912:                elog(ERROR, "cannot handle per-attribute ON SELECT rule");
1.71      momjian   913: 
1.63      tgl       914:        /*
1.153     tgl       915:         * Make a modifiable copy of the view query, and acquire needed locks
                    916:         * on the relations it mentions.
1.58      tgl       917:         */
1.136     neilc     918:        rule_action = copyObject(linitial(rule->actions));
1.22      momjian   919: 
1.153     tgl       920:        AcquireRewriteLocks(rule_action);
                    921: 
                    922:        /*
                    923:         * Recursively expand any view references inside the view.
                    924:         */
1.118     tgl       925:        rule_action = fireRIRrules(rule_action, activeRIRs);
1.22      momjian   926: 
1.81      tgl       927:        /*
1.90      momjian   928:         * VIEWs are really easy --- just plug the view query in as a
                    929:         * subselect, replacing the relation's original RTE.
1.81      tgl       930:         */
                    931:        rte = rt_fetch(rt_index, parsetree->rtable);
1.58      tgl       932: 
1.99      tgl       933:        rte->rtekind = RTE_SUBQUERY;
1.81      tgl       934:        rte->relid = InvalidOid;
                    935:        rte->subquery = rule_action;
                    936:        rte->inh = false;                       /* must not be set for a subquery */
1.71      momjian   937: 
1.58      tgl       938:        /*
1.81      tgl       939:         * We move the view's permission check data down to its rangetable.
                    940:         * The checks will actually be done against the *OLD* entry therein.
1.58      tgl       941:         */
1.81      tgl       942:        subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
                    943:        Assert(subrte->relid == relation->rd_id);
1.133     tgl       944:        subrte->requiredPerms = rte->requiredPerms;
1.93      tgl       945:        subrte->checkAsUser = rte->checkAsUser;
1.27      thomas    946: 
1.133     tgl       947:        rte->requiredPerms = 0;         /* no permission check on subquery itself */
1.155   ! tgl       948:        rte->checkAsUser = InvalidOid;
1.60      tgl       949: 
1.71      momjian   950:        /*
1.151     tgl       951:         * FOR UPDATE/SHARE of view?
1.60      tgl       952:         */
1.138     neilc     953:        if (list_member_int(parsetree->rowMarks, rt_index))
1.22      momjian   954:        {
1.45      momjian   955:                /*
1.81      tgl       956:                 * Remove the view from the list of rels that will actually be
1.151     tgl       957:                 * marked FOR UPDATE/SHARE by the executor.  It will still be access-
1.81      tgl       958:                 * checked for write access, though.
1.29      vadim     959:                 */
1.138     neilc     960:                parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index);
1.58      tgl       961: 
1.81      tgl       962:                /*
1.151     tgl       963:                 * Set up the view's referenced tables as if FOR UPDATE/SHARE.
1.81      tgl       964:                 */
1.151     tgl       965:                markQueryForLocking(rule_action, parsetree->forUpdate, true);
1.86      tgl       966:        }
                    967: 
                    968:        return parsetree;
                    969: }
                    970: 
                    971: /*
1.151     tgl       972:  * Recursively mark all relations used by a view as FOR UPDATE/SHARE.
1.86      tgl       973:  *
                    974:  * This may generate an invalid query, eg if some sub-query uses an
                    975:  * aggregate.  We leave it to the planner to detect that.
                    976:  *
1.151     tgl       977:  * NB: this must agree with the parser's transformLocking() routine.
1.86      tgl       978:  */
                    979: static void
1.151     tgl       980: markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew)
1.86      tgl       981: {
                    982:        Index           rti = 0;
1.136     neilc     983:        ListCell   *l;
1.86      tgl       984: 
1.151     tgl       985:        if (qry->rowMarks && forUpdate != qry->forUpdate)
                    986:                ereport(ERROR,
                    987:                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    988:                                 errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
                    989:        qry->forUpdate = forUpdate;
                    990: 
1.86      tgl       991:        foreach(l, qry->rtable)
                    992:        {
                    993:                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
                    994: 
                    995:                rti++;
                    996: 
                    997:                /* Ignore OLD and NEW entries if we are at top level of view */
                    998:                if (skipOldNew &&
                    999:                        (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
                   1000:                        continue;
                   1001: 
1.99      tgl      1002:                if (rte->rtekind == RTE_RELATION)
1.86      tgl      1003:                {
1.138     neilc    1004:                        if (!list_member_int(qry->rowMarks, rti))
                   1005:                                qry->rowMarks = lappend_int(qry->rowMarks, rti);
1.133     tgl      1006:                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
1.29      vadim    1007:                }
1.99      tgl      1008:                else if (rte->rtekind == RTE_SUBQUERY)
                   1009:                {
1.151     tgl      1010:                        /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
                   1011:                        markQueryForLocking(rte->subquery, forUpdate, false);
1.99      tgl      1012:                }
1.29      vadim    1013:        }
1.22      momjian  1014: }
                   1015: 
                   1016: 
1.58      tgl      1017: /*
1.81      tgl      1018:  * fireRIRonSubLink -
                   1019:  *     Apply fireRIRrules() to each SubLink (subselect in expression) found
                   1020:  *     in the given tree.
1.58      tgl      1021:  *
                   1022:  * NOTE: although this has the form of a walker, we cheat and modify the
1.71      momjian  1023:  * SubLink nodes in-place.     It is caller's responsibility to ensure that
1.58      tgl      1024:  * no unwanted side-effects occur!
1.80      tgl      1025:  *
                   1026:  * This is unlike most of the other routines that recurse into subselects,
                   1027:  * because we must take control at the SubLink node in order to replace
                   1028:  * the SubLink's subselect link with the possibly-rewritten subquery.
1.58      tgl      1029:  */
                   1030: static bool
1.118     tgl      1031: fireRIRonSubLink(Node *node, List *activeRIRs)
1.22      momjian  1032: {
                   1033:        if (node == NULL)
1.58      tgl      1034:                return false;
                   1035:        if (IsA(node, SubLink))
                   1036:        {
                   1037:                SubLink    *sub = (SubLink *) node;
1.22      momjian  1038: 
1.58      tgl      1039:                /* Do what we came for */
1.118     tgl      1040:                sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,
                   1041:                                                                                           activeRIRs);
1.80      tgl      1042:                /* Fall through to process lefthand args of SubLink */
1.22      momjian  1043:        }
1.90      momjian  1044: 
1.80      tgl      1045:        /*
                   1046:         * Do NOT recurse into Query nodes, because fireRIRrules already
1.81      tgl      1047:         * processed subselects of subselects for us.
1.80      tgl      1048:         */
1.81      tgl      1049:        return expression_tree_walker(node, fireRIRonSubLink,
1.118     tgl      1050:                                                                  (void *) activeRIRs);
1.22      momjian  1051: }
                   1052: 
                   1053: 
                   1054: /*
                   1055:  * fireRIRrules -
                   1056:  *     Apply all RIR rules on each rangetable entry in a query
                   1057:  */
                   1058: static Query *
1.118     tgl      1059: fireRIRrules(Query *parsetree, List *activeRIRs)
1.22      momjian  1060: {
1.45      momjian  1061:        int                     rt_index;
1.22      momjian  1062: 
1.71      momjian  1063:        /*
                   1064:         * don't try to convert this into a foreach loop, because rtable list
                   1065:         * can get changed each time through...
1.60      tgl      1066:         */
1.22      momjian  1067:        rt_index = 0;
1.138     neilc    1068:        while (rt_index < list_length(parsetree->rtable))
1.45      momjian  1069:        {
1.83      tgl      1070:                RangeTblEntry *rte;
                   1071:                Relation        rel;
                   1072:                List       *locks;
                   1073:                RuleLock   *rules;
                   1074:                RewriteRule *rule;
                   1075:                int                     i;
                   1076: 
1.22      momjian  1077:                ++rt_index;
                   1078: 
1.62      tgl      1079:                rte = rt_fetch(rt_index, parsetree->rtable);
1.44      wieck    1080: 
1.60      tgl      1081:                /*
1.81      tgl      1082:                 * A subquery RTE can't have associated rules, so there's nothing
                   1083:                 * to do to this level of the query, but we must recurse into the
                   1084:                 * subquery to expand any rule references in it.
                   1085:                 */
1.99      tgl      1086:                if (rte->rtekind == RTE_SUBQUERY)
1.81      tgl      1087:                {
1.118     tgl      1088:                        rte->subquery = fireRIRrules(rte->subquery, activeRIRs);
1.81      tgl      1089:                        continue;
                   1090:                }
                   1091: 
                   1092:                /*
1.99      tgl      1093:                 * Joins and other non-relation RTEs can be ignored completely.
                   1094:                 */
                   1095:                if (rte->rtekind != RTE_RELATION)
                   1096:                        continue;
                   1097: 
                   1098:                /*
1.72      tgl      1099:                 * If the table is not referenced in the query, then we ignore it.
                   1100:                 * This prevents infinite expansion loop due to new rtable entries
                   1101:                 * inserted by expansion of a rule. A table is referenced if it is
1.80      tgl      1102:                 * part of the join set (a source table), or is referenced by any
                   1103:                 * Var nodes, or is the result table.
1.60      tgl      1104:                 */
1.152     tgl      1105:                if (rt_index != parsetree->resultRelation &&
                   1106:                        !rangeTableEntry_used((Node *) parsetree, rt_index, 0))
1.22      momjian  1107:                        continue;
1.45      momjian  1108: 
1.83      tgl      1109:                /*
1.153     tgl      1110:                 * We can use NoLock here since either the parser or
                   1111:                 * AcquireRewriteLocks should have locked the rel already.
1.83      tgl      1112:                 */
1.153     tgl      1113:                rel = heap_open(rte->relid, NoLock);
1.92      tgl      1114: 
                   1115:                /*
                   1116:                 * Collect the RIR rules that we must apply
                   1117:                 */
1.60      tgl      1118:                rules = rel->rd_rules;
                   1119:                if (rules == NULL)
1.45      momjian  1120:                {
1.83      tgl      1121:                        heap_close(rel, NoLock);
1.22      momjian  1122:                        continue;
                   1123:                }
1.60      tgl      1124:                locks = NIL;
1.45      momjian  1125:                for (i = 0; i < rules->numLocks; i++)
                   1126:                {
1.22      momjian  1127:                        rule = rules->rules[i];
                   1128:                        if (rule->event != CMD_SELECT)
                   1129:                                continue;
1.45      momjian  1130: 
1.60      tgl      1131:                        if (rule->attrno > 0)
                   1132:                        {
                   1133:                                /* per-attr rule; do we need it? */
1.80      tgl      1134:                                if (!attribute_used((Node *) parsetree, rt_index,
1.71      momjian  1135:                                                                        rule->attrno, 0))
1.60      tgl      1136:                                        continue;
                   1137:                        }
1.22      momjian  1138: 
                   1139:                        locks = lappend(locks, rule);
                   1140:                }
                   1141: 
                   1142:                /*
1.118     tgl      1143:                 * If we found any, apply them --- but first check for recursion!
1.22      momjian  1144:                 */
1.118     tgl      1145:                if (locks != NIL)
1.45      momjian  1146:                {
1.136     neilc    1147:                        ListCell   *l;
1.118     tgl      1148: 
1.138     neilc    1149:                        if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
1.124     tgl      1150:                                ereport(ERROR,
                   1151:                                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                   1152:                                                 errmsg("infinite recursion detected in rules for relation \"%s\"",
                   1153:                                                                RelationGetRelationName(rel))));
1.138     neilc    1154:                        activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
1.118     tgl      1155: 
                   1156:                        foreach(l, locks)
                   1157:                        {
                   1158:                                rule = lfirst(l);
1.22      momjian  1159: 
1.118     tgl      1160:                                parsetree = ApplyRetrieveRule(parsetree,
                   1161:                                                                                          rule,
                   1162:                                                                                          rt_index,
                   1163:                                                                                          rule->attrno == -1,
                   1164:                                                                                          rel,
1.137     tgl      1165:                                                                                          activeRIRs);
1.118     tgl      1166:                        }
1.137     tgl      1167: 
                   1168:                        activeRIRs = list_delete_first(activeRIRs);
1.4       momjian  1169:                }
1.22      momjian  1170: 
1.83      tgl      1171:                heap_close(rel, NoLock);
1.22      momjian  1172:        }
                   1173: 
1.81      tgl      1174:        /*
1.109     tgl      1175:         * Recurse into sublink subqueries, too.  But we already did the ones
                   1176:         * in the rtable.
1.81      tgl      1177:         */
                   1178:        if (parsetree->hasSubLinks)
1.118     tgl      1179:                query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
1.116     tgl      1180:                                                  QTW_IGNORE_RT_SUBQUERIES);
1.81      tgl      1181: 
1.22      momjian  1182:        return parsetree;
                   1183: }
                   1184: 
                   1185: 
                   1186: /*
1.113     tgl      1187:  * Modify the given query by adding 'AND rule_qual IS NOT TRUE' to its
                   1188:  * qualification.  This is used to generate suitable "else clauses" for
                   1189:  * conditional INSTEAD rules.  (Unfortunately we must use "x IS NOT TRUE",
                   1190:  * not just "NOT x" which the planner is much smarter about, else we will
                   1191:  * do the wrong thing when the qual evaluates to NULL.)
1.84      tgl      1192:  *
1.90      momjian  1193:  * The rule_qual may contain references to OLD or NEW. OLD references are
1.84      tgl      1194:  * replaced by references to the specified rt_index (the relation that the
                   1195:  * rule applies to).  NEW references are only possible for INSERT and UPDATE
                   1196:  * queries on the relation itself, and so they should be replaced by copies
                   1197:  * of the related entries in the query's own targetlist.
                   1198:  */
1.5       momjian  1199: static Query *
1.113     tgl      1200: CopyAndAddInvertedQual(Query *parsetree,
                   1201:                                           Node *rule_qual,
                   1202:                                           int rt_index,
                   1203:                                           CmdType event)
1.4       momjian  1204: {
1.153     tgl      1205:        /* Don't scribble on the passed qual (it's in the relcache!) */
1.84      tgl      1206:        Node       *new_qual = (Node *) copyObject(rule_qual);
1.4       momjian  1207: 
1.153     tgl      1208:        /*
                   1209:         * In case there are subqueries in the qual, acquire necessary locks and
                   1210:         * fix any deleted JOIN RTE entries.  (This is somewhat redundant with
                   1211:         * rewriteRuleAction, but not entirely ... consider restructuring so
                   1212:         * that we only need to process the qual this way once.)
                   1213:         */
                   1214:        (void) acquireLocksOnSubLinks(new_qual, NULL);
                   1215: 
1.84      tgl      1216:        /* Fix references to OLD */
                   1217:        ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
                   1218:        /* Fix references to NEW */
                   1219:        if (event == CMD_INSERT || event == CMD_UPDATE)
                   1220:                new_qual = ResolveNew(new_qual,
                   1221:                                                          PRS2_NEW_VARNO,
                   1222:                                                          0,
1.154     tgl      1223:                                                          rt_fetch(rt_index, parsetree->rtable),
1.84      tgl      1224:                                                          parsetree->targetList,
                   1225:                                                          event,
                   1226:                                                          rt_index);
                   1227:        /* And attach the fixed qual */
1.153     tgl      1228:        AddInvertedQual(parsetree, new_qual);
1.4       momjian  1229: 
1.153     tgl      1230:        return parsetree;
1.1       scrappy  1231: }
                   1232: 
                   1233: 
                   1234: /*
1.4       momjian  1235:  *     fireRules -
1.18      scrappy  1236:  *        Iterate through rule locks applying rules.
1.21      momjian  1237:  *
1.112     tgl      1238:  * Input arguments:
                   1239:  *     parsetree - original query
                   1240:  *     rt_index - RT index of result relation in original query
                   1241:  *     event - type of rule event
                   1242:  *     locks - list of rules to fire
                   1243:  * Output arguments:
                   1244:  *     *instead_flag - set TRUE if any unqualified INSTEAD rule is found
                   1245:  *                                     (must be initialized to FALSE)
                   1246:  *     *qual_product - filled with modified original query if any qualified
                   1247:  *                                     INSTEAD rule is found (must be initialized to NULL)
                   1248:  * Return value:
                   1249:  *     list of rule actions adjusted for use with this query
1.1       scrappy  1250:  *
1.112     tgl      1251:  * Qualified INSTEAD rules generate their action with the qualification
                   1252:  * condition added.  They also generate a modified version of the original
                   1253:  * query with the negated qualification added, so that it will run only for
                   1254:  * rows that the qualified action doesn't act on.  (If there are multiple
                   1255:  * qualified INSTEAD rules, we AND all the negated quals onto a single
                   1256:  * modified original query.)  We won't execute the original, unmodified
1.126     momjian  1257:  * query if we find either qualified or unqualified INSTEAD rules.     If
1.112     tgl      1258:  * we find both, the modified original query is discarded too.
1.1       scrappy  1259:  */
1.5       momjian  1260: static List *
1.6       momjian  1261: fireRules(Query *parsetree,
1.4       momjian  1262:                  int rt_index,
                   1263:                  CmdType event,
1.112     tgl      1264:                  List *locks,
1.6       momjian  1265:                  bool *instead_flag,
1.112     tgl      1266:                  Query **qual_product)
1.4       momjian  1267: {
1.5       momjian  1268:        List       *results = NIL;
1.136     neilc    1269:        ListCell   *l;
1.4       momjian  1270: 
1.136     neilc    1271:        foreach(l, locks)
1.4       momjian  1272:        {
1.136     neilc    1273:                RewriteRule *rule_lock = (RewriteRule *) lfirst(l);
1.112     tgl      1274:                Node       *event_qual = rule_lock->qual;
                   1275:                List       *actions = rule_lock->actions;
1.126     momjian  1276:                QuerySource qsrc;
1.136     neilc    1277:                ListCell   *r;
1.4       momjian  1278: 
1.111     tgl      1279:                /* Determine correct QuerySource value for actions */
                   1280:                if (rule_lock->isInstead)
                   1281:                {
                   1282:                        if (event_qual != NULL)
                   1283:                                qsrc = QSRC_QUAL_INSTEAD_RULE;
                   1284:                        else
1.112     tgl      1285:                        {
1.111     tgl      1286:                                qsrc = QSRC_INSTEAD_RULE;
1.126     momjian  1287:                                *instead_flag = true;   /* report unqualified INSTEAD */
1.112     tgl      1288:                        }
1.111     tgl      1289:                }
                   1290:                else
                   1291:                        qsrc = QSRC_NON_INSTEAD_RULE;
                   1292: 
                   1293:                if (qsrc == QSRC_QUAL_INSTEAD_RULE)
1.21      momjian  1294:                {
1.91      momjian  1295:                        /*
1.112     tgl      1296:                         * If there are INSTEAD rules with qualifications, the
1.91      momjian  1297:                         * original query is still performed. But all the negated rule
1.112     tgl      1298:                         * qualifications of the INSTEAD rules are added so it does
1.91      momjian  1299:                         * its actions only in cases where the rule quals of all
1.112     tgl      1300:                         * INSTEAD rules are false. Think of it as the default action
1.126     momjian  1301:                         * in a case. We save this in *qual_product so RewriteQuery()
                   1302:                         * can add it to the query list after we mangled it up enough.
1.112     tgl      1303:                         *
1.126     momjian  1304:                         * If we have already found an unqualified INSTEAD rule, then
                   1305:                         * *qual_product won't be used, so don't bother building it.
1.18      scrappy  1306:                         */
1.126     momjian  1307:                        if (!*instead_flag)
1.112     tgl      1308:                        {
                   1309:                                if (*qual_product == NULL)
1.153     tgl      1310:                                        *qual_product = copyObject(parsetree);
1.113     tgl      1311:                                *qual_product = CopyAndAddInvertedQual(*qual_product,
                   1312:                                                                                                           event_qual,
                   1313:                                                                                                           rt_index,
                   1314:                                                                                                           event);
1.112     tgl      1315:                        }
1.18      scrappy  1316:                }
                   1317: 
1.111     tgl      1318:                /* Now process the rule's actions and add them to the result list */
1.4       momjian  1319:                foreach(r, actions)
                   1320:                {
1.5       momjian  1321:                        Query      *rule_action = lfirst(r);
1.4       momjian  1322: 
1.18      scrappy  1323:                        if (rule_action->commandType == CMD_NOTHING)
                   1324:                                continue;
1.25      momjian  1325: 
1.95      tgl      1326:                        rule_action = rewriteRuleAction(parsetree, rule_action,
                   1327:                                                                                        event_qual, rt_index, event);
1.4       momjian  1328: 
1.111     tgl      1329:                        rule_action->querySource = qsrc;
1.120     tgl      1330:                        rule_action->canSetTag = false;         /* might change later */
1.111     tgl      1331: 
1.95      tgl      1332:                        results = lappend(results, rule_action);
1.4       momjian  1333:                }
                   1334:        }
1.111     tgl      1335: 
1.4       momjian  1336:        return results;
                   1337: }
                   1338: 
1.18      scrappy  1339: 
1.112     tgl      1340: /*
1.118     tgl      1341:  * RewriteQuery -
                   1342:  *       rewrites the query and apply the rules again on the queries rewritten
1.112     tgl      1343:  *
1.118     tgl      1344:  * rewrite_events is a list of open query-rewrite actions, so we can detect
                   1345:  * infinite recursion.
1.112     tgl      1346:  */
1.5       momjian  1347: static List *
1.118     tgl      1348: RewriteQuery(Query *parsetree, List *rewrite_events)
1.4       momjian  1349: {
1.118     tgl      1350:        CmdType         event = parsetree->commandType;
                   1351:        bool            instead = false;
                   1352:        Query      *qual_product = NULL;
                   1353:        List       *rewritten = NIL;
1.1       scrappy  1354: 
1.22      momjian  1355:        /*
1.118     tgl      1356:         * If the statement is an update, insert or delete - fire rules on it.
                   1357:         *
1.45      momjian  1358:         * SELECT rules are handled later when we have all the queries that
1.126     momjian  1359:         * should get executed.  Also, utilities aren't rewritten at all (do
                   1360:         * we still need that check?)
1.22      momjian  1361:         */
1.118     tgl      1362:        if (event != CMD_SELECT && event != CMD_UTILITY)
1.22      momjian  1363:        {
1.118     tgl      1364:                int                     result_relation;
                   1365:                RangeTblEntry *rt_entry;
                   1366:                Relation        rt_entry_relation;
                   1367:                List       *locks;
1.22      momjian  1368: 
1.118     tgl      1369:                result_relation = parsetree->resultRelation;
                   1370:                Assert(result_relation != 0);
                   1371:                rt_entry = rt_fetch(result_relation, parsetree->rtable);
                   1372:                Assert(rt_entry->rtekind == RTE_RELATION);
1.1       scrappy  1373: 
1.118     tgl      1374:                /*
1.153     tgl      1375:                 * We can use NoLock here since either the parser or
                   1376:                 * AcquireRewriteLocks should have locked the rel already.
1.118     tgl      1377:                 */
1.153     tgl      1378:                rt_entry_relation = heap_open(rt_entry->relid, NoLock);
1.77      tgl      1379: 
1.118     tgl      1380:                /*
1.126     momjian  1381:                 * If it's an INSERT or UPDATE, rewrite the targetlist into
                   1382:                 * standard form.  This will be needed by the planner anyway, and
                   1383:                 * doing it now ensures that any references to NEW.field will
                   1384:                 * behave sanely.
1.118     tgl      1385:                 */
                   1386:                if (event == CMD_INSERT || event == CMD_UPDATE)
                   1387:                        rewriteTargetList(parsetree, rt_entry_relation);
1.10      momjian  1388: 
1.118     tgl      1389:                /*
                   1390:                 * Collect and apply the appropriate rules.
                   1391:                 */
                   1392:                locks = matchLocks(event, rt_entry_relation->rd_rules,
                   1393:                                                   result_relation, parsetree);
1.1       scrappy  1394: 
1.118     tgl      1395:                if (locks != NIL)
                   1396:                {
                   1397:                        List       *product_queries;
1.1       scrappy  1398: 
1.118     tgl      1399:                        product_queries = fireRules(parsetree,
                   1400:                                                                                result_relation,
                   1401:                                                                                event,
                   1402:                                                                                locks,
                   1403:                                                                                &instead,
                   1404:                                                                                &qual_product);
1.1       scrappy  1405: 
1.118     tgl      1406:                        /*
1.126     momjian  1407:                         * If we got any product queries, recursively rewrite them ---
                   1408:                         * but first check for recursion!
1.118     tgl      1409:                         */
                   1410:                        if (product_queries != NIL)
                   1411:                        {
1.144     momjian  1412:                                ListCell   *n;
                   1413:                                rewrite_event *rev;
1.4       momjian  1414: 
1.118     tgl      1415:                                foreach(n, rewrite_events)
                   1416:                                {
                   1417:                                        rev = (rewrite_event *) lfirst(n);
                   1418:                                        if (rev->relation == RelationGetRelid(rt_entry_relation) &&
                   1419:                                                rev->event == event)
1.124     tgl      1420:                                                ereport(ERROR,
1.126     momjian  1421:                                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                   1422:                                                          errmsg("infinite recursion detected in rules for relation \"%s\"",
                   1423:                                                   RelationGetRelationName(rt_entry_relation))));
1.118     tgl      1424:                                }
                   1425: 
                   1426:                                rev = (rewrite_event *) palloc(sizeof(rewrite_event));
                   1427:                                rev->relation = RelationGetRelid(rt_entry_relation);
                   1428:                                rev->event = event;
                   1429:                                rewrite_events = lcons(rev, rewrite_events);
                   1430: 
                   1431:                                foreach(n, product_queries)
                   1432:                                {
                   1433:                                        Query      *pt = (Query *) lfirst(n);
                   1434:                                        List       *newstuff;
                   1435: 
                   1436:                                        newstuff = RewriteQuery(pt, rewrite_events);
1.138     neilc    1437:                                        rewritten = list_concat(rewritten, newstuff);
1.118     tgl      1438:                                }
1.146     tgl      1439: 
                   1440:                                rewrite_events = list_delete_first(rewrite_events);
1.118     tgl      1441:                        }
                   1442:                }
1.4       momjian  1443: 
1.153     tgl      1444:                heap_close(rt_entry_relation, NoLock);
1.4       momjian  1445:        }
1.18      scrappy  1446: 
1.91      momjian  1447:        /*
1.98      momjian  1448:         * For INSERTs, the original query is done first; for UPDATE/DELETE,
                   1449:         * it is done last.  This is needed because update and delete rule
                   1450:         * actions might not do anything if they are invoked after the update
                   1451:         * or delete is performed. The command counter increment between the
1.112     tgl      1452:         * query executions makes the deleted (and maybe the updated) tuples
1.98      momjian  1453:         * disappear so the scans for them in the rule actions cannot find
                   1454:         * them.
1.112     tgl      1455:         *
1.126     momjian  1456:         * If we found any unqualified INSTEAD, the original query is not done at
                   1457:         * all, in any form.  Otherwise, we add the modified form if qualified
                   1458:         * INSTEADs were found, else the unmodified form.
1.18      scrappy  1459:         */
1.112     tgl      1460:        if (!instead)
1.97      tgl      1461:        {
1.112     tgl      1462:                if (parsetree->commandType == CMD_INSERT)
                   1463:                {
                   1464:                        if (qual_product != NULL)
                   1465:                                rewritten = lcons(qual_product, rewritten);
                   1466:                        else
                   1467:                                rewritten = lcons(parsetree, rewritten);
                   1468:                }
                   1469:                else
                   1470:                {
                   1471:                        if (qual_product != NULL)
                   1472:                                rewritten = lappend(rewritten, qual_product);
                   1473:                        else
                   1474:                                rewritten = lappend(rewritten, parsetree);
                   1475:                }
1.97      tgl      1476:        }
1.4       momjian  1477: 
                   1478:        return rewritten;
1.12      scrappy  1479: }
1.22      momjian  1480: 
                   1481: 
                   1482: /*
1.82      tgl      1483:  * QueryRewrite -
                   1484:  *       Primary entry point to the query rewriter.
                   1485:  *       Rewrite one query via query rewrite system, possibly returning 0
                   1486:  *       or many queries.
                   1487:  *
1.153     tgl      1488:  * NOTE: the parsetree must either have come straight from the parser,
                   1489:  * or have been scanned by AcquireRewriteLocks to acquire suitable locks.
1.22      momjian  1490:  */
1.82      tgl      1491: List *
                   1492: QueryRewrite(Query *parsetree)
1.22      momjian  1493: {
1.45      momjian  1494:        List       *querylist;
                   1495:        List       *results = NIL;
1.136     neilc    1496:        ListCell   *l;
1.120     tgl      1497:        CmdType         origCmdType;
                   1498:        bool            foundOriginalQuery;
                   1499:        Query      *lastInstead;
1.22      momjian  1500: 
                   1501:        /*
                   1502:         * Step 1
                   1503:         *
                   1504:         * Apply all non-SELECT rules possibly getting 0 or many queries
                   1505:         */
1.118     tgl      1506:        querylist = RewriteQuery(parsetree, NIL);
1.22      momjian  1507: 
                   1508:        /*
1.62      tgl      1509:         * Step 2
1.22      momjian  1510:         *
                   1511:         * Apply all the RIR rules on each query
                   1512:         */
1.45      momjian  1513:        foreach(l, querylist)
                   1514:        {
1.90      momjian  1515:                Query      *query = (Query *) lfirst(l);
1.45      momjian  1516: 
1.118     tgl      1517:                query = fireRIRrules(query, NIL);
1.71      momjian  1518: 
1.45      momjian  1519:                /*
1.82      tgl      1520:                 * If the query target was rewritten as a view, complain.
1.45      momjian  1521:                 */
1.82      tgl      1522:                if (query->resultRelation)
1.45      momjian  1523:                {
1.82      tgl      1524:                        RangeTblEntry *rte = rt_fetch(query->resultRelation,
                   1525:                                                                                  query->rtable);
                   1526: 
1.99      tgl      1527:                        if (rte->rtekind == RTE_SUBQUERY)
1.82      tgl      1528:                        {
                   1529:                                switch (query->commandType)
                   1530:                                {
                   1531:                                        case CMD_INSERT:
1.124     tgl      1532:                                                ereport(ERROR,
                   1533:                                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   1534:                                                                 errmsg("cannot insert into a view"),
                   1535:                                                                 errhint("You need an unconditional ON INSERT DO INSTEAD rule.")));
1.82      tgl      1536:                                                break;
                   1537:                                        case CMD_UPDATE:
1.124     tgl      1538:                                                ereport(ERROR,
                   1539:                                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   1540:                                                                 errmsg("cannot update a view"),
                   1541:                                                                 errhint("You need an unconditional ON UPDATE DO INSTEAD rule.")));
1.82      tgl      1542:                                                break;
                   1543:                                        case CMD_DELETE:
1.124     tgl      1544:                                                ereport(ERROR,
                   1545:                                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   1546:                                                                 errmsg("cannot delete from a view"),
                   1547:                                                                 errhint("You need an unconditional ON DELETE DO INSTEAD rule.")));
1.82      tgl      1548:                                                break;
                   1549:                                        default:
1.124     tgl      1550:                                                elog(ERROR, "unrecognized commandType: %d",
1.82      tgl      1551:                                                         (int) query->commandType);
                   1552:                                                break;
                   1553:                                }
1.45      momjian  1554:                        }
                   1555:                }
                   1556: 
1.82      tgl      1557:                results = lappend(results, query);
1.45      momjian  1558:        }
1.120     tgl      1559: 
                   1560:        /*
                   1561:         * Step 3
                   1562:         *
                   1563:         * Determine which, if any, of the resulting queries is supposed to set
1.126     momjian  1564:         * the command-result tag; and update the canSetTag fields
                   1565:         * accordingly.
1.120     tgl      1566:         *
                   1567:         * If the original query is still in the list, it sets the command tag.
                   1568:         * Otherwise, the last INSTEAD query of the same kind as the original
                   1569:         * is allowed to set the tag.  (Note these rules can leave us with no
                   1570:         * query setting the tag.  The tcop code has to cope with this by
                   1571:         * setting up a default tag based on the original un-rewritten query.)
                   1572:         *
                   1573:         * The Asserts verify that at most one query in the result list is marked
1.126     momjian  1574:         * canSetTag.  If we aren't checking asserts, we can fall out of the
                   1575:         * loop as soon as we find the original query.
1.120     tgl      1576:         */
                   1577:        origCmdType = parsetree->commandType;
                   1578:        foundOriginalQuery = false;
                   1579:        lastInstead = NULL;
                   1580: 
                   1581:        foreach(l, results)
                   1582:        {
                   1583:                Query      *query = (Query *) lfirst(l);
                   1584: 
                   1585:                if (query->querySource == QSRC_ORIGINAL)
                   1586:                {
                   1587:                        Assert(query->canSetTag);
                   1588:                        Assert(!foundOriginalQuery);
                   1589:                        foundOriginalQuery = true;
                   1590: #ifndef USE_ASSERT_CHECKING
                   1591:                        break;
                   1592: #endif
                   1593:                }
                   1594:                else
                   1595:                {
                   1596:                        Assert(!query->canSetTag);
                   1597:                        if (query->commandType == origCmdType &&
                   1598:                                (query->querySource == QSRC_INSTEAD_RULE ||
                   1599:                                 query->querySource == QSRC_QUAL_INSTEAD_RULE))
                   1600:                                lastInstead = query;
                   1601:                }
                   1602:        }
                   1603: 
                   1604:        if (!foundOriginalQuery && lastInstead != NULL)
                   1605:                lastInstead->canSetTag = true;
1.45      momjian  1606: 
1.82      tgl      1607:        return results;
1.22      momjian  1608: }

PostgreSQL CVSweb <webmaster@postgresql.org>