Values.java
/*
* 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.core;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.AbstractRelNode;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexDigestIncludeType;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.util.Pair;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Relational expression whose value is a sequence of zero or more literal row
* values.
*/
public abstract class Values extends AbstractRelNode implements Hintable {
public static final Predicate<? super Values> IS_EMPTY_J = Values::isEmpty;
protected final ImmutableList<RelHint> hints;
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
public static final com.google.common.base.Predicate<? super Values>
IS_EMPTY = Values::isEmpty;
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
public static final com.google.common.base.Predicate<? super Values>
IS_NOT_EMPTY = Values::isNotEmpty;
//~ Instance fields --------------------------------------------------------
public final ImmutableList<ImmutableList<RexLiteral>> tuples;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new Values.
*
* <p>Note that tuples passed in become owned by
* this rel (without a deep copy), so caller must not modify them after this
* call, otherwise bad things will happen.
*
* @param cluster Cluster that this relational expression belongs to
* @param hints Hints for this node
* @param rowType Row type for tuples produced by this rel
* @param tuples 2-dimensional array of tuple values to be produced; outer
* list contains tuples; each inner list is one tuple; all
* tuples must be of same length, conforming to rowType
*/
@SuppressWarnings("method.invocation.invalid")
protected Values(
RelOptCluster cluster,
List<RelHint> hints,
RelDataType rowType,
ImmutableList<ImmutableList<RexLiteral>> tuples,
RelTraitSet traits) {
super(cluster, traits);
this.rowType = rowType;
this.tuples = tuples;
this.hints = ImmutableList.copyOf(hints);
assert assertRowType();
}
/**
* Creates a new Values.
*
* <p>Note that tuples passed in become owned by
* this rel (without a deep copy), so caller must not modify them after this
* call, otherwise bad things will happen.
*
* @param cluster Cluster that this relational expression belongs to
* @param rowType Row type for tuples produced by this rel
* @param tuples 2-dimensional array of tuple values to be produced; outer
* list contains tuples; each inner list is one tuple; all
* tuples must be of same length, conforming to rowType
*/
@SuppressWarnings("method.invocation.invalid")
protected Values(
RelOptCluster cluster,
RelDataType rowType,
ImmutableList<ImmutableList<RexLiteral>> tuples,
RelTraitSet traits) {
this(cluster, Collections.emptyList(), rowType, tuples, traits);
}
/**
* Creates a Values by parsing serialized output.
*/
protected Values(RelInput input) {
this(input.getCluster(), input.getRowType("type"),
input.getTuples("tuples"), input.getTraitSet());
}
//~ Methods ----------------------------------------------------------------
/** Predicate, to be used when defining an operand of a {@link RelOptRule},
* that returns true if a Values contains zero tuples.
*
* <p>This is the conventional way to represent an empty relational
* expression. There are several rules that recognize empty relational
* expressions and prune away that section of the tree.
*/
public static boolean isEmpty(Values values) {
return values.getTuples().isEmpty();
}
/** Predicate, to be used when defining an operand of a {@link RelOptRule},
* that returns true if a Values contains one or more tuples.
*
* <p>This is the conventional way to represent an empty relational
* expression. There are several rules that recognize empty relational
* expressions and prune away that section of the tree.
*/
public static boolean isNotEmpty(Values values) {
return !isEmpty(values);
}
public static boolean isSingleValue(Values values) {
return values.tuples.size() == 1;
}
public ImmutableList<ImmutableList<RexLiteral>> getTuples(RelInput input) {
return input.getTuples("tuples");
}
/** Returns the rows of literals represented by this Values relational
* expression. */
public ImmutableList<ImmutableList<RexLiteral>> getTuples() {
return tuples;
}
/** Returns true if all tuples match rowType; otherwise, assert on
* mismatch. */
private boolean assertRowType() {
RelDataType rowType = getRowType();
for (List<RexLiteral> tuple : tuples) {
assert tuple.size() == rowType.getFieldCount();
for (Pair<RexLiteral, RelDataTypeField> pair
: Pair.zip(tuple, rowType.getFieldList())) {
RexLiteral literal = pair.left;
RelDataType fieldType = pair.right.getType();
// TODO jvs 19-Feb-2006: strengthen this a bit. For example,
// overflow, rounding, and padding/truncation must already have
// been dealt with.
if (!RexLiteral.isNullLiteral(literal)) {
assert SqlTypeUtil.canAssignFrom(fieldType, literal.getType())
: "to " + fieldType + " from " + literal;
}
}
}
return true;
}
@Override protected RelDataType deriveRowType() {
if (rowType == null) {
throw new AssertionError("rowType must not be null for " + this);
}
return rowType;
}
@Override public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
double dRows = mq.getRowCount(this);
// Assume CPU is negligible since values are precomputed.
double dCpu = 1;
double dIo = 0;
return planner.getCostFactory().makeCost(dRows, dCpu, dIo);
}
// implement RelNode
@Override public double estimateRowCount(RelMetadataQuery mq) {
return tuples.size();
}
// implement RelNode
@Override public RelWriter explainTerms(RelWriter pw) {
// A little adapter just to get the tuples to come out
// with curly brackets instead of square brackets. Plus
// more whitespace for readability.
RelDataType rowType = getRowType();
RelWriter relWriter = super.explainTerms(pw)
// For rel digest, include the row type since a rendered
// literal may leave the type ambiguous (e.g. "null").
.itemIf("type", rowType,
pw.getDetailLevel() == SqlExplainLevel.DIGEST_ATTRIBUTES)
.itemIf("type", rowType.getFieldList(), pw.nest());
if (pw.nest()) {
pw.item("tuples", tuples);
} else {
pw.item("tuples",
tuples.stream()
.map(row -> row.stream()
.map(lit -> lit.computeDigest(RexDigestIncludeType.NO_TYPE))
.collect(Collectors.joining(", ", "{ ", " }")))
.collect(Collectors.joining(", ", "[", "]")));
}
return relWriter;
}
@Override public ImmutableList<RelHint> getHints() {
return hints;
}
}