3.4 Case Folding
Key names and value names are case-insensitive but case-preserving. loregd
implements this by storing both forms: the name as written, and a folded
form alongside it in a _folded column.
The folded form is computed once, when the name is written, and it is the
folded column that appears in every WHERE clause and every primary key.
Lookups are therefore plain binary comparisons:
SELECT * FROM path_entries
WHERE parent_guid = ? AND child_name_folded = ?
This is why no custom SQLite collation is registered anywhere in loregd — the case-insensitivity has already happened by the time SQLite sees the query. The canonical name is what comes back in responses, so callers see the case they originally supplied while storage compares the folded form.
Hive names are folded too, though not stored: routing a request to a hive and rejecting duplicate hive declarations (§2.1) both compare folded names.
Layer names are not folded. They are compared as binary, so layer names are case-sensitive.
3.4.1 How the folded form is computed #
loregd derives the folded form from the Go standard library's
unicode.ToLower, with three corrections where lowercasing and simple
case folding disagree:
| Codepoint | Folds to | Why the correction |
|---|---|---|
| U+00B5 MICRO SIGN | U+03BC GREEK SMALL LETTER MU | Lowercasing leaves it unchanged. |
| U+017F LATIN SMALL LETTER LONG S | U+0073 LATIN SMALL LETTER S | Lowercasing leaves it unchanged. |
| U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE | unchanged | Its folding is a two-codepoint sequence, which simple folding does not produce. |
The standard library's case tables are Unicode 15.0.0.