Bug #68713 create_duplicate_weedout_tmp_table() leaves key_part_flag uninitialized
Submitted: 19 Mar 2013 7:53 Modified: 25 Mar 2015 14:45
Reporter: Alexey Kopytov Email Updates:
Status: Closed Impact on me:
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:5.6 OS:Any
Assigned to: Tor Didriksen CPU Architecture:Any

[19 Mar 2013 7:53] Alexey Kopytov
create_duplicate_weedout_tmp_table() leaves the key_part_flag field of key_part_info uninitialized when building a key definition for the temporary table. Under some circumstances it may also use that uninitialized value when adding the HA_END_SPACE_ARE_EQUAL flag:

      key_part_info->field=  field;
      key_part_info->offset= field->offset(table->record[0]);
      key_part_info->length= (uint16) field->key_length();
      key_part_info->type=   (uint8) field->key_type();
      key_part_info->key_type = FIELDFLAG_BINARY;
      if (!using_unique_constraint)
        key_field= field->new_key_field(thd->mem_root, table, group_buff);
        if (!key_field)
	  goto err;
        key_part_info->key_part_flag|= HA_END_SPACE_ARE_EQUAL; //todo need this?
      keyinfo->key_length+=  key_part_info->length;

The table being created may be either HEAP or MyISAM. The key_part_flag field is not used by the HEAP engine in MySQL (though it is used in Percona Server), but it is used by MyISAM in all branches.

For comparison, create_tmp_table() always initializes that field by calling KEY_PART_INFO::init_from_field().

It may take me a while to build a test case for MySQL that would fail (or indicate uninitialized value usage under Valgrind). However, the problem should be clear from code analysis and comparison with create_tmp_table().

How to repeat:
Read the referenced code from create_duplicate_weedout_tmp_table().
[19 Mar 2013 9:13] MySQL Verification Team
I'm setting this verified.  I hit the location in visual studio, but for some reason the same query didn't trigger a valgrind error, so I also don't have a solid testcase yet.

mysqld.exe!create_duplicate_weedout_tmp_table(THD * thd=0x000000005c7bc930, unsigned int uniq_tuple_length_arg=0, SJ_TMP_TABLE * sjtbl=0x000000005c7bbcb0)  Line 1426	C++
mysqld.exe!setup_semijoin_dups_elimination(JOIN * join=0x000000004e6e8cf0, unsigned __int64 options=1551616304, unsigned int no_jbuf_after=322781104)  Line 619	C++
mysqld.exe!make_join_readinfo(JOIN * join=0x00000000133d3fb0, unsigned __int64 options=4, unsigned int no_jbuf_after=4294967295)  Line 2772 + 0xa bytes	C++
mysqld.exe!JOIN::optimize()  Line 734 + 0xf bytes	C++
mysqld.exe!mysql_execute_select(THD * thd=0x00000000133d5d88, st_select_lex * select_lex=0x00000000493c1410, bool free_join=true)  Line 1086 + 0xb bytes	C++
mysqld.exe!mysql_explain_unit(THD * thd=0x00000000493c1410, st_select_lex_unit * unit=0x00000000133d5cd8, select_result * result=0x00000000133d63b8)  Line 2119	C++
mysqld.exe!explain_query_expression(THD * thd=0x0000000000000000, select_result * result=0x00000000133d5cd8)  Line 2020 + 0x2f bytes	C++
mysqld.exe!execute_sqlcom_select(THD * thd=0x00000000493c1410, TABLE_LIST * all_tables=0x0000000000000000)  Line 4796 + 0xb bytes	C++
mysqld.exe!mysql_execute_command(THD * thd=0x0000000000000204)  Line 2467 + 0xd bytes	C++
mysqld.exe!mysql_parse(THD * thd=0x0000000000000206, char * rawbuf=0x000000005d2144b1, unsigned int length=315484832, Parser_state * parser_state=0x0000000000000003)  Line 5964	C++
mysqld.exe!dispatch_command(enum_server_command command=COM_SLEEP, THD * thd=0x0000000000000000, char * packet=0x0000000011788ce0, unsigned int packet_length=293082720)  Line 1334	C++
mysqld.exe!do_command(THD * thd=0x0000000000000001)  Line 1034	C++
mysqld.exe!do_handle_one_connection(THD * thd_arg=0x00000000133d3fb0)  Line 982 + 0xa bytes	C++
mysqld.exe!handle_one_connection(void * arg=0x0000000011788ce0)  Line 900	C++
mysqld.exe!pfs_spawn_thread(void * arg=0x0000000011781660)  Line 1925	C++
mysqld.exe!pthread_start(void * p=0x00000000117903d0)  Line 63	C
mysqld.exe!_callthreadstartex()  Line 314 + 0xd bytes	C
mysqld.exe!_threadstartex(void * ptd=0x0000000000000000)  Line 292 + 0x5 bytes	C

if (!using_unique_constraint)
        key_field= field->new_key_field(thd->mem_root, table, group_buff);
        if (!key_field)
	  goto err;
        key_part_info->key_part_flag|= HA_END_SPACE_ARE_EQUAL; //todo need this?
      keyinfo->key_length+=  key_part_info->length;
	key_part_flag	61453	unsigned short

Note that before the or'ing, 61453 is 0xF00D in hex. Clearly uninitialized.
[19 Mar 2013 9:44] MySQL Verification Team
fwiw, the create_tmp_table one was fixed here:
[25 Nov 2014 8:57] Laurynas Biveinis
Bug 68713 patch for 5.7

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: bug68713.patch (application/octet-stream, text), 1.19 KiB.

[25 Nov 2014 8:58] Laurynas Biveinis
Patch author Sergei Glushchenko.
[25 Mar 2015 14:45] Paul DuBois
Noted in 5.7.7, 5.8.0 changelogs.

Some key descriptors used by the optimizer were uninitialized. Thanks
to Sergei Glushchenko for the patch.
[16 Oct 2015 12:09] Laurynas Biveinis
$ git show 92a0280
commit 92a0280e65d2536152c708518b6691aa0153f94c
Author: Tor Didriksen <tor.didriksen@oracle.com>
Date:   Thu Feb 12 07:06:33 2015 +0100

    Bug#68713 create_duplicate_weedout_tmp_table() leaves key_part_flag uninitialized
    Replace the custom key_part_info initialization code with a
    key_part_info->init_from_field call that initializes key_part_flag too.
    Patch contributed by Sergei Glushchenko / Laurynas Biveinis