The row standing for a name is picked before the filters, not after #183
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found reviewing #180, which made it easy to reach. The shape predates that
change —
restrictedsits in the same place and has the same consequence —so this is not a regression.
What happens
crates/ephyra-db/src/query/names.rspicks the row that stands for a namebefore narrowing, and narrows afterwards:
representative_ofreads the whole group, unfiltered. Upstream does it theother way round —
BaseItemRepository.ByName.cs:196-224runsTranslateQuery(innerQuery, context, outerQueryFilter)first and only thengroups by
PresentationUniqueKeyand takesMin(Id), so the representative ischosen from the rows that survived the filter.
Where it shows
Two
MusicArtistrows sharing a presentation key — the row a music libraryfound and the row written beside the metadata, which is the case
names.rs:107-110documents and the reasonrepresentative_ofhas aMusicArtistbranch at all./Artists?isFavorite=false: upstreamstill lists the name, represented by the other row. We drop it.
/Artists?isFavorite=true:upstream lists it. We answer nothing.
The same holds for an account the rating limit or a blocked tag hides the
representative from: upstream offers the name under the row it may see, we
offer nothing. That half is older and harder to reach, because a filter that
hides one row of a group usually hides the other too — a mark lands on exactly
one row, which is what makes this easy.
Where to start
The conditions
restrictedandmarked_favoritebuild are written againstBaseItems., so choosing the representative among the rows that pass themneeds the same conditions over the group's alias — either inside the
correlated subquery, or as a
NOT EXISTSover a better-ranked row that alsopasses. Either way the account's filters have to be generated for a named
table rather than for
BaseItemsalone, which is the whole of the work.How to know it is done
A fixture with two rows of one presentation key — one reachable, one not, and
one marked — answers the name under the row that survived, on
/Artistsandfor an account with something hidden from it.
crates/ephyra-http/tests/by_name.rshas no two rows sharing a key today, so the case has to be built.
House rules
Tests run under
cargo nextest; upstream's source is the checkout inupstream/at the pin. Generated artifacts are regenerated, never edited.Anything touching the query builder or authorization is reviewed.
Fixed in
e538a53, with the review's nits infe81cdf.The narrowing moves into a
Keptterm and the group is taken over that, so theaccount's own conditions stay written against
BaseItemswhere they were andthe row that stands for a name is chosen from what survived — which is
upstream's order in
GetItemValues.crates/ephyra-http/tests/by_name.rsgrows the case: a secondMusicArtistrow sharing the first's presentation key and folded name, with an id that wins
the pick, and the mark on the row that loses it. It answers the name; without
the change it answers nothing.
The review executed the emitted SQL rather than reading it. Binding order holds
in the maximal shape — a restricted account with a rating limit, blocked tags,
an allowlist, a blocked unrated kind and a blocked library, an
ancestor_id,two
top_parent_ids,isFavorite=trueand a params-carrying sort: 43placeholders, 43 parameters, each in its slot. The answer set can only grow and
only where intended:
Keptis a subset of what the old query considered andboth representative rules are stable under a subset containing the old winner,
so a plain listing returns the same names and the same representative ids —
measured over 300 names, 30 of them two-row groups.
Two things worth recording. It also restores upstream's answer for a group
whose lowest-id row carries a
CleanNameoutside the value set, which is thesame bug in another dress. And it costs 3–8% on the listing, because the
account's predicates are re-evaluated inside the correlated group — measured
over 4,000 films and 2,000 tracks.