package edu.brown.designer.partitioners; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.voltdb.catalog.CatalogType; import org.voltdb.catalog.Column; import org.voltdb.catalog.Table; import edu.brown.benchmark.tm1.TM1Constants; import edu.brown.catalog.CatalogPair; import edu.brown.catalog.special.MultiColumn; import edu.brown.catalog.special.ReplicatedColumn; import edu.brown.catalog.special.VerticalPartitionColumn; import edu.brown.designer.AccessGraph; import edu.brown.designer.DesignerEdge; import edu.brown.designer.DesignerVertex; import edu.brown.designer.AccessGraph.EdgeAttributes; import edu.brown.designer.generators.AccessGraphGenerator; import edu.brown.utils.PredicatePairs; import edu.brown.utils.ProjectType; public class TestConstraintPropagator extends BasePartitionerTestCase { private static AccessGraph agraph; private ConstraintPropagator cp; @Override protected void setUp() throws Exception { super.setUp(ProjectType.TM1, true); if (agraph == null) { agraph = AccessGraphGenerator.generateGlobal(this.info); agraph = AccessGraphGenerator.convertToSingleColumnEdges(catalog_db, agraph); agraph.setVerbose(true); // System.err.println("Dumping AccessGraph to " + FileUtil.writeStringToFile("/tmp/tpcc.dot", GraphvizExport.export(agraph, "tpcc"))); } assertNotNull(agraph); this.cp = new ConstraintPropagator(info, hints, agraph); } /** * testInitialize */ public void testInitialize() throws Exception { // System.err.println(this.cp.toString()); for (DesignerVertex v : agraph.getVertices()) { assertNotNull(cp.getEdgeColumns(v)); // System.err.println(v + " => " + cp.getEdgeColumns(v)); } boolean has_multi_vertex_edge = false; for (DesignerEdge e : agraph.getEdges()) { Set<DesignerVertex> vertices = new HashSet<DesignerVertex>(agraph.getIncidentVertices(e)); assertFalse(vertices.isEmpty()); has_multi_vertex_edge = has_multi_vertex_edge || (vertices.size() > 1); // if (vertices.size() > 1) System.err.println(e); } // FOR assert(has_multi_vertex_edge); } /** * testInitializeVerticalPartitioning */ public void testInitializeVerticalPartitioning() throws Exception { hints.enable_vertical_partitioning = true; cp = new ConstraintPropagator(info, hints, agraph); // Check that a vertical partition candidate column for SUBSCRIBER Table catalog_tbl = this.getTable(TM1Constants.TABLENAME_SUBSCRIBER); DesignerVertex v = agraph.getVertex(catalog_tbl); assertNotNull(v); Collection<VerticalPartitionColumn> vp_columns = new HashSet<VerticalPartitionColumn>(); for (Collection<Column> catalog_cols : cp.getEdgeColumns(v).values()) { for (Column catalog_col : catalog_cols) { if (catalog_col instanceof VerticalPartitionColumn) { vp_columns.add((VerticalPartitionColumn)catalog_col); } } // FOR } // FOR assertFalse(vp_columns.isEmpty()); // Make sure that each of these have a horizontal partition column // We expected to at least have a VerticalPartition with SUB_NBR in it Column expected_hp = this.getColumn(catalog_tbl, "S_ID"); Column expected_vp = this.getColumn(catalog_tbl, "SUB_NBR"); boolean found = false; for (VerticalPartitionColumn vp_col : vp_columns) { assertNotNull(vp_col); assertNotNull(vp_col.toString(), vp_col.getHorizontalColumn()); assertNotNull(vp_col.toString(), vp_col.getVerticalMultiColumn()); if (vp_col.getVerticalMultiColumn().contains(expected_vp) && vp_col.getHorizontalColumn().equals(expected_hp)) { assert(vp_col.getVerticalMultiColumn().contains(expected_hp)); assert(vp_col.hasOptimizedQueries()); found = true; } // System.err.println(vp_col.toString() + "\n"); } // FOR assert(found); } /** * testInitializeMultiAttribute */ public void testInitializeMultiAttribute() throws Exception { hints.enable_multi_partitioning = true; cp = new ConstraintPropagator(info, hints, agraph); // Check that we have multi-attribute candidates for ACCESS_INFO Table catalog_tbl = this.getTable(TM1Constants.TABLENAME_ACCESS_INFO); DesignerVertex v = agraph.getVertex(catalog_tbl); assertNotNull(v); Collection<MultiColumn> multi_columns = new HashSet<MultiColumn>(); for (Collection<Column> catalog_cols : cp.getEdgeColumns(v).values()) { for (Column catalog_col : catalog_cols) { if (catalog_col instanceof MultiColumn) { multi_columns.add((MultiColumn)catalog_col); } } // FOR } // FOR assertFalse(multi_columns.isEmpty()); } /** * testUpdateTwice */ public void testUpdateTwice() throws Exception { Table catalog_tbl = this.getTable(TM1Constants.TABLENAME_SUBSCRIBER); cp.update(catalog_tbl); try { cp.update(catalog_tbl); } catch (AssertionError ex) { return; } assert(false) : "Was allowed to update twice!"; } /** * testUpdateTable */ public void testUpdateTable() throws Exception { Set<Table> targets = new HashSet<Table>(); for (String table_name : new String[]{ TM1Constants.TABLENAME_SUBSCRIBER, TM1Constants.TABLENAME_ACCESS_INFO }) { Table catalog_tbl = this.getTable(table_name); cp.update(catalog_tbl); targets.add(catalog_tbl); } // FOR // Make sure that the columns that remain for the edges to our target tables // are only connected through the column that the target tables were partitioned on Collection<Column> columns; for (Table catalog_tbl : catalog_db.getTables()) { if (targets.contains(catalog_tbl) || catalog_tbl.getSystable()) continue; DesignerVertex v0 = agraph.getVertex(catalog_tbl); try { columns = cp.getCandidateValues(catalog_tbl, Column.class); } catch (IllegalArgumentException ex) { continue; } assertNotNull(columns); assertFalse(columns.isEmpty()); System.err.println(String.format("Examining %d columns for %s", columns.size(), catalog_tbl)); // For each column that is still available for this table, check to make sure that: // (1) It does not have an unmarked edge to one of our target tables // (2) If it is does have an edge to one of target tables then that edge uses the column that the table is partitioned on for (Column catalog_col : columns) { if (catalog_col instanceof ReplicatedColumn) continue; Collection<DesignerEdge> edges = agraph.findEdgeSet(v0, catalog_col); assertNotNull(catalog_col.fullName(), edges); assertFalse(catalog_col.fullName(), edges.isEmpty()); for (DesignerEdge e : edges) { if (cp.isMarked(e)) continue; DesignerVertex v1 = agraph.getOpposite(v0, e); assertNotNull(e.toString(), v1); Table other_tbl = v1.getCatalogItem(); assertNotNull(other_tbl); // Skip if not one of our target tables if (targets.contains(other_tbl) == false || other_tbl.getPartitioncolumn() == null) continue; // Get the ColumnSet, which should only have one entry PredicatePairs cset = e.getAttribute(EdgeAttributes.COLUMNSET); assertNotNull(cset); assertEquals(cset.toString(), 1, cset.size()); CatalogPair entry = cset.get(0); assertNotNull(entry); // Get the element from the entry that is not our column CatalogType other = entry.getOther(catalog_col); assertNotNull(other); if ((other instanceof Column) == false) continue; Column other_col = (Column)other; System.err.println(String.format("catalog_col=%s, other_tbl=%s, other_col=%s", catalog_col.fullName(), other_tbl.fullName(), other_col.fullName())); assertEquals(e.toString(), other_tbl, other_col.getParent()); // Make sure that the table's partitioning column matches assertEquals(String.format("%s<-->%s\n%s", catalog_tbl, other_tbl, e.toString()), other_tbl.getPartitioncolumn(), other_col); } // FOR (Edge) } // FOR (Column) } // FOR } /** * testUpdateTableVerticalPartitioning */ public void testUpdateTableVerticalPartitioning() throws Exception { hints.enable_vertical_partitioning = true; cp = new ConstraintPropagator(info, hints, agraph); // We're going to mark SUBSCRIBER as partitioned on S_ID and get the candidates for ACCESS_INFO Table catalog_tbl0 = this.getTable(TM1Constants.TABLENAME_SUBSCRIBER); Column catalog_col = this.getColumn(catalog_tbl0, "S_ID"); catalog_tbl0.setPartitioncolumn(catalog_col); cp.update(catalog_tbl0); Table catalog_tbl1 = this.getTable(TM1Constants.TABLENAME_ACCESS_INFO); Collection<Column> expected_cols = cp.getCandidateValues(catalog_tbl1, Column.class); assertNotNull(expected_cols); assertFalse(expected_cols.isEmpty()); // Get the VerticalPartitionColumn that uses S_ID as the horizontal partitioning Column // Make sure that the optimized query plans have not been applied yet Collection<VerticalPartitionColumn> vp_columns = cp.getVerticalPartitionColumns(catalog_tbl0); assertNotNull(vp_columns); VerticalPartitionColumn vp_col = null; for (VerticalPartitionColumn c : vp_columns) { if (c.getHorizontalColumn().equals(catalog_col)) { vp_col = c; break; } } // FOR assertNotNull(vp_col); assertFalse(vp_col.isUpdateApplied()); // Now partition SUBSCRIBER using its VerticalPartitionColumn cp.reset(catalog_tbl0); catalog_tbl0.setPartitioncolumn(vp_col); cp.update(catalog_tbl0); // And make sure that we get back the Columns we expect and that the Statements // have been updated Collection<Column> actual_cols = cp.getCandidateValues(catalog_tbl1, Column.class); assertNotNull(actual_cols); assertEquals(expected_cols.size(), actual_cols.size()); assertEquals(expected_cols, actual_cols); assertFalse(vp_col.isUpdateApplied()); // Revert catalog_tbl0.setPartitioncolumn(catalog_col); cp.reset(catalog_tbl0); } // /** // * testUpdateTableMultiAttribute // */ // public void testUpdateTableMultiAttribute() throws Exception { // Database clone_db = CatalogCloner.cloneDatabase(catalog_db); // info = this.generateInfo(clone_db); // hints.enable_multi_partitioning = true; // // designer = new Designer(info, hints, info.getArgs()); // RandomPartitioner partitioner = new RandomPartitioner(designer, info); // AccessGraph agraph = AccessGraphGenerator.convertToSingleColumnEdges(clone_db, partitioner.generateAccessGraph()); // cp = new ConstraintPropagator(info, hints, agraph); // // Table catalog_tbl0 = this.getTable(clone_db, TM1Constants.TABLENAME_CALL_FORWARDING); // Collection<MultiColumn> multi_columns = new HashSet<MultiColumn>(); // DesignerVertex v = agraph.getVertex(catalog_tbl0); // assertNotNull(catalog_tbl0.fullName(), v); // for (Collection<Column> catalog_cols : cp.getEdgeColumns(v).values()) { // for (Column catalog_col : catalog_cols) { // String catalog_key = CatalogKey.createKey(catalog_col); // assertNotNull(catalog_key); // catalog_col = CatalogKey.getFromKey(clone_db, catalog_key, Column.class); // assertNotNull(catalog_col); // // if (catalog_col instanceof MultiColumn) { // multi_columns.add((MultiColumn)catalog_col); // } // } // FOR // } // FOR // assertFalse(multi_columns.isEmpty()); // // // We're going to mark SPECIAL_FACILITY as updated using a particular Column that // // should cause all the MultiColumns for CALL_FORWADING to get invalidated // Table catalog_tbl1 = this.getTable(clone_db, TM1Constants.TABLENAME_SPECIAL_FACILITY); // catalog_tbl0.setPartitioncolumn(this.getColumn(catalog_tbl1, 2)); // cp.update(catalog_tbl1); // // // Make sure that the columns that remain for the edges to our target tables // // are only connected through the column that the target tables were partitioned on // Collection<Column> expected = CollectionUtil.addAll(new HashSet<Column>(), // this.getColumn(catalog_tbl0, "S_ID"), // this.getColumn(catalog_tbl0, "SF_TYPE")); // // Collection<Column> columns = cp.getCandidateValues(catalog_tbl0, Column.class); // assertNotNull(columns); // assertFalse(columns.isEmpty()); // for (Column catalog_col : columns) { // if (catalog_col instanceof MultiColumn) { // MultiColumn mc = (MultiColumn)catalog_col; // assertTrue(catalog_col.toString(), CollectionUtils.intersection(mc, expected).isEmpty()); // } // } // FOR // } /** * testResetTable */ public void testResetTable() throws Exception { // First update all of the tables for (Table catalog_tbl : catalog_db.getTables()) { try { cp.update(catalog_tbl); } catch (IllegalArgumentException ex) { continue; } } // FOR // Get the list of columns that we can use for CALL_FORWARDING Table catalog_tbl0 = this.getTable(TM1Constants.TABLENAME_CALL_FORWARDING); Collection<Column> orig_columns = new HashSet<Column>((Collection<Column>)cp.getCandidateValues(catalog_tbl0, Column.class)); assertFalse(catalog_tbl0.toString(), orig_columns.isEmpty()); // Reset both ACCESS_INFO and CALL_FORWARDING // This should give us more possible columns for CALL_FORWARDING Table catalog_tbl1 = this.getTable(TM1Constants.TABLENAME_ACCESS_INFO); cp.reset(catalog_tbl0); cp.reset(catalog_tbl1); // Then grab the list of colunms for ORDERS again. It should be larger than our original list // And the original list should be a subset Collection<Column> new_columns = cp.getCandidateValues(catalog_tbl0, Column.class); assert(new_columns.size() > orig_columns.size()) : orig_columns; assert(new_columns.containsAll(orig_columns)) : String.format("ORIG: %s\nNEW: %s", orig_columns, new_columns); } /** * testResetTwice */ public void testResetTwice() throws Exception { Table catalog_tbl = this.getTable(TM1Constants.TABLENAME_SUBSCRIBER); cp.update(catalog_tbl); cp.reset(catalog_tbl); try { cp.reset(catalog_tbl); } catch (AssertionError ex) { return; } assert(false) : "Was allowed to reset twice!"; } }