blob: 981370b83be5bc13d503829b14dd30041e3cb3e9 [file] [log] [blame]
/**
* 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.codegen;
import org.apache.tajo.catalog.Column;
import org.apache.tajo.catalog.Schema;
import org.apache.tajo.datum.Datum;
import org.apache.tajo.datum.IntervalDatum;
import org.apache.tajo.datum.ProtobufDatum;
import org.apache.tajo.engine.json.CoreGsonHelper;
import org.apache.tajo.org.objectweb.asm.ClassWriter;
import org.apache.tajo.org.objectweb.asm.Label;
import org.apache.tajo.org.objectweb.asm.Opcodes;
import org.apache.tajo.org.objectweb.asm.Type;
import org.apache.tajo.plan.expr.*;
import org.apache.tajo.storage.Tuple;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.util.Stack;
import static org.apache.tajo.engine.codegen.TajoGeneratorAdapter.getDescription;
import static org.apache.tajo.plan.expr.FunctionEval.ParamType;
public class EvalCodeGenerator extends SimpleEvalNodeVisitor<EvalCodeGenContext> {
public static final byte UNKNOWN = 0;
public static final byte TRUE = 1;
public static final byte FALSE = 2;
/** 0 - UNKNOWN, 1 - TRUE, 2 - FALSE */
@SuppressWarnings("unused")
public static final byte [] THREE_VALUES = new byte[] {UNKNOWN, TRUE, FALSE};
@SuppressWarnings("unused")
public static final byte [] NOT_LOGIC = new byte[] {UNKNOWN, FALSE, TRUE};
@SuppressWarnings("unused")
public static final byte [][] AND_LOGIC = new byte [][] {
// unknown true false
new byte [] {UNKNOWN, UNKNOWN, FALSE}, // unknown
new byte [] {UNKNOWN, TRUE, FALSE}, // true
new byte [] {FALSE, FALSE, FALSE} // false
};
@SuppressWarnings("unused")
public static final byte [][] OR_LOGIC = new byte [][] {
// unknown true false
new byte [] {UNKNOWN, TRUE, UNKNOWN}, // unknown
new byte [] {TRUE, TRUE, TRUE}, // true
new byte [] {UNKNOWN, TRUE, FALSE} // false
};
private final TajoClassLoader classLoader;
static int classSeq = 1;
public EvalCodeGenerator(TajoClassLoader classLoader) {
this.classLoader = classLoader;
}
protected synchronized static int getAndIncrementClassSeq() {
return classSeq++;
}
public EvalNode compile(Schema schema, EvalNode expr) throws CompilationError {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String className = EvalCodeGenerator.class.getPackage().getName() + ".CompiledEval" + getAndIncrementClassSeq();
EvalCodeGenContext context = new EvalCodeGenContext(TajoGeneratorAdapter.getInternalName(className),
schema, classWriter, expr);
visit(context, expr, new Stack<>());
context.emitReturn();
Class aClass = classLoader.defineClass(className, classWriter.toByteArray());
Constructor constructor;
EvalNode compiledEval;
try {
constructor = aClass.getConstructor();
compiledEval = (EvalNode) constructor.newInstance();
} catch (Throwable t) {
throw new CompilationError(expr, t, classWriter.toByteArray());
}
return compiledEval;
}
private void printOut(EvalCodeGenContext context, String message) {
context.methodvisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
context.push(message);
context.invokeVirtual(PrintStream.class, "println", void.class, new Class[]{String.class});
}
public EvalNode visitBinaryEval(EvalCodeGenContext context, Stack<EvalNode> stack, BinaryEval binaryEval) {
if (EvalType.isLogicalOperator(binaryEval.getType())) {
return visitAndOrEval(context, binaryEval, stack);
} else if (EvalType.isArithmeticOperator(binaryEval.getType())) {
return visitArithmeticEval(context, binaryEval, stack);
} else if (EvalType.isComparisonOperator(binaryEval.getType())) {
return visitComparisonEval(context, binaryEval, stack);
} else if (binaryEval.getType() == EvalType.CONCATENATE) {
return visitStringConcat(context, binaryEval, stack);
} else if (binaryEval.getType() == EvalType.LIKE || binaryEval.getType() == EvalType.SIMILAR_TO
|| binaryEval.getType() == EvalType.REGEX) {
return visitStringPatternMatch(context, binaryEval, stack);
} else if (binaryEval.getType() == EvalType.IN) {
return visitInPredicate(context, binaryEval, stack);
} else {
stack.push(binaryEval);
visit(context, binaryEval.getLeftExpr(), stack);
visit(context, binaryEval.getRightExpr(), stack);
stack.pop();
return binaryEval;
}
}
public EvalNode visitUnaryEval(EvalCodeGenContext context, UnaryEval unary, Stack<EvalNode> stack) {
stack.push(unary);
if (unary.getType() == EvalType.CAST) {
visitCast(context, stack, (CastEval) unary);
} else if (unary.getType() == EvalType.NOT) {
visit(context, unary.getChild(), stack);
context.methodvisitor.visitVarInsn(Opcodes.ISTORE, 9);
context.methodvisitor.visitVarInsn(Opcodes.ISTORE, 10);
Label ifNull = new Label();
Label endIf = new Label();
context.emitNullityCheck(ifNull, 9);
context.methodvisitor.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(EvalCodeGenerator.class),
"NOT_LOGIC", "[B");
context.methodvisitor.visitVarInsn(Opcodes.ILOAD, 10);
context.methodvisitor.visitInsn(Opcodes.BALOAD);
context.pushNullFlag(true);
emitGotoLabel(context, endIf);
emitLabel(context, ifNull);
context.pushDummyValue(unary.getValueType());
context.pushNullFlag(false);
emitLabel(context, endIf);
} else if (unary.getType() == EvalType.IS_NULL) {
return visitIsNull(context, (IsNullEval) unary, stack);
} else if (unary.getType() == EvalType.SIGNED) {
visit(context, unary.getChild(), stack);
Label ifNull = new Label();
Label endIf = new Label();
context.emitNullityCheck(ifNull);
SignedEval signed = (SignedEval) unary;
switch (signed.getValueType().kind()) {
case BOOLEAN:
case CHAR:
case INT1:
case INT2:
case INT4: context.methodvisitor.visitInsn(Opcodes.INEG); break;
case INT8: context.methodvisitor.visitInsn(Opcodes.LNEG); break;
case FLOAT4: context.methodvisitor.visitInsn(Opcodes.FNEG); break;
case FLOAT8: context.methodvisitor.visitInsn(Opcodes.DNEG); break;
default: throw new InvalidEvalException(unary.getType() + " operation to " + signed.getChild() + " is invalid.");
}
context.pushNullFlag(true);
emitGotoLabel(context, endIf);
emitLabel(context, ifNull);
context.pushNullFlag(false);
emitLabel(context, endIf);
} else {
super.visit(context, unary, stack);
}
stack.pop();
return unary;
}
public EvalNode visitBetween(EvalCodeGenContext context, BetweenPredicateEval between, Stack<EvalNode> stack) {
EvalNode predicand = between.getPredicand();
EvalNode begin = between.getBegin();
EvalNode end = between.getEnd();
stack.push(between);
visit(context, predicand, stack);
final int PREDICAND_NULLFLAG = context.istore();
final int PREDICAND = context.store(predicand.getValueType());
visit(context, begin, stack);
final int BEGIN_NULLFLAG = context.istore();
final int BEGIN = context.store(begin.getValueType());
visit(context, end, stack); // < end, right_nullflag
final int END_NULLFLAG = context.istore();
final int END = context.store(end.getValueType()); // <
stack.pop();
Label ifNullCommon = new Label();
Label ifNotMatched = new Label();
Label afterEnd = new Label();
context.emitNullityCheck(ifNullCommon, PREDICAND_NULLFLAG, BEGIN_NULLFLAG, END_NULLFLAG);
if (between.isSymmetric()) {
Label ifFirstMatchFailed = new Label();
Label ifSecondMatchFailed = new Label();
Label secondCheck = new Label();
Label finalDisjunctive = new Label();
//////////////////////////////////////////////////////////////////////////////////////////
// second check
//////////////////////////////////////////////////////////////////////////////////////////
// predicand <= begin
context.load(begin.getValueType(), BEGIN);
context.load(predicand.getValueType(), PREDICAND);
context.ifCmp(predicand.getValueType(), EvalType.LEQ, ifFirstMatchFailed);
// end <= predicand
context.load(end.getValueType(), END);
context.load(predicand.getValueType(), PREDICAND);
// inverse the operator GEQ -> LTH
context.ifCmp(predicand.getValueType(), EvalType.GEQ, ifFirstMatchFailed);
context.push(true);
emitGotoLabel(context, secondCheck);
emitLabel(context, ifFirstMatchFailed);
context.push(false);
//////////////////////////////////////////////////////////////////////////////////////////
// second check
//////////////////////////////////////////////////////////////////////////////////////////
emitLabel(context, secondCheck);
// predicand <= end
context.load(end.getValueType(), END);
context.load(predicand.getValueType(), PREDICAND);
// inverse the operator LEQ -> GTH
context.ifCmp(predicand.getValueType(), EvalType.LEQ, ifSecondMatchFailed);
// end <= predicand
context.load(begin.getValueType(), BEGIN);
context.load(predicand.getValueType(), PREDICAND);
// inverse the operator GEQ -> LTH
context.ifCmp(predicand.getValueType(), EvalType.GEQ, ifSecondMatchFailed);
context.push(true);
emitGotoLabel(context, finalDisjunctive);
emitLabel(context, ifSecondMatchFailed);
context.push(false);
emitLabel(context, finalDisjunctive);
context.methodvisitor.visitInsn(Opcodes.IOR);
context.methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNotMatched);
} else {
// predicand <= begin
context.load(begin.getValueType(), BEGIN);
context.load(predicand.getValueType(), PREDICAND);
context.ifCmp(predicand.getValueType(), EvalType.LEQ, ifNotMatched);
// end <= predicand
context.load(end.getValueType(), END);
context.load(predicand.getValueType(), PREDICAND);
context.ifCmp(predicand.getValueType(), EvalType.GEQ, ifNotMatched);
}
// IF MATCHED
context.pushBooleanOfThreeValuedLogic(between.isNot() ? false : true);
context.pushNullFlag(true);
emitGotoLabel(context, afterEnd);
emitLabel(context, ifNotMatched); // IF NOT MATCHED
context.pushBooleanOfThreeValuedLogic(between.isNot() ? true : false);
context.pushNullFlag(true);
emitGotoLabel(context, afterEnd);
emitLabel(context, ifNullCommon); // IF NULL
context.pushNullOfThreeValuedLogic();
context.pushNullFlag(false);
emitLabel(context, afterEnd);
return between;
}
private void emitGotoLabel(EvalCodeGenContext context, Label label) {
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, label);
}
void emitLabel(EvalCodeGenContext context, Label label) {
context.methodvisitor.visitLabel(label);
}
public EvalNode visitCast(EvalCodeGenContext context, Stack<EvalNode> stack, CastEval cast) {
org.apache.tajo.type.Type srcType = cast.getOperand().getValueType();
org.apache.tajo.type.Type targetType = cast.getValueType();
if (srcType.equals(targetType)) {
visit(context, cast.getChild(), stack);
return cast;
}
visit(context, cast.getChild(), stack);
Label ifNull = new Label();
Label afterEnd = new Label();
context.emitNullityCheck(ifNull);
context.castInsn(srcType, targetType);
context.pushNullFlag(true);
emitGotoLabel(context, afterEnd);
emitLabel(context, ifNull);
context.pop(srcType);
context.pushDummyValue(targetType);
context.pushNullFlag(false);
emitLabel(context, afterEnd);
return cast;
}
public EvalNode visitField(EvalCodeGenContext context, FieldEval field, Stack<EvalNode> stack) {
if (field.getValueType().isNull()) {
context.pushNullOfThreeValuedLogic();
context.pushNullFlag(false);
} else {
Column columnRef = field.getColumnRef();
int fieldIdx;
if (columnRef.hasQualifier()) {
fieldIdx = context.schema.getColumnId(columnRef.getQualifiedName());
} else {
fieldIdx = context.schema.getColumnIdByName(columnRef.getSimpleName());
}
context.methodvisitor.visitVarInsn(Opcodes.ALOAD, 1);
context.push(fieldIdx);
context.invokeInterface(Tuple.class, "isNull", boolean.class, new Class [] {int.class});
context.push(true);
Label ifNull = new Label();
Label afterAll = new Label();
context.methodvisitor.visitJumpInsn(Opcodes.IF_ICMPEQ, ifNull);
String methodName = null;
Class returnType = null;
Class [] paramTypes = null;
switch (field.getValueType().kind()) {
case BOOLEAN:
methodName = "getByte";
returnType = byte.class;
paramTypes = new Class[] {int.class};
break;
case CHAR: {
methodName = "getText";
returnType = String.class;
paramTypes = new Class[] {int.class};
break;
}
case INT1:
case INT2:
case INT4:
case DATE:
case INET4:
methodName = "getInt4";
returnType = int.class;
paramTypes = new Class [] {int.class};
break;
case INT8:
case TIMESTAMP:
case TIME:
methodName = "getInt8";
returnType = long.class;
paramTypes = new Class [] {int.class};
break;
case FLOAT4:
methodName = "getFloat4";
returnType = float.class;
paramTypes = new Class [] {int.class};
break;
case FLOAT8:
methodName = "getFloat8";
returnType = double.class;
paramTypes = new Class [] {int.class};
break;
case TEXT:
methodName = "getText";
returnType = String.class;
paramTypes = new Class [] {int.class};
break;
case INTERVAL:
methodName = "getInterval";
returnType = IntervalDatum.class;
paramTypes = new Class [] {int.class};
break;
case PROTOBUF:
methodName = "getProtobufDatum";
returnType = ProtobufDatum.class;
paramTypes = new Class [] {int.class};
break;
default:
throw new InvalidEvalException(field.getValueType() + " is not supported yet");
}
context.methodvisitor.visitVarInsn(Opcodes.ALOAD, 1);
context.push(fieldIdx);
context.invokeInterface(Tuple.class, methodName, returnType, paramTypes);
context.pushNullFlag(true); // not null
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, afterAll);
context.methodvisitor.visitLabel(ifNull);
context.pushDummyValue(field.getValueType());
context.pushNullFlag(false);
context.methodvisitor.visitLabel(afterAll);
}
return field;
}
public EvalNode visitAndOrEval(EvalCodeGenContext context, BinaryEval evalNode, Stack<EvalNode> stack) {
stack.push(evalNode);
visit(context, evalNode.getLeftExpr(), stack);
context.pop();
int LHS = context.istore();
visit(context, evalNode.getRightExpr(), stack);
context.pop();
int RHS = context.istore();
stack.pop();
if (evalNode.getType() == EvalType.AND) {
context.methodvisitor.visitFieldInsn(Opcodes.GETSTATIC,
org.apache.tajo.org.objectweb.asm.Type.getInternalName(EvalCodeGenerator.class), "AND_LOGIC", "[[B");
} else if (evalNode.getType() == EvalType.OR) {
context.methodvisitor.visitFieldInsn(Opcodes.GETSTATIC,
org.apache.tajo.org.objectweb.asm.Type.getInternalName(EvalCodeGenerator.class), "OR_LOGIC", "[[B");
} else {
throw new CompilationError("visitAndOrEval() cannot generate the code at " + evalNode);
}
context.load(evalNode.getLeftExpr().getValueType(), LHS);
context.methodvisitor.visitInsn(Opcodes.AALOAD);
context.load(evalNode.getRightExpr().getValueType(), RHS);
context.methodvisitor.visitInsn(Opcodes.BALOAD); // get three valued logic number from the AND/OR_LOGIC array
context.methodvisitor.visitInsn(Opcodes.DUP); // three valued logic number x 2, three valued logic number can be null flag.
return evalNode;
}
public static int store(EvalCodeGenContext context, org.apache.tajo.type.Type type, int idx) {
switch (type.kind()) {
case NULL_TYPE:
case BOOLEAN:
case CHAR:
case INT1:
case INT2:
case INT4:
context.methodvisitor.visitVarInsn(Opcodes.ISTORE, idx);
break;
case INT8: context.methodvisitor.visitVarInsn(Opcodes.LSTORE, idx); break;
case FLOAT4: context.methodvisitor.visitVarInsn(Opcodes.FSTORE, idx); break;
case FLOAT8: context.methodvisitor.visitVarInsn(Opcodes.DSTORE, idx); break;
default: context.methodvisitor.visitVarInsn(Opcodes.ASTORE, idx); break;
}
return idx + TajoGeneratorAdapter.getWordSize(type);
}
public EvalNode visitArithmeticEval(EvalCodeGenContext context, BinaryEval evalNode, Stack<EvalNode> stack) {
stack.push(evalNode);
visit(context, evalNode.getLeftExpr(), stack); // < left_child, push nullflag
int LHS_NULLFLAG = context.istore();
int LHS = context.store(evalNode.getLeftExpr().getValueType());
visit(context, evalNode.getRightExpr(), stack); // < left_child, right_child, nullflag
int RHS_NULLFLAG = context.istore();
int RHS = context.store(evalNode.getRightExpr().getValueType());
stack.pop();
Label ifNull = new Label();
Label afterEnd = new Label();
context.emitNullityCheck(ifNull, LHS_NULLFLAG, RHS_NULLFLAG);
context.load(evalNode.getLeftExpr().getValueType(), LHS);
context.load(evalNode.getRightExpr().getValueType(), RHS);
int opCode = TajoGeneratorAdapter.getOpCode(evalNode.getType(), evalNode.getValueType());
context.methodvisitor.visitInsn(opCode);
context.pushNullFlag(true);
emitGotoLabel(context, afterEnd);
emitLabel(context, ifNull);
context.pushDummyValue(evalNode.getValueType());
context.pushNullFlag(false);
emitLabel(context, afterEnd);
return evalNode;
}
public EvalNode visitComparisonEval(EvalCodeGenContext context, BinaryEval evalNode, Stack<EvalNode> stack)
throws CompilationError {
org.apache.tajo.type.Type lhsType = evalNode.getLeftExpr().getValueType();
org.apache.tajo.type.Type rhsType = evalNode.getRightExpr().getValueType();
if (lhsType.isNull() || rhsType.isNull()) {
context.pushNullOfThreeValuedLogic();
context.pushNullFlag(false);
} else {
stack.push(evalNode);
visit(context, evalNode.getLeftExpr(), stack); // < lhs, l_null
final int LHS_NULLFLAG = context.istore();
int LHS = context.store(evalNode.getLeftExpr().getValueType()); // <
visit(context, evalNode.getRightExpr(), stack); // < rhs, r_nullflag
final int RHS_NULLFLAG = context.istore();
final int RHS = context.store(evalNode.getRightExpr().getValueType()); // <
stack.pop();
Label ifNull = new Label();
Label ifNotMatched = new Label();
Label afterEnd = new Label();
context.emitNullityCheck(ifNull, LHS_NULLFLAG, RHS_NULLFLAG);
context.load(evalNode.getLeftExpr().getValueType(), LHS); // < lhs
context.load(evalNode.getRightExpr().getValueType(), RHS); // < lhs, rhs
context.ifCmp(evalNode.getLeftExpr().getValueType(), evalNode.getType(), ifNotMatched);
context.pushBooleanOfThreeValuedLogic(true);
context.pushNullFlag(true);
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, afterEnd);
context.methodvisitor.visitLabel(ifNotMatched);
context.pushBooleanOfThreeValuedLogic(false);
context.pushNullFlag(true);
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, afterEnd);
context.methodvisitor.visitLabel(ifNull);
context.pushNullOfThreeValuedLogic();
context.pushNullFlag(false);
context.methodvisitor.visitLabel(afterEnd);
}
return evalNode;
}
public EvalNode visitStringConcat(EvalCodeGenContext context, BinaryEval evalNode, Stack<EvalNode> stack)
throws CompilationError {
stack.push(evalNode);
visit(context, evalNode.getLeftExpr(), stack); // < lhs, l_null
final int LHS_NULLFLAG = context.istore(); // < lhs
final int LHS = context.store(evalNode.getLeftExpr().getValueType());
visit(context, evalNode.getRightExpr(), stack); // < rhs, r_nullflag
int RHS_NULLFLAG = context.istore();
int RHS = context.store(evalNode.getRightExpr().getValueType()); // <
stack.pop();
Label ifNull = new Label();
Label afterEnd = new Label();
context.emitNullityCheck(ifNull, LHS_NULLFLAG, RHS_NULLFLAG);
context.load(evalNode.getLeftExpr().getValueType(), LHS); // < lhs
context.load(evalNode.getRightExpr().getValueType(), RHS); // < lhs, rhs
context.invokeVirtual(String.class, "concat", String.class, new Class[] {String.class});
context.pushNullFlag(true);
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, afterEnd);
context.methodvisitor.visitLabel(ifNull);
context.pushDummyValue(evalNode.getValueType());
context.pushNullFlag(false);
context.methodvisitor.visitLabel(afterEnd);
return evalNode;
}
public EvalNode visitIsNull(EvalCodeGenContext context, IsNullEval isNullEval, Stack<EvalNode> stack) {
visit(context, isNullEval.getChild(), stack);
Label ifNull = new Label();
Label endIf = new Label();
context.emitNullityCheck(ifNull);
context.pop(isNullEval.getChild().getValueType());
context.pushBooleanOfThreeValuedLogic(isNullEval.isNot() ? true : false);
context.methodvisitor.visitJumpInsn(Opcodes.GOTO, endIf);
context.methodvisitor.visitLabel(ifNull);
context.pop(isNullEval.getChild().getValueType());
context.pushBooleanOfThreeValuedLogic(isNullEval.isNot() ? false : true);
emitLabel(context, endIf);
context.methodvisitor.visitInsn(Opcodes.ICONST_1); // NOT NULL
return isNullEval;
}
@Override
public EvalNode visitConst(EvalCodeGenContext context, ConstEval constEval, Stack<EvalNode> stack) {
switch (constEval.getValueType().kind()) {
case NULL_TYPE:
if (stack.isEmpty()) {
context.pushNullOfThreeValuedLogic();
} else {
EvalNode parentNode = stack.peek();
if (parentNode instanceof BinaryEval) {
BinaryEval parent = (BinaryEval) stack.peek();
if (parent.getLeftExpr() == constEval) {
context.pushDummyValue(parent.getRightExpr().getValueType());
} else {
context.pushDummyValue(parent.getLeftExpr().getValueType());
}
} else if (parentNode instanceof CaseWhenEval) {
CaseWhenEval caseWhen = (CaseWhenEval) parentNode;
context.pushDummyValue(caseWhen.getValueType());
} else {
throw new CompilationError("Cannot find matched type in the stack: " + constEval);
}
}
break;
case BOOLEAN:
context.push(constEval.getValue().asInt4());
break;
case INT1:
case INT2:
case INT4:
case DATE:
context.push(constEval.getValue().asInt4());
break;
case INT8:
case TIMESTAMP:
case TIME:
context.push(constEval.getValue().asInt8());
break;
case FLOAT4:
context.push(constEval.getValue().asFloat4());
break;
case FLOAT8:
context.push(constEval.getValue().asFloat8());
break;
case CHAR:
case TEXT:
context.push(constEval.getValue().asChars());
break;
case INTERVAL:
// load pre-stored variable.
emitGetField(context, context.owner, context.symbols.get(constEval), IntervalDatum.class);
break;
default:
throw new UnsupportedOperationException(constEval.getValueType() + " const type is not supported");
}
context.pushNullFlag(!constEval.getValueType().isNull());
return constEval;
}
public static ParamType[] getParamTypes(EvalNode [] arguments) {
ParamType[] paramTypes = new ParamType[arguments.length];
for (int i = 0; i < arguments.length; i++) {
if (arguments[i].getType() == EvalType.CONST) {
if (arguments[i].getValueType().isNull()) {
paramTypes[i] = ParamType.NULL;
} else {
paramTypes[i] = ParamType.CONSTANT;
}
} else {
paramTypes[i] = ParamType.VARIABLE;
}
}
return paramTypes;
}
@Override
public EvalNode visitFuncCall(EvalCodeGenContext context, FunctionEval func, Stack<EvalNode> stack) {
if (func.getFuncDesc().getInvocation().hasScalar()) {
ScalarFunctionBindingEmitter.emit(this, context, func, stack);
return func;
}
if (func.getFuncDesc().getInvocation().hasLegacy()) {
LegacyFunctionBindingEmitter.emit(this, context, func, stack);
return func;
}
return func;
}
public EvalNode visitInPredicate(EvalCodeGenContext context, EvalNode patternEval, Stack<EvalNode> stack) {
String fieldName = context.symbols.get(patternEval);
emitGetField(context, context.owner, fieldName, InEval.class);
context.aload(1); // tuple
context.invokeVirtual(InEval.class, "eval", Datum.class, new Class[]{Tuple.class});
context.convertToPrimitive(patternEval.getValueType());
return patternEval;
}
protected EvalNode visitStringPatternMatch(EvalCodeGenContext context, EvalNode patternEval, Stack<EvalNode> stack) {
Class clazz = getStringPatternEvalClass(patternEval.getType());
String fieldName = context.symbols.get(patternEval);
emitGetField(context, context.owner, fieldName, clazz);
context.aload(1); // tuple
context.invokeVirtual(clazz, "eval", Datum.class, new Class[]{Tuple.class});
context.convertToPrimitive(patternEval.getValueType());
return patternEval;
}
protected static void emitGetField(EvalCodeGenContext context, String owner, String fieldName, Class clazz) {
context.aload(0);
context.methodvisitor.visitFieldInsn(Opcodes.GETFIELD, owner, fieldName, getDescription(clazz));
}
public static Class getStringPatternEvalClass(EvalType type) {
if (type == EvalType.LIKE) {
return LikePredicateEval.class;
} else if (type == EvalType.SIMILAR_TO) {
return SimilarToPredicateEval.class;
} else {
return RegexPredicateEval.class;
}
}
@SuppressWarnings("unused")
public static EvalNode createEval(String json) {
return CoreGsonHelper.fromJson(json, EvalNode.class);
}
@SuppressWarnings("unused")
public static ConstEval createConstEval(String json) {
return (ConstEval) CoreGsonHelper.fromJson(json, EvalNode.class);
}
@SuppressWarnings("unused")
public static RowConstantEval createRowConstantEval(String json) {
return (RowConstantEval) CoreGsonHelper.fromJson(json, EvalNode.class);
}
@SuppressWarnings("unused")
public static Schema createSchema(String json) {
return CoreGsonHelper.fromJson(json, Schema.class);
}
@Override
protected EvalNode visitCaseWhen(EvalCodeGenContext context, CaseWhenEval caseWhen, Stack<EvalNode> stack) {
CaseWhenEmitter.emit(this, context, caseWhen, stack);
return caseWhen;
}
@Override
protected EvalNode visitIfThen(EvalCodeGenContext context, CaseWhenEval.IfThenEval evalNode, Stack<EvalNode> stack) {
stack.push(evalNode);
visit(context, evalNode.getCondition(), stack);
visit(context, evalNode.getResult(), stack);
stack.pop();
return evalNode;
}
}