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 3868ba95eb GH-47717: [C++][FlightRPC] ODBC close cursor (#48043)
3868ba95eb is described below

commit 3868ba95eb0bfc3b70f4d72f7a1e3b3d017d6231
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Thu Dec 4 16:08:48 2025 -0800

    GH-47717: [C++][FlightRPC] ODBC close cursor (#48043)
    
    ### Rationale for this change
    Implement support for explicitly closing cursor in ODBC. Cursors are 
implicitly closed when ODBC disconnects, and this implementation allows BI 
tools to close a cursor by passing its associated statement handle.
    
    ### What changes are included in this PR?
    - SQLCloseCursor & Tests
    - Fix close cursor state code to be 24000
    ### Are these changes tested?
    Tested on local MSVC
    ### Are there any user-facing changes?
    
    N/A
    
    * GitHub Issue: #47717
    
    Authored-by: Alina (Xi) Li <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 cpp/src/arrow/flight/sql/odbc/odbc_api.cc          | 12 +++++++++--
 .../flight/sql/odbc/odbc_impl/odbc_statement.cc    |  2 +-
 .../flight/sql/odbc/odbc_impl/odbc_statement.h     | 10 +++-------
 .../arrow/flight/sql/odbc/tests/statement_test.cc  | 23 ++++++++++++++++++++++
 4 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index 76d0024680..c4604f8aa2 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -1092,8 +1092,16 @@ SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT 
record_number, SQLSMALLINT c_ty
 
 SQLRETURN SQLCloseCursor(SQLHSTMT stmt) {
   ARROW_LOG(DEBUG) << "SQLCloseCursor called with stmt: " << stmt;
-  // GH-47717 TODO: Implement SQLCloseCursor
-  return SQL_INVALID_HANDLE;
+
+  using ODBC::ODBCStatement;
+  return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
+    ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
+
+    // Close cursor with suppressErrors set to false
+    statement->CloseCursor(false);
+
+    return SQL_SUCCESS;
+  });
 }
 
 SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT 
c_type,
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
index 8234017e7f..aa490816b7 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
@@ -693,7 +693,7 @@ void ODBCStatement::RevertAppDescriptor(bool isApd) {
 
 void ODBCStatement::CloseCursor(bool suppress_errors) {
   if (!suppress_errors && !current_result_) {
-    throw DriverException("Invalid cursor state", "28000");
+    throw DriverException("Invalid cursor state", "24000");
   }
 
   if (current_result_) {
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
index 8e128db1bd..2409c17d29 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
@@ -80,15 +80,11 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
   bool GetData(SQLSMALLINT record_number, SQLSMALLINT c_type, SQLPOINTER 
data_ptr,
                SQLLEN buffer_length, SQLLEN* indicator_ptr);
 
-  /**
-   * @brief Closes the cursor. This does _not_ un-prepare the statement or 
change
-   * bindings.
-   */
+  /// \brief Closes the cursor. This does _not_ un-prepare the statement or 
change
+  /// bindings.
   void CloseCursor(bool suppress_errors);
 
-  /**
-   * @brief Releases this statement from memory.
-   */
+  /// \brief Releases this statement from memory.
   void ReleaseStatement();
 
   void GetTables(const std::string* catalog, const std::string* schema,
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc 
b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
index a83855c218..4dbe87ddbb 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
@@ -217,4 +217,27 @@ TYPED_TEST(StatementTest, 
TestSQLNativeSqlReturnsErrorOnBadInputs) {
   VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY090);
 }
 
+TYPED_TEST(StatementTest, TestSQLCloseCursor) {
+  std::wstring wsql = L"SELECT 1;";
+  std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+  ASSERT_EQ(SQL_SUCCESS,
+            SQLExecDirect(this->stmt, &sql0[0], 
static_cast<SQLINTEGER>(sql0.size())));
+
+  ASSERT_EQ(SQL_SUCCESS, SQLCloseCursor(this->stmt));
+}
+
+TYPED_TEST(StatementTest, TestSQLFreeStmtSQLCloseWithoutCursor) {
+  // Verify SQLFreeStmt(SQL_CLOSE) does not throw error with invalid cursor
+
+  ASSERT_EQ(SQL_SUCCESS, SQLFreeStmt(this->stmt, SQL_CLOSE));
+}
+
+TYPED_TEST(StatementTest, TestSQLCloseCursorWithoutCursor) {
+  ASSERT_EQ(SQL_ERROR, SQLCloseCursor(this->stmt));
+
+  // Verify invalid cursor error state is returned
+  VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState24000);
+}
+
 }  // namespace arrow::flight::sql::odbc

Reply via email to