package com.taobao.tddl.optimizer.costbased.pusher;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import com.taobao.tddl.optimizer.core.ASTNodeFactory;
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.expression.IBooleanFilter;
import com.taobao.tddl.optimizer.core.expression.IColumn;
import com.taobao.tddl.optimizer.core.expression.IFilter;
import com.taobao.tddl.optimizer.core.expression.IFilter.OPERATION;
import com.taobao.tddl.optimizer.core.expression.ISelectable;
import com.taobao.tddl.optimizer.exceptions.QueryException;
import com.taobao.tddl.optimizer.utils.FilterUtils;
import com.taobao.tddl.optimizer.utils.OptimizerUtils;
/**
* 将filter进行下推
*
* <pre>
* a. 如果条件中包含||条件则暂不优化,下推时会导致语义不正确
* b. 如果条件中的column/value包含function,也不做下推 (比较麻烦,需要递归处理函数中的字段信息,同时检查是否符合下推条件,先简答处理)
* c. 如果条件中的column/value中的字段来自于子节点的函数查询,也不做下推
*
* 几种场景:
* 1. where条件尽可能提前到叶子节点,同时提取出joinFilter
* 处理类型: JoinNode/QueryNode
* 注意点:JoinNode如果是outter节点,则不能继续下推
*
* 如: tabl1.join(table2).query("table1.id>5 && table2.id<10 && table1.name = table2.name")
* 优化成: table1.query("table1.id>5").join(table2.query("table2.id<10").on("table1.name = table2.name")
*
* 如: table1.join(table2).query("table1.id = table2.id")
* 优化成:table1.join(table2).on("table1.id = table2.id")
*
* 2. join中的非字段列条件,比如column = 1的常量关系,提前到叶子节点
* 处理类型:JoinNode
* 注意点:
*
* 如: tabl1.join(table2).on("table1.id>5&&table2.id<10")
* 优化成: table1.query("table1.id>5").join(table2.query("table2.id<10")) t但如果条件中包含
*
* 3. join filter中的字段进行条件推导到左/右的叶子节点上,在第1和第2步优化中同时处理
* 处理类型:JoinNode
*
* 如: table.join(table2).on("table1.id = table2.id and table1.id>5 && table2.id<10")
* 优化成:table1.query("table1.id>5 && table1.id<10").join(table2.query("table2.id>5 && table2.id<10"))
*/
public class FilterPusher {
/**
* 详细优化见类描述 {@linkplain FilterPusher}
*/
public static QueryTreeNode optimize(QueryTreeNode qtn) throws QueryException {
qtn = pushFilter(qtn, null);
qtn = pushJoinOnFilter(qtn, null);
qtn.build();
return qtn;
}
private static QueryTreeNode pushFilter(QueryTreeNode qtn, List<IFilter> DNFNodeToPush) throws QueryException {
// 如果是根节点,接收filter做为where条件,否则继续合并当前where条件,然后下推
if (qtn.getChildren().isEmpty()) {
IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToPush);
if (node != null) {
qtn.query(FilterUtils.and(qtn.getWhereFilter(), node));
qtn.build();
}
return qtn;
}
// 对于or连接的条件,就不能下推了
IFilter filterInWhere = qtn.getWhereFilter();
if (filterInWhere != null && FilterUtils.isCNFNode(filterInWhere)) {
List DNFNode = FilterUtils.toDNFNode(filterInWhere);
qtn.query((IFilter) null);// 清空where条件
if (DNFNodeToPush == null) {
DNFNodeToPush = new ArrayList<IFilter>();
}
DNFNodeToPush.addAll(OptimizerUtils.copyFilter(DNFNode));// 需要复制一份出来
}
if (qtn instanceof QueryNode) {
QueryNode qn = (QueryNode) qtn;
List<IFilter> DNFNodeToCurrent = new LinkedList<IFilter>();
if (DNFNodeToPush != null) {
for (IFilter node : DNFNodeToPush) {
// 可能是多级节点,字段在select中,设置为select中的字段,这样才可以继续下推
if (!tryPushColumn(node, qn.getChild())) {
// 可能where条件是函数,暂时不下推
DNFNodeToCurrent.add(node);
}
}
DNFNodeToPush.removeAll(DNFNodeToCurrent);
}
QueryTreeNode child = pushFilter(qn.getChild(), DNFNodeToPush);
// 针对不能下推的,合并到当前的where
IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToCurrent);
if (node != null) {
qtn.query(FilterUtils.and(qtn.getWhereFilter(), node));
}
((QueryNode) qtn).setChild(child);
qtn.build();
} else if (qtn instanceof JoinNode) {
JoinNode jn = (JoinNode) qtn;
List<IFilter> DNFNodetoPushToLeft = new LinkedList<IFilter>();
List<IFilter> DNFNodetoPushToRight = new LinkedList<IFilter>();
List<IFilter> DNFNodeToCurrent = new LinkedList<IFilter>();
if (DNFNodeToPush != null) {
// 需要处理不能下推的条件
// 1. 处理a.id=b.id,左右两边都为column列
// 2. 处理a.id = b.id + 1,一边为column,一边为function
// 情况2这种不优化,直接当作where条件处理
findJoinKeysAndRemoveIt(DNFNodeToPush, jn);
for (IFilter node : DNFNodeToPush) {
if (tryPushColumn(node, jn.getLeftNode())) {
DNFNodetoPushToLeft.add(node);
} else if (tryPushColumn(node, jn.getRightNode())) {
DNFNodetoPushToRight.add(node);
} else {
// 可能是函数,不继续下推
DNFNodeToCurrent.add(node);
}
}
// 将左条件的表达式,推导到join filter的右条件上
DNFNodetoPushToRight.addAll(copyFilterToJoinOnColumns(DNFNodeToPush,
jn.getLeftKeys(),
jn.getRightKeys()));
// 将右条件的表达式,推导到join filter的左条件上
DNFNodetoPushToLeft.addAll(copyFilterToJoinOnColumns(DNFNodeToPush, jn.getRightKeys(), jn.getLeftKeys()));
}
// 针对不能下推的,合并到当前的where
IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToCurrent);
if (node != null) {
qtn.query(FilterUtils.and(qtn.getWhereFilter(), node));
}
if (jn.isInnerJoin()) {
jn.setLeftNode(pushFilter(jn.getLeftNode(), DNFNodetoPushToLeft));
jn.setRightNode(pushFilter(((JoinNode) qtn).getRightNode(), DNFNodetoPushToRight));
} else if (jn.isLeftOuterJoin()) {
jn.setLeftNode(pushFilter(jn.getLeftNode(), DNFNodetoPushToLeft));
if (DNFNodeToPush != null && !DNFNodeToPush.isEmpty()) {
jn.query(FilterUtils.DNFToAndLogicTree(DNFNodetoPushToRight)); // 在父节点完成filter,不能下推
}
} else if (jn.isRightOuterJoin()) {
jn.setRightNode(pushFilter(((JoinNode) qtn).getRightNode(), DNFNodetoPushToRight));
if (DNFNodeToPush != null && !DNFNodeToPush.isEmpty()) {
jn.query(FilterUtils.DNFToAndLogicTree(DNFNodetoPushToLeft));// 在父节点完成filter,不能下推
}
} else {
if (DNFNodeToPush != null && !DNFNodeToPush.isEmpty()) {
jn.query(FilterUtils.DNFToAndLogicTree(DNFNodeToPush));
}
}
jn.build();
return jn;
}
return qtn;
}
/**
* 约束条件应该尽量提前,针对join条件中的非join column列,比如column = 1的常量关系
*
* <pre>
* 如: tabl1.join(table2).on("table1.id>10&&table2.id<5")
* 优化成: able1.query("table1.id>10").join(table2.query("table2.id<5")) t但如果条件中包含||条件则暂不优化
* </pre>
*/
private static QueryTreeNode pushJoinOnFilter(QueryTreeNode qtn, List<IFilter> DNFNodeToPush) throws QueryException {
if (qtn.getChildren().isEmpty()) {
IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToPush);
if (node != null) {
qtn.setOtherJoinOnFilter(FilterUtils.and(qtn.getOtherJoinOnFilter(), (IFilter) node.copy()));
qtn.build();
}
return qtn;
}
// 对于 or连接的条件,就不能下推了
IFilter filterInOtherJoin = qtn.getOtherJoinOnFilter();
if (filterInOtherJoin != null && FilterUtils.isCNFNode(filterInOtherJoin)) {
// 需要复制,下推到子节点后,会改变column/value的tableName
List<IFilter> DNFNode = FilterUtils.toDNFNode((IFilter) filterInOtherJoin.copy());
if (DNFNodeToPush == null) {
DNFNodeToPush = new ArrayList<IFilter>();
}
DNFNodeToPush.addAll(DNFNode);
}
if (qtn instanceof QueryNode) {
QueryNode qn = (QueryNode) qtn;
List<IFilter> DNFNodeToCurrent = new LinkedList<IFilter>();
if (DNFNodeToPush != null) {
// 如果是join/query/join,可能需要转一次select column,不然下推就会失败
for (IFilter node : DNFNodeToPush) {
// 可能是多级节点,字段在select中,设置为select中的字段,这样才可以继续下推
// 因为query不可能是顶级节点,只会是传递的中间状态,不需要处理DNFNodeToCurrent
if (!tryPushColumn(node, qn.getChild())) {
// 可能where条件是函数,暂时不下推
DNFNodeToCurrent.add(node);
}
}
DNFNodeToPush.removeAll(DNFNodeToCurrent);
}
QueryTreeNode child = pushJoinOnFilter(qn.getChild(), DNFNodeToPush);
// 针对不能下推的,合并到当前的where
IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToCurrent);
if (node != null) {
qtn.query(FilterUtils.and(qtn.getOtherJoinOnFilter(), (IFilter) node.copy()));
}
((QueryNode) qtn).setChild(child);
qtn.build();
return qn;
} else if (qtn instanceof JoinNode) {
JoinNode jn = (JoinNode) qtn;
List<IFilter> DNFNodetoPushToLeft = new LinkedList<IFilter>();
List<IFilter> DNFNodetoPushToRight = new LinkedList<IFilter>();
List<IFilter> DNFNodeToCurrent = new LinkedList<IFilter>();
if (DNFNodeToPush != null) {
for (IFilter node : DNFNodeToPush) {
if (tryPushColumn(node, jn.getLeftNode())) {
DNFNodetoPushToLeft.add(node);
} else if (tryPushColumn(node, jn.getRightNode())) {
DNFNodetoPushToRight.add(node);
} else {
// 可能是函数,不继续下推
DNFNodeToCurrent.add(node);
}
}
// 将左条件的表达式,推导到join filter的右条件上
// 比如: table a left join table b on (a.id = b.id and b.id = 1)
// 这时对应的b.id = 1的条件不能推导到左表,否则语义不对
if (jn.isInnerJoin() || jn.isLeftOuterJoin()) {
DNFNodetoPushToRight.addAll(copyFilterToJoinOnColumns(DNFNodeToPush,
jn.getLeftKeys(),
jn.getRightKeys()));
}
if (jn.isInnerJoin() || jn.isRightOuterJoin()) {
// 将右条件的表达式,推导到join filter的左条件上
DNFNodetoPushToLeft.addAll(copyFilterToJoinOnColumns(DNFNodeToPush,
jn.getRightKeys(),
jn.getLeftKeys()));
}
}
// 针对不能下推的,合并到当前的where,otherJoinOnFilter暂时不做清理,不需要做合并
// IFilter node = FilterUtils.DNFToAndLogicTree(DNFNodeToCurrent);
// if (node != null) {
// qtn.setOtherJoinOnFilter(FilterUtils.and(qtn.getOtherJoinOnFilter(),
// (IFilter) node.copy()));
// }
pushJoinOnFilter(jn.getLeftNode(), DNFNodetoPushToLeft);
pushJoinOnFilter(jn.getRightNode(), DNFNodetoPushToRight);
jn.build();
return jn;
}
return qtn;
}
/**
* 将连接列上的约束复制到目标节点内
*
* @param DNF 要复制的DNF filter
* @param other 要复制的目标节点
* @param qnColumns 源节点的join字段
* @param otherColumns 目标节点的join字段
* @throws QueryException
*/
private static List<IFilter> copyFilterToJoinOnColumns(List<IFilter> DNF, List<ISelectable> qnColumns,
List<ISelectable> otherColumns) throws QueryException {
List<IFilter> newIFilterToPush = new LinkedList<IFilter>();
for (IFilter bool : DNF) {
int index = qnColumns.indexOf(((IBooleanFilter) bool).getColumn());
if (index >= 0) {// 只考虑在源查找,在目标查找在上一层进行控制
IBooleanFilter o = ASTNodeFactory.getInstance().createBooleanFilter().setOperation(bool.getOperation());
o.setColumn(otherColumns.get(index));
if (bool.getOperation() == OPERATION.IN) {
o.setValues(((IBooleanFilter) bool).getValues());
} else {
o.setValue(((IBooleanFilter) bool).getValue());
}
newIFilterToPush.add(o);
}
}
return newIFilterToPush;
}
/**
* 将原本的Join的where条件中的a.id=b.id构建为join条件,并从where条件中移除
*/
private static void findJoinKeysAndRemoveIt(List<IFilter> DNFNode, JoinNode join) throws QueryException {
// filter中可能包含join列,如id=id
// 目前必须满足以下条件
// 1、不包含or
// 2、=连接
List<IFilter> joinFilters = new LinkedList();
if (isFilterContainsColumnJoin(DNFNode)) {
List<Object> leftJoinKeys = new ArrayList<Object>();
List<Object> rightJoinKeys = new ArrayList<Object>();
for (IFilter sub : DNFNode) { // 一定是简单条件
ISelectable[] keys = getJoinKeysWithColumnJoin((IBooleanFilter) sub);
if (keys != null) {// 存在join column
if (join.getLeftNode().hasColumn(keys[0])) {
if (!join.getLeftNode().hasColumn(keys[1])) {
if (join.getRightNode().hasColumn(keys[1])) {
leftJoinKeys.add(keys[0]);
rightJoinKeys.add(keys[1]);
joinFilters.add(sub);
join.addJoinFilter((IBooleanFilter) sub);
} else {
throw new IllegalArgumentException("join查询表右边不包含join column,请修改查询语句...");
}
} else {
if (!(join.getLeftNode() instanceof JoinNode)) {
throw new IllegalArgumentException("join查询的join column都在左表上,请修改查询语句...");
}
}
} else if (join.getLeftNode().hasColumn(keys[1])) {
if (!join.getLeftNode().hasColumn(keys[0])) {
if (join.getRightNode().hasColumn(keys[0])) {
leftJoinKeys.add(keys[1]);
rightJoinKeys.add(keys[0]);
joinFilters.add(sub);
// 交换一下
Object tmp = ((IBooleanFilter) sub).getColumn();
((IBooleanFilter) sub).setColumn(((IBooleanFilter) sub).getValue());
((IBooleanFilter) sub).setValue(tmp);
join.addJoinFilter((IBooleanFilter) sub);
} else {
throw new IllegalArgumentException("join查询表左边不包含join column,请修改查询语句...");
}
} else {
if (!(join.getRightNode() instanceof JoinNode)) {
throw new IllegalArgumentException("join查询的join column都在右表上,请修改查询语句...");
}
}
}
}
}
DNFNode.removeAll(joinFilters);
}
join.build();
}
private static boolean isFilterContainsColumnJoin(List<IFilter> DNFNode) {
for (IFilter f : DNFNode) {
if (f instanceof IBooleanFilter) {
if (((IBooleanFilter) f).getColumn() instanceof IColumn
&& ((IBooleanFilter) f).getValue() instanceof IColumn) {
return true;
}
}
}
return false;
}
/**
* 找到join的列条件的所有列信息,必须是a.id=b.id的情况,针对a.id=1返回为null
*/
private static ISelectable[] getJoinKeysWithColumnJoin(IBooleanFilter filter) {
if (filter.getColumn() instanceof IColumn && filter.getValue() instanceof IColumn) {
if (OPERATION.EQ.equals(filter.getOperation())) {
return new ISelectable[] { (ISelectable) filter.getColumn(), (ISelectable) filter.getValue() };
}
}
return null;
}
/**
* 尝试推一下column到子节点,会设置为查找到子节点上的column<br/>
* 比如需要下推字段,可能来自于子节点的select,所以需要先转化为子节点上的select信息,再下推
*/
private static boolean tryPushColumn(IFilter filter, QueryTreeNode qtn) {
return tryPushColumn(filter, true, qtn) && tryPushColumn(filter, false, qtn);
}
private static boolean tryPushColumn(IFilter filter, boolean isColumn, QueryTreeNode qtn) {
Object value = null;
if (isColumn) {
value = ((IBooleanFilter) filter).getColumn();
} else {
value = ((IBooleanFilter) filter).getValue();
}
if (value instanceof ISelectable) {
ISelectable c = qtn.findColumn((ISelectable) value);
if (c instanceof IColumn) {
if (isColumn) {
((IBooleanFilter) filter).setColumn(c.copy());
} else {
((IBooleanFilter) filter).setValue(c.copy());
}
return true;
} else {
return false;
}
} else {
return true;
}
}
}