From 187958a9516fd5d1054682590b1925db857168d8 Mon Sep 17 00:00:00 2001
From: Sergey Vojtovich <svoj@sun.com>
Date: Wed, 25 Nov 2009 16:25:01 +0400
Subject: [PATCH] Backport from 6.0-codebase.

WL#3951 - MyISAM: Additional Error Logs for Data Corruption

When table corruption is detected, in addition to current error message
provide following information:
- list of threads (and queries) accessing a table;
- thread_id of a thread that detected corruption;
- source file name and line number where this corruption was detected;
- optional extra information (string).
---
 .../r/myisam_crash_before_flush_keys.result   |  2 +
 .../t/myisam_crash_before_flush_keys.test     |  3 ++
 storage/myisam/CMakeLists.txt                 |  3 +-
 storage/myisam/Makefile.am                    |  2 +-
 storage/myisam/ha_myisam.cc                   | 40 +++++++++++++++++++
 storage/myisam/mi_extrafunc.h                 | 21 ++++++++++
 storage/myisam/mi_locking.c                   |  4 ++
 storage/myisam/mi_test1.c                     |  2 +
 storage/myisam/mi_test2.c                     |  2 +
 storage/myisam/mi_test3.c                     |  2 +
 storage/myisam/myisam_ftdump.c                |  2 +
 storage/myisam/myisamchk.c                    |  2 +
 storage/myisam/myisamdef.h                    |  6 +++
 storage/myisam/myisamlog.c                    |  2 +
 storage/myisam/myisampack.c                   |  2 +-
 storage/myisam/rt_test.c                      |  2 +
 storage/myisam/sp_test.c                      |  1 +
 storage/myisammrg/ha_myisammrg.cc             |  3 ++
 18 files changed, 98 insertions(+), 3 deletions(-)
 create mode 100644 storage/myisam/mi_extrafunc.h

diff --git a/mysql-test/r/myisam_crash_before_flush_keys.result b/mysql-test/r/myisam_crash_before_flush_keys.result
index d3545ea47d0..43eb1625409 100644
--- a/mysql-test/r/myisam_crash_before_flush_keys.result
+++ b/mysql-test/r/myisam_crash_before_flush_keys.result
@@ -3,6 +3,8 @@
 #
 # Don't test this under valgrind, memory leaks will occur
 # Binary must be compiled with debug for crash to occur
+call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:");
+call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table");
 SET GLOBAL delay_key_write=ALL;
 CREATE TABLE t1(a INT, 
 b INT, 
diff --git a/mysql-test/t/myisam_crash_before_flush_keys.test b/mysql-test/t/myisam_crash_before_flush_keys.test
index 1860ddd27e3..0b0491098cd 100644
--- a/mysql-test/t/myisam_crash_before_flush_keys.test
+++ b/mysql-test/t/myisam_crash_before_flush_keys.test
@@ -8,6 +8,9 @@
 --echo # Binary must be compiled with debug for crash to occur
 --source include/have_debug.inc
 
+call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:");
+call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table");
+
 let $MYSQLD_DATADIR= `select @@datadir`;
 SET GLOBAL delay_key_write=ALL;
 CREATE TABLE t1(a INT, 
diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt
index 829d89a798a..9a0aa06e861 100755
--- a/storage/myisam/CMakeLists.txt
+++ b/storage/myisam/CMakeLists.txt
@@ -26,7 +26,8 @@ SET(MYISAM_SOURCES  ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c
 				mi_rfirst.c mi_rlast.c mi_rnext.c mi_rnext_same.c mi_rprev.c mi_rrnd.c
 				mi_rsame.c mi_rsamepos.c mi_scan.c mi_search.c mi_static.c mi_statrec.c
 				mi_unique.c mi_update.c mi_write.c rt_index.c rt_key.c rt_mbr.c
-				rt_split.c sort.c sp_key.c ft_eval.h myisamdef.h rt_index.h mi_rkey.c)
+				rt_split.c sort.c sp_key.c ft_eval.h mi_extrafunc.h myisamdef.h
+				rt_index.h mi_rkey.c)
 
 MYSQL_STORAGE_ENGINE(MYISAM)
 
diff --git a/storage/myisam/Makefile.am b/storage/myisam/Makefile.am
index 6dd0d2bcbdb..c659d05be40 100644
--- a/storage/myisam/Makefile.am
+++ b/storage/myisam/Makefile.am
@@ -50,7 +50,7 @@ myisampack_LDADD=		@CLIENT_EXTRA_LDFLAGS@ libmyisam.a \
 noinst_PROGRAMS =	mi_test1 mi_test2 mi_test3 rt_test sp_test #ft_test1 ft_eval
 noinst_HEADERS =	myisamdef.h rt_index.h rt_key.h rt_mbr.h sp_defs.h \
 			fulltext.h ftdefs.h ft_test1.h ft_eval.h \
-			ha_myisam.h
+			ha_myisam.h mi_extrafunc.h
 mi_test1_DEPENDENCIES=	$(LIBRARIES)
 mi_test1_LDADD=		@CLIENT_EXTRA_LDFLAGS@ libmyisam.a \
 			$(top_builddir)/mysys/libmysys.a \
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index 612d02bbcd3..1dd964f520e 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -539,6 +539,45 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
   va_end(args);
 }
 
+
+/**
+  Report list of threads (and queries) accessing a table, thread_id of a
+  thread that detected corruption, ource file name and line number where
+  this corruption was detected, optional extra information (string).
+
+  This function is intended to be used when table corruption is detected.
+
+  @param[in] file      MI_INFO object.
+  @param[in] message   Optional error message.
+  @param[in] sfile     Name of source file.
+  @param[in] sline     Line number in source file.
+
+  @return void
+*/
+
+void _mi_report_crashed(MI_INFO *file, const char *message,
+                        const char *sfile, uint sline)
+{
+  THD *cur_thd;
+  LIST *element;
+  char buf[1024];
+  pthread_mutex_lock(&file->s->intern_lock);
+  if ((cur_thd= (THD*) file->in_use.data))
+    sql_print_error("Got an error from thread_id=%lu, %s:%d", cur_thd->thread_id,
+                    sfile, sline);
+  else
+    sql_print_error("Got an error from unknown thread, %s:%d", sfile, sline);
+  if (message)
+    sql_print_error("%s", message);
+  for (element= file->s->in_use; element; element= list_rest(element))
+  {
+    THD *thd= (THD*) element->data;
+    sql_print_error("%s", thd ? thd_security_context(thd, buf, sizeof(buf), 0)
+                              : "Unknown thread accessing table");
+  }
+  pthread_mutex_unlock(&file->s->intern_lock);
+}
+
 }
 
 
@@ -1894,6 +1933,7 @@ int ha_myisam::delete_table(const char *name)
 
 int ha_myisam::external_lock(THD *thd, int lock_type)
 {
+  file->in_use.data= thd;
   return mi_lock_database(file, !table->s->tmp_table ?
 			  lock_type : ((lock_type == F_UNLCK) ?
 				       F_UNLCK : F_EXTRA_LCK));
diff --git a/storage/myisam/mi_extrafunc.h b/storage/myisam/mi_extrafunc.h
new file mode 100644
index 00000000000..4aa28832c6d
--- /dev/null
+++ b/storage/myisam/mi_extrafunc.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+void _mi_report_crashed(MI_INFO *file __attribute__((unused)),
+                        const char *message __attribute__((unused)),
+                        const char *sfile __attribute__((unused)),
+                        uint sline __attribute__((unused)))
+{
+}
diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c
index 8a5866ae763..fd2094caa54 100644
--- a/storage/myisam/mi_locking.c
+++ b/storage/myisam/mi_locking.c
@@ -45,6 +45,7 @@ int mi_lock_database(MI_INFO *info, int lock_type)
     ++share->w_locks;
     ++share->tot_locks;
     info->lock_type= lock_type;
+    info->s->in_use= list_add(info->s->in_use, &info->in_use);
     DBUG_RETURN(0);
   }
 
@@ -136,6 +137,7 @@ int mi_lock_database(MI_INFO *info, int lock_type)
       }
       info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
       info->lock_type= F_UNLCK;
+      info->s->in_use= list_delete(info->s->in_use, &info->in_use);
       break;
     case F_RDLCK:
       if (info->lock_type == F_WRLCK)
@@ -182,6 +184,7 @@ int mi_lock_database(MI_INFO *info, int lock_type)
       share->r_locks++;
       share->tot_locks++;
       info->lock_type=lock_type;
+      info->s->in_use= list_add(info->s->in_use, &info->in_use);
       break;
     case F_WRLCK:
       if (info->lock_type == F_RDLCK)
@@ -231,6 +234,7 @@ int mi_lock_database(MI_INFO *info, int lock_type)
       info->invalidator=info->s->invalidator;
       share->w_locks++;
       share->tot_locks++;
+      info->s->in_use= list_add(info->s->in_use, &info->in_use);
       break;
     default:
       break;				/* Impossible */
diff --git a/storage/myisam/mi_test1.c b/storage/myisam/mi_test1.c
index f218bf4e77f..6a938c6e269 100644
--- a/storage/myisam/mi_test1.c
+++ b/storage/myisam/mi_test1.c
@@ -679,3 +679,5 @@ static void usage()
   my_print_help(my_long_options);
   my_print_variables(my_long_options);
 }
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/mi_test2.c b/storage/myisam/mi_test2.c
index 23c58638166..40732db3d4f 100644
--- a/storage/myisam/mi_test2.c
+++ b/storage/myisam/mi_test2.c
@@ -1055,3 +1055,5 @@ static void copy_key(MI_INFO *info,uint inx,uchar *rec,uchar *key_buff)
   }
   return;
 }
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/mi_test3.c b/storage/myisam/mi_test3.c
index 5bdc33b8518..44f194f5c9b 100644
--- a/storage/myisam/mi_test3.c
+++ b/storage/myisam/mi_test3.c
@@ -488,6 +488,8 @@ int test_update(MI_INFO *file,int id,int lock_type)
   return 0;
 }
 
+#include "mi_extrafunc.h"
+
 #else /* __NETWARE__ */
 
 #include <stdio.h>
diff --git a/storage/myisam/myisam_ftdump.c b/storage/myisam/myisam_ftdump.c
index 63d954242a0..7c14ae37de2 100644
--- a/storage/myisam/myisam_ftdump.c
+++ b/storage/myisam/myisam_ftdump.c
@@ -274,3 +274,5 @@ static void complain(int val) /* Kinda assert :-)  */
     exit(1);
   }
 }
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c
index 611fb6325c8..b26f3200504 100644
--- a/storage/myisam/myisamchk.c
+++ b/storage/myisam/myisamchk.c
@@ -1815,3 +1815,5 @@ void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
   va_end(args);
   DBUG_VOID_RETURN;
 }
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h
index b64c7f6bd7f..9866b44ba15 100644
--- a/storage/myisam/myisamdef.h
+++ b/storage/myisam/myisamdef.h
@@ -165,6 +165,7 @@ typedef struct st_mi_isam_share {	/* Shared between opens */
   MI_COLUMNDEF *rec;			/* Pointer to field information */
   MI_PACK    pack;			/* Data about packed records */
   MI_BLOB    *blobs;			/* Pointer to blobs */
+  LIST *in_use;                         /* List of threads using this table */
   char  *unique_file_name;		/* realpath() of index file */
   char  *data_file_name,		/* Resolved path names from symlinks */
         *index_file_name;
@@ -242,6 +243,7 @@ struct st_myisam_info {
   DYNAMIC_ARRAY *ft1_to_ft2;            /* used only in ft1->ft2 conversion */
   MEM_ROOT      ft_memroot;             /* used by the parser               */
   MYSQL_FTPARSER_PARAM *ftparser_param; /* share info between init/deinit   */
+  LIST in_use;                          /* Thread using this table          */
   char *filename;			/* parameter to open filename       */
   uchar *buff,				/* Temp area for key                */
 	*lastkey,*lastkey2;		/* Last used search key             */
@@ -385,8 +387,10 @@ typedef struct st_mi_sort_param
 #define mi_putint(x,y,nod) { uint16 boh=(nod ? (uint16) 32768 : 0) + (uint16) (y);\
 			  mi_int2store(x,boh); }
 #define mi_test_if_nod(x) (x[0] & 128 ? info->s->base.key_reflength : 0)
+#define mi_report_crashed(A, B) _mi_report_crashed((A), (B), __FILE__, __LINE__)
 #define mi_mark_crashed(x) do{(x)->s->state.changed|= STATE_CRASHED; \
                               DBUG_PRINT("error", ("Marked table crashed")); \
+                              mi_report_crashed((x), 0); \
                            }while(0)
 #define mi_mark_crashed_on_repair(x) do{(x)->s->state.changed|= \
                                         STATE_CRASHED|STATE_CRASHED_ON_REPAIR; \
@@ -764,6 +768,8 @@ int mi_open_keyfile(MYISAM_SHARE *share);
 void mi_setup_functions(register MYISAM_SHARE *share);
 my_bool mi_dynmap_file(MI_INFO *info, my_off_t size);
 void mi_remap_file(MI_INFO *info, my_off_t size);
+void _mi_report_crashed(MI_INFO *file, const char *message,
+                        const char *sfile, uint sline);
 
     /* Functions needed by mi_check */
 volatile int *killed_ptr(MI_CHECK *param);
diff --git a/storage/myisam/myisamlog.c b/storage/myisam/myisamlog.c
index fafb5140a5e..47667a52d16 100644
--- a/storage/myisam/myisamlog.c
+++ b/storage/myisam/myisamlog.c
@@ -845,3 +845,5 @@ static my_bool cmp_filename(struct file_info *file_info, char * name)
     return 1;
   return strcmp(file_info->name,name) ? 1 : 0;
 }
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c
index 908c32e48d3..f63e8087fd5 100644
--- a/storage/myisam/myisampack.c
+++ b/storage/myisam/myisampack.c
@@ -3201,4 +3201,4 @@ static int fakecmp(my_off_t **count1, my_off_t **count2)
 }
 #endif
 
-
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/rt_test.c b/storage/myisam/rt_test.c
index 7d15afd12ef..4a9b61605d9 100644
--- a/storage/myisam/rt_test.c
+++ b/storage/myisam/rt_test.c
@@ -468,3 +468,5 @@ int main(int argc __attribute__((unused)),char *argv[] __attribute__((unused)))
   exit(0);
 }
 #endif /*HAVE_RTREE_KEYS*/
+
+#include "mi_extrafunc.h"
diff --git a/storage/myisam/sp_test.c b/storage/myisam/sp_test.c
index f572c7ab19b..069f43c320d 100644
--- a/storage/myisam/sp_test.c
+++ b/storage/myisam/sp_test.c
@@ -562,3 +562,4 @@ int main(int argc __attribute__((unused)),char *argv[] __attribute__((unused)))
 }
 #endif /*HAVE_SPATIAL*/
 
+#include "mi_extrafunc.h"
diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
index 471e2243aac..b28529b08e7 100644
--- a/storage/myisammrg/ha_myisammrg.cc
+++ b/storage/myisammrg/ha_myisammrg.cc
@@ -1009,7 +1009,10 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
 
 int ha_myisammrg::external_lock(THD *thd, int lock_type)
 {
+  MYRG_TABLE *tmp;
   DBUG_ASSERT(this->file->children_attached);
+  for (tmp= file->open_tables; tmp != file->end_table; tmp++)
+    tmp->table->in_use.data= thd;
   return myrg_lock_database(file,lock_type);
 }