Package edu.brown.catalog

Source Code of edu.brown.catalog.CatalogUtil

package edu.brown.catalog;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import org.voltdb.CatalogContext;
import org.voltdb.VoltTable;
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.ColumnRef;
import org.voltdb.catalog.ConstantValue;
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.catalog.TableRef;
import org.voltdb.compiler.JarBuilder;
import org.voltdb.expressions.AbstractExpression;
import org.voltdb.expressions.AbstractValueExpression;
import org.voltdb.expressions.ComparisonExpression;
import org.voltdb.expressions.ConjunctionExpression;
import org.voltdb.expressions.ConstantValueExpression;
import org.voltdb.expressions.InComparisonExpression;
import org.voltdb.expressions.ParameterValueExpression;
import org.voltdb.expressions.TupleAddressExpression;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.planner.PlanColumn;
import org.voltdb.planner.PlannerContext;
import org.voltdb.plannodes.AbstractJoinPlanNode;
import org.voltdb.plannodes.AbstractOperationPlanNode;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.AbstractScanPlanNode;
import org.voltdb.plannodes.IndexScanPlanNode;
import org.voltdb.plannodes.InsertPlanNode;
import org.voltdb.plannodes.MaterializePlanNode;
import org.voltdb.plannodes.NestLoopIndexPlanNode;
import org.voltdb.plannodes.OrderByPlanNode;
import org.voltdb.plannodes.ProjectionPlanNode;
import org.voltdb.plannodes.SeqScanPlanNode;
import org.voltdb.plannodes.UpdatePlanNode;
import org.voltdb.types.ConstraintType;
import org.voltdb.types.ExpressionType;
import org.voltdb.types.PlanNodeType;
import org.voltdb.types.QueryType;
import org.voltdb.utils.Encoder;
import org.voltdb.utils.JarReader;
import org.voltdb.utils.Pair;

import edu.brown.catalog.special.NullProcParameter;
import edu.brown.catalog.special.ReplicatedColumn;
import edu.brown.catalog.special.SpecialProcParameter;
import edu.brown.expressions.ExpressionTreeWalker;
import edu.brown.expressions.ExpressionUtil;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.plannodes.PlanNodeTreeWalker;
import edu.brown.plannodes.PlanNodeUtil;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.FileUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.PredicatePairs;
import edu.brown.utils.StringUtil;

/**
* @author pavlo
*/
public abstract class CatalogUtil extends org.voltdb.utils.CatalogUtil {
    private static final Logger LOG = Logger.getLogger(CatalogUtil.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug, trace);
    }

    public static final String DEFAULT_CLUSTER_NAME = "cluster";
    public static final String DEFAULT_DATABASE_NAME = "database";
    public static final String DEFAULT_PROCEDURE_NAME = "procedure";
    public static final String DEFAULT_STATEMENT_NAME = "statement";

    // ------------------------------------------------------------
    // CACHES
    // ------------------------------------------------------------

    @Deprecated
    private static class Cache {

        /**
         * The set of read-only columns excluding inserts
         */
        private final Map<Table, Set<Column>> READONLY_COLUMNS_NO_INSERTS = new HashMap<Table, Set<Column>>();
        /**
         * The set of read-only columns including inserts
         */
        private final Map<Table, Set<Column>> READONLY_COLUMNS_ALL = new HashMap<Table, Set<Column>>();
        /**
         * The set of Columns referenced for each Statement Statement ->
         * Set<Column>
         */
        private final Map<Statement, Set<Column>> STATEMENT_ALL_COLUMNS = new HashMap<Statement, Set<Column>>();
        /**
         * The set of Columns referenced modified in each Statement Statement ->
         * Set<Column>
         */
        private final Map<Statement, Set<Column>> STATEMENT_MODIFIED_COLUMNS = new HashMap<Statement, Set<Column>>();
        /**
         * The set of Columns that read-only in each Statement Statement ->
         * Set<Column>
         */
        private final Map<Statement, Collection<Column>> STATEMENT_READONLY_COLUMNS = new HashMap<Statement, Collection<Column>>();
        /**
         * The set of Columns that are used in the ORDER BY clause of the query
         * Statement -> Set<Column>
         */
        private final Map<Statement, Set<Column>> STATEMENT_ORDERBY_COLUMNS = new HashMap<Statement, Set<Column>>();
        /**
         * Statement -> Set<Table>
         */
        private final Map<Statement, Collection<Table>> STATEMENT_TABLES = new HashMap<Statement, Collection<Table>>();
        /**
         * Procedure -> Set<Column>
         */
        private final Map<Procedure, Set<Column>> PROCEDURE_COLUMNS = new HashMap<Procedure, Set<Column>>();
        /**
         * Procedure -> Set
         * <Table>
         */
        private final Map<Procedure, Set<Table>> PROCEDURE_TABLES = new HashMap<Procedure, Set<Table>>();
        /**
         * PartitionId -> Partition
         */
        private final ListOrderedMap<Integer, Partition> PARTITION_XREF = new ListOrderedMap<Integer, Partition>();
        /**
         * Host -> Set<Site>
         */
        private final Map<Host, Set<Site>> HOST_SITES = new TreeMap<Host, Set<Site>>(new CatalogFieldComparator<Host>("ipaddr"));
        /**
         * Column -> Foreign Key Parent Column
         */
        private final Map<Column, Column> FOREIGNKEY_PARENT = new HashMap<Column, Column>();
        /**
         * SiteId -> Set<<Host, Port>>
         */
        private final Map<Integer, Set<Pair<String, Integer>>> EXECUTION_SITES = new HashMap<Integer, Set<Pair<String, Integer>>>();

        private final Map<Pair<String, Collection<String>>, PredicatePairs> EXTRACTED_PREDICATES = new HashMap<Pair<String, Collection<String>>, PredicatePairs>();

        /**
         * Construct the internal PARTITION_XREF cache map
         *
         * @param cache
         * @param catalog_item
         */
        private synchronized void buildPartitionCache(CatalogType catalog_item) {
            Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
            for (Site catalog_site : catalog_clus.getSites()) {
                for (Partition catalog_part : catalog_site.getPartitions()) {
                    this.PARTITION_XREF.put(catalog_part.getId(), catalog_part);
                } // FOR
            } // FOR
        }

        private synchronized void buildReadOnlyColumnCache(CatalogType catalog_item) {
            Database catalog_db = CatalogUtil.getDatabase(catalog_item);

            Set<Column> all_modified = new HashSet<Column>();
            Set<Column> all_modified_no_inserts = new HashSet<Column>();

            for (Procedure catalog_proc : catalog_db.getProcedures()) {
                for (Statement catalog_stmt : catalog_proc.getStatements()) {
                    QueryType qtype = QueryType.get(catalog_stmt.getQuerytype());
                    if (qtype == QueryType.SELECT)
                        continue;

                    // Get the columns that referenced by this Statement
                    CatalogUtil.getReferencedColumns(catalog_stmt);
                    Set<Column> modified_cols = STATEMENT_MODIFIED_COLUMNS.get(catalog_stmt);
                    assert (modified_cols != null) : "Failed to get modified columns for " + catalog_stmt.fullName();
                    all_modified.addAll(modified_cols);
                    if (debug.val)
                        LOG.debug("ALL - " + catalog_stmt.fullName() + ": " + modified_cols);
                    if (qtype != QueryType.INSERT) {
                        all_modified_no_inserts.addAll(modified_cols);
                        if (debug.val)
                            LOG.debug("NOINSERT - " + catalog_stmt.fullName() + ": " + modified_cols);
                    }
                } // FOR (stmt)
            } // FOR (proc)

            for (Table catalog_tbl : catalog_db.getTables()) {
                Set<Column> readonly_with_inserts = new TreeSet<Column>();
                Set<Column> readonly_no_inserts = new TreeSet<Column>();

                for (Column catalog_col : catalog_tbl.getColumns()) {
                    // If this Column was not modified at all, then we can
                    // include it is
                    // in both READONLY_WITH_INSERTS and READONLY_NO_INSERTS
                    if (all_modified.contains(catalog_col) == false) {
                        readonly_with_inserts.add(catalog_col);
                        readonly_no_inserts.add(catalog_col);
                        // If this Column was modified but not by any non-INSERT
                        // query, then
                        // it is READONLY_NO_INSERTS
                    } else if (all_modified_no_inserts.contains(catalog_col) == false) {
                        readonly_no_inserts.add(catalog_col);
                    }
                } // FOR (col)

                if (debug.val)
                    LOG.debug(String.format("%s: READONLY_ALL%s - READONLY_NOINSERT%s", catalog_tbl.getName(), readonly_with_inserts, readonly_no_inserts));

                READONLY_COLUMNS_ALL.put(catalog_tbl, readonly_with_inserts);
                READONLY_COLUMNS_NO_INSERTS.put(catalog_tbl, readonly_no_inserts);
            } // FOR (tbl)
        }

    }

    private static final Map<Database, CatalogUtil.Cache> CACHE = new HashMap<Database, CatalogUtil.Cache>();

    /**
     * Get the Cache handle for the Database catalog object If one doesn't exist
     * yet, it will be created
     *
     * @param catalog_item
     * @return
     */
    private static CatalogUtil.Cache getCatalogCache(CatalogType catalog_item) {
        final Database catalog_db = (catalog_item instanceof Database ? (Database) catalog_item : CatalogUtil.getDatabase(catalog_item));
        CatalogUtil.Cache ret = CACHE.get(catalog_db);
        if (ret == null) {
            ret = new CatalogUtil.Cache();
            CACHE.put(catalog_db, ret);
        }
        assert (ret != null) : "Failed to cache for " + catalog_item.fullName();
        return (ret);
    }

    public static void preload(CatalogType catalog_obj) {
        assert (catalog_obj != null);
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);

        // Pre-load all the arrays for the CatalogMaps that we will access
        // frequently
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            catalog_proc.getStatements().values();
            for (Statement catalog_stmt : catalog_proc.getStatements()) {
                catalog_stmt.getFragments().values();
                catalog_stmt.getMs_fragments().values();
            } // STATEMENT
        } // PROCEDURE
    }

    public static void clearCache(CatalogType catalog_obj) {
        assert (catalog_obj != null);
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        CACHE.remove(catalog_db);
    }

    // ------------------------------------------------------------
    // COMPARATOR
    // ------------------------------------------------------------

    /**
     * Loads a serialized catalog specification from a jar file and creates a
     * new CatalogContext object from it
     * @param jar_path
     */
    public static CatalogContext loadCatalogContextFromJar(File jar_path) {
        Catalog catalog = CatalogUtil.loadCatalogFromJar(jar_path);
        return new CatalogContext(catalog, jar_path);
    }
   
    /**
     * Loads a serialized catalog specification from a jar file and creates a
     * new Catalog object from it
     *
     * @param jar_path
     * @return
     */
    @Deprecated
    public static Catalog loadCatalogFromJar(String jar_path) {
        return loadCatalogFromJar(new File(jar_path));
    }
   
    /**
     * Loads a serialized catalog specification from a jar file and creates a
     * new Catalog object from it
     *
     * @param jar_path
     * @return
     */
    public static Catalog loadCatalogFromJar(File jar_path) {
        assert(jar_path != null) : "Unexpected null jar path";
        if (debug.val)
            LOG.debug("Loading catalog from jar file at '" + jar_path.getAbsolutePath() + "'");
        if (!jar_path.exists()) {
            LOG.error("The catalog jar file '" + jar_path + "' does not exist");
            return (null);
        }
       
        Catalog catalog = null;
        String serializedCatalog = null;
        try {
            serializedCatalog = JarReader.readFileFromJarfile(jar_path.getAbsolutePath(), CatalogUtil.CATALOG_FILENAME);
        } catch (Exception ex) {
            ex.printStackTrace();
            return (null);
        }
        if (serializedCatalog == null) {
            LOG.warn("The catalog file '" + CatalogUtil.CATALOG_FILENAME + "' in jar file '" + jar_path + "' is null");
        } else if (serializedCatalog.isEmpty()) {
            LOG.warn("The catalog file '" + CatalogUtil.CATALOG_FILENAME + "' in jar file '" + jar_path + "' is empty");
        } else {
            catalog = new Catalog();
            if (debug.val)
                LOG.debug("Extracted file '" + CatalogUtil.CATALOG_FILENAME + "' from jar file '" + jar_path + "'");
            catalog.execute(serializedCatalog);
        }
        return (catalog);
    }

    /**
     * Loads a serialized catalog specification from a text file and creates a
     * new Catalog object from it
     *
     * @param jar_path
     * @return
     */
    public static Catalog loadCatalog(String path) {
        Catalog catalog = null;
        String serializedCatalog = null;
        try {
            serializedCatalog = FileUtil.readFile(path);
        } catch (Exception ex) {
            ex.printStackTrace();
            return (null);
        }
        if (serializedCatalog == null) {
            LOG.warn("The catalog file '" + CatalogUtil.CATALOG_FILENAME + "' in file '" + path + "' is null");
        } else if (serializedCatalog.isEmpty()) {
            LOG.warn("The catalog file '" + CatalogUtil.CATALOG_FILENAME + "' in file '" + path + "' is empty");
        } else {
            catalog = new Catalog();
            LOG.debug("Executing catalog from file '" + path + "'");
            catalog.execute(serializedCatalog);
        }
        return (catalog);
    }

    /**
     * @param catalog
     * @param file_path
     */
    public static File saveCatalog(Catalog catalog, String file_path) {
        File file = new File(file_path);
        try {
            FileUtil.writeStringToFile(file, catalog.serialize());
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
        LOG.info("Wrote catalog contents to '" + file.getAbsolutePath() + "'");
        return (file);
    }

    /**
     * Updates the catalog file in the Jar.
     *
     * @param jarFileName
     * @param catalog
     * @throws Exception (VoltCompilerException DNE?)
     */
    public static void updateCatalogInJar(File jarFileName, Catalog catalog, File...additions) throws Exception {
        catalog.serialize();
        // Read the old jar file into memory with JarReader.
        JarReader reader = new JarReader(jarFileName.getAbsolutePath());
        List<String> files = reader.getContentsFromJarfile();
        ArrayList<byte[]> bytes = new ArrayList<byte[]>();
        for (String file : files) {
            bytes.add(JarReader.readFileFromJarAtURL(jarFileName.getAbsolutePath(), file));
        }
       
        // Write everything from the old jar except the catalog to the same
        // file with JarBuilder.
        JarBuilder builder = new JarBuilder(null);
        for (int i = 0; i < files.size(); ++i) {
            String file = files.get(i);
            if (file.equals(CatalogUtil.CATALOG_FILENAME)) {
                builder.addEntry(CatalogUtil.CATALOG_FILENAME,
                                 catalog.serialize().getBytes());
            }
            else {
                builder.addEntry(file, bytes.get(i));
            }
        }
       
        // Add any additions that they want to the root of the the jar structure
        for (File f : additions) {
            if (f != null) {
                builder.addEntry(f.getName(), FileUtil.readBytesFromFile(f.getAbsolutePath()));
            }
        } // FOR
       
        builder.writeJarToDisk(jarFileName.getAbsolutePath());
    }
   
    /**
     * @param items
     * @return
     */
    public static Map<Object, String> getHistogramLabels(Iterable<? extends CatalogType> items) {
        Map<Object, String> labels = new HashMap<Object, String>();
        for (CatalogType ct : items) {
            labels.put(ct, CatalogUtil.getDisplayName(ct));
        } // FOR
        return (labels);
    }

    // ------------------------------------------------------------
    // CLUSTER + DATABASE
    // ------------------------------------------------------------

    /**
     * Convenience method to return the default database object from a catalog
     *
     * @param catalog
     * @return
     */
    public static Database getDatabase(CatalogType catalog_item) {
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        assert (catalog_clus != null);
        return (catalog_clus.getDatabases().get(DEFAULT_DATABASE_NAME));
    }

    /**
     * Convenience method to return the default cluster object from a catalog
     *
     * @param catalog
     * @return
     */
    public static Cluster getCluster(CatalogType catalog_item) {
        assert (catalog_item != null) : "Null Catalog Item!";
        if (catalog_item instanceof Cluster)
            return ((Cluster) catalog_item);
        Catalog catalog = catalog_item.getCatalog();
        assert (catalog != null) : catalog_item + " is missing its catalog?";
        return (catalog.getClusters().get(DEFAULT_CLUSTER_NAME));
    }

    // ------------------------------------------------------------
    // PROCEDURES + STATEMENTS + PARAMETERS
    // ------------------------------------------------------------

    /**
     * Return all of the internal system Procedures for the database
     */
    @Deprecated
    public static Collection<Procedure> getSysProcedures(Database catalog_db) {
        Collection<Procedure> procs = new ArrayList<Procedure>();
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            if (catalog_proc.getSystemproc()) {
                assert(procs.contains(catalog_proc) == false);
                procs.add(catalog_proc);
            }
        }
        return (procs);
    }

    /**
     * Return all of the MapReduce Procedures for the database
     */
    @Deprecated
    public static Collection<Procedure> getMapReduceProcedures(Database catalog_db) {
        Collection<Procedure> procs = new ArrayList<Procedure>();
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            if (catalog_proc.getMapreduce())
                procs.add(catalog_proc);
        }
        return (procs);
    }

    /**
     * Construct a collection of all the Statements in the catalog
     *
     * @param catalog_obj
     * @return
     */
    public static Collection<Statement> getAllStatements(CatalogType catalog_obj) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        Collection<Statement> ret = new ArrayList<Statement>();
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            ret.addAll(catalog_proc.getStatements());
        } // FOR
        return (ret);
    }
   
    /**
     * Construct a collection of all prefetchable Statements for a Procedure
     * @param catalog_proc
     * @return
     */
    public static Collection<Statement> getPrefetchableStatements(Procedure catalog_proc) {
        Collection<Statement> ret = new ArrayList<Statement>();
        for (Statement catalog_stmt : catalog_proc.getStatements()) {
            if (catalog_stmt.getPrefetchable()) ret.add(catalog_stmt);
        } // FOR
        return (ret);
    }

    /**
     * Returns a set of all of the PlanFragments (both multi-partition and
     * single-partition)
     *
     * @param catalogs_stmt
     * @return
     */
    public static Set<PlanFragment> getAllPlanFragments(Statement catalog_stmt) {
        Set<PlanFragment> frags = new HashSet<PlanFragment>();
        for (PlanFragment catalog_frag : catalog_stmt.getFragments())
            frags.add(catalog_frag);
        for (PlanFragment catalog_frag : catalog_stmt.getMs_fragments())
            frags.add(catalog_frag);
        return (frags);
    }

    /**
     * Return the partitioning parameter used by this Procedure
     *
     * @param catalog_proc
     * @return
     */
    public static ProcParameter getPartitioningProcParameter(Procedure catalog_proc) {
        assert (catalog_proc != null);
        ProcParameter catalog_param = null;
        if (catalog_proc.getParameters().size() > 0 && !catalog_proc.getSystemproc()) {
            int idx = catalog_proc.getPartitionparameter();
            if (idx == NullProcParameter.PARAM_IDX) {
                catalog_param = NullProcParameter.singleton(catalog_proc);
            } else {
                catalog_param = catalog_proc.getParameters().get(idx);
                assert (catalog_param != null) : "Unexpected Null ProcParameter for " + catalog_proc.getName() + " at idx #" + idx;
            }
        }
        return (catalog_param);
    }

    /**
     * Return an ordered list of VoltTypes for the ProcParameters for the given
     * Procedure
     *
     * @param catalog_proc
     * @return
     */
    public static List<VoltType> getProcParameterTypes(final Procedure catalog_proc) {
        List<VoltType> vtypes = new ArrayList<VoltType>();
        for (ProcParameter catalog_param : CatalogUtil.getSortedCatalogItems(catalog_proc.getParameters(), "index")) {
            VoltType vtype = VoltType.get(catalog_param.getType());
            assert (vtype != null);
            assert (vtype != VoltType.INVALID);
            vtypes.add(vtype);
        } // FOR
        return (vtypes);
    }

    /**
     * Return the list of ProcParameters that are array parameters for the given
     * procedure
     */
    public static List<ProcParameter> getArrayProcParameters(final Procedure catalog_proc) {
        List<ProcParameter> params = new ArrayList<ProcParameter>();
        for (ProcParameter catalog_param : catalog_proc.getParameters()) {
            if (catalog_param.getIsarray())
                params.add(catalog_param);
        } // FOR
        return (params);
    }

    /**
     * Return the list of every ProcParameter except for SpecialProcParameters
     */
    public static List<ProcParameter> getRegularProcParameters(final Procedure catalog_proc) {
        List<ProcParameter> params = new ArrayList<ProcParameter>();
        for (ProcParameter catalog_param : catalog_proc.getParameters()) {
            if (catalog_param instanceof SpecialProcParameter)
                continue;
            params.add(catalog_param);
        } // FOR
        return (params);
    }

    /**
     * Returns all the StmtParameters that are linked to the ProcParameter
     *
     * @param catalog_stmt
     * @param catalog_proc_param
     * @return
     */
    public static Set<StmtParameter> getStmtParameters(Statement catalog_stmt, ProcParameter catalog_proc_param) {
        Set<StmtParameter> found = new HashSet<StmtParameter>();
        for (StmtParameter param : catalog_stmt.getParameters()) {
            if (param.getProcparameter() != null && param.getProcparameter().equals(catalog_proc_param))
                found.add(param);
        } // FOR
        return (found);
    }

    /**
     * Copy the query plans from Statement to another.
     * This will overwrite both the existing single-partition and
     * multi-partition query plans.
     * @param copy_src
     * @param copy_dest
     */
    public static void copyQueryPlans(Statement copy_src, Statement copy_dest) {
        // Update both the single and multi-partition query plans
        for (boolean sp : new boolean[] { true, false }) {
            if (debug.val)
                LOG.debug(String.format("Copying %s-partition query plan from %s to %s", (sp ? "single" : "multi"), copy_src.fullName(), copy_dest.fullName()));
            String fields[] = { (sp ? "" : "ms_") + "exptree", (sp ? "" : "ms_") + "fullplan", "has_" + (sp ? "single" : "multi") + "sited", };
            copyFields(copy_src, copy_dest, fields);

            CatalogMap<PlanFragment> copy_src_fragments = null;
            CatalogMap<PlanFragment> copy_dest_fragments = null;
            if (sp) {
                copy_src_fragments = copy_src.getFragments();
                copy_dest_fragments = copy_dest.getFragments();
            } else {
                copy_src_fragments = copy_src.getMs_fragments();
                copy_dest_fragments = copy_dest.getMs_fragments();
            }
            assert (copy_src_fragments != null);
            assert (copy_dest_fragments != null);

            copy_dest_fragments.clear();
            for (PlanFragment copy_src_frag : copy_src_fragments) {
                PlanFragment copy_dest_frag = copy_dest_fragments.add(copy_src_frag.getName());
                if (trace.val)
                    LOG.trace(String.format("Copying %s to %s", copy_src_frag.fullName(), copy_dest_frag.fullName()));
                copyFields(copy_src_frag, copy_dest_frag, copy_src_frag.getFields().toArray(new String[0]));
            } // FOR
        }
    }

    private static <T extends CatalogType> void copyFields(T copy_src, T copy_dest, String... fields) {
        for (String f : fields) {
            Object src_val = copy_src.getField(f);
            if (src_val != null) {
                if (src_val instanceof String)
                    src_val = "\"" + src_val + "\""; // HACK
                if (trace.val)
                    LOG.trace(String.format("Copied value '%s' for field '%s': %s => %s", src_val, f, copy_dest.fullName(), copy_src.fullName()));
                copy_dest.set(f, src_val.toString());
            } else if (debug.val) {
                LOG.warn(String.format("Missing value for field '%s': %s => %s", f, copy_dest.fullName(), copy_src.fullName()));
            }
        } // FOR
    }

    // ------------------------------------------------------------
    // HOSTS + SITES + PARTITIONS
    // ------------------------------------------------------------

    /**
     * Return the unique Site catalog object for the given id
     *
     * @param catalog_item
     * @return
     */
    @Deprecated
    public static Site getSiteFromId(CatalogType catalog_item, int site_id) {
        assert (site_id >= 0);
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        for (Site catalog_site : catalog_clus.getSites()) {
            if (catalog_site.getId() == site_id)
                return (catalog_site);
        } // FOR
        return (null);
    }

    /**
     * Return the number of hosts for a catalog for any catalog item
     *
     * @param catalog_item
     * @return
     */
    @Deprecated
    public static int getNumberOfHosts(CatalogType catalog_item) {
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        int ret = catalog_clus.getHosts().size();
        assert (ret > 0);
        return (ret);
    }

    /**
     * Return the number of sites for a catalog for any catalog item
     *
     * @param catalog_item
     * @return
     */
    @Deprecated
    public static int getNumberOfSites(CatalogType catalog_item) {
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        int ret = catalog_clus.getSites().size();
        assert (ret > 0);
        return (ret);
    }

    /**
     * Return the number of partitions for a catalog for any catalog item
     *
     * @param catalog_item
     * @return
     */
    @Deprecated
    public static int getNumberOfPartitions(CatalogType catalog_item) {
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        int ret = catalog_clus.getNum_partitions();
        return (ret);
    }

    /**
     * Return the Partition catalog object for the given PartitionId
     *
     * @param catalog_item
     * @param id
     * @return
     */
    @Deprecated
    public static Partition getPartitionById(CatalogType catalog_item, Integer id) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_item);
        if (cache.PARTITION_XREF.isEmpty())
            cache.buildPartitionCache(catalog_item);
        Partition catalog_part = cache.PARTITION_XREF.get(id);
        return (catalog_part);
    }
   
    /**
     * Return a Collection of all the Partition catalog objects
     *
     * @param catalog_item
     * @return
     */
    public static Collection<Partition> getAllPartitions(CatalogType catalog_item) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_item);
        if (cache.PARTITION_XREF.isEmpty())
            cache.buildPartitionCache(catalog_item);
        return (Collections.unmodifiableCollection(cache.PARTITION_XREF.values()));
    }

    /**
     * Get a new list of all the partition ids in this catalog
     *
     * @return
     */
    @Deprecated
    public static PartitionSet getAllPartitionIds(CatalogType catalog_item) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_item);
        if (cache.PARTITION_XREF.isEmpty())
            cache.buildPartitionCache(catalog_item);
        return (new PartitionSet(cache.PARTITION_XREF.asList()));
    }

    /**
     * Returns true if the given Partition is the "first" one at the given Site
     *
     * @param catalog_site
     * @param catalog_part
     * @return
     */
    public static boolean isFirstPartition(Site catalog_site, Partition catalog_part) {
        for (Partition p : getSortedCatalogItems(catalog_site.getPartitions(), "id")) {
            if (p.getId() == catalog_part.getId())
                return (true);
            break;
        }
        return (false);
    }

    public static Collection<Site> getAllSites(CatalogType catalog_item) {
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
        return (catalog_clus.getSites());
    }

    /**
     * Get a mapping of sites for each host. We have to return the Site objects
     * in order to get the Partition handle that we want
     *
     * @return
     */
    public static synchronized Map<Host, Set<Site>> getSitesPerHost(CatalogType catalog_item) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_item);
        final Map<Host, Set<Site>> sites = cache.HOST_SITES;

        if (sites.isEmpty()) {
            // Sort them by site ids
            final Comparator<Site> comparator = new Comparator<Site>() {
                @Override
                public int compare(Site o1, Site o2) {
                    return (o1.getId() - o2.getId());
                }
            };

            Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
            for (Site catalog_site : catalog_clus.getSites()) {
                Host catalog_host = catalog_site.getHost();
                if (!sites.containsKey(catalog_host)) {
                    sites.put(catalog_host, new TreeSet<Site>(comparator));
                }
                sites.get(catalog_host).add(catalog_site);
                if (debug.val)
                    LOG.debug(catalog_host + " => " + catalog_site);
            } // FOR
            assert (sites.size() == catalog_clus.getHosts().size());
            if (debug.val)
                LOG.debug("HOST SITES: " + sites);
        }
        return (sites);
    }

    /**
     * Get a reverse mapping from PartitionId -> SiteID
     * @param catalog_item
     * @return
     */
    @Deprecated
    public static Map<Integer, Integer> getPartitionSiteXref(CatalogType catalog_item) {
        Map<Integer, Integer> m = new HashMap<Integer, Integer>();
        for (Partition catalog_part : CatalogUtil.getAllPartitions(catalog_item)) {
            Site catalog_site = catalog_part.getParent();
            m.put(catalog_part.getId(), catalog_site.getId());
        } // FOR
        return (m);
    }
   
    /**
     * Get a reverse mapping array from PartitionId -> SiteID
     * @param catalog_item
     * @return
     */
    public static int[] getPartitionSiteXrefArray(CatalogType catalog_item) {
        int partition_site_xref[] = new int[CatalogUtil.getNumberOfPartitions(catalog_item)];
        for (Partition catalog_part : CatalogUtil.getAllPartitions(catalog_item)) {
            partition_site_xref[catalog_part.getId()] = ((Site)catalog_part.getParent()).getId();
        } // FOR
        return (partition_site_xref);
    }

    /**
     * Return the list of Sites for a particular host
     *
     * @param catalog_host
     * @return
     */
    public static Collection<Site> getSitesForHost(Host catalog_host) {
        List<Site> sites = new ArrayList<Site>();
        Cluster cluster = (Cluster) catalog_host.getParent();
        for (Site catalog_site : cluster.getSites()) {
            if (catalog_site.getHost().getName().equals(catalog_host.getName()))
                sites.add(catalog_site);
        } // FOR
        // Sort them by id
        Collections.sort(sites, new CatalogFieldComparator<Site>("id"));
        return (sites);
    }
   
    /**
     * Return the list of Partition for a particular host
     *
     * @param catalog_host
     * @return
     */
    public static Collection<Partition> getPartitionsForHost(Host catalog_host) {
        List<Partition> partitions = new ArrayList<Partition>();
        Cluster cluster = (Cluster) catalog_host.getParent();
        for (Site catalog_site : cluster.getSites()) {
            if (catalog_site.getHost().getName().equals(catalog_host.getName()))
                partitions.addAll(catalog_site.getPartitions());
        } // FOR
        return (partitions);
    }

    /**
     * Return a mapping from SiteId to <Host, Port#>
     *
     * @param catalog_item
     * @return
     */
    public static Map<Integer, Set<Pair<String, Integer>>> getExecutionSites(CatalogType catalog_item) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_item);
        final Map<Integer, Set<Pair<String, Integer>>> sites = cache.EXECUTION_SITES;

        if (sites.isEmpty()) {
            Cluster catalog_clus = CatalogUtil.getCluster(catalog_item);
            for (Site catalog_site : CatalogUtil.getSortedCatalogItems(catalog_clus.getSites(), "id")) {
                Host catalog_host = catalog_site.getHost();
                assert (catalog_host != null);
                Set<Pair<String, Integer>> s = new HashSet<Pair<String, Integer>>();
                for (Integer port : CatalogUtil.getExecutionSitePorts(catalog_site)) {
                    s.add(Pair.of(catalog_host.getIpaddr(), port));
                    // break;
                } // FOR
                sites.put(catalog_site.getId(), s);
            } // FOR
        }
        return (sites);
    }

    public static Collection<Integer> getExecutionSitePorts(Site catalog_site) {
        return Collections.singleton(catalog_site.getProc_port());
    }

    /**
     * Return the partition ids stored at this Site
     *
     * @param catalogContext
     * @return
     */
    public static PartitionSet getLocalPartitionIds(Site catalog_site) {
        PartitionSet partition_ids = new PartitionSet();
        for (Partition catalog_proc : catalog_site.getPartitions()) {
            partition_ids.add(catalog_proc.getId());
        } // FOR
        return (partition_ids);
    }

    /**
     * @param catalog_db
     * @param base_partition
     * @return
     */
    public static PartitionSet getLocalPartitionIds(Database catalog_db, int base_partition) {
        PartitionSet partition_ids = new PartitionSet();
        for (Partition catalog_proc : CatalogUtil.getLocalPartitions(catalog_db, base_partition)) {
            partition_ids.add(catalog_proc.getId());
        } // FOR
        return (partition_ids);
    }

    /**
     * For a given base partition id, return the list of partitions that are on
     * same node as the base partition
     *
     * @param catalog_db
     * @param base_partition
     * @return
     */
    public static Collection<Partition> getLocalPartitions(Database catalog_db, int base_partition) {
        Set<Partition> partitions = new ListOrderedSet<Partition>();

        // First figure out what partition we are in the catalog
        Cluster catalog_clus = CatalogUtil.getCluster(catalog_db);
        assert (catalog_clus != null);
        Partition catalog_part = CatalogUtil.getPartitionById(catalog_clus, base_partition);
        assert (catalog_part != null);
        Site catalog_site = catalog_part.getParent();
        assert (catalog_site != null);
        Host catalog_host = catalog_site.getHost();
        assert (catalog_host != null);

        // Now look at what other partitions are on the same host that we are
        for (Site other_site : catalog_clus.getSites()) {
            if (other_site.getHost().equals(catalog_host) == false)
                continue;
            LOG.trace(catalog_host + " => " + CatalogUtil.debug(other_site.getPartitions()));
            CollectionUtil.addAll(partitions, other_site.getPartitions());
        } // FOR
        return (partitions);
    }

    // ------------------------------------------------------------
    // TABLES + COLUMNS
    // ------------------------------------------------------------

    /**
     * Return the tables encoded in the given TableRefs
     * @param tableRefs
     * @return
     */
    public static Collection<Table> getTablesFromRefs(Collection<TableRef> tableRefs) {
        List<Table> tables = new ArrayList<Table>();
        for (TableRef ref : tableRefs) {
            tables.add(ref.getTable());
        } // FOR
        return (tables);
    }
   
    /**
     * Return all of the internal system tables for the database
     */
    public static Collection<Table> getSysTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getSystable())
                tables.add(catalog_tbl);
        }
        return (tables);
    }

    /**
     * Return all of the user-defined data tables for the database
     */
    public static Collection<Table> getDataTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getSystable() == false &&
                catalog_tbl.getMapreduce() == false &&
                catalog_tbl.getMaterializer() == null)
                tables.add(catalog_tbl);
        }
        return (tables);
    }
   
    /**
     * Return all of the materialized view tables for the database
     */
    public static Collection<Table> getViewTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getMaterializer() != null)
                tables.add(catalog_tbl);
        }
        return (tables);
    }

    /**
     * Return all of the MapReduce input data tables for the database
     */
    public static Collection<Table> getMapReduceTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getMapreduce())
                tables.add(catalog_tbl);
        }
        return (tables);
    }
   
    /**
     * Return all of the replicated tables for the database
     */
    public static Collection<Table> getReplicatedTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getIsreplicated()) tables.add(catalog_tbl);
        }
        return (tables);
    }
   
    /**
     * Return all of the evictable tables for the database
     */
    public static Collection<Table> getEvictableTables(Database catalog_db) {
        List<Table> tables = new ArrayList<Table>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (catalog_tbl.getEvictable()) tables.add(catalog_tbl);
        }
        return (tables);
    }

    /**
     * For a given VoltTable object, return the matching Table catalog object
     * based on the column names.
     *
     * @param catalog_db
     * @param voltTable
     * @return
     */
    public static Table getCatalogTable(Database catalog_db, VoltTable voltTable) {
        int num_columns = voltTable.getColumnCount();
        for (Table catalog_tbl : catalog_db.getTables()) {
            if (num_columns == catalog_tbl.getColumns().size()) {
                boolean match = true;
                List<Column> catalog_cols = CatalogUtil.getSortedCatalogItems(catalog_tbl.getColumns(), "index");
                for (int i = 0; i < num_columns; i++) {
                    if (!voltTable.getColumnName(i).equals(catalog_cols.get(i).getName())) {
                        match = false;
                        break;
                    }
                } // FOR
                if (match)
                    return (catalog_tbl);
            }
        } // FOR
        return (null);
    }

    /**
     * Return a set of all the names of the tables in the catalog This set will
     * exclude system table names
     *
     * @param catalog_obj
     */
    public static Collection<String> getAllTableNames(CatalogType catalog_obj) {
        return (CatalogUtil.getAllTableNames(catalog_obj, false));
    }

    /**
     * Return a set of all the names of the tables in the catalog If include_sys
     * is set to true, then the output will include system table names
     * Otherwise, they we will be excluded
     *
     * @param catalog_obj
     * @param include_sys
     * @return
     */
    public static Collection<String> getAllTableNames(CatalogType catalog_obj, boolean include_sys) {
        Set<String> tableNames = new HashSet<String>();
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        for (Table catalog_tbl : catalog_db.getTables()) {
            boolean is_systable = catalog_tbl.getSystable();
            if (include_sys || is_systable == false)
                tableNames.add(catalog_tbl.getName());
        } // FOR
        return (tableNames);
    }

    /**
     * Return a mapping from Tables to their vertical partitions
     *
     * @param catalog_obj
     */
    public static Map<Table, MaterializedViewInfo> getVerticallyPartitionedTables(CatalogType catalog_obj) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        Map<Table, MaterializedViewInfo> ret = new HashMap<Table, MaterializedViewInfo>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            MaterializedViewInfo catalog_view = CatalogUtil.getVerticalPartition(catalog_tbl);
            if (catalog_view != null)
                ret.put(catalog_tbl, catalog_view);
        } // FOR
        return (ret);
    }

    /**
     * Return the VerticalPartition for the given Table
     *
     * @param catalog_tbl
     * @return
     */
    public static MaterializedViewInfo getVerticalPartition(Table catalog_tbl) {
        for (MaterializedViewInfo catalog_view : catalog_tbl.getViews()) {
            if (catalog_view.getVerticalpartition()) {
                return catalog_view;
            }
        } // FOR
        return (null);
    }

    /**
     * Return the set of read-only Columns for the given table If
     * exclude_inserts is true, then only UPDATES will be counted against
     * columns
     *
     * @param catalog_tbl
     * @param exclude_inserts
     * @return
     */
    public static Collection<Column> getReadOnlyColumns(Table catalog_tbl, boolean exclude_inserts) {
        assert (catalog_tbl != null);
        final CatalogUtil.Cache c = CatalogUtil.getCatalogCache(catalog_tbl);
        Set<Column> columns = (exclude_inserts ? c.READONLY_COLUMNS_NO_INSERTS : c.READONLY_COLUMNS_ALL).get(catalog_tbl);
        if (columns == null) {
            c.buildReadOnlyColumnCache(catalog_tbl);
            columns = (exclude_inserts ? c.READONLY_COLUMNS_NO_INSERTS : c.READONLY_COLUMNS_ALL).get(catalog_tbl);
        }
        return (columns);
    }

    /**
     * Construct a collection of all the Columns in the catalog
     *
     * @param catalog_obj
     * @return
     */
    public static Collection<Column> getAllColumns(CatalogType catalog_obj) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        Set<Column> ret = new HashSet<Column>();
        for (Table catalog_tbl : catalog_db.getTables()) {
            ret.addAll(catalog_tbl.getColumns());
        } // FOR
        return (ret);
    }

    /**
     * @param from_column
     * @return
     */
    public static Column getForeignKeyParent(Column from_column) {
        assert (from_column != null);
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(from_column);
        Column to_column = cache.FOREIGNKEY_PARENT.get(from_column);

        if (to_column == null) {
            for (Constraint catalog_const : CatalogUtil.getConstraints(from_column.getConstraints())) {
                if (catalog_const.getType() == ConstraintType.FOREIGN_KEY.getValue()) {
                    assert (!catalog_const.getForeignkeycols().isEmpty());
                    for (ColumnRef catalog_col_ref : catalog_const.getForeignkeycols()) {
                        if (catalog_col_ref.getName().equals(from_column.getName())) {
                            assert (to_column == null);
                            to_column = catalog_col_ref.getColumn();
                            break;
                        }
                    } // FOR
                    if (to_column != null)
                        break;
                }
            } // FOR
            cache.FOREIGNKEY_PARENT.put(from_column, to_column);
        }
        return (to_column);
    }

    /**
     * Returns all the columns for this table that have a foreign key dependency
     * on another table
     *
     * @param catalog_tbl
     * @return
     */
    public static Collection<Column> getForeignKeyDependents(Table catalog_tbl) {
        Set<Column> found = new ListOrderedSet<Column>();
        for (Column catalog_col : catalog_tbl.getColumns()) {
            assert (catalog_col != null);
            if (!catalog_col.getConstraints().isEmpty()) {
                // System.out.println(catalog_col + ": " +
                // CatalogUtil.getConstraints(catalog_col.getConstraints()));
                if (!CatalogUtil.findAll(CatalogUtil.getConstraints(catalog_col.getConstraints()), "type", ConstraintType.FOREIGN_KEY.getValue()).isEmpty()) {
                    found.add(catalog_col);
                }
            }
        } // FOR
        return (found);
    }
   
    public static String[] getChildTables(Database catalog_db, Table catalog_tbl){
        List<String> found= new ArrayList<String>();
        CatalogMap<Column> columns = catalog_tbl.getColumns();
        for (Table t : catalog_db.getTables()){
          if(t!=catalog_tbl){
            for(Column c: getForeignKeyDependents(t)){
              Column parentColumn = CatalogUtil.getForeignKeyParent(c);
              if(columns.containsKey(parentColumn.getName())){
                found.add(t.getName());
                break;
              }
            }
          }
        }
        String [] tableNames = new String[found.size()];
        return found.toArray(tableNames);     
    }

    /**
     * Return the real Column objects for the ColumnRefs
     * @param map
     * @return
     */
    public static Collection<Column> getColumns(Iterable<ColumnRef> map) {
        List<Column> ret = new ArrayList<Column>();
        if (map != null) {
            for (ColumnRef ref : map) {
                Column catalog_item = ref.getColumn();
                assert (catalog_item != null);
                ret.add(catalog_item);
            }
        }
        return (ret);
    }

    /**
     * @param catalog_db
     * @param table_name
     * @param column_name
     * @return
     */
    public static Column getColumn(Database catalog_db, String table_name, String column_name) {
        Column catalog_col = null;
        Table catalog_table = catalog_db.getTables().get(table_name);
        if (catalog_table != null)
            catalog_col = catalog_table.getColumns().get(column_name);
        return (catalog_col);
    }

    /**
     * Returns all the tables access/modified in all the Statements for this
     * Procedure
     *
     * @param catalog_proc
     * @return
     * @throws Exception
     */
    public static Collection<Table> getReferencedTables(Procedure catalog_proc) throws Exception {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_proc);
        Set<Table> ret = cache.PROCEDURE_TABLES.get(catalog_proc);
        if (ret == null) {
            Set<Table> tables = new ListOrderedSet<Table>();
            for (Statement catalog_stmt : catalog_proc.getStatements()) {
                tables.addAll(CatalogUtil.getReferencedTables(catalog_stmt));
            } // FOR
            ret = Collections.unmodifiableSet(tables);
            cache.PROCEDURE_TABLES.put(catalog_proc, ret);
        }
        return (ret);
    }

    /**
     * Returns all the columns access/modified in all the Statements for this
     * Procedure
     *
     * @param catalog_proc
     * @return
     * @throws Exception
     */
    public static Collection<Column> getReferencedColumns(Procedure catalog_proc) throws Exception {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_proc);
        Set<Column> ret = cache.PROCEDURE_COLUMNS.get(catalog_proc);
        if (ret == null) {
            Set<Column> columns = new ListOrderedSet<Column>();
            for (Statement catalog_stmt : catalog_proc.getStatements()) {
                columns.addAll(CatalogUtil.getReferencedColumns(catalog_stmt));
            } // FOR
            ret = Collections.unmodifiableSet(columns);
            cache.PROCEDURE_COLUMNS.put(catalog_proc, ret);
        }
        return (ret);
    }

    /**
     * Returns all of the procedures that access/modify the given table
     *
     * @param catalog_tbl
     * @return
     * @throws Exception
     */
    public static Collection<Procedure> getReferencingProcedures(Table catalog_tbl) throws Exception {
        Set<Procedure> ret = new ListOrderedSet<Procedure>();
        Database catalog_db = CatalogUtil.getDatabase(catalog_tbl);
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            if (catalog_proc.getSystemproc())
                continue;
            if (CatalogUtil.getReferencedTables(catalog_proc).contains(catalog_tbl)) {
                ret.add(catalog_proc);
            }
        } // FOR
        return (ret);
    }

    /**
     * Returns all of the procedures that access/modify the given Column
     *
     * @param catalog_col
     * @return
     * @throws Exception
     */
    public static Collection<Procedure> getReferencingProcedures(Column catalog_col) throws Exception {
        Set<Procedure> ret = new HashSet<Procedure>();
        Database catalog_db = CatalogUtil.getDatabase(catalog_col.getParent());

        // Special Case: ReplicatedColumn
        if (catalog_col instanceof ReplicatedColumn) {
            Table catalog_tbl = catalog_col.getParent();
            for (Column col : catalog_tbl.getColumns()) {
                for (Procedure catalog_proc : catalog_db.getProcedures()) {
                    if (catalog_proc.getSystemproc())
                        continue;
                    if (CatalogUtil.getReferencedColumns(catalog_proc).contains(col)) {
                        ret.add(catalog_proc);
                    }
                } // FOR
            } // FOR
        } else {
            for (Procedure catalog_proc : catalog_db.getProcedures()) {
                if (catalog_proc.getSystemproc())
                    continue;
                if (CatalogUtil.getReferencedColumns(catalog_proc).contains(catalog_col)) {
                    ret.add(catalog_proc);
                }
            } // FOR
        }
        return (ret);
    }

    /**
     * Returns all the tables access/modified in the given Statement's query
     * @param catalog_stmt
     */
    public static Collection<Table> getReferencedTables(Statement catalog_stmt) {
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Collection<Table> ret = cache.STATEMENT_TABLES.get(catalog_stmt);
        if (ret == null) {
            Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
            AbstractPlanNode node = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);
            Collection<Table> tables = CatalogUtil.getReferencedTablesForTree(catalog_db, node);
            ret = Collections.unmodifiableCollection(tables);
            // cache.STATEMENT_TABLES.put(catalog_stmt, ret);
        }
        return (ret);
    }
   
    /**
     * Returns all the indexes access/modified in the given Statement's query
     * @param catalog_stmt
     */
    public static Collection<Index> getReferencedIndexes(Statement catalog_stmt) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
        AbstractPlanNode node = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);
        Collection<Index> indexes = CatalogUtil.getReferencedIndexesForTree(catalog_db, node);
        return (Collections.unmodifiableCollection(indexes));
    }
   
    /**
     * Get all the tables referenced in this PlanFragment
     * @param catalog_fag
     */
    public static Collection<Table> getReferencedTables(PlanFragment catalog_frag) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_frag);
        AbstractPlanNode node = PlanNodeUtil.getPlanNodeTreeForPlanFragment(catalog_frag);
        return (CatalogUtil.getReferencedTablesForTree(catalog_db, node));
    }

    /**
     * Returns all the columns access/modified in the given Statement's query
     * This does not include the columns that are output by SELECT queries
     *
     * @param catalog_stmt
     * @return
     * @throws Exception
     */
    public static Collection<Column> getReferencedColumns(Statement catalog_stmt) {
        if (debug.val)
            LOG.debug("Extracting referenced columns from statement " + CatalogUtil.getDisplayName(catalog_stmt));

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Set<Column> ret = cache.STATEMENT_ALL_COLUMNS.get(catalog_stmt);
        if (ret == null) {
            final Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
            ret = new ListOrderedSet<Column>();

            CatalogFieldComparator<Column> comparator = new CatalogFieldComparator<Column>("index");
            Set<Column> modified = new TreeSet<Column>(comparator);
            Set<Column> readOnly = new TreeSet<Column>(comparator);

            // 2010-07-14: Always use the AbstractPlanNodes from the PlanFragments
            // to figure out what columns the query touches. It's more accurate
            // because we will pick apart plan nodes and expression trees to figure things out
            AbstractPlanNode node = null;
            Set<Column> columns = null;
            try {
                node = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);
                columns = CatalogUtil.getReferencedColumnsForTree(catalog_db, node, ret, modified, readOnly);
            } catch (Throwable ex) {
                throw new RuntimeException("Failed to get columns for " + catalog_stmt.fullName(), ex);
            }
            assert (columns != null) : "Failed to get columns for " + catalog_stmt.fullName();
            ret = Collections.unmodifiableSet(columns);
            cache.STATEMENT_ALL_COLUMNS.put(catalog_stmt, ret);
            cache.STATEMENT_MODIFIED_COLUMNS.put(catalog_stmt, Collections.unmodifiableSet(modified));
            cache.STATEMENT_READONLY_COLUMNS.put(catalog_stmt, Collections.unmodifiableSet(readOnly));
        }
        return (ret);
    }

    /**
     * Returns all the columns access/modified in the given Statement's query
     *
     * @param catalog_stmt
     * @return
     * @throws Exception
     */
    public static Collection<Column> getModifiedColumns(Statement catalog_stmt) {
        if (debug.val)
            LOG.debug("Extracting modified columns from statement " + CatalogUtil.getDisplayName(catalog_stmt));

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Set<Column> ret = cache.STATEMENT_MODIFIED_COLUMNS.get(catalog_stmt);
        if (ret == null) {
            CatalogUtil.getReferencedColumns(catalog_stmt);
            ret = cache.STATEMENT_MODIFIED_COLUMNS.get(catalog_stmt);
        }
        return (ret);
    }

    /**
     * Returns all the columns that are not modified in the given Statement's
     * query
     *
     * @param catalog_stmt
     * @return
     * @throws Exception
     */
    public static Collection<Column> getReadOnlyColumns(Statement catalog_stmt) {
        if (debug.val)
            LOG.debug("Extracting read-only columns from statement " + CatalogUtil.getDisplayName(catalog_stmt));

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Collection<Column> ret = cache.STATEMENT_READONLY_COLUMNS.get(catalog_stmt);
        if (ret == null) {
            CatalogUtil.getReferencedColumns(catalog_stmt);
            ret = cache.STATEMENT_READONLY_COLUMNS.get(catalog_stmt);
        }
        return (ret);
    }

    /**
     * Return all the Columns used in ORDER BY clauses for the given Statement
     *
     * @param catalog_stmt
     * @return
     */
    public static Collection<Column> getOrderByColumns(Statement catalog_stmt) {
        if (debug.val)
            LOG.debug("Extracting order-by columns from statement " + CatalogUtil.getDisplayName(catalog_stmt));

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Set<Column> ret = cache.STATEMENT_ORDERBY_COLUMNS.get(catalog_stmt);
        if (ret == null) {
            Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
            ret = new ListOrderedSet<Column>();
            try {
                AbstractPlanNode root = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);
                assert (root != null);
                PlannerContext context = PlannerContext.singleton();
                for (OrderByPlanNode node : PlanNodeUtil.getPlanNodes(root, OrderByPlanNode.class)) {
                    for (Integer guid : node.getSortColumnGuids()) {
                        PlanColumn pcol = context.get(guid);
                        assert (pcol != null);
                        ret.addAll(ExpressionUtil.getReferencedColumns(catalog_db, pcol.getExpression()));
                    } // FOR
                } // FOR
            } catch (Throwable ex) {
                throw new RuntimeException("Failed to retrieve ORDER BY columns for " + catalog_stmt.fullName());
            }
            cache.STATEMENT_ORDERBY_COLUMNS.put(catalog_stmt, ret);
        }
        return (ret);
    }
   
    /**
     * Returns the set of Column catalog objects modified by the given
     * AbstractPlanNode. If you're looking for where we figure out what columns
     * a PlanNode touches that is of interest to us for figuring out how we will
     * partition things, then you've come to the right place.
     *
     * @param catalog_db
     * @param node
     * @return
     * @throws Exception
     */
    public static Collection<Column> getReferencedColumnsForTree(final Database catalog_db, AbstractPlanNode node) throws Exception {
        return (getReferencedColumnsForTree(catalog_db, node, new ListOrderedSet<Column>(), null, null));
    }

    private static Set<Column> getReferencedColumnsForTree(final Database catalog_db, final AbstractPlanNode node, final Set<Column> columns, final Set<Column> modified, final Set<Column> readOnly)
            throws Exception {
        new PlanNodeTreeWalker(true) {
            @Override
            protected void callback(final AbstractPlanNode node) {
                try {
                    CatalogUtil.getReferencedColumnsForPlanNode(catalog_db, node, columns, modified, readOnly);
                } catch (Exception ex) {
                    LOG.fatal("Failed to extract columns from " + node, ex);
                    throw new RuntimeException(ex);
                }
                return;
            }
        }.traverse(node);
        return (columns);
    }

    /**
     * Return all of the columns referenced in the given AbstractPlanNode
     * Non-recursive.
     *
     * @param catalog_db
     * @param node
     * @return
     * @throws Exception
     */
    public static Collection<Column> getReferencedColumnsForPlanNode(final Database catalog_db, final AbstractPlanNode node) throws Exception {
        final Set<Column> ret = new ListOrderedSet<Column>();
        CatalogUtil.getReferencedColumnsForPlanNode(catalog_db, node, ret, null, null);
        return (ret);
    }

    private static void updateReferenceColumns(Collection<Column> cols, boolean is_readOnly, final Set<Column> ref_columns, final Set<Column> modified_cols, final Set<Column> readOnly) {
        for (Column col : cols) {
            updateReferenceColumns(col, is_readOnly, ref_columns, modified_cols, readOnly);
        } // FOR
    }

    private static void updateReferenceColumns(Column col, boolean isReadOnly, final Set<Column> allCols, final Set<Column> modifiedCols, final Set<Column> readOnlyCols) {
        allCols.add(col);
        if (modifiedCols != null) {
            assert (readOnlyCols != null);
            if (isReadOnly) {
                if (modifiedCols.contains(col) == false)
                    readOnlyCols.add(col);
            } else {
                readOnlyCols.remove(col);
                modifiedCols.add(col);
            }
        }
    }

    /**
     * Returns all the columns referenced for the given PlanNode If
     * modified_cols is not null, then it will contain the Columns that are
     * modified in the PlanNode
     *
     * @param catalog_db
     * @param node
     * @param allCols
     * @param modifiedCols
     * @throws Exception
     */
    private static void getReferencedColumnsForPlanNode(final Database catalog_db,
                                                        final AbstractPlanNode node,
                                                        final Set<Column> allCols,
                                                        final Set<Column> modifiedCols,
                                                        final Set<Column> readOnlyCols) throws Exception {
        switch (node.getPlanNodeType()) {
        // ---------------------------------------------------
        // INSERT
        // ---------------------------------------------------
            case INSERT: {
                // All columns are accessed whenever we insert a new record
                InsertPlanNode ins_node = (InsertPlanNode) node;
                Table catalog_tbl = catalog_db.getTables().get(ins_node.getTargetTableName());
                assert (catalog_tbl != null) : "Missing table " + ins_node.getTargetTableName();
                updateReferenceColumns(catalog_tbl.getColumns(), false, allCols, modifiedCols, readOnlyCols);
                break;
            }
            // ---------------------------------------------------
            // UPDATE
            // ---------------------------------------------------
            case UPDATE: {
                // Need to make sure we get both the WHERE clause and the fields
                // that are updated
                // We need to get the list of columns from the ScanPlanNode
                // below us
                UpdatePlanNode up_node = (UpdatePlanNode) node;
                Table catalog_tbl = catalog_db.getTables().get(up_node.getTargetTableName());
                assert (catalog_tbl != null) : "Missing table " + up_node.getTargetTableName();

                AbstractScanPlanNode scan_node = CollectionUtil.first(PlanNodeUtil.getPlanNodes(up_node, AbstractScanPlanNode.class));
                assert (scan_node != null) : "Failed to find underlying scan node for " + up_node;

                updateReferenceColumns(PlanNodeUtil.getUpdatedColumnsForPlanNode(catalog_db, scan_node), false, allCols, modifiedCols, readOnlyCols);

                // XXX: Why is this necessary?
                if (scan_node.getInlinePlanNodeCount() > 0) {
                    ProjectionPlanNode proj_node = scan_node.getInlinePlanNode(PlanNodeType.PROJECTION);
                    assert (proj_node != null);

                    // This is a bit tricky. We have to go by the names of the
                    // output columns to find what
                    // column is meant to be updated
                    PlannerContext pcontext = PlannerContext.singleton();
                    for (Integer col_guid : proj_node.getOutputColumnGUIDs()) {
                        PlanColumn pc = pcontext.get(col_guid);
                        assert (pc != null);
                        if (pc.getExpression() instanceof TupleAddressExpression)
                            continue;

                        Column catalog_col = catalog_tbl.getColumns().get(pc.getDisplayName());
                        if (catalog_col == null) {
                            LOG.error("Invalid PlanNode:\n" + PlanNodeUtil.debug(up_node));
                        }
                        assert (catalog_col != null) : String.format("Missing %s.%s\n%s", catalog_tbl.getName(), pc.getDisplayName(), pc);

                        updateReferenceColumns(catalog_col, false, allCols, modifiedCols, readOnlyCols);
                    } // FOR
                }
                break;
            }
            // ---------------------------------------------------
            // DELETE
            // ---------------------------------------------------
            case DELETE:
                // I don't think we need anything here because all the
                // columns will get get picked up by the scans that feed into
                // the DELETE
                break;
            // ---------------------------------------------------
            // EVERYTHING ELSE
            // ---------------------------------------------------
            default: {
                Collection<AbstractExpression> node_exps = PlanNodeUtil.getExpressionsForPlanNode(node, PlanNodeType.PROJECTION, PlanNodeType.ORDERBY, PlanNodeType.AGGREGATE,
                        PlanNodeType.HASHAGGREGATE);
                for (AbstractExpression exp : node_exps) {
                    Collection<Column> columns = null;
                    try {
                        columns = ExpressionUtil.getReferencedColumns(catalog_db, exp);
                    } catch (Throwable ex) {
                        throw new RuntimeException("Failed to get referenced columns from " + exp + " extract from " + node, ex);
                    }
                    assert (columns != null);
                    updateReferenceColumns(columns, true, allCols, modifiedCols, readOnlyCols);
                } // FOR
            }
        } // SWITCH
    }

    /**
     * Return all of the tables referenced in the given AbstractPlanNode
     * Non-recursive.
     *
     * @param catalog_db
     * @param node
     * @return
     * @throws Exception
     */
    public static Collection<Table> getReferencedTablesForPlanNode(final Database catalog_db,
                                                                   final AbstractPlanNode node) {
        final Set<Table> ret = new ListOrderedSet<Table>();
        try {
            CatalogUtil.getReferencedTablesForPlanNode(catalog_db, node, ret);
        } catch (Exception ex) {
            throw new RuntimeException("Failed to get referenced tables for " + node, ex);
        }
        return (ret);
    }

    /**
     * Return all of tables referenced in the PlanNode tree, regardless if they
     * are modified or not
     *
     * @param catalog_db
     * @param root
     * @return
     */
    public static Collection<Table> getReferencedTablesForTree(final Database catalog_db,
                                                               final AbstractPlanNode root) {
        final Set<Table> found = new ListOrderedSet<Table>();
        new PlanNodeTreeWalker(true) {
            @Override
            protected void callback(AbstractPlanNode element) {
                CatalogUtil.getReferencedTablesForPlanNode(catalog_db, element, found);
                return;
            }
        }.traverse(root);
        return (found);
    }

    private static void getReferencedTablesForPlanNode(final Database catalog_db,
                                                       final AbstractPlanNode node,
                                                       final Collection<Table> found) {
        String table_name = null;
        // AbstractScanNode
        if (node instanceof AbstractScanPlanNode) {
            AbstractScanPlanNode cast_node = (AbstractScanPlanNode) node;
            table_name = cast_node.getTargetTableName();
            assert (table_name != null);
            assert (!table_name.isEmpty());
        }
        // NestLoopIndexNode
        else if (node instanceof NestLoopIndexPlanNode) {
            NestLoopIndexPlanNode cast_node = (NestLoopIndexPlanNode) node;
            assert (cast_node.getInlinePlanNodeCount() == 1);
            IndexScanPlanNode idx_node = cast_node.getInlinePlanNode(PlanNodeType.INDEXSCAN);
            table_name = idx_node.getTargetTableName();
        }
        // AbstractOperationPlanNode
        else if (node instanceof AbstractOperationPlanNode) {
            AbstractOperationPlanNode cast_node = (AbstractOperationPlanNode) node;
            table_name = cast_node.getTargetTableName();
            assert (table_name != null);
            assert (!table_name.isEmpty());
        }

        if (table_name != null) {
            Table catalog_tbl = catalog_db.getTables().get(table_name);
            assert (catalog_tbl != null) : String.format("Invalid table '%s' extracted from %s. Valid tables: %s", table_name, node, CatalogUtil.getDisplayNames(catalog_db.getTables()));
            found.add(catalog_tbl);
        }
    }
   
    /**
     * Return all of indexes referenced in the PlanNode tree, regardless if they
     * are modified or not
     * @param catalog_db
     * @param root
     * @return
     */
    public static Collection<Index> getReferencedIndexesForTree(final Database catalog_db,
                                                                final AbstractPlanNode root) {
        final Set<Index> found = new ListOrderedSet<Index>();
        new PlanNodeTreeWalker(true) {
            @Override
            protected void callback(AbstractPlanNode element) {
                // IndexScanNode
                if (element instanceof IndexScanPlanNode) {
                    IndexScanPlanNode cast_node = (IndexScanPlanNode)element;
                    String table_name = cast_node.getTargetTableName();
                    assert(table_name != null);
                    assert(table_name.isEmpty() == false);
                   
                    String index_name = cast_node.getTargetIndexName();
                    assert(index_name != null);
                    assert(index_name.isEmpty() == false);
               
                    Table catalog_tbl = catalog_db.getTables().get(table_name);
                    assert(catalog_tbl != null) :
                        String.format("Invalid table '%s' extracted from %s. Valid tables: %s",
                                      table_name, element, CatalogUtil.getDisplayNames(catalog_db.getTables()));
                   
                    Index catalog_idx = catalog_tbl.getIndexes().get(index_name);
                    assert(catalog_idx != null) :
                        String.format("Invalid index '%s.%s' extracted from %s. Valid indexes: %s",
                                table_name, index_name, element,
                                CatalogUtil.getDisplayNames(catalog_tbl.getIndexes()));
                    found.add(catalog_idx);
                }
                return;
            }
        }.traverse(root);
        return (found);
    }
   
    /**
     * Extract a ColumnSet for the given Statement catalog object for all Tables
     * If convert_params is set to true, then we will convert any StmtParameters
     * that are mapped to ProcParameter directly into the ProcParameter object.
     *
     * @param catalog_stmt
     * @param convert_params
     * @return
     * @throws Exception
     */
    public static PredicatePairs extractStatementPredicates(final Statement catalog_stmt, final boolean convert_params) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
        Table tables[] = catalog_db.getTables().values();
        return (CatalogUtil.extractStatementPredicates(catalog_stmt, convert_params, tables));
    }

    /**
     * Returns true if the given query is an equality predicate (with all the columns)
     * on a unique index. This essentially means that the query will only return
     * one row at runtime.
     * <B>WARNING:</B> This is wildly inaccurate and should not used by to make runtime decisions.
     * @param catalog_stmt
     * @return
     */
    public static boolean isEqualityIndexScan(Statement catalog_stmt) {
        Collection<Table> tables = getReferencedTables(catalog_stmt);
        PredicatePairs cset = extractStatementPredicates(catalog_stmt, false, tables.toArray(new Table[0]));
       
        Collection<Index> indexes = getReferencedIndexes(catalog_stmt);
       
        for (CatalogPair cp : cset) {
            if (cp.getComparisonExp() != ExpressionType.COMPARE_EQUAL) {
                return (false);
            }
           
            CatalogType ctypes[] = { cp.getFirst(), cp.getSecond() };
            for (int i = 0; i < ctypes.length; i++) {
                if (ctypes[i] instanceof Column) {
                    Column target_col = (Column)ctypes[i];
                    Table target_tbl = target_col.getParent();

                    // Find what index it's using
                    // This is a rough approximation...
                    Index target_idx = null;
                    for (Index idx : indexes) {
                        if (idx.getParent().equals(target_tbl)) {
                            for (Column col : CatalogUtil.getColumns(idx.getColumns())) {
                                if (col.equals(target_col)) {
                                    target_idx = idx;
                                    break;
                                }
                            } // FOR
                        }
                    } // FOR
                    if (target_idx == null) {
                        return (false);
                    }
                }
            } // FOR

        } // FOR
        return (true);
    }
   
    /**
     * Extract a ColumnSet for the given Statement catalog object that only
     * consists of the Columns that are referenced in the list of tables. If
     * convert_params is set to true, then we will convert any StmtParameters
     * that are mapped to ProcParameter directly into the ProcParameter object.
     *
     * @param catalog_stmt
     * @param convert_params
     * @param catalog_tables
     * @return
     * @throws Exception
     */
    public static PredicatePairs extractStatementPredicates(final Statement catalog_stmt,
                                                            final boolean convert_params,
                                                            final Table... catalog_tables) {
        final Database catalog_db = (Database) catalog_stmt.getParent().getParent();
        final Set<Table> tables = new HashSet<Table>();
        final Collection<String> table_keys = new HashSet<String>();
        for (Table table : catalog_tables) {
            // For some reason we get a null table when we use itemsArray up above
            if (table == null)
                continue;
            assert (table != null) : "Null table object? " + Arrays.toString(catalog_tables);
            tables.add(table);
            table_keys.add(CatalogKey.createKey(table));
        } // FOR

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Pair<String, Collection<String>> key = Pair.of(CatalogKey.createKey(catalog_stmt), table_keys);
        PredicatePairs cset = cache.EXTRACTED_PREDICATES.get(key);
        if (cset == null) {
            cset = new PredicatePairs();
            AbstractPlanNode root_node = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);

            try {
                // WHERE Clause
                if (catalog_stmt.getExptree() != null && !catalog_stmt.getExptree().isEmpty()) {
                    AbstractExpression root_exp = ExpressionUtil.deserializeExpression(catalog_db, catalog_stmt.getExptree());
                    CatalogUtil.extractExpressionPredicates(catalog_stmt,
                                                           catalog_db,
                                                           cset,
                                                           root_exp,
                                                           convert_params,
                                                           tables);
                }
                // INSERT
                if (catalog_stmt.getQuerytype() == QueryType.INSERT.getValue()) {
                    CatalogUtil.extractInsertPredicates(catalog_stmt,
                                                       cset,
                                                       root_node,
                                                       convert_params,
                                                       catalog_tables);
                    // UPDATE
                    // XXX: Should we be doing this?
                } else if (catalog_stmt.getQuerytype() == QueryType.UPDATE.getValue()) {
                    CatalogUtil.extractUpdatePredicates(catalog_stmt,
                                                       catalog_db,
                                                       cset,
                                                       root_node,
                                                       convert_params,
                                                       tables);
                }
            } catch (Exception ex) {
                throw new RuntimeException("Failed to extract ColumnSet for " + catalog_stmt, ex);
            }

            cache.EXTRACTED_PREDICATES.put(key, cset);
        }
        return (cset);
    }

    /**
     * Extract ColumnSet for the SET portion of an UPDATE statement
     *
     * @param catalog_stmt
     * @param catalog_tables
     * @return
     * @throws Exception
     */
    public static PredicatePairs extractUpdateColumnSet(final Statement catalog_stmt,
                                                   final boolean convert_params,
                                                   final Table... catalog_tables) throws Exception {
        assert (catalog_stmt.getQuerytype() == QueryType.UPDATE.getValue());
        final Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);

        final Set<Table> tables = new HashSet<Table>();
        final Collection<String> table_keys = new HashSet<String>();
        for (Table table : catalog_tables) {
            tables.add(table);
            table_keys.add(CatalogKey.createKey(table));
        } // FOR

        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Pair<String, Collection<String>> key = Pair.of(CatalogKey.createKey(catalog_stmt), table_keys);
        PredicatePairs cset = cache.EXTRACTED_PREDICATES.get(key);
        if (cset == null) {
            cset = new PredicatePairs();
            AbstractPlanNode root_node = PlanNodeUtil.getRootPlanNodeForStatement(catalog_stmt, true);
            CatalogUtil.extractUpdatePredicates(catalog_stmt, catalog_db, cset, root_node, convert_params, tables);
            cache.EXTRACTED_PREDICATES.put(key, cset);
        }
        return (cset);
    }

    /**
     * @param stats_catalog_db
     * @param last_cset
     * @param root_exp
     * @param catalog_tables
     */
    public static PredicatePairs extractFragmentPredicates(final PlanFragment catalog_frag,
                                                     final boolean convert_params,
                                                     final Collection<Table> catalog_tables) throws Exception {
        final Statement catalog_stmt = (Statement) catalog_frag.getParent();
        final Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
        // if (catalog_frag.guid == 279) LOG.setLevel(Level.DEBUG);

        // We need to be clever about what we're doing here
        // We always have to examine the fragment (rather than just the entire
        // Statement), because
        // we don't know whehter they want the multi-sited version or not
        final Collection<String> table_keys = CatalogKey.createKeys(catalog_tables);

        if (debug.val)
            LOG.debug("Extracting column set for fragment #" + catalog_frag.getName() + ": " + catalog_tables);
        final CatalogUtil.Cache cache = CatalogUtil.getCatalogCache(catalog_stmt);
        Pair<String, Collection<String>> key = Pair.of(CatalogKey.createKey(catalog_frag), table_keys);
        PredicatePairs cset = cache.EXTRACTED_PREDICATES.get(key);
        if (cset == null) {
            AbstractPlanNode root_node = PlanNodeUtil.getPlanNodeTreeForPlanFragment(catalog_frag);
            // LOG.debug("PlanFragment Node:\n" +
            // PlanNodeUtil.debug(root_node));
            cset = new PredicatePairs();
            CatalogUtil.extractPlanNodePredicates(catalog_stmt, catalog_db, cset, root_node, convert_params, catalog_tables);
            cache.EXTRACTED_PREDICATES.put(key, cset);
        }

        return (cset);
    }

    /**
     * @param catalog_stmt
     * @param catalogContext
     * @param cset
     * @param root_node
     * @param tables
     * @throws Exception
     */
    public static void extractInsertPredicates(final Statement catalog_stmt,
                                               final PredicatePairs cset,
                                               final AbstractPlanNode root_node,
                                               final boolean convert_params,
                                               final Table... catalog_tables) throws Exception {
        assert (catalog_stmt.getQuerytype() == QueryType.INSERT.getValue());
        final Database catalog_db = CatalogUtil.getDatabase(catalog_stmt);
        final List<List<CatalogType>> materialize_elements = new ArrayList<List<CatalogType>>();

        // Find the MaterializePlanNode that feeds into the Insert
        // This will have the list of columns that will be used to insert into
        // the table
        new PlanNodeTreeWalker() {
            @Override
            protected void callback(final AbstractPlanNode node) {
                // We should find the Materialize node before the Insert
                if (node instanceof MaterializePlanNode) {
                    for (Integer column_guid : node.getOutputColumnGUIDs()) {
                        PlanColumn column = PlannerContext.singleton().get(column_guid);
                        assert (column != null);
                        AbstractExpression exp = column.getExpression();

                        // Now extract the CatalogType objects that are being
                        // referenced by this materialization column
                        final List<CatalogType> catalog_refs = new ArrayList<CatalogType>();
                        new ExpressionTreeWalker() {
                            @Override
                            protected void callback(AbstractExpression exp) {
                                if (!(exp instanceof AbstractValueExpression))
                                    return;
                                CatalogType element = null;
                                switch (exp.getExpressionType()) {
                                    case VALUE_PARAMETER: {
                                        int param_idx = ((ParameterValueExpression) exp).getParameterId();
                                        element = catalog_stmt.getParameters().get(param_idx);
                                        if (element == null) {
                                            LOG.warn("ERROR: Unable to find Parameter object in catalog [" + ((ParameterValueExpression) exp).getParameterId() + "]");
                                            this.stop();
                                        }
                                        // We want to use the ProcParameter instead of the StmtParameter
                                        // It's not an error if the StmtParameter is not mapped to a
                                        // ProcParameter
                                        if (convert_params && ((StmtParameter) element).getProcparameter() != null) {
                                            LOG.debug(element + "(" + element + ") --> ProcParameter[" + element.getField("procparameter") + "]");
                                            element = ((StmtParameter) element).getProcparameter();
                                        }
                                        break;
                                    }
                                    case VALUE_TUPLE_ADDRESS:
                                    case VALUE_TUPLE: {
                                        // This shouldn't happen, but it is nice
                                        // to be told if it does...
                                        LOG.warn("Unexpected " + exp.getClass().getSimpleName() + " node when examining " + node.getClass().getSimpleName() + " for " + catalog_stmt);
                                        break;
                                    }
                                    default: {
                                        // Do nothing...
                                    }
                                } // SWITCH
                                if (element != null) {
                                    catalog_refs.add(element);
                                    LOG.debug(node + ": " + catalog_refs);
                                }
                                return;
                            }
                        }.traverse(exp);
                        materialize_elements.add(catalog_refs);
                    } // FOR

                    // InsertPlanNode
                } else if (node instanceof InsertPlanNode) {
                    InsertPlanNode insert_node = (InsertPlanNode) node;
                    Table catalog_tbl = catalog_db.getTables().get(insert_node.getTargetTableName());

                    // We only support when the Materialize node is inserting
                    // data into all columns
                    if (materialize_elements.size() != catalog_tbl.getColumns().size()) {
                        String msg = String.format("%s has %d columns but the MaterializePlanNode has %d output columns", catalog_tbl, catalog_tbl.getColumns().size(), materialize_elements.size());
                        LOG.fatal(PlanNodeUtil.debug(node));
                        throw new RuntimeException(msg);
                    }

                    // Loop through each column position and add an entry in the
                    // ColumnSet for
                    // each catalog item that was used in the
                    // MaterializePlanNode
                    // For example, if the INSERT clause for a column FOO was
                    // "PARAM1 + PARAM2", then
                    // the column set will have separate entries for FOO->PARAM1
                    // and FOO->PARAM2
                    LOG.debug("Materialize Elements: " + materialize_elements);
                    for (int ctr = 0, cnt = materialize_elements.size(); ctr < cnt; ctr++) {
                        Column catalog_col = catalog_tbl.getColumns().get(ctr);
                        assert (catalog_col != null);
                        for (CatalogType catalog_item : materialize_elements.get(ctr)) {
                            cset.add(catalog_col, catalog_item, ExpressionType.COMPARE_EQUAL, catalog_stmt);
                            LOG.debug(String.format("[%02d] Adding Entry %s => %s", ctr, catalog_col, catalog_item));
                        } // FOR
                    } // FOR
                }
                return;
            }
        }.traverse(root_node);
        return;
    }

    /**
     * @param catalog_stmt
     * @param catalog_db
     * @param cset
     * @param root_node
     * @param tables
     * @throws Exception
     */
    public static void extractPlanNodePredicates(final Statement catalog_stmt,
                                                 final Database catalog_db,
                                                 final PredicatePairs cset,
                                                 final AbstractPlanNode root_node,
                                                 final boolean convert_params,
                                                 final Collection<Table> tables) throws Exception {
        // Walk through the tree and figure out how the tables are being
        // referenced
        new PlanNodeTreeWalker() {
            {
                this.setAllowRevisit(true);
            }

            protected void populate_children(PlanNodeTreeWalker.Children<AbstractPlanNode> children, AbstractPlanNode node) {
                super.populate_children(children, node);
                List<AbstractPlanNode> to_add = new ArrayList<AbstractPlanNode>();
                for (AbstractPlanNode child : children.getBefore()) {
                    to_add.addAll(child.getInlinePlanNodes().values());
                } // FOR
                children.addBefore(to_add);

                to_add.clear();
                for (AbstractPlanNode child : children.getAfter()) {
                    to_add.addAll(child.getInlinePlanNodes().values());
                } // FOR
                children.addAfter(to_add);

                if (debug.val) {
                    LOG.debug(children);
                    LOG.debug("-------------------------");
                }
            };

            @Override
            protected void callback(final AbstractPlanNode node) {
                try {
                    if (debug.val)
                        LOG.debug("Examining child node " + node);
                    this._callback(node);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw new RuntimeException(ex);
                }
            }

            protected void _callback(final AbstractPlanNode node) throws Exception {
                ListOrderedSet<AbstractExpression> exps = new ListOrderedSet<AbstractExpression>();

                // IndexScanPlanNode
                if (node instanceof IndexScanPlanNode) {
                    IndexScanPlanNode cast_node = (IndexScanPlanNode) node;
                    Table catalog_tbl = catalog_db.getTables().get(cast_node.getTargetTableName());
                    assert (catalog_tbl != null);
                    Index catalog_idx = catalog_tbl.getIndexes().get(cast_node.getTargetIndexName());
                    assert (catalog_idx != null);

                    // Search Key Expressions
                    List<ColumnRef> index_cols = CatalogUtil.getSortedCatalogItems(catalog_idx.getColumns(), "index");
                    for (int i = 0, cnt = cast_node.getSearchKeyExpressions().size(); i < cnt; i++) {
                        AbstractExpression index_exp = cast_node.getSearchKeyExpressions().get(i);
                        Column catalog_col = index_cols.get(i).getColumn();
                        if (debug.val)
                            LOG.debug("[" + i + "] " + catalog_col);
                        exps.add(CatalogUtil.createTempExpression(catalog_col, index_exp));
                        if (debug.val)
                            LOG.debug("Added temp index search key expression:\n" + ExpressionUtil.debug(exps.get(exps.size() - 1)));
                    } // FOR

                    // End Expression
                    if (cast_node.getEndExpression() != null) {
                        exps.add(cast_node.getEndExpression());
                        if (debug.val)
                            LOG.debug("Added scan end expression:\n" + ExpressionUtil.debug(exps.get(exps.size() - 1)));
                    }

                    // Post-Scan Expression
                    if (cast_node.getPredicate() != null) {
                        exps.add(cast_node.getPredicate());
                        if (debug.val)
                            LOG.debug("Added post-scan predicate:\n" + ExpressionUtil.debug(exps.get(exps.size() - 1)));
                    }

                    // SeqScanPlanNode
                } else if (node instanceof SeqScanPlanNode) {
                    SeqScanPlanNode cast_node = (SeqScanPlanNode) node;
                    if (cast_node.getPredicate() != null) {
                        exps.add(cast_node.getPredicate());
                        if (debug.val)
                            LOG.debug("Adding scan node predicate:\n" + ExpressionUtil.debug(exps.get(exps.size() - 1)));
                    }

                    // Materialize
                } else if (node instanceof MaterializePlanNode) {
                    // Assume that if we're here, then they want the mappings
                    // from columns to StmtParameters
                    assert (tables.size() == 1);
                    Table catalog_tbl = CollectionUtil.first(tables);
                    for (int ctr = 0, cnt = node.getOutputColumnGUIDs().size(); ctr < cnt; ctr++) {
                        int column_guid = node.getOutputColumnGUIDs().get(ctr);
                        PlanColumn column = PlannerContext.singleton().get(column_guid);
                        assert (column != null);

                        Column catalog_col = catalog_tbl.getColumns().get(column.getDisplayName());
                        assert (catalog_col != null) : "Invalid column name '" + column.getDisplayName() + "' for " + catalog_tbl;

                        AbstractExpression exp = column.getExpression();
                        if (exp instanceof ParameterValueExpression) {
                            StmtParameter catalog_param = catalog_stmt.getParameters().get(((ParameterValueExpression) exp).getParameterId());
                            cset.add(catalog_col, catalog_param, ExpressionType.COMPARE_EQUAL, catalog_stmt);
                        } else if (exp instanceof AbstractValueExpression) {
                            if (debug.val)
                                LOG.debug("Ignoring AbstractExpressionType type: " + exp);
                        } else {
                            throw new Exception("Unexpected AbstractExpression type: " + exp);
                        }

                    } // FOR
                    // Join Nodes
                } else if (node instanceof AbstractJoinPlanNode) {
                    AbstractJoinPlanNode cast_node = (AbstractJoinPlanNode) node;
                    if (cast_node.getPredicate() != null) {
                        exps.add(cast_node.getPredicate());
                        if (debug.val)
                            LOG.debug("Added join node predicate: " + ExpressionUtil.debug(exps.get(exps.size() - 1)));
                    }
                }

                if (debug.val)
                    LOG.debug("Extracting expressions information from " + node + " for tables " + tables);
                for (AbstractExpression exp : exps) {
                    if (exp == null)
                        continue;
                    CatalogUtil.extractExpressionPredicates(catalog_stmt, catalog_db, cset, exp, convert_params, tables);
                } // FOR
                return;
            }
        }.traverse(root_node);
        return;
    }

    /**
     * @param catalog_stmt
     * @param catalog_db
     * @param cset
     * @param root_exp
     * @param tables
     * @throws Exception
     */
    public static void extractExpressionPredicates(final Statement catalog_stmt,
                                                   final Database catalog_db,
                                                   final PredicatePairs cset,
                                                   final AbstractExpression root_exp,
                                                   final boolean convert_params,
                                                   final Collection<Table> tables) throws Exception {
        if (debug.val)
            LOG.debug(catalog_stmt + "\n" + ExpressionUtil.debug(root_exp));

        new ExpressionTreeWalker() {
            {
                this.setAllowRevisit(true);
            }

            boolean on_leftside = true;
            ExpressionType compare_exp = null;
            final Set<CatalogType> left = new HashSet<CatalogType>();
            final Set<CatalogType> right = new HashSet<CatalogType>();
            final Set<Table> used_tables = new HashSet<Table>();

            private void populate() {
                if (debug.val) {
                    LOG.debug("POPULATE!");
                    LOG.debug("LEFT:  " + this.left);
                    LOG.debug("RIGHT: " + this.right);
                    LOG.debug("USED:  " + this.used_tables);
                }
                //
                // Both sides can't be empty and our extract items must cover
                // all the tables that we are looking for. That is, if we were
                // asked
                // to lookup information on two tables (i.e., a JOIN), then we
                // need
                // to have extract information from our tree that uses those two
                // tables
                //
                if (!this.left.isEmpty() && !this.right.isEmpty()) { // &&
                                                                     // (this.used_tables.size()
                                                                     // ==
                                                                     // tables.size()))
                                                                     // {
                    for (CatalogType left_element : this.left) {
                        for (CatalogType right_element : this.right) {
                            if (debug.val)
                                LOG.debug("Added entry: [" + left_element + " " + this.compare_exp + " " + right_element + "]");
                            cset.add(left_element, right_element, this.compare_exp, catalog_stmt);
                            if (debug.val)
                                LOG.debug("ColumnSet:\n" + cset.debug());
                        } // FOR
                    } // FOR
                }
                this.left.clear();
                this.right.clear();
                this.used_tables.clear();
                this.on_leftside = true;
                this.compare_exp = null;
                return;
            }

            @Override
            protected void callback(AbstractExpression exp) {
                if (debug.val)
                    LOG.debug("CALLBACK(counter=" + this.getCounter() + ") " + exp.getClass().getSimpleName());
                // if (exp instanceof ComparisonExpression) {
                // LOG.debug("\n" + ExpressionUtil.debug(exp));
                // }

                // ComparisionExpression
                // We need to switch from the left to the right element set.
                // TODO: This assumes that they aren't doing something funky
                // like "(col1 AND col2) AND col3"
                if (exp instanceof ComparisonExpression) {
                    if (!this.on_leftside) {
                        LOG.error("ERROR: Invalid expression tree format : Unexpected ComparisonExpression");
                        this.stop();
                        return;
                    }
                    this.on_leftside = false;
                    this.compare_exp = exp.getExpressionType();

                    // Special Case: IN
                    if (exp instanceof InComparisonExpression) {
                        InComparisonExpression in_exp = (InComparisonExpression) exp;
                        for (AbstractExpression value_exp : in_exp.getValues()) {
                            this.processValueExpression(value_exp);
                        } // FOR
                        this.populate();
                    }
                }
                // When we hit a conjunction, we need to make a cross
                // product of the left and
                // right attribute sets
                else if (exp instanceof ConjunctionExpression) {
                    this.populate();
                }
                // Values: NULL, Tuple, Constants, Parameters
                else if (exp instanceof AbstractValueExpression) {
                    this.processValueExpression(exp);
                }
                return;
            }

            /**
             * @param exp
             */
            private void processValueExpression(AbstractExpression exp) {
                CatalogType element = null;
                switch (exp.getExpressionType()) {
                    case VALUE_TUPLE: {
                        String table_name = ((TupleValueExpression) exp).getTableName();
                        String column_name = ((TupleValueExpression) exp).getColumnName();
                        if (debug.val)
                            LOG.debug("VALUE TUPLE: " + table_name + "." + column_name);
                        Table catalog_tbl = catalog_db.getTables().get(table_name);

                        // Always use because we don't know whether the next
                        // table will be one of the ones that we are looking for
                        if (tables.contains(catalog_tbl)) {
                            if (debug.val)
                                LOG.debug("FOUND VALUE_TUPLE: " + table_name);
                            element = catalog_tbl.getColumns().get(column_name);
                            this.used_tables.add(catalog_tbl);
                        }
                        break;
                    }
                    case VALUE_TUPLE_ADDRESS: {
                        //
                        // ????
                        //
                        // String table_name =
                        // ((TupleAddressExpression)exp).getTableName();
                        // String column_name =
                        // ((TupleAddressExpression)exp).getColumnName();
                        // element = CatalogUtil.getColumn(catalog_db,
                        // table_name, column_name);
                        break;
                    }
                    case VALUE_CONSTANT: {
                        element = new ConstantValue();
                        ((ConstantValue) element).setIs_null(false);
                        ((ConstantValue) element).setType(((ConstantValueExpression) exp).getValueType().getValue());
                        ((ConstantValue) element).setValue(((ConstantValueExpression) exp).getValue());
                        break;
                    }
                    case VALUE_PARAMETER: {
                        int param_idx = ((ParameterValueExpression) exp).getParameterId();
                        element = catalog_stmt.getParameters().get(param_idx);
                        if (element == null) {
                            LOG.warn("ERROR: Unable to find Parameter object in " + catalog_stmt.fullName() + " [" + ((ParameterValueExpression) exp).getParameterId() + "]");
                            this.stop();
                        }
                        // We want to use the ProcParameter instead of the StmtParameter
                        // It's not an error if the StmtParameter is not mapped
                        // to a ProcParameter
                        if (convert_params && ((StmtParameter) element).getProcparameter() != null) {
                            element = ((StmtParameter) element).getProcparameter();
                        }

                        break;
                    }
                    default:
                        // Do nothing...
                } // SWITCH
                if (element != null) {
                    if (this.on_leftside)
                        this.left.add(element);
                    else
                        this.right.add(element);
                    if (debug.val) {
                        LOG.debug("LEFT: " + this.left);
                        LOG.debug("RIGHT: " + this.right);
                    }
                }
            }

            @Override
            protected void callback_last(AbstractExpression element) {
                this.populate();
            }
        }.traverse(root_exp);
        return;
    }

    /**
     * @param catalog_stmt
     * @param catalog_db
     * @param cset
     * @param root_node
     * @param tables
     * @throws Exception
     */
    public static void extractUpdatePredicates(final Statement catalog_stmt,
                                               final Database catalog_db,
                                               final PredicatePairs cset,
                                               final AbstractPlanNode root_node,
                                               final boolean convert_params,
                                               final Collection<Table> tables) throws Exception {
        // Grab the columns that the plannode is going to update from the
        // children feeding into us.
        Collection<UpdatePlanNode> update_nodes = PlanNodeUtil.getPlanNodes(root_node, UpdatePlanNode.class);
        for (UpdatePlanNode update_node : update_nodes) {
            Table catalog_tbl = catalog_db.getTables().get(update_node.getTargetTableName());
            // Grab all the scan nodes that are feeding into us
            Collection<AbstractScanPlanNode> scan_nodes = PlanNodeUtil.getPlanNodes(root_node, AbstractScanPlanNode.class);
            assert (!scan_nodes.isEmpty());
            for (AbstractScanPlanNode scan_node : scan_nodes) {
                List<PlanColumn> output_cols = new ArrayList<PlanColumn>();
                List<AbstractExpression> output_exps = new ArrayList<AbstractExpression>();
                if (scan_node.getOutputColumnGUIDs().isEmpty()) {
                    if (scan_node.getInlinePlanNode(PlanNodeType.PROJECTION) != null) {
                        ProjectionPlanNode proj_node = (ProjectionPlanNode) scan_node.getInlinePlanNode(PlanNodeType.PROJECTION);
                        for (int guid : proj_node.getOutputColumnGUIDs()) {
                            PlanColumn column = PlannerContext.singleton().get(guid);
                            assert (column != null);
                            output_cols.add(column);
                            output_exps.add(column.getExpression());
                        } // FOR
                    }
                } else {
                    for (int guid : scan_node.getOutputColumnGUIDs()) {
                        PlanColumn column = PlannerContext.singleton().get(guid);
                        assert (column != null);
                        output_cols.add(column);
                        output_exps.add(column.getExpression());
                    } // FOR
                }

                for (int i = 0, cnt = output_cols.size(); i < cnt; i++) {
                    PlanColumn column = output_cols.get(i);
                    AbstractExpression exp = output_exps.get(i);
                    // Skip TupleAddressExpression
                    if (!(exp instanceof TupleAddressExpression)) {
                        //
                        // Make a temporary expression where COL = Expression
                        // Har har har! I'm so clever!
                        //
                        String column_name = (column.originColumnName() != null ? column.originColumnName() : column.getDisplayName());
                        Column catalog_col = catalog_tbl.getColumns().get(column_name);
                        if (catalog_col == null)
                            System.err.println(catalog_tbl + ": " + CatalogUtil.debug(catalog_tbl.getColumns()));
                        assert (catalog_col != null) : "Missing column '" + catalog_tbl.getName() + "." + column_name + "'";
                        AbstractExpression root_exp = CatalogUtil.createTempExpression(catalog_col, exp);
                        // System.out.println(ExpressionUtil.debug(root_exp));
                        CatalogUtil.extractExpressionPredicates(catalog_stmt, catalog_db, cset, root_exp, convert_params, tables);
                    }
                } // FOR
            }
            // System.out.println(PlanNodeUtil.debug(root_node));
        }
    }

    /**
     * Create a temporary column expression that can be used with
     * extractExpressionColumnSet
     *
     * @param catalog_col
     * @param exp
     * @return
     */
    private static AbstractExpression createTempExpression(Column catalog_col, AbstractExpression exp) {
        Table catalog_tbl = (Table) catalog_col.getParent();

        TupleValueExpression tuple_exp = new TupleValueExpression();
        tuple_exp.setTableName(catalog_tbl.getName());
        tuple_exp.setColumnIndex(catalog_col.getIndex());
        tuple_exp.setColumnAlias(catalog_col.getName());
        tuple_exp.setColumnName(catalog_col.getName());

        return (new ComparisonExpression(ExpressionType.COMPARE_EQUAL, tuple_exp, exp));
    }

    /**
     * Return a PlanFragment for a given id. This is slow and should only be
     * used for debugging purposes
     *
     * @param catalog_obj
     * @param id
     * @return
     */
    public static PlanFragment getPlanFragment(CatalogType catalog_obj, int id) {
        Database catalog_db = CatalogUtil.getDatabase(catalog_obj);
        for (Procedure catalog_proc : catalog_db.getProcedures()) {
            for (Statement catalog_stmt : catalog_proc.getStatements()) {
                for (PlanFragment catalog_frag : catalog_stmt.getFragments())
                    if (catalog_frag.getId() == id)
                        return (catalog_frag);
                for (PlanFragment catalog_frag : catalog_stmt.getMs_fragments())
                    if (catalog_frag.getId() == id)
                        return (catalog_frag);
            } // FOR (stmt)
        } // FOR (proc)
        return (null);
    }

    // ------------------------------------------------------------
    // UTILITY METHODS
    // ------------------------------------------------------------

    /**
     * Return an ordered list of the parents for each of the catalog items
     */
    public static Collection<CatalogType> getParents(Iterable<? extends CatalogType> catalog_items) {
        Collection<CatalogType> parents = new ArrayList<CatalogType>();
        for (CatalogType item : catalog_items) {
            if (item != null) {
                parents.add(item.getParent());
            } else {
                parents.add(null);
            }
        } // FOR
        return (parents);
    }

    /**
     * @param <T>
     * @param <U>
     * @param items
     * @param field
     * @param value
     * @return
     */
    public static <T extends CatalogType, U> Set<T> findAll(Iterable<T> items, String field, U value) {
        Set<T> found = new ListOrderedSet<T>();
        for (T catalog_item : items) {
            assert (catalog_item != null);
            try {
                Object field_value = catalog_item.getField(field);
                if (field_value.equals(value))
                    found.add(catalog_item);
            } catch (NullPointerException ex) {
                LOG.fatal(catalog_item + ": " + catalog_item.getFields());
                LOG.fatal(catalog_item + " does not have a field '" + field + "'");
                throw ex;
            }
        } // FOR
        return (found);
    }

    /**
     * @param <T>
     * @param <U>
     * @param items
     * @param field
     * @param value
     * @return
     */
    public static <T extends CatalogType, U> T findOne(Iterable<T> items, String field, U value) {
        return (CollectionUtil.first(CatalogUtil.findAll(items, field, value)));
    }

    /**
     * @param item0
     * @param item1
     * @return
     */
    public static Pair<CatalogType, CatalogType> pair(CatalogType item0, CatalogType item1) {
        Pair<CatalogType, CatalogType> pair = null;
        if (item0.compareTo(item1) < 0) {
            pair = Pair.of(item0, item1);
        } else {
            pair = Pair.of(item1, item0);
        }
        return (pair);
    }

    /**
     * @param item
     * @return
     */
    public static String getDisplayName(CatalogType item) {
        return (CatalogUtil.getDisplayName(item, false));
    }

    /**
     * Return the list of display names for a collection of CatalogType
     *
     * @param items
     * @return
     */
    public static Collection<String> getDisplayNames(Iterable<? extends CatalogType> items) {
        List<String> ret = new ArrayList<String>();
        for (CatalogType i : items) {
            ret.add(i != null ? CatalogUtil.getDisplayName(i, false) : null);
        }
        return (ret);
    }
   
    /**
     * Return the list of display names for a collection of CatalogType
     *
     * @param items
     * @return
     */
    public static Collection<String> getDisplayNames(CatalogType...items) {
        List<String> ret = new ArrayList<String>();
        for (CatalogType i : items) {
            ret.add(i != null ? CatalogUtil.getDisplayName(i, false) : null);
        }
        return (ret);
    }
   
    /**
     * Return a mapping from the CatalogType handle to their display name
     * @param items
     * @return
     */
    public static Map<CatalogType, String> getDisplayNameMapping(CatalogType...items) {
        Map<CatalogType, String> ret = new HashMap<CatalogType, String>();
        for (CatalogType i : items) {
            if (i == null) continue;
            ret.put(i, CatalogUtil.getDisplayName(i, false));
        } // FOR
        return (ret);
    }
   
    /**
     * Return a mapping from the CatalogType handle to their display name
     * @param items
     * @return
     */
    public static Map<CatalogType, String> getDisplayNameMapping(Iterable<? extends CatalogType> items) {
        Map<CatalogType, String> ret = new HashMap<CatalogType, String>();
        for (CatalogType i : items) {
            if (i == null) continue;
            ret.put(i, CatalogUtil.getDisplayName(i, false));
        } // FOR
        return (ret);
    }

    /**
     * @param item
     * @param include_class
     * @return
     */
    public static String getDisplayName(CatalogType item, boolean include_class) {
        if (item != null) {
            String ret = null;

            // Column/Statement/Constraint/Index
            // Format: <Parent>.<Item>
            if (item instanceof Column || item instanceof Statement || item instanceof Constraint || item instanceof Index) {
                ret = String.format("%s.%s", item.getParent().getName(), item.getName());
            }
            // StmtParameter
            // Format: <Procedure>.<Statement>.<Item>
            else if (item instanceof StmtParameter) {
                ret = String.format("%s{%s.%s.#%02d}",
                                    item.getClass().getSimpleName(),
                                    item.getParent().getParent().getName(),
                                    item.getParent().getName(),
                                    ((StmtParameter)item).getIndex());
            }   
            // ProcParameter
            // Format: <Parent>.<Item>
            else if (item instanceof ProcParameter) {
                ret = String.format("%s{%s.#%02d}",
                                    item.getClass().getSimpleName(),
                                    item.getParent().getName(),
                                    ((ProcParameter)item).getIndex());
            }
            // PlanFragment
            // Format: <Procedure>.<Statement>.[Fragment #XYZ]
            else if (item instanceof PlanFragment) {
                ret = String.format("%s.%s.[Fragment #%s]", item.getParent().getParent().getName(), item.getParent().getName(), item.getName());
            }
            // ConstantValue
            // Format: ConstantValue{XYZ}
            else if (item instanceof ConstantValue) {
                ret = String.format("%s{%s}", item.getClass().getSimpleName(), ((ConstantValue) item).getValue());
            }
            // Everything Else
            // Format: <OptionalClassName>.<Item>
            else {
                ret = String.format("%s%s", (include_class ? item.getClass().getSimpleName() + ":" : ""), item.getName());
            }
            return (ret);
        }
        return (null);
    }

    /**
     * @param catalog_stmt
     * @return
     */
    public static String debugJSON(Statement catalog_stmt) {
        String jsonString = Encoder.hexDecodeToString(catalog_stmt.getFullplan());
        String line = "\n----------------------------------------\n";
        String ret = "FULL PLAN ORIG STRING:\n" + jsonString + line;

        for (PlanFragment catalog_frgmt : catalog_stmt.getFragments()) {
            jsonString = Encoder.hexDecodeToString(catalog_frgmt.getPlannodetree());
            try {
                JSONObject jsonObject = new JSONObject(jsonString);
                ret += "FRAGMENT " + catalog_frgmt.getName() + "\n" + jsonObject.toString(2) + line;
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new RuntimeException(ex);
            }
        } // FOR
        return (ret);
    }

    /**
     * Return a string representation of this CatalogType handle
     *
     * @param catalog_item
     * @return
     */
    public static String debug(CatalogType catalog_item) {
        Map<String, Object> m = new ListOrderedMap<String, Object>();
        for (String field : CollectionUtils.union(catalog_item.getFields(), catalog_item.getChildFields())) {
            String val_str = null;
            if (catalog_item.getChildFields().contains(field)) {
                val_str = CatalogUtil.debug(catalog_item.getChildren(field));
            } else {
                Object val = catalog_item.getField(field);
                if (val != null) {
                    val_str = val.toString();
                } else {
                    val_str = "null";
                }
            }
            m.put(field, val_str);
        } // FOR
        return (catalog_item.fullName() + "\n" + StringUtil.formatMaps(m));
    }

    public static String debug(Collection<? extends CatalogType> items) {
        if (items == null)
            return (null);
        String ret = "";
        String add = "";
        for (CatalogType item : items) {
            if (item != null) {
                ret += add + CatalogUtil.getDisplayName(item);
                add = ", ";
            }
        } // FOR
        return ("[" + ret + "]");
    }
} // END CLASS
TOP

Related Classes of edu.brown.catalog.CatalogUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.