00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "postgres.h"
00030
00031 #include "access/htup_details.h"
00032 #include "catalog/pg_aggregate.h"
00033 #include "catalog/pg_type.h"
00034 #include "nodes/makefuncs.h"
00035 #include "nodes/nodeFuncs.h"
00036 #include "optimizer/clauses.h"
00037 #include "optimizer/cost.h"
00038 #include "optimizer/paths.h"
00039 #include "optimizer/planmain.h"
00040 #include "optimizer/planner.h"
00041 #include "optimizer/subselect.h"
00042 #include "parser/parsetree.h"
00043 #include "parser/parse_clause.h"
00044 #include "utils/lsyscache.h"
00045 #include "utils/syscache.h"
00046
00047
00048 static bool find_minmax_aggs_walker(Node *node, List **context);
00049 static bool build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
00050 Oid eqop, Oid sortop, bool nulls_first);
00051 static void minmax_qp_callback(PlannerInfo *root, void *extra);
00052 static void make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo);
00053 static Node *replace_aggs_with_params_mutator(Node *node, PlannerInfo *root);
00054 static Oid fetch_agg_sort_op(Oid aggfnoid);
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 void
00069 preprocess_minmax_aggregates(PlannerInfo *root, List *tlist)
00070 {
00071 Query *parse = root->parse;
00072 FromExpr *jtnode;
00073 RangeTblRef *rtr;
00074 RangeTblEntry *rte;
00075 List *aggs_list;
00076 ListCell *lc;
00077
00078
00079 Assert(root->minmax_aggs == NIL);
00080
00081
00082 if (!parse->hasAggs)
00083 return;
00084
00085 Assert(!parse->setOperations);
00086 Assert(parse->rowMarks == NIL);
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 if (parse->groupClause || parse->hasWindowFuncs)
00099 return;
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 jtnode = parse->jointree;
00111 while (IsA(jtnode, FromExpr))
00112 {
00113 if (list_length(jtnode->fromlist) != 1)
00114 return;
00115 jtnode = linitial(jtnode->fromlist);
00116 }
00117 if (!IsA(jtnode, RangeTblRef))
00118 return;
00119 rtr = (RangeTblRef *) jtnode;
00120 rte = planner_rt_fetch(rtr->rtindex, root);
00121 if (rte->rtekind == RTE_RELATION)
00122 ;
00123 else if (rte->rtekind == RTE_SUBQUERY && rte->inh)
00124 ;
00125 else
00126 return;
00127
00128
00129
00130
00131
00132 aggs_list = NIL;
00133 if (find_minmax_aggs_walker((Node *) tlist, &aggs_list))
00134 return;
00135 if (find_minmax_aggs_walker(parse->havingQual, &aggs_list))
00136 return;
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 foreach(lc, aggs_list)
00147 {
00148 MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
00149 Oid eqop;
00150 bool reverse;
00151
00152
00153
00154
00155
00156 eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse);
00157 if (!OidIsValid(eqop))
00158 elog(ERROR, "could not find equality operator for ordering operator %u",
00159 mminfo->aggsortop);
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse))
00170 continue;
00171 if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse))
00172 continue;
00173
00174
00175 return;
00176 }
00177
00178
00179
00180
00181
00182
00183 root->minmax_aggs = aggs_list;
00184 }
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202 Plan *
00203 optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
00204 const AggClauseCosts *aggcosts, Path *best_path)
00205 {
00206 Query *parse = root->parse;
00207 Cost total_cost;
00208 Path agg_p;
00209 Plan *plan;
00210 Node *hqual;
00211 ListCell *lc;
00212
00213
00214 if (root->minmax_aggs == NIL)
00215 return NULL;
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225 total_cost = 0;
00226 foreach(lc, root->minmax_aggs)
00227 {
00228 MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
00229
00230 total_cost += mminfo->pathcost;
00231 }
00232
00233 cost_agg(&agg_p, root, AGG_PLAIN, aggcosts,
00234 0, 0,
00235 best_path->startup_cost, best_path->total_cost,
00236 best_path->parent->rows);
00237
00238 if (total_cost > agg_p.total_cost)
00239 return NULL;
00240
00241
00242
00243
00244
00245
00246 foreach(lc, root->minmax_aggs)
00247 {
00248 MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
00249
00250 make_agg_subplan(root, mminfo);
00251 }
00252
00253
00254
00255
00256 tlist = (List *) replace_aggs_with_params_mutator((Node *) tlist, root);
00257 hqual = replace_aggs_with_params_mutator(parse->havingQual, root);
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 mutate_eclass_expressions(root,
00271 replace_aggs_with_params_mutator,
00272 (void *) root,
00273 false);
00274
00275
00276
00277
00278 plan = (Plan *) make_result(root, tlist, hqual, NULL);
00279
00280
00281 add_tlist_costs_to_plan(root, plan, tlist);
00282
00283 return plan;
00284 }
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303 static bool
00304 find_minmax_aggs_walker(Node *node, List **context)
00305 {
00306 if (node == NULL)
00307 return false;
00308 if (IsA(node, Aggref))
00309 {
00310 Aggref *aggref = (Aggref *) node;
00311 Oid aggsortop;
00312 TargetEntry *curTarget;
00313 MinMaxAggInfo *mminfo;
00314 ListCell *l;
00315
00316 Assert(aggref->agglevelsup == 0);
00317 if (list_length(aggref->args) != 1 || aggref->aggorder != NIL)
00318 return true;
00319
00320 curTarget = (TargetEntry *) linitial(aggref->args);
00321
00322 aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
00323 if (!OidIsValid(aggsortop))
00324 return true;
00325
00326 if (contain_mutable_functions((Node *) curTarget->expr))
00327 return true;
00328
00329 if (type_is_rowtype(exprType((Node *) curTarget->expr)))
00330 return true;
00331
00332
00333
00334
00335 foreach(l, *context)
00336 {
00337 mminfo = (MinMaxAggInfo *) lfirst(l);
00338 if (mminfo->aggfnoid == aggref->aggfnoid &&
00339 equal(mminfo->target, curTarget->expr))
00340 return false;
00341 }
00342
00343 mminfo = makeNode(MinMaxAggInfo);
00344 mminfo->aggfnoid = aggref->aggfnoid;
00345 mminfo->aggsortop = aggsortop;
00346 mminfo->target = curTarget->expr;
00347 mminfo->subroot = NULL;
00348 mminfo->path = NULL;
00349 mminfo->pathcost = 0;
00350 mminfo->param = NULL;
00351
00352 *context = lappend(*context, mminfo);
00353
00354
00355
00356
00357
00358 return false;
00359 }
00360 Assert(!IsA(node, SubLink));
00361 return expression_tree_walker(node, find_minmax_aggs_walker,
00362 (void *) context);
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373 static bool
00374 build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
00375 Oid eqop, Oid sortop, bool nulls_first)
00376 {
00377 PlannerInfo *subroot;
00378 Query *parse;
00379 TargetEntry *tle;
00380 NullTest *ntest;
00381 SortGroupClause *sortcl;
00382 Path *cheapest_path;
00383 Path *sorted_path;
00384 double dNumGroups;
00385 Cost path_cost;
00386 double path_fraction;
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396 subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo));
00397 memcpy(subroot, root, sizeof(PlannerInfo));
00398 subroot->parse = parse = (Query *) copyObject(root->parse);
00399
00400 subroot->init_plans = list_copy(root->init_plans);
00401
00402 Assert(subroot->join_info_list == NIL);
00403 Assert(subroot->lateral_info_list == NIL);
00404
00405 Assert(subroot->placeholder_list == NIL);
00406
00407
00408 tle = makeTargetEntry(copyObject(mminfo->target),
00409 (AttrNumber) 1,
00410 pstrdup("agg_target"),
00411 false);
00412 parse->targetList = list_make1(tle);
00413
00414
00415 parse->havingQual = NULL;
00416 subroot->hasHavingQual = false;
00417 parse->distinctClause = NIL;
00418 parse->hasDistinctOn = false;
00419 parse->hasAggs = false;
00420
00421
00422 ntest = makeNode(NullTest);
00423 ntest->nulltesttype = IS_NOT_NULL;
00424 ntest->arg = copyObject(mminfo->target);
00425
00426 ntest->argisrow = false;
00427
00428
00429 if (!list_member((List *) parse->jointree->quals, ntest))
00430 parse->jointree->quals = (Node *)
00431 lcons(ntest, (List *) parse->jointree->quals);
00432
00433
00434 sortcl = makeNode(SortGroupClause);
00435 sortcl->tleSortGroupRef = assignSortGroupRef(tle, parse->targetList);
00436 sortcl->eqop = eqop;
00437 sortcl->sortop = sortop;
00438 sortcl->nulls_first = nulls_first;
00439 sortcl->hashable = false;
00440 parse->sortClause = list_make1(sortcl);
00441
00442
00443 parse->limitOffset = NULL;
00444 parse->limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
00445 sizeof(int64),
00446 Int64GetDatum(1), false,
00447 FLOAT8PASSBYVAL);
00448
00449
00450
00451
00452
00453 query_planner(subroot, parse->targetList, 1.0, 1.0,
00454 minmax_qp_callback, NULL,
00455 &cheapest_path, &sorted_path, &dNumGroups);
00456
00457
00458
00459
00460
00461
00462
00463 if (!sorted_path)
00464 {
00465 if (cheapest_path &&
00466 pathkeys_contained_in(subroot->sort_pathkeys,
00467 cheapest_path->pathkeys))
00468 sorted_path = cheapest_path;
00469 else
00470 return false;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479 if (sorted_path->parent->rows > 1.0)
00480 path_fraction = 1.0 / sorted_path->parent->rows;
00481 else
00482 path_fraction = 1.0;
00483
00484 path_cost = sorted_path->startup_cost +
00485 path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
00486
00487
00488 mminfo->subroot = subroot;
00489 mminfo->path = sorted_path;
00490 mminfo->pathcost = path_cost;
00491
00492 return true;
00493 }
00494
00495
00496
00497
00498 static void
00499 minmax_qp_callback(PlannerInfo *root, void *extra)
00500 {
00501 root->group_pathkeys = NIL;
00502 root->window_pathkeys = NIL;
00503 root->distinct_pathkeys = NIL;
00504
00505 root->sort_pathkeys =
00506 make_pathkeys_for_sortclauses(root,
00507 root->parse->sortClause,
00508 root->parse->targetList);
00509
00510 root->query_pathkeys = root->sort_pathkeys;
00511 }
00512
00513
00514
00515
00516 static void
00517 make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo)
00518 {
00519 PlannerInfo *subroot = mminfo->subroot;
00520 Query *subparse = subroot->parse;
00521 Plan *plan;
00522
00523
00524
00525
00526
00527 plan = create_plan(subroot, mminfo->path);
00528
00529 plan->targetlist = subparse->targetList;
00530
00531 plan = (Plan *) make_limit(plan,
00532 subparse->limitOffset,
00533 subparse->limitCount,
00534 0, 1);
00535
00536
00537
00538
00539 mminfo->param =
00540 SS_make_initplan_from_plan(subroot, plan,
00541 exprType((Node *) mminfo->target),
00542 -1,
00543 exprCollation((Node *) mminfo->target));
00544
00545
00546
00547
00548
00549
00550
00551
00552 root->init_plans = list_concat_unique_ptr(root->init_plans,
00553 subroot->init_plans);
00554 }
00555
00556
00557
00558
00559 static Node *
00560 replace_aggs_with_params_mutator(Node *node, PlannerInfo *root)
00561 {
00562 if (node == NULL)
00563 return NULL;
00564 if (IsA(node, Aggref))
00565 {
00566 Aggref *aggref = (Aggref *) node;
00567 TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
00568 ListCell *lc;
00569
00570 foreach(lc, root->minmax_aggs)
00571 {
00572 MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
00573
00574 if (mminfo->aggfnoid == aggref->aggfnoid &&
00575 equal(mminfo->target, curTarget->expr))
00576 return (Node *) mminfo->param;
00577 }
00578 elog(ERROR, "failed to re-find MinMaxAggInfo record");
00579 }
00580 Assert(!IsA(node, SubLink));
00581 return expression_tree_mutator(node, replace_aggs_with_params_mutator,
00582 (void *) root);
00583 }
00584
00585
00586
00587
00588
00589 static Oid
00590 fetch_agg_sort_op(Oid aggfnoid)
00591 {
00592 HeapTuple aggTuple;
00593 Form_pg_aggregate aggform;
00594 Oid aggsortop;
00595
00596
00597 aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggfnoid));
00598 if (!HeapTupleIsValid(aggTuple))
00599 return InvalidOid;
00600 aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
00601 aggsortop = aggform->aggsortop;
00602 ReleaseSysCache(aggTuple);
00603
00604 return aggsortop;
00605 }