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

com.google.cloud.spanner.Struct Maven / Gradle / Ivy

There is a newer version: 6.81.1
Show newest version
/*
 * 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 static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.cloud.ByteArray;
import com.google.cloud.Date;
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.Type.Code;
import com.google.cloud.spanner.Type.StructField;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Longs;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.ProtocolMessageEnum;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import javax.annotation.concurrent.Immutable;

/**
 * Represents a non-{@code NULL} value of {@link Type.Code#STRUCT}. Such values are a tuple of named
 * and typed columns, where individual columns may be null. Individual rows from a read or query
 * operation can be considered as structs; {@link ResultSet#getCurrentRowAsStruct()} allows an
 * immutable struct to be created from the row that the result set is currently positioned over.
 *
 * 

{@code Struct} instances are immutable. * *

This class does not support representing typed {@code NULL} {@code Struct} values. * *

However, struct values inside SQL queries are always typed and can be externally * supplied to a query only in the form of struct/array-of-struct query parameter values for which * typed {@code NULL} struct values can be specified in the following ways: * *

1. As a standalone {@code NULL} struct value or as a nested struct field value, constructed * using {@link ValueBinder#to(Type, Struct)} or {@link Value#struct(Type, Struct)}. * *

2. As as a null {@code Struct} reference representing a {@code NULL} struct typed element * value inside an array/list of '{@code Struct}' references, that is used to construct an * array-of-struct value using {@link Value#structArray(Type, Iterable)} or {@link * ValueBinder#toStructArray(Type, Iterable)}. In this case, the type of the {@code NULL} struct * value is assumed to be the same as the explicitly specified struct element type of the * array/list. */ @Immutable public abstract class Struct extends AbstractStructReader implements Serializable { // Only implementations within the package are allowed. Struct() {} /** Returns a builder for creating a non-{@code NULL} {@code Struct} instance. */ public static Builder newBuilder() { return new Builder(); } /** Builder for constructing non-{@code NULL} {@code Struct} instances. */ public static final class Builder { private final List types = new ArrayList<>(); private final List values = new ArrayList<>(); private final ValueBinder binder; private String currentField; private Builder() { this.binder = new ValueBinder() { @Override Builder handle(Value value) { checkBindingInProgress(true); addInternal(currentField, value); currentField = null; return Builder.this; } }; } /** * Returns a binder to set the value of a new field in the struct named {@code fieldName}. * * @param fieldName name of the field to set. Can be empty or the same as an existing field name * in the {@code STRUCT} */ public ValueBinder set(String fieldName) { checkBindingInProgress(false); currentField = checkNotNull(fieldName); return binder; } /** Adds a new unnamed field {@code fieldName} with the given value. */ public Builder add(Value value) { checkBindingInProgress(false); addInternal("", value); return this; } public Struct build() { checkBindingInProgress(false); return new ValueListStruct(types, values); } private void addInternal(String fieldName, Value value) { types.add(Type.StructField.of(fieldName, value.getType())); values.add(value); } private void checkBindingInProgress(boolean expectInProgress) { if (expectInProgress) { checkState(currentField != null, "No binding currently active"); } else if (currentField != null) { throw new IllegalStateException("Incomplete binding for column " + currentField); } } } /** * TODO(user) : Consider moving these methods to the StructReader interface once STRUCT-typed * columns are supported in {@link ResultSet}. */ /* Public methods for accessing struct-typed fields */ public Struct getStruct(int columnIndex) { checkNonNullStruct(columnIndex, columnIndex); return getStructInternal(columnIndex); } public Struct getStruct(String columnName) { int columnIndex = getColumnIndex(columnName); checkNonNullStruct(columnIndex, columnName); return getStructInternal(columnIndex); } /* Sub-classes must implement this method */ protected abstract Struct getStructInternal(int columnIndex); private void checkNonNullStruct(int columnIndex, Object columnNameForError) { Type actualType = getColumnType(columnIndex); checkState( actualType.getCode() == Code.STRUCT, "Column %s is not of correct type: expected STRUCT<...> but was %s", columnNameForError, actualType); checkNonNull(columnIndex, columnNameForError); } /** Default implementation for value structs produced by {@link Builder}. */ private static class ValueListStruct extends Struct { private final Type type; private final List values; private ValueListStruct(Iterable types, Iterable values) { this.type = Type.struct(types); this.values = ImmutableList.copyOf(values); } @Override protected boolean getBooleanInternal(int columnIndex) { return values.get(columnIndex).getBool(); } @Override protected long getLongInternal(int columnIndex) { return values.get(columnIndex).getInt64(); } @Override protected float getFloatInternal(int columnIndex) { return values.get(columnIndex).getFloat32(); } @Override protected double getDoubleInternal(int columnIndex) { return values.get(columnIndex).getFloat64(); } @Override protected BigDecimal getBigDecimalInternal(int columnIndex) { return values.get(columnIndex).getNumeric(); } @Override protected String getStringInternal(int columnIndex) { return values.get(columnIndex).getString(); } @Override protected String getJsonInternal(int columnIndex) { return values.get(columnIndex).getJson(); } @Override protected String getPgJsonbInternal(int columnIndex) { return values.get(columnIndex).getPgJsonb(); } @Override protected ByteArray getBytesInternal(int columnIndex) { return values.get(columnIndex).getBytes(); } @Override protected Timestamp getTimestampInternal(int columnIndex) { return values.get(columnIndex).getTimestamp(); } @Override protected Date getDateInternal(int columnIndex) { return values.get(columnIndex).getDate(); } @Override protected T getProtoMessageInternal(int columnIndex, T message) { return values.get(columnIndex).getProtoMessage(message); } @Override protected T getProtoEnumInternal( int columnIndex, Function method) { return values.get(columnIndex).getProtoEnum(method); } @Override protected Value getValueInternal(int columnIndex) { return values.get(columnIndex); } @Override protected Struct getStructInternal(int columnIndex) { return values.get(columnIndex).getStruct(); } @Override protected boolean[] getBooleanArrayInternal(int columnIndex) { return Booleans.toArray(getBooleanListInternal(columnIndex)); } @Override protected List getBooleanListInternal(int columnIndex) { return values.get(columnIndex).getBoolArray(); } @Override protected long[] getLongArrayInternal(int columnIndex) { return Longs.toArray(getLongListInternal(columnIndex)); } @Override protected List getLongListInternal(int columnIndex) { return values.get(columnIndex).getInt64Array(); } @Override protected float[] getFloatArrayInternal(int columnIndex) { return Floats.toArray(getFloatListInternal(columnIndex)); } @Override protected List getFloatListInternal(int columnIndex) { return values.get(columnIndex).getFloat32Array(); } @Override protected double[] getDoubleArrayInternal(int columnIndex) { return Doubles.toArray(getDoubleListInternal(columnIndex)); } @Override protected List getDoubleListInternal(int columnIndex) { return values.get(columnIndex).getFloat64Array(); } @Override protected List getBigDecimalListInternal(int columnIndex) { return values.get(columnIndex).getNumericArray(); } @Override protected List getStringListInternal(int columnIndex) { return values.get(columnIndex).getStringArray(); } @Override protected List getJsonListInternal(int columnIndex) { return values.get(columnIndex).getJsonArray(); } @Override protected List getPgJsonbListInternal(int columnIndex) { return values.get(columnIndex).getPgJsonbArray(); } @Override protected List getBytesListInternal(int columnIndex) { return values.get(columnIndex).getBytesArray(); } @Override protected List getTimestampListInternal(int columnIndex) { return values.get(columnIndex).getTimestampArray(); } @Override protected List getProtoMessageListInternal( int columnIndex, T message) { return values.get(columnIndex).getProtoMessageArray(message); } @Override protected List getProtoEnumListInternal( int columnIndex, Function method) { return values.get(columnIndex).getProtoEnumArray(method); } @Override protected List getDateListInternal(int columnIndex) { return values.get(columnIndex).getDateArray(); } @Override protected List getStructListInternal(int columnIndex) { return values.get(columnIndex).getStructArray(); } @Override public Type getType() { return type; } @Override public boolean isNull(int columnIndex) { return values.get(columnIndex).isNull(); } @Override public String toString() { // TODO(user): Consider pulling a generic toString() up to Struct. return values.toString(); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Struct)) { return false; } Struct that = (Struct) o; if (!getType().equals(that.getType())) { return false; } for (int i = 0; i < getColumnCount(); ++i) { if (!Objects.equals(getAsObject(i), that.getAsObject(i))) { return false; } } return true; } @Override public int hashCode() { int result = getType().hashCode(); for (int i = 0; i < getColumnCount(); ++i) { result = 31 * result + Objects.hashCode(getAsObject(i)); } return result; } private Object getAsObject(int columnIndex) { Type type = getColumnType(columnIndex); if (isNull(columnIndex)) { return null; } switch (type.getCode()) { case BOOL: return getBooleanInternal(columnIndex); case INT64: case PG_OID: case ENUM: return getLongInternal(columnIndex); case FLOAT32: return getFloatInternal(columnIndex); case FLOAT64: return getDoubleInternal(columnIndex); case NUMERIC: return getBigDecimalInternal(columnIndex); case PG_NUMERIC: return getStringInternal(columnIndex); case STRING: return getStringInternal(columnIndex); case JSON: return getJsonInternal(columnIndex); case PG_JSONB: return getPgJsonbInternal(columnIndex); case BYTES: case PROTO: return getBytesInternal(columnIndex); case TIMESTAMP: return getTimestampInternal(columnIndex); case DATE: return getDateInternal(columnIndex); case STRUCT: return getStructInternal(columnIndex); case ARRAY: switch (type.getArrayElementType().getCode()) { case BOOL: return getBooleanListInternal(columnIndex); case INT64: case PG_OID: case ENUM: return getLongListInternal(columnIndex); case FLOAT32: return getFloatListInternal(columnIndex); case FLOAT64: return getDoubleListInternal(columnIndex); case NUMERIC: return getBigDecimalListInternal(columnIndex); case PG_NUMERIC: return getStringListInternal(columnIndex); case STRING: return getStringListInternal(columnIndex); case JSON: return getJsonListInternal(columnIndex); case PG_JSONB: return getPgJsonbListInternal(columnIndex); case BYTES: case PROTO: return getBytesListInternal(columnIndex); case TIMESTAMP: return getTimestampListInternal(columnIndex); case DATE: return getDateListInternal(columnIndex); case STRUCT: return getStructListInternal(columnIndex); default: throw new AssertionError("Invalid type " + type); } default: throw new AssertionError("Invalid type " + type); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy