From a7f782d0648218e9c38720a6b505776aed3155fc Mon Sep 17 00:00:00 2001 From: Neeraj Dwivedi Date: Fri, 14 Aug 2026 15:27:29 +0530 Subject: [PATCH 1/3] impl(bq_driver): Add impl for SQL_ATTR_ANSI_APP attribute --- .../odbc/bq_driver/internal/odbc_conn_attr.cc | 7 +++++++ .../odbc/bq_driver/internal/odbc_conn_handle.h | 10 ++++++++++ google/cloud/odbc/bq_driver/odbc_connection.cc | 7 +++++++ google/cloud/odbc/testing/odbc_utils/connection.cc | 14 ++++++++++++-- google/cloud/odbc/testing/odbc_utils/connection.h | 2 +- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc b/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc index a0cb3307ec..a1291c7c89 100644 --- a/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc +++ b/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc @@ -36,6 +36,9 @@ ConnectionAttr::ConnectionAttr() { {SQL_ATTR_LOGIN_TIMEOUT, {"SQL_ATTR_LOGIN_TIMEOUT", ConnectionValidation::kBefore, SupportedAttribute::kBoth, (SQLPOINTER)0}}, + {SQL_ATTR_ANSI_APP, + {"SQL_ATTR_ANSI_APP", ConnectionValidation::kBefore, + SupportedAttribute::kSet, (SQLPOINTER)SQL_AA_FALSE}}, {SQL_ATTR_METADATA_ID, {"SQL_ATTR_METADATA_ID", ConnectionValidation::kEither, SupportedAttribute::kBoth, (SQLPOINTER)SQL_FALSE}}, @@ -76,6 +79,10 @@ ConnectionAttr::ConnectionAttr() { {"SQL_ATTR_AUTOCOMMIT", ConnectionValueType::kSqlUInt, {(SQLPOINTER)SQL_AUTOCOMMIT_OFF, (SQLPOINTER)SQL_AUTOCOMMIT_ON}}}, + {SQL_ATTR_ANSI_APP, + {"SQL_ATTR_ANSI_APP", + ConnectionValueType::kSqlULen, + {(SQLPOINTER)SQL_AA_TRUE, (SQLPOINTER)SQL_AA_FALSE}}}, {SQL_ATTR_METADATA_ID, {"SQL_ATTR_METADATA_ID", ConnectionValueType::kSqlUInt, diff --git a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h index 04a2085138..db17fa7a1a 100644 --- a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h +++ b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h @@ -42,6 +42,8 @@ struct Authentication { std::string refresh_token; }; +enum struct ApplicationType { kUnknown = 0, kAnsi = 1, kUnicode = 2 }; + // This is populated by SQL*Connect APIs after parsing the DSN section from // odbc.ini/Windows Registry struct Dsn { @@ -133,6 +135,12 @@ class ConnectionHandle : public Handle { void SetUp(Section& dsn_section, std::string const& dsn_name); + void SetApplicationType(ApplicationType type) { type_ = type; }; + + ApplicationType GetApplicationType() { return type_; } + + bool IsAnsiApplication() { return type_ == ApplicationType::kAnsi; } + Dsn GetDsn() const { return dsn_; } std::shared_ptr GetClient() { return client_; } @@ -197,6 +205,8 @@ class ConnectionHandle : public Handle { // storage of all explicitly allocated descriptor handles associated with this // connection handle std::set desc_handles_; + + ApplicationType type_ = ApplicationType::kUnknown; EnvironmentHandle* env_handle_{nullptr}; mutable std::mutex connection_handle_mutex_; // Session ID of the started session. diff --git a/google/cloud/odbc/bq_driver/odbc_connection.cc b/google/cloud/odbc/bq_driver/odbc_connection.cc index 9baa70a973..dc9d09fd75 100644 --- a/google/cloud/odbc/bq_driver/odbc_connection.cc +++ b/google/cloud/odbc/bq_driver/odbc_connection.cc @@ -31,6 +31,7 @@ namespace google::cloud::odbc_bq_driver { using google::cloud::odbc_bigquery_client_interface::OauthMechanism; using google::cloud::odbc_bq_driver::ToCharStr; +using google::cloud::odbc_bq_driver_internal::ApplicationType; using google::cloud::odbc_bq_driver_internal::Authentication; using google::cloud::odbc_bq_driver_internal::ConnectionHandle; using google::cloud::odbc_bq_driver_internal::DescriptorHandle; @@ -471,6 +472,12 @@ SQLRETURN SQLSetConnectAttrInternal(SQLHDBC connection_handle, return LogAndReturnCode(*conn_handle, status_record); } + if (attribute == SQL_ATTR_ANSI_APP) { + conn_handle->SetApplicationType( + (reinterpret_cast(value) == SQL_AA_TRUE) + ? ApplicationType::kAnsi + : ApplicationType::kUnicode); + } // Additionally set these attributes to all associated statement handles if (attribute == SQL_ATTR_METADATA_ID || attribute == SQL_ATTR_ASYNC_ENABLE) { for (auto* const stmt_handle : conn_handle->GetStatementHandles()) { diff --git a/google/cloud/odbc/testing/odbc_utils/connection.cc b/google/cloud/odbc/testing/odbc_utils/connection.cc index da32dda607..0d474f04e7 100644 --- a/google/cloud/odbc/testing/odbc_utils/connection.cc +++ b/google/cloud/odbc/testing/odbc_utils/connection.cc @@ -36,6 +36,11 @@ void SetAttributes(std::shared_ptr const& conn, int timeout, status = SQLSetConnectAttrA(conn->hdbc, SQL_ATTR_CONNECTION_TIMEOUT, ToSqlPointer(timeout), 0); CheckError(status, "SQLSetConnectAttr", conn, use_ansi); +#ifndef WIN32 + status = SQLSetConnectAttrA(conn->hdbc, SQL_ATTR_ANSI_APP, + ToSqlPointer(SQL_AA_TRUE), 0); + CheckError(status, "SQLSetConnectAttr", conn, use_ansi); +#endif // WIN32 } else { status = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_LOGIN_TIMEOUT, ToSqlPointer(10), 0); @@ -44,12 +49,17 @@ void SetAttributes(std::shared_ptr const& conn, int timeout, status = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_CONNECTION_TIMEOUT, ToSqlPointer(timeout), 0); CheckError(status, "SQLSetConnectAttr", conn, use_ansi); +#ifndef WIN32 + status = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_ANSI_APP, + ToSqlPointer(SQL_AA_FALSE), 0); + CheckError(status, "SQLSetConnectAttr", conn, use_ansi); +#endif // WIN32 } } SQLRETURN Connect(std::string const& conn_str, - std::shared_ptr const& conn, int timeout, - bool use_ansi) { + std::shared_ptr const& conn, bool use_ansi, + int timeout) { SQLSMALLINT buflen; SQLCHAR data_source[kBufferLength]; SQLSMALLINT out_len; diff --git a/google/cloud/odbc/testing/odbc_utils/connection.h b/google/cloud/odbc/testing/odbc_utils/connection.h index 34fe3e0eec..ab4de7fabd 100644 --- a/google/cloud/odbc/testing/odbc_utils/connection.h +++ b/google/cloud/odbc/testing/odbc_utils/connection.h @@ -55,7 +55,7 @@ std::string const kImpersonatedAccountEmail = // Connect using a and populate the ODBCHandles SQLRETURN Connect(std::string const& conn_str, std::shared_ptr const& conn, - int timeout = 30, bool use_ansi = false); + bool use_ansi = false, int timeout = 30); // To validate SQLDriverConnect and SQLDriverConnectW with NULL output // parameters. From 7679b0d4d4d89e90fea01971252543ee92af3d12 Mon Sep 17 00:00:00 2001 From: Neeraj Dwivedi Date: Tue, 25 Aug 2026 13:14:54 +0530 Subject: [PATCH 2/3] add verification test case --- .../odbc/bq_driver/internal/odbc_conn_attr.cc | 2 +- .../odbc_driver_tests/connection_test.cc | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc b/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc index a1291c7c89..b3d99cc32b 100644 --- a/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc +++ b/google/cloud/odbc/bq_driver/internal/odbc_conn_attr.cc @@ -37,7 +37,7 @@ ConnectionAttr::ConnectionAttr() { {"SQL_ATTR_LOGIN_TIMEOUT", ConnectionValidation::kBefore, SupportedAttribute::kBoth, (SQLPOINTER)0}}, {SQL_ATTR_ANSI_APP, - {"SQL_ATTR_ANSI_APP", ConnectionValidation::kBefore, + {"SQL_ATTR_ANSI_APP", ConnectionValidation::kEither, SupportedAttribute::kSet, (SQLPOINTER)SQL_AA_FALSE}}, {SQL_ATTR_METADATA_ID, {"SQL_ATTR_METADATA_ID", ConnectionValidation::kEither, diff --git a/google/cloud/odbc/integration_tests/odbc_driver_tests/connection_test.cc b/google/cloud/odbc/integration_tests/odbc_driver_tests/connection_test.cc index 2798bef220..79af3165ca 100644 --- a/google/cloud/odbc/integration_tests/odbc_driver_tests/connection_test.cc +++ b/google/cloud/odbc/integration_tests/odbc_driver_tests/connection_test.cc @@ -597,6 +597,26 @@ TEST(MultipleConnectionTest, SQLDriverConnect) { } } +TEST(ConnectionTest, VerifySQLANSIAttributes) { + auto conn = std::make_shared(); + SQLRETURN status; + SQLCHAR data_source[kBufferLength]; + SQLSMALLINT buflen = 0; + SetAttributes(conn, 30, true); + StrToChar(reinterpret_cast(data_source), kDefaultConnectionString); + + status = + SQLDriverConnectA(conn->hdbc, nullptr, data_source, SQL_NTS, + reinterpret_cast(conn->outdsn), + sizeof(conn->outdsn), &buflen, SQL_DRIVER_COMPLETE); + CheckError(status, "SQLDriverConnectA", conn, true); + + // SQL_ATTR_ANSI_APP is expected to be successful after connection. + status = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_ANSI_APP, + ToSqlPointer(SQL_AA_FALSE), 0); + EXPECT_EQ(status, SQL_SUCCESS); +} + TEST(ConnectionTest, SQLDriverConnectA) { auto conn = std::make_shared(); EXPECT_EQ(Connect(kDefaultConnectionString, conn, true), SQL_SUCCESS); From 233b2c92ce4299cbdc3f6d50bcb8093110994c31 Mon Sep 17 00:00:00 2001 From: Neeraj Dwivedi Date: Thu, 3 Sep 2026 10:09:36 +0530 Subject: [PATCH 3/3] new changes --- google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h | 9 --------- google/cloud/odbc/bq_driver/odbc_connection.cc | 6 +----- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h index db17fa7a1a..6aa7e780e3 100644 --- a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h +++ b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h @@ -42,8 +42,6 @@ struct Authentication { std::string refresh_token; }; -enum struct ApplicationType { kUnknown = 0, kAnsi = 1, kUnicode = 2 }; - // This is populated by SQL*Connect APIs after parsing the DSN section from // odbc.ini/Windows Registry struct Dsn { @@ -135,12 +133,6 @@ class ConnectionHandle : public Handle { void SetUp(Section& dsn_section, std::string const& dsn_name); - void SetApplicationType(ApplicationType type) { type_ = type; }; - - ApplicationType GetApplicationType() { return type_; } - - bool IsAnsiApplication() { return type_ == ApplicationType::kAnsi; } - Dsn GetDsn() const { return dsn_; } std::shared_ptr GetClient() { return client_; } @@ -206,7 +198,6 @@ class ConnectionHandle : public Handle { // connection handle std::set desc_handles_; - ApplicationType type_ = ApplicationType::kUnknown; EnvironmentHandle* env_handle_{nullptr}; mutable std::mutex connection_handle_mutex_; // Session ID of the started session. diff --git a/google/cloud/odbc/bq_driver/odbc_connection.cc b/google/cloud/odbc/bq_driver/odbc_connection.cc index dc9d09fd75..ebfad3f41c 100644 --- a/google/cloud/odbc/bq_driver/odbc_connection.cc +++ b/google/cloud/odbc/bq_driver/odbc_connection.cc @@ -31,7 +31,6 @@ namespace google::cloud::odbc_bq_driver { using google::cloud::odbc_bigquery_client_interface::OauthMechanism; using google::cloud::odbc_bq_driver::ToCharStr; -using google::cloud::odbc_bq_driver_internal::ApplicationType; using google::cloud::odbc_bq_driver_internal::Authentication; using google::cloud::odbc_bq_driver_internal::ConnectionHandle; using google::cloud::odbc_bq_driver_internal::DescriptorHandle; @@ -473,10 +472,7 @@ SQLRETURN SQLSetConnectAttrInternal(SQLHDBC connection_handle, } if (attribute == SQL_ATTR_ANSI_APP) { - conn_handle->SetApplicationType( - (reinterpret_cast(value) == SQL_AA_TRUE) - ? ApplicationType::kAnsi - : ApplicationType::kUnicode); + return SQL_SUCCESS; } // Additionally set these attributes to all associated statement handles if (attribute == SQL_ATTR_METADATA_ID || attribute == SQL_ATTR_ASYNC_ENABLE) {