package edu.washington.escience.myria.operator;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import edu.washington.escience.myria.DbException;
import edu.washington.escience.myria.Schema;
import edu.washington.escience.myria.column.Column;
import edu.washington.escience.myria.storage.ConcatColumn;
import edu.washington.escience.myria.storage.TupleBatch;
import edu.washington.escience.myria.storage.TupleUtils;
/**
* An abstract class used to make those specialized operators that only consume tuples simpler to implement.
*
*
*/
public abstract class RootOperator extends Operator {
/** Required for Java serialization. */
private static final long serialVersionUID = 1L;
/** Source of the tuples to be consumed. */
private Operator child;
/** The coalescing threshold. 0 or negative means do not coalesce. */
private int threshold;
/**
* Sets important parameters for successful operation.
*
* @param child the source of tuples that this Root operator consumes.
*/
public RootOperator(final Operator child) {
this.child = child;
}
/**
* Set threshold for the root operator, only used by DBInsert and DBIsertTemp operatos.
* @param threshold size of tuplebatch
*/
public void setThreshold(int threshold) {
this.threshold = threshold;
}
/**
* Perform the function of this operator on the provided tuples. For instance, may print the tuples to the screen or
* write them to disk.
*
* @param tuples the tuples to be consumed.
* @throws DbException if there's an error in the database.
*/
protected abstract void consumeTuples(TupleBatch tuples) throws DbException;
/**
* If the child EOS is meet, the method is called back to let the root operators deal with this event.
*
* @throws DbException if any error occurs.
* */
protected abstract void childEOS() throws DbException;
/**
* call if the child meets EOI.
*
* @throws DbException if any error occurs.
* */
protected abstract void childEOI() throws DbException;
/**
* Implement coalescing tuples together if necessary.
*
* @param first the first, non-null tuple batch to be handled.
* @return a coalesced tuple batch consisting of either all ready tuples or at least {@link #threshold} tuples.
* @throws DbException if there is an error.
*/
private TupleBatch getCoalesced(@Nonnull final TupleBatch first) throws DbException {
if (first.numTuples() > threshold) {
return first;
}
TupleBatch next = child.nextReady();
if (next == null) {
return first;
}
/* Start a concat column of the first two batches we found. */
List<ConcatColumn<?>> cols = new ArrayList<>(first.numColumns());
for (Column<?> c : first.getDataColumns()) {
ConcatColumn<?> col = new ConcatColumn<>(c.getType());
col.addColumn(c);
cols.add(col);
}
int i = 0;
for (Column<?> c : next.getDataColumns()) {
cols.get(i).addColumn(c);
++i;
}
while (cols.get(0).size() < threshold) {
next = child.nextReady();
if (next == null) {
break;
}
i = 0;
for (Column<?> c : next.getDataColumns()) {
cols.get(i).addColumn(c);
++i;
}
}
return new TupleBatch(getSchema(), cols);
}
@Override
protected final TupleBatch fetchNextReady() throws DbException {
TupleBatch tb = null;
tb = child.nextReady();
if (tb != null) {
tb = getCoalesced(tb);
consumeTuples(tb);
} else if (child.eoi()) {
childEOI();
} else if (child.eos()) {
childEOS();
}
return tb;
}
/**
* @return the source of the tuples that this Root operator consumes.
*/
public final Operator getChild() {
return child;
}
@Override
public final Operator[] getChildren() {
return new Operator[] {child};
}
@Override
public final void setChildren(final Operator[] children) {
if (children.length != 1) {
throw new IllegalArgumentException("a root operator must have exactly one child");
}
Preconditions.checkNotNull(children[0], "RootOperator opId=%s has a null child", getOpId());
child = children[0];
}
@Override
public final Schema generateSchema() {
if (child == null) {
return null;
}
return child.getSchema();
}
/**
* process EOS and EOI logic.
* */
@Override
protected void checkEOSAndEOI() {
if (child.eos()) {
setEOS();
} else if (child.eoi()) {
setEOI(true);
child.setEOI(false);
}
}
}