/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hive.ql.optimizer.calcite.reloperators; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelShuttle; import org.apache.calcite.rel.core.Project; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexUtil; import org.apache.calcite.util.Util; import org.apache.calcite.util.mapping.Mapping; import org.apache.calcite.util.mapping.MappingType; import org.apache.hadoop.hive.ql.optimizer.calcite.CalciteSemanticException; import org.apache.hadoop.hive.ql.optimizer.calcite.CalciteSemanticException.UnsupportedFeature; import org.apache.hadoop.hive.ql.optimizer.calcite.HiveCalciteUtil; import org.apache.hadoop.hive.ql.optimizer.calcite.HiveRelShuttle; import org.apache.hadoop.hive.ql.optimizer.calcite.TraitsUtil; import com.google.common.collect.ImmutableList; public class HiveProject extends Project implements HiveRelNode { private final List<Integer> virtualCols; private boolean isSysnthetic; /** * Creates a HiveProject. * * @param cluster * Cluster this relational expression belongs to * @param child * input relational expression * @param exps * List of expressions for the input columns * @param rowType * output row type * @param flags * values as in {@link Project.Flags} */ public HiveProject(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, List<? extends RexNode> exps, RelDataType rowType, int flags) { super(cluster, traitSet, child, exps, rowType, flags); assert traitSet.containsIfApplicable(HiveRelNode.CONVENTION); virtualCols = ImmutableList.copyOf(HiveCalciteUtil.getVirtualCols(exps)); } /** * Creates a HiveProject with no sort keys. * * @param child * input relational expression * @param exps * set of expressions for the input columns * @param fieldNames * aliases of the expressions */ public static HiveProject create(RelNode child, List<? extends RexNode> exps, List<String> fieldNames) throws CalciteSemanticException{ RelOptCluster cluster = child.getCluster(); // 1 Ensure columnNames are unique - CALCITE-411 if (fieldNames != null && !Util.isDistinct(fieldNames)) { String msg = "Select list contains multiple expressions with the same name." + fieldNames; throw new CalciteSemanticException(msg, UnsupportedFeature.Same_name_in_multiple_expressions); } RelDataType rowType = RexUtil.createStructType(cluster.getTypeFactory(), exps, fieldNames); return create(cluster, child, exps, rowType, Collections.<RelCollation> emptyList()); } /** * Creates a HiveProject. */ public static HiveProject create(RelOptCluster cluster, RelNode child, List<? extends RexNode> exps, RelDataType rowType, final List<RelCollation> collationList) { RelTraitSet traitSet = TraitsUtil.getDefaultTraitSet(cluster); return new HiveProject(cluster, traitSet, child, exps, rowType, Flags.BOXED); } /** * Creates a HiveProject. */ public static HiveProject create(RelOptCluster cluster, RelNode child, List<? extends RexNode> exps, RelDataType rowType, RelTraitSet traitSet, final List<RelCollation> collationList) { return new HiveProject(cluster, traitSet, child, exps, rowType, Flags.BOXED); } /** * 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 constraints {@link MappingType#isMandatorySource()} and * {@link MappingType#isSingleSource()}, as does * {@link MappingType#INVERSE_FUNCTION}. * @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 * @throws CalciteSemanticException */ public static RelNode projectMapping(RelNode rel, Mapping mapping, List<String> fieldNames) throws CalciteSemanticException { assert mapping.getMappingType().isSingleSource(); assert mapping.getMappingType().isMandatorySource(); if (mapping.isIdentity()) { return rel; } final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> outputProjList = new ArrayList<RexNode>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); for (int i = 0; i < mapping.getTargetCount(); i++) { int source = mapping.getSource(i); final RelDataTypeField sourceField = fields.get(source); outputNameList .add(((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? sourceField .getName() : fieldNames.get(i)); outputProjList.add(rexBuilder.makeInputRef(rel, source)); } return create(rel, outputProjList, outputNameList); } @Override public Project copy(RelTraitSet traitSet, RelNode input, List<RexNode> exps, RelDataType rowType) { assert traitSet.containsIfApplicable(HiveRelNode.CONVENTION); HiveProject hp = new HiveProject(getCluster(), traitSet, input, exps, rowType, getFlags()); if (this.isSynthetic()) { hp.setSynthetic(); } return hp; } @Override public void implement(Implementor implementor) { } public List<Integer> getVirtualCols() { return virtualCols; } // TODO: this should come through RelBuilder to the constructor as opposed to // set method. This requires calcite change public void setSynthetic() { this.isSysnthetic = true; } public boolean isSynthetic() { return isSysnthetic; } //required for HiveRelDecorrelator @Override public RelNode accept(RelShuttle shuttle) { if(shuttle instanceof HiveRelShuttle) { return ((HiveRelShuttle)shuttle).visit(this); } return shuttle.visit(this); } }