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

com.hazelcast.org.apache.calcite.rel.metadata.RelMdCollation Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * 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.rel.metadata;

import com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableCorrelate;
import com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableHashJoin;
import com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableMergeJoin;
import com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableMergeUnion;
import com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableNestedLoopJoin;
import com.hazelcast.org.apache.calcite.adapter.jdbc.JdbcToEnumerableConverter;
import com.hazelcast.org.apache.calcite.linq4j.Ord;
import com.hazelcast.org.apache.calcite.plan.RelOptTable;
import com.hazelcast.org.apache.calcite.plan.hep.HepRelVertex;
import com.hazelcast.org.apache.calcite.plan.volcano.RelSubset;
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.rel.core.Calc;
import com.hazelcast.org.apache.calcite.rel.core.Filter;
import com.hazelcast.org.apache.calcite.rel.core.Join;
import com.hazelcast.org.apache.calcite.rel.core.JoinRelType;
import com.hazelcast.org.apache.calcite.rel.core.Match;
import com.hazelcast.org.apache.calcite.rel.core.Project;
import com.hazelcast.org.apache.calcite.rel.core.Sort;
import com.hazelcast.org.apache.calcite.rel.core.SortExchange;
import com.hazelcast.org.apache.calcite.rel.core.TableModify;
import com.hazelcast.org.apache.calcite.rel.core.TableScan;
import com.hazelcast.org.apache.calcite.rel.core.Values;
import com.hazelcast.org.apache.calcite.rel.core.Window;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rex.RexCall;
import com.hazelcast.org.apache.calcite.rex.RexCallBinding;
import com.hazelcast.org.apache.calcite.rex.RexInputRef;
import com.hazelcast.org.apache.calcite.rex.RexLiteral;
import com.hazelcast.org.apache.calcite.rex.RexNode;
import com.hazelcast.org.apache.calcite.rex.RexProgram;
import com.hazelcast.org.apache.calcite.sql.validate.SqlMonotonicity;
import com.hazelcast.org.apache.calcite.util.ImmutableBitSet;
import com.hazelcast.org.apache.calcite.util.ImmutableIntList;
import com.hazelcast.org.apache.calcite.util.Pair;
import com.hazelcast.org.apache.calcite.util.Util;

import com.hazelcast.com.google.common.collect.ImmutableList;
import com.hazelcast.com.google.common.collect.LinkedListMultimap;
import com.hazelcast.com.google.common.collect.Multimap;
import com.hazelcast.com.google.common.collect.Ordering;

import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * RelMdCollation supplies a default implementation of
 * {@link com.hazelcast.org.apache.calcite.rel.metadata.RelMetadataQuery#collations}
 * for the standard logical algebra.
 */
public class RelMdCollation
    implements MetadataHandler {
  public static final RelMetadataProvider SOURCE =
      ReflectiveRelMetadataProvider.reflectiveSource(
          new RelMdCollation(), BuiltInMetadata.Collation.Handler.class);

  //~ Constructors -----------------------------------------------------------

  private RelMdCollation() {}

  //~ Methods ----------------------------------------------------------------

  @Override public MetadataDef getDef() {
    return BuiltInMetadata.Collation.DEF;
  }

  /** Catch-all implementation for
   * {@link BuiltInMetadata.Collation#collations()},
   * invoked using reflection, for any relational expression not
   * handled by a more specific method.
   *
   * 

{@link com.hazelcast.org.apache.calcite.rel.core.Union}, * {@link com.hazelcast.org.apache.calcite.rel.core.Intersect}, * {@link com.hazelcast.org.apache.calcite.rel.core.Minus}, * {@link com.hazelcast.org.apache.calcite.rel.core.Join}, * {@link com.hazelcast.org.apache.calcite.rel.core.Correlate} * do not in general return sorted results * (but implementations using particular algorithms may). * * @param rel Relational expression * @return Relational expression's collations * * @see com.hazelcast.org.apache.calcite.rel.metadata.RelMetadataQuery#collations(RelNode) */ public @Nullable ImmutableList collations(RelNode rel, RelMetadataQuery mq) { return null; } private static @Nullable ImmutableList copyOf(@Nullable Collection values) { return values == null ? null : ImmutableList.copyOf(values); } public @Nullable ImmutableList collations(Window rel, RelMetadataQuery mq) { return copyOf(window(mq, rel.getInput(), rel.groups)); } public @Nullable ImmutableList collations(Match rel, RelMetadataQuery mq) { return copyOf( match(mq, rel.getInput(), rel.getRowType(), rel.getPattern(), rel.isStrictStart(), rel.isStrictEnd(), rel.getPatternDefinitions(), rel.getMeasures(), rel.getAfter(), rel.getSubsets(), rel.isAllRows(), rel.getPartitionKeys(), rel.getOrderKeys(), rel.getInterval())); } public @Nullable ImmutableList collations(Filter rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } public @Nullable ImmutableList collations(TableModify rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } public @Nullable ImmutableList collations(TableScan scan, RelMetadataQuery mq) { return copyOf(table(scan.getTable())); } public @Nullable ImmutableList collations(EnumerableMergeJoin join, RelMetadataQuery mq) { // In general a join is not sorted. But a merge join preserves the sort // order of the left and right sides. return copyOf( RelMdCollation.mergeJoin(mq, join.getLeft(), join.getRight(), join.analyzeCondition().leftKeys, join.analyzeCondition().rightKeys, join.getJoinType())); } public @Nullable ImmutableList collations(EnumerableHashJoin join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableHashJoin(mq, join.getLeft(), join.getRight(), join.getJoinType())); } public @Nullable ImmutableList collations(EnumerableNestedLoopJoin join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableNestedLoopJoin(mq, join.getLeft(), join.getRight(), join.getJoinType())); } public @Nullable ImmutableList collations(EnumerableMergeUnion mergeUnion, RelMetadataQuery mq) { final RelCollation collation = mergeUnion.getTraitSet().getCollation(); if (collation == null) { // should not happen return null; } // MergeUnion guarantees order, like a sort return copyOf(RelMdCollation.sort(collation)); } public @Nullable ImmutableList collations(EnumerableCorrelate join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableCorrelate(mq, join.getLeft(), join.getRight(), join.getJoinType())); } public @Nullable ImmutableList collations(Sort sort, RelMetadataQuery mq) { return copyOf( RelMdCollation.sort(sort.getCollation())); } public @Nullable ImmutableList collations(SortExchange sort, RelMetadataQuery mq) { return copyOf( RelMdCollation.sort(sort.getCollation())); } public @Nullable ImmutableList collations(Project project, RelMetadataQuery mq) { return copyOf( project(mq, project.getInput(), project.getProjects())); } public @Nullable ImmutableList collations(Calc calc, RelMetadataQuery mq) { return copyOf(calc(mq, calc.getInput(), calc.getProgram())); } public @Nullable ImmutableList collations(Values values, RelMetadataQuery mq) { return copyOf( values(mq, values.getRowType(), values.getTuples())); } public @Nullable ImmutableList collations(JdbcToEnumerableConverter rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } public @Nullable ImmutableList collations(HepRelVertex rel, RelMetadataQuery mq) { return mq.collations(rel.getCurrentRel()); } public @Nullable ImmutableList collations(RelSubset rel, RelMetadataQuery mq) { return copyOf( Objects.requireNonNull( rel.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE))); } // Helper methods /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.TableScan}'s collation. */ public static @Nullable List table(RelOptTable table) { return table.getCollationList(); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Snapshot}'s collation. */ public static @Nullable List snapshot(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Sort}'s collation. */ public static List sort(RelCollation collation) { return ImmutableList.of(collation); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Filter}'s collation. */ public static @Nullable List filter(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } /** Helper method to determine a * limit's collation. */ public static @Nullable List limit(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Calc}'s collation. */ public static @Nullable List calc(RelMetadataQuery mq, RelNode input, RexProgram program) { final List projects = program .getProjectList() .stream() .map(program::expandLocalRef) .collect(Collectors.toList()); return project(mq, input, projects); } /** Helper method to determine a {@link Project}'s collation. */ public static @Nullable List project(RelMetadataQuery mq, RelNode input, List projects) { final NavigableSet collations = new TreeSet<>(); final List inputCollations = mq.collations(input); if (inputCollations == null || inputCollations.isEmpty()) { return ImmutableList.of(); } final Multimap targets = LinkedListMultimap.create(); final Map targetsWithMonotonicity = new HashMap<>(); for (Ord project : Ord.zip(projects)) { if (project.e instanceof RexInputRef) { targets.put(((RexInputRef) project.e).getIndex(), project.i); } else if (project.e instanceof RexCall) { final RexCall call = (RexCall) project.e; final RexCallBinding binding = RexCallBinding.create(input.getCluster().getTypeFactory(), call, inputCollations); targetsWithMonotonicity.put(project.i, call.getOperator().getMonotonicity(binding)); } } final List fieldCollations = new ArrayList<>(); loop: for (RelCollation ic : inputCollations) { if (ic.getFieldCollations().isEmpty()) { continue; } fieldCollations.clear(); for (RelFieldCollation ifc : ic.getFieldCollations()) { final Collection integers = targets.get(ifc.getFieldIndex()); if (integers.isEmpty()) { continue loop; // cannot do this collation } fieldCollations.add(ifc.withFieldIndex(integers.iterator().next())); } assert !fieldCollations.isEmpty(); collations.add(RelCollations.of(fieldCollations)); } final List fieldCollationsForRexCalls = new ArrayList<>(); for (Map.Entry entry : targetsWithMonotonicity.entrySet()) { final SqlMonotonicity value = entry.getValue(); switch (value) { case NOT_MONOTONIC: case CONSTANT: break; default: fieldCollationsForRexCalls.add( new RelFieldCollation(entry.getKey(), RelFieldCollation.Direction.of(value))); break; } } if (!fieldCollationsForRexCalls.isEmpty()) { collations.add(RelCollations.of(fieldCollationsForRexCalls)); } return copyOf(collations); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Window}'s collation. * *

A Window projects the fields of its input first, followed by the output * from each of its windows. Assuming (quite reasonably) that the * implementation does not re-order its input rows, then any collations of its * input are preserved. */ public static @Nullable List window(RelMetadataQuery mq, RelNode input, ImmutableList groups) { return mq.collations(input); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Match}'s collation. */ public static @Nullable List match(RelMetadataQuery mq, RelNode input, RelDataType rowType, RexNode pattern, boolean strictStart, boolean strictEnd, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, @Nullable RexNode interval) { return mq.collations(input); } /** Helper method to determine a * {@link com.hazelcast.org.apache.calcite.rel.core.Values}'s collation. * *

We actually under-report the collations. A Values with 0 or 1 rows - an * edge case, but legitimate and very common - is ordered by every permutation * of every subset of the columns. * *

So, our algorithm aims to:

    *
  • produce at most N collations (where N is the number of columns); *
  • make each collation as long as possible; *
  • do not repeat combinations already emitted - * if we've emitted {@code (a, b)} do not later emit {@code (b, a)}; *
  • probe the actual values and make sure that each collation is * consistent with the data *
* *

So, for an empty Values with 4 columns, we would emit * {@code (a, b, c, d), (b, c, d), (c, d), (d)}. */ public static List values(RelMetadataQuery mq, RelDataType rowType, ImmutableList> tuples) { Util.discard(mq); // for future use final List list = new ArrayList<>(); final int n = rowType.getFieldCount(); final List>>> pairs = new ArrayList<>(); outer: for (int i = 0; i < n; i++) { pairs.clear(); for (int j = i; j < n; j++) { final RelFieldCollation fieldCollation = new RelFieldCollation(j); Ordering> comparator = comparator(fieldCollation); Ordering> ordering; if (pairs.isEmpty()) { ordering = comparator; } else { ordering = Util.last(pairs).right.compound(comparator); } pairs.add(Pair.of(fieldCollation, ordering)); if (!ordering.isOrdered(tuples)) { if (j == i) { continue outer; } pairs.remove(pairs.size() - 1); } } if (!pairs.isEmpty()) { list.add(RelCollations.of(Pair.left(pairs))); } } return list; } public static Ordering> comparator( RelFieldCollation fieldCollation) { final int nullComparison = fieldCollation.nullDirection.nullComparison; final int x = fieldCollation.getFieldIndex(); switch (fieldCollation.direction) { case ASCENDING: return new Ordering>() { @Override public int compare(List o1, List o2) { final Comparable c1 = o1.get(x).getValueAs(Comparable.class); final Comparable c2 = o2.get(x).getValueAs(Comparable.class); return RelFieldCollation.compare(c1, c2, nullComparison); } }; default: return new Ordering>() { @Override public int compare(List o1, List o2) { final Comparable c1 = o1.get(x).getValueAs(Comparable.class); final Comparable c2 = o2.get(x).getValueAs(Comparable.class); return RelFieldCollation.compare(c2, c1, -nullComparison); } }; } } /** Helper method to determine a {@link Join}'s collation assuming that it * uses a merge-join algorithm. * *

If the inputs are sorted on other keys in addition to the join * key, the result preserves those collations too. * @deprecated Use {@link #mergeJoin(RelMetadataQuery, RelNode, RelNode, ImmutableIntList, ImmutableIntList, JoinRelType)} */ @Deprecated // to be removed before 2.0 public static @Nullable List mergeJoin(RelMetadataQuery mq, RelNode left, RelNode right, ImmutableIntList leftKeys, ImmutableIntList rightKeys) { return mergeJoin(mq, left, right, leftKeys, rightKeys, JoinRelType.INNER); } /** Helper method to determine a {@link Join}'s collation assuming that it * uses a merge-join algorithm. * *

If the inputs are sorted on other keys in addition to the join * key, the result preserves those collations too. */ public static @Nullable List mergeJoin(RelMetadataQuery mq, RelNode left, RelNode right, ImmutableIntList leftKeys, ImmutableIntList rightKeys, JoinRelType joinType) { assert EnumerableMergeJoin.isMergeJoinSupported(joinType) : "EnumerableMergeJoin unsupported for join type " + joinType; final ImmutableList leftCollations = mq.collations(left); if (!joinType.projectsRight()) { return leftCollations; } if (leftCollations == null) { return null; } final ImmutableList rightCollations = mq.collations(right); if (rightCollations == null) { return leftCollations; } final ImmutableList.Builder builder = ImmutableList.builder(); builder.addAll(leftCollations); final int leftFieldCount = left.getRowType().getFieldCount(); for (RelCollation collation : rightCollations) { builder.add(RelCollations.shift(collation, leftFieldCount)); } return builder.build(); } /** * Returns the collation of {@link EnumerableHashJoin} based on its inputs and the join type. */ public static @Nullable List enumerableHashJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { if (joinType == JoinRelType.SEMI) { return enumerableSemiJoin(mq, left, right); } else { return enumerableJoin0(mq, left, right, joinType); } } /** * Returns the collation of {@link EnumerableNestedLoopJoin} * based on its inputs and the join type. */ public static @Nullable List enumerableNestedLoopJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { return enumerableJoin0(mq, left, right, joinType); } public static @Nullable List enumerableCorrelate(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } public static @Nullable List enumerableSemiJoin(RelMetadataQuery mq, RelNode left, RelNode right) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } @SuppressWarnings("unused") public static @Nullable List enumerableBatchNestedLoopJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } @SuppressWarnings("unused") private static @Nullable List enumerableJoin0(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation can preserve the sort order of the left input if one of the // following conditions hold: // (i) join type is INNER or LEFT; // (ii) RelCollation always orders nulls last. final ImmutableList leftCollations = mq.collations(left); if (leftCollations == null) { return null; } switch (joinType) { case SEMI: case ANTI: case INNER: case LEFT: return leftCollations; case RIGHT: case FULL: for (RelCollation collation : leftCollations) { for (RelFieldCollation field : collation.getFieldCollations()) { if (!(RelFieldCollation.NullDirection.LAST == field.nullDirection)) { return null; } } } return leftCollations; default: break; } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy