From bb0507fa5d3e1161d418112f1c412fb9fa815571 Mon Sep 17 00:00:00 2001
From: unknown <jan@hundin.mysql.fi>
Date: Thu, 3 Feb 2005 10:54:38 +0200
Subject: [PATCH] Fixed a bug: deadlock without any locking, simple select and
 update (Bug #7975).

innobase/row/row0ins.c:
  If the SQL-query will update or replace duplicate key row we take X-lcok
  for duplicate row.
sql/ha_innodb.cc:
  INSERT ON DUPLICATE KEY UPDATE will also update duplicate key and we can
  take X-lock in this case for duplicate key records.
BitKeeper/etc/ignore:
  Added innobase/row/row0index.c to the ignore list
---
 .bzrignore             |  1 +
 innobase/row/row0ins.c | 41 +++++++++++++++++++++++------------------
 sql/ha_innodb.cc       | 19 +++++++++++++++----
 3 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/.bzrignore b/.bzrignore
index bb1cdb1c0c..3aab3a7b4d 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1055,3 +1055,4 @@ vio/viotest-ssl
 include/mysqld_ername.h
 include/mysqld_error.h
 include/sql_state.h
+innobase/row/row0index.c
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index aab0f5affa..9edc18025d 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -51,14 +51,19 @@ innobase_invalidate_query_cache(
 				chars count */
 
 /**********************************************************************
-This function returns true if SQL-query in the current thread
+This function returns true if 
+
+1) SQL-query in the current thread
 is either REPLACE or LOAD DATA INFILE REPLACE. 
+
+2) SQL-query in the current thread
+is INSERT ON DUPLICATE KEY UPDATE.
+
 NOTE that /mysql/innobase/row/row0ins.c must contain the 
 prototype for this function ! */
 
 ibool
-innobase_query_is_replace(void);
-/*===========================*/
+innobase_query_is_update(void);
 
 /*************************************************************************
 Creates an insert node struct. */
@@ -1597,12 +1602,12 @@ row_ins_scan_sec_index_for_duplicate(
 		offsets = rec_get_offsets(rec, index, offsets,
 					ULINT_UNDEFINED, &heap);
 
-		if (innobase_query_is_replace()) {
+		if (innobase_query_is_update()) {
 
-			/* The manual defines the REPLACE semantics that it 
-			is either an INSERT or DELETE(s) for duplicate key
-			+ INSERT. Therefore, we should take X-lock for 
-			duplicates */
+			/* If the SQL-query will update or replace
+			duplicate key we will take X-lock for 
+			duplicates ( REPLACE, LOAD DATAFILE REPLACE, 
+			INSERT ON DUPLICATE KEY UPDATE). */
 			
 			err = row_ins_set_exclusive_rec_lock(LOCK_ORDINARY,
 						rec, index, offsets, thr);
@@ -1720,12 +1725,12 @@ row_ins_duplicate_error_in_clust(
 			sure that in roll-forward we get the same duplicate
 			errors as in original execution */
 
-			if (innobase_query_is_replace()) {
+			if (innobase_query_is_update()) {
 
-				/* The manual defines the REPLACE semantics 
-				that it is either an INSERT or DELETE(s) 
-				for duplicate key + INSERT. Therefore, we 
-				should take X-lock for duplicates */
+				/* If the SQL-query will update or replace
+				duplicate key we will take X-lock for 
+				duplicates ( REPLACE, LOAD DATAFILE REPLACE, 
+				INSERT ON DUPLICATE KEY UPDATE). */
 				
 				err = row_ins_set_exclusive_rec_lock(
 					LOCK_REC_NOT_GAP,rec,cursor->index,
@@ -1759,12 +1764,12 @@ row_ins_duplicate_error_in_clust(
 			offsets = rec_get_offsets(rec, cursor->index, offsets,
 						ULINT_UNDEFINED, &heap);
 
-			/* The manual defines the REPLACE semantics that it 
-			is either an INSERT or DELETE(s) for duplicate key
-			+ INSERT. Therefore, we should take X-lock for
-			duplicates. */
+			if (innobase_query_is_update()) {
 
-			if (innobase_query_is_replace()) {
+				/* If the SQL-query will update or replace
+				duplicate key we will take X-lock for 
+				duplicates ( REPLACE, LOAD DATAFILE REPLACE, 
+				INSERT ON DUPLICATE KEY UPDATE). */
 
 				err = row_ins_set_exclusive_rec_lock(
 						LOCK_REC_NOT_GAP, rec,
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 797f51c029..04ae021197 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -6142,13 +6142,19 @@ innobase_get_at_most_n_mbchars(
 
 extern "C" {
 /**********************************************************************
-This function returns true if SQL-query in the current thread
+This function returns true if 
+
+1) SQL-query in the current thread
 is either REPLACE or LOAD DATA INFILE REPLACE. 
+
+2) SQL-query in the current thread
+is INSERT ON DUPLICATE KEY UPDATE.
+
 NOTE that /mysql/innobase/row/row0ins.c must contain the 
 prototype for this function ! */
 
 ibool
-innobase_query_is_replace(void)
+innobase_query_is_update(void)
 /*===========================*/
 {
 	THD*	thd;
@@ -6160,9 +6166,14 @@ innobase_query_is_replace(void)
 	     ( thd->lex->sql_command == SQLCOM_LOAD &&
 	       thd->lex->duplicates == DUP_REPLACE )) {
 		return true;
-	} else {
-		return false;
 	}
+
+	if ( thd->lex->sql_command == SQLCOM_INSERT &&
+	     thd->lex->duplicates  == DUP_UPDATE ) {
+		return true;
+	}
+
+	return false;
 }
 }
 
-- 
2.30.9