Skip to content

Commit 3868ba9

Browse files
authored
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]>
1 parent 90261d6 commit 3868ba9

File tree

4 files changed

+37
-10
lines changed

4 files changed

+37
-10
lines changed

cpp/src/arrow/flight/sql/odbc/odbc_api.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,8 +1092,16 @@ SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT c_ty
10921092

10931093
SQLRETURN SQLCloseCursor(SQLHSTMT stmt) {
10941094
ARROW_LOG(DEBUG) << "SQLCloseCursor called with stmt: " << stmt;
1095-
// GH-47717 TODO: Implement SQLCloseCursor
1096-
return SQL_INVALID_HANDLE;
1095+
1096+
using ODBC::ODBCStatement;
1097+
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
1098+
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
1099+
1100+
// Close cursor with suppressErrors set to false
1101+
statement->CloseCursor(false);
1102+
1103+
return SQL_SUCCESS;
1104+
});
10971105
}
10981106

10991107
SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT c_type,

cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ void ODBCStatement::RevertAppDescriptor(bool isApd) {
693693

694694
void ODBCStatement::CloseCursor(bool suppress_errors) {
695695
if (!suppress_errors && !current_result_) {
696-
throw DriverException("Invalid cursor state", "28000");
696+
throw DriverException("Invalid cursor state", "24000");
697697
}
698698

699699
if (current_result_) {

cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,11 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
8080
bool GetData(SQLSMALLINT record_number, SQLSMALLINT c_type, SQLPOINTER data_ptr,
8181
SQLLEN buffer_length, SQLLEN* indicator_ptr);
8282

83-
/**
84-
* @brief Closes the cursor. This does _not_ un-prepare the statement or change
85-
* bindings.
86-
*/
83+
/// \brief Closes the cursor. This does _not_ un-prepare the statement or change
84+
/// bindings.
8785
void CloseCursor(bool suppress_errors);
8886

89-
/**
90-
* @brief Releases this statement from memory.
91-
*/
87+
/// \brief Releases this statement from memory.
9288
void ReleaseStatement();
9389

9490
void GetTables(const std::string* catalog, const std::string* schema,

cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,27 @@ TYPED_TEST(StatementTest, TestSQLNativeSqlReturnsErrorOnBadInputs) {
217217
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY090);
218218
}
219219

220+
TYPED_TEST(StatementTest, TestSQLCloseCursor) {
221+
std::wstring wsql = L"SELECT 1;";
222+
std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
223+
224+
ASSERT_EQ(SQL_SUCCESS,
225+
SQLExecDirect(this->stmt, &sql0[0], static_cast<SQLINTEGER>(sql0.size())));
226+
227+
ASSERT_EQ(SQL_SUCCESS, SQLCloseCursor(this->stmt));
228+
}
229+
230+
TYPED_TEST(StatementTest, TestSQLFreeStmtSQLCloseWithoutCursor) {
231+
// Verify SQLFreeStmt(SQL_CLOSE) does not throw error with invalid cursor
232+
233+
ASSERT_EQ(SQL_SUCCESS, SQLFreeStmt(this->stmt, SQL_CLOSE));
234+
}
235+
236+
TYPED_TEST(StatementTest, TestSQLCloseCursorWithoutCursor) {
237+
ASSERT_EQ(SQL_ERROR, SQLCloseCursor(this->stmt));
238+
239+
// Verify invalid cursor error state is returned
240+
VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState24000);
241+
}
242+
220243
} // namespace arrow::flight::sql::odbc

0 commit comments

Comments
 (0)