blob: 189b776e66578afaface598a80830670d9a6a271 [file] [log] [blame]
/*
* 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.calcite.rel;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.mapping.Mappings;
import com.google.common.collect.ImmutableList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Utilities concerning {@link org.apache.calcite.rel.RelCollation}
* and {@link org.apache.calcite.rel.RelFieldCollation}.
*/
public class RelCollations {
/**
* A collation indicating that a relation is not sorted. Ordering by no
* columns.
*/
public static final RelCollation EMPTY =
RelCollationTraitDef.INSTANCE.canonize(
new RelCollationImpl(ImmutableList.of()));
/**
* A collation that cannot be replicated by applying a sort. The only
* implementation choice is to apply operations that preserve order.
*/
@Deprecated // to be removed before 2.0
public static final RelCollation PRESERVE =
RelCollationTraitDef.INSTANCE.canonize(
new RelCollationImpl(
ImmutableList.of(new RelFieldCollation(-1))) {
@Override public String toString() {
return "PRESERVE";
}
});
private RelCollations() {}
public static RelCollation of(RelFieldCollation... fieldCollations) {
return of(ImmutableList.copyOf(fieldCollations));
}
public static RelCollation of(List<RelFieldCollation> fieldCollations) {
RelCollation collation;
if (Util.isDistinct(ordinals(fieldCollations))) {
collation = new RelCollationImpl(ImmutableList.copyOf(fieldCollations));
} else {
// Remove field collations whose field has already been seen
final ImmutableList.Builder<RelFieldCollation> builder =
ImmutableList.builder();
final Set<Integer> set = new HashSet<>();
for (RelFieldCollation fieldCollation : fieldCollations) {
if (set.add(fieldCollation.getFieldIndex())) {
builder.add(fieldCollation);
}
}
collation = new RelCollationImpl(builder.build());
}
return RelCollationTraitDef.INSTANCE.canonize(collation);
}
/**
* Creates a collation containing one field.
*/
public static RelCollation of(int fieldIndex) {
return of(new RelFieldCollation(fieldIndex));
}
/**
* Creates a collation containing multiple fields.
*/
public static RelCollation of(ImmutableIntList keys) {
List<RelFieldCollation> cols = keys.stream()
.map(k -> new RelFieldCollation(k))
.collect(Collectors.toList());
return of(cols);
}
/**
* Creates a list containing one collation containing one field.
*/
public static List<RelCollation> createSingleton(int fieldIndex) {
return ImmutableList.of(of(fieldIndex));
}
/**
* Checks that a collection of collations is valid.
*
* @param rowType Row type of the relational expression
* @param collationList List of collations
* @param fail Whether to fail if invalid
* @return Whether valid
*/
public static boolean isValid(
RelDataType rowType,
List<RelCollation> collationList,
boolean fail) {
final int fieldCount = rowType.getFieldCount();
for (RelCollation collation : collationList) {
for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
final int index = fieldCollation.getFieldIndex();
if (index < 0 || index >= fieldCount) {
assert !fail;
return false;
}
}
}
return true;
}
public static boolean equal(
List<RelCollation> collationList1,
List<RelCollation> collationList2) {
return collationList1.equals(collationList2);
}
/** Returns the indexes of the field collations in a given collation. */
public static List<Integer> ordinals(RelCollation collation) {
return ordinals(collation.getFieldCollations());
}
/** Returns the indexes of the fields in a list of field collations. */
public static List<Integer> ordinals(
List<RelFieldCollation> fieldCollations) {
return Util.transform(fieldCollations, RelFieldCollation::getFieldIndex);
}
/** Returns whether a collation indicates that the collection is sorted on
* a given list of keys.
*
* @param collation Collation
* @param keys List of keys
* @return Whether the collection is sorted on the given keys
*/
public static boolean contains(RelCollation collation,
Iterable<Integer> keys) {
return contains(collation, Util.distinctList(keys));
}
private static boolean contains(RelCollation collation,
List<Integer> keys) {
final int n = collation.getFieldCollations().size();
final Iterator<Integer> iterator = keys.iterator();
for (int i = 0; i < n; i++) {
final RelFieldCollation fieldCollation =
collation.getFieldCollations().get(i);
if (!iterator.hasNext()) {
return true;
}
if (fieldCollation.getFieldIndex() != iterator.next()) {
return false;
}
}
return !iterator.hasNext();
}
/** Returns whether one of a list of collations indicates that the collection
* is sorted on the given list of keys. */
public static boolean contains(List<RelCollation> collations,
ImmutableIntList keys) {
final List<Integer> distinctKeys = Util.distinctList(keys);
for (RelCollation collation : collations) {
if (contains(collation, distinctKeys)) {
return true;
}
}
return false;
}
/** Returns whether a collation contains a given list of keys regardless
* the order.
*
* @param collation Collation
* @param keys List of keys
* @return Whether the collection contains the given keys
*/
public static boolean containsOrderless(RelCollation collation,
List<Integer> keys) {
final List<Integer> distinctKeys = Util.distinctList(keys);
final ImmutableBitSet keysBitSet = ImmutableBitSet.of(distinctKeys);
List<Integer> colKeys = Util.distinctList(collation.getKeys());
if (colKeys.size() < distinctKeys.size()) {
return false;
} else {
ImmutableBitSet bitset = ImmutableBitSet.of(
colKeys.subList(0, distinctKeys.size()));
return bitset.equals(keysBitSet);
}
}
/** Returns whether a collation is contained by a given list of keys regardless ordering.
*
* @param collation Collation
* @param keys List of keys
* @return Whether the collection contains the given keys
*/
public static boolean containsOrderless(
List<Integer> keys, RelCollation collation) {
final List<Integer> distinctKeys = Util.distinctList(keys);
List<Integer> colKeys = Util.distinctList(collation.getKeys());
if (colKeys.size() > distinctKeys.size()) {
return false;
} else {
return colKeys.stream().allMatch(i -> distinctKeys.contains(i));
}
}
/**
* Returns whether one of a list of collations contains the given list of keys
* regardless the order.
*/
public static boolean collationsContainKeysOrderless(
List<RelCollation> collations, List<Integer> keys) {
for (RelCollation collation : collations) {
if (containsOrderless(collation, keys)) {
return true;
}
}
return false;
}
/**
* Returns whether one of a list of collations is contained by the given list of keys
* regardless the order.
*/
public static boolean keysContainCollationsOrderless(
List<Integer> keys, List<RelCollation> collations) {
for (RelCollation collation : collations) {
if (containsOrderless(keys, collation)) {
return true;
}
}
return false;
}
public static RelCollation shift(RelCollation collation, int offset) {
if (offset == 0) {
return collation; // save some effort
}
final ImmutableList.Builder<RelFieldCollation> fieldCollations =
ImmutableList.builder();
for (RelFieldCollation fc : collation.getFieldCollations()) {
fieldCollations.add(fc.shift(offset));
}
return new RelCollationImpl(fieldCollations.build());
}
/** Creates a copy of this collation that changes the ordinals of input
* fields. */
public static RelCollation permute(RelCollation collation,
Map<Integer, Integer> mapping) {
return of(
Util.transform(collation.getFieldCollations(),
fc -> fc.withFieldIndex(
requireNonNull(mapping.get(fc.getFieldIndex()),
() -> "no entry for " + fc.getFieldIndex() + " in " + mapping))));
}
/** Creates a copy of this collation that changes the ordinals of input
* fields. */
public static RelCollation permute(RelCollation collation,
Mappings.TargetMapping mapping) {
return of(
Util.transform(collation.getFieldCollations(),
fc -> fc.withFieldIndex(mapping.getTarget(fc.getFieldIndex()))));
}
}