Commit 326d75bd authored by Marko Mäkelä's avatar Marko Mäkelä

Merge a contribution from Ryan Mack at Facebook:

Bugfix for 53290, fast unique index creation fails on duplicate null values

    Summary:
    Bug in the fast index creation code incorrectly considers null
    values to be duplicates during block merging.  Innodb policy is that
    multiple null values are allowed in a unique index.  Null duplicates
    were correctly ignored while sorting individual blocks and with slow
    index creation.

    Test Plan:
    mtr, including new test, load dbs using deferred index creation

    DiffCamp Revision: 110840
    Reviewed By: mcallaghan
    CC: mcallaghan, mysql-devel@lists
    Revert Plan:
    OK
parent fa2c00d3
create table bug53290 (x bigint) engine=innodb;
insert into bug53290 () values (),(),(),(),(),(),(),(),(),(),(),();
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
alter table bug53290 add unique index `idx` (x);
drop table bug53290;
-- source include/have_innodb_plugin.inc
create table bug53290 (x bigint) engine=innodb;
insert into bug53290 () values (),(),(),(),(),(),(),(),(),(),(),();
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
insert into bug53290 select * from bug53290;
alter table bug53290 add unique index `idx` (x);
drop table bug53290;
...@@ -148,7 +148,9 @@ cmp_rec_rec_simple( ...@@ -148,7 +148,9 @@ cmp_rec_rec_simple(
const rec_t* rec2, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */
const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */
const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */
const dict_index_t* index); /*!< in: data dictionary index */ const dict_index_t* index, /*!< in: data dictionary index */
ibool* null_eq);/*!< out: set to TRUE if
found matching null values */
/*************************************************************//** /*************************************************************//**
This function is used to compare two physical records. Only the common This function is used to compare two physical records. Only the common
first fields are compared, and if an externally stored field is first fields are compared, and if an externally stored field is
......
...@@ -706,7 +706,9 @@ cmp_rec_rec_simple( ...@@ -706,7 +706,9 @@ cmp_rec_rec_simple(
const rec_t* rec2, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */
const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */
const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */
const dict_index_t* index) /*!< in: data dictionary index */ const dict_index_t* index, /*!< in: data dictionary index */
ibool* null_eq)/*!< out: set to TRUE if
found matching null values */
{ {
ulint rec1_f_len; /*!< length of current field in rec1 */ ulint rec1_f_len; /*!< length of current field in rec1 */
const byte* rec1_b_ptr; /*!< pointer to the current byte const byte* rec1_b_ptr; /*!< pointer to the current byte
...@@ -753,6 +755,9 @@ cmp_rec_rec_simple( ...@@ -753,6 +755,9 @@ cmp_rec_rec_simple(
|| rec2_f_len == UNIV_SQL_NULL) { || rec2_f_len == UNIV_SQL_NULL) {
if (rec1_f_len == rec2_f_len) { if (rec1_f_len == rec2_f_len) {
if (null_eq) {
*null_eq = TRUE;
}
goto next_field; goto next_field;
......
...@@ -1075,11 +1075,14 @@ row_merge_cmp( ...@@ -1075,11 +1075,14 @@ row_merge_cmp(
record to be compared */ record to be compared */
const ulint* offsets1, /*!< in: first record offsets */ const ulint* offsets1, /*!< in: first record offsets */
const ulint* offsets2, /*!< in: second record offsets */ const ulint* offsets2, /*!< in: second record offsets */
const dict_index_t* index) /*!< in: index */ const dict_index_t* index, /*!< in: index */
ibool* null_eq) /*!< out: set to TRUE if
found matching null values */
{ {
int cmp; int cmp;
cmp = cmp_rec_rec_simple(mrec1, mrec2, offsets1, offsets2, index); cmp = cmp_rec_rec_simple(mrec1, mrec2, offsets1, offsets2, index,
null_eq);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (row_merge_print_cmp) { if (row_merge_print_cmp) {
...@@ -1445,11 +1448,13 @@ corrupt: ...@@ -1445,11 +1448,13 @@ corrupt:
} }
while (mrec0 && mrec1) { while (mrec0 && mrec1) {
ibool null_eq = FALSE;
switch (row_merge_cmp(mrec0, mrec1, switch (row_merge_cmp(mrec0, mrec1,
offsets0, offsets1, index)) { offsets0, offsets1, index,
&null_eq)) {
case 0: case 0:
if (UNIV_UNLIKELY if (UNIV_UNLIKELY
(dict_index_is_unique(index))) { (dict_index_is_unique(index) && !null_eq)) {
innobase_rec_to_mysql(table, mrec0, innobase_rec_to_mysql(table, mrec0,
index, offsets0); index, offsets0);
mem_heap_free(heap); mem_heap_free(heap);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment