package edu.washington.escience.myria.operator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
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.storage.TupleBatch;
/**
* Unions the output of a set of operators without eliminating duplicates.
*/
public final class UnionAll extends NAryOperator {
/** Required for Java serialization. */
private static final long serialVersionUID = 1L;
/** The column names from the first child, whose names we pass on. */
private List<String> outputColumnNames = null;
/**
* List of children that have not yet returned EOS.
*/
private transient LinkedList<Operator> childrenWithData;
/**
* @param children the children to be united.
*/
public UnionAll(final Operator[] children) {
super(children);
}
@Override
protected void cleanup() throws DbException {}
@Override
protected TupleBatch fetchNextReady() throws DbException {
/*
* If this variable gets to 0, it means that we've checked every child that could have data and none currently do.
* At that point, we return null and sleep until one of those children gets data.
*/
int uncheckedChildren = childrenWithData.size();
while (!childrenWithData.isEmpty()) {
if (uncheckedChildren == 0) {
return null;
}
uncheckedChildren--;
Operator child = childrenWithData.removeFirst();
if (child.eos()) {
continue;
}
TupleBatch tb = child.nextReady();
if (!child.eos()) {
childrenWithData.addLast(child);
}
if (tb != null) {
if (tb.getSchema().getColumnNames().equals(outputColumnNames)) {
return tb;
} else {
return tb.rename(outputColumnNames);
}
}
}
return null;
}
@Override
public void init(final ImmutableMap<String, Object> execEnvVars) throws DbException {
final Operator[] children = Objects.requireNonNull(getChildren(), "children");
Preconditions.checkArgument(children.length > 0, "UnionAll requires at least one child");
outputColumnNames = children[0].getSchema().getColumnNames();
childrenWithData = new LinkedList<Operator>();
for (Operator child : getChildren()) {
Preconditions.checkNotNull(child, "child");
Preconditions.checkArgument(
getSchema().compatible(child.getSchema()),
"Child schema %s is incompatible with %s",
child.getSchema(),
getSchema());
childrenWithData.add(child);
}
}
@Override
public Schema generateSchema() {
Operator[] children = getChildren();
if (children == null) {
return null;
}
if (children[0] == null) {
return null;
}
return children[0].getSchema();
}
}