summaryrefslogtreecommitdiff
path: root/sql/sql_list.h
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2005-05-15 22:56:45 +0200
committerunknown <sergefp@mysql.com>2005-05-15 22:56:45 +0200
commit06736ab48a6ea988c230127896a8083bf88557d9 (patch)
tree1690114b729879844dd5b6acc3ed911663fdf891 /sql/sql_list.h
parentce41e0de8132a95c98c16fe1b21c48cc5f366203 (diff)
downloadmariadb-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.h74
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)
{