Skip to content
  • Olav Sandstaa's avatar
    fb41887c
    Fix for Bug#13249966 MRR: RANDOM ERROR DUE TO UNINITIALIZED RES WITH · fb41887c
    Olav Sandstaa authored
                         SMALL READ_RND_BUFFER_SIZE
    
    This bug is triggered by setting the system variable
    read_rnd_buffer_size so low that the created sort buffer used by
    DS-MRR is too small to fit a single key. When the range scan using
    DS-MRR starts, it is unable to store any keys in the sort buffer and
    an uninitialized return value is returned. This return value would
    have been given its initial value when the first record was read from
    the index. This causes either a random error value or wrong results
    from the range scan (zero records). The patch fixes two bugs in the
    DS-MRR implementation.
    
    Bug 1 (the original problem):
    
    The cause for this failure is found in DsMrr_impl::choose_mrr_impl().
    One of its arguments is the buffer size which is initially specified
    by read_rnd_buffer_size. Before this is used as argument in the call
    to get_disk_sweep_mrr_cost() the following is subtracted from the
    buffer size:
    
      table->key_info[keyno].key_length + h->ref_length;
    
    The buffer size is stored as an unsigned integer and if it is smaller
    than the above expression, the result of the subtraction becomes a
    very large number. When get_disk_sweep_mrr_cost() is called with this
    huge buffer size, it concludes that there are plenty of room for
    storing keys in this buffer when it should have concluded that the
    buffer was too small for storing a single key. This causes the
    choose_mrr_impl() to return that DS-MRR should be used when it should
    have returned that the default MRR implementation should have been used due
    to too small buffer size.
    
    The subtraction (the above expression) from the buffer size in
    DsMrr_impl::choose_mrr_impl() was originally done because an early
    version of the DsMrr implementation used the sort buffer to store the
    current position of the scan each time it had filled the sort
    buffer. This was needed in order to be able to restart the scan next
    time it should fill the sort buffer. The subtraction was done to count
    for the space needed for storing the current position. This
    implementation has since been changed to use a second handler object
    for scanning the index. With this is place, there was no need for
    storing the current position in the sort buffer. The code for storing
    and reading the current scan position to/from the sort buffer has been
    removed but the reservation of space for it in
    DsMrr_impl::choose_mrr_impl() has been left in the code. The fix for
    avoiding that the buffer size is assigned a wrong value is to remove
    this subtraction.
    
    Bug 2:
    
    This fix solved the original bug but caused several query plans to
    wrongly change from using MRR or BKA to not using these. This was
    caused by a bug in DsMrr_impl::dsmrr_info(). This function first
    computes the cost for using the default MRR strategy by calling
    handler::multi_range_read_info(). This takes as one of its arguments a
    pointer to the buffer size. Since the default MRR implementation does
    not need a sort buffer, it sets the buffer size to zero. To avoid that
    this overwrites the original buffer size, a local variable def_bufsz
    is used in DsMrr_impl::dsmrr_info(). After the call to
    handler::multi_range_read_info() this will thus always be zero. The
    bug is that this local variable is used as argument when calling
    choose_mrr_impl() instead of the original buffer size stored in
    *bufsz. This causes choose_mrr_impl() to be called with a buffer size
    of 0 bytes, and as a result it will return that the default MRR
    implementation should be used (since the zery byte buffer size is too
    small).
    
    The reason this bug has not surfaced earlier is that it has been
    "hidden" by the first bug. When calling choose_mrr_impl() with a
    buffer size of 0 the subtraction in choose_mrr_impl() would (wrongly)
    change this to a very large buffer size that was large enough to use
    for DS-MRR (or in another way: this second bug sets the supplied
    buffer size to 0 in DsMrr_impl::dsmrr_info() and then the first bug
    compensated by changing the 0 byte buffer size to a very large buffer
    size in choose_mrr_impl()).
    
    The fix for this is to replace the bufsz and flags arguments in the
    call to choose_mrr_impl() to use the original values instead of the
    values in the local variables def_flags and def_bufsz. After this fix
    the implementation of DsMrr_impl::dsmrr_info() is similar to the
    corresponding implementation in DsMrr_impl::dsmrr_info_const().
    fb41887c
    Fix for Bug#13249966 MRR: RANDOM ERROR DUE TO UNINITIALIZED RES WITH
    Olav Sandstaa authored
                         SMALL READ_RND_BUFFER_SIZE
    
    This bug is triggered by setting the system variable
    read_rnd_buffer_size so low that the created sort buffer used by
    DS-MRR is too small to fit a single key. When the range scan using
    DS-MRR starts, it is unable to store any keys in the sort buffer and
    an uninitialized return value is returned. This return value would
    have been given its initial value when the first record was read from
    the index. This causes either a random error value or wrong results
    from the range scan (zero records). The patch fixes two bugs in the
    DS-MRR implementation.
    
    Bug 1 (the original problem):
    
    The cause for this failure is found in DsMrr_impl::choose_mrr_impl().
    One of its arguments is the buffer size which is initially specified
    by read_rnd_buffer_size. Before this is used as argument in the call
    to get_disk_sweep_mrr_cost() the following is subtracted from the
    buffer size:
    
      table->key_info[keyno].key_length + h->ref_length;
    
    The buffer size is stored as an unsigned integer and if it is smaller
    than the above expression, the result of the subtraction becomes a
    very large number. When get_disk_sweep_mrr_cost() is called with this
    huge buffer size, it concludes that there are plenty of room for
    storing keys in this buffer when it should have concluded that the
    buffer was too small for storing a single key. This causes the
    choose_mrr_impl() to return that DS-MRR should be used when it should
    have returned that the default MRR implementation should have been used due
    to too small buffer size.
    
    The subtraction (the above expression) from the buffer size in
    DsMrr_impl::choose_mrr_impl() was originally done because an early
    version of the DsMrr implementation used the sort buffer to store the
    current position of the scan each time it had filled the sort
    buffer. This was needed in order to be able to restart the scan next
    time it should fill the sort buffer. The subtraction was done to count
    for the space needed for storing the current position. This
    implementation has since been changed to use a second handler object
    for scanning the index. With this is place, there was no need for
    storing the current position in the sort buffer. The code for storing
    and reading the current scan position to/from the sort buffer has been
    removed but the reservation of space for it in
    DsMrr_impl::choose_mrr_impl() has been left in the code. The fix for
    avoiding that the buffer size is assigned a wrong value is to remove
    this subtraction.
    
    Bug 2:
    
    This fix solved the original bug but caused several query plans to
    wrongly change from using MRR or BKA to not using these. This was
    caused by a bug in DsMrr_impl::dsmrr_info(). This function first
    computes the cost for using the default MRR strategy by calling
    handler::multi_range_read_info(). This takes as one of its arguments a
    pointer to the buffer size. Since the default MRR implementation does
    not need a sort buffer, it sets the buffer size to zero. To avoid that
    this overwrites the original buffer size, a local variable def_bufsz
    is used in DsMrr_impl::dsmrr_info(). After the call to
    handler::multi_range_read_info() this will thus always be zero. The
    bug is that this local variable is used as argument when calling
    choose_mrr_impl() instead of the original buffer size stored in
    *bufsz. This causes choose_mrr_impl() to be called with a buffer size
    of 0 bytes, and as a result it will return that the default MRR
    implementation should be used (since the zery byte buffer size is too
    small).
    
    The reason this bug has not surfaced earlier is that it has been
    "hidden" by the first bug. When calling choose_mrr_impl() with a
    buffer size of 0 the subtraction in choose_mrr_impl() would (wrongly)
    change this to a very large buffer size that was large enough to use
    for DS-MRR (or in another way: this second bug sets the supplied
    buffer size to 0 in DsMrr_impl::dsmrr_info() and then the first bug
    compensated by changing the 0 byte buffer size to a very large buffer
    size in choose_mrr_impl()).
    
    The fix for this is to replace the bufsz and flags arguments in the
    call to choose_mrr_impl() to use the original values instead of the
    values in the local variables def_flags and def_bufsz. After this fix
    the implementation of DsMrr_impl::dsmrr_info() is similar to the
    corresponding implementation in DsMrr_impl::dsmrr_info_const().
Loading