Skip to content

Commit 1f599f4

Browse files
authored
GH-47713: [C++][FlightRPC] ODBC Basic Data Retrieval (#48034)
### Rationale for this change Add implementation for ODBC basic data fetching and retrieval ### What changes are included in this PR? - SQLFetch & SQLGetData implementation, data retrieval bug fixes under `odbc_impl` - tests ### Are these changes tested? Tested on local 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]>
1 parent 6a37b9c commit 1f599f4

File tree

7 files changed

+1158
-56
lines changed

7 files changed

+1158
-56
lines changed

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,8 +1052,24 @@ SQLRETURN SQLExecute(SQLHSTMT stmt) {
10521052

10531053
SQLRETURN SQLFetch(SQLHSTMT stmt) {
10541054
ARROW_LOG(DEBUG) << "SQLFetch called with stmt: " << stmt;
1055-
// GH-47713 TODO: Implement SQLFetch
1056-
return SQL_INVALID_HANDLE;
1055+
1056+
using ODBC::ODBCDescriptor;
1057+
using ODBC::ODBCStatement;
1058+
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
1059+
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
1060+
1061+
// The SQL_ATTR_ROW_ARRAY_SIZE statement attribute specifies the number of rows in the
1062+
// rowset. Retrieve it with GetArraySize.
1063+
ODBCDescriptor* ard = statement->GetARD();
1064+
size_t rows = static_cast<size_t>(ard->GetArraySize());
1065+
1066+
if (statement->Fetch(rows)) {
1067+
return SQL_SUCCESS;
1068+
} else {
1069+
// Reached the end of rowset
1070+
return SQL_NO_DATA;
1071+
}
1072+
});
10571073
}
10581074

10591075
SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetch_orientation,
@@ -1113,8 +1129,14 @@ SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT c_ty
11131129
<< ", record_number: " << record_number << ", c_type: " << c_type
11141130
<< ", data_ptr: " << data_ptr << ", buffer_length: " << buffer_length
11151131
<< ", indicator_ptr: " << static_cast<const void*>(indicator_ptr);
1116-
// GH-47713 TODO: Implement SQLGetData
1117-
return SQL_INVALID_HANDLE;
1132+
1133+
using ODBC::ODBCStatement;
1134+
1135+
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
1136+
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
1137+
return statement->GetData(record_number, c_type, data_ptr, buffer_length,
1138+
indicator_ptr);
1139+
});
11181140
}
11191141

11201142
SQLRETURN SQLMoreResults(SQLHSTMT stmt) {

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set.h"
1919

20+
#include <sql.h>
21+
2022
#include <utility>
2123
#include "arrow/flight/types.h"
2224
#include "arrow/scalar.h"
@@ -212,14 +214,14 @@ void FlightSqlResultSet::Cancel() {
212214
current_chunk_.data = nullptr;
213215
}
214216

215-
bool FlightSqlResultSet::GetData(int column_n, int16_t target_type, int precision,
216-
int scale, void* buffer, size_t buffer_length,
217-
ssize_t* str_len_buffer) {
217+
SQLRETURN FlightSqlResultSet::GetData(int column_n, int16_t target_type, int precision,
218+
int scale, void* buffer, size_t buffer_length,
219+
ssize_t* str_len_buffer) {
218220
reset_get_data_ = true;
219221
// Check if the offset is already at the end.
220222
int64_t& value_offset = get_data_offsets_[column_n - 1];
221223
if (value_offset == -1) {
222-
return false;
224+
return SQL_NO_DATA;
223225
}
224226

225227
ColumnBinding binding(util::ConvertCDataTypeFromV2ToV3(target_type), precision, scale,
@@ -235,7 +237,11 @@ bool FlightSqlResultSet::GetData(int column_n, int16_t target_type, int precisio
235237
diagnostics_, nullptr);
236238

237239
// If there was truncation, the converter would have reported it to the diagnostics.
238-
return diagnostics_.HasWarning();
240+
if (diagnostics_.HasWarning()) {
241+
return SQL_SUCCESS_WITH_INFO;
242+
} else {
243+
return SQL_SUCCESS;
244+
}
239245
}
240246

241247
std::shared_ptr<ResultSetMetadata> FlightSqlResultSet::GetMetadata() { return metadata_; }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class FlightSqlResultSet : public ResultSet {
6161

6262
void Cancel() override;
6363

64-
bool GetData(int column_n, int16_t target_type, int precision, int scale, void* buffer,
65-
size_t buffer_length, ssize_t* str_len_buffer) override;
64+
SQLRETURN GetData(int column_n, int16_t target_type, int precision, int scale,
65+
void* buffer, size_t buffer_length, ssize_t* str_len_buffer) override;
6666

6767
size_t Move(size_t rows, size_t bind_offset, size_t bind_type,
6868
uint16_t* row_status_array) override;

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

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ SQLSMALLINT getc_typeForSQLType(const DescriptorRecord& record) {
129129
case SQL_WLONGVARCHAR:
130130
return SQL_C_WCHAR;
131131

132+
case SQL_BIT:
133+
return SQL_C_BIT;
134+
132135
case SQL_BINARY:
133136
case SQL_VARBINARY:
134137
case SQL_LONGVARBINARY:
@@ -146,13 +149,20 @@ SQLSMALLINT getc_typeForSQLType(const DescriptorRecord& record) {
146149
case SQL_BIGINT:
147150
return record.is_unsigned ? SQL_C_UBIGINT : SQL_C_SBIGINT;
148151

152+
case SQL_NUMERIC:
153+
case SQL_DECIMAL:
154+
return SQL_C_NUMERIC;
155+
156+
case SQL_FLOAT:
149157
case SQL_REAL:
150158
return SQL_C_FLOAT;
151159

152-
case SQL_FLOAT:
153160
case SQL_DOUBLE:
154161
return SQL_C_DOUBLE;
155162

163+
case SQL_GUID:
164+
return SQL_C_GUID;
165+
156166
case SQL_DATE:
157167
case SQL_TYPE_DATE:
158168
return SQL_C_TYPE_DATE;
@@ -165,32 +175,32 @@ SQLSMALLINT getc_typeForSQLType(const DescriptorRecord& record) {
165175
case SQL_TYPE_TIMESTAMP:
166176
return SQL_C_TYPE_TIMESTAMP;
167177

168-
case SQL_C_INTERVAL_DAY:
169-
return SQL_INTERVAL_DAY;
170-
case SQL_C_INTERVAL_DAY_TO_HOUR:
171-
return SQL_INTERVAL_DAY_TO_HOUR;
172-
case SQL_C_INTERVAL_DAY_TO_MINUTE:
173-
return SQL_INTERVAL_DAY_TO_MINUTE;
174-
case SQL_C_INTERVAL_DAY_TO_SECOND:
175-
return SQL_INTERVAL_DAY_TO_SECOND;
176-
case SQL_C_INTERVAL_HOUR:
177-
return SQL_INTERVAL_HOUR;
178-
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
179-
return SQL_INTERVAL_HOUR_TO_MINUTE;
180-
case SQL_C_INTERVAL_HOUR_TO_SECOND:
181-
return SQL_INTERVAL_HOUR_TO_SECOND;
182-
case SQL_C_INTERVAL_MINUTE:
183-
return SQL_INTERVAL_MINUTE;
184-
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
185-
return SQL_INTERVAL_MINUTE_TO_SECOND;
186-
case SQL_C_INTERVAL_SECOND:
187-
return SQL_INTERVAL_SECOND;
188-
case SQL_C_INTERVAL_YEAR:
189-
return SQL_INTERVAL_YEAR;
190-
case SQL_C_INTERVAL_YEAR_TO_MONTH:
191-
return SQL_INTERVAL_YEAR_TO_MONTH;
192-
case SQL_C_INTERVAL_MONTH:
193-
return SQL_INTERVAL_MONTH;
178+
case SQL_INTERVAL_DAY:
179+
return SQL_C_INTERVAL_DAY;
180+
case SQL_INTERVAL_DAY_TO_HOUR:
181+
return SQL_C_INTERVAL_DAY_TO_HOUR;
182+
case SQL_INTERVAL_DAY_TO_MINUTE:
183+
return SQL_C_INTERVAL_DAY_TO_MINUTE;
184+
case SQL_INTERVAL_DAY_TO_SECOND:
185+
return SQL_C_INTERVAL_DAY_TO_SECOND;
186+
case SQL_INTERVAL_HOUR:
187+
return SQL_C_INTERVAL_HOUR;
188+
case SQL_INTERVAL_HOUR_TO_MINUTE:
189+
return SQL_C_INTERVAL_HOUR_TO_MINUTE;
190+
case SQL_INTERVAL_HOUR_TO_SECOND:
191+
return SQL_C_INTERVAL_HOUR_TO_SECOND;
192+
case SQL_INTERVAL_MINUTE:
193+
return SQL_C_INTERVAL_MINUTE;
194+
case SQL_INTERVAL_MINUTE_TO_SECOND:
195+
return SQL_C_INTERVAL_MINUTE_TO_SECOND;
196+
case SQL_INTERVAL_SECOND:
197+
return SQL_C_INTERVAL_SECOND;
198+
case SQL_INTERVAL_YEAR:
199+
return SQL_C_INTERVAL_YEAR;
200+
case SQL_INTERVAL_YEAR_TO_MONTH:
201+
return SQL_C_INTERVAL_YEAR_TO_MONTH;
202+
case SQL_INTERVAL_MONTH:
203+
return SQL_C_INTERVAL_MONTH;
194204

195205
default:
196206
throw DriverException("Unknown SQL type: " + std::to_string(record.concise_type),
@@ -707,9 +717,9 @@ void ODBCStatement::CloseCursor(bool suppress_errors) {
707717
has_reached_end_of_result_ = false;
708718
}
709719

710-
bool ODBCStatement::GetData(SQLSMALLINT record_number, SQLSMALLINT c_type,
711-
SQLPOINTER data_ptr, SQLLEN buffer_length,
712-
SQLLEN* indicator_ptr) {
720+
SQLRETURN ODBCStatement::GetData(SQLSMALLINT record_number, SQLSMALLINT c_type,
721+
SQLPOINTER data_ptr, SQLLEN buffer_length,
722+
SQLLEN* indicator_ptr) {
713723
if (record_number == 0) {
714724
throw DriverException("Bookmarks are not supported", "07009");
715725
} else if (record_number > ird_->GetRecords().size()) {

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
5858
void ExecutePrepared();
5959
void ExecuteDirect(const std::string& query);
6060

61-
/**
62-
* @brief Returns true if the number of rows fetch was greater than zero.
63-
*/
61+
/// \brief Return true if the number of rows fetch was greater than zero.
6462
bool Fetch(size_t rows);
6563
bool IsPrepared() const;
6664

@@ -77,8 +75,8 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
7775

7876
inline SQLULEN GetRowsetSize() { return rowset_size_; }
7977

80-
bool GetData(SQLSMALLINT record_number, SQLSMALLINT c_type, SQLPOINTER data_ptr,
81-
SQLLEN buffer_length, SQLLEN* indicator_ptr);
78+
SQLRETURN GetData(SQLSMALLINT record_number, SQLSMALLINT c_type, SQLPOINTER data_ptr,
79+
SQLLEN buffer_length, SQLLEN* indicator_ptr);
8280

8381
/// \brief Closes the cursor. This does _not_ un-prepare the statement or change
8482
/// bindings.

cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include "arrow/flight/sql/odbc/odbc_impl/types.h"
2626

27+
#include <sqltypes.h>
28+
2729
namespace arrow::flight::sql::odbc {
2830

2931
class ResultSetMetadata;
@@ -87,10 +89,10 @@ class ResultSet {
8789
/// \param buffer Target buffer to be populated.
8890
/// \param buffer_length Target buffer length.
8991
/// \param strlen_buffer Buffer that holds the length of value being fetched.
90-
/// \returns true if there is more data to fetch from the current cell;
91-
/// false if the whole value was already fetched.
92-
virtual bool GetData(int column, int16_t target_type, int precision, int scale,
93-
void* buffer, size_t buffer_length, ssize_t* strlen_buffer) = 0;
92+
/// \return SQLRETURN for SQLGetData.
93+
virtual SQLRETURN GetData(int column, int16_t target_type, int precision, int scale,
94+
void* buffer, size_t buffer_length,
95+
ssize_t* strlen_buffer) = 0;
9496
};
9597

9698
} // namespace arrow::flight::sql::odbc

0 commit comments

Comments
 (0)