← Back to team overview

maria-developers team mailing list archive

Re: 3e01da668d1: MDEV-21785: sequences used as default by other table not dumped in right order by mysqldump

 

Hi, Oleksandr!

On Jan 21, Oleksandr Byelkin wrote:
> revision-id: 3e01da668d1 (mariadb-10.3.26-64-g3e01da668d1)
> parent(s): 59998d3480f
> author: Oleksandr Byelkin <sanja@xxxxxxxxxxx>
> committer: Oleksandr Byelkin <sanja@xxxxxxxxxxx>
> timestamp: 2021-01-14 10:45:51 +0100
> message:
> 
> MDEV-21785: sequences used as default by other table not dumped in right order by mysqldump
> 
> Dump sequences first.
> 
> This atch made to keep it small and
> to keep number of queries to the server the same.
> 
> Order of tables in a dump can not be changed
> (except sequences first) because mysql_list_tables
> uses SHOW TABLES and I used SHOW FULL TABLES.

it looks like a rather overengineered implementation. I'd suggest to
simplify it as:

> diff --git a/client/mysqldump.c b/client/mysqldump.c
> index a964f96437d..17837713c01 100644
> --- a/client/mysqldump.c
> +++ b/client/mysqldump.c
> @@ -42,6 +42,11 @@
>  /* on merge conflict, bump to a higher version again */
>  #define DUMP_VERSION "10.19"
>  
> +/**
> +  First mysql version supporting sequences.
> +*/
> +#define FIRST_SEQUENCE_VERSION 100300

seems unnecessary, you implemented SHOW FULL TABLES
in 5.0.2-alpha. Oct 2004.

You can safely use SHOW FULL TABLES unconditionally.

> +
>  #include <my_global.h>
>  #include <my_sys.h>
>  #include <my_user.h>
> @@ -92,6 +97,13 @@
>  /* Max length GTID position that we will output. */
>  #define MAX_GTID_LENGTH 1024
>  
> +
> +/* Dump sequence/tables control */
> +#define DUMP_TABLE_TABLE 1
> +#define DUMP_TABLE_SEQUENCE 2
> +#define DUMP_TABLE_ALL (DUMP_TABLE_TABLE | DUMP_TABLE_SEQUENCE)
> +
> +
>  static my_bool ignore_table_data(const uchar *hash_key, size_t len);
>  static void add_load_option(DYNAMIC_STRING *str, const char *option,
>                               const char *option_value);
> @@ -171,6 +183,7 @@ static DYNAMIC_STRING dynamic_where;
>  static MYSQL_RES *get_table_name_result= NULL;
>  static MEM_ROOT glob_root;
>  static MYSQL_RES *routine_res, *routine_list_res;
> +static int get_table_name_result_short= 1;

that'll be unnecessary

>  
>  
>  #include <sslopt-vars.h>
> @@ -3853,13 +3866,16 @@ static char *alloc_query_str(size_t size)
>    ARGS
>     table - table name
>     db    - db name
> +   type_ctrl - dump table or sequences or both
>  
>     RETURNS
>      void
>  */
>  
>  
> -static void dump_table(const char *table, const char *db, const uchar *hash_key, size_t len)
> +static void dump_table(const char *table, const char *db,
> +                       const uchar *hash_key, size_t len,
> +                       const uint type_ctrl)
>  {
>    char ignore_flag;
>    char buf[200], table_buff[NAME_LEN+3];
> @@ -3881,9 +3897,11 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
>    */
>    if (check_if_ignore_table(table, table_type) & IGNORE_SEQUENCE_TABLE)
>    {
> -    get_sequence_structure(table, db);
> +    if (type_ctrl & DUMP_TABLE_SEQUENCE)
> +      get_sequence_structure(table, db);
> +    DBUG_VOID_RETURN;
> +  } else if (!(type_ctrl & DUMP_TABLE_TABLE))
>      DBUG_VOID_RETURN;
> -  }

here I'd remove the whole if(). Let's say the convention is that
dump_table() should never be invoked for sequences, it's the caller's
job to ensure it.

>    /*
>      Make sure you get the create table info before the following check for
>      --no-data flag below. Otherwise, the create table info won't be printed.
> @@ -4368,18 +4386,49 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
>  } /* dump_table */
>  
>  
> -static char *getTableName(int reset)
> +static char *getTableName(int reset, uint table_control)
>  {
>    MYSQL_ROW row;
>  
>    if (!get_table_name_result)
>    {
> -    if (!(get_table_name_result= mysql_list_tables(mysql,NullS)))
> -      return(NULL);
> +    if (mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION)
> +    {
> +      const char *query=
> +        "SHOW FULL TABLES";
> +
> +      if (mysql_query_with_error_report(mysql, 0, query))
> +        return (NULL);
> +
> +      if (!(get_table_name_result= mysql_store_result(mysql)))
> +        return (NULL);
> +
> +      get_table_name_result_short= 0;
> +    }
> +    else
> +    {
> +      if (!(get_table_name_result= mysql_list_tables(mysql,NullS)))
> +        return(NULL);
> +      get_table_name_result_short= 1;
> +    }

this will be twice as small, if you won't try to support before-5.0
servers.

>    }
>    if ((row= mysql_fetch_row(get_table_name_result)))
> -    return((char*) row[0]);
> -
> +  {
> +    int sequence;
> +    if (get_table_name_result_short ||
> +        table_control == DUMP_TABLE_ALL)
> +      return((char*) row[0]);
> +    while (row &&
> +             (((sequence= (strcmp(row[1], "SEQUENCE") == 0)) &&
> +               (table_control & DUMP_TABLE_TABLE)) ||
> +              ((!sequence) &&
> +               (table_control & DUMP_TABLE_SEQUENCE))))

I'd rather just do

   static char *getTableName(int reset, uint want_sequence)

and

   while (row && strcmp(row[1], "SEQUENCE") == want_sequence)
     row= mysql_fetch_row(get_table_name_result);

> +    {
> +      row= mysql_fetch_row(get_table_name_result);
> +    }
> +    if (row)
> +      return((char*) row[0]);
> +  }
>    if (reset)
>      mysql_data_seek(get_table_name_result,0);      /* We want to read again */
>    else
> @@ -4785,17 +4834,17 @@ static int dump_all_stats()
>    /* EITS added in 10.0.1 */
>    if (mysql_get_server_version(mysql) >= 100001)
>    {
> -    dump_table("column_stats", "mysql", NULL, 0);
> -    dump_table("index_stats", "mysql", NULL, 0);
> -    dump_table("table_stats", "mysql", NULL, 0);
> +    dump_table("column_stats", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +    dump_table("index_stats", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +    dump_table("table_stats", "mysql", NULL, 0, DUMP_TABLE_TABLE);

this wouldn't be needed if the caller will only use dump_table for
tables

>    }
>    /* Innodb may be disabled */
>    if (!mysql_query(mysql, "show fields from innodb_index_stats"))
>    {
>      MYSQL_RES *tableres= mysql_store_result(mysql);
>      mysql_free_result(tableres);
> -    dump_table("innodb_index_stats", "mysql", NULL, 0);
> -    dump_table("innodb_table_stats", "mysql", NULL, 0);
> +    dump_table("innodb_index_stats", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +    dump_table("innodb_table_stats", "mysql", NULL, 0, DUMP_TABLE_TABLE);
>    }
>    opt_no_create_info= prev_no_create_info;
>    return 0;
> @@ -4817,11 +4866,11 @@ static int dump_all_timezones()
>    opt_prev_no_create_info= opt_no_create_info;
>    opt_no_create_info= 1;
>    fprintf(md_result_file,"\nUSE mysql;\n");
> -  dump_table("time_zone", "mysql", NULL, 0);
> -  dump_table("time_zone_name", "mysql", NULL, 0);
> -  dump_table("time_zone_leap_second", "mysql", NULL, 0);
> -  dump_table("time_zone_transition", "mysql", NULL, 0);
> -  dump_table("time_zone_transition_type", "mysql", NULL, 0);
> +  dump_table("time_zone", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +  dump_table("time_zone_name", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +  dump_table("time_zone_leap_second", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +  dump_table("time_zone_transition", "mysql", NULL, 0, DUMP_TABLE_TABLE);
> +  dump_table("time_zone_transition_type", "mysql", NULL, 0, DUMP_TABLE_TABLE);
>    opt_no_create_info= opt_prev_no_create_info;
>    return 0;
>  }
> @@ -5346,12 +5396,29 @@ static int dump_all_tables_in_db(char *database)
>        DBUG_RETURN(1);
>      }
>    }
> -  while ((table= getTableName(0)))
> +
> +  if (mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION &&
> +      !opt_no_create_info)
> +  {
> +    // First process sequences
> +    while ((table= getTableName(1, DUMP_TABLE_SEQUENCE)))
> +    {
> +      char *end= strmov(afterdot, table);
> +      if (include_table((uchar*) hash_key, end - hash_key))
> +      {
> +        dump_table(table, database, (uchar*) hash_key, end - hash_key,
> +                   DUMP_TABLE_SEQUENCE);
> +      }
> +    }
> +    table_ctrl= DUMP_TABLE_TABLE; // next pass
> +  }

and instead of all the above, you could do just

    for (int table_ctrl= 1; table_ctrl >= 0; table_ctrl--)

> +  while ((table= getTableName(0, table_ctrl)))
>    {
>      char *end= strmov(afterdot, table);
>      if (include_table((uchar*) hash_key, end - hash_key))
>      {
> -      dump_table(table, database, (uchar*) hash_key, end - hash_key);
> +      dump_table(table, database, (uchar*) hash_key, end - hash_key,
> +                 table_ctrl);
>        my_free(order_by);
>        order_by= 0;
>        if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009)
> @@ -5745,11 +5813,24 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
>        DBUG_RETURN(1);
>      }
>    }
> +
> +  if (mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION &&
> +      !opt_no_create_info)
> +  {
> +    /* Dump Sequence first */
> +    for (pos= dump_tables; pos < end; pos++)
> +    {
> +      DBUG_PRINT("info",("Dumping sequence(?) %s", *pos));
> +      dump_table(*pos, db, NULL, 0, DUMP_TABLE_SEQUENCE);
> +    }
> +  }
> +  else
> +    table_ctrl|= DUMP_TABLE_SEQUENCE; // dump all on next pass

and here, in dump_selected_tables(), you'd need to do
check_if_ignore_table() == IGNORE_SEQUENCE_TABLE.

>    /* Dump each selected table */
>    for (pos= dump_tables; pos < end; pos++)
>    {
>      DBUG_PRINT("info",("Dumping table %s", *pos));
> -    dump_table(*pos, db, NULL, 0);
> +    dump_table(*pos, db, NULL, 0, table_ctrl);
>      if (opt_dump_triggers &&
>          mysql_get_server_version(mysql) >= 50009)
>      {
Regards,
Sergei
VP of MariaDB Server Engineering
and security@xxxxxxxxxxx