From 276f996af917851b9c5fef4a41f93e68d398af36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Wed, 22 Jan 2020 18:51:29 +0200
Subject: [PATCH] MDEV-12353: Replace MLOG_*_END_COPY_CREATED

Instead of writing the high-level redo log records
MLOG_LIST_END_COPY_CREATED, MLOG_COMP_LIST_END_COPY_CREATED
write log for each individual insert of a record.

page_copy_rec_list_end_to_created_page(): Remove.

This will improve the fill factor of some pages.
Adjust some tests accordingly.

PageBulk::init(), PageBulk::finish(): Avoid setting bogus limits
to PAGE_HEAP_TOP and PAGE_N_DIR_SLOTS. Avoid accessor functions
that would enforce these limits before the correct ones are set
at the end of PageBulk::finish().
---
 .../r/innodb-discard-import-change.result     |   2 +-
 .../innodb/r/index_merge_threshold.result     | 280 ++++++++++--------
 .../innodb/r/index_tree_operation.result      |  21 +-
 .../r/innodb-change-buffer-recovery.result    |   3 +-
 .../r/innodb-page_compression_default.result  |   2 +-
 .../r/innodb-page_compression_snappy.result   |   2 +-
 .../r/innodb_defragment_fill_factor.result    |   1 +
 .../suite/innodb/r/instant_alter,32k.rdiff    |   6 +-
 .../suite/innodb/r/instant_alter,4k.rdiff     |   2 +-
 .../suite/innodb/r/instant_alter,8k.rdiff     |  18 +-
 ...leaf_page_corrupted_during_recovery.result |   8 +-
 .../t/innodb-change-buffer-recovery.test      |   7 +
 .../leaf_page_corrupted_during_recovery.test  |  16 +-
 .../innodb_zip/r/wl6347_comp_indx_stat.result |   6 +-
 storage/innobase/btr/btr0bulk.cc              | 223 ++++++++------
 storage/innobase/include/page0cur.h           |   1 +
 storage/innobase/page/page0cur.cc             |   1 +
 storage/innobase/page/page0page.cc            |  52 ++--
 18 files changed, 361 insertions(+), 290 deletions(-)

diff --git a/mysql-test/suite/encryption/r/innodb-discard-import-change.result b/mysql-test/suite/encryption/r/innodb-discard-import-change.result
index cafdbef4b69..d13c730c7e7 100644
--- a/mysql-test/suite/encryption/r/innodb-discard-import-change.result
+++ b/mysql-test/suite/encryption/r/innodb-discard-import-change.result
@@ -100,5 +100,5 @@ NOT FOUND /verysecretmessage/ in t3.ibd
 # t4 page compressed and encrypted expecting NOT FOUND
 NOT FOUND /verysecretmessage/ in t4.ibd
 # t5 normal expecting FOUND
-FOUND 289 /verysecretmessage/ in t5.ibd
+FOUND 256 /verysecretmessage/ in t5.ibd
 DROP TABLE t1,t2,t3,t4,t5,t6;
diff --git a/mysql-test/suite/innodb/r/index_merge_threshold.result b/mysql-test/suite/innodb/r/index_merge_threshold.result
index 35cb82e6f1e..b8a6d81aecc 100644
--- a/mysql-test/suite/innodb/r/index_merge_threshold.result
+++ b/mysql-test/suite/innodb/r/index_merge_threshold.result
@@ -204,9 +204,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 begin;
 delete from tab1 where a = 12;
 delete from tab1 where a = 13;
@@ -220,8 +221,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -230,31 +231,32 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	4
-5	4
+5	2
+6	4
 delete from tab1 where a = 11;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 10;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 delete from tab1 where a = 9;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=35)
 CREATE TABLE tab1 (a bigint primary key, b varchar(2048)) engine=InnoDB
@@ -285,9 +287,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 begin;
 delete from tab1 where a = 12;
 delete from tab1 where a = 13;
@@ -301,8 +304,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -311,31 +314,32 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	4
-5	4
+5	2
+6	4
 delete from tab1 where a = 11;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 10;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 9;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=25)
 CREATE TABLE tab1 (a bigint primary key, b varchar(2048)) engine=InnoDB
@@ -366,9 +370,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 begin;
 delete from tab1 where a = 12;
 delete from tab1 where a = 13;
@@ -382,8 +387,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -392,31 +397,32 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	4
-5	4
+5	1
+6	4
 delete from tab1 where a = 11;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 10;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 9;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 # test to confirm partitioned table (MERGE_THRESHOLD=35)
 CREATE TABLE tab1 (a bigint primary key, b varchar(2048))
@@ -452,9 +458,10 @@ where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	0
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 begin;
 delete from tab1 where a = 12;
 delete from tab1 where a = 13;
@@ -468,8 +475,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -479,31 +486,32 @@ PAGE_NUMBER	NUMBER_RECORDS
 3	0
 3	2
 4	4
-5	4
+5	2
+6	4
 delete from tab1 where a = 11;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 10;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 9;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 #
 # behavior for updating to smaller records
@@ -536,9 +544,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 update tab1 set b='' where a = 12;
 update tab1 set b='' where a = 13;
 update tab1 set b='' where a = 14;
@@ -549,8 +558,8 @@ update tab1 set b='' where a = 7;
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -559,28 +568,29 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	7
-5	7
+5	3
+6	7
 update tab1 set b='' where a = 11;
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 update tab1 set b='' where a = 10;
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 update tab1 set b='' where a = 9;
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=35)
 CREATE TABLE tab1 (a bigint primary key, b varchar(2048)) engine=InnoDB
@@ -611,9 +621,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 update tab1 set b='' where a = 12;
 update tab1 set b='' where a = 13;
 update tab1 set b='' where a = 14;
@@ -624,8 +635,8 @@ update tab1 set b='' where a = 7;
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -634,28 +645,29 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	7
-5	7
+5	3
+6	7
 update tab1 set b='' where a = 11;
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 update tab1 set b='' where a = 10;
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 update tab1 set b='' where a = 9;
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=25)
 CREATE TABLE tab1 (a bigint primary key, b varchar(2048)) engine=InnoDB
@@ -686,9 +698,10 @@ INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
 where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
-3	2
+3	3
 4	7
-5	7
+5	3
+6	4
 update tab1 set b='' where a = 12;
 update tab1 set b='' where a = 13;
 update tab1 set b='' where a = 14;
@@ -699,8 +712,8 @@ update tab1 set b='' where a = 7;
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -709,28 +722,29 @@ and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	2
 4	7
-5	7
+5	3
+6	7
 update tab1 set b='' where a = 11;
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 update tab1 set b='' where a = 10;
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 update tab1 set b='' where a = 9;
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 # test to confirm explicit temporary table (MERGE_THRESHOLD=35)
 # (though not registered to SYS_TABLES,SYS_INDEXES, it works correctly)
@@ -771,8 +785,8 @@ update tab1 set b='' where a = 7;
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -784,22 +798,22 @@ update tab1 set b='' where a = 11;
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 update tab1 set b='' where a = 10;
 # check page merge happens (MERGE_THRESHOLD=35 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 update tab1 set b='' where a = 9;
 # check page merge happens (MERGE_THRESHOLD=25 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 #
 # behavior for secondary index with blob
@@ -863,9 +877,10 @@ where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	42
-4	2
+4	3
 27	21
-28	21
+28	10
+30	11
 begin;
 delete from tab1 where a = 33;
 delete from tab1 where a = 34;
@@ -893,8 +908,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -904,31 +919,32 @@ PAGE_NUMBER	NUMBER_RECORDS
 3	22
 4	2
 27	11
-28	11
+28	9
+30	11
 delete from tab1 where a = 32;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 31;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=45 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 delete from tab1 where a = 30;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=40 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=45)
 CREATE TABLE tab1 (a bigint primary key, b blob) engine=InnoDB row_format=dynamic;
@@ -989,9 +1005,10 @@ where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	42
-4	2
+4	3
 27	21
-28	21
+28	10
+30	11
 begin;
 delete from tab1 where a = 33;
 delete from tab1 where a = 34;
@@ -1019,8 +1036,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -1030,31 +1047,32 @@ PAGE_NUMBER	NUMBER_RECORDS
 3	22
 4	2
 27	11
-28	11
+28	9
+30	11
 delete from tab1 where a = 32;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 31;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=45 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 30;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=40 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 # test to confirm behavior (MERGE_THRESHOLD=40)
 CREATE TABLE tab1 (a bigint primary key, b blob) engine=InnoDB row_format=dynamic;
@@ -1115,9 +1133,10 @@ where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	42
-4	2
+4	3
 27	21
-28	21
+28	10
+30	11
 begin;
 delete from tab1 where a = 33;
 delete from tab1 where a = 34;
@@ -1145,8 +1164,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -1156,31 +1175,32 @@ PAGE_NUMBER	NUMBER_RECORDS
 3	22
 4	2
 27	11
-28	11
+28	8
+30	11
 delete from tab1 where a = 32;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 31;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=45 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 30;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=40 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 DROP TABLE tab1;
 # compressed table behaves same (MERGE_THRESHOLD=45)
 CREATE TABLE tab1 (a bigint primary key, b blob) engine=InnoDB
@@ -1242,9 +1262,10 @@ where s1.SPACE = s2.SPACE AND NAME like 'test/tab1%'
 and PAGE_TYPE = "INDEX" order by PAGE_NUMBER, NUMBER_RECORDS;
 PAGE_NUMBER	NUMBER_RECORDS
 3	42
-4	2
+4	3
 27	21
-28	21
+28	10
+30	11
 begin;
 delete from tab1 where a = 33;
 delete from tab1 where a = 34;
@@ -1272,8 +1293,8 @@ InnoDB		0 transactions not purged
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 select PAGE_NUMBER, NUMBER_RECORDS
 from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES s1,
 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE s2
@@ -1283,30 +1304,31 @@ PAGE_NUMBER	NUMBER_RECORDS
 3	22
 4	2
 27	11
-28	11
+28	9
+30	11
 delete from tab1 where a = 32;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=50 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	0
-index_page_merge_successful	0
+index_page_merge_attempts	1
+index_page_merge_successful	1
 delete from tab1 where a = 31;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=45 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	1
-index_page_merge_successful	1
+index_page_merge_attempts	2
+index_page_merge_successful	2
 delete from tab1 where a = 30;
 InnoDB		0 transactions not purged
 # check page merge happens (MERGE_THRESHOLD=40 causes merge here)
 SELECT name,count_reset FROM information_schema.innodb_metrics
 WHERE name like 'index_page_merge_%';
 name	count_reset
-index_page_merge_attempts	2
-index_page_merge_successful	2
+index_page_merge_attempts	3
+index_page_merge_successful	3
 DROP TABLE tab1;
 SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
diff --git a/mysql-test/suite/innodb/r/index_tree_operation.result b/mysql-test/suite/innodb/r/index_tree_operation.result
index 29660962e0c..18890d99750 100644
--- a/mysql-test/suite/innodb/r/index_tree_operation.result
+++ b/mysql-test/suite/innodb/r/index_tree_operation.result
@@ -16,9 +16,10 @@ information_schema.innodb_buffer_page s2
 WHERE s1.space = s2.space AND name = 'test/t1'
 AND page_type = "INDEX" ORDER BY page_number;
 page_number	number_records
-3	2
+3	3
 4	3
-5	3
+5	1
+6	2
 INSERT INTO t1 VALUES (999, REPEAT('a', 4096));
 SELECT page_number, number_records
 FROM information_schema.innodb_sys_tablespaces s1,
@@ -28,8 +29,8 @@ AND page_type = "INDEX" ORDER BY page_number;
 page_number	number_records
 3	3
 4	3
-5	3
-6	1
+5	1
+6	3
 INSERT INTO t1 VALUES (998, REPEAT('a', 4096));
 SELECT page_number, number_records
 FROM information_schema.innodb_sys_tablespaces s1,
@@ -37,10 +38,11 @@ information_schema.innodb_buffer_page s2
 WHERE s1.space = s2.space AND name = 'test/t1'
 AND page_type = "INDEX" ORDER BY page_number;
 page_number	number_records
-3	3
+3	4
 4	3
-5	3
-6	2
+5	1
+6	3
+7	1
 INSERT INTO t1 VALUES (997, REPEAT('a', 4096));
 SELECT page_number, number_records
 FROM information_schema.innodb_sys_tablespaces s1,
@@ -48,8 +50,9 @@ information_schema.innodb_buffer_page s2
 WHERE s1.space = s2.space AND name = 'test/t1'
 AND page_type = "INDEX" ORDER BY page_number;
 page_number	number_records
-3	3
+3	4
 4	3
-5	3
+5	1
 6	3
+7	2
 DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result b/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result
index d795b516d5e..f8bf417969e 100644
--- a/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result
+++ b/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result
@@ -13,6 +13,7 @@ c INT,
 INDEX(b))
 ENGINE=InnoDB STATS_PERSISTENT=0;
 SET GLOBAL innodb_change_buffering_debug = 1;
+SET GLOBAL innodb_limit_optimistic_insert_debug=700;
 INSERT INTO t1 SELECT 0,'x',1 FROM seq_1_to_8192;
 BEGIN;
 SELECT b FROM t1 LIMIT 3;
@@ -50,5 +51,5 @@ Table	Op	Msg_type	Msg_text
 test.t1	check	status	OK
 SHOW ENGINE INNODB STATUS;
 Type	Name	Status
-InnoDB		insert 79, delete mark 1
+InnoDB		insert 139, delete mark 1
 DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/innodb-page_compression_default.result b/mysql-test/suite/innodb/r/innodb-page_compression_default.result
index 4610d251fc0..23aa0a28c90 100644
--- a/mysql-test/suite/innodb/r/innodb-page_compression_default.result
+++ b/mysql-test/suite/innodb/r/innodb-page_compression_default.result
@@ -36,7 +36,7 @@ select count(*) from innodb_page_compressed9;
 count(*)
 10000
 # innodb_normal expected FOUND
-FOUND 24084 /AaAaAaAa/ in innodb_normal.ibd
+FOUND 24000 /AaAaAaAa/ in innodb_normal.ibd
 # innodb_page_compressed1 page compressed expected NOT FOUND
 NOT FOUND /AaAaAaAa/ in innodb_page_compressed1.ibd
 # innodb_page_compressed2 page compressed expected NOT FOUND
diff --git a/mysql-test/suite/innodb/r/innodb-page_compression_snappy.result b/mysql-test/suite/innodb/r/innodb-page_compression_snappy.result
index 2f675bcf1b4..ea3b18011df 100644
--- a/mysql-test/suite/innodb/r/innodb-page_compression_snappy.result
+++ b/mysql-test/suite/innodb/r/innodb-page_compression_snappy.result
@@ -37,7 +37,7 @@ select count(*) from innodb_page_compressed9;
 count(*)
 10000
 # innodb_normal expected FOUND
-FOUND 24084 /AaAaAaAa/ in innodb_normal.ibd
+FOUND 24000 /AaAaAaAa/ in innodb_normal.ibd
 # innodb_page_compressed1 page compressed expected NOT FOUND
 NOT FOUND /AaAaAaAa/ in innodb_page_compressed1.ibd
 # innodb_page_compressed2 page compressed expected NOT FOUND
diff --git a/mysql-test/suite/innodb/r/innodb_defragment_fill_factor.result b/mysql-test/suite/innodb/r/innodb_defragment_fill_factor.result
index 8453050a92a..fcd73caf978 100644
--- a/mysql-test/suite/innodb/r/innodb_defragment_fill_factor.result
+++ b/mysql-test/suite/innodb/r/innodb_defragment_fill_factor.result
@@ -115,4 +115,5 @@ insert into t2 values (201, REPEAT('A', 16), REPEAT('B', 32));
 insert into t2 values (202, REPEAT('A', 16), REPEAT('B', 32));
 insert into t2 values (203, REPEAT('A', 16), REPEAT('B', 32));
 insert into t2 values (204, REPEAT('A', 16), REPEAT('B', 32));
+Too little space is reserved on second index.
 DROP TABLE t2;
diff --git a/mysql-test/suite/innodb/r/instant_alter,32k.rdiff b/mysql-test/suite/innodb/r/instant_alter,32k.rdiff
index 37c3c479e68..14a19856be5 100644
--- a/mysql-test/suite/innodb/r/instant_alter,32k.rdiff
+++ b/mysql-test/suite/innodb/r/instant_alter,32k.rdiff
@@ -44,7 +44,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+5
++4
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
@@ -71,7 +71,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+5
++4
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
@@ -98,7 +98,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+5
++4
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
diff --git a/mysql-test/suite/innodb/r/instant_alter,4k.rdiff b/mysql-test/suite/innodb/r/instant_alter,4k.rdiff
index ca93b1d6519..ddd2bbf89ae 100644
--- a/mysql-test/suite/innodb/r/instant_alter,4k.rdiff
+++ b/mysql-test/suite/innodb/r/instant_alter,4k.rdiff
@@ -117,7 +117,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+28
++27
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
diff --git a/mysql-test/suite/innodb/r/instant_alter,8k.rdiff b/mysql-test/suite/innodb/r/instant_alter,8k.rdiff
index b96262866e5..8f4294b56d2 100644
--- a/mysql-test/suite/innodb/r/instant_alter,8k.rdiff
+++ b/mysql-test/suite/innodb/r/instant_alter,8k.rdiff
@@ -41,7 +41,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  ALTER TABLE big ADD COLUMN
  (d1 INT DEFAULT 0, d2 VARCHAR(20) DEFAULT 'abcde',
@@ -50,7 +50,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+13
++12
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
@@ -59,7 +59,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  InnoDB		0 transactions not purged
  DROP TABLE t1,t2,t3,big;
@@ -104,7 +104,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  ALTER TABLE big ADD COLUMN
  (d1 INT DEFAULT 0, d2 VARCHAR(20) DEFAULT 'abcde',
@@ -113,7 +113,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+13
++12
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
@@ -122,7 +122,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  InnoDB		0 transactions not purged
  DROP TABLE t1,t2,t3,big;
@@ -167,7 +167,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  ALTER TABLE big ADD COLUMN
  (d1 INT DEFAULT 0, d2 VARCHAR(20) DEFAULT 'abcde',
@@ -176,7 +176,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -7
-+13
++12
  connection default;
  ROLLBACK;
  CHECKSUM TABLE big;
@@ -185,7 +185,7 @@
  WHERE name = 'test/big';
  clust_index_size
 -3
-+5
++4
  connection default;
  InnoDB		0 transactions not purged
  DROP TABLE t1,t2,t3,big;
diff --git a/mysql-test/suite/innodb/r/leaf_page_corrupted_during_recovery.result b/mysql-test/suite/innodb/r/leaf_page_corrupted_during_recovery.result
index 37ddb0a9348..6e9cf71925a 100644
--- a/mysql-test/suite/innodb/r/leaf_page_corrupted_during_recovery.result
+++ b/mysql-test/suite/innodb/r/leaf_page_corrupted_during_recovery.result
@@ -4,17 +4,23 @@ INSERT INTO t1 VALUES(1, 'sql'), (2, 'server'), (3, 'mariadb'),
 (4, 'mariadb'), (5, 'test1'), (6, 'test2'), (7, 'test3'),
 (8, 'test4'), (9, 'test5'), (10, 'test6'), (11, 'test7'),
 (12, 'test8');
+FLUSH TABLE t1 FOR EXPORT;
+UNLOCK TABLES;
+SET GLOBAL innodb_log_checkpoint_now=ON;
 SELECT COUNT(*) FROM t1;
 COUNT(*)
 12
 UPDATE t1 SET c='best8' WHERE pk=12;
 # Kill the server
 # Corrupt the pages
+# restart
 SELECT * FROM t1 WHERE PK = 1;
 ERROR 42000: Unknown storage engine 'InnoDB'
+# restart: --innodb-force-recovery=1
 SELECT * FROM t1 WHERE PK = 1;
 pk	c
 1	sql
 SELECT * FROM t1 WHERE pk = 12;
-ERROR HY000: Index for table 't1' is corrupt; try to repair it
+ERROR HY000: Got error 168 "Unknown (generic) error from engine" from storage engine InnoDB
 DROP TABLE t1;
+# restart
diff --git a/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test b/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test
index 79d9cc814a0..9c5acedb620 100644
--- a/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test
+++ b/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test
@@ -35,9 +35,16 @@ ENGINE=InnoDB STATS_PERSISTENT=0;
 SET GLOBAL innodb_change_buffering_debug = 1;
 let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err;
 
+# The removed function page_copy_rec_list_end_to_created_page() would create
+# less dense pages than the remaining part of page_copy_rec_list_end().
+# For the INSERT after the DELETE to be buffered, the leftmost page
+# must not be too densely packed.
+SET GLOBAL innodb_limit_optimistic_insert_debug=700;
+
 # Create enough rows for the table, so that the change buffer will be
 # used for modifying the secondary index page. There must be multiple
 # index pages, because changes to the root page are never buffered.
+
 INSERT INTO t1 SELECT 0,'x',1 FROM seq_1_to_8192;
 
 BEGIN;
diff --git a/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test b/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test
index bd8784236b6..6e080078832 100644
--- a/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test
+++ b/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test
@@ -3,8 +3,8 @@
 
 --disable_query_log
 call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed file read of tablespace test/t1 page ");
-call mtr.add_suppression("InnoDB: Background Page read failed to read or decrypt \\[page id: space=\\d+, page number=19\\]");
-call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to read file '.*test.t1\\.ibd' at offset 19: Page read from tablespace is corrupted\\.");
+call mtr.add_suppression("InnoDB: Background Page read failed to read or decrypt \\[page id: space=\\d+, page number=14\\]");
+call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to read file '.*test.t1\\.ibd' at offset 14: Page read from tablespace is corrupted\\.");
 call mtr.add_suppression("\\[ERROR\\] InnoDB: Plugin initialization aborted at srv0start\\.cc.* with error Data structure corruption");
 call mtr.add_suppression("\\[ERROR\\] Plugin 'InnoDB' (init function|registration)");
 call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption");
@@ -20,8 +20,12 @@ INSERT INTO t1 VALUES(1, 'sql'), (2, 'server'), (3, 'mariadb'),
 	(8, 'test4'), (9, 'test5'), (10, 'test6'), (11, 'test7'),
 	(12, 'test8');
 
-let $restart_noprint=2;
---source include/restart_mysqld.inc
+# Flush all pages of the table, and perform log checkpoint,
+# so that no page initialization can be replayed from the redo log
+# to undo the page corruption that we are going to inject.
+FLUSH TABLE t1 FOR EXPORT;
+UNLOCK TABLES;
+SET GLOBAL innodb_log_checkpoint_now=ON;
 
 let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
 let MYSQLD_DATADIR=`select @@datadir`;
@@ -38,7 +42,7 @@ perl;
 my $file = "$ENV{MYSQLD_DATADIR}/test/t1.ibd";
 open(FILE, "+<$file") || die "Unable to open $file";
 binmode FILE;
-seek (FILE, $ENV{INNODB_PAGE_SIZE} * 19 + 38, SEEK_SET) or die "seek";
+seek (FILE, $ENV{INNODB_PAGE_SIZE} * 14 + 38, SEEK_SET) or die "seek";
 print FILE "junk";
 close FILE or die "close";
 EOF
@@ -50,7 +54,7 @@ SELECT * FROM t1 WHERE PK = 1;
 let $restart_parameters=--innodb-force-recovery=1;
 --source include/restart_mysqld.inc
 SELECT * FROM t1 WHERE PK = 1;
---error ER_NOT_KEYFILE
+--error ER_GET_ERRNO
 SELECT * FROM t1 WHERE pk = 12;
 
 DROP TABLE t1;
diff --git a/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result b/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result
index 3b98527250b..d7dc9a745f7 100644
--- a/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result
+++ b/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result
@@ -962,7 +962,7 @@ AND compress_ops BETWEEN @inl_val AND 1000
 AND table_name='tab5' AND database_name='test'
 AND index_name like 'idx%' ;
 compress_stat	1
-The size of the tab5.ibd file: 163840
+The size of the tab5.ibd file: 159744
 # fetch the compressed page and check the stats
 ===============
 Fetch Records
@@ -986,7 +986,7 @@ AND compress_ops BETWEEN @inl_val AND 1000
 AND table_name='tab5' AND database_name='test'
 AND index_name like 'idx%' ;
 compress_stat	1
-The size of the tab5.ibd file: 163840
+The size of the tab5.ibd file: 159744
 # fetch the compressed same page once again and check the stats
 # the stat figures should be same as above query
 ===============
@@ -1011,7 +1011,7 @@ AND compress_ops BETWEEN @inl_val AND 1000
 AND table_name='tab5' AND database_name='test'
 AND index_name like 'idx%' ;
 compress_stat	1
-The size of the tab5.ibd file: 163840
+The size of the tab5.ibd file: 159744
 DROP TABLE tab5;
 #******************************************************************
 # Test 1-8K: innodb_cmp_per_index_enabled=ON and innodb_compression_level=0 with page size 8K
diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc
index d0bccd11688..de3ea753215 100644
--- a/storage/innobase/btr/btr0bulk.cc
+++ b/storage/innobase/btr/btr0bulk.cc
@@ -98,7 +98,7 @@ PageBulk::init()
 			page_zip_write_header(new_page_zip, index_id,
 					      8, &m_mtr);
 		} else {
-			ut_ad(!dict_index_is_spatial(m_index));
+			ut_ad(!m_index->is_spatial());
 			page_create(new_block, &m_mtr,
 				    m_index->table->not_redundant(),
 				    false);
@@ -153,9 +153,6 @@ PageBulk::init()
 	m_rec_no = page_header_get_field(new_page, PAGE_N_RECS);
 
 	ut_d(m_total_data = 0);
-	/* See page_copy_rec_list_end_to_created_page() */
-	ut_d(page_header_set_field(m_page, NULL, PAGE_HEAP_TOP,
-				   srv_page_size - 1));
 
 	return(DB_SUCCESS);
 }
@@ -178,7 +175,7 @@ inline void PageBulk::insertPage(const rec_t *rec, offset_t *offsets)
 
 #ifdef UNIV_DEBUG
 	/* Check whether records are in order. */
-	if (!page_rec_is_infimum(m_cur_rec)) {
+	if (!page_rec_is_infimum_low(page_offset(m_cur_rec))) {
 		rec_t*	old_rec = m_cur_rec;
 		offset_t* old_offsets = rec_get_offsets(
 			old_rec, m_index, NULL,	is_leaf,
@@ -193,21 +190,26 @@ inline void PageBulk::insertPage(const rec_t *rec, offset_t *offsets)
 
 	/* 1. Copy the record to page. */
 	rec_t*	insert_rec = rec_copy(m_heap_top, rec, offsets);
+	ut_ad(page_align(insert_rec) == m_page);
 	rec_offs_make_valid(insert_rec, m_index, is_leaf, offsets);
 
 	/* 2. Insert the record in the linked list. */
-	rec_t*	next_rec = page_rec_get_next(m_cur_rec);
-
-	page_rec_set_next(insert_rec, next_rec);
-	page_rec_set_next(m_cur_rec, insert_rec);
-
-	/* 3. Set the n_owned field in the inserted record to zero,
-	and set the heap_no field. */
 	if (fmt != REDUNDANT) {
+		rec_t* next_rec = m_page
+			+ page_offset(m_cur_rec
+				      + mach_read_from_2(m_cur_rec
+							 - REC_NEXT));
+		mach_write_to_2(insert_rec - REC_NEXT,
+				static_cast<uint16_t>(next_rec - insert_rec));
+		mach_write_to_2(m_cur_rec - REC_NEXT,
+				static_cast<uint16_t>(insert_rec - m_cur_rec));
 		rec_set_n_owned_new(insert_rec, NULL, 0);
 		rec_set_heap_no_new(insert_rec,
 				    PAGE_HEAP_NO_USER_LOW + m_rec_no);
 	} else {
+		mach_write_to_2(insert_rec - REC_NEXT,
+				mach_read_from_2(m_cur_rec - REC_NEXT));
+		mach_write_to_2(m_cur_rec - REC_NEXT, page_offset(insert_rec));
 		rec_set_n_owned_old(insert_rec, 0);
 		rec_set_heap_no_old(insert_rec,
 				    PAGE_HEAP_NO_USER_LOW + m_rec_no);
@@ -225,7 +227,7 @@ inline void PageBulk::insertPage(const rec_t *rec, offset_t *offsets)
 	m_heap_top += rec_size;
 	m_rec_no += 1;
 
-	if (!m_page_zip) {
+	if (fmt != COMPRESSED) {
 		/* For ROW_FORMAT=COMPRESSED, redo log may be written
 		in PageBulk::compress(). */
 		page_cur_insert_rec_write_log(insert_rec, rec_size,
@@ -250,110 +252,137 @@ inline void PageBulk::insert(const rec_t *rec, offset_t *offsets)
 
 /** Mark end of insertion to the page. Scan all records to set page dirs,
 and set page header members.
-@tparam fmt     the page format */
+@tparam fmt  page format */
 template<PageBulk::format fmt>
 inline void PageBulk::finishPage()
 {
-	ut_ad(m_rec_no > 0);
-	ut_ad((m_page_zip != nullptr) == (fmt == COMPRESSED));
-	ut_ad((fmt != REDUNDANT) == m_is_comp);
-	ut_ad(m_total_data + page_dir_calc_reserved_space(m_rec_no)
-	      <= page_get_free_space_of_empty(fmt != REDUNDANT));
-	/* See page_copy_rec_list_end_to_created_page() */
-	ut_d(page_dir_set_n_slots(m_page, NULL, srv_page_size / 2));
+  ut_ad(m_rec_no > 0);
+  ut_ad((m_page_zip != nullptr) == (fmt == COMPRESSED));
+  ut_ad((fmt != REDUNDANT) == m_is_comp);
 
-	ulint	count = 0;
-	ulint	n_recs = 0;
-	ulint	slot_index = 0;
-	rec_t*	insert_rec = page_rec_get_next(page_get_infimum_rec(m_page));
-	page_dir_slot_t* slot = NULL;
+  ulint count= 0;
+  ulint n_recs= 0;
+  byte *slot= my_assume_aligned<2>(m_page + srv_page_size -
+                                   (PAGE_DIR + PAGE_DIR_SLOT_SIZE));
+  const page_dir_slot_t *const slot0 = slot;
+  compile_time_assert(PAGE_DIR_SLOT_SIZE == 2);
+  if (fmt != REDUNDANT)
+  {
+    uint16_t offset= mach_read_from_2(PAGE_NEW_INFIMUM - REC_NEXT + m_page);
+    ut_ad(offset >= PAGE_NEW_SUPREMUM - PAGE_NEW_INFIMUM);
+    offset += PAGE_NEW_INFIMUM;
+    /* Set owner & dir. */
+    do
+    {
+      ut_ad(offset >= PAGE_NEW_SUPREMUM);
+      ut_ad(offset < page_offset(slot));
+      count++;
+      n_recs++;
 
-	/* Set owner & dir. */
-	do {
+      if (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2)
+      {
+        slot-= PAGE_DIR_SLOT_SIZE;
+        mach_write_to_2(slot, offset);
+        rec_set_n_owned_new(m_page + offset, nullptr, count);
+        count = 0;
+      }
 
-		count++;
-		n_recs++;
+      uint16_t next= (mach_read_from_2(m_page + offset - REC_NEXT) + offset) &
+        (srv_page_size - 1);
+      ut_ad(next);
+      offset= next;
+    }
+    while (offset != PAGE_NEW_SUPREMUM);
+  }
+  else
+  {
+    rec_t *insert_rec= m_page +
+      mach_read_from_2(PAGE_OLD_INFIMUM - REC_NEXT + m_page);
 
-		if (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2) {
+    /* Set owner & dir. */
+    do
+    {
+      count++;
+      n_recs++;
 
-			slot_index++;
+      if (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2)
+      {
+        slot-= PAGE_DIR_SLOT_SIZE;
+        mach_write_to_2(slot, page_offset(insert_rec));
+        rec_set_n_owned_old(insert_rec, count);
 
-			slot = page_dir_get_nth_slot(m_page, slot_index);
+        count= 0;
+      }
 
-			page_dir_slot_set_rec(slot, insert_rec);
-			page_dir_slot_set_n_owned(slot, NULL, count);
+      insert_rec= m_page + mach_read_from_2(insert_rec - REC_NEXT);
+    }
+    while (insert_rec != m_page + PAGE_OLD_SUPREMUM);
+  }
 
-			count = 0;
-		}
+  if (slot0 != slot && (count + 1 + (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2 <=
+                        PAGE_DIR_SLOT_MAX_N_OWNED)) {
+    /* We can merge the two last dir slots. This operation is here to
+    make this function imitate exactly the equivalent task made using
+    page_cur_insert_rec(), which we use in database recovery to
+    reproduce the task performed by this function. To be able to
+    check the correctness of recovery, it is good that it imitates exactly. */
 
-		insert_rec = page_rec_get_next(insert_rec);
-	} while (!page_rec_is_supremum(insert_rec));
+    count+= (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2;
 
-	if (slot_index > 0
-	    && (count + 1 + (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2
-		<= PAGE_DIR_SLOT_MAX_N_OWNED)) {
-		/* We can merge the two last dir slots. This operation is
-		here to make this function imitate exactly the equivalent
-		task made using page_cur_insert_rec, which we use in database
-		recovery to reproduce the task performed by this function.
-		To be able to check the correctness of recovery, it is good
-		that it imitates exactly. */
+    page_dir_slot_set_n_owned(slot, nullptr, 0);
+    slot+= PAGE_DIR_SLOT_SIZE;
+  }
 
-		count += (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2;
+  slot-= PAGE_DIR_SLOT_SIZE;
+  page_dir_slot_set_rec(slot, page_get_supremum_rec(m_page));
+  page_dir_slot_set_n_owned(slot, nullptr, count + 1);
 
-		page_dir_slot_set_n_owned(slot, NULL, 0);
+  ut_ad(!dict_index_is_spatial(m_index));
+  ut_ad(!page_get_instant(m_page));
+  ut_ad(!mach_read_from_2(PAGE_HEADER + PAGE_N_DIRECTION + m_page));
 
-		slot_index--;
-	}
+  if (fmt != COMPRESSED)
+  {
+    m_mtr.write<2,mtr_t::OPT>(*m_block,
+                              PAGE_HEADER + PAGE_N_DIR_SLOTS + m_page,
+                              1 + static_cast<ulint>(slot0 - slot) /
+                              PAGE_DIR_SLOT_SIZE);
+    m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_HEAP_TOP + m_page,
+                   static_cast<ulint>(m_heap_top - m_page));
+    m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_N_HEAP + m_page,
+                   (PAGE_HEAP_NO_USER_LOW + m_rec_no) |
+                   uint16_t{fmt != REDUNDANT} << 15);
+    m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_N_RECS + m_page, m_rec_no);
+    m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_LAST_INSERT + m_page,
+                   static_cast<ulint>(m_cur_rec - m_page));
+    m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_DIRECTION_B - 1 + m_page,
+                   PAGE_RIGHT);
+  }
+  else
+  {
+    /* For ROW_FORMAT=COMPRESSED, redo log may be written in
+    PageBulk::compress(). */
+    mach_write_to_2(PAGE_HEADER + PAGE_N_DIR_SLOTS + m_page,
+                    1 + (slot0 - slot) / PAGE_DIR_SLOT_SIZE);
+    mach_write_to_2(PAGE_HEADER + PAGE_HEAP_TOP + m_page,
+                    static_cast<ulint>(m_heap_top - m_page));
+    mach_write_to_2(PAGE_HEADER + PAGE_N_HEAP + m_page,
+                    (PAGE_HEAP_NO_USER_LOW + m_rec_no) |
+                    uint16_t{fmt != REDUNDANT} << 15);
+    mach_write_to_2(PAGE_HEADER + PAGE_N_RECS + m_page, m_rec_no);
+    mach_write_to_2(PAGE_HEADER + PAGE_LAST_INSERT + m_page,
+                    static_cast<ulint>(m_cur_rec - m_page));
+    mach_write_to_2(PAGE_HEADER + PAGE_DIRECTION_B - 1 + m_page, PAGE_RIGHT);
+  }
 
-	slot = page_dir_get_nth_slot(m_page, 1 + slot_index);
-	page_dir_slot_set_rec(slot, page_get_supremum_rec(m_page));
-	page_dir_slot_set_n_owned(slot, NULL, count + 1);
-
-	ut_ad(!dict_index_is_spatial(m_index));
-	ut_ad(!page_get_instant(m_page));
-	ut_ad(!mach_read_from_2(PAGE_HEADER + PAGE_N_DIRECTION + m_page));
-
-	if (fmt != COMPRESSED) {
-		m_mtr.write<2,mtr_t::OPT>(*m_block,
-					  PAGE_HEADER + PAGE_N_DIR_SLOTS
-					  + m_page, 2 + slot_index);
-		m_mtr.write<2>(*m_block, PAGE_HEADER + PAGE_HEAP_TOP + m_page,
-			       ulint(m_heap_top - m_page));
-		m_mtr.write<2>(*m_block,
-			       PAGE_HEADER + PAGE_N_HEAP + m_page,
-			       (PAGE_HEAP_NO_USER_LOW + m_rec_no)
-			       | uint16_t{fmt != REDUNDANT} << 15);
-		m_mtr.write<2>(*m_block,
-			       PAGE_HEADER + PAGE_N_RECS + m_page, m_rec_no);
-		m_mtr.write<2>(*m_block,
-			       PAGE_HEADER + PAGE_LAST_INSERT + m_page,
-			       ulint(m_cur_rec - m_page));
-		m_mtr.write<2>(*m_block,
-			       PAGE_HEADER + PAGE_DIRECTION_B - 1 + m_page,
-			       PAGE_RIGHT);
-	} else {
-		/* For ROW_FORMAT=COMPRESSED, redo log may be written
-		in PageBulk::compress(). */
-		mach_write_to_2(PAGE_HEADER + PAGE_N_DIR_SLOTS + m_page,
-				2 + slot_index);
-		mach_write_to_2(PAGE_HEADER + PAGE_HEAP_TOP + m_page,
-				ulint(m_heap_top - m_page));
-		mach_write_to_2(PAGE_HEADER + PAGE_N_HEAP + m_page,
-				(PAGE_HEAP_NO_USER_LOW + m_rec_no)
-				| uint16_t{fmt != REDUNDANT} << 15);
-		mach_write_to_2(PAGE_HEADER + PAGE_N_RECS + m_page, m_rec_no);
-		mach_write_to_2(PAGE_HEADER + PAGE_LAST_INSERT + m_page,
-				ulint(m_cur_rec - m_page));
-		mach_write_to_2(PAGE_HEADER + PAGE_DIRECTION_B - 1 + m_page,
-				PAGE_RIGHT);
-	}
-
-	m_block->skip_flush_check = false;
+  ut_ad(m_total_data + page_dir_calc_reserved_space(m_rec_no) <=
+        page_get_free_space_of_empty(m_is_comp));
+  m_block->skip_flush_check= false;
 }
 
 /** Mark end of insertion to the page. Scan all records to set page dirs,
-and set page header members. */
+and set page header members.
+@tparam compressed  whether the page is in ROW_FORMAT=COMPRESSED */
 inline void PageBulk::finish()
 {
   if (UNIV_LIKELY_NULL(m_page_zip))
diff --git a/storage/innobase/include/page0cur.h b/storage/innobase/include/page0cur.h
index ad5bee18606..fd1f38538bb 100644
--- a/storage/innobase/include/page0cur.h
+++ b/storage/innobase/include/page0cur.h
@@ -217,6 +217,7 @@ IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
 if this is a compressed leaf page in a secondary index.
 This has to be done either within the same mini-transaction,
 or by invoking ibuf_reset_free_bits() before mtr_commit(). */
+ATTRIBUTE_COLD /* only used when crash-upgrading */
 void
 page_copy_rec_list_end_to_created_page(
 /*===================================*/
diff --git a/storage/innobase/page/page0cur.cc b/storage/innobase/page/page0cur.cc
index e8476f98600..727ab63da39 100644
--- a/storage/innobase/page/page0cur.cc
+++ b/storage/innobase/page/page0cur.cc
@@ -2041,6 +2041,7 @@ IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
 if this is a compressed leaf page in a secondary index.
 This has to be done either within the same mini-transaction,
 or by invoking ibuf_reset_free_bits() before mtr_commit(). */
+ATTRIBUTE_COLD /* only used when crash-upgrading */
 void
 page_copy_rec_list_end_to_created_page(
 /*===================================*/
diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc
index f303f742ab3..4819ec95932 100644
--- a/storage/innobase/page/page0page.cc
+++ b/storage/innobase/page/page0page.cc
@@ -599,37 +599,30 @@ page_copy_rec_list_end(
 	/* Here, "ret" may be pointing to a user record or the
 	predefined supremum record. */
 
-	mtr_log_t	log_mode = MTR_LOG_NONE;
+	const mtr_log_t log_mode = new_page_zip
+		? mtr->set_log_mode(MTR_LOG_NONE) : MTR_LOG_NONE;
+	ut_d(const bool was_empty = page_dir_get_n_heap(new_page)
+	     == PAGE_HEAP_NO_USER_LOW);
 
-	if (new_page_zip) {
-		log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
-	}
+	if (index->is_spatial()) {
+		ulint	max_to_move = page_get_n_recs(
+			buf_block_get_frame(block));
+		heap = mem_heap_create(256);
 
-	if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) {
-		page_copy_rec_list_end_to_created_page(new_page, rec,
-						       index, mtr);
+		rec_move = static_cast<rtr_rec_move_t*>(
+			mem_heap_alloc(heap, max_to_move * sizeof *rec_move));
+
+		/* For spatial index, we need to insert recs one by one
+		to keep recs ordered. */
+		rtr_page_copy_rec_list_end_no_locks(new_block,
+						    block, rec, index,
+						    heap, rec_move,
+						    max_to_move,
+						    &num_moved,
+						    mtr);
 	} else {
-		if (dict_index_is_spatial(index)) {
-			ulint	max_to_move = page_get_n_recs(
-						buf_block_get_frame(block));
-			heap = mem_heap_create(256);
-
-			rec_move = static_cast<rtr_rec_move_t*>(mem_heap_alloc(
-					heap,
-					sizeof (*rec_move) * max_to_move));
-
-			/* For spatial index, we need to insert recs one by one
-			to keep recs ordered. */
-			rtr_page_copy_rec_list_end_no_locks(new_block,
-							    block, rec, index,
-							    heap, rec_move,
-							    max_to_move,
-							    &num_moved,
-							    mtr);
-		} else {
-			page_copy_rec_list_end_no_locks(new_block, block, rec,
-							index, mtr);
-		}
+		page_copy_rec_list_end_no_locks(new_block, block, rec,
+						index, mtr);
 	}
 
 	/* Update PAGE_MAX_TRX_ID on the uncompressed page.
@@ -642,6 +635,9 @@ page_copy_rec_list_end(
 	if (dict_index_is_sec_or_ibuf(index)
 	    && page_is_leaf(page)
 	    && !index->table->is_temporary()) {
+		ut_ad(!was_empty || page_dir_get_n_heap(new_page)
+		      == PAGE_HEAP_NO_USER_LOW
+		      + page_header_get_field(new_page, PAGE_N_RECS));
 		page_update_max_trx_id(new_block, NULL,
 				       page_get_max_trx_id(page), mtr);
 	}