SlotDescriptor.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.
// This file is copied from
// https://github.com/apache/impala/blob/branch-2.9.0/fe/src/main/java/org/apache/impala/SlotDescriptor.java
// and modified by Doris
package org.apache.doris.analysis;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.ColumnStats;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Type;
import org.apache.doris.thrift.TSlotDescriptor;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
public class SlotDescriptor {
private static final Logger LOG = LogManager.getLogger(SlotDescriptor.class);
private final SlotId id;
private final TupleDescriptor parent;
private Type type;
private Column column; // underlying column, if there is one
// for SlotRef.toSql() in the absence of a path
private String label;
// for variant column's sub column lables
private List<String> subColPath;
// materializedColumnName is the target name of a slot
// it could be either column name or a composed name for a variant
// subcolumn like `a.b.c`
private String materializedColumnName;
// Expr(s) materialized into this slot; multiple exprs for unions. Should be empty if
// path_ is set.
private List<Expr> sourceExprs = Lists.newArrayList();
// if false, this slot doesn't need to be materialized in parent tuple
// (and physical layout parameters are invalid)
private boolean isMaterialized;
// if false, this slot cannot be NULL
private boolean isNullable;
// physical layout parameters
private int byteSize;
private int byteOffset = 0; // within tuple
private int slotIdx; // index within tuple struct
private int slotOffset; // index within slot array list
private ColumnStats stats; // only set if 'column' isn't set
private boolean isAgg;
// If set to false, then such slots will be ignored during
// materialize them.Used to optimize to read less data and less memory usage
private boolean needMaterialize = true;
private boolean isAutoInc = false;
public SlotDescriptor(SlotId id, TupleDescriptor parent) {
this.id = id;
this.parent = parent;
this.byteOffset = -1; // invalid
this.isMaterialized = false;
this.isNullable = true;
this.isAgg = false;
}
public SlotDescriptor(SlotId id, TupleDescriptor parent, SlotDescriptor src) {
this.id = id;
this.parent = parent;
this.byteOffset = src.byteOffset;
this.slotIdx = src.slotIdx;
this.isMaterialized = src.isMaterialized;
this.column = src.column;
this.isNullable = src.isNullable;
this.byteSize = src.byteSize;
this.isAgg = false;
this.stats = src.stats;
this.type = src.type;
this.sourceExprs.add(new SlotRef(src));
}
public boolean getIsAgg() {
return isAgg;
}
public void setNeedMaterialize(boolean needMaterialize) {
this.needMaterialize = needMaterialize;
}
public boolean isInvalid() {
return !this.needMaterialize;
}
public void setIsAgg(boolean agg) {
isAgg = agg;
}
public SlotId getId() {
return id;
}
public void setSubColLables(List<String> subColPath) {
this.subColPath = subColPath;
}
public List<String> getSubColLables() {
return this.subColPath;
}
public TupleDescriptor getParent() {
return parent;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Column getColumn() {
return column;
}
public void setColumn(Column column) {
this.column = column;
this.type = column.getType();
}
public void setSrcColumn(Column column) {
this.column = column;
}
public boolean isMaterialized() {
return isMaterialized;
}
public void setIsMaterialized(boolean value) {
isMaterialized = value;
}
public boolean isAutoInc() {
return isAutoInc;
}
public void setAutoInc(boolean isAutoInc) {
this.isAutoInc = isAutoInc;
}
public void materializeSrcExpr() {
if (sourceExprs == null) {
return;
}
for (Expr expr : sourceExprs) {
if (!(expr instanceof SlotRef)) {
expr.materializeSrcExpr();
continue;
}
SlotRef slotRef = (SlotRef) expr;
SlotDescriptor slotDesc = slotRef.getDesc();
slotDesc.setIsMaterialized(true);
slotDesc.materializeSrcExpr();
}
}
public boolean getIsNullable() {
return isNullable;
}
public void setIsNullable(boolean value) {
isNullable = value;
}
public int getByteSize() {
return byteSize;
}
public void setByteSize(int byteSize) {
this.byteSize = byteSize;
}
public int getByteOffset() {
return byteOffset;
}
public void setByteOffset(int byteOffset) {
this.byteOffset = byteOffset;
}
public void setSlotIdx(int slotIdx) {
this.slotIdx = slotIdx;
}
public void setStats(ColumnStats stats) {
this.stats = stats;
}
public void setMaterializedColumnName(String name) {
this.materializedColumnName = name;
}
public ColumnStats getStats() {
if (stats == null) {
if (column != null) {
stats = column.getStats();
} else {
stats = new ColumnStats();
}
}
// FIXME(dhc): mock ndv
stats.setNumDistinctValues((long) parent.getCardinality());
return stats;
}
public void setSlotOffset(int slotOffset) {
this.slotOffset = slotOffset;
}
public int getSlotOffset() {
return slotOffset;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public void setSourceExpr(Expr expr) {
sourceExprs = Collections.singletonList(expr);
}
public void addSourceExpr(Expr expr) {
sourceExprs.add(expr);
}
public List<Expr> getSourceExprs() {
return sourceExprs;
}
public int getUniqueId() {
if (column == null) {
return -1;
}
return column.getUniqueId();
}
/**
* Initializes a slot by setting its source expression information
*/
public void initFromExpr(Expr expr) {
setIsNullable(expr.isNullable());
setLabel(expr.toSql());
Preconditions.checkState(sourceExprs.isEmpty());
setSourceExpr(expr);
setStats(ColumnStats.fromExpr(expr));
Preconditions.checkState(expr.getType().isValid());
setType(expr.getType());
}
/**
* Return true if the physical layout of this descriptor matches the physical layout
* of the other descriptor, but not necessarily ids.
*/
public boolean layoutEquals(SlotDescriptor other) {
if (!getType().equals(other.getType())) {
return false;
}
if (isNullable != other.isNullable) {
return false;
}
if (getByteSize() != other.getByteSize()) {
return false;
}
if (getByteOffset() != other.getByteOffset()) {
return false;
}
return true;
}
public TSlotDescriptor toThrift() {
// Non-nullable slots will have 0 for the byte offset and -1 for the bit mask
String colName = materializedColumnName != null ? materializedColumnName :
((column != null) ? column.getNonShadowName() : "");
TSlotDescriptor tSlotDescriptor = new TSlotDescriptor(id.asInt(), parent.getId().asInt(), type.toThrift(), -1,
byteOffset, 0, getIsNullable() ? 0 : -1, colName, slotIdx,
isMaterialized);
tSlotDescriptor.setNeedMaterialize(needMaterialize);
tSlotDescriptor.setIsAutoIncrement(isAutoInc);
if (column != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("column name:{}, column unique id:{}", column.getNonShadowName(), column.getUniqueId());
}
tSlotDescriptor.setColUniqueId(column.getUniqueId());
tSlotDescriptor.setPrimitiveType(column.getDataType().toThrift());
tSlotDescriptor.setIsKey(column.isKey());
tSlotDescriptor.setColDefaultValue(column.getDefaultValue());
}
if (subColPath != null) {
tSlotDescriptor.setColumnPaths(subColPath);
}
return tSlotDescriptor;
}
public String debugString() {
String colStr = (column == null ? "null" : column.getName());
String typeStr = (type == null ? "null" : type.toString());
String parentTupleId = (parent == null) ? "null" : parent.getId().toString();
return MoreObjects.toStringHelper(this).add("id", id.asInt()).add("parent", parentTupleId).add("col", colStr)
.add("type", typeStr).add("materialized", isMaterialized).add("byteSize", byteSize)
.add("byteOffset", byteOffset).add("slotIdx", slotIdx).add("nullable", getIsNullable())
.add("isAutoIncrement", isAutoInc).add("subColPath", subColPath).toString();
}
@Override
public String toString() {
return debugString();
}
public String getExplainString(String prefix) {
return new StringBuilder()
.append(prefix).append("SlotDescriptor{")
.append("id=").append(id)
.append(", col=").append(column == null ? "null" : column.getName())
.append(", colUniqueId=").append(column == null ? "null" : column.getUniqueId())
.append(", type=").append(type == null ? "null" : type.toSql())
.append(", nullable=").append(isNullable)
.append(", isAutoIncrement=").append(isAutoInc)
.append(", subColPath=").append(subColPath)
.append("}")
.toString();
}
public boolean isScanSlot() {
return parent.getTable() instanceof OlapTable;
}
}