package io.mycat.backend.jdbc.mongodb; import java.sql.Types; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLNullExpr; import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; 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.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlSelectGroupByExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; /** * 功能详细描述 * @author sohudo[http://blog.csdn.net/wind520] * @create 2014年12月19日 下午6:50:23 * @version 0.0.1 */ public class MongoSQLParser { private static final Logger LOGGER = LoggerFactory.getLogger(MongoSQLParser.class); private final DB _db; // private final String _sql; private final SQLStatement statement; private List _params; private int _pos; public MongoSQLParser(DB db, String sql) throws MongoSQLException { this._db = db; // this._sql = sql; this.statement = parser(sql); } public SQLStatement parser(String s) throws MongoSQLException { s = s.trim(); try { MySqlStatementParser parser = new MySqlStatementParser(s); return parser.parseStatement(); } catch (Exception e) { LOGGER.error("MongoSQLParser.parserError", e); } throw new MongoSQLException.ErrorSQL(s); } public void setParams(List params) { this._pos = 1; this._params = params; } public MongoData query() throws MongoSQLException{ if (!(statement instanceof SQLSelectStatement)) { //return null; throw new IllegalArgumentException("not a query sql statement"); } MongoData mongo=new MongoData(); DBCursor c=null; SQLSelectStatement selectStmt = (SQLSelectStatement)statement; SQLSelectQuery sqlSelectQuery =selectStmt.getSelect().getQuery(); int icount=0; if(sqlSelectQuery instanceof MySqlSelectQueryBlock) { MySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery(); BasicDBObject fields = new BasicDBObject(); //显示的字段 for(SQLSelectItem item : mysqlSelectQuery.getSelectList()) { //System.out.println(item.toString()); if (!(item.getExpr() instanceof SQLAllColumnExpr)) { if (item.getExpr() instanceof SQLAggregateExpr) { SQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr(); if (expr.getMethodName().equals("COUNT")) { icount=1; mongo.setField(getExprFieldName(expr), Types.BIGINT); } fields.put(getExprFieldName(expr), Integer.valueOf(1)); } else { fields.put(getFieldName(item), Integer.valueOf(1)); } } } //表名 SQLTableSource table=mysqlSelectQuery.getFrom(); DBCollection coll =this._db.getCollection(table.toString()); mongo.setTable(table.toString()); SQLExpr expr=mysqlSelectQuery.getWhere(); DBObject query = parserWhere(expr); //System.out.println(query); SQLSelectGroupByClause groupby=mysqlSelectQuery.getGroupBy(); BasicDBObject gbkey = new BasicDBObject(); if (groupby!=null) { for (SQLExpr gbexpr:groupby.getItems()){ if (gbexpr instanceof MySqlSelectGroupByExpr) { SQLExpr gbyexpr=((MySqlSelectGroupByExpr) gbexpr).getExpr(); gbkey.put(getFieldName2(gbyexpr), Integer.valueOf(1)); } } icount=2; } int limitoff=0; int limitnum=0; if (mysqlSelectQuery.getLimit()!=null) { limitoff=getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset()); limitnum=getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount()); } if (icount==1) { mongo.setCount(coll.count(query)); } else if (icount==2){ BasicDBObject initial = new BasicDBObject(); initial.put("num", 0); String reduce="function (obj, prev) { " +" prev.num++}"; mongo.setGrouyBy(coll.group(gbkey, query, initial, reduce)); } else { if ((limitoff>0) || (limitnum>0)) { c = coll.find(query, fields).skip(limitoff).limit(limitnum); } else { c = coll.find(query, fields); } SQLOrderBy orderby=mysqlSelectQuery.getOrderBy(); if (orderby != null ){ BasicDBObject order = new BasicDBObject(); for (int i = 0; i < orderby.getItems().size(); i++) { SQLSelectOrderByItem orderitem = orderby.getItems().get(i); order.put(orderitem.getExpr().toString(), Integer.valueOf(getSQLExprToAsc(orderitem.getType()))); } c.sort(order); // System.out.println(order); } } mongo.setCursor(c); } return mongo; } public int executeUpdate() throws MongoSQLException { if (statement instanceof SQLInsertStatement) { return InsertData((SQLInsertStatement)statement); } if (statement instanceof SQLUpdateStatement) { return UpData((SQLUpdateStatement)statement); } if (statement instanceof SQLDropTableStatement) { return dropTable((SQLDropTableStatement)statement); } if (statement instanceof SQLDeleteStatement) { return DeleteDate((SQLDeleteStatement)statement); } if (statement instanceof SQLCreateTableStatement) { return 1; } return 1; } private int InsertData(SQLInsertStatement state) { if (state.getValues().getValues().size() ==0 ){ throw new RuntimeException("number of columns error"); } if (state.getValues().getValues().size() != state.getColumns().size()){ throw new RuntimeException("number of values and columns have to match"); } SQLTableSource table=state.getTableSource(); BasicDBObject o = new BasicDBObject(); int i=0; for(SQLExpr col : state.getColumns()) { o.put(getFieldName2(col), getExpValue(state.getValues().getValues().get(i))); i++; } DBCollection coll =this._db.getCollection(table.toString()); coll.insert(new DBObject[] { o }); return 1; } private int UpData(SQLUpdateStatement state) { SQLTableSource table=state.getTableSource(); DBCollection coll =this._db.getCollection(table.toString()); SQLExpr expr=state.getWhere(); DBObject query = parserWhere(expr); BasicDBObject set = new BasicDBObject(); for(SQLUpdateSetItem col : state.getItems()){ set.put(getFieldName2(col.getColumn()), getExpValue(col.getValue())); } DBObject mod = new BasicDBObject("$set", set); coll.updateMulti(query, mod); //System.out.println("changs count:"+coll.getStats().size()); return 1; } private int DeleteDate(SQLDeleteStatement state) { SQLTableSource table=state.getTableSource(); DBCollection coll =this._db.getCollection(table.toString()); SQLExpr expr=state.getWhere(); if (expr==null) { throw new RuntimeException("not where of sql"); } DBObject query = parserWhere(expr); coll.remove(query); return 1; } private int dropTable(SQLDropTableStatement state) { for (SQLTableSource table : state.getTableSources()){ DBCollection coll =this._db.getCollection(table.toString()); coll.drop(); } return 1; } private int getSQLExprToInt(SQLExpr expr){ if (expr instanceof SQLIntegerExpr){ return ((SQLIntegerExpr)expr).getNumber().intValue(); } return 0; } private int getSQLExprToAsc(SQLOrderingSpecification ASC){ if (ASC==null ) return 1; if (ASC==SQLOrderingSpecification.DESC){ return -1; } else { return 1; } } public String remove(String resource,char ch) { StringBuffer buffer=new StringBuffer(); int position=0; char currentChar; while(position<resource.length()) { currentChar=resource.charAt(position++); if(currentChar!=ch) buffer.append(currentChar); } return buffer.toString(); } private Object getExpValue(SQLExpr expr){ if (expr instanceof SQLIntegerExpr){ return ((SQLIntegerExpr)expr).getNumber().intValue(); } if (expr instanceof SQLNumberExpr){ return ((SQLNumberExpr)expr).getNumber().doubleValue(); } if (expr instanceof SQLCharExpr){ String va=((SQLCharExpr)expr).toString(); return remove(va,'\''); } if (expr instanceof SQLBooleanExpr){ return ((SQLBooleanExpr)expr).getValue(); } if (expr instanceof SQLNullExpr){ return null; } if (expr instanceof SQLVariantRefExpr) { return this._params.get(this._pos++); } return expr; } private String getExprFieldName(SQLAggregateExpr expr){ String field=""; for (SQLExpr item :expr.getArguments()){ field+=item.toString(); } return expr.getMethodName()+"("+field+")"; } private String getFieldName2(SQLExpr item){ return item.toString(); } private String getFieldName(SQLSelectItem item){ return item.toString(); } private DBObject parserWhere(SQLExpr expr){ BasicDBObject o = new BasicDBObject(); parserWhere(expr,o); return o; } private void parserDBObject(BasicDBObject ob,String akey, String aop,Object aval){ boolean isok=false; if (!(ob.keySet().isEmpty())) { for (String field : ob.keySet()) { if (akey.equals(field)){ Object val = ob.get(field); if (val instanceof BasicDBObject) { ((BasicDBObject) val).put(aop, aval); ob.put(field, (BasicDBObject) val); isok=true; break; } else if (val instanceof BasicDBList) { // newobj.put(field, ((BasicDBList)val).copy()); } } } } if (!isok) { BasicDBObject xo = new BasicDBObject(); xo.put(aop, aval); ob.put(akey,xo); } } @SuppressWarnings("unused") private void opSQLExpr(SQLBinaryOpExpr expr,BasicDBObject o) { SQLExpr exprL=expr.getLeft(); if (!(exprL instanceof SQLBinaryOpExpr)) { if (expr.getOperator().getName().equals("=")) { o.put(exprL.toString(), getExpValue(expr.getRight())); } else { //BasicDBObject xo = new BasicDBObject(); String op=""; if (expr.getOperator().getName().equals("<")) op="$lt"; if (expr.getOperator().getName().equals("<=")) op="$lte"; if (expr.getOperator().getName().equals(">")) op="$gt"; if (expr.getOperator().getName().equals(">=")) op="$gte"; if (expr.getOperator().getName().equals("!=")) op="$ne"; if (expr.getOperator().getName().equals("<>")) op="$ne"; //xo.put(op, getExpValue(expr.getRight())); // o.put(exprL.toString(),xo); parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight())); } } } private void parserWhere(SQLExpr aexpr,BasicDBObject o){ if(aexpr instanceof SQLBinaryOpExpr){ SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr; SQLExpr exprL=expr.getLeft(); if (!(exprL instanceof SQLBinaryOpExpr)) { //opSQLExpr((SQLBinaryOpExpr)aexpr,o); if (expr.getOperator().getName().equals("=")) { o.put(exprL.toString(), getExpValue(expr.getRight())); } else { String op=""; if (expr.getOperator().getName().equals("<")) op="$lt"; if (expr.getOperator().getName().equals("<=")) op="$lte"; if (expr.getOperator().getName().equals(">")) op="$gt"; if (expr.getOperator().getName().equals(">=")) op="$gte"; if (expr.getOperator().getName().equals("!=")) op="$ne"; if (expr.getOperator().getName().equals("<>")) op="$ne"; parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight())); } } else { if (expr.getOperator().getName().equals("AND")) { parserWhere(exprL,o); parserWhere(expr.getRight(),o); } else if (expr.getOperator().getName().equals("OR")) { orWhere(exprL,expr.getRight(),o); } else { throw new RuntimeException("Can't identify the operation of of where"); } } } } private void orWhere(SQLExpr exprL,SQLExpr exprR ,BasicDBObject ob){ BasicDBObject xo = new BasicDBObject(); BasicDBObject yo = new BasicDBObject(); parserWhere(exprL,xo); parserWhere(exprR,yo); ob.put("$or",new Object[]{xo,yo}); } }