@Override public boolean canConvert( RelOptPlanner planner, DistributionTrait fromTrait, DistributionTrait toTrait, RelNode fromRel) { if (fromTrait.equals(toTrait)) { return true; } // Source trait is "ANY", which is abstract type of distribution. // We do not want to convert from "ANY", since it's abstract. // Source trait should be concrete type: SINGLETON, HASH_DISTRIBUTED, etc. if (fromTrait.equals(DistributionTrait.DEFAULT) && !(fromRel instanceof RelSubset) ) { return false; } // It is only possible to apply a distribution trait to a PHYSICAL convention. if (fromRel.getConvention() != Prel.PHYSICAL) { return false; } if (fromTrait.getType() == DistributionType.HASH_DISTRIBUTED && toTrait.getType() == DistributionType.BROADCAST_DISTRIBUTED) { return false; } if (fromTrait.getType() == DistributionType.BROADCAST_DISTRIBUTED && toTrait.getType() == DistributionType.HASH_DISTRIBUTED) { return false; } return true; }
private DistributionTrait convertDist(DistributionTrait srcDist, Map<Integer, Integer> inToOut) { List<DistributionField> newFields = Lists.newArrayList(); for (DistributionField field : srcDist.getFields()) { if (inToOut.containsKey(field.getFieldId())) { newFields.add(new DistributionField(inToOut.get(field.getFieldId()))); } } // After the projection, if the new distribution fields is empty, or new distribution fields is a subset of // original distribution field, we should replace with either SINGLETON or RANDOM_DISTRIBUTED. if (newFields.isEmpty() || newFields.size() < srcDist.getFields().size()) { if (srcDist.getType() != DistributionType.SINGLETON) { return DistributionTrait.ANY; } else { return DistributionTrait.SINGLETON; } } else { return new DistributionTrait(srcDist.getType(), ImmutableList.copyOf(newFields)); } }
@Override public boolean satisfies(RelTrait trait) { if (trait instanceof DistributionTrait) { DistributionType requiredDist = ((DistributionTrait) trait).getType(); if (requiredDist == DistributionType.ANY) { return true; } if (this.type == DistributionType.HASH_DISTRIBUTED) { if (requiredDist == DistributionType.HASH_DISTRIBUTED) { // A subset of the required distribution columns can satisfy (subsume) the requirement // e.g: required distribution: {a, b, c} // Following can satisfy the requirements: {a}, {b}, {c}, {a, b}, {b, c}, {a, c} or {a, b, c} // New: Use equals for subsumes check of hash distribution. If we uses subsumes, // a join may end up with hash-distributions using different keys. This would // cause incorrect query result. return this.equals(trait); } } } return this.equals(trait); }
if(distribution.getType() != DistributionType.HASH_DISTRIBUTED){ throw UserException.planError().message("Tried to plan a distribution writer but distribution was incorrect.").build(logger);
@Override public RelNode convert( RelOptPlanner planner, RelNode rel, DistributionTrait toDist, boolean allowInfiniteCostConverters) { switch(toDist.getType()){ // UnionExchange, HashToRandomExchange, OrderedPartitionExchange and BroadcastExchange destroy the ordering property, // therefore RelCollation is set to default, which is EMPTY. case SINGLETON: return new UnionExchangePrel(rel.getCluster(), planner.emptyTraitSet().plus(Prel.PHYSICAL).plus(toDist), rel); case HASH_DISTRIBUTED: return new HashToRandomExchangePrel(rel.getCluster(), planner.emptyTraitSet().plus(Prel.PHYSICAL).plus(toDist), rel, toDist.getFields()); case RANGE_DISTRIBUTED: return new OrderedPartitionExchangePrel(rel.getCluster(), planner.emptyTraitSet().plus(Prel.PHYSICAL).plus(toDist), rel); case BROADCAST_DISTRIBUTED: return new BroadcastExchangePrel(rel.getCluster(), planner.emptyTraitSet().plus(Prel.PHYSICAL).plus(toDist), rel); case ROUND_ROBIN_DISTRIBUTED: return new RoundRobinExchangePrel(rel.getCluster(), planner.emptyTraitSet().plus(Prel.PHYSICAL).plus(toDist), rel); case ANY: // If target is "any", any input would satisfy "any". Return input directly. return rel; default: return null; } }