A field declared string, given a value containing the byte 0xFF, through a
real daemon:
sent 61 ff 62 ("a", 0xFF, "b")
stored 61 ef bf bd 62 ("a", U+FFFD, "b")
answer changed 1
Accepted. Not refused, not warned about. The byte is replaced with the Unicode replacement character
on the way in, and the original is unrecoverable. A string is UTF-8 text and the wire
encoding enforces that; it is not a byte container and never was.
One thing this measurement does not say. A first attempt sent a NUL byte and saw the value truncated, which looked like a second bug. It was the test harness: a process argument cannot carry a NUL, so the value was cut at the exec boundary and never reached the database. Reported here because the wrong version of this ticket would have carried it.
The obvious workaround is base64 in a string, and it works. But that makes base64
mandatory rather than conventional, and three things follow that a caller is not
told:
The question that prompted this ticket was whether sapedb has an equivalent of Postgres
BYTEA. It does not, and the honest form of that answer is this ticket rather than a
paragraph telling people to encode it themselves.
bytes, so the store decodes it once and stores bytes. The
difference from today is that the encoding is the protocol's job rather than the caller's, and a
value that is not valid base64 is refused by name instead of stored as text.0x00 and terminates with
0x01; bytes need the same treatment or a length prefix, and which one is a decision
because it changes what sorts before what.protocol.MaxPayload is 16 MiB, enforced when a
frame is written and again when one is read, and its comment gives the reason — a length
field is a promise about an allocation, and this is the limit on how much a peer can make the
server believe. So the gap is narrower than this ticket first claimed: what is
missing is a ceiling on a single value or document, and a ceiling
on what one database may hold in total. A 16 MiB frame already bounds one message; it bounds
nothing about ten thousand of them. The driver may check early as a convenience, but the server is
where it has to be enforced, because a check only the client performs is not a limit. The earlier
version of this item was wrong to say that no cap existed: the search that went looking for one
never included the constant's own name, MaxPayload, and a search that found nothing
was read as a thing that is not there.Uint8Array is the
obvious emit, and the obvious emit should still be written down.61 ff 62 go in and come out identical, through a
real daemon, read back from the file. That exact value, because it is the one measured to corrupt
today.bytes field is refused by name,
not stored.bytes field sorts bytewise: 00 before 7f
before ff, which base64 text does not.string field still refuses nothing it accepts today. This ticket adds a type; it
does not tighten the old one — and if the group decides string should reject
invalid UTF-8, that is a separate decision with a deadline, because tightening cannot happen after
the tag.string does with invalid UTF-8. Named above as its own decision.