/**
* 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.engine.eval;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.Expose;
import org.apache.tajo.catalog.CatalogUtil;
import org.apache.tajo.catalog.Schema;
import org.apache.tajo.common.TajoDataTypes.DataType;
import org.apache.tajo.datum.Datum;
import org.apache.tajo.datum.DatumFactory;
import org.apache.tajo.datum.NullDatum;
import org.apache.tajo.storage.Tuple;
import static org.apache.tajo.common.TajoDataTypes.Type;
public class BinaryEval extends EvalNode implements Cloneable {
@Expose private DataType returnType = null;
/**
* @param type
*/
public BinaryEval(EvalType type, EvalNode left, EvalNode right) {
super(type, left, right);
Preconditions.checkNotNull(type);
Preconditions.checkNotNull(left);
Preconditions.checkNotNull(right);
if(
type == EvalType.AND ||
type == EvalType.OR ||
type == EvalType.EQUAL ||
type == EvalType.NOT_EQUAL ||
type == EvalType.LTH ||
type == EvalType.GTH ||
type == EvalType.LEQ ||
type == EvalType.GEQ ) {
this.returnType = CatalogUtil.newSimpleDataType(Type.BOOLEAN);
} else if (
type == EvalType.PLUS ||
type == EvalType.MINUS ||
type == EvalType.MULTIPLY ||
type == EvalType.DIVIDE ||
type == EvalType.MODULAR ) {
this.returnType = determineType(left.getValueType(), right.getValueType());
} else if (type == EvalType.CONCATENATE) {
this.returnType = CatalogUtil.newSimpleDataType(Type.TEXT);
}
}
public BinaryEval(PartialBinaryExpr expr) {
this(expr.type, expr.leftExpr, expr.rightExpr);
}
/**
* This is verified by ExprsVerifier.checkArithmeticOperand().
*/
private DataType determineType(DataType left, DataType right) throws InvalidEvalException {
switch (left.getType()) {
case INT4: {
switch(right.getType()) {
case INT2:
case INT4: return CatalogUtil.newSimpleDataType(Type.INT4);
case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
}
}
case INT8: {
switch(right.getType()) {
case INT2:
case INT4:
case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
}
}
case FLOAT4: {
switch(right.getType()) {
case INT2:
case INT4:
case INT8:
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
}
}
case FLOAT8: {
switch(right.getType()) {
case INT2:
case INT4:
case INT8:
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
}
}
default: return left;
}
}
@Override
public Datum eval(Schema schema, Tuple tuple) {
Datum lhs = leftExpr.eval(schema, tuple);
Datum rhs = rightExpr.eval(schema, tuple);
switch(type) {
case AND:
return lhs.and(rhs);
case OR:
return lhs.or(rhs);
case EQUAL:
return lhs.equalsTo(rhs);
case NOT_EQUAL:
return lhs.notEqualsTo(rhs);
case LTH:
return lhs.lessThan(rhs);
case LEQ:
return lhs.lessThanEqual(rhs);
case GTH:
return lhs.greaterThan(rhs);
case GEQ:
return lhs.greaterThanEqual(rhs);
case PLUS:
return lhs.plus(rhs);
case MINUS:
return lhs.minus(rhs);
case MULTIPLY:
return lhs.multiply(rhs);
case DIVIDE:
return lhs.divide(rhs);
case MODULAR:
return lhs.modular(rhs);
case CONCATENATE:
if (lhs.type() == Type.NULL_TYPE || rhs.type() == Type.NULL_TYPE) {
return NullDatum.get();
}
return DatumFactory.createText(lhs.asChars() + rhs.asChars());
default:
throw new InvalidEvalException("We does not support " + type + " expression yet");
}
}
@Override
public String getName() {
return type.name();
}
@Override
public DataType getValueType() {
return returnType;
}
public String toString() {
return leftExpr +" " + type.getOperatorName() + " "+rightExpr;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BinaryEval) {
BinaryEval other = (BinaryEval) obj;
boolean b1 = this.type == other.type;
boolean b2 = leftExpr.equals(other.leftExpr);
boolean b3 = rightExpr.equals(other.rightExpr);
return b1 && b2 && b3;
}
return false;
}
public int hashCode() {
return Objects.hashCode(this.type, leftExpr, rightExpr);
}
@Override
public Object clone() throws CloneNotSupportedException {
BinaryEval eval = (BinaryEval) super.clone();
eval.returnType = returnType;
return eval;
}
}