diff -Nru mysql-5.0.67.orig/include/my_time.h mysql-5.0.67.microslow_and_userstats/include/my_time.h
--- mysql-5.0.67.orig/include/my_time.h	Mon Aug  4 15:19:12 2008
+++ mysql-5.0.67.microslow_and_userstats/include/my_time.h	Wed Sep  3 12:11:39 2008
@@ -140,7 +140,7 @@
 int my_date_to_str(const MYSQL_TIME *l_time, char *to);
 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to);
 int my_TIME_to_str(const MYSQL_TIME *l_time, char *to);
-
+ulonglong my_timer(ulonglong *ltime, ulonglong frequency);
 C_MODE_END
 
 #endif /* _my_time_h_ */
diff -Nru mysql-5.0.67.orig/include/mysql_com.h mysql-5.0.67.microslow_and_userstats/include/mysql_com.h
--- mysql-5.0.67.orig/include/mysql_com.h	Mon Aug  4 15:19:12 2008
+++ mysql-5.0.67.microslow_and_userstats/include/mysql_com.h	Wed Sep  3 12:07:46 2008
@@ -106,6 +106,8 @@
 					   thread */
 #define REFRESH_MASTER          128     /* Remove all bin logs in the index
 					   and truncate the index */
+#define REFRESH_TABLE_STATS     256     /* Refresh table stats hash table */
+#define REFRESH_INDEX_STATS     512     /* Refresh index stats hash table */
 
 /* The following can't be set with mysql_refresh() */
 #define REFRESH_READ_LOCK	16384	/* Lock tables for read */
diff -Nru mysql-5.0.67.orig/innobase/buf/buf0buf.c mysql-5.0.67.microslow_and_userstats/innobase/buf/buf0buf.c
--- mysql-5.0.67.orig/innobase/buf/buf0buf.c	Mon Aug  4 15:19:12 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/buf/buf0buf.c	Wed Sep  3 12:11:39 2008
@@ -37,6 +37,7 @@
 #include "log0log.h"
 #include "trx0undo.h"
 #include "srv0srv.h"
+#include "thr0loc.h"
 
 /*
 		IMPLEMENTATION OF THE BUFFER POOL
@@ -1086,6 +1087,31 @@
 	return(block);
 }
 
+inline void _increment_page_get_statistics(buf_block_t* block, trx_t* trx)
+{
+	ulint           block_hash;
+	ulint           block_hash_byte;
+	byte            block_hash_offset;
+
+	ut_ad(block);
+
+	if (!trx || !trx->distinct_page_access_hash)
+		return;
+
+        block_hash = ut_hash_ulint((block->space << 20) + block->space +
+					block->offset, DPAH_SIZE << 3);
+	block_hash_byte = block_hash >> 3;
+	block_hash_offset = (byte) block_hash & 0x07;
+	if (block_hash_byte < 0 || block_hash_byte >= DPAH_SIZE)
+		fprintf(stderr, "!!! block_hash_byte = %lu  block_hash_offset = %lu !!!\n", block_hash_byte, block_hash_offset);
+	if (block_hash_offset < 0 || block_hash_offset > 7)
+		fprintf(stderr, "!!! block_hash_byte = %lu  block_hash_offset = %lu !!!\n", block_hash_byte, block_hash_offset);
+	if ((trx->distinct_page_access_hash[block_hash_byte] & ((byte) 0x01 << block_hash_offset)) == 0)
+		trx->distinct_page_access++;
+	trx->distinct_page_access_hash[block_hash_byte] |= (byte) 0x01 << block_hash_offset;
+	return;
+}
+
 /************************************************************************
 This is the general function used to get access to a database page. */
 
@@ -1108,6 +1134,11 @@
 	ulint		fix_type;
 	ibool		success;
 	ibool		must_read;
+	trx_t*          trx;
+	ulint           sec;
+	ulint           ms;
+	ib_longlong     start_time;
+	ib_longlong     finish_time;
 	
 	ut_ad(mtr);
 	ut_ad((rw_latch == RW_S_LATCH)
@@ -1119,6 +1150,7 @@
 #ifndef UNIV_LOG_DEBUG
 	ut_ad(!ibuf_inside() || ibuf_page(space, offset));
 #endif
+	trx = thr_local_get_trx(os_thread_get_curr_id());
 	buf_pool->n_page_gets++;
 loop:
 	block = NULL;
@@ -1148,7 +1180,7 @@
 			return(NULL);
 		}
 
-		buf_read_page(space, offset);
+		buf_read_page(space, offset, trx);
 
 #ifdef UNIV_DEBUG
 		buf_dbg_counter++;
@@ -1261,6 +1293,11 @@
 		        /* Let us wait until the read operation
 			completes */
 
+			if (trx)
+			{
+				ut_usectime(&sec, &ms);
+				start_time = (ib_longlong)sec * 1000000 + ms;
+			}
 		        for (;;) {
 				mutex_enter(&block->mutex);
 
@@ -1276,6 +1313,12 @@
 				       break;
 				}
 			}
+                	if (trx)
+			{
+				ut_usectime(&sec, &ms);
+        	        	finish_time = (ib_longlong)sec * 1000000 + ms;
+                		trx->io_reads_wait_timer += (ulint)(finish_time - start_time);
+			}
 		}
 
 		fix_type = MTR_MEMO_BUF_FIX;
@@ -1296,12 +1339,15 @@
 		/* In the case of a first access, try to apply linear
 		read-ahead */
 
-		buf_read_ahead_linear(space, offset);
+		buf_read_ahead_linear(space, offset, trx);
 	}
 
 #ifdef UNIV_IBUF_DEBUG
 	ut_a(ibuf_count_get(block->space, block->offset) == 0);
 #endif
+
+	_increment_page_get_statistics(block, trx);
+	
 	return(block->frame);		
 }
 
@@ -1326,6 +1372,7 @@
 	ibool		accessed;
 	ibool		success;
 	ulint		fix_type;
+	trx_t*          trx;
 
 	ut_ad(mtr && block);
 	ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH));
@@ -1440,7 +1487,7 @@
 		read-ahead */
 
 		buf_read_ahead_linear(buf_frame_get_space_id(guess),
-					buf_frame_get_page_no(guess));
+					buf_frame_get_page_no(guess), trx);
 	}
 
 #ifdef UNIV_IBUF_DEBUG
@@ -1448,6 +1495,9 @@
 #endif
 	buf_pool->n_page_gets++;
 
+	trx = thr_local_get_trx(os_thread_get_curr_id());
+	_increment_page_get_statistics(block, trx);
+
 	return(TRUE);
 }
 
@@ -1470,6 +1520,7 @@
 	buf_block_t*	block;
 	ibool		success;
 	ulint		fix_type;
+	trx_t*		trx;
 
 	ut_ad(mtr);
 	ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH));
@@ -1559,6 +1610,9 @@
 #endif
 	buf_pool->n_page_gets++;
 
+	trx = thr_local_get_trx(os_thread_get_curr_id());
+	_increment_page_get_statistics(block, trx);
+
 	return(TRUE);
 }
 
diff -Nru mysql-5.0.67.orig/innobase/buf/buf0rea.c mysql-5.0.67.microslow_and_userstats/innobase/buf/buf0rea.c
--- mysql-5.0.67.orig/innobase/buf/buf0rea.c	Mon Aug  4 15:19:12 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/buf/buf0rea.c	Wed Sep  3 12:11:39 2008
@@ -70,7 +70,8 @@
 			treat the tablespace as dropped; this is a timestamp we
 			use to stop dangling page reads from a tablespace
 			which we have DISCARDed + IMPORTed back */
-	ulint	offset)	/* in: page number */
+	ulint	offset,	/* in: page number */
+	trx_t*  trx)
 {
 	buf_block_t*	block;
 	ulint		wake_later;
@@ -140,10 +141,10 @@
 
 	ut_a(block->state == BUF_BLOCK_FILE_PAGE);
 
-	*err = fil_io(OS_FILE_READ | wake_later,
+	*err = _fil_io(OS_FILE_READ | wake_later,
 			sync, space,
 			offset, 0, UNIV_PAGE_SIZE,
-			(void*)block->frame, (void*)block);
+			(void*)block->frame, (void*)block, trx);
 	ut_a(*err == DB_SUCCESS);
 
 	if (sync) {
@@ -174,8 +175,9 @@
 			the page at the given page number does not get
 			read even if we return a value > 0! */
 	ulint	space,	/* in: space id */
-	ulint	offset)	/* in: page number of a page which the current thread
+	ulint	offset,	/* in: page number of a page which the current thread
 			wants to access */
+	trx_t*  trx)
 {
 	ib_longlong	tablespace_version;
 	buf_block_t*	block;
@@ -270,7 +272,7 @@
 		if (!ibuf_bitmap_page(i)) {
 			count += buf_read_page_low(&err, FALSE, ibuf_mode
 					| OS_AIO_SIMULATED_WAKE_LATER,
-				        space, tablespace_version, i);
+				        space, tablespace_version, i, trx);
 			if (err == DB_TABLESPACE_DELETED) {
 				ut_print_timestamp(stderr);
 				fprintf(stderr,
@@ -314,7 +316,8 @@
 			/* out: number of page read requests issued: this can
 			be > 1 if read-ahead occurred */
 	ulint	space,	/* in: space id */
-	ulint	offset)	/* in: page number */
+	ulint	offset,	/* in: page number */
+	trx_t*  trx)
 {
 	ib_longlong	tablespace_version;
 	ulint		count;
@@ -323,13 +326,13 @@
 
 	tablespace_version = fil_space_get_version(space);
 
-	count = buf_read_ahead_random(space, offset);
+	count = buf_read_ahead_random(space, offset, trx);
 
 	/* We do the i/o in the synchronous aio mode to save thread
 	switches: hence TRUE */
 
 	count2 = buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
-					tablespace_version, offset);
+					tablespace_version, offset, trx);
         srv_buf_pool_reads+= count2;
 	if (err == DB_TABLESPACE_DELETED) {
 	        ut_print_timestamp(stderr);
@@ -374,8 +377,9 @@
 /*==================*/
 			/* out: number of page read requests issued */
 	ulint	space,	/* in: space id */
-	ulint	offset)	/* in: page number of a page; NOTE: the current thread
+	ulint	offset,	/* in: page number of a page; NOTE: the current thread
 			must want access to this page (see NOTE 3 above) */
+	trx_t*  trx)
 {
 	ib_longlong	tablespace_version;
 	buf_block_t*	block;
@@ -556,7 +560,7 @@
 		if (!ibuf_bitmap_page(i)) {
 			count += buf_read_page_low(&err, FALSE, ibuf_mode
 					| OS_AIO_SIMULATED_WAKE_LATER,
-					space, 	tablespace_version, i);
+					space, 	tablespace_version, i, trx);
 			if (err == DB_TABLESPACE_DELETED) {
 				ut_print_timestamp(stderr);
 				fprintf(stderr,
@@ -625,10 +629,10 @@
 	for (i = 0; i < n_stored; i++) {
 		if ((i + 1 == n_stored) && sync) {
 			buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE,
-				space_ids[i], space_versions[i], page_nos[i]);
+				space_ids[i], space_versions[i], page_nos[i], NULL);
 		} else {
 			buf_read_page_low(&err, FALSE, BUF_READ_ANY_PAGE,
-				space_ids[i], space_versions[i], page_nos[i]);
+				space_ids[i], space_versions[i], page_nos[i], NULL);
 		}
 
 		if (err == DB_TABLESPACE_DELETED) {
@@ -704,11 +708,11 @@
 
 		if ((i + 1 == n_stored) && sync) {
 			buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
-					tablespace_version, page_nos[i]);
+					tablespace_version, page_nos[i], NULL);
 		} else {
 			buf_read_page_low(&err, FALSE, BUF_READ_ANY_PAGE
 					| OS_AIO_SIMULATED_WAKE_LATER,
-				       space, tablespace_version, page_nos[i]);
+				       space, tablespace_version, page_nos[i], NULL);
 		}
 	}
 	
diff -Nru mysql-5.0.67.orig/innobase/fil/fil0fil.c mysql-5.0.67.microslow_and_userstats/innobase/fil/fil0fil.c
--- mysql-5.0.67.orig/innobase/fil/fil0fil.c	Mon Aug  4 15:19:13 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/fil/fil0fil.c	Wed Sep  3 12:11:39 2008
@@ -3527,7 +3527,7 @@
 			node->name, node->handle, buf,
 			offset_low, offset_high,
 			UNIV_PAGE_SIZE * n_pages,
-			NULL, NULL);
+			NULL, NULL, NULL);
 #endif
 		if (success) {
 			node->size += n_pages;
@@ -3851,7 +3851,7 @@
 Reads or writes data. This operation is asynchronous (aio). */
 
 ulint
-fil_io(
+_fil_io(
 /*===*/
 				/* out: DB_SUCCESS, or DB_TABLESPACE_DELETED
 				if we are trying to do i/o on a tablespace
@@ -3877,8 +3877,9 @@
 	void*	buf,		/* in/out: buffer where to store read data
 				or from where to write; in aio this must be
 				appropriately aligned */
-	void*	message)	/* in: message for aio handler if non-sync
+	void*	message,	/* in: message for aio handler if non-sync
 				aio used, else ignored */
+	trx_t*  trx)
 {
 	fil_system_t*	system		= fil_system;
 	ulint		mode;
@@ -4018,7 +4019,7 @@
 #else
 	/* Queue the aio request */
 	ret = os_aio(type, mode | wake_later, node->name, node->handle, buf,
-				offset_low, offset_high, len, node, message);
+				offset_low, offset_high, len, node, message, trx);
 #endif
 	ut_a(ret);
 
diff -Nru mysql-5.0.67.orig/innobase/include/buf0rea.h mysql-5.0.67.microslow_and_userstats/innobase/include/buf0rea.h
--- mysql-5.0.67.orig/innobase/include/buf0rea.h	Mon Aug  4 15:19:13 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/include/buf0rea.h	Wed Sep  3 12:11:39 2008
@@ -10,6 +10,7 @@
 #define buf0rea_h
 
 #include "univ.i"
+#include "trx0types.h"
 #include "buf0types.h"
 
 /************************************************************************
@@ -25,7 +26,8 @@
 			/* out: number of page read requests issued: this can
 			be > 1 if read-ahead occurred */
 	ulint	space,	/* in: space id */
-	ulint	offset);/* in: page number */
+	ulint	offset,	/* in: page number */
+	trx_t*  trx);
 /************************************************************************
 Applies linear read-ahead if in the buf_pool the page is a border page of
 a linear read-ahead area and all the pages in the area have been accessed.
@@ -55,8 +57,9 @@
 /*==================*/
 			/* out: number of page read requests issued */
 	ulint	space,	/* in: space id */
-	ulint	offset);/* in: page number of a page; NOTE: the current thread
+	ulint	offset,	/* in: page number of a page; NOTE: the current thread
 			must want access to this page (see NOTE 3 above) */
+	trx_t*  trx);
 /************************************************************************
 Issues read requests for pages which the ibuf module wants to read in, in
 order to contract the insert buffer tree. Technically, this function is like
diff -Nru mysql-5.0.67.orig/innobase/include/fil0fil.h mysql-5.0.67.microslow_and_userstats/innobase/include/fil0fil.h
--- mysql-5.0.67.orig/innobase/include/fil0fil.h	Mon Aug  4 15:19:13 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/include/fil0fil.h	Wed Sep  3 12:11:39 2008
@@ -534,8 +534,11 @@
 /************************************************************************
 Reads or writes data. This operation is asynchronous (aio). */
 
+#define fil_io(type, sync, space_id, block_offset, byte_offset, len, buf, message) \
+	_fil_io(type, sync, space_id, block_offset, byte_offset, len, buf, message, NULL)
+
 ulint
-fil_io(
+_fil_io(
 /*===*/
 				/* out: DB_SUCCESS, or DB_TABLESPACE_DELETED
 				if we are trying to do i/o on a tablespace
@@ -561,8 +564,9 @@
 	void*	buf,		/* in/out: buffer where to store read data
 				or from where to write; in aio this must be
 				appropriately aligned */
-	void*	message);	/* in: message for aio handler if non-sync
+	void*	message,	/* in: message for aio handler if non-sync
 				aio used, else ignored */
+	trx_t*  trx);
 /************************************************************************
 Reads data from a space to a buffer. Remember that the possible incomplete
 blocks at the end of file are ignored: they are not taken into account when
diff -Nru mysql-5.0.67.orig/innobase/include/os0file.h mysql-5.0.67.microslow_and_userstats/innobase/include/os0file.h
--- mysql-5.0.67.orig/innobase/include/os0file.h	Mon Aug  4 15:19:14 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/include/os0file.h	Wed Sep  3 12:11:39 2008
@@ -11,6 +11,8 @@
 
 #include "univ.i"
 
+#include "trx0types.h"
+
 #ifndef __WIN__
 #include <dirent.h>
 #include <sys/stat.h>
@@ -421,8 +423,11 @@
 /***********************************************************************
 Requests a synchronous read operation. */
 
+#define os_file_read(file, buf, offset, offset_high, n)         \
+		_os_file_read(file, buf, offset, offset_high, n, NULL)
+
 ibool
-os_file_read(
+_os_file_read(
 /*=========*/
 				/* out: TRUE if request was
 				successful, FALSE if fail */
@@ -432,7 +437,8 @@
 				offset where to read */
 	ulint		offset_high,/* in: most significant 32 bits of
 				offset */
-	ulint		n);	/* in: number of bytes to read */	
+	ulint		n,	/* in: number of bytes to read */
+	trx_t*		trx);
 /***********************************************************************
 Rewind file to its start, read at most size - 1 bytes from it to str, and
 NUL-terminate str. All errors are silently ignored. This function is
@@ -584,7 +590,8 @@
 				can be used to identify a completed aio
 				operation); if mode is OS_AIO_SYNC, these
 				are ignored */
-	void*		message2);
+	void*		message2,
+	trx_t*          trx);
 /****************************************************************************
 Wakes up all async i/o threads so that they know to exit themselves in
 shutdown. */
diff -Nru mysql-5.0.67.orig/innobase/include/thr0loc.h mysql-5.0.67.microslow_and_userstats/innobase/include/thr0loc.h
--- mysql-5.0.67.orig/innobase/include/thr0loc.h	Mon Aug  4 15:19:15 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/include/thr0loc.h	Wed Sep  3 12:11:39 2008
@@ -15,6 +15,7 @@
 
 #include "univ.i"
 #include "os0thread.h"
+#include "trx0trx.h"
 
 /********************************************************************
 Initializes the thread local storage module. */
@@ -36,6 +37,14 @@
 /*===========*/
 	os_thread_id_t	id);	/* in: thread id */
 /***********************************************************************
+Gets trx */
+
+trx_t*
+thr_local_get_trx(
+/*==================*/
+				/* out: trx for mysql */
+	os_thread_id_t	id);	/* in: thread id of the thread */
+/***********************************************************************
 Gets the slot number in the thread table of a thread. */
 
 ulint
@@ -46,6 +55,14 @@
 /***********************************************************************
 Sets in the local storage the slot number in the thread table of a thread. */
 
+void
+thr_local_set_trx(
+/*==================*/
+	os_thread_id_t	id,	/* in: thread id of the thread */
+	trx_t*		trx);	/* in: slot number */
+/***********************************************************************
+Sets in the local storage the slot number in the thread table of a thread. */
+
 void
 thr_local_set_slot_no(
 /*==================*/
diff -Nru mysql-5.0.67.orig/innobase/include/trx0trx.h mysql-5.0.67.microslow_and_userstats/innobase/include/trx0trx.h
--- mysql-5.0.67.orig/innobase/include/trx0trx.h	Mon Aug  4 15:19:15 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/include/trx0trx.h	Wed Sep  3 12:11:39 2008
@@ -668,6 +668,17 @@
 	/*------------------------------*/
 	char detailed_error[256];	/* detailed error message for last
 					error, or empty. */
+	/*------------------------------*/
+	os_thread_id_t	trx_thread_id;
+	ulint		io_reads;
+	ib_longlong     io_read;
+	ulint		io_reads_wait_timer;
+	ib_longlong     lock_que_wait_ustarted;
+	ulint           lock_que_wait_timer;
+	ulint           innodb_que_wait_timer;
+	ulint           distinct_page_access;
+#define	DPAH_SIZE	8192
+	byte*		distinct_page_access_hash;
 };
 
 #define TRX_MAX_N_THREADS	32	/* maximum number of concurrent
diff -Nru mysql-5.0.67.orig/innobase/lock/lock0lock.c mysql-5.0.67.microslow_and_userstats/innobase/lock/lock0lock.c
--- mysql-5.0.67.orig/innobase/lock/lock0lock.c	Mon Aug  4 15:19:16 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/lock/lock0lock.c	Wed Sep  3 12:11:39 2008
@@ -1806,6 +1806,8 @@
 {
 	lock_t*	lock;
 	trx_t*	trx;
+	ulint   sec;
+	ulint   ms;
 	
 #ifdef UNIV_SYNC_DEBUG
 	ut_ad(mutex_own(&kernel_mutex));
@@ -1861,6 +1863,8 @@
 	trx->que_state = TRX_QUE_LOCK_WAIT;
 	trx->was_chosen_as_deadlock_victim = FALSE;
 	trx->wait_started = time(NULL);
+	ut_usectime(&sec, &ms);
+	trx->lock_que_wait_ustarted = (ib_longlong)sec * 1000000 + ms;
 
 	ut_a(que_thr_stop(thr));
 
@@ -3514,7 +3518,9 @@
 {
 	lock_t*	lock;
 	trx_t*	trx;
-	
+	ulint   sec;
+	ulint   ms;
+
 #ifdef UNIV_SYNC_DEBUG
 	ut_ad(mutex_own(&kernel_mutex));
 #endif /* UNIV_SYNC_DEBUG */
@@ -3563,7 +3569,10 @@
 	
 		return(DB_SUCCESS);
 	}
-	
+
+	trx->wait_started = time(NULL);
+	ut_usectime(&sec, &ms);
+	trx->lock_que_wait_ustarted = (ib_longlong)sec * 1000000 + ms;
 	trx->que_state = TRX_QUE_LOCK_WAIT;
 	trx->was_chosen_as_deadlock_victim = FALSE;
 	trx->wait_started = time(NULL);
@@ -4289,7 +4298,7 @@
 	ulint	i;
 	mtr_t	mtr;
 	trx_t*	trx;
-
+	
 	fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
 
 	/* First print info on non-active transactions */
diff -Nru mysql-5.0.67.orig/innobase/os/os0file.c mysql-5.0.67.microslow_and_userstats/innobase/os/os0file.c
--- mysql-5.0.67.orig/innobase/os/os0file.c	Mon Aug  4 15:19:16 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/os/os0file.c	Wed Sep  3 12:11:39 2008
@@ -14,6 +14,7 @@
 #include "srv0start.h"
 #include "fil0fil.h"
 #include "buf0buf.h"
+#include "trx0sys.h"
 
 #if defined(UNIV_HOTBACKUP) && defined(__WIN__)
 /* Add includes for the _stat() call to compile on Windows */
@@ -101,6 +102,7 @@
 	struct aiocb	control;	/* Posix control block for aio
 					request */
 #endif
+        trx_t*		trx;
 };
 
 /* The aio array structure */
@@ -1903,9 +1905,13 @@
 #ifndef __WIN__
 /***********************************************************************
 Does a synchronous read operation in Posix. */
+
+#define os_file_pread(file, buf, n, offset, offset_high)        \
+		_os_file_pread(file, buf, n, offset, offset_high, NULL);
+
 static
 ssize_t
-os_file_pread(
+_os_file_pread(
 /*==========*/
 				/* out: number of bytes read, -1 if error */
 	os_file_t	file,	/* in: handle to a file */
@@ -1913,12 +1919,17 @@
 	ulint		n,	/* in: number of bytes to read */	
 	ulint		offset,	/* in: least significant 32 bits of file
 				offset from where to read */
-	ulint		offset_high) /* in: most significant 32 bits of
+	ulint		offset_high, /* in: most significant 32 bits of
 				offset */
+        trx_t*		trx)
 {
         off_t	offs;
 	ssize_t	n_bytes;
-
+	ulint           sec;
+	ulint           ms;
+	ib_longlong     start_time;
+	ib_longlong     finish_time;
+	
 	ut_a((offset & 0xFFFFFFFFUL) == offset);
         
         /* If off_t is > 4 bytes in size, then we assume we can pass a
@@ -1937,7 +1948,13 @@
         }
 
 	os_n_file_reads++;
-
+	if (trx)
+	{
+	        trx->io_reads++;
+		trx->io_read += n;
+		ut_usectime(&sec, &ms);
+		start_time = (ib_longlong)sec * 1000000 + ms;
+	}
 #if defined(HAVE_PREAD) && !defined(HAVE_BROKEN_PREAD)
         os_mutex_enter(os_file_count_mutex);
 	os_file_n_pending_preads++;
@@ -1951,6 +1968,13 @@
 	os_n_pending_reads--;
         os_mutex_exit(os_file_count_mutex);
 
+        if (trx)
+        {
+		ut_usectime(&sec, &ms);
+        	finish_time = (ib_longlong)sec * 1000000 + ms;
+                trx->io_reads_wait_timer += (ulint)(finish_time - start_time);
+	}
+
 	return(n_bytes);
 #else
 	{
@@ -1981,6 +2005,13 @@
 	os_n_pending_reads--;
         os_mutex_exit(os_file_count_mutex);
 
+        if (trx)
+        {
+		ut_usectime(&sec, &ms);
+        	finish_time = (ib_longlong)sec * 1000000 + ms;
+                trx->io_reads_wait_timer += (ulint)(finish_time - start_time);
+	}
+
 	return(ret);
 	}
 #endif
@@ -2103,7 +2134,7 @@
 Requests a synchronous positioned read operation. */
 
 ibool
-os_file_read(
+_os_file_read(
 /*=========*/
 				/* out: TRUE if request was
 				successful, FALSE if fail */
@@ -2113,7 +2144,8 @@
 				offset where to read */
 	ulint		offset_high, /* in: most significant 32 bits of
 				offset */
-	ulint		n)	/* in: number of bytes to read */	
+	ulint		n,	/* in: number of bytes to read */
+        trx_t*		trx)
 {
 #ifdef __WIN__
 	BOOL		ret;
@@ -2128,8 +2160,7 @@
 
 	os_n_file_reads++;
 	os_bytes_read_since_printout += n;
-
-try_again:	
+try_again:
 	ut_ad(file);
 	ut_ad(buf);
 	ut_ad(n > 0);
@@ -2177,7 +2208,7 @@
 	os_bytes_read_since_printout += n;
 
 try_again:
-	ret = os_file_pread(file, buf, n, offset, offset_high);
+	ret = _os_file_pread(file, buf, n, offset, offset_high, trx);
 
 	if ((ulint)ret == n) {
 
@@ -3137,7 +3168,8 @@
 				offset */
 	ulint		offset_high, /* in: most significant 32 bits of
 				offset */
-	ulint		len)	/* in: length of the block to read or write */
+	ulint		len,	/* in: length of the block to read or write */
+	trx_t*          trx)
 {
 	os_aio_slot_t*	slot;
 #ifdef WIN_ASYNC_IO
@@ -3196,7 +3228,7 @@
 	slot->offset   = offset;
 	slot->offset_high = offset_high;
 	slot->io_already_done = FALSE;
-	
+
 #ifdef WIN_ASYNC_IO		
 	control = &(slot->control);
 	control->Offset = (DWORD)offset;
@@ -3390,7 +3422,8 @@
 				can be used to identify a completed aio
 				operation); if mode is OS_AIO_SYNC, these
 				are ignored */
-	void*		message2)
+	void*		message2,
+	trx_t*          trx)
 {
 	os_aio_array_t*	array;
 	os_aio_slot_t*	slot;
@@ -3429,8 +3462,8 @@
 		wait in the Windows case. */
 
 		if (type == OS_FILE_READ) {
-			return(os_file_read(file, buf, offset,
-							offset_high, n));
+			return(_os_file_read(file, buf, offset,
+							offset_high, n, trx));
 		}
 
 		ut_a(type == OS_FILE_WRITE);
@@ -3463,14 +3496,19 @@
 		ut_error;
 	}
 	
+	if (trx && type == OS_FILE_READ)
+	{
+		trx->io_reads++;
+		trx->io_read += n;
+	}
 	slot = os_aio_array_reserve_slot(type, array, message1, message2, file,
-					name, buf, offset, offset_high, n);
+					name, buf, offset, offset_high, n, trx);
 	if (type == OS_FILE_READ) {
 		if (os_aio_use_native_aio) {
 #ifdef WIN_ASYNC_IO
 			os_n_file_reads++;
 			os_bytes_read_since_printout += len;
-			
+
 			ret = ReadFile(file, buf, (DWORD)n, &len,
 							&(slot->control));
 #elif defined(POSIX_ASYNC_IO)
@@ -4038,7 +4076,7 @@
 
 			ut_memcpy(consecutive_ios[i]->buf, combined_buf + offs, 
 						consecutive_ios[i]->len);
-			offs += consecutive_ios[i]->len;
+			offs += consecutive_ios[i]->len;			
 		}
 	}
 
@@ -4050,9 +4088,8 @@
 
 	/* Mark the i/os done in slots */
 
-	for (i = 0; i < n_consecutive; i++) {
+	for (i = 0; i < n_consecutive; i++) 
 		consecutive_ios[i]->io_already_done = TRUE;
-	}
 
 	/* We return the messages for the first slot now, and if there were
 	several slots, the messages will be returned with subsequent calls
diff -Nru mysql-5.0.67.orig/innobase/srv/srv0srv.c mysql-5.0.67.microslow_and_userstats/innobase/srv/srv0srv.c
--- mysql-5.0.67.orig/innobase/srv/srv0srv.c	Mon Aug  4 15:19:17 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/srv/srv0srv.c	Wed Sep  3 12:11:39 2008
@@ -996,6 +996,10 @@
 	ibool			has_slept = FALSE;
 	srv_conc_slot_t*	slot	  = NULL;
 	ulint			i;
+	ib_longlong             start_time = 0L;
+	ib_longlong             finish_time = 0L;
+	ulint                   sec;
+	ulint                   ms;
 
 	/* If trx has 'free tickets' to enter the engine left, then use one
 	such ticket */
@@ -1054,6 +1058,7 @@
     if (SRV_THREAD_SLEEP_DELAY > 0)
     {
       os_thread_sleep(SRV_THREAD_SLEEP_DELAY);
+      trx->innodb_que_wait_timer += SRV_THREAD_SLEEP_DELAY;
     }
 
 		trx->op_info = "";
@@ -1109,12 +1114,19 @@
 	/* Go to wait for the event; when a thread leaves InnoDB it will
 	release this thread */
 
+	ut_usectime(&sec, &ms);
+	start_time = (ib_longlong)sec * 1000000 + ms;
+
 	trx->op_info = "waiting in InnoDB queue";
 
 	os_event_wait(slot->event);
 
 	trx->op_info = "";
 
+	ut_usectime(&sec, &ms);
+	finish_time = (ib_longlong)sec * 1000000 + ms;
+	trx->innodb_que_wait_timer += (ulint)(finish_time - start_time);
+
 	os_fast_mutex_lock(&srv_conc_mutex);
 
 	srv_conc_n_waiting_threads--;
diff -Nru mysql-5.0.67.orig/innobase/thr/thr0loc.c mysql-5.0.67.microslow_and_userstats/innobase/thr/thr0loc.c
--- mysql-5.0.67.orig/innobase/thr/thr0loc.c	Mon Aug  4 15:19:17 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/thr/thr0loc.c	Wed Sep  3 12:11:39 2008
@@ -45,6 +45,7 @@
 				for this thread */
 	ibool		in_ibuf;/* TRUE if the the thread is doing an ibuf
 				operation */
+	trx_t*          trx;
 	hash_node_t	hash;	/* hash chain node */
 	ulint		magic_n;
 };
@@ -113,6 +114,29 @@
 }
 
 /***********************************************************************
+Gets trx */
+
+trx_t*
+thr_local_get_trx(
+/*==================*/
+				/* out: trx for mysql */
+	os_thread_id_t	id)	/* in: thread id of the thread */
+{
+	trx_t*          trx;
+	thr_local_t*	local;
+
+	mutex_enter(&thr_local_mutex);
+
+	local = thr_local_get(id);
+
+	trx = local->trx;
+
+	mutex_exit(&thr_local_mutex);
+
+	return(trx);
+}
+
+/***********************************************************************
 Sets the slot number in the thread table of a thread. */
 
 void
@@ -124,11 +148,31 @@
 	thr_local_t*	local;
 
 	mutex_enter(&thr_local_mutex);
-	
+
 	local = thr_local_get(id);
 
 	local->slot_no = slot_no;
-	
+
+	mutex_exit(&thr_local_mutex);
+}
+
+/***********************************************************************
+Sets trx */
+
+void
+thr_local_set_trx(
+/*==================*/
+	os_thread_id_t	id,	/* in: thread id of the thread */
+	trx_t*		trx)	/* in: trx */
+{
+	thr_local_t*	local;
+
+	mutex_enter(&thr_local_mutex);
+
+	local = thr_local_get(id);
+
+	local->trx = trx;
+
 	mutex_exit(&thr_local_mutex);
 }
 
@@ -172,6 +216,7 @@
 	local->magic_n = THR_LOCAL_MAGIC_N;
 
  	local->in_ibuf = FALSE;
+ 	local->trx = NULL;
 	
 	mutex_enter(&thr_local_mutex);
 
diff -Nru mysql-5.0.67.orig/innobase/trx/trx0trx.c mysql-5.0.67.microslow_and_userstats/innobase/trx/trx0trx.c
--- mysql-5.0.67.orig/innobase/trx/trx0trx.c	Mon Aug  4 15:19:17 2008
+++ mysql-5.0.67.microslow_and_userstats/innobase/trx/trx0trx.c	Wed Sep  3 12:11:39 2008
@@ -190,6 +190,16 @@
 	trx->global_read_view_heap = mem_heap_create(256);
 	trx->global_read_view = NULL;
 	trx->read_view = NULL;
+	
+	trx->io_reads = 0;
+	trx->io_read = 0;
+	trx->io_reads_wait_timer = 0;
+	trx->lock_que_wait_timer = 0;
+	trx->innodb_que_wait_timer = 0;
+	trx->distinct_page_access = 0;
+	trx->distinct_page_access_hash = NULL;
+	trx->trx_thread_id = os_thread_get_curr_id();
+	thr_local_set_trx(trx->trx_thread_id, NULL);
 
 	/* Set X/Open XA transaction identification to NULL */
 	memset(&trx->xid, 0, sizeof(trx->xid));
@@ -230,6 +240,10 @@
 
 	trx->mysql_process_no = os_proc_get_number();
 	
+	trx->distinct_page_access_hash = mem_alloc(DPAH_SIZE);
+	memset(trx->distinct_page_access_hash, 0, DPAH_SIZE);
+	thr_local_set_trx(trx->mysql_thread_id, trx);
+
 	return(trx);
 }
 
@@ -355,6 +369,8 @@
 
 	ut_a(trx->read_view == NULL);
 	
+	thr_local_free(trx->trx_thread_id);
+	
 	mem_free(trx);
 }
 
@@ -366,6 +382,12 @@
 /*===============*/
 	trx_t*	trx)	/* in, own: trx object */
 {
+	if (trx->distinct_page_access_hash)
+	{
+		mem_free(trx->distinct_page_access_hash);
+		trx->distinct_page_access_hash= NULL;
+	}
+
 	thr_local_free(trx->mysql_thread_id);
 
 	mutex_enter(&kernel_mutex);
@@ -1064,7 +1086,10 @@
 	trx_t*	trx)	/* in: transaction */
 {
 	que_thr_t*	thr;
-
+	ulint           sec;
+	ulint           ms;
+	ib_longlong     now;
+	
 #ifdef UNIV_SYNC_DEBUG
 	ut_ad(mutex_own(&kernel_mutex));
 #endif /* UNIV_SYNC_DEBUG */
@@ -1080,6 +1105,9 @@
 		thr = UT_LIST_GET_FIRST(trx->wait_thrs);
 	}
 
+	ut_usectime(&sec, &ms);
+	now = (ib_longlong)sec * 1000000 + ms;
+	trx->lock_que_wait_timer += (ulint)(now - trx->lock_que_wait_ustarted);
 	trx->que_state = TRX_QUE_RUNNING;
 }
 
@@ -1093,6 +1121,9 @@
 	trx_t*	trx)	/* in: transaction in the TRX_QUE_LOCK_WAIT state */
 {
 	que_thr_t*	thr;
+	ulint           sec;
+	ulint           ms;
+	ib_longlong     now;
 
 #ifdef UNIV_SYNC_DEBUG
 	ut_ad(mutex_own(&kernel_mutex));
@@ -1109,6 +1140,9 @@
 		thr = UT_LIST_GET_FIRST(trx->wait_thrs);
 	}
 
+	ut_usectime(&sec, &ms);
+	now = (ib_longlong)sec * 1000000 + ms;
+	trx->lock_que_wait_timer += (ulint)(now - trx->lock_que_wait_ustarted);
 	trx->que_state = TRX_QUE_RUNNING;
 }
 
diff -Nru mysql-5.0.67.orig/scripts/mysqldumpslow.sh mysql-5.0.67.microslow_and_userstats/scripts/mysqldumpslow.sh
--- mysql-5.0.67.orig/scripts/mysqldumpslow.sh	Mon Aug  4 15:20:02 2008
+++ mysql-5.0.67.microslow_and_userstats/scripts/mysqldumpslow.sh	Wed Sep  3 12:11:39 2008
@@ -83,8 +83,8 @@
     s/^#? Time: \d{6}\s+\d+:\d+:\d+.*\n//;
     my ($user,$host) = s/^#? User\@Host:\s+(\S+)\s+\@\s+(\S+).*\n// ? ($1,$2) : ('','');
 
-    s/^# Query_time: (\d+)  Lock_time: (\d+)  Rows_sent: (\d+).*\n//;
-    my ($t, $l, $r) = ($1, $2, $3);
+    s/^# Query_time: (\d+(\.\d+)?)  Lock_time: (\d+(\.\d+)?)  Rows_sent: (\d+(\.\d+)?).*\n//;
+    my ($t, $l, $r) = ($1, $3, $5);
     $t -= $l unless $opt{l};
 
     # remove fluff that mysqld writes to log when it (re)starts:
diff -Nru mysql-5.0.67.orig/sql/filesort.cc mysql-5.0.67.microslow_and_userstats/sql/filesort.cc
--- mysql-5.0.67.orig/sql/filesort.cc	Mon Aug  4 15:20:03 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/filesort.cc	Wed Sep  3 12:11:39 2008
@@ -180,6 +180,7 @@
   {
     statistic_increment(thd->status_var.filesort_scan_count, &LOCK_status);
   }
+  thd->query_plan_flags|= QPLAN_FILESORT;
 #ifdef CAN_TRUST_RANGE
   if (select && select->quick && select->quick->records > 0L)
   {
@@ -245,6 +246,7 @@
   }
   else
   {
+    thd->query_plan_flags|= QPLAN_FILESORT_DISK;
     if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
     {
       x_free(table_sort.buffpek);
@@ -1076,6 +1078,7 @@
 
   statistic_increment(current_thd->status_var.filesort_merge_passes,
 		      &LOCK_status);
+  current_thd->query_plan_fsort_passes++;
   if (param->not_killable)
   {
     killed= &not_killable;
diff -Nru mysql-5.0.67.orig/sql/ha_innodb.cc mysql-5.0.67.microslow_and_userstats/sql/ha_innodb.cc
--- mysql-5.0.67.orig/sql/ha_innodb.cc	Mon Aug  4 15:20:03 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/ha_innodb.cc	Wed Sep  3 12:11:39 2008
@@ -1,3 +1,4 @@
+
 /* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
 
    This program is free software; you can redistribute it and/or modify
@@ -3286,6 +3287,8 @@
 
 	error = row_insert_for_mysql((byte*) record, prebuilt);
 
+        if (error == DB_SUCCESS) rows_changed++;
+
 	if (error == DB_SUCCESS && auto_inc_used) {
 
         	/* Fetch the value that was set in the autoincrement field */
@@ -3558,6 +3561,8 @@
 		}
 	}
 
+	if (error == DB_SUCCESS) rows_changed++;
+
 	innodb_srv_conc_exit_innodb(prebuilt->trx);
 
 	error = convert_error_code_to_mysql(error, user_thd);
@@ -3606,6 +3611,8 @@
 
 	error = row_update_for_mysql((byte*) record, prebuilt);
 
+	if (error == DB_SUCCESS) rows_changed++;
+
 	innodb_srv_conc_exit_innodb(prebuilt->trx);
 
 	error = convert_error_code_to_mysql(error, user_thd);
@@ -3885,6 +3892,9 @@
 	if (ret == DB_SUCCESS) {
 		error = 0;
 		table->status = 0;
+                rows_read++;
+                if (active_index >= 0 && active_index < MAX_KEY)
+                        index_rows_read[active_index]++;
 
 	} else if (ret == DB_RECORD_NOT_FOUND) {
 		error = HA_ERR_KEY_NOT_FOUND;
@@ -4038,6 +4048,9 @@
 	if (ret == DB_SUCCESS) {
 		error = 0;
 		table->status = 0;
+                rows_read++;
+                if (active_index >= 0 && active_index < MAX_KEY)
+                        index_rows_read[active_index]++;
 
 	} else if (ret == DB_RECORD_NOT_FOUND) {
 		error = HA_ERR_END_OF_FILE;
@@ -6096,6 +6109,7 @@
 {
 	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
 	trx_t*		trx;
+	int i;
 
   	DBUG_ENTER("ha_innobase::external_lock");
 	DBUG_PRINT("enter",("lock_type: %d", lock_type));
@@ -6219,7 +6233,24 @@
 
 	if (trx->n_mysql_tables_in_use == 0) {
 
-	        trx->mysql_n_tables_locked = 0;
+		current_thd->innodb_was_used = TRUE;
+		current_thd->innodb_io_reads += trx->io_reads;
+		current_thd->innodb_io_read += trx->io_read;
+		current_thd->innodb_io_reads_wait_timer += trx->io_reads_wait_timer;
+		current_thd->innodb_lock_que_wait_timer += trx->lock_que_wait_timer;
+		current_thd->innodb_innodb_que_wait_timer += trx->innodb_que_wait_timer;
+                current_thd->innodb_page_access += trx->distinct_page_access;
+
+		trx->io_reads = 0;
+		trx->io_read = 0;
+		trx->io_reads_wait_timer = 0;
+		trx->lock_que_wait_timer = 0;
+		trx->innodb_que_wait_timer = 0;
+		trx->distinct_page_access = 0;
+		if (trx->distinct_page_access_hash)
+			memset(trx->distinct_page_access_hash, 0, DPAH_SIZE);
+
+    		trx->mysql_n_tables_locked = 0;
 		prebuilt->used_in_HANDLER = FALSE;
 
 		if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
diff -Nru mysql-5.0.67.orig/sql/ha_innodb.cc.orig mysql-5.0.67.microslow_and_userstats/sql/ha_innodb.cc.orig
--- mysql-5.0.67.orig/sql/ha_innodb.cc.orig	Thu Jan  1 02:00:00 1970
+++ mysql-5.0.67.microslow_and_userstats/sql/ha_innodb.cc.orig	Wed Sep  3 12:07:46 2008
@@ -0,0 +1,7421 @@
+/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
+
+   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 */
+
+/* This file defines the InnoDB handler: the interface between MySQL and InnoDB
+NOTE: You can only use noninlined InnoDB functions in this file, because we
+have disabled the InnoDB inlining in this file. */
+
+/* TODO list for the InnoDB handler in 5.0:
+  - Remove the flag trx->active_trans and look at the InnoDB
+    trx struct state field
+  - fix savepoint functions to use savepoint storage area
+  - Find out what kind of problems the OS X case-insensitivity causes to
+    table and database names; should we 'normalize' the names like we do
+    in Windows?
+*/
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation				// gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "slave.h"
+
+#ifdef HAVE_INNOBASE_DB
+#include <m_ctype.h>
+#include <hash.h>
+#include <myisampack.h>
+#include <mysys_err.h>
+#include <my_sys.h>
+
+#define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1))
+
+#include "ha_innodb.h"
+
+pthread_mutex_t innobase_share_mutex, /* to protect innobase_open_files */
+                prepare_commit_mutex; /* to force correct commit order in
+				      binlog */
+ulong commit_threads= 0;
+pthread_mutex_t commit_threads_m;
+pthread_cond_t commit_cond;
+pthread_mutex_t commit_cond_m;
+bool innodb_inited= 0;
+
+/*-----------------------------------------------------------------*/
+/* These variables are used to implement (semi-)synchronous MySQL binlog
+replication for InnoDB tables. */
+
+pthread_cond_t  innobase_repl_cond;             /* Posix cond variable;
+                                                this variable is signaled
+                                                when enough binlog has been
+                                                sent to slave, so that a
+                                                waiting trx can return the
+                                                'ok' message to the client
+                                                for a commit */
+pthread_mutex_t innobase_repl_cond_mutex;       /* Posix cond variable mutex
+                                                that also protects the next
+                                                innobase_repl_... variables */
+uint            innobase_repl_state;            /* 1 if synchronous replication
+                                                is switched on and is working
+                                                ok; else 0 */
+uint            innobase_repl_file_name_inited  = 0; /* This is set to 1 when
+                                                innobase_repl_file_name
+                                                contains meaningful data */
+char*           innobase_repl_file_name;        /* The binlog name up to which
+                                                we have sent some binlog to
+                                                the slave */
+my_off_t        innobase_repl_pos;              /* The position in that file
+                                                up to which we have sent the
+                                                binlog to the slave */
+uint            innobase_repl_n_wait_threads    = 0; /* This tells how many
+                                                transactions currently are
+                                                waiting for the binlog to be
+                                                sent to the client */
+uint            innobase_repl_wait_file_name_inited = 0; /* This is set to 1
+                                                when we know the 'smallest'
+                                                wait position */
+char*           innobase_repl_wait_file_name;   /* NULL, or the 'smallest'
+                                                innobase_repl_file_name that
+                                                a transaction is waiting for */
+my_off_t        innobase_repl_wait_pos;         /* The smallest position in
+                                                that file that a trx is
+                                                waiting for: the trx can
+                                                proceed and send an 'ok' to
+                                                the client when MySQL has sent
+                                                the binlog up to this position
+                                                to the slave */
+/*-----------------------------------------------------------------*/
+
+
+
+/* Store MySQL definition of 'byte': in Linux it is char while InnoDB
+uses unsigned char; the header univ.i which we include next defines
+'byte' as a macro which expands to 'unsigned char' */
+
+typedef byte	mysql_byte;
+
+#define INSIDE_HA_INNOBASE_CC
+
+/* Include necessary InnoDB headers */
+extern "C" {
+#include "../innobase/include/univ.i"
+#include "../innobase/include/os0file.h"
+#include "../innobase/include/os0thread.h"
+#include "../innobase/include/srv0start.h"
+#include "../innobase/include/srv0srv.h"
+#include "../innobase/include/trx0roll.h"
+#include "../innobase/include/trx0trx.h"
+#include "../innobase/include/trx0sys.h"
+#include "../innobase/include/mtr0mtr.h"
+#include "../innobase/include/row0ins.h"
+#include "../innobase/include/row0mysql.h"
+#include "../innobase/include/row0sel.h"
+#include "../innobase/include/row0upd.h"
+#include "../innobase/include/log0log.h"
+#include "../innobase/include/lock0lock.h"
+#include "../innobase/include/dict0crea.h"
+#include "../innobase/include/btr0cur.h"
+#include "../innobase/include/btr0btr.h"
+#include "../innobase/include/fsp0fsp.h"
+#include "../innobase/include/sync0sync.h"
+#include "../innobase/include/fil0fil.h"
+#include "../innobase/include/trx0xa.h"
+}
+
+#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */
+#define HA_INNOBASE_RANGE_COUNT	  100
+
+ulong 	innobase_large_page_size = 0;
+
+/* The default values for the following, type long or longlong, start-up
+parameters are declared in mysqld.cc: */
+
+long innobase_mirrored_log_groups, innobase_log_files_in_group,
+     innobase_log_buffer_size, innobase_buffer_pool_awe_mem_mb,
+     innobase_additional_mem_pool_size, innobase_file_io_threads,
+     innobase_lock_wait_timeout, innobase_force_recovery,
+     innobase_open_files;
+
+longlong innobase_buffer_pool_size, innobase_log_file_size;
+
+/* The default values for the following char* start-up parameters
+are determined in innobase_init below: */
+
+char*	innobase_data_home_dir			= NULL;
+char*	innobase_data_file_path 		= NULL;
+char*	innobase_log_group_home_dir		= NULL;
+char*	innobase_log_arch_dir			= NULL;/* unused */
+/* The following has a misleading name: starting from 4.0.5, this also
+affects Windows: */
+char*	innobase_unix_file_flush_method		= NULL;
+
+/* Below we have boolean-valued start-up parameters, and their default
+values */
+
+ulong	innobase_fast_shutdown			= 1;
+my_bool innobase_log_archive			= FALSE;/* unused */
+my_bool innobase_use_doublewrite    = TRUE;
+my_bool innobase_use_checksums      = TRUE;
+my_bool innobase_use_large_pages    = FALSE;
+my_bool	innobase_use_native_aio			= FALSE;
+my_bool	innobase_file_per_table			= FALSE;
+my_bool innobase_locks_unsafe_for_binlog        = FALSE;
+my_bool innobase_rollback_on_timeout		= FALSE;
+my_bool innobase_create_status_file		= FALSE;
+my_bool innobase_adaptive_hash_index		= TRUE;
+
+static char *internal_innobase_data_file_path	= NULL;
+
+/* The following counter is used to convey information to InnoDB
+about server activity: in selects it is not sensible to call
+srv_active_wake_master_thread after each fetch or search, we only do
+it every INNOBASE_WAKE_INTERVAL'th step. */
+
+#define INNOBASE_WAKE_INTERVAL	32
+ulong	innobase_active_counter	= 0;
+
+static HASH 	innobase_open_tables;
+
+#ifdef __NETWARE__  	/* some special cleanup for NetWare */
+bool nw_panic = FALSE;
+#endif
+
+static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
+			      my_bool not_used __attribute__((unused)));
+static INNOBASE_SHARE *get_share(const char *table_name);
+static void free_share(INNOBASE_SHARE *share);
+static int innobase_close_connection(THD* thd);
+static int innobase_commit(THD* thd, bool all);
+static int innobase_rollback(THD* thd, bool all);
+static int innobase_rollback_to_savepoint(THD* thd, void *savepoint);
+static int innobase_savepoint(THD* thd, void *savepoint);
+static int innobase_release_savepoint(THD* thd, void *savepoint);
+
+handlerton innobase_hton = {
+  "InnoDB",
+  SHOW_OPTION_YES,
+  "Supports transactions, row-level locking, and foreign keys",
+  DB_TYPE_INNODB,
+  innobase_init,
+  0,				/* slot */
+  sizeof(trx_named_savept_t),	/* savepoint size. TODO: use it */
+  innobase_close_connection,
+  innobase_savepoint,
+  innobase_rollback_to_savepoint,
+  innobase_release_savepoint,
+  innobase_commit,		/* commit */
+  innobase_rollback,		/* rollback */
+  innobase_xa_prepare,		/* prepare */
+  innobase_xa_recover,		/* recover */
+  innobase_commit_by_xid,	/* commit_by_xid */
+  innobase_rollback_by_xid,     /* rollback_by_xid */
+  innobase_create_cursor_view,
+  innobase_set_cursor_view,
+  innobase_close_cursor_view,
+  HTON_NO_FLAGS
+};
+
+/*********************************************************************
+Commits a transaction in an InnoDB database. */
+
+void
+innobase_commit_low(
+/*================*/
+	trx_t*	trx);	/* in: transaction handle */
+
+struct show_var_st innodb_status_variables[]= {
+  {"buffer_pool_pages_data",
+  (char*) &export_vars.innodb_buffer_pool_pages_data,     SHOW_LONG},
+  {"buffer_pool_pages_dirty",
+  (char*) &export_vars.innodb_buffer_pool_pages_dirty,    SHOW_LONG},
+  {"buffer_pool_pages_flushed",
+  (char*) &export_vars.innodb_buffer_pool_pages_flushed,  SHOW_LONG},
+  {"buffer_pool_pages_free",
+  (char*) &export_vars.innodb_buffer_pool_pages_free,     SHOW_LONG},
+  {"buffer_pool_pages_latched",
+  (char*) &export_vars.innodb_buffer_pool_pages_latched,  SHOW_LONG},
+  {"buffer_pool_pages_misc",
+  (char*) &export_vars.innodb_buffer_pool_pages_misc,     SHOW_LONG},
+  {"buffer_pool_pages_total",
+  (char*) &export_vars.innodb_buffer_pool_pages_total,    SHOW_LONG},
+  {"buffer_pool_read_ahead_rnd",
+  (char*) &export_vars.innodb_buffer_pool_read_ahead_rnd, SHOW_LONG},
+  {"buffer_pool_read_ahead_seq",
+  (char*) &export_vars.innodb_buffer_pool_read_ahead_seq, SHOW_LONG},
+  {"buffer_pool_read_requests",
+  (char*) &export_vars.innodb_buffer_pool_read_requests,  SHOW_LONG},
+  {"buffer_pool_reads",
+  (char*) &export_vars.innodb_buffer_pool_reads,          SHOW_LONG},
+  {"buffer_pool_wait_free",
+  (char*) &export_vars.innodb_buffer_pool_wait_free,      SHOW_LONG},
+  {"buffer_pool_write_requests",
+  (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
+  {"data_fsyncs",
+  (char*) &export_vars.innodb_data_fsyncs,                SHOW_LONG},
+  {"data_pending_fsyncs",
+  (char*) &export_vars.innodb_data_pending_fsyncs,        SHOW_LONG},
+  {"data_pending_reads",
+  (char*) &export_vars.innodb_data_pending_reads,         SHOW_LONG},
+  {"data_pending_writes",
+  (char*) &export_vars.innodb_data_pending_writes,        SHOW_LONG},
+  {"data_read",
+  (char*) &export_vars.innodb_data_read,                  SHOW_LONG},
+  {"data_reads",
+  (char*) &export_vars.innodb_data_reads,                 SHOW_LONG},
+  {"data_writes",
+  (char*) &export_vars.innodb_data_writes,                SHOW_LONG},
+  {"data_written",
+  (char*) &export_vars.innodb_data_written,               SHOW_LONG},
+  {"dblwr_pages_written",
+  (char*) &export_vars.innodb_dblwr_pages_written,        SHOW_LONG},
+  {"dblwr_writes",
+  (char*) &export_vars.innodb_dblwr_writes,               SHOW_LONG},
+  {"log_waits",
+  (char*) &export_vars.innodb_log_waits,                  SHOW_LONG},
+  {"log_write_requests",
+  (char*) &export_vars.innodb_log_write_requests,         SHOW_LONG},
+  {"log_writes",
+  (char*) &export_vars.innodb_log_writes,                 SHOW_LONG},
+  {"os_log_fsyncs",
+  (char*) &export_vars.innodb_os_log_fsyncs,              SHOW_LONG},
+  {"os_log_pending_fsyncs",
+  (char*) &export_vars.innodb_os_log_pending_fsyncs,      SHOW_LONG},
+  {"os_log_pending_writes",
+  (char*) &export_vars.innodb_os_log_pending_writes,      SHOW_LONG},
+  {"os_log_written",
+  (char*) &export_vars.innodb_os_log_written,             SHOW_LONG},
+  {"page_size",
+  (char*) &export_vars.innodb_page_size,                  SHOW_LONG},
+  {"pages_created",
+  (char*) &export_vars.innodb_pages_created,              SHOW_LONG},
+  {"pages_read",
+  (char*) &export_vars.innodb_pages_read,                 SHOW_LONG},
+  {"pages_written",
+  (char*) &export_vars.innodb_pages_written,              SHOW_LONG},
+  {"row_lock_current_waits",
+  (char*) &export_vars.innodb_row_lock_current_waits,     SHOW_LONG},
+  {"row_lock_time",
+  (char*) &export_vars.innodb_row_lock_time,              SHOW_LONGLONG},
+  {"row_lock_time_avg",
+  (char*) &export_vars.innodb_row_lock_time_avg,          SHOW_LONG},
+  {"row_lock_time_max",
+  (char*) &export_vars.innodb_row_lock_time_max,          SHOW_LONG},
+  {"row_lock_waits",
+  (char*) &export_vars.innodb_row_lock_waits,             SHOW_LONG},
+  {"rows_deleted",
+  (char*) &export_vars.innodb_rows_deleted,               SHOW_LONG},
+  {"rows_inserted",
+  (char*) &export_vars.innodb_rows_inserted,              SHOW_LONG},
+  {"rows_read",
+  (char*) &export_vars.innodb_rows_read,                  SHOW_LONG},
+  {"rows_updated",
+  (char*) &export_vars.innodb_rows_updated,               SHOW_LONG},
+  {NullS, NullS, SHOW_LONG}};
+
+/* General functions */
+
+/**********************************************************************
+Save some CPU by testing the value of srv_thread_concurrency in inline
+functions. */
+inline
+void
+innodb_srv_conc_enter_innodb(
+/*=========================*/
+	trx_t*	trx)	/* in: transaction handle */
+{
+	if (UNIV_LIKELY(!srv_thread_concurrency)) {
+
+		return;
+	}
+
+	srv_conc_enter_innodb(trx);
+}
+
+/**********************************************************************
+Save some CPU by testing the value of srv_thread_concurrency in inline
+functions. */
+inline
+void
+innodb_srv_conc_exit_innodb(
+/*========================*/
+	trx_t*	trx)	/* in: transaction handle */
+{
+	if (UNIV_LIKELY(!srv_thread_concurrency)) {
+
+		return;
+	}
+
+	srv_conc_exit_innodb(trx);
+}
+
+/**********************************************************************
+Releases possible search latch and InnoDB thread FIFO ticket. These should
+be released at each SQL statement end, and also when mysqld passes the
+control to the client. It does no harm to release these also in the middle
+of an SQL statement. */
+inline
+void
+innobase_release_stat_resources(
+/*============================*/
+	trx_t*	trx)	/* in: transaction object */
+{
+	if (trx->has_search_latch) {
+		trx_search_latch_release_if_reserved(trx);
+	}
+
+	if (trx->declared_to_be_inside_innodb) {
+		/* Release our possible ticket in the FIFO */
+
+		srv_conc_force_exit_innodb(trx);
+	}
+}
+
+/************************************************************************
+Call this function when mysqld passes control to the client. That is to
+avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more
+documentation, see handler.cc. */
+
+void
+innobase_release_temporary_latches(
+/*===============================*/
+        THD *thd)
+{
+	trx_t*	trx;
+
+	if (!innodb_inited) {
+
+		return;
+	}
+
+	trx = (trx_t*) thd->ha_data[innobase_hton.slot];
+
+	if (trx) {
+        	innobase_release_stat_resources(trx);
+	}
+}
+
+/************************************************************************
+Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
+time calls srv_active_wake_master_thread. This function should be used
+when a single database operation may introduce a small need for
+server utility activity, like checkpointing. */
+inline
+void
+innobase_active_small(void)
+/*=======================*/
+{
+	innobase_active_counter++;
+
+	if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
+		srv_active_wake_master_thread();
+	}
+}
+
+/************************************************************************
+Converts an InnoDB error code to a MySQL error code and also tells to MySQL
+about a possible transaction rollback inside InnoDB caused by a lock wait
+timeout or a deadlock. */
+static
+int
+convert_error_code_to_mysql(
+/*========================*/
+			/* out: MySQL error code */
+	int	error,	/* in: InnoDB error code */
+	THD*	thd)	/* in: user thread handle or NULL */
+{
+	if (error == DB_SUCCESS) {
+
+		return(0);
+
+  	} else if (error == (int) DB_DUPLICATE_KEY) {
+
+    		return(HA_ERR_FOUND_DUPP_KEY);
+
+ 	} else if (error == (int) DB_RECORD_NOT_FOUND) {
+
+    		return(HA_ERR_NO_ACTIVE_RECORD);
+
+ 	} else if (error == (int) DB_ERROR) {
+
+    		return(-1); /* unspecified error */
+
+ 	} else if (error == (int) DB_DEADLOCK) {
+ 		/* Since we rolled back the whole transaction, we must
+ 		tell it also to MySQL so that MySQL knows to empty the
+ 		cached binlog for this transaction */
+
+                mark_transaction_to_rollback(thd, TRUE);
+
+    		return(HA_ERR_LOCK_DEADLOCK);
+
+ 	} else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
+
+		/* Starting from 5.0.13, we let MySQL just roll back the
+		latest SQL statement in a lock wait timeout. Previously, we
+		rolled back the whole transaction. */
+
+                mark_transaction_to_rollback(thd,
+                                             (bool)row_rollback_on_timeout);
+
+   		return(HA_ERR_LOCK_WAIT_TIMEOUT);
+
+ 	} else if (error == (int) DB_NO_REFERENCED_ROW) {
+
+    		return(HA_ERR_NO_REFERENCED_ROW);
+
+ 	} else if (error == (int) DB_ROW_IS_REFERENCED) {
+
+    		return(HA_ERR_ROW_IS_REFERENCED);
+
+        } else if (error == (int) DB_CANNOT_ADD_CONSTRAINT) {
+
+    		return(HA_ERR_CANNOT_ADD_FOREIGN);
+
+        } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
+
+    		return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
+						misleading, a new MySQL error
+						code should be introduced */
+        } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
+
+    		return(HA_ERR_CRASHED);
+
+ 	} else if (error == (int) DB_OUT_OF_FILE_SPACE) {
+
+    		return(HA_ERR_RECORD_FILE_FULL);
+
+ 	} else if (error == (int) DB_TABLE_IS_BEING_USED) {
+
+    		return(HA_ERR_WRONG_COMMAND);
+
+ 	} else if (error == (int) DB_TABLE_NOT_FOUND) {
+
+    		return(HA_ERR_NO_SUCH_TABLE);
+
+  	} else if (error == (int) DB_TOO_BIG_RECORD) {
+
+    		return(HA_ERR_TO_BIG_ROW);
+
+  	} else if (error == (int) DB_CORRUPTION) {
+
+    		return(HA_ERR_CRASHED);
+  	} else if (error == (int) DB_NO_SAVEPOINT) {
+
+    		return(HA_ERR_NO_SAVEPOINT);
+  	} else if (error == (int) DB_LOCK_TABLE_FULL) {
+ 		/* Since we rolled back the whole transaction, we must
+ 		tell it also to MySQL so that MySQL knows to empty the
+ 		cached binlog for this transaction */
+
+                mark_transaction_to_rollback(thd, TRUE);
+
+    		return(HA_ERR_LOCK_TABLE_FULL);
+	} else if (error == DB_UNSUPPORTED) {
+
+		return(HA_ERR_UNSUPPORTED);
+    	} else {
+    		return(-1);			// Unknown error
+    	}
+}
+
+/*****************************************************************
+If you want to print a thd that is not associated with the current thread,
+you must call this function before reserving the InnoDB kernel_mutex, to
+protect MySQL from setting thd->query NULL. If you print a thd of the current
+thread, we know that MySQL cannot modify thd->query, and it is not necessary
+to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release
+the kernel_mutex.
+NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
+function! */
+extern "C"
+void
+innobase_mysql_prepare_print_arbitrary_thd(void)
+/*============================================*/
+{
+	VOID(pthread_mutex_lock(&LOCK_thread_count));
+}
+
+/*****************************************************************
+Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
+NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
+function! */
+extern "C"
+void
+innobase_mysql_end_print_arbitrary_thd(void)
+/*========================================*/
+{
+	VOID(pthread_mutex_unlock(&LOCK_thread_count));
+}
+
+/*****************************************************************
+Prints info of a THD object (== user session thread) to the given file.
+NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for
+this function! */
+extern "C"
+void
+innobase_mysql_print_thd(
+/*=====================*/
+	FILE*   f,		/* in: output stream */
+	void*   input_thd,	/* in: pointer to a MySQL THD object */
+	uint	max_query_len)	/* in: max query length to print, or 0 to
+				   use the default max length */
+{
+	const THD*	thd;
+        const Security_context *sctx;
+	const char*	s;
+
+        thd = (const THD*) input_thd;
+        /* We probably want to have original user as part of debug output. */
+        sctx = &thd->main_security_ctx;
+
+
+  	fprintf(f, "MySQL thread id %lu, query id %lu",
+		thd->thread_id, (ulong) thd->query_id);
+	if (sctx->host) {
+		putc(' ', f);
+		fputs(sctx->host, f);
+	}
+
+	if (sctx->ip) {
+		putc(' ', f);
+		fputs(sctx->ip, f);
+	}
+
+        if (sctx->user) {
+		putc(' ', f);
+		fputs(sctx->user, f);
+  	}
+
+	if ((s = thd->proc_info)) {
+		putc(' ', f);
+		fputs(s, f);
+	}
+
+	if ((s = thd->query)) {
+		/* 3100 is chosen because currently 3000 is the maximum
+		   max_query_len we ever give this. */
+		char	buf[3100];
+		uint	len;
+
+		/* If buf is too small, we dynamically allocate storage
+		   in this. */
+		char*	dyn_str = NULL;
+
+		/* Points to buf or dyn_str. */
+		char*	str = buf;
+
+		if (max_query_len == 0)
+		{
+			/* ADDITIONAL SAFETY: the default is to print at
+			   most 300 chars to reduce the probability of a
+			   seg fault if there is a race in
+			   thd->query_length in MySQL; after May 14, 2004
+			   probably no race any more, but better be
+			   safe */
+			max_query_len = 300;
+		}
+
+		len = min(thd->query_length, max_query_len);
+
+		if (len > (sizeof(buf) - 1))
+		{
+			dyn_str = my_malloc(len + 1, MYF(0));
+			str = dyn_str;
+		}
+
+                /* Use strmake to reduce the timeframe for a race,
+                   compared to fwrite() */
+		len = (uint) (strmake(str, s, len) - str);
+		putc('\n', f);
+		fwrite(str, 1, len, f);
+
+		if (dyn_str)
+		{
+			my_free(dyn_str, MYF(0));
+		}
+	}
+
+	putc('\n', f);
+}
+
+/**********************************************************************
+Get the variable length bounds of the given character set.
+
+NOTE that the exact prototype of this function has to be in
+/innobase/data/data0type.ic! */
+extern "C"
+void
+innobase_get_cset_width(
+/*====================*/
+	ulint	cset,		/* in: MySQL charset-collation code */
+	ulint*	mbminlen,	/* out: minimum length of a char (in bytes) */
+	ulint*	mbmaxlen)	/* out: maximum length of a char (in bytes) */
+{
+	CHARSET_INFO*	cs;
+	ut_ad(cset < 256);
+	ut_ad(mbminlen);
+	ut_ad(mbmaxlen);
+
+	cs = all_charsets[cset];
+	if (cs) {
+		*mbminlen = cs->mbminlen;
+		*mbmaxlen = cs->mbmaxlen;
+	} else {
+		ut_a(cset == 0);
+		*mbminlen = *mbmaxlen = 0;
+	}
+}
+
+/**********************************************************************
+Compares NUL-terminated UTF-8 strings case insensitively.
+
+NOTE that the exact prototype of this function has to be in
+/innobase/dict/dict0dict.c! */
+extern "C"
+int
+innobase_strcasecmp(
+/*================*/
+				/* out: 0 if a=b, <0 if a<b, >1 if a>b */
+	const char*	a,	/* in: first string to compare */
+	const char*	b)	/* in: second string to compare */
+{
+	return(my_strcasecmp(system_charset_info, a, b));
+}
+
+/**********************************************************************
+Makes all characters in a NUL-terminated UTF-8 string lower case.
+
+NOTE that the exact prototype of this function has to be in
+/innobase/dict/dict0dict.c! */
+extern "C"
+void
+innobase_casedn_str(
+/*================*/
+	char*	a)	/* in/out: string to put in lower case */
+{
+	my_casedn_str(system_charset_info, a);
+}
+
+/*************************************************************************
+Creates a temporary file. */
+extern "C"
+int
+innobase_mysql_tmpfile(void)
+/*========================*/
+			/* out: temporary file descriptor, or < 0 on error */
+{
+	char	filename[FN_REFLEN];
+	int	fd2 = -1;
+	File	fd = create_temp_file(filename, mysql_tmpdir, "ib",
+#ifdef __WIN__
+				O_BINARY | O_TRUNC | O_SEQUENTIAL |
+				O_TEMPORARY | O_SHORT_LIVED |
+#endif /* __WIN__ */
+				O_CREAT | O_EXCL | O_RDWR,
+				MYF(MY_WME));
+	if (fd >= 0) {
+#ifndef __WIN__
+		/* On Windows, open files cannot be removed, but files can be
+		created with the O_TEMPORARY flag to the same effect
+		("delete on close"). */
+		unlink(filename);
+#endif /* !__WIN__ */
+		/* Copy the file descriptor, so that the additional resources
+		allocated by create_temp_file() can be freed by invoking
+		my_close().
+
+		Because the file descriptor returned by this function
+		will be passed to fdopen(), it will be closed by invoking
+		fclose(), which in turn will invoke close() instead of
+		my_close(). */
+		fd2 = dup(fd);
+		if (fd2 < 0) {
+			DBUG_PRINT("error",("Got error %d on dup",fd2));
+			my_errno=errno;
+                        my_error(EE_OUT_OF_FILERESOURCES,
+                                 MYF(ME_BELL+ME_WAITTANG),
+                                 filename, my_errno);
+                }
+		my_close(fd, MYF(MY_WME));
+	}
+	return(fd2);
+}
+
+/*************************************************************************
+Gets the InnoDB transaction handle for a MySQL handler object, creates
+an InnoDB transaction struct if the corresponding MySQL thread struct still
+lacks one. */
+static
+trx_t*
+check_trx_exists(
+/*=============*/
+			/* out: InnoDB transaction handle */
+	THD*	thd)	/* in: user thread handle */
+{
+	trx_t*	trx;
+
+	ut_ad(thd == current_thd);
+
+        trx = (trx_t*) thd->ha_data[innobase_hton.slot];
+
+	if (trx == NULL) {
+	        DBUG_ASSERT(thd != NULL);
+		trx = trx_allocate_for_mysql();
+
+		trx->mysql_thd = thd;
+		trx->mysql_query_str = &(thd->query);
+                trx->active_trans = 0;
+
+		/* Update the info whether we should skip XA steps that eat
+		CPU time */
+		trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
+
+                thd->ha_data[innobase_hton.slot] = trx;
+	} else {
+		if (trx->magic_n != TRX_MAGIC_N) {
+			mem_analyze_corruption((byte*)trx);
+
+			ut_a(0);
+		}
+	}
+
+	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
+		trx->check_foreigns = FALSE;
+	} else {
+		trx->check_foreigns = TRUE;
+	}
+
+	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
+		trx->check_unique_secondary = FALSE;
+	} else {
+		trx->check_unique_secondary = TRUE;
+	}
+
+	return(trx);
+}
+
+
+/*************************************************************************
+Construct ha_innobase handler. */
+
+ha_innobase::ha_innobase(TABLE *table_arg)
+  :handler(&innobase_hton, table_arg),
+  int_table_flags(HA_REC_NOT_IN_SEQ |
+                  HA_NULL_IN_KEY |
+                  HA_CAN_INDEX_BLOBS |
+                  HA_CAN_SQL_HANDLER |
+                  HA_NOT_EXACT_COUNT |
+                  HA_PRIMARY_KEY_IN_READ_INDEX |
+                  HA_CAN_GEOMETRY |
+                  HA_TABLE_SCAN_ON_INDEX),
+  start_of_scan(0),
+  num_write_row(0)
+{}
+
+/*************************************************************************
+Updates the user_thd field in a handle and also allocates a new InnoDB
+transaction handle if needed, and updates the transaction fields in the
+prebuilt struct. */
+inline
+int
+ha_innobase::update_thd(
+/*====================*/
+			/* out: 0 or error code */
+	THD*	thd)	/* in: thd to use the handle */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	trx_t*		trx;
+
+	trx = check_trx_exists(thd);
+
+	if (prebuilt->trx != trx) {
+
+		row_update_prebuilt_trx(prebuilt, trx);
+	}
+
+	user_thd = thd;
+
+	return(0);
+}
+
+/*************************************************************************
+Registers that InnoDB takes part in an SQL statement, so that MySQL knows to
+roll back the statement if the statement results in an error. This MUST be
+called for every SQL statement that may be rolled back by MySQL. Calling this
+several times to register the same statement is allowed, too. */
+inline
+void
+innobase_register_stmt(
+/*===================*/
+	THD*	thd)	/* in: MySQL thd (connection) object */
+{
+        /* Register the statement */
+        trans_register_ha(thd, FALSE, &innobase_hton);
+}
+
+/*************************************************************************
+Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
+to call the InnoDB prepare and commit, or rollback for the transaction. This
+MUST be called for every transaction for which the user may call commit or
+rollback. Calling this several times to register the same transaction is
+allowed, too.
+This function also registers the current SQL statement. */
+inline
+void
+innobase_register_trx_and_stmt(
+/*===========================*/
+	THD*	thd)	/* in: MySQL thd (connection) object */
+{
+	/* NOTE that actually innobase_register_stmt() registers also
+	the transaction in the AUTOCOMMIT=1 mode. */
+
+	innobase_register_stmt(thd);
+
+        if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+
+              /* No autocommit mode, register for a transaction */
+              trans_register_ha(thd, TRUE, &innobase_hton);
+        }
+}
+
+/*   BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
+     ------------------------------------------------------------
+
+1) The use of the query cache for TBL is disabled when there is an
+uncommitted change to TBL.
+
+2) When a change to TBL commits, InnoDB stores the current value of
+its global trx id counter, let us denote it by INV_TRX_ID, to the table object
+in the InnoDB data dictionary, and does only allow such transactions whose
+id <= INV_TRX_ID to use the query cache.
+
+3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit
+modification because an ON DELETE CASCADE, we invalidate the MySQL query cache
+of TBL immediately.
+
+How this is implemented inside InnoDB:
+
+1) Since every modification always sets an IX type table lock on the InnoDB
+table, it is easy to check if there can be uncommitted modifications for a
+table: just check if there are locks in the lock list of the table.
+
+2) When a transaction inside InnoDB commits, it reads the global trx id
+counter and stores the value INV_TRX_ID to the tables on which it had a lock.
+
+3) If there is an implicit table change from ON DELETE CASCADE or SET NULL,
+InnoDB calls an invalidate method for the MySQL query cache for that table.
+
+How this is implemented inside sql_cache.cc:
+
+1) The query cache for an InnoDB table TBL is invalidated immediately at an
+INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay
+invalidation to the transaction commit.
+
+2) To store or retrieve a value from the query cache of an InnoDB table TBL,
+any query must first ask InnoDB's permission. We must pass the thd as a
+parameter because InnoDB will look at the trx id, if any, associated with
+that thd.
+
+3) Use of the query cache for InnoDB tables is now allowed also when
+AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer
+put restrictions on the use of the query cache.
+*/
+
+/**********************************************************************
+The MySQL query cache uses this to check from InnoDB if the query cache at
+the moment is allowed to operate on an InnoDB table. The SQL query must
+be a non-locking SELECT.
+
+The query cache is allowed to operate on certain query only if this function
+returns TRUE for all tables in the query.
+
+If thd is not in the autocommit state, this function also starts a new
+transaction for thd if there is no active trx yet, and assigns a consistent
+read view to it if there is no read view yet.
+
+Why a deadlock of threads is not possible: the query cache calls this function
+at the start of a SELECT processing. Then the calling thread cannot be
+holding any InnoDB semaphores. The calling thread is holding the
+query cache mutex, and this function will reserver the InnoDB kernel mutex.
+Thus, the 'rank' in sync0sync.h of the MySQL query cache mutex is above
+the InnoDB kernel mutex. */
+
+my_bool
+innobase_query_caching_of_table_permitted(
+/*======================================*/
+				/* out: TRUE if permitted, FALSE if not;
+				note that the value FALSE does not mean
+				we should invalidate the query cache:
+				invalidation is called explicitly */
+	THD*	thd,		/* in: thd of the user who is trying to
+				store a result to the query cache or
+				retrieve it */
+	char*	full_name,	/* in: concatenation of database name,
+				the null character '\0', and the table
+				name */
+	uint	full_name_len,	/* in: length of the full name, i.e.
+				len(dbname) + len(tablename) + 1 */
+        ulonglong *unused)      /* unused for this engine */
+{
+	ibool	is_autocommit;
+	trx_t*	trx;
+	char	norm_name[1000];
+
+	ut_a(full_name_len < 999);
+
+	if (thd->variables.tx_isolation == ISO_SERIALIZABLE) {
+		/* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
+		plain SELECT if AUTOCOMMIT is not on. */
+
+		return((my_bool)FALSE);
+	}
+
+        trx = check_trx_exists(thd);
+	if (trx->has_search_latch) {
+		ut_print_timestamp(stderr);
+		sql_print_error("The calling thread is holding the adaptive "
+				"search, latch though calling "
+				"innobase_query_caching_of_table_permitted.");
+
+		mutex_enter_noninline(&kernel_mutex);
+		trx_print(stderr, trx, 1024);
+		mutex_exit_noninline(&kernel_mutex);
+	}
+
+	innobase_release_stat_resources(trx);
+
+	if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
+
+		is_autocommit = TRUE;
+	} else {
+		is_autocommit = FALSE;
+
+	}
+
+	if (is_autocommit && trx->n_mysql_tables_in_use == 0) {
+		/* We are going to retrieve the query result from the query
+		cache. This cannot be a store operation to the query cache
+		because then MySQL would have locks on tables already.
+
+		TODO: if the user has used LOCK TABLES to lock the table,
+		then we open a transaction in the call of row_.. below.
+		That trx can stay open until UNLOCK TABLES. The same problem
+		exists even if we do not use the query cache. MySQL should be
+		modified so that it ALWAYS calls some cleanup function when
+		the processing of a query ends!
+
+		We can imagine we instantaneously serialize this consistent
+		read trx to the current trx id counter. If trx2 would have
+		changed the tables of a query result stored in the cache, and
+		trx2 would have already committed, making the result obsolete,
+		then trx2 would have already invalidated the cache. Thus we
+		can trust the result in the cache is ok for this query. */
+
+		return((my_bool)TRUE);
+	}
+
+	/* Normalize the table name to InnoDB format */
+
+	memcpy(norm_name, full_name, full_name_len);
+
+	norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the
+					    separator between db and table */
+	norm_name[full_name_len] = '\0';
+#ifdef __WIN__
+	innobase_casedn_str(norm_name);
+#endif
+	/* The call of row_search_.. will start a new transaction if it is
+	not yet started */
+
+        if (trx->active_trans == 0) {
+
+                innobase_register_trx_and_stmt(thd);
+                trx->active_trans = 1;
+        }
+
+	if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
+
+		/* printf("Query cache for %s permitted\n", norm_name); */
+
+		return((my_bool)TRUE);
+	}
+
+	/* printf("Query cache for %s NOT permitted\n", norm_name); */
+
+	return((my_bool)FALSE);
+}
+
+/*********************************************************************
+Invalidates the MySQL query cache for the table.
+NOTE that the exact prototype of this function has to be in
+/innobase/row/row0ins.c! */
+extern "C"
+void
+innobase_invalidate_query_cache(
+/*============================*/
+	trx_t*	trx,		/* in: transaction which modifies the table */
+	char*	full_name,	/* in: concatenation of database name, null
+				char '\0', table name, null char'\0';
+				NOTE that in Windows this is always
+				in LOWER CASE! */
+	ulint	full_name_len)	/* in: full name length where also the null
+				chars count */
+{
+	/* Note that the sync0sync.h rank of the query cache mutex is just
+	above the InnoDB kernel mutex. The caller of this function must not
+	have latches of a lower rank. */
+
+	/* Argument TRUE below means we are using transactions */
+#ifdef HAVE_QUERY_CACHE
+	query_cache.invalidate((THD*)(trx->mysql_thd),
+					(const char*)full_name,
+					(uint32)full_name_len,
+					TRUE);
+#endif
+}
+
+/*********************************************************************
+Get the quote character to be used in SQL identifiers.
+This definition must match the one in innobase/ut/ut0ut.c! */
+extern "C"
+int
+mysql_get_identifier_quote_char(
+/*============================*/
+				/* out: quote character to be
+				used in SQL identifiers; EOF if none */
+	trx_t*		trx,	/* in: transaction */
+	const char*	name,	/* in: name to print */
+	ulint		namelen)/* in: length of name */
+{
+	if (!trx || !trx->mysql_thd) {
+		return(EOF);
+	}
+	return(get_quote_char_for_identifier((THD*) trx->mysql_thd,
+						name, (int) namelen));
+}
+
+/**************************************************************************
+Determines if the currently running transaction has been interrupted. */
+extern "C"
+ibool
+trx_is_interrupted(
+/*===============*/
+			/* out: TRUE if interrupted */
+	trx_t*	trx)	/* in: transaction */
+{
+	return(trx && trx->mysql_thd && ((THD*) trx->mysql_thd)->killed);
+}
+
+/**************************************************************************
+Obtain a pointer to the MySQL THD object, as in current_thd().  This
+definition must match the one in sql/ha_innodb.cc! */
+extern "C"
+void*
+innobase_current_thd(void)
+/*======================*/
+			/* out: MySQL THD object */
+{
+	return(current_thd);
+}
+
+/*********************************************************************
+Call this when you have opened a new table handle in HANDLER, before you
+call index_read_idx() etc. Actually, we can let the cursor stay open even
+over a transaction commit! Then you should call this before every operation,
+fetch next etc. This function inits the necessary things even after a
+transaction commit. */
+
+void
+ha_innobase::init_table_handle_for_HANDLER(void)
+/*============================================*/
+{
+        row_prebuilt_t* prebuilt;
+
+        /* If current thd does not yet have a trx struct, create one.
+        If the current handle does not yet have a prebuilt struct, create
+        one. Update the trx pointers in the prebuilt struct. Normally
+        this operation is done in external_lock. */
+
+        update_thd(current_thd);
+
+        /* Initialize the prebuilt struct much like it would be inited in
+        external_lock */
+
+        prebuilt = (row_prebuilt_t*)innobase_prebuilt;
+
+	innobase_release_stat_resources(prebuilt->trx);
+
+        /* If the transaction is not started yet, start it */
+
+        trx_start_if_not_started_noninline(prebuilt->trx);
+
+        /* Assign a read view if the transaction does not have it yet */
+
+        trx_assign_read_view(prebuilt->trx);
+
+	/* Set the MySQL flag to mark that there is an active transaction */
+
+        if (prebuilt->trx->active_trans == 0) {
+
+                innobase_register_trx_and_stmt(current_thd);
+
+                prebuilt->trx->active_trans = 1;
+        }
+
+        /* We did the necessary inits in this function, no need to repeat them
+        in row_search_for_mysql */
+
+        prebuilt->sql_stat_start = FALSE;
+
+        /* We let HANDLER always to do the reads as consistent reads, even
+        if the trx isolation level would have been specified as SERIALIZABLE */
+
+        prebuilt->select_lock_type = LOCK_NONE;
+        prebuilt->stored_select_lock_type = LOCK_NONE;
+
+        /* Always fetch all columns in the index record */
+
+        prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
+
+        /* We want always to fetch all columns in the whole row? Or do
+	we???? */
+
+        prebuilt->read_just_key = FALSE;
+
+	prebuilt->used_in_HANDLER = TRUE;
+
+	prebuilt->keep_other_fields_on_keyread = FALSE;
+}
+
+/*************************************************************************
+Opens an InnoDB database. */
+
+bool
+innobase_init(void)
+/*===============*/
+			/* out: &innobase_hton, or NULL on error */
+{
+	static char	current_dir[3];		/* Set if using current lib */
+	int		err;
+	bool		ret;
+	char 	        *default_path;
+
+  	DBUG_ENTER("innobase_init");
+
+         if (have_innodb != SHOW_OPTION_YES)
+           goto error;
+
+	ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
+
+	/* Check that values don't overflow on 32-bit systems. */
+	if (sizeof(ulint) == 4) {
+		if (innobase_buffer_pool_size > UINT_MAX32) {
+			sql_print_error(
+				"innobase_buffer_pool_size can't be over 4GB"
+				" on 32-bit systems");
+
+			goto error;
+		}
+
+		if (innobase_log_file_size > UINT_MAX32) {
+			sql_print_error(
+				"innobase_log_file_size can't be over 4GB"
+				" on 32-bit systems");
+
+			goto error;
+		}
+	}
+
+  	os_innodb_umask = (ulint)my_umask;
+
+	/* First calculate the default path for innodb_data_home_dir etc.,
+	in case the user has not given any value.
+
+	Note that when using the embedded server, the datadirectory is not
+	necessarily the current directory of this program. */
+
+	if (mysqld_embedded) {
+		default_path = mysql_real_data_home;
+		fil_path_to_mysql_datadir = mysql_real_data_home;
+	} else {
+	  	/* It's better to use current lib, to keep paths short */
+	  	current_dir[0] = FN_CURLIB;
+	  	current_dir[1] = FN_LIBCHAR;
+	  	current_dir[2] = 0;
+	  	default_path = current_dir;
+	}
+
+	ut_a(default_path);
+
+	if (specialflag & SPECIAL_NO_PRIOR) {
+	        srv_set_thread_priorities = FALSE;
+	} else {
+	        srv_set_thread_priorities = TRUE;
+	        srv_query_thread_priority = QUERY_PRIOR;
+	}
+
+	/* Set InnoDB initialization parameters according to the values
+	read from MySQL .cnf file */
+
+	/*--------------- Data files -------------------------*/
+
+	/* The default dir for data files is the datadir of MySQL */
+
+	srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir :
+			 default_path);
+
+	/* Set default InnoDB data file size to 10 MB and let it be
+  	auto-extending. Thus users can use InnoDB in >= 4.0 without having
+	to specify any startup options. */
+
+	if (!innobase_data_file_path) {
+  		innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
+	}
+
+	/* Since InnoDB edits the argument in the next call, we make another
+	copy of it: */
+
+	internal_innobase_data_file_path = my_strdup(innobase_data_file_path,
+						   MYF(MY_FAE));
+
+	ret = (bool) srv_parse_data_file_paths_and_sizes(
+				internal_innobase_data_file_path,
+				&srv_data_file_names,
+				&srv_data_file_sizes,
+				&srv_data_file_is_raw_partition,
+				&srv_n_data_files,
+				&srv_auto_extend_last_data_file,
+				&srv_last_file_size_max);
+	if (ret == FALSE) {
+	  	sql_print_error(
+			"InnoDB: syntax error in innodb_data_file_path");
+	  	my_free(internal_innobase_data_file_path,
+						MYF(MY_ALLOW_ZERO_PTR));
+                goto error;
+	}
+
+	/* -------------- Log files ---------------------------*/
+
+	/* The default dir for log files is the datadir of MySQL */
+
+	if (!innobase_log_group_home_dir) {
+	  	innobase_log_group_home_dir = default_path;
+	}
+
+#ifdef UNIV_LOG_ARCHIVE
+	/* Since innodb_log_arch_dir has no relevance under MySQL,
+	starting from 4.0.6 we always set it the same as
+	innodb_log_group_home_dir: */
+
+	innobase_log_arch_dir = innobase_log_group_home_dir;
+
+	srv_arch_dir = innobase_log_arch_dir;
+#endif /* UNIG_LOG_ARCHIVE */
+
+	ret = (bool)
+		srv_parse_log_group_home_dirs(innobase_log_group_home_dir,
+						&srv_log_group_home_dirs);
+
+	if (ret == FALSE || innobase_mirrored_log_groups != 1) {
+	  sql_print_error("syntax error in innodb_log_group_home_dir, or a "
+			  "wrong number of mirrored log groups");
+
+	  	my_free(internal_innobase_data_file_path,
+						MYF(MY_ALLOW_ZERO_PTR));
+                goto error;
+	}
+
+	/* --------------------------------------------------*/
+
+	srv_file_flush_method_str = innobase_unix_file_flush_method;
+
+	srv_n_log_groups = (ulint) innobase_mirrored_log_groups;
+	srv_n_log_files = (ulint) innobase_log_files_in_group;
+	srv_log_file_size = (ulint) innobase_log_file_size;
+
+#ifdef UNIV_LOG_ARCHIVE
+	srv_log_archive_on = (ulint) innobase_log_archive;
+#endif /* UNIV_LOG_ARCHIVE */
+	srv_log_buffer_size = (ulint) innobase_log_buffer_size;
+
+        /* We set srv_pool_size here in units of 1 kB. InnoDB internally
+        changes the value so that it becomes the number of database pages. */
+
+        if (innobase_buffer_pool_awe_mem_mb == 0) {
+                /* Careful here: we first convert the signed long int to ulint
+                and only after that divide */
+
+                srv_pool_size = ((ulint) innobase_buffer_pool_size) / 1024;
+        } else {
+                srv_use_awe = TRUE;
+                srv_pool_size = (ulint)
+                                (1024 * innobase_buffer_pool_awe_mem_mb);
+                srv_awe_window_size = (ulint) innobase_buffer_pool_size;
+
+                /* Note that what the user specified as
+                innodb_buffer_pool_size is actually the AWE memory window
+                size in this case, and the real buffer pool size is
+                determined by .._awe_mem_mb. */
+        }
+
+	srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;
+
+	srv_n_file_io_threads = (ulint) innobase_file_io_threads;
+
+	srv_lock_wait_timeout = (ulint) innobase_lock_wait_timeout;
+	srv_force_recovery = (ulint) innobase_force_recovery;
+
+	srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
+	srv_use_checksums = (ibool) innobase_use_checksums;
+
+	srv_use_adaptive_hash_indexes = (ibool) innobase_adaptive_hash_index;
+
+	os_use_large_pages = (ibool) innobase_use_large_pages;
+	os_large_page_size = (ulint) innobase_large_page_size;
+
+	row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
+
+	srv_file_per_table = (ibool) innobase_file_per_table;
+        srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
+
+	srv_max_n_open_files = (ulint) innobase_open_files;
+	srv_innodb_status = (ibool) innobase_create_status_file;
+
+	srv_print_verbose_log = mysqld_embedded ? 0 : 1;
+
+	/* Store the default charset-collation number of this MySQL
+	installation */
+
+	data_mysql_default_charset_coll = (ulint)default_charset_info->number;
+
+	ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
+					my_charset_latin1.number);
+	ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
+
+	/* Store the latin1_swedish_ci character ordering table to InnoDB. For
+	non-latin1_swedish_ci charsets we use the MySQL comparison functions,
+	and consequently we do not need to know the ordering internally in
+	InnoDB. */
+
+	ut_a(0 == strcmp((char*)my_charset_latin1.name,
+						(char*)"latin1_swedish_ci"));
+	memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256);
+
+	/* Since we in this module access directly the fields of a trx
+        struct, and due to different headers and flags it might happen that
+	mutex_t has a different size in this module and in InnoDB
+	modules, we check at run time that the size is the same in
+	these compilation modules. */
+
+	srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t);
+
+	err = innobase_start_or_create_for_mysql();
+
+	if (err != DB_SUCCESS) {
+	  	my_free(internal_innobase_data_file_path,
+						MYF(MY_ALLOW_ZERO_PTR));
+                goto error;
+	}
+
+	(void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
+			 		(hash_get_key) innobase_get_key, 0, 0);
+        pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
+        pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
+        pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
+        pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
+        pthread_cond_init(&commit_cond, NULL);
+	innodb_inited= 1;
+
+	/* If this is a replication slave and we needed to do a crash recovery,
+	set the master binlog position to what InnoDB internally knew about
+	how far we got transactions durable inside InnoDB. There is a
+	problem here: if the user used also MyISAM tables, InnoDB might not
+	know the right position for them.
+
+	THIS DOES NOT WORK CURRENTLY because replication seems to initialize
+	glob_mi also after innobase_init. */
+
+/*	if (trx_sys_mysql_master_log_pos != -1) {
+		ut_memcpy(glob_mi.log_file_name, trx_sys_mysql_master_log_name,
+				1 + ut_strlen(trx_sys_mysql_master_log_name));
+		glob_mi.pos = trx_sys_mysql_master_log_pos;
+	}
+*/
+	DBUG_RETURN(FALSE);
+error:
+        have_innodb= SHOW_OPTION_DISABLED;	// If we couldn't use handler
+        DBUG_RETURN(TRUE);
+}
+
+/***********************************************************************
+Closes an InnoDB database. */
+
+bool
+innobase_end(void)
+/*==============*/
+				/* out: TRUE if error */
+{
+	int	err= 0;
+
+	DBUG_ENTER("innobase_end");
+
+#ifdef __NETWARE__ 	/* some special cleanup for NetWare */
+	if (nw_panic) {
+		set_panic_flag_for_netware();
+	}
+#endif
+	if (innodb_inited) {
+
+	        srv_fast_shutdown = (ulint) innobase_fast_shutdown;
+	  	innodb_inited = 0;
+	  	if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
+	    		err = 1;
+		}
+	  	hash_free(&innobase_open_tables);
+	  	my_free(internal_innobase_data_file_path,
+						MYF(MY_ALLOW_ZERO_PTR));
+                pthread_mutex_destroy(&innobase_share_mutex);
+                pthread_mutex_destroy(&prepare_commit_mutex);
+                pthread_mutex_destroy(&commit_threads_m);
+                pthread_mutex_destroy(&commit_cond_m);
+                pthread_cond_destroy(&commit_cond);
+	}
+
+  	DBUG_RETURN(err);
+}
+
+/********************************************************************
+Flushes InnoDB logs to disk and makes a checkpoint. Really, a commit flushes
+the logs, and the name of this function should be innobase_checkpoint. */
+
+bool
+innobase_flush_logs(void)
+/*=====================*/
+				/* out: TRUE if error */
+{
+  	bool 	result = 0;
+
+  	DBUG_ENTER("innobase_flush_logs");
+
+	log_buffer_flush_to_disk();
+
+  	DBUG_RETURN(result);
+}
+
+/*********************************************************************
+Commits a transaction in an InnoDB database. */
+
+void
+innobase_commit_low(
+/*================*/
+	trx_t*	trx)	/* in: transaction handle */
+{
+        if (trx->conc_state == TRX_NOT_STARTED) {
+
+                return;
+        }
+
+#ifdef HAVE_REPLICATION
+        THD *thd=current_thd;
+
+        if (thd && thd->slave_thread) {
+                /* Update the replication position info inside InnoDB */
+
+                trx->mysql_master_log_file_name
+                                        = active_mi->rli.group_master_log_name;
+                trx->mysql_master_log_pos = ((ib_longlong)
+                                active_mi->rli.future_group_master_log_pos);
+        }
+#endif /* HAVE_REPLICATION */
+
+	trx_commit_for_mysql(trx);
+}
+
+/*********************************************************************
+Creates an InnoDB transaction struct for the thd if it does not yet have one.
+Starts a new InnoDB transaction if a transaction is not yet started. And
+assigns a new snapshot for a consistent read if the transaction does not yet
+have one. */
+
+int
+innobase_start_trx_and_assign_read_view(
+/*====================================*/
+			/* out: 0 */
+	THD*	thd)	/* in: MySQL thread handle of the user for whom
+			the transaction should be committed */
+{
+	trx_t*	trx;
+
+  	DBUG_ENTER("innobase_start_trx_and_assign_read_view");
+
+	/* Create a new trx struct for thd, if it does not yet have one */
+
+	trx = check_trx_exists(thd);
+
+	/* This is just to play safe: release a possible FIFO ticket and
+	search latch. Since we will reserve the kernel mutex, we have to
+	release the search system latch first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+	/* If the transaction is not started yet, start it */
+
+	trx_start_if_not_started_noninline(trx);
+
+	/* Assign a read view if the transaction does not have it yet */
+
+	trx_assign_read_view(trx);
+
+	/* Set the MySQL flag to mark that there is an active transaction */
+
+        if (trx->active_trans == 0) {
+
+                innobase_register_trx_and_stmt(current_thd);
+
+                trx->active_trans = 1;
+        }
+
+	DBUG_RETURN(0);
+}
+
+/*********************************************************************
+Commits a transaction in an InnoDB database or marks an SQL statement
+ended. */
+static
+int
+innobase_commit(
+/*============*/
+			/* out: 0 */
+	THD*	thd,	/* in: MySQL thread handle of the user for whom
+			the transaction should be committed */
+        bool    all)    /* in: TRUE - commit transaction
+                               FALSE - the current SQL statement ended */
+{
+	trx_t*		trx;
+
+  	DBUG_ENTER("innobase_commit");
+  	DBUG_PRINT("trans", ("ending transaction"));
+
+	trx = check_trx_exists(thd);
+
+	/* Update the info whether we should skip XA steps that eat CPU time */
+	trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+        if (trx->has_search_latch) {
+                          trx_search_latch_release_if_reserved(trx);
+        }
+
+        /* The flag trx->active_trans is set to 1 in
+
+	1. ::external_lock(),
+	2. ::start_stmt(),
+	3. innobase_query_caching_of_table_permitted(),
+	4. innobase_savepoint(),
+	5. ::init_table_handle_for_HANDLER(),
+	6. innobase_start_trx_and_assign_read_view(),
+	7. ::transactional_table_lock()
+
+	and it is only set to 0 in a commit or a rollback. If it is 0 we know
+	there cannot be resources to be freed and we could return immediately.
+	For the time being, we play safe and do the cleanup though there should
+	be nothing to clean up. */
+
+        if (trx->active_trans == 0
+	    && trx->conc_state != TRX_NOT_STARTED) {
+
+	  sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+			  "TRX_NOT_STARTED");
+	}
+        if (all
+	    || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
+
+ 		/* We were instructed to commit the whole transaction, or
+		this is an SQL statement end and autocommit is on */
+
+                /* We need current binlog position for ibbackup to work.
+                Note, the position is current because of prepare_commit_mutex */
+retry:
+                if (srv_commit_concurrency > 0)
+                {
+                  pthread_mutex_lock(&commit_cond_m);
+                  commit_threads++;
+                  if (commit_threads > srv_commit_concurrency)
+                  {
+                    commit_threads--;
+                    pthread_cond_wait(&commit_cond, &commit_cond_m);
+                    pthread_mutex_unlock(&commit_cond_m);
+                    goto retry;
+                  }
+                  else
+                    pthread_mutex_unlock(&commit_cond_m);
+                }
+
+                trx->mysql_log_file_name = mysql_bin_log.get_log_fname();
+                trx->mysql_log_offset =
+                        (ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;
+
+		innobase_commit_low(trx);
+
+                if (srv_commit_concurrency > 0)
+                {
+                  pthread_mutex_lock(&commit_cond_m);
+                  commit_threads--;
+                  pthread_cond_signal(&commit_cond);
+                  pthread_mutex_unlock(&commit_cond_m);
+                }
+
+                if (trx->active_trans == 2) {
+
+                        pthread_mutex_unlock(&prepare_commit_mutex);
+                }
+
+                trx->active_trans = 0;
+
+	} else {
+	        /* We just mark the SQL statement ended and do not do a
+		transaction commit */
+
+		if (trx->auto_inc_lock) {
+			/* If we had reserved the auto-inc lock for some
+			table in this SQL statement we release it now */
+
+			row_unlock_table_autoinc_for_mysql(trx);
+		}
+		/* Store the current undo_no of the transaction so that we
+		know where to roll back if we have to roll back the next
+		SQL statement */
+
+		trx_mark_sql_stat_end(trx);
+	}
+
+	/* Tell the InnoDB server that there might be work for utility
+	threads: */
+        if (trx->declared_to_be_inside_innodb) {
+                          /* Release our possible ticket in the FIFO */
+
+                          srv_conc_force_exit_innodb(trx);
+        }
+	srv_active_wake_master_thread();
+
+	DBUG_RETURN(0);
+}
+
+/* TODO: put the
+MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
+to work. */
+
+/*********************************************************************
+This is called when MySQL writes the binlog entry for the current
+transaction. Writes to the InnoDB tablespace info which tells where the
+MySQL binlog entry for the current transaction ended. Also commits the
+transaction inside InnoDB but does NOT flush InnoDB log files to disk.
+To flush you have to call innobase_commit_complete(). We have separated
+flushing to eliminate the bottleneck of LOCK_log in log.cc which disabled
+InnoDB's group commit capability. */
+
+int
+innobase_report_binlog_offset_and_commit(
+/*=====================================*/
+                                /* out: 0 */
+        THD*    thd,            /* in: user thread */
+        void*   trx_handle,     /* in: InnoDB trx handle */
+        char*   log_file_name,  /* in: latest binlog file name */
+        my_off_t end_offset)    /* in: the offset in the binlog file
+                                   up to which we wrote */
+{
+	trx_t*	trx;
+
+	trx = (trx_t*)trx_handle;
+
+	ut_a(trx != NULL);
+
+	trx->mysql_log_file_name = log_file_name;
+	trx->mysql_log_offset = (ib_longlong)end_offset;
+
+	trx->flush_log_later = TRUE;
+
+	innobase_commit(thd, TRUE);
+
+	trx->flush_log_later = FALSE;
+
+	return(0);
+}
+
+#if 0
+/***********************************************************************
+This function stores the binlog offset and flushes logs. */
+
+void
+innobase_store_binlog_offset_and_flush_log(
+/*=======================================*/
+    char *binlog_name,          /* in: binlog name */
+    longlong	offset)		/* in: binlog offset */
+{
+	mtr_t mtr;
+
+	assert(binlog_name != NULL);
+
+	/* Start a mini-transaction */
+        mtr_start_noninline(&mtr);
+
+	/* Update the latest MySQL binlog name and offset info
+        in trx sys header */
+
+        trx_sys_update_mysql_binlog_offset(
+            binlog_name,
+            offset,
+            TRX_SYS_MYSQL_LOG_INFO, &mtr);
+
+        /* Commits the mini-transaction */
+        mtr_commit(&mtr);
+
+	/* Synchronous flush of the log buffer to disk */
+	log_buffer_flush_to_disk();
+}
+#endif
+
+/*********************************************************************
+This is called after MySQL has written the binlog entry for the current
+transaction. Flushes the InnoDB log files to disk if required. */
+
+int
+innobase_commit_complete(
+/*=====================*/
+                                /* out: 0 */
+        THD*    thd)            /* in: user thread */
+{
+	trx_t*	trx;
+
+        trx = (trx_t*) thd->ha_data[innobase_hton.slot];
+
+        if (trx && trx->active_trans) {
+
+                trx->active_trans = 0;
+
+                if (UNIV_UNLIKELY(srv_flush_log_at_trx_commit == 0)) {
+
+                        return(0);
+                }
+
+                trx_commit_complete_for_mysql(trx);
+        }
+
+	return(0);
+}
+
+/*********************************************************************
+Rolls back a transaction or the latest SQL statement. */
+
+static int
+innobase_rollback(
+/*==============*/
+			/* out: 0 or error number */
+	THD*	thd,	/* in: handle to the MySQL thread of the user
+			whose transaction should be rolled back */
+        bool    all)    /* in: TRUE - commit transaction
+                               FALSE - the current SQL statement ended */
+{
+	int	error = 0;
+	trx_t*	trx;
+
+	DBUG_ENTER("innobase_rollback");
+	DBUG_PRINT("trans", ("aborting transaction"));
+
+	trx = check_trx_exists(thd);
+
+	/* Update the info whether we should skip XA steps that eat CPU time */
+	trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+        if (trx->auto_inc_lock) {
+		/* If we had reserved the auto-inc lock for some table (if
+		we come here to roll back the latest SQL statement) we
+		release it now before a possibly lengthy rollback */
+
+		row_unlock_table_autoinc_for_mysql(trx);
+	}
+
+        if (all
+	    || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
+
+		error = trx_rollback_for_mysql(trx);
+                trx->active_trans = 0;
+	} else {
+		error = trx_rollback_last_sql_stat_for_mysql(trx);
+	}
+
+	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
+}
+
+/*********************************************************************
+Rolls back a transaction */
+
+int
+innobase_rollback_trx(
+/*==================*/
+			/* out: 0 or error number */
+	trx_t*	trx)	/*  in: transaction */
+{
+	int	error = 0;
+
+	DBUG_ENTER("innobase_rollback_trx");
+	DBUG_PRINT("trans", ("aborting transaction"));
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+        if (trx->auto_inc_lock) {
+		/* If we had reserved the auto-inc lock for some table (if
+		we come here to roll back the latest SQL statement) we
+		release it now before a possibly lengthy rollback */
+
+		row_unlock_table_autoinc_for_mysql(trx);
+	}
+
+	error = trx_rollback_for_mysql(trx);
+
+	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
+}
+
+/*********************************************************************
+Rolls back a transaction to a savepoint. */
+
+static int
+innobase_rollback_to_savepoint(
+/*===========================*/
+				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
+				no savepoint with the given name */
+	THD*	thd,		/* in: handle to the MySQL thread of the user
+				whose transaction should be rolled back */
+        void *savepoint)        /* in: savepoint data */
+{
+	ib_longlong mysql_binlog_cache_pos;
+	int	    error = 0;
+	trx_t*	    trx;
+        char 	    name[64];
+
+	DBUG_ENTER("innobase_rollback_to_savepoint");
+
+	trx = check_trx_exists(thd);
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+        /* TODO: use provided savepoint data area to store savepoint data */
+
+        longlong2str((ulint)savepoint, name, 36);
+
+        error = (int) trx_rollback_to_savepoint_for_mysql(trx, name,
+						&mysql_binlog_cache_pos);
+	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
+}
+
+/*********************************************************************
+Release transaction savepoint name. */
+static
+int
+innobase_release_savepoint(
+/*=======================*/
+				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
+				no savepoint with the given name */
+	THD*	thd,		/* in: handle to the MySQL thread of the user
+				whose transaction should be rolled back */
+        void*	savepoint)      /* in: savepoint data */
+{
+	int	    error = 0;
+	trx_t*	    trx;
+        char 	    name[64];
+
+	DBUG_ENTER("innobase_release_savepoint");
+
+	trx = check_trx_exists(thd);
+
+        /* TODO: use provided savepoint data area to store savepoint data */
+
+        longlong2str((ulint)savepoint, name, 36);
+
+	error = (int) trx_release_savepoint_for_mysql(trx, name);
+
+	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
+}
+
+/*********************************************************************
+Sets a transaction savepoint. */
+static
+int
+innobase_savepoint(
+/*===============*/
+				/* out: always 0, that is, always succeeds */
+	THD*	thd,		/* in: handle to the MySQL thread */
+        void*	savepoint)      /* in: savepoint data */
+{
+	int	error = 0;
+	trx_t*	trx;
+
+	DBUG_ENTER("innobase_savepoint");
+
+        /*
+          In the autocommit mode there is no sense to set a savepoint
+          (unless we are in sub-statement), so SQL layer ensures that
+          this method is never called in such situation.
+        */
+        DBUG_ASSERT(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
+                    thd->in_sub_stmt);
+
+	trx = check_trx_exists(thd);
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+        /* cannot happen outside of transaction */
+        DBUG_ASSERT(trx->active_trans);
+
+        /* TODO: use provided savepoint data area to store savepoint data */
+        char name[64];
+        longlong2str((ulint)savepoint,name,36);
+
+        error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0);
+
+	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
+}
+
+/*********************************************************************
+Frees a possible InnoDB trx object associated with the current THD. */
+static
+int
+innobase_close_connection(
+/*======================*/
+			/* out: 0 or error number */
+	THD*	thd)	/* in: handle to the MySQL thread of the user
+			whose resources should be free'd */
+{
+	trx_t*	trx;
+
+	trx = (trx_t*)thd->ha_data[innobase_hton.slot];
+
+	ut_a(trx);
+
+        if (trx->active_trans == 0
+	    && trx->conc_state != TRX_NOT_STARTED) {
+
+	  sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+			  "TRX_NOT_STARTED");
+	}
+
+
+	if (trx->conc_state != TRX_NOT_STARTED &&
+            global_system_variables.log_warnings)
+          sql_print_warning("MySQL is closing a connection that has an active "
+                            "InnoDB transaction.  %lu row modifications will "
+                            "roll back.",
+                            (ulong)trx->undo_no.low);
+
+	innobase_rollback_trx(trx);
+
+        trx_free_for_mysql(trx);
+
+	return(0);
+}
+
+
+/*****************************************************************************
+** InnoDB database tables
+*****************************************************************************/
+
+/********************************************************************
+Get the record format from the data dictionary. */
+enum row_type
+ha_innobase::get_row_type() const
+/*=============================*/
+			/* out: ROW_TYPE_REDUNDANT or ROW_TYPE_COMPACT */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+
+	if (prebuilt && prebuilt->table) {
+		if (prebuilt->table->comp) {
+			return(ROW_TYPE_COMPACT);
+		} else {
+			return(ROW_TYPE_REDUNDANT);
+		}
+	}
+	ut_ad(0);
+	return(ROW_TYPE_NOT_USED);
+}
+
+/********************************************************************
+Gives the file extension of an InnoDB single-table tablespace. */
+static const char* ha_innobase_exts[] = {
+  ".ibd",
+  NullS
+};
+
+const char**
+ha_innobase::bas_ext() const
+/*========================*/
+				/* out: file extension string */
+{
+  return ha_innobase_exts;
+}
+
+
+/*********************************************************************
+Normalizes a table name string. A normalized name consists of the
+database name catenated to '/' and table name. An example:
+test/mytable. On Windows normalization puts both the database name and the
+table name always to lower case. */
+static
+void
+normalize_table_name(
+/*=================*/
+	char*		norm_name,	/* out: normalized name as a
+					null-terminated string */
+	const char*	name)		/* in: table name string */
+{
+	char*	name_ptr;
+	char*	db_ptr;
+	char*	ptr;
+
+	/* Scan name from the end */
+
+	ptr = strend(name)-1;
+
+	while (ptr >= name && *ptr != '\\' && *ptr != '/') {
+		ptr--;
+	}
+
+	name_ptr = ptr + 1;
+
+	DBUG_ASSERT(ptr > name);
+
+	ptr--;
+
+	while (ptr >= name && *ptr != '\\' && *ptr != '/') {
+		ptr--;
+	}
+
+	db_ptr = ptr + 1;
+
+	memcpy(norm_name, db_ptr, strlen(name) + 1 - (db_ptr - name));
+
+	norm_name[name_ptr - db_ptr - 1] = '/';
+
+#ifdef __WIN__
+	innobase_casedn_str(norm_name);
+#endif
+}
+
+/*********************************************************************
+Creates and opens a handle to a table which already exists in an InnoDB
+database. */
+
+int
+ha_innobase::open(
+/*==============*/
+					/* out: 1 if error, 0 if success */
+	const char*	name,		/* in: table name */
+	int 		mode,		/* in: not used */
+	uint 		test_if_locked)	/* in: not used */
+{
+	dict_table_t*	ib_table;
+  	char		norm_name[1000];
+	THD*		thd;
+
+	DBUG_ENTER("ha_innobase::open");
+
+	UT_NOT_USED(mode);
+	UT_NOT_USED(test_if_locked);
+
+	thd = current_thd;
+	normalize_table_name(norm_name, name);
+
+	user_thd = NULL;
+
+	last_query_id = (ulong)-1;
+
+	if (!(share=get_share(name))) {
+
+		DBUG_RETURN(1);
+	}
+
+	/* Create buffers for packing the fields of a record. Why
+	table->reclength did not work here? Obviously, because char
+	fields when packed actually became 1 byte longer, when we also
+	stored the string length as the first byte. */
+
+	upd_and_key_val_buff_len =
+				table->s->reclength + table->s->max_key_length
+							+ MAX_REF_PARTS * 3;
+	if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
+				     &upd_buff, upd_and_key_val_buff_len,
+				     &key_val_buff, upd_and_key_val_buff_len,
+				     NullS)) {
+	  	free_share(share);
+
+	  	DBUG_RETURN(1);
+  	}
+
+	/* Get pointer to a table object in InnoDB dictionary cache */
+
+	ib_table = dict_table_get_and_increment_handle_count(
+				      		     norm_name, NULL);
+ 	if (NULL == ib_table) {
+	        ut_print_timestamp(stderr);
+		sql_print_error("Cannot find table %s from the internal data "
+				"dictionary\nof InnoDB though the .frm file "
+				"for the table exists. Maybe you\nhave "
+				"deleted and recreated InnoDB data files but "
+				"have forgotten\nto delete the corresponding "
+				".frm files of InnoDB tables, or you\n"
+				"have moved .frm files to another database?\n"
+				"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
+				"how you can resolve the problem.\n",
+				norm_name);
+	        free_share(share);
+    		my_free((gptr) upd_buff, MYF(0));
+    		my_errno = ENOENT;
+
+    		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
+  	}
+
+ 	if (ib_table->ibd_file_missing && !thd->tablespace_op) {
+	        ut_print_timestamp(stderr);
+		sql_print_error("MySQL is trying to open a table handle but "
+				"the .ibd file for\ntable %s does not exist.\n"
+				"Have you deleted the .ibd file from the "
+				"database directory under\nthe MySQL datadir, "
+				"or have you used DISCARD TABLESPACE?\n"
+				"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
+				"how you can resolve the problem.\n",
+				norm_name);
+	        free_share(share);
+    		my_free((gptr) upd_buff, MYF(0));
+    		my_errno = ENOENT;
+
+		dict_table_decrement_handle_count(ib_table);
+    		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
+  	}
+
+	innobase_prebuilt = row_create_prebuilt(ib_table);
+
+	((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
+							table->s->reclength;
+
+	/* Looks like MySQL-3.23 sometimes has primary key number != 0 */
+
+ 	primary_key = table->s->primary_key;
+	key_used_on_scan = primary_key;
+
+	/* Allocate a buffer for a 'row reference'. A row reference is
+	a string of bytes of length ref_length which uniquely specifies
+        a row in our table. Note that MySQL may also compare two row
+        references for equality by doing a simple memcmp on the strings
+        of length ref_length! */
+
+  	if (!row_table_got_default_clust_index(ib_table)) {
+	        if (primary_key >= MAX_KEY) {
+		  sql_print_error("Table %s has a primary key in InnoDB data "
+				  "dictionary, but not in MySQL!", name);
+		}
+
+		((row_prebuilt_t*)innobase_prebuilt)
+				->clust_index_was_generated = FALSE;
+ 		/* MySQL allocates the buffer for ref. key_info->key_length
+		includes space for all key columns + one byte for each column
+		that may be NULL. ref_length must be as exact as possible to
+		save space, because all row reference buffers are allocated
+		based on ref_length. */
+
+  		ref_length = table->key_info[primary_key].key_length;
+	} else {
+	        if (primary_key != MAX_KEY) {
+		  sql_print_error("Table %s has no primary key in InnoDB data "
+				  "dictionary, but has one in MySQL! If you "
+				  "created the table with a MySQL version < "
+				  "3.23.54 and did not define a primary key, "
+				  "but defined a unique key with all non-NULL "
+				  "columns, then MySQL internally treats that "
+				  "key as the primary key. You can fix this "
+				  "error by dump + DROP + CREATE + reimport "
+				  "of the table.", name);
+		}
+
+		((row_prebuilt_t*)innobase_prebuilt)
+				->clust_index_was_generated = TRUE;
+
+  		ref_length = DATA_ROW_ID_LEN;
+
+		/* If we automatically created the clustered index, then
+		MySQL does not know about it, and MySQL must NOT be aware
+		of the index used on scan, to make it avoid checking if we
+		update the column of the index. That is why we assert below
+		that key_used_on_scan is the undefined value MAX_KEY.
+		The column is the row id in the automatical generation case,
+		and it will never be updated anyway. */
+
+		if (key_used_on_scan != MAX_KEY) {
+		  sql_print_warning("Table %s key_used_on_scan is %lu even "
+				    "though there is no primary key inside "
+				    "InnoDB.", name, (ulong) key_used_on_scan);
+		}
+	}
+
+	block_size = 16 * 1024;	/* Index block size in InnoDB: used by MySQL
+				in query optimization */
+
+	/* Init table lock structure */
+	thr_lock_data_init(&share->lock,&lock,(void*) 0);
+
+  	info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
+
+  	DBUG_RETURN(0);
+}
+
+uint
+ha_innobase::max_supported_key_part_length() const
+{
+	return(DICT_MAX_INDEX_COL_LEN - 1);
+}
+
+/**********************************************************************
+Closes a handle to an InnoDB table. */
+
+int
+ha_innobase::close(void)
+/*====================*/
+				/* out: 0 */
+{
+	THD*	thd;
+
+  	DBUG_ENTER("ha_innobase::close");
+
+	thd = current_thd;  // avoid calling current_thd twice, it may be slow
+	if (thd != NULL) {
+		innobase_release_temporary_latches(thd);
+	}
+
+	row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt);
+
+    	my_free((gptr) upd_buff, MYF(0));
+        free_share(share);
+
+	/* Tell InnoDB server that there might be work for
+	utility threads: */
+
+	srv_active_wake_master_thread();
+
+  	DBUG_RETURN(0);
+}
+
+/* The following accessor functions should really be inside MySQL code! */
+
+/******************************************************************
+Gets field offset for a field in a table. */
+inline
+uint
+get_field_offset(
+/*=============*/
+			/* out: offset */
+	TABLE*	table,	/* in: MySQL table object */
+	Field*	field)	/* in: MySQL field object */
+{
+	return((uint) (field->ptr - (char*) table->record[0]));
+}
+
+/******************************************************************
+Checks if a field in a record is SQL NULL. Uses the record format
+information in table to track the null bit in record. */
+inline
+uint
+field_in_record_is_null(
+/*====================*/
+			/* out: 1 if NULL, 0 otherwise */
+	TABLE*	table,	/* in: MySQL table object */
+	Field*	field,	/* in: MySQL field object */
+	char*	record)	/* in: a row in MySQL format */
+{
+	int	null_offset;
+
+	if (!field->null_ptr) {
+
+		return(0);
+	}
+
+	null_offset = (uint) ((char*) field->null_ptr
+					- (char*) table->record[0]);
+
+	if (record[null_offset] & field->null_bit) {
+
+		return(1);
+	}
+
+	return(0);
+}
+
+/******************************************************************
+Sets a field in a record to SQL NULL. Uses the record format
+information in table to track the null bit in record. */
+inline
+void
+set_field_in_record_to_null(
+/*========================*/
+	TABLE*	table,	/* in: MySQL table object */
+	Field*	field,	/* in: MySQL field object */
+	char*	record)	/* in: a row in MySQL format */
+{
+	int	null_offset;
+
+	null_offset = (uint) ((char*) field->null_ptr
+					- (char*) table->record[0]);
+
+	record[null_offset] = record[null_offset] | field->null_bit;
+}
+
+extern "C" {
+/*****************************************************************
+InnoDB uses this function to compare two data fields for which the data type
+is such that we must use MySQL code to compare them. NOTE that the prototype
+of this function is in rem0cmp.c in InnoDB source code! If you change this
+function, remember to update the prototype there! */
+
+int
+innobase_mysql_cmp(
+/*===============*/
+					/* out: 1, 0, -1, if a is greater,
+					equal, less than b, respectively */
+	int		mysql_type,	/* in: MySQL type */
+	uint		charset_number,	/* in: number of the charset */
+	unsigned char*	a,		/* in: data field */
+	unsigned int	a_length,	/* in: data field length,
+					not UNIV_SQL_NULL */
+	unsigned char*	b,		/* in: data field */
+	unsigned int	b_length)	/* in: data field length,
+					not UNIV_SQL_NULL */
+{
+	CHARSET_INFO*		charset;
+	enum_field_types	mysql_tp;
+	int                     ret;
+
+	DBUG_ASSERT(a_length != UNIV_SQL_NULL);
+	DBUG_ASSERT(b_length != UNIV_SQL_NULL);
+
+	mysql_tp = (enum_field_types) mysql_type;
+
+	switch (mysql_tp) {
+
+        case MYSQL_TYPE_BIT:
+	case MYSQL_TYPE_STRING:
+	case MYSQL_TYPE_VAR_STRING:
+	case FIELD_TYPE_TINY_BLOB:
+	case FIELD_TYPE_MEDIUM_BLOB:
+	case FIELD_TYPE_BLOB:
+	case FIELD_TYPE_LONG_BLOB:
+        case MYSQL_TYPE_VARCHAR:
+		/* Use the charset number to pick the right charset struct for
+		the comparison. Since the MySQL function get_charset may be
+		slow before Bar removes the mutex operation there, we first
+		look at 2 common charsets directly. */
+
+		if (charset_number == default_charset_info->number) {
+			charset = default_charset_info;
+		} else if (charset_number == my_charset_latin1.number) {
+			charset = &my_charset_latin1;
+		} else {
+			charset = get_charset(charset_number, MYF(MY_WME));
+
+			if (charset == NULL) {
+			  sql_print_error("InnoDB needs charset %lu for doing "
+					  "a comparison, but MySQL cannot "
+					  "find that charset.",
+					  (ulong) charset_number);
+				ut_a(0);
+			}
+		}
+
+                /* Starting from 4.1.3, we use strnncollsp() in comparisons of
+                non-latin1_swedish_ci strings. NOTE that the collation order
+                changes then: 'b\0\0...' is ordered BEFORE 'b  ...'. Users
+                having indexes on such data need to rebuild their tables! */
+
+                ret = charset->coll->strnncollsp(charset,
+                                  a, a_length,
+                                                 b, b_length, 0);
+		if (ret < 0) {
+		        return(-1);
+		} else if (ret > 0) {
+		        return(1);
+		} else {
+		        return(0);
+	        }
+	default:
+		assert(0);
+	}
+
+	return(0);
+}
+}
+
+/******************************************************************
+Converts a MySQL type to an InnoDB type. Note that this function returns
+the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
+VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'. */
+inline
+ulint
+get_innobase_type_from_mysql_type(
+/*==============================*/
+				/* out: DATA_BINARY, DATA_VARCHAR, ... */
+	ulint*	unsigned_flag,	/* out: DATA_UNSIGNED if an 'unsigned type';
+				at least ENUM and SET, and unsigned integer
+				types are 'unsigned types' */
+	Field*	field)		/* in: MySQL field */
+{
+	/* The following asserts try to check that the MySQL type code fits in
+	8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to
+	the type */
+
+	DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256);
+	DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256);
+	DBUG_ASSERT((ulint)FIELD_TYPE_DOUBLE < 256);
+	DBUG_ASSERT((ulint)FIELD_TYPE_FLOAT < 256);
+	DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256);
+
+	if (field->flags & UNSIGNED_FLAG) {
+
+		*unsigned_flag = DATA_UNSIGNED;
+	} else {
+		*unsigned_flag = 0;
+	}
+
+	if (field->real_type() == FIELD_TYPE_ENUM
+	    || field->real_type() == FIELD_TYPE_SET) {
+
+		/* MySQL has field->type() a string type for these, but the
+		data is actually internally stored as an unsigned integer
+		code! */
+
+		*unsigned_flag = DATA_UNSIGNED; /* MySQL has its own unsigned
+						flag set to zero, even though
+						internally this is an unsigned
+						integer type */
+		return(DATA_INT);
+	}
+
+	switch (field->type()) {
+	        /* NOTE that we only allow string types in DATA_MYSQL
+		and DATA_VARMYSQL */
+                case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
+                case MYSQL_TYPE_VARCHAR:    /* new >= 5.0.3 true VARCHAR */
+					if (field->binary()) {
+						return(DATA_BINARY);
+					} else if (strcmp(
+						  field->charset()->name,
+						 "latin1_swedish_ci") == 0) {
+						return(DATA_VARCHAR);
+					} else {
+						return(DATA_VARMYSQL);
+					}
+                case MYSQL_TYPE_BIT:
+		case MYSQL_TYPE_STRING: if (field->binary()) {
+
+						return(DATA_FIXBINARY);
+					} else if (strcmp(
+						   field->charset()->name,
+						   "latin1_swedish_ci") == 0) {
+						return(DATA_CHAR);
+					} else {
+						return(DATA_MYSQL);
+					}
+                case FIELD_TYPE_NEWDECIMAL:
+                                        return(DATA_FIXBINARY);
+		case FIELD_TYPE_LONG:
+		case FIELD_TYPE_LONGLONG:
+		case FIELD_TYPE_TINY:
+		case FIELD_TYPE_SHORT:
+		case FIELD_TYPE_INT24:
+		case FIELD_TYPE_DATE:
+		case FIELD_TYPE_DATETIME:
+		case FIELD_TYPE_YEAR:
+		case FIELD_TYPE_NEWDATE:
+		case FIELD_TYPE_TIME:
+		case FIELD_TYPE_TIMESTAMP:
+					return(DATA_INT);
+		case FIELD_TYPE_FLOAT:
+					return(DATA_FLOAT);
+		case FIELD_TYPE_DOUBLE:
+					return(DATA_DOUBLE);
+		case FIELD_TYPE_DECIMAL:
+					return(DATA_DECIMAL);
+		case FIELD_TYPE_GEOMETRY:
+		case FIELD_TYPE_TINY_BLOB:
+		case FIELD_TYPE_MEDIUM_BLOB:
+		case FIELD_TYPE_BLOB:
+		case FIELD_TYPE_LONG_BLOB:
+					return(DATA_BLOB);
+		default:
+					assert(0);
+	}
+
+	return(0);
+}
+
+/***********************************************************************
+Writes an unsigned integer value < 64k to 2 bytes, in the little-endian
+storage format. */
+inline
+void
+innobase_write_to_2_little_endian(
+/*==============================*/
+	byte*	buf,	/* in: where to store */
+	ulint	val)	/* in: value to write, must be < 64k */
+{
+	ut_a(val < 256 * 256);
+
+	buf[0] = (byte)(val & 0xFF);
+	buf[1] = (byte)(val / 256);
+}
+
+/***********************************************************************
+Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
+storage format. */
+inline
+uint
+innobase_read_from_2_little_endian(
+/*===============================*/
+			/* out: value */
+	const mysql_byte*	buf)	/* in: from where to read */
+{
+	return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
+}
+
+/***********************************************************************
+Stores a key value for a row to a buffer. */
+
+uint
+ha_innobase::store_key_val_for_row(
+/*===============================*/
+				/* out: key value length as stored in buff */
+	uint 		keynr,	/* in: key number */
+	char*		buff,	/* in/out: buffer for the key value (in MySQL
+				format) */
+	uint		buff_len,/* in: buffer length */
+	const mysql_byte* record)/* in: row in MySQL format */
+{
+	KEY*		key_info 	= table->key_info + keynr;
+  	KEY_PART_INFO*	key_part	= key_info->key_part;
+  	KEY_PART_INFO*	end		= key_part + key_info->key_parts;
+	char*		buff_start	= buff;
+	enum_field_types mysql_type;
+	Field*		field;
+	ibool		is_null;
+
+  	DBUG_ENTER("store_key_val_for_row");
+
+	/* The format for storing a key field in MySQL is the following:
+
+	1. If the column can be NULL, then in the first byte we put 1 if the
+	field value is NULL, 0 otherwise.
+
+	2. If the column is of a BLOB type (it must be a column prefix field
+	in this case), then we put the length of the data in the field to the
+	next 2 bytes, in the little-endian format. If the field is SQL NULL,
+	then these 2 bytes are set to 0. Note that the length of data in the
+	field is <= column prefix length.
+
+	3. In a column prefix field, prefix_len next bytes are reserved for
+	data. In a normal field the max field length next bytes are reserved
+	for data. For a VARCHAR(n) the max field length is n. If the stored
+	value is the SQL NULL then these data bytes are set to 0.
+
+	4. We always use a 2 byte length for a true >= 5.0.3 VARCHAR. Note that
+	in the MySQL row format, the length is stored in 1 or 2 bytes,
+	depending on the maximum allowed length. But in the MySQL key value
+	format, the length always takes 2 bytes.
+
+	We have to zero-fill the buffer so that MySQL is able to use a
+	simple memcmp to compare two key values to determine if they are
+	equal. MySQL does this to compare contents of two 'ref' values. */
+
+	bzero(buff, buff_len);
+
+  	for (; key_part != end; key_part++) {
+	        is_null = FALSE;
+
+    		if (key_part->null_bit) {
+      			if (record[key_part->null_offset]
+						& key_part->null_bit) {
+				*buff = 1;
+				is_null = TRUE;
+      			} else {
+				*buff = 0;
+			}
+			buff++;
+    		}
+
+		field = key_part->field;
+		mysql_type = field->type();
+
+		if (mysql_type == MYSQL_TYPE_VARCHAR) {
+						/* >= 5.0.3 true VARCHAR */
+			ulint	lenlen;
+			ulint	len;
+			byte*	data;
+			ulint	key_len;
+			ulint	true_len;
+			CHARSET_INFO*	cs;
+			int	error=0;
+
+			key_len = key_part->length;
+
+			if (is_null) {
+				buff += key_len + 2;
+
+				continue;
+			}
+			cs = field->charset();
+
+			lenlen = (ulint)
+				(((Field_varstring*)field)->length_bytes);
+
+			data = row_mysql_read_true_varchar(&len,
+				(byte*) (record
+				+ (ulint)get_field_offset(table, field)),
+				lenlen);
+
+			true_len = len;
+
+			/* For multi byte character sets we need to calculate
+			the true length of the key */
+
+			if (len > 0 && cs->mbmaxlen > 1) {
+				true_len = (ulint) cs->cset->well_formed_len(cs,
+						(const char *) data,
+						(const char *) data + len,
+                                                (uint) (key_len /
+                                                        cs->mbmaxlen),
+						&error);
+			}
+
+			/* In a column prefix index, we may need to truncate
+			the stored value: */
+
+			if (true_len > key_len) {
+				true_len = key_len;
+			}
+
+			/* The length in a key value is always stored in 2
+			bytes */
+
+			row_mysql_store_true_var_len((byte*)buff, true_len, 2);
+			buff += 2;
+
+			memcpy(buff, data, true_len);
+
+			/* Note that we always reserve the maximum possible
+			length of the true VARCHAR in the key value, though
+			only len first bytes after the 2 length bytes contain
+			actual data. The rest of the space was reset to zero
+			in the bzero() call above. */
+
+			buff += key_len;
+
+		} else if (mysql_type == FIELD_TYPE_TINY_BLOB
+		    || mysql_type == FIELD_TYPE_MEDIUM_BLOB
+		    || mysql_type == FIELD_TYPE_BLOB
+		    || mysql_type == FIELD_TYPE_LONG_BLOB) {
+
+			CHARSET_INFO*	cs;
+			ulint		key_len;
+			ulint		true_len;
+			int		error=0;
+			ulint		blob_len;
+			byte*		blob_data;
+
+			ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
+
+			key_len = key_part->length;
+
+		        if (is_null) {
+				buff += key_len + 2;
+
+				continue;
+			}
+
+			cs = field->charset();
+
+		        blob_data = row_mysql_read_blob_ref(&blob_len,
+				(byte*) (record
+				+ (ulint)get_field_offset(table, field)),
+					(ulint) field->pack_length());
+
+			true_len = blob_len;
+
+			ut_a(get_field_offset(table, field)
+						     == key_part->offset);
+
+			/* For multi byte character sets we need to calculate
+			the true length of the key */
+
+			if (blob_len > 0 && cs->mbmaxlen > 1) {
+				true_len = (ulint) cs->cset->well_formed_len(cs,
+						(const char *) blob_data,
+						(const char *) blob_data
+							+ blob_len,
+                                                (uint) (key_len /
+                                                        cs->mbmaxlen),
+						&error);
+			}
+
+			/* All indexes on BLOB and TEXT are column prefix
+			indexes, and we may need to truncate the data to be
+			stored in the key value: */
+
+			if (true_len > key_len) {
+				true_len = key_len;
+			}
+
+			/* MySQL reserves 2 bytes for the length and the
+			storage of the number is little-endian */
+
+			innobase_write_to_2_little_endian(
+					(byte*)buff, true_len);
+			buff += 2;
+
+			memcpy(buff, blob_data, true_len);
+
+			/* Note that we always reserve the maximum possible
+			length of the BLOB prefix in the key value. */
+
+			buff += key_len;
+		} else {
+			/* Here we handle all other data types except the
+			true VARCHAR, BLOB and TEXT. Note that the column
+			value we store may be also in a column prefix
+			index. */
+
+			CHARSET_INFO*		cs;
+			ulint			true_len;
+			ulint			key_len;
+			const mysql_byte*	src_start;
+			int			error=0;
+			enum_field_types	real_type;
+
+			key_len = key_part->length;
+
+		        if (is_null) {
+				 buff += key_len;
+
+				 continue;
+			}
+
+			src_start = record + key_part->offset;
+			real_type = field->real_type();
+			true_len = key_len;
+
+			/* Character set for the field is defined only
+			to fields whose type is string and real field
+			type is not enum or set. For these fields check
+			if character set is multi byte. */
+
+			if (real_type != FIELD_TYPE_ENUM
+				&& real_type != FIELD_TYPE_SET
+				&& ( mysql_type == MYSQL_TYPE_VAR_STRING
+					|| mysql_type == MYSQL_TYPE_STRING)) {
+
+				cs = field->charset();
+
+				/* For multi byte character sets we need to
+				calculate the true length of the key */
+
+				if (key_len > 0 && cs->mbmaxlen > 1) {
+
+					true_len = (ulint)
+						cs->cset->well_formed_len(cs,
+							(const char *)src_start,
+							(const char *)src_start
+								+ key_len,
+                                                        (uint) (key_len /
+                                                                cs->mbmaxlen),
+							&error);
+				}
+			}
+
+			memcpy(buff, src_start, true_len);
+			buff += true_len;
+
+			/* Pad the unused space with spaces. Note that no
+			padding is ever needed for UCS-2 because in MySQL,
+			all UCS2 characters are 2 bytes, as MySQL does not
+			support surrogate pairs, which are needed to represent
+			characters in the range U+10000 to U+10FFFF. */
+
+			if (true_len < key_len) {
+				ulint pad_len = key_len - true_len;
+				memset(buff, ' ', pad_len);
+				buff += pad_len;
+			}
+		}
+  	}
+
+	ut_a(buff <= buff_start + buff_len);
+
+	DBUG_RETURN((uint)(buff - buff_start));
+}
+
+/******************************************************************
+Builds a 'template' to the prebuilt struct. The template is used in fast
+retrieval of just those column values MySQL needs in its processing. */
+static
+void
+build_template(
+/*===========*/
+	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct */
+	THD*		thd,		/* in: current user thread, used
+					only if templ_type is
+					ROW_MYSQL_REC_FIELDS */
+	TABLE*		table,		/* in: MySQL table */
+	ulint		templ_type)	/* in: ROW_MYSQL_WHOLE_ROW or
+					ROW_MYSQL_REC_FIELDS */
+{
+	dict_index_t*	index;
+	dict_index_t*	clust_index;
+	mysql_row_templ_t* templ;
+	Field*		field;
+	ulint		n_fields;
+	ulint		n_requested_fields	= 0;
+	ibool		fetch_all_in_key	= FALSE;
+	ibool		fetch_primary_key_cols	= FALSE;
+	ulint		i;
+	/* byte offset of the end of last requested column */
+	ulint		mysql_prefix_len	= 0;
+
+	if (prebuilt->select_lock_type == LOCK_X) {
+		/* We always retrieve the whole clustered index record if we
+		use exclusive row level locks, for example, if the read is
+		done in an UPDATE statement. */
+
+	        templ_type = ROW_MYSQL_WHOLE_ROW;
+	}
+
+	if (templ_type == ROW_MYSQL_REC_FIELDS) {
+	     if (prebuilt->hint_need_to_fetch_extra_cols
+						== ROW_RETRIEVE_ALL_COLS) {
+
+		/* We know we must at least fetch all columns in the key, or
+		all columns in the table */
+
+		if (prebuilt->read_just_key) {
+			/* MySQL has instructed us that it is enough to
+			fetch the columns in the key; looks like MySQL
+			can set this flag also when there is only a
+			prefix of the column in the key: in that case we
+			retrieve the whole column from the clustered
+			index */
+
+			fetch_all_in_key = TRUE;
+		} else {
+			templ_type = ROW_MYSQL_WHOLE_ROW;
+		}
+	    } else if (prebuilt->hint_need_to_fetch_extra_cols
+						== ROW_RETRIEVE_PRIMARY_KEY) {
+		/* We must at least fetch all primary key cols. Note that if
+		the clustered index was internally generated by InnoDB on the
+		row id (no primary key was defined), then
+		row_search_for_mysql() will always retrieve the row id to a
+		special buffer in the prebuilt struct. */
+
+		fetch_primary_key_cols = TRUE;
+	    }
+	}
+
+	clust_index = dict_table_get_first_index_noninline(prebuilt->table);
+
+	if (templ_type == ROW_MYSQL_REC_FIELDS) {
+		index = prebuilt->index;
+	} else {
+		index = clust_index;
+	}
+
+	if (index == clust_index) {
+		prebuilt->need_to_access_clustered = TRUE;
+	} else {
+		prebuilt->need_to_access_clustered = FALSE;
+		/* Below we check column by column if we need to access
+		the clustered index */
+	}
+
+	n_fields = (ulint)table->s->fields; /* number of columns */
+
+	if (!prebuilt->mysql_template) {
+		prebuilt->mysql_template = (mysql_row_templ_t*)
+						mem_alloc_noninline(
+					n_fields * sizeof(mysql_row_templ_t));
+	}
+
+	prebuilt->template_type = templ_type;
+	prebuilt->null_bitmap_len = table->s->null_bytes;
+
+	prebuilt->templ_contains_blob = FALSE;
+
+	/* Note that in InnoDB, i is the column number. MySQL calls columns
+	'fields'. */
+	for (i = 0; i < n_fields; i++) {
+		templ = prebuilt->mysql_template + n_requested_fields;
+		field = table->field[i];
+
+		if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
+			/* Decide which columns we should fetch
+			and which we can skip. */
+			register const ibool	index_contains_field =
+				dict_index_contains_col_or_prefix(index, i);
+
+			if (!index_contains_field && prebuilt->read_just_key) {
+				/* If this is a 'key read', we do not need
+				columns that are not in the key */
+
+				goto skip_field;
+			}
+
+			if (index_contains_field && fetch_all_in_key) {
+				/* This field is needed in the query */
+
+				goto include_field;
+			}
+
+			if (thd->query_id == field->query_id) {
+				/* This field is needed in the query */
+
+				goto include_field;
+			}
+
+			if (fetch_primary_key_cols
+			    && dict_table_col_in_clustered_key(index->table,
+									i)) {
+				/* This field is needed in the query */
+
+				goto include_field;
+			}
+
+			/* This field is not needed in the query, skip it */
+
+			goto skip_field;
+		}
+include_field:
+		n_requested_fields++;
+
+		templ->col_no = i;
+
+		if (index == clust_index) {
+			templ->rec_field_no = (index->table->cols + i)
+								->clust_pos;
+		} else {
+			templ->rec_field_no = dict_index_get_nth_col_pos(
+								index, i);
+		}
+
+		if (templ->rec_field_no == ULINT_UNDEFINED) {
+			prebuilt->need_to_access_clustered = TRUE;
+		}
+
+		if (field->null_ptr) {
+			templ->mysql_null_byte_offset =
+				(ulint) ((char*) field->null_ptr
+					- (char*) table->record[0]);
+
+			templ->mysql_null_bit_mask = (ulint) field->null_bit;
+		} else {
+			templ->mysql_null_bit_mask = 0;
+		}
+
+		templ->mysql_col_offset = (ulint)
+					get_field_offset(table, field);
+
+		templ->mysql_col_len = (ulint) field->pack_length();
+		if (mysql_prefix_len < templ->mysql_col_offset
+				+ templ->mysql_col_len) {
+			mysql_prefix_len = templ->mysql_col_offset
+				+ templ->mysql_col_len;
+		}
+		templ->type = index->table->cols[i].type.mtype;
+		templ->mysql_type = (ulint)field->type();
+
+		if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
+			templ->mysql_length_bytes = (ulint)
+				    (((Field_varstring*)field)->length_bytes);
+		}
+
+		templ->charset = dtype_get_charset_coll_noninline(
+				index->table->cols[i].type.prtype);
+		templ->mbminlen = index->table->cols[i].type.mbminlen;
+		templ->mbmaxlen = index->table->cols[i].type.mbmaxlen;
+		templ->is_unsigned = index->table->cols[i].type.prtype
+							& DATA_UNSIGNED;
+		if (templ->type == DATA_BLOB) {
+			prebuilt->templ_contains_blob = TRUE;
+		}
+skip_field:
+		;
+	}
+
+	prebuilt->n_template = n_requested_fields;
+	prebuilt->mysql_prefix_len = mysql_prefix_len;
+
+	if (index != clust_index && prebuilt->need_to_access_clustered) {
+		/* Change rec_field_no's to correspond to the clustered index
+		record */
+		for (i = 0; i < n_requested_fields; i++) {
+			templ = prebuilt->mysql_template + i;
+
+			templ->rec_field_no =
+			    (index->table->cols + templ->col_no)->clust_pos;
+		}
+	}
+}
+
+/************************************************************************
+Stores a row in an InnoDB database, to the table specified in this
+handle. */
+
+int
+ha_innobase::write_row(
+/*===================*/
+				/* out: error code */
+	mysql_byte* 	record)	/* in: a row in MySQL format */
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
+  	int 		error;
+	longlong	auto_inc;
+	longlong	dummy;
+	ibool           auto_inc_used= FALSE;
+
+  	DBUG_ENTER("ha_innobase::write_row");
+
+	if (prebuilt->trx !=
+                        (trx_t*) current_thd->ha_data[innobase_hton.slot]) {
+	  sql_print_error("The transaction object for the table handle is at "
+			  "%p, but for the current thread it is at %p",
+			  prebuilt->trx,
+			  (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+		fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
+		ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
+		fputs("\n"
+			"InnoDB: Dump of 200 bytes around transaction.all: ",
+			stderr);
+		ut_print_buf(stderr,
+           	 ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100,
+								200);
+		putc('\n', stderr);
+		ut_error;
+	}
+
+  	statistic_increment(current_thd->status_var.ha_write_count,
+			    &LOCK_status);
+
+        if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
+                table->timestamp_field->set_time();
+
+	if ((user_thd->lex->sql_command == SQLCOM_ALTER_TABLE
+	    || user_thd->lex->sql_command == SQLCOM_OPTIMIZE
+	    || user_thd->lex->sql_command == SQLCOM_CREATE_INDEX
+	    || user_thd->lex->sql_command == SQLCOM_DROP_INDEX)
+	    && num_write_row >= 10000) {
+		/* ALTER TABLE is COMMITted at every 10000 copied rows.
+		The IX table lock for the original table has to be re-issued.
+		As this method will be called on a temporary table where the
+		contents of the original table is being copied to, it is
+		a bit tricky to determine the source table.  The cursor
+		position in the source table need not be adjusted after the
+		intermediate COMMIT, since writes by other transactions are
+		being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
+
+		dict_table_t*	src_table;
+		ulint		mode;
+
+		num_write_row = 0;
+
+		/* Commit the transaction.  This will release the table
+		locks, so they have to be acquired again. */
+
+		/* Altering an InnoDB table */
+		/* Get the source table. */
+		src_table = lock_get_src_table(
+				prebuilt->trx, prebuilt->table, &mode);
+		if (!src_table) {
+no_commit:
+			/* Unknown situation: do not commit */
+			/*
+			ut_print_timestamp(stderr);
+			fprintf(stderr,
+				"  InnoDB error: ALTER TABLE is holding lock"
+				" on %lu tables!\n",
+				prebuilt->trx->mysql_n_tables_locked);
+			*/
+			;
+		} else if (src_table == prebuilt->table) {
+			/* Source table is not in InnoDB format:
+			no need to re-acquire locks on it. */
+
+			/* Altering to InnoDB format */
+                        innobase_commit(user_thd, 1);
+			/* Note that this transaction is still active. */
+			prebuilt->trx->active_trans = 1;
+			/* We will need an IX lock on the destination table. */
+		        prebuilt->sql_stat_start = TRUE;
+		} else {
+			/* Ensure that there are no other table locks than
+			LOCK_IX and LOCK_AUTO_INC on the destination table. */
+
+			if (!lock_is_table_exclusive(prebuilt->table,
+							prebuilt->trx)) {
+				goto no_commit;
+			}
+
+			/* Commit the transaction.  This will release the table
+			locks, so they have to be acquired again. */
+                        innobase_commit(user_thd, 1);
+			/* Note that this transaction is still active. */
+			prebuilt->trx->active_trans = 1;
+			/* Re-acquire the table lock on the source table. */
+			row_lock_table_for_mysql(prebuilt, src_table, mode);
+			/* We will need an IX lock on the destination table. */
+		        prebuilt->sql_stat_start = TRUE;
+		}
+	}
+
+	num_write_row++;
+
+	if (last_query_id != user_thd->query_id) {
+	        prebuilt->sql_stat_start = TRUE;
+                last_query_id = user_thd->query_id;
+
+		innobase_release_stat_resources(prebuilt->trx);
+	}
+
+  	if (table->next_number_field && record == table->record[0]) {
+		/* This is the case where the table has an
+		auto-increment column */
+
+		/* Initialize the auto-inc counter if it has not been
+		initialized yet */
+
+		if (0 == dict_table_autoinc_peek(prebuilt->table)) {
+
+			/* This call initializes the counter */
+		        error = innobase_read_and_init_auto_inc(&dummy);
+
+			if (error) {
+				/* Deadlock or lock wait timeout */
+
+				goto func_exit;
+			}
+
+			/* We have to set sql_stat_start to TRUE because
+			the above call probably has called a select, and
+			has reset that flag; row_insert_for_mysql has to
+			know to set the IX intention lock on the table,
+			something it only does at the start of each
+			statement */
+
+			prebuilt->sql_stat_start = TRUE;
+		}
+
+		/* We have to use the transactional lock mechanism on the
+		auto-inc counter of the table to ensure that replication and
+		roll-forward of the binlog exactly imitates also the given
+		auto-inc values. The lock is released at each SQL statement's
+		end. This lock also prevents a race where two threads would
+		call ::get_auto_increment() simultaneously. */
+
+		error = row_lock_table_autoinc_for_mysql(prebuilt);
+
+		if (error != DB_SUCCESS) {
+			/* Deadlock or lock wait timeout */
+
+			error = convert_error_code_to_mysql(error, user_thd);
+
+			goto func_exit;
+		}
+
+		/* We must use the handler code to update the auto-increment
+                value to be sure that we increment it correctly. */
+
+    		if ((error= update_auto_increment()))
+			goto func_exit;
+                auto_inc_used = 1;
+
+	}
+
+	if (prebuilt->mysql_template == NULL
+			|| prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
+		/* Build the template used in converting quickly between
+		the two database formats */
+
+		build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
+	}
+
+	innodb_srv_conc_enter_innodb(prebuilt->trx);
+
+	error = row_insert_for_mysql((byte*) record, prebuilt);
+
+        if (error == DB_SUCCESS) rows_changed++;
+
+	if (error == DB_SUCCESS && auto_inc_used) {
+
+        	/* Fetch the value that was set in the autoincrement field */
+
+          	auto_inc = table->next_number_field->val_int();
+
+          	if (auto_inc != 0) {
+			/* This call will update the counter according to the
+			value that was inserted in the table */
+
+            		dict_table_autoinc_update(prebuilt->table, auto_inc);
+          	}
+        }
+
+        /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
+        key error themselves, and we must update the autoinc counter if we are
+        performing those statements. */
+
+        if (error == DB_DUPLICATE_KEY && auto_inc_used
+            && (user_thd->lex->sql_command == SQLCOM_REPLACE
+                || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
+                || (user_thd->lex->sql_command == SQLCOM_LOAD
+                    && user_thd->lex->duplicates == DUP_REPLACE))) {
+
+                auto_inc = table->next_number_field->val_int();
+
+                if (auto_inc != 0) {
+                        dict_table_autoinc_update(prebuilt->table, auto_inc);
+                }
+        }
+
+	innodb_srv_conc_exit_innodb(prebuilt->trx);
+
+	error = convert_error_code_to_mysql(error, user_thd);
+
+	/* Tell InnoDB server that there might be work for
+	utility threads: */
+func_exit:
+	innobase_active_small();
+
+  	DBUG_RETURN(error);
+}
+
+/**************************************************************************
+Checks which fields have changed in a row and stores information
+of them to an update vector. */
+static
+int
+calc_row_difference(
+/*================*/
+					/* out: error number or 0 */
+	upd_t*		uvect,		/* in/out: update vector */
+	mysql_byte* 	old_row,	/* in: old row in MySQL format */
+	mysql_byte* 	new_row,	/* in: new row in MySQL format */
+	struct st_table* table,		/* in: table in MySQL data
+					dictionary */
+	mysql_byte*	upd_buff,	/* in: buffer to use */
+	ulint		buff_len,	/* in: buffer length */
+	row_prebuilt_t*	prebuilt,	/* in: InnoDB prebuilt struct */
+	THD*		thd)		/* in: user thread */
+{
+	mysql_byte*	original_upd_buff = upd_buff;
+	Field*		field;
+	enum_field_types field_mysql_type;
+	uint		n_fields;
+	ulint		o_len;
+	ulint		n_len;
+	ulint		col_pack_len;
+	byte*		new_mysql_row_col;
+	byte*	        o_ptr;
+        byte*	        n_ptr;
+        byte*	        buf;
+	upd_field_t*	ufield;
+	ulint		col_type;
+	ulint		n_changed = 0;
+	dfield_t	dfield;
+	uint		i;
+
+	n_fields = table->s->fields;
+
+	/* We use upd_buff to convert changed fields */
+	buf = (byte*) upd_buff;
+
+	for (i = 0; i < n_fields; i++) {
+		field = table->field[i];
+
+		/* if (thd->query_id != field->query_id) { */
+			/* TODO: check that these fields cannot have
+			changed! */
+
+		/*	goto skip_field;
+		}*/
+
+		o_ptr = (byte*) old_row + get_field_offset(table, field);
+		n_ptr = (byte*) new_row + get_field_offset(table, field);
+
+		/* Use new_mysql_row_col and col_pack_len save the values */
+
+		new_mysql_row_col = n_ptr;
+		col_pack_len = field->pack_length();
+
+		o_len = col_pack_len;
+		n_len = col_pack_len;
+
+		/* We use o_ptr and n_ptr to dig up the actual data for
+		comparison. */
+
+		field_mysql_type = field->type();
+
+		col_type = prebuilt->table->cols[i].type.mtype;
+
+		switch (col_type) {
+
+		case DATA_BLOB:
+			o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
+			n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
+
+			break;
+
+		case DATA_VARCHAR:
+		case DATA_BINARY:
+		case DATA_VARMYSQL:
+			if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
+				/* This is a >= 5.0.3 type true VARCHAR where
+				the real payload data length is stored in
+				1 or 2 bytes */
+
+				o_ptr = row_mysql_read_true_varchar(
+						&o_len, o_ptr,
+				    (ulint)
+				    (((Field_varstring*)field)->length_bytes));
+
+				n_ptr = row_mysql_read_true_varchar(
+						&n_len, n_ptr,
+				    (ulint)
+				    (((Field_varstring*)field)->length_bytes));
+			}
+
+			break;
+		default:
+			;
+		}
+
+		if (field->null_ptr) {
+			if (field_in_record_is_null(table, field,
+							(char*) old_row)) {
+				o_len = UNIV_SQL_NULL;
+			}
+
+			if (field_in_record_is_null(table, field,
+							(char*) new_row)) {
+				n_len = UNIV_SQL_NULL;
+			}
+		}
+
+		if (o_len != n_len || (o_len != UNIV_SQL_NULL &&
+					0 != memcmp(o_ptr, n_ptr, o_len))) {
+			/* The field has changed */
+
+			ufield = uvect->fields + n_changed;
+
+			/* Let us use a dummy dfield to make the conversion
+			from the MySQL column format to the InnoDB format */
+
+			dfield.type = (prebuilt->table->cols + i)->type;
+
+			if (n_len != UNIV_SQL_NULL) {
+				buf = row_mysql_store_col_in_innobase_format(
+						&dfield,
+						(byte*)buf,
+						TRUE,
+						new_mysql_row_col,
+						col_pack_len,
+						prebuilt->table->comp);
+				ufield->new_val.data = dfield.data;
+				ufield->new_val.len = dfield.len;
+			} else {
+				ufield->new_val.data = NULL;
+				ufield->new_val.len = UNIV_SQL_NULL;
+			}
+
+			ufield->exp = NULL;
+			ufield->field_no = prebuilt->table->cols[i].clust_pos;
+			n_changed++;
+		}
+	}
+
+	uvect->n_fields = n_changed;
+	uvect->info_bits = 0;
+
+	ut_a(buf <= (byte*)original_upd_buff + buff_len);
+
+	return(0);
+}
+
+/**************************************************************************
+Updates a row given as a parameter to a new value. Note that we are given
+whole rows, not just the fields which are updated: this incurs some
+overhead for CPU when we check which fields are actually updated.
+TODO: currently InnoDB does not prevent the 'Halloween problem':
+in a searched update a single row can get updated several times
+if its index columns are updated! */
+
+int
+ha_innobase::update_row(
+/*====================*/
+					/* out: error number or 0 */
+	const mysql_byte* 	old_row,/* in: old row in MySQL format */
+	mysql_byte* 		new_row)/* in: new row in MySQL format */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	upd_t*		uvect;
+	int		error = 0;
+
+	DBUG_ENTER("ha_innobase::update_row");
+
+	ut_ad(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+        if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+                table->timestamp_field->set_time();
+
+	if (last_query_id != user_thd->query_id) {
+	        prebuilt->sql_stat_start = TRUE;
+                last_query_id = user_thd->query_id;
+
+		innobase_release_stat_resources(prebuilt->trx);
+	}
+
+	if (prebuilt->upd_node) {
+		uvect = prebuilt->upd_node->update;
+	} else {
+		uvect = row_get_prebuilt_update_vector(prebuilt);
+	}
+
+	/* Build an update vector from the modified fields in the rows
+	(uses upd_buff of the handle) */
+
+	calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table,
+			upd_buff, (ulint)upd_and_key_val_buff_len,
+			prebuilt, user_thd);
+
+	/* This is not a delete */
+	prebuilt->upd_node->is_delete = FALSE;
+
+	assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
+
+	innodb_srv_conc_enter_innodb(prebuilt->trx);
+
+	error = row_update_for_mysql((byte*) old_row, prebuilt);
+
+	/* We need to do some special AUTOINC handling for the following case:
+
+	    INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ...
+
+	We need to use the AUTOINC counter that was actually used by
+	MySQL in the UPDATE statement, which can be different from the
+	value used in the INSERT statement.*/
+	if (error == DB_SUCCESS
+	    && table->next_number_field && new_row == table->record[0]
+	    && user_thd->lex->sql_command == SQLCOM_INSERT
+	    && user_thd->lex->duplicates == DUP_UPDATE) {
+
+		longlong	auto_inc;
+
+		auto_inc = table->next_number_field->val_int();
+
+		if (auto_inc != 0) {
+			dict_table_autoinc_update(prebuilt->table, auto_inc);
+		}
+	}
+
+	if (error == DB_SUCCESS) rows_changed++;
+
+	innodb_srv_conc_exit_innodb(prebuilt->trx);
+
+	error = convert_error_code_to_mysql(error, user_thd);
+
+	/* Tell InnoDB server that there might be work for
+	utility threads: */
+
+	innobase_active_small();
+
+	DBUG_RETURN(error);
+}
+
+/**************************************************************************
+Deletes a row given as the parameter. */
+
+int
+ha_innobase::delete_row(
+/*====================*/
+					/* out: error number or 0 */
+	const mysql_byte* record)	/* in: a row in MySQL format */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	int		error = 0;
+
+	DBUG_ENTER("ha_innobase::delete_row");
+
+	ut_ad(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	if (last_query_id != user_thd->query_id) {
+	        prebuilt->sql_stat_start = TRUE;
+                last_query_id = user_thd->query_id;
+
+		innobase_release_stat_resources(prebuilt->trx);
+	}
+
+	if (!prebuilt->upd_node) {
+		row_get_prebuilt_update_vector(prebuilt);
+	}
+
+	/* This is a delete */
+
+	prebuilt->upd_node->is_delete = TRUE;
+
+	innodb_srv_conc_enter_innodb(prebuilt->trx);
+
+	error = row_update_for_mysql((byte*) record, prebuilt);
+
+	if (error == DB_SUCCESS) rows_changed++;
+
+	innodb_srv_conc_exit_innodb(prebuilt->trx);
+
+	error = convert_error_code_to_mysql(error, user_thd);
+
+	/* Tell the InnoDB server that there might be work for
+	utility threads: */
+
+	innobase_active_small();
+
+	DBUG_RETURN(error);
+}
+
+/**************************************************************************
+Removes a new lock set on a row. This method does nothing unless the
+option innodb_locks_unsafe_for_binlog is set.*/
+
+void
+ha_innobase::unlock_row(void)
+/*=========================*/
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+
+	DBUG_ENTER("ha_innobase::unlock_row");
+
+	if (last_query_id != user_thd->query_id) {
+		ut_print_timestamp(stderr);
+		sql_print_error("last_query_id is %lu != user_thd_query_id is "
+				"%lu", (ulong) last_query_id,
+				(ulong) user_thd->query_id);
+		mem_analyze_corruption((byte *) prebuilt->trx);
+		ut_error;
+	}
+
+	/* Consistent read does not take any locks, thus there is
+	nothing to unlock. */
+
+	if (prebuilt->select_lock_type == LOCK_NONE) {
+		DBUG_VOID_RETURN;
+	}
+
+	if (srv_locks_unsafe_for_binlog) {
+		row_unlock_for_mysql(prebuilt, FALSE);
+	}
+
+	DBUG_VOID_RETURN;
+
+}
+
+/**********************************************************************
+Initializes a handle to use an index. */
+
+int
+ha_innobase::index_init(
+/*====================*/
+			/* out: 0 or error number */
+	uint 	keynr)	/* in: key (index) number */
+{
+	int 	error	= 0;
+  	DBUG_ENTER("index_init");
+
+	error = change_active_index(keynr);
+
+  	DBUG_RETURN(error);
+}
+
+/**********************************************************************
+Currently does nothing. */
+
+int
+ha_innobase::index_end(void)
+/*========================*/
+{
+	int 	error	= 0;
+  	DBUG_ENTER("index_end");
+        active_index=MAX_KEY;
+  	DBUG_RETURN(error);
+}
+
+/*************************************************************************
+Converts a search mode flag understood by MySQL to a flag understood
+by InnoDB. */
+inline
+ulint
+convert_search_mode_to_innobase(
+/*============================*/
+	enum ha_rkey_function	find_flag)
+{
+	switch (find_flag) {
+  		case HA_READ_KEY_EXACT:		return(PAGE_CUR_GE);
+  			/* the above does not require the index to be UNIQUE */
+  		case HA_READ_KEY_OR_NEXT:	return(PAGE_CUR_GE);
+		case HA_READ_KEY_OR_PREV:	return(PAGE_CUR_LE);
+		case HA_READ_AFTER_KEY:		return(PAGE_CUR_G);
+		case HA_READ_BEFORE_KEY:	return(PAGE_CUR_L);
+		case HA_READ_PREFIX:		return(PAGE_CUR_GE);
+	        case HA_READ_PREFIX_LAST:       return(PAGE_CUR_LE);
+                case HA_READ_PREFIX_LAST_OR_PREV:return(PAGE_CUR_LE);
+		  /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
+		  pass a complete-field prefix of a key value as the search
+		  tuple. I.e., it is not allowed that the last field would
+		  just contain n first bytes of the full field value.
+		  MySQL uses a 'padding' trick to convert LIKE 'abc%'
+		  type queries so that it can use as a search tuple
+		  a complete-field-prefix of a key value. Thus, the InnoDB
+		  search mode PAGE_CUR_LE_OR_EXTENDS is never used.
+		  TODO: when/if MySQL starts to use also partial-field
+		  prefixes, we have to deal with stripping of spaces
+		  and comparison of non-latin1 char type fields in
+		  innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
+		  work correctly. */
+		case HA_READ_MBR_CONTAIN:
+		case HA_READ_MBR_INTERSECT:
+		case HA_READ_MBR_WITHIN:
+		case HA_READ_MBR_DISJOINT:
+		case HA_READ_MBR_EQUAL:
+			my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
+			return(PAGE_CUR_UNSUPP);
+		/* do not use "default:" in order to produce a gcc warning:
+		enumeration value '...' not handled in switch
+		(if -Wswitch or -Wall is used)
+		*/
+	}
+
+	my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "this functionality");
+
+	return(PAGE_CUR_UNSUPP);
+}
+
+/*
+   BACKGROUND INFO: HOW A SELECT SQL QUERY IS EXECUTED
+   ---------------------------------------------------
+The following does not cover all the details, but explains how we determine
+the start of a new SQL statement, and what is associated with it.
+
+For each table in the database the MySQL interpreter may have several
+table handle instances in use, also in a single SQL query. For each table
+handle instance there is an InnoDB  'prebuilt' struct which contains most
+of the InnoDB data associated with this table handle instance.
+
+  A) if the user has not explicitly set any MySQL table level locks:
+
+  1) MySQL calls ::external_lock to set an 'intention' table level lock on
+the table of the handle instance. There we set
+prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set
+true if we are taking this table handle instance to use in a new SQL
+statement issued by the user. We also increment trx->n_mysql_tables_in_use.
+
+  2) If prebuilt->sql_stat_start == TRUE we 'pre-compile' the MySQL search
+instructions to prebuilt->template of the table handle instance in
+::index_read. The template is used to save CPU time in large joins.
+
+  3) In row_search_for_mysql, if prebuilt->sql_stat_start is true, we
+allocate a new consistent read view for the trx if it does not yet have one,
+or in the case of a locking read, set an InnoDB 'intention' table level
+lock on the table.
+
+  4) We do the SELECT. MySQL may repeatedly call ::index_read for the
+same table handle instance, if it is a join.
+
+  5) When the SELECT ends, MySQL removes its intention table level locks
+in ::external_lock. When trx->n_mysql_tables_in_use drops to zero,
+ (a) we execute a COMMIT there if the autocommit is on,
+ (b) we also release possible 'SQL statement level resources' InnoDB may
+have for this SQL statement. The MySQL interpreter does NOT execute
+autocommit for pure read transactions, though it should. That is why the
+table handler in that case has to execute the COMMIT in ::external_lock.
+
+  B) If the user has explicitly set MySQL table level locks, then MySQL
+does NOT call ::external_lock at the start of the statement. To determine
+when we are at the start of a new SQL statement we at the start of
+::index_read also compare the query id to the latest query id where the
+table handle instance was used. If it has changed, we know we are at the
+start of a new SQL statement. Since the query id can theoretically
+overwrap, we use this test only as a secondary way of determining the
+start of a new SQL statement. */
+
+
+/**************************************************************************
+Positions an index cursor to the index specified in the handle. Fetches the
+row if any. */
+
+int
+ha_innobase::index_read(
+/*====================*/
+					/* out: 0, HA_ERR_KEY_NOT_FOUND,
+					or error number */
+	mysql_byte*		buf,	/* in/out: buffer for the returned
+					row */
+	const mysql_byte* 	key_ptr,/* in: key value; if this is NULL
+					we position the cursor at the
+					start or end of index; this can
+					also contain an InnoDB row id, in
+					which case key_len is the InnoDB
+					row id length; the key value can
+					also be a prefix of a full key value,
+					and the last column can be a prefix
+					of a full column */
+	uint			key_len,/* in: key value length */
+	enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	ulint		mode;
+	dict_index_t*	index;
+	ulint		match_mode 	= 0;
+	int 		error;
+	ulint		ret;
+
+  	DBUG_ENTER("index_read");
+
+	ut_ad(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+  	statistic_increment(current_thd->status_var.ha_read_key_count,
+			    &LOCK_status);
+
+	if (last_query_id != user_thd->query_id) {
+	        prebuilt->sql_stat_start = TRUE;
+                last_query_id = user_thd->query_id;
+
+		innobase_release_stat_resources(prebuilt->trx);
+	}
+
+	index = prebuilt->index;
+
+	/* Note that if the index for which the search template is built is not
+        necessarily prebuilt->index, but can also be the clustered index */
+
+	if (prebuilt->sql_stat_start) {
+		build_template(prebuilt, user_thd, table,
+							ROW_MYSQL_REC_FIELDS);
+	}
+
+	if (key_ptr) {
+	        /* Convert the search key value to InnoDB format into
+		prebuilt->search_tuple */
+
+		row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple,
+					(byte*) key_val_buff,
+					(ulint)upd_and_key_val_buff_len,
+					index,
+					(byte*) key_ptr,
+					(ulint) key_len, prebuilt->trx);
+	} else {
+		/* We position the cursor to the last or the first entry
+		in the index */
+
+ 		dtuple_set_n_fields(prebuilt->search_tuple, 0);
+	}
+
+	mode = convert_search_mode_to_innobase(find_flag);
+
+	match_mode = 0;
+
+	if (find_flag == HA_READ_KEY_EXACT) {
+		match_mode = ROW_SEL_EXACT;
+
+	} else if (find_flag == HA_READ_PREFIX
+				|| find_flag == HA_READ_PREFIX_LAST) {
+		match_mode = ROW_SEL_EXACT_PREFIX;
+	}
+
+	last_match_mode = (uint) match_mode;
+
+	if (mode != PAGE_CUR_UNSUPP) {
+
+		innodb_srv_conc_enter_innodb(prebuilt->trx);
+
+		ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
+					   match_mode, 0);
+
+		innodb_srv_conc_exit_innodb(prebuilt->trx);
+	} else {
+
+		ret = DB_UNSUPPORTED;
+	}
+
+	if (ret == DB_SUCCESS) {
+		error = 0;
+		table->status = 0;
+                rows_read++;
+                if (active_index >= 0 && active_index < MAX_KEY)
+                        index_rows_read[active_index]++;
+
+	} else if (ret == DB_RECORD_NOT_FOUND) {
+		error = HA_ERR_KEY_NOT_FOUND;
+		table->status = STATUS_NOT_FOUND;
+
+	} else if (ret == DB_END_OF_INDEX) {
+		error = HA_ERR_KEY_NOT_FOUND;
+		table->status = STATUS_NOT_FOUND;
+	} else {
+		error = convert_error_code_to_mysql((int) ret, user_thd);
+		table->status = STATUS_NOT_FOUND;
+	}
+
+	DBUG_RETURN(error);
+}
+
+/***********************************************************************
+The following functions works like index_read, but it find the last
+row with the current key value or prefix. */
+
+int
+ha_innobase::index_read_last(
+/*=========================*/
+			           /* out: 0, HA_ERR_KEY_NOT_FOUND, or an
+				   error code */
+        mysql_byte*       buf,     /* out: fetched row */
+        const mysql_byte* key_ptr, /* in: key value, or a prefix of a full
+				   key value */
+	uint              key_len) /* in: length of the key val or prefix
+				   in bytes */
+{
+        return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
+}
+
+/************************************************************************
+Changes the active index of a handle. */
+
+int
+ha_innobase::change_active_index(
+/*=============================*/
+			/* out: 0 or error code */
+	uint 	keynr)	/* in: use this index; MAX_KEY means always clustered
+			index, even if it was internally generated by
+			InnoDB */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	KEY*		key=0;
+	statistic_increment(current_thd->status_var.ha_read_key_count,
+			    &LOCK_status);
+	DBUG_ENTER("change_active_index");
+
+	ut_ad(user_thd == current_thd);
+	ut_ad(prebuilt->trx ==
+             (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	active_index = keynr;
+
+	if (keynr != MAX_KEY && table->s->keys > 0) {
+		key = table->key_info + active_index;
+
+		prebuilt->index = dict_table_get_index_noninline(
+						     prebuilt->table,
+						     key->name);
+        } else {
+		prebuilt->index = dict_table_get_first_index_noninline(
+							   prebuilt->table);
+	}
+
+	if (!prebuilt->index) {
+	       sql_print_error("Innodb could not find key n:o %u with name %s "
+			       "from dict cache for table %s",
+			       keynr, key ? key->name : "NULL",
+			       prebuilt->table->name);
+	      DBUG_RETURN(1);
+	}
+
+	assert(prebuilt->search_tuple != 0);
+
+	dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
+
+	dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
+			prebuilt->index->n_fields);
+
+	/* MySQL changes the active index for a handle also during some
+	queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
+	and then calculates the sum. Previously we played safe and used
+	the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
+	copying. Starting from MySQL-4.1 we use a more efficient flag here. */
+
+	build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
+
+	DBUG_RETURN(0);
+}
+
+/**************************************************************************
+Positions an index cursor to the index specified in keynr. Fetches the
+row if any. */
+/* ??? This is only used to read whole keys ??? */
+
+int
+ha_innobase::index_read_idx(
+/*========================*/
+					/* out: error number or 0 */
+	mysql_byte*	buf,		/* in/out: buffer for the returned
+					row */
+	uint 		keynr,		/* in: use this index */
+	const mysql_byte* key,		/* in: key value; if this is NULL
+					we position the cursor at the
+					start or end of index */
+	uint		key_len,	/* in: key value length */
+	enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
+{
+	if (change_active_index(keynr)) {
+
+		return(1);
+	}
+
+	return(index_read(buf, key, key_len, find_flag));
+}
+
+/***************************************************************************
+Reads the next or previous row from a cursor, which must have previously been
+positioned using index_read. */
+
+int
+ha_innobase::general_fetch(
+/*=======================*/
+				/* out: 0, HA_ERR_END_OF_FILE, or error
+				number */
+	mysql_byte* 	buf,	/* in/out: buffer for next row in MySQL
+				format */
+	uint 	direction,	/* in: ROW_SEL_NEXT or ROW_SEL_PREV */
+	uint	match_mode)	/* in: 0, ROW_SEL_EXACT, or
+				ROW_SEL_EXACT_PREFIX */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	ulint		ret;
+	int		error	= 0;
+
+	DBUG_ENTER("general_fetch");
+
+	ut_ad(prebuilt->trx ==
+             (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	innodb_srv_conc_enter_innodb(prebuilt->trx);
+
+	ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
+								direction);
+	innodb_srv_conc_exit_innodb(prebuilt->trx);
+
+	if (ret == DB_SUCCESS) {
+		error = 0;
+		table->status = 0;
+                rows_read++;
+                if (active_index >= 0 && active_index < MAX_KEY)
+                        index_rows_read[active_index]++;
+
+	} else if (ret == DB_RECORD_NOT_FOUND) {
+		error = HA_ERR_END_OF_FILE;
+		table->status = STATUS_NOT_FOUND;
+
+	} else if (ret == DB_END_OF_INDEX) {
+		error = HA_ERR_END_OF_FILE;
+		table->status = STATUS_NOT_FOUND;
+	} else {
+		error = convert_error_code_to_mysql((int) ret, user_thd);
+		table->status = STATUS_NOT_FOUND;
+	}
+
+	DBUG_RETURN(error);
+}
+
+/***************************************************************************
+Reads the next row from a cursor, which must have previously been
+positioned using index_read. */
+
+int
+ha_innobase::index_next(
+/*====================*/
+				/* out: 0, HA_ERR_END_OF_FILE, or error
+				number */
+	mysql_byte* 	buf)	/* in/out: buffer for next row in MySQL
+				format */
+{
+  	statistic_increment(current_thd->status_var.ha_read_next_count,
+			    &LOCK_status);
+
+	return(general_fetch(buf, ROW_SEL_NEXT, 0));
+}
+
+/***********************************************************************
+Reads the next row matching to the key value given as the parameter. */
+
+int
+ha_innobase::index_next_same(
+/*=========================*/
+				/* out: 0, HA_ERR_END_OF_FILE, or error
+				number */
+	mysql_byte* 	buf,	/* in/out: buffer for the row */
+	const mysql_byte* key,	/* in: key value */
+	uint 		keylen)	/* in: key value length */
+{
+  	statistic_increment(current_thd->status_var.ha_read_next_count,
+			    &LOCK_status);
+
+	return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode));
+}
+
+/***************************************************************************
+Reads the previous row from a cursor, which must have previously been
+positioned using index_read. */
+
+int
+ha_innobase::index_prev(
+/*====================*/
+				/* out: 0, HA_ERR_END_OF_FILE, or error
+				number */
+	mysql_byte* 	buf)	/* in/out: buffer for previous row in MySQL
+				format */
+{
+  	statistic_increment(current_thd->status_var.ha_read_prev_count,
+			    &LOCK_status);
+
+	return(general_fetch(buf, ROW_SEL_PREV, 0));
+}
+
+/************************************************************************
+Positions a cursor on the first record in an index and reads the
+corresponding row to buf. */
+
+int
+ha_innobase::index_first(
+/*=====================*/
+				/* out: 0, HA_ERR_END_OF_FILE,
+				or error code */
+	mysql_byte*	buf)	/* in/out: buffer for the row */
+{
+	int	error;
+
+  	DBUG_ENTER("index_first");
+  	statistic_increment(current_thd->status_var.ha_read_first_count,
+			    &LOCK_status);
+
+  	error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
+
+        /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
+
+  	if (error == HA_ERR_KEY_NOT_FOUND) {
+  		error = HA_ERR_END_OF_FILE;
+  	}
+
+  	DBUG_RETURN(error);
+}
+
+/************************************************************************
+Positions a cursor on the last record in an index and reads the
+corresponding row to buf. */
+
+int
+ha_innobase::index_last(
+/*====================*/
+				/* out: 0, HA_ERR_END_OF_FILE, or error code */
+	mysql_byte*	buf)	/* in/out: buffer for the row */
+{
+	int	error;
+
+  	DBUG_ENTER("index_last");
+  	statistic_increment(current_thd->status_var.ha_read_last_count,
+			    &LOCK_status);
+
+  	error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
+
+        /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
+
+  	if (error == HA_ERR_KEY_NOT_FOUND) {
+  		error = HA_ERR_END_OF_FILE;
+  	}
+
+  	DBUG_RETURN(error);
+}
+
+/********************************************************************
+Initialize a table scan. */
+
+int
+ha_innobase::rnd_init(
+/*==================*/
+			/* out: 0 or error number */
+	bool	scan)	/* in: ???????? */
+{
+	int	err;
+
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+
+	/* Store the active index value so that we can restore the original
+	value after a scan */
+
+	if (prebuilt->clust_index_was_generated) {
+		err = change_active_index(MAX_KEY);
+	} else {
+		err = change_active_index(primary_key);
+	}
+
+  	start_of_scan = 1;
+
+ 	return(err);
+}
+
+/*********************************************************************
+Ends a table scan. */
+
+int
+ha_innobase::rnd_end(void)
+/*======================*/
+				/* out: 0 or error number */
+{
+	return(index_end());
+}
+
+/*********************************************************************
+Reads the next row in a table scan (also used to read the FIRST row
+in a table scan). */
+
+int
+ha_innobase::rnd_next(
+/*==================*/
+			/* out: 0, HA_ERR_END_OF_FILE, or error number */
+	mysql_byte* buf)/* in/out: returns the row in this buffer,
+			in MySQL format */
+{
+	int	error;
+
+  	DBUG_ENTER("rnd_next");
+  	statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
+			    &LOCK_status);
+
+  	if (start_of_scan) {
+		error = index_first(buf);
+		if (error == HA_ERR_KEY_NOT_FOUND) {
+			error = HA_ERR_END_OF_FILE;
+		}
+		start_of_scan = 0;
+	} else {
+		error = general_fetch(buf, ROW_SEL_NEXT, 0);
+	}
+
+  	DBUG_RETURN(error);
+}
+
+/**************************************************************************
+Fetches a row from the table based on a row reference. */
+
+int
+ha_innobase::rnd_pos(
+/*=================*/
+				/* out: 0, HA_ERR_KEY_NOT_FOUND,
+				or error code */
+	mysql_byte* 	buf,	/* in/out: buffer for the row */
+	mysql_byte*	pos)	/* in: primary key value of the row in the
+				MySQL format, or the row id if the clustered
+				index was internally generated by InnoDB;
+				the length of data in pos has to be
+				ref_length */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	int		error;
+	uint		keynr	= active_index;
+	DBUG_ENTER("rnd_pos");
+	DBUG_DUMP("key", (uchar *)pos, ref_length);
+
+	statistic_increment(current_thd->status_var.ha_read_rnd_count,
+			    &LOCK_status);
+
+	ut_ad(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	if (prebuilt->clust_index_was_generated) {
+		/* No primary key was defined for the table and we
+		generated the clustered index from the row id: the
+		row reference is the row id, not any key value
+		that MySQL knows of */
+
+		error = change_active_index(MAX_KEY);
+	} else {
+		error = change_active_index(primary_key);
+	}
+
+	if (error) {
+	        DBUG_PRINT("error", ("Got error: %d", error));
+		DBUG_RETURN(error);
+	}
+
+	/* Note that we assume the length of the row reference is fixed
+        for the table, and it is == ref_length */
+
+	error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
+
+	if (error) {
+		DBUG_PRINT("error", ("Got error: %d", error));
+	}
+
+	change_active_index(keynr);
+
+  	DBUG_RETURN(error);
+}
+
+/*************************************************************************
+Stores a reference to the current row to 'ref' field of the handle. Note
+that in the case where we have generated the clustered index for the
+table, the function parameter is illogical: we MUST ASSUME that 'record'
+is the current 'position' of the handle, because if row ref is actually
+the row id internally generated in InnoDB, then 'record' does not contain
+it. We just guess that the row id must be for the record where the handle
+was positioned the last time. */
+
+void
+ha_innobase::position(
+/*==================*/
+	const mysql_byte*	record)	/* in: row in MySQL format */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	uint		len;
+
+	ut_ad(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	if (prebuilt->clust_index_was_generated) {
+		/* No primary key was defined for the table and we
+		generated the clustered index from row id: the
+		row reference will be the row id, not any key value
+		that MySQL knows of */
+
+		len = DATA_ROW_ID_LEN;
+
+		memcpy(ref, prebuilt->row_id, len);
+	} else {
+		len = store_key_val_for_row(primary_key, (char*)ref,
+							 ref_length, record);
+	}
+
+	/* We assume that the 'ref' value len is always fixed for the same
+	table. */
+
+	if (len != ref_length) {
+	  sql_print_error("Stored ref len is %lu, but table ref len is %lu",
+			  (ulong) len, (ulong) ref_length);
+	}
+}
+
+/*********************************************************************
+Creates a table definition to an InnoDB database. */
+static
+int
+create_table_def(
+/*=============*/
+	trx_t*		trx,		/* in: InnoDB transaction handle */
+	TABLE*		form,		/* in: information on table
+					columns and indexes */
+	const char*	table_name,	/* in: table name */
+	const char*	path_of_temp_table,/* in: if this is a table explicitly
+					created by the user with the
+					TEMPORARY keyword, then this
+					parameter is the dir path where the
+					table should be placed if we create
+					an .ibd file for it (no .ibd extension
+					in the path, though); otherwise this
+					is NULL */
+	ibool		comp)		/* in: TRUE=compact record format */
+{
+	Field*		field;
+	dict_table_t*	table;
+	ulint		n_cols;
+  	int 		error;
+  	ulint		col_type;
+	ulint		col_len;
+  	ulint		nulls_allowed;
+	ulint		unsigned_type;
+	ulint		binary_type;
+	ulint		long_true_varchar;
+	ulint		charset_no;
+  	ulint		i;
+
+  	DBUG_ENTER("create_table_def");
+  	DBUG_PRINT("enter", ("table_name: %s", table_name));
+
+	n_cols = form->s->fields;
+
+	/* We pass 0 as the space id, and determine at a lower level the space
+	id where to store the table */
+
+	table = dict_mem_table_create(table_name, 0, n_cols, comp);
+
+	if (path_of_temp_table) {
+		table->dir_path_of_temp_table =
+			mem_heap_strdup(table->heap, path_of_temp_table);
+	}
+
+	for (i = 0; i < n_cols; i++) {
+		field = form->field[i];
+
+		col_type = get_innobase_type_from_mysql_type(&unsigned_type,
+									field);
+		if (field->null_ptr) {
+			nulls_allowed = 0;
+		} else {
+			nulls_allowed = DATA_NOT_NULL;
+		}
+
+		if (field->binary()) {
+			binary_type = DATA_BINARY_TYPE;
+		} else {
+			binary_type = 0;
+		}
+
+		charset_no = 0;
+
+		if (dtype_is_string_type(col_type)) {
+
+			charset_no = (ulint)field->charset()->number;
+
+			ut_a(charset_no < 256); /* in data0type.h we assume
+						that the number fits in one
+						byte */
+		}
+
+		ut_a(field->type() < 256); /* we assume in dtype_form_prtype()
+					   that this fits in one byte */
+		col_len = field->pack_length();
+
+		/* The MySQL pack length contains 1 or 2 bytes length field
+		for a true VARCHAR. Let us subtract that, so that the InnoDB
+		column length in the InnoDB data dictionary is the real
+		maximum byte length of the actual data. */
+
+		long_true_varchar = 0;
+
+		if (field->type() == MYSQL_TYPE_VARCHAR) {
+			col_len -= ((Field_varstring*)field)->length_bytes;
+
+			if (((Field_varstring*)field)->length_bytes == 2) {
+				long_true_varchar = DATA_LONG_TRUE_VARCHAR;
+			}
+		}
+
+		dict_mem_table_add_col(table,
+					(char*) field->field_name,
+					col_type,
+					dtype_form_prtype(
+					    (ulint)field->type()
+					     | nulls_allowed | unsigned_type
+					     | binary_type | long_true_varchar,
+					    charset_no),
+					col_len,
+					0);
+	}
+
+	error = row_create_table_for_mysql(table, trx);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	DBUG_RETURN(error);
+}
+
+/*********************************************************************
+Creates an index in an InnoDB database. */
+static
+int
+create_index(
+/*=========*/
+	trx_t*		trx,		/* in: InnoDB transaction handle */
+	TABLE*		form,		/* in: information on table
+					columns and indexes */
+	const char*	table_name,	/* in: table name */
+	uint		key_num)	/* in: index number */
+{
+	Field*		field;
+	dict_index_t*	index;
+  	int 		error;
+	ulint		n_fields;
+	KEY*		key;
+	KEY_PART_INFO*	key_part;
+	ulint		ind_type;
+	ulint		col_type;
+	ulint		prefix_len;
+	ulint		is_unsigned;
+  	ulint		i;
+  	ulint		j;
+	ulint*		field_lengths;
+
+  	DBUG_ENTER("create_index");
+
+	key = form->key_info + key_num;
+
+    	n_fields = key->key_parts;
+
+    	ind_type = 0;
+
+    	if (key_num == form->s->primary_key) {
+		ind_type = ind_type | DICT_CLUSTERED;
+	}
+
+	if (key->flags & HA_NOSAME ) {
+		ind_type = ind_type | DICT_UNIQUE;
+	}
+
+	/* We pass 0 as the space id, and determine at a lower level the space
+	id where to store the table */
+
+	index = dict_mem_index_create((char*) table_name, key->name, 0,
+						ind_type, n_fields);
+
+	field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields,
+		MYF(MY_FAE));
+
+	for (i = 0; i < n_fields; i++) {
+		key_part = key->key_part + i;
+
+		/* (The flag HA_PART_KEY_SEG denotes in MySQL a column prefix
+		field in an index: we only store a specified number of first
+		bytes of the column to the index field.) The flag does not
+		seem to be properly set by MySQL. Let us fall back on testing
+		the length of the key part versus the column. */
+
+		field = NULL;
+		for (j = 0; j < form->s->fields; j++) {
+
+			field = form->field[j];
+
+			if (0 == innobase_strcasecmp(
+					field->field_name,
+					key_part->field->field_name)) {
+				/* Found the corresponding column */
+
+				break;
+			}
+		}
+
+		ut_a(j < form->s->fields);
+
+		col_type = get_innobase_type_from_mysql_type(
+					&is_unsigned, key_part->field);
+
+		if (DATA_BLOB == col_type
+		    || (key_part->length < field->pack_length()
+			&& field->type() != MYSQL_TYPE_VARCHAR)
+		    || (field->type() == MYSQL_TYPE_VARCHAR
+			&& key_part->length < field->pack_length()
+			          - ((Field_varstring*)field)->length_bytes)) {
+
+		        prefix_len = key_part->length;
+
+			if (col_type == DATA_INT
+			    || col_type == DATA_FLOAT
+			    || col_type == DATA_DOUBLE
+			    || col_type == DATA_DECIMAL) {
+			  sql_print_error("MySQL is trying to create a column "
+					  "prefix index field, on an "
+					  "inappropriate data type. Table "
+					  "name %s, column name %s.",
+					  table_name,
+					  key_part->field->field_name);
+
+			        prefix_len = 0;
+			}
+		} else {
+		        prefix_len = 0;
+		}
+
+		field_lengths[i] = key_part->length;
+
+		/* We assume all fields should be sorted in ascending
+		order, hence the '0': */
+
+		dict_mem_index_add_field(index,
+				(char*) key_part->field->field_name,
+				0, prefix_len);
+	}
+
+	/* Even though we've defined max_supported_key_part_length, we
+	still do our own checking using field_lengths to be absolutely
+	sure we don't create too long indexes. */
+	error = row_create_index_for_mysql(index, trx, field_lengths);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	my_free((gptr) field_lengths, MYF(0));
+
+	DBUG_RETURN(error);
+}
+
+/*********************************************************************
+Creates an index to an InnoDB table when the user has defined no
+primary index. */
+static
+int
+create_clustered_index_when_no_primary(
+/*===================================*/
+	trx_t*		trx,		/* in: InnoDB transaction handle */
+	const char*	table_name)	/* in: table name */
+{
+	dict_index_t*	index;
+  	int 		error;
+
+	/* We pass 0 as the space id, and determine at a lower level the space
+	id where to store the table */
+
+	index = dict_mem_index_create((char*) table_name,
+				      (char*) "GEN_CLUST_INDEX",
+				      0, DICT_CLUSTERED, 0);
+	error = row_create_index_for_mysql(index, trx, NULL);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	return(error);
+}
+
+/*********************************************************************
+Update create_info.  Used in SHOW CREATE TABLE et al. */
+
+void
+ha_innobase::update_create_info(
+/*============================*/
+	HA_CREATE_INFO* create_info)	/* in/out: create info */
+{
+  if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
+    ha_innobase::info(HA_STATUS_AUTO);
+    create_info->auto_increment_value = auto_increment_value;
+  }
+}
+
+/*********************************************************************
+Creates a new table to an InnoDB database. */
+
+int
+ha_innobase::create(
+/*================*/
+					/* out: error number */
+	const char*	name,		/* in: table name */
+	TABLE*		form,		/* in: information on table
+					columns and indexes */
+	HA_CREATE_INFO*	create_info)	/* in: more information of the
+					created table, contains also the
+					create statement string */
+{
+	int		error;
+	dict_table_t*	innobase_table;
+	trx_t*		parent_trx;
+	trx_t*		trx;
+	int		primary_key_no;
+	uint		i;
+	char		name2[FN_REFLEN];
+	char		norm_name[FN_REFLEN];
+	THD		*thd= current_thd;
+	ib_longlong     auto_inc_value;
+
+  	DBUG_ENTER("ha_innobase::create");
+
+	DBUG_ASSERT(thd != NULL);
+
+	if (form->s->fields > 1000) {
+		/* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
+		but we play safe here */
+
+	        DBUG_RETURN(HA_ERR_TO_BIG_ROW);
+	}
+
+	/* Get the transaction associated with the current thd, or create one
+	if not yet created */
+
+	parent_trx = check_trx_exists(current_thd);
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(parent_trx);
+
+	trx = trx_allocate_for_mysql();
+
+	trx->mysql_thd = thd;
+	trx->mysql_query_str = &((*thd).query);
+
+	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
+		trx->check_foreigns = FALSE;
+	}
+
+	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
+		trx->check_unique_secondary = FALSE;
+	}
+
+	if (lower_case_table_names) {
+		srv_lower_case_table_names = TRUE;
+	} else {
+		srv_lower_case_table_names = FALSE;
+	}
+
+	fn_format(name2, name, "", "", 2);	// Remove the .frm extension
+
+	normalize_table_name(norm_name, name2);
+
+	/* Latch the InnoDB data dictionary exclusively so that no deadlocks
+	or lock waits can happen in it during a table create operation.
+	Drop table etc. do this latching in row0mysql.c. */
+
+	row_mysql_lock_data_dictionary(trx);
+
+	/* Create the table definition in InnoDB */
+
+	error = create_table_def(trx, form, norm_name,
+		create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
+		form->s->row_type != ROW_TYPE_REDUNDANT);
+
+  	if (error) {
+		goto cleanup;
+ 	}
+
+	/* Look for a primary key */
+
+	primary_key_no= (table->s->primary_key != MAX_KEY ?
+			 (int) table->s->primary_key :
+			 -1);
+
+	/* Our function row_get_mysql_key_number_for_index assumes
+	the primary key is always number 0, if it exists */
+
+	DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0);
+
+	/* Create the keys */
+
+	if (form->s->keys == 0 || primary_key_no == -1) {
+		/* Create an index which is used as the clustered index;
+		order the rows by their row id which is internally generated
+		by InnoDB */
+
+		error = create_clustered_index_when_no_primary(trx,
+							norm_name);
+  		if (error) {
+			goto cleanup;
+      		}
+	}
+
+	if (primary_key_no != -1) {
+		/* In InnoDB the clustered index must always be created
+		first */
+	    	if ((error = create_index(trx, form, norm_name,
+					  (uint) primary_key_no))) {
+			goto cleanup;
+      		}
+      	}
+
+	for (i = 0; i < form->s->keys; i++) {
+
+		if (i != (uint) primary_key_no) {
+
+    			if ((error = create_index(trx, form, norm_name, i))) {
+				goto cleanup;
+      			}
+      		}
+  	}
+
+	if (current_thd->query != NULL) {
+		LEX_STRING q;
+
+		if (thd->convert_string(&q, system_charset_info,
+					current_thd->query,
+					current_thd->query_length,
+					current_thd->charset())) {
+			error = HA_ERR_OUT_OF_MEM;
+
+			goto cleanup;
+		}
+
+		error = row_table_add_foreign_constraints(trx,
+			q.str, norm_name,
+			create_info->options & HA_LEX_CREATE_TMP_TABLE);
+
+		error = convert_error_code_to_mysql(error, NULL);
+
+		if (error) {
+			goto cleanup;
+		}
+	}
+
+  	innobase_commit_low(trx);
+
+	row_mysql_unlock_data_dictionary(trx);
+
+	/* Flush the log to reduce probability that the .frm files and
+	the InnoDB data dictionary get out-of-sync if the user runs
+	with innodb_flush_log_at_trx_commit = 0 */
+
+	log_buffer_flush_to_disk();
+
+	innobase_table = dict_table_get(norm_name, NULL);
+
+	DBUG_ASSERT(innobase_table != 0);
+
+	if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
+	   (create_info->auto_increment_value != 0)) {
+
+		/* Query was ALTER TABLE...AUTO_INCREMENT = x; or
+		CREATE TABLE ...AUTO_INCREMENT = x; Find out a table
+		definition from the dictionary and get the current value
+		of the auto increment field. Set a new value to the
+		auto increment field if the value is greater than the
+		maximum value in the column. */
+
+		auto_inc_value = create_info->auto_increment_value;
+		dict_table_autoinc_initialize(innobase_table, auto_inc_value);
+	}
+
+	/* Tell the InnoDB server that there might be work for
+	utility threads: */
+
+	srv_active_wake_master_thread();
+
+  	trx_free_for_mysql(trx);
+
+	DBUG_RETURN(0);
+
+cleanup:
+	innobase_commit_low(trx);
+
+	row_mysql_unlock_data_dictionary(trx);
+
+	trx_free_for_mysql(trx);
+
+	DBUG_RETURN(error);
+}
+
+/*********************************************************************
+Discards or imports an InnoDB tablespace. */
+
+int
+ha_innobase::discard_or_import_tablespace(
+/*======================================*/
+				/* out: 0 == success, -1 == error */
+	my_bool discard)	/* in: TRUE if discard, else import */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	dict_table_t*	dict_table;
+	trx_t*		trx;
+	int		err;
+
+ 	DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
+
+	ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
+	ut_a(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	dict_table = prebuilt->table;
+	trx = prebuilt->trx;
+
+	if (discard) {
+		err = row_discard_tablespace_for_mysql(dict_table->name, trx);
+	} else {
+		err = row_import_tablespace_for_mysql(dict_table->name, trx);
+	}
+
+	err = convert_error_code_to_mysql(err, NULL);
+
+	DBUG_RETURN(err);
+}
+
+/*********************************************************************
+Deletes all rows of an InnoDB table. */
+
+int
+ha_innobase::delete_all_rows(void)
+/*==============================*/
+				/* out: error number */
+{
+	row_prebuilt_t*	prebuilt	= (row_prebuilt_t*)innobase_prebuilt;
+	int		error;
+	trx_t*		trx;
+	THD*		thd		= current_thd;
+
+	DBUG_ENTER("ha_innobase::delete_all_rows");
+
+	if (thd->lex->sql_command != SQLCOM_TRUNCATE) {
+	fallback:
+		/* We only handle TRUNCATE TABLE t as a special case.
+		DELETE FROM t will have to use ha_innobase::delete_row(). */
+		DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
+	}
+
+	/* Get the transaction associated with the current thd, or create one
+	if not yet created */
+
+	trx = check_trx_exists(thd);
+
+	/* Truncate the table in InnoDB */
+
+	error = row_truncate_table_for_mysql(prebuilt->table, trx);
+	if (error == DB_ERROR) {
+		/* Cannot truncate; resort to ha_innobase::delete_row() */
+		goto fallback;
+	}
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	DBUG_RETURN(error);
+}
+
+/*********************************************************************
+Drops a table from an InnoDB database. Before calling this function,
+MySQL calls innobase_commit to commit the transaction of the current user.
+Then the current user cannot have locks set on the table. Drop table
+operation inside InnoDB will remove all locks any user has on the table
+inside InnoDB. */
+
+int
+ha_innobase::delete_table(
+/*======================*/
+				/* out: error number */
+	const char*	name)	/* in: table name */
+{
+	ulint	name_len;
+	int	error;
+	trx_t*	parent_trx;
+	trx_t*	trx;
+	THD     *thd= current_thd;
+	char	norm_name[1000];
+
+ 	DBUG_ENTER("ha_innobase::delete_table");
+
+	/* Get the transaction associated with the current thd, or create one
+	if not yet created */
+
+	parent_trx = check_trx_exists(current_thd);
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(parent_trx);
+
+	if (lower_case_table_names) {
+		srv_lower_case_table_names = TRUE;
+	} else {
+		srv_lower_case_table_names = FALSE;
+	}
+
+	trx = trx_allocate_for_mysql();
+
+	trx->mysql_thd = current_thd;
+	trx->mysql_query_str = &((*current_thd).query);
+
+	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
+		trx->check_foreigns = FALSE;
+	}
+
+	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
+		trx->check_unique_secondary = FALSE;
+	}
+
+	name_len = strlen(name);
+
+	assert(name_len < 1000);
+
+	/* Strangely, MySQL passes the table name without the '.frm'
+	extension, in contrast to ::create */
+
+	normalize_table_name(norm_name, name);
+
+  	/* Drop the table in InnoDB */
+
+	error = row_drop_table_for_mysql(norm_name, trx,
+		thd->lex->sql_command == SQLCOM_DROP_DB);
+
+	/* Flush the log to reduce probability that the .frm files and
+	the InnoDB data dictionary get out-of-sync if the user runs
+	with innodb_flush_log_at_trx_commit = 0 */
+
+	log_buffer_flush_to_disk();
+
+	/* Tell the InnoDB server that there might be work for
+	utility threads: */
+
+	srv_active_wake_master_thread();
+
+  	innobase_commit_low(trx);
+
+  	trx_free_for_mysql(trx);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	DBUG_RETURN(error);
+}
+
+/*********************************************************************
+Removes all tables in the named database inside InnoDB. */
+
+int
+innobase_drop_database(
+/*===================*/
+			/* out: error number */
+	char*	path)	/* in: database path; inside InnoDB the name
+			of the last directory in the path is used as
+			the database name: for example, in 'mysql/data/test'
+			the database name is 'test' */
+{
+	ulint	len		= 0;
+	trx_t*	parent_trx;
+	trx_t*	trx;
+	char*	ptr;
+	int	error;
+	char*	namebuf;
+
+	/* Get the transaction associated with the current thd, or create one
+	if not yet created */
+
+	parent_trx = check_trx_exists(current_thd);
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(parent_trx);
+
+	ptr = strend(path) - 2;
+
+	while (ptr >= path && *ptr != '\\' && *ptr != '/') {
+		ptr--;
+		len++;
+	}
+
+	ptr++;
+	namebuf = my_malloc((uint) len + 2, MYF(0));
+
+	memcpy(namebuf, ptr, len);
+	namebuf[len] = '/';
+	namebuf[len + 1] = '\0';
+#ifdef  __WIN__
+	innobase_casedn_str(namebuf);
+#endif
+	trx = trx_allocate_for_mysql();
+	trx->mysql_thd = current_thd;
+	trx->mysql_query_str = &((*current_thd).query);
+
+	if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
+		trx->check_foreigns = FALSE;
+	}
+
+  	error = row_drop_database_for_mysql(namebuf, trx);
+	my_free(namebuf, MYF(0));
+
+	/* Flush the log to reduce probability that the .frm files and
+	the InnoDB data dictionary get out-of-sync if the user runs
+	with innodb_flush_log_at_trx_commit = 0 */
+
+	log_buffer_flush_to_disk();
+
+	/* Tell the InnoDB server that there might be work for
+	utility threads: */
+
+	srv_active_wake_master_thread();
+
+  	innobase_commit_low(trx);
+  	trx_free_for_mysql(trx);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	return(error);
+}
+
+/*************************************************************************
+Renames an InnoDB table. */
+
+int
+ha_innobase::rename_table(
+/*======================*/
+				/* out: 0 or error code */
+	const char*	from,	/* in: old name of the table */
+	const char*	to)	/* in: new name of the table */
+{
+	ulint	name_len1;
+	ulint	name_len2;
+	int	error;
+	trx_t*	parent_trx;
+	trx_t*	trx;
+	char	norm_from[1000];
+	char	norm_to[1000];
+
+  	DBUG_ENTER("ha_innobase::rename_table");
+
+	/* Get the transaction associated with the current thd, or create one
+	if not yet created */
+
+	parent_trx = check_trx_exists(current_thd);
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(parent_trx);
+
+	if (lower_case_table_names) {
+		srv_lower_case_table_names = TRUE;
+	} else {
+		srv_lower_case_table_names = FALSE;
+	}
+
+	trx = trx_allocate_for_mysql();
+	trx->mysql_thd = current_thd;
+	trx->mysql_query_str = &((*current_thd).query);
+
+	if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
+		trx->check_foreigns = FALSE;
+	}
+
+	name_len1 = strlen(from);
+	name_len2 = strlen(to);
+
+	assert(name_len1 < 1000);
+	assert(name_len2 < 1000);
+
+	normalize_table_name(norm_from, from);
+	normalize_table_name(norm_to, to);
+
+  	/* Rename the table in InnoDB */
+
+  	error = row_rename_table_for_mysql(norm_from, norm_to, trx);
+
+	/* Flush the log to reduce probability that the .frm files and
+	the InnoDB data dictionary get out-of-sync if the user runs
+	with innodb_flush_log_at_trx_commit = 0 */
+
+	log_buffer_flush_to_disk();
+
+	/* Tell the InnoDB server that there might be work for
+	utility threads: */
+
+	srv_active_wake_master_thread();
+
+  	innobase_commit_low(trx);
+  	trx_free_for_mysql(trx);
+
+	error = convert_error_code_to_mysql(error, NULL);
+
+	DBUG_RETURN(error);
+}
+
+/*************************************************************************
+Estimates the number of index records in a range. */
+
+ha_rows
+ha_innobase::records_in_range(
+/*==========================*/
+						/* out: estimated number of
+						rows */
+	uint 			keynr,		/* in: index number */
+        key_range		*min_key,	/* in: start key value of the
+                                                   range, may also be 0 */
+	key_range		*max_key)	/* in: range end key val, may
+                                                   also be 0 */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	KEY*		key;
+	dict_index_t*	index;
+	mysql_byte*	key_val_buff2 	= (mysql_byte*) my_malloc(
+						  table->s->reclength
+      					+ table->s->max_key_length + 100,
+								MYF(MY_FAE));
+	ulint		buff2_len = table->s->reclength
+      					+ table->s->max_key_length + 100;
+	dtuple_t*	range_start;
+	dtuple_t*	range_end;
+	ib_longlong	n_rows;
+	ulint		mode1;
+	ulint		mode2;
+	void*           heap1;
+	void*           heap2;
+
+   	DBUG_ENTER("records_in_range");
+
+	prebuilt->trx->op_info = (char*)"estimating records in index range";
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+
+	active_index = keynr;
+
+	key = table->key_info + active_index;
+
+	index = dict_table_get_index_noninline(prebuilt->table, key->name);
+
+	range_start = dtuple_create_for_mysql(&heap1, key->key_parts);
+ 	dict_index_copy_types(range_start, index, key->key_parts);
+
+	range_end = dtuple_create_for_mysql(&heap2, key->key_parts);
+ 	dict_index_copy_types(range_end, index, key->key_parts);
+
+	row_sel_convert_mysql_key_to_innobase(
+				range_start, (byte*) key_val_buff,
+				(ulint)upd_and_key_val_buff_len,
+				index,
+				(byte*) (min_key ? min_key->key :
+                                         (const mysql_byte*) 0),
+				(ulint) (min_key ? min_key->length : 0),
+				prebuilt->trx);
+
+	row_sel_convert_mysql_key_to_innobase(
+				range_end, (byte*) key_val_buff2,
+				buff2_len, index,
+				(byte*) (max_key ? max_key->key :
+                                         (const mysql_byte*) 0),
+				(ulint) (max_key ? max_key->length : 0),
+				prebuilt->trx);
+
+	mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
+                                                HA_READ_KEY_EXACT);
+	mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
+                                                HA_READ_KEY_EXACT);
+
+	if (mode1 != PAGE_CUR_UNSUPP && mode2 != PAGE_CUR_UNSUPP) {
+
+		n_rows = btr_estimate_n_rows_in_range(index, range_start,
+						      mode1, range_end,
+						      mode2);
+	} else {
+
+		n_rows = 0;
+	}
+
+	dtuple_free_for_mysql(heap1);
+	dtuple_free_for_mysql(heap2);
+
+    	my_free((gptr) key_val_buff2, MYF(0));
+
+	prebuilt->trx->op_info = (char*)"";
+
+	/* The MySQL optimizer seems to believe an estimate of 0 rows is
+	always accurate and may return the result 'Empty set' based on that.
+	The accuracy is not guaranteed, and even if it were, for a locking
+	read we should anyway perform the search to set the next-key lock.
+	Add 1 to the value to make sure MySQL does not make the assumption! */
+
+	if (n_rows == 0) {
+	        n_rows = 1;
+	}
+
+	DBUG_RETURN((ha_rows) n_rows);
+}
+
+/*************************************************************************
+Gives an UPPER BOUND to the number of rows in a table. This is used in
+filesort.cc. */
+
+ha_rows
+ha_innobase::estimate_rows_upper_bound(void)
+/*======================================*/
+			/* out: upper bound of rows */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	dict_index_t*	index;
+	ulonglong	estimate;
+	ulonglong	local_data_file_length;
+
+ 	DBUG_ENTER("estimate_rows_upper_bound");
+
+	/* We do not know if MySQL can call this function before calling
+	external_lock(). To be safe, update the thd of the current table
+	handle. */
+
+	update_thd(current_thd);
+
+	prebuilt->trx->op_info = (char*)
+	                         "calculating upper bound for table rows";
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+
+	index = dict_table_get_first_index_noninline(prebuilt->table);
+
+	local_data_file_length = ((ulonglong) index->stat_n_leaf_pages)
+    							* UNIV_PAGE_SIZE;
+
+	/* Calculate a minimum length for a clustered index record and from
+	that an upper bound for the number of rows. Since we only calculate
+	new statistics in row0mysql.c when a table has grown by a threshold
+	factor, we must add a safety factor 2 in front of the formula below. */
+
+	estimate = 2 * local_data_file_length /
+					 dict_index_calc_min_rec_len(index);
+
+	prebuilt->trx->op_info = (char*)"";
+
+	DBUG_RETURN((ha_rows) estimate);
+}
+
+/*************************************************************************
+How many seeks it will take to read through the table. This is to be
+comparable to the number returned by records_in_range so that we can
+decide if we should scan the table or use keys. */
+
+double
+ha_innobase::scan_time()
+/*====================*/
+			/* out: estimated time measured in disk seeks */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+
+	/* Since MySQL seems to favor table scans too much over index
+	searches, we pretend that a sequential read takes the same time
+	as a random disk read, that is, we do not divide the following
+	by 10, which would be physically realistic. */
+
+	return((double) (prebuilt->table->stat_clustered_index_size));
+}
+
+/**********************************************************************
+Calculate the time it takes to read a set of ranges through an index
+This enables us to optimise reads for clustered indexes. */
+
+double
+ha_innobase::read_time(
+/*===================*/
+			/* out: estimated time measured in disk seeks */
+	uint    index,	/* in: key number */
+	uint	ranges,	/* in: how many ranges */
+	ha_rows rows)	/* in: estimated number of rows in the ranges */
+{
+	ha_rows total_rows;
+	double  time_for_scan;
+
+	if (index != table->s->primary_key) {
+		/* Not clustered */
+	  	return(handler::read_time(index, ranges, rows));
+	}
+
+	if (rows <= 2) {
+
+		return((double) rows);
+	}
+
+	/* Assume that the read time is proportional to the scan time for all
+	rows + at most one seek per range. */
+
+	time_for_scan = scan_time();
+
+	if ((total_rows = estimate_rows_upper_bound()) < rows) {
+
+	  	return(time_for_scan);
+	}
+
+	return(ranges + (double) rows / (double) total_rows * time_for_scan);
+}
+
+/*************************************************************************
+Returns statistics information of the table to the MySQL interpreter,
+in various fields of the handle object. */
+
+int
+ha_innobase::info(
+/*==============*/
+	uint flag)	/* in: what information MySQL requests */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	dict_table_t*	ib_table;
+	dict_index_t*	index;
+	ha_rows		rec_per_key;
+	ib_longlong	n_rows;
+	ulong		j;
+	ulong		i;
+	char		path[FN_REFLEN];
+	os_file_stat_t  stat_info;
+
+ 	DBUG_ENTER("info");
+
+        /* If we are forcing recovery at a high level, we will suppress
+	statistics calculation on tables, because that may crash the
+	server if an index is badly corrupted. */
+
+        if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
+
+		/* We return success (0) instead of HA_ERR_CRASHED,
+		because we want MySQL to process this query and not
+		stop, like it would do if it received the error code
+		HA_ERR_CRASHED. */
+
+		DBUG_RETURN(0);
+        }
+
+	/* We do not know if MySQL can call this function before calling
+	external_lock(). To be safe, update the thd of the current table
+	handle. */
+
+	update_thd(current_thd);
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	prebuilt->trx->op_info = (char*)"returning various info to MySQL";
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+
+ 	ib_table = prebuilt->table;
+
+ 	if (flag & HA_STATUS_TIME) {
+ 		/* In sql_show we call with this flag: update then statistics
+ 		so that they are up-to-date */
+
+	        prebuilt->trx->op_info = (char*)"updating table statistics";
+
+ 		dict_update_statistics(ib_table);
+
+		prebuilt->trx->op_info = (char*)
+		                          "returning various info to MySQL";
+		my_snprintf(path, sizeof(path), "%s/%s%s",
+				    mysql_data_home, ib_table->name,
+				    reg_ext);
+
+		unpack_filename(path,path);
+
+		/* Note that we do not know the access time of the table,
+		nor the CHECK TABLE time, nor the UPDATE or INSERT time. */
+
+		if (os_file_get_status(path,&stat_info)) {
+			create_time = stat_info.ctime;
+		}
+ 	}
+
+	if (flag & HA_STATUS_VARIABLE) {
+		n_rows = ib_table->stat_n_rows;
+
+		/* Because we do not protect stat_n_rows by any mutex in a
+		delete, it is theoretically possible that the value can be
+		smaller than zero! TODO: fix this race.
+
+		The MySQL optimizer seems to assume in a left join that n_rows
+		is an accurate estimate if it is zero. Of course, it is not,
+		since we do not have any locks on the rows yet at this phase.
+		Since SHOW TABLE STATUS seems to call this function with the
+		HA_STATUS_TIME flag set, while the left join optimizer does not
+		set that flag, we add one to a zero value if the flag is not
+		set. That way SHOW TABLE STATUS will show the best estimate,
+		while the optimizer never sees the table empty. */
+
+		if (n_rows < 0) {
+			n_rows = 0;
+		}
+
+		if (n_rows == 0 && !(flag & HA_STATUS_TIME)) {
+			n_rows++;
+		}
+
+    		records = (ha_rows)n_rows;
+    		deleted = 0;
+    		data_file_length = ((ulonglong)
+				ib_table->stat_clustered_index_size)
+    					* UNIV_PAGE_SIZE;
+    		index_file_length = ((ulonglong)
+				ib_table->stat_sum_of_other_index_sizes)
+    					* UNIV_PAGE_SIZE;
+    		delete_length = 0;
+    		check_time = 0;
+
+    		if (records == 0) {
+    			mean_rec_length = 0;
+    		} else {
+    			mean_rec_length = (ulong) (data_file_length / records);
+    		}
+    	}
+
+	if (flag & HA_STATUS_CONST) {
+		index = dict_table_get_first_index_noninline(ib_table);
+
+		if (prebuilt->clust_index_was_generated) {
+			index = dict_table_get_next_index_noninline(index);
+		}
+
+		for (i = 0; i < table->s->keys; i++) {
+			if (index == NULL) {
+				ut_print_timestamp(stderr);
+				sql_print_error("Table %s contains fewer "
+						"indexes inside InnoDB than "
+						"are defined in the MySQL "
+						".frm file. Have you mixed up "
+						".frm files from different "
+						"installations? See "
+"http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
+
+						ib_table->name);
+				break;
+			}
+
+			for (j = 0; j < table->key_info[i].key_parts; j++) {
+
+				if (j + 1 > index->n_uniq) {
+				        ut_print_timestamp(stderr);
+					sql_print_error(
+"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking "
+"statistics for %lu columns. Have you mixed up .frm files from different "
+"installations? "
+"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
+							index->name,
+							ib_table->name,
+							(unsigned long)
+							index->n_uniq, j + 1);
+				        break;
+				}
+
+				if (index->stat_n_diff_key_vals[j + 1] == 0) {
+
+					rec_per_key = records;
+				} else {
+					rec_per_key = (ha_rows)(records /
+   				         index->stat_n_diff_key_vals[j + 1]);
+				}
+
+				/* Since MySQL seems to favor table scans
+				too much over index searches, we pretend
+				index selectivity is 2 times better than
+				our estimate: */
+
+				rec_per_key = rec_per_key / 2;
+
+				if (rec_per_key == 0) {
+					rec_per_key = 1;
+				}
+
+ 				table->key_info[i].rec_per_key[j]=
+				  rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
+				  (ulong) rec_per_key;
+			}
+
+			index = dict_table_get_next_index_noninline(index);
+		}
+	}
+
+  	if (flag & HA_STATUS_ERRKEY) {
+		ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
+
+		errkey = (unsigned int) row_get_mysql_key_number_for_index(
+				       (dict_index_t*)
+				       trx_get_error_info(prebuilt->trx));
+  	}
+
+	if (flag & HA_STATUS_AUTO && table->found_next_number_field) {
+		longlong	auto_inc;
+		int		ret;
+
+		/* The following function call can the first time fail in
+		a lock wait timeout error because it reserves the auto-inc
+		lock on the table. If it fails, then someone is already initing
+		the auto-inc counter, and the second call is guaranteed to
+		succeed. */
+
+		ret = innobase_read_and_init_auto_inc(&auto_inc);
+
+		if (ret != 0) {
+			ret = innobase_read_and_init_auto_inc(&auto_inc);
+
+			if (ret != 0) {
+				ut_print_timestamp(stderr);
+				sql_print_error("Cannot get table %s auto-inc"
+						"counter value in ::info\n",
+						ib_table->name);
+				auto_inc = 0;
+			}
+		}
+
+		auto_increment_value = auto_inc;
+	}
+
+	prebuilt->trx->op_info = (char*)"";
+
+  	DBUG_RETURN(0);
+}
+
+/**************************************************************************
+Updates index cardinalities of the table, based on 8 random dives into
+each index tree. This does NOT calculate exact statistics on the table. */
+
+int
+ha_innobase::analyze(
+/*=================*/
+					/* out: returns always 0 (success) */
+	THD*		thd,		/* in: connection thread handle */
+	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
+{
+	/* Simply call ::info() with all the flags */
+	info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE);
+
+	return(0);
+}
+
+/**************************************************************************
+This is mapped to "ALTER TABLE tablename TYPE=InnoDB", which rebuilds
+the table in MySQL. */
+
+int
+ha_innobase::optimize(
+/*==================*/
+	THD*		thd,		/* in: connection thread handle */
+	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
+{
+        return(HA_ADMIN_TRY_ALTER);
+}
+
+/***********************************************************************
+Tries to check that an InnoDB table is not corrupted. If corruption is
+noticed, prints to stderr information about it. In case of corruption
+may also assert a failure and crash the server. */
+
+int
+ha_innobase::check(
+/*===============*/
+					/* out: HA_ADMIN_CORRUPT or
+					HA_ADMIN_OK */
+	THD* 		thd,		/* in: user thread handle */
+	HA_CHECK_OPT* 	check_opt)	/* in: check options, currently
+					ignored */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	ulint		ret;
+
+	ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
+	ut_a(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
+	if (prebuilt->mysql_template == NULL) {
+		/* Build the template; we will use a dummy template
+		in index scans done in checking */
+
+		build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
+	}
+
+	ret = row_check_table_for_mysql(prebuilt);
+
+	if (ret == DB_SUCCESS) {
+		return(HA_ADMIN_OK);
+	}
+
+  	return(HA_ADMIN_CORRUPT);
+}
+
+/*****************************************************************
+Adds information about free space in the InnoDB tablespace to a table comment
+which is printed out when a user calls SHOW TABLE STATUS. Adds also info on
+foreign keys. */
+
+char*
+ha_innobase::update_table_comment(
+/*==============================*/
+				/* out: table comment + InnoDB free space +
+				info on foreign keys */
+        const char*	comment)/* in: table comment defined by user */
+{
+	uint	length			= (uint) strlen(comment);
+	char*				str;
+	row_prebuilt_t*	prebuilt	= (row_prebuilt_t*)innobase_prebuilt;
+	long	flen;
+
+	/* We do not know if MySQL can call this function before calling
+	external_lock(). To be safe, update the thd of the current table
+	handle. */
+
+	if (length > 64000 - 3) {
+		return((char*)comment); /* string too long */
+	}
+
+	update_thd(current_thd);
+
+	prebuilt->trx->op_info = (char*)"returning table comment";
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+	str = NULL;
+
+	/* output the data to a temporary file */
+
+	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
+	rewind(srv_dict_tmpfile);
+
+ 	fprintf(srv_dict_tmpfile, "InnoDB free: %llu kB",
+ 		fsp_get_available_space_in_free_extents(
+ 			prebuilt->table->space));
+
+	dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
+				prebuilt->trx, prebuilt->table);
+	flen = ftell(srv_dict_tmpfile);
+	if (flen < 0) {
+		flen = 0;
+	} else if (length + flen + 3 > 64000) {
+		flen = 64000 - 3 - length;
+	}
+
+	/* allocate buffer for the full string, and
+	read the contents of the temporary file */
+
+	str = my_malloc(length + flen + 3, MYF(0));
+
+	if (str) {
+		char* pos	= str + length;
+		if (length) {
+			memcpy(str, comment, length);
+			*pos++ = ';';
+			*pos++ = ' ';
+		}
+		rewind(srv_dict_tmpfile);
+		flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
+		pos[flen] = 0;
+	}
+
+	mutex_exit_noninline(&srv_dict_tmpfile_mutex);
+
+        prebuilt->trx->op_info = (char*)"";
+
+  	return(str ? str : (char*) comment);
+}
+
+/***********************************************************************
+Gets the foreign key create info for a table stored in InnoDB. */
+
+char*
+ha_innobase::get_foreign_key_create_info(void)
+/*==========================================*/
+			/* out, own: character string in the form which
+			can be inserted to the CREATE TABLE statement,
+			MUST be freed with ::free_foreign_key_create_info */
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
+	char*	str	= 0;
+	long	flen;
+
+	ut_a(prebuilt != NULL);
+
+	/* We do not know if MySQL can call this function before calling
+	external_lock(). To be safe, update the thd of the current table
+	handle. */
+
+	update_thd(current_thd);
+
+	prebuilt->trx->op_info = (char*)"getting info on foreign keys";
+
+	/* In case MySQL calls this in the middle of a SELECT query,
+	release possible adaptive hash latch to avoid
+	deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+
+	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
+	rewind(srv_dict_tmpfile);
+
+	/* output the data to a temporary file */
+	dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
+				prebuilt->trx, prebuilt->table);
+	prebuilt->trx->op_info = (char*)"";
+
+	flen = ftell(srv_dict_tmpfile);
+	if (flen < 0) {
+		flen = 0;
+	} else if (flen > 64000 - 1) {
+		flen = 64000 - 1;
+	}
+
+	/* allocate buffer for the string, and
+	read the contents of the temporary file */
+
+	str = my_malloc(flen + 1, MYF(0));
+
+	if (str) {
+		rewind(srv_dict_tmpfile);
+		flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
+		str[flen] = 0;
+	}
+
+	mutex_exit_noninline(&srv_dict_tmpfile_mutex);
+
+  	return(str);
+}
+
+
+int
+ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
+{
+  dict_foreign_t* foreign;
+
+  DBUG_ENTER("get_foreign_key_list");
+  row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
+  ut_a(prebuilt != NULL);
+  update_thd(current_thd);
+  prebuilt->trx->op_info = (char*)"getting list of foreign keys";
+  trx_search_latch_release_if_reserved(prebuilt->trx);
+  mutex_enter_noninline(&(dict_sys->mutex));
+  foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
+
+  while (foreign != NULL)
+  {
+    uint i;
+    FOREIGN_KEY_INFO f_key_info;
+    LEX_STRING *name= 0;
+    const char *tmp_buff;
+
+    tmp_buff= foreign->id;
+    i= 0;
+    while (tmp_buff[i] != '/')
+      i++;
+    tmp_buff+= i + 1;
+    f_key_info.forein_id= make_lex_string(thd, 0, tmp_buff,
+                                          (uint) strlen(tmp_buff), 1);
+    tmp_buff= foreign->referenced_table_name;
+    i= 0;
+    while (tmp_buff[i] != '/')
+      i++;
+    f_key_info.referenced_db= make_lex_string(thd, 0,
+                                              tmp_buff, i, 1);
+    tmp_buff+= i + 1;
+    f_key_info.referenced_table= make_lex_string(thd, 0, tmp_buff,
+                                               (uint) strlen(tmp_buff), 1);
+
+    for (i= 0;;)
+    {
+      tmp_buff= foreign->foreign_col_names[i];
+      name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
+      f_key_info.foreign_fields.push_back(name);
+      tmp_buff= foreign->referenced_col_names[i];
+      name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
+      f_key_info.referenced_fields.push_back(name);
+      if (++i >= foreign->n_fields)
+        break;
+    }
+
+    ulong length= 0;
+    if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE)
+    {
+      length=17;
+      tmp_buff= "ON DELETE CASCADE";
+    }
+    else if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL)
+    {
+      length=18;
+      tmp_buff= "ON DELETE SET NULL";
+    }
+    else if (foreign->type == DICT_FOREIGN_ON_DELETE_NO_ACTION)
+    {
+      length=19;
+      tmp_buff= "ON DELETE NO ACTION";
+    }
+    else if (foreign->type == DICT_FOREIGN_ON_UPDATE_CASCADE)
+    {
+      length=17;
+      tmp_buff= "ON UPDATE CASCADE";
+    }
+    else if (foreign->type == DICT_FOREIGN_ON_UPDATE_SET_NULL)
+    {
+      length=18;
+      tmp_buff= "ON UPDATE SET NULL";
+    }
+    else if (foreign->type == DICT_FOREIGN_ON_UPDATE_NO_ACTION)
+    {
+      length=19;
+      tmp_buff= "ON UPDATE NO ACTION";
+    }
+    f_key_info.constraint_method= make_lex_string(thd,
+                                                  f_key_info.constraint_method,
+                                                  tmp_buff, length, 1);
+
+    FOREIGN_KEY_INFO *pf_key_info= ((FOREIGN_KEY_INFO *)
+                                    thd->memdup((gptr) &f_key_info,
+                                                sizeof(FOREIGN_KEY_INFO)));
+    f_key_list->push_back(pf_key_info);
+    foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
+  }
+  mutex_exit_noninline(&(dict_sys->mutex));
+  prebuilt->trx->op_info = (char*)"";
+  DBUG_RETURN(0);
+}
+
+/*********************************************************************
+Checks if ALTER TABLE may change the storage engine of the table.
+Changing storage engines is not allowed for tables for which there
+are foreign key constraints (parent or child tables). */
+
+bool
+ha_innobase::can_switch_engines(void)
+/*=================================*/
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	bool	can_switch;
+
+ 	DBUG_ENTER("ha_innobase::can_switch_engines");
+	prebuilt->trx->op_info =
+			"determining if there are foreign key constraints";
+	row_mysql_lock_data_dictionary(prebuilt->trx);
+
+	can_switch = !UT_LIST_GET_FIRST(prebuilt->table->referenced_list)
+			&& !UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
+
+	row_mysql_unlock_data_dictionary(prebuilt->trx);
+	prebuilt->trx->op_info = "";
+
+	DBUG_RETURN(can_switch);
+}
+
+/***********************************************************************
+Checks if a table is referenced by a foreign key. The MySQL manual states that
+a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
+delete is then allowed internally to resolve a duplicate key conflict in
+REPLACE, not an update. */
+
+uint
+ha_innobase::referenced_by_foreign_key(void)
+/*========================================*/
+			/* out: > 0 if referenced by a FOREIGN KEY */
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
+
+	if (dict_table_referenced_by_foreign_key(prebuilt->table)) {
+
+		return(1);
+	}
+
+	return(0);
+}
+
+/***********************************************************************
+Frees the foreign key create info for a table stored in InnoDB, if it is
+non-NULL. */
+
+void
+ha_innobase::free_foreign_key_create_info(
+/*======================================*/
+	char*	str)	/* in, own: create info string to free  */
+{
+	if (str) {
+		my_free(str, MYF(0));
+	}
+}
+
+/***********************************************************************
+Tells something additional to the handler about how to do things. */
+
+int
+ha_innobase::extra(
+/*===============*/
+			   /* out: 0 or error number */
+	enum ha_extra_function operation)
+                           /* in: HA_EXTRA_RETRIEVE_ALL_COLS or some
+			   other flag */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+
+	/* Warning: since it is not sure that MySQL calls external_lock
+	before calling this function, the trx field in prebuilt can be
+	obsolete! */
+
+	switch (operation) {
+                case HA_EXTRA_FLUSH:
+                        if (prebuilt->blob_heap) {
+                                row_mysql_prebuilt_free_blob_heap(prebuilt);
+                        }
+                        break;
+                case HA_EXTRA_RESET:
+                        if (prebuilt->blob_heap) {
+                                row_mysql_prebuilt_free_blob_heap(prebuilt);
+                        }
+                        prebuilt->keep_other_fields_on_keyread = 0;
+                        prebuilt->read_just_key = 0;
+                        break;
+  		case HA_EXTRA_RESET_STATE:
+	        	prebuilt->keep_other_fields_on_keyread = 0;
+	        	prebuilt->read_just_key = 0;
+    	        	break;
+		case HA_EXTRA_NO_KEYREAD:
+    			prebuilt->read_just_key = 0;
+    			break;
+	        case HA_EXTRA_RETRIEVE_ALL_COLS:
+			prebuilt->hint_need_to_fetch_extra_cols
+					= ROW_RETRIEVE_ALL_COLS;
+			break;
+	        case HA_EXTRA_RETRIEVE_PRIMARY_KEY:
+			if (prebuilt->hint_need_to_fetch_extra_cols == 0) {
+				prebuilt->hint_need_to_fetch_extra_cols
+					= ROW_RETRIEVE_PRIMARY_KEY;
+			}
+			break;
+	        case HA_EXTRA_KEYREAD:
+	        	prebuilt->read_just_key = 1;
+	        	break;
+		case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
+			prebuilt->keep_other_fields_on_keyread = 1;
+			break;
+		default:/* Do nothing */
+			;
+	}
+
+	return(0);
+}
+
+/**********************************************************************
+MySQL calls this function at the start of each SQL statement inside LOCK
+TABLES. Inside LOCK TABLES the ::external_lock method does not work to
+mark SQL statement borders. Note also a special case: if a temporary table
+is created inside LOCK TABLES, MySQL has not called external_lock() at all
+on that table.
+MySQL-5.0 also calls this before each statement in an execution of a stored
+procedure. To make the execution more deterministic for binlogging, MySQL-5.0
+locks all tables involved in a stored procedure with full explicit table
+locks (thd->in_lock_tables is true in ::store_lock()) before executing the
+procedure. */
+
+int
+ha_innobase::start_stmt(
+/*====================*/
+	              /* out: 0 or error code */
+	THD*    thd,  /* in: handle to the user thread */
+        thr_lock_type lock_type)
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	trx_t*		trx;
+
+	update_thd(thd);
+
+	trx = prebuilt->trx;
+
+	/* Here we release the search latch and the InnoDB thread FIFO ticket
+	if they were reserved. They should have been released already at the
+	end of the previous statement, but because inside LOCK TABLES the
+	lock count method does not work to mark the end of a SELECT statement,
+	that may not be the case. We MUST release the search latch before an
+	INSERT, for example. */
+
+	innobase_release_stat_resources(trx);
+
+	prebuilt->sql_stat_start = TRUE;
+	prebuilt->hint_need_to_fetch_extra_cols = 0;
+	prebuilt->read_just_key = 0;
+        prebuilt->keep_other_fields_on_keyread = FALSE;
+
+	if (!prebuilt->mysql_has_locked) {
+	        /* This handle is for a temporary table created inside
+	        this same LOCK TABLES; since MySQL does NOT call external_lock
+	        in this case, we must use x-row locks inside InnoDB to be
+	        prepared for an update of a row */
+
+	        prebuilt->select_lock_type = LOCK_X;
+	} else {
+		if (trx->isolation_level != TRX_ISO_SERIALIZABLE
+		    && thd->lex->sql_command == SQLCOM_SELECT
+		    && lock_type == TL_READ) {
+
+			/* For other than temporary tables, we obtain
+			no lock for consistent read (plain SELECT). */
+
+			prebuilt->select_lock_type = LOCK_NONE;
+		} else {
+			/* Not a consistent read: restore the
+			select_lock_type value. The value of
+			stored_select_lock_type was decided in:
+			1) ::store_lock(),
+			2) ::external_lock(),
+			3) ::init_table_handle_for_HANDLER(), and
+			4) :.transactional_table_lock(). */
+
+			prebuilt->select_lock_type =
+				prebuilt->stored_select_lock_type;
+		}
+	}
+
+	trx->detailed_error[0] = '\0';
+
+	/* Set the MySQL flag to mark that there is an active transaction */
+        if (trx->active_trans == 0) {
+
+                innobase_register_trx_and_stmt(thd);
+                trx->active_trans = 1;
+        } else {
+		innobase_register_stmt(thd);
+	}
+
+	return(0);
+}
+
+/**********************************************************************
+Maps a MySQL trx isolation level code to the InnoDB isolation level code */
+inline
+ulint
+innobase_map_isolation_level(
+/*=========================*/
+					/* out: InnoDB isolation level */
+	enum_tx_isolation	iso)	/* in: MySQL isolation level code */
+{
+	switch(iso) {
+		case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ);
+		case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
+		case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
+		case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
+		default: ut_a(0); return(0);
+	}
+}
+
+/**********************************************************************
+As MySQL will execute an external lock for every new table it uses when it
+starts to process an SQL statement (an exception is when MySQL calls
+start_stmt for the handle) we can use this function to store the pointer to
+the THD in the handle. We will also use this function to communicate
+to InnoDB that a new SQL statement has started and that we must store a
+savepoint to our transaction handle, so that we are able to roll back
+the SQL statement in case of an error. */
+
+int
+ha_innobase::external_lock(
+/*=======================*/
+			        /* out: 0 */
+	THD*	thd,		/* in: handle to the user thread */
+	int 	lock_type)	/* in: lock type */
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	trx_t*		trx;
+
+  	DBUG_ENTER("ha_innobase::external_lock");
+	DBUG_PRINT("enter",("lock_type: %d", lock_type));
+
+	update_thd(thd);
+
+	trx = prebuilt->trx;
+
+	prebuilt->sql_stat_start = TRUE;
+	prebuilt->hint_need_to_fetch_extra_cols = 0;
+
+	prebuilt->read_just_key = 0;
+	prebuilt->keep_other_fields_on_keyread = FALSE;
+
+	if (lock_type == F_WRLCK) {
+
+		/* If this is a SELECT, then it is in UPDATE TABLE ...
+		or SELECT ... FOR UPDATE */
+		prebuilt->select_lock_type = LOCK_X;
+		prebuilt->stored_select_lock_type = LOCK_X;
+	}
+
+	if (lock_type != F_UNLCK) {
+		/* MySQL is setting a new table lock */
+
+		trx->detailed_error[0] = '\0';
+
+		/* Set the MySQL flag to mark that there is an active
+		transaction */
+                if (trx->active_trans == 0) {
+
+                        innobase_register_trx_and_stmt(thd);
+                        trx->active_trans = 1;
+                } else if (trx->n_mysql_tables_in_use == 0) {
+			innobase_register_stmt(thd);
+		}
+
+		trx->n_mysql_tables_in_use++;
+		prebuilt->mysql_has_locked = TRUE;
+
+		if (trx->n_mysql_tables_in_use == 1) {
+		        trx->isolation_level = innobase_map_isolation_level(
+						(enum_tx_isolation)
+						thd->variables.tx_isolation);
+
+	                      if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
+				  && trx->global_read_view) {
+
+				/* At low transaction isolation levels we let
+				each consistent read set its own snapshot */
+
+				read_view_close_for_mysql(trx);
+                        }
+		}
+
+		if (trx->isolation_level == TRX_ISO_SERIALIZABLE
+		    && prebuilt->select_lock_type == LOCK_NONE
+		    && (thd->options
+				& (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
+
+			/* To get serializable execution, we let InnoDB
+			conceptually add 'LOCK IN SHARE MODE' to all SELECTs
+			which otherwise would have been consistent reads. An
+			exception is consistent reads in the AUTOCOMMIT=1 mode:
+			we know that they are read-only transactions, and they
+			can be serialized also if performed as consistent
+			reads. */
+
+			prebuilt->select_lock_type = LOCK_S;
+			prebuilt->stored_select_lock_type = LOCK_S;
+		}
+
+		/* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
+		TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
+		an InnoDB table lock if it is released immediately at the end
+		of LOCK TABLES, and InnoDB's table locks in that case cause
+		VERY easily deadlocks.
+
+		We do not set InnoDB table locks if user has not explicitly
+		requested a table lock. Note that thd->in_lock_tables
+		can  be TRUE on some cases e.g. at the start of a stored
+		procedure call (SQLCOM_CALL). */
+
+		if (prebuilt->select_lock_type != LOCK_NONE) {
+
+			if (thd->in_lock_tables &&
+			    thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
+			    thd->variables.innodb_table_locks &&
+			    (thd->options & OPTION_NOT_AUTOCOMMIT)) {
+
+				ulint	error;
+				error = row_lock_table_for_mysql(prebuilt,
+							NULL, 0);
+
+				if (error != DB_SUCCESS) {
+					error = convert_error_code_to_mysql(
+						(int) error, user_thd);
+					DBUG_RETURN((int) error);
+				}
+			}
+
+		  	trx->mysql_n_tables_locked++;
+		}
+
+		DBUG_RETURN(0);
+	}
+
+	/* MySQL is releasing a table lock */
+
+	trx->n_mysql_tables_in_use--;
+	prebuilt->mysql_has_locked = FALSE;
+
+	/* Release a possible FIFO ticket and search latch. Since we
+	may reserve the kernel mutex, we have to release the search
+	system latch first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+	/* If the MySQL lock count drops to zero we know that the current SQL
+	statement has ended */
+
+	if (trx->n_mysql_tables_in_use == 0) {
+
+	        trx->mysql_n_tables_locked = 0;
+		prebuilt->used_in_HANDLER = FALSE;
+
+		if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
+                        if (trx->active_trans != 0) {
+                                innobase_commit(thd, TRUE);
+			}
+		} else {
+			if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
+	    					&& trx->global_read_view) {
+
+				/* At low transaction isolation levels we let
+				each consistent read set its own snapshot */
+
+				read_view_close_for_mysql(trx);
+			}
+		}
+	}
+
+	DBUG_RETURN(0);
+}
+
+/**********************************************************************
+With this function MySQL request a transactional lock to a table when
+user issued query LOCK TABLES..WHERE ENGINE = InnoDB. */
+
+int
+ha_innobase::transactional_table_lock(
+/*==================================*/
+			        /* out: error code */
+	THD*	thd,		/* in: handle to the user thread */
+	int 	lock_type)	/* in: lock type */
+{
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	trx_t*		trx;
+
+  	DBUG_ENTER("ha_innobase::transactional_table_lock");
+	DBUG_PRINT("enter",("lock_type: %d", lock_type));
+
+	/* We do not know if MySQL can call this function before calling
+	external_lock(). To be safe, update the thd of the current table
+	handle. */
+
+	update_thd(thd);
+
+ 	if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
+	        ut_print_timestamp(stderr);
+	        fprintf(stderr, "  InnoDB error:\n"
+"MySQL is trying to use a table handle but the .ibd file for\n"
+"table %s does not exist.\n"
+"Have you deleted the .ibd file from the database directory under\n"
+"the MySQL datadir?"
+"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
+"how you can resolve the problem.\n",
+				prebuilt->table->name);
+		DBUG_RETURN(HA_ERR_CRASHED);
+	}
+
+	trx = prebuilt->trx;
+
+	prebuilt->sql_stat_start = TRUE;
+	prebuilt->hint_need_to_fetch_extra_cols = 0;
+
+	prebuilt->read_just_key = 0;
+	prebuilt->keep_other_fields_on_keyread = FALSE;
+
+	if (lock_type == F_WRLCK) {
+		prebuilt->select_lock_type = LOCK_X;
+		prebuilt->stored_select_lock_type = LOCK_X;
+	} else if (lock_type == F_RDLCK) {
+		prebuilt->select_lock_type = LOCK_S;
+		prebuilt->stored_select_lock_type = LOCK_S;
+	} else {
+	        ut_print_timestamp(stderr);
+	        fprintf(stderr, "  InnoDB error:\n"
+"MySQL is trying to set transactional table lock with corrupted lock type\n"
+"to table %s, lock type %d does not exist.\n",
+				prebuilt->table->name, lock_type);
+		DBUG_RETURN(HA_ERR_CRASHED);
+	}
+
+	/* MySQL is setting a new transactional table lock */
+
+	/* Set the MySQL flag to mark that there is an active transaction */
+        if (trx->active_trans == 0) {
+
+                innobase_register_trx_and_stmt(thd);
+                trx->active_trans = 1;
+        }
+
+	if (thd->in_lock_tables && thd->variables.innodb_table_locks) {
+		ulint	error = DB_SUCCESS;
+
+		error = row_lock_table_for_mysql(prebuilt, NULL, 0);
+
+		if (error != DB_SUCCESS) {
+			error = convert_error_code_to_mysql((int) error, user_thd);
+			DBUG_RETURN((int) error);
+		}
+
+		if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+
+			/* Store the current undo_no of the transaction
+			so that we know where to roll back if we have
+			to roll back the next SQL statement */
+
+			trx_mark_sql_stat_end(trx);
+		}
+	}
+
+	DBUG_RETURN(0);
+}
+
+/****************************************************************************
+Here we export InnoDB status variables to MySQL.  */
+
+void
+innodb_export_status(void)
+/*======================*/
+{
+	if (innodb_inited) {
+		srv_export_innodb_status();
+	}
+}
+
+/****************************************************************************
+Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
+Monitor to the client. */
+
+bool
+innodb_show_status(
+/*===============*/
+	THD*	thd)	/* in: the MySQL query thread of the caller */
+{
+	Protocol*		protocol = thd->protocol;
+	trx_t*			trx;
+	static const char	truncated_msg[] = "... truncated...\n";
+	const long		MAX_STATUS_SIZE = 64000;
+	ulint			trx_list_start = ULINT_UNDEFINED;
+	ulint			trx_list_end = ULINT_UNDEFINED;
+
+        DBUG_ENTER("innodb_show_status");
+
+        if (have_innodb != SHOW_OPTION_YES) {
+                my_message(ER_NOT_SUPPORTED_YET,
+          "Cannot call SHOW INNODB STATUS because skip-innodb is defined",
+                           MYF(0));
+                DBUG_RETURN(TRUE);
+        }
+
+	trx = check_trx_exists(thd);
+
+	innobase_release_stat_resources(trx);
+
+	/* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
+	bytes of text. */
+
+	long	flen, usable_len;
+	char*	str;
+
+	mutex_enter_noninline(&srv_monitor_file_mutex);
+	rewind(srv_monitor_file);
+	srv_printf_innodb_monitor(srv_monitor_file,
+				&trx_list_start, &trx_list_end);
+	flen = ftell(srv_monitor_file);
+	os_file_set_eof(srv_monitor_file);
+
+	if (flen < 0) {
+		flen = 0;
+	}
+
+	if (flen > MAX_STATUS_SIZE) {
+		usable_len = MAX_STATUS_SIZE;
+	} else {
+		usable_len = flen;
+	}
+
+	/* allocate buffer for the string, and
+	read the contents of the temporary file */
+
+	if (!(str = my_malloc(usable_len + 1, MYF(0))))
+        {
+          mutex_exit_noninline(&srv_monitor_file_mutex);
+          DBUG_RETURN(TRUE);
+        }
+
+	rewind(srv_monitor_file);
+	if (flen < MAX_STATUS_SIZE) {
+		/* Display the entire output. */
+		flen = (long) fread(str, 1, flen, srv_monitor_file);
+	} else if (trx_list_end < (ulint) flen
+			&& trx_list_start < trx_list_end
+			&& trx_list_start + (flen - trx_list_end)
+			< MAX_STATUS_SIZE - sizeof truncated_msg - 1) {
+		/* Omit the beginning of the list of active transactions. */
+		long len = (long) fread(str, 1, trx_list_start, srv_monitor_file);
+		memcpy(str + len, truncated_msg, sizeof truncated_msg - 1);
+		len += sizeof truncated_msg - 1;
+		usable_len = (MAX_STATUS_SIZE - 1) - len;
+		fseek(srv_monitor_file, flen - usable_len, SEEK_SET);
+		len += (long) fread(str + len, 1, usable_len, srv_monitor_file);
+		flen = len;
+	} else {
+		/* Omit the end of the output. */
+		flen = (long) fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
+	}
+
+	mutex_exit_noninline(&srv_monitor_file_mutex);
+
+	List<Item> field_list;
+
+	field_list.push_back(new Item_empty_string("Status", flen));
+
+	if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+                                               Protocol::SEND_EOF)) {
+		my_free(str, MYF(0));
+
+		DBUG_RETURN(TRUE);
+	}
+
+        protocol->prepare_for_resend();
+        protocol->store(str, flen, system_charset_info);
+        my_free(str, MYF(0));
+
+        if (protocol->write()) {
+
+        	DBUG_RETURN(TRUE);
+	}
+	send_eof(thd);
+
+  	DBUG_RETURN(FALSE);
+}
+
+/****************************************************************************
+Implements the SHOW MUTEX STATUS command. . */
+
+bool
+innodb_mutex_show_status(
+/*===============*/
+  THD*  thd)  /* in: the MySQL query thread of the caller */
+{
+  Protocol        *protocol= thd->protocol;
+  List<Item> field_list;
+  mutex_t*  mutex;
+#ifdef UNIV_DEBUG
+  ulint   rw_lock_count= 0;
+  ulint   rw_lock_count_spin_loop= 0;
+  ulint   rw_lock_count_spin_rounds= 0;
+  ulint   rw_lock_count_os_wait= 0;
+  ulint   rw_lock_count_os_yield= 0;
+  ulonglong rw_lock_wait_time= 0;
+#endif /* UNIV_DEBUG */
+  DBUG_ENTER("innodb_mutex_show_status");
+
+#ifdef UNIV_DEBUG
+  field_list.push_back(new Item_empty_string("Mutex", FN_REFLEN));
+  field_list.push_back(new Item_empty_string("Module", FN_REFLEN));
+  field_list.push_back(new Item_uint("Count", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("Spin_waits", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("Spin_rounds", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("OS_waits", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("OS_yields", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("OS_waits_time", MY_INT64_NUM_DECIMAL_DIGITS));
+#else /* UNIV_DEBUG */
+  field_list.push_back(new Item_empty_string("File", FN_REFLEN));
+  field_list.push_back(new Item_uint("Line", MY_INT64_NUM_DECIMAL_DIGITS));
+  field_list.push_back(new Item_uint("OS_waits", MY_INT64_NUM_DECIMAL_DIGITS));
+#endif /* UNIV_DEBUG */
+
+  if (protocol->send_fields(&field_list,
+                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+    DBUG_RETURN(TRUE);
+
+  mutex_enter_noninline(&mutex_list_mutex);
+
+  mutex = UT_LIST_GET_FIRST(mutex_list);
+
+  while ( mutex != NULL )
+  {
+#ifdef UNIV_DEBUG
+    if (mutex->mutex_type != 1)
+    {
+      if (mutex->count_using > 0)
+      {
+        protocol->prepare_for_resend();
+        protocol->store(mutex->cmutex_name, system_charset_info);
+        protocol->store(mutex->cfile_name, system_charset_info);
+        protocol->store((ulonglong)mutex->count_using);
+        protocol->store((ulonglong)mutex->count_spin_loop);
+        protocol->store((ulonglong)mutex->count_spin_rounds);
+        protocol->store((ulonglong)mutex->count_os_wait);
+        protocol->store((ulonglong)mutex->count_os_yield);
+        protocol->store((ulonglong)mutex->lspent_time/1000);
+
+        if (protocol->write())
+        {
+          mutex_exit_noninline(&mutex_list_mutex);
+          DBUG_RETURN(1);
+        }
+      }
+    }
+    else
+    {
+      rw_lock_count += mutex->count_using;
+      rw_lock_count_spin_loop += mutex->count_spin_loop;
+      rw_lock_count_spin_rounds += mutex->count_spin_rounds;
+      rw_lock_count_os_wait += mutex->count_os_wait;
+      rw_lock_count_os_yield += mutex->count_os_yield;
+      rw_lock_wait_time += mutex->lspent_time;
+    }
+#else /* UNIV_DEBUG */
+    protocol->prepare_for_resend();
+    protocol->store(mutex->cfile_name, system_charset_info);
+    protocol->store((ulonglong)mutex->cline);
+    protocol->store((ulonglong)mutex->count_os_wait);
+
+    if (protocol->write())
+    {
+      mutex_exit_noninline(&mutex_list_mutex);
+      DBUG_RETURN(1);
+    }
+#endif /* UNIV_DEBUG */
+
+    mutex = UT_LIST_GET_NEXT(list, mutex);
+  }
+
+  mutex_exit_noninline(&mutex_list_mutex);
+
+#ifdef UNIV_DEBUG
+  protocol->prepare_for_resend();
+  protocol->store("rw_lock_mutexes", system_charset_info);
+  protocol->store("", system_charset_info);
+  protocol->store((ulonglong)rw_lock_count);
+  protocol->store((ulonglong)rw_lock_count_spin_loop);
+  protocol->store((ulonglong)rw_lock_count_spin_rounds);
+  protocol->store((ulonglong)rw_lock_count_os_wait);
+  protocol->store((ulonglong)rw_lock_count_os_yield);
+  protocol->store((ulonglong)rw_lock_wait_time/1000);
+
+  if (protocol->write())
+  {
+    DBUG_RETURN(1);
+  }
+#endif /* UNIV_DEBUG */
+
+  send_eof(thd);
+  DBUG_RETURN(FALSE);
+}
+
+/****************************************************************************
+ Handling the shared INNOBASE_SHARE structure that is needed to provide table
+ locking.
+****************************************************************************/
+
+static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
+			      my_bool not_used __attribute__((unused)))
+{
+  *length=share->table_name_length;
+  return (mysql_byte*) share->table_name;
+}
+
+static INNOBASE_SHARE *get_share(const char *table_name)
+{
+	INNOBASE_SHARE *share;
+	pthread_mutex_lock(&innobase_share_mutex);
+	uint length=(uint) strlen(table_name);
+
+	if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
+				(mysql_byte*) table_name,
+				length))) {
+
+		share = (INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1,
+			MYF(MY_FAE | MY_ZEROFILL));
+
+		share->table_name_length=length;
+		share->table_name=(char*) (share+1);
+		strmov(share->table_name,table_name);
+
+		if (my_hash_insert(&innobase_open_tables,
+				(mysql_byte*) share)) {
+			pthread_mutex_unlock(&innobase_share_mutex);
+			my_free((gptr) share,0);
+
+			return 0;
+		}
+
+		thr_lock_init(&share->lock);
+		pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
+	}
+
+	share->use_count++;
+	pthread_mutex_unlock(&innobase_share_mutex);
+
+	return share;
+}
+
+static void free_share(INNOBASE_SHARE *share)
+{
+  pthread_mutex_lock(&innobase_share_mutex);
+  if (!--share->use_count)
+  {
+    hash_delete(&innobase_open_tables, (mysql_byte*) share);
+    thr_lock_delete(&share->lock);
+    pthread_mutex_destroy(&share->mutex);
+    my_free((gptr) share, MYF(0));
+  }
+  pthread_mutex_unlock(&innobase_share_mutex);
+}
+
+/*********************************************************************
+Converts a MySQL table lock stored in the 'lock' field of the handle to
+a proper type before storing pointer to the lock into an array of pointers.
+MySQL also calls this if it wants to reset some table locks to a not-locked
+state during the processing of an SQL query. An example is that during a
+SELECT the read lock is released early on the 'const' tables where we only
+fetch one row. MySQL does not call this when it releases all locks at the
+end of an SQL statement. */
+
+THR_LOCK_DATA**
+ha_innobase::store_lock(
+/*====================*/
+						/* out: pointer to the next
+						element in the 'to' array */
+	THD*			thd,		/* in: user thread handle */
+	THR_LOCK_DATA**		to,		/* in: pointer to an array
+						of pointers to lock structs;
+						pointer to the 'lock' field
+						of current handle is stored
+						next to this array */
+	enum thr_lock_type 	lock_type)	/* in: lock type to store in
+						'lock'; this may also be
+						TL_IGNORE */
+{
+	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+	trx_t*		trx;
+
+	/* Note that trx in this function is NOT necessarily prebuilt->trx
+	because we call update_thd() later, in ::external_lock()! Failure to
+	understand this caused a serious memory corruption bug in 5.1.11. */
+
+	trx = check_trx_exists(thd);
+
+	/* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
+	Be careful to ignore TL_IGNORE if we are going to do something with
+	only 'real' locks! */
+
+	if ((lock_type == TL_READ && thd->in_lock_tables) ||
+	    (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) ||
+	    lock_type == TL_READ_WITH_SHARED_LOCKS ||
+	    lock_type == TL_READ_NO_INSERT ||
+	    (thd->lex->sql_command != SQLCOM_SELECT
+	     && lock_type != TL_IGNORE)) {
+
+		/* The OR cases above are in this order:
+		1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
+		are processing a stored procedure or function, or
+		2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
+		3) this is a SELECT ... IN SHARE MODE, or
+		4) we are doing a complex SQL statement like
+		INSERT INTO ... SELECT ... and the logical logging (MySQL
+		binlog) requires the use of a locking read, or
+		MySQL is doing LOCK TABLES ... READ.
+		5) we let InnoDB do locking reads for all SQL statements that
+		are not simple SELECTs; note that select_lock_type in this
+		case may get strengthened in ::external_lock() to LOCK_X.
+		Note that we MUST use a locking read in all data modifying
+		SQL statements, because otherwise the execution would not be
+		serializable, and also the results from the update could be
+		unexpected if an obsolete consistent read view would be
+		used. */
+
+		if (srv_locks_unsafe_for_binlog &&
+		    trx->isolation_level != TRX_ISO_SERIALIZABLE &&
+		    (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) &&
+		    (thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
+		     thd->lex->sql_command == SQLCOM_UPDATE ||
+		     thd->lex->sql_command == SQLCOM_CREATE_TABLE)) {
+
+			/* In case we have innobase_locks_unsafe_for_binlog
+			option set and isolation level of the transaction
+			is not set to serializable and MySQL is doing
+			INSERT INTO...SELECT or UPDATE ... = (SELECT ...) or
+			CREATE  ... SELECT... without FOR UPDATE or
+			IN SHARE MODE in select, then we use consistent
+			read for select. */
+
+			prebuilt->select_lock_type = LOCK_NONE;
+			prebuilt->stored_select_lock_type = LOCK_NONE;
+		} else if (thd->lex->sql_command == SQLCOM_CHECKSUM) {
+			/* Use consistent read for checksum table */
+
+			prebuilt->select_lock_type = LOCK_NONE;
+			prebuilt->stored_select_lock_type = LOCK_NONE;
+		} else {
+			prebuilt->select_lock_type = LOCK_S;
+			prebuilt->stored_select_lock_type = LOCK_S;
+		}
+
+	} else if (lock_type != TL_IGNORE) {
+
+		/* We set possible LOCK_X value in external_lock, not yet
+		here even if this would be SELECT ... FOR UPDATE */
+
+		prebuilt->select_lock_type = LOCK_NONE;
+		prebuilt->stored_select_lock_type = LOCK_NONE;
+	}
+
+	if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
+
+		/* Starting from 5.0.7, we weaken also the table locks
+		set at the start of a MySQL stored procedure call, just like
+		we weaken the locks set at the start of an SQL statement.
+		MySQL does set thd->in_lock_tables TRUE there, but in reality
+		we do not need table locks to make the execution of a
+		single transaction stored procedure call deterministic
+		(if it does not use a consistent read). */
+
+		if (lock_type == TL_READ
+		    && thd->lex->sql_command == SQLCOM_LOCK_TABLES) {
+			/* We come here if MySQL is processing LOCK TABLES
+			... READ LOCAL. MyISAM under that table lock type
+			reads the table as it was at the time the lock was
+			granted (new inserts are allowed, but not seen by the
+			reader). To get a similar effect on an InnoDB table,
+			we must use LOCK TABLES ... READ. We convert the lock
+			type here, so that for InnoDB, READ LOCAL is
+			equivalent to READ. This will change the InnoDB
+			behavior in mysqldump, so that dumps of InnoDB tables
+			are consistent with dumps of MyISAM tables. */
+
+			lock_type = TL_READ_NO_INSERT;
+		}
+
+		/* If we are not doing a LOCK TABLE, DISCARD/IMPORT
+		TABLESPACE or TRUNCATE TABLE then allow multiple
+		writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
+		< TL_WRITE_CONCURRENT_INSERT.
+
+		We especially allow multiple writers if MySQL is at the
+		start of a stored procedure call (SQLCOM_CALL) or a
+		stored function call (MySQL does have thd->in_lock_tables
+		TRUE there). */
+
+    		if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
+		    && lock_type <= TL_WRITE)
+		    && !(thd->in_lock_tables
+			    && thd->lex->sql_command == SQLCOM_LOCK_TABLES)
+		    && !thd->tablespace_op
+		    && thd->lex->sql_command != SQLCOM_TRUNCATE
+		    && thd->lex->sql_command != SQLCOM_OPTIMIZE
+		    && thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
+
+			lock_type = TL_WRITE_ALLOW_WRITE;
+      		}
+
+		/* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
+		MySQL would use the lock TL_READ_NO_INSERT on t2, and that
+		would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
+		to t2. Convert the lock to a normal read lock to allow
+		concurrent inserts to t2.
+
+		We especially allow concurrent inserts if MySQL is at the
+		start of a stored procedure call (SQLCOM_CALL)
+		(MySQL does have thd->in_lock_tables TRUE there). */
+
+		if (lock_type == TL_READ_NO_INSERT
+		    && thd->lex->sql_command != SQLCOM_LOCK_TABLES) {
+
+			lock_type = TL_READ;
+		}
+
+		lock.type = lock_type;
+	}
+
+	*to++= &lock;
+
+	return(to);
+}
+
+/***********************************************************************
+This function initializes the auto-inc counter if it has not been
+initialized yet. This function does not change the value of the auto-inc
+counter if it already has been initialized. In parameter ret returns
+the value of the auto-inc counter. */
+
+int
+ha_innobase::innobase_read_and_init_auto_inc(
+/*=========================================*/
+				/* out: 0 or error code: deadlock or lock wait
+				timeout */
+	longlong*	ret)	/* out: auto-inc value */
+{
+  	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
+    	longlong        auto_inc;
+	ulint		old_select_lock_type;
+	ibool		trx_was_not_started	= FALSE;
+  	int     	error;
+
+  	ut_a(prebuilt);
+	ut_a(prebuilt->trx ==
+                (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+	ut_a(prebuilt->table);
+
+	if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
+		trx_was_not_started = TRUE;
+	}
+
+	/* In case MySQL calls this in the middle of a SELECT query, release
+	possible adaptive hash latch to avoid deadlocks of threads */
+
+	trx_search_latch_release_if_reserved(prebuilt->trx);
+
+	auto_inc = dict_table_autoinc_read(prebuilt->table);
+
+	if (auto_inc != 0) {
+		/* Already initialized */
+		*ret = auto_inc;
+
+		error = 0;
+
+		goto func_exit_early;
+	}
+
+	error = row_lock_table_autoinc_for_mysql(prebuilt);
+
+	if (error != DB_SUCCESS) {
+		error = convert_error_code_to_mysql(error, user_thd);
+
+		goto func_exit_early;
+	}
+
+	/* Check again if someone has initialized the counter meanwhile */
+	auto_inc = dict_table_autoinc_read(prebuilt->table);
+
+	if (auto_inc != 0) {
+		*ret = auto_inc;
+
+		error = 0;
+
+		goto func_exit_early;
+	}
+
+  	(void) extra(HA_EXTRA_KEYREAD);
+  	index_init(table->s->next_number_index);
+
+	/* Starting from 5.0.9, we use a consistent read to read the auto-inc
+	column maximum value. This eliminates the spurious deadlocks caused
+	by the row X-lock that we previously used. Note the following flaw
+	in our algorithm: if some other user meanwhile UPDATEs the auto-inc
+	column, our consistent read will not return the largest value. We
+	accept this flaw, since the deadlocks were a bigger trouble. */
+
+  	/* Fetch all the columns in the key */
+
+	prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
+
+	old_select_lock_type = prebuilt->select_lock_type;
+  	prebuilt->select_lock_type = LOCK_NONE;
+
+	/* Eliminate an InnoDB error print that happens when we try to SELECT
+	from a table when no table has been locked in ::external_lock(). */
+	prebuilt->trx->n_mysql_tables_in_use++;
+
+	error = index_last(table->record[1]);
+
+	prebuilt->trx->n_mysql_tables_in_use--;
+  	prebuilt->select_lock_type = old_select_lock_type;
+
+  	if (error) {
+		if (error == HA_ERR_END_OF_FILE) {
+			/* The table was empty, initialize to 1 */
+			auto_inc = 1;
+
+			error = 0;
+		} else {
+			/* This should not happen in a consistent read */
+		  sql_print_error("Consistent read of auto-inc column "
+				  "returned %lu", (ulong) error);
+  			auto_inc = -1;
+
+  			goto func_exit;
+  		}
+  	} else {
+		/* Initialize to max(col) + 1; we use
+		'found_next_number_field' below because MySQL in SHOW TABLE
+		STATUS does not seem to set 'next_number_field'. The comment
+		in table.h says that 'next_number_field' is set when it is
+		'active'. */
+
+    		auto_inc = (longlong) table->found_next_number_field->
+                        	val_int_offset(table->s->rec_buff_length) + 1;
+  	}
+
+	dict_table_autoinc_initialize(prebuilt->table, auto_inc);
+
+func_exit:
+  	(void) extra(HA_EXTRA_NO_KEYREAD);
+
+	index_end();
+
+	*ret = auto_inc;
+
+func_exit_early:
+	/* Since MySQL does not seem to call autocommit after SHOW TABLE
+	STATUS (even if we would register the trx here), we commit our
+	transaction here if it was started here. This is to eliminate a
+	dangling transaction. If the user had AUTOCOMMIT=0, then SHOW
+	TABLE STATUS does leave a dangling transaction if the user does not
+	himself call COMMIT. */
+
+	if (trx_was_not_started) {
+
+		innobase_commit_low(prebuilt->trx);
+	}
+
+ 	return(error);
+}
+
+/***********************************************************************
+This function initializes the auto-inc counter if it has not been
+initialized yet. This function does not change the value of the auto-inc
+counter if it already has been initialized. Returns the value of the
+auto-inc counter. */
+
+ulonglong
+ha_innobase::get_auto_increment()
+/*=============================*/
+                         /* out: auto-increment column value, -1 if error
+                         (deadlock or lock wait timeout) */
+{
+  	longlong        nr;
+  	int     	error;
+
+	error = innobase_read_and_init_auto_inc(&nr);
+
+	if (error) {
+		/* This should never happen in the current (5.0.6) code, since
+		we call this function only after the counter has been
+		initialized. */
+
+		ut_print_timestamp(stderr);
+		sql_print_error("Error %lu in ::get_auto_increment()",
+				(ulong) error);
+          	return(~(ulonglong) 0);
+	}
+
+	return((ulonglong) nr);
+}
+
+/* See comment in handler.h */
+int
+ha_innobase::reset_auto_increment(ulonglong value)
+{
+	DBUG_ENTER("ha_innobase::reset_auto_increment");
+
+	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+  	int     	error;
+
+	error = row_lock_table_autoinc_for_mysql(prebuilt);
+
+	if (error != DB_SUCCESS) {
+		error = convert_error_code_to_mysql(error, user_thd);
+
+		DBUG_RETURN(error);
+	}
+
+	dict_table_autoinc_initialize(prebuilt->table, value);
+
+	DBUG_RETURN(0);
+}
+
+/* See comment in handler.cc */
+bool
+ha_innobase::get_error_message(int error, String *buf)
+{
+	trx_t*	    trx = check_trx_exists(current_thd);
+
+	buf->copy(trx->detailed_error, strlen(trx->detailed_error),
+		system_charset_info);
+
+	return FALSE;
+}
+
+/***********************************************************************
+Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
+If there is no explicitly declared non-null unique key or a primary key, then
+InnoDB internally uses the row id as the primary key. */
+
+int
+ha_innobase::cmp_ref(
+/*=================*/
+				/* out: < 0 if ref1 < ref2, 0 if equal, else
+				> 0 */
+	const mysql_byte* ref1,	/* in: an (internal) primary key value in the
+				MySQL key value format */
+	const mysql_byte* ref2)	/* in: an (internal) primary key value in the
+				MySQL key value format */
+{
+	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+	enum_field_types mysql_type;
+	Field*		field;
+	KEY_PART_INFO*	key_part;
+	KEY_PART_INFO*	key_part_end;
+	uint		len1;
+	uint		len2;
+	int 		result;
+
+	if (prebuilt->clust_index_was_generated) {
+		/* The 'ref' is an InnoDB row id */
+
+		return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
+	}
+
+	/* Do a type-aware comparison of primary key fields. PK fields
+	are always NOT NULL, so no checks for NULL are performed. */
+
+	key_part = table->key_info[table->s->primary_key].key_part;
+
+	key_part_end = key_part
+			+ table->key_info[table->s->primary_key].key_parts;
+
+	for (; key_part != key_part_end; ++key_part) {
+		field = key_part->field;
+		mysql_type = field->type();
+
+		if (mysql_type == FIELD_TYPE_TINY_BLOB
+		    || mysql_type == FIELD_TYPE_MEDIUM_BLOB
+		    || mysql_type == FIELD_TYPE_BLOB
+		    || mysql_type == FIELD_TYPE_LONG_BLOB) {
+
+			/* In the MySQL key value format, a column prefix of
+			a BLOB is preceded by a 2-byte length field */
+
+			len1 = innobase_read_from_2_little_endian(ref1);
+			len2 = innobase_read_from_2_little_endian(ref2);
+
+			ref1 += 2;
+			ref2 += 2;
+			result = ((Field_blob*)field)->cmp(
+						    (const char*)ref1, len1,
+			                            (const char*)ref2, len2);
+		} else {
+			result = field->key_cmp(ref1, ref2);
+		}
+
+		if (result) {
+
+			return(result);
+		}
+
+		ref1 += key_part->store_length;
+		ref2 += key_part->store_length;
+	}
+
+	return(0);
+}
+
+char*
+ha_innobase::get_mysql_bin_log_name()
+{
+	return(trx_sys_mysql_bin_log_name);
+}
+
+ulonglong
+ha_innobase::get_mysql_bin_log_pos()
+{
+  	/* trx... is ib_longlong, which is a typedef for a 64-bit integer
+	(__int64 or longlong) so it's ok to cast it to ulonglong. */
+
+  	return(trx_sys_mysql_bin_log_pos);
+}
+
+extern "C" {
+/**********************************************************************
+This function is used to find the storage length in bytes of the first n
+characters for prefix indexes using a multibyte character set. The function
+finds charset information and returns length of prefix_len characters in the
+index field in bytes.
+
+NOTE: the prototype of this function is copied to data0type.c! If you change
+this function, you MUST change also data0type.c! */
+
+ulint
+innobase_get_at_most_n_mbchars(
+/*===========================*/
+				/* out: number of bytes occupied by the first
+				n characters */
+	ulint charset_id,	/* in: character set id */
+	ulint prefix_len,	/* in: prefix length in bytes of the index
+				(this has to be divided by mbmaxlen to get the
+				number of CHARACTERS n in the prefix) */
+	ulint data_len,         /* in: length of the string in bytes */
+	const char* str)	/* in: character string */
+{
+	ulint char_length;	/* character length in bytes */
+	ulint n_chars;		/* number of characters in prefix */
+	CHARSET_INFO* charset;	/* charset used in the field */
+
+	charset = get_charset((uint) charset_id, MYF(MY_WME));
+
+	ut_ad(charset);
+	ut_ad(charset->mbmaxlen);
+
+	/* Calculate how many characters at most the prefix index contains */
+
+	n_chars = prefix_len / charset->mbmaxlen;
+
+	/* If the charset is multi-byte, then we must find the length of the
+	first at most n chars in the string. If the string contains less
+	characters than n, then we return the length to the end of the last
+	character. */
+
+	if (charset->mbmaxlen > 1) {
+		/* my_charpos() returns the byte length of the first n_chars
+		characters, or a value bigger than the length of str, if
+		there were not enough full characters in str.
+
+		Why does the code below work:
+		Suppose that we are looking for n UTF-8 characters.
+
+		1) If the string is long enough, then the prefix contains at
+		least n complete UTF-8 characters + maybe some extra
+		characters + an incomplete UTF-8 character. No problem in
+		this case. The function returns the pointer to the
+		end of the nth character.
+
+		2) If the string is not long enough, then the string contains
+		the complete value of a column, that is, only complete UTF-8
+		characters, and we can store in the column prefix index the
+		whole string. */
+
+		char_length = my_charpos(charset, str,
+						str + data_len, (int) n_chars);
+		if (char_length > data_len) {
+			char_length = data_len;
+		}
+	} else {
+		if (data_len < prefix_len) {
+			char_length = data_len;
+		} else {
+			char_length = prefix_len;
+		}
+	}
+
+	return(char_length);
+}
+}
+
+extern "C" {
+/**********************************************************************
+This function returns true if
+
+1) SQL-query in the current thread
+is either REPLACE or LOAD DATA INFILE REPLACE.
+
+2) SQL-query in the current thread
+is INSERT ON DUPLICATE KEY UPDATE.
+
+NOTE that /mysql/innobase/row/row0ins.c must contain the
+prototype for this function ! */
+
+ibool
+innobase_query_is_update(void)
+/*==========================*/
+{
+	THD*	thd;
+
+	thd = (THD *)innobase_current_thd();
+
+	if (thd->lex->sql_command == SQLCOM_REPLACE ||
+	    thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
+	    (thd->lex->sql_command == SQLCOM_LOAD &&
+	     thd->lex->duplicates == DUP_REPLACE)) {
+
+		return(1);
+	}
+
+	if (thd->lex->sql_command == SQLCOM_INSERT &&
+	    thd->lex->duplicates  == DUP_UPDATE) {
+
+		return(1);
+	}
+
+	return(0);
+}
+}
+
+/***********************************************************************
+This function is used to prepare X/Open XA distributed transaction   */
+
+int
+innobase_xa_prepare(
+/*================*/
+			/* out: 0 or error number */
+	THD*	thd,	/* in: handle to the MySQL thread of the user
+			whose XA transaction should be prepared */
+	bool	all)	/* in: TRUE - commit transaction
+			FALSE - the current SQL statement ended */
+{
+	int error = 0;
+        trx_t* trx = check_trx_exists(thd);
+
+        if (thd->lex->sql_command != SQLCOM_XA_PREPARE) {
+
+                /* For ibbackup to work the order of transactions in binlog
+                and InnoDB must be the same. Consider the situation
+
+                  thread1> prepare; write to binlog; ...
+                          <context switch>
+                  thread2> prepare; write to binlog; commit
+                  thread1>                           ... commit
+
+                To ensure this will not happen we're taking the mutex on
+                prepare, and releasing it on commit.
+
+                Note: only do it for normal commits, done via ha_commit_trans.
+                If 2pc protocol is executed by external transaction
+                coordinator, it will be just a regular MySQL client
+                executing XA PREPARE and XA COMMIT commands.
+                In this case we cannot know how many minutes or hours
+                will be between XA PREPARE and XA COMMIT, and we don't want
+                to block for undefined period of time.
+                */
+                pthread_mutex_lock(&prepare_commit_mutex);
+                trx->active_trans = 2;
+        }
+
+	if (!thd->variables.innodb_support_xa) {
+
+		return(0);
+	}
+
+        trx->xid=thd->transaction.xid_state.xid;
+
+	/* Release a possible FIFO ticket and search latch. Since we will
+	reserve the kernel mutex, we have to release the search system latch
+	first to obey the latching order. */
+
+	innobase_release_stat_resources(trx);
+
+	if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
+
+	  sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+			  "TRX_NOT_STARTED");
+	}
+
+	if (all
+	    || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
+
+                /* We were instructed to prepare the whole transaction, or
+                this is an SQL statement end and autocommit is on */
+
+                ut_ad(trx->active_trans);
+
+		error = (int) trx_prepare_for_mysql(trx);
+	} else {
+	        /* We just mark the SQL statement ended and do not do a
+		transaction prepare */
+
+		if (trx->auto_inc_lock) {
+			/* If we had reserved the auto-inc lock for some
+			table in this SQL statement we release it now */
+
+			row_unlock_table_autoinc_for_mysql(trx);
+		}
+		/* Store the current undo_no of the transaction so that we
+		know where to roll back if we have to roll back the next
+		SQL statement */
+
+		trx_mark_sql_stat_end(trx);
+	}
+
+	/* Tell the InnoDB server that there might be work for utility
+	threads: */
+
+	srv_active_wake_master_thread();
+
+        return error;
+}
+
+/***********************************************************************
+This function is used to recover X/Open XA distributed transactions   */
+
+int
+innobase_xa_recover(
+/*================*/
+				/* out: number of prepared transactions
+				stored in xid_list */
+	XID*    xid_list, 	/* in/out: prepared transactions */
+	uint	len)		/* in: number of slots in xid_list */
+{
+	if (len == 0 || xid_list == NULL) {
+
+		return(0);
+	}
+
+	return(trx_recover_for_mysql(xid_list, len));
+}
+
+/***********************************************************************
+This function is used to commit one X/Open XA distributed transaction
+which is in the prepared state */
+
+int
+innobase_commit_by_xid(
+/*===================*/
+			/* out: 0 or error number */
+	XID*	xid)	/* in: X/Open XA transaction identification */
+{
+	trx_t*	trx;
+
+	trx = trx_get_trx_by_xid(xid);
+
+	if (trx) {
+		innobase_commit_low(trx);
+
+		return(XA_OK);
+	} else {
+		return(XAER_NOTA);
+	}
+}
+
+/***********************************************************************
+This function is used to rollback one X/Open XA distributed transaction
+which is in the prepared state */
+
+int
+innobase_rollback_by_xid(
+/*=====================*/
+			/* out: 0 or error number */
+	XID	*xid)	/* in: X/Open XA transaction identification */
+{
+	trx_t*	trx;
+
+	trx = trx_get_trx_by_xid(xid);
+
+	if (trx) {
+		return(innobase_rollback_trx(trx));
+	} else {
+		return(XAER_NOTA);
+	}
+}
+
+/***********************************************************************
+Create a consistent view for a cursor based on current transaction
+which is created if the corresponding MySQL thread still lacks one.
+This consistent view is then used inside of MySQL when accessing records
+using a cursor. */
+
+void*
+innobase_create_cursor_view(void)
+/*=============================*/
+			/* out: Pointer to cursor view or NULL */
+{
+	return(read_cursor_view_create_for_mysql(
+					check_trx_exists(current_thd)));
+}
+
+/***********************************************************************
+Close the given consistent cursor view of a transaction and restore
+global read view to a transaction read view. Transaction is created if the
+corresponding MySQL thread still lacks one. */
+
+void
+innobase_close_cursor_view(
+/*=======================*/
+	void*	curview)/* in: Consistent read view to be closed */
+{
+	read_cursor_view_close_for_mysql(check_trx_exists(current_thd),
+						(cursor_view_t*) curview);
+}
+
+/***********************************************************************
+Set the given consistent cursor view to a transaction which is created
+if the corresponding MySQL thread still lacks one. If the given
+consistent cursor view is NULL global read view of a transaction is
+restored to a transaction read view. */
+
+void
+innobase_set_cursor_view(
+/*=====================*/
+	void*	curview)/* in: Consistent cursor view to be set */
+{
+	read_cursor_set_for_mysql(check_trx_exists(current_thd),
+						(cursor_view_t*) curview);
+}
+
+#endif /* HAVE_INNOBASE_DB */
diff -Nru mysql-5.0.67.orig/sql/ha_myisam.cc mysql-5.0.67.microslow_and_userstats/sql/ha_myisam.cc
--- mysql-5.0.67.orig/sql/ha_myisam.cc	Mon Aug  4 15:20:03 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/ha_myisam.cc	Wed Sep  3 12:07:46 2008
@@ -670,7 +670,9 @@
     if ((error= update_auto_increment()))
       return error;
   }
-  return mi_write(file,buf);
+  int error=mi_write(file,buf);
+  if (!error) rows_changed++;
+  return error;
 }
 
 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
@@ -1518,13 +1520,17 @@
   statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status);
   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
     table->timestamp_field->set_time();
-  return mi_update(file,old_data,new_data);
+  int error=mi_update(file,old_data,new_data);
+  if (!error) rows_changed++;
+  return error;
 }
 
 int ha_myisam::delete_row(const byte * buf)
 {
   statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
-  return mi_delete(file,buf);
+  int error=mi_delete(file,buf);
+  if (!error) rows_changed++;
+  return error;
 }
 
 int ha_myisam::index_read(byte * buf, const byte * key,
@@ -1535,6 +1541,13 @@
 		      &LOCK_status);
   int error=mi_rkey(file,buf,active_index, key, key_len, find_flag);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1545,6 +1558,14 @@
 		      &LOCK_status);
   int error=mi_rkey(file,buf,index, key, key_len, find_flag);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+//    int inx = (active_index == -1) ? file->lastinx : active_index;
+    int inx = index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1555,6 +1576,13 @@
 		      &LOCK_status);
   int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1565,6 +1593,13 @@
 		      &LOCK_status);
   int error=mi_rnext(file,buf,active_index);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1575,6 +1610,13 @@
 		      &LOCK_status);
   int error=mi_rprev(file,buf, active_index);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1585,6 +1627,13 @@
 		      &LOCK_status);
   int error=mi_rfirst(file, buf, active_index);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1595,6 +1644,13 @@
 		      &LOCK_status);
   int error=mi_rlast(file, buf, active_index);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1611,6 +1667,13 @@
     error= mi_rnext_same(file,buf);
   } while (error == HA_ERR_RECORD_DELETED);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) {
+    rows_read++;
+
+    int inx = (active_index == -1) ? file->lastinx : active_index;
+    if (inx >= 0 && inx < MAX_KEY)
+      index_rows_read[inx]++;
+  }
   return error;
 }
 
@@ -1628,6 +1691,7 @@
 		      &LOCK_status);
   int error=mi_scan(file, buf);
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) rows_read++;
   return error;
 }
 
@@ -1642,6 +1706,7 @@
 		      &LOCK_status);
   int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
   table->status=error ? STATUS_NOT_FOUND: 0;
+  if (!error) rows_read++;
   return error;
 }
 
diff -Nru mysql-5.0.67.orig/sql/handler.cc mysql-5.0.67.microslow_and_userstats/sql/handler.cc
--- mysql-5.0.67.orig/sql/handler.cc	Mon Aug  4 15:20:04 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/handler.cc	Wed Sep  3 12:07:46 2008
@@ -20,6 +20,12 @@
 #pragma implementation				// gcc: Class implementation
 #endif
 
+/* 
+ * Ugh. Something is fishy with the SAFE_MUTEX stuff in include/my_pthread.h.
+ * This makes things compile with gcc 4.1
+ */
+#include <string>
+
 #include "mysql_priv.h"
 #include "ha_heap.h"
 #include "ha_myisam.h"
@@ -756,6 +762,7 @@
         error=1;
       }
       statistic_increment(thd->status_var.ha_commit_count,&LOCK_status);
+      thd->diff_commit_trans++;
       *ht= 0;
     }
     trans->nht=0;
@@ -812,6 +819,7 @@
         error=1;
       }
       statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
+      thd->diff_rollback_trans++;
       *ht= 0;
     }
     trans->nht=0;
@@ -1205,6 +1213,7 @@
       error=1;
     }
     statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
+    thd->diff_rollback_trans++;
     *ht=0; // keep it conveniently zero-filled
   }
   DBUG_RETURN(error);
@@ -1437,6 +1446,8 @@
     else
       dupp_ref=ref+ALIGN_SIZE(ref_length);
   }
+  rows_read = rows_changed = 0;
+  memset(index_rows_read, 0, sizeof(index_rows_read));
   DBUG_RETURN(error);
 }
 
@@ -2222,6 +2233,97 @@
   return error;
 }
 
+// Updates the global table stats with the TABLE this handler represents.
+void handler::update_global_table_stats() {
+  if (!rows_read && !rows_changed) return;  // Nothing to update.
+  // table_cache_key is db_name + '\0' + table_name + '\0'.
+  if (!table->s || !table->s->table_cache_key || !table->s->table_name) return;
+
+  TABLE_STATS* table_stats;
+  char key[NAME_LEN * 2 + 2];
+  // [db] + '.' + [table]
+  sprintf(key, "%s.%s", table->s->table_cache_key, table->s->table_name);
+
+  pthread_mutex_lock(&LOCK_global_table_stats);
+  // Gets the global table stats, creating one if necessary.
+  if (!(table_stats = (TABLE_STATS*)hash_search(&global_table_stats,
+                                                (byte*)key,
+                                                strlen(key)))) {
+    if (!(table_stats = ((TABLE_STATS*)
+                         my_malloc(sizeof(TABLE_STATS), MYF(MY_WME))))) {
+      // Out of memory.
+      sql_print_error("Allocating table stats failed.");
+      goto end;
+    }
+    strncpy(table_stats->table, key, sizeof(table_stats->table));
+    table_stats->rows_read = 0;
+    table_stats->rows_changed = 0;
+    table_stats->rows_changed_x_indexes = 0;
+
+    if (my_hash_insert(&global_table_stats, (byte*)table_stats)) {
+      // Out of memory.
+      sql_print_error("Inserting table stats failed.");
+      my_free((char*)table_stats, 0);
+      goto end;
+    }
+  }
+  // Updates the global table stats.
+  table_stats->rows_read += rows_read;
+  table_stats->rows_changed += rows_changed;
+  table_stats->rows_changed_x_indexes +=
+      rows_changed * (table->s->keys ? table->s->keys : 1);
+  rows_read = rows_changed = 0;
+end:
+  pthread_mutex_unlock(&LOCK_global_table_stats);
+}
+
+// Updates the global index stats with this handler's accumulated index reads.
+void handler::update_global_index_stats() {
+  // table_cache_key is db_name + '\0' + table_name + '\0'.
+  if (!table->s || !table->s->table_cache_key || !table->s->table_name) return;
+
+  for (int x = 0; x < table->s->keys; x++) {
+    if (index_rows_read[x]) {
+      // Rows were read using this index.
+      KEY* key_info = &table->key_info[x];
+
+      if (!key_info->name) continue;
+
+      INDEX_STATS* index_stats;
+      char key[NAME_LEN * 3 + 3];
+      // [db] + '.' + [table] + '.' + [index]
+      sprintf(key, "%s.%s.%s",  table->s->table_cache_key,
+              table->s->table_name, key_info->name);
+
+      pthread_mutex_lock(&LOCK_global_index_stats);
+      // Gets the global index stats, creating one if necessary.
+      if (!(index_stats = (INDEX_STATS*)hash_search(&global_index_stats,
+                                                    (byte*)key,
+                                                    strlen(key)))) {
+        if (!(index_stats = ((INDEX_STATS*)
+                             my_malloc(sizeof(INDEX_STATS), MYF(MY_WME))))) {
+          // Out of memory.
+          sql_print_error("Allocating index stats failed.");
+          goto end;
+        }
+        strncpy(index_stats->index, key, sizeof(index_stats->index));
+        index_stats->rows_read = 0;
+
+        if (my_hash_insert(&global_index_stats, (byte*)index_stats)) {
+          // Out of memory.
+          sql_print_error("Inserting index stats failed.");
+          my_free((char*)index_stats, 0);
+          goto end;
+        }
+      }
+      // Updates the global index stats.
+      index_stats->rows_read += index_rows_read[x];
+      index_rows_read[x] = 0;
+end:
+      pthread_mutex_unlock(&LOCK_global_index_stats);
+    }
+  }
+}
 
 /****************************************************************************
 ** Some general functions that isn't in the handler class
diff -Nru mysql-5.0.67.orig/sql/handler.h mysql-5.0.67.microslow_and_userstats/sql/handler.h
--- mysql-5.0.67.orig/sql/handler.h	Mon Aug  4 15:20:04 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/handler.h	Wed Sep  3 12:07:46 2008
@@ -32,6 +32,10 @@
 #define USING_TRANSACTIONS
 #endif
 
+#if MAX_KEY > 128
+#error MAX_KEY is too large.  Values up to 128 are supported.
+#endif
+
 // the following is for checking tables
 
 #define HA_ADMIN_ALREADY_DONE	  1
@@ -604,6 +608,9 @@
   bool  auto_increment_column_changed;
   bool implicit_emptied;                /* Can be !=0 only if HEAP */
   const COND *pushed_cond;
+  ulonglong rows_read;
+  ulonglong rows_changed;
+  ulonglong index_rows_read[MAX_KEY];
 
   handler(const handlerton *ht_arg, TABLE *table_arg) :table(table_arg),
     ht(ht_arg),
@@ -615,8 +622,10 @@
     ref_length(sizeof(my_off_t)), block_size(0),
     raid_type(0), ft_handler(0), inited(NONE),
     locked(FALSE), implicit_emptied(0),
-    pushed_cond(NULL)
-    {}
+    pushed_cond(NULL), rows_read(0), rows_changed(0)
+    {
+      memset(index_rows_read, 0, sizeof(index_rows_read));
+    }
   virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); /* TODO: DBUG_ASSERT(inited == NONE); */ }
   virtual handler *clone(MEM_ROOT *mem_root);
   int ha_open(const char *name, int mode, int test_if_locked);
@@ -625,7 +634,11 @@
   virtual void print_error(int error, myf errflag);
   virtual bool get_error_message(int error, String *buf);
   uint get_dup_key(int error);
-  void change_table_ptr(TABLE *table_arg) { table=table_arg; }
+  void change_table_ptr(TABLE *table_arg) {
+    table=table_arg;
+    rows_read = rows_changed = 0;
+    memset(index_rows_read, 0, sizeof(index_rows_read));
+  }
   virtual double scan_time()
     { return ulonglong2double(data_file_length) / IO_SIZE + 2; }
   virtual double read_time(uint index, uint ranges, ha_rows rows)
@@ -885,6 +898,9 @@
   virtual bool is_crashed() const  { return 0; }
   virtual bool auto_repair() const { return 0; }
 
+  void update_global_table_stats();
+  void update_global_index_stats();
+
   /*
     default rename_table() and delete_table() rename/delete files with a
     given name and extensions from bas_ext()
diff -Nru mysql-5.0.67.orig/sql/lex.h mysql-5.0.67.microslow_and_userstats/sql/lex.h
--- mysql-5.0.67.orig/sql/lex.h	Mon Aug  4 15:20:07 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/lex.h	Wed Sep  3 12:07:46 2008
@@ -238,6 +238,7 @@
   { "IN",		SYM(IN_SYM)},
   { "INDEX",		SYM(INDEX_SYM)},
   { "INDEXES",		SYM(INDEXES)},
+  { "INDEX_STATISTICS",	SYM(INDEX_STATS_SYM)},
   { "INFILE",		SYM(INFILE)},
   { "INNER",		SYM(INNER_SYM)},
   { "INNOBASE",		SYM(INNOBASE_SYM)},
@@ -487,6 +488,7 @@
   { "TABLE",		SYM(TABLE_SYM)},
   { "TABLES",		SYM(TABLES)},
   { "TABLESPACE",	SYM(TABLESPACE)},
+  { "TABLE_STATISTICS",	SYM(TABLE_STATS_SYM)},
   { "TEMPORARY",	SYM(TEMPORARY)},
   { "TEMPTABLE",	SYM(TEMPTABLE_SYM)},
   { "TERMINATED",	SYM(TERMINATED)},
@@ -524,6 +526,7 @@
   { "USE",		SYM(USE_SYM)},
   { "USER",		SYM(USER)},
   { "USER_RESOURCES",	SYM(RESOURCES)},
+  { "USER_STATISTICS",	SYM(USER_STATS_SYM)},
   { "USE_FRM",		SYM(USE_FRM)},
   { "USING",		SYM(USING)},
   { "UTC_DATE",         SYM(UTC_DATE_SYM)},
diff -Nru mysql-5.0.67.orig/sql/log.cc mysql-5.0.67.microslow_and_userstats/sql/log.cc
--- mysql-5.0.67.orig/sql/log.cc	Mon Aug  4 15:20:07 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/log.cc	Wed Sep  3 12:11:39 2008
@@ -2177,10 +2177,11 @@
 */
 
 bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
-		      time_t query_start_arg)
+		      time_t query_start_arg, ulonglong query_start_timer)
 {
   bool error=0;
   time_t current_time;
+  ulonglong current_timer;
   if (!is_open())
     return 0;
   DBUG_ENTER("MYSQL_LOG::write");
@@ -2191,7 +2192,8 @@
     int tmp_errno=0;
     char buff[80],*end;
     end=buff;
-    if (!(thd->options & OPTION_UPDATE_LOG))
+    if (!(thd->options & OPTION_UPDATE_LOG) &&
+        !(thd->slave_thread && opt_log_slow_slave_statements))
     {
       VOID(pthread_mutex_unlock(&LOCK_log));
       DBUG_RETURN(0);
@@ -2221,22 +2223,69 @@
       if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n",
                       sctx->priv_user ?
                       sctx->priv_user : "",
-                      sctx->user ? sctx->user : "",
+                      sctx->user ? sctx->user : (thd->slave_thread ? "SQL_SLAVE" : ""),
                       sctx->host ? sctx->host : "",
                       sctx->ip ? sctx->ip : "") ==
           (uint) -1)
         tmp_errno=errno;
     }
-    if (query_start_arg)
+    if (query_start_timer)
     {
+      char buf[5][20];
+      ulonglong current_timer= my_timer(&current_timer, frequency);
+      sprintf(buf[0], "%.6f", (current_timer - query_start_timer) / 1000000.0);
+      sprintf(buf[1], "%.6f", (thd->timer_after_lock - query_start_timer) / 1000000.0);
+      if (!query_length)
+      {
+        thd->sent_row_count= thd->examined_row_count= 0;
+        thd->innodb_was_used= FALSE;
+        thd->query_plan_flags= QPLAN_NONE;
+        thd->query_plan_fsort_passes= 0;
+      }
+
       /* For slow query log */
       if (my_b_printf(&log_file,
-                      "# Query_time: %lu  Lock_time: %lu  Rows_sent: %lu  Rows_examined: %lu\n",
-                      (ulong) (current_time - query_start_arg),
-                      (ulong) (thd->time_after_lock - query_start_arg),
+                      "# Thread_id: %lu  Schema: %s\n" \
+                      "# Query_time: %s  Lock_time: %s  Rows_sent: %lu  Rows_examined: %lu\n", 
+                      (ulong) thd->thread_id, (thd->db ? thd->db : ""),
+                      buf[0], buf[1],
                       (ulong) thd->sent_row_count,
                       (ulong) thd->examined_row_count) == (uint) -1)
         tmp_errno=errno;
+      if ((thd->variables.log_slow_verbosity & SLOG_V_QUERY_PLAN) &&
+           my_b_printf(&log_file,
+                      "# QC_Hit: %s  Full_scan: %s  Full_join: %s  Tmp_table: %s  Tmp_table_on_disk: %s\n" \
+                      "# Filesort: %s  Filesort_on_disk: %s  Merge_passes: %lu\n",
+                      ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"),
+                      ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ? "Yes" : "No"),
+                      thd->query_plan_fsort_passes) == (uint) -1)
+        tmp_errno=errno;
+      if ((thd->variables.log_slow_verbosity & SLOG_V_INNODB) && thd->innodb_was_used)
+      {
+        sprintf(buf[2], "%.6f", thd->innodb_io_reads_wait_timer / 1000000.0);
+        sprintf(buf[3], "%.6f", thd->innodb_lock_que_wait_timer / 1000000.0);
+        sprintf(buf[4], "%.6f", thd->innodb_innodb_que_wait_timer / 1000000.0);
+        if (my_b_printf(&log_file,
+                        "#   InnoDB_IO_r_ops: %lu  InnoDB_IO_r_bytes: %lu  InnoDB_IO_r_wait: %s\n" \
+                        "#   InnoDB_rec_lock_wait: %s  InnoDB_queue_wait: %s\n" \
+                        "#   InnoDB_pages_distinct: %lu\n",
+                        (ulong) thd->innodb_io_reads,
+                        (ulong) thd->innodb_io_read,
+                        buf[2], buf[3], buf[4],
+                        (ulong) thd->innodb_page_access) == (uint) -1)
+          tmp_errno=errno;
+      } 
+      else
+      {
+        if ((thd->variables.log_slow_verbosity & SLOG_V_INNODB) &&
+            my_b_printf(&log_file,"# No InnoDB statistics available for this query\n") == (uint) -1)
+          tmp_errno=errno;
+      }
     }
     if (thd->db && strcmp(thd->db,db))
     {						// Database changed
diff -Nru mysql-5.0.67.orig/sql/log_event.cc mysql-5.0.67.microslow_and_userstats/sql/log_event.cc
--- mysql-5.0.67.orig/sql/log_event.cc	Mon Aug  4 15:20:07 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/log_event.cc	Wed Sep  3 12:11:39 2008
@@ -2039,6 +2039,7 @@
       /* Execute the query (note that we bypass dispatch_command()) */
       const char* found_semicolon= NULL;
       mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
+      log_slow_statement(thd);
 
     }
     else
diff -Nru mysql-5.0.67.orig/sql/mysql_priv.h mysql-5.0.67.microslow_and_userstats/sql/mysql_priv.h
--- mysql-5.0.67.orig/sql/mysql_priv.h	Mon Aug  4 15:20:07 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/mysql_priv.h	Wed Sep  3 12:11:39 2008
@@ -489,6 +489,78 @@
 
 #define STRING_BUFFER_USUAL_SIZE 80
 
+/* Slow log */
+
+struct msl_opts
+{
+  ulong val;
+  const char *name;
+};
+
+#define SLOG_V_MICROTIME      1 << 0
+#define SLOG_V_QUERY_PLAN     1 << 1
+#define SLOG_V_INNODB         1 << 2
+/* ... */
+#define SLOG_V_INVALID        1 << 31
+#define SLOG_V_NONE           SLOG_V_MICROTIME
+
+static const struct msl_opts slog_verb[]= 
+{
+  /* Basic flags */
+
+  { SLOG_V_MICROTIME, "microtime" },
+  { SLOG_V_QUERY_PLAN, "query_plan" },
+  { SLOG_V_INNODB, "innodb" },
+
+  /* End of baisc flags */
+
+  { 0, "" },
+
+  /* Complex flags */
+
+  { SLOG_V_MICROTIME, "minimal" },
+  { SLOG_V_MICROTIME|SLOG_V_QUERY_PLAN, "standard" },
+  { SLOG_V_MICROTIME|SLOG_V_QUERY_PLAN|SLOG_V_INNODB, "full" },
+
+  /* End of complex flags */
+
+  { SLOG_V_INVALID, (char *)0 }
+};
+
+#define QPLAN_NONE            0
+#define QPLAN_QC              1 << 0
+#define QPLAN_QC_NO           1 << 1
+#define QPLAN_FULL_SCAN       1 << 2
+#define QPLAN_FULL_JOIN       1 << 3
+#define QPLAN_TMP_TABLE       1 << 4
+#define QPLAN_TMP_DISK        1 << 5
+#define QPLAN_FILESORT        1 << 6
+#define QPLAN_FILESORT_DISK   1 << 7
+/* ... */
+#define QPLAN_MAX             1 << 31
+
+#define SLOG_F_QC_NO          QPLAN_QC_NO
+#define SLOG_F_FULL_SCAN      QPLAN_FULL_SCAN
+#define SLOG_F_FULL_JOIN      QPLAN_FULL_JOIN
+#define SLOG_F_TMP_TABLE      QPLAN_TMP_TABLE
+#define SLOG_F_TMP_DISK       QPLAN_TMP_DISK
+#define SLOG_F_FILESORT       QPLAN_FILESORT
+#define SLOG_F_FILESORT_DISK  QPLAN_FILESORT_DISK
+#define SLOG_F_INVALID        1 << 31
+#define SLOG_F_NONE           0
+
+static const struct msl_opts slog_filter[]= 
+{
+  { SLOG_F_QC_NO,         "qc_miss" },
+  { SLOG_F_FULL_SCAN,     "full_scan" },
+  { SLOG_F_FULL_JOIN,     "full_join" },
+  { SLOG_F_TMP_TABLE,     "tmp_table" },
+  { SLOG_F_TMP_DISK,      "tmp_table_on_disk" },
+  { SLOG_F_FILESORT,      "filesort" },
+  { SLOG_F_FILESORT_DISK, "filesort_on_disk" },
+  { SLOG_F_INVALID,       (char *)0 }
+};
+
 enum enum_parsing_place
 {
   NO_MATTER,
@@ -744,7 +816,13 @@
 bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
 void init_max_user_conn(void);
 void init_update_queries(void);
+void init_global_user_stats(void);
+void init_global_table_stats(void);
+void init_global_index_stats(void);
 void free_max_user_conn(void);
+void free_global_user_stats(void);
+void free_global_table_stats(void);
+void free_global_index_stats(void);
 pthread_handler_t handle_one_connection(void *arg);
 pthread_handler_t handle_bootstrap(void *arg);
 void end_thread(THD *thd,bool put_in_cache);
@@ -965,6 +1043,9 @@
 void mysqld_list_processes(THD *thd,const char *user,bool verbose);
 int mysqld_show_status(THD *thd);
 int mysqld_show_variables(THD *thd,const char *wild);
+int mysqld_show_user_stats(THD *thd, const char *wild);
+int mysqld_show_table_stats(THD *thd, const char *wild);
+int mysqld_show_index_stats(THD *thd, const char *wild);
 bool mysqld_show_storage_engines(THD *thd);
 bool mysqld_show_privileges(THD *thd);
 bool mysqld_show_column_types(THD *thd);
@@ -1352,7 +1433,7 @@
 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
 extern my_bool opt_secure_auth;
 extern char* opt_secure_file_priv;
-extern my_bool opt_log_slow_admin_statements;
+extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
 extern my_bool sp_automatic_privileges, opt_noacl;
 extern my_bool opt_old_style_user_limits, trust_function_creators;
 extern uint opt_crash_binlog_innodb;
@@ -1395,6 +1476,12 @@
 extern struct system_variables max_system_variables;
 extern struct system_status_var global_status_var;
 extern struct rand_struct sql_rand;
+extern HASH global_user_stats;
+extern pthread_mutex_t LOCK_global_user_stats;
+extern HASH global_table_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern HASH global_index_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
 
 extern const char *opt_date_time_formats[];
 extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
diff -Nru mysql-5.0.67.orig/sql/mysql_priv.h.orig mysql-5.0.67.microslow_and_userstats/sql/mysql_priv.h.orig
--- mysql-5.0.67.orig/sql/mysql_priv.h.orig	Thu Jan  1 02:00:00 1970
+++ mysql-5.0.67.microslow_and_userstats/sql/mysql_priv.h.orig	Wed Sep  3 12:07:46 2008
@@ -0,0 +1,1807 @@
+/* Copyright (C) 2000-2003 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 */
+
+/*
+  Mostly this file is used in the server. But a little part of it is used in
+  mysqlbinlog too (definition of SELECT_DISTINCT and others).
+  The consequence is that 90% of the file is wrapped in #ifndef MYSQL_CLIENT,
+  except the part which must be in the server and in the client.
+*/
+
+#ifndef MYSQL_PRIV_H_INCLUDED
+#define MYSQL_PRIV_H_INCLUDED
+
+#ifndef MYSQL_CLIENT
+
+#include <my_global.h>
+#include <mysql_version.h>
+#include <mysql_embed.h>
+#include <my_sys.h>
+#include <my_time.h>
+#include <m_string.h>
+#include <hash.h>
+#include <signal.h>
+#include <thr_lock.h>
+#include <my_base.h>			/* Needed by field.h */
+#include "sql_bitmap.h"
+#include "sql_array.h"
+
+#ifdef __EMX__
+#undef write  /* remove pthread.h macro definition for EMX */
+#endif
+
+/* TODO convert all these three maps to Bitmap classes */
+typedef ulonglong table_map;          /* Used for table bits in join */
+#if MAX_INDEXES <= 64
+typedef Bitmap<64>  key_map;          /* Used for finding keys */
+#else
+typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
+#endif
+typedef ulong key_part_map;           /* Used for finding key parts */
+typedef ulong nesting_map;  /* Used for flags of nesting constructs */
+/*
+  Used to identify NESTED_JOIN structures within a join (applicable only to
+  structures that have not been simplified away and embed more the one
+  element)
+*/
+typedef ulonglong nested_join_map;
+
+/* query_id */
+typedef ulonglong query_id_t;
+extern query_id_t global_query_id;
+
+/* increment query_id and return it.  */
+inline query_id_t next_query_id() { return global_query_id++; }
+
+/* useful constants */
+extern const key_map key_map_empty;
+extern key_map key_map_full;          /* Should be threaded as const */
+extern const char *primary_key_name;
+
+#include "mysql_com.h"
+#include <violite.h>
+#include "unireg.h"
+#define IS_NUM(t)	((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL)
+
+void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
+gptr sql_alloc(unsigned size);
+gptr sql_calloc(unsigned size);
+char *sql_strdup(const char *str);
+char *sql_strmake(const char *str,uint len);
+gptr sql_memdup(const void * ptr,unsigned size);
+void sql_element_free(void *ptr);
+char *sql_strmake_with_convert(const char *str, uint32 arg_length,
+			       CHARSET_INFO *from_cs,
+			       uint32 max_res_length,
+			       CHARSET_INFO *to_cs, uint32 *result_length);
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
+bool net_request_file(NET* net, const char* fname);
+char* query_table_status(THD *thd,const char *db,const char *table_name);
+
+#define x_free(A)	{ my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); }
+#define safeFree(x)	{ if(x) { my_free((gptr) x,MYF(0)); x = NULL; } }
+#define PREV_BITS(type,A)	((type) (((type) 1 << (A)) -1))
+#define all_bits_set(A,B) ((A) & (B) != (B))
+
+#define WARN_DEPRECATED(Thd,Ver,Old,New)                                             \
+  do {                                                                               \
+    DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0);              \
+    if (((gptr)Thd) != NULL)                                                         \
+      push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN,                \
+                        ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX),    \
+                        (Old), (Ver), (New));                                        \
+    else                                                                             \
+      sql_print_warning("The syntax %s is deprecated and will be removed "           \
+                        "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
+  } while(0)
+
+extern CHARSET_INFO *system_charset_info, *files_charset_info ;
+extern CHARSET_INFO *national_charset_info, *table_alias_charset;
+
+
+enum Derivation
+{
+  DERIVATION_IGNORABLE= 5,
+  DERIVATION_COERCIBLE= 4,
+  DERIVATION_SYSCONST= 3,
+  DERIVATION_IMPLICIT= 2,
+  DERIVATION_NONE= 1,
+  DERIVATION_EXPLICIT= 0
+};
+
+
+typedef struct my_locale_st
+{
+  uint  number;
+  const char *name;
+  const char *description;
+  const bool is_ascii;
+  TYPELIB *month_names;
+  TYPELIB *ab_month_names;
+  TYPELIB *day_names;
+  TYPELIB *ab_day_names;
+#ifdef __cplusplus 
+  my_locale_st(uint number_par,
+               const char *name_par, const char *descr_par, bool is_ascii_par,
+               TYPELIB *month_names_par, TYPELIB *ab_month_names_par,
+               TYPELIB *day_names_par, TYPELIB *ab_day_names_par) : 
+    number(number_par),
+    name(name_par), description(descr_par), is_ascii(is_ascii_par),
+    month_names(month_names_par), ab_month_names(ab_month_names_par),
+    day_names(day_names_par), ab_day_names(ab_day_names_par)
+  {}
+#endif
+} MY_LOCALE;
+
+extern MY_LOCALE my_locale_en_US;
+extern MY_LOCALE *my_locales[];
+extern MY_LOCALE *my_default_lc_time_names;
+
+MY_LOCALE *my_locale_by_name(const char *name);
+MY_LOCALE *my_locale_by_number(uint number);
+
+/***************************************************************************
+  Configuration parameters
+****************************************************************************/
+
+#define ACL_CACHE_SIZE		256
+#define MAX_PASSWORD_LENGTH	32
+#define HOST_CACHE_SIZE		128
+#define MAX_ACCEPT_RETRY	10	// Test accept this many times
+#define MAX_FIELDS_BEFORE_HASH	32
+#define USER_VARS_HASH_SIZE     16
+#define TABLE_OPEN_CACHE_MIN    64
+#define TABLE_OPEN_CACHE_DEFAULT 64
+
+/* 
+ Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper
+ Drake, libc6 2.3.6-0ubuntu2, Linux kernel 2.6.15-27-686, on x86.  (Added 
+ 100 bytes as reasonable buffer against growth and other environments'
+ requirements.)
+
+ Feel free to raise this by the smallest amount you can to get the
+ "execution_constants" test to pass.
+ */
+#define STACK_MIN_SIZE          12000   // Abort if less stack during eval.
+
+#define STACK_MIN_SIZE_FOR_OPEN 1024*80
+#define STACK_BUFF_ALLOC        352     // For stack overrun checks
+#ifndef MYSQLD_NET_RETRY_COUNT
+#define MYSQLD_NET_RETRY_COUNT  10	// Abort read after this many int.
+#endif
+#define TEMP_POOL_SIZE          128
+
+#define QUERY_ALLOC_BLOCK_SIZE		8192
+#define QUERY_ALLOC_PREALLOC_SIZE   	8192
+#define TRANS_ALLOC_BLOCK_SIZE		4096
+#define TRANS_ALLOC_PREALLOC_SIZE	4096
+#define RANGE_ALLOC_BLOCK_SIZE		4096
+#define ACL_ALLOC_BLOCK_SIZE		1024
+#define UDF_ALLOC_BLOCK_SIZE		1024
+#define TABLE_ALLOC_BLOCK_SIZE		1024
+#define BDB_LOG_ALLOC_BLOCK_SIZE	1024
+#define WARN_ALLOC_BLOCK_SIZE		2048
+#define WARN_ALLOC_PREALLOC_SIZE	1024
+#define PROFILE_ALLOC_BLOCK_SIZE  2048
+#define PROFILE_ALLOC_PREALLOC_SIZE 1024
+
+/*
+  The following parameters is to decide when to use an extra cache to
+  optimise seeks when reading a big table in sorted order
+*/
+#define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (10L*1024*1024)
+#define MIN_ROWS_TO_USE_TABLE_CACHE	 100
+#define MIN_ROWS_TO_USE_BULK_INSERT	 100
+
+/*
+  The following is used to decide if MySQL should use table scanning
+  instead of reading with keys.  The number says how many evaluation of the
+  WHERE clause is comparable to reading one extra row from a table.
+*/
+#define TIME_FOR_COMPARE   5	// 5 compares == one read
+
+/*
+  Number of comparisons of table rowids equivalent to reading one row from a 
+  table.
+*/
+#define TIME_FOR_COMPARE_ROWID  (TIME_FOR_COMPARE*2)
+
+/*
+  For sequential disk seeks the cost formula is:
+    DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip  
+  
+  The cost of average seek 
+    DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
+*/
+#define DISK_SEEK_BASE_COST ((double)0.5)
+
+#define BLOCKS_IN_AVG_SEEK  128
+
+#define DISK_SEEK_PROP_COST ((double)0.5/BLOCKS_IN_AVG_SEEK)
+
+
+/*
+  Number of rows in a reference table when refereed through a not unique key.
+  This value is only used when we don't know anything about the key
+  distribution.
+*/
+#define MATCHING_ROWS_IN_OTHER_TABLE 10
+
+/* Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used) */
+#define KEY_DEFAULT_PACK_LENGTH 8
+
+/* Characters shown for the command in 'show processlist' */
+#define PROCESS_LIST_WIDTH 100
+
+#define PRECISION_FOR_DOUBLE 53
+#define PRECISION_FOR_FLOAT  24
+
+/*
+  Default time to wait before aborting a new client connection
+  that does not respond to "initial server greeting" timely
+*/
+#define CONNECT_TIMEOUT		10
+
+/* The following can also be changed from the command line */
+#define DEFAULT_CONCURRENCY	10
+#define DELAYED_LIMIT		100		/* pause after xxx inserts */
+#define DELAYED_QUEUE_SIZE	1000
+#define DELAYED_WAIT_TIMEOUT	5*60		/* Wait for delayed insert */
+#define FLUSH_TIME		0		/* Don't flush tables */
+#define MAX_CONNECT_ERRORS	10		// errors before disabling host
+
+#ifdef HAVE_INNOBASE_DB
+#define IF_INNOBASE_DB(A, B) (A)
+#else
+#define IF_INNOBASE_DB(A, B) (B)
+#endif
+#ifdef __NETWARE__
+#define IF_NETWARE(A,B) (A)
+#else
+#define IF_NETWARE(A,B) (B)
+#endif
+
+#if defined(__WIN__) || defined(OS2)
+#undef	FLUSH_TIME
+#define FLUSH_TIME	1800			/* Flush every half hour */
+
+#define INTERRUPT_PRIOR -2
+#define CONNECT_PRIOR	-1
+#define WAIT_PRIOR	0
+#define QUERY_PRIOR	2
+#else
+#define INTERRUPT_PRIOR 10
+#define CONNECT_PRIOR	9
+#define WAIT_PRIOR	8
+#define QUERY_PRIOR	6
+#endif /* __WIN92__ */
+
+	/* Bits from testflag */
+#define TEST_PRINT_CACHED_TABLES 1
+#define TEST_NO_KEY_GROUP	 2
+#define TEST_MIT_THREAD		4
+#define TEST_BLOCKING		8
+#define TEST_KEEP_TMP_TABLES	16
+#define TEST_NO_THREADS		32	/* For debugging under Linux */
+#define TEST_READCHECK		64	/* Force use of readcheck */
+#define TEST_NO_EXTRA		128
+#define TEST_CORE_ON_SIGNAL	256	/* Give core if signal */
+#define TEST_NO_STACKTRACE	512
+#define TEST_SIGINT		1024	/* Allow sigint on threads */
+#define TEST_SYNCHRONIZATION    2048    /* get server to do sleep in
+                                           some places */
+#endif
+
+/*
+   This is included in the server and in the client.
+   Options for select set by the yacc parser (stored in lex->options).
+
+   XXX:
+   log_event.h defines OPTIONS_WRITTEN_TO_BIN_LOG to specify what THD
+   options list are written into binlog. These options can NOT change their
+   values, or it will break replication between version.
+
+   context is encoded as following:
+   SELECT - SELECT_LEX_NODE::options
+   THD    - THD::options
+   intern - neither. used only as
+            func(..., select_node->options | thd->options | OPTION_XXX, ...)
+
+   TODO: separate three contexts above, move them to separate bitfields.
+*/
+
+#define SELECT_DISTINCT         (ULL(1) << 0)     // SELECT, user
+#define SELECT_STRAIGHT_JOIN    (ULL(1) << 1)     // SELECT, user
+#define SELECT_DESCRIBE         (ULL(1) << 2)     // SELECT, user
+#define SELECT_SMALL_RESULT     (ULL(1) << 3)     // SELECT, user
+#define SELECT_BIG_RESULT       (ULL(1) << 4)     // SELECT, user
+#define OPTION_FOUND_ROWS       (ULL(1) << 5)     // SELECT, user
+#define OPTION_TO_QUERY_CACHE   (ULL(1) << 6)     // SELECT, user
+#define SELECT_NO_JOIN_CACHE    (ULL(1) << 7)     // intern
+#define OPTION_BIG_TABLES       (ULL(1) << 8)     // THD, user
+#define OPTION_BIG_SELECTS      (ULL(1) << 9)     // THD, user
+#define OPTION_LOG_OFF          (ULL(1) << 10)    // THD, user
+#define OPTION_UPDATE_LOG       (ULL(1) << 11)    // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS   (ULL(1) << 12)    // SELECT, intern
+#define OPTION_WARNINGS         (ULL(1) << 13)    // THD, user
+#define OPTION_AUTO_IS_NULL     (ULL(1) << 14)    // THD, user, binlog
+#define OPTION_FOUND_COMMENT    (ULL(1) << 15)    // SELECT, intern, parser
+#define OPTION_SAFE_UPDATES     (ULL(1) << 16)    // THD, user
+#define OPTION_BUFFER_RESULT    (ULL(1) << 17)    // SELECT, user
+#define OPTION_BIN_LOG          (ULL(1) << 18)    // THD, user
+#define OPTION_NOT_AUTOCOMMIT   (ULL(1) << 19)    // THD, user
+#define OPTION_BEGIN            (ULL(1) << 20)    // THD, intern
+#define OPTION_TABLE_LOCK       (ULL(1) << 21)    // THD, intern
+#define OPTION_QUICK            (ULL(1) << 22)    // SELECT (for DELETE)
+#define OPTION_QUOTE_SHOW_CREATE (ULL(1) << 23)   // THD, user
+
+/* Thr following is used to detect a conflict with DISTINCT
+   in the user query has requested */
+#define SELECT_ALL              (ULL(1) << 24)    // SELECT, user, parser
+
+/* The following can be set when importing tables in a 'wrong order'
+   to suppress foreign key checks */
+#define OPTION_NO_FOREIGN_KEY_CHECKS    (ULL(1) << 26) // THD, user, binlog
+/* The following speeds up inserts to InnoDB tables by suppressing unique
+   key checks in some cases */
+#define OPTION_RELAXED_UNIQUE_CHECKS    (ULL(1) << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK                (ULL(1) << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE             (ULL(1) << 29) // SELECT, intern
+/* Flag set if setup_tables already done */
+#define OPTION_SETUP_TABLES_DONE        (ULL(1) << 30) // intern
+/* If not set then the thread will ignore all warnings with level notes. */
+#define OPTION_SQL_NOTES                (ULL(1) << 31) // THD, user
+/* 
+  Force the used temporary table to be a MyISAM table (because we will use
+  fulltext functions when reading from it.
+*/
+#define TMP_TABLE_FORCE_MYISAM          (ULL(1) << 32)
+#define OPTION_PROFILING                (ULL(1) << 33)
+
+
+
+/*
+  Maximum length of time zone name that we support
+  (Time zone name is char(64) in db). mysqlbinlog needs it.
+*/
+#define MAX_TIME_ZONE_NAME_LENGTH 72
+
+/* The rest of the file is included in the server only */
+#ifndef MYSQL_CLIENT
+
+/* Bits for different SQL modes modes (including ANSI mode) */
+#define MODE_REAL_AS_FLOAT              1
+#define MODE_PIPES_AS_CONCAT            2
+#define MODE_ANSI_QUOTES                4
+#define MODE_IGNORE_SPACE		8
+#define MODE_NOT_USED			16
+#define MODE_ONLY_FULL_GROUP_BY		32
+#define MODE_NO_UNSIGNED_SUBTRACTION	64
+#define MODE_NO_DIR_IN_CREATE		128
+#define MODE_POSTGRESQL			256
+#define MODE_ORACLE			512
+#define MODE_MSSQL			1024
+#define MODE_DB2			2048
+#define MODE_MAXDB			4096
+#define MODE_NO_KEY_OPTIONS             8192
+#define MODE_NO_TABLE_OPTIONS           16384 
+#define MODE_NO_FIELD_OPTIONS           32768
+#define MODE_MYSQL323                   65536
+#define MODE_MYSQL40                    (MODE_MYSQL323*2)
+#define MODE_ANSI	                (MODE_MYSQL40*2)
+#define MODE_NO_AUTO_VALUE_ON_ZERO      (MODE_ANSI*2)
+#define MODE_NO_BACKSLASH_ESCAPES       (MODE_NO_AUTO_VALUE_ON_ZERO*2)
+#define MODE_STRICT_TRANS_TABLES	(MODE_NO_BACKSLASH_ESCAPES*2)
+#define MODE_STRICT_ALL_TABLES		(MODE_STRICT_TRANS_TABLES*2)
+#define MODE_NO_ZERO_IN_DATE		(MODE_STRICT_ALL_TABLES*2)
+#define MODE_NO_ZERO_DATE		(MODE_NO_ZERO_IN_DATE*2)
+#define MODE_INVALID_DATES		(MODE_NO_ZERO_DATE*2)
+#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2)
+#define MODE_TRADITIONAL		(MODE_ERROR_FOR_DIVISION_BY_ZERO*2)
+#define MODE_NO_AUTO_CREATE_USER	(MODE_TRADITIONAL*2)
+#define MODE_HIGH_NOT_PRECEDENCE	(MODE_NO_AUTO_CREATE_USER*2)
+#define MODE_NO_ENGINE_SUBSTITUTION     (MODE_HIGH_NOT_PRECEDENCE*2)
+/*
+  Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
+  use strictly more than 64 bits by adding one more define above, you should
+  contact the replication team because the replication code should then be
+  updated (to store more bytes on disk).
+
+  NOTE: When adding new SQL_MODE types, make sure to also add them to
+  the scripts used for creating the MySQL system tables
+  in scripts/mysql_system_tables.sql and scripts/mysql_system_tables_fix.sql
+
+*/
+
+#define RAID_BLOCK_SIZE 1024
+
+#define MY_CHARSET_BIN_MB_MAXLEN 1
+
+// uncachable cause
+#define UNCACHEABLE_DEPENDENT   1
+#define UNCACHEABLE_RAND        2
+#define UNCACHEABLE_SIDEEFFECT	4
+// forcing to save JOIN for explain
+#define UNCACHEABLE_EXPLAIN     8
+/* Don't evaluate subqueries in prepare even if they're not correlated */
+#define UNCACHEABLE_PREPARE    16
+/* For uncorrelated SELECT in an UNION with some correlated SELECTs */
+#define UNCACHEABLE_UNITED     32
+
+/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
+#define UNDEF_POS (-1)
+#ifdef EXTRA_DEBUG
+/*
+  Sync points allow us to force the server to reach a certain line of code
+  and block there until the client tells the server it is ok to go on.
+  The client tells the server to block with SELECT GET_LOCK()
+  and unblocks it with SELECT RELEASE_LOCK(). Used for debugging difficult
+  concurrency problems
+*/
+#define DBUG_SYNC_POINT(lock_name,lock_timeout) \
+ debug_sync_point(lock_name,lock_timeout)
+void debug_sync_point(const char* lock_name, uint lock_timeout);
+#else
+#define DBUG_SYNC_POINT(lock_name,lock_timeout)
+#endif /* EXTRA_DEBUG */
+
+/* BINLOG_DUMP options */
+
+#define BINLOG_DUMP_NON_BLOCK   1
+
+/* sql_show.cc:show_log_files() */
+#define SHOW_LOG_STATUS_FREE "FREE"
+#define SHOW_LOG_STATUS_INUSE "IN USE"
+
+struct TABLE_LIST;
+class String;
+void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
+
+/* Options to add_table_to_list() */
+#define TL_OPTION_UPDATING	1
+#define TL_OPTION_FORCE_INDEX	2
+#define TL_OPTION_IGNORE_LEAVES 4
+#define TL_OPTION_ALIAS         8
+
+/* Some portable defines */
+
+#define portable_sizeof_char_ptr 8
+
+#define tmp_file_prefix "#sql"			/* Prefix for tmp tables */
+#define tmp_file_prefix_length 4
+
+/* Flags for calc_week() function.  */
+#define WEEK_MONDAY_FIRST    1
+#define WEEK_YEAR            2
+#define WEEK_FIRST_WEEKDAY   4
+
+#define STRING_BUFFER_USUAL_SIZE 80
+
+enum enum_parsing_place
+{
+  NO_MATTER,
+  IN_HAVING,
+  SELECT_LIST,
+  IN_WHERE,
+  IN_ON
+};
+
+struct st_table;
+class THD;
+
+#define thd_proc_info(thd, msg)  set_thd_proc_info(thd, msg, __func__, __FILE__, __LINE__)
+
+/* Struct to handle simple linked lists */
+
+typedef struct st_sql_list {
+  uint elements;
+  byte *first;
+  byte **next;
+
+  st_sql_list() {}                              /* Remove gcc warning */
+  inline void empty()
+  {
+    elements=0;
+    first=0;
+    next= &first;
+  }
+  inline void link_in_list(byte *element,byte **next_ptr)
+  {
+    elements++;
+    (*next)=element;
+    next= next_ptr;
+    *next=0;
+  }
+  inline void save_and_clear(struct st_sql_list *save)
+  {
+    *save= *this;
+    empty();
+  }
+  inline void push_front(struct st_sql_list *save)
+  {
+    *save->next= first;				/* link current list last */
+    first= save->first;
+    elements+= save->elements;
+  }
+  inline void push_back(struct st_sql_list *save)
+  {
+    if (save->first)
+    {
+      *next= save->first;
+      next= save->next;
+      elements+= save->elements;
+    }
+  }
+} SQL_LIST;
+
+
+extern pthread_key(THD*, THR_THD);
+inline THD *_current_thd(void)
+{
+  return my_pthread_getspecific_ptr(THD*,THR_THD);
+}
+#define current_thd _current_thd()
+
+/*
+  External variables
+*/
+extern ulong server_id, concurrency;
+
+
+typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
+                                      uint key_length,
+                                      ulonglong *engine_data);
+#include "sql_string.h"
+#include "sql_list.h"
+#include "sql_map.h"
+#include "my_decimal.h"
+#include "handler.h"
+#include "parse_file.h"
+#include "table.h"
+#include "sql_error.h"
+#include "field.h"				/* Field definitions */
+#include "protocol.h"
+#include "sql_udf.h"
+#include "sql_profile.h"
+
+class user_var_entry;
+class Security_context;
+enum enum_var_type
+{
+  OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
+};
+class sys_var;
+class Comp_creator;
+typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
+#include "item.h"
+extern my_decimal decimal_zero;
+
+/**    
+  The meat of thd_proc_info(THD*, char*), a macro that packs the last
+  three calling-info parameters. 
+*/
+extern "C"
+const char *set_thd_proc_info(THD *thd, const char *info, 
+                              const char *calling_func,
+                              const char *calling_file,
+                              const unsigned int calling_line);
+
+/* sql_parse.cc */
+void free_items(Item *item);
+void cleanup_items(Item *item);
+class THD;
+void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
+bool check_one_table_access(THD *thd, ulong privilege,
+			   TABLE_LIST *tables);
+bool check_single_table_access(THD *thd, ulong privilege,
+			   TABLE_LIST *tables);
+bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
+			  bool is_proc, bool no_errors);
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
+bool check_merge_table_access(THD *thd, char *db,
+			      TABLE_LIST *table_list);
+bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
+bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
+bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
+int mysql_multi_update_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd);
+bool mysql_insert_select_prepare(THD *thd);
+bool update_precheck(THD *thd, TABLE_LIST *tables);
+bool delete_precheck(THD *thd, TABLE_LIST *tables);
+bool insert_precheck(THD *thd, TABLE_LIST *tables);
+bool create_table_precheck(THD *thd, TABLE_LIST *tables,
+                           TABLE_LIST *create_table);
+int append_query_string(CHARSET_INFO *csinfo,
+                        String const *from, String *to);
+
+void get_default_definer(THD *thd, LEX_USER *definer);
+LEX_USER *create_default_definer(THD *thd);
+LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
+LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+bool check_string_length(LEX_STRING *str,
+                         const char *err_msg, uint max_length);
+
+enum enum_mysql_completiontype {
+  ROLLBACK_RELEASE=-2, ROLLBACK=1,  ROLLBACK_AND_CHAIN=7,
+  COMMIT_RELEASE=-1,   COMMIT=0,    COMMIT_AND_CHAIN=6
+};
+
+int end_trans(THD *thd, enum enum_mysql_completiontype completion);
+
+Item *negate_expression(THD *thd, Item *expr);
+
+/* log.cc */
+void sql_perror(const char *message);
+
+void vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_information(const char *format, ...)
+  ATTRIBUTE_FORMAT(printf, 1, 2);
+
+#include "sql_class.h"
+#include "sql_acl.h"
+#include "tztime.h"
+#include "opt_range.h"
+
+#ifdef HAVE_QUERY_CACHE
+struct Query_cache_query_flags
+{
+  unsigned int client_long_flag:1;
+  unsigned int client_protocol_41:1;
+  unsigned int more_results_exists:1;
+  unsigned int pkt_nr;
+  uint character_set_client_num;
+  uint character_set_results_num;
+  uint collation_connection_num;
+  ha_rows limit;
+  Time_zone *time_zone;
+  ulong sql_mode;
+  ulong max_sort_length;
+  ulong group_concat_max_len;
+  ulong default_week_format;
+  ulong div_precision_increment;
+  MY_LOCALE *lc_time_names;
+};
+#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
+#include "sql_cache.h"
+#define query_cache_store_query(A, B) query_cache.store_query(A, B)
+#define query_cache_destroy() query_cache.destroy()
+#define query_cache_result_size_limit(A) query_cache.result_size_limit(A)
+#define query_cache_init() query_cache.init()
+#define query_cache_resize(A) query_cache.resize(A)
+#define query_cache_set_min_res_unit(A) query_cache.set_min_res_unit(A)
+#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C)
+#define query_cache_invalidate1(A) query_cache.invalidate(A)
+#define query_cache_send_result_to_client(A, B, C) \
+  query_cache.send_result_to_client(A, B, C)
+#define query_cache_invalidate_by_MyISAM_filename_ref \
+  &query_cache_invalidate_by_MyISAM_filename
+#else
+#define QUERY_CACHE_FLAGS_SIZE 0
+#define query_cache_store_query(A, B)
+#define query_cache_destroy()
+#define query_cache_result_size_limit(A)
+#define query_cache_init()
+#define query_cache_resize(A)
+#define query_cache_set_min_res_unit(A)
+#define query_cache_invalidate3(A, B, C)
+#define query_cache_invalidate1(A)
+#define query_cache_send_result_to_client(A, B, C) 0
+#define query_cache_invalidate_by_MyISAM_filename_ref NULL
+
+#define query_cache_abort(A)
+#define query_cache_end_of_result(A)
+#define query_cache_invalidate_by_MyISAM_filename_ref NULL
+#endif /*HAVE_QUERY_CACHE*/
+
+uint build_table_path(char *buff, size_t bufflen, const char *db,
+                      const char *table, const char *ext);
+int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
+bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
+bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
+void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
+bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
+                    my_bool drop_temporary);
+int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
+			 bool drop_temporary, bool drop_view, bool log_query);
+int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
+				   bool if_exists, bool drop_temporary,
+				   bool log_query);
+int quick_rm_table(enum db_type base,const char *db,
+		   const char *table_name);
+void close_cached_table(THD *thd, TABLE *table);
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
+bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
+                      char *new_table_name, char *new_table_alias,
+                      bool skip_error);
+bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
+                     bool force_switch);
+
+void mysql_parse(THD *thd, const char *inBuf, uint length,
+                 const char ** semicolon);
+
+bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
+bool is_update_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_reset_thd_for_next_command(THD *thd);
+bool mysql_new_select(LEX *lex, bool move_down);
+void create_select_for_variable(const char *var_name);
+void mysql_init_multi_delete(LEX *lex);
+bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
+void init_max_user_conn(void);
+void init_update_queries(void);
+void init_global_user_stats(void);
+void init_global_table_stats(void);
+void init_global_index_stats(void);
+void free_max_user_conn(void);
+void free_global_user_stats(void);
+void free_global_table_stats(void);
+void free_global_index_stats(void);
+pthread_handler_t handle_one_connection(void *arg);
+pthread_handler_t handle_bootstrap(void *arg);
+void end_thread(THD *thd,bool put_in_cache);
+void flush_thread_cache();
+int mysql_execute_command(THD *thd);
+bool dispatch_command(enum enum_server_command command, THD *thd,
+		      char* packet, uint packet_length);
+void log_slow_statement(THD *thd);
+bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
+bool compare_record(TABLE *table, query_id_t query_id);
+
+bool table_cache_init(void);
+void table_cache_free(void);
+uint cached_tables(void);
+void kill_mysql(void);
+void close_connection(THD *thd, uint errcode, bool lock);
+bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, 
+                          bool *write_to_binlog);
+bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
+		  bool no_grant, bool no_errors, bool schema_db);
+bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
+			bool no_errors);
+bool check_global_access(THD *thd, ulong want_access);
+
+bool mysql_backup_table(THD* thd, TABLE_LIST* table_list);
+bool mysql_restore_table(THD* thd, TABLE_LIST* table_list);
+
+bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list,
+                          HA_CHECK_OPT* check_opt);
+bool mysql_check_table(THD* thd, TABLE_LIST* table_list,
+                       HA_CHECK_OPT* check_opt);
+bool mysql_repair_table(THD* thd, TABLE_LIST* table_list,
+                        HA_CHECK_OPT* check_opt);
+bool mysql_analyze_table(THD* thd, TABLE_LIST* table_list,
+                         HA_CHECK_OPT* check_opt);
+bool mysql_optimize_table(THD* thd, TABLE_LIST* table_list,
+                          HA_CHECK_OPT* check_opt);
+bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
+                              LEX_STRING *key_cache_name);
+bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
+int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
+                             KEY_CACHE *dst_cache);
+TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
+
+bool mysql_xa_recover(THD *thd);
+
+bool check_simple_select();
+
+SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length,
+                                  SORT_FIELD *sortorder);
+int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+		List<Item> &fields, List <Item> &all_fields, ORDER *order);
+int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+		List<Item> &fields, List<Item> &all_fields, ORDER *order,
+		bool *hidden_group_fields);
+bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
+                   Item **ref_pointer_array);
+
+bool handle_select(THD *thd, LEX *lex, select_result *result,
+                   ulong setup_tables_done_option);
+bool mysql_select(THD *thd, Item ***rref_pointer_array,
+                  TABLE_LIST *tables, uint wild_num,  List<Item> &list,
+                  COND *conds, uint og_num, ORDER *order, ORDER *group,
+                  Item *having, ORDER *proc_param, ulonglong select_type, 
+                  select_result *result, SELECT_LEX_UNIT *unit, 
+                  SELECT_LEX *select_lex);
+void free_underlaid_joins(THD *thd, SELECT_LEX *select);
+bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
+                         select_result *result);
+int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
+			 select_result *result);
+bool mysql_union(THD *thd, LEX *lex, select_result *result,
+                 SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
+bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd,
+                                                      LEX *lex,
+                                                      TABLE_LIST *table));
+bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
+bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
+Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
+			Item ***copy_func, Field **from_field,
+                        Field **def_field,
+			bool group, bool modify_item,
+			bool table_cant_handle_bit_fields,
+                        bool make_copy_field,
+                        uint convert_blob_length);
+void sp_prepare_create_field(THD *thd, create_field *sql_field);
+int prepare_create_field(create_field *sql_field, 
+			 uint *blob_columns, 
+			 int *timestamps, int *timestamps_with_niladic,
+			 uint table_flags);
+bool mysql_create_table(THD *thd,const char *db, const char *table_name,
+                        HA_CREATE_INFO *create_info,
+                        Alter_info *alter_info,
+                        bool tmp_table, uint select_field_count);
+
+bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
+                       HA_CREATE_INFO *create_info,
+                       TABLE_LIST *table_list,
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore);
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
+bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table,
+                             HA_CREATE_INFO *create_info);
+bool mysql_rename_table(enum db_type base,
+			const char *old_db,
+			const char * old_name,
+			const char *new_db,
+			const char * new_name);
+bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
+                          Item **conds, uint order_num, ORDER *order);
+int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
+		 List<Item> &values,COND *conds,
+		 uint order_num, ORDER *order, ha_rows limit,
+		 enum enum_duplicates handle_duplicates, bool ignore);
+bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
+                        List<Item> *fields, List<Item> *values,
+                        COND *conds, ulonglong options,
+                        enum enum_duplicates handle_duplicates, bool ignore,
+                        SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
+bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
+                          List<Item> &fields, List_item *values,
+                          List<Item> &update_fields,
+                          List<Item> &update_values, enum_duplicates duplic,
+                          COND **where, bool select_insert,
+                          bool check_fields, bool abort_on_warning);
+bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
+                  List<List_item> &values, List<Item> &update_fields,
+                  List<Item> &update_values, enum_duplicates flag,
+                  bool ignore);
+int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
+                                           TABLE_LIST *table_list);
+void prepare_triggers_for_insert_stmt(THD *thd, TABLE *table,
+                                      enum_duplicates duplic);
+void mark_fields_used_by_triggers_for_insert_stmt(THD *thd, TABLE *table,
+                                                  enum_duplicates duplic);
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
+bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
+                  SQL_LIST *order, ha_rows rows, ulonglong options,
+                  bool reset_auto_increment);
+bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
+bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
+TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
+TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
+		  bool *refresh, uint flags);
+bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
+TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
+                                      uint key_length);
+bool table_cache_has_open_placeholder(THD *thd, const char *db,
+                                      const char *table_name);
+TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
+bool reopen_table(TABLE *table,bool locked);
+bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
+bool close_data_tables(THD *thd,const char *db, const char *table_name);
+bool wait_for_tables(THD *thd);
+bool table_is_used(TABLE *table, bool wait_for_name_lock);
+bool drop_locked_tables(THD *thd,const char *db, const char *table_name);
+void abort_locked_tables(THD *thd,const char *db, const char *table_name);
+void execute_init_command(THD *thd, sys_var_str *init_command_var,
+			  rw_lock_t *var_mutex);
+extern Field *not_found_field;
+extern Field *view_ref_found;
+
+enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
+				  IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
+                                  IGNORE_EXCEPT_NON_UNIQUE};
+Field *
+find_field_in_tables(THD *thd, Item_ident *item,
+                     TABLE_LIST *first_table, TABLE_LIST *last_table,
+                     Item **ref, find_item_error_report_type report_error,
+                     bool check_privileges, bool register_tree_change);
+Field *
+find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
+                        const char *name, uint length,
+                        const char *item_name, const char *db_name,
+                        const char *table_name, Item **ref,
+                        bool check_privileges, bool allow_rowid,
+                        uint *cached_field_index_ptr,
+                        bool register_tree_change, TABLE_LIST **actual_table);
+Field *
+find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+                    bool allow_rowid, uint *cached_field_index_ptr);
+
+#ifdef HAVE_OPENSSL
+#include <openssl/des.h>
+struct st_des_keyblock
+{
+  DES_cblock key1, key2, key3;
+};
+struct st_des_keyschedule
+{
+  DES_key_schedule ks1, ks2, ks3;
+};
+extern char *des_key_file;
+extern struct st_des_keyschedule des_keyschedule[10];
+extern uint des_default_key;
+extern pthread_mutex_t LOCK_des_key_file;
+bool load_des_key_file(const char *file_name);
+#endif /* HAVE_OPENSSL */
+
+/* sql_do.cc */
+bool mysql_do(THD *thd, List<Item> &values);
+
+/* sql_analyse.h */
+bool append_escaped(String *to_str, String *from_str);
+
+/* sql_show.cc */
+bool mysqld_show_open_tables(THD *thd,const char *wild);
+bool mysqld_show_logs(THD *thd);
+void append_identifier(THD *thd, String *packet, const char *name,
+		       uint length);
+int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
+int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
+bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
+bool mysqld_show_create_db(THD *thd, char *dbname,
+                           const HA_CREATE_INFO *create);
+
+void mysqld_list_processes(THD *thd,const char *user,bool verbose);
+int mysqld_show_status(THD *thd);
+int mysqld_show_variables(THD *thd,const char *wild);
+int mysqld_show_user_stats(THD *thd, const char *wild);
+int mysqld_show_table_stats(THD *thd, const char *wild);
+int mysqld_show_index_stats(THD *thd, const char *wild);
+bool mysqld_show_storage_engines(THD *thd);
+bool mysqld_show_privileges(THD *thd);
+bool mysqld_show_column_types(THD *thd);
+bool mysqld_help (THD *thd, const char *text);
+void calc_sum_of_all_status(STATUS_VAR *to);
+
+void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
+                    const LEX_STRING *definer_host);
+
+
+/* information schema */
+extern LEX_STRING INFORMATION_SCHEMA_NAME;
+LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
+                            const char* str, uint length,
+                            bool allocate_lex_string);
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
+ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
+int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
+                         enum enum_schema_tables schema_table_idx);
+int make_schema_select(THD *thd,  SELECT_LEX *sel,
+                       enum enum_schema_tables schema_table_idx);
+int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list);
+int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+bool get_schema_tables_result(JOIN *join,
+                              enum enum_schema_table_state executed_place);
+enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
+
+bool schema_table_store_record(THD *thd, TABLE *table);
+
+#define is_schema_db(X) \
+  !my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X))
+
+/* sql_prepare.cc */
+
+void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length);
+void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
+void mysql_stmt_close(THD *thd, char *packet);
+void mysql_sql_stmt_prepare(THD *thd);
+void mysql_sql_stmt_execute(THD *thd);
+void mysql_sql_stmt_close(THD *thd);
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
+void mysql_stmt_reset(THD *thd, char *packet);
+void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
+void reinit_stmt_before_use(THD *thd, LEX *lex);
+
+/* sql_handler.cc */
+bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
+bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
+bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
+                   List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
+int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
+                   bool is_locked);
+/* mysql_ha_flush mode_flags bits */
+#define MYSQL_HA_CLOSE_FINAL        0x00
+#define MYSQL_HA_REOPEN_ON_USAGE    0x01
+#define MYSQL_HA_FLUSH_ALL          0x02
+
+/* sql_base.cc */
+#define TMP_TABLE_KEY_EXTRA 8
+void set_item_name(Item *item,char *pos,uint length);
+bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type,
+		       char *length, char *decimal,
+		       uint type_modifier,
+		       Item *default_value, Item *on_update_value,
+		       LEX_STRING *comment,
+		       char *change, List<String> *interval_list,
+		       CHARSET_INFO *cs,
+		       uint uint_geom_type);
+create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
+				char *length, char *decimals,
+				uint type_modifier, 
+				Item *default_value, Item *on_update_value,
+				LEX_STRING *comment, char *change, 
+				List<String> *interval_list, CHARSET_INFO *cs,
+				uint uint_geom_type);
+void store_position_for_column(const char *name);
+bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
+bool push_new_name_resolution_context(THD *thd,
+                                      TABLE_LIST *left_op,
+                                      TABLE_LIST *right_op);
+void add_join_on(TABLE_LIST *b,Item *expr);
+void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
+                      SELECT_LEX *lex);
+bool add_proc_to_list(THD *thd, Item *item);
+TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
+void drop_open_table(THD *thd, TABLE *table, const char *db_name,
+                     const char *table_name);
+void update_non_unique_table_error(TABLE_LIST *update,
+                                   const char *operation,
+                                   TABLE_LIST *duplicate);
+
+SQL_SELECT *make_select(TABLE *head, table_map const_tables,
+			table_map read_tables, COND *conds,
+                        bool allow_null_cond,  int *error);
+extern Item **not_found_item;
+
+/*
+  A set of constants used for checking non aggregated fields and sum
+  functions mixture in the ONLY_FULL_GROUP_BY_MODE.
+*/
+#define NON_AGG_FIELD_USED  1
+#define SUM_FUNC_USED       2
+
+/*
+  This enumeration type is used only by the function find_item_in_list
+  to return the info on how an item has been resolved against a list
+  of possibly aliased items.
+  The item can be resolved: 
+   - against an alias name of the list's element (RESOLVED_AGAINST_ALIAS)
+   - against non-aliased field name of the list  (RESOLVED_WITH_NO_ALIAS)
+   - against an aliased field name of the list   (RESOLVED_BEHIND_ALIAS)
+   - ignoring the alias name in cases when SQL requires to ignore aliases
+     (e.g. when the resolved field reference contains a table name or
+     when the resolved item is an expression)   (RESOLVED_IGNORING_ALIAS)    
+*/
+enum enum_resolution_type {
+  NOT_RESOLVED=0,
+  RESOLVED_IGNORING_ALIAS,
+  RESOLVED_BEHIND_ALIAS,
+  RESOLVED_WITH_NO_ALIAS,
+  RESOLVED_AGAINST_ALIAS
+};
+Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
+                          find_item_error_report_type report_error,
+                          enum_resolution_type *resolution);
+bool get_key_map_from_key_list(key_map *map, TABLE *table,
+                               List<String> *index_list);
+bool insert_fields(THD *thd, Name_resolution_context *context,
+		   const char *db_name, const char *table_name,
+                   List_iterator<Item> *it, bool any_privileges);
+bool setup_tables(THD *thd, Name_resolution_context *context,
+                  List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+                  Item **conds, TABLE_LIST **leaves, bool select_insert);
+bool setup_tables_and_check_access (THD *thd, 
+                                    Name_resolution_context *context,
+                                    List<TABLE_LIST> *from_clause, 
+                                    TABLE_LIST *tables, Item **conds, 
+                                    TABLE_LIST **leaves, 
+                                    bool select_insert,
+                                    ulong want_access_first,
+                                    ulong want_access);
+int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
+	       List<Item> *sum_func_list, uint wild_num);
+bool setup_fields(THD *thd, Item** ref_pointer_array,
+                  List<Item> &item, bool set_query_id,
+                  List<Item> *sum_func_list, bool allow_sum_func);
+inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array,
+                                     List<Item> &item, bool set_query_id,
+                                     List<Item> *sum_func_list,
+                                     bool allow_sum_func)
+{
+  bool res;
+  thd->lex->select_lex.no_wrap_view_item= TRUE;
+  res= setup_fields(thd, ref_pointer_array, item, set_query_id, sum_func_list,
+                    allow_sum_func);
+  thd->lex->select_lex.no_wrap_view_item= FALSE;
+  return res;
+}
+int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
+		COND **conds);
+int setup_ftfuncs(SELECT_LEX* select);
+int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
+void wait_for_refresh(THD *thd);
+int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
+int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
+int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
+int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
+TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
+			    const char *table_name, bool link_in_list);
+bool rm_temporary_table(enum db_type base, char *path);
+void free_io_cache(TABLE *entry);
+void intern_close_table(TABLE *entry);
+bool close_thread_table(THD *thd, TABLE **table_ptr);
+void close_temporary_tables(THD *thd);
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
+TABLE_LIST *find_table_in_list(TABLE_LIST *table,
+                               TABLE_LIST *TABLE_LIST::*link,
+                               const char *db_name,
+                               const char *table_name);
+TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
+                         bool check_alias);
+TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
+bool close_temporary_table(THD *thd, const char *db, const char *table_name);
+void close_temporary(TABLE *table, bool delete_table);
+bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
+			    const char *table_name);
+void remove_db_from_cache(const char *db);
+void flush_tables();
+bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
+
+/* bits for last argument to remove_table_from_cache() */
+#define RTFC_NO_FLAG                0x0000
+#define RTFC_OWNED_BY_THD_FLAG      0x0001
+#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
+#define RTFC_CHECK_KILLED_FLAG      0x0004
+bool remove_table_from_cache(THD *thd, const char *db, const char *table,
+                             uint flags);
+
+bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables);
+void copy_field_from_tmp_record(Field *field,int offset);
+bool fill_record(THD *thd, Field **field, List<Item> &values,
+                 bool ignore_errors);
+bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
+                                          List<Item> &values,
+                                          bool ignore_errors,
+                                          Table_triggers_list *triggers,
+                                          enum trg_event_type event);
+bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
+                                          List<Item> &values,
+                                          bool ignore_errors,
+                                          Table_triggers_list *triggers,
+                                          enum trg_event_type event);
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
+
+inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
+                                             const char *db_name,
+                                             const char *table_name)
+{
+  return find_table_in_list(table, &TABLE_LIST::next_global,
+                            db_name, table_name);
+}
+
+inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
+                                            const char *db_name,
+                                            const char *table_name)
+{
+  return find_table_in_list(table, &TABLE_LIST::next_local,
+                            db_name, table_name);
+}
+
+
+/* sql_calc.cc */
+bool eval_const_cond(COND *cond);
+
+/* sql_load.cc */
+int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
+	        List<Item> &fields_vars, List<Item> &set_fields,
+                List<Item> &set_values_list,
+                enum enum_duplicates handle_duplicates, bool ignore,
+                bool local_file);
+int write_record(THD *thd, TABLE *table, COPY_INFO *info);
+
+/* sql_manager.cc */
+/* bits set in manager_status */
+#define MANAGER_BERKELEY_LOG_CLEANUP    (1L << 0)
+extern ulong volatile manager_status;
+extern bool volatile manager_thread_in_use, mqh_used;
+extern pthread_t manager_thread;
+pthread_handler_t handle_manager(void *arg);
+
+/* sql_test.cc */
+#ifndef DBUG_OFF
+void print_where(COND *cond,const char *info);
+void print_cached_tables(void);
+void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
+void print_plan(JOIN* join,uint idx, double record_count, double read_time,
+                double current_read_time, const char *info);
+#endif
+void mysql_print_status();
+/* key.cc */
+int find_ref_key(TABLE *form,Field *field, uint *offset);
+void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
+void key_restore(byte *to_record, byte *from_key, KEY *key_info,
+                 uint key_length);
+bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
+void key_unpack(String *to,TABLE *form,uint index);
+bool is_key_used(TABLE *table, uint idx, List<Item> &fields);
+int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
+
+bool init_errmessage(void);
+
+bool fn_format_relative_to_data_home(my_string to, const char *name,
+				     const char *dir, const char *extension);
+File open_binlog(IO_CACHE *log, const char *log_file_name,
+                 const char **errmsg);
+
+/* mysqld.cc */
+extern void MYSQLerror(const char*);
+void refresh_status(THD *thd);
+my_bool mysql_rm_tmp_tables(void);
+
+/* item_func.cc */
+extern bool check_reserved_words(LEX_STRING *name);
+extern enum_field_types agg_field_type(Item **items, uint nitems);
+
+/* strfunc.cc */
+ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs,
+		   char **err_pos, uint *err_len, bool *set_warning);
+uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match);
+uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs);
+void unhex_type2(TYPELIB *lib);
+uint check_word(TYPELIB *lib, const char *val, const char *end,
+		const char **end_of_word);
+
+
+bool is_keyword(const char *name, uint len);
+
+#define MY_DB_OPT_FILE "db.opt"
+bool check_db_dir_existence(const char *db_name);
+bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
+bool load_db_opt_by_name(THD *thd, const char *db_name,
+                         HA_CREATE_INFO *db_create_info);
+bool my_dbopt_init(void);
+void my_dbopt_cleanup(void);
+void my_dbopt_free(void);
+
+/*
+  External variables
+*/
+
+extern time_t server_start_time, flush_status_time;
+extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
+	    mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[],
+	    mysql_unpacked_real_data_home[],
+            def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
+#define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
+extern MY_TMPDIR mysql_tmpdir_list;
+extern const char *command_name[];
+extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
+extern const char **errmesg;			/* Error messages */
+extern const char *myisam_recover_options_str;
+extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
+extern const char * const triggers_file_ext;
+extern const char * const trigname_file_ext;
+extern Eq_creator eq_creator;
+extern Ne_creator ne_creator;
+extern Gt_creator gt_creator;
+extern Lt_creator lt_creator;
+extern Ge_creator ge_creator;
+extern Le_creator le_creator;
+extern char language[FN_REFLEN], reg_ext[FN_EXTLEN];
+extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
+extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
+extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
+extern ulonglong log_10_int[20];
+extern ulonglong keybuff_size;
+extern ulonglong thd_startup_options;
+extern ulong flush_version, thread_id;
+extern ulong binlog_cache_use, binlog_cache_disk_use;
+extern ulong aborted_threads,aborted_connects;
+extern ulong delayed_insert_timeout;
+extern ulong delayed_insert_limit, delayed_queue_size;
+extern ulong delayed_insert_threads, delayed_insert_writes;
+extern ulong delayed_rows_in_use,delayed_insert_errors;
+extern ulong slave_open_temp_tables;
+extern ulong query_cache_size, query_cache_min_res_unit;
+extern ulong slow_launch_threads, slow_launch_time;
+extern ulong table_cache_size;
+extern ulong max_connections,max_connect_errors, connect_timeout;
+extern ulong slave_net_timeout, slave_trans_retries;
+extern uint max_user_connections;
+extern ulong what_to_log,flush_time;
+extern ulong query_buff_size, thread_stack;
+extern ulong max_prepared_stmt_count, prepared_stmt_count;
+extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
+extern ulong max_binlog_size, max_relay_log_size;
+extern ulong rpl_recovery_rank, thread_cache_size;
+extern ulong back_log;
+extern ulong specialflag, current_pid;
+extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
+extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
+extern ulong tc_log_page_waits;
+extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
+extern uint test_flags,select_errors,ha_open_options;
+extern uint protocol_version, mysqld_port, dropping_tables;
+extern uint delay_key_write_options, lower_case_table_names;
+extern bool opt_endinfo, using_udf_functions;
+extern my_bool locked_in_memory;
+extern bool opt_using_transactions, mysqld_embedded;
+extern bool using_update_log, opt_large_files, server_id_supplied;
+extern bool opt_update_log, opt_bin_log, opt_error_log;
+extern my_bool opt_log, opt_slow_log, opt_log_queries_not_using_indexes;
+extern bool opt_disable_networking, opt_skip_show_db;
+extern my_bool opt_character_set_client_handshake;
+extern bool volatile abort_loop, shutdown_in_progress, grant_option;
+extern uint volatile thread_count, thread_running, global_read_lock;
+extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
+extern my_bool opt_safe_show_db, opt_local_infile;
+extern my_bool opt_slave_compressed_protocol, use_temp_pool;
+extern my_bool opt_readonly, lower_case_file_system;
+extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
+extern my_bool opt_secure_auth;
+extern char* opt_secure_file_priv;
+extern my_bool opt_log_slow_admin_statements;
+extern my_bool sp_automatic_privileges, opt_noacl;
+extern my_bool opt_old_style_user_limits, trust_function_creators;
+extern uint opt_crash_binlog_innodb;
+extern char *shared_memory_base_name, *mysqld_unix_port;
+extern my_bool opt_enable_shared_memory;
+extern char *default_tz_name;
+extern my_bool opt_large_pages;
+extern uint opt_large_page_size;
+
+extern char *opt_plugin_dir_ptr;
+extern char opt_plugin_dir[FN_REFLEN];
+
+extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log;
+extern FILE *bootstrap_file;
+extern int bootstrap_error;
+extern FILE *stderror_file;
+extern pthread_key(MEM_ROOT**,THR_MALLOC);
+extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
+       LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
+       LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
+       LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
+       LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
+       LOCK_global_system_variables, LOCK_user_conn,
+       LOCK_prepared_stmt_count,
+       LOCK_bytes_sent, LOCK_bytes_received;
+#ifdef HAVE_OPENSSL
+extern pthread_mutex_t LOCK_des_key_file;
+#endif
+extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager;
+extern pthread_cond_t COND_global_read_lock;
+extern pthread_attr_t connection_attrib;
+extern I_List<THD> threads;
+extern I_List<NAMED_LIST> key_caches;
+extern MY_BITMAP temp_pool;
+extern String my_empty_string;
+extern const String my_null_string;
+extern SHOW_VAR init_vars[],status_vars[], internal_vars[];
+extern struct system_variables global_system_variables;
+extern struct system_variables max_system_variables;
+extern struct system_status_var global_status_var;
+extern struct rand_struct sql_rand;
+extern HASH global_user_stats;
+extern pthread_mutex_t LOCK_global_user_stats;
+extern HASH global_table_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern HASH global_index_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
+
+extern const char *opt_date_time_formats[];
+extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
+
+extern String null_string;
+extern HASH open_cache;
+extern TABLE *unused_tables;
+extern I_List<i_string> binlog_do_db, binlog_ignore_db;
+extern const char* any_db;
+extern struct my_option my_long_options[];
+extern const LEX_STRING view_type;
+
+/* optional things, have_* variables */
+
+#ifdef HAVE_INNOBASE_DB
+extern handlerton innobase_hton;
+#define have_innodb innobase_hton.state
+#else
+extern SHOW_COMP_OPTION have_innodb;
+#endif
+#ifdef HAVE_BERKELEY_DB
+extern handlerton berkeley_hton;
+#define have_berkeley_db berkeley_hton.state
+#else
+extern SHOW_COMP_OPTION have_berkeley_db;
+#endif
+#ifdef HAVE_EXAMPLE_DB
+extern handlerton example_hton;
+#define have_example_db example_hton.state
+#else
+extern SHOW_COMP_OPTION have_example_db;
+#endif
+#ifdef HAVE_ARCHIVE_DB
+extern handlerton archive_hton;
+#define have_archive_db archive_hton.state
+#else
+extern SHOW_COMP_OPTION have_archive_db;
+#endif
+#ifdef HAVE_CSV_DB
+extern handlerton tina_hton;
+#define have_csv_db tina_hton.state
+#else
+extern SHOW_COMP_OPTION have_csv_db;
+#endif
+#ifdef HAVE_FEDERATED_DB
+extern handlerton federated_hton;
+#define have_federated_db federated_hton.state
+#else
+extern SHOW_COMP_OPTION have_federated_db;
+#endif
+#ifdef HAVE_BLACKHOLE_DB
+extern handlerton blackhole_hton;
+#define have_blackhole_db blackhole_hton.state
+#else
+extern SHOW_COMP_OPTION have_blackhole_db;
+#endif
+#ifdef HAVE_NDBCLUSTER_DB
+extern handlerton ndbcluster_hton;
+#define have_ndbcluster ndbcluster_hton.state
+#else
+extern SHOW_COMP_OPTION have_ndbcluster;
+#endif
+
+/* MRG_MYISAM handler is always built, but may be skipped */
+extern handlerton myisammrg_hton;
+#define have_merge_db myisammrg_hton.state
+
+extern SHOW_COMP_OPTION have_isam;
+extern SHOW_COMP_OPTION have_raid, have_ssl, have_symlink, have_dlopen;
+extern SHOW_COMP_OPTION have_query_cache;
+extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
+extern SHOW_COMP_OPTION have_crypt;
+extern SHOW_COMP_OPTION have_compress;
+
+#ifndef __WIN__
+extern pthread_t signal_thread;
+#endif
+
+#ifdef HAVE_OPENSSL
+extern struct st_VioSSLFd * ssl_acceptor_fd;
+#endif /* HAVE_OPENSSL */
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
+                              uint flags, bool *need_reopen);
+/* mysql_lock_tables() and open_table() flags bits */
+#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
+#define MYSQL_LOCK_IGNORE_FLUSH                 0x0002
+#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004
+#define MYSQL_OPEN_TEMPORARY_ONLY               0x0008
+
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
+                       bool always_unlock);
+void mysql_lock_abort(THD *thd, TABLE *table);
+bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
+TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
+                                      TABLE_LIST *haystack);
+bool lock_global_read_lock(THD *thd);
+void unlock_global_read_lock(THD *thd);
+bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
+                              bool is_not_commit);
+void start_waiting_global_read_lock(THD *thd);
+bool make_global_read_lock_block_commit(THD *thd);
+bool set_protect_against_global_read_lock(void);
+void unset_protect_against_global_read_lock(void);
+void broadcast_refresh(void);
+
+/* Lock based on name */
+int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
+int lock_table_name(THD *thd, TABLE_LIST *table_list);
+void unlock_table_name(THD *thd, TABLE_LIST *table_list);
+bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
+bool lock_table_names(THD *thd, TABLE_LIST *table_list);
+void unlock_table_names(THD *thd, TABLE_LIST *table_list,
+			TABLE_LIST *last_table);
+
+
+/* old unireg functions */
+
+void unireg_init(ulong options);
+void unireg_end(void);
+bool mysql_create_frm(THD *thd, my_string file_name,
+                      const char *db, const char *table,
+		      HA_CREATE_INFO *create_info,
+		      List<create_field> &create_field,
+		      uint key_count,KEY *key_info,handler *db_type);
+int rea_create_table(THD *thd, my_string file_name,
+                     const char *db, const char *table,
+                     HA_CREATE_INFO *create_info,
+		     List<create_field> &create_field,
+		     uint key_count,KEY *key_info);
+int format_number(uint inputflag,uint max_length,my_string pos,uint length,
+		  my_string *errpos);
+int openfrm(THD *thd, const char *name,const char *alias,uint filestat,
+            uint prgflag, uint ha_open_flags, TABLE *outparam);
+int readfrm(const char *name, const void** data, uint* length);
+int writefrm(const char* name, const void* data, uint len);
+int closefrm(TABLE *table);
+int read_string(File file, gptr *to, uint length);
+void free_blobs(TABLE *table);
+int set_zone(int nr,int min_zone,int max_zone);
+ulong convert_period_to_month(ulong period);
+ulong convert_month_to_period(ulong month);
+void get_date_from_daynr(long daynr,uint *year, uint *month,
+			 uint *day);
+my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *not_exist);
+bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time);
+timestamp_type str_to_datetime_with_warn(const char *str, uint length,
+                                         MYSQL_TIME *l_time, uint flags);
+void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
+void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
+
+void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+                                  const char *str_val,
+  				  uint str_length, timestamp_type time_type,
+                                    const char *field_name);
+  
+bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval);
+bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
+                      longlong *seconds_out, long *microseconds_out);
+
+extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
+					       const char *format_str,
+					       uint format_length);
+extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
+					       DATE_TIME_FORMAT *format);
+const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
+				     timestamp_type type);
+extern bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
+			   timestamp_type type, String *str);
+void make_datetime(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
+                   String *str);
+void make_date(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
+               String *str);
+void make_time(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
+               String *str);
+ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
+                             Item *warn_item, bool *is_null);
+
+int test_if_number(char *str,int *res,bool allow_wildcards);
+void change_byte(byte *,uint,char,char);
+void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
+		      SQL_SELECT *select,
+		      int use_record_cache, bool print_errors);
+void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, 
+                          bool print_error, uint idx);
+void end_read_record(READ_RECORD *info);
+ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
+		 uint s_length, SQL_SELECT *select,
+		 ha_rows max_rows, ha_rows *examined_rows);
+void filesort_free_buffers(TABLE *table, bool full);
+void change_double_for_sort(double nr,byte *to);
+double my_double_round(double value, longlong dec, bool dec_unsigned,
+                       bool truncate);
+int get_quick_record(SQL_SELECT *select);
+int calc_weekday(long daynr,bool sunday_first_day_of_week);
+uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
+void find_date(char *pos,uint *vek,uint flag);
+TYPELIB *convert_strings_to_array_type(my_string *typelibs, my_string *end);
+TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
+ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
+ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
+		     const char *newname);
+ulong next_io_size(ulong pos);
+void append_unescaped(String *res, const char *pos, uint length);
+int create_frm(THD *thd, char *name, const char *db, const char *table,
+               uint reclength,uchar *fileinfo,
+	       HA_CREATE_INFO *create_info, uint keys);
+void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
+int rename_file_ext(const char * from,const char * to,const char * ext);
+bool check_db_name(char *db);
+bool check_column_name(const char *name);
+bool check_table_name(const char *name, uint length);
+char *get_field(MEM_ROOT *mem, Field *field);
+bool get_field(MEM_ROOT *mem, Field *field, class String *res);
+int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
+
+/* from hostname.cc */
+struct in_addr;
+my_string ip_to_hostname(struct in_addr *in,uint *errors);
+void inc_host_errors(struct in_addr *in);
+void reset_host_errors(struct in_addr *in);
+bool hostname_cache_init();
+void hostname_cache_free();
+void hostname_cache_refresh(void);
+
+/* sql_cache.cc */
+extern bool sql_cache_init();
+extern void sql_cache_free();
+extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
+
+/* item_func.cc */
+Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
+		     LEX_STRING component);
+int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
+                        LEX_STRING &name, user_var_entry **out_entry);
+/* log.cc */
+bool flush_error_log(void);
+
+/* sql_list.cc */
+void free_list(I_List <i_string_pair> *list);
+void free_list(I_List <i_string> *list);
+
+/* sql_yacc.cc */
+extern int MYSQLparse(void *thd);
+#ifndef DBUG_OFF
+extern void turn_parser_debug_on();
+#endif
+
+/* frm_crypt.cc */
+#ifdef HAVE_CRYPTED_FRM
+SQL_CRYPT *get_crypt_for_frm(void);
+#endif
+
+#include "sql_view.h"
+
+/* Some inline functions for more speed */
+
+inline bool add_item_to_list(THD *thd, Item *item)
+{
+  return thd->lex->current_select->add_item_to_list(thd, item);
+}
+
+inline bool add_value_to_list(THD *thd, Item *value)
+{
+  return thd->lex->value_list.push_back(value);
+}
+
+inline bool add_order_to_list(THD *thd, Item *item, bool asc)
+{
+  return thd->lex->current_select->add_order_to_list(thd, item, asc);
+}
+
+inline bool add_group_to_list(THD *thd, Item *item, bool asc)
+{
+  return thd->lex->current_select->add_group_to_list(thd, item, asc);
+}
+
+inline void mark_as_null_row(TABLE *table)
+{
+  table->null_row=1;
+  table->status|=STATUS_NULL_ROW;
+  bfill(table->null_flags,table->s->null_bytes,255);
+}
+
+inline void table_case_convert(char * name, uint length)
+{
+  if (lower_case_table_names)
+    files_charset_info->cset->casedn(files_charset_info,
+                                     name, length, name, length);
+}
+
+inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
+{
+  return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
+}
+
+inline ulong sql_rnd_with_mutex()
+{
+  pthread_mutex_lock(&LOCK_thread_count);
+  ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
+  pthread_mutex_unlock(&LOCK_thread_count);
+  return tmp;
+}
+
+Comp_creator *comp_eq_creator(bool invert);
+Comp_creator *comp_ge_creator(bool invert);
+Comp_creator *comp_gt_creator(bool invert);
+Comp_creator *comp_le_creator(bool invert);
+Comp_creator *comp_lt_creator(bool invert);
+Comp_creator *comp_ne_creator(bool invert);
+
+Item * all_any_subquery_creator(Item *left_expr,
+				chooser_compare_func_creator cmp,
+				bool all,
+				SELECT_LEX *select_lex);
+
+/*
+  clean/setup table fields and map
+
+  SYNOPSYS
+    setup_table_map()
+    table - TABLE structure pointer (which should be setup)
+    table_list TABLE_LIST structure pointer (owner of TABLE)
+    tablenr - table number
+*/
+
+inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
+{
+  table->used_fields= 0;
+  table->const_table= 0;
+  table->null_row= 0;
+  table->status= STATUS_NO_RECORD;
+  table->keys_in_use_for_query= table->s->keys_in_use;
+  table->maybe_null= table_list->outer_join;
+  TABLE_LIST *embedding= table_list->embedding;
+  while (!table->maybe_null && embedding)
+  {
+    table->maybe_null= embedding->outer_join;
+    embedding= embedding->embedding;
+  }
+  table->tablenr= tablenr;
+  table->map= (table_map) 1 << tablenr;
+  table->force_index= table_list->force_index;
+}
+
+
+/*
+  SYNOPSYS
+    hexchar_to_int()
+    convert a hex digit into number
+*/
+
+inline int hexchar_to_int(char c)
+{
+  if (c <= '9' && c >= '0')
+    return c-'0';
+  c|=32;
+  if (c <= 'f' && c >= 'a')
+    return c-'a'+10;
+  return -1;
+}
+
+/*
+  is_user_table()
+  return true if the table was created explicitly
+*/
+
+inline bool is_user_table(TABLE * table)
+{
+  const char *name= table->s->table_name;
+  return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
+}
+
+/*
+  Some functions that are different in the embedded library and the normal
+  server
+*/
+
+#ifndef EMBEDDED_LIBRARY
+extern "C" void unireg_abort(int exit_code);
+void kill_delayed_threads(void);
+bool check_stack_overrun(THD *thd, long margin, char *dummy);
+#else
+#define unireg_abort(exit_code) DBUG_RETURN(exit_code)
+inline void kill_delayed_threads(void) {}
+#define check_stack_overrun(A, B, C) 0
+#endif
+
+#endif /* MYSQL_CLIENT */
+
+#endif
diff -Nru mysql-5.0.67.orig/sql/mysqld.cc mysql-5.0.67.microslow_and_userstats/sql/mysqld.cc
--- mysql-5.0.67.orig/sql/mysqld.cc	Mon Aug  4 15:20:07 2008
+++ mysql-5.0.67.microslow_and_userstats/sql/mysqld.cc	Wed Sep  3 12:11:39 2008
@@ -176,7 +176,6 @@
 static void getvolumeID(BYTE *volumeName);
 #endif /* __NETWARE__ */
 
-
 #ifdef _AIX41
 int initgroups(const char *,unsigned int);
 #endif
@@ -409,6 +408,7 @@
 my_bool opt_secure_auth= 0;
 char* opt_secure_file_priv= 0;
 my_bool opt_log_slow_admin_statements= 0;
+my_bool opt_log_slow_slave_statements= 0;
 my_bool lower_case_file_system= 0;
 my_bool opt_large_pages= 0;
 uint    opt_large_page_size= 0;
@@ -506,6 +506,7 @@
 Ge_creator ge_creator;
 Le_creator le_creator;
 
+ulonglong frequency= 0;
 
 FILE *bootstrap_file;
 int bootstrap_error;
@@ -547,6 +548,9 @@
 		LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
 	        LOCK_global_system_variables,
 		LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
+pthread_mutex_t LOCK_global_user_stats;
+pthread_mutex_t LOCK_global_table_stats;
+pthread_mutex_t LOCK_global_index_stats;
 /*
   The below lock protects access to two global server variables:
   max_prepared_stmt_count and prepared_stmt_count. These variables
@@ -1188,6 +1192,9 @@
   x_free(opt_secure_file_priv);
   bitmap_free(&temp_pool);
   free_max_user_conn();
+  free_global_user_stats();
+  free_global_table_stats();
+  free_global_index_stats();
 #ifdef HAVE_REPLICATION
   end_slave_list();
   free_list(&replicate_do_db);
@@ -1302,6 +1309,9 @@
   (void) pthread_cond_destroy(&COND_thread_cache);
   (void) pthread_cond_destroy(&COND_flush_thread_cache);
   (void) pthread_cond_destroy(&COND_manager);
+  (void) pthread_mutex_destroy(&LOCK_global_user_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_table_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_index_stats);
 }
 
 #endif /*EMBEDDED_LIBRARY*/
@@ -3147,6 +3157,9 @@
   (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
   (void) pthread_cond_init(&COND_rpl_status, NULL);
 #endif
+  (void) pthread_mutex_init(&LOCK_global_user_stats, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
   sp_cache_init();
   /* Parameter for threads created for connections */
   (void) pthread_attr_init(&connection_attrib);
@@ -3418,6 +3431,10 @@
     sql_print_error("Out of memory");
     unireg_abort(1);
   }
+
+  init_global_table_stats();
+  init_global_index_stats();
+
   if (ha_init())
   {
     sql_print_error("Can't init databases");
@@ -3500,6 +3517,7 @@
 
   init_max_user_conn();
   init_update_queries();
+  init_global_user_stats();
   DBUG_RETURN(0);
 }
 
@@ -3652,6 +3670,8 @@
       unireg_abort(1);
     }
   }
+  if (!QueryPerformanceFrequency((LARGE_INTEGER *)&frequency))
+    frequency= 0;
 #endif /* __WIN__ */
 
   if (init_common_variables(MYSQL_CONFIG_NAME,
@@ -4895,7 +4915,7 @@
   OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
   OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
   OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
-  OPT_LONG_QUERY_TIME,
+  OPT_LONG_QUERY_TIME, OPT_MIN_EXAMINED_ROW_LIMIT,
   OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
   OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
   OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
@@ -4986,6 +5006,10 @@
   OPT_TIMED_MUTEXES,
   OPT_OLD_STYLE_USER_LIMITS,
   OPT_LOG_SLOW_ADMIN_STATEMENTS,
+  OPT_LOG_SLOW_SLAVE_STATEMENTS,
+  OPT_LOG_SLOW_RATE_LIMIT,
+  OPT_LOG_SLOW_VERBOSITY,
+  OPT_LOG_SLOW_FILTER,
   OPT_TABLE_LOCK_WAIT_TIMEOUT,
   OPT_PLUGIN_DIR,
   OPT_PORT_OPEN_TIMEOUT,
@@ -5380,6 +5404,11 @@
    (gptr*) &opt_log_slow_admin_statements,
    (gptr*) &opt_log_slow_admin_statements,
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"log-slow-slave-statements", OPT_LOG_SLOW_SLAVE_STATEMENTS,
+   "Log slow replicated statements to the slow log if it is open.",
+   (gptr*) &opt_log_slow_slave_statements,
+   (gptr*) &opt_log_slow_slave_statements,
+   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"log-slow-queries", OPT_SLOW_QUERY_LOG,
     "Log slow queries to this log file. Defaults logging to hostname-slow.log file. Must be enabled to activate other slow log options.",
    (gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG,
@@ -6049,11 +6078,27 @@
    (gptr*) 0,
    0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100,
    1, 100, 0, 1, 0},
+  {"log_slow_filter", OPT_LOG_SLOW_FILTER,
+    "Log only the queries that followed certain execution plan. Multiple flags allowed in a comma-separated string. [qc_miss, full_scan, full_join, tmp_table, tmp_table_on_disk, filesort, filesort_on_disk]",
+    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, SLOG_F_NONE, 0, 0},
+  {"log_slow_rate_limit", OPT_LOG_SLOW_RATE_LIMIT,
+    "Rate limit statement writes to slow log to only those from every (1/log_slow_rate_limit) session.",
+    (gptr*) &global_system_variables.log_slow_rate_limit,
+    (gptr*) &max_system_variables.log_slow_rate_limit, 0, GET_ULONG,
+    REQUIRED_ARG, 1, 1, ~0L, 0, 1L, 0},
+  {"log_slow_verbosity", OPT_LOG_SLOW_VERBOSITY,
+    "Choose how verbose the messages to your slow log will be. Multiple flags allowed in a comma-separated string. [microtime, query_plan, innodb]",
+    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, SLOG_V_MICROTIME, 0, 0},
   {"long_query_time", OPT_LONG_QUERY_TIME,
    "Log all queries that have taken more than long_query_time seconds to execute to file.",
    (gptr*) &global_system_variables.long_query_time,
    (gptr*) &max_system_variables.long_query_time, 0, GET_ULONG,
-   REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0},
+    REQUIRED_ARG, 10000000, 0, LONG_TIMEOUT * 1000000, 0, 1, 0},
+  {"min_examined_row_limit", OPT_MIN_EXAMINED_ROW_LIMIT,
+    "Don't log queries which examine less than min_examined_row_limit rows to file.",
+    (gptr*) &global_system_variables.min_examined_row_limit,
+    (gptr*) &max_system_variables.min_examined_row_limit, 0, GET_ULONG,
+    REQUIRED_ARG, 0, 0, ~0L, 0, 1L, 0},
   {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES,
    "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive.  Should be set to 2 if you are using a case insensitive file system",
    (gptr*) &lower_case_table_names,
@@ -6826,7 +6871,9 @@
   global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
   max_system_variables.max_join_size=   (ulonglong) HA_POS_ERROR;
   global_system_variables.old_passwords= 0;
-
+  global_system_variables.log_slow_verbosity= SLOG_V_MICROTIME;
+  global_system_variables.log_slow_filter= SLOG_F_NONE;
+  
   /*
     Default behavior for 4.1 and 5.0 is to treat NULL values as unequal
     when collecting index statistics for MyISAM tables.
@@ -7287,6 +7334,24 @@
   case OPT_BOOTSTRAP:
     opt_noacl=opt_bootstrap=1;
     break;
+  case OPT_LOG_SLOW_FILTER:
+    if ((global_system_variables.log_slow_filter= 
+          msl_flag_resolve_by_name(slog_filter, argument,
+                                   SLOG_F_NONE, SLOG_F_INVALID)) == SLOG_F_INVALID)
+    {
+      fprintf(stderr,"Invalid argument to log_slow_filter\n");
+      exit(1);
+    }
+    break;
+  case OPT_LOG_SLOW_VERBOSITY:
+    if ((global_system_variables.log_slow_verbosity= 
+         msl_flag_resolve_by_name(slog_verb, argument,
+                                  SLOG_V_NONE, SLOG_V_INVALID)) == SLOG_V_INVALID)
+    {
+      fprintf(stderr,"Invalid argument to log_slow_verbosity\n");
+      exit(1);
+    }
+    break;
   case OPT_STORAGE_ENGINE:
   {
     if ((enum db_type)((global_system_variables.table_type=
@@ -7619,10 +7684,14 @@
   if (opt_bdb)
     sql_print_warning("this binary does not contain BDB storage engine");
 #endif
-  if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes) &&
+  if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes ||
+        opt_log_slow_slave_statements) &&
       !opt_slow_log)
-    sql_print_warning("options --log-slow-admin-statements and --log-queries-not-using-indexes have no effect if --log-slow-queries is not set");
-
+  {
+    sql_print_warning("options --log-slow-admin-statements, --log-slow-slave-statements and --log-queries-not-using-indexes have no effect if --log-slow-queries is not set");
+    opt_log_slow_slave_statements= FALSE;
+  }
+  
   if (argc > 0)
   {
     fprintf(stderr, "%s: Too many arguments (first extra is '%s').\nUse --help to get a list of available options\n", my_progname, *argv);
diff -Nru mysql-5.0.67.orig/sql/mysqld.cc.orig mysql-5.0.67.microslow_and_userstats/sql/mysqld.cc.orig
--- mysql-5.0.67.orig/sql/mysqld.cc.orig	Thu Jan  1 02:00:00 1970
+++ mysql-5.0.67.microslow_and_userstats/sql/mysqld.cc.orig	Wed Sep  3 12:07:46 2008
@@ -0,0 +1,8031 @@
+/* Copyright (C) 2000-2003 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 */
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <my_dir.h>
+#include "slave.h"
+#include "sql_repl.h"
+#include "repl_failsafe.h"
+#include "stacktrace.h"
+#include "mysqld_suffix.h"
+#include "mysys_err.h"
+#ifdef HAVE_BERKELEY_DB
+#include "ha_berkeley.h"
+#endif
+#ifdef HAVE_INNOBASE_DB
+#include "ha_innodb.h"
+#endif
+#include "ha_myisam.h"
+#ifdef HAVE_NDBCLUSTER_DB
+#include "ha_ndbcluster.h"
+#endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#ifdef HAVE_INNOBASE_DB
+#define OPT_INNODB_DEFAULT 1
+#else
+#define OPT_INNODB_DEFAULT 0
+#endif
+#define OPT_BDB_DEFAULT 0
+#ifdef HAVE_NDBCLUSTER_DB
+#define OPT_NDBCLUSTER_DEFAULT 0
+#if defined(NOT_ENOUGH_TESTED) \
+  && defined(NDB_SHM_TRANSPORTER) && MYSQL_VERSION_ID >= 50000
+#define OPT_NDB_SHM_DEFAULT 1
+#else
+#define OPT_NDB_SHM_DEFAULT 0
+#endif
+#else
+#define OPT_NDBCLUSTER_DEFAULT 0
+#endif
+
+#ifndef DEFAULT_SKIP_THREAD_PRIORITY
+#define DEFAULT_SKIP_THREAD_PRIORITY 0
+#endif
+
+#include <thr_alarm.h>
+#include <ft_global.h>
+#include <errmsg.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
+
+#define mysqld_charset &my_charset_latin1
+
+#ifndef DBUG_OFF
+#define ONE_THREAD
+#endif
+
+#ifdef HAVE_purify
+#define IF_PURIFY(A,B) (A)
+#else
+#define IF_PURIFY(A,B) (B)
+#endif
+
+#if SIZEOF_CHARP == 4
+#define MAX_MEM_TABLE_SIZE ~(ulong) 0
+#else
+#define MAX_MEM_TABLE_SIZE ~(ulonglong) 0
+#endif
+
+/* stack traces are only supported on linux intel */
+#if defined(__linux__)  && defined(__i386__) && defined(USE_PSTACK)
+#define	HAVE_STACK_TRACE_ON_SEGV
+#include "../pstack/pstack.h"
+char pstack_file_name[80];
+#endif /* __linux__ */
+
+/* We have HAVE_purify below as this speeds up the shutdown of MySQL */
+
+#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_purify) && defined(__linux__)
+#define HAVE_CLOSE_SERVER_SOCK 1
+#endif
+
+extern "C" {					// Because of SCO 3.2V4.2
+#include <errno.h>
+#include <sys/stat.h>
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__				// Skip warnings in getopt.h
+#endif
+#include <my_getopt.h>
+#ifdef HAVE_SYSENT_H
+#include <sysent.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>				// For getpwent
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#include <my_net.h>
+
+#if defined(OS2)
+#  include <sys/un.h>
+#elif !defined(__WIN__)
+#  ifndef __NETWARE__
+#include <sys/resource.h>
+#  endif /* __NETWARE__ */
+#ifdef HAVE_SYS_UN_H
+#  include <sys/un.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+#  include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/utsname.h>
+#endif /* __WIN__ */
+
+#include <my_libwrap.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef __WIN__ 
+#include <crtdbg.h>
+#define SIGNAL_FMT "exception 0x%x"
+#else
+#define SIGNAL_FMT "signal %d"
+#endif
+
+#ifdef __NETWARE__
+#define zVOLSTATE_ACTIVE 6
+#define zVOLSTATE_DEACTIVE 2
+#define zVOLSTATE_MAINTENANCE 3
+
+#include <nks/netware.h>
+#include <nks/vm.h>
+#include <library.h>
+#include <monitor.h>
+#include <zOmni.h>                              //For NEB
+#include <neb.h>                                //For NEB
+#include <nebpub.h>                             //For NEB
+#include <zEvent.h>                             //For NSS event structures
+#include <zPublics.h>
+
+static void *neb_consumer_id= NULL;             //For storing NEB consumer id
+static char datavolname[256]= {0};
+static VolumeID_t datavolid;
+static event_handle_t eh;
+static Report_t ref;
+static void *refneb= NULL;
+my_bool event_flag= FALSE;
+static int volumeid= -1;
+
+  /* NEB event callback */
+unsigned long neb_event_callback(struct EventBlock *eblock);
+static void registerwithneb();
+static void getvolumename();
+static void getvolumeID(BYTE *volumeName);
+#endif /* __NETWARE__ */
+
+
+#ifdef _AIX41
+int initgroups(const char *,unsigned int);
+#endif
+
+#if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#ifdef HAVE_FP_EXCEPT				// Fix type conflict
+typedef fp_except fp_except_t;
+#endif
+
+  /* We can't handle floating point exceptions with threads, so disable
+     this on freebsd
+  */
+
+inline void set_proper_floating_point_mode()
+{
+  /* Don't fall for overflow, underflow,divide-by-zero or loss of precision */
+#if defined(__i386__)
+  fpsetmask(~(FP_X_INV | FP_X_DNML | FP_X_OFL | FP_X_UFL | FP_X_DZ |
+	      FP_X_IMP));
+#else
+ fpsetmask(~(FP_X_INV |             FP_X_OFL | FP_X_UFL | FP_X_DZ |
+	     FP_X_IMP));
+#endif
+}
+#elif defined(__sgi)
+/* for IRIX to use set_fpc_csr() */
+#include <sys/fpu.h>
+
+inline void set_proper_floating_point_mode()
+{
+  /* Enable denormalized DOUBLE values support for IRIX */
+  {
+    union fpc_csr n;
+    n.fc_word = get_fpc_csr();
+    n.fc_struct.flush = 0;
+    set_fpc_csr(n.fc_word);
+  }
+}
+#else
+#define set_proper_floating_point_mode()
+#endif /* __FreeBSD__ && HAVE_IEEEFP_H */
+
+} /* cplusplus */
+
+#define MYSQL_KILL_SIGNAL SIGTERM
+
+#ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R
+#include <sys/types.h>
+#else
+#include <my_pthread.h>			// For thr_setconcurency()
+#endif
+
+#ifdef SOLARIS
+extern "C" int gethostname(char *name, int namelen);
+#endif
+
+extern "C" sig_handler handle_segfault(int sig);
+
+/* Constants */
+
+const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};
+static const char *sql_mode_names[]=
+{
+  "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
+  "?", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",
+  "NO_DIR_IN_CREATE",
+  "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS",
+  "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI",
+  "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES",
+  "STRICT_ALL_TABLES",
+  "NO_ZERO_IN_DATE", "NO_ZERO_DATE", "ALLOW_INVALID_DATES",
+  "ERROR_FOR_DIVISION_BY_ZERO",
+  "TRADITIONAL", "NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE",
+  "NO_ENGINE_SUBSTITUTION",
+  NullS
+};
+static const unsigned int sql_mode_names_len[]=
+{
+  /*REAL_AS_FLOAT*/               13,
+  /*PIPES_AS_CONCAT*/             15,
+  /*ANSI_QUOTES*/                 11,
+  /*IGNORE_SPACE*/                12,
+  /*?*/                           1,
+  /*ONLY_FULL_GROUP_BY*/          18,
+  /*NO_UNSIGNED_SUBTRACTION*/     23,
+  /*NO_DIR_IN_CREATE*/            16,
+  /*POSTGRESQL*/                  10,
+  /*ORACLE*/                      6,
+  /*MSSQL*/                       5,
+  /*DB2*/                         3,
+  /*MAXDB*/                       5,
+  /*NO_KEY_OPTIONS*/              14,
+  /*NO_TABLE_OPTIONS*/            16,
+  /*NO_FIELD_OPTIONS*/            16,
+  /*MYSQL323*/                    8,
+  /*MYSQL40*/                     7,
+  /*ANSI*/                        4,
+  /*NO_AUTO_VALUE_ON_ZERO*/       21,
+  /*NO_BACKSLASH_ESCAPES*/        20,
+  /*STRICT_TRANS_TABLES*/         19,
+  /*STRICT_ALL_TABLES*/           17,
+  /*NO_ZERO_IN_DATE*/             15,
+  /*NO_ZERO_DATE*/                12,
+  /*ALLOW_INVALID_DATES*/         19,
+  /*ERROR_FOR_DIVISION_BY_ZERO*/  26,
+  /*TRADITIONAL*/                 11,
+  /*NO_AUTO_CREATE_USER*/         19,
+  /*HIGH_NOT_PRECEDENCE*/         19,
+  /*NO_ENGINE_SUBSTITUTION*/      22
+};
+TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"",
+			    sql_mode_names,
+                            (unsigned int *)sql_mode_names_len };
+static const char *tc_heuristic_recover_names[]=
+{
+  "COMMIT", "ROLLBACK", NullS
+};
+static TYPELIB tc_heuristic_recover_typelib=
+{
+  array_elements(tc_heuristic_recover_names)-1,"",
+  tc_heuristic_recover_names, NULL
+};
+const char *first_keyword= "first", *binary_keyword= "BINARY";
+const char *my_localhost= "localhost", *delayed_user= "DELAYED";
+#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
+#define GET_HA_ROWS GET_ULL
+#else
+#define GET_HA_ROWS GET_ULONG
+#endif
+
+bool opt_large_files= sizeof(my_off_t) > 4;
+
+/*
+  Used with --help for detailed option
+*/
+static my_bool opt_help= 0, opt_verbose= 0;
+
+arg_cmp_func Arg_comparator::comparator_matrix[5][2] =
+{{&Arg_comparator::compare_string,     &Arg_comparator::compare_e_string},
+ {&Arg_comparator::compare_real,       &Arg_comparator::compare_e_real},
+ {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int},
+ {&Arg_comparator::compare_row,        &Arg_comparator::compare_e_row},
+ {&Arg_comparator::compare_decimal,    &Arg_comparator::compare_e_decimal}};
+
+/* static variables */
+
+char opt_plugin_dir[FN_REFLEN];
+char *opt_plugin_dir_ptr;
+
+static bool lower_case_table_names_used= 0;
+static bool volatile select_thread_in_use, signal_thread_in_use;
+static bool volatile ready_to_exit;
+static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
+static my_bool opt_bdb, opt_isam, opt_ndbcluster, opt_merge, opt_federated;
+static my_bool opt_short_log_format= 0;
+static uint kill_cached_threads, wake_thread;
+static ulong killed_threads, thread_created;
+static ulong max_used_connections;
+static ulong my_bind_addr;			/* the address we bind to */
+static volatile ulong cached_thread_count= 0;
+static const char *sql_mode_str= "OFF";
+static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr;
+static char *opt_init_slave, *language_ptr, *opt_init_connect;
+static char *default_character_set_name;
+static char *character_set_filesystem_name;
+static char *lc_time_names_name;
+static char *my_bind_addr_str;
+static char *default_collation_name;
+static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
+static char mysql_data_home_buff[2];
+static I_List<THD> thread_cache;
+
+#ifndef EMBEDDED_LIBRARY
+static struct passwd *user_info;
+static pthread_t select_thread;
+static uint thr_kill_signal;
+#endif
+
+static pthread_cond_t COND_thread_cache, COND_flush_thread_cache;
+
+#ifdef HAVE_BERKELEY_DB
+static my_bool opt_sync_bdb_logs;
+#endif
+
+/* Global variables */
+
+bool opt_update_log, opt_bin_log;
+my_bool opt_log, opt_slow_log, opt_log_queries_not_using_indexes= 0;
+bool opt_error_log= IF_WIN(1,0);
+bool opt_disable_networking=0, opt_skip_show_db=0;
+my_bool opt_character_set_client_handshake= 1;
+bool server_id_supplied = 0;
+bool opt_endinfo, using_udf_functions;
+my_bool locked_in_memory;
+bool opt_using_transactions, using_update_log;
+bool volatile abort_loop;
+bool volatile shutdown_in_progress;
+/**
+   @brief 'grant_option' is used to indicate if privileges needs
+   to be checked, in which case the lock, LOCK_grant, is used
+   to protect access to the grant table.
+   @note This flag is dropped in 5.1 
+   @see grant_init()
+ */
+bool volatile grant_option;
+
+my_bool opt_skip_slave_start = 0; // If set, slave is not autostarted
+my_bool opt_reckless_slave = 0;
+my_bool opt_enable_named_pipe= 0;
+my_bool opt_local_infile, opt_slave_compressed_protocol;
+my_bool opt_safe_user_create = 0, opt_no_mix_types = 0;
+my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0;
+my_bool opt_log_slave_updates= 0;
+my_bool	opt_innodb;
+bool slave_warning_issued = false; 
+
+#ifdef HAVE_NDBCLUSTER_DB
+const char *opt_ndbcluster_connectstring= 0;
+const char *opt_ndb_connectstring= 0;
+char opt_ndb_constrbuf[1024];
+unsigned opt_ndb_constrbuf_len= 0;
+my_bool	opt_ndb_shm, opt_ndb_optimized_node_selection;
+ulong opt_ndb_cache_check_time;
+const char *opt_ndb_mgmd;
+ulong opt_ndb_nodeid;
+#endif
+my_bool opt_readonly, use_temp_pool, relay_log_purge;
+my_bool opt_sync_frm, opt_allow_suspicious_udfs;
+my_bool opt_secure_auth= 0;
+char* opt_secure_file_priv= 0;
+my_bool opt_log_slow_admin_statements= 0;
+my_bool lower_case_file_system= 0;
+my_bool opt_large_pages= 0;
+uint    opt_large_page_size= 0;
+my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
+/*
+  True if there is at least one per-hour limit for some user, so we should
+  check them before each query (and possibly reset counters when hour is
+  changed). False otherwise.
+*/
+volatile bool mqh_used = 0;
+my_bool opt_noacl;
+my_bool sp_automatic_privileges= 1;
+
+#ifdef HAVE_INITGROUPS
+static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */
+#endif
+uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
+uint mysqld_port_timeout;
+uint delay_key_write_options, protocol_version;
+uint lower_case_table_names;
+uint tc_heuristic_recover= 0;
+uint volatile thread_count, thread_running;
+ulonglong thd_startup_options;
+ulong back_log, connect_timeout, concurrency, server_id;
+ulong table_cache_size, thread_stack, what_to_log;
+ulong query_buff_size, slow_launch_time, slave_open_temp_tables;
+ulong open_files_limit, max_binlog_size, max_relay_log_size;
+ulong slave_net_timeout, slave_trans_retries;
+ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0;
+ulong query_cache_size=0;
+ulong refresh_version, flush_version;	/* Increments on each reload */
+query_id_t global_query_id;
+ulong aborted_threads, aborted_connects;
+ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
+ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use;
+ulong delayed_insert_errors,flush_time;
+ulong specialflag=0;
+ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
+ulong max_connections, max_connect_errors;
+uint  max_user_connections= 0;
+/*
+  Limit of the total number of prepared statements in the server.
+  Is necessary to protect the server against out-of-memory attacks.
+*/
+ulong max_prepared_stmt_count;
+/*
+  Current total number of prepared statements in the server. This number
+  is exact, and therefore may not be equal to the difference between
+  `com_stmt_prepare' and `com_stmt_close' (global status variables), as
+  the latter ones account for all registered attempts to prepare
+  a statement (including unsuccessful ones).  Prepared statements are
+  currently connection-local: if the same SQL query text is prepared in
+  two different connections, this counts as two distinct prepared
+  statements.
+*/
+ulong prepared_stmt_count=0;
+ulong thread_id=1L,current_pid;
+ulong slow_launch_threads = 0, sync_binlog_period;
+ulong expire_logs_days = 0;
+ulong rpl_recovery_rank=0;
+
+time_t server_start_time, flush_status_time;
+
+char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30];
+char *default_tz_name;
+char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN];
+char mysql_real_data_home[FN_REFLEN],
+     language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
+     *opt_init_file, *opt_tc_log_file,
+     mysql_unpacked_real_data_home[FN_REFLEN],
+     def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
+char *mysql_data_home= mysql_real_data_home;
+const key_map key_map_empty(0);
+key_map key_map_full(0);                        // Will be initialized later
+
+const char *opt_date_time_formats[3];
+char server_version[SERVER_VERSION_LENGTH];
+char *mysqld_unix_port, *opt_mysql_tmpdir;
+const char **errmesg;			/* Error messages */
+const char *myisam_recover_options_str="OFF";
+const char *myisam_stats_method_str="nulls_unequal";
+
+/* name of reference on left espression in rewritten IN subquery */
+const char *in_left_expr_name= "<left expr>";
+/* name of additional condition */
+const char *in_additional_cond= "<IN COND>";
+const char *in_having_cond= "<IN HAVING>";
+
+my_decimal decimal_zero;
+/* classes for comparation parsing/processing */
+Eq_creator eq_creator;
+Ne_creator ne_creator;
+Gt_creator gt_creator;
+Lt_creator lt_creator;
+Ge_creator ge_creator;
+Le_creator le_creator;
+
+
+FILE *bootstrap_file;
+int bootstrap_error;
+FILE *stderror_file=0;
+
+I_List<i_string_pair> replicate_rewrite_db;
+I_List<i_string> replicate_do_db, replicate_ignore_db;
+// allow the user to tell us which db to replicate and which to ignore
+I_List<i_string> binlog_do_db, binlog_ignore_db;
+I_List<THD> threads;
+I_List<NAMED_LIST> key_caches;
+
+struct system_variables global_system_variables;
+struct system_variables max_system_variables;
+struct system_status_var global_status_var;
+
+MY_TMPDIR mysql_tmpdir_list;
+MY_BITMAP temp_pool;
+
+CHARSET_INFO *system_charset_info, *files_charset_info ;
+CHARSET_INFO *national_charset_info, *table_alias_charset;
+CHARSET_INFO *character_set_filesystem;
+
+MY_LOCALE *my_default_lc_time_names;
+
+SHOW_COMP_OPTION have_isam;
+SHOW_COMP_OPTION have_raid, have_ssl, have_symlink, have_query_cache;
+SHOW_COMP_OPTION have_geometry, have_rtree_keys, have_dlopen;
+SHOW_COMP_OPTION have_crypt, have_compress;
+
+/* Thread specific variables */
+
+pthread_key(MEM_ROOT**,THR_MALLOC);
+pthread_key(THD*, THR_THD);
+pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
+		LOCK_mapped_file, LOCK_status, LOCK_global_read_lock,
+		LOCK_error_log, LOCK_uuid_generator,
+		LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
+		LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
+	        LOCK_global_system_variables,
+		LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
+pthread_mutex_t LOCK_global_user_stats;
+pthread_mutex_t LOCK_global_table_stats;
+pthread_mutex_t LOCK_global_index_stats;
+/*
+  The below lock protects access to two global server variables:
+  max_prepared_stmt_count and prepared_stmt_count. These variables
+  set the limit and hold the current total number of prepared statements
+  in the server, respectively. As PREPARE/DEALLOCATE rate in a loaded
+  server may be fairly high, we need a dedicated lock.
+*/
+pthread_mutex_t LOCK_prepared_stmt_count;
+#ifdef HAVE_OPENSSL
+pthread_mutex_t LOCK_des_key_file;
+#endif
+rw_lock_t	LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+pthread_cond_t COND_refresh,COND_thread_count, COND_global_read_lock;
+pthread_t signal_thread;
+pthread_attr_t connection_attrib;
+
+File_parser_dummy_hook file_parser_dummy_hook;
+
+/* replication parameters, if master_host is not NULL, we are a slave */
+uint master_port= MYSQL_PORT, master_connect_retry = 60;
+uint report_port= MYSQL_PORT;
+ulong master_retry_count=0;
+char *master_user, *master_password, *master_host, *master_info_file;
+char *relay_log_info_file, *report_user, *report_password, *report_host;
+char *opt_relay_logname = 0, *opt_relaylog_index_name=0;
+my_bool master_ssl;
+char *master_ssl_key, *master_ssl_cert;
+char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher;
+
+/* Static variables */
+
+static bool kill_in_progress, segfaulted;
+static my_bool opt_do_pstack, opt_bootstrap, opt_myisam_log;
+static int cleanup_done;
+static ulong opt_specialflag, opt_myisam_block_size;
+static char *opt_logname, *opt_update_logname, *opt_binlog_index_name;
+static char *opt_slow_logname, *opt_tc_heuristic_recover;
+static char *mysql_home_ptr, *pidfile_name_ptr;
+static char **defaults_argv;
+static char *opt_bin_logname;
+
+static my_socket unix_sock,ip_sock;
+struct rand_struct sql_rand; // used by sql_class.cc:THD::THD()
+
+/* OS specific variables */
+
+#ifdef __WIN__
+#undef	 getpid
+#include <process.h>
+
+static pthread_cond_t COND_handler_count;
+static uint handler_count;
+static bool start_mode=0, use_opt_args;
+static int opt_argc;
+static char **opt_argv;
+
+#if !defined(EMBEDDED_LIBRARY)
+static HANDLE hEventShutdown;
+static char shutdown_event_name[40];
+#include "nt_servc.h"
+static	 NTService  Service;	      // Service object for WinNT
+#endif /* EMBEDDED_LIBRARY */
+#endif /* __WIN__ */
+
+#ifdef __NT__
+static char pipe_name[512];
+static SECURITY_ATTRIBUTES saPipeSecurity;
+static SECURITY_DESCRIPTOR sdPipeDescriptor;
+static HANDLE hPipe = INVALID_HANDLE_VALUE;
+#endif
+
+#ifdef OS2
+pthread_cond_t eventShutdown;
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+bool mysqld_embedded=0;
+#else
+bool mysqld_embedded=1;
+#endif
+
+#ifndef DBUG_OFF
+static const char* default_dbug_option;
+#endif
+#ifdef HAVE_LIBWRAP
+const char *libwrapName= NULL;
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+#ifdef HAVE_QUERY_CACHE
+static ulong query_cache_limit= 0;
+ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE;
+Query_cache query_cache;
+#endif
+#ifdef HAVE_SMEM
+char *shared_memory_base_name= default_shared_memory_base_name;
+my_bool opt_enable_shared_memory;
+HANDLE smem_event_connect_request= 0;
+#endif
+
+#define SSL_VARS_NOT_STATIC
+#include "sslopt-vars.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/crypto.h>
+#ifndef HAVE_YASSL
+typedef struct CRYPTO_dynlock_value
+{
+  rw_lock_t lock;
+} openssl_lock_t;
+
+static openssl_lock_t *openssl_stdlocks;
+static openssl_lock_t *openssl_dynlock_create(const char *, int);
+static void openssl_dynlock_destroy(openssl_lock_t *, const char *, int);
+static void openssl_lock_function(int, int, const char *, int);
+static void openssl_lock(int, openssl_lock_t *, const char *, int);
+static unsigned long openssl_id_function();
+#endif
+char *des_key_file;
+struct st_VioSSLFd *ssl_acceptor_fd;
+#endif /* HAVE_OPENSSL */
+
+
+/* Function declarations */
+
+pthread_handler_t signal_hand(void *arg);
+static void mysql_init_variables(void);
+static void get_options(int argc,char **argv);
+static void set_server_version(void);
+static int init_thread_environment();
+static char *get_relative_path(const char *path);
+static void fix_paths(void);
+pthread_handler_t handle_connections_sockets(void *arg);
+pthread_handler_t kill_server_thread(void *arg);
+static void bootstrap(FILE *file);
+static bool read_init_file(char *file_name);
+#ifdef __NT__
+pthread_handler_t handle_connections_namedpipes(void *arg);
+#endif
+#ifdef HAVE_SMEM
+pthread_handler_t handle_connections_shared_memory(void *arg);
+#endif
+pthread_handler_t handle_slave(void *arg);
+static ulong find_bit_type(const char *x, TYPELIB *bit_lib);
+static void clean_up(bool print_message);
+static int test_if_case_insensitive(const char *dir_name);
+
+#ifndef EMBEDDED_LIBRARY
+static void start_signal_handler(void);
+static void close_server_sock();
+static void clean_up_mutexes(void);
+static void wait_for_signal_thread_to_end(void);
+static void create_pid_file();
+#endif
+
+
+#ifndef EMBEDDED_LIBRARY
+/****************************************************************************
+** Code to end mysqld
+****************************************************************************/
+
+static void close_connections(void)
+{
+#ifdef EXTRA_DEBUG
+  int count=0;
+#endif
+  DBUG_ENTER("close_connections");
+
+  /* Clear thread cache */
+  kill_cached_threads++;
+  flush_thread_cache();
+
+  /* kill flush thread */
+  (void) pthread_mutex_lock(&LOCK_manager);
+  if (manager_thread_in_use)
+  {
+    DBUG_PRINT("quit",("killing manager thread: 0x%lx",manager_thread));
+   (void) pthread_cond_signal(&COND_manager);
+  }
+  (void) pthread_mutex_unlock(&LOCK_manager);
+
+  /* kill connection thread */
+#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
+  DBUG_PRINT("quit",("waiting for select thread: 0x%lx",select_thread));
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+
+  while (select_thread_in_use)
+  {
+    struct timespec abstime;
+    int error;
+    LINT_INIT(error);
+    DBUG_PRINT("info",("Waiting for select thread"));
+
+#ifndef DONT_USE_THR_ALARM
+    if (pthread_kill(select_thread, thr_client_alarm))
+      break;					// allready dead
+#endif
+    set_timespec(abstime, 2);
+    for (uint tmp=0 ; tmp < 10 && select_thread_in_use; tmp++)
+    {
+      error=pthread_cond_timedwait(&COND_thread_count,&LOCK_thread_count,
+				   &abstime);
+      if (error != EINTR)
+	break;
+    }
+#ifdef EXTRA_DEBUG
+    if (error != 0 && !count++)
+      sql_print_error("Got error %d from pthread_cond_timedwait",error);
+#endif
+    close_server_sock();
+  }
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+#endif /* __WIN__ */
+
+
+  /* Abort listening to new connections */
+  DBUG_PRINT("quit",("Closing sockets"));
+  if (!opt_disable_networking )
+  {
+    if (ip_sock != INVALID_SOCKET)
+    {
+      (void) shutdown(ip_sock, SHUT_RDWR);
+      (void) closesocket(ip_sock);
+      ip_sock= INVALID_SOCKET;
+    }
+  }
+#ifdef __NT__
+  if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe)
+  {
+    HANDLE temp;
+    DBUG_PRINT("quit", ("Closing named pipes") );
+
+    /* Create connection to the handle named pipe handler to break the loop */
+    if ((temp = CreateFile(pipe_name,
+			   GENERIC_READ | GENERIC_WRITE,
+			   0,
+			   NULL,
+			   OPEN_EXISTING,
+			   0,
+			   NULL )) != INVALID_HANDLE_VALUE)
+    {
+      WaitNamedPipe(pipe_name, 1000);
+      DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
+      SetNamedPipeHandleState(temp, &dwMode, NULL, NULL);
+      CancelIo(temp);
+      DisconnectNamedPipe(temp);
+      CloseHandle(temp);
+    }
+  }
+#endif
+#ifdef HAVE_SYS_UN_H
+  if (unix_sock != INVALID_SOCKET)
+  {
+    (void) shutdown(unix_sock, SHUT_RDWR);
+    (void) closesocket(unix_sock);
+    (void) unlink(mysqld_unix_port);
+    unix_sock= INVALID_SOCKET;
+  }
+#endif
+  end_thr_alarm(0);			 // Abort old alarms.
+
+  /*
+    First signal all threads that it's time to die
+    This will give the threads some time to gracefully abort their
+    statements and inform their clients that the server is about to die.
+  */
+
+  THD *tmp;
+  (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+
+  I_List_iterator<THD> it(threads);
+  while ((tmp=it++))
+  {
+    DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
+		       tmp->thread_id));
+    /* We skip slave threads on this first loop through. */
+    if (tmp->slave_thread)
+      continue;
+
+    tmp->killed= THD::KILL_CONNECTION;
+    if (tmp->mysys_var)
+    {
+      tmp->mysys_var->abort=1;
+      pthread_mutex_lock(&tmp->mysys_var->mutex);
+      if (tmp->mysys_var->current_cond)
+      {
+	pthread_mutex_lock(tmp->mysys_var->current_mutex);
+	pthread_cond_broadcast(tmp->mysys_var->current_cond);
+	pthread_mutex_unlock(tmp->mysys_var->current_mutex);
+      }
+      pthread_mutex_unlock(&tmp->mysys_var->mutex);
+    }
+  }
+  (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
+
+  end_slave();
+
+  if (thread_count)
+    sleep(2);					// Give threads time to die
+
+  /*
+    Force remaining threads to die by closing the connection to the client
+    This will ensure that threads that are waiting for a command from the
+    client on a blocking read call are aborted.
+  */
+
+  for (;;)
+  {
+    DBUG_PRINT("quit",("Locking LOCK_thread_count"));
+    (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+    if (!(tmp=threads.get()))
+    {
+      DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
+      (void) pthread_mutex_unlock(&LOCK_thread_count);
+      break;
+    }
+#ifndef __bsdi__				// Bug in BSDI kernel
+    if (tmp->vio_ok())
+    {
+      if (global_system_variables.log_warnings)
+        sql_print_warning(ER(ER_FORCING_CLOSE),my_progname,
+                          tmp->thread_id,
+                          (tmp->main_security_ctx.user ?
+                           tmp->main_security_ctx.user : ""));
+      close_connection(tmp,0,0);
+    }
+#endif
+    DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
+    (void) pthread_mutex_unlock(&LOCK_thread_count);
+  }
+  /* All threads has now been aborted */
+  DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  while (thread_count)
+  {
+    (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+    DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
+  }
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+  DBUG_PRINT("quit",("close_connections thread"));
+  DBUG_VOID_RETURN;
+}
+
+
+static void close_server_sock()
+{
+#ifdef HAVE_CLOSE_SERVER_SOCK
+  DBUG_ENTER("close_server_sock");
+  my_socket tmp_sock;
+  tmp_sock=ip_sock;
+  if (tmp_sock != INVALID_SOCKET)
+  {
+    ip_sock=INVALID_SOCKET;
+    DBUG_PRINT("info",("calling shutdown on TCP/IP socket"));
+    VOID(shutdown(tmp_sock, SHUT_RDWR));
+#if defined(__NETWARE__)
+    /*
+      The following code is disabled for normal systems as it causes MySQL
+      to hang on AIX 4.3 during shutdown
+    */
+    DBUG_PRINT("info",("calling closesocket on TCP/IP socket"));
+    VOID(closesocket(tmp_sock));
+#endif
+  }
+  tmp_sock=unix_sock;
+  if (tmp_sock != INVALID_SOCKET)
+  {
+    unix_sock=INVALID_SOCKET;
+    DBUG_PRINT("info",("calling shutdown on unix socket"));
+    VOID(shutdown(tmp_sock, SHUT_RDWR));
+#if defined(__NETWARE__)
+    /*
+      The following code is disabled for normal systems as it may cause MySQL
+      to hang on AIX 4.3 during shutdown
+    */
+    DBUG_PRINT("info",("calling closesocket on unix/IP socket"));
+    VOID(closesocket(tmp_sock));
+#endif
+    VOID(unlink(mysqld_unix_port));
+  }
+  DBUG_VOID_RETURN;
+#endif
+}
+
+#endif /*EMBEDDED_LIBRARY*/
+
+
+void kill_mysql(void)
+{
+  DBUG_ENTER("kill_mysql");
+
+#if defined(SIGNALS_DONT_BREAK_READ) && !defined(EMBEDDED_LIBRARY)
+  abort_loop=1;					// Break connection loops
+  close_server_sock();				// Force accept to wake up
+#endif
+
+#if defined(__WIN__)
+#if !defined(EMBEDDED_LIBRARY)
+  {
+    if (!SetEvent(hEventShutdown))
+    {
+      DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError()));
+    }
+    /*
+      or:
+      HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown");
+      SetEvent(hEventShutdown);
+      CloseHandle(hEvent);
+    */
+  }
+#endif
+#elif defined(OS2)
+  pthread_cond_signal(&eventShutdown);		// post semaphore
+#elif defined(HAVE_PTHREAD_KILL)
+  if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
+  {
+    DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */
+  }
+#elif !defined(SIGNALS_DONT_BREAK_READ)
+  kill(current_pid, MYSQL_KILL_SIGNAL);
+#endif
+  DBUG_PRINT("quit",("After pthread_kill"));
+  shutdown_in_progress=1;			// Safety if kill didn't work
+#ifdef SIGNALS_DONT_BREAK_READ
+  if (!kill_in_progress)
+  {
+    pthread_t tmp;
+    abort_loop=1;
+    if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
+			   (void*) 0))
+      sql_print_error("Can't create thread to kill server");
+  }
+#endif
+  DBUG_VOID_RETURN;
+}
+
+/*
+  Force server down. Kill all connections and threads and exit
+
+  SYNOPSIS
+  kill_server
+
+  sig_ptr       Signal number that caused kill_server to be called.
+
+  NOTE!
+    A signal number of 0 mean that the function was not called
+    from a signal handler and there is thus no signal to block
+    or stop, we just want to kill the server.
+
+*/
+
+#if defined(OS2) || defined(__NETWARE__)
+extern "C" void kill_server(int sig_ptr)
+#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN
+#elif !defined(__WIN__)
+static void *kill_server(void *sig_ptr)
+#define RETURN_FROM_KILL_SERVER DBUG_RETURN(0)
+#else
+static void __cdecl kill_server(int sig_ptr)
+#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN
+#endif
+{
+  DBUG_ENTER("kill_server");
+#ifndef EMBEDDED_LIBRARY
+  int sig=(int) (long) sig_ptr;			// This is passed a int
+  // if there is a signal during the kill in progress, ignore the other
+  if (kill_in_progress)				// Safety
+    RETURN_FROM_KILL_SERVER;
+  kill_in_progress=TRUE;
+  abort_loop=1;					// This should be set
+  if (sig != 0) // 0 is not a valid signal number
+    my_sigset(sig, SIG_IGN);                    /* purify inspected */
+  if (sig == MYSQL_KILL_SIGNAL || sig == 0)
+    sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname);
+  else
+    sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
+
+#if defined(HAVE_SMEM) && defined(__WIN__)
+  /*
+   Send event to smem_event_connect_request for aborting
+   */
+  if (!SetEvent(smem_event_connect_request))
+  {
+	  DBUG_PRINT("error",
+		("Got error: %ld from SetEvent of smem_event_connect_request",
+		 GetLastError()));
+  }
+#endif
+
+#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2))
+  my_thread_init();				// If this is a new thread
+#endif
+  close_connections();
+  if (sig != MYSQL_KILL_SIGNAL &&
+      sig != 0)
+    unireg_abort(1);				/* purecov: inspected */
+  else
+    unireg_end();
+
+#ifdef __NETWARE__
+  if (!event_flag)
+    pthread_join(select_thread, NULL);		// wait for main thread
+#endif /* __NETWARE__ */
+
+#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2))
+  my_thread_end();
+#endif
+
+  pthread_exit(0);				/* purecov: deadcode */
+
+#endif /* EMBEDDED_LIBRARY */
+  RETURN_FROM_KILL_SERVER;
+}
+
+
+#if defined(USE_ONE_SIGNAL_HAND) || (defined(__NETWARE__) && defined(SIGNALS_DONT_BREAK_READ))
+pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
+{
+  my_thread_init();				// Initialize new thread
+  kill_server(0);
+  my_thread_end();				// Normally never reached
+  return 0;
+}
+#endif
+
+extern "C" sig_handler print_signal_warning(int sig)
+{
+  if (global_system_variables.log_warnings)
+    sql_print_warning("Got signal %d from thread %ld",
+                      sig, my_thread_id());
+#ifdef DONT_REMEMBER_SIGNAL
+  my_sigset(sig,print_signal_warning);		/* int. thread system calls */
+#endif
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+  if (sig == SIGALRM)
+    alarm(2);					/* reschedule alarm */
+#endif
+}
+
+/*
+  cleanup all memory and end program nicely
+
+  SYNOPSIS
+    unireg_end()
+
+  NOTES
+    This function never returns.
+
+    If SIGNALS_DONT_BREAK_READ is defined, this function is called
+    by the main thread. To get MySQL to shut down nicely in this case
+    (Mac OS X) we have to call exit() instead if pthread_exit().
+*/
+
+#ifndef EMBEDDED_LIBRARY
+void unireg_end(void)
+{
+  clean_up(1);
+  my_thread_end();
+#if defined(SIGNALS_DONT_BREAK_READ) && !defined(__NETWARE__)
+  exit(0);
+#else
+  pthread_exit(0);				// Exit is in main thread
+#endif
+}
+
+extern "C" void unireg_abort(int exit_code)
+{
+  DBUG_ENTER("unireg_abort");
+  if (exit_code)
+    sql_print_error("Aborting\n");
+  clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
+  DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
+  wait_for_signal_thread_to_end();
+  clean_up_mutexes();
+  my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
+  exit(exit_code); /* purecov: inspected */
+}
+#endif
+
+
+void clean_up(bool print_message)
+{
+  DBUG_PRINT("exit",("clean_up"));
+  if (cleanup_done++)
+    return; /* purecov: inspected */
+
+  mysql_log.cleanup();
+  mysql_slow_log.cleanup();
+  mysql_bin_log.cleanup();
+
+#ifdef HAVE_REPLICATION
+  if (use_slave_mask)
+    bitmap_free(&slave_error_mask);
+#endif
+  my_tz_free();
+  my_dbopt_free();
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  acl_free(1);
+  grant_free();
+#endif
+  query_cache_destroy();
+  table_cache_free();
+  hostname_cache_free();
+  item_user_lock_free();
+  lex_free();				/* Free some memory */
+  set_var_free();
+  free_charsets();
+#ifdef HAVE_DLOPEN
+  if (!opt_noacl)
+    udf_free();
+#endif
+  (void) ha_panic(HA_PANIC_CLOSE);	/* close all tables and logs */
+  if (tc_log)
+    tc_log->close();
+  xid_cache_free();
+  delete_elements(&key_caches, (void (*)(const char*, gptr)) free_key_cache);
+  multi_keycache_free();
+  end_thr_alarm(1);			/* Free allocated memory */
+#ifdef USE_RAID
+  end_raid();
+#endif
+  my_free_open_file_info();
+  my_free((char*) global_system_variables.date_format,
+	  MYF(MY_ALLOW_ZERO_PTR));
+  my_free((char*) global_system_variables.time_format,
+	  MYF(MY_ALLOW_ZERO_PTR));
+  my_free((char*) global_system_variables.datetime_format,
+	  MYF(MY_ALLOW_ZERO_PTR));
+  if (defaults_argv)
+    free_defaults(defaults_argv);
+  my_free(sys_init_connect.value, MYF(MY_ALLOW_ZERO_PTR));
+  my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR));
+  free_tmpdir(&mysql_tmpdir_list);
+#ifdef HAVE_REPLICATION
+  my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR));
+#endif
+  x_free(opt_bin_logname);
+  x_free(opt_relay_logname);
+  x_free(opt_secure_file_priv);
+  bitmap_free(&temp_pool);
+  free_max_user_conn();
+  free_global_user_stats();
+  free_global_table_stats();
+  free_global_index_stats();
+#ifdef HAVE_REPLICATION
+  end_slave_list();
+  free_list(&replicate_do_db);
+  free_list(&replicate_ignore_db);
+  free_list(&binlog_do_db);
+  free_list(&binlog_ignore_db);
+  free_list(&replicate_rewrite_db);
+#endif
+#ifdef HAVE_OPENSSL
+  if (ssl_acceptor_fd)
+  {
+    SSL_CTX_free(ssl_acceptor_fd->ssl_context);
+    my_free((gptr) ssl_acceptor_fd, MYF(0));
+  }
+#endif /* HAVE_OPENSSL */
+  vio_end();
+
+#ifdef USE_REGEX
+  my_regex_end();
+#endif
+
+  if (print_message && errmesg)
+    sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname);
+#if !defined(EMBEDDED_LIBRARY)
+  if (!opt_bootstrap)
+    (void) my_delete(pidfile_name,MYF(0));	// This may not always exist
+#endif
+  finish_client_errs();
+  my_free((gptr) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST),
+          MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+  DBUG_PRINT("quit", ("Error messages freed"));
+  /* Tell main we are ready */
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  DBUG_PRINT("quit", ("got thread count lock"));
+  ready_to_exit=1;
+  /* do the broadcast inside the lock to ensure that my_end() is not called */
+  (void) pthread_cond_broadcast(&COND_thread_count);
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+  /*
+    The following lines may never be executed as the main thread may have
+    killed us
+  */
+  DBUG_PRINT("quit", ("done with cleanup"));
+} /* clean_up */
+
+
+#ifndef EMBEDDED_LIBRARY
+
+/*
+  This is mainly needed when running with purify, but it's still nice to
+  know that all child threads have died when mysqld exits
+*/
+
+static void wait_for_signal_thread_to_end()
+{
+#ifndef __NETWARE__
+  uint i;
+  /*
+    Wait up to 10 seconds for signal thread to die. We use this mainly to
+    avoid getting warnings that my_thread_end has not been called
+  */
+  for (i= 0 ; i < 100 && signal_thread_in_use; i++)
+  {
+    if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL) != ESRCH)
+      break;
+    my_sleep(100);				// Give it time to die
+  }
+#endif
+}
+
+
+static void clean_up_mutexes()
+{
+  (void) pthread_mutex_destroy(&LOCK_mysql_create_db);
+  (void) pthread_mutex_destroy(&LOCK_Acl);
+  (void) rwlock_destroy(&LOCK_grant);
+  (void) pthread_mutex_destroy(&LOCK_open);
+  (void) pthread_mutex_destroy(&LOCK_thread_count);
+  (void) pthread_mutex_destroy(&LOCK_mapped_file);
+  (void) pthread_mutex_destroy(&LOCK_status);
+  (void) pthread_mutex_destroy(&LOCK_error_log);
+  (void) pthread_mutex_destroy(&LOCK_delayed_insert);
+  (void) pthread_mutex_destroy(&LOCK_delayed_status);
+  (void) pthread_mutex_destroy(&LOCK_delayed_create);
+  (void) pthread_mutex_destroy(&LOCK_manager);
+  (void) pthread_mutex_destroy(&LOCK_crypt);
+  (void) pthread_mutex_destroy(&LOCK_bytes_sent);
+  (void) pthread_mutex_destroy(&LOCK_bytes_received);
+  (void) pthread_mutex_destroy(&LOCK_user_conn);
+#ifdef HAVE_OPENSSL
+  (void) pthread_mutex_destroy(&LOCK_des_key_file);
+#ifndef HAVE_YASSL
+  for (int i= 0; i < CRYPTO_num_locks(); ++i)
+    (void) rwlock_destroy(&openssl_stdlocks[i].lock);
+  OPENSSL_free(openssl_stdlocks);
+#endif
+#endif
+#ifdef HAVE_REPLICATION
+  (void) pthread_mutex_destroy(&LOCK_rpl_status);
+  (void) pthread_cond_destroy(&COND_rpl_status);
+#endif
+  (void) pthread_mutex_destroy(&LOCK_active_mi);
+  (void) rwlock_destroy(&LOCK_sys_init_connect);
+  (void) rwlock_destroy(&LOCK_sys_init_slave);
+  (void) pthread_mutex_destroy(&LOCK_global_system_variables);
+  (void) pthread_mutex_destroy(&LOCK_global_read_lock);
+  (void) pthread_mutex_destroy(&LOCK_uuid_generator);
+  (void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
+  (void) pthread_cond_destroy(&COND_thread_count);
+  (void) pthread_cond_destroy(&COND_refresh);
+  (void) pthread_cond_destroy(&COND_global_read_lock);
+  (void) pthread_cond_destroy(&COND_thread_cache);
+  (void) pthread_cond_destroy(&COND_flush_thread_cache);
+  (void) pthread_cond_destroy(&COND_manager);
+  (void) pthread_mutex_destroy(&LOCK_global_user_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_table_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_index_stats);
+}
+
+#endif /*EMBEDDED_LIBRARY*/
+
+
+/****************************************************************************
+** Init IP and UNIX socket
+****************************************************************************/
+
+static void set_ports()
+{
+  char	*env;
+  if (!mysqld_port && !opt_disable_networking)
+  {					// Get port if not from commandline
+    struct  servent *serv_ptr;
+    mysqld_port= MYSQL_PORT;
+
+    /*
+      if builder specifically requested a default port, use that
+      (even if it coincides with our factory default).
+      only if they didn't do we check /etc/services (and, failing
+      on that, fall back to the factory default of 3306).
+      either default can be overridden by the environment variable
+      MYSQL_TCP_PORT, which in turn can be overridden with command
+      line options.
+    */
+
+#if MYSQL_PORT_DEFAULT == 0
+    if ((serv_ptr= getservbyname("mysql", "tcp")))
+      mysqld_port= ntohs((u_short) serv_ptr->s_port); /* purecov: inspected */
+#endif
+    if ((env = getenv("MYSQL_TCP_PORT")))
+      mysqld_port= (uint) atoi(env);		/* purecov: inspected */
+  }
+  if (!mysqld_unix_port)
+  {
+#ifdef __WIN__
+    mysqld_unix_port= (char*) MYSQL_NAMEDPIPE;
+#else
+    mysqld_unix_port= (char*) MYSQL_UNIX_ADDR;
+#endif
+    if ((env = getenv("MYSQL_UNIX_PORT")))
+      mysqld_unix_port= env;			/* purecov: inspected */
+  }
+}
+
+#ifndef EMBEDDED_LIBRARY
+/* Change to run as another user if started with --user */
+
+static struct passwd *check_user(const char *user)
+{
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+  struct passwd *tmp_user_info;
+  uid_t user_id= geteuid();
+
+  // Don't bother if we aren't superuser
+  if (user_id)
+  {
+    if (user)
+    {
+      /* Don't give a warning, if real user is same as given with --user */
+      /* purecov: begin tested */
+      tmp_user_info= getpwnam(user);
+      if ((!tmp_user_info || user_id != tmp_user_info->pw_uid) &&
+	  global_system_variables.log_warnings)
+        sql_print_warning(
+                    "One can only use the --user switch if running as root\n");
+      /* purecov: end */    
+    }
+    return NULL;
+  }
+  if (!user)
+  {
+    if (!opt_bootstrap)
+    {
+      sql_print_error("Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n");
+      unireg_abort(1);
+    }
+    return NULL;
+  }
+  /* purecov: begin tested */
+  if (!strcmp(user,"root"))
+    return NULL;                        // Avoid problem with dynamic libraries
+
+  if (!(tmp_user_info= getpwnam(user)))
+  {
+    // Allow a numeric uid to be used
+    const char *pos;
+    for (pos= user; my_isdigit(mysqld_charset,*pos); pos++) ;
+    if (*pos)                                   // Not numeric id
+      goto err;
+    if (!(tmp_user_info= getpwuid(atoi(user))))
+      goto err;
+    else
+      return tmp_user_info;
+  }
+  else
+    return tmp_user_info;
+  /* purecov: end */    
+
+err:
+  sql_print_error("Fatal error: Can't change to run as user '%s' ;  Please check that the user exists!\n",user);
+  unireg_abort(1);
+
+#ifdef PR_SET_DUMPABLE
+  if (test_flags & TEST_CORE_ON_SIGNAL)
+  {
+    /* inform kernel that process is dumpable */
+    (void) prctl(PR_SET_DUMPABLE, 1);
+  }
+#endif
+
+#endif
+  return NULL;
+}
+
+static void set_user(const char *user, struct passwd *user_info_arg)
+{
+  /* purecov: begin tested */
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+  DBUG_ASSERT(user_info_arg != 0);
+#ifdef HAVE_INITGROUPS
+  /*
+    We can get a SIGSEGV when calling initgroups() on some systems when NSS
+    is configured to use LDAP and the server is statically linked.  We set
+    calling_initgroups as a flag to the SIGSEGV handler that is then used to
+    output a specific message to help the user resolve this problem.
+  */
+  calling_initgroups= TRUE;
+  initgroups((char*) user, user_info_arg->pw_gid);
+  calling_initgroups= FALSE;
+#endif
+  if (setgid(user_info_arg->pw_gid) == -1)
+  {
+    sql_perror("setgid");
+    unireg_abort(1);
+  }
+  if (setuid(user_info_arg->pw_uid) == -1)
+  {
+    sql_perror("setuid");
+    unireg_abort(1);
+  }
+#endif
+  /* purecov: end */    
+}
+
+
+static void set_effective_user(struct passwd *user_info_arg)
+{
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+  DBUG_ASSERT(user_info_arg != 0);
+  if (setregid((gid_t)-1, user_info_arg->pw_gid) == -1)
+  {
+    sql_perror("setregid");
+    unireg_abort(1);
+  }
+  if (setreuid((uid_t)-1, user_info_arg->pw_uid) == -1)
+  {
+    sql_perror("setreuid");
+    unireg_abort(1);
+  }
+#endif
+}
+
+
+/* Change root user if started with  --chroot */
+
+static void set_root(const char *path)
+{
+#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
+  if (chroot(path) == -1)
+  {
+    sql_perror("chroot");
+    unireg_abort(1);
+  }
+  my_setwd("/", MYF(0));
+#endif
+}
+
+static void network_init(void)
+{
+  struct sockaddr_in	IPaddr;
+#ifdef HAVE_SYS_UN_H
+  struct sockaddr_un	UNIXaddr;
+#endif
+  int	arg=1;
+  int   ret;
+  uint  waited;
+  uint  this_wait;
+  uint  retry;
+  DBUG_ENTER("network_init");
+  LINT_INIT(ret);
+
+  set_ports();
+
+  if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap)
+  {
+    DBUG_PRINT("general",("IP Socket is %d",mysqld_port));
+    ip_sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (ip_sock == INVALID_SOCKET)
+    {
+      DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
+      sql_perror(ER(ER_IPSOCK_ERROR));		/* purecov: tested */
+      unireg_abort(1);				/* purecov: tested */
+    }
+    bzero((char*) &IPaddr, sizeof(IPaddr));
+    IPaddr.sin_family = AF_INET;
+    IPaddr.sin_addr.s_addr = my_bind_addr;
+    IPaddr.sin_port = (unsigned short) htons((unsigned short) mysqld_port);
+
+#ifndef __WIN__
+    /*
+      We should not use SO_REUSEADDR on windows as this would enable a
+      user to open two mysqld servers with the same TCP/IP port.
+    */
+    (void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
+#endif /* __WIN__ */
+    /*
+      Sometimes the port is not released fast enough when stopping and
+      restarting the server. This happens quite often with the test suite
+      on busy Linux systems. Retry to bind the address at these intervals:
+      Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
+      Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
+      Limit the sequence by mysqld_port_timeout (set --port-open-timeout=#).
+    */
+    for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
+    {
+      if (((ret= bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr),
+                      sizeof(IPaddr))) >= 0) ||
+          (socket_errno != SOCKET_EADDRINUSE) ||
+          (waited >= mysqld_port_timeout))
+        break;
+      sql_print_information("Retrying bind on TCP/IP port %u", mysqld_port);
+      this_wait= retry * retry / 3 + 1;
+      sleep(this_wait);
+    }
+    if (ret < 0)
+    {
+      DBUG_PRINT("error",("Got error: %d from bind",socket_errno));
+      sql_perror("Can't start server: Bind on TCP/IP port");
+      sql_print_error("Do you already have another mysqld server running on port: %d ?",mysqld_port);
+      unireg_abort(1);
+    }
+    if (listen(ip_sock,(int) back_log) < 0)
+    {
+      sql_perror("Can't start server: listen() on TCP/IP port");
+      sql_print_error("listen() on TCP/IP failed with error %d",
+		      socket_errno);
+      unireg_abort(1);
+    }
+  }
+
+#ifdef __NT__
+  /* create named pipe */
+  if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
+      opt_enable_named_pipe)
+  {
+
+    pipe_name[sizeof(pipe_name)-1]= 0;		/* Safety if too long string */
+    strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\",
+	     mysqld_unix_port, NullS);
+    bzero((char*) &saPipeSecurity, sizeof(saPipeSecurity));
+    bzero((char*) &sdPipeDescriptor, sizeof(sdPipeDescriptor));
+    if (!InitializeSecurityDescriptor(&sdPipeDescriptor,
+				      SECURITY_DESCRIPTOR_REVISION))
+    {
+      sql_perror("Can't start server : Initialize security descriptor");
+      unireg_abort(1);
+    }
+    if (!SetSecurityDescriptorDacl(&sdPipeDescriptor, TRUE, NULL, FALSE))
+    {
+      sql_perror("Can't start server : Set security descriptor");
+      unireg_abort(1);
+    }
+    saPipeSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor;
+    saPipeSecurity.bInheritHandle = FALSE;
+    if ((hPipe= CreateNamedPipe(pipe_name,
+				PIPE_ACCESS_DUPLEX,
+				PIPE_TYPE_BYTE |
+				PIPE_READMODE_BYTE |
+				PIPE_WAIT,
+				PIPE_UNLIMITED_INSTANCES,
+				(int) global_system_variables.net_buffer_length,
+				(int) global_system_variables.net_buffer_length,
+				NMPWAIT_USE_DEFAULT_WAIT,
+				&saPipeSecurity)) == INVALID_HANDLE_VALUE)
+      {
+	LPVOID lpMsgBuf;
+	int error=GetLastError();
+	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		      FORMAT_MESSAGE_FROM_SYSTEM,
+		      NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		      (LPTSTR) &lpMsgBuf, 0, NULL );
+	sql_perror((char *)lpMsgBuf);
+	LocalFree(lpMsgBuf);
+	unireg_abort(1);
+      }
+  }
+#endif
+
+#if defined(HAVE_SYS_UN_H)
+  /*
+  ** Create the UNIX socket
+  */
+  if (mysqld_unix_port[0] && !opt_bootstrap)
+  {
+    DBUG_PRINT("general",("UNIX Socket is %s",mysqld_unix_port));
+
+    if (strlen(mysqld_unix_port) > (sizeof(UNIXaddr.sun_path) - 1))
+    {
+      sql_print_error("The socket file path is too long (> %u): %s",
+                      (uint) sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port);
+      unireg_abort(1);
+    }
+    if ((unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+    {
+      sql_perror("Can't start server : UNIX Socket "); /* purecov: inspected */
+      unireg_abort(1);				/* purecov: inspected */
+    }
+    bzero((char*) &UNIXaddr, sizeof(UNIXaddr));
+    UNIXaddr.sun_family = AF_UNIX;
+    strmov(UNIXaddr.sun_path, mysqld_unix_port);
+    (void) unlink(mysqld_unix_port);
+    (void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
+		      sizeof(arg));
+    umask(0);
+    if (bind(unix_sock, my_reinterpret_cast(struct sockaddr *) (&UNIXaddr),
+	     sizeof(UNIXaddr)) < 0)
+    {
+      sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */
+      sql_print_error("Do you already have another mysqld server running on socket: %s ?",mysqld_unix_port);
+      unireg_abort(1);					/* purecov: tested */
+    }
+    umask(((~my_umask) & 0666));
+#if defined(S_IFSOCK) && defined(SECURE_SOCKETS)
+    (void) chmod(mysqld_unix_port,S_IFSOCK);	/* Fix solaris 2.6 bug */
+#endif
+    if (listen(unix_sock,(int) back_log) < 0)
+      sql_print_warning("listen() on Unix socket failed with error %d",
+		      socket_errno);
+  }
+#endif
+  DBUG_PRINT("info",("server started"));
+  DBUG_VOID_RETURN;
+}
+
+#endif /*!EMBEDDED_LIBRARY*/
+
+
+#ifndef EMBEDDED_LIBRARY
+/*
+  Close a connection
+
+  SYNOPSIS
+    close_connection()
+    thd		Thread handle
+    errcode	Error code to print to console
+    lock	1 if we have have to lock LOCK_thread_count
+
+  NOTES
+    For the connection that is doing shutdown, this is called twice
+*/
+
+void close_connection(THD *thd, uint errcode, bool lock)
+{
+  st_vio *vio;
+  DBUG_ENTER("close_connection");
+  DBUG_PRINT("enter",("fd: %s  error: '%s'",
+		      thd->net.vio ? vio_description(thd->net.vio) :
+		      "(not connected)",
+		      errcode ? ER(errcode) : ""));
+  if (lock)
+    (void) pthread_mutex_lock(&LOCK_thread_count);
+  thd->killed= THD::KILL_CONNECTION;
+  if ((vio= thd->net.vio) != 0)
+  {
+    if (errcode)
+      net_send_error(thd, errcode, ER(errcode)); /* purecov: inspected */
+    vio_close(vio);			/* vio is freed in delete thd */
+  }
+  if (lock)
+    (void) pthread_mutex_unlock(&LOCK_thread_count);
+  DBUG_VOID_RETURN;
+}
+#endif /* EMBEDDED_LIBRARY */
+
+
+	/* Called when a thread is aborted */
+	/* ARGSUSED */
+
+extern "C" sig_handler end_thread_signal(int sig __attribute__((unused)))
+{
+  THD *thd=current_thd;
+  DBUG_ENTER("end_thread_signal");
+  if (thd && ! thd->bootstrap)
+  {
+    statistic_increment(killed_threads, &LOCK_status);
+    end_thread(thd,0);
+  }
+  DBUG_VOID_RETURN;				/* purecov: deadcode */
+}
+
+
+void end_thread(THD *thd, bool put_in_cache)
+{
+  DBUG_ENTER("end_thread");
+  thd->cleanup();
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  thread_count--;
+  delete thd;
+
+  if (put_in_cache && cached_thread_count < thread_cache_size &&
+      ! abort_loop && !kill_cached_threads)
+  {
+    /* Don't kill the thread, just put it in cache for reuse */
+    DBUG_PRINT("info", ("Adding thread to cache"));
+    cached_thread_count++;
+    while (!abort_loop && ! wake_thread && ! kill_cached_threads)
+      (void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);
+    cached_thread_count--;
+    if (kill_cached_threads)
+      pthread_cond_signal(&COND_flush_thread_cache);
+    if (wake_thread)
+    {
+      wake_thread--;
+      thd=thread_cache.get();
+      thd->real_id=pthread_self();
+      thd->thread_stack= (char*) &thd;          // For store_globals
+      (void) thd->store_globals();
+      /*
+        THD::mysys_var::abort is associated with physical thread rather
+        than with THD object. So we need to reset this flag before using
+        this thread for handling of new THD object/connection.
+      */
+      thd->mysys_var->abort= 0;
+      thd->thr_create_time= time(NULL);
+      threads.append(thd);
+      pthread_mutex_unlock(&LOCK_thread_count);
+      DBUG_VOID_RETURN;
+    }
+  }
+
+  /* Tell main we are ready */
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+  /* It's safe to broadcast outside a lock (COND... is not deleted here) */
+  DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
+  (void) pthread_cond_broadcast(&COND_thread_count);
+#ifdef ONE_THREAD
+  if (!(test_flags & TEST_NO_THREADS))	// For debugging under Linux
+#endif
+  {
+    my_thread_end();
+    pthread_exit(0);
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+void flush_thread_cache()
+{
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  kill_cached_threads++;
+  while (cached_thread_count)
+  {
+    pthread_cond_broadcast(&COND_thread_cache);
+    pthread_cond_wait(&COND_flush_thread_cache,&LOCK_thread_count);
+  }
+  kill_cached_threads--;
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+}
+
+
+/*
+  Aborts a thread nicely. Commes here on SIGPIPE
+  TODO: One should have to fix that thr_alarm know about this
+  thread too.
+*/
+
+#ifdef THREAD_SPECIFIC_SIGPIPE
+extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
+{
+  THD *thd=current_thd;
+  DBUG_ENTER("abort_thread");
+  if (thd)
+    thd->killed= THD::KILL_CONNECTION;
+  DBUG_VOID_RETURN;
+}
+#endif
+
+/******************************************************************************
+  Setup a signal thread with handles all signals.
+  Because Linux doesn't support schemas use a mutex to check that
+  the signal thread is ready before continuing
+******************************************************************************/
+
+
+#if defined(__WIN__)
+
+
+/*
+  On Windows, we use native SetConsoleCtrlHandler for handle events like Ctrl-C
+  with graceful shutdown.
+  Also, we do not use signal(), but SetUnhandledExceptionFilter instead - as it
+  provides possibility to pass the exception to just-in-time debugger, collect
+  dumps and potentially also the exception and thread context used to output
+  callstack.
+*/
+
+static BOOL WINAPI console_event_handler( DWORD type ) 
+{
+  DBUG_ENTER("console_event_handler");
+  if(type == CTRL_C_EVENT)
+  {
+     /*
+       Do not shutdown before startup is finished and shutdown
+       thread is initialized. Otherwise there is a race condition 
+       between main thread doing initialization and CTRL-C thread doing
+       cleanup, which can result into crash.
+     */
+     if(hEventShutdown)
+       kill_mysql();
+     else
+       sql_print_warning("CTRL-C ignored during startup");
+     DBUG_RETURN(TRUE);
+  }
+  DBUG_RETURN(FALSE);
+}
+
+
+/*
+  In Visual Studio 2005 and later, default SIGABRT handler will overwrite
+  any unhandled exception filter set by the application  and will try to
+  call JIT debugger. This is not what we want, this we calling __debugbreak
+  to stop in debugger, if process is being debugged or to generate 
+  EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
+*/
+
+#if (_MSC_VER >= 1400)
+static void my_sigabrt_handler(int sig)
+{
+  __debugbreak();
+}
+#endif /*_MSC_VER >=1400 */
+
+void win_install_sigabrt_handler(void)
+{
+#if (_MSC_VER >=1400)
+  /*abort() should not override our exception filter*/
+  _set_abort_behavior(0,_CALL_REPORTFAULT);
+  signal(SIGABRT,my_sigabrt_handler);
+#endif /* _MSC_VER >=1400 */
+}
+
+#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
+#define DEBUGGER_ATTACH_TIMEOUT 120
+/*
+  Wait for debugger to attach and break into debugger. If debugger is not attached,
+  resume after timeout.
+*/
+static void wait_for_debugger(int timeout_sec)
+{
+   if(!IsDebuggerPresent())
+   {
+     int i;
+     printf("Waiting for debugger to attach, pid=%u\n",GetCurrentProcessId());
+     fflush(stdout);
+     for(i= 0; i < timeout_sec; i++)
+     {
+       Sleep(1000);
+       if(IsDebuggerPresent())
+       {
+         /* Break into debugger */
+         __debugbreak();
+         return;
+       }
+     }
+     printf("pid=%u, debugger not attached after %d seconds, resuming\n",GetCurrentProcessId(),
+       timeout_sec);
+     fflush(stdout);
+   }
+}
+#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
+
+LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
+{
+   static BOOL first_time= TRUE;
+   if(!first_time)
+   {
+     /*
+       This routine can be called twice, typically
+       when detaching in JIT debugger.
+       Return EXCEPTION_EXECUTE_HANDLER to terminate process.
+     */
+     return EXCEPTION_EXECUTE_HANDLER;
+   }
+   first_time= FALSE;
+#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
+   /*
+    Unfortunately there is no clean way to debug unhandled exception filters,
+    as debugger does not stop there(also documented in MSDN) 
+    To overcome, one could put a MessageBox, but this will not work in service.
+    Better solution is to print error message and sleep some minutes 
+    until debugger is attached
+  */
+  wait_for_debugger(DEBUGGER_ATTACH_TIMEOUT);
+#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
+  __try
+  {
+    set_exception_pointers(ex_pointers);
+    handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode);
+  }
+  __except(EXCEPTION_EXECUTE_HANDLER)
+  {
+    DWORD written;
+    const char msg[] = "Got exception in exception handler!\n";
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),msg, sizeof(msg)-1, 
+      &written,NULL);
+  }
+  /*
+    Return EXCEPTION_CONTINUE_SEARCH to give JIT debugger
+    (drwtsn32 or vsjitdebugger) possibility to attach,
+    if JIT debugger is configured.
+    Windows Error reporting might generate a dump here.
+  */
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+static void init_signals(void)
+{
+  win_install_sigabrt_handler();
+  if(opt_console)
+    SetConsoleCtrlHandler(console_event_handler,TRUE);
+  else
+  {
+    /* Avoid MessageBox()es*/
+   _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+   _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+   _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+   _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+   _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+   _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+
+   /*
+     Do not use SEM_NOGPFAULTERRORBOX in the following SetErrorMode (),
+     because it would prevent JIT debugger and Windows error reporting
+     from working. We need WER or JIT-debugging, since our own unhandled
+     exception filter is not guaranteed to work in all situation
+     (like heap corruption or stack overflow)
+   */
+   SetErrorMode(SetErrorMode(0)|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+  }
+  SetUnhandledExceptionFilter(my_unhandler_exception_filter);
+}
+
+static void start_signal_handler(void)
+{
+  // Save vm id of this process
+  if (!opt_bootstrap)
+    create_pid_file();
+}
+
+static void check_data_home(const char *path)
+{}
+
+
+#elif defined(__NETWARE__)
+
+// down server event callback
+void mysql_down_server_cb(void *, void *)
+{
+  event_flag= TRUE;
+  kill_server(0);
+}
+
+
+// destroy callback resources
+void mysql_cb_destroy(void *)
+{
+  UnRegisterEventNotification(eh);  // cleanup down event notification
+  NX_UNWRAP_INTERFACE(ref);
+  /* Deregister NSS volume deactivation event */
+  NX_UNWRAP_INTERFACE(refneb);
+  if (neb_consumer_id)
+    UnRegisterConsumer(neb_consumer_id, NULL);
+}
+
+
+// initialize callbacks
+void mysql_cb_init()
+{
+  // register for down server event
+  void *handle = getnlmhandle();
+  rtag_t rt= AllocateResourceTag(handle, "MySQL Down Server Callback",
+                                 EventSignature);
+  NX_WRAP_INTERFACE((void *)mysql_down_server_cb, 2, (void **)&ref);
+  eh= RegisterForEventNotification(rt, EVENT_PRE_DOWN_SERVER,
+                                   EVENT_PRIORITY_APPLICATION,
+                                   NULL, ref, NULL);
+
+  /*
+    Register for volume deactivation event
+    Wrap the callback function, as it is called by non-LibC thread
+  */
+  (void *) NX_WRAP_INTERFACE(neb_event_callback, 1, &refneb);
+  registerwithneb();
+
+  NXVmRegisterExitHandler(mysql_cb_destroy, NULL);  // clean-up
+}
+
+
+/* To get the name of the NetWare volume having MySQL data folder */
+
+static void getvolumename()
+{
+  char *p;
+  /*
+    We assume that data path is already set.
+    If not it won't come here. Terminate after volume name
+  */
+  if ((p= strchr(mysql_real_data_home, ':')))
+    strmake(datavolname, mysql_real_data_home,
+            (uint) (p - mysql_real_data_home));
+}
+
+
+/*
+  Registering with NEB for NSS Volume Deactivation event
+*/
+
+static void registerwithneb()
+{
+
+  ConsumerRegistrationInfo reg_info;
+
+  /* Clear NEB registration structure */
+  bzero((char*) &reg_info, sizeof(struct ConsumerRegistrationInfo));
+
+  /* Fill the NEB consumer information structure */
+  reg_info.CRIVersion= 1;  	            // NEB version
+  /* NEB Consumer name */
+  reg_info.CRIConsumerName= (BYTE *) "MySQL Database Server";
+  /* Event of interest */
+  reg_info.CRIEventName= (BYTE *) "NSS.ChangeVolState.Enter";
+  reg_info.CRIUserParameter= NULL;	    // Consumer Info
+  reg_info.CRIEventFlags= 0;	            // Event flags
+  /* Consumer NLM handle */
+  reg_info.CRIOwnerID= (LoadDefinitionStructure *)getnlmhandle();
+  reg_info.CRIConsumerESR= NULL;	    // No consumer ESR required
+  reg_info.CRISecurityToken= 0;	            // No security token for the event
+  reg_info.CRIConsumerFlags= 0;             // SMP_ENABLED_BIT;
+  reg_info.CRIFilterName= 0;	            // No event filtering
+  reg_info.CRIFilterDataLength= 0;          // No filtering data
+  reg_info.CRIFilterData= 0;	            // No filtering data
+  /* Callback function for the event */
+  (void *)reg_info.CRIConsumerCallback= (void *) refneb;
+  reg_info.CRIOrder= 0;	                    // Event callback order
+  reg_info.CRIConsumerType= CHECK_CONSUMER; // Consumer type
+
+  /* Register for the event with NEB */
+  if (RegisterConsumer(&reg_info))
+  {
+    consoleprintf("Failed to register for NSS Volume Deactivation event \n");
+    return;
+  }
+  /* This ID is required for deregistration */
+  neb_consumer_id= reg_info.CRIConsumerID;
+
+  /* Get MySQL data volume name, stored in global variable datavolname */
+  getvolumename();
+
+  /*
+    Get the NSS volume ID of the MySQL Data volume.
+    Volume ID is stored in a global variable
+  */
+  getvolumeID((BYTE*) datavolname);
+}
+
+
+/*
+  Callback for NSS Volume Deactivation event
+*/
+
+ulong neb_event_callback(struct EventBlock *eblock)
+{
+  EventChangeVolStateEnter_s *voldata;
+  extern bool nw_panic;
+
+  voldata= (EventChangeVolStateEnter_s *)eblock->EBEventData;
+
+  /* Deactivation of a volume */
+  if ((voldata->oldState == zVOLSTATE_ACTIVE &&
+       voldata->newState == zVOLSTATE_DEACTIVE ||
+       voldata->newState == zVOLSTATE_MAINTENANCE))
+  {
+    /*
+      Ensure that we bring down MySQL server only for MySQL data
+      volume deactivation
+    */
+    if (!memcmp(&voldata->volID, &datavolid, sizeof(VolumeID_t)))
+    {
+      consoleprintf("MySQL data volume is deactivated, shutting down MySQL Server \n");
+      event_flag= TRUE;
+      nw_panic = TRUE;
+      event_flag= TRUE;
+      kill_server(0);
+    }
+  }
+  return 0;
+}
+
+
+/*
+  Function to get NSS volume ID of the MySQL data
+*/
+
+#define ADMIN_VOL_PATH					"_ADMIN:/Volumes/"
+
+static void getvolumeID(BYTE *volumeName)
+{
+  char path[zMAX_FULL_NAME];
+  Key_t rootKey= 0, fileKey= 0;
+  QUAD getInfoMask;
+  zInfo_s info;
+  STATUS status;
+
+  /* Get the root key */
+  if ((status= zRootKey(0, &rootKey)) != zOK)
+  {
+    consoleprintf("\nGetNSSVolumeProperties - Failed to get root key, status: %d\n.", (int) status);
+    goto exit;
+  }
+
+  /*
+    Get the file key. This is the key to the volume object in the
+    NSS admin volumes directory.
+  */
+
+  strxmov(path, (const char *) ADMIN_VOL_PATH, (const char *) volumeName,
+          NullS);
+  if ((status= zOpen(rootKey, zNSS_TASK, zNSPACE_LONG|zMODE_UTF8,
+                     (BYTE *) path, zRR_READ_ACCESS, &fileKey)) != zOK)
+  {
+    consoleprintf("\nGetNSSVolumeProperties - Failed to get file, status: %d\n.", (int) status);
+    goto exit;
+  }
+
+  getInfoMask= zGET_IDS | zGET_VOLUME_INFO ;
+  if ((status= zGetInfo(fileKey, getInfoMask, sizeof(info),
+                        zINFO_VERSION_A, &info)) != zOK)
+  {
+    consoleprintf("\nGetNSSVolumeProperties - Failed in zGetInfo, status: %d\n.", (int) status);
+    goto exit;
+  }
+
+  /* Copy the data to global variable */
+  datavolid.timeLow= info.vol.volumeID.timeLow;
+  datavolid.timeMid= info.vol.volumeID.timeMid;
+  datavolid.timeHighAndVersion= info.vol.volumeID.timeHighAndVersion;
+  datavolid.clockSeqHighAndReserved= info.vol.volumeID.clockSeqHighAndReserved;
+  datavolid.clockSeqLow= info.vol.volumeID.clockSeqLow;
+  /* This is guranteed to be 6-byte length (but sizeof() would be better) */
+  memcpy(datavolid.node, info.vol.volumeID.node, (unsigned int) 6);
+
+exit:
+  if (rootKey)
+    zClose(rootKey);
+  if (fileKey)
+    zClose(fileKey);
+}
+
+
+static void init_signals(void)
+{
+  int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT};
+
+  for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
+    signal(signals[i], kill_server);
+  mysql_cb_init();  // initialize callbacks
+
+}
+
+
+static void start_signal_handler(void)
+{
+  // Save vm id of this process
+  if (!opt_bootstrap)
+    create_pid_file();
+  // no signal handler
+}
+
+
+/*
+  Warn if the data is on a Traditional volume
+
+  NOTE
+    Already done by mysqld_safe
+*/
+
+static void check_data_home(const char *path)
+{
+}
+
+#elif defined(__EMX__)
+static void sig_reload(int signo)
+{
+ // Flush everything
+  bool not_used;
+  reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0, &not_used);
+  signal(signo, SIG_ACK);
+}
+
+static void sig_kill(int signo)
+{
+  if (!kill_in_progress)
+  {
+    abort_loop=1;				// mark abort for threads
+    kill_server((void*) signo);
+  }
+  signal(signo, SIG_ACK);
+}
+
+static void init_signals(void)
+{
+  signal(SIGQUIT, sig_kill);
+  signal(SIGKILL, sig_kill);
+  signal(SIGTERM, sig_kill);
+  signal(SIGINT,  sig_kill);
+  signal(SIGHUP,  sig_reload);	// Flush everything
+  signal(SIGALRM, SIG_IGN);
+  signal(SIGBREAK,SIG_IGN);
+  signal_thread = pthread_self();
+}
+
+
+static void start_signal_handler(void)
+{}
+
+static void check_data_home(const char *path)
+{}
+#endif /*__WIN__ || __NETWARE || __EMX__*/
+
+
+#ifdef HAVE_LINUXTHREADS
+#define UNSAFE_DEFAULT_LINUX_THREADS 200
+#endif
+
+extern "C" sig_handler handle_segfault(int sig)
+{
+  time_t curr_time;
+  struct tm tm;
+  THD *thd=current_thd;
+
+  /*
+    Strictly speaking, one needs a mutex here
+    but since we have got SIGSEGV already, things are a mess
+    so not having the mutex is not as bad as possibly using a buggy
+    mutex - so we keep things simple
+  */
+  if (segfaulted)
+  {
+    fprintf(stderr, "Fatal " SIGNAL_FMT " while backtracing\n", sig);
+    exit(1);
+  }
+
+  segfaulted = 1;
+
+  curr_time= time(NULL);
+  localtime_r(&curr_time, &tm);
+
+  fprintf(stderr,"\
+%02d%02d%02d %2d:%02d:%02d - mysqld got " SIGNAL_FMT " ;\n\
+This could be because you hit a bug. It is also possible that this binary\n\
+or one of the libraries it was linked against is corrupt, improperly built,\n\
+or misconfigured. This error can also be caused by malfunctioning hardware.\n",
+          tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday,
+          tm.tm_hour, tm.tm_min, tm.tm_sec,
+	  sig);
+  fprintf(stderr, "\
+We will try our best to scrape up some info that will hopefully help diagnose\n\
+the problem, but since we have already crashed, something is definitely wrong\n\
+and this may fail.\n\n");
+  fprintf(stderr, "key_buffer_size=%lu\n",
+          (ulong) dflt_key_cache->key_cache_mem_size);
+  fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size);
+  fprintf(stderr, "max_used_connections=%lu\n", max_used_connections);
+  fprintf(stderr, "max_connections=%lu\n", max_connections);
+  fprintf(stderr, "threads_connected=%u\n", thread_count);
+  fprintf(stderr, "It is possible that mysqld could use up to \n\
+key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = %lu K\n\
+bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size +
+		     (global_system_variables.read_buff_size +
+		      global_system_variables.sortbuff_size) *
+		     max_connections)/ 1024);
+  fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n");
+
+#if defined(HAVE_LINUXTHREADS)
+  if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS)
+  {
+    fprintf(stderr, "\
+You seem to be running 32-bit Linux and have %d concurrent connections.\n\
+If you have not changed STACK_SIZE in LinuxThreads and built the binary \n\
+yourself, LinuxThreads is quite likely to steal a part of the global heap for\n\
+the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n",
+	    thread_count);
+  }
+#endif /* HAVE_LINUXTHREADS */
+
+#ifdef HAVE_STACKTRACE
+  if (!(test_flags & TEST_NO_STACKTRACE))
+  {
+    fprintf(stderr,"thd=%p\n",thd);
+    fprintf(stderr,"\
+Attempting backtrace. You can use the following information to find out\n\
+where mysqld died. If you see no messages after this, something went\n\
+terribly wrong...\n");
+    print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0,
+		     thread_stack);
+  }
+  if (thd)
+  {
+    fprintf(stderr, "Trying to get some variables.\n\
+Some pointers may be invalid and cause the dump to abort...\n");
+    safe_print_str("thd->query", thd->query, 1024);
+    fprintf(stderr, "thd->thread_id=%lu\n", (ulong) thd->thread_id);
+  }
+  fprintf(stderr, "\
+The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\
+information that should help you find out what is causing the crash.\n");
+  fflush(stderr);
+#endif /* HAVE_STACKTRACE */
+
+#ifdef HAVE_INITGROUPS
+  if (calling_initgroups)
+    fprintf(stderr, "\n\
+This crash occured while the server was calling initgroups(). This is\n\
+often due to the use of a mysqld that is statically linked against glibc\n\
+and configured to use LDAP in /etc/nsswitch.conf. You will need to either\n\
+upgrade to a version of glibc that does not have this problem (2.3.4 or\n\
+later when used with nscd), disable LDAP in your nsswitch.conf, or use a\n\
+mysqld that is not statically linked.\n");
+#endif
+
+#ifdef HAVE_NPTL
+  if (thd_lib_detected == THD_LIB_LT && !getenv("LD_ASSUME_KERNEL"))
+    fprintf(stderr,"\n\
+You are running a statically-linked LinuxThreads binary on an NPTL system.\n\
+This can result in crashes on some distributions due to LT/NPTL conflicts.\n\
+You should either build a dynamically-linked binary, or force LinuxThreads\n\
+to be used with the LD_ASSUME_KERNEL environment variable. Please consult\n\
+the documentation for your distribution on how to do that.\n");
+#endif
+  
+  if (locked_in_memory)
+  {
+    fprintf(stderr, "\n\
+The \"--memlock\" argument, which was enabled, uses system calls that are\n\
+unreliable and unstable on some operating systems and operating-system\n\
+versions (notably, some versions of Linux).  This crash could be due to use\n\
+of those buggy OS calls.  You should consider whether you really need the\n\
+\"--memlock\" parameter and/or consult the OS distributer about \"mlockall\"\n\
+bugs.\n");
+  }
+
+#ifdef HAVE_WRITE_CORE
+  if (test_flags & TEST_CORE_ON_SIGNAL)
+  {
+    fprintf(stderr, "Writing a core file\n");
+    fflush(stderr);
+    write_core(sig);
+  }
+#endif
+
+#ifndef __WIN__
+  /* On Windows, do not terminate, but pass control to exception filter */
+  exit(1);
+#endif
+}
+
+#if !defined(__WIN__) && !defined(__NETWARE__) && !defined(__EMX__)
+#ifndef SA_RESETHAND
+#define SA_RESETHAND 0
+#endif
+#ifndef SA_NODEFER
+#define SA_NODEFER 0
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+
+static void init_signals(void)
+{
+  sigset_t set;
+  struct sigaction sa;
+  DBUG_ENTER("init_signals");
+
+  my_sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called!
+
+  if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL))
+  {
+    sa.sa_flags = SA_RESETHAND | SA_NODEFER;
+    sigemptyset(&sa.sa_mask);
+    sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL);
+
+    init_stacktrace();
+#if defined(__amiga__)
+    sa.sa_handler=(void(*)())handle_segfault;
+#else
+    sa.sa_handler=handle_segfault;
+#endif
+    sigaction(SIGSEGV, &sa, NULL);
+    sigaction(SIGABRT, &sa, NULL);
+#ifdef SIGBUS
+    sigaction(SIGBUS, &sa, NULL);
+#endif
+    sigaction(SIGILL, &sa, NULL);
+    sigaction(SIGFPE, &sa, NULL);
+  }
+
+#ifdef HAVE_GETRLIMIT
+  if (test_flags & TEST_CORE_ON_SIGNAL)
+  {
+    /* Change limits so that we will get a core file */
+    STRUCT_RLIMIT rl;
+    rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+    if (setrlimit(RLIMIT_CORE, &rl) && global_system_variables.log_warnings)
+      sql_print_warning("setrlimit could not change the size of core files to 'infinity';  We may not be able to generate a core file on signals");
+  }
+#endif
+  (void) sigemptyset(&set);
+  my_sigset(SIGPIPE,SIG_IGN);
+  sigaddset(&set,SIGPIPE);
+#ifndef IGNORE_SIGHUP_SIGQUIT
+  sigaddset(&set,SIGQUIT);
+  sigaddset(&set,SIGHUP);
+#endif
+  sigaddset(&set,SIGTERM);
+
+  /* Fix signals if blocked by parents (can happen on Mac OS X) */
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sa.sa_handler = print_signal_warning;
+  sigaction(SIGTERM, &sa, (struct sigaction*) 0);
+  sa.sa_flags = 0;
+  sa.sa_handler = print_signal_warning;
+  sigaction(SIGHUP, &sa, (struct sigaction*) 0);
+#ifdef SIGTSTP
+  sigaddset(&set,SIGTSTP);
+#endif
+  if (thd_lib_detected != THD_LIB_LT)
+    sigaddset(&set,THR_SERVER_ALARM);
+  if (test_flags & TEST_SIGINT)
+  {
+    my_sigset(thr_kill_signal, end_thread_signal);
+    // May be SIGINT
+    sigdelset(&set, thr_kill_signal);
+  }
+  else
+    sigaddset(&set,SIGINT);
+  sigprocmask(SIG_SETMASK,&set,NULL);
+  pthread_sigmask(SIG_SETMASK,&set,NULL);
+  DBUG_VOID_RETURN;
+}
+
+
+static void start_signal_handler(void)
+{
+  int error;
+  pthread_attr_t thr_attr;
+  DBUG_ENTER("start_signal_handler");
+
+  (void) pthread_attr_init(&thr_attr);
+#if !defined(HAVE_DEC_3_2_THREADS)
+  pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM);
+  (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
+  if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_attr_setprio(&thr_attr,INTERRUPT_PRIOR);
+#if defined(__ia64__) || defined(__ia64)
+  /*
+    Peculiar things with ia64 platforms - it seems we only have half the
+    stack size in reality, so we have to double it here
+  */
+  pthread_attr_setstacksize(&thr_attr,thread_stack*2);
+#else
+  pthread_attr_setstacksize(&thr_attr,thread_stack);
+#endif
+#endif
+
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  if ((error=pthread_create(&signal_thread,&thr_attr,signal_hand,0)))
+  {
+    sql_print_error("Can't create interrupt-thread (error %d, errno: %d)",
+		    error,errno);
+    exit(1);
+  }
+  (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+  pthread_mutex_unlock(&LOCK_thread_count);
+
+  (void) pthread_attr_destroy(&thr_attr);
+  DBUG_VOID_RETURN;
+}
+
+
+/* This threads handles all signals and alarms */
+
+/* ARGSUSED */
+pthread_handler_t signal_hand(void *arg __attribute__((unused)))
+{
+  sigset_t set;
+  int sig;
+  my_thread_init();				// Init new thread
+  DBUG_ENTER("signal_hand");
+  signal_thread_in_use= 1;
+
+  /*
+    Setup alarm handler
+    This should actually be '+ max_number_of_slaves' instead of +10,
+    but the +10 should be quite safe.
+  */
+  init_thr_alarm(max_connections +
+		 global_system_variables.max_insert_delayed_threads + 10);
+  if (thd_lib_detected != THD_LIB_LT && (test_flags & TEST_SIGINT))
+  {
+    (void) sigemptyset(&set);			// Setup up SIGINT for debug
+    (void) sigaddset(&set,SIGINT);		// For debugging
+    (void) pthread_sigmask(SIG_UNBLOCK,&set,NULL);
+  }
+  (void) sigemptyset(&set);			// Setup up SIGINT for debug
+#ifdef USE_ONE_SIGNAL_HAND
+  (void) sigaddset(&set,THR_SERVER_ALARM);	// For alarms
+#endif
+#ifndef IGNORE_SIGHUP_SIGQUIT
+  (void) sigaddset(&set,SIGQUIT);
+  (void) sigaddset(&set,SIGHUP);
+#endif
+  (void) sigaddset(&set,SIGTERM);
+  (void) sigaddset(&set,SIGTSTP);
+
+  /* Save pid to this process (or thread on Linux) */
+  if (!opt_bootstrap)
+    create_pid_file();
+
+#ifdef HAVE_STACK_TRACE_ON_SEGV
+  if (opt_do_pstack)
+  {
+    sprintf(pstack_file_name,"mysqld-%lu-%%d-%%d.backtrace", (ulong)getpid());
+    pstack_install_segv_action(pstack_file_name);
+  }
+#endif /* HAVE_STACK_TRACE_ON_SEGV */
+
+  /*
+    signal to start_signal_handler that we are ready
+    This works by waiting for start_signal_handler to free mutex,
+    after which we signal it that we are ready.
+    At this pointer there is no other threads running, so there
+    should not be any other pthread_cond_signal() calls.
+  */
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+  (void) pthread_cond_broadcast(&COND_thread_count);
+
+  (void) pthread_sigmask(SIG_BLOCK,&set,NULL);
+  for (;;)
+  {
+    int error;					// Used when debugging
+    if (shutdown_in_progress && !abort_loop)
+    {
+      sig= SIGTERM;
+      error=0;
+    }
+    else
+      while ((error=my_sigwait(&set,&sig)) == EINTR) ;
+    if (cleanup_done)
+    {
+      DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
+      my_thread_end();
+      signal_thread_in_use= 0;
+      pthread_exit(0);				// Safety
+    }
+    switch (sig) {
+    case SIGTERM:
+    case SIGQUIT:
+    case SIGKILL:
+#ifdef EXTRA_DEBUG
+      sql_print_information("Got signal %d to shutdown mysqld",sig);
+#endif
+      DBUG_PRINT("info",("Got signal: %d  abort_loop: %d",sig,abort_loop));
+      if (!abort_loop)
+      {
+	abort_loop=1;				// mark abort for threads
+#ifdef USE_ONE_SIGNAL_HAND
+	pthread_t tmp;
+	if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+	  my_pthread_attr_setprio(&connection_attrib,INTERRUPT_PRIOR);
+	if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
+			   (void*) &sig))
+	  sql_print_error("Can't create thread to kill server");
+#else
+	kill_server((void*) sig);	// MIT THREAD has a alarm thread
+#endif
+      }
+      break;
+    case SIGHUP:
+      if (!abort_loop)
+      {
+        bool not_used;
+	mysql_print_status();		// Print some debug info
+	reload_acl_and_cache((THD*) 0,
+			     (REFRESH_LOG | REFRESH_TABLES | REFRESH_FAST |
+			      REFRESH_GRANT |
+			      REFRESH_THREADS | REFRESH_HOSTS),
+			     (TABLE_LIST*) 0, &not_used); // Flush logs
+      }
+      break;
+#ifdef USE_ONE_SIGNAL_HAND
+    case THR_SERVER_ALARM:
+      process_alarm(sig);			// Trigger alarms.
+      break;
+#endif
+    default:
+#ifdef EXTRA_DEBUG
+      sql_print_warning("Got signal: %d  error: %d",sig,error); /* purecov: tested */
+#endif
+      break;					/* purecov: tested */
+    }
+  }
+  return(0);					/* purecov: deadcode */
+}
+
+static void check_data_home(const char *path)
+{}
+
+#endif /*!EMBEDDED_LIBRARY*/
+#endif	/* __WIN__*/
+
+
+/*
+  All global error messages are sent here where the first one is stored
+  for the client
+*/
+
+
+/* ARGSUSED */
+static int my_message_sql(uint error, const char *str, myf MyFlags)
+{
+  THD *thd;
+  DBUG_ENTER("my_message_sql");
+  DBUG_PRINT("error", ("error: %u  message: '%s'", error, str));
+  /*
+    Put here following assertion when situation with EE_* error codes
+    will be fixed
+    DBUG_ASSERT(error != 0);
+  */
+  if ((thd= current_thd))
+  {
+    /*
+      TODO: There are two exceptions mechanism (THD and sp_rcontext),
+      this could be improved by having a common stack of handlers.
+    */
+    if (thd->handle_error(error,
+                          MYSQL_ERROR::WARN_LEVEL_ERROR))
+      DBUG_RETURN(0);
+
+    if (thd->spcont &&
+        thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
+    {
+      DBUG_RETURN(0);
+    }
+
+    thd->query_error=  1; // needed to catch query errors during replication
+
+    if (!thd->no_warnings_for_error)
+    {
+      thd->no_warnings_for_error= TRUE;
+      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
+      thd->no_warnings_for_error= FALSE;
+    }
+
+    /*
+      thd->lex->current_select == 0 if lex structure is not inited
+      (not query command (COM_QUERY))
+    */
+    if (thd->lex->current_select &&
+	thd->lex->current_select->no_error && !thd->is_fatal_error)
+    {
+      DBUG_PRINT("error", ("Error converted to warning: current_select: no_error %d  fatal_error: %d",
+                           (thd->lex->current_select ?
+                            thd->lex->current_select->no_error : 0),
+                           (int) thd->is_fatal_error));
+    }
+    else
+    {
+      NET *net= &thd->net;
+      net->report_error= 1;
+      query_cache_abort(net);
+      if (!net->last_error[0])			// Return only first message
+      {
+	strmake(net->last_error, str, sizeof(net->last_error)-1);
+	net->last_errno= error ? error : ER_UNKNOWN_ERROR;
+      }
+    }
+  }
+  if (!thd || MyFlags & ME_NOREFRESH)
+    sql_print_error("%s: %s",my_progname,str); /* purecov: inspected */
+  DBUG_RETURN(0);
+}
+
+
+#ifndef EMBEDDED_LIBRARY
+static void *my_str_malloc_mysqld(size_t size)
+{
+  return my_malloc(size, MYF(MY_FAE));
+}
+
+
+static void my_str_free_mysqld(void *ptr)
+{
+  my_free((gptr)ptr, MYF(MY_FAE));
+}
+#endif /* EMBEDDED_LIBRARY */
+
+
+#ifdef __WIN__
+pthread_handler_t handle_shutdown(void *arg)
+{
+  MSG msg;
+  my_thread_init();
+
+  /* this call should create the message queue for this thread */
+  PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE);
+#if !defined(EMBEDDED_LIBRARY)
+  if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0)
+#endif /* EMBEDDED_LIBRARY */
+     kill_server(MYSQL_KILL_SIGNAL);
+  return 0;
+}
+#endif
+
+
+#ifdef OS2
+pthread_handler_t handle_shutdown(void *arg)
+{
+  my_thread_init();
+
+  // wait semaphore
+  pthread_cond_wait(&eventShutdown, NULL);
+
+  // close semaphore and kill server
+  pthread_cond_destroy(&eventShutdown);
+
+  /*
+    Exit main loop on main thread, so kill will be done from
+    main thread (this is thread 2)
+  */
+  abort_loop = 1;
+
+  // unblock select()
+  so_cancel(ip_sock);
+  so_cancel(unix_sock);
+
+  return 0;
+}
+#endif
+
+
+static const char *load_default_groups[]= {
+#ifdef HAVE_NDBCLUSTER_DB
+"mysql_cluster",
+#endif
+"mysqld","server", MYSQL_BASE_VERSION, 0, 0};
+
+#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
+static const int load_default_groups_sz=
+sizeof(load_default_groups)/sizeof(load_default_groups[0]);
+#endif
+
+
+/*
+  Initialize one of the global date/time format variables
+
+  SYNOPSIS
+    init_global_datetime_format()
+    format_type		What kind of format should be supported
+    var_ptr		Pointer to variable that should be updated
+
+  NOTES
+    The default value is taken from either opt_date_time_formats[] or
+    the ISO format (ANSI SQL)
+
+  RETURN
+    0 ok
+    1 error
+*/
+
+static bool init_global_datetime_format(timestamp_type format_type,
+                                        DATE_TIME_FORMAT **var_ptr)
+{
+  /* Get command line option */
+  const char *str= opt_date_time_formats[format_type];
+
+  if (!str)					// No specified format
+  {
+    str= get_date_time_format_str(&known_date_time_formats[ISO_FORMAT],
+				  format_type);
+    /*
+      Set the "command line" option to point to the generated string so
+      that we can set global formats back to default
+    */
+    opt_date_time_formats[format_type]= str;
+  }
+  if (!(*var_ptr= date_time_format_make(format_type, str, strlen(str))))
+  {
+    fprintf(stderr, "Wrong date/time format specifier: %s\n", str);
+    return 1;
+  }
+  return 0;
+}
+
+
+static int init_common_variables(const char *conf_file_name, int argc,
+				 char **argv, const char **groups)
+{
+  umask(((~my_umask) & 0666));
+  my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
+  tzset();			// Set tzname
+
+  max_system_variables.pseudo_thread_id= (ulong)~0;
+  server_start_time= flush_status_time= time((time_t*) 0);
+  if (init_thread_environment())
+    return 1;
+  mysql_init_variables();
+
+#ifdef OS2
+  {
+    // fix timezone for daylight saving
+    struct tm *ts = localtime(&start_time);
+    if (ts->tm_isdst > 0)
+      _timezone -= 3600;
+  }
+#endif
+#ifdef HAVE_TZNAME
+  {
+    struct tm tm_tmp;
+    localtime_r(&server_start_time,&tm_tmp);
+    strmake(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0],
+            sizeof(system_time_zone)-1);
+
+ }
+#endif
+  /*
+    We set SYSTEM time zone as reasonable default and
+    also for failure of my_tz_init() and bootstrap mode.
+    If user explicitly set time zone with --default-time-zone
+    option we will change this value in my_tz_init().
+  */
+  global_system_variables.time_zone= my_tz_SYSTEM;
+
+  /*
+    Init mutexes for the global MYSQL_LOG objects.
+    As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of
+    global MYSQL_LOGs in their constructors, because then they would be inited
+    before MY_INIT(). So we do it here.
+  */
+  mysql_log.init_pthread_objects();
+  mysql_slow_log.init_pthread_objects();
+  mysql_bin_log.init_pthread_objects();
+
+  if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0)
+  {
+    strmake(glob_hostname, STRING_WITH_LEN("localhost"));
+    sql_print_warning("gethostname failed, using '%s' as hostname",
+                      glob_hostname);
+    strmake(pidfile_name, STRING_WITH_LEN("mysql"));
+  }
+  else
+    strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5);
+  strmov(fn_ext(pidfile_name),".pid");		// Add proper extension
+
+  load_defaults(conf_file_name, groups, &argc, &argv);
+  defaults_argv=argv;
+  get_options(argc,argv);
+  set_server_version();
+
+  DBUG_PRINT("info",("%s  Ver %s for %s on %s\n",my_progname,
+		     server_version, SYSTEM_TYPE,MACHINE_TYPE));
+
+#ifdef HAVE_LARGE_PAGES
+  /* Initialize large page size */
+  if (opt_large_pages && (opt_large_page_size= my_get_large_page_size()))
+  {
+      my_use_large_pages= 1;
+      my_large_page_size= opt_large_page_size;
+#ifdef HAVE_INNOBASE_DB
+      innobase_use_large_pages= 1;
+      innobase_large_page_size= opt_large_page_size;
+#endif
+  }
+#endif /* HAVE_LARGE_PAGES */
+
+  /* connections and databases needs lots of files */
+  {
+    uint files, wanted_files, max_open_files;
+
+    /* MyISAM requires two file handles per table. */
+    wanted_files= 10+max_connections+table_cache_size*2;
+    /*
+      We are trying to allocate no less than max_connections*5 file
+      handles (i.e. we are trying to set the limit so that they will
+      be available).  In addition, we allocate no less than how much
+      was already allocated.  However below we report a warning and
+      recompute values only if we got less file handles than were
+      explicitly requested.  No warning and re-computation occur if we
+      can't get max_connections*5 but still got no less than was
+      requested (value of wanted_files).
+    */
+    max_open_files= max(max(wanted_files, max_connections*5),
+                        open_files_limit);
+    files= my_set_max_open_files(max_open_files);
+
+    if (files < wanted_files)
+    {
+      if (!open_files_limit)
+      {
+        /*
+          If we have requested too much file handles than we bring
+          max_connections in supported bounds.
+        */
+        max_connections= (ulong) min(files-10-TABLE_OPEN_CACHE_MIN*2,
+                                     max_connections);
+        /*
+          Decrease table_cache_size according to max_connections, but
+          not below TABLE_OPEN_CACHE_MIN.  Outer min() ensures that we
+          never increase table_cache_size automatically (that could
+          happen if max_connections is decreased above).
+        */
+        table_cache_size= (ulong) min(max((files-10-max_connections)/2,
+                                          TABLE_OPEN_CACHE_MIN),
+                                      table_cache_size);    
+	DBUG_PRINT("warning",
+		   ("Changed limits: max_open_files: %u  max_connections: %ld  table_cache: %ld",
+		    files, max_connections, table_cache_size));
+	if (global_system_variables.log_warnings)
+	  sql_print_warning("Changed limits: max_open_files: %u  max_connections: %ld  table_cache: %ld",
+			files, max_connections, table_cache_size);
+      }
+      else if (global_system_variables.log_warnings)
+	sql_print_warning("Could not increase number of max_open_files to more than %u (request: %u)", files, wanted_files);
+    }
+    open_files_limit= files;
+  }
+  unireg_init(opt_specialflag); /* Set up extern variabels */
+  if (init_errmessage())	/* Read error messages from file */
+    return 1;
+  init_client_errs();
+  lex_init();
+  item_init();
+  set_var_init();
+  mysys_uses_curses=0;
+#ifdef USE_REGEX
+  my_regex_init(&my_charset_latin1);
+#endif
+  /*
+    Process a comma-separated character set list and choose
+    the first available character set. This is mostly for
+    test purposes, to be able to start "mysqld" even if
+    the requested character set is not available (see bug#18743).
+  */
+  for (;;)
+  {
+    char *next_character_set_name= strchr(default_character_set_name, ',');
+    if (next_character_set_name)
+      *next_character_set_name++= '\0';
+    if (!(default_charset_info=
+          get_charset_by_csname(default_character_set_name,
+                                MY_CS_PRIMARY, MYF(MY_WME))))
+    {
+      if (next_character_set_name)
+      {
+        default_character_set_name= next_character_set_name;
+        default_collation_name= 0;          // Ignore collation
+      }
+      else
+        return 1;                           // Eof of the list
+    }
+    else
+      break;
+  }
+
+  if (default_collation_name)
+  {
+    CHARSET_INFO *default_collation;
+    default_collation= get_charset_by_name(default_collation_name, MYF(0));
+    if (!default_collation)
+    {
+      sql_print_error(ER(ER_UNKNOWN_COLLATION), default_collation_name);
+      return 1;
+    }
+    if (!my_charset_same(default_charset_info, default_collation))
+    {
+      sql_print_error(ER(ER_COLLATION_CHARSET_MISMATCH),
+		      default_collation_name,
+		      default_charset_info->csname);
+      return 1;
+    }
+    default_charset_info= default_collation;
+  }
+  /* Set collactions that depends on the default collation */
+  global_system_variables.collation_server=	 default_charset_info;
+  global_system_variables.collation_database=	 default_charset_info;
+  global_system_variables.collation_connection=  default_charset_info;
+  global_system_variables.character_set_results= default_charset_info;
+  global_system_variables.character_set_client= default_charset_info;
+
+  if (!(character_set_filesystem=
+        get_charset_by_csname(character_set_filesystem_name,
+                              MY_CS_PRIMARY, MYF(MY_WME))))
+    return 1;
+  global_system_variables.character_set_filesystem= character_set_filesystem;
+
+  if (!(my_default_lc_time_names=
+        my_locale_by_name(lc_time_names_name)))
+  {
+    sql_print_error("Unknown locale: '%s'", lc_time_names_name);
+    return 1;
+  }
+  global_system_variables.lc_time_names= my_default_lc_time_names;
+  
+  sys_init_connect.value_length= 0;
+  if ((sys_init_connect.value= opt_init_connect))
+    sys_init_connect.value_length= strlen(opt_init_connect);
+  else
+    sys_init_connect.value=my_strdup("",MYF(0));
+
+  sys_init_slave.value_length= 0;
+  if ((sys_init_slave.value= opt_init_slave))
+    sys_init_slave.value_length= strlen(opt_init_slave);
+  else
+    sys_init_slave.value=my_strdup("",MYF(0));
+
+  if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
+    return 1;
+  if (my_dbopt_init())
+    return 1;
+
+  /*
+    Ensure that lower_case_table_names is set on system where we have case
+    insensitive names.  If this is not done the users MyISAM tables will
+    get corrupted if accesses with names of different case.
+  */
+  DBUG_PRINT("info", ("lower_case_table_names: %d", lower_case_table_names));
+  lower_case_file_system= test_if_case_insensitive(mysql_real_data_home);
+  if (!lower_case_table_names && lower_case_file_system == 1)
+  {
+    if (lower_case_table_names_used)
+    {
+      if (global_system_variables.log_warnings)
+	sql_print_warning("\
+You have forced lower_case_table_names to 0 through a command-line \
+option, even though your file system '%s' is case insensitive.  This means \
+that you can corrupt a MyISAM table by accessing it with different cases. \
+You should consider changing lower_case_table_names to 1 or 2",
+			mysql_real_data_home);
+    }
+    else
+    {
+      if (global_system_variables.log_warnings)
+	sql_print_warning("Setting lower_case_table_names=2 because file system for %s is case insensitive", mysql_real_data_home);
+      lower_case_table_names= 2;
+    }
+  }
+  else if (lower_case_table_names == 2 &&
+           !(lower_case_file_system=
+             (test_if_case_insensitive(mysql_real_data_home) == 1)))
+  {
+    if (global_system_variables.log_warnings)
+      sql_print_warning("lower_case_table_names was set to 2, even though your "
+                        "the file system '%s' is case sensitive.  Now setting "
+                        "lower_case_table_names to 0 to avoid future problems.",
+			mysql_real_data_home);
+    lower_case_table_names= 0;
+  }
+  else
+  {
+    lower_case_file_system=
+      (test_if_case_insensitive(mysql_real_data_home) == 1);
+  }
+
+  /* Reset table_alias_charset, now that lower_case_table_names is set. */
+  table_alias_charset= (lower_case_table_names ?
+			files_charset_info :
+			&my_charset_bin);
+
+  return 0;
+}
+
+
+static int init_thread_environment()
+{
+  (void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW);
+  (void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW);
+  (void) pthread_mutex_init(&LOCK_open,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW);
+  (void) pthread_mutex_init(&LOCK_status,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_error_log,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_delayed_insert,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_delayed_status,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_delayed_create,MY_MUTEX_INIT_SLOW);
+  (void) pthread_mutex_init(&LOCK_manager,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_crypt,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST);
+#ifdef HAVE_OPENSSL
+  (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
+#ifndef HAVE_YASSL
+  openssl_stdlocks= (openssl_lock_t*) OPENSSL_malloc(CRYPTO_num_locks() *
+                                                     sizeof(openssl_lock_t));
+  for (int i= 0; i < CRYPTO_num_locks(); ++i)
+    (void) my_rwlock_init(&openssl_stdlocks[i].lock, NULL);
+  CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
+  CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
+  CRYPTO_set_dynlock_lock_callback(openssl_lock);
+  CRYPTO_set_locking_callback(openssl_lock_function);
+  CRYPTO_set_id_callback(openssl_id_function);
+#endif
+#endif
+  (void) my_rwlock_init(&LOCK_sys_init_connect, NULL);
+  (void) my_rwlock_init(&LOCK_sys_init_slave, NULL);
+  (void) my_rwlock_init(&LOCK_grant, NULL);
+  (void) pthread_cond_init(&COND_thread_count,NULL);
+  (void) pthread_cond_init(&COND_refresh,NULL);
+  (void) pthread_cond_init(&COND_global_read_lock,NULL);
+  (void) pthread_cond_init(&COND_thread_cache,NULL);
+  (void) pthread_cond_init(&COND_flush_thread_cache,NULL);
+  (void) pthread_cond_init(&COND_manager,NULL);
+#ifdef HAVE_REPLICATION
+  (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
+  (void) pthread_cond_init(&COND_rpl_status, NULL);
+#endif
+  (void) pthread_mutex_init(&LOCK_global_user_stats, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
+  sp_cache_init();
+  /* Parameter for threads created for connections */
+  (void) pthread_attr_init(&connection_attrib);
+  (void) pthread_attr_setdetachstate(&connection_attrib,
+				     PTHREAD_CREATE_DETACHED);
+  pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
+  if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR);
+
+  if (pthread_key_create(&THR_THD,NULL) ||
+      pthread_key_create(&THR_MALLOC,NULL))
+  {
+    sql_print_error("Can't create thread-keys");
+    return 1;
+  }
+  return 0;
+}
+
+
+#if defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)
+static unsigned long openssl_id_function()
+{
+  return (unsigned long) pthread_self();
+}
+
+
+static openssl_lock_t *openssl_dynlock_create(const char *file, int line)
+{
+  openssl_lock_t *lock= new openssl_lock_t;
+  my_rwlock_init(&lock->lock, NULL);
+  return lock;
+}
+
+
+static void openssl_dynlock_destroy(openssl_lock_t *lock, const char *file,
+				    int line)
+{
+  rwlock_destroy(&lock->lock);
+  delete lock;
+}
+
+
+static void openssl_lock_function(int mode, int n, const char *file, int line)
+{
+  if (n < 0 || n > CRYPTO_num_locks())
+  {
+    /* Lock number out of bounds. */
+    sql_print_error("Fatal: OpenSSL interface problem (n = %d)", n);
+    abort();
+  }
+  openssl_lock(mode, &openssl_stdlocks[n], file, line);
+}
+
+
+static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
+			 int line)
+{
+  int err;
+  char const *what;
+
+  switch (mode) {
+  case CRYPTO_LOCK|CRYPTO_READ:
+    what = "read lock";
+    err = rw_rdlock(&lock->lock);
+    break;
+  case CRYPTO_LOCK|CRYPTO_WRITE:
+    what = "write lock";
+    err = rw_wrlock(&lock->lock);
+    break;
+  case CRYPTO_UNLOCK|CRYPTO_READ:
+  case CRYPTO_UNLOCK|CRYPTO_WRITE:
+    what = "unlock";
+    err = rw_unlock(&lock->lock);
+    break;
+  default:
+    /* Unknown locking mode. */
+    sql_print_error("Fatal: OpenSSL interface problem (mode=0x%x)", mode);
+    abort();
+  }
+  if (err)
+  {
+    sql_print_error("Fatal: can't %s OpenSSL lock", what);
+    abort();
+  }
+}
+#endif /* HAVE_OPENSSL */
+
+
+#ifndef EMBEDDED_LIBRARY
+
+static void init_ssl()
+{
+#ifdef HAVE_OPENSSL
+  if (opt_use_ssl)
+  {
+    /* having ssl_acceptor_fd != 0 signals the use of SSL */
+    ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
+					  opt_ssl_ca, opt_ssl_capath,
+					  opt_ssl_cipher);
+    DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd));
+    if (!ssl_acceptor_fd)
+    {
+      sql_print_warning("Failed to setup SSL");
+      opt_use_ssl = 0;
+      have_ssl= SHOW_OPTION_DISABLED;
+    }
+  }
+  else
+  {
+    have_ssl= SHOW_OPTION_DISABLED;
+  }
+  if (des_key_file)
+    load_des_key_file(des_key_file);
+#endif /* HAVE_OPENSSL */
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
+static int init_server_components()
+{
+  DBUG_ENTER("init_server_components");
+  if (table_cache_init() || hostname_cache_init())
+    unireg_abort(1);
+
+  query_cache_result_size_limit(query_cache_limit);
+  query_cache_set_min_res_unit(query_cache_min_res_unit);
+  query_cache_init();
+  query_cache_resize(query_cache_size);
+  randominit(&sql_rand,(ulong) server_start_time,(ulong) server_start_time/2);
+  set_proper_floating_point_mode();
+  init_thr_lock();
+#ifdef HAVE_REPLICATION
+  init_slave_list();
+#endif
+  /* Setup log files */
+  if (opt_log)
+    mysql_log.open_query_log(opt_logname);
+  if (opt_update_log)
+  {
+    /*
+      Update log is removed since 5.0. But we still accept the option.
+      The idea is if the user already uses the binlog and the update log,
+      we completely ignore any option/variable related to the update log, like
+      if the update log did not exist. But if the user uses only the update
+      log, then we translate everything into binlog for him (with warnings).
+      Implementation of the above :
+      - If mysqld is started with --log-update and --log-bin,
+      ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
+      is used, and turn off --sql-bin-update-same.
+      This will completely ignore SQL_LOG_UPDATE
+      - If mysqld is started with --log-update only,
+      change it to --log-bin (with the filename passed to log-update,
+      plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
+      used, and turn on --sql-bin-update-same.
+      This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
+
+      Note that we tell the user that --sql-bin-update-same is deprecated and
+      does nothing, and we don't take into account if he used this option or
+      not; but internally we give this variable a value to have the behaviour
+      we want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
+      As sql-bin-update-same, log-update and log-bin cannot be changed by the
+      user after starting the server (they are not variables), the user will
+      not later interfere with the settings we do here.
+    */
+    if (opt_bin_log)
+    {
+      opt_sql_bin_update= 0;
+      sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log.");
+    }
+    else
+    {
+      opt_sql_bin_update= 1;
+      opt_bin_log= 1;
+      if (opt_update_logname)
+      {
+        /* as opt_bin_log==0, no need to free opt_bin_logname */
+        if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+          exit(EXIT_OUT_OF_MEMORY);
+        sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin='%s' instead.",opt_bin_logname);
+      }
+      else
+        sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin instead.");
+    }
+  }
+  if (opt_log_slave_updates && !opt_bin_log)
+  {
+    sql_print_warning("You need to use --log-bin to make "
+                      "--log-slave-updates work.");
+      unireg_abort(1);
+  }
+
+  if (opt_slow_log)
+    mysql_slow_log.open_slow_log(opt_slow_logname);
+
+#ifdef HAVE_REPLICATION
+  if (opt_log_slave_updates && replicate_same_server_id)
+  {
+    sql_print_error("\
+using --replicate-same-server-id in conjunction with \
+--log-slave-updates is impossible, it would lead to infinite loops in this \
+server.");
+    unireg_abort(1);
+  }
+#endif
+
+  if (opt_error_log)
+  {
+    if (!log_error_file_ptr[0])
+      fn_format(log_error_file, pidfile_name, mysql_data_home, ".err",
+                MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */
+    else
+      fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
+		MY_UNPACK_FILENAME | MY_SAFE_PATH);
+    if (!log_error_file[0])
+      opt_error_log= 1;				// Too long file name
+    else
+    {
+#ifndef EMBEDDED_LIBRARY
+      if (freopen(log_error_file, "a+", stdout))
+#endif
+	stderror_file= freopen(log_error_file, "a+", stderr);
+    }
+  }
+
+  if (opt_bin_log)
+  {
+    char buf[FN_REFLEN];
+    const char *ln;
+    ln= mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf);
+    if (!opt_bin_logname && !opt_binlog_index_name)
+    {
+      /*
+        User didn't give us info to name the binlog index file.
+        Picking `hostname`-bin.index like did in 4.x, causes replication to
+        fail if the hostname is changed later. So, we would like to instead
+        require a name. But as we don't want to break many existing setups, we
+        only give warning, not error.
+      */
+      sql_print_warning("No argument was provided to --log-bin, and "
+                        "--log-bin-index was not used; so replication "
+                        "may break when this MySQL server acts as a "
+                        "master and has his hostname changed!! Please "
+                        "use '--log-bin=%s' to avoid this problem.", ln);
+    }
+    if (ln == buf)
+    {
+      my_free(opt_bin_logname, MYF(MY_ALLOW_ZERO_PTR));
+      opt_bin_logname=my_strdup(buf, MYF(0));
+    }
+    if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln))
+    {
+      unireg_abort(1);
+    }
+
+    /*
+      Used to specify which type of lock we need to use for queries of type
+      INSERT ... SELECT. This will change when we have row level logging.
+    */
+    using_update_log=1;
+  }
+
+  if (xid_cache_init())
+  {
+    sql_print_error("Out of memory");
+    unireg_abort(1);
+  }
+
+  init_global_table_stats();
+  init_global_index_stats();
+
+  if (ha_init())
+  {
+    sql_print_error("Can't init databases");
+    unireg_abort(1);
+  }
+
+  /*
+    Check that the default storage engine is actually available.
+  */
+  if (!ha_storage_engine_is_enabled((enum db_type)
+                                    global_system_variables.table_type))
+  {
+    if (!opt_bootstrap)
+    {
+      sql_print_error("Default storage engine (%s) is not available",
+                      ha_get_storage_engine((enum db_type)
+                                            global_system_variables.table_type));
+      unireg_abort(1);
+    }
+    global_system_variables.table_type= DB_TYPE_MYISAM;
+  }
+
+  tc_log= (total_ha_2pc > 1 ? (opt_bin_log  ?
+                               (TC_LOG *) &mysql_bin_log :
+                               (TC_LOG *) &tc_log_mmap) :
+           (TC_LOG *) &tc_log_dummy);
+
+  if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
+  {
+    sql_print_error("Can't init tc log");
+    unireg_abort(1);
+  }
+
+  if (ha_recover(0))
+  {
+    unireg_abort(1);
+  }
+
+  if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
+                                        WRITE_CACHE, 0, max_binlog_size, 0))
+    unireg_abort(1);
+
+#ifdef HAVE_REPLICATION
+  if (opt_bin_log && expire_logs_days)
+  {
+    time_t purge_time= time(0) - expire_logs_days*24*60*60;
+    if (purge_time >= 0)
+      mysql_bin_log.purge_logs_before_date(purge_time);
+  }
+#endif
+
+  if (opt_myisam_log)
+    (void) mi_log(1);
+
+  /* call ha_init_key_cache() on all key caches to init them */
+  process_key_caches(&ha_init_key_cache);
+
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY)
+  if (locked_in_memory && !getuid())
+  {
+    if (setreuid((uid_t)-1, 0) == -1)
+    {                        // this should never happen
+      sql_perror("setreuid");
+      unireg_abort(1);
+    }
+    if (mlockall(MCL_CURRENT))
+    {
+      if (global_system_variables.log_warnings)
+	sql_print_warning("Failed to lock memory. Errno: %d\n",errno);
+      locked_in_memory= 0;
+    }
+    if (user_info)
+      set_user(mysqld_user, user_info);
+  }
+  else
+#endif
+    locked_in_memory=0;
+
+  ft_init_stopwords();
+
+  init_max_user_conn();
+  init_update_queries();
+  init_global_user_stats();
+  DBUG_RETURN(0);
+}
+
+
+#ifndef EMBEDDED_LIBRARY
+static void create_maintenance_thread()
+{
+  if (
+#ifdef HAVE_BERKELEY_DB
+      (have_berkeley_db == SHOW_OPTION_YES) ||
+#endif
+      (flush_time && flush_time != ~(ulong) 0L))
+  {
+    pthread_t hThread;
+    if (pthread_create(&hThread,&connection_attrib,handle_manager,0))
+      sql_print_warning("Can't create thread to manage maintenance");
+  }
+}
+
+
+static void create_shutdown_thread()
+{
+#ifdef __WIN__
+  hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
+  pthread_t hThread;
+  if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
+    sql_print_warning("Can't create thread to handle shutdown requests");
+
+  // On "Stop Service" we have to do regular shutdown
+  Service.SetShutdownEvent(hEventShutdown);
+#endif
+#ifdef OS2
+  pthread_cond_init(&eventShutdown, NULL);
+  pthread_t hThread;
+  if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
+    sql_print_warning("Can't create thread to handle shutdown requests");
+#endif
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
+#if defined(__NT__) || defined(HAVE_SMEM)
+static void handle_connections_methods()
+{
+  pthread_t hThread;
+  DBUG_ENTER("handle_connections_methods");
+#ifdef __NT__
+  if (hPipe == INVALID_HANDLE_VALUE &&
+      (!have_tcpip || opt_disable_networking) &&
+      !opt_enable_shared_memory)
+  {
+    sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS");
+    unireg_abort(1);				// Will not return
+  }
+#endif
+
+  pthread_mutex_lock(&LOCK_thread_count);
+  (void) pthread_cond_init(&COND_handler_count,NULL);
+  handler_count=0;
+#ifdef __NT__
+  if (hPipe != INVALID_HANDLE_VALUE)
+  {
+    handler_count++;
+    if (pthread_create(&hThread,&connection_attrib,
+		       handle_connections_namedpipes, 0))
+    {
+      sql_print_warning("Can't create thread to handle named pipes");
+      handler_count--;
+    }
+  }
+#endif /* __NT__ */
+  if (have_tcpip && !opt_disable_networking)
+  {
+    handler_count++;
+    if (pthread_create(&hThread,&connection_attrib,
+		       handle_connections_sockets, 0))
+    {
+      sql_print_warning("Can't create thread to handle TCP/IP");
+      handler_count--;
+    }
+  }
+#ifdef HAVE_SMEM
+  if (opt_enable_shared_memory)
+  {
+    handler_count++;
+    if (pthread_create(&hThread,&connection_attrib,
+		       handle_connections_shared_memory, 0))
+    {
+      sql_print_warning("Can't create thread to handle shared memory");
+      handler_count--;
+    }
+  }
+#endif
+
+  while (handler_count > 0)
+    pthread_cond_wait(&COND_handler_count,&LOCK_thread_count);
+  pthread_mutex_unlock(&LOCK_thread_count);
+  DBUG_VOID_RETURN;
+}
+
+void decrement_handler_count()
+{
+  pthread_mutex_lock(&LOCK_thread_count);
+  handler_count--;
+  pthread_cond_signal(&COND_handler_count);
+  pthread_mutex_unlock(&LOCK_thread_count);  
+  my_thread_end();
+}
+#else
+#define decrement_handler_count()
+#endif /* defined(__NT__) || defined(HAVE_SMEM) */
+
+
+#ifndef EMBEDDED_LIBRARY
+#ifdef __WIN__
+int win_main(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+  MY_INIT(argv[0]);		// init my_sys library & pthreads
+  /* ^^^  Nothing should be before this line! */
+
+  /* Set signal used to kill MySQL */
+#if defined(SIGUSR2)
+  thr_kill_signal= thd_lib_detected == THD_LIB_LT ? SIGINT : SIGUSR2;
+#else
+  thr_kill_signal= SIGINT;
+#endif
+  
+#ifdef _CUSTOMSTARTUPCONFIG_
+  if (_cust_check_startup())
+  {
+    / * _cust_check_startup will report startup failure error * /
+    exit(1);
+  }
+#endif
+
+#ifdef	__WIN__
+  /*
+    Before performing any socket operation (like retrieving hostname
+    in init_common_variables we have to call WSAStartup
+  */
+  {
+    WSADATA WsaData;
+    if (SOCKET_ERROR == WSAStartup (0x0101, &WsaData))
+    {
+      /* errors are not read yet, so we use english text here */
+      my_message(ER_WSAS_FAILED, "WSAStartup Failed", MYF(0));
+      unireg_abort(1);
+    }
+  }
+#endif /* __WIN__ */
+
+  if (init_common_variables(MYSQL_CONFIG_NAME,
+			    argc, argv, load_default_groups))
+    unireg_abort(1);				// Will do exit
+
+  init_signals();
+  if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_setprio(pthread_self(),CONNECT_PRIOR);
+#if defined(__ia64__) || defined(__ia64)
+  /*
+    Peculiar things with ia64 platforms - it seems we only have half the
+    stack size in reality, so we have to double it here
+  */
+  pthread_attr_setstacksize(&connection_attrib,thread_stack*2);
+#else
+  pthread_attr_setstacksize(&connection_attrib,thread_stack);
+#endif
+#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
+  {
+    /* Retrieve used stack size;  Needed for checking stack overflows */
+    size_t stack_size= 0;
+    pthread_attr_getstacksize(&connection_attrib, &stack_size);
+#if defined(__ia64__) || defined(__ia64)
+    stack_size/= 2;
+#endif
+    /* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */
+    if (stack_size && stack_size < thread_stack)
+    {
+      if (global_system_variables.log_warnings)
+	sql_print_warning("Asked for %lu thread stack, but got %ld",
+			  thread_stack, (long) stack_size);
+#if defined(__ia64__) || defined(__ia64)
+      thread_stack= stack_size*2;
+#else
+      thread_stack= stack_size;
+#endif
+    }
+  }
+#endif
+#ifdef __NETWARE__
+  /* Increasing stacksize of threads on NetWare */
+
+  pthread_attr_setstacksize(&connection_attrib, NW_THD_STACKSIZE);
+#endif
+
+  (void) thr_setconcurrency(concurrency);	// 10 by default
+
+  select_thread=pthread_self();
+  select_thread_in_use=1;
+  init_ssl();
+
+#ifdef HAVE_LIBWRAP
+  libwrapName= my_progname+dirname_length(my_progname);
+  openlog(libwrapName, LOG_PID, LOG_AUTH);
+#endif
+
+  /*
+    We have enough space for fiddling with the argv, continue
+  */
+  check_data_home(mysql_real_data_home);
+  if (my_setwd(mysql_real_data_home,MYF(MY_WME)))
+  {
+    unireg_abort(1);				/* purecov: inspected */
+  }
+  mysql_data_home= mysql_data_home_buff;
+  mysql_data_home[0]=FN_CURLIB;		// all paths are relative from here
+  mysql_data_home[1]=0;
+
+  if ((user_info= check_user(mysqld_user)))
+  {
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
+    if (locked_in_memory) // getuid() == 0 here
+      set_effective_user(user_info);
+    else
+#endif
+      set_user(mysqld_user, user_info);
+  }
+
+
+  if (opt_bin_log && !server_id)
+  {
+    server_id= !master_host ? 1 : 2;
+#ifdef EXTRA_DEBUG
+    switch (server_id) {
+    case 1:
+      sql_print_warning("\
+You have enabled the binary log, but you haven't set server-id to \
+a non-zero value: we force server id to 1; updates will be logged to the \
+binary log, but connections from slaves will not be accepted.");
+      break;
+    case 2:
+      sql_print_warning("\
+You should set server-id to a non-0 value if master_host is set; \
+we force server id to 2, but this MySQL server will not act as a slave.");
+      break;
+    }
+#endif
+  }
+
+  if (init_server_components())
+    exit(1);
+
+  network_init();
+
+#ifdef __WIN__
+  if (!opt_console)
+  {
+    freopen(log_error_file,"a+",stdout);
+    freopen(log_error_file,"a+",stderr);
+    FreeConsole();				// Remove window
+  }
+#endif
+
+  /*
+   Initialize my_str_malloc() and my_str_free()
+  */
+  my_str_malloc= &my_str_malloc_mysqld;
+  my_str_free= &my_str_free_mysqld;
+
+  /*
+    init signals & alarm
+    After this we can't quit by a simple unireg_abort
+  */
+  error_handler_hook= my_message_sql;
+  start_signal_handler();				// Creates pidfile
+  if (mysql_rm_tmp_tables() || acl_init(opt_noacl) ||
+      my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
+  {
+    abort_loop=1;
+    select_thread_in_use=0;
+#ifndef __NETWARE__
+    (void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL);
+#endif /* __NETWARE__ */
+
+    if (!opt_bootstrap)
+      (void) my_delete(pidfile_name,MYF(MY_WME));	// Not needed anymore
+
+    if (unix_sock != INVALID_SOCKET)
+      unlink(mysqld_unix_port);
+    exit(1);
+  }
+  if (!opt_noacl)
+    (void) grant_init();
+
+#ifdef HAVE_DLOPEN
+  if (!opt_noacl)
+    udf_init();
+#endif
+  if (opt_bootstrap) /* If running with bootstrap, do not start replication. */
+    opt_skip_slave_start= 1;
+  /*
+    init_slave() must be called after the thread keys are created.
+    Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
+    places) assume that active_mi != 0, so let's fail if it's 0 (out of
+    memory); a message has already been printed.
+  */
+  if (init_slave() && !active_mi)
+  {
+    end_thr_alarm(1);				// Don't allow alarms
+    unireg_abort(1);
+  }
+
+  if (opt_bootstrap)
+  {
+    select_thread_in_use= 0;                    // Allow 'kill' to work
+    bootstrap(stdin);
+    end_thr_alarm(1);				// Don't allow alarms
+    unireg_abort(bootstrap_error ? 1 : 0);
+  }
+  if (opt_init_file)
+  {
+    if (read_init_file(opt_init_file))
+    {
+      end_thr_alarm(1);				// Don't allow alarms
+      unireg_abort(1);
+    }
+  }
+
+  create_shutdown_thread();
+  create_maintenance_thread();
+
+  sql_print_information(ER(ER_STARTUP),my_progname,server_version,
+                        ((unix_sock == INVALID_SOCKET) ? (char*) ""
+                                                       : mysqld_unix_port),
+                         mysqld_port,
+                         MYSQL_COMPILATION_COMMENT);
+
+#if defined(__NT__) || defined(HAVE_SMEM)
+  handle_connections_methods();
+#else
+#ifdef __WIN__
+  if (!have_tcpip || opt_disable_networking)
+  {
+    sql_print_error("TCP/IP unavailable or disabled with --skip-networking; no available interfaces");
+    unireg_abort(1);
+  }
+#endif
+  handle_connections_sockets(0);
+#endif /* __NT__ */
+
+  /* (void) pthread_attr_destroy(&connection_attrib); */
+
+  DBUG_PRINT("quit",("Exiting main thread"));
+
+#ifndef __WIN__
+#ifdef EXTRA_DEBUG2
+  sql_print_error("Before Lock_thread_count");
+#endif
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  DBUG_PRINT("quit", ("Got thread_count mutex"));
+  select_thread_in_use=0;			// For close_connections
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+  (void) pthread_cond_broadcast(&COND_thread_count);
+#ifdef EXTRA_DEBUG2
+  sql_print_error("After lock_thread_count");
+#endif
+#endif /* __WIN__ */
+
+  /* Wait until cleanup is done */
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  while (!ready_to_exit)
+    pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
+  if (Service.IsNT() && start_mode)
+    Service.Stop();
+  else
+  {
+    Service.SetShutdownEvent(0);
+    if (hEventShutdown)
+      CloseHandle(hEventShutdown);
+  }
+#endif
+  wait_for_signal_thread_to_end();
+  clean_up_mutexes();
+  my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
+
+  exit(0);
+  return(0);					/* purecov: deadcode */
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
+
+/****************************************************************************
+  Main and thread entry function for Win32
+  (all this is needed only to run mysqld as a service on WinNT)
+****************************************************************************/
+
+#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
+int mysql_service(void *p)
+{
+  if (use_opt_args)
+    win_main(opt_argc, opt_argv);
+  else
+    win_main(Service.my_argc, Service.my_argv);
+  return 0;
+}
+
+
+/* Quote string if it contains space, else copy */
+
+static char *add_quoted_string(char *to, const char *from, char *to_end)
+{
+  uint length= (uint) (to_end-to);
+
+  if (!strchr(from, ' '))
+    return strnmov(to, from, length);
+  return strxnmov(to, length, "\"", from, "\"", NullS);
+}
+
+
+/*
+  Handle basic handling of services, like installation and removal
+
+  SYNOPSIS
+    default_service_handling()
+    argv		Pointer to argument list
+    servicename		Internal name of service
+    displayname		Display name of service (in taskbar ?)
+    file_path		Path to this program
+    startup_option	Startup option to mysqld
+
+  RETURN VALUES
+    0		option handled
+    1		Could not handle option
+ */
+
+static bool
+default_service_handling(char **argv,
+			 const char *servicename,
+			 const char *displayname,
+			 const char *file_path,
+			 const char *extra_opt,
+			 const char *account_name)
+{
+  char path_and_service[FN_REFLEN+FN_REFLEN+32], *pos, *end;
+  end= path_and_service + sizeof(path_and_service)-3;
+
+  /* We have to quote filename if it contains spaces */
+  pos= add_quoted_string(path_and_service, file_path, end);
+  if (*extra_opt)
+  {
+    /* Add (possible quoted) option after file_path */
+    *pos++= ' ';
+    pos= add_quoted_string(pos, extra_opt, end);
+  }
+  /* We must have servicename last */
+  *pos++= ' ';
+  (void) add_quoted_string(pos, servicename, end);
+
+  if (Service.got_service_option(argv, "install"))
+  {
+    Service.Install(1, servicename, displayname, path_and_service,
+                    account_name);
+    return 0;
+  }
+  if (Service.got_service_option(argv, "install-manual"))
+  {
+    Service.Install(0, servicename, displayname, path_and_service,
+                    account_name);
+    return 0;
+  }
+  if (Service.got_service_option(argv, "remove"))
+  {
+    Service.Remove(servicename);
+    return 0;
+  }
+  return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+
+  /*
+    When several instances are running on the same machine, we
+    need to have an  unique  named  hEventShudown  through the
+    application PID e.g.: MySQLShutdown1890; MySQLShutdown2342
+  */
+  int10_to_str((int) GetCurrentProcessId(),strmov(shutdown_event_name,
+                                                  "MySQLShutdown"), 10);
+
+  /* Must be initialized early for comparison of service name */
+  system_charset_info= &my_charset_utf8_general_ci;
+
+  if (Service.GetOS())	/* true NT family */
+  {
+    char file_path[FN_REFLEN];
+    my_path(file_path, argv[0], "");		      /* Find name in path */
+    fn_format(file_path,argv[0],file_path,"",
+	      MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS);
+
+    if (argc == 2)
+    {
+      if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
+				   file_path, "", NULL))
+	return 0;
+      if (Service.IsService(argv[1]))        /* Start an optional service */
+      {
+	/*
+	  Only add the service name to the groups read from the config file
+	  if it's not "MySQL". (The default service name should be 'mysqld'
+	  but we started a bad tradition by calling it MySQL from the start
+	  and we are now stuck with it.
+	*/
+	if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
+	  load_default_groups[load_default_groups_sz-2]= argv[1];
+        start_mode= 1;
+        Service.Init(argv[1], mysql_service);
+        return 0;
+      }
+    }
+    else if (argc == 3) /* install or remove any optional service */
+    {
+      if (!default_service_handling(argv, argv[2], argv[2], file_path, "",
+                                    NULL))
+	return 0;
+      if (Service.IsService(argv[2]))
+      {
+	/*
+	  mysqld was started as
+	  mysqld --defaults-file=my_path\my.ini service-name
+	*/
+	use_opt_args=1;
+	opt_argc= 2;				// Skip service-name
+	opt_argv=argv;
+	start_mode= 1;
+	if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
+	  load_default_groups[load_default_groups_sz-2]= argv[2];
+	Service.Init(argv[2], mysql_service);
+	return 0;
+      }
+    }
+    else if (argc == 4 || argc == 5)
+    {
+      /*
+        This may seem strange, because we handle --local-service while
+        preserving 4.1's behavior of allowing any one other argument that is
+        passed to the service on startup. (The assumption is that this is
+        --defaults-file=file, but that was not enforced in 4.1, so we don't
+        enforce it here.)
+      */
+      const char *extra_opt= NullS;
+      const char *account_name = NullS;
+      int index;
+      for (index = 3; index < argc; index++)
+      {
+        if (!strcmp(argv[index], "--local-service"))
+          account_name= "NT AUTHORITY\\LocalService";
+        else
+          extra_opt= argv[index];
+      }
+
+      if (argc == 4 || account_name)
+        if (!default_service_handling(argv, argv[2], argv[2], file_path,
+                                      extra_opt, account_name))
+          return 0;
+    }
+    else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
+    {
+      /* start the default service */
+      start_mode= 1;
+      Service.Init(MYSQL_SERVICENAME, mysql_service);
+      return 0;
+    }
+  }
+  /* Start as standalone server */
+  Service.my_argc=argc;
+  Service.my_argv=argv;
+  mysql_service(NULL);
+  return 0;
+}
+#endif
+
+
+/*
+  Execute all commands from a file. Used by the mysql_install_db script to
+  create MySQL privilege tables without having to start a full MySQL server.
+*/
+
+static void bootstrap(FILE *file)
+{
+  DBUG_ENTER("bootstrap");
+
+  THD *thd= new THD;
+  thd->bootstrap=1;
+  my_net_init(&thd->net,(st_vio*) 0);
+  thd->max_client_packet_length= thd->net.max_packet;
+  thd->security_ctx->master_access= ~(ulong)0;
+  thd->thread_id=thread_id++;
+  thread_count++;
+
+  bootstrap_file=file;
+#ifndef EMBEDDED_LIBRARY			// TODO:  Enable this
+  if (pthread_create(&thd->real_id,&connection_attrib,handle_bootstrap,
+		     (void*) thd))
+  {
+    sql_print_warning("Can't create thread to handle bootstrap");
+    bootstrap_error=-1;
+    DBUG_VOID_RETURN;
+  }
+  /* Wait for thread to die */
+  (void) pthread_mutex_lock(&LOCK_thread_count);
+  while (thread_count)
+  {
+    (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+    DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
+  }
+  (void) pthread_mutex_unlock(&LOCK_thread_count);
+#else
+  thd->mysql= 0;
+  handle_bootstrap((void *)thd);
+#endif
+
+  DBUG_VOID_RETURN;
+}
+
+
+static bool read_init_file(char *file_name)
+{
+  FILE *file;
+  DBUG_ENTER("read_init_file");
+  DBUG_PRINT("enter",("name: %s",file_name));
+  if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME))))
+    return(1);
+  bootstrap(file);
+  (void) my_fclose(file,MYF(MY_WME));
+  return 0;
+}
+
+
+#ifndef EMBEDDED_LIBRARY
+/*
+  Create new thread to handle incoming connection.
+
+  SYNOPSIS
+    create_new_thread()
+      thd in/out    Thread handle of future thread.
+
+  DESCRIPTION
+    This function will create new thread to handle the incoming
+    connection.  If there are idle cached threads one will be used.
+    'thd' will be pushed into 'threads'.
+
+    In single-threaded mode (#define ONE_THREAD) connection will be
+    handled inside this function.
+
+  RETURN VALUE
+    none
+*/
+
+static void create_new_thread(THD *thd)
+{
+  NET *net=&thd->net;
+  DBUG_ENTER("create_new_thread");
+
+  if (protocol_version > 9)
+    net->return_errno=1;
+
+  /* don't allow too many connections */
+  if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop)
+  {
+    DBUG_PRINT("error",("Too many connections"));
+    close_connection(thd, ER_CON_COUNT_ERROR, 1);
+    delete thd;
+    DBUG_VOID_RETURN;
+  }
+  pthread_mutex_lock(&LOCK_thread_count);
+  thd->thread_id=thread_id++;
+
+  thd->real_id=pthread_self();			// Keep purify happy
+
+  /* Start a new thread to handle connection */
+  thread_count++;
+
+#ifdef ONE_THREAD
+  if (test_flags & TEST_NO_THREADS)		// For debugging under Linux
+  {
+    thread_cache_size=0;			// Safety
+    threads.append(thd);
+    thd->real_id=pthread_self();
+    (void) pthread_mutex_unlock(&LOCK_thread_count);
+    handle_one_connection((void*) thd);
+  }
+  else
+#endif
+  {
+    if (thread_count-delayed_insert_threads > max_used_connections)
+      max_used_connections=thread_count-delayed_insert_threads;
+
+    if (cached_thread_count > wake_thread)
+    {
+      thread_cache.append(thd);
+      wake_thread++;
+      pthread_cond_signal(&COND_thread_cache);
+    }
+    else
+    {
+      int error;
+      thread_created++;
+      threads.append(thd);
+      DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
+      thd->connect_time = time(NULL);
+      if ((error=pthread_create(&thd->real_id,&connection_attrib,
+				handle_one_connection,
+				(void*) thd)))
+      {
+	DBUG_PRINT("error",
+		   ("Can't create thread to handle request (error %d)",
+		    error));
+	thread_count--;
+	thd->killed= THD::KILL_CONNECTION;			// Safety
+	(void) pthread_mutex_unlock(&LOCK_thread_count);
+	statistic_increment(aborted_connects,&LOCK_status);
+	net_printf_error(thd, ER_CANT_CREATE_THREAD, error);
+	(void) pthread_mutex_lock(&LOCK_thread_count);
+	close_connection(thd,0,0);
+	delete thd;
+	(void) pthread_mutex_unlock(&LOCK_thread_count);
+	DBUG_VOID_RETURN;
+      }
+    }
+    (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+  }
+  DBUG_PRINT("info",("Thread created"));
+  DBUG_VOID_RETURN;
+}
+#endif /* EMBEDDED_LIBRARY */
+
+
+#ifdef SIGNALS_DONT_BREAK_READ
+inline void kill_broken_server()
+{
+  /* hack to get around signals ignored in syscalls for problem OS's */
+  if (
+#if !defined(__NETWARE__)
+      unix_sock == INVALID_SOCKET ||
+#endif
+      (!opt_disable_networking && ip_sock == INVALID_SOCKET))
+  {
+    select_thread_in_use = 0;
+    /* The following call will never return */
+    kill_server(IF_NETWARE(MYSQL_KILL_SIGNAL, (void*) MYSQL_KILL_SIGNAL));
+  }
+}
+#define MAYBE_BROKEN_SYSCALL kill_broken_server();
+#else
+#define MAYBE_BROKEN_SYSCALL
+#endif
+
+	/* Handle new connections and spawn new process to handle them */
+
+#ifndef EMBEDDED_LIBRARY
+pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
+{
+  my_socket sock,new_sock;
+  uint error_count=0;
+  uint max_used_connection= (uint) (max(ip_sock,unix_sock)+1);
+  fd_set readFDs,clientFDs;
+  THD *thd;
+  struct sockaddr_in cAddr;
+  int ip_flags=0,socket_flags=0,flags;
+  st_vio *vio_tmp;
+  DBUG_ENTER("handle_connections_sockets");
+
+  LINT_INIT(new_sock);
+
+  (void) my_pthread_getprio(pthread_self());		// For debugging
+
+  FD_ZERO(&clientFDs);
+  if (ip_sock != INVALID_SOCKET)
+  {
+    FD_SET(ip_sock,&clientFDs);
+#ifdef HAVE_FCNTL
+    ip_flags = fcntl(ip_sock, F_GETFL, 0);
+#endif
+  }
+#ifdef HAVE_SYS_UN_H
+  FD_SET(unix_sock,&clientFDs);
+#ifdef HAVE_FCNTL
+  socket_flags=fcntl(unix_sock, F_GETFL, 0);
+#endif
+#endif
+
+  DBUG_PRINT("general",("Waiting for connections."));
+  MAYBE_BROKEN_SYSCALL;
+  while (!abort_loop)
+  {
+    readFDs=clientFDs;
+#ifdef HPUX10
+    if (select(max_used_connection,(int*) &readFDs,0,0,0) < 0)
+      continue;
+#else
+    if (select((int) max_used_connection,&readFDs,0,0,0) < 0)
+    {
+      if (socket_errno != SOCKET_EINTR)
+      {
+	if (!select_errors++ && !abort_loop)	/* purecov: inspected */
+	  sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
+      }
+      MAYBE_BROKEN_SYSCALL
+      continue;
+    }
+#endif	/* HPUX10 */
+    if (abort_loop)
+    {
+      MAYBE_BROKEN_SYSCALL;
+      break;
+    }
+
+    /* Is this a new connection request ? */
+#ifdef HAVE_SYS_UN_H
+    if (FD_ISSET(unix_sock,&readFDs))
+    {
+      sock = unix_sock;
+      flags= socket_flags;
+    }
+    else
+#endif
+    {
+      sock = ip_sock;
+      flags= ip_flags;
+    }
+
+#if !defined(NO_FCNTL_NONBLOCK)
+    if (!(test_flags & TEST_BLOCKING))
+    {
+#if defined(O_NONBLOCK)
+      fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+#elif defined(O_NDELAY)
+      fcntl(sock, F_SETFL, flags | O_NDELAY);
+#endif
+    }
+#endif /* NO_FCNTL_NONBLOCK */
+    for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
+    {
+      size_socket length=sizeof(struct sockaddr_in);
+      new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
+			&length);
+#ifdef __NETWARE__
+      // TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149
+      if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL))
+      {
+        kill_server(SIGTERM);
+      }
+#endif
+      if (new_sock != INVALID_SOCKET ||
+	  (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
+	break;
+      MAYBE_BROKEN_SYSCALL;
+#if !defined(NO_FCNTL_NONBLOCK)
+      if (!(test_flags & TEST_BLOCKING))
+      {
+	if (retry == MAX_ACCEPT_RETRY - 1)
+	  fcntl(sock, F_SETFL, flags);		// Try without O_NONBLOCK
+      }
+#endif
+    }
+#if !defined(NO_FCNTL_NONBLOCK)
+    if (!(test_flags & TEST_BLOCKING))
+      fcntl(sock, F_SETFL, flags);
+#endif
+    if (new_sock == INVALID_SOCKET)
+    {
+      if ((error_count++ & 255) == 0)		// This can happen often
+	sql_perror("Error in accept");
+      MAYBE_BROKEN_SYSCALL;
+      if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
+	sleep(1);				// Give other threads some time
+      continue;
+    }
+
+#ifdef HAVE_LIBWRAP
+    {
+      if (sock == ip_sock)
+      {
+	struct request_info req;
+	signal(SIGCHLD, SIG_DFL);
+	request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL);
+	my_fromhost(&req);
+	if (!my_hosts_access(&req))
+	{
+	  /*
+	    This may be stupid but refuse() includes an exit(0)
+	    which we surely don't want...
+	    clean_exit() - same stupid thing ...
+	  */
+	  syslog(deny_severity, "refused connect from %s",
+		 my_eval_client(&req));
+
+	  /*
+	    C++ sucks (the gibberish in front just translates the supplied
+	    sink function pointer in the req structure from a void (*sink)();
+	    to a void(*sink)(int) if you omit the cast, the C++ compiler
+	    will cry...
+	  */
+	  if (req.sink)
+	    ((void (*)(int))req.sink)(req.fd);
+
+	  (void) shutdown(new_sock, SHUT_RDWR);
+	  (void) closesocket(new_sock);
+	  continue;
+	}
+      }
+    }
+#endif /* HAVE_LIBWRAP */
+
+    {
+      size_socket dummyLen;
+      struct sockaddr dummy;
+      dummyLen = sizeof(struct sockaddr);
+      if (getsockname(new_sock,&dummy, &dummyLen) < 0)
+      {
+	sql_perror("Error on new connection socket");
+	(void) shutdown(new_sock, SHUT_RDWR);
+	(void) closesocket(new_sock);
+	continue;
+      }
+    }
+
+    /*
+    ** Don't allow too many connections
+    */
+
+    if (!(thd= new THD))
+    {
+      (void) shutdown(new_sock, SHUT_RDWR);
+      VOID(closesocket(new_sock));
+      continue;
+    }
+    if (!(vio_tmp=vio_new(new_sock,
+			  sock == unix_sock ? VIO_TYPE_SOCKET :
+			  VIO_TYPE_TCPIP,
+			  sock == unix_sock ? VIO_LOCALHOST: 0)) ||
+	my_net_init(&thd->net,vio_tmp))
+    {
+      /*
+        Only delete the temporary vio if we didn't already attach it to the
+        NET object. The destructor in THD will delete any initialized net
+        structure.
+      */
+      if (vio_tmp && thd->net.vio != vio_tmp)
+        vio_delete(vio_tmp);
+      else
+      {
+	(void) shutdown(new_sock, SHUT_RDWR);
+	(void) closesocket(new_sock);
+      }
+      delete thd;
+      continue;
+    }
+    if (sock == unix_sock)
+      thd->security_ctx->host=(char*) my_localhost;
+
+    create_new_thread(thd);
+  }
+
+#ifdef OS2
+  // kill server must be invoked from thread 1!
+  kill_server(MYSQL_KILL_SIGNAL);
+#endif
+  decrement_handler_count();
+  DBUG_RETURN(0);
+}
+
+
+#ifdef __NT__
+pthread_handler_t handle_connections_namedpipes(void *arg)
+{
+  HANDLE hConnectedPipe;
+  BOOL fConnected;
+  THD *thd;
+  my_thread_init();
+  DBUG_ENTER("handle_connections_namedpipes");
+  (void) my_pthread_getprio(pthread_self());		// For debugging
+
+  DBUG_PRINT("general",("Waiting for named pipe connections."));
+  while (!abort_loop)
+  {
+    /* wait for named pipe connection */
+    fConnected = ConnectNamedPipe(hPipe, NULL);
+    if (abort_loop)
+      break;
+    if (!fConnected)
+      fConnected = GetLastError() == ERROR_PIPE_CONNECTED;
+    if (!fConnected)
+    {
+      CloseHandle(hPipe);
+      if ((hPipe= CreateNamedPipe(pipe_name,
+                                  PIPE_ACCESS_DUPLEX,
+                                  PIPE_TYPE_BYTE |
+                                  PIPE_READMODE_BYTE |
+                                  PIPE_WAIT,
+                                  PIPE_UNLIMITED_INSTANCES,
+                                  (int) global_system_variables.
+                                  net_buffer_length,
+                                  (int) global_system_variables.
+                                  net_buffer_length,
+                                  NMPWAIT_USE_DEFAULT_WAIT,
+                                  &saPipeSecurity)) ==
+	  INVALID_HANDLE_VALUE)
+      {
+	sql_perror("Can't create new named pipe!");
+	break;					// Abort
+      }
+    }
+    hConnectedPipe = hPipe;
+    /* create new pipe for new connection */
+    if ((hPipe = CreateNamedPipe(pipe_name,
+				 PIPE_ACCESS_DUPLEX,
+				 PIPE_TYPE_BYTE |
+				 PIPE_READMODE_BYTE |
+				 PIPE_WAIT,
+				 PIPE_UNLIMITED_INSTANCES,
+				 (int) global_system_variables.net_buffer_length,
+				 (int) global_system_variables.net_buffer_length,
+				 NMPWAIT_USE_DEFAULT_WAIT,
+				 &saPipeSecurity)) ==
+	INVALID_HANDLE_VALUE)
+    {
+      sql_perror("Can't create new named pipe!");
+      hPipe=hConnectedPipe;
+      continue;					// We have to try again
+    }
+
+    if (!(thd = new THD))
+    {
+      DisconnectNamedPipe(hConnectedPipe);
+      CloseHandle(hConnectedPipe);
+      continue;
+    }
+    if (!(thd->net.vio = vio_new_win32pipe(hConnectedPipe)) ||
+	my_net_init(&thd->net, thd->net.vio))
+    {
+      close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+      delete thd;
+      continue;
+    }
+    /* Host is unknown */
+    thd->security_ctx->host= my_strdup(my_localhost, MYF(0));
+    create_new_thread(thd);
+  }
+
+  decrement_handler_count();
+  DBUG_RETURN(0);
+}
+#endif /* __NT__ */
+
+
+/*
+  Thread of shared memory's service
+
+  SYNOPSIS
+    handle_connections_shared_memory()
+    arg                              Arguments of thread
+*/
+
+#ifdef HAVE_SMEM
+pthread_handler_t handle_connections_shared_memory(void *arg)
+{
+  /* file-mapping object, use for create shared memory */
+  HANDLE handle_connect_file_map= 0;
+  char  *handle_connect_map= 0;                 // pointer on shared memory
+  HANDLE event_connect_answer= 0;
+  ulong smem_buffer_length= shared_memory_buffer_length + 4;
+  ulong connect_number= 1;
+  char *tmp= NULL;
+  char *suffix_pos;
+  char connect_number_char[22], *p;
+  const char *errmsg= 0;
+  SECURITY_ATTRIBUTES *sa_event= 0, *sa_mapping= 0;
+  my_thread_init();
+  DBUG_ENTER("handle_connections_shared_memorys");
+  DBUG_PRINT("general",("Waiting for allocated shared memory."));
+
+  /*
+     get enough space base-name + '_' + longest suffix we might ever send
+   */
+  if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, MYF(MY_FAE))))
+    goto error;
+
+  if (my_security_attr_create(&sa_event, &errmsg,
+                              GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE))
+    goto error;
+
+  if (my_security_attr_create(&sa_mapping, &errmsg,
+                             GENERIC_ALL, FILE_MAP_READ | FILE_MAP_WRITE))
+    goto error;
+
+  /*
+    The name of event and file-mapping events create agree next rule:
+      shared_memory_base_name+unique_part
+    Where:
+      shared_memory_base_name is unique value for each server
+      unique_part is unique value for each object (events and file-mapping)
+  */
+  suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS);
+  strmov(suffix_pos, "CONNECT_REQUEST");
+  if ((smem_event_connect_request= CreateEvent(sa_event,
+                                               FALSE, FALSE, tmp)) == 0)
+  {
+    errmsg= "Could not create request event";
+    goto error;
+  }
+  strmov(suffix_pos, "CONNECT_ANSWER");
+  if ((event_connect_answer= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
+  {
+    errmsg="Could not create answer event";
+    goto error;
+  }
+  strmov(suffix_pos, "CONNECT_DATA");
+  if ((handle_connect_file_map=
+       CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping,
+                         PAGE_READWRITE, 0, sizeof(connect_number), tmp)) == 0)
+  {
+    errmsg= "Could not create file mapping";
+    goto error;
+  }
+  if ((handle_connect_map= (char *)MapViewOfFile(handle_connect_file_map,
+						  FILE_MAP_WRITE,0,0,
+						  sizeof(DWORD))) == 0)
+  {
+    errmsg= "Could not create shared memory service";
+    goto error;
+  }
+
+  while (!abort_loop)
+  {
+    /* Wait a request from client */
+    WaitForSingleObject(smem_event_connect_request,INFINITE);
+
+    /*
+       it can be after shutdown command
+    */
+    if (abort_loop)
+      goto error;
+
+    HANDLE handle_client_file_map= 0;
+    char  *handle_client_map= 0;
+    HANDLE event_client_wrote= 0;
+    HANDLE event_client_read= 0;    // for transfer data server <-> client
+    HANDLE event_server_wrote= 0;
+    HANDLE event_server_read= 0;
+    HANDLE event_conn_closed= 0;
+    THD *thd= 0;
+
+    p= int10_to_str(connect_number, connect_number_char, 10);
+    /*
+      The name of event and file-mapping events create agree next rule:
+        shared_memory_base_name+unique_part+number_of_connection
+        Where:
+	  shared_memory_base_name is uniquel value for each server
+	  unique_part is unique value for each object (events and file-mapping)
+	  number_of_connection is connection-number between server and client
+    */
+    suffix_pos= strxmov(tmp,shared_memory_base_name,"_",connect_number_char,
+			 "_",NullS);
+    strmov(suffix_pos, "DATA");
+    if ((handle_client_file_map=
+         CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping,
+                           PAGE_READWRITE, 0, smem_buffer_length, tmp)) == 0)
+    {
+      errmsg= "Could not create file mapping";
+      goto errorconn;
+    }
+    if ((handle_client_map= (char*)MapViewOfFile(handle_client_file_map,
+						  FILE_MAP_WRITE,0,0,
+						  smem_buffer_length)) == 0)
+    {
+      errmsg= "Could not create memory map";
+      goto errorconn;
+    }
+    strmov(suffix_pos, "CLIENT_WROTE");
+    if ((event_client_wrote= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
+    {
+      errmsg= "Could not create client write event";
+      goto errorconn;
+    }
+    strmov(suffix_pos, "CLIENT_READ");
+    if ((event_client_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
+    {
+      errmsg= "Could not create client read event";
+      goto errorconn;
+    }
+    strmov(suffix_pos, "SERVER_READ");
+    if ((event_server_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
+    {
+      errmsg= "Could not create server read event";
+      goto errorconn;
+    }
+    strmov(suffix_pos, "SERVER_WROTE");
+    if ((event_server_wrote= CreateEvent(sa_event,
+                                         FALSE, FALSE, tmp)) == 0)
+    {
+      errmsg= "Could not create server write event";
+      goto errorconn;
+    }
+    strmov(suffix_pos, "CONNECTION_CLOSED");
+    if ((event_conn_closed= CreateEvent(sa_event,
+                                        TRUE, FALSE, tmp)) == 0)
+    {
+      errmsg= "Could not create closed connection event";
+      goto errorconn;
+    }
+    if (abort_loop)
+      goto errorconn;
+    if (!(thd= new THD))
+      goto errorconn;
+    /* Send number of connection to client */
+    int4store(handle_connect_map, connect_number);
+    if (!SetEvent(event_connect_answer))
+    {
+      errmsg= "Could not send answer event";
+      goto errorconn;
+    }
+    /* Set event that client should receive data */
+    if (!SetEvent(event_client_read))
+    {
+      errmsg= "Could not set client to read mode";
+      goto errorconn;
+    }
+    if (!(thd->net.vio= vio_new_win32shared_memory(&thd->net,
+                                                   handle_client_file_map,
+                                                   handle_client_map,
+                                                   event_client_wrote,
+                                                   event_client_read,
+                                                   event_server_wrote,
+                                                   event_server_read,
+                                                   event_conn_closed)) ||
+                        my_net_init(&thd->net, thd->net.vio))
+    {
+      close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+      errmsg= 0;
+      goto errorconn;
+    }
+    thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); /* Host is unknown */
+    create_new_thread(thd);
+    connect_number++;
+    continue;
+
+errorconn:
+    /* Could not form connection;  Free used handlers/memort and retry */
+    if (errmsg)
+    {
+      char buff[180];
+      strxmov(buff, "Can't create shared memory connection: ", errmsg, ".",
+	      NullS);
+      sql_perror(buff);
+    }
+    if (handle_client_file_map)
+      CloseHandle(handle_client_file_map);
+    if (handle_client_map)
+      UnmapViewOfFile(handle_client_map);
+    if (event_server_wrote)
+      CloseHandle(event_server_wrote);
+    if (event_server_read)
+      CloseHandle(event_server_read);
+    if (event_client_wrote)
+      CloseHandle(event_client_wrote);
+    if (event_client_read)
+      CloseHandle(event_client_read);
+    if (event_conn_closed)
+      CloseHandle(event_conn_closed);
+    delete thd;
+  }
+
+  /* End shared memory handling */
+error:
+  if (tmp)
+    my_free(tmp, MYF(0));
+
+  if (errmsg)
+  {
+    char buff[180];
+    strxmov(buff, "Can't create shared memory service: ", errmsg, ".", NullS);
+    sql_perror(buff);
+  }
+  my_security_attr_free(sa_event);
+  my_security_attr_free(sa_mapping);
+  if (handle_connect_map)	UnmapViewOfFile(handle_connect_map);
+  if (handle_connect_file_map)	CloseHandle(handle_connect_file_map);
+  if (event_connect_answer)	CloseHandle(event_connect_answer);
+  if (smem_event_connect_request) CloseHandle(smem_event_connect_request);
+
+  decrement_handler_count();
+  DBUG_RETURN(0);
+}
+#endif /* HAVE_SMEM */
+#endif /* EMBEDDED_LIBRARY */
+
+
+/****************************************************************************
+  Handle start options
+******************************************************************************/
+
+enum options_mysqld
+{
+  OPT_ISAM_LOG=256,            OPT_SKIP_NEW,
+  OPT_SKIP_GRANT,              OPT_SKIP_LOCK,
+  OPT_ENABLE_LOCK,             OPT_USE_LOCKING,
+  OPT_SOCKET,                  OPT_UPDATE_LOG,
+  OPT_BIN_LOG,                 OPT_SKIP_RESOLVE,
+  OPT_SKIP_NETWORKING,         OPT_BIN_LOG_INDEX,
+  OPT_BIND_ADDRESS,            OPT_PID_FILE,
+  OPT_SKIP_PRIOR,              OPT_BIG_TABLES,
+  OPT_STANDALONE,              OPT_ONE_THREAD,
+  OPT_CONSOLE,                 OPT_LOW_PRIORITY_UPDATES,
+  OPT_SKIP_HOST_CACHE,         OPT_SHORT_LOG_FORMAT,
+  OPT_FLUSH,                   OPT_SAFE,
+  OPT_BOOTSTRAP,               OPT_SKIP_SHOW_DB,
+  OPT_STORAGE_ENGINE,          OPT_INIT_FILE,
+  OPT_DELAY_KEY_WRITE_ALL,     OPT_SLOW_QUERY_LOG,
+  OPT_DELAY_KEY_WRITE,	       OPT_CHARSETS_DIR,
+  OPT_BDB_HOME,                OPT_BDB_LOG,
+  OPT_BDB_TMP,                 OPT_BDB_SYNC,
+  OPT_BDB_LOCK,                OPT_BDB,
+  OPT_BDB_NO_RECOVER,	    OPT_BDB_SHARED,
+  OPT_MASTER_HOST,             OPT_MASTER_USER,
+  OPT_MASTER_PASSWORD,         OPT_MASTER_PORT,
+  OPT_MASTER_INFO_FILE,        OPT_MASTER_CONNECT_RETRY,
+  OPT_MASTER_RETRY_COUNT,      OPT_LOG_TC, OPT_LOG_TC_SIZE,
+  OPT_MASTER_SSL,              OPT_MASTER_SSL_KEY,
+  OPT_MASTER_SSL_CERT,         OPT_MASTER_SSL_CAPATH,
+  OPT_MASTER_SSL_CIPHER,       OPT_MASTER_SSL_CA,
+  OPT_SQL_BIN_UPDATE_SAME,     OPT_REPLICATE_DO_DB,
+  OPT_REPLICATE_IGNORE_DB,     OPT_LOG_SLAVE_UPDATES,
+  OPT_BINLOG_DO_DB,            OPT_BINLOG_IGNORE_DB,
+  OPT_WANT_CORE,               OPT_CONCURRENT_INSERT,
+  OPT_MEMLOCK,                 OPT_MYISAM_RECOVER,
+  OPT_REPLICATE_REWRITE_DB,    OPT_SERVER_ID,
+  OPT_SKIP_SLAVE_START,        OPT_SKIP_INNOBASE,
+  OPT_SAFEMALLOC_MEM_LIMIT,    OPT_REPLICATE_DO_TABLE,
+  OPT_REPLICATE_IGNORE_TABLE,  OPT_REPLICATE_WILD_DO_TABLE,
+  OPT_REPLICATE_WILD_IGNORE_TABLE, OPT_REPLICATE_SAME_SERVER_ID,
+  OPT_DISCONNECT_SLAVE_EVENT_COUNT, OPT_TC_HEURISTIC_RECOVER,
+  OPT_ABORT_SLAVE_EVENT_COUNT,
+  OPT_INNODB_DATA_HOME_DIR,
+  OPT_INNODB_DATA_FILE_PATH,
+  OPT_INNODB_LOG_GROUP_HOME_DIR,
+  OPT_INNODB_LOG_ARCH_DIR,
+  OPT_INNODB_LOG_ARCHIVE,
+  OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT,
+  OPT_INNODB_FLUSH_METHOD,
+  OPT_INNODB_DOUBLEWRITE,
+  OPT_INNODB_CHECKSUMS,
+  OPT_INNODB_FAST_SHUTDOWN,
+  OPT_INNODB_FILE_PER_TABLE, OPT_CRASH_BINLOG_INNODB,
+  OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
+  OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
+  OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG,
+  OPT_INNODB, OPT_ISAM,
+  OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, 
+  OPT_NDB_USE_EXACT_COUNT, OPT_NDB_USE_TRANSACTIONS,
+  OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ,
+  OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME,
+  OPT_NDB_MGMD, OPT_NDB_NODEID,
+  OPT_SKIP_SAFEMALLOC,
+  OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
+  OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
+  OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
+  OPT_SAFE_USER_CREATE, OPT_SQL_MODE,
+  OPT_HAVE_NAMED_PIPE,
+  OPT_DO_PSTACK, OPT_REPORT_HOST,
+  OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT,
+  OPT_SHOW_SLAVE_AUTH_INFO,
+  OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE,
+  OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE,
+  OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE,
+  OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE,
+  OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA,
+  OPT_SSL_CAPATH, OPT_SSL_CIPHER,
+  OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE,
+  OPT_CONNECT_TIMEOUT, OPT_DELAYED_INSERT_TIMEOUT,
+  OPT_DELAYED_INSERT_LIMIT, OPT_DELAYED_QUEUE_SIZE,
+  OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, OPT_FT_BOOLEAN_SYNTAX,
+  OPT_FT_MAX_WORD_LEN, OPT_FT_QUERY_EXPANSION_LIMIT, OPT_FT_STOPWORD_FILE,
+  OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
+  OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
+  OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
+  OPT_LONG_QUERY_TIME,
+  OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
+  OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
+  OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
+  OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE,
+  OPT_MAX_JOIN_SIZE, OPT_MAX_PREPARED_STMT_COUNT,
+  OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH,
+  OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS,
+  OPT_MAX_LENGTH_FOR_SORT_DATA,
+  OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE,
+  OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE,
+  OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
+  OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE,
+  OPT_MYISAM_STATS_METHOD,
+  OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT,
+  OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT,
+  OPT_OPEN_FILES_LIMIT,
+  OPT_PRELOAD_BUFFER_SIZE,
+  OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE,
+  OPT_QUERY_CACHE_TYPE, OPT_QUERY_CACHE_WLOCK_INVALIDATE, OPT_RECORD_BUFFER,
+  OPT_RECORD_RND_BUFFER, OPT_DIV_PRECINCREMENT, OPT_RELAY_LOG_SPACE_LIMIT,
+  OPT_RELAY_LOG_PURGE,
+  OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME,
+  OPT_SLAVE_TRANS_RETRIES, OPT_READONLY, OPT_DEBUGGING,
+  OPT_SORT_BUFFER, OPT_TABLE_CACHE,
+  OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
+  OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
+  OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS,
+  OPT_INNODB_MIRRORED_LOG_GROUPS,
+  OPT_INNODB_LOG_FILES_IN_GROUP,
+  OPT_INNODB_LOG_FILE_SIZE,
+  OPT_INNODB_LOG_BUFFER_SIZE,
+  OPT_INNODB_BUFFER_POOL_SIZE,
+  OPT_INNODB_BUFFER_POOL_AWE_MEM_MB,
+  OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
+  OPT_INNODB_MAX_PURGE_LAG,
+  OPT_INNODB_FILE_IO_THREADS,
+  OPT_INNODB_LOCK_WAIT_TIMEOUT,
+  OPT_INNODB_THREAD_CONCURRENCY,
+  OPT_INNODB_COMMIT_CONCURRENCY,
+  OPT_INNODB_FORCE_RECOVERY,
+  OPT_INNODB_STATUS_FILE,
+  OPT_INNODB_MAX_DIRTY_PAGES_PCT,
+  OPT_INNODB_TABLE_LOCKS,
+  OPT_INNODB_SUPPORT_XA,
+  OPT_INNODB_OPEN_FILES,
+  OPT_INNODB_AUTOEXTEND_INCREMENT,
+  OPT_INNODB_SYNC_SPIN_LOOPS,
+  OPT_INNODB_CONCURRENCY_TICKETS,
+  OPT_INNODB_THREAD_SLEEP_DELAY,
+  OPT_BDB_CACHE_SIZE,
+  OPT_BDB_LOG_BUFFER_SIZE,
+  OPT_BDB_MAX_LOCK,
+  OPT_ERROR_LOG_FILE,
+  OPT_DEFAULT_WEEK_FORMAT,
+  OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
+  OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
+  OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
+  OPT_SYNC_FRM, OPT_SYNC_BINLOG,
+  OPT_SYNC_REPLICATION,
+  OPT_SYNC_REPLICATION_SLAVE_ID,
+  OPT_SYNC_REPLICATION_TIMEOUT,
+  OPT_BDB_NOSYNC,
+  OPT_ENABLE_SHARED_MEMORY,
+  OPT_SHARED_MEMORY_BASE_NAME,
+  OPT_OLD_PASSWORDS,
+  OPT_EXPIRE_LOGS_DAYS,
+  OPT_GROUP_CONCAT_MAX_LEN,
+  OPT_DEFAULT_COLLATION,
+  OPT_CHARACTER_SET_CLIENT_HANDSHAKE,
+  OPT_CHARACTER_SET_FILESYSTEM,
+  OPT_LC_TIME_NAMES,
+  OPT_INIT_CONNECT,
+  OPT_INIT_SLAVE,
+  OPT_SECURE_AUTH,
+  OPT_DATE_FORMAT,
+  OPT_TIME_FORMAT,
+  OPT_DATETIME_FORMAT,
+  OPT_LOG_QUERIES_NOT_USING_INDEXES,
+  OPT_DEFAULT_TIME_ZONE,
+  OPT_SYSDATE_IS_NOW,
+  OPT_OPTIMIZER_SEARCH_DEPTH,
+  OPT_OPTIMIZER_PRUNE_LEVEL,
+  OPT_UPDATABLE_VIEWS_WITH_LIMIT,
+  OPT_SP_AUTOMATIC_PRIVILEGES,
+  OPT_MAX_SP_RECURSION_DEPTH,
+  OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
+  OPT_ENABLE_LARGE_PAGES,
+  OPT_TIMED_MUTEXES,
+  OPT_OLD_STYLE_USER_LIMITS,
+  OPT_LOG_SLOW_ADMIN_STATEMENTS,
+  OPT_TABLE_LOCK_WAIT_TIMEOUT,
+  OPT_PLUGIN_DIR,
