All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.sql.impl.calcite.opt.physical.ProjectPhysicalRule Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2021, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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 com.hazelcast.sql.impl.calcite.opt.physical;

import com.hazelcast.sql.impl.calcite.opt.HazelcastConventions;
import com.hazelcast.sql.impl.calcite.opt.OptUtils;
import com.hazelcast.sql.impl.calcite.opt.distribution.DistributionTrait;
import com.hazelcast.sql.impl.calcite.opt.distribution.DistributionType;
import com.hazelcast.sql.impl.calcite.opt.logical.ProjectLogicalRel;
import com.hazelcast.org.apache.calcite.plan.RelOptRule;
import com.hazelcast.org.apache.calcite.plan.RelOptRuleCall;
import com.hazelcast.org.apache.calcite.plan.RelTraitSet;
import com.hazelcast.org.apache.calcite.rel.RelCollation;
import com.hazelcast.org.apache.calcite.rel.RelCollationTraitDef;
import com.hazelcast.org.apache.calcite.rel.RelCollations;
import com.hazelcast.org.apache.calcite.rel.RelFieldCollation;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rex.RexInputRef;
import com.hazelcast.org.apache.calcite.rex.RexNode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.hazelcast.sql.impl.calcite.opt.distribution.DistributionType.PARTITIONED;

/**
 * This rule converts logical projection into physical projection. Physical projection inherits distribution and collation of
 * the input provided that input columns responsible for distribution/collation are present in the project operator.
 */
public final class ProjectPhysicalRule extends RelOptRule {
    public static final RelOptRule INSTANCE = new ProjectPhysicalRule();

    private ProjectPhysicalRule() {
        super(
            OptUtils.parentChild(ProjectLogicalRel.class, RelNode.class, HazelcastConventions.LOGICAL),
            ProjectPhysicalRule.class.getSimpleName()
        );
    }

    @Override
    public void onMatch(RelOptRuleCall call) {
        ProjectLogicalRel logicalProject = call.rel(0);
        RelNode input = logicalProject.getInput();

        Collection transforms = getTransforms(logicalProject.getProjects(), input);

        for (InputAndTraitSet transform : transforms) {
            ProjectPhysicalRel newProject = new ProjectPhysicalRel(
                logicalProject.getCluster(),
                transform.getTraitSet(),
                transform.getInput(),
                logicalProject.getProjects(),
                logicalProject.getRowType()
            );

            call.transformTo(newProject);
        }
    }

    /**
     * Get conversions which will be applied to the given logical project.
     *
     * @param input Input.
     * @return Conversions (converted input + trait set).
     */
    private static Collection getTransforms(List projects, RelNode input) {
        List res = new ArrayList<>(1);

        Collection physicalInputs = OptUtils.getPhysicalRelsFromSubset(input);

        // Initialize a field index to the project index map
        Map inputFieldIndex2ProjectIndexMap = new HashMap<>();
        for (int i = 0; i < projects.size(); ++i) {
            RexNode projectExp = projects.get(i);
            if (projectExp instanceof RexInputRef) {
                inputFieldIndex2ProjectIndexMap.put(((RexInputRef) projectExp).getIndex(), i);
            }
        }

        for (RelNode physicalInput : physicalInputs) {
            RelTraitSet traitSet = createPhysicalTraitSet(physicalInput);
            RelCollation transformedCollation =
                convertCollation(inputFieldIndex2ProjectIndexMap, traitSet.getTrait(RelCollationTraitDef.INSTANCE));
            RelTraitSet finalTraitSet = OptUtils.traitPlus(traitSet, transformedCollation);

            res.add(new InputAndTraitSet(physicalInput, finalTraitSet));
        }

        return res;
    }

    /**
     * Transforms the collation remapping the collation fields in accordance with the
     * project fields.
     *
     * @param inputFieldIndex2ProjectIndex a mapping from the input index field to the project index
     * @param collation                    an initial collation
     * @return the transformed collation
     */
    private static RelCollation convertCollation(Map inputFieldIndex2ProjectIndex, RelCollation collation) {
        List transformedFields = new ArrayList<>(collation.getFieldCollations().size());

        for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
            int fieldIndex = fieldCollation.getFieldIndex();
            Integer projectFiledIndex = inputFieldIndex2ProjectIndex.get(fieldIndex);
            if (projectFiledIndex == null) {
                // Project removes the field and we loose a sorting property
                // for the subsequent fields in the collation
                break;
            }
            RelFieldCollation transformedFieldCollation =
                new RelFieldCollation(projectFiledIndex, fieldCollation.getDirection());
            transformedFields.add(transformedFieldCollation);
        }

        return RelCollations.of(transformedFields);
    }

    /**
     * Create a trait set for physical project.
     *
     * @param physicalInput Project's input.
     * @return Trait set.
     */
    private static RelTraitSet createPhysicalTraitSet(RelNode physicalInput) {
        return OptUtils.traitPlus(physicalInput.getTraitSet(), deriveDistribution(physicalInput));
    }

    /**
     * Get distribution trait which should be used by project based on the distribution of its input.
     *
     * @param physicalInput Physical input.
     * @return Distribution which should be used by project.
     */
    @SuppressWarnings({"checkstyle:RegexpSingleline", "SwitchStatementWithTooFewBranches"})
    private static DistributionTrait deriveDistribution(RelNode physicalInput) {
        DistributionTrait physicalInputDist = OptUtils.getDistribution(physicalInput);

        DistributionType type = physicalInputDist.getType();

        switch (type) {
            case ROOT:
                // Singleton remains singleton.
                return physicalInputDist;

            case REPLICATED:
                // Replicated remains replicated.
                return physicalInputDist;

            default:
                assert type == PARTITIONED;

                return physicalInputDist;
        }
    }

    /**
     * A pair of input and trait set which should be used for transformation.
     */
    private static final class InputAndTraitSet {
        /**
         * Input of the projection.
         */
        private final RelNode input;

        /**
         * Trait set of the projection.
         */
        private final RelTraitSet traitSet;

        private InputAndTraitSet(RelNode input, RelTraitSet traitSet) {
            this.input = input;
            this.traitSet = traitSet;
        }

        public RelNode getInput() {
            return input;
        }

        public RelTraitSet getTraitSet() {
            return traitSet;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy