You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2014/03/01 09:31:43 UTC

svn commit: r1573131 - in /cayenne/main/trunk/cayenne-server/src: main/java/org/apache/cayenne/exp/parser/ test/java/org/apache/cayenne/exp/parser/

Author: aadamchik
Date: Sat Mar  1 08:31:43 2014
New Revision: 1573131

URL: http://svn.apache.org/r1573131
Log:
CAY-1877 In-memory evaluation of expression may fail with UnsupportedOpeartionException depending on order of nodes

Modified:
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
    cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java
    cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/EvaluatorTest.java
    cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java Sat Mar  1 08:31:43 2014
@@ -57,7 +57,17 @@ public class ASTBetween extends Conditio
         Object o3 = evaluateChild(2, o);
         Evaluator e = Evaluator.evaluator(o1);
 
-        return e.compare(o1, o2) >= 0 && e.compare(o1, o3) <= 0 ? Boolean.TRUE : Boolean.FALSE;
+        Integer c1 = e.compare(o1, o2);
+        if (c1 == null) {
+            return Boolean.FALSE;
+        }
+
+        Integer c2 = e.compare(o1, o3);
+        if (c2 == null) {
+            return Boolean.FALSE;
+        }
+
+        return c1 >= 0 && c2 <= 0 ? Boolean.TRUE : Boolean.FALSE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java Sat Mar  1 08:31:43 2014
@@ -53,9 +53,9 @@ public class ASTGreater extends Conditio
 
         Object o1 = evaluateChild(0, o);
         Object o2 = evaluateChild(1, o);
-        int diff = Evaluator.evaluator(o1).compare(o1, o2);
+        Integer c = Evaluator.evaluator(o1).compare(o1, o2);
 
-        return diff > 0 ? Boolean.TRUE : Boolean.FALSE;
+        return c != null && c > 0 ? Boolean.TRUE : Boolean.FALSE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java Sat Mar  1 08:31:43 2014
@@ -55,9 +55,9 @@ public class ASTGreaterOrEqual extends C
 
         Object o1 = evaluateChild(0, o);
         Object o2 = evaluateChild(1, o);
-        int diff = Evaluator.evaluator(o1).compare(o1, o2);
+        Integer c = Evaluator.evaluator(o1).compare(o1, o2);
 
-        return diff >= 0 ? Boolean.TRUE : Boolean.FALSE;
+        return c != null && c >= 0 ? Boolean.TRUE : Boolean.FALSE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java Sat Mar  1 08:31:43 2014
@@ -56,6 +56,8 @@ public class ASTIn extends ConditionNode
         }
 
         Object o1 = evaluateChild(0, o);
+        // TODO: what if there's a NULL inside IN list? 
+        // e.g. ASTEqual evals as "NULL == NULL"
         if (o1 == null) {
             return Boolean.FALSE;
         }

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java Sat Mar  1 08:31:43 2014
@@ -54,9 +54,9 @@ public class ASTLess extends ConditionNo
 
         Object o1 = evaluateChild(0, o);
         Object o2 = evaluateChild(1, o);
-        int diff = Evaluator.evaluator(o1).compare(o1, o2);
+        Integer c = Evaluator.evaluator(o1).compare(o1, o2);
 
-        return diff < 0 ? Boolean.TRUE : Boolean.FALSE;
+        return c != null && c < 0 ? Boolean.TRUE : Boolean.FALSE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java Sat Mar  1 08:31:43 2014
@@ -55,9 +55,9 @@ public class ASTLessOrEqual extends Cond
 
         Object o1 = evaluateChild(0, o);
         Object o2 = evaluateChild(1, o);
-        int diff = Evaluator.evaluator(o1).compare(o1, o2);
+        Integer c = Evaluator.evaluator(o1).compare(o1, o2);
 
-        return diff <= 0 ? Boolean.TRUE : Boolean.FALSE;
+        return c != null && c <= 0 ? Boolean.TRUE : Boolean.FALSE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java Sat Mar  1 08:31:43 2014
@@ -53,8 +53,18 @@ public class ASTNotBetween extends Condi
         Object o2 = evaluateChild(1, o);
         Object o3 = evaluateChild(2, o);
         Evaluator e = Evaluator.evaluator(o1);
+        
+        Integer c1 = e.compare(o1, o2);
+        if (c1 == null) {
+            return Boolean.FALSE;
+        }
 
-        return e.compare(o1, o2) >= 0 && e.compare(o1, o3) <= 0 ? Boolean.FALSE : Boolean.TRUE;
+        Integer c2 = e.compare(o1, o3);
+        if (c2 == null) {
+            return Boolean.FALSE;
+        }
+
+        return c1 >= 0 && c2 <= 0 ? Boolean.FALSE : Boolean.TRUE;
     }
 
     /**

Modified: cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java (original)
+++ cayenne/main/trunk/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java Sat Mar  1 08:31:43 2014
@@ -55,7 +55,7 @@ abstract class Evaluator {
         }
 
         @Override
-        int compare(Object lhs, Object rhs) {
+        Integer compare(Object lhs, Object rhs) {
 
             if (rhs == null) {
                 return 1;
@@ -75,8 +75,8 @@ abstract class Evaluator {
 
         NULL_LHS_EVALUATOR = new Evaluator() {
             @Override
-            int compare(Object lhs, Object rhs) {
-                throw new UnsupportedOperationException("Unsupported");
+            Integer compare(Object lhs, Object rhs) {
+                return null;
             }
 
             @Override
@@ -92,16 +92,16 @@ abstract class Evaluator {
             }
 
             @Override
-            int compare(Object lhs, Object rhs) {
-                throw new UnsupportedOperationException("Unsupported");
+            Integer compare(Object lhs, Object rhs) {
+                return null;
             }
         });
 
         PERSISTENT_EVALUATOR = new NonNullLhsEvaluator(new Evaluator() {
 
             @Override
-            int compare(Object lhs, Object rhs) {
-                throw new UnsupportedOperationException("Unsupported");
+            Integer compare(Object lhs, Object rhs) {
+                return null;
             }
 
             @Override
@@ -145,7 +145,7 @@ abstract class Evaluator {
         BIG_DECIMAL_EVALUATOR = new NonNullLhsEvaluator(new Evaluator() {
 
             @Override
-            int compare(Object lhs, Object rhs) {
+            Integer compare(Object lhs, Object rhs) {
                 return ((BigDecimal) lhs).compareTo(ConversionUtil.toBigDecimal(rhs));
             }
 
@@ -155,7 +155,8 @@ abstract class Evaluator {
                 // BigDecimals must be compared using compareTo (
                 // see CAY-280 and BigDecimal.equals JavaDoc)
 
-                return compare(lhs, rhs) == 0;
+                Integer c = compare(lhs, rhs);
+                return c != null && c == 0;
             }
         });
 
@@ -163,7 +164,7 @@ abstract class Evaluator {
 
             @SuppressWarnings({ "unchecked", "rawtypes" })
             @Override
-            int compare(Object lhs, Object rhs) {
+            Integer compare(Object lhs, Object rhs) {
                 return ((Comparable) lhs).compareTo(ConversionUtil.toComparable(rhs));
             }
 
@@ -229,5 +230,10 @@ abstract class Evaluator {
 
     abstract boolean eq(Object lhs, Object rhs);
 
-    abstract int compare(Object lhs, Object rhs);
+    /**
+     * Returns NULL if comparison is invalid, otherwise returns positive,
+     * negative or zero, with the same meaning as
+     * {@link Comparable#compareTo(Object)}.
+     */
+    abstract Integer compare(Object lhs, Object rhs);
 }

Modified: cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/EvaluatorTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/EvaluatorTest.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/EvaluatorTest.java (original)
+++ cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/EvaluatorTest.java Sat Mar  1 08:31:43 2014
@@ -64,7 +64,8 @@ public class EvaluatorTest extends TestC
         assertTrue(e.eq(lhs, new BigDecimal("1.1")));
         assertFalse(e.eq(lhs, new BigDecimal("1.10001")));
 
-        assertEquals(-1, e.compare(lhs, new BigDecimal("1.10001")));
+        Integer c = e.compare(lhs, new BigDecimal("1.10001"));
+        assertEquals(-1, c.intValue());
     }
 
     public void testEvaluator_Persistent() {

Modified: cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java?rev=1573131&r1=1573130&r2=1573131&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java (original)
+++ cayenne/main/trunk/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java Sat Mar  1 08:31:43 2014
@@ -162,26 +162,30 @@ public class ExpressionEvaluateInMemoryT
         assertTrue("Failed: " + notEqualTo, notEqualTo.match(noMatch));
     }
 
-    public void testEvaluateEQUAL_TONull() throws Exception {
-        Expression equalTo = new ASTEqual(new ASTObjPath("artistName"), null);
+    public void testEvaluateEQUAL_TO_Null() throws Exception {
+        Expression equalToNull = new ASTEqual(new ASTObjPath("artistName"), null);
+        Expression equalToNotNull = new ASTEqual(new ASTObjPath("artistName"), "abc");
 
         Artist match = new Artist();
-        assertTrue(equalTo.match(match));
+        assertTrue(equalToNull.match(match));
+        assertFalse(equalToNotNull.match(match));
 
         Artist noMatch = new Artist();
-        noMatch.setArtistName("123");
-        assertFalse("Failed: " + equalTo, equalTo.match(noMatch));
+        noMatch.setArtistName("abc");
+        assertFalse(equalToNull.match(noMatch));
     }
 
     public void testEvaluateNOT_EQUAL_TONull() throws Exception {
-        Expression equalTo = new ASTNotEqual(new ASTObjPath("artistName"), null);
+        Expression notEqualToNull = new ASTNotEqual(new ASTObjPath("artistName"), null);
+        Expression notEqualToNotNull = new ASTNotEqual(new ASTObjPath("artistName"), "abc");
 
         Artist match = new Artist();
-        assertFalse(equalTo.match(match));
+        assertFalse(notEqualToNull.match(match));
+        assertTrue(notEqualToNotNull.match(match));
 
         Artist noMatch = new Artist();
         noMatch.setArtistName("123");
-        assertTrue("Failed: " + equalTo, equalTo.match(noMatch));
+        assertTrue("Failed: " + notEqualToNull, notEqualToNull.match(noMatch));
     }
 
     public void testEvaluateEQUAL_TODataObject() throws Exception {
@@ -303,6 +307,15 @@ public class ExpressionEvaluateInMemoryT
         match.setEstimatedPrice(new BigDecimal(9999));
         assertTrue("Failed: " + e, e.match(match));
     }
+    
+    public void testEvaluateLESS_THAN_Null() throws Exception {
+        Expression ltNull = new ASTLess(new ASTObjPath("estimatedPrice"), null);
+        Expression ltNotNull = new ASTLess(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
+
+        Painting noMatch = new Painting();
+        assertFalse(ltNull.match(noMatch));
+        assertFalse(ltNotNull.match(noMatch));
+    }
 
     public void testEvaluateLESS_THAN_EQUAL_TO() throws Exception {
         Expression e = new ASTLessOrEqual(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
@@ -319,6 +332,15 @@ public class ExpressionEvaluateInMemoryT
         match.setEstimatedPrice(new BigDecimal(9999));
         assertTrue("Failed: " + e, e.match(match));
     }
+    
+    public void testEvaluateLESS_THAN_EQUAL_TO_Null() throws Exception {
+        Expression ltNull = new ASTLessOrEqual(new ASTObjPath("estimatedPrice"), null);
+        Expression ltNotNull = new ASTLessOrEqual(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
+
+        Painting noMatch = new Painting();
+        assertFalse(ltNull.match(noMatch));
+        assertFalse(ltNotNull.match(noMatch));
+    }
 
     public void testEvaluateGREATER_THAN() throws Exception {
         Expression e = new ASTGreater(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
@@ -335,6 +357,15 @@ public class ExpressionEvaluateInMemoryT
         match.setEstimatedPrice(new BigDecimal(10001));
         assertTrue("Failed: " + e, e.match(match));
     }
+    
+    public void testEvaluateGREATER_THAN_Null() throws Exception {
+        Expression gtNull = new ASTGreater(new ASTObjPath("estimatedPrice"), null);
+        Expression gtNotNull = new ASTGreater(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
+
+        Painting noMatch = new Painting();
+        assertFalse(gtNull.match(noMatch));
+        assertFalse(gtNotNull.match(noMatch));
+    }
 
     public void testEvaluateGREATER_THAN_EQUAL_TO() throws Exception {
         Expression e = new ASTGreaterOrEqual(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
@@ -351,6 +382,15 @@ public class ExpressionEvaluateInMemoryT
         match.setEstimatedPrice(new BigDecimal(10001));
         assertTrue("Failed: " + e, e.match(match));
     }
+    
+    public void testEvaluateGREATER_THAN_EQUAL_TO_Null() throws Exception {
+        Expression gtNull = new ASTGreaterOrEqual(new ASTObjPath("estimatedPrice"), null);
+        Expression gtNotNull = new ASTGreaterOrEqual(new ASTObjPath("estimatedPrice"), new BigDecimal(10000d));
+
+        Painting noMatch = new Painting();
+        assertFalse(gtNull.match(noMatch));
+        assertFalse(gtNotNull.match(noMatch));
+    }
 
     public void testEvaluateBETWEEN() throws Exception {
         // evaluate both BETWEEN and NOT_BETWEEN
@@ -378,6 +418,16 @@ public class ExpressionEvaluateInMemoryT
         assertTrue("Failed: " + between, between.match(match3));
         assertFalse("Failed: " + notBetween, notBetween.match(match3));
     }
+    
+    public void testEvaluateBETWEEN_Null() throws Exception {
+        Expression btNull = new ASTBetween(new ASTObjPath("estimatedPrice"), new BigDecimal(10d), new BigDecimal(20d));
+        Expression btNotNull = new ASTNotBetween(new ASTObjPath("estimatedPrice"), new BigDecimal(10d),
+                new BigDecimal(20d));
+
+        Painting noMatch = new Painting();
+        assertFalse(btNull.match(noMatch));
+        assertFalse(btNotNull.match(noMatch));
+    }
 
     public void testEvaluateIN() throws Exception {
         Expression in = new ASTIn(new ASTObjPath("estimatedPrice"), new ASTList(new Object[] { new BigDecimal("10"),
@@ -406,6 +456,17 @@ public class ExpressionEvaluateInMemoryT
         assertTrue("Failed: " + in, in.match(match2));
         assertFalse("Failed: " + notIn, notIn.match(match2));
     }
+    
+    public void testEvaluateIN_Null() throws Exception {
+        Expression in = new ASTIn(new ASTObjPath("estimatedPrice"), new ASTList(new Object[] {
+                new BigDecimal("10"), new BigDecimal("20") }));
+        Expression notIn = new ASTNotIn(new ASTObjPath("estimatedPrice"), new ASTList(new Object[] {
+                new BigDecimal("10"), new BigDecimal("20") }));
+
+        Painting noMatch = new Painting();
+        assertFalse(in.match(noMatch));
+        assertFalse(notIn.match(noMatch));
+    }
 
     public void testEvaluateLIKE1() throws Exception {
         Expression like = new ASTLike(new ASTObjPath("artistName"), "abc%d");