diff --git a/innobase/include/lock0lock.h b/innobase/include/lock0lock.h
index f8435e14d97666143c58790a62e67dd4247ac859..ff5b6aae02e947d5bd007a2e9bc2d3bbaf255008 100644
--- a/innobase/include/lock0lock.h
+++ b/innobase/include/lock0lock.h
@@ -463,13 +463,32 @@ lock_rec_hash(
 	ulint	space,	/* in: space */
 	ulint	page_no);/* in: page number */
 /*************************************************************************
-Gets the table covered by an IX table lock. */
+Gets the source table of an ALTER TABLE transaction.  The table must be
+covered by an IX or IS table lock. */
 
 dict_table_t*
-lock_get_ix_table(
-/*==============*/
-			/* out: the table covered by the lock */
-	lock_t*	lock);	/* in: table lock */
+lock_get_src_table(
+/*===============*/
+				/* out: the source table of transaction,
+				if it is covered by an IX or IS table lock;
+				dest if there is no source table, and
+				NULL if the transaction is locking more than
+				two tables or an inconsistency is found */
+	trx_t*		trx,	/* in: transaction */
+	dict_table_t*	dest,	/* in: destination of ALTER TABLE */
+	ulint*		mode);	/* out: lock mode of the source table */
+/*************************************************************************
+Determine if the given table is exclusively "owned" by the given
+transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC
+on the table. */
+
+ibool
+lock_table_exclusive(
+/*=================*/
+				/* out: TRUE if table is only locked by trx,
+				with LOCK_IX, and possibly LOCK_AUTO_INC */
+	dict_table_t*	table,	/* in: table */
+	trx_t*		trx);	/* in: transaction */
 /*************************************************************************
 Checks that a transaction id is sensible, i.e., not in the future. */
 
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 73f41dea0dafc723315421676ce553756669066a..f47ce74ce3791106ce9c6ee3f42ec1abe2d5ca69 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -177,10 +177,12 @@ row_lock_table_for_mysql(
 					/* out: error code or DB_SUCCESS */
 	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct in the MySQL
 					table handle */
-	dict_table_t*	table);		/* in: table to LOCK_IX, or NULL
+	dict_table_t*	table,		/* in: table to lock, or NULL
 					if prebuilt->table should be
 					locked as LOCK_TABLE_EXP |
 					prebuilt->select_lock_type */
+	ulint		mode);		/* in: lock mode of table */
+					   
 /*************************************************************************
 Does an insert for MySQL. */
 
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index 78a78c9dd95d5968cad788f734f03f707e4345f3..16c4ddfd96d507247e8d49a3035cc1365545c906 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -395,19 +395,6 @@ lock_rec_get_nth_bit(
 	return(ut_bit_get_nth(b, bit_index));
 }	
 
-/*************************************************************************
-Gets the table covered by an IX table lock. */
-
-dict_table_t*
-lock_get_ix_table(
-/*==============*/
-			/* out: the table covered by the lock */
-	lock_t*	lock)	/* in: table lock */
-{
-	ut_a(lock->type_mode == (LOCK_TABLE | LOCK_IX));
-	return(lock->un_member.tab_lock.table);
-}
-
 /*************************************************************************/
 
 #define lock_mutex_enter_kernel()	mutex_enter(&kernel_mutex)
@@ -614,6 +601,128 @@ lock_get_wait(
 	return(FALSE);
 }
 
+/*************************************************************************
+Gets the source table of an ALTER TABLE transaction.  The table must be
+covered by an IX or IS table lock. */
+
+dict_table_t*
+lock_get_src_table(
+/*===============*/
+				/* out: the source table of transaction,
+				if it is covered by an IX or IS table lock;
+				dest if there is no source table, and
+				NULL if the transaction is locking more than
+				two tables or an inconsistency is found */
+	trx_t*		trx,	/* in: transaction */
+	dict_table_t*	dest,	/* in: destination of ALTER TABLE */
+	ulint*		mode)	/* out: lock mode of the source table */
+{
+	dict_table_t*	src;
+	lock_t*		lock;
+
+	src = NULL;
+	*mode = LOCK_NONE;
+
+	for (lock = UT_LIST_GET_FIRST(trx->trx_locks);
+	     lock;
+	     lock = UT_LIST_GET_NEXT(trx_locks, lock)) {
+		lock_table_t*	tab_lock;
+		ulint		lock_mode;
+		if (!(lock_get_type(lock) & LOCK_TABLE)) {
+			/* We are only interested in table locks. */
+			continue;
+		}
+		tab_lock = &lock->un_member.tab_lock;
+		if (dest == tab_lock->table) {
+			/* We are not interested in the destination table. */
+			continue;
+		} else if (!src) {
+			/* This presumably is the source table. */
+			src = tab_lock->table;
+			if (UT_LIST_GET_LEN(src->locks) != 1 ||
+			    UT_LIST_GET_FIRST(src->locks) != lock) {
+				/* We only support the case when
+				there is only one lock on this table. */
+				return(NULL);
+			}
+		} else if (src != tab_lock->table) {
+			/* The transaction is locking more than
+			two tables (src and dest): abort */
+			return(NULL);
+		}
+
+		/* Check that the source table is locked by
+		LOCK_IX or LOCK_IS. */
+		lock_mode = lock_get_mode(lock);
+		switch (lock_mode) {
+		case LOCK_IX:
+		case LOCK_IS:
+			if (*mode != LOCK_NONE && *mode != lock_mode) {
+				/* There are multiple locks on src. */
+				return(NULL);
+			}
+			*mode = lock_mode;
+			break;
+		}
+	}
+
+	if (!src) {
+		/* No source table lock found: flag the situation to caller */
+		src = dest;
+	}
+
+	return(src);
+}
+
+/*************************************************************************
+Determine if the given table is exclusively "owned" by the given
+transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC
+on the table. */
+
+ibool
+lock_is_table_exclusive(
+/*====================*/
+				/* out: TRUE if table is only locked by trx,
+				with LOCK_IX, and possibly LOCK_AUTO_INC */
+	dict_table_t*	table,	/* in: table */
+	trx_t*		trx)	/* in: transaction */
+{
+	lock_t*	lock;
+	bool	ok	= FALSE;
+
+	ut_ad(table && trx);
+
+	for (lock = UT_LIST_GET_FIRST(table->locks);
+	     lock;
+	     lock = UT_LIST_GET_NEXT(locks, &lock->un_member.tab_lock)) {
+		if (lock->trx != trx) {
+			/* A lock on the table is held
+			by some other transaction. */
+			return(FALSE);
+		}
+
+		if (!(lock_get_type(lock) & LOCK_TABLE)) {
+			/* We are interested in table locks only. */
+			continue;
+		}
+
+		switch (lock_get_mode(lock)) {
+		case LOCK_IX:
+			ok = TRUE;
+			break;
+		case LOCK_AUTO_INC:
+			/* It is allowed for trx to hold an
+			auto_increment lock. */
+			break;
+		default:
+			/* Other table locks than LOCK_IX are not allowed. */
+			return(FALSE);
+		}
+	}
+
+	return(ok);
+}
+
 /*************************************************************************
 Sets the wait flag of a lock and the back pointer in trx to lock. */
 UNIV_INLINE
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 9613da2e286460d19133bdd0091f0305b24e07d0..dec2b19559c205b5f809cd9a7d3b9f76950615a3 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -782,10 +782,11 @@ row_lock_table_for_mysql(
 					/* out: error code or DB_SUCCESS */
 	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct in the MySQL
 					table handle */
-	dict_table_t*	table)		/* in: table to LOCK_IX, or NULL
+	dict_table_t*	table,		/* in: table to lock, or NULL
 					if prebuilt->table should be
 					locked as LOCK_TABLE_EXP |
 					prebuilt->select_lock_type */
+	ulint		mode)		/* in: lock mode of table */
 {
 	trx_t*		trx 		= prebuilt->trx;
 	que_thr_t*	thr;
@@ -819,7 +820,7 @@ run_again:
 	trx_start_if_not_started(trx);
 
 	if (table) {
-		err = lock_table(0, table, LOCK_IX, thr);
+		err = lock_table(0, table, mode, thr);
 	} else {
 		err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
 			prebuilt->select_lock_type, thr);
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 2515b4956d07bc64b605f87259667e357475020a..cc69762cbdbc10a584049a021a1af6014c6da052 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -2324,20 +2324,58 @@ ha_innobase::write_row(
 		position in the source table need not be adjusted after the
 		intermediate COMMIT, since writes by other transactions are
 		being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
-		ut_a(prebuilt->trx->mysql_n_tables_locked == 2);
-		ut_a(UT_LIST_GET_LEN(prebuilt->trx->trx_locks) >= 2);
-		dict_table_t* table = lock_get_ix_table(
-				UT_LIST_GET_FIRST(prebuilt->trx->trx_locks));
+
+		dict_table_t*	src_table;
+		ibool		mode;
+
 		num_write_row = 0;
+
 		/* Commit the transaction.  This will release the table
 		locks, so they have to be acquired again. */
-		innobase_commit(user_thd, prebuilt->trx);
-		/* Note that this transaction is still active. */
-		user_thd->transaction.all.innodb_active_trans = 1;
-		/* Re-acquire the IX table lock on the source table. */
-		row_lock_table_for_mysql(prebuilt, table);
-		/* We will need an IX lock on the destination table. */
-	        prebuilt->sql_stat_start = TRUE;
+
+		/* Altering an InnoDB table */
+		/* Get the source table. */
+		src_table = lock_get_src_table(
+				prebuilt->trx, prebuilt->table, &mode);
+		if (!src_table) {
+		no_commit:
+			/* Unknown situation: do not commit */
+			/*
+			ut_print_timestamp(stderr);
+			fprintf(stderr,
+				"  InnoDB error: ALTER TABLE is holding lock"
+				" on %lu tables!\n",
+				prebuilt->trx->mysql_n_tables_locked);
+			*/
+			;
+		} else if (src_table == prebuilt->table) {
+			/* Source table is not in InnoDB format:
+			no need to re-acquire locks on it. */
+
+			/* Altering to InnoDB format */
+			innobase_commit(user_thd, prebuilt->trx);
+			/* Note that this transaction is still active. */
+			user_thd->transaction.all.innodb_active_trans = 1;
+			/* We will need an IX lock on the destination table. */
+		        prebuilt->sql_stat_start = TRUE;
+		} else {
+			/* Ensure that there are no other table locks than
+			LOCK_IX and LOCK_AUTO_INC on the destination table. */
+			if (!lock_is_table_exclusive(prebuilt->table,
+							prebuilt->trx)) {
+				goto no_commit;
+			}
+
+			/* Commit the transaction.  This will release the table
+			locks, so they have to be acquired again. */
+			innobase_commit(user_thd, prebuilt->trx);
+			/* Note that this transaction is still active. */
+			user_thd->transaction.all.innodb_active_trans = 1;
+			/* Re-acquire the table lock on the source table. */
+			row_lock_table_for_mysql(prebuilt, src_table, mode);
+			/* We will need an IX lock on the destination table. */
+		        prebuilt->sql_stat_start = TRUE;
+		}
 	}
 
 	num_write_row++;
@@ -5015,7 +5053,8 @@ ha_innobase::external_lock(
 			if (thd->in_lock_tables &&
 			    thd->variables.innodb_table_locks) {
 				ulint	error;
-				error = row_lock_table_for_mysql(prebuilt, 0);
+				error = row_lock_table_for_mysql(prebuilt,
+							NULL, LOCK_TABLE_EXP);
 
 				if (error != DB_SUCCESS) {
 					error = convert_error_code_to_mysql(