From 15eaeace394320d96b6bf193f9e049f9358d2b07 Mon Sep 17 00:00:00 2001
From: Sergey Vojtovich <svoj@mariadb.org>
Date: Wed, 12 Dec 2018 19:58:20 +0400
Subject: [PATCH 01/10] MDEV-16987 - ALTER DATABASE possible in read-only mode

Forbid ALTER DATABASE under read_only.
---
 mysql-test/r/read_only.result | 11 +++++++++++
 mysql-test/t/read_only.test   | 13 +++++++++++++
 sql/sql_parse.cc              |  1 +
 3 files changed, 25 insertions(+)

diff --git a/mysql-test/r/read_only.result b/mysql-test/r/read_only.result
index 2d0f9d730fd..4afddab6851 100644
--- a/mysql-test/r/read_only.result
+++ b/mysql-test/r/read_only.result
@@ -162,3 +162,14 @@ delete from mysql.columns_priv where User like 'mysqltest_%';
 flush privileges;
 drop database mysqltest_db1;
 set global read_only= @start_read_only;
+#
+# MDEV-16987 - ALTER DATABASE possible in read-only mode
+#
+GRANT ALTER ON test1.* TO user1@localhost;
+CREATE DATABASE test1;
+SET GLOBAL read_only=1;
+ALTER DATABASE test1 CHARACTER SET utf8;
+ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement
+SET GLOBAL read_only=0;
+DROP DATABASE test1;
+DROP USER user1@localhost;
diff --git a/mysql-test/t/read_only.test b/mysql-test/t/read_only.test
index eb9bea803c2..dd9d6430172 100644
--- a/mysql-test/t/read_only.test
+++ b/mysql-test/t/read_only.test
@@ -310,3 +310,16 @@ set global read_only= @start_read_only;
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc
 
+--echo #
+--echo # MDEV-16987 - ALTER DATABASE possible in read-only mode
+--echo #
+GRANT ALTER ON test1.* TO user1@localhost;
+CREATE DATABASE test1;
+SET GLOBAL read_only=1;
+change_user user1;
+--error ER_OPTION_PREVENTS_STATEMENT
+ALTER DATABASE test1 CHARACTER SET utf8;
+change_user root;
+SET GLOBAL read_only=0;
+DROP DATABASE test1;
+DROP USER user1@localhost;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 9f8a625325f..95243ead2fe 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -831,6 +831,7 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
     DBUG_RETURN(FALSE);
 
   if (lex->sql_command == SQLCOM_CREATE_DB ||
+      lex->sql_command == SQLCOM_ALTER_DB ||
       lex->sql_command == SQLCOM_DROP_DB)
     DBUG_RETURN(TRUE);
 

From 1a7158b88a117f6543e1318a7abdc490cdfd5e67 Mon Sep 17 00:00:00 2001
From: Sergei Golubchik <serg@mariadb.org>
Date: Thu, 13 Dec 2018 19:51:40 +0100
Subject: [PATCH 02/10] remove unsed variable

---
 sql/opt_range.cc | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 005ae92a665..b1f8366d83b 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3322,10 +3322,6 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param,
 {
   Field **field_ptr;
   TABLE *table= param->table;
-  partition_info *part_info= NULL;
-  #ifdef WITH_PARTITION_STORAGE_ENGINE
-    part_info= table->part_info;
-  #endif
   uint parts= 0;
 
   for (field_ptr= table->field; *field_ptr; field_ptr++)

From d1f399408d245dd8b971ba331eaaedf488e083b6 Mon Sep 17 00:00:00 2001
From: Varun Gupta <varunraiko1803@gmail.com>
Date: Sun, 16 Dec 2018 21:50:49 +0200
Subject: [PATCH 03/10] MDEV-6453: Assertion `inited==NONE || (inited==RND &&
 scan)' failed in handler::ha_rnd_init(bool) with InnoDB, joins, AND/OR
 conditions

The inited parameter handler is not initialised when we do a quick_select after a table scan.
---
 mysql-test/r/range_innodb.result | 18 ++++++++++++++++++
 mysql-test/t/range_innodb.test   | 17 +++++++++++++++++
 sql/sql_select.cc                |  4 ++++
 3 files changed, 39 insertions(+)

diff --git a/mysql-test/r/range_innodb.result b/mysql-test/r/range_innodb.result
index 794e6c7b3cc..8bb1c833a56 100644
--- a/mysql-test/r/range_innodb.result
+++ b/mysql-test/r/range_innodb.result
@@ -37,3 +37,21 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t0	ALL	NULL	NULL	NULL	NULL	10	
 1	SIMPLE	t2	range	a,b	b	5	NULL	201	Using where; Using join buffer (flat, BNL join)
 drop table t0,t1,t2;
+CREATE TABLE t1 (
+pk INT PRIMARY KEY, f1 INT, f2 CHAR(1), f3 CHAR(1),
+KEY(f1), KEY(f2)
+) ENGINE=InnoDB;
+INSERT INTO t1 VALUES
+(1,4,'v',NULL),(2,6,'v',NULL),(3,7,'c',NULL),(4,1,'e',NULL),(5,0,'x',NULL),
+(6,7,'i',NULL),(7,7,'e',NULL),(8,1,'p',NULL),(9,7,'s',NULL),(10,1,'j',NULL),
+(11,5,'z',NULL),(12,2,'c',NULL),(13,0,'a',NULL),(14,1,'q',NULL),(15,8,'y',NULL),
+(16,1,'m',NULL),(17,1,'r',NULL),(18,9,'v',NULL),(19,1,'n',NULL);
+CREATE TABLE t2 (f4 INT, f5 CHAR(1)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (4,'q'),(NULL,'j');
+SELECT * FROM t1 AS t1_1, t1 AS t1_2, t2
+WHERE f5 = t1_2.f2 AND ( t1_1.f1 = 103 AND t1_1.f2 = 'o' OR t1_1.pk < f4 );
+pk	f1	f2	f3	pk	f1	f2	f3	f4	f5
+1	4	v	NULL	14	1	q	NULL	4	q
+2	6	v	NULL	14	1	q	NULL	4	q
+3	7	c	NULL	14	1	q	NULL	4	q
+drop table t1,t2;
diff --git a/mysql-test/t/range_innodb.test b/mysql-test/t/range_innodb.test
index f76794814ef..605006587cc 100644
--- a/mysql-test/t/range_innodb.test
+++ b/mysql-test/t/range_innodb.test
@@ -45,3 +45,20 @@ explain select * from t0 left join t2 on t2.a <t0.a and t2.b between 50 and 250;
 
 drop table t0,t1,t2;
 
+CREATE TABLE t1 (
+  pk INT PRIMARY KEY, f1 INT, f2 CHAR(1), f3 CHAR(1),
+  KEY(f1), KEY(f2)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES
+(1,4,'v',NULL),(2,6,'v',NULL),(3,7,'c',NULL),(4,1,'e',NULL),(5,0,'x',NULL),
+(6,7,'i',NULL),(7,7,'e',NULL),(8,1,'p',NULL),(9,7,'s',NULL),(10,1,'j',NULL),
+(11,5,'z',NULL),(12,2,'c',NULL),(13,0,'a',NULL),(14,1,'q',NULL),(15,8,'y',NULL),
+(16,1,'m',NULL),(17,1,'r',NULL),(18,9,'v',NULL),(19,1,'n',NULL);
+
+CREATE TABLE t2 (f4 INT, f5 CHAR(1)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (4,'q'),(NULL,'j');
+
+SELECT * FROM t1 AS t1_1, t1 AS t1_2, t2
+WHERE f5 = t1_2.f2 AND ( t1_1.f1 = 103 AND t1_1.f2 = 'o' OR t1_1.pk < f4 );
+drop table t1,t2;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index ca9a6a46fda..6fafbbb11df 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -18883,6 +18883,10 @@ test_if_quick_select(JOIN_TAB *tab)
 
   delete tab->select->quick;
   tab->select->quick=0;
+
+  if (tab->table->file->inited != handler::NONE)
+    tab->table->file->ha_index_or_rnd_end();
+
   return tab->select->test_quick_select(tab->join->thd, tab->keys,
 					(table_map) 0, HA_POS_ERROR, 0,
                                         FALSE);

From 65525550ab8988a1a1a36d0403824ebaec160347 Mon Sep 17 00:00:00 2001
From: Sergei Golubchik <serg@mariadb.org>
Date: Mon, 17 Dec 2018 16:09:28 +0100
Subject: [PATCH 04/10] Don't default to bundled zlib

This reverts part of c54271723c6
---
 cmake/zlib.cmake | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/cmake/zlib.cmake b/cmake/zlib.cmake
index 840337d1ca3..bfb2b70be74 100644
--- a/cmake/zlib.cmake
+++ b/cmake/zlib.cmake
@@ -34,11 +34,6 @@ ENDMACRO()
 
 MACRO (MYSQL_CHECK_ZLIB_WITH_COMPRESS)
 
-    # For NDBCLUSTER: Use bundled zlib by default
-    IF (NOT WITH_ZLIB)
-      SET(WITH_ZLIB "bundled"  CACHE STRING "By default use bundled zlib on this platform")
-    ENDIF()
-  
   IF(WITH_ZLIB STREQUAL "bundled")
     MYSQL_USE_BUNDLED_ZLIB()
   ELSE()

From b0fd06a6f27212cee770961171439a44626d8f14 Mon Sep 17 00:00:00 2001
From: Sergey Vojtovich <svoj@mariadb.org>
Date: Tue, 18 Dec 2018 17:07:29 +0400
Subject: [PATCH 05/10] MDEV-15670 - unit.my_atomic failed in buildbot with
 Signal 11 thrown

Workaround glibc bug: https://sourceware.org/bugzilla/show_bug.cgi?id=20116
by making unittest threads joinable. It makes code better anyway.
---
 unittest/mysys/lf-t.c              |  5 -----
 unittest/mysys/my_atomic-t.c       | 17 +---------------
 unittest/mysys/thr_template.c      | 31 +++++++++---------------------
 unittest/mysys/waiting_threads-t.c |  4 +---
 4 files changed, 11 insertions(+), 46 deletions(-)

diff --git a/unittest/mysys/lf-t.c b/unittest/mysys/lf-t.c
index 573a56cc1d6..c1c89f60864 100644
--- a/unittest/mysys/lf-t.c
+++ b/unittest/mysys/lf-t.c
@@ -48,9 +48,6 @@ pthread_handler_t test_lf_pinbox(void *arg)
     pins= lf_pinbox_get_pins(&lf_allocator.pinbox);
   }
   lf_pinbox_put_pins(pins);
-  pthread_mutex_lock(&mutex);
-  if (!--running_threads) pthread_cond_signal(&cond);
-  pthread_mutex_unlock(&mutex);
 
   if (with_my_thread_init)
     my_thread_end();
@@ -105,7 +102,6 @@ pthread_handler_t test_lf_alloc(void *arg)
     bad|= lf_allocator.mallocs - lf_alloc_pool_count(&lf_allocator);
 #endif
   }
-  if (!--running_threads) pthread_cond_signal(&cond);
   pthread_mutex_unlock(&mutex);
 
   if (with_my_thread_init)
@@ -159,7 +155,6 @@ pthread_handler_t test_lf_hash(void *arg)
          lf_hash.size, inserts);
     bad|= lf_hash.count;
   }
-  if (!--running_threads) pthread_cond_signal(&cond);
   pthread_mutex_unlock(&mutex);
   if (with_my_thread_init)
     my_thread_end();
diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c
index 35e782eb360..5eb988e2e15 100644
--- a/unittest/mysys/my_atomic-t.c
+++ b/unittest/mysys/my_atomic-t.c
@@ -35,9 +35,6 @@ pthread_handler_t test_atomic_add(void *arg)
     my_atomic_add32(&bad, -x);
     my_atomic_rwlock_wrunlock(&rwl);
   }
-  pthread_mutex_lock(&mutex);
-  if (!--running_threads) pthread_cond_signal(&cond);
-  pthread_mutex_unlock(&mutex);
   return 0;
 }
 
@@ -58,13 +55,6 @@ pthread_handler_t test_atomic_add64(void *arg)
     my_atomic_add64(&a64, -x);
     my_atomic_rwlock_wrunlock(&rwl);
   }
-  pthread_mutex_lock(&mutex);
-  if (!--running_threads)
-  {
-    bad= (a64 != 0);
-    pthread_cond_signal(&cond);
-  }
-  pthread_mutex_unlock(&mutex);
   return 0;
 }
 
@@ -108,9 +98,6 @@ pthread_handler_t test_atomic_fas(void *arg)
   my_atomic_add32(&bad, -x);
   my_atomic_rwlock_wrunlock(&rwl);
 
-  pthread_mutex_lock(&mutex);
-  if (!--running_threads) pthread_cond_signal(&cond);
-  pthread_mutex_unlock(&mutex);
   return 0;
 }
 
@@ -140,9 +127,6 @@ pthread_handler_t test_atomic_cas(void *arg)
       my_atomic_rwlock_wrunlock(&rwl);
     } while (!ok) ;
   }
-  pthread_mutex_lock(&mutex);
-  if (!--running_threads) pthread_cond_signal(&cond);
-  pthread_mutex_unlock(&mutex);
   return 0;
 }
 
@@ -178,6 +162,7 @@ void do_tests()
   }
   a64=0;
   test_concurrently("my_atomic_add64", test_atomic_add64, THREADS, CYCLES);
+  bad= (a64 != 0);
 
   my_atomic_rwlock_destroy(&rwl);
 }
diff --git a/unittest/mysys/thr_template.c b/unittest/mysys/thr_template.c
index 7304eb50955..d1bc0868ca0 100644
--- a/unittest/mysys/thr_template.c
+++ b/unittest/mysys/thr_template.c
@@ -20,35 +20,34 @@
 #include <tap.h>
 
 volatile uint32 bad;
-pthread_attr_t thr_attr;
 pthread_mutex_t mutex;
-pthread_cond_t cond;
-uint running_threads;
 
 void do_tests();
 
 void test_concurrently(const char *test, pthread_handler handler, int n, int m)
 {
-  pthread_t t;
+  pthread_t *threads= malloc(n * sizeof(pthread_t));
+  int i;
   ulonglong now= my_interval_timer();
 
+  assert(threads);
   bad= 0;
 
   diag("Testing %s with %d threads, %d iterations... ", test, n, m);
-  for (running_threads= n ; n ; n--)
+  for (i= n; i; i--)
   {
-    if (pthread_create(&t, &thr_attr, handler, &m) != 0)
+    if (pthread_create(&threads[i], 0, handler, &m) != 0)
     {
       diag("Could not create thread");
       abort();
     }
   }
-  pthread_mutex_lock(&mutex);
-  while (running_threads)
-    pthread_cond_wait(&cond, &mutex);
-  pthread_mutex_unlock(&mutex);
+
+  for (i= n; i; i--)
+    pthread_join(threads[i], 0);
 
   now= my_interval_timer() - now;
+  free(threads);
   ok(!bad, "tested %s in %g secs (%d)", test, ((double)now)/1e9, bad);
 }
 
@@ -60,9 +59,6 @@ int main(int argc __attribute__((unused)), char **argv)
     DBUG_SET_INITIAL(argv[1]);
 
   pthread_mutex_init(&mutex, 0);
-  pthread_cond_init(&cond, 0);
-  pthread_attr_init(&thr_attr);
-  pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
 
 #ifdef MY_ATOMIC_MODE_RWLOCKS
 #if defined(HPUX11) || defined(__POWERPC__) /* showed to be very slow (scheduler-related) */
@@ -79,16 +75,7 @@ int main(int argc __attribute__((unused)), char **argv)
 
   do_tests();
 
-  /*
-    workaround until we know why it crashes randomly on some machine
-    (BUG#22320).
-  */
-#ifdef NOT_USED
-  sleep(2);
-#endif
   pthread_mutex_destroy(&mutex);
-  pthread_cond_destroy(&cond);
-  pthread_attr_destroy(&thr_attr);
   my_end(0);
   return exit_status();
 }
diff --git a/unittest/mysys/waiting_threads-t.c b/unittest/mysys/waiting_threads-t.c
index 35e86aca319..eca6ba408c3 100644
--- a/unittest/mysys/waiting_threads-t.c
+++ b/unittest/mysys/waiting_threads-t.c
@@ -136,10 +136,8 @@ retry:
   pthread_mutex_unlock(&lock);
   pthread_mutex_unlock(& thds[id].lock);
   wt_thd_destroy(& thds[id].thd);
-
-  if (!--running_threads) /* now, signal when everybody is done with deinit */
-    pthread_cond_signal(&cond);
   pthread_mutex_unlock(&mutex);
+
   DBUG_PRINT("wt", ("exiting"));
   my_thread_end();
   return 0;

From da4efd56aa9bc3c39d94a73eb216ca7f559ce734 Mon Sep 17 00:00:00 2001
From: Varun Gupta <varun.gupta@mariadb.com>
Date: Wed, 19 Dec 2018 10:38:29 +0530
Subject: [PATCH 06/10] Backported MDEV-11196(e4d10e09cf31) and
 MDEV-10360(8a8ba1949bf4) to 10.0

---
 mysql-test/r/innodb_ext_key.result |  95 ++++++++++++++++++++++++++
 mysql-test/t/innodb_ext_key.test   | 106 +++++++++++++++++++++++++++++
 sql/table.cc                       |  48 +++++++++++--
 3 files changed, 244 insertions(+), 5 deletions(-)

diff --git a/mysql-test/r/innodb_ext_key.result b/mysql-test/r/innodb_ext_key.result
index 2b3b98eb26a..600269ba433 100644
--- a/mysql-test/r/innodb_ext_key.result
+++ b/mysql-test/r/innodb_ext_key.result
@@ -1068,5 +1068,100 @@ a
 1
 drop table t1, t2;
 set optimizer_switch=@save_optimizer_switch;
+# 
+# MDEV-10360: Extended keys: index properties depend on index order
+# 
+create table t0 (a int);
+insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t1 (
+index_id bigint(20) unsigned NOT NULL,
+index_class varchar(265) COLLATE latin1_general_ci DEFAULT NULL ,
+index_object_id int(10) unsigned NOT NULL DEFAULT '0' ,
+index_date_updated int(10) unsigned DEFAULT NULL ,
+PRIMARY KEY (index_id),
+KEY object (index_class(181),index_object_id),
+KEY index_date_updated (index_date_updated)
+) engine=innodb;
+create table t2 (
+index_id bigint(20) unsigned NOT NULL,
+index_class varchar(265) COLLATE latin1_general_ci DEFAULT NULL ,
+index_object_id int(10) unsigned NOT NULL DEFAULT '0' ,
+index_date_updated int(10) unsigned DEFAULT NULL ,
+PRIMARY KEY (index_id),
+KEY index_date_updated (index_date_updated),
+KEY object (index_class(181),index_object_id)
+) engine=innodb;
+insert into t1 select 
+@a:=A.a + 10*B.a + 100*C.a,
+concat('val-', @a),
+123456,
+A.a + 10*B.a
+from 
+t0 A, t0 B, t0 C;
+insert into t2 select * from t1;
+# This must have the same query plan as the query below it:
+# type=range, key=index_date_updated, key_len=13
+explain 
+select * from t1 force index(index_date_updated) 
+where index_date_updated= 10 and index_id < 800;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	index_date_updated	index_date_updated	13	NULL	#	Using index condition
+# This used to work from the start:
+explain 
+select * from t2 force index(index_date_updated) 
+where index_date_updated= 10 and index_id < 800;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	range	index_date_updated	index_date_updated	13	NULL	#	Using index condition
+drop table t0,t1,t2;
+#
+# MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the variable 'key_buff'
+# was corrupted, server crashes in opt_sum_query
+set @save_innodb_file_format= @@innodb_file_format;
+set @save_innodb_large_prefix= @@innodb_large_prefix;
+set global innodb_file_format = BARRACUDA;
+set global innodb_large_prefix = ON;
+CREATE TABLE t1 (
+pk INT,
+f1 VARCHAR(3),
+f2 VARCHAR(1024),
+PRIMARY KEY (pk),
+KEY(f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+INSERT INTO t1 VALUES (1,'foo','abc'),(2,'bar','def');
+SELECT MAX(t2.pk) FROM t1 t2 INNER JOIN t1 t3 ON t2.f1 = t3.f1 WHERE t2.pk <= 4;
+MAX(t2.pk)
+2
+drop table t1;
+CREATE TABLE t1 (
+pk1 INT,
+pk2 INT,
+f1 VARCHAR(3),
+f2 VARCHAR(1021),
+PRIMARY KEY (pk1,pk2),
+KEY(f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
+explain
+select * from t1 force index(f2)  where pk1 <= 5 and pk2 <=5 and f2 = 'abc' and f1 <= '3';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	f2	f2	3070	NULL	1	Using index condition; Using where
+drop table t1;
+CREATE TABLE t1 (
+f2 INT,
+pk2 INT,
+f1 VARCHAR(3),
+pk1 VARCHAR(1000),
+PRIMARY KEY (pk1,pk2),
+KEY k1(pk1,f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
+explain
+select * from t1 force index(k1)  where f2 <= 5 and pk2 <=5 and pk1 = 'abc' and f1 <= '3';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	k1	k1	3011	NULL	1	Using index condition; Using where
+drop table t1;
+set optimizer_switch=@save_ext_key_optimizer_switch;
+set global innodb_file_format = @save_innodb_file_format;
+set global innodb_large_prefix = @save_innodb_large_prefix;
 set optimizer_switch=@save_ext_key_optimizer_switch;
 SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/mysql-test/t/innodb_ext_key.test b/mysql-test/t/innodb_ext_key.test
index 9f3a89ff948..d53deb46348 100644
--- a/mysql-test/t/innodb_ext_key.test
+++ b/mysql-test/t/innodb_ext_key.test
@@ -693,5 +693,111 @@ drop table t1, t2;
 
 set optimizer_switch=@save_optimizer_switch;
 
+--echo # 
+--echo # MDEV-10360: Extended keys: index properties depend on index order
+--echo # 
+create table t0 (a int);
+insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+create table t1 (
+  index_id bigint(20) unsigned NOT NULL,
+  index_class varchar(265) COLLATE latin1_general_ci DEFAULT NULL ,
+  index_object_id int(10) unsigned NOT NULL DEFAULT '0' ,
+  index_date_updated int(10) unsigned DEFAULT NULL ,
+ 
+  PRIMARY KEY (index_id),
+  KEY object (index_class(181),index_object_id),
+  KEY index_date_updated (index_date_updated)
+) engine=innodb;
+
+create table t2 (
+  index_id bigint(20) unsigned NOT NULL,
+  index_class varchar(265) COLLATE latin1_general_ci DEFAULT NULL ,
+  index_object_id int(10) unsigned NOT NULL DEFAULT '0' ,
+  index_date_updated int(10) unsigned DEFAULT NULL ,
+ 
+  PRIMARY KEY (index_id),
+  KEY index_date_updated (index_date_updated),
+  KEY object (index_class(181),index_object_id)
+) engine=innodb;
+
+insert into t1 select 
+  @a:=A.a + 10*B.a + 100*C.a,
+  concat('val-', @a),
+  123456,
+  A.a + 10*B.a
+from 
+  t0 A, t0 B, t0 C;
+
+insert into t2 select * from t1;
+
+--echo # This must have the same query plan as the query below it:
+--echo # type=range, key=index_date_updated, key_len=13
+--replace_column 9 #
+explain 
+select * from t1 force index(index_date_updated) 
+where index_date_updated= 10 and index_id < 800;
+
+--echo # This used to work from the start:
+--replace_column 9 #
+explain 
+select * from t2 force index(index_date_updated) 
+where index_date_updated= 10 and index_id < 800;
+
+drop table t0,t1,t2;
+
+
+--echo #
+--echo # MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the variable 'key_buff'
+--echo # was corrupted, server crashes in opt_sum_query
+
+set @save_innodb_file_format= @@innodb_file_format;
+set @save_innodb_large_prefix= @@innodb_large_prefix;
+set global innodb_file_format = BARRACUDA;
+set global innodb_large_prefix = ON;
+
+CREATE TABLE t1 (
+  pk INT,
+  f1 VARCHAR(3),
+  f2 VARCHAR(1024),
+  PRIMARY KEY (pk),
+  KEY(f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+
+INSERT INTO t1 VALUES (1,'foo','abc'),(2,'bar','def');
+SELECT MAX(t2.pk) FROM t1 t2 INNER JOIN t1 t3 ON t2.f1 = t3.f1 WHERE t2.pk <= 4;
+drop table t1;
+
+CREATE TABLE t1 (
+  pk1 INT,
+  pk2 INT,
+  f1 VARCHAR(3),
+  f2 VARCHAR(1021),
+  PRIMARY KEY (pk1,pk2),
+  KEY(f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+
+INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
+explain
+select * from t1 force index(f2)  where pk1 <= 5 and pk2 <=5 and f2 = 'abc' and f1 <= '3';
+drop table t1;
+
+CREATE TABLE t1 (
+f2 INT,
+pk2 INT,
+f1 VARCHAR(3),
+pk1 VARCHAR(1000),
+PRIMARY KEY (pk1,pk2),
+KEY k1(pk1,f2)
+) ENGINE=InnoDB CHARSET utf8 ROW_FORMAT= DYNAMIC;
+INSERT INTO t1 VALUES (1,2,'2','abc'),(2,3,'3','def');
+explain
+select * from t1 force index(k1)  where f2 <= 5 and pk2 <=5 and pk1 = 'abc' and f1 <= '3';
+drop table t1;
+
+set optimizer_switch=@save_ext_key_optimizer_switch;
+set global innodb_file_format = @save_innodb_file_format;
+set global innodb_large_prefix = @save_innodb_large_prefix;
+
 set optimizer_switch=@save_ext_key_optimizer_switch;
 SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/sql/table.cc b/sql/table.cc
index 98bf6d8b4dd..5a34d47367a 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1690,6 +1690,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
     keyinfo= share->key_info;
     uint primary_key= my_strcasecmp(system_charset_info, share->keynames.type_names[0],
                                     primary_key_name) ? MAX_KEY : 0;
+    KEY* key_first_info= NULL;
 
     if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME)
     {
@@ -1769,34 +1770,71 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
                keyinfo->name_length+1);
       }
 
+      if (!key)
+        key_first_info= keyinfo;
+
       if (ext_key_parts > share->key_parts && key)
       {
         KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
                                      (keyinfo-1)->ext_key_parts;
+        uint add_keyparts_for_this_key= add_first_key_parts;
+        uint length_bytes= 0, len_null_byte= 0, ext_key_length= 0;
+        Field *field;
 
         /* 
           Do not extend the key that contains a component
           defined over the beginning of a field.
 	*/ 
         for (i= 0; i < keyinfo->user_defined_key_parts; i++)
-	{
+        {
           uint fieldnr= keyinfo->key_part[i].fieldnr;
+          field= share->field[keyinfo->key_part[i].fieldnr-1];
+
+          if (field->null_ptr)
+            len_null_byte= HA_KEY_NULL_LENGTH;
+
+          if (field->type() == MYSQL_TYPE_BLOB ||
+             field->real_type() == MYSQL_TYPE_VARCHAR ||
+             field->type() == MYSQL_TYPE_GEOMETRY)
+          {
+            length_bytes= HA_KEY_BLOB_LENGTH;
+          }
+          ext_key_length+= keyinfo->key_part[i].length + len_null_byte
+                            + length_bytes;
           if (share->field[fieldnr-1]->key_length() !=
               keyinfo->key_part[i].length)
 	  {
-            add_first_key_parts= 0;
+            add_keyparts_for_this_key= 0;
             break;
           }
         }
 
-        if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->user_defined_key_parts)
-	{
+        if (add_keyparts_for_this_key)
+        {
+          for (i= 0; i < add_keyparts_for_this_key; i++)
+          {
+            uint pk_part_length= key_first_info->key_part[i].store_length;
+            if (keyinfo->ext_key_part_map & 1<<i)
+            {
+              if (ext_key_length + pk_part_length > MAX_KEY_LENGTH)
+              {
+                add_keyparts_for_this_key= i;
+                break;
+              }
+              ext_key_length+= pk_part_length;
+            }
+          }
+        }
+
+        if (add_keyparts_for_this_key < keyinfo->ext_key_parts -
+                                        keyinfo->user_defined_key_parts)
+        {
           share->ext_key_parts-= keyinfo->ext_key_parts;
           key_part_map ext_key_part_map= keyinfo->ext_key_part_map;
           keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
           keyinfo->ext_key_flags= keyinfo->flags;
 	  keyinfo->ext_key_part_map= 0; 
-          for (i= 0; i < add_first_key_parts; i++)
+          for (i= 0; i <  add_keyparts_for_this_key; i++)
 	  {
             if (ext_key_part_map & 1<<i)
 	    {

From 7e606a2d5ca59dbd901b7c8909214a0fc12fbadd Mon Sep 17 00:00:00 2001
From: Varun Gupta <varun.gupta@mariadb.com>
Date: Wed, 19 Dec 2018 10:34:30 +0530
Subject: [PATCH 07/10] MDEV-17589: Stack-buffer-overflow with indexed varchar
 (utf8) field

Create a new constant MAX_DATA_LENGTH_FOR_KEY.
Replace the value of MAX_KEY_LENGTH to also include the LENGTH and NULL BYTES
of a field.
---
 mysql-test/r/func_group_innodb.result | 30 +++++++++++++++++++++++++++
 mysql-test/t/func_group_innodb.test   | 26 +++++++++++++++++++++++
 sql/handler.h                         | 12 ++++++++---
 sql/partition_info.cc                 |  4 ++--
 sql/sql_const.h                       | 12 ++++++++++-
 sql/table.cc                          |  2 +-
 6 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/mysql-test/r/func_group_innodb.result b/mysql-test/r/func_group_innodb.result
index 52d5922df95..17b3c1e797e 100644
--- a/mysql-test/r/func_group_innodb.result
+++ b/mysql-test/r/func_group_innodb.result
@@ -246,4 +246,34 @@ EXPLAIN SELECT MIN(c) FROM t1 GROUP BY b;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	range	NULL	b	263	NULL	3	Using index for group-by
 DROP TABLE t1;
+#
+# MDEV-17589: Stack-buffer-overflow with indexed varchar (utf8) field
+#
+set @save_innodb_file_format= @@innodb_file_format;
+set @save_innodb_large_prefix= @@innodb_large_prefix;
+set global innodb_file_format = BARRACUDA;
+set global innodb_large_prefix = ON;
+CREATE TABLE t1 (v1 varchar(1020), v2 varchar(2), v3 varchar(2),
+KEY k1 (v3,v2,v1)) ENGINE=InnoDB CHARACTER SET=utf8 ROW_FORMAT=DYNAMIC;
+INSERT INTO t1 VALUES ('king', 'qu','qu'), ('bad','go','go');
+explain
+SELECT MIN(t1.v1) FROM t1 where t1.v2='qu' and t1.v3='qu';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+SELECT MIN(t1.v1) FROM t1 where t1.v2='qu' and t1.v3='qu';
+MIN(t1.v1)
+king
+drop table t1;
+CREATE TABLE t1 (v1 varchar(1024) CHARACTER SET utf8, KEY v1 (v1)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
+INSERT INTO t1 VALUES ('king'), ('bad');
+explain
+SELECT MIN(x.v1) FROM (SELECT t1.* FROM t1 WHERE t1.v1 >= 'p') x;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No matching min/max row
+SELECT MIN(x.v1) FROM (SELECT t1.* FROM t1 WHERE t1.v1 >= 'p') x;
+MIN(x.v1)
+NULL
+drop table t1;
+set global innodb_file_format = @save_innodb_file_format;
+set global innodb_large_prefix = @save_innodb_large_prefix;
 End of 5.5 tests
diff --git a/mysql-test/t/func_group_innodb.test b/mysql-test/t/func_group_innodb.test
index c62d3d08496..a65d2326d0f 100644
--- a/mysql-test/t/func_group_innodb.test
+++ b/mysql-test/t/func_group_innodb.test
@@ -192,4 +192,30 @@ EXPLAIN SELECT MIN(c) FROM t1 GROUP BY b;
 
 DROP TABLE t1;
 
+--echo #
+--echo # MDEV-17589: Stack-buffer-overflow with indexed varchar (utf8) field
+--echo #
+
+set @save_innodb_file_format= @@innodb_file_format;
+set @save_innodb_large_prefix= @@innodb_large_prefix;
+set global innodb_file_format = BARRACUDA;
+set global innodb_large_prefix = ON;
+
+CREATE TABLE t1 (v1 varchar(1020), v2 varchar(2), v3 varchar(2),
+                KEY k1 (v3,v2,v1)) ENGINE=InnoDB CHARACTER SET=utf8 ROW_FORMAT=DYNAMIC;
+INSERT INTO t1 VALUES ('king', 'qu','qu'), ('bad','go','go');
+explain
+SELECT MIN(t1.v1) FROM t1 where t1.v2='qu' and t1.v3='qu';
+SELECT MIN(t1.v1) FROM t1 where t1.v2='qu' and t1.v3='qu';
+drop table t1;
+
+CREATE TABLE t1 (v1 varchar(1024) CHARACTER SET utf8, KEY v1 (v1)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
+INSERT INTO t1 VALUES ('king'), ('bad');
+explain
+SELECT MIN(x.v1) FROM (SELECT t1.* FROM t1 WHERE t1.v1 >= 'p') x;
+SELECT MIN(x.v1) FROM (SELECT t1.* FROM t1 WHERE t1.v1 >= 'p') x;
+drop table t1;
+set global innodb_file_format = @save_innodb_file_format;
+set global innodb_large_prefix = @save_innodb_large_prefix;
+
 --echo End of 5.5 tests
diff --git a/sql/handler.h b/sql/handler.h
index 52396b84c0d..606adade679 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -384,6 +384,12 @@ enum enum_alter_inplace_result {
 #define HA_KEY_NULL_LENGTH	1
 #define HA_KEY_BLOB_LENGTH	2
 
+/* Maximum length of any index lookup key, in bytes */
+
+#define MAX_KEY_LENGTH (MAX_DATA_LENGTH_FOR_KEY \
+                         +(MAX_REF_PARTS \
+                          *(HA_KEY_NULL_LENGTH + HA_KEY_BLOB_LENGTH)))
+
 #define HA_LEX_CREATE_TMP_TABLE	1
 #define HA_LEX_CREATE_IF_NOT_EXISTS 2
 #define HA_LEX_CREATE_TABLE_LIKE 4
@@ -3233,14 +3239,14 @@ public:
   uint max_key_parts() const
   { return MY_MIN(MAX_REF_PARTS, max_supported_key_parts()); }
   uint max_key_length() const
-  { return MY_MIN(MAX_KEY_LENGTH, max_supported_key_length()); }
+  { return MY_MIN(MAX_DATA_LENGTH_FOR_KEY, max_supported_key_length()); }
   uint max_key_part_length() const
-  { return MY_MIN(MAX_KEY_LENGTH, max_supported_key_part_length()); }
+  { return MY_MIN(MAX_DATA_LENGTH_FOR_KEY, max_supported_key_part_length()); }
 
   virtual uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
   virtual uint max_supported_keys() const { return 0; }
   virtual uint max_supported_key_parts() const { return MAX_REF_PARTS; }
-  virtual uint max_supported_key_length() const { return MAX_KEY_LENGTH; }
+  virtual uint max_supported_key_length() const { return MAX_DATA_LENGTH_FOR_KEY; }
   virtual uint max_supported_key_part_length() const { return 255; }
   virtual uint min_record_length(uint options) const { return 1; }
 
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index e6a30c8a7f0..7106e741331 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1990,12 +1990,12 @@ bool partition_info::check_partition_field_length()
 
   for (i= 0; i < num_part_fields; i++)
     store_length+= get_partition_field_store_length(part_field_array[i]);
-  if (store_length > MAX_KEY_LENGTH)
+  if (store_length > MAX_DATA_LENGTH_FOR_KEY)
     DBUG_RETURN(TRUE);
   store_length= 0;
   for (i= 0; i < num_subpart_fields; i++)
     store_length+= get_partition_field_store_length(subpart_field_array[i]);
-  if (store_length > MAX_KEY_LENGTH)
+  if (store_length > MAX_DATA_LENGTH_FOR_KEY)
     DBUG_RETURN(TRUE);
   DBUG_RETURN(FALSE);
 }
diff --git a/sql/sql_const.h b/sql/sql_const.h
index c37d8dd68f7..c0b343c6ca4 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -33,7 +33,17 @@
 #define MAX_SYS_VAR_LENGTH 32
 #define MAX_KEY MAX_INDEXES                     /* Max used keys */
 #define MAX_REF_PARTS 32			/* Max parts used as ref */
-#define MAX_KEY_LENGTH 3072			/* max possible key */
+
+/*
+  Maximum length of the data part of an index lookup key.
+
+  The "data part" is defined as the value itself, not including the
+  NULL-indicator bytes or varchar length bytes ("the Extras"). We need this
+  value because there was a bug where length of the Extras were not counted.
+
+  You probably need MAX_KEY_LENGTH, not this constant.
+*/
+#define MAX_DATA_LENGTH_FOR_KEY 3072
 #if SIZEOF_OFF_T > 4
 #define MAX_REFLENGTH 8				/* Max length for record ref */
 #else
diff --git a/sql/table.cc b/sql/table.cc
index 5a34d47367a..ded79de0e83 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1816,7 +1816,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
             uint pk_part_length= key_first_info->key_part[i].store_length;
             if (keyinfo->ext_key_part_map & 1<<i)
             {
-              if (ext_key_length + pk_part_length > MAX_KEY_LENGTH)
+              if (ext_key_length + pk_part_length > MAX_DATA_LENGTH_FOR_KEY)
               {
                 add_keyparts_for_this_key= i;
                 break;

From f16d4d4c6ee81cfee3b0d1cb2f2219b1fa982e2e Mon Sep 17 00:00:00 2001
From: Sachin <sachin.setiya@mariadb.com>
Date: Wed, 19 Dec 2018 16:33:00 +0530
Subject: [PATCH 08/10] MDEV-17720 slave_ddl_exec_mode=IDEMPOTENT does not
 handle DROP DATABASE

Relevant if exists flag are added for create database and drop database.
---
 mysql-test/suite/rpl/r/rpl_idempotency.result | 12 +++++++++++
 mysql-test/suite/rpl/t/rpl_idempotency.test   | 21 +++++++++++++++++++
 sql/sql_parse.cc                              |  6 ++++++
 3 files changed, 39 insertions(+)

diff --git a/mysql-test/suite/rpl/r/rpl_idempotency.result b/mysql-test/suite/rpl/r/rpl_idempotency.result
index 38b955d7697..03482e6fefb 100644
--- a/mysql-test/suite/rpl/r/rpl_idempotency.result
+++ b/mysql-test/suite/rpl/r/rpl_idempotency.result
@@ -67,6 +67,18 @@ a
 -3
 1
 include/check_slave_no_error.inc
+drop table t1, t2;
 DROP TABLE t1, t2;
+include/check_slave_no_error.inc
+create database d;
+create database e;
+create database d;
+create database if not exists e;
+include/check_slave_no_error.inc
+drop database d;
+drop database e;
+drop database d;
+drop database if exists e;
+include/check_slave_no_error.inc
 SET @@global.slave_exec_mode= @old_slave_exec_mode;
 include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_idempotency.test b/mysql-test/suite/rpl/t/rpl_idempotency.test
index 186c6260154..e801aac9b5e 100644
--- a/mysql-test/suite/rpl/t/rpl_idempotency.test
+++ b/mysql-test/suite/rpl/t/rpl_idempotency.test
@@ -75,9 +75,30 @@ SELECT * FROM t1 ORDER BY a;
 SELECT * FROM t2 ORDER BY a;
 --source include/check_slave_no_error.inc
 
+connection slave;
+drop table t1, t2;
+
 connection master;
 DROP TABLE t1, t2;
 sync_slave_with_master;
+--source include/check_slave_no_error.inc
+create database d;
+create database e;
+
+connection master;
+create database d;
+create database if not exists e;
+
+sync_slave_with_master;
+--source include/check_slave_no_error.inc
+drop database d;
+drop database e;
+
+connection master;
+drop database d;
+drop database if exists e;
+sync_slave_with_master;
+--source include/check_slave_no_error.inc
 
 SET @@global.slave_exec_mode= @old_slave_exec_mode;
 
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a9849c7248d..5ca22f0dedc 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3884,6 +3884,9 @@ end_with_restore_list:
         my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
         break;
       }
+    if (slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
+        !(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
+      create_info.options|= HA_LEX_CREATE_IF_NOT_EXISTS;
     }
 #endif
     if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
@@ -3915,6 +3918,9 @@ end_with_restore_list:
         my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
         break;
       }
+    if (!thd->slave_expected_error &&
+        slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
+      lex->check_exists= 1;
     }
 #endif
     if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))

From b74eb5a5feb41164f7e5cab986cf203537c2128a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Fri, 28 Dec 2018 12:28:16 +0200
Subject: [PATCH 09/10] row_drop_table_for_mysql(): Correct a parameter to
 innobase_format_name()

This fixes a regression that was introduced in MySQL 5.6.6
in an error handling code path, in the following change:

commit 024f363d6b5f09b20d1bba411af55be95c7398d3
Author: kevin.lewis@oracle.com <>
Date:   Fri Jun 15 09:01:42 2012 -0500

    Bug #14169459 INNODB; DROP TABLE DOES NOT DELETE THE IBD FILE
    FOR A TEMPORARY TABLE.
---
 storage/innobase/row/row0mysql.cc | 3 ++-
 storage/xtradb/row/row0mysql.cc   | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index d9e18a99201..68329658618 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -4323,7 +4323,8 @@ do_drop:
 					char msg_tablename[MAX_FULL_NAME_LEN + 1];
 
 					innobase_format_name(
-						msg_tablename, sizeof(tablename),
+						msg_tablename,
+						sizeof msg_tablename,
 						tablename, FALSE);
 
 					ib_logf(IB_LOG_LEVEL_INFO,
diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc
index cb6e6bbcb1f..e828d8a8611 100644
--- a/storage/xtradb/row/row0mysql.cc
+++ b/storage/xtradb/row/row0mysql.cc
@@ -4347,7 +4347,8 @@ do_drop:
 					char msg_tablename[MAX_FULL_NAME_LEN + 1];
 
 					innobase_format_name(
-						msg_tablename, sizeof(tablename),
+						msg_tablename,
+						sizeof msg_tablename,
 						tablename, FALSE);
 
 					ib_logf(IB_LOG_LEVEL_INFO,

From 802ce9672ff630bbef08235e0e39bf599075f985 Mon Sep 17 00:00:00 2001
From: Eugene Kosov <claprix@yandex.ru>
Date: Sat, 29 Dec 2018 20:44:40 +0300
Subject: [PATCH 10/10] MDEV-18041 Database corruption after renaming a
 prefix-indexed column

This is a regression after MDEV-13671.

The bug is related to key part prefix lengths wich are stored in SYS_FIELDS.
Storage format is not obvious and was handled incorrectly which led to data
dictionary corruption.

SYS_FIELDS.POS actually contains prefix length too in case if any key part
has prefix length.

innobase_rename_column_try(): fixed prefixes handling

Tests for prefixed indexes added too.

Closes #1063
---
 mysql-test/suite/innodb/r/innodb-alter.result | 21 ++++++++++++
 mysql-test/suite/innodb/t/innodb-alter.test   | 13 ++++++++
 storage/innobase/handler/handler0alter.cc     | 32 +++++++++++--------
 storage/xtradb/handler/handler0alter.cc       | 32 +++++++++++--------
 4 files changed, 70 insertions(+), 28 deletions(-)

diff --git a/mysql-test/suite/innodb/r/innodb-alter.result b/mysql-test/suite/innodb/r/innodb-alter.result
index 2f9b832328b..57638a84517 100644
--- a/mysql-test/suite/innodb/r/innodb-alter.result
+++ b/mysql-test/suite/innodb/r/innodb-alter.result
@@ -865,6 +865,27 @@ WHERE T.NAME='test/t1';
 NAME
 a
 DROP TABLE t1;
+# and an MDEV-18041 regression related to indexes prefixes
+create table `test` (
+`test_old` varchar(255) NOT NULL,
+`other` varchar(255) NOT NULL,
+PRIMARY KEY (`test_old`,`other`),
+UNIQUE KEY uk (`test_old`(100), `other`)
+) ENGINE=InnoDB;
+select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
+name	pos
+test_old	0
+other	1
+test_old	0
+other	1
+alter table `test` CHANGE COLUMN `test_old` `test_new` varchar(255) NOT NULL;
+select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
+name	pos
+test_new	0
+other	1
+test_new	0
+other	1
+drop table `test`;
 #
 # BUG 20029625 - HANDLE_FATAL_SIGNAL (SIG=11) IN
 #                DICT_MEM_TABLE_COL_RENAME_LOW
diff --git a/mysql-test/suite/innodb/t/innodb-alter.test b/mysql-test/suite/innodb/t/innodb-alter.test
index 30e3292ec10..a47573626aa 100644
--- a/mysql-test/suite/innodb/t/innodb-alter.test
+++ b/mysql-test/suite/innodb/t/innodb-alter.test
@@ -522,6 +522,19 @@ SELECT C.NAME FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS C INNER JOIN
   WHERE T.NAME='test/t1';
 DROP TABLE t1;
 
+--echo # and an MDEV-18041 regression related to indexes prefixes
+create table `test` (
+  `test_old` varchar(255) NOT NULL,
+  `other` varchar(255) NOT NULL,
+  PRIMARY KEY (`test_old`,`other`),
+  UNIQUE KEY uk (`test_old`(100), `other`)
+) ENGINE=InnoDB;
+
+select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
+alter table `test` CHANGE COLUMN `test_old` `test_new` varchar(255) NOT NULL;
+select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
+drop table `test`;
+
 
 --echo #
 --echo # BUG 20029625 - HANDLE_FATAL_SIGNAL (SIG=11) IN
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 6d9255a628e..8fbdf6c6884 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -4533,36 +4533,40 @@ err_exit:
 	     index != NULL;
 	     index = dict_table_get_next_index(index)) {
 
+		bool has_prefixes = false;
+		for (size_t i = 0; i < dict_index_get_n_fields(index); i++) {
+			if (dict_index_get_nth_field(index, i)->prefix_len) {
+				has_prefixes = true;
+				break;
+			}
+		}
+
 		for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
-			if (my_strcasecmp(
-				system_charset_info,
-				dict_index_get_nth_field(index, i)->name,
-				from)) {
+			const dict_field_t* field
+			    = dict_index_get_nth_field(index, i);
+			if (my_strcasecmp(system_charset_info, field->name,
+					  from)) {
 				continue;
 			}
 
 			info = pars_info_create();
 
+			int pos = i;
+			if (has_prefixes) {
+				pos = (pos << 16) + field->prefix_len;
+			}
+
 			pars_info_add_ull_literal(info, "indexid", index->id);
-			pars_info_add_int4_literal(info, "nth", i);
+			pars_info_add_int4_literal(info, "nth", pos);
 			pars_info_add_str_literal(info, "new", to);
 
 			error = que_eval_sql(
 				info,
 				"PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
 				"BEGIN\n"
-
 				"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
 				"WHERE INDEX_ID=:indexid\n"
 				"AND POS=:nth;\n"
-
-				/* Try again, in case there is a prefix_len
-				encoded in SYS_FIELDS.POS */
-
-				"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
-				"WHERE INDEX_ID=:indexid\n"
-				"AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
-
 				"END;\n",
 				FALSE, trx);
 
diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc
index 9508adbbd12..bdec9a076ac 100644
--- a/storage/xtradb/handler/handler0alter.cc
+++ b/storage/xtradb/handler/handler0alter.cc
@@ -4547,36 +4547,40 @@ err_exit:
 	     index != NULL;
 	     index = dict_table_get_next_index(index)) {
 
+		bool has_prefixes = false;
+		for (size_t i = 0; i < dict_index_get_n_fields(index); i++) {
+			if (dict_index_get_nth_field(index, i)->prefix_len) {
+				has_prefixes = true;
+				break;
+			}
+		}
+
 		for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
-			if (my_strcasecmp(
-				system_charset_info,
-				dict_index_get_nth_field(index, i)->name,
-				from)) {
+			const dict_field_t* field
+			    = dict_index_get_nth_field(index, i);
+			if (my_strcasecmp(system_charset_info, field->name,
+					  from)) {
 				continue;
 			}
 
 			info = pars_info_create();
 
+			int pos = i;
+			if (has_prefixes) {
+				pos = (pos << 16) + field->prefix_len;
+			}
+
 			pars_info_add_ull_literal(info, "indexid", index->id);
-			pars_info_add_int4_literal(info, "nth", i);
+			pars_info_add_int4_literal(info, "nth", pos);
 			pars_info_add_str_literal(info, "new", to);
 
 			error = que_eval_sql(
 				info,
 				"PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
 				"BEGIN\n"
-
 				"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
 				"WHERE INDEX_ID=:indexid\n"
 				"AND POS=:nth;\n"
-
-				/* Try again, in case there is a prefix_len
-				encoded in SYS_FIELDS.POS */
-
-				"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
-				"WHERE INDEX_ID=:indexid\n"
-				"AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
-
 				"END;\n",
 				FALSE, trx);