List:Commits« Previous MessageNext Message »
From:Alexander Barkov Date:November 16 2010 2:39pm
Subject:bzr commit into mysql-5.5-bugteam branch (bar:3121) Bug#57306
View as plain text  
#At file:///home/bar/mysql-bzr/mysql-5.5-bugteam.b57306/ based on revid:vvaintroub@stripped3mkujaucde

 3121 Alexander Barkov	2010-11-16
      Bug#57306 SHOW PROCESSLIST does not display string literals well.
      
      Problem: Extended characters outside of ASCII range where not displayed
      properly in SHOW PROCESSLIST, because thd_info->query was always sent as 
      system_character_set (utf8). This was wrong, because query buffer
      is never converted to utf8 - it is always have client character set.
            
      Fix: sending query buffer using query character set
      
        @ sql/sql_class.cc
        @ sql/sql_class.h
          Introducing a new class CSET_STRING, a LEX_STRING with character set,
          with automatic initialization in constructor.
          Adding set_query(&CSET_STRING)
          Adding reset_query(), to use instead of set_query(0, NULL).
      
        @ sql/event_data_objects.cc
          Using reset_query()
      
        @ sql/log_event.cc
          Using reset_query()
          Adding charset argument to set_query_and_id().
      
        @ sql/slave.cc
          Using reset_query().
      
        @ sql/sp_head.cc
          Changing backing up and restore code to use CSET_STRING.
      
        @ sql/sql_audit.h
          Using CSET_STRING.
          In the "else" branch it's OK not to use
          global_system_variables.character_set_client.
          &my_charset_latin1, which is set in constructor, is fine
          (verified with Sergey Vojtovich).
      
        @ sql/sql_insert.cc
          Using set_query() with proper character set: table_name is utf8.
      
        @ sql/sql_parse.cc
          Adding character set argument to set_query_and_id().
          (This is the main point where thd->charset() is stored
           into thd->query_string.cs, for use in "SHOW PROCESSLIST".)
          Using reset_query().
          
        @ sql/sql_prepare.cc
          Storing client character set into thd->query_string.cs.
      
        @ sql/sql_show.cc
          Using CSET_STRING to fetch and send charset-aware query information
          from threads.
      
        @ storage/myisam/ha_myisam.cc
          Using set_query() with proper character set: table_name is utf8.
      
        @ mysql-test/r/show_check.result
        @ mysql-test/t/show_check.test
          Adding tests

    modified:
      mysql-test/r/show_check.result
      mysql-test/t/show_check.test
      sql/event_data_objects.cc
      sql/log_event.cc
      sql/slave.cc
      sql/sp_head.cc
      sql/sql_audit.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_insert.cc
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_show.cc
      storage/myisam/ha_myisam.cc
=== modified file 'mysql-test/r/show_check.result'
--- a/mysql-test/r/show_check.result	2010-07-23 08:44:55 +0000
+++ b/mysql-test/r/show_check.result	2010-11-16 14:25:27 +0000
@@ -1514,3 +1514,27 @@ ALTER TABLE t1 CHARACTER SET = utf8;
 COMMIT;
 DROP TRIGGER t1_bi;
 DROP TABLE t1;
+#
+# Bug#57306 SHOW PROCESSLIST does not display string literals well.
+#
+SET NAMES latin1;
+SELECT GET_LOCK('t', 1000);
+GET_LOCK('t', 1000)
+1
+SET NAMES latin1;
+SELECT GET_LOCK('t',1000) AS 'óóóó';;
+SHOW PROCESSLIST;
+Id	User	Host	db	Command	Time	State	Info
+###	root	localhost	test	Query	###	###	SHOW PROCESSLIST
+###	root	localhost	test	Query	###	###	SELECT GET_LOCK('t',1000) AS 'óóóó'
+SET NAMES utf8;
+SHOW PROCESSLIST;
+Id	User	Host	db	Command	Time	State	Info
+###	root	localhost	test	Query	###	###	SHOW PROCESSLIST
+###	root	localhost	test	Query	###	###	SELECT GET_LOCK('t',1000) AS 'óóóó'
+SELECT RELEASE_LOCK('t');
+RELEASE_LOCK('t')
+1
+óóóó
+1
+SET NAMES latin1;

=== modified file 'mysql-test/t/show_check.test'
--- a/mysql-test/t/show_check.test	2010-07-27 14:32:42 +0000
+++ b/mysql-test/t/show_check.test	2010-11-16 14:25:27 +0000
@@ -1328,3 +1328,26 @@ disconnect con1;
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc
 
+--echo #
+--echo # Bug#57306 SHOW PROCESSLIST does not display string literals well.
+--echo #
+
+SET NAMES latin1;
+SELECT GET_LOCK('t', 1000);
+--connect (con1,localhost,root,,)
+--connection con1
+SET NAMES latin1;
+--send SELECT GET_LOCK('t',1000) AS 'óóóó';
+--connection default
+--replace_column 1 ### 6 ### 7 ###
+SHOW PROCESSLIST;
+SET NAMES utf8;
+--replace_column 1 ### 6 ### 7 ###
+SHOW PROCESSLIST;
+SELECT RELEASE_LOCK('t');
+--connection con1
+--reap
+--disconnect con1
+--connection default
+SET NAMES latin1;
+

=== modified file 'sql/event_data_objects.cc'
--- a/sql/event_data_objects.cc	2010-07-27 10:25:53 +0000
+++ b/sql/event_data_objects.cc	2010-11-16 14:25:27 +0000
@@ -1526,7 +1526,7 @@ end:
   thd->end_statement();
   thd->cleanup_after_query();
   /* Avoid races with SHOW PROCESSLIST */
-  thd->set_query(NULL, 0);
+  thd->reset_query();
 
   DBUG_PRINT("info", ("EXECUTED %s.%s  ret: %d", dbname.str, name.str, ret));
 

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-10-23 13:09:27 +0000
+++ b/sql/log_event.cc	2010-11-16 14:25:27 +0000
@@ -3241,7 +3241,8 @@ int Query_log_event::do_apply_event(Rela
   if (is_trans_keyword() || rpl_filter->db_ok(thd->db))
   {
     thd->set_time((time_t)when);
-    thd->set_query_and_id((char*)query_arg, q_len_arg, next_query_id());
+    thd->set_query_and_id((char*)query_arg, q_len_arg,
+                          thd->charset(), next_query_id());
     thd->variables.pseudo_thread_id= thread_id;		// for temp tables
     DBUG_PRINT("query",("%s", thd->query()));
 
@@ -3513,7 +3514,7 @@ end:
   */
   thd->catalog= 0;
   thd->set_db(NULL, 0);                 /* will free the current database */
-  thd->set_query(NULL, 0);
+  thd->reset_query();
   DBUG_PRINT("info", ("end: query= 0"));
   /*
     As a disk space optimization, future masters will not log an event for
@@ -4750,7 +4751,7 @@ int Load_log_event::do_apply_event(NET* 
   new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
   thd->set_db(new_db.str, new_db.length);
   DBUG_ASSERT(thd->query() == 0);
-  thd->set_query_inner(NULL, 0);               // Should not be needed
+  thd->reset_query_inner();                    // Should not be needed
   thd->is_slave_error= 0;
   clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
 
@@ -4944,7 +4945,7 @@ error:
   const char *remember_db= thd->db;
   thd->catalog= 0;
   thd->set_db(NULL, 0);                   /* will free the current database */
-  thd->set_query(NULL, 0);
+  thd->reset_query();
   thd->stmt_da->can_overwrite_status= TRUE;
   thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
   thd->stmt_da->can_overwrite_status= FALSE;

=== modified file 'sql/slave.cc'
--- a/sql/slave.cc	2010-10-16 14:20:35 +0000
+++ b/sql/slave.cc	2010-11-16 14:25:27 +0000
@@ -3011,7 +3011,7 @@ err:
   sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
                   IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
   RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
-  thd->set_query(NULL, 0);
+  thd->reset_query();
   thd->reset_db(NULL, 0);
   if (mysql)
   {
@@ -3401,7 +3401,7 @@ the slave SQL thread with \"SLAVE START\
     variables is supposed to set them to 0 before terminating)).
   */
   thd->catalog= 0;
-  thd->set_query(NULL, 0);
+  thd->reset_query();
   thd->reset_db(NULL, 0);
   thd_proc_info(thd, "Waiting for slave mutex on exit");
   mysql_mutex_lock(&rli->run_lock);

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-11-11 05:06:16 +0000
+++ b/sql/sp_head.cc	2010-11-16 14:25:27 +0000
@@ -3065,8 +3065,7 @@ int sp_instr::exec_core(THD *thd, uint *
 int
 sp_instr_stmt::execute(THD *thd, uint *nextp)
 {
-  char *query;
-  uint32 query_length;
+  CSET_STRING query_backup;
   int res;
   DBUG_ENTER("sp_instr_stmt::execute");
   DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
@@ -3074,8 +3073,7 @@ sp_instr_stmt::execute(THD *thd, uint *n
   if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
     DBUG_RETURN(TRUE);
 
-  query= thd->query();
-  query_length= thd->query_length();
+  query_backup.set(thd->query_string);
 #if defined(ENABLED_PROFILING)
   /* This s-p instr is profilable and will be captured. */
   thd->profiling.set_query_source(m_query.str, m_query.length);
@@ -3105,7 +3103,7 @@ sp_instr_stmt::execute(THD *thd, uint *n
     }
     else
       *nextp= m_ip+1;
-    thd->set_query(query, query_length);
+    thd->set_query(query_backup);
     thd->query_name_consts= 0;
 
     if (!thd->is_error())

=== modified file 'sql/sql_audit.h'
--- a/sql/sql_audit.h	2010-09-20 14:17:32 +0000
+++ b/sql/sql_audit.h	2010-11-16 14:25:27 +0000
@@ -99,32 +99,29 @@ void mysql_audit_general(THD *thd, uint 
   {
     time_t time= my_time(0);
     uint msglen= msg ? strlen(msg) : 0;
-    const char *query, *user;
-    uint querylen, userlen;
+    const char *user;
+    uint userlen;
     char user_buff[MAX_USER_HOST_SIZE];
-    CHARSET_INFO *clientcs;
+    CSET_STRING query;
     ha_rows rows;
 
     if (thd)
     {
-      query= thd->query();
-      querylen= thd->query_length();
+      query.set(thd->query_string);
       user= user_buff;
       userlen= make_user_name(thd, user_buff);
-      clientcs= thd->variables.character_set_client;
       rows= thd->warning_info->current_row_for_warning();
     }
     else
     {
-      query= user= 0;
-      querylen= userlen= 0;
-      clientcs= global_system_variables.character_set_client;
+      user= 0;
+      userlen= 0;
       rows= 0;
     }
 
     mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
                        error_code, time, user, userlen, msg, msglen,
-                       query, querylen, clientcs, rows);
+                       query.str(), query.length(), query.charset(), rows);
   }
 #endif
 }

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-10-23 13:09:27 +0000
+++ b/sql/sql_class.cc	2010-11-16 14:25:27 +0000
@@ -2585,8 +2585,6 @@ Statement::Statement(LEX *lex_arg, MEM_R
   db(NULL),
   db_length(0)
 {
-  query_string.length= 0;
-  query_string.str= NULL;
   name.str= NULL;
 }
 
@@ -2602,7 +2600,7 @@ void Statement::set_statement(Statement 
   id=             stmt->id;
   mark_used_columns=   stmt->mark_used_columns;
   lex=            stmt->lex;
-  query_string=   stmt->query_string;
+  query_string.set(stmt->query_string);
 }
 
 
@@ -2627,10 +2625,10 @@ void Statement::restore_backup_statement
 
 /** Assign a new value to thd->query.  */
 
-void Statement::set_query_inner(char *query_arg, uint32 query_length_arg)
+void Statement::set_query_inner(char *query_arg, uint32 query_length_arg,
+                                CHARSET_INFO *cs_arg)
 {
-  query_string.str= query_arg;
-  query_string.length= query_length_arg;
+  query_string.set(query_arg, query_length_arg, cs_arg);
 }
 
 
@@ -3159,7 +3157,7 @@ extern "C" struct charset_info_st *thd_c
 */
 extern "C" char **thd_query(MYSQL_THD thd)
 {
-  return(&thd->query_string.str);
+  return (&thd->query_string.string.str);
 }
 
 /**
@@ -3170,7 +3168,7 @@ extern "C" char **thd_query(MYSQL_THD th
 */
 extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd)
 {
-  return(&thd->query_string);
+  return(&thd->query_string.string);
 }
 
 extern "C" int thd_slave_thread(const MYSQL_THD thd)
@@ -3415,20 +3413,21 @@ void THD::set_statement(Statement *stmt)
 
 /** Assign a new value to thd->query.  */
 
-void THD::set_query(char *query_arg, uint32 query_length_arg)
+void THD::set_query(char *query_arg, uint32 query_length_arg, CHARSET_INFO *cs)
 {
   mysql_mutex_lock(&LOCK_thd_data);
-  set_query_inner(query_arg, query_length_arg);
+  set_query_inner(query_arg, query_length_arg, cs);
   mysql_mutex_unlock(&LOCK_thd_data);
 }
 
 /** Assign a new value to thd->query and thd->query_id.  */
 
 void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
+                           CHARSET_INFO *cs,
                            query_id_t new_query_id)
 {
   mysql_mutex_lock(&LOCK_thd_data);
-  set_query_inner(query_arg, query_length_arg);
+  set_query_inner(query_arg, query_length_arg, cs);
   query_id= new_query_id;
   mysql_mutex_unlock(&LOCK_thd_data);
 }

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-10-23 13:09:27 +0000
+++ b/sql/sql_class.h	2010-11-16 14:25:27 +0000
@@ -109,6 +109,37 @@ extern MYSQL_PLUGIN_IMPORT const char **
 
 extern bool volatile shutdown_in_progress;
 
+
+/*
+  Character set armed LEX_STRING
+  with automaic initialization in constructor
+*/
+struct CSET_STRING
+{
+  LEX_STRING string;
+  CHARSET_INFO *cs;
+  CSET_STRING() { reset(); }
+  void set(char *str_arg, size_t length_arg, CHARSET_INFO *cs_arg)
+  {
+    DBUG_ASSERT(cs_arg != NULL);
+    string.str= str_arg;
+    string.length= length_arg;
+    cs= cs_arg;
+  }
+  void set(CSET_STRING &string_arg)
+  {
+    DBUG_ASSERT(string_arg.cs != NULL);
+    string= string_arg.string;
+    cs= string_arg.cs;
+  }
+  void reset() { set(NULL, 0, &my_charset_latin1);}
+
+  inline char *str() { return string.str; }
+  inline uint32 length() { return string.length; }
+  CHARSET_INFO *charset() { return cs; }
+};
+
+
 #define TC_LOG_PAGE_SIZE   8192
 #define TC_LOG_MIN_SIZE    (3*TC_LOG_PAGE_SIZE)
 
@@ -722,11 +753,14 @@ public:
     This printing is needed at least in SHOW PROCESSLIST and SHOW
     ENGINE INNODB STATUS.
   */
-  LEX_STRING query_string;
+  CSET_STRING query_string;
 
-  inline char *query() { return query_string.str; }
-  inline uint32 query_length() { return query_string.length; }
-  void set_query_inner(char *query_arg, uint32 query_length_arg);
+  inline char *query() { return query_string.str(); }
+  inline uint32 query_length() { return query_string.length(); }
+  CHARSET_INFO *query_charset() { return query_string.charset(); }
+  void set_query_inner(char *query_arg, uint32 query_length_arg,
+                       CHARSET_INFO *cs_arg);
+  void reset_query_inner() { set_query_inner(NULL, 0, &my_charset_latin1); }
 
   /**
     Name of the current (default) database.
@@ -2676,9 +2710,16 @@ public:
     Assign a new value to thd->query and thd->query_id and mysys_var.
     Protected with LOCK_thd_data mutex.
   */
-  void set_query(char *query_arg, uint32 query_length_arg);
+  void set_query(char *query_arg, uint32 query_length_arg,
+                 CHARSET_INFO *cs_arg);
+  void set_query(char *query_arg, uint32 query_length_arg) /*Mutex protected*/
+  { set_query(query_arg, query_length_arg, charset()); }
+  void set_query(CSET_STRING &str) /* Mutex protected */
+  { set_query(str.str(), str.length(), str.charset()); }
+  void reset_query()               /* Mutex protected */
+  { set_query(NULL, 0, &my_charset_latin1); }
   void set_query_and_id(char *query_arg, uint32 query_length_arg,
-                        query_id_t new_query_id);
+                        CHARSET_INFO *cs, query_id_t new_query_id);
   void set_query_id(query_id_t new_query_id);
   void set_open_tables(TABLE *open_tables_arg)
   {

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-10-07 10:01:51 +0000
+++ b/sql/sql_insert.cc	2010-11-16 14:25:27 +0000
@@ -2099,7 +2099,8 @@ bool delayed_get_table(THD *thd, TABLE_L
       mysql_mutex_unlock(&LOCK_thread_count);
       di->thd.set_db(table_list->db, (uint) strlen(table_list->db));
       di->thd.set_query(my_strdup(table_list->table_name,
-                                  MYF(MY_WME | ME_FATALERROR)), 0);
+                                  MYF(MY_WME | ME_FATALERROR)),
+                        0, system_charset_info);
       if (di->thd.db == NULL || di->thd.query() == NULL)
       {
         /* The error is reported */

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-10-23 13:09:27 +0000
+++ b/sql/sql_parse.cc	2010-11-16 14:25:27 +0000
@@ -550,7 +550,7 @@ static void handle_bootstrap_impl(THD *t
     query= (char *) thd->memdup_w_gap(buff, length + 1,
                                       thd->db_length + 1 +
                                       QUERY_CACHE_FLAGS_SIZE);
-    thd->set_query_and_id(query, length, next_query_id());
+    thd->set_query_and_id(query, length, thd->charset(), next_query_id());
     DBUG_PRINT("query",("%-.4096s",thd->query()));
 #if defined(ENABLED_PROFILING)
     thd->profiling.start_new_query();
@@ -1065,7 +1065,8 @@ bool dispatch_command(enum enum_server_c
                         thd->security_ctx->priv_user,
                         (char *) thd->security_ctx->host_or_ip);
 
-      thd->set_query_and_id(beginning_of_next_stmt, length, next_query_id());
+      thd->set_query_and_id(beginning_of_next_stmt, length,
+                            thd->charset(), next_query_id());
       /*
         Count each statement from the client.
       */
@@ -1390,7 +1391,7 @@ bool dispatch_command(enum enum_server_c
   log_slow_statement(thd);
 
   thd_proc_info(thd, "cleaning up");
-  thd->set_query(NULL, 0);
+  thd->reset_query();
   thd->command=COM_SLEEP;
   dec_thread_running();
   thd_proc_info(thd, 0);
@@ -5494,7 +5495,8 @@ void mysql_parse(THD *thd, char *rawbuf,
           if (found_semicolon && (ulong) (found_semicolon - thd->query()))
             thd->set_query_inner(thd->query(),
                                  (uint32) (found_semicolon -
-                                           thd->query() - 1));
+                                           thd->query() - 1),
+                                 thd->charset());
           /* Actually execute the query */
           if (found_semicolon)
           {

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2010-11-03 14:15:18 +0000
+++ b/sql/sql_prepare.cc	2010-11-16 14:25:27 +0000
@@ -3735,7 +3735,8 @@ bool Prepared_statement::execute(String 
     to point at it even after we restore from backup. This is ok, as
     expanded query was allocated in thd->mem_root.
   */
-  stmt_backup.set_query_inner(thd->query(), thd->query_length());
+  stmt_backup.set_query_inner(thd->query(), thd->query_length(),
+                              thd->charset());
 
   /*
     At first execution of prepared statement we may perform logical

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-11-02 14:02:16 +0000
+++ b/sql/sql_show.cc	2010-11-16 14:25:27 +0000
@@ -1727,7 +1727,7 @@ public:
   time_t start_time;
   uint   command;
   const char *user,*host,*db,*proc_info,*state_info;
-  char *query;
+  CSET_STRING query_string;
 };
 
 #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
@@ -1824,12 +1824,14 @@ void mysqld_list_processes(THD *thd,cons
         if (mysys_var)
           mysql_mutex_unlock(&mysys_var->mutex);
 
-        thd_info->query=0;
+        thd_info->query_string.reset();
         /* Lock THD mutex that protects its data when looking at it. */
         if (tmp->query())
         {
           uint length= min(max_query_length, tmp->query_length());
-          thd_info->query= (char*) thd->strmake(tmp->query(),length);
+          char *q= thd->strmake(tmp->query(),length);
+          /* Safety: in case strmake failed, we set length to 0. */
+          thd_info->query_string.set(q, q ? length : 0, tmp->query_charset());
         }
         mysql_mutex_unlock(&tmp->LOCK_thd_data);
         thd_info->start_time= tmp->start_time;
@@ -1857,7 +1859,9 @@ void mysqld_list_processes(THD *thd,cons
     else
       protocol->store_null();
     protocol->store(thd_info->state_info, system_charset_info);
-    protocol->store(thd_info->query, system_charset_info);
+    protocol->store(thd_info->query_string.str(),
+                    thd_info->query_string.length(),
+                    thd_info->query_string.charset());
     if (protocol->write())
       break; /* purecov: inspected */
   }

=== modified file 'storage/myisam/ha_myisam.cc'
--- a/storage/myisam/ha_myisam.cc	2010-10-06 14:34:28 +0000
+++ b/storage/myisam/ha_myisam.cc	2010-11-16 14:25:27 +0000
@@ -1499,8 +1499,7 @@ bool ha_myisam::check_and_repair(THD *th
 {
   int error=0;
   int marked_crashed;
-  char *old_query;
-  uint old_query_length;
+  CSET_STRING query_backup;
   HA_CHECK_OPT check_opt;
   DBUG_ENTER("ha_myisam::check_and_repair");
 
@@ -1511,10 +1510,9 @@ bool ha_myisam::check_and_repair(THD *th
     check_opt.flags|=T_QUICK;
   sql_print_warning("Checking table:   '%s'",table->s->path.str);
 
-  old_query= thd->query();
-  old_query_length= thd->query_length();
+  query_backup.set(thd->query_string);
   thd->set_query(table->s->table_name.str,
-                 (uint) table->s->table_name.length);
+                 (uint) table->s->table_name.length, system_charset_info);
 
   if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
   {
@@ -1527,7 +1525,7 @@ bool ha_myisam::check_and_repair(THD *th
     if (repair(thd, &check_opt))
       error=1;
   }
-  thd->set_query(old_query, old_query_length);
+  thd->set_query(query_backup);
   DBUG_RETURN(error);
 }
 

Attachment: [text/bzr-bundle] bzr/bar@mysql.com-20101116142527-h8oe2oc794zls192.bundle
Thread
bzr commit into mysql-5.5-bugteam branch (bar:3121) Bug#57306Alexander Barkov16 Nov
  • Re: bzr commit into mysql-5.5-bugteam branch (bar:3121) Bug#57306Guilhem Bichot16 Nov