Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-31647

Stack looping and SIGSEGV in Item_args::walk_args on UPDATE

Details

    Description

      --source include/have_innodb.inc
      CREATE TABLE c (c INT, c2 INT) ENGINE=InnoDB;
      UPDATE c SET c=0 WHERE c=(SELECT 1 FROM (SELECT * FROM (SELECT 1) AS v1 JOIN c HAVING c=1) AS v2 NATURAL JOIN c) ORDER BY LAST_VALUE (c2) OVER (ORDER BY c2);
      

      Leads to:

      11.1.2 3883eb63dc5e663558571c33d086c9fd3aa0cf8f (Debug)

      Core was generated by `/test/MD220623-mariadb-11.1.2-linux-x86_64-dbg/bin/mariadbd --no-defaults --cor'.
      Program terminated with signal SIGSEGV, Segmentation fault.
      #0  0x0000559a05d4c44b in Item_args::walk_args (arg=0x1505280a7a40, 
          walk_subquery=true, processor=<optimized out>, this=0x150528025b70)
          at /test/11.1_dbg/sql/item.h:2794
       
      warning: Source file is more recent than executable.
      2794	      if (args[i]->walk(processor, walk_subquery, arg))
      [Current thread is 1 (Thread 0x15059c0c5640 (LWP 2549356))]
      (gdb) bt
      #0  0x0000559a05d4c44b in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      #1  Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #2  0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #3  0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      #4  Item_func_or_sum::walk (this=0x1505280257b0, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #5  0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      #6  Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #7  0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #8  0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      #9  Item_func_or_sum::walk (this=0x1505280257b0, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #10 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      #11 Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #12 0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #13 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      #14 Item_func_or_sum::walk (this=0x1505280257b0, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #15 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      #16 Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #17 0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #18 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      #19 Item_func_or_sum::walk (this=0x1505280257b0, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #20 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      #21 Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #22 0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #23 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      ... stack looping ...
      #986 Item_func_or_sum::walk (this=0x150528025af8, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #987 0x0000559a05dc6680 in Item_ref::walk (this=0x150528032a90, processor=&virtual Item::register_field_in_read_map(void*), walk_subquery=<optimized out>, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5699
      #988 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025828) at /test/11.1_dbg/sql/item.h:2794
      #989 Item_func_or_sum::walk (this=0x1505280257b0, processor=<optimized out>, walk_subquery=true, arg=0x1505280a7a40) at /test/11.1_dbg/sql/item.h:5493
      #990 0x0000559a05d4c451 in Item_args::walk_args (arg=0x1505280a7a40, walk_subquery=true, processor=<optimized out>, this=0x150528025b70) at /test/11.1_dbg/sql/item.h:2794
      

      Bug confirmed present in:
      MariaDB: 10.4.31 (dbg), 10.4.31 (opt), 10.5.22 (dbg), 10.5.22 (opt), 10.6.15 (dbg), 10.6.15 (opt), 10.9.8 (dbg), 10.9.8 (opt), 10.10.6 (dbg), 10.10.6 (opt), 10.11.5 (dbg), 10.11.5 (opt), 11.0.3 (dbg), 11.0.3 (opt), 11.1.2 (dbg), 11.1.2 (opt)

      Bug (or feature/syntax) confirmed not present in:
      MySQL: 5.5.62 (dbg), 5.5.62 (opt), 5.6.51 (dbg), 5.6.51 (opt), 5.7.40 (dbg), 8.0.33 (dbg), 8.0.33 (opt)

      Attachments

        Issue Links

          Activity

            We saw similar stack looping with Item_ref in MDEV-22534. Maybe, this one is related.

            psergei Sergei Petrunia added a comment - We saw similar stack looping with Item_ref in MDEV-22534 . Maybe, this one is related.
            ycp Yuchen Pei added a comment - - edited

            This looks different from MDEV-22534. The cycle was both formed (1)
            and walked (2) during the final stage of exec, inside
            AGGR_OP::end_send (see below). I'm not sure what's going on, but the
            Item_field "c.c2" referenced by an Item_aggregate_ref "c.c2", in the
            Item_sum_last_value "last_value(c.c2)", in the Item_window_func
            "last_value(c.c2) over ( order by c.c2)" was substituted to be the
            Item_window_func, forming a cycle.

            11.0 be24e75229a4496e525677149c8a116a3ff4b372

            // inside AGGR_OP::end_send():
              // Update ref array
              join_tab->join->set_items_ref_array(*join_tab->ref_array); // (1)
              bool keep_last_filesort_result = join_tab->filesort ? false : true;
              if (join_tab->window_funcs_step)
              {
                if (join_tab->window_funcs_step->exec(join, keep_last_filesort_result)) // (2)
                  return NESTED_LOOP_ERROR;
              }

            When the cycle is walked:

            Item_func_or_sum::walk > Item_ref::walk > Item_args::walk_args > Item_args::walk_args > Item_func_or_sum::walk > find_all_keys > filesort > create_sort_index > Window_funcs_computation::exec > AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec > mysql_select > mysql_multi_update > mysql_parse > dispatch_command > do_command > handle_one_connection > pfs_spawn_thread

            When the cycle was formed:

            JOIN::copy_ref_ptr_array > JOIN::set_items_ref_array > AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec > mysql_select > mysql_multi_update > mysql_parse > dispatch_command > do_command > handle_one_connection > pfs_spawn_thread

            The source array was created (ref_pointer_array below) in a call
            to change_refs_to_tmp_fields, during optimize stage:

            change_refs_to_tmp_fields > JOIN::make_aggr_tables_info > JOIN::optimize_inner > JOIN::optimize > mysql_multi_update > mysql_execute_command > dispatch_command > do_command > do_handle_one_connection > pfs_spawn_thread

            11.0 be24e75229a4496e525677149c8a116a3ff4b372

            static bool
            change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
            			  List<Item> &res_selected_fields,
            			  List<Item> &res_all_fields, uint elements,
            			  List<Item> &all_fields)
            {
            //  [... 6 lines elided]
              for (i= 0; (item= it++); i++)
              {
            //  [... 10 lines elided]
                ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
                  new_item;
              }
            //  [... 7 lines elided]
            }

            All this makes it look like it's caused by different problems as the
            cycle in MDEV-22534.

            ycp Yuchen Pei added a comment - - edited This looks different from MDEV-22534 . The cycle was both formed (1) and walked (2) during the final stage of exec, inside AGGR_OP::end_send (see below). I'm not sure what's going on, but the Item_field "c.c2" referenced by an Item_aggregate_ref "c.c2", in the Item_sum_last_value "last_value(c.c2)", in the Item_window_func "last_value(c.c2) over ( order by c.c2)" was substituted to be the Item_window_func, forming a cycle. 11.0 be24e75229a4496e525677149c8a116a3ff4b372 // inside AGGR_OP::end_send(): // Update ref array join_tab->join->set_items_ref_array(*join_tab->ref_array); // (1) bool keep_last_filesort_result = join_tab->filesort ? false : true ; if (join_tab->window_funcs_step) { if (join_tab->window_funcs_step->exec(join, keep_last_filesort_result)) // (2) return NESTED_LOOP_ERROR; } When the cycle is walked: Item_func_or_sum::walk > Item_ref::walk > Item_args::walk_args > Item_args::walk_args > Item_func_or_sum::walk > find_all_keys > filesort > create_sort_index > Window_funcs_computation::exec > AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec > mysql_select > mysql_multi_update > mysql_parse > dispatch_command > do_command > handle_one_connection > pfs_spawn_thread When the cycle was formed: JOIN::copy_ref_ptr_array > JOIN::set_items_ref_array > AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec > mysql_select > mysql_multi_update > mysql_parse > dispatch_command > do_command > handle_one_connection > pfs_spawn_thread The source array was created ( ref_pointer_array below) in a call to change_refs_to_tmp_fields, during optimize stage: change_refs_to_tmp_fields > JOIN::make_aggr_tables_info > JOIN::optimize_inner > JOIN::optimize > mysql_multi_update > mysql_execute_command > dispatch_command > do_command > do_handle_one_connection > pfs_spawn_thread 11.0 be24e75229a4496e525677149c8a116a3ff4b372 static bool change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &res_selected_fields, List<Item> &res_all_fields, uint elements, List<Item> &all_fields) { // [... 6 lines elided] for (i= 0; (item= it++); i++) { // [... 10 lines elided] ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= new_item; } // [... 7 lines elided] } All this makes it look like it's caused by different problems as the cycle in MDEV-22534 .
            ycp Yuchen Pei added a comment - - edited

            A closer look

            • How the Item_aggregate_ref points to join->ref_ptrs[1]. During prepare, in window_func::split_sum_func():
              On one hand, window_func() is args[0] of the Item_window_func, which is an Item_sum_last_value, whose 0th arg becomes an Item_aggregate_ref that points to the Item_field, see below.
              On the other hand ref_ptrs.m_array[1] is assigned the Item_field in Item::split_sum_func2() (note that this points to the Item_field):

                uint el= fields.elements;
                /*
                  If this is an item_ref, get the original item
                  This is a safety measure if this is called for things that is
                  already a reference.
                */
                Item *real_itm= real_item();
                ref_pointer_array[el]= real_itm;

              On a side note, it's a bit strange that none of the branches with actual statements in the first part of Item::split_sum_func2() is entered.
              In Item::split_sum_func2(), it creates a new Item_aggregate_ref, whose ref is join->ref_ptrs.m_array + 1 (el == 1), and attach this Item_aggregate_ref to the Item_window_func's window_func()'s 0th argument (i == 0), so we have window->args[0]->args[0]->ref == join->ref_ptrs.m_array + 1

              // in JOIN::prepare()
                if (setup_without_group(thd, ref_ptrs, tables_list,
                                        select_lex->leaf_tables, fields_list,
                                        all_fields, &conds, order, group_list,
                                        select_lex->window_specs,
                                        select_lex->window_funcs,
                                        &hidden_group_fields))
              // several stacks higher
              void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
                                                    List<Item> &fields, uint flags)
              {
                for (uint i=0; i < window_func()->argument_count(); i++)
                {
                  Item **p_item= &window_func()->arguments()[i];
                  (*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags);
                }
                window_func()->setup_caches(thd);
              }
              void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
                                         List<Item> &fields, Item **ref,
                                         uint split_flags)
              {
              //  [... 87 lines elided]
                  if (!(item_ref= (new (thd->mem_root)
                                   Item_aggregate_ref(thd,
                                                      &thd->lex->current_select->context,
                                                      &ref_pointer_array[el],
                                                      null_clex_str, name))))
              //  [... 5 lines elided]
                thd->change_item_tree(ref, item_ref);
              }

              Stack:
              Item::split_sum_func2 > Item_window_func::split_sum_func > setup_order > setup_without_group > JOIN::prepare > mysql_select ...

            • How the Item_window_func becomes an element in *join_tab->ref_array. During optimization, in change_refs_to_tmp_fields(). Note that item->get_tmp_table_item(thd) returns item itself when it is an Item_window_func.

              static bool
              change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
              			  List<Item> &res_selected_fields,
              			  List<Item> &res_all_fields, uint elements,
              			  List<Item> &all_fields)
              {
                List_iterator_fast<Item> it(all_fields);
              //  [... 4 lines elided]
                uint i, border= all_fields.elements - elements;
                for (i= 0; (item= it++); i++)
                {
                  if (item->type() == Item::SUM_FUNC_ITEM && item->const_item())
                    new_item= item;
                  else
                  {
                    if (!(new_item= item->get_tmp_table_item(thd)))
                      return 1;
                  }
               
                  if (res_all_fields.push_back(new_item, thd->mem_root))
                    return 1;
                  ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
                    new_item;
                }
              //  [... 7 lines elided]
              }

              Stack:
              change_refs_to_tmp_fields > JOIN::make_aggr_tables_info > JOIN::optimize_stage2 > JOIN::optimize_inner > JOIN::optimize > mysql_select ...

            • How the cycle is formed. During exec, in AGGR_OP::end_send(),

                // Update ref array
                join_tab->join->set_items_ref_array(*join_tab->ref_array);

              So we copy from *join_tab->ref_array to join_tab->join->ref_ptrs. This causes join->ref_ptrs.m_array[1] to get overwritten by join_tab->ref_array->m_array[1]. Since window->args[0]->args[0]->ref == join->ref_ptrs.m_array + 1, this has the effect of re-pointing the ref to join_tab->ref_array->m_array[1], i.e. now we have *window->args[0]->args[0]->ref == join->ref_ptrs.m_array[1] == window, forming the cycle
              Stack:
              AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec_inner > JOIN::exec > mysql_select ...

            ycp Yuchen Pei added a comment - - edited A closer look How the Item_aggregate_ref points to join->ref_ptrs [1] . During prepare, in window_func::split_sum_func() : On one hand, window_func() is args [0] of the Item_window_func , which is an Item_sum_last_value, whose 0th arg becomes an Item_aggregate_ref that points to the Item_field , see below. On the other hand ref_ptrs.m_array [1] is assigned the Item_field in Item::split_sum_func2() (note that this points to the Item_field ): uint el= fields.elements; /* If this is an item_ref, get the original item This is a safety measure if this is called for things that is already a reference. */ Item *real_itm= real_item(); ref_pointer_array[el]= real_itm; On a side note, it's a bit strange that none of the branches with actual statements in the first part of Item::split_sum_func2() is entered. In Item::split_sum_func2() , it creates a new Item_aggregate_ref, whose ref is join->ref_ptrs.m_array + 1 ( el == 1 ), and attach this Item_aggregate_ref to the Item_window_func 's window_func() 's 0th argument ( i == 0 ), so we have window->args [0] ->args [0] ->ref == join->ref_ptrs.m_array + 1 // in JOIN::prepare() if (setup_without_group(thd, ref_ptrs, tables_list, select_lex->leaf_tables, fields_list, all_fields, &conds, order, group_list, select_lex->window_specs, select_lex->window_funcs, &hidden_group_fields)) // several stacks higher void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, uint flags) { for (uint i=0; i < window_func()->argument_count(); i++) { Item **p_item= &window_func()->arguments()[i]; (*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags); } window_func()->setup_caches(thd); } void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, Item ** ref , uint split_flags) { // [... 87 lines elided] if (!(item_ref= ( new (thd->mem_root) Item_aggregate_ref(thd, &thd->lex->current_select->context, &ref_pointer_array[el], null_clex_str, name)))) // [... 5 lines elided] thd->change_item_tree( ref , item_ref); } Stack: Item::split_sum_func2 > Item_window_func::split_sum_func > setup_order > setup_without_group > JOIN::prepare > mysql_select ... How the Item_window_func becomes an element in *join_tab->ref_array . During optimization, in change_refs_to_tmp_fields() . Note that item->get_tmp_table_item(thd) returns item itself when it is an Item_window_func . static bool change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &res_selected_fields, List<Item> &res_all_fields, uint elements, List<Item> &all_fields) { List_iterator_fast<Item> it(all_fields); // [... 4 lines elided] uint i, border= all_fields.elements - elements; for (i= 0; (item= it++); i++) { if (item->type() == Item::SUM_FUNC_ITEM && item->const_item()) new_item= item; else { if (!(new_item= item->get_tmp_table_item(thd))) return 1; }   if (res_all_fields.push_back(new_item, thd->mem_root)) return 1; ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= new_item; } // [... 7 lines elided] } Stack: change_refs_to_tmp_fields > JOIN::make_aggr_tables_info > JOIN::optimize_stage2 > JOIN::optimize_inner > JOIN::optimize > mysql_select ... How the cycle is formed. During exec, in AGGR_OP::end_send() , // Update ref array join_tab->join->set_items_ref_array(*join_tab->ref_array); So we copy from *join_tab->ref_array to join_tab->join->ref_ptrs . This causes join->ref_ptrs.m_array [1] to get overwritten by join_tab->ref_array->m_array [1] . Since window->args [0] ->args [0] ->ref == join->ref_ptrs.m_array + 1 , this has the effect of re-pointing the ref to join_tab->ref_array->m_array [1] , i.e. now we have *window->args [0] ->args [0] ->ref == join->ref_ptrs.m_array [1] == window , forming the cycle Stack: AGGR_OP::end_send > sub_select_postjoin_aggr > sub_select > do_select > JOIN::exec_inner > JOIN::exec > mysql_select ...
            ycp Yuchen Pei added a comment - - edited

            Reading the docstring of Item_window_func::split_sum_func, we understand that its window_func() returns the Item_sum_last_value. So it is the arg of Item_sum_last_value, i.e. the field that is added to ref_pointer_array, and an Item_aggregate_ref is created that points to the location in ref_pointer_array that points to the field, so that when ref_pointer_array points to temp.table fields, the ref will automatically point to the temp.table.

            However, change_to_use_tmp_fields() is never reached before the segfault in our testcase.

            The similarly named function that was reached, change_refs_to_tmp_fields(), does something similar, and it does seem to assign a ref_pointer_array item with a temp table field if the item is an Item_field:

            // inside change_refs_to_tmp_fields()
              for (i= 0; (item= it++); i++)
              {
                if (item->type() == Item::SUM_FUNC_ITEM && item->const_item())
                  new_item= item;
                else
                {
                  if (!(new_item= item->get_tmp_table_item(thd)))
                    return 1;
                }
             
                if (res_all_fields.push_back(new_item, thd->mem_root))
                  return 1;
                ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
                  new_item;
              }

            However, this function was called with ref_pointer_array=items1, rather than ref_ptrs that was used in split_sum_func(). In fact, none of the callsites of change_refs_to_tmp_fields() or change_to_use_tmp_fields() uses ref_ptrs, so the promise in the docstring is never fulfilled?

            Another finding is, if we remove the order by part in OVER, we get the same reference cycle, but not segfault because order was allocated in a different way which makes it share locations with ref_ptrs (thus order->item[0] is the Item_window_func when ref_ptrs gets updated in AGGR_OP::end_send()), and later go into a branch in Filesort::make_sortorder() that causes the sort_field->field to be NULL, which causes the walking of the infinite cycle later in register_used_fields().

            // in Window_funcs_sort::setup()
            ORDER* sort_order= concat_order_lists(thd->mem_root, 
                                                  spec->partition_list->first,
                                                  spec->order_list->first);
            if (sort_order == NULL) // No partition or order by clause.
            {
            //  [... 16 lines elided]
              order->item= (Item **)alloc_root(thd->mem_root, 2 * sizeof(Item *));
            //  [... 4 lines elided]
            }
             
            // in Filesort::make_sortorder()
              pos->field= 0; pos->item= 0;
              if (item->type() == Item::FIELD_ITEM)
                pos->field= ((Item_field*) item)->field;
              else if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item())
              {
                // Aggregate, or Item_aggregate_ref
                DBUG_ASSERT(first->type() == Item::SUM_FUNC_ITEM ||
                            (first->type() == Item::REF_ITEM &&
                             static_cast<Item_ref*>(first)->ref_type() ==
                             Item_ref::AGGREGATE_REF));
                pos->field= first->get_tmp_table_field();
              }
              else if (item->type() == Item::COPY_STR_ITEM)
              {						// Blob patch
                pos->item= ((Item_copy*) item)->get_item();
              }
              else
                pos->item= *ord->item;
             
            // in register_used_fields()
              if ((field= sort_field->field))
              {
                if (field->table == table)
                  field->register_field_in_read_map();
              }
              else
              {						// Item
                sort_field->item->walk(&Item::register_field_in_read_map, 1, table);
              }

            ycp Yuchen Pei added a comment - - edited Reading the docstring of Item_window_func::split_sum_func , we understand that its window_func() returns the Item_sum_last_value . So it is the arg of Item_sum_last_value , i.e. the field that is added to ref_pointer_array , and an Item_aggregate_ref is created that points to the location in ref_pointer_array that points to the field, so that when ref_pointer_array points to temp.table fields, the ref will automatically point to the temp.table. However, change_to_use_tmp_fields() is never reached before the segfault in our testcase. The similarly named function that was reached, change_refs_to_tmp_fields() , does something similar, and it does seem to assign a ref_pointer_array item with a temp table field if the item is an Item_field : // inside change_refs_to_tmp_fields() for (i= 0; (item= it++); i++) { if (item->type() == Item::SUM_FUNC_ITEM && item->const_item()) new_item= item; else { if (!(new_item= item->get_tmp_table_item(thd))) return 1; }   if (res_all_fields.push_back(new_item, thd->mem_root)) return 1; ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= new_item; } However, this function was called with ref_pointer_array=items1 , rather than ref_ptrs that was used in split_sum_func() . In fact, none of the callsites of change_refs_to_tmp_fields() or change_to_use_tmp_fields() uses ref_ptrs , so the promise in the docstring is never fulfilled? Another finding is, if we remove the order by part in OVER , we get the same reference cycle, but not segfault because order was allocated in a different way which makes it share locations with ref_ptrs (thus order->item [0] is the Item_window_func when ref_ptrs gets updated in AGGR_OP::end_send() ), and later go into a branch in Filesort::make_sortorder() that causes the sort_field->field to be NULL , which causes the walking of the infinite cycle later in register_used_fields() . // in Window_funcs_sort::setup() ORDER* sort_order= concat_order_lists(thd->mem_root, spec->partition_list->first, spec->order_list->first); if (sort_order == NULL) // No partition or order by clause. { // [... 16 lines elided] order->item= (Item **)alloc_root(thd->mem_root, 2 * sizeof (Item *)); // [... 4 lines elided] }   // in Filesort::make_sortorder() pos->field= 0; pos->item= 0; if (item->type() == Item::FIELD_ITEM) pos->field= ((Item_field*) item)->field; else if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item()) { // Aggregate, or Item_aggregate_ref DBUG_ASSERT(first->type() == Item::SUM_FUNC_ITEM || (first->type() == Item::REF_ITEM && static_cast <Item_ref*>(first)->ref_type() == Item_ref::AGGREGATE_REF)); pos->field= first->get_tmp_table_field(); } else if (item->type() == Item::COPY_STR_ITEM) { // Blob patch pos->item= ((Item_copy*) item)->get_item(); } else pos->item= *ord->item;   // in register_used_fields() if ((field= sort_field->field)) { if (field->table == table) field->register_field_in_read_map(); } else { // Item sort_field->item->walk(&Item::register_field_in_read_map, 1, table); }

            Please review b41ab27e20d (HEAD -> bb-10.5-midenok2, mariadb/bb-10.5-midenok2)

            midenok Aleksey Midenkov added a comment - Please review b41ab27e20d (HEAD -> bb-10.5-midenok2, mariadb/bb-10.5-midenok2)
            sanja Oleksandr Byelkin added a comment -

            All is OK except please pay attantion to test cases:
            1. there is no end of version at the and
            2. there is unneeded empty line at the end which highlited by "git citool" or "git show"

            I will fix above but next time please do it corrcectly

            sanja Oleksandr Byelkin added a comment - All is OK except please pay attantion to test cases: 1. there is no end of version at the and 2. there is unneeded empty line at the end which highlited by "git citool" or "git show" I will fix above but next time please do it corrcectly

            People

              sanja Oleksandr Byelkin
              Roel Roel Van de Paar
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.