diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCallStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCallStatement.java index 5d59620f1e..f465f281b6 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCallStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCallStatement.java @@ -21,14 +21,27 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatementImpl; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLCallStatement extends SQLStatementImpl { + private boolean brace = false; + + private SQLVariantRefExpr outParameter; + private SQLName procedureName; private final List parameters = new ArrayList(); + public SQLVariantRefExpr getOutParameter() { + return outParameter; + } + + public void setOutParameter(SQLVariantRefExpr outParameter) { + this.outParameter = outParameter; + } + public SQLName getProcedureName() { return procedureName; } @@ -41,11 +54,21 @@ public class SQLCallStatement extends SQLStatementImpl { return parameters; } + public boolean isBrace() { + return brace; + } + + public void setBrace(boolean brace) { + this.brace = brace; + } + protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { + acceptChild(visitor, this.outParameter); acceptChild(visitor, this.procedureName); acceptChild(visitor, this.parameters); } visitor.endVisit(this); } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2EvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2EvalVisitor.java index c8e9d3e194..69e3596d64 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2EvalVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2EvalVisitor.java @@ -135,6 +135,11 @@ public class DB2EvalVisitor extends DB2ASTVisitorAdapter implements SQLEvalVisit public void registerFunction(String funcName, Function function) { functions.put(funcName, function); } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java index 17c4314aab..c70e199db1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java @@ -162,4 +162,9 @@ public class MySqlEvalVisitorImpl extends MySqlASTVisitorAdapter implements SQLE public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleEvalVisitor.java index cd7176e10c..87cfe03cfa 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleEvalVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleEvalVisitor.java @@ -135,6 +135,11 @@ public class OracleEvalVisitor extends OracleASTVisitorAdapter implements SQLEva public void registerFunction(String funcName, Function function) { functions.put(funcName, function); } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGEvalVisitor.java index 6836039648..56d9a5381d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGEvalVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGEvalVisitor.java @@ -135,6 +135,11 @@ public class PGEvalVisitor extends PGASTVisitorAdapter implements SQLEvalVisitor public void registerFunction(String funcName, Function function) { functions.put(funcName, function); } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java index 35ee8dfcad..bba9edddc9 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java @@ -136,6 +136,11 @@ public class SQLServerEvalVisitor extends SQLServerASTVisitorAdapter implements functions.put(funcName, function); } + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } + public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index b62ded9360..11f7259ddf 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -25,6 +25,7 @@ import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLListExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn; import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddConstraint; import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddIndex; @@ -1115,10 +1116,18 @@ public class SQLStatementParser extends SQLParser { lexer.nextToken(); brace = true; } + + SQLCallStatement stmt = new SQLCallStatement(); + + if (lexer.token() == Token.QUES) { + lexer.nextToken(); + accept(Token.EQ); + stmt.setOutParameter(new SQLVariantRefExpr("?")); + } acceptIdentifier("CALL"); - SQLCallStatement stmt = new SQLCallStatement(); + stmt.setProcedureName(exprParser.name()); if (lexer.token() == Token.LPAREN) { @@ -1129,6 +1138,7 @@ public class SQLStatementParser extends SQLParser { if (brace) { accept(Token.RBRACE); + stmt.setBrace(true); } return stmt; diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index a621694ac1..b4349ef25d 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -1141,11 +1141,23 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab @Override public boolean visit(SQLCallStatement x) { + if (x.isBrace()) { + print("{"); + } + if (x.getOutParameter() != null) { + x.getOutParameter().accept(this); + print(" = "); + } + print("CALL "); x.getProcedureName().accept(this); print('('); + printAndAccept(x.getParameters(), ", "); print(')'); + if (x.isBrace()) { + print("}"); + } return false; } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitor.java index 72bd1c3eed..5b72660c11 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitor.java @@ -30,6 +30,8 @@ public interface SQLEvalVisitor extends SQLASTVisitor { Function getFunction(String funcName); void registerFunction(String funcName, Function function); + + void unregisterFunction(String funcName); List getParameters(); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java index 52a52cf555..6c016e0c01 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java @@ -138,4 +138,9 @@ public class SQLEvalVisitorImpl extends SQLASTVisitorAdapter implements SQLEvalV public boolean visit(SQLIdentifierExpr x) { return SQLEvalVisitorUtils.visit(this, x); } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index 218a1b835f..ee15695723 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -228,7 +228,11 @@ public class SQLEvalVisitorUtils { public static boolean visit(SQLEvalVisitor visitor, SQLMethodInvokeExpr x) { String methodName = x.getMethodName().toLowerCase(); - Function function = functions.get(methodName); + Function function = visitor.getFunction(methodName); + + if (function == null) { + function = functions.get(methodName); + } if (function != null) { Object result = function.eval(visitor, x); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/functions/Nil.java b/src/main/java/com/alibaba/druid/sql/visitor/functions/Nil.java new file mode 100644 index 0000000000..17cca7a131 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/visitor/functions/Nil.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.sql.visitor.functions; + +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.visitor.SQLEvalVisitor; + +public class Nil implements Function { + + public final static Nil instance = new Nil(); + + @Override + public Object eval(SQLEvalVisitor visitor, SQLMethodInvokeExpr x) { + return null; + } + +} diff --git a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java index 559e2f1c43..6c93a2e95f 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -100,7 +100,9 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.visitor.ExportParameterVisitor; +import com.alibaba.druid.sql.visitor.SQLEvalVisitor; import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; +import com.alibaba.druid.sql.visitor.functions.Nil; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcUtils; @@ -739,7 +741,7 @@ public class WallVisitorUtils { dbType = wallContext.getDbType(); } - return SQLEvalVisitorUtils.eval(dbType, x, Collections.emptyList(), false); + return eval(dbType, x, Collections.emptyList()); } public static SQLExpr getFirst(SQLExpr x) { @@ -1042,7 +1044,7 @@ public class WallVisitorUtils { || x instanceof SQLInListExpr // || x instanceof SQLUnaryExpr // ) { - return SQLEvalVisitorUtils.eval(dbType, x, Collections.emptyList(), false); + return eval(dbType, x, Collections.emptyList()); } if (visitor != null && (!visitor.getConfig().isCaseConditionAllow()) && x instanceof SQLCaseExpr) { @@ -1065,11 +1067,21 @@ public class WallVisitorUtils { } } - return SQLEvalVisitorUtils.eval(dbType, x, Collections.emptyList(), false); + return eval(dbType, x, Collections.emptyList()); } return null; } + + public static Object eval(String dbType, SQLObject sqlObject, List parameters) { + SQLEvalVisitor visitor = SQLEvalVisitorUtils.createEvalVisitor(dbType); + visitor.setParameters(parameters); + visitor.registerFunction("rand", Nil.instance); + sqlObject.accept(visitor); + + Object value = SQLEvalVisitorUtils.getValue(sqlObject); + return value; + } public static boolean isSimpleCountTableSource(WallVisitor visitor, SQLTableSource tableSource) { if (!(tableSource instanceof SQLSubqueryTableSource)) { diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest144.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest144.java new file mode 100644 index 0000000000..bbc4ba116d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest144.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.wall.WallProvider; +import com.alibaba.druid.wall.spi.MySqlWallProvider; + +public class MySqlWallTest144 extends TestCase { + + public void test_false() throws Exception { + WallProvider provider = new MySqlWallProvider(); + + String sql = "select min(id) from wx_interact where activityid=1008 group by true_name,mobile having rand()<1"; + Assert.assertTrue(provider.checkValid(sql)); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCallTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCallTest2.java new file mode 100644 index 0000000000..bc802eb48d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCallTest2.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.oracle; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; + +public class OracleCallTest2 extends OracleTest { + + public void test_0() throws Exception { + String sql = "{?= call FUNC_QUERY_DEPT(?,?)}"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(0, visitor.getTables().size()); + +// Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments"))); + + Assert.assertEquals(0, visitor.getColumns().size()); + +// Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("departments", "department_id"))); + } +}