ctlib: fix ct_results() hang on empty result set with CS_DYNAMIC_CMD#726
Open
tdaniel22 wants to merge 1 commit into
Open
ctlib: fix ct_results() hang on empty result set with CS_DYNAMIC_CMD#726tdaniel22 wants to merge 1 commit into
tdaniel22 wants to merge 1 commit into
Conversation
When a prepared statement (CS_DYNAMIC_CMD) returns an empty result set, ct_results() hangs indefinitely in tds_process_tokens(). In 4fea9e7 ("fix ct-library rows count", 2016-04-30), CS_ROWFMT_RESULT was split from CS_COMPUTEFMT_RESULT into its own case, with an intentional fallthrough to CS_ROW_RESULT for non-cursor/non-dynamic commands. The CS_DYNAMIC_CMD check was added to break before the fallthrough. Later, 4a8e777 (2016-05-11) restructured the CS_ROWFMT_RESULT case, replacing the fallthrough with an explicit process_flags update that adds TDS_STOPAT_DONE. However, the CS_DYNAMIC_CMD early break was preserved, now causing it to skip the process_flags update instead of just skipping a fallthrough. Without TDS_STOPAT_DONE, ct_results() consumes the DONE token internally and returns a manufactured CS_ROW_RESULT. The caller then calls ct_fetch(), which calls tds_peek() to check the next token. Since the DONE token was already consumed and TDS state is IDLE, tds_peek() blocks forever on a socket read that will never produce data. With rows present, TDS_STOPAT_ROW (already set) catches row tokens before the DONE, so ct_fetch sees the DONE via tds_peek and returns CS_END_DATA. Only empty result sets trigger the hang. The fix removes CS_DYNAMIC_CMD from the early break so that process_flags is updated with TDS_STOPAT_DONE after CS_ROWFMT_RESULT, consistent with all other non-cursor command types. Tested with SQL Anywhere 17 over TDS 5.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a prepared statement (CS_DYNAMIC_CMD) returns an empty result set, ct_results() hangs indefinitely in tds_process_tokens().
In 4fea9e7 ("fix ct-library rows count", 2016-04-30), CS_ROWFMT_RESULT was split from CS_COMPUTEFMT_RESULT into its own case, with an intentional fallthrough to CS_ROW_RESULT for non-cursor/non-dynamic commands. The CS_DYNAMIC_CMD check was added to break before the fallthrough.
Later, 4a8e777 (2016-05-11) restructured the CS_ROWFMT_RESULT case, replacing the fallthrough with an explicit process_flags update that adds TDS_STOPAT_DONE. However, the CS_DYNAMIC_CMD early break was preserved, now causing it to skip the process_flags update instead of just skipping a fallthrough.
Without TDS_STOPAT_DONE, ct_results() consumes the DONE token internally and returns a manufactured CS_ROW_RESULT. The caller then calls ct_fetch(), which calls tds_peek() to check the next token. Since the DONE token was already consumed and TDS state is IDLE, tds_peek() blocks forever on a socket read that will never produce data.
With rows present, TDS_STOPAT_ROW (already set) catches row tokens before the DONE, so ct_fetch sees the DONE via tds_peek and returns CS_END_DATA. Only empty result sets trigger the hang.
The fix removes CS_DYNAMIC_CMD from the early break so that process_flags is updated with TDS_STOPAT_DONE after CS_ROWFMT_RESULT, consistent with all other non-cursor command types.
Tested with SQL Anywhere 17 over TDS 5.0.