diff --git a/core/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java b/core/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java index 22ea850bda..d25f2eefe7 100644 --- a/core/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java +++ b/core/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java @@ -90,6 +90,7 @@ public enum SQLBinaryOperator { Escape("ESCAPE", 110), RegExp("REGEXP", 110), NotRegExp("NOT REGEXP", 110), + MemberOf("MEMBER OF", 110), Equality("=", 110), EqEq("==", 110), @@ -145,6 +146,7 @@ public boolean isRelational() { case NotRLike: case RegExp: case NotRegExp: + case MemberOf: case Is: case IsNot: return true; diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index 68c4cced48..0827ece6de 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -560,6 +560,37 @@ protected void parseIndexOptions(SQLIndexDefinition indexDefinition) { } } + @Override + public SQLExpr relationalRest(SQLExpr expr) { + if (lexer.token() == Token.IDENTIFIER && lexer.hashLCase() == FnvHash.Constants.MEMBER) { + Lexer.SavePoint mark = lexer.mark(); + lexer.nextToken(); + + if (lexer.token() == Token.OF || lexer.identifierEquals("OF")) { + lexer.nextToken(); + + SQLExpr rightExp; + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + rightExp = expr(); + accept(Token.RPAREN); + if (rightExp instanceof SQLExprImpl) { + ((SQLExprImpl) rightExp).setParenthesized(true); + } + rightExp = primaryRest(rightExp); + } else { + rightExp = bitOr(); + } + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.MemberOf, rightExp, dbType); + return super.relationalRest(expr); + } + lexer.reset(mark); + } + + return super.relationalRest(expr); + } + @Override protected SQLExpr parseSelectItemRest(String ident, long hash_lower) { SQLExpr expr = null; diff --git a/core/src/test/java/com/alibaba/druid/bvt/filter/wall/mysql/MySqlWallTest100.java b/core/src/test/java/com/alibaba/druid/bvt/filter/wall/mysql/MySqlWallTest100.java index 9a48784346..e2a735541a 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/filter/wall/mysql/MySqlWallTest100.java +++ b/core/src/test/java/com/alibaba/druid/bvt/filter/wall/mysql/MySqlWallTest100.java @@ -19,7 +19,8 @@ import com.alibaba.druid.wall.spi.MySqlWallProvider; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MySqlWallTest100 { @Test @@ -31,4 +32,13 @@ public void test_false() throws Exception { assertFalse(provider.checkValid(sql)); } + + @Test + public void test_member_of() { + WallProvider provider = new MySqlWallProvider(); + + String sql = "select 'ab' member of('[23, \"abc\", 17, \"ab\", 10]')"; + + assertTrue(provider.checkValid(sql)); + } } diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/ComparisonFunctionsAndOperatorsTest.java b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/ComparisonFunctionsAndOperatorsTest.java index a65cce0243..93817791b2 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/ComparisonFunctionsAndOperatorsTest.java +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/ComparisonFunctionsAndOperatorsTest.java @@ -356,6 +356,18 @@ public void test_26() throws Exception { assertEquals("SELECT CAST(LEAST(3600, 9223372036854775808.0) AS SIGNED);", text); } + @Test + public void test_27() throws Exception { + String sql = "SELECT 'ab' MEMBER OF('[23, \"abc\", 17, \"ab\", 10]')"; + + SQLStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + + String text = output(stmtList); + + assertEquals("SELECT 'ab' MEMBER OF '[23, \"abc\", 17, \"ab\", 10]'", text); + } + private String output(List stmtList) { return SQLUtils.toSQLString(stmtList, JdbcConstants.MYSQL); }