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 9b336502c2..34fc43e751 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 @@ -1550,15 +1550,15 @@ private Value getValue(int fieldIndex) { Type elementType = fieldType.getArrayElementType(); switch (elementType.getCode()) { case BOOL: - return Value.boolArray(value.getBooleanArray(fieldIndex)); + return Value.boolArray(value.getBooleanList(fieldIndex)); case INT64: - return Value.int64Array(value.getLongArray(fieldIndex)); + return Value.int64Array(value.getLongList(fieldIndex)); case STRING: return Value.stringArray(value.getStringList(fieldIndex)); case BYTES: return Value.bytesArray(value.getBytesList(fieldIndex)); case FLOAT64: - return Value.float64Array(value.getDoubleArray(fieldIndex)); + return Value.float64Array(value.getDoubleList(fieldIndex)); case NUMERIC: return Value.numericArray(value.getBigDecimalList(fieldIndex)); case DATE: diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index 503fd124f7..8692e92e82 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -19,6 +19,7 @@ import static com.google.common.testing.SerializableTester.reserializeAndAssert; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import com.google.cloud.ByteArray; @@ -29,6 +30,8 @@ import com.google.common.collect.ForwardingList; import com.google.common.collect.Lists; import com.google.common.testing.EqualsTester; +import com.google.protobuf.ListValue; +import com.google.protobuf.NullValue; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; @@ -1024,6 +1027,380 @@ public void structArrayInvalidType() { } } + @Test + public void testValueToProto() { + // BASE types. + assertEquals( + com.google.protobuf.Value.newBuilder().setBoolValue(true).build(), + Value.bool(true).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setBoolValue(false).build(), + Value.bool(false).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.bool(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder().setStringValue("1").build(), + Value.int64(1L).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.int64(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build(), + Value.float64(3.14d).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.float64(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder().setStringValue("test").build(), + Value.string("test").toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.string(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder() + .setStringValue(ByteArray.copyFrom("test").toBase64()) + .build(), + Value.bytes(ByteArray.copyFrom("test")).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.bytes(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder().setStringValue("3.14").build(), + Value.numeric(new BigDecimal("3.14")).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.numeric(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder().setStringValue("2010-02-28").build(), + Value.date(Date.fromYearMonthDay(2010, 2, 28)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.date(null).toProto()); + + assertEquals( + com.google.protobuf.Value.newBuilder() + .setStringValue("2012-04-10T15:16:17.123456789Z") + .build(), + Value.timestamp(Timestamp.parseTimestamp("2012-04-10T15:16:17.123456789Z")).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.timestamp(null).toProto()); + + // ARRAY types. + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setBoolValue(true).build(), + com.google.protobuf.Value.newBuilder().setBoolValue(false).build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.boolArray(Arrays.asList(true, false, null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setStringValue("1").build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.int64Array(Arrays.asList(1L, null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.float64Array(Arrays.asList(3.14d, null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setStringValue("test").build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.stringArray(Arrays.asList("test", null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue(ByteArray.copyFrom("test").toBase64()) + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.bytesArray(Arrays.asList(ByteArray.copyFrom("test"), null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setStringValue("3.14").build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.numericArray(Arrays.asList(new BigDecimal("3.14"), null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("2010-02-28") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.dateArray(Arrays.asList(Date.fromYearMonthDay(2010, 2, 28), null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("2012-04-10T15:16:17.123456789Z") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.timestampArray( + Arrays.asList(Timestamp.parseTimestamp("2012-04-10T15:16:17.123456789Z"), null)) + .toProto()); + + // STRUCT type with array field. + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setBoolValue(true) + .build(), + com.google.protobuf.Value.newBuilder() + .setBoolValue(false) + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder().add(Value.boolArray(Arrays.asList(true, false, null))).build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("1") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct(Struct.newBuilder().add(Value.int64Array(Arrays.asList(1L, null))).build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setNumberValue(3.14d) + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder().add(Value.float64Array(Arrays.asList(3.14d, null))).build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("test") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder().add(Value.stringArray(Arrays.asList("test", null))).build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue( + ByteArray.copyFrom("test").toBase64()) + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder() + .add(Value.bytesArray(Arrays.asList(ByteArray.copyFrom("test"), null))) + .build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("3.14") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder() + .add(Value.numericArray(Arrays.asList(new BigDecimal("3.14"), null))) + .build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("2010-02-28") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder() + .add(Value.dateArray(Arrays.asList(Date.fromYearMonthDay(2010, 2, 28), null))) + .build()) + .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setStringValue("2012-04-10T15:16:17.123456789Z") + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder() + .add( + Value.timestampArray( + Arrays.asList( + Timestamp.parseTimestamp("2012-04-10T15:16:17.123456789Z"), null))) + .build()) + .toProto()); + } + @Test public void testEqualsHashCode() { EqualsTester tester = new EqualsTester(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java index b66128a9ea..d45f833397 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java @@ -19,6 +19,9 @@ import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -781,6 +784,63 @@ public void bindStructWithNullStructField() { assertThat(row.getBoolean(0)).isTrue(); } + @Test + public void bindStructWithBoolArrayFieldThatContainsNulls() { + Struct p = + Struct.newBuilder() + .set("boolArray") + .to(Value.boolArray(Arrays.asList(true, false, null))) + .build(); + List rows = + resultRows( + Statement.newBuilder("SELECT * FROM UNNEST(@p.boolArray) ORDER BY 1") + .bind("p") + .to(p) + .build(), + Type.struct(StructField.of("", Type.bool()))); + assertTrue(rows.get(0).isNull(0)); + assertFalse(rows.get(1).getBoolean(0)); + assertTrue(rows.get(2).getBoolean(0)); + } + + @Test + public void bindStructWithInt64ArrayFieldThatContainsNulls() { + Struct p = + Struct.newBuilder() + .set("int64Array") + .to(Value.int64Array(Arrays.asList(1L, 100L, null))) + .build(); + List rows = + resultRows( + Statement.newBuilder("SELECT * FROM UNNEST(@p.int64Array) ORDER BY 1") + .bind("p") + .to(p) + .build(), + Type.struct(StructField.of("", Type.int64()))); + assertTrue(rows.get(0).isNull(0)); + assertEquals(1L, rows.get(1).getLong(0)); + assertEquals(100L, rows.get(2).getLong(0)); + } + + @Test + public void bindStructWithFloat64ArrayFieldThatContainsNulls() { + Struct p = + Struct.newBuilder() + .set("float64Array") + .to(Value.float64Array(Arrays.asList(1d, 3.14d, null))) + .build(); + List rows = + resultRows( + Statement.newBuilder("SELECT * FROM UNNEST(@p.float64Array) ORDER BY 1") + .bind("p") + .to(p) + .build(), + Type.struct(StructField.of("", Type.float64()))); + assertTrue(rows.get(0).isNull(0)); + assertEquals(1d, rows.get(1).getDouble(0), 0d); + assertEquals(3.14d, rows.get(2).getDouble(0), 0d); + } + @Test public void bindStructWithStructField() { Struct nestedStruct = Struct.newBuilder().set("ff1").to("abc").build();