Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions cpp/src/arrow/flight/sql/odbc/entry_points.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText,

SQLRETURN SQL_API SQLFetch(SQLHSTMT stmt) { return arrow::SQLFetch(stmt); }

SQLRETURN SQL_API SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetchOrientation,
SQLLEN fetchOffset, SQLULEN* rowCountPtr,
SQLUSMALLINT* rowStatusArray) {
return arrow::SQLExtendedFetch(stmt, fetchOrientation, fetchOffset, rowCountPtr,
rowStatusArray);
}

SQLRETURN SQL_API SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength,
SQLLEN* indicatorPtr) {
Expand All @@ -150,15 +157,11 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER text

SQLRETURN SQL_API SQLExecute(SQLHSTMT stmt) { return arrow::SQLExecute(stmt); }

SQLRETURN SQL_API SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT columnNumber,
SQLSMALLINT targetType, SQLPOINTER targetValuePtr,
SQLLEN bufferLength, SQLLEN* strLen_or_IndPtr) {
LOG_DEBUG(
"SQLBindCol called with stmt: {}, columnNumber: {}, targetType: {}, "
"targetValuePtr: {}, bufferLength: {}, strLen_or_IndPtr: {}",
stmt, columnNumber, targetType, targetValuePtr, bufferLength,
fmt::ptr(strLen_or_IndPtr));
return SQL_ERROR;
SQLRETURN SQL_API SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength,
SQLLEN* indicatorPtr) {
return arrow::SQLBindCol(stmt, recordNumber, cType, dataPtr, bufferLength,
indicatorPtr);
}

SQLRETURN SQL_API SQLCancel(SQLHSTMT stmt) {
Expand Down
1 change: 1 addition & 0 deletions cpp/src/arrow/flight/sql/odbc/odbc.def
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ EXPORTS
SQLErrorW
SQLExecDirectW
SQLExecute
SQLExtendedFetch
SQLFetch
SQLForeignKeysW
SQLFreeEnv
Expand Down
61 changes: 59 additions & 2 deletions cpp/src/arrow/flight/sql/odbc/odbc_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,16 @@ SQLRETURN SQLFreeStmt(SQLHSTMT handle, SQLUSMALLINT option) {
return SQLFreeHandle(SQL_HANDLE_STMT, handle);
}

// TODO Implement SQLBindCol
case SQL_UNBIND: {
return SQL_SUCCESS;
using ODBC::ODBCDescriptor;
using ODBC::ODBCStatement;
return ODBCStatement::ExecuteWithDiagnostics(handle, SQL_ERROR, [=]() {
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(handle);
ODBCDescriptor* ard = statement->GetARD();
// Unbind columns
ard->SetHeaderField(SQL_DESC_COUNT, (void*)0, 0);
return SQL_SUCCESS;
});
}

// SQLBindParameter is not supported
Expand Down Expand Up @@ -976,6 +983,56 @@ SQLRETURN SQLFetch(SQLHSTMT stmt) {
});
}

SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetchOrientation,
SQLLEN fetchOffset, SQLULEN* rowCountPtr,
SQLUSMALLINT* rowStatusArray) {
// GH-47110: SQLExtendedFetch should return SQL_SUCCESS_WITH_INFO for certain diag
// states
LOG_DEBUG(
"SQLExtendedFetch called with stmt: {}, fetchOrientation: {}, fetchOffset: {}, "
"rowCountPtr: {}, rowStatusArray: {}",
stmt, fetchOrientation, fetchOffset, fmt::ptr(rowCountPtr),
fmt::ptr(rowStatusArray));
using ODBC::ODBCDescriptor;
using ODBC::ODBCStatement;
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
if (fetchOrientation != SQL_FETCH_NEXT) {
throw DriverException("Optional feature not supported.", "HYC00");
}
// fetchOffset is ignored as only SQL_FETCH_NEXT is supported

ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);

// The SQL_ROWSET_SIZE statement attribute specifies the number of rows in the
// rowset.
SQLULEN rowSetSize = statement->GetRowsetSize();
LOG_DEBUG("SQL_ROWSET_SIZE value for SQLExtendedFetch: {}", rowSetSize);
if (statement->Fetch(static_cast<size_t>(rowSetSize), rowCountPtr, rowStatusArray)) {
return SQL_SUCCESS;
} else {
// Reached the end of rowset
return SQL_NO_DATA;
}
});
}

SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr) {
LOG_DEBUG(
"SQLBindCol called with stmt: {}, recordNumber: {}, cType: {}, "
"dataPtr: {}, bufferLength: {}, strLen_or_IndPtr: {}",
stmt, recordNumber, cType, dataPtr, bufferLength, fmt::ptr(indicatorPtr));
using ODBC::ODBCDescriptor;
using ODBC::ODBCStatement;
return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
// GH-47021: implement driver to return indicator value when data pointer is null
ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
ODBCDescriptor* ard = statement->GetARD();
ard->BindCol(recordNumber, cType, dataPtr, bufferLength, indicatorPtr);
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
Expand Down
5 changes: 5 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/odbc_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLengt
SQLRETURN SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLength);
SQLRETURN SQLExecute(SQLHSTMT stmt);
SQLRETURN SQLFetch(SQLHSTMT stmt);
SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetchOrientation,
SQLLEN fetchOffset, SQLULEN* rowCountPtr,
SQLUSMALLINT* rowStatusArray);
SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr);
SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength, SQLLEN* indicatorPtr);
SQLRETURN SQLMoreResults(SQLHSTMT stmt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {

/**
* @brief Returns true if the number of rows fetch was greater than zero.
* rowCountPtr and rowStatusArray are optional arguments, they are only needed for
* SQLExtendedFetch
*/
bool Fetch(size_t rows);
bool Fetch(size_t rows, SQLULEN* rowCountPtr = 0, SQLUSMALLINT* rowStatusArray = 0);
bool isPrepared() const;

void GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ void ODBCStatement::ExecuteDirect(const std::string& query) {
m_isPrepared = false;
}

bool ODBCStatement::Fetch(size_t rows) {
bool ODBCStatement::Fetch(size_t rows, SQLULEN* rowCountPtr,
SQLUSMALLINT* rowStatusArray) {
if (m_hasReachedEndOfResult) {
m_ird->SetRowsProcessed(0);
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have to update rowCountPtr/rowStatusArray here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea this is the simplest way I found. I don't know if the original flightsql-odbc code handles rowCountPtr/rowStatusArray from SQLExtendedFetch. The logic in the driver for setting SQL_ATTR_ROWS_FETCHED_PTR and SQL_ATTR_ROW_STATUS_PTR cannot be reused for rowCountPtr and rowStatusArray respectively, since the spec says the rowCountPtr/rowStatusArray buffers are only used by SQLExtendedFetch and not by SQLFetch or SQLFetchScroll.

@jduo jduo Jul 16, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old driver cheated a bit. It temporarily wrote the SQL_ATTR_ROWS_FETCHED_PTR and SQL_ATTR_ROW_STATUS_PTR with the inputs from SQLExtendedFetch then reverted them after the call finished.

Expand Down Expand Up @@ -350,11 +351,24 @@ bool ODBCStatement::Fetch(size_t rows) {
m_currentArd->NotifyBindingsHavePropagated();
}

size_t rowsFetched = m_currenResult->Move(rows, m_currentArd->GetBindOffset(),
m_currentArd->GetBoundStructOffset(),
m_ird->GetArrayStatusPtr());
uint16_t* row_status_array = 0;
Comment thread
alinaliBQ marked this conversation as resolved.
Outdated
if (rowStatusArray) {
// For SQLExtendedFetch only
row_status_array = rowStatusArray;
Comment thread
alinaliBQ marked this conversation as resolved.
Outdated
} else {
row_status_array = m_ird->GetArrayStatusPtr();
}

size_t rowsFetched =
m_currenResult->Move(rows, m_currentArd->GetBindOffset(),
m_currentArd->GetBoundStructOffset(), row_status_array);
m_ird->SetRowsProcessed(static_cast<SQLULEN>(rowsFetched));

if (rowCountPtr) {
// For SQLExtendedFetch only
*rowCountPtr = rowsFetched;
}

m_rowNumber += rowsFetched;
m_hasReachedEndOfResult = rowsFetched != rows;
return rowsFetched != 0;
Expand Down
Loading