List:Commits« Previous MessageNext Message »
From:vasil.dimov Date:December 29 2011 2:35pm
Subject:bzr push into mysql-5.5 branch (vasil.dimov:3668 to 3673) Bug#11764622
View as plain text  
 3673 Vasil Dimov	2011-12-29
      Partial fix for Bug#11764622 57480: MEMORY LEAK WHEN HAVING 256+ TABLES
      
      Port vasil.dimov@strippedvtfnjatt from mysql-trunk

    modified:
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/handler/ha_innodb.h
      storage/innobase/include/row0sel.h
      storage/innobase/row/row0sel.c
 3672 Vasil Dimov	2011-12-29
      Partial fix for Bug#11764622 57480: MEMORY LEAK WHEN HAVING 256+ TABLES
      
      Port vasil.dimov@oracle.com-20111205082900-lx9om1joscejr25e from mysql-trunk

    modified:
      storage/innobase/dict/dict0load.c
      storage/innobase/include/data0data.h
      storage/innobase/include/data0data.ic
 3671 Vasil Dimov	2011-12-29
      Partial fix for Bug#11764622 57480: MEMORY LEAK WHEN HAVING 256+ TABLES
      
      Port vasil.dimov@oracle.com-20111205082831-7v1qu50hvd9hjr3g from mysql-trunk

    modified:
      storage/innobase/btr/btr0pcur.c
      storage/innobase/include/btr0pcur.h
      storage/innobase/include/row0mysql.h
      storage/innobase/row/row0mysql.c
      storage/innobase/row/row0sel.c
 3670 Vasil Dimov	2011-12-29
      Partial fix for Bug#11764622 57480: MEMORY LEAK WHEN HAVING 256+ TABLES
      
      Port vasil.dimov@stripped from mysql-trunk

    modified:
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/handler/handler0alter.cc
      storage/innobase/include/data0data.h
      storage/innobase/include/data0data.ic
      storage/innobase/include/row0mysql.h
      storage/innobase/row/row0mysql.c
 3669 Vasil Dimov	2011-12-29
      Partial fix for Bug#11764622 57480: MEMORY LEAK WHEN HAVING 256+ TABLES
      
      Port vasil.dimov@strippedf48dq1ouk86r from mysql-trunk

    modified:
      storage/innobase/pars/pars0pars.c
 3668 Marko Mäkelä	2011-12-28 [merge]
      Merge mysql-5.1 to mysql-5.5.

    modified:
      storage/innobase/buf/buf0buf.c
      storage/innobase/include/mem0mem.ic
      storage/innobase/include/univ.i
      storage/innobase/include/ut0mem.h
      storage/innobase/mem/mem0pool.c
      storage/innobase/os/os0proc.c
      storage/innobase/ut/ut0mem.c
=== modified file 'storage/innobase/btr/btr0pcur.c'
--- a/storage/innobase/btr/btr0pcur.c	revid:marko.makela@stripped103118-u4h9x122nv2j7zt8
+++ b/storage/innobase/btr/btr0pcur.c	revid:vasil.dimov@strippedx2wi239e
@@ -52,12 +52,13 @@ btr_pcur_create_for_mysql(void)
 }
 
 /**************************************************************//**
-Frees the memory for a persistent cursor object. */
+Resets a persistent cursor object, freeing ::old_rec_buf if it is
+allocated and resetting the other members to their initial values. */
 UNIV_INTERN
 void
-btr_pcur_free_for_mysql(
-/*====================*/
-	btr_pcur_t*	cursor)	/*!< in, own: persistent cursor */
+btr_pcur_reset(
+/*===========*/
+	btr_pcur_t*	cursor)	/*!< in, out: persistent cursor */
 {
 	if (cursor->old_rec_buf != NULL) {
 
@@ -66,6 +67,7 @@ btr_pcur_free_for_mysql(
 		cursor->old_rec_buf = NULL;
 	}
 
+	cursor->btr_cur.index = NULL;
 	cursor->btr_cur.page_cur.rec = NULL;
 	cursor->old_rec = NULL;
 	cursor->old_n_fields = 0;
@@ -73,7 +75,17 @@ btr_pcur_free_for_mysql(
 
 	cursor->latch_mode = BTR_NO_LATCHES;
 	cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
+}
 
+/**************************************************************//**
+Frees the memory for a persistent cursor object. */
+UNIV_INTERN
+void
+btr_pcur_free_for_mysql(
+/*====================*/
+	btr_pcur_t*	cursor)	/*!< in, own: persistent cursor */
+{
+	btr_pcur_reset(cursor);
 	mem_free(cursor);
 }
 

=== modified file 'storage/innobase/dict/dict0load.c'
--- a/storage/innobase/dict/dict0load.c	revid:marko.makela@stripped3118-u4h9x122nv2j7zt8
+++ b/storage/innobase/dict/dict0load.c	revid:vasil.dimov@strippedx2wi239e
@@ -2066,8 +2066,9 @@ static
 void
 dict_load_foreign_cols(
 /*===================*/
-	const char*	id,	/*!< in: foreign constraint id as a
-				null-terminated string */
+	const char*	id,	/*!< in: foreign constraint id, not
+				necessary '\0'-terminated */
+	ulint		id_len,	/*!< in: id length */
 	dict_foreign_t*	foreign)/*!< in: foreign constraint object */
 {
 	dict_table_t*	sys_foreign_cols;
@@ -2097,7 +2098,7 @@ dict_load_foreign_cols(
 	tuple = dtuple_create(foreign->heap, 1);
 	dfield = dtuple_get_nth_field(tuple, 0);
 
-	dfield_set_data(dfield, id, ut_strlen(id));
+	dfield_set_data(dfield, id, id_len);
 	dict_index_copy_types(tuple, sys_index, 1);
 
 	btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
@@ -2110,7 +2111,7 @@ dict_load_foreign_cols(
 		ut_a(!rec_get_deleted_flag(rec, 0));
 
 		field = rec_get_nth_field_old(rec, 0, &len);
-		ut_a(len == ut_strlen(id));
+		ut_a(len == id_len);
 		ut_a(ut_memcmp(id, field, len) == 0);
 
 		field = rec_get_nth_field_old(rec, 1, &len);
@@ -2139,8 +2140,9 @@ static
 ulint
 dict_load_foreign(
 /*==============*/
-	const char*	id,	/*!< in: foreign constraint id as a
-				null-terminated string */
+	const char*	id,	/*!< in: foreign constraint id, not
+				necessary '\0'-terminated */
+	ulint		id_len,	/*!< in: id length */
 	ibool		check_charsets,
 				/*!< in: TRUE=check charset compatibility */
 	ibool		check_recursive)
@@ -2176,7 +2178,7 @@ dict_load_foreign(
 	tuple = dtuple_create(heap2, 1);
 	dfield = dtuple_get_nth_field(tuple, 0);
 
-	dfield_set_data(dfield, id, ut_strlen(id));
+	dfield_set_data(dfield, id, id_len);
 	dict_index_copy_types(tuple, sys_index, 1);
 
 	btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
@@ -2188,8 +2190,8 @@ dict_load_foreign(
 		/* Not found */
 
 		fprintf(stderr,
-			"InnoDB: Error A: cannot load foreign constraint %s\n",
-			id);
+			"InnoDB: Error A: cannot load foreign constraint "
+			"%.*s\n", (int) id_len, id);
 
 		btr_pcur_close(&pcur);
 		mtr_commit(&mtr);
@@ -2201,11 +2203,11 @@ dict_load_foreign(
 	field = rec_get_nth_field_old(rec, 0, &len);
 
 	/* Check if the id in record is the searched one */
-	if (len != ut_strlen(id) || ut_memcmp(id, field, len) != 0) {
+	if (len != id_len || ut_memcmp(id, field, len) != 0) {
 
 		fprintf(stderr,
-			"InnoDB: Error B: cannot load foreign constraint %s\n",
-			id);
+			"InnoDB: Error B: cannot load foreign constraint "
+			"%.*s\n", (int) id_len, id);
 
 		btr_pcur_close(&pcur);
 		mtr_commit(&mtr);
@@ -2231,7 +2233,7 @@ dict_load_foreign(
 	foreign->type = (unsigned int) (n_fields_and_type >> 24);
 	foreign->n_fields = (unsigned int) (n_fields_and_type & 0x3FFUL);
 
-	foreign->id = mem_heap_strdup(foreign->heap, id);
+	foreign->id = mem_heap_strdupl(foreign->heap, id, id_len);
 
 	field = rec_get_nth_field_old(rec, 3, &len);
 
@@ -2247,7 +2249,7 @@ dict_load_foreign(
 	btr_pcur_close(&pcur);
 	mtr_commit(&mtr);
 
-	dict_load_foreign_cols(id, foreign);
+	dict_load_foreign_cols(id, id_len, foreign);
 
 	ref_table = dict_table_check_if_in_cache_low(
 			foreign->referenced_table_name_lookup);
@@ -2326,8 +2328,8 @@ dict_load_foreigns(
 	ibool		check_charsets)	/*!< in: TRUE=check charset
 					compatibility */
 {
+	char		tuple_buf[DTUPLE_EST_ALLOC(1)];
 	btr_pcur_t	pcur;
-	mem_heap_t*	heap;
 	dtuple_t*	tuple;
 	dfield_t*	dfield;
 	dict_index_t*	sec_index;
@@ -2335,7 +2337,6 @@ dict_load_foreigns(
 	const rec_t*	rec;
 	const byte*	field;
 	ulint		len;
-	char*		id ;
 	ulint		err;
 	mtr_t		mtr;
 
@@ -2362,9 +2363,8 @@ dict_load_foreigns(
 	sec_index = dict_table_get_next_index(
 		dict_table_get_first_index(sys_foreign));
 start_load:
-	heap = mem_heap_create(256);
 
-	tuple  = dtuple_create(heap, 1);
+	tuple = dtuple_create_from_mem(tuple_buf, sizeof(tuple_buf), 1);
 	dfield = dtuple_get_nth_field(tuple, 0);
 
 	dfield_set_data(dfield, table_name, ut_strlen(table_name));
@@ -2418,7 +2418,6 @@ loop:
 
 	/* Now we get a foreign key constraint id */
 	field = rec_get_nth_field_old(rec, 1, &len);
-	id = mem_heap_strdupl(heap, (char*) field, len);
 
 	btr_pcur_store_position(&pcur, &mtr);
 
@@ -2426,11 +2425,11 @@ loop:
 
 	/* Load the foreign constraint definition to the dictionary cache */
 
-	err = dict_load_foreign(id, check_charsets, check_recursive);
+	err = dict_load_foreign((char*) field, len, check_charsets,
+				check_recursive);
 
 	if (err != DB_SUCCESS) {
 		btr_pcur_close(&pcur);
-		mem_heap_free(heap);
 
 		return(err);
 	}
@@ -2446,7 +2445,6 @@ next_rec:
 load_next_index:
 	btr_pcur_close(&pcur);
 	mtr_commit(&mtr);
-	mem_heap_free(heap);
 
 	sec_index = dict_table_get_next_index(sec_index);
 

=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc	revid:marko.makela@stripped4h9x122nv2j7zt8
+++ b/storage/innobase/handler/ha_innodb.cc	revid:vasil.dimov@strippedwi239e
@@ -3737,22 +3737,9 @@ ha_innobase::open(
 		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 (!(uchar*) 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);
-	}
+	/* Will be allocated if it is needed in ::update_row() */
+	upd_buf = NULL;
+	upd_buf_size = 0;
 
 	/* We look for pattern #P# to see if the table is partitioned
 	MySQL table. The retry logic for partitioned tables is a
@@ -3793,7 +3780,6 @@ retry:
 				"how you can resolve the problem.\n",
 				norm_name);
 		free_share(share);
-		my_free(upd_buff);
 		my_errno = ENOENT;
 
 		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
@@ -3809,16 +3795,14 @@ retry:
 				"how you can resolve the problem.\n",
 				norm_name);
 		free_share(share);
-		my_free(upd_buff);
 		my_errno = ENOENT;
 
 		dict_table_decrement_handle_count(ib_table, FALSE);
 		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
 	}
 
-	prebuilt = row_create_prebuilt(ib_table);
+	prebuilt = row_create_prebuilt(ib_table, table->s->reclength);
 
-	prebuilt->mysql_row_len = table->s->reclength;
 	prebuilt->default_rec = table->s->default_values;
 	ut_ad(prebuilt->default_rec);
 
@@ -4007,7 +3991,13 @@ ha_innobase::close(void)
 
 	row_prebuilt_free(prebuilt, FALSE);
 
-	my_free(upd_buff);
+	if (upd_buf != NULL) {
+		ut_ad(upd_buf_size != 0);
+		my_free(upd_buf);
+		upd_buf = NULL;
+		upd_buf_size = 0;
+	}
+
 	free_share(share);
 
 	/* Tell InnoDB server that there might be work for
@@ -5300,6 +5290,23 @@ ha_innobase::update_row(
 
 	ut_a(prebuilt->trx == trx);
 
+	if (upd_buf == NULL) {
+		ut_ad(upd_buf_size == 0);
+
+		/* Create a buffer 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_buf_size = table->s->reclength + table->s->max_key_length
+			+ MAX_REF_PARTS * 3;
+		upd_buf = (uchar*) my_malloc(upd_buf_size, MYF(MY_WME));
+		if (upd_buf == NULL) {
+			upd_buf_size = 0;
+			DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+		}
+	}
+
 	ha_statistic_increment(&SSV::ha_update_count);
 
 	if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
@@ -5312,11 +5319,10 @@ ha_innobase::update_row(
 	}
 
 	/* Build an update vector from the modified fields in the rows
-	(uses upd_buff of the handle) */
+	(uses upd_buf of the handle) */
 
 	calc_row_difference(uvect, (uchar*) old_row, new_row, table,
-			upd_buff, (ulint)upd_and_key_val_buff_len,
-			prebuilt, user_thd);
+			    upd_buf, upd_buf_size, prebuilt, user_thd);
 
 	/* This is not a delete */
 	prebuilt->upd_node->is_delete = FALSE;
@@ -5693,8 +5699,7 @@ ha_innobase::index_read(
 
 		row_sel_convert_mysql_key_to_innobase(
 			prebuilt->search_tuple,
-			(byte*) key_val_buff,
-			(ulint)upd_and_key_val_buff_len,
+			srch_key_val1, sizeof(srch_key_val1),
 			index,
 			(byte*) key_ptr,
 			(ulint) key_len,
@@ -7512,12 +7517,6 @@ ha_innobase::records_in_range(
 {
 	KEY*		key;
 	dict_index_t*	index;
-	uchar*		key_val_buff2	= (uchar*) 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_int64_t	n_rows;
@@ -7569,8 +7568,8 @@ ha_innobase::records_in_range(
 	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,
+				range_start,
+				srch_key_val1, sizeof(srch_key_val1),
 				index,
 				(byte*) (min_key ? min_key->key :
 					 (const uchar*) 0),
@@ -7581,8 +7580,9 @@ ha_innobase::records_in_range(
 		    : range_start->n_fields == 0);
 
 	row_sel_convert_mysql_key_to_innobase(
-				range_end, (byte*) key_val_buff2,
-				buff2_len, index,
+				range_end,
+				srch_key_val2, sizeof(srch_key_val2),
+				index,
 				(byte*) (max_key ? max_key->key :
 					 (const uchar*) 0),
 				(ulint) (max_key ? max_key->length : 0),
@@ -7609,7 +7609,6 @@ ha_innobase::records_in_range(
 	mem_heap_free(heap);
 
 func_exit:
-	my_free(key_val_buff2);
 
 	prebuilt->trx->op_info = (char*)"";
 

=== modified file 'storage/innobase/handler/ha_innodb.h'
--- a/storage/innobase/handler/ha_innodb.h	revid:marko.makela@strippedm-20111228103118-u4h9x122nv2j7zt8
+++ b/storage/innobase/handler/ha_innodb.h	revid:vasil.dimov@stripped141933-d1onx8j7x2wi239e
@@ -78,13 +78,14 @@ class ha_innobase: public handler
 	INNOBASE_SHARE*	share;		/*!< information for MySQL
 					table locking */
 
-	uchar*		upd_buff;	/*!< buffer used in updates */
-	uchar*		key_val_buff;	/*!< buffer used in converting
+	uchar*		upd_buf;	/*!< buffer used in updates */
+	ulint		upd_buf_size;	/*!< the size of upd_buf in bytes */
+	uchar		srch_key_val1[REC_VERSION_56_MAX_INDEX_COL_LEN + 2];
+	uchar		srch_key_val2[REC_VERSION_56_MAX_INDEX_COL_LEN + 2];
+					/*!< buffers used in converting
 					search key values from MySQL format
-					to Innodb format */
-	ulong		upd_and_key_val_buff_len;
-					/* the length of each of the previous
-					two buffers */
+					to InnoDB format. "+ 2" for the two
+					bytes where the length is stored */
 	Table_flags	int_table_flags;
 	uint		primary_key;
 	ulong		start_of_scan;	/*!< this is set to 1 when we are

=== modified file 'storage/innobase/handler/handler0alter.cc'
--- a/storage/innobase/handler/handler0alter.cc	revid:marko.makela@oracle.com-20111228103118-u4h9x122nv2j7zt8
+++ b/storage/innobase/handler/handler0alter.cc	revid:vasil.dimov@oracle.com-20111229141933-d1onx8j7x2wi239e
@@ -1008,7 +1008,12 @@ ha_innobase::final_add_index(
 			row_prebuilt_free(prebuilt, TRUE);
 			error = row_merge_drop_table(trx, old_table);
 			add->indexed_table->n_mysql_handles_opened++;
-			prebuilt = row_create_prebuilt(add->indexed_table);
+			prebuilt = row_create_prebuilt(add->indexed_table,
+				0 /* XXX Do we know the mysql_row_len here?
+				Before the addition of this parameter to
+				row_create_prebuilt() the mysql_row_len
+				member was left 0 (from zalloc) in the
+				prebuilt object. */);
 		}
 
 		err = convert_error_code_to_mysql(

=== modified file 'storage/innobase/include/btr0pcur.h'
--- a/storage/innobase/include/btr0pcur.h	revid:marko.makela@strippedu4h9x122nv2j7zt8
+++ b/storage/innobase/include/btr0pcur.h	revid:vasil.dimov@strippedi239e
@@ -53,6 +53,16 @@ UNIV_INTERN
 btr_pcur_t*
 btr_pcur_create_for_mysql(void);
 /*============================*/
+
+/**************************************************************//**
+Resets a persistent cursor object, freeing ::old_rec_buf if it is
+allocated and resetting the other members to their initial values. */
+UNIV_INTERN
+void
+btr_pcur_reset(
+/*===========*/
+	btr_pcur_t*	cursor);/*!< in, out: persistent cursor */
+
 /**************************************************************//**
 Frees the memory for a persistent cursor object. */
 UNIV_INTERN

=== modified file 'storage/innobase/include/data0data.h'
--- a/storage/innobase/include/data0data.h	revid:marko.makela@stripped
+++ b/storage/innobase/include/data0data.h	revid:vasil.dimov@oracle.com-20111229141933-d1onx8j7x2wi239e
@@ -231,6 +231,26 @@ dtuple_set_n_fields_cmp(
 	dtuple_t*	tuple,		/*!< in: tuple */
 	ulint		n_fields_cmp);	/*!< in: number of fields used in
 					comparisons in rem0cmp.* */
+
+/* Estimate the number of bytes that are going to be allocated when
+creating a new dtuple_t object */
+#define DTUPLE_EST_ALLOC(n_fields)	\
+	(sizeof(dtuple_t) + (n_fields) * sizeof(dfield_t))
+
+/**********************************************************//**
+Creates a data tuple from an already allocated chunk of memory.
+The size of the chunk must be at least DTUPLE_EST_ALLOC(n_fields).
+The default value for number of fields used in record comparisons
+for this tuple is n_fields.
+@return	created tuple (inside buf) */
+UNIV_INLINE
+dtuple_t*
+dtuple_create_from_mem(
+/*===================*/
+	void*	buf,		/*!< in, out: buffer to use */
+	ulint	buf_size,	/*!< in: buffer size */
+	ulint	n_fields);	/*!< in: number of fields */
+
 /**********************************************************//**
 Creates a data tuple to a memory heap. The default value for number
 of fields used in record comparisons for this tuple is n_fields.
@@ -240,7 +260,8 @@ dtuple_t*
 dtuple_create(
 /*==========*/
 	mem_heap_t*	heap,	/*!< in: memory heap where the tuple
-				is created */
+				is created, DTUPLE_EST_ALLOC(n_fields)
+				bytes will be allocated from this heap */
 	ulint		n_fields); /*!< in: number of fields */
 
 /**********************************************************//**

=== modified file 'storage/innobase/include/data0data.ic'
--- a/storage/innobase/include/data0data.ic	revid:marko.makela@stripped8-u4h9x122nv2j7zt8
+++ b/storage/innobase/include/data0data.ic	revid:vasil.dimov@stripped7x2wi239e
@@ -348,23 +348,25 @@ dtuple_get_nth_field(
 #endif /* UNIV_DEBUG */
 
 /**********************************************************//**
-Creates a data tuple to a memory heap. The default value for number
-of fields used in record comparisons for this tuple is n_fields.
-@return	own: created tuple */
+Creates a data tuple from an already allocated chunk of memory.
+The size of the chunk must be at least DTUPLE_EST_ALLOC(n_fields).
+The default value for number of fields used in record comparisons
+for this tuple is n_fields.
+@return	created tuple (inside buf) */
 UNIV_INLINE
 dtuple_t*
-dtuple_create(
-/*==========*/
-	mem_heap_t*	heap,	/*!< in: memory heap where the tuple
-				is created */
-	ulint		n_fields) /*!< in: number of fields */
+dtuple_create_from_mem(
+/*===================*/
+	void*	buf,		/*!< in, out: buffer to use */
+	ulint	buf_size,	/*!< in: buffer size */
+	ulint	n_fields)	/*!< in: number of fields */
 {
 	dtuple_t*	tuple;
 
-	ut_ad(heap);
+	ut_ad(buf != NULL);
+	ut_a(buf_size >= DTUPLE_EST_ALLOC(n_fields));
 
-	tuple = (dtuple_t*) mem_heap_alloc(heap, sizeof(dtuple_t)
-					   + n_fields * sizeof(dfield_t));
+	tuple = (dtuple_t*) buf;
 	tuple->info_bits = 0;
 	tuple->n_fields = n_fields;
 	tuple->n_fields_cmp = n_fields;
@@ -386,9 +388,38 @@ dtuple_create(
 			dfield_get_type(field)->mtype = DATA_ERROR;
 		}
 	}
+#endif
+	return(tuple);
+}
+
+/**********************************************************//**
+Creates a data tuple to a memory heap. The default value for number
+of fields used in record comparisons for this tuple is n_fields.
+@return	own: created tuple */
+UNIV_INLINE
+dtuple_t*
+dtuple_create(
+/*==========*/
+	mem_heap_t*	heap,	/*!< in: memory heap where the tuple
+				is created, DTUPLE_EST_ALLOC(n_fields)
+				bytes will be allocated from this heap */
+	ulint		n_fields) /*!< in: number of fields */
+{
+	void*		buf;
+	ulint		buf_size;
+	dtuple_t*	tuple;
+
+	ut_ad(heap);
+
+	buf_size = DTUPLE_EST_ALLOC(n_fields);
+	buf = mem_heap_alloc(heap, buf_size);
 
+	tuple = dtuple_create_from_mem(buf, buf_size, n_fields);
+
+#ifdef UNIV_DEBUG
 	UNIV_MEM_INVALID(tuple->fields, n_fields * sizeof *tuple->fields);
 #endif
+
 	return(tuple);
 }
 

=== modified file 'storage/innobase/include/row0mysql.h'
--- a/storage/innobase/include/row0mysql.h	revid:marko.makela@oracle.com-20111228103118-u4h9x122nv2j7zt8
+++ b/storage/innobase/include/row0mysql.h	revid:vasil.dimov@stripped0111229141933-d1onx8j7x2wi239e
@@ -168,7 +168,9 @@ UNIV_INTERN
 row_prebuilt_t*
 row_create_prebuilt(
 /*================*/
-	dict_table_t*	table);	/*!< in: Innobase table handle */
+	dict_table_t*	table,		/*!< in: Innobase table handle */
+	ulint		mysql_row_len);	/*!< in: length in bytes of a row in
+					the MySQL format */
 /********************************************************************//**
 Free a prebuilt struct for a MySQL table handle. */
 UNIV_INTERN
@@ -672,9 +674,9 @@ struct row_prebuilt_struct {
 					in inserts */
 	que_fork_t*	upd_graph;	/*!< Innobase SQL query graph used
 					in updates or deletes */
-	btr_pcur_t*	pcur;		/*!< persistent cursor used in selects
+	btr_pcur_t	pcur;		/*!< persistent cursor used in selects
 					and updates */
-	btr_pcur_t*	clust_pcur;	/*!< persistent cursor used in
+	btr_pcur_t	clust_pcur;	/*!< persistent cursor used in
 					some selects and updates */
 	que_fork_t*	sel_graph;	/*!< dummy query graph used in
 					selects */

=== modified file 'storage/innobase/include/row0sel.h'
--- a/storage/innobase/include/row0sel.h	revid:marko.makela@strippedv2j7zt8
+++ b/storage/innobase/include/row0sel.h	revid:vasil.dimov@stripped
@@ -128,7 +128,12 @@ row_sel_convert_mysql_key_to_innobase(
 					in the tuple is already according
 					to index! */
 	byte*		buf,		/*!< in: buffer to use in field
-					conversions */
+					conversions; NOTE that dtuple->data
+					may end up pointing inside buf so
+					do not discard that buffer while
+					the tuple is being used. See
+					row_mysql_store_col_in_innobase_format()
+					in the case of DATA_INT */
 	ulint		buf_len,	/*!< in: buffer length */
 	dict_index_t*	index,		/*!< in: index of the key value */
 	const byte*	key_ptr,	/*!< in: MySQL key value */

=== modified file 'storage/innobase/pars/pars0pars.c'
--- a/storage/innobase/pars/pars0pars.c	revid:marko.makela@stripped
+++ b/storage/innobase/pars/pars0pars.c	revid:vasil.dimov@stripped-20111229141933-d1onx8j7x2wi239e
@@ -1857,7 +1857,7 @@ pars_sql(
 
 	ut_ad(str);
 
-	heap = mem_heap_create(256);
+	heap = mem_heap_create(16000);
 
 	/* Currently, the parser is not reentrant: */
 	ut_ad(mutex_own(&(dict_sys->mutex)));

=== modified file 'storage/innobase/row/row0mysql.c'
--- a/storage/innobase/row/row0mysql.c	revid:marko.makela@stripped
+++ b/storage/innobase/row/row0mysql.c	revid:vasil.dimov@oracle.com-20111229141933-d1onx8j7x2wi239e
@@ -667,17 +667,60 @@ UNIV_INTERN
 row_prebuilt_t*
 row_create_prebuilt(
 /*================*/
-	dict_table_t*	table)	/*!< in: Innobase table handle */
+	dict_table_t*	table,		/*!< in: Innobase table handle */
+	ulint		mysql_row_len)	/*!< in: length in bytes of a row in
+					the MySQL format */
 {
 	row_prebuilt_t*	prebuilt;
 	mem_heap_t*	heap;
 	dict_index_t*	clust_index;
 	dtuple_t*	ref;
 	ulint		ref_len;
+	ulint		search_tuple_n_fields;
+
+	search_tuple_n_fields = 2 * dict_table_get_n_cols(table);
+
+	clust_index = dict_table_get_first_index(table);
+
+	/* Make sure that search_tuple is long enough for clustered index */
+	ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields);
+
+	ref_len = dict_index_get_n_unique(clust_index);
 
-	heap = mem_heap_create(sizeof *prebuilt + 128);
+#define PREBUILT_HEAP_INITIAL_SIZE	\
+	( \
+	sizeof(*prebuilt) \
+	/* allocd in this function */ \
+	+ DTUPLE_EST_ALLOC(search_tuple_n_fields) \
+	+ DTUPLE_EST_ALLOC(ref_len) \
+	/* allocd in row_prebuild_sel_graph() */ \
+	+ sizeof(sel_node_t) \
+	+ sizeof(que_fork_t) \
+	+ sizeof(que_thr_t) \
+	/* allocd in row_get_prebuilt_update_vector() */ \
+	+ sizeof(upd_node_t) \
+	+ sizeof(upd_t) \
+	+ sizeof(upd_field_t) \
+	  * dict_table_get_n_cols(table) \
+	+ sizeof(que_fork_t) \
+	+ sizeof(que_thr_t) \
+	/* allocd in row_get_prebuilt_insert_row() */ \
+	+ sizeof(ins_node_t) \
+	/* mysql_row_len could be huge and we are not \
+	sure if this prebuilt instance is going to be \
+	used in inserts */ \
+	+ (mysql_row_len < 256 ? mysql_row_len : 0) \
+	+ DTUPLE_EST_ALLOC(dict_table_get_n_cols(table)) \
+	+ sizeof(que_fork_t) \
+	+ sizeof(que_thr_t) \
+	)
+
+	/* We allocate enough space for the objects that are likely to
+	be created later in order to minimize the number of malloc()
+	calls */
+	heap = mem_heap_create(PREBUILT_HEAP_INITIAL_SIZE);
 
-	prebuilt = mem_heap_zalloc(heap, sizeof *prebuilt);
+	prebuilt = mem_heap_zalloc(heap, sizeof(*prebuilt));
 
 	prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
 	prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
@@ -687,23 +730,15 @@ row_create_prebuilt(
 	prebuilt->sql_stat_start = TRUE;
 	prebuilt->heap = heap;
 
-	prebuilt->pcur = btr_pcur_create_for_mysql();
-	prebuilt->clust_pcur = btr_pcur_create_for_mysql();
+	btr_pcur_reset(&prebuilt->pcur);
+	btr_pcur_reset(&prebuilt->clust_pcur);
 
 	prebuilt->select_lock_type = LOCK_NONE;
 	prebuilt->stored_select_lock_type = 99999999;
 	UNIV_MEM_INVALID(&prebuilt->stored_select_lock_type,
 			 sizeof prebuilt->stored_select_lock_type);
 
-	prebuilt->search_tuple = dtuple_create(
-		heap, 2 * dict_table_get_n_cols(table));
-
-	clust_index = dict_table_get_first_index(table);
-
-	/* Make sure that search_tuple is long enough for clustered index */
-	ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields);
-
-	ref_len = dict_index_get_n_unique(clust_index);
+	prebuilt->search_tuple = dtuple_create(heap, search_tuple_n_fields);
 
 	ref = dtuple_create(heap, ref_len);
 
@@ -720,6 +755,8 @@ row_create_prebuilt(
 
 	prebuilt->autoinc_last_value = 0;
 
+	prebuilt->mysql_row_len = mysql_row_len;
+
 	return(prebuilt);
 }
 
@@ -755,8 +792,8 @@ row_prebuilt_free(
 	prebuilt->magic_n = ROW_PREBUILT_FREED;
 	prebuilt->magic_n2 = ROW_PREBUILT_FREED;
 
-	btr_pcur_free_for_mysql(prebuilt->pcur);
-	btr_pcur_free_for_mysql(prebuilt->clust_pcur);
+	btr_pcur_reset(&prebuilt->pcur);
+	btr_pcur_reset(&prebuilt->clust_pcur);
 
 	if (prebuilt->mysql_template) {
 		mem_free(prebuilt->mysql_template);
@@ -1416,11 +1453,11 @@ row_update_for_mysql(
 
 	clust_index = dict_table_get_first_index(table);
 
-	if (prebuilt->pcur->btr_cur.index == clust_index) {
-		btr_pcur_copy_stored_position(node->pcur, prebuilt->pcur);
+	if (prebuilt->pcur.btr_cur.index == clust_index) {
+		btr_pcur_copy_stored_position(node->pcur, &prebuilt->pcur);
 	} else {
 		btr_pcur_copy_stored_position(node->pcur,
-					      prebuilt->clust_pcur);
+					      &prebuilt->clust_pcur);
 	}
 
 	ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
@@ -1524,8 +1561,8 @@ row_unlock_for_mysql(
 					clust_pcur, and we do not need
 					to reposition the cursors. */
 {
-	btr_pcur_t*	pcur		= prebuilt->pcur;
-	btr_pcur_t*	clust_pcur	= prebuilt->clust_pcur;
+	btr_pcur_t*	pcur		= &prebuilt->pcur;
+	btr_pcur_t*	clust_pcur	= &prebuilt->clust_pcur;
 	trx_t*		trx		= prebuilt->trx;
 
 	ut_ad(prebuilt && trx);

=== modified file 'storage/innobase/row/row0sel.c'
--- a/storage/innobase/row/row0sel.c	revid:marko.makela@stripped
+++ b/storage/innobase/row/row0sel.c	revid:vasil.dimov@oracle.com-20111229141933-d1onx8j7x2wi239e
@@ -2301,7 +2301,12 @@ row_sel_convert_mysql_key_to_innobase(
 					in the tuple is already according
 					to index! */
 	byte*		buf,		/*!< in: buffer to use in field
-					conversions */
+					conversions; NOTE that dtuple->data
+					may end up pointing inside buf so
+					do not discard that buffer while
+					the tuple is being used. See
+					row_mysql_store_col_in_innobase_format()
+					in the case of DATA_INT */
 	ulint		buf_len,	/*!< in: buffer length */
 	dict_index_t*	index,		/*!< in: index of the key value */
 	const byte*	key_ptr,	/*!< in: MySQL key value */
@@ -2433,6 +2438,7 @@ row_sel_convert_mysql_key_to_innobase(
 		/* Storing may use at most data_len bytes of buf */
 
 		if (UNIV_LIKELY(!is_null)) {
+			ut_a(buf + data_len <= original_buf + buf_len);
 			row_mysql_store_col_in_innobase_format(
 				dfield, buf,
 				FALSE, /* MySQL key value format col */
@@ -2915,17 +2921,17 @@ row_sel_get_clust_rec_for_mysql(
 
 	btr_pcur_open_with_no_init(clust_index, prebuilt->clust_ref,
 				   PAGE_CUR_LE, BTR_SEARCH_LEAF,
-				   prebuilt->clust_pcur, 0, mtr);
+				   &prebuilt->clust_pcur, 0, mtr);
 
-	clust_rec = btr_pcur_get_rec(prebuilt->clust_pcur);
+	clust_rec = btr_pcur_get_rec(&prebuilt->clust_pcur);
 
-	prebuilt->clust_pcur->trx_if_known = trx;
+	prebuilt->clust_pcur.trx_if_known = trx;
 
 	/* Note: only if the search ends up on a non-infimum record is the
 	low_match value the real match to the search tuple */
 
 	if (!page_rec_is_user_rec(clust_rec)
-	    || btr_pcur_get_low_match(prebuilt->clust_pcur)
+	    || btr_pcur_get_low_match(&prebuilt->clust_pcur)
 	    < dict_index_get_n_unique(clust_index)) {
 
 		/* In a rare case it is possible that no clust rec is found
@@ -2974,7 +2980,7 @@ row_sel_get_clust_rec_for_mysql(
 		we set a LOCK_REC_NOT_GAP type lock */
 
 		err = lock_clust_rec_read_check_and_lock(
-			0, btr_pcur_get_block(prebuilt->clust_pcur),
+			0, btr_pcur_get_block(&prebuilt->clust_pcur),
 			clust_rec, clust_index, *offsets,
 			prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr);
 		switch (err) {
@@ -3052,7 +3058,7 @@ func_exit:
 		/* We may use the cursor in update or in unlock_row():
 		store its position */
 
-		btr_pcur_store_position(prebuilt->clust_pcur, mtr);
+		btr_pcur_store_position(&prebuilt->clust_pcur, mtr);
 	}
 
 err_exit:
@@ -3300,7 +3306,7 @@ row_sel_try_search_shortcut_for_mysql(
 {
 	dict_index_t*	index		= prebuilt->index;
 	const dtuple_t*	search_tuple	= prebuilt->search_tuple;
-	btr_pcur_t*	pcur		= prebuilt->pcur;
+	btr_pcur_t*	pcur		= &prebuilt->pcur;
 	trx_t*		trx		= prebuilt->trx;
 	const rec_t*	rec;
 
@@ -3389,7 +3395,7 @@ row_search_for_mysql(
 	dict_index_t*	index		= prebuilt->index;
 	ibool		comp		= dict_table_is_comp(index->table);
 	const dtuple_t*	search_tuple	= prebuilt->search_tuple;
-	btr_pcur_t*	pcur		= prebuilt->pcur;
+	btr_pcur_t*	pcur		= &prebuilt->pcur;
 	trx_t*		trx		= prebuilt->trx;
 	dict_index_t*	clust_index;
 	que_thr_t*	thr;

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.5 branch (vasil.dimov:3668 to 3673) Bug#11764622vasil.dimov29 Dec