package jef.database.routing.sql; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import jef.common.Pair; import jef.common.PairSO; import jef.database.DbUtils; import jef.database.Field; import jef.database.ORMConfig; import jef.database.annotation.PartitionFunction; import jef.database.annotation.PartitionKey; import jef.database.innerpool.PartitionSupport; import jef.database.jdbc.JDBCTarget; import jef.database.jsqlparser.expression.BinaryExpression; import jef.database.jsqlparser.expression.Column; import jef.database.jsqlparser.expression.JdbcParameter; import jef.database.jsqlparser.expression.JpqlParameter; import jef.database.jsqlparser.expression.Parenthesis; import jef.database.jsqlparser.expression.operators.conditional.AndExpression; import jef.database.jsqlparser.expression.operators.conditional.OrExpression; 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.GreaterThan; import jef.database.jsqlparser.expression.operators.relational.GreaterThanEquals; import jef.database.jsqlparser.expression.operators.relational.InExpression; import jef.database.jsqlparser.expression.operators.relational.LikeExpression; import jef.database.jsqlparser.expression.operators.relational.MinorThan; import jef.database.jsqlparser.expression.operators.relational.MinorThanEquals; import jef.database.jsqlparser.expression.operators.relational.NotEqualsTo; import jef.database.jsqlparser.statement.delete.Delete; import jef.database.jsqlparser.statement.insert.Insert; import jef.database.jsqlparser.statement.select.PlainSelect; import jef.database.jsqlparser.statement.select.Select; import jef.database.jsqlparser.statement.select.SubSelect; import jef.database.jsqlparser.statement.update.Update; import jef.database.jsqlparser.visitor.Expression; import jef.database.jsqlparser.visitor.ExpressionType; import jef.database.jsqlparser.visitor.FromItem; import jef.database.jsqlparser.visitor.Notable; import jef.database.jsqlparser.visitor.SelectBody; import jef.database.jsqlparser.visitor.SqlValue; import jef.database.jsqlparser.visitor.Statement; import jef.database.jsqlparser.visitor.VisitorAdapter; import jef.database.meta.AbstractMetadata; import jef.database.meta.ITableMetadata; import jef.database.query.ComplexDimension; import jef.database.query.Dimension; import jef.database.query.RangeDimension; import jef.database.query.RegexpDimension; import jef.database.routing.PartitionResult; import jef.database.routing.jdbc.ParameterContext; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; /** * 基于SQL语句的分库分表解析。主要逻辑部分 * @author jiyi * */ public class SqlAnalyzer { /** * 获得select语句的执行计划 * @param sql AST of select * @param value 绑定变量值 * @param db 数据库Session * @return */ public static QueryablePlan getSelectExecutionPlan(Select sql,Map<Expression, Object> params, List<Object> value, JDBCTarget db) { TableMetaCollector collector = new TableMetaCollector(); sql.accept(collector); if(collector.get()==null)return new SimpleExecutionPlan(sql, value, null, db); AbstractMetadata meta=collector.get(); if(meta.getPartition() == null){ if(meta.getBindDsName()!=null && !meta.getBindDsName().equals(db.getDbkey())){ return new SimpleExecutionPlan(sql,value,meta.getBindDsName(),db); }else{ return new SimpleExecutionPlan(sql, value, null, db); } } Select select = (Select) sql; SelectBody body = select.getSelectBody(); if (body instanceof PlainSelect) { StatementContext<PlainSelect> context=new StatementContext<PlainSelect>((PlainSelect) body,meta,params,value,db,collector.getModificationPoints()); return getPlainSelectExePlan(context); } else {//已经是Union语句的暂不支持 throw new UnsupportedOperationException(); } } public static List<Object> asValue(List<ParameterContext> params) { List<Object> values=new ArrayList<Object>(params.size()); for(ParameterContext context:params){ values.add(context.getValue()); } return values; } public static TableMetaCollector getTableMeta(Statement st){ TableMetaCollector collector = new TableMetaCollector(); st.accept(collector); return collector; } /** * 按每组参数计算路由结果,并按路由结果对Batch中的参数进行分组 * @param params * @param st * @param db * @return */ public static Multimap<String, List<ParameterContext>> doGroup(AbstractMetadata meta,List<List<ParameterContext>> params,Statement st,JDBCTarget db) { Multimap<String,List<ParameterContext>> result=ArrayListMultimap.create(); for(List<ParameterContext> param:params){ List<Object> values=asValue(param); Map<Expression, Object> paramMap = reverse(st, values); // 参数对应关系还原 PartitionResult routing=getPartitionResult(st,meta,paramMap,db.getPartitionSupport()); String key=routing.getDatabase()+"-"+routing.getAsOneTable(); result.put(key, param); } return result; } /** * 获得其他操作语句(Insert,Delete,Update语句的执行计划) * @param sql AST of /Update/Delete/Insert * @param value 绑定变量值 * @param db 数据库Session * @return */ public static ExecuteablePlan getExecutionPlan(Statement sql,Map<Expression,Object> params, List<Object> value, JDBCTarget db) { TableMetaCollector collector = new TableMetaCollector(); sql.accept(collector); AbstractMetadata meta=collector.get(); if (meta == null) { return new SimpleExecutionPlan(sql,value,null,db); } if(meta.getPartition() == null){ if(meta.getBindDsName()==null || meta.getBindDsName().equals(db.getDbkey())){ return new SimpleExecutionPlan(sql,value,null,db); }else{ return new SimpleExecutionPlan(sql,value,meta.getBindDsName(),db); } } if (sql instanceof Insert) { StatementContext<Insert> context=new StatementContext<Insert>((Insert) sql,meta,params,value,db,collector.getModificationPoints()); return getInsertExePlan(context); } else if (sql instanceof Update) { StatementContext<Update> context=new StatementContext<Update>((Update) sql,meta,params,value,db,collector.getModificationPoints()); return getUpdateExePlan(context); } else if (sql instanceof Delete) { StatementContext<Delete> context=new StatementContext<Delete>((Delete) sql,meta,params,value,db,collector.getModificationPoints()); return getDeleteExePlan(context); } return new SimpleExecutionPlan(sql,value,null,db); } /* * 将顺序的参数重新变为和JpqlParameter对应的map */ static class ParamReverser extends VisitorAdapter { ParamReverser(List<Object> raw) { this.rawParams = raw.iterator(); } final Map<Expression, Object> params = new IdentityHashMap<Expression, Object>(); private Iterator<Object> rawParams; @Override public void visit(JpqlParameter parameter) { int res = parameter.resolvedCount(); if (res == 0) { params.put(parameter, rawParams.next()); } else if (res > 0) { Object[] array = new Object[res]; for (int i = 0; i < res; i++) { array[i] = rawParams.next(); } params.put(parameter, array); } } @Override public void visit(JdbcParameter jdbcParameter) { params.put(jdbcParameter, rawParams.next()); } public Map<Expression, Object> getParams() { return params; } } /* * 将已经顺序排列好的参数和解析后的AST中的参数对象一一对应。 */ public static Map<Expression, Object> reverse(Statement sql, List<Object> value) { ParamReverser p = new ParamReverser(value); sql.accept(p); return p.params; } /* * 为Delete生成执行计划 */ private static ExecuteablePlan getDeleteExePlan(StatementContext<Delete> context) { DimensionCollector collector = new DimensionCollector(context.meta, context.paramsMap); Map<String, Dimension> val = getPartitionCondition(context.statement, collector); val=fill(val,collector); PartitionResult[] results=DbUtils.partitionUtil.toTableNames(context.meta, val, context.db.getPartitionSupport(),ORMConfig.getInstance().isFilterAbsentTables()); DeleteExecutionPlan ex=new DeleteExecutionPlan(results,context); return ex; } public static PartitionResult[] getPartitionResultOfSQL(Statement sql,List<Object> values,PartitionSupport support){ TableMetaCollector collector = new TableMetaCollector(); sql.accept(collector); if(collector.get()==null){ throw new IllegalArgumentException("The SQL is a known partition table."); } AbstractMetadata meta=collector.get(); if (meta == null || meta.getPartition() == null) { return null; } Map<Expression, Object> params = reverse(sql, values); // 参数对应关系还原 Map<String, Dimension> val=null; if (sql instanceof Select) { Select select = (Select) sql; DimensionCollector dims = new DimensionCollector(meta, params); val = getPartitionCondition(select.getSelectBody(), dims); } else if(sql instanceof Insert){//已经是Union语句的暂不支持 DimensionCollector dims = new DimensionCollector(meta, params); val = getPartitionCondition((Insert)sql, dims); } else if(sql instanceof Delete){//已经是Union语句的暂不支持 DimensionCollector dims = new DimensionCollector(meta, params); val = getPartitionCondition((Delete)sql, dims); } else if(sql instanceof Update){//已经是Union语句的暂不支持 DimensionCollector dims = new DimensionCollector(meta, params); val = getPartitionCondition((Update)sql, dims); }else{ throw new UnsupportedOperationException(sql.getClass().toString()); } return DbUtils.partitionUtil.toTableNames(meta, val, support,ORMConfig.getInstance().isFilterAbsentTables()); } /* * 为Update生成执行计划 */ private static ExecuteablePlan getUpdateExePlan(StatementContext<Update> context) { DimensionCollector collector = new DimensionCollector(context.meta, context.paramsMap); Map<String, Dimension> val = getPartitionCondition(context.statement, collector); val=fill(val,collector); PartitionResult[] results=DbUtils.partitionUtil.toTableNames(context.meta, val, context.db.getPartitionSupport(),ORMConfig.getInstance().isFilterAbsentTables()); UpdateExecutionPlan ex=new UpdateExecutionPlan(results,context); return ex; } /* * 为Select生成执行计划 */ private static SelectExecutionPlan getPlainSelectExePlan(StatementContext<PlainSelect> context) { DimensionCollector collector = new DimensionCollector(context.meta, context.paramsMap); Map<String, Dimension> val = getPartitionCondition(context.statement, collector); val=fill(val,collector); PartitionResult[] results=DbUtils.partitionUtil.toTableNames(context.meta, val, context.db.getPartitionSupport(),ORMConfig.getInstance().isFilterAbsentTables()); SelectExecutionPlan ex=new SelectExecutionPlan(results,context); return ex; } private static Map<String, Dimension> fill(Map<String, Dimension> result,DimensionCollector collector) { Set<String> keys=collector.meta.getMinUnitFuncForEachPartitionKey().keySet(); if(result.size()<keys.size()){ result=new HashMap<String,Dimension>(result); for(String s: keys){ if(!result.containsKey(s)){ result.put(s, RangeDimension.EMPTY_RANGE); } } } return result; } public static PartitionResult getPartitionResult(Statement st,AbstractMetadata meta,Map<Expression, Object> paramsMap,PartitionSupport support){ DimensionCollector collector = new DimensionCollector(meta, paramsMap); Map<String, Dimension> val ; if(st instanceof Insert){ val= getPartitionCondition((Insert)st, collector); }else if(st instanceof Update){ val= getPartitionCondition((Update)st, collector); }else if(st instanceof Delete){ val= getPartitionCondition((Delete)st, collector); }else{ throw new UnsupportedOperationException(st.getClass().getSimpleName()); } val=fill(val,collector); return DbUtils.partitionUtil.toTableName(meta, val, support); } /* * 为Insert生成执行计划 */ private static ExecuteablePlan getInsertExePlan(StatementContext<Insert> context) { DimensionCollector collector = new DimensionCollector(context.meta, context.paramsMap); Map<String, Dimension> val = getPartitionCondition(context.statement, collector); val=fill(val,collector); PartitionResult results=DbUtils.partitionUtil.toTableName(context.meta, val, context.db.getPartitionSupport()); InsertExecutionPlan ex=new InsertExecutionPlan(new PartitionResult[]{results},context); return ex; } /* * 收集路由维度,从Insert语句 */ private static Map<String, Dimension> getPartitionCondition(Insert statement, DimensionCollector collector) { List<Column> cols=statement.getColumns(); if(cols==null){ throw new UnsupportedOperationException("the SQL must assign column names."); } if(statement.getItemsList() instanceof SubSelect){ throw new UnsupportedOperationException("Can not support a subselect"); } ExpressionList exp=(ExpressionList)statement.getItemsList(); Map<String,Dimension> result=new HashMap<String,Dimension>(); for(int i=0;i<exp.size();i++){ Column c=cols.get(i); String field=collector.getPartitionField(c); if(field==null)continue; Object obj=collector.getAsValue(exp.get(i)); if(obj==ObjectUtils.NULL){ continue; } result.put(field, (Dimension)RangeDimension.create(obj, obj)); } return result; } /* * 收集路由维度(从Delete语句) */ private static Map<String, Dimension> getPartitionCondition(Delete statement, DimensionCollector collector) { if (statement.getWhere() != null) { return collector.parse(statement.getWhere()); }else{ return Collections.emptyMap(); } } /* * 收集路由维度 (从Update语句) * @param statement * @param collector * @return */ private static Map<String, Dimension> getPartitionCondition(Update sql, DimensionCollector collector) { Map<String,Dimension> result; if (sql.getWhere() != null) { result=collector.parse(sql.getWhere()); }else{ result=Collections.emptyMap(); } for(Pair<Column,Expression> set:sql.getSets()){ String field=collector.getPartitionField(set.first); if(field==null)continue; if(result.get(field)!=null){ continue; } Object value=collector.getAsValue(set.second); if(ObjectUtils.NULL!=value){ result.put(field, RangeDimension.create(value, value)); } } return result; } /* * 递归实现——收集路由维度 */ private static Map<String, Dimension> getPartitionCondition(PlainSelect sql,DimensionCollector context) { Map<String,Dimension> result; if (sql.getWhere() != null) { result=context.parse(sql.getWhere()); }else{ result=Collections.emptyMap(); } FromItem from = sql.getFromItem(); if(from instanceof SubSelect){ Map<String,Dimension> cond=getPartitionCondition(((SubSelect) from).getSelectBody(),context); result=mergeAnd(result, cond); } return result; } /* * 递归实现——收集路由维度 */ private static Map<String, Dimension> getPartitionCondition(SelectBody selectBody, DimensionCollector context) { if (selectBody instanceof PlainSelect) { return getPartitionCondition((PlainSelect) selectBody, context); } else { // Union 暂不支持 throw new UnsupportedOperationException(); } } @SuppressWarnings("rawtypes") static class DimensionCollector { private Map<Expression, Object> params; private final Map<String, String> columnToPartitionKey = new HashMap<String, String>(); private ITableMetadata meta; DimensionCollector(ITableMetadata meta, Map<Expression, Object> params) { this.params = params; this.meta=meta; for (Map.Entry<PartitionKey, PartitionFunction> key : meta.getEffectPartitionKeys()) { String field = key.getKey().field(); Field fld = meta.getField(field); if (fld == null) { throw new IllegalArgumentException("The partition field [" + field + "] is not a database column."); } String columnName = meta.getColumnDef(fld).upperColumnName(); columnToPartitionKey.put(columnName, key.getKey().field()); } } public Map<String, Dimension> parse(Expression exp) { PairSO<Dimension> dim = null; switch (exp.getType()) { case and: { AndExpression and = (AndExpression) exp; Map<String, Dimension> left = parse(and.getLeftExpression()); Map<String, Dimension> right = parse(and.getRightExpression()); return mergeAnd(left, right); } case or: { OrExpression or = (OrExpression) exp; Map<String, Dimension> left = parse(or.getLeftExpression()); Map<String, Dimension> right = parse(or.getRightExpression()); return mergeOr(left, right); } case parenthesis: Parenthesis p = (Parenthesis) exp; Map<String, Dimension> in = parse(p.getExpression()); if (p.isNot()) { return mergeNot(in); } return in; // ////////////////////多维度运算结束///////////// case between: dim = process((Between) exp); break; case eq: dim = process((EqualsTo) exp); break; case ge: dim = process((GreaterThanEquals) exp); break; case gt: dim = process((GreaterThan) exp); break; case in: dim = process((InExpression) exp); break; case lt: dim = process((MinorThan) exp); break; case le: dim = process((MinorThanEquals) exp); break; case like: dim = process((LikeExpression) exp); break; case ne: dim = process((NotEqualsTo) exp); break; // /////////////////单维度运算结束//////////////// // 不处理的类型 case isnull: case complex: case arithmetic: case param: case value: default: break; } // 处理Not的场景 if (dim != null && (exp instanceof Notable)) { boolean not = ((Notable) exp).isNot(); if (not) { dim.second = dim.second.mergeNot(); } return Collections.singletonMap(dim.first, dim.second); } return Collections.emptyMap(); } private PairSO<Dimension> process(InExpression exp) { if (exp.getLeftExpression().getType() != ExpressionType.column) { return null; } String field = this.getPartitionField((Column) exp.getLeftExpression()); if (field == null) return null; List<Object> values = new ArrayList<Object>(); if (exp.getItemsList() instanceof ExpressionList) { for (Expression ex : ((ExpressionList) exp.getItemsList()).getExpressions()) { Object v = getAsValue(ex); if (v == ObjectUtils.NULL) return null;// in条件中有任意一个无法解析的表达式,则整个维度条件无效。 if(v instanceof Object[]){ values.addAll(Arrays.asList((Object[])v)); }else{ values.add(v); } } } Dimension d = ComplexDimension.create((Comparable[]) values.toArray(new Comparable[values.size()])); return new PairSO<Dimension>(field, d); } private PairSO<Dimension> process(EqualsTo exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { return v.<Dimension>replaceSecond(RangeDimension.create(v.second, v.second)); } return null; } private PairSO<Dimension> process(NotEqualsTo exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { Dimension d = RangeDimension.create(v.second, v.second); return v.replaceSecond(d.mergeNot()); } return null; } private PairSO<Dimension> process(LikeExpression exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { String like = String.valueOf(v.second); if (like.endsWith("%") && !like.startsWith("%")) { String base = StringUtils.substringBefore(like, "%"); return v.<Dimension> replaceSecond(new RegexpDimension(base)); } } return null; } private PairSO<Dimension> process(MinorThanEquals exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { return v.<Dimension> replaceSecond(RangeDimension.createCL(null, v.second)); } return null; } private PairSO<Dimension> process(MinorThan exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { return v.<Dimension> replaceSecond(RangeDimension.createCC(null, v.second)); } return null; } private PairSO<Dimension> process(GreaterThan exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { return v.<Dimension> replaceSecond(RangeDimension.createCC(v.second, null)); } return null; } private PairSO<Dimension> process(GreaterThanEquals exp) { PairSO<Object> v = getFromBinaryOperate(exp); if (v != null) { return v.<Dimension> replaceSecond(RangeDimension.createLC(v.second, null)); } return null; } private PairSO<Object> getFromBinaryOperate(BinaryExpression exp) { Column column = null; Expression valueExp = null; if (exp.getLeftExpression().getType() == ExpressionType.column) { column = (Column) exp.getLeftExpression(); valueExp = exp.getRightExpression(); } if (exp.getRightExpression().getType() == ExpressionType.column) { column = (Column) exp.getRightExpression(); valueExp = exp.getLeftExpression(); } String field = getPartitionField(column); if (field != null) { Object obj = getAsValue(valueExp); if (ObjectUtils.NULL != obj) { return new PairSO<Object>(field, obj); } } return null; } private PairSO<Dimension> process(Between exp) { if (exp.getLeftExpression().getType() == ExpressionType.column) { String field = getPartitionField((Column) exp.getLeftExpression()); if (field == null) return null; Expression start = exp.getBetweenExpressionStart(); Expression end = exp.getBetweenExpressionEnd(); Object min = getAsValue(start); Object max = getAsValue(end); // 无效 if (min == ObjectUtils.NULL && max == ObjectUtils.NULL) { return null; } if (min == ObjectUtils.NULL) min = null; if (max == ObjectUtils.NULL) max = null; return new PairSO<Dimension>(field, RangeDimension.create(min, max)); } return null; } private String getPartitionField(Column column) { if (column == null) return null; String key = columnToPartitionKey.get(StringUtils.upperCase(column.getColumnName())); if (key == null) return null; return key; } /** * 返回ObjectUtils.null表示是无效条件 * * @param exp * @return */ private Object getAsValue(Expression exp) { if (exp.getType() == ExpressionType.value) { SqlValue value = (SqlValue) exp; return value.getValue(); } else if (exp.getType() == ExpressionType.param) { Object value = params.get(exp); return value; } return ObjectUtils.NULL; } } // 对所有维度取非 private static Map<String, Dimension> mergeNot(Map<String, Dimension> in) { Map<String, Dimension> result = new HashMap<String, Dimension>(); for (Map.Entry<String, Dimension> d : in.entrySet()) { result.put(d.getKey(), d.getValue().mergeNot()); } return result; } // 合并两次的维度,与 private static Map<String, Dimension> mergeOr(Map<String, Dimension> left, Map<String, Dimension> right) { Map<String, Dimension> m = new HashMap<String, Dimension>(left); for (Map.Entry<String, Dimension> e : right.entrySet()) { Dimension old = m.put(e.getKey(), e.getValue()); if (old != null) { m.put(e.getKey(), e.getValue().mergeOr(old)); } } return m; } // 合并两次的维度,或 private static Map<String, Dimension> mergeAnd(Map<String, Dimension> left, Map<String, Dimension> right) { Map<String, Dimension> m = new HashMap<String, Dimension>(left); for (Map.Entry<String, Dimension> e : right.entrySet()) { Dimension old = m.put(e.getKey(), e.getValue()); if (old != null) { m.put(e.getKey(), e.getValue().mergeAnd(old)); } } return m; } public static <T> List<T> repeat(List<T> source,int count){ if(count==1){ return source; } List<T> result=new ArrayList<T>(source.size()*count); for(int i=0;i<count;i++){ result.addAll(source); } return result; } }