Skip to content

General bugfixes for cursor results - #125

Open
Swiddis wants to merge 2 commits into
opensearch-project:mainfrom
Swiddis:fix/cursor-page-error
Open

Swiddis wants to merge 2 commits into
opensearch-project:mainfrom
Swiddis:fix/cursor-page-error

Conversation

@Swiddis

@Swiddis Swiddis commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Description

2 bugfixes related to pagination: fix fetching beyond page 1 overall, and fix partial failures when a page at N>1 fails.

Issues Resolved

[List any issues this PR will resolve]

Check List

  • New functionality includes testing.
    • All tests pass, including unit test, integration test and doctest
  • New functionality has been documented.
    • New functionality has javadoc added
    • New functionality has user manual doc added
  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

GetNextResultSet called the public SQLNumResultCols. In a Linux shared
object that call is interposable and binds to the driver manager's
export, which doesn't know the driver's own statement handle and fails,
so GetNextResultSet returned before popping a page: SQLFetch stopped
after the first FetchSize rows with SQL_NO_DATA. Call the internal
OPENSEARCHAPI_NumResultCols, which is what macOS and Windows already
reached through the same call.

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
When a later cursor page failed (an HTTP error such as a proxy's 502, a
dropped connection, an unparseable body), SendCursorQueries stopped and
GetNextResultSet took the empty queue for the end of data: SQLFetch
returned SQL_NO_DATA on a truncated result set. A failure on the second
page truncated it to the first; one on a later page left the server
cursor id set, so the fetch loop spun forever. A NULL response returned
without clearing m_is_retrieving, so PopResult waited forever.

The cursor thread now records its error in a dedicated, mutex-guarded
member (m_error_details is read unlocked by the app thread, so the
thread no longer writes it), always clears m_is_retrieving, and makes a
best-effort close of the server cursor after an error response.
GetNextResultSet returns SQL_ERROR when a page failed and keeps the
cursor id, and SQLFetch reports it, with the status and bounded body,
once the rows received before it are fetched: SQLSTATE 08S01 for a
connection failure, HY000 otherwise. The first page's cursor id is kept
so a failed second page has a cursor to report on. m_is_retrieving is
now atomic and set before the thread starts, so a PopResult that wins
the race doesn't see the end of data either. The next ExecDirect clears
the error.

Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
@Swiddis Swiddis changed the title Fetch cursor pages past the first on Linux General bugfixes for cursor results Nov 6, 2026

@mengweieric mengweieric left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Checked both fixes against the artifact and the code paths.

The Linux interposition claim holds: the built libsqlodbc.so exports SQLNumResultCols with default visibility and the dynamic section has no SYMBOLIC flag, so a call from inside the library goes through the PLT and binds to whichever DSO the loader saw first, which is the driver manager when the app links unixODBC. Calling OPENSEARCHAPI_NumResultCols directly sidesteps that, and the head has no remaining undefined reference to SQLNumResultCols. Worth knowing this pattern applies to any other internal call into a public SQL* entry point on Linux.

The cursor error path is well put together. The separate mutex guarded member is the right choice given the app thread reads m_error_details unlocked, m_is_retrieving as atomic and set before the thread starts closes the PopResult race, and every exit from SendCursorQueries now reaches the m_is_retrieving reset so PopResult cannot wait forever. In ExtendedFetch, a break with rows_left falls through to return the rows already received, and because the server cursor id is kept, the next fetch re enters GetNextResultSet with report_error true and returns SQL_ERROR with the status and bounded body. The SQLSTATE split (08S01 for a connection failure, HY000 otherwise) matches what the error types mean. SendCloseCursorRequest wrapped in its own try is necessary now that it runs on the detached thread.

All 44 ut_conn tests pass on both e2e legs, including the five new TestCursorError cases, and the two pagination tests still pass. One thing to be aware of: the e2e job runs ut_conn, which drives OpenSearchCommunication directly, so the SQLNumResultCols fix in GetNextResultSet is not exercised by CI. The itodbc suites would cover it once they build on unixODBC. LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants