package com.taobao.tddl.executor.function;
import java.util.Arrays;
import java.util.List;
import com.taobao.tddl.common.exception.TddlRuntimeException;
import com.taobao.tddl.executor.rowset.IRowSet;
import com.taobao.tddl.executor.utils.ExecUtils;
import com.taobao.tddl.optimizer.core.expression.IColumn;
import com.taobao.tddl.optimizer.core.expression.IExtraFunction;
import com.taobao.tddl.optimizer.core.expression.IFunction;
import com.taobao.tddl.optimizer.core.expression.IFunction.FunctionType;
import com.taobao.tddl.optimizer.core.expression.ISelectable;
import com.taobao.tddl.optimizer.exceptions.FunctionException;
public abstract class ExtraFunction implements IExtraFunction {
protected IFunction function;
protected Object result;
@Override
public void setFunction(IFunction function) {
this.function = function;
}
/**
* 如果可以用db的函数,那就直接使用
*
* @param function
*/
protected abstract String getDbFunction();
protected List getReduceArgs(IFunction func) {
String resArgs = getDbFunction();
Object[] obs = resArgs.split(",");
return Arrays.asList(obs);
}
protected List getMapArgs(IFunction func) {
return func.getArgs();
}
/**
* 用于标记数据应该在server端进行计算。
*
* <pre>
* 因为server有分发的过程,所以这里也模拟了分发过程。
* 比如:
* 1. sum会先map到所有相关的机器上
* 2. reduce方法内做合并
*
* 比较特殊的是avg
* 1. 首先它在map的时候,需要下面的节点统计count + sum.
* 2. reduce则是进行avg计算的地方,进行count/sum处理
*
* 因为有map/reduce模型,所有带有函数计算的执行计划都被设定为: merge { query } 结构,也就是merge下面挂query的模型,
*
* </pre>
*
* @param args
* @throws FunctionException
*/
public abstract void serverMap(Object[] args) throws FunctionException;
/**
* 用于标记数据应该在server端进行计算。
*
* <pre>
* 因为server有分发的过程,所以这里也模拟了分发过程。
* 比如:
* 1. sum会先map到所有相关的机器上
* 2. reduce方法内做合并
*
* 比较特殊的是avg
* 1. 首先它在map的时候,需要下面的节点统计count + sum.
* 2. reduce则是进行avg计算的地方,进行count/sum处理
*
* 因为有map/reduce模型,所有带有函数计算的执行计划都被设定为: merge { query } 结构,也就是merge下面挂query的模型,
*
* </pre>
*
* @param args
* @throws FunctionException
*/
public abstract void serverReduce(Object[] args) throws FunctionException;
/**
* 外部执行器传递ResultSet中的row记录,进行function的map计算
*
* @param kvPair
* @throws Exception
*/
public void serverMap(IRowSet kvPair) throws TddlRuntimeException {
// 当前function需要的args 有些可能是函数,也有些是其他的一些数据
List<Object> argsArr = getMapArgs(function);
// 函数的input参数
Object[] inputArg = new Object[argsArr.size()];
int index = 0;
for (Object funcArg : argsArr) {
if (funcArg instanceof IFunction) {
if (((IFunction) funcArg).getExtraFunction().getFunctionType().equals(FunctionType.Aggregate)) {
// aggregate function
inputArg[index] = ((AggregateFunction) ((IFunction) funcArg).getExtraFunction()).getResult();
index++;
} else {
// scalar function
((ScalarFunction) ((IFunction) funcArg).getExtraFunction()).serverMap(kvPair);
inputArg[index] = ((ScalarFunction) ((IFunction) funcArg).getExtraFunction()).getResult();
index++;
}
} else if (funcArg instanceof ISelectable) {// 如果是IColumn,那么应该从输入的参数中获取对应column
if (IColumn.STAR.equals(((ISelectable) funcArg).getColumnName())) {
inputArg[index] = kvPair;
} else {
inputArg[index] = ExecUtils.getValueByIColumn(kvPair, ((ISelectable) funcArg));
}
index++;
} else {
inputArg[index] = funcArg;
index++;
}
}
serverMap(inputArg);
}
/**
* 外部执行器传递ResultSet中的row记录,进行function的reduce计算
*
* @param kvPair
* @throws Exception
*/
public void serverReduce(IRowSet kvPair) throws TddlRuntimeException {
if (this.getFunctionType().equals(FunctionType.Scalar)) {
if (this.getClass().equals(Dummy.class)) {
Integer index = kvPair.getParentCursorMeta().getIndex(null, this.function.getColumnName());
if (index != null) {
Object v = kvPair.getObject(index);
((ScalarFunction) this).setResult(v);
} else {
throw new TddlRuntimeException(this.function.getFunctionName() + " 没有实现,结果集中也没有");
}
} else {
// 目前存在一问题,如果是row+1的字段,tddl还会再计算一次
try {
this.serverMap(kvPair);
} catch (RuntimeException e) {
Integer index = kvPair.getParentCursorMeta().getIndex(null, this.function.getColumnName());
if (index != null) {
Object v = kvPair.getObject(index);
((ScalarFunction) this).setResult(v);
} else {
throw e;
}
}
}
} else {
// 函数的input参数
List<Object> reduceArgs = this.getReduceArgs(function);
Object[] inputArg = new Object[reduceArgs.size()];
// 目前认为所有scalar函数可下推
for (int i = 0; i < reduceArgs.size(); i++) {
String name = reduceArgs.get(i).toString();
Object val = ExecUtils.getValueByTableAndName(kvPair, this.function.getTableName(), name);
inputArg[i] = val;
}
serverReduce(inputArg);
}
}
public void setResult(Object result) {
this.result = result;
}
@Override
public Object getResult() {
return result;
}
@Override
public void clear() {
result = null;
}
}