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

com.hazelcast.shaded.org.apache.calcite.rel.AbstractRelNode 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.shaded.org.apache.calcite.rel;

import com.hazelcast.shaded.org.apache.calcite.plan.Convention;
import com.hazelcast.shaded.org.apache.calcite.plan.ConventionTraitDef;
import com.hazelcast.shaded.org.apache.calcite.plan.RelDigest;
import com.hazelcast.shaded.org.apache.calcite.plan.RelOptCluster;
import com.hazelcast.shaded.org.apache.calcite.plan.RelOptCost;
import com.hazelcast.shaded.org.apache.calcite.plan.RelOptPlanner;
import com.hazelcast.shaded.org.apache.calcite.plan.RelOptTable;
import com.hazelcast.shaded.org.apache.calcite.plan.RelOptUtil;
import com.hazelcast.shaded.org.apache.calcite.plan.RelTraitSet;
import com.hazelcast.shaded.org.apache.calcite.rel.core.CorrelationId;
import com.hazelcast.shaded.org.apache.calcite.rel.hint.Hintable;
import com.hazelcast.shaded.org.apache.calcite.rel.hint.RelHint;
import com.hazelcast.shaded.org.apache.calcite.rel.metadata.Metadata;
import com.hazelcast.shaded.org.apache.calcite.rel.metadata.MetadataFactory;
import com.hazelcast.shaded.org.apache.calcite.rel.metadata.RelMetadataQuery;
import com.hazelcast.shaded.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.shaded.org.apache.calcite.rex.RexShuttle;
import com.hazelcast.shaded.org.apache.calcite.runtime.PairList;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlExplainLevel;
import com.hazelcast.shaded.org.apache.calcite.util.Litmus;
import com.hazelcast.shaded.org.apache.calcite.util.Pair;
import com.hazelcast.shaded.org.apache.calcite.util.Util;

import com.hazelcast.shaded.com.google.common.collect.ImmutableSet;

import org.apiguardian.api.API;
import com.hazelcast.shaded.org.checkerframework.checker.initialization.qual.UnknownInitialization;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import com.hazelcast.shaded.org.checkerframework.dataflow.qual.Pure;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import static java.util.Objects.requireNonNull;

/**
 * Base class for every relational expression ({@link RelNode}).
 */
public abstract class AbstractRelNode implements RelNode {
  //~ Static fields/initializers ---------------------------------------------

  /** Generator for {@link #id} values. */
  private static final AtomicInteger NEXT_ID = new AtomicInteger(0);

  //~ Instance fields --------------------------------------------------------

  /**
   * Cached type of this relational expression.
   */
  protected @MonotonicNonNull RelDataType rowType;

  /**
   * The digest that uniquely identifies the node.
   */
  @API(since = "1.24", status = API.Status.INTERNAL)
  protected RelDigest digest;

  private final RelOptCluster cluster;

  /** Unique id of this object, for debugging. */
  protected final int id;

  /** RelTraitSet that describes the traits of this RelNode. */
  protected RelTraitSet traitSet;

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

  /**
   * Creates an AbstractRelNode.
   */
  protected AbstractRelNode(RelOptCluster cluster, RelTraitSet traitSet) {
    super();
    assert cluster != null;
    this.cluster = cluster;
    this.traitSet = traitSet;
    this.id = NEXT_ID.getAndIncrement();
    this.digest = new InnerRelDigest();
  }

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

  @Override public RelNode copy(RelTraitSet traitSet, List inputs) {
    // Note that empty set equals empty set, so relational expressions
    // with zero inputs do not generally need to implement their own copy
    // method.
    if (getInputs().equals(inputs)
        && traitSet == getTraitSet()) {
      return this;
    }
    throw new AssertionError("Relational expression should override copy. "
        + "Class=[" + getClass()
        + "]; traits=[" + getTraitSet()
        + "]; desired traits=[" + traitSet
        + "]");
  }

  protected static  T sole(List collection) {
    assert collection.size() == 1;
    return collection.get(0);
  }

  @Override public final RelOptCluster getCluster() {
    return cluster;
  }

  @Pure
  @Override public final @Nullable Convention getConvention(
      @UnknownInitialization AbstractRelNode this) {
    return traitSet == null ? null : traitSet.getTrait(ConventionTraitDef.INSTANCE);
  }

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

  @Override public @Nullable String getCorrelVariable() {
    return null;
  }

  @Override public int getId() {
    return id;
  }

  @Override public RelNode getInput(int i) {
    List inputs = getInputs();
    return inputs.get(i);
  }

  @Override public void register(RelOptPlanner planner) {
    Util.discard(planner);
  }

  // It is not recommended to override this method, but sub-classes can do it at their own risk.
  @Override public String getRelTypeName() {
    String cn = getClass().getName();
    int i = cn.length();
    while (--i >= 0) {
      if (cn.charAt(i) == '$' || cn.charAt(i) == '.') {
        return cn.substring(i + 1);
      }
    }
    return cn;
  }

  @Override public boolean isValid(Litmus litmus, @Nullable Context context) {
    return litmus.succeed();
  }

  @Override public final RelDataType getRowType() {
    if (rowType == null) {
      rowType = deriveRowType();
      assert rowType != null : this;
    }
    return rowType;
  }

  protected RelDataType deriveRowType() {
    // This method is only called if rowType is null, so you don't NEED to
    // implement it if rowType is always set.
    throw new UnsupportedOperationException();
  }

  @Override public RelDataType getExpectedInputRowType(int ordinalInParent) {
    return getRowType();
  }

  @Override public List getInputs() {
    return Collections.emptyList();
  }

  @Override public double estimateRowCount(RelMetadataQuery mq) {
    return 1.0;
  }

  @Override public Set getVariablesSet() {
    return ImmutableSet.of();
  }

  @Override public void collectVariablesUsed(Set variableSet) {
    // for default case, nothing to do
  }

  @Override public boolean isEnforcer() {
    return false;
  }

  @Override public void collectVariablesSet(Set variableSet) {
  }

  @Override public void childrenAccept(RelVisitor visitor) {
    List inputs = getInputs();
    for (int i = 0; i < inputs.size(); i++) {
      visitor.visit(inputs.get(i), i, this);
    }
  }

  @Override public RelNode accept(RelShuttle shuttle) {
    // Call fall-back method. Specific logical types (such as LogicalProject
    // and LogicalJoin) have their own RelShuttle.visit methods.
    return shuttle.visit(this);
  }

  @Override public RelNode accept(RexShuttle shuttle) {
    return this;
  }

  @Override public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner,
      RelMetadataQuery mq) {
    // by default, assume cost is proportional to number of rows
    double rowCount = mq.getRowCount(this);
    return planner.getCostFactory().makeCost(rowCount, rowCount, 0);
  }

  @Deprecated // to be removed before 2.0
  @Override public final <@Nullable M extends @Nullable Metadata> M metadata(Class metadataClass,
      RelMetadataQuery mq) {
    final MetadataFactory factory = cluster.getMetadataFactory();
    final M metadata = factory.query(this, mq, metadataClass);
    assert metadata != null
        : "no provider found (rel=" + this + ", m=" + metadataClass
        + "); a backstop provider is recommended";
    // Usually the metadata belongs to the rel that created it. RelSubset and
    // HepRelVertex are notable exceptions, so disable the assert. It's not
    // worth the performance hit to override this method for them.
    //   assert metadata.rel() == this : "someone else's metadata";
    return metadata;
  }

  @Override public void explain(RelWriter pw) {
    explainTerms(pw).done(this);
  }

  /**
   * Describes the inputs and attributes of this relational expression.
   * Each node should call {@code super.explainTerms}, then call the
   * {@link com.hazelcast.shaded.org.apache.calcite.rel.externalize.RelWriterImpl#input(String, RelNode)}
   * and
   * {@link RelWriter#item(String, Object)}
   * methods for each input and attribute.
   *
   * @param pw Plan writer
   * @return Plan writer for fluent-explain pattern
   */
  public RelWriter explainTerms(RelWriter pw) {
    return pw;
  }

  @Override public RelNode onRegister(RelOptPlanner planner) {
    List oldInputs = getInputs();
    List inputs = new ArrayList<>(oldInputs.size());
    for (final RelNode input : oldInputs) {
      RelNode e = planner.ensureRegistered(input, null);
      assert e == input || RelOptUtil.equal("rowtype of rel before registration",
          input.getRowType(),
          "rowtype of rel after registration",
          e.getRowType(),
          Litmus.THROW);
      inputs.add(e);
    }
    RelNode r = this;
    if (!Util.equalShallow(oldInputs, inputs)) {
      r = copy(getTraitSet(), inputs);
    }
    r.recomputeDigest();
    assert r.isValid(Litmus.THROW, null);
    return r;
  }

  @Override public void recomputeDigest() {
    digest.clear();
  }

  @Override public void replaceInput(
      int ordinalInParent,
      RelNode p) {
    throw new UnsupportedOperationException("replaceInput called on " + this);
  }

  /** Description; consists of id plus digest. */
  @Override public String toString() {
    return "rel#" + id + ':' + getDigest();
  }

  @Deprecated // to be removed before 2.0
  @Override public final String getDescription() {
    return this.toString();
  }

  @Override public String getDigest() {
    return digest.toString();
  }

  @Override public final RelDigest getRelDigest() {
    return digest;
  }

  @Override public @Nullable RelOptTable getTable() {
    return null;
  }

  /**
   * {@inheritDoc}
   *
   * 

This method (and {@link #hashCode} is intentionally final. We do not want * sub-classes of {@link RelNode} to redefine identity. Various algorithms * (e.g. visitors, planner) can define the identity as meets their needs. */ @Override public final boolean equals(@Nullable Object obj) { return super.equals(obj); } /** * {@inheritDoc} * *

This method (and {@link #equals} is intentionally final. We do not want * sub-classes of {@link RelNode} to redefine identity. Various algorithms * (e.g. visitors, planner) can define the identity as meets their needs. */ @Override public final int hashCode() { return super.hashCode(); } /** * Equality check for RelNode digest. * *

By default this method collects digest attributes from * {@link #explainTerms(RelWriter)}, then compares each attribute pair. * This should work well for most cases. If this method is a performance * bottleneck for your project, or the default behavior can't handle * your scenario properly, you can choose to override this method and * {@link #deepHashCode()}. See {@code LogicalJoin} as an example. * * @return Whether the 2 RelNodes are equivalent or have the same digest. * @see #deepHashCode() */ @API(since = "1.25", status = API.Status.MAINTAINED) @Override public boolean deepEquals(@Nullable Object obj) { if (this == obj) { return true; } if (obj == null || this.getClass() != obj.getClass()) { return false; } AbstractRelNode that = (AbstractRelNode) obj; boolean result = this.getTraitSet().equals(that.getTraitSet()) && this.getRowType().equalsSansFieldNames(that.getRowType()); if (!result) { return false; } PairList items1 = this.getDigestItems(); PairList items2 = that.getDigestItems(); if (items1.size() != items2.size()) { return false; } for (int i = 0; result && i < items1.size(); i++) { Map.Entry attr1 = items1.get(i); Map.Entry attr2 = items2.get(i); if (attr1.getValue() instanceof RelNode) { result = ((RelNode) attr1.getValue()).deepEquals(attr2.getValue()); } else { result = attr1.equals(attr2); } } return result; } /** * Compute hash code for RelNode digest. * * @see RelNode#deepEquals(Object) */ @API(since = "1.25", status = API.Status.MAINTAINED) @Override public int deepHashCode() { int result = 31 + getTraitSet().hashCode(); PairList items = this.getDigestItems(); for (@Nullable Object value : items.rightList()) { final int h; if (value == null) { h = 0; } else if (value instanceof RelNode) { h = ((RelNode) value).deepHashCode(); } else { h = value.hashCode(); } result = result * 31 + h; } return result; } private PairList getDigestItems() { RelDigestWriter rdw = new RelDigestWriter(); explainTerms(rdw); if (this instanceof Hintable) { List hints = ((Hintable) this).getHints(); rdw.itemIf("hints", hints, !hints.isEmpty()); } return rdw.attrs; } /** Implementation of {@link RelDigest}. */ private class InnerRelDigest implements RelDigest { /** Cached hash code. */ private int hash = 0; @Override public RelNode getRel() { return AbstractRelNode.this; } @Override public void clear() { hash = 0; } @Override public boolean equals(final @Nullable Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final InnerRelDigest relDigest = (InnerRelDigest) o; return deepEquals(relDigest.getRel()); } @Override public int hashCode() { if (hash == 0) { hash = deepHashCode(); } return hash; } @Override public String toString() { RelDigestWriter rdw = new RelDigestWriter(); explain(rdw); return requireNonNull(rdw.digest, "digest"); } } /** * A writer object used exclusively for computing the digest of a RelNode. * *

The writer is meant to be used only for computing a single digest and * then thrown away. After calling {@link #done(RelNode)} the writer should * be used only to obtain the computed {@link #digest}. Any other action is * prohibited. */ private static final class RelDigestWriter implements RelWriter { private final PairList attrs = PairList.of(); @Nullable String digest = null; @Override public void explain(final RelNode rel, final List> valueList) { throw new IllegalStateException("Should not be called for computing digest"); } @Override public SqlExplainLevel getDetailLevel() { return SqlExplainLevel.DIGEST_ATTRIBUTES; } @Override public RelWriter item(String term, @Nullable Object value) { if (value != null && value.getClass().isArray()) { // We can't call hashCode and equals on Array, so // convert it to String to keep the same behaviour. value = "" + value; } attrs.add(Pair.of(term, value)); return this; } @Override public RelWriter done(RelNode node) { StringBuilder sb = new StringBuilder(); sb.append(node.getRelTypeName()); sb.append('.'); sb.append(node.getTraitSet()); sb.append('('); attrs.forEachIndexed((j, left, right) -> { if (j > 0) { sb.append(','); } sb.append(left); sb.append('='); if (right instanceof RelNode) { RelNode input = (RelNode) right; sb.append(input.getRelTypeName()); sb.append('#'); sb.append(input.getId()); } else { sb.append(right); } }); sb.append(')'); digest = sb.toString(); return this; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy