← Back to team overview

maria-developers team mailing list archive

Re: [Commits] 5ac7ee3: MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the variable 'key_buff'

 

On Wed, May 24, 2017 at 9:45 PM, Sergey Petrunia <sergey@xxxxxxxxxxx> wrote:

> Hi Varun,
>
> Good to see that the feedback from the last time was addressed.
>
> There is another issue, though.
>
> Consider a case where some (but not necessarily all) PK columns are
> present in
> the secondary key:
>
> create table t1 (
>   pk1 VARCHAR(1000) not null,
>   pk2 INT  not null,
>   col2 INT not null,
>
>   KEY key1(pk1, col2),
>   PRIMARY KEY(pk1, pk2)
> ) CHARSET utf8 engine=innodb;
> insert into t1 select a,a,a from ten;
>
> In this case, key1 still has a suffix, but the suffix only includes columns
> that are not already present in the key.
> That is, the "real" definition of key1 is
>
>   (pk1, col2, pk2)
>
> and not
>
>   (pk1, col2, pk1, pk2)
> .
>
> Let's try that.
>
> explain
> select * from t1 force index(key1) where pk1 < 'zz' and col2 <=2 and pk2
> <=4;
>
> Without the patch, I have all 3 key parts used:
>
> | {
>   "query_block": {
>     "select_id": 1,
>     "table": {
>       "table_name": "t1",
>       "access_type": "range",
>       "possible_keys": ["key1"],
>       "key": "key1",
>       "key_length": "3010",
>       "used_key_parts": ["pk1", "col2", "pk2"],
>       "rows": 1,
>       "filtered": 100,
>       "attached_condition": "t1.pk1 <= '0' and t1.col2 <= 2 and t1.pk2 <=
> 4",
>       "using_index": true
>     }
>   }
> } |
>
> With the patch, I only see the explicitly defined key parts used:
>
> | {
>   "query_block": {
>     "select_id": 1,
>     "table": {
>       "table_name": "t1",
>       "access_type": "range",
>       "possible_keys": ["key1"],
>       "key": "key1",
>       "key_length": "3006",
>       "used_key_parts": ["pk1", "col2"],
>       "rows": 1,
>       "filtered": 100,
>       "attached_condition": "t1.pk1 <= '0' and t1.col2 <= 2 and t1.pk2 <=
> 4",
>       "using_index": true
>     }
>   }
> } |
>
> Which I think is wrong, because the key length never exceeds MAX_KEY_LEN.
>
> This can be easily fixed by appyling this patch over your patch:
>
> --- table.cc.varun      2017-05-24 18:30:39.076341082 +0300
> +++ table.cc    2017-05-24 19:04:25.637740027 +0300
> @@ -2164,10 +2164,13 @@
>            for (i= 0; i < add_keyparts_for_this_key; i++)
>            {
>              uint pk_part_length= key_first_info->key_part[i].
> store_length;
> -            if (ext_key_length + pk_part_length > MAX_KEY_LENGTH)
> +            if (keyinfo->ext_key_part_map & 1<<i)
>              {
> -              add_keyparts_for_this_key= i;
> -              break;
> +              if (ext_key_length + pk_part_length > MAX_KEY_LENGTH)
> +              {
> +                add_keyparts_for_this_key= i;
> +                break;
> +              }
>              }
>              ext_key_length+= pk_part_length;
>            }
>
> Do you agree with this change? Any amendments? If it's ok, please add this
> (together with the testcase) into your patch.


Yes I agree with the above change. I have added the testcase to the patch
and would be sending it in another commit.

>
>
> I've checked MySQL 5.7 also, and they do something weird:
> ...
> insert into t1 select A.a + 1000*B.a, A.a + 1000*B.a, A.a + 1000*B.a from
> one_k A, ten B;
> explain format=json
> select * from t1 force index(key1) where pk1 <= '0' and col2 <=2 and pk2
> <=4;
> EXPLAIN
> {
>   "query_block": {
>     "select_id": 1,
>     "cost_info": {
>       "query_cost": "1.41"
>     },
>     "table": {
>       "table_name": "t1",
>       "access_type": "range",
>       "possible_keys": [
>         "key1"
>       ],
>       "key": "key1",
>       "used_key_parts": [
>         "pk1"
>       ],
>       "key_length": "3010",
>       "rows_examined_per_scan": 1,
>       "rows_produced_per_join": 0,
>       "filtered": "11.11",
>       "using_index": true,
>       "cost_info": {
>         "read_cost": "1.39",
>         "eval_cost": "0.02",
>         "prefix_cost": "1.41",
>         "data_read_per_join": "335"
>       },
>       "used_columns": [
>         "pk1",
>         "pk2",
>         "col2"
>       ],
>       "attached_condition": "((`test`.`t1`.`pk1` <= '0') and
> (`test`.`t1`.`col2` <= 2) and (`test`.`t1`.`pk2` <= 4))"
>     }
>   }
> }
>
> key_length is 3010, but used_key_parts only includes "pk1"... The numbers
> do
> not match.
>
> On Tue, May 23, 2017 at 07:08:48PM +0530, Varun wrote:
> > revision-id: 5ac7ee32a8e373d989bf9d665b5e01f770ac583f
> (mariadb-10.1.20-282-g5ac7ee3)
> > parent(s): a1b6128dedb4419db9fadaf94c356d3477d4e06f
> > author: Varun Gupta
> > committer: Varun Gupta
> > timestamp: 2017-05-23 19:00:15 +0530
> > message:
> >
> > MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the variable
> 'key_buff'
> > was corrupted, server crashes in opt_sum_query
> >
> > extended keys feature disabled if the length of extended key is longer
> than MAX_KEY_LEN
> >
> > ---
> >  mysql-test/r/innodb_ext_key.result | 51 ++++++++++++++++++++++++++++++
> ++++++++
> >  mysql-test/t/innodb_ext_key.test   | 38 ++++++++++++++++++++++++++++
> >  sql/table.cc                       | 36 ++++++++++++++++++++++++++-
> >  3 files changed, 124 insertions(+), 1 deletion(-)
> >
> > diff --git a/mysql-test/r/innodb_ext_key.result
> b/mysql-test/r/innodb_ext_key.result
> > index 1305be8..c76660c 100644
> > --- a/mysql-test/r/innodb_ext_key.result
> > +++ b/mysql-test/r/innodb_ext_key.result
> > @@ -1133,5 +1133,56 @@ where index_date_updated= 10 and index_id < 800;
> >  id   select_type     table   type    possible_keys   key     key_len
> ref     rows    Extra
> >  1    SIMPLE  t2      range   index_date_updated
> index_date_updated      13      NULL    #       Using index condition
> >  drop table t0,t1,t2;
> > +#
> > +# MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the
> variable 'key_buff'
> > +# was corrupted, server crashes in opt_sum_query
> > +set @save_innodb_file_format= @@innodb_file_format;
> > +set @save_innodb_large_prefix= @@innodb_large_prefix;
> > +set global innodb_file_format = BARRACUDA;
> > +set global innodb_large_prefix = ON;
> > +CREATE TABLE t1 (
> > +pk INT,
> > +f1 VARCHAR(3),
> > +f2 VARCHAR(1024),
> > +PRIMARY KEY (pk),
> > +KEY(f2)
> > +) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
> > +INSERT INTO t1 VALUES (1,'foo','abc'),(2,'bar','def');
> > +SELECT MAX(t2.pk) FROM t1 t2 INNER JOIN t1 t3 ON t2.f1 = t3.f1 WHERE
> t2.pk <= 4;
> > +MAX(t2.pk)
> > +2
> > +drop table t1;
> > +CREATE TABLE t1 (
> > +pk1 INT,
> > +pk2 INT,
> > +f1 VARCHAR(3),
> > +f2 VARCHAR(1021),
> > +PRIMARY KEY (pk1,pk2),
> > +KEY(f2)
> > +) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
> > +INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
> > +explain format= json
> > +select * from t1 force index(f2)  where pk1 <= 5 and pk2 <=5 and f2 =
> 'abc' and f1 <= '3';
> > +EXPLAIN
> > +{
> > +  "query_block": {
> > +    "select_id": 1,
> > +    "table": {
> > +      "table_name": "t1",
> > +      "access_type": "range",
> > +      "possible_keys": ["f2"],
> > +      "key": "f2",
> > +      "key_length": "3070",
> > +      "used_key_parts": ["f2", "pk1"],
> > +      "rows": 1,
> > +      "filtered": 100,
> > +      "index_condition": "((t1.pk1 <= 5) and (t1.pk2 <= 5) and (t1.f2 =
> 'abc'))",
> > +      "attached_condition": "(t1.f1 <= '3')"
> > +    }
> > +  }
> > +}
> > +drop table t1;
> >  set optimizer_switch=@save_ext_key_optimizer_switch;
> > +set global innodb_file_format = @save_innodb_file_format;
> > +set global innodb_large_prefix = @save_innodb_large_prefix;
> >  SET SESSION STORAGE_ENGINE=DEFAULT;
> > diff --git a/mysql-test/t/innodb_ext_key.test
> b/mysql-test/t/innodb_ext_key.test
> > index bf94b7d..adb032f 100644
> > --- a/mysql-test/t/innodb_ext_key.test
> > +++ b/mysql-test/t/innodb_ext_key.test
> > @@ -778,5 +778,43 @@ where index_date_updated= 10 and index_id < 800;
> >
> >  drop table t0,t1,t2;
> >
> > +
> > +--echo #
> > +--echo # MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the
> variable 'key_buff'
> > +--echo # was corrupted, server crashes in opt_sum_query
> > +
> > +set @save_innodb_file_format= @@innodb_file_format;
> > +set @save_innodb_large_prefix= @@innodb_large_prefix;
> > +set global innodb_file_format = BARRACUDA;
> > +set global innodb_large_prefix = ON;
> > +
> > +CREATE TABLE t1 (
> > +  pk INT,
> > +  f1 VARCHAR(3),
> > +  f2 VARCHAR(1024),
> > +  PRIMARY KEY (pk),
> > +  KEY(f2)
> > +) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
> > +
> > +INSERT INTO t1 VALUES (1,'foo','abc'),(2,'bar','def');
> > +SELECT MAX(t2.pk) FROM t1 t2 INNER JOIN t1 t3 ON t2.f1 = t3.f1 WHERE
> t2.pk <= 4;
> > +drop table t1;
> > +
> > +CREATE TABLE t1 (
> > +  pk1 INT,
> > +  pk2 INT,
> > +  f1 VARCHAR(3),
> > +  f2 VARCHAR(1021),
> > +  PRIMARY KEY (pk1,pk2),
> > +  KEY(f2)
> > +) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
> > +
> > +INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
> > +explain format= json
> > +select * from t1 force index(f2)  where pk1 <= 5 and pk2 <=5 and f2 =
> 'abc' and f1 <= '3';
> > +drop table t1;
> > +
> >  set optimizer_switch=@save_ext_key_optimizer_switch;
> > +set global innodb_file_format = @save_innodb_file_format;
> > +set global innodb_large_prefix = @save_innodb_large_prefix;
> >  SET SESSION STORAGE_ENGINE=DEFAULT;
> > diff --git a/sql/table.cc b/sql/table.cc
> > index 37c0b63..c8c9696 100644
> > --- a/sql/table.cc
> > +++ b/sql/table.cc
> > @@ -1721,6 +1721,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD
> *thd, bool write,
> >      keyinfo= share->key_info;
> >      uint primary_key= my_strcasecmp(system_charset_info,
> share->keynames.type_names[0],
> >                                      primary_key_name) ? MAX_KEY : 0;
> > +    KEY* key_first_info;
> >
> >      if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME)
> >      {
> > @@ -1800,19 +1801,38 @@ int TABLE_SHARE::init_from_binary_frm_image(THD
> *thd, bool write,
> >                 keyinfo->name_length+1);
> >        }
> >
> > +      if (!key)
> > +        key_first_info= keyinfo;
> > +
> >        if (ext_key_parts > share->key_parts && key)
> >        {
> >          KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
> >                                       (keyinfo-1)->ext_key_parts;
> >          uint add_keyparts_for_this_key= add_first_key_parts;
> > +        uint length_bytes= 0, len_null_byte= 0, ext_key_length= 0;
> > +        Field *field;
> >
> >          /*
> >            Do not extend the key that contains a component
> >            defined over the beginning of a field.
> >       */
> >          for (i= 0; i < keyinfo->user_defined_key_parts; i++)
> > -     {
> > +        {
> >            uint fieldnr= keyinfo->key_part[i].fieldnr;
> > +          field= share->field[keyinfo->key_part[i].fieldnr-1];
> > +
> > +          if (field->null_ptr)
> > +            len_null_byte= HA_KEY_NULL_LENGTH;
> > +
> > +          if (field->type() == MYSQL_TYPE_BLOB ||
> > +             field->real_type() == MYSQL_TYPE_VARCHAR ||
> > +             field->type() == MYSQL_TYPE_GEOMETRY)
> > +          {
> > +            length_bytes= HA_KEY_BLOB_LENGTH;
> > +          }
> > +
> > +          ext_key_length+= keyinfo->key_part[i].length + len_null_byte
> > +                            + length_bytes;
> >            if (share->field[fieldnr-1]->key_length() !=
> >                keyinfo->key_part[i].length)
> >         {
> > @@ -1821,6 +1841,20 @@ int TABLE_SHARE::init_from_binary_frm_image(THD
> *thd, bool write,
> >            }
> >          }
> >
> > +        if (add_keyparts_for_this_key)
> > +        {
> > +          for (i= 0; i < add_keyparts_for_this_key; i++)
> > +          {
> > +            uint pk_part_length= key_first_info->key_part[i].
> store_length;
> > +            if (ext_key_length + pk_part_length > MAX_KEY_LENGTH)
> > +            {
> > +              add_keyparts_for_this_key= i;
> > +              break;
> > +            }
> > +            ext_key_length+= pk_part_length;
> > +          }
> > +        }
> > +
> >          if (add_keyparts_for_this_key < (keyinfo->ext_key_parts -
> >                                          keyinfo->user_defined_key_
> parts))
> >       {
> > _______________________________________________
> > commits mailing list
> > commits@xxxxxxxxxxx
> > https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
>
> --
> BR
>  Sergei
> --
> Sergei Petrunia, Software Developer
> MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
>
>
>

References