Skip to content

Commit 2d729f2

Browse files
author
Ajo Robert
committed
Bug#25369742 INCORRECT BEHAVIOUR IN SEL_ARG::RB_INSERT DURING
RANGE QUERY OPTIMIZATION Summary ======== This patch fixes a wrong reference counting during creation of RB tree resulting in server exit and also causing incorrect estimates and thereby wrong plan choice for some cases. Details ====== Range optimizer creates a RB tree representing the entire where condition present in the query. Nodes in the tree represented by SEL_ARG's are OR conditions from where clause. For each node, next_key_part will point to another RB tree which is the AND condition present in the query along with current node condition. ref_count in rb_tree/SEL_ARG represents how many nodes are referring to this particular tree. Please refer opt_range.cc for RB tree details. Below explanation uses a simplified format for representing the tree. Symbols used below are --> => AND relation. | => OR condition [ Node Condition, Node Name, ref_count ] => Node format Consider a query with where clause as: (c1 = 1 AND c2 = 2) OR (c1 = 2 AND c2 = 1) OR (c1 IN (3, 100) AND c2 = 10) OR (c1 IN (1, 2, 3) AND c2 = 2); The resulting condition for this should be: (c1 =1 AND c2 = 2) OR (c1 = 2 AND c2 = 1) OR (c1 = 3 AND c2= 10) OR (c1 = 100 AND c2 = 10) OR (c1= 1 AND c2 = 2) OR (c1=2 AND c2=2) OR (c1=3 AND c2=2) First part of the where clause: [ (c1 = 1 AND c2 = 2) OR (c1 = 2 AND c2 = 1) OR (c1 IN (3, 100) AND c2 = 10) ] will result in below RB tree First time when we OR, we get [c1=1, D, 1] --> [c2=2, C, 1] | v [c1=2, A, 0] --> [c2=1, B, 1] Second time we OR, we get [Tree 1] [c1=1, D, 1] --> [c2=2, C, 1] | | [c1=2, A, 0] --> [c2=1, B, 1] | | [c1=3, E, 0] --> [c2=10, G, 2] | ^ | / [c1=100, F, 0] Rest of the condition [ (c1 IN (1, 2, 3) AND c2 = 2) ] will result in below tree formation [Tree 2] [c1=1, I, 1] | \ | v [c1=2, H, 0] --> [c2=2, K, 3] | ^ | / [c1=3, J, 0] Below steps are performed while doing tree_or between these two trees. Step 1. Take the first node from Tree.2 (Node I) and try to insert into tree 1. As there is an exact match found in Tree 1 (Node D), this node will be ignored and will be deleted from Tree 2. As part of the delete operation, increment_use_count() for the deleted node will be called with -1. This function will reduce ref_count of next_key_part of all nodes in the current tree. As a result, ref_count of Node.K will be reduced 3 times (one for Node H, I, J). Resulting [Tree 2] will look like below [c1 = 2, H, 1] | \ | v [C1 = 3, J, 0] --> [c2 = 2, K, 0] Step 2. Node H will be merged with Node A. Steps to merge these nodes are; As the condition is exactly same, it will copy next key part of H and merge with B. If the next key part node (Node K) ref_count is more than 1 then a copy is made to avoid any unwanted links. In this case, it is (0) not more than 1 and Node.K will be added as an OR element to Node.B. Resulting Tree 1 will look like below. [c1=1, D, 1] --> [c2=2, C, 1] | | [c1=2, A, 0] --> [c2=1, B, 1] | | | | | [c2=2, K, 0] <-- [c1=3, J, 0] | [c1=3, E, 0] --> [c2=10, G, 2] | ^ | / [c1=100, F, 0] Note that Node.J is attached to Tree 1 now. Which is an unwanted consequence of the ref_count reduction of Node.K 3 times at step 1. Step 3: Node J will be merged with node E. Similar to above case, node conditions are exactly same, so next_key_part merge will happen. Like above scenario Node.K ref_count is not more than 1, so no copy will be made but a copy of G will be made (Node H)(due to ref_count = 2) and together attached to node E. Resulting tree will look like below. [c1=1, D, 1] --> [c2=2, C, 1] | | [c1=2, A, 0] --> [c2=1, B, 1] | | | | | [c2=2, K, 0] | | | | [c1=3, E, 0] --> [c2=10, H, 2] | | [c1=100, F, 0] --> [c2=10, G, 2] As a result of above tree, few new conditions are possible. Which were not present in orginal querry. Example, Consider link C1 = 2 and (C2 = 1 or C2 = 2 or c2 = 10) from above tree. In this, condition (c1 = 2 and c2 = 10) was not present in orginal query. This will result in wrong estimate as it accounts for the newly formed possibilities as well and results in wrong plan. Issue that caused server exit is due to Node.H insertion to Node K will result in undecided result as node K suppose to be the root of the tree and which not the case here due to its link with B as a consequence of the wrong ref_count of Node.K. Solution: During node deletion in tree_or, reduce ref_count of next key_part only once instead of one for each node in the tree.
1 parent da46a89 commit 2d729f2

File tree

1 file changed

+28
-11
lines changed

1 file changed

+28
-11
lines changed

sql/opt_range.cc

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,27 @@ class SEL_ARG :public Sql_alloc
786786
}
787787
}
788788
}
789+
790+
/**
791+
Update use count for SEL_ARG's next_key_part.
792+
This function does NOT update use_count of the current
793+
SEL_ARG object.
794+
795+
Primarily used for reducing reference count of next_key_part of a
796+
node when removed from SEL_ARG tree during tree merge operations.
797+
798+
@param count The number of additional references to this SEL_ARG
799+
tree.
800+
*/
801+
void increment_next_key_part_use_count(long count)
802+
{
803+
if (next_key_part)
804+
{
805+
next_key_part->use_count+= count;
806+
next_key_part->increment_use_count(count);
807+
}
808+
}
809+
789810
void free_tree()
790811
{
791812
for (SEL_ARG *pos=first(); pos ; pos=pos->next)
@@ -8564,7 +8585,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2)
85648585
Use the relevant range in key1.
85658586
*/
85668587
cur_key1->merge_flags(cur_key2); // Copy maybe flags
8567-
cur_key2->increment_use_count(-1); // Free not used tree
8588+
cur_key2->increment_next_key_part_use_count(-1); // Free not used tree
85688589
}
85698590
else
85708591
{
@@ -8694,15 +8715,11 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2)
86948715
86958716
Move on to next range in key2
86968717
*/
8697-
if (cur_key2->next_key_part)
8698-
{
8699-
/*
8700-
cur_key2 will no longer be used. Reduce reference count
8701-
of SEL_ARGs in its next_key_part.
8702-
*/
8703-
cur_key2->next_key_part->use_count--;
8704-
cur_key2->next_key_part->increment_use_count(-1);
8705-
}
8718+
/*
8719+
cur_key2 will no longer be used. Reduce reference count
8720+
of SEL_ARGs in its next_key_part.
8721+
*/
8722+
cur_key2->increment_next_key_part_use_count(-1);
87068723
cur_key2= cur_key2->next;
87078724
continue;
87088725
}
@@ -9041,7 +9058,7 @@ SEL_ARG::tree_delete(SEL_ARG *key)
90419058
key->prev->next=key->next;
90429059
if (key->next)
90439060
key->next->prev=key->prev;
9044-
key->increment_use_count(-1);
9061+
key->increment_next_key_part_use_count(-1);
90459062
if (!key->parent)
90469063
par= &root;
90479064
else

0 commit comments

Comments
 (0)