From 987af6de06e96258f1898c1f0b9810da6a230225 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 11 Jun 2025 13:52:39 +0530 Subject: [PATCH 1/4] feat: Support getOrNull and getOrDefault in Struct --- .../google/cloud/spanner/StructReader.java | 32 ++++++++++++++ .../com/google/cloud/spanner/StructTest.java | 42 +++++++++++++++++++ 2 files changed, 74 insertions(+) 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 f690011f389..a2b2f415a4c 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 @@ -176,6 +176,38 @@ default float getFloat(String columnName) { */ String getString(String columnName); + /** + * @param columnIndex index of the column + * @return the value of a column with type T. return value(T) can be null. + */ + default T getOrNull(int columnIndex, Function function) { + return isNull(columnIndex) ? null : function.apply(columnIndex); + } + + /** + * @param columnName index of the column + * @return the value of a column with type T. return value(T) can be null. + */ + default T getOrNull(String columnName, Function function) { + return isNull(columnName) ? null : function.apply(columnName); + } + + /** + * @param columnIndex index of the column + * @return the value of a column with type T. if column value is null, returns default value. + */ + default T getOrDefault(int columnIndex, Function function, T defaultValue) { + return isNull(columnIndex) ? defaultValue : function.apply(columnIndex); + } + + /** + * @param columnName name of the column + * @return the value of a column with type T. if column value is null, returns default value. + */ + default T getOrDefault(String columnName, Function function, T defaultValue) { + return isNull(columnName) ? defaultValue : function.apply(columnName); + } + /** * @param columnIndex index of the column * @return the value of a non-{@code NULL} column with type {@link Type#json()}. diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java index d357a14f9d0..a2383844043 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java @@ -57,6 +57,48 @@ public void builder() { assertThat(struct.getLong(1)).isEqualTo(2); } + @Test + public void getOrNullTests() { + Struct struct = + Struct.newBuilder() + .set("f1") + .to("x") + .set("f2") + .to(2) + .set("f3") + .to(Value.bool(null)) + .build(); + String column1 = struct.getOrNull(0, struct::getString); + assertThat(column1).isEqualTo("x"); + + Long column2 = struct.getOrNull(1, struct::getLong); + assertThat(column2).isEqualTo(2); + + String column3 = struct.getOrNull("f3", struct::getString); + assertThat(column3).isNull(); + } + + @Test + public void getOrDefaultTests() { + Struct struct = + Struct.newBuilder() + .set("f1") + .to("x") + .set("f2") + .to(2) + .set("f3") + .to(Value.bool(null)) + .build(); + String column1 = struct.getOrDefault(0, struct::getString, ""); + assertThat(column1).isEqualTo("x"); + + Long column2 = struct.getOrDefault("f2", struct::getLong, -1L); + assertThat(column2).isEqualTo(2); + + String column3 = struct.getOrDefault(2, struct::getString, ""); + assertThat(column3).isEqualTo(""); + } + @Test public void duplicateFields() { // Duplicate fields are allowed - some SQL queries produce this type of value. From 7f5b7515b076f745a2a9d69e17399d241e117566 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 11 Jun 2025 14:10:02 +0530 Subject: [PATCH 2/4] Addressed comments --- .../clirr-ignored-differences.xml | 20 +++++++++++ .../google/cloud/spanner/StructReader.java | 33 ++++++++++++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 94bb5870f81..d47275c7906 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -1008,4 +1008,24 @@ com/google/cloud/spanner/TransactionManager com.google.cloud.spanner.TransactionContext begin(com.google.cloud.spanner.AbortedException) + + 7012 + com/google/cloud/spanner/StructReader + java.lang.Object getOrNull(int, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + java.lang.Object getOrNull(java.lang.String, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + java.lang.Object getOrDefault(int, java.util.function.Function, java.lang.Object) + + + 7012 + com/google/cloud/spanner/StructReader + java.lang.Object getOrDefault(java.lang.String, java.util.function.Function, java.lang.Object) + 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 a2b2f415a4c..22af621af92 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 @@ -178,7 +178,13 @@ default float getFloat(String columnName) { /** * @param columnIndex index of the column - * @return the value of a column with type T. return value(T) can be null. + * @return the value of a column with type T or null if the column contains a null value + *

Example + * + *

{@code
+   * Struct row = ...
+   * String name = row.getOrNull(1, row::getString)
+   * }
*/ default T getOrNull(int columnIndex, Function function) { return isNull(columnIndex) ? null : function.apply(columnIndex); @@ -186,7 +192,13 @@ default T getOrNull(int columnIndex, Function function) { /** * @param columnName index of the column - * @return the value of a column with type T. return value(T) can be null. + * @return the value of a column with type T or null if the column contains a null value + *

Example + * + *

{@code
+   * Struct row = ...
+   * String name = row.getOrNull("name", row::getString)
+   * }
*/ default T getOrNull(String columnName, Function function) { return isNull(columnName) ? null : function.apply(columnName); @@ -194,7 +206,13 @@ default T getOrNull(String columnName, Function function) { /** * @param columnIndex index of the column - * @return the value of a column with type T. if column value is null, returns default value. + * @return the value of a column with type T, or the given default if the column value is null + *

Example + * + *

{@code
+   * Struct row = ...
+   * String name = row.getOrDefault(1, row::getString, "")
+   * }
*/ default T getOrDefault(int columnIndex, Function function, T defaultValue) { return isNull(columnIndex) ? defaultValue : function.apply(columnIndex); @@ -202,7 +220,14 @@ default T getOrDefault(int columnIndex, Function function, T def /** * @param columnName name of the column - * @return the value of a column with type T. if column value is null, returns default value. + * @return the value of a column with type T, or the given default if the column value is null + * + *

Example + * + *

{@code
+   * Struct row = ...
+   * String name = row.getOrDefault("name", row::getString, "")
+   * }
*/ default T getOrDefault(String columnName, Function function, T defaultValue) { return isNull(columnName) ? defaultValue : function.apply(columnName); From f9b57ddc45157bc81b002d7f75c882124cd259b2 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 11 Jun 2025 15:17:12 +0530 Subject: [PATCH 3/4] Change Function interface to BiFunction --- .../google/cloud/spanner/StructReader.java | 48 +++++++++---------- .../com/google/cloud/spanner/StructTest.java | 12 ++--- 2 files changed, 29 insertions(+), 31 deletions(-) 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 22af621af92..ab645588bf1 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 @@ -24,6 +24,7 @@ import java.math.BigDecimal; import java.util.List; import java.util.UUID; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -179,58 +180,55 @@ default float getFloat(String columnName) { /** * @param columnIndex index of the column * @return the value of a column with type T or null if the column contains a null value - *

Example - * - *

{@code
+   *     

Example + *

{@code
    * Struct row = ...
-   * String name = row.getOrNull(1, row::getString)
+   * String name = row.getOrNull(1, StructReader::getString)
    * }
*/ - default T getOrNull(int columnIndex, Function function) { - return isNull(columnIndex) ? null : function.apply(columnIndex); + default T getOrNull(int columnIndex, BiFunction function) { + return isNull(columnIndex) ? null : function.apply(this, columnIndex); } /** * @param columnName index of the column * @return the value of a column with type T or null if the column contains a null value - *

Example - * - *

{@code
+   *     

Example + *

{@code
    * Struct row = ...
-   * String name = row.getOrNull("name", row::getString)
+   * String name = row.getOrNull("name", StructReader::getString)
    * }
*/ - default T getOrNull(String columnName, Function function) { - return isNull(columnName) ? null : function.apply(columnName); + default T getOrNull(String columnName, BiFunction function) { + return isNull(columnName) ? null : function.apply(this, columnName); } /** * @param columnIndex index of the column * @return the value of a column with type T, or the given default if the column value is null - *

Example - * - *

{@code
+   *     

Example + *

{@code
    * Struct row = ...
-   * String name = row.getOrDefault(1, row::getString, "")
+   * String name = row.getOrDefault(1, StructReader::getString, "")
    * }
*/ - default T getOrDefault(int columnIndex, Function function, T defaultValue) { - return isNull(columnIndex) ? defaultValue : function.apply(columnIndex); + default T getOrDefault( + int columnIndex, BiFunction function, T defaultValue) { + return isNull(columnIndex) ? defaultValue : function.apply(this, columnIndex); } /** * @param columnName name of the column * @return the value of a column with type T, or the given default if the column value is null - * - *

Example - * - *

{@code
+   *     

Example + *

{@code
    * Struct row = ...
-   * String name = row.getOrDefault("name", row::getString, "")
+   * String name = row.getOrDefault("name", StructReader::getString, "")
    * }
*/ - default T getOrDefault(String columnName, Function function, T defaultValue) { - return isNull(columnName) ? defaultValue : function.apply(columnName); + default T getOrDefault( + String columnName, BiFunction function, T defaultValue) { + return isNull(columnName) ? defaultValue : function.apply(this, columnName); } /** diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java index a2383844043..55d066e165e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/StructTest.java @@ -68,13 +68,13 @@ public void getOrNullTests() { .set("f3") .to(Value.bool(null)) .build(); - String column1 = struct.getOrNull(0, struct::getString); + String column1 = struct.getOrNull(0, StructReader::getString); assertThat(column1).isEqualTo("x"); - Long column2 = struct.getOrNull(1, struct::getLong); + Long column2 = struct.getOrNull(1, StructReader::getLong); assertThat(column2).isEqualTo(2); - String column3 = struct.getOrNull("f3", struct::getString); + String column3 = struct.getOrNull("f3", StructReader::getString); assertThat(column3).isNull(); } @@ -89,13 +89,13 @@ public void getOrDefaultTests() { .set("f3") .to(Value.bool(null)) .build(); - String column1 = struct.getOrDefault(0, struct::getString, ""); + String column1 = struct.getOrDefault(0, StructReader::getString, ""); assertThat(column1).isEqualTo("x"); - Long column2 = struct.getOrDefault("f2", struct::getLong, -1L); + Long column2 = struct.getOrDefault("f2", StructReader::getLong, -1L); assertThat(column2).isEqualTo(2); - String column3 = struct.getOrDefault(2, struct::getString, ""); + String column3 = struct.getOrDefault(2, StructReader::getString, ""); assertThat(column3).isEqualTo(""); } From 4803d8d04d48e1e9c9071fc30bf2c82268739d32 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 11 Jun 2025 18:18:38 +0530 Subject: [PATCH 4/4] Update clirr ignored differences --- google-cloud-spanner/clirr-ignored-differences.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index d47275c7906..46a11518dbb 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -1011,21 +1011,21 @@ 7012 com/google/cloud/spanner/StructReader - java.lang.Object getOrNull(int, java.util.function.Function) + java.lang.Object getOrNull(int, java.util.function.BiFunction) 7012 com/google/cloud/spanner/StructReader - java.lang.Object getOrNull(java.lang.String, java.util.function.Function) + java.lang.Object getOrNull(java.lang.String, java.util.function.BiFunction) 7012 com/google/cloud/spanner/StructReader - java.lang.Object getOrDefault(int, java.util.function.Function, java.lang.Object) + java.lang.Object getOrDefault(int, java.util.function.BiFunction, java.lang.Object) 7012 com/google/cloud/spanner/StructReader - java.lang.Object getOrDefault(java.lang.String, java.util.function.Function, java.lang.Object) + java.lang.Object getOrDefault(java.lang.String, java.util.function.BiFunction, java.lang.Object)