diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java index f01578ec42..74f7c2756c 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java @@ -214,6 +214,11 @@ private Node expressionNodeToSqlNode(Expression node, Expression parentNode) { case GREATER_THAN_EQUAL_TO: return new OpExpressionNode(expToStr(node.getType())); case NEGATIVE: + // If operand is a scalar null, produce NULL directly (no unary minus) + if (node.getOperandCount() > 0 && node.getOperand(0) == null) { + expressionsToSkip.add(node); + return new ValueNode(null, false, null, false); + } // we need to add minus sign as a prefix, not a separator return new FunctionNode(expToStr(node.getType()), null, false); case TRUE: diff --git a/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorTest.java b/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorTest.java index efe89d619c..4b6ef59a3b 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorTest.java @@ -34,6 +34,7 @@ import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.hamcrest.CoreMatchers.instanceOf; @@ -158,6 +159,39 @@ public void translateBetween() { } } + @Test + public void translateNegate_NullScalar() { + Node node = translate("-(null)"); + assertThat(node, instanceOf(ValueNode.class)); + assertNull(((ValueNode) node).getValue()); + + SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable()); + node.visit(visitor); + assertEquals(" NULL", visitor.getSQLString()); + } + + @Test + public void translateNegate_NumberScalar() { + Node node = translate("-1"); + assertThat(node, instanceOf(FunctionNode.class)); + + SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable()); + node.visit(visitor); + assertEquals(" - 1", visitor.getSQLString()); + } + + @Test + public void translateNegate_NullParam() { + Expression exp = ExpressionFactory.exp("-$v").params(Collections.singletonMap("v", null)); + Node node = translator.translate(exp); + assertThat(node, instanceOf(ValueNode.class)); + assertNull(((ValueNode) node).getValue()); + + SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable()); + node.visit(visitor); + assertEquals(" NULL", visitor.getSQLString()); + } + @Test public void translateNot() { Node not = translate("not true"); @@ -587,4 +621,4 @@ private Node translate(String s) { return translator.translate(ExpressionFactory.exp(s)); } -} \ No newline at end of file +} diff --git a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNegateTest.java b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNegateTest.java index 9e692e5cca..9e7e01faf7 100644 --- a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNegateTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNegateTest.java @@ -38,4 +38,12 @@ public void testParse() { assertEquals("-1", exp.toString()); } -} \ No newline at end of file + @Test + public void testEvaluate_null() { + ASTNegate negate = new ASTNegate(); + negate.setOperand(0, new ASTScalar(null)); + + Object result = negate.evaluate(null); + assertNull(result); + } +}