All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.topicus.jdbc.shaded.com.google.cloud.spanner.Type Maven / Gradle / Ivy

/*
 * Copyright 2017 Google LLC
 *
 * 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 nl.topicus.jdbc.shaded.com.google.cloud.spanner;

import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkArgument;

import nl.topicus.jdbc.shaded.com.google.common.base.Preconditions;
import nl.topicus.jdbc.shaded.com.google.common.base.Strings;
import nl.topicus.jdbc.shaded.com.google.common.collect.ImmutableList;
import nl.topicus.jdbc.shaded.com.google.common.collect.ImmutableMap;
import nl.topicus.jdbc.shaded.com.google.spanner.v1.TypeCode;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import nl.topicus.jdbc.shaded.javax.annotation.Nullable;
import nl.topicus.jdbc.shaded.javax.annotation.concurrent.Immutable;

/**
 * Describes a type in the Cloud Spanner type system. Types can either be primitive (for example,
 * {@code INT64} and {@code STRING}) or composite (for example, {@code ARRAY} or {@code
 * STRUCT}).
 *
 * 

{@code Type} instances are immutable. */ @Immutable public final class Type implements Serializable { private static final Type TYPE_BOOL = new Type(Code.BOOL, null, null); private static final Type TYPE_INT64 = new Type(Code.INT64, null, null); private static final Type TYPE_FLOAT64 = new Type(Code.FLOAT64, null, null); private static final Type TYPE_STRING = new Type(Code.STRING, null, null); private static final Type TYPE_BYTES = new Type(Code.BYTES, null, null); private static final Type TYPE_TIMESTAMP = new Type(Code.TIMESTAMP, null, null); private static final Type TYPE_DATE = new Type(Code.DATE, null, null); private static final Type TYPE_ARRAY_BOOL = new Type(Code.ARRAY, TYPE_BOOL, null); private static final Type TYPE_ARRAY_INT64 = new Type(Code.ARRAY, TYPE_INT64, null); private static final Type TYPE_ARRAY_FLOAT64 = new Type(Code.ARRAY, TYPE_FLOAT64, null); private static final Type TYPE_ARRAY_STRING = new Type(Code.ARRAY, TYPE_STRING, null); private static final Type TYPE_ARRAY_BYTES = new Type(Code.ARRAY, TYPE_BYTES, null); private static final Type TYPE_ARRAY_TIMESTAMP = new Type(Code.ARRAY, TYPE_TIMESTAMP, null); private static final Type TYPE_ARRAY_DATE = new Type(Code.ARRAY, TYPE_DATE, null); private static final int AMBIGUOUS_FIELD = -1; private static final long serialVersionUID = -3076152125004114582L; /** Returns the descriptor for the {@code BOOL type}. */ public static Type bool() { return TYPE_BOOL; } /** * Returns the descriptor for the {@code INT64} type: an integral type with the same value domain * as a Java {@code long}. */ public static Type int64() { return TYPE_INT64; } /** * Returns the descriptor for the {@code FLOAT64} type: a floating point type with the same value * domain as a Java {code double}. */ public static Type float64() { return TYPE_FLOAT64; } /** * Returns the descriptor for the {@code STRING} type: a variable-length Unicode character string. */ public static Type string() { return TYPE_STRING; } /** Returns the descriptor for the {@code BYTES} type: a variable-length byte string. */ public static Type bytes() { return TYPE_BYTES; } /** * Returns the descriptor for the {@code TIMESTAMP} type: a nano precision timestamp in the range * [0000-01-01 00:00:00, 9999-12-31 23:59:59.999999999 UTC]. */ public static Type timestamp() { return TYPE_TIMESTAMP; } /** * Returns the descriptor for the {@code DATE} type: a timezone independent date in the range * [1678-01-01, 2262-01-01). */ public static Type date() { return TYPE_DATE; } /** Returns a descriptor for an array of {@code elementType}. */ public static Type array(Type elementType) { Preconditions.checkNotNull(elementType); switch (elementType.getCode()) { case BOOL: return TYPE_ARRAY_BOOL; case INT64: return TYPE_ARRAY_INT64; case FLOAT64: return TYPE_ARRAY_FLOAT64; case STRING: return TYPE_ARRAY_STRING; case BYTES: return TYPE_ARRAY_BYTES; case TIMESTAMP: return TYPE_ARRAY_TIMESTAMP; case DATE: return TYPE_ARRAY_DATE; default: return new Type(Code.ARRAY, elementType, null); } } /** * Returns a descriptor for a {@code STRUCT} type: an ordered collection of named and typed * fields. */ public static Type struct(Iterable fields) { return new Type(Code.STRUCT, null, ImmutableList.copyOf(fields)); } /** * Returns a descriptor for a {@code STRUCT} type: an ordered collection of named and typed * fields. */ public static Type struct(StructField... fields) { return new Type(Code.STRUCT, null, ImmutableList.copyOf(fields)); } private final Code code; private final Type arrayElementType; private final ImmutableList structFields; /** * Map of field name to field index. Ambiguous names are indexed to {@link #AMBIGUOUS_FIELD}. The * map is lazily initialized with a benign race. */ private Map fieldsByName; private Type( Code code, @Nullable Type arrayElementType, @Nullable ImmutableList structFields) { this.code = code; this.arrayElementType = arrayElementType; this.structFields = structFields; } /** Enumerates the categories of types. */ public enum Code { BOOL(TypeCode.BOOL), INT64(TypeCode.INT64), FLOAT64(TypeCode.FLOAT64), STRING(TypeCode.STRING), BYTES(TypeCode.BYTES), TIMESTAMP(TypeCode.TIMESTAMP), DATE(TypeCode.DATE), ARRAY(TypeCode.ARRAY), STRUCT(TypeCode.STRUCT); private static final Map protoToCode; static { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Code code : Code.values()) { builder.put(code.protoCode(), code); } protoToCode = builder.build(); } private final TypeCode protoCode; Code(TypeCode protoCode) { this.protoCode = protoCode; } TypeCode protoCode() { return protoCode; } static Code fromProtoCode(TypeCode protoCode) { Code code = protoToCode.get(protoCode); checkArgument(code != null, "Invalid code: %s", protoCode); return code; } } /** Describes an individual field in a {@code STRUCT type}. */ public static final class StructField implements Serializable { private static final long serialVersionUID = 8640511292704408210L; private final String name; private final Type type; public static StructField of(String name, Type type) { return new StructField(name, type); } private StructField(String name, Type type) { this.name = Preconditions.checkNotNull(name); this.type = Preconditions.checkNotNull(type); } public String getName() { return name; } public Type getType() { return type; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } StructField that = (StructField) o; return name.equals(that.name) && type.equals(that.type); } @Override public int hashCode() { return Objects.hash(name, type); } } /** Returns the type code corresponding to this type. */ public Code getCode() { return code; } /** * Returns the type descriptor for elements of this {@code ARRAY} type. * * @throws IllegalStateException if {@code code() != Code.ARRAY} */ public Type getArrayElementType() { Preconditions.checkState(code == Code.ARRAY, "Illegal call for non-ARRAY type"); return arrayElementType; } /** * Returns the fields of this {@code STRUCT} type. * * @return an immutable list of the fields * @throws IllegalStateException if {@code code() != Code.STRUCT} */ public List getStructFields() { Preconditions.checkState(code == Code.STRUCT, "Illegal call for non-STRUCT type"); return structFields; } /** * Returns the index of the field named {@code fieldName} in this {@code STRUCT} type. * * @throws IllegalArgumentException if there is not exactly one element of {@link * #getStructFields()} with {@link StructField#getName()} equal to {@code fieldName} * @throws IllegalStateException if {@code code() != Code.STRUCT} */ public int getFieldIndex(String fieldName) { Preconditions.checkState(code == Code.STRUCT, "Illegal call for non-STRUCT type"); if (fieldsByName == null) { Map tmp = new TreeMap<>(); for (int i = 0; i < getStructFields().size(); ++i) { Type.StructField field = getStructFields().get(i); if (tmp.put(field.getName(), i) != null) { // Column name appears more than once: mark as ambiguous. tmp.put(field.getName(), AMBIGUOUS_FIELD); } } // Benign race: Java's final field semantics mean that if we see a non-null "fieldsByName", // we are guaranteed to see it in a fully initialized state. It is thus important that we // use an ImmutableMap here, which necessarily uses final fields or equivalent reasoning. // Since all computations of "fieldsByName" produce the same value, there is no risk of // inconsistency. fieldsByName = ImmutableMap.copyOf(tmp); } Integer index = fieldsByName.get(fieldName); if (index == null) { throw new IllegalArgumentException("Field not found: " + fieldName); } if (index == AMBIGUOUS_FIELD) { throw new IllegalArgumentException("Ambiguous field name: " + fieldName); } return index; } void toString(StringBuilder b) { if (code == Code.ARRAY) { b.append("ARRAY<"); arrayElementType.toString(b); b.append('>'); } else if (code == Code.STRUCT) { b.append("STRUCT<"); for (int i = 0; i < structFields.size(); ++i) { if (i > 0) { b.append(", "); } StructField f = structFields.get(i); b.append(f.getName()).append(' '); f.getType().toString(b); } b.append('>'); } else { b.append(code.toString()); } } @Override public String toString() { StringBuilder b = new StringBuilder(); toString(b); return b.toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Type that = (Type) o; return code == that.code && Objects.equals(arrayElementType, that.arrayElementType) && Objects.equals(structFields, that.structFields); } @Override public int hashCode() { return Objects.hash(code, arrayElementType, structFields); } nl.topicus.jdbc.shaded.com.google.spanner.v1.Type toProto() { nl.topicus.jdbc.shaded.com.google.spanner.v1.Type.Builder proto = nl.topicus.jdbc.shaded.com.google.spanner.v1.Type.newBuilder(); proto.setCode(code.protoCode()); if (code == Code.ARRAY) { proto.setArrayElementType(arrayElementType.toProto()); } else if (code == Code.STRUCT) { nl.topicus.jdbc.shaded.com.google.spanner.v1.StructType.Builder fields = proto.getStructTypeBuilder(); for (StructField field : structFields) { fields.addFieldsBuilder().setName(field.getName()).setType(field.getType().toProto()); } } return proto.build(); } static Type fromProto(nl.topicus.jdbc.shaded.com.google.spanner.v1.Type proto) { Code type = Code.fromProtoCode(proto.getCode()); switch (type) { case BOOL: return bool(); case INT64: return int64(); case FLOAT64: return float64(); case STRING: return string(); case BYTES: return bytes(); case TIMESTAMP: return timestamp(); case DATE: return date(); case ARRAY: checkArgument( proto.hasArrayElementType(), "Missing expected 'array_element_type' field in 'Type' message: %s", proto); Type elementType; try { elementType = fromProto(proto.getArrayElementType()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( "Could not parse 'array_element_type' attribute in 'Type' message: " + proto, e); } return array(elementType); case STRUCT: checkArgument( proto.hasStructType(), "Missing expected 'struct_type' field in 'Type' message: %s", proto); List fields = new ArrayList<>(proto.getStructType().getFieldsCount()); for (nl.topicus.jdbc.shaded.com.google.spanner.v1.StructType.Field field : proto.getStructType().getFieldsList()) { checkArgument(field.hasType(), "Missing expected 'type' attribute in 'Field': %s", proto); // Names may be empty; for example, the name of the column returned by "SELECT 1". String name = Strings.nullToEmpty(field.getName()); fields.add(StructField.of(name, fromProto(field.getType()))); } return struct(fields); default: throw new AssertionError("Unimplemented case: " + type); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy