From 45fdcdc52db195a288785ed17d8a19feb7ae303c Mon Sep 17 00:00:00 2001 From: Anshu6250 Date: Tue, 25 Aug 2026 15:29:13 +0530 Subject: [PATCH] impl(bq_driver): added integration test --- .../odbc_driver_tests/catalog_test.cc | 82 ++++++++++++++++ .../odbc_driver_tests/statement_test.cc | 29 ++++++ .../cloud/odbc/testing/odbc_utils/catalog.cc | 97 +++++++++++++++++++ .../cloud/odbc/testing/odbc_utils/catalog.h | 7 ++ 4 files changed, 215 insertions(+) diff --git a/google/cloud/odbc/integration_tests/odbc_driver_tests/catalog_test.cc b/google/cloud/odbc/integration_tests/odbc_driver_tests/catalog_test.cc index 80a8bccdf7..2d848a4b47 100644 --- a/google/cloud/odbc/integration_tests/odbc_driver_tests/catalog_test.cc +++ b/google/cloud/odbc/integration_tests/odbc_driver_tests/catalog_test.cc @@ -2215,4 +2215,86 @@ TEST(CatalogTest, SQLTables_NullCatalogFiltersToCurrentProject) { EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); } +#ifndef BQ_DRIVER_INTEGRATION_TESTS +TEST(CatalogTest, SQLSpecialColumns_SQL_BEST_ROWID_TableWithPrimaryKeys) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithPKSchema); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_BEST_ROWID, kDatasetName, kCatalogDatasetTableWithPK); + + // existing driver returns an empty result set for SQL_BEST_ROWID. + EXPECT_TRUE(special_columns.empty()); + + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} + +TEST(CatalogTest, SQLSpecialColumns_SQL_BEST_ROWID_TableWithoutPrimaryKeys) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithOutPKSchema); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_BEST_ROWID, kDatasetName, kCatalogDatasetTableWithoutPK); + + EXPECT_TRUE(special_columns.empty()); + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} + +TEST(CatalogTest, SQLSpecialColumns_SQL_ROWVER) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithPKSchema); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_ROWVER, kDatasetName, kCatalogDatasetTableWithPK); + + EXPECT_TRUE(special_columns.empty()); + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} + +TEST(CatalogTest, ANSI_SQLSpecialColumns_SQL_BEST_ROWID_TableWithPrimaryKeys) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn, true), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithPKSchema, true); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_BEST_ROWID, kDatasetName, kCatalogDatasetTableWithPK, + SQL_SCOPE_SESSION, SQL_NO_NULLS, true); + + // existing driver returns an empty result set for SQL_BEST_ROWID. + EXPECT_TRUE(special_columns.empty()); + + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} + +TEST(CatalogTest, + ANSI_SQLSpecialColumns_SQL_BEST_ROWID_TableWithoutPrimaryKeys) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn, true), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithOutPKSchema, true); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_BEST_ROWID, kDatasetName, kCatalogDatasetTableWithoutPK, + SQL_SCOPE_SESSION, SQL_NO_NULLS, true); + + EXPECT_TRUE(special_columns.empty()); + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} + +TEST(CatalogTest, ANSI_SQLSpecialColumns_SQL_ROWVER) { + auto conn = std::make_shared(); + EXPECT_EQ(Connect(kDefaultConnectionString, conn, true), SQL_SUCCESS); + CreateTableDirect(conn, kTableWithPKSchema, true); + + RowWiseResults special_columns = Catalog::GetSpecialColumns( + conn, SQL_ROWVER, kDatasetName, kCatalogDatasetTableWithPK, + SQL_SCOPE_SESSION, SQL_NO_NULLS, true); + + EXPECT_TRUE(special_columns.empty()); + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} +#endif // BQ_DRIVER_INTEGRATION_TESTS + } // namespace google::cloud::odbc_tests diff --git a/google/cloud/odbc/integration_tests/odbc_driver_tests/statement_test.cc b/google/cloud/odbc/integration_tests/odbc_driver_tests/statement_test.cc index f4f93443dd..2f19309125 100644 --- a/google/cloud/odbc/integration_tests/odbc_driver_tests/statement_test.cc +++ b/google/cloud/odbc/integration_tests/odbc_driver_tests/statement_test.cc @@ -889,6 +889,35 @@ TEST(StatementTest, SQLPrimaryKeys_VerifyMetadata) { EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); } +#ifndef BQ_DRIVER_INTEGRATION_TESTS +TEST(StatementTest, SQLSpecialColumns_VerifyMetadata) { + auto conn = std::make_shared(); + ASSERT_EQ(Connect(kDefaultConnectionString, conn), SQL_SUCCESS); + + SQLRETURN ret = SQLSpecialColumns( + conn->hstmt, SQL_BEST_ROWID, (SQLCHAR*)"bigquery-devtools-drivers", + SQL_NTS, (SQLCHAR*)"INTEGRATION_TESTS", SQL_NTS, (SQLCHAR*)"Test_Table", + SQL_NTS, SQL_SCOPE_SESSION, SQL_NULLABLE); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + ExpectedColMetadata expected[] = { + {"SCOPE", SQL_SMALLINT, 5, 0, SQL_NULLABLE}, + {"COLUMN_NAME", SQL_WVARCHAR, 128, 0, SQL_NO_NULLS}, + {"DATA_TYPE", SQL_SMALLINT, 5, 0, SQL_NO_NULLS}, + {"TYPE_NAME", SQL_WVARCHAR, 128, 0, SQL_NO_NULLS}, + {"COLUMN_SIZE", SQL_INTEGER, 10, 0, SQL_NULLABLE}, + {"BUFFER_LENGTH", SQL_INTEGER, 10, 0, SQL_NULLABLE}, + {"DECIMAL_DIGITS", SQL_SMALLINT, 5, 0, SQL_NULLABLE}, + {"PSEUDO_COLUMN", SQL_SMALLINT, 5, 0, SQL_NULLABLE}, + }; + + VerifyResultSetMetadata( + conn->hstmt, static_cast(std::size(expected)), expected); + + EXPECT_EQ(Disconnect(conn), SQL_SUCCESS); +} +#endif // BQ_DRIVER_INTEGRATION_TESTS + TEST(StatementTest, SQLForeignKeys_VerifyMetadata) { auto conn = std::make_shared(); ASSERT_EQ(Connect(kDefaultConnectionString, conn), SQL_SUCCESS); diff --git a/google/cloud/odbc/testing/odbc_utils/catalog.cc b/google/cloud/odbc/testing/odbc_utils/catalog.cc index 2a37e4b52b..0ab79a25d3 100644 --- a/google/cloud/odbc/testing/odbc_utils/catalog.cc +++ b/google/cloud/odbc/testing/odbc_utils/catalog.cc @@ -403,6 +403,103 @@ RowWiseResults Catalog::GetPrimaryKeys(std::shared_ptr const& conn, return results; } +RowWiseResults Catalog::GetSpecialColumns( + std::shared_ptr const& conn, SQLUSMALLINT identifier_type, + std::string const& dataset, std::string const& table, SQLUSMALLINT scope, + SQLUSMALLINT nullable, bool use_ansi) { + SQLRETURN status; + int const res_cols = 8; + RowWiseResults results; + + if (dataset.empty() || table.empty()) { + return results; + } + + std::optional project_id_opt = ::google::cloud::internal::GetEnv( + "CPP_BIGQUERY_ODBC_TEST_GOOGLE_CLOUD_PROJECT"); + auto catalog_name = + (!project_id_opt.has_value()) ? kCatalogName : project_id_opt.value(); + + status = SQLSetStmtAttr(conn->hstmt, SQL_ATTR_METADATA_ID, + ToSqlPointer(SQL_FALSE), 0); + CheckError(status, "SQLSetStmtAttr", conn); + + TestingDataBuffer columns[res_cols]; + + // 1. SCOPE (Smallint) + // 2. COLUMN_NAME (Varchar) + // 3. DATA_TYPE (Smallint) + // 4. TYPE_NAME (Varchar) + // 5. COLUMN_SIZE (Integer) + // 6. BUFFER_LENGTH (Integer) + // 7. DECIMAL_DIGITS (Smallint) + // 8. PSEUDO_COLUMN (Smallint) + for (int i = 0; i < res_cols; i++) { + if (i == 0 || i == 2 || i == 6 || i == 7) { + columns[i].target_type = SQL_C_SSHORT; + } else if (i == 4 || i == 5) { + columns[i].target_type = SQL_C_SLONG; + } else { + columns[i].target_type = SQL_C_CHAR; + } + status = SQLBindCol(conn->hstmt, static_cast(i + 1), + columns[i].target_type, columns[i].target_value, + columns[i].buffer_length, &(columns[i].str_len)); + CheckError(status, "SQLBindCol", conn); + } + + if (use_ansi) { + status = SQLSpecialColumnsA( + conn->hstmt, identifier_type, + const_cast( + reinterpret_cast(catalog_name.c_str())), + static_cast(catalog_name.length()), + const_cast(reinterpret_cast(dataset.c_str())), + static_cast(dataset.length()), + const_cast(reinterpret_cast(table.c_str())), + static_cast(table.length()), scope, nullable); + } else { + status = SQLSpecialColumns( + conn->hstmt, identifier_type, + const_cast( + reinterpret_cast(catalog_name.c_str())), + static_cast(catalog_name.length()), + const_cast(reinterpret_cast(dataset.c_str())), + static_cast(dataset.length()), + const_cast(reinterpret_cast(table.c_str())), + static_cast(table.length()), scope, nullable); + } + CheckError(status, "SQLSpecialColumns", conn, use_ansi); + + while (true) { + Row row_result; + status = SQLFetch(conn->hstmt); + if (status == SQL_NO_DATA) { + break; + } + if (!SQL_SUCCEEDED(status)) { + CheckError(status, "SQLFetch", conn); + } + for (int i = 0; i < res_cols; i++) { + if (columns[i].str_len == SQL_NULL_DATA) { + } else if (i == 0 || i == 2 || i == 6 || i == 7) { + row_result.insert( + {i + 1, std::to_string(*reinterpret_cast( + columns[i].target_value))}); + } else if (i == 4 || i == 5) { + row_result.insert({i + 1, std::to_string(*reinterpret_cast( + columns[i].target_value))}); + } else { + row_result.insert( + {i + 1, reinterpret_cast(columns[i].target_value)}); + } + } + results.emplace_back(row_result); + } + + return results; +} + RowWiseResults Catalog::GetForeignKeys(std::shared_ptr const& conn, std::string const& dataset, std::string const& pk_table, diff --git a/google/cloud/odbc/testing/odbc_utils/catalog.h b/google/cloud/odbc/testing/odbc_utils/catalog.h index caeaef6cfc..4ab584e3a5 100644 --- a/google/cloud/odbc/testing/odbc_utils/catalog.h +++ b/google/cloud/odbc/testing/odbc_utils/catalog.h @@ -147,6 +147,13 @@ class Catalog { std::string const& table = "", bool use_ansi = false); + // Uses the SQLSpecialColumns API to fetch special columns in a dataset. + static RowWiseResults GetSpecialColumns( + std::shared_ptr const& conn, SQLUSMALLINT identifier_type, + std::string const& dataset = "", std::string const& table = "", + SQLUSMALLINT scope = SQL_SCOPE_SESSION, + SQLUSMALLINT nullable = SQL_NO_NULLS, bool use_ansi = false); + // Uses the SQLForeignKeys API to fetch foreign keys in a dataset. static RowWiseResults GetForeignKeys(std::shared_ptr const& conn, std::string const& dataset = "",