docs(driver.sql): topByTimestamp is only defined when the ordering column is unique (Driver.Sql-008)

This commit is contained in:
Joseph Doherty
2026-08-10 08:06:27 -04:00
parent 336e79ea9a
commit 9d4760fd2f
2 changed files with 20 additions and 2 deletions
+8
View File
@@ -80,6 +80,14 @@ the nested form.
} }
``` ```
⚠️ **`topByTimestamp` needs an ordering column that is unique per row.** It is executed as an
`ORDER BY <column> DESC` with the dialect's single-row limit, so when the column ties the server
returns an arbitrary row among the tied ones — and the driver cannot warn about it, because the other
rows were discarded before the result set reached it (the `whereColumn`/`whereValue` form *does*
warn, on `Rows.Count > 1`). A `date`-typed column ties for every row written on the same day, and
browse-commit will nominate one. Prefer a `datetime2`/sequence column that is unique per row, or
point the tag at a latest-row view.
`whereValue` may be authored as a JSON string, number or boolean (it is bound as a parameter either `whereValue` may be authored as a JSON string, number or boolean (it is bound as a parameter either
way). `keyValue` on the `KeyValue` model must be a **string**. way). `keyValue` on the `KeyValue` model must be a **string**.
@@ -566,9 +566,19 @@ public sealed class SqlPollReader
/// one. Worse, it would destroy the only evidence this method has that the selector is ambiguous /// one. Worse, it would destroy the only evidence this method has that the selector is ambiguous
/// (<c>Rows.Count &gt; 1</c>), converting a loud misconfiguration into a permanently silent one — /// (<c>Rows.Count &gt; 1</c>), converting a loud misconfiguration into a permanently silent one —
/// precisely the failure mode this class's summary calls its top risk. The <c>topByTimestamp</c> /// precisely the failure mode this class's summary calls its top risk. The <c>topByTimestamp</c>
/// form keeps its row limit because there the <c>ORDER BY</c> makes "the first row" a defined /// form keeps its row limit because there the <c>ORDER BY</c> at least fixes <em>which</em> rows can
/// answer. The accepted cost is that an ambiguous selector materialises every matching row for one /// be first. The accepted cost is that an ambiguous selector materialises every matching row for one
/// poll; the warning is what gets it fixed.</para> /// poll; the warning is what gets it fixed.</para>
/// <para><b>⚠ The <c>topByTimestamp</c> answer is only defined when the ordering column is unique</b>
/// (Driver.Sql-008). <c>ORDER BY ts DESC</c> plus a one-row limit over a column with ties returns an
/// arbitrary row among the tied ones, and — unlike the where-pair form — this method can never
/// detect it, because the server discarded the other rows before the result set was built: there is
/// no <c>Rows.Count &gt; 1</c> left to warn about. The tie shape is reachable rather than
/// theoretical, since <c>SqlBrowseSession.PreferredTimestampColumn</c> will nominate a
/// <c>date</c>-typed column, which ties for every row written on the same day. The model's premise is
/// unchanged — the selector must identify one row — but for this form the operator, not the driver,
/// is the only thing that can check it. Point the tag at a column that is unique per row (or at a
/// latest-row view).</para>
/// </summary> /// </summary>
private object?[]? SelectWideRow(SqlQueryPlan plan, GroupResult outcome) private object?[]? SelectWideRow(SqlQueryPlan plan, GroupResult outcome)
{ {