diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml
index 641389cda9..c6f4c1f3a9 100644
--- a/google-cloud-spanner/clirr-ignored-differences.xml
+++ b/google-cloud-spanner/clirr-ignored-differences.xml
@@ -592,4 +592,17 @@
com/google/cloud/spanner/AsyncTransactionManager$CommitTimestampFuture
java.lang.Object get()
+
+
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ com.google.cloud.spanner.Value getValue(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ com.google.cloud.spanner.Value getValue(java.lang.String)
+
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
index 00391447d9..6dd8d485ba 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
@@ -28,6 +28,7 @@
import com.google.cloud.ByteArray;
import com.google.cloud.Date;
import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.Type.StructField;
import com.google.cloud.spanner.spi.v1.SpannerRpc;
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
import com.google.common.annotations.VisibleForTesting;
@@ -679,6 +680,62 @@ protected Date getDateInternal(int columnIndex) {
return (Date) rowData.get(columnIndex);
}
+ @Override
+ protected Value getValueInternal(int columnIndex) {
+ final List structFields = getType().getStructFields();
+ final StructField structField = structFields.get(columnIndex);
+ final Type columnType = structField.getType();
+ final boolean isNull = rowData.get(columnIndex) == null;
+ switch (columnType.getCode()) {
+ case BOOL:
+ return Value.bool(isNull ? null : getBooleanInternal(columnIndex));
+ case INT64:
+ return Value.int64(isNull ? null : getLongInternal(columnIndex));
+ case NUMERIC:
+ return Value.numeric(isNull ? null : getBigDecimalInternal(columnIndex));
+ case FLOAT64:
+ return Value.float64(isNull ? null : getDoubleInternal(columnIndex));
+ case STRING:
+ return Value.string(isNull ? null : getStringInternal(columnIndex));
+ case BYTES:
+ return Value.bytes(isNull ? null : getBytesInternal(columnIndex));
+ case TIMESTAMP:
+ return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex));
+ case DATE:
+ return Value.date(isNull ? null : getDateInternal(columnIndex));
+ case STRUCT:
+ return Value.struct(isNull ? null : getStructInternal(columnIndex));
+ case ARRAY:
+ switch (columnType.getArrayElementType().getCode()) {
+ case BOOL:
+ return Value.boolArray(isNull ? null : getBooleanListInternal(columnIndex));
+ case INT64:
+ return Value.int64Array(isNull ? null : getLongListInternal(columnIndex));
+ case NUMERIC:
+ return Value.numericArray(isNull ? null : getBigDecimalListInternal(columnIndex));
+ case FLOAT64:
+ return Value.float64Array(isNull ? null : getDoubleListInternal(columnIndex));
+ case STRING:
+ return Value.stringArray(isNull ? null : getStringListInternal(columnIndex));
+ case BYTES:
+ return Value.bytesArray(isNull ? null : getBytesListInternal(columnIndex));
+ case TIMESTAMP:
+ return Value.timestampArray(isNull ? null : getTimestampListInternal(columnIndex));
+ case DATE:
+ return Value.dateArray(isNull ? null : getDateListInternal(columnIndex));
+ case STRUCT:
+ return Value.structArray(
+ columnType.getArrayElementType(),
+ isNull ? null : getStructListInternal(columnIndex));
+ default:
+ throw new IllegalArgumentException(
+ "Invalid array value type " + this.type.getArrayElementType());
+ }
+ default:
+ throw new IllegalArgumentException("Invalid value type " + this.type);
+ }
+ }
+
@Override
protected Struct getStructInternal(int columnIndex) {
return (Struct) rowData.get(columnIndex);
@@ -1280,6 +1337,11 @@ protected Date getDateInternal(int columnIndex) {
return currRow().getDateInternal(columnIndex);
}
+ @Override
+ protected Value getValueInternal(int columnIndex) {
+ return currRow().getValueInternal(columnIndex);
+ }
+
@Override
protected boolean[] getBooleanArrayInternal(int columnIndex) {
return currRow().getBooleanArrayInternal(columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
index 75e683e2e1..9b3f1810eb 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
@@ -49,6 +49,10 @@ public abstract class AbstractStructReader implements StructReader {
protected abstract Date getDateInternal(int columnIndex);
+ protected Value getValueInternal(int columnIndex) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
protected abstract boolean[] getBooleanArrayInternal(int columnIndex);
protected abstract List getBooleanListInternal(int columnIndex);
@@ -197,6 +201,18 @@ public Date getDate(String columnName) {
return getDateInternal(columnIndex);
}
+ @Override
+ public Value getValue(int columnIndex) {
+ checkNonNull(columnIndex, columnIndex);
+ return getValueInternal(columnIndex);
+ }
+
+ @Override
+ public Value getValue(String columnName) {
+ int columnIndex = getColumnIndex(columnName);
+ return getValueInternal(columnIndex);
+ }
+
@Override
public boolean[] getBooleanArray(int columnIndex) {
checkNonNullOfType(columnIndex, Type.array(Type.bool()), columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
index f7e49daafd..d5f9488178 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
@@ -333,4 +333,16 @@ public List getStructList(String columnName) {
checkValidState();
return delegate.get().getStructList(columnName);
}
+
+ @Override
+ public Value getValue(int columnIndex) {
+ checkValidState();
+ return delegate.get().getValue(columnIndex);
+ }
+
+ @Override
+ public Value getValue(String columnName) {
+ checkValidState();
+ return delegate.get().getValue(columnName);
+ }
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
index 5ec54960e6..245e585ceb 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
@@ -273,6 +273,16 @@ public Date getDate(String columnName) {
return getCurrentRowAsStruct().getDate(columnName);
}
+ @Override
+ public Value getValue(int columnIndex) {
+ return getCurrentRowAsStruct().getValue(columnIndex);
+ }
+
+ @Override
+ public Value getValue(String columnName) {
+ return getCurrentRowAsStruct().getValue(columnName);
+ }
+
@Override
public boolean[] getBooleanArray(int columnIndex) {
return getCurrentRowAsStruct().getBooleanArray(columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
index 7982618948..3a7ddc8d99 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
@@ -207,6 +207,11 @@ protected Date getDateInternal(int columnIndex) {
return values.get(columnIndex).getDate();
}
+ @Override
+ protected Value getValueInternal(int columnIndex) {
+ return values.get(columnIndex);
+ }
+
@Override
protected Struct getStructInternal(int columnIndex) {
return values.get(columnIndex).getStruct();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
index 33cffeed35..7b02f7078b 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
@@ -132,6 +132,16 @@ public interface StructReader {
/** Returns the value of a non-{@code NULL} column with type {@link Type#date()}. */
Date getDate(String columnName);
+ /** Returns the value of a nullable column as a {@link Value}. */
+ default Value getValue(int columnIndex) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
+ /** Returns the value of a nullable column as a {@link Value}. */
+ default Value getValue(String columnName) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
/**
* Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.bool())}.
*
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java
index fa6953b81b..9b336502c2 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java
@@ -543,7 +543,7 @@ private Value() {}
*
* @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
*/
- abstract List getStructArray();
+ public abstract List getStructArray();
@Override
public String toString() {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
index 69782517d9..f23a1fca1b 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
@@ -23,6 +23,7 @@
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.Type;
+import com.google.cloud.spanner.Value;
import com.google.common.base.Preconditions;
import com.google.spanner.v1.ResultSetStats;
import java.math.BigDecimal;
@@ -231,6 +232,18 @@ public Date getDate(String columnName) {
return delegate.getDate(columnName);
}
+ @Override
+ public Value getValue(int columnIndex) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getValue(columnIndex);
+ }
+
+ @Override
+ public Value getValue(String columnName) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getValue(columnName);
+ }
+
@Override
public boolean[] getBooleanArray(int columnIndex) {
Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
index 2bd4f947cc..fac9e40f11 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
@@ -25,6 +25,7 @@
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.Type;
+import com.google.cloud.spanner.Value;
import com.google.common.base.Preconditions;
import com.google.spanner.v1.ResultSetStats;
import java.math.BigDecimal;
@@ -231,6 +232,18 @@ public Date getDate(String columnName) {
return delegate.getDate(columnName);
}
+ @Override
+ public Value getValue(int columnIndex) {
+ checkClosed();
+ return delegate.getValue(columnIndex);
+ }
+
+ @Override
+ public Value getValue(String columnName) {
+ checkClosed();
+ return delegate.getValue(columnName);
+ }
+
@Override
public boolean[] getBooleanArray(int columnIndex) {
checkClosed();
diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
index 49154980d4..5873d466d3 100644
--- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
+++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
@@ -32,6 +32,7 @@
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.Before;
@@ -84,6 +85,11 @@ protected Date getDateInternal(int columnIndex) {
return null;
}
+ @Override
+ protected Value getValueInternal(int columnIndex) {
+ return null;
+ }
+
@Override
protected boolean[] getBooleanArrayInternal(int columnIndex) {
return null;
@@ -162,81 +168,111 @@ public boolean isNull(int columnIndex) {
public static Collection