diff options
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 381 |
1 files changed, 194 insertions, 187 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 94140d304f7..38b040ffff3 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -152,12 +152,17 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, double limit_tuples); static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, + Relids relids, + const AttrNumber *reqColIdx, bool adjust_tlist_in_place, int *p_numsortkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, Oid **p_collations, bool **p_nullsFirst); +static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, + TargetEntry *tle, + Relids relids); static Material *make_material(Plan *lefttree); @@ -706,6 +711,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) /* Compute sort column info, and adjust MergeAppend's tlist as needed */ (void) prepare_sort_from_pathkeys(root, plan, pathkeys, + NULL, + NULL, true, &node->numCols, &node->sortColIdx, @@ -733,6 +740,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) /* Compute sort column info, and adjust subplan's tlist as needed */ subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys, + subpath->parent->relids, + node->sortColIdx, false, &numsortkeys, &sortColIdx, @@ -2695,11 +2704,8 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) ListCell *indexpr_item; /* - * Remove any PlaceHolderVars or binary-compatible relabeling of the - * indexkey (this must match logic in match_index_to_operand()). + * Remove any binary-compatible relabeling of the indexkey */ - while (IsA(node, PlaceHolderVar)) - node = (Node *) ((PlaceHolderVar *) node)->phexpr; if (IsA(node, RelabelType)) node = (Node *) ((RelabelType *) node)->arg; @@ -3516,55 +3522,6 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, } /* - * add_sort_column --- utility subroutine for building sort info arrays - * - * We need this routine because the same column might be selected more than - * once as a sort key column; if so, the extra mentions are redundant. - * - * Caller is assumed to have allocated the arrays large enough for the - * max possible number of columns. Return value is the new column count. - */ -static int -add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, - int numCols, AttrNumber *sortColIdx, - Oid *sortOperators, Oid *collations, bool *nullsFirst) -{ - int i; - - Assert(OidIsValid(sortOp)); - - for (i = 0; i < numCols; i++) - { - /* - * Note: we check sortOp because it's conceivable that "ORDER BY foo - * USING <, foo USING <<<" is not redundant, if <<< distinguishes - * values that < considers equal. We need not check nulls_first - * however because a lower-order column with the same sortop but - * opposite nulls direction is redundant. - * - * We could probably consider sort keys with the same sortop and - * different collations to be redundant too, but for the moment treat - * them as not redundant. This will be needed if we ever support - * collations with different notions of equality. - */ - if (sortColIdx[i] == colIdx && - sortOperators[numCols] == sortOp && - collations[numCols] == coll) - { - /* Already sorting by this col, so extra sort key is useless */ - return numCols; - } - } - - /* Add the column */ - sortColIdx[numCols] = colIdx; - sortOperators[numCols] = sortOp; - collations[numCols] = coll; - nullsFirst[numCols] = nulls_first; - return numCols + 1; -} - -/* * prepare_sort_from_pathkeys * Prepare to sort according to given pathkeys * @@ -3573,8 +3530,10 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, * plan targetlist if needed to add resjunk sort columns. * * Input parameters: - * 'lefttree' is the node which yields input tuples + * 'lefttree' is the plan node which yields input tuples * 'pathkeys' is the list of pathkeys by which the result is to be sorted + * 'relids' identifies the child relation being sorted, if any + * 'reqColIdx' is NULL or an array of required sort key column numbers * 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place * * We must convert the pathkey information into arrays of sort key column @@ -3582,6 +3541,14 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, * which is the representation the executor wants. These are returned into * the output parameters *p_numsortkeys etc. * + * When looking for matches to an EquivalenceClass's members, we will only + * consider child EC members if they match 'relids'. This protects against + * possible incorrect matches to child expressions that contain no Vars. + * + * If reqColIdx isn't NULL then it contains sort key column numbers that + * we should match. This is used when making child plans for a MergeAppend; + * it's an error if we can't match the columns. + * * If the pathkeys include expressions that aren't simple Vars, we will * usually need to add resjunk items to the input plan's targetlist to * compute these expressions, since the Sort/MergeAppend node itself won't @@ -3596,6 +3563,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, */ static Plan * prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, + Relids relids, + const AttrNumber *reqColIdx, bool adjust_tlist_in_place, int *p_numsortkeys, AttrNumber **p_sortColIdx, @@ -3626,6 +3595,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, { PathKey *pathkey = (PathKey *) lfirst(i); EquivalenceClass *ec = pathkey->pk_eclass; + EquivalenceMember *em; TargetEntry *tle = NULL; Oid pk_datatype = InvalidOid; Oid sortop; @@ -3645,16 +3615,41 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, Assert(list_length(ec->ec_members) == 1); pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype; } + else if (reqColIdx != NULL) + { + /* + * If we are given a sort column number to match, only consider + * the single TLE at that position. It's possible that there + * is no such TLE, in which case fall through and generate a + * resjunk targetentry (we assume this must have happened in the + * parent plan as well). If there is a TLE but it doesn't match + * the pathkey's EC, we do the same, which is probably the wrong + * thing but we'll leave it to caller to complain about the + * mismatch. + */ + tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]); + if (tle) + { + em = find_ec_member_for_tle(ec, tle, relids); + if (em) + { + /* found expr at right place in tlist */ + pk_datatype = em->em_datatype; + } + else + tle = NULL; + } + } else { /* * Otherwise, we can sort by any non-constant expression listed in - * the pathkey's EquivalenceClass. For now, we take the first one - * that corresponds to an available item in the tlist. If there - * isn't any, use the first one that is an expression in the - * input's vars. (The non-const restriction only matters if the - * EC is below_outer_join; but if it isn't, it won't contain - * consts anyway, else we'd have discarded the pathkey as + * the pathkey's EquivalenceClass. For now, we take the first + * tlist item found in the EC. If there's no match, we'll generate + * a resjunk entry using the first EC member that is an expression + * in the input's vars. (The non-const restriction only matters + * if the EC is below_outer_join; but if it isn't, it won't + * contain consts anyway, else we'd have discarded the pathkey as * redundant.) * * XXX if we have a choice, is there any way of figuring out which @@ -3663,9 +3658,36 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, * in the same equivalence class...) Not clear that we ever will * have an interesting choice in practice, so it may not matter. */ + foreach(j, tlist) + { + tle = (TargetEntry *) lfirst(j); + em = find_ec_member_for_tle(ec, tle, relids); + if (em) + { + /* found expr already in tlist */ + pk_datatype = em->em_datatype; + break; + } + tle = NULL; + } + } + + if (!tle) + { + /* + * No matching tlist item; look for a computable expression. + * Note that we treat Aggrefs as if they were variables; this + * is necessary when attempting to sort the output from an Agg + * node for use in a WindowFunc (since grouping_planner will + * have treated the Aggrefs as variables, too). + */ + Expr *sortexpr = NULL; + foreach(j, ec->ec_members) { EquivalenceMember *em = (EquivalenceMember *) lfirst(j); + List *exprvars; + ListCell *k; /* * We shouldn't be trying to sort by an equivalence class that @@ -3675,91 +3697,56 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, if (em->em_is_const) continue; - tle = tlist_member((Node *) em->em_expr, tlist); - if (tle) - { - pk_datatype = em->em_datatype; - break; /* found expr already in tlist */ - } - /* - * We can also use it if the pathkey expression is a relabel - * of the tlist entry, or vice versa. This is needed for - * binary-compatible cases (cf. make_pathkey_from_sortinfo). - * We prefer an exact match, though, so we do the basic search - * first. + * Ignore child members unless they match the rel being sorted. */ - tle = tlist_member_ignore_relabel((Node *) em->em_expr, tlist); - if (tle) + if (em->em_is_child && + !bms_equal(em->em_relids, relids)) + continue; + + sortexpr = em->em_expr; + exprvars = pull_var_clause((Node *) sortexpr, + PVC_INCLUDE_AGGREGATES, + PVC_INCLUDE_PLACEHOLDERS); + foreach(k, exprvars) + { + if (!tlist_member_ignore_relabel(lfirst(k), tlist)) + break; + } + list_free(exprvars); + if (!k) { pk_datatype = em->em_datatype; - break; /* found expr already in tlist */ + break; /* found usable expression */ } } + if (!j) + elog(ERROR, "could not find pathkey item to sort"); - if (!tle) + /* + * Do we need to insert a Result node? + */ + if (!adjust_tlist_in_place && + !is_projection_capable_plan(lefttree)) { - /* - * No matching tlist item; look for a computable expression. - * Note that we treat Aggrefs as if they were variables; this - * is necessary when attempting to sort the output from an Agg - * node for use in a WindowFunc (since grouping_planner will - * have treated the Aggrefs as variables, too). - */ - Expr *sortexpr = NULL; - - foreach(j, ec->ec_members) - { - EquivalenceMember *em = (EquivalenceMember *) lfirst(j); - List *exprvars; - ListCell *k; - - if (em->em_is_const) - continue; - sortexpr = em->em_expr; - exprvars = pull_var_clause((Node *) sortexpr, - PVC_INCLUDE_AGGREGATES, - PVC_INCLUDE_PLACEHOLDERS); - foreach(k, exprvars) - { - if (!tlist_member_ignore_relabel(lfirst(k), tlist)) - break; - } - list_free(exprvars); - if (!k) - { - pk_datatype = em->em_datatype; - break; /* found usable expression */ - } - } - if (!j) - elog(ERROR, "could not find pathkey item to sort"); - - /* - * Do we need to insert a Result node? - */ - if (!adjust_tlist_in_place && - !is_projection_capable_plan(lefttree)) - { - /* copy needed so we don't modify input's tlist below */ - tlist = copyObject(tlist); - lefttree = (Plan *) make_result(root, tlist, NULL, - lefttree); - } + /* copy needed so we don't modify input's tlist below */ + tlist = copyObject(tlist); + lefttree = (Plan *) make_result(root, tlist, NULL, + lefttree); + } - /* Don't bother testing is_projection_capable_plan again */ - adjust_tlist_in_place = true; + /* Don't bother testing is_projection_capable_plan again */ + adjust_tlist_in_place = true; - /* - * Add resjunk entry to input's tlist - */ - tle = makeTargetEntry(sortexpr, - list_length(tlist) + 1, - NULL, - true); - tlist = lappend(tlist, tle); - lefttree->targetlist = tlist; /* just in case NIL before */ - } + /* + * Add resjunk entry to input's tlist + */ + tle = makeTargetEntry(sortexpr, + list_length(tlist) + 1, + NULL, + true); + tlist = lappend(tlist, tle); + lefttree->targetlist = tlist; /* just in case NIL before */ } /* @@ -3775,23 +3762,14 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, pathkey->pk_strategy, pk_datatype, pk_datatype, pathkey->pk_opfamily); - /* - * The column might already be selected as a sort key, if the pathkeys - * contain duplicate entries. (This can happen in scenarios where - * multiple mergejoinable clauses mention the same var, for example.) - * So enter it only once in the sort arrays. - */ - numsortkeys = add_sort_column(tle->resno, - sortop, - pathkey->pk_eclass->ec_collation, - pathkey->pk_nulls_first, - numsortkeys, - sortColIdx, sortOperators, - collations, nullsFirst); + /* Add the column to the sort arrays */ + sortColIdx[numsortkeys] = tle->resno; + sortOperators[numsortkeys] = sortop; + collations[numsortkeys] = ec->ec_collation; + nullsFirst[numsortkeys] = pathkey->pk_nulls_first; + numsortkeys++; } - Assert(numsortkeys > 0); - /* Return results */ *p_numsortkeys = numsortkeys; *p_sortColIdx = sortColIdx; @@ -3803,6 +3781,57 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, } /* + * find_ec_member_for_tle + * Locate an EquivalenceClass member matching the given TLE, if any + * + * Child EC members are ignored unless they match 'relids'. + */ +static EquivalenceMember * +find_ec_member_for_tle(EquivalenceClass *ec, + TargetEntry *tle, + Relids relids) +{ + Expr *tlexpr; + ListCell *lc; + + /* We ignore binary-compatible relabeling on both ends */ + tlexpr = tle->expr; + while (tlexpr && IsA(tlexpr, RelabelType)) + tlexpr = ((RelabelType *) tlexpr)->arg; + + foreach(lc, ec->ec_members) + { + EquivalenceMember *em = (EquivalenceMember *) lfirst(lc); + Expr *emexpr; + + /* + * We shouldn't be trying to sort by an equivalence class that + * contains a constant, so no need to consider such cases any + * further. + */ + if (em->em_is_const) + continue; + + /* + * Ignore child members unless they match the rel being sorted. + */ + if (em->em_is_child && + !bms_equal(em->em_relids, relids)) + continue; + + /* Match if same expression (after stripping relabel) */ + emexpr = em->em_expr; + while (emexpr && IsA(emexpr, RelabelType)) + emexpr = ((RelabelType *) emexpr)->arg; + + if (equal(emexpr, tlexpr)) + return em; + } + + return NULL; +} + +/* * make_sort_from_pathkeys * Create sort plan to sort according to given pathkeys * @@ -3823,6 +3852,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, /* Compute sort column info, and adjust lefttree as needed */ lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys, + NULL, + NULL, false, &numsortkeys, &sortColIdx, @@ -3854,9 +3885,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) Oid *collations; bool *nullsFirst; - /* - * We will need at most list_length(sortcls) sort columns; possibly less - */ + /* Convert list-ish representation to arrays wanted by executor */ numsortkeys = list_length(sortcls); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); @@ -3864,27 +3893,18 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; - foreach(l, sortcls) { SortGroupClause *sortcl = (SortGroupClause *) lfirst(l); TargetEntry *tle = get_sortgroupclause_tle(sortcl, sub_tlist); - /* - * Check for the possibility of duplicate order-by clauses --- the - * parser should have removed 'em, but no point in sorting - * redundantly. - */ - numsortkeys = add_sort_column(tle->resno, sortcl->sortop, - exprCollation((Node *) tle->expr), - sortcl->nulls_first, - numsortkeys, - sortColIdx, sortOperators, - collations, nullsFirst); + sortColIdx[numsortkeys] = tle->resno; + sortOperators[numsortkeys] = sortcl->sortop; + collations[numsortkeys] = exprCollation((Node *) tle->expr); + nullsFirst[numsortkeys] = sortcl->nulls_first; + numsortkeys++; } - Assert(numsortkeys > 0); - return make_sort(root, lefttree, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, -1.0); @@ -3910,7 +3930,6 @@ make_sort_from_groupcols(PlannerInfo *root, Plan *lefttree) { List *sub_tlist = lefttree->targetlist; - int grpno = 0; ListCell *l; int numsortkeys; AttrNumber *sortColIdx; @@ -3918,9 +3937,7 @@ make_sort_from_groupcols(PlannerInfo *root, Oid *collations; bool *nullsFirst; - /* - * We will need at most list_length(groupcls) sort columns; possibly less - */ + /* Convert list-ish representation to arrays wanted by executor */ numsortkeys = list_length(groupcls); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); @@ -3928,28 +3945,18 @@ make_sort_from_groupcols(PlannerInfo *root, nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; - foreach(l, groupcls) { SortGroupClause *grpcl = (SortGroupClause *) lfirst(l); - TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[grpno]); + TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[numsortkeys]); - /* - * Check for the possibility of duplicate group-by clauses --- the - * parser should have removed 'em, but no point in sorting - * redundantly. - */ - numsortkeys = add_sort_column(tle->resno, grpcl->sortop, - exprCollation((Node *) tle->expr), - grpcl->nulls_first, - numsortkeys, - sortColIdx, sortOperators, - collations, nullsFirst); - grpno++; + sortColIdx[numsortkeys] = tle->resno; + sortOperators[numsortkeys] = grpcl->sortop; + collations[numsortkeys] = exprCollation((Node *) tle->expr); + nullsFirst[numsortkeys] = grpcl->nulls_first; + numsortkeys++; } - Assert(numsortkeys > 0); - return make_sort(root, lefttree, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, -1.0); |