From 9886b02975d17499f8b88a667e695301be6414bb Mon Sep 17 00:00:00 2001 From: Dave Lindquist Date: Tue, 26 Apr 2016 13:15:54 -0600 Subject: [PATCH] Added ability to have operators like '>=' or '<=' separated by a space. This includes: * Modifying the JJT syntax to support the 'space in the middle' versions of operators (any quantity of whitespace is supported). * Modifying the various operators to inherit from a new 'ComparisonOperator' class, which handles the (previously NotEqualsTo- only) logic for capturing the form of the operator. * Giving each of the various operators a constructor that accepts the syntax used. * Modifying TestUtils to strip comments out before comparing SQL text (necessary because condition07.sql is now passing, and has a comment). * Updating SpecialOracleTest to indicate 130 tests passing now (condition7.sql now passes). * Adding a new test specifically for operators into SpecialOracleTest. NOTE: Because the "! =" form of the 'not equals' operator means something different in PostgresSQL (factorial of previous argument + equals), we do NOT include that case. --- .../relational/ComparisonOperator.java | 36 ++++++++++ .../operators/relational/EqualsTo.java | 11 ++-- .../operators/relational/GreaterThan.java | 11 ++-- .../relational/GreaterThanEquals.java | 15 +++-- .../operators/relational/MinorThan.java | 11 ++-- .../operators/relational/MinorThanEquals.java | 15 +++-- .../operators/relational/NotEqualsTo.java | 16 +---- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 23 ++++--- .../net/sf/jsqlparser/test/TestUtils.java | 6 +- .../test/select/SpecialOracleTest.java | 65 ++++++++++++++++++- 10 files changed, 155 insertions(+), 54 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java new file mode 100644 index 000000000..1f0b6f35f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java @@ -0,0 +1,36 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +public abstract class ComparisonOperator extends OldOracleJoinBinaryExpression { + + private final String operator; + + public ComparisonOperator(String operator) { + this.operator = operator; + } + + @Override + public String getStringExpression() { + return operator; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java index 804b16b56..578a851a0 100755 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java @@ -23,15 +23,14 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class EqualsTo extends OldOracleJoinBinaryExpression { +public class EqualsTo extends ComparisonOperator { - @Override - public void accept(ExpressionVisitor expressionVisitor) { - expressionVisitor.visit(this); + public EqualsTo() { + super("="); } @Override - public String getStringExpression() { - return "="; + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); } } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java index 66dacd6f1..d5380b55e 100755 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java @@ -23,15 +23,14 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class GreaterThan extends OldOracleJoinBinaryExpression { +public class GreaterThan extends ComparisonOperator { + public GreaterThan() { + super(">"); + } + @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); } - - @Override - public String getStringExpression() { - return ">"; - } } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java index 17e462d13..211e1b79d 100755 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java @@ -23,15 +23,18 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class GreaterThanEquals extends OldOracleJoinBinaryExpression { +public class GreaterThanEquals extends ComparisonOperator { - @Override - public void accept(ExpressionVisitor expressionVisitor) { - expressionVisitor.visit(this); + public GreaterThanEquals() { + super(">="); + } + + public GreaterThanEquals(String operator) { + super(operator); } @Override - public String getStringExpression() { - return ">="; + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); } } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java index b0879b0cd..db5004293 100755 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java @@ -23,15 +23,14 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class MinorThan extends OldOracleJoinBinaryExpression { +public class MinorThan extends ComparisonOperator { - @Override - public void accept(ExpressionVisitor expressionVisitor) { - expressionVisitor.visit(this); + public MinorThan() { + super("<"); } @Override - public String getStringExpression() { - return "<"; + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); } } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java index 170325279..869f92e5a 100755 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java @@ -23,15 +23,18 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class MinorThanEquals extends OldOracleJoinBinaryExpression { +public class MinorThanEquals extends ComparisonOperator { - @Override - public void accept(ExpressionVisitor expressionVisitor) { - expressionVisitor.visit(this); + public MinorThanEquals() { + super("<="); + } + + public MinorThanEquals(String operator) { + super(operator); } @Override - public String getStringExpression() { - return "<="; + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); } } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java index a327b8175..3ff92d3f5 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java @@ -23,28 +23,18 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; -public class NotEqualsTo extends OldOracleJoinBinaryExpression { - - private final String operator; +public class NotEqualsTo extends ComparisonOperator { public NotEqualsTo() { - operator = "<>"; + super("<>"); } public NotEqualsTo(String operator) { - this.operator = operator; - if (!"!=".equals(operator) && !"<>".equals(operator)) { - throw new IllegalArgumentException("only <> or != allowed"); - } + super(operator); } @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); } - - @Override - public String getStringExpression() { - return operator; - } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 7cd9e57c2..eac8ae8be 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -95,10 +95,7 @@ PARSER_END(CCJSqlParser) SKIP: { - " " -| "\t" -| "\r" -| "\n" + } TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ @@ -237,6 +234,14 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | } +TOKEN : /* Operators */ +{ + " ()* "="> +| )* "="> +| )* ">"> +| ( ["e","E"] (["+", "-"])? )? @@ -1611,14 +1616,14 @@ Expression RegularCondition(): [ "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ] - ( + ( LOOKAHEAD(2) ">" { result = new GreaterThan(); } | "<" { result = new MinorThan(); } | "=" { result = new EqualsTo(); } - | ">=" { result = new GreaterThanEquals(); } - | "<=" { result = new MinorThanEquals(); } - | "<>" { result = new NotEqualsTo(); } - | "!=" { result = new NotEqualsTo("!="); } + | token= { result = new GreaterThanEquals(token.image); } + | token= { result = new MinorThanEquals(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | 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); } diff --git a/src/test/java/net/sf/jsqlparser/test/TestUtils.java b/src/test/java/net/sf/jsqlparser/test/TestUtils.java index 0c63a7056..908c6802b 100644 --- a/src/test/java/net/sf/jsqlparser/test/TestUtils.java +++ b/src/test/java/net/sf/jsqlparser/test/TestUtils.java @@ -25,6 +25,7 @@ import net.sf.jsqlparser.util.deparser.*; import java.io.*; +import java.util.regex.Pattern; import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertEquals; @@ -42,6 +43,8 @@ */ public class TestUtils { + private static final Pattern SQL_COMMENT_PATTERN = Pattern.compile("(--.*$)|(/\\*.*?\\*/)",Pattern.MULTILINE); + public static void assertSqlCanBeParsedAndDeparsed(String statement) throws JSQLParserException { assertSqlCanBeParsedAndDeparsed(statement, false); } @@ -72,8 +75,9 @@ public static void assertStatementCanBeDeparsedAs(Statement parsed, String state assertEquals(buildSqlString(statement, laxDeparsingCheck), buildSqlString(deParser.getBuffer().toString(), laxDeparsingCheck)); } - + public static String buildSqlString(String sql, boolean laxDeparsingCheck) { + sql = SQL_COMMENT_PATTERN.matcher(sql).replaceAll(""); if (laxDeparsingCheck) { return sql.replaceAll("\\s", " ").replaceAll("\\s+", " ").replaceAll("\\s*([/,()=+\\-*|\\]<>])\\s*", "$1").toLowerCase().trim(); } else { diff --git a/src/test/java/net/sf/jsqlparser/test/select/SpecialOracleTest.java b/src/test/java/net/sf/jsqlparser/test/select/SpecialOracleTest.java index 28786f00f..54aa078df 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SpecialOracleTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SpecialOracleTest.java @@ -24,6 +24,9 @@ import java.util.logging.Logger; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.parser.TokenMgrError; + import static net.sf.jsqlparser.test.TestUtils.*; import org.apache.commons.io.FileUtils; import static org.junit.Assert.assertTrue; @@ -63,6 +66,9 @@ public void testAllSqlsParseDeparse() throws IOException { } catch (JSQLParserException ex) { //LOG.log(Level.SEVERE, null, ex); LOG.log(Level.INFO, " -> PROBLEM {0}", ex.toString()); + } catch (TokenMgrError ex) { + //LOG.log(Level.SEVERE, null, ex); + LOG.log(Level.INFO, " -> PROBLEM {0}", ex.toString()); } catch (Exception ex) { LOG.log(Level.INFO, " -> PROBLEM {0}", ex.toString()); } @@ -70,7 +76,7 @@ public void testAllSqlsParseDeparse() throws IOException { } LOG.log(Level.INFO, "tested {0} files. got {1} correct parse results", new Object[]{count, success}); - assertTrue(success >= 129); + assertTrue(success >= 130); } @Test @@ -89,4 +95,61 @@ public void testAllSqlsOnlyParse() throws IOException { } } } + + @Test + public void testOperatorsWithSpaces() throws Exception { + String sql; + Statement statement; + + // First, the regular way (normal for most databases). + sql = "SELECT\n" + + " Something\n" + + "FROM\n" + + " Sometable\n" + + "WHERE\n" + + " Somefield >= Somevalue\n" + + " AND Somefield <= Somevalue\n" + + " AND Somefield <> Somevalue\n" + + " AND Somefield != Somevalue\n"; + + statement = CCJSqlParserUtil.parse(sql); + + System.out.println(statement.toString()); + + assertSqlCanBeParsedAndDeparsed(sql, true); + + // Second, the special crap Oracle lets you get away with. + sql = "SELECT\n" + + " Something\n" + + "FROM\n" + + " Sometable\n" + + "WHERE\n" + + " Somefield > = Somevalue\n" + + " AND Somefield < = Somevalue\n" + + " AND Somefield < > Somevalue\n"; + + // Note, we do not (currently) test the "!=" with spaces in between -- Postgresql deals with this as two operators, "factorial" and "equals". + + statement = CCJSqlParserUtil.parse(sql); + + System.out.println(statement.toString()); + + assertSqlCanBeParsedAndDeparsed(sql, true); + + // And then with multiple whitespace + sql = "SELECT\n" + + " Something\n" + + "FROM\n" + + " Sometable\n" + + "WHERE\n" + + " Somefield > \t = Somevalue\n" + + " AND Somefield < = Somevalue\n" + + " AND Somefield <\t\t> Somevalue\n"; + + statement = CCJSqlParserUtil.parse(sql); + + System.out.println(statement.toString()); + + assertSqlCanBeParsedAndDeparsed(sql, true); + } }