Hi, Oleksandr! A couple of minor comments, see below On Jul 15, Oleksandr Byelkin wrote:
revision-id: d7b274fa257 (mariadb-10.2.25-63-gd7b274fa257) parent(s): 64900e3d7c3 author: Oleksandr Byelkin <sanja@mariadb.com> committer: Oleksandr Byelkin <sanja@mariadb.com> timestamp: 2019-07-11 13:29:51 +0200 message:
MDEV-16932: ASAN heap-use-after-free in my_charlen_utf8 / my_well_formed_char_length_utf8 on 2nd execution of SP with ALTER trying to add bad CHECK
Make automatic name generation during execution (not prepare).
Check result of memory allocation operation.
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 043cfaaaaaa..636fb9f427c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6205,6 +6210,10 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) Virtual_column_info *check; TABLE_SHARE *share= table->s; uint c; + + if (fix_constraints_names(thd, &alter_info->check_constraint_list)) + DBUG_RETURN(TRUE);
It's not very logical, I think, to generate constraint names in handle_if_exists_options(), I'd rather move it out and put directly in mysql_alter_table().
+ while ((check=it++)) { if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) && @@ -6232,10 +6241,44 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) } }
- DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); }
+static bool fix_constraints_names(THD *thd, List<Virtual_column_info> + *check_constraint_list) +{ + List_iterator<Virtual_column_info> it((*check_constraint_list)); + Virtual_column_info *check; + uint nr= 1; + DBUG_ENTER("fix_constraints_names"); + if (!check_constraint_list) + DBUG_RETURN(FALSE); + // Prevent accessing freed memory during generating unique names + while ((check=it++)) + { + if (check->automatic_name) + { + check->name.str= NULL; + check->name.length= 0; + } + } + it.rewind(); + // Grnerate unique names if needed
typo
+ while ((check=it++)) + { + if (!check->name.length) + { + check->automatic_name= TRUE; + if (make_unique_constraint_name(thd, &check->name, + check_constraint_list, + &nr)) + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE);
I would remove the first while() at all and just do the second one till the end: bool ret= FALSE; while ((check=it++)) if (check->automatic_name) ret |= make_unique_constraint_name(...); return ret; Because thd->strmake() basically never fails, it doesn't make much sense to optimize for a case when it does. This code above assumes you set automatic_name=TRUE in mysql_prepare_create_table().
+} + /** Get Create_field object for newly created table by field index.
Regards, Sergei