/** * Creates a JavaRelImplementor * * @param rexBuilder Builder for {@link RexNode}s * @param implementorTable Table of implementations of operators. Must not be null */ public JavaRelImplementor(RexBuilder rexBuilder, OJRexImplementorTable implementorTable) { super(rexBuilder); Util.pre(rexBuilder != null, "rexBuilder != null"); Util.pre(implementorTable != null, "implementorTable != null"); this.implementorTable = implementorTable; nextVariableId = 0; }
@Override public ParseTree visitChildInternal(RelNode child, int ordinal) { final Convention convention = child.getConvention(); if (!(child instanceof JavaRel)) { throw Util.newInternal( "Relational expression '" + child + "' has '" + convention + "' calling convention, so must implement interface " + JavaRel.class); } JavaRel javaRel = (JavaRel) child; final ParseTree p = javaRel.implement(this); if ((convention == CallingConvention.JAVA) && (p != null)) { throw Util.newInternal( "Relational expression '" + child + "' returned '" + p + " on implement, but should have " + "returned null, because it has JAVA calling-convention. " + "(Note that similar calling-conventions, such as " + "Iterator, must return a value.)"); } return p; }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link TableModificationRel}. */ public TrimResult trimFields( TableModificationRel modifier, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { // Ignore what consumer wants. We always project all columns. Util.discard(fieldsUsed); final RelDataType rowType = modifier.getRowType(); final int fieldCount = rowType.getFieldCount(); RelNode input = modifier.getChild(); // We want all fields from the child. final int inputFieldCount = input.getRowType().getFieldCount(); BitSet inputFieldsUsed = Util.bitSetBetween(0, inputFieldCount); // Create input with trimmed columns. final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); TrimResult trimResult = trimChild(modifier, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; if (!inputMapping.isIdentity()) { // We asked for all fields. Can't believe that the child decided // to permute them! throw Util.newInternal("Expected identity mapping, got " + inputMapping); } TableModificationRel newModifier = modifier; if (newInput != input) { newModifier = modifier.copy(modifier.getTraitSet(), Collections.singletonList(newInput)); } assert newModifier.getClass() == modifier.getClass(); // Always project all fields. Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(newModifier, mapping); }
/** * Trims the fields of an input relational expression. * * @param rel Relational expression * @param input Input relational expression, whose fields to trim * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected TrimResult trimChild( RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { Util.discard(rel); if (input.getClass().getName().endsWith("MedMdrClassExtentRel")) { // MedMdrJoinRule cannot handle Join of Project of // MedMdrClassExtentRel, only naked MedMdrClassExtentRel. // So, disable trimming. fieldsUsed = Util.bitSetBetween(0, input.getRowType().getFieldCount()); } return dispatchTrimFields(input, fieldsUsed, extraFields); }
/** * Creates an expression which references correlating variable <code> * correlName</code> from the context of <code>rel</code>. For example, if <code>correlName</code> * is set by the 1st child of <code>rel</code>'s 2nd child, then this method returns <code> * $input2.$input1</code>. */ public Expression makeReference(String correlName, RelNode rel) { JavaFrame frame = (JavaFrame) mapCorrel2Frame.get(correlName); assert (frame != null); assert Util.equal(frame.rel.getCorrelVariable(), correlName); assert (frame.hasVariable()); return frame.getVariable(); }
private void bindDeferred(JavaFrame frame, final RelNode rel) { final StatementList statementList = getStatementList(); if (frame.bind == null) { // this relational expression has not bound itself, so we presume // that we can call its implementSelf() method if (!(rel instanceof JavaSelfRel)) { throw Util.newInternal( "In order to bind-deferred, a " + "relational expression must implement JavaSelfRel: " + rel); } final JavaSelfRel selfRel = (JavaSelfRel) rel; LazyBind lazyBind = new LazyBind( newVariable(), statementList, getTypeFactory(), rel.getRowType(), new VariableInitializerThunk() { public VariableInitializer getInitializer() { return selfRel.implementSelf(JavaRelImplementor.this); } }); bind(rel, lazyBind); } else if ((frame.bind instanceof LazyBind) && (((LazyBind) frame.bind).statementList != statementList)) { // Frame is already bound, but to a variable declared in a different // scope. Re-bind it. final LazyBind lazyBind = (LazyBind) frame.bind; lazyBind.statementList = statementList; lazyBind.bound = false; } }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link TableFunctionRel}. */ public TrimResult trimFields( TableFunctionRel tabFun, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = tabFun.getRowType(); final int fieldCount = rowType.getFieldCount(); List<RelNode> newInputs = new ArrayList<RelNode>(); for (RelNode input : tabFun.getInputs()) { final int inputFieldCount = input.getRowType().getFieldCount(); BitSet inputFieldsUsed = Util.bitSetBetween(0, inputFieldCount); // Create input with trimmed columns. final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); TrimResult trimResult = trimChildRestore(tabFun, input, inputFieldsUsed, inputExtraFields); assert trimResult.right.isIdentity(); newInputs.add(trimResult.left); } TableFunctionRel newTabFun = tabFun; if (!tabFun.getInputs().equals(newInputs)) { newTabFun = tabFun.copy(tabFun.getTraitSet(), newInputs); } assert newTabFun.getClass() == tabFun.getClass(); // Always project all fields. Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(newTabFun, mapping); }
private Mapping createMapping(BitSet fieldsUsed, int fieldCount) { final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality()); int i = 0; for (int field : Util.toIter(fieldsUsed)) { mapping.set(field, i++); } return mapping; }
/** * Trims unused fields from a relational expression. * * <p>We presume that all fields of the relational expression are wanted by its consumer, so only * trim fields that are not used within the tree. * * @param root Root node of relational expression * @return Trimmed relational expression */ public RelNode trim(RelNode root) { final int fieldCount = root.getRowType().getFieldCount(); final BitSet fieldsUsed = Util.bitSetBetween(0, fieldCount); final Set<RelDataTypeField> extraFields = Collections.emptySet(); final TrimResult trimResult = dispatchTrimFields(root, fieldsUsed, extraFields); if (!trimResult.right.isIdentity()) { throw new IllegalArgumentException(); } return trimResult.left; }
public RelNode convertSqlToRel(String sql) { Util.pre(sql != null, "sql != null"); final SqlNode sqlQuery; try { sqlQuery = parseQuery(sql); } catch (Exception e) { throw Util.newInternal(e); // todo: better handling } final RelDataTypeFactory typeFactory = getTypeFactory(); final Prepare.CatalogReader catalogReader = createCatalogReader(typeFactory); final SqlValidator validator = createValidator(catalogReader, typeFactory); final SqlToRelConverter converter = createSqlToRelConverter(validator, catalogReader, typeFactory); converter.setTrimUnusedFields(true); final SqlNode validatedQuery = validator.validate(sqlQuery); final RelNode rel = converter.convertQuery(validatedQuery, false, true); Util.post(rel != null, "return != null"); return rel; }
/** * Records the fact that instances of <code>rel</code> are available via <code>bind</code> (which * may be eager or lazy). */ private void bind(RelNode rel, Bind bind) { tracer.log(Level.FINE, "Bind " + rel.toString() + " to " + bind); JavaFrame frame = (JavaFrame) mapRel2Frame.get(rel); frame.bind = bind; boolean stupid = SaffronProperties.instance().stupid.get(); if (stupid) { // trigger the declaration of the variable, even though it // may not be used Util.discard(bind.getVariable()); } }
/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link * org.eigenbase.rel.TableAccessRel}. */ public TrimResult trimFields( final TableAccessRelBase tableAccessRel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final int fieldCount = tableAccessRel.getRowType().getFieldCount(); if (fieldsUsed.equals(Util.bitSetBetween(0, fieldCount)) && extraFields.isEmpty()) { return trimFields((RelNode) tableAccessRel, fieldsUsed, extraFields); } final RelNode newTableAccessRel = tableAccessRel.project(fieldsUsed, extraFields); final Mapping mapping = createMapping(fieldsUsed, fieldCount); return new TrimResult(newTableAccessRel, mapping); }
private static int find(StatementList list, Statement statement) { if (statement == null) { return 0; } else { for (int i = 0, n = list.size(); i < n; i++) { if (list.get(i) == statement) { return i + 1; } } throw Util.newInternal("could not find statement " + statement + " in list " + list); } }
/** Returns whether an array of expressions has any common sub-expressions. */ public static boolean containCommonExprs(RexNode[] exprs, boolean fail) { final ExpressionNormalizer visitor = new ExpressionNormalizer(false); for (int i = 0; i < exprs.length; i++) { try { exprs[i].accept(visitor); } catch (ExpressionNormalizer.SubExprExistsException e) { Util.swallow(e, null); assert !fail; } } return false; }
public boolean matches(RelOptRuleCall call) { JoinRel join = (JoinRel) call.rels[0]; switch (join.getJoinType()) { case INNER: case LEFT: return true; case FULL: case RIGHT: return false; default: throw Util.unexpected(join.getJoinType()); } }
/** * Replaces the operands of a call. The new operands' types must match the old operands' types. */ public static RexCall replaceOperands(RexCall call, RexNode[] operands) { if (call.operands == operands) { return call; } for (int i = 0; i < operands.length; i++) { RelDataType oldType = call.operands[i].getType(); RelDataType newType = operands[i].getType(); if (!oldType.isNullable() && newType.isNullable()) { throw Util.newInternal("invalid nullability"); } assert (oldType.toString().equals(newType.toString())); } return new RexCall(call.getType(), call.getOperator(), operands); }
/** * Returns whether a given tree contains any {@link org.eigenbase.rex.RexFieldAccess} nodes. * * @param node a RexNode tree */ public static boolean containsFieldAccess(RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitFieldAccess(RexFieldAccess fieldAccess) { throw new Util.FoundOne(fieldAccess); } }; node.accept(visitor); return false; } catch (Util.FoundOne e) { Util.swallow(e, null); return true; } }
/** * Returns whether a given tree contains any {link RexInputRef} nodes. * * @param node a RexNode tree */ public static boolean containsInputRef(RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitInputRef(RexInputRef inputRef) { throw new Util.FoundOne(inputRef); } }; node.accept(visitor); return false; } catch (Util.FoundOne e) { Util.swallow(e, null); return true; } }
/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link org.eigenbase.rel.ValuesRel}. */ public TrimResult trimFields( ValuesRel values, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = values.getRowType(); final int fieldCount = rowType.getFieldCount(); // If they are asking for no fields, we can't give them what they want, // because zero-column records are illegal. Give them the last field, // which is unlikely to be a system field. if (fieldsUsed.isEmpty()) { fieldsUsed = Util.bitSetBetween(fieldCount - 1, fieldCount); } // If all fields are used, return unchanged. if (fieldsUsed.equals(Util.bitSetBetween(0, fieldCount))) { Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(values, mapping); } List<List<RexLiteral>> newTuples = new ArrayList<List<RexLiteral>>(); for (List<RexLiteral> tuple : values.getTuples()) { List<RexLiteral> newTuple = new ArrayList<RexLiteral>(); for (int field : Util.toIter(fieldsUsed)) { newTuple.add(tuple.get(field)); } newTuples.add(newTuple); } final Mapping mapping = createMapping(fieldsUsed, fieldCount); RelDataType newRowType = values .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final ValuesRel newValues = new ValuesRel(values.getCluster(), newRowType, newTuples); return new TrimResult(newValues, mapping); }
/** * Returns whether an array of expressions contains a forward reference. That is, if expression #i * contains a {@link RexInputRef} referencing field i or greater. * * @param exprs Array of expressions * @param inputRowType Input row type * @param fail Whether to assert if there is a forward reference * @return Whether there is a forward reference */ public static boolean containForwardRefs( RexNode[] exprs, RelDataType inputRowType, boolean fail) { final ForwardRefFinder visitor = new ForwardRefFinder(inputRowType); for (int i = 0; i < exprs.length; i++) { RexNode expr = exprs[i]; visitor.setLimit(i); // field cannot refer to self or later field try { expr.accept(visitor); } catch (ForwardRefFinder.IllegalForwardRefException e) { Util.swallow(e, null); assert !fail : "illegal forward reference in " + expr; return true; } } return false; }
/** * Returns whether a given node contains a RexCall with a specified operator * * @param operator to look for * @param node a RexNode tree */ public static RexCall findOperatorCall(final SqlOperator operator, RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitCall(RexCall call) { if (call.getOperator().equals(operator)) { throw new Util.FoundOne(call); } return super.visitCall(call); } }; node.accept(visitor); return null; } catch (Util.FoundOne e) { Util.swallow(e, null); return (RexCall) e.getNode(); } }
public Variable getConnectionVariable() { throw Util.needToImplement("getConnectionVariable"); }
/** * Visit method, per {@link org.eigenbase.util.ReflectiveVisitor}. * * <p>This method is invoked reflectively, so there may not be any apparent calls to it. The class * (or derived classes) may contain overloads of this method with more specific types for the * {@code rel} parameter. * * <p>Returns a pair: the relational expression created, and the mapping between the original * fields and the fields of the newly created relational expression. * * @param rel Relational expression * @param fieldsUsed Fields needed by the consumer * @return relational expression and mapping */ public TrimResult trimFields(RelNode rel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { // We don't know how to trim this kind of relational expression, so give // it back intact. Util.discard(fieldsUsed); return new TrimResult(rel, Mappings.createIdentity(rel.getRowType().getFieldCount())); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link JoinRel}. */ public TrimResult trimFields(JoinRel join, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = join.getRowType(); final int fieldCount = rowType.getFieldCount(); final RexNode conditionExpr = join.getCondition(); final int systemFieldCount = join.getSystemFieldList().size(); // Add in fields used in the condition. BitSet fieldsUsedPlus = (BitSet) fieldsUsed.clone(); final Set<RelDataTypeField> combinedInputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(fieldsUsedPlus, combinedInputExtraFields); conditionExpr.accept(inputFinder); // If no system fields are used, we can remove them. int systemFieldUsedCount = 0; for (int i = 0; i < systemFieldCount; ++i) { if (fieldsUsed.get(i)) { ++systemFieldUsedCount; } } final int newSystemFieldCount; if (systemFieldUsedCount == 0) { newSystemFieldCount = 0; } else { newSystemFieldCount = systemFieldCount; } int offset = systemFieldCount; int changeCount = 0; int newFieldCount = newSystemFieldCount; List<RelNode> newInputs = new ArrayList<RelNode>(2); List<Mapping> inputMappings = new ArrayList<Mapping>(); List<Integer> inputExtraFieldCounts = new ArrayList<Integer>(); for (RelNode input : join.getInputs()) { final RelDataType inputRowType = input.getRowType(); final int inputFieldCount = inputRowType.getFieldCount(); // Compute required mapping. BitSet inputFieldsUsed = new BitSet(inputFieldCount); for (int bit : Util.toIter(fieldsUsedPlus)) { if (bit >= offset && bit < offset + inputFieldCount) { inputFieldsUsed.set(bit - offset); } } // If there are system fields, we automatically use the // corresponding field in each input. if (newSystemFieldCount > 0) { // calling with newSystemFieldCount == 0 should be safe but hits // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222207 inputFieldsUsed.set(0, newSystemFieldCount); } // FIXME: We ought to collect extra fields for each input // individually. For now, we assume that just one input has // on-demand fields. Set<RelDataTypeField> inputExtraFields = input.getRowType().getField("_extra") == null ? Collections.<RelDataTypeField>emptySet() : combinedInputExtraFields; inputExtraFieldCounts.add(inputExtraFields.size()); TrimResult trimResult = trimChild(join, input, inputFieldsUsed, inputExtraFields); newInputs.add(trimResult.left); if (trimResult.left != input) { ++changeCount; } final Mapping inputMapping = trimResult.right; inputMappings.add(inputMapping); // Move offset to point to start of next input. offset += inputFieldCount; newFieldCount += inputMapping.getTargetCount() + inputExtraFields.size(); } Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, newFieldCount); for (int i = 0; i < newSystemFieldCount; ++i) { mapping.set(i, i); } offset = systemFieldCount; int newOffset = newSystemFieldCount; for (int i = 0; i < inputMappings.size(); i++) { Mapping inputMapping = inputMappings.get(i); for (IntPair pair : inputMapping) { mapping.set(pair.source + offset, pair.target + newOffset); } offset += inputMapping.getSourceCount(); newOffset += inputMapping.getTargetCount() + inputExtraFieldCounts.get(i); } if (changeCount == 0 && mapping.isIdentity()) { return new TrimResult(join, Mappings.createIdentity(fieldCount)); } // Build new join. final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(mapping, newInputs.get(0), newInputs.get(1)); RexNode newConditionExpr = conditionExpr.accept(shuttle); final JoinRel newJoin = join.copy(join.getTraitSet(), newConditionExpr, newInputs.get(0), newInputs.get(1)); return new TrimResult(newJoin, mapping); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link AggregateRel}. */ public TrimResult trimFields( AggregateRel aggregate, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { // Fields: // // | sys fields | group fields | agg functions | // // Two kinds of trimming: // // 1. If agg rel has system fields but none of these are used, create an // agg rel with no system fields. // // 2. If aggregate functions are not used, remove them. // // But grouping fields stay, even if they are not used. final RelDataType rowType = aggregate.getRowType(); // Compute which input fields are used. BitSet inputFieldsUsed = new BitSet(); // 1. group fields are always used for (int i : Util.toIter(aggregate.getGroupSet())) { inputFieldsUsed.set(i); } // 2. agg functions for (AggregateCall aggCall : aggregate.getAggCallList()) { for (int i : aggCall.getArgList()) { inputFieldsUsed.set(i); } } // Create input with trimmed columns. final RelNode input = aggregate.getInput(0); final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); final TrimResult trimResult = trimChild(aggregate, input, inputFieldsUsed, inputExtraFields); final RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing to do. if (input == newInput && fieldsUsed.equals(Util.bitSetBetween(0, rowType.getFieldCount()))) { return new TrimResult(aggregate, Mappings.createIdentity(rowType.getFieldCount())); } // Which agg calls are used by our consumer? final int groupCount = aggregate.getGroupSet().cardinality(); int j = groupCount; int usedAggCallCount = 0; for (int i = 0; i < aggregate.getAggCallList().size(); i++) { if (fieldsUsed.get(j++)) { ++usedAggCallCount; } } // Offset due to the number of system fields having changed. Mapping mapping = Mappings.create( MappingType.InverseSurjection, rowType.getFieldCount(), groupCount + usedAggCallCount); final BitSet newGroupSet = Mappings.apply(inputMapping, aggregate.getGroupSet()); // Populate mapping of where to find the fields. System and grouping // fields first. for (IntPair pair : inputMapping) { if (pair.source < groupCount) { mapping.set(pair.source, pair.target); } } // Now create new agg calls, and populate mapping for them. final List<AggregateCall> newAggCallList = new ArrayList<AggregateCall>(); j = groupCount; for (AggregateCall aggCall : aggregate.getAggCallList()) { if (fieldsUsed.get(j)) { AggregateCall newAggCall = new AggregateCall( aggCall.getAggregation(), aggCall.isDistinct(), Mappings.apply2(inputMapping, aggCall.getArgList()), aggCall.getType(), aggCall.getName()); if (newAggCall.equals(aggCall)) { newAggCall = aggCall; // immutable -> canonize to save space } mapping.set(j, groupCount + newAggCallList.size()); newAggCallList.add(newAggCall); } ++j; } RelNode newAggregate = new AggregateRel(aggregate.getCluster(), newInput, newGroupSet, newAggCallList); assert newAggregate.getClass() == aggregate.getClass(); return new TrimResult(newAggregate, mapping); }
/** * 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; }
SubExprExistsException(RexNode expr) { Util.discard(expr); }
/** * Creates a RelFieldTrimmer. * * @param validator Validator */ public RelFieldTrimmer(SqlValidator validator) { Util.discard(validator); // may be useful one day this.trimFieldsDispatcher = ReflectUtil.createMethodDispatcher( TrimResult.class, this, "trimFields", RelNode.class, BitSet.class, Set.class); }