/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.metadata; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import io.crate.core.collections.EnumSets; import io.crate.types.DataType; import io.crate.types.DataTypes; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import javax.annotation.Nonnull; import java.io.IOException; import java.util.Set; public class FunctionInfo implements Comparable<FunctionInfo>, Streamable { public static final Set<Feature> NO_FEATURES = ImmutableSet.of(); public static final Set<Feature> DETERMINISTIC_ONLY = Sets.immutableEnumSet(Feature.DETERMINISTIC); public static final Set<Feature> DETERMINISTIC_AND_COMPARISON_REPLACEMENT = Sets.immutableEnumSet( Feature.DETERMINISTIC, Feature.COMPARISON_REPLACEMENT); private FunctionIdent ident; private DataType returnType; private Type type; private Set<Feature> features; public FunctionInfo() { } public FunctionInfo(FunctionIdent ident, DataType returnType) { this(ident, returnType, Type.SCALAR); } public FunctionInfo(FunctionIdent ident, DataType returnType, Type type) { this(ident, returnType, type, DETERMINISTIC_ONLY); } public FunctionInfo(FunctionIdent ident, DataType returnType, Type type, Set<Feature> features) { assert features.size() < 32 : "features size must not exceed 32"; this.ident = ident; this.returnType = returnType; this.type = type; this.features = features; } public FunctionIdent ident() { return ident; } public Type type() { return type; } public DataType returnType() { return returnType; } public Set<Feature> features() { return features; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FunctionInfo that = (FunctionInfo) o; return Objects.equal(ident, that.ident) && Objects.equal(returnType, that.returnType) && Objects.equal(type, that.type) && Objects.equal(features, that.features); } @Override public int hashCode() { return Objects.hashCode(ident, returnType, type, features); } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("type", type) .add("ident", ident) .add("returnType", returnType) .add("features", features) .toString(); } @Override public int compareTo(@Nonnull FunctionInfo o) { return ComparisonChain.start() .compare(type, o.type) .compare(ident, o.ident) .compare(returnType, o.returnType) .compare(features, o.features, Ordering.<Feature>natural().lexicographical()) .result(); } @Override public void readFrom(StreamInput in) throws IOException { ident = new FunctionIdent(); ident.readFrom(in); returnType = DataTypes.fromStream(in); type = Type.values()[in.readVInt()]; int enumElements = in.readVInt(); this.features = Sets.immutableEnumSet(EnumSets.unpackFromInt(enumElements, Feature.class)); } @Override public void writeTo(StreamOutput out) throws IOException { ident.writeTo(out); DataTypes.toStream(returnType, out); out.writeVInt(type.ordinal()); out.writeVInt(EnumSets.packToInt(features)); } public enum Type { SCALAR, AGGREGATE, TABLE } public enum Feature { DETERMINISTIC, /** * If this feature is set, for this function it is possible to replace the containing * comparison while preserving the truth value for all used operators * with or without an operator mapping. * <p> * It describes the following: * <p> * say we have a comparison-query like this: * <p> * col > 10.5 * <p> * then a function f, for which comparisons are replaceable, can be applied so * that: * <p> * f(col) > f(10.5) * <p> * for all col for which col > 10.5 is true. Maybe > needs to be mapped to another * operator, but this may not depend on the actual values used here. * <p> * Fun facts: * <p> * * This property holds for the = comparison operator for all functions f. * * This property is transitive so if f and g are replaceable, * then f(g(x)) also is * * it is possible to replace: * <p> * col > 10.5 * <p> * with: * <p> * f(col) > f(10.5) * <p> * for every operator (possibly mapped) and the query is still equivalent. * <p> * Example 1: * <p> * if f is defined as f(v) = v + 1 * then col + 1 > 11.5 must be true for all col > 10.5. * This is indeed true. * <p> * So f is replaceable for >. * <p> * Fun fact: for f all comparison operators =, >, <, >=,<= are replaceable * <p> * Example 2 (a rounding function): * <p> * if f is defined as f(v) = ceil(v) * then ceil(col) > 11 for all col > 10.5. * But there is 10.8 for which f is 11 and * 11 > 11 is false. * <p> * Here a simple mapping of the operator will do the trick: * <p> * > -> >= * < -> <= * <p> * So for f comparisons are replaceable using the mapping above. * <p> * Example 3: * <p> * if f is defined as f(v) = v % 5 * then col % 5 > 0.5 for all col > 10.5 * but there is 20 for which * f is 0 and * 0 > 0.5 is false. * <p> * So for f comparisons cannot be replaced. */ COMPARISON_REPLACEMENT, LAZY_ATTRIBUTES } }