diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java index ab86cfdc9d17..34dc1f903f75 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java @@ -27,7 +27,7 @@ import java.sql.Types; /** This class returns ResultSetMetadata for the JSON and the Arrow ResultSets */ -class BigQueryResultSetMetadata implements ResultSetMetaData { +public class BigQueryResultSetMetadata implements ResultSetMetaData { private static final BigQueryJdbcResultSetLogger LOG = BigQueryJdbcResultSetLogger.getLogger(BigQueryResultSetMetadata.class); private final FieldList schemaFieldList; @@ -53,11 +53,21 @@ BigQueryStatement getStatement() { return this.statement; } - private Field getField(int sqlColumn) { + /** + * Returns the BigQuery {@link Field} schema definition for the given 1-based SQL column index. + * + * @param sqlColumn the first column is 1, the second is 2, ... + * @return the {@link Field} for the specified column + * @throws SQLException if the column index is out of bounds + */ + public Field getField(int sqlColumn) throws SQLException { + if (sqlColumn < 1 || sqlColumn > this.columnCount) { + throw new BigQueryJdbcException("Column index out of bounds: " + sqlColumn); + } return this.schemaFieldList.get(sqlColumn - 1); } - private boolean isPicosecondTimestampColumn(int sqlColumn) { + private boolean isPicosecondTimestampColumn(int sqlColumn) throws SQLException { if (!this.enableTimestampPicos) { return false; } @@ -77,7 +87,7 @@ public boolean isAutoIncrement(int column) { } @Override - public boolean isCaseSensitive(int column) { + public boolean isCaseSensitive(int column) throws SQLException { int colType = getColumnType(column); return colType == Types.NVARCHAR; } @@ -93,7 +103,7 @@ public boolean isCurrency(int column) { } @Override - public int isNullable(int column) { + public int isNullable(int column) throws SQLException { Mode colMode = getField(column).getMode(); if (colMode == null) { return ResultSetMetaData.columnNullableUnknown; @@ -104,7 +114,7 @@ public int isNullable(int column) { } @Override - public boolean isSigned(int column) { + public boolean isSigned(int column) throws SQLException { int colType = getColumnType(column); return colType == Types.FLOAT || colType == Types.DOUBLE @@ -113,7 +123,7 @@ public boolean isSigned(int column) { } @Override - public int getColumnDisplaySize(int column) { + public int getColumnDisplaySize(int column) throws SQLException { if (isPicosecondTimestampColumn(column)) { return 32; } @@ -136,17 +146,17 @@ public int getColumnDisplaySize(int column) { } @Override - public String getColumnLabel(int column) { + public String getColumnLabel(int column) throws SQLException { return getField(column).getName(); } @Override - public String getColumnName(int column) { + public String getColumnName(int column) throws SQLException { return getField(column).getName(); } @Override - public int getPrecision(int column) { + public int getPrecision(int column) throws SQLException { Long precision = getField(column).getPrecision(); if (precision != null) { return precision.intValue(); @@ -163,7 +173,7 @@ public int getPrecision(int column) { } @Override - public int getScale(int column) { + public int getScale(int column) throws SQLException { Long scale = getField(column).getScale(); if (scale != null) { return scale.intValue(); @@ -196,7 +206,7 @@ public String getSchemaName(int column) { return ""; // not applicable } - private StandardSQLTypeName getStandardSQLTypeName(int column) { + private StandardSQLTypeName getStandardSQLTypeName(int column) throws SQLException { Field field = getField(column); if (field.getMode() == Mode.REPEATED) { return StandardSQLTypeName.ARRAY; @@ -205,7 +215,7 @@ private StandardSQLTypeName getStandardSQLTypeName(int column) { } @Override - public int getColumnType(int column) { + public int getColumnType(int column) throws SQLException { if (isPicosecondTimestampColumn(column)) { return Types.VARCHAR; } @@ -213,7 +223,7 @@ public int getColumnType(int column) { } @Override - public String getColumnTypeName(int column) { + public String getColumnTypeName(int column) throws SQLException { if (isPicosecondTimestampColumn(column)) { return BigQueryTemporalUtility.TIMESTAMP_PICOSECONDS_TYPE_NAME; } diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java index 762c40cf8c2b..b56bce432f93 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java @@ -271,6 +271,13 @@ public void testWrapperMethods() throws SQLException { assertThat(unwrappedImpl).isNotSameInstanceAs(resultSetMetaData); assertThat(unwrappedImpl).isInstanceOf(BigQueryResultSetMetadata.class); + BigQueryResultSetMetadata bqMetaData = + resultSetMetaData.unwrap(BigQueryResultSetMetadata.class); + assertThat(bqMetaData.getField(1)).isEqualTo(fieldList.get(0)); + assertThat(bqMetaData.getField(8).getMode()).isEqualTo(Field.Mode.REPEATED); + assertThrows(SQLException.class, () -> bqMetaData.getField(0)); + assertThrows(SQLException.class, () -> bqMetaData.getField(fieldList.size() + 1)); + BigQueryJdbcException e = assertThrows( BigQueryJdbcException.class, () -> resultSetMetaData.unwrap(java.sql.Connection.class));