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

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

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

PostgreSQL CVSweb <webmaster@postgresql.org>