Beispiel #1
0
 /**
  * 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;
 }
Beispiel #2
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;
 }
Beispiel #3
0
  /** 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);
  }
Beispiel #4
0
 /**
  * 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);
 }
Beispiel #5
0
 /**
  * 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();
 }
Beispiel #6
0
 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;
   }
 }
Beispiel #7
0
  /** 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);
  }
Beispiel #8
0
 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;
 }
Beispiel #9
0
 /**
  * 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;
 }
Beispiel #10
0
 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;
 }
Beispiel #11
0
 /**
  * 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());
   }
 }
Beispiel #12
0
 /**
  * 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);
 }
Beispiel #13
0
 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);
   }
 }
Beispiel #14
0
 /** 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;
 }
Beispiel #15
0
 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());
   }
 }
Beispiel #16
0
 /**
  * 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);
 }
Beispiel #17
0
 /**
  * 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;
   }
 }
Beispiel #18
0
 /**
  * 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;
   }
 }
Beispiel #19
0
  /**
   * 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);
  }
Beispiel #20
0
 /**
  * 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;
 }
Beispiel #21
0
 /**
  * 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();
   }
 }
Beispiel #22
0
 public Variable getConnectionVariable() {
   throw Util.needToImplement("getConnectionVariable");
 }
Beispiel #23
0
 /**
  * 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()));
 }
Beispiel #24
0
  /** 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);
  }
Beispiel #25
0
  /** 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);
  }
Beispiel #26
0
  /**
   * 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;
  }
Beispiel #27
0
 SubExprExistsException(RexNode expr) {
   Util.discard(expr);
 }
Beispiel #28
0
 /**
  * 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);
 }