package edu.brown.hstore; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.voltdb.VoltTable; import org.voltdb.VoltType; import org.voltdb.catalog.Catalog; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Database; import org.voltdb.catalog.Host; import org.voltdb.catalog.ProcParameter; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Site; import org.voltdb.client.Client; import org.voltdb.client.ClientFactory; import org.voltdb.client.ClientResponse; import org.voltdb.utils.VoltTypeUtil; import edu.brown.catalog.CatalogUtil; import edu.brown.utils.ArgumentsParser; import edu.brown.utils.CollectionUtil; import edu.brown.utils.StringUtil; /** * Invoke a stored procedure programmatically * @author pavlo * @author evanj */ public class VoltProcedureInvoker { private static final Logger LOG = Logger.getLogger(VoltProcedureInvoker.class); /** * Invoke a stored procedure using the given client handle * The string parameters will be automatically converted to proper type * Throws an exception if there was an error * @param catalog * @param client * @param procName * @param params * @return * @throws */ public static ClientResponse invoke(Catalog catalog, Client client, String procName, String...params) throws Exception { Database catalog_db = CatalogUtil.getDatabase(catalog); // Get Procedure catalog handle if (procName == null || procName.isEmpty()) { LOG.error("Missing procedure name"); return (null); } Procedure catalog_proc = catalog_db.getProcedures().getIgnoreCase(procName); if (catalog_proc == null) { throw new Exception("Invalid procedure name '" + procName + "'"); } int expectedParams = catalog_proc.getParameters().size(); // Hack for @AdHoc if (catalog_proc.getName().equalsIgnoreCase("@AdHoc")) { expectedParams = 1; } // Procedure Parameters if (params.length != expectedParams) { Map<String, Object> m = new LinkedHashMap<String, Object>(); for (ProcParameter catalog_param : CatalogUtil.getSortedCatalogItems(catalog_proc.getParameters(), "index")) { VoltType vtype = VoltType.get(catalog_param.getType()); String key = String.format("[%02d]", catalog_param.getIndex()); String val = vtype.name(); if (vtype == VoltType.TIMESTAMP) { val += " (FORMATS: "; String add = ""; int spacer = val.length(); for (String f : VoltTypeUtil.DATE_FORMAT_PATTERNS) { val += add + f; add = "\n" + StringUtil.repeat(" ", spacer); } val += ")"; } m.put(key, val); } throw new Exception(String.format("Incorrect number of parameters for %s. Expected %d, but was only given %d\n" + "Expected Input Parameters:\n%s", catalog_proc, catalog_proc.getParameters().size(), params.length, StringUtil.prefix(StringUtil.formatMaps(m), " ")).trim()); } // Convert parameters to the proper type Object parameters[] = new Object[expectedParams]; for (int i = 0; i < expectedParams; i++) { ProcParameter catalog_param = catalog_proc.getParameters().get(i); assert(catalog_param != null) : String.format("Null %s parameter at %d", catalog_proc.getName(), i); VoltType vt = VoltType.get(catalog_param.getType()); try { // Split into array if (catalog_param.getIsarray()) { List<String> arr = (List<String>)CollectionUtil.addAll(new ArrayList<String>(), params[i].split(",")); Object inner[] = new Object[arr.size()]; for (int ii = 0, cnt = arr.size(); ii < cnt; ii++) { inner[ii] = VoltTypeUtil.getObjectFromString(vt, arr.get(ii)); } // FOR parameters[i] = inner; // Scalar Paramter } else { parameters[i] = VoltTypeUtil.getObjectFromString(vt, params[i]); } } catch (ParseException ex) { LOG.error("Invalid parameter #" + i + ": " + params[i], ex); return (null); } if (LOG.isDebugEnabled()) LOG.debug(String.format("%s: %s [%s / %s]", catalog_param.fullName(), (catalog_param.getIsarray() ? Arrays.toString((Object[])parameters[i]) : parameters[i]), vt, parameters[i].getClass())); } // FOR LOG.info(String.format("Invoking %s [params=%s]", catalog_proc.getName(), Arrays.toString(parameters))); ClientResponse cresponse = client.callProcedure(catalog_proc.getName(), parameters); return (cresponse); } public static void main(String[] vargs) throws Exception { ArgumentsParser args = ArgumentsParser.load(vargs); args.require(ArgumentsParser.PARAM_CATALOG); String procName = args.getOptParam(0); String parameters[] = new String[args.getOptParamCount()-1]; for (int i = 0; i < parameters.length; i++) { parameters[i] = args.getOptParam(i+1); } // FOR Client client = ClientFactory.createClient(128, null, false, null); Cluster catalog_clus = args.catalog_db.getParent(); Site catalog_site = CollectionUtil.first(catalog_clus.getSites()); assert(catalog_site != null); Host catalog_host = catalog_site.getHost(); assert(catalog_host != null); Integer port = CollectionUtil.random(CatalogUtil.getExecutionSitePorts(catalog_site)); assert(port != null); client.createConnection(null, catalog_host.getIpaddr(), port, "user", "password"); LOG.info(String.format("Connected to H-Store cluster at %s:%d", catalog_host.getIpaddr(), port)); ClientResponse cresponse = VoltProcedureInvoker.invoke(args.catalog, client, procName, parameters); if (cresponse == null) System.exit(-1); // m.put("Status", cresponse.getStatus()); // m.put("Length", String.format("%d [bytes=%d]", cresponse.getResults().length, cresponse.getResultsSize())); // m.put("Results", StringUtil.join("\n", )); Map<String, Object> m = new LinkedHashMap<String, Object>(); for (int i = 0; i < cresponse.getResults().length; i++) { VoltTable vt = cresponse.getResults()[i]; m.put(String.format(" [%02d]", i), vt); } // FOR LOG.info(StringUtil.repeat("-", 50)); LOG.info(String.format("%s Txn #%d - Status %s\n%s", procName, cresponse.getTransactionId(), cresponse.getStatus(), cresponse.toString())); } }