Example #1
0
  /** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link SortRel}. */
  public TrimResult trimFields(SortRel sort, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    final RelDataType rowType = sort.getRowType();
    final int fieldCount = rowType.getFieldCount();
    final RelCollation collation = sort.getCollation();
    final RelNode input = sort.getChild();

    // We use the fields used by the consumer, plus any fields used as sort
    // keys.
    BitSet inputFieldsUsed = (BitSet) fieldsUsed.clone();
    for (RelFieldCollation field : collation.getFieldCollations()) {
      inputFieldsUsed.set(field.getFieldIndex());
    }

    // Create input with trimmed columns.
    final Set<RelDataTypeField> inputExtraFields = Collections.emptySet();
    TrimResult trimResult = trimChild(sort, 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 && inputMapping.isIdentity() && fieldsUsed.cardinality() == fieldCount) {
      return new TrimResult(sort, Mappings.createIdentity(fieldCount));
    }

    final SortRel newSort =
        sort.copy(sort.getTraitSet(), newInput, RexUtil.apply(inputMapping, collation));
    assert newSort.getClass() == sort.getClass();

    // The result has the same mapping as the input gave us. Sometimes we
    // return fields that the consumer didn't ask for, because the filter
    // needs them for its condition.
    return new TrimResult(newSort, inputMapping);
  }
Example #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);
  }
Example #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);
  }
  /**
   * 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;
  }
Example #5
0
 /**
  * Burrows into a synthetic record and returns the underlying relation which provides the field
  * called <code>fieldName</code>.
  */
 public JavaRel implementFieldAccess(JavaRelImplementor implementor, String fieldName) {
   if (!isBoxed()) {
     return implementor.implementFieldAccess((JavaRel) getChild(), fieldName);
   }
   RelDataType type = getRowType();
   int field = type.getFieldOrdinal(fieldName);
   RexLocalRef ref = program.getProjectList().get(field);
   final int index = ref.getIndex();
   return implementor.findRel((JavaRel) this, program.getExprList().get(index));
 }
Example #6
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);
  }
Example #7
0
 /**
  * Generates a cast from one row type to another
  *
  * @param rexBuilder RexBuilder to use for constructing casts
  * @param lhsRowType target row type
  * @param rhsRowType source row type; fields must be 1-to-1 with lhsRowType, in same order
  * @return cast expressions
  */
 public static RexNode[] generateCastExpressions(
     RexBuilder rexBuilder, RelDataType lhsRowType, RelDataType rhsRowType) {
   int n = rhsRowType.getFieldCount();
   assert n == lhsRowType.getFieldCount()
       : "field count: lhs [" + lhsRowType + "] rhs [" + rhsRowType + "]";
   RexNode[] rhsExps = new RexNode[n];
   for (int i = 0; i < n; ++i) {
     rhsExps[i] = rexBuilder.makeInputRef(rhsRowType.getFields()[i].getType(), i);
   }
   return generateCastExpressions(rexBuilder, lhsRowType, rhsExps);
 }
Example #8
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;
 }
Example #9
0
 /**
  * Creates an expression which references the <i> fieldOrdinal</i><sup>th</sup> field of the
  * <i>ordinal</i><sup>th</sup> input.
  *
  * <p>(We can potentially optimize the generation process, so we can access field values without
  * actually instantiating the row.)
  */
 public Expression translateInputField(JavaRel rel, int ordinal, int fieldOrdinal) {
   assert ordinal >= 0;
   assert ordinal < rel.getInputs().size();
   assert fieldOrdinal >= 0;
   assert fieldOrdinal < rel.getInput(ordinal).getRowType().getFieldList().size();
   RelDataType rowType = rel.getRowType();
   final RelDataTypeField[] fields = rowType.getFields();
   final int fieldIndex = computeFieldOffset(rel, ordinal) + fieldOrdinal;
   assert fieldIndex >= 0;
   assert fieldIndex < fields.length;
   final RexNode expr = rexBuilder.makeInputRef(fields[fieldIndex].getType(), fieldIndex);
   return translate(rel, expr);
 }
Example #10
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);
 }
Example #11
0
 public Void visitInputRef(RexInputRef inputRef) {
   super.visitInputRef(inputRef);
   if (inputRef.getIndex() >= inputRowType.getFieldCount()) {
     throw new IllegalForwardRefException();
   }
   return null;
 }
Example #12
0
 /** Creates an array of {@link RexLocalRef} objects, one for each field of a given rowtype. */
 public static RexLocalRef[] toLocalRefs(RelDataType rowType) {
   final RelDataTypeField[] fields = rowType.getFields();
   final RexLocalRef[] refs = new RexLocalRef[fields.length];
   for (int i = 0; i < refs.length; i++) {
     refs[i] = new RexLocalRef(i, fields[i].getType());
   }
   return refs;
 }
Example #13
0
 /**
  * Creates an array of {@link RexInputRef} objects, one for each field of a given rowtype,
  * according to a permutation.
  *
  * @param args Permutation
  * @param rowType Input row type
  * @return Array of input refs
  */
 public static RexInputRef[] toInputRefs(int[] args, RelDataType rowType) {
   final RelDataTypeField[] fields = rowType.getFields();
   final RexInputRef[] rexNodes = new RexInputRef[args.length];
   for (int i = 0; i < args.length; i++) {
     int fieldOrdinal = args[i];
     rexNodes[i] = new RexInputRef(fieldOrdinal, fields[fieldOrdinal].getType());
   }
   return rexNodes;
 }
Example #14
0
 /**
  * Generates a cast for a row type.
  *
  * @param rexBuilder RexBuilder to use for constructing casts
  * @param lhsRowType target row type
  * @param rhsExps expressions to be cast
  * @return cast expressions
  */
 public static RexNode[] generateCastExpressions(
     RexBuilder rexBuilder, RelDataType lhsRowType, RexNode[] rhsExps) {
   RelDataTypeField[] lhsFields = lhsRowType.getFields();
   final int fieldCount = lhsFields.length;
   RexNode[] castExps = new RexNode[fieldCount];
   assert fieldCount == rhsExps.length;
   for (int i = 0; i < fieldCount; ++i) {
     RelDataTypeField lhsField = lhsFields[i];
     RelDataType lhsType = lhsField.getType();
     RelDataType rhsType = rhsExps[i].getType();
     if (lhsType.equals(rhsType)) {
       castExps[i] = rhsExps[i];
     } else {
       castExps[i] = rexBuilder.makeCast(lhsType, rhsExps[i]);
     }
   }
   return castExps;
 }
Example #15
0
  /**
   * Determines whether a {@link RexCall} requires decimal expansion. It usually requires expansion
   * if it has decimal operands.
   *
   * <p>Exceptions to this rule are:
   *
   * <ul>
   *   <li>isNull doesn't require expansion
   *   <li>It's okay to cast decimals to and from char types
   *   <li>It's okay to cast nulls as decimals
   *   <li>Casts require expansion if their return type is decimal
   *   <li>Reinterpret casts can handle a decimal operand
   * </ul>
   *
   * @param expr expression possibly in need of expansion
   * @param recurse whether to check nested calls
   * @return whether the expression requires expansion
   */
  public static boolean requiresDecimalExpansion(RexNode expr, boolean recurse) {
    if (!(expr instanceof RexCall)) {
      return false;
    }
    RexCall call = (RexCall) expr;

    boolean localCheck = true;
    switch (call.getKind()) {
      case Reinterpret:
      case IsNull:
        localCheck = false;
        break;
      case Cast:
        RelDataType lhsType = call.getType();
        RelDataType rhsType = call.operands[0].getType();
        if (rhsType.getSqlTypeName() == SqlTypeName.NULL) {
          return false;
        }
        if (SqlTypeUtil.inCharFamily(lhsType) || SqlTypeUtil.inCharFamily(rhsType)) {
          localCheck = false;
        } else if (SqlTypeUtil.isDecimal(lhsType) && (lhsType != rhsType)) {
          return true;
        }
        break;
      default:
        localCheck = call.getOperator().requiresDecimalExpansion();
    }

    if (localCheck) {
      if (SqlTypeUtil.isDecimal(call.getType())) {
        // NOTE jvs 27-Mar-2007: Depending on the type factory, the
        // result of a division may be decimal, even though both inputs
        // are integer.
        return true;
      }
      for (int i = 0; i < call.operands.length; i++) {
        if (SqlTypeUtil.isDecimal(call.operands[i].getType())) {
          return true;
        }
      }
    }
    return (recurse && requiresDecimalExpansion(call.operands, recurse));
  }
Example #16
0
    private List<RelCollation> deduceMonotonicity(SqlValidatorTable table) {
      final RelDataType rowType = table.getRowType();
      final List<RelCollation> collationList = new ArrayList<RelCollation>();

      // Deduce which fields the table is sorted on.
      int i = -1;
      for (RelDataTypeField field : rowType.getFieldList()) {
        ++i;
        final SqlMonotonicity monotonicity = table.getMonotonicity(field.getName());
        if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) {
          final RelFieldCollation.Direction direction =
              monotonicity.isDecreasing()
                  ? RelFieldCollation.Direction.DESCENDING
                  : RelFieldCollation.Direction.ASCENDING;
          collationList.add(
              RelCollationImpl.of(
                  new RelFieldCollation(
                      i, direction, RelFieldCollation.NullDirection.UNSPECIFIED)));
        }
      }
      return collationList;
    }
Example #17
0
 public static void checkCharsetAndCollateConsistentIfCharType(RelDataType type) {
   // (every charset must have a default collation)
   if (SqlTypeUtil.inCharFamily(type)) {
     Charset strCharset = type.getCharset();
     Charset colCharset = type.getCollation().getCharset();
     assert null != strCharset;
     assert null != colCharset;
     if (!strCharset.equals(colCharset)) {
       if (false) {
         // todo: enable this checking when we have a charset to
         //   collation mapping
         throw new Error(
             type.toString()
                 + " was found to have charset '"
                 + strCharset.name()
                 + "' and a mismatched collation charset '"
                 + colCharset.name()
                 + "'");
       }
     }
   }
 }
Example #18
0
  /** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link FilterRel}. */
  public TrimResult trimFields(
      FilterRel filter, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    final RelDataType rowType = filter.getRowType();
    final int fieldCount = rowType.getFieldCount();
    final RexNode conditionExpr = filter.getCondition();
    final RelNode input = filter.getChild();

    // We use the fields used by the consumer, plus any fields used in the
    // filter.
    BitSet inputFieldsUsed = (BitSet) fieldsUsed.clone();
    final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields);
    RelOptUtil.InputFinder inputFinder =
        new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields);
    conditionExpr.accept(inputFinder);

    // Create input with trimmed columns.
    TrimResult trimResult = trimChild(filter, 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(filter, Mappings.createIdentity(fieldCount));
    }

    // Build new project expressions, and populate the mapping.
    final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput);
    RexNode newConditionExpr = conditionExpr.accept(shuttle);

    final FilterRel newFilter = new FilterRel(filter.getCluster(), newInput, newConditionExpr);
    assert newFilter.getClass() == filter.getClass();

    // The result has the same mapping as the input gave us. Sometimes we
    // return fields that the consumer didn't ask for, because the filter
    // needs them for its condition.
    return new TrimResult(newFilter, inputMapping);
  }
Example #19
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()));
 }
    private void reduceCasts(RexCall outerCast) {
      RexNode[] operands = outerCast.getOperands();
      if (operands.length != 1) {
        return;
      }
      RelDataType outerCastType = outerCast.getType();
      RelDataType operandType = operands[0].getType();
      if (operandType.equals(outerCastType)) {
        removableCasts.add(outerCast);
        return;
      }

      // See if the reduction
      // CAST((CAST x AS type) AS type NOT NULL)
      // -> CAST(x AS type NOT NULL)
      // applies.  TODO jvs 15-Dec-2008:  consider
      // similar cases for precision changes.
      if (!(operands[0] instanceof RexCall)) {
        return;
      }
      RexCall innerCast = (RexCall) operands[0];
      if (innerCast.getOperator() != SqlStdOperatorTable.castFunc) {
        return;
      }
      if (innerCast.getOperands().length != 1) {
        return;
      }
      RelDataTypeFactory typeFactory = preparingStmt.getFarragoTypeFactory();
      RelDataType outerTypeNullable = typeFactory.createTypeWithNullability(outerCastType, true);
      RelDataType innerTypeNullable = typeFactory.createTypeWithNullability(operandType, true);
      if (outerTypeNullable != innerTypeNullable) {
        return;
      }
      if (operandType.isNullable()) {
        removableCasts.add(innerCast);
      }
    }
Example #21
0
  /**
   * Looks up a field with a given name, returning null if not found.
   *
   * @param rowType Row type
   * @param columnName Field name
   * @return Field, or null if not found
   */
  public static RelDataTypeField lookupField(
      boolean caseSensitive, final RelDataType rowType, String columnName) {
    RelDataTypeField field = rowType.getField(columnName, caseSensitive);
    if (field != null) {
      return field;
    }

    // If record type is flagged as having "any field you ask for",
    // return a type. (TODO: Better way to mark accommodating types.)
    RelDataTypeField extra = RelDataTypeImpl.extra(rowType);
    if (extra != null) {
      return new RelDataTypeFieldImpl(columnName, -1, extra.getType());
    }
    return null;
  }
Example #22
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);
  }
Example #23
0
 /**
  * Returns whether the type of an array of expressions is compatible with a struct type.
  *
  * @param exprs Array of expressions
  * @param type Type
  * @param fail Whether to fail if there is a mismatch
  * @return Whether every expression has the same type as the corresponding member of the struct
  *     type
  * @see RelOptUtil#eq(String, RelDataType, String, RelDataType, boolean)
  */
 public static boolean compatibleTypes(RexNode[] exprs, RelDataType type, boolean fail) {
   final RelDataTypeField[] fields = type.getFields();
   if (exprs.length != fields.length) {
     assert !fail : "rowtype mismatches expressions";
     return false;
   }
   for (int i = 0; i < fields.length; i++) {
     final RelDataType exprType = exprs[i].getType();
     final RelDataType fieldType = fields[i].getType();
     if (!RelOptUtil.eq("type1", exprType, "type2", fieldType, fail)) {
       return false;
     }
   }
   return true;
 }
Example #24
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);
 }
Example #25
0
 /**
  * Returns whether the leading edge of a given array of expressions is wholly {@link RexInputRef}
  * objects with types corresponding to the underlying datatype.
  */
 public static boolean containIdentity(RexNode[] exprs, RelDataType rowType, boolean fail) {
   final RelDataTypeField[] fields = rowType.getFields();
   if (exprs.length < fields.length) {
     assert !fail : "exprs/rowType length mismatch";
     return false;
   }
   for (int i = 0; i < fields.length; i++) {
     if (!(exprs[i] instanceof RexInputRef)) {
       assert !fail : "expr[" + i + "] is not a RexInputRef";
       return false;
     }
     RexInputRef inputRef = (RexInputRef) exprs[i];
     if (inputRef.getIndex() != i) {
       assert !fail : "expr[" + i + "] has ordinal " + inputRef.getIndex();
       return false;
     }
     if (!RelOptUtil.eq("type1", exprs[i].getType(), "type2", fields[i].getType(), fail)) {
       return false;
     }
   }
   return true;
 }
 protected Integer mapFieldRef(RexNode exp, List<String> origFieldName, RelDataType rowType) {
   RexInputRef fieldAccess = (RexInputRef) exp;
   origFieldName.add(rowType.getFields()[fieldAccess.getIndex()].getName());
   return fieldAccess.getIndex();
 }
Example #27
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);
  }
Example #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);
  }
  /**
   * Returns true if a type is a simple cast of another type. It is if the cast type is nullable and
   * the cast is one of the following:
   * <li>x TO x
   * <li>char(n) TO varchar(m)
   * <li>varchar(n) TO varchar(m)
   * <li>x not null TO x nullable
   *
   * @param origType original type passed into the cast operand
   * @param castType type the operand will be casted to
   * @return true if the cast is simple
   */
  private boolean isCastSimple(RelDataType origType, RelDataType castType) {
    SqlTypeName origTypeName = origType.getSqlTypeName();
    SqlTypeName castTypeName = castType.getSqlTypeName();

    if (!(castType.isNullable())) {
      return false;
    }

    Charset origCharset = origType.getCharset();
    Charset castCharset = castType.getCharset();
    if ((origCharset != null) || (castCharset != null)) {
      if ((origCharset == null) || (castCharset == null)) {
        return false;
      }
      if (!origCharset.equals(castCharset)) {
        return false;
      }
    }

    return ((origType == castType)
        || ((origTypeName == SqlTypeName.CHAR) && (castTypeName == SqlTypeName.VARCHAR))
        || ((origTypeName == SqlTypeName.VARCHAR) && (castTypeName == SqlTypeName.VARCHAR))
        || ((origTypeName == castTypeName)
            && (origType.getPrecision() == castType.getPrecision())
            && ((origTypeName != SqlTypeName.DECIMAL)
                || (origType.getScale() == castType.getScale()))
            && (!origType.isNullable() && castType.isNullable())));
  }
  // implement FarragoMedDataServer
  public FarragoMedColumnSet newColumnSet(
      String[] localName,
      Properties tableProps,
      FarragoTypeFactory typeFactory,
      RelDataType rowType,
      Map<String, Properties> columnPropMap)
      throws SQLException {
    if (rowType == null) {
      rowType = createMockRowType(typeFactory);
    }

    assert (rowType.getFieldList().size() == 1);
    RelDataType type = rowType.getFields()[0].getType();
    assert (!type.isNullable());
    assert (typeFactory.getClassForPrimitive(type) != null);

    // TODO jvs 5-Aug-2005:  clean up usage of server properties
    // as defaults

    long nRows = -1;
    String rowCountSql = tableProps.getProperty(PROP_ROW_COUNT_SQL);
    if (rowCountSql != null) {
      // Attempt to issue a loopback query into Farrago to
      // get the number of rows to produce.
      DataSource loopbackDataSource = getLoopbackDataSource();
      Connection connection = null;
      if (loopbackDataSource != null) {
        try {
          connection = loopbackDataSource.getConnection();
          Statement stmt = connection.createStatement();
          ResultSet resultSet = stmt.executeQuery(rowCountSql);
          if (resultSet.next()) {
            nRows = resultSet.getLong(1);
          }
        } finally {
          // It's OK not to clean up stmt and resultSet;
          // connection.close() will do that for us.
          if (connection != null) {
            connection.close();
          }
        }
      }
    }

    if (nRows == -1) {
      nRows =
          getLongProperty(
              tableProps, PROP_ROW_COUNT, getLongProperty(getProperties(), PROP_ROW_COUNT, 10));
    }

    String executorImpl =
        tableProps.getProperty(
            PROP_EXECUTOR_IMPL, getProperties().getProperty(PROP_EXECUTOR_IMPL, PROPVAL_JAVA));
    assert (executorImpl.equals(PROPVAL_JAVA) || executorImpl.equals(PROPVAL_FENNEL));

    String udxSpecificName = tableProps.getProperty(PROP_UDX_SPECIFIC_NAME);

    if (udxSpecificName != null) {
      assert (executorImpl.equals(PROPVAL_JAVA));
    }

    checkNameMatch(getForeignSchemaName(), tableProps.getProperty(PROP_SCHEMA_NAME));

    checkNameMatch(getForeignTableName(), tableProps.getProperty(PROP_TABLE_NAME));

    return new MedMockColumnSet(this, localName, rowType, nRows, executorImpl, udxSpecificName);
  }