List:Commits« Previous MessageNext Message »
From:Konstantin Osipov Date:February 20 2008 12:30pm
Subject:Re: bk commit into 5.0 tree (davi:1.2583) BUG#28386
View as plain text  
* Davi Arnaut <davi@stripped> [07/12/21 14:02]:
> ChangeSet@stripped, 2007-12-21 08:49:53-02:00, davi@stripped +5 -0
>   Bug#28386 the general log is incomplete

Hi Davi,

Please see below.

> +static void log_refresh_cmd(THD *thd, ulong options)
> +{
> +  char buff[512];
> +  String str(buff,(uint32) sizeof(buff), system_charset_info);
> +  static struct {
> +    ulong option;
> +    const char *str;
> +  } const refresh_opt[]= {
> +    { REFRESH_GRANT, "PRIVILEGES, " },
> +    { REFRESH_LOG, "LOGS, " },
> +    { REFRESH_TABLES, "TABLES, " },
> +    { REFRESH_HOSTS, "HOSTS, " },
> +    { REFRESH_STATUS, "STATUS, " },
> +    { REFRESH_THREADS, "THREADS, " },
> +    { REFRESH_SLAVE, "SLAVE, " },
> +    { REFRESH_MASTER, "MASTER, " },
> +    { REFRESH_READ_LOCK, "TABLES WITH READ LOCK, " },
> +    { REFRESH_QUERY_CACHE, "QUERY CACHE, " },
> +    { REFRESH_QUERY_CACHE_FREE, "QUERY CACHE FREE, " },
> +    { REFRESH_DES_KEY_FILE, "DES_KEY_FILE, " },
> +    { REFRESH_USER_RESOURCES, "USER_RESOURCES, " }
> +  };
> +
> +  str.length(0);
> +
> +  for (uint i= 0; i < array_elements(refresh_opt); i++)
> +  {
> +    if (refresh_opt[i].option & options)
> +      str.append(refresh_opt[i].str);
> +  }
> +
> +  if (str.is_empty())
> +    mysql_log.write(thd, thd->command, NullS);
> +  else
> +    mysql_log.write(thd, thd->command, "%.*s",
> +                    str.length() - 2, str.c_ptr_quick());
> +}

I'm not sure this change can be classified as a bug fix. It is not
very extensible (no compile-time check that one doesn't forget to
add an option name when a new REFRESH option is added).
I would exclude this change from the patch.

> +
>  void thd_init_client_charset(THD *thd, uint cs_number)
>  {
>    /*
> @@ -2079,7 +2117,7 @@ bool dispatch_command(enum enum_server_c
>      ulong options= (ulong) (uchar) packet[0];
>      if (check_global_access(thd,RELOAD_ACL))
>        break;
> -    mysql_log.write(thd,command,NullS);
> +    log_refresh_cmd(thd, options);
>      if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
>        send_ok(thd);
>      break;
> @@ -2159,6 +2197,7 @@ bool dispatch_command(enum enum_server_c
>    }
>    case COM_PING:
>      statistic_increment(thd->status_var.com_other, &LOCK_status);
> +    mysql_log.write(thd, command, NullS);

I believe not logging COM_PING in the general log is intentional.
COM_PING may be generated by a heartbeat service. 
Since we don't provide a way to fine-tune what gets into the
general log, people may complain  when this is added.

E.g. when we changed behaviour of --log-slow-admin-statements
many screamed loudly.

I think this should not be part of the patch.

> @@ -2458,6 +2458,11 @@ void mysql_stmt_reset(THD *thd, char *pa
>  
>    stmt->state= Query_arena::PREPARED;
>  
> +  /* Don't print if inside a stored procedure. */
> +  if (thd->spcont == NULL)
> +    mysql_log.write(thd, thd->command, "[%lu] %.*s", stmt->id,
> +                    stmt->name.length, stmt->name.str);
> +

OK, but mysql_stmt_reset can never be called from a stored
procedure. Thus no need to check for thd->spcont. You could add a
comment instead.

>    send_ok(thd);
>  
>    DBUG_VOID_RETURN;
> @@ -2543,6 +2548,8 @@ void mysql_stmt_get_longdata(THD *thd, c
>    uint param_number;
>    Prepared_statement *stmt;
>    Item_param *param;
> +  char *extra_data;
> +  ulong extra_length;
>  #ifndef EMBEDDED_LIBRARY
>    char *packet_end= packet + packet_length - 1;
>  #endif
> @@ -2582,15 +2589,29 @@ void mysql_stmt_get_longdata(THD *thd, c
>    param= stmt->param_array[param_number];
>  
>  #ifndef EMBEDDED_LIBRARY
> -  if (param->set_longdata(packet, (ulong) (packet_end - packet)))
> +  extra_data= packet;
> +  extra_length= (ulong) (packet_end - packet);
>  #else
> -  if (param->set_longdata(thd->extra_data, thd->extra_length))
> +  extra_data= thd->extra_data;
> +  extra_length= thd->extra_length;
>  #endif
> +
> +  if (param->set_longdata(extra_data, extra_length))
>    {
>      stmt->state= Query_arena::ERROR;
>      stmt->last_errno= ER_OUTOFMEMORY;
>      sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
>    }
> +
> +  /* Don't print if inside a stored procedure. */
> +  if (thd->spcont == NULL)
> +  {
> +    const char fmt[]= "[%lu] %.*s %.*b";
> +    mysql_log.write(thd, thd->command, fmt, stmt->id,
> +                    stmt->name.length, stmt->name.str,
> +                    extra_length, extra_data);
> +  }

OK, but please do not log the argument. It can be
binary, to start with.
Once again, this code is never executed in a stored procedure.

>    DBUG_VOID_RETURN;
>  }
>  
> @@ -3064,6 +3085,12 @@ bool Prepared_statement::deallocate()
>      my_error(ER_PS_NO_RECURSION, MYF(0));
>      return TRUE;
>    }
> +
> +  /* Don't print if inside a stored procedure. */
> +  if (thd->spcont == NULL)
> +    mysql_log.write(thd, thd->command, "[%lu] %.*s", id,
> +                    name.length, name.str);

OK. This code is never executed in a stored procedure. 


> +static FILE *open_master_log(void)
> +{
> +  FILE *log_file;
> +  char *master_log_filename;
> +
> +  master_log_filename = (char *) malloc(strlen(opt_vardir) +
> strlen("/log/master.log") + 1);
> +  strcpy(master_log_filename, opt_vardir);
> +  strcat(master_log_filename, "/log/master.log");
> +  printf("Opening '%s'\n", master_log_filename);
> +  log_file= my_fopen(master_log_filename, (int) (O_RDONLY | O_BINARY),
> MYF(MY_WME));
> +  free(master_log_filename);
> +
> +  return log_file;
> +}
> +
>  /*
>    Bug#17667: An attacker has the opportunity to bypass query logging.
>  
> @@ -15067,7 +15082,6 @@ static void test_bug17667()
>  
>    struct buffer_and_length *statement_cursor;
>    FILE *log_file;
> -  char *master_log_filename;
>  
>    myheader("test_bug17667");
>  
> @@ -15109,12 +15123,7 @@ static void test_bug17667()
>    rc= mysql_query(mysql, "flush logs");
>    myquery(rc);
>  
> -  master_log_filename = (char *) malloc(strlen(opt_vardir) +
> strlen("/log/master.log") + 1);
> -  strcpy(master_log_filename, opt_vardir);
> -  strcat(master_log_filename, "/log/master.log");
> -  printf("Opening '%s'\n", master_log_filename);
> -  log_file= my_fopen(master_log_filename, (int) (O_RDONLY | O_BINARY),
> MYF(MY_WME));
> -  free(master_log_filename);
> +  log_file= open_master_log();
>  
>    if (log_file != NULL) {
>  
> @@ -16152,6 +16161,89 @@ static void test_bug31669()
>    DBUG_VOID_RETURN;
>  }
>  
> +static void test_bug28386()
> +{
> +  int rc;
> +  uint i;
> +  MYSQL_STMT *stmt;
> +  FILE *log_file;
> +  char buffer[MAX_TEST_QUERY_LENGTH*2], *line;
> +  struct {
> +    const char *str;
> +    size_t len;
> +    int found;
> +  } tokens[] = {
> +    { "Close stmt", 0, 0 },
> +    { "Reset stmt", 0, 0 },
> +    { "Long Data", 0, 0 },
> +    { "Ping", 0, 0 },
> +    { "Refresh", 0, 0 },
> +    { "PRIVILEGES, STATUS", 0, 0 }
> +  };
> +  DBUG_ENTER("test_bug28386");
> +  myheader("test_bug28386");
> +
> +  sleep(30);

Ouch!

Why don't you use the approach from the other test:

/* Make sure the server has written the logs to disk before
 * reading it */
rc= mysql_query(mysql, "flush logs");
myquery(rc);

?

Or rather. Add the test case to 5.1 only. Use logging-to-tables
feature and write the test case in SQL.

> +
> +  for (i= 0; i < array_elements(tokens); i++)
> +    tokens[i].len= strlen(tokens[i].str);
> +
> +  stmt= mysql_simple_prepare(mysql, "SELECT 1");
> +  check_stmt(stmt);
> +
> +  rc= mysql_stmt_execute(stmt);
> +  check_execute(stmt, rc);
> +
> +  rc= my_process_stmt_result(stmt);
> +  DIE_UNLESS(rc == 1);
> +
> +  rc= mysql_stmt_reset(stmt);
> +  check_execute(stmt, rc);
> +
> +  rc= mysql_stmt_close(stmt);
> +  DIE_UNLESS(!rc);
> +
> +  rc= mysql_ping(mysql);
> +  DIE_UNLESS(!rc);
> +
> +  mysql_refresh(mysql, REFRESH_GRANT);
> +  mysql_refresh(mysql, REFRESH_STATUS | REFRESH_GRANT);
> +
> +  log_file= open_master_log();
> +
> +  if (log_file == NULL)
> +  {
> +    fprintf(stderr, "Could not find the log file, VARDIR/log/master.log, so "
> +            "test_bug28386 is \ninconclusive.  Run test from the "
> +            "mysql-test/mysql-test-run* program \nto set up the correct "
> +            "environment for this test.\n\n");
> +    DBUG_VOID_RETURN;
> +  }
> +
> +  while ((line= fgets(buffer, MAX_TEST_QUERY_LENGTH*2, log_file)))
> +  {
> +    for (i= 0; i < array_elements(tokens); i++)
> +    {
> +      if (my_memmem(line, MAX_TEST_QUERY_LENGTH*2, tokens[i].str,
> +                    tokens[i].len))
> +        tokens[i].found= 1;
> +    }
> +  }
> +
> +  for (i= 0; i < array_elements(tokens); i++)
> +    DIE_UNLESS(tokens[i].found == 1);
> +
> +  my_fclose(log_file, MYF(0));
> +
> +  DBUG_VOID_RETURN;
> +}
> +

To sum up:

 - OK to push the approved parts. Not approved parts in my view
   more close in nature to a feature request, and they need 
   to be thought through before getting in. I recommend that you
   open a separate bug report for them (feature request).
   Not approved:
    - logging arguments of LONGDATA
    - logging COM_PING
    - logging arguments of COM_REFRESH
 - please rewrite the test in SQL.
 - I would like to take a look at the patch before you push.

-- 
-- Konstantin Osipov              Software Developer, Moscow, Russia
-- MySQL AB, www.mysql.com   The best DATABASE COMPANY in the GALAXY
Thread
bk commit into 5.0 tree (davi:1.2583) BUG#28386Davi Arnaut21 Dec
  • Re: bk commit into 5.0 tree (davi:1.2583) BUG#28386Konstantin Osipov20 Feb
    • Re: bk commit into 5.0 tree (davi:1.2583) BUG#28386Davi Arnaut20 Feb