📄 predmig.c
字号:
/*------------------------------------------------------------------------- * * predmig.c * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/optimizer/path/_deadcode/predmig.c,v 1.4 1999/05/25 22:41:35 momjian Exp $ * *------------------------------------------------------------------------- *//*** DESCRIPTION** Main Routines to handle Predicate Migration (i.e. correct optimization** of queries with expensive functions.)**** The reasoning behind some of these algorithms is rather detailed.** Have a look at Sequoia Tech Report 92/13 for more info. Also** see Monma and Sidney's paper "Sequencing with Series-Parallel** Precedence Constraints", in "Mathematics of Operations Research",** volume 4 (1979), pp. 215-224.**** The main thing that this code does that wasn't handled in xfunc.c is** it considers the possibility that two joins in a stream may not** be ordered by ascending rank -- in such a scenario, it may be optimal** to pullup more restrictions than we did via xfunc_try_pullup.**** This code in some sense generalizes xfunc_try_pullup; if you** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this** code will do everything that xfunc_try_pullup would have, and maybe** more. However, this results in no pruning, which may slow down the** optimizer and/or cause the system to run out of memory.** -- JMH, 11/13/92*/#include "nodes/pg_list.h"#include "nodes/nodes.h"#include "nodes/primnodes.h"#include "nodes/relation.h"#include "utils/palloc.h"#include "utils/elog.h"#include "optimizer/xfunc.h"#include "optimizer/pathnode.h"#include "optimizer/internal.h"#include "optimizer/cost.h"#include "optimizer/keys.h"#include "optimizer/tlist.h"#define is_clause(node) (get_cinfo(node)) /* a stream node * represents a clause * (not a join) iff it has * a non-NULL cinfo field */static void xfunc_predmig(JoinPath pathnode, Stream streamroot, Stream laststream, bool *progressp);static bool xfunc_series_llel(Stream stream);static bool xfunc_llel_chains(Stream root, Stream bottom);static Stream xfunc_complete_stream(Stream stream);static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath);static void xfunc_form_groups(Stream root, Stream bottom);static void xfunc_free_stream(Stream root);static Stream xfunc_add_clauses(Stream current);static void xfunc_setup_group(Stream node, Stream bottom);static Stream xfunc_streaminsert(RestrictInfo restrictinfo, Stream current, int clausetype);static int xfunc_num_relids(Stream node);static StreamPtr xfunc_get_downjoin(Stream node);static StreamPtr xfunc_get_upjoin(Stream node);static Stream xfunc_stream_qsort(Stream root, Stream bottom);static int xfunc_stream_compare(void *arg1, void *arg2);static bool xfunc_check_stream(Stream node);static bool xfunc_in_stream(Stream node, Stream stream);/* ----------------- MAIN FUNCTIONS ------------------------ *//*** xfunc_do_predmig** wrapper for Predicate Migration. It calls xfunc_predmig until no** more progress is made.** return value says if any changes were ever made.*/boolxfunc_do_predmig(Path root){ bool progress, changed = false; if (is_join(root)) do { progress = false; Assert(IsA(root, JoinPath)); xfunc_predmig((JoinPath) root, (Stream) NULL, (Stream) NULL, &progress); if (changed && progress) elog(DEBUG, "Needed to do a second round of predmig!\n"); if (progress) changed = true; } while (progress); return changed;}/* ** xfunc_predmig ** The main routine for Predicate Migration. It traverses a join tree, ** and for each root-to-leaf path in the plan tree it constructs a ** "Stream", which it passes to xfunc_series_llel for optimization. ** Destructively modifies the join tree (via predicate pullup). */static voidxfunc_predmig(JoinPath pathnode,/* root of the join tree */ Stream streamroot, Stream laststream,/* for recursive calls -- these are the * root of the stream under construction, * and the lowest node created so far */ bool *progressp){ Stream newstream; /* * * traverse the join tree dfs-style, constructing a stream as you * go. * When you hit a scan node, pass the stream off to * xfunc_series_llel. */ /* sanity check */ if ((!streamroot && laststream) || (streamroot && !laststream)) elog(ERROR, "called xfunc_predmig with bad inputs"); if (streamroot) Assert(xfunc_check_stream(streamroot)); /* add path node to stream */ newstream = RMakeStream(); if (!streamroot) streamroot = newstream; set_upstream(newstream, (StreamPtr) laststream); if (laststream) set_downstream(laststream, (StreamPtr) newstream); set_downstream(newstream, (StreamPtr) NULL); set_pathptr(newstream, (pathPtr) pathnode); set_cinfo(newstream, (RestrictInfo) NULL); set_clausetype(newstream, XFUNC_UNKNOWN); /* base case: we're at a leaf, call xfunc_series_llel */ if (!is_join(pathnode)) { /* form a fleshed-out copy of the stream */ Stream fullstream = xfunc_complete_stream(streamroot); /* sort it via series-llel */ if (xfunc_series_llel(fullstream)) *progressp = true; /* free up the copy */ xfunc_free_stream(fullstream); } else { /* visit left child */ xfunc_predmig((JoinPath) get_outerjoinpath(pathnode), streamroot, newstream, progressp); /* visit right child */ xfunc_predmig((JoinPath) get_innerjoinpath(pathnode), streamroot, newstream, progressp); } /* remove this node */ if (get_upstream(newstream)) set_downstream((Stream) get_upstream(newstream), (StreamPtr) NULL); pfree(newstream);}/* ** xfunc_series_llel ** A flavor of Monma and Sidney's Series-Parallel algorithm. ** Traverse stream downwards. When you find a node with restrictions on it, ** call xfunc_llel_chains on the substream from root to that node. */static boolxfunc_series_llel(Stream stream){ Stream temp, next; bool progress = false; for (temp = stream; temp != (Stream) NULL; temp = next) { next = (Stream) xfunc_get_downjoin(temp); /* * * if there are restrictions/secondary join clauses above this * * node, call xfunc_llel_chains */ if (get_upstream(temp) && is_clause((Stream) get_upstream(temp))) if (xfunc_llel_chains(stream, temp)) progress = true; } return progress;}/* ** xfunc_llel_chains ** A flavor of Monma and Sidney's Parallel Chains algorithm. ** Given a stream which has been well-ordered except for its lowermost ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. ** What that means here is to form groups in the chain above the lowest ** join node above bottom inclusive, and then take all the restrictions ** following bottom, and try to pull them up as far as possible. */static boolxfunc_llel_chains(Stream root, Stream bottom){ bool progress = false; Stream origstream; Stream tmpstream, pathstream; Stream rootcopy = root; Assert(xfunc_check_stream(root)); /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ origstream = (Stream) copyObject((Node) root); /* form groups among ill-ordered nodes */ xfunc_form_groups(root, bottom); /* sort chain by rank */ Assert(xfunc_in_stream(bottom, root)); rootcopy = xfunc_stream_qsort(root, bottom); /* * * traverse sorted stream -- if any restriction has moved above a * join, * we must pull it up in the plan. That is, make plan tree * * reflect order of sorted stream. */ for (tmpstream = rootcopy, pathstream = (Stream) xfunc_get_downjoin(rootcopy); tmpstream != (Stream) NULL && pathstream != (Stream) NULL; tmpstream = (Stream) get_downstream(tmpstream)) { if (is_clause(tmpstream) && get_pathptr(pathstream) != get_pathptr(tmpstream)) { /* * * If restriction moved above a Join after sort, we pull it * * up in the join plan. * If restriction moved down, we * ignore it. * This is because Joey's Sequoia paper proves * that * restrictions should never move down. If this * one * were moved down, it would violate "semantic correctness", * * i.e. it would be lower than the attributes it references. */ Assert(xfunc_num_relids(pathstream) > xfunc_num_relids(tmpstream)); progress = xfunc_prdmig_pullup(origstream, tmpstream, (JoinPath) get_pathptr(pathstream)); } if (get_downstream(tmpstream)) pathstream = (Stream) xfunc_get_downjoin((Stream) get_downstream(tmpstream)); } /* free up origstream */ xfunc_free_stream(origstream); return progress;}/* ** xfunc_complete_stream ** Given a stream composed of join nodes only, make a copy containing the ** join nodes along with the associated restriction nodes. */static Streamxfunc_complete_stream(Stream stream){ Stream tmpstream, copystream, curstream = (Stream) NULL; copystream = (Stream) copyObject((Node) stream); Assert(xfunc_check_stream(copystream)); curstream = copystream; Assert(!is_clause(curstream)); /* curstream = (Stream)xfunc_get_downjoin(curstream); */ while (curstream != (Stream) NULL) { xfunc_add_clauses(curstream); curstream = (Stream) xfunc_get_downjoin(curstream); } /* find top of stream and return it */ for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr) NULL; tmpstream = (Stream) get_upstream(tmpstream)) /* no body in for loop */ ; return tmpstream;}/* ** xfunc_prdmig_pullup ** pullup a clause in a path above joinpath. Since the JoinPath tree ** doesn't have upward pointers, it's difficult to deal with. Thus we ** require the original stream, which maintains pointers to all the path ** nodes. We use the original stream to find out what joins are ** above the clause. */static boolxfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath){ RestrictInfo restrictinfo = get_cinfo(pullme); bool progress = false; Stream upjoin, orignode, temp; int whichchild; /* find node in origstream that contains clause */ for (orignode = origstream; orignode != (Stream) NULL && get_cinfo(orignode) != restrictinfo; orignode = (Stream) get_downstream(orignode)) /* empty body in for loop */ ; if (!orignode) elog(ERROR, "Didn't find matching node in original stream"); /* pull up this node as far as it should go */ for (upjoin = (Stream) xfunc_get_upjoin(orignode); upjoin != (Stream) NULL && (JoinPath) get_pathptr((Stream) xfunc_get_downjoin(upjoin)) != joinpath; upjoin = (Stream) xfunc_get_upjoin(upjoin)) {#ifdef DEBUG elog(DEBUG, "pulling up in xfunc_predmig_pullup!");#endif /* move clause up in path */ if (get_pathptr((Stream) get_downstream(upjoin)) == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))) whichchild = OUTER; else whichchild = INNER; restrictinfo = xfunc_pullup((Path) get_pathptr((Stream) get_downstream(upjoin)), (JoinPath) get_pathptr(upjoin), restrictinfo, whichchild, get_clausetype(orignode)); set_pathptr(pullme, get_pathptr(upjoin)); /* pullme has been moved into locrestrictinfo */ set_clausetype(pullme, XFUNC_LOCPRD); /* * * xfunc_pullup makes new path nodes for children of * * get_pathptr(current). We must modify the stream nodes to point * * to these path nodes */ if (whichchild == OUTER) { for (temp = (Stream) get_downstream(upjoin); is_clause(temp); temp = (Stream) get_downstream(temp)) set_pathptr (temp, (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr (temp, (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))); } else { for (temp = (Stream) get_downstream(upjoin); is_clause(temp); temp = (Stream) get_downstream(temp)) set_pathptr (temp, (pathPtr) get_innerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr (temp, (pathPtr) get_innerjoinpath((JoinPath) get_pathptr(upjoin))); } progress = true; } if (!progress) elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); return progress;}/* ** xfunc_form_groups ** A group is a pair of stream nodes a,b such that a is constrained to ** precede b (for instance if a and b are both joins), but rank(a) > rank(b). ** In such a situation, Monma and Sidney prove that no clauses should end ** up between a and b, and therefore we may treat them as a group, with ** selectivity equal to the product of their selectivities, and cost ** equal to the cost of the first plus the selectivity of the first times the ** cost of the second. We define each node to be in a group by itself, ** and then repeatedly find adjacent groups which are ordered by descending ** rank, and make larger groups. You know that two adjacent nodes are in a ** group together if the lower has groupup set to true. They will both have ** the same groupcost and groupsel (since they're in the same group!) */static voidxfunc_form_groups(Query *queryInfo, Stream root, Stream bottom)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -