1 --- a/storage/innobase/row/row0merge.c
2 +++ b/storage/innobase/row/row0merge.c
3 @@ -1607,22 +1607,28 @@
4 const dict_index_t* index, /*!< in: index being created */
5 merge_file_t* file, /*!< in/out: file containing
7 - ulint* half, /*!< in/out: half the file */
8 row_merge_block_t* block, /*!< in/out: 3 buffers */
9 int* tmpfd, /*!< in/out: temporary file handle */
10 - struct TABLE* table) /*!< in/out: MySQL table, for
11 + struct TABLE* table, /*!< in/out: MySQL table, for
12 reporting erroneous key value
14 + ulint* num_run,/*!< in/out: Number of runs remain
16 + ulint* run_offset) /*!< in/out: Array contains the
17 + first offset number for each merge
20 ulint foffs0; /*!< first input offset */
21 ulint foffs1; /*!< second input offset */
22 ulint error; /*!< error code */
23 merge_file_t of; /*!< output file */
24 - const ulint ihalf = *half;
25 + const ulint ihalf = run_offset[*num_run / 2];
26 /*!< half the input file */
27 - ulint ohalf; /*!< half the output file */
29 + /*!< num of runs generated from this merge */
31 UNIV_MEM_ASSERT_W(block[0], 3 * sizeof block[0]);
33 ut_ad(ihalf < file->offset);
36 @@ -1638,17 +1644,20 @@
37 #endif /* POSIX_FADV_SEQUENTIAL */
39 /* Merge blocks to the output file. */
44 + UNIV_MEM_INVALID(run_offset, *num_run * sizeof *run_offset);
46 for (; foffs0 < ihalf && foffs1 < file->offset; foffs0++, foffs1++) {
47 - ulint ahalf; /*!< arithmetic half the input file */
49 if (UNIV_UNLIKELY(trx_is_interrupted(trx))) {
50 return(DB_INTERRUPTED);
53 + /* Remember the offset number for this run */
54 + run_offset[n_run++] = of.offset;
56 error = row_merge_blocks(index, file, block,
57 &foffs0, &foffs1, &of, table);
59 @@ -1656,21 +1665,6 @@
63 - /* Record the offset of the output file when
64 - approximately half the output has been generated. In
65 - this way, the next invocation of row_merge() will
66 - spend most of the time in this loop. The initial
67 - estimate is ohalf==0. */
68 - ahalf = file->offset / 2;
69 - ut_ad(ohalf <= of.offset);
71 - /* Improve the estimate until reaching half the input
72 - file size, or we can not get any closer to it. All
73 - comparands should be non-negative when !(ohalf < ahalf)
74 - because ohalf <= of.offset. */
75 - if (ohalf < ahalf || of.offset - ahalf < ohalf - ahalf) {
80 /* Copy the last blocks, if there are any. */
82 return(DB_INTERRUPTED);
85 + /* Remember the offset number for this run */
86 + run_offset[n_run++] = of.offset;
88 if (!row_merge_blocks_copy(index, file, block, &foffs0, &of)) {
89 return(DB_CORRUPTION);
92 return(DB_INTERRUPTED);
95 + /* Remember the offset number for this run */
96 + run_offset[n_run++] = of.offset;
98 if (!row_merge_blocks_copy(index, file, block, &foffs1, &of)) {
99 return(DB_CORRUPTION);
101 @@ -1703,10 +1703,23 @@
102 return(DB_CORRUPTION);
105 + ut_ad(n_run <= *num_run);
109 + /* Each run can contain one or more offsets. As merge goes on,
110 + the number of runs (to merge) will reduce until we have one
111 + single run. So the number of runs will always be smaller than
112 + the number of offsets in file */
113 + ut_ad((*num_run) <= file->offset);
115 + /* The number of offsets in output file is always equal or
116 + smaller than input file */
117 + ut_ad(of.offset <= file->offset);
119 /* Swap file descriptors for the next pass. */
124 UNIV_MEM_INVALID(block[0], 3 * sizeof block[0]);
126 @@ -1731,27 +1744,44 @@
129 ulint half = file->offset / 2;
132 + ulint error = DB_SUCCESS;
134 + /* Record the number of merge runs we need to perform */
135 + num_runs = file->offset;
137 + /* If num_runs are less than 1, nothing to merge */
138 + if (num_runs <= 1) {
142 + /* "run_offset" records each run's first offset number */
143 + run_offset = (ulint*) mem_alloc(file->offset * sizeof(ulint));
145 + /* This tells row_merge() where to start for the first round
147 + run_offset[half] = half;
149 /* The file should always contain at least one byte (the end
150 of file marker). Thus, it must be at least one block. */
151 ut_ad(file->offset > 0);
153 + /* Merge the runs until we have one big run */
156 + error = row_merge(trx, index, file, block, tmpfd,
157 + table, &num_runs, run_offset);
159 - error = row_merge(trx, index, file, &half,
160 - block, tmpfd, table);
161 + UNIV_MEM_ASSERT_RW(run_offset, num_runs * sizeof *run_offset);
163 if (error != DB_SUCCESS) {
167 + } while (num_runs > 1);
169 - /* half > 0 should hold except when the file consists
170 - of one block. No need to merge further then. */
171 - ut_ad(half > 0 || file->offset == 1);
172 - } while (half < file->offset && half > 0);
173 + mem_free(run_offset);
175 - return(DB_SUCCESS);
179 /*************************************************************//**
181 +++ b/mysql-test/suite/innodb/r/bug54330.result
183 +DROP TABLE IF EXISTS t1;
185 +id BIGINT(20) AUTO_INCREMENT PRIMARY KEY,
188 +SELECT COUNT(*) FROM t1;
191 +ALTER TABLE t1 ADD INDEX baz (bar);
192 +SELECT COUNT(*) FROM t1 FORCE INDEX (baz);
197 +++ b/mysql-test/suite/innodb/t/bug54330.test
199 +# Testcase for MySQL bug #54330 - broken fast index creation
202 +DROP TABLE IF EXISTS t1;
206 + id BIGINT(20) AUTO_INCREMENT PRIMARY KEY,
211 +SET @old_autocommit=@@AUTOCOMMIT;
216 + eval INSERT INTO t1 (bar) VALUES (NULL);
222 + eval INSERT INTO t1 (bar) VALUES ($1);
226 +SET AUTOCOMMIT=@old_autocommit;
229 +SELECT COUNT(*) FROM t1;
231 +ALTER TABLE t1 ADD INDEX baz (bar);
233 +# With the bug present this will differ from the SELECT above!
234 +SELECT COUNT(*) FROM t1 FORCE INDEX (baz);