WL#5272: Ordinal collation

Affects: Server-Prototype Only   —   Status: Un-Assigned   —   Priority: Very High

Make a collation where every character has a unique
primary weight, with variations of the character
(caused by accents or case differences) appearing

This task description exists because of discussion re
WL#923 Store regular identifiers in upper case.
It will probably be rejected if we don't find a
use for it with identifiers.

The collation has:
1. Case sensitivity. For example E <> e.
2. "Accent sensitivity". For example E <> É.
3. Accented characters following base characters. For example E < É.
4. Natural-looking order. For example E will be between
   D and F as in most western alphabets, and katakana PE
   will be near hiragana PE as in Japanese.
5. Unique weights. For example if the weight of 'E'
   is 555, then no other character has a weight of 555.
6. The same weight for the same character, regardless
   of encoding. So _ucs2 ='a' = _latin1 'a'.
7. The NO PAD characteristic.
   For example 'A ' <> 'A'.
8. One weight for one character.
   No attention to ignorables or expansions or combining
   characters or repeat marks.
9. Simple uppercasing.
   For example Upper(sharp s) = S, not SS.

The rules are theoretically applicable to any
character set, but our only concern is UTF8.


Some possible names are:

The prefix UTF8_ is okay and conventional but unnecessary.
The collation doesn't really depend on UTF8, although some
people may think it's convenient to define thus:
In this case we don't need to specify character set
because the collation name tells us that the character
set must be UTF8.

The name UTF8_GENERAL_CS has been proposed before,
and might be what some people expect now.
However, this collation is not a case-sensitive
variant of UTF8_GENERAL_CI, so such a name is confusing.

The suffix _AS (Accent Sensitive) is what you might
see in Microsoft SQL Server.

The name ORDINAL should remind people of Microsoft's
"ordinal" collations.
Although we're not exactly like that because Microsoft
doesn't exactly follow DUCET, we're pretty close.
(Other lovely Microsoft terms are "invariant" and "lexicographic".)

The name SQL_IDENTIFIER will help people see that
the primary use of this collation is for identifiers
of SQL objects. However, in standard SQL the
character set, a restricted subset of Unicode, which
we don't support.

The name SOMETHING_ELSE is a placeholder. That means:
if you have a better idea, speak up now.

Weight Table

The weight table is a list of weights in
Unicode-character-code order, such that
given a character, one can return a weight.

There is no table for going the other way
(from weight to character), although such
a thing is possible given the one-to-one
relationship between characters and weights.

The weight values will be the line numbers in
a UCA DUCET table, such as 'allkeys.txt'
At time of writing the 'latest' allkeys.txt version
is 5.2.0. The implementor may choose a different version.

There are about 40,000 lines in allkeys.txt.
Although that means the table can have 16-bit weights,
the real number of Unicode characters is far greater
(we are going to calculate rather than look up for
characters outside allkeys.txt), and there will be
new characters in future Unicode versions.
So allow for 24-bit or 32-bit weights.
(In the "identifier" application described in WL#923,
we can disallow exotic characters so this might not
be a problem.)

Before producing weights, the implementor may re-order
allkeys.txt to make all Variable Weighting characters sort
after all ignorable characters (if we decide we do want ignorables).
(Such weights are marked with '*' in allkeys.txt.)
For example, the original allkeys.txt (version 5.0.0) contains:
    03F6  ; [*04B3.0020.0002.03F6] # GREEK REVERSED LUNATE EPSILON SYMBOL
    0482  ; [*03C0.0020.0002.0482] # CYRILLIC THOUSANDS SIGN
    0488  ; [.0000.0000.0000.0488] # COMBINING CYRILLIC HUNDRED THOUSANDS SIGN
    0489  ; [.0000.0000.0000.0489] # COMBINING CYRILLIC MILLIONS SIGN
    055A  ; [*032E.0020.0002.055A] # ARMENIAN APOSTROPHE
Alexander Barkov prefers this order:
    0488  ; [.0000.0000.0000.0488] # COMBINING CYRILLIC HUNDRED THOUSANDS SIGN
    0489  ; [.0000.0000.0000.0489] # COMBINING CYRILLIC MILLIONS SIGN
    055A  ; [*032E.0020.0002.055A] # ARMENIAN APOSTROPHE
    0482  ; [*03C0.0020.0002.0482] # CYRILLIC THOUSANDS SIGN
    03F6  ; [*04B3.0020.0002.03F6] # GREEK REVERSED LUNATE EPSILON SYMBOL
This command seems to do the right job:
cat allkeys.txt |tr "*" "." |sort --key=3

To produce a weight in the table, given character X:
* If X exists in allkeys.txt,
  then use the line number within allkeys.txt.
* If X does not exist in allkeys.txt,
  then use the code point value + a base number which
  is greater than the number of lines in allkeys.txt.
  This is inspired by, but not the same as, the
  Unicode recommendation for implicit weights
  as described in Unicode Technical Report 10:
  "Any code points that are not explicitly mentioned in this
  table [i.e. allkeys] are given a derived collation element,
  as described in  Section 7, Weight Derivation."

For example, just looking at allkeys.txt with an editor, one sees that
LATIN SMALL LETTER U is line 8023 decimal
So the weights are 8023, 8024, 8041, 8068.

Good Things and Bad Things

GOOD THING. To an ordinary user, '<' comparisons and
'>' comparisons and ORDER BY results should
look slightly less bizarre than if collation was binary.
Da will still be greater than DB, but in very short lists
Da will be 'near' DB. Actually this is the only serious
reason that we have for making this new collation, so if
users don't really think the results are natural, this
whole task is dubious.

GOOD THING: The utf8 ordinal collation is compatible
with utf8_bin in this sense:
the effect of '=' and '<>' comparisons is guaranteed to
be the same, for all characters and character combinations.
(The effect of '>' and '<' comparisons will not be the same.)
So if string1 = string2 with utf8 ordinal collation, then
string1 = string2 with utf8_bin collation, and vice versa.
This is a mandatory requirement so that internal searches
with utf8_bin can continue to be carried out with binary
The optimizer might be able to use this fact and
change ordinal comparisons to binary comparisons in some cases.

GOOD THING: The new collation might help us pacify the
people who demand an "accent sensitive" collation.
Peter Gulutzan thinks that those people simply don't
understand all the implications, so this is not a
practical usage.

GOOD THING: Comparisons with the new collation should in
theory be faster than comparisons with a multiple-weight
collation. And besides, we don't have a multiple-weight
collation yet because we haven't done WL#896.

BAD THING: Combining-character sequences like A and CIRCUMFLEX
must be treated as two separate characters, not as
A CIRCUMFLEX. Otherwise we'd lose the utf8_bin compatibility.

BAD THING: Although the collation is "ordinal", the comparison
method is not fully compatible with Microsoft's
Ordinal and OrdinalIgnoreCase string comparison methods.

Radical Tailoring

In early discussions about this feature, there wass
another suggestion. Rather than taking the line number
of the DUCET table entry, take the primary weight, and
add the secondary + tertiary weights to it, with some
shifting to ensure uniqueness. We call this "radical

There were some flaws with the original proposal,
and we found it easier to come up with numbers by
using the line numbers. 

However, if we used radical tailoring instead, for example
by taking the primary weight and adding the secondary weight
to it, we'd be able to use tables made for WL#896 "Primary,
Secondary and Tertiary Sorts" to simulate the ordinal collation's
weight table. Possibly we gave up on radical tailoring too soon.


dev-private email thread "WL#923, Identifiers, Collation"