package edu.brown.designer.partitioners;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.voltdb.catalog.CatalogType;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.Table;
import edu.brown.designer.AccessGraph;
import edu.brown.designer.DesignerEdge;
import edu.brown.designer.DesignerEdge.Members;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.PredicatePairs;
public class TablePartitionSets extends HashSet<TablePartitionSets.Entry> {
private static final long serialVersionUID = 3462969885348478117L;
private static final Logger LOG = Logger.getLogger(TablePartitionSets.class.getName());
private final Map<Table, Set<Entry>> table_entry_xref = new HashMap<Table, Set<Entry>>();
private final Map<PredicatePairs, Entry> cset_entry_xref = new HashMap<PredicatePairs, Entry>();
private final Table parent_table;
/**
* A unique set of attributes that the parent table can be partitioned on
*/
public class Entry extends HashSet<Column> {
private static final long serialVersionUID = 8592594865097644058L;
protected double weight = 0;
private final Map<Table, Set<PredicatePairs>> table_cset_xref = new HashMap<Table, Set<PredicatePairs>>();
private final Map<Table, Set<DesignerEdge>> table_edge_xref = new HashMap<Table, Set<DesignerEdge>>();
private final Map<Table, Collection<Column>> table_partition_xref = new HashMap<Table, Collection<Column>>();
/**
* @param other
*/
public void merge(Entry other) {
//
// Find the intersecting entries in order to scrub the ones that
// don't belong from the ColumnSets of the merging into
//
for (CatalogType catalog_item : other) {
if (!this.contains(catalog_item)) {
//
// Loop through all the ColumnSets for the tables that
// reference this entry
// and remove the attribute from them
//
for (Table catalog_tbl : other.table_cset_xref.keySet()) {
for (PredicatePairs cset : other.table_cset_xref.get(catalog_tbl)) {
cset.removeAll(cset.findAll(catalog_item));
} // FOR
} // FOR
}
} // FOR
this.table_cset_xref.putAll(other.table_cset_xref);
this.table_edge_xref.putAll(other.table_edge_xref);
this.table_partition_xref.putAll(other.table_partition_xref);
return;
}
/**
* Return all the tables used in this PartitionSet
*
* @return
*/
public Set<Table> getTables() {
return (this.table_cset_xref.keySet());
}
/**
* Return the parent table of this Entry object
*
* @return
*/
public Table getParent() {
return (TablePartitionSets.this.parent_table);
}
/**
* Returns the edge that connects the parent table in the PartitionSets
* object to the child table provided
*
* @param child_table
* @return
*/
public DesignerEdge getEdge(Table child_table) {
DesignerEdge ret = null;
if (this.table_edge_xref.containsKey(child_table)) {
ret = CollectionUtil.first(this.table_edge_xref.get(child_table));
}
return (ret);
}
/**
* Returns the list of columns that were used to link the child table
* provided to the parent table based on this AttributeSet
*
* @param child_table
* @return
*/
public Collection<Column> getChildAttributes(Table child_table) {
return (this.table_partition_xref.get(child_table));
}
/**
* @return
*/
public double getWeight() {
return this.weight;
}
/**
* @param catalog_tbl
* @return
*/
public Set<PredicatePairs> getColumnSets(Table catalog_tbl) {
return (this.table_cset_xref.get(catalog_tbl));
}
public String debug() {
String ret = this.getClass().getSimpleName() + "\n";
ret += " Attributes: " + this.toString() + "\n";
ret += " Weight: " + this.getWeight() + "\n";
ret += " Tables: " + this.table_edge_xref.keySet();
return (ret);
}
} // END CLASS
public TablePartitionSets(Table parent_table) {
super();
this.parent_table = parent_table;
}
public Table getParentTable() {
return this.parent_table;
}
public boolean add(Table child_table, DesignerEdge edge) {
//
// We first need to convert the ColumnSet into a set of attributes
// that "connect" the child table with the parent
//
PredicatePairs cset = null;
Entry entry = new Entry();
if (this.parent_table == child_table) {
cset = (PredicatePairs) edge.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name());
entry.addAll(cset.findAllForParent(Column.class, child_table));
} else {
cset = ((PredicatePairs) edge.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name())).createPredicatePairsForParent(Column.class, child_table);
entry.addAll(cset.findAllForOtherParent(Column.class, child_table));
}
double weight = (Double) edge.getAttribute(Members.WEIGHTS.name());
entry.weight = weight;
// System.out.println(cset + " Weight=" + entry.weight);
//
// Check whether we already contain this AttributeSet
// If we do, then we need to add the weight of the one we're being given
// to the one that we already have in there
//
boolean found = false;
for (Entry aset : this) {
if (aset.equals(entry)) {
found = true;
aset.weight += weight;
entry = aset;
break;
}
} // FOR
if (!found)
super.add(entry);
//
// Build our various index structures. We probably should probably think
// this through
// a bit more than just making anything we want here...
//
//
// Table -> ColumnSets
//
if (!entry.table_cset_xref.containsKey(child_table)) {
entry.table_cset_xref.put(child_table, new HashSet<PredicatePairs>());
}
entry.table_cset_xref.get(child_table).add(cset);
//
// Table -> Entry
//
if (!this.table_entry_xref.containsKey(child_table)) {
this.table_entry_xref.put(child_table, new HashSet<Entry>());
}
this.table_entry_xref.get(child_table).add(entry);
//
// ColumnSet -> AttributeSet
//
this.cset_entry_xref.put(cset, entry);
//
// Child Table -> Edges
//
if (!entry.table_edge_xref.containsKey(child_table)) {
entry.table_edge_xref.put(child_table, new HashSet<DesignerEdge>());
}
entry.table_edge_xref.get(child_table).add(edge);
//
// Child Table -> Attributes
//
if (entry.table_partition_xref.containsKey(child_table)) {
LOG.fatal("The AttributeSet for parent table '" + this.parent_table + "' " + "already contains attributes for child table '" + child_table + "'");
return (false);
}
entry.table_partition_xref.put(child_table, cset.findAllForOtherParent(Column.class, this.parent_table));
return (true);
}
public Set<Entry> getMaxWeightAttributes() {
Set<Entry> ret = new HashSet<Entry>();
double max_weight = 0;
for (Entry aset : this) {
if (aset.weight == max_weight) {
ret.add(aset);
} else if (aset.weight > max_weight) {
ret.clear();
ret.add(aset);
max_weight = aset.weight;
}
} // FOR
return (ret);
}
/**
* @throws Exception
*/
public void generateSubSets() throws Exception {
//
// Simple hack for now
//
LOG.info("Trying to generate entry subsets");
System.out.println(this.debug());
Set<Entry> remove = new HashSet<Entry>();
for (Entry entry0 : this) {
for (Entry entry1 : this) {
if (entry0 == entry1)
continue;
if (entry0.containsAll(entry1) && entry0.size() > entry1.size()) {
LOG.info("Merging " + entry0 + " into " + entry1);
entry1.merge(entry0);
remove.add(entry0);
break;
} else {
LOG.debug("entry0: [" + entry0.size() + "] " + entry0);
LOG.debug("entry1: [" + entry1.size() + "] " + entry1);
LOG.debug("entry0.containsAll(entry1): " + entry0.containsAll(entry1));
LOG.debug("entry1.containsAll(entry0): " + entry1.containsAll(entry0));
}
} // FOR
if (!remove.isEmpty())
break;
} // FOR
if (!remove.isEmpty()) {
this.removeAll(remove);
LOG.debug(this.debug());
} else {
LOG.warn("Failed to find any entries to merge!");
// if (this.parent_table.getName().equals("CUSTOMER"))
// System.exit(1);
}
return;
}
public String debug() {
String ret = this.getClass().getSimpleName() + ": " + this.parent_table + "\n";
for (Entry entry : this) {
ret += entry.debug() + "\n";
}
return (ret);
}
}