http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/codegen/EvalCodeGenerator.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/codegen/EvalCodeGenerator.java b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/EvalCodeGenerator.java new file mode 100644 index 0000000..c57f923 --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/EvalCodeGenerator.java @@ -0,0 +1,840 @@ +/** + * 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.FunctionDesc; +import org.apache.tajo.catalog.Schema; +import org.apache.tajo.common.TajoDataTypes; +import org.apache.tajo.datum.Datum; +import org.apache.tajo.datum.IntervalDatum; +import org.apache.tajo.datum.ProtobufDatum; +import org.apache.tajo.engine.eval.*; +import org.apache.tajo.engine.json.CoreGsonHelper; +import org.apache.tajo.org.objectweb.asm.*; +import org.apache.tajo.storage.Tuple; +import org.apache.tajo.storage.VTuple; + +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.util.Stack; + +import static org.apache.tajo.common.TajoDataTypes.DataType; +import static org.apache.tajo.engine.codegen.TajoGeneratorAdapter.*; +import static org.apache.tajo.engine.eval.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; + } + + public EvalNode compile(Schema schema, EvalNode expr) throws CompilationError { + + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + + String className = EvalCodeGenerator.class.getPackage().getName() + ".CompiledEval" + classSeq++; + EvalCodeGenContext context = new EvalCodeGenContext(TajoGeneratorAdapter.getInternalName(className), + schema, classWriter, expr); + visit(context, expr, new Stack<EvalNode>()); + 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, Stack<EvalNode> stack, UnaryEval unary) { + 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().getType()) { + 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) { + DataType srcType = cast.getOperand().getValueType(); + DataType 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); + printOut(context, "endIfNull"); + + emitLabel(context, afterEnd); + return cast; + } + + public EvalNode visitField(EvalCodeGenContext context, Stack<EvalNode> stack, FieldEval field) { + + if (field.getValueType().getType() == TajoDataTypes.Type.NULL_TYPE) { + 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, 2); + 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().getType()) { + 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, 2); + 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, DataType type, int idx) { + switch (type.getType()) { + 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 { + + DataType lhsType = evalNode.getLeftExpr().getValueType(); + DataType rhsType = evalNode.getRightExpr().getValueType(); + + if (lhsType.getType() == TajoDataTypes.Type.NULL_TYPE || rhsType.getType() == TajoDataTypes.Type.NULL_TYPE) { + 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().getType()) { + 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().getType().name() + + " const type is not supported"); + } + + context.pushNullFlag(constEval.getValueType().getType() != TajoDataTypes.Type.NULL_TYPE); + 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().getType() == TajoDataTypes.Type.NULL_TYPE) { + 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) { + int paramNum = func.getArgs().length; + context.push(paramNum); + context.newArray(Datum.class); // new Datum[paramNum] + final int DATUM_ARRAY = context.astore(); + + stack.push(func); + EvalNode [] params = func.getArgs(); + for (int paramIdx = 0; paramIdx < func.getArgs().length; paramIdx++) { + context.aload(DATUM_ARRAY); // array ref + context.methodvisitor.visitLdcInsn(paramIdx); // array idx + visit(context, params[paramIdx], stack); + context.convertToDatum(params[paramIdx].getValueType(), true); // value + context.methodvisitor.visitInsn(Opcodes.AASTORE); + } + stack.pop(); + + context.methodvisitor.visitTypeInsn(Opcodes.NEW, TajoGeneratorAdapter.getInternalName(VTuple.class)); + context.methodvisitor.visitInsn(Opcodes.DUP); + context.aload(DATUM_ARRAY); + context.newInstance(VTuple.class, new Class[]{Datum[].class}); // new VTuple(datum []) + context.methodvisitor.visitTypeInsn(Opcodes.CHECKCAST, TajoGeneratorAdapter.getInternalName(Tuple.class)); // cast to Tuple + final int TUPLE = context.astore(); + + FunctionDesc desc = func.getFuncDesc(); + + String fieldName = context.symbols.get(func); + String funcDescName = "L" + TajoGeneratorAdapter.getInternalName(desc.getFuncClass()) + ";"; + + context.aload(0); + context.methodvisitor.visitFieldInsn(Opcodes.GETFIELD, context.owner, fieldName, funcDescName); + context.aload(TUPLE); + context.invokeVirtual(desc.getFuncClass(), "eval", Datum.class, new Class[] {Tuple.class}); + + context.convertToPrimitive(func.getValueType()); + 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); + if (context.schema != null) { + emitGetField(context, context.owner, "schema", Schema.class); + } else { + context.methodvisitor.visitInsn(Opcodes.ACONST_NULL); + } + context.aload(2); // tuple + context.invokeVirtual(InEval.class, "eval", Datum.class, new Class[]{Schema.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); + if (context.schema != null) { + emitGetField(context, context.owner, "schema", Schema.class); + } else { + context.methodvisitor.visitInsn(Opcodes.ACONST_NULL); + } + context.aload(2); // tuple + context.invokeVirtual(clazz, "eval", Datum.class, new Class[]{Schema.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.getInstance().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; + } + +}
http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/codegen/ExecutorPreCompiler.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/codegen/ExecutorPreCompiler.java b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/ExecutorPreCompiler.java new file mode 100644 index 0000000..54d857b --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/ExecutorPreCompiler.java @@ -0,0 +1,213 @@ +/* + * 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 com.google.common.collect.Maps; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.tajo.catalog.Schema; +import org.apache.tajo.engine.eval.EvalNode; +import org.apache.tajo.engine.planner.*; +import org.apache.tajo.engine.planner.logical.*; +import org.apache.tajo.util.Pair; + +import java.util.Collections; +import java.util.Map; +import java.util.Stack; + +public class ExecutorPreCompiler extends BasicLogicalPlanVisitor<ExecutorPreCompiler.CompilationContext, LogicalNode> { + private static final Log LOG = LogFactory.getLog(ExecutorPreCompiler.class); + + private static final ExecutorPreCompiler instance; + + static { + instance = new ExecutorPreCompiler(); + } + + public static void compile(CompilationContext context, LogicalNode node) throws PlanningException { + instance.visit(context, null, null, node, new Stack<LogicalNode>()); + context.compiledEval = Collections.unmodifiableMap(context.compiledEval); + } + + public static Map<Pair<Schema, EvalNode>, EvalNode> compile(TajoClassLoader classLoader, LogicalNode node) + throws PlanningException { + CompilationContext context = new CompilationContext(classLoader); + instance.visit(context, null, null, node, new Stack<LogicalNode>()); + return context.compiledEval; + } + + public static class CompilationContext { + private final EvalCodeGenerator compiler; + private Map<Pair<Schema,EvalNode>, EvalNode> compiledEval; + + public CompilationContext(TajoClassLoader classLoader) { + this.compiler = new EvalCodeGenerator(classLoader); + this.compiledEval = Maps.newHashMap(); + } + + public EvalCodeGenerator getCompiler() { + return compiler; + } + + public Map<Pair<Schema, EvalNode>, EvalNode> getPrecompiedEvals() { + return compiledEval; + } + } + + private static void compileIfAbsent(CompilationContext context, Schema schema, EvalNode eval) { + Pair<Schema, EvalNode> key = new Pair<Schema, EvalNode>(schema, eval); + if (!context.compiledEval.containsKey(key)) { + try { + EvalNode compiled = context.compiler.compile(schema, eval); + context.compiledEval.put(key, compiled); + + } catch (Throwable t) { + // If any compilation error occurs, it works in a fallback mode. This mode just uses EvalNode objects + // instead of a compiled EvalNode. + context.compiledEval.put(key, eval); + LOG.warn(t); + } + } + } + + private static void compileProjectableNode(CompilationContext context, Schema schema, Projectable node) { + Target [] targets; + if (node.hasTargets()) { + targets = node.getTargets(); + } else { + targets = PlannerUtil.schemaToTargets(node.getOutSchema()); + } + + for (Target target : targets) { + compileIfAbsent(context, schema, target.getEvalTree()); + } + } + + private static void compileSelectableNode(CompilationContext context, Schema schema, SelectableNode node) { + if (node.hasQual()) { + compileIfAbsent(context, schema, node.getQual()); + } + } + + @Override + public LogicalNode visitProjection(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + ProjectionNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitProjection(context, plan, block, node, stack); + + compileProjectableNode(context, node.getInSchema(), node); + + return node; + } + + @Override + public LogicalNode visitHaving(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + HavingNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitHaving(context, plan, block, node, stack); + + compileSelectableNode(context, node.getInSchema(), node); + + return node; + } + + @Override + public LogicalNode visitGroupBy(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + GroupbyNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitGroupBy(context, plan, block, node, stack); + + compileProjectableNode(context, node.getInSchema(), node); + + return node; + } + + @Override + public LogicalNode visitWindowAgg(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + WindowAggNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitWindowAgg(context, plan, block, node, stack); + + compileProjectableNode(context, node.getInSchema(), node); + + return node; + } + + public LogicalNode visitDistinct(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + DistinctGroupbyNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitDistinct(context, plan, block, node, stack); + + compileProjectableNode(context, node.getInSchema(), node); + return node; + } + + @Override + public LogicalNode visitFilter(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + SelectionNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitFilter(context, plan, block, node, stack); + + compileSelectableNode(context, node.getInSchema(), node); + + return node; + } + + @Override + public LogicalNode visitJoin(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + JoinNode node, Stack<LogicalNode> stack) throws PlanningException { + super.visitJoin(context, plan, block, node, stack); + + compileProjectableNode(context, node.getInSchema(), node); + + if (node.hasJoinQual()) { + compileIfAbsent(context, node.getInSchema(), node.getJoinQual()); + } + + return node; + } + + @Override + public LogicalNode visitTableSubQuery(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException { + stack.push(node); + visit(context, plan, null, node.getSubQuery(), stack); + stack.pop(); + + if (node.hasTargets()) { + for (Target target : node.getTargets()) { + compileIfAbsent(context, node.getTableSchema(), target.getEvalTree()); + } + } + + return node; + } + + @Override + public LogicalNode visitPartitionedTableScan(CompilationContext context, LogicalPlan plan, + LogicalPlan.QueryBlock block, PartitionedTableScanNode node, + Stack<LogicalNode> stack) throws PlanningException { + visitScan(context, plan, block, node, stack); + return node; + } + + @Override + public LogicalNode visitScan(CompilationContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + ScanNode node, Stack<LogicalNode> stack) throws PlanningException { + + compileProjectableNode(context, node.getInSchema(), node); + compileSelectableNode(context, node.getInSchema(), node); + + return node; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoClassLoader.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoClassLoader.java b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoClassLoader.java new file mode 100644 index 0000000..16be3b0 --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoClassLoader.java @@ -0,0 +1,30 @@ +/** + * 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; + +public class TajoClassLoader extends ClassLoader { + + public Class defineClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + + public void clean() throws Throwable { + super.finalize(); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoGeneratorAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoGeneratorAdapter.java b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoGeneratorAdapter.java new file mode 100644 index 0000000..6fac1a8 --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/TajoGeneratorAdapter.java @@ -0,0 +1,953 @@ +/** + * 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 com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.apache.tajo.common.TajoDataTypes; +import org.apache.tajo.datum.*; +import org.apache.tajo.engine.eval.EvalNode; +import org.apache.tajo.engine.eval.EvalType; +import org.apache.tajo.exception.InvalidCastException; +import org.apache.tajo.exception.UnsupportedException; +import org.apache.tajo.util.TUtil; +import org.apache.tajo.util.datetime.DateTimeUtil; +import org.apache.tajo.org.objectweb.asm.Label; +import org.apache.tajo.org.objectweb.asm.MethodVisitor; +import org.apache.tajo.org.objectweb.asm.Opcodes; +import org.apache.tajo.org.objectweb.asm.Type; +import org.apache.tajo.org.objectweb.asm.commons.GeneratorAdapter; +import org.apache.tajo.org.objectweb.asm.commons.TableSwitchGenerator; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.tajo.common.TajoDataTypes.Type.*; + +class TajoGeneratorAdapter { + + public static final Map<EvalType, Map<TajoDataTypes.Type, Integer>> OpCodesMap = Maps.newHashMap(); + + static { + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT1, Opcodes.IADD); + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT2, Opcodes.IADD); + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT4, Opcodes.IADD); + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT8, Opcodes.LADD); + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, FLOAT4, Opcodes.FADD); + TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, FLOAT8, Opcodes.DADD); + + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT1, Opcodes.ISUB); + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT2, Opcodes.ISUB); + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT4, Opcodes.ISUB); + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT8, Opcodes.LSUB); + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, FLOAT4, Opcodes.FSUB); + TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, FLOAT8, Opcodes.DSUB); + + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT1, Opcodes.IMUL); + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT2, Opcodes.IMUL); + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT4, Opcodes.IMUL); + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT8, Opcodes.LMUL); + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, FLOAT4, Opcodes.FMUL); + TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, FLOAT8, Opcodes.DMUL); + + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT1, Opcodes.IDIV); + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT2, Opcodes.IDIV); + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT4, Opcodes.IDIV); + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT8, Opcodes.LDIV); + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, FLOAT4, Opcodes.FDIV); + TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, FLOAT8, Opcodes.DDIV); + + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT1, Opcodes.IREM); + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT2, Opcodes.IREM); + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT4, Opcodes.IREM); + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT8, Opcodes.LREM); + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, FLOAT4, Opcodes.FREM); + TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, FLOAT8, Opcodes.DREM); + + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT1, Opcodes.IAND); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT2, Opcodes.IAND); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT4, Opcodes.IAND); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT8, Opcodes.LAND); + + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT1, Opcodes.IOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT2, Opcodes.IOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT4, Opcodes.IOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT8, Opcodes.LOR); + + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT1, Opcodes.IXOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT2, Opcodes.IXOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT4, Opcodes.IXOR); + TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT8, Opcodes.LXOR); + + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT1, Opcodes.IF_ICMPEQ); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT2, Opcodes.IF_ICMPEQ); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT4, Opcodes.IF_ICMPEQ); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, FLOAT8, Opcodes.DCMPG); + TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, TEXT, Opcodes.IF_ACMPNE); + + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT1, Opcodes.IF_ICMPNE); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT2, Opcodes.IF_ICMPNE); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT4, Opcodes.IF_ICMPNE); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, FLOAT8, Opcodes.DCMPG); + TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, TEXT, Opcodes.IF_ACMPNE); + + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT1, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT2, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT4, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT8, Opcodes.DCMPG); + + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT1, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT2, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT4, Opcodes.IF_ICMPLT); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT8, Opcodes.DCMPG); + + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT1, Opcodes.IF_ICMPLE); + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT2, Opcodes.IF_ICMPLE); + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT4, Opcodes.IF_ICMPLE); + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, FLOAT8, Opcodes.DCMPG); + + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT1, Opcodes.IF_ICMPGT); + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT2, Opcodes.IF_ICMPGT); + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT4, Opcodes.IF_ICMPGT); + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, FLOAT8, Opcodes.DCMPG); + + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT1, Opcodes.IF_ICMPGE); + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT2, Opcodes.IF_ICMPGE); + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT4, Opcodes.IF_ICMPGE); + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT8, Opcodes.LCMP); + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, FLOAT4, Opcodes.FCMPL); + TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, FLOAT8, Opcodes.DCMPG); + } + + protected int access; + protected MethodVisitor methodvisitor; + protected GeneratorAdapter generatorAdapter; + + public TajoGeneratorAdapter() {} + + public TajoGeneratorAdapter(int access, MethodVisitor methodVisitor, String name, String desc) { + this.access = access; + this.methodvisitor = methodVisitor; + generatorAdapter = new GeneratorAdapter(methodVisitor, access, name, desc); + } + + public static boolean isJVMInternalInt(TajoDataTypes.DataType dataType) { + TajoDataTypes.Type type = dataType.getType(); + return type == BOOLEAN || type == INT1 || type == INT2 || type == INT4 || type== INET4; + } + + public static int getWordSize(TajoDataTypes.DataType type) { + if (type.getType() == INT8 || type.getType() == FLOAT8 || type.getType() == TIMESTAMP || type.getType() == TIME) { + return 2; + } else { + return 1; + } + } + + public void push(final boolean value) { + methodvisitor.visitInsn(value ? Opcodes.ICONST_1 : Opcodes.ICONST_0); + } + + public void push(final int value) { + if (value >= -1 && value <= 5) { + methodvisitor.visitInsn(Opcodes.ICONST_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + methodvisitor.visitIntInsn(Opcodes.BIPUSH, value); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + methodvisitor.visitIntInsn(Opcodes.SIPUSH, value); + } else { + methodvisitor.visitLdcInsn(new Integer(value)); + } + } + + public void push(final long value) { + if (value == 0L || value == 1L) { + methodvisitor.visitInsn(Opcodes.LCONST_0 + (int) value); + } else { + methodvisitor.visitLdcInsn(new Long(value)); + } + } + + public void push(final float value) { + int bits = Float.floatToIntBits(value); + if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 + methodvisitor.visitInsn(Opcodes.FCONST_0 + (int) value); + } else { + methodvisitor.visitLdcInsn(new Float(value)); + } + } + + public void push(final double value) { + long bits = Double.doubleToLongBits(value); + if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d + methodvisitor.visitInsn(Opcodes.DCONST_0 + (int) value); + } else { + methodvisitor.visitLdcInsn(new Double(value)); + } + } + + public void push(final String value) { + Preconditions.checkNotNull(value); + methodvisitor.visitLdcInsn(value); + } + + public void ifCmp(TajoDataTypes.DataType dataType, EvalType evalType, Label elseLabel) { + + if (isJVMInternalInt(dataType)) { + switch (evalType) { + case EQUAL: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPNE, elseLabel); + break; + case NOT_EQUAL: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPEQ, elseLabel); + break; + case LTH: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPGE, elseLabel); + break; + case LEQ: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPGT, elseLabel); + break; + case GTH: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPLE, elseLabel); + break; + case GEQ: + methodvisitor.visitJumpInsn(Opcodes.IF_ICMPLT, elseLabel); + break; + default: + throw new CompilationError("Unknown comparison operator: " + evalType.name()); + } + } else { + + if (dataType.getType() == TEXT) { + invokeVirtual(String.class, "compareTo", int.class, new Class[]{String.class}); + } else { + int opCode = TajoGeneratorAdapter.getOpCode(evalType, dataType); + methodvisitor.visitInsn(opCode); + } + + switch (evalType) { + case EQUAL: + methodvisitor.visitJumpInsn(Opcodes.IFNE, elseLabel); + break; + case NOT_EQUAL: + methodvisitor.visitJumpInsn(Opcodes.IFEQ, elseLabel); + break; + case LTH: + methodvisitor.visitJumpInsn(Opcodes.IFGE, elseLabel); + break; + case LEQ: + methodvisitor.visitJumpInsn(Opcodes.IFGT, elseLabel); + break; + case GTH: + methodvisitor.visitJumpInsn(Opcodes.IFLE, elseLabel); + break; + case GEQ: + methodvisitor.visitJumpInsn(Opcodes.IFLT, elseLabel); + break; + default: + throw new CompilationError("Unknown comparison operator: " + evalType.name()); + } + } + } + + public void load(TajoDataTypes.DataType dataType, int idx) { + switch (dataType.getType()) { + case NULL_TYPE: + case BOOLEAN: + case CHAR: + case INT1: + case INT2: + case INT4: + case INET4: + methodvisitor.visitVarInsn(Opcodes.ILOAD, idx); + break; + case INT8: + methodvisitor.visitVarInsn(Opcodes.LLOAD, idx); + break; + case FLOAT4: + methodvisitor.visitVarInsn(Opcodes.FLOAD, idx); + break; + case FLOAT8: + methodvisitor.visitVarInsn(Opcodes.DLOAD, idx); + break; + case TEXT: + case INTERVAL: + case PROTOBUF: + methodvisitor.visitVarInsn(Opcodes.ALOAD, idx); + break; + default: + throw new CompilationError("Unknown data type: " + dataType.getType().name()); + } + } + + public static String getDescription(Class clazz) { + if (clazz == null) { + return ""; + } else if (clazz == void.class) { + return "V"; + } else if (clazz == boolean.class) { + return "Z"; + } else if (clazz == char.class) { + return "C"; + } else if (clazz == byte.class) { + return "B"; + } else if (clazz == short.class) { + return "S"; + } else if (clazz == int.class) { + return "I"; + } else if (clazz == long.class) { + return "J"; + } else if (clazz == float.class) { + return "F"; + } else if (clazz == double.class) { + return "D"; + } else if (clazz.isArray()) { + return "[" + getDescription(clazz.getComponentType()); + } else { + return "L" + getInternalName(clazz) + ";"; + } + } + + public static String getMethodDescription(Class returnType, Class [] argumentTypes) { + StringBuilder builder = new StringBuilder(); + builder.append("("); + if (argumentTypes != null) { + for (Class argType : argumentTypes) { + builder.append(getDescription(argType)); + } + } + builder.append(")"); + + builder.append(getDescription(returnType)); + return builder.toString(); + } + + public Label newLabel() { + return new Label(); + } + + public void gotoLabel(Label label) { + methodvisitor.visitJumpInsn(Opcodes.GOTO, label); + } + + public void pushBooleanOfThreeValuedLogic(boolean value) { + push(value ? 1 : 2); // TRUE or FALSE + } + + public void pushNullOfThreeValuedLogic() { + push(0); // NULL of three valued logic + } + + public void pushNullFlag(boolean trueIfNotNull) { + push(trueIfNotNull ? true : false); + } + + public void emitNullityCheck(Label ifNull) { + methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNull); + } + + /** + * If at least one of all local variables corresponding to <code>varIds</code> is null, jump the <code>label</code>. + * + * @param ifNull The label to jump + * @param varIds A list of variable Ids. + */ + public void emitNullityCheck(Label ifNull, int ... varIds) { + // TODO - ANDing can be reduced if we interleave IAND into a sequence of ILOAD instructions. + for (int varId : varIds) { + methodvisitor.visitVarInsn(Opcodes.ILOAD, varId); + } + if (varIds.length > 1) { + for (int i = 0; i < varIds.length - 1; i++) { + methodvisitor.visitInsn(Opcodes.IAND); + } + } + emitNullityCheck(ifNull); + } + + public void pushDummyValue(TajoDataTypes.DataType type) { + if (type.getType() == NULL_TYPE) { + pushNullOfThreeValuedLogic(); + } else if (isJVMInternalInt(type) || type.getType() == DATE) { + push(0); + } else if (type.getType() == TajoDataTypes.Type.INT8 || type.getType() == TIMESTAMP || type.getType() == TIME) { + push(0L); + } else if (type.getType() == TajoDataTypes.Type.FLOAT8) { + push(0.0d); + } else if (type.getType() == TajoDataTypes.Type.FLOAT4) { + push(0.0f); + } else if (type.getType() == TajoDataTypes.Type.CHAR || type.getType() == TajoDataTypes.Type.TEXT) { + push(""); + } else if (type.getType() == INTERVAL || type.getType() == PROTOBUF) { + invokeStatic(NullDatum.class, "get", NullDatum.class, new Class[]{}); + } else { + assert false; + } + } + + public void newInstance(Class owner, Class [] paramTypes) { + methodvisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, getInternalName(owner), "<init>", + getMethodDescription(void.class, paramTypes)); + } + + public void invokeSpecial(Class owner, String methodName, Class returnType, Class [] paramTypes) { + methodvisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, getInternalName(owner), methodName, + getMethodDescription(returnType, paramTypes)); + } + + public void invokeStatic(Class owner, String methodName, Class returnType, Class [] paramTypes) { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, getInternalName(owner), methodName, + getMethodDescription(returnType, paramTypes)); + } + + public void invokeVirtual(Class owner, String methodName, Class returnType, Class [] paramTypes) { + methodvisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(owner), methodName, + getMethodDescription(returnType, paramTypes)); + } + + public void invokeInterface(Class owner, String methodName, Class returnType, Class [] paramTypes) { + methodvisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(owner), methodName, + getMethodDescription(returnType, paramTypes)); + } + + public static boolean isPrimitiveOpCode(EvalType evalType, TajoDataTypes.DataType returnType) { + return TUtil.containsInNestedMap(OpCodesMap, evalType, returnType.getType()); + } + + public static int getOpCode(EvalType evalType, TajoDataTypes.DataType returnType) { + if (!isPrimitiveOpCode(evalType, returnType)) { + throw new CompilationError("No Such OpCode for " + evalType + " returning " + returnType.getType().name()); + } + return TUtil.getFromNestedMap(OpCodesMap, evalType, returnType.getType()); + } + + public void castInsn(TajoDataTypes.DataType srcType, TajoDataTypes.DataType targetType) { + TajoDataTypes.Type srcRawType = srcType.getType(); + TajoDataTypes.Type targetRawType = targetType.getType(); + switch(srcRawType) { + case BOOLEAN: + case CHAR: { + if (srcType.hasLength() && srcType.getLength() == 1) { + switch (targetType.getType()) { + case CHAR: + case INT1: + case INT2: + case INT4: break; + case INT8: methodvisitor.visitInsn(Opcodes.I2L); break; + case FLOAT4: methodvisitor.visitInsn(Opcodes.I2F); break; + case FLOAT8: methodvisitor.visitInsn(Opcodes.I2D); break; + case TEXT: emitStringValueOfChar(); break; + default: + throw new InvalidCastException(srcType, targetType); + } + } else { + switch (targetRawType) { + case CHAR: + case INT1: + case INT2: + case INT4: emitParseInt4(); break; + case INT8: emitParseInt8(); break; + case FLOAT4: emitParseFloat4(); break; + case FLOAT8: emitParseFloat8(); break; + case TEXT: break; + default: throw new InvalidCastException(srcType, targetType); + } + } + break; + } + case INT1: + case INT2: + case INT4: + switch (targetType.getType()) { + case CHAR: + case INT1: methodvisitor.visitInsn(Opcodes.I2C); break; + case INT2: methodvisitor.visitInsn(Opcodes.I2S); break; + case INT4: return; + case INT8: methodvisitor.visitInsn(Opcodes.I2L); break; + case FLOAT4: methodvisitor.visitInsn(Opcodes.I2F); break; + case FLOAT8: methodvisitor.visitInsn(Opcodes.I2D); break; + case TEXT: emitStringValueOfInt4(); break; + default: throw new InvalidCastException(srcType, targetType); + } + break; + case INT8: + switch (targetRawType) { + case CHAR: + case INT1: + case INT2: + case INT4: methodvisitor.visitInsn(Opcodes.L2I); break; + case INT8: return; + case FLOAT4: methodvisitor.visitInsn(Opcodes.L2F); break; + case FLOAT8: methodvisitor.visitInsn(Opcodes.L2D); break; + case TEXT: emitStringValueOfInt8(); break; + default: throw new InvalidCastException(srcType, targetType); + } + break; + case FLOAT4: + switch (targetRawType) { + case CHAR: + case INT1: + case INT2: + case INT4: methodvisitor.visitInsn(Opcodes.F2I); break; + case INT8: methodvisitor.visitInsn(Opcodes.F2L); break; + case FLOAT4: return; + case FLOAT8: methodvisitor.visitInsn(Opcodes.F2D); break; + case TEXT: emitStringValueOfFloat4(); break; + default: throw new InvalidCastException(srcType, targetType); + } + break; + case FLOAT8: + switch (targetRawType) { + case CHAR: + case INT1: + case INT2: + case INT4: methodvisitor.visitInsn(Opcodes.D2I); break; + case INT8: methodvisitor.visitInsn(Opcodes.D2L); break; + case FLOAT4: methodvisitor.visitInsn(Opcodes.D2F); break; + case FLOAT8: return; + case TEXT: emitStringValueOfFloat8(); break; + default: throw new InvalidCastException(srcType, targetType); + } + break; + case TEXT: + switch (targetRawType) { + case CHAR: + case INT1: + case INT2: + case INT4: emitParseInt4(); break; + case INT8: emitParseInt8(); break; + case FLOAT4: emitParseFloat4(); break; + case FLOAT8: emitParseFloat8(); break; + case TEXT: break; + case TIMESTAMP: { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), + "toJulianTimestampWithTZ", "(L" + Type.getInternalName(String.class) + ";)J"); + break; + } + case DATE: { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), + "toJulianDate", "(L" + Type.getInternalName(String.class) + ";)I"); + break; + } + case TIME: { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), + "toJulianTime", "(L" + Type.getInternalName(String.class) + ";)J"); + break; + } + default: throw new InvalidCastException(srcType, targetType); + } + break; + default: throw new InvalidCastException(srcType, targetType); + } + } + + public static String getInternalName(String className) { + return className.replace('.', '/'); + } + + public static String getInternalName(Class clazz) { + return clazz.getName().replace('.', '/'); + } + + public void convertToPrimitive(TajoDataTypes.DataType type) { + + Label ifNull = new Label(); + Label afterAll = new Label(); + + // datum + int datum = astore(); + + aload(datum); + invokeVirtual(Datum.class, "isNotNull", boolean.class, new Class [] {}); + methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNull); // datum + + aload(datum); + switch (type.getType()) { + case BOOLEAN: + case INT1: + case INT2: + invokeVirtual(Datum.class, "asInt2", short.class, new Class[] {}); + break; + case INT4: + case DATE: + invokeVirtual(Datum.class, "asInt4", int.class, new Class[] {}); + break; + case INT8: + case TIMESTAMP: + case TIME: + invokeVirtual(Datum.class, "asInt8", long.class, new Class[] {}); + break; + case FLOAT4: + invokeVirtual(Datum.class, "asFloat4", float.class, new Class[] {}); + break; + case FLOAT8: + invokeVirtual(Datum.class, "asFloat8", double.class, new Class[] {}); + break; + case CHAR: + case TEXT: + invokeVirtual(Datum.class, "asChars", String.class, new Class[]{}); + break; + default: + throw new UnsupportedException("Unsupported type: " + type); + } + + pushNullFlag(true); + gotoLabel(afterAll); + + methodvisitor.visitLabel(ifNull); + pushDummyValue(type); + pushNullFlag(false); + + methodvisitor.visitLabel(afterAll); + } + + public void convertToDatum(TajoDataTypes.DataType type, boolean castToDatum) { + String convertMethod; + Class returnType; + Class [] paramTypes; + switch (type.getType()) { + case NULL_TYPE: + pop(); // pop null flag + pop(type); // pop null datum + invokeStatic(NullDatum.class, "get", NullDatum.class, new Class[] {}); + if (castToDatum) { + methodvisitor.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(Datum.class)); + } + return; + + case BOOLEAN: + convertMethod = "createBool"; + returnType = Datum.class; + paramTypes = new Class[] {int.class}; + break; + case CHAR: + convertMethod = "createChar"; + returnType = CharDatum.class; + paramTypes = new Class[] {String.class}; + break; + case INT1: + case INT2: + convertMethod = "createInt2"; + returnType = Int2Datum.class; + paramTypes = new Class[] {short.class}; + break; + case INT4: + convertMethod = "createInt4"; + returnType = Int4Datum.class; + paramTypes = new Class[] {int.class}; + break; + case INT8: + convertMethod = "createInt8"; + returnType = Int8Datum.class; + paramTypes = new Class[] {long.class}; + break; + case FLOAT4: + convertMethod = "createFloat4"; + returnType = Float4Datum.class; + paramTypes = new Class[] {float.class}; + break; + case FLOAT8: + convertMethod = "createFloat8"; + returnType = Float8Datum.class; + paramTypes = new Class[] {double.class}; + break; + case TEXT: + convertMethod = "createText"; + returnType = TextDatum.class; + paramTypes = new Class[] {String.class}; + break; + case TIMESTAMP: + convertMethod = "createTimestamp"; + returnType = TimestampDatum.class; + paramTypes = new Class[] {long.class}; + break; + case DATE: + convertMethod = "createDate"; + returnType = DateDatum.class; + paramTypes = new Class[] {int.class}; + break; + case TIME: + convertMethod = "createTime"; + returnType = TimeDatum.class; + paramTypes = new Class[] {long.class}; + break; + case INTERVAL: + case PROTOBUF: + convertMethod = null; + returnType = null; + paramTypes = null; + break; + case INET4: + convertMethod = "createInet4"; + returnType = Inet4Datum.class; + paramTypes = new Class[] {int.class}; + break; + default: + throw new RuntimeException("Unsupported type: " + type.getType().name()); + } + + Label ifNull = new Label(); + Label afterAll = new Label(); + + emitNullityCheck(ifNull); + if (convertMethod != null) { + invokeStatic(DatumFactory.class, convertMethod, returnType, paramTypes); + } + methodvisitor.visitJumpInsn(Opcodes.GOTO, afterAll); + + methodvisitor.visitLabel(ifNull); + pop(type); + invokeStatic(NullDatum.class, "get", NullDatum.class, null); + + methodvisitor.visitLabel(afterAll); + if (castToDatum) { + methodvisitor.visitTypeInsn(Opcodes.CHECKCAST, TajoGeneratorAdapter.getInternalName(Datum.class)); + } + } + + public void pop(TajoDataTypes.DataType type) { + if (getWordSize(type) == 2) { + methodvisitor.visitInsn(Opcodes.POP2); + } else { + methodvisitor.visitInsn(Opcodes.POP); + } + } + + public void emitStringValueOfChar() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), + "valueOf", "(C)L" + Type.getInternalName(String.class) + ";"); + } + + public void emitStringValueOfInt4() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), + "valueOf", "(I)L" + Type.getInternalName(String.class) + ";"); + } + + public void emitStringValueOfInt8() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), + "valueOf", "(J)L" + Type.getInternalName(String.class) + ";"); + } + + public void emitStringValueOfFloat4() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), + "valueOf", "(F)L" + Type.getInternalName(String.class) + ";"); + } + + public void emitStringValueOfFloat8() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), + "valueOf", "(D)L" + Type.getInternalName(String.class) + ";"); + } + + public void emitParseInt4() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Integer.class), + "parseInt", "(L" + Type.getInternalName(String.class) + ";)I"); + } + + public void emitParseInt8() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Long.class), + "parseLong", "(L" + Type.getInternalName(String.class) + ";)J"); + } + + public void emitParseFloat4() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class), + "parseFloat", "(L" + Type.getInternalName(String.class) + ";)F"); + } + + public void emitParseFloat8() { + methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Double.class), + "parseDouble", "(L" + Type.getInternalName(String.class) + ";)D"); + } + + public void newArray(final Class clazz) { + int typeCode; + if (clazz == boolean.class) { + typeCode = Opcodes.T_BOOLEAN; + } else if (clazz == char.class) { + typeCode = Opcodes.T_CHAR; + } else if (clazz == byte.class) { + typeCode = Opcodes.T_BYTE; + } else if (clazz == short.class) { + typeCode = Opcodes.T_SHORT; + } else if (clazz == int.class) { + typeCode = Opcodes.T_INT; + } else if (clazz == long.class) { + typeCode = Opcodes.T_LONG; + } else if (clazz == float.class) { + typeCode = Opcodes.T_FLOAT; + } else if (clazz == double.class) { + typeCode = Opcodes.T_DOUBLE; + } else { + methodvisitor.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(clazz)); + return; + } + + methodvisitor.visitIntInsn(Opcodes.NEWARRAY, typeCode); + } + + private int nextVarId = 3; + + private Map<String, Integer> localVariablesMap = new HashMap<String, Integer>(); + + public void astore(String name) { + if (localVariablesMap.containsKey(name)) { + int varId = localVariablesMap.get(name); + methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); + } else { + int varId = nextVarId++; + methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); + localVariablesMap.put(name, varId); + } + } + + public int astore() { + int varId = getCurVarIdAndIncrease(); + methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); + return varId; + } + + public void astore(int varId) { + methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); + } + + public void aload(String name) { + if (localVariablesMap.containsKey(name)) { + int varId = localVariablesMap.get(name); + methodvisitor.visitVarInsn(Opcodes.ALOAD, varId); + } else { + throw new RuntimeException("No such variable name: " + name); + } + } + + public void aload(int varId) { + methodvisitor.visitVarInsn(Opcodes.ALOAD, varId); + } + + public void dup() { + methodvisitor.visitInsn(Opcodes.DUP); + } + + public void pop() { + methodvisitor.visitInsn(Opcodes.POP); + } + + public void pop2() { + methodvisitor.visitInsn(Opcodes.POP2); + } + + public int istore() { + int varId = getCurVarIdAndIncrease(); + methodvisitor.visitVarInsn(Opcodes.ISTORE, varId); + return varId; + } + + public void iload(int varId) { + methodvisitor.visitVarInsn(Opcodes.ILOAD, varId); + } + + private int getCurVarIdAndIncrease() { + int varId = nextVarId++; + return varId; + } + + private int getCurVarIdAndIncrease(TajoDataTypes.DataType type) { + int varId = nextVarId; + nextVarId += getWordSize(type); + return varId; + } + + public int store(TajoDataTypes.DataType type) { + int varId = nextVarId; + nextVarId += TajoGeneratorAdapter.getWordSize(type); + + switch (type.getType()) { + case NULL_TYPE: + case BOOLEAN: + case CHAR: + case INT1: + case INT2: + case INT4: + case INET4: + methodvisitor.visitVarInsn(Opcodes.ISTORE, varId); + break; + case TIME: + case TIMESTAMP: + case INT8: + methodvisitor.visitVarInsn(Opcodes.LSTORE, varId); + break; + case FLOAT4: + methodvisitor.visitVarInsn(Opcodes.FSTORE, varId); + break; + case FLOAT8: + methodvisitor.visitVarInsn(Opcodes.DSTORE, varId); + break; + case INTERVAL: + case TEXT: + methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); + break; + default: + throw new CompilationError("Unknown data type: " + type.getType().name()); + } + + return varId; + } + + public static interface SwitchCaseGenerator extends TableSwitchGenerator { + int size(); + int min(); + int max(); + int key(int index); + void generateCase(int index, Label end); + void generateDefault(); + } + + public static class SwitchCase implements Comparable<SwitchCase> { + private final int index; + private final EvalNode thanResult; + + public SwitchCase(int index, EvalNode thanResult) { + this.index = index; + this.thanResult = thanResult; + } + + public int key() { + return index; + } + + public EvalNode result() { + return thanResult; + } + + @Override + public int compareTo(SwitchCase o) { + return index - o.index; + } + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/codegen/VariablesPreBuilder.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/codegen/VariablesPreBuilder.java b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/VariablesPreBuilder.java new file mode 100644 index 0000000..9f50bb5 --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/codegen/VariablesPreBuilder.java @@ -0,0 +1,83 @@ +/* + * 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.common.TajoDataTypes; +import org.apache.tajo.datum.IntervalDatum; +import org.apache.tajo.engine.eval.*; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +import java.util.Stack; + +class VariablesPreBuilder extends SimpleEvalNodeVisitor<EvalCodeGenContext> { + + public EvalNode visitBinaryEval(EvalCodeGenContext context, Stack<EvalNode> stack, BinaryEval binaryEval) { + super.visitBinaryEval(context, stack, binaryEval); + + if (EvalType.isStringPatternMatchOperator(binaryEval.getType())) { + if (!context.symbols.containsKey(binaryEval)) { + String fieldName = binaryEval.getType().name() + "_" + context.seqId++; + context.symbols.put(binaryEval, fieldName); + + Class clazz = EvalCodeGenerator.getStringPatternEvalClass(binaryEval.getType()); + context.classWriter.visitField(Opcodes.ACC_PRIVATE, fieldName, + "L" + TajoGeneratorAdapter.getInternalName(clazz) + ";", null, null); + } + } else if (binaryEval.getType() == EvalType.IN) { + if (!context.symbols.containsKey(binaryEval)) { + String fieldName = binaryEval.getType().name() + "_" + context.seqId++; + context.symbols.put(binaryEval, fieldName); + + context.classWriter.visitField(Opcodes.ACC_PRIVATE, fieldName, + "L" + TajoGeneratorAdapter.getInternalName(InEval.class) + ";", null, null); + } + } + + return binaryEval; + } + + @Override + public EvalNode visitConst(EvalCodeGenContext context, ConstEval constEval, Stack<EvalNode> stack) { + + if (constEval.getValueType().getType() == TajoDataTypes.Type.INTERVAL) { + if (!context.symbols.containsKey(constEval)) { + String fieldName = constEval.getValueType().getType().name() + "_" + context.seqId++; + context.symbols.put(constEval, fieldName); + + context.classWriter.visitField(Opcodes.ACC_PRIVATE, fieldName, + "L" + TajoGeneratorAdapter.getInternalName(IntervalDatum.class) + ";", null, null); + } + } + return constEval; + } + + @Override + public EvalNode visitFuncCall(EvalCodeGenContext context, FunctionEval function, Stack<EvalNode> stack) { + super.visitFuncCall(context, function, stack); + + if (!context.symbols.containsKey(function)) { + String fieldName = function.getFuncDesc().getSignature() + "_" + context.seqId++; + context.symbols.put(function, fieldName); + context.classWriter.visitField(Opcodes.ACC_PRIVATE, fieldName, + "L" + TajoGeneratorAdapter.getInternalName(function.getFuncDesc().getFuncClass()) + ";", null, null); + } + + return function; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java index 4c94f4a..d7473e9 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java @@ -35,6 +35,7 @@ import static org.apache.tajo.common.TajoDataTypes.Type; public class BinaryEval extends EvalNode implements Cloneable { @Expose protected EvalNode leftExpr; @Expose protected EvalNode rightExpr; + @Expose protected DataType returnType; protected BinaryEval(EvalType type) { super(type); http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java index 48ab516..754f888 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java @@ -32,7 +32,6 @@ import org.apache.tajo.storage.Tuple; */ public abstract class EvalNode implements Cloneable, GsonObject { @Expose protected EvalType type; - @Expose protected DataType returnType = null; public EvalNode() { } @@ -70,7 +69,6 @@ public abstract class EvalNode implements Cloneable, GsonObject { public Object clone() throws CloneNotSupportedException { EvalNode evalNode = (EvalNode) super.clone(); evalNode.type = type; - evalNode.returnType = returnType; return evalNode; } } http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java index 0035636..457f651 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java @@ -43,12 +43,18 @@ public class EvalTreeUtil { node.postOrder(new ChangeColumnRefVisitor(oldName, newName)); } - public static void replace(EvalNode expr, EvalNode targetExpr, EvalNode tobeReplaced) { + public static int replace(EvalNode expr, EvalNode targetExpr, EvalNode tobeReplaced) { EvalReplaceVisitor replacer = new EvalReplaceVisitor(targetExpr, tobeReplaced); - replacer.visitChild(null, expr, new Stack<EvalNode>()); + ReplaceContext context = new ReplaceContext(); + replacer.visitChild(context, expr, new Stack<EvalNode>()); + return context.countOfReplaces; } - public static class EvalReplaceVisitor extends BasicEvalNodeVisitor<EvalNode, EvalNode> { + private static class ReplaceContext { + int countOfReplaces = 0; + } + + public static class EvalReplaceVisitor extends BasicEvalNodeVisitor<ReplaceContext, EvalNode> { private EvalNode target; private EvalNode tobeReplaced; @@ -58,10 +64,12 @@ public class EvalTreeUtil { } @Override - public EvalNode visitChild(EvalNode context, EvalNode evalNode, Stack<EvalNode> stack) { + public EvalNode visitChild(ReplaceContext context, EvalNode evalNode, Stack<EvalNode> stack) { super.visitChild(context, evalNode, stack); if (evalNode.equals(target)) { + context.countOfReplaces++; + EvalNode parent = stack.peek(); if (parent instanceof BetweenPredicateEval) { http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java index 500928a..d533510 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java @@ -152,6 +152,16 @@ public enum EvalType { return match; } + public static boolean isStringPatternMatchOperator(EvalType type) { + boolean match = false; + + match |= type == LIKE; + match |= type == SIMILAR_TO; + match |= type == REGEX; + + return match; + } + public String getOperatorName() { return operatorName != null ? operatorName : name(); } http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java index 9447ef2..5d358f7 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java @@ -47,6 +47,10 @@ public abstract class FunctionEval extends EvalNode implements Cloneable { this.argEvals = argEvals; } + public FunctionDesc getFuncDesc() { + return funcDesc; + } + public ParamType [] getParamType() { ParamType [] paramTypes = new ParamType[argEvals.length]; for (int i = 0; i < argEvals.length; i++) { @@ -93,6 +97,7 @@ public abstract class FunctionEval extends EvalNode implements Cloneable { return argEvals[idx]; } + public DataType getValueType() { return this.funcDesc.getReturnType(); } @@ -105,10 +110,6 @@ public abstract class FunctionEval extends EvalNode implements Cloneable { return funcDesc.getSignature(); } - public FunctionDesc getFuncDesc() { - return this.funcDesc; - } - @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -117,7 +118,7 @@ public abstract class FunctionEval extends EvalNode implements Cloneable { if(i+1 < argEvals.length) sb.append(","); } - return funcDesc.getSignature() + "(" + (isDistinct() ? " distinct" : "") + sb+")"; + return funcDesc.getSignature() + "(" + (isDistinct() ? " distinct " : "") + sb+")"; } @Override http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/LikePredicateEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/LikePredicateEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/LikePredicateEval.java index bfac33e..535677f 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/LikePredicateEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/LikePredicateEval.java @@ -25,6 +25,10 @@ import java.util.regex.PatternSyntaxException; public class LikePredicateEval extends PatternMatchPredicateEval { + public LikePredicateEval(boolean not, EvalNode field, ConstEval pattern) { + super(EvalType.LIKE, not, field, pattern, false); + } + public LikePredicateEval(boolean not, EvalNode field, ConstEval pattern, boolean caseSensitive) { super(EvalType.LIKE, not, field, pattern, caseSensitive); } http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-core/src/main/java/org/apache/tajo/engine/eval/PatternMatchPredicateEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/PatternMatchPredicateEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/PatternMatchPredicateEval.java index 8d78b0b..0a8e800 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/PatternMatchPredicateEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/PatternMatchPredicateEval.java @@ -55,6 +55,14 @@ public abstract class PatternMatchPredicateEval extends BinaryEval { abstract void compile(String pattern) throws PatternSyntaxException; + public boolean isNot() { + return not; + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + @Override public DataType getValueType() { return RES_TYPE;
