This file describes how MySQL index navigation commands are translated into RocksDB index navigation commands. Index tuples are shown as ( kv )-aaa-pkN here * '(kv)' is the 4-byte index number. * '-' is just for readability * everything that follows the '-' is mem-comparable form of the key. In ascii encoding, aaa < bbb < ccc < xxx. Tuples that start with '#' do not exist in the database. They are only shown to demonstrate where Seek() calls end up with. == HA_READ_KEY_EXACT, forward CF == (kv-1)-xxx-pk # ( kv )-aaa <-- "kv-aaa" doesn't exist in the database, but it would be here. ( kv )-aaa-pk <--- Seek("kv-aaa") will put us here on the next record. ( kv )-aaa-pk2 ( kv )-bbb-... RocksDB calls: it->Seek(kv); if (it->Valid() && kd->covers_key(..) && kd->cmp_full_keys(...)) return record. == HA_READ_KEY_EXACT, backward CF == When we need to seek to a tuple that is a prefix of a full key: (kv+1)-xxx-pk ( kv )-ccc-pk ( kv )-bbb-pk3 ( kv )-bbb-pk2 ( kv )-bbb-pk1 < -- We need to be here # ( kv )-bbb <---we call Seek(kv-bbb) ( kv )-aaa-pk ... and end up here. Should call it->Prev(). There is a special case when (kv)-bbb-pk1 is the last record in the CF, and we get invalid iterator. Then, we need to call SeekToLast(). Another kind of special case is when we need to seek to the full value. Suppose, the lookup tuple is kv-bbb-pk1: (kv+1)-xxx-pk ( kv )-ccc-pk ( kv )-bbb-pk3 ( kv )-bbb-pk2 ( kv )-bbb-pk1 < -- Seek(kv-bbb-pk1) ( kv )-bbb-pk0 Then, Seek(kv-bbb-pk1) will position us exactly the tuple we need, and we won't need to call it->Prev(). If we get an invalid iterator, there is no need to call SeekToLast(). RocksDB calls: it->Seek(tuple); if (!using_full_key) { if (!it->Valid()) it->SeekToLast(); else it->Prev(); } if (it->Valid() && kd->covers_key(..) && kd->cmp_full_keys(...)) return record. == HA_READ_KEY_OR_NEXT, forward CF == This is finding min(key) such that key >= lookup_tuple. If lookup tuple is kv-bbb: ( kv )-aaa-pk # ( kv )-bbb <-- "kv-bbb" doesn't exist in the database, but it would be here. ( kv )-bbb-pk1 <--- Seek("kv-bbb") will put us here on the next record. ( kv )-bbb-pk2 ( kv )-bbb-... RocksDB calls: Seek(kv); if (it->Valid() && kd->covers_key(..) && kd->cmp_full_keys(...)) return record. == HA_READ_KEY_OR_NEXT, backward CF == When specified key tuple is a key prefix: (kv+1)-xxx-pk ( kv )-ccc-pk ( kv )-bbb-pk3 ( kv )-bbb-pk2 ( kv )-bbb-pk1 < -- We need to be here (or above) # ( kv )-bbb <---we call Seek(kv-bbb) ( kv )-aaa-pk ... and end up here. Should call it->Prev(). There is a special case when (kv)-bbb-pk1 is the last record in the CF, and we get invalid iterator. Then, we need to call SeekToLast(). Another kind of special case is when we need to seek to the full value. Suppose, the lookup tuple is kv-bbb-pk1: (kv+1)-xxx-pk ( kv )-ccc-pk ( kv )-bbb-pk3 ( kv )-bbb-pk2 ( kv )-bbb-pk1 < -- Seek(kv-bbb-pk1) ( kv )-bbb-pk0 Then, Seek(kv-bbb-pk1) may position us exactly at the tuple we need, and we won't need to call it->Prev(). If kv-bbb-pk1 is not present in the database, we will be positioned on kv-bbb-pk0, and we will need to call it->Prev(). If we get an invalid iterator, we DO need to call SeekToLast(). RocksDB calls: Seek(...); if (!it->Valid()) it->SeekToLast(); else { if (!using_full_key || !(kd->covers_key(...) || kd->cmp_full_keys(...)) it->Prev(); } if (it->Valid() && kd->covers_key(..)) return record. == HA_READ_AFTER_KEY, forward CF == This is finding min(key) such that key > lookup_key. Suppose lookup_key = kv-bbb ( kv )-aaa-pk # ( kv )-bbb ( kv )-bbb-pk1 <--- Seek("kv-bbb") will put us here. We need to ( kv )-bbb-pk2 get to the value that is next after 'bbb'. ( kv )-bbb-pk3 ( kv )-bbb-pk4 ( kv )-bbb-pk5 ( kv )-ccc-pkN <-- That is, we need to be here. However, we don't know that the next value is kv-ccc. Instead, we seek to the first value that strictly greater than 'kv-bbb'. It is Successor(kv-bbb). It doesn't matter if we're using a full extended key or not. RocksDB calls: Seek(Successor(kv-bbb)); if (it->Valid() && kd->covers_key(it.key())) return record; Note that the code is the same as with HA_READ_KEY_OR_NEXT, except that we seek to Successor($lookup_key) instead of $lookup_key itself. == HA_READ_AFTER_KEY, backward CF == Suppose, the lookup key is 'kv-bbb': (kv+1)-xxx-pk ( kv )-ccc-pk7 ( kv )-ccc-pk6 <-- We need to be here. # Successor(kv-bbb) <-- We get here when we call Seek(Successor(kv-bbb)) ( kv )-bbb-pk5 and we will need to call Prev() (*) ( kv )-bbb-pk4 ( kv )-bbb-pk3 ( kv )-bbb-pk2 ( kv )-bbb-pk1 # ( kv )-bbb <-- We would get here if we called Seek(kv-bbb). ( kv )-aaa-pk (*) - unless Successor(kv-bbb)=(kv-ccc), and Seek(kv-ccc) hits the row. In that case, we won't need to call Prev(). RocksDB calls: Seek(Successor(kv-bbb)); if (!it->Valid()) { /* We may get EOF if rows with 'kv-bbb' (below the Successor... line in the diagram) do not exist. This doesn't mean that rows with values kv-ccc do not exist. */ it->SeekToLast(); } else { if (!using_full_key || !kd->value_matches_prefix(...)) { it->Prev(); } } if (it->Valid() && kd->covers_key(...)) return record. Note that the code is the same as with HA_READ_KEY_OR_NEXT, except that we seek to Successor($lookup_key) instead of $lookup_key itself. == HA_READ_BEFORE_KEY, forward CF == This is finding max(key) such that key < lookup_tuple. Suppose, lookup_tuple=kv-bbb. ( kv )-aaa-pk1 ( kv )-aaa-pk2 ( kv )-aaa-pk3 <-- Need to be here. # ( kv )-bbb ( kv )-bbb-pk4 <-- Seek("kv-bbb") will put us here. ( kv )-bbb-pk5 ( kv )-bbb-pk6 1. Seek(kv-bbb) will put us at kv-bbb-pk4 (or return an invalid iterator if kv-bbb-pk4 and subsequent rows do not exist in the db). 2. We will need to call Prev() to get to the record before. (if there is no record before kv-bbb, then we can't find a record). It doesn't matter if we're using a full extended key or not. RocksDB calls: it->Seek(kv-bbb); if (it->Valid()) it->Prev(); else it->SeekToLast(); if (it->Valid() && kd->covers_key(...)) return record; == HA_READ_BEFORE_KEY, backward CF == This is finding max(key) such that key < lookup_tuple. Suppose, lookup_tuple=kv-bbb, a prefix of the full key. ( kv )-bbb-pk6 ( kv )-bbb-pk5 ( kv )-bbb-pk4 # ( kv )-bbb ( kv )-aaa-pk3 <-- Need to be here, and Seek("kv-bbb") will put us here ( kv )-aaa-pk2 ( kv )-aaa-pk1 If the lookup tuple is a full key (e.g. kv-bbb-pk4), and the key is present in the database, the iterator will be positioned on the key. We will need to call Next() to get the next key. RocksDB calls: it->Seek(kv-bbb); if (it->Valid() && using_full_key && kd->value_matches_prefix(...)) { /* We are using full key and we've hit an exact match */ it->Next(); } if (it->Valid() && kd->covers_key(...)) return record; == HA_READ_PREFIX_LAST, forward CF == Find the last record with the specified index prefix lookup_tuple. Suppose, lookup_tuple='kv-bbb' ( kv )-aaa-pk2 ( kv )-aaa-pk3 # ( kv )-bbb ( kv )-bbb-pk4 ( kv )-bbb-pk5 ( kv )-bbb-pk6 ( kv )-bbb-pk7 <--- Need to be here. # ( kv )-ccc ( kv )-ccc-pk8 <-- Seek(Successor(kv-bbb)) will get us here. will need ( kv )-ccc-pk9 to call Prev(). RocksDB calls: Seek(Successor(kv-bbb)); if (!it->Valid()) it->SeekToLast(); else it->Prev(); if (it->Valid() && kd->covers_key(...)) { if (!cmp_full_keys(lookup_tuple)) // not needed in _OR_PREV { // the record's prefix matches lookup_tuple. return record; } } == HA_READ_PREFIX_LAST, backward CF == Suppose, lookup_tuple='kv-bbb' ( kv )-ccc-pk9 ( kv )-ccc-pk8 # ( kv )-ccc <-- 2. Seek(Successor(kv-bbb)) will point here and it will fall down to the next row. ( kv )-bbb-pk7 <--- 1. Need to be here. ( kv )-bbb-pk6 ( kv )-bbb-pk5 ( kv )-bbb-pk4 # ( kv )-bbb ( kv )-aaa-pk3 ( kv )-aaa-pk2 RocksDB calls: it->Seek(Successor(kv-bbb)); if (using_full_key && it->Valid() && !cmp_full_keys(Sucessor(lookup_key))) it->Next(); if (it->Valid() && kd->covers_key(..)) { if (!cmp_full_keys(...)) // not needed in _OR_PREV { // the record's prefix matches lookup_tuple. return record; } } == HA_READ_PREFIX_LAST_OR_PREV, forward or backward CF == This is just like HA_READ_PREFIX_LAST but we don't need to check that the key we've got is in the search prefix. (search for "not needed in _OR_PREV" above)