/** * Casts the rhs to a non nullable primitive value. Non nullable primitive values only have a * single value field. */ private Expression castToNotNullPrimitive() { // If the left and the right types are the same, perform a // trivial cast. if (lhsType == rhsType) { return getDirectAssignment(); } // Retrieve the value of the right side if it is a nullable // primitive or a Datetime or an Interval type. // TODO: is Decimal a nullable primitive? if (translator.isNullablePrimitive(rhsType) || SqlTypeUtil.isDatetime(rhsType) || SqlTypeUtil.isInterval(rhsType)) { rhsExp = getValue(rhsType, rhsExp); } // Get the name of the numeric class such as Byte, Short, etc. String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType); OJClass lhsClass = getLhsClass(); // When casting from a string (or binary) to a number, trim the // value and perform the cast by calling a class-specific parsing // function. if ((numClassName != null) && SqlTypeUtil.inCharOrBinaryFamilies(rhsType) && !SqlTypeUtil.isLob(rhsType)) { // TODO: toString will cause too much garbage collection. rhsExp = new MethodCall(rhsExp, "toString", new ExpressionList()); rhsExp = new MethodCall(rhsExp, "trim", new ExpressionList()); String methodName = "parse" + numClassName; if (lhsType.getSqlTypeName() == SqlTypeName.INTEGER) { methodName = "parseInt"; } rhsExp = new MethodCall( new Literal(Literal.STRING, numClassName), methodName, new ExpressionList(rhsExp)); Variable outTemp = translator.getRelImplementor().newVariable(); translator.addStatement( new VariableDeclaration( TypeName.forOJClass(lhsClass), new VariableDeclarator(outTemp.toString(), rhsExp))); rhsExp = outTemp; // Note: this check for overflow should only be required // when the string conversion does not perform a check. checkOverflow(); } else if ((lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN) && SqlTypeUtil.inCharOrBinaryFamilies(rhsType) && !SqlTypeUtil.isLob(rhsType)) { // Casting from string to boolean relies on the runtime type. // Note: string is trimmed by conversion method. // TODO: toString will cause too much garbage collection. Expression str = new MethodCall(rhsExp, "toString", new ExpressionList()); rhsExp = new MethodCall( OJClass.forClass(NullablePrimitive.NullableBoolean.class), "convertString", new ExpressionList(str)); } else { // In general, check for overflow checkOverflow(); } roundAsNeeded(); rhsExp = new CastExpression(lhsClass, rhsExp); return getDirectAssignment(); }
/** * Makes a statement to declare a stack variable * * @param clazz OJ class of the variable to declare * @param var the variable to be declared * @param init initial value for the declaration. */ private VariableDeclaration declareStackVar(OJClass clazz, Variable var, Expression init) { return new VariableDeclaration( TypeName.forOJClass(clazz), new VariableDeclarator(var.toString(), init)); }
/** * Generates code for a Java expression satisfying the {@link org.eigenbase.runtime.TupleIter} * interface. The generated code allocates a {@link org.eigenbase.runtime.CalcTupleIter} with a * dynamic {@link org.eigenbase.runtime.TupleIter#fetchNext()} method. If the "abort on error" * flag is false, or an error handling tag is specified, then fetchNext is written to handle row * errors. * * <p>Row errors are handled by wrapping expressions that can fail with a try/catch block. A * caught RuntimeException is then published to an "connection variable." In the event that errors * can overflow, an "error buffering" flag allows them to be posted again on the next iteration of * fetchNext. * * @param implementor an object that implements relations as Java code * @param rel the relation to be implemented * @param childExp the implemented child of the relation * @param varInputRow the Java variable to use for the input row * @param inputRowType the rel data type of the input row * @param outputRowType the rel data type of the output row * @param program the rex program to implemented by the relation * @param tag an error handling tag * @return a Java expression satisfying the TupleIter interface */ public static Expression implementAbstractTupleIter( JavaRelImplementor implementor, JavaRel rel, Expression childExp, Variable varInputRow, final RelDataType inputRowType, final RelDataType outputRowType, RexProgram program, String tag) { MemberDeclarationList memberList = new MemberDeclarationList(); // Perform error recovery if continuing on errors or if // an error handling tag has been specified boolean errorRecovery = !abortOnError || (tag != null); // Error buffering should not be enabled unless error recovery is assert !errorBuffering || errorRecovery; // Allow backwards compatibility until all Farrago extensions are // satisfied with the new error handling semantics. The new semantics // include: // (1) cast input object to input row object outside of try block, // should be fine, at least for base Farrago // (2) maintain a columnIndex counter to better locate of error, // at the cost of a few cycles // (3) publish errors to the runtime context. FarragoRuntimeContext // now supports this API boolean backwardsCompatible = true; if (tag != null) { backwardsCompatible = false; } RelDataTypeFactory typeFactory = implementor.getTypeFactory(); OJClass outputRowClass = OJUtil.typeToOJClass(outputRowType, typeFactory); OJClass inputRowClass = OJUtil.typeToOJClass(inputRowType, typeFactory); Variable varOutputRow = implementor.newVariable(); FieldDeclaration inputRowVarDecl = new FieldDeclaration( new ModifierList(ModifierList.PRIVATE), TypeName.forOJClass(inputRowClass), varInputRow.toString(), null); FieldDeclaration outputRowVarDecl = new FieldDeclaration( new ModifierList(ModifierList.PRIVATE), TypeName.forOJClass(outputRowClass), varOutputRow.toString(), new AllocationExpression(outputRowClass, new ExpressionList())); // The method body for fetchNext, a main target of code generation StatementList nextMethodBody = new StatementList(); // First, post an error if it overflowed the previous time // if (pendingError) { // rc = handleRowError(...); // if (rc instanceof NoDataReason) { // return rc; // } // pendingError = false; // } if (errorBuffering) { // add to next method body... } // Most of fetchNext falls within a while() block. The while block // allows us to try multiple input rows against a filter condition // before returning a single row. // while (true) { // Object varInputObj = inputIterator.fetchNext(); // if (varInputObj instanceof TupleIter.NoDataReason) { // return varInputObj; // } // varInputRow = (InputRowClass) varInputObj; // int columnIndex = 0; // [calculation statements] // } StatementList whileBody = new StatementList(); Variable varInputObj = implementor.newVariable(); whileBody.add( new VariableDeclaration( OJUtil.typeNameForClass(Object.class), varInputObj.toString(), new MethodCall(new FieldAccess("inputIterator"), "fetchNext", new ExpressionList()))); StatementList ifNoDataReasonBody = new StatementList(); whileBody.add( new IfStatement( new InstanceofExpression( varInputObj, OJUtil.typeNameForClass(TupleIter.NoDataReason.class)), ifNoDataReasonBody)); ifNoDataReasonBody.add(new ReturnStatement(varInputObj)); // Push up the row declaration for new error handling so that the // input row is available to the error handler if (!backwardsCompatible) { whileBody.add(assignInputRow(inputRowClass, varInputRow, varInputObj)); } Variable varColumnIndex = null; if (errorRecovery && !backwardsCompatible) { // NOTE jvs 7-Oct-2006: Declare varColumnIndex as a member // (rather than a local) in case in the future we want // to decompose complex expressions into helper methods. varColumnIndex = implementor.newVariable(); FieldDeclaration varColumnIndexDecl = new FieldDeclaration( new ModifierList(ModifierList.PRIVATE), OJUtil.typeNameForClass(int.class), varColumnIndex.toString(), null); memberList.add(varColumnIndexDecl); whileBody.add( new ExpressionStatement( new AssignmentExpression( varColumnIndex, AssignmentExpression.EQUALS, Literal.makeLiteral(0)))); } // Calculator (projection, filtering) statements are later appended // to calcStmts. Typically, this target will be the while list itself. StatementList calcStmts; if (!errorRecovery) { calcStmts = whileBody; } else { // For error recovery, we wrap the calc statements // (e.g., everything but the code that reads rows from the // inputIterator) in a try/catch that publishes exceptions. calcStmts = new StatementList(); // try { /* calcStmts */ } // catch(RuntimeException ex) { // Object rc = connection.handleRowError(...); // [buffer error if necessary] // } StatementList catchStmts = new StatementList(); if (backwardsCompatible) { catchStmts.add( new ExpressionStatement( new MethodCall( new MethodCall( OJUtil.typeNameForClass(EigenbaseTrace.class), "getStatementTracer", null), "log", new ExpressionList( new FieldAccess(OJUtil.typeNameForClass(Level.class), "WARNING"), Literal.makeLiteral("java calc exception"), new FieldAccess("ex"))))); } else { Variable varRc = implementor.newVariable(); ExpressionList handleRowErrorArgs = new ExpressionList(varInputRow, new FieldAccess("ex"), varColumnIndex); handleRowErrorArgs.add(Literal.makeLiteral(tag)); catchStmts.add( new VariableDeclaration( OJUtil.typeNameForClass(Object.class), varRc.toString(), new MethodCall( implementor.getConnectionVariable(), "handleRowError", handleRowErrorArgs))); // Buffer an error if it overflowed // if (rc instanceof NoDataReason) { // pendingError = true; // [save error state] // return rc; // } if (errorBuffering) { // add to catch statements... } } CatchList catchList = new CatchList( new CatchBlock( new Parameter(OJUtil.typeNameForClass(RuntimeException.class), "ex"), catchStmts)); TryStatement tryStmt = new TryStatement(calcStmts, catchList); whileBody.add(tryStmt); } if (backwardsCompatible) { calcStmts.add(assignInputRow(inputRowClass, varInputRow, varInputObj)); } StatementList condBody; RexToOJTranslator translator = implementor.newStmtTranslator(rel, calcStmts, memberList); try { translator.pushProgram(program); if (program.getCondition() != null) { // TODO jvs 8-Oct-2006: move condition to its own // method if big, as below for project exprs. condBody = new StatementList(); RexNode rexIsTrue = rel.getCluster() .getRexBuilder() .makeCall(SqlStdOperatorTable.isTrueOperator, program.getCondition()); Expression conditionExp = translator.translateRexNode(rexIsTrue); calcStmts.add(new IfStatement(conditionExp, condBody)); } else { condBody = calcStmts; } RelDataTypeField[] fields = outputRowType.getFields(); final List<RexLocalRef> projectRefList = program.getProjectList(); int i = -1; for (RexLocalRef rhs : projectRefList) { // NOTE jvs 14-Sept-2006: Put complicated project expressions // into their own method, otherwise a big select list can easily // blow the 64K Java limit on method bytecode size. Make // methods private final in the hopes that they will get inlined // JIT. For now we decide "complicated" based on the size of // the generated Java parse tree. A big enough select list of // simple expressions could still blow the limit, so we may need // to group them together, sub-divide, etc. StatementList projMethodBody = new StatementList(); if (errorRecovery && !backwardsCompatible) { projMethodBody.add( new ExpressionStatement( new UnaryExpression(varColumnIndex, UnaryExpression.POST_INCREMENT))); } ++i; RexToOJTranslator projTranslator = translator.push(projMethodBody); String javaFieldName = Util.toJavaId(fields[i].getName(), i); Expression lhs = new FieldAccess(varOutputRow, javaFieldName); projTranslator.translateAssignment(fields[i], lhs, rhs); int complexity = OJUtil.countParseTreeNodes(projMethodBody); // REVIEW: HCP 5/18/2011 // The projMethod should be checked // for causing possible compiler errors caused by the use of // variables declared in other projMethods. Also the // local declaration of variabled used by other proj methods // should also be checked. // Fixing for backswing integration 14270 // TODO: check if abstracting this method body will cause // a compiler error if (true) { // No method needed; just append. condBody.addAll(projMethodBody); continue; } // Need a separate method. String projMethodName = "calc_" + varOutputRow.toString() + "_f_" + i; MemberDeclaration projMethodDecl = new MethodDeclaration( new ModifierList(ModifierList.PRIVATE | ModifierList.FINAL), TypeName.forOJClass(OJSystem.VOID), projMethodName, new ParameterList(), null, projMethodBody); memberList.add(projMethodDecl); condBody.add(new ExpressionStatement(new MethodCall(projMethodName, new ExpressionList()))); } } finally { translator.popProgram(program); } condBody.add(new ReturnStatement(varOutputRow)); WhileStatement whileStmt = new WhileStatement(Literal.makeLiteral(true), whileBody); nextMethodBody.add(whileStmt); MemberDeclaration fetchNextMethodDecl = new MethodDeclaration( new ModifierList(ModifierList.PUBLIC), OJUtil.typeNameForClass(Object.class), "fetchNext", new ParameterList(), null, nextMethodBody); // The restart() method should reset variables used to buffer errors // pendingError = false if (errorBuffering) { // declare refinement of restart() and add to member list... } memberList.add(inputRowVarDecl); memberList.add(outputRowVarDecl); memberList.add(fetchNextMethodDecl); Expression newTupleIterExp = new AllocationExpression( OJUtil.typeNameForClass(CalcTupleIter.class), new ExpressionList(childExp), memberList); return newTupleIterExp; }