// implement RelOptRule
  public void onMatch(RelOptRuleCall call) {
    CalcRel calcRel = (CalcRel) call.rels[0];
    RexProgram program = calcRel.getProgram();

    // check the projection
    List<Integer> projOrdinals = new ArrayList<Integer>();
    RelDataType outputRowType = isProjectSimple(calcRel, projOrdinals);
    if (outputRowType == null) {
      return;
    }

    RexLocalRef condition = program.getCondition();
    CompOperatorEnum compOp = CompOperatorEnum.COMP_NOOP;
    Integer[] filterOrdinals = {};
    List<RexLiteral> filterLiterals = new ArrayList<RexLiteral>();

    // check the condition
    if (condition != null) {
      RexNode filterExprs = program.expandLocalRef(condition);
      List<Integer> filterList = new ArrayList<Integer>();

      List<CompOperatorEnum> op = new ArrayList<CompOperatorEnum>();
      if (!isConditionSimple(calcRel, filterExprs, filterList, filterLiterals, op)) {
        return;
      }

      compOp = op.get(0);
      filterOrdinals = filterList.toArray(new Integer[filterList.size()]);
    }

    RelNode fennelInput =
        mergeTraitsAndConvert(
            calcRel.getTraits(), FennelRel.FENNEL_EXEC_CONVENTION, calcRel.getChild());
    if (fennelInput == null) {
      return;
    }

    Integer[] projection = projOrdinals.toArray(new Integer[projOrdinals.size()]);
    FennelReshapeRel reshapeRel =
        new FennelReshapeRel(
            calcRel.getCluster(),
            fennelInput,
            projection,
            outputRowType,
            compOp,
            filterOrdinals,
            filterLiterals,
            new FennelRelParamId[] {},
            new Integer[] {},
            null);

    call.transformTo(reshapeRel);
  }
  /**
   * Determines if a projection is simple.
   *
   * @param calcRel CalcRel containing the projection
   * @param projOrdinals if the projection is simple, returns the ordinals of the projection inputs
   * @return rowtype corresponding to the projection, provided it is simple; otherwise null is
   *     returned
   */
  private RelDataType isProjectSimple(CalcRel calcRel, List<Integer> projOrdinals) {
    // Loop through projection expressions.  If we find a non-simple
    // projection expression, simply return.
    RexProgram program = calcRel.getProgram();
    List<RexLocalRef> projList = program.getProjectList();
    int nProjExprs = projList.size();
    RelDataType[] types = new RelDataType[nProjExprs];
    String[] fieldNames = new String[nProjExprs];
    RelDataTypeField[] projFields = calcRel.getRowType().getFields();

    for (int i = 0; i < nProjExprs; i++) {
      RexNode projExpr = program.expandLocalRef(projList.get(i));
      if (projExpr instanceof RexInputRef) {
        projOrdinals.add(((RexInputRef) projExpr).getIndex());
        types[i] = projExpr.getType();
        fieldNames[i] = projFields[i].getName();
        continue;
      } else if (!(projExpr instanceof RexCall)) {
        return null;
      }

      RexCall rexCall = (RexCall) projExpr;
      if (rexCall.getOperator() != SqlStdOperatorTable.castFunc) {
        return null;
      }
      RexNode castOperand = rexCall.getOperands()[0];
      if (!(castOperand instanceof RexInputRef)) {
        return null;
      }
      RelDataType castType = projExpr.getType();
      RelDataType origType = castOperand.getType();
      if (isCastSimple(origType, castType)) {
        projOrdinals.add(((RexInputRef) castOperand).getIndex());
        types[i] = castType;
        fieldNames[i] = projFields[i].getName();
      } else {
        return null;
      }
    }

    // return the rowtype corresponding to the output of the projection
    return calcRel.getCluster().getTypeFactory().createStructType(types, fieldNames);
  }