예제 #1
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);
  }
예제 #2
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);
  }
예제 #3
0
파일: CalcRel.java 프로젝트: vlsi/optiq
 /**
  * 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);
   }
 }
예제 #4
0
 /**
  * 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);
   }
 }
예제 #5
0
  /**
   * Combines the inputs into a JoinRel into an array of inputs.
   *
   * @param join original join
   * @param left left input into join
   * @param right right input into join
   * @param projFieldsList returns a list of the new combined projection fields
   * @param joinFieldRefCountsList returns a list of the new combined join field reference counts
   * @return combined left and right inputs in an array
   */
  private RelNode[] combineInputs(
      JoinRel join,
      RelNode left,
      RelNode right,
      List<BitSet> projFieldsList,
      List<int[]> joinFieldRefCountsList) {
    // leave the null generating sides of an outer join intact; don't
    // pull up those children inputs into the array we're constructing
    int nInputs;
    int nInputsOnLeft;
    MultiJoinRel leftMultiJoin = null;
    JoinRelType joinType = join.getJoinType();
    boolean combineLeft = canCombine(left, joinType.generatesNullsOnLeft());
    if (combineLeft) {
      leftMultiJoin = (MultiJoinRel) left;
      nInputs = left.getInputs().length;
      nInputsOnLeft = nInputs;
    } else {
      nInputs = 1;
      nInputsOnLeft = 1;
    }
    MultiJoinRel rightMultiJoin = null;
    boolean combineRight = canCombine(right, joinType.generatesNullsOnRight());
    if (combineRight) {
      rightMultiJoin = (MultiJoinRel) right;
      nInputs += right.getInputs().length;
    } else {
      nInputs += 1;
    }

    RelNode[] newInputs = new RelNode[nInputs];
    int i = 0;
    if (combineLeft) {
      for (; i < left.getInputs().length; i++) {
        newInputs[i] = leftMultiJoin.getInput(i);
        projFieldsList.add(((MultiJoinRel) left).getProjFields()[i]);
        joinFieldRefCountsList.add(((MultiJoinRel) left).getJoinFieldRefCountsMap().get(i));
      }
    } else {
      newInputs[0] = left;
      i = 1;
      projFieldsList.add(null);
      joinFieldRefCountsList.add(new int[left.getRowType().getFieldCount()]);
    }
    if (combineRight) {
      for (; i < nInputs; i++) {
        newInputs[i] = rightMultiJoin.getInput(i - nInputsOnLeft);
        projFieldsList.add(((MultiJoinRel) right).getProjFields()[i - nInputsOnLeft]);
        joinFieldRefCountsList.add(
            ((MultiJoinRel) right).getJoinFieldRefCountsMap().get(i - nInputsOnLeft));
      }
    } else {
      newInputs[i] = right;
      projFieldsList.add(null);
      joinFieldRefCountsList.add(new int[right.getRowType().getFieldCount()]);
    }

    return newInputs;
  }
예제 #6
0
  /**
   * Combines the outer join conditions and join types from the left and right join inputs. If the
   * join itself is either a left or right outer join, then the join condition corresponding to the
   * join is also set in the position corresponding to the null-generating input into the join. The
   * join type is also set.
   *
   * @param joinRel join rel
   * @param combinedInputs the combined inputs to the join
   * @param left left child of the joinrel
   * @param right right child of the joinrel
   * @param combinedConds the array containing the combined join conditions
   * @param joinTypes the array containing the combined join types
   * @return combined join filters AND'd together
   */
  private RexNode[] combineOuterJoins(
      JoinRel joinRel,
      RelNode[] combinedInputs,
      RelNode left,
      RelNode right,
      RexNode[] combinedConds,
      JoinRelType[] joinTypes) {
    JoinRelType joinType = joinRel.getJoinType();
    int nCombinedInputs = combinedInputs.length;
    boolean leftCombined = canCombine(left, joinType.generatesNullsOnLeft());
    boolean rightCombined = canCombine(right, joinType.generatesNullsOnRight());
    if (joinType == JoinRelType.LEFT) {
      if (leftCombined) {
        copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null);
      } else {
        joinTypes[0] = JoinRelType.INNER;
      }
      combinedConds[nCombinedInputs - 1] = joinRel.getCondition();
      joinTypes[nCombinedInputs - 1] = joinType;
    } else if (joinType == JoinRelType.RIGHT) {
      if (rightCombined) {
        copyOuterJoinInfo(
            (MultiJoinRel) right,
            combinedConds,
            joinTypes,
            1,
            left.getRowType().getFieldCount(),
            right.getRowType().getFields(),
            joinRel.getRowType().getFields());
      } else {
        joinTypes[nCombinedInputs - 1] = JoinRelType.INNER;
      }
      combinedConds[0] = joinRel.getCondition();
      joinTypes[0] = joinType;
    } else {
      int nInputsLeft;
      if (leftCombined) {
        nInputsLeft = left.getInputs().length;
        copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null);
      } else {
        nInputsLeft = 1;
        joinTypes[0] = JoinRelType.INNER;
      }
      if (rightCombined) {
        copyOuterJoinInfo(
            (MultiJoinRel) right,
            combinedConds,
            joinTypes,
            nInputsLeft,
            left.getRowType().getFieldCount(),
            right.getRowType().getFields(),
            joinRel.getRowType().getFields());
      } else {
        joinTypes[nInputsLeft] = JoinRelType.INNER;
      }
    }

    return combinedConds;
  }
예제 #7
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);
 }
예제 #8
0
 /**
  * Returns the index of the first field in <code>rel</code> which comes from its <code>ordinal
  * </code>th input.
  *
  * <p>For example, if rel joins T0(A,B,C) to T1(D,E), then countFields(0,rel) yields 0, and
  * countFields(1,rel) yields 3.
  */
 private int computeFieldOffset(RelNode rel, int ordinal) {
   if (ordinal == 0) {
     // short-circuit for the common case
     return 0;
   }
   int fieldOffset = 0;
   final List<RelNode> inputs = rel.getInputs();
   for (int i = 0; i < ordinal; i++) {
     RelNode input = inputs.get(i);
     fieldOffset += input.getRowType().getFieldList().size();
   }
   return fieldOffset;
 }
예제 #9
0
  // implement RelOptRule
  public void onMatch(RelOptRuleCall call) {
    ProjectRel origProj = call.rel(0);
    JoinRel joinRel = call.rel(1);

    // locate all fields referenced in the projection and join condition;
    // determine which inputs are referenced in the projection and
    // join condition; if all fields are being referenced and there are no
    // special expressions, no point in proceeding any further
    PushProjector pushProject =
        new PushProjector(origProj, joinRel.getCondition(), joinRel, preserveExprCondition);
    if (pushProject.locateAllRefs()) {
      return;
    }

    // create left and right projections, projecting only those
    // fields referenced on each side
    RelNode leftProjRel = pushProject.createProjectRefsAndExprs(joinRel.getLeft(), true, false);
    RelNode rightProjRel = pushProject.createProjectRefsAndExprs(joinRel.getRight(), true, true);

    // convert the join condition to reference the projected columns
    RexNode newJoinFilter = null;
    int[] adjustments = pushProject.getAdjustments();
    if (joinRel.getCondition() != null) {
      List<RelDataTypeField> projJoinFieldList = new ArrayList<RelDataTypeField>();
      projJoinFieldList.addAll(joinRel.getSystemFieldList());
      projJoinFieldList.addAll(leftProjRel.getRowType().getFieldList());
      projJoinFieldList.addAll(rightProjRel.getRowType().getFieldList());
      newJoinFilter =
          pushProject.convertRefsAndExprs(joinRel.getCondition(), projJoinFieldList, adjustments);
    }

    // create a new joinrel with the projected children
    JoinRel newJoinRel =
        new JoinRel(
            joinRel.getCluster(),
            leftProjRel,
            rightProjRel,
            newJoinFilter,
            joinRel.getJoinType(),
            Collections.<String>emptySet(),
            joinRel.isSemiJoinDone(),
            joinRel.getSystemFieldList());

    // put the original project on top of the join, converting it to
    // reference the modified projection list
    ProjectRel topProject = pushProject.createNewProject(newJoinRel, adjustments);

    call.transformTo(topProject);
  }
예제 #10
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;
   }
 }
예제 #11
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;
 }
예제 #12
0
    private JavaFrame findFrame() {
      RelNode previous = rel;
      while (true) {
        JavaFrame frame = (JavaFrame) mapRel2Frame.get(previous);
        if (frame.bind != null) {
          tracer.log(
              Level.FINE,
              "Bind " + rel.toString() + " to " + previous.toString() + "(" + frame.bind + ")");
          return frame;
        }

        // go deeper
        List<RelNode> inputs = previous.getInputs();
        assert (inputs.size() == 1) : "input is not bound";
        previous = inputs.get(0);
      }
    }
예제 #13
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;
 }
예제 #14
0
 /**
  * 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);
   }
 }
예제 #15
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());
   }
 }
예제 #16
0
 /**
  * Invokes {@link #trimFields}, or the appropriate method for the type of the rel parameter, using
  * multi-method dispatch.
  *
  * @param rel Relational expression
  * @param fieldsUsed Bitmap of fields needed by the consumer
  * @return New relational expression and its field mapping
  */
 protected final TrimResult dispatchTrimFields(
     RelNode rel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
   final TrimResult trimResult = trimFieldsDispatcher.invoke(rel, fieldsUsed, extraFields);
   final RelNode newRel = trimResult.left;
   final Mapping mapping = trimResult.right;
   final int fieldCount = rel.getRowType().getFieldCount();
   assert mapping.getSourceCount() == fieldCount
       : "source: " + mapping.getSourceCount() + " != " + fieldCount;
   final int newFieldCount = newRel.getRowType().getFieldCount();
   assert mapping.getTargetCount() + extraFields.size() == newFieldCount
       : "target: "
           + mapping.getTargetCount()
           + " + "
           + extraFields.size()
           + " != "
           + newFieldCount;
   if (Bug.TodoFixed) assert newFieldCount > 0 : "rel has no fields after trim: " + rel;
   if (newRel.equals(rel)) {
     return new TrimResult(rel, mapping);
   }
   return trimResult;
 }
예제 #17
0
 /**
  * Returns the variable which, in the generated program, will hold the current row of a given
  * relational expression. This method is only applicable if the relational expression is the
  * current one or an input; if it is an ancestor, there is no current value, and this method
  * returns null.
  */
 public Variable findInputVariable(RelNode rel) {
   while (true) {
     JavaFrame frame = (JavaFrame) mapRel2Frame.get(rel);
     if ((frame != null) && frame.hasVariable()) {
       return frame.getVariable();
     }
     List<RelNode> inputs = rel.getInputs();
     if (inputs.size() == 1) {
       rel = inputs.get(0);
     } else {
       return null;
     }
   }
 }
예제 #18
0
 /**
  * Trims a child relational expression, then adds back a dummy project to restore the fields that
  * were removed.
  *
  * <p>Sounds pointless? It causes unused fields to be removed further down the tree (towards the
  * leaves), but it ensure that the consuming relational expression continues to see the same
  * fields.
  *
  * @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 trimChildRestore(
     RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
   TrimResult trimResult = trimChild(rel, input, fieldsUsed, extraFields);
   if (trimResult.right.isIdentity()) {
     return trimResult;
   }
   final RelDataType rowType = input.getRowType();
   List<RelDataTypeField> fieldList = rowType.getFieldList();
   final List<RexNode> exprList = new ArrayList<RexNode>();
   final List<String> nameList = rowType.getFieldNames();
   RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
   assert trimResult.right.getSourceCount() == fieldList.size();
   for (int i = 0; i < fieldList.size(); i++) {
     int source = trimResult.right.getTargetOpt(i);
     RelDataTypeField field = fieldList.get(i);
     exprList.add(
         source < 0
             ? rexBuilder.makeZeroLiteral(field.getType())
             : rexBuilder.makeInputRef(field.getType(), source));
   }
   RelNode project = CalcRel.createProject(trimResult.left, exprList, nameList);
   return new TrimResult(project, Mappings.createIdentity(fieldList.size()));
 }
  /**
   * Creates new RelNodes replacing/removing the original project/row scan
   *
   * @param projectedScan new scan that is now projected
   * @param origProject original projection
   * @param needRename true if fields from the row scan need to be renamed
   * @param newProject projection that contains the new projection expressions, in the case where
   *     the original projection cannot be removed because it projects expressions
   * @return new RelNode
   */
  public RelNode createNewRelNode(
      RelNode projectedScan, ProjectRel origProject, boolean needRename, ProjectRel newProject) {
    RelNode scanRel;
    if (needRename) {
      // Replace calling convention with FENNEL_EXEC_CONVENTION
      RelTraitSet traits = RelOptUtil.clone(origProject.getTraits());
      traits.setTrait(CallingConventionTraitDef.instance, FennelRel.FENNEL_EXEC_CONVENTION);
      if (!traits.equals(projectedScan.getTraits())) {
        RelNode mergedProjectedScan = convert(projectedScan, traits);
        RelOptPlanner planner = projectedScan.getCluster().getPlanner();
        // register projectedScan == mergedProjectedScan
        // so mergedProjectedScan will have a set later on
        projectedScan = planner.ensureRegistered(mergedProjectedScan, projectedScan);
      }
      scanRel =
          new FennelRenameRel(
              origProject.getCluster(),
              projectedScan,
              RelOptUtil.getFieldNames(origProject.getRowType()),
              traits);
    } else {
      scanRel = projectedScan;
    }

    if (newProject == null) {
      return scanRel;
    } else {
      // in the case where the projection had expressions, put the
      // new, modified projection on top of the projected row scan
      return (ProjectRel)
          CalcRel.createProject(
              scanRel,
              newProject.getProjectExps(),
              RelOptUtil.getFieldNames(newProject.getRowType()));
    }
  }
예제 #20
0
  /**
   * Declares a variable, and binds it lazily, so it only gets initialized if it is actually used.
   *
   * @return the Variable so declared
   */
  public Variable bind(
      RelNode rel, StatementList statementList, final VariableInitializer initializer) {
    VariableInitializerThunk thunk =
        new VariableInitializerThunk() {
          public VariableInitializer getInitializer() {
            return initializer;
          }
        };

    Variable variable = newVariable();
    LazyBind bind =
        new LazyBind(variable, statementList, getTypeFactory(), rel.getRowType(), thunk);
    bind(rel, bind);
    return bind.getVariable();
  }
예제 #21
0
  /**
   * Returns a relational expression which has the same fields as the underlying expression, but the
   * fields have different names.
   *
   * @param rel Relational expression
   * @param fieldNames Field names
   * @return Renamed relational expression
   */
  public static RelNode createRename(RelNode rel, List<String> fieldNames) {
    final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
    assert fieldNames.size() == fields.size();
    final List<Pair<RexNode, String>> refs =
        new AbstractList<Pair<RexNode, String>>() {
          public int size() {
            return fields.size();
          }

          public Pair<RexNode, String> get(int index) {
            return RexInputRef.of2(index, fields);
          }
        };
    return createProject(rel, refs, true);
  }
예제 #22
0
 /**
  * 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());
 }
예제 #23
0
  /**
   * Shifts a filter originating from the right child of the JoinRel to the right, to reflect the
   * filter now being applied on the resulting MultiJoinRel.
   *
   * @param joinRel the original JoinRel
   * @param left the left child of the JoinRel
   * @param right the right child of the JoinRel
   * @param rightFilter the filter originating from the right child
   * @return the adjusted right filter
   */
  private RexNode shiftRightFilter(
      JoinRel joinRel, RelNode left, MultiJoinRel right, RexNode rightFilter) {
    if (rightFilter == null) {
      return null;
    }

    int nFieldsOnLeft = left.getRowType().getFields().length;
    int nFieldsOnRight = right.getRowType().getFields().length;
    int[] adjustments = new int[nFieldsOnRight];
    for (int i = 0; i < nFieldsOnRight; i++) {
      adjustments[i] = nFieldsOnLeft;
    }
    rightFilter =
        rightFilter.accept(
            new RelOptUtil.RexInputConverter(
                joinRel.getCluster().getRexBuilder(),
                right.getRowType().getFields(),
                joinRel.getRowType().getFields(),
                adjustments));
    return rightFilter;
  }
예제 #24
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());
 }
예제 #25
0
  /** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link ProjectRel}. */
  public TrimResult trimFields(
      ProjectRel project, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    final RelDataType rowType = project.getRowType();
    final int fieldCount = rowType.getFieldCount();
    final RelNode input = project.getChild();
    final RelDataType inputRowType = input.getRowType();

    // Which fields are required from the input?
    BitSet inputFieldsUsed = new BitSet(inputRowType.getFieldCount());
    final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields);
    RelOptUtil.InputFinder inputFinder =
        new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields);
    for (Ord<RexNode> ord : Ord.zip(project.getProjects())) {
      if (fieldsUsed.get(ord.i)) {
        ord.e.accept(inputFinder);
      }
    }

    // Create input with trimmed columns.
    TrimResult trimResult = trimChild(project, input, inputFieldsUsed, inputExtraFields);
    RelNode newInput = trimResult.left;
    final Mapping inputMapping = trimResult.right;

    // If the input is unchanged, and we need to project all columns,
    // there's nothing we can do.
    if (newInput == input && fieldsUsed.cardinality() == fieldCount) {
      return new TrimResult(project, Mappings.createIdentity(fieldCount));
    }

    // Some parts of the system can't handle rows with zero fields, so
    // pretend that one field is used.
    if (fieldsUsed.cardinality() == 0) {
      final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, 1);
      final RexLiteral expr =
          project.getCluster().getRexBuilder().makeExactLiteral(BigDecimal.ZERO);
      RelDataType newRowType =
          project
              .getCluster()
              .getTypeFactory()
              .createStructType(
                  Collections.singletonList(expr.getType()), Collections.singletonList("DUMMY"));
      ProjectRel newProject =
          new ProjectRel(
              project.getCluster(),
              project.getCluster().traitSetOf(RelCollationImpl.EMPTY),
              newInput,
              Collections.<RexNode>singletonList(expr),
              newRowType,
              project.getFlags());
      return new TrimResult(newProject, mapping);
    }

    // Build new project expressions, and populate the mapping.
    List<RexNode> newProjectExprList = new ArrayList<RexNode>();
    final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput);
    final Mapping mapping =
        Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality());
    for (Ord<RexNode> ord : Ord.zip(project.getProjects())) {
      if (fieldsUsed.get(ord.i)) {
        mapping.set(ord.i, newProjectExprList.size());
        RexNode newProjectExpr = ord.e.accept(shuttle);
        newProjectExprList.add(newProjectExpr);
      }
    }

    final RelDataType newRowType =
        project
            .getCluster()
            .getTypeFactory()
            .createStructType(Mappings.apply3(mapping, rowType.getFieldList()));

    final List<RelCollation> newCollations =
        RexUtil.apply(inputMapping, project.getCollationList());

    final RelNode newProject;
    if (RemoveTrivialProjectRule.isIdentity(
        newProjectExprList, newRowType, newInput.getRowType())) {
      // The new project would be the identity. It is equivalent to return
      // its child.
      newProject = newInput;
    } else {
      newProject =
          new ProjectRel(
              project.getCluster(),
              project
                  .getCluster()
                  .traitSetOf(
                      newCollations.isEmpty() ? RelCollationImpl.EMPTY : newCollations.get(0)),
              newInput,
              newProjectExprList,
              newRowType,
              project.getFlags());
      assert newProject.getClass() == project.getClass();
    }
    return new TrimResult(newProject, mapping);
  }
예제 #26
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()));
 }
 static FarragoSession getSession(RelNode rel) {
   FarragoSessionPlanner planner = (FarragoSessionPlanner) rel.getCluster().getPlanner();
   FarragoSessionPreparingStmt preparingStmt = planner.getPreparingStmt();
   return preparingStmt.getSession();
 }
예제 #28
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);
  }
예제 #29
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);
  }
  /**
   * Reduces a list of expressions.
   *
   * @param rel Relational expression
   * @param expList List of expressions, modified in place
   * @return whether reduction found something to change, and succeeded
   */
  static boolean reduceExpressions(RelNode rel, List<RexNode> expList) {
    RexBuilder rexBuilder = rel.getCluster().getRexBuilder();

    // Find reducible expressions.
    FarragoSessionPlanner planner = (FarragoSessionPlanner) rel.getCluster().getPlanner();
    FarragoSessionPreparingStmt preparingStmt = planner.getPreparingStmt();
    List<RexNode> constExps = new ArrayList<RexNode>();
    List<Boolean> addCasts = new ArrayList<Boolean>();
    List<RexNode> removableCasts = new ArrayList<RexNode>();
    findReducibleExps(preparingStmt, expList, constExps, addCasts, removableCasts);
    if (constExps.isEmpty() && removableCasts.isEmpty()) {
      return false;
    }

    // Remove redundant casts before reducing constant expressions.
    // If the argument to the redundant cast is a reducible constant,
    // reducing that argument to a constant first will result in not being
    // able to locate the original cast expression.
    if (!removableCasts.isEmpty()) {
      List<RexNode> reducedExprs = new ArrayList<RexNode>();
      List<Boolean> noCasts = new ArrayList<Boolean>();
      for (RexNode exp : removableCasts) {
        RexCall call = (RexCall) exp;
        reducedExprs.add(call.getOperands()[0]);
        noCasts.add(false);
      }
      RexReplacer replacer = new RexReplacer(rexBuilder, removableCasts, reducedExprs, noCasts);
      replacer.apply(expList);
    }

    if (constExps.isEmpty()) {
      return true;
    }

    // Compute the values they reduce to.
    List<RexNode> reducedValues = new ArrayList<RexNode>();
    ReentrantValuesStmt reentrantStmt =
        new ReentrantValuesStmt(
            preparingStmt.getRootStmtContext(), rexBuilder, constExps, reducedValues);
    FarragoSession session = getSession(rel);
    reentrantStmt.execute(session, true);
    if (reentrantStmt.failed) {
      return false;
    }

    // For ProjectRel, we have to be sure to preserve the result
    // types, so always cast regardless of the expression type.
    // For other RelNodes like FilterRel, in general, this isn't necessary,
    // and the presence of casts could hinder other rules such as sarg
    // analysis, which require bare literals.  But there are special cases,
    // like when the expression is a UDR argument, that need to be
    // handled as special cases.
    if (rel instanceof ProjectRel) {
      for (int i = 0; i < reducedValues.size(); i++) {
        addCasts.set(i, true);
      }
    }

    RexReplacer replacer = new RexReplacer(rexBuilder, constExps, reducedValues, addCasts);
    replacer.apply(expList);
    return true;
  }