1/*-------------------------------------------------------------------------
2 *
3 * appendinfo.c
4 * Routines for mapping between append parent(s) and children
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/optimizer/path/appendinfo.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/htup_details.h"
18#include "nodes/makefuncs.h"
19#include "nodes/nodeFuncs.h"
20#include "optimizer/appendinfo.h"
21#include "parser/parsetree.h"
22#include "utils/lsyscache.h"
23#include "utils/rel.h"
24#include "utils/syscache.h"
25
26
27typedef struct
28{
29 PlannerInfo *root;
30 int nappinfos;
31 AppendRelInfo **appinfos;
32} adjust_appendrel_attrs_context;
33
34static void make_inh_translation_list(Relation oldrelation,
35 Relation newrelation,
36 Index newvarno,
37 List **translated_vars);
38static Node *adjust_appendrel_attrs_mutator(Node *node,
39 adjust_appendrel_attrs_context *context);
40static List *adjust_inherited_tlist(List *tlist,
41 AppendRelInfo *context);
42
43
44/*
45 * make_append_rel_info
46 * Build an AppendRelInfo for the parent-child pair
47 */
48AppendRelInfo *
49make_append_rel_info(Relation parentrel, Relation childrel,
50 Index parentRTindex, Index childRTindex)
51{
52 AppendRelInfo *appinfo = makeNode(AppendRelInfo);
53
54 appinfo->parent_relid = parentRTindex;
55 appinfo->child_relid = childRTindex;
56 appinfo->parent_reltype = parentrel->rd_rel->reltype;
57 appinfo->child_reltype = childrel->rd_rel->reltype;
58 make_inh_translation_list(parentrel, childrel, childRTindex,
59 &appinfo->translated_vars);
60 appinfo->parent_reloid = RelationGetRelid(parentrel);
61
62 return appinfo;
63}
64
65/*
66 * make_inh_translation_list
67 * Build the list of translations from parent Vars to child Vars for
68 * an inheritance child.
69 *
70 * For paranoia's sake, we match type/collation as well as attribute name.
71 */
72static void
73make_inh_translation_list(Relation oldrelation, Relation newrelation,
74 Index newvarno,
75 List **translated_vars)
76{
77 List *vars = NIL;
78 TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
79 TupleDesc new_tupdesc = RelationGetDescr(newrelation);
80 Oid new_relid = RelationGetRelid(newrelation);
81 int oldnatts = old_tupdesc->natts;
82 int newnatts = new_tupdesc->natts;
83 int old_attno;
84 int new_attno = 0;
85
86 for (old_attno = 0; old_attno < oldnatts; old_attno++)
87 {
88 Form_pg_attribute att;
89 char *attname;
90 Oid atttypid;
91 int32 atttypmod;
92 Oid attcollation;
93
94 att = TupleDescAttr(old_tupdesc, old_attno);
95 if (att->attisdropped)
96 {
97 /* Just put NULL into this list entry */
98 vars = lappend(vars, NULL);
99 continue;
100 }
101 attname = NameStr(att->attname);
102 atttypid = att->atttypid;
103 atttypmod = att->atttypmod;
104 attcollation = att->attcollation;
105
106 /*
107 * When we are generating the "translation list" for the parent table
108 * of an inheritance set, no need to search for matches.
109 */
110 if (oldrelation == newrelation)
111 {
112 vars = lappend(vars, makeVar(newvarno,
113 (AttrNumber) (old_attno + 1),
114 atttypid,
115 atttypmod,
116 attcollation,
117 0));
118 continue;
119 }
120
121 /*
122 * Otherwise we have to search for the matching column by name.
123 * There's no guarantee it'll have the same column position, because
124 * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
125 * However, in simple cases, the relative order of columns is mostly
126 * the same in both relations, so try the column of newrelation that
127 * follows immediately after the one that we just found, and if that
128 * fails, let syscache handle it.
129 */
130 if (new_attno >= newnatts ||
131 (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||
132 strcmp(attname, NameStr(att->attname)) != 0)
133 {
134 HeapTuple newtup;
135
136 newtup = SearchSysCacheAttName(new_relid, attname);
137 if (!HeapTupleIsValid(newtup))
138 elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
139 attname, RelationGetRelationName(newrelation));
140 new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
141 ReleaseSysCache(newtup);
142
143 att = TupleDescAttr(new_tupdesc, new_attno);
144 }
145
146 /* Found it, check type and collation match */
147 if (atttypid != att->atttypid || atttypmod != att->atttypmod)
148 elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
149 attname, RelationGetRelationName(newrelation));
150 if (attcollation != att->attcollation)
151 elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
152 attname, RelationGetRelationName(newrelation));
153
154 vars = lappend(vars, makeVar(newvarno,
155 (AttrNumber) (new_attno + 1),
156 atttypid,
157 atttypmod,
158 attcollation,
159 0));
160 new_attno++;
161 }
162
163 *translated_vars = vars;
164}
165
166/*
167 * adjust_appendrel_attrs
168 * Copy the specified query or expression and translate Vars referring to a
169 * parent rel to refer to the corresponding child rel instead. We also
170 * update rtindexes appearing outside Vars, such as resultRelation and
171 * jointree relids.
172 *
173 * Note: this is only applied after conversion of sublinks to subplans,
174 * so we don't need to cope with recursion into sub-queries.
175 *
176 * Note: this is not hugely different from what pullup_replace_vars() does;
177 * maybe we should try to fold the two routines together.
178 */
179Node *
180adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
181 AppendRelInfo **appinfos)
182{
183 Node *result;
184 adjust_appendrel_attrs_context context;
185
186 context.root = root;
187 context.nappinfos = nappinfos;
188 context.appinfos = appinfos;
189
190 /* If there's nothing to adjust, don't call this function. */
191 Assert(nappinfos >= 1 && appinfos != NULL);
192
193 /*
194 * Must be prepared to start with a Query or a bare expression tree.
195 */
196 if (node && IsA(node, Query))
197 {
198 Query *newnode;
199 int cnt;
200
201 newnode = query_tree_mutator((Query *) node,
202 adjust_appendrel_attrs_mutator,
203 (void *) &context,
204 QTW_IGNORE_RC_SUBQUERIES);
205 for (cnt = 0; cnt < nappinfos; cnt++)
206 {
207 AppendRelInfo *appinfo = appinfos[cnt];
208
209 if (newnode->resultRelation == appinfo->parent_relid)
210 {
211 newnode->resultRelation = appinfo->child_relid;
212 /* Fix tlist resnos too, if it's inherited UPDATE */
213 if (newnode->commandType == CMD_UPDATE)
214 newnode->targetList =
215 adjust_inherited_tlist(newnode->targetList,
216 appinfo);
217 break;
218 }
219 }
220
221 result = (Node *) newnode;
222 }
223 else
224 result = adjust_appendrel_attrs_mutator(node, &context);
225
226 return result;
227}
228
229static Node *
230adjust_appendrel_attrs_mutator(Node *node,
231 adjust_appendrel_attrs_context *context)
232{
233 AppendRelInfo **appinfos = context->appinfos;
234 int nappinfos = context->nappinfos;
235 int cnt;
236
237 if (node == NULL)
238 return NULL;
239 if (IsA(node, Var))
240 {
241 Var *var = (Var *) copyObject(node);
242 AppendRelInfo *appinfo = NULL;
243
244 for (cnt = 0; cnt < nappinfos; cnt++)
245 {
246 if (var->varno == appinfos[cnt]->parent_relid)
247 {
248 appinfo = appinfos[cnt];
249 break;
250 }
251 }
252
253 if (var->varlevelsup == 0 && appinfo)
254 {
255 var->varno = appinfo->child_relid;
256 var->varnoold = appinfo->child_relid;
257 if (var->varattno > 0)
258 {
259 Node *newnode;
260
261 if (var->varattno > list_length(appinfo->translated_vars))
262 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
263 var->varattno, get_rel_name(appinfo->parent_reloid));
264 newnode = copyObject(list_nth(appinfo->translated_vars,
265 var->varattno - 1));
266 if (newnode == NULL)
267 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
268 var->varattno, get_rel_name(appinfo->parent_reloid));
269 return newnode;
270 }
271 else if (var->varattno == 0)
272 {
273 /*
274 * Whole-row Var: if we are dealing with named rowtypes, we
275 * can use a whole-row Var for the child table plus a coercion
276 * step to convert the tuple layout to the parent's rowtype.
277 * Otherwise we have to generate a RowExpr.
278 */
279 if (OidIsValid(appinfo->child_reltype))
280 {
281 Assert(var->vartype == appinfo->parent_reltype);
282 if (appinfo->parent_reltype != appinfo->child_reltype)
283 {
284 ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
285
286 r->arg = (Expr *) var;
287 r->resulttype = appinfo->parent_reltype;
288 r->convertformat = COERCE_IMPLICIT_CAST;
289 r->location = -1;
290 /* Make sure the Var node has the right type ID, too */
291 var->vartype = appinfo->child_reltype;
292 return (Node *) r;
293 }
294 }
295 else
296 {
297 /*
298 * Build a RowExpr containing the translated variables.
299 *
300 * In practice var->vartype will always be RECORDOID here,
301 * so we need to come up with some suitable column names.
302 * We use the parent RTE's column names.
303 *
304 * Note: we can't get here for inheritance cases, so there
305 * is no need to worry that translated_vars might contain
306 * some dummy NULLs.
307 */
308 RowExpr *rowexpr;
309 List *fields;
310 RangeTblEntry *rte;
311
312 rte = rt_fetch(appinfo->parent_relid,
313 context->root->parse->rtable);
314 fields = copyObject(appinfo->translated_vars);
315 rowexpr = makeNode(RowExpr);
316 rowexpr->args = fields;
317 rowexpr->row_typeid = var->vartype;
318 rowexpr->row_format = COERCE_IMPLICIT_CAST;
319 rowexpr->colnames = copyObject(rte->eref->colnames);
320 rowexpr->location = -1;
321
322 return (Node *) rowexpr;
323 }
324 }
325 /* system attributes don't need any other translation */
326 }
327 return (Node *) var;
328 }
329 if (IsA(node, CurrentOfExpr))
330 {
331 CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
332
333 for (cnt = 0; cnt < nappinfos; cnt++)
334 {
335 AppendRelInfo *appinfo = appinfos[cnt];
336
337 if (cexpr->cvarno == appinfo->parent_relid)
338 {
339 cexpr->cvarno = appinfo->child_relid;
340 break;
341 }
342 }
343 return (Node *) cexpr;
344 }
345 if (IsA(node, RangeTblRef))
346 {
347 RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
348
349 for (cnt = 0; cnt < nappinfos; cnt++)
350 {
351 AppendRelInfo *appinfo = appinfos[cnt];
352
353 if (rtr->rtindex == appinfo->parent_relid)
354 {
355 rtr->rtindex = appinfo->child_relid;
356 break;
357 }
358 }
359 return (Node *) rtr;
360 }
361 if (IsA(node, JoinExpr))
362 {
363 /* Copy the JoinExpr node with correct mutation of subnodes */
364 JoinExpr *j;
365 AppendRelInfo *appinfo;
366
367 j = (JoinExpr *) expression_tree_mutator(node,
368 adjust_appendrel_attrs_mutator,
369 (void *) context);
370 /* now fix JoinExpr's rtindex (probably never happens) */
371 for (cnt = 0; cnt < nappinfos; cnt++)
372 {
373 appinfo = appinfos[cnt];
374
375 if (j->rtindex == appinfo->parent_relid)
376 {
377 j->rtindex = appinfo->child_relid;
378 break;
379 }
380 }
381 return (Node *) j;
382 }
383 if (IsA(node, PlaceHolderVar))
384 {
385 /* Copy the PlaceHolderVar node with correct mutation of subnodes */
386 PlaceHolderVar *phv;
387
388 phv = (PlaceHolderVar *) expression_tree_mutator(node,
389 adjust_appendrel_attrs_mutator,
390 (void *) context);
391 /* now fix PlaceHolderVar's relid sets */
392 if (phv->phlevelsup == 0)
393 phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,
394 context->appinfos);
395 return (Node *) phv;
396 }
397 /* Shouldn't need to handle planner auxiliary nodes here */
398 Assert(!IsA(node, SpecialJoinInfo));
399 Assert(!IsA(node, AppendRelInfo));
400 Assert(!IsA(node, PlaceHolderInfo));
401 Assert(!IsA(node, MinMaxAggInfo));
402
403 /*
404 * We have to process RestrictInfo nodes specially. (Note: although
405 * set_append_rel_pathlist will hide RestrictInfos in the parent's
406 * baserestrictinfo list from us, it doesn't hide those in joininfo.)
407 */
408 if (IsA(node, RestrictInfo))
409 {
410 RestrictInfo *oldinfo = (RestrictInfo *) node;
411 RestrictInfo *newinfo = makeNode(RestrictInfo);
412
413 /* Copy all flat-copiable fields */
414 memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
415
416 /* Recursively fix the clause itself */
417 newinfo->clause = (Expr *)
418 adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
419
420 /* and the modified version, if an OR clause */
421 newinfo->orclause = (Expr *)
422 adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
423
424 /* adjust relid sets too */
425 newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
426 context->nappinfos,
427 context->appinfos);
428 newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
429 context->nappinfos,
430 context->appinfos);
431 newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
432 context->nappinfos,
433 context->appinfos);
434 newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,
435 context->nappinfos,
436 context->appinfos);
437 newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
438 context->nappinfos,
439 context->appinfos);
440 newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
441 context->nappinfos,
442 context->appinfos);
443
444 /*
445 * Reset cached derivative fields, since these might need to have
446 * different values when considering the child relation. Note we
447 * don't reset left_ec/right_ec: each child variable is implicitly
448 * equivalent to its parent, so still a member of the same EC if any.
449 */
450 newinfo->eval_cost.startup = -1;
451 newinfo->norm_selec = -1;
452 newinfo->outer_selec = -1;
453 newinfo->left_em = NULL;
454 newinfo->right_em = NULL;
455 newinfo->scansel_cache = NIL;
456 newinfo->left_bucketsize = -1;
457 newinfo->right_bucketsize = -1;
458 newinfo->left_mcvfreq = -1;
459 newinfo->right_mcvfreq = -1;
460
461 return (Node *) newinfo;
462 }
463
464 /*
465 * NOTE: we do not need to recurse into sublinks, because they should
466 * already have been converted to subplans before we see them.
467 */
468 Assert(!IsA(node, SubLink));
469 Assert(!IsA(node, Query));
470
471 return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
472 (void *) context);
473}
474
475/*
476 * adjust_appendrel_attrs_multilevel
477 * Apply Var translations from a toplevel appendrel parent down to a child.
478 *
479 * In some cases we need to translate expressions referencing a parent relation
480 * to reference an appendrel child that's multiple levels removed from it.
481 */
482Node *
483adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
484 Relids child_relids,
485 Relids top_parent_relids)
486{
487 AppendRelInfo **appinfos;
488 Bitmapset *parent_relids = NULL;
489 int nappinfos;
490 int cnt;
491
492 Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));
493
494 appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
495
496 /* Construct relids set for the immediate parent of given child. */
497 for (cnt = 0; cnt < nappinfos; cnt++)
498 {
499 AppendRelInfo *appinfo = appinfos[cnt];
500
501 parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
502 }
503
504 /* Recurse if immediate parent is not the top parent. */
505 if (!bms_equal(parent_relids, top_parent_relids))
506 node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,
507 top_parent_relids);
508
509 /* Now translate for this child */
510 node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
511
512 pfree(appinfos);
513
514 return node;
515}
516
517/*
518 * Substitute child relids for parent relids in a Relid set. The array of
519 * appinfos specifies the substitutions to be performed.
520 */
521Relids
522adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
523{
524 Bitmapset *result = NULL;
525 int cnt;
526
527 for (cnt = 0; cnt < nappinfos; cnt++)
528 {
529 AppendRelInfo *appinfo = appinfos[cnt];
530
531 /* Remove parent, add child */
532 if (bms_is_member(appinfo->parent_relid, relids))
533 {
534 /* Make a copy if we are changing the set. */
535 if (!result)
536 result = bms_copy(relids);
537
538 result = bms_del_member(result, appinfo->parent_relid);
539 result = bms_add_member(result, appinfo->child_relid);
540 }
541 }
542
543 /* If we made any changes, return the modified copy. */
544 if (result)
545 return result;
546
547 /* Otherwise, return the original set without modification. */
548 return relids;
549}
550
551/*
552 * Replace any relid present in top_parent_relids with its child in
553 * child_relids. Members of child_relids can be multiple levels below top
554 * parent in the partition hierarchy.
555 */
556Relids
557adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
558 Relids child_relids, Relids top_parent_relids)
559{
560 AppendRelInfo **appinfos;
561 int nappinfos;
562 Relids parent_relids = NULL;
563 Relids result;
564 Relids tmp_result = NULL;
565 int cnt;
566
567 /*
568 * If the given relids set doesn't contain any of the top parent relids,
569 * it will remain unchanged.
570 */
571 if (!bms_overlap(relids, top_parent_relids))
572 return relids;
573
574 appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
575
576 /* Construct relids set for the immediate parent of the given child. */
577 for (cnt = 0; cnt < nappinfos; cnt++)
578 {
579 AppendRelInfo *appinfo = appinfos[cnt];
580
581 parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
582 }
583
584 /* Recurse if immediate parent is not the top parent. */
585 if (!bms_equal(parent_relids, top_parent_relids))
586 {
587 tmp_result = adjust_child_relids_multilevel(root, relids,
588 parent_relids,
589 top_parent_relids);
590 relids = tmp_result;
591 }
592
593 result = adjust_child_relids(relids, nappinfos, appinfos);
594
595 /* Free memory consumed by any intermediate result. */
596 if (tmp_result)
597 bms_free(tmp_result);
598 bms_free(parent_relids);
599 pfree(appinfos);
600
601 return result;
602}
603
604/*
605 * Adjust the targetlist entries of an inherited UPDATE operation
606 *
607 * The expressions have already been fixed, but we have to make sure that
608 * the target resnos match the child table (they may not, in the case of
609 * a column that was added after-the-fact by ALTER TABLE). In some cases
610 * this can force us to re-order the tlist to preserve resno ordering.
611 * (We do all this work in special cases so that preptlist.c is fast for
612 * the typical case.)
613 *
614 * The given tlist has already been through expression_tree_mutator;
615 * therefore the TargetEntry nodes are fresh copies that it's okay to
616 * scribble on.
617 *
618 * Note that this is not needed for INSERT because INSERT isn't inheritable.
619 */
620static List *
621adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
622{
623 bool changed_it = false;
624 ListCell *tl;
625 List *new_tlist;
626 bool more;
627 int attrno;
628
629 /* This should only happen for an inheritance case, not UNION ALL */
630 Assert(OidIsValid(context->parent_reloid));
631
632 /* Scan tlist and update resnos to match attnums of child rel */
633 foreach(tl, tlist)
634 {
635 TargetEntry *tle = (TargetEntry *) lfirst(tl);
636 Var *childvar;
637
638 if (tle->resjunk)
639 continue; /* ignore junk items */
640
641 /* Look up the translation of this column: it must be a Var */
642 if (tle->resno <= 0 ||
643 tle->resno > list_length(context->translated_vars))
644 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
645 tle->resno, get_rel_name(context->parent_reloid));
646 childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
647 if (childvar == NULL || !IsA(childvar, Var))
648 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
649 tle->resno, get_rel_name(context->parent_reloid));
650
651 if (tle->resno != childvar->varattno)
652 {
653 tle->resno = childvar->varattno;
654 changed_it = true;
655 }
656 }
657
658 /*
659 * If we changed anything, re-sort the tlist by resno, and make sure
660 * resjunk entries have resnos above the last real resno. The sort
661 * algorithm is a bit stupid, but for such a seldom-taken path, small is
662 * probably better than fast.
663 */
664 if (!changed_it)
665 return tlist;
666
667 new_tlist = NIL;
668 more = true;
669 for (attrno = 1; more; attrno++)
670 {
671 more = false;
672 foreach(tl, tlist)
673 {
674 TargetEntry *tle = (TargetEntry *) lfirst(tl);
675
676 if (tle->resjunk)
677 continue; /* ignore junk items */
678
679 if (tle->resno == attrno)
680 new_tlist = lappend(new_tlist, tle);
681 else if (tle->resno > attrno)
682 more = true;
683 }
684 }
685
686 foreach(tl, tlist)
687 {
688 TargetEntry *tle = (TargetEntry *) lfirst(tl);
689
690 if (!tle->resjunk)
691 continue; /* here, ignore non-junk items */
692
693 tle->resno = attrno;
694 new_tlist = lappend(new_tlist, tle);
695 attrno++;
696 }
697
698 return new_tlist;
699}
700
701/*
702 * find_appinfos_by_relids
703 * Find AppendRelInfo structures for all relations specified by relids.
704 *
705 * The AppendRelInfos are returned in an array, which can be pfree'd by the
706 * caller. *nappinfos is set to the number of entries in the array.
707 */
708AppendRelInfo **
709find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
710{
711 AppendRelInfo **appinfos;
712 int cnt = 0;
713 int i;
714
715 *nappinfos = bms_num_members(relids);
716 appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);
717
718 i = -1;
719 while ((i = bms_next_member(relids, i)) >= 0)
720 {
721 AppendRelInfo *appinfo = root->append_rel_array[i];
722
723 if (!appinfo)
724 elog(ERROR, "child rel %d not found in append_rel_array", i);
725
726 appinfos[cnt++] = appinfo;
727 }
728 return appinfos;
729}
730