List:Commits« Previous MessageNext Message »
From:marko.makela Date:March 29 2012 11:29am
Subject:bzr push into mysql-trunk branch (marko.makela:3871 to 3875) WL#5526 WL#5534
View as plain text  
 3875 Marko Mäkelä	2012-03-29
      Remove unnecessary steps from innodb.innodb, now that WL#5534/WL#5526
      allows in-place ALTER TABLE DROP INDEX, ADD INDEX of the same index.

    modified:
      mysql-test/suite/innodb/r/innodb.result
      mysql-test/suite/innodb/t/innodb.test
 3874 Marko Mäkelä	2012-03-29
      Remove unused function dict_index_get_on_id_low().

    modified:
      storage/innobase/dict/dict0dict.cc
 3873 Marko Mäkelä	2012-03-29
      Non-functional change: Add some GCC function attributes.

    modified:
      storage/innobase/include/data0data.h
      storage/innobase/include/data0data.ic
 3872 Marko Mäkelä	2012-03-29
      Bug#13866532 RECORD NOT FOUND OR NOT DELETE-MARKED AFTER CREATING INDEX ONLINE
      
      In online index creation, when scanning the clustered index, we must
      copy delete-marked records whose transactions are still
      active. Otherwise, problems could occur when an error occurs during
      the attempt to UPDATE a PRIMARY KEY.
      
      innodb-index-online-delete.test: Trigger a duplicate key error in an
      update of a primary key while a secondary index is being created
      online.
      
      row_update_for_mysql(): Add DEBUG_SYNC row_update_for_mysql_error.
      
      ha_innobase::inplace_alter_table(): Add DEBUG_SYNC
      innodb_after_inplace_alter_table.
      
      row_merge_read_clustered_index(): When creating an index online, copy
      delete-marked records that are implicitly locked by an active
      transaction.
      
      As part of this fix, we must preserve the delete-mark flags during the
      merge sort.
      
      mtuple_t: New struct, comprising del_mark and fields. The tuples used
      to be stored as an array of dfield_t* in row_merge_buf_t.
      
      row_merge_tuple_print(), row_merge_buf_encode(),
      row_merge_tuple_cmp(), row_merge_tuple_sort(): Operate on mtuple_t
      instead of dfield_t[].
      
      mrec_t: When creating an index online, store the delete-mark flag as
      the last bit in the null-flag bitmap in the record. This requires that
      we replace index->n_nullable with a parameter in the low-level
      functions.
      
      rec_get_converted_size_comp_prefix(),
      rec_init_offsets_comp_ordinary(), rec_convert_dtuple_to_rec_comp(),
      rec_get_converted_size_comp_prefix(), row_merge_read_rec(),
      row_merge_blocks(), row_merge_blocks_copy(), row_merge():
      Add the parameter n_null, which must be at least index->n_nullable.
      
      row_merge_insert_index_tuples(): Add the parameter del_marks.
      
      rb:995 approved by Jimmy Yang

    added:
      mysql-test/suite/innodb/r/innodb-index-online-delete.result
      mysql-test/suite/innodb/t/innodb-index-online-delete.test
    modified:
      storage/innobase/btr/btr0cur.cc
      storage/innobase/handler/handler0alter.cc
      storage/innobase/include/rem0rec.h
      storage/innobase/include/rem0rec.ic
      storage/innobase/include/row0merge.h
      storage/innobase/rem/rem0rec.cc
      storage/innobase/row/row0ftsort.cc
      storage/innobase/row/row0log.cc
      storage/innobase/row/row0merge.cc
      storage/innobase/row/row0mysql.cc
 3871 Sunny Bains	2012-03-29 [merge]
      Null merge from mysql-5.5. This is for a patch that was backported from trunk
      by directly patching mysql-5.5.

=== added file 'mysql-test/suite/innodb/r/innodb-index-online-delete.result'
--- a/mysql-test/suite/innodb/r/innodb-index-online-delete.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/r/innodb-index-online-delete.result	revid:marko.makela@stripped7152bqa
@@ -0,0 +1,12 @@
+CREATE TABLE t (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+INSERT INTO t VALUES(1,2),(2,3);
+SET DEBUG_SYNC='alter_table_inplace_after_lock_downgrade SIGNAL do WAIT_FOR m';
+SET DEBUG_SYNC='innodb_after_inplace_alter_table SIGNAL scanned WAIT_FOR done';
+CREATE INDEX tb ON t(b);
+SET DEBUG_SYNC='now WAIT_FOR do';
+SET DEBUG_SYNC='row_update_for_mysql_error SIGNAL m WAIT_FOR scanned';
+UPDATE t SET a=2 WHERE a=1;
+ERROR 23000: Duplicate entry '2' for key 'PRIMARY'
+SET DEBUG_SYNC='now SIGNAL done';
+SET DEBUG_SYNC='RESET';
+DROP TABLE t;

=== modified file 'mysql-test/suite/innodb/r/innodb.result'
--- a/mysql-test/suite/innodb/r/innodb.result	revid:sunny.bains@strippedvgr
+++ b/mysql-test/suite/innodb/r/innodb.result	revid:marko.makela@stripped
@@ -695,8 +695,6 @@ select count(*) from t1 where sca_pic is
 count(*)
 2
 alter table t1 drop index sca_pic, add index sca_pic (cat_code, sca_pic);
-alter table t1 drop index sca_pic;
-alter table t1 add index sca_pic (cat_code, sca_pic);
 select count(*) from t1 where sca_code='PD' and sca_pic is null;
 count(*)
 1
@@ -704,8 +702,6 @@ select count(*) from t1 where cat_code='
 count(*)
 0
 alter table t1 drop index sca_pic, add index (sca_pic, cat_code);
-alter table t1 drop index sca_pic;
-alter table t1 add index (sca_pic, cat_code);
 select count(*) from t1 where sca_code='PD' and sca_pic is null;
 count(*)
 1

=== added file 'mysql-test/suite/innodb/t/innodb-index-online-delete.test'
--- a/mysql-test/suite/innodb/t/innodb-index-online-delete.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb-index-online-delete.test	revid:marko.makela@stripped
@@ -0,0 +1,35 @@
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+
+# Save the initial number of concurrent sessions.
+--source include/count_sessions.inc
+
+connect (con1,localhost,root,,);
+
+connection default;
+
+CREATE TABLE t (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+INSERT INTO t VALUES(1,2),(2,3);
+
+SET DEBUG_SYNC='alter_table_inplace_after_lock_downgrade SIGNAL do WAIT_FOR m';
+SET DEBUG_SYNC='innodb_after_inplace_alter_table SIGNAL scanned WAIT_FOR done';
+--send
+CREATE INDEX tb ON t(b);
+
+connection con1;
+SET DEBUG_SYNC='now WAIT_FOR do';
+SET DEBUG_SYNC='row_update_for_mysql_error SIGNAL m WAIT_FOR scanned';
+--error ER_DUP_ENTRY
+UPDATE t SET a=2 WHERE a=1;
+SET DEBUG_SYNC='now SIGNAL done';
+
+disconnect con1;
+
+connection default;
+reap;
+SET DEBUG_SYNC='RESET';
+DROP TABLE t;
+
+# Check that all connections opened by test cases in this file are really
+# gone so execution of other tests won't be affected by their presence.
+--source include/wait_until_count_sessions.inc

=== modified file 'mysql-test/suite/innodb/t/innodb.test'
--- a/mysql-test/suite/innodb/t/innodb.test	revid:sunny.bains@strippedpb8vgr
+++ b/mysql-test/suite/innodb/t/innodb.test	revid:marko.makela@stripped
@@ -444,19 +444,11 @@ INSERT INTO t1 ( sca_code, cat_code, sca
 select count(*) from t1 where sca_code = 'PD';
 select count(*) from t1 where sca_code <= 'PD';
 select count(*) from t1 where sca_pic is null;
-# this should be fixed by MySQL (see Bug #51451)
-# now that http://bugs.mysql.com/49838 is fixed the following ALTER does
-# copy the table instead of failing
-# --error ER_WRONG_NAME_FOR_INDEX
 alter table t1 drop index sca_pic, add index sca_pic (cat_code, sca_pic);
-alter table t1 drop index sca_pic;
-alter table t1 add index sca_pic (cat_code, sca_pic);
 select count(*) from t1 where sca_code='PD' and sca_pic is null;
 select count(*) from t1 where cat_code='E';
 
 alter table t1 drop index sca_pic, add index (sca_pic, cat_code);
-alter table t1 drop index sca_pic;
-alter table t1 add index (sca_pic, cat_code);
 select count(*) from t1 where sca_code='PD' and sca_pic is null;
 select count(*) from t1 where sca_pic >= 'n';
 select sca_pic from t1 where sca_pic is null;

=== modified file 'storage/innobase/btr/btr0cur.cc'
--- a/storage/innobase/btr/btr0cur.cc	revid:sunny.bains@strippedvgr
+++ b/storage/innobase/btr/btr0cur.cc	revid:marko.makela@stripped
@@ -1319,7 +1319,7 @@ btr_cur_optimistic_insert(
 		    && UNIV_UNLIKELY(REC_NODE_PTR_SIZE
 				     + rec_get_converted_size_comp_prefix(
 					     index, entry->fields, n_uniq,
-					     NULL)
+					     index->n_nullable, NULL)
 				     /* On a compressed page, there is
 				     a two-byte entry in the dense
 				     page directory for every record.

=== modified file 'storage/innobase/dict/dict0dict.cc'
--- a/storage/innobase/dict/dict0dict.cc	revid:sunny.bains@stripped
+++ b/storage/innobase/dict/dict0dict.cc	revid:marko.makela@oracle.com-20120329112900-pcp4ca6df7152bqa
@@ -628,33 +628,6 @@ dict_table_autoinc_unlock(
 {
 	mutex_exit(&table->autoinc_mutex);
 }
-
-/**********************************************************************//**
-Looks for an index with the given table and index id.
-Note: Does not reserve the dictionary mutex.
-@return	index or NULL if not found in cache */
-UNIV_INTERN
-dict_index_t*
-dict_index_get_on_id_low(
-/*=====================*/
-	dict_table_t*	table,	/*!< in: table */
-	index_id_t	id)	/*!< in: index id */
-{
-	dict_index_t*	index;
-
-	for (index = dict_table_get_first_index(table);
-	     index != NULL;
-	     index = dict_table_get_next_index(index)) {
-
-		if (id == index->id) {
-			/* Found */
-
-			return(index);
-		}
-	}
-
-	return(NULL);
-}
 #endif /* !UNIV_HOTBACKUP */
 
 /********************************************************************//**

=== modified file 'storage/innobase/handler/handler0alter.cc'
--- a/storage/innobase/handler/handler0alter.cc	revid:sunny.bains@stripped00-tvbzrhe57qpb8vgr
+++ b/storage/innobase/handler/handler0alter.cc	revid:marko.makela@strippedcp4ca6df7152bqa
@@ -24,6 +24,7 @@ Smart ALTER TABLE
 #include <unireg.h>
 #include <mysqld_error.h>
 #include <log.h>
+#include <debug_sync.h>
 #include <mysql/innodb_priv.h>
 
 #include "dict0stats.h"
@@ -2156,6 +2157,7 @@ oom:
 		happens to be executing on this very table. */
 		DBUG_ASSERT(ctx->indexed_table == prebuilt->table
 			    || prebuilt->table->n_ref_count - 1 <= 1);
+		DEBUG_SYNC(user_thd, "innodb_after_inplace_alter_table");
 		DBUG_RETURN(false);
 	case DB_DUPLICATE_KEY:
 		if (prebuilt->trx->error_key_num == ULINT_UNDEFINED) {

=== modified file 'storage/innobase/include/data0data.h'
--- a/storage/innobase/include/data0data.h	revid:sunny.bains@stripped0329070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/include/data0data.h	revid:marko.makela@stripped0-pcp4ca6df7152bqa
@@ -45,7 +45,8 @@ UNIV_INLINE
 dtype_t*
 dfield_get_type(
 /*============*/
-	const dfield_t*	field);	/*!< in: SQL data field */
+	const dfield_t*	field)	/*!< in: SQL data field */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Gets pointer to the data in a field.
 @return	pointer to data */
@@ -53,7 +54,8 @@ UNIV_INLINE
 void*
 dfield_get_data(
 /*============*/
-	const dfield_t* field);	/*!< in: field */
+	const dfield_t* field)	/*!< in: field */
+	__attribute__((nonnull, warn_unused_result));
 #else /* UNIV_DEBUG */
 # define dfield_get_type(field) (&(field)->type)
 # define dfield_get_data(field) ((field)->data)
@@ -65,7 +67,8 @@ void
 dfield_set_type(
 /*============*/
 	dfield_t*	field,	/*!< in: SQL data field */
-	dtype_t*	type);	/*!< in: pointer to data type struct */
+	const dtype_t*	type)	/*!< in: pointer to data type struct */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Gets length of field data.
 @return	length of data; UNIV_SQL_NULL if SQL null data */
@@ -73,7 +76,8 @@ UNIV_INLINE
 ulint
 dfield_get_len(
 /*===========*/
-	const dfield_t* field);	/*!< in: field */
+	const dfield_t* field)	/*!< in: field */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Sets length in a field. */
 UNIV_INLINE
@@ -81,7 +85,8 @@ void
 dfield_set_len(
 /*===========*/
 	dfield_t*	field,	/*!< in: field */
-	ulint		len);	/*!< in: length or UNIV_SQL_NULL */
+	ulint		len)	/*!< in: length or UNIV_SQL_NULL */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Determines if a field is SQL NULL
 @return	nonzero if SQL null data */
@@ -89,7 +94,8 @@ UNIV_INLINE
 ulint
 dfield_is_null(
 /*===========*/
-	const dfield_t* field);	/*!< in: field */
+	const dfield_t* field)	/*!< in: field */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Determines if a field is externally stored
 @return	nonzero if externally stored */
@@ -97,14 +103,16 @@ UNIV_INLINE
 ulint
 dfield_is_ext(
 /*==========*/
-	const dfield_t* field);	/*!< in: field */
+	const dfield_t* field)	/*!< in: field */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Sets the "external storage" flag */
 UNIV_INLINE
 void
 dfield_set_ext(
 /*===========*/
-	dfield_t*	field);	/*!< in/out: field */
+	dfield_t*	field)	/*!< in/out: field */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Sets pointer to the data and length in a field. */
 UNIV_INLINE
@@ -113,14 +121,16 @@ dfield_set_data(
 /*============*/
 	dfield_t*	field,	/*!< in: field */
 	const void*	data,	/*!< in: data */
-	ulint		len);	/*!< in: length or UNIV_SQL_NULL */
+	ulint		len)	/*!< in: length or UNIV_SQL_NULL */
+	__attribute__((nonnull(1)));
 /*********************************************************************//**
 Sets a data field to SQL NULL. */
 UNIV_INLINE
 void
 dfield_set_null(
 /*============*/
-	dfield_t*	field);	/*!< in/out: field */
+	dfield_t*	field)	/*!< in/out: field */
+	__attribute__((nonnull));
 /**********************************************************************//**
 Writes an SQL null field full of zeros. */
 UNIV_INLINE
@@ -128,7 +138,8 @@ void
 data_write_sql_null(
 /*================*/
 	byte*	data,	/*!< in: pointer to a buffer of size len */
-	ulint	len);	/*!< in: SQL null size in bytes */
+	ulint	len)	/*!< in: SQL null size in bytes */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Copies the data and len fields. */
 UNIV_INLINE
@@ -136,7 +147,8 @@ void
 dfield_copy_data(
 /*=============*/
 	dfield_t*	field1,	/*!< out: field to copy to */
-	const dfield_t*	field2);/*!< in: field to copy from */
+	const dfield_t*	field2)	/*!< in: field to copy from */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Copies a data field to another. */
 UNIV_INLINE
@@ -144,7 +156,8 @@ void
 dfield_copy(
 /*========*/
 	dfield_t*	field1,	/*!< out: field to copy to */
-	const dfield_t*	field2);/*!< in: field to copy from */
+	const dfield_t*	field2)	/*!< in: field to copy from */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Copies the data pointed to by a data field. */
 UNIV_INLINE
@@ -152,7 +165,8 @@ void
 dfield_dup(
 /*=======*/
 	dfield_t*	field,	/*!< in/out: data field */
-	mem_heap_t*	heap);	/*!< in: memory heap where allocated */
+	mem_heap_t*	heap)	/*!< in: memory heap where allocated */
+	__attribute__((nonnull));
 #ifndef UNIV_HOTBACKUP
 /*********************************************************************//**
 Tests if two data fields are equal.
@@ -187,7 +201,8 @@ UNIV_INLINE
 ulint
 dtuple_get_n_fields(
 /*================*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 #ifdef UNIV_DEBUG
 /*********************************************************************//**
 Gets nth field of a tuple.
@@ -208,7 +223,8 @@ UNIV_INLINE
 ulint
 dtuple_get_info_bits(
 /*=================*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Sets info bits in a data tuple. */
 UNIV_INLINE
@@ -216,7 +232,8 @@ void
 dtuple_set_info_bits(
 /*=================*/
 	dtuple_t*	tuple,		/*!< in: tuple */
-	ulint		info_bits);	/*!< in: info bits */
+	ulint		info_bits)	/*!< in: info bits */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Gets number of fields used in record comparisons.
 @return	number of fields used in comparisons in rem0cmp.* */
@@ -224,7 +241,8 @@ UNIV_INLINE
 ulint
 dtuple_get_n_fields_cmp(
 /*====================*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 /*********************************************************************//**
 Gets number of fields used in record comparisons. */
 UNIV_INLINE
@@ -232,8 +250,9 @@ void
 dtuple_set_n_fields_cmp(
 /*====================*/
 	dtuple_t*	tuple,		/*!< in: tuple */
-	ulint		n_fields_cmp);	/*!< in: number of fields used in
+	ulint		n_fields_cmp)	/*!< in: number of fields used in
 					comparisons in rem0cmp.* */
+	__attribute__((nonnull));
 
 /* Estimate the number of bytes that are going to be allocated when
 creating a new dtuple_t object */
@@ -252,7 +271,8 @@ dtuple_create_from_mem(
 /*===================*/
 	void*	buf,		/*!< in, out: buffer to use */
 	ulint	buf_size,	/*!< in: buffer size */
-	ulint	n_fields);	/*!< in: number of fields */
+	ulint	n_fields)	/*!< in: number of fields */
+	__attribute__((nonnull, warn_unused_result));
 
 /**********************************************************//**
 Creates a data tuple to a memory heap. The default value for number
@@ -265,7 +285,8 @@ 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 */
+	ulint		n_fields)/*!< in: number of fields */
+	__attribute__((nonnull, malloc));
 
 /**********************************************************//**
 Wrap data fields in a tuple. The default value for number
@@ -277,7 +298,8 @@ dtuple_from_fields(
 /*===============*/
 	dtuple_t*	tuple,		/*!< in: storage for data tuple */
 	const dfield_t*	fields,		/*!< in: fields */
-	ulint		n_fields);	/*!< in: number of fields */
+	ulint		n_fields)	/*!< in: number of fields */
+	__attribute__((nonnull, warn_unused_result));
 
 /*********************************************************************//**
 Sets number of fields used in a tuple. Normally this is set in
@@ -287,7 +309,8 @@ void
 dtuple_set_n_fields(
 /*================*/
 	dtuple_t*	tuple,		/*!< in: tuple */
-	ulint		n_fields);	/*!< in: number of fields */
+	ulint		n_fields)	/*!< in: number of fields */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Copies a data tuple to another.  This is a shallow copy; if a deep copy
 is desired, dfield_dup() will have to be invoked on each field.
@@ -297,8 +320,9 @@ dtuple_t*
 dtuple_copy(
 /*========*/
 	const dtuple_t*	tuple,	/*!< in: tuple to copy from */
-	mem_heap_t*	heap);	/*!< in: memory heap
+	mem_heap_t*	heap)	/*!< in: memory heap
 				where the tuple is created */
+	__attribute__((nonnull, malloc));
 /**********************************************************//**
 The following function returns the sum of data lengths of a tuple. The space
 occupied by the field structs or the tuple struct is not counted.
@@ -308,7 +332,8 @@ ulint
 dtuple_get_data_size(
 /*=================*/
 	const dtuple_t*	tuple,	/*!< in: typed data tuple */
-	ulint		comp);	/*!< in: nonzero=ROW_FORMAT=COMPACT  */
+	ulint		comp)	/*!< in: nonzero=ROW_FORMAT=COMPACT  */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Computes the number of externally stored fields in a data tuple.
 @return	number of fields */
@@ -316,7 +341,8 @@ UNIV_INLINE
 ulint
 dtuple_get_n_ext(
 /*=============*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull));
 /************************************************************//**
 Compare two data tuples, respecting the collation of character fields.
 @return 1, 0 , -1 if tuple1 is greater, equal, less, respectively,
@@ -326,7 +352,8 @@ int
 dtuple_coll_cmp(
 /*============*/
 	const dtuple_t*	tuple1,	/*!< in: tuple 1 */
-	const dtuple_t*	tuple2);/*!< in: tuple 2 */
+	const dtuple_t*	tuple2)	/*!< in: tuple 2 */
+	__attribute__((nonnull, warn_unused_result));
 /************************************************************//**
 Folds a prefix given as the number of fields of a tuple.
 @return	the folded value */
@@ -339,7 +366,7 @@ dtuple_fold(
 	ulint		n_bytes,/*!< in: number of bytes to fold in an
 				incomplete last field */
 	index_id_t	tree_id)/*!< in: index tree id */
-	__attribute__((pure));
+	__attribute__((nonnull, pure, warn_unused_result));
 /*******************************************************************//**
 Sets types of fields binary in a tuple. */
 UNIV_INLINE
@@ -347,7 +374,8 @@ void
 dtuple_set_types_binary(
 /*====================*/
 	dtuple_t*	tuple,	/*!< in: data tuple */
-	ulint		n);	/*!< in: number of fields to set */
+	ulint		n)	/*!< in: number of fields to set */
+	__attribute__((nonnull));
 /**********************************************************************//**
 Checks if a dtuple contains an SQL null value.
 @return	TRUE if some field is SQL null */
@@ -355,7 +383,8 @@ UNIV_INLINE
 ibool
 dtuple_contains_null(
 /*=================*/
-	const dtuple_t*	tuple);	/*!< in: dtuple */
+	const dtuple_t*	tuple)	/*!< in: dtuple */
+	__attribute__((nonnull, warn_unused_result));
 /**********************************************************//**
 Checks that a data field is typed. Asserts an error if not.
 @return	TRUE if ok */
@@ -363,7 +392,8 @@ UNIV_INTERN
 ibool
 dfield_check_typed(
 /*===============*/
-	const dfield_t*	field);	/*!< in: data field */
+	const dfield_t*	field)	/*!< in: data field */
+	__attribute__((nonnull, warn_unused_result));
 /**********************************************************//**
 Checks that a data tuple is typed. Asserts an error if not.
 @return	TRUE if ok */
@@ -371,7 +401,8 @@ UNIV_INTERN
 ibool
 dtuple_check_typed(
 /*===============*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 /**********************************************************//**
 Checks that a data tuple is typed.
 @return	TRUE if ok */
@@ -379,7 +410,8 @@ UNIV_INTERN
 ibool
 dtuple_check_typed_no_assert(
 /*=========================*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 #ifdef UNIV_DEBUG
 /**********************************************************//**
 Validates the consistency of a tuple which must be complete, i.e,
@@ -389,7 +421,8 @@ UNIV_INTERN
 ibool
 dtuple_validate(
 /*============*/
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull, warn_unused_result));
 #endif /* UNIV_DEBUG */
 /*************************************************************//**
 Pretty prints a dfield value according to its data type. */
@@ -397,7 +430,8 @@ UNIV_INTERN
 void
 dfield_print(
 /*=========*/
-	const dfield_t*	dfield);/*!< in: dfield */
+	const dfield_t*	dfield)	/*!< in: dfield */
+	__attribute__((nonnull));
 /*************************************************************//**
 Pretty prints a dfield value according to its data type. Also the hex string
 is printed if a string contains non-printable characters. */
@@ -405,7 +439,8 @@ UNIV_INTERN
 void
 dfield_print_also_hex(
 /*==================*/
-	const dfield_t*	dfield);	 /*!< in: dfield */
+	const dfield_t*	dfield)	 /*!< in: dfield */
+	__attribute__((nonnull));
 /**********************************************************//**
 The following function prints the contents of a tuple. */
 UNIV_INTERN
@@ -413,7 +448,8 @@ void
 dtuple_print(
 /*=========*/
 	FILE*		f,	/*!< in: output stream */
-	const dtuple_t*	tuple);	/*!< in: tuple */
+	const dtuple_t*	tuple)	/*!< in: tuple */
+	__attribute__((nonnull));
 /**************************************************************//**
 Moves parts of long fields in entry to the big record vector so that
 the size of tuple drops below the maximum record size allowed in the
@@ -428,8 +464,9 @@ dtuple_convert_big_rec(
 /*===================*/
 	dict_index_t*	index,	/*!< in: index */
 	dtuple_t*	entry,	/*!< in/out: index entry */
-	ulint*		n_ext);	/*!< in/out: number of
+	ulint*		n_ext)	/*!< in/out: number of
 				externally stored columns */
+	__attribute__((nonnull, malloc, warn_unused_result));
 /**************************************************************//**
 Puts back to entry the data stored in vector. Note that to ensure the
 fields in entry can accommodate the data, vector must have been created
@@ -440,16 +477,18 @@ dtuple_convert_back_big_rec(
 /*========================*/
 	dict_index_t*	index,	/*!< in: index */
 	dtuple_t*	entry,	/*!< in: entry whose data was put to vector */
-	big_rec_t*	vector);/*!< in, own: big rec vector; it is
+	big_rec_t*	vector)	/*!< in, own: big rec vector; it is
 				freed in this function */
+	__attribute__((nonnull));
 /**************************************************************//**
 Frees the memory in a big rec vector. */
 UNIV_INLINE
 void
 dtuple_big_rec_free(
 /*================*/
-	big_rec_t*	vector);	/*!< in, own: big rec vector; it is
+	big_rec_t*	vector)	/*!< in, own: big rec vector; it is
 				freed in this function */
+	__attribute__((nonnull));
 
 /*######################################################################*/
 

=== modified file 'storage/innobase/include/data0data.ic'
--- a/storage/innobase/include/data0data.ic	revid:sunny.bains@stripped070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/include/data0data.ic	revid:marko.makela@strippedcp4ca6df7152bqa
@@ -54,7 +54,7 @@ void
 dfield_set_type(
 /*============*/
 	dfield_t*	field,	/*!< in: SQL data field */
-	dtype_t*	type)	/*!< in: pointer to data type struct */
+	const dtype_t*	type)	/*!< in: pointer to data type struct */
 {
 	ut_ad(field && type);
 

=== modified file 'storage/innobase/include/rem0rec.h'
--- a/storage/innobase/include/rem0rec.h	revid:sunny.bains@stripped20329070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/include/rem0rec.h	revid:marko.makela@stripped-pcp4ca6df7152bqa
@@ -376,8 +376,11 @@ rec_init_offsets_comp_ordinary(
 					the data payload
 					(usually REC_N_NEW_EXTRA_BYTES) */
 	const dict_index_t*	index,	/*!< in: record descriptor */
-	ulint*			offsets);/*!< in/out: array of offsets;
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
+	ulint*			offsets)/*!< in/out: array of offsets;
 					in: n=rec_offs_n_fields(offsets) */
+	__attribute__((nonnull));
 
 /******************************************************//**
 The following function determines the offsets to each field
@@ -695,7 +698,10 @@ rec_convert_dtuple_to_rec_comp(
 	const dict_index_t*	index,	/*!< in: record descriptor */
 	ulint			status,	/*!< in: status bits of the record */
 	const dfield_t*		fields,	/*!< in: array of data fields */
-	ulint			n_fields);/*!< in: number of data fields */
+	ulint			n_fields,/*!< in: number of data fields */
+	ulint			n_null)	/*!< in: number of fields that
+					can be NULL */
+	__attribute__((nonnull));
 /*********************************************************//**
 Builds a physical record out of a data tuple and
 stores it into the given buffer.
@@ -721,7 +727,7 @@ rec_get_converted_extra_size(
 	ulint	data_size,	/*!< in: data size */
 	ulint	n_fields,	/*!< in: number of fields */
 	ulint	n_ext)		/*!< in: number of externally stored columns */
-		__attribute__((const));
+	__attribute__((const));
 /**********************************************************//**
 Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT.
 @return	total size */
@@ -735,7 +741,10 @@ rec_get_converted_size_comp_prefix(
 					it does not */
 	const dfield_t*		fields,	/*!< in: array of data fields */
 	ulint			n_fields,/*!< in: number of data fields */
-	ulint*			extra);	/*!< out: extra size */
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
+	ulint*			extra)	/*!< out: extra size */
+	__attribute__((warn_unused_result, nonnull(1,2)));
 /**********************************************************//**
 Determines the size of a data tuple in ROW_FORMAT=COMPACT.
 @return	total size */
@@ -750,7 +759,10 @@ rec_get_converted_size_comp(
 	ulint			status,	/*!< in: status bits of the record */
 	const dfield_t*		fields,	/*!< in: array of data fields */
 	ulint			n_fields,/*!< in: number of data fields */
-	ulint*			extra);	/*!< out: extra size */
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
+	ulint*			extra)	/*!< out: extra size */
+	__attribute__((nonnull(1,3)));
 /**********************************************************//**
 The following function returns the size of a data tuple when converted to
 a physical record.
@@ -761,7 +773,8 @@ rec_get_converted_size(
 /*===================*/
 	dict_index_t*	index,	/*!< in: record descriptor */
 	const dtuple_t*	dtuple,	/*!< in: data tuple */
-	ulint		n_ext);	/*!< in: number of externally stored columns */
+	ulint		n_ext)	/*!< in: number of externally stored columns */
+	__attribute__((warn_unused_result, nonnull));
 #ifndef UNIV_HOTBACKUP
 /**************************************************************//**
 Copies the first n fields of a physical record to a data tuple.

=== modified file 'storage/innobase/include/rem0rec.ic'
--- a/storage/innobase/include/rem0rec.ic	revid:sunny.bains@stripped070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/include/rem0rec.ic	revid:marko.makela@stripped4ca6df7152bqa
@@ -1600,7 +1600,8 @@ rec_get_converted_size(
 						   dtuple_get_info_bits(dtuple)
 						   & REC_NEW_STATUS_MASK,
 						   dtuple->fields,
-						   dtuple->n_fields, NULL));
+						   dtuple->n_fields,
+						   index->n_nullable, NULL));
 	}
 
 	data_size = dtuple_get_data_size(dtuple, 0);

=== modified file 'storage/innobase/include/row0merge.h'
--- a/storage/innobase/include/row0merge.h	revid:sunny.bains@stripped-tvbzrhe57qpb8vgr
+++ b/storage/innobase/include/row0merge.h	revid:marko.makela@strippedf7152bqa
@@ -63,6 +63,12 @@ The format is the same as a record in RO
 exception that the REC_N_NEW_EXTRA_BYTES are omitted. */
 typedef byte	mrec_t;
 
+/** Merge record in row_merge_buf_t */
+struct mtuple_t {
+	bool		del_mark;	/*!< true when delete-marked */
+	dfield_t*	fields;		/*!< data fields */
+};
+
 /** Buffer for sorting in main memory. */
 struct row_merge_buf_struct {
 	mem_heap_t*	heap;		/*!< memory heap where allocated */
@@ -70,10 +76,8 @@ struct row_merge_buf_struct {
 	ulint		total_size;	/*!< total amount of data bytes */
 	ulint		n_tuples;	/*!< number of data tuples */
 	ulint		max_tuples;	/*!< maximum number of data tuples */
-	const dfield_t**tuples;		/*!< array of pointers to
-					arrays of fields that form
-					the data tuples */
-	const dfield_t**tmp_tuples;	/*!< temporary copy of tuples,
+	mtuple_t*	tuples;		/*!< array of data tuples */
+	mtuple_t*	tmp_tuples;	/*!< temporary copy of tuples,
 					for sorting */
 };
 
@@ -348,9 +352,10 @@ row_merge_sort(
 					index entries */
 	row_merge_block_t*	block,	/*!< in/out: 3 buffers */
 	int*			tmpfd,	/*!< in/out: temporary file handle */
-	struct TABLE*		table);	/*!< in/out: MySQL table, for
+	struct TABLE*		table)	/*!< in/out: MySQL table, for
 					reporting erroneous key value
 					if applicable */
+	__attribute__((nonnull));
 /*********************************************************************//**
 Allocate a sort buffer.
 @return own: sort buffer */
@@ -391,7 +396,7 @@ row_merge_read(
 /********************************************************************//**
 Read a merge record.
 @return pointer to next record, or NULL on I/O error or end of list */
-UNIV_INTERN __attribute__((nonnull))
+UNIV_INTERN
 const byte*
 row_merge_read_rec(
 /*===============*/
@@ -399,10 +404,12 @@ row_merge_read_rec(
 	mrec_buf_t*		buf,	/*!< in/out: secondary buffer */
 	const byte*		b,	/*!< in: pointer to record */
 	const dict_index_t*	index,	/*!< in: index of the record */
+	ulint			n_null,	/*!< in: size of the NULL-bit bitmap */
 	int			fd,	/*!< in: file descriptor */
 	ulint*			foffs,	/*!< in/out: file offset */
 	const mrec_t**		mrec,	/*!< out: pointer to merge record,
 					or NULL on end of list
 					(non-NULL on I/O error) */
-	ulint*			offsets);/*!< out: offsets of mrec */
+	ulint*			offsets)/*!< out: offsets of mrec */
+	__attribute__((nonnull, warn_unused_result));
 #endif /* row0merge.h */

=== modified file 'storage/innobase/rem/rem0rec.cc'
--- a/storage/innobase/rem/rem0rec.cc	revid:sunny.bains@oracle.com-20120329070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/rem/rem0rec.cc	revid:marko.makela@stripped120329112900-pcp4ca6df7152bqa
@@ -251,6 +251,8 @@ rec_init_offsets_comp_ordinary(
 					the data payload
 					(usually REC_N_NEW_EXTRA_BYTES) */
 	const dict_index_t*	index,	/*!< in: record descriptor */
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
 	ulint*			offsets)/*!< in/out: array of offsets;
 					in: n=rec_offs_n_fields(offsets) */
 {
@@ -258,8 +260,7 @@ rec_init_offsets_comp_ordinary(
 	ulint		offs		= 0;
 	ulint		any_ext		= 0;
 	const byte*	nulls		= rec - (extra + 1);
-	const byte*	lens		= nulls
-		- UT_BITS_IN_BYTES(index->n_nullable);
+	const byte*	lens		= nulls - UT_BITS_IN_BYTES(n_null);
 	dict_field_t*	field;
 	ulint		null_mask	= 1;
 
@@ -270,6 +271,7 @@ rec_init_offsets_comp_ordinary(
 	offsets[2] = (ulint) rec;
 	offsets[3] = (ulint) index;
 #endif /* UNIV_DEBUG */
+	ut_ad(n_null >= index->n_nullable);
 
 	/* read the lengths of fields 0..n */
 	do {
@@ -394,9 +396,9 @@ rec_init_offsets(
 				= dict_index_get_n_unique_in_tree(index);
 			break;
 		case REC_STATUS_ORDINARY:
-			rec_init_offsets_comp_ordinary(rec,
-						       REC_N_NEW_EXTRA_BYTES,
-						       index, offsets);
+			rec_init_offsets_comp_ordinary(
+				rec, REC_N_NEW_EXTRA_BYTES,
+				index, index->n_nullable, offsets);
 			return;
 		}
 
@@ -784,6 +786,8 @@ rec_get_converted_size_comp_prefix(
 					it does not */
 	const dfield_t*		fields,	/*!< in: array of data fields */
 	ulint			n_fields,/*!< in: number of data fields */
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
 	ulint*			extra)	/*!< out: extra size */
 {
 	ulint	extra_size;
@@ -793,9 +797,9 @@ rec_get_converted_size_comp_prefix(
 	ut_ad(fields);
 	ut_ad(n_fields > 0);
 	ut_ad(n_fields <= dict_index_get_n_fields(index));
+	ut_ad(n_null >= index->n_nullable);
 
-	extra_size = REC_N_NEW_EXTRA_BYTES
-		+ UT_BITS_IN_BYTES(index->n_nullable);
+	extra_size = REC_N_NEW_EXTRA_BYTES + UT_BITS_IN_BYTES(n_null);
 	data_size = 0;
 
 	/* read the lengths of fields 0..n */
@@ -869,12 +873,15 @@ rec_get_converted_size_comp(
 	ulint			status,	/*!< in: status bits of the record */
 	const dfield_t*		fields,	/*!< in: array of data fields */
 	ulint			n_fields,/*!< in: number of data fields */
+	ulint			n_null,	/*!< in: number of fields that
+					can be NULL */
 	ulint*			extra)	/*!< out: extra size */
 {
 	ulint	size;
 	ut_ad(index);
 	ut_ad(fields);
 	ut_ad(n_fields > 0);
+	ut_ad(n_null >= index->n_nullable);
 
 	switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) {
 	case REC_STATUS_ORDINARY:
@@ -899,8 +906,8 @@ rec_get_converted_size_comp(
 		return(ULINT_UNDEFINED);
 	}
 
-	return(size + rec_get_converted_size_comp_prefix(index, fields,
-							 n_fields, extra));
+	return(size + rec_get_converted_size_comp_prefix(
+		       index, fields, n_fields, n_null, extra));
 }
 
 /***********************************************************//**
@@ -1089,7 +1096,9 @@ rec_convert_dtuple_to_rec_comp(
 	const dict_index_t*	index,	/*!< in: record descriptor */
 	ulint			status,	/*!< in: status bits of the record */
 	const dfield_t*		fields,	/*!< in: array of data fields */
-	ulint			n_fields)/*!< in: number of data fields */
+	ulint			n_fields,/*!< in: number of data fields */
+	ulint			n_null)	/*!< in: number of fields that
+					can be NULL */
 {
 	const dfield_t*	field;
 	const dtype_t*	type;
@@ -1104,6 +1113,7 @@ rec_convert_dtuple_to_rec_comp(
 	ut_ad(extra == 0 || dict_table_is_comp(index->table));
 	ut_ad(extra == 0 || extra == REC_N_NEW_EXTRA_BYTES);
 	ut_ad(n_fields > 0);
+	ut_ad(n_null >= index->n_nullable);
 
 	switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) {
 	case REC_STATUS_ORDINARY:
@@ -1126,7 +1136,7 @@ rec_convert_dtuple_to_rec_comp(
 
 	end = rec;
 	nulls = rec - (extra + 1);
-	lens = nulls - UT_BITS_IN_BYTES(index->n_nullable);
+	lens = nulls - UT_BITS_IN_BYTES(n_null);
 	/* clear the SQL-null flags */
 	memset(lens + 1, 0, nulls - lens);
 
@@ -1229,12 +1239,12 @@ rec_convert_dtuple_to_rec_new(
 	status = dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK;
 	rec_get_converted_size_comp(index, status,
 				    dtuple->fields, dtuple->n_fields,
-				    &extra_size);
+				    index->n_nullable, &extra_size);
 	rec = buf + extra_size;
 
 	rec_convert_dtuple_to_rec_comp(
 		rec, REC_N_NEW_EXTRA_BYTES, index, status,
-		dtuple->fields, dtuple->n_fields);
+		dtuple->fields, dtuple->n_fields, index->n_nullable);
 
 	/* Set the info bits of the record */
 	rec_set_info_and_status_bits(rec, dtuple_get_info_bits(dtuple));

=== modified file 'storage/innobase/row/row0ftsort.cc'
--- a/storage/innobase/row/row0ftsort.cc	revid:sunny.bains@stripped00-tvbzrhe57qpb8vgr
+++ b/storage/innobase/row/row0ftsort.cc	revid:marko.makela@strippedf7152bqa
@@ -35,7 +35,7 @@ Created 10/13/2010 Jimmy Yang
 #define ROW_MERGE_READ_GET_NEXT(N)					\
 	do {								\
 		b[N] = row_merge_read_rec(				\
-			block[N], buf[N], b[N], index,			\
+			block[N], buf[N], b[N], index, n_null,		\
 			fd[N], &foffs[N], &mrec[N], offsets[N]);	\
 		if (UNIV_UNLIKELY(!b[N])) {				\
 			if (mrec[N]) {					\
@@ -430,12 +430,12 @@ row_merge_fts_doc_tokenize(
 		ut_a(t_ctx->buf_used < FTS_NUM_AUX_INDEX);
 		idx = t_ctx->buf_used;
 
-		buf->tuples[buf->n_tuples + n_tuple[idx]] = field =
-			static_cast<dfield_t*>(mem_heap_alloc(
-				buf->heap,
-				FTS_NUM_FIELDS_SORT * sizeof *field));
+		mtuple_t* mtuple = &buf->tuples[buf->n_tuples + n_tuple[idx]];
 
-		ut_a(field);
+		mtuple->del_mark = false;
+		field = mtuple->fields = static_cast<dfield_t*>(
+			mem_heap_alloc(buf->heap,
+				       FTS_NUM_FIELDS_SORT * sizeof *field));
 
 		/* The first field is the tokenized word */
 		dfield_set_data(field, t_str.f_str, t_str.f_len);
@@ -519,6 +519,10 @@ row_merge_fts_doc_tokenize(
 	/* Update the data length and the number of new word tuples
 	added in this round of tokenization */
 	for (i = 0; i <  FTS_NUM_AUX_INDEX; i++) {
+		/* The computation of total_size below assumes that no
+		delete-mark flags will be stored and that all fields
+		are NOT NULL and fixed-length. */
+
 		sort_buf[i]->total_size += data_size[i];
 
 		sort_buf[i]->n_tuples += n_tuple[i];
@@ -1336,6 +1340,8 @@ row_fts_merge_insert(
 	ins_ctx.fts_table.parent = index->table->name;
 	ins_ctx.fts_table.table = NULL;
 
+	const ulint	n_null = index->n_nullable;
+
 	for (i = 0; i < fts_sort_pll_degree; i++) {
 		if (psort_info[i].merge_file[id]->n_rec == 0) {
 			/* No Rows to read */

=== modified file 'storage/innobase/row/row0log.cc'
--- a/storage/innobase/row/row0log.cc	revid:sunny.bains@stripped-20120329070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/row/row0log.cc	revid:marko.makela@stripped0-pcp4ca6df7152bqa
@@ -137,9 +137,9 @@ op_ok:
 	row_merge_buf_encode(), because here we do not encode
 	extra_size+1 (and reserve 0 as the end-of-chunk marker). */
 
-	size = rec_get_converted_size_comp(
-		index, REC_STATUS_ORDINARY,
-		tuple->fields, tuple->n_fields, &extra_size);
+	size = rec_get_converted_size_comp_prefix(
+		index, tuple->fields, tuple->n_fields,
+		index->n_nullable, &extra_size);
 	ut_ad(size >= extra_size);
 	ut_ad(extra_size >= REC_N_NEW_EXTRA_BYTES);
 	extra_size -= REC_N_NEW_EXTRA_BYTES;
@@ -170,7 +170,8 @@ op_ok:
 
 	rec_convert_dtuple_to_rec_comp(
 		b + extra_size, 0, index,
-		REC_STATUS_ORDINARY, tuple->fields, tuple->n_fields);
+		REC_STATUS_ORDINARY, tuple->fields, tuple->n_fields,
+		index->n_nullable);
 	b += size;
 
 	if (mrec_size >= avail_size) {
@@ -679,7 +680,8 @@ corrupted:
 		return(NULL);
 	}
 
-	rec_init_offsets_comp_ordinary(mrec, 0, index, offsets);
+	rec_init_offsets_comp_ordinary(
+		mrec, 0, index, index->n_nullable, offsets);
 
 	if (rec_offs_any_extern(offsets)) {
 		/* There should never be any externally stored fields

=== modified file 'storage/innobase/row/row0merge.cc'
--- a/storage/innobase/row/row0merge.cc	revid:sunny.bains@stripped
+++ b/storage/innobase/row/row0merge.cc	revid:marko.makela@oracle.com-20120329112900-pcp4ca6df7152bqa
@@ -90,18 +90,20 @@ UNIV_INTERN char	srv_disable_sort_file_c
 #ifdef UNIV_DEBUG
 /******************************************************//**
 Display a merge tuple. */
-static
+static __attribute__((nonnull))
 void
 row_merge_tuple_print(
 /*==================*/
 	FILE*		f,	/*!< in: output stream */
-	const dfield_t*	entry,	/*!< in: tuple to print */
+	const mtuple_t*	entry,	/*!< in: tuple to print */
 	ulint		n_fields)/*!< in: number of fields in the tuple */
 {
 	ulint	j;
 
+	fputs(entry->del_mark ? "(deleted)" : "", stderr);
+
 	for (j = 0; j < n_fields; j++) {
-		const dfield_t*	field = &entry[j];
+		const dfield_t*	field = &entry->fields[j];
 
 		if (dfield_is_null(field)) {
 			fputs("\n NULL;", f);
@@ -125,25 +127,29 @@ row_merge_tuple_print(
 
 /******************************************************//**
 Encode an index record. */
-static
+static __attribute__((nonnull))
 void
 row_merge_buf_encode(
 /*=================*/
 	byte**			b,		/*!< in/out: pointer to
 						current end of output buffer */
 	const dict_index_t*	index,		/*!< in: index */
-	const dfield_t*		entry,		/*!< in: index fields
+	const mtuple_t*		entry,		/*!< in: index fields
 						of the record to encode */
-	ulint			n_fields)	/*!< in: number of fields
+	ulint			n_fields,	/*!< in: number of fields
 						in the entry */
+	ulint			n_null)		/*!< in: size of the null
+						flags bitmap */
 {
 	ulint	size;
 	ulint	extra_size;
 
-	size = rec_get_converted_size_comp(
-		index, REC_STATUS_ORDINARY, entry, n_fields, &extra_size);
+	size = rec_get_converted_size_comp_prefix(
+		index, entry->fields, n_fields, n_null, &extra_size);
 	ut_ad(size >= extra_size);
 	ut_ad(extra_size >= REC_N_NEW_EXTRA_BYTES);
+	ut_ad((ulint) (n_null - index->n_nullable) <= 1);
+
 	extra_size -= REC_N_NEW_EXTRA_BYTES;
 	size -= REC_N_NEW_EXTRA_BYTES;
 
@@ -157,7 +163,17 @@ row_merge_buf_encode(
 	}
 
 	rec_convert_dtuple_to_rec_comp(*b + extra_size, 0, index,
-				       REC_STATUS_ORDINARY, entry, n_fields);
+				       REC_STATUS_ORDINARY,
+				       entry->fields, n_fields, n_null);
+
+	/* In mrec, the delete-mark flag is stored in the NULL flags
+	bitmap after the flags of any fields that can be NULL. */
+	if (entry->del_mark) {
+		ut_ad(n_null == (ulint) index->n_nullable + 1);
+		(*b)[extra_size - 1 - (int) index->n_nullable / 8]
+			|= 1 << (index->n_nullable & 7);
+	}
+
 	*b += size;
 }
 
@@ -185,7 +201,7 @@ row_merge_buf_create_low(
 	buf->heap = heap;
 	buf->index = index;
 	buf->max_tuples = max_tuples;
-	buf->tuples = static_cast<const dfield_t**>(
+	buf->tuples = static_cast<mtuple_t*>(
 		ut_malloc(2 * max_tuples * sizeof *buf->tuples));
 	buf->tmp_tuples = buf->tuples + max_tuples;
 
@@ -227,13 +243,11 @@ row_merge_buf_empty(
 /*================*/
 	row_merge_buf_t*	buf)	/*!< in,own: sort buffer */
 {
-	ulint		buf_size;
+	ulint		buf_size	= sizeof *buf;
 	ulint		max_tuples	= buf->max_tuples;
 	mem_heap_t*	heap		= buf->heap;
 	dict_index_t*	index		= buf->index;
-	void*		tuple		= buf->tuples;
-
-	buf_size = (sizeof *buf);;
+	mtuple_t*	tuples		= buf->tuples;
 
 	mem_heap_empty(heap);
 
@@ -241,7 +255,7 @@ row_merge_buf_empty(
 	buf->heap = heap;
 	buf->index = index;
 	buf->max_tuples = max_tuples;
-	buf->tuples = static_cast<const dfield_t**>(tuple);
+	buf->tuples = tuples;
 	buf->tmp_tuples = buf->tuples + max_tuples;
 
 	return(buf);
@@ -279,7 +293,7 @@ row_merge_buf_add(
 {
 	ulint			i;
 	const dict_index_t*	index;
-	dfield_t*		entry;
+	mtuple_t*		entry;
 	dfield_t*		field;
 	const dict_field_t*	ifield;
 	ulint			n_fields;
@@ -306,14 +320,17 @@ row_merge_buf_add(
 
 	n_fields = dict_index_get_n_fields(index);
 
-	entry = static_cast<dfield_t*>(
-		mem_heap_alloc(buf->heap, n_fields * sizeof *entry));
-
-	buf->tuples[buf->n_tuples] = entry;
-	field = entry;
+	entry = &buf->tuples[buf->n_tuples];
+	entry->del_mark = !!(dtuple_get_info_bits(row)
+			     & REC_INFO_DELETED_FLAG);
+	field = entry->fields = static_cast<dfield_t*>(
+		mem_heap_alloc(buf->heap, n_fields * sizeof *entry->fields));
 
 	data_size = 0;
-	extra_size = UT_BITS_IN_BYTES(index->n_nullable);
+	extra_size = UT_BITS_IN_BYTES(
+		index->n_nullable
+		+ (dict_index_get_online_status(index)
+		   != ONLINE_INDEX_COMPLETE));
 
 	ifield = dict_index_get_nth_field(index, 0);
 
@@ -491,9 +508,12 @@ row_merge_buf_add(
 		ulint	size;
 		ulint	extra;
 
-		size = rec_get_converted_size_comp(index,
-						   REC_STATUS_ORDINARY,
-						   entry, n_fields, &extra);
+		size = rec_get_converted_size_comp_prefix(
+			index, entry->fields, n_fields,
+			index->n_nullable
+			+ (dict_index_get_online_status(index)
+			   != ONLINE_INDEX_COMPLETE),
+			&extra);
 
 		ut_ad(data_size + extra == size);
 		ut_ad(extra_size + REC_N_NEW_EXTRA_BYTES == extra);
@@ -506,12 +526,6 @@ row_merge_buf_add(
 	of extra_size. */
 	data_size += (extra_size + 1) + ((extra_size + 1) >= 0x80);
 
-	/* The following assertion may fail if row_merge_block_t is
-	declared very small and a PRIMARY KEY is being created with
-	many prefix columns.  In that case, the record may exceed the
-	page_zip_rec_needs_ext() limit.  However, no further columns
-	will be moved to external storage until the record is inserted
-	to the clustered index B-tree. */
 	ut_ad(data_size < srv_sort_buf_size);
 
 	/* Reserve one byte for the end marker of row_merge_block_t. */
@@ -523,7 +537,7 @@ row_merge_buf_add(
 	buf->n_tuples++;
 	n_row_added++;
 
-	field = entry;
+	field = entry->fields;
 
 	/* Copy the data fields. */
 
@@ -580,19 +594,20 @@ row_merge_dup_report(
 /*************************************************************//**
 Compare two tuples.
 @return	1, 0, -1 if a is greater, equal, less, respectively, than b */
-static __attribute__((warn_unused_result, nonnull(3,4)))
+static __attribute__((warn_unused_result))
 int
 row_merge_tuple_cmp(
 /*================*/
 	ulint			n_uniq,	/*!< in: number of unique fields */
 	ulint			n_field,/*!< in: number of fields */
-	const dfield_t*		a,	/*!< in: first tuple to be compared */
-	const dfield_t*		b,	/*!< in: second tuple to be compared */
+	const mtuple_t&		a,	/*!< in: first tuple to be compared */
+	const mtuple_t&		b,	/*!< in: second tuple to be compared */
 	row_merge_dup_t*	dup)	/*!< in/out: for reporting duplicates,
 					NULL if non-unique index */
 {
 	int		cmp;
-	const dfield_t*	field	= a;
+	const dfield_t*	af	= a.fields;
+	const dfield_t*	bf	= b.fields;
 	ulint		n	= n_uniq;
 
 	ut_ad(n_field > 0);
@@ -602,7 +617,7 @@ row_merge_tuple_cmp(
 	found or we run out of fields to compare.  If !cmp at the
 	end, the tuples are equal. */
 	do {
-		cmp = cmp_dfield_dfield(a++, b++);
+		cmp = cmp_dfield_dfield(af++, bf++);
 	} while (!cmp && --n);
 
 	if (cmp) {
@@ -614,20 +629,20 @@ row_merge_tuple_cmp(
 		logically equal.  NULL columns are logically inequal,
 		although they are equal in the sorting order.  Find
 		out if any of the fields are NULL. */
-		for (const dfield_t* d = field; d != a; d++) {
-			if (dfield_is_null(d)) {
+		for (const dfield_t* df = a.fields; df != af; df++) {
+			if (dfield_is_null(df)) {
 				goto no_report;
 			}
 		}
 
-		row_merge_dup_report(dup, field);
+		row_merge_dup_report(dup, a.fields);
 	}
 
 no_report:
 	/* The n_uniq fields were equal, but we compare all fields so
 	that we will get the same order as in the B-tree. */
 	for (n = n_field - n_uniq + 1; --n; ) {
-		cmp = cmp_dfield_dfield(a++, b++);
+		cmp = cmp_dfield_dfield(af++, bf++);
 		if (cmp) {
 			return(cmp);
 		}
@@ -665,8 +680,8 @@ row_merge_tuple_sort(
 	ulint			n_field,/*!< in: number of fields */
 	row_merge_dup_t*	dup,	/*!< in/out: reporter of duplicates
 					(NULL if non-unique index) */
-	const dfield_t**	tuples,	/*!< in/out: tuples */
-	const dfield_t**	aux,	/*!< in/out: work area */
+	mtuple_t*		tuples,	/*!< in/out: tuples */
+	mtuple_t*		aux,	/*!< in/out: work area */
 	ulint			low,	/*!< in: lower bound of the
 					sorting area, inclusive */
 	ulint			high)	/*!< in: upper bound of the
@@ -708,14 +723,18 @@ row_merge_buf_write(
 {
 	const dict_index_t*	index	= buf->index;
 	ulint			n_fields= dict_index_get_n_fields(index);
+	ulint			n_null	= index->n_nullable
+		+ (dict_index_get_online_status(index)
+		   != ONLINE_INDEX_COMPLETE);
 	byte*			b	= &block[0];
 
-	ulint		i;
-
-	for (i = 0; i < buf->n_tuples; i++) {
-		const dfield_t*	entry	= buf->tuples[i];
+	for (ulint i = 0; i < buf->n_tuples; i++) {
+		const mtuple_t*	entry	= &buf->tuples[i];
 
-		row_merge_buf_encode(&b, index, entry, n_fields);
+		ut_ad(!entry->del_mark
+		      || dict_index_get_online_status(index)
+		      != ONLINE_INDEX_COMPLETE);
+		row_merge_buf_encode(&b, index, entry, n_fields, n_null);
 		ut_ad(b < &block[srv_sort_buf_size]);
 #ifdef UNIV_DEBUG
 		if (row_merge_print_write) {
@@ -889,7 +908,7 @@ row_merge_write(
 /********************************************************************//**
 Read a merge record.
 @return	pointer to next record, or NULL on I/O error or end of list */
-UNIV_INTERN __attribute__((nonnull))
+UNIV_INTERN
 const byte*
 row_merge_read_rec(
 /*===============*/
@@ -897,6 +916,7 @@ row_merge_read_rec(
 	mrec_buf_t*		buf,	/*!< in/out: secondary buffer */
 	const byte*		b,	/*!< in: pointer to record */
 	const dict_index_t*	index,	/*!< in: index of the record */
+	ulint			n_null,	/*!< in: size of the NULL-bit bitmap */
 	int			fd,	/*!< in: file descriptor */
 	ulint*			foffs,	/*!< in/out: file offset */
 	const mrec_t**		mrec,	/*!< out: pointer to merge record,
@@ -982,7 +1002,8 @@ err_exit:
 
 		*mrec = *buf + extra_size;
 
-		rec_init_offsets_comp_ordinary(*mrec, 0, index, offsets);
+		rec_init_offsets_comp_ordinary(
+			*mrec, 0, index, n_null, offsets);
 
 		data_size = rec_offs_data_size(offsets);
 
@@ -1001,7 +1022,7 @@ err_exit:
 
 	*mrec = b + extra_size;
 
-	rec_init_offsets_comp_ordinary(*mrec, 0, index, offsets);
+	rec_init_offsets_comp_ordinary(*mrec, 0, index, n_null, offsets);
 
 	data_size = rec_offs_data_size(offsets);
 	ut_ad(extra_size + data_size < sizeof *buf);
@@ -1431,18 +1452,29 @@ row_merge_read_clustered_index(
 
 		rec = page_cur_get_rec(cur);
 
-		if (rec_get_deleted_flag(rec, dict_table_is_comp(old_table))) {
-			/* Skip delete-marked records. This will make
-			the created indexes unuseable for transactions
+		offsets = rec_get_offsets(rec, clust_index, NULL,
+					  ULINT_UNDEFINED, &row_heap);
+
+		if (rec_get_deleted_flag(rec, dict_table_is_comp(old_table))
+		    && (!online
+			|| !trx_rw_is_active(
+				row_get_rec_trx_id(rec, clust_index, offsets),
+				NULL))) {
+			/* Skip delete-marked records, unless the
+			transaction is still active. Active transactions
+			would invoke row_log_online_op(), because during
+			ha_innobase::prepare_inplace_alter_table() there
+			must have been no active transactions on the table.
+
+			Skipping delete-marked records will make the
+			created indexes unuseable for transactions
 			whose read views were created before the index
 			creation completed, but preserving the history
-			would make it tricky to detect duplicate keys. */
+			would make it tricky to detect duplicate
+			keys. */
 			continue;
 		}
 
-		offsets = rec_get_offsets(rec, clust_index, NULL,
-					  ULINT_UNDEFINED, &row_heap);
-
 		/* This is essentially a READ UNCOMMITTED to fetch the
 		most recent version of the record. */
 
@@ -1707,7 +1739,7 @@ wait_again:
 		}							\
 		b##N = row_merge_read_rec(&block[N * srv_sort_buf_size],\
 					  &buf[N],			\
-					  b##N, index,			\
+					  b##N, index, n_null,		\
 					  file->fd, foffs##N,		\
 					  &mrec##N, offsets##N);	\
 		if (UNIV_UNLIKELY(!b##N)) {				\
@@ -1721,11 +1753,12 @@ wait_again:
 /*************************************************************//**
 Merge two blocks of records on disk and write a bigger block.
 @return	DB_SUCCESS or error code */
-static
+static __attribute__((nonnull, warn_unused_result))
 db_err
 row_merge_blocks(
 /*=============*/
 	const dict_index_t*	index,	/*!< in: index being created */
+	ulint			n_null,	/*!< in: size of the NULL-bit bitmap */
 	const merge_file_t*	file,	/*!< in: file containing
 					index entries */
 	row_merge_block_t*	block,	/*!< in/out: 3 buffers */
@@ -1778,11 +1811,11 @@ corrupt:
 	b1 = &block[srv_sort_buf_size];
 	b2 = &block[2 * srv_sort_buf_size];
 
-	b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, file->fd,
-				foffs0, &mrec0, offsets0);
+	b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, n_null,
+				file->fd, foffs0, &mrec0, offsets0);
 	b1 = row_merge_read_rec(&block[srv_sort_buf_size],
-				&buf[srv_sort_buf_size], b1, index, file->fd,
-				foffs1, &mrec1, offsets1);
+				&buf[srv_sort_buf_size], b1, index, n_null,
+				file->fd, foffs1, &mrec1, offsets1);
 	if (UNIV_UNLIKELY(!b0 && mrec0)
 	    || UNIV_UNLIKELY(!b1 && mrec1)) {
 
@@ -1832,11 +1865,12 @@ done1:
 /*************************************************************//**
 Copy a block of index entries.
 @return	TRUE on success, FALSE on failure */
-static __attribute__((nonnull))
+static __attribute__((nonnull, warn_unused_result))
 ibool
 row_merge_blocks_copy(
 /*==================*/
 	const dict_index_t*	index,	/*!< in: index being created */
+	ulint			n_null,	/*!< in: size of the NULL-bit bitmap */
 	const merge_file_t*	file,	/*!< in: input file */
 	row_merge_block_t*	block,	/*!< in/out: 3 buffers */
 	ulint*			foffs0,	/*!< in/out: input file offset */
@@ -1877,8 +1911,8 @@ corrupt:
 
 	b2 = &block[2 * srv_sort_buf_size];
 
-	b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, file->fd,
-				foffs0, &mrec0, offsets0);
+	b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, n_null,
+				file->fd, foffs0, &mrec0, offsets0);
 	if (UNIV_UNLIKELY(!b0 && mrec0)) {
 
 		goto corrupt;
@@ -1911,6 +1945,7 @@ row_merge(
 /*======*/
 	trx_t*			trx,	/*!< in: transaction */
 	const dict_index_t*	index,	/*!< in: index being created */
+	ulint			n_null,	/*!< in: size of the NULL-bit bitmap */
 	merge_file_t*		file,	/*!< in/out: file containing
 					index entries */
 	row_merge_block_t*	block,	/*!< in/out: 3 buffers */
@@ -1964,7 +1999,7 @@ row_merge(
 		/* Remember the offset number for this run */
 		run_offset[n_run++] = of.offset;
 
-		error = row_merge_blocks(index, file, block,
+		error = row_merge_blocks(index, n_null, file, block,
 					 &foffs0, &foffs1, &of, table);
 
 		if (error != DB_SUCCESS) {
@@ -1983,7 +2018,8 @@ row_merge(
 		/* Remember the offset number for this run */
 		run_offset[n_run++] = of.offset;
 
-		if (!row_merge_blocks_copy(index, file, block, &foffs0, &of)) {
+		if (!row_merge_blocks_copy(index, n_null, file, block,
+					   &foffs0, &of)) {
 			return(DB_CORRUPTION);
 		}
 	}
@@ -1998,7 +2034,8 @@ row_merge(
 		/* Remember the offset number for this run */
 		run_offset[n_run++] = of.offset;
 
-		if (!row_merge_blocks_copy(index, file, block, &foffs1, &of)) {
+		if (!row_merge_blocks_copy(index, n_null, file, block,
+					   &foffs1, &of)) {
 			return(DB_CORRUPTION);
 		}
 	}
@@ -2049,10 +2086,13 @@ row_merge_sort(
 					reporting erroneous key value
 					if applicable */
 {
-	ulint	half = file->offset / 2;
-	ulint	num_runs;
-	ulint*	run_offset;
-	db_err	error = DB_SUCCESS;
+	const ulint	n_null	= index->n_nullable
+		+ (dict_index_get_online_status(index)
+		   != ONLINE_INDEX_COMPLETE);
+	const ulint	half	= file->offset / 2;
+	ulint		num_runs;
+	ulint*		run_offset;
+	db_err		error	= DB_SUCCESS;
 
 	/* Record the number of merge runs we need to perform */
 	num_runs = file->offset;
@@ -2075,7 +2115,7 @@ row_merge_sort(
 
 	/* Merge the runs until we have one big run */
 	do {
-		error = row_merge(trx, index, file, block, tmpfd,
+		error = row_merge(trx, index, n_null, file, block, tmpfd,
 				  table, &num_runs, run_offset);
 
 		UNIV_MEM_ASSERT_RW(run_offset, num_runs * sizeof *run_offset);
@@ -2144,6 +2184,9 @@ row_merge_insert_index_tuples(
 /*==========================*/
 	trx_id_t		trx_id,	/*!< in: transaction identifier */
 	dict_index_t*		index,	/*!< in: index */
+	bool			del_marks,
+					/*!< in: whether some tuples may
+					be delete-marked */
 	ulint			zip_size,/*!< in: compressed page size of
 					 the old table, or 0 if uncompressed */
 	int			fd,	/*!< in: file descriptor */
@@ -2158,6 +2201,8 @@ row_merge_insert_index_tuples(
 	mrec_buf_t*		buf;
 
 	ut_ad(!(index->type & DICT_FTS));
+	ut_ad(del_marks == (dict_index_get_online_status(index)
+			    != ONLINE_INDEX_COMPLETE));
 
 	tuple_heap = mem_heap_create(1000);
 
@@ -2176,6 +2221,8 @@ row_merge_insert_index_tuples(
 	if (!row_merge_read(fd, foffs, block)) {
 		error = DB_CORRUPTION;
 	} else {
+		const ulint	n_null = index->n_nullable + del_marks;
+
 		buf = static_cast<mrec_buf_t*>(
 			mem_heap_alloc(heap, sizeof *buf));
 
@@ -2188,7 +2235,7 @@ row_merge_insert_index_tuples(
 			btr_cur_t	cursor;
 			mtr_t		mtr;
 
-			b = row_merge_read_rec(block, buf, b, index,
+			b = row_merge_read_rec(block, buf, b, index, n_null,
 					       fd, &foffs, &mrec, offsets);
 			if (UNIV_UNLIKELY(!b)) {
 				/* End of list, or I/O error */
@@ -2201,6 +2248,15 @@ row_merge_insert_index_tuples(
 			dtuple = row_rec_to_index_entry_low(
 				mrec, index, offsets, &n_ext, tuple_heap);
 
+			/* In mrec, the delete-mark flag is stored in
+			the NULL flags bitmap after the flags of any
+			fields that can be NULL. */
+			if (del_marks && (mrec[-1 - (int) index->n_nullable / 8]
+					  & (1 << (index->n_nullable & 7)))) {
+				dtuple_set_info_bits(dtuple,
+						     REC_INFO_DELETED_FLAG);
+			}
+
 			if (UNIV_UNLIKELY(n_ext)) {
 				row_merge_copy_blobs(mrec, offsets, zip_size,
 						     dtuple, tuple_heap);
@@ -3234,12 +3290,13 @@ wait_again:
 			DEBUG_FTS_SORT_PRINT("FTS_SORT: Complete Insert\n");
 #endif
 		} else {
-			error = row_merge_sort(trx, sort_idx, &merge_files[i],
-					       block, &tmpfd, table);
+			error = row_merge_sort(
+				trx, sort_idx, &merge_files[i],
+				block, &tmpfd, table);
 
 			if (error == DB_SUCCESS) {
 				error = row_merge_insert_index_tuples(
-					trx->id, sort_idx,
+					trx->id, sort_idx, online,
 					dict_table_zip_size(old_table),
 					merge_files[i].fd, block);
 			}

=== modified file 'storage/innobase/row/row0mysql.cc'
--- a/storage/innobase/row/row0mysql.cc	revid:sunny.bains@oracle.com-20120329070400-tvbzrhe57qpb8vgr
+++ b/storage/innobase/row/row0mysql.cc	revid:marko.makela@stripped29112900-pcp4ca6df7152bqa
@@ -30,6 +30,8 @@ Created 9/17/2000 Heikki Tuuri
 #include "row0mysql.ic"
 #endif
 
+#include <debug_sync.h>
+
 #include "row0ins.h"
 #include "row0merge.h"
 #include "row0sel.h"
@@ -1686,6 +1688,10 @@ run_again:
 		}
 
 		thr->lock_state= QUE_THR_LOCK_ROW;
+
+		DEBUG_SYNC(static_cast<THD*>(trx->mysql_thd),
+			   "row_update_for_mysql_error");
+
 		was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
 							&savept);
 		thr->lock_state= QUE_THR_LOCK_NOLOCK;

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (marko.makela:3871 to 3875) WL#5526 WL#5534marko.makela29 Mar