| /* |
| * 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.cassandra.cql3.functions.types; |
| |
| import java.util.*; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| import org.apache.cassandra.transport.ProtocolVersion; |
| |
| /** |
| * Data types supported by cassandra. |
| */ |
| public abstract class DataType |
| { |
| |
| /** |
| * The CQL type name. |
| */ |
| public enum Name |
| { |
| CUSTOM(0), |
| ASCII(1), |
| BIGINT(2), |
| BLOB(3), |
| BOOLEAN(4), |
| COUNTER(5), |
| DECIMAL(6), |
| DOUBLE(7), |
| FLOAT(8), |
| INT(9), |
| TEXT(10) |
| { |
| @Override |
| public boolean isCompatibleWith(Name that) |
| { |
| return this == that || that == VARCHAR; |
| } |
| }, |
| TIMESTAMP(11), |
| UUID(12), |
| VARCHAR(13) |
| { |
| @Override |
| public boolean isCompatibleWith(Name that) |
| { |
| return this == that || that == TEXT; |
| } |
| }, |
| VARINT(14), |
| TIMEUUID(15), |
| INET(16), |
| DATE(17, ProtocolVersion.V4), |
| TIME(18, ProtocolVersion.V4), |
| SMALLINT(19, ProtocolVersion.V4), |
| TINYINT(20, ProtocolVersion.V4), |
| DURATION(21, ProtocolVersion.V5), |
| LIST(32), |
| MAP(33), |
| SET(34), |
| UDT(48, ProtocolVersion.V3), |
| TUPLE(49, ProtocolVersion.V3); |
| |
| final int protocolId; |
| |
| final ProtocolVersion minProtocolVersion; |
| |
| private static final Name[] nameToIds; |
| |
| static |
| { |
| int maxCode = -1; |
| for (Name name : Name.values()) maxCode = Math.max(maxCode, name.protocolId); |
| nameToIds = new Name[maxCode + 1]; |
| for (Name name : Name.values()) |
| { |
| if (nameToIds[name.protocolId] != null) throw new IllegalStateException("Duplicate Id"); |
| nameToIds[name.protocolId] = name; |
| } |
| } |
| |
| Name(int protocolId) |
| { |
| this(protocolId, ProtocolVersion.V1); |
| } |
| |
| Name(int protocolId, ProtocolVersion minProtocolVersion) |
| { |
| this.protocolId = protocolId; |
| this.minProtocolVersion = minProtocolVersion; |
| } |
| |
| /** |
| * Return {@code true} if the provided Name is equal to this one, or if they are aliases for |
| * each other, and {@code false} otherwise. |
| * |
| * @param that the Name to compare with the current one. |
| * @return {@code true} if the provided Name is equal to this one, or if they are aliases for |
| * each other, and {@code false} otherwise. |
| */ |
| public boolean isCompatibleWith(Name that) |
| { |
| return this == that; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return super.toString().toLowerCase(); |
| } |
| } |
| |
| private static final Map<Name, DataType> primitiveTypeMap = |
| new EnumMap<>(Name.class); |
| |
| static |
| { |
| primitiveTypeMap.put(Name.ASCII, new DataType.NativeType(Name.ASCII)); |
| primitiveTypeMap.put(Name.BIGINT, new DataType.NativeType(Name.BIGINT)); |
| primitiveTypeMap.put(Name.BLOB, new DataType.NativeType(Name.BLOB)); |
| primitiveTypeMap.put(Name.BOOLEAN, new DataType.NativeType(Name.BOOLEAN)); |
| primitiveTypeMap.put(Name.COUNTER, new DataType.NativeType(Name.COUNTER)); |
| primitiveTypeMap.put(Name.DECIMAL, new DataType.NativeType(Name.DECIMAL)); |
| primitiveTypeMap.put(Name.DOUBLE, new DataType.NativeType(Name.DOUBLE)); |
| primitiveTypeMap.put(Name.FLOAT, new DataType.NativeType(Name.FLOAT)); |
| primitiveTypeMap.put(Name.INET, new DataType.NativeType(Name.INET)); |
| primitiveTypeMap.put(Name.INT, new DataType.NativeType(Name.INT)); |
| primitiveTypeMap.put(Name.TEXT, new DataType.NativeType(Name.TEXT)); |
| primitiveTypeMap.put(Name.TIMESTAMP, new DataType.NativeType(Name.TIMESTAMP)); |
| primitiveTypeMap.put(Name.UUID, new DataType.NativeType(Name.UUID)); |
| primitiveTypeMap.put(Name.VARCHAR, new DataType.NativeType(Name.VARCHAR)); |
| primitiveTypeMap.put(Name.VARINT, new DataType.NativeType(Name.VARINT)); |
| primitiveTypeMap.put(Name.TIMEUUID, new DataType.NativeType(Name.TIMEUUID)); |
| primitiveTypeMap.put(Name.SMALLINT, new DataType.NativeType(Name.SMALLINT)); |
| primitiveTypeMap.put(Name.TINYINT, new DataType.NativeType(Name.TINYINT)); |
| primitiveTypeMap.put(Name.DATE, new DataType.NativeType(Name.DATE)); |
| primitiveTypeMap.put(Name.TIME, new DataType.NativeType(Name.TIME)); |
| primitiveTypeMap.put(Name.DURATION, new DataType.NativeType(Name.DURATION)); |
| } |
| |
| protected final DataType.Name name; |
| |
| protected DataType(DataType.Name name) |
| { |
| this.name = name; |
| } |
| |
| /** |
| * Returns the ASCII type. |
| * |
| * @return The ASCII type. |
| */ |
| public static DataType ascii() |
| { |
| return primitiveTypeMap.get(Name.ASCII); |
| } |
| |
| /** |
| * Returns the BIGINT type. |
| * |
| * @return The BIGINT type. |
| */ |
| public static DataType bigint() |
| { |
| return primitiveTypeMap.get(Name.BIGINT); |
| } |
| |
| /** |
| * Returns the BLOB type. |
| * |
| * @return The BLOB type. |
| */ |
| public static DataType blob() |
| { |
| return primitiveTypeMap.get(Name.BLOB); |
| } |
| |
| /** |
| * Returns the BOOLEAN type. |
| * |
| * @return The BOOLEAN type. |
| */ |
| public static DataType cboolean() |
| { |
| return primitiveTypeMap.get(Name.BOOLEAN); |
| } |
| |
| /** |
| * Returns the COUNTER type. |
| * |
| * @return The COUNTER type. |
| */ |
| public static DataType counter() |
| { |
| return primitiveTypeMap.get(Name.COUNTER); |
| } |
| |
| /** |
| * Returns the DECIMAL type. |
| * |
| * @return The DECIMAL type. |
| */ |
| public static DataType decimal() |
| { |
| return primitiveTypeMap.get(Name.DECIMAL); |
| } |
| |
| /** |
| * Returns the DOUBLE type. |
| * |
| * @return The DOUBLE type. |
| */ |
| public static DataType cdouble() |
| { |
| return primitiveTypeMap.get(Name.DOUBLE); |
| } |
| |
| /** |
| * Returns the FLOAT type. |
| * |
| * @return The FLOAT type. |
| */ |
| public static DataType cfloat() |
| { |
| return primitiveTypeMap.get(Name.FLOAT); |
| } |
| |
| /** |
| * Returns the INET type. |
| * |
| * @return The INET type. |
| */ |
| public static DataType inet() |
| { |
| return primitiveTypeMap.get(Name.INET); |
| } |
| |
| /** |
| * Returns the TINYINT type. |
| * |
| * @return The TINYINT type. |
| */ |
| public static DataType tinyint() |
| { |
| return primitiveTypeMap.get(Name.TINYINT); |
| } |
| |
| /** |
| * Returns the SMALLINT type. |
| * |
| * @return The SMALLINT type. |
| */ |
| public static DataType smallint() |
| { |
| return primitiveTypeMap.get(Name.SMALLINT); |
| } |
| |
| /** |
| * Returns the INT type. |
| * |
| * @return The INT type. |
| */ |
| public static DataType cint() |
| { |
| return primitiveTypeMap.get(Name.INT); |
| } |
| |
| /** |
| * Returns the TEXT type. |
| * |
| * @return The TEXT type. |
| */ |
| public static DataType text() |
| { |
| return primitiveTypeMap.get(Name.TEXT); |
| } |
| |
| /** |
| * Returns the TIMESTAMP type. |
| * |
| * @return The TIMESTAMP type. |
| */ |
| public static DataType timestamp() |
| { |
| return primitiveTypeMap.get(Name.TIMESTAMP); |
| } |
| |
| /** |
| * Returns the DATE type. |
| * |
| * @return The DATE type. |
| */ |
| public static DataType date() |
| { |
| return primitiveTypeMap.get(Name.DATE); |
| } |
| |
| /** |
| * Returns the TIME type. |
| * |
| * @return The TIME type. |
| */ |
| public static DataType time() |
| { |
| return primitiveTypeMap.get(Name.TIME); |
| } |
| |
| /** |
| * Returns the UUID type. |
| * |
| * @return The UUID type. |
| */ |
| public static DataType uuid() |
| { |
| return primitiveTypeMap.get(Name.UUID); |
| } |
| |
| /** |
| * Returns the VARCHAR type. |
| * |
| * @return The VARCHAR type. |
| */ |
| public static DataType varchar() |
| { |
| return primitiveTypeMap.get(Name.VARCHAR); |
| } |
| |
| /** |
| * Returns the VARINT type. |
| * |
| * @return The VARINT type. |
| */ |
| public static DataType varint() |
| { |
| return primitiveTypeMap.get(Name.VARINT); |
| } |
| |
| /** |
| * Returns the TIMEUUID type. |
| * |
| * @return The TIMEUUID type. |
| */ |
| public static DataType timeuuid() |
| { |
| return primitiveTypeMap.get(Name.TIMEUUID); |
| } |
| |
| /** |
| * Returns the type of lists of {@code elementType} elements. |
| * |
| * @param elementType the type of the list elements. |
| * @param frozen whether the list is frozen. |
| * @return the type of lists of {@code elementType} elements. |
| */ |
| public static CollectionType list(DataType elementType, boolean frozen) |
| { |
| return new DataType.CollectionType(Name.LIST, ImmutableList.of(elementType), frozen); |
| } |
| |
| /** |
| * Returns the type of "not frozen" lists of {@code elementType} elements. |
| * |
| * <p>This is a shorthand for {@code list(elementType, false);}. |
| * |
| * @param elementType the type of the list elements. |
| * @return the type of "not frozen" lists of {@code elementType} elements. |
| */ |
| public static CollectionType list(DataType elementType) |
| { |
| return list(elementType, false); |
| } |
| |
| /** |
| * Returns the type of sets of {@code elementType} elements. |
| * |
| * @param elementType the type of the set elements. |
| * @param frozen whether the set is frozen. |
| * @return the type of sets of {@code elementType} elements. |
| */ |
| public static CollectionType set(DataType elementType, boolean frozen) |
| { |
| return new DataType.CollectionType(Name.SET, ImmutableList.of(elementType), frozen); |
| } |
| |
| /** |
| * Returns the type of "not frozen" sets of {@code elementType} elements. |
| * |
| * <p>This is a shorthand for {@code set(elementType, false);}. |
| * |
| * @param elementType the type of the set elements. |
| * @return the type of "not frozen" sets of {@code elementType} elements. |
| */ |
| public static CollectionType set(DataType elementType) |
| { |
| return set(elementType, false); |
| } |
| |
| /** |
| * Returns the type of maps of {@code keyType} to {@code valueType} elements. |
| * |
| * @param keyType the type of the map keys. |
| * @param valueType the type of the map values. |
| * @param frozen whether the map is frozen. |
| * @return the type of maps of {@code keyType} to {@code valueType} elements. |
| */ |
| public static CollectionType map(DataType keyType, DataType valueType, boolean frozen) |
| { |
| return new DataType.CollectionType(Name.MAP, ImmutableList.of(keyType, valueType), frozen); |
| } |
| |
| /** |
| * Returns the type of "not frozen" maps of {@code keyType} to {@code valueType} elements. |
| * |
| * <p>This is a shorthand for {@code map(keyType, valueType, false);}. |
| * |
| * @param keyType the type of the map keys. |
| * @param valueType the type of the map values. |
| * @return the type of "not frozen" maps of {@code keyType} to {@code valueType} elements. |
| */ |
| public static CollectionType map(DataType keyType, DataType valueType) |
| { |
| return map(keyType, valueType, false); |
| } |
| |
| /** |
| * Returns a Custom type. |
| * |
| * <p>A custom type is defined by the name of the class used on the Cassandra side to implement |
| * it. Note that the support for custom types by the driver is limited. |
| * |
| * <p>The use of custom types is rarely useful and is thus not encouraged. |
| * |
| * @param typeClassName the server-side fully qualified class name for the type. |
| * @return the custom type for {@code typeClassName}. |
| */ |
| public static DataType.CustomType custom(String typeClassName) |
| { |
| if (typeClassName == null) throw new NullPointerException(); |
| return new DataType.CustomType(Name.CUSTOM, typeClassName); |
| } |
| |
| /** |
| * Returns the Duration type, introduced in Cassandra 3.10. |
| * |
| * <p>Note that a Duration type does not have a native representation in CQL, and technically, is |
| * merely a special {@link DataType#custom(String) custom type} from the driver's point of view. |
| * |
| * @return the Duration type. The returned instance is a singleton. |
| */ |
| public static DataType duration() |
| { |
| return primitiveTypeMap.get(Name.DURATION); |
| } |
| |
| /** |
| * Returns the name of that type. |
| * |
| * @return the name of that type. |
| */ |
| public Name getName() |
| { |
| return name; |
| } |
| |
| /** |
| * Returns whether this data type is frozen. |
| * |
| * <p>This applies to User Defined Types, tuples and nested collections. Frozen types are |
| * serialized as a single value in Cassandra's storage engine, whereas non-frozen types are stored |
| * in a form that allows updates to individual subfields. |
| * |
| * @return whether this data type is frozen. |
| */ |
| public abstract boolean isFrozen(); |
| |
| /** |
| * Returns whether this data type represent a CQL {@link |
| * DataType.CollectionType collection type}, that is, a list, set or map. |
| * |
| * @return whether this data type name represent the name of a collection type. |
| */ |
| public boolean isCollection() |
| { |
| return this instanceof CollectionType; |
| } |
| |
| /** |
| * Returns the type arguments of this type. |
| * |
| * <p>Note that only the collection types (LIST, MAP, SET) have type arguments. For the other |
| * types, this will return an empty list. |
| * |
| * <p>For the collection types: |
| * |
| * <ul> |
| * <li>For lists and sets, this method returns one argument, the type of the elements. |
| * <li>For maps, this method returns two arguments, the first one is the type of the map keys, |
| * the second one is the type of the map values. |
| * </ul> |
| * |
| * @return an immutable list containing the type arguments of this type. |
| */ |
| public List<DataType> getTypeArguments() |
| { |
| return Collections.emptyList(); |
| } |
| |
| /** |
| * Returns a String representation of this data type suitable for inclusion as a parameter type in |
| * a function or aggregate signature. |
| * |
| * <p>In such places, the String representation might vary from the canonical one as returned by |
| * {@link #toString()}; e.g. the {@code frozen} keyword is not accepted. |
| * |
| * @return a String representation of this data type suitable for inclusion as a parameter type in |
| * a function or aggregate signature. |
| */ |
| public String asFunctionParameterString() |
| { |
| return toString(); |
| } |
| |
| /** |
| * Instances of this class represent CQL native types, also known as CQL primitive types. |
| */ |
| public static class NativeType extends DataType |
| { |
| |
| private NativeType(DataType.Name name) |
| { |
| super(name); |
| } |
| |
| @Override |
| public boolean isFrozen() |
| { |
| return false; |
| } |
| |
| @Override |
| public final int hashCode() |
| { |
| return (name == Name.TEXT) ? Name.VARCHAR.hashCode() : name.hashCode(); |
| } |
| |
| @Override |
| public final boolean equals(Object o) |
| { |
| if (!(o instanceof DataType.NativeType)) return false; |
| |
| NativeType that = (DataType.NativeType) o; |
| return this.name.isCompatibleWith(that.name); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return name.toString(); |
| } |
| } |
| |
| /** |
| * Instances of this class represent collection types, that is, lists, sets or maps. |
| */ |
| public static class CollectionType extends DataType |
| { |
| |
| private final List<DataType> typeArguments; |
| private final boolean frozen; |
| |
| private CollectionType(DataType.Name name, List<DataType> typeArguments, boolean frozen) |
| { |
| super(name); |
| this.typeArguments = typeArguments; |
| this.frozen = frozen; |
| } |
| |
| @Override |
| public boolean isFrozen() |
| { |
| return frozen; |
| } |
| |
| @Override |
| public List<DataType> getTypeArguments() |
| { |
| return typeArguments; |
| } |
| |
| @Override |
| public final int hashCode() |
| { |
| return Objects.hash(name, typeArguments); |
| } |
| |
| @Override |
| public final boolean equals(Object o) |
| { |
| if (!(o instanceof DataType.CollectionType)) return false; |
| |
| DataType.CollectionType d = (DataType.CollectionType) o; |
| return name == d.name && typeArguments.equals(d.typeArguments); |
| } |
| |
| @Override |
| public String toString() |
| { |
| if (name == Name.MAP) |
| { |
| String template = frozen ? "frozen<%s<%s, %s>>" : "%s<%s, %s>"; |
| return String.format(template, name, typeArguments.get(0), typeArguments.get(1)); |
| } |
| else |
| { |
| String template = frozen ? "frozen<%s<%s>>" : "%s<%s>"; |
| return String.format(template, name, typeArguments.get(0)); |
| } |
| } |
| |
| @Override |
| public String asFunctionParameterString() |
| { |
| if (name == Name.MAP) |
| { |
| String template = "%s<%s, %s>"; |
| return String.format( |
| template, |
| name, |
| typeArguments.get(0).asFunctionParameterString(), |
| typeArguments.get(1).asFunctionParameterString()); |
| } |
| else |
| { |
| String template = "%s<%s>"; |
| return String.format(template, name, typeArguments.get(0).asFunctionParameterString()); |
| } |
| } |
| } |
| |
| /** |
| * A "custom" type is a type that cannot be expressed as a CQL type. |
| * |
| * <p>Each custom type is merely identified by the fully qualified {@code |
| * #getCustomTypeClassName() class name} that represents this type server-side. |
| * |
| * <p>The driver provides a minimal support for such types through instances of this class. |
| * |
| * <p>A codec for custom types can be obtained via {@link TypeCodec#custom(DataType.CustomType)}. |
| */ |
| public static class CustomType extends DataType |
| { |
| |
| private final String customClassName; |
| |
| private CustomType(DataType.Name name, String className) |
| { |
| super(name); |
| this.customClassName = className; |
| } |
| |
| @Override |
| public boolean isFrozen() |
| { |
| return false; |
| } |
| |
| @Override |
| public final int hashCode() |
| { |
| return Objects.hash(name, customClassName); |
| } |
| |
| @Override |
| public final boolean equals(Object o) |
| { |
| if (!(o instanceof DataType.CustomType)) return false; |
| |
| DataType.CustomType d = (DataType.CustomType) o; |
| return name == d.name && Objects.equals(customClassName, d.customClassName); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return String.format("'%s'", customClassName); |
| } |
| } |
| } |