diff --git a/cpp/src/arrow/flight/sql/odbc/entry_points.cc b/cpp/src/arrow/flight/sql/odbc/entry_points.cc index 6087eb1635f5..52ab94dd74fe 100644 --- a/cpp/src/arrow/flight/sql/odbc/entry_points.cc +++ b/cpp/src/arrow/flight/sql/odbc/entry_points.cc @@ -186,14 +186,7 @@ SQLRETURN SQL_API SQLCancel(SQLHSTMT stmt) { }); } -SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT stmt) { - LOG_DEBUG("SQLCloseCursor called with stmt: {}", stmt); - return ODBC::ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { - throw driver::odbcabstraction::DriverException("SQLCloseCursor is not implemented", - "IM001"); - return SQL_ERROR; - }); -} +SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT stmt) { return arrow::SQLCloseCursor(stmt); } SQLRETURN SQL_API SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLUSMALLINT fieldIdentifier, diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index e87e713caa88..23f7a2b011cf 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -179,12 +179,8 @@ SQLRETURN SQLFreeStmt(SQLHSTMT handle, SQLUSMALLINT option) { case SQL_CLOSE: { using ODBC::ODBCStatement; - ODBCStatement* statement = reinterpret_cast(handle); - - return ODBCStatement::ExecuteWithDiagnostics(statement, SQL_ERROR, [=]() { - if (!statement) { - return SQL_INVALID_HANDLE; - } + return ODBCStatement::ExecuteWithDiagnostics(handle, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(handle); // Close cursor with suppressErrors set to true statement->closeCursor(true); @@ -1060,6 +1056,19 @@ SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType }); } +SQLRETURN SQLCloseCursor(SQLHSTMT stmt) { + LOG_DEBUG("SQLCloseCursor called with stmt: {}", stmt); + using ODBC::ODBCStatement; + return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(stmt); + + // Close cursor with suppressErrors set to false + statement->closeCursor(false); + + return SQL_SUCCESS; + }); +} + SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType, SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr) { // GH-46979: support SQL_C_GUID data type @@ -1080,7 +1089,6 @@ SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType SQLRETURN SQLMoreResults(SQLHSTMT stmt) { LOG_DEBUG("SQLMoreResults called with stmt: {}", stmt); - // TODO: write tests for SQLMoreResults using ODBC::ODBCStatement; // Multiple result sets not supported. Return SQL_NO_DATA by default. return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { @@ -1092,7 +1100,6 @@ SQLRETURN SQLMoreResults(SQLHSTMT stmt) { SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) { LOG_DEBUG("SQLNumResultCols called with stmt: {}, columnCountPtr: {}", stmt, fmt::ptr(columnCountPtr)); - // TODO: write tests for SQLNumResultCols using ODBC::ODBCStatement; return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { ODBCStatement* statement = reinterpret_cast(stmt); @@ -1104,7 +1111,6 @@ SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) { SQLRETURN SQLRowCount(SQLHSTMT stmt, SQLLEN* rowCountPtr) { LOG_DEBUG("SQLRowCount called with stmt: {}, columnCountPtr: {}", stmt, fmt::ptr(rowCountPtr)); - // TODO: write tests for SQLRowCount using ODBC::ODBCStatement; return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { ODBCStatement* statement = reinterpret_cast(stmt); diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.h b/cpp/src/arrow/flight/sql/odbc/odbc_api.h index d9181ff329b6..e4139f963336 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.h +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.h @@ -74,6 +74,7 @@ SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetchOrientation, SQLRETURN SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT fetchOrientation, SQLLEN fetchOffset); SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType, SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr); +SQLRETURN SQLCloseCursor(SQLHSTMT stmt); SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType, SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr); SQLRETURN SQLMoreResults(SQLHSTMT stmt); diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 70ee80ea08f2..c990f766df5f 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -703,7 +703,7 @@ void ODBCStatement::RevertAppDescriptor(bool isApd) { void ODBCStatement::closeCursor(bool suppressErrors) { if (!suppressErrors && !m_currenResult) { - throw DriverException("Invalid cursor state", "28000"); + throw DriverException("Invalid cursor state", "24000"); } if (m_currenResult) { 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 fee8ab5f7002..89fce1a23d22 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc @@ -2470,4 +2470,64 @@ TYPED_TEST(FlightSQLODBCTestBase, SQLNumResultColsFunctionSequenceErrorOnNoQuery this->disconnect(); } +TYPED_TEST(FlightSQLODBCTestBase, TestSQLFreeStmtSQLClose) { + this->connect(); + + std::wstring wsql = L"SELECT 1;"; + std::vector sql0(wsql.begin(), wsql.end()); + + SQLRETURN ret = + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size())); + + EXPECT_EQ(ret, SQL_SUCCESS); + + ret = SQLFreeStmt(this->stmt, SQL_CLOSE); + + EXPECT_EQ(ret, SQL_SUCCESS); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLCloseCursor) { + this->connect(); + + std::wstring wsql = L"SELECT 1;"; + std::vector sql0(wsql.begin(), wsql.end()); + + SQLRETURN ret = + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size())); + + EXPECT_EQ(ret, SQL_SUCCESS); + + ret = SQLCloseCursor(this->stmt); + + EXPECT_EQ(ret, SQL_SUCCESS); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLFreeStmtSQLCloseWithoutCursor) { + // SQLFreeStmt(SQL_CLOSE) does not throw error with invalid cursor + this->connect(); + + SQLRETURN ret = SQLFreeStmt(this->stmt, SQL_CLOSE); + + EXPECT_EQ(ret, SQL_SUCCESS); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLCloseCursorWithoutCursor) { + this->connect(); + + SQLRETURN ret = SQLCloseCursor(this->stmt); + + EXPECT_EQ(ret, SQL_ERROR); + + // Verify invalid cursor error state is returned + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, error_state_24000); + + this->disconnect(); +} + } // namespace arrow::flight::sql::odbc