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