fix(sql): row-cap key-value and where-pair plans with a TOP/LIMIT N+1 dialect seam (R06:S-1)
Key-value and where-pair plans had no row limit, so a selector pointed at a history or events table materialised every matching row on every poll. ISqlDialect gains RowLimitPrefix/RowLimitSuffix(n) (SQL Server TOP (n), Postgres LIMIT n, Oracle FETCH FIRST n ROWS ONLY); plans carry a RowCap (distinct keys, or 1) and ask for RowCap+1 rows; the reader also stops client-side at RowCap+1. Getting the extra row proves the selector ambiguous: the group publishes BadConfigurationError with a rate-limited warning. The reader reports those answered refusals so the driver counts them as answer evidence, and any refusal-only poll now restores Healthy after a comms fault. Refs #744 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -166,6 +166,17 @@ emits a `SqlQueryPlan` per distinct group key. Grouping is deterministic (groups
|
||||
order of their first member, members keep their input order, parameters keep first-appearance order),
|
||||
so an unchanged configuration produces byte-identical SQL.
|
||||
|
||||
**Every key-value and where-pair query is row-capped (R06:S-1).** A key-value plan asks for one row more
|
||||
than it has distinct keys (`TOP (n+1)` / `LIMIT n+1` / `FETCH FIRST n+1 ROWS ONLY`), a where-pair wide-row plan
|
||||
for two, and the reader stops reading there even if a provider ignores the limit. Receiving the extra row
|
||||
proves the selector is ambiguous — a history/events table, or a where column that is not unique — so every
|
||||
member of that group publishes `BadConfigurationError` with a rate-limited warning naming the table and
|
||||
selector, instead of the old behaviour of materialising every matching row on every poll and publishing an
|
||||
arbitrary "last row" at Good. A duplicate that fits inside the cap (a duplicated key alongside an absent one)
|
||||
still resolves last-row-wins. `topByTimestamp` keeps its single-row limit. An over-cap answer, like every
|
||||
other refusal (absent row, NULL cell, uncoercible cell), is the database answering: it advances
|
||||
`LastSuccessfulRead` and restores `Healthy` after a comms fault.
|
||||
|
||||
⚠️ **Group keys are case-sensitive on identifiers.** Two tags authored `dbo.T` and `DBO.T` plan as
|
||||
**two** groups — one extra round-trip, not a correctness problem, but worth knowing if a group count
|
||||
looks higher than expected.
|
||||
|
||||
Reference in New Issue
Block a user