package com.bigdata.relation.rule.eval.pipeline;
import com.bigdata.bop.IBindingSet;
import com.bigdata.relation.accesspath.BufferClosedException;
import com.bigdata.relation.accesspath.IBlockingBuffer;
import com.bigdata.relation.accesspath.IBuffer;
import com.bigdata.relation.accesspath.IElementFilter;
import com.bigdata.relation.rule.IRule;
import com.bigdata.relation.rule.eval.IJoinNexus;
import com.bigdata.relation.rule.eval.ISolution;
/**
* Implementation used to write on the {@link JoinTask#getSolutionBuffer()}
* for the last join dimension. The solution buffer is either an
* {@link IBlockingBuffer} (for query) or a buffer that writes on the head
* relation for the rule (for mutation).
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <E>
*/
class UnsynchronizedSolutionBuffer<E extends IBindingSet> extends
UnsynchronizedOutputBuffer<E> {
private final IJoinNexus joinNexus;
/**
* An optional filter on the generated {@link ISolution}s. This filter
* is obtained from {@link IJoinNexus#getSolutionFilter()}. When non-<code>null</code>,
* {@link #handleChunk(IBindingSet[])} applies the filter to keep
* {@link ISolution}s licensed by the {@link IBindingSet}s that do not
* meet constraint imposed by that filter. (For RDF, this is used to
* keep literals out of the subject position, keep axioms from being
* written into the DB by inference, etc.)
*/
private final IElementFilter<ISolution> solutionFilter;
public UnsynchronizedSolutionBuffer(final JoinTask joinTask,
final IJoinNexus joinNexus, final int capacity) {
super(joinTask, capacity);
this.joinNexus = joinNexus;
// Note: MAY be null.
this.solutionFilter = joinNexus.getSolutionFilter();
}
/**
* Generate a chunk of {@link ISolution}s for the accepted
* {@link IBindingSet}s and add those those {@link ISolution}s to the
* {@link JoinTask#getSolutionBuffer()}. For query, that will be a
* (proxy for) the {@link IJoinNexus#newQueryBuffer()} created by the
* {@link JoinMasterTask}. For mutation, that will be a buffer created
* for the {@link JoinTask} instance (this avoids have all data for
* mutation flow through the master).
*
* @throws BufferClosedException
* If the {@link IBuffer} returned by
* {@link JoinTask#getSolutionBuffer()} is an
* {@link IBlockingBuffer} which has been closed. This will
* occur for query if the query specifies a SLICE and the
* SLICE has been satisified. Under these conditions the
* {@link IBlockingBuffer} will be closed asynchronously by
* the query consumer and {@link BufferClosedException} will
* be thown by {@link IBlockingBuffer#add(Object)}.
*/
protected void handleChunk(final E[] chunk) {
final IBuffer<ISolution[]> solutionBuffer = joinTask
.getSolutionBuffer();
final IRule rule = joinTask.rule;
ISolution[] a = new ISolution[chunk.length];
int naccepted = 0;
for (int i = 0; i < chunk.length; i++) {
// an accepted binding set.
final IBindingSet bindingSet = chunk[i];
/*
* Note: The [joinNexus] MUST have access to the global and
* mutable index views. For the federation, this means that it
* is not the same instance that you are using to read on the
* access path!
*/
final ISolution solution = joinNexus.newSolution(rule,
bindingSet);
if (solutionFilter == null || solutionFilter.isValid(solution)) {
a[naccepted++] = solution;
}
}
if (naccepted == 0)
return;
if (naccepted < chunk.length) {
// Make the array dense and snug.
final ISolution[] b = new ISolution[naccepted];
System.arraycopy(a, 0, b, 0, naccepted);
a = b;
}
/*
* Add the chunk to the [solutionBuffer].
*
* Note: This can throw a BufferClosedException. In particular, this
* exception will be thrown if the [solutionBuffer] is a query
* buffer and a SLICE been satisified causing the [solutionBuffer]
* to be asynchronously closed by the query consumer.
*/
solutionBuffer.add(a);
joinTask.stats.bindingSetChunksOut++;
joinTask.stats.bindingSetsOut += naccepted;
}
}