diff options
author | unknown <sergefp@mysql.com> | 2005-05-15 22:56:45 +0200 |
---|---|---|
committer | unknown <sergefp@mysql.com> | 2005-05-15 22:56:45 +0200 |
commit | 06736ab48a6ea988c230127896a8083bf88557d9 (patch) | |
tree | 1690114b729879844dd5b6acc3ed911663fdf891 /sql/sql_list.h | |
parent | ce41e0de8132a95c98c16fe1b21c48cc5f366203 (diff) | |
download | mariadb-git-06736ab48a6ea988c230127896a8083bf88557d9.tar.gz |
Fix for BUG#10095: {wrong query results because of incorrect constant propagation}
The problem: base_list::remove didn't modify base_list::last when removing
the last list element.
The fix: If we remove the last element, find the element before it (by walking
from the beginning of the list) and set base_list::last accordingly.
The list gets corrupted in both 4.0 and 4.1. There are no visible problems in
current 4.1 because current 4.1 doesn't call where_cond->fix_fields() after
constant propagation step.
mysql-test/r/select.result:
Testcase for BUG#10095
mysql-test/t/select.test:
Testcase for BUG#10095
sql/sql_list.h:
Fix for BUG#10095: {wrong query results because of incorrect constant propagation}
The problem: base_list::remove didn't modify base_list::last when removing
the last list element.
The fix: If we remove the last element, find the element before it (by walking
from the beginning of the list) and set base_list::last accordingly.
Diffstat (limited to 'sql/sql_list.h')
-rw-r--r-- | sql/sql_list.h | 74 |
1 files changed, 72 insertions, 2 deletions
diff --git a/sql/sql_list.h b/sql/sql_list.h index 370642df2d0..9e62b7ce730 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -110,10 +110,32 @@ public: void remove(list_node **prev) { list_node *node=(*prev)->next; + if (&(*prev)->next == last) + { + /* + We're removing the last element from the list. Adjust "last" to point + to the previous element. + The other way to fix this would be to change this function to + remove_next() and have base_list_iterator save ptr to previous node + (one extra assignment in iterator++) but as the remove() of the last + element isn't a common operation it's faster to just walk through the + list from the beginning here. + */ + list_node *cur= first; + if (cur == *prev) + { + last= &first; + } + else + { + while (cur->next != *prev) + cur= cur->next; + last= &(cur->next); + } + } delete *prev; *prev=node; - if (!--elements) - last= &first; + elements--; } inline void *pop(void) { @@ -130,6 +152,54 @@ public: inline list_node *last_ref() { return &end_of_list; } friend class base_list_iterator; +#ifdef LIST_EXTRA_DEBUG + /* + Check list invariants and print results into trace. Invariants are: + - (*last) points to end_of_list + - There are no NULLs in the list. + - base_list::elements is the number of elements in the list. + + SYNOPSIS + check_list() + name Name to print to trace file + + RETURN + 1 The list is Ok. + 0 List invariants are not met. + */ + + bool check_list(const char *name) + { + base_list *list= this; + list_node *node= first; + uint cnt= 0; + + while (node->next != &end_of_list) + { + if (!node->info) + { + DBUG_PRINT("list_invariants",("%s: error: NULL element in the list", + name)); + return FALSE; + } + node= node->next; + cnt++; + } + if (last != &(node->next)) + { + DBUG_PRINT("list_invariants", ("%s: error: wrong last pointer", name)); + return FALSE; + } + if (cnt+1 != elements) + { + DBUG_PRINT("list_invariants", ("%s: error: wrong element count", name)); + return FALSE; + } + DBUG_PRINT("list_invariants", ("%s: list is ok", name)); + return TRUE; + } +#endif // LIST_EXTRA_DEBUG + protected: void after(void *info,list_node *node) { |