List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:June 17 2011 11:33am
Subject:Re: bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902
Bug#12402875
View as plain text  
Hi Alfranio,

Thank you for this work! I think it is great that we clarify how 
replication tables work and that we make them work like other tables. 
You have analyzed and identified the correct things that need to be 
fixed. As we discussed on IRC, we need to refine the check that analyzes 
which statements should be allowed to execute concurrently with slave 
threads. Also, two optimizations may be possible. And I think we need a 
test case and I agree with all of Luis comments. So I will reject the patch.

  1. The check for which commands are allowed needs to be refined:
      - A SELECT can update the table (through a SF), so we have to check
        what tables SELECT accesses, so please remove
        CF_ACCESS_RPL_INFO_COMMAND from SQLCOM_SELECT.

      - We have to distinguish reads and writes, because we want SELECT
        to be able to read from the table but not write to it. It is not
        known at parse which tables are read and which are written.
        So we have to move the check to later, probably around
        lock_tables or decide_logging_format.

  2. Two optimizations may be possible:
      - Please, check if it is possible to optimize the check by
        comparing the table_share pointers instead of the table names and
        database names (not sure if this is possible).

      - Please, optimize the check for commands that could not access the
        tables such as CREATE USER etc, by setting
        CF_ACCESS_RPL_INFO_COMMAND for them. But please note that many
        commands that look like they would not touch any tables can
        actually call stored functions that could read or write tables:
        for instance, SHOW VARIABLES LIKE stored_function(),
        SET @foo= stored_function(), etc.

  3. Please write a test case that verifies that:

      - For each of the commands
        SELECT, DO, SHOW, SET, INSERT, INSERT...SELECT REPLACE,
        REPLACE...SELECT, UPDATE, DELETE, and CREATE...SELECT,
        if the command invokes a stored function:
         - An error is generated if the stored function updates a
           replication table when a slave thread is running.
         - No error is generated if the stored function reads a
           replication table when a slave thread is running.
         - No error is generated if the stored function writes a
           replication table when no slave thread is running.

      - For each of the commands
        CREATE, CREATE...SELECT, INSERT, INSERT...SELECT, REPLACE,
        REPLACE...SELECT, UPDATE, multi-table UPDATE, DELETE,
        ALTER TABLE, DROP TABLE, TRUNCATE, BINLOG, OPTIMIZE,
        LOAD DATA INFILE:
        An error is generated if the target table is a replication table
        and a slave thread is running.

      - SHOW CREATE TABLE does not generate an error when the target
        table is a replication table and a slave threads is running.

      - CREATE TEMPORARY TABLE mysql.rpl_info_table does not crash and
        does not allow users to access tables that they don't have
        permissions for.

/Sven


On 06/01/2011 12:05 AM, Alfranio Correia wrote:
> #At
> file:///home/acorreia/workspace.oracle/repository.mysql/bzrwork/bug-60902/mysql-trunk/
> based on revid:bjorn.munch@stripped
>
>   3125 Alfranio Correia	2011-05-31
>        BUG#12402875 - BUG#60902: MYSQLDUMP DOES NOT DUMP THE REPLICATION *_INFO
> TABLES
>
>        When WL#2775 was developed you have decided to follow the pattern used by
>        log tables where direct updates on them re forbidden. Then later we would
>        provide users with the necessary commands to execute the maintenance tasks
>        such as changing the engine in use or dumping data.
>
>        However, we have realized that this would unnecessarily duplicate code,
>        i.e. functionality.
>
>        So in this patch, we revert the code that follows the behavior adopt by
>        log tables and allow any sort of statement to be executed against the
>        replication tables provided that users have the appropriate permissions
>        and replication is not running. Read operations such as a SELECT can be
>        concurrently executed.
>       @ client/mysqldump.c
>          Removed the code that ignored replication tables while running mysqldump.
>       @ sql/lock.cc
>          Removed checks that automatically forbid write locks on replication
>          tables what was borrowed from log tables.
>       @ sql/rpl_info_file.cc
>          Removed this code that was introduced just to track down BUG#11766392.
>          However, we have figured that the problem was an environment issue.
>       @ sql/sql_class.h
>          CF_WRITE_RPL_INFO_COMMAND was removed because any update to a
>          replication table is forbidden while replication is running.
>       @ sql/sql_parse.cc
>          The file has:
>            . augmented the SQL command flag to indicate if one can execute
>              a statement against a replication table while replication is
>              running. Currently only reads, e.g. SELECT, are allowed.
>
>            . CF_WRITE_RPL_INFO_COMMAND was removed because any update to a
>              replication table is forbidden while replication is running.
>
>            . Introduced "check_access_rpl_repository_before" what checks
>              if an SQL statment can be executed against a replication
>              table. If a replication table is being accessed a lock is
>              acquired on LOCK_active_mi and active_mi mutexes.
>
>            . Introduced "check_access_rpl_repository_after" that releases
>              any lock acquired at "check_access_rpl_repository_before".
>       @ sql/sql_parse.h
>          Created a function to check if the SQL command flag is tagged
>          with CF_ACCESS_RPL_INFO_COMMAND.
>
>          This means that the SQL statement can access, currently read,
>          replication tables while replication is running.
>       @ sql/sql_table.cc
>          Created a function to check if a table belongs to the set of
>          replication tables.
>       @ sql/sql_table.h
>          Created a function to check if a table belongs to the set of
>          replication tables.
>
>      modified:
>        client/mysqldump.c
>        sql/lock.cc
>        sql/rpl_info_file.cc
>        sql/sql_class.h
>        sql/sql_parse.cc
>        sql/sql_parse.h
>        sql/sql_table.cc
>        sql/sql_table.h
> === modified file 'client/mysqldump.c'
> --- a/client/mysqldump.c	2011-05-26 15:20:09 +0000
> +++ b/client/mysqldump.c	2011-05-31 22:05:19 +0000
> @@ -910,11 +910,7 @@ static int get_options(int *argc, char *
>         my_hash_insert(&ignore_table,
>                        (uchar*) my_strdup("mysql.general_log", MYF(MY_WME))) ||
>         my_hash_insert(&ignore_table,
> -                     (uchar*) my_strdup("mysql.slow_log", MYF(MY_WME))) ||
> -      my_hash_insert(&ignore_table,
> -                     (uchar*) my_strdup("mysql.slave_master_info", MYF(MY_WME))) ||
> -      my_hash_insert(&ignore_table,
> -                     (uchar*) my_strdup("mysql.slave_relay_log_info",
> MYF(MY_WME))))
> +                     (uchar*) my_strdup("mysql.slow_log", MYF(MY_WME))))
>       return(EX_EOM);
>
>     if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
>
> === modified file 'sql/lock.cc'
> --- a/sql/lock.cc	2011-05-04 09:54:04 +0000
> +++ b/sql/lock.cc	2011-05-31 22:05:19 +0000
> @@ -121,7 +121,7 @@ lock_tables_check(THD *thd, TABLE **tabl
>       Identifies if the executed sql command can updated either a log
>       or rpl info table.
>     */
> -  bool log_table_write_query, rpl_info_table_write_query;
> +  bool log_table_write_query;
>
>     DBUG_ENTER("lock_tables_check");
>
> @@ -129,8 +129,6 @@ lock_tables_check(THD *thd, TABLE **tabl
>     is_superuser= thd->security_ctx->master_access&  SUPER_ACL;
>     log_table_write_query=
>        is_log_table_write_query(thd->lex->sql_command);
> -  rpl_info_table_write_query=
> -     is_rpl_info_table_write_query(thd->lex->sql_command);
>
>     for (i=0 ; i<count; i++)
>     {
> @@ -145,23 +143,6 @@ lock_tables_check(THD *thd, TABLE **tabl
>         When a user is requesting a lock, the following
>         constraints are enforced:
>       */
> -    if (t->s->table_category == TABLE_CATEGORY_RPL_INFO&&
> -        (flags&  MYSQL_LOCK_RPL_INFO_TABLE) == 0&&
> -        !rpl_info_table_write_query)
> -    {
> -      /*
> -        A user should not be able to prevent writes,
> -        or hold any type of lock in a session,
> -        since this would be a DOS attack.
> -      */
> -      if (t->reginfo.lock_type>= TL_READ_NO_INSERT ||
> -          thd->lex->sql_command == SQLCOM_LOCK_TABLES)
> -      {
> -          my_error(ER_CANT_LOCK_RPL_INFO_TABLE, MYF(0));
> -          DBUG_RETURN(1);
> -      }
> -    }
> -
>       if (t->s->table_category == TABLE_CATEGORY_LOG&&
>           (flags&  MYSQL_LOCK_LOG_TABLE) == 0&&
>           !log_table_write_query)
>
> === modified file 'sql/rpl_info_file.cc'
> --- a/sql/rpl_info_file.cc	2011-05-26 15:20:09 +0000
> +++ b/sql/rpl_info_file.cc	2011-05-31 22:05:19 +0000
> @@ -126,25 +126,6 @@ int Rpl_info_file::do_prepare_info_for_w
>
>   int Rpl_info_file::do_check_info()
>   {
> -  /*
> -    This function checks if the file exists and in other modules
> -    further actions are taken based on this. If the file exists
> -    but users do not have the appropriated rights to access it,
> -    other modules will assume that the file does not exist and
> -    will take the appropriate actions and most likely will fail
> -    safely after trying to create it.
> -
> -    This is behavior is not a problem. However, in other modules,
> -    it is not possible to print out what is the root of the
> -    failure as a detailed error code is not returned. For that
> -    reason, we print out such information in here.
> -  */
> -  if (my_access(info_fname, F_OK | R_OK | W_OK))
> -    sql_print_information("Info file %s cannot be accessed (errno %d)."
> -                          " Most likely this is a new slave or you are "
> -                          " changing the repository type.", info_fname,
> -                          errno);
> -
>     return my_access(info_fname, F_OK);
>   }
>
>
> === modified file 'sql/sql_class.h'
> --- a/sql/sql_class.h	2011-05-26 15:20:09 +0000
> +++ b/sql/sql_class.h	2011-05-31 22:05:19 +0000
> @@ -4052,9 +4052,10 @@ public:
>   #define CF_HA_CLOSE             (1U<<  11)
>
>   /**
> -  Identifies statements that can directly update a rpl info table.
> +  Identifies statements that can access a rpl info table while replication
> +  is running.
>   */
> -#define CF_WRITE_RPL_INFO_COMMAND (1U<<  12)
> +#define CF_ACCESS_RPL_INFO_COMMAND (1U<<  12)
>
>   /* Bits in server_command_flags */
>
>
> === modified file 'sql/sql_parse.cc'
> --- a/sql/sql_parse.cc	2011-05-26 15:20:09 +0000
> +++ b/sql/sql_parse.cc	2011-05-31 22:05:19 +0000
> @@ -117,6 +117,10 @@ static bool execute_sqlcom_select(THD *t
>   static bool check_show_access(THD *thd, TABLE_LIST *table);
>   static void sql_kill(THD *thd, ulong id, bool only_kill_query);
>   static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
> +#ifdef HAVE_REPLICATION
> +static bool check_access_rpl_repository_before(THD *thd);
> +static void check_access_rpl_repository_after(THD *thd);
> +#endif
>
>   const char *any_db="*any*";	// Special symbol for check_access
>
> @@ -300,8 +304,7 @@ void init_update_queries(void)
>                                               CF_CAN_GENERATE_ROW_EVENTS;
>     sql_command_flags[SQLCOM_CREATE_INDEX]=   CF_CHANGES_DATA |
> CF_AUTO_COMMIT_TRANS;
>     sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND
> |
> -                                            CF_AUTO_COMMIT_TRANS |
> -                                            CF_WRITE_RPL_INFO_COMMAND;
> +                                            CF_AUTO_COMMIT_TRANS;
>     sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND
> |
>                                               CF_AUTO_COMMIT_TRANS;
>     sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA |
> CF_AUTO_COMMIT_TRANS;
> @@ -339,7 +342,8 @@ void init_update_queries(void)
>     sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA |
> CF_REEXECUTION_FRAGILE |
>                                               CF_CAN_GENERATE_ROW_EVENTS;
>     sql_command_flags[SQLCOM_SELECT]=         CF_REEXECUTION_FRAGILE |
> -                                            CF_CAN_GENERATE_ROW_EVENTS;
> +                                            CF_CAN_GENERATE_ROW_EVENTS |
> +                                            CF_ACCESS_RPL_INFO_COMMAND;
>     sql_command_flags[SQLCOM_SET_OPTION]=     CF_REEXECUTION_FRAGILE |
> CF_AUTO_COMMIT_TRANS;
>     sql_command_flags[SQLCOM_DO]=             CF_REEXECUTION_FRAGILE |
>                                               CF_CAN_GENERATE_ROW_EVENTS;
> @@ -371,7 +375,7 @@ void init_update_queries(void)
>     sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
>     sql_command_flags[SQLCOM_SHOW_GRANTS]=      CF_STATUS_COMMAND;
>     sql_command_flags[SQLCOM_SHOW_CREATE_DB]=   CF_STATUS_COMMAND;
> -  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND;
> +  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND |
> CF_ACCESS_RPL_INFO_COMMAND;
>     sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
>     sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]=  CF_STATUS_COMMAND;
>     sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
> @@ -392,7 +396,6 @@ void init_update_queries(void)
>                                                   CF_SHOW_TABLE_COMMAND |
>                                                   CF_REEXECUTION_FRAGILE);
>
> -
>     sql_command_flags[SQLCOM_CREATE_USER]=       CF_CHANGES_DATA;
>     sql_command_flags[SQLCOM_RENAME_USER]=       CF_CHANGES_DATA;
>     sql_command_flags[SQLCOM_DROP_USER]=         CF_CHANGES_DATA;
> @@ -423,14 +426,10 @@ void init_update_queries(void)
>       The following admin table operations are allowed
>       on log tables.
>     */
> -  sql_command_flags[SQLCOM_REPAIR]=    CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS
> |
> -                                       CF_WRITE_RPL_INFO_COMMAND;
> -  sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS
> |
> -                                       CF_WRITE_RPL_INFO_COMMAND;
> -  sql_command_flags[SQLCOM_ANALYZE]=   CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS
> |
> -                                       CF_WRITE_RPL_INFO_COMMAND;
> -  sql_command_flags[SQLCOM_CHECK]=     CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS
> |
> -                                       CF_WRITE_RPL_INFO_COMMAND;
> +  sql_command_flags[SQLCOM_REPAIR]=    CF_WRITE_LOGS_COMMAND |
> CF_AUTO_COMMIT_TRANS;
> +  sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND |
> CF_AUTO_COMMIT_TRANS;
> +  sql_command_flags[SQLCOM_ANALYZE]=   CF_WRITE_LOGS_COMMAND |
> CF_AUTO_COMMIT_TRANS;
> +  sql_command_flags[SQLCOM_CHECK]=     CF_WRITE_LOGS_COMMAND |
> CF_AUTO_COMMIT_TRANS;
>
>     sql_command_flags[SQLCOM_CREATE_USER]|=       CF_AUTO_COMMIT_TRANS;
>     sql_command_flags[SQLCOM_DROP_USER]|=         CF_AUTO_COMMIT_TRANS;
> @@ -529,14 +528,14 @@ bool is_log_table_write_query(enum enum_
>   }
>
>   /**
> -  Check if a sql command is allowed to write to rpl info tables.
> +  Check if a sql command is allowed to access rpl info tables.
>     @param command The SQL command
> -  @return true if writing is allowed
> +  @return true if access is allowed
>   */
> -bool is_rpl_info_table_write_query(enum enum_sql_command command)
> +bool is_rpl_info_table_access_query(enum enum_sql_command command)
>   {
>     DBUG_ASSERT(command>= 0&&  command<= SQLCOM_END);
> -  return (sql_command_flags[command]&  CF_WRITE_RPL_INFO_COMMAND) != 0;
> +  return (sql_command_flags[command]&  CF_ACCESS_RPL_INFO_COMMAND) != 0;
>   }
>
>   void execute_init_command(THD *thd, LEX_STRING *init_command,
> @@ -2156,6 +2155,17 @@ mysql_execute_command(THD *thd)
>       }
>   #ifdef HAVE_REPLICATION
>     } /* endif unlikely slave */
> +
> +  /*
> +    This function must be moved to another module, transformed in a
> +    callback or refactored after WL#5675. Maybe the same should be
> +    applied to the code the follows this function.
> +  */
> +  if (check_access_rpl_repository_before(thd))
> +  {
> +    check_access_rpl_repository_after(thd);
> +    DBUG_RETURN(-1);
> +  }
>   #endif
>
>     status_var_increment(thd->status_var.com_stat[lex->sql_command]);
> @@ -4586,6 +4596,17 @@ finish:
>       thd->mdl_context.release_statement_locks();
>     }
>
> +#ifdef HAVE_REPLICATION
> +  /*
> +    This function must be moved to another module, transformed in a
> +    callback or refactored after WL#5675.
> +    This function is not supposed to fail and must just reset the
> +    flag in replication, if necessary, in order to allow replication
> +    to start.
> +  */
> +  check_access_rpl_repository_after(thd);
> +#endif
> +
>     DBUG_RETURN(res || thd->is_error());
>   }
>
> @@ -7629,3 +7650,83 @@ merge_charset_and_collation(const CHARSE
>     }
>     return cs;
>   }
> +
> +#ifdef HAVE_REPLICATION
> +/**
> +  Checks if it is possible to access a replication table while
> +  replicaiton is running. Currently, it is only possible to
> +  read such tables.
> +
> +  If a replication table is accessed and replication is not
> +  running the LOCK_active_mi and active_mi mutexes are locked
> +  while running the statement.
> +
> +  @param thd Pointer to the current thread.
> +
> +  @return true if it is not possible, false, otherwise.
> +*/
> +bool check_access_rpl_repository_before(THD *thd)
> +{
> +  int running= 0;
> +  LEX* lex= thd->lex;
> +  TABLE_LIST *table= NULL;
> +  DBUG_ENTER("check_access_rpl_repository");
> +
> +  if (!is_rpl_info_table_access_query(lex->sql_command))
> +  {
> +    for (table= lex->query_tables; table; table= table->next_local)
> +    {
> +      if (check_if_rpl_table(table->db_length, table->db,
> +                             table->table_name_length,
> +                             table->table_name))
> +      {
> +        mysql_mutex_lock(&LOCK_active_mi);
> +        lock_slave_threads(active_mi);
> +        init_thread_mask(&running, active_mi, FALSE);
> +        if (running)
> +        {
> +          /*
> +            Notify user that the command cannot be executed against
> +            replication tables while slave is running.
> +          */
> +          my_error(ER_SLAVE_MUST_STOP, MYF(0));
> +          DBUG_RETURN(TRUE);
> +        }
> +        DBUG_RETURN(FALSE);
> +      }
> +    }
> +  }
> +  DBUG_RETURN(FALSE);
> +}
> +
> +/**
> +  Cleans up after the check_access_rpl_repository_after.
> +
> +  Basically, it releases the locks on the LOCK_active_mi and
> +  active_mi if necessary.
> +
> +  @param thd Pointer to the current thread.
> +*/
> +void check_access_rpl_repository_after(THD *thd)
> +{
> +  LEX* lex= thd->lex;
> +  TABLE_LIST *table= NULL;
> +  DBUG_ENTER("check_access_rpl_repository");
> +
> +  if (!is_rpl_info_table_access_query(lex->sql_command))
> +  {
> +    for (table= lex->query_tables; table; table= table->next_local)
> +    {
> +      if (check_if_rpl_table(table->db_length, table->db,
> +                             table->table_name_length,
> +                             table->table_name))
> +      {
> +        unlock_slave_threads(active_mi);
> +        mysql_mutex_unlock(&LOCK_active_mi);
> +        DBUG_VOID_RETURN;
> +      }
> +    }
> +  }
> +  DBUG_VOID_RETURN;
> +}
> +#endif
>
> === modified file 'sql/sql_parse.h'
> --- a/sql/sql_parse.h	2011-05-04 07:51:15 +0000
> +++ b/sql/sql_parse.h	2011-05-31 22:05:19 +0000
> @@ -83,7 +83,7 @@ bool check_identifier_name(LEX_STRING *s
>   bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
>   bool is_update_query(enum enum_sql_command command);
>   bool is_log_table_write_query(enum enum_sql_command command);
> -bool is_rpl_info_table_write_query(enum enum_sql_command command);
> +bool is_rpl_info_table_access_query(enum enum_sql_command command);
>   bool alloc_query(THD *thd, const char *packet, uint packet_length);
>   void mysql_init_select(LEX *lex);
>   void mysql_parse(THD *thd, char *rawbuf, uint length,
>
> === modified file 'sql/sql_table.cc'
> --- a/sql/sql_table.cc	2011-05-26 15:20:09 +0000
> +++ b/sql/sql_table.cc	2011-05-31 22:05:19 +0000
> @@ -7556,3 +7556,40 @@ static bool check_engine(THD *thd, const
>     }
>     return FALSE;
>   }
> +
> +/**
> +  Checks if db.table_name is a replication table, i.e. MI_INFO_NAME or
> +  RLI_INFO_NAME.
> +
> +  @param db_len Size of database's name.
> +  @param db Database's name.
> +  @param table_name_len Size of the table's name.
> +  @param table_name Table's name.
> +
> +  @return 1, if db.table is a replication table. 0, otherwise.
> +*/
> +int check_if_rpl_table(size_t db_len, const char *db, size_t table_name_len,
> +                       const char *table_name)
> +{
> +  if (db_len == MYSQL_SCHEMA_NAME.length&&
> +      !(lower_case_table_names ?
> +        my_strcasecmp(system_charset_info, db, MYSQL_SCHEMA_NAME.str) :
> +        strcmp(db, MYSQL_SCHEMA_NAME.str)))
> +  {
> +    if (table_name_len == RLI_INFO_NAME.length&&  !(lower_case_table_names
> ?
> +                                  my_strcasecmp(system_charset_info,
> +                                                table_name, RLI_INFO_NAME.str) :
> +                                  strcmp(table_name, RLI_INFO_NAME.str)))
> +    {
> +      return 1;
> +    }
> +
> +    if (table_name_len == MI_INFO_NAME.length&&  !(lower_case_table_names ?
> +      my_strcasecmp(system_charset_info, table_name, MI_INFO_NAME.str) :
> +      strcmp(table_name, MI_INFO_NAME.str)))
> +    {
> +      return 1;
> +    }
> +  }
> +  return 0;
> +}
>
> === modified file 'sql/sql_table.h'
> --- a/sql/sql_table.h	2011-03-09 20:54:55 +0000
> +++ b/sql/sql_table.h	2011-05-31 22:05:19 +0000
> @@ -217,6 +217,8 @@ void release_ddl_log();
>   void execute_ddl_log_recovery();
>   bool execute_ddl_log_entry(THD *thd, uint first_entry);
>   bool check_duplicate_warning(THD *thd, char *msg, ulong length);
> +int check_if_rpl_table(size_t db_len, const char *db, size_t table_name_len,
> +                       const char *table_name);
>
>   /*
>     These prototypes where under INNODB_COMPATIBILITY_HOOKS.
>
>
>
>
>

Thread
bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902Bug#12402875Alfranio Correia1 Jun
  • Re: bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902Bug#12402875Luís Soares4 Jun
    • Re: bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902Bug#12402875Alfranio Correia4 Jun
  • Re: bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902Bug#12402875Sven Sandberg19 Jun
    • Re: bzr commit into mysql-trunk branch (alfranio.correia:3125) Bug#60902Bug#12402875Alfranio Correia22 Jul