package edu.brown.gui.catalog; import java.util.ArrayList; 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 javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import org.apache.log4j.Logger; import org.voltdb.VoltType; import org.voltdb.catalog.Catalog; import org.voltdb.catalog.CatalogMap; import org.voltdb.catalog.CatalogType; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Column; import org.voltdb.catalog.ConflictPair; import org.voltdb.catalog.ConflictSet; import org.voltdb.catalog.Constraint; import org.voltdb.catalog.Database; import org.voltdb.catalog.Host; import org.voltdb.catalog.Index; import org.voltdb.catalog.MaterializedViewInfo; import org.voltdb.catalog.Partition; import org.voltdb.catalog.PlanFragment; import org.voltdb.catalog.ProcParameter; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Site; import org.voltdb.catalog.Statement; import org.voltdb.catalog.StmtParameter; import org.voltdb.catalog.Table; import org.voltdb.plannodes.AbstractPlanNode; import org.voltdb.types.ConflictType; import edu.brown.catalog.CatalogUtil; import edu.brown.plannodes.PlanNodeUtil; import edu.brown.utils.ArgumentsParser; import edu.brown.utils.CollectionUtil; import edu.brown.utils.StringUtil; /** * * @author pavlo * */ public class CatalogTreeModel extends DefaultTreeModel { private static final Logger LOG = Logger.getLogger(CatalogTreeModel.class); private static final long serialVersionUID = 1L; private final Catalog catalog; protected DefaultMutableTreeNode procedures_node; protected DefaultMutableTreeNode tables_node; protected ProcedureConflictGraphNode conflictgraph_node; protected final Set<Procedure> conflictGraphExcludes = new HashSet<Procedure>(); // ---------------------------------------------- // SEARCH INDEXES // ---------------------------------------------- protected final Map<Integer, DefaultMutableTreeNode> guid_node_xref = new HashMap<Integer, DefaultMutableTreeNode>(); protected final Map<String, Set<DefaultMutableTreeNode>> name_node_xref = new HashMap<String, Set<DefaultMutableTreeNode>>(); protected final Map<Integer, Set<DefaultMutableTreeNode>> plannode_node_xref = new HashMap<Integer, Set<DefaultMutableTreeNode>>(); public CatalogTreeModel(ArgumentsParser args, Catalog catalog, String catalog_path) { super(new DefaultMutableTreeNode(catalog.getName() + " [" + catalog_path + "]")); this.catalog = catalog; // Procedures to exclude in Conflict Graph if (args.hasParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_PROCEDURES)) { String param = args.getParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_PROCEDURES); Database catalog_db = CatalogUtil.getDatabase(this.catalog); for (String procName : param.split(",")) { Procedure catalog_proc = catalog_db.getProcedures().getIgnoreCase(procName); if (catalog_proc != null) { this.conflictGraphExcludes.add(catalog_proc); } else { LOG.warn("Invalid procedure name to exclude '" + procName + "'"); } } // FOR LOG.debug("Excluded ConflictGraph Procedures: " + this.conflictGraphExcludes); } this.buildModel(); } public Map<Integer, DefaultMutableTreeNode> getGuidNodeXref() { return this.guid_node_xref; } public Map<Integer, Set<DefaultMutableTreeNode>> getPlanNodeGuidNodeXref() { return this.plannode_node_xref; } public Map<String, Set<DefaultMutableTreeNode>> getNameNodeXref() { return this.name_node_xref; } public ProcedureConflictGraphNode getProcedureConflictGraphNode() { return (this.conflictgraph_node); } /** * @return the procedures_node */ public DefaultMutableTreeNode getProceduresNode() { return procedures_node; } /** * @return the tables_node */ public DefaultMutableTreeNode getTablesNode() { return tables_node; } /** * Adds new information to the search index * @param catalog_obj * @param node */ protected void buildSearchIndex(CatalogType catalog_obj, DefaultMutableTreeNode node) { //this.guid_node_xref.put(catalog_obj.getGuid(), node); List<String> keys = new ArrayList<String>(); keys.add(catalog_obj.getName()); // Add the SQL statements if (catalog_obj instanceof Statement) { Statement catalog_stmt = (Statement)catalog_obj; keys.add(catalog_stmt.getSqltext()); } for (String k : keys) { k = k.toLowerCase(); if (!this.name_node_xref.containsKey(k)) { this.name_node_xref.put(k, new HashSet<DefaultMutableTreeNode>()); } this.name_node_xref.get(k).add(node); } // FOR if (catalog_obj instanceof Statement) { Statement catalog_stmt = (Statement)catalog_obj; try { AbstractPlanNode root = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, false); for (Integer guid : PlanNodeUtil.getAllPlanColumnGuids(root)) { if (this.plannode_node_xref.containsKey(guid) == false) { this.plannode_node_xref.put(guid, new HashSet<DefaultMutableTreeNode>()); } this.plannode_node_xref.get(guid).add(node); } // FOR } catch (Exception ex) { ex.printStackTrace(); System.exit(1); } } } /** * */ protected void buildModel() { Map<Table, MaterializedViewInfo> vertical_partitions = CatalogUtil.getVerticallyPartitionedTables(catalog); // Clusters DefaultMutableTreeNode root_node = (DefaultMutableTreeNode)this.getRoot(); int cluster_ctr = 0; for (Cluster cluster_cat : catalog.getClusters()) { DefaultMutableTreeNode cluster_node = new DefaultMutableTreeNode(new WrapperNode(cluster_cat)); this.insertNodeInto(cluster_node, root_node, cluster_ctr++); buildSearchIndex(cluster_cat, cluster_node); // Databases for (Database database_cat : cluster_cat.getDatabases()) { DefaultMutableTreeNode database_node = new DefaultMutableTreeNode(new WrapperNode(database_cat)); cluster_node.add(database_node); buildSearchIndex(database_cat, database_node); // Tables tables_node = new CatalogMapTreeNode(Table.class, "Tables", database_cat.getTables()); database_node.add(tables_node); // List data tables first, views second List<Table> tables = new ArrayList<Table>(); tables.addAll(CatalogUtil.getDataTables(database_cat)); tables.addAll(CatalogUtil.getViewTables(database_cat)); for (Table catalog_tbl : tables) { WrapperNode wrapper = null; if (catalog_tbl.getMaterializer() != null) { wrapper = new WrapperNode(catalog_tbl, "VIEW:"+catalog_tbl.getName()); } else { wrapper = new WrapperNode(catalog_tbl); } DefaultMutableTreeNode table_node = new DefaultMutableTreeNode(wrapper); tables_node.add(table_node); buildSearchIndex(catalog_tbl, table_node); // Columns DefaultMutableTreeNode columns_node = new CatalogMapTreeNode(Column.class, "Columns", catalog_tbl.getColumns()); table_node.add(columns_node); for (Column catalog_col : CatalogUtil.getSortedCatalogItems(catalog_tbl.getColumns(), "index")) { DefaultMutableTreeNode column_node = new DefaultMutableTreeNode(new WrapperNode(catalog_col) { @Override public String toString() { Column column_cat = (Column)this.getCatalogType(); String type = VoltType.get((byte)column_cat.getType()).toSQLString(); return (super.toString() + " (" + type + ")"); } }); columns_node.add(column_node); buildSearchIndex(catalog_col, column_node); } // FOR (columns) // Indexes if (!catalog_tbl.getIndexes().isEmpty()) { DefaultMutableTreeNode indexes_node = new CatalogMapTreeNode(Index.class, "Indexes", catalog_tbl.getIndexes()); table_node.add(indexes_node); for (Index catalog_idx : catalog_tbl.getIndexes()) { DefaultMutableTreeNode index_node = new DefaultMutableTreeNode(new WrapperNode(catalog_idx)); indexes_node.add(index_node); buildSearchIndex(catalog_idx, index_node); } // FOR (indexes) } // Constraints if (!catalog_tbl.getConstraints().isEmpty()) { DefaultMutableTreeNode constraints_node = new CatalogMapTreeNode(Constraint.class, "Constraints", catalog_tbl.getConstraints()); table_node.add(constraints_node); for (Constraint catalog_cnst : catalog_tbl.getConstraints()) { DefaultMutableTreeNode constraint_node = new DefaultMutableTreeNode(new WrapperNode(catalog_cnst)); constraints_node.add(constraint_node); buildSearchIndex(catalog_cnst, constraint_node); } // FOR (constraints) } // Vertical Partitions final MaterializedViewInfo catalog_vp = vertical_partitions.get(catalog_tbl); if (catalog_vp != null) { DefaultMutableTreeNode vp_node = new CatalogMapTreeNode(MaterializedViewInfo.class, "Vertical Partition", catalog_vp.getGroupbycols()); table_node.add(vp_node); for (Column catalog_col : CatalogUtil.getSortedCatalogItems(CatalogUtil.getColumns(catalog_vp.getGroupbycols()), "index")) { DefaultMutableTreeNode column_node = new DefaultMutableTreeNode(new WrapperNode(catalog_col) { @Override public String toString() { Column column_cat = (Column)this.getCatalogType(); String type = VoltType.get((byte)column_cat.getType()).toSQLString(); return (String.format("%s.%s (%s)", catalog_vp.getName(), super.toString(), type)); } }); vp_node.add(column_node); // buildSearchIndex(catalog_col, column_node); } // FOR } } // FOR (tables) // System Stored Procedures Collection<Procedure> sysProcs = CatalogUtil.getSysProcedures(database_cat); procedures_node = new CatalogMapTreeNode(Procedure.class, "System Procedures", sysProcs.size()); database_node.add(procedures_node); this.buildProceduresTree(procedures_node, sysProcs); // Benchmark Stored Procedures List<Procedure> procs = new ArrayList<Procedure>(database_cat.getProcedures()); procs.removeAll(sysProcs); procedures_node = new CatalogMapTreeNode(Procedure.class, "Stored Procedures", procs.size()); database_node.add(procedures_node); // Conflicts Graph // Remove anything that should be excluded Set<Procedure> conflictProcs = new HashSet<Procedure>(procs); conflictProcs.removeAll(this.conflictGraphExcludes); this.conflictgraph_node = new ProcedureConflictGraphNode(conflictProcs); DefaultMutableTreeNode conflictNode = new DefaultMutableTreeNode(this.conflictgraph_node); this.procedures_node.add(conflictNode); this.buildProceduresTree(this.procedures_node, procs); } // FOR (databases) // Construct a xref mapping between host->sites and site->partitions Map<Host, Set<Site>> host_site_xref = new HashMap<Host, Set<Site>>(); Map<Site, Collection<Partition>> site_partition_xref = new HashMap<Site, Collection<Partition>>(); for (Site site_cat : cluster_cat.getSites()) { Host host_cat = site_cat.getHost(); if (!host_site_xref.containsKey(host_cat)) { host_site_xref.put(host_cat, new HashSet<Site>()); } host_site_xref.get(host_cat).add(site_cat); Collection<Partition> partitions = CollectionUtil.addAll(new HashSet<Partition>(), site_cat.getPartitions()); site_partition_xref.put(site_cat, partitions); } // FOR // Hosts DefaultMutableTreeNode hosts_node = new CatalogMapTreeNode(Host.class, "Hosts", cluster_cat.getHosts()); cluster_node.add(hosts_node); for (Host host_cat : cluster_cat.getHosts()) { DefaultMutableTreeNode host_node = new DefaultMutableTreeNode(new WrapperNode(host_cat, host_cat.getIpaddr())); hosts_node.add(host_node); buildSearchIndex(host_cat, host_node); // Sites if (host_site_xref.containsKey(host_cat)) { for (Site site_cat : host_site_xref.get(host_cat)) { DefaultMutableTreeNode site_node = new DefaultMutableTreeNode(new WrapperNode(site_cat, true)); host_node.add(site_node); buildSearchIndex(site_cat, site_node); // Partitions if (site_partition_xref.containsKey(site_cat)) { for (Partition part_cat : site_partition_xref.get(site_cat)) { DefaultMutableTreeNode part_node = new DefaultMutableTreeNode(new WrapperNode(part_cat, true)); site_node.add(part_node); buildSearchIndex(part_cat, part_node); } // FOR } } // FOR } } // FOR } // FOR (clusters) } private void buildProceduresTree(DefaultMutableTreeNode parentNode, Collection<Procedure> procedures) { for (Procedure catalog_proc : procedures) { DefaultMutableTreeNode procNode = new DefaultMutableTreeNode(new WrapperNode(catalog_proc)); parentNode.add(procNode); buildSearchIndex(catalog_proc, procNode); // Parameters DefaultMutableTreeNode parameters_node = new CatalogMapTreeNode(ProcParameter.class, "Parameters", catalog_proc.getParameters()); procNode.add(parameters_node); for (ProcParameter param_cat : CatalogUtil.getSortedCatalogItems(catalog_proc.getParameters(), "index")) { DefaultMutableTreeNode param_node = new DefaultMutableTreeNode(new WrapperNode(param_cat) { @Override public String toString() { ProcParameter param_cat = (ProcParameter)this.getCatalogType(); VoltType type = VoltType.get((byte)param_cat.getType()); return (super.toString() + " :: " + type.name()); } }); parameters_node.add(param_node); buildSearchIndex(param_cat, param_node); } // FOR (parameters) // Statements if (catalog_proc.getSystemproc() == false) { DefaultMutableTreeNode statementRootNode = new CatalogMapTreeNode(Statement.class, "Statements", catalog_proc.getStatements()); procNode.add(statementRootNode); for (Statement statement_cat : catalog_proc.getStatements()) { DefaultMutableTreeNode statement_node = new DefaultMutableTreeNode(new WrapperNode(statement_cat)); statementRootNode.add(statement_node); buildSearchIndex(statement_cat, statement_node); // Plan Trees for (boolean is_singlepartition : new boolean[] { true, false }) { if (is_singlepartition && !statement_cat.getHas_singlesited()) continue; if (!is_singlepartition && !statement_cat.getHas_multisited()) continue; String label = (is_singlepartition ? "Single-Partition" : "Distributed") + " Plan Fragments"; // String attributes = ""; AbstractPlanNode node = null; try { node = PlanNodeUtil.getRootPlanNodeForStatement(statement_cat, is_singlepartition); } catch (Exception e) { String msg = e.getMessage(); if (msg == null || msg.length() == 0) { e.printStackTrace(); } else { LOG.warn(msg); } } CatalogMap<PlanFragment> fragments = (is_singlepartition ? statement_cat.getFragments() : statement_cat.getMs_fragments()); PlanTreeCatalogNode planTreeNode = new PlanTreeCatalogNode(label, fragments, node); DefaultMutableTreeNode planNode = new DefaultMutableTreeNode(planTreeNode); statement_node.add(planNode); // Plan Fragments for (PlanFragment fragment_cat : CatalogUtil.getSortedCatalogItems(fragments, "id")) { DefaultMutableTreeNode fragment_node = new DefaultMutableTreeNode(new WrapperNode(fragment_cat)); planNode.add(fragment_node); buildSearchIndex(fragment_cat, fragment_node); } // FOR (fragments) } // Statement Parameter DefaultMutableTreeNode paramRootNode = new CatalogMapTreeNode(StmtParameter.class, "Parameters", statement_cat.getParameters()); statement_node.add(paramRootNode); for (StmtParameter param_cat : CatalogUtil.getSortedCatalogItems(statement_cat.getParameters(), "index")) { DefaultMutableTreeNode param_node = new DefaultMutableTreeNode(new WrapperNode(param_cat) { @Override public String toString() { StmtParameter param_cat = (StmtParameter)this.getCatalogType(); VoltType type = VoltType.get((byte)param_cat.getJavatype()); return (super.toString() + " :: " + type.name()); } }); paramRootNode.add(param_node); buildSearchIndex(param_cat, param_node); } // FOR (parameters) // Output Columns DefaultMutableTreeNode columnRootNode = new DefaultMutableTreeNode("Output Columns"); statement_node.add(columnRootNode); for (Column column_cat : statement_cat.getOutput_columns()) { DefaultMutableTreeNode column_node = new DefaultMutableTreeNode(new WrapperNode(column_cat) { @Override public String toString() { Column column_cat = (Column)this.getCatalogType(); String type = VoltType.get((byte)column_cat.getType()).toSQLString(); return (super.toString() + " (" + type + ")"); } }); columnRootNode.add(column_node); buildSearchIndex(column_cat, column_node); } // FOR (output columns) } // FOR (statements) // Conflicts if (catalog_proc.getConflicts().isEmpty() == false) { DefaultMutableTreeNode conflictRootNode = new CatalogMapTreeNode(ConflictSet.class, "Conflicts", catalog_proc.getConflicts()); procNode.add(conflictRootNode); Database catalog_db = CatalogUtil.getDatabase(catalog_proc); for (ConflictSet conflicts : catalog_proc.getConflicts()) { final Procedure other = catalog_db.getProcedures().getIgnoreCase(conflicts.getName()); assert(other != null) : "Invalid conflict procedure name '" + conflicts.getName() + "'"; String attrText = ""; // READ-WRITE CONFLICTS attrText += this.formatConflictSet(conflicts.getReadwriteconflicts().values(), ConflictType.READ_WRITE); // WRITE-WRITE CONFLICTS attrText += this.formatConflictSet(conflicts.getWritewriteconflicts().values(), ConflictType.WRITE_WRITE); AttributesNode conflict_node = new AttributesNode(other.getName(), attrText); conflictRootNode.add(new DefaultMutableTreeNode(conflict_node)); } // FOR } // SYSPROC } } // FOR (procedures) } private String formatConflictSet(ConflictPair conflicts[], ConflictType conflictType) { StringBuilder sb = new StringBuilder(); sb.append(StringUtil.header(conflictType.name().toUpperCase() + " CONFLICTS")).append("\n"); if (conflicts.length == 0) { sb.append("<NONE>\n\n"); } else { int ctr = 0; for (ConflictPair cp : conflicts) { sb.append(String.format("[%02d] %s -> %s\n", ctr++, cp.getStatement0().fullName(), cp.getStatement1().fullName())); sb.append(String.format(" Always Conflict: %s\n", cp.getAlwaysconflicting())); sb.append(String.format(" Tables: %s\n", CatalogUtil.getDisplayNames(CatalogUtil.getTablesFromRefs(cp.getTables())))); sb.append("\n"); } // FOR } return (sb.toString()); } }