From a8034ab226d3ab6548289bffe50f606ff03e8bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Sat, 1 Nov 2014 12:53:33 +0200 Subject: [PATCH] Partition lists when needed --- .../mysema/query/types/ExpressionUtils.java | 31 ++++++++++++++-- .../com/mysema/query/sql/OracleTemplates.java | 1 + .../com/mysema/query/sql/SQLSerializer.java | 35 ++++++++++++++----- .../com/mysema/query/sql/SQLTemplates.java | 17 +++++++-- .../java/com/mysema/query/SelectBase.java | 22 ++++++++++++ .../mysema/query/sql/SQLSerializerTest.java | 25 +++++++++++++ 6 files changed, 118 insertions(+), 13 deletions(-) diff --git a/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java b/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java index 035ec70a7..b712da3ac 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java @@ -13,13 +13,12 @@ */ package com.mysema.query.types; +import javax.annotation.Nullable; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nullable; - import com.google.common.collect.ImmutableList; import com.mysema.query.QueryException; @@ -389,6 +388,34 @@ public final class ExpressionUtils { return PredicateOperation.create(Ops.NE, left, right); } + /** + * Create an left not in right expression + * + * @param + * @param left + * @param right + * @return + */ + public static Predicate notIn(Expression left, CollectionExpression right) { + return PredicateOperation.create(Ops.NOT_IN, left, right); + } + + /** + * Create an left not in right expression + * + * @param + * @param left + * @param right + * @return + */ + public static Predicate notIn(Expression left, Collection right) { + if (right.size() == 1) { + return neConst(left, right.iterator().next()); + } else { + return PredicateOperation.create(Ops.NOT_IN, left, ConstantImpl.create(right)); + } + } + /** * Create a left or right expression * diff --git a/querydsl-sql/src/main/java/com/mysema/query/sql/OracleTemplates.java b/querydsl-sql/src/main/java/com/mysema/query/sql/OracleTemplates.java index 94a201e19..42d2c1231 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/sql/OracleTemplates.java +++ b/querydsl-sql/src/main/java/com/mysema/query/sql/OracleTemplates.java @@ -68,6 +68,7 @@ public class OracleTemplates extends SQLTemplates { setBatchCountViaGetUpdateCount(true); setWithRecursive("with "); setCountViaAnalytics(true); + setListMaxSize(1000); add(Ops.ALIAS, "{0} {1}"); add(SQLOps.NEXTVAL, "{0s}.nextval"); diff --git a/querydsl-sql/src/main/java/com/mysema/query/sql/SQLSerializer.java b/querydsl-sql/src/main/java/com/mysema/query/sql/SQLSerializer.java index 104f25a6c..f525ba3ae 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/sql/SQLSerializer.java +++ b/querydsl-sql/src/main/java/com/mysema/query/sql/SQLSerializer.java @@ -18,13 +18,11 @@ import java.util.*; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.mysema.commons.lang.Pair; -import com.mysema.query.JoinExpression; -import com.mysema.query.JoinFlag; -import com.mysema.query.QueryFlag; +import com.mysema.query.*; import com.mysema.query.QueryFlag.Position; -import com.mysema.query.QueryMetadata; import com.mysema.query.sql.types.Null; import com.mysema.query.support.Expressions; import com.mysema.query.support.SerializerBase; @@ -828,6 +826,7 @@ public class SQLSerializer extends SerializerBase { @Override protected void visitOperation(Class type, Operator operator, List> args) { + boolean pathAdded = false; if (args.size() == 2 && !useLiterals && args.get(0) instanceof Path @@ -838,6 +837,7 @@ public class SQLSerializer extends SerializerBase { for (Element element : templates.getTemplate(operator).getElements()) { if (element instanceof Template.ByIndex && ((Template.ByIndex)element).getIndex() == 1) { constantPaths.add((Path)args.get(0)); + pathAdded = true; break; } } @@ -877,10 +877,29 @@ public class SQLSerializer extends SerializerBase { } else if ((operator == Ops.IN || operator == Ops.NOT_IN) && args.get(0) instanceof Path - && args.get(1) instanceof Constant - && ((Constant)args.get(1)).getConstant().isEmpty()) { - super.visitOperation(type, operator == Ops.IN ? Ops.EQ : Ops.NE, - ImmutableList.of(NumberTemplate.ONE, NumberTemplate.TWO)); + && args.get(1) instanceof Constant) { + Collection coll = ((Constant)args.get(1)).getConstant(); + if (coll.isEmpty()) { + super.visitOperation(type, operator == Ops.IN ? Ops.EQ : Ops.NE, + ImmutableList.of(NumberTemplate.ONE, NumberTemplate.TWO)); + } else { + if (templates.getListMaxSize() == 0 || coll.size() <= templates.getListMaxSize()) { + super.visitOperation(type, operator, args); + } else { + if (!constantPaths.isEmpty()) { + constantPaths.removeLast(); + } + BooleanBuilder b = new BooleanBuilder(); + for (List part : Iterables.partition(coll, templates.getListMaxSize())) { + if (operator == Ops.IN) { + b.or(ExpressionUtils.in(args.get(0), part)); + } else { + b.and(ExpressionUtils.notIn(args.get(0), part)); + } + } + b.getValue().accept(this, null); + } + } } else if (operator == SQLOps.WITH_COLUMNS) { boolean oldSkipParent = skipParent; diff --git a/querydsl-sql/src/main/java/com/mysema/query/sql/SQLTemplates.java b/querydsl-sql/src/main/java/com/mysema/query/sql/SQLTemplates.java index ef0937438..1b6ecc20e 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/sql/SQLTemplates.java +++ b/querydsl-sql/src/main/java/com/mysema/query/sql/SQLTemplates.java @@ -13,11 +13,12 @@ */ package com.mysema.query.sql; -import static com.google.common.base.CharMatcher.inRange; - import java.lang.reflect.Field; import java.sql.Types; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; import com.google.common.base.CharMatcher; import com.google.common.collect.ImmutableSet; @@ -31,6 +32,7 @@ import com.mysema.query.QueryMetadata; import com.mysema.query.QueryModifiers; import com.mysema.query.sql.types.Type; import com.mysema.query.types.*; +import static com.google.common.base.CharMatcher.inRange; /** * SQLTemplates extends Templates to provides SQL specific extensions @@ -273,6 +275,8 @@ public class SQLTemplates extends Templates { private boolean arraysSupported = true; + private int listMaxSize = 0; + @Deprecated protected SQLTemplates(String quoteStr, char escape, boolean useQuotes) { this(SQL_RESERVED_WORDS, quoteStr, escape, useQuotes); @@ -787,6 +791,10 @@ public class SQLTemplates extends Templates { return arraysSupported; } + public int getListMaxSize() { + return listMaxSize; + } + protected void newLineToSingleSpace() { for (Class cl : Arrays.>asList(getClass(), SQLTemplates.class)) { for (Field field : cl.getDeclaredFields()) { @@ -1147,4 +1155,7 @@ public class SQLTemplates extends Templates { this.arraysSupported = b; } + protected void setListMaxSize(int i ) { + listMaxSize = i; + } } diff --git a/querydsl-sql/src/test/java/com/mysema/query/SelectBase.java b/querydsl-sql/src/test/java/com/mysema/query/SelectBase.java index e8d14bf30..a9eb31e25 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/SelectBase.java +++ b/querydsl-sql/src/test/java/com/mysema/query/SelectBase.java @@ -623,6 +623,28 @@ public class SelectBase extends AbstractBaseTest { query().from(employee).where(employee.id.in(Arrays.asList(1,2))).list(employee); } + @Test + @ExcludeIn(SQLITE) + public void In_Long_List() { + List ids = Lists.newArrayList(); + for (int i = 0; i < 20000; i++) { + ids.add(i); + } + assertEquals( + query().from(employee).count(), + query().from(employee).where(employee.id.in(ids)).count()); + } + + @Test + @ExcludeIn(SQLITE) + public void NotIn_Long_List() { + List ids = Lists.newArrayList(); + for (int i = 0; i < 20000; i++) { + ids.add(i); + } + assertEquals(0, query().from(employee).where(employee.id.notIn(ids)).count()); + } + @Test public void In_Empty() { assertEquals(0, query().from(employee).where(employee.id.in(ImmutableList.of())).count()); diff --git a/querydsl-sql/src/test/java/com/mysema/query/sql/SQLSerializerTest.java b/querydsl-sql/src/test/java/com/mysema/query/sql/SQLSerializerTest.java index f4434a92a..beb7f7e23 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/sql/SQLSerializerTest.java +++ b/querydsl-sql/src/test/java/com/mysema/query/sql/SQLSerializerTest.java @@ -27,6 +27,7 @@ import com.mysema.query.sql.domain.QEmployeeNoPK; import com.mysema.query.sql.domain.QSurvey; import com.mysema.query.support.Expressions; import com.mysema.query.types.Expression; +import com.mysema.query.types.ExpressionUtils; import com.mysema.query.types.Path; import com.mysema.query.types.SubQueryExpression; import com.mysema.query.types.expr.Wildcard; @@ -110,6 +111,30 @@ public class SQLSerializerTest { "where \"user\".id = ?)", serializer.toString()); } + @Test + public void In() { + StringPath path = Expressions.stringPath("str"); + Expression expr = ExpressionUtils.in(path, Arrays.asList("1", "2", "3")); + + SQLSerializer serializer = new SQLSerializer(Configuration.DEFAULT); + serializer.handle(expr); + assertEquals(Arrays.asList(path, path, path), serializer.getConstantPaths()); + assertEquals(3, serializer.getConstants().size()); + } + + @Test + public void Or_In() { + StringPath path = Expressions.stringPath("str"); + Expression expr = ExpressionUtils.anyOf( + ExpressionUtils.in(path, Arrays.asList("1", "2", "3")), + ExpressionUtils.in(path, Arrays.asList("4", "5", "6"))); + + SQLSerializer serializer = new SQLSerializer(Configuration.DEFAULT); + serializer.handle(expr); + assertEquals(Arrays.asList(path, path, path, path, path, path), serializer.getConstantPaths()); + assertEquals(6, serializer.getConstants().size()); + } + @Test public void Some() { //select some((e.FIRSTNAME is not null)) from EMPLOYEE