planner.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:27k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * planner.c
  4.  *   The query optimizer external interface.
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.57.2.1 1999/08/02 06:27:02 scrappy Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. #include <sys/types.h>
  15. #include "postgres.h"
  16. #include "access/genam.h"
  17. #include "access/heapam.h"
  18. #include "catalog/pg_type.h"
  19. #include "executor/executor.h"
  20. #include "nodes/makefuncs.h"
  21. #include "optimizer/clauses.h"
  22. #include "optimizer/internal.h"
  23. #include "optimizer/planmain.h"
  24. #include "optimizer/planner.h"
  25. #include "optimizer/prep.h"
  26. #include "optimizer/subselect.h"
  27. #include "optimizer/tlist.h"
  28. #include "optimizer/var.h"
  29. #include "parser/parse_expr.h"
  30. #include "parser/parse_oper.h"
  31. #include "utils/builtins.h"
  32. #include "utils/lsyscache.h"
  33. #include "utils/syscache.h"
  34. static List *make_subplanTargetList(Query *parse, List *tlist,
  35.    AttrNumber **groupColIdx);
  36. static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
  37.    List *groupClause, AttrNumber *grpColIdx,
  38.    Plan *subplan);
  39. static bool need_sortplan(List *sortcls, Plan *plan);
  40. static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
  41. /*****************************************************************************
  42.  *
  43.  *    Query optimizer entry point
  44.  *
  45.  *****************************************************************************/
  46. Plan *
  47. planner(Query *parse)
  48. {
  49. Plan    *result_plan;
  50. /* Initialize state for subselects */
  51. PlannerQueryLevel = 1;
  52. PlannerInitPlan = NULL;
  53. PlannerParamVar = NULL;
  54. PlannerPlanId = 0;
  55. transformKeySetQuery(parse);
  56. result_plan = union_planner(parse);
  57. Assert(PlannerQueryLevel == 1);
  58. if (PlannerPlanId > 0)
  59. {
  60. result_plan->initPlan = PlannerInitPlan;
  61. (void) SS_finalize_plan(result_plan);
  62. }
  63. result_plan->nParamExec = length(PlannerParamVar);
  64. return result_plan;
  65. }
  66. /*
  67.  * union_planner
  68.  *
  69.  *   Invokes the planner on union queries if there are any left,
  70.  *   recursing if necessary to get them all, then processes normal plans.
  71.  *
  72.  * Returns a query plan.
  73.  *
  74.  */
  75. Plan *
  76. union_planner(Query *parse)
  77. {
  78. List    *tlist = parse->targetList;
  79. List    *rangetable = parse->rtable;
  80. Plan    *result_plan = (Plan *) NULL;
  81. AttrNumber *groupColIdx = NULL;
  82. Index rt_index;
  83. if (parse->unionClause)
  84. {
  85. result_plan = (Plan *) plan_union_queries(parse);
  86. /* XXX do we need to do this? bjm 12/19/97 */
  87. tlist = preprocess_targetlist(tlist,
  88.   parse->commandType,
  89.   parse->resultRelation,
  90.   parse->rtable);
  91. }
  92. else if ((rt_index = first_inherit_rt_entry(rangetable)) != -1)
  93. {
  94. List    *sub_tlist;
  95. /*
  96.  * Generate appropriate target list for subplan; may be different
  97.  * from tlist if grouping or aggregation is needed.
  98.  */
  99. sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
  100. /*
  101.  * Recursively plan the subqueries needed for inheritance
  102.  */
  103. result_plan = (Plan *) plan_inherit_queries(parse, sub_tlist,
  104. rt_index);
  105. /*
  106.  * Fix up outer target list.  NOTE: unlike the case for non-inherited
  107.  * query, we pass the unfixed tlist to subplans, which do their own
  108.  * fixing.  But we still want to fix the outer target list afterwards.
  109.  * I *think* this is correct --- doing the fix before recursing is
  110.  * definitely wrong, because preprocess_targetlist() will do the
  111.  * wrong thing if invoked twice on the same list. Maybe that is a bug?
  112.  * tgl 6/6/99
  113.  */
  114. tlist = preprocess_targetlist(tlist,
  115.   parse->commandType,
  116.   parse->resultRelation,
  117.   parse->rtable);
  118. if (parse->rowMark != NULL)
  119. elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
  120. }
  121. else
  122. {
  123. List    *sub_tlist;
  124. /* Preprocess targetlist in case we are inside an INSERT/UPDATE. */
  125. tlist = preprocess_targetlist(tlist,
  126.   parse->commandType,
  127.   parse->resultRelation,
  128.   parse->rtable);
  129. /*
  130.  * Add row-mark targets for UPDATE (should this be done in
  131.  * preprocess_targetlist?)
  132.  */
  133. if (parse->rowMark != NULL)
  134. {
  135. List    *l;
  136. foreach(l, parse->rowMark)
  137. {
  138. RowMark    *rowmark = (RowMark *) lfirst(l);
  139. TargetEntry *ctid;
  140. Resdom    *resdom;
  141. Var    *var;
  142. char    *resname;
  143. if (!(rowmark->info & ROW_MARK_FOR_UPDATE))
  144. continue;
  145. resname = (char *) palloc(32);
  146. sprintf(resname, "ctid%u", rowmark->rti);
  147. resdom = makeResdom(length(tlist) + 1,
  148. TIDOID,
  149. -1,
  150. resname,
  151. 0,
  152. 0,
  153. true);
  154. var = makeVar(rowmark->rti, -1, TIDOID,
  155.   -1, 0, rowmark->rti, -1);
  156. ctid = makeTargetEntry(resdom, (Node *) var);
  157. tlist = lappend(tlist, ctid);
  158. }
  159. }
  160. /*
  161.  * Generate appropriate target list for subplan; may be different
  162.  * from tlist if grouping or aggregation is needed.
  163.  */
  164. sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
  165. /* Generate the (sub) plan */
  166. result_plan = query_planner(parse,
  167. parse->commandType,
  168. sub_tlist,
  169. (List *) parse->qual);
  170. }
  171. /* query_planner returns NULL if it thinks plan is bogus */
  172. if (! result_plan)
  173. elog(ERROR, "union_planner: failed to create plan");
  174. /*
  175.  * If we have a GROUP BY clause, insert a group node (with the
  176.  * appropriate sort node.)
  177.  */
  178. if (parse->groupClause)
  179. {
  180. bool tuplePerGroup;
  181. List    *group_tlist;
  182. /*
  183.  * Decide whether how many tuples per group the Group node needs
  184.  * to return. (Needs only one tuple per group if no aggregate is
  185.  * present. Otherwise, need every tuple from the group to do the
  186.  * aggregation.)  Note tuplePerGroup is named backwards :-(
  187.  */
  188. tuplePerGroup = parse->hasAggs;
  189. /*
  190.  * If there are aggregates then the Group node should just return
  191.  * the same (simplified) tlist as the subplan, which we indicate
  192.  * to make_groupplan by passing NIL.  If there are no aggregates
  193.  * then the Group node had better compute the final tlist.
  194.  */
  195. group_tlist = parse->hasAggs ? NIL : tlist;
  196. result_plan = make_groupplan(group_tlist,
  197.  tuplePerGroup,
  198.  parse->groupClause,
  199.  groupColIdx,
  200.  result_plan);
  201. }
  202. /*
  203.  * If we have a HAVING clause, do the necessary things with it.
  204.  */
  205. if (parse->havingQual)
  206. {
  207. /* convert the havingQual to conjunctive normal form (cnf) */
  208. parse->havingQual = (Node *) cnfify((Expr *) parse->havingQual, true);
  209. if (parse->hasSubLinks)
  210. {
  211. /*
  212.  * There may be a subselect in the havingQual, so we have to
  213.  * process it using the same function as for a subselect in
  214.  * 'where'
  215.  */
  216. parse->havingQual = SS_process_sublinks(parse->havingQual);
  217. /*
  218.  * Check for ungrouped variables passed to subplans. (Probably
  219.  * this should be done for the targetlist as well???)
  220.  */
  221. check_having_for_ungrouped_vars(parse->havingQual,
  222. parse->groupClause,
  223. parse->targetList);
  224. }
  225. /* Calculate the opfids from the opnos */
  226. parse->havingQual = (Node *) fix_opids((List *) parse->havingQual);
  227. }
  228. /*
  229.  * If aggregate is present, insert the agg node
  230.  */
  231. if (parse->hasAggs)
  232. {
  233. result_plan = (Plan *) make_agg(tlist, result_plan);
  234. /* HAVING clause, if any, becomes qual of the Agg node */
  235. result_plan->qual = (List *) parse->havingQual;
  236. /*
  237.  * Update vars to refer to subplan result tuples, find Aggrefs,
  238.  * make sure there is an Aggref in every HAVING clause.
  239.  */
  240. if (!set_agg_tlist_references((Agg *) result_plan))
  241. elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
  242. /*
  243.  * Check that we actually found some aggregates, else executor
  244.  * will die unpleasantly.  (This defends against possible bugs in
  245.  * parser or rewrite that might cause hasAggs to be incorrectly
  246.  * set 'true'. It's not easy to recover here, since we've already
  247.  * made decisions assuming there will be an Agg node.)
  248.  */
  249. if (((Agg *) result_plan)->aggs == NIL)
  250. elog(ERROR, "union_planner: query is marked hasAggs, but I don't see any");
  251. }
  252. /*
  253.  * For now, before we hand back the plan, check to see if there is a
  254.  * user-specified sort that needs to be done.  Eventually, this will
  255.  * be moved into the guts of the planner s.t. user specified sorts
  256.  * will be considered as part of the planning process. Since we can
  257.  * only make use of user-specified sorts in special cases, we can do
  258.  * the optimization step later.
  259.  */
  260. if (parse->uniqueFlag)
  261. {
  262. Plan    *sortplan = make_sortplan(tlist, parse->sortClause, result_plan);
  263. return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag));
  264. }
  265. else
  266. {
  267. if (parse->sortClause && need_sortplan(parse->sortClause, result_plan))
  268. return (make_sortplan(tlist, parse->sortClause, result_plan));
  269. else
  270. return ((Plan *) result_plan);
  271. }
  272. }
  273. /*---------------
  274.  * make_subplanTargetList
  275.  *   Generate appropriate target lists when grouping is required.
  276.  *
  277.  * When union_planner inserts Aggregate and/or Group/Sort plan nodes above
  278.  * the result of query_planner, we typically need to pass a different
  279.  * target list to query_planner than the outer plan nodes should have.
  280.  * This routine generates the correct target list for the subplan, and
  281.  * if necessary modifies the target list for the inserted nodes as well.
  282.  *
  283.  * The initial target list passed from the parser already contains entries
  284.  * for all ORDER BY and GROUP BY expressions, but it will not have entries
  285.  * for variables used only in HAVING clauses; so we need to add those
  286.  * variables to the subplan target list.  Also, if we are doing either
  287.  * grouping or aggregation, we flatten all expressions except GROUP BY items
  288.  * into their component variables; the other expressions will be computed by
  289.  * the inserted nodes rather than by the subplan.  For example,
  290.  * given a query like
  291.  * SELECT a+b,SUM(c+d) FROM table GROUP BY a+b;
  292.  * we want to pass this targetlist to the subplan:
  293.  * a+b,c,d
  294.  * where the a+b target will be used by the Sort/Group steps, and the
  295.  * c and d targets will be needed to compute the aggregate results.
  296.  *
  297.  * 'parse' is the query being processed.
  298.  * 'tlist' is the query's target list.  CAUTION: list elements may be
  299.  * modified by this routine!
  300.  * 'groupColIdx' receives an array of column numbers for the GROUP BY
  301.  * expressions (if there are any) in the subplan's target list.
  302.  *
  303.  * The result is the targetlist to be passed to the subplan.  Also,
  304.  * the parent tlist is modified so that any nontrivial targetlist items that
  305.  * exactly match GROUP BY items are replaced by simple Var nodes referencing
  306.  * those outputs of the subplan.  This avoids redundant recalculations in
  307.  * cases like
  308.  * SELECT a+1, ... GROUP BY a+1
  309.  * Note, however, that other varnodes in the parent's targetlist (and
  310.  * havingQual, if any) will still need to be updated to refer to outputs
  311.  * of the subplan. This routine is quite large enough already, so we do
  312.  * that later.
  313.  *---------------
  314.  */
  315. static List *
  316. make_subplanTargetList(Query *parse,
  317.    List *tlist,
  318.    AttrNumber **groupColIdx)
  319. {
  320. List    *sub_tlist;
  321. List    *prnt_tlist;
  322. List    *sl,
  323.    *gl;
  324. List    *glc = NIL;
  325. List    *extravars = NIL;
  326. int numCols;
  327. AttrNumber *grpColIdx = NULL;
  328. int next_resno = 1;
  329. *groupColIdx = NULL;
  330. /*
  331.  * If we're not grouping or aggregating, nothing to do here;
  332.  * query_planner should receive the unmodified target list.
  333.  */
  334. if (!parse->hasAggs && !parse->groupClause && !parse->havingQual)
  335. return tlist;
  336. /*
  337.  * If grouping, make a working copy of groupClause list (which we use
  338.  * just to verify that we found all the groupClause items in tlist).
  339.  * Also allocate space to remember where the group columns are in the
  340.  * subplan tlist.
  341.  */
  342. numCols = length(parse->groupClause);
  343. if (numCols > 0)
  344. {
  345. glc = listCopy(parse->groupClause);
  346. grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
  347. *groupColIdx = grpColIdx;
  348. }
  349. sub_tlist = new_unsorted_tlist(tlist); /* make a modifiable copy */
  350. /*
  351.  * Step 1: build grpColIdx by finding targetlist items that match
  352.  * GroupBy entries.  If there are aggregates, remove non-GroupBy items
  353.  * from sub_tlist, and reset its resnos accordingly.  When we leave an
  354.  * expression in the subplan tlist, modify the parent tlist to copy
  355.  * the value from the subplan output rather than re-evaluating it.
  356.  */
  357. prnt_tlist = tlist; /* scans parent tlist in sync with sl */
  358. foreach(sl, sub_tlist)
  359. {
  360. TargetEntry *te = (TargetEntry *) lfirst(sl);
  361. TargetEntry *parentte = (TargetEntry *) lfirst(prnt_tlist);
  362. Resdom    *resdom = te->resdom;
  363. bool keepInSubPlan = true;
  364. bool foundGroupClause = false;
  365. int keyno = 0;
  366. foreach(gl, parse->groupClause)
  367. {
  368. GroupClause *grpcl = (GroupClause *) lfirst(gl);
  369. keyno++; /* sort key # for this GroupClause */
  370. if (grpcl->tleGroupref == resdom->resgroupref)
  371. {
  372. /* Found a matching groupclause; record info for sorting */
  373. foundGroupClause = true;
  374. resdom->reskey = keyno;
  375. resdom->reskeyop = get_opcode(grpcl->grpOpoid);
  376. grpColIdx[keyno - 1] = next_resno;
  377. /*
  378.  * Remove groupclause from our list of unmatched
  379.  * groupclauses. NB: this depends on having used a shallow
  380.  * listCopy() above.
  381.  */
  382. glc = lremove((void *) grpcl, glc);
  383. break;
  384. }
  385. }
  386. if (!foundGroupClause)
  387. {
  388. /*
  389.  * Non-GroupBy entry: remove it from subplan if there are
  390.  * aggregates in query - it will be evaluated by Aggregate
  391.  * plan. But do not remove simple-Var entries; we'd just have
  392.  * to add them back anyway, and we risk confusing
  393.  * INSERT/UPDATE.
  394.  */
  395. if (parse->hasAggs && !IsA(te->expr, Var))
  396. keepInSubPlan = false;
  397. }
  398. if (keepInSubPlan)
  399. {
  400. /* Assign new sequential resnos to subplan tlist items */
  401. resdom->resno = next_resno++;
  402. if (!IsA(parentte->expr, Var))
  403. {
  404. /*
  405.  * Since the item is being computed in the subplan, we can
  406.  * just make a Var node to reference it in the outer plan,
  407.  * rather than recomputing it there. Note we use varnoold
  408.  * = -1 as a flag to let replace_vars_with_subplan_refs
  409.  * know it needn't change this Var node. If it's only a
  410.  * Var anyway, we leave it alone for now;
  411.  * replace_vars_with_subplan_refs will fix it later.
  412.  */
  413. parentte->expr = (Node *) makeVar(1, resdom->resno,
  414.   resdom->restype,
  415.   resdom->restypmod,
  416.   0, -1, resdom->resno);
  417. }
  418. }
  419. else
  420. {
  421. /*
  422.  * Remove this tlist item from the subplan, but remember the
  423.  * vars it needs.  The outer tlist item probably needs
  424.  * changes, but that will happen later.
  425.  */
  426. sub_tlist = lremove(te, sub_tlist);
  427. extravars = nconc(extravars, pull_var_clause(te->expr));
  428. }
  429. prnt_tlist = lnext(prnt_tlist);
  430. }
  431. /* We should have found all the GROUP BY clauses in the tlist. */
  432. if (length(glc) != 0)
  433. elog(ERROR, "make_subplanTargetList: GROUP BY attribute not found in target list");
  434. /*
  435.  * Add subplan targets for any variables needed by removed tlist
  436.  * entries that aren't otherwise mentioned in the subplan target list.
  437.  * We'll also need targets for any variables seen only in HAVING.
  438.  */
  439. extravars = nconc(extravars, pull_var_clause(parse->havingQual));
  440. foreach(gl, extravars)
  441. {
  442. Var    *v = (Var *) lfirst(gl);
  443. if (tlist_member(v, sub_tlist) == NULL)
  444. {
  445. /*
  446.  * Make sure sub_tlist element is a fresh object not shared
  447.  * with any other structure; not sure if anything will break
  448.  * if it is shared, but better to be safe...
  449.  */
  450. sub_tlist = lappend(sub_tlist,
  451. create_tl_element((Var *) copyObject(v),
  452.   next_resno));
  453. next_resno++;
  454. }
  455. }
  456. return sub_tlist;
  457. }
  458. static Plan *
  459. make_groupplan(List *group_tlist,
  460.    bool tuplePerGroup,
  461.    List *groupClause,
  462.    AttrNumber *grpColIdx,
  463.    Plan *subplan)
  464. {
  465. List    *sort_tlist;
  466. List    *sl;
  467. Sort    *sortplan;
  468. Group    *grpplan;
  469. int numCols = length(groupClause);
  470. /*
  471.  * Make the targetlist for the Sort node; it always just references
  472.  * each of the corresponding target items of the subplan.  We need to
  473.  * ensure that simple Vars in the subplan's target list are
  474.  * recognizable by replace_vars_with_subplan_refs when it's applied to
  475.  * the Sort/Group target list, so copy up their varnoold/varoattno.
  476.  */
  477. sort_tlist = NIL;
  478. foreach(sl, subplan->targetlist)
  479. {
  480. TargetEntry *te = (TargetEntry *) lfirst(sl);
  481. Resdom    *resdom = te->resdom;
  482. Var    *newvar;
  483. if (IsA(te->expr, Var))
  484. {
  485. Var    *subvar = (Var *) te->expr;
  486. newvar = makeVar(1, resdom->resno,
  487.  resdom->restype, resdom->restypmod,
  488.  0, subvar->varnoold, subvar->varoattno);
  489. }
  490. else
  491. {
  492. newvar = makeVar(1, resdom->resno,
  493.  resdom->restype, resdom->restypmod,
  494.  0, -1, resdom->resno);
  495. }
  496. sort_tlist = lappend(sort_tlist,
  497.    makeTargetEntry((Resdom *) copyObject(resdom),
  498.    (Node *) newvar));
  499. }
  500. /*
  501.  * Make the Sort node
  502.  */
  503. sortplan = make_sort(sort_tlist,
  504.  _NONAME_RELATION_ID_,
  505.  subplan,
  506.  numCols);
  507. sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
  508. /*
  509.  * If the caller gave us a target list, use it after fixing the
  510.  * variables. If not, we need the same sort of "repeater" tlist as for
  511.  * the Sort node.
  512.  */
  513. if (group_tlist)
  514. {
  515. group_tlist = copyObject(group_tlist); /* necessary?? */
  516. replace_tlist_with_subplan_refs(group_tlist,
  517. (Index) 0,
  518. subplan->targetlist);
  519. }
  520. else
  521. group_tlist = copyObject(sort_tlist);
  522. /*
  523.  * Make the Group node
  524.  */
  525. grpplan = make_group(group_tlist, tuplePerGroup, numCols,
  526.  grpColIdx, sortplan);
  527. return (Plan *) grpplan;
  528. }
  529. /*
  530.  * make_sortplan
  531.  *   Returns a sortplan which is basically a SORT node attached to the
  532.  *   top of the plan returned from the planner.  It also adds the
  533.  *    cost of sorting into the plan.
  534.  *
  535.  * sortkeys: ( resdom1 resdom2 resdom3 ...)
  536.  * sortops:  (sortop1 sortop2 sortop3 ...)
  537.  */
  538. static Plan *
  539. make_sortplan(List *tlist, List *sortcls, Plan *plannode)
  540. {
  541. Plan    *sortplan = (Plan *) NULL;
  542. List    *temp_tlist = NIL;
  543. List    *i = NIL;
  544. Resdom    *resnode = (Resdom *) NULL;
  545. Resdom    *resdom = (Resdom *) NULL;
  546. int keyno = 1;
  547. /*
  548.  * First make a copy of the tlist so that we don't corrupt the the
  549.  * original .
  550.  */
  551. temp_tlist = new_unsorted_tlist(tlist);
  552. foreach(i, sortcls)
  553. {
  554. SortClause *sortcl = (SortClause *) lfirst(i);
  555. resnode = sortcl->resdom;
  556. resdom = tlist_resdom(temp_tlist, resnode);
  557. /*
  558.  * Order the resdom keys and replace the operator OID for each key
  559.  * with the regproc OID.
  560.  */
  561. resdom->reskey = keyno;
  562. resdom->reskeyop = get_opcode(sortcl->opoid);
  563. keyno += 1;
  564. }
  565. sortplan = (Plan *) make_sort(temp_tlist,
  566.   _NONAME_RELATION_ID_,
  567.   (Plan *) plannode,
  568.   length(sortcls));
  569. /*
  570.  * XXX Assuming that an internal sort has no. cost. This is wrong, but
  571.  * given that at this point, we don't know the no. of tuples returned,
  572.  * etc, we can't do better than to add a constant cost. This will be
  573.  * fixed once we move the sort further into the planner, but for now
  574.  * ... functionality....
  575.  */
  576. sortplan->cost = plannode->cost;
  577. return sortplan;
  578. }
  579. /*
  580.  * pg_checkretval() -- check return value of a list of sql parse
  581.  * trees.
  582.  *
  583.  * The return value of a sql function is the value returned by
  584.  * the final query in the function.  We do some ad-hoc define-time
  585.  * type checking here to be sure that the user is returning the
  586.  * type he claims.
  587.  *
  588.  * XXX Why is this function in this module?
  589.  */
  590. void
  591. pg_checkretval(Oid rettype, List *queryTreeList)
  592. {
  593. Query    *parse;
  594. List    *tlist;
  595. List    *rt;
  596. int cmd;
  597. Type typ;
  598. Resdom    *resnode;
  599. Relation reln;
  600. Oid relid;
  601. Oid tletype;
  602. int relnatts;
  603. int i;
  604. /* find the final query */
  605. parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
  606. /*
  607.  * test 1: if the last query is a utility invocation, then there had
  608.  * better not be a return value declared.
  609.  */
  610. if (parse->commandType == CMD_UTILITY)
  611. {
  612. if (rettype == InvalidOid)
  613. return;
  614. else
  615. elog(ERROR, "return type mismatch in function decl: final query is a catalog utility");
  616. }
  617. /* okay, it's an ordinary query */
  618. tlist = parse->targetList;
  619. rt = parse->rtable;
  620. cmd = parse->commandType;
  621. /*
  622.  * test 2: if the function is declared to return no value, then the
  623.  * final query had better not be a retrieve.
  624.  */
  625. if (rettype == InvalidOid)
  626. {
  627. if (cmd == CMD_SELECT)
  628. elog(ERROR,
  629.  "function declared with no return type, but final query is a retrieve");
  630. else
  631. return;
  632. }
  633. /* by here, the function is declared to return some type */
  634. if ((typ = typeidType(rettype)) == NULL)
  635. elog(ERROR, "can't find return type %u for functionn", rettype);
  636. /*
  637.  * test 3: if the function is declared to return a value, then the
  638.  * final query had better be a retrieve.
  639.  */
  640. if (cmd != CMD_SELECT)
  641. elog(ERROR, "function declared to return type %s, but final query is not a retrieve", typeTypeName(typ));
  642. /*
  643.  * test 4: for base type returns, the target list should have exactly
  644.  * one entry, and its type should agree with what the user declared.
  645.  */
  646. if (typeTypeRelid(typ) == InvalidOid)
  647. {
  648. if (ExecTargetListLength(tlist) > 1)
  649. elog(ERROR, "function declared to return %s returns multiple values in final retrieve", typeTypeName(typ));
  650. resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
  651. if (resnode->restype != rettype)
  652. elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", typeTypeName(typ), typeidTypeName(resnode->restype));
  653. /* by here, base return types match */
  654. return;
  655. }
  656. /*
  657.  * If the target list is of length 1, and the type of the varnode in
  658.  * the target list is the same as the declared return type, this is
  659.  * okay.  This can happen, for example, where the body of the function
  660.  * is 'retrieve (x = func2())', where func2 has the same return type
  661.  * as the function that's calling it.
  662.  */
  663. if (ExecTargetListLength(tlist) == 1)
  664. {
  665. resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
  666. if (resnode->restype == rettype)
  667. return;
  668. }
  669. /*
  670.  * By here, the procedure returns a (set of) tuples.  This part of the
  671.  * typechecking is a hack. We look up the relation that is the
  672.  * declared return type, and be sure that attributes 1 .. n in the
  673.  * target list match the declared types.
  674.  */
  675. reln = heap_open(typeTypeRelid(typ));
  676. if (!RelationIsValid(reln))
  677. elog(ERROR, "cannot open relation relid %u", typeTypeRelid(typ));
  678. relid = reln->rd_id;
  679. relnatts = reln->rd_rel->relnatts;
  680. if (ExecTargetListLength(tlist) != relnatts)
  681. elog(ERROR, "function declared to return type %s does not retrieve (%s.*)", typeTypeName(typ), typeTypeName(typ));
  682. /* expect attributes 1 .. n in order */
  683. for (i = 1; i <= relnatts; i++)
  684. {
  685. TargetEntry *tle = lfirst(tlist);
  686. Node    *thenode = tle->expr;
  687. tlist = lnext(tlist);
  688. tletype = exprType(thenode);
  689. #ifdef NOT_USED /* fix me */
  690. /* this is tedious */
  691. if (IsA(thenode, Var))
  692. tletype = (Oid) ((Var *) thenode)->vartype;
  693. else if (IsA(thenode, Const))
  694. tletype = (Oid) ((Const *) thenode)->consttype;
  695. else if (IsA(thenode, Param))
  696. tletype = (Oid) ((Param *) thenode)->paramtype;
  697. else if (IsA(thenode, Expr))
  698. tletype = Expr;
  699. else if (IsA(thenode, LispList))
  700. {
  701. thenode = lfirst(thenode);
  702. if (IsA(thenode, Oper))
  703. tletype = (Oid) get_opresulttype((Oper *) thenode);
  704. else if (IsA(thenode, Func))
  705. tletype = (Oid) get_functype((Func *) thenode);
  706. else
  707. elog(ERROR, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ));
  708. }
  709. else
  710. elog(ERROR, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ));
  711. #endif
  712. /* reach right in there, why don't you? */
  713. if (tletype != reln->rd_att->attrs[i - 1]->atttypid)
  714. elog(ERROR, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ));
  715. }
  716. heap_close(reln);
  717. /* success */
  718. return;
  719. }
  720. /* ----------
  721.  * Support function for need_sortplan
  722.  * ----------
  723.  */
  724. static TargetEntry *
  725. get_matching_tle(Plan *plan, Resdom *resdom)
  726. {
  727. List    *i;
  728. TargetEntry *tle;
  729. foreach(i, plan->targetlist)
  730. {
  731. tle = (TargetEntry *) lfirst(i);
  732. if (tle->resdom->resno == resdom->resno)
  733. return tle;
  734. }
  735. return NULL;
  736. }
  737. /* ----------
  738.  * Check if a user requested ORDER BY is already satisfied by
  739.  * the choosen index scan.
  740.  *
  741.  * Returns TRUE if sort is required, FALSE if can be omitted.
  742.  * ----------
  743.  */
  744. static bool
  745. need_sortplan(List *sortcls, Plan *plan)
  746. {
  747. Relation indexRel;
  748. IndexScan  *indexScan;
  749. Oid indexId;
  750. List    *i;
  751. HeapTuple htup;
  752. Form_pg_index index_tup;
  753. int key_no = 0;
  754. /* ----------
  755.  * Must be an IndexScan
  756.  * ----------
  757.  */
  758. if (nodeTag(plan) != T_IndexScan)
  759. return TRUE;
  760. indexScan = (IndexScan *) plan;
  761. /* ----------
  762.  * Should not have left- or righttree
  763.  * ----------
  764.  */
  765. if (plan->lefttree != NULL)
  766. return TRUE;
  767. if (plan->righttree != NULL)
  768. return TRUE;
  769. /* ----------
  770.  * Must be a single index scan
  771.  * ----------
  772.  */
  773. if (length(indexScan->indxid) != 1)
  774. return TRUE;
  775. /* ----------
  776.  * Indices can only have up to 8 attributes. So an ORDER BY using
  777.  * more that 8 attributes could never be satisfied by an index.
  778.  * ----------
  779.  */
  780. if (length(sortcls) > 8)
  781. return TRUE;
  782. /* ----------
  783.  * The choosen Index must be a btree
  784.  * ----------
  785.  */
  786. indexId = lfirsti(indexScan->indxid);
  787. indexRel = index_open(indexId);
  788. if (strcmp(nameout(&(indexRel->rd_am->amname)), "btree") != 0)
  789. {
  790. heap_close(indexRel);
  791. return TRUE;
  792. }
  793. heap_close(indexRel);
  794. /* ----------
  795.  * Fetch the index tuple
  796.  * ----------
  797.  */
  798. htup = SearchSysCacheTuple(INDEXRELID,
  799.    ObjectIdGetDatum(indexId), 0, 0, 0);
  800. if (!HeapTupleIsValid(htup))
  801. elog(ERROR, "cache lookup for index %u failed", indexId);
  802. index_tup = (Form_pg_index) GETSTRUCT(htup);
  803. /* ----------
  804.  * Check if all the sort clauses match the attributes in the index
  805.  * ----------
  806.  */
  807. foreach(i, sortcls)
  808. {
  809. SortClause *sortcl;
  810. Resdom    *resdom;
  811. TargetEntry *tle;
  812. Var    *var;
  813. sortcl = (SortClause *) lfirst(i);
  814. resdom = sortcl->resdom;
  815. tle = get_matching_tle(plan, resdom);
  816. if (tle == NULL)
  817. {
  818. /* ----------
  819.  * Could this happen?
  820.  * ----------
  821.  */
  822. return TRUE;
  823. }
  824. if (nodeTag(tle->expr) != T_Var)
  825. {
  826. /* ----------
  827.  * The target list expression isn't a var, so it
  828.  * cannot be the indexed attribute
  829.  * ----------
  830.  */
  831. return TRUE;
  832. }
  833. var = (Var *) (tle->expr);
  834. if (var->varno != indexScan->scan.scanrelid)
  835. {
  836. /* ----------
  837.  * This Var isn't from the scan relation. So it isn't
  838.  * that of the index
  839.  * ----------
  840.  */
  841. return TRUE;
  842. }
  843. if (var->varattno != index_tup->indkey[key_no])
  844. {
  845. /* ----------
  846.  * It isn't the indexed attribute.
  847.  * ----------
  848.  */
  849. return TRUE;
  850. }
  851. if (oprid(oper("<", resdom->restype, resdom->restype, FALSE)) != sortcl->opoid)
  852. {
  853. /* ----------
  854.  * Sort order isn't in ascending order.
  855.  * ----------
  856.  */
  857. return TRUE;
  858. }
  859. key_no++;
  860. }
  861. /* ----------
  862.  * Index matches ORDER BY - sort not required
  863.  * ----------
  864.  */
  865. return FALSE;
  866. }