| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.tajo.plan.verifier; |
| |
| import org.apache.tajo.catalog.Column; |
| import org.apache.tajo.common.TajoDataTypes; |
| import org.apache.tajo.error.Errors; |
| import org.apache.tajo.exception.TajoException; |
| import org.apache.tajo.exception.TajoInternalError; |
| import org.apache.tajo.exception.UndefinedOperatorException; |
| import org.apache.tajo.plan.expr.*; |
| import org.apache.tajo.plan.logical.LogicalNode; |
| |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import static org.apache.tajo.common.TajoDataTypes.Type.*; |
| |
| /** |
| * It verifies one predicate or expression with the semantic and data type checks as follows: |
| * <ul> |
| * <ul>Both expressions in a binary expression are compatible to each other</ul> |
| * <ul>All column references of one expression are available at this node</ul> |
| * </ul> |
| */ |
| public class ExprsVerifier extends BasicEvalNodeVisitor<VerificationState, EvalNode> { |
| private static final ExprsVerifier instance; |
| |
| static { |
| instance = new ExprsVerifier(); |
| } |
| |
| public static VerificationState verify(VerificationState state, LogicalNode currentNode, EvalNode expression) { |
| instance.visit(state, expression, new Stack<>()); |
| Set<Column> referredColumns = EvalTreeUtil.findUniqueColumns(expression); |
| for (Column referredColumn : referredColumns) { |
| if (!currentNode.getInSchema().contains(referredColumn)) { |
| throw new TajoInternalError("Invalid State: " + referredColumn + " cannot be accessible at Node (" |
| + currentNode.getPID() + ")"); |
| } |
| } |
| return state; |
| } |
| |
| /** |
| * It checks the compatibility of two data types. |
| */ |
| private static boolean isCompatibleType(org.apache.tajo.type.Type dataType1, org.apache.tajo.type.Type dataType2) { |
| if (checkNumericType(dataType1) && checkNumericType(dataType2)) { |
| return true; |
| } |
| |
| if (checkTextData(dataType1) && checkTextData(dataType2)) { |
| return true; |
| } |
| |
| if (checkDateTime(dataType1) && checkDateTime(dataType2)) { |
| return true; |
| } |
| |
| if (checkNetworkType(dataType1) && checkNetworkType(dataType2)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * It checks both expressions in a comparison operator are compatible to each other. |
| */ |
| private static void verifyComparisonOperator(VerificationState state, BinaryEval expr) { |
| if (!isCompatibleType(expr.getLeftExpr().getValueType(), expr.getRightExpr().getValueType())) { |
| state.addVerification(new UndefinedOperatorException(expr.toString())); |
| } |
| } |
| |
| public EvalNode visitEqual(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitEqual(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| public EvalNode visitNotEqual(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitNotEqual(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| @Override |
| public EvalNode visitLessThan(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitLessThan(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| @Override |
| public EvalNode visitLessThanOrEqual(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitLessThanOrEqual(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| @Override |
| public EvalNode visitGreaterThan(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitGreaterThan(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| @Override |
| public EvalNode visitGreaterThanOrEqual(VerificationState context, BinaryEval expr, Stack<EvalNode> stack) { |
| super.visitGreaterThanOrEqual(context, expr, stack); |
| verifyComparisonOperator(context, expr); |
| return expr; |
| } |
| |
| private static void checkDivisionByZero(VerificationState state, BinaryEval evalNode) { |
| if (evalNode.getRightExpr().getType() == EvalType.CONST) { |
| ConstEval constEval = evalNode.getRightExpr(); |
| if (constEval.getValue().asFloat8() == 0) { |
| state.addVerification(new TajoException(Errors.ResultCode.DIVISION_BY_ZERO, evalNode.toString())); |
| } |
| } |
| } |
| |
| private static void checkArithmeticOperand(VerificationState state, BinaryEval evalNode) { |
| EvalNode leftExpr = evalNode.getLeftExpr(); |
| EvalNode rightExpr = evalNode.getRightExpr(); |
| |
| org.apache.tajo.type.Type leftDataType = leftExpr.getValueType(); |
| org.apache.tajo.type.Type rightDataType = rightExpr.getValueType(); |
| |
| TajoDataTypes.Type leftType = leftDataType.baseType(); |
| TajoDataTypes.Type rightType = rightDataType.baseType(); |
| |
| if (leftType == DATE && |
| (checkIntType(rightDataType) || |
| rightType == DATE || rightType == INTERVAL || rightType == TIME)) { |
| return; |
| } |
| |
| if (leftType == INTERVAL && |
| (checkNumericType(rightDataType) || |
| rightType == DATE || rightType == INTERVAL || rightType == TIME || |
| rightType == TIMESTAMP)) { |
| return; |
| } |
| |
| if (leftType == TIME && |
| (rightType == DATE || rightType == INTERVAL || rightType == TIME || |
| rightType == TIMESTAMP)) { |
| return; |
| } |
| |
| if (leftType == TIMESTAMP && |
| (rightType == TIMESTAMP || rightType == INTERVAL || rightType == TajoDataTypes.Type.TIME)) { |
| return; |
| } |
| |
| if (!(checkNumericType(leftDataType) && checkNumericType(rightDataType))) { |
| state.addVerification(new UndefinedOperatorException(evalNode.toString())); |
| } |
| } |
| |
| private static boolean checkNetworkType(org.apache.tajo.type.Type dataType) { |
| return dataType.baseType() == INET4 || dataType.baseType() == INET6; |
| } |
| |
| private static boolean checkIntType(org.apache.tajo.type.Type dataType) { |
| int typeNumber = dataType.baseType().getNumber(); |
| return INT1.getNumber() < typeNumber && typeNumber <= INT8.getNumber(); |
| } |
| |
| private static boolean checkNumericType(org.apache.tajo.type.Type dataType) { |
| int typeNumber = dataType.baseType().getNumber(); |
| return INT1.getNumber() <= typeNumber && typeNumber <= NUMERIC.getNumber(); |
| } |
| |
| private static boolean checkTextData(org.apache.tajo.type.Type dataType) { |
| int typeNumber = dataType.baseType().getNumber(); |
| return CHAR.getNumber() <= typeNumber && typeNumber <= TEXT.getNumber(); |
| } |
| |
| private static boolean checkDateTime(org.apache.tajo.type.Type dataType) { |
| int typeNumber = dataType.baseType().getNumber(); |
| return (DATE.getNumber() <= typeNumber && typeNumber <= INTERVAL.getNumber()) || |
| (TIMEZ.getNumber() <= typeNumber && typeNumber <= TIMESTAMPZ.getNumber()); |
| } |
| |
| @Override |
| public EvalNode visitPlus(VerificationState context, BinaryEval evalNode, Stack<EvalNode> stack) { |
| super.visitPlus(context, evalNode, stack); |
| checkArithmeticOperand(context, evalNode); |
| return evalNode; |
| } |
| |
| @Override |
| public EvalNode visitMinus(VerificationState context, BinaryEval evalNode, Stack<EvalNode> stack) { |
| super.visitMinus(context, evalNode, stack); |
| checkArithmeticOperand(context, evalNode); |
| return evalNode; |
| } |
| |
| @Override |
| public EvalNode visitMultiply(VerificationState context, BinaryEval evalNode, Stack<EvalNode> stack) { |
| super.visitMultiply(context, evalNode, stack); |
| checkArithmeticOperand(context, evalNode); |
| return evalNode; |
| } |
| |
| @Override |
| public EvalNode visitDivide(VerificationState context, BinaryEval evalNode, Stack<EvalNode> stack) { |
| super.visitDivide(context, evalNode, stack); |
| checkArithmeticOperand(context, evalNode); |
| checkDivisionByZero(context, evalNode); |
| return evalNode; |
| } |
| |
| @Override |
| public EvalNode visitModular(VerificationState context, BinaryEval evalNode, Stack<EvalNode> stack) { |
| super.visitDivide(context, evalNode, stack); |
| checkArithmeticOperand(context, evalNode); |
| checkDivisionByZero(context, evalNode); |
| return evalNode; |
| } |
| |
| @Override |
| public EvalNode visitFuncCall(VerificationState context, GeneralFunctionEval evalNode, Stack<EvalNode> stack) { |
| super.visitFuncCall(context, evalNode, stack); |
| if (evalNode.getArgs() != null) { |
| for (EvalNode param : evalNode.getArgs()) { |
| visit(context, param, stack); |
| } |
| } |
| return evalNode; |
| } |
| } |