package com.taobao.tddl.executor.handler; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import org.apache.commons.lang.StringUtils; import com.taobao.tddl.common.exception.TddlException; import com.taobao.tddl.common.utils.TStringUtil; import com.taobao.tddl.executor.common.ExecutionContext; import com.taobao.tddl.executor.common.ExecutorContext; import com.taobao.tddl.executor.cursor.ISchematicCursor; import com.taobao.tddl.executor.cursor.impl.DistinctCursor; import com.taobao.tddl.executor.spi.IRepository; import com.taobao.tddl.executor.utils.ExecUtils; import com.taobao.tddl.monitor.Monitor; import com.taobao.tddl.optimizer.core.ASTNodeFactory; import com.taobao.tddl.optimizer.core.expression.IColumn; import com.taobao.tddl.optimizer.core.expression.IFilter; import com.taobao.tddl.optimizer.core.expression.IFunction; import com.taobao.tddl.optimizer.core.expression.IFunction.FunctionType; import com.taobao.tddl.optimizer.core.expression.IOrderBy; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.optimizer.core.plan.IDataNodeExecutor; import com.taobao.tddl.optimizer.core.plan.IQueryTree; import com.taobao.tddl.optimizer.core.plan.query.IJoin; import com.taobao.tddl.optimizer.core.plan.query.IMerge; import com.taobao.tddl.optimizer.core.plan.query.IQuery; import com.taobao.tddl.common.utils.logger.Logger; import com.taobao.tddl.common.utils.logger.LoggerFactory; /** * @author mengshi.sunmengshi 2013-12-5 上午11:06:01 * @since 5.0.0 */ public abstract class QueryHandlerCommon extends HandlerCommon { public QueryHandlerCommon(){ super(); } /** * 完全匹配 */ public static int MATCH = 0; /** * 前缀匹配 */ public static int PREFIX_MATCH = 1; /** * 不匹配 */ public static int NOT_MATCH = -1; public static Logger logger = LoggerFactory.getLogger(QueryHandlerCommon.class); @Override public ISchematicCursor handle(IDataNodeExecutor executor, ExecutionContext executionContext) throws TddlException { long time = System.currentTimeMillis(); // 先做查询 ISchematicCursor cursor = doQuery(null, executor, executionContext); if (executor.getSql() == null) { IQueryTree IQueryTree = (IQueryTree) executor; cursor = processValueFilter(cursor, executionContext, IQueryTree); cursor = processGroupByAndOrderBy(cursor, executionContext, IQueryTree); cursor = processLimitFromTo(cursor, executionContext, IQueryTree); cursor = processColumnAndAlias(cursor, executionContext, IQueryTree); } time = Monitor.monitorAndRenewTime(Monitor.KEY1, Monitor.ServerQuery, Monitor.Key3Success, time); return cursor; } /** * <pre> * 首先获取四个关键的属性 * 1. groupby * 2. agg columns 算法 * 3. distinct? * 4. order by * 5. merge * </pre> */ protected ISchematicCursor processGroupByAndOrderBy(ISchematicCursor cursor, ExecutionContext executionContext, IQueryTree IQueryTree) throws TddlException { // 处理 group by 和aggregate function. cursor = cursor = processGroupByAndAggregateFunction(cursor, IQueryTree, executionContext); cursor = processDistinct(cursor, IQueryTree, executionContext); cursor = processHavingFilter(cursor, executionContext, IQueryTree); // 接着处理排序 cursor = processOrderBy(cursor, IQueryTree.getOrderBys(), executionContext, IQueryTree, true); return cursor; } private ISchematicCursor processDistinct(ISchematicCursor cursor, IQueryTree IQueryTree, ExecutionContext executionContext) throws TddlException { if (isDistinct(IQueryTree)) { cursor = processOrderBy(cursor, getOrderBy(IQueryTree.getColumns()), executionContext, IQueryTree, false); cursor = new DistinctCursor(cursor, getOrderBy(IQueryTree.getColumns())); } return cursor; } boolean isDistinct(IQueryTree qc) { for (Object c : qc.getColumns()) { if (c instanceof ISelectable && ((ISelectable<IColumn>) c).isDistinct()) { return true; } } return false; } /** * 处理 group by 和aggregate function. * * <pre> * cursor = processGroupByAndAggregateFunction(context, cursor, IQueryTree, executionContext); * // 接着处理排序 * cursor = processOrderBy(cursor, IQueryTree.getOrderBy(), executionContext, IQueryTree); * return cursor; * </pre> */ protected abstract ISchematicCursor doQuery(ISchematicCursor cursor, IDataNodeExecutor executor, ExecutionContext executionContext) throws TddlException; /** * 左数据集和右数据集,排序是否相同。 * * @param o1 * @param o2 * @return */ protected boolean equalsIOrderBy(IOrderBy o1, IOrderBy o2) { IColumn c1 = ExecUtils.getColumn(o1.getColumn()); IColumn c2 = ExecUtils.getColumn(o2.getColumn()); return StringUtils.equalsIgnoreCase(c1.getTableName(), c2.getTableName()) && c1.getColumnName().equals(c2.getColumnName()) && o1.getDirection() == o2.getDirection(); } protected List<IFunction> getMergeAggregates(List retColumns) { return getAggregatesCommon(retColumns, true); } /** * 从select [columns] 里面获取aggregate functions * * @param retColumns * @return */ protected List<IFunction> getAggregatesCommon(List retColumns, boolean isMergeAggregates) { List<IFunction> aggregates = new ArrayList<IFunction>(); for (int i = 0; i < retColumns.size(); i++) { Object o = retColumns.get(i); if (o instanceof IFunction) { // 如果retColumn中出现了函数名字,那么进入这个逻辑 IFunction f = (IFunction) o; if (FunctionType.Aggregate.equals(f.getFunctionType())) { aggregates.add(f); } else { List<IFunction> aggregateInThisScalar = new ArrayList(); findAggregateFunctionsInScalar(f, aggregateInThisScalar); if (!aggregateInThisScalar.isEmpty()) { aggregates.add(f); } } } } return aggregates; } private void findAggregateFunctionsInScalar(IFunction s, List<IFunction> res) { if (IFunction.FunctionType.Aggregate.equals(s.getFunctionType())) { res.add(s); } for (Object arg : s.getArgs()) { if (arg instanceof IFunction) { this.findAggregateFunctionsInScalar((IFunction) arg, res); } } } /** * <pre> * group by和aggregate Function。 * 对单机来说,原则就是尽可能的使用索引完成count max min的功能。 * 参考的关键条件有: * 1. 是否需要group by * 2. 是什么aggregate. * 3. 是否需要distinct * 4. 是否是merge节点 * </pre> */ protected ISchematicCursor processGroupByAndAggregateFunction(ISchematicCursor cursor, IQueryTree IQueryTree, ExecutionContext executionContext) throws TddlException { // 是否带有group by 列。。 List<IOrderBy> groupBycols = IQueryTree.getGroupBys(); boolean closeResultCursor = executionContext.isCloseResultSet(); final IRepository repo = executionContext.getCurrentRepository(); List retColumns = getEmptyListIfRetColumnIsNull(IQueryTree); List<IFunction> _agg = getAggregates(retColumns); // 接着处理group by if (groupBycols != null && !groupBycols.isEmpty()) { // group by之前需要进行排序,按照group by列排序 cursor = processOrderBy(cursor, (groupBycols), executionContext, IQueryTree, false); } cursor = executeAgg(cursor, IQueryTree, closeResultCursor, repo, _agg, groupBycols, executionContext); return cursor; } protected ISchematicCursor executeAgg(ISchematicCursor cursor, IDataNodeExecutor executor, boolean closeResultCursor, IRepository repo, List<IFunction> aggregates, List<IOrderBy> groupBycols, ExecutionContext executionContext) throws TddlException { List<ISelectable> _retColumns = null; if (executor instanceof IQuery) { _retColumns = ((IQuery) executor).getColumns(); } else if (executor instanceof IJoin) { _retColumns = ((IJoin) executor).getColumns(); } else if (executor instanceof IMerge) { _retColumns = ((IMerge) executor).getColumns(); } if (_retColumns != null) { if ((aggregates != null && !aggregates.isEmpty()) || (groupBycols != null && !groupBycols.isEmpty())) { cursor = repo.getCursorFactory().aggregateCursor(executionContext, cursor, aggregates, groupBycols, _retColumns, false); } } return cursor; } protected List getEmptyListIfRetColumnIsNull(IQueryTree IQueryTree) { return IQueryTree.getColumns() == null ? Collections.EMPTY_LIST : IQueryTree.getColumns(); } protected List<IFunction> getAggregates(List retColumns) { return getAggregatesCommon(retColumns, false); } /** * 根据列名,生成order by 条件.永远是正向。 * * @param columns * @return */ protected static final List<IOrderBy> getOrderBy(List<ISelectable> columns) { if (columns == null) { columns = Collections.EMPTY_LIST; } List<IOrderBy> orderBys = new ArrayList<IOrderBy>(columns.size()); for (Object cobj : columns) { IColumn c = ExecUtils.getColumn(cobj); orderBys.add(ASTNodeFactory.getInstance().createOrderBy().setColumn(c).setDirection(true)); } return orderBys; } protected ISchematicCursor processOrderBy(ISchematicCursor cursor, List<IOrderBy> ordersInRequest, ExecutionContext executionContext, IQueryTree IQueryTree, boolean needOrderMatch) throws TddlException { IRepository repo = executionContext.getCurrentRepository(); boolean hasOrderBy = ordersInRequest != null && !ordersInRequest.isEmpty(); if (!hasOrderBy) { return cursor; } OrderByResult orderByResult = chooseOrderByMethod(cursor, ordersInRequest, executionContext, needOrderMatch); switch (orderByResult) { case temporaryTable: { return repo.getCursorFactory().tempTableSortCursor(executionContext, cursor, ordersInRequest, true, IQueryTree.getRequestID()); } case reverseCursor: return repo.getCursorFactory().reverseOrderCursor(executionContext, cursor); case normal: if (requestCMTabisCursorCMTab(ordersInRequest, cursor.getOrderBy())) { return cursor; } else { return repo.getCursorFactory().setOrderCursor(executionContext, cursor, ordersInRequest); } default: throw new IllegalArgumentException("should not be here"); } } private boolean requestCMTabisCursorCMTab(List<IOrderBy> ordersInRequest, List<IOrderBy> ordersInCursor) { for (int i = 0; i < ordersInRequest.size(); i++) { IOrderBy orderInCursor = ordersInCursor.get(i); IOrderBy orderInRequest = ordersInRequest.get(i); if (!TStringUtil.equals(orderInCursor.getColumn().getTableName(), orderInRequest.getColumn().getTableName())) { return false; } } return true; } /** * 比较两个排序,排序可不相同 * * @param o1 * @param o2 * @return */ protected static boolean isTwoOrderByMatched(IOrderBy o1, IOrderBy o2) { IColumn c1 = ExecUtils.getIColumn(o1.getColumn()); IColumn c2 = ExecUtils.getIColumn(o2.getColumn()); boolean columnMatch = c1 != null && c2 != null && (StringUtils.equals(c1.getTableName(), c2.getTableName())) && (c1.getColumnName().equals(c2.getColumnName()) || (c1.getAlias() != null && c1.getAlias() .equals(c2.getColumnName()))); return columnMatch; } /** * @param ordersInCursor * @param ordersInRequest * @param executionContext * @param needOrderMatch * 为true时,ordersInRequest和ordersInCursor必须顺序一致才会认定为不需要排序 * 为false时,只要ordersInCursor包含ordersInRequest中的列即可 * ,不必要顺序一致,亦不考虑order的direction * @return */ protected static OrderByResult chooseOrderByMethod(List<IOrderBy> ordersInCursor, List<IOrderBy> ordersInRequest, ExecutionContext executionContext, boolean needOrderMatch) { if (!needOrderMatch) { return chooseOrderByMethodNotNeedOrderMatch(ordersInCursor, ordersInRequest, executionContext); } if (ordersInRequest != null && ordersInRequest.size() <= ordersInCursor.size()) { // 察看order顺序 int requestOrderSize = ordersInRequest.size(); boolean first = true; boolean firstOrderInCursor = true; OrderByResult ret = null; for (int i = 0; i < requestOrderSize; i++) { // 在当前cursor(也就是原本数据中的order by IOrderBy orderInCursor = ordersInCursor.get(i); IOrderBy orderInRequest = ordersInRequest.get(i); Boolean bool = orderInCursor.getDirection(); if (bool == null) { return OrderByResult.temporaryTable; } if (first) { first = false; firstOrderInCursor = orderInCursor.getDirection(); } boolean columnNotMatch = !isTwoOrderByMatched(orderInCursor, orderInRequest); if (columnNotMatch) { return OrderByResult.temporaryTable; } /** * <pre> * 1.cursor中的顺序全部相同,并且request中的顺序也全部相同,才可能使用reverse * 2.cursor中的顺序不同,但与request中的顺序相同,可以使用normal * 3. 其他都是临时表 * </pre> */ if (firstOrderInCursor == orderInCursor.getDirection()) { if (orderInCursor.getDirection() != orderInRequest.getDirection()) { if (ret == null) ret = OrderByResult.reverseCursor; else if (ret != OrderByResult.reverseCursor) return OrderByResult.temporaryTable; } else { if (ret == null) ret = OrderByResult.normal; else if (ret != OrderByResult.normal) return OrderByResult.temporaryTable; } } else { if (orderInCursor.getDirection() != orderInRequest.getDirection()) { return OrderByResult.temporaryTable; } else { if (ret == null) ret = OrderByResult.normal; else if (ret != OrderByResult.normal) return OrderByResult.temporaryTable; } } } if (ret != null) { return ret; } else { return OrderByResult.temporaryTable; } } return OrderByResult.temporaryTable; } /** * 只要ordersInCursor中包含所有的ordersInRequest,不论方向顺序,则不需要排序 用于distinct * * @param ordersInCursor * @param ordersInRequest * @param executionContext * @return */ protected static OrderByResult chooseOrderByMethodNotNeedOrderMatch(List<IOrderBy> ordersInCursor, List<IOrderBy> ordersInRequest, ExecutionContext executionContext) { if (ordersInRequest != null && ordersInRequest.size() <= ordersInCursor.size()) { // 察看order顺序 int requestOrderSize = ordersInRequest.size(); OrderByResult ret = null; for (int i = 0; i < requestOrderSize; i++) { IOrderBy orderInRequest = ordersInRequest.get(i); boolean columnNotMatch = true; for (int j = 0; j < ordersInCursor.size(); j++) { IOrderBy orderInCursor = ordersInCursor.get(j); columnNotMatch = columnNotMatch & !isTwoOrderByMatched(orderInCursor, orderInRequest); if (columnNotMatch == false) { // 出现false,代表有一个匹配成功 break; } } if (columnNotMatch) { return OrderByResult.temporaryTable; } else { ret = OrderByResult.normal; } } if (ret != null) { return ret; } else { return OrderByResult.temporaryTable; } } return OrderByResult.temporaryTable; } /** * <pre> * 算order应该用什么方法来实现的方法。 具体可以看OrderByResult的解说 * 1. 如果全部列都匹配,并且asc也全部匹配,那么认为是normal. * 2. 只要有一个列不匹配,那么就是临时表 * 3. 第三中情况略微复杂,做个详细说明 * 用户的request里面有可能会出现几种情况 * 1. 用户请求排序顺序与实际数据顺序完全一致,那么应该正常返回。 * 2. 用户请求顺序与实际数据顺序完全相反,那么应该返回反转cursor * 3. 用户请求顺序与实际数据顺序出现反转后反转,返回临时表。 * (比如用户请求:order by colA(asc),B(desc),C(asc) 真正的数据顺序 A(desc),B(asc),C(desc) 。 * * <pre> * @param cursor * @param ordersInRequest * @param executionContext * @param IQueryTree * @param needOrderMatch * 为true时,ordersInRequest和ordersInCursor必须顺序一致才会认定为不需要排序 * 为false时,只要ordersInCursor包含ordersInRequest中的列即可 * ,不必要顺序一致,亦不考虑order的direction * @return */ protected static OrderByResult chooseOrderByMethod(ISchematicCursor cursor, List<IOrderBy> ordersInRequest, ExecutionContext executionContext, boolean needOrderMatch) { if (cursor.getJoinOrderBys() != null && cursor.getJoinOrderBys().size() > 1) { OrderByResult last = OrderByResult.temporaryTable; for (List<IOrderBy> ordersInCursor : cursor.getJoinOrderBys()) { if (ordersInCursor == null) { ordersInCursor = Collections.emptyList(); } OrderByResult result = chooseOrderByMethod(ordersInCursor, ordersInRequest, executionContext, needOrderMatch); // 不可能出现一个匹配reverse,另一个匹配normal的情况 if (result == OrderByResult.normal) { return result; } else if (result.ordinal() > last.ordinal()) { last = result; } } // 没有匹配的normal/reverseCurosr,直接返回临时表 return last; } else { List<IOrderBy> ordersInCursor = cursor.getOrderBy(); if (ordersInCursor == null) { ordersInCursor = Collections.emptyList(); } return chooseOrderByMethod(ordersInCursor, ordersInRequest, executionContext, needOrderMatch); } } protected static enum OrderByResult { /** * 临时表 */ temporaryTable, /** * 需要反转 */ reverseCursor, /** * 正常 */ normal; } /** * 用于处理列的选择性filter 如 A join B 总共有3列 A.a,A.b,B.a 实际上只需要两列。那么 这里就允许进行列的filter * * @param cursor * @param executionContext * @param IQueryTree * @return * @throws TddlException */ protected ISchematicCursor processColumnAndAlias(ISchematicCursor cursor, final ExecutionContext executionContext, IQueryTree IQueryTree) throws TddlException { if (IQueryTree.getColumns() == null || IQueryTree.getColumns().isEmpty()) { return cursor; } IRepository repo = executionContext.getCurrentRepository(); List<ISelectable> retColumns = IQueryTree.getColumns(); // 过滤多其它不必要的select字段 cursor = repo.getCursorFactory().columnAliasCursor(executionContext, cursor, retColumns, IQueryTree.getAlias()); return cursor; } protected ISchematicCursor processValueFilter(ISchematicCursor cursor, final ExecutionContext executionContext, IQueryTree IQueryTree) throws TddlException { // 接着处理valueFilter IFilter _valueFilter = IQueryTree.getValueFilter(); if (_valueFilter != null) { cursor = executionContext.getCurrentRepository() .getCursorFactory() .valueFilterCursor(executionContext, cursor, IQueryTree.getValueFilter()); } return cursor; } protected ISchematicCursor processHavingFilter(ISchematicCursor cursor, final ExecutionContext executionContext, IQueryTree IQueryTree) throws TddlException { // 接着处理havingFilter IFilter havingFilter = IQueryTree.getHavingFilter(); if (havingFilter != null) { cursor = executionContext.getCurrentRepository() .getCursorFactory() .valueFilterCursor(executionContext, cursor, IQueryTree.getHavingFilter()); } return cursor; } protected ISchematicCursor processLimitFromTo(ISchematicCursor cursor, final ExecutionContext executionContext, IQueryTree IQueryTree) throws TddlException { // 接着处理valueFilter if (IQueryTree.getLimitTo() == null || (Long) IQueryTree.getLimitTo() == 0l) {// 如果limitTo为空或为0,则意味着不需要包装limit // from // to这个东西到cursor上。 return cursor; } if (IQueryTree.getLimitFrom() == null) { // should not be here... logger.warn("should not be here ,limit from is null .but this val should be fill with 0 before there"); IQueryTree.setLimitFrom(0); } // limit to 不为空,limit from也不为空。所以包装个limit from To cursor // 如果limit from 也不 cursor = executionContext.getCurrentRepository() .getCursorFactory() .limitFromToCursor(executionContext, cursor, (Long) IQueryTree.getLimitFrom(), (Long) IQueryTree.getLimitTo()); return cursor; } /** * <pre> * 判断前缀索引 可判断o1是否包含o2.在组合索引里,要尽可能匹配更多的列。 在用于组合索引排序的时候 o1 是表的源组合索引。 o2 * 是where条件中需要的走索引的key filters的组合关系。 * 假如 : * 1. 原来的组合索引(o1)是A ,B -> PK 如果o2 key filter是 A ,则返回0 * 2. 如果o2 key filter是A,B 则返回0 , o2 Key Filter是C 返回-1, 无关其他属性,返回0,也就意味着不需要排序。 * * -1 一般来说会导致使用临时表。 0不会 * </pre> * * @param o2 条件,参数 * @param o1 索引本身的顺序 * @return 0 : 完全正常匹配成功,相等 1 : 前缀匹配 -1 : 不匹配 */ protected final int matchIndex(List<IOrderBy> o2, List<IOrderBy> o1) { if (o1 == null || o2 == null) { return MATCH; } if (o2.isEmpty()) { return MATCH; } if (o1.isEmpty()) { return NOT_MATCH; } if (o2.size() > o1.size()) {// o2.length比o1大,返回-1,不可用 return NOT_MATCH; } for (int i = 0; i < o2.size(); i++) { if (!isTwoOrderByMatched(o1.get(i), o2.get(i))) { return NOT_MATCH; } if (i == o2.size() - 1) { if (o1.size() > o2.size()) { return PREFIX_MATCH; } else { return MATCH; } } } return PREFIX_MATCH; } protected Future<ISchematicCursor> executeFuture(final ExecutionContext executionContext, final IDataNodeExecutor query) throws TddlException { return ExecutorContext.getContext().getTopologyExecutor().execByExecPlanNodeFuture(query, executionContext); } }