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 2a3c5db790 GH-47713: [C++][FlightRPC] ODBC return number of result
columns (#48036)
2a3c5db790 is described below
commit 2a3c5db7900028fbdf54a4162ce6c5b3f6e9f8e4
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Mon Dec 8 12:33:21 2025 -0800
GH-47713: [C++][FlightRPC] ODBC return number of result columns (#48036)
### Rationale for this change
Implement ODBC to return the number of result columns
### What changes are included in this PR?
- SQLNumResultCols implementation & tests
### Are these changes tested?
Tested locally on MSVC
### Are there any user-facing changes?
N/A
* GitHub Issue: #47713
Authored-by: Alina (Xi) Li <[email protected]>
Signed-off-by: David Li <[email protected]>
---
cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 9 ++++-
.../flight/sql/odbc/odbc_impl/odbc_statement.cc | 10 +++++
.../flight/sql/odbc/odbc_impl/odbc_statement.h | 3 ++
.../arrow/flight/sql/odbc/tests/statement_test.cc | 44 ++++++++++++++++++++++
4 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index 3c6b06740c..8ce3c77181 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -1147,8 +1147,13 @@ SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT*
column_count_ptr) {
ARROW_LOG(DEBUG) << "SQLNumResultCols called with stmt: " << stmt
<< ", column_count_ptr: "
<< static_cast<const void*>(column_count_ptr);
- // GH-47713 TODO: Implement SQLNumResultCols
- return SQL_INVALID_HANDLE;
+
+ using ODBC::ODBCStatement;
+ return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
+ ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
+ statement->GetColumnCount(column_count_ptr);
+ return SQL_SUCCESS;
+ });
}
SQLRETURN SQLRowCount(SQLHSTMT stmt, SQLLEN* row_count_ptr) {
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 e19bb0ed12..3d4f48828f 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
@@ -761,6 +761,16 @@ SQLRETURN ODBCStatement::GetData(SQLSMALLINT
record_number, SQLSMALLINT c_type,
data_ptr, buffer_length, indicator_ptr);
}
+void ODBCStatement::GetColumnCount(SQLSMALLINT* column_count_ptr) {
+ if (!column_count_ptr) {
+ // column count pointer is not valid, do nothing as ODBC spec does not
mention this as
+ // an error
+ return;
+ }
+ size_t column_count = ird_->GetRecords().size();
+ *column_count_ptr = static_cast<SQLSMALLINT>(column_count);
+}
+
void ODBCStatement::GetRowCount(SQLLEN* row_count_ptr) {
if (!row_count_ptr) {
// row count pointer is not valid, do nothing as ODBC spec does not
mention this as an
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 475a9018ca..11c4d529d2 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
@@ -78,6 +78,9 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
SQLRETURN GetData(SQLSMALLINT record_number, SQLSMALLINT c_type, SQLPOINTER
data_ptr,
SQLLEN buffer_length, SQLLEN* indicator_ptr);
+ /// \brief Return number of columns from data set
+ void GetColumnCount(SQLSMALLINT* column_count_ptr);
+
/// \brief Return number of rows affected by an UPDATE, INSERT, or DELETE
statement\
///
/// -1 is returned as driver only supports SELECT statement
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 844df782db..1dd25d73dc 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
@@ -1281,6 +1281,50 @@ TYPED_TEST(StatementTest,
TestSQLNativeSqlReturnsErrorOnBadInputs) {
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, kErrorStateHY090);
}
+TYPED_TEST(StatementTest, SQLNumResultColsReturnsColumnsOnSelect) {
+ SQLSMALLINT column_count = 0;
+ SQLSMALLINT expected_value = 3;
+ SQLWCHAR sql_query[] = L"SELECT 1 AS col1, 'One' AS col2, 3 AS col3";
+ SQLINTEGER query_length = static_cast<SQLINTEGER>(wcslen(sql_query));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckIntColumn(this->stmt, 1, 1);
+ CheckStringColumnW(this->stmt, 2, L"One");
+ CheckIntColumn(this->stmt, 3, 3);
+
+ ASSERT_EQ(SQL_SUCCESS, SQLNumResultCols(this->stmt, &column_count));
+
+ EXPECT_EQ(expected_value, column_count);
+}
+
+TYPED_TEST(StatementTest, SQLNumResultColsReturnsSuccessOnNullptr) {
+ SQLWCHAR sql_query[] = L"SELECT 1 AS col1, 'One' AS col2, 3 AS col3";
+ SQLINTEGER query_length = static_cast<SQLINTEGER>(wcslen(sql_query));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckIntColumn(this->stmt, 1, 1);
+ CheckStringColumnW(this->stmt, 2, L"One");
+ CheckIntColumn(this->stmt, 3, 3);
+
+ ASSERT_EQ(SQL_SUCCESS, SQLNumResultCols(this->stmt, nullptr));
+}
+
+TYPED_TEST(StatementTest, SQLNumResultColsFunctionSequenceErrorOnNoQuery) {
+ SQLSMALLINT column_count = 0;
+ SQLSMALLINT expected_value = 0;
+
+ ASSERT_EQ(SQL_ERROR, SQLNumResultCols(this->stmt, &column_count));
+ VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateHY010);
+
+ EXPECT_EQ(expected_value, column_count);
+}
+
TYPED_TEST(StatementTest, SQLRowCountReturnsNegativeOneOnSelect) {
SQLLEN row_count = 0;
SQLLEN expected_value = -1;