com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableMergeUnionRule Maven / Gradle / Ivy
/*
* 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 com.hazelcast.org.apache.calcite.adapter.enumerable;
import com.hazelcast.org.apache.calcite.plan.RelOptRuleCall;
import com.hazelcast.org.apache.calcite.plan.RelRule;
import com.hazelcast.org.apache.calcite.rel.RelCollation;
import com.hazelcast.org.apache.calcite.rel.RelFieldCollation;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.core.Sort;
import com.hazelcast.org.apache.calcite.rel.core.Union;
import com.hazelcast.org.apache.calcite.rel.logical.LogicalSort;
import com.hazelcast.org.apache.calcite.rel.logical.LogicalUnion;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.org.apache.calcite.rex.RexLiteral;
import com.hazelcast.org.apache.calcite.rex.RexNode;
import com.hazelcast.org.apache.calcite.tools.RelBuilder;
import com.hazelcast.org.apache.calcite.util.ImmutableBitSet;
import org.immutables.value.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Rule to convert a {@link com.hazelcast.org.apache.calcite.rel.logical.LogicalSort} on top of a
* {@link com.hazelcast.org.apache.calcite.rel.logical.LogicalUnion} into a {@link EnumerableMergeUnion}.
*
* @see EnumerableRules#ENUMERABLE_MERGE_UNION_RULE
*/
@Value.Enclosing
public class EnumerableMergeUnionRule extends RelRule {
/** Rule configuration. */
@Value.Immutable
public interface Config extends RelRule.Config {
Config DEFAULT_CONFIG = ImmutableEnumerableMergeUnionRule.Config.of()
.withDescription("EnumerableMergeUnionRule").withOperandSupplier(
b0 -> b0.operand(LogicalSort.class).oneInput(
b1 -> b1.operand(LogicalUnion.class).anyInputs()));
@Override default EnumerableMergeUnionRule toRule() {
return new EnumerableMergeUnionRule(this);
}
}
public EnumerableMergeUnionRule(Config config) {
super(config);
}
@Override public boolean matches(RelOptRuleCall call) {
final Sort sort = call.rel(0);
final RelCollation collation = sort.getCollation();
if (collation == null || collation.getFieldCollations().isEmpty()) {
return false;
}
final Union union = call.rel(1);
if (union.getInputs().size() < 2) {
return false;
}
return true;
}
@Override public void onMatch(RelOptRuleCall call) {
final Sort sort = call.rel(0);
final RelCollation collation = sort.getCollation();
final Union union = call.rel(1);
final int unionInputsSize = union.getInputs().size();
// Push down sort limit, if possible.
RexNode inputFetch = null;
if (sort.fetch != null) {
if (sort.offset == null) {
inputFetch = sort.fetch;
} else if (sort.fetch instanceof RexLiteral && sort.offset instanceof RexLiteral) {
inputFetch = call.builder().literal(
RexLiteral.intValue(sort.fetch) + RexLiteral.intValue(sort.offset));
}
}
final RelBuilder builder = call.builder();
final List unionFieldList = union.getRowType().getFieldList();
final List inputs = new ArrayList<>(unionInputsSize);
for (RelNode input : union.getInputs()) {
// Check if type collations match, otherwise store it in this bitset to generate a cast later
// to guarantee that all inputs will be sorted in the same way
final ImmutableBitSet.Builder fieldsRequiringCastBuilder = ImmutableBitSet.builder();
for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
final int index = fieldCollation.getFieldIndex();
final RelDataType unionType = unionFieldList.get(index).getType();
final RelDataType inputType = input.getRowType().getFieldList().get(index).getType();
if (!Objects.equals(unionType.getCollation(), inputType.getCollation())) {
fieldsRequiringCastBuilder.set(index);
}
}
final ImmutableBitSet fieldsRequiringCast = fieldsRequiringCastBuilder.build();
final RelNode unsortedInput;
if (fieldsRequiringCast.isEmpty()) {
unsortedInput = input;
} else {
// At least one cast is required, generate the corresponding projection
builder.push(input);
final List fields = builder.fields();
final List projFields = new ArrayList<>(fields.size());
for (int i = 0; i < fields.size(); i++) {
RexNode node = fields.get(i);
if (fieldsRequiringCast.get(i)) {
final RelDataType targetType = unionFieldList.get(i).getType();
node = builder.getRexBuilder().makeCast(targetType, node);
}
projFields.add(node);
}
builder.project(projFields);
unsortedInput = builder.build();
}
final RelNode newInput =
sort.copy(sort.getTraitSet(), unsortedInput, collation, null, inputFetch);
inputs.add(
convert(newInput, newInput.getTraitSet().replace(EnumerableConvention.INSTANCE)));
}
RelNode result = EnumerableMergeUnion.create(sort.getCollation(), inputs, union.all);
// If Sort contained a LIMIT / OFFSET, then put it back as an EnumerableLimit.
// The output of the MergeUnion is already sorted, so we do not need a sort anymore.
if (sort.offset != null || sort.fetch != null) {
result = EnumerableLimit.create(result, sort.offset, sort.fetch);
}
call.transformTo(result);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy