SDSMappedCursor behaves like the direct fetch cursors synthesized for
SDS types, but performs a compactMap-like transform on each element.
Like SDS cursors, it can throw, and callers are required to handle
that; this uncovered some places where we can do better recovery if an
error ever were to happen.
Also, sort them in the database query, so they don't need to be sorted
afterwards. (This is important for existing code that works in
batches.)
We do lose an optimization where the unread messages are counted in a
read query before marked read in a write query. If that's important I
can contrive a way to put it back.