/** * Creates a relational expression which projects an array of expressions, and optionally * optimizes. * * <p>The result may not be a {@link ProjectRel}. If the projection is trivial, <code>child</code> * is returned directly; and future versions may return other formulations of expressions, such as * {@link CalcRel}. * * @param child input relational expression * @param exprs list of expressions for the input columns * @param fieldNames aliases of the expressions, or null to generate * @param optimize Whether to return <code>child</code> unchanged if the projections are trivial. */ public static RelNode createProject( RelNode child, List<RexNode> exprs, List<String> fieldNames, boolean optimize) { final RelOptCluster cluster = child.getCluster(); final RexProgram program = RexProgram.create(child.getRowType(), exprs, null, fieldNames, cluster.getRexBuilder()); final List<RelCollation> collationList = program.getCollations(child.getCollationList()); if (DeprecateProjectAndFilter) { return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, collationList); } else { final RelDataType rowType = RexUtil.createStructType(cluster.getTypeFactory(), exprs, fieldNames); if (optimize && RemoveTrivialProjectRule.isIdentity(exprs, rowType, child.getRowType())) { return child; } return new ProjectRel( cluster, cluster.traitSetOf( collationList.isEmpty() ? RelCollationImpl.EMPTY : collationList.get(0)), child, exprs, rowType, ProjectRelBase.Flags.Boxed); } }
/** * Creates a relational expression which projects an array of expressions, and optionally * optimizes. * * <p>The result may not be a {@link ProjectRel}. If the projection is trivial, <code>child</code> * is returned directly; and future versions may return other formulations of expressions, such as * {@link CalcRel}. * * @param child input relational expression * @param exprs list of expressions for the input columns * @param fieldNames aliases of the expressions, or null to generate * @param optimize Whether to return <code>child</code> unchanged if the projections are trivial. */ public static RelNode createProject( RelNode child, List<RexNode> exprs, List<String> fieldNames, boolean optimize) { final RelOptCluster cluster = child.getCluster(); final RexProgram program = RexProgram.create(child.getRowType(), exprs, null, fieldNames, cluster.getRexBuilder()); final List<RelCollation> collationList = program.getCollations(child.getCollationList()); if (DEPRECATE_PROJECT_AND_FILTER) { return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, collationList); } else { final RelDataType rowType = RexUtil.createStructType( cluster.getTypeFactory(), exprs, fieldNames == null ? null : SqlValidatorUtil.uniquify(fieldNames, SqlValidatorUtil.F_SUGGESTER)); if (optimize && RemoveTrivialProjectRule.isIdentity(exprs, rowType, child.getRowType())) { return child; } return new ProjectRel( cluster, cluster.traitSetOf( collationList.isEmpty() ? RelCollationImpl.EMPTY : collationList.get(0)), child, exprs, rowType, ProjectRelBase.Flags.BOXED); } }
private Expression translate0(RexNode expr) { if (expr instanceof RexInputRef) { // TODO: multiple inputs, e.g. joins final Expression input = getInput(0); final int index = ((RexInputRef) expr).getIndex(); final List<RelDataTypeField> fields = program.getInputRowType().getFieldList(); final RelDataTypeField field = fields.get(index); if (fields.size() == 1) { return input; } else if (input.getType() == Object[].class) { return Expressions.convert_( Expressions.arrayIndex(input, Expressions.constant(field.getIndex())), Types.box(JavaRules.EnumUtil.javaClass(typeFactory, field.getType()))); } else { return Expressions.field(input, field.getName()); } } if (expr instanceof RexLocalRef) { return translate(program.getExprList().get(((RexLocalRef) expr).getIndex())); } if (expr instanceof RexLiteral) { return Expressions.constant( ((RexLiteral) expr).getValue(), typeFactory.getJavaClass(expr.getType())); } if (expr instanceof RexCall) { final RexCall call = (RexCall) expr; final SqlOperator operator = call.getOperator(); final ExpressionType expressionType = SQL_TO_LINQ_OPERATOR_MAP.get(operator); if (expressionType != null) { switch (operator.getSyntax()) { case Binary: return Expressions.makeBinary( expressionType, translate(call.getOperands()[0]), translate(call.getOperands()[1])); case Postfix: case Prefix: return Expressions.makeUnary(expressionType, translate(call.getOperands()[0])); default: throw new RuntimeException("unknown syntax " + operator.getSyntax()); } } Method method = SQL_OP_TO_JAVA_METHOD_MAP.get(operator); if (method != null) { List<Expression> exprs = translateList(Arrays.asList(call.operands)); return !Modifier.isStatic(method.getModifiers()) ? Expressions.call(exprs.get(0), method, exprs.subList(1, exprs.size())) : Expressions.call(method, exprs); } switch (expr.getKind()) { default: throw new RuntimeException("cannot translate expression " + expr); } } throw new RuntimeException("cannot translate expression " + expr); }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { CalcRel calc = call.rel(0); RexProgram program = calc.getProgram(); if (!program.isTrivial()) { return; } RelNode child = calc.getInput(0); child = call.getPlanner().register(child, calc); call.transformTo(convert(child, calc.getTraitSet())); }
/** * 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)); }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { CalcRel calcRel = (CalcRel) call.rels[0]; RexProgram program = calcRel.getProgram(); // check the projection List<Integer> projOrdinals = new ArrayList<Integer>(); RelDataType outputRowType = isProjectSimple(calcRel, projOrdinals); if (outputRowType == null) { return; } RexLocalRef condition = program.getCondition(); CompOperatorEnum compOp = CompOperatorEnum.COMP_NOOP; Integer[] filterOrdinals = {}; List<RexLiteral> filterLiterals = new ArrayList<RexLiteral>(); // check the condition if (condition != null) { RexNode filterExprs = program.expandLocalRef(condition); List<Integer> filterList = new ArrayList<Integer>(); List<CompOperatorEnum> op = new ArrayList<CompOperatorEnum>(); if (!isConditionSimple(calcRel, filterExprs, filterList, filterLiterals, op)) { return; } compOp = op.get(0); filterOrdinals = filterList.toArray(new Integer[filterList.size()]); } RelNode fennelInput = mergeTraitsAndConvert( calcRel.getTraits(), FennelRel.FENNEL_EXEC_CONVENTION, calcRel.getChild()); if (fennelInput == null) { return; } Integer[] projection = projOrdinals.toArray(new Integer[projOrdinals.size()]); FennelReshapeRel reshapeRel = new FennelReshapeRel( calcRel.getCluster(), fennelInput, projection, outputRowType, compOp, filterOrdinals, filterLiterals, new FennelRelParamId[] {}, new Integer[] {}, null); call.transformTo(reshapeRel); }
/** * Creates a relational expression which permutes the output fields of a relational expression * according to a permutation. * * <p>Optimizations: * * <ul> * <li>If the relational expression is a {@link CalcRel} or {@link ProjectRel} which is already * acting as a permutation, combines the new permutation with the old; * <li>If the permutation is the identity, returns the original relational expression. * </ul> * * <p>If a permutation is combined with its inverse, these optimizations would combine to remove * them both. * * @param rel Relational expression * @param permutation Permutation to apply to fields * @param fieldNames Field names; if null, or if a particular entry is null, the name of the * permuted field is used * @return relational expression which permutes its input fields */ public static RelNode permute(RelNode rel, Permutation permutation, List<String> fieldNames) { if (permutation.isIdentity()) { return rel; } if (rel instanceof CalcRel) { CalcRel calcRel = (CalcRel) rel; Permutation permutation1 = calcRel.getProgram().getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } if (rel instanceof ProjectRel) { Permutation permutation1 = ((ProjectRel) rel).getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } final List<RelDataType> outputTypeList = new ArrayList<RelDataType>(); final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<RexLocalRef> projectRefList = new ArrayList<RexLocalRef>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); for (int i = 0; i < permutation.getTargetCount(); i++) { int target = permutation.getTarget(i); final RelDataTypeField targetField = fields.get(target); outputTypeList.add(targetField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? targetField.getName() : fieldNames.get(i)); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(fields.get(i).getType(), i)); final int source = permutation.getSource(i); projectRefList.add(new RexLocalRef(source, fields.get(source).getType())); } final RexProgram program = new RexProgram( rel.getRowType(), exprList, projectRefList, null, rel.getCluster().getTypeFactory().createStructType(outputTypeList, outputNameList)); return new CalcRel( rel.getCluster(), rel.getTraitSet(), rel, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); }
/** * Translates a {@link RexProgram} to a sequence of expressions and declarations. * * @param inputs Variables holding the current record of each input relational expression * @param program Program to be translated * @return Sequence of expressions, optional condition */ public static List<Expression> translateProjects( List<Expression> inputs, RexProgram program, JavaTypeFactory typeFactory, List<Statement> list) { return new RexToLixTranslator(program, typeFactory, inputs) .translate(list, program.getProjectList()); }
public IterCalcRel( RelOptCluster cluster, RelNode child, RexProgram program, int flags, String tag) { super(cluster, new RelTraitSet(CallingConvention.ITERATOR), child); this.flags = flags; this.program = program; this.rowType = program.getOutputRowType(); this.tag = tag; }
/** * Determines if a projection is simple. * * @param calcRel CalcRel containing the projection * @param projOrdinals if the projection is simple, returns the ordinals of the projection inputs * @return rowtype corresponding to the projection, provided it is simple; otherwise null is * returned */ private RelDataType isProjectSimple(CalcRel calcRel, List<Integer> projOrdinals) { // Loop through projection expressions. If we find a non-simple // projection expression, simply return. RexProgram program = calcRel.getProgram(); List<RexLocalRef> projList = program.getProjectList(); int nProjExprs = projList.size(); RelDataType[] types = new RelDataType[nProjExprs]; String[] fieldNames = new String[nProjExprs]; RelDataTypeField[] projFields = calcRel.getRowType().getFields(); for (int i = 0; i < nProjExprs; i++) { RexNode projExpr = program.expandLocalRef(projList.get(i)); if (projExpr instanceof RexInputRef) { projOrdinals.add(((RexInputRef) projExpr).getIndex()); types[i] = projExpr.getType(); fieldNames[i] = projFields[i].getName(); continue; } else if (!(projExpr instanceof RexCall)) { return null; } RexCall rexCall = (RexCall) projExpr; if (rexCall.getOperator() != SqlStdOperatorTable.castFunc) { return null; } RexNode castOperand = rexCall.getOperands()[0]; if (!(castOperand instanceof RexInputRef)) { return null; } RelDataType castType = projExpr.getType(); RelDataType origType = castOperand.getType(); if (isCastSimple(origType, castType)) { projOrdinals.add(((RexInputRef) castOperand).getIndex()); types[i] = castType; fieldNames[i] = projFields[i].getName(); } else { return null; } } // return the rowtype corresponding to the output of the projection return calcRel.getCluster().getTypeFactory().createStructType(types, fieldNames); }
/** * Creates a relational expression which filters according to a given condition, returning the * same fields as its input. * * @param child Child relational expression * @param condition Condition * @return Relational expression */ public static RelNode createFilter(RelNode child, RexNode condition) { if (DeprecateProjectAndFilter) { final RelOptCluster cluster = child.getCluster(); RexProgramBuilder builder = new RexProgramBuilder(child.getRowType(), cluster.getRexBuilder()); builder.addIdentity(); builder.addCondition(condition); final RexProgram program = builder.getProgram(); return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); } else { return new FilterRel(child.getCluster(), child, condition); } }
/** * Determines whether it is possible to implement a set of expressions in Java. * * @param program Program to translate * @return whether all expressions in the program can be implemented */ public boolean canTranslate(RelNode rel, RexProgram program) { RexToOJTranslator translator = newTranslator(rel); TranslationTester tester = new TranslationTester(translator, true); final List<RexNode> exprList = program.getExprList(); for (RexNode expr : exprList) { if (!tester.canTranslate(expr)) { return false; } } return true; }
public static Expression translateCondition( List<Expression> inputs, RexProgram program, JavaTypeFactory typeFactory, List<Statement> list) { List<Expression> x = new RexToLixTranslator(program, typeFactory, inputs) .translate(list, Collections.singletonList(program.getCondition())); assert x.size() == 1; return x.get(0); }
/** * Creates a relational expression which projects the output fields of a relational expression * according to a partial mapping. * * <p>A partial mapping is weaker than a permutation: every target has one source, but a source * may have 0, 1 or more than one targets. Usually the result will have fewer fields than the * source, unless some source fields are projected multiple times. * * <p>This method could optimize the result as {@link #permute} does, but does not at present. * * @param rel Relational expression * @param mapping Mapping from source fields to target fields. The mapping type must obey the * constaints {@link MappingType#isMandatorySource()} and {@link * MappingType#isSingleSource()}, as does {@link MappingType#InverseFunction}. * @param fieldNames Field names; if null, or if a particular entry is null, the name of the * permuted field is used * @return relational expression which projects a subset of the input fields */ public static RelNode projectMapping(RelNode rel, Mapping mapping, List<String> fieldNames) { assert mapping.getMappingType().isSingleSource(); assert mapping.getMappingType().isMandatorySource(); if (mapping.isIdentity()) { return rel; } final List<RelDataType> outputTypeList = new ArrayList<RelDataType>(); final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<RexLocalRef> projectRefList = new ArrayList<RexLocalRef>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); for (int i = 0; i < fields.size(); i++) { final RelDataTypeField field = fields.get(i); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(field.getType(), i)); } for (int i = 0; i < mapping.getTargetCount(); i++) { int source = mapping.getSource(i); final RelDataTypeField sourceField = fields.get(source); outputTypeList.add(sourceField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? sourceField.getName() : fieldNames.get(i)); projectRefList.add(new RexLocalRef(source, sourceField.getType())); } final RexProgram program = new RexProgram( rel.getRowType(), exprList, projectRefList, null, rel.getCluster().getTypeFactory().createStructType(outputTypeList, outputNameList)); return new CalcRel( rel.getCluster(), rel.getTraitSet(), rel, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); }
public void onMatch(RelOptRuleCall call) { CalcRel calc = (CalcRel) call.getRels()[0]; RexProgram program = calc.getProgram(); final List<RexNode> exprList = program.getExprList(); // Form a list of expressions with sub-expressions fully // expanded. final List<RexNode> expandedExprList = new ArrayList<RexNode>(exprList.size()); final RexShuttle shuttle = new RexShuttle() { public RexNode visitLocalRef(RexLocalRef localRef) { return expandedExprList.get(localRef.getIndex()); } }; for (RexNode expr : exprList) { expandedExprList.add(expr.accept(shuttle)); } if (reduceExpressions(calc, expandedExprList)) { final RexProgramBuilder builder = new RexProgramBuilder( calc.getChild().getRowType(), calc.getCluster().getRexBuilder()); List<RexLocalRef> list = new ArrayList<RexLocalRef>(); for (RexNode expr : expandedExprList) { list.add(builder.registerInput(expr)); } if (program.getCondition() != null) { final int conditionIndex = program.getCondition().getIndex(); final RexNode newConditionExp = expandedExprList.get(conditionIndex); if (newConditionExp.isAlwaysTrue()) { // condition is always TRUE - drop it } else if ((newConditionExp instanceof RexLiteral) || RexUtil.isNullLiteral(newConditionExp, true)) { // condition is always NULL or FALSE - replace calc // with empty call.transformTo(new EmptyRel(calc.getCluster(), calc.getRowType())); return; } else { builder.addCondition(list.get(conditionIndex)); } } int k = 0; for (RexLocalRef projectExpr : program.getProjectList()) { final int index = projectExpr.getIndex(); builder.addProject( list.get(index).getIndex(), program.getOutputRowType().getFieldList().get(k++).getName()); } call.transformTo( new CalcRel( calc.getCluster(), calc.getTraits(), calc.getChild(), calc.getRowType(), builder.getProgram(), calc.getCollationList())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(calc, 0.0); } }
/** Converts an expression from {@link RexNode} to {@link SqlNode} format. */ SqlNode toSql(RexProgram program, RexNode rex) { if (rex instanceof RexLocalRef) { final int index = ((RexLocalRef) rex).getIndex(); return toSql(program, program.getExprList().get(index)); } else if (rex instanceof RexInputRef) { return field(((RexInputRef) rex).getIndex()); } else if (rex instanceof RexLiteral) { final RexLiteral literal = (RexLiteral) rex; switch (literal.getTypeName().getFamily()) { case CHARACTER: return SqlLiteral.createCharString((String) literal.getValue2(), POS); case NUMERIC: case EXACT_NUMERIC: return SqlLiteral.createExactNumeric(literal.getValue().toString(), POS); case APPROXIMATE_NUMERIC: return SqlLiteral.createApproxNumeric(literal.getValue().toString(), POS); case BOOLEAN: return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS); case DATE: return SqlLiteral.createDate((Calendar) literal.getValue(), POS); case TIME: return SqlLiteral.createTime( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case TIMESTAMP: return SqlLiteral.createTimestamp( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case ANY: switch (literal.getTypeName()) { case NULL: return SqlLiteral.createNull(POS); // fall through } default: throw new AssertionError(literal + ": " + literal.getTypeName()); } } else if (rex instanceof RexCall) { final RexCall call = (RexCall) rex; final SqlOperator op = call.getOperator(); final List<SqlNode> nodeList = toSql(program, call.getOperands()); if (op == SqlStdOperatorTable.CAST) { RelDataType type = call.getType(); if (type.getSqlTypeName() == SqlTypeName.VARCHAR && dialect.getDatabaseProduct() == SqlDialect.DatabaseProduct.MYSQL) { // MySQL doesn't have a VARCHAR type, only CHAR. nodeList.add( new SqlDataTypeSpec( new SqlIdentifier("CHAR", POS), type.getPrecision(), -1, null, null, POS)); } else { nodeList.add(toSql(type)); } } if (op == SqlStdOperatorTable.CASE) { final SqlNode valueNode; final List<SqlNode> whenList = Expressions.list(); final List<SqlNode> thenList = Expressions.list(); final SqlNode elseNode; if (nodeList.size() % 2 == 0) { // switched: // "case x when v1 then t1 when v2 then t2 ... else e end" valueNode = nodeList.get(0); for (int i = 1; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } else { // other: "case when w1 then t1 when w2 then t2 ... else e end" valueNode = null; for (int i = 0; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } elseNode = nodeList.get(nodeList.size() - 1); return op.createCall( POS, valueNode, new SqlNodeList(whenList, POS), new SqlNodeList(thenList, POS), elseNode); } if (op instanceof SqlBinaryOperator && nodeList.size() > 2) { // In RexNode trees, OR and AND have any number of children; // SqlCall requires exactly 2. So, convert to a left-deep binary tree. return createLeftCall(op, nodeList); } return op.createCall(new SqlNodeList(nodeList, POS)); } else { throw new AssertionError(rex); } }
@Override public CalcRelBase copy( RelTraitSet traitSet, RelNode child, RexProgram program, List<RelCollation> collationList) { return new CalcRel( getCluster(), traitSet, child, program.getOutputRowType(), program, collationList); }
public RelOptCost computeSelfCost(RelOptPlanner planner) { double dRows = RelMetadataQuery.getRowCount(this); double dCpu = RelMetadataQuery.getRowCount(getChild()) * program.getExprCount(); double dIo = 0; return planner.makeCost(dRows, dCpu, dIo); }
public IterCalcRel clone() { IterCalcRel clone = new IterCalcRel(getCluster(), getChild().clone(), program.copy(), getFlags(), tag); clone.inheritTraitsFrom(this); return clone; }
/** * 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; }
public void explain(RelOptPlanWriter pw) { program.explainCalc(this, pw); }