/**
     * Casts the rhs to an {@link AssignableValue} using that interface's standard assignment
     * method. i.e.
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[assignMethod](rhs);
     * </pre>
     *
     * or perhaps a type-specific cast:
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[castMethod](rhs, lhs.getPrecision());
     * </pre>
     *
     * <p>Code is also generated to pad and truncate values which need special handling, such as
     * date and time types. Plus good old null handling.
     */
    private Expression castToAssignableValue() {
      ensureLhs();

      if (requiresSpecializedCast() && rhsType.isNullable()) {
        assert (lhsType.isNullable());

        // propagate null value; normally, we can rely on
        // assignFrom to do it for us, but for specialized casts,
        // we can't
        Expression nullTest =
            new MethodCall(rhsExp, NullableValue.NULL_IND_ACCESSOR_NAME, new ExpressionList());
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp, NullableValue.NULL_IND_MUTATOR_NAME, new ExpressionList(nullTest))));
        StatementList ifStmtList = new StatementList();
        addStatement(new IfStatement(not(nullTest), ifStmtList));
        borrowStmtList(ifStmtList);
        try {
          return castToAssignableValueImpl();
        } finally {
          returnStmtList(ifStmtList);
        }
      } else {
        return castToAssignableValueImpl();
      }
    }
 /**
  * Gets the right hand expression as a valid value to be assigned to the left hand side. Usually
  * returns the original rhs. However, if the lhs is of a primitive type, and the rhs is an
  * explicit null, returns a primitive value instead.
  */
 private Expression rhsAsValue() {
   if (SqlTypeUtil.isJavaPrimitive(lhsType) && (rhsType.getSqlTypeName() == SqlTypeName.NULL)) {
     if (lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN) {
       return Literal.constantFalse();
     } else {
       return Literal.constantZero();
     }
   }
   return rhsExp;
 }
コード例 #3
0
ファイル: IterCalcRel.java プロジェクト: Jach/luciddb
 /**
  * Burrows into a synthetic record and returns the underlying relation which provides the field
  * called <code>fieldName</code>.
  */
 public JavaRel implementFieldAccess(JavaRelImplementor implementor, String fieldName) {
   if (!isBoxed()) {
     return implementor.implementFieldAccess((JavaRel) getChild(), fieldName);
   }
   RelDataType type = getRowType();
   int field = type.getFieldOrdinal(fieldName);
   RexLocalRef ref = program.getProjectList().get(field);
   final int index = ref.getIndex();
   return implementor.findRel((JavaRel) this, program.getExprList().get(index));
 }
 /** Generates code to throw an exception when a NULL value is casted to a NOT NULL type */
 private void checkNotNull() {
   if (!lhsType.isNullable() && rhsType.isNullable()) {
     rhsExp = rhsAsJava();
     addStatement(
         new ExpressionStatement(
             new MethodCall(
                 translator.getRelImplementor().getConnectionVariable(),
                 "checkNotNull",
                 new ExpressionList(Literal.makeLiteral(targetName), rhsExp))));
   }
 }
    /**
     * Implements a cast from any Java primitive to a nullable Java primitive as a simple
     * assignment. i.e.
     *
     * <pre>
     * [NullablePrimitiveType] lhs;
     * lhs.[nullIndicator] = ...;
     * if (! lhs.[nullIndicator]) {
     *     // check overflow ...
     *     // round ...
     *     lhs.[value] = ...;
     * }
     * </pre>
     */
    private Expression castPrimitiveToNullablePrimitive() {
      ensureLhs();
      boolean nullableSource = rhsType.isNullable();
      Expression rhsIsNull;
      if (nullableSource) {
        rhsIsNull = getNullIndicator(rhsExp);
        rhsExp = getValue(rhsType, rhsExp);
      } else {
        rhsIsNull = Literal.constantFalse();
      }

      addStatement(assign(getNullIndicator(lhsExp), rhsIsNull));
      StatementList setValueBlock = new StatementList();
      StatementList oldList = borrowStmtList(setValueBlock);
      try {
        checkOverflow();
        roundAsNeeded();
        addStatement(assign(getValue(lhsType, lhsExp), new CastExpression(getLhsClass(), rhsExp)));
      } finally {
        returnStmtList(oldList);
      }
      if (nullableSource) {
        addStatement(new IfStatement(not(getNullIndicator(lhsExp)), setValueBlock));
      } else {
        addStatementList(setValueBlock);
      }
      return lhsExp;
    }
    /**
     * Checks for overflow when assigning one primitive type to another. Non-primitive types check
     * for overflow during assignment.
     */
    private void checkOverflow() {
      String maxLiteral = null;
      String minLiteral = null;
      if (lhsType == null) {
        return;
      }

      // Assume that equivalent types can be assigned without overflow
      if (lhsType.getSqlTypeName() == rhsType.getSqlTypeName()) {
        return;
      }

      // Approximate numerics have a wider range than exact numerics
      if (SqlTypeUtil.isApproximateNumeric(lhsType) && SqlTypeUtil.isExactNumeric(rhsType)) {
        return;
      }

      // We can skip an error check if the left type is "larger"
      if (SqlTypeUtil.isIntType(lhsType)
          && SqlTypeUtil.isIntType(rhsType)
          && (SqlTypeUtil.maxValue(lhsType) >= SqlTypeUtil.maxValue(rhsType))) {
        return;
      }
      if (SqlTypeUtil.isExactNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        minLiteral = numClassName + ".MIN_VALUE";
        maxLiteral = numClassName + ".MAX_VALUE";
      } else if (SqlTypeUtil.isApproximateNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        maxLiteral = numClassName + ".MAX_VALUE";
        minLiteral = "-" + maxLiteral;
      }
      if (maxLiteral == null) {
        return;
      }
      Statement ifstmt =
          new IfStatement(
              new BinaryExpression(
                  new BinaryExpression(
                      rhsExp, BinaryExpression.LESS, new Literal(Literal.STRING, minLiteral)),
                  BinaryExpression.LOGICAL_OR,
                  new BinaryExpression(
                      rhsExp, BinaryExpression.GREATER, new Literal(Literal.STRING, maxLiteral))),
              getThrowStmtList());
      addStatement(ifstmt);
    }
 private boolean requiresSpecializedCast() {
   if ((rhsType != null)
       && (SqlTypeUtil.isNumeric(rhsType) || (rhsType.getSqlTypeName() == SqlTypeName.BOOLEAN))
       && SqlTypeUtil.inCharOrBinaryFamilies(lhsType)
       && !SqlTypeUtil.isLob(lhsType)) {
     // Boolean or Numeric to String.
     // sometimes the Integer got slipped by.
     return true;
   } else {
     return false;
   }
 }
  // implement FarragoOJRexImplementor
  public Expression implementFarrago(
      FarragoRexToOJTranslator translator, RexCall call, Expression[] operands) {
    RelDataType lhsType = call.getType();
    RelDataType rhsType = call.operands[0].getType();
    Expression rhsExp = operands[0];

    SqlTypeName lhsTypeName = lhsType.getSqlTypeName();
    if ((lhsTypeName == SqlTypeName.CURSOR) || (lhsTypeName == SqlTypeName.COLUMN_LIST)) {
      // Conversion should already have been taken care of outside.
      return rhsExp;
    }

    // NOTE jvs 19-Nov-2008:  In some cases (e.g. FRG-273) a cast
    // may be illegal at the SQL level, but allowable as part of
    // implementation, so don't try to enforce
    // SqlTypeUtil.canCastFrom here.  Anything which was supposed
    // to have been prevented should already have been caught
    // by the validator.

    CastHelper helper =
        new CastHelper(translator, null, call.toString(), lhsType, rhsType, null, rhsExp);

    return helper.implement();
  }
    /**
     * Implement the cast expression.
     *
     * <p>TODO: check for overflow
     *
     * @return the rhs expression casted as the lhs type
     */
    public Expression implement() {
      // Check for invalid null assignment. Code generated afterwards
      // can assume null will never be assigned to a not null value.
      checkNotNull();

      // Check for an explicit rhs null value. Code generated
      // afterwards need never check for an explicit null.
      if (rhsType.getSqlTypeName() == SqlTypeName.NULL) {
        if (lhsType.isNullable()) {
          return castFromNull();
        } else {
          // NOTE jvs 27-Jan-2005:  this code will never actually
          // be executed do to previous checkNotNull test, but
          // it still has to compile!
          return rhsAsValue();
        }
      }

      // Case when left hand side is a nullable primitive
      if (translator.isNullablePrimitive(lhsType)) {
        if (SqlTypeUtil.isJavaPrimitive(rhsType)
            && (!rhsType.isNullable() || translator.isNullablePrimitive(rhsType))) {
          return castPrimitiveToNullablePrimitive();
        }
        return castToAssignableValue();
      }

      // Case when left hand side is a not nullable primitive
      if (SqlTypeUtil.isJavaPrimitive(lhsType)) {
        return castToNotNullPrimitive();
      }

      // Case when left hand side is a structure
      if (lhsType.isStruct()) {
        assert (rhsType.isStruct());

        // TODO jvs 27-May-2004:  relax this assert and deal with
        // conversions, null checks, etc.
        assert (lhsType.equals(rhsType));

        return getDirectAssignment();
      }

      // Default is to treat non-primitives as AssignableValue
      return castToAssignableValue();
    }
    /**
     * 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();
    }
    private Expression castToAssignableValueImpl() {
      if (requiresSpecializedCast()) {
        if (rhsType.isNullable() && (!SqlTypeUtil.isDecimal(rhsType))) {
          rhsExp = getValue(rhsType, rhsExp);
        }
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp,
                    "cast",
                    new ExpressionList(rhsExp, Literal.makeLiteral(lhsType.getPrecision())))));
      } else {
        // Set current_date for casting time to timestamp. If
        // rhsType is null then we may have to be ready for anything.
        // But it will be null even for current_timestamp, so the
        // condition below seems a bit excessive.
        if ((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP)
            && ((rhsType == null) || (rhsType.getSqlTypeName() == SqlTypeName.TIME))) {
          addStatement(
              new ExpressionStatement(
                  new MethodCall(lhsExp, "setCurrentDate", new ExpressionList(getCurrentDate()))));
        }
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp, AssignableValue.ASSIGNMENT_METHOD_NAME, new ExpressionList(rhsExp))));
      }

      // Trim precision of datetime values.
      //
      if (((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP)
          || (lhsType.getSqlTypeName() == SqlTypeName.TIME))) {
        if ((rhsType != null)
            // FIXME: JavaType(java.sql.Time) and
            // JavaType(java.sql.Timestamp) say they support precision
            // but do not.
            && !rhsType.toString().startsWith("JavaType(")
            && rhsType.getSqlTypeName().allowsPrec()
            && (lhsType.getPrecision() < rhsType.getPrecision())) {
          int lhsPrecision = lhsType.getPrecision();
          if (lhsPrecision == -1) {
            lhsPrecision = 0;
          }
          addStatement(
              new ExpressionStatement(
                  new MethodCall(
                      lhsExp,
                      SqlDateTimeWithoutTZ.ADJUST_PRECISION_METHOD_NAME,
                      new ExpressionList(Literal.makeLiteral(lhsPrecision)))));
        }
      }

      boolean mayNeedPadOrTruncate = false;
      if (SqlTypeUtil.inCharOrBinaryFamilies(lhsType) && !SqlTypeUtil.isLob(lhsType)) {
        mayNeedPadOrTruncate = true;
      }
      if (mayNeedPadOrTruncate) {
        // check overflow if it is datetime.
        // TODO: should check it at the run time.
        // so, it should be in the
        // cast(SqlDateTimeWithTZ, int precision);
        if ((rhsType != null) && (rhsType.getSqlTypeName() != null)) {
          SqlTypeName typeName = rhsType.getSqlTypeName();
          int precision = 0;
          switch (typeName) {
            case DATE:
              precision = 10;
              break;
            case TIME:
              precision = 8;
              break;
            case TIMESTAMP:
              precision = 19;
              break;
          }
          if ((precision != 0) && (precision > lhsType.getPrecision())) {
            addStatement(
                new IfStatement(
                    new BinaryExpression(
                        Literal.makeLiteral(precision),
                        BinaryExpression.GREATER,
                        Literal.makeLiteral(lhsType.getPrecision())),
                    getThrowStmtList()));
          }
        }
        if ((rhsType != null)
            && (rhsType.getFamily() == lhsType.getFamily())
            && !SqlTypeUtil.isLob(rhsType)) {
          // we may be able to skip pad/truncate based on
          // known facts about source and target precisions
          if (SqlTypeUtil.isBoundedVariableWidth(lhsType)) {
            if (lhsType.getPrecision() >= rhsType.getPrecision()) {
              // target precision is greater than source
              // precision, so truncation is impossible
              // and we can skip adjustment
              return lhsExp;
            }
          } else {
            if ((lhsType.getPrecision() == rhsType.getPrecision())
                && !SqlTypeUtil.isBoundedVariableWidth(rhsType)) {
              // source and target are both fixed-width, and
              // precisions are the same, so there's no adjustment
              // needed
              return lhsExp;
            }
          }
        }

        // determine target precision
        Expression precisionExp = Literal.makeLiteral(lhsType.getPrecision());

        // need to pad only for fixed width
        Expression needPadExp = Literal.makeLiteral(!SqlTypeUtil.isBoundedVariableWidth(lhsType));

        // pad character is 0 for binary, space for character
        Expression padByteExp;
        if (!SqlTypeUtil.inCharFamily(lhsType)) {
          padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(0));
        } else {
          padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(' '));
        }

        // generate the call to do the job
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp,
                    BytePointer.ENFORCE_PRECISION_METHOD_NAME,
                    new ExpressionList(precisionExp, needPadExp, padByteExp))));
      }

      return lhsExp;
    }
コード例 #12
0
ファイル: IterCalcRel.java プロジェクト: Jach/luciddb
  /**
   * 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;
  }