diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java index d729c61a5..ea0671783 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java @@ -17,12 +17,26 @@ public class RegExpMySQLOperator extends BinaryExpression { private RegExpMatchOperatorType operatorType; private boolean useRLike = false; + private boolean not = false; public RegExpMySQLOperator(RegExpMatchOperatorType operatorType) { + this(false, operatorType); + } + + public RegExpMySQLOperator(boolean not, RegExpMatchOperatorType operatorType) { if (operatorType == null) { throw new NullPointerException(); } this.operatorType = operatorType; + this.not = not; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; } public RegExpMatchOperatorType getOperatorType() { @@ -45,7 +59,8 @@ public void accept(ExpressionVisitor expressionVisitor) { @Override public String getStringExpression() { - return (useRLike ? "RLIKE" : "REGEXP") + return (not?"NOT ":"") + + (useRLike ? "RLIKE" : "REGEXP") + (operatorType == RegExpMatchOperatorType.MATCH_CASESENSITIVE ? " BINARY" : ""); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index bd07803d6..3408dcf02 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -2630,6 +2630,7 @@ Expression RegularCondition() #RegularCondition: int oracleJoin=EqualsTo.NO_ORACLE_JOIN; int oraclePrior=EqualsTo.NO_ORACLE_PRIOR; boolean binary = false; + boolean not = false; } { [ LOOKAHEAD(2) { oraclePrior = EqualsTo.ORACLE_PRIOR_START; }] @@ -2647,7 +2648,7 @@ Expression RegularCondition() #RegularCondition: | token= { result = new NotEqualsTo(token.image); } | "@@" { result = new Matches(); } | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } - | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } + | [ { not=true; } ] [ { binary=true; } ] { result = new RegExpMySQLOperator(not, binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE).useRLike(); } | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 9c43130da..8b3c5b68a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -2509,6 +2509,16 @@ public void testRegexpMySQL() throws JSQLParserException { String stmt = "SELECT * FROM mytable WHERE first_name REGEXP '^Ste(v|ph)en$'"; assertSqlCanBeParsedAndDeparsed(stmt); } + + @Test + public void testNotRegexpMySQLIssue887() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE first_name NOT REGEXP '^Ste(v|ph)en$'"); + } + + @Test + public void testNotRegexpMySQLIssue887_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE NOT first_name REGEXP '^Ste(v|ph)en$'"); + } @Test public void testRegexpBinaryMySQL() throws JSQLParserException {