|
30 | 30 | import org.eclipse.rdf4j.collection.factory.api.CollectionFactory; |
31 | 31 | import org.eclipse.rdf4j.collection.factory.impl.DefaultCollectionFactory; |
32 | 32 | import org.eclipse.rdf4j.common.iteration.AbstractCloseableIteratorIteration; |
| 33 | +import org.eclipse.rdf4j.common.iteration.CloseableIteration; |
33 | 34 | import org.eclipse.rdf4j.common.transaction.QueryEvaluationMode; |
34 | 35 | import org.eclipse.rdf4j.model.Literal; |
35 | 36 | import org.eclipse.rdf4j.model.Value; |
@@ -89,6 +90,9 @@ public class GroupIterator extends AbstractCloseableIteratorIteration<BindingSet |
89 | 90 |
|
90 | 91 | private final QueryEvaluationStep arguments; |
91 | 92 |
|
| 93 | + // The iteration of the arguments, stored while building entries for allowing premature closing |
| 94 | + private volatile CloseableIteration<BindingSet> argumentsIter; |
| 95 | + |
92 | 96 | private final ValueFactory vf; |
93 | 97 |
|
94 | 98 | private final CollectionFactory cf; |
@@ -129,7 +133,13 @@ public GroupIterator(EvaluationStrategy strategy, Group group, BindingSet parent |
129 | 133 |
|
130 | 134 | @Override |
131 | 135 | public void handleClose() throws QueryEvaluationException { |
132 | | - cf.close(); |
| 136 | + try { |
| 137 | + cf.close(); |
| 138 | + } finally { |
| 139 | + var iter = argumentsIter; |
| 140 | + if (iter != null) |
| 141 | + iter.close(); |
| 142 | + } |
133 | 143 | } |
134 | 144 |
|
135 | 145 | @Override |
@@ -256,42 +266,46 @@ private BiConsumer<Entry, MutableBindingSet> makeBindSolution( |
256 | 266 |
|
257 | 267 | private Collection<Entry> buildEntries(List<AggregatePredicateCollectorSupplier<?, ?>> aggregates) |
258 | 268 | throws QueryEvaluationException { |
259 | | - try (var iter = arguments.evaluate(parentBindings)) { |
| 269 | + // store the arguments' iterator so it can be closed while building entries |
| 270 | + this.argumentsIter = arguments.evaluate(parentBindings); |
| 271 | + try (var iter = argumentsIter) { |
| 272 | + if (!iter.hasNext()) { |
| 273 | + return emptySolutionSpecialCase(aggregates); |
| 274 | + } |
| 275 | + |
260 | 276 | List<Function<BindingSet, Value>> getValues = group.getGroupBindingNames() |
261 | 277 | .stream() |
262 | 278 | .map(n -> context.getValue(n)) |
263 | 279 | .collect(Collectors.toList()); |
264 | 280 |
|
265 | | - if (!iter.hasNext()) { |
266 | | - return emptySolutionSpecialCase(aggregates); |
267 | | - } else { |
268 | | - // TODO: this is an in memory map with no backing into any disk form. |
269 | | - // Fixing this requires separating the computation of the aggregates and their |
270 | | - // distinct sets if needed from the intermediary values. |
271 | | - |
272 | | - Map<BindingSetKey, Entry> entries = cf.createGroupByMap(); |
273 | | - // Make an optimized hash function valid during this query evaluation step. |
274 | | - ToIntFunction<BindingSet> hashMaker = cf.hashOfBindingSetFuntion(getValues); |
275 | | - while (iter.hasNext()) { |
276 | | - BindingSet sol = iter.next(); |
277 | | - // The binding set key will be constant |
278 | | - BindingSetKey key = cf.createBindingSetKey(sol, getValues, hashMaker); |
279 | | - Entry entry = entries.get(key); |
280 | | - if (entry == null) { |
281 | | - List<AggregateCollector> collectors = makeCollectors(aggregates); |
282 | | - List<Predicate<?>> predicates = new ArrayList<>(aggregates.size()); |
283 | | - for (AggregatePredicateCollectorSupplier<?, ?> a : aggregates) { |
284 | | - predicates.add(a.makePotentialDistinctTest.get()); |
285 | | - } |
286 | | - |
287 | | - entry = new Entry(sol, collectors, predicates); |
288 | | - entries.put(key, entry); |
| 281 | + // TODO: this is an in memory map with no backing into any disk form. |
| 282 | + // Fixing this requires separating the computation of the aggregates and their |
| 283 | + // distinct sets if needed from the intermediary values. |
| 284 | + |
| 285 | + Map<BindingSetKey, Entry> entries = cf.createGroupByMap(); |
| 286 | + // Make an optimized hash function valid during this query evaluation step. |
| 287 | + ToIntFunction<BindingSet> hashMaker = cf.hashOfBindingSetFuntion(getValues); |
| 288 | + while (!isClosed() && iter.hasNext()) { |
| 289 | + BindingSet sol = iter.next(); |
| 290 | + // The binding set key will be constant |
| 291 | + BindingSetKey key = cf.createBindingSetKey(sol, getValues, hashMaker); |
| 292 | + Entry entry = entries.get(key); |
| 293 | + if (entry == null) { |
| 294 | + List<AggregateCollector> collectors = makeCollectors(aggregates); |
| 295 | + List<Predicate<?>> predicates = new ArrayList<>(aggregates.size()); |
| 296 | + for (AggregatePredicateCollectorSupplier<?, ?> a : aggregates) { |
| 297 | + predicates.add(a.makePotentialDistinctTest.get()); |
289 | 298 | } |
290 | 299 |
|
291 | | - entry.addSolution(sol, aggregates); |
| 300 | + entry = new Entry(sol, collectors, predicates); |
| 301 | + entries.put(key, entry); |
292 | 302 | } |
293 | | - return entries.values(); |
| 303 | + |
| 304 | + entry.addSolution(sol, aggregates); |
294 | 305 | } |
| 306 | + return entries.values(); |
| 307 | + } finally { |
| 308 | + this.argumentsIter = null; |
295 | 309 | } |
296 | 310 | } |
297 | 311 |
|
|
0 commit comments