/***************************************************************************
* Copyright (C) 2009 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
package edu.brown.workload;
import java.text.ParseException;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONString;
import org.json.JSONStringer;
import org.voltdb.VoltTable;
import org.voltdb.VoltTableRow;
import org.voltdb.VoltType;
import org.voltdb.catalog.CatalogMap;
import org.voltdb.catalog.CatalogType;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Statement;
import org.voltdb.utils.VoltTypeUtil;
import edu.brown.catalog.CatalogUtil;
/**
*
* @author Andy Pavlo <pavlo@cs.brown.edu>
*
*/
public abstract class AbstractTraceElement<T extends CatalogType> implements JSONString, Cloneable {
/** java.util.logging logger. */
protected static final Logger LOG = Logger.getLogger(AbstractTraceElement.class);
public enum Members {
// Catalog Name
NAME,
// Start Time (relative to start of trace)
START,
// Stop Time (relative to start of trace)
STOP,
// Element Parameters
PARAMS,
// Aborted (true/false)
ABORTED,
// Output (optional)
OUTPUT,
// Weight (optional)
WEIGHT,
};
protected Long start_timestamp;
protected Long stop_timestamp;
protected Object params[];
protected String catalog_item_name;
protected boolean aborted = false;
protected Object output[][][];
protected VoltType output_types[][];
protected short weight = 1;
public AbstractTraceElement() {
// Nothing to do...
}
public AbstractTraceElement(String catalog_item_name, Object params[]) {
this.params = params;
this.catalog_item_name = catalog_item_name;
this.start_timestamp = System.nanoTime();
//this.stop_timestamp = -1l;
}
public AbstractTraceElement(T catalog_item, Object params[]) {
this(catalog_item.getName(), params);
}
@Override
public AbstractTraceElement<T> clone() {
AbstractTraceElement<T> clone = this.cloneImpl();
clone.start_timestamp = this.start_timestamp;
clone.stop_timestamp = this.stop_timestamp;
clone.aborted = this.aborted;
clone.output = this.output;
clone.output_types = this.output_types;
clone.weight = this.weight;
return (clone);
}
protected abstract <X> X cloneImpl();
public void setWeight(int weight) {
this.weight = (short)weight;
}
public void incrementWeight(int delta) {
this.weight += delta;
}
public int getWeight() {
return (this.weight);
}
@Override
public String toString() {
return (this.getClass().getSimpleName() + "[" + this.catalog_item_name + "]");
}
public void stop() {
this.stop_timestamp = System.nanoTime();
}
public void abort() {
this.stop();
this.aborted = true;
}
public boolean isStopped() {
return (this.stop_timestamp != null);
}
public boolean isAborted() {
return (this.aborted);
}
public void setTimestamps(Long start, Long stop) {
this.start_timestamp = start;
this.stop_timestamp = stop;
}
/**
* @return the start_timestamp
*/
public Long getStartTimestamp() {
return this.start_timestamp;
}
/**
* @return the stop_timestamp
*/
public Long getStopTimestamp() {
return this.stop_timestamp;
}
public String getCatalogItemName() {
return this.catalog_item_name;
}
public abstract T getCatalogItem(Database catalog_db);
public Boolean getAborted() {
return aborted;
}
/**
* Get the number of parameters that this trace element has
* @return
*/
public int getParamCount() {
return (this.params.length);
}
/**
* Get the parameters object array for this trace element
* @return
*/
public Object[] getParams() {
return this.params;
}
/**
* @return the params
*/
@SuppressWarnings("unchecked")
public <U> U getParam(int i) {
return (U)this.params[i];
}
public void setParam(int i, Object value) {
this.params[i] = value;
}
public boolean hasOutput() {
return (this.output != null);
}
public void setOutput(Object[][]...output) {
if (output == null || output.length == 0) return;
this.output = output;
// Build Output Types
this.output_types = new VoltType[this.output.length][];
for (int i = 0; i < this.output.length; i++) {
Object data[][] = this.output[i];
if (data == null) continue;
Integer missing = null;
for (int j = 0; j < data.length; j++) {
Object row[] = data[j];
if (row == null) continue;
if (missing == null) {
missing = row.length;
this.output_types[i] = new VoltType[row.length];
}
assert(missing != null);
for (int k = 0; k < row.length; k++) {
if (this.output_types[i][k] != null) continue;
Object val = row[k];
if (val == null) continue;
this.output_types[i][k] = VoltType.typeFromClass(val.getClass());
missing--;
} // FOR (columns)
if (missing == 0) break;
} // FOR (rows)
} // FOR
}
public void setOutput(VoltTable...output) {
if (output == null || output.length == 0) return;
this.output = new Object[output.length][][];
this.output_types = new VoltType[output.length][];
for (int i = 0; i < this.output.length; i++) {
VoltTable vt = output[i];
if (vt == null) continue;
// TYPES
this.output_types[i] = new VoltType[vt.getColumnCount()];
for (int k = 0; k < this.output_types[i].length; k++) {
this.output_types[i][k] = vt.getColumnType(k);
} // FOR
// DATA
this.output[i] = new Object[vt.getRowCount()][vt.getColumnCount()];
int j = 0;
while (vt.advanceRow()) {
VoltTableRow row = vt.getRow();
for (int k = 0; k < this.output[i][j].length; k++) {
this.output[i][j][k] = row.get(k);
} // FOR (columns)
j++;
} // WHILE (rows)
} // FOR (tables)
}
public Object[][][] getOutput() {
return (this.output);
}
public Object[][] getOutput(int idx) {
return (this.output[idx]);
}
public VoltType[] getOutputTypes(int idx) {
return (this.output_types[idx]);
}
/**
*
* @param catalog_db
* @return
*/
public abstract String debug(Database catalog_db);
@Override
public String toJSONString() {
assert(false);
// Can't be implemented since we always need to have a Database catalog object
return null;
}
public String toJSONString(Database catalog_db) {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
this.toJSONString(stringer, catalog_db);
stringer.endObject();
} catch (JSONException e) {
e.printStackTrace();
System.exit(-1);
}
return stringer.toString();
}
public void toJSONString(JSONStringer stringer, Database catalog_db) throws JSONException {
stringer.key(Members.NAME.name()).value(this.catalog_item_name);
stringer.key(Members.START.name()).value(this.start_timestamp);
stringer.key(Members.STOP.name()).value(this.stop_timestamp);
stringer.key(Members.ABORTED.name()).value(this.aborted);
// WEIGHT
if (this.weight > 1) {
stringer.key(Members.WEIGHT.name()).value(this.weight);
}
// Output Tables
stringer.key(Members.OUTPUT.name()).array();
if (this.output != null) {
for (int i = 0; i < this.output.length; i++) {
stringer.object();
// COLUMN TYPES
stringer.key("TYPES").array();
VoltType types[] = this.output_types[i];
if (types != null) {
for (int j = 0; j < types.length; j++) {
stringer.value((types[j] == null ? VoltType.NULL : types[j]).name());
} // FOR
}
stringer.endArray();
// DATA
stringer.key("DATA").array();
Object data[][] = this.output[i];
if (data != null) {
for (int j = 0; j < data.length; j++) {
Object row[] = data[j];
stringer.array();
for (int k = 0; k < row.length; k++) {
stringer.value(data[j][k]);
} // FOR
stringer.endArray();
} // FOR
}
stringer.endArray();
stringer.endObject();
} // FOR
}
stringer.endArray();
// This doesn't work because CatalogType doesn't have have we need
//assert(this.catalog_item.getField("parameters") != null);
//CatalogMap<CatalogType> param_map = (CatalogMap<CatalogType>)this.catalog_item.getField("parameters");
CatalogMap<? extends CatalogType> param_map = null;
T catalog_item = this.getCatalogItem(catalog_db);
if (catalog_item instanceof Procedure) {
param_map = (CatalogMap<? extends CatalogType>)((Procedure)catalog_item).getParameters();
} else {
param_map = (CatalogMap<? extends CatalogType>)((Statement)catalog_item).getParameters();
}
stringer.key(Members.PARAMS.name()).array();
for (int i = 0; i < this.params.length; i++) {
CatalogType catalog_param = param_map.get(i);
Object param_isarray = catalog_param.getField("isarray");
if (param_isarray != null && (Boolean)param_isarray) {
stringer.array();
Class<?> type = params[i].getClass();
assert(type.isArray());
Class<?> dataType = type.getComponentType();
// I couldn't think of a better way to do this dynamically..
// It's not trivial to go from primitive arrays to object arrays
if (dataType == Long.TYPE) {
for (Object value : (long[])this.params[i]) {
stringer.value(value);
} // FOR
} else if (dataType == Integer.TYPE) {
for (Object value : (int[])this.params[i]) {
stringer.value(value);
} // FOR
} else if (dataType == Short.TYPE) {
for (Object value : (short[])this.params[i]) {
stringer.value(value);
} // FOR
} else if (dataType == Byte.TYPE) {
for (Object value : (byte[])this.params[i]) {
stringer.value(value);
} // FOR
} else {
for (Object value : (Object[])this.params[i]) {
stringer.value(value);
} // FOR
}
stringer.endArray();
} else {
stringer.value(this.params[i]);
}
} // FOR
stringer.endArray();
}
protected <U extends CatalogType> void paramsFromJSONObject(JSONObject object, CatalogMap<U> catalog_params, String param_field) throws Exception {
assert(catalog_params != null);
final Thread self = Thread.currentThread();
JSONArray jsonParams = object.getJSONArray(Members.PARAMS.name());
int num_params = catalog_params.size();
this.params = new Object[num_params];
for (int i = 0; i < num_params; i++) {
// If we ran out of JSON parameters, then just throw in null
if (i > jsonParams.length()) {
this.params[i] = null;
continue;
}
U catalog_param = catalog_params.get(i);
VoltType param_type = VoltType.get(((Integer)catalog_param.getField(param_field)).byteValue());
// if (param_type == VoltType.TINYINT) param_type = VoltType.INTEGER;
if (param_type == null) {
throw new Exception("Parameter type is null for " + catalog_param);
}
// We don't know whether we have a StmtParameter or a ProcParameter
Object _isarray = catalog_param.getField("isarray");
boolean param_isarray = (_isarray != null && (Boolean)_isarray);
// HACK: If parameter says its an array, but JSON disagrees, then just
// treat it is as a String. The Volt guys started using byte arrays instead of
// strings but didn't add a byte VoltType
JSONArray jsonInner = null;
if (param_isarray) {
if (jsonParams.isNull(i)) {
this.params[i] = new Object[0];
continue;
} else {
try {
jsonInner = jsonParams.getJSONArray(i);
} catch (JSONException ex) {
if (param_type == VoltType.TINYINT) {
param_isarray = false;
param_type = VoltType.STRING;
} else {
LOG.error("Failed to deserialize " + CatalogUtil.getDisplayName(catalog_param) + " [type=" + param_type + ", isarray=" + param_isarray + "]", ex);
throw ex;
}
}
}
}
if (param_isarray) {
Object inner[] = new Object[jsonInner.length()];
for (int j = 0; j < jsonInner.length(); j++) {
inner[j] = VoltTypeUtil.getObjectFromString(param_type, jsonInner.getString(j), self);
if (inner[j] == null) {
throw new RuntimeException("Array parameter " + j + " for " + catalog_param + " is null");
}
} // FOR
this.params[i] = VoltTypeUtil.getPrimitiveArray(param_type, inner);
} else if (jsonParams.isNull(i)) {
this.params[i] = null;
} else {
//System.err.println("[" + i + "] " + jsonParams.getString(i) + " (" + param_type + ")");
try {
this.params[i] = VoltTypeUtil.getObjectFromString(param_type, jsonParams.getString(i), self);
if (this.params[i] == null) {
throw new Exception(catalog_param + " is null [" + param_type + "]");
}
} catch (Exception ex) {
LOG.fatal("Failed to convert param '" + jsonParams.getString(i) + "' to " + param_type + " for " + catalog_param + " in " + CatalogUtil.getDisplayName(catalog_param.getParent()), ex);
throw ex;
}
}
} // FOR
}
protected void fromJSONObject(JSONObject object, Database db) throws JSONException {
this.start_timestamp = object.getLong(Members.START.name());
if (!object.isNull(Members.STOP.name())) {
this.stop_timestamp = object.getLong(Members.STOP.name());
}
this.catalog_item_name = object.getString(Members.NAME.name());
this.aborted = object.getBoolean(Members.ABORTED.name());
// WEIGHT
if (object.has(Members.WEIGHT.name())) {
this.weight = (short)object.getInt(Members.WEIGHT.name());
}
// OUTPUT
JSONArray output_arr = null;
try {
output_arr = object.getJSONArray(Members.OUTPUT.name());
} catch (JSONException ex) {
// IGNORE
}
if (output_arr != null) {
for (int i = 0, cnt = output_arr.length(); i < cnt; i++) {
if (i == 0) {
this.output = new Object[cnt][][];
this.output_types = new VoltType[cnt][];
}
JSONObject inner = output_arr.getJSONObject(i);
assert(inner != null);
// TYPES
JSONArray types_arr = inner.getJSONArray("TYPES");
this.output_types[i] = new VoltType[types_arr.length()];
for (int j = 0; j < this.output_types[i].length; j++) {
this.output_types[i][j] = VoltType.typeFromString(types_arr.getString(j));
} // FOR
// DATA
JSONArray data_arr = inner.getJSONArray("DATA");
this.output[i] = new Object[data_arr.length()][this.output_types[i].length];
for (int j = 0; j < this.output[i].length; j++) {
JSONArray row_arr = data_arr.getJSONArray(j);
if (row_arr == null) continue;
for (int k = 0; k < this.output[i][j].length; k++) {
String val_str = row_arr.getString(k);
try {
this.output[i][j][k] = VoltTypeUtil.getObjectFromString(this.output_types[i][k], val_str);
} catch (ParseException ex) {
throw new RuntimeException(String.format("Failed to deserialize output %s [%d][%d][%d]", i, j, k));
}
} // FOR (columns)
} // FOR (rows)
} // FOR ( tables)
}
}
}