diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result
index 1e0b5deaf25d351a295706664b9b08c301acc19b..9b851d74a12f9e086cbf65b24df4292e40e30ddd 100644
--- a/mysql-test/r/type_timestamp.result
+++ b/mysql-test/r/type_timestamp.result
@@ -436,3 +436,50 @@ t1	CREATE TABLE "t1" (
 )
 set sql_mode='';
 drop table t1;
+create table t1 (a int auto_increment primary key, b int, c timestamp);
+insert into t1 (a, b, c) values (1, 0, '2001-01-01 01:01:01'),
+(2, 0, '2002-02-02 02:02:02'), (3, 0, '2003-03-03 03:03:03');
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	0	2002-02-02 02:02:02
+3	0	2003-03-03 03:03:03
+update t1 set b = 2, c = c where a = 2;
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	2	2002-02-02 02:02:02
+3	0	2003-03-03 03:03:03
+insert into t1 (a) values (4);
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	2	2002-02-02 02:02:02
+3	0	2003-03-03 03:03:03
+4	NULL	2001-09-09 04:46:59
+update t1 set c = '2004-04-04 04:04:04' where a = 4;
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	2	2002-02-02 02:02:02
+3	0	2003-03-03 03:03:03
+4	NULL	2004-04-04 04:04:04
+insert into t1 (a) values (3), (5) on duplicate key update b = 3, c = c;
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	2	2002-02-02 02:02:02
+3	3	2003-03-03 03:03:03
+4	NULL	2004-04-04 04:04:04
+5	NULL	2001-09-09 04:46:59
+insert into t1 (a, c) values (4, '2004-04-04 00:00:00'),
+(6, '2006-06-06 06:06:06') on duplicate key update b = 4;
+select * from t1;
+a	b	c
+1	0	2001-01-01 01:01:01
+2	2	2002-02-02 02:02:02
+3	3	2003-03-03 03:03:03
+4	4	2001-09-09 04:46:59
+5	NULL	2001-09-09 04:46:59
+6	NULL	2006-06-06 06:06:06
+drop table t1;
diff --git a/mysql-test/t/type_timestamp.test b/mysql-test/t/type_timestamp.test
index 62e7510405d9d9659108875232490816cdd8e081..01d18afc5af9d1ca99306f944fa14f19189c7b4c 100644
--- a/mysql-test/t/type_timestamp.test
+++ b/mysql-test/t/type_timestamp.test
@@ -300,3 +300,24 @@ show create table t1;
 # restore default mode
 set sql_mode='';
 drop table t1;
+
+#
+# Bug#7806 - insert on duplicate key and auto-update of timestamp
+#
+create table t1 (a int auto_increment primary key, b int, c timestamp);
+insert into t1 (a, b, c) values (1, 0, '2001-01-01 01:01:01'),
+  (2, 0, '2002-02-02 02:02:02'), (3, 0, '2003-03-03 03:03:03');
+select * from t1;
+update t1 set b = 2, c = c where a = 2;
+select * from t1;
+insert into t1 (a) values (4);
+select * from t1;
+update t1 set c = '2004-04-04 04:04:04' where a = 4;
+select * from t1;
+insert into t1 (a) values (3), (5) on duplicate key update b = 3, c = c;
+select * from t1;
+insert into t1 (a, c) values (4, '2004-04-04 00:00:00'),
+  (6, '2006-06-06 06:06:06') on duplicate key update b = 4;
+select * from t1;
+drop table t1;
+
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 3b65cc6b19513ead05d8e40f391f429df7106413..d12b8d8f2cc2645a604fa09ac1270981f5ea294a 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -43,15 +43,29 @@ static bool check_view_insertability(TABLE_LIST *view, query_id_t query_id);
 #define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
 #endif
 
+
 /*
   Check if insert fields are correct.
-  Sets table->timestamp_field_type to TIMESTAMP_NO_AUTO_SET or leaves it
-  as is, depending on if timestamp should be updated or not.
+
+  SYNOPSIS
+    check_insert_fields()
+    thd                         The current thread.
+    table                       The table for insert.
+    fields                      The insert fields.
+    values                      The insert values.
+
+  NOTE
+    Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
+    or leaves it as is, depending on if timestamp should be updated or
+    not.
+
+  RETURN
+    0           OK
+    -1          Error
 */
 
-static int
-check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
-		    List<Item> &values, ulong counter, bool check_unique)
+static int check_insert_fields(THD *thd, TABLE *table, List<Item> &fields,
+                               List<Item> &values)
 {
   TABLE *table= table_list->table;
 
@@ -87,7 +101,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
         return -1;
     }
 #endif
-    table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+    (int) table->timestamp_field_type&= ~ (int) TIMESTAMP_AUTO_SET_ON_INSERT;
   }
   else
   {						// Part field list
@@ -134,7 +148,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
     }
     if (table->timestamp_field &&	// Don't set timestamp if used
 	table->timestamp_field->query_id == thd->query_id)
-      table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+      (int) table->timestamp_field_type&= ~ (int) TIMESTAMP_AUTO_SET_ON_INSERT;
   }
   // For the values we need select_priv
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -153,6 +167,62 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
 }
 
 
+/*
+  Check update fields for the timestamp field.
+
+  SYNOPSIS
+    check_update_fields()
+    thd                         The current thread.
+    insert_table_list           The insert table list.
+    table                       The table for update.
+    update_fields               The update fields.
+
+  NOTE
+    If the update fields include the timestamp field,
+    remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
+
+  RETURN
+    0           OK
+    -1          Error
+*/
+
+static int check_update_fields(THD *thd, TABLE *table,
+                               TABLE_LIST *insert_table_list,
+                               List<Item> &update_fields)
+{
+  ulong		timestamp_query_id;
+  LINT_INIT(timestamp_query_id);
+
+  /*
+    Change the query_id for the timestamp column so that we can
+    check if this is modified directly.
+  */
+  if (table->timestamp_field)
+  {
+    timestamp_query_id= table->timestamp_field->query_id;
+    table->timestamp_field->query_id= thd->query_id-1;
+  }
+
+  /*
+    Check the fields we are going to modify. This will set the query_id
+    of all used fields to the threads query_id.
+  */
+  if (setup_fields(thd, 0, insert_table_list, update_fields, 1, 0, 0))
+    return -1;
+
+  if (table->timestamp_field)
+  {
+    /* Don't set timestamp column if this is modified. */
+    if (table->timestamp_field->query_id == thd->query_id)
+      (int) table->timestamp_field_type&= ~ (int) TIMESTAMP_AUTO_SET_ON_UPDATE;
+    else
+      table->timestamp_field->query_id= timestamp_query_id;
+  }
+
+  return 0;
+}
+
+
 bool mysql_insert(THD *thd,TABLE_LIST *table_list,
                   List<Item> &fields,
                   List<List_item> &values_list,
@@ -696,6 +766,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
                                      !insert_into_view)) ||
       (values && setup_fields(thd, 0, table_list, *values, 0, 0, 0)) ||
       (duplic == DUP_UPDATE &&
+       (check_update_fields(thd, table, insert_table_list, update_fields) ||
        ((thd->lex->select_lex.no_wrap_view_item= 1,
          (res= setup_fields(thd, 0, table_list, update_fields, 1, 0, 0)),
          thd->lex->select_lex.no_wrap_view_item= 0,
diff --git a/sql/table.h b/sql/table.h
index 270ec31324048f8ddcb5c8d8f740f1c8a93cb318..d00ac41140ee192c11bf62ecd97217e26749f19c 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -73,6 +73,10 @@ typedef struct st_filesort_info
 /*
   Values in this enum are used to indicate during which operations value
   of TIMESTAMP field should be set to current timestamp.
+  WARNING: The values are used for bit operations. If you change the enum,
+  you must keep the bitwise relation of the values. For example:
+  (int) TIMESTAMP_AUTO_SET_ON_BOTH ==
+    (int) TIMESTAMP_AUTO_SET_ON_INSERT | (int) TIMESTAMP_AUTO_SET_ON_UPDATE.
 */
 enum timestamp_auto_set_type
 {