mssql-odbc: resolve SQL_C_DEFAULT in SQLGetData instead of rejecting it with HYC00 - #481
mssql-odbc: resolve SQL_C_DEFAULT in SQLGetData instead of rejecting it with HYC00#481David Engel (David-Engel) wants to merge 8 commits into
Conversation
SQLBindCol has resolved SQL_C_DEFAULT from the IRD since AB#47481, but SQLGetData still answered HYC00, so the driver gave the same placeholder two answers depending only on how an application read a column. Resolve once ahead of the captured/PLP dispatch through the same odbc_sql_type -> resolve_default_c_type pair, reading the declared ODBC version before the STMT lock to keep parent-before-child lock ordering. A resolved fixed-width target the caller's BufferLength cannot hold keeps the placeholder and reports HYC00 rather than writing past the buffer. Unlike the bound resolver, BufferLength 0 is not exempt here: on SQLGetData that value is also how an application asks for a length without wanting a value written. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new streaming tests can exercise buffered delivery instead of the intended PLP path.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Resolves SQL_C_DEFAULT in SQLGetData using column metadata and ODBC version.
Changes:
- Adds default C-type resolution and fixed-width buffer protection.
- Shares fixed-width sizing logic with bound fetches.
- Adds unit/E2E coverage and documents deviations.
File summaries
| File | Description |
|---|---|
mssql-odbc/src/api/get_data.rs |
Implements resolution and tests. |
mssql-odbc/src/api/fetch_scroll.rs |
Exposes shared stride helper. |
mssql-odbc/tests/e2e/tests/get_data_test.cpp |
Adds default-target E2E tests. |
mssql-odbc/docs/typed-columnar-fetch-plan.md |
Documents behavior. |
.github/instructions/mssql-odbc.instructions.md |
Records parity deviations. |
Review details
Suppressed comments (1)
mssql-odbc/tests/e2e/tests/get_data_test.cpp:1912
- This payload only exceeds the buffer at the default 8,000-byte packet size. The test harness accepts connection-string overrides, and with a supported 32,768-byte packet size the 18,000-byte UTF-16 value can be captured whole, making this “streaming” test vacuous. Size it above twice the maximum packet size as well.
ASSERT_SQL_OK(ExecDirect("SELECT REPLICATE(CAST(N'A' AS NVARCHAR(MAX)), 9000)"),
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql-odbc/src/api/get_data.rs🔗 Quick Links |
ttk (Theekshna)
left a comment
There was a problem hiding this comment.
Automated note: this review was generated by an authorized unattended run.
This PR resolves SQL_C_DEFAULT for SQLGetData from column metadata plus the declared ODBC version, reuses the fixed-width slot check from the bound-fetch path, and adds unit/e2e coverage plus divergence docs. I checked the reviewed head against the full worktree diff, prior review threads, and the posted coverage report, then independently verified one finding by reading stream_active_plp_chunk and the new e2e test file directly.
| Severity | Count |
|---|---|
| Blocking | 0 |
| Suggestion | 1 |
| Nit | 0 |
Blocking: none.
Suggestion:
mssql-odbc/src/api/get_data.rs:362— the new doc comment says avarbinary/imagecolumn "still serves only as the zero-length length probe" once resolved toSQL_C_BINARY. That is only true on the captured-value path. For a PLP-sized column (VARBINARY(MAX),image),resume_row_to_columnreturnsCursorColumn::PlpStreaming, andstream_active_plp_chunkrejects any target other thanSQL_C_CHAR/SQL_C_WCHARwithHYC00before it ever looks atbuffer_length(get_data.rs:746). So a zero-length probe against a non-NULLVARBINARY(MAX)column also returnsHYC00, not the length the doc claims. I confirmed the new e2e testDefaultTargetOnABinaryColumnIsStillUnimplemented(get_data_test.cpp:1853) only exercisesVARBINARY(8), andNullVarbinaryMaxToBinaryTargetReportsNull(get_data_test.cpp:1604) only covers the NULL leg, which never reaches the PLP-streaming branch — so the MAX/non-NULL case is untested. I'm keeping this as a Suggestion rather than Blocking: the underlying HYC00-for-PLP-binary behavior is pre-existing and already tracked (AB#47239, and this same file says so explicitly at get_data.rs:815-816 for the streaming path), so this PR does not regress anything a caller could previously do. The fix is scoping the doc comment to the non-PLP case (or adding the MAX-column caveat) and adding a regression test that pins today'sHYC00for a non-NULLVARBINARY(MAX)zero-length probe, so a future PR that actually implements the PLP-binary probe has something to flip red.
Nit: none.
Checks specific to this run: msodbcsql parity is not separately applicable here since the observed behavior matches the driver's own explicit posture (HYC00, tracked by AB#47239); the PR description/checklist match the diff and link AB#47815/AB#47239; gh pr checks is green and consistent with the checklist; no undocumented divergence found beyond what's already recorded in the new doc comments; no AI-slop found beyond the one overclaim above.
A varbinary(max)/image column refuses even the zero-length probe: the PLP target gate admits only the two character targets before it looks at BufferLength, so there is no probe branch to reach. Only a non-PLP varbinary(n) answers it. Add an e2e case pinning that the defaulted spelling agrees with an explicit SQL_C_BINARY on a MAX column, which is the invariant this change owns; the HYC00 itself stays with AB#47239. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ttk (Theekshna)
left a comment
There was a problem hiding this comment.
Automated unattended run — comment-only, not checked by a human before posting.
Summary
Re-review triggered by head move. My prior review (9fb61ed3, findings=1, Suggestion) flagged that the doc comment at get_data.rs:362 overclaimed the SQL_C_BINARY zero-length probe worked uniformly, when stream_active_plp_chunk actually rejects any non-character target — including the probe — before it looks at buffer_length for a PLP (varbinary(max)/image) column.
Both commits since then (c25da708, 471933dd) fix exactly that:
- The doc comment at
get_data.rs:362(and the mirrored text intyped-columnar-fetch-plan.mdand.github/instructions/mssql-odbc.instructions.md) now correctly splits non-PLP (varbinary(n): probe works) from PLP (varbinary(max)/image: probe also refused) — I independently re-readstream_active_plp_chunk(get_data.rs:761) and confirmed the target-type check (SQL_C_CHAR/SQL_C_WCHARonly) runs before anybuffer_lengthinspection, matching the new claim exactly. - A new e2e test,
DefaultTargetOnABinaryMaxColumnRefusesEvenTheProbe(get_data_test.cpp), pins the previously-untested case: asserts a non-NULLVARBINARY(MAX)zero-length probe returnsHYC00for both the defaulted and explicitSQL_C_BINARYspellings, and that the two agree — which is the invariant this PR owns, correctly leaving theHYC00itself to AB#47239. - All three doc locations (inline comment, design doc, divergence registry) now say the same thing consistently.
No new issues found in the diff since the last review.
CI note (not a finding): coverage-report shows failed — I inspected the log rather than assuming a code problem: it's the GH Actions poller timing out after 75 minutes waiting for a Cobertura artifact from the ADO build (Coverage artifact not ready yet, 12 attempts, none ever appearing), while ADO's own Merge Coverage job passed. This looks like cross-pipeline artifact-handoff infra flakiness, unrelated to this PR's diff. Did not re-run it per this run's restrictions — flagging for a human to judge, per SKILL.md's guidance that a reviewer's infrastructure trouble isn't the author's problem.
| Check | Result |
|---|---|
| msodbcsql parity | Already addressed correctly in the PR itself — Sql2CDefault on the shared GetColData path is cited as the reference behavior, and the driver's posture (HYC00 for PLP binary) is pre-existing and tracked (AB#47239), not a new divergence. |
| Test sufficiency | The gap from my prior review is now closed with DefaultTargetOnABinaryMaxColumnRefusesEvenTheProbe. No other untested branch found in the delta since the last review (both new commits are doc + one new test). |
| Divergence documented | Yes, consistently across all three locations now. |
| PR description currency | Matches diff; checklist matches gh pr checks modulo the coverage-report infra timeout noted above; AB#47815/AB#47239 linked. |
| AI slop | None — the new prose narrows an overclaim and adds a citation (decoder.rs's try_decode_buffered bail-out) rather than restating code. |
| Severity | Count |
|---|---|
| Blocking | 0 |
| Suggestion | 0 |
| Nit | 0 |
No new findings — my prior Suggestion is resolved.
Diagnosed and cleared — it was infra, but with a specific and reproducible cause worth recording rather than filing under general flakiness. The poller latched onto ADO build 172055 in 172055 was the build for So the handoff itself is fine; the poller just watched the wrong build. I re-ran the job and it found build 172056 and the artifact on attempt 1. All 19 checks are green on Confirming your read that it was unrelated to the diff, from two independent angles: Latent workflow bug, for someone to pick up separately. if [[ "$STATUS" == "completed" && "$RESULT" == "failed" ]]; thenA |
Resolves conflicts with #446 (row-wise SQLGetData optimization), which rewrote get_data.rs around a new buffered fast path. All three conflicts were import unions; the SQL_C_DEFAULT resolution already sits ahead of every dispatch, so #446's new direct-write branches receive the resolved target type unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Read the full diff, the head of Blocking: none. Suggestions: 1. Nits: 1. VerifiedThe width guard has no hole. This is the part that would be a real bug if it were wrong, so I enumerated it rather than spot-checking. Precision/scale from the ARD is moot here, correctly. The spec says that for Suggestion
if !is_valid_c_type(canonical_c_type(target_type)) {
post_diag(&mut stmt_state, ERR_INVALID_C_DATA_TYPE);
return SQL_ERROR;
}
The spec's own definition of that code:
// Else not a BOOKMARK column, check if the fCType is SQL_ARD_TYPE
if (fCType == SQL_ARD_TYPE)
{
// Need to get type from ARD
LPBINDINFO lpbindinfo;
if (lpstmt->pARD->lpplbindinfo == NULL || icol > CItemsPl(lpstmt->pARD->lpplbindinfo))
{ wError = IDS_S1_002; SETRC_SERR_GOTO(retcode, ErrorRet); }
lpbindinfo = (LPBINDINFO)GetPlPtr(lpstmt->pARD->lpplbindinfo, icol);
fCType = lpbindinfo->fCType;
...
}— Three reasons I think this belongs on this PR's radar rather than being noticed later:
Not blocking: it is pre-existing, out of the stated scope, and nothing regresses. But it is unregistered in all three doc locations this PR touches, and the deviations file is the natural home for it. NitThe That is the right answer — it is what msodbcsql returns too, since a NULL needs no buffer, so the narrow-buffer refusal correctly does not fire on a value that will not be written. Only the sentence over-reaches. Given the same doc already carves out the NULL exception for the PLP-binary case, adding it here would make the two read consistently. |
Shiwani Gupta (shiwanigupta0809)
left a comment
There was a problem hiding this comment.
Reviewed - see #481 (comment)
ttk (Theekshna)
left a comment
There was a problem hiding this comment.
Unattended run found no new findings. Covered: msodbcsql parity for the SQL_C_DEFAULT resolution path (independently verified Sql2CDefault/GetColData at sqlcdata.h:254-256 and the SQL_ARD_TYPE citation at sqlcdata.cpp:209), the element_stride/width-guard logic, test mutation-resistance (ODBC-version-gated test, width-refusal tests), divergence documentation currency, PR description/checklist vs. green CI, and AI-slop in the new comments/tests — none found. Two open items from Shiwani Gupta (@shiwanigupta0809)'s prior review (SQL_ARD_TYPE HY003 gap, NULL-exception doc wording) remain unanswered; not re-filing.
| Severity | Count |
|---|---|
| Blocking | 0 |
| Suggestion | 0 |
| Nit | 0 |
The resolve_default_target doc claimed both unresolved cases surface as HYC00. That holds for a value but not a NULL: write_captured_column answers NULL off the indicator before computing deliverable_target, so an unresolved placeholder over a NULL reports SQL_NULL_DATA/SQL_SUCCESS. That is the correct answer -- the refusals prevent a write, and a NULL writes nothing -- so document it and pin it with a test that uses the shape the width guard would otherwise refuse. Also register SQL_ARD_TYPE as a known gap. It is the spec's other placeholder TargetType for the same argument, absent from the crate, and answered HY003 -- the SQLSTATE defined as 'not a valid type, SQL_C_DEFAULT, or SQL_ARD_TYPE'. Measured, not inferred. Implementing it needs ARD-record lookup and the unbound-column case, so it wants its own work item rather than a re-spelled error. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Thanks Shiwani Gupta (@shiwanigupta0809) — both items were right, and I verified each against the code before acting rather than taking them on faith. Addressed in e768e68. Nit: the NULL carve-out — fixedConfirmed exactly as you describe. The sentence now carries the carve-out, and says why it is right rather than just noting it: the refusals exist to prevent a write, and a NULL writes nothing, so both "target this driver cannot deliver" and "buffer too narrow to hold it" are moot. That also makes it read consistently with the PLP-binary NULL exception a few lines down, which was your point. I also added Suggestion:
|
Saurabh Singh (saurabh500)
left a comment
There was a problem hiding this comment.
Unattended review sweep — comment-only, and not checked by a human before posting.
Verdict: Correct and well-tested. SQLGetData now resolves SQL_C_DEFAULT through the same odbc_sql_type → resolve_default_c_type pair as the bind/fetch paths, the two new guards hold under mutation, and the msodbcsql parity claims check out. No blocking issues — I only COMMENT, the merge is yours. This is my first pass on the PR; it has already been through Copilot, several unattended runs, and Shiwani Gupta (@shiwanigupta0809), and the current head resolves the last of those threads.
What I re-derived myself rather than trusting the prior threads:
- The streaming e2e tests genuinely hit the PLP path (answers Copilot''s suppressed "vacuous at a larger packet size" note). The pause is
stop_here && meta.is_plp()attoken_stream.rs:589, andColumnMetadata::is_plp()(query/metadata.rs:79) is a pureTypeInfoVariant::PartialLencheck with no size term — so aVARCHAR(MAX)of any size streams here, unlike the bound path. Your inline rebuttal to Copilot is right. - The two new guards are mutation-resistant. Hardcoding the pre-lock version read to
Odbc3_80reddensget_data_default_follows_the_declared_odbc_version(its 3.0 leg expectsSQL_ERRORbecauseSQL_SS_TIME2falls toSQL_C_BINARYbelow 3.8); disabling the width guard (if false && ...) reddens both..._too_narrow_for_its_fixed_target...and..._with_zero_buffer_length.... Built and rancargo nextest run -p mssqlodbc --lib get_data_default. - Parity holds for the default selection.
Sql2CDefault(sqlcprot.h:1823) is called from the sharedGetColDatapath (sqlcdata.h:256) as well as bind/fetch (sqlcfunc.cpp), so "one answer in every direction" is msodbcsql''s own posture; andSQL_ARD_TYPEresolution sits atsqlcdata.cpp:209exactly as the registered gap documents.
Blocking: none.
Suggestion: one, optional — inline at get_data.rs:971, on pinning the width-guard invariant with a test. Additive to the SQL_C_NUMERIC point Shiwani Gupta (@shiwanigupta0809) already raised and you acknowledged; the observation is settled, this is just about turning it into a regression check.
Nit: none.
Both SQL_C_DEFAULT resolvers ask element_stride for a resolved target's width by calling it with buffer_length 0, and its catch-all returns that 0 for anything absent from the match. A fixed-width C type missing there therefore reports width 0, the fixed_width > 0 guard skips it, and the resolver hands a fixed-width target to a possibly-smaller buffer. Nothing caught that: element_stride's own test checks a hand-picked subset. Verified by simulating a new mapping row emitting SQL_C_NUMERIC (fixed-width, absent from element_stride) -- only this test reddens, the other 1287 pass. Raised by Saurabh in review. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Only the deviations registry conflicted: #490 rewrote the SQL_C_DEFAULT binary bullet to add CLR UDT while this branch had extended the same bullet with the SQLGetData PLP/non-PLP probe distinction. Kept both. #489 (NUMERIC columns report SQL_NUMERIC) and #490 (UDT reports SQL_SS_UDT) both feed odbc_sql_type into this branch's resolver; they map to SQL_C_CHAR and SQL_C_BINARY respectively, so both are application-sized and the width guard is unaffected. The new invariant test covers that exhaustively rather than by inspection. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ttk (Theekshna)
left a comment
There was a problem hiding this comment.
Unattended re-review at 6a561a7. Head moved past my last marker only by a merge from main (six already-merged commits, e.g. #490/#489/#461/#485/#444) plus two genuine new commits responding to open review threads: �768e68d "Carve out the NULL exception and register the SQL_ARD_TYPE gap" and 54e0d85 "Pin the width guard's element_stride coupling with an invariant test." Both directly close items I'd noted as open-and-unanswered from @shiwanigupta0809 and @saurabh500 in my prior passes.
| Severity | Count |
|---|---|
| Blocking | 0 |
| Suggestion | 1 |
| Nit | 0 |
Verified independently, not taken on trust:
- �768e68d's SQL_ARD_TYPE citation — I grepped sqlcdata.cpp:209-210 in the reference tree myself; the if (fCType == SQL_ARD_TYPE) branch and the ARD-lookup comment are exactly where and what the commit describes.
- The �lement_stride catch-all is genuinely _ => buffer_length.max(0) as usize (etch_scroll.rs:1585) — a fixed-width C type absent from the match really does report width 0, so the new �very_default_c_type_has_a_width_or_is_app_sized test (iterating the full i16::MIN..=i16::MAX SQL-type space, not a hand-picked list) is closing a real, previously-untested hole rather than padding. SQL_C_SS_VECTOR's inclusion in APP_SIZED is correct — vectors are variable-length, same class as char/binary.
- I could not run either new test locally — this machine's toolchain fails to codegen crc32fast's AVX-512 intrinsics for any test binary (llvm.x86.pclmulqdq.256/512, simd_xor), a pre-existing environment limitation unrelated to this PR (confirmed in my review of PR #486 earlier this run). Relying on gh pr checks (green) and the commit's own mutation claim ("only this test reddens, the other 1287 pass").
Suggestion — PR description hasn't caught up with these two commits. The top-level description's "Behaviour" section and checklist (cargo btest passes... 1246 passed) predate both follow-up commits: it doesn't mention the NULL-over-unresolvable-placeholder carve-out (get_data.rs:915-921) or the registered-but-untracked SQL_ARD_TYPE gap, and the test count is now stale (at least two tests were added since). Neither is wrong, just missing from the summary a reviewer would read first — worth a short addendum before merge.
Other checks: parity — covered above (SQL_ARD_TYPE) plus everything already exhaustively audited in my prior two passes (18 measured base types, all matching). Divergence docs — both new gaps recorded consistently in .github/instructions/mssql-odbc.instructions.md and yped-columnar-fetch-plan.md. AI slop — the new doc comments and test docs explain non-obvious why (lock ordering, the mutation each test pins) rather than restating code; not flagging length.
Description
SQLBindColhas acceptedSQL_C_DEFAULTand resolved it per fetch from the IRD since AB#47481.SQLGetDatastill rejected it withHYC00, so the driver answered the same placeholder two different ways depending only on how an application read a column:SQLBindCol(col, SQL_C_DEFAULT, ...)+SQLFetch→ resolves from the column's SQL type and delivers.SQLGetData(col, SQL_C_DEFAULT, ...)→HYC00.msodbcsql has no such split —
Sql2CDefaultis consulted on theGetColDatapath that serves both — so this was an internal inconsistency rather than just a gap.SQLGetDatanow resolves the placeholder through the samedescribe_col::odbc_sql_type→type_rules::resolve_default_c_typepair the other two directions use. Resolution happens once, ahead of the captured / PLP / continuation dispatch, so every delivery path sees one concrete C type for a column. The declared ODBC version was not previously reachable inget_data.rs; it is read from the ENV before theSTMTlock to preserve parent-before-child lock ordering, followingbind_param.rs,catalog.rs, andfetch_scroll.rs. The read is gated on the placeholder, which costs nothing here becauseTargetTypeis a scalar argument rather than something that has to be read out of the bindings first.Behaviour
Unchanged for any caller that names a C type — the non-
SQL_C_DEFAULTbranch is a literal identity.The placeholder is kept (and the existing target gate reports
HYC00) in two cases:SQL_UNKNOWN_TYPEfor a TDS type this driver does not map.BufferLengthcannot hold it. ASQL_C_DEFAULTretrieval names nothing, so honouring the C type's width would put the 16 bytes auniqueidentifierresolves to into a 4-byte buffer.SQLFetchScrollrefuses the same shape.BufferLength0 is deliberately not exempt here, unlike in the bound resolver. On a binding, 0 is the documented idiom for a fixed-width slot and makes no width claim; onSQLGetDatait is also how an application asks for a length without wanting a value written — which is exactly how this file already reads it forSQL_C_BINARY. Exempting it would let the driver write up to 20 bytes into a buffer the caller declared as holding none, in a process the driver was loaded into. Refusing costs an application nothing it had before, since the same call answeredHYC00prior to this change, and theSQL_C_BINARYlength probe still works because an application-sized target has no fixed width to test.Resolved
SQL_C_BINARYis still not delivered (AB#47239), and how much of it answers depends on the path — worth stating precisely, because the two differ:varbinary(n)/binary(n): the zero-length length probe works; only a read with a real buffer isHYC00.varbinary(max)/image: even the probe isHYC00, becausestream_active_plp_chunkadmits onlySQL_C_CHAR/SQL_C_WCHARand rejects everything else before it looks atBufferLength. NULL is the exception — it never enters the streaming path.Both are pre-existing for an explicit
SQL_C_BINARYread, the same posture the bound path took in AB#47481; resolution only makes them reachable without the application naming the C type.fetch_scroll::element_stridebecomespub(crate)so bothSQL_C_DEFAULTresolvers test a resolved fixed-width target against the caller's declared slot through one definition of the C-type widths.Tests
Unit tests in
get_data.rs:int→SQL_C_SLONG).nvarchar→SQL_C_WCHAR, indicator in UTF-16 bytes).timecolumn resolves toSQL_C_SS_TIME2at 3.8 andSQL_C_BINARYbelow it. Verified that hardcoding the version read fails the 3.0 arm, so this cannot pass if the version stops being read.HYC00.BufferLength0 on a fixed-width target is refused; theSQL_C_BINARYprobe still answers.E2E tests in
tests/e2e/tests/get_data_test.cpp, all run locally against SQL Server 2025 in Docker (64 passed, alongsidefetch_scrollandfetch_error_semanticsfor regressions):DefaultTargetResolvesFromTheColumnType—intand narrowvarchar, the two types both drivers resolve identically, so it runs on the msodbcsql leg and compares.DefaultTargetStreamsAVarcharMaxAcrossChunks— the PLP continuation path, also comparable.DefaultTargetResolvesWideAndGuidToTypedTargets,DefaultTargetTooNarrowForItsFixedTargetIsRefused,DefaultTargetOnABinaryColumnIsStillUnimplemented,DefaultTargetOnABinaryMaxColumnRefusesEvenTheProbe,DefaultTargetStreamsAnNvarcharMaxAsWideChunks— assert registered deviations, so they carrySKIP_IF_COMPARING_MSODBCSQL().Each deviation was measured against a real msodbcsql18 while writing the tests rather than assumed — that is how the wide column's three-narrow-bytes/indicator-3 result and the narrow-buffer GUID's
22003(after writing insideBufferLengthand consuming the column) were established. Those local measurements were against 18.6.1.1; CI pins 18.6.2.1, so the compare leg is the authority, and it has run green on this head.DefaultTargetOnABinaryMaxColumnRefusesEvenTheProbeasserts that the defaulted spelling gives the same answer as namingSQL_C_BINARYexplicitly, plus one assertion that today's shared answer isHYC00. The agreement is what this change owns; theHYC00belongs to AB#47239, so when that lands only the one literal needs revisiting. It uses a separate result set per spelling because a PLP column whose stream was begun and then refused cannot be re-read on the same row.Docs updated:
mssql-odbc/docs/typed-columnar-fetch-plan.md(the note that flagged this gap as untracked now points at the work item) and the registered deviations in.github/instructions/mssql-odbc.instructions.md.Related Issues
https://sqlclientdrivers.visualstudio.com/mssql-rs/_workitems/edit/47815
Checklist
cargo bfmtpassescargo bclippypassescargo btestpasses (mssqlodbc: 1246 passed; themssql-tdsintegration-test failures are pre-existing on this machine, which has no.envfor a live server — confirmed by stashing the change)