com.google.cloud.spanner.Value Maven / Gradle / Ivy
Show all versions of google-cloud-spanner Show documentation
/*
* 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 com.google.cloud.spanner;
import com.google.cloud.ByteArray;
import com.google.cloud.Date;
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.AbstractResultSet.LazyByteArray;
import com.google.cloud.spanner.Type.Code;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.CharSource;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.ProtocolMessageEnum;
import com.google.protobuf.Value.KindCase;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* Represents a value to be consumed by the Cloud Spanner API. A value can be {@code NULL} or
* non-{@code NULL}; regardless, values always have an associated type.
*
* The {@code Value} API is optimized for construction, since this is the majority use-case when
* using this class with the Cloud Spanner libraries. The factory method signatures and internal
* representations are design to minimize memory usage and object creation while still maintaining
* the immutability contract of this class. In particular, arrays of primitive types can be
* constructed without requiring boxing into collections of wrapper types. The getters in this class
* are intended primarily for test purposes, and so do not share the same performance
* characteristics; in particular, getters for array types may be expensive.
*
*
{@code Value} instances are immutable.
*/
@Immutable
public abstract class Value implements Serializable {
/**
* Placeholder value to be passed to a mutation to make Cloud Spanner store the commit timestamp
* in that column. The commit timestamp is the timestamp corresponding to when Cloud Spanner
* commits the transaction containing the mutation.
*
*
Note that this particular timestamp instance has no semantic meaning. In particular the
* value of seconds and nanoseconds in this timestamp are meaningless. This placeholder can only
* be used for columns that have set the option "(allow_commit_timestamp=true)" in the schema.
*
*
When reading the value stored in such a column, the value returned is an actual timestamp
* corresponding to the commit time of the transaction, which has no relation to this placeholder.
*
* @see
* Transaction Semantics
*/
public static final Timestamp COMMIT_TIMESTAMP = Timestamp.ofTimeMicroseconds(0L);
static final com.google.protobuf.Value NULL_PROTO =
com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build();
/** Constant to specify a PG Numeric NaN value. */
public static final String NAN = "NaN";
private static final int MAX_DEBUG_STRING_LENGTH = 36;
private static final String ELLIPSIS = "...";
private static final String NULL_STRING = "NULL";
private static final char LIST_SEPARATOR = ',';
private static final char LIST_OPEN = '[';
private static final char LIST_CLOSE = ']';
private static final long serialVersionUID = -5289864325087675338L;
/**
* Returns a {@link Value} that wraps the given proto value. This can be used to construct a value
* without a specific type, and let the backend infer the type based on the statement where it is
* used.
*
* @param value the non-null proto value (a {@link NullValue} is allowed)
*/
public static Value untyped(com.google.protobuf.Value value) {
return new ProtoBackedValueImpl(Preconditions.checkNotNull(value), null);
}
/** Returns a generic Value backed by a protobuf value. This is used for unrecognized types. */
static Value unrecognized(com.google.protobuf.Value value, Type type) {
Preconditions.checkArgument(
type.getCode() == Code.UNRECOGNIZED
|| type.getCode() == Code.ARRAY
&& type.getArrayElementType().getCode() == Code.UNRECOGNIZED);
return new ProtoBackedValueImpl(Preconditions.checkNotNull(value), type);
}
/**
* Returns a {@code BOOL} value.
*
* @param v the value, which may be null
*/
public static Value bool(@Nullable Boolean v) {
return new BoolImpl(v == null, v == null ? false : v);
}
/** Returns a {@code BOOL} value. */
public static Value bool(boolean v) {
return new BoolImpl(false, v);
}
/**
* Returns an {@code INT64} value.
*
* @param v the value, which may be null
*/
public static Value int64(@Nullable Long v) {
return new Int64Impl(v == null, v == null ? 0 : v);
}
/** Returns an {@code INT64} value. */
public static Value int64(long v) {
return new Int64Impl(false, v);
}
/**
* Returns a {@code FLOAT32} value.
*
* @param v the value, which may be null
*/
public static Value float32(@Nullable Float v) {
return new Float32Impl(v == null, v == null ? 0 : v);
}
/** Returns a {@code FLOAT32} value. */
public static Value float32(float v) {
return new Float32Impl(false, v);
}
/**
* Returns a {@code FLOAT64} value.
*
* @param v the value, which may be null
*/
public static Value float64(@Nullable Double v) {
return new Float64Impl(v == null, v == null ? 0 : v);
}
/** Returns a {@code FLOAT64} value. */
public static Value float64(double v) {
return new Float64Impl(false, v);
}
/**
* Returns a {@code NUMERIC} value. The valid value range for the whole component of the {@link
* BigDecimal} is from -9,999,999,999,999,999,999,999,999 to +9,999,999,999,999,999,999,999,999
* (both inclusive), i.e. the max length of the whole component is 29 digits. The max length of
* the fractional part is 9 digits. Trailing zeros in the fractional part are not considered and
* will be lost, as Cloud Spanner does not preserve the precision of a numeric value.
*
*
If you set a numeric value of a record to for example 0.10, Cloud Spanner will return this
* value as 0.1 in subsequent queries. Use {@link BigDecimal#stripTrailingZeros()} to compare
* inserted values with retrieved values if your application might insert numeric values with
* trailing zeros.
*
* @param v the value, which may be null
*/
public static Value numeric(@Nullable BigDecimal v) {
if (v != null) {
// Cloud Spanner does not preserve the precision, so 0.1 is considered equal to 0.10.
BigDecimal test = v.stripTrailingZeros();
if (test.scale() > 9) {
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.OUT_OF_RANGE,
String.format(
"Max scale for a numeric is 9. The requested numeric has scale %d", test.scale()));
}
if (test.precision() - test.scale() > 29) {
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.OUT_OF_RANGE,
String.format(
"Max precision for the whole component of a numeric is 29. The requested numeric has a whole component with precision %d",
test.precision() - test.scale()));
}
}
return new NumericImpl(v == null, v);
}
/**
* Returns a {@code PG NUMERIC} value. This value has flexible precision and scale which is
* specified in the Database DDL. This value also supports {@code NaNs}, which can be specified
* with {@code Value.pgNumeric(Value.NAN)} or simply as {@code Value.pgNumeric("NaN")}.
*
*
Note that this flavour of numeric is different than Spanner numerics ({@link
* Value#numeric(BigDecimal)}). It should be used only for handling numerics in the PostgreSQL
* dialect.
*
* @param v the value, which may be null
*/
public static Value pgNumeric(@Nullable String v) {
return new PgNumericImpl(v == null, v);
}
/**
* Returns a {@code STRING} value.
*
* @param v the value, which may be null
*/
public static Value string(@Nullable String v) {
return new StringImpl(v == null, v);
}
/**
* Returns a {@code JSON} value.
*
* @param v the value, which may be null
*/
public static Value json(@Nullable String v) {
return new JsonImpl(v == null, v);
}
/**
* Returns a {@code PG JSONB} value.
*
* @param v the value, which may be null
*/
public static Value pgJsonb(@Nullable String v) {
return new PgJsonbImpl(v == null, v);
}
/**
* Returns an {@code PG_OID} value.
*
* @param v the value, which may be null
*/
public static Value pgOid(@Nullable Long v) {
return new PgOidImpl(v == null, v == null ? 0 : v);
}
/** Returns an {@code PG_OID} value. */
public static Value pgOid(long v) {
return new PgOidImpl(false, v);
}
/**
* Return a {@code PROTO} value for not null proto messages.
*
* @param v Not null Proto message.
*/
public static Value protoMessage(AbstractMessage v) {
Preconditions.checkNotNull(
v, "Use protoMessage((ByteArray) null, MyProtoClass.getDescriptor()) for null values.");
return protoMessage(
ByteArray.copyFrom(v.toByteArray()), v.getDescriptorForType().getFullName());
}
/**
* Return a {@code PROTO} value
*
* @param v Serialized Proto Array, which may be null.
* @param protoTypeFqn Fully qualified name of proto representing the proto definition. Use static
* method from proto class {@code MyProtoClass.getDescriptor().getFullName()}
*/
public static Value protoMessage(@Nullable ByteArray v, String protoTypeFqn) {
return new ProtoMessageImpl(v == null, v, protoTypeFqn);
}
/**
* Return a {@code PROTO} value
*
* @param v Serialized Proto Array, which may be null.
* @param descriptor Proto Type Descriptor, use static method from proto class {@code
* MyProtoClass.getDescriptor()}.
*/
public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) {
Preconditions.checkNotNull(descriptor, "descriptor can't be null.");
return protoMessage(v, descriptor.getFullName());
}
/**
* Return a {@code ENUM} value for not null proto messages.
*
* @param v Proto Enum, which may be null.
*/
public static Value protoEnum(ProtocolMessageEnum v) {
Preconditions.checkNotNull(
v, "Use protoEnum((Long) null, MyProtoEnum.getDescriptor()) for null values.");
return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName());
}
/**
* Return a {@code ENUM} value.
*
* @param v Enum non-primitive Integer constant.
* @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static
* method from proto class {@code MyProtoEnum.getDescriptor().getFullName()}
*/
public static Value protoEnum(@Nullable Long v, String protoTypeFqn) {
return new ProtoEnumImpl(v == null, v, protoTypeFqn);
}
/**
* Return a {@code ENUM} value.
*
* @param v Enum non-primitive Integer constant.
* @param enumDescriptor Enum Type Descriptor. Use static method from proto class {@code *
* MyProtoEnum.getDescriptor()}.
*/
public static Value protoEnum(@Nullable Long v, EnumDescriptor enumDescriptor) {
Preconditions.checkNotNull(enumDescriptor, "descriptor can't be null.");
return protoEnum(v, enumDescriptor.getFullName());
}
/**
* Return a {@code ENUM} value.
*
* @param v Enum integer primitive constant.
* @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static
* method from proto class {@code MyProtoEnum.getDescriptor().getFullName()}
*/
public static Value protoEnum(long v, String protoTypeFqn) {
return new ProtoEnumImpl(false, v, protoTypeFqn);
}
/**
* e Returns a {@code BYTES} value. Returns a {@code BYTES} value.
*
* @param v the value, which may be null
*/
public static Value bytes(@Nullable ByteArray v) {
return new LazyBytesImpl(v == null, v);
}
/**
* Returns a {@code BYTES} value.
*
* @param base64String the value in Base64 encoding, which may be null. This value must be a valid
* base64 string.
*/
public static Value bytesFromBase64(@Nullable String base64String) {
return new LazyBytesImpl(
base64String == null, base64String == null ? null : new LazyByteArray(base64String));
}
static Value internalBytes(@Nullable LazyByteArray bytes) {
return new LazyBytesImpl(bytes == null, bytes);
}
/** Returns a {@code TIMESTAMP} value. */
public static Value timestamp(@Nullable Timestamp v) {
return new TimestampImpl(v == null, v == Value.COMMIT_TIMESTAMP, v);
}
/**
* Returns a {@code DATE} value. The range [1678-01-01, 2262-01-01) is the legal interval for
* cloud spanner dates. A write to a date column is rejected if the value is outside of that
* interval.
*/
public static Value date(@Nullable Date v) {
return new DateImpl(v == null, v);
}
/** Returns a non-{@code NULL} {#code STRUCT} value. */
public static Value struct(Struct v) {
Preconditions.checkNotNull(v, "Illegal call to create a NULL struct value.");
return new StructImpl(v);
}
/**
* Returns a {@code STRUCT} value of {@code Type} type.
*
* @param type the type of the {@code STRUCT} value
* @param v the struct {@code STRUCT} value. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. If non-{@code null}, {@link Struct#getType()} must match
* type.
*/
public static Value struct(Type type, @Nullable Struct v) {
if (v == null) {
Preconditions.checkArgument(
type.getCode() == Code.STRUCT,
"Illegal call to create a NULL struct with a non-struct type.");
return new StructImpl(type);
} else {
Preconditions.checkArgument(
type.equals(v.getType()), "Mismatch between struct value and type.");
return new StructImpl(v);
}
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
*/
public static Value boolArray(@Nullable boolean[] v) {
return boolArray(v, 0, v == null ? 0 : v.length);
}
/**
* Returns an {@code ARRAY} value that takes its elements from a region of an array.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
* @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
* null}.
* @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
* null}.
*/
public static Value boolArray(@Nullable boolean[] v, int pos, int length) {
return boolArrayFactory.create(v, pos, length);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value boolArray(@Nullable Iterable v) {
// TODO(user): Consider memory optimizing boolArray() to use BitSet instead of boolean[].
return boolArrayFactory.create(v);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
*/
public static Value int64Array(@Nullable long[] v) {
return int64Array(v, 0, v == null ? 0 : v.length);
}
/**
* Returns an {@code ARRAY} value that takes its elements from a region of an array.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
* @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
* null}.
* @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
* null}.
*/
public static Value int64Array(@Nullable long[] v, int pos, int length) {
return int64ArrayFactory.create(v, pos, length);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value int64Array(@Nullable Iterable v) {
return int64ArrayFactory.create(v);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
*/
public static Value float32Array(@Nullable float[] v) {
return float32Array(v, 0, v == null ? 0 : v.length);
}
/**
* Returns an {@code ARRAY} value that takes its elements from a region of an array.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
* @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
* null}.
* @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
* null}.
*/
public static Value float32Array(@Nullable float[] v, int pos, int length) {
return float32ArrayFactory.create(v, pos, length);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value float32Array(@Nullable Iterable v) {
return float32ArrayFactory.create(v);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
*/
public static Value float64Array(@Nullable double[] v) {
return float64Array(v, 0, v == null ? 0 : v.length);
}
/**
* Returns an {@code ARRAY} value that takes its elements from a region of an array.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
* @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
* null}.
* @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
* null}.
*/
public static Value float64Array(@Nullable double[] v, int pos, int length) {
return float64ArrayFactory.create(v, pos, length);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value float64Array(@Nullable Iterable v) {
return float64ArrayFactory.create(v);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value numericArray(@Nullable Iterable v) {
return new NumericArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}. Individual
* elements may be {@code "NaN"} or {@link Value#NAN}.
*/
public static Value pgNumericArray(@Nullable Iterable v) {
return new PgNumericArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value stringArray(@Nullable Iterable v) {
return new StringArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value jsonArray(@Nullable Iterable v) {
return new JsonArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value pgJsonbArray(@Nullable Iterable v) {
return new PgJsonbArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
*/
public static Value pgOidArray(@Nullable long[] v) {
return pgOidArray(v, 0, v == null ? 0 : v.length);
}
/**
* Returns an {@code ARRAY} value that takes its elements from a region of an array.
*
* @param v the source of element values, which may be null to produce a value for which {@code
* isNull()} is {@code true}
* @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
* null}.
* @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
* null}.
*/
public static Value pgOidArray(@Nullable long[] v, int pos, int length) {
return pgOidArrayFactory.create(v, pos, length);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value pgOidArray(@Nullable Iterable v) {
return pgOidArrayFactory.create(v);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
* @param descriptor Proto Type Descriptor, use static method from proto class {@code
* MyProtoClass.getDescriptor()}.
*/
public static Value protoMessageArray(
@Nullable Iterable v, Descriptor descriptor) {
if (v == null) {
return new ProtoMessageArrayImpl(true, null, descriptor.getFullName());
}
List serializedArray = new ArrayList<>();
v.forEach(
(message) -> {
if (message != null) {
serializedArray.add(ByteArray.copyFrom(message.toByteArray()));
} else {
serializedArray.add(null);
}
});
return new ProtoMessageArrayImpl(false, serializedArray, descriptor.getFullName());
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
* @param protoTypeFqn Fully qualified name of proto representing the proto definition. Use static
* method from proto class {@code MyProtoClass.getDescriptor().getFullName()}
*/
public static Value protoMessageArray(@Nullable Iterable v, String protoTypeFqn) {
return new ProtoMessageArrayImpl(
v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
* @param descriptor Proto Type Descriptor, use static method from proto class {@code
* MyProtoClass.getDescriptor()}.
*/
public static Value protoEnumArray(
@Nullable Iterable v, EnumDescriptor descriptor) {
if (v == null) {
return new ProtoEnumArrayImpl(true, null, descriptor.getFullName());
}
List enumConstValues = new ArrayList<>();
v.forEach(
(protoEnum) -> {
if (protoEnum != null) {
enumConstValues.add((long) protoEnum.getNumber());
} else {
enumConstValues.add(null);
}
});
return new ProtoEnumArrayImpl(false, enumConstValues, descriptor.getFullName());
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
* @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static
* method from proto class {@code MyProtoEnum.getDescriptor().getFullName()}
*/
public static Value protoEnumArray(@Nullable Iterable v, String protoTypeFqn) {
return new ProtoEnumArrayImpl(v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value bytesArray(@Nullable Iterable v) {
return new LazyBytesArrayImpl(v == null, v == null ? null : byteArraysToLazyByteArrayList(v));
}
private static List byteArraysToLazyByteArrayList(Iterable byteArrays) {
List list = new ArrayList<>();
for (ByteArray byteArray : byteArrays) {
list.add(byteArray == null ? null : new LazyByteArray(byteArray));
}
return Collections.unmodifiableList(list);
}
/**
* Returns an {@code ARRAY} value.
*
* @param base64Strings the source of element values. This may be {@code null} to produce a value
* for which {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
* Non-null values must be a valid Base64 string.
*/
public static Value bytesArrayFromBase64(@Nullable Iterable base64Strings) {
return new LazyBytesArrayImpl(
base64Strings == null,
base64Strings == null ? null : base64StringsToLazyByteArrayList(base64Strings));
}
private static List base64StringsToLazyByteArrayList(
Iterable base64Strings) {
List list = new ArrayList<>();
for (String base64 : base64Strings) {
list.add(base64 == null ? null : new LazyByteArray(base64));
}
return Collections.unmodifiableList(list);
}
/**
* Returns an {@code ARRAY} value.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value timestampArray(@Nullable Iterable v) {
return new TimestampArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY} value. The range [1678-01-01, 2262-01-01) is the legal interval
* for cloud spanner dates. A write to a date column is rejected if the value is outside of that
* interval.
*
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value dateArray(@Nullable Iterable v) {
return new DateArrayImpl(v == null, v == null ? null : immutableCopyOf(v));
}
/**
* Returns an {@code ARRAY>} value.
*
* @param elementType
* @param v the source of element values. This may be {@code null} to produce a value for which
* {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
*/
public static Value structArray(Type elementType, @Nullable Iterable v) {
if (v == null) {
Preconditions.checkArgument(
elementType.getCode() == Code.STRUCT,
"Illegal call to create a NULL array-of-struct with a non-struct element type.");
return new StructArrayImpl(elementType, null);
}
List values = immutableCopyOf(v);
for (Struct value : values) {
if (value != null) {
Preconditions.checkArgument(
value.getType().equals(elementType),
"Members of v must have type %s (found %s)",
elementType,
value.getType());
}
}
return new StructArrayImpl(elementType, values);
}
private Value() {}
/** Returns the type of this value. This will return a type even if {@code isNull()} is true. */
public abstract Type getType();
/** Returns {@code true} if this instance represents a {@code NULL} value. */
public abstract boolean isNull();
/**
* Returns the value of a {@code BOOL}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract boolean getBool();
/**
* Returns the value of a {@code INT64}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract long getInt64();
/**
* Returns the value of a {@code FLOAT32}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract float getFloat32();
/**
* Returns the value of a {@code FLOAT64}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract double getFloat64();
/**
* Returns the value of a {@code NUMERIC}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract BigDecimal getNumeric();
/**
* Returns the value of a {@code STRING}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract String getString();
/**
* Returns the value of a {@code JSON}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public String getJson() {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of a {@code JSONB}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public String getPgJsonb() {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of a {@code PROTO}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public T getProtoMessage(T m) {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of a {@code ENUM}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public T getProtoEnum(
Function method) {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of a {@code BYTES}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract ByteArray getBytes();
/**
* Returns the value of a {@code TIMESTAMP}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type or
* {@link #isCommitTimestamp()}.
*/
public abstract Timestamp getTimestamp();
/** Returns true if this is a commit timestamp value. */
public abstract boolean isCommitTimestamp();
/**
* Returns the value of a {@code DATE}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract Date getDate();
/**
* Returns the value of a {@code STRUCT}-typed instance.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract Struct getStruct();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself will
* never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getBoolArray();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getInt64Array();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getFloat32Array();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getFloat64Array();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getNumericArray();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getStringArray();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself will
* never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public List getJsonArray() {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public List getPgJsonbArray() {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public List getProtoMessageArray(T m) {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself will
* never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public List getProtoEnumArray(
Function method) {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getBytesArray();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getTimestampArray();
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself will
* never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getDateArray();
/**
* Returns the value of an {@code ARRAY>}-typed instance. While the returned list
* itself will never be {@code null}, elements of that list may be null.
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
public abstract List getStructArray();
@Override
public String toString() {
StringBuilder b = new StringBuilder();
toString(b);
return b.toString();
}
/**
* Returns this value as a raw string representation. This is guaranteed to work for all values,
* regardless of the underlying data type, and is guaranteed not to be truncated.
*
* Returns the string "NULL" for null values.
*/
@Nonnull
public String getAsString() {
return toString();
}
/**
* Returns this value as a list of raw string representations. This is guaranteed to work for all
* values, regardless of the underlying data type, and the strings are guaranteed not to be
* truncated. The method returns a singleton list for non-array values and a list containing as
* many elements as there are in the array for array values. This method can be used instead of
* the {@link #getAsString()} method if you need to quote the individual elements in an array.
*
*