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 62afc09cf6 GH-46096: [C++][FlightRPC] Environment and Connection 
Handle Allocation (#47759)
62afc09cf6 is described below

commit 62afc09cf67cb96b18f2ce5fa93bb03f2c5506db
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Wed Oct 22 17:01:47 2025 -0700

    GH-46096: [C++][FlightRPC] Environment and Connection Handle Allocation 
(#47759)
    
    ### Rationale for this change
    ODBC driver needs to handle environment handle and connection handle 
allocation and deallocation.
    
    ### What changes are included in this PR?
    - Implement SQLAllocHandle and SQLFreeHandle APIs for allocating and 
deallocating environment and connection handles.
    - Tests
    
    ### Are these changes tested?
    - Tested locally on Windows MSVC.
    - Will be tested in CI after https://github.com/apache/arrow/pull/47689 is 
merged
    
    ### Are there any user-facing changes?
    No
    * GitHub Issue: #46096
    * GitHub Issue: #46097
    
    Authored-by: Alina (Xi) Li <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 cpp/src/arrow/flight/sql/odbc/odbc_api.cc          | 177 +++++++++++++++++++--
 .../arrow/flight/sql/odbc/tests/connection_test.cc |  84 ++++++++--
 2 files changed, 240 insertions(+), 21 deletions(-)

diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index b4408d2656..bce7c10e82 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -36,26 +36,179 @@ SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE 
parent, SQLHANDLE* result)
   ARROW_LOG(DEBUG) << "SQLAllocHandle called with type: " << type
                    << ", parent: " << parent
                    << ", result: " << static_cast<const void*>(result);
-  // GH-46096 TODO: Implement SQLAllocEnv
-  // GH-46097 TODO: Implement SQLAllocConnect, pre-requisite requires 
SQLAllocEnv
-  // implementation
-
-  // GH-47706 TODO: Implement SQLAllocStmt, pre-requisite requires
+  // GH-47706 TODO: Add tests for SQLAllocStmt, pre-requisite requires
   // SQLDriverConnect implementation
 
-  // GH-47707 TODO: Implement SQL_HANDLE_DESC for
+  // GH-47707 TODO: Add tests for SQL_HANDLE_DESC implementation for
   // descriptor handle, pre-requisite requires SQLAllocStmt
-  return SQL_INVALID_HANDLE;
+
+  *result = nullptr;
+
+  switch (type) {
+    case SQL_HANDLE_ENV: {
+      using ODBC::ODBCEnvironment;
+
+      *result = SQL_NULL_HENV;
+
+      try {
+        static std::shared_ptr<FlightSqlDriver> odbc_driver =
+            std::make_shared<FlightSqlDriver>();
+        *result = reinterpret_cast<SQLHENV>(new ODBCEnvironment(odbc_driver));
+
+        return SQL_SUCCESS;
+      } catch (const std::bad_alloc&) {
+        // allocating environment failed so cannot log diagnostic error here
+        return SQL_ERROR;
+      }
+    }
+
+    case SQL_HANDLE_DBC: {
+      using ODBC::ODBCConnection;
+      using ODBC::ODBCEnvironment;
+
+      *result = SQL_NULL_HDBC;
+
+      ODBCEnvironment* environment = 
reinterpret_cast<ODBCEnvironment*>(parent);
+
+      return ODBCEnvironment::ExecuteWithDiagnostics(environment, SQL_ERROR, 
[=]() {
+        std::shared_ptr<ODBCConnection> conn = environment->CreateConnection();
+
+        if (conn) {
+          // Inside `CreateConnection`, the shared_ptr `conn` is kept
+          // in a `std::vector` of connections inside the environment handle.
+          // As long as the parent environment handle is alive, the connection 
shared_ptr
+          // will be kept alive unless the user frees the connection.
+          *result = reinterpret_cast<SQLHDBC>(conn.get());
+
+          return SQL_SUCCESS;
+        }
+
+        return SQL_ERROR;
+      });
+    }
+
+    case SQL_HANDLE_STMT: {
+      using ODBC::ODBCConnection;
+      using ODBC::ODBCStatement;
+
+      *result = SQL_NULL_HSTMT;
+
+      ODBCConnection* connection = reinterpret_cast<ODBCConnection*>(parent);
+
+      return ODBCConnection::ExecuteWithDiagnostics(connection, SQL_ERROR, 
[=]() {
+        std::shared_ptr<ODBCStatement> statement = 
connection->CreateStatement();
+
+        if (statement) {
+          *result = reinterpret_cast<SQLHSTMT>(statement.get());
+
+          return SQL_SUCCESS;
+        }
+
+        return SQL_ERROR;
+      });
+    }
+
+    case SQL_HANDLE_DESC: {
+      using ODBC::ODBCConnection;
+      using ODBC::ODBCDescriptor;
+
+      *result = SQL_NULL_HDESC;
+
+      ODBCConnection* connection = reinterpret_cast<ODBCConnection*>(parent);
+
+      return ODBCConnection::ExecuteWithDiagnostics(connection, SQL_ERROR, 
[=]() {
+        std::shared_ptr<ODBCDescriptor> descriptor = 
connection->CreateDescriptor();
+
+        if (descriptor) {
+          *result = reinterpret_cast<SQLHDESC>(descriptor.get());
+
+          return SQL_SUCCESS;
+        }
+
+        return SQL_ERROR;
+      });
+    }
+
+    default:
+      break;
+  }
+
+  return SQL_ERROR;
 }
 
 SQLRETURN SQLFreeHandle(SQLSMALLINT type, SQLHANDLE handle) {
   ARROW_LOG(DEBUG) << "SQLFreeHandle called with type: " << type
                    << ", handle: " << handle;
-  // GH-46096 TODO: Implement SQLFreeEnv
-  // GH-46097 TODO: Implement SQLFreeConnect
-  // GH-47706 TODO: Implement SQLFreeStmt
-  // GH-47707 TODO: Implement SQL_HANDLE_DESC for descriptor handle
-  return SQL_INVALID_HANDLE;
+  // GH-47706 TODO: Add tests for SQLFreeStmt, pre-requisite requires
+  // SQLAllocStmt tests
+
+  // GH-47707 TODO: Add tests for SQL_HANDLE_DESC implementation for
+  // descriptor handle
+  switch (type) {
+    case SQL_HANDLE_ENV: {
+      using ODBC::ODBCEnvironment;
+
+      ODBCEnvironment* environment = 
reinterpret_cast<ODBCEnvironment*>(handle);
+
+      if (!environment) {
+        return SQL_INVALID_HANDLE;
+      }
+
+      delete environment;
+
+      return SQL_SUCCESS;
+    }
+
+    case SQL_HANDLE_DBC: {
+      using ODBC::ODBCConnection;
+
+      ODBCConnection* conn = reinterpret_cast<ODBCConnection*>(handle);
+
+      if (!conn) {
+        return SQL_INVALID_HANDLE;
+      }
+
+      // `ReleaseConnection` does the equivalent of `delete`.
+      // `ReleaseConnection` removes the connection `shared_ptr` from the 
`std::vector` of
+      // connections, and the `shared_ptr` is automatically destructed 
afterwards.
+      conn->ReleaseConnection();
+
+      return SQL_SUCCESS;
+    }
+
+    case SQL_HANDLE_STMT: {
+      using ODBC::ODBCStatement;
+
+      ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(handle);
+
+      if (!statement) {
+        return SQL_INVALID_HANDLE;
+      }
+
+      statement->ReleaseStatement();
+
+      return SQL_SUCCESS;
+    }
+
+    case SQL_HANDLE_DESC: {
+      using ODBC::ODBCDescriptor;
+
+      ODBCDescriptor* descriptor = reinterpret_cast<ODBCDescriptor*>(handle);
+
+      if (!descriptor) {
+        return SQL_INVALID_HANDLE;
+      }
+
+      descriptor->ReleaseDescriptor();
+
+      return SQL_SUCCESS;
+    }
+
+    default:
+      break;
+  }
+
+  return SQL_ERROR;
 }
 
 SQLRETURN SQLFreeStmt(SQLHSTMT handle, SQLUSMALLINT option) {
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc 
b/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
index fa1ccf2854..9500b8fa83 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
@@ -14,30 +14,96 @@
 // 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"
 
-#ifdef _WIN32
-#  include <windows.h>
-#endif
+#include "arrow/flight/sql/odbc/odbc_impl/platform.h"
 
 #include <sql.h>
 #include <sqltypes.h>
 #include <sqlucode.h>
 
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace arrow::flight::sql::odbc {
 
-TEST(SQLAllocHandle, SQLAllocHandleEnv) {
+template <typename T>
+class ConnectionTest : public T {};
+
+// GH-46574 TODO: add remote server test cases using `ConnectionRemoteTest`
+class ConnectionRemoteTest : public FlightSQLODBCRemoteTestBase {};
+using TestTypes = ::testing::Types<FlightSQLODBCMockTestBase, 
ConnectionRemoteTest>;
+TYPED_TEST_SUITE(ConnectionTest, TestTypes);
+
+TEST(SQLAllocHandle, TestSQLAllocHandleEnv) {
+  SQLHENV env;
+
   // Allocate an environment handle
-  SQLHENV env = nullptr;
   ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, 
&env));
 
-  // Check for valid handle
-  ASSERT_NE(nullptr, env);
+  ASSERT_NE(env, nullptr);
 
   // Free an environment handle
   ASSERT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_ENV, env));
 }
 
+TEST(SQLAllocEnv, TestSQLAllocEnv) {
+  SQLHENV env;
+
+  // Allocate an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env));
+
+  // Free an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env));
+}
+
+TEST(SQLAllocHandle, TestSQLAllocHandleConnect) {
+  SQLHENV env;
+  SQLHDBC conn;
+
+  // Allocate an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env));
+
+  // Allocate a connection using alloc handle
+  ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_DBC, env, &conn));
+
+  // Free a connection handle
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_DBC, conn));
+
+  // Free an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_ENV, env));
+}
+
+TEST(SQLAllocConnect, TestSQLAllocHandleConnect) {
+  SQLHENV env;
+  SQLHDBC conn;
+
+  // Allocate an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env));
+
+  // Allocate a connection using alloc handle
+  ASSERT_EQ(SQL_SUCCESS, SQLAllocConnect(env, &conn));
+
+  // Free a connection handle
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeConnect(conn));
+
+  // Free an environment handle
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env));
+}
+
+TEST(SQLFreeHandle, TestFreeNullHandles) {
+  SQLHENV env = NULL;
+  SQLHDBC conn = NULL;
+  SQLHSTMT stmt = NULL;
+
+  // Verifies attempt to free invalid handle does not cause segfault
+  // Attempt to free null statement handle
+  ASSERT_EQ(SQL_INVALID_HANDLE, SQLFreeHandle(SQL_HANDLE_STMT, stmt));
+
+  // Attempt to free null connection handle
+  ASSERT_EQ(SQL_INVALID_HANDLE, SQLFreeHandle(SQL_HANDLE_DBC, conn));
+
+  // Attempt to free null environment handle
+  ASSERT_EQ(SQL_INVALID_HANDLE, SQLFreeHandle(SQL_HANDLE_ENV, env));
+}
+
 }  // namespace arrow::flight::sql::odbc

Reply via email to