Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exp: regression in processing (not)in with empty list #682

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Release 5.0-M21

* #681 Exp: regression in processing (not)in with empty list

## Release 5.0-M20

* #651 Exp: numeric scalars to support underscore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ public void parseIn() {

Expression e3 = parser.parse(Exp.in("a", 5, 6, 7));
assertEquals(ExpressionFactory.exp("a in (5, 6, 7)"), e3);

Expression e4 = parser.parse(Exp.in("a"));
assertEquals(ExpressionFactory.exp("false"), e4);
}

@Test
public void parseNotIn() {
Expression e1 = parser.parse(Exp.notIn("a", 5, 6, 7));
assertEquals(ExpressionFactory.exp("a not in (5, 6, 7)"), e1);

Expression e2 = parser.parse(Exp.notIn("a", "x", "y", "z"));
assertEquals(ExpressionFactory.exp("a not in ('x','y','z')"), e2);

Expression e3 = parser.parse(Exp.notIn("a", 5, 6, 7));
assertEquals(ExpressionFactory.exp("a not in (5, 6, 7)"), e3);

Expression e4 = parser.parse(Exp.notIn("a"));
assertEquals(ExpressionFactory.exp("true"), e4);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

class NamedParamTransformer implements Function<Object, Object> {

Expand All @@ -17,7 +18,8 @@ class NamedParamTransformer implements Function<Object, Object> {
public Object apply(Object object) {

if (!(object instanceof ExpNamedParameter)) {
return object;
// after parameters are resolved, we may need to shake down the tree a bit
return optimize(object);
}

String name = ((ExpNamedParameter) object).getName();
Expand All @@ -31,4 +33,43 @@ public Object apply(Object object) {
return value != null ? SimpleNode.wrapParameterValue(value) : new ExpScalar(null);
}
}

private Object optimize(Object object) {
if(object instanceof SimpleNode) {
return ((SimpleNode) object).jjtAccept(new OptimizationVisitor(), null);
}
return object;
}

static class OptimizationVisitor extends AgExpressionParserDefaultVisitor<SimpleNode> {

@Override
public SimpleNode defaultVisit(SimpleNode node, SimpleNode data) {
// note, we do not go down to children, just process this node and that's it
return node;
}

@Override
public SimpleNode visit(ExpIn node, SimpleNode data) {
return optimizeIn(node, ExpFalse::new);
}

@Override
public SimpleNode visit(ExpNotIn node, SimpleNode data) {
return optimizeIn(node, ExpTrue::new);
}

private static SimpleNode optimizeIn(SimpleNode node, Supplier<SimpleNode> supplier) {
if(node.jjtGetNumChildren() < 2) {
return node;
}
Node child = node.jjtGetChild(1);
if(child instanceof ExpScalarList) {
if(((ExpScalarList) child).getValue().isEmpty()) {
return supplier.get();
}
}
return node;
}
}
}
10 changes: 8 additions & 2 deletions agrest-engine/src/main/java/io/agrest/protocol/Exp.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,20 @@ static Exp notBetween(String left, Object right1, Object right2) {
/**
* @since 5.0
*/
static Exp in(String path, Object... values) {
return ExpUtils.composeBinary(new ExpIn(), path(path), ExpUtils.scalarArray(values));
static Exp in(String path, Object... scalars) {
if(scalars.length == 0){
return new ExpFalse();
}
return ExpUtils.composeBinary(new ExpIn(), path(path), ExpUtils.scalarArray(scalars));
}

/**
* @since 5.0
*/
static Exp notIn(String path, Object... scalars) {
if(scalars.length == 0){
return new ExpTrue();
}
return ExpUtils.composeBinary(new ExpNotIn(), path(path), ExpUtils.scalarArray(scalars));
}

Expand Down
13 changes: 13 additions & 0 deletions agrest-engine/src/test/java/io/agrest/exp/parser/ExpInTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -53,6 +54,18 @@ public void parseInvalidGrammar(String expString) {
assertThrows(AgException.class, () -> Exp.parse(expString));
}

@Test
public void emptyIn() {
Exp exp = Exp.parse("a in $a").namedParams(Map.of("a", List.of()));
assertEquals("false", exp.toString());
}

@Test
public void emptyNotIn() {
Exp exp = Exp.parse("a not in $a").namedParams(Map.of("a", List.of()));
assertEquals("true", exp.toString());
}

@Test
public void parameterizedToString() {
assertEquals("a in ('x', 'y')", Exp.parse("a in $l").positionalParams(List.of("x", "y")).toString());
Expand Down
Loading