Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed 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.facebook.presto.jdbc.internal.spi;
import com.facebook.presto.jdbc.internal.jackson.annotation.JsonCreator;
import com.facebook.presto.jdbc.internal.jackson.annotation.JsonIgnore;
import com.facebook.presto.jdbc.internal.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Defines a set of valid tuples according to the constraints on each of its constituent columns
*/
public final class TupleDomain
{
/**
* TupleDomain is internally represented as a normalized map of each column to its
* respective allowable value Domain. Conceptually, these Domains can be thought of
* as being AND'ed together to form the representative predicate.
*
* This map is normalized in the following ways:
* 1) The map will not contain Domain.none() as any of its values. If any of the Domain
* values are Domain.none(), then the whole map will instead be null. This enforces the fact that
* any single Domain.none() value effectively turns this TupleDomain into "none" as well.
* 2) The map will not contain Domain.all() as any of its values. Our convention here is that
* any unmentioned column is equivalent to having Domain.all(). To normalize this structure,
* we remove any Domain.all() values from the map.
*/
private final Map domains;
private TupleDomain(Map domains)
{
if (domains == null || containsNoneDomain(domains)) {
this.domains = null;
}
else {
this.domains = Collections.unmodifiableMap(normalizeAndCopy(domains));
}
}
public static TupleDomain withColumnDomains(Map domains)
{
return new TupleDomain<>(Objects.requireNonNull(domains, "domains is null"));
}
public static TupleDomain none()
{
return new TupleDomain<>(null);
}
public static TupleDomain all()
{
return new TupleDomain<>(Collections.emptyMap());
}
/**
* Convert a map of columns to values into the TupleDomain which requires
* those columns to be fixed to those values.
*/
public static TupleDomain withFixedValues(Map> fixedValues)
{
Map domains = new HashMap<>();
for (Map.Entry> entry : fixedValues.entrySet()) {
domains.put(entry.getKey(), Domain.singleValue(entry.getValue()));
}
return withColumnDomains(domains);
}
/**
* Convert a map of columns to values into the TupleDomain which requires
* those columns to be fixed to those values. Null is allowed as a fixed value.
*/
public static TupleDomain withNullableFixedValues(Map fixedValues)
{
Map domains = new HashMap<>();
for (Map.Entry entry : fixedValues.entrySet()) {
if (entry.getValue().getValue() != null) {
domains.put(entry.getKey(), Domain.singleValue(entry.getValue().getValue()));
}
else {
domains.put(entry.getKey(), Domain.onlyNull(entry.getValue().getType()));
}
}
return withColumnDomains(domains);
}
@JsonCreator
// Available for Jackson deserialization only!
public static TupleDomain fromNullableColumnDomains(@JsonProperty("nullableColumnDomains") List> nullableColumnDomains)
{
if (nullableColumnDomains == null) {
return none();
}
return withColumnDomains(toMap(nullableColumnDomains));
}
@JsonProperty
// Available for Jackson serialization only!
public List> getNullableColumnDomains()
{
return domains == null ? null : toList(domains);
}
private static Map toMap(List> columnDomains)
{
Map map = new HashMap<>();
for (ColumnDomain columnDomain : columnDomains) {
if (map.containsKey(columnDomain.getColumnHandle())) {
throw new IllegalArgumentException("Duplicate column handle!");
}
map.put(columnDomain.getColumnHandle(), columnDomain.getDomain());
}
return map;
}
private static List> toList(Map columnDomains)
{
List> list = new ArrayList<>();
for (Map.Entry entry : columnDomains.entrySet()) {
list.add(new ColumnDomain<>(entry.getKey(), entry.getValue()));
}
return list;
}
private static boolean containsNoneDomain(Map domains)
{
for (Domain domain : domains.values()) {
if (domain.isNone()) {
return true;
}
}
return false;
}
private static Map normalizeAndCopy(Map domains)
{
Map map = new HashMap<>();
for (Map.Entry entry : domains.entrySet()) {
if (!entry.getValue().isAll()) {
map.put(entry.getKey(), entry.getValue());
}
}
return map;
}
/**
* Returns true if any tuples would satisfy this TupleDomain
*/
@JsonIgnore
public boolean isAll()
{
return domains != null && domains.isEmpty();
}
/**
* Returns true if no tuple could ever satisfy this TupleDomain
*/
@JsonIgnore
public boolean isNone()
{
return domains == null;
}
/**
* Gets the TupleDomain as a map of each column to its respective Domain.
* - You must check to make sure that this TupleDomain is not None before calling this method
* - Unmentioned columns have an implicit value of Domain.all()
* - The column Domains can be thought of as AND'ed to together to form the whole predicate
*/
@JsonIgnore
public Map getDomains()
{
if (domains == null) {
throw new IllegalStateException("Can not get column Domains from a none TupleDomain");
}
return domains;
}
/**
* Extract all column constraints that require exactly one value in their respective Domains.
*/
public Map> extractFixedValues()
{
if (isNone()) {
return Collections.emptyMap();
}
Map> fixedValues = new HashMap<>();
for (Map.Entry entry : getDomains().entrySet()) {
if (entry.getValue().isSingleValue()) {
fixedValues.put(entry.getKey(), entry.getValue().getSingleValue());
}
}
return fixedValues;
}
/**
* Extract all column constraints that require exactly one value or only null in their respective Domains.
*/
public Map extractNullableFixedValues()
{
if (isNone()) {
return Collections.emptyMap();
}
Map builder = new HashMap<>();
for (Map.Entry entry : getDomains().entrySet()) {
if (entry.getValue().isSingleValue()) {
builder.put(entry.getKey(), new SerializableNativeValue(entry.getValue().getType(), entry.getValue().getSingleValue()));
}
else if (entry.getValue().isOnlyNull()) {
builder.put(entry.getKey(), new SerializableNativeValue(entry.getValue().getType(), null));
}
}
return builder;
}
/**
* Returns the strict intersection of the TupleDomains.
* The resulting TupleDomain represents the set of tuples that would would be valid
* in both TupleDomains.
*/
public TupleDomain intersect(TupleDomain other)
{
if (this.isNone() || other.isNone()) {
return none();
}
Map intersected = new HashMap<>(this.getDomains());
for (Map.Entry entry : other.getDomains().entrySet()) {
Domain intersectionDomain = intersected.get(entry.getKey());
if (intersectionDomain == null) {
intersected.put(entry.getKey(), entry.getValue());
}
else {
intersected.put(entry.getKey(), intersectionDomain.intersect(entry.getValue()));
}
}
return withColumnDomains(intersected);
}
@SafeVarargs
public static TupleDomain columnWiseUnion(TupleDomain first, TupleDomain second, TupleDomain... rest)
{
List> domains = new ArrayList<>();
domains.add(first);
domains.add(second);
domains.addAll(Arrays.asList(rest));
return columnWiseUnion(domains);
}
/**
* Returns a TupleDomain in which corresponding column Domains are unioned together.
*
* Note that this is NOT equivalent to a strict union as the final result may allow tuples
* that do not exist in either TupleDomain.
* For example:
* TupleDomain X: a => 1, b => 2
* TupleDomain Y: a => 2, b => 3
* Column-wise unioned TupleDomain: a = > 1 OR 2, b => 2 OR 3
* In the above resulting TupleDomain, tuple (a => 1, b => 3) would be considered valid but would
* not be valid for either TupleDomain X or TupleDomain Y.
* However, this result is guaranteed to be a superset of the strict union.
*/
public static TupleDomain columnWiseUnion(List> tupleDomains)
{
if (tupleDomains.isEmpty()) {
throw new IllegalArgumentException("tupleDomains must have at least one element");
}
if (tupleDomains.size() == 1) {
return tupleDomains.get(0);
}
// gather all common columns
Set commonColumns = new HashSet<>();
// first, find a non-none domain
boolean found = false;
Iterator> domains = tupleDomains.iterator();
while (domains.hasNext()) {
TupleDomain domain = domains.next();
if (!domain.isNone()) {
found = true;
commonColumns.addAll(domain.getDomains().keySet());
break;
}
}
if (!found) {
return TupleDomain.none();
}
// then, get the common columns
while (domains.hasNext()) {
TupleDomain domain = domains.next();
if (!domain.isNone()) {
commonColumns.retainAll(domain.getDomains().keySet());
}
}
// group domains by column (only for common columns)
Map> domainsByColumn = new HashMap<>();
for (TupleDomain domain : tupleDomains) {
if (!domain.isNone()) {
for (Map.Entry entry : domain.getDomains().entrySet()) {
if (commonColumns.contains(entry.getKey())) {
List domainForColumn = domainsByColumn.get(entry.getKey());
if (domainForColumn == null) {
domainForColumn = new ArrayList<>();
domainsByColumn.put(entry.getKey(), domainForColumn);
}
domainForColumn.add(entry.getValue());
}
}
}
}
// finally, do the column-wise union
Map result = new HashMap<>();
for (Map.Entry> entry : domainsByColumn.entrySet()) {
result.put(entry.getKey(), Domain.union(entry.getValue()));
}
return withColumnDomains(result);
}
/**
* Returns true only if there exists a strict intersection between the TupleDomains.
* i.e. there exists some potential tuple that would be allowable in both TupleDomains.
*/
public boolean overlaps(TupleDomain other)
{
return !this.intersect(other).isNone();
}
/**
* Returns true only if the this TupleDomain contains all possible tuples that would be allowable by
* the other TupleDomain.
*/
public boolean contains(TupleDomain other)
{
return other.isNone() || columnWiseUnion(this, other).equals(this);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (!(o instanceof TupleDomain)) {
return false;
}
TupleDomain that = (TupleDomain) o;
if (domains != null ? !domains.equals(that.domains) : that.domains != null) {
return false;
}
return true;
}
@Override
public int hashCode()
{
return domains != null ? domains.hashCode() : 0;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder()
.append("TupleDomain:");
if (isAll()) {
builder.append("ALL");
}
else if (isNone()) {
builder.append("NONE");
}
else {
builder.append(domains);
}
return builder.toString();
}
public TupleDomain transform(Function function)
{
if (domains == null) {
return new TupleDomain<>(null);
}
HashMap result = new HashMap<>(domains.size());
for (Map.Entry entry : domains.entrySet()) {
U key = function.apply(entry.getKey());
Domain previous = result.put(key, entry.getValue());
if (previous != null) {
throw new IllegalArgumentException(String.format("Every argument must have a unique mapping. %s maps to %s and %s", entry.getKey(), entry.getValue(), previous));
}
}
return new TupleDomain<>(result);
}
// Available for Jackson serialization only!
public static class ColumnDomain
{
private final C columnHandle;
private final Domain domain;
@JsonCreator
public ColumnDomain(
@JsonProperty("columnHandle") C columnHandle,
@JsonProperty("domain") Domain domain)
{
this.columnHandle = Objects.requireNonNull(columnHandle, "columnHandle is null");
this.domain = Objects.requireNonNull(domain, "domain is null");
}
@JsonProperty
public C getColumnHandle()
{
return columnHandle;
}
@JsonProperty
public Domain getDomain()
{
return domain;
}
}
// Custom Function interface because SPI does not include Guava
public static interface Function
{
T apply(F input);
}
}