Skip to content

Commit 2d36a46

Browse files
authored
SQLCloseCursor Implementation
* fix error state to return the correct state 24000. * add tests for SQLCloseCursor and SQLFreeStmt(SQL_CLOSE)
1 parent 328485f commit 2d36a46

File tree

5 files changed

+78
-18
lines changed

5 files changed

+78
-18
lines changed

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,7 @@ SQLRETURN SQL_API SQLCancel(SQLHSTMT stmt) {
186186
});
187187
}
188188

189-
SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT stmt) {
190-
LOG_DEBUG("SQLCloseCursor called with stmt: {}", stmt);
191-
return ODBC::ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
192-
throw driver::odbcabstraction::DriverException("SQLCloseCursor is not implemented",
193-
"IM001");
194-
return SQL_ERROR;
195-
});
196-
}
189+
SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT stmt) { return arrow::SQLCloseCursor(stmt); }
197190

198191
SQLRETURN SQL_API SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT recordNumber,
199192
SQLUSMALLINT fieldIdentifier,

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,8 @@ SQLRETURN SQLFreeStmt(SQLHSTMT handle, SQLUSMALLINT option) {
179179
case SQL_CLOSE: {
180180
using ODBC::ODBCStatement;
181181

182-
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(handle);
183-
184-
return ODBCStatement::ExecuteWithDiagnostics(statement, SQL_ERROR, [=]() {
185-
if (!statement) {
186-
return SQL_INVALID_HANDLE;
187-
}
182+
return ODBCStatement::ExecuteWithDiagnostics(handle, SQL_ERROR, [=]() {
183+
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(handle);
188184

189185
// Close cursor with suppressErrors set to true
190186
statement->closeCursor(true);
@@ -1060,6 +1056,19 @@ SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType
10601056
});
10611057
}
10621058

1059+
SQLRETURN SQLCloseCursor(SQLHSTMT stmt) {
1060+
LOG_DEBUG("SQLCloseCursor called with stmt: {}", stmt);
1061+
using ODBC::ODBCStatement;
1062+
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
1063+
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
1064+
1065+
// Close cursor with suppressErrors set to false
1066+
statement->closeCursor(false);
1067+
1068+
return SQL_SUCCESS;
1069+
});
1070+
}
1071+
10631072
SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
10641073
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr) {
10651074
// GH-46979: support SQL_C_GUID data type
@@ -1080,7 +1089,6 @@ SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType
10801089

10811090
SQLRETURN SQLMoreResults(SQLHSTMT stmt) {
10821091
LOG_DEBUG("SQLMoreResults called with stmt: {}", stmt);
1083-
// TODO: write tests for SQLMoreResults
10841092
using ODBC::ODBCStatement;
10851093
// Multiple result sets not supported. Return SQL_NO_DATA by default.
10861094
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
@@ -1092,7 +1100,6 @@ SQLRETURN SQLMoreResults(SQLHSTMT stmt) {
10921100
SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) {
10931101
LOG_DEBUG("SQLNumResultCols called with stmt: {}, columnCountPtr: {}", stmt,
10941102
fmt::ptr(columnCountPtr));
1095-
// TODO: write tests for SQLNumResultCols
10961103
using ODBC::ODBCStatement;
10971104
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
10981105
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
@@ -1104,7 +1111,6 @@ SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) {
11041111
SQLRETURN SQLRowCount(SQLHSTMT stmt, SQLLEN* rowCountPtr) {
11051112
LOG_DEBUG("SQLRowCount called with stmt: {}, columnCountPtr: {}", stmt,
11061113
fmt::ptr(rowCountPtr));
1107-
// TODO: write tests for SQLRowCount
11081114
using ODBC::ODBCStatement;
11091115
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
11101116
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetchOrientation,
7474
SQLRETURN SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT fetchOrientation, SQLLEN fetchOffset);
7575
SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
7676
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr);
77+
SQLRETURN SQLCloseCursor(SQLHSTMT stmt);
7778
SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
7879
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr);
7980
SQLRETURN SQLMoreResults(SQLHSTMT stmt);

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

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

704704
void ODBCStatement::closeCursor(bool suppressErrors) {
705705
if (!suppressErrors && !m_currenResult) {
706-
throw DriverException("Invalid cursor state", "28000");
706+
throw DriverException("Invalid cursor state", "24000");
707707
}
708708

709709
if (m_currenResult) {

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,4 +2470,64 @@ TYPED_TEST(FlightSQLODBCTestBase, SQLNumResultColsFunctionSequenceErrorOnNoQuery
24702470
this->disconnect();
24712471
}
24722472

2473+
TYPED_TEST(FlightSQLODBCTestBase, TestSQLFreeStmtSQLClose) {
2474+
this->connect();
2475+
2476+
std::wstring wsql = L"SELECT 1;";
2477+
std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
2478+
2479+
SQLRETURN ret =
2480+
SQLExecDirect(this->stmt, &sql0[0], static_cast<SQLINTEGER>(sql0.size()));
2481+
2482+
EXPECT_EQ(ret, SQL_SUCCESS);
2483+
2484+
ret = SQLFreeStmt(this->stmt, SQL_CLOSE);
2485+
2486+
EXPECT_EQ(ret, SQL_SUCCESS);
2487+
2488+
this->disconnect();
2489+
}
2490+
2491+
TYPED_TEST(FlightSQLODBCTestBase, TestSQLCloseCursor) {
2492+
this->connect();
2493+
2494+
std::wstring wsql = L"SELECT 1;";
2495+
std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
2496+
2497+
SQLRETURN ret =
2498+
SQLExecDirect(this->stmt, &sql0[0], static_cast<SQLINTEGER>(sql0.size()));
2499+
2500+
EXPECT_EQ(ret, SQL_SUCCESS);
2501+
2502+
ret = SQLCloseCursor(this->stmt);
2503+
2504+
EXPECT_EQ(ret, SQL_SUCCESS);
2505+
2506+
this->disconnect();
2507+
}
2508+
2509+
TYPED_TEST(FlightSQLODBCTestBase, TestSQLFreeStmtSQLCloseWithoutCursor) {
2510+
// SQLFreeStmt(SQL_CLOSE) does not throw error with invalid cursor
2511+
this->connect();
2512+
2513+
SQLRETURN ret = SQLFreeStmt(this->stmt, SQL_CLOSE);
2514+
2515+
EXPECT_EQ(ret, SQL_SUCCESS);
2516+
2517+
this->disconnect();
2518+
}
2519+
2520+
TYPED_TEST(FlightSQLODBCTestBase, TestSQLCloseCursorWithoutCursor) {
2521+
this->connect();
2522+
2523+
SQLRETURN ret = SQLCloseCursor(this->stmt);
2524+
2525+
EXPECT_EQ(ret, SQL_ERROR);
2526+
2527+
// Verify invalid cursor error state is returned
2528+
VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, error_state_24000);
2529+
2530+
this->disconnect();
2531+
}
2532+
24732533
} // namespace arrow::flight::sql::odbc

0 commit comments

Comments
 (0)