Package org.voltdb.expressions

Source Code of org.voltdb.expressions.ExpressionUtil

/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.expressions;

import java.util.List;
import java.util.Stack;

import org.voltdb.VoltType;
import org.voltdb.planner.PlanColumn;
import org.voltdb.planner.PlannerContext;
import org.voltdb.types.ExpressionType;
import org.voltdb.utils.NotImplementedException;
import org.voltdb.utils.Pair;
import org.voltdb.utils.VoltTypeUtil;

/**
*
*/
public abstract class ExpressionUtil {

    /**
     * Convenience method for determining whether an Expression object should have a child
     * Expression on its RIGHT side. The follow types of Expressions do not need a right child:
     *      OPERATOR_NOT
     *      COMPARISON_IN
     *      AggregageExpression
     *
     * @param exp
     * @return Does this expression need a right expression to be valid?
     */
    public static Boolean needsRightExpression(AbstractExpression exp) {
        assert(exp != null);
        return (exp.getExpressionType() != ExpressionType.OPERATOR_NOT &&
                exp.getExpressionType() != ExpressionType.COMPARE_IN &&
                !(exp instanceof AggregateExpression));
    }

    /**
     * Given an expression, find and convert its NUMERIC literals to
     * acceptable VoltTypes. Inserts may have expressions that are stand-alone
     * constant value expressions. In this case, allow the caller to pass
     * a column type. Otherwise, base the NUMERIC to VoltType conversion
     * on the other operand's type.
     */
    public static void assignLiteralConstantTypesRecursively(AbstractExpression exp) {
        assignLiteralConstantTypesRecursively(exp, VoltType.INVALID);
    }

    public static void assignLiteralConstantTypesRecursively(
            AbstractExpression exp, VoltType columnType)
    {
        if (exp == null)
            return;

        if ((exp instanceof ConstantValueExpression) &&
            (exp.m_valueType == VoltType.NUMERIC))
        {
            assert(columnType != VoltType.INVALID);
            exp.m_valueType = columnType;
            exp.m_valueSize = columnType.getLengthInBytesForFixedTypes();
            return;
        }
        assignLiteralConstantTypes_recurse(exp);
    }

    /**
     * Constant literals have a place-holder type of NUMERIC. These types
     * need to be converted to DECIMAL or DOUBLE in Volt based on the other
     * operand's type.
     */
    private static void assignLiteralConstantTypes_recurse(AbstractExpression exp)
    {
        // Depth first search for NUMERIC children.
        if (exp == null) {
            return;
        }

        assignLiteralConstantTypes_recurse(exp.m_left);
        assignLiteralConstantTypes_recurse(exp.m_right);

        if (exp.m_left != null && exp.m_left.m_valueType == VoltType.NUMERIC) {
            assignLiteralConstantType(exp.m_left, exp.m_right);
        }
        if (exp.m_right != null && exp.m_right.m_valueType == VoltType.NUMERIC) {
            assignLiteralConstantType(exp.m_right, exp.m_left);
        }
    }

    /**
     * Helper function to patch up NUMERIC typed constants.
     */
    private static void assignLiteralConstantType(AbstractExpression literal,
                                                  AbstractExpression other)
    {
        if (other.m_valueType != VoltType.DECIMAL) {
            literal.m_valueType = VoltType.FLOAT;
            literal.m_valueSize = VoltType.FLOAT.getLengthInBytesForFixedTypes();
        }
        else {
            literal.m_valueType = VoltType.DECIMAL;
            literal.m_valueSize = VoltType.DECIMAL.getLengthInBytesForFixedTypes();
        }
    }

    public static void assignOutputValueTypesRecursively(AbstractExpression exp) {
        if (exp == null)
            return;
        if (exp.m_left != null)
            assignOutputValueTypesRecursively(exp.m_left);
        if (exp.m_right != null)
            assignOutputValueTypesRecursively(exp.m_right);

        Pair<VoltType,Integer> exprData = calculateOutputValueTypes(exp);
        exp.m_valueType = exprData.getFirst();
        exp.m_valueSize = exprData.getSecond();
    }

    /**
     * For a given Expression node, figure out what its output values are
     * This method only looks at a single node and will not recursively walk through the tree
     *
     * @param exp
     */
    public static Pair<VoltType,Integer> calculateOutputValueTypes(AbstractExpression exp) {
        VoltType retType = VoltType.INVALID;
        int retSize = 0;
        //
        // First get the value types for the left and right children
        //
        ExpressionType exp_type = exp.getExpressionType();
        AbstractExpression left_exp = exp.getLeft();
        AbstractExpression right_exp = exp.getRight();

        // -------------------------------
        // CONSTANT/NULL/PARAMETER/TUPLE VALUES
        // If our current expression is a Value node, then the QueryPlanner should have
        // already figured out our types and there is nothing we need to do here
        // -------------------------------
        if (exp instanceof ConstantValueExpression ||
            exp instanceof NullValueExpression ||
            exp instanceof ParameterValueExpression ||
            exp instanceof TupleValueExpression) {
            //
            // Nothing to do...
            // We have to return what we already have to keep the QueryPlanner happy
            //
            retType = exp.getValueType();
            retSize = exp.getValueSize();
        // -------------------------------
        // CONJUNCTION & COMPARISON
        // If it is an Comparison or Conjunction node, then the output is always
        // going to be either true or false
        // -------------------------------
        } else if (exp instanceof ComparisonExpression ||
                   exp instanceof ConjunctionExpression) {
            //
            // Make sure that they have the same number of output values
            // NOTE: We do not need to do this check for COMPARE_IN
            //
            if (exp_type != ExpressionType.COMPARE_IN) {
                //
                // IMPORTANT:
                // We are not handling the case where one of types is NULL. That is because we
                // are only dealing with what the *output* type should be, not what the actual
                // value is at execution time. There will need to be special handling code
                // over on the ExecutionEngine to handle special cases for conjunctions with NULLs
                // Therefore, it is safe to assume that the output is always going to be an
                // integer (for booleans)
                //
                retType = VoltType.BIGINT;
                retSize = retType.getLengthInBytesForFixedTypes();
            //
            // Everything else...
            //
            } else {
                //
                // TODO: Need to figure out how COMPARE_IN is going to work
                //
                throw new NotImplementedException("The '" + exp_type + "' Expression is not yet supported");
            }
        // -------------------------------
        // AGGREGATES
        // -------------------------------
        } else if (exp instanceof AggregateExpression) {
            switch (exp_type) {
                case AGGREGATE_COUNT:
                case AGGREGATE_COUNT_STAR:
                    //
                    // Always an integer
                    //
                    retType = VoltType.BIGINT;
                    retSize = retType.getLengthInBytesForFixedTypes();
                    break;
                case AGGREGATE_AVG:
                case AGGREGATE_MAX:
                case AGGREGATE_MIN:
                    //
                    // It's always whatever the base type is
                    //
                    retType = left_exp.getValueType();
                    retSize = left_exp.getValueSize();
                    break;
                case AGGREGATE_SUM:
                    if (left_exp.getValueType() == VoltType.TINYINT ||
                            left_exp.getValueType() == VoltType.SMALLINT ||
                            left_exp.getValueType() == VoltType.INTEGER) {
                        retType = VoltType.BIGINT;
                        retSize = retType.getLengthInBytesForFixedTypes();
                    } else {
                        retType = left_exp.getValueType();
                        retSize = left_exp.getValueSize();
                    }
                    break;
                default:
                    throw new RuntimeException("ERROR: Invalid Expression type '" + exp_type + "' for Expression '" + exp + "'");
            } // SWITCH
        // -------------------------------
        // EVERYTHING ELSE
        // We need to look at our children and iterate through their
        // output value types. We will match up the left and right output types
        // at each position and call the method to figure out the cast type
        // -------------------------------
        } else {
            VoltType left_type = left_exp.getValueType();
            VoltType right_type = right_exp.getValueType();
            VoltType cast_type = VoltType.INVALID;
            //
            // If there doesn't need to be a a right expression, then the type will always be a integer (for booleans)
            //
            if (!ExpressionUtil.needsRightExpression(exp)) {
                //
                // Make sure that they can cast the left-side expression with integer
                // This is just a simple check to make sure that it is a numeric value
                //
                try {
                    // NOTE: in some brave new Decimal world, this check will be
                    // unnecessary.  This code path is currently extremely unlikely
                    // anyway since we don't support any of the ways to get here
                    cast_type = VoltType.DECIMAL;
                    if (left_type != VoltType.DECIMAL)
                    {
                        VoltTypeUtil.determineImplicitCasting(left_type, VoltType.BIGINT);
                        cast_type = VoltType.BIGINT;
                    }
                } catch (Exception ex) {
                    throw new RuntimeException("ERROR: Invalid type '" + left_type + "' used in a '" + exp_type + "' Expression");
                }
            //
            // Otherwise, use VoltTypeUtil to figure out what to case the value to
            //
            } else {
                cast_type = VoltTypeUtil.determineImplicitCasting(left_type, right_type);
            }
            if (cast_type == VoltType.INVALID) {
                throw new RuntimeException("ERROR: Invalid output value type for Expression '" + exp + "'");
            }
            retType = cast_type;
            // this may not always be safe
            retSize = cast_type.getLengthInBytesForFixedTypes();
        }
        return new Pair<VoltType, Integer>(retType, retSize);
    }

    /**
     * Returns true if the provided Expression object is a ConstantValueExpression and its
     * value will evaluate to true.
     * @param exp the Expression to check whether it is a
     * @return true if the exp is a ConstantValueExpression that evaluates to true
     */
    public static Boolean isTrueBooleanExpression(AbstractExpression exp) {
        return (exp instanceof ConstantValueExpression &&
                Integer.valueOf(((ConstantValueExpression)exp).getValue()) == 1);
    }

    /**
     *
     * @param exp
     * @throws Exception
     */
    public static AbstractExpression clone(AbstractExpression exp) throws Exception {
        AbstractExpression ret = (AbstractExpression)exp.clone();
//        if (exp.getLeft() != null) {
//            ret.setLeft(ExpressionUtil.clone(exp.getLeft()));
//        }
//        if (exp.getRight() != null) {
//            ret.setRight(ExpressionUtil.clone(exp.getRight()));
//        }
        return ret;
    }


    /**
     *
     * @param exps
     */
    public static AbstractExpression combine(List<AbstractExpression> exps) {
        Stack<AbstractExpression> stack = new Stack<AbstractExpression>();
        stack.addAll(exps);

        if (stack.isEmpty()) {
            return null;
        } else if (stack.size() == 1) {
            return stack.pop();
        }
        AbstractExpression ret = null;
        while (stack.size() > 1) {
            AbstractExpression child_exp = stack.pop();
            //
            // If our return node is null, then we need to make a new one
            //
            if (ret == null) {
                ret = new ConjunctionExpression(ExpressionType.CONJUNCTION_AND);
                ret.setLeft(child_exp);
            //
            // Check whether we can add it to the right side
            //
            } else if (ret.getRight() == null) {
                ret.setRight(child_exp);
                stack.push(ret);
                ret = null;
            }
        }
        if (ret == null) {
            ret = stack.pop();
        } else {
            ret.setRight(stack.pop());
        }
        return ret;
    }

    /**
     *
     * @param left
     * @param right
     * @return Both expressions passed in combined by an And conjunction.
     */
    public static AbstractExpression combine(AbstractExpression left, AbstractExpression right) {
        AbstractExpression ret = null;
        ret = new ConjunctionExpression(ExpressionType.CONJUNCTION_AND);
        ret.setLeft(left);
        ret.setRight(right);
        return ret;
    }

    public static void
    setAndOffsetColumnIndexes(PlannerContext context,
                              AbstractExpression input,
                              int offset,
                              String tableName,
                              List<Integer> outputColumns)
    {
        // recursive stopping step
        if (input == null)
            return;

        // recursive call
        setAndOffsetColumnIndexes(context, input.m_left, offset, tableName, outputColumns);
        setAndOffsetColumnIndexes(context, input.m_right, offset, tableName, outputColumns);

        // actual work
        if (input instanceof TupleValueExpression) {
            TupleValueExpression expr = (TupleValueExpression) input;
            if (expr.m_tableName.equals(tableName)) {
                expr.m_columnIndex += offset;
            }
            final String exprTableName = expr.getTableName();
            final String columnName = expr.getColumnName();
            int ii = 0;
            for (Integer colGuid : outputColumns) {
                PlanColumn info = context.get(colGuid);
                if (info.originTableName().equals(exprTableName)) {
                    if (info.getDisplayName().equals(columnName)) {
                        expr.setColumnIndex(ii);
                        return;
                    }
                }
                ii++;
            }
        }
    }

    /**
     * An AbstractExpression may be applied to a tuple that is a result of several joins and projections. Until the
     * order of joins and projections is known it is not possible to determine the column index of the value a TupleValueExpression
     * should return. Once that information is known the expression can be updated to have the correct index. It does this
     * by searching the list of output columns for the column that originated from the correct table and column name.
     * @param input The expression to update
     * @param outputColumns The description of the output columns of the tuple that this expression will be applied to.
     */
    public static void setColumnIndexes(PlannerContext context, AbstractExpression input, List<Integer> outputColumns) {
        if (input == null) {
            return;
        }

        // recursive call
        setColumnIndexes(context, input.m_left, outputColumns);
        setColumnIndexes(context, input.m_right, outputColumns);

        // actual work
        if (input instanceof TupleValueExpression) {
            final TupleValueExpression expr = (TupleValueExpression) input;
            final String tableName = expr.getTableName();
            final String columnName = expr.getColumnName();
            int ii = 0;
            for (Integer colGuid : outputColumns) {
                PlanColumn info = context.get(colGuid);
                if (info.originTableName().equals(tableName)) {
                    if (info.getDisplayName().equals(columnName)) {
                        expr.setColumnIndex(ii);
                        return;
                    }
                }
                ii++;
            }
        }

    }

    public static AbstractExpression getOtherTableExpression(AbstractExpression expr, String tableName) {
        assert(expr != null);
        AbstractExpression retval = expr.getLeft();

        AbstractExpression left = expr.getLeft();
        if (left instanceof TupleValueExpression) {
            TupleValueExpression lv = (TupleValueExpression) left;
            if (lv.getTableName().equals(tableName))
                retval = null;
        }

        if (retval == null) {
            retval = expr.getRight();
            AbstractExpression right = expr.getRight();
            if (right instanceof TupleValueExpression) {
                TupleValueExpression rv = (TupleValueExpression) right;
                if (rv.getTableName().equals(tableName))
                    retval = null;
            }
        }

        return retval;
    }
}
TOP

Related Classes of org.voltdb.expressions.ExpressionUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.