DATAJDBC-410 - Properly render NOT IN clauses.

See also: https://github.com/spring-projects/spring-data-r2dbc/issues/177
Original pull request: #167.
This commit is contained in:
Jens Schauder
2019-09-11 13:06:50 +02:00
committed by Mark Paluch
parent 045c894d2e
commit 85bf1e6dc7
5 changed files with 166 additions and 5 deletions

View File

@@ -193,6 +193,26 @@ public class Column extends AbstractSegment implements Expression, Named {
return Conditions.in(this, subselect);
}
/**
* Creates a new {@code not} {@link In} {@link Condition} given right {@link Expression}s.
*
* @param expression right side of the comparison.
* @return the {@link In} condition.
*/
public In notIn(Expression... expression) {
return Conditions.notIn(this, expression);
}
/**
* Creates a new {@code not} {@link In} {@link Condition} given a subselects.
*
* @param subselect right side of the comparison.
* @return the {@link In} condition.
*/
public In notIn(Select subselect) {
return Conditions.notIn(this, subselect);
}
/**
* Creates a {@code IS NULL} condition.
*

View File

@@ -192,6 +192,66 @@ public abstract class Conditions {
return in(column, new SubselectExpression(subselect));
}
/**
* Creates a {@code NOT IN} {@link Condition clause}.
*
* @param columnOrExpression left side of the comparison.
* @param arg IN argument.
* @return the {@link In} condition.
*/
public static In notIn(Expression columnOrExpression, Expression arg) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(arg, "Expression argument must not be null");
return In.create(columnOrExpression, arg);
}
/**
* Creates a new {@code NOT IN} {@link Condition} given left and right {@link Expression}s.
*
* @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}.
* @param expressions right hand side (collection {@link Expression}) must not be {@literal null}.
* @return the {@link In} {@link Condition}.
*/
public static Condition notIn(Expression columnOrExpression, Collection<? extends Expression> expressions) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return In.createNotIn(columnOrExpression, new ArrayList<>(expressions));
}
/**
* Creates a new {@code NOT IN} {@link Condition} given left and right {@link Expression}s.
*
* @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}.
* @param expressions right hand side (collection {@link Expression}) must not be {@literal null}.
* @return the {@link In} {@link Condition}.
*/
public static In notIn(Expression columnOrExpression, Expression... expressions) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return In.createNotIn(columnOrExpression, Arrays.asList(expressions));
}
/**
* Creates a {@code NOT IN} {@link Condition clause} for a {@link Select subselect}.
*
* @param column the column to compare.
* @param subselect the subselect.
* @return the {@link In} condition.
*/
public static In notIn(Column column, Select subselect) {
Assert.notNull(column, "Column must not be null");
Assert.notNull(subselect, "Subselect must not be null");
return notIn(column, new SubselectExpression(subselect));
}
static class ConstantCondition extends AbstractSegment implements Condition {
private final String condition;

View File

@@ -34,13 +34,15 @@ public class In extends AbstractSegment implements Condition {
private final Expression left;
private final Collection<Expression> expressions;
private final boolean notIn;
private In(Expression left, Collection<Expression> expressions) {
private In(Expression left, Collection<Expression> expressions, boolean notIn) {
super(toArray(left, expressions));
this.left = left;
this.expressions = expressions;
this.notIn = notIn;
}
private static Segment[] toArray(Expression expression, Collection<Expression> expressions) {
@@ -69,7 +71,7 @@ public class In extends AbstractSegment implements Condition {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(arg, "Expression argument must not be null");
return new In(columnOrExpression, Collections.singletonList(arg));
return new In(columnOrExpression, Collections.singletonList(arg), false);
}
/**
@@ -84,7 +86,7 @@ public class In extends AbstractSegment implements Condition {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return new In(columnOrExpression, new ArrayList<>(expressions));
return new In(columnOrExpression, new ArrayList<>(expressions), false);
}
/**
@@ -99,7 +101,58 @@ public class In extends AbstractSegment implements Condition {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return new In(columnOrExpression, Arrays.asList(expressions));
return new In(columnOrExpression, Arrays.asList(expressions), false);
}
/**
* Creates a new {@link In} {@link Condition} given left and right {@link Expression}s.
*
* @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}.
* @param arg right hand side (collection {@link Expression}) must not be {@literal null}.
* @return the {@link In} {@link Condition}.
*/
public static In createNotIn(Expression columnOrExpression, Expression arg) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(arg, "Expression argument must not be null");
return new In(columnOrExpression, Collections.singletonList(arg), true);
}
/**
* Creates a new {@link In} {@link Condition} given left and right {@link Expression}s.
*
* @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}.
* @param expressions right hand side (collection {@link Expression}) must not be {@literal null}.
* @return the {@link In} {@link Condition}.
*/
public static In createNotIn(Expression columnOrExpression, Collection<? extends Expression> expressions) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return new In(columnOrExpression, new ArrayList<>(expressions), true);
}
/**
* Creates a new {@link In} {@link Condition} given left and right {@link Expression}s.
*
* @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}.
* @param expressions right hand side (collection {@link Expression}) must not be {@literal null}.
* @return the {@link In} {@link Condition}.
*/
public static In createNotIn(Expression columnOrExpression, Expression... expressions) {
Assert.notNull(columnOrExpression, "Comparison column or expression must not be null");
Assert.notNull(expressions, "Expression argument must not be null");
return new In(columnOrExpression, Arrays.asList(expressions), true);
}
@Override
public Condition not() {
return new In(left, expressions, !notIn);
}
/*
@@ -108,6 +161,10 @@ public class In extends AbstractSegment implements Condition {
*/
@Override
public String toString() {
return left + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")";
return left + (notIn ? " NOT" : "") + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")";
}
public boolean isNotIn() {
return notIn;
}
}

View File

@@ -30,6 +30,7 @@ class InVisitor extends TypedSingleConditionRenderSupport<In> {
private final RenderTarget target;
private final StringBuilder part = new StringBuilder();
private boolean needsComma = false;
private boolean notIn = false;
InVisitor(RenderContext context, RenderTarget target) {
super(context);
@@ -52,6 +53,9 @@ class InVisitor extends TypedSingleConditionRenderSupport<In> {
if (part.length() == 0) {
part.append(renderedPart);
if (notIn) {
part.append(" NOT");
}
part.append(" IN (");
} else {
part.append(renderedPart);
@@ -62,6 +66,14 @@ class InVisitor extends TypedSingleConditionRenderSupport<In> {
return super.leaveNested(segment);
}
@Override
Delegation enterMatched(In segment) {
notIn = segment.isNotIn();
return super.enterMatched(segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)

View File

@@ -126,4 +126,16 @@ public class ConditionRendererUnitTests {
assertThat(sql).endsWith("WHERE my_table.left IS NOT NULL");
}
@Test // DATAJDBC-410
public void shouldRenderNotIn() {
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.in(right).not()).build());
assertThat(sql).endsWith("WHERE my_table.left NOT IN (my_table.right)");
sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.notIn(right)).build());
assertThat(sql).endsWith("WHERE my_table.left NOT IN (my_table.right)");
}
}