package edu.washington.escience.myria.parallel;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import edu.washington.escience.myria.DbException;
import edu.washington.escience.myria.MyriaConstants;
import edu.washington.escience.myria.api.MyriaJsonMapperProvider;
import edu.washington.escience.myria.api.encoding.PlanFragmentEncoding;
import edu.washington.escience.myria.api.encoding.QueryConstruct;
import edu.washington.escience.myria.api.encoding.QueryConstruct.ConstructArgs;
import edu.washington.escience.myria.coordinator.CatalogException;
import edu.washington.escience.myria.operator.EOSSource;
import edu.washington.escience.myria.operator.EmptySink;
/**
* A {@link QueryPlan} that runs a single subquery. Note that a {@link JsonSubQuery} cannot have a {@link QueryPlan} as
* a child, but rather can only accept a physical JSON subquery as a set of fragments.
*/
public final class JsonSubQuery extends QueryPlan {
/** The logger for this class. */
private static final Logger LOGGER = LoggerFactory.getLogger(JsonSubQuery.class);
/** The json query to be executed. */
private final List<PlanFragmentEncoding> fragments;
/**
* @return the fragments of the query.
*/
protected List<PlanFragmentEncoding> getFragments() {
return ImmutableList.copyOf(fragments);
}
/**
* Construct a {@link QueryPlan} that runs the given subquery. The subquery will be instantiated using
* {@link QueryConstruct#instantiate(List, edu.washington.escience.myria.parallel.Server)}.
*
* @param fragments the JSON query to be executed, broken into fragments
* @see QueryConstruct#instantiate(List, edu.washington.escience.myria.parallel.Server)
*/
public JsonSubQuery(final List<PlanFragmentEncoding> fragments) {
this.fragments = Objects.requireNonNull(fragments, "fragments");
}
@Override
public void instantiate(
final LinkedList<QueryPlan> planQ,
final LinkedList<SubQuery> subQueryQ,
final ConstructArgs args)
throws DbException {
QueryPlan task = planQ.peekFirst();
Verify.verify(
task == this,
"this Fragment %s should be the first object on the queue, not %s!",
this,
task);
planQ.removeFirst();
Map<Integer, SubQueryPlan> allPlans;
try {
allPlans = QueryConstruct.instantiate(fragments, args);
} catch (CatalogException e) {
throw new DbException("Error instantiating JsonSubQuery", e);
}
SubQueryPlan serverPlan = allPlans.get(MyriaConstants.MASTER_ID);
Map<Integer, SubQueryPlan> workerPlans;
if (serverPlan != null) {
workerPlans = new HashMap<>();
for (Map.Entry<Integer, SubQueryPlan> entry : allPlans.entrySet()) {
if (entry.getKey() != MyriaConstants.MASTER_ID) {
workerPlans.put(entry.getKey(), entry.getValue());
}
}
} else {
workerPlans = allPlans;
/* Create the empty server plan. TODO why do we need this? */
serverPlan = new SubQueryPlan(new EmptySink(new EOSSource()));
}
String planEncoding = null;
try {
planEncoding = MyriaJsonMapperProvider.getMapper().writeValueAsString(fragments);
} catch (JsonProcessingException e) {
LOGGER.error("Unable to encode JsonSubQuery fragments", e);
}
subQueryQ.addFirst(new SubQuery(null, serverPlan, workerPlans, planEncoding));
}
@Override
public void reset() {
/* Do nothing. */
}
}