// We assume plan is with normalized shape: // LogicFilter // LogicJoin // ... // There shall be only 1 join filter on top. // Subqueries is not considered here. // internal static JoinGraph ExtractJoinGraph(LogicNode plan, out LogicNode filterNodeParent, out int index, out LogicFilter filterNode) { // find the join filter var parents = new List <LogicNode>(); var indexes = new List <int>(); var filters = new List <LogicFilter>(); plan.FindNodeTypeMatch <LogicFilter>(parents, indexes, filters); var joinfilters = filters.Where(x => x.child_() is LogicJoin).ToList(); Debug.Assert(joinfilters.Count <= 1); if (joinfilters.Count == 1) { JoinGraph graph = null; var joinfilter = joinfilters[0]; var topjoin = joinfilter.child_() as LogicJoin; // vertices are non-join nodes. We don't do any cross boundary optimization // (say pull aggregation up thus we have bigger join space etc), which is // the job of upper layer. // var vertices = new List <LogicNode>(); topjoin.VisitEach(x => { if (!(x is LogicJoin)) { vertices.Add(x as LogicNode); } }); graph = new JoinGraph(vertices, joinfilters[0].filter_.FilterToAndList()); index = indexes[0]; filterNodeParent = parents[0]; filterNode = joinfilter; Debug.Assert(filterNodeParent is null || filterNodeParent.children_[index] == filterNode); return(graph); } // there is no join or we can't handle this query filterNodeParent = null; index = -1; filterNode = null; return(null); }
LogicNode FilterPushDown(LogicNode plan, bool pushJoinFilter) { // locate the all filters ingoring any within FromQuery as it will // be optimized by subquery optimization and this will cause double // predicate push down (e.g., a1>1 && a1>1) // var parents = new List <LogicNode>(); var indexes = new List <int>(); var filters = new List <LogicFilter>(); var cntFilter = plan.FindNodeTypeMatch(parents, indexes, filters, skipParentType: typeof(LogicFromQuery)); for (int i = 0; i < cntFilter; i++) { var parent = parents[i]; var filter = filters[i]; var index = indexes[i]; Debug.Assert(!(parent is LogicFromQuery)); if (filter?.filter_ != null && filter?.movable_ is true) { List <Expr> andlist = new List <Expr>(); var filterexpr = filter.filter_; // if it is a constant true filer, remove it. If a false filter, we leave // it there - shall we try hard to stop query early? Nope, it is no deserved // to poke around for this corner case. // var isConst = filterexpr.FilterIsConst(out bool trueOrFalse); if (isConst) { if (!trueOrFalse) { andlist.Add(LiteralExpr.MakeLiteral("false", new BoolType())); } else { Debug.Assert(andlist.Count == 0); } } else { // filter push down andlist = filterexpr.FilterToAndList(); andlist.RemoveAll(e => { var isConst = e.FilterIsConst(out bool trueOrFalse); if (isConst) { Debug.Assert(trueOrFalse); return(true); } return(pushdownFilter(plan, e, pushJoinFilter)); }); } // stich the new plan if (andlist.Count == 0) { if (parent is null) { // take it out from the tree plan = plan.child_(); } else { parent.children_[index] = filter.child_(); } } else { filter.filter_ = andlist.AndListToExpr(); } } } return(plan); }