| 1 | /*------------------------------------------------------------------------- | 
|---|
| 2 | * | 
|---|
| 3 | * planagg.c | 
|---|
| 4 | *	  Special planning for aggregate queries. | 
|---|
| 5 | * | 
|---|
| 6 | * This module tries to replace MIN/MAX aggregate functions by subqueries | 
|---|
| 7 | * of the form | 
|---|
| 8 | *		(SELECT col FROM tab | 
|---|
| 9 | *		 WHERE col IS NOT NULL AND existing-quals | 
|---|
| 10 | *		 ORDER BY col ASC/DESC | 
|---|
| 11 | *		 LIMIT 1) | 
|---|
| 12 | * Given a suitable index on tab.col, this can be much faster than the | 
|---|
| 13 | * generic scan-all-the-rows aggregation plan.  We can handle multiple | 
|---|
| 14 | * MIN/MAX aggregates by generating multiple subqueries, and their | 
|---|
| 15 | * orderings can be different.  However, if the query contains any | 
|---|
| 16 | * non-optimizable aggregates, there's no point since we'll have to | 
|---|
| 17 | * scan all the rows anyway. | 
|---|
| 18 | * | 
|---|
| 19 | * | 
|---|
| 20 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group | 
|---|
| 21 | * Portions Copyright (c) 1994, Regents of the University of California | 
|---|
| 22 | * | 
|---|
| 23 | * | 
|---|
| 24 | * IDENTIFICATION | 
|---|
| 25 | *	  src/backend/optimizer/plan/planagg.c | 
|---|
| 26 | * | 
|---|
| 27 | *------------------------------------------------------------------------- | 
|---|
| 28 | */ | 
|---|
| 29 | #include "postgres.h" | 
|---|
| 30 |  | 
|---|
| 31 | #include "access/htup_details.h" | 
|---|
| 32 | #include "catalog/pg_aggregate.h" | 
|---|
| 33 | #include "catalog/pg_type.h" | 
|---|
| 34 | #include "nodes/makefuncs.h" | 
|---|
| 35 | #include "nodes/nodeFuncs.h" | 
|---|
| 36 | #include "optimizer/clauses.h" | 
|---|
| 37 | #include "optimizer/cost.h" | 
|---|
| 38 | #include "optimizer/optimizer.h" | 
|---|
| 39 | #include "optimizer/pathnode.h" | 
|---|
| 40 | #include "optimizer/paths.h" | 
|---|
| 41 | #include "optimizer/planmain.h" | 
|---|
| 42 | #include "optimizer/subselect.h" | 
|---|
| 43 | #include "optimizer/tlist.h" | 
|---|
| 44 | #include "parser/parsetree.h" | 
|---|
| 45 | #include "parser/parse_clause.h" | 
|---|
| 46 | #include "rewrite/rewriteManip.h" | 
|---|
| 47 | #include "utils/lsyscache.h" | 
|---|
| 48 | #include "utils/syscache.h" | 
|---|
| 49 |  | 
|---|
| 50 |  | 
|---|
| 51 | static bool find_minmax_aggs_walker(Node *node, List **context); | 
|---|
| 52 | static bool build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, | 
|---|
| 53 | Oid eqop, Oid sortop, bool nulls_first); | 
|---|
| 54 | static void minmax_qp_callback(PlannerInfo *root, void *); | 
|---|
| 55 | static Oid	fetch_agg_sort_op(Oid aggfnoid); | 
|---|
| 56 |  | 
|---|
| 57 |  | 
|---|
| 58 | /* | 
|---|
| 59 | * preprocess_minmax_aggregates - preprocess MIN/MAX aggregates | 
|---|
| 60 | * | 
|---|
| 61 | * Check to see whether the query contains MIN/MAX aggregate functions that | 
|---|
| 62 | * might be optimizable via indexscans.  If it does, and all the aggregates | 
|---|
| 63 | * are potentially optimizable, then create a MinMaxAggPath and add it to | 
|---|
| 64 | * the (UPPERREL_GROUP_AGG, NULL) upperrel. | 
|---|
| 65 | * | 
|---|
| 66 | * This should be called by grouping_planner() just before it's ready to call | 
|---|
| 67 | * query_planner(), because we generate indexscan paths by cloning the | 
|---|
| 68 | * planner's state and invoking query_planner() on a modified version of | 
|---|
| 69 | * the query parsetree.  Thus, all preprocessing needed before query_planner() | 
|---|
| 70 | * must already be done. | 
|---|
| 71 | */ | 
|---|
| 72 | void | 
|---|
| 73 | preprocess_minmax_aggregates(PlannerInfo *root) | 
|---|
| 74 | { | 
|---|
| 75 | Query	   *parse = root->parse; | 
|---|
| 76 | FromExpr   *jtnode; | 
|---|
| 77 | RangeTblRef *rtr; | 
|---|
| 78 | RangeTblEntry *rte; | 
|---|
| 79 | List	   *aggs_list; | 
|---|
| 80 | RelOptInfo *grouped_rel; | 
|---|
| 81 | ListCell   *lc; | 
|---|
| 82 |  | 
|---|
| 83 | /* minmax_aggs list should be empty at this point */ | 
|---|
| 84 | Assert(root->minmax_aggs == NIL); | 
|---|
| 85 |  | 
|---|
| 86 | /* Nothing to do if query has no aggregates */ | 
|---|
| 87 | if (!parse->hasAggs) | 
|---|
| 88 | return; | 
|---|
| 89 |  | 
|---|
| 90 | Assert(!parse->setOperations);	/* shouldn't get here if a setop */ | 
|---|
| 91 | Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */ | 
|---|
| 92 |  | 
|---|
| 93 | /* | 
|---|
| 94 | * Reject unoptimizable cases. | 
|---|
| 95 | * | 
|---|
| 96 | * We don't handle GROUP BY or windowing, because our current | 
|---|
| 97 | * implementations of grouping require looking at all the rows anyway, and | 
|---|
| 98 | * so there's not much point in optimizing MIN/MAX. | 
|---|
| 99 | */ | 
|---|
| 100 | if (parse->groupClause || list_length(parse->groupingSets) > 1 || | 
|---|
| 101 | parse->hasWindowFuncs) | 
|---|
| 102 | return; | 
|---|
| 103 |  | 
|---|
| 104 | /* | 
|---|
| 105 | * Reject if query contains any CTEs; there's no way to build an indexscan | 
|---|
| 106 | * on one so we couldn't succeed here.  (If the CTEs are unreferenced, | 
|---|
| 107 | * that's not true, but it doesn't seem worth expending cycles to check.) | 
|---|
| 108 | */ | 
|---|
| 109 | if (parse->cteList) | 
|---|
| 110 | return; | 
|---|
| 111 |  | 
|---|
| 112 | /* | 
|---|
| 113 | * We also restrict the query to reference exactly one table, since join | 
|---|
| 114 | * conditions can't be handled reasonably.  (We could perhaps handle a | 
|---|
| 115 | * query containing cartesian-product joins, but it hardly seems worth the | 
|---|
| 116 | * trouble.)  However, the single table could be buried in several levels | 
|---|
| 117 | * of FromExpr due to subqueries.  Note the "single" table could be an | 
|---|
| 118 | * inheritance parent, too, including the case of a UNION ALL subquery | 
|---|
| 119 | * that's been flattened to an appendrel. | 
|---|
| 120 | */ | 
|---|
| 121 | jtnode = parse->jointree; | 
|---|
| 122 | while (IsA(jtnode, FromExpr)) | 
|---|
| 123 | { | 
|---|
| 124 | if (list_length(jtnode->fromlist) != 1) | 
|---|
| 125 | return; | 
|---|
| 126 | jtnode = linitial(jtnode->fromlist); | 
|---|
| 127 | } | 
|---|
| 128 | if (!IsA(jtnode, RangeTblRef)) | 
|---|
| 129 | return; | 
|---|
| 130 | rtr = (RangeTblRef *) jtnode; | 
|---|
| 131 | rte = planner_rt_fetch(rtr->rtindex, root); | 
|---|
| 132 | if (rte->rtekind == RTE_RELATION) | 
|---|
| 133 | /* ordinary relation, ok */ ; | 
|---|
| 134 | else if (rte->rtekind == RTE_SUBQUERY && rte->inh) | 
|---|
| 135 | /* flattened UNION ALL subquery, ok */ ; | 
|---|
| 136 | else | 
|---|
| 137 | return; | 
|---|
| 138 |  | 
|---|
| 139 | /* | 
|---|
| 140 | * Scan the tlist and HAVING qual to find all the aggregates and verify | 
|---|
| 141 | * all are MIN/MAX aggregates.  Stop as soon as we find one that isn't. | 
|---|
| 142 | */ | 
|---|
| 143 | aggs_list = NIL; | 
|---|
| 144 | if (find_minmax_aggs_walker((Node *) root->processed_tlist, &aggs_list)) | 
|---|
| 145 | return; | 
|---|
| 146 | if (find_minmax_aggs_walker(parse->havingQual, &aggs_list)) | 
|---|
| 147 | return; | 
|---|
| 148 |  | 
|---|
| 149 | /* | 
|---|
| 150 | * OK, there is at least the possibility of performing the optimization. | 
|---|
| 151 | * Build an access path for each aggregate.  If any of the aggregates | 
|---|
| 152 | * prove to be non-indexable, give up; there is no point in optimizing | 
|---|
| 153 | * just some of them. | 
|---|
| 154 | */ | 
|---|
| 155 | foreach(lc, aggs_list) | 
|---|
| 156 | { | 
|---|
| 157 | MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc); | 
|---|
| 158 | Oid			eqop; | 
|---|
| 159 | bool		reverse; | 
|---|
| 160 |  | 
|---|
| 161 | /* | 
|---|
| 162 | * We'll need the equality operator that goes with the aggregate's | 
|---|
| 163 | * ordering operator. | 
|---|
| 164 | */ | 
|---|
| 165 | eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse); | 
|---|
| 166 | if (!OidIsValid(eqop))	/* shouldn't happen */ | 
|---|
| 167 | elog(ERROR, "could not find equality operator for ordering operator %u", | 
|---|
| 168 | mminfo->aggsortop); | 
|---|
| 169 |  | 
|---|
| 170 | /* | 
|---|
| 171 | * We can use either an ordering that gives NULLS FIRST or one that | 
|---|
| 172 | * gives NULLS LAST; furthermore there's unlikely to be much | 
|---|
| 173 | * performance difference between them, so it doesn't seem worth | 
|---|
| 174 | * costing out both ways if we get a hit on the first one.  NULLS | 
|---|
| 175 | * FIRST is more likely to be available if the operator is a | 
|---|
| 176 | * reverse-sort operator, so try that first if reverse. | 
|---|
| 177 | */ | 
|---|
| 178 | if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse)) | 
|---|
| 179 | continue; | 
|---|
| 180 | if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse)) | 
|---|
| 181 | continue; | 
|---|
| 182 |  | 
|---|
| 183 | /* No indexable path for this aggregate, so fail */ | 
|---|
| 184 | return; | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | /* | 
|---|
| 188 | * OK, we can do the query this way.  Prepare to create a MinMaxAggPath | 
|---|
| 189 | * node. | 
|---|
| 190 | * | 
|---|
| 191 | * First, create an output Param node for each agg.  (If we end up not | 
|---|
| 192 | * using the MinMaxAggPath, we'll waste a PARAM_EXEC slot for each agg, | 
|---|
| 193 | * which is not worth worrying about.  We can't wait till create_plan time | 
|---|
| 194 | * to decide whether to make the Param, unfortunately.) | 
|---|
| 195 | */ | 
|---|
| 196 | foreach(lc, aggs_list) | 
|---|
| 197 | { | 
|---|
| 198 | MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc); | 
|---|
| 199 |  | 
|---|
| 200 | mminfo->param = | 
|---|
| 201 | SS_make_initplan_output_param(root, | 
|---|
| 202 | exprType((Node *) mminfo->target), | 
|---|
| 203 | -1, | 
|---|
| 204 | exprCollation((Node *) mminfo->target)); | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | /* | 
|---|
| 208 | * Create a MinMaxAggPath node with the appropriate estimated costs and | 
|---|
| 209 | * other needed data, and add it to the UPPERREL_GROUP_AGG upperrel, where | 
|---|
| 210 | * it will compete against the standard aggregate implementation.  (It | 
|---|
| 211 | * will likely always win, but we need not assume that here.) | 
|---|
| 212 | * | 
|---|
| 213 | * Note: grouping_planner won't have created this upperrel yet, but it's | 
|---|
| 214 | * fine for us to create it first.  We will not have inserted the correct | 
|---|
| 215 | * consider_parallel value in it, but MinMaxAggPath paths are currently | 
|---|
| 216 | * never parallel-safe anyway, so that doesn't matter.  Likewise, it | 
|---|
| 217 | * doesn't matter that we haven't filled FDW-related fields in the rel. | 
|---|
| 218 | * Also, because there are no rowmarks, we know that the processed_tlist | 
|---|
| 219 | * doesn't need to change anymore, so making the pathtarget now is safe. | 
|---|
| 220 | */ | 
|---|
| 221 | grouped_rel = fetch_upper_rel(root, UPPERREL_GROUP_AGG, NULL); | 
|---|
| 222 | add_path(grouped_rel, (Path *) | 
|---|
| 223 | create_minmaxagg_path(root, grouped_rel, | 
|---|
| 224 | create_pathtarget(root, | 
|---|
| 225 | root->processed_tlist), | 
|---|
| 226 | aggs_list, | 
|---|
| 227 | (List *) parse->havingQual)); | 
|---|
| 228 | } | 
|---|
| 229 |  | 
|---|
| 230 | /* | 
|---|
| 231 | * find_minmax_aggs_walker | 
|---|
| 232 | *		Recursively scan the Aggref nodes in an expression tree, and check | 
|---|
| 233 | *		that each one is a MIN/MAX aggregate.  If so, build a list of the | 
|---|
| 234 | *		distinct aggregate calls in the tree. | 
|---|
| 235 | * | 
|---|
| 236 | * Returns true if a non-MIN/MAX aggregate is found, false otherwise. | 
|---|
| 237 | * (This seemingly-backward definition is used because expression_tree_walker | 
|---|
| 238 | * aborts the scan on true return, which is what we want.) | 
|---|
| 239 | * | 
|---|
| 240 | * Found aggregates are added to the list at *context; it's up to the caller | 
|---|
| 241 | * to initialize the list to NIL. | 
|---|
| 242 | * | 
|---|
| 243 | * This does not descend into subqueries, and so should be used only after | 
|---|
| 244 | * reduction of sublinks to subplans.  There mustn't be outer-aggregate | 
|---|
| 245 | * references either. | 
|---|
| 246 | */ | 
|---|
| 247 | static bool | 
|---|
| 248 | find_minmax_aggs_walker(Node *node, List **context) | 
|---|
| 249 | { | 
|---|
| 250 | if (node == NULL) | 
|---|
| 251 | return false; | 
|---|
| 252 | if (IsA(node, Aggref)) | 
|---|
| 253 | { | 
|---|
| 254 | Aggref	   *aggref = (Aggref *) node; | 
|---|
| 255 | Oid			aggsortop; | 
|---|
| 256 | TargetEntry *curTarget; | 
|---|
| 257 | MinMaxAggInfo *mminfo; | 
|---|
| 258 | ListCell   *l; | 
|---|
| 259 |  | 
|---|
| 260 | Assert(aggref->agglevelsup == 0); | 
|---|
| 261 | if (list_length(aggref->args) != 1) | 
|---|
| 262 | return true;		/* it couldn't be MIN/MAX */ | 
|---|
| 263 |  | 
|---|
| 264 | /* | 
|---|
| 265 | * ORDER BY is usually irrelevant for MIN/MAX, but it can change the | 
|---|
| 266 | * outcome if the aggsortop's operator class recognizes non-identical | 
|---|
| 267 | * values as equal.  For example, 4.0 and 4.00 are equal according to | 
|---|
| 268 | * numeric_ops, yet distinguishable.  If MIN() receives more than one | 
|---|
| 269 | * value equal to 4.0 and no value less than 4.0, it is unspecified | 
|---|
| 270 | * which of those equal values MIN() returns.  An ORDER BY expression | 
|---|
| 271 | * that differs for each of those equal values of the argument | 
|---|
| 272 | * expression makes the result predictable once again.  This is a | 
|---|
| 273 | * niche requirement, and we do not implement it with subquery paths. | 
|---|
| 274 | * In any case, this test lets us reject ordered-set aggregates | 
|---|
| 275 | * quickly. | 
|---|
| 276 | */ | 
|---|
| 277 | if (aggref->aggorder != NIL) | 
|---|
| 278 | return true; | 
|---|
| 279 | /* note: we do not care if DISTINCT is mentioned ... */ | 
|---|
| 280 |  | 
|---|
| 281 | /* | 
|---|
| 282 | * We might implement the optimization when a FILTER clause is present | 
|---|
| 283 | * by adding the filter to the quals of the generated subquery.  For | 
|---|
| 284 | * now, just punt. | 
|---|
| 285 | */ | 
|---|
| 286 | if (aggref->aggfilter != NULL) | 
|---|
| 287 | return true; | 
|---|
| 288 |  | 
|---|
| 289 | aggsortop = fetch_agg_sort_op(aggref->aggfnoid); | 
|---|
| 290 | if (!OidIsValid(aggsortop)) | 
|---|
| 291 | return true;		/* not a MIN/MAX aggregate */ | 
|---|
| 292 |  | 
|---|
| 293 | curTarget = (TargetEntry *) linitial(aggref->args); | 
|---|
| 294 |  | 
|---|
| 295 | if (contain_mutable_functions((Node *) curTarget->expr)) | 
|---|
| 296 | return true;		/* not potentially indexable */ | 
|---|
| 297 |  | 
|---|
| 298 | if (type_is_rowtype(exprType((Node *) curTarget->expr))) | 
|---|
| 299 | return true;		/* IS NOT NULL would have weird semantics */ | 
|---|
| 300 |  | 
|---|
| 301 | /* | 
|---|
| 302 | * Check whether it's already in the list, and add it if not. | 
|---|
| 303 | */ | 
|---|
| 304 | foreach(l, *context) | 
|---|
| 305 | { | 
|---|
| 306 | mminfo = (MinMaxAggInfo *) lfirst(l); | 
|---|
| 307 | if (mminfo->aggfnoid == aggref->aggfnoid && | 
|---|
| 308 | equal(mminfo->target, curTarget->expr)) | 
|---|
| 309 | return false; | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | mminfo = makeNode(MinMaxAggInfo); | 
|---|
| 313 | mminfo->aggfnoid = aggref->aggfnoid; | 
|---|
| 314 | mminfo->aggsortop = aggsortop; | 
|---|
| 315 | mminfo->target = curTarget->expr; | 
|---|
| 316 | mminfo->subroot = NULL; /* don't compute path yet */ | 
|---|
| 317 | mminfo->path = NULL; | 
|---|
| 318 | mminfo->pathcost = 0; | 
|---|
| 319 | mminfo->param = NULL; | 
|---|
| 320 |  | 
|---|
| 321 | *context = lappend(*context, mminfo); | 
|---|
| 322 |  | 
|---|
| 323 | /* | 
|---|
| 324 | * We need not recurse into the argument, since it can't contain any | 
|---|
| 325 | * aggregates. | 
|---|
| 326 | */ | 
|---|
| 327 | return false; | 
|---|
| 328 | } | 
|---|
| 329 | Assert(!IsA(node, SubLink)); | 
|---|
| 330 | return expression_tree_walker(node, find_minmax_aggs_walker, | 
|---|
| 331 | (void *) context); | 
|---|
| 332 | } | 
|---|
| 333 |  | 
|---|
| 334 | /* | 
|---|
| 335 | * build_minmax_path | 
|---|
| 336 | *		Given a MIN/MAX aggregate, try to build an indexscan Path it can be | 
|---|
| 337 | *		optimized with. | 
|---|
| 338 | * | 
|---|
| 339 | * If successful, stash the best path in *mminfo and return true. | 
|---|
| 340 | * Otherwise, return false. | 
|---|
| 341 | */ | 
|---|
| 342 | static bool | 
|---|
| 343 | build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, | 
|---|
| 344 | Oid eqop, Oid sortop, bool nulls_first) | 
|---|
| 345 | { | 
|---|
| 346 | PlannerInfo *subroot; | 
|---|
| 347 | Query	   *parse; | 
|---|
| 348 | TargetEntry *tle; | 
|---|
| 349 | List	   *tlist; | 
|---|
| 350 | NullTest   *ntest; | 
|---|
| 351 | SortGroupClause *sortcl; | 
|---|
| 352 | RelOptInfo *final_rel; | 
|---|
| 353 | Path	   *sorted_path; | 
|---|
| 354 | Cost		path_cost; | 
|---|
| 355 | double		path_fraction; | 
|---|
| 356 |  | 
|---|
| 357 | /* | 
|---|
| 358 | * We are going to construct what is effectively a sub-SELECT query, so | 
|---|
| 359 | * clone the current query level's state and adjust it to make it look | 
|---|
| 360 | * like a subquery.  Any outer references will now be one level higher | 
|---|
| 361 | * than before.  (This means that when we are done, there will be no Vars | 
|---|
| 362 | * of level 1, which is why the subquery can become an initplan.) | 
|---|
| 363 | */ | 
|---|
| 364 | subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo)); | 
|---|
| 365 | memcpy(subroot, root, sizeof(PlannerInfo)); | 
|---|
| 366 | subroot->query_level++; | 
|---|
| 367 | subroot->parent_root = root; | 
|---|
| 368 | /* reset subplan-related stuff */ | 
|---|
| 369 | subroot->plan_params = NIL; | 
|---|
| 370 | subroot->outer_params = NULL; | 
|---|
| 371 | subroot->init_plans = NIL; | 
|---|
| 372 |  | 
|---|
| 373 | subroot->parse = parse = copyObject(root->parse); | 
|---|
| 374 | IncrementVarSublevelsUp((Node *) parse, 1, 1); | 
|---|
| 375 |  | 
|---|
| 376 | /* append_rel_list might contain outer Vars? */ | 
|---|
| 377 | subroot->append_rel_list = copyObject(root->append_rel_list); | 
|---|
| 378 | IncrementVarSublevelsUp((Node *) subroot->append_rel_list, 1, 1); | 
|---|
| 379 | /* There shouldn't be any OJ info to translate, as yet */ | 
|---|
| 380 | Assert(subroot->join_info_list == NIL); | 
|---|
| 381 | /* and we haven't made equivalence classes, either */ | 
|---|
| 382 | Assert(subroot->eq_classes == NIL); | 
|---|
| 383 | /* and we haven't created PlaceHolderInfos, either */ | 
|---|
| 384 | Assert(subroot->placeholder_list == NIL); | 
|---|
| 385 |  | 
|---|
| 386 | /*---------- | 
|---|
| 387 | * Generate modified query of the form | 
|---|
| 388 | *		(SELECT col FROM tab | 
|---|
| 389 | *		 WHERE col IS NOT NULL AND existing-quals | 
|---|
| 390 | *		 ORDER BY col ASC/DESC | 
|---|
| 391 | *		 LIMIT 1) | 
|---|
| 392 | *---------- | 
|---|
| 393 | */ | 
|---|
| 394 | /* single tlist entry that is the aggregate target */ | 
|---|
| 395 | tle = makeTargetEntry(copyObject(mminfo->target), | 
|---|
| 396 | (AttrNumber) 1, | 
|---|
| 397 | pstrdup( "agg_target"), | 
|---|
| 398 | false); | 
|---|
| 399 | tlist = list_make1(tle); | 
|---|
| 400 | subroot->processed_tlist = parse->targetList = tlist; | 
|---|
| 401 |  | 
|---|
| 402 | /* No HAVING, no DISTINCT, no aggregates anymore */ | 
|---|
| 403 | parse->havingQual = NULL; | 
|---|
| 404 | subroot->hasHavingQual = false; | 
|---|
| 405 | parse->distinctClause = NIL; | 
|---|
| 406 | parse->hasDistinctOn = false; | 
|---|
| 407 | parse->hasAggs = false; | 
|---|
| 408 |  | 
|---|
| 409 | /* Build "target IS NOT NULL" expression */ | 
|---|
| 410 | ntest = makeNode(NullTest); | 
|---|
| 411 | ntest->nulltesttype = IS_NOT_NULL; | 
|---|
| 412 | ntest->arg = copyObject(mminfo->target); | 
|---|
| 413 | /* we checked it wasn't a rowtype in find_minmax_aggs_walker */ | 
|---|
| 414 | ntest->argisrow = false; | 
|---|
| 415 | ntest->location = -1; | 
|---|
| 416 |  | 
|---|
| 417 | /* User might have had that in WHERE already */ | 
|---|
| 418 | if (!list_member((List *) parse->jointree->quals, ntest)) | 
|---|
| 419 | parse->jointree->quals = (Node *) | 
|---|
| 420 | lcons(ntest, (List *) parse->jointree->quals); | 
|---|
| 421 |  | 
|---|
| 422 | /* Build suitable ORDER BY clause */ | 
|---|
| 423 | sortcl = makeNode(SortGroupClause); | 
|---|
| 424 | sortcl->tleSortGroupRef = assignSortGroupRef(tle, subroot->processed_tlist); | 
|---|
| 425 | sortcl->eqop = eqop; | 
|---|
| 426 | sortcl->sortop = sortop; | 
|---|
| 427 | sortcl->nulls_first = nulls_first; | 
|---|
| 428 | sortcl->hashable = false;	/* no need to make this accurate */ | 
|---|
| 429 | parse->sortClause = list_make1(sortcl); | 
|---|
| 430 |  | 
|---|
| 431 | /* set up expressions for LIMIT 1 */ | 
|---|
| 432 | parse->limitOffset = NULL; | 
|---|
| 433 | parse->limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid, | 
|---|
| 434 | sizeof(int64), | 
|---|
| 435 | Int64GetDatum(1), false, | 
|---|
| 436 | FLOAT8PASSBYVAL); | 
|---|
| 437 |  | 
|---|
| 438 | /* | 
|---|
| 439 | * Generate the best paths for this query, telling query_planner that we | 
|---|
| 440 | * have LIMIT 1. | 
|---|
| 441 | */ | 
|---|
| 442 | subroot->tuple_fraction = 1.0; | 
|---|
| 443 | subroot->limit_tuples = 1.0; | 
|---|
| 444 |  | 
|---|
| 445 | final_rel = query_planner(subroot, minmax_qp_callback, NULL); | 
|---|
| 446 |  | 
|---|
| 447 | /* | 
|---|
| 448 | * Since we didn't go through subquery_planner() to handle the subquery, | 
|---|
| 449 | * we have to do some of the same cleanup it would do, in particular cope | 
|---|
| 450 | * with params and initplans used within this subquery.  (This won't | 
|---|
| 451 | * matter if we end up not using the subplan.) | 
|---|
| 452 | */ | 
|---|
| 453 | SS_identify_outer_params(subroot); | 
|---|
| 454 | SS_charge_for_initplans(subroot, final_rel); | 
|---|
| 455 |  | 
|---|
| 456 | /* | 
|---|
| 457 | * Get the best presorted path, that being the one that's cheapest for | 
|---|
| 458 | * fetching just one row.  If there's no such path, fail. | 
|---|
| 459 | */ | 
|---|
| 460 | if (final_rel->rows > 1.0) | 
|---|
| 461 | path_fraction = 1.0 / final_rel->rows; | 
|---|
| 462 | else | 
|---|
| 463 | path_fraction = 1.0; | 
|---|
| 464 |  | 
|---|
| 465 | sorted_path = | 
|---|
| 466 | get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist, | 
|---|
| 467 | subroot->query_pathkeys, | 
|---|
| 468 | NULL, | 
|---|
| 469 | path_fraction); | 
|---|
| 470 | if (!sorted_path) | 
|---|
| 471 | return false; | 
|---|
| 472 |  | 
|---|
| 473 | /* | 
|---|
| 474 | * The path might not return exactly what we want, so fix that.  (We | 
|---|
| 475 | * assume that this won't change any conclusions about which was the | 
|---|
| 476 | * cheapest path.) | 
|---|
| 477 | */ | 
|---|
| 478 | sorted_path = apply_projection_to_path(subroot, final_rel, sorted_path, | 
|---|
| 479 | create_pathtarget(subroot, | 
|---|
| 480 | subroot->processed_tlist)); | 
|---|
| 481 |  | 
|---|
| 482 | /* | 
|---|
| 483 | * Determine cost to get just the first row of the presorted path. | 
|---|
| 484 | * | 
|---|
| 485 | * Note: cost calculation here should match | 
|---|
| 486 | * compare_fractional_path_costs(). | 
|---|
| 487 | */ | 
|---|
| 488 | path_cost = sorted_path->startup_cost + | 
|---|
| 489 | path_fraction * (sorted_path->total_cost - sorted_path->startup_cost); | 
|---|
| 490 |  | 
|---|
| 491 | /* Save state for further processing */ | 
|---|
| 492 | mminfo->subroot = subroot; | 
|---|
| 493 | mminfo->path = sorted_path; | 
|---|
| 494 | mminfo->pathcost = path_cost; | 
|---|
| 495 |  | 
|---|
| 496 | return true; | 
|---|
| 497 | } | 
|---|
| 498 |  | 
|---|
| 499 | /* | 
|---|
| 500 | * Compute query_pathkeys and other pathkeys during query_planner() | 
|---|
| 501 | */ | 
|---|
| 502 | static void | 
|---|
| 503 | minmax_qp_callback(PlannerInfo *root, void *) | 
|---|
| 504 | { | 
|---|
| 505 | root->group_pathkeys = NIL; | 
|---|
| 506 | root->window_pathkeys = NIL; | 
|---|
| 507 | root->distinct_pathkeys = NIL; | 
|---|
| 508 |  | 
|---|
| 509 | root->sort_pathkeys = | 
|---|
| 510 | make_pathkeys_for_sortclauses(root, | 
|---|
| 511 | root->parse->sortClause, | 
|---|
| 512 | root->parse->targetList); | 
|---|
| 513 |  | 
|---|
| 514 | root->query_pathkeys = root->sort_pathkeys; | 
|---|
| 515 | } | 
|---|
| 516 |  | 
|---|
| 517 | /* | 
|---|
| 518 | * Get the OID of the sort operator, if any, associated with an aggregate. | 
|---|
| 519 | * Returns InvalidOid if there is no such operator. | 
|---|
| 520 | */ | 
|---|
| 521 | static Oid | 
|---|
| 522 | fetch_agg_sort_op(Oid aggfnoid) | 
|---|
| 523 | { | 
|---|
| 524 | HeapTuple	aggTuple; | 
|---|
| 525 | Form_pg_aggregate aggform; | 
|---|
| 526 | Oid			aggsortop; | 
|---|
| 527 |  | 
|---|
| 528 | /* fetch aggregate entry from pg_aggregate */ | 
|---|
| 529 | aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggfnoid)); | 
|---|
| 530 | if (!HeapTupleIsValid(aggTuple)) | 
|---|
| 531 | return InvalidOid; | 
|---|
| 532 | aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); | 
|---|
| 533 | aggsortop = aggform->aggsortop; | 
|---|
| 534 | ReleaseSysCache(aggTuple); | 
|---|
| 535 |  | 
|---|
| 536 | return aggsortop; | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|