From: Date: August 21 2008 4:28pm Subject: bzr commit into mysql-5.0 branch (kgeorge:2648) Bug#37943 List-Archive: http://lists.mysql.com/commits/52215 X-Bug: 37943 Message-Id: <200808211428.m7LES8Ed028020@magare.gmz> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///home/kgeorge/mysql/bzr/old-B37943-5.0-bugteam/ 2648 Georgi Kodinov 2008-08-21 Bug#37943: Reproducible mysqld crash/sigsegv in sel_trees_can_be_ored When analyzing the possible index use cases the server was re-using an internal structure. This is wrong, as this internal structure gets updated during the analysis. Fixed by making a copy of the internal structure for every place it needs to be used. Also stopped the generation of empty structures that unnecessary complicate the analysis. modified: mysql-test/r/index_merge.result mysql-test/t/index_merge.test sql/opt_range.cc per-file messages: mysql-test/r/index_merge.result Bug#37943: test case mysql-test/t/index_merge.test Bug#37943: test case sql/opt_range.cc Bug#37943: - Make copy constructors for SEL_TREE and sub-structures and use them when OR-ing trees. - don't generate empty SEL_TREEs. Return NULL instead. === modified file 'mysql-test/r/index_merge.result' --- a/mysql-test/r/index_merge.result 2007-11-30 05:32:04 +0000 +++ b/mysql-test/r/index_merge.result 2008-08-21 14:27:27 +0000 @@ -518,3 +518,13 @@ a filler b 4 zz 4 5 qq 4 drop table t1, t2; +CREATE TABLE t1 (a varchar(8), b set('a','b','c','d','e','f','g','h'), +KEY b(b), KEY a(a)); +INSERT INTO t1 VALUES ('y',''), ('z',''); +SELECT b,a from t1 WHERE (b!='c' AND b!='f' && b!='h') OR +(a='pure-S') OR (a='DE80337a') OR (a='DE80799'); +b a + y + z +DROP TABLE t1; +End of 5.0 tests === modified file 'mysql-test/t/index_merge.test' --- a/mysql-test/t/index_merge.test 2007-07-14 18:02:10 +0000 +++ b/mysql-test/t/index_merge.test 2008-08-21 14:27:27 +0000 @@ -463,3 +463,18 @@ select * from t2 where a=4 or b=4; drop table t1, t2; +# +# Bug #37943: Reproducible mysqld crash/sigsegv in sel_trees_can_be_ored +# + +CREATE TABLE t1 (a varchar(8), b set('a','b','c','d','e','f','g','h'), + KEY b(b), KEY a(a)); +INSERT INTO t1 VALUES ('y',''), ('z',''); + +#should not crash +SELECT b,a from t1 WHERE (b!='c' AND b!='f' && b!='h') OR + (a='pure-S') OR (a='DE80337a') OR (a='DE80799'); + +DROP TABLE t1; + +--echo End of 5.0 tests === modified file 'sql/opt_range.cc' --- a/sql/opt_range.cc 2008-03-28 18:02:27 +0000 +++ b/sql/opt_range.cc 2008-08-21 14:27:27 +0000 @@ -481,36 +481,6 @@ public: class SEL_IMERGE; - -class SEL_TREE :public Sql_alloc -{ -public: - enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type; - SEL_TREE(enum Type type_arg) :type(type_arg) {} - SEL_TREE() :type(KEY) - { - keys_map.clear_all(); - bzero((char*) keys,sizeof(keys)); - } - SEL_ARG *keys[MAX_KEY]; - key_map keys_map; /* bitmask of non-NULL elements in keys */ - - /* - Possible ways to read rows using index_merge. The list is non-empty only - if type==KEY. Currently can be non empty only if keys_map.is_clear_all(). - */ - List merges; - - /* The members below are filled/used only after get_mm_tree is done */ - key_map ror_scans_map; /* bitmask of ROR scan-able elements in keys */ - uint n_ror_scans; /* number of set bits in ror_scans_map */ - - struct st_ror_scan_info **ror_scans; /* list of ROR key scans */ - struct st_ror_scan_info **ror_scans_end; /* last ROR scan */ - /* Note that #records for each key scan is stored in table->quick_rows */ -}; - - typedef struct st_qsel_param { THD *thd; TABLE *table; @@ -548,6 +518,37 @@ typedef struct st_qsel_param { uint alloced_sel_args; } PARAM; + +class SEL_TREE :public Sql_alloc +{ +public: + enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type; + SEL_TREE(enum Type type_arg) :type(type_arg) {} + SEL_TREE() :type(KEY) + { + keys_map.clear_all(); + bzero((char*) keys,sizeof(keys)); + } + SEL_TREE(SEL_TREE *arg, PARAM *param); + SEL_ARG *keys[MAX_KEY]; + key_map keys_map; /* bitmask of non-NULL elements in keys */ + + /* + Possible ways to read rows using index_merge. The list is non-empty only + if type==KEY. Currently can be non empty only if keys_map.is_clear_all(). + */ + List merges; + + /* The members below are filled/used only after get_mm_tree is done */ + key_map ror_scans_map; /* bitmask of ROR scan-able elements in keys */ + uint n_ror_scans; /* number of set bits in ror_scans_map */ + + struct st_ror_scan_info **ror_scans; /* list of ROR key scans */ + struct st_ror_scan_info **ror_scans_end; /* last ROR scan */ + /* Note that #records for each key scan is stored in table->quick_rows */ +}; + + class TABLE_READ_PLAN; class TRP_RANGE; class TRP_ROR_INTERSECT; @@ -648,6 +649,7 @@ public: trees_next(trees), trees_end(trees + PREALLOCED_TREES) {} + SEL_IMERGE (SEL_IMERGE *arg, PARAM *param); int or_sel_tree(PARAM *param, SEL_TREE *tree); int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree); int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge); @@ -764,6 +766,40 @@ int SEL_IMERGE::or_sel_imerge_with_check } +SEL_TREE::SEL_TREE(SEL_TREE *arg, PARAM *param): Sql_alloc() +{ + keys_map= arg->keys_map; + type= arg->type; + for (int idx= 0; idx < MAX_KEY; idx++) + if ((keys[idx]= arg->keys[idx])) + keys[idx]->increment_use_count(1); + + List_iterator it(arg->merges); + for (SEL_IMERGE *el= it++; el; el= it++) + merges.push_back (new SEL_IMERGE(el, param)); +} + + +SEL_IMERGE::SEL_IMERGE (SEL_IMERGE *arg, PARAM *param) : Sql_alloc() +{ + uint elements= (arg->trees_end - arg->trees); + if (elements > PREALLOCED_TREES) + { + uint size= elements * sizeof (SEL_TREE **); + trees= (SEL_TREE **)alloc_root(param->mem_root, size); + } + else + trees= &trees_prealloced[0]; + + trees_next= trees; + trees_end= trees + elements; + + for (SEL_TREE **tree = trees, **arg_tree= arg->trees; tree < trees_end; + tree++, arg_tree++) + *tree= new SEL_TREE(*arg_tree, param); +} + + /* Perform AND operation on two index_merge lists and store result in *im1. */ @@ -823,10 +859,15 @@ int imerge_list_or_tree(PARAM *param, { SEL_IMERGE *imerge; List_iterator it(*im1); + bool tree_used= false; while ((imerge= it++)) { - if (imerge->or_sel_tree_with_checks(param, tree)) + if (imerge->or_sel_tree_with_checks(param, + tree_used ? + new SEL_TREE(tree, param) : + tree)) it.remove(); + tree_used= true; } return im1->is_empty(); } @@ -4238,6 +4279,8 @@ get_mm_parts(PARAM *param, COND *cond_fu } } + if (tree && tree->merges.is_empty() && tree->keys_map.is_clear_all()) + tree= NULL; DBUG_RETURN(tree); }