Skip to content

Commit 46d6a86

Browse files
authored
fix(bigquery-jdbc): implement JDBC wrapper interface methods (#13322)
b/473601569 # Description Standardize and relocate JDBC `Wrapper` interface methods (`unwrap` and `isWrapperFor`) in the BigQuery JDBC driver. ### Key Changes - **Removed placeholder stubs from NoOps classes:** - Removed `unwrap` and `isWrapperFor` from `BigQueryNoOpsConnection`, `BigQueryNoOpsStatement`, and `BigQueryNoOpsResultSet`. - **Implemented spec-compliant wrapper logic:** - Added `unwrap` and `isWrapperFor` to `BigQueryConnection`, `BigQueryStatement`, and `BigQueryBaseResultSet` (enabling support across all concrete statements/result sets via inheritance). - Ensured `DataSource` also supports compliant `unwrap` and `isWrapperFor` checks. - **Unit Testing:** - Added wrapper unit tests in `BigQueryConnectionTest`, `BigQueryStatementTest`, `BigQueryBaseResultSetTest`, and created a new `DataSourceTest`.
1 parent 842f64e commit 46d6a86

15 files changed

Lines changed: 173 additions & 59 deletions

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.cloud.bigquery.exception.BigQueryConversionException;
2828
import com.google.cloud.bigquery.exception.BigQueryJdbcCoercionException;
2929
import com.google.cloud.bigquery.exception.BigQueryJdbcCoercionNotFoundException;
30+
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
3031
import java.io.InputStream;
3132
import java.io.Reader;
3233
import java.io.StringReader;
@@ -674,4 +675,17 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException {
674675
public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
675676
return getTimestamp(getColumnIndex(columnLabel), cal);
676677
}
678+
679+
@Override
680+
public <T> T unwrap(Class<T> iface) throws SQLException {
681+
if (iface.isInstance(this)) {
682+
return iface.cast(this);
683+
}
684+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
685+
}
686+
687+
@Override
688+
public boolean isWrapperFor(Class<?> iface) throws SQLException {
689+
return iface != null && iface.isInstance(this);
690+
}
677691
}

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,4 +1220,17 @@ private boolean checkIsReadOnlyTokenUsed(Map<String, String> authProps) {
12201220
}
12211221
return false;
12221222
}
1223+
1224+
@Override
1225+
public <T> T unwrap(Class<T> iface) throws SQLException {
1226+
if (iface.isInstance(this)) {
1227+
return iface.cast(this);
1228+
}
1229+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
1230+
}
1231+
1232+
@Override
1233+
public boolean isWrapperFor(Class<?> iface) throws SQLException {
1234+
return iface != null && iface.isInstance(this);
1235+
}
12231236
}

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4756,7 +4756,7 @@ public <T> T unwrap(Class<T> iface) throws SQLException {
47564756
if (iface.isInstance(this)) {
47574757
return iface.cast(this);
47584758
}
4759-
throw new SQLException("Cannot unwrap to " + iface.getName());
4759+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
47604760
}
47614761

47624762
@Override

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsConnection.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,6 @@ public String nativeSQL(String sql) throws SQLException {
4747
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
4848
}
4949

50-
@Override
51-
public <T> T unwrap(Class<T> iface) throws SQLException {
52-
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
53-
}
54-
55-
@Override
56-
public boolean isWrapperFor(Class<?> iface) {
57-
return false;
58-
}
59-
6050
@Override
6151
public boolean isReadOnly() {
6252
return false;
@@ -68,8 +58,6 @@ public void setReadOnly(boolean readOnly) {}
6858
@Override
6959
public void setCatalog(String catalog) {}
7060

71-
// TODO: post MVP feature
72-
7361
@Override
7462
public Map<String, Class<?>> getTypeMap() throws SQLException {
7563
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsResultSet.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -655,16 +655,6 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
655655
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
656656
}
657657

658-
@Override
659-
public <T> T unwrap(Class<T> iface) throws SQLException {
660-
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
661-
}
662-
663-
@Override
664-
public boolean isWrapperFor(Class<?> iface) throws SQLException {
665-
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
666-
}
667-
668658
@Override
669659
public SQLWarning getWarnings() throws SQLException {
670660
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsStatement.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,11 @@ abstract class BigQueryNoOpsStatement implements Statement {
2727

2828
@Override
2929
public void setCursorName(String name) throws SQLException {
30-
// TODO: ResultSet Concurrency is read only(Not updatable)
31-
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
32-
}
33-
34-
@Override
35-
public <T> T unwrap(Class<T> iface) throws SQLException {
36-
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
37-
}
38-
39-
@Override
40-
public boolean isWrapperFor(Class<?> iface) throws SQLException {
4130
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
4231
}
4332

4433
@Override
4534
public ResultSet getGeneratedKeys() throws SQLException {
46-
// TODO: Returns an empty resultset.
47-
// return empty ResultSet
4835
throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED);
4936
}
5037

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.cloud.bigquery.Field.Mode;
2121
import com.google.cloud.bigquery.FieldList;
2222
import com.google.cloud.bigquery.StandardSQLTypeName;
23+
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
2324
import java.sql.ResultSetMetaData;
2425
import java.sql.SQLException;
2526
import java.sql.Statement;
@@ -209,7 +210,7 @@ public <T> T unwrap(Class<T> iface) throws SQLException {
209210
if (iface.isInstance(this)) {
210211
return iface.cast(this);
211212
}
212-
throw new SQLException("Cannot unwrap to " + iface.getName());
213+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
213214
}
214215

215216
@Override

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryStatement.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,20 +1573,6 @@ private boolean getMoreResultsImpl(int current) throws SQLException {
15731573
}
15741574
}
15751575

1576-
@Override
1577-
public boolean isWrapperFor(Class<?> iface) {
1578-
return iface.isInstance(this);
1579-
}
1580-
1581-
@Override
1582-
public <T> T unwrap(Class<T> iface) throws SQLException {
1583-
if (!isWrapperFor(iface)) {
1584-
throw new BigQueryJdbcException(
1585-
String.format("Unable to cast Statement to %s class.", iface.getName()));
1586-
}
1587-
return (T) this;
1588-
}
1589-
15901576
@Override
15911577
public int getResultSetHoldability() {
15921578
return ResultSet.CLOSE_CURSORS_AT_COMMIT;
@@ -1617,6 +1603,19 @@ public boolean isCloseOnCompletion() {
16171603
return this.closeOnCompletion;
16181604
}
16191605

1606+
@Override
1607+
public <T> T unwrap(Class<T> iface) throws SQLException {
1608+
if (iface.isInstance(this)) {
1609+
return iface.cast(this);
1610+
}
1611+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
1612+
}
1613+
1614+
@Override
1615+
public boolean isWrapperFor(Class<?> iface) throws SQLException {
1616+
return iface != null && iface.isInstance(this);
1617+
}
1618+
16201619
protected void logQueryExecutionStart(String sql) {
16211620
if (sql == null) {
16221621
return;

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,13 +1368,16 @@ public Logger getParentLogger() {
13681368
}
13691369

13701370
@Override
1371-
public <T> T unwrap(Class<T> iface) {
1372-
return null;
1371+
public <T> T unwrap(Class<T> iface) throws SQLException {
1372+
if (iface.isInstance(this)) {
1373+
return iface.cast(this);
1374+
}
1375+
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
13731376
}
13741377

13751378
@Override
1376-
public boolean isWrapperFor(Class<?> iface) {
1377-
return false;
1379+
public boolean isWrapperFor(Class<?> iface) throws SQLException {
1380+
return iface != null && iface.isInstance(this);
13781381
}
13791382

13801383
private static void validateNonNegative(long val, String propertyName) {

java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSetTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import static com.google.common.truth.Truth.assertThat;
2020
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertSame;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
import static org.junit.jupiter.api.Assertions.assertTrue;
2124
import static org.mockito.ArgumentMatchers.any;
2225
import static org.mockito.Mockito.CALLS_REAL_METHODS;
2326
import static org.mockito.Mockito.doReturn;
@@ -27,7 +30,9 @@
2730
import com.google.cloud.bigquery.Job;
2831
import com.google.cloud.bigquery.JobId;
2932
import com.google.cloud.bigquery.JobStatistics.QueryStatistics;
33+
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
3034
import java.lang.reflect.Field;
35+
import java.sql.SQLException;
3136
import org.junit.jupiter.api.BeforeEach;
3237
import org.junit.jupiter.api.Test;
3338

@@ -101,4 +106,22 @@ public void testGetQueryStatistics_no_job() {
101106
doReturn(job).when(bigQuery).getJob(any(JobId.class));
102107
assertThat(resultSet.getQueryStatistics()).isNull();
103108
}
109+
110+
@Test
111+
public void testWrapperMethods() throws SQLException {
112+
assertTrue(resultSet.isWrapperFor(java.sql.ResultSet.class));
113+
assertTrue(resultSet.isWrapperFor(BigQueryBaseResultSet.class));
114+
assertFalse(resultSet.isWrapperFor(java.sql.Statement.class));
115+
assertFalse(resultSet.isWrapperFor(null));
116+
117+
Object unwrappedRs = resultSet.unwrap(java.sql.ResultSet.class);
118+
assertSame(unwrappedRs, resultSet);
119+
120+
Object unwrappedImpl = resultSet.unwrap(BigQueryBaseResultSet.class);
121+
assertSame(unwrappedImpl, resultSet);
122+
123+
BigQueryJdbcException e =
124+
assertThrows(BigQueryJdbcException.class, () -> resultSet.unwrap(java.sql.Statement.class));
125+
assertTrue(e.getMessage().contains("Cannot unwrap to java.sql.Statement"));
126+
}
104127
}

0 commit comments

Comments
 (0)