package com.taobao.tddl.optimizer.costbased.chooser; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.taobao.tddl.optimizer.core.ast.ASTNode; import com.taobao.tddl.optimizer.core.ast.QueryTreeNode; import com.taobao.tddl.optimizer.core.ast.query.JoinNode; import com.taobao.tddl.optimizer.core.ast.query.QueryNode; import com.taobao.tddl.optimizer.core.ast.query.TableNode; import com.taobao.tddl.optimizer.core.expression.IBooleanFilter; import com.taobao.tddl.optimizer.core.expression.IFilter; import com.taobao.tddl.optimizer.core.expression.IOrderBy; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.optimizer.utils.PermutationGenerator; /** * 对于inner join的所有节点生成一个全排列 * * @author Dreamond */ public final class JoinPermutationGenerator { private PermutationGenerator pg = null; private List<IOrderBy> orderBys; private IFilter valueFilter; private Comparable limitFrom; private Comparable limitTo; private List<IOrderBy> groupBys; /** * A join B on A.id = B.id 转变为 {A.id-> {B.id->BF[A.id=B.id]}, B.id-> * {A.id->BF[A.id=B.id]} } * * <pre> * 第一层key: one column * 第二层key:a pair of Key col * 第三层value : the whole join on columns pair * </pre> */ private Map<ISelectable, Map<ISelectable, IBooleanFilter>> joinColumnsAndFilter = new HashMap(); private List<QueryTreeNode> queryNodes = new ArrayList(); private List<ISelectable> columns; private IFilter allWhereFilter; public JoinPermutationGenerator(QueryTreeNode qtn){ this.orderBys = qtn.getOrderBys(); this.groupBys = qtn.getGroupBys(); this.columns = qtn.getColumnsSelected(); this.valueFilter = qtn.getResultFilter(); this.limitFrom = qtn.getLimitFrom(); this.limitTo = qtn.getLimitTo(); this.allWhereFilter = qtn.getAllWhereFilter(); this.getQueryNodesFromQueryTree(qtn); pg = new PermutationGenerator(this.queryNodes); visit(qtn); } /** * 从查询树中收集不会再被调整Join顺序的子树 * * <pre> * 包括: * 1、非InnerJoin的Join节点。因为既然用户已经指定了left或者right,说明用户已经指定了outter的就为驱动表,所以无需再做额外的调整 * 2、被标记为子查询的Join节点,子查询中的节点会单独去调整 * 3、不在1中的Query节点 * </pre> */ private void getQueryNodesFromQueryTree(QueryTreeNode node) { if (node instanceof JoinNode) { if (!((JoinNode) node).isInnerJoin() || node.isSubQuery() || ((JoinNode) node).getOtherJoinOnFilter() != null) { this.queryNodes.add(node); return; } } if (node instanceof TableNode || node instanceof QueryNode) { this.queryNodes.add(node); return; } for (ASTNode child : node.getChildren()) { getQueryNodesFromQueryTree((QueryTreeNode) child); } } private void visit(QueryTreeNode node) { if (node instanceof JoinNode) { List<ISelectable> leftColumns = ((JoinNode) node).getLeftKeys(); List<ISelectable> rightColumns = ((JoinNode) node).getRightKeys(); assert (leftColumns.size() == rightColumns.size()); for (IFilter filter : ((JoinNode) node).getJoinFilter()) { addJoinFilter((IBooleanFilter) filter); } } for (ASTNode child : node.getChildren()) { visit((QueryTreeNode) child); } } private void addJoinFilter(IBooleanFilter filter) { ISelectable left = (ISelectable) filter.getColumn(); ISelectable right = (ISelectable) filter.getValue(); if (!this.joinColumnsAndFilter.containsKey(left)) { this.joinColumnsAndFilter.put(left, new HashMap()); } this.joinColumnsAndFilter.get(left).put(right, filter); // add by shenxun : 这里应该进行对调,map中应该维持左->右这个关系 IBooleanFilter ibfnew = convertJoinOnColumns(filter); if (!this.joinColumnsAndFilter.containsKey(right)) { this.joinColumnsAndFilter.put(right, new HashMap()); } this.joinColumnsAndFilter.get(right).put(left, ibfnew); } private IBooleanFilter convertJoinOnColumns(IBooleanFilter filter) { IBooleanFilter newbf = filter.copy(); newbf.setColumn(filter.getValue()); newbf.setValue((Comparable) filter.getColumn()); return newbf; } public QueryTreeNode getNext() { while (pg.hasNext()) { List<QueryTreeNode> nodes = pg.next(); for (int i = 0; i < nodes.size(); i++) { nodes.set(i, nodes.get(i).deepCopy()); } QueryTreeNode newTree = getQueryTreeFromQueryNodes(nodes); if (newTree != null) { newTree.setOrderBys(orderBys); newTree.setResultFilter(this.valueFilter); newTree.setGroupBys(groupBys); newTree.select(this.columns); newTree.setLimitFrom(limitFrom); newTree.setLimitTo(limitTo); newTree.setAllWhereFilter(allWhereFilter); return newTree; } } return null; } /** * 构造一个join */ private QueryTreeNode getQueryTreeFromQueryNodes(List<QueryTreeNode> nodes) { if (nodes.size() == 1) { return nodes.get(0); } JoinNode join = null; for (int i = 1; i < nodes.size(); i++) { List<IBooleanFilter> filterToJoinOn; if (join == null) { filterToJoinOn = canJoinAndThenReturnFilters(nodes.get(i - 1), nodes.get(i)); if (filterToJoinOn == null || filterToJoinOn.isEmpty()) { return null; } join = nodes.get(i - 1).join(nodes.get(i)); join.setJoinFilter(filterToJoinOn); } else { filterToJoinOn = canJoinAndThenReturnFilters(join, nodes.get(i)); if (filterToJoinOn == null) { return null; } join = join.join(nodes.get(i)); join.setJoinFilter(filterToJoinOn); } join.build(); } return join; } /** * 找到left/right节点存在的join条件 */ private List<IBooleanFilter> canJoinAndThenReturnFilters(QueryTreeNode leftNode, QueryTreeNode rightNode) { List<IBooleanFilter> filters = new LinkedList(); for (ISelectable leftColumn : leftNode.getColumnsSelectedForParent()) { if (!this.joinColumnsAndFilter.containsKey(leftColumn)) { continue; } Map<ISelectable, IBooleanFilter> rightColumnsAndFilter = this.joinColumnsAndFilter.get(leftColumn); List<ISelectable> rightColumns = rightNode.getColumnsSelectedForParent(); for (ISelectable rightColumn : rightColumnsAndFilter.keySet()) { if (rightColumns.contains(rightColumn)) { filters.add(rightColumnsAndFilter.get(rightColumn)); } } } return filters; } }