docs(postgres): a representative row must not be picked by Min(name) (#59135)

This commit is contained in:
Mihir Kandoi
2026-09-17 14:10:46 +05:30
committed by GitHub
parent f3c2a836eb
commit 4f312fcba4
2 changed files with 9 additions and 2 deletions

View File

@@ -180,6 +180,13 @@ audit of these fixes found four recurring mistakes:
the arbitrary-pick preservation the wrap is usually justified as. Confirmed on CI; see #56241.
Note a local macOS PostgreSQL gives a **false all-clear** — its collation happens to agree with
MariaDB on case. Fix: take a representative row rather than sorting text.
**Picking that row is the hard part.** `Min(name)` is still a text sort: `autoname="hash"` is
not reliably lower case, because `_get_timestamp_prefix()` prepends `get_trace_id()[-1:]`
un-lowered and a client-supplied `X-Frappe-Request-Id` can put an upper case `A-F` there. A
non-text key (`Min(idx)`) works only where it is **unique within the group** and the join-back
carries the **full group key** — a date is usually neither, and joining on a duplicated value
turns one group into several rows (§3). Otherwise select the row in Python, sorting with
`key=str.casefold` so the order matches MariaDB's collation without depending on the database's.
- **Wrong bound** — where the value has a semantic, pick the bound deliberately:
`Min(schedule_date)` for a "required by", `Min(idx)` for first-line ordering, a qty-weighted
average for a rate. A blind `Max` can understate urgency or overstate a figure.

View File

@@ -2415,8 +2415,8 @@ class QueryPaymentLedger:
.groupby(ple.account, ple.voucher_type, ple.voucher_no, ple.party_type, ple.party)
).as_("grouped")
# Payment Ledger Entry has no autoname rule, so frappe names it by hash -- lower-case, which
# keeps Min(name) free of the collation divergence that picking Max() over free text has.
# KNOWN DIVERGENCE: Min(name) is a text sort. Hash names are not reliably lower case -- the
# trace-id prefix is not lowered -- so the engines can pick different rows here.
representative_ple = qb.DocType("Payment Ledger Entry").as_("representative_ple")
query_voucher_amount = (
qb.from_(grouped_voucher_amount)