diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 92f01a9e80f..da643ff5b82 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -93,6 +93,13 @@ Other * LUCENE-7743: Never call new String(String). (Daniel Jelinski via Adrien Grand) +======================= Lucene 6.5.1 ======================= + +Bug Fixes + +* LUCENE-7755: Fixed join queries to not reference IndexReaders, as it could + cause leaks if they are cached. (Adrien Grand) + ======================= Lucene 6.5.0 ======================= API Changes diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java b/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java index dada3ff7263..bca7a140c18 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java @@ -46,7 +46,15 @@ public abstract class IndexReaderContext { this.ordInParent = ordInParent; this.isTopLevel = parent==null; } - + + /** Expert: Return an {@link Object} that uniquely identifies this context. + * The returned object does neither reference this {@link IndexReaderContext} + * nor the wrapped {@link IndexReader}. + * @lucene.experimental */ + public Object id() { + return identity; + } + /** Returns the {@link IndexReader}, this context represents. */ public abstract IndexReader reader(); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java index 93edcc073d8..5aaca1af153 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java @@ -20,7 +20,7 @@ import java.io.IOException; import java.util.Set; import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.MultiDocValues; import org.apache.lucene.index.SortedDocValues; @@ -48,19 +48,23 @@ final class GlobalOrdinalsQuery extends Query { // just for hashcode and equals: private final Query fromQuery; - private final IndexReader indexReader; + // id of the context rather than the context itself in order not to hold references to index readers + private final Object indexReaderContextId; - GlobalOrdinalsQuery(LongBitSet foundOrds, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, IndexReader indexReader) { + GlobalOrdinalsQuery(LongBitSet foundOrds, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, IndexReaderContext context) { this.foundOrds = foundOrds; this.joinField = joinField; this.globalOrds = globalOrds; this.toQuery = toQuery; this.fromQuery = fromQuery; - this.indexReader = indexReader; + this.indexReaderContextId = context.id(); } @Override public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException { + if (searcher.getTopReaderContext().id() != indexReaderContextId) { + throw new IllegalStateException("Creating the weight against a different index reader than this query has been built for."); + } return new W(this, toQuery.createWeight(searcher, false, 1f), boost); } @@ -74,7 +78,7 @@ final class GlobalOrdinalsQuery extends Query { return fromQuery.equals(other.fromQuery) && joinField.equals(other.joinField) && toQuery.equals(other.toQuery) && - indexReader.equals(other.indexReader); + indexReaderContextId.equals(other.indexReaderContextId); } @Override @@ -83,7 +87,7 @@ final class GlobalOrdinalsQuery extends Query { result = 31 * result + joinField.hashCode(); result = 31 * result + toQuery.hashCode(); result = 31 * result + fromQuery.hashCode(); - result = 31 * result + indexReader.hashCode(); + result = 31 * result + indexReaderContextId.hashCode(); return result; } diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java index 0aedf353f85..5e614ea41f3 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java @@ -20,7 +20,7 @@ import java.io.IOException; import java.util.Set; import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.MultiDocValues; import org.apache.lucene.index.SortedDocValues; @@ -48,9 +48,10 @@ final class GlobalOrdinalsWithScoreQuery extends Query { private final Query fromQuery; private final int min; private final int max; - private final IndexReader indexReader; + // id of the context rather than the context itself in order not to hold references to index readers + private final Object indexReaderContextId; - GlobalOrdinalsWithScoreQuery(GlobalOrdinalsWithScoreCollector collector, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, int min, int max, IndexReader indexReader) { + GlobalOrdinalsWithScoreQuery(GlobalOrdinalsWithScoreCollector collector, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, int min, int max, IndexReaderContext context) { this.collector = collector; this.joinField = joinField; this.globalOrds = globalOrds; @@ -58,11 +59,14 @@ final class GlobalOrdinalsWithScoreQuery extends Query { this.fromQuery = fromQuery; this.min = min; this.max = max; - this.indexReader = indexReader; + this.indexReaderContextId = context.id(); } @Override public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException { + if (searcher.getTopReaderContext().id() != indexReaderContextId) { + throw new IllegalStateException("Creating the weight against a different index reader than this query has been built for."); + } return new W(this, toQuery.createWeight(searcher, false, 1f)); } @@ -78,7 +82,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query { joinField.equals(other.joinField) && fromQuery.equals(other.fromQuery) && toQuery.equals(other.toQuery) && - indexReader.equals(other.indexReader); + indexReaderContextId.equals(other.indexReaderContextId); } @Override @@ -89,7 +93,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query { result = 31 * result + fromQuery.hashCode(); result = 31 * result + min; result = 31 * result + max; - result = 31 * result + indexReader.hashCode(); + result = 31 * result + indexReaderContextId.hashCode(); return result; } diff --git a/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java b/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java index bfc1f9bf3ec..537b2244aee 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java @@ -32,7 +32,6 @@ import org.apache.lucene.document.LongPoint; import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.DocValues; import org.apache.lucene.index.DocValuesType; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.MultiDocValues; @@ -467,8 +466,7 @@ public final class JoinUtil { MultiDocValues.OrdinalMap ordinalMap, int min, int max) throws IOException { - IndexReader indexReader = searcher.getIndexReader(); - int numSegments = indexReader.leaves().size(); + int numSegments = searcher.getIndexReader().leaves().size(); final long valueCount; if (numSegments == 0) { return new MatchNoDocsQuery("JoinUtil.createJoinQuery with no segments"); @@ -509,7 +507,7 @@ public final class JoinUtil { if (min <= 0 && max == Integer.MAX_VALUE) { GlobalOrdinalsCollector globalOrdinalsCollector = new GlobalOrdinalsCollector(joinField, ordinalMap, valueCount); searcher.search(rewrittenFromQuery, globalOrdinalsCollector); - return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, indexReader); + return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, searcher.getTopReaderContext()); } else { globalOrdinalsWithScoreCollector = new GlobalOrdinalsWithScoreCollector.NoScore(joinField, ordinalMap, valueCount, min, max); break; @@ -518,7 +516,7 @@ public final class JoinUtil { throw new IllegalArgumentException(String.format(Locale.ROOT, "Score mode %s isn't supported.", scoreMode)); } searcher.search(rewrittenFromQuery, globalOrdinalsWithScoreCollector); - return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, min, max, indexReader); + return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, min, max, searcher.getTopReaderContext()); } }