package edu.brown.catalog; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import org.apache.log4j.Logger; import org.voltdb.catalog.Catalog; import org.voltdb.catalog.CatalogMap; import org.voltdb.catalog.CatalogProxy; import org.voltdb.catalog.CatalogType; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Column; import org.voltdb.catalog.ColumnRef; import org.voltdb.catalog.ConflictPair; import org.voltdb.catalog.ConflictSet; import org.voltdb.catalog.Constraint; import org.voltdb.catalog.ConstraintRef; import org.voltdb.catalog.Database; import org.voltdb.catalog.Index; import org.voltdb.catalog.MaterializedViewInfo; 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.catalog.TableRef; import org.voltdb.types.ConstraintType; import edu.brown.catalog.special.MultiColumn; import edu.brown.catalog.special.MultiProcParameter; import edu.brown.catalog.special.ReplicatedColumn; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.utils.AbstractTreeWalker; public abstract class CatalogCloner { private static final Logger LOG = Logger.getLogger(CatalogCloner.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } /** * Clone and return the given catalog * * @param catalog_db * @return * @throws Exception */ public static Database cloneDatabase(Database catalog_db) throws Exception { assert (catalog_db != null); // Catalog clone_catalog = new Catalog(); // clone_catalog.execute(catalog_db.getCatalog().serialize()); // return (CatalogUtil.getDatabase(clone_catalog)); final Catalog clone_catalog = CatalogCloner.cloneBaseCatalog(catalog_db.getCatalog(), new ArrayList<Class<? extends CatalogType>>()); Database clone_db = CatalogUtil.getDatabase(clone_catalog); assert (!catalog_db.equals(clone_db)); // Need to also clone the MultiColumn guys too! for (Table catalog_tbl : catalog_db.getTables()) { Table clone_tbl = clone_db.getTables().get(catalog_tbl.getName()); for (Column catalog_col : catalog_tbl.getColumns()) { if (catalog_col instanceof MultiColumn) { MultiColumn mc = (MultiColumn) catalog_col; Column clone_cols[] = new Column[mc.size()]; for (int i = 0; i < clone_cols.length; i++) { clone_cols[i] = clone_tbl.getColumns().get(mc.get(i).getName()); } // FOR MultiColumn clone_mc = MultiColumn.get(clone_cols); assert (clone_mc != null); } } assert (catalog_tbl.getColumns().size() == clone_tbl.getColumns().size()) : catalog_tbl.getColumns() + " != " + clone_tbl.getColumns(); } // FOR // And don't forget MultiProcParameter! for (Procedure catalog_proc : catalog_db.getProcedures()) { Procedure clone_proc = clone_db.getProcedures().get(catalog_proc.getName()); for (ProcParameter catalog_param : catalog_proc.getParameters()) { if (catalog_param instanceof MultiProcParameter) { MultiProcParameter mpp = (MultiProcParameter) catalog_param; ProcParameter clone_params[] = new ProcParameter[mpp.size()]; for (int i = 0; i < clone_params.length; i++) { clone_params[i] = clone_proc.getParameters().get(mpp.get(i).getIndex()); } // FOR // This will automatically add our guy into clone_tbl MultiProcParameter clone_mpp = MultiProcParameter.get(clone_params); assert (clone_mpp != null); } } assert (catalog_proc.getParameters().size() == clone_proc.getParameters().size()) : catalog_proc.getParameters() + " != " + clone_proc.getParameters(); } // FOR return (clone_db); } /** * Clones the base components of a catalog. All underlying objects are recreated * @param catalog * @return */ public static Catalog cloneBaseCatalog(Catalog catalog) { HashSet<Class<? extends CatalogType>> skip_types = new HashSet<Class<? extends CatalogType>>(); skip_types.add(Table.class); // XXX: I don't remember why I did this instead of just re-executing Catalog.serialize()? return (CatalogCloner.cloneBaseCatalog(catalog, skip_types)); } public static Catalog cloneBaseCatalog(Catalog catalog, Class<? extends CatalogType>... skip_types) { return (CatalogCloner.cloneBaseCatalog(catalog, Arrays.asList(skip_types))); } /** * @param catalog * @param skip_types * @return */ public static Catalog cloneBaseCatalog(Catalog catalog, final Collection<Class<? extends CatalogType>> skip_types) { final Catalog new_catalog = new Catalog(); new AbstractTreeWalker<CatalogType>() { protected void populate_children(AbstractTreeWalker.Children<CatalogType> children, CatalogType element) { if (element instanceof Catalog) { children.addAfter(((Catalog) element).getClusters().values()); } else if (element instanceof Cluster) { children.addAfter(((Cluster) element).getDatabases().values()); children.addAfter(((Cluster) element).getHosts().values()); children.addAfter(((Cluster) element).getSites().values()); } else if (element instanceof Site) { children.addAfter(((Site) element).getPartitions().values()); } else if (element instanceof Database) { children.addAfter(((Database) element).getProcedures().values()); children.addAfter(((Database) element).getPrograms().values()); children.addAfter(((Database) element).getTables().values()); } else if (element instanceof Procedure) { for (ProcParameter param : ((Procedure) element).getParameters().values()) { if (!(param instanceof MultiProcParameter)) children.addAfter(param); } // FOR children.addAfter(((Procedure) element).getStatements().values()); } else if (element instanceof Statement) { children.addAfter(((Statement) element).getParameters().values()); children.addAfter(((Statement) element).getFragments().values()); children.addAfter(((Statement) element).getMs_fragments().values()); children.addAfter(((Statement) element).getOutput_columns().values()); } else if (element instanceof PlanFragment) { // children.addAfter(((PlanFragment)element).getDependencyids().values()); // children.addAfter(((PlanFragment)element).getOutputdependencyids().values()); } }; @Override protected void callback(CatalogType element) { if (element != null && !skip_types.contains(element.getClass())) CatalogCloner.clone(element, new_catalog); } }.traverse(catalog); Database orig_database = CatalogUtil.getDatabase(catalog); Database new_database = CatalogUtil.getDatabase(new_catalog); if (skip_types.contains(Table.class) == false && skip_types.contains(Column.class) == false) { // Clone constraints if they were not skipped if (skip_types.contains(Constraint.class) == false) { CatalogCloner.cloneConstraints(orig_database, new_database); } // Clone MaterializedViewes if they were not skipped // if (skip_types.contains(MaterializedViewInfo.class) == false) { // CatalogCloner.cloneViews(CatalogUtil.getDatabase(catalog), // CatalogUtil.getDatabase(new_catalog)); // } } // Clone Procedure conflicts if they were not skipped if (skip_types.contains(Procedure.class) == false) { CatalogCloner.cloneConflicts(orig_database, new_database); } return (new_catalog); } /** * Add a single catalog element from one catalog into the destination * catalog Note that this will not copy constraints for tables, since that * needs to be done later to ensure that any foreign key references are * included properly * * @param <T> * @param src_item * @param dest_catalog * @return */ @SuppressWarnings("unchecked") public static <T extends CatalogType> T clone(T src_item, Catalog dest_catalog) { StringBuilder buffer = new StringBuilder(); if (src_item instanceof MultiProcParameter) { LOG.warn(src_item + ": ??????????"); return (null); } CatalogProxy.writeCommands(src_item, buffer); dest_catalog.execute(buffer.toString()); T clone = (T) dest_catalog.getItemForRef(src_item.getPath()); // SPECIAL HANDLING // Table if (src_item instanceof Table) { Table src_tbl = (Table) src_item; Table dest_tbl = (Table) clone; // Columns for (Column src_col : src_tbl.getColumns()) { if (!(src_col instanceof MultiColumn)) CatalogCloner.clone(src_col, dest_catalog); } // FOR // Indexes for (Index src_idx : src_tbl.getIndexes()) { CatalogCloner.clone(src_idx, dest_catalog); } // FOR // MaterializedViews for (MaterializedViewInfo src_view : src_tbl.getViews()) { CatalogCloner.clone(src_view, dest_catalog); } // FOR // // Constraints // for (Constraint src_cons : ((Table)src).getConstraints()) { // CatalogUtil.clone(src_cons, dest_catalog); // } // FOR // Partitioning Column if (src_tbl.getPartitioncolumn() != null) { Column src_part_col = src_tbl.getPartitioncolumn(); Column dest_part_col = null; // Special Case: Replicated Column Marker if (src_part_col instanceof ReplicatedColumn) { dest_part_col = ReplicatedColumn.get(dest_tbl); // Special Case: MultiColumn } else if (src_part_col instanceof MultiColumn) { MultiColumn mc = (MultiColumn) src_part_col; Column dest_cols[] = new Column[mc.size()]; for (int i = 0; i < dest_cols.length; i++) { dest_cols[i] = dest_tbl.getColumns().get(mc.get(i).getName()); } // FOR dest_part_col = MultiColumn.get(dest_cols); } else { dest_part_col = dest_tbl.getColumns().get(src_part_col.getName()); } assert (dest_part_col != null) : "Missing partitioning column " + CatalogUtil.getDisplayName(src_part_col); dest_tbl.setPartitioncolumn(dest_part_col); } // MaterializedViewInfo } else if (src_item instanceof MaterializedViewInfo) { // ColumnRefs MaterializedViewInfo src_view = (MaterializedViewInfo) src_item; MaterializedViewInfo dest_view = (MaterializedViewInfo) clone; updateColumnsRefs((Table) src_view.getParent(), src_view.getGroupbycols(), (Table) dest_view.getParent(), dest_view.getGroupbycols()); // Index } else if (src_item instanceof Index) { // ColumnRefs Index src_idx = (Index) src_item; Index dest_idx = (Index) clone; updateColumnsRefs((Table) src_idx.getParent(), src_idx.getColumns(), (Table) dest_idx.getParent(), dest_idx.getColumns()); // Constraint } else if (src_item instanceof Constraint) { // ColumnRefs Constraint src_cons = (Constraint) src_item; Constraint dest_cons = (Constraint) clone; Table src_fkey_tbl = src_cons.getForeignkeytable(); if (src_fkey_tbl != null) { Database dest_db = (Database) dest_cons.getParent().getParent(); Table dest_fkey_tbl = dest_db.getTables().get(src_fkey_tbl.getName()); if (dest_fkey_tbl != null) { dest_cons.setForeignkeytable(dest_fkey_tbl); for (ColumnRef src_cref : ((Constraint) src_item).getForeignkeycols()) { CatalogCloner.clone(src_cref, dest_catalog); // Correct what it's pointing to ColumnRef dest_colref = dest_cons.getForeignkeycols().get(src_cref.getName()); assert (dest_colref != null); dest_colref.setColumn(dest_fkey_tbl.getColumns().get(src_cref.getColumn().getName())); } // FOR } } // Important: We have to add ConstraintRefs to Columns *after* we add the columns Table src_tbl = (Table) src_cons.getParent(); Table dest_tbl = (Table) dest_cons.getParent(); for (Column src_col : src_tbl.getColumns()) { Column dest_col = dest_tbl.getColumns().get(src_col.getName()); assert (dest_col != null); for (ConstraintRef src_conref : src_col.getConstraints()) { if (!src_conref.getConstraint().equals(src_cons)) continue; CatalogCloner.clone(src_conref, dest_catalog); // Correct what it's pointing to ConstraintRef dest_conref = dest_col.getConstraints().get(src_conref.getName()); assert (dest_conref != null); // System.out.println("dest_tbl: " + dest_tbl); // System.out.println("dest_tbl.getConstraints(): " + // CatalogUtil.debug(dest_tbl.getConstraints())); // System.out.println("src_confref: " + src_conref); // System.out.println("src_confref.getConstraint(): " + // src_conref.getConstraint()); dest_conref.setConstraint(dest_tbl.getConstraints().get(src_conref.getConstraint().getName())); } // FOR } // FOR Index src_index = src_cons.getIndex(); if (src_index != null) { Index dest_index = dest_tbl.getIndexes().get(src_index.getName()); dest_cons.setIndex(dest_index); } // StmtParameter } else if (src_item instanceof StmtParameter) { // We need to fix the reference to the ProcParameter (if one exists) StmtParameter src_stmt_param = (StmtParameter) src_item; StmtParameter dest_stmt_param = (StmtParameter) clone; if (src_stmt_param.getProcparameter() != null) { Procedure dest_proc = (Procedure) dest_stmt_param.getParent().getParent(); ProcParameter src_proc_param = src_stmt_param.getProcparameter(); ProcParameter dest_proc_param = dest_proc.getParameters().get(src_proc_param.getName()); if (dest_proc_param == null) { LOG.warn("dest_proc: " + dest_proc); LOG.warn("dest_stmt: " + dest_stmt_param.getParent()); LOG.warn("src_proc_param: " + src_proc_param); LOG.warn("dest_proc.getParameters(): " + CatalogUtil.debug(dest_proc.getParameters())); CatalogUtil.saveCatalog(dest_catalog, CatalogUtil.CATALOG_FILENAME); } assert (dest_proc_param != null); dest_stmt_param.setProcparameter(dest_proc_param); } } return (clone); } private static void updateColumnsRefs(Table src_tbl, CatalogMap<ColumnRef> src_refs, Table dest_tbl, CatalogMap<ColumnRef> dest_refs) { for (ColumnRef src_colref : src_refs) { // First clone it CatalogCloner.clone(src_colref, dest_tbl.getCatalog()); // Correct what it's pointing to ColumnRef dest_colref = dest_refs.get(src_colref.getName()); dest_colref.setColumn(dest_tbl.getColumns().get(src_colref.getColumn().getName())); } // FOR } /** * Clone Procedure conflicts * @param src_db * @param dest_db */ public static void cloneConflicts(Database src_db, Database dest_db) { for (Procedure src_proc : src_db.getProcedures()) { Procedure dest_proc = dest_db.getProcedures().get(src_proc.getName()); assert(dest_proc != null) : src_proc; for (ConflictSet src_conflicts : src_proc.getConflicts()) { Procedure dest_otherProc = dest_db.getProcedures().get(src_conflicts.getProcedure().getName()); ConflictSet dest_conflicts = dest_proc.getConflicts().add(src_conflicts.getName()); dest_conflicts.setProcedure(dest_otherProc); for (ConflictPair src_pair : src_conflicts.getReadwriteconflicts()) { ConflictPair dest_pair = clone(src_pair, dest_db.getCatalog()); dest_pair.setStatement0(dest_proc.getStatements().get(src_pair.getStatement0().getName())); dest_pair.setStatement1(dest_otherProc.getStatements().get(src_pair.getStatement1().getName())); for (TableRef src_ref : src_pair.getTables()) { TableRef dest_ref = dest_pair.getTables().add(src_ref.getName()); dest_ref.setTable(dest_db.getTables().get(src_ref.getTable().getName())); } // FOR } // FOR for (ConflictPair src_pair : src_conflicts.getWritewriteconflicts()) { ConflictPair dest_pair = clone(src_pair, dest_db.getCatalog()); dest_pair.setStatement0(dest_proc.getStatements().get(src_pair.getStatement0().getName())); dest_pair.setStatement1(dest_otherProc.getStatements().get(src_pair.getStatement1().getName())); for (TableRef src_ref : src_pair.getTables()) { TableRef dest_ref = dest_pair.getTables().add(src_ref.getName()); dest_ref.setTable(dest_db.getTables().get(src_ref.getTable().getName())); } // FOR } // FOR } } // FOR } /** * @param src_db * @param dest_db */ public static void cloneConstraints(Database src_db, Database dest_db) { Catalog dest_catalog = dest_db.getCatalog(); for (Table src_tbl : src_db.getTables()) { Table dest_tbl = dest_db.getTables().get(src_tbl.getName()); if (dest_tbl != null) { for (Constraint src_cons : src_tbl.getConstraints()) { // Only clone FKEY constraint if the other table is in the catalog ConstraintType cons_type = ConstraintType.get(src_cons.getType()); if (cons_type != ConstraintType.FOREIGN_KEY || (cons_type == ConstraintType.FOREIGN_KEY && dest_db.getTables().get(src_cons.getForeignkeytable().getName()) != null)) { Constraint dest_cons = clone(src_cons, dest_catalog); assert (dest_cons != null); } } // FOR } } // FOR } }