package edu.brown.expressions; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.json.JSONObject; import org.voltdb.catalog.Column; import org.voltdb.catalog.Database; import org.voltdb.catalog.Table; import org.voltdb.expressions.AbstractExpression; import org.voltdb.expressions.AggregateExpression; import org.voltdb.expressions.ComparisonExpression; import org.voltdb.expressions.ConjunctionExpression; import org.voltdb.expressions.ConstantValueExpression; import org.voltdb.expressions.InComparisonExpression; import org.voltdb.expressions.NullValueExpression; import org.voltdb.expressions.OperatorExpression; import org.voltdb.expressions.ParameterValueExpression; import org.voltdb.expressions.TupleAddressExpression; import org.voltdb.expressions.TupleValueExpression; import org.voltdb.planner.PlanAssembler; import org.voltdb.types.ExpressionType; import org.voltdb.utils.Encoder; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; /** * @author pavlo */ public abstract class ExpressionUtil extends org.voltdb.expressions.ExpressionUtil { private static final Logger LOG = Logger.getLogger(ExpressionUtil.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } public static final Map<ExpressionType, String> EXPRESSION_STRING = new HashMap<ExpressionType, String>(); static { EXPRESSION_STRING.put(ExpressionType.COMPARE_EQUAL, "="); EXPRESSION_STRING.put(ExpressionType.COMPARE_NOTEQUAL, "!="); EXPRESSION_STRING.put(ExpressionType.COMPARE_LESSTHAN, "<"); EXPRESSION_STRING.put(ExpressionType.COMPARE_LESSTHANOREQUALTO, "<="); EXPRESSION_STRING.put(ExpressionType.COMPARE_GREATERTHAN, ">"); EXPRESSION_STRING.put(ExpressionType.COMPARE_GREATERTHANOREQUALTO, ">"); EXPRESSION_STRING.put(ExpressionType.COMPARE_LIKE, "LIKE"); EXPRESSION_STRING.put(ExpressionType.COMPARE_IN, "IN"); } /** * Recursively check whether the trees rooted at the given * AbstractExpressions are equal * * @param exp0 * @param exp1 * @return */ public static boolean equals(AbstractExpression exp0, AbstractExpression exp1) { if (exp0 == null) { return (exp1 == null); } else if (exp1 == null) { return (false); } return (exp0.equals(exp1) ? ExpressionUtil.equals(exp0.getLeft(), exp1.getLeft()) && ExpressionUtil.equals(exp0.getRight(), exp1.getRight()) : false); } /** * @param catalog_db * @param exptree * @return * @throws Exception */ public static AbstractExpression deserializeExpression(Database catalog_db, String exptree) throws Exception { AbstractExpression exp = null; if (exptree != null && !exptree.isEmpty()) { JSONObject json_obj = new JSONObject(Encoder.hexDecodeToString(exptree)); exp = AbstractExpression.fromJSONObject(json_obj, catalog_db); } return (exp); } /** * Returns all the Column catalog handles that are access/modified in the * Expression tree * * @param catalog_db * @param exp */ public static Collection<Column> getReferencedColumns(final Database catalog_db, AbstractExpression exp) { final Set<Column> found_columns = new HashSet<Column>(); new ExpressionTreeWalker() { @Override protected void callback(AbstractExpression element) { if (element instanceof TupleValueExpression) { String table_name = ((TupleValueExpression) element).getTableName(); Table catalog_tbl = catalog_db.getTables().get(table_name); if (catalog_tbl == null) { // If it's a temp then we just ignore it. Otherwise // throw an error! if (table_name.contains(PlanAssembler.AGGREGATE_TEMP_TABLE) == false) { this.stop(); throw new RuntimeException(String.format("Unknown table '%s' referenced in Expression node %s", table_name, element)); } else if (debug.val) { LOG.debug("Ignoring temporary table '" + table_name + "'"); } return; } String column_name = ((TupleValueExpression) element).getColumnName(); Column catalog_col = catalog_tbl.getColumns().get(column_name); if (catalog_col == null) { this.stop(); throw new RuntimeException(String.format("Unknown column '%s.%s' referenced in Expression node %s", table_name, column_name, element)); } found_columns.add(catalog_col); } return; } }.traverse(exp); return (found_columns); } /** * Returns all the Table catalog handles that are access/modified in the * Expression tree * * @param catalog_db * @param exp */ public static Collection<Table> getReferencedTables(final Database catalog_db, AbstractExpression exp) { Set<Table> found_tables = new HashSet<Table>(); for (Column catalog_col : ExpressionUtil.getReferencedColumns(catalog_db, exp)) { Table catalog_tbl = catalog_col.getParent(); found_tables.add(catalog_tbl); } // FOR return (found_tables); } /** * Get the set of table names referenced in this expression tree. This can * be used without a Database catalog handle * * @param exp * @return */ public static Collection<String> getReferencedTableNames(AbstractExpression exp) { final Set<String> tableNames = new HashSet<String>(); new ExpressionTreeWalker() { @Override protected void callback(AbstractExpression element) { if (element instanceof TupleValueExpression) { tableNames.add(((TupleValueExpression) element).getTableName()); } return; } }.traverse(exp); return (tableNames); } /** * Get all of the ExpressionTypes used in the tree below the given root * * @param root * @return */ public static Collection<ExpressionType> getExpressionTypes(AbstractExpression root) { final Set<ExpressionType> found = new HashSet<ExpressionType>(); new ExpressionTreeWalker() { @Override protected void callback(AbstractExpression element) { found.add(element.getExpressionType()); } }.traverse(root); return (found); } /** * @param <T> * @param root * @param search_class * @return */ public static <T extends AbstractExpression> Collection<T> getExpressions(AbstractExpression root, final Class<? extends T> search_class) { final Set<T> found = new HashSet<T>(); new ExpressionTreeWalker() { @SuppressWarnings("unchecked") @Override protected void callback(AbstractExpression element) { if (element.getClass().equals(search_class)) { found.add((T) element); } return; } }.traverse(root); return (found); } public static String debug(AbstractExpression exp) { return (ExpressionUtil.debug(exp, "")); } public static String debug(AbstractExpression exp, String spacer) { assert (exp != null); final String orig_spacer = spacer; String name = exp.getClass().getSimpleName(); ExpressionType etype = exp.getExpressionType(); final StringBuilder sb = new StringBuilder(); spacer += " "; sb.append(spacer).append("ValueType[").append(exp.getValueType()).append("]\n"); if (exp instanceof AggregateExpression) { // Nothing } else if (exp instanceof ComparisonExpression) { name += "[" + etype.name().replace("COMPARE_", "") + "]"; } else if (exp instanceof ConjunctionExpression) { name += "[" + etype.name().replace("CONJUNCTION_", "") + "]"; } else if (exp instanceof ConstantValueExpression) { sb.append(spacer).append("Value[").append(((ConstantValueExpression) exp).getValue()).append("]\n"); } else if (exp instanceof InComparisonExpression) { InComparisonExpression in_exp = (InComparisonExpression) exp; sb.append(spacer).append("Values[").append(in_exp.getValues().size()).append("]:\n"); for (int ctr = 0, cnt = in_exp.getValues().size(); ctr < cnt; ctr++) { sb.append(ExpressionUtil.debug(in_exp.getValues().get(ctr), spacer)); } // FOR } else if (exp instanceof NullValueExpression) { // Nothing } else if (exp instanceof OperatorExpression) { name += "[" + etype.name().replace("OPERATOR_", "") + "]"; } else if (exp instanceof ParameterValueExpression) { sb.append(spacer).append("Parameter[").append(((ParameterValueExpression) exp).getParameterId()).append("]\n"); } else if (exp instanceof TupleAddressExpression) { // Nothing } else if (exp instanceof TupleValueExpression) { sb.append(spacer).append("Column Reference: ").append("[").append(((TupleValueExpression) exp).getColumnIndex()).append("] ").append(((TupleValueExpression) exp).getTableName()) .append(".").append(((TupleValueExpression) exp).getColumnName()).append(" AS ").append(((TupleValueExpression) exp).getColumnAlias()).append("\n"); } // Print out all of our children if (exp.getLeft() != null || exp.getRight() != null) { sb.append(spacer).append("left: ").append(exp.getLeft() != null ? "\n" + ExpressionUtil.debug(exp.getLeft(), spacer) : null + "\n"); sb.append(spacer).append("right: ").append(exp.getRight() != null ? "\n" + ExpressionUtil.debug(exp.getRight(), spacer) : null + "\n"); } return (orig_spacer + "+ " + name + "\n" + sb.toString()); } }