/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.types;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
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.FunctionExpression;
import org.voltdb.expressions.HashRangeExpression;
import org.voltdb.expressions.InComparisonExpression;
import org.voltdb.expressions.OperatorExpression;
import org.voltdb.expressions.ParameterValueExpression;
import org.voltdb.expressions.RowSubqueryExpression;
import org.voltdb.expressions.ScalarValueExpression;
import org.voltdb.expressions.SelectSubqueryExpression;
import org.voltdb.expressions.TupleAddressExpression;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.expressions.VectorValueExpression;
import org.voltdb.expressions.WindowFunctionExpression;
/**
*
*/
public enum ExpressionType {
INVALID (null, 0, "<invalid>"),
// ----------------------------
// Arthimetic Operators
// ----------------------------
OPERATOR_PLUS (OperatorExpression.class, 1, "+"),
// left + right (both must be number. implicitly casted)
OPERATOR_MINUS (OperatorExpression.class, 2, "-"),
// left - right (both must be number. implicitly casted)
OPERATOR_MULTIPLY (OperatorExpression.class, 3, "*"),
// left * right (both must be number. implicitly casted)
OPERATOR_DIVIDE (OperatorExpression.class, 4, "/"),
// left / right (both must be number. implicitly casted)
OPERATOR_CONCAT (OperatorExpression.class, 5, "||"),
// left || right (both must be char/varchar)
OPERATOR_MOD (OperatorExpression.class, 6, "%"),
// left % right (both must be integer)
OPERATOR_CAST (OperatorExpression.class, 7, "<cast>"),
// explicitly cast left as right (right is integer in ValueType enum)
OPERATOR_NOT (OperatorExpression.class, 8, "NOT", true),
// logical not
OPERATOR_IS_NULL (OperatorExpression.class, 9, "IS NULL", true),
// unary null evaluation
OPERATOR_EXISTS (OperatorExpression.class, 18, "EXISTS", true),
// unary exists evaluation
// ----------------------------
// Binary Comparison
// ----------------------------
COMPARE_EQUAL (ComparisonExpression.class, 10, "=", true),
// equal operator between left and right
COMPARE_NOTEQUAL (ComparisonExpression.class, 11, "<>", true),
// inequal operator between left and right
COMPARE_LESSTHAN (ComparisonExpression.class, 12, "<", true),
// less than operator between left and right
COMPARE_GREATERTHAN (ComparisonExpression.class, 13, ">", true),
// greater than operator between left and right
COMPARE_LESSTHANOREQUALTO (ComparisonExpression.class, 14, "<=", true),
// less than equal operator between left and right
COMPARE_GREATERTHANOREQUALTO (ComparisonExpression.class, 15, ">=", true),
// greater than equal operator between left and right
COMPARE_LIKE (ComparisonExpression.class, 16, "LIKE", true),
// LIKE operator (left LIKE right). both children must be string.
COMPARE_IN (InComparisonExpression.class, 17, "IN", true),
// IN operator. left IN right. right must be VectorValue
// value 18 is assigned to OPERATOR_EXISTS
COMPARE_NOTDISTINCT (ComparisonExpression.class, 19, "IS NOT DISTINCT FROM", true),
// Not distinct operator between left and right
// ----------------------------
// Conjunction Operator
// ----------------------------
CONJUNCTION_AND (ConjunctionExpression.class, 20, "AND", true),
CONJUNCTION_OR (ConjunctionExpression.class, 21, "OR", true),
// ----------------------------
// Values
// ----------------------------
VALUE_CONSTANT (ConstantValueExpression.class, 30, "<constant>", true),
VALUE_PARAMETER (ParameterValueExpression.class, 31, "<parameter>", true),
VALUE_TUPLE (TupleValueExpression.class, 32, "<column>", true),
VALUE_TUPLE_ADDRESS (TupleAddressExpression.class, 33, "<address>", true),
VALUE_VECTOR (VectorValueExpression.class, 35, "<vector>", true),
VALUE_SCALAR (ScalarValueExpression.class, 36, "<scalar>", true),
// ----------------------------
// Aggregate
// ----------------------------
AGGREGATE_COUNT (AggregateExpression.class, 40, "COUNT", true),
AGGREGATE_COUNT_STAR (AggregateExpression.class, 41, "COUNT(*)", true),
AGGREGATE_SUM (AggregateExpression.class, 42, "SUM"),
AGGREGATE_MIN (AggregateExpression.class, 43, "MIN", true),
AGGREGATE_MAX (AggregateExpression.class, 44, "MAX", true),
AGGREGATE_AVG (AggregateExpression.class, 45, "AVG"),
AGGREGATE_APPROX_COUNT_DISTINCT(AggregateExpression.class, 46, "APPROX_COUNT_DISTINCT", true),
AGGREGATE_VALS_TO_HYPERLOGLOG (AggregateExpression.class, 47, "VALS_TO_HYPERLOGLOG"),
AGGREGATE_HYPERLOGLOGS_TO_CARD(AggregateExpression.class, 48, "HYPERLOGLOGS_TO_CARD"),
// ----------------------------
// Windowed Aggregates. We need to treat these
// somewhat differently than the non-windowed
// aggregates. For example, AGGREGATE_MAX is a
// different kind of thing from AGGREGATE_WINDOWED_MAX.
// For one thing, windowed aggregates have class WindowFunctionExpression.class,
// and non-windowed aggregates have class AggregateExpression.class.
//
// We only support RANK and DENSE_RANK now. But when we support different
// aggregate functions we will want to keep them as
// separate ExpressionType enumerals.
// ----------------------------
AGGREGATE_WINDOWED_RANK (WindowFunctionExpression.class, 70, "RANK"),
AGGREGATE_WINDOWED_DENSE_RANK (WindowFunctionExpression.class, 71, "DENSE_RANK"),
AGGREGATE_WINDOWED_COUNT (WindowFunctionExpression.class, 72, "COUNT"),
AGGREGATE_WINDOWED_MAX (WindowFunctionExpression.class, 73, "MAX"),
AGGREGATE_WINDOWED_MIN (WindowFunctionExpression.class, 74, "MIN"),
AGGREGATE_WINDOWED_SUM (WindowFunctionExpression.class, 75, "SUM"),
// No support for PERCENT_RANK yet.
// AGGREGATE_WINDOWED_PERCENT_RANK(WindowFunctionExpression.class, 73, "PERCENT_RANK"),
// No support for CUME_DIST yet.
// AGGREGATE_WINDOWED_CUME_DIST (WindowFunctionExpression.class, 74, "CUME_DIST"),
// ----------------------------
// Function
// ----------------------------
//TODO: Should there be multiple classes for function expressions
// maybe based on their support for optimization methods?
//TODO: Should there be multiple FunctionExpression ExpressionTypes?
FUNCTION (FunctionExpression.class, 100, "<function>"),
// -----------------------------
// Internals added for Elastic
// -----------------------------
HASH_RANGE (HashRangeExpression.class, 200, "#"),
// -----------------------------
// Internals added for CASE WHEN expression.
// -----------------------------
OPERATOR_CASE_WHEN (OperatorExpression.class, 300, "CASEWHEN"),
OPERATOR_ALTERNATIVE (OperatorExpression.class, 301, "ALTERNATIVE"),
// -----------------------------
// Subquery
// -----------------------------
ROW_SUBQUERY (RowSubqueryExpression.class, 400, "<row subquery>"),
SELECT_SUBQUERY (SelectSubqueryExpression.class, 401, "<select subquery>")
;
private final int m_value;
private final String m_symbol;
private final Class<? extends AbstractExpression> m_expressionClass;
// Does this expression type have the risk to fail a DDL.
private boolean m_isSafeForDDL;
ExpressionType(Class<? extends AbstractExpression> expressionClass,
int val, String symbol) {
this(expressionClass, val, symbol, false);
}
ExpressionType(Class<? extends AbstractExpression> expressionClass,
int val, String symbol, boolean isSafeForDDL) {
m_value = val;
m_symbol = symbol;
m_expressionClass = expressionClass;
m_isSafeForDDL = isSafeForDDL;
}
public Class<? extends AbstractExpression> getExpressionClass() {
return m_expressionClass;
}
private static final Map<Integer, ExpressionType> idx_lookup =
new HashMap<>();
private static final Map<String, ExpressionType> name_lookup =
new HashMap<>();
static {
for (ExpressionType vt : EnumSet.allOf(ExpressionType.class)) {
ExpressionType.idx_lookup.put(vt.m_value, vt);
String name = vt.name().toLowerCase();
ExpressionType.name_lookup.put(name, vt);
//
// Also store the name of the operation without the prefix
// This makes it easier to parse plans
//
String shortName = name.substring(name.indexOf("_") + 1);
ExpressionType.name_lookup.put(shortName, vt);
}
//
// Alternative Operation Names
//
ExpressionType.name_lookup.put("add", ExpressionType.OPERATOR_PLUS);
ExpressionType.name_lookup.put("sub", ExpressionType.OPERATOR_MINUS);
ExpressionType.name_lookup.put("subtract", ExpressionType.OPERATOR_MINUS);
}
public int getValue() {
return m_value;
}
public static ExpressionType get(Integer idx) {
ExpressionType ret = ExpressionType.idx_lookup.get(idx);
return (ret == null ? ExpressionType.INVALID : ret);
}
public static ExpressionType get(String name) {
// interned strings can't be garbage collected, so it's a potential for a memory leak.
// See link: http://stackoverflow.com/questions/2431540/garbage-collection-behaviour-for-string-intern
// or link: http://stackoverflow.com/questions/1091045/is-it-good-practice-to-use-java-lang-string-intern
ExpressionType ret = ExpressionType.name_lookup.get(name.toLowerCase());
return (ret == null ? ExpressionType.INVALID : ret);
}
public String symbol() {
return m_symbol;
}
public boolean isAggregateExpression() {
return getExpressionClass() == AggregateExpression.class;
}
/**
* Return true iff this expression type is safe for
* creating materialized views on non-empty tables.
*/
public boolean isSafeForDDL() {
return m_isSafeForDDL;
}
/**
* When generating an output schema for a projection node we need to
* know if the preceeding aggregate expression is going to create a
* column's value. This is true when the expression is an aggregate
* or a windowed aggregate. We can't just make the windowed aggregate
* operations have class AggregateExpression.class because we will need
* the class to create WindowFunctionExpression objects, and these have a
* different representation than other aggregate expression objects.
* For example, they have a PartitionBy list and an OrderBy list.
*
* @return true if an expression's value is generated by an early plan node.
*/
public boolean isGeneratedAggregateExpression() {
return (getExpressionClass() == AggregateExpression.class)
|| (getExpressionClass() == WindowFunctionExpression.class);
}
public boolean isNullary() {
return this == ExpressionType.AGGREGATE_COUNT_STAR;
}
private static Map<ExpressionType, String> m_windowedAggName;
static {
m_windowedAggName = new HashMap<>();
m_windowedAggName.put(ExpressionType.AGGREGATE_WINDOWED_RANK, "RANK");
m_windowedAggName.put(ExpressionType.AGGREGATE_WINDOWED_DENSE_RANK, "DENSE_RANK");
m_windowedAggName.put(ExpressionType.AGGREGATE_WINDOWED_COUNT, "COUNT");
m_windowedAggName.put(ExpressionType.AGGREGATE_MAX, "MAX");
m_windowedAggName.put(ExpressionType.AGGREGATE_MIN, "MIN");
m_windowedAggName.put(ExpressionType.AGGREGATE_SUM, "SUM");
}
}