// 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);
  }
  /**
   * Creates projection list for scan. If the projection contains expressions, then the input
   * references from those expressions are extracted and that list of references becomes the
   * projection list.
   *
   * @param origScan row scan underneath the project
   * @param projRel ProjectRel that we will be creating the projection for
   * @param projectedColumns returns a list of the projected column ordinals, if it is possible to
   *     project
   * @param preserveExprCondition condition that identifies special expressions that should be
   *     preserved in the projection
   * @param defaultExpr expression to be used in the projection if no fields or special columns are
   *     selected
   * @param newProjList returns a new projection RelNode corresponding to a projection that now
   *     references a rowscan that is projecting the input references that were extracted from the
   *     original projection expressions; if the original expression didn't contain expressions,
   *     then this list is returned empty
   * @return true if columns in projection list from the scan need to be renamed
   */
  public boolean createProjectionList(
      FennelRel origScan,
      ProjectRel projRel,
      List<Integer> projectedColumns,
      PushProjector.ExprCondition preserveExprCondition,
      RexNode defaultExpr,
      List<ProjectRel> newProjList) {
    // REVIEW:  what about AnonFields?
    int n = projRel.getChildExps().length;
    RelDataType rowType = origScan.getRowType();
    RelDataType projType = projRel.getRowType();
    RelDataTypeField[] projFields = projType.getFields();
    List<Integer> tempProjList = new ArrayList<Integer>();
    boolean needRename = false;
    for (int i = 0; i < n; ++i) {
      RexNode exp = projRel.getChildExps()[i];
      List<String> origFieldName = new ArrayList<String>();
      Integer projIndex = mapProjCol(exp, origFieldName, rowType);
      if (projIndex == null) {
        // there are expressions in the projection; we need to extract
        // all input references and any special expressions from the
        // projection
        PushProjector pushProject =
            new PushProjector(projRel, null, origScan, preserveExprCondition);
        ProjectRel newProject = pushProject.convertProject(defaultExpr);
        if (newProject == null) {
          // can't do any further projection
          return false;
        }
        newProjList.add(newProject);

        // using the input references we just extracted, it should now
        // be possible to create a projection for the row scan
        needRename =
            createProjectionList(
                origScan,
                (ProjectRel) newProject.getChild(),
                projectedColumns,
                preserveExprCondition,
                defaultExpr,
                newProjList);
        assert (projectedColumns.size() > 0);
        return needRename;
      }
      String projFieldName = projFields[i].getName();
      if (!projFieldName.equals(origFieldName.get(0))) {
        needRename = true;
      }
      tempProjList.add(projIndex);
    }

    // now that we've determined it is possible to project, add the
    // ordinals to the return list
    projectedColumns.addAll(tempProjList);
    return needRename;
  }