Bug #95062 Invalid assertion in Cmp_splocal_locations::operator()
Submitted: 19 Apr 2019 8:27 Modified: 7 Jun 2019 17:36
Reporter: Alexey Kopytov Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Stored Routines Severity:S6 (Debug Builds)
Version:5.7, 8.0 OS:Any
Assigned to: CPU Architecture:Any

[19 Apr 2019 8:27] Alexey Kopytov
Description:
Cmp_splocal_locations::operator() in sp_instr.cc has a debug assertion
which I think is too narrow:

class Cmp_splocal_locations
    : public std::binary_function<const Item_splocal *, const Item_splocal *,
                                  bool> {
 public:
  bool operator()(const Item_splocal *a, const Item_splocal *b) {
    DBUG_ASSERT(a->pos_in_query != b->pos_in_query);
    return a->pos_in_query < b->pos_in_query;
  }
};

The function is used to sort an array of pointers to Item_splocal objects in
subst_spvars() using std::sort(). The comparison is performed using the
pos_in_query member variable of Item_splocal.

Apparently, the assertion is intended to ensure there are never duplicate
pos_in_query values for different elements of the array. However, it
doesn't take into account that std::sort() may sometimes compare an
element to itself, in which case a->pos_in_query would be equal to
b->pos_in_query, since a == b.

This behavior is implementation-defined, but I have been unable to find
anything that would prevent std::sort() from comparing elements to
themselves.

Background: I'm looking into a couple of MTR test failures for debug
builds. The reason is the mentioned assertion failing because
std::sort() calls Cmp_splocal_local::operator() with a == b, even though
all array elements have different pos_in_query values. The failures
occur on some platforms (macOS 10.14, CentOS 6), but not on the others
(tested on CentOS 7 and Ubuntu 18.04). Removing the assertion or
replacing std::sort() with std::stable_sort(), for example, makes the
problem disappear, but I think the correct fix is relaxing the
assertion.

How to repeat:
- Look at DBUG_ASSERT() in Cmp_splocal_location::operator()

- note that the assertion would fail if passed two equal pointers
  (i.e. a == b)

- try to find a single reason why a std::sort() implementation cannot
  compare elements to themselves for code optimization purposes or
  whatnot.

Suggested fix:
Relax the assertion from:
    DBUG_ASSERT(a->pos_in_query != b->pos_in_query);
to:
    DBUG_ASSERT(a == b || a->pos_in_query != b->pos_in_query);
[23 Apr 2019 6:33] Alexey Kopytov
If it helps verification, the assertion I'm referring to has been introduced with the following commit:

4c715b798e15b7b92dcec473ef918e89176bd00e
Author:     Tor Didriksen <tor.didriksen@oracle.com>
AuthorDate: Tue Nov 26 09:49:06 2013 +0100
Commit:     Tor Didriksen <tor.didriksen@oracle.com>
CommitDate: Tue Nov 26 09:49:06 2013 +0100

Bug#17781461 GET RID OF DYNAMIC_ARRAY IN THE SP CODE

Patch number 2: remove Dynamic_array from sp_rcontext and sp_instr as well.
[9 May 2019 14:03] MySQL Verification Team
Hi Kaamos, my dear friend,

I have analysed carefully the problem with the above assertion and I agree with you 100 %.

Verified as reported.
[7 Jun 2019 17:36] Paul DuBois
Posted by developer:
 
Fixed in 5.7.27, 8.0.17.

An overly strict assertion could be raised during sorting of stored
program local objects.
[10 Jun 2019 12:39] MySQL Verification Team
Thank you, Paul and thanks a lot, Kaamos .....