package edu.brown.optimizer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.log4j.Logger;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Table;
import org.voltdb.planner.PlannerContext;
import org.voltdb.plannodes.AbstractJoinPlanNode;
import org.voltdb.plannodes.AbstractPlanNode;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.StringUtil;
public class PlanOptimizerState {
private static final Logger LOG = Logger.getLogger(PlanOptimizerState.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
/**
* Database Catalog Object
*/
public final Database catalog_db;
/**
* Context object with planner-local information.
*/
public final PlannerContext plannerContext;
/**
* All the columns a plan node references
*/
protected final Map<AbstractPlanNode, Set<Column>> planNodeColumns = new HashMap<AbstractPlanNode, Set<Column>>();
/**
* All referenced columns for a given table
*/
public final Map<Table, SortedSet<Column>> tableColumns = new HashMap<Table, SortedSet<Column>>();
/**
* Mapping from Column -> Set<PlanColumnGUID>
*/
public final Map<Column, Set<Integer>> column_guid_xref = new HashMap<Column, Set<Integer>>();
/**
* Mapping from PlanColumnGUID -> Column
*/
public final Map<Integer, Column> guid_column_xref = new HashMap<Integer, Column>();
/**
* Maintain the original output PlanColumnnGUIDs per PlanNode so we can
* figure out offsets
*/
public final Map<AbstractPlanNode, List<Integer>> orig_node_output = new HashMap<AbstractPlanNode, List<Integer>>();
/**
* AbstractPlanNode -> TableNames
*/
public final Map<AbstractPlanNode, Set<String>> join_tbl_mapping = new HashMap<AbstractPlanNode, Set<String>>();
/**
* AbstractJoinPlanNode to the order that it appears in the tree
*/
public final SortedMap<Integer, AbstractJoinPlanNode> join_node_index = new TreeMap<Integer, AbstractJoinPlanNode>();
/**
* AbstractJoinPlanNode -> Output PlanColumnDisplayName -> Offset
*/
public final Map<AbstractJoinPlanNode, Map<String, Integer>> join_outputs = new HashMap<AbstractJoinPlanNode, Map<String, Integer>>();
// ------------------------------------------------------------
// INTERNAL STATE
// ------------------------------------------------------------
/**
* Set of PlanNodes that have been modified and thus are marked as dirty
*/
private final Set<AbstractPlanNode> dirtyPlanNodes = new HashSet<AbstractPlanNode>();
// ------------------------------------------------------------
// CONSTRUCTOR
// ------------------------------------------------------------
public PlanOptimizerState(Database catalog_db, PlannerContext context) {
this.catalog_db = catalog_db;
this.plannerContext = context;
}
// ------------------------------------------------------------
// UTILITY METHODS
// ------------------------------------------------------------
public void clearDirtyNodes() {
this.dirtyPlanNodes.clear();
}
public void markDirty(AbstractPlanNode node) {
if (trace.val)
LOG.trace("Marking " + node + " as dirty");
this.dirtyPlanNodes.add(node);
}
public boolean hasDirtyNodes() {
return (this.dirtyPlanNodes.isEmpty() == false);
}
public boolean isDirty(AbstractPlanNode node) {
return (this.dirtyPlanNodes.contains(node));
}
public boolean areChildrenDirty(AbstractPlanNode node) {
int ctr = 0;
for (AbstractPlanNode child_node : node.getChildren()) {
if (this.isDirty(child_node))
ctr++;
}
if (debug.val)
LOG.debug(String.format("%s has %d dirty children", node, ctr));
return (ctr > 0);
}
public void updateColumnInfo(AbstractPlanNode node) {
// Clears the internal data structures that stores the column info
if (debug.val)
LOG.debug("Clearing internal state information");
this.orig_node_output.clear();
this.tableColumns.clear();
this.column_guid_xref.clear();
this.planNodeColumns.clear();
this.guid_column_xref.clear();
PlanOptimizerUtil.populateTableNodeInfo(this, node);
}
protected void addTableColumn(Column catalog_col) {
Table catalog_tbl = catalog_col.getParent();
if (this.tableColumns.containsKey(catalog_tbl) == false) {
this.tableColumns.put(catalog_tbl, new TreeSet<Column>(PlanOptimizer.COLUMN_COMPARATOR));
}
this.tableColumns.get(catalog_tbl).add(catalog_col);
}
public void addColumnMapping(Column catalog_col, Integer guid) {
if (this.column_guid_xref.containsKey(catalog_col) == false) {
this.column_guid_xref.put(catalog_col, new HashSet<Integer>());
}
this.column_guid_xref.get(catalog_col).add(guid);
this.guid_column_xref.put(guid, catalog_col);
if (trace.val)
LOG.trace(String.format("Added Column GUID Mapping: %s => %d", catalog_col.fullName(), guid));
}
protected void addPlanNodeColumn(AbstractPlanNode node, Column catalog_col) {
if (this.planNodeColumns.containsKey(node) == false) {
this.planNodeColumns.put(node, new HashSet<Column>());
}
if (trace.val)
LOG.trace(String.format("Referenced Columns %s -> %s", node, catalog_col));
this.planNodeColumns.get(node).add(catalog_col);
}
public Collection<Column> getPlanNodeColumns(AbstractPlanNode node) {
return this.planNodeColumns.get(node);
}
// ------------------------------------------------------------
// DEBUG
// ------------------------------------------------------------
@Override
public String toString() {
Map<String, Object> m = new ListOrderedMap<String, Object>();
m.put("PlanNode Columns", this.planNodeColumns);
m.put("PlanNode Hash", Arrays.toString(CollectionUtil.hashCode(this.planNodeColumns.keySet())));
m.put("Table Columns", this.tableColumns);
m.put("Column -> PlanColumnGUID", this.column_guid_xref);
m.put("PlanColumnGUID -> Column", this.guid_column_xref);
m.put("Original Output PlanColumnGUIDs", this.orig_node_output);
m.put("JoinPlanNode Tables", this.join_tbl_mapping);
m.put("JoinPlanNode Depths", this.join_node_index);
m.put("JoinPlanNode Output", this.join_outputs);
return (StringUtil.formatMaps(m));
}
}