From 0d294e1b64745051c14f17d017f44a9ea005f3ab Mon Sep 17 00:00:00 2001
From: "venu@myvenu.com" <>
Date: Sat, 11 Jan 2003 00:36:13 -0800
Subject: [PATCH 1/2] Fix to make the tables re-inited for every execute call

---
 sql/sql_prepare.cc | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 092f7c7a497..3907995676f 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -628,7 +628,7 @@ static bool parse_prepare_query(PREP_STMT *stmt,
   Initialize parameter items in statement
 */
 
-static bool init_param_items( PREP_STMT *stmt)
+static bool init_param_items(PREP_STMT *stmt)
 {
   List<Item> &params= stmt->thd->lex.param_list;
   Item_param **to;
@@ -642,6 +642,24 @@ static bool init_param_items( PREP_STMT *stmt)
   return 0;
 }
 
+/*
+  Initialize stmt execution
+*/
+
+static void init_stmt_execute(PREP_STMT *stmt)
+{
+  THD *thd= stmt->thd;
+  TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select_lex.table_list.first;
+  
+  /*
+  TODO: When the new table structure is ready, then have a status bit 
+        to indicate the table is altered, and re-do the setup_* 
+        and open the tables back.
+  */
+  if (tables)
+    tables->table=0; //safety - nasty init
+}
+
 /*
   Parse the query and send the total number of parameters 
   and resultset metadata information back to client (if any), 
@@ -722,6 +740,8 @@ void mysql_stmt_execute(THD *thd, char *packet)
     DBUG_VOID_RETURN;
   }
 
+  init_stmt_execute(stmt);
+
   if (stmt->param_count && setup_params_data(stmt))
     DBUG_VOID_RETURN;
 

From 8e6a4c6201ab14414837dcba79cfb4180b92e18d Mon Sep 17 00:00:00 2001
From: "heikki@hundin.mysql.fi" <>
Date: Sun, 12 Jan 2003 23:58:56 +0200
Subject: [PATCH 2/2] ut0mem.c, row0sel.c, row0mysql.c, ut0mem.h, row0sel.h,
 row0mysql.h:   Test allocation of memory beforehand if we are trying to
 return a > 2 MB BLOB; normally InnoDB asserts if memory allocation fails
 ha_innodb.cc:   Do not fetch all columns if change_active_index() is called
 during a query; a sum(a), max(a) query seemed to do that, doing unnecessary
 copying (the change actually made in the previous bk ci)   Free BLOB heap of
 handle when MySQL calls some ::extra()'s

---
 innobase/include/row0mysql.h |  8 ++++++++
 innobase/include/row0sel.h   |  3 ++-
 innobase/include/ut0mem.h    | 10 ++++++++++
 innobase/row/row0mysql.c     | 13 +++++++++++++
 innobase/row/row0sel.c       | 37 +++++++++++++++++++++++++++++++-----
 innobase/ut/ut0mem.c         | 37 +++++++++++++++++++++++++++++++++++-
 sql/ha_innodb.cc             | 34 ++++++++++++++++++++++-----------
 7 files changed, 124 insertions(+), 18 deletions(-)

diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 25d2ab77007..972fabc74cf 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -52,6 +52,14 @@ row_mysql_read_var_ref_noninline(
 	ulint*	len,	/* out: variable-length field length */
 	byte*	field);	/* in: field */
 /***********************************************************************
+Frees the blob heap in prebuilt when no longer needed. */
+
+void
+row_mysql_prebuilt_free_blob_heap(
+/*==============================*/
+	row_prebuilt_t*	prebuilt);	/* in: prebuilt struct of a
+					ha_innobase:: table handle */
+/***********************************************************************
 Stores a reference to a BLOB in the MySQL format. */
 
 void
diff --git a/innobase/include/row0sel.h b/innobase/include/row0sel.h
index aa2da6fe5f6..cfc30852b87 100644
--- a/innobase/include/row0sel.h
+++ b/innobase/include/row0sel.h
@@ -115,7 +115,8 @@ row_search_for_mysql(
 /*=================*/
 					/* out: DB_SUCCESS,
 					DB_RECORD_NOT_FOUND, 
-					DB_END_OF_INDEX, or DB_DEADLOCK */
+					DB_END_OF_INDEX, DB_DEADLOCK,
+					or DB_TOO_BIG_RECORD */
 	byte*		buf,		/* in/out: buffer for the fetched
 					row in the MySQL format */
 	ulint		mode,		/* in: search mode PAGE_CUR_L, ... */
diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h
index 09e0d800685..d3d04d58596 100644
--- a/innobase/include/ut0mem.h
+++ b/innobase/include/ut0mem.h
@@ -50,6 +50,16 @@ ut_malloc(
 	                /* out, own: allocated memory */
         ulint   n);     /* in: number of bytes to allocate */
 /**************************************************************************
+Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs
+out. It cannot be used if we want to return an error message. Prints to
+stderr a message if fails. */
+
+ibool
+ut_test_malloc(
+/*===========*/
+			/* out: TRUE if succeeded */
+	ulint	n);	/* in: try to allocate this many bytes */
+/**************************************************************************
 Frees a memory bloock allocated with ut_malloc. */
 
 void
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index ba56b3071cd..7cef63d1337 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -58,6 +58,19 @@ row_mysql_read_var_ref_noninline(
 	return(row_mysql_read_var_ref(len, field));
 }
 
+/***********************************************************************
+Frees the blob heap in prebuilt when no longer needed. */
+
+void
+row_mysql_prebuilt_free_blob_heap(
+/*==============================*/
+	row_prebuilt_t*	prebuilt)	/* in: prebuilt struct of a
+					ha_innobase:: table handle */
+{
+	mem_heap_free(prebuilt->blob_heap);
+	prebuilt->blob_heap = NULL;
+}
+
 /***********************************************************************
 Stores a reference to a BLOB in the MySQL format. */
 
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index ea5b3020c08..34f951b0c8a 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2039,9 +2039,12 @@ Note that the template in prebuilt may advise us to copy only a few
 columns to mysql_rec, other columns are left blank. All columns may not
 be needed in the query. */
 static
-void
+ibool
 row_sel_store_mysql_rec(
 /*====================*/
+					/* out: TRUE if success, FALSE
+					if could not allocate memory for a
+					BLOB */
 	byte*		mysql_rec,	/* out: row in the MySQL format */
 	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct */
 	rec_t*		rec)		/* in: Innobase record in the index
@@ -2092,7 +2095,19 @@ row_sel_store_mysql_rec(
 			if (templ->type == DATA_BLOB) {
 
 				ut_a(prebuilt->templ_contains_blob);
-				
+
+				/* A heuristic test that we can allocate
+				the memory for a big BLOB. We have a safety
+				margin of 1000000 bytes. Since the test
+				takes some CPU time, we do not use for small
+				BLOBs. */
+
+				if (len > 2000000
+				    && !ut_test_malloc(len + 1000000)) {
+
+					return(FALSE);
+				}
+
 				/* Copy the BLOB data to the BLOB
 				heap of prebuilt */
 
@@ -2142,6 +2157,8 @@ row_sel_store_mysql_rec(
 			}
 		}
 	} 
+
+	return(TRUE);
 }
 
 /*************************************************************************
@@ -2526,7 +2543,8 @@ row_search_for_mysql(
 /*=================*/
 					/* out: DB_SUCCESS,
 					DB_RECORD_NOT_FOUND, 
-					DB_END_OF_INDEX, or DB_DEADLOCK */
+					DB_END_OF_INDEX, DB_DEADLOCK,
+					or DB_TOO_BIG_RECORD */
 	byte*		buf,		/* in/out: buffer for the fetched
 					row in the MySQL format */
 	ulint		mode,		/* in: search mode PAGE_CUR_L, ... */
@@ -2758,7 +2776,12 @@ row_search_for_mysql(
 #ifdef UNIV_SEARCH_DEBUG
 				ut_a(0 == cmp_dtuple_rec(search_tuple, rec));
 #endif 
-				row_sel_store_mysql_rec(buf, prebuilt, rec);
+				if (!row_sel_store_mysql_rec(buf, prebuilt,
+								rec)) {
+ 					err = DB_TOO_BIG_RECORD;
+
+ 					goto lock_wait_or_error;
+				}
 	
  				mtr_commit(&mtr);
 
@@ -3200,7 +3223,11 @@ rec_loop:
 						rec_get_size(rec));
 			mach_write_to_4(buf, rec_get_extra_size(rec) + 4);
 		} else {
-			row_sel_store_mysql_rec(buf, prebuilt, rec);
+			if (!row_sel_store_mysql_rec(buf, prebuilt, rec)) {
+				err = DB_TOO_BIG_RECORD;
+
+				goto lock_wait_or_error;
+			}
 		}
 
 		if (prebuilt->clust_index_was_generated) {
diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c
index 03f15031fdf..a5991d5683d 100644
--- a/innobase/ut/ut0mem.c
+++ b/innobase/ut/ut0mem.c
@@ -77,8 +77,9 @@ ut_malloc_low(
 	ret = malloc(n + sizeof(ut_mem_block_t));
 
 	if (ret == NULL) {
+		ut_print_timestamp(stderr);
 		fprintf(stderr,
-		"InnoDB: Fatal error: cannot allocate %lu bytes of\n"
+		"  InnoDB: Fatal error: cannot allocate %lu bytes of\n"
 		"InnoDB: memory with malloc! Total allocated memory\n"
 		"InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
 		"InnoDB: Cannot continue operation!\n"
@@ -134,6 +135,40 @@ ut_malloc(
         return(ut_malloc_low(n, TRUE));
 }
 
+/**************************************************************************
+Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs
+out. It cannot be used if we want to return an error message. Prints to
+stderr a message if fails. */
+
+ibool
+ut_test_malloc(
+/*===========*/
+			/* out: TRUE if succeeded */
+	ulint	n)	/* in: try to allocate this many bytes */
+{
+	void*	ret;
+
+	ret = malloc(n);
+
+	if (ret == NULL) {
+		ut_print_timestamp(stderr);
+		fprintf(stderr,
+		"  InnoDB: Error: cannot allocate %lu bytes of memory for\n"
+		"InnoDB: a BLOB with malloc! Total allocated memory\n"
+		"InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
+		"InnoDB: Check if you should increase the swap file or\n"
+		"InnoDB: ulimits of your operating system.\n"
+		"InnoDB: On FreeBSD check you have compiled the OS with\n"
+		"InnoDB: a big enough maximum process size.\n",
+		                  n, ut_total_allocated_memory, errno);
+		return(FALSE);
+	}
+
+	free(ret);
+
+	return(TRUE);
+}	
+
 /**************************************************************************
 Frees a memory block allocated with ut_malloc. */
 
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 0d56f216b3b..cfec8282e42 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -1624,6 +1624,12 @@ build_template(
 	}
 
 	if (prebuilt->select_lock_type == LOCK_X) {
+		/* In versions < 3.23.50 we always retrieved the clustered
+		index record if prebuilt->select_lock_type == LOCK_S,
+		but there is really not need for that, and in some cases
+		performance could be seriously degraded because the MySQL
+		optimizer did not know about our convention! */
+
 		/* 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. */
@@ -1632,12 +1638,6 @@ build_template(
 	}
 
 	if (templ_type == ROW_MYSQL_REC_FIELDS) {
-		/* In versions < 3.23.50 we always retrieved the clustered
-		index record if prebuilt->select_lock_type == LOCK_S,
-		but there is really not need for that, and in some cases
-		performance could be seriously degraded because the MySQL
-		optimizer did not know about our convention! */
-
 		index = prebuilt->index;
 	} else {
 		index = clust_index;
@@ -2506,11 +2506,13 @@ ha_innobase::change_active_index(
   dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
 			prebuilt->index->n_fields);
 
-  /* Maybe MySQL changes the active index for a handle also
-     during some queries, we do not know: then it is safest to build
-     the template such that all columns will be fetched */
+  /* 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 te 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_WHOLE_ROW);
+  build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
 
   DBUG_RETURN(0);
 }
@@ -3742,8 +3744,18 @@ ha_innobase::extra(
 	obsolete! */
 
 	switch (operation) {
+	        case HA_EXTRA_FLUSH:
+	                if (prebuilt->blob_heap) {
+	                        row_mysql_prebuilt_free_blob_heap(prebuilt);
+	                }
+	                break;
  		case HA_EXTRA_RESET:
-  		case HA_EXTRA_RESET_STATE:
+	                if (prebuilt->blob_heap) {
+	                        row_mysql_prebuilt_free_blob_heap(prebuilt);
+	                }
+	        	prebuilt->read_just_key = 0;
+	        	break;
+ 		case HA_EXTRA_RESET_STATE:
 	        	prebuilt->read_just_key = 0;
 	        	break;
 		case HA_EXTRA_NO_KEYREAD: