예제 #1
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);
   }
 }
예제 #2
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);
  }
예제 #3
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);
   }
 }
예제 #4
0
 /**
  * Adds a child to this expression.
  *
  * @param child child to add
  */
 public void addChild(SargExpr child) {
   assert (child.getDataType() == dataType);
   if (setOp == SargSetOperator.COMPLEMENT) {
     assert (children.isEmpty());
   }
   children.add(child);
 }
    private void analyzeCall(RexCall call, Constancy callConstancy) {
      parentCallTypeStack.add(call.getOperator());

      // visit operands, pushing their states onto stack
      super.visitCall(call);

      // look for NON_CONSTANT operands
      int nOperands = call.getOperands().length;
      List<Constancy> operandStack = stack.subList(stack.size() - nOperands, stack.size());
      for (Constancy operandConstancy : operandStack) {
        if (operandConstancy == Constancy.NON_CONSTANT) {
          callConstancy = Constancy.NON_CONSTANT;
        }
      }

      // Even if all operands are constant, the call itself may
      // be non-deterministic.
      if (!call.getOperator().isDeterministic()) {
        callConstancy = Constancy.NON_CONSTANT;
      } else if (call.getOperator().isDynamicFunction()) {
        // We can reduce the call to a constant, but we can't
        // cache the plan if the function is dynamic
        preparingStmt.disableStatementCaching();
      }

      // Row operator itself can't be reduced to a literal, but if
      // the operands are constants, we still want to reduce those
      if ((callConstancy == Constancy.REDUCIBLE_CONSTANT)
          && (call.getOperator() instanceof SqlRowOperator)) {
        callConstancy = Constancy.NON_CONSTANT;
      }

      if (callConstancy == Constancy.NON_CONSTANT) {
        // any REDUCIBLE_CONSTANT children are now known to be maximal
        // reducible subtrees, so they can be added to the result
        // list
        for (int iOperand = 0; iOperand < nOperands; ++iOperand) {
          Constancy constancy = operandStack.get(iOperand);
          if (constancy == Constancy.REDUCIBLE_CONSTANT) {
            addResult(call.getOperands()[iOperand]);
          }
        }

        // if this cast expression can't be reduced to a literal,
        // then see if we can remove the cast
        if (call.getOperator() == SqlStdOperatorTable.castFunc) {
          reduceCasts(call);
        }
      }

      // pop operands off of the stack
      operandStack.clear();

      // pop this parent call operator off the stack
      parentCallTypeStack.remove(parentCallTypeStack.size() - 1);

      // push constancy result for this call onto stack
      stack.add(callConstancy);
    }
예제 #6
0
파일: RexUtil.java 프로젝트: kunlqt/optiq
 /**
  * Applies a visitor to a list of expressions and, if specified, a single expression.
  *
  * @param visitor Visitor
  * @param exprs List of expressions
  * @param expr Single expression, may be null
  */
 public static void apply(RexVisitor<Void> visitor, List<? extends RexNode> exprs, RexNode expr) {
   for (int i = 0; i < exprs.size(); i++) {
     exprs.get(i).accept(visitor);
   }
   if (expr != null) {
     expr.accept(visitor);
   }
 }
예제 #7
0
파일: RexUtil.java 프로젝트: kunlqt/optiq
 /**
  * Applies a mapping to a list of field collations.
  *
  * @param mapping Mapping
  * @param fieldCollations Field collations
  * @return collations with mapping applied
  */
 public static List<RelFieldCollation> apply(
     Mapping mapping, List<RelFieldCollation> fieldCollations) {
   final List<RelFieldCollation> newFieldCollations =
       new ArrayList<RelFieldCollation>(fieldCollations.size());
   for (RelFieldCollation fieldCollation : fieldCollations) {
     newFieldCollations.add(apply(mapping, fieldCollation));
   }
   return newFieldCollations;
 }
 public static Mapping target(List<Integer> sources, int sourceCount) {
   final int targetCount = sources.size();
   final PartialFunctionImpl mapping =
       new PartialFunctionImpl(sourceCount, targetCount, MappingType.FUNCTION);
   for (int target = 0; target < targetCount; target++) {
     int source = sources.get(target);
     mapping.set(source, target);
   }
   return mapping;
 }
 public static Mapping source(List<Integer> targets, int targetCount) {
   final int sourceCount = targets.size();
   final PartialFunctionImpl mapping =
       new PartialFunctionImpl(sourceCount, targetCount, MappingType.FUNCTION);
   for (int source = 0; source < sourceCount; source++) {
     int target = targets.get(source);
     mapping.set(source, target);
   }
   return mapping;
 }
예제 #10
0
  private List<SargIntervalSequence> evaluateChildren(SargSetExpr setExpr) {
    List<SargIntervalSequence> list = new ArrayList<SargIntervalSequence>();

    for (SargExpr child : setExpr.children) {
      SargIntervalSequence newSeq = child.evaluate();
      list.add(newSeq);
    }

    return list;
  }
        public void onMatch(RelOptRuleCall call) {
          CalcRel calc = (CalcRel) call.getRels()[0];
          RexProgram program = calc.getProgram();
          final List<RexNode> exprList = program.getExprList();

          // Form a list of expressions with sub-expressions fully
          // expanded.
          final List<RexNode> expandedExprList = new ArrayList<RexNode>(exprList.size());
          final RexShuttle shuttle =
              new RexShuttle() {
                public RexNode visitLocalRef(RexLocalRef localRef) {
                  return expandedExprList.get(localRef.getIndex());
                }
              };
          for (RexNode expr : exprList) {
            expandedExprList.add(expr.accept(shuttle));
          }
          if (reduceExpressions(calc, expandedExprList)) {
            final RexProgramBuilder builder =
                new RexProgramBuilder(
                    calc.getChild().getRowType(), calc.getCluster().getRexBuilder());
            List<RexLocalRef> list = new ArrayList<RexLocalRef>();
            for (RexNode expr : expandedExprList) {
              list.add(builder.registerInput(expr));
            }
            if (program.getCondition() != null) {
              final int conditionIndex = program.getCondition().getIndex();
              final RexNode newConditionExp = expandedExprList.get(conditionIndex);
              if (newConditionExp.isAlwaysTrue()) {
                // condition is always TRUE - drop it
              } else if ((newConditionExp instanceof RexLiteral)
                  || RexUtil.isNullLiteral(newConditionExp, true)) {
                // condition is always NULL or FALSE - replace calc
                // with empty
                call.transformTo(new EmptyRel(calc.getCluster(), calc.getRowType()));
                return;
              } else {
                builder.addCondition(list.get(conditionIndex));
              }
            }
            int k = 0;
            for (RexLocalRef projectExpr : program.getProjectList()) {
              final int index = projectExpr.getIndex();
              builder.addProject(
                  list.get(index).getIndex(),
                  program.getOutputRowType().getFieldList().get(k++).getName());
            }
            call.transformTo(
                new CalcRel(
                    calc.getCluster(),
                    calc.getTraits(),
                    calc.getChild(),
                    calc.getRowType(),
                    builder.getProgram(),
                    calc.getCollationList()));

            // New plan is absolutely better than old plan.
            call.getPlanner().setImportance(calc, 0.0);
          }
        }
예제 #12
0
  /**
   * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link SetOpRel} (including UNION and
   * UNION ALL).
   */
  public TrimResult trimFields(
      SetOpRel setOp, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    final RelDataType rowType = setOp.getRowType();
    final int fieldCount = rowType.getFieldCount();
    int changeCount = 0;

    // Fennel abhors an empty row type, so pretend that the parent rel
    // wants the last field. (The last field is the least likely to be a
    // system field.)
    if (fieldsUsed.isEmpty()) {
      fieldsUsed.set(rowType.getFieldCount() - 1);
    }

    // Compute the desired field mapping. Give the consumer the fields they
    // want, in the order that they appear in the bitset.
    final Mapping mapping = createMapping(fieldsUsed, fieldCount);

    // Create input with trimmed columns.
    final List<RelNode> newInputs = new ArrayList<RelNode>();
    for (RelNode input : setOp.getInputs()) {
      TrimResult trimResult = trimChild(setOp, input, fieldsUsed, extraFields);
      RelNode newInput = trimResult.left;
      final Mapping inputMapping = trimResult.right;

      // We want "mapping", the input gave us "inputMapping", compute
      // "remaining" mapping.
      //    |                   |                |
      //    |---------------- mapping ---------->|
      //    |-- inputMapping -->|                |
      //    |                   |-- remaining -->|
      //
      // For instance, suppose we have columns [a, b, c, d],
      // the consumer asked for mapping = [b, d],
      // and the transformed input has columns inputMapping = [d, a, b].
      // remaining will permute [b, d] to [d, a, b].
      Mapping remaining = Mappings.divide(mapping, inputMapping);

      // Create a projection; does nothing if remaining is identity.
      newInput = CalcRel.projectMapping(newInput, remaining, null);

      if (input != newInput) {
        ++changeCount;
      }
      newInputs.add(newInput);
    }

    // If the input is unchanged, and we need to project all columns,
    // there's to do.
    if (changeCount == 0 && mapping.isIdentity()) {
      return new TrimResult(setOp, mapping);
    }

    RelNode newSetOp = setOp.copy(setOp.getTraitSet(), newInputs);
    return new TrimResult(newSetOp, mapping);
  }
예제 #13
0
파일: RexUtil.java 프로젝트: kunlqt/optiq
  /**
   * Creates an OR expression from a list of RexNodes
   *
   * @param rexList list of RexNodes
   * @return OR'd expression
   */
  public static RexNode orRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) {
    if (rexList.isEmpty()) {
      return null;
    }

    RexNode orExpr = rexList.get(rexList.size() - 1);
    for (int i = rexList.size() - 2; i >= 0; i--) {
      orExpr = rexBuilder.makeCall(SqlStdOperatorTable.orOperator, rexList.get(i), orExpr);
    }
    return orExpr;
  }
예제 #14
0
 private List<Object> explainInputs(List<RelNode> inputs) {
   final List<Object> list = jsonBuilder.list();
   for (RelNode input : inputs) {
     String id = relIdMap.get(input);
     if (id == null) {
       input.explain(this);
       id = previousId;
     }
     list.add(id);
   }
   return list;
 }
 /** Returns whether a list of integers is the identity mapping [0, ..., n - 1]. */
 public static boolean isIdentity(List<Integer> list, int count) {
   if (list.size() != count) {
     return false;
   }
   for (int i = 0; i < count; i++) {
     final Integer o = list.get(i);
     if (o == null || o != i) {
       return false;
     }
   }
   return true;
 }
예제 #16
0
파일: RexUtil.java 프로젝트: kunlqt/optiq
  /**
   * Creates an AND expression from a list of RexNodes
   *
   * @param rexList list of RexNodes
   * @return AND'd expression
   */
  public static RexNode andRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) {
    if (rexList.isEmpty()) {
      return null;
    }

    // create a right-deep tree to allow short-circuiting during
    // expression evaluation
    RexNode andExpr = rexList.get(rexList.size() - 1);
    for (int i = rexList.size() - 2; i >= 0; i--) {
      andExpr = rexBuilder.makeCall(SqlStdOperatorTable.andOperator, rexList.get(i), andExpr);
    }
    return andExpr;
  }
예제 #17
0
 /**
  * Derives the list of column names suitable for NATURAL JOIN. These are the columns that occur
  * exactly once on each side of the join.
  *
  * @param leftRowType Row type of left input to the join
  * @param rightRowType Row type of right input to the join
  * @return List of columns that occur once on each side
  */
 public static List<String> deriveNaturalJoinColumnList(
     RelDataType leftRowType, RelDataType rightRowType) {
   List<String> naturalColumnNames = new ArrayList<String>();
   final List<String> leftNames = leftRowType.getFieldNames();
   final List<String> rightNames = rightRowType.getFieldNames();
   for (String name : leftNames) {
     if ((Collections.frequency(leftNames, name) == 1)
         && (Collections.frequency(rightNames, name) == 1)) {
       naturalColumnNames.add(name);
     }
   }
   return naturalColumnNames;
 }
 /**
  * Locates expressions that can be reduced to literals or converted to expressions with redundant
  * casts removed.
  *
  * @param preparingStmt the statement containing the expressions
  * @param exps list of candidate expressions to be examined for reduction
  * @param constExps returns the list of expressions that can be constant reduced
  * @param addCasts indicator for each expression that can be constant reduced, whether a cast of
  *     the resulting reduced expression is potentially necessary
  * @param removableCasts returns the list of cast expressions where the cast can be removed
  */
 private static void findReducibleExps(
     FarragoSessionPreparingStmt preparingStmt,
     List<RexNode> exps,
     List<RexNode> constExps,
     List<Boolean> addCasts,
     List<RexNode> removableCasts) {
   ReducibleExprLocator gardener =
       new ReducibleExprLocator(preparingStmt, constExps, addCasts, removableCasts);
   for (RexNode exp : exps) {
     gardener.analyze(exp);
   }
   assert (constExps.size() == addCasts.size());
 }
예제 #19
0
 /**
  * Resolves a multi-part identifier such as "SCHEMA.EMP.EMPNO" to a namespace. The returned
  * namespace may represent a schema, table, column, etc.
  *
  * @pre names.size() > 0
  * @post return != null
  */
 public static SqlValidatorNamespace lookup(SqlValidatorScope scope, List<String> names) {
   Util.pre(names.size() > 0, "names.size() > 0");
   SqlValidatorNamespace namespace = null;
   for (int i = 0; i < names.size(); i++) {
     String name = names.get(i);
     if (i == 0) {
       namespace = scope.resolve(name, null, null);
     } else {
       namespace = namespace.lookupChild(name);
     }
   }
   Util.permAssert(namespace != null, "post: namespace != null");
   return namespace;
 }
예제 #20
0
 public static RelDataType createTypeFromProjection(
     RelDataType type,
     List<String> columnNameList,
     RelDataTypeFactory typeFactory,
     boolean caseSensitive) {
   // If the names in columnNameList and type have case-sensitive differences,
   // the resulting type will use those from type. These are presumably more
   // canonical.
   final List<RelDataTypeField> fields = new ArrayList<RelDataTypeField>(columnNameList.size());
   for (String name : columnNameList) {
     RelDataTypeField field = type.getField(name, caseSensitive);
     fields.add(type.getFieldList().get(field.getIndex()));
   }
   return typeFactory.createStructType(fields);
 }
예제 #21
0
  public static void getSchemaObjectMonikers(
      SqlValidatorCatalogReader catalogReader, List<String> names, List<SqlMoniker> hints) {
    // Assume that the last name is 'dummy' or similar.
    List<String> subNames = Util.skipLast(names);
    hints.addAll(catalogReader.getAllSchemaObjectNames(subNames));

    // If the name has length 0, try prepending the name of the default
    // schema. So, the empty name would yield a list of tables in the
    // default schema, as well as a list of schemas from the above code.
    if (subNames.size() == 0) {
      hints.addAll(
          catalogReader.getAllSchemaObjectNames(
              Collections.singletonList(catalogReader.getSchemaName())));
    }
  }
    public void analyze(RexNode exp) {
      assert (stack.isEmpty());

      exp.accept(this);

      // Deal with top of stack
      assert (stack.size() == 1);
      assert (parentCallTypeStack.isEmpty());
      Constancy rootConstancy = stack.get(0);
      if (rootConstancy == Constancy.REDUCIBLE_CONSTANT) {
        // The entire subtree was constant, so add it to the result.
        addResult(exp);
      }
      stack.clear();
    }
예제 #23
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);
  }
예제 #24
0
  private SargIntervalSequence evaluateIntersection(List<SargIntervalSequence> list) {
    SargIntervalSequence seq = null;

    if (list.isEmpty()) {
      // Counterintuitive but true: intersection of no sets is the
      // universal set (kinda like 2^0=1).  One way to prove this to
      // yourself is to apply DeMorgan's law.  The union of no sets is
      // certainly the empty set.  So the complement of that union is the
      // universal set.  That's equivalent to the intersection of the
      // complements of no sets, which is the intersection of no sets.
      // QED.
      seq = new SargIntervalSequence();
      seq.addInterval(new SargInterval(factory, getDataType()));
      return seq;
    }

    // The way we evaluate the intersection is to start with the first
    // entry as a baseline, and then keep deleting stuff from it by
    // intersecting the other entrie in turn.  Whatever makes it through
    // this filtering remains as the final result.
    for (SargIntervalSequence newSeq : list) {
      if (seq == null) {
        // first child
        seq = newSeq;
        continue;
      }
      intersectSequences(seq, newSeq);
    }

    return seq;
  }
예제 #25
0
 public RelOptTable getTableForMember(List<String> names) {
   final SqlValidatorTable table = catalogReader.getTable(names);
   final RelDataType rowType = table.getRowType();
   final List<RelCollation> collationList = deduceMonotonicity(table);
   if (names.size() < 3) {
     String[] newNames2 = {"CATALOG", "SALES", ""};
     List<String> newNames = new ArrayList<String>();
     int i = 0;
     while (newNames.size() < newNames2.length) {
       newNames.add(i, newNames2[i]);
       ++i;
     }
     names = newNames;
   }
   return createColumnSet(table, names, rowType, collationList);
 }
예제 #26
0
파일: RexUtil.java 프로젝트: kunlqt/optiq
 public RexNode visitCall(RexCall call) {
   List<RexNode> normalizedOperands = new ArrayList<RexNode>();
   int diffCount = 0;
   for (RexNode operand : call.getOperands()) {
     operand.accept(this);
     final RexNode normalizedOperand = lookup(operand);
     normalizedOperands.add(normalizedOperand);
     if (normalizedOperand != operand) {
       ++diffCount;
     }
   }
   if (diffCount > 0) {
     call = call.clone(call.getType(), normalizedOperands);
   }
   return register(call);
 }
 /**
  * Applies a mapping to a list.
  *
  * @param mapping Mapping
  * @param list List
  * @param <T> Element type
  * @return List with elements permuted according to mapping
  */
 public static <T> List<T> apply(final Mapping mapping, final List<T> list) {
   if (mapping.getSourceCount() != list.size()) {
     // REVIEW: too strict?
     throw new IllegalArgumentException(
         "mapping source count "
             + mapping.getSourceCount()
             + " does not match list size "
             + list.size());
   }
   final int targetCount = mapping.getTargetCount();
   final List<T> list2 = new ArrayList<T>(targetCount);
   for (int target = 0; target < targetCount; ++target) {
     final int source = mapping.getSource(target);
     list2.add(list.get(source));
   }
   return list2;
 }
        public void onMatch(RelOptRuleCall call) {
          ProjectRel project = (ProjectRel) call.rels[0];
          List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(project.getChildExps()));
          if (reduceExpressions(project, expList)) {
            call.transformTo(
                new ProjectRel(
                    project.getCluster(),
                    project.getChild(),
                    expList.toArray(new RexNode[expList.size()]),
                    project.getRowType(),
                    ProjectRel.Flags.Boxed,
                    Collections.<RelCollation>emptyList()));

            // New plan is absolutely better than old plan.
            call.getPlanner().setImportance(project, 0.0);
          }
        }
        public void onMatch(RelOptRuleCall call) {
          JoinRel join = (JoinRel) call.rels[0];
          List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(join.getChildExps()));
          if (reduceExpressions(join, expList)) {
            call.transformTo(
                new JoinRel(
                    join.getCluster(),
                    join.getLeft(),
                    join.getRight(),
                    expList.get(0),
                    join.getJoinType(),
                    join.getVariablesStopped()));

            // New plan is absolutely better than old plan.
            call.getPlanner().setImportance(join, 0.0);
          }
        }
 /**
  * Creates a partial mapping from a list. For example, <code>
  * PartialMapping({1, 2, 4}, 6)</code> creates the mapping
  *
  * <table border="1">
  * <caption>Example</caption>
  * <tr>
  * <th>source</th>
  * <td>0</td>
  * <td>1</td>
  * <td>2</td>
  * <td>3</td>
  * <td>4</td>
  * <td>5</td>
  * </tr>
  * <tr>
  * <th>target</th>
  * <td>-1</td>
  * <td>0</td>
  * <td>1</td>
  * <td>-1</td>
  * <td>2</td>
  * <td>-1</td>
  * </tr>
  * </table>
  *
  * @param sourceList List whose i'th element is the source of target #i
  * @param sourceCount Number of elements in the source domain
  * @param mappingType Mapping type, must be {@link
  *     org.eigenbase.util.mapping.MappingType#PARTIAL_SURJECTION} or stronger.
  */
 public PartialMapping(List<Integer> sourceList, int sourceCount, MappingType mappingType) {
   this.mappingType = mappingType;
   assert mappingType.isSingleSource();
   assert mappingType.isSingleTarget();
   int targetCount = sourceList.size();
   this.targets = new int[sourceCount];
   this.sources = new int[targetCount];
   Arrays.fill(sources, -1);
   for (int i = 0; i < sourceList.size(); ++i) {
     final int source = sourceList.get(i);
     sources[i] = source;
     if (source >= 0) {
       targets[source] = i;
     } else {
       assert !this.mappingType.isMandatorySource();
     }
   }
 }