microsoft / microsoft/mssql-rs
Cleanups surfaced by row decode perf work (PLP validation, cfg-gated duplication, sentinels)
- Dominant language
- Rust
- Stars
- 53
- Forks
- 14
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 137
Description
Sub-issue of #247. These surfaced while prototyping the performance work but are **independent of it** — none require the perf changes to land first, and each is small. Grouped into one issue because individually they are too small to track separately.
### Problem statement
Six pieces of duplication and near-duplication in the row decode path. Several of them actively increased the cost or risk of the perf spikes in #247.
### Proposed solution
- [ ] **Extract a shared `PlpChunkValidator`.**
`ensure_active_chunk` (`mssql-tds/src/datatypes/decoder.rs:242`) performs **6** validation checks: chunk count, chunk size, overflow, `MAX_PLP_SIZE`, exceeds-declared-length, and ended-before-declared-length.
PR #238's `validate_plp_chunk` reimplements only **2 of those 6** and re-declares the constants locally. The #253 spike's `stream_plp_into` now duplicates all 6 a **third** time.
A sync `PlpChunkValidator { declared, total_read, chunks_seen }` with `accept_chunk(&mut self, len: usize) -> TdsResult` would serve the async path, the streaming path, and any future sans-I/O path from one implementation.
Checks "exceeds declared" and "ended before declared" are **load-bearing** given that `LenHint::Exact` is trusted downstream by #253.
Constants for reference: `MAX_PLP_SIZE` = 64 KiB fuzzing / `i32::MAX`; `MAX_PLP_CHUNKS` = 1000 / 100_000; `MAX_PLP_CHUNK_SIZE` = 8 KiB / 16 MiB.
- [ ] **Intern strings as `Rc` / `Arc`.**
`mssql-js/src/binary_row_writer.rs:82` does `string_map.insert(s.clone(), idx)` — a full `String` clone used only as a dedup-map key. This is the **last remaining string allocation** after #253 lands.
- [ ] **Avoid `columns.to_vec()` per paused row.**
`read_row_header` in `token_stream.rs` clones the entire `Vec`, including every `column_name: String`, on each pause. Substantially more expensive than the 6-byte bitmap #255 removes — though it is on the cursor/pause path rather than the bulk-scan path, so it does not show up in the #238 benchmark at all.
- [ ] **Collapse the cfg-gated trait/struct duplication.**
`packet_reader.rs` defines `TdsPacketReader` **twice** with identical bodies (`pub(crate)` vs `pub`, cfg-gated). Same pattern for `ColumnPolicy`, `RowReadResult`, and `RowPauseState` in `token_stream.rs`.
This **doubled the edit cost of both #252 and #254** and is a standing correctness hazard — the two copies can silently drift. Open question: whether `macro_rules!` can collapse them without breaking the fuzz targets.
- [ ] **Replace raw `0xFFFF` literals with `LENGTH_NULL`.**
`packet_reader.rs:9` already defines `pub const LENGTH_NULL: u16 = 0xffff`, yet `decoder.rs` contained **22** raw literals. The #251/#253 spikes converted two; the rest remain.
- [ ] **Consolidate PLP sentinels**, currently declared in three places: `decoder.rs:490`, `sqltypes.rs:146`, `tds_value_serializer.rs:26`.
### Affected crate
mssql-tds
### Alternatives considered
Doing these opportunistically inside the perf PRs. Rejected — it would inflate diffs on changes that are already wide, and make the perf review harder. They should land separately, ideally *before* the perf work where they reduce its cost (the trait duplication in particular).
### Additional context
Also worth noting for anyone in this area: `SqlTypeDecode::decode` and `decode_into` are two complete ~40-arm type switches that must be kept in agreement by hand. #254 risks adding a **third**. That is called out in #254 rather than here because the resolution is a design decision for that issue, not a mechanical cleanup.
Contributor guide
Research direction
Choose one checklist item first, then read the named entry points in mssql-tds/src/datatypes/decoder.rs, packet_reader.rs, token_stream.rs, or the mssql-js binary row writer. Compare the existing duplicated implementations and the referenced PLP constants or validation paths; done means the selected duplication is consolidated without changing behavior, while preserving fuzz-target compatibility where relevant.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, rust
- Domain
- backend, databases
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100