This is an automated email from the ASF dual-hosted git repository.

dakirily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git


The following commit(s) were added to refs/heads/main by this push:
     new 953e3d6786 QPID-8761: [Broker-J] QueryEvaluationException: Objects of 
types 'null' and 'String' can not be compared (#448)
953e3d6786 is described below

commit 953e3d67862a6a1e5f96a710996ab3eaad5eec5f
Author: Daniil Kirilyuk <[email protected]>
AuthorDate: Sun Sep 20 03:31:05 2026 +0200

    QPID-8761: [Broker-J] QueryEvaluationException: Objects of types 'null' and 
'String' can not be compared (#448)
---
 broker-plugins/query-engine/pom.xml                |  6 +++
 .../comparison/AbstractComparisonExpression.java   | 13 +++++
 .../expression/comparison/EqualExpression.java     | 10 ++--
 .../comparison/GreaterThanExpression.java          |  2 +-
 .../comparison/GreaterThanOrEqualExpression.java   |  2 +-
 .../expression/comparison/LessThanExpression.java  |  2 +-
 .../comparison/LessThanOrEqualExpression.java      |  2 +-
 .../expression/comparison/EqualExpressionTest.java | 55 ++++++++++++----------
 .../comparison/GreaterThanExpressionTest.java      | 45 +++++++++---------
 .../GreaterThanOrEqualExpressionTest.java          | 45 +++++++++---------
 .../comparison/LessThanExpressionTest.java         | 45 +++++++++---------
 .../comparison/LessThanOrEqualExpressionTest.java  | 45 +++++++++---------
 12 files changed, 143 insertions(+), 129 deletions(-)

diff --git a/broker-plugins/query-engine/pom.xml 
b/broker-plugins/query-engine/pom.xml
index 4bb8356769..902ae45a86 100644
--- a/broker-plugins/query-engine/pom.xml
+++ b/broker-plugins/query-engine/pom.xml
@@ -52,6 +52,12 @@
       <artifactId>qpid-broker-plugins-access-control</artifactId>
     </dependency>
 
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <scope>test</scope>
+    </dependency>
+
   </dependencies>
 
   <build>
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/AbstractComparisonExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/AbstractComparisonExpression.java
index 21aefb4696..0ca3745025 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/AbstractComparisonExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/AbstractComparisonExpression.java
@@ -86,6 +86,19 @@ public abstract class AbstractComparisonExpression<T, R> 
extends AbstractExpress
         super(children);
     }
 
+    /**
+     * Returns true if one of the operands is null and false otherwise
+     *
+     * @param left Left operand
+     * @param right Right operand
+     *
+     * @return true if one of the operands is null and false otherwise
+     */
+    protected boolean hasNullOperand(final Object left, final Object right)
+    {
+        return left == null || right == null;
+    }
+
     /**
      * Evaluates value to a boolean result
      *
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpression.java
index f13900f20e..ea8f6ed475 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpression.java
@@ -84,6 +84,11 @@ public class EqualExpression<T, R> extends 
AbstractComparisonExpression<T, Boole
             return Boolean.FALSE;
         }
 
+        if (hasNullOperand(left, right))
+        {
+            return left == right;
+        }
+
         if (!_typeValidator.test(left) || !_typeValidator.test(right))
         {
             throw QueryEvaluationException.of(
@@ -93,11 +98,6 @@ public class EqualExpression<T, R> extends 
AbstractComparisonExpression<T, Boole
             );
         }
 
-        if ((left == null) ^ (right == null))
-        {
-            return Boolean.FALSE;
-        }
-
         if ((left == right) || Objects.equals(left, right))
         {
             return Boolean.TRUE;
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpression.java
index 11f558686b..04d24014af 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpression.java
@@ -78,7 +78,7 @@ public class GreaterThanExpression<T, R> extends 
AbstractComparisonExpression<T,
         final R left = evaluateChild(0, value);
         final R right = evaluateChild(1, value);
 
-        if (right instanceof EmptySetExpression)
+        if (right instanceof EmptySetExpression || hasNullOperand(left, right))
         {
             return Boolean.FALSE;
         }
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpression.java
index 1128e00678..bb94945313 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpression.java
@@ -79,7 +79,7 @@ public class GreaterThanOrEqualExpression<T, R> extends 
AbstractComparisonExpres
         final R left = evaluateChild(0, value);
         final R right = evaluateChild(1, value);
 
-        if (right instanceof EmptySetExpression)
+        if (right instanceof EmptySetExpression || hasNullOperand(left, right))
         {
             return Boolean.FALSE;
         }
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpression.java
index 0ab403ecb1..c3bf6f1999 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpression.java
@@ -79,7 +79,7 @@ public class LessThanExpression<T, R> extends 
AbstractComparisonExpression<T, Bo
         final R left = evaluateChild(0, value);
         final R right = evaluateChild(1, value);
 
-        if (right instanceof EmptySetExpression)
+        if (right instanceof EmptySetExpression || hasNullOperand(left, right))
         {
             return Boolean.FALSE;
         }
diff --git 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpression.java
 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpression.java
index 341b6ebfd0..2d894e22b6 100644
--- 
a/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpression.java
+++ 
b/broker-plugins/query-engine/src/main/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpression.java
@@ -78,7 +78,7 @@ public class LessThanOrEqualExpression<T, R> extends 
AbstractComparisonExpressio
         final R left = evaluateChild(0, value);
         final R right = evaluateChild(1, value);
 
-        if (right instanceof EmptySetExpression)
+        if (right instanceof EmptySetExpression || hasNullOperand(left, right))
         {
             return Boolean.FALSE;
         }
diff --git 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpressionTest.java
 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpressionTest.java
index ac917ab1dd..6cb09add45 100644
--- 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpressionTest.java
+++ 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/EqualExpressionTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 import org.apache.qpid.server.query.engine.TestBroker;
 import org.apache.qpid.server.query.engine.exception.QueryEvaluationException;
@@ -222,32 +224,37 @@ public class EqualExpressionTest
         }
     }
 
-    @Test()
-    public void comparingNulls()
+    @ParameterizedTest
+    @CsvSource(value =
     {
-        String query = "select 1 = null as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'Integer' and 'null' can not be 
compared", e.getMessage());
-        }
+        "1 = null, false",
+        "null = 1, false",
+        "'test' = null, false",
+        "null = 'test', false",
+        "(null + 1) = 1, false",
+        "1 = (null + 1), false",
+        "null = (select name from queue where 1 = 0), false",
+        "null = null, true"
+    }, quoteCharacter = '"')
+    public void comparingNulls(final String expression, final boolean expected)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select " + expression + " as result").getResults();
+        assertEquals(1, result.size(), expression);
+        assertEquals(expected, result.get(0).get("result"), expression);
+    }
 
-        query = "select null = 'test' as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'null' and 'String' can not be 
compared", e.getMessage());
-        }
+    @ParameterizedTest
+    @CsvSource(value =
+    {
+        "description = 'test description 2', 10",
+        "'test description 2' = description, 10",
+        "description = 'missing', 0"
+    }, quoteCharacter = '"')
+    public void filteringNullableFields(final String predicate, final int 
expected)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select count(*) as cnt from queue where " + 
predicate).getResults();
+        assertEquals(1, result.size(), predicate);
+        assertEquals(expected, result.get(0).get("cnt"), predicate);
     }
 
     @Test()
diff --git 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpressionTest.java
 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpressionTest.java
index 76b4a7f82a..a3f00f4b9e 100644
--- 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpressionTest.java
+++ 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanExpressionTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import org.apache.qpid.server.query.engine.TestBroker;
 import org.apache.qpid.server.query.engine.exception.QueryEvaluationException;
@@ -194,31 +196,26 @@ public class GreaterThanExpressionTest
         }
     }
 
-    @Test()
-    public void comparingNulls()
+    @ParameterizedTest
+    @ValueSource(strings =
     {
-        String query = "select 1 > null as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'Integer' and 'null' can not be 
compared", e.getMessage());
-        }
+        "1 > null", "null > 1", "'test' > null", "null > 'test'",
+        "null > null", "(null + 1) > 1", "1 > (null + 1)",
+        "null > (select name from queue where 1 = 0)"
+    })
+    public void comparingNulls(final String expression)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select " + expression + " as result").getResults();
+        assertEquals(1, result.size(), expression);
+        assertEquals(false, result.get(0).get("result"), expression);
+    }
 
-        query = "select null > 'test' as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'null' and 'String' can not be 
compared", e.getMessage());
-        }
+    @ParameterizedTest
+    @ValueSource(strings = { "description > 'test description 2'", "'test 
description 2' > description" })
+    public void filteringNullableFields(final String predicate)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select count(*) as cnt from queue where " + 
predicate).getResults();
+        assertEquals(1, result.size(), predicate);
+        assertEquals(10, result.get(0).get("cnt"), predicate);
     }
 }
diff --git 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpressionTest.java
 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpressionTest.java
index 3a82d0b351..dead008ea3 100644
--- 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpressionTest.java
+++ 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/GreaterThanOrEqualExpressionTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import org.apache.qpid.server.query.engine.TestBroker;
 import org.apache.qpid.server.query.engine.exception.QueryEvaluationException;
@@ -224,31 +226,26 @@ public class GreaterThanOrEqualExpressionTest
         }
     }
 
-    @Test()
-    public void comparingNulls()
+    @ParameterizedTest
+    @ValueSource(strings =
     {
-        String query = "select 1 >= null as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'Integer' and 'null' can not be 
compared", e.getMessage());
-        }
+        "1 >= null", "null >= 1", "'test' >= null", "null >= 'test'",
+        "null >= null", "(null + 1) >= 1", "1 >= (null + 1)",
+        "null >= (select name from queue where 1 = 0)"
+    })
+    public void comparingNulls(final String expression)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select " + expression + " as result").getResults();
+        assertEquals(1, result.size(), expression);
+        assertEquals(false, result.get(0).get("result"), expression);
+    }
 
-        query = "select null >= 'test' as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'null' and 'String' can not be 
compared", e.getMessage());
-        }
+    @ParameterizedTest
+    @ValueSource(strings = { "description >= 'test description 2'", "'test 
description 2' >= description" })
+    public void filteringNullableFields(final String predicate)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select count(*) as cnt from queue where " + 
predicate).getResults();
+        assertEquals(1, result.size(), predicate);
+        assertEquals(20, result.get(0).get("cnt"), predicate);
     }
 }
diff --git 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpressionTest.java
 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpressionTest.java
index f56d97d01a..69ae4c76ef 100644
--- 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpressionTest.java
+++ 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanExpressionTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import org.apache.qpid.server.query.engine.TestBroker;
 import org.apache.qpid.server.query.engine.exception.QueryEvaluationException;
@@ -194,31 +196,26 @@ public class LessThanExpressionTest
         }
     }
 
-    @Test()
-    public void comparingNulls()
+    @ParameterizedTest
+    @ValueSource(strings =
     {
-        String query = "select 1 < null as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'Integer' and 'null' can not be 
compared", e.getMessage());
-        }
+        "1 < null", "null < 1", "'test' < null", "null < 'test'",
+        "null < null", "(null + 1) < 1", "1 < (null + 1)",
+        "null < (select name from queue where 1 = 0)"
+    })
+    public void comparingNulls(final String expression)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select " + expression + " as result").getResults();
+        assertEquals(1, result.size(), expression);
+        assertEquals(false, result.get(0).get("result"), expression);
+    }
 
-        query = "select null < 'test' as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'null' and 'String' can not be 
compared", e.getMessage());
-        }
+    @ParameterizedTest
+    @ValueSource(strings = { "description < 'test description 2'", "'test 
description 2' < description" })
+    public void filteringNullableFields(final String predicate)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select count(*) as cnt from queue where " + 
predicate).getResults();
+        assertEquals(1, result.size(), predicate);
+        assertEquals(10, result.get(0).get("cnt"), predicate);
     }
 }
diff --git 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpressionTest.java
 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpressionTest.java
index 8d9b08cc5d..588cceedd4 100644
--- 
a/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpressionTest.java
+++ 
b/broker-plugins/query-engine/src/test/java/org/apache/qpid/server/query/engine/parsing/expression/comparison/LessThanOrEqualExpressionTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import org.apache.qpid.server.query.engine.TestBroker;
 import org.apache.qpid.server.query.engine.exception.QueryEvaluationException;
@@ -264,31 +266,26 @@ public class LessThanOrEqualExpressionTest
         }
     }
 
-    @Test()
-    public void comparingNulls()
+    @ParameterizedTest
+    @ValueSource(strings =
     {
-        String query = "select 1 <= null as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'Integer' and 'null' can not be 
compared", e.getMessage());
-        }
+        "1 <= null", "null <= 1", "'test' <= null", "null <= 'test'",
+        "null <= null", "(null + 1) <= 1", "1 <= (null + 1)",
+        "null <= (select name from queue where 1 = 0)"
+    })
+    public void comparingNulls(final String expression)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select " + expression + " as result").getResults();
+        assertEquals(1, result.size(), expression);
+        assertEquals(false, result.get(0).get("result"), expression);
+    }
 
-        query = "select null <= 'test' as result from queue";
-        try
-        {
-            _queryEvaluator.execute(query);
-            fail("Expected exception not thrown");
-        }
-        catch (Exception e)
-        {
-            assertEquals(QueryEvaluationException.class, e.getClass());
-            assertEquals("Objects of types 'null' and 'String' can not be 
compared", e.getMessage());
-        }
+    @ParameterizedTest
+    @ValueSource(strings = { "description <= 'test description 2'", "'test 
description 2' <= description" })
+    public void filteringNullableFields(final String predicate)
+    {
+        final List<Map<String, Object>> result = 
_queryEvaluator.execute("select count(*) as cnt from queue where " + 
predicate).getResults();
+        assertEquals(1, result.size(), predicate);
+        assertEquals(20, result.get(0).get("cnt"), predicate);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to