From 8c247679a329f695494726ae01cbf01754d167b9 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 10:21:29 -0400 Subject: [PATCH 1/7] chore: move extended test suit to monorepo --- .../google/cloud/bigquery/jdbc/it/ITBase.java | 654 +++++ .../bigquery/jdbc/it/ITBigQueryJDBCTest.java | 2420 ++++------------- .../jdbc/it/ITCallableStatementTest.java | 665 +++++ .../jdbc/it/ITConnectionPoolingTest.java | 356 +++ .../bigquery/jdbc/it/ITConnectionTest.java | 471 ++++ .../jdbc/it/ITDatabaseMetadataTest.java | 1242 +++++++++ .../cloud/bigquery/jdbc/it/ITDriverTest.java | 120 + .../jdbc/it/ITResultSetMetadataTest.java | 82 + .../bigquery/jdbc/it/ITStatementTest.java | 412 +++ 9 files changed, 4532 insertions(+), 1890 deletions(-) create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITCallableStatementTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java index ba92e2d70aec..8ec77822b17f 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java @@ -16,14 +16,141 @@ package com.google.cloud.bigquery.jdbc.it; +import static java.sql.Types.TIME; +import static java.sql.Types.TIMESTAMP; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertNotNull; +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.QueryJobConfiguration; import com.google.cloud.bigquery.jdbc.BigQueryJdbcBaseTest; +import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.Assert; public class ITBase extends BigQueryJdbcBaseTest { + public static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + public static String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" + + DEFAULT_CATALOG + + ";OAuthType=3;Timeout=3600;"; + + public static final String createDatasetQuery = + "CREATE SCHEMA IF NOT EXISTS `%s.%s` OPTIONS(default_table_expiration_days = 5)"; + public static final String dropSchema = "DROP SCHEMA IF EXISTS `%s.%s` CASCADE;"; + public static final String createTableQuery = + "CREATE OR REPLACE TABLE " + + " `%s.%s.%s` " + + " (\n" + + "`StringField` STRING,\n" + + "`BytesField` BYTES,\n" + + "`IntegerField` INTEGER,\n" + + "`FloatField` FLOAT64,\n" + + "`NumericField` NUMERIC,\n" + + "`BigNumericField` BIGNUMERIC,\n" + + "`BooleanField` BOOLEAN,\n" + + "`TimestampField` TIMESTAMP,\n" + + "`DateField` DATE,\n" + + "`TimeField` TIME,\n" + + "`DateTimeField` DATETIME,\n" + + "`GeographyField` GEOGRAPHY,\n" + + "`RecordField` STRUCT,\n" + + "`JsonField` JSON,\n" + + ");"; + public static final String insertQuery1 = + "INSERT INTO " + + " `%s.%s.%s` " + + " (\n" + + "StringField, BytesField,IntegerField,FloatField,NumericField,BigNumericField,BooleanField,\n" + + "TimestampField,DateField,TimeField,DateTimeField,GeographyField,RecordField,JsonField )\n" + + "VALUES('string1',CAST ('string1' AS BYTES),111,1.1, CAST('11.1E11' AS NUMERIC), \n" + + "CAST('1.1E37' AS BIGNUMERIC), TRUE,CAST('2001-05-1 8:05:01' AS TIMESTAMP), \n" + + "CAST('2001-05-1' AS DATE),CAST('5:1:11.041' AS TIME), CAST('2001-05-1 11:31:45' AS DATETIME), \n" + + "CAST(ST_GEOGFROMTEXT('POINT(1.500989010415034 -1.11471081311336843)') AS GEOGRAPHY), \n" + + "CAST(('name1', 1) AS STRUCT), \n" + + " JSON \"\"\"{\n" + + " \"name\": \"Alice1\",\n" + + " \"items\": [\n" + + " {\"product\": \"book1\", \"price\": 1},\n" + + " {\"product\": \"food1\", \"price\": 1}\n" + + " ]\n" + + " }\"\"\"\n" + + ");"; + public static final String insertQuery2 = + "INSERT INTO " + + " `%s.%s.%s` " + + " (\n" + + " StringField, BytesField,IntegerField,FloatField,NumericField,BigNumericField,BooleanField,\n" + + " TimestampField,DateField,TimeField,DateTimeField,GeographyField,RecordField,JsonField )\n" + + " VALUES('string2',CAST ('string2' AS BYTES),222,2.2, CAST('22.2E22' AS NUMERIC),\n" + + " CAST('2.2E37' AS BIGNUMERIC), TRUE,CAST('2002-05-2 8:05:02' AS TIMESTAMP),\n" + + " CAST('2002-05-2' AS DATE),CAST('5:2:22.042' AS TIME), CAST('2002-05-2 22:32:45' AS DATETIME),\n" + + " CAST(ST_GEOGFROMTEXT('POINT(2.500989020425034 -2.22472082322336843)') AS GEOGRAPHY),\n" + + " CAST(('name2', 2) AS STRUCT),\n" + + " JSON \"\"\"{\n" + + " \"name\": \"Alice2\",\n" + + " \"items\": [\n" + + " {\"product\": \"book2\", \"price\": 2},\n" + + " {\"product\": \"food2\", \"price\": 2}\n" + + " ]\n" + + " }\"\"\"\n" + + " );"; + + public static final String createProcedure = + "CREATE OR REPLACE PROCEDURE `%s.%s`.create_customer() \n" + + "\tBEGIN\n" + + "\t\tDECLARE id STRING;\n" + + "\t\tSET id = GENERATE_UUID();\n" + + "\t\tINSERT INTO `%s.%s.%s` (StringField) VALUES(id);\n" + + "\t\tSELECT FORMAT(\"Created customer.\");\n" + + "\tEND"; + + public static void setUpProcedure(String dataset, String table) throws InterruptedException { + { + BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); + bigQuery.query( + QueryJobConfiguration.of( + String.format( + createProcedure, DEFAULT_CATALOG, dataset, DEFAULT_CATALOG, dataset, table))); + } + } + + public static void setUpDataset(String dataset) throws InterruptedException { + BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); + bigQuery.query( + QueryJobConfiguration.of(String.format(createDatasetQuery, DEFAULT_CATALOG, dataset))); + } + + public static void setUpTable(String dataset, String table) throws InterruptedException { + BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); + bigQuery.query( + QueryJobConfiguration.of(String.format(createTableQuery, DEFAULT_CATALOG, dataset, table))); + bigQuery.query( + QueryJobConfiguration.of(String.format(insertQuery1, DEFAULT_CATALOG, dataset, table))); + bigQuery.query( + QueryJobConfiguration.of(String.format(insertQuery2, DEFAULT_CATALOG, dataset, table))); + } + + public static void cleanUp(String dataset) throws InterruptedException { + BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); + bigQuery.query(QueryJobConfiguration.of(String.format(dropSchema, DEFAULT_CATALOG, dataset))); + } + protected static String requireEnvVar(String varName) { String value = System.getenv(varName); assertNotNull( @@ -39,4 +166,531 @@ protected int resultSetRowCount(ResultSet resultSet) throws SQLException { } return rowCount; } + + public static List getInfoBySQL(Connection connection, String sqlCmd) + throws SQLException { + List result = new ArrayList<>(); + try { + Statement st = connection.createStatement(); + ResultSet rs = st.executeQuery(sqlCmd); + while (rs.next()) { + result.add(rs.getString(1)); + } + } catch (SQLException e) { + throw e; + } + return result; + } + + protected void verifyAllBooleanMethods(DatabaseMetaData metaData) throws SQLException { + assertFalse(metaData.allProceduresAreCallable()); // false + assertTrue(metaData.allTablesAreSelectable()); // true + + assertFalse(metaData.nullsAreSortedHigh()); // false + assertTrue(metaData.nullsAreSortedLow()); // true + assertFalse(metaData.nullsAreSortedAtEnd()); // false + assertFalse(metaData.nullsAreSortedAtStart()); // false + assertTrue(metaData.nullPlusNonNullIsNull()); // true + + assertFalse(metaData.usesLocalFiles()); // false + assertFalse(metaData.usesLocalFilePerTable()); // false + assertFalse(metaData.storesUpperCaseIdentifiers()); // false + assertFalse(metaData.storesLowerCaseIdentifiers()); // false + assertTrue(metaData.supportsMixedCaseQuotedIdentifiers()); + assertFalse(metaData.storesUpperCaseQuotedIdentifiers()); // false + assertFalse(metaData.storesLowerCaseQuotedIdentifiers()); // false + assertFalse(metaData.storesMixedCaseQuotedIdentifiers()); // false + + assertFalse(metaData.supportsAlterTableWithAddColumn()); // false + assertFalse(metaData.supportsAlterTableWithDropColumn()); // false + assertTrue(metaData.supportsColumnAliasing()); + assertTrue(metaData.nullPlusNonNullIsNull()); + assertFalse(metaData.supportsConvert()); // false + assertFalse(metaData.supportsConvert(TIME, TIMESTAMP)); // false + assertTrue(metaData.supportsTableCorrelationNames()); + assertTrue(metaData.supportsExpressionsInOrderBy()); + assertFalse(metaData.supportsOrderByUnrelated()); // false + assertTrue(metaData.supportsGroupBy()); + assertFalse(metaData.supportsGroupByUnrelated()); // false + assertTrue(metaData.supportsGroupByBeyondSelect()); + + assertTrue(metaData.supportsMultipleTransactions()); + assertFalse(metaData.supportsNonNullableColumns()); + assertTrue(metaData.supportsMinimumSQLGrammar()); + assertTrue(metaData.supportsCoreSQLGrammar()); + assertFalse(metaData.supportsExtendedSQLGrammar()); + assertTrue(metaData.supportsANSI92EntryLevelSQL()); + assertFalse(metaData.supportsANSI92IntermediateSQL()); + assertFalse(metaData.supportsANSI92FullSQL()); + assertFalse(metaData.supportsFullOuterJoins()); + assertFalse(metaData.supportsLimitedOuterJoins()); + assertTrue(metaData.isCatalogAtStart()); + assertTrue(metaData.supportsSchemasInDataManipulation()); + + assertTrue(metaData.supportsSchemasInTableDefinitions()); + assertTrue(metaData.supportsSchemasInIndexDefinitions()); + assertTrue(metaData.supportsSchemasInPrivilegeDefinitions()); + assertTrue(metaData.supportsCatalogsInDataManipulation()); + assertFalse(metaData.supportsPositionedDelete()); + assertFalse(metaData.supportsPositionedUpdate()); + + assertFalse(metaData.supportsSelectForUpdate()); + assertTrue(metaData.supportsStoredProcedures()); + assertTrue(metaData.supportsSubqueriesInComparisons()); + assertTrue(metaData.supportsSubqueriesInExists()); + assertTrue(metaData.supportsSubqueriesInIns()); + assertTrue(metaData.supportsSubqueriesInQuantifieds()); + assertTrue(metaData.supportsCorrelatedSubqueries()); + assertFalse(metaData.supportsOpenCursorsAcrossCommit()); + + assertFalse(metaData.supportsOpenCursorsAcrossRollback()); + assertTrue(metaData.supportsOpenStatementsAcrossCommit()); + assertTrue(metaData.supportsOpenStatementsAcrossRollback()); + + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); // f + + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); // f + assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE)); // f + assertFalse(metaData.supportsDifferentTableCorrelationNames()); // f + + assertFalse(metaData.dataDefinitionIgnoredInTransactions()); + assertFalse(metaData.doesMaxRowSizeIncludeBlobs()); + + assertTrue(metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)); // t + assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)); // f + assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)); // f + + assertTrue( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); // t + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); // f + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)); + + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertTrue(metaData.supportsBatchUpdates()); + assertFalse(metaData.supportsSavepoints()); + assertFalse(metaData.supportsNamedParameters()); + assertFalse(metaData.supportsMultipleOpenResults()); + assertFalse(metaData.supportsGetGeneratedKeys()); + + assertFalse(metaData.generatedKeyAlwaysReturned()); + assertFalse(metaData.supportsIntegrityEnhancementFacility()); + assertFalse(metaData.supportsDataDefinitionAndDataManipulationTransactions()); + assertFalse(metaData.isReadOnly()); + } + + protected void verifyIntMethods(DatabaseMetaData metaData) throws SQLException { + assertEquals(0, metaData.getMaxBinaryLiteralLength()); // 0 + assertEquals(0, metaData.getMaxCharLiteralLength()); // 0 + assertEquals(0, metaData.getMaxColumnsInGroupBy()); // 0 + assertEquals(0, metaData.getMaxColumnsInIndex()); // 0 + assertEquals(0, metaData.getMaxColumnsInOrderBy()); // 0 + assertEquals(0, metaData.getMaxColumnsInSelect()); // 0 + assertEquals(0, metaData.getMaxConnections()); // 0 + assertEquals(0, metaData.getMaxCursorNameLength()); // 0 + assertEquals(0, metaData.getMaxIndexLength()); // 0 + assertEquals(0, metaData.getMaxProcedureNameLength()); // 0 + assertEquals(0, metaData.getMaxRowSize()); // 0 + assertEquals(0, metaData.getMaxStatementLength()); // 0 + assertEquals(0, metaData.getMaxStatements()); // 0 + assertEquals(1000, metaData.getMaxTablesInSelect()); // 1000 + assertEquals(0, metaData.getMaxUserNameLength()); // 0 + } + + protected void verifyAllStringMethods(DatabaseMetaData metaData, String procedure) + throws SQLException { + assertEquals("`", metaData.getIdentifierQuoteString()); + assertTrue(procedure.equalsIgnoreCase(metaData.getProcedureTerm())); + } + + protected void verifyGetPrimaryKeys( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIMARYKEY"; + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(KEYCOLUMN int NOT NULL, SECONDCOLUMN string, THIRDCOLUMN timestamp);"); + ResultSet resultSet = metaData.getPrimaryKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + assertFalse(resultSet.next()); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyTestProcedure(DatabaseMetaData metaData, String expectedProcedure) + throws SQLException { + assertEquals(expectedProcedure, metaData.getProcedureTerm()); + assertTrue(metaData.supportsStoredProcedures()); + ResultSet resultSet; + resultSet = metaData.getProcedureColumns("%", "%", "%", "%"); + assertEquals(0, resultSetRowCount(resultSet)); + resultSet = metaData.getProcedures("%", "%", "%"); + assertEquals(0, resultSetRowCount(resultSet)); + } + + protected void verifyDriverMetadata( + DatabaseMetaData metaData, + int expectedMajorVer, + int expectedMinorVersion, + String expectedDatabaseProductName, + String expectedDriverName, + Pattern versionPattern) + throws SQLException { + + assertEquals(expectedMajorVer, metaData.getJDBCMajorVersion()); + assertEquals(expectedMinorVersion, metaData.getJDBCMinorVersion()); + assertEquals(expectedDatabaseProductName, metaData.getDatabaseProductName()); + assertEquals(expectedDriverName, metaData.getDriverName()); + String driverVersion = metaData.getDriverVersion(); + Matcher m = versionPattern.matcher(driverVersion); + assertTrue(m.matches()); + int majorVersion = metaData.getDriverMajorVersion(); + int minorVersion = metaData.getDriverMinorVersion(); + assertEquals(Integer.parseInt(m.group(1)), majorVersion); + assertEquals(Integer.parseInt(m.group(2)), minorVersion); + } + + protected void verifyCatalogHelper( + DatabaseMetaData metaData, + String expectedCatalogSeparator, + String expectedCatalogTerm, + int expectedCatalogNameLength, + String defaultCatalog, + boolean isBeforeFirstSupported, + boolean isFirstSupported) + throws SQLException { + assertEquals(expectedCatalogSeparator, metaData.getCatalogSeparator()); + // Simba BQ JDBC driver maps Catalogs to projects + assertEquals(expectedCatalogTerm, metaData.getCatalogTerm()); + assertEquals(expectedCatalogNameLength, metaData.getMaxCatalogNameLength()); + + // This should return the project name of the connection + ResultSet resultSet = metaData.getCatalogs(); + + // Compares ResultSetMetadata. + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); + if (isBeforeFirstSupported) { + assertTrue(resultSet.isBeforeFirst()); + } + + int count = 0; + boolean defaultCatalogFound = false; + while (resultSet.next()) { + if (count == 0 && isFirstSupported) { + assertTrue(resultSet.isFirst()); + } + ++count; + if (defaultCatalog.equals(resultSet.getString(1))) { + defaultCatalogFound = true; + break; + } + } + assertTrue(defaultCatalogFound); + resultSet.close(); + } + + protected void verifySchemaHelper( + Connection connection, + DatabaseMetaData metaData, + String schemaTerm, + int maxSchemaNameLength, + boolean isBeforeFirstSupported, + boolean isFirstSupported) + throws SQLException { + // Simba BQ JDBC driver maps Table Datasets to Schemas + assertEquals(schemaTerm, metaData.getSchemaTerm()); + assertEquals(maxSchemaNameLength, metaData.getMaxSchemaNameLength()); + // This should return all the datasets in the project of the connection + ResultSet resultSet = metaData.getSchemas(); + // Compares ResultSetMetadata. + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); + if (isBeforeFirstSupported) { + assertTrue(resultSet.isBeforeFirst()); + } + + int count = 0; + Set allVisibleDatasets = new HashSet<>(); + while (resultSet.next()) { + allVisibleDatasets.add(resultSet.getString(1)); + if (count == 0 && isFirstSupported) { + assertTrue(resultSet.isFirst()); + } + ++count; + } + assertTrue(count >= 1); + List allAccessibleDatasets = + getInfoBySQL(connection, "SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA;"); + assertTrue(allVisibleDatasets.containsAll(allAccessibleDatasets)); + assertTrue(count >= allAccessibleDatasets.size()); + resultSet.close(); + } + + protected void verifyTableTypes(DatabaseMetaData metaData, Collection expectedTypes) + throws SQLException { + ResultSet resultSet = metaData.getTableTypes(); + Set types = new HashSet<>(); + while (resultSet.next()) { + String col = resultSet.getString(1); + types.add(col); + } + assertTrue(types.size() > 0); + assertTrue(types.containsAll(expectedTypes)); + } + + protected void verifyGetTables(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTTABLE"; + final String jdbctestview = "JDBCTESTVIEW"; + + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` " + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + statement.execute( + String.format( + "create or replace view `%s.%s.%s` " + " as select 1 as VIEWCOLUMN;", + ITBase.DEFAULT_CATALOG, dataset, jdbctestview)); + + ResultSet resultSet; + + // Pattern match for table + resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"TABLE"}); + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_TABLES); + Set tables = new HashSet<>(); + while (resultSet.next()) { + tables.add(resultSet.getString(3)); + } + assertTrue(tables.contains("JDBCTESTTABLE")); + + // exact match for tablename + resultSet = + metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, new String[] {"TABLE"}); + tables = new HashSet<>(); + while (resultSet.next()) { + tables.add(resultSet.getString(3)); + } + assertEquals(jdbctesttable, tables.iterator().next()); + + // Pattern match for view + resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"VIEW"}); + Set views = new HashSet<>(); + while (resultSet.next()) { + views.add(resultSet.getString(3)); + } + assertTrue(views.contains(jdbctestview)); + + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + connection.createStatement().execute("drop view if exists " + dataset + "." + jdbctestview); + } + + // GetTablePrivileges not supported for JDBC and ODBC datasource. + // Returns empty resultset + protected void verifyGetTablePrivileges( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; + + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + + ResultSet resultSet = + metaData.getTablePrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyGetColumnPrivileges( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; + + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + + ResultSet resultSet = + metaData.getColumnPrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%"); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyForeignKeys(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTTABLEFOREIGNKEYS"; + + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + + ResultSet resultSet = metaData.getImportedKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyGetColumns(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTCOLUMNS"; + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + ResultSet resultSet = + metaData.getColumns(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%COLUMN"); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + // Resultset is not empty + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + } + ++count; + } + resultSet.close(); + assertTrue(count > 0); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyVersionColumns( + Connection connection, + DatabaseMetaData metaData, + String dataset, + int expectedNumCols, + Collection expectedColNames) + throws SQLException { + final String jdbctesttable = "JDBCTESTCOLUMNS"; + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + ResultSet resultSet = + metaData.getVersionColumns(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + int numCols = resultSet.getMetaData().getColumnCount(); + assertEquals(expectedNumCols, numCols); + List colNames = new ArrayList<>(); + for (int i = 1; i <= numCols; i++) { + colNames.add(resultSet.getMetaData().getColumnName(i)); + } + assertTrue(colNames.containsAll(expectedColNames)); + resultSet.close(); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyIndexInfo(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTTABLE"; + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + ResultSet rs = + metaData.getIndexInfo(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, false, false); + assertFalse(rs.next()); // no index defined. + rs.close(); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBigQueryJDBCTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBigQueryJDBCTest.java index 771faa6402a0..316444aebb75 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBigQueryJDBCTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBigQueryJDBCTest.java @@ -38,16 +38,11 @@ import com.google.cloud.bigquery.jdbc.BigQueryConnection; import com.google.cloud.bigquery.jdbc.BigQueryDriver; import com.google.cloud.bigquery.jdbc.DataSource; -import com.google.cloud.bigquery.jdbc.PooledConnectionDataSource; -import com.google.cloud.bigquery.jdbc.PooledConnectionListener; -import com.google.cloud.bigquery.jdbc.utils.TestUtilities.TestConnectionListener; import com.google.common.collect.ImmutableMap; import java.io.File; import java.io.IOException; -import java.math.BigDecimal; -import java.sql.CallableStatement; +import java.nio.charset.StandardCharsets; import java.sql.Connection; -import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.Driver; import java.sql.DriverManager; @@ -58,19 +53,12 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; -import java.sql.Types; import java.time.Instant; import java.time.LocalTime; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashSet; import java.util.Properties; import java.util.Random; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.BiFunction; -import javax.sql.PooledConnection; +import org.junit.Assert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -78,11 +66,6 @@ public class ITBigQueryJDBCTest extends ITBase { static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); - static Connection bigQueryConnection; - static BigQuery bigQuery; - static Statement bigQueryStatement; - static Connection bigQueryConnectionNoReadApi; - static Statement bigQueryStatementNoReadApi; static final String connection_uri = "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + PROJECT_ID @@ -94,24 +77,12 @@ public class ITBigQueryJDBCTest extends ITBase { private static final Random random = new Random(); private static final int randomNumber = random.nextInt(9999); private static final String DATASET = "JDBC_PRESUBMIT_INTEGRATION_DATASET"; - private static final String DATASET2 = "JDBC_PRESUBMIT_INTEGRATION_DATASET_2"; - private static final String CONSTRAINTS_DATASET = "JDBC_CONSTRAINTS_TEST_DATASET"; - private static final String CONSTRAINTS_TABLE_NAME = "JDBC_CONSTRAINTS_TEST_TABLE"; - private static final String CONSTRAINTS_TABLE_NAME2 = "JDBC_CONSTRAINTS_TEST_TABLE2"; - private static final String CONSTRAINTS_TABLE_NAME3 = "JDBC_CONSTRAINTS_TEST_TABLE3"; - private static final String CALLABLE_STMT_PROC_NAME = "IT_CALLABLE_STMT_PROC_TEST"; - private static final String CALLABLE_STMT_TABLE_NAME = "IT_CALLABLE_STMT_PROC_TABLE"; - private static final String CALLABLE_STMT_PARAM_KEY = "CALL_STMT_PARAM_KEY"; - private static final String CALLABLE_STMT_DML_INSERT_PROC_NAME = - "IT_CALLABLE_STMT_PROC_DML_INSERT_TEST"; - private static final String CALLABLE_STMT_DML_UPDATE_PROC_NAME = - "IT_CALLABLE_STMT_PROC_DML_UPDATE_TEST"; - private static final String CALLABLE_STMT_DML_DELETE_PROC_NAME = - "IT_CALLABLE_STMT_PROC_DML_DELETE_TEST"; - private static final String CALLABLE_STMT_DML_TABLE_NAME = "IT_CALLABLE_STMT_PROC_DML_TABLE"; - private static final Long DEFAULT_CONN_POOL_SIZE = 10L; - private static final Long CUSTOM_CONN_POOL_SIZE = 5L; private static final Object EXCEPTION_REPLACEMENT = "EXCEPTION-WAS-RAISED"; + static Connection bigQueryConnection; + static BigQuery bigQuery; + static Statement bigQueryStatement; + static Connection bigQueryConnectionNoReadApi; + static Statement bigQueryStatementNoReadApi; @BeforeAll public static void beforeClass() throws SQLException { @@ -133,6 +104,49 @@ public static void afterClass() throws SQLException { bigQueryConnectionNoReadApi.close(); } + @Test + public void testValidAllDataTypesSerializationFromSelectQueryArrowDataset() throws SQLException { + String DATASET = "JDBC_INTEGRATION_DATASET"; + String TABLE_NAME = "JDBC_INTEGRATION_ARROW_TEST_TABLE"; + String selectQuery = "select * from " + DATASET + "." + TABLE_NAME + " LIMIT 5000;"; + String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;ProjectId=" + + DEFAULT_CATALOG + + ";EnableHighThroughputAPI=1;" + + "HighThroughputActivationRatio=2;" + + "HighThroughputMinTableSize=1000;"; + + // Read data via JDBC + Connection connection = DriverManager.getConnection(connection_uri); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(selectQuery); + Assert.assertNotNull(resultSet); + + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + resultSet.next(); + Assert.assertEquals(15, resultSetMetaData.getColumnCount()); + Assert.assertTrue(resultSet.getBoolean(1)); + Assert.assertEquals(33, resultSet.getInt(2)); + Assert.assertEquals(50.05f, resultSet.getFloat(3), 0.0); + Assert.assertEquals(123.456, resultSet.getDouble(4), 0.0); + Assert.assertEquals(123.456789, resultSet.getDouble(5), 0.0); + Assert.assertEquals("testString", resultSet.getString(6)); + Assert.assertEquals("Test String", new String(resultSet.getBytes(7), StandardCharsets.UTF_8)); + Assert.assertEquals(Timestamp.valueOf("2020-04-27 18:07:25.356"), resultSet.getObject(10)); + Assert.assertEquals(Timestamp.valueOf("2020-04-27 18:07:25.356"), resultSet.getTimestamp(10)); + Assert.assertEquals(Date.valueOf("2019-1-12"), resultSet.getObject(11)); + Assert.assertEquals(Date.valueOf("2019-1-12"), resultSet.getDate(11)); + Assert.assertEquals(Time.valueOf("14:00:00"), resultSet.getObject(12)); + Assert.assertEquals(Time.valueOf("14:00:00"), resultSet.getTime(12)); + Assert.assertEquals(Timestamp.valueOf("2022-01-22 22:22:12.142265"), resultSet.getObject(13)); + Assert.assertEquals("POINT(1 2)", resultSet.getString(14)); + Assert.assertEquals( + "{\"class\":{\"students\":[{\"name\":\"Jane\"}]}}", resultSet.getString(15)); + connection.close(); + connection.close(); + } + @Test public void testFastQueryPathSmall() throws SQLException { String query = @@ -143,6 +157,19 @@ public void testFastQueryPathSmall() throws SQLException { assertEquals(850, resultSetRowCount(jsonResultSet)); } + @Test + public void testFastQueryPathEmpty() throws SQLException { + String query = + "SELECT DISTINCT repository_name FROM `bigquery-public-data.samples.github_timeline` LIMIT" + + " 0"; + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection.createStatement(); + ResultSet jsonResultSet = bigQueryStatement.executeQuery(query); + Assert.assertEquals(0, resultSetRowCount(jsonResultSet)); + connection.close(); + } + @Test public void testSmallSelectAndVerifyResults() throws SQLException { String query = @@ -176,6 +203,104 @@ public void testIterateOrderJsonMultiThread_NoReadApi() throws SQLException { assertEquals(expectedCnt, cnt); // all the records were retrieved } + @Test + // reads using ReadAPI and makes sure that they are in order, which implies threads worked + // correctly + public void testIterateOrderArrowMultiThread() throws SQLException { + int expectedCnt = 200000; + String longQuery = String.format(BASE_QUERY, expectedCnt); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection.createStatement(); + ResultSet rs = bigQueryStatement.executeQuery(longQuery); + int cnt = 0; + double oldTriDis = 0.0d; + while (rs.next()) { + double tripDis = rs.getDouble("trip_distance"); + ++cnt; + Assert.assertTrue(oldTriDis <= tripDis); + oldTriDis = tripDis; + } + Assert.assertEquals(expectedCnt, cnt); // all the records were retrieved + connection.close(); + } + + @Test + public void testReadAPIPathLarge() throws SQLException { + Properties withReadApi = new Properties(); + withReadApi.setProperty("EnableHighThroughputAPI", "1"); + withReadApi.setProperty("HighThroughputActivationRatio", "2"); + withReadApi.setProperty("HighThroughputMinTableSize", "1000"); + withReadApi.setProperty("MaxResults", "300"); + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG), withReadApi); + Statement statement = connection.createStatement(); + int expectedCnt = 5000; + String longQuery = String.format(BASE_QUERY, expectedCnt); + ResultSet arrowResultSet = statement.executeQuery(longQuery); + Assert.assertEquals(expectedCnt, resultSetRowCount(arrowResultSet)); + arrowResultSet.close(); + connection.close(); + } + + @Test + public void testReadAPIPathLargeWithThresholdParameters() throws SQLException { + String connectionUri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + + DEFAULT_CATALOG + + ";OAUTHTYPE=3;MaxResults=300;HighThroughputActivationRatio=2;" + + "HighThroughputMinTableSize=100;EnableHighThroughputAPI=1"; + Connection connection = DriverManager.getConnection(connectionUri); + Statement statement = connection.createStatement(); + int expectedCnt = 1000; + String longQuery = String.format(BASE_QUERY, expectedCnt); + ResultSet arrowResultSet = statement.executeQuery(longQuery); + Assert.assertEquals(expectedCnt, resultSetRowCount(arrowResultSet)); + arrowResultSet.close(); + connection.close(); + } + + @Test + public void testReadAPIPathLargeWithThresholdNotMet() throws SQLException { + String connectionUri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + + DEFAULT_CATALOG + + ";OAUTHTYPE=3;HighThroughputActivationRatio=4;" + + "HighThroughputMinTableSize=100;EnableHighThroughputAPI=1"; + Connection connection = DriverManager.getConnection(connectionUri); + Statement statement = connection.createStatement(); + int expectedCnt = 5000; + String longQuery = String.format(BASE_QUERY, expectedCnt); + ResultSet arrowResultSet = statement.executeQuery(longQuery); + Assert.assertEquals(expectedCnt, resultSetRowCount(arrowResultSet)); + arrowResultSet.close(); + connection.close(); + } + + @Test + public void testStatelessQueryPathSmall() throws SQLException { + Properties jobCreationMode = new Properties(); + jobCreationMode.setProperty("JobCreationMode", "2"); + Connection connectionUseStateless = + DriverManager.getConnection(ITBase.connectionUrl, jobCreationMode); + + Statement statement = connectionUseStateless.createStatement(); + + String query = + "SELECT DISTINCT repository_name FROM `bigquery-public-data.samples.github_timeline` LIMIT" + + " 850"; + ResultSet jsonResultSet = statement.executeQuery(query); + Assert.assertEquals(850, resultSetRowCount(jsonResultSet)); + + String queryEmpty = + "SELECT DISTINCT repository_name FROM `bigquery-public-data.samples.github_timeline` LIMIT" + + " 0"; + ResultSet jsonResultSetEmpty = statement.executeQuery(queryEmpty); + Assert.assertEquals(0, resultSetRowCount(jsonResultSetEmpty)); + connectionUseStateless.close(); + } + @Test public void testInvalidQuery() throws SQLException { String query = "SELECT *"; @@ -436,6 +561,48 @@ public void testCommitWithNoTransactionStartedThrowsIllegalState() throws SQLExc connection.close(); } + @Test + public void testRollbackOnConnectionClosed() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE1" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String insertQuery = + String.format( + "INSERT INTO %s.%s (id, name, age) VALUES (15, 'Farhan', %s);", + DATASET, TRANSACTION_TABLE, randomNumber); + String updateQuery = + String.format( + "UPDATE %s.%s SET age = 12 WHERE age = %s;", DATASET, TRANSACTION_TABLE, randomNumber); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s WHERE id = 12;", DATASET, TRANSACTION_TABLE); + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + connection.setAutoCommit(false); + Statement statement = connection.createStatement(); + + boolean status = statement.execute(insertQuery); + Assert.assertFalse(status); + int rows = statement.executeUpdate(updateQuery); + Assert.assertEquals(1, rows); + status = statement.execute(selectQuery); + Assert.assertTrue(status); + connection.close(); + + // Separate query to check if transaction rollback worked + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + Assert.assertFalse(resultSet.next()); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + connection.close(); + } + @Test public void testRollbackWithConnectionClosedThrowsIllegalState() throws SQLException { BigQueryConnection connection = @@ -460,6 +627,115 @@ public void testRollbackWithoutTransactionStartedThrowsIllegalState() throws SQL connection.close(); } + @Test + public void testMultiStatementTransactionRollbackByUser() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE3" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String insertQuery = + String.format( + "INSERT INTO %s.%s (id, name, age) VALUES (12, 'Farhan', %s);", + DATASET, TRANSACTION_TABLE, randomNumber); + String updateQuery = + String.format( + "UPDATE %s.%s SET age = 14 WHERE age = %s;", DATASET, TRANSACTION_TABLE, randomNumber); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s WHERE id = 12;", DATASET, TRANSACTION_TABLE); + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + connection.setAutoCommit(false); + Statement statement = connection.createStatement(); + + boolean status = statement.execute(insertQuery); + Assert.assertFalse(status); + int rows = statement.executeUpdate(updateQuery); + Assert.assertEquals(1, rows); + status = statement.execute(selectQuery); + Assert.assertTrue(status); + connection.rollback(); + + // Separate query to check if transaction rollback worked + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + Assert.assertFalse(resultSet.next()); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + connection.close(); + } + + @Test + public void testSingleStatementTransaction() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE2" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String beginTransaction = "BEGIN TRANSACTION; "; + String insertQuery = + String.format( + "INSERT INTO %s.%s (id, name, age) VALUES (12, 'Farhan', %s);", + DATASET, TRANSACTION_TABLE, randomNumber); + String updateQuery = + String.format( + "UPDATE %s.%s SET age = 14 WHERE age = %s;", DATASET, TRANSACTION_TABLE, randomNumber); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s WHERE id = 12;", DATASET, TRANSACTION_TABLE); + String commitTransaction = "COMMIT TRANSACTION;"; + + String transactionQuery = + beginTransaction + + insertQuery + + insertQuery + + updateQuery + + selectQuery + + commitTransaction; + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + + // Run the transaction + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + Statement statement = connection.createStatement(); + statement.execute(transactionQuery); + + // Test each query's result with getMoreResults + int resultsCount = 0; + boolean hasMoreResult = statement.getMoreResults(); + while (hasMoreResult || statement.getUpdateCount() != -1) { + if (statement.getUpdateCount() == -1) { + ResultSet result = statement.getResultSet(); + Assert.assertTrue(result.next()); + Assert.assertEquals(-1, statement.getUpdateCount()); + } else { + Assert.assertTrue(statement.getUpdateCount() > -1); + } + hasMoreResult = statement.getMoreResults(); + resultsCount++; + } + + // Check the transaction was actually committed. + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + int rowCount = 0; + while (resultSet.next()) { + rowCount++; + Assert.assertEquals(14, resultSet.getInt(3)); + } + Assert.assertEquals(2, rowCount); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + connection.close(); + } + @Test public void testGetLocationWhenConnectionClosedThrows() throws SQLException { Driver driver = BigQueryDriver.getRegisteredDriver(); @@ -499,6 +775,137 @@ public void testSetAutocommitWhenConnectionClosedThrows() throws SQLException { assertThrows(IllegalStateException.class, () -> connection.setAutoCommit(true)); } + @Test + public void testMultiStatementTransactionDoesNotCommitWithoutCommit() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE4" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String insertQuery = + String.format( + "INSERT INTO %s.%s (id, name, age) VALUES (12, 'Farhan', %s);", + DATASET, TRANSACTION_TABLE, randomNumber); + String updateQuery = + String.format( + "UPDATE %s.%s SET age = 14 WHERE age = %s;", DATASET, TRANSACTION_TABLE, randomNumber); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s WHERE id = 12;", DATASET, TRANSACTION_TABLE); + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + connection.setAutoCommit(false); + Statement statement = connection.createStatement(); + + boolean status = statement.execute(insertQuery); + Assert.assertFalse(status); + int rows = statement.executeUpdate(updateQuery); + Assert.assertEquals(1, rows); + status = statement.execute(selectQuery); + Assert.assertTrue(status); + + // Separate query to check nothing committed + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + Assert.assertFalse(resultSet.next()); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + statement.close(); + connection.close(); + } + + @Test + public void testValidMultiStatementTransactionCommits() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE5" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String insertQuery = + String.format( + "INSERT INTO %s.%s (id, name, age) VALUES (12, 'Farhan', %s);", + DATASET, TRANSACTION_TABLE, randomNumber); + String updateQuery = + String.format( + "UPDATE %s.%s SET age = 14 WHERE age = %s;", DATASET, TRANSACTION_TABLE, randomNumber); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s WHERE id = 12;", DATASET, TRANSACTION_TABLE); + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + connection.setAutoCommit(false); + Statement statement = connection.createStatement(); + + boolean status = statement.execute(insertQuery); + Assert.assertFalse(status); + status = statement.execute(updateQuery); + Assert.assertFalse(status); + status = statement.execute(selectQuery); + Assert.assertTrue(status); + connection.commit(); + + // Separate query to check inserted and updated data committed + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals(14, resultSet.getInt(3)); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + statement.close(); + connection.close(); + } + + @Test + public void testTransactionRollbackOnError() throws SQLException { + String TRANSACTION_TABLE = "JDBC_TRANSACTION_TABLE6" + random.nextInt(99); + String createTransactionTable = + String.format( + "CREATE OR REPLACE TABLE %s.%s (`id` INTEGER, `name` STRING, `age` INTEGER);", + DATASET, TRANSACTION_TABLE); + String selectQuery = + String.format("SELECT id, name, age FROM %s.%s ;", DATASET, TRANSACTION_TABLE); + + Connection connection1 = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement bigQueryStatement = connection1.createStatement(); + bigQueryStatement.execute(createTransactionTable); + String transactionOnError = + "BEGIN\n" + + "\n" + + " BEGIN TRANSACTION;\n" + + " INSERT INTO " + + DATASET + + "." + + TRANSACTION_TABLE + + "\n" + + " VALUES (39, 'Drake', 123);\n" + + " SELECT 1/0;\n" + + " COMMIT TRANSACTION;\n" + + "\n" + + "EXCEPTION WHEN ERROR THEN\n" + + " SELECT @@error.message;\n" + + " ROLLBACK TRANSACTION;\n" + + "END;"; + Connection connection = DriverManager.getConnection(session_enabled_connection_uri); + Statement statement = connection.createStatement(); + statement.execute(transactionOnError); + + // do a check to see if no vals inserted + ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery); + Assert.assertFalse(resultSet.next()); + + bigQueryStatement.execute( + String.format("DROP TABLE IF EXISTS %S.%s", DATASET, TRANSACTION_TABLE)); + connection.close(); + connection1.close(); + } + @Test public void testExecuteUpdate() throws SQLException { String TABLE_NAME = "JDBC_EXECUTE_UPDATE_TABLE_" + randomNumber; @@ -1327,46 +1734,87 @@ public void testValidDestinationTableSavesQueriesWithLegacySQL() throws SQLExcep } @Test - public void testValidDestinationTableSavesQueriesWithStandardSQL() throws SQLException { + public void testNonEnabledUseLegacySQLThrowsSyntaxError() throws SQLException { // setup String connection_uri = "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + "OAuthType=3;" + "ProjectId=" - + PROJECT_ID - + ";QueryDialect=SQL;" - + "LargeResultTable=destination_table_test;" - + "LargeResultDataset=INTEGRATION_TESTS;"; + + DEFAULT_CATALOG + + ";"; String selectLegacyQuery = - "SELECT * FROM `bigquery-public-data.deepmind_alphafold.metadata` LIMIT 200;"; - Driver driver = BigQueryDriver.getRegisteredDriver(); - Connection connection = driver.connect(connection_uri, new Properties()); + "SELECT * FROM [bigquery-public-data.deepmind_alphafold.metadata] LIMIT 20000000;"; + Connection connection = DriverManager.getConnection(connection_uri, new Properties()); Statement statement = connection.createStatement(); - // act - ResultSet resultSet = statement.executeQuery(selectLegacyQuery); - - // assertion - assertNotNull(resultSet); - String selectQuery = "SELECT * FROM INTEGRATION_TESTS.destination_table_test;"; - ResultSet actualResultSet = bigQueryStatement.executeQuery(selectQuery); - assertEquals(200, resultSetRowCount(actualResultSet)); - - // clean up - String deleteRows = "DELETE FROM `INTEGRATION_TESTS.destination_table_test` WHERE 1=1;"; - bigQueryStatement.execute(deleteRows); + // act & assertion + Assert.assertThrows(SQLException.class, () -> statement.execute(selectLegacyQuery)); connection.close(); } @Test - public void testDestinationTableAndDestinationDatasetThatDoesNotExistsCreates() - throws SQLException { + public void testUseLegacySQLWithLargeResultsNotAllowedQueries() throws SQLException { // setup String connection_uri = "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + "OAuthType=3;" + "ProjectId=" - + PROJECT_ID + + DEFAULT_CATALOG + + ";QueryDialect=BIG_QUERY;AllowLargeResults=0;"; + String selectLegacyQuery = + "SELECT * FROM [bigquery-public-data.deepmind_alphafold.metadata] LIMIT 250000;"; + Connection connection = DriverManager.getConnection(connection_uri, new Properties()); + Statement statement = connection.createStatement(); + + // act + ResultSet resultSet = statement.executeQuery(selectLegacyQuery); + + // assertion + Assert.assertNotNull(resultSet); + connection.close(); + } + + @Test + public void testValidDestinationTableSavesQueriesWithStandardSQL() throws SQLException { + // setup + String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;" + + "ProjectId=" + + PROJECT_ID + + ";QueryDialect=SQL;" + + "LargeResultTable=destination_table_test;" + + "LargeResultDataset=INTEGRATION_TESTS;"; + String selectLegacyQuery = + "SELECT * FROM `bigquery-public-data.deepmind_alphafold.metadata` LIMIT 200;"; + Driver driver = BigQueryDriver.getRegisteredDriver(); + Connection connection = driver.connect(connection_uri, new Properties()); + Statement statement = connection.createStatement(); + + // act + ResultSet resultSet = statement.executeQuery(selectLegacyQuery); + + // assertion + assertNotNull(resultSet); + String selectQuery = "SELECT * FROM INTEGRATION_TESTS.destination_table_test;"; + ResultSet actualResultSet = bigQueryStatement.executeQuery(selectQuery); + assertEquals(200, resultSetRowCount(actualResultSet)); + + // clean up + String deleteRows = "DELETE FROM `INTEGRATION_TESTS.destination_table_test` WHERE 1=1;"; + bigQueryStatement.execute(deleteRows); + connection.close(); + } + + @Test + public void testDestinationTableAndDestinationDatasetThatDoesNotExistsCreates() + throws SQLException { + // setup + String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;" + + "ProjectId=" + + PROJECT_ID + ";QueryDialect=BIG_QUERY;" + "AllowLargeResults=1;" + "LargeResultTable=FakeTable;" @@ -1391,6 +1839,29 @@ public void testDestinationTableAndDestinationDatasetThatDoesNotExistsCreates() connection.close(); } + @Test + public void testUseLegacySQLWithLargeResultsAllowedWithNoDestinationTableDefaults() + throws SQLException { + // setup + String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;" + + "ProjectId=" + + DEFAULT_CATALOG + + ";QueryDialect=BIG_QUERY;AllowLargeResults=1;"; + String selectLegacyQuery = + "SELECT * FROM [bigquery-public-data.deepmind_alphafold.metadata] LIMIT 250000;"; + Connection connection = DriverManager.getConnection(connection_uri, new Properties()); + Statement statement = connection.createStatement(); + + // act + ResultSet resultSet = statement.executeQuery(selectLegacyQuery); + + // assertion + Assert.assertNotNull(resultSet); + connection.close(); + } + @Test public void testDestinationTableWithMissingDestinationDatasetDefaults() throws SQLException { // setup @@ -1471,728 +1942,6 @@ public void testNonSelectForStandardDestinationTableDoesNotThrow() throws SQLExc connection.close(); } - @Test - public void testTableConstraints() throws SQLException { - ResultSet primaryKey1 = - bigQueryConnection - .getMetaData() - .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); - primaryKey1.next(); - assertEquals("id", primaryKey1.getString(4)); - assertFalse(primaryKey1.next()); - - ResultSet primaryKey2 = - bigQueryConnection - .getMetaData() - .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME2); - primaryKey2.next(); - assertEquals("first_name", primaryKey2.getString(4)); - primaryKey2.next(); - assertEquals("last_name", primaryKey2.getString(4)); - assertFalse(primaryKey2.next()); - - ResultSet foreignKeys = - bigQueryConnection - .getMetaData() - .getImportedKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); - foreignKeys.next(); - assertEquals(CONSTRAINTS_TABLE_NAME2, foreignKeys.getString(3)); - assertEquals("first_name", foreignKeys.getString(4)); - assertEquals("name", foreignKeys.getString(8)); - foreignKeys.next(); - assertEquals(CONSTRAINTS_TABLE_NAME2, foreignKeys.getString(3)); - assertEquals("last_name", foreignKeys.getString(4)); - assertEquals("second_name", foreignKeys.getString(8)); - foreignKeys.next(); - assertEquals(CONSTRAINTS_TABLE_NAME3, foreignKeys.getString(3)); - assertEquals("address", foreignKeys.getString(4)); - assertEquals("address", foreignKeys.getString(8)); - assertFalse(foreignKeys.next()); - - ResultSet crossReference = - bigQueryConnection - .getMetaData() - .getCrossReference( - PROJECT_ID, - CONSTRAINTS_DATASET, - CONSTRAINTS_TABLE_NAME2, - PROJECT_ID, - CONSTRAINTS_DATASET, - CONSTRAINTS_TABLE_NAME); - crossReference.next(); - assertEquals(CONSTRAINTS_TABLE_NAME2, crossReference.getString(3)); - assertEquals("first_name", crossReference.getString(4)); - assertEquals("name", crossReference.getString(8)); - crossReference.next(); - assertEquals("last_name", crossReference.getString(4)); - assertEquals("second_name", crossReference.getString(8)); - assertFalse(crossReference.next()); - } - - @Test - public void testDatabaseMetadataGetCatalogs() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - try (ResultSet rs = databaseMetaData.getCatalogs()) { - assertNotNull(rs, "ResultSet from getCatalogs() should not be null"); - - ResultSetMetaData rsmd = rs.getMetaData(); - assertNotNull(rsmd, "ResultSetMetaData should not be null"); - assertEquals(1, rsmd.getColumnCount(), "Should have one column"); - assertEquals("TABLE_CAT", rsmd.getColumnName(1), "Column name should be TABLE_CAT"); - - assertTrue(rs.next(), "ResultSet should have one row"); - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT"), "Catalog name should match Project ID"); - assertFalse(rs.next(), "ResultSet should have no more rows"); - } - } - - @Test - public void testDatabaseMetadataGetProcedures() throws SQLException { - String DATASET = "JDBC_INTEGRATION_DATASET"; - String procedureName = "create_customer"; - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - ResultSet resultSet = databaseMetaData.getProcedures(PROJECT_ID, DATASET, procedureName); - while (resultSet.next()) { - assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); - assertEquals(DATASET, resultSet.getString("PROCEDURE_SCHEM")); - assertEquals(procedureName, resultSet.getString("PROCEDURE_NAME")); - assertEquals(procedureName, resultSet.getString("SPECIFIC_NAME")); - assertEquals(DatabaseMetaData.procedureResultUnknown, resultSet.getInt("PROCEDURE_TYPE")); - } - } - - @Test - public void testDatabaseMetadataGetProcedureColumns() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - - // --- Test Case 1: Specific schema and procedure, null column name pattern --- - String specificSchema = "JDBC_INTEGRATION_DATASET"; - String specificProcedure = "create_customer"; - ResultSet resultSet = - databaseMetaData.getProcedureColumns(PROJECT_ID, specificSchema, specificProcedure, null); - int specificProcRows = 0; - boolean foundNameParam = false; - boolean foundIdParam = false; - while (resultSet.next()) { - specificProcRows++; - assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); - assertEquals(specificSchema, resultSet.getString("PROCEDURE_SCHEM")); - assertEquals(specificProcedure, resultSet.getString("PROCEDURE_NAME")); - assertEquals(specificProcedure, resultSet.getString("SPECIFIC_NAME")); - if ("name".equals(resultSet.getString("COLUMN_NAME"))) { - foundNameParam = true; - assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); - } - if ("id".equals(resultSet.getString("COLUMN_NAME"))) { - foundIdParam = true; - assertEquals(2, resultSet.getInt("ORDINAL_POSITION")); - } - } - assertEquals(2, specificProcRows, "Should find 2 parameters for " + specificProcedure); - assertTrue(foundNameParam, "Parameter 'name' should be found"); - assertTrue(foundIdParam, "Parameter 'id' should be found"); - resultSet.close(); - - // --- Test Case 2: Specific schema, procedure, and column name pattern --- - String specificColumn = "name"; - resultSet = - databaseMetaData.getProcedureColumns( - PROJECT_ID, specificSchema, specificProcedure, specificColumn); - assertTrue(resultSet.next(), "Should find the specific column 'name'"); - assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); - assertEquals(specificSchema, resultSet.getString("PROCEDURE_SCHEM")); - assertEquals(specificProcedure, resultSet.getString("PROCEDURE_NAME")); - assertEquals(specificColumn, resultSet.getString("COLUMN_NAME")); - assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); - assertEquals( - (short) DatabaseMetaData.procedureColumnUnknown, resultSet.getShort("COLUMN_TYPE")); - assertEquals(java.sql.Types.NVARCHAR, resultSet.getInt("DATA_TYPE")); - assertEquals("NVARCHAR", resultSet.getString("TYPE_NAME")); - assertFalse(resultSet.next(), "Should only find one row for exact column match"); - resultSet.close(); - - // --- Test Case 3: Non-existent procedure --- - resultSet = - databaseMetaData.getProcedureColumns( - PROJECT_ID, specificSchema, "non_existent_procedure_xyz", null); - assertFalse(resultSet.next(), "Should not find columns for a non-existent procedure"); - resultSet.close(); - } - - @Test - public void testDatabaseMetadataGetColumns() throws SQLException { - String DATASET = "JDBC_INTEGRATION_DATASET"; - String TABLE_NAME = "JDBC_DATATYPES_INTEGRATION_TEST_TABLE"; - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - - // --- Test Case 1: Specific Column (StringField) --- - ResultSet resultSet = - databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "StringField"); - - assertTrue(resultSet.next()); - assertEquals(PROJECT_ID, resultSet.getString("TABLE_CAT")); - assertEquals(DATASET, resultSet.getString("TABLE_SCHEM")); - assertEquals(TABLE_NAME, resultSet.getString("TABLE_NAME")); - assertEquals("StringField", resultSet.getString("COLUMN_NAME")); - assertEquals("NVARCHAR", resultSet.getString("TYPE_NAME")); - resultSet.getObject("COLUMN_SIZE"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(6, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 2: All Columns --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, null); - assertTrue(resultSet.next()); - int count = 0; - do { - count++; - assertEquals(PROJECT_ID, resultSet.getString("TABLE_CAT")); - assertEquals(DATASET, resultSet.getString("TABLE_SCHEM")); - assertEquals(TABLE_NAME, resultSet.getString("TABLE_NAME")); - assertNotNull(resultSet.getString("COLUMN_NAME")); - } while (resultSet.next()); - assertEquals(16, count); - - // --- Test Case 3: Column Name Pattern Matching (%Field) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "%Time%"); - assertTrue(resultSet.next()); - count = 0; - do { - count++; - String columnName = resultSet.getString("COLUMN_NAME"); - assertTrue(columnName.contains("Time")); - } while (resultSet.next()); - assertEquals(3, count); - - // --- Test Case 4: Column Name Pattern Matching (Integer%) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "Integer%"); - assertTrue(resultSet.next()); - assertEquals("IntegerField", resultSet.getString("COLUMN_NAME")); - assertEquals("BIGINT", resultSet.getString("TYPE_NAME")); - assertEquals(19, resultSet.getInt("COLUMN_SIZE")); - assertEquals(0, resultSet.getInt("DECIMAL_DIGITS")); - assertEquals(10, resultSet.getInt("NUM_PREC_RADIX")); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(2, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 5: Specific Column (BooleanField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "BooleanField"); - assertTrue(resultSet.next()); - assertEquals("BooleanField", resultSet.getString("COLUMN_NAME")); - assertEquals("BOOLEAN", resultSet.getString("TYPE_NAME")); - assertEquals(1, resultSet.getInt("COLUMN_SIZE")); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 6: Specific Column (NumericField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "NumericField"); - assertTrue(resultSet.next()); - assertEquals("NumericField", resultSet.getString("COLUMN_NAME")); - assertEquals("NUMERIC", resultSet.getString("TYPE_NAME")); - assertEquals(38, resultSet.getInt("COLUMN_SIZE")); - assertEquals(9, resultSet.getInt("DECIMAL_DIGITS")); - assertEquals(10, resultSet.getInt("NUM_PREC_RADIX")); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(4, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 7: Specific Column (BytesField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "BytesField"); - assertTrue(resultSet.next()); - assertEquals("BytesField", resultSet.getString("COLUMN_NAME")); - assertEquals("VARBINARY", resultSet.getString("TYPE_NAME")); - resultSet.getObject("COLUMN_SIZE"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(7, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 8: Specific Column (ArrayField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "ArrayField"); - assertTrue(resultSet.next()); - assertEquals("ArrayField", resultSet.getString("COLUMN_NAME")); - assertEquals("ARRAY", resultSet.getString("TYPE_NAME")); - resultSet.getObject("COLUMN_SIZE"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(9, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 9: Specific Column (TimestampField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "TimestampField"); - assertTrue(resultSet.next()); - assertEquals("TimestampField", resultSet.getString("COLUMN_NAME")); - assertEquals("TIMESTAMP", resultSet.getString("TYPE_NAME")); - assertEquals(29, resultSet.getInt("COLUMN_SIZE")); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(10, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 10: Specific Column (DateField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "DateField"); - assertTrue(resultSet.next()); - assertEquals("DateField", resultSet.getString("COLUMN_NAME")); - assertEquals("DATE", resultSet.getString("TYPE_NAME")); - assertEquals(10, resultSet.getInt("COLUMN_SIZE")); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(11, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 11: Specific Column (TimeField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "TimeField"); - assertTrue(resultSet.next()); - assertEquals("TimeField", resultSet.getString("COLUMN_NAME")); - assertEquals("TIME", resultSet.getString("TYPE_NAME")); - assertEquals(15, resultSet.getInt("COLUMN_SIZE")); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(12, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 12: Specific Column (DateTimeField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "DateTimeField"); - assertTrue(resultSet.next()); - assertEquals("DateTimeField", resultSet.getString("COLUMN_NAME")); - assertEquals("TIMESTAMP", resultSet.getString("TYPE_NAME")); - assertEquals(29, resultSet.getInt("COLUMN_SIZE")); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(13, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - - // --- Test Case 13: Specific Column (GeographyField) --- - resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "GeographyField"); - assertTrue(resultSet.next()); - assertEquals("GeographyField", resultSet.getString("COLUMN_NAME")); - assertEquals("VARCHAR", resultSet.getString("TYPE_NAME")); - resultSet.getObject("COLUMN_SIZE"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("DECIMAL_DIGITS"); - assertTrue(resultSet.wasNull()); - resultSet.getObject("NUM_PREC_RADIX"); - assertTrue(resultSet.wasNull()); - assertEquals(1, resultSet.getInt("NULLABLE")); - assertEquals(14, resultSet.getInt("ORDINAL_POSITION")); - assertFalse(resultSet.next()); - } - - @Test - public void testDatabaseMetadataGetTables() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - String DATASET = "JDBC_TABLE_TYPES_TEST"; - - // --- Test Case 1: Get all tables (types = null) --- - ResultSet rsAll = databaseMetaData.getTables(PROJECT_ID, DATASET, null, null); - Set allTableNames = new HashSet<>(); - while (rsAll.next()) { - allTableNames.add(rsAll.getString("TABLE_NAME")); - } - assertTrue(allTableNames.contains("base_table")); - assertTrue(allTableNames.contains("my_view")); - assertTrue(allTableNames.contains("external_table")); - assertTrue(allTableNames.contains("my_materialized_view")); - assertTrue(allTableNames.contains("base_table_clone")); - assertTrue(allTableNames.contains("base_table_snapshot")); - assertEquals(6, allTableNames.size()); - - // --- Test Case 2: Get only "TABLE" type --- - ResultSet rsTable = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"TABLE"}); - Set tableNames = new HashSet<>(); - while (rsTable.next()) { - tableNames.add(rsTable.getString("TABLE_NAME")); - } - assertTrue(tableNames.contains("base_table")); - assertTrue(tableNames.contains("base_table_clone")); - assertEquals(2, tableNames.size()); - - // --- Test Case 3: Get "VIEW" type --- - ResultSet rsView = databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"VIEW"}); - assertTrue(rsView.next()); - assertEquals("my_view", rsView.getString("TABLE_NAME")); - assertEquals("VIEW", rsView.getString("TABLE_TYPE")); - assertFalse(rsView.next()); - - // --- Test Case 4: Get "EXTERNAL TABLE" type --- - ResultSet rsExternal = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"EXTERNAL"}); - assertTrue(rsExternal.next()); - assertEquals("external_table", rsExternal.getString("TABLE_NAME")); - assertEquals("EXTERNAL", rsExternal.getString("TABLE_TYPE")); - assertFalse(rsExternal.next()); - - // --- Test Case 5: Get "MATERIALIZED_VIEW" type --- - ResultSet rsMaterialized = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"MATERIALIZED_VIEW"}); - assertTrue(rsMaterialized.next()); - assertEquals("my_materialized_view", rsMaterialized.getString("TABLE_NAME")); - assertEquals("MATERIALIZED_VIEW", rsMaterialized.getString("TABLE_TYPE")); - assertFalse(rsMaterialized.next()); - - // --- Test Case 6: Get "SNAPSHOT" type --- - ResultSet rsSnapshot = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"SNAPSHOT"}); - assertTrue(rsSnapshot.next()); - assertEquals("base_table_snapshot", rsSnapshot.getString("TABLE_NAME")); - assertEquals("SNAPSHOT", rsSnapshot.getString("TABLE_TYPE")); - assertFalse(rsSnapshot.next()); - - // --- Test Case 8: Get multiple types ("TABLE" and "VIEW") --- - ResultSet rsMulti = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"TABLE", "VIEW"}); - Set multiTableNames = new HashSet<>(); - while (rsMulti.next()) { - multiTableNames.add(rsMulti.getString("TABLE_NAME")); - } - assertTrue(multiTableNames.contains("base_table")); - assertTrue(multiTableNames.contains("base_table_clone")); - assertTrue(multiTableNames.contains("my_view")); - assertEquals(3, multiTableNames.size()); - - // --- Test Case 9: tableNamePattern --- - ResultSet rsNamePattern = databaseMetaData.getTables(PROJECT_ID, DATASET, "base%", null); - Set baseTableNames = new HashSet<>(); - while (rsNamePattern.next()) { - baseTableNames.add(rsNamePattern.getString("TABLE_NAME")); - } - assertTrue(baseTableNames.contains("base_table")); - assertTrue(baseTableNames.contains("base_table_clone")); - assertTrue(baseTableNames.contains("base_table_snapshot")); - assertEquals(3, baseTableNames.size()); - - // --- Test Case 10: No matching table --- - ResultSet rsNoMatch = - databaseMetaData.getTables(PROJECT_ID, DATASET, "nonexistent_table", null); - assertFalse(rsNoMatch.next()); - - // --- Test Case 11: Null type in array --- - ResultSet rsNullType = - databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {null, "VIEW"}); - assertTrue(rsNullType.next()); - assertEquals("VIEW", rsNullType.getString("TABLE_TYPE")); - assertEquals("my_view", rsNullType.getString("TABLE_NAME")); - assertFalse(rsNullType.next()); - } - - @Test - public void testDatabaseMetadataGetSchemas() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - - // Test case 1: Get all schemas with catalog and check for the presence of specific schemas - ResultSet rsAll = databaseMetaData.getSchemas(PROJECT_ID, null); - Set actualSchemas = new HashSet<>(); - while (rsAll.next()) { - assertEquals(PROJECT_ID, rsAll.getString("TABLE_CATALOG")); - actualSchemas.add(rsAll.getString("TABLE_SCHEM")); - } - assertTrue(actualSchemas.contains("JDBC_INTEGRATION_DATASET")); - assertTrue(actualSchemas.contains("JDBC_TABLE_TYPES_TEST")); - assertTrue(actualSchemas.contains("ODBC_TEST_DATASET")); - - // Test case 2: Get schemas with catalog and schemaPattern matching "JDBC_NIGHTLY_IT_DATASET" - ResultSet rsPattern = databaseMetaData.getSchemas(PROJECT_ID, "JDBC_NIGHTLY_IT_DATASET"); - Set actualSchemasPattern = new HashSet<>(); - while (rsPattern.next()) { - assertEquals(PROJECT_ID, rsPattern.getString("TABLE_CATALOG")); - actualSchemasPattern.add(rsPattern.getString("TABLE_SCHEM")); - } - assertTrue(actualSchemasPattern.contains("JDBC_NIGHTLY_IT_DATASET")); - assertEquals(1, actualSchemasPattern.size()); - - // Test case 3: Get schemas with catalog and schemaPattern matching "nonexistent" - ResultSet rsNoMatch = databaseMetaData.getSchemas(PROJECT_ID, "nonexistent"); - assertFalse(rsNoMatch.next()); - - // Test case 4: Get schemas with non-existent catalog - rsNoMatch = databaseMetaData.getSchemas("invalid-catalog", null); - assertFalse(rsNoMatch.next()); - } - - @Test - public void testDatabaseMetadataGetSchemasNoArgs() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - String expectedCatalog = bigQueryConnection.getCatalog(); - assertNotNull(expectedCatalog, "Project ID (catalog) from connection should not be null"); - - // Test case: Get all schemas (datasets) for the current project - try (ResultSet rsAll = databaseMetaData.getSchemas()) { - assertNotNull(rsAll, "ResultSet from getSchemas() should not be null"); - boolean foundTestDataset = false; - int rowCount = 0; - while (rsAll.next()) { - rowCount++; - assertEquals( - expectedCatalog, - rsAll.getString("TABLE_CATALOG"), - "TABLE_CATALOG should match the connection's project ID"); - String schemaName = rsAll.getString("TABLE_SCHEM"); - assertNotNull(schemaName, "TABLE_SCHEM should not be null"); - if (DATASET.equals(schemaName) - || DATASET2.equals(schemaName) - || CONSTRAINTS_DATASET.equals(schemaName) - || "JDBC_TABLE_TYPES_TEST".equals(schemaName) - || "JDBC_INTEGRATION_DATASET".equals(schemaName)) { - foundTestDataset = true; - } - } - assertTrue(foundTestDataset, "At least one of the known test datasets should be found"); - assertTrue(rowCount > 0, "Should retrieve at least one schema/dataset"); - } - } - - @Test - public void testDatabaseMetaDataGetFunctions() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - String testSchema = "JDBC_TABLE_TYPES_TEST"; - String testCatalog = PROJECT_ID; - - Set expectedFunctionNames = - new HashSet<>( - Arrays.asList( - "complex_scalar_sql_udf", - "persistent_sql_udf_named_params", - "scalar_js_udf", - "scalar_sql_udf")); - - // Test 1: Get all functions from a specific schema - ResultSet rsAll = databaseMetaData.getFunctions(testCatalog, testSchema, null); - Set foundFunctionNames = new HashSet<>(); - int countAll = 0; - while (rsAll.next()) { - countAll++; - assertEquals(testCatalog, rsAll.getString("FUNCTION_CAT")); - assertEquals(testSchema, rsAll.getString("FUNCTION_SCHEM")); - String funcName = rsAll.getString("FUNCTION_NAME"); - foundFunctionNames.add(funcName); - assertNull(rsAll.getString("REMARKS")); - assertEquals(DatabaseMetaData.functionResultUnknown, rsAll.getShort("FUNCTION_TYPE")); - assertEquals(funcName, rsAll.getString("SPECIFIC_NAME")); - } - assertEquals( - expectedFunctionNames.size(), - countAll, - "Should find all " + expectedFunctionNames.size() + " functions in " + testSchema); - assertEquals(expectedFunctionNames, foundFunctionNames); - rsAll.close(); - - // Test 2: Get a specific function using functionNamePattern - String specificFunctionName = "scalar_sql_udf"; - ResultSet rsSpecific = - databaseMetaData.getFunctions(testCatalog, testSchema, specificFunctionName); - assertTrue(rsSpecific.next(), "Should find the specific function " + specificFunctionName); - assertEquals(testCatalog, rsSpecific.getString("FUNCTION_CAT")); - assertEquals(testSchema, rsSpecific.getString("FUNCTION_SCHEM")); - assertEquals(specificFunctionName, rsSpecific.getString("FUNCTION_NAME")); - assertNull(rsSpecific.getString("REMARKS")); - assertEquals(DatabaseMetaData.functionResultUnknown, rsSpecific.getShort("FUNCTION_TYPE")); - assertEquals(specificFunctionName, rsSpecific.getString("SPECIFIC_NAME")); - assertFalse(rsSpecific.next(), "Should only find one row for exact function match"); - rsSpecific.close(); - - // Test 3: Get functions using a wildcard functionNamePattern "scalar%" - // Expected order due to sorting: scalar_js_udf, scalar_sql_udf - ResultSet rsWildcard = databaseMetaData.getFunctions(testCatalog, testSchema, "scalar%"); - assertTrue(rsWildcard.next(), "Should find functions matching 'scalar%'"); - assertEquals("scalar_js_udf", rsWildcard.getString("FUNCTION_NAME")); - assertEquals(DatabaseMetaData.functionResultUnknown, rsWildcard.getShort("FUNCTION_TYPE")); - - assertTrue(rsWildcard.next(), "Should find the second function matching 'scalar%'"); - assertEquals("scalar_sql_udf", rsWildcard.getString("FUNCTION_NAME")); - assertEquals(DatabaseMetaData.functionResultUnknown, rsWildcard.getShort("FUNCTION_TYPE")); - assertFalse(rsWildcard.next(), "Should be no more functions matching 'scalar%'"); - rsWildcard.close(); - - // Test 4: Schema pattern with wildcard - ResultSet rsSchemaWildcard = - databaseMetaData.getFunctions(testCatalog, "JDBC_TABLE_TYPES_T%", "complex_scalar_sql_udf"); - assertTrue(rsSchemaWildcard.next(), "Should find function with schema wildcard"); - assertEquals(testSchema, rsSchemaWildcard.getString("FUNCTION_SCHEM")); - assertEquals("complex_scalar_sql_udf", rsSchemaWildcard.getString("FUNCTION_NAME")); - assertFalse( - rsSchemaWildcard.next(), - "Should only find one row for this schema wildcard and specific function"); - rsSchemaWildcard.close(); - - // Test 5: Non-existent function - ResultSet rsNonExistentFunc = - databaseMetaData.getFunctions(testCatalog, testSchema, "non_existent_function_xyz123"); - assertFalse(rsNonExistentFunc.next(), "Should not find a non-existent function"); - rsNonExistentFunc.close(); - - // Test 6: Non-existent schema - ResultSet rsNonExistentSchema = - databaseMetaData.getFunctions(testCatalog, "NON_EXISTENT_SCHEMA_XYZ123", null); - assertFalse(rsNonExistentSchema.next(), "Should not find functions in a non-existent schema"); - rsNonExistentSchema.close(); - - // Test 7: Empty schema pattern - ResultSet rsEmptySchema = databaseMetaData.getFunctions(testCatalog, "", null); - assertFalse(rsEmptySchema.next(), "Empty schema pattern should return no results"); - rsEmptySchema.close(); - - // Test 8: Empty function name pattern - ResultSet rsEmptyFunction = databaseMetaData.getFunctions(testCatalog, testSchema, ""); - assertFalse(rsEmptyFunction.next(), "Empty function name pattern should return no results"); - rsEmptyFunction.close(); - - // Test 9: Null catalog - ResultSet rsNullCatalog = databaseMetaData.getFunctions(null, testSchema, null); - assertFalse(rsNullCatalog.next(), "Null catalog should return no results"); - rsNullCatalog.close(); - } - - @Test - public void testDatabaseMetadataGetFunctionColumns() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - String testCatalog = PROJECT_ID; - String testSchema = "JDBC_TABLE_TYPES_TEST"; - - // Test Case 1: Specific function 'scalar_sql_udf', specific column 'x' - String specificFunction1 = "scalar_sql_udf"; - String specificColumn1 = "x"; - ResultSet rs = - databaseMetaData.getFunctionColumns( - testCatalog, testSchema, specificFunction1, specificColumn1); - - assertTrue(rs.next(), "Should find column 'x' for function 'scalar_sql_udf'"); - assertEquals(testCatalog, rs.getString("FUNCTION_CAT")); - assertEquals(testSchema, rs.getString("FUNCTION_SCHEM")); - assertEquals(specificFunction1, rs.getString("FUNCTION_NAME")); - assertEquals(specificColumn1, rs.getString("COLUMN_NAME")); - assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); - assertEquals(Types.BIGINT, rs.getInt("DATA_TYPE")); - assertEquals("BIGINT", rs.getString("TYPE_NAME")); - assertEquals(19, rs.getInt("PRECISION")); - assertEquals(null, rs.getObject("LENGTH")); - assertTrue(rs.wasNull()); - assertEquals(0, rs.getShort("SCALE")); - assertEquals(10, rs.getShort("RADIX")); - assertEquals(DatabaseMetaData.functionNullableUnknown, rs.getShort("NULLABLE")); - assertNull(rs.getString("REMARKS")); - assertEquals(null, rs.getObject("CHAR_OCTET_LENGTH")); - assertTrue(rs.wasNull()); - assertEquals(1, rs.getInt("ORDINAL_POSITION")); - assertEquals("", rs.getString("IS_NULLABLE")); - assertEquals(specificFunction1, rs.getString("SPECIFIC_NAME")); - assertFalse(rs.next(), "Should only find one row for exact column match"); - rs.close(); - - // Test Case 2: Specific function 'complex_scalar_sql_udf', specific column 'arr' - String specificFunction2 = "complex_scalar_sql_udf"; - String specificColumn2 = "arr"; - rs = - databaseMetaData.getFunctionColumns( - testCatalog, testSchema, specificFunction2, specificColumn2); - assertTrue(rs.next(), "Should find column 'arr' for function 'complex_scalar_sql_udf'"); - assertEquals(testCatalog, rs.getString("FUNCTION_CAT")); - assertEquals(testSchema, rs.getString("FUNCTION_SCHEM")); - assertEquals(specificFunction2, rs.getString("FUNCTION_NAME")); - assertEquals(specificColumn2, rs.getString("COLUMN_NAME")); - assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); - assertEquals(Types.ARRAY, rs.getInt("DATA_TYPE")); - assertEquals("ARRAY", rs.getString("TYPE_NAME")); - assertEquals(null, rs.getObject("PRECISION")); - assertTrue(rs.wasNull()); - assertEquals(null, rs.getObject("LENGTH")); - assertTrue(rs.wasNull()); - assertEquals(null, rs.getObject("SCALE")); - assertTrue(rs.wasNull()); - assertEquals(null, rs.getObject("RADIX")); - assertTrue(rs.wasNull()); - assertEquals(DatabaseMetaData.functionNullableUnknown, rs.getShort("NULLABLE")); - assertNull(rs.getString("REMARKS")); - assertEquals(null, rs.getObject("CHAR_OCTET_LENGTH")); - assertTrue(rs.wasNull()); - assertEquals(1, rs.getInt("ORDINAL_POSITION")); - assertEquals("", rs.getString("IS_NULLABLE")); - assertEquals(specificFunction2, rs.getString("SPECIFIC_NAME")); - assertFalse(rs.next(), "Should only find one row for exact column match"); - rs.close(); - - // Test Case 3: All columns for 'persistent_sql_udf_named_params' (sorted by ordinal position) - String specificFunction3 = "persistent_sql_udf_named_params"; - rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, specificFunction3, null); - assertTrue(rs.next(), "Should find columns for " + specificFunction3); - assertEquals(specificFunction3, rs.getString("FUNCTION_NAME")); - assertEquals("value1", rs.getString("COLUMN_NAME")); // Ordinal Position 1 - assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); - assertEquals(Types.BIGINT, rs.getInt("DATA_TYPE")); - assertEquals("BIGINT", rs.getString("TYPE_NAME")); - assertEquals(1, rs.getInt("ORDINAL_POSITION")); - - assertTrue(rs.next(), "Should find second column for " + specificFunction3); - assertEquals(specificFunction3, rs.getString("FUNCTION_NAME")); - assertEquals("value-two", rs.getString("COLUMN_NAME")); // Ordinal Position 2 - assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); - assertEquals(Types.NVARCHAR, rs.getInt("DATA_TYPE")); - assertEquals("NVARCHAR", rs.getString("TYPE_NAME")); - assertEquals(2, rs.getInt("ORDINAL_POSITION")); - assertFalse(rs.next(), "Should be no more columns for " + specificFunction3); - rs.close(); - - // Test Case 4: Wildcard for function name "scalar%", specific column name "x" - rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, "scalar%", "x"); - assertTrue(rs.next(), "Should find column 'x' for functions matching 'scalar%'"); - assertEquals("scalar_sql_udf", rs.getString("FUNCTION_NAME")); - assertEquals("x", rs.getString("COLUMN_NAME")); - assertEquals(1, rs.getInt("ORDINAL_POSITION")); - assertFalse(rs.next(), "Should be no more columns named 'x' for functions matching 'scalar%'"); - rs.close(); - - // Test Case 5: Wildcard for column name "%" for 'scalar_js_udf' - String specificFunction4 = "scalar_js_udf"; - rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, specificFunction4, "%"); - assertTrue(rs.next(), "Should find columns for " + specificFunction4 + " with wildcard"); - assertEquals(specificFunction4, rs.getString("FUNCTION_NAME")); - assertEquals("name", rs.getString("COLUMN_NAME")); // Ordinal Position 1 - assertEquals(1, rs.getInt("ORDINAL_POSITION")); - - assertTrue(rs.next(), "Should find second column for " + specificFunction4 + " with wildcard"); - assertEquals(specificFunction4, rs.getString("FUNCTION_NAME")); - assertEquals("age", rs.getString("COLUMN_NAME")); // Ordinal Position 2 - assertEquals(2, rs.getInt("ORDINAL_POSITION")); - assertFalse(rs.next(), "Should be no more columns for " + specificFunction4 + " with wildcard"); - rs.close(); - - // Test Case 6: Non-existent function - rs = - databaseMetaData.getFunctionColumns( - testCatalog, testSchema, "non_existent_function_xyz", null); - assertFalse(rs.next(), "Should not find columns for a non-existent function"); - rs.close(); - } - @Test public void testRangeDataTypeWithJsonResultSet() throws SQLException { String RANGE_DATA_TABLE = "JDBC_RANGE_DATA_TEST_TABLE_" + random.nextInt(99); @@ -2276,910 +2025,6 @@ public void testRangeDataTypeWithArrowResultSet() throws SQLException { connection.close(); } - @Test - public void testPrepareCallSql() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc"); - assertNotNull(callableStatement); - callableStatement.close(); - } - - @Test - public void testRegisterOutParamIndex() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - callableStatement.registerOutParameter(1, Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testRegisterOutParamName() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testRegisterOutParamIndexScale() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - callableStatement.registerOutParameter(1, Types.NUMERIC, 2); - callableStatement.close(); - } - - @Test - public void testRegisterOutParamNameScale() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); - callableStatement.close(); - } - - @Test - public void testPrepareCallSqlResultSetTypeConcurrency() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - assertNotNull(callableStatement); - callableStatement.close(); - } - - @Test - public void testPrepareCallConcurrencyRegisterOutParamIndex() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - assertNotNull(callableStatement); - callableStatement.registerOutParameter(1, Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testPrepareCallConcurrencyRegisterOutParamName() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testPrepareCallConcurrencyRegisterOutParamIndexScale() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - assertNotNull(callableStatement); - callableStatement.registerOutParameter(1, Types.NUMERIC, 2); - callableStatement.close(); - } - - @Test - public void testPrepareCallConcurrencyRegisterOutParamNameScale() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); - callableStatement.close(); - } - - @Test - public void testPrepareCallSqlResultSetTypeConcurrencyHoldability() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertNotNull(callableStatement); - callableStatement.close(); - } - - @Test - public void testPrepareCallHoldabilityRegisterOutParamIndex() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertNotNull(callableStatement); - callableStatement.registerOutParameter(1, Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testPrepareCallHoldabilityRegisterOutParamName() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); - callableStatement.close(); - } - - @Test - public void testPrepareCallHoldabilityRegisterOutParamIndexScale() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertNotNull(callableStatement); - callableStatement.close(); - } - - @Test - public void testPrepareCallHoldabilityRegisterOutParamNameScale() throws SQLException { - CallableStatement callableStatement = - this.bigQueryConnection.prepareCall( - "call testProc('?')", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertNotNull(callableStatement); - callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); - callableStatement.close(); - } - - @Test - public void testPrepareCallFailureResultSetType() throws SQLException { - assertThrows( - BigQueryJdbcSqlFeatureNotSupportedException.class, - () -> - this.bigQueryConnection.prepareCall( - "call testProc", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); - } - - @Test - public void testPrepareCallFailureResultSetConcurrency() throws SQLException { - assertThrows( - BigQueryJdbcSqlFeatureNotSupportedException.class, - () -> - this.bigQueryConnection.prepareCall( - "call testProc", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); - } - - @Test - public void testPrepareCallFailureResultSetHoldability() throws SQLException { - assertThrows( - BigQueryJdbcSqlFeatureNotSupportedException.class, - () -> - this.bigQueryConnection.prepareCall( - "call testProc", - ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, - ResultSet.HOLD_CURSORS_OVER_COMMIT)); - } - - // Integration tests for CallableStatement Setters and Getters - @Test - public void testSetterGetterBigDecimal() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - BigDecimal expected = new BigDecimal(12344); - callableStatement.setBigDecimal(CALLABLE_STMT_PARAM_KEY, expected); - BigDecimal actual = callableStatement.getBigDecimal(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterBoolean() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Boolean expected = true; - callableStatement.setBoolean(CALLABLE_STMT_PARAM_KEY, expected); - Boolean actual = callableStatement.getBoolean(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterByte() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Byte expected = "hello".getBytes()[0]; - callableStatement.setByte(CALLABLE_STMT_PARAM_KEY, expected); - Byte actual = callableStatement.getByte(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterBytes() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - byte[] expected = "hello".getBytes(); - callableStatement.setBytes(CALLABLE_STMT_PARAM_KEY, expected); - byte[] actual = callableStatement.getBytes(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterDate() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Date expected = new Date(1234567); - callableStatement.setDate(CALLABLE_STMT_PARAM_KEY, expected); - Date actual = callableStatement.getDate(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterDateCal() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Date expected = new Date(1L); - Calendar cal = Calendar.getInstance(); - callableStatement.setDate(CALLABLE_STMT_PARAM_KEY, expected, cal); - Date actual = callableStatement.getDate(CALLABLE_STMT_PARAM_KEY, cal); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterDouble() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Double expected = 123.2345; - callableStatement.setDouble(CALLABLE_STMT_PARAM_KEY, expected); - Double actual = callableStatement.getDouble(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterFloat() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Float expected = 123.2345F; - callableStatement.setFloat(CALLABLE_STMT_PARAM_KEY, expected); - Float actual = callableStatement.getFloat(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterInt() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Integer expected = 123; - callableStatement.setInt(CALLABLE_STMT_PARAM_KEY, expected); - Integer actual = callableStatement.getInt(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterLong() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Long expected = 123L; - callableStatement.setLong(CALLABLE_STMT_PARAM_KEY, expected); - Long actual = callableStatement.getLong(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterNString() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - String expected = "heelo"; - callableStatement.setNString(CALLABLE_STMT_PARAM_KEY, expected); - String actual = callableStatement.getNString(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterObject() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - String expected = "heelo"; - callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected); - Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterObjectWithSQLType() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - String expected = "heelo"; - callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected, Types.NVARCHAR); - Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterObjectWithSqlTypeAndScale() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - String expected = "heelo"; - callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected, Types.NVARCHAR, 0); - Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterString() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - String expected = "123"; - callableStatement.setString(CALLABLE_STMT_PARAM_KEY, expected); - String actual = callableStatement.getString(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterTime() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Time expected = new Time(1234567); - callableStatement.setTime(CALLABLE_STMT_PARAM_KEY, expected); - Time actual = callableStatement.getTime(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterTimeCal() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Time expected = new Time(1L); - Calendar cal = Calendar.getInstance(); - callableStatement.setTime(CALLABLE_STMT_PARAM_KEY, expected, cal); - Time actual = callableStatement.getTime(CALLABLE_STMT_PARAM_KEY, cal); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterTimestamp() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Timestamp expected = new Timestamp(1234567); - callableStatement.setTimestamp(CALLABLE_STMT_PARAM_KEY, expected); - Timestamp actual = callableStatement.getTimestamp(CALLABLE_STMT_PARAM_KEY); - assertEquals(expected, actual); - } - - @Test - public void testSetterGetterTimestampCal() throws SQLException { - CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); - assertNotNull(callableStatement); - Timestamp expected = new Timestamp(1L); - Calendar cal = Calendar.getInstance(); - callableStatement.setTimestamp(CALLABLE_STMT_PARAM_KEY, expected, cal); - Timestamp actual = callableStatement.getTimestamp(CALLABLE_STMT_PARAM_KEY, cal); - assertEquals(expected, actual); - } - - @Test - public void testPooledConnectionDataSourceSuccess() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - } - - @Test - public void testPooledConnectionDataSourceFailNoConnectionURl() throws SQLException { - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - - assertThrows(BigQueryJdbcException.class, () -> pooledDataSource.getPooledConnection()); - } - - @Test - public void testPooledConnectionDataSourceFailInvalidConnectionURl() { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;" - + "ListenerPoolSize=invalid"; - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - assertThrows(NumberFormatException.class, () -> pooledDataSource.getPooledConnection()); - } - - @Test - public void testPooledConnectionAddConnectionListener() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - TestConnectionListener listener = new TestConnectionListener(); - pooledConnection.addConnectionEventListener(listener); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - } - - @Test - public void testPooledConnectionRemoveConnectionListener() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - TestConnectionListener listener = new TestConnectionListener(); - pooledConnection.removeConnectionEventListener(listener); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - } - - @Test - public void testPooledConnectionConnectionClosed() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - TestConnectionListener listener = new TestConnectionListener(); - pooledConnection.addConnectionEventListener(listener); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - - Connection connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - - connection.close(); - assertEquals(1, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - } - - @Test - public void testPooledConnectionClose() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - TestConnectionListener listener = new TestConnectionListener(); - pooledConnection.addConnectionEventListener(listener); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - - pooledConnection.close(); - assertEquals(1, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - } - - @Test - public void testPooledConnectionConnectionError() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - TestConnectionListener listener = new TestConnectionListener(); - pooledConnection.addConnectionEventListener(listener); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(0, listener.getConnectionErrorCount()); - - Connection connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - - ExecutorService executor = Executors.newFixedThreadPool(3); - connection.abort(executor); - assertEquals(0, listener.getConnectionClosedCount()); - assertEquals(1, listener.getConnectionErrorCount()); - - executor.shutdown(); - connection.close(); - pooledConnection.close(); - } - - @Test - public void testPooledConnectionListenerAddListener() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); - pooledConnection.addConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - pooledConnection.close(); - } - - @Test - public void testPooledConnectionListenerRemoveListener() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); - pooledConnection.addConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - - pooledConnection.removeConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - pooledConnection.close(); - } - - @Test - public void testPooledConnectionListenerCloseConnection() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); - pooledConnection.addConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - - Connection connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - - connection.close(); - assertFalse(listener.isConnectionPoolEmpty()); - pooledConnection.close(); - } - - @Test - public void testPooledConnectionListenerClosePooledConnection() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); - pooledConnection.addConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - - pooledConnection.close(); - assertFalse(listener.isConnectionPoolEmpty()); - } - - @Test - public void testPooledConnectionListenerConnectionError() throws SQLException { - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; - - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionUrl); - - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); - pooledConnection.addConnectionEventListener(listener); - assertTrue(listener.isConnectionPoolEmpty()); - - Connection connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - - ExecutorService executor = Executors.newFixedThreadPool(3); - connection.abort(executor); - assertTrue(listener.isConnectionPoolEmpty()); - - executor.shutdown(); - connection.close(); - pooledConnection.close(); - } - - @Test - public void testExecuteQueryWithConnectionPoolingEnabledDefaultPoolSize() throws SQLException { - String connectionURL = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" - + "OAuthType=3;ProjectId=" - + PROJECT_ID - + ";"; - assertConnectionPoolingResults(connectionURL, DEFAULT_CONN_POOL_SIZE); - } - - @Test - public void testExecuteQueryWithConnectionPoolingEnabledCustomPoolSize() throws SQLException { - String connectionURL = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" - + "OAuthType=3;ProjectId=" - + PROJECT_ID - + ";" - + "ConnectionPoolSize=" - + CUSTOM_CONN_POOL_SIZE - + ";"; - assertConnectionPoolingResults(connectionURL, CUSTOM_CONN_POOL_SIZE); - } - - private void assertConnectionPoolingResults(String connectionURL, Long connectionPoolSize) - throws SQLException { - // Create Pooled Connection Datasource - PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); - pooledDataSource.setURL(connectionURL); - - // Get pooled connection and ensure listner was added with default connection pool size. - PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); - assertNotNull(pooledConnection); - PooledConnectionListener listener = pooledDataSource.getConnectionPoolManager(); - assertNotNull(listener); - assertTrue(listener.isConnectionPoolEmpty()); - - // Get Underlying physical connection - Connection connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - - // Execute query with physical connection - String query = - "SELECT DISTINCT repository_name FROM `bigquery-public-data.samples.github_timeline` LIMIT" - + " 850"; - Statement statement = connection.createStatement(); - ResultSet jsonResultSet = statement.executeQuery(query); - assertTrue(jsonResultSet.getClass().getName().contains("BigQueryJsonResultSet")); - - // Close physical connection - connection.close(); - assertFalse(listener.isConnectionPoolEmpty()); - assertEquals(1, listener.getConnectionPoolCurrentCapacity()); - assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); - - // Reuse same physical connection. - connection = pooledConnection.getConnection(); - assertNotNull(connection); - assertFalse(connection.isClosed()); - assertFalse(listener.isConnectionPoolEmpty()); - assertEquals(1, listener.getConnectionPoolCurrentCapacity()); - assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); - - // Execute query with reusable physical connection - jsonResultSet = statement.executeQuery(query); - assertTrue(jsonResultSet.getClass().getName().contains("BigQueryJsonResultSet")); - - // Return connection back to the pool. - connection.close(); - assertFalse(listener.isConnectionPoolEmpty()); - assertEquals(1, listener.getConnectionPoolCurrentCapacity()); - assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); - pooledConnection.close(); - } - - @Test - public void testAdditionalProjectsInMetadata() throws SQLException { - String additionalProjectsValue = "bigquery-public-data"; - String datasetInAdditionalProject = "baseball"; - - String urlWithAdditionalProjects = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" - + PROJECT_ID - + ";OAuthType=3" - + ";AdditionalProjects=" - + additionalProjectsValue; - - try (Connection conn = DriverManager.getConnection(urlWithAdditionalProjects)) { - DatabaseMetaData dbMetaData = conn.getMetaData(); - - // 1. Test getCatalogs() - Set foundCatalogs = new HashSet<>(); - try (ResultSet catalogsRs = dbMetaData.getCatalogs()) { - while (catalogsRs.next()) { - foundCatalogs.add(catalogsRs.getString("TABLE_CAT")); - } - } - assertTrue( - foundCatalogs.contains(PROJECT_ID), - "getCatalogs() should contain the primary project ID"); - assertTrue( - foundCatalogs.contains(additionalProjectsValue), - "getCatalogs() should contain the additional project ID"); - - // 2. Test getSchemas() - Set catalogsForSchemasFromAll = new HashSet<>(); - boolean foundAdditionalDataset = false; - try (ResultSet schemasRs = dbMetaData.getSchemas()) { - while (schemasRs.next()) { - String schemaName = schemasRs.getString("TABLE_SCHEM"); - String catalogName = schemasRs.getString("TABLE_CATALOG"); - catalogsForSchemasFromAll.add(catalogName); - if (additionalProjectsValue.equals(catalogName) - && datasetInAdditionalProject.equals(schemaName)) { - foundAdditionalDataset = true; - } - } - } - assertTrue( - catalogsForSchemasFromAll.contains(PROJECT_ID), - "getSchemas() should list datasets from the primary project"); - assertTrue( - catalogsForSchemasFromAll.contains(additionalProjectsValue), - "getSchemas() should list datasets from the additional project"); - assertTrue( - foundAdditionalDataset, - "Known dataset from additional project not found in getSchemas()"); - - } catch (SQLException e) { - System.err.println("SQL Error during AdditionalProjects test: " + e.getMessage()); - throw e; - } - } - - @Test - public void testFilterTablesOnDefaultDataset_getTables() throws SQLException { - String defaultDatasetValue = CONSTRAINTS_DATASET; - String table1InDefaultDataset = CONSTRAINTS_TABLE_NAME; - String table2InDefaultDataset = CONSTRAINTS_TABLE_NAME2; - - String specificDatasetValue = "JDBC_TABLE_TYPES_TEST"; - String table1InSpecificDataset = "base_table"; - String table2InSpecificDataset = "external_table"; - - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" - + PROJECT_ID - + ";OAuthType=3" - + ";DefaultDataset=" - + defaultDatasetValue - + ";FilterTablesOnDefaultDataset=1"; - try (Connection conn = DriverManager.getConnection(connectionUrl)) { - DatabaseMetaData dbMetaData = conn.getMetaData(); - - // Case 1: Catalog and schemaPattern are null/wildcard, should use DefaultDataset - try (ResultSet rs = dbMetaData.getTables(null, null, null, null)) { - Set tableNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); - tableNames.add(rs.getString("TABLE_NAME")); - } - assertTrue(tableNames.contains(table1InDefaultDataset)); - assertTrue(tableNames.contains(table2InDefaultDataset)); - } - - // Case 2: Explicit schemaPattern overrides DefaultDataset - try (ResultSet rs = dbMetaData.getTables(null, specificDatasetValue, null, null)) { - Set tableNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); - tableNames.add(rs.getString("TABLE_NAME")); - } - assertTrue(tableNames.contains(table1InSpecificDataset)); - assertTrue(tableNames.contains(table2InSpecificDataset)); - } - - // Case 3: Explicit catalog, schemaPattern is null/wildcard, should use DefaultDataset within - // that catalog - try (ResultSet rs = dbMetaData.getTables(PROJECT_ID, null, null, null)) { - Set tableNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); - tableNames.add(rs.getString("TABLE_NAME")); - } - assertTrue(tableNames.contains(table1InDefaultDataset)); - assertTrue(tableNames.contains(table2InDefaultDataset)); - } - - // Case 4: Explicit catalog and schemaPattern override DefaultDataset - try (ResultSet rs = dbMetaData.getTables(PROJECT_ID, specificDatasetValue, null, null)) { - Set tableNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); - tableNames.add(rs.getString("TABLE_NAME")); - } - assertTrue(tableNames.contains(table1InSpecificDataset)); - assertTrue(tableNames.contains(table2InSpecificDataset)); - } - } - } - - @Test - public void testFilterTablesOnDefaultDataset_getColumns() throws SQLException { - String defaultDatasetValue = CONSTRAINTS_DATASET; - String tableInDefaultDataset = CONSTRAINTS_TABLE_NAME; - String[] columnsInDefaultTable = {"id", "name", "second_name", "address"}; - - String specificDatasetValue = "JDBC_TABLE_TYPES_TEST"; - String tableInSpecificDataset = "base_table"; - String[] columnsInSpecificTable = {"id", "name", "created_at"}; - - String connectionUrl = - "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" - + PROJECT_ID - + ";OAuthType=3" - + ";DefaultDataset=" - + defaultDatasetValue - + ";FilterTablesOnDefaultDataset=1"; - - try (Connection conn = DriverManager.getConnection(connectionUrl)) { - DatabaseMetaData dbMetaData = conn.getMetaData(); - - // Case 1: Catalog and schemaPattern are null/wildcard, should use DefaultDataset - try (ResultSet rs = dbMetaData.getColumns(null, null, tableInDefaultDataset, null)) { - Set columnNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); - assertEquals(tableInDefaultDataset, rs.getString("TABLE_NAME")); - columnNames.add(rs.getString("COLUMN_NAME")); - } - for (String expectedCol : columnsInDefaultTable) { - assertTrue(columnNames.contains(expectedCol)); - } - assertEquals(columnsInDefaultTable.length, columnNames.size()); - } - - // Case 2: Explicit schemaPattern overrides DefaultDataset - try (ResultSet rs = - dbMetaData.getColumns(null, specificDatasetValue, tableInSpecificDataset, null)) { - Set columnNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); - assertEquals(tableInSpecificDataset, rs.getString("TABLE_NAME")); - columnNames.add(rs.getString("COLUMN_NAME")); - } - for (String expectedCol : columnsInSpecificTable) { - assertTrue(columnNames.contains(expectedCol)); - } - assertEquals(columnsInSpecificTable.length, columnNames.size()); - } - - // Case 3: Explicit catalog, schemaPattern is null/wildcard, should use DefaultDataset within - // that catalog - try (ResultSet rs = dbMetaData.getColumns(PROJECT_ID, null, tableInDefaultDataset, null)) { - Set columnNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); - assertEquals(tableInDefaultDataset, rs.getString("TABLE_NAME")); - columnNames.add(rs.getString("COLUMN_NAME")); - } - for (String expectedCol : columnsInDefaultTable) { - assertTrue(columnNames.contains(expectedCol)); - } - assertEquals(columnsInDefaultTable.length, columnNames.size()); - } - - // Case 4: Explicit catalog and schemaPattern override DefaultDataset - try (ResultSet rs = - dbMetaData.getColumns(PROJECT_ID, specificDatasetValue, tableInSpecificDataset, null)) { - Set columnNames = new HashSet<>(); - while (rs.next()) { - assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); - assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); - assertEquals(tableInSpecificDataset, rs.getString("TABLE_NAME")); - columnNames.add(rs.getString("COLUMN_NAME")); - } - for (String expectedCol : columnsInSpecificTable) { - assertTrue(columnNames.contains(expectedCol)); - } - assertEquals(columnsInSpecificTable.length, columnNames.size()); - } - } - } - @Test public void testAlterTable() throws SQLException { String TABLE_NAME = "JDBC_ALTER_TABLE_" + randomNumber; @@ -3495,211 +2340,6 @@ private String getSessionId() throws InterruptedException { return stubJob.getStatistics().getSessionInfo().getSessionId(); } - @Test - public void testCallableStatementScriptExecuteUpdate() throws SQLException { - int randomNum = java.util.UUID.randomUUID().hashCode(); - String insertName = "callable-statement-dml-insert-test"; - String insertResult = String.format("%s-%d", insertName, randomNum); - String updateName = "callable-statement-dml-update-test"; - String updateResult = String.format("%s-%d", updateName, randomNum); - String selectStmtQuery = - String.format("SELECT * FROM %s.%s WHERE id = ?", DATASET, CALLABLE_STMT_DML_TABLE_NAME); - String insertCallStmtQuery = - String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_INSERT_PROC_NAME); - String updateCallStmtQuery = - String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_UPDATE_PROC_NAME); - String deleteCallStmtQuery = - String.format("CALL %s.%s(?);", DATASET, CALLABLE_STMT_DML_DELETE_PROC_NAME); - - // DML INSERT - CallableStatement callableStatement = bigQueryConnection.prepareCall(insertCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setString(1, insertName); - callableStatement.setInt(2, randomNum); - callableStatement.setString(3, insertResult); - int rowsInserted = callableStatement.executeUpdate(); - assertEquals(1, rowsInserted); - - PreparedStatement preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - ResultSet rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertTrue(rs.next()); - - assertEquals(insertName, rs.getString(1)); - assertEquals(randomNum, rs.getInt(2)); - assertEquals(insertResult, rs.getString(3)); - - // DML UPDATE - callableStatement = bigQueryConnection.prepareCall(updateCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setString(1, updateName); - callableStatement.setInt(2, randomNum); - callableStatement.setString(3, updateResult); - int rowsUpdated = callableStatement.executeUpdate(); - assertEquals(1, rowsUpdated); - - preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertTrue(rs.next()); - - assertEquals(updateName, rs.getString(1)); - assertEquals(randomNum, rs.getInt(2)); - assertEquals(updateResult, rs.getString(3)); - - // DML DELETE - callableStatement = bigQueryConnection.prepareCall(deleteCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setInt(1, randomNum); - int rowsDeleted = callableStatement.executeUpdate(); - assertEquals(1, rowsDeleted); - - preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertFalse(rs.next()); - - callableStatement.close(); - } - - @Test - public void testCallableStatementScriptExecuteLargeUpdate() throws SQLException { - int randomNum = java.util.UUID.randomUUID().hashCode(); - String insertName = "callable-statement-dml-insert-test"; - String insertResult = String.format("%s-%d", insertName, randomNum); - String updateName = "callable-statement-dml-update-test"; - String updateResult = String.format("%s-%d", updateName, randomNum); - String selectStmtQuery = - String.format("SELECT * FROM %s.%s WHERE id = ?", DATASET, CALLABLE_STMT_DML_TABLE_NAME); - String insertCallStmtQuery = - String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_INSERT_PROC_NAME); - String updateCallStmtQuery = - String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_UPDATE_PROC_NAME); - String deleteCallStmtQuery = - String.format("CALL %s.%s(?);", DATASET, CALLABLE_STMT_DML_DELETE_PROC_NAME); - - // DML INSERT - CallableStatement callableStatement = bigQueryConnection.prepareCall(insertCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setString(1, insertName); - callableStatement.setInt(2, randomNum); - callableStatement.setString(3, insertResult); - long rowsInserted = callableStatement.executeLargeUpdate(); - assertEquals(1L, rowsInserted); - - PreparedStatement preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - ResultSet rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertTrue(rs.next()); - - assertEquals(insertName, rs.getString(1)); - assertEquals(randomNum, rs.getInt(2)); - assertEquals(insertResult, rs.getString(3)); - - // DML UPDATE - callableStatement = bigQueryConnection.prepareCall(updateCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setString(1, updateName); - callableStatement.setInt(2, randomNum); - callableStatement.setString(3, updateResult); - long rowsUpdated = callableStatement.executeLargeUpdate(); - assertEquals(1L, rowsUpdated); - - preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertTrue(rs.next()); - - assertEquals(updateName, rs.getString(1)); - assertEquals(randomNum, rs.getInt(2)); - assertEquals(updateResult, rs.getString(3)); - - // DML DELETE - callableStatement = bigQueryConnection.prepareCall(deleteCallStmtQuery); - assertNotNull(callableStatement); - callableStatement.setInt(1, randomNum); - long rowsDeleted = callableStatement.executeLargeUpdate(); - assertEquals(1L, rowsDeleted); - - preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); - assertNotNull(preparedStatement); - preparedStatement.setInt(1, randomNum); - rs = preparedStatement.executeQuery(); - assertNotNull(rs); - assertFalse(rs.next()); - - callableStatement.close(); - } - - @Test - public void testScript() throws SQLException { - String BASE_QUERY = - "SELECT * FROM bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2017 order by" - + " trip_distance asc LIMIT %s;"; - String query1 = String.format(BASE_QUERY, 5000); - String query2 = String.format(BASE_QUERY, 7000); - String query3 = String.format(BASE_QUERY, 9000); - - bigQueryStatement.execute(query1 + query2 + query3); - ResultSet resultSet = bigQueryStatement.getResultSet(); - assertEquals(5000, resultSetRowCount(resultSet)); - - boolean hasMoreResult = bigQueryStatement.getMoreResults(); - assertTrue(hasMoreResult); - resultSet = bigQueryStatement.getResultSet(); - assertEquals(7000, resultSetRowCount(resultSet)); - - hasMoreResult = bigQueryStatement.getMoreResults(); - assertTrue(hasMoreResult); - resultSet = bigQueryStatement.getResultSet(); - assertEquals(9000, resultSetRowCount(resultSet)); - } - - @Test - public void testCallableStatementScriptExecute() throws SQLException { - int randomNum = random.nextInt(99); - String callableStmtQuery = - String.format( - "DECLARE call_result STRING;" - + "CALL %s.%s(?,?,call_result);" - + "SELECT * FROM %s.%s WHERE result = call_result;", - DATASET, CALLABLE_STMT_PROC_NAME, DATASET, CALLABLE_STMT_TABLE_NAME); - CallableStatement callableStatement = bigQueryConnection.prepareCall(callableStmtQuery); - callableStatement.setString(1, "callable-stmt-test"); - callableStatement.setInt(2, randomNum); - - assertFalse(callableStatement.execute()); - assertEquals(1, callableStatement.getUpdateCount()); - - // This is an actual SELECT * from the above - assertTrue(callableStatement.getMoreResults()); - ResultSet resultSet = callableStatement.getResultSet(); - ResultSetMetaData rsMetadata = resultSet.getMetaData(); - assertEquals(3, rsMetadata.getColumnCount()); - - assertTrue(resultSet.next()); - - String expected = String.format("callable-stmt-test-%d", randomNum); - String actual = resultSet.getString(3); - - assertEquals(expected, actual); - - // Validate there are no more results - assertFalse(callableStatement.getMoreResults()); - assertEquals(-1, callableStatement.getUpdateCount()); - callableStatement.close(); - } - @Test public void testExecuteScriptWithExpession() throws SQLException { int randomNum = random.nextInt(99); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITCallableStatementTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITCallableStatementTest.java new file mode 100644 index 000000000000..04690ccd5586 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITCallableStatementTest.java @@ -0,0 +1,665 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.exception.BigQueryJdbcSqlFeatureNotSupportedException; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import java.util.Properties; +import java.util.Random; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ITCallableStatementTest extends ITBase { + static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); + static final String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + + PROJECT_ID + + ";OAUTHTYPE=3"; + private static final Random random = new Random(); + private static final String DATASET = "JDBC_PRESUBMIT_INTEGRATION_DATASET"; + private static final String CALLABLE_STMT_PROC_NAME = "IT_CALLABLE_STMT_PROC_TEST"; + private static final String CALLABLE_STMT_TABLE_NAME = "IT_CALLABLE_STMT_PROC_TABLE"; + private static final String CALLABLE_STMT_PARAM_KEY = "CALL_STMT_PARAM_KEY"; + private static final String CALLABLE_STMT_DML_INSERT_PROC_NAME = + "IT_CALLABLE_STMT_PROC_DML_INSERT_TEST"; + private static final String CALLABLE_STMT_DML_UPDATE_PROC_NAME = + "IT_CALLABLE_STMT_PROC_DML_UPDATE_TEST"; + private static final String CALLABLE_STMT_DML_DELETE_PROC_NAME = + "IT_CALLABLE_STMT_PROC_DML_DELETE_TEST"; + private static final String CALLABLE_STMT_DML_TABLE_NAME = "IT_CALLABLE_STMT_PROC_DML_TABLE"; + + static Connection bigQueryConnection; + static BigQuery bigQuery; + static Statement bigQueryStatement; + + @BeforeAll + public static void beforeClass() throws SQLException { + bigQueryConnection = DriverManager.getConnection(connection_uri, new Properties()); + bigQueryStatement = bigQueryConnection.createStatement(); + bigQuery = BigQueryOptions.newBuilder().build().getService(); + } + + @AfterAll + public static void afterClass() throws SQLException { + bigQueryStatement.close(); + bigQueryConnection.close(); + } + + // Block A Tests + @Test + public void testPrepareCallSql() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc"); + assertNotNull(callableStatement); + callableStatement.close(); + } + + @Test + public void testRegisterOutParamIndex() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + callableStatement.registerOutParameter(1, Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testRegisterOutParamName() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testRegisterOutParamIndexScale() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + callableStatement.registerOutParameter(1, Types.NUMERIC, 2); + callableStatement.close(); + } + + @Test + public void testRegisterOutParamNameScale() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); + callableStatement.close(); + } + + @Test + public void testPrepareCallSqlResultSetTypeConcurrency() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement); + callableStatement.close(); + } + + @Test + public void testPrepareCallConcurrencyRegisterOutParamIndex() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement); + callableStatement.registerOutParameter(1, Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testPrepareCallConcurrencyRegisterOutParamName() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testPrepareCallConcurrencyRegisterOutParamIndexScale() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement); + callableStatement.registerOutParameter(1, Types.NUMERIC, 2); + callableStatement.close(); + } + + @Test + public void testPrepareCallConcurrencyRegisterOutParamNameScale() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); + callableStatement.close(); + } + + @Test + public void testPrepareCallSqlResultSetTypeConcurrencyHoldability() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertNotNull(callableStatement); + callableStatement.close(); + } + + @Test + public void testPrepareCallHoldabilityRegisterOutParamIndex() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertNotNull(callableStatement); + callableStatement.registerOutParameter(1, Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testPrepareCallHoldabilityRegisterOutParamName() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.VARCHAR); + callableStatement.close(); + } + + @Test + public void testPrepareCallHoldabilityRegisterOutParamIndexScale() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertNotNull(callableStatement); + callableStatement.close(); + } + + @Test + public void testPrepareCallHoldabilityRegisterOutParamNameScale() throws SQLException { + CallableStatement callableStatement = + this.bigQueryConnection.prepareCall( + "call testProc('?')", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertNotNull(callableStatement); + callableStatement.registerOutParameter("ParamKey", Types.NUMERIC, 2); + callableStatement.close(); + } + + @Test + public void testPrepareCallFailureResultSetType() throws SQLException { + assertThrows( + BigQueryJdbcSqlFeatureNotSupportedException.class, + () -> + this.bigQueryConnection.prepareCall( + "call testProc", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); + } + + @Test + public void testPrepareCallFailureResultSetConcurrency() throws SQLException { + assertThrows( + BigQueryJdbcSqlFeatureNotSupportedException.class, + () -> + this.bigQueryConnection.prepareCall( + "call testProc", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); + } + + @Test + public void testPrepareCallFailureResultSetHoldability() throws SQLException { + assertThrows( + BigQueryJdbcSqlFeatureNotSupportedException.class, + () -> + this.bigQueryConnection.prepareCall( + "call testProc", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT)); + } + + @Test + public void testSetterGetterBigDecimal() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + BigDecimal expected = new BigDecimal(12344); + callableStatement.setBigDecimal(CALLABLE_STMT_PARAM_KEY, expected); + BigDecimal actual = callableStatement.getBigDecimal(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterBoolean() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Boolean expected = true; + callableStatement.setBoolean(CALLABLE_STMT_PARAM_KEY, expected); + Boolean actual = callableStatement.getBoolean(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterByte() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Byte expected = "hello".getBytes()[0]; + callableStatement.setByte(CALLABLE_STMT_PARAM_KEY, expected); + Byte actual = callableStatement.getByte(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterBytes() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + byte[] expected = "hello".getBytes(); + callableStatement.setBytes(CALLABLE_STMT_PARAM_KEY, expected); + byte[] actual = callableStatement.getBytes(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterDate() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Date expected = new Date(1234567); + callableStatement.setDate(CALLABLE_STMT_PARAM_KEY, expected); + Date actual = callableStatement.getDate(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterDateCal() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Date expected = new Date(1L); + Calendar cal = Calendar.getInstance(); + callableStatement.setDate(CALLABLE_STMT_PARAM_KEY, expected, cal); + Date actual = callableStatement.getDate(CALLABLE_STMT_PARAM_KEY, cal); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterDouble() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Double expected = 123.2345; + callableStatement.setDouble(CALLABLE_STMT_PARAM_KEY, expected); + Double actual = callableStatement.getDouble(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterFloat() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Float expected = 123.2345F; + callableStatement.setFloat(CALLABLE_STMT_PARAM_KEY, expected); + Float actual = callableStatement.getFloat(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterInt() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Integer expected = 123; + callableStatement.setInt(CALLABLE_STMT_PARAM_KEY, expected); + Integer actual = callableStatement.getInt(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterLong() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Long expected = 123L; + callableStatement.setLong(CALLABLE_STMT_PARAM_KEY, expected); + Long actual = callableStatement.getLong(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterNString() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + String expected = "heelo"; + callableStatement.setNString(CALLABLE_STMT_PARAM_KEY, expected); + String actual = callableStatement.getNString(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterObject() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + String expected = "heelo"; + callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected); + Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterObjectWithSQLType() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + String expected = "heelo"; + callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected, Types.NVARCHAR); + Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterObjectWithSqlTypeAndScale() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + String expected = "heelo"; + callableStatement.setObject(CALLABLE_STMT_PARAM_KEY, expected, Types.NVARCHAR, 0); + Object actual = callableStatement.getObject(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterString() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + String expected = "123"; + callableStatement.setString(CALLABLE_STMT_PARAM_KEY, expected); + String actual = callableStatement.getString(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterTime() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Time expected = new Time(1234567); + callableStatement.setTime(CALLABLE_STMT_PARAM_KEY, expected); + Time actual = callableStatement.getTime(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterTimeCal() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Time expected = new Time(1L); + Calendar cal = Calendar.getInstance(); + callableStatement.setTime(CALLABLE_STMT_PARAM_KEY, expected, cal); + Time actual = callableStatement.getTime(CALLABLE_STMT_PARAM_KEY, cal); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterTimestamp() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Timestamp expected = new Timestamp(1234567); + callableStatement.setTimestamp(CALLABLE_STMT_PARAM_KEY, expected); + Timestamp actual = callableStatement.getTimestamp(CALLABLE_STMT_PARAM_KEY); + assertEquals(expected, actual); + } + + @Test + public void testSetterGetterTimestampCal() throws SQLException { + CallableStatement callableStatement = this.bigQueryConnection.prepareCall("call testProc('?')"); + assertNotNull(callableStatement); + Timestamp expected = new Timestamp(1L); + Calendar cal = Calendar.getInstance(); + callableStatement.setTimestamp(CALLABLE_STMT_PARAM_KEY, expected, cal); + Timestamp actual = callableStatement.getTimestamp(CALLABLE_STMT_PARAM_KEY, cal); + assertEquals(expected, actual); + } + + // Block B Tests + @Test + public void testCallableStatementScriptExecuteUpdate() throws SQLException { + int randomNum = java.util.UUID.randomUUID().hashCode(); + String insertName = "callable-statement-dml-insert-test"; + String insertResult = String.format("%s-%d", insertName, randomNum); + String updateName = "callable-statement-dml-update-test"; + String updateResult = String.format("%s-%d", updateName, randomNum); + String selectStmtQuery = + String.format("SELECT * FROM %s.%s WHERE id = ?", DATASET, CALLABLE_STMT_DML_TABLE_NAME); + String insertCallStmtQuery = + String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_INSERT_PROC_NAME); + String updateCallStmtQuery = + String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_UPDATE_PROC_NAME); + String deleteCallStmtQuery = + String.format("CALL %s.%s(?);", DATASET, CALLABLE_STMT_DML_DELETE_PROC_NAME); + + // DML INSERT + CallableStatement callableStatement = bigQueryConnection.prepareCall(insertCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setString(1, insertName); + callableStatement.setInt(2, randomNum); + callableStatement.setString(3, insertResult); + int rowsInserted = callableStatement.executeUpdate(); + assertEquals(1, rowsInserted); + + PreparedStatement preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + ResultSet rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertTrue(rs.next()); + + assertEquals(insertName, rs.getString(1)); + assertEquals(randomNum, rs.getInt(2)); + assertEquals(insertResult, rs.getString(3)); + + // DML UPDATE + callableStatement = bigQueryConnection.prepareCall(updateCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setString(1, updateName); + callableStatement.setInt(2, randomNum); + callableStatement.setString(3, updateResult); + int rowsUpdated = callableStatement.executeUpdate(); + assertEquals(1, rowsUpdated); + + preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertTrue(rs.next()); + + assertEquals(updateName, rs.getString(1)); + assertEquals(randomNum, rs.getInt(2)); + assertEquals(updateResult, rs.getString(3)); + + // DML DELETE + callableStatement = bigQueryConnection.prepareCall(deleteCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setInt(1, randomNum); + int rowsDeleted = callableStatement.executeUpdate(); + assertEquals(1, rowsDeleted); + + preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertFalse(rs.next()); + + callableStatement.close(); + } + + @Test + public void testCallableStatementScriptExecuteLargeUpdate() throws SQLException { + int randomNum = java.util.UUID.randomUUID().hashCode(); + String insertName = "callable-statement-dml-insert-test"; + String insertResult = String.format("%s-%d", insertName, randomNum); + String updateName = "callable-statement-dml-update-test"; + String updateResult = String.format("%s-%d", updateName, randomNum); + String selectStmtQuery = + String.format("SELECT * FROM %s.%s WHERE id = ?", DATASET, CALLABLE_STMT_DML_TABLE_NAME); + String insertCallStmtQuery = + String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_INSERT_PROC_NAME); + String updateCallStmtQuery = + String.format("CALL %s.%s(?,?,?);", DATASET, CALLABLE_STMT_DML_UPDATE_PROC_NAME); + String deleteCallStmtQuery = + String.format("CALL %s.%s(?);", DATASET, CALLABLE_STMT_DML_DELETE_PROC_NAME); + + // DML INSERT + CallableStatement callableStatement = bigQueryConnection.prepareCall(insertCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setString(1, insertName); + callableStatement.setInt(2, randomNum); + callableStatement.setString(3, insertResult); + long rowsInserted = callableStatement.executeLargeUpdate(); + assertEquals(1L, rowsInserted); + + PreparedStatement preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + ResultSet rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertTrue(rs.next()); + + assertEquals(insertName, rs.getString(1)); + assertEquals(randomNum, rs.getInt(2)); + assertEquals(insertResult, rs.getString(3)); + + // DML UPDATE + callableStatement = bigQueryConnection.prepareCall(updateCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setString(1, updateName); + callableStatement.setInt(2, randomNum); + callableStatement.setString(3, updateResult); + long rowsUpdated = callableStatement.executeLargeUpdate(); + assertEquals(1L, rowsUpdated); + + preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertTrue(rs.next()); + + assertEquals(updateName, rs.getString(1)); + assertEquals(randomNum, rs.getInt(2)); + assertEquals(updateResult, rs.getString(3)); + + // DML DELETE + callableStatement = bigQueryConnection.prepareCall(deleteCallStmtQuery); + assertNotNull(callableStatement); + callableStatement.setInt(1, randomNum); + long rowsDeleted = callableStatement.executeLargeUpdate(); + assertEquals(1L, rowsDeleted); + + preparedStatement = bigQueryConnection.prepareStatement(selectStmtQuery); + assertNotNull(preparedStatement); + preparedStatement.setInt(1, randomNum); + rs = preparedStatement.executeQuery(); + assertNotNull(rs); + assertFalse(rs.next()); + + callableStatement.close(); + } + + @Test + public void testScript() throws SQLException { + String BASE_QUERY = + "SELECT * FROM bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2017 order by" + + " trip_distance asc LIMIT %s;"; + String query1 = String.format(BASE_QUERY, 5000); + String query2 = String.format(BASE_QUERY, 7000); + String query3 = String.format(BASE_QUERY, 9000); + + bigQueryStatement.execute(query1 + query2 + query3); + ResultSet resultSet = bigQueryStatement.getResultSet(); + assertEquals(5000, resultSetRowCount(resultSet)); + + boolean hasMoreResult = bigQueryStatement.getMoreResults(); + assertTrue(hasMoreResult); + resultSet = bigQueryStatement.getResultSet(); + assertEquals(7000, resultSetRowCount(resultSet)); + + hasMoreResult = bigQueryStatement.getMoreResults(); + assertTrue(hasMoreResult); + resultSet = bigQueryStatement.getResultSet(); + assertEquals(9000, resultSetRowCount(resultSet)); + } + + @Test + public void testCallableStatementScriptExecute() throws SQLException { + int randomNum = random.nextInt(99); + String callableStmtQuery = + String.format( + "DECLARE call_result STRING;" + + "CALL %s.%s(?,?,call_result);" + + "SELECT * FROM %s.%s WHERE result = call_result;", + DATASET, CALLABLE_STMT_PROC_NAME, DATASET, CALLABLE_STMT_TABLE_NAME); + CallableStatement callableStatement = bigQueryConnection.prepareCall(callableStmtQuery); + callableStatement.setString(1, "callable-stmt-test"); + callableStatement.setInt(2, randomNum); + + assertFalse(callableStatement.execute()); + assertEquals(1, callableStatement.getUpdateCount()); + + // This is an actual SELECT * from the above + assertTrue(callableStatement.getMoreResults()); + ResultSet resultSet = callableStatement.getResultSet(); + ResultSetMetaData rsMetadata = resultSet.getMetaData(); + assertEquals(3, rsMetadata.getColumnCount()); + + assertTrue(resultSet.next()); + + String expected = String.format("callable-stmt-test-%d", randomNum); + String actual = resultSet.getString(3); + + assertEquals(expected, actual); + + // Validate there are no more results + assertFalse(callableStatement.getMoreResults()); + assertEquals(-1, callableStatement.getUpdateCount()); + callableStatement.close(); + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java new file mode 100644 index 000000000000..46e64258c34a --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java @@ -0,0 +1,356 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.exception.BigQueryJdbcException; +import com.google.cloud.bigquery.jdbc.PooledConnectionDataSource; +import com.google.cloud.bigquery.jdbc.PooledConnectionListener; +import com.google.cloud.bigquery.jdbc.utils.TestUtilities.TestConnectionListener; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.sql.PooledConnection; +import org.junit.jupiter.api.Test; + +public class ITConnectionPoolingTest extends ITBase { + static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); + private static final Long DEFAULT_CONN_POOL_SIZE = 10L; + private static final Long CUSTOM_CONN_POOL_SIZE = 5L; + + @Test + public void testPooledConnectionDataSourceSuccess() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + } + + @Test + public void testPooledConnectionDataSourceFailNoConnectionURl() { + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + + assertThrows(BigQueryJdbcException.class, () -> pooledDataSource.getPooledConnection()); + } + + @Test + public void testPooledConnectionDataSourceFailInvalidConnectionURl() { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;" + + "ListenerPoolSize=invalid"; + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + assertThrows(NumberFormatException.class, () -> pooledDataSource.getPooledConnection()); + } + + @Test + public void testPooledConnectionAddConnectionListener() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + TestConnectionListener listener = new TestConnectionListener(); + pooledConnection.addConnectionEventListener(listener); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + } + + @Test + public void testPooledConnectionRemoveConnectionListener() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + TestConnectionListener listener = new TestConnectionListener(); + pooledConnection.removeConnectionEventListener(listener); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + } + + @Test + public void testPooledConnectionConnectionClosed() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + TestConnectionListener listener = new TestConnectionListener(); + pooledConnection.addConnectionEventListener(listener); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + + Connection connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + + connection.close(); + assertEquals(1, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + } + + @Test + public void testPooledConnectionClose() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + TestConnectionListener listener = new TestConnectionListener(); + pooledConnection.addConnectionEventListener(listener); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + + pooledConnection.close(); + assertEquals(1, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + } + + @Test + public void testPooledConnectionConnectionError() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + TestConnectionListener listener = new TestConnectionListener(); + pooledConnection.addConnectionEventListener(listener); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(0, listener.getConnectionErrorCount()); + + Connection connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + + ExecutorService executor = Executors.newFixedThreadPool(3); + connection.abort(executor); + assertEquals(0, listener.getConnectionClosedCount()); + assertEquals(1, listener.getConnectionErrorCount()); + + executor.shutdown(); + connection.close(); + pooledConnection.close(); + } + + @Test + public void testPooledConnectionListenerAddListener() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); + pooledConnection.addConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + pooledConnection.close(); + } + + @Test + public void testPooledConnectionListenerRemoveListener() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); + pooledConnection.addConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + + pooledConnection.removeConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + pooledConnection.close(); + } + + @Test + public void testPooledConnectionListenerCloseConnection() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); + pooledConnection.addConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + + Connection connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + + connection.close(); + assertFalse(listener.isConnectionPoolEmpty()); + pooledConnection.close(); + } + + @Test + public void testPooledConnectionListenerClosePooledConnection() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); + pooledConnection.addConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + + pooledConnection.close(); + assertFalse(listener.isConnectionPoolEmpty()); + } + + @Test + public void testPooledConnectionListenerConnectionError() throws SQLException { + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;OAuthType=3;ProjectId=testProject;ConnectionPoolSize=20;ListenerPoolSize=20;"; + + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionUrl); + + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = new PooledConnectionListener(DEFAULT_CONN_POOL_SIZE); + pooledConnection.addConnectionEventListener(listener); + assertTrue(listener.isConnectionPoolEmpty()); + + Connection connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + + ExecutorService executor = Executors.newFixedThreadPool(3); + connection.abort(executor); + assertTrue(listener.isConnectionPoolEmpty()); + + executor.shutdown(); + connection.close(); + pooledConnection.close(); + } + + @Test + public void testExecuteQueryWithConnectionPoolingEnabledDefaultPoolSize() throws SQLException { + String connectionURL = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;ProjectId=" + + PROJECT_ID + + ";"; + assertConnectionPoolingResults(connectionURL, DEFAULT_CONN_POOL_SIZE); + } + + @Test + public void testExecuteQueryWithConnectionPoolingEnabledCustomPoolSize() throws SQLException { + String connectionURL = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;ProjectId=" + + PROJECT_ID + + ";" + + "ConnectionPoolSize=" + + CUSTOM_CONN_POOL_SIZE + + ";"; + assertConnectionPoolingResults(connectionURL, CUSTOM_CONN_POOL_SIZE); + } + + private void assertConnectionPoolingResults(String connectionURL, Long connectionPoolSize) + throws SQLException { + // Create Pooled Connection Datasource + PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource(); + pooledDataSource.setURL(connectionURL); + + // Get pooled connection and ensure listner was added with default connection pool size. + PooledConnection pooledConnection = pooledDataSource.getPooledConnection(); + assertNotNull(pooledConnection); + PooledConnectionListener listener = pooledDataSource.getConnectionPoolManager(); + assertNotNull(listener); + assertTrue(listener.isConnectionPoolEmpty()); + + // Get Underlying physical connection + Connection connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + + // Execute query with physical connection + String query = + "SELECT DISTINCT repository_name FROM `bigquery-public-data.samples.github_timeline` LIMIT" + + " 850"; + Statement statement = connection.createStatement(); + ResultSet jsonResultSet = statement.executeQuery(query); + assertTrue(jsonResultSet.getClass().getName().contains("BigQueryJsonResultSet")); + + // Close physical connection + connection.close(); + assertFalse(listener.isConnectionPoolEmpty()); + assertEquals(1, listener.getConnectionPoolCurrentCapacity()); + assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); + + // Reuse same physical connection. + connection = pooledConnection.getConnection(); + assertNotNull(connection); + assertFalse(connection.isClosed()); + assertFalse(listener.isConnectionPoolEmpty()); + assertEquals(1, listener.getConnectionPoolCurrentCapacity()); + assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); + + // Execute query with reusable physical connection + jsonResultSet = statement.executeQuery(query); + assertTrue(jsonResultSet.getClass().getName().contains("BigQueryJsonResultSet")); + + // Return connection back to the pool. + connection.close(); + assertFalse(listener.isConnectionPoolEmpty()); + assertEquals(1, listener.getConnectionPoolCurrentCapacity()); + assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); + pooledConnection.close(); + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java new file mode 100644 index 000000000000..2c316b46ff5e --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java @@ -0,0 +1,471 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import java.sql.Array; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.util.Properties; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ITConnectionTest { + + private static final String DATASET = "EXT_JDBC_CONNECTION_TEST_DATASET"; + private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + static Random random = new Random(); + static int randomNumber = random.nextInt(999); + private static final String TABLE_NAME = "EXT_JDBC_CONNECTION_TEST_TABLE" + randomNumber; + + private static String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=%s;OAuthType=3;Timeout=3600;"; + + @BeforeClass + public static void beforeClass() throws InterruptedException { + + ITBase.setUpDataset(DATASET); + ITBase.setUpTable(DATASET, TABLE_NAME); + ITBase.setUpProcedure(DATASET, TABLE_NAME); + } + + @AfterClass + public static void afterClass() throws InterruptedException { + ITBase.cleanUp(DATASET); + } + + @Test + public void testGetMetaData() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertFalse(connection.isClosed()); + DatabaseMetaData metaData = connection.getMetaData(); + assertNotNull(metaData); + assertEquals("Google BigQuery", metaData.getDatabaseProductName()); + assertEquals(4, metaData.getJDBCMajorVersion()); + assertEquals(2, metaData.getJDBCMinorVersion()); + assertEquals("Google BigQuery", metaData.getDatabaseProductName()); + assertEquals("SimbaJDBCDriverforGoogleBigQuery", metaData.getDriverName()); + } + + @Test + public void testCreateStatement() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertFalse(connection.isClosed()); + Statement statement = connection.createStatement(); + assertNotNull(statement); + statement.close(); + connection.close(); + } + + @Test + public void testPrepareStatement1() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT 1"); + assertNotNull(preparedStatement); + preparedStatement.execute(); + preparedStatement.close(); + connection.close(); + } + + @Test + public void testPrepareStatement2() throws SQLException { + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + PreparedStatement preparedStatement = + connection.prepareStatement("SELECT 1", Statement.NO_GENERATED_KEYS); + assertNotNull(preparedStatement); + preparedStatement.execute(); + preparedStatement.close(); + connection.close(); + } + + @Test + public void testPrepareStatement3() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> connection.prepareStatement("SELECT 1", new int[] {1, 2, 3})); + } + + @Test + public void testPrepareStatement4() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + PreparedStatement preparedStatement = + connection.prepareStatement( + "SELECT 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + assertNotNull(preparedStatement); + preparedStatement.execute(); + preparedStatement.close(); + connection.close(); + } + + @Test + public void testPrepareStatement5() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> + connection.prepareStatement( + "SELECT 1", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT)); + } + + @Test + public void testPrepareStatement6() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> connection.prepareStatement("SELECT 1", new String[] {"a", "b"})); + } + + @Test + public void testPrepareCall1() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + try { + CallableStatement callableStatement = + connection.prepareCall( + String.format("call `%s.%s`.create_customer()", DEFAULT_CATALOG, DATASET)); + + assertNotNull(callableStatement); + callableStatement.execute(); + callableStatement.close(); + + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testPrepareCall2() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + try { + + CallableStatement callableStatement1 = + connection.prepareCall( + String.format("call `%s.%s`.create_customer()", DEFAULT_CATALOG, DATASET), + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY); + assertNotNull(callableStatement1); + callableStatement1.execute(); + assertNull(callableStatement1.getResultSet()); + callableStatement1.close(); + + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testPrepareCall3() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> + connection.prepareCall( + String.format("call %s.%s.create_customer()", DEFAULT_CATALOG, DATASET), + ResultSet.TYPE_SCROLL_INSENSITIVE, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT)); + } + + @Test + public void testSetAutoCommit() throws SQLException { + Properties properties = new Properties(); + properties.setProperty("EnableSession", "1"); + + Connection connection1 = DriverManager.getConnection(ITBase.connectionUrl, properties); + connection1.setAutoCommit(false); + assertFalse(connection1.getAutoCommit()); + connection1.setAutoCommit(true); + assertTrue(connection1.getAutoCommit()); + connection1.close(); + } + + @Test + public void testCommitAndRollback() throws SQLException { + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + Properties properties = new Properties(); + properties.setProperty("EnableSession", "1"); + + Connection connection1 = DriverManager.getConnection(ITBase.connectionUrl, properties); + connection1.setAutoCommit(false); + Statement statement1 = connection1.createStatement(); + statement.execute( + String.format( + "CREATE TABLE IF NOT EXISTS %s.%s.autocommit_test_table (id INT)", + DEFAULT_CATALOG, DATASET)); + connection1.commit(); + + statement1.executeUpdate( + String.format( + "INSERT INTO %s.%s.autocommit_test_table (id) VALUES (1)", DEFAULT_CATALOG, DATASET)); + connection1.rollback(); + + ResultSet resultSet = + statement1.executeQuery( + String.format( + "SELECT COUNT(*) FROM %s.%s.autocommit_test_table", DEFAULT_CATALOG, DATASET)); + resultSet.next(); + assertEquals(0, resultSet.getInt(1)); // Table should be empty due to rollback + + connection1.setAutoCommit(true); + statement1.executeUpdate( + String.format( + "DROP TABLE IF EXISTS %s.%s.autocommit_test_table", DEFAULT_CATALOG, DATASET)); + statement1.close(); + connection1.close(); + statement.close(); + connection.close(); + } + + @Test + public void testSetCatalog() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + try { + String catalog = connection.getCatalog(); + if (catalog != null) { + connection.setCatalog(catalog); + assertEquals(catalog, connection.getCatalog()); + } + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testSetSchema() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + try { + String schema = connection.getSchema(); + if (schema != null) { + connection.setSchema(schema); + assertEquals(schema, connection.getSchema()); + } + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testSetClientInfo() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet resultSet = databaseMetaData.getClientInfoProperties(); + while (resultSet.next()) { + assertNotNull(resultSet.getString("NAME")); + assertNull(resultSet.getString("DEFAULT_VALUE")); + } + connection.setClientInfo("APPLICATIONNAME", "MyTestApp"); + assertEquals("MyTestApp", connection.getClientInfo("APPLICATIONNAME")); + connection.setClientInfo("CLIENTUSER", "test"); + assertEquals("test", connection.getClientInfo("CLIENTUSER")); + + // Test setClientInfo with Properties + Properties properties = new Properties(); + properties.setProperty("CLIENTHOSTNAME", "testHost"); + connection.setClientInfo(properties); + assertEquals("testHost", connection.getClientInfo("CLIENTHOSTNAME")); + connection.setClientInfo("CLIENTHOSTNAME", "testHost23"); + assertEquals("testHost23", connection.getClientInfo("CLIENTHOSTNAME")); + connection.close(); + } + + @Test + public void testSetNetworkTimeout() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> connection.setNetworkTimeout(null, 1000)); // 1 second timeout + } + + @Test + public void testCreateClob() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows(SQLFeatureNotSupportedException.class, () -> connection.createClob()); + } + + @Test + public void testCreateBlob() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows(SQLFeatureNotSupportedException.class, () -> connection.createBlob()); + } + + @Test + public void testCreateNClob() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows(SQLFeatureNotSupportedException.class, () -> connection.createNClob()); + } + + @Test + public void testCreateSQLXML() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows(SQLFeatureNotSupportedException.class, () -> connection.createSQLXML()); + } + + @Test + public void testCreateArray() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + try { + Array array = connection.createArrayOf("INTEGER", new Object[] {1, 2, 3}); + assertNotNull(array); + + array.free(); // Remember to free resources + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testCreateStruct() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> connection.createStruct("ADDRESS", new Object[] {"123 Main St", "Anytown"})); + } + + @Test + public void testNativeSQL() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + String sql = "SELECT 1"; + String nativeSql = connection.nativeSQL(sql); + assertEquals(sql, nativeSql); + assertNotNull(nativeSql); + connection.close(); + } + + @Test + public void testSetSavepoint() throws SQLException { + Properties properties = new Properties(); + properties.setProperty("EnableSession", "1"); + + Connection connection1 = DriverManager.getConnection(ITBase.connectionUrl, properties); + connection1.setAutoCommit(false); + Statement statement = connection1.createStatement(); + statement.execute("SELECT 1; SELECT 2; SELECT 3;"); + assertThrows(SQLFeatureNotSupportedException.class, () -> connection1.setSavepoint()); + assertThrows( + SQLFeatureNotSupportedException.class, () -> connection1.setSavepoint("mySavepoint")); + assertThrows( + SQLFeatureNotSupportedException.class, + () -> connection1.releaseSavepoint(connection1.setSavepoint())); + connection1.setAutoCommit(true); + connection1.close(); + } + + // TODO:rewrite test + @Test + public void testAbort() throws SQLException, InterruptedException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + // This test is tricky to automate fully, as it involves asynchronous behavior + // We'll just test that we can call it without throwing an exception immediately + + // Create an ExecutorService to simulate asynchronous operation + java.util.concurrent.ExecutorService executor = + java.util.concurrent.Executors.newSingleThreadExecutor(); + executor.submit( + () -> { + try { + // This will likely throw an exception as the connection is already open + // But we mainly want to test that calling abort doesn't cause a direct error + assertFalse(connection.isClosed()); + assertTrue(connection.isClosed()); + System.out.println(connection.isClosed()); + connection.abort(java.util.concurrent.Executors.newSingleThreadExecutor()); + System.out.println(connection.isClosed()); + assertTrue(connection.isClosed()); + } catch (SQLException e) { + e.printStackTrace(); + // It's expected that calling abort might cause an exception + // depending on the state of the connection and driver. + System.out.println("Abort test threw an exception, which is expected in some cases."); + } + }); + + // Give the thread a short time to execute + Thread.sleep(100); + + // Shutdown the executor + executor.shutdown(); + executor.awaitTermination(100, java.util.concurrent.TimeUnit.MILLISECONDS); + connection.close(); + } + + @Test + public void testIsWrapperFor() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertTrue(connection.isWrapperFor(Connection.class)); + connection.close(); + } + + @Test + public void testIsValid() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + assertTrue(connection.isValid(0)); // 0 seconds timeout + connection.close(); + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java new file mode 100644 index 000000000000..9b9bb9138640 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java @@ -0,0 +1,1242 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.google.cloud.ServiceOptions; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Properties; +import java.util.Random; +import java.util.Set; +import java.util.regex.Pattern; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class ITDatabaseMetadataTest extends ITBase { + static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); + static final String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + + PROJECT_ID + + ";OAUTHTYPE=3"; + private static final Random random = new Random(); + private static final int randomNumber = random.nextInt(9999); + private static final String DATASET = "JDBC_PRESUBMIT_INTEGRATION_DATASET"; + private static final String DATASET2 = "JDBC_PRESUBMIT_INTEGRATION_DATASET_2"; + private static final String CONSTRAINTS_DATASET = "JDBC_CONSTRAINTS_TEST_DATASET"; + private static final String CONSTRAINTS_TABLE_NAME = "JDBC_CONSTRAINTS_TEST_TABLE"; + private static final String CONSTRAINTS_TABLE_NAME2 = "JDBC_CONSTRAINTS_TEST_TABLE2"; + private static final String CONSTRAINTS_TABLE_NAME3 = "JDBC_CONSTRAINTS_TEST_TABLE3"; + + static Connection bigQueryConnection; + static Statement bigQueryStatement; + + private static final Pattern VERSION_PATTERN = + Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.\\d+)+\\s*.*"); + private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + private static final String TABLE_NAME = "JDBC_DBMETADATA_TEST_TABLE" + randomNumber; + private static String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=%s;OAuthType=3;Timeout=3600;"; + + @BeforeClass + public static void beforeClass() throws InterruptedException, SQLException { + // Set up Dataset + ITBase.setUpTable(DATASET, TABLE_NAME); + bigQueryConnection = DriverManager.getConnection(connection_uri, new Properties()); + bigQueryStatement = bigQueryConnection.createStatement(); + } + + @AfterClass + public static void afterClass() throws SQLException { + bigQueryStatement.close(); + bigQueryConnection.close(); + } + + @Test + public void testGetCatalogs() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyCatalogHelper(metaData, ".", "Project", 128, DEFAULT_CATALOG, true, true); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + // This test is throwing the following error: [Simba][BigQueryJDBCDriver](100068) Error fetching + // metadata + @Test + public void testGetSchemas() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifySchemaHelper(connection, metaData, "Dataset", 1024, true, true); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testGetTableTypes() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + // this is either 3 or 4 in different projects + + verifyTableTypes(metaData, Arrays.asList("EXTERNAL", "TABLE", "VIEW")); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testGetTables() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyGetTables(connection, metaData, DATASET); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + // this currently returns empty. + // TODO: Make it return non-empty + @Test + public void testGetTablePrivileges() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyGetTablePrivileges(connection, metaData, DATASET); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testGetClientInfoProperties() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + ResultSet resultSet = metaData.getClientInfoProperties(); + // Resultset is not empty + assertTrue(resultSet.next()); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testGetColumns() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyGetColumns(connection, metaData, DATASET); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testDriverMetadataInfo() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyDriverMetadata( + metaData, 4, 2, "Google BigQuery", "SimbaJDBCDriverforGoogleBigQuery", VERSION_PATTERN); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testProcedure() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + + verifyTestProcedure(metaData, "procedure"); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testAllBooleanMethods() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + // This method toggles between true and false in different environments + assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE)); // f + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)); + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); // f + + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); // f + assertTrue(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE)); + assertEquals(Connection.TRANSACTION_SERIALIZABLE, metaData.getDefaultTransactionIsolation()); + assertTrue(metaData.supportsTransactions()); + + verifyAllBooleanMethods(metaData); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testAllIntMethods() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + verifyIntMethods(metaData); + assertEquals(2, metaData.getResultSetHoldability()); // 2 + assertEquals(2, metaData.getDatabaseMajorVersion()); // 2 + assertEquals(0, metaData.getDatabaseMinorVersion()); // 0 + assertEquals(2, metaData.getSQLStateType()); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testAllStringMethods() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + verifyAllStringMethods(metaData, "procedure"); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @Test + public void testGetPrimaryKeys() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData metaData = connection.getMetaData(); + try { + verifyGetPrimaryKeys(connection, metaData, DATASET); + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } + + @org.junit.jupiter.api.Test + public void testTableConstraints() throws SQLException { + ResultSet primaryKey1 = + bigQueryConnection + .getMetaData() + .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); + primaryKey1.next(); + Assertions.assertEquals("id", primaryKey1.getString(4)); + Assertions.assertFalse(primaryKey1.next()); + + ResultSet primaryKey2 = + bigQueryConnection + .getMetaData() + .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME2); + primaryKey2.next(); + Assertions.assertEquals("first_name", primaryKey2.getString(4)); + primaryKey2.next(); + Assertions.assertEquals("last_name", primaryKey2.getString(4)); + Assertions.assertFalse(primaryKey2.next()); + + ResultSet foreignKeys = + bigQueryConnection + .getMetaData() + .getImportedKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); + foreignKeys.next(); + Assertions.assertEquals(CONSTRAINTS_TABLE_NAME2, foreignKeys.getString(3)); + Assertions.assertEquals("first_name", foreignKeys.getString(4)); + Assertions.assertEquals("name", foreignKeys.getString(8)); + foreignKeys.next(); + Assertions.assertEquals(CONSTRAINTS_TABLE_NAME2, foreignKeys.getString(3)); + Assertions.assertEquals("last_name", foreignKeys.getString(4)); + Assertions.assertEquals("second_name", foreignKeys.getString(8)); + foreignKeys.next(); + Assertions.assertEquals(CONSTRAINTS_TABLE_NAME3, foreignKeys.getString(3)); + Assertions.assertEquals("address", foreignKeys.getString(4)); + Assertions.assertEquals("address", foreignKeys.getString(8)); + Assertions.assertFalse(foreignKeys.next()); + + ResultSet crossReference = + bigQueryConnection + .getMetaData() + .getCrossReference( + PROJECT_ID, + CONSTRAINTS_DATASET, + CONSTRAINTS_TABLE_NAME2, + PROJECT_ID, + CONSTRAINTS_DATASET, + CONSTRAINTS_TABLE_NAME); + crossReference.next(); + Assertions.assertEquals(CONSTRAINTS_TABLE_NAME2, crossReference.getString(3)); + Assertions.assertEquals("first_name", crossReference.getString(4)); + Assertions.assertEquals("name", crossReference.getString(8)); + crossReference.next(); + Assertions.assertEquals("last_name", crossReference.getString(4)); + Assertions.assertEquals("second_name", crossReference.getString(8)); + Assertions.assertFalse(crossReference.next()); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetCatalogs() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + try (ResultSet rs = databaseMetaData.getCatalogs()) { + assertNotNull(rs, "ResultSet from getCatalogs() should not be null"); + + ResultSetMetaData rsmd = rs.getMetaData(); + assertNotNull(rsmd, "ResultSetMetaData should not be null"); + Assertions.assertEquals(1, rsmd.getColumnCount(), "Should have one column"); + Assertions.assertEquals( + "TABLE_CAT", rsmd.getColumnName(1), "Column name should be TABLE_CAT"); + + Assertions.assertTrue(rs.next(), "ResultSet should have one row"); + Assertions.assertEquals( + PROJECT_ID, rs.getString("TABLE_CAT"), "Catalog name should match Project ID"); + Assertions.assertFalse(rs.next(), "ResultSet should have no more rows"); + } + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetProcedures() throws SQLException { + String DATASET = "JDBC_INTEGRATION_DATASET"; + String procedureName = "create_customer"; + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + ResultSet resultSet = databaseMetaData.getProcedures(PROJECT_ID, DATASET, procedureName); + while (resultSet.next()) { + Assertions.assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); + Assertions.assertEquals(DATASET, resultSet.getString("PROCEDURE_SCHEM")); + Assertions.assertEquals(procedureName, resultSet.getString("PROCEDURE_NAME")); + Assertions.assertEquals(procedureName, resultSet.getString("SPECIFIC_NAME")); + Assertions.assertEquals( + DatabaseMetaData.procedureResultUnknown, resultSet.getInt("PROCEDURE_TYPE")); + } + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetProcedureColumns() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + + // --- Test Case 1: Specific schema and procedure, null column name pattern --- + String specificSchema = "JDBC_INTEGRATION_DATASET"; + String specificProcedure = "create_customer"; + ResultSet resultSet = + databaseMetaData.getProcedureColumns(PROJECT_ID, specificSchema, specificProcedure, null); + int specificProcRows = 0; + boolean foundNameParam = false; + boolean foundIdParam = false; + while (resultSet.next()) { + specificProcRows++; + Assertions.assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); + Assertions.assertEquals(specificSchema, resultSet.getString("PROCEDURE_SCHEM")); + Assertions.assertEquals(specificProcedure, resultSet.getString("PROCEDURE_NAME")); + Assertions.assertEquals(specificProcedure, resultSet.getString("SPECIFIC_NAME")); + if ("name".equals(resultSet.getString("COLUMN_NAME"))) { + foundNameParam = true; + Assertions.assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); + } + if ("id".equals(resultSet.getString("COLUMN_NAME"))) { + foundIdParam = true; + Assertions.assertEquals(2, resultSet.getInt("ORDINAL_POSITION")); + } + } + Assertions.assertEquals( + 2, specificProcRows, "Should find 2 parameters for " + specificProcedure); + Assertions.assertTrue(foundNameParam, "Parameter 'name' should be found"); + Assertions.assertTrue(foundIdParam, "Parameter 'id' should be found"); + resultSet.close(); + + // --- Test Case 2: Specific schema, procedure, and column name pattern --- + String specificColumn = "name"; + resultSet = + databaseMetaData.getProcedureColumns( + PROJECT_ID, specificSchema, specificProcedure, specificColumn); + Assertions.assertTrue(resultSet.next(), "Should find the specific column 'name'"); + Assertions.assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); + Assertions.assertEquals(specificSchema, resultSet.getString("PROCEDURE_SCHEM")); + Assertions.assertEquals(specificProcedure, resultSet.getString("PROCEDURE_NAME")); + Assertions.assertEquals(specificColumn, resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertEquals( + (short) DatabaseMetaData.procedureColumnUnknown, resultSet.getShort("COLUMN_TYPE")); + Assertions.assertEquals(java.sql.Types.NVARCHAR, resultSet.getInt("DATA_TYPE")); + Assertions.assertEquals("NVARCHAR", resultSet.getString("TYPE_NAME")); + Assertions.assertFalse(resultSet.next(), "Should only find one row for exact column match"); + resultSet.close(); + + // --- Test Case 3: Non-existent procedure --- + resultSet = + databaseMetaData.getProcedureColumns( + PROJECT_ID, specificSchema, "non_existent_procedure_xyz", null); + Assertions.assertFalse( + resultSet.next(), "Should not find columns for a non-existent procedure"); + resultSet.close(); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetColumns() throws SQLException { + String DATASET = "JDBC_INTEGRATION_DATASET"; + String TABLE_NAME = "JDBC_DATATYPES_INTEGRATION_TEST_TABLE"; + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + + // --- Test Case 1: Specific Column (StringField) --- + ResultSet resultSet = + databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "StringField"); + + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals(PROJECT_ID, resultSet.getString("TABLE_CAT")); + Assertions.assertEquals(DATASET, resultSet.getString("TABLE_SCHEM")); + Assertions.assertEquals(TABLE_NAME, resultSet.getString("TABLE_NAME")); + Assertions.assertEquals("StringField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("NVARCHAR", resultSet.getString("TYPE_NAME")); + resultSet.getObject("COLUMN_SIZE"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(6, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 2: All Columns --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, null); + Assertions.assertTrue(resultSet.next()); + int count = 0; + do { + count++; + Assertions.assertEquals(PROJECT_ID, resultSet.getString("TABLE_CAT")); + Assertions.assertEquals(DATASET, resultSet.getString("TABLE_SCHEM")); + Assertions.assertEquals(TABLE_NAME, resultSet.getString("TABLE_NAME")); + assertNotNull(resultSet.getString("COLUMN_NAME")); + } while (resultSet.next()); + Assertions.assertEquals(16, count); + + // --- Test Case 3: Column Name Pattern Matching (%Field) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "%Time%"); + Assertions.assertTrue(resultSet.next()); + count = 0; + do { + count++; + String columnName = resultSet.getString("COLUMN_NAME"); + Assertions.assertTrue(columnName.contains("Time")); + } while (resultSet.next()); + Assertions.assertEquals(3, count); + + // --- Test Case 4: Column Name Pattern Matching (Integer%) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "Integer%"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("IntegerField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("BIGINT", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(19, resultSet.getInt("COLUMN_SIZE")); + Assertions.assertEquals(0, resultSet.getInt("DECIMAL_DIGITS")); + Assertions.assertEquals(10, resultSet.getInt("NUM_PREC_RADIX")); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(2, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 5: Specific Column (BooleanField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "BooleanField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("BooleanField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("BOOLEAN", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(1, resultSet.getInt("COLUMN_SIZE")); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(1, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 6: Specific Column (NumericField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "NumericField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("NumericField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("NUMERIC", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(38, resultSet.getInt("COLUMN_SIZE")); + Assertions.assertEquals(9, resultSet.getInt("DECIMAL_DIGITS")); + Assertions.assertEquals(10, resultSet.getInt("NUM_PREC_RADIX")); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(4, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 7: Specific Column (BytesField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "BytesField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("BytesField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("VARBINARY", resultSet.getString("TYPE_NAME")); + resultSet.getObject("COLUMN_SIZE"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(7, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 8: Specific Column (ArrayField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "ArrayField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("ArrayField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("ARRAY", resultSet.getString("TYPE_NAME")); + resultSet.getObject("COLUMN_SIZE"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(9, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 9: Specific Column (TimestampField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "TimestampField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("TimestampField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("TIMESTAMP", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(29, resultSet.getInt("COLUMN_SIZE")); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(10, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 10: Specific Column (DateField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "DateField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("DateField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("DATE", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(10, resultSet.getInt("COLUMN_SIZE")); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(11, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 11: Specific Column (TimeField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "TimeField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("TimeField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("TIME", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(15, resultSet.getInt("COLUMN_SIZE")); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(12, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 12: Specific Column (DateTimeField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "DateTimeField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("DateTimeField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("TIMESTAMP", resultSet.getString("TYPE_NAME")); + Assertions.assertEquals(29, resultSet.getInt("COLUMN_SIZE")); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(13, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + + // --- Test Case 13: Specific Column (GeographyField) --- + resultSet = databaseMetaData.getColumns(PROJECT_ID, DATASET, TABLE_NAME, "GeographyField"); + Assertions.assertTrue(resultSet.next()); + Assertions.assertEquals("GeographyField", resultSet.getString("COLUMN_NAME")); + Assertions.assertEquals("VARCHAR", resultSet.getString("TYPE_NAME")); + resultSet.getObject("COLUMN_SIZE"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("DECIMAL_DIGITS"); + Assertions.assertTrue(resultSet.wasNull()); + resultSet.getObject("NUM_PREC_RADIX"); + Assertions.assertTrue(resultSet.wasNull()); + Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); + Assertions.assertEquals(14, resultSet.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(resultSet.next()); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetTables() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + String DATASET = "JDBC_TABLE_TYPES_TEST"; + + // --- Test Case 1: Get all tables (types = null) --- + ResultSet rsAll = databaseMetaData.getTables(PROJECT_ID, DATASET, null, null); + Set allTableNames = new HashSet<>(); + while (rsAll.next()) { + allTableNames.add(rsAll.getString("TABLE_NAME")); + } + Assertions.assertTrue(allTableNames.contains("base_table")); + Assertions.assertTrue(allTableNames.contains("my_view")); + Assertions.assertTrue(allTableNames.contains("external_table")); + Assertions.assertTrue(allTableNames.contains("my_materialized_view")); + Assertions.assertTrue(allTableNames.contains("base_table_clone")); + Assertions.assertTrue(allTableNames.contains("base_table_snapshot")); + Assertions.assertEquals(6, allTableNames.size()); + + // --- Test Case 2: Get only "TABLE" type --- + ResultSet rsTable = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"TABLE"}); + Set tableNames = new HashSet<>(); + while (rsTable.next()) { + tableNames.add(rsTable.getString("TABLE_NAME")); + } + Assertions.assertTrue(tableNames.contains("base_table")); + Assertions.assertTrue(tableNames.contains("base_table_clone")); + Assertions.assertEquals(2, tableNames.size()); + + // --- Test Case 3: Get "VIEW" type --- + ResultSet rsView = databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"VIEW"}); + Assertions.assertTrue(rsView.next()); + Assertions.assertEquals("my_view", rsView.getString("TABLE_NAME")); + Assertions.assertEquals("VIEW", rsView.getString("TABLE_TYPE")); + Assertions.assertFalse(rsView.next()); + + // --- Test Case 4: Get "EXTERNAL TABLE" type --- + ResultSet rsExternal = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"EXTERNAL"}); + Assertions.assertTrue(rsExternal.next()); + Assertions.assertEquals("external_table", rsExternal.getString("TABLE_NAME")); + Assertions.assertEquals("EXTERNAL", rsExternal.getString("TABLE_TYPE")); + Assertions.assertFalse(rsExternal.next()); + + // --- Test Case 5: Get "MATERIALIZED_VIEW" type --- + ResultSet rsMaterialized = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"MATERIALIZED_VIEW"}); + Assertions.assertTrue(rsMaterialized.next()); + Assertions.assertEquals("my_materialized_view", rsMaterialized.getString("TABLE_NAME")); + Assertions.assertEquals("MATERIALIZED_VIEW", rsMaterialized.getString("TABLE_TYPE")); + Assertions.assertFalse(rsMaterialized.next()); + + // --- Test Case 6: Get "SNAPSHOT" type --- + ResultSet rsSnapshot = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"SNAPSHOT"}); + Assertions.assertTrue(rsSnapshot.next()); + Assertions.assertEquals("base_table_snapshot", rsSnapshot.getString("TABLE_NAME")); + Assertions.assertEquals("SNAPSHOT", rsSnapshot.getString("TABLE_TYPE")); + Assertions.assertFalse(rsSnapshot.next()); + + // --- Test Case 8: Get multiple types ("TABLE" and "VIEW") --- + ResultSet rsMulti = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {"TABLE", "VIEW"}); + Set multiTableNames = new HashSet<>(); + while (rsMulti.next()) { + multiTableNames.add(rsMulti.getString("TABLE_NAME")); + } + Assertions.assertTrue(multiTableNames.contains("base_table")); + Assertions.assertTrue(multiTableNames.contains("base_table_clone")); + Assertions.assertTrue(multiTableNames.contains("my_view")); + Assertions.assertEquals(3, multiTableNames.size()); + + // --- Test Case 9: tableNamePattern --- + ResultSet rsNamePattern = databaseMetaData.getTables(PROJECT_ID, DATASET, "base%", null); + Set baseTableNames = new HashSet<>(); + while (rsNamePattern.next()) { + baseTableNames.add(rsNamePattern.getString("TABLE_NAME")); + } + Assertions.assertTrue(baseTableNames.contains("base_table")); + Assertions.assertTrue(baseTableNames.contains("base_table_clone")); + Assertions.assertTrue(baseTableNames.contains("base_table_snapshot")); + Assertions.assertEquals(3, baseTableNames.size()); + + // --- Test Case 10: No matching table --- + ResultSet rsNoMatch = + databaseMetaData.getTables(PROJECT_ID, DATASET, "nonexistent_table", null); + Assertions.assertFalse(rsNoMatch.next()); + + // --- Test Case 11: Null type in array --- + ResultSet rsNullType = + databaseMetaData.getTables(PROJECT_ID, DATASET, null, new String[] {null, "VIEW"}); + Assertions.assertTrue(rsNullType.next()); + Assertions.assertEquals("VIEW", rsNullType.getString("TABLE_TYPE")); + Assertions.assertEquals("my_view", rsNullType.getString("TABLE_NAME")); + Assertions.assertFalse(rsNullType.next()); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetSchemas() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + + // Test case 1: Get all schemas with catalog and check for the presence of specific schemas + ResultSet rsAll = databaseMetaData.getSchemas(PROJECT_ID, null); + Set actualSchemas = new HashSet<>(); + while (rsAll.next()) { + Assertions.assertEquals(PROJECT_ID, rsAll.getString("TABLE_CATALOG")); + actualSchemas.add(rsAll.getString("TABLE_SCHEM")); + } + Assertions.assertTrue(actualSchemas.contains("JDBC_INTEGRATION_DATASET")); + Assertions.assertTrue(actualSchemas.contains("JDBC_TABLE_TYPES_TEST")); + Assertions.assertTrue(actualSchemas.contains("ODBC_TEST_DATASET")); + + // Test case 2: Get schemas with catalog and schemaPattern matching "JDBC_NIGHTLY_IT_DATASET" + ResultSet rsPattern = databaseMetaData.getSchemas(PROJECT_ID, "JDBC_NIGHTLY_IT_DATASET"); + Set actualSchemasPattern = new HashSet<>(); + while (rsPattern.next()) { + Assertions.assertEquals(PROJECT_ID, rsPattern.getString("TABLE_CATALOG")); + actualSchemasPattern.add(rsPattern.getString("TABLE_SCHEM")); + } + Assertions.assertTrue(actualSchemasPattern.contains("JDBC_NIGHTLY_IT_DATASET")); + Assertions.assertEquals(1, actualSchemasPattern.size()); + + // Test case 3: Get schemas with catalog and schemaPattern matching "nonexistent" + ResultSet rsNoMatch = databaseMetaData.getSchemas(PROJECT_ID, "nonexistent"); + Assertions.assertFalse(rsNoMatch.next()); + + // Test case 4: Get schemas with non-existent catalog + rsNoMatch = databaseMetaData.getSchemas("invalid-catalog", null); + Assertions.assertFalse(rsNoMatch.next()); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetSchemasNoArgs() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + String expectedCatalog = bigQueryConnection.getCatalog(); + assertNotNull(expectedCatalog, "Project ID (catalog) from connection should not be null"); + + // Test case: Get all schemas (datasets) for the current project + try (ResultSet rsAll = databaseMetaData.getSchemas()) { + assertNotNull(rsAll, "ResultSet from getSchemas() should not be null"); + boolean foundTestDataset = false; + int rowCount = 0; + while (rsAll.next()) { + rowCount++; + Assertions.assertEquals( + expectedCatalog, + rsAll.getString("TABLE_CATALOG"), + "TABLE_CATALOG should match the connection's project ID"); + String schemaName = rsAll.getString("TABLE_SCHEM"); + assertNotNull(schemaName, "TABLE_SCHEM should not be null"); + if (DATASET.equals(schemaName) + || DATASET2.equals(schemaName) + || CONSTRAINTS_DATASET.equals(schemaName) + || "JDBC_TABLE_TYPES_TEST".equals(schemaName) + || "JDBC_INTEGRATION_DATASET".equals(schemaName)) { + foundTestDataset = true; + } + } + Assertions.assertTrue( + foundTestDataset, "At least one of the known test datasets should be found"); + Assertions.assertTrue(rowCount > 0, "Should retrieve at least one schema/dataset"); + } + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetaDataGetFunctions() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + String testSchema = "JDBC_TABLE_TYPES_TEST"; + String testCatalog = PROJECT_ID; + + Set expectedFunctionNames = + new HashSet<>( + Arrays.asList( + "complex_scalar_sql_udf", + "persistent_sql_udf_named_params", + "scalar_js_udf", + "scalar_sql_udf")); + + // Test 1: Get all functions from a specific schema + ResultSet rsAll = databaseMetaData.getFunctions(testCatalog, testSchema, null); + Set foundFunctionNames = new HashSet<>(); + int countAll = 0; + while (rsAll.next()) { + countAll++; + Assertions.assertEquals(testCatalog, rsAll.getString("FUNCTION_CAT")); + Assertions.assertEquals(testSchema, rsAll.getString("FUNCTION_SCHEM")); + String funcName = rsAll.getString("FUNCTION_NAME"); + foundFunctionNames.add(funcName); + assertNull(rsAll.getString("REMARKS")); + Assertions.assertEquals( + DatabaseMetaData.functionResultUnknown, rsAll.getShort("FUNCTION_TYPE")); + Assertions.assertEquals(funcName, rsAll.getString("SPECIFIC_NAME")); + } + Assertions.assertEquals( + expectedFunctionNames.size(), + countAll, + "Should find all " + expectedFunctionNames.size() + " functions in " + testSchema); + Assertions.assertEquals(expectedFunctionNames, foundFunctionNames); + rsAll.close(); + + // Test 2: Get a specific function using functionNamePattern + String specificFunctionName = "scalar_sql_udf"; + ResultSet rsSpecific = + databaseMetaData.getFunctions(testCatalog, testSchema, specificFunctionName); + Assertions.assertTrue( + rsSpecific.next(), "Should find the specific function " + specificFunctionName); + Assertions.assertEquals(testCatalog, rsSpecific.getString("FUNCTION_CAT")); + Assertions.assertEquals(testSchema, rsSpecific.getString("FUNCTION_SCHEM")); + Assertions.assertEquals(specificFunctionName, rsSpecific.getString("FUNCTION_NAME")); + assertNull(rsSpecific.getString("REMARKS")); + Assertions.assertEquals( + DatabaseMetaData.functionResultUnknown, rsSpecific.getShort("FUNCTION_TYPE")); + Assertions.assertEquals(specificFunctionName, rsSpecific.getString("SPECIFIC_NAME")); + Assertions.assertFalse(rsSpecific.next(), "Should only find one row for exact function match"); + rsSpecific.close(); + + // Test 3: Get functions using a wildcard functionNamePattern "scalar%" + // Expected order due to sorting: scalar_js_udf, scalar_sql_udf + ResultSet rsWildcard = databaseMetaData.getFunctions(testCatalog, testSchema, "scalar%"); + Assertions.assertTrue(rsWildcard.next(), "Should find functions matching 'scalar%'"); + Assertions.assertEquals("scalar_js_udf", rsWildcard.getString("FUNCTION_NAME")); + Assertions.assertEquals( + DatabaseMetaData.functionResultUnknown, rsWildcard.getShort("FUNCTION_TYPE")); + + Assertions.assertTrue(rsWildcard.next(), "Should find the second function matching 'scalar%'"); + Assertions.assertEquals("scalar_sql_udf", rsWildcard.getString("FUNCTION_NAME")); + Assertions.assertEquals( + DatabaseMetaData.functionResultUnknown, rsWildcard.getShort("FUNCTION_TYPE")); + Assertions.assertFalse(rsWildcard.next(), "Should be no more functions matching 'scalar%'"); + rsWildcard.close(); + + // Test 4: Schema pattern with wildcard + ResultSet rsSchemaWildcard = + databaseMetaData.getFunctions(testCatalog, "JDBC_TABLE_TYPES_T%", "complex_scalar_sql_udf"); + Assertions.assertTrue(rsSchemaWildcard.next(), "Should find function with schema wildcard"); + Assertions.assertEquals(testSchema, rsSchemaWildcard.getString("FUNCTION_SCHEM")); + Assertions.assertEquals("complex_scalar_sql_udf", rsSchemaWildcard.getString("FUNCTION_NAME")); + Assertions.assertFalse( + rsSchemaWildcard.next(), + "Should only find one row for this schema wildcard and specific function"); + rsSchemaWildcard.close(); + + // Test 5: Non-existent function + ResultSet rsNonExistentFunc = + databaseMetaData.getFunctions(testCatalog, testSchema, "non_existent_function_xyz123"); + Assertions.assertFalse(rsNonExistentFunc.next(), "Should not find a non-existent function"); + rsNonExistentFunc.close(); + + // Test 6: Non-existent schema + ResultSet rsNonExistentSchema = + databaseMetaData.getFunctions(testCatalog, "NON_EXISTENT_SCHEMA_XYZ123", null); + Assertions.assertFalse( + rsNonExistentSchema.next(), "Should not find functions in a non-existent schema"); + rsNonExistentSchema.close(); + + // Test 7: Empty schema pattern + ResultSet rsEmptySchema = databaseMetaData.getFunctions(testCatalog, "", null); + Assertions.assertFalse(rsEmptySchema.next(), "Empty schema pattern should return no results"); + rsEmptySchema.close(); + + // Test 8: Empty function name pattern + ResultSet rsEmptyFunction = databaseMetaData.getFunctions(testCatalog, testSchema, ""); + Assertions.assertFalse( + rsEmptyFunction.next(), "Empty function name pattern should return no results"); + rsEmptyFunction.close(); + + // Test 9: Null catalog + ResultSet rsNullCatalog = databaseMetaData.getFunctions(null, testSchema, null); + Assertions.assertFalse(rsNullCatalog.next(), "Null catalog should return no results"); + rsNullCatalog.close(); + } + + @org.junit.jupiter.api.Test + public void testDatabaseMetadataGetFunctionColumns() throws SQLException { + DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + String testCatalog = PROJECT_ID; + String testSchema = "JDBC_TABLE_TYPES_TEST"; + + // Test Case 1: Specific function 'scalar_sql_udf', specific column 'x' + String specificFunction1 = "scalar_sql_udf"; + String specificColumn1 = "x"; + ResultSet rs = + databaseMetaData.getFunctionColumns( + testCatalog, testSchema, specificFunction1, specificColumn1); + + Assertions.assertTrue(rs.next(), "Should find column 'x' for function 'scalar_sql_udf'"); + Assertions.assertEquals(testCatalog, rs.getString("FUNCTION_CAT")); + Assertions.assertEquals(testSchema, rs.getString("FUNCTION_SCHEM")); + Assertions.assertEquals(specificFunction1, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals(specificColumn1, rs.getString("COLUMN_NAME")); + Assertions.assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); + Assertions.assertEquals(Types.BIGINT, rs.getInt("DATA_TYPE")); + Assertions.assertEquals("BIGINT", rs.getString("TYPE_NAME")); + Assertions.assertEquals(19, rs.getInt("PRECISION")); + Assertions.assertEquals(null, rs.getObject("LENGTH")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(0, rs.getShort("SCALE")); + Assertions.assertEquals(10, rs.getShort("RADIX")); + Assertions.assertEquals(DatabaseMetaData.functionNullableUnknown, rs.getShort("NULLABLE")); + assertNull(rs.getString("REMARKS")); + Assertions.assertEquals(null, rs.getObject("CHAR_OCTET_LENGTH")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(1, rs.getInt("ORDINAL_POSITION")); + Assertions.assertEquals("", rs.getString("IS_NULLABLE")); + Assertions.assertEquals(specificFunction1, rs.getString("SPECIFIC_NAME")); + Assertions.assertFalse(rs.next(), "Should only find one row for exact column match"); + rs.close(); + + // Test Case 2: Specific function 'complex_scalar_sql_udf', specific column 'arr' + String specificFunction2 = "complex_scalar_sql_udf"; + String specificColumn2 = "arr"; + rs = + databaseMetaData.getFunctionColumns( + testCatalog, testSchema, specificFunction2, specificColumn2); + Assertions.assertTrue( + rs.next(), "Should find column 'arr' for function 'complex_scalar_sql_udf'"); + Assertions.assertEquals(testCatalog, rs.getString("FUNCTION_CAT")); + Assertions.assertEquals(testSchema, rs.getString("FUNCTION_SCHEM")); + Assertions.assertEquals(specificFunction2, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals(specificColumn2, rs.getString("COLUMN_NAME")); + Assertions.assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); + Assertions.assertEquals(Types.ARRAY, rs.getInt("DATA_TYPE")); + Assertions.assertEquals("ARRAY", rs.getString("TYPE_NAME")); + Assertions.assertEquals(null, rs.getObject("PRECISION")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(null, rs.getObject("LENGTH")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(null, rs.getObject("SCALE")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(null, rs.getObject("RADIX")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(DatabaseMetaData.functionNullableUnknown, rs.getShort("NULLABLE")); + assertNull(rs.getString("REMARKS")); + Assertions.assertEquals(null, rs.getObject("CHAR_OCTET_LENGTH")); + Assertions.assertTrue(rs.wasNull()); + Assertions.assertEquals(1, rs.getInt("ORDINAL_POSITION")); + Assertions.assertEquals("", rs.getString("IS_NULLABLE")); + Assertions.assertEquals(specificFunction2, rs.getString("SPECIFIC_NAME")); + Assertions.assertFalse(rs.next(), "Should only find one row for exact column match"); + rs.close(); + + // Test Case 3: All columns for 'persistent_sql_udf_named_params' (sorted by ordinal position) + String specificFunction3 = "persistent_sql_udf_named_params"; + rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, specificFunction3, null); + Assertions.assertTrue(rs.next(), "Should find columns for " + specificFunction3); + Assertions.assertEquals(specificFunction3, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals("value1", rs.getString("COLUMN_NAME")); // Ordinal Position 1 + Assertions.assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); + Assertions.assertEquals(Types.BIGINT, rs.getInt("DATA_TYPE")); + Assertions.assertEquals("BIGINT", rs.getString("TYPE_NAME")); + Assertions.assertEquals(1, rs.getInt("ORDINAL_POSITION")); + + Assertions.assertTrue(rs.next(), "Should find second column for " + specificFunction3); + Assertions.assertEquals(specificFunction3, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals("value-two", rs.getString("COLUMN_NAME")); // Ordinal Position 2 + Assertions.assertEquals(DatabaseMetaData.functionColumnUnknown, rs.getShort("COLUMN_TYPE")); + Assertions.assertEquals(Types.NVARCHAR, rs.getInt("DATA_TYPE")); + Assertions.assertEquals("NVARCHAR", rs.getString("TYPE_NAME")); + Assertions.assertEquals(2, rs.getInt("ORDINAL_POSITION")); + Assertions.assertFalse(rs.next(), "Should be no more columns for " + specificFunction3); + rs.close(); + + // Test Case 4: Wildcard for function name "scalar%", specific column name "x" + rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, "scalar%", "x"); + Assertions.assertTrue(rs.next(), "Should find column 'x' for functions matching 'scalar%'"); + Assertions.assertEquals("scalar_sql_udf", rs.getString("FUNCTION_NAME")); + Assertions.assertEquals("x", rs.getString("COLUMN_NAME")); + Assertions.assertEquals(1, rs.getInt("ORDINAL_POSITION")); + Assertions.assertFalse( + rs.next(), "Should be no more columns named 'x' for functions matching 'scalar%'"); + rs.close(); + + // Test Case 5: Wildcard for column name "%" for 'scalar_js_udf' + String specificFunction4 = "scalar_js_udf"; + rs = databaseMetaData.getFunctionColumns(testCatalog, testSchema, specificFunction4, "%"); + Assertions.assertTrue( + rs.next(), "Should find columns for " + specificFunction4 + " with wildcard"); + Assertions.assertEquals(specificFunction4, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals("name", rs.getString("COLUMN_NAME")); // Ordinal Position 1 + Assertions.assertEquals(1, rs.getInt("ORDINAL_POSITION")); + + Assertions.assertTrue( + rs.next(), "Should find second column for " + specificFunction4 + " with wildcard"); + Assertions.assertEquals(specificFunction4, rs.getString("FUNCTION_NAME")); + Assertions.assertEquals("age", rs.getString("COLUMN_NAME")); // Ordinal Position 2 + Assertions.assertEquals(2, rs.getInt("ORDINAL_POSITION")); + Assertions.assertFalse( + rs.next(), "Should be no more columns for " + specificFunction4 + " with wildcard"); + rs.close(); + + // Test Case 6: Non-existent function + rs = + databaseMetaData.getFunctionColumns( + testCatalog, testSchema, "non_existent_function_xyz", null); + Assertions.assertFalse(rs.next(), "Should not find columns for a non-existent function"); + rs.close(); + } + + @org.junit.jupiter.api.Test + public void testAdditionalProjectsInMetadata() throws SQLException { + String additionalProjectsValue = "bigquery-public-data"; + String datasetInAdditionalProject = "baseball"; + + String urlWithAdditionalProjects = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" + + PROJECT_ID + + ";OAuthType=3" + + ";AdditionalProjects=" + + additionalProjectsValue; + + try (Connection conn = DriverManager.getConnection(urlWithAdditionalProjects)) { + DatabaseMetaData dbMetaData = conn.getMetaData(); + + // 1. Test getCatalogs() + Set foundCatalogs = new HashSet<>(); + try (ResultSet catalogsRs = dbMetaData.getCatalogs()) { + while (catalogsRs.next()) { + foundCatalogs.add(catalogsRs.getString("TABLE_CAT")); + } + } + Assertions.assertTrue( + foundCatalogs.contains(PROJECT_ID), + "getCatalogs() should contain the primary project ID"); + Assertions.assertTrue( + foundCatalogs.contains(additionalProjectsValue), + "getCatalogs() should contain the additional project ID"); + + // 2. Test getSchemas() + Set catalogsForSchemasFromAll = new HashSet<>(); + boolean foundAdditionalDataset = false; + try (ResultSet schemasRs = dbMetaData.getSchemas()) { + while (schemasRs.next()) { + String schemaName = schemasRs.getString("TABLE_SCHEM"); + String catalogName = schemasRs.getString("TABLE_CATALOG"); + catalogsForSchemasFromAll.add(catalogName); + if (additionalProjectsValue.equals(catalogName) + && datasetInAdditionalProject.equals(schemaName)) { + foundAdditionalDataset = true; + } + } + } + Assertions.assertTrue( + catalogsForSchemasFromAll.contains(PROJECT_ID), + "getSchemas() should list datasets from the primary project"); + Assertions.assertTrue( + catalogsForSchemasFromAll.contains(additionalProjectsValue), + "getSchemas() should list datasets from the additional project"); + Assertions.assertTrue( + foundAdditionalDataset, + "Known dataset from additional project not found in getSchemas()"); + + } catch (SQLException e) { + System.err.println("SQL Error during AdditionalProjects test: " + e.getMessage()); + throw e; + } + } + + @org.junit.jupiter.api.Test + public void testFilterTablesOnDefaultDataset_getTables() throws SQLException { + String defaultDatasetValue = CONSTRAINTS_DATASET; + String table1InDefaultDataset = CONSTRAINTS_TABLE_NAME; + String table2InDefaultDataset = CONSTRAINTS_TABLE_NAME2; + + String specificDatasetValue = "JDBC_TABLE_TYPES_TEST"; + String table1InSpecificDataset = "base_table"; + String table2InSpecificDataset = "external_table"; + + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" + + PROJECT_ID + + ";OAuthType=3" + + ";DefaultDataset=" + + defaultDatasetValue + + ";FilterTablesOnDefaultDataset=1"; + try (Connection conn = DriverManager.getConnection(connectionUrl)) { + DatabaseMetaData dbMetaData = conn.getMetaData(); + + // Case 1: Catalog and schemaPattern are null/wildcard, should use DefaultDataset + try (ResultSet rs = dbMetaData.getTables(null, null, null, null)) { + Set tableNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); + tableNames.add(rs.getString("TABLE_NAME")); + } + Assertions.assertTrue(tableNames.contains(table1InDefaultDataset)); + Assertions.assertTrue(tableNames.contains(table2InDefaultDataset)); + } + + // Case 2: Explicit schemaPattern overrides DefaultDataset + try (ResultSet rs = dbMetaData.getTables(null, specificDatasetValue, null, null)) { + Set tableNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); + tableNames.add(rs.getString("TABLE_NAME")); + } + Assertions.assertTrue(tableNames.contains(table1InSpecificDataset)); + Assertions.assertTrue(tableNames.contains(table2InSpecificDataset)); + } + + // Case 3: Explicit catalog, schemaPattern is null/wildcard, should use DefaultDataset within + // that catalog + try (ResultSet rs = dbMetaData.getTables(PROJECT_ID, null, null, null)) { + Set tableNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); + tableNames.add(rs.getString("TABLE_NAME")); + } + Assertions.assertTrue(tableNames.contains(table1InDefaultDataset)); + Assertions.assertTrue(tableNames.contains(table2InDefaultDataset)); + } + + // Case 4: Explicit catalog and schemaPattern override DefaultDataset + try (ResultSet rs = dbMetaData.getTables(PROJECT_ID, specificDatasetValue, null, null)) { + Set tableNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); + tableNames.add(rs.getString("TABLE_NAME")); + } + Assertions.assertTrue(tableNames.contains(table1InSpecificDataset)); + Assertions.assertTrue(tableNames.contains(table2InSpecificDataset)); + } + } + } + + @org.junit.jupiter.api.Test + public void testFilterTablesOnDefaultDataset_getColumns() throws SQLException { + String defaultDatasetValue = CONSTRAINTS_DATASET; + String tableInDefaultDataset = CONSTRAINTS_TABLE_NAME; + String[] columnsInDefaultTable = {"id", "name", "second_name", "address"}; + + String specificDatasetValue = "JDBC_TABLE_TYPES_TEST"; + String tableInSpecificDataset = "base_table"; + String[] columnsInSpecificTable = {"id", "name", "created_at"}; + + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" + + PROJECT_ID + + ";OAuthType=3" + + ";DefaultDataset=" + + defaultDatasetValue + + ";FilterTablesOnDefaultDataset=1"; + + try (Connection conn = DriverManager.getConnection(connectionUrl)) { + DatabaseMetaData dbMetaData = conn.getMetaData(); + + // Case 1: Catalog and schemaPattern are null/wildcard, should use DefaultDataset + try (ResultSet rs = dbMetaData.getColumns(null, null, tableInDefaultDataset, null)) { + Set columnNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); + Assertions.assertEquals(tableInDefaultDataset, rs.getString("TABLE_NAME")); + columnNames.add(rs.getString("COLUMN_NAME")); + } + for (String expectedCol : columnsInDefaultTable) { + Assertions.assertTrue(columnNames.contains(expectedCol)); + } + Assertions.assertEquals(columnsInDefaultTable.length, columnNames.size()); + } + + // Case 2: Explicit schemaPattern overrides DefaultDataset + try (ResultSet rs = + dbMetaData.getColumns(null, specificDatasetValue, tableInSpecificDataset, null)) { + Set columnNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); + Assertions.assertEquals(tableInSpecificDataset, rs.getString("TABLE_NAME")); + columnNames.add(rs.getString("COLUMN_NAME")); + } + for (String expectedCol : columnsInSpecificTable) { + Assertions.assertTrue(columnNames.contains(expectedCol)); + } + Assertions.assertEquals(columnsInSpecificTable.length, columnNames.size()); + } + + // Case 3: Explicit catalog, schemaPattern is null/wildcard, should use DefaultDataset within + // that catalog + try (ResultSet rs = dbMetaData.getColumns(PROJECT_ID, null, tableInDefaultDataset, null)) { + Set columnNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(defaultDatasetValue, rs.getString("TABLE_SCHEM")); + Assertions.assertEquals(tableInDefaultDataset, rs.getString("TABLE_NAME")); + columnNames.add(rs.getString("COLUMN_NAME")); + } + for (String expectedCol : columnsInDefaultTable) { + Assertions.assertTrue(columnNames.contains(expectedCol)); + } + Assertions.assertEquals(columnsInDefaultTable.length, columnNames.size()); + } + + // Case 4: Explicit catalog and schemaPattern override DefaultDataset + try (ResultSet rs = + dbMetaData.getColumns(PROJECT_ID, specificDatasetValue, tableInSpecificDataset, null)) { + Set columnNames = new HashSet<>(); + while (rs.next()) { + Assertions.assertEquals(PROJECT_ID, rs.getString("TABLE_CAT")); + Assertions.assertEquals(specificDatasetValue, rs.getString("TABLE_SCHEM")); + Assertions.assertEquals(tableInSpecificDataset, rs.getString("TABLE_NAME")); + columnNames.add(rs.getString("COLUMN_NAME")); + } + for (String expectedCol : columnsInSpecificTable) { + Assertions.assertTrue(columnNames.contains(expectedCol)); + } + Assertions.assertEquals(columnsInSpecificTable.length, columnNames.size()); + } + } + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java new file mode 100644 index 000000000000..74c8cc4f23dd --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.Random; +import org.junit.Test; + +public class ITDriverTest { + + private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + static Random random = new Random(); + static int randomNumber = random.nextInt(999); + String createDataset = "CREATE SCHEMA `%s.%s` OPTIONS(default_table_expiration_days = 5);"; + String createQuery = + "CREATE OR REPLACE TABLE `%s.%s.%s` " + + " (\n" + + "`StringField` STRING,\n" + + "`BytesField` BYTES,\n" + + "`IntegerField` INTEGER" + + ");"; + String insertQuery1 = + "INSERT INTO `%s.%s.%s` " + + " (StringField, BytesField,IntegerField) " + + "VALUES('string1', CAST('string1' AS BYTES),111);"; + + String selectQuery = "SELECT * FROM `%s.%s.%s` ;"; + + @Test + public void testGetDriverMethod() throws SQLException, InterruptedException { + String OAUTH_TYPE = "3"; + String CONNECTION_URL = + "jdbc:bigquery://https://bigquery.googleapis.com/bigquery/v2/:443;ProjectId=" + + DEFAULT_CATALOG + + ";OAuthType=" + + OAUTH_TYPE + + ";LOCATION=US;"; + + String dataset = "EXT_JDBC_DRIVER_TEST_DATASET" + random.nextInt(999); + String tableName = "EXT_JDBC_DRIVER_TEST_TABLE" + randomNumber; + + Driver driver = DriverManager.getDriver(CONNECTION_URL); + + Connection con = driver.connect(CONNECTION_URL, new Properties()); + assertTrue(driver.acceptsURL(CONNECTION_URL)); + assertFalse(driver.jdbcCompliant()); + assertEquals(1, driver.getMajorVersion()); + assertEquals(6, driver.getMinorVersion()); + + Statement statement = con.createStatement(); + statement.execute(String.format(createDataset, DEFAULT_CATALOG, dataset)); + statement.execute(String.format(createQuery, DEFAULT_CATALOG, dataset, tableName)); + boolean insertResult = + statement.execute(String.format(insertQuery1, DEFAULT_CATALOG, dataset, tableName)); + assertFalse(insertResult); + statement.execute(String.format(selectQuery, DEFAULT_CATALOG, dataset, tableName)); + ResultSet res = statement.getResultSet(); + assertTrue(res.next()); + ITBase.cleanUp(dataset); + con.close(); + } + + @Test + public void testDriverLocation() throws SQLException, InterruptedException { + + String datasetUS = "EXT_JDBC_DRIVER_US_TEST_DATASET" + random.nextInt(999); + String tableNameUS = "EXT_JDBC_DRIVER_US_TEST_TABLE" + randomNumber; + String OAUTH_TYPE = "3"; + String CONNECTION_URL = + "jdbc:bigquery://https://bigquery.googleapis.com/bigquery/v2/:443;ProjectId=%s;OAuthType=%s;LOCATION=%s;"; + + // US Connection + Connection connectionUS = + DriverManager.getConnection( + String.format(CONNECTION_URL, DEFAULT_CATALOG, OAUTH_TYPE, "us-east5")); + Statement statementUS = connectionUS.createStatement(); + statementUS.execute(String.format(createDataset, DEFAULT_CATALOG, datasetUS)); + statementUS.execute(String.format(createQuery, DEFAULT_CATALOG, datasetUS, tableNameUS)); + boolean insertResult = + statementUS.execute(String.format(insertQuery1, DEFAULT_CATALOG, datasetUS, tableNameUS)); + assertFalse(insertResult); + statementUS.execute(String.format(selectQuery, DEFAULT_CATALOG, datasetUS, tableNameUS)); + ResultSet res = statementUS.getResultSet(); + assertTrue(res.next()); + + BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); + Dataset retrievedDataset = bigQuery.getDataset(DatasetId.of(DEFAULT_CATALOG, datasetUS)); + assertEquals("us-east5", retrievedDataset.getLocation()); + ITBase.cleanUp(datasetUS); + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java new file mode 100644 index 000000000000..2f2ec8b4fa97 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.Assert.assertEquals; + +import com.google.cloud.ServiceOptions; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ITResultSetMetadataTest { + + private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + static Random random = new Random(); + static int randomNumber = random.nextInt(999); + private static final String TABLE_NAME = "EXT_JDBC_RSMETADATA_TEST_TABLE" + randomNumber; + private static final String DATASET = "EXT_JDBC_RSMETADATA_TEST_DATASET"; + private static ResultSetMetaData metaData; + + @BeforeClass + public static void beforeClass() throws InterruptedException { + ITBase.setUpDataset(DATASET); + ITBase.setUpTable(DATASET, TABLE_NAME); + } + + @AfterClass + public static void afterClass() throws InterruptedException { + ITBase.cleanUp(DATASET); + } + + @Test + public void testResultSetMetadata() throws SQLException { + String selectData = "SELECT * FROM " + DATASET + "." + TABLE_NAME + ";"; + Connection connection = + DriverManager.getConnection(String.format(ITBase.connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(selectData); + metaData = resultSet.getMetaData(); + try { + assertEquals(DEFAULT_CATALOG, metaData.getCatalogName(1)); + + assertEquals(14, metaData.getColumnCount()); + + assertEquals("StringField", metaData.getColumnName(1)); + assertEquals("StringField", metaData.getColumnLabel(1)); + assertEquals("java.lang.String", metaData.getColumnClassName(1)); + assertEquals(65535, metaData.getColumnDisplaySize(1)); + assertEquals("STRING", metaData.getColumnTypeName(1)); + assertEquals(12, metaData.getColumnType(1)); + + assertEquals(26, metaData.getColumnDisplaySize(8)); + assertEquals("TIMESTAMP", metaData.getColumnTypeName(8)); + assertEquals(93, metaData.getColumnType(8)); + + } catch (SQLException e) { + e.printStackTrace(); + } + connection.close(); + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java new file mode 100644 index 000000000000..b29e2206895c --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java @@ -0,0 +1,412 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc.it; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLDataException; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.util.Properties; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ITStatementTest { + private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); + private static final String DATASET = "JDBC_STATEMENT_TEST_DATASET"; + private static String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=%s;OAuthType=3;Timeout=3600;"; + private static Random random = new Random(); + private static int randomNumber = random.nextInt(999); + private static final String TABLE_NAME = "JDBC_STATEMENT_TEST_TABLE" + randomNumber; + + @BeforeClass + public static void beforeClass() throws InterruptedException { + ITBase.setUpDataset(DATASET); + ITBase.setUpTable(DATASET, TABLE_NAME); + } + + @AfterClass + public static void afterClass() throws InterruptedException { + ITBase.cleanUp(DATASET); + } + + @Test + public void testExecute() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + String selectQuery = "select * from " + DATASET + "." + TABLE_NAME; + + Boolean selectQueryReturn = statement.execute(selectQuery); + assertEquals(true, selectQueryReturn); + + ResultSet resultSet = statement.getResultSet(); + while (resultSet.next()) { + if (resultSet.getInt("IntegerField") == 111) { + assertEquals("string1", resultSet.getString("StringField")); + assertEquals("737472696E6731", resultSet.getString("BytesField")); + } + } + + assertEquals(0, statement.getQueryTimeout()); + assertEquals(ResultSet.FETCH_FORWARD, statement.getFetchDirection()); + assertNotEquals(ResultSet.FETCH_REVERSE, statement.getFetchDirection()); + assertNotEquals(ResultSet.FETCH_UNKNOWN, statement.getFetchDirection()); + + assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, statement.getResultSetHoldability()); + assertNotEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, statement.getResultSetHoldability()); + + assertEquals(ResultSet.TYPE_FORWARD_ONLY, statement.getResultSetType()); + assertNotEquals(ResultSet.TYPE_SCROLL_SENSITIVE, statement.getResultSetType()); + assertNotEquals(ResultSet.TYPE_SCROLL_INSENSITIVE, statement.getResultSetType()); + + assertEquals(ResultSet.CONCUR_READ_ONLY, statement.getResultSetConcurrency()); + assertNotEquals(ResultSet.CONCUR_UPDATABLE, statement.getResultSetConcurrency()); + + assertEquals(-1, statement.getUpdateCount()); + // assertEquals(-1L, statement.getLargeUpdateCount()); + + statement.close(); + connection.close(); + } + + @Test + public void testExecuteQuery() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + String selectQuery = + "SELECT * FROM bigquery-public-data.chicago_taxi_trips.taxi_trips LIMIT 1000;"; + + ResultSet selectQueryResult = statement.executeQuery(selectQuery); + ResultSet statementResult = statement.getResultSet(); + assertEquals(statementResult, selectQueryResult); + + assertEquals(0, statement.getFetchSize()); + + // setMaxRows Test + statement.setMaxRows(5); + ResultSet maxRowsResultSet = statement.executeQuery(selectQuery); + assertEquals(5, getSizeOfResultSet(maxRowsResultSet)); + + try { + statement.setMaxRows(0); + maxRowsResultSet = statement.executeQuery(selectQuery); + } catch (SQLDataException exception) { + assertTrue(true); + statement.close(); + } + + // TODO(note): setFetchSize not working + /*statement.setFetchSize(1); + ResultSet newResultSet = statement.executeQuery(selectQuery); + assertEquals(1, getSizeOfResultSet(newResultSet));*/ + + assertEquals(0, statement.getQueryTimeout()); + assertEquals(ResultSet.FETCH_FORWARD, statement.getFetchDirection()); + assertNotEquals(ResultSet.FETCH_REVERSE, statement.getFetchDirection()); + assertNotEquals(ResultSet.FETCH_UNKNOWN, statement.getFetchDirection()); + try { + statement.setFetchDirection(ResultSet.FETCH_REVERSE); + } catch (SQLFeatureNotSupportedException exception) { + assertTrue(true); + } + + assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, statement.getResultSetHoldability()); + assertNotEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, statement.getResultSetHoldability()); + + assertEquals(ResultSet.TYPE_FORWARD_ONLY, statement.getResultSetType()); + assertNotEquals(ResultSet.TYPE_SCROLL_SENSITIVE, statement.getResultSetType()); + assertNotEquals(ResultSet.TYPE_SCROLL_INSENSITIVE, statement.getResultSetType()); + + assertEquals(ResultSet.CONCUR_READ_ONLY, statement.getResultSetConcurrency()); + assertNotEquals(ResultSet.CONCUR_UPDATABLE, statement.getResultSetConcurrency()); + + assertEquals(-1, statement.getUpdateCount()); + assertEquals(-1L, statement.getLargeUpdateCount()); + statement.close(); + connection.close(); + } + + @Test + public void testExecuteUpdate() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + String TEMP_TABLE_NAME = "TEMP_DDL_STATEMENT_TABLE"; + String createQuery = + "CREATE OR REPLACE TABLE " + + DATASET + + "." + + TEMP_TABLE_NAME + + " (\n" + + "`StringField` STRING,\n" + + "`BytesField` BYTES,\n" + + "`IntegerField` INTEGER);"; + String alterQuery = + "ALTER TABLE " + DATASET + "." + TEMP_TABLE_NAME + " ADD COLUMN `NewField` INTEGER;"; + String updateQuery = + "UPDATE " + + DATASET + + "." + + TEMP_TABLE_NAME + + " SET StringField = 'string71' " + + " WHERE IntegerField = 333 " + + " ;"; + String deleteQuery = "DROP TABLE " + DATASET + "." + TEMP_TABLE_NAME; + String multiInsertQuery = + "INSERT INTO " + + DATASET + + "." + + TEMP_TABLE_NAME + + " (\n" + + " StringField, BytesField,IntegerField)\n" + + " VALUES ('string3',CAST ('string3' AS BYTES),333), " + + " ('string4',CAST ('string4' AS BYTES),444), " + + " ('string5',CAST ('string5' AS BYTES),555) " + + " ;"; + String selectQuery = "select * from " + DATASET + "." + TEMP_TABLE_NAME; + + Statement statement = connection.createStatement(); + int createCount = statement.executeUpdate(createQuery); + assertEquals(0, createCount); + + int insertCount = statement.executeUpdate(multiInsertQuery); + assertEquals(3, insertCount); + + int alterCount = statement.executeUpdate(alterQuery); + assertEquals(0, alterCount); + ResultSet resultSet = statement.executeQuery(selectQuery); + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + assertEquals(4, resultSetMetaData.getColumnCount()); + + int updateCount = statement.executeUpdate(updateQuery); + assertEquals(1, updateCount); + + int deleteCount = statement.executeUpdate(deleteQuery); + assertEquals(0, deleteCount); + + statement.close(); + connection.close(); + } + + @Test + public void testScript() throws SQLException { + String connection_uri = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID=" + + DEFAULT_CATALOG + + ";OAUTHTYPE=3"; + Properties withReadApi = new Properties(); + withReadApi.setProperty("EnableHighThroughputAPI", "1"); + Connection connection = DriverManager.getConnection(connection_uri, withReadApi); + Statement statement = connection.createStatement(); + String BASE_QUERY = + "SELECT * FROM bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2017 order by" + + " trip_distance asc LIMIT %s;"; + int expectedCnt = 500000; + String longQuery = String.format(BASE_QUERY, expectedCnt); + String longerQuery = String.format(BASE_QUERY, 700000); + String longerQuery2 = String.format(BASE_QUERY, 900000); + statement.execute(longQuery + longerQuery + longerQuery2); + ResultSet arrowResultSet = statement.getResultSet(); + assertEquals(500000, resultSetRowCount(arrowResultSet)); + arrowResultSet.close(); + connection.close(); + } + + private int resultSetRowCount(ResultSet resultSet) throws SQLException { + int rowCount = 0; + while (resultSet.next()) { + rowCount++; + } + return rowCount; + } + + @Test + public void testStringColumnLength() throws SQLException { + String projectId = DEFAULT_CATALOG; + String TABLE_NAME = "StringColumnLengthTable"; + String oauthType = "3"; // Google Application Credentials + int length = 10; + String connectionUrl = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=" + + projectId + + ";OAuthType=" + + oauthType + + ";Timeout=3600;" + + "StringColumnLength=" + + length + + ";"; + // + "EnableSession=1"; + Connection connection1 = DriverManager.getConnection(connectionUrl); + Statement statement = connection1.createStatement(); + + String createQuery = + "CREATE OR REPLACE TABLE " + + DATASET + + "." + + TABLE_NAME + + " (\n" + + "`StringField1` STRING,\n" + + "`StringField2` STRING,\n" + + "`StringField3` STRING \n" + + ");"; + String insertQuery = + "INSERT INTO %s.%s (StringField1, StringField2, StringField3) VALUES ('%s', '%s', '%s') ;"; + String selectQuery = String.format("SELECT * FROM %s.%s; ", DATASET, TABLE_NAME); + statement.execute(createQuery); + + String s1 = generateString(1111); + String s2 = generateString(11111); + String s3 = generateString(111111); + statement.execute(String.format(insertQuery, DATASET, TABLE_NAME, s1, s2, s3)); + ResultSet rs = statement.executeQuery(selectQuery); + ResultSetMetaData metadata = rs.getMetaData(); + int i = 0; + while (rs.next()) { + assertNotNull(rs.getString(1)); + assertNotNull(rs.getString(2)); + assertNotNull(rs.getString(3)); + } + } + + private String generateString(int len) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < len; i++) { + s = s.append("s"); + } + return s.toString(); + } + + @Test + public void testCloseStatement() throws SQLException { + String selectQuery = "select * from " + DATASET + "." + TABLE_NAME; + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + + assertFalse(statement.isClosed()); + assertFalse(statement.isCloseOnCompletion()); + statement.closeOnCompletion(); + assertTrue(statement.isCloseOnCompletion()); + ResultSet resultSet = statement.executeQuery(selectQuery); + resultSet.close(); + + assertTrue(statement.isClosed()); + statement = connection.createStatement(); + statement.close(); + assertTrue(statement.isClosed()); + connection.close(); + } + + @Test + public void testSetTimeout() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + + String selectQuery = + "SELECT views FROM bigquery-public-data.wikipedia.pageviews_2020 WHERE datehour >=" + + " '2020-01-01' LIMIT 9000000"; + + // statement.execute(selectQuery); + assertEquals(0, statement.getQueryTimeout()); + statement.setQueryTimeout(1); + assertEquals(1, statement.getQueryTimeout()); + try { + statement.executeQuery(selectQuery); + } catch (SQLException e) { + assertTrue(true); + assertEquals("SQL execution canceled", e.getMessage()); + } + statement.close(); + connection.close(); + } + + @Test + public void testSetTimeoutThrows() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + assertThrows(SQLException.class, () -> statement.setQueryTimeout(-1)); + } + + @Test + public void testDefaultValues() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + assertEquals(0, statement.getMaxFieldSize()); + // assertEquals(0, statement.getLargeMaxRows()); + assertNull(statement.getWarnings()); + assertFalse(statement.isPoolable()); + statement.close(); + connection.close(); + } + + @Test + public void testRangeSelectDataset() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + Statement statement = connection.createStatement(); + + // execute + try { + statement.execute( + "CREATE TABLE " + + DEFAULT_CATALOG + + "." + + DATASET + + ".RangeTable (x RANGE OPTIONS (description = 'An optional RANGE" + + " field'), y STRUCT > OPTIONS (description = 'An array of" + + " RANGE field')>);"); + ResultSet selectQueryResult = + statement.executeQuery( + "SELECT * FROM " + String.format(" `%s.%s.RangeTable`;", DEFAULT_CATALOG, DATASET)); + + assertEquals(selectQueryResult.getMetaData().getColumnTypeName(1), "RANGE"); + } finally { + // clean up + statement.execute( + String.format("DROP TABLE IF EXISTS %s.%s.RangeTable;", DEFAULT_CATALOG, DATASET)); + statement.close(); + } + connection.close(); + } + + int getSizeOfResultSet(ResultSet resultSet) throws SQLException { + int count = 0; + while (resultSet.next()) { + count++; + } + return count; + } +} From 3cda27fe901105fad00a2344adc1aeb9bbac1464 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 10:24:51 -0400 Subject: [PATCH 2/7] fix header year --- .../google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java | 2 +- .../com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java | 2 +- .../google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java | 2 +- .../java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java | 2 +- .../google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java | 2 +- .../java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java index 46e64258c34a..9c6b0aa09974 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java index 2c316b46ff5e..5a8d7e663fd7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java index 9b9bb9138640..61c130327e2e 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java index 74c8cc4f23dd..b1d0daeddb57 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java index 2f2ec8b4fa97..79f061894edb 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java index b29e2206895c..1285b7e56a82 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITStatementTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 12daf8c12853ac3224734c0f4554f1b40632d889 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 10:31:58 -0400 Subject: [PATCH 3/7] chore: update gemini suggestions --- .../java/com/google/cloud/bigquery/jdbc/it/ITBase.java | 5 ++--- .../cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java | 3 +++ .../google/cloud/bigquery/jdbc/it/ITConnectionTest.java | 4 ++-- .../com/google/cloud/bigquery/jdbc/it/ITDriverTest.java | 9 +++++---- .../cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java index 8ec77822b17f..0582e48e5176 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java @@ -170,9 +170,8 @@ protected int resultSetRowCount(ResultSet resultSet) throws SQLException { public static List getInfoBySQL(Connection connection, String sqlCmd) throws SQLException { List result = new ArrayList<>(); - try { - Statement st = connection.createStatement(); - ResultSet rs = st.executeQuery(sqlCmd); + try (Statement st = connection.createStatement(); + ResultSet rs = st.executeQuery(sqlCmd)) { while (rs.next()) { result.add(rs.getString(1)); } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java index 9c6b0aa09974..ea53aeb84a29 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java @@ -352,5 +352,8 @@ private void assertConnectionPoolingResults(String connectionURL, Long connectio assertEquals(1, listener.getConnectionPoolCurrentCapacity()); assertEquals(connectionPoolSize, listener.getConnectionPoolSize()); pooledConnection.close(); + jsonResultSet.close(); + statement.close(); + connection.close(); } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java index 5a8d7e663fd7..a4921224ea3d 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionTest.java @@ -42,11 +42,11 @@ public class ITConnectionTest { - private static final String DATASET = "EXT_JDBC_CONNECTION_TEST_DATASET"; + private static final String DATASET = "JDBC_CONNECTION_TEST_DATASET"; private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); static Random random = new Random(); static int randomNumber = random.nextInt(999); - private static final String TABLE_NAME = "EXT_JDBC_CONNECTION_TEST_TABLE" + randomNumber; + private static final String TABLE_NAME = "JDBC_CONNECTION_TEST_TABLE" + randomNumber; private static String connectionUrl = "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=%s;OAuthType=3;Timeout=3600;"; diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java index b1d0daeddb57..d0e17e75e46e 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDriverTest.java @@ -65,8 +65,8 @@ public void testGetDriverMethod() throws SQLException, InterruptedException { + OAUTH_TYPE + ";LOCATION=US;"; - String dataset = "EXT_JDBC_DRIVER_TEST_DATASET" + random.nextInt(999); - String tableName = "EXT_JDBC_DRIVER_TEST_TABLE" + randomNumber; + String dataset = "JDBC_DRIVER_TEST_DATASET" + random.nextInt(999); + String tableName = "JDBC_DRIVER_TEST_TABLE" + randomNumber; Driver driver = DriverManager.getDriver(CONNECTION_URL); @@ -92,8 +92,8 @@ public void testGetDriverMethod() throws SQLException, InterruptedException { @Test public void testDriverLocation() throws SQLException, InterruptedException { - String datasetUS = "EXT_JDBC_DRIVER_US_TEST_DATASET" + random.nextInt(999); - String tableNameUS = "EXT_JDBC_DRIVER_US_TEST_TABLE" + randomNumber; + String datasetUS = "JDBC_DRIVER_US_TEST_DATASET" + random.nextInt(999); + String tableNameUS = "JDBC_DRIVER_US_TEST_TABLE" + randomNumber; String OAUTH_TYPE = "3"; String CONNECTION_URL = "jdbc:bigquery://https://bigquery.googleapis.com/bigquery/v2/:443;ProjectId=%s;OAuthType=%s;LOCATION=%s;"; @@ -116,5 +116,6 @@ public void testDriverLocation() throws SQLException, InterruptedException { Dataset retrievedDataset = bigQuery.getDataset(DatasetId.of(DEFAULT_CATALOG, datasetUS)); assertEquals("us-east5", retrievedDataset.getLocation()); ITBase.cleanUp(datasetUS); + connectionUS.close(); } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java index 79f061894edb..cbf475868931 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITResultSetMetadataTest.java @@ -35,8 +35,8 @@ public class ITResultSetMetadataTest { private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); static Random random = new Random(); static int randomNumber = random.nextInt(999); - private static final String TABLE_NAME = "EXT_JDBC_RSMETADATA_TEST_TABLE" + randomNumber; - private static final String DATASET = "EXT_JDBC_RSMETADATA_TEST_DATASET"; + private static final String TABLE_NAME = "JDBC_RSMETADATA_TEST_TABLE" + randomNumber; + private static final String DATASET = "JDBC_RSMETADATA_TEST_DATASET"; private static ResultSetMetaData metaData; @BeforeClass From bc23be16cacbf391e1bbef6eced8572dcb9a7cd5 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 10:55:37 -0400 Subject: [PATCH 4/7] add new tests to suite --- .../jdbc/it/suites/ITDriverAgnosticTests.java | 12 +++++++++++- .../jdbc/it/suites/ITPresubmitTests.java | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITDriverAgnosticTests.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITDriverAgnosticTests.java index fe20abcbb2c3..95239049edbc 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITDriverAgnosticTests.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITDriverAgnosticTests.java @@ -17,9 +17,19 @@ package com.google.cloud.bigquery.jdbc.it.suites; import com.google.cloud.bigquery.jdbc.it.ITAuthTests; +import com.google.cloud.bigquery.jdbc.it.ITConnectionTest; +import com.google.cloud.bigquery.jdbc.it.ITDatabaseMetadataTest; +import com.google.cloud.bigquery.jdbc.it.ITDriverTest; +import com.google.cloud.bigquery.jdbc.it.ITResultSetMetadataTest; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.Suite; @Suite -@SelectClasses({ITAuthTests.class}) +@SelectClasses({ + ITAuthTests.class, + ITConnectionTest.class, + ITDatabaseMetadataTest.class, + ITDriverTest.class, + ITResultSetMetadataTest.class +}) public class ITDriverAgnosticTests {} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITPresubmitTests.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITPresubmitTests.java index 5f0517f17399..90a569cc55cd 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITPresubmitTests.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITPresubmitTests.java @@ -18,9 +18,24 @@ import com.google.cloud.bigquery.jdbc.it.ITAuthTests; import com.google.cloud.bigquery.jdbc.it.ITBigQueryJDBCTest; +import com.google.cloud.bigquery.jdbc.it.ITCallableStatementTest; +import com.google.cloud.bigquery.jdbc.it.ITConnectionPoolingTest; +import com.google.cloud.bigquery.jdbc.it.ITConnectionTest; +import com.google.cloud.bigquery.jdbc.it.ITDatabaseMetadataTest; +import com.google.cloud.bigquery.jdbc.it.ITDriverTest; +import com.google.cloud.bigquery.jdbc.it.ITResultSetMetadataTest; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.Suite; @Suite -@SelectClasses({ITAuthTests.class, ITBigQueryJDBCTest.class}) +@SelectClasses({ + ITAuthTests.class, + ITBigQueryJDBCTest.class, + ITCallableStatementTest.class, + ITConnectionTest.class, + ITConnectionPoolingTest.class, + ITDatabaseMetadataTest.class, + ITDriverTest.class, + ITResultSetMetadataTest.class +}) public class ITPresubmitTests {} From ffc7b72c71848c3ef3a86c4e5051c58f748f56d6 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 11:49:23 -0400 Subject: [PATCH 5/7] chore: fail build on test failure --- java-bigquery/google-cloud-bigquery-jdbc/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/Makefile b/java-bigquery/google-cloud-bigquery-jdbc/Makefile index 46ddae268aeb..7be6b71bcb81 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/Makefile +++ b/java-bigquery/google-cloud-bigquery-jdbc/Makefile @@ -41,7 +41,8 @@ integration-test: -Denforcer.skip=true \ -Dit.failIfNoSpecifiedTests=true \ -Dit.test=$(test) \ - integration-test + integration-test \ + verify unit-test-coverage: $(MAKE) unittest From 1eecf840609ad3194066472169d1e2d1e9fe21e6 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 7 Apr 2026 12:55:20 -0400 Subject: [PATCH 6/7] fix dbmetadata tests --- .../jdbc/it/ITDatabaseMetadataTest.java | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java index 61c130327e2e..e835320a4945 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java @@ -29,11 +29,9 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Types; import java.util.Arrays; import java.util.HashSet; -import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.regex.Pattern; @@ -56,10 +54,6 @@ public class ITDatabaseMetadataTest extends ITBase { private static final String CONSTRAINTS_TABLE_NAME = "JDBC_CONSTRAINTS_TEST_TABLE"; private static final String CONSTRAINTS_TABLE_NAME2 = "JDBC_CONSTRAINTS_TEST_TABLE2"; private static final String CONSTRAINTS_TABLE_NAME3 = "JDBC_CONSTRAINTS_TEST_TABLE3"; - - static Connection bigQueryConnection; - static Statement bigQueryStatement; - private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.\\d+)+\\s*.*"); private static final String DEFAULT_CATALOG = ServiceOptions.getDefaultProjectId(); @@ -71,15 +65,10 @@ public class ITDatabaseMetadataTest extends ITBase { public static void beforeClass() throws InterruptedException, SQLException { // Set up Dataset ITBase.setUpTable(DATASET, TABLE_NAME); - bigQueryConnection = DriverManager.getConnection(connection_uri, new Properties()); - bigQueryStatement = bigQueryConnection.createStatement(); } @AfterClass - public static void afterClass() throws SQLException { - bigQueryStatement.close(); - bigQueryConnection.close(); - } + public static void afterClass() throws SQLException {} @Test public void testGetCatalogs() throws SQLException { @@ -284,10 +273,12 @@ public void testGetPrimaryKeys() throws SQLException { connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testTableConstraints() throws SQLException { + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); ResultSet primaryKey1 = - bigQueryConnection + connection .getMetaData() .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); primaryKey1.next(); @@ -295,7 +286,7 @@ public void testTableConstraints() throws SQLException { Assertions.assertFalse(primaryKey1.next()); ResultSet primaryKey2 = - bigQueryConnection + connection .getMetaData() .getPrimaryKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME2); primaryKey2.next(); @@ -305,7 +296,7 @@ public void testTableConstraints() throws SQLException { Assertions.assertFalse(primaryKey2.next()); ResultSet foreignKeys = - bigQueryConnection + connection .getMetaData() .getImportedKeys(PROJECT_ID, CONSTRAINTS_DATASET, CONSTRAINTS_TABLE_NAME); foreignKeys.next(); @@ -323,7 +314,7 @@ public void testTableConstraints() throws SQLException { Assertions.assertFalse(foreignKeys.next()); ResultSet crossReference = - bigQueryConnection + connection .getMetaData() .getCrossReference( PROJECT_ID, @@ -340,11 +331,15 @@ public void testTableConstraints() throws SQLException { Assertions.assertEquals("last_name", crossReference.getString(4)); Assertions.assertEquals("second_name", crossReference.getString(8)); Assertions.assertFalse(crossReference.next()); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetCatalogs() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); try (ResultSet rs = databaseMetaData.getCatalogs()) { assertNotNull(rs, "ResultSet from getCatalogs() should not be null"); @@ -359,13 +354,17 @@ public void testDatabaseMetadataGetCatalogs() throws SQLException { PROJECT_ID, rs.getString("TABLE_CAT"), "Catalog name should match Project ID"); Assertions.assertFalse(rs.next(), "ResultSet should have no more rows"); } + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetProcedures() throws SQLException { + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); String DATASET = "JDBC_INTEGRATION_DATASET"; String procedureName = "create_customer"; - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet resultSet = databaseMetaData.getProcedures(PROJECT_ID, DATASET, procedureName); while (resultSet.next()) { Assertions.assertEquals(PROJECT_ID, resultSet.getString("PROCEDURE_CAT")); @@ -375,11 +374,16 @@ public void testDatabaseMetadataGetProcedures() throws SQLException { Assertions.assertEquals( DatabaseMetaData.procedureResultUnknown, resultSet.getInt("PROCEDURE_TYPE")); } + resultSet.close(); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetProcedureColumns() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); // --- Test Case 1: Specific schema and procedure, null column name pattern --- String specificSchema = "JDBC_INTEGRATION_DATASET"; @@ -435,13 +439,17 @@ public void testDatabaseMetadataGetProcedureColumns() throws SQLException { Assertions.assertFalse( resultSet.next(), "Should not find columns for a non-existent procedure"); resultSet.close(); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetColumns() throws SQLException { + + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); String DATASET = "JDBC_INTEGRATION_DATASET"; String TABLE_NAME = "JDBC_DATATYPES_INTEGRATION_TEST_TABLE"; - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); // --- Test Case 1: Specific Column (StringField) --- ResultSet resultSet = @@ -623,11 +631,15 @@ public void testDatabaseMetadataGetColumns() throws SQLException { Assertions.assertEquals(1, resultSet.getInt("NULLABLE")); Assertions.assertEquals(14, resultSet.getInt("ORDINAL_POSITION")); Assertions.assertFalse(resultSet.next()); + + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetTables() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); String DATASET = "JDBC_TABLE_TYPES_TEST"; // --- Test Case 1: Get all tables (types = null) --- @@ -721,11 +733,14 @@ public void testDatabaseMetadataGetTables() throws SQLException { Assertions.assertEquals("VIEW", rsNullType.getString("TABLE_TYPE")); Assertions.assertEquals("my_view", rsNullType.getString("TABLE_NAME")); Assertions.assertFalse(rsNullType.next()); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetSchemas() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); // Test case 1: Get all schemas with catalog and check for the presence of specific schemas ResultSet rsAll = databaseMetaData.getSchemas(PROJECT_ID, null); @@ -755,12 +770,15 @@ public void testDatabaseMetadataGetSchemas() throws SQLException { // Test case 4: Get schemas with non-existent catalog rsNoMatch = databaseMetaData.getSchemas("invalid-catalog", null); Assertions.assertFalse(rsNoMatch.next()); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetSchemasNoArgs() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); - String expectedCatalog = bigQueryConnection.getCatalog(); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String expectedCatalog = connection.getCatalog(); assertNotNull(expectedCatalog, "Project ID (catalog) from connection should not be null"); // Test case: Get all schemas (datasets) for the current project @@ -788,11 +806,14 @@ public void testDatabaseMetadataGetSchemasNoArgs() throws SQLException { foundTestDataset, "At least one of the known test datasets should be found"); Assertions.assertTrue(rowCount > 0, "Should retrieve at least one schema/dataset"); } + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetaDataGetFunctions() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); String testSchema = "JDBC_TABLE_TYPES_TEST"; String testCatalog = PROJECT_ID; @@ -896,11 +917,14 @@ public void testDatabaseMetaDataGetFunctions() throws SQLException { ResultSet rsNullCatalog = databaseMetaData.getFunctions(null, testSchema, null); Assertions.assertFalse(rsNullCatalog.next(), "Null catalog should return no results"); rsNullCatalog.close(); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testDatabaseMetadataGetFunctionColumns() throws SQLException { - DatabaseMetaData databaseMetaData = bigQueryConnection.getMetaData(); + Connection connection = + DriverManager.getConnection(String.format(connectionUrl, DEFAULT_CATALOG)); + DatabaseMetaData databaseMetaData = connection.getMetaData(); String testCatalog = PROJECT_ID; String testSchema = "JDBC_TABLE_TYPES_TEST"; @@ -1022,9 +1046,10 @@ public void testDatabaseMetadataGetFunctionColumns() throws SQLException { testCatalog, testSchema, "non_existent_function_xyz", null); Assertions.assertFalse(rs.next(), "Should not find columns for a non-existent function"); rs.close(); + connection.close(); } - @org.junit.jupiter.api.Test + @Test public void testAdditionalProjectsInMetadata() throws SQLException { String additionalProjectsValue = "bigquery-public-data"; String datasetInAdditionalProject = "baseball"; @@ -1083,8 +1108,9 @@ public void testAdditionalProjectsInMetadata() throws SQLException { } } - @org.junit.jupiter.api.Test + @Test public void testFilterTablesOnDefaultDataset_getTables() throws SQLException { + String defaultDatasetValue = CONSTRAINTS_DATASET; String table1InDefaultDataset = CONSTRAINTS_TABLE_NAME; String table2InDefaultDataset = CONSTRAINTS_TABLE_NAME2; @@ -1154,7 +1180,7 @@ public void testFilterTablesOnDefaultDataset_getTables() throws SQLException { } } - @org.junit.jupiter.api.Test + @Test public void testFilterTablesOnDefaultDataset_getColumns() throws SQLException { String defaultDatasetValue = CONSTRAINTS_DATASET; String tableInDefaultDataset = CONSTRAINTS_TABLE_NAME; From 65df93b56d504c136e23f8dec8bb84131803296b Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 9 Apr 2026 15:06:16 -0400 Subject: [PATCH 7/7] rearrage code --- .../google/cloud/bigquery/jdbc/it/ITBase.java | 524 ------------------ .../jdbc/it/ITDatabaseMetadataTest.java | 472 ++++++++++++++++ 2 files changed, 472 insertions(+), 524 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java index 0582e48e5176..af8beedf388c 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBase.java @@ -16,11 +16,6 @@ package com.google.cloud.bigquery.jdbc.it; -import static java.sql.Types.TIME; -import static java.sql.Types.TIMESTAMP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.google.cloud.ServiceOptions; @@ -29,18 +24,11 @@ import com.google.cloud.bigquery.QueryJobConfiguration; import com.google.cloud.bigquery.jdbc.BigQueryJdbcBaseTest; import java.sql.Connection; -import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.junit.Assert; public class ITBase extends BigQueryJdbcBaseTest { @@ -180,516 +168,4 @@ public static List getInfoBySQL(Connection connection, String sqlCmd) } return result; } - - protected void verifyAllBooleanMethods(DatabaseMetaData metaData) throws SQLException { - assertFalse(metaData.allProceduresAreCallable()); // false - assertTrue(metaData.allTablesAreSelectable()); // true - - assertFalse(metaData.nullsAreSortedHigh()); // false - assertTrue(metaData.nullsAreSortedLow()); // true - assertFalse(metaData.nullsAreSortedAtEnd()); // false - assertFalse(metaData.nullsAreSortedAtStart()); // false - assertTrue(metaData.nullPlusNonNullIsNull()); // true - - assertFalse(metaData.usesLocalFiles()); // false - assertFalse(metaData.usesLocalFilePerTable()); // false - assertFalse(metaData.storesUpperCaseIdentifiers()); // false - assertFalse(metaData.storesLowerCaseIdentifiers()); // false - assertTrue(metaData.supportsMixedCaseQuotedIdentifiers()); - assertFalse(metaData.storesUpperCaseQuotedIdentifiers()); // false - assertFalse(metaData.storesLowerCaseQuotedIdentifiers()); // false - assertFalse(metaData.storesMixedCaseQuotedIdentifiers()); // false - - assertFalse(metaData.supportsAlterTableWithAddColumn()); // false - assertFalse(metaData.supportsAlterTableWithDropColumn()); // false - assertTrue(metaData.supportsColumnAliasing()); - assertTrue(metaData.nullPlusNonNullIsNull()); - assertFalse(metaData.supportsConvert()); // false - assertFalse(metaData.supportsConvert(TIME, TIMESTAMP)); // false - assertTrue(metaData.supportsTableCorrelationNames()); - assertTrue(metaData.supportsExpressionsInOrderBy()); - assertFalse(metaData.supportsOrderByUnrelated()); // false - assertTrue(metaData.supportsGroupBy()); - assertFalse(metaData.supportsGroupByUnrelated()); // false - assertTrue(metaData.supportsGroupByBeyondSelect()); - - assertTrue(metaData.supportsMultipleTransactions()); - assertFalse(metaData.supportsNonNullableColumns()); - assertTrue(metaData.supportsMinimumSQLGrammar()); - assertTrue(metaData.supportsCoreSQLGrammar()); - assertFalse(metaData.supportsExtendedSQLGrammar()); - assertTrue(metaData.supportsANSI92EntryLevelSQL()); - assertFalse(metaData.supportsANSI92IntermediateSQL()); - assertFalse(metaData.supportsANSI92FullSQL()); - assertFalse(metaData.supportsFullOuterJoins()); - assertFalse(metaData.supportsLimitedOuterJoins()); - assertTrue(metaData.isCatalogAtStart()); - assertTrue(metaData.supportsSchemasInDataManipulation()); - - assertTrue(metaData.supportsSchemasInTableDefinitions()); - assertTrue(metaData.supportsSchemasInIndexDefinitions()); - assertTrue(metaData.supportsSchemasInPrivilegeDefinitions()); - assertTrue(metaData.supportsCatalogsInDataManipulation()); - assertFalse(metaData.supportsPositionedDelete()); - assertFalse(metaData.supportsPositionedUpdate()); - - assertFalse(metaData.supportsSelectForUpdate()); - assertTrue(metaData.supportsStoredProcedures()); - assertTrue(metaData.supportsSubqueriesInComparisons()); - assertTrue(metaData.supportsSubqueriesInExists()); - assertTrue(metaData.supportsSubqueriesInIns()); - assertTrue(metaData.supportsSubqueriesInQuantifieds()); - assertTrue(metaData.supportsCorrelatedSubqueries()); - assertFalse(metaData.supportsOpenCursorsAcrossCommit()); - - assertFalse(metaData.supportsOpenCursorsAcrossRollback()); - assertTrue(metaData.supportsOpenStatementsAcrossCommit()); - assertTrue(metaData.supportsOpenStatementsAcrossRollback()); - - assertFalse( - metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); // f - - assertFalse( - metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); // f - assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE)); // f - assertFalse(metaData.supportsDifferentTableCorrelationNames()); // f - - assertFalse(metaData.dataDefinitionIgnoredInTransactions()); - assertFalse(metaData.doesMaxRowSizeIncludeBlobs()); - - assertTrue(metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)); // t - assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)); // f - assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)); // f - - assertTrue( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); // t - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); // f - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)); - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)); - - assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_FORWARD_ONLY)); - assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); - - assertTrue(metaData.supportsBatchUpdates()); - assertFalse(metaData.supportsSavepoints()); - assertFalse(metaData.supportsNamedParameters()); - assertFalse(metaData.supportsMultipleOpenResults()); - assertFalse(metaData.supportsGetGeneratedKeys()); - - assertFalse(metaData.generatedKeyAlwaysReturned()); - assertFalse(metaData.supportsIntegrityEnhancementFacility()); - assertFalse(metaData.supportsDataDefinitionAndDataManipulationTransactions()); - assertFalse(metaData.isReadOnly()); - } - - protected void verifyIntMethods(DatabaseMetaData metaData) throws SQLException { - assertEquals(0, metaData.getMaxBinaryLiteralLength()); // 0 - assertEquals(0, metaData.getMaxCharLiteralLength()); // 0 - assertEquals(0, metaData.getMaxColumnsInGroupBy()); // 0 - assertEquals(0, metaData.getMaxColumnsInIndex()); // 0 - assertEquals(0, metaData.getMaxColumnsInOrderBy()); // 0 - assertEquals(0, metaData.getMaxColumnsInSelect()); // 0 - assertEquals(0, metaData.getMaxConnections()); // 0 - assertEquals(0, metaData.getMaxCursorNameLength()); // 0 - assertEquals(0, metaData.getMaxIndexLength()); // 0 - assertEquals(0, metaData.getMaxProcedureNameLength()); // 0 - assertEquals(0, metaData.getMaxRowSize()); // 0 - assertEquals(0, metaData.getMaxStatementLength()); // 0 - assertEquals(0, metaData.getMaxStatements()); // 0 - assertEquals(1000, metaData.getMaxTablesInSelect()); // 1000 - assertEquals(0, metaData.getMaxUserNameLength()); // 0 - } - - protected void verifyAllStringMethods(DatabaseMetaData metaData, String procedure) - throws SQLException { - assertEquals("`", metaData.getIdentifierQuoteString()); - assertTrue(procedure.equalsIgnoreCase(metaData.getProcedureTerm())); - } - - protected void verifyGetPrimaryKeys( - Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { - final String jdbctesttable = "JDBCTESTPRIMARYKEY"; - connection - .createStatement() - .execute( - "create or replace table " - + dataset - + "." - + jdbctesttable - + "(KEYCOLUMN int NOT NULL, SECONDCOLUMN string, THIRDCOLUMN timestamp);"); - ResultSet resultSet = metaData.getPrimaryKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); - assertFalse(resultSet.next()); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyTestProcedure(DatabaseMetaData metaData, String expectedProcedure) - throws SQLException { - assertEquals(expectedProcedure, metaData.getProcedureTerm()); - assertTrue(metaData.supportsStoredProcedures()); - ResultSet resultSet; - resultSet = metaData.getProcedureColumns("%", "%", "%", "%"); - assertEquals(0, resultSetRowCount(resultSet)); - resultSet = metaData.getProcedures("%", "%", "%"); - assertEquals(0, resultSetRowCount(resultSet)); - } - - protected void verifyDriverMetadata( - DatabaseMetaData metaData, - int expectedMajorVer, - int expectedMinorVersion, - String expectedDatabaseProductName, - String expectedDriverName, - Pattern versionPattern) - throws SQLException { - - assertEquals(expectedMajorVer, metaData.getJDBCMajorVersion()); - assertEquals(expectedMinorVersion, metaData.getJDBCMinorVersion()); - assertEquals(expectedDatabaseProductName, metaData.getDatabaseProductName()); - assertEquals(expectedDriverName, metaData.getDriverName()); - String driverVersion = metaData.getDriverVersion(); - Matcher m = versionPattern.matcher(driverVersion); - assertTrue(m.matches()); - int majorVersion = metaData.getDriverMajorVersion(); - int minorVersion = metaData.getDriverMinorVersion(); - assertEquals(Integer.parseInt(m.group(1)), majorVersion); - assertEquals(Integer.parseInt(m.group(2)), minorVersion); - } - - protected void verifyCatalogHelper( - DatabaseMetaData metaData, - String expectedCatalogSeparator, - String expectedCatalogTerm, - int expectedCatalogNameLength, - String defaultCatalog, - boolean isBeforeFirstSupported, - boolean isFirstSupported) - throws SQLException { - assertEquals(expectedCatalogSeparator, metaData.getCatalogSeparator()); - // Simba BQ JDBC driver maps Catalogs to projects - assertEquals(expectedCatalogTerm, metaData.getCatalogTerm()); - assertEquals(expectedCatalogNameLength, metaData.getMaxCatalogNameLength()); - - // This should return the project name of the connection - ResultSet resultSet = metaData.getCatalogs(); - - // Compares ResultSetMetadata. - // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); - if (isBeforeFirstSupported) { - assertTrue(resultSet.isBeforeFirst()); - } - - int count = 0; - boolean defaultCatalogFound = false; - while (resultSet.next()) { - if (count == 0 && isFirstSupported) { - assertTrue(resultSet.isFirst()); - } - ++count; - if (defaultCatalog.equals(resultSet.getString(1))) { - defaultCatalogFound = true; - break; - } - } - assertTrue(defaultCatalogFound); - resultSet.close(); - } - - protected void verifySchemaHelper( - Connection connection, - DatabaseMetaData metaData, - String schemaTerm, - int maxSchemaNameLength, - boolean isBeforeFirstSupported, - boolean isFirstSupported) - throws SQLException { - // Simba BQ JDBC driver maps Table Datasets to Schemas - assertEquals(schemaTerm, metaData.getSchemaTerm()); - assertEquals(maxSchemaNameLength, metaData.getMaxSchemaNameLength()); - // This should return all the datasets in the project of the connection - ResultSet resultSet = metaData.getSchemas(); - // Compares ResultSetMetadata. - // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); - if (isBeforeFirstSupported) { - assertTrue(resultSet.isBeforeFirst()); - } - - int count = 0; - Set allVisibleDatasets = new HashSet<>(); - while (resultSet.next()) { - allVisibleDatasets.add(resultSet.getString(1)); - if (count == 0 && isFirstSupported) { - assertTrue(resultSet.isFirst()); - } - ++count; - } - assertTrue(count >= 1); - List allAccessibleDatasets = - getInfoBySQL(connection, "SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA;"); - assertTrue(allVisibleDatasets.containsAll(allAccessibleDatasets)); - assertTrue(count >= allAccessibleDatasets.size()); - resultSet.close(); - } - - protected void verifyTableTypes(DatabaseMetaData metaData, Collection expectedTypes) - throws SQLException { - ResultSet resultSet = metaData.getTableTypes(); - Set types = new HashSet<>(); - while (resultSet.next()) { - String col = resultSet.getString(1); - types.add(col); - } - assertTrue(types.size() > 0); - assertTrue(types.containsAll(expectedTypes)); - } - - protected void verifyGetTables(Connection connection, DatabaseMetaData metaData, String dataset) - throws SQLException { - final String jdbctesttable = "JDBCTESTTABLE"; - final String jdbctestview = "JDBCTESTVIEW"; - - Statement statement = connection.createStatement(); - statement.execute( - String.format( - "create or replace table `%s.%s.%s` " - + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", - ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); - statement.execute( - String.format( - "create or replace view `%s.%s.%s` " + " as select 1 as VIEWCOLUMN;", - ITBase.DEFAULT_CATALOG, dataset, jdbctestview)); - - ResultSet resultSet; - - // Pattern match for table - resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"TABLE"}); - // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_TABLES); - Set tables = new HashSet<>(); - while (resultSet.next()) { - tables.add(resultSet.getString(3)); - } - assertTrue(tables.contains("JDBCTESTTABLE")); - - // exact match for tablename - resultSet = - metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, new String[] {"TABLE"}); - tables = new HashSet<>(); - while (resultSet.next()) { - tables.add(resultSet.getString(3)); - } - assertEquals(jdbctesttable, tables.iterator().next()); - - // Pattern match for view - resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"VIEW"}); - Set views = new HashSet<>(); - while (resultSet.next()) { - views.add(resultSet.getString(3)); - } - assertTrue(views.contains(jdbctestview)); - - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - connection.createStatement().execute("drop view if exists " + dataset + "." + jdbctestview); - } - - // GetTablePrivileges not supported for JDBC and ODBC datasource. - // Returns empty resultset - protected void verifyGetTablePrivileges( - Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { - final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; - - Statement statement = connection.createStatement(); - statement.execute( - String.format( - "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", - ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); - - ResultSet resultSet = - metaData.getTablePrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); - int cols = resultSet.getMetaData().getColumnCount(); - assertTrue(cols > 0); - int count = 0; - while (resultSet.next()) { - for (int i = 1; i <= cols; i++) { - Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); - Assert.assertNotNull(resultSet.getString(i)); - } - ++count; - } - assertEquals(0, count); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyGetColumnPrivileges( - Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { - final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; - - connection - .createStatement() - .execute( - "create or replace table " - + dataset - + "." - + jdbctesttable - + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); - - ResultSet resultSet = - metaData.getColumnPrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%"); - int cols = resultSet.getMetaData().getColumnCount(); - assertTrue(cols > 0); - int count = 0; - while (resultSet.next()) { - for (int i = 1; i <= cols; i++) { - Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); - Assert.assertNotNull(resultSet.getString(i)); - } - ++count; - } - assertEquals(0, count); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyForeignKeys(Connection connection, DatabaseMetaData metaData, String dataset) - throws SQLException { - final String jdbctesttable = "JDBCTESTTABLEFOREIGNKEYS"; - - connection - .createStatement() - .execute( - "create or replace table " - + dataset - + "." - + jdbctesttable - + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); - - ResultSet resultSet = metaData.getImportedKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); - int cols = resultSet.getMetaData().getColumnCount(); - assertTrue(cols > 0); - int count = 0; - while (resultSet.next()) { - for (int i = 1; i <= cols; i++) { - Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); - Assert.assertNotNull(resultSet.getString(i)); - } - ++count; - } - assertEquals(0, count); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyGetColumns(Connection connection, DatabaseMetaData metaData, String dataset) - throws SQLException { - final String jdbctesttable = "JDBCTESTCOLUMNS"; - Statement statement = connection.createStatement(); - statement.execute( - String.format( - "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", - ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); - ResultSet resultSet = - metaData.getColumns(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%COLUMN"); - int cols = resultSet.getMetaData().getColumnCount(); - assertTrue(cols > 0); - // Resultset is not empty - int count = 0; - while (resultSet.next()) { - for (int i = 1; i <= cols; i++) { - Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); - } - ++count; - } - resultSet.close(); - assertTrue(count > 0); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyVersionColumns( - Connection connection, - DatabaseMetaData metaData, - String dataset, - int expectedNumCols, - Collection expectedColNames) - throws SQLException { - final String jdbctesttable = "JDBCTESTCOLUMNS"; - connection - .createStatement() - .execute( - "create or replace table " - + dataset - + "." - + jdbctesttable - + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); - ResultSet resultSet = - metaData.getVersionColumns(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); - int numCols = resultSet.getMetaData().getColumnCount(); - assertEquals(expectedNumCols, numCols); - List colNames = new ArrayList<>(); - for (int i = 1; i <= numCols; i++) { - colNames.add(resultSet.getMetaData().getColumnName(i)); - } - assertTrue(colNames.containsAll(expectedColNames)); - resultSet.close(); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } - - protected void verifyIndexInfo(Connection connection, DatabaseMetaData metaData, String dataset) - throws SQLException { - final String jdbctesttable = "JDBCTESTTABLE"; - connection - .createStatement() - .execute( - "create or replace table " - + dataset - + "." - + jdbctesttable - + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); - ResultSet rs = - metaData.getIndexInfo(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, false, false); - assertFalse(rs.next()); // no index defined. - rs.close(); - connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); - } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java index e835320a4945..f9bf504d0f03 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITDatabaseMetadataTest.java @@ -16,6 +16,8 @@ package com.google.cloud.bigquery.jdbc.it; +import static java.sql.Types.TIME; +import static java.sql.Types.TIMESTAMP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -29,13 +31,18 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.sql.Types; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Random; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -1265,4 +1272,469 @@ public void testFilterTablesOnDefaultDataset_getColumns() throws SQLException { } } } + + protected void verifyAllBooleanMethods(DatabaseMetaData metaData) throws SQLException { + assertFalse(metaData.allProceduresAreCallable()); // false + assertTrue(metaData.allTablesAreSelectable()); // true + + assertFalse(metaData.nullsAreSortedHigh()); // false + assertTrue(metaData.nullsAreSortedLow()); // true + assertFalse(metaData.nullsAreSortedAtEnd()); // false + assertFalse(metaData.nullsAreSortedAtStart()); // false + assertTrue(metaData.nullPlusNonNullIsNull()); // true + + assertFalse(metaData.usesLocalFiles()); // false + assertFalse(metaData.usesLocalFilePerTable()); // false + assertFalse(metaData.storesUpperCaseIdentifiers()); // false + assertFalse(metaData.storesLowerCaseIdentifiers()); // false + assertTrue(metaData.supportsMixedCaseQuotedIdentifiers()); + assertFalse(metaData.storesUpperCaseQuotedIdentifiers()); // false + assertFalse(metaData.storesLowerCaseQuotedIdentifiers()); // false + assertFalse(metaData.storesMixedCaseQuotedIdentifiers()); // false + + assertFalse(metaData.supportsAlterTableWithAddColumn()); // false + assertFalse(metaData.supportsAlterTableWithDropColumn()); // false + assertTrue(metaData.supportsColumnAliasing()); + assertTrue(metaData.nullPlusNonNullIsNull()); + assertFalse(metaData.supportsConvert()); // false + assertFalse(metaData.supportsConvert(TIME, TIMESTAMP)); // false + assertTrue(metaData.supportsTableCorrelationNames()); + assertTrue(metaData.supportsExpressionsInOrderBy()); + assertFalse(metaData.supportsOrderByUnrelated()); // false + assertTrue(metaData.supportsGroupBy()); + assertFalse(metaData.supportsGroupByUnrelated()); // false + assertTrue(metaData.supportsGroupByBeyondSelect()); + + assertTrue(metaData.supportsMultipleTransactions()); + assertFalse(metaData.supportsNonNullableColumns()); + assertTrue(metaData.supportsMinimumSQLGrammar()); + assertTrue(metaData.supportsCoreSQLGrammar()); + assertFalse(metaData.supportsExtendedSQLGrammar()); + assertTrue(metaData.supportsANSI92EntryLevelSQL()); + assertFalse(metaData.supportsANSI92IntermediateSQL()); + assertFalse(metaData.supportsANSI92FullSQL()); + assertFalse(metaData.supportsFullOuterJoins()); + assertFalse(metaData.supportsLimitedOuterJoins()); + assertTrue(metaData.isCatalogAtStart()); + assertTrue(metaData.supportsSchemasInDataManipulation()); + + assertTrue(metaData.supportsSchemasInTableDefinitions()); + assertTrue(metaData.supportsSchemasInIndexDefinitions()); + assertTrue(metaData.supportsSchemasInPrivilegeDefinitions()); + assertTrue(metaData.supportsCatalogsInDataManipulation()); + assertFalse(metaData.supportsPositionedDelete()); + assertFalse(metaData.supportsPositionedUpdate()); + + assertFalse(metaData.supportsSelectForUpdate()); + assertTrue(metaData.supportsStoredProcedures()); + assertTrue(metaData.supportsSubqueriesInComparisons()); + assertTrue(metaData.supportsSubqueriesInExists()); + assertTrue(metaData.supportsSubqueriesInIns()); + assertTrue(metaData.supportsSubqueriesInQuantifieds()); + assertTrue(metaData.supportsCorrelatedSubqueries()); + assertFalse(metaData.supportsOpenCursorsAcrossCommit()); + + assertFalse(metaData.supportsOpenCursorsAcrossRollback()); + assertTrue(metaData.supportsOpenStatementsAcrossCommit()); + assertTrue(metaData.supportsOpenStatementsAcrossRollback()); + + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); // f + + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); // f + assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE)); // f + assertFalse(metaData.supportsDifferentTableCorrelationNames()); // f + + assertFalse(metaData.dataDefinitionIgnoredInTransactions()); + assertFalse(metaData.doesMaxRowSizeIncludeBlobs()); + + assertTrue(metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)); // t + assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)); // f + assertFalse(metaData.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)); // f + + assertTrue( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); // t + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); // f + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)); + + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersDeletesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.othersInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.updatesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.deletesAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_FORWARD_ONLY)); + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.insertsAreDetected(ResultSet.TYPE_SCROLL_SENSITIVE)); + + assertTrue(metaData.supportsBatchUpdates()); + assertFalse(metaData.supportsSavepoints()); + assertFalse(metaData.supportsNamedParameters()); + assertFalse(metaData.supportsMultipleOpenResults()); + assertFalse(metaData.supportsGetGeneratedKeys()); + + assertFalse(metaData.generatedKeyAlwaysReturned()); + assertFalse(metaData.supportsIntegrityEnhancementFacility()); + assertFalse(metaData.supportsDataDefinitionAndDataManipulationTransactions()); + assertFalse(metaData.isReadOnly()); + } + + protected void verifyIntMethods(DatabaseMetaData metaData) throws SQLException { + assertEquals(0, metaData.getMaxBinaryLiteralLength()); // 0 + assertEquals(0, metaData.getMaxCharLiteralLength()); // 0 + assertEquals(0, metaData.getMaxColumnsInGroupBy()); // 0 + assertEquals(0, metaData.getMaxColumnsInIndex()); // 0 + assertEquals(0, metaData.getMaxColumnsInOrderBy()); // 0 + assertEquals(0, metaData.getMaxColumnsInSelect()); // 0 + assertEquals(0, metaData.getMaxConnections()); // 0 + assertEquals(0, metaData.getMaxCursorNameLength()); // 0 + assertEquals(0, metaData.getMaxIndexLength()); // 0 + assertEquals(0, metaData.getMaxProcedureNameLength()); // 0 + assertEquals(0, metaData.getMaxRowSize()); // 0 + assertEquals(0, metaData.getMaxStatementLength()); // 0 + assertEquals(0, metaData.getMaxStatements()); // 0 + assertEquals(1000, metaData.getMaxTablesInSelect()); // 1000 + assertEquals(0, metaData.getMaxUserNameLength()); // 0 + } + + protected void verifyAllStringMethods(DatabaseMetaData metaData, String procedure) + throws SQLException { + assertEquals("`", metaData.getIdentifierQuoteString()); + assertTrue(procedure.equalsIgnoreCase(metaData.getProcedureTerm())); + } + + protected void verifyGetPrimaryKeys( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIMARYKEY"; + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(KEYCOLUMN int NOT NULL, SECONDCOLUMN string, THIRDCOLUMN timestamp);"); + ResultSet resultSet = metaData.getPrimaryKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + assertFalse(resultSet.next()); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyTestProcedure(DatabaseMetaData metaData, String expectedProcedure) + throws SQLException { + assertEquals(expectedProcedure, metaData.getProcedureTerm()); + assertTrue(metaData.supportsStoredProcedures()); + ResultSet resultSet; + resultSet = metaData.getProcedureColumns("%", "%", "%", "%"); + assertEquals(0, resultSetRowCount(resultSet)); + resultSet = metaData.getProcedures("%", "%", "%"); + assertEquals(0, resultSetRowCount(resultSet)); + } + + protected void verifyDriverMetadata( + DatabaseMetaData metaData, + int expectedMajorVer, + int expectedMinorVersion, + String expectedDatabaseProductName, + String expectedDriverName, + Pattern versionPattern) + throws SQLException { + + assertEquals(expectedMajorVer, metaData.getJDBCMajorVersion()); + assertEquals(expectedMinorVersion, metaData.getJDBCMinorVersion()); + assertEquals(expectedDatabaseProductName, metaData.getDatabaseProductName()); + assertEquals(expectedDriverName, metaData.getDriverName()); + String driverVersion = metaData.getDriverVersion(); + Matcher m = versionPattern.matcher(driverVersion); + assertTrue(m.matches()); + int majorVersion = metaData.getDriverMajorVersion(); + int minorVersion = metaData.getDriverMinorVersion(); + assertEquals(Integer.parseInt(m.group(1)), majorVersion); + assertEquals(Integer.parseInt(m.group(2)), minorVersion); + } + + protected void verifyCatalogHelper( + DatabaseMetaData metaData, + String expectedCatalogSeparator, + String expectedCatalogTerm, + int expectedCatalogNameLength, + String defaultCatalog, + boolean isBeforeFirstSupported, + boolean isFirstSupported) + throws SQLException { + assertEquals(expectedCatalogSeparator, metaData.getCatalogSeparator()); + // Simba BQ JDBC driver maps Catalogs to projects + assertEquals(expectedCatalogTerm, metaData.getCatalogTerm()); + assertEquals(expectedCatalogNameLength, metaData.getMaxCatalogNameLength()); + + // This should return the project name of the connection + ResultSet resultSet = metaData.getCatalogs(); + + // Compares ResultSetMetadata. + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); + if (isBeforeFirstSupported) { + assertTrue(resultSet.isBeforeFirst()); + } + + int count = 0; + boolean defaultCatalogFound = false; + while (resultSet.next()) { + if (count == 0 && isFirstSupported) { + assertTrue(resultSet.isFirst()); + } + ++count; + if (defaultCatalog.equals(resultSet.getString(1))) { + defaultCatalogFound = true; + break; + } + } + assertTrue(defaultCatalogFound); + resultSet.close(); + } + + protected void verifySchemaHelper( + Connection connection, + DatabaseMetaData metaData, + String schemaTerm, + int maxSchemaNameLength, + boolean isBeforeFirstSupported, + boolean isFirstSupported) + throws SQLException { + // Simba BQ JDBC driver maps Table Datasets to Schemas + assertEquals(schemaTerm, metaData.getSchemaTerm()); + assertEquals(maxSchemaNameLength, metaData.getMaxSchemaNameLength()); + // This should return all the datasets in the project of the connection + ResultSet resultSet = metaData.getSchemas(); + // Compares ResultSetMetadata. + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); + if (isBeforeFirstSupported) { + assertTrue(resultSet.isBeforeFirst()); + } + + int count = 0; + Set allVisibleDatasets = new HashSet<>(); + while (resultSet.next()) { + allVisibleDatasets.add(resultSet.getString(1)); + if (count == 0 && isFirstSupported) { + assertTrue(resultSet.isFirst()); + } + ++count; + } + assertTrue(count >= 1); + List allAccessibleDatasets = + getInfoBySQL(connection, "SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA;"); + assertTrue(allVisibleDatasets.containsAll(allAccessibleDatasets)); + assertTrue(count >= allAccessibleDatasets.size()); + resultSet.close(); + } + + protected void verifyTableTypes(DatabaseMetaData metaData, Collection expectedTypes) + throws SQLException { + ResultSet resultSet = metaData.getTableTypes(); + Set types = new HashSet<>(); + while (resultSet.next()) { + String col = resultSet.getString(1); + types.add(col); + } + assertTrue(types.size() > 0); + assertTrue(types.containsAll(expectedTypes)); + } + + protected void verifyGetTables(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTTABLE"; + final String jdbctestview = "JDBCTESTVIEW"; + + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` " + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + statement.execute( + String.format( + "create or replace view `%s.%s.%s` " + " as select 1 as VIEWCOLUMN;", + ITBase.DEFAULT_CATALOG, dataset, jdbctestview)); + + ResultSet resultSet; + + // Pattern match for table + resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"TABLE"}); + // verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_TABLES); + Set tables = new HashSet<>(); + while (resultSet.next()) { + tables.add(resultSet.getString(3)); + } + assertTrue(tables.contains("JDBCTESTTABLE")); + + // exact match for tablename + resultSet = + metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, new String[] {"TABLE"}); + tables = new HashSet<>(); + while (resultSet.next()) { + tables.add(resultSet.getString(3)); + } + assertEquals(jdbctesttable, tables.iterator().next()); + + // Pattern match for view + resultSet = metaData.getTables(ITBase.DEFAULT_CATALOG, dataset, "%", new String[] {"VIEW"}); + Set views = new HashSet<>(); + while (resultSet.next()) { + views.add(resultSet.getString(3)); + } + assertTrue(views.contains(jdbctestview)); + + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + connection.createStatement().execute("drop view if exists " + dataset + "." + jdbctestview); + } + + // GetTablePrivileges not supported for JDBC and ODBC datasource. + // Returns empty resultset + protected void verifyGetTablePrivileges( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; + + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + + ResultSet resultSet = + metaData.getTablePrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyGetColumnPrivileges( + Connection connection, DatabaseMetaData metaData, String dataset) throws SQLException { + final String jdbctesttable = "JDBCTESTPRIVILEGETABLE"; + + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + + ResultSet resultSet = + metaData.getColumnPrivileges(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%"); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyForeignKeys(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTTABLEFOREIGNKEYS"; + + connection + .createStatement() + .execute( + "create or replace table " + + dataset + + "." + + jdbctesttable + + "(INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);"); + + ResultSet resultSet = metaData.getImportedKeys(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + Assert.assertNotNull(resultSet.getString(i)); + } + ++count; + } + assertEquals(0, count); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } + + protected void verifyGetColumns(Connection connection, DatabaseMetaData metaData, String dataset) + throws SQLException { + final String jdbctesttable = "JDBCTESTCOLUMNS"; + Statement statement = connection.createStatement(); + statement.execute( + String.format( + "create or replace table `%s.%s.%s` (INTCOLUMN int, STRINGCOLUMN string, TIMECOLUMN timestamp);", + ITBase.DEFAULT_CATALOG, dataset, jdbctesttable)); + ResultSet resultSet = + metaData.getColumns(ITBase.DEFAULT_CATALOG, dataset, jdbctesttable, "%COLUMN"); + int cols = resultSet.getMetaData().getColumnCount(); + assertTrue(cols > 0); + // Resultset is not empty + int count = 0; + while (resultSet.next()) { + for (int i = 1; i <= cols; i++) { + Assert.assertNotNull(resultSet.getMetaData().getColumnName(i)); + } + ++count; + } + resultSet.close(); + assertTrue(count > 0); + connection.createStatement().execute("drop table if exists " + dataset + "." + jdbctesttable); + } }