package edu.brown.mappings; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.json.JSONException; import org.json.JSONObject; import org.voltdb.ParameterSet; import org.voltdb.VoltType; import org.voltdb.catalog.Database; import org.voltdb.catalog.ProcParameter; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Statement; import org.voltdb.catalog.StmtParameter; import org.voltdb.types.TimestampType; import org.voltdb.utils.JarReader; import edu.brown.catalog.CatalogUtil; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.utils.CollectionUtil; import edu.brown.utils.FileUtil; import edu.brown.utils.ProjectType; /** * @author pavlo */ public abstract class ParametersUtil { private static final Logger LOG = Logger.getLogger(ParametersUtil.class); private static final LoggerBoolean debug = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug); } public static final int NULL_PROC_PARAMETER_OFFSET = -1; /** * Find a parameter correlations file for a given project type * @param current * @param type * @return * @throws IOException */ public static File getParameterMappingsFile(ProjectType type) throws IOException { // HACK HACK HACK File srcDir = FileUtil.findDirectory("src"); File mappingsFile = FileUtil.join(srcDir.getAbsolutePath(), "benchmarks", type.getPackageName().replace(".", File.separator), type.name().toLowerCase() + ".mappings"); return (mappingsFile); } /** * */ public static class DefaultParameterMapping extends HashMap<String, Map<Integer, Integer>> { private static final long serialVersionUID = 1L; private final Map<String, Map<Integer, String>> stmt_param_names = new HashMap<String, Map<Integer, String>>(); private final Map<String, Map<String, Integer>> stmt_param_idxs = new HashMap<String, Map<String, Integer>>(); private final Map<Integer, String> proc_param_names = new HashMap<Integer, String>(); private final Map<String, Integer> proc_param_idxs = new HashMap<String, Integer>(); public DefaultParameterMapping() { super(); } public Set<String> getStmtParamNames(String stmt_name) { return (this.stmt_param_idxs.get(stmt_name).keySet()); } public String getStmtParamName(String stmt_name, int idx) { if (this.stmt_param_names.containsKey(stmt_name)) { return (this.stmt_param_names.get(stmt_name).get(idx)); } return (null); } public Integer getStmtParamIndex(String stmt_name, String param_name) { if (this.stmt_param_idxs.containsKey(stmt_name)) { return (this.stmt_param_idxs.get(stmt_name).get(param_name)); } return (null); } public Set<String> getProcParamNames() { return (this.proc_param_idxs.keySet()); } public String getProcParamName(int idx) { return (this.proc_param_names.get(idx)); } public Integer getProcParamIndex(String param_name) { return (this.proc_param_idxs.get(param_name)); } /** * Adds a new mapping entry for a particular query in the catalogs. The * stmt_param is the index of the StmtParameter in the catalogs that * gets mapped to an index location of a ProcParamter * * @param stmt_name * - the name of the Statement for this parameter mapping * @param stmt_param * - the index of the StmtParameter for this query * @param proc_param * - the index of the ProcParameter for this StmtParameter * corresponds to */ public void add(String stmt_name, Integer stmt_param, String stmt_param_name, Integer proc_param, String proc_param_name) { if (!this.containsKey(stmt_name)) { this.put(stmt_name, new HashMap<Integer, Integer>()); this.stmt_param_names.put(stmt_name, new HashMap<Integer, String>()); this.stmt_param_idxs.put(stmt_name, new HashMap<String, Integer>()); } this.get(stmt_name).put(stmt_param, proc_param); this.stmt_param_names.get(stmt_name).put(stmt_param, stmt_param_name); this.stmt_param_idxs.get(stmt_name).put(stmt_param_name, stmt_param); this.proc_param_names.put(proc_param, proc_param_name); this.proc_param_idxs.put(proc_param_name, proc_param); } /** * Adds a new mapping entry for a particular query in the catalogs. * @param proc_param * @param stmt_name * @param stmt_param */ public void add(int proc_param, String stmt_name, int stmt_param) { if (!this.containsKey(stmt_name)) { this.put(stmt_name, new HashMap<Integer, Integer>()); this.stmt_param_names.put(stmt_name, new HashMap<Integer, String>()); this.stmt_param_idxs.put(stmt_name, new HashMap<String, Integer>()); } this.get(stmt_name).put(stmt_param, proc_param); } } // END CLASS /** * Load a ParameterMappingsSet from a project jar file. * @param catalog_db * @param jarPath * @return */ public static ParameterMappingsSet getParameterMappingsSetFromJar(Database catalog_db, File jarPath) { LOG.debug("Loading ParameterMappingsSet from jar file at '" + jarPath.getAbsolutePath() + "'"); if (!jarPath.exists()) { throw new RuntimeException("The catalog jar file '" + jarPath + "' does not exist"); } String paramFile = catalog_db.getProject() + ".mappings"; String serialized = null; try { serialized = JarReader.readFileFromJarfile(jarPath.getAbsolutePath(), paramFile); } catch (Exception ex) { String msg = String.format("Failed to find ParameterMappingSet file '%s' in '%s'", paramFile, jarPath); LOG.warn(msg); // , (debug.val ? ex : null)); return (null); } ParameterMappingsSet pms = null; try { pms = new ParameterMappingsSet(); JSONObject json = new JSONObject(serialized); pms.fromJSON(json, catalog_db); } catch (JSONException ex) { String msg = String.format("Failed to load ParameterMappingSet '%s' from '%s'", paramFile, jarPath); throw new RuntimeException(msg, ex); } LOG.debug(String.format("Loaded ParameterMappingSet '%s' from '%s'", paramFile, jarPath)); return (pms); } /** * Convert a ParameterCorrelations object into a map * String->ParameterMapping * * @param catalog_db * @param mappings * @return * @throws Exception */ public static Map<String, DefaultParameterMapping> generateXrefFromParameterMappings(Database catalog_db, ParameterMappingsSet mappings) { assert (catalog_db != null); assert (mappings != null); Map<String, DefaultParameterMapping> xrefMap = new HashMap<String, DefaultParameterMapping>(); for (Procedure catalog_proc : catalog_db.getProcedures()) { String proc_name = catalog_proc.getName(); DefaultParameterMapping map = new DefaultParameterMapping(); for (Statement catalog_stmt : catalog_proc.getStatements()) { String stmt_name = catalog_stmt.getName(); for (StmtParameter catalog_stmt_param : catalog_stmt.getParameters()) { String stmt_param_name = catalog_stmt_param.getName(); Collection<ParameterMapping> m = mappings.get(catalog_stmt, catalog_stmt_param); if (m.isEmpty()) { if (debug.val) LOG.debug("No ParameterMapping found for " + CatalogUtil.getDisplayName(catalog_stmt_param) + ". Skipping..."); continue; } // HACK: I'm lazy, just take the first one for now ParameterMapping c = CollectionUtil.first(m); assert (c != null); Integer proc_param = c.getProcParameter().getIndex(); String proc_param_name = c.getProcParameter().getName(); map.add(stmt_name, catalog_stmt_param.getIndex(), stmt_param_name, proc_param, proc_param_name); } // FOR (StmtParameter) } // FOR (Statement) xrefMap.put(proc_name, map); } // FOR (Procedure) assert (xrefMap.size() == catalog_db.getProcedures().size()); return (xrefMap); } public static void applyParameterMappings(Database catalog_db, ParameterMappingsSet mappings) { Map<String, DefaultParameterMapping> proc_mapping = ParametersUtil.generateXrefFromParameterMappings(catalog_db, mappings); ParametersUtil.populateCatalog(catalog_db, proc_mapping, true); return; } /** * @param catalog_db * @param proc_mapping * @throws Exception */ public static void populateCatalog(Database catalog_db, Map<String, DefaultParameterMapping> proc_mapping) throws Exception { populateCatalog(catalog_db, proc_mapping, false); } public static void populateCatalog(Database catalog_db, Map<String, DefaultParameterMapping> proc_mapping, boolean force) { // For each Procedure in the catalog, we need to find the matching record // in the mapping and update the catalog elements as necessary for (String proc_name : proc_mapping.keySet()) { Procedure catalog_proc = catalog_db.getProcedures().getIgnoreCase(proc_name); if (catalog_proc == null) { // throw new RuntimeException("Unknown Procedure name '" + proc_name + "' in ParameterMapping"); continue; } if (debug.val) LOG.debug("Updating parameter mapping for Procedure '" + proc_name + "'"); DefaultParameterMapping map = proc_mapping.get(proc_name); for (String stmt_name : map.keySet()) { Statement catalog_stmt = catalog_proc.getStatements().get(stmt_name); if (catalog_stmt == null) { throw new RuntimeException("Unknown Statement name '" + stmt_name + "' in ParameterMapping"); } for (Integer stmt_param : map.get(stmt_name).keySet()) { StmtParameter catalog_stmt_param = catalog_stmt.getParameters().get(stmt_param); Integer proc_param = map.get(stmt_name).get(stmt_param); ProcParameter catalog_proc_param = catalog_proc.getParameters().get(proc_param); // Skip if it already has the proper ProcParameter set if (!force && catalog_stmt_param.getProcparameter() != null && catalog_stmt_param.getProcparameter().equals(catalog_proc_param)) { if (debug.val) LOG.debug("Skipping parameter mapping in " + catalog_stmt + " because it is already set"); } else { catalog_stmt_param.setProcparameter(catalog_proc_param); if (debug.val) LOG.debug("Added parameter mapping in Statement '" + stmt_name + "' from StmtParameter '" + catalog_stmt_param.getName() + "' to '" + catalog_proc_param.getName() + "'"); } } // FOR } // FOR } // FOR return; } /** * Return the corresponding StmtParameter value from the ProcParameter ParameterSet * using the information provided in the given ParameterMapping. * @param params * @param pm * @return */ public static Object getValue(ParameterSet params, ParameterMapping pm) { Object val = null; Object orig = params.toArray()[pm.procedure_parameter.getIndex()]; VoltType vtype = VoltType.get(pm.procedure_parameter.getType()); if (pm.procedure_parameter.getIsarray()) { assert(pm.procedure_parameter_index != ParametersUtil.NULL_PROC_PARAMETER_OFFSET); switch (vtype) { case TINYINT: { if (orig instanceof byte[]) val = ((byte[])orig)[pm.procedure_parameter_index]; else val = ((Byte[])orig)[pm.procedure_parameter_index]; break; } case SMALLINT: { if (orig instanceof short[]) val = ((short[])orig)[pm.procedure_parameter_index]; else val = ((Short[])orig)[pm.procedure_parameter_index]; break; } case INTEGER: { if (orig instanceof int[]) val = ((int[])orig)[pm.procedure_parameter_index]; else val = ((Integer[])orig)[pm.procedure_parameter_index]; break; } case BIGINT: { if (orig instanceof long[]) val = ((long[])orig)[pm.procedure_parameter_index]; else val = ((Long[])orig)[pm.procedure_parameter_index]; break; } case FLOAT: { if (orig instanceof float[]) val = ((float[])orig)[pm.procedure_parameter_index]; else if (orig instanceof double[]) val = ((double[])orig)[pm.procedure_parameter_index]; else if (orig instanceof Float[]) val = ((Float[])orig)[pm.procedure_parameter_index]; else val = ((Double[])orig)[pm.procedure_parameter_index]; break; } case TIMESTAMP: { val = ((TimestampType[])orig)[pm.procedure_parameter_index]; break; } default: val = ((Object[])orig)[pm.procedure_parameter_index]; } // SWITCH } else { val = params.toArray()[pm.procedure_parameter.getIndex()]; } return (val); } }