diff -durpN pgsql.orig/src/backend/executor/nodeAppend.c pgsql/src/backend/executor/nodeAppend.c --- pgsql.orig/src/backend/executor/nodeAppend.c 2010-07-13 10:51:01.000000000 +0200 +++ pgsql/src/backend/executor/nodeAppend.c 2010-09-03 10:50:49.000000000 +0200 @@ -59,9 +59,25 @@ #include "executor/execdebug.h" #include "executor/nodeAppend.h" +#include "utils/lsyscache.h" +#include "access/nbtree.h" + +/* It gets quite confusing having a heap array (indexed by integers) which + * contains integers which index into the slots array. These typedefs try to + * clear it up but without making simple inline accessing functions they don't + * actually produce any warnings on mistakes */ + +typedef int SlotNumber; +typedef int HeapPosition; + +#define WHICHPLAN_PLANS_UNINITIALIZED (-1) static bool exec_append_initialize_next(AppendState *appendstate); +static int heap_compare_slots(AppendState *node, SlotNumber slot1, SlotNumber slot2); + +static void heap_siftup_slot(AppendState *node); +static void heap_insert_slot(AppendState *node, SlotNumber new_slot); /* ---------------------------------------------------------------- * exec_append_initialize_next @@ -142,6 +158,7 @@ ExecInitAppend(Append *node, EState *est appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; appendstate->as_nplans = nplans; + appendstate->as_is_ordered = node->isOrdered; /* * Miscellaneous initialization @@ -175,11 +192,53 @@ ExecInitAppend(Append *node, EState *est ExecAssignResultTypeFromTL(&appendstate->ps); appendstate->ps.ps_ProjInfo = NULL; - /* - * initialize to scan first subplan - */ - appendstate->as_whichplan = 0; - exec_append_initialize_next(appendstate); + if (!appendstate->as_is_ordered) + { + /* + * initialize to scan first subplan + */ + appendstate->as_whichplan = 0; + exec_append_initialize_next(appendstate); + } + else + { + /* set up scan keys and initialize *all* the subnodes */ + int i; + + appendstate->as_nkeys = node->numCols; + appendstate->as_scankeys = palloc(sizeof(ScanKeyData) * node->numCols); + appendstate->as_slots = palloc(sizeof(TupleTableSlot *) * nplans); + appendstate->as_heap = palloc(sizeof(int) * nplans); + appendstate->as_heap_size = 0; + + for (i=0; i < nplans; i++) + { + appendstate->as_whichplan = i; + exec_append_initialize_next(appendstate); + } + + appendstate->as_whichplan = WHICHPLAN_PLANS_UNINITIALIZED; + + for (i = 0; i < node->numCols; i++) + { + Oid sortFunction; + bool reverse; + + get_compare_function_for_ordering_op(node->sortOperators[i], + &sortFunction, &reverse); + + ScanKeyInit(&appendstate->as_scankeys[i], + node->sortColIdx[i], + InvalidStrategy, + sortFunction, + (Datum)0); + + if (reverse) + appendstate->as_scankeys[i].sk_flags |= SK_BT_DESC; + if (node->nullsFirst[i]) + appendstate->as_scankeys[i].sk_flags |= SK_BT_NULLS_FIRST; + } + } return appendstate; } @@ -193,45 +252,169 @@ ExecInitAppend(Append *node, EState *est TupleTableSlot * ExecAppend(AppendState *node) { - for (;;) + if (!node->as_is_ordered) { - PlanState *subnode; - TupleTableSlot *result; + for (;;) + { + PlanState *subnode; + TupleTableSlot *result; - /* - * figure out which subplan we are currently processing - */ - subnode = node->appendplans[node->as_whichplan]; + /* + * figure out which subplan we are currently processing + */ + subnode = node->appendplans[node->as_whichplan]; - /* - * get a tuple from the subplan - */ - result = ExecProcNode(subnode); + /* + * get a tuple from the subplan + */ + result = ExecProcNode(subnode); + + if (!TupIsNull(result)) + { + /* + * If the subplan gave us something then return it as-is. We do + * NOT make use of the result slot that was set up in + * ExecInitAppend; there's no need for it. + */ + return result; + } - if (!TupIsNull(result)) - { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * Go on to the "next" subplan in the appropriate direction. If no + * more subplans, return the empty slot set up for us by + * ExecInitAppend. */ - return result; + if (ScanDirectionIsForward(node->ps.state->es_direction)) + node->as_whichplan++; + else + node->as_whichplan--; + if (!exec_append_initialize_next(node)) + return ExecClearTuple(node->ps.ps_ResultTupleSlot); + + /* Else loop back and try to get a tuple from the new subplan */ } + } + else + { + TupleTableSlot *result; + SlotNumber i; - /* - * Go on to the "next" subplan in the appropriate direction. If no - * more subplans, return the empty slot set up for us by - * ExecInitAppend. - */ - if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; - else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) - return ExecClearTuple(node->ps.ps_ResultTupleSlot); + if (node->as_whichplan == WHICHPLAN_PLANS_UNINITIALIZED) + { + for (i = 0; i < node->as_nplans; i++) + { + node->as_slots[i] = ExecProcNode(node->appendplans[i]); + if (!TupIsNull(node->as_slots[i])) + heap_insert_slot(node, i); + } + } + else + { + i = node->as_whichplan; + node->as_slots[i] = ExecProcNode(node->appendplans[i]); + if (TupIsNull(node->as_slots[i])) + ; + else if (node->as_heap_size <= 0 || heap_compare_slots(node, node->as_heap[0], i) >= 0) + return node->as_slots[i]; + else + heap_insert_slot(node, i); + } - /* Else loop back and try to get a tuple from the new subplan */ + if (node->as_heap_size > 0) + { + i = node->as_heap[0]; + node->as_whichplan = i; + heap_siftup_slot(node); + result = node->as_slots[i]; + } else { + result = ExecClearTuple(node->ps.ps_ResultTupleSlot); + } + + return result; + } +} + +static void +heap_insert_slot(AppendState *node, SlotNumber new_slot) +{ + HeapPosition j; + SlotNumber *heap; + + Assert(!TupIsNull(node->as_slots[new_slot])); + + j = node->as_heap_size++; + heap = node->as_heap; + + while (j > 0) + { + int i = (j-1)/2; + if (heap_compare_slots(node, new_slot, node->as_heap[i]) >= 0) + break; + heap[j] = heap[i]; + j = i; } + heap[j] = new_slot; +} + +static void +heap_siftup_slot(AppendState *node) +{ + HeapPosition i=0, j, n; + + if (--node->as_heap_size <= 0) + return; + n = node->as_heap_size; + + for (;;) + { + j = 2 * i + 1; + if (j >= n) + break; + if (j+1 < n && heap_compare_slots(node, node->as_heap[j], node->as_heap[j+1]) > 0) + j++; + if (heap_compare_slots(node, node->as_heap[n], node->as_heap[j]) <= 0) + break; + node->as_heap[i] = node->as_heap[j]; + i = j; + } + node->as_heap[i] = node->as_heap[n]; +} + +static int +heap_compare_slots(AppendState *node, SlotNumber slot1, SlotNumber slot2) +{ + int nkey; + TupleTableSlot *s1, *s2; + + Assert(slot1 < node->as_nplans); + Assert(slot2 < node->as_nplans); + + s1 = node->as_slots[slot1]; + s2 = node->as_slots[slot2]; + Assert(!TupIsNull(s1)); + Assert(!TupIsNull(s2)); + + for (nkey = 0; nkey < node->as_nkeys; nkey++) + { + ScanKey scankey = node->as_scankeys + nkey; + AttrNumber attno = scankey->sk_attno; + Datum d1, d2; + bool n1, n2; + int compare; + + d1 = slot_getattr(s1, attno, &n1); + d2 = slot_getattr(s2, attno, &n2); + + if (n1 && !n2) + return (scankey->sk_flags & SK_BT_NULLS_FIRST) ? -1 : 1; + if (!n1 && n2) + return (scankey->sk_flags & SK_BT_NULLS_FIRST) ? 1 : -1; + + compare = FunctionCall2(&scankey->sk_func, d1, d2); + if (compare != 0) + return (scankey->sk_flags & SK_BT_DESC) ? -compare : compare; + } + return 0; } /* ---------------------------------------------------------------- diff -durpN pgsql.orig/src/backend/optimizer/path/allpaths.c pgsql/src/backend/optimizer/path/allpaths.c --- pgsql.orig/src/backend/optimizer/path/allpaths.c 2010-03-29 00:59:32.000000000 +0200 +++ pgsql/src/backend/optimizer/path/allpaths.c 2010-09-03 10:55:18.000000000 +0200 @@ -51,7 +51,7 @@ static void set_plain_rel_pathlist(Plann RangeTblEntry *rte); static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); -static void set_dummy_rel_pathlist(RelOptInfo *rel); +static void set_dummy_rel_pathlist(PlannerInfo *root, RelOptInfo *rel); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -222,7 +222,7 @@ set_plain_rel_pathlist(PlannerInfo *root if (rel->reloptkind == RELOPT_BASEREL && relation_excluded_by_constraints(root, rel, rte)) { - set_dummy_rel_pathlist(rel); + set_dummy_rel_pathlist(root, rel); return; } @@ -267,6 +267,22 @@ set_plain_rel_pathlist(PlannerInfo *root set_cheapest(rel); } + +/* It's possible that the child is itself an appendrel, in which case + * we can "cut out the middleman" and just add its child paths to our + * own list. (We don't try to do this earlier because we need to + * apply both levels of transformation to the quals.) + */ + +#define LAPPEND_PATH_FLATTEN_APPENDPATHS(subpaths_, childpath_) \ + do { \ + if (IsA((childpath_), AppendPath)) \ + (subpaths_) = list_concat((subpaths_), \ + ((AppendPath *) (childpath_))->subpaths); \ + else \ + subpaths_ = lappend(subpaths_, childpath_); \ + } while (0) + /* * set_append_rel_pathlist * Build access paths for an "append relation" @@ -283,12 +299,13 @@ set_append_rel_pathlist(PlannerInfo *roo Index rti, RangeTblEntry *rte) { int parentRTindex = rti; - List *subpaths = NIL; + List *best_subpaths = NIL; /* list of paths */ + List *all_pathkeys = NIL; /* union of all pathkeys in sub paths */ double parent_rows; double parent_size; double *parent_attrsizes; int nattrs; - ListCell *l; + ListCell *l, *ll, *lll; /* * Initialize to compute size estimates for whole append relation. @@ -366,7 +383,7 @@ set_append_rel_pathlist(PlannerInfo *roo * Restriction reduces to constant FALSE or constant NULL after * substitution, so this child need not be scanned. */ - set_dummy_rel_pathlist(childrel); + set_dummy_rel_pathlist(root, childrel); continue; } childquals = make_ands_implicit((Expr *) childqual); @@ -381,7 +398,7 @@ set_append_rel_pathlist(PlannerInfo *roo * appendrel. Mark it with a dummy cheapest-path though, in case * best_appendrel_indexscan() looks at it later. */ - set_dummy_rel_pathlist(childrel); + set_dummy_rel_pathlist(root, childrel); continue; } @@ -397,7 +414,9 @@ set_append_rel_pathlist(PlannerInfo *roo * We have to make child entries in the EquivalenceClass data * structures as well. */ +#ifdef FIXME if (rel->has_eclass_joins) +#endif { add_child_rel_equivalences(root, appinfo, rel, childrel); childrel->has_eclass_joins = true; @@ -413,21 +432,34 @@ set_append_rel_pathlist(PlannerInfo *roo /* * Compute the child's access paths, and add the cheapest one to the - * Append path we are constructing for the parent. - * - * It's possible that the child is itself an appendrel, in which case - * we can "cut out the middleman" and just add its child paths to our - * own list. (We don't try to do this earlier because we need to - * apply both levels of transformation to the quals.) + * list of cheap paths (best_paths) and the pathkeys of all the paths + * to the union, all_pathkeys. */ set_rel_pathlist(root, childrel, childRTindex, childRTE); childpath = childrel->cheapest_total_path; - if (IsA(childpath, AppendPath)) - subpaths = list_concat(subpaths, - ((AppendPath *) childpath)->subpaths); - else - subpaths = lappend(subpaths, childpath); + LAPPEND_PATH_FLATTEN_APPENDPATHS(best_subpaths, childpath); + + /* gather up all the pathkeys of all sub-paths + * FIXME -- this is O(n^2)! + */ + foreach(ll, childrel->pathlist) + { + ListCell *lll; + Path *childpath = (Path *) lfirst(ll); + bool found = false; + + foreach (lll, all_pathkeys) + { + List *existing_pathkeys = (List *)lfirst(lll); + + if (compare_pathkeys(existing_pathkeys, + childpath->pathkeys) == PATHKEYS_EQUAL) + found = true; + } + if (!found) + all_pathkeys = lappend(all_pathkeys, childpath->pathkeys); + } /* * Accumulate size information from each child. @@ -487,10 +519,74 @@ set_append_rel_pathlist(PlannerInfo *roo * the parent rel. (Note: this is correct even if we have zero or one * live subpath due to constraint exclusion.) */ - add_path(rel, (Path *) create_append_path(rel, subpaths)); + if (list_length(best_subpaths) == 1) + { + /* special case for a singleton append node, just grab the pathlist + * from the child rels and use it directly. + */ + rel->pathlist = ((Path*) linitial(best_subpaths))->parent->pathlist; + set_cheapest(rel); + return; + } - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); + /* First add the unordered append path */ + add_path(rel, (Path *) create_append_path(root, rel, best_subpaths, NIL)); + + /* Now try to add various ordered append paths */ + foreach (ll, all_pathkeys) + { + List *pathkeys = (List *)lfirst(ll); + List *startup_subpaths = NIL; /* subpaths for minimal startup cost append path */ + List *total_subpaths = NIL; /* subpaths for minimal total cost append path */ + + /* populate both subpaths lists based on this pathkeys list */ + foreach(lll, root->append_rel_list) + { + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lll); + RelOptInfo *childrel; + Path *childpath; + + /* append_rel_list contains all append rels; ignore others */ + if (appinfo->parent_relid != parentRTindex) + continue; + + childrel = find_base_rel(root, appinfo->child_relid); + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + + /* First the cheapest startup cost */ + childpath = get_cheapest_path_for_pathkeys(childrel->pathlist, + pathkeys, + TOTAL_COST); + + /* If we can't find any plans with the right order just add the + * cheapest total plan to both paths, we'll have to sort it + * anyways. Perhaps consider not generating a startup path for such + * pathkeys since it'll be unlikely to be the cheapest startup + * cost. */ + + if (!childpath) { + childpath = childrel->cheapest_total_path; + LAPPEND_PATH_FLATTEN_APPENDPATHS(startup_subpaths, childpath); + LAPPEND_PATH_FLATTEN_APPENDPATHS(total_subpaths, childpath); + continue; + } + + LAPPEND_PATH_FLATTEN_APPENDPATHS(total_subpaths, childpath); + + /* Also do the the cheapest startup cost */ + childpath = get_cheapest_path_for_pathkeys(childrel->pathlist, + pathkeys, + STARTUP_COST); + Assert(childpath); /* above special case ought to have caught this */ + LAPPEND_PATH_FLATTEN_APPENDPATHS(startup_subpaths, childpath); + } + + add_path(rel, (Path *) create_append_path(root, rel, startup_subpaths, pathkeys)); + add_path(rel, (Path *) create_append_path(root, rel, total_subpaths, pathkeys)); + } + + /* Select cheapest path */ + set_cheapest(rel); } /* @@ -501,13 +597,13 @@ set_append_rel_pathlist(PlannerInfo *roo * AppendPath with no members (see also IS_DUMMY_PATH macro). */ static void -set_dummy_rel_pathlist(RelOptInfo *rel) +set_dummy_rel_pathlist(PlannerInfo *root, RelOptInfo *rel) { /* Set dummy size estimates --- we leave attr_widths[] as zeroes */ rel->rows = 0; rel->width = 0; - add_path(rel, (Path *) create_append_path(rel, NIL)); + add_path(rel, (Path *) create_append_path(root, rel, NIL, NIL)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); diff -durpN pgsql.orig/src/backend/optimizer/path/equivclass.c pgsql/src/backend/optimizer/path/equivclass.c --- pgsql.orig/src/backend/optimizer/path/equivclass.c 2010-02-26 03:00:44.000000000 +0100 +++ pgsql/src/backend/optimizer/path/equivclass.c 2010-09-03 10:20:58.000000000 +0200 @@ -1634,9 +1634,10 @@ add_child_rel_equivalences(PlannerInfo * * Won't generate joinclauses if const or single-member (the latter * test covers the volatile case too) */ +#if FIXME if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1) continue; - +#endif /* No point in searching if parent rel not mentioned in eclass */ if (!bms_is_subset(parent_rel->relids, cur_ec->ec_relids)) continue; diff -durpN pgsql.orig/src/backend/optimizer/path/joinpath.c pgsql/src/backend/optimizer/path/joinpath.c --- pgsql.orig/src/backend/optimizer/path/joinpath.c 2010-04-19 09:42:28.000000000 +0200 +++ pgsql/src/backend/optimizer/path/joinpath.c 2010-09-03 10:20:58.000000000 +0200 @@ -955,7 +955,7 @@ best_appendrel_indexscan(PlannerInfo *ro return NULL; /* Form and return the completed Append path. */ - return (Path *) create_append_path(rel, append_paths); + return (Path *) create_append_path(root, rel, append_paths, NIL); } /* diff -durpN pgsql.orig/src/backend/optimizer/path/joinrels.c pgsql/src/backend/optimizer/path/joinrels.c --- pgsql.orig/src/backend/optimizer/path/joinrels.c 2010-02-26 03:00:45.000000000 +0100 +++ pgsql/src/backend/optimizer/path/joinrels.c 2010-09-03 10:20:58.000000000 +0200 @@ -932,7 +932,7 @@ mark_dummy_rel(RelOptInfo *rel) rel->pathlist = NIL; /* Set up the dummy path */ - add_path(rel, (Path *) create_append_path(rel, NIL)); + add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL)); /* Set or update cheapest_total_path */ set_cheapest(rel); diff -durpN pgsql.orig/src/backend/optimizer/plan/createplan.c pgsql/src/backend/optimizer/plan/createplan.c --- pgsql.orig/src/backend/optimizer/plan/createplan.c 2010-07-13 10:51:03.000000000 +0200 +++ pgsql/src/backend/optimizer/plan/createplan.c 2010-09-03 10:27:46.000000000 +0200 @@ -24,6 +24,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/paths.h" #include "optimizer/plancat.h" #include "optimizer/planmain.h" #include "optimizer/predtest.h" @@ -609,11 +610,19 @@ create_append_plan(PlannerInfo *root, Ap foreach(subpaths, best_path->subpaths) { Path *subpath = (Path *) lfirst(subpaths); + Plan *subplan; - subplans = lappend(subplans, create_plan_recurse(root, subpath)); + subplan = create_plan(root, subpath); + if (!pathkeys_contained_in(best_path->path.pathkeys, subpath->pathkeys)) + subplan = (Plan *)make_sort_from_pathkeys(root, + subplan, + best_path->path.pathkeys, + -1.0); + + subplans = lappend(subplans, subplan); } - plan = make_append(subplans, tlist); + plan = make_append(root, subplans, tlist, best_path->path.pathkeys); return (Plan *) plan; } @@ -2784,7 +2793,8 @@ make_worktablescan(List *qptlist, } Append * -make_append(List *appendplans, List *tlist) +make_append(PlannerInfo *root, List *appendplans, + List *tlist, List *pathkeys) { Append *node = makeNode(Append); Plan *plan = &node->plan; @@ -2792,9 +2802,11 @@ make_append(List *appendplans, List *tli ListCell *subnode; /* - * Compute cost as sum of subplan costs. We charge nothing extra for the - * Append itself, which perhaps is too optimistic, but since it doesn't do - * any selection or projection, it is a pretty cheap node. + * Compute cost as sum of subplan costs. We charge nothing extra for a + * plain Append itself, which perhaps is too optimistic, but since it + * doesn't do any selection or projection, it is a pretty cheap node. In + * the case of an ordered append we construct an equivalent bounded Sort + * node and steal the cost calculations from it. */ plan->startup_cost = 0; plan->total_cost = 0; @@ -2804,8 +2816,10 @@ make_append(List *appendplans, List *tli { Plan *subplan = (Plan *) lfirst(subnode); - if (subnode == list_head(appendplans)) /* first node? */ - plan->startup_cost = subplan->startup_cost; + /* If it's ordered then the startup cost is the sum of the startup + * costs, otherwise it's the startup cost of just the first plan */ + if (pathkeys || subnode == list_head(appendplans)) + plan->startup_cost += subplan->startup_cost; plan->total_cost += subplan->total_cost; plan->plan_rows += subplan->plan_rows; total_size += subplan->plan_width * subplan->plan_rows; @@ -2821,6 +2835,30 @@ make_append(List *appendplans, List *tli plan->righttree = NULL; node->appendplans = appendplans; + if (!pathkeys) + node->isOrdered = false; + else + { + /* generate a throwaway sort node to find the sort functions */ + Sort *tmp = make_sort_from_pathkeys(root, (Plan*)node, pathkeys, list_length(appendplans)); + + node->isOrdered = true; + + node->numCols = tmp->numCols; + node->sortColIdx = tmp->sortColIdx; + node->sortOperators = tmp->sortOperators; + node->nullsFirst = tmp->nullsFirst; + + /* a limited sort is the same kind of work (bounded heap sort) as an + * ordered append with the bound set to the number of plans, so we just + * use that to calculate the total cost. The startup cost is just the + * sum of the startup costs of the nodes plus a bit to build the heap + * (similar to processing that many rows in the bounded sort case). + */ + plan->total_cost = tmp->plan.total_cost - (enable_sort ? 0 : disable_cost); + plan->startup_cost += plan->total_cost / plan->plan_rows * list_length(appendplans); + } + return node; } @@ -3182,9 +3220,10 @@ make_sort_from_pathkeys(PlannerInfo *roo { EquivalenceMember *em = (EquivalenceMember *) lfirst(j); +#ifdef FIXME if (em->em_is_const || em->em_is_child) continue; - +#endif tle = tlist_member((Node *) em->em_expr, tlist); if (tle) { @@ -3218,8 +3257,10 @@ make_sort_from_pathkeys(PlannerInfo *roo List *exprvars; ListCell *k; +#ifdef FIXME if (em->em_is_const || em->em_is_child) continue; +#endif sortexpr = em->em_expr; exprvars = pull_var_clause((Node *) sortexpr, PVC_INCLUDE_PLACEHOLDERS); diff -durpN pgsql.orig/src/backend/optimizer/prep/prepunion.c pgsql/src/backend/optimizer/prep/prepunion.c --- pgsql.orig/src/backend/optimizer/prep/prepunion.c 2010-07-11 11:14:51.000000000 +0200 +++ pgsql/src/backend/optimizer/prep/prepunion.c 2010-09-03 10:22:43.000000000 +0200 @@ -449,7 +449,7 @@ generate_union_plan(SetOperationStmt *op /* * Append the child results together. */ - plan = (Plan *) make_append(planlist, tlist); + plan = (Plan *) make_append(root, planlist, tlist, NIL); /* * For UNION ALL, we just need the Append plan. For UNION, need to add @@ -540,7 +540,7 @@ generate_nonunion_plan(SetOperationStmt /* * Append the child results together. */ - plan = (Plan *) make_append(planlist, tlist); + plan = (Plan *) make_append(root, planlist, tlist, NIL); /* Identify the grouping semantics */ groupList = generate_setop_grouplist(op, tlist); diff -durpN pgsql.orig/src/backend/optimizer/util/pathnode.c pgsql/src/backend/optimizer/util/pathnode.c --- pgsql.orig/src/backend/optimizer/util/pathnode.c 2010-03-29 00:59:33.000000000 +0200 +++ pgsql/src/backend/optimizer/util/pathnode.c 2010-09-03 10:20:58.000000000 +0200 @@ -635,31 +635,74 @@ create_tidscan_path(PlannerInfo *root, R /* * create_append_path * Creates a path corresponding to an Append plan, returning the - * pathnode. + * pathnode. + * + * Note that we must support create_append_path(null, rel, nil, nil) which + * is used to create dummy plans. */ +#define LOG2(x) (log(x) / 0.693147180559945) + AppendPath * -create_append_path(RelOptInfo *rel, List *subpaths) +create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *pathkeys) { AppendPath *pathnode = makeNode(AppendPath); ListCell *l; + Cost total_cost=0, startup_cost=0; + Cost this_total_cost, this_startup_cost; + int npaths = 0; pathnode->path.pathtype = T_Append; pathnode->path.parent = rel; - pathnode->path.pathkeys = NIL; /* result is always considered - * unsorted */ + pathnode->path.pathkeys = pathkeys; pathnode->subpaths = subpaths; - pathnode->path.startup_cost = 0; - pathnode->path.total_cost = 0; foreach(l, subpaths) { Path *subpath = (Path *) lfirst(l); - if (l == list_head(subpaths)) /* first node? */ - pathnode->path.startup_cost = subpath->startup_cost; - pathnode->path.total_cost += subpath->total_cost; + if (pathkeys_contained_in(pathkeys, subpath->pathkeys)) + { + this_startup_cost = subpath->startup_cost; + this_total_cost = subpath->total_cost; + } else { + Path sort_path; /* dummy for result of cost_sort */ + cost_sort(&sort_path, + root, + pathkeys, + subpath->total_cost, + subpath->parent->tuples, + subpath->parent->width, + -1.0); + this_total_cost = sort_path.total_cost; + this_startup_cost = sort_path.startup_cost; + } + + npaths++; + total_cost += this_total_cost; + /* If it's unsorted the startup cost is just the first subpath's + * startup cost, otherwise it's the sum of all startup costs */ + if (pathkeys != NIL || l == list_head(subpaths)) + startup_cost += this_startup_cost; + } + + /* The cost of merging using a heapsort */ + if (pathkeys != NIL) + { + Path sort_path; + cost_sort(&sort_path, + root, + pathkeys, + total_cost, + rel->rows, + rel->width, + npaths); + total_cost = sort_path.total_cost - (enable_sort ? 0 : disable_cost); + startup_cost += total_cost / rel->rows * npaths; } + pathnode->path.total_cost = total_cost; + pathnode->path.startup_cost = startup_cost; + return pathnode; } diff -durpN pgsql.orig/src/include/nodes/execnodes.h pgsql/src/include/nodes/execnodes.h --- pgsql.orig/src/include/nodes/execnodes.h 2010-07-29 11:33:00.000000000 +0200 +++ pgsql/src/include/nodes/execnodes.h 2010-09-03 10:46:22.000000000 +0200 @@ -1047,6 +1047,13 @@ typedef struct AppendState PlanState **appendplans; /* array of PlanStates for my inputs */ int as_nplans; int as_whichplan; + + bool as_is_ordered; + int as_nkeys; + ScanKey as_scankeys; /* array of length as_nkeys */ + TupleTableSlot **as_slots; /* array of length as_nplans */ + int *as_heap; /* array of length as_nplans */ + int as_heap_size; /* how many slots are present in the heap */ } AppendState; /* ---------------- diff -durpN pgsql.orig/src/include/nodes/plannodes.h pgsql/src/include/nodes/plannodes.h --- pgsql.orig/src/include/nodes/plannodes.h 2010-07-13 10:51:07.000000000 +0200 +++ pgsql/src/include/nodes/plannodes.h 2010-09-03 10:47:41.000000000 +0200 @@ -180,6 +180,14 @@ typedef struct Append { Plan plan; List *appendplans; + bool isOrdered; + + /* used only if the append is an ordered append */ + + int numCols; /* number of sort-key columns */ + AttrNumber *sortColIdx; /* their indexes in the target list */ + Oid *sortOperators; /* OIDs of operators to sort them by */ + bool *nullsFirst; /* NULLS FIRST/LAST directions */ } Append; /* ---------------- diff -durpN pgsql.orig/src/include/optimizer/pathnode.h pgsql/src/include/optimizer/pathnode.h --- pgsql.orig/src/include/optimizer/pathnode.h 2010-03-29 00:59:33.000000000 +0200 +++ pgsql/src/include/optimizer/pathnode.h 2010-09-03 10:20:58.000000000 +0200 @@ -46,7 +46,10 @@ extern BitmapOrPath *create_bitmap_or_pa List *bitmapquals); extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals); -extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths); +extern AppendPath *create_append_path(PlannerInfo *root, + RelOptInfo *rel, + List *subpaths, + List *pathkeys); extern ResultPath *create_result_path(List *quals); extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath); extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel, diff -durpN pgsql.orig/src/include/optimizer/planmain.h pgsql/src/include/optimizer/planmain.h --- pgsql.orig/src/include/optimizer/planmain.h 2010-03-29 00:59:33.000000000 +0200 +++ pgsql/src/include/optimizer/planmain.h 2010-09-03 10:44:50.000000000 +0200 @@ -43,7 +43,8 @@ extern Node *fix_indexqual_operand(Node extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, Plan *subplan, List *subrtable, List *subrowmark); -extern Append *make_append(List *appendplans, List *tlist); +extern Append *make_append(PlannerInfo *root, List *appendplans, + List *tlist, List *pathkeys); extern RecursiveUnion *make_recursive_union(List *tlist, Plan *lefttree, Plan *righttree, int wtParam, List *distinctList, long numGroups);