package jef.database.routing.sql;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jef.database.Condition.Operator;
import jef.database.jdbc.result.ResultSetContainer;
import jef.database.jdbc.statement.ResultSetLaterProcess;
import jef.database.jsqlparser.RemovedDelayProcess;
import jef.database.jsqlparser.expression.BinaryExpression;
import jef.database.jsqlparser.expression.BinaryExpression.Prior;
import jef.database.jsqlparser.expression.Column;
import jef.database.jsqlparser.expression.JpqlParameter;
import jef.database.jsqlparser.expression.Parenthesis;
import jef.database.jsqlparser.expression.Table;
import jef.database.jsqlparser.expression.operators.relational.Between;
import jef.database.jsqlparser.expression.operators.relational.EqualsTo;
import jef.database.jsqlparser.expression.operators.relational.ExpressionList;
import jef.database.jsqlparser.expression.operators.relational.InExpression;
import jef.database.jsqlparser.statement.select.Limit;
import jef.database.jsqlparser.statement.select.PlainSelect;
import jef.database.jsqlparser.statement.select.Select;
import jef.database.jsqlparser.statement.select.StartWithExpression;
import jef.database.jsqlparser.visitor.Expression;
import jef.database.jsqlparser.visitor.ExpressionType;
import jef.database.jsqlparser.visitor.SqlValue;
import jef.database.jsqlparser.visitor.Statement;
import jef.database.jsqlparser.visitor.VisitorAdapter;
import jef.database.query.ParameterProvider;
import jef.database.wrapper.clause.InMemoryPaging;
import jef.database.wrapper.clause.InMemoryStartWithConnectBy;
import jef.database.wrapper.populator.ColumnDescription;
import jef.database.wrapper.populator.ColumnMeta;
import jef.tools.PageLimit;
import jef.tools.StringUtils;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
public class SqlAndParameter implements InMemoryOperateProvider {
public Statement statement;
public List<Object> params;
private ParameterProvider rawParams;
private Map<Expression, Object> paramsMap;
// 后处理
private StartWithExpression startWith;
private Limit limit;
/**
* @param st
* SQL Statement
* @param params
* 参数
* @param rawParams
* 参数
*/
public SqlAndParameter(Statement st, List<Object> params, ParameterProvider rawParams) {
this.statement = st;
this.params = params;
this.rawParams = rawParams;
paramsMap = SqlAnalyzer.reverse(st, params); // 参数对应关系还原
}
public Map<Expression, Object> getParamsMap() {
return paramsMap;
}
private InMemoryStartWithConnectBy parseStartWith(ColumnMeta columns) {
if (statement instanceof Select) {
return parse((Select) statement, columns);
}
throw new UnsupportedOperationException();
}
private InMemoryStartWithConnectBy parse(Select st, ColumnMeta columns) {
if (st.getSelectBody() instanceof PlainSelect) {
return parse((PlainSelect) st.getSelectBody(), columns);
}
throw new UnsupportedOperationException();
}
private InMemoryStartWithConnectBy parse(PlainSelect selectBody, ColumnMeta columns) {
if (startWith == null) {
return null;
}
// 1 收集别名和表名的关系
Map<String, String> maps = new HashMap<String, String>();
selectBody.accept(new TableCollector(maps)); // 收集为大写别名 和 大写表名
// 2解析
InMemoryStartWithConnectBy result = new InMemoryStartWithConnectBy();
parseStartWith(startWith.getStartExpression(), result, maps, columns);
parseConnectBy(getAsEqualsTo(startWith.getConnectExpression()), result, maps, columns);
return result;
}
private EqualsTo getAsEqualsTo(Expression ex) {
ExpressionType type = ex.getType();
if (type == ExpressionType.parenthesis) {
return getAsEqualsTo(((Parenthesis) ex).getExpression());
} else if (type == ExpressionType.eq) {
return (EqualsTo) ex;
}
throw new UnsupportedOperationException(ex.toString());
}
private void parseStartWith(Expression startExpression, InMemoryStartWithConnectBy result, Map<String, String> maps, ColumnMeta columns) {
int leftColumn;
Operator op;
Object value;
switch (startExpression.getType()) {
case eq:
op = Operator.EQUALS;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case ge:
op = Operator.GREAT_EQUALS;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case gt:
op = Operator.GREAT;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case in:
op = Operator.IN;
InExpression in = (InExpression) startExpression;
leftColumn = getColumnId(in.getLeftExpression(), columns, maps);
if (in.getItemsList() instanceof ExpressionList) {
List<Object> values = new ArrayList<Object>();
for (Expression ex : ((ExpressionList) in.getItemsList()).getExpressions()) {
Object v = getAsValue(ex);
if (v instanceof Object[]) {
values.addAll(Arrays.asList((Object[]) v));
} else {
values.add(v);
}
}
value = values;
} else {
throw new UnsupportedOperationException(in.getItemsList().toString());
}
break;
case lt:
op = Operator.LESS;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case le:
op = Operator.LESS_EQUALS;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case like:
op = Operator.MATCH_ANY;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case ne:
op = Operator.NOT_EQUALS;
leftColumn = getColumnId(((BinaryExpression) startExpression).getLeftExpression(), columns, maps);
value = getAsValue(((BinaryExpression) startExpression).getRightExpression());
break;
case between:
op = Operator.BETWEEN_L_L;
Between be = (Between) startExpression;
leftColumn = getColumnId(be.getLeftExpression(), columns, maps);
value = Arrays.asList(getAsValue(be.getBetweenExpressionStart()), getAsValue(be.getBetweenExpressionEnd()));
break;
default:
throw new UnsupportedOperationException();
}
result.startWithColumn = leftColumn;
result.startWithOperator = op;
result.startWithValue = value;
}
private int getColumnId(Expression leftExpression, ColumnMeta columns, Map<String, String> maps) {
if (leftExpression instanceof Column) {
return getColumn(columns, (Column) leftExpression, maps);
}
throw new UnsupportedOperationException(leftExpression.toString());
}
private Object getAsValue(Expression exp) {
if (exp.getType() == ExpressionType.value) {
SqlValue value = (SqlValue) exp;
return value.getValue();
} else if (exp instanceof JpqlParameter) {
JpqlParameter jp = (JpqlParameter) exp;
Object value;
if (jp.getName() == null) {
value = rawParams.getIndexedParam(jp.getIndex());
} else {
value = rawParams.getNamedParam(jp.getName());
}
return value;
} else if (exp instanceof JdbcParameter) {
throw new UnsupportedOperationException();
}
throw new UnsupportedOperationException(exp.toString());
}
private void parseConnectBy(EqualsTo equals, InMemoryStartWithConnectBy result, Map<String, String> maps, ColumnMeta columns) {
Expression parent;
Expression current;
if (equals.getPrior() == Prior.LEFT) {
parent = equals.getRightExpression();
current = equals.getLeftExpression();
} else if (equals.getPrior() == Prior.RIGHT) {
parent = (Column) equals.getLeftExpression();
current = (Column) equals.getRightExpression();
} else {
throw new UnsupportedOperationException("NO PRIOR Found");
}
if (parent instanceof Column && current instanceof Column) {
Column c1 = (Column) current;
Column c2 = (Column) parent;
int n1 = getColumn(columns, c1, maps);
int n2 = getColumn(columns, c2, maps);
if (n1 > 0 && n2 > 0) {
result.connectPrior = n1;
result.connectParent = n2;
return;
}
throw new UnsupportedOperationException("The connect condition parser Error:" + c1 + "=" + c2);
}
throw new UnsupportedOperationException("Prior conditions must be Column");
}
private int getColumn(ColumnMeta columns, Column c1, Map<String, String> maps) {
String table1 = null;
if (StringUtils.isNotEmpty(c1.getTableAlias())) {
table1 = maps.get(StringUtils.upperCase(c1.getTableAlias()));
}
int n1 = 0;
for (ColumnDescription cd : columns.getColumns()) {
if (match(table1, c1.getColumnName(), cd)) {
n1 = cd.getN();
}
}
return n1;
}
private boolean match(String keyTable, String keyColumn, ColumnDescription cd) {
String table=cd.getTable();
if (keyTable != null && StringUtils.isNotEmpty(table)) {
if (!StringUtils.equalsIgnoreCase(keyTable, table)) {
return false;
}
}
return StringUtils.equalsIgnoreCase(keyColumn, cd.getName());
}
static class TableCollector extends VisitorAdapter {
Map<String, String> tableAlias;
TableCollector(Map<String, String> map) {
tableAlias = map;
}
@Override
public void visit(Table table) {
if (StringUtils.isNotEmpty(table.getAlias())) {
tableAlias.put(StringUtils.upperCase(table.getAlias()), StringUtils.upperCase(table.getName()));
}
}
}
private int[] getLimitLength(Limit limit) {
int offset = 0;
int rowcount = 0;
if (limit.getOffsetJdbcParameter() != null) {
Object obj=getParamsMap().get(limit.getOffsetJdbcParameter());
if(obj instanceof Number){
offset = ((Number) obj).intValue();
}
} else {
offset = (int)limit.getOffset();
}
if (limit.getRowCountJdbcParameter() != null) {
Object obj=getParamsMap().get(limit.getRowCountJdbcParameter());
if(obj instanceof Number){
rowcount = ((Number) obj).intValue();
}
} else {
rowcount = (int)limit.getRowCount();
}
return new int[]{offset,rowcount};
}
public InMemoryPaging parseLimit(Limit limit,ColumnMeta columns) {
int[] value=getLimitLength(limit);
int offset=value[0];
int rowcount=value[1];
if(offset>0 || rowcount>0){
return new InMemoryPaging(offset, rowcount);
}else{
return null;
}
}
public long getLimitSpan() {
if(limit!=null){
int[] values=getLimitLength(limit);
return values[1];
}
return 0;
}
@Override
public boolean hasInMemoryOperate() {
return startWith!=null || limit!=null;
}
@Override
public void parepareInMemoryProcess(PageLimit range, ResultSetContainer mrs) {
if(startWith!=null){
mrs.setInMemoryConnectBy(parseStartWith(mrs.getColumns()));
}
if(limit!=null){
mrs.setInMemoryPage(parseLimit(limit,mrs.getColumns()));
}
}
/**
* 设置可能需要内存计算的任务
*
* @param delays
*/
public void setInMemoryClause(RemovedDelayProcess delays) {
if(delays!=null){
this.startWith = delays.startWith;
this.limit=delays.limit;
}
}
public Limit getLimit() {
return limit;
}
public void setNewLimit(PageLimit range) {
if(range==null){
limit=null;
}else{
Limit limit=new Limit();
limit.setOffset(range.getStart());
limit.setRowCount(range.getLimit());
this.limit=limit;
}
}
public void setLimit(Limit countLimit) {
this.limit=countLimit;
}
private ResultSetLaterProcess reverseResultSet;
@Override
public ResultSetLaterProcess getRsLaterProcessor() {
return reverseResultSet;
}
public void setReverseResultSet(ResultSetLaterProcess reverseResultSet) {
this.reverseResultSet = reverseResultSet;
}
}