List:Commits« Previous MessageNext Message »
From:marko.makela Date:June 4 2012 9:51pm
Subject:bzr push into mysql-trunk-wl6255 branch (marko.makela:3934 to 3937) WL#6255
View as plain text  
 3937 Marko Mäkelä	2012-06-05
      WL#6255 ALTER_COLUMN_ORDER and DROP_COLUMN (DROP COLUMN and column reorder)
      
      ha_innobase_inplace_ctx(): Add col_map for mapping old column numbers
      to new ones when rebuilding a table.
      
      innobase_build_col_map(): Map old column numbers to new ones. Dropped
      columns are indicated by ULINT_UNDEFINED.
      
      prepare_inplace_alter_table_dict(): Invoke innobase_build_col_map()
      whenever rebuilding the table.
      
      row_log_table_apply(), row_merge_build_indexes(), row_build(),
      row_log_table_apply_convert_mrec(), row_log_table_apply_insert(),
      row_log_table_apply_update(), row_log_table_apply_op(),
      row_log_table_apply_ops(), row_merge_build_indexes(): Add the
      parameter col_map.
      
      row_merge_buf_add(): Remove the tweaks for FTS_DOC_ID. They will now
      be handled by the col_map.
      
      row_merge_read_clustered_index(): Add the parameter col_map.  Remove
      the array nullable[]. No longer tweak the DATA_NOT_NULL field of the
      row tuple, because it will have been set up in row_build().

    modified:
      mysql-test/r/alter_table.result
      mysql-test/suite/innodb/r/innodb-table-online.result
      mysql-test/suite/innodb/t/innodb-table-online.test
      storage/innobase/handler/handler0alter.cc
      storage/innobase/include/row0log.h
      storage/innobase/include/row0merge.h
      storage/innobase/include/row0row.h
      storage/innobase/row/row0log.cc
      storage/innobase/row/row0merge.cc
      storage/innobase/row/row0row.cc
      storage/innobase/row/row0undo.cc
      storage/innobase/row/row0upd.cc
      storage/innobase/row/row0vers.cc
 3936 Marko Mäkelä	2012-06-04
      fts_get_doc_id_from_rec(): Remove an unnecessary cast and add assertions.

    modified:
      storage/innobase/fts/fts0fts.cc
 3935 Marko Mäkelä	2012-06-04
      Crank up Valgrind instrumentation.
      
      dtuple_create_from_mem(): Add Valgrind instrumentation for tuple->fields.
      dtuple_create(): Remove the Valgrind instrumentation.

    modified:
      storage/innobase/include/data0data.ic
 3934 Marko Mäkelä	2012-06-04
      WL#6255 bug fix: Disallow online addition of a hidden FTS_DOC_ID.
      
      If FTS_DOC_ID is being explicitly added or renamed by the user,
      that is OK, because the column is supposed to be unique already.

    modified:
      mysql-test/suite/innodb/r/innodb-alter.result
      mysql-test/suite/innodb/t/innodb-alter.test
      storage/innobase/handler/ha_innodb.h
      storage/innobase/handler/handler0alter.cc
=== modified file 'mysql-test/r/alter_table.result'
--- a/mysql-test/r/alter_table.result	revid:marko.makela@stripped105343-n95ug4qqgn787nea
+++ b/mysql-test/r/alter_table.result	revid:marko.makela@stripped75k2gp2kp
@@ -1704,8 +1704,8 @@ ALTER TABLE tm1 ADD PRIMARY KEY(a);
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
 ALTER TABLE ti1 DROP COLUMN d2;
-affected rows: 2
-info: Records: 2  Duplicates: 0  Warnings: 0
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
 ALTER TABLE tm1 DROP COLUMN d2;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
@@ -1752,14 +1752,14 @@ ALTER TABLE tm1 MODIFY COLUMN e INT;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
 ALTER TABLE ti1 MODIFY COLUMN e INT AFTER h;
-affected rows: 2
-info: Records: 2  Duplicates: 0  Warnings: 0
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
 ALTER TABLE tm1 MODIFY COLUMN e INT AFTER h;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
 ALTER TABLE ti1 MODIFY COLUMN e INT FIRST;
-affected rows: 2
-info: Records: 2  Duplicates: 0  Warnings: 0
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
 ALTER TABLE tm1 MODIFY COLUMN e INT FIRST;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
@@ -1782,14 +1782,14 @@ ALTER TABLE tm1 MODIFY COLUMN h VARCHAR(
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
 ALTER TABLE ti1 MODIFY COLUMN h VARCHAR(30) AFTER d;
-affected rows: 2
-info: Records: 2  Duplicates: 0  Warnings: 0
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
 ALTER TABLE tm1 MODIFY COLUMN h VARCHAR(30) AFTER d;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0
 ALTER TABLE ti1 DROP COLUMN h;
-affected rows: 2
-info: Records: 2  Duplicates: 0  Warnings: 0
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
 ALTER TABLE tm1 DROP COLUMN h;
 affected rows: 2
 info: Records: 2  Duplicates: 0  Warnings: 0

=== modified file 'mysql-test/suite/innodb/r/innodb-table-online.result'
--- a/mysql-test/suite/innodb/r/innodb-table-online.result	revid:marko.makela@stripped343-n95ug4qqgn787nea
+++ b/mysql-test/suite/innodb/r/innodb-table-online.result	revid:marko.makela@stripped0604214744-29tpakp75k2gp2kp
@@ -215,7 +215,7 @@ ERROR 42000: Multiple primary key define
 ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c22f), CHANGE c2 c22f INT;
 ERROR 23000: Duplicate entry '5' for key 'PRIMARY'
 ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c1,c22f), CHANGE c2 c22f INT,
-CHANGE c3 c3 TEXT NULL;
+CHANGE c3 c3 TEXT NULL, CHANGE c1 c1 INT AFTER c22f;
 SET DEBUG_SYNC = 'now WAIT_FOR rebuilt3';
 SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
 name	count
@@ -254,16 +254,16 @@ ALTER TABLE t1 DROP PRIMARY KEY, ADD PRI
 ERROR 23000: Duplicate entry '' for key 'PRIMARY'
 UPDATE t1 SET c3 = NULL WHERE c3 = '';
 SET lock_wait_timeout = 1;
-ALTER TABLE t1 ADD PRIMARY KEY c3p5(c3(5));
+ALTER TABLE t1 DROP COLUMN c22f, ADD PRIMARY KEY c3p5(c3(5));
 ERROR 42000: Multiple primary key defined
-ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5)),
+ALTER TABLE t1 DROP COLUMN c22f, DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5)),
 ALGORITHM = INPLACE;
 ERROR 22004: Invalid use of NULL value
 ALTER TABLE t1 MODIFY c3 TEXT NOT NULL;
 ERROR 22004: Invalid use of NULL value
 UPDATE t1 SET c3=CONCAT(c1,REPEAT('foo',c1)) WHERE c3 IS NULL;
 SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created0 WAIT_FOR ins_done0';
-ALTER TABLE t1 MODIFY c3 TEXT NOT NULL;
+ALTER TABLE t1 MODIFY c3 TEXT NOT NULL, DROP COLUMN c22f;
 SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created0';
 BEGIN;
 INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti');
@@ -273,7 +273,7 @@ ERROR 22004: Invalid use of NULL value
 ROLLBACK;
 ALTER TABLE t1 MODIFY c3 TEXT NOT NULL;
 SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created WAIT_FOR ins_done';
-ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5));
+ALTER TABLE t1 DROP PRIMARY KEY, DROP COLUMN c22f, ADD PRIMARY KEY c3p5(c3(5));
 SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
 SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out';
 INSERT INTO t1 VALUES(347,33101,NULL);
@@ -301,14 +301,14 @@ ERROR HY000: Lock wait timeout exceeded;
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` int(11) NOT NULL,
   `c22f` int(11) NOT NULL DEFAULT '0',
+  `c1` int(11) NOT NULL DEFAULT '0',
   `c3` text NOT NULL,
   PRIMARY KEY (`c1`,`c22f`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT
-ALTER TABLE t1 ROW_FORMAT=REDUNDANT, ALGORITHM = INPLACE;
+ALTER TABLE t1 DROP COLUMN c3, ROW_FORMAT=REDUNDANT, ALGORITHM = INPLACE;
 SELECT * FROM t1;
-c1	c22f	c3
+c22f	c1
 SET DEBUG_SYNC = 'RESET';
 SET DEBUG = '';
 SET GLOBAL innodb_monitor_disable = module_ddl;

=== modified file 'mysql-test/suite/innodb/t/innodb-table-online.test'
--- a/mysql-test/suite/innodb/t/innodb-table-online.test	revid:marko.makela@stripped5343-n95ug4qqgn787nea
+++ b/mysql-test/suite/innodb/t/innodb-table-online.test	revid:marko.makela@stripped604214744-29tpakp75k2gp2kp
@@ -221,7 +221,7 @@ ALTER TABLE t1 ADD PRIMARY KEY(c22f), CH
 ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c22f), CHANGE c2 c22f INT;
 --send
 ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c1,c22f), CHANGE c2 c22f INT,
-CHANGE c3 c3 TEXT NULL;
+CHANGE c3 c3 TEXT NULL, CHANGE c1 c1 INT AFTER c22f;
 
 connection default;
 SET DEBUG_SYNC = 'now WAIT_FOR rebuilt3';
@@ -251,9 +251,9 @@ ALTER TABLE t1 DROP PRIMARY KEY, ADD PRI
 UPDATE t1 SET c3 = NULL WHERE c3 = '';
 SET lock_wait_timeout = 1;
 --error ER_MULTIPLE_PRI_KEY
-ALTER TABLE t1 ADD PRIMARY KEY c3p5(c3(5));
+ALTER TABLE t1 DROP COLUMN c22f, ADD PRIMARY KEY c3p5(c3(5));
 --error ER_INVALID_USE_OF_NULL
-ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5)),
+ALTER TABLE t1 DROP COLUMN c22f, DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5)),
 ALGORITHM = INPLACE;
 
 --error ER_INVALID_USE_OF_NULL
@@ -262,7 +262,7 @@ UPDATE t1 SET c3=CONCAT(c1,REPEAT('foo',
 
 SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created0 WAIT_FOR ins_done0';
 --send
-ALTER TABLE t1 MODIFY c3 TEXT NOT NULL;
+ALTER TABLE t1 MODIFY c3 TEXT NOT NULL, DROP COLUMN c22f;
 
 connection default;
 
@@ -284,7 +284,7 @@ ALTER TABLE t1 MODIFY c3 TEXT NOT NULL;
 
 SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created WAIT_FOR ins_done';
 --send
-ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY c3p5(c3(5));
+ALTER TABLE t1 DROP PRIMARY KEY, DROP COLUMN c22f, ADD PRIMARY KEY c3p5(c3(5));
 
 connection default;
 SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
@@ -342,7 +342,7 @@ reap;
 SHOW CREATE TABLE t1;
 # The source tablespace was discarded during the previous online rebuild.
 # The rebuild will re-create t1 as an empty table.
-ALTER TABLE t1 ROW_FORMAT=REDUNDANT, ALGORITHM = INPLACE;
+ALTER TABLE t1 DROP COLUMN c3, ROW_FORMAT=REDUNDANT, ALGORITHM = INPLACE;
 SELECT * FROM t1;
 
 SET DEBUG_SYNC = 'RESET';

=== modified file 'storage/innobase/fts/fts0fts.cc'
--- a/storage/innobase/fts/fts0fts.cc	revid:marko.makela@strippedqqgn787nea
+++ b/storage/innobase/fts/fts0fts.cc	revid:marko.makela@stripped
@@ -4853,13 +4853,13 @@ fts_get_doc_id_from_rec(
 
 	col_no = dict_col_get_clust_pos(
 		&table->cols[table->fts->doc_col], clust_index);
+	ut_ad(col_no != ULINT_UNDEFINED);
 
-	/* We have no choice but to cast rec here :-( */
-	data = rec_get_nth_field((rec_t*) rec, offsets, col_no, &len);
+	data = rec_get_nth_field(rec, offsets, col_no, &len);
 
 	ut_a(len == 8);
-	ut_a(len == sizeof(doc_id));
-	doc_id = (doc_id_t) mach_read_from_8(data);
+	ut_ad(8 == sizeof(doc_id));
+	doc_id = static_cast<doc_id_t>(mach_read_from_8(data));
 
 	return(doc_id);
 }

=== modified file 'storage/innobase/handler/handler0alter.cc'
--- a/storage/innobase/handler/handler0alter.cc	revid:marko.makela@oracle.com-20120604105343-n95ug4qqgn787nea
+++ b/storage/innobase/handler/handler0alter.cc	revid:marko.makela@oracle.com-20120604214744-29tpakp75k2gp2kp
@@ -59,12 +59,12 @@ static const Alter_inplace_info::HA_ALTE
 	| Alter_inplace_info::CHANGE_CREATE_OPTION
 	| Alter_inplace_info::ALTER_COLUMN_NULLABLE
 	| Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
+	| Alter_inplace_info::ALTER_COLUMN_ORDER
+	| Alter_inplace_info::DROP_COLUMN
 	/*
 	| Alter_inplace_info::ALTER_COLUMN_TYPE
-	| Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH,
-	| Alter_inplace_info::ALTER_COLUMN_ORDER
+	| Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH
 	| Alter_inplace_info::ADD_COLUMN
-	| Alter_inplace_info::DROP_COLUMN
 	*/
 	;
 
@@ -1759,6 +1759,8 @@ public:
 	trx_t*		trx;
 	/** table where the indexes are being created or dropped */
 	dict_table_t*	indexed_table;
+	/** mapping of old column numbers to new ones, or NULL */
+	const ulint*	col_map;
 	ha_innobase_inplace_ctx(trx_t* user_trx,
 				dict_index_t** add_arg,
 				const ulint* add_key_numbers_arg,
@@ -1772,7 +1774,8 @@ public:
 				bool online_arg,
 				mem_heap_t* heap_arg,
 				trx_t* trx_arg,
-				dict_table_t* indexed_table_arg) :
+				dict_table_t* indexed_table_arg,
+				const ulint* col_map_arg) :
 		inplace_alter_handler_ctx(),
 		add (add_arg), add_key_numbers (add_key_numbers_arg),
 		num_to_add (num_to_add_arg),
@@ -1780,7 +1783,8 @@ public:
 		drop_fk (drop_fk_arg), num_to_drop_fk (num_to_drop_fk_arg),
 		add_fk (add_fk_arg), num_to_add_fk (num_to_add_fk_arg),
 		online (online_arg), heap (heap_arg), trx (trx_arg),
-		indexed_table (indexed_table_arg) {
+		indexed_table (indexed_table_arg),
+		col_map (col_map_arg) {
 #ifdef UNIV_DEBUG
 		for (ulint i = 0; i < num_to_add; i++) {
 			ut_ad(!add[i]->to_be_dropped);
@@ -2041,6 +2045,106 @@ innobase_check_foreigns(
 	return(false);
 }
 
+/** Construct the translation table for reordering, dropping or
+adding columns.
+
+@param ha_alter_info	Data used during in-place alter
+@param altered_table	MySQL table that is being altered
+@param table		MySQL table as it is before the ALTER operation
+@param new_table	InnoDB table corresponding to MySQL altered_table
+@param old_table	InnoDB table corresponding to MYSQL table
+@param heap	Memory heap where allocated
+@return	array of integers, mapping column numbers in the table
+to column numbers in altered_table */
+static __attribute__((nonnull, warn_unused_result))
+const ulint*
+innobase_build_col_map(
+/*===================*/
+	Alter_inplace_info*	ha_alter_info,
+	const TABLE*		altered_table,
+	const TABLE*		table,
+	const dict_table_t*	new_table,
+	const dict_table_t*	old_table,
+	mem_heap_t*		heap)
+{
+	DBUG_ENTER("innobase_build_col_map");
+	DBUG_ASSERT(altered_table != table);
+	DBUG_ASSERT(new_table != old_table);
+	DBUG_ASSERT(dict_table_get_n_cols(new_table)
+		    >= altered_table->s->fields + DATA_N_SYS_COLS);
+	DBUG_ASSERT(dict_table_get_n_cols(old_table)
+		    >= table->s->fields + DATA_N_SYS_COLS);
+
+	ulint*	col_map = static_cast<ulint*>(
+		mem_heap_alloc(heap, old_table->n_cols * sizeof *col_map));
+
+	List_iterator_fast<Create_field> cf_it;
+	cf_it.init(ha_alter_info->alter_info->create_list);
+	uint i = 0;
+
+	/* Any dropped columns will map to ULINT_UNDEFINED. */
+	for (uint old_i = 0; old_i + DATA_N_SYS_COLS < old_table->n_cols;
+	     old_i++) {
+		col_map[old_i] = ULINT_UNDEFINED;
+	}
+
+	while (const Create_field* new_field = cf_it++) {
+		for (uint old_i = 0; table->field[old_i]; old_i++) {
+			const Field* field = table->field[old_i];
+			if (new_field->field == field) {
+				col_map[old_i] = i;
+				goto found_col;
+			}
+		}
+		/* ALTER_ADD_COLUMN */
+		ut_ad(0);//TODO
+found_col:
+		i++;
+		continue;
+	}
+
+	DBUG_ASSERT(i == altered_table->s->fields);
+
+	i = table->s->fields;
+
+	/* Add the InnoDB hidden FTS_DOC_ID column, if any. */
+	if (i + DATA_N_SYS_COLS < old_table->n_cols) {
+		/* There should be exactly one extra field,
+		the FTS_DOC_ID. */
+		DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(old_table,
+						 DICT_TF2_FTS_HAS_DOC_ID));
+		DBUG_ASSERT(i + DATA_N_SYS_COLS + 1 == old_table->n_cols);
+		DBUG_ASSERT(!strcmp(dict_table_get_col_name(
+					    old_table, table->s->fields),
+				    FTS_DOC_ID_COL_NAME));
+		if (altered_table->s->fields + DATA_N_SYS_COLS
+		    < new_table->n_cols) {
+			DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(
+					    new_table,
+					    DICT_TF2_FTS_HAS_DOC_ID));
+			DBUG_ASSERT(altered_table->s->fields
+				    + DATA_N_SYS_COLS + 1
+				    == new_table->n_cols);
+			col_map[i] = altered_table->s->fields;
+		} else {
+			DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
+					    new_table,
+					    DICT_TF2_FTS_HAS_DOC_ID));
+			col_map[i] = ULINT_UNDEFINED;
+		}
+	} else {
+		DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
+				    old_table,
+				    DICT_TF2_FTS_HAS_DOC_ID));
+	}
+
+	for (; i < old_table->n_cols; i++) {
+		col_map[i] = i + new_table->n_cols - old_table->n_cols;
+	}
+
+	DBUG_RETURN(col_map);
+}
+
 /** Update internal structures with concurrent writes blocked,
 while preparing ALTER TABLE.
 
@@ -2096,6 +2200,7 @@ prepare_inplace_alter_table_dict(
 	ulint			new_clustered	= 0;
 	dberr_t			error;
 	THD*			user_thd	= user_trx->mysql_thd;
+	const ulint*		col_map;
 
 	const bool locked =
 		add_fts_doc_id
@@ -2157,10 +2262,9 @@ prepare_inplace_alter_table_dict(
 	column is to be added, and the primary index definition
 	is just copied from old table and stored in indexdefs[0] */
 	DBUG_ASSERT(!add_fts_doc_id || new_clustered);
-	DBUG_ASSERT(!!new_clustered
-		    == ((ha_alter_info->handler_flags
-			 & INNOBASE_INPLACE_REBUILD)
-			|| add_fts_doc_id));
+	DBUG_ASSERT(!!new_clustered ==
+		    (innobase_need_rebuild(ha_alter_info)
+		     || add_fts_doc_id));
 
 	/* Allocate memory for dictionary index definitions */
 
@@ -2242,9 +2346,7 @@ prepare_inplace_alter_table_dict(
 				| DICT_TF2_FTS;
 		}
 
-		if (add_fts_doc_id_idx) {
-			DBUG_ASSERT(flags2 & DICT_TF2_FTS);
-		}
+		DBUG_ASSERT(!add_fts_doc_id_idx || (flags2 & DICT_TF2_FTS));
 
 		/* Create the table. */
 		trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
@@ -2335,6 +2437,8 @@ col_fail:
 			fts_add_doc_id_column(indexed_table, heap);
 			indexed_table->fts->doc_col = fts_doc_id_col;
 			ut_ad(fts_doc_id_col == altered_table->s->fields);
+		} else if (indexed_table->fts) {
+			indexed_table->fts->doc_col = fts_doc_id_col;
 		}
 
 		error = row_create_table_for_mysql(indexed_table, trx);
@@ -2379,6 +2483,13 @@ col_fail:
 			trx_commit_for_mysql(user_trx);
 			DBUG_RETURN(true);
 		}
+
+		col_map = innobase_build_col_map(
+			ha_alter_info, altered_table, old_table,
+			indexed_table, user_table, heap);
+	} else {
+		DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info));
+		col_map = NULL;
 	}
 
 	/* Assign table_id, so that no table id of
@@ -2516,11 +2627,6 @@ op_ok:
 			}
 		}
 
-		if (indexed_table != user_table && user_table->fts) {
-			indexed_table->fts->doc_col
-				= user_table->fts->doc_col;
-		}
-
 		ut_ad(trx_get_dict_operation(trx) == op);
 	}
 
@@ -2556,7 +2662,7 @@ error_handling:
 			drop_index, n_drop_index,
 			drop_foreign, n_drop_foreign,
 			add_foreign, n_add_foreign,
-			!locked, heap, trx, indexed_table);
+			!locked, heap, trx, indexed_table, col_map);
 		DBUG_RETURN(false);
 	case DB_TABLESPACE_ALREADY_EXISTS:
 		my_error(ER_TABLE_EXISTS_ERROR, MYF(0), "(unknown)");
@@ -3076,7 +3182,7 @@ index_needed:
 					drop_index, n_drop_index,
 					drop_fk, n_drop_fk,
 					add_fk, n_add_fk, !locked,
-					heap, NULL, indexed_table);
+					heap, NULL, indexed_table, NULL);
 		}
 
 func_exit:
@@ -3224,7 +3330,8 @@ ok_exit:
 		prebuilt->trx,
 		prebuilt->table, ctx->indexed_table,
 		ctx->online,
-		ctx->add, ctx->add_key_numbers, ctx->num_to_add, table);
+		ctx->add, ctx->add_key_numbers, ctx->num_to_add, table,
+		ctx->col_map);
 #ifndef DBUG_OFF
 oom:
 #endif /* !DBUG_OFF */
@@ -3232,7 +3339,7 @@ oom:
 	    && ctx->indexed_table != prebuilt->table) {
 		DEBUG_SYNC_C("row_log_table_apply1_before");
 		error = row_log_table_apply(
-			ctx->thr, prebuilt->table, table);
+			ctx->thr, prebuilt->table, table, ctx->col_map);
 	}
 
 	/* After an error, remove all those index definitions
@@ -3811,8 +3918,8 @@ ha_innobase::commit_inplace_alter_table(
 		if (ctx->online) {
 			DEBUG_SYNC_C("row_log_table_apply2_before");
 			error = row_log_table_apply(
-				ctx->thr, prebuilt->table,
-				table);
+				ctx->thr, prebuilt->table, table,
+				ctx->col_map);
 
 			switch (error) {
 				KEY*	dup_key;

=== modified file 'storage/innobase/include/data0data.ic'
--- a/storage/innobase/include/data0data.ic	revid:marko.makela@stripped
+++ b/storage/innobase/include/data0data.ic	revid:marko.makela@oracle.com-20120604214744-29tpakp75k2gp2kp
@@ -407,6 +407,8 @@ dtuple_create_from_mem(
 		}
 	}
 #endif
+	UNIV_MEM_ASSERT_W(tuple->fields, n_fields * sizeof *tuple->fields);
+	UNIV_MEM_INVALID(tuple->fields, n_fields * sizeof *tuple->fields);
 	return(tuple);
 }
 
@@ -434,10 +436,6 @@ dtuple_create(
 
 	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/row0log.h'
--- a/storage/innobase/include/row0log.h	revid:marko.makela@strippedg4qqgn787nea
+++ b/storage/innobase/include/row0log.h	revid:marko.makela@strippedp
@@ -176,8 +176,10 @@ row_log_table_apply(
 	que_thr_t*	thr,	/*!< in: query graph */
 	dict_table_t*	old_table,
 				/*!< in: old table */
-	struct TABLE*	table)	/*!< in/out: MySQL table
+	struct TABLE*	table,	/*!< in/out: MySQL table
 				(for reporting duplicates) */
+	const ulint*	col_map)/*!< in: mapping of old column
+				numbers to new ones, or NULL */
 	__attribute__((nonnull, warn_unused_result));
 
 /******************************************************//**

=== modified file 'storage/innobase/include/row0merge.h'
--- a/storage/innobase/include/row0merge.h	revid:marko.makela@strippedgn787nea
+++ b/storage/innobase/include/row0merge.h	revid:marko.makela@stripped
@@ -290,10 +290,13 @@ row_merge_build_indexes(
 	dict_index_t**	indexes,	/*!< in: indexes to be created */
 	const ulint*	key_numbers,	/*!< in: MySQL key numbers */
 	ulint		n_indexes,	/*!< in: size of indexes[] */
-	struct TABLE*	table)		/*!< in/out: MySQL table, for
+	struct TABLE*	table,		/*!< in/out: MySQL table, for
 					reporting erroneous key value
 					if applicable */
-	__attribute__((nonnull, warn_unused_result));
+	const ulint*	col_map)	/*!< in: mapping of old column
+					numbers to new ones, or NULL
+					if old_table == new_table */
+	__attribute__((nonnull(1,2,3,5,6,8), warn_unused_result));
 /********************************************************************//**
 Write a buffer to a block. */
 UNIV_INTERN

=== modified file 'storage/innobase/include/row0row.h'
--- a/storage/innobase/include/row0row.h	revid:marko.makela@oracle.com-20120604105343-n95ug4qqgn787nea
+++ b/storage/innobase/include/row0row.h	revid:marko.makela@stripped0604214744-29tpakp75k2gp2kp
@@ -143,12 +143,14 @@ row_build(
 					consulted instead; the user
 					columns in this table should be
 					the same columns as in index->table */
+	const ulint*		col_map,/*!< in: mapping of old column
+					numbers to new ones, or NULL */
 	row_ext_t**		ext,	/*!< out, own: cache of
 					externally stored column
 					prefixes, or NULL */
 	mem_heap_t*		heap)	/*!< in: memory heap from which
 					the memory needed is allocated */
-	__attribute__((nonnull(2,3,7), warn_unused_result));
+	__attribute__((nonnull(2,3,8), warn_unused_result));
 /*******************************************************************//**
 Converts an index record to a typed data tuple.
 @return index entry built; does not set info_bits, and the data fields

=== modified file 'storage/innobase/row/row0log.cc'
--- a/storage/innobase/row/row0log.cc	revid:marko.makela@stripped4qqgn787nea
+++ b/storage/innobase/row/row0log.cc	revid:marko.makela@stripped
@@ -1009,6 +1009,8 @@ row_log_table_apply_convert_mrec(
 	const mrec_t*		mrec,		/*!< in: merge record */
 	const dict_index_t*	index,		/*!< in: index of mrec */
 	const ulint*		offsets,	/*!< in: offsets of mrec */
+	const ulint*		col_map,	/*!< in: mapping of old column
+						numbers to new ones, or NULL */
 	mem_heap_t*		heap,		/*!< in/out: memory heap */
 	dict_table_t*		new_table,	/*!< in/out: table
 						being rebuilt */
@@ -1018,16 +1020,33 @@ row_log_table_apply_convert_mrec(
 	dtuple_t*	row;
 
 	/* This is based on row_build(). */
-	row = dtuple_create(heap, dict_table_get_n_cols(index->table));
-	dict_table_copy_types(row, index->table);
+	row = dtuple_create(heap, dict_table_get_n_cols(new_table));
+	dict_table_copy_types(row, new_table);
 
 	for (ulint i = 0; i < rec_offs_n_fields(offsets); i++) {
 		const dict_field_t*	ind_field
 			= dict_index_get_nth_field(index, i);
+
+		if (ind_field->prefix_len) {
+			/* Column prefixes can only occur in key
+			fields, which cannot be stored externally. For
+			a column prefix, there should also be the full
+			field in the clustered index tuple. The row
+			tuple comprises full fields, not prefixes. */
+			ut_ad(!rec_offs_nth_extern(offsets, i));
+			continue;
+		}
+
 		const dict_col_t*	col
 			= dict_field_get_col(ind_field);
 		ulint			col_no
-			= dict_col_get_no(col);
+			= col_map[dict_col_get_no(col)];
+
+		if (col_no == ULINT_UNDEFINED) {
+			/* dropped column */
+			continue;
+		}
+
 		dfield_t*		dfield
 			= dtuple_get_nth_field(row, col_no);
 		ulint			len;
@@ -1039,24 +1058,17 @@ row_log_table_apply_convert_mrec(
 				dict_table_zip_size(index->table),
 				i, &len, heap);
 			ut_a(data);
-			dfield_set_data(dfield, data, len);
-		} else if (ind_field->prefix_len == 0) {
+		} else {
 			data = rec_get_nth_field(mrec, offsets, i, &len);
-			dfield_set_data(dfield, data, len);
 		}
 
-		ut_ad(dict_col_type_assert_equal(col,
-						 dfield_get_type(dfield)));
+		dfield_set_data(dfield, data, len);
 
 		/* See if any columns were changed to NULL or NOT NULL. */
-		/* TODO: adjust for ADD COLUMN, DROP COLUMN */
-		if (col_no >= dict_table_get_n_cols(new_table)) {
-			continue;
-		}
-
 		const dict_col_t*	new_col
 			= dict_table_get_nth_col(new_table, col_no);
 		ut_ad(new_col->mtype == col->mtype);
+
 		/* Assert that prtype matches except for nullability. */
 		ut_ad(!((new_col->prtype ^ col->prtype) & ~DATA_NOT_NULL));
 		ut_ad(!((new_col->prtype ^ dfield_get_type(dfield)->prtype)
@@ -1080,9 +1092,6 @@ row_log_table_apply_convert_mrec(
 						 dfield_get_type(dfield)));
 	}
 
-	/* TODO: convert row to new_table->cols, in case columns are
-	added or dropped or reordered */
-
 	*error = DB_SUCCESS;
 	return(row);
 }
@@ -1166,6 +1175,8 @@ row_log_table_apply_insert(
 	const ulint*		offsets,	/*!< in: offsets of mrec */
 	mem_heap_t*		offsets_heap,	/*!< in/out: memory heap
 						that can be emptied */
+	const ulint*		col_map,	/*!< in: mapping of old column
+						numbers to new ones, or NULL */
 	mem_heap_t*		heap,		/*!< in/out: memory heap */
 	dict_table_t*		new_table,	/*!< in/out: table
 						being rebuilt */
@@ -1174,7 +1185,7 @@ row_log_table_apply_insert(
 {
 	dberr_t		error;
 	const dtuple_t*	row	= row_log_table_apply_convert_mrec(
-		mrec, dup->index, offsets, heap, new_table,
+		mrec, dup->index, offsets, col_map, heap, new_table,
 		&error);
 
 	ut_ad(!row == (error != DB_SUCCESS));
@@ -1217,7 +1228,7 @@ row_log_table_apply_delete_low(
 		/* Build a row template for purging secondary index entries. */
 		row = row_build(
 			ROW_COPY_DATA, index, btr_pcur_get_rec(pcur),
-			offsets, NULL, &ext, heap);
+			offsets, NULL, NULL, &ext, heap);
 	} else {
 		row = NULL;
 	}
@@ -1393,6 +1404,8 @@ row_log_table_apply_update(
 	const ulint*		offsets,	/*!< in: offsets of mrec */
 	mem_heap_t*		offsets_heap,	/*!< in/out: memory heap
 						that can be emptied */
+	const ulint*		col_map,	/*!< in: mapping of old column
+						numbers to new ones, or NULL */
 	mem_heap_t*		heap,		/*!< in/out: memory heap */
 	dict_table_t*		new_table,	/*!< in/out: table
 						being rebuilt */
@@ -1416,7 +1429,7 @@ row_log_table_apply_update(
 	      + (dup->index->online_log->same_pk ? 0 : 2));
 
 	row = row_log_table_apply_convert_mrec(
-		mrec, dup->index, offsets, heap, new_table, &error);
+		mrec, dup->index, offsets, col_map, heap, new_table, &error);
 
 	ut_ad(!row == (error != DB_SUCCESS));
 
@@ -1546,7 +1559,8 @@ delete_insert:
 		the record. */
 		old_row = row_build(ROW_COPY_DATA, index,
 				    btr_pcur_get_rec(&pcur),
-				    cur_offsets, NULL, &old_ext, heap);
+				    cur_offsets, new_table,
+				    col_map, &old_ext, heap);
 		ut_ad(old_row);
 	} else {
 		old_row = NULL;
@@ -1647,6 +1661,8 @@ row_log_table_apply_op(
 						DB_TRX_ID in new index */
 	dict_table_t*		new_table,	/*!< in/out: table
 						being rebuilt */
+	const ulint*		col_map,	/*!< in: mapping of old column
+						numbers to new ones, or NULL */
 	row_merge_dup_t*	dup,		/*!< in/out: for reporting
 						duplicate key errors */
 	dberr_t*		error,		/*!< out: DB_SUCCESS
@@ -1712,7 +1728,7 @@ row_log_table_apply_op(
 				    trx_read_trx_id(db_trx_id))) {
 				*error = row_log_table_apply_insert(
 					thr, mrec, offsets, offsets_heap,
-					heap, new_table, dup);
+					col_map, heap, new_table, dup);
 			}
 		}
 		break;
@@ -1881,8 +1897,9 @@ row_log_table_apply_op(
 				    trx_read_trx_id(db_trx_id))) {
 				*error = row_log_table_apply_update(
 					thr, trx_id_col, new_trx_id_col,
-					mrec, offsets, offsets_heap, heap,
-					new_table, dup, old_pk);
+					mrec, offsets, offsets_heap,
+					col_map,
+					heap, new_table, dup, old_pk);
 			}
 		}
 
@@ -1902,6 +1919,8 @@ dberr_t
 row_log_table_apply_ops(
 /*====================*/
 	que_thr_t*	thr,	/*!< in: query graph */
+	const ulint*	col_map,/*!< in: mapping of old column
+				numbers to new ones, or NULL */
 	row_merge_dup_t*dup)	/*!< in/out: for reporting duplicate key
 				errors */
 {
@@ -2076,7 +2095,7 @@ all_done:
 		       (&index->online_log->head.buf)[1] - mrec_end);
 		mrec = row_log_table_apply_op(
 			thr, trx_id_col, new_trx_id_col,
-			index->online_log->table,
+			index->online_log->table, col_map,
 			dup, &error, offsets_heap, heap,
 			index->online_log->head.buf,
 			(&index->online_log->head.buf)[1], offsets);
@@ -2175,7 +2194,7 @@ all_done:
 
 		next_mrec = row_log_table_apply_op(
 			thr, trx_id_col, new_trx_id_col,
-			index->online_log->table,
+			index->online_log->table, col_map,
 			dup, &error, offsets_heap, heap,
 			mrec, mrec_end, offsets);
 
@@ -2242,8 +2261,10 @@ row_log_table_apply(
 	que_thr_t*	thr,	/*!< in: query graph */
 	dict_table_t*	old_table,
 				/*!< in: old table */
-	struct TABLE*	table)	/*!< in/out: MySQL table
+	struct TABLE*	table,	/*!< in/out: MySQL table
 				(for reporting duplicates) */
+	const ulint*	col_map)/*!< in: mapping of old column
+				numbers to new ones, or NULL */
 {
 	dberr_t		error;
 	dict_index_t*	clust_index;
@@ -2270,7 +2291,7 @@ row_log_table_apply(
 		ut_ad(0);
 		error = DB_ERROR;
 	} else {
-		error = row_log_table_apply_ops(thr, &dup);
+		error = row_log_table_apply_ops(thr, col_map, &dup);
 	}
 
 	rw_lock_x_unlock(dict_index_get_lock(clust_index));

=== modified file 'storage/innobase/row/row0merge.cc'
--- a/storage/innobase/row/row0merge.cc	revid:marko.makela@oracle.com-20120604105343-n95ug4qqgn787nea
+++ b/storage/innobase/row/row0merge.cc	revid:marko.makela@stripped0604214744-29tpakp75k2gp2kp
@@ -284,7 +284,7 @@ row_merge_buf_add(
 	dict_index_t*		fts_index,/*!< in: fts index to be created */
 	const dict_table_t*	old_table,/*!< in: original table */
 	fts_psort_t*		psort_info, /*!< in: parallel sort info */
-	const dtuple_t*		row,	/*!< in: row in clustered index */
+	const dtuple_t*		row,	/*!< in: table row */
 	const row_ext_t*	ext,	/*!< in: cache of externally stored
 					column prefixes, or NULL */
 	doc_id_t*		doc_id)	/*!< in/out: Doc ID if we are
@@ -338,35 +338,13 @@ row_merge_buf_add(
 		const dict_col_t*	col;
 		ulint			col_no;
 		const dfield_t*		row_field;
-		ibool			col_adjusted;
 
 		col = ifield->col;
 		col_no = dict_col_get_no(col);
-		col_adjusted = FALSE;
-
-		/* If we are creating a FTS index, a new Doc
-		ID column is being added, so we need to adjust
-		any column number positioned after this Doc ID.
-		However, if we are rebuilding the table for adding
-		new primary, and if the old table already has
-		FTS_DOC_ID, such adjustment is not needed */
-		if (*doc_id > 0
-		    && DICT_TF2_FLAG_IS_SET(index->table,
-					    DICT_TF2_FTS_ADD_DOC_ID)
-		    && !DICT_TF2_FLAG_IS_SET(old_table,
-					     DICT_TF2_FTS_HAS_DOC_ID)
-		    && col_no > index->table->fts->doc_col) {
-
-			ut_ad(index->table->fts);
-
-			col_no--;
-			col_adjusted = TRUE;
-		}
 
 		/* Process the Doc ID column */
 		if (*doc_id > 0
-		    && col_no == index->table->fts->doc_col
-		    && !col_adjusted) {
+		    && col_no == index->table->fts->doc_col) {
 			fts_write_doc_id((byte*) &write_doc_id, *doc_id);
 
 			/* Note: field->data now points to a value on the
@@ -1235,7 +1213,7 @@ row_merge_skip_rec(
 Reads clustered index of the table and create temporary files
 containing the index entries for the indexes to be built.
 @return	DB_SUCCESS or error */
-static __attribute__((nonnull))
+static __attribute__((nonnull(1,2,3,4,6,9,10,13),warn_unused_result))
 dberr_t
 row_merge_read_clustered_index(
 /*===========================*/
@@ -1251,12 +1229,18 @@ row_merge_read_clustered_index(
 					online */
 	dict_index_t**		index,	/*!< in: indexes to be created */
 	dict_index_t*		fts_sort_idx,
-					/*!< in: indexes to be created */
-	fts_psort_t*		psort_info, /*!< in: parallel sort info */
+					/*!< in: full-text index to be created,
+					or NULL */
+	fts_psort_t*		psort_info,
+					/*!< in: parallel sort info for
+					fts_sort_idx creation, or NULL */
 	merge_file_t*		files,	/*!< in: temporary files */
 	const ulint*		key_numbers,
 					/*!< in: MySQL key numbers to create */
 	ulint			n_index,/*!< in: number of indexes to create */
+	const ulint*		col_map,/*!< in: mapping of old column
+					numbers to new ones, or NULL
+					if old_table == new_table */
 	row_merge_block_t*	block)	/*!< in/out: file buffer */
 {
 	dict_index_t*		clust_index;	/* Clustered index */
@@ -1270,9 +1254,6 @@ row_merge_read_clustered_index(
 	ulint			n_nonnull = 0;	/* number of columns
 						changed to NOT NULL */
 	ulint*			nonnull = NULL;	/* NOT NULL columns */
-	ulint			n_nullable = 0;	/* number of columns
-						changed to NULL */
-	ulint*			nullable = NULL;/* NULLable columns */
 	dict_index_t*		fts_index = NULL;/* FTS index */
 	doc_id_t		doc_id = 0;
 	doc_id_t		max_doc_id = 0;
@@ -1281,6 +1262,8 @@ row_merge_read_clustered_index(
 	ibool			fts_pll_sort = FALSE;
 	ib_int64_t		sig_count = 0;
 
+	ut_ad(old_table == new_table || col_map);
+
 	trx->op_info = "reading clustered index";
 
 #ifdef FTS_INTERNAL_DIAG_PRINT
@@ -1292,7 +1275,6 @@ row_merge_read_clustered_index(
 	merge_buf = static_cast<row_merge_buf_t**>(
 		mem_alloc(n_index * sizeof *merge_buf));
 
-
 	for (ulint i = 0; i < n_index; i++) {
 		if (index[i]->type & DICT_FTS) {
 
@@ -1335,44 +1317,38 @@ row_merge_read_clustered_index(
 	btr_pcur_open_at_index_side(
 		TRUE, clust_index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr);
 
-	if (UNIV_UNLIKELY(old_table != new_table)) {
-		ulint	n_cols = dict_table_get_n_cols(old_table);
-
-		/* A primary key will be created.  Identify the
-		columns that were flagged NOT NULL in the new table,
-		so that we can quickly check that the records in the
-		(old) clustered index do not violate the added NOT
-		NULL constraints. */
-
-		if (!fts_sort_idx) {
-			ut_a(n_cols == dict_table_get_n_cols(new_table));
-		}
+	if (old_table != new_table) {
+		/* The table is being rebuilt.  Identify the columns
+		that were flagged NOT NULL in the new table, so that
+		we can quickly check that the records in the old table
+		do not violate the added NOT NULL constraints. */
 
 		nonnull = static_cast<ulint*>(
-			mem_alloc(2 * n_cols * sizeof *nonnull));
-		nullable = nonnull + n_cols;
+			mem_alloc(dict_table_get_n_cols(new_table)
+				  * sizeof *nonnull));
+
+		for (ulint i = 0; i < dict_table_get_n_cols(old_table); i++) {
+			if (dict_table_get_nth_col(old_table, i)->prtype
+			    & DATA_NOT_NULL) {
+				continue;
+			}
 
-		for (ulint i = 0; i < n_cols; i++) {
-			/* TODO: adjust for ADD COLUMN, DROP COLUMN */
-			ulint	old_nonnull	= DATA_NOT_NULL
-				& dict_table_get_nth_col(old_table, i)->prtype;
-			ulint	new_nonnull	= DATA_NOT_NULL
-				& dict_table_get_nth_col(new_table, i)->prtype;
+			const ulint j = col_map[i];
 
-			if (old_nonnull == new_nonnull) {
+			if (j == ULINT_UNDEFINED) {
+				/* The column was dropped. */
 				continue;
 			}
 
-			if (old_nonnull) {
-				nullable[n_nullable++] = i;
-			} else {
-				nonnull[n_nonnull++] = i;
+			if (dict_table_get_nth_col(new_table, j)->prtype
+			    & DATA_NOT_NULL) {
+				nonnull[n_nonnull++] = j;
 			}
 		}
 
-		if (!n_nonnull && !n_nullable) {
+		if (!n_nonnull) {
 			mem_free(nonnull);
-			nonnull = nullable = NULL;
+			nonnull = NULL;
 		}
 	}
 
@@ -1535,31 +1511,20 @@ row_merge_read_clustered_index(
 		/* Build a row based on the clustered index. */
 
 		row = row_build(ROW_COPY_POINTERS, clust_index,
-				rec, offsets, new_table, &ext, row_heap);
+				rec, offsets, new_table,
+				col_map, &ext, row_heap);
 		ut_ad(row);
 
 		for (ulint i = 0; i < n_nonnull; i++) {
-			dfield_t*	field
-				= &row->fields[nonnull[i]];
-			dtype_t*	field_type
-				= dfield_get_type(field);
+			const dfield_t*	field	= &row->fields[nonnull[i]];
 
-			ut_ad(!(field_type->prtype & DATA_NOT_NULL));
+			ut_ad(dfield_get_type(field)->prtype & DATA_NOT_NULL);
 
 			if (dfield_is_null(field)) {
 				err = DB_INVALID_NULL;
 				trx->error_key_num = 0;
 				goto func_exit;
 			}
-
-			field_type->prtype |= DATA_NOT_NULL;
-		}
-
-		for (ulint i = 0; i < n_nullable; i++) {
-			dtype_t*	field_type
-				= dfield_get_type(&row->fields[nullable[i]]);
-			ut_ad((field_type->prtype & DATA_NOT_NULL));
-			field_type->prtype &= ~DATA_NOT_NULL;
 		}
 
 		/* Get the next Doc ID */
@@ -3231,9 +3196,12 @@ row_merge_build_indexes(
 	dict_index_t**	indexes,	/*!< in: indexes to be created */
 	const ulint*	key_numbers,	/*!< in: MySQL key numbers */
 	ulint		n_indexes,	/*!< in: size of indexes[] */
-	struct TABLE*	table)		/*!< in/out: MySQL table, for
+	struct TABLE*	table,		/*!< in/out: MySQL table, for
 					reporting erroneous key value
 					if applicable */
+	const ulint*	col_map)	/*!< in: mapping of old column
+					numbers to new ones, or NULL
+					if old_table == new_table */
 {
 	merge_file_t*		merge_files;
 	row_merge_block_t*	block;
@@ -3247,6 +3215,8 @@ row_merge_build_indexes(
 	fts_psort_t*		merge_info = NULL;
 	ib_int64_t		sig_count = 0;
 
+	ut_ad(old_table == new_table || col_map);
+
 	/* Allocate memory for merge file data structure and initialize
 	fields */
 
@@ -3294,7 +3264,7 @@ row_merge_build_indexes(
 	error = row_merge_read_clustered_index(
 		trx, table, old_table, new_table, online, indexes,
 		fts_sort_idx, psort_info, merge_files, key_numbers,
-		n_indexes, block);
+		n_indexes, col_map, block);
 
 	if (error != DB_SUCCESS) {
 

=== modified file 'storage/innobase/row/row0row.cc'
--- a/storage/innobase/row/row0row.cc	revid:marko.makela@stripped
+++ b/storage/innobase/row/row0row.cc	revid:marko.makela@stripped-20120604214744-29tpakp75k2gp2kp
@@ -217,6 +217,8 @@ row_build(
 					of an index, or NULL if
 					index->table should be
 					consulted instead */
+	const ulint*		col_map,/*!< in: mapping of old column
+					numbers to new ones, or NULL */
 	row_ext_t**		ext,	/*!< out, own: cache of
 					externally stored column
 					prefixes, or NULL */
@@ -225,14 +227,10 @@ row_build(
 {
 	const byte*		copy;
 	dtuple_t*		row;
-	const dict_table_t*	table;
-	ulint			n_fields;
 	ulint			n_ext_cols;
 	ulint*			ext_cols	= NULL; /* remove warning */
 	ulint			len;
-	ulint			row_len;
 	byte*			buf;
-	ulint			i;
 	ulint			j;
 	mem_heap_t*		tmp_heap	= NULL;
 	ulint			offsets_[REC_OFFS_NORMAL_SIZE];
@@ -241,6 +239,7 @@ row_build(
 	ut_ad(index && rec && heap);
 	ut_ad(dict_index_is_clust(index));
 	ut_ad(!mutex_own(&trx_sys->mutex));
+	ut_ad(!col_map || col_table);
 
 	if (!offsets) {
 		offsets = rec_get_offsets(rec, index, offsets_,
@@ -272,17 +271,6 @@ row_build(
 		copy = rec;
 	}
 
-	table = index->table;
-	row_len = dict_table_get_n_cols(table);
-
-	row = dtuple_create(heap, row_len);
-
-	dict_table_copy_types(row, table);
-
-	dtuple_set_info_bits(row, rec_get_info_bits(
-				     rec, dict_table_is_comp(table)));
-
-	n_fields = rec_offs_n_fields(offsets);
 	n_ext_cols = rec_offs_n_extern(offsets);
 	if (n_ext_cols) {
 		ext_cols = static_cast<ulint*>(
@@ -292,33 +280,62 @@ row_build(
 	/* Avoid a debug assertion in rec_offs_validate(). */
 	rec_offs_make_valid(copy, index, const_cast<ulint*>(offsets));
 
-	for (i = j = 0; i < n_fields; i++) {
-		dict_field_t*		ind_field
+	if (!col_table) {
+		ut_ad(!col_map);
+		col_table = index->table;
+	}
+
+	//row = dtuple_copy(heap, default_row);// todo: ADD_COLUMN
+	row = dtuple_create(heap, dict_table_get_n_cols(col_table));
+
+	dict_table_copy_types(row, col_table);
+	dtuple_set_info_bits(row, rec_get_info_bits(
+				     copy, rec_offs_comp(offsets)));
+
+	j = 0;
+
+	for (ulint i = 0; i < rec_offs_n_fields(offsets); i++) {
+		const dict_field_t*	ind_field
 			= dict_index_get_nth_field(index, i);
+
+		if (ind_field->prefix_len) {
+			/* Column prefixes can only occur in key
+			fields, which cannot be stored externally. For
+			a column prefix, there should also be the full
+			field in the clustered index tuple. The row
+			tuple comprises full fields, not prefixes. */
+			ut_ad(!rec_offs_nth_extern(offsets, i));
+			continue;
+		}
+
 		const dict_col_t*	col
 			= dict_field_get_col(ind_field);
 		ulint			col_no
 			= dict_col_get_no(col);
-		dfield_t*		dfield
-			= dtuple_get_nth_field(row, col_no);
-
-		if (ind_field->prefix_len == 0) {
 
-			const byte*	field = rec_get_nth_field(
-				copy, offsets, i, &len);
+		if (col_map) {
+			col_no = col_map[col_no];
 
-			dfield_set_data(dfield, field, len);
+			if (col_no == ULINT_UNDEFINED) {
+				/* dropped column */
+				continue;
+			}
+		} else if (col_no >= dict_table_get_n_cols(col_table)) {
+			/* dropped last columns */
+			continue;
 		}
 
+		dfield_t*	dfield = dtuple_get_nth_field(row, col_no);
+
+		const byte*	field = rec_get_nth_field(
+			copy, offsets, i, &len);
+
+		dfield_set_data(dfield, field, len);
+
 		if (rec_offs_nth_extern(offsets, i)) {
 			dfield_set_ext(dfield);
 
-			if (UNIV_LIKELY_NULL(col_table)) {
-				ut_a(col_no
-				     < dict_table_get_n_cols(col_table));
-				col = dict_table_get_nth_col(
-					col_table, col_no);
-			}
+			col = dict_table_get_nth_col(col_table, col_no);
 
 			if (col->ord_part) {
 				/* We will have to fetch prefixes of

=== modified file 'storage/innobase/row/row0undo.cc'
--- a/storage/innobase/row/row0undo.cc	revid:marko.makela@stripped05343-n95ug4qqgn787nea
+++ b/storage/innobase/row/row0undo.cc	revid:marko.makela@stripped75k2gp2kp
@@ -216,7 +216,8 @@ row_undo_search_clust_to_pcur(
 		}
 
 		node->row = row_build(ROW_COPY_DATA, clust_index, rec,
-				      offsets, NULL, ext, node->heap);
+				      offsets, NULL,
+				      NULL, ext, node->heap);
 		if (node->update) {
 			node->undo_row = dtuple_copy(node->row, node->heap);
 			row_upd_replace(node->undo_row, &node->undo_ext,

=== modified file 'storage/innobase/row/row0upd.cc'
--- a/storage/innobase/row/row0upd.cc	revid:marko.makela@strippednea
+++ b/storage/innobase/row/row0upd.cc	revid:marko.makela@stripped
@@ -1584,7 +1584,7 @@ row_upd_store_row(
 	}
 
 	node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
-			      NULL, ext, node->heap);
+			      NULL, NULL, ext, node->heap);
 	if (node->is_delete) {
 		node->upd_row = NULL;
 		node->upd_ext = NULL;

=== modified file 'storage/innobase/row/row0vers.cc'
--- a/storage/innobase/row/row0vers.cc	revid:marko.makela@strippedn787nea
+++ b/storage/innobase/row/row0vers.cc	revid:marko.makela@stripped
@@ -163,7 +163,8 @@ row_vers_impl_x_locked_low(
 		columns. */
 
 		row = row_build(ROW_COPY_POINTERS, clust_index, prev_version,
-				clust_offsets, NULL, &ext, heap);
+				clust_offsets,
+				NULL, NULL, &ext, heap);
 
 		entry = row_build_index_entry(row, ext, index, heap);
 
@@ -383,7 +384,8 @@ row_vers_old_has_index_entry(
 		Thus, it is safe to fetch the prefixes for
 		externally stored columns. */
 		row = row_build(ROW_COPY_POINTERS, clust_index,
-				rec, clust_offsets, NULL, &ext, heap);
+				rec, clust_offsets,
+				NULL, NULL, &ext, heap);
 		entry = row_build_index_entry(row, ext, index, heap);
 
 		/* If entry == NULL, the record contains unset BLOB
@@ -444,7 +446,7 @@ row_vers_old_has_index_entry(
 			externally stored columns. */
 			row = row_build(ROW_COPY_POINTERS, clust_index,
 					prev_version, clust_offsets,
-					NULL, &ext, heap);
+					NULL, NULL, &ext, heap);
 			entry = row_build_index_entry(row, ext, index, heap);
 
 			/* If entry == NULL, the record contains unset

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk-wl6255 branch (marko.makela:3934 to 3937) WL#6255marko.makela4 Jun