示例#1
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);
  }
示例#2
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;
 }
示例#3
0
 public List<SqlOperator> lookupOperatorOverloads(
     SqlIdentifier opName, SqlFunctionCategory category, SqlSyntax syntax) {
   if (syntax != SqlSyntax.Function) {
     return Collections.emptyList();
   }
   // FIXME: ignoring prefix of opName
   String name = opName.names[opName.names.length - 1];
   List<TableFunction> tableFunctions = rootSchema.getTableFunctions(name);
   if (tableFunctions.isEmpty()) {
     return Collections.emptyList();
   }
   return toOps(name, tableFunctions);
 }
示例#4
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);
  }
示例#5
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);
  }
示例#6
0
    public PreparedResult prepareQueryable(Queryable queryable, RelDataType resultType) {
      queryString = null;
      Class runtimeContextClass = Object.class;
      final Argument[] arguments = {new Argument(connectionVariable, runtimeContextClass, null)};
      ClassDeclaration decl = init(arguments);

      final RelOptQuery query = new RelOptQuery(planner);
      final RelOptCluster cluster =
          query.createCluster(env, rexBuilder.getTypeFactory(), rexBuilder);

      RelNode rootRel = new LixToRelTranslator(cluster).translate(queryable);

      if (timingTracer != null) {
        timingTracer.traceTime("end sql2rel");
      }

      final RelDataType jdbcType = makeStruct(rexBuilder.getTypeFactory(), resultType);
      fieldOrigins = Collections.nCopies(jdbcType.getFieldCount(), null);

      // Structured type flattening, view expansion, and plugging in
      // physical storage.
      rootRel = flattenTypes(rootRel, true);

      // Trim unused fields.
      rootRel = trimUnusedFields(rootRel);

      rootRel = optimize(resultType, rootRel);
      containsJava = treeContainsJava(rootRel);

      if (timingTracer != null) {
        timingTracer.traceTime("end optimization");
      }

      return implement(resultType, rootRel, SqlKind.SELECT, decl, arguments);
    }
示例#7
0
 private static List<Expression> simpleList(BlockExpression expression) {
   Expression simple = Blocks.simple(expression);
   if (simple instanceof NewExpression) {
     NewExpression newExpression = (NewExpression) simple;
     return newExpression.arguments;
   } else {
     return Collections.singletonList(simple);
   }
 }
示例#8
0
 /**
  * Creates a ProjectRel with no sort keys.
  *
  * @param cluster Cluster this relational expression belongs to
  * @param child input relational expression
  * @param exps set of expressions for the input columns
  * @param fieldNames aliases of the expressions
  * @param flags values as in {@link ProjectRelBase.Flags}
  */
 public ProjectRel(
     RelOptCluster cluster, RelNode child, RexNode[] exps, String[] fieldNames, int flags) {
   this(
       cluster,
       child,
       exps,
       RexUtil.createStructType(cluster.getTypeFactory(), exps, fieldNames),
       flags,
       Collections.<RelCollation>emptyList());
 }
示例#9
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;
 }
示例#10
0
 public ProjectRel clone() {
   ProjectRel clone =
       new ProjectRel(
           getCluster(),
           getChild().clone(),
           RexUtil.clone(exps),
           rowType,
           getFlags(),
           Collections.<RelCollation>emptyList());
   clone.inheritTraitsFrom(this);
   return clone;
 }
示例#11
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());
 }
 private List<AggregateCall> transformAggCalls(
     RelDataTypeFactory typeFactory, int nGroupCols, List<AggregateCall> origCalls) {
   List<AggregateCall> newCalls = new ArrayList<AggregateCall>();
   int iInput = nGroupCols;
   for (AggregateCall origCall : origCalls) {
     if (origCall.isDistinct()
         || !SUPPORTED_AGGREGATES.containsKey(origCall.getAggregation().getClass())) {
       return null;
     }
     Aggregation aggFun;
     RelDataType aggType;
     if (origCall.getAggregation().getName().equals("COUNT")) {
       aggFun = new SqlSumEmptyIsZeroAggFunction(origCall.getType());
       SqlAggFunction af = (SqlAggFunction) aggFun;
       final AggregateRelBase.AggCallBinding binding =
           new AggregateRelBase.AggCallBinding(
               typeFactory, af, Collections.singletonList(origCall.getType()), nGroupCols);
       // count(any) is always not null, however nullability of sum might
       // depend on the number of columns in GROUP BY.
       // Here we use SUM0 since we are sure we will not face nullable
       // inputs nor we'll face empty set.
       aggType = af.inferReturnType(binding);
     } else {
       aggFun = origCall.getAggregation();
       aggType = origCall.getType();
     }
     AggregateCall newCall =
         new AggregateCall(
             aggFun,
             origCall.isDistinct(),
             Collections.singletonList(iInput),
             aggType,
             origCall.getName());
     newCalls.add(newCall);
     ++iInput;
   }
   return newCalls;
 }
  // 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);
  }
示例#14
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 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);
          }
        }
示例#16
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);
   }
 }
示例#17
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());
 }
示例#18
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);
  }
示例#19
0
 public List<RelCollation> getCollationList() {
   return Collections.emptyList();
 }
示例#20
0
  <T> PrepareResult<T> prepare_(
      Context context, String sql, Queryable<T> queryable, Type elementType) {
    final JavaTypeFactory typeFactory = context.getTypeFactory();
    OptiqCatalogReader catalogReader = new OptiqCatalogReader(context.getRootSchema(), typeFactory);
    final OptiqPreparingStmt preparingStmt =
        new OptiqPreparingStmt(catalogReader, typeFactory, context.getRootSchema());
    preparingStmt.setResultCallingConvention(CallingConvention.ENUMERABLE);

    final RelDataType x;
    final PreparedResult preparedResult;
    if (sql != null) {
      assert queryable == null;
      SqlParser parser = new SqlParser(sql);
      SqlNode sqlNode;
      try {
        sqlNode = parser.parseQuery();
      } catch (SqlParseException e) {
        throw new RuntimeException("parse failed", e);
      }
      final Schema rootSchema = context.getRootSchema();
      SqlValidator validator =
          new SqlValidatorImpl(
              new ChainedSqlOperatorTable(
                  Arrays.<SqlOperatorTable>asList(
                      SqlStdOperatorTable.instance(),
                      new MySqlOperatorTable(rootSchema, typeFactory))),
              catalogReader,
              typeFactory,
              SqlConformance.Default) {};
      preparedResult = preparingStmt.prepareSql(sqlNode, Object.class, validator, true);
      x = validator.getValidatedNodeType(sqlNode);
    } else {
      assert queryable != null;
      x = context.getTypeFactory().createType(elementType);
      preparedResult = preparingStmt.prepareQueryable(queryable, x);
    }

    // TODO: parameters
    final List<Parameter> parameters = Collections.emptyList();
    // TODO: column meta data
    final List<ColumnMetaData> columns = new ArrayList<ColumnMetaData>();
    RelDataType jdbcType = makeStruct(typeFactory, x);
    for (RelDataTypeField field : jdbcType.getFields()) {
      RelDataType type = field.getType();
      SqlTypeName sqlTypeName = type.getSqlTypeName();
      columns.add(
          new ColumnMetaData(
              columns.size(),
              false,
              true,
              false,
              false,
              type.isNullable() ? 1 : 0,
              true,
              0,
              field.getName(),
              field.getName(),
              null,
              sqlTypeName.allowsPrec() && false ? type.getPrecision() : -1,
              sqlTypeName.allowsScale() ? type.getScale() : -1,
              null,
              null,
              sqlTypeName.getJdbcOrdinal(),
              sqlTypeName.getName(),
              true,
              false,
              false,
              null));
    }
    return new PrepareResult<T>(sql, parameters, columns, (Enumerable<T>) preparedResult.execute());
  }
示例#21
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);
  }
示例#22
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);
  }