package com.midea.cloudSearch.druid.prase; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLListExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLNotExpr; import com.alibaba.druid.sql.ast.expr.SQLNullExpr; import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlSelectGroupByExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.midea.cloudSearch.druid.segment.Condition; import com.midea.cloudSearch.druid.segment.Delete; import com.midea.cloudSearch.druid.segment.Field; import com.midea.cloudSearch.druid.segment.From; import com.midea.cloudSearch.druid.segment.Hint; import com.midea.cloudSearch.druid.segment.HintFactory; import com.midea.cloudSearch.druid.segment.JoinSelect; import com.midea.cloudSearch.druid.segment.Select; import com.midea.cloudSearch.druid.segment.TableOnJoinSelect; import com.midea.cloudSearch.druid.segment.Where; import com.midea.cloudSearch.druid.segment.Where.CONN; import com.midea.cloudSearch.druid.spatial.SpatialParamsFactory; import com.midea.cloudSearch.exception.SqlParseException; public class SqlParser { public SqlParser() { } public Select parseSelect(SQLQueryExpr mySqlExpr) throws SqlParseException { MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) mySqlExpr.getSubQuery().getQuery(); Select select = parseSelect(query); return select; } private Select parseSelect(MySqlSelectQueryBlock query) throws SqlParseException { Select select = new Select(); findSelect(query, select,null); select.getFrom().addAll(findFrom(query.getFrom())); select.setWhere(findWhere(query.getWhere())); select.fillSubQueries(); select.getHints().addAll(parseHints(query.getHints())); findLimit(query.getLimit(), select); findOrderBy(query, select); findGroupBy(query, select); return select; } public Delete parseDelete(SQLDeleteStatement deleteStatement) throws SqlParseException { Delete delete = new Delete(); delete.getFrom().addAll(findFrom(deleteStatement.getTableSource())); delete.setWhere(findWhere(deleteStatement.getWhere())); return delete; } private Where findWhere(SQLExpr where) throws SqlParseException { if(where == null) { return null; } Where myWhere = Where.newInstance(); parseWhere(where, myWhere); return myWhere; } private boolean isCond(SQLBinaryOpExpr expr) { SQLExpr leftSide = expr.getLeft(); if(leftSide instanceof SQLMethodInvokeExpr){ return isAllowedMethodOnConditionLeft((SQLMethodInvokeExpr) leftSide,expr.getOperator()); } return leftSide instanceof SQLIdentifierExpr || leftSide instanceof SQLPropertyExpr || leftSide instanceof SQLVariantRefExpr; } private boolean isAllowedMethodOnConditionLeft(SQLMethodInvokeExpr method, SQLBinaryOperator operator) { return method.getMethodName().toLowerCase().equals("nested") && !operator.isLogical(); } public void parseWhere(SQLExpr expr, Where where) throws SqlParseException { if (expr instanceof SQLBinaryOpExpr && !isCond((SQLBinaryOpExpr) expr)) { SQLBinaryOpExpr bExpr = (SQLBinaryOpExpr) expr; routeCond(bExpr, bExpr.getLeft(), where); routeCond(bExpr, bExpr.getRight(), where); } else if (expr instanceof SQLNotExpr) { parseWhere(((SQLNotExpr) expr).getExpr(), where); negateWhere(where); } else { explanCond("AND", expr, where); } } private void routeCond(SQLBinaryOpExpr bExpr, SQLExpr sub, Where where) throws SqlParseException { if (sub instanceof SQLBinaryOpExpr && !isCond((SQLBinaryOpExpr) sub)) { SQLBinaryOpExpr binarySub = (SQLBinaryOpExpr) sub; if (binarySub.getOperator().priority != bExpr.getOperator().priority) { Where subWhere = new Where(bExpr.getOperator().name); where.addWhere(subWhere); parseWhere(binarySub, subWhere); } else { parseWhere(binarySub, where); } } else if (sub instanceof SQLNotExpr) { Where subWhere = new Where(bExpr.getOperator().name); where.addWhere(subWhere); parseWhere(((SQLNotExpr) sub).getExpr(), subWhere); negateWhere(subWhere); } else { explanCond(bExpr.getOperator().name, sub, where); } } private void explanCond(String opear, SQLExpr expr, Where where) throws SqlParseException { if (expr instanceof SQLBinaryOpExpr) { SQLBinaryOpExpr soExpr = (SQLBinaryOpExpr) expr; boolean methodAsOpear = false; boolean nestedFieldCondition = false; String nestedPath = null ; NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(soExpr.getLeft())){ soExpr.setLeft(new SQLIdentifierExpr(nestedType.field)); nestedFieldCondition = true; nestedPath = nestedType.path ; } if(soExpr.getRight() instanceof SQLMethodInvokeExpr){ SQLMethodInvokeExpr method = (SQLMethodInvokeExpr) soExpr.getRight(); String methodName = method.getMethodName().toLowerCase(); if(Condition.OPEAR.methodNameToOpear.containsKey(methodName)){ Object[] methodParametersValue = getMethodValuesWithSubQueries(method); Condition condition = new Condition(CONN.valueOf(opear) ,soExpr.getLeft().toString(), Condition.OPEAR.methodNameToOpear.get(methodName),methodParametersValue,nestedFieldCondition,nestedPath); where.addWhere(condition); methodAsOpear = true; } } if(!methodAsOpear){ Condition condition = new Condition(CONN.valueOf(opear), soExpr.getLeft().toString(), soExpr.getOperator().name, parseValue(soExpr.getRight()),nestedFieldCondition,nestedPath); where.addWhere(condition); } } else if (expr instanceof SQLInListExpr) { SQLInListExpr siExpr = (SQLInListExpr) expr; NestedType nestedType = new NestedType(); String leftSide = siExpr.getExpr().toString(); if(nestedType.tryFillFromExpr(siExpr.getExpr())){ leftSide = nestedType.field; } Condition condition = new Condition(CONN.valueOf(opear), leftSide, siExpr.isNot() ? "NOT IN" : "IN", parseValue(siExpr.getTargetList()),nestedType.field!=null,nestedType.path); where.addWhere(condition); } else if (expr instanceof SQLBetweenExpr) { SQLBetweenExpr between = ((SQLBetweenExpr) expr); String leftSide = between.getTestExpr().toString(); NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(between.getTestExpr())){ leftSide = nestedType.field; } Condition condition = new Condition(CONN.valueOf(opear), leftSide, between.isNot() ? "NOT BETWEEN" : "BETWEEN", new Object[]{parseValue(between.beginExpr), parseValue(between.endExpr)},nestedType.field!=null,nestedType.path); where.addWhere(condition); } else if (expr instanceof SQLMethodInvokeExpr) { SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) expr; List<SQLExpr> methodParameters = methodExpr.getParameters(); String methodName = methodExpr.getMethodName(); if(SpatialParamsFactory.isAllowedMethod(methodName)){ String fieldName = methodParameters.get(0).toString(); NestedType nestedType = new NestedType(); if (nestedType.tryFillFromExpr(methodParameters.get(0))) { fieldName = nestedType.field; } Object spatialParamsObject = SpatialParamsFactory.generateSpatialParamsObject(methodName, methodParameters); Condition condition = new Condition(CONN.valueOf(opear), fieldName, methodName, spatialParamsObject, nestedType.field != null, nestedType.path); where.addWhere(condition); } else if (methodName.toLowerCase().equals("nested")){ NestedType nestedType = new NestedType(); if(!nestedType.tryFillFromExpr(expr)){ throw new SqlParseException("could not fill nested from expr:"+expr); } Condition condition = new Condition(CONN.valueOf(opear),nestedType.path,methodName.toUpperCase(),nestedType.where); where.addWhere(condition); } else if (methodName.toLowerCase().equals("script")){ ScriptFilter scriptFilter = new ScriptFilter(); if(!scriptFilter.tryParseFromMethodExpr(methodExpr)){ throw new SqlParseException("could not parse script filter"); } Condition condition = new Condition(CONN.valueOf(opear),null,"SCRIPT",scriptFilter); where.addWhere(condition); } else { throw new SqlParseException("unsupported method: " + methodName); } } else if (expr instanceof SQLInSubQueryExpr){ SQLInSubQueryExpr sqlIn = (SQLInSubQueryExpr) expr; Select innerSelect = parseSelect((MySqlSelectQueryBlock) sqlIn.getSubQuery().getQuery()); if(innerSelect.getFields() == null || innerSelect.getFields().size()!=1) throw new SqlParseException("should only have one return field in subQuery"); SubQueryExpression subQueryExpression = new SubQueryExpression(innerSelect); String leftSide = sqlIn.getExpr().toString(); NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(sqlIn.getExpr())){ leftSide = nestedType.field; } Condition condition = new Condition(CONN.valueOf(opear), leftSide, sqlIn.isNot() ? "NOT IN" : "IN",subQueryExpression,nestedType.field!=null,nestedType.path); where.addWhere(condition); } else { throw new SqlParseException("err find condition " + expr.getClass()); } } private Object[] getMethodValuesWithSubQueries(SQLMethodInvokeExpr method) throws SqlParseException { List<Object> values = new ArrayList<>(); for(SQLExpr innerExpr : method.getParameters()){ if(innerExpr instanceof SQLQueryExpr){ Select select = parseSelect((MySqlSelectQueryBlock) ((SQLQueryExpr) innerExpr).getSubQuery().getQuery()); values.add(new SubQueryExpression(select)); } else if(innerExpr instanceof SQLTextLiteralExpr){ values.add(((SQLTextLiteralExpr)innerExpr).getText()); } else { values.add(innerExpr); } } return values.toArray(); } private Object[] parseValue(List<SQLExpr> targetList) throws SqlParseException { Object[] value = new Object[targetList.size()]; for (int i = 0; i < targetList.size(); i++) { value[i] = parseValue(targetList.get(i)); } return value; } private Object parseValue(SQLExpr expr) throws SqlParseException { if (expr instanceof SQLNumericLiteralExpr) { return ((SQLNumericLiteralExpr) expr).getNumber(); } else if (expr instanceof SQLCharExpr) { return ((SQLCharExpr) expr).getText(); } else if (expr instanceof SQLMethodInvokeExpr) { return expr; } else if (expr instanceof SQLNullExpr) { return null; } else if (expr instanceof SQLIdentifierExpr) { return expr; } else if (expr instanceof SQLPropertyExpr){ return expr; } else { throw new SqlParseException( String.format("Failed to parse SqlExpression of type %s. expression value: %s", expr.getClass(), expr) ); } } private void findSelect(MySqlSelectQueryBlock query, Select select,String tableAlias) throws SqlParseException { //select a,b,c List<SQLSelectItem> selectList = query.getSelectList(); for (SQLSelectItem sqlSelectItem : selectList) { Field field = FieldMaker.makeField(sqlSelectItem.getExpr(), sqlSelectItem.getAlias(),tableAlias); select.addField(field); } } private void findGroupBy(MySqlSelectQueryBlock query, Select select) throws SqlParseException { SQLSelectGroupByClause groupBy = query.getGroupBy(); if (groupBy == null) { return; } List<SQLExpr> items = groupBy.getItems(); List<SQLExpr> standardGroupBys = new ArrayList<>(); for (SQLExpr sqlExpr : items) { //todo: mysql expr patch if (sqlExpr instanceof MySqlSelectGroupByExpr) { MySqlSelectGroupByExpr sqlSelectGroupByExpr = (MySqlSelectGroupByExpr) sqlExpr; sqlExpr = sqlSelectGroupByExpr.getExpr(); } if ((sqlExpr instanceof SQLParensIdentifierExpr || !(sqlExpr instanceof SQLIdentifierExpr|| sqlExpr instanceof SQLMethodInvokeExpr)) && !standardGroupBys.isEmpty()) { // flush the standard group bys select.addGroupBy(convertExprsToFields(standardGroupBys)); standardGroupBys = new ArrayList<>(); } if (sqlExpr instanceof SQLParensIdentifierExpr) { // single item with parens (should get its own aggregation) select.addGroupBy(FieldMaker.makeField(sqlExpr, null,null)); } else if (sqlExpr instanceof SQLListExpr) { // multiple items in their own list SQLListExpr listExpr = (SQLListExpr) sqlExpr; select.addGroupBy(convertExprsToFields(listExpr.getItems())); } else { // everything else gets added to the running list of standard group bys standardGroupBys.add(sqlExpr); } } if (!standardGroupBys.isEmpty()) { select.addGroupBy(convertExprsToFields(standardGroupBys)); } } private List<Field> convertExprsToFields(List<? extends SQLExpr> exprs) throws SqlParseException { List<Field> fields = new ArrayList<>(exprs.size()); for (SQLExpr expr : exprs) { fields.add(FieldMaker.makeField(expr, null,null)); } return fields; } private String sameAliasWhere(Where where, String... aliases) throws SqlParseException { if(where == null) return null; if(where instanceof Condition) { Condition condition = (Condition) where; String fieldName = condition.getName(); for (String alias : aliases){ String prefix = alias + "."; if(fieldName.startsWith(prefix)){ return alias; } } throw new SqlParseException(String.format("fieldName : %s on codition:%s does not contain alias", fieldName, condition.toString())); } List<String> sameAliases = new ArrayList<>(); if(where.getWheres()!=null && where.getWheres().size() > 0) { for (Where innerWhere : where.getWheres()) sameAliases.add(sameAliasWhere(innerWhere, aliases)); } if ( sameAliases.contains(null) ) return null; String firstAlias = sameAliases.get(0); //return null if more than one alias for(String alias : sameAliases){ if(!alias.equals(firstAlias)) return null; } return firstAlias; } private void findOrderBy(MySqlSelectQueryBlock query, Select select) throws SqlParseException { SQLOrderBy orderBy = query.getOrderBy(); if (orderBy == null) { return; } List<SQLSelectOrderByItem> items = orderBy.getItems(); addOrderByToSelect(select, items, null); } private void addOrderByToSelect(Select select, List<SQLSelectOrderByItem> items, String alias) throws SqlParseException { for (SQLSelectOrderByItem sqlSelectOrderByItem : items) { SQLExpr expr = sqlSelectOrderByItem.getExpr(); String orderByName = FieldMaker.makeField(expr, null, null).toString(); if (sqlSelectOrderByItem.getType() == null) { sqlSelectOrderByItem.setType(SQLOrderingSpecification.ASC); } String type = sqlSelectOrderByItem.getType().toString(); orderByName = orderByName.replace("`", ""); if(alias!=null) orderByName = orderByName.replaceFirst(alias+"\\.",""); select.addOrderBy(orderByName, type); } } private void findLimit(MySqlSelectQueryBlock.Limit limit, Select select) { if (limit == null) { return; } select.setRowCount(Integer.parseInt(limit.getRowCount().toString())); if (limit.getOffset() != null) select.setOffset(Integer.parseInt(limit.getOffset().toString())); } /** * Parse the from clause * @param from the from clause. * @return list of From objects represents all the sources. */ private List<From> findFrom(SQLTableSource from) { boolean isSqlExprTable = from.getClass().isAssignableFrom(SQLExprTableSource.class); if(isSqlExprTable){ SQLExprTableSource fromExpr = (SQLExprTableSource) from; String[] split = fromExpr.getExpr().toString().split(","); ArrayList<From> fromList = new ArrayList<>(); for (String source : split) { fromList.add(new From(source.trim(),fromExpr.getAlias())); } return fromList; } SQLJoinTableSource joinTableSource = ((SQLJoinTableSource) from); List<From> fromList = new ArrayList<>(); fromList.addAll(findFrom(joinTableSource.getLeft())); fromList.addAll(findFrom(joinTableSource.getRight())); return fromList; } public JoinSelect parseJoinSelect(SQLQueryExpr sqlExpr) throws SqlParseException { MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery(); List<From> joinedFrom = findJoinedFrom(query.getFrom()); if(joinedFrom.size() != 2) throw new RuntimeException("currently supports only 2 tables join"); JoinSelect joinSelect = createBasicJoinSelectAccordingToTableSource((SQLJoinTableSource) query.getFrom()); List<Hint> hints = parseHints(query.getHints()); joinSelect.setHints(hints); String firstTableAlias = joinedFrom.get(0).getAlias(); String secondTableAlias = joinedFrom.get(1).getAlias(); Map<String, Where> aliasToWhere = splitAndFindWhere(query.getWhere(), firstTableAlias, secondTableAlias); Map<String, List<SQLSelectOrderByItem>> aliasToOrderBy = splitAndFindOrder(query.getOrderBy(), firstTableAlias, secondTableAlias); List<Condition> connectedConditions = getConditionsFlatten(joinSelect.getConnectedWhere()); joinSelect.setConnectedConditions(connectedConditions); fillTableSelectedJoin(joinSelect.getFirstTable(), query, joinedFrom.get(0), aliasToWhere.get(firstTableAlias),aliasToOrderBy.get(firstTableAlias), connectedConditions); fillTableSelectedJoin(joinSelect.getSecondTable(), query, joinedFrom.get(1), aliasToWhere.get(secondTableAlias), aliasToOrderBy.get(secondTableAlias),connectedConditions); updateJoinLimit(query.getLimit(), joinSelect); //todo: throw error feature not supported: no group bys on joins ? return joinSelect; } private Map<String, List<SQLSelectOrderByItem>> splitAndFindOrder(SQLOrderBy orderBy, String firstTableAlias, String secondTableAlias) throws SqlParseException { Map<String,List<SQLSelectOrderByItem>> aliasToOrderBys = new HashMap<>(); aliasToOrderBys.put(firstTableAlias,new ArrayList<SQLSelectOrderByItem>()); aliasToOrderBys.put(secondTableAlias,new ArrayList<SQLSelectOrderByItem>()); if(orderBy == null) return aliasToOrderBys; List<SQLSelectOrderByItem> orderByItems = orderBy.getItems(); for(SQLSelectOrderByItem orderByItem : orderByItems){ if(orderByItem.getExpr().toString().startsWith(firstTableAlias+".")){ aliasToOrderBys.get(firstTableAlias).add(orderByItem); } else if(orderByItem.getExpr().toString().startsWith(secondTableAlias+".")){ aliasToOrderBys.get(secondTableAlias).add(orderByItem); } else throw new SqlParseException("order by field on join request should have alias before, got " + orderByItem.getExpr().toString()); } return aliasToOrderBys; } private void updateJoinLimit(MySqlSelectQueryBlock.Limit limit, JoinSelect joinSelect) { if(limit != null && limit.getRowCount()!= null) { int sizeLimit = Integer.parseInt(limit.getRowCount().toString()); joinSelect.setTotalLimit(sizeLimit); } } private List<Hint> parseHints(List<SQLCommentHint> sqlHints) { List<Hint> hints = new ArrayList<>(); for (SQLCommentHint sqlHint : sqlHints) { Hint hint = HintFactory.getHintFromString(sqlHint.getText()); if (hint != null) hints.add(hint); } return hints; } private JoinSelect createBasicJoinSelectAccordingToTableSource(SQLJoinTableSource joinTableSource) throws SqlParseException { JoinSelect joinSelect = new JoinSelect(); if(joinTableSource.getCondition() != null ) { Where where = Where.newInstance(); parseWhere(joinTableSource.getCondition(), where); joinSelect.setConnectedWhere(where); } SQLJoinTableSource.JoinType joinType = joinTableSource.getJoinType(); joinSelect.setJoinType(joinType); return joinSelect; } private Map<String, Where> splitAndFindWhere(SQLExpr whereExpr, String firstTableAlias, String secondTableAlias) throws SqlParseException { Where where = findWhere(whereExpr); return splitWheres(where, firstTableAlias, secondTableAlias); } private void fillTableSelectedJoin(TableOnJoinSelect tableOnJoin, MySqlSelectQueryBlock query, From tableFrom, Where where, List<SQLSelectOrderByItem> orderBys, List<Condition> conditions) throws SqlParseException { String alias = tableFrom.getAlias(); fillBasicTableSelectJoin(tableOnJoin, tableFrom, where,orderBys, query); tableOnJoin.setConnectedFields(getConnectedFields(conditions, alias)); tableOnJoin.setSelectedFields(new ArrayList<Field>(tableOnJoin.getFields())); tableOnJoin.setAlias(alias); tableOnJoin.fillSubQueries(); } private List<Field> getConnectedFields(List<Condition> conditions, String alias) throws SqlParseException { List<Field> fields = new ArrayList<>(); String prefix = alias + "."; for(Condition condition : conditions) { if(condition.getName().startsWith(prefix)){ fields.add(new Field(condition.getName().replaceFirst(prefix,""),null)); } else { if(! ((condition.getValue() instanceof SQLPropertyExpr)||(condition.getValue() instanceof SQLIdentifierExpr)||(condition.getValue() instanceof String))){ throw new SqlParseException("conditions on join should be one side is firstTable second Other , condition was:" + condition.toString()); } String aliasDotValue = condition.getValue().toString(); int indexOfDot = aliasDotValue.indexOf("."); String owner = aliasDotValue.substring(0, indexOfDot); if(owner.equals(alias)) fields.add(new Field(aliasDotValue.substring(indexOfDot+1),null)); } } return fields; } private void fillBasicTableSelectJoin(TableOnJoinSelect select, From from, Where where, List<SQLSelectOrderByItem> orderBys, MySqlSelectQueryBlock query) throws SqlParseException { select.getFrom().add(from); findSelect(query, select,from.getAlias()); select.setWhere(where); addOrderByToSelect(select, orderBys,from.getAlias()); } private List<Condition> getJoinConditionsFlatten(SQLJoinTableSource from) throws SqlParseException { List<Condition> conditions = new ArrayList<>(); if(from.getCondition() == null ) return conditions; Where where = Where.newInstance(); parseWhere(from.getCondition(), where); addIfConditionRecursive(where, conditions); return conditions; } private List<Condition> getConditionsFlatten(Where where) throws SqlParseException { List<Condition> conditions = new ArrayList<>(); if(where == null) return conditions; addIfConditionRecursive(where, conditions); return conditions; } private Map<String,Where> splitWheres(Where where, String... aliases) throws SqlParseException { Map<String,Where> aliasToWhere = new HashMap<>(); for(String alias : aliases){ aliasToWhere.put(alias,null); } if(where == null) return aliasToWhere; String allWhereFromSameAlias = sameAliasWhere(where, aliases); if( allWhereFromSameAlias != null ) { removeAliasPrefix(where,allWhereFromSameAlias); aliasToWhere.put(allWhereFromSameAlias,where); return aliasToWhere; } for(Where innerWhere : where.getWheres()){ String sameAlias = sameAliasWhere(innerWhere, aliases); if(sameAlias == null ) throw new SqlParseException("Currently support only one hierarchy on different tables where"); removeAliasPrefix(innerWhere,sameAlias); Where aliasCurrentWhere = aliasToWhere.get(sameAlias); if(aliasCurrentWhere == null) { aliasToWhere.put(sameAlias, innerWhere); } else { Where andWhereContainer = Where.newInstance(); andWhereContainer.addWhere(aliasCurrentWhere); andWhereContainer.addWhere(innerWhere); aliasToWhere.put(sameAlias,andWhereContainer); } } return aliasToWhere; } private void removeAliasPrefix(Where where, String alias) { if(where instanceof Condition) { Condition cond = (Condition) where; String fieldName = cond.getName(); String aliasPrefix = alias + "."; cond.setName(cond.getName().replaceFirst(aliasPrefix, "")); return; } for(Where innerWhere : where.getWheres()) { removeAliasPrefix(innerWhere, alias); } } private void addIfConditionRecursive(Where where, List<Condition> conditions) throws SqlParseException { if(where instanceof Condition){ Condition cond = (Condition) where; if( ! ((cond.getValue() instanceof SQLIdentifierExpr) ||(cond.getValue() instanceof SQLPropertyExpr)|| (cond.getValue() instanceof String))){ throw new SqlParseException("conditions on join should be one side is secondTable OPEAR firstTable, condition was:" + cond.toString()); } conditions.add(cond); } for(Where innerWhere : where.getWheres()) { addIfConditionRecursive(innerWhere,conditions); } } private List<From> findJoinedFrom(SQLTableSource from) { SQLJoinTableSource joinTableSource = ((SQLJoinTableSource) from); List<From> fromList = new ArrayList<>(); fromList.addAll(findFrom(joinTableSource.getLeft())); fromList.addAll(findFrom(joinTableSource.getRight())); return fromList; } private void negateWhere(Where where) throws SqlParseException { for (Where sub : where.getWheres()) { if (sub instanceof Condition) { Condition cond = (Condition) sub; cond.setOpear(cond.getOpear().negative()); } else { negateWhere(sub); } sub.setConn(sub.getConn().negative()); } } }