package com.taobao.tddl.optimizer.costbased;
import java.util.List;
import com.taobao.tddl.optimizer.core.ASTNodeFactory;
import com.taobao.tddl.optimizer.core.ast.ASTNode;
import com.taobao.tddl.optimizer.core.ast.QueryTreeNode;
import com.taobao.tddl.optimizer.core.expression.IBooleanFilter;
import com.taobao.tddl.optimizer.core.expression.IFilter;
import com.taobao.tddl.optimizer.core.expression.IFilter.OPERATION;
import com.taobao.tddl.optimizer.core.expression.ILogicalFilter;
import com.taobao.tddl.optimizer.core.expression.ISelectable;
import com.taobao.tddl.optimizer.exceptions.OptimizerException;
import com.taobao.tddl.optimizer.exceptions.QueryException;
import com.taobao.tddl.optimizer.utils.FilterUtils;
/**
* 预先处理子查询
*
* <pre>
* 1. 尝试将子查询调整为join
*
* 比如 select * from table1 where table1.id = (select id from table2)
* 转化为 selct table1.* from table1 join table2 on (table1.id = table2.id)
*
*
* 链接:http://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html
* </pre>
*
* @author jianghang 2013-12-19 下午6:44:55
* @since 5.0.0
*/
public class SubQueryPreProcessor {
public static QueryTreeNode optimize(QueryTreeNode qtn) throws QueryException {
qtn = findComparisonsSubQueryToJoinNode(qtn);
return qtn;
}
/**
* 处理下 = in的子查询转化为join
* http://dev.mysql.com/doc/refman/5.0/en/comparisons-using-subqueries.html
*/
private static QueryTreeNode findComparisonsSubQueryToJoinNode(QueryTreeNode qtn) {
for (int i = 0; i < qtn.getChildren().size(); i++) {
ASTNode ast = qtn.getChildren().get(i);
if (!(ast instanceof QueryTreeNode)) {
continue;
}
qtn.getChildren().set(i, findComparisonsSubQueryToJoinNode((QueryTreeNode) ast));
}
SubQueryAndFilter find = new SubQueryAndFilter();
find.query = qtn;
find.filter = null;
SubQueryAndFilter result = buildSubQuery(find,
qtn.getWhereFilter(),
!FilterUtils.isCNFNode(qtn.getWhereFilter()));
if (result != find) {
// 如果出现filter,代表where条件中没有组合条件,只有单自查询的条件,直接替换即可
result.query.query(result.filter);
qtn.query("");
return result.query;
} else {
return qtn; // 没变化
}
}
private static class SubQueryAndFilter {
QueryTreeNode query; // 自查询可能会改变query节点为join node
IFilter filter; // 子查询带上来的filter
}
private static SubQueryAndFilter buildSubQuery(SubQueryAndFilter qtn, IFilter filter, boolean existOr) {
if (filter instanceof IBooleanFilter) {
Object column = ((IBooleanFilter) filter).getColumn();
Object value = ((IBooleanFilter) filter).getValue();
if (filter.getOperation() == OPERATION.IN) {
value = ((IBooleanFilter) filter).getValues().get(0);
}
boolean columnIsSubQuery = (column != null && column instanceof QueryTreeNode);
boolean valueIsSubQuery = (value != null && value instanceof QueryTreeNode);
if (columnIsSubQuery && valueIsSubQuery) {
throw new OptimizerException("条件左右两边都是子查询,暂时不支持...");
} else if (columnIsSubQuery || valueIsSubQuery) {
if (existOr) {
throw new OptimizerException("出现子查询条件时,不支持和另一个条件的OR关系");
}
QueryTreeNode query = (QueryTreeNode) (columnIsSubQuery ? column : value);
Object c = columnIsSubQuery ? value : column;
if (!(c instanceof ISelectable)) {
throw new OptimizerException("不支持常量和子查询组合的条件");
}
if (!query.getOrderBys().isEmpty() || query.isExistAggregate()) {
throw new OptimizerException("子查询存在聚合查询条件太复杂,无法转化为semi-join");
}
ISelectable leftColumn = (ISelectable) c;
// 出现子查询
if (filter.getOperation() == OPERATION.EQ || filter.getOperation() == OPERATION.IN) {
if (query.getColumnsSelected().size() > 1) {
throw new OptimizerException("条件子查询只能返回一个字段");
}
SubQueryAndFilter result = new SubQueryAndFilter();
ISelectable rightColumn = query.getColumnsSelected().get(0);
List<ISelectable> newSelects = qtn.query.getColumnsSelected();
result.query = qtn.query.join(query).addJoinKeys(leftColumn, rightColumn);
result.query.select(newSelects);
if (filter.getOperation() == OPERATION.IN && filter.isNot()) {
// not in的语法
// http://dev.mysql.com/doc/refman/5.6/en/rewriting-subqueries.html
IBooleanFilter f = ASTNodeFactory.getInstance().createBooleanFilter();
f.setOperation(OPERATION.IS_NULL);
f.setColumn(rightColumn);
result.filter = and(f, query.getWhereFilter());
query.query("");// 清空底下的查询条件
} else {
IBooleanFilter f = ASTNodeFactory.getInstance().createBooleanFilter();
f.setOperation(OPERATION.CONSTANT);
f.setColumn("1");
result.filter = and(f, query.getWhereFilter());
query.query("");// 清空底下的查询条件
}
return result;
} else {
throw new OptimizerException("条件子查询,暂时不支持操作符:" + filter.getOperation().getOPERATIONString());
}
} else {
qtn.filter = filter;
return qtn; // 没有子查询
}
} else if (filter instanceof ILogicalFilter) {
ILogicalFilter logical = (ILogicalFilter) filter;
for (int i = 0; i < logical.getSubFilter().size(); i++) {
SubQueryAndFilter result = buildSubQuery(qtn, logical.getSubFilter().get(i), existOr);
if (result != qtn) {
logical.getSubFilter().set(i, result.filter);
qtn = result;
}
}
qtn.filter = logical;
}
return qtn;
}
private static IFilter and(IFilter filter, IFilter subQueryFilter) {
if (subQueryFilter == null) {
return filter;
}
ILogicalFilter and = ASTNodeFactory.getInstance().createLogicalFilter().setOperation(OPERATION.AND);
and.addSubFilter(filter);
and.addSubFilter(subQueryFilter);
return and;
}
}