blob: 7b3dd448c5d39ea6d3fd0c0ce0a1269473977b4d [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.druid.segment.column;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.google.common.base.Preconditions;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.java.util.common.ISE;
import javax.annotation.Nullable;
/**
*
*/
public class ColumnCapabilitiesImpl implements ColumnCapabilities
{
private static final CoercionLogic ALL_FALSE = new CoercionLogic()
{
@Override
public boolean dictionaryEncoded()
{
return false;
}
@Override
public boolean dictionaryValuesSorted()
{
return false;
}
@Override
public boolean dictionaryValuesUnique()
{
return false;
}
@Override
public boolean multipleValues()
{
return false;
}
@Override
public boolean hasNulls()
{
return false;
}
};
public static ColumnCapabilitiesImpl copyOf(@Nullable final ColumnCapabilities other)
{
final ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl();
if (other != null) {
capabilities.type = other.getType();
capabilities.complexTypeName = other.getComplexTypeName();
capabilities.dictionaryEncoded = other.isDictionaryEncoded();
capabilities.hasInvertedIndexes = other.hasBitmapIndexes();
capabilities.hasSpatialIndexes = other.hasSpatialIndexes();
capabilities.hasMultipleValues = other.hasMultipleValues();
capabilities.dictionaryValuesSorted = other.areDictionaryValuesSorted();
capabilities.dictionaryValuesUnique = other.areDictionaryValuesUnique();
capabilities.hasNulls = other.hasNulls();
capabilities.filterable = other.isFilterable();
}
return capabilities;
}
/**
* Copy a {@link ColumnCapabilities} and coerce all {@link ColumnCapabilities.Capable#UNKNOWN} to
* {@link ColumnCapabilities.Capable#TRUE} or {@link ColumnCapabilities.Capable#FALSE} as specified by
* {@link ColumnCapabilities.CoercionLogic}
*/
@Nullable
public static ColumnCapabilitiesImpl snapshot(@Nullable final ColumnCapabilities capabilities, CoercionLogic coerce)
{
if (capabilities == null) {
return null;
}
ColumnCapabilitiesImpl copy = copyOf(capabilities);
copy.dictionaryEncoded = copy.dictionaryEncoded.coerceUnknownToBoolean(coerce.dictionaryEncoded());
copy.dictionaryValuesSorted = copy.dictionaryValuesSorted.coerceUnknownToBoolean(coerce.dictionaryValuesSorted());
copy.dictionaryValuesUnique = copy.dictionaryValuesUnique.coerceUnknownToBoolean(coerce.dictionaryValuesUnique());
copy.hasMultipleValues = copy.hasMultipleValues.coerceUnknownToBoolean(coerce.multipleValues());
copy.hasNulls = copy.hasNulls.coerceUnknownToBoolean(coerce.hasNulls());
return copy;
}
/**
* Snapshots a pair of capabilities and then merges them
*/
@Nullable
public static ColumnCapabilitiesImpl merge(
@Nullable final ColumnCapabilities capabilities,
@Nullable final ColumnCapabilities other,
CoercionLogic coercionLogic
)
{
ColumnCapabilitiesImpl merged = snapshot(capabilities, coercionLogic);
ColumnCapabilitiesImpl otherSnapshot = snapshot(other, coercionLogic);
if (merged == null) {
return otherSnapshot;
} else if (otherSnapshot == null) {
return merged;
}
if (merged.type == null) {
merged.type = other.getType();
}
if (!merged.type.equals(otherSnapshot.getType())) {
throw new ISE("Cannot merge columns of type[%s] and [%s]", merged.type, otherSnapshot.getType());
}
if (merged.type == ValueType.COMPLEX && merged.complexTypeName == null) {
merged.complexTypeName = other.getComplexTypeName();
}
if (merged.type == ValueType.COMPLEX && merged.complexTypeName != null && !merged.complexTypeName.equals(other.getComplexTypeName())) {
throw new ISE("Cannot merge columns of typeName[%s] and [%s]", merged.complexTypeName, other.getComplexTypeName());
}
merged.dictionaryEncoded = merged.dictionaryEncoded.or(otherSnapshot.isDictionaryEncoded());
merged.hasMultipleValues = merged.hasMultipleValues.or(otherSnapshot.hasMultipleValues());
merged.dictionaryValuesSorted = merged.dictionaryValuesSorted.and(otherSnapshot.areDictionaryValuesSorted());
merged.dictionaryValuesUnique = merged.dictionaryValuesUnique.and(otherSnapshot.areDictionaryValuesUnique());
merged.hasNulls = merged.hasNulls.or(other.hasNulls());
merged.hasInvertedIndexes |= otherSnapshot.hasBitmapIndexes();
merged.hasSpatialIndexes |= otherSnapshot.hasSpatialIndexes();
merged.filterable &= otherSnapshot.isFilterable();
return merged;
}
/**
* Creates a {@link ColumnCapabilitiesImpl} where all {@link ColumnCapabilities.Capable} that default to unknown
* instead are coerced to true or false
*/
public static ColumnCapabilitiesImpl createDefault()
{
return ColumnCapabilitiesImpl.snapshot(new ColumnCapabilitiesImpl(), ALL_FALSE);
}
/**
* Create a no frills, simple column with {@link ValueType} set and everything else false
*/
public static ColumnCapabilitiesImpl createSimpleNumericColumnCapabilities(ValueType valueType)
{
ColumnCapabilitiesImpl builder = new ColumnCapabilitiesImpl().setType(valueType)
.setHasMultipleValues(false)
.setHasBitmapIndexes(false)
.setDictionaryEncoded(false)
.setDictionaryValuesSorted(false)
.setDictionaryValuesUnique(false)
.setHasSpatialIndexes(false);
if (NullHandling.replaceWithDefault()) {
builder.setHasNulls(false);
}
return builder;
}
/**
* Simple, single valued, non dictionary encoded string without bitmap index or anything fancy
*/
public static ColumnCapabilitiesImpl createSimpleSingleValueStringColumnCapabilities()
{
return new ColumnCapabilitiesImpl().setType(ValueType.STRING)
.setHasMultipleValues(false)
.setHasBitmapIndexes(false)
.setDictionaryEncoded(false)
.setDictionaryValuesSorted(false)
.setDictionaryValuesUnique(false)
.setHasSpatialIndexes(false)
.setHasNulls(true);
}
/**
* Similar to {@link #createSimpleNumericColumnCapabilities} except {@link #hasMultipleValues} is explicitly true
* and {@link #hasNulls} is not set
*/
public static ColumnCapabilitiesImpl createSimpleArrayColumnCapabilities(ValueType valueType)
{
ColumnCapabilitiesImpl builder = new ColumnCapabilitiesImpl().setType(valueType)
.setHasMultipleValues(true)
.setHasBitmapIndexes(false)
.setDictionaryEncoded(false)
.setDictionaryValuesSorted(false)
.setDictionaryValuesUnique(false)
.setHasSpatialIndexes(false);
return builder;
}
@Nullable
private ValueType type = null;
@Nullable
private String complexTypeName = null;
private boolean hasInvertedIndexes = false;
private boolean hasSpatialIndexes = false;
private Capable dictionaryEncoded = Capable.UNKNOWN;
private Capable hasMultipleValues = Capable.UNKNOWN;
// These capabilities are computed at query time and not persisted in the segment files.
@JsonIgnore
private Capable dictionaryValuesSorted = Capable.UNKNOWN;
@JsonIgnore
private Capable dictionaryValuesUnique = Capable.UNKNOWN;
@JsonIgnore
private boolean filterable;
@JsonIgnore
private Capable hasNulls = Capable.UNKNOWN;
@Override
@JsonProperty
public ValueType getType()
{
return type;
}
@Override
@JsonProperty
public String getComplexTypeName()
{
return complexTypeName;
}
public ColumnCapabilitiesImpl setType(ValueType type)
{
this.type = Preconditions.checkNotNull(type, "'type' must be nonnull");
return this;
}
public ColumnCapabilitiesImpl setComplexTypeName(String typeName)
{
this.complexTypeName = typeName;
return this;
}
@Override
@JsonProperty("dictionaryEncoded")
public Capable isDictionaryEncoded()
{
return dictionaryEncoded;
}
@JsonSetter("dictionaryEncoded")
public ColumnCapabilitiesImpl setDictionaryEncoded(boolean dictionaryEncoded)
{
this.dictionaryEncoded = Capable.of(dictionaryEncoded);
return this;
}
@Override
public Capable areDictionaryValuesSorted()
{
return dictionaryValuesSorted;
}
public ColumnCapabilitiesImpl setDictionaryValuesSorted(boolean dictionaryValuesSorted)
{
this.dictionaryValuesSorted = Capable.of(dictionaryValuesSorted);
return this;
}
@Override
public Capable areDictionaryValuesUnique()
{
return dictionaryValuesUnique;
}
public ColumnCapabilitiesImpl setDictionaryValuesUnique(boolean dictionaryValuesUnique)
{
this.dictionaryValuesUnique = Capable.of(dictionaryValuesUnique);
return this;
}
@Override
@JsonProperty("hasBitmapIndexes")
public boolean hasBitmapIndexes()
{
return hasInvertedIndexes;
}
public ColumnCapabilitiesImpl setHasBitmapIndexes(boolean hasInvertedIndexes)
{
this.hasInvertedIndexes = hasInvertedIndexes;
return this;
}
@Override
@JsonProperty("hasSpatialIndexes")
public boolean hasSpatialIndexes()
{
return hasSpatialIndexes;
}
public ColumnCapabilitiesImpl setHasSpatialIndexes(boolean hasSpatialIndexes)
{
this.hasSpatialIndexes = hasSpatialIndexes;
return this;
}
@Override
@JsonProperty("hasMultipleValues")
public Capable hasMultipleValues()
{
return hasMultipleValues;
}
public ColumnCapabilitiesImpl setHasMultipleValues(boolean hasMultipleValues)
{
this.hasMultipleValues = Capable.of(hasMultipleValues);
return this;
}
@Override
public Capable hasNulls()
{
return hasNulls;
}
public ColumnCapabilitiesImpl setHasNulls(boolean hasNulls)
{
this.hasNulls = Capable.of(hasNulls);
return this;
}
public ColumnCapabilitiesImpl setHasNulls(Capable hasNulls)
{
this.hasNulls = hasNulls;
return this;
}
@Override
public boolean isFilterable()
{
return type == ValueType.STRING ||
type == ValueType.LONG ||
type == ValueType.FLOAT ||
type == ValueType.DOUBLE ||
filterable;
}
public ColumnCapabilitiesImpl setFilterable(boolean filterable)
{
this.filterable = filterable;
return this;
}
}