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

Server crashes in Charset::set_charset upon SELECT

Details

    Description

      CREATE TABLE t1 (a VARBINARY(8));
      INSERT INTO t1 VALUES ('foo'),('bar');
      CREATE TABLE t2 (b VARBINARY(8));
       
      SET SESSION optimizer_switch= 'condition_pushdown_from_having=off';
       
      SELECT a FROM t1 GROUP BY a HAVING (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2);
       
      # Cleanup
      DROP TABLE t1;
      

      10.4 d67e17bb

      #3  <signal handler called>
      #4  0x000056032fcef510 in Charset::set_charset (this=0x7feec8019428, other=...) at /data/src/10.4/sql/sql_string.h:165
      #5  0x000056032fcef663 in String::copy (this=0x7feec8019428, s=...) at /data/src/10.4/sql/sql_string.h:827
      #6  0x00005603300ffc09 in Item_cache_str::cache_value (this=0x7feec8019328) at /data/src/10.4/sql/item.cc:10128
      #7  0x00005603301004c1 in Item_cache_row::cache_value (this=0x7feec8018db0) at /data/src/10.4/sql/item.cc:10247
      #8  0x000056033011015e in Item_in_optimizer::val_int (this=0x7feec8018cd0) at /data/src/10.4/sql/item_cmpfunc.cc:1557
      #9  0x000056032ff807e3 in Type_handler_int_result::Item_val_bool (this=0x5603312ebc38 <type_handler_bool>, item=0x7feec8018cd0) at /data/src/10.4/sql/sql_type.cc:4514
      #10 0x000056032fc2f420 in Item::val_bool (this=0x7feec8018cd0) at /data/src/10.4/sql/item.h:1465
      #11 0x000056032fdf221f in Item::eval_const_cond (this=0x7feec8018cd0) at /data/src/10.4/sql/item.h:1473
      #12 0x000056032fdcfaaa in Item::remove_eq_conds (this=0x7feec8018cd0, thd=0x7feec8000d90, cond_value=0x7feeda474214, top_level_arg=false) at /data/src/10.4/sql/sql_select.cc:17438
      #13 0x000056032fdcf1c1 in Item_cond::remove_eq_conds (this=0x7feec80172b8, thd=0x7feec8000d90, cond_value=0x7feec80180b4, top_level_arg=true) at /data/src/10.4/sql/sql_select.cc:17261
      #14 0x000056032fdce845 in optimize_cond (join=0x7feec8017da0, conds=0x7feec80172b8, join_list=0x7feec8013740, ignore_on_conds=true, cond_value=0x7feec80180b4, cond_equal=0x7feec80181e0, flags=0) at /data/src/10.4/sql/sql_select.cc:16988
      #15 0x000056032fda349b in JOIN::optimize_inner (this=0x7feec8017da0) at /data/src/10.4/sql/sql_select.cc:2027
      #16 0x000056032fda1c77 in JOIN::optimize (this=0x7feec8017da0) at /data/src/10.4/sql/sql_select.cc:1619
      #17 0x000056032fdad15f in mysql_select (thd=0x7feec8000d90, tables=0x7feec8013b48, wild_num=0, fields=..., conds=0x0, og_num=1, order=0x0, group=0x7feec8014338, having=0x7feec80172b8, proc_param=0x0, select_options=2147748608, result=0x7feec8017d78, unit=0x7feec8004cc0, select_lex=0x7feec8013580) at /data/src/10.4/sql/sql_select.cc:4685
      #18 0x000056032fd9cbd5 in handle_select (thd=0x7feec8000d90, lex=0x7feec8004c00, result=0x7feec8017d78, setup_tables_done_option=0) at /data/src/10.4/sql/sql_select.cc:410
      #19 0x000056032fd61cf4 in execute_sqlcom_select (thd=0x7feec8000d90, all_tables=0x7feec8013b48) at /data/src/10.4/sql/sql_parse.cc:6417
      #20 0x000056032fd58273 in mysql_execute_command (thd=0x7feec8000d90) at /data/src/10.4/sql/sql_parse.cc:3936
      #21 0x000056032fd65d19 in mysql_parse (thd=0x7feec8000d90, rawbuf=0x7feec8013458 "SELECT a FROM t1 GROUP BY a HAVING (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2)", length=98, parser_state=0x7feeda475550, is_com_multi=false, is_next_command=false) at /data/src/10.4/sql/sql_parse.cc:7958
      #22 0x000056032fd52041 in dispatch_command (command=COM_QUERY, thd=0x7feec8000d90, packet=0x7feec80087b1 "SELECT a FROM t1 GROUP BY a HAVING (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2)", packet_length=98, is_com_multi=false, is_next_command=false) at /data/src/10.4/sql/sql_parse.cc:1855
      #23 0x000056032fd508a9 in do_command (thd=0x7feec8000d90) at /data/src/10.4/sql/sql_parse.cc:1373
      #24 0x000056032fedfb93 in do_handle_one_connection (connect=0x5603324040f0) at /data/src/10.4/sql/sql_connect.cc:1412
      #25 0x000056032fedf8dc in handle_one_connection (arg=0x5603324040f0) at /data/src/10.4/sql/sql_connect.cc:1316
      #26 0x0000560330900768 in pfs_spawn_thread (arg=0x560332351920) at /data/src/10.4/storage/perfschema/pfs.cc:1869
      #27 0x00007feee0d26609 in start_thread (arg=<optimized out>) at pthread_create.c:477
      #28 0x00007feee0591293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
      

      EXPLAIN crashes the same way.

      Reproducible on 10.4-10.6, debug and non-debug alike; also on older versions of 10.4 and 10.5.
      Reproducible with at least MyISAM, InnoDB, Aria.
      Couldn't reproduce on 10.3.
      Couldn't reproduce with data in t2.
      Couldn't reproduce with the default optimizer switch (condition_pushdown_from_having=on).

      Attachments

        Activity

          varun Varun Gupta (Inactive) added a comment - Patch http://lists.askmonty.org/pipermail/commits/2021-January/014439.html

          It looks like that Item_cache_row::bring_value() has lines that can be removed without causing any problem:

          --- a/sql/item.cc
          +++ b/sql/item.cc
          @@ -10298,8 +10298,6 @@ void Item_cache_row::bring_value()
               return;
             example->bring_value();
             null_value= example->null_value;
          -  for (uint i= 0; i < item_count; i++)
          -    values[i]->bring_value();
           }
          

          They became unneeded after the following commit:

          commit da7646b64c7e76930e05c0235c724872416ba1dc
          Author:	Alexey Kopytov <Alexey.Kopytov@Sun.com>  Thu Sep  9 07:44:53 2010
          Committer:	Alexey Kopytov <Alexey.Kopytov@Sun.com>  Thu Sep  9 07:44:53 2010
          

          igor Igor Babaev (Inactive) added a comment - It looks like that Item_cache_row::bring_value() has lines that can be removed without causing any problem: --- a/sql/item.cc +++ b/sql/item.cc @@ -10298,8 +10298,6 @@ void Item_cache_row::bring_value() return; example->bring_value(); null_value= example->null_value; - for (uint i= 0; i < item_count; i++) - values[i]->bring_value(); } They became unneeded after the following commit: commit da7646b64c7e76930e05c0235c724872416ba1dc Author: Alexey Kopytov <Alexey.Kopytov@Sun.com> Thu Sep 9 07:44:53 2010 Committer: Alexey Kopytov <Alexey.Kopytov@Sun.com> Thu Sep 9 07:44:53 2010

          Interesting that the following patch also fixes the problem:

          --- a/sql/item_subselect.cc
          +++ b/sql/item_subselect.cc
          @@ -1308,8 +1308,18 @@ bool Item_singlerow_subselect::null_inside()
           
           void Item_singlerow_subselect::bring_value()
           {
          +  if (!unit->uncacheable && engine->is_executed())
          +    return;
             if (!exec() && assigned())
          +  {
               null_value= 0;
          +    if (!unit->uncacheable)
          +    {
          +      null_value= 1;
          +      for (uint i= 0; i < max_columns ;i++)
          +       null_value&= row[i]->null_value;
          +    }
          +  }
             else
               reset();
           }
          

          This patch does not call exec() for a cacheable subquery if it has been already executed.

          It looks like we still have some inconsistency in how we set null_value flags for Item_row objects and Item_cache_row objects

          igor Igor Babaev (Inactive) added a comment - Interesting that the following patch also fixes the problem: --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1308,8 +1308,18 @@ bool Item_singlerow_subselect::null_inside() void Item_singlerow_subselect::bring_value() { + if (!unit->uncacheable && engine->is_executed()) + return; if (!exec() && assigned()) + { null_value= 0; + if (!unit->uncacheable) + { + null_value= 1; + for (uint i= 0; i < max_columns ;i++) + null_value&= row[i]->null_value; + } + } else reset(); } This patch does not call exec() for a cacheable subquery if it has been already executed. It looks like we still have some inconsistency in how we set null_value flags for Item_row objects and Item_cache_row objects
          varun Varun Gupta (Inactive) added a comment - Made a couple of patch With the help of monty . Patch1: https://github.com/MariaDB/server/commit/d6782fcded7cfe5f561359124153d5deaba28ec9 Patch2: https://github.com/MariaDB/server/commit/a87320e335b6345f1ce04eea8b790a741c1ec23e

          I continued investigating the problem and came to the conclusion that it is a result of different interpretations of the null_value flag for row values. Some code assumes that the flag null_value for a row is set to true if every component of the row has null_value flag set to true (see, for example, the code that compares two row values). In other pieces of code the flag is set to true if there is a component with its null_value flag set to true:

          bool Item_cache_row::cache_value()
          {
            if (!example)
              return FALSE;
            value_cached= TRUE;
            null_value= 0;
            example->bring_value();
            for (uint i= 0; i < item_count; i++)
            {
              values[i]->cache_value();
              null_value|= values[i]->null_value;
            }
            return TRUE;
          } 
          

          Another function that uses such interpretation of the null_value flag in row items is Item_in_optimizer::val_int().
          In the following patch I tried to make sure that we use only the first interpretation of the null_value flag in row item (it seems to me that was the original one).

          diff --git a/sql/item.cc b/sql/item.cc
          index e633964..36b7a3e 100644
          --- a/sql/item.cc
          +++ b/sql/item.cc
          @@ -10218,12 +10218,13 @@ bool Item_cache_row::cache_value()
             if (!example)
               return FALSE;
             value_cached= TRUE;
          -  null_value= 0;
             example->bring_value();
          +  null_value= 1;
             for (uint i= 0; i < item_count; i++)
             {
          +    values[i]->bring_value();
               values[i]->cache_value();
          -    null_value|= values[i]->null_value;
          +    null_value&= values[i]->null_value;
             }
             return TRUE;
           }
          diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
          index d16c741..ecaeb39 100644
          --- a/sql/item_cmpfunc.cc
          +++ b/sql/item_cmpfunc.cc
          @@ -1596,7 +1596,7 @@ longlong Item_in_optimizer::val_int()
               DBUG_RETURN(res);
             }
           
          -  if (cache->null_value)
          +  if (cache->null_value || cache->null_inside())
             {
                DBUG_PRINT("info", ("Left NULL..."));
               /*
          diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
          index 802bfca..56b3b1b 100644
          --- a/sql/item_subselect.cc
          +++ b/sql/item_subselect.cc
          @@ -831,7 +831,8 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
           
           inline bool Item_in_subselect::left_expr_has_null()
           {
          -  return (*(optimizer->get_cache()))->null_value;
          +  return (*(optimizer->get_cache()))->null_value ||
          +    (*(optimizer->get_cache()))->null_inside();
           }
           
           
          @@ -1299,7 +1300,17 @@ bool Item_singlerow_subselect::null_inside()
           void Item_singlerow_subselect::bring_value()
           {
             if (!exec() && assigned())
          -    null_value= 0;
          +  {
          +    null_value= 1;
          +    for (uint i= 0; i < max_columns ; i++)
          +    {
          +      if (!row[i]->null_value)
          +      {
          +        null_value= 0;
          +        return;
          +      }
          +    }
          +  }
             else
               reset();
           }
          

          All tests passed with this patch (as well as the reported test case of course). Yet I'm not sure that I covered all pieces of code where the interpretations of the null_value flag in rows differed.

          igor Igor Babaev (Inactive) added a comment - I continued investigating the problem and came to the conclusion that it is a result of different interpretations of the null_value flag for row values. Some code assumes that the flag null_value for a row is set to true if every component of the row has null_value flag set to true (see, for example, the code that compares two row values). In other pieces of code the flag is set to true if there is a component with its null_value flag set to true: bool Item_cache_row::cache_value() { if (!example) return FALSE; value_cached= TRUE; null_value= 0; example->bring_value(); for (uint i= 0; i < item_count; i++) { values[i]->cache_value(); null_value|= values[i]->null_value; } return TRUE; } Another function that uses such interpretation of the null_value flag in row items is Item_in_optimizer::val_int(). In the following patch I tried to make sure that we use only the first interpretation of the null_value flag in row item (it seems to me that was the original one). diff --git a/sql/item.cc b/sql/item.cc index e633964..36b7a3e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10218,12 +10218,13 @@ bool Item_cache_row::cache_value() if (!example) return FALSE; value_cached= TRUE; - null_value= 0; example->bring_value(); + null_value= 1; for (uint i= 0; i < item_count; i++) { + values[i]->bring_value(); values[i]->cache_value(); - null_value|= values[i]->null_value; + null_value&= values[i]->null_value; } return TRUE; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d16c741..ecaeb39 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1596,7 +1596,7 @@ longlong Item_in_optimizer::val_int() DBUG_RETURN(res); } - if (cache->null_value) + if (cache->null_value || cache->null_inside()) { DBUG_PRINT("info", ("Left NULL...")); /* diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 802bfca..56b3b1b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -831,7 +831,8 @@ bool Item_subselect::expr_cache_is_needed(THD *thd) inline bool Item_in_subselect::left_expr_has_null() { - return (*(optimizer->get_cache()))->null_value; + return (*(optimizer->get_cache()))->null_value || + (*(optimizer->get_cache()))->null_inside(); } @@ -1299,7 +1300,17 @@ bool Item_singlerow_subselect::null_inside() void Item_singlerow_subselect::bring_value() { if (!exec() && assigned()) - null_value= 0; + { + null_value= 1; + for (uint i= 0; i < max_columns ; i++) + { + if (!row[i]->null_value) + { + null_value= 0; + return; + } + } + } else reset(); } All tests passed with this patch (as well as the reported test case of course). Yet I'm not sure that I covered all pieces of code where the interpretations of the null_value flag in rows differed.

          People

            varun Varun Gupta (Inactive)
            elenst Elena Stepanova
            Votes:
            0 Vote for this issue
            Watchers:
            4 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.