This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 95cdc0c11d GH-47708: [C++][FlightRPC] Connection Attribute Support for 
ODBC (#47772)
95cdc0c11d is described below

commit 95cdc0c11d20fab563416afeba055fe43113b75b
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Sat Nov 29 23:03:55 2025 -0800

    GH-47708: [C++][FlightRPC] Connection Attribute Support for ODBC (#47772)
    
    ### Rationale for this change
    Add connection attribute support for ODBC driver.
    
    ### What changes are included in this PR?
    - Implementation of `SQLGetConnectAttr` and `SQLSetConnectAttr` to get and 
set connection attributes
    - Tests
    
    ### Are these changes tested?
    Tested on local MSVC
    
    ### Are there any user-facing changes?
    No
    
    * GitHub Issue: #47708
    
    Authored-by: Alina (Xi) Li <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 cpp/src/arrow/flight/sql/odbc/odbc_api.cc          |  13 +-
 .../flight/sql/odbc/odbc_impl/odbc_connection.cc   |  36 +-
 .../flight/sql/odbc/odbc_impl/odbc_connection.h    |   5 +-
 cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt |   3 +-
 .../flight/sql/odbc/tests/connection_attr_test.cc  | 362 +++++++++++++++++++++
 5 files changed, 395 insertions(+), 24 deletions(-)

diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index 19c6e62eba..fc50e4a816 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -714,8 +714,15 @@ SQLRETURN SQLGetConnectAttr(SQLHDBC conn, SQLINTEGER 
attribute, SQLPOINTER value
                    << ", attribute: " << attribute << ", value_ptr: " << 
value_ptr
                    << ", buffer_length: " << buffer_length << ", 
string_length_ptr: "
                    << static_cast<const void*>(string_length_ptr);
-  // GH-47708 TODO: Implement SQLGetConnectAttr
-  return SQL_INVALID_HANDLE;
+
+  using ODBC::ODBCConnection;
+
+  return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() {
+    const bool is_unicode = true;
+    ODBCConnection* connection = reinterpret_cast<ODBCConnection*>(conn);
+    return connection->GetConnectAttr(attribute, value_ptr, buffer_length,
+                                      string_length_ptr, is_unicode);
+  });
 }
 
 SQLRETURN SQLSetConnectAttr(SQLHDBC conn, SQLINTEGER attr, SQLPOINTER 
value_ptr,
@@ -723,7 +730,7 @@ SQLRETURN SQLSetConnectAttr(SQLHDBC conn, SQLINTEGER attr, 
SQLPOINTER value_ptr,
   ARROW_LOG(DEBUG) << "SQLSetConnectAttrW called with conn: " << conn
                    << ", attr: " << attr << ", value_ptr: " << value_ptr
                    << ", value_len: " << value_len;
-  // GH-47708 TODO: Add tests for SQLSetConnectAttr
+
   using ODBC::ODBCConnection;
 
   return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() {
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
index ead2beada4..ce6ec67f23 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
@@ -528,58 +528,58 @@ void ODBCConnection::SetConnectAttr(SQLINTEGER attribute, 
SQLPOINTER value,
   }
 }
 
-void ODBCConnection::GetConnectAttr(SQLINTEGER attribute, SQLPOINTER value,
-                                    SQLINTEGER buffer_length, SQLINTEGER* 
output_length,
-                                    bool is_unicode) {
+SQLRETURN ODBCConnection::GetConnectAttr(SQLINTEGER attribute, SQLPOINTER 
value,
+                                         SQLINTEGER buffer_length,
+                                         SQLINTEGER* output_length, bool 
is_unicode) {
   boost::optional<Connection::Attribute> spi_attribute;
 
   switch (attribute) {
     // Internal connection attributes
-#ifdef SQL_ATR_ASYNC_DBC_EVENT
+#ifdef SQL_ATTR_ASYNC_DBC_EVENT
     case SQL_ATTR_ASYNC_DBC_EVENT:
       GetAttribute(static_cast<SQLPOINTER>(NULL), value, buffer_length, 
output_length);
-      return;
+      return SQL_SUCCESS;
 #endif
 #ifdef SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE
     case SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE:
       GetAttribute(static_cast<SQLUINTEGER>(SQL_ASYNC_DBC_ENABLE_OFF), value,
                    buffer_length, output_length);
-      return;
+      return SQL_SUCCESS;
 #endif
-#ifdef SQL_ATTR_ASYNC_PCALLBACK
+#ifdef SQL_ATTR_ASYNC_DBC_PCALLBACK
     case SQL_ATTR_ASYNC_DBC_PCALLBACK:
       GetAttribute(static_cast<SQLPOINTER>(NULL), value, buffer_length, 
output_length);
-      return;
+      return SQL_SUCCESS;
 #endif
 #ifdef SQL_ATTR_ASYNC_DBC_PCONTEXT
     case SQL_ATTR_ASYNC_DBC_PCONTEXT:
       GetAttribute(static_cast<SQLPOINTER>(NULL), value, buffer_length, 
output_length);
-      return;
+      return SQL_SUCCESS;
 #endif
     case SQL_ATTR_ASYNC_ENABLE:
       GetAttribute(static_cast<SQLULEN>(SQL_ASYNC_ENABLE_OFF), value, 
buffer_length,
                    output_length);
-      return;
+      return SQL_SUCCESS;
     case SQL_ATTR_AUTO_IPD:
       GetAttribute(static_cast<SQLUINTEGER>(SQL_FALSE), value, buffer_length,
                    output_length);
-      return;
+      return SQL_SUCCESS;
     case SQL_ATTR_AUTOCOMMIT:
       GetAttribute(static_cast<SQLUINTEGER>(SQL_AUTOCOMMIT_ON), value, 
buffer_length,
                    output_length);
-      return;
+      return SQL_SUCCESS;
 #ifdef SQL_ATTR_DBC_INFO_TOKEN
     case SQL_ATTR_DBC_INFO_TOKEN:
       throw DriverException("Cannot read set-only attribute", "HY092");
 #endif
     case SQL_ATTR_ENLIST_IN_DTC:
       GetAttribute(static_cast<SQLPOINTER>(NULL), value, buffer_length, 
output_length);
-      return;
+      return SQL_SUCCESS;
     case SQL_ATTR_ODBC_CURSORS:  // DM-only.
       throw DriverException("Invalid attribute", "HY092");
     case SQL_ATTR_QUIET_MODE:
       GetAttribute(static_cast<SQLPOINTER>(NULL), value, buffer_length, 
output_length);
-      return;
+      return SQL_SUCCESS;
     case SQL_ATTR_TRACE:  // DM-only
       throw DriverException("Invalid attribute", "HY092");
     case SQL_ATTR_TRACEFILE:
@@ -589,7 +589,7 @@ void ODBCConnection::GetConnectAttr(SQLINTEGER attribute, 
SQLPOINTER value,
     case SQL_ATTR_TRANSLATE_OPTION:
       throw DriverException("Optional feature not supported.", "HYC00");
     case SQL_ATTR_TXN_ISOLATION:
-      throw DriverException("Optional feature not supported.", "HCY00");
+      throw DriverException("Optional feature not supported.", "HYC00");
 
     case SQL_ATTR_CURRENT_CATALOG: {
       const auto& catalog = 
spi_connection_->GetAttribute(Connection::CURRENT_CATALOG);
@@ -597,9 +597,8 @@ void ODBCConnection::GetConnectAttr(SQLINTEGER attribute, 
SQLPOINTER value,
         throw DriverException("Optional feature not supported.", "HYC00");
       }
       const std::string& info_value = boost::get<std::string>(*catalog);
-      GetStringAttribute(is_unicode, info_value, true, value, buffer_length,
-                         output_length, GetDiagnostics());
-      return;
+      return GetStringAttribute(is_unicode, info_value, true, value, 
buffer_length,
+                                output_length, GetDiagnostics());
     }
 
     // These all are uint32_t attributes.
@@ -628,6 +627,7 @@ void ODBCConnection::GetConnectAttr(SQLINTEGER attribute, 
SQLPOINTER value,
 
   GetAttribute(static_cast<SQLUINTEGER>(boost::get<uint32_t>(*spi_attribute)), 
value,
                buffer_length, output_length);
+  return SQL_SUCCESS;
 }
 
 void ODBCConnection::Disconnect() {
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.h
index 2e5ab57ad4..c2fc190da8 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.h
@@ -57,8 +57,9 @@ class ODBCConnection : public ODBCHandle<ODBCConnection> {
                SQLSMALLINT* output_length, bool is_unicode);
   void SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER 
string_length,
                       bool isUnicode);
-  void GetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER 
buffer_length,
-                      SQLINTEGER* output_length, bool is_unicode);
+  SQLRETURN GetConnectAttr(SQLINTEGER attribute, SQLPOINTER value,
+                           SQLINTEGER buffer_length, SQLINTEGER* output_length,
+                           bool is_unicode);
 
   ~ODBCConnection() = default;
 
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt 
b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
index 7be318039d..9efae5c2a9 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
+++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
@@ -34,8 +34,9 @@ add_arrow_test(flight_sql_odbc_test
                SOURCES
                odbc_test_suite.cc
                odbc_test_suite.h
-               statement_attr_test.cc
+               connection_attr_test.cc
                connection_test.cc
+               statement_attr_test.cc
                # Enable Protobuf cleanup after test execution
                # GH-46889: move protobuf_test_util to a more common location
                ../../../../engine/substrait/protobuf_test_util.cc
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc 
b/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc
new file mode 100644
index 0000000000..ed1db2560c
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc
@@ -0,0 +1,362 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h"
+
+#include "arrow/flight/sql/odbc/odbc_impl/platform.h"
+
+#include <sql.h>
+#include <sqltypes.h>
+#include <sqlucode.h>
+
+#include <gtest/gtest.h>
+
+namespace arrow::flight::sql::odbc {
+
+template <typename T>
+class ConnectionAttributeTest : public T {};
+
+using TestTypes =
+    ::testing::Types<FlightSQLODBCMockTestBase, FlightSQLODBCRemoteTestBase>;
+TYPED_TEST_SUITE(ConnectionAttributeTest, TestTypes);
+
+#ifdef SQL_ATTR_ASYNC_DBC_EVENT
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrAsyncDbcEventUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_ASYNC_DBC_EVENT, 
0, 0));
+  // Driver Manager on Windows returns error code HY118
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY118);
+}
+#endif
+
+#ifdef SQL_ATTR_ASYNC_ENABLE
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrAyncEnableUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_ASYNC_ENABLE, 0, 
0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+#endif
+
+#ifdef SQL_ATTR_ASYNC_DBC_PCALLBACK
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrAyncDbcPcCallbackUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, 
SQL_ATTR_ASYNC_DBC_PCALLBACK, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+#endif
+
+#ifdef SQL_ATTR_ASYNC_DBC_PCONTEXT
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrAyncDbcPcContextUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, 
SQL_ATTR_ASYNC_DBC_PCONTEXT, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+#endif
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrAutoIpdReadOnly) {
+  // Verify read-only attribute cannot be set
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_AUTO_IPD, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY092);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrConnectionDeadReadOnly) {
+  // Verify read-only attribute cannot be set
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_CONNECTION_DEAD, 
0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY092);
+}
+
+#ifdef SQL_ATTR_DBC_INFO_TOKEN
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrDbcInfoTokenUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_DBC_INFO_TOKEN, 
0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+#endif
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrEnlistInDtcUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_ENLIST_IN_DTC, 
0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrOdbcCursorsDMOnly) {
+  this->AllocEnvConnHandles();
+
+  // Verify DM-only attribute is settable via Driver Manager
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLSetConnectAttr(this->conn, SQL_ATTR_ODBC_CURSORS,
+                              
reinterpret_cast<SQLPOINTER>(SQL_CUR_USE_DRIVER), 0));
+
+  std::string connect_str = this->GetConnectionString();
+  this->ConnectWithString(connect_str);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrQuietModeReadOnly) {
+  // Verify read-only attribute cannot be set
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_QUIET_MODE, 0, 
0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY092);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTraceDMOnly) {
+  // Verify DM-only attribute is settable via Driver Manager
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLSetConnectAttr(this->conn, SQL_ATTR_TRACE,
+                              reinterpret_cast<SQLPOINTER>(SQL_OPT_TRACE_OFF), 
0));
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTracefileDMOnly) {
+  // Verify DM-only attribute is handled by Driver Manager
+
+  // Use placeholder value as we want the call to fail, or else
+  // the driver manager will produce a trace file.
+  std::wstring trace_file = L"invalid/file/path";
+  std::vector<SQLWCHAR> trace_file0(trace_file.begin(), trace_file.end());
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_TRACEFILE, 
&trace_file0[0],
+                                         
static_cast<SQLINTEGER>(trace_file0.size())));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY000);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTranslateLabDMOnly) {
+  // Verify DM-only attribute is handled by Driver Manager
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, SQL_ATTR_TRANSLATE_LIB, 
0, 0));
+  // Checks for invalid argument return error
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY024);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrTranslateOptionUnsupported) {
+  ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(this->conn, 
SQL_ATTR_TRANSLATE_OPTION, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrTxnIsolationUnsupported) {
+  ASSERT_EQ(SQL_ERROR,
+            SQLSetConnectAttr(this->conn, SQL_ATTR_TXN_ISOLATION,
+                              
reinterpret_cast<SQLPOINTER>(SQL_TXN_READ_UNCOMMITTED), 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+#ifdef SQL_ATTR_DBC_INFO_TOKEN
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrDbcInfoTokenSetOnly) {
+  // Verify that set-only attribute cannot be read
+  SQLPOINTER ptr = NULL;
+  ASSERT_EQ(SQL_ERROR, SQLGetConnectAttr(this->conn, SQL_ATTR_DBC_INFO_TOKEN, 
ptr, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY092);
+}
+#endif
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrOdbcCursorsDMOnly) {
+  // Verify that DM-only attribute is handled by driver manager
+  SQLULEN cursor_attr;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ODBC_CURSORS, &cursor_attr, 
0, 0));
+  EXPECT_EQ(SQL_CUR_USE_DRIVER, cursor_attr);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceDMOnly) {
+  // Verify that DM-only attribute is handled by driver manager
+  SQLUINTEGER trace;
+  ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(this->conn, SQL_ATTR_TRACE, &trace, 
0, 0));
+  EXPECT_EQ(SQL_OPT_TRACE_OFF, trace);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceFileDMOnly) {
+  // Verify that DM-only attribute is handled by driver manager
+  SQLWCHAR out_str[kOdbcBufferSize];
+  SQLINTEGER out_str_len;
+  ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(this->conn, SQL_ATTR_TRACEFILE, 
out_str,
+                                           kOdbcBufferSize, &out_str_len));
+  // Length is returned in bytes for SQLGetConnectAttr,
+  // we want the number of characters
+  out_str_len /= arrow::flight::sql::odbc::GetSqlWCharSize();
+  std::string out_connection_string =
+      ODBC::SqlWcharToString(out_str, static_cast<SQLSMALLINT>(out_str_len));
+  EXPECT_FALSE(out_connection_string.empty());
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLGetConnectAttrTranslateLibUnsupported) {
+  SQLWCHAR out_str[kOdbcBufferSize];
+  SQLINTEGER out_str_len;
+  ASSERT_EQ(SQL_ERROR, SQLGetConnectAttr(this->conn, SQL_ATTR_TRANSLATE_LIB, 
out_str,
+                                         kOdbcBufferSize, &out_str_len));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLGetConnectAttrTranslateOptionUnsupported) {
+  SQLINTEGER option;
+  ASSERT_EQ(SQL_ERROR,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_TRANSLATE_OPTION, &option, 
0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLGetConnectAttrTxnIsolationUnsupported) {
+  SQLINTEGER isolation;
+  ASSERT_EQ(SQL_ERROR,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_TXN_ISOLATION, &isolation, 
0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHYC00);
+}
+
+#ifdef SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE
+TYPED_TEST(ConnectionAttributeTest,
+           TestSQLGetConnectAttrAsyncDbcFunctionsEnableUnsupported) {
+  // Verifies that the Windows driver manager returns HY114 for unsupported 
functionality
+  SQLUINTEGER enable;
+  ASSERT_EQ(SQL_ERROR, SQLGetConnectAttr(this->conn, 
SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE,
+                                         &enable, 0, 0));
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY114);
+}
+#endif
+
+// Tests for supported attributes
+
+#ifdef SQL_ATTR_ASYNC_DBC_EVENT
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrAsyncDbcEventDefault) 
{
+  SQLPOINTER ptr = NULL;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ASYNC_DBC_EVENT, ptr, 0, 
0));
+  EXPECT_EQ(reinterpret_cast<SQLPOINTER>(NULL), ptr);
+}
+#endif
+
+#ifdef SQL_ATTR_ASYNC_DBC_PCALLBACK
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLGetConnectAttrAsyncDbcPcallbackDefault) {
+  SQLPOINTER ptr = NULL;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ASYNC_DBC_PCALLBACK, ptr, 
0, 0));
+  EXPECT_EQ(reinterpret_cast<SQLPOINTER>(NULL), ptr);
+}
+#endif
+
+#ifdef SQL_ATTR_ASYNC_DBC_PCONTEXT
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLGetConnectAttrAsyncDbcPcontextDefault) {
+  SQLPOINTER ptr = NULL;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ASYNC_DBC_PCONTEXT, ptr, 0, 
0));
+  EXPECT_EQ(reinterpret_cast<SQLPOINTER>(NULL), ptr);
+}
+#endif
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrAsyncEnableDefault) {
+  SQLULEN enable;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ASYNC_ENABLE, &enable, 0, 
0));
+  EXPECT_EQ(SQL_ASYNC_ENABLE_OFF, enable);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrAutoIpdDefault) {
+  SQLUINTEGER ipd;
+  ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(this->conn, SQL_ATTR_AUTO_IPD, 
&ipd, 0, 0));
+  EXPECT_EQ(static_cast<SQLUINTEGER>(SQL_FALSE), ipd);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrAutocommitDefault) {
+  SQLUINTEGER auto_commit;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_AUTOCOMMIT, &auto_commit, 
0, 0));
+  EXPECT_EQ(SQL_AUTOCOMMIT_ON, auto_commit);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrEnlistInDtcDefault) {
+  SQLPOINTER ptr = NULL;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ENLIST_IN_DTC, ptr, 0, 0));
+  EXPECT_EQ(reinterpret_cast<SQLPOINTER>(NULL), ptr);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrQuietModeDefault) {
+  HWND ptr = NULL;
+  ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(this->conn, SQL_ATTR_QUIET_MODE, 
ptr, 0, 0));
+  EXPECT_EQ(reinterpret_cast<SQLPOINTER>(NULL), ptr);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrAccessModeValid) {
+  // The driver always returns SQL_MODE_READ_WRITE
+
+  // Check default value first
+  SQLUINTEGER mode = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ACCESS_MODE, &mode, 0, 0));
+  EXPECT_EQ(SQL_MODE_READ_WRITE, mode);
+
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLSetConnectAttr(this->conn, SQL_ATTR_ACCESS_MODE,
+                              
reinterpret_cast<SQLPOINTER>(SQL_MODE_READ_WRITE), 0));
+
+  mode = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_ACCESS_MODE, &mode, 0, 0));
+  EXPECT_EQ(SQL_MODE_READ_WRITE, mode);
+
+  // Attempt to set to SQL_MODE_READ_ONLY, driver should return warning and 
not error
+  EXPECT_EQ(SQL_SUCCESS_WITH_INFO,
+            SQLSetConnectAttr(this->conn, SQL_ATTR_ACCESS_MODE,
+                              
reinterpret_cast<SQLPOINTER>(SQL_MODE_READ_ONLY), 0));
+
+  // Verify warning status
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorState01S02);
+}
+
+TYPED_TEST(ConnectionAttributeTest, 
TestSQLSetConnectAttrConnectionTimeoutValid) {
+  // Check default value first
+  SQLUINTEGER timeout = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_CONNECTION_TIMEOUT, 
&timeout, 0, 0));
+  EXPECT_EQ(0, timeout);
+
+  ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(this->conn, 
SQL_ATTR_CONNECTION_TIMEOUT,
+                                           reinterpret_cast<SQLPOINTER>(42), 
0));
+
+  timeout = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_CONNECTION_TIMEOUT, 
&timeout, 0, 0));
+  EXPECT_EQ(42, timeout);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrLoginTimeoutValid) {
+  // Check default value first
+  SQLUINTEGER timeout = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_LOGIN_TIMEOUT, &timeout, 0, 
0));
+  EXPECT_EQ(0, timeout);
+
+  ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(this->conn, SQL_ATTR_LOGIN_TIMEOUT,
+                                           reinterpret_cast<SQLPOINTER>(42), 
0));
+
+  timeout = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_LOGIN_TIMEOUT, &timeout, 0, 
0));
+  EXPECT_EQ(42, timeout);
+}
+
+TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrPacketSizeValid) {
+  // The driver always returns 0. PACKET_SIZE value is unused by the driver.
+
+  // Check default value first
+  SQLUINTEGER size = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_PACKET_SIZE, &size, 0, 0));
+  EXPECT_EQ(0, size);
+
+  ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(this->conn, SQL_ATTR_PACKET_SIZE,
+                                           reinterpret_cast<SQLPOINTER>(0), 
0));
+
+  size = -1;
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLGetConnectAttr(this->conn, SQL_ATTR_PACKET_SIZE, &size, 0, 0));
+  EXPECT_EQ(0, size);
+
+  // Attempt to set to non-zero value, driver should return warning and not 
error
+  EXPECT_EQ(SQL_SUCCESS_WITH_INFO, SQLSetConnectAttr(this->conn, 
SQL_ATTR_PACKET_SIZE,
+                                                     
reinterpret_cast<SQLPOINTER>(2), 0));
+
+  // Verify warning status
+  VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorState01S02);
+}
+
+}  // namespace arrow::flight::sql::odbc

Reply via email to