Skip to content

Commit

Permalink
fix: NPE was thrown when getting an array of structs from a ResultSet (
Browse files Browse the repository at this point in the history
…#445)

A NullPointerException was thrown if a ResultSet contained an array of Structs and
the getArray() method was called on the column.

Fixes #444
  • Loading branch information
olavloite committed Apr 28, 2021
1 parent cd52b2b commit 1dfb37b
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 10 deletions.
27 changes: 27 additions & 0 deletions src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java
Expand Up @@ -17,6 +17,7 @@
package com.google.cloud.spanner.jdbc;

import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.Type;
import com.google.cloud.spanner.Type.Code;
import java.math.BigDecimal;
Expand Down Expand Up @@ -253,6 +254,32 @@ public List<Timestamp> getArrayElements(ResultSet rs, int columnIndex) {
public Type getSpannerType() {
return Type.timestamp();
}
},
STRUCT {
@Override
public int getSqlType() {
return Types.STRUCT;
}

@Override
public Class<Struct> getJavaClass() {
return Struct.class;
}

@Override
public Code getCode() {
return Code.STRUCT;
}

@Override
public List<Struct> getArrayElements(ResultSet rs, int columnIndex) {
return rs.getStructList(columnIndex);
}

@Override
public Type getSpannerType() {
return Type.struct();
}
};

public abstract int getSqlType();
Expand Down
25 changes: 15 additions & 10 deletions src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java
Expand Up @@ -18,15 +18,19 @@

import static com.google.cloud.spanner.jdbc.JdbcTypeConverter.toSqlDate;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;

import com.google.cloud.spanner.ErrorCode;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.jdbc.JdbcSqlExceptionFactory.JdbcSqlExceptionImpl;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Timestamp;
import java.sql.Types;
import org.junit.Test;
Expand Down Expand Up @@ -174,16 +178,17 @@ public void testCreateArrayOfArray() {
}

@Test
public void testCreateArrayOfStruct() {
try {
JdbcArray.createArray("STRUCT", new Object[] {});
fail("missing expected exception");
} catch (SQLException e) {
assertThat((Exception) e).isInstanceOf(JdbcSqlException.class);
JdbcSqlException jse = (JdbcSqlException) e;
assertThat(jse.getErrorCode())
.isEqualTo(ErrorCode.INVALID_ARGUMENT.getGrpcStatusCode().value());
}
public void testCreateArrayOfStruct() throws SQLException {
JdbcArray array =
JdbcArray.createArray(
"STRUCT",
new Struct[] {Struct.newBuilder().set("f1").to("v1").set("f2").to(1L).build(), null});
assertEquals(Types.STRUCT, array.getBaseType());
assertThat((Struct[]) array.getArray())
.asList()
.containsExactly(Struct.newBuilder().set("f1").to("v1").set("f2").to(1L).build(), null)
.inOrder();
assertThrows(SQLFeatureNotSupportedException.class, () -> array.getResultSet());
}

@Test
Expand Down
Expand Up @@ -17,14 +17,22 @@
package com.google.cloud.spanner.jdbc.it;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.google.cloud.spanner.IntegrationTest;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.Value;
import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import org.junit.Test;
import org.junit.experimental.categories.Category;
Expand Down Expand Up @@ -149,4 +157,38 @@ public void testAddBatchWhenAlreadyInBatch() {
"Calling addBatch() is not allowed when a DML or DDL batch has been started on the connection.");
}
}

@Test
public void testSelectArrayOfStructs() throws SQLException {
String sql =
"WITH points AS\n"
+ " (SELECT [1, 5] as point\n"
+ " UNION ALL SELECT [2, 8] as point\n"
+ " UNION ALL SELECT [3, 7] as point\n"
+ " UNION ALL SELECT [4, 1] as point\n"
+ " UNION ALL SELECT [5, 7] as point)\n"
+ "SELECT ARRAY(\n"
+ " SELECT STRUCT(point)\n"
+ " FROM points)\n"
+ " AS coordinates";
try (Connection connection = createConnection()) {
try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
assertTrue(resultSet.next());
assertEquals(resultSet.getMetaData().getColumnCount(), 1);
Array array = resultSet.getArray(1);
assertThat((Struct[]) array.getArray())
.asList()
.containsExactly(
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {1L, 5L})).build(),
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {2L, 8L})).build(),
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {3L, 7L})).build(),
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {4L, 1L})).build(),
Struct.newBuilder().set("point").to(Value.int64Array(new long[] {5L, 7L})).build());
// Getting a result set from an array of structs is not supported, as structs are not
// supported as a valid column type in a result set.
assertThrows(SQLFeatureNotSupportedException.class, () -> array.getResultSet());
assertFalse(resultSet.next());
}
}
}
}

0 comments on commit 1dfb37b

Please sign in to comment.