Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 1dfb37b

Browse files
authored
fix: NPE was thrown when getting an array of structs from a ResultSet (#445)
A NullPointerException was thrown if a ResultSet contained an array of Structs and the getArray() method was called on the column. Fixes #444
1 parent cd52b2b commit 1dfb37b

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.cloud.spanner.jdbc;
1818

1919
import com.google.cloud.spanner.ResultSet;
20+
import com.google.cloud.spanner.Struct;
2021
import com.google.cloud.spanner.Type;
2122
import com.google.cloud.spanner.Type.Code;
2223
import java.math.BigDecimal;
@@ -253,6 +254,32 @@ public List<Timestamp> getArrayElements(ResultSet rs, int columnIndex) {
253254
public Type getSpannerType() {
254255
return Type.timestamp();
255256
}
257+
},
258+
STRUCT {
259+
@Override
260+
public int getSqlType() {
261+
return Types.STRUCT;
262+
}
263+
264+
@Override
265+
public Class<Struct> getJavaClass() {
266+
return Struct.class;
267+
}
268+
269+
@Override
270+
public Code getCode() {
271+
return Code.STRUCT;
272+
}
273+
274+
@Override
275+
public List<Struct> getArrayElements(ResultSet rs, int columnIndex) {
276+
return rs.getStructList(columnIndex);
277+
}
278+
279+
@Override
280+
public Type getSpannerType() {
281+
return Type.struct();
282+
}
256283
};
257284

258285
public abstract int getSqlType();

src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818

1919
import static com.google.cloud.spanner.jdbc.JdbcTypeConverter.toSqlDate;
2020
import static com.google.common.truth.Truth.assertThat;
21+
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertThrows;
2123
import static org.junit.Assert.fail;
2224

2325
import com.google.cloud.spanner.ErrorCode;
26+
import com.google.cloud.spanner.Struct;
2427
import com.google.cloud.spanner.jdbc.JdbcSqlExceptionFactory.JdbcSqlExceptionImpl;
2528
import java.math.BigDecimal;
2629
import java.sql.Date;
2730
import java.sql.ResultSet;
2831
import java.sql.ResultSetMetaData;
2932
import java.sql.SQLException;
33+
import java.sql.SQLFeatureNotSupportedException;
3034
import java.sql.Timestamp;
3135
import java.sql.Types;
3236
import org.junit.Test;
@@ -174,16 +178,17 @@ public void testCreateArrayOfArray() {
174178
}
175179

176180
@Test
177-
public void testCreateArrayOfStruct() {
178-
try {
179-
JdbcArray.createArray("STRUCT", new Object[] {});
180-
fail("missing expected exception");
181-
} catch (SQLException e) {
182-
assertThat((Exception) e).isInstanceOf(JdbcSqlException.class);
183-
JdbcSqlException jse = (JdbcSqlException) e;
184-
assertThat(jse.getErrorCode())
185-
.isEqualTo(ErrorCode.INVALID_ARGUMENT.getGrpcStatusCode().value());
186-
}
181+
public void testCreateArrayOfStruct() throws SQLException {
182+
JdbcArray array =
183+
JdbcArray.createArray(
184+
"STRUCT",
185+
new Struct[] {Struct.newBuilder().set("f1").to("v1").set("f2").to(1L).build(), null});
186+
assertEquals(Types.STRUCT, array.getBaseType());
187+
assertThat((Struct[]) array.getArray())
188+
.asList()
189+
.containsExactly(Struct.newBuilder().set("f1").to("v1").set("f2").to(1L).build(), null)
190+
.inOrder();
191+
assertThrows(SQLFeatureNotSupportedException.class, () -> array.getResultSet());
187192
}
188193

189194
@Test

src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcSimpleStatementsTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,22 @@
1717
package com.google.cloud.spanner.jdbc.it;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertFalse;
22+
import static org.junit.Assert.assertThrows;
23+
import static org.junit.Assert.assertTrue;
2024
import static org.junit.Assert.fail;
2125

2226
import com.google.cloud.spanner.IntegrationTest;
27+
import com.google.cloud.spanner.Struct;
28+
import com.google.cloud.spanner.Value;
2329
import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest;
30+
import java.sql.Array;
2431
import java.sql.Connection;
2532
import java.sql.PreparedStatement;
2633
import java.sql.ResultSet;
2734
import java.sql.SQLException;
35+
import java.sql.SQLFeatureNotSupportedException;
2836
import java.sql.Statement;
2937
import org.junit.Test;
3038
import org.junit.experimental.categories.Category;
@@ -149,4 +157,38 @@ public void testAddBatchWhenAlreadyInBatch() {
149157
"Calling addBatch() is not allowed when a DML or DDL batch has been started on the connection.");
150158
}
151159
}
160+
161+
@Test
162+
public void testSelectArrayOfStructs() throws SQLException {
163+
String sql =
164+
"WITH points AS\n"
165+
+ " (SELECT [1, 5] as point\n"
166+
+ " UNION ALL SELECT [2, 8] as point\n"
167+
+ " UNION ALL SELECT [3, 7] as point\n"
168+
+ " UNION ALL SELECT [4, 1] as point\n"
169+
+ " UNION ALL SELECT [5, 7] as point)\n"
170+
+ "SELECT ARRAY(\n"
171+
+ " SELECT STRUCT(point)\n"
172+
+ " FROM points)\n"
173+
+ " AS coordinates";
174+
try (Connection connection = createConnection()) {
175+
try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
176+
assertTrue(resultSet.next());
177+
assertEquals(resultSet.getMetaData().getColumnCount(), 1);
178+
Array array = resultSet.getArray(1);
179+
assertThat((Struct[]) array.getArray())
180+
.asList()
181+
.containsExactly(
182+
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {1L, 5L})).build(),
183+
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {2L, 8L})).build(),
184+
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {3L, 7L})).build(),
185+
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {4L, 1L})).build(),
186+
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {5L, 7L})).build());
187+
// Getting a result set from an array of structs is not supported, as structs are not
188+
// supported as a valid column type in a result set.
189+
assertThrows(SQLFeatureNotSupportedException.class, () -> array.getResultSet());
190+
assertFalse(resultSet.next());
191+
}
192+
}
193+
}
152194
}

0 commit comments

Comments
 (0)