/** * */ package edu.brown.designer.indexselectors; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.voltdb.catalog.Column; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Table; import org.voltdb.types.QueryType; import org.voltdb.utils.CatalogUtil; import edu.brown.designer.AccessGraph; import edu.brown.designer.Designer; import edu.brown.designer.DesignerEdge; import edu.brown.designer.DesignerEdge.Members; import edu.brown.designer.DesignerInfo; import edu.brown.designer.DesignerVertex; import edu.brown.designer.IndexPlan; import edu.brown.designer.PartitionTree; import edu.brown.designer.partitioners.plan.PartitionEntry; import edu.brown.designer.partitioners.plan.PartitionPlan; import edu.brown.utils.PredicatePairs; /** * @author pavlo */ public class SimpleIndexSelector extends AbstractIndexSelector { public SimpleIndexSelector(Designer designer, DesignerInfo info) { super(designer, info); } /* * (non-Javadoc) * @see edu.brown.designer.indexselectors.AbstractIndexSelector#generate() */ @Override public IndexPlan generate(PartitionPlan plan) throws Exception { IndexPlan indexPlan = new IndexPlan(info.catalogContext.database); // // Go through and count up all the attribute sets that aren't used for // partitioning // PartitionTree ptree = null; // FIXME designer.getPartitionTree(); for (Procedure catalog_proc : info.catalogContext.database.getProcedures()) { AccessGraph agraph = designer.getAccessGraph(catalog_proc); if (agraph == null) continue; for (DesignerEdge edge : agraph.getEdges()) { ArrayList<DesignerVertex> vertices = new ArrayList<DesignerVertex>(); vertices.addAll(agraph.getIncidentVertices(edge)); // FIXME if (true || !(ptree.getPath(vertices.get(0), vertices.get(1)).isEmpty() && ptree.getPath(vertices.get(1), vertices.get(0)).isEmpty())) { PredicatePairs cset = (PredicatePairs) (edge.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name())); for (DesignerVertex vertex : vertices) { Table catalog_tbl = vertex.getCatalogItem(); Collection<Column> edge_columns = cset.findAllForParent(Column.class, catalog_tbl); // // Exclusion: Check whether this table is already // partitioned on these columns // PartitionEntry pentry = plan.getTableEntries().get(catalog_tbl); if (pentry == null) { LOG.warn("PartitionEntry is null for " + catalog_tbl); continue; // } else if (pentry.getMethod() != // PartitionMethodType.REPLICATION && // pentry.getAttributes().equals(edge_columns)) { // LOG.info(catalog_tbl + // " is already partitioned on " + edge_columns + // ". Skipping..."); // continue; } // // Exclusion: Check whether this is the table's primary // key // Collection<Column> pkeys = CatalogUtil.getPrimaryKeyColumns(catalog_tbl); if (pkeys.containsAll(edge_columns) && edge_columns.containsAll(pkeys)) { LOG.info(catalog_tbl + "'s primary key already contains " + edge_columns + ". Skipping..."); continue; } // // Exclusion: These columns are only used in INSERTS // Map<QueryType, Integer> query_counts = cset.getQueryCounts(); if (query_counts.get(QueryType.SELECT) == 0 && query_counts.get(QueryType.UPDATE) == 0 && query_counts.get(QueryType.DELETE) == 0) { LOG.info("The columns " + edge_columns + " are only used in INSERT operations on " + catalog_tbl + ". Skipping..."); continue; } // // Check whether we already have a candidate index for // this set of columns // IndexPlan.Entry found = null; for (IndexPlan.Entry index : indexPlan.get(catalog_tbl)) { if (index.getColumns().containsAll(edge_columns) && edge_columns.containsAll(index.getColumns())) { found = index; break; } } // FOR // // We have a match, so we need to add this edge's weight // to it // Double weight = (Double) edge.getAttribute(Members.WEIGHTS.name()); if (found != null) { weight += found.getWeight(); } else { found = indexPlan.new Entry(catalog_tbl); found.getColumns().addAll(edge_columns); } found.setWeight(weight); found.getProcedures().add(catalog_proc); indexPlan.get(catalog_tbl).add(found); } // FOR } // IF } // FOR } // FOR // // We now need to consolidate overlapping indexes for each table if they // are // used in the same procedure // for (Table catalog_tbl : indexPlan.keySet()) { Iterator<IndexPlan.Entry> it0 = indexPlan.get(catalog_tbl).iterator(); while (it0.hasNext()) { IndexPlan.Entry index0 = it0.next(); // // Look for another index that has all our columns // for (IndexPlan.Entry index1 : indexPlan.get(catalog_tbl)) { if (index0 == index1) continue; if (index1.getColumns().containsAll(index0.getColumns()) && (index1.getProcedures().containsAll(index0.getProcedures()) || index0.getProcedures().containsAll(index1.getProcedures()))) { // // Merge the one index into the other // index1.merge(index0); it0.remove(); break; } } // FOR } // WHILE } // FOR // // Now that we have our candidate indexes, we need to go through and // multiple // the index weights by the number of times the procedures are executed // that would // use that index // /* * for (Table catalog_tbl : this.indexes.keySet()) { * Set<IndexPlan.Entry> remove = new HashSet<IndexPlan.Entry>(); for * (IndexPlan.Entry index : this.indexes.get(catalog_tbl)) { Double * weight = index.getWeight(); // // Important! Some procedures may have * never been executed, so we need // to make sure we don't multiply the * weight if the count is zero // for (Procedure catalog_proc : * index.getProcedures()) { int count = * this.info.stats.get(catalog_proc).proc_counts; if (count > 0) weight * *= count; } // FOR if (weight == 0) { * LOG.info("Removing candidate index " + index + * " because its weight is zero"); remove.add(index); } * index.setWeight(weight); } // FOR if (!remove.isEmpty()) * this.indexes.get(catalog_tbl).removeAll(remove); } // FOR */ // // Ah-ha! We can now sort the indexes by their weights // // for (IndexPlan.Entry index : sorted) { // System.out.println("[" + index.getWeight() + "] " + index + " - " + // index.getProcedures()); // } return (indexPlan); } }