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