Bug #59465 btr_estimate_number_of_different_key_vals use incorrect offset for external_siz
Submitted: 13 Jan 2011 10:57 Modified: 10 Feb 2011 20:00
Reporter: Jimmy Yang Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:5.1, 5.5 OS:Any
Assigned to: Jimmy Yang CPU Architecture:Any
Triage: Needs Triage: D3 (Medium) / R2 (Low) / E2 (Low)

[13 Jan 2011 10:57] Jimmy Yang
Description:
It seems there is a bug in function btr_estimate_number_of_different_key_vals() in  btr/btr0cur.c when calculating "total_external_size". The issue is in 5.1 and above:

btr_estimate_number_of_different_key_vals() 
{

   n_cols = dict_index_get_n_unique(index);  <== n_cols is num of unique
   ...
   while (!page_rec_is_supremum(rec)) {
           
                        .....
                        offsets_next_rec = rec_get_offsets(next_rec, index,
                                                           offsets_next_rec,
                                                           n_cols, &heap);
                        total_external_size
                                += btr_rec_get_externally_stored_len(
                                        rec, offsets_rec);  <=== notice this offsets is also num of unique instead of all columns, should use ULINT_UNDEFINED
                        rec = next_rec;

                        /* Initialize offsets_rec for the next round
                        and assign the old offsets_rec buffer to
                        offsets_next_rec. */
                        {
                                ulint*  offsets_tmp = offsets_rec;
                                offsets_rec = offsets_next_rec;
                                offsets_next_rec = offsets_tmp;
                        }
                }

     ..
}

Essentially, we are passing unique column "n_cols" to rec_get_offsets to get unique column offsets, subseqently btr_rec_get_externally_stored_len() is used to get the external stored length, but with offset of unique columns instead of all columns.

Note in the while loop, offset_rec is assigned to offsets_next_rec whenever we move to next rec, and then used in calculate external size by calling btr_rec_get_externally_stored_len():

total_external_size += btr_rec_get_externally_stored_len(rec, offsets_rec);

So this is incorrect as it has only n_cols column offsets when we estimate the number of external storage pages. 

How to repeat:
code inspection

Suggested fix:
Use ULINT_UNDEFINED instead of n_cols when getting offset
[30 Jan 2011 16:59] Bugs System
Pushed into mysql-5.1 5.1.56 (revid:vasil.dimov@oracle.com-20110130164158-1q99a41kb2wvkw3a) (version source revid:vasil.dimov@oracle.com-20110130164158-1q99a41kb2wvkw3a) (merge vers: 5.1.56) (pib:24)
[30 Jan 2011 17:00] Bugs System
Pushed into mysql-trunk 5.6.2 (revid:vasil.dimov@oracle.com-20110130165639-1pr3opz839b98q5j) (version source revid:vasil.dimov@oracle.com-20110130165522-m0o6al0pn5ig9kv3) (merge vers: 5.6.2) (pib:24)
[30 Jan 2011 17:00] Bugs System
Pushed into mysql-5.5 5.5.10 (revid:vasil.dimov@oracle.com-20110130165343-he9art47agq1a3gr) (version source revid:vasil.dimov@oracle.com-20110130165137-5lvzsq9j29j0hp1s) (merge vers: 5.5.10) (pib:24)