WL#896: Primary, Secondary and Tertiary Sorts

Affects: Server-6.x   —   Status: Assigned

The Unicode Collation Algorithm has guidelines for sorting  
characters. An introductory article is:  
Our authoritative text is:  
"Unicode Technical Standard #10 Unicode Collation Algorithm"  
which we'll call "The UCA document".  
Trying to summarize in one sentence:  
For sort keys and indexes, compare the weights of the
characters, so that collation will be possible (approximately)
as the UCA document requires, with up to four levels.
Multi-level comparisons work thus:
  Compare the level-1 weights.
  If the result is equal, compare the level-2 weights.
  If the result is still equal, compare the level-3 weights.
  And so on, for the number of weights in the collation.
Variation is possible, for example comparing
the level-3 weights before the level-2 weights.
Alexander Barkov (Bar) and Sergei Golubchik believed this task
(WL#896) could be in 5.0, which was the original plan. Clearly
it will be later.  
This has some possible interest for a Japanese customer  
[ name deleted, see Progress Reports ] which suggested a patch
for MySQL 4.1, but (as Sergei points out) they can use their own
patch with 4.1 while we work toward WL#896.  

Sergei, Bar, and I (Peter Gulutzan) agree that the Japanese-customer
patch (also known as utf8_general_cs)  
is good for their particular purpose but not for our usual  
customers. They do two passes: sort according to the MySQL  
official collation (which I suppose is sjis_japanese_ci),  
then for all values which are "equal" in that collation,  
compare using memcmp. This means that a level-3 difference  
trumps a level-2 difference, e.g. 'AĆ©' < 'ae'. The  
Japanese-customer solution is good -- these are intelligent
people and the solution works -- but we merely say that our
plan must fit other, non-Japanese, situations as well.

WL#896 is a prerequisite for new Hungarian collations
(WL#2993) and standard Japanese (WL#2555).

The "allkeys.txt" file discussed in the HLS is here:

See also:
BUG#34130 incorrect french order in utf8_unicode_ci

The mandatory new collations will be utf8_unicode_600_w1234,
ucs2_unicode_600_w1234, utf8mb4_unicode_600_w1234,
utf16_unicode_600_w1234, utf32_unicode_600_w1234.
See WL#5844 for the naming convention details.
They will follow exactly the allkeys.txt guideline for level-1,
including "lower case first" (see below).
The implementor can decide what version of allkeys.txt to use,
that is, '600' is merely an assumption about Unicode 6.0.0.
If WL#5704 happens first, the only new collation is
unicode_600_w1234 (no need for character set names).

LOWER CASE FIRST: in allkeys.txt, lower case precedes      
upper case. We'll follow allkeys.txt. The behaviour
will not be optional, that is, we will not allow for
the "caseFirst" attribute mentioned in the UCA document.

It is possible to define any column with a _w123      
collation. However, for conditional expressions and      
distinct and grouping, there is no      
difference between ucs2_unicode_w123 and ucs2_unicode_ci.      
You only see a difference with ORDER BY expression,      
where expression -- either because a column is defined      
with a _w123 collation or because there is a COLLATE      
clause -- has a _w123 collation.      
We do not expect to see two-level collations, and
we do not expect to see four-level collations outside Japanese.     
We do expect to continue with some one-level collations.
(One-level collations are the norm now, and   
utf8_unicode_w1 is similar to the old utf8_unicode_ci.    

A _w123 collation is always compatible with a _ci      
collation, because we will always use the _ci collation.      
For example, one can concatenate a _w3 value      
with a _ci value, the result will have a _ci collation.      
Bar will try compression tricks to keep the sort key      
length reasonable, but we realize      
that lots of memory is necessary. One suggestion is:      
make an initial assumption "multiplier = 6", try to      
make keys, if overflow occurs say "multiplier++" and      
try again or else flag the keys that will need special      
handling after the sort pass. Details are not part of      
this description but should be in low-level architecture      
or in a separate worklog task. Another suggestion is:     
since the later levels are usually only one or two     
bits, and the first level is less than 16 bits, we     
could try to store all levels in one 16-bit word,     
and do multiple passes with appropriate ANDs / ORs.     
PAD SPACE: The new collations are all "PAD SPACE".  
In _w123 collations the level-2 and level-3      
weights are applicable only for ORDER BY.      
Variant collations which have the suffix _w123a      
("weight 123 always" or "weight 123 strong") are applicable for all      
operations, including UNIQUE, > and = and <      
comparisons (but not LIKE), GROUP BY, ORDER BY.      
The UCA document allows the option, saying:      
" For comparison, most of the time a three-level strength      
will need to be used. In some cases, a larger number of      
levels will be needed, while in others - especially in      
searching - fewer levels will be desired."      
"Users of search algorithms should be allowed to modify      
the comparison strength, thus excluding differences at      
less significant levels. This is especially useful for      
searching, but can also apply to comparison."      
Bar points out that latin2_czech_ci already works      
like a _w123a collation, and the code that he already      
has done is for a _w123a collation, and this would be      
compatible with the Japanese-customer patch.      
The standard Japanese collation (WL#2555) is _w1234a.        
The WEIGHT_STRING function will return a binary string    
of weights. Comparable functions are Oracle's NLSSORT    
and Microsoft's tertiary_weights.    
This is now a separate completed task, WL#3716 WEIGHT_STRING function.
See also WL#3804 WEIGHT_STRING deferred features.
By default, there will be no compression, we will not    
implement any of the suggestion in the UCA description    
section 6.1 "Reducing sort key lengths".    
Peter would be happy if we did WL#896 with no indexes,      
but apparently Bar has already done something, and      
Peter supposes that the Japanese customer won't be happy unless we      
have a solution with indexing.      
[ Note added 2011-03-04 ]
Peter asked six questions, of varying quality.
For one question, the answer was to refer to WL#826.
For four questions, answers by Serg and/or Bar were wrong
and are hereby superseded. One question was silly and is removed.

Prefix indexes, which Peter wrongly called partial indexes, are disallowed
for multi-level collations. For example, the following statements
(which involve an implicit prefix index because InnoDB has a
767-byte limit until WL#5743 "InnoDB: Lift the limit of index
key prefixes" is done) will cause an error:
CREATE TABLE t1 (a VARCHAR(1000) CHARACTER SET utf8 COLLATE utf8_unicode_600_w1234);
CREATE INDEX i ON t1 (a));
Result: ERROR xxxx (42000): prefix index for multi-level collation.
Bar believes that we may be able to do more with
prefix indexes after doing
WL#5615 Prefix key changes for tricky collations.
The index can be used as a covering index, because
the value which will be stored is the original string,
not the weight of the string. Peter objected that
storing values instead of weights makes searches
substantially slower (perhaps 30% - 40% if one
guesses after looking at latin2_czech_cs examples).
But this is an implementor decision, and Bar
decided we'll store values.

Expressions like "LIKE 'A_'" will not use the index for
columns that have a multi-level collation.
There are some problems that would have to be solved with
the optimizer first. An example of the problems is:
Assume a w1234 index.
It's ordered thus:
I'm searching with LIKE 'a_'.
Assume that we apply a method like the one
that's been used in the past for non-w1234 indexes:
start with the first key that is >= 'a'
(so it finds 'a'), and stop with the first
key that is > 'a' (so it doesn't find 'aC').
If we applied that idea in the w1234 index,
Bar explains that it should work as follows:
1. either scan all records between
   using primary level only.
   where  - smallest primary non-ignorable character
   and  - biggest primary non-ignorable character in the collation.
2. or make like_range generate the range as follows:
    notice capital A WITH RING BELOW in the second value - which
    is the greatest among all 'A's in DUCET.
With this range we can use all levels to scan the range.
But I think, this type of range generation is quite tricky.
It will need operation which is able to:
"Find a letter which is the same on the primary
level and greatest on the other levels", which
in the above example will convert "SMALL LETTER A"
Implementing this feature will need some extra tables.
So I think N1 is a better solution.
That means we should either support one-level search in a multi-level
index under terms of WL#896, or we'll have to disallow LIKE

Generally speaking, a UNIQUE index is compared for level-1 only,
except for Hungarian and Japanese. We use comparison strength.
So in a _w1234a index 'A' and 'a' are duplicates.
In a _w1234 index 'A' and 'a' are not duplicates.

Open questions
- multi-level collations and InnoDB
- multi-level collations and NDB
- multi-level collations and partitioning

Trailing spaces

Trailing spaces (U+0020) will be represented at level-1.
Trailing spaces will not be represented at level-2 or level-3.
Trailing spaces will be represented at level-4.

Email archives      
The contents of this task come from some emails      
exchanged in November 2004 between Alexander Barkov,      
Sergei Golubchik, and Peter Gulutzan, threads      
"MySQL 4.1 for [name of another Japanese customer deleted,
see progress reports]" and "MySQL 4.1 case sensitive patch for
[name of another Japanese customer deleted, see progress reports]".      
Earlier discussions are in scrum-tasks, scrum-daily, and support archives:      
Thread "Character sets" [January 2004]      
Thread "Bar's proposed scrum todo for February" [February 2004]      
Thread "[req] utf8_general_cs patch" [July 2004]      

Thread "Re: WL#896 Primary, Secondary and Tertiary Sorts"
search in dev-private email archive

"What the @!#$% is the TERTIARY_WEIGHTS() function for?" (Microsoft blogger)
Detailed design discussions have taken place via
email; no further LLD needed.