Commit 5dfe4d50 authored by marko's avatar marko

branches/zip: Fix a bug in the retrieval of old versions of records containing

externally stored columns.

innodb-zip.test: Correct the test case.  Without the fixes, the test
would fail, because the BLOB would be prepended with a 768-byte prefix
of the data.

row_upd_index_replace_new_col_vals_index_pos(),
row_upd_index_replace_new_col_vals(): Use only one "heap"
parameter that must be non-NULL.  When fetching externally
stored columns, use upd_field_t::orig_len.

upd_get_field_by_field_no(): New accessor function, for retrieving
an field from an update vector by field_no.

row_upd_index_replace_new_col_val(): New function, for replacing the
value from an update vector.  This used to be duplicated code in
row_upd_index_replace_new_col_vals_index_pos() and
row_upd_index_replace_new_col_vals().
parent aacaca62
...@@ -1867,7 +1867,7 @@ any_extern: ...@@ -1867,7 +1867,7 @@ any_extern:
corresponding to new_entry is latched in mtr. corresponding to new_entry is latched in mtr.
Thus the following call is safe. */ Thus the following call is safe. */
row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update, row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update,
FALSE, NULL, heap); FALSE, heap);
old_rec_size = rec_offs_size(offsets); old_rec_size = rec_offs_size(offsets);
new_rec_size = rec_get_converted_size(index, new_entry, 0); new_rec_size = rec_get_converted_size(index, new_entry, 0);
...@@ -2144,7 +2144,7 @@ btr_cur_pessimistic_update( ...@@ -2144,7 +2144,7 @@ btr_cur_pessimistic_update(
purge would also have removed the clustered index record purge would also have removed the clustered index record
itself. Thus the following call is safe. */ itself. Thus the following call is safe. */
row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update, row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update,
FALSE, *heap, *heap); FALSE, *heap);
if (!(flags & BTR_KEEP_SYS_FLAG)) { if (!(flags & BTR_KEEP_SYS_FLAG)) {
row_upd_index_entry_sys_field(new_entry, index, DATA_ROLL_PTR, row_upd_index_entry_sys_field(new_entry, index, DATA_ROLL_PTR,
roll_ptr); roll_ptr);
......
...@@ -62,6 +62,16 @@ upd_field_set_field_no( ...@@ -62,6 +62,16 @@ upd_field_set_field_no(
dict_index_t* index, /* in: index */ dict_index_t* index, /* in: index */
trx_t* trx); /* in: transaction */ trx_t* trx); /* in: transaction */
/************************************************************************* /*************************************************************************
Returns a field of an update vector by field_no. */
UNIV_INLINE
const upd_field_t*
upd_get_field_by_field_no(
/*======================*/
/* out: update vector field, or NULL */
const upd_t* update, /* in: update vector */
ulint no) /* in: field_no */
__attribute__((nonnull, pure));
/*************************************************************************
Writes into the redo log the values of trx id and roll ptr and enough info Writes into the redo log the values of trx id and roll ptr and enough info
to determine their positions within a clustered index record. */ to determine their positions within a clustered index record. */
UNIV_INTERN UNIV_INTERN
...@@ -198,14 +208,9 @@ row_upd_index_replace_new_col_vals_index_pos( ...@@ -198,14 +208,9 @@ row_upd_index_replace_new_col_vals_index_pos(
/* in: if TRUE, limit the replacement to /* in: if TRUE, limit the replacement to
ordering fields of index; note that this ordering fields of index; note that this
does not work for non-clustered indexes. */ does not work for non-clustered indexes. */
mem_heap_t* heap, /* in: memory heap to which we allocate and mem_heap_t* heap) /* in: memory heap for allocating and
copy the new values, set this as NULL if you copying the new values */
do not want allocation */ __attribute__((nonnull));
mem_heap_t* ext_heap);/* in: memory heap where to allocate
column prefixes of externally stored
columns, may be NULL if the index
record does not contain externally
stored columns or column prefixes */
/*************************************************************** /***************************************************************
Replaces the new column values stored in the update vector to the index entry Replaces the new column values stored in the update vector to the index entry
given. */ given. */
...@@ -222,14 +227,9 @@ row_upd_index_replace_new_col_vals( ...@@ -222,14 +227,9 @@ row_upd_index_replace_new_col_vals(
const upd_t* update, /* in: an update vector built for the const upd_t* update, /* in: an update vector built for the
CLUSTERED index so that the field number in CLUSTERED index so that the field number in
an upd_field is the clustered index position */ an upd_field is the clustered index position */
mem_heap_t* heap, /* in: memory heap to which we allocate and mem_heap_t* heap) /* in: memory heap for allocating and
copy the new values, set this as NULL if you copying the new values */
do not want allocation */ __attribute__((nonnull));
mem_heap_t* ext_heap);/* in: memory heap where to allocate
column prefixes of externally stored
columns, may be NULL if the index
record does not contain externally
stored columns or column prefixes */
/*************************************************************** /***************************************************************
Replaces the new column values stored in the update vector. */ Replaces the new column values stored in the update vector. */
UNIV_INTERN UNIV_INTERN
......
...@@ -97,6 +97,29 @@ upd_field_set_field_no( ...@@ -97,6 +97,29 @@ upd_field_set_field_no(
dfield_get_type(&upd_field->new_val)); dfield_get_type(&upd_field->new_val));
} }
/*************************************************************************
Returns a field of an update vector by field_no. */
UNIV_INLINE
const upd_field_t*
upd_get_field_by_field_no(
/*======================*/
/* out: update vector field, or NULL */
const upd_t* update, /* in: update vector */
ulint no) /* in: field_no */
{
ulint i;
for (i = 0; i < upd_get_n_fields(update); i++) {
const upd_field_t* uf = upd_get_nth_field(update, i);
if (uf->field_no == no) {
return(uf);
}
}
return(NULL);
}
/************************************************************************* /*************************************************************************
Updates the trx id and roll ptr field in a clustered index record when Updates the trx id and roll ptr field in a clustered index record when
a row is updated or marked deleted. */ a row is updated or marked deleted. */
......
...@@ -49,21 +49,22 @@ t1 CREATE TABLE `t1` ( ...@@ -49,21 +49,22 @@ t1 CREATE TABLE `t1` (
PRIMARY KEY (`a`) PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 KEY_BLOCK_SIZE=1 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 KEY_BLOCK_SIZE=1
drop table t1; drop table t1;
create table t1(a int primary key) engine=innodb key_block_size=9;
ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'KEY_BLOCK_SIZE'
create table t1(a int not null, b text, index(b(10))) engine=innodb create table t1(a int not null, b text, index(b(10))) engine=innodb
key_block_size=1; key_block_size=1;
insert into t1 values (1,1); create table t2(b text)engine=innodb;
insert into t2 values(concat('1abcdefghijklmnopqrstuvwxyz', repeat('A',5000)));
insert into t1 select 1, b from t2;
commit; commit;
begin; begin;
update t1 set b=repeat('B',100); update t1 set b=repeat('B',100);
select a,left(b,40),b=1 is_equal from t1; select a,left(b,40) from t1 natural join t2;
a left(b,40) is_equal a left(b,40)
1 1 1 1 1abcdefghijklmnopqrstuvwxyzAAAAAAAAAAAAA
rollback; rollback;
select a,left(b,40),b=1 is_equal from t1; select a,left(b,40) from t1 natural join t2;
a left(b,40) is_equal a left(b,40)
1 1 1 1 1abcdefghijklmnopqrstuvwxyzAAAAAAAAAAAAA
drop table t1; drop table t1;
drop table t2;
set global innodb_file_per_table=0; set global innodb_file_per_table=0;
set global innodb_file_format=0; set global innodb_file_format=0;
...@@ -37,15 +37,13 @@ key_block_size=1; ...@@ -37,15 +37,13 @@ key_block_size=1;
show create table t1; show create table t1;
drop table t1; drop table t1;
--error 1478
create table t1(a int primary key) engine=innodb key_block_size=9;
create table t1(a int not null, b text, index(b(10))) engine=innodb create table t1(a int not null, b text, index(b(10))) engine=innodb
key_block_size=1; key_block_size=1;
let $b=`select '1abcdefghijklmnopqrstuvwxyz'+repeat('A',5000)`; create table t2(b text)engine=innodb;
insert into t2 values(concat('1abcdefghijklmnopqrstuvwxyz', repeat('A',5000)));
eval insert into t1 values (1,$b); insert into t1 select 1, b from t2;
commit; commit;
connect (a,localhost,root,,); connect (a,localhost,root,,);
...@@ -56,18 +54,19 @@ begin; ...@@ -56,18 +54,19 @@ begin;
update t1 set b=repeat('B',100); update t1 set b=repeat('B',100);
connection b; connection b;
eval select a,left(b,40),b=$b is_equal from t1; select a,left(b,40) from t1 natural join t2;
connection a; connection a;
rollback; rollback;
connection b; connection b;
eval select a,left(b,40),b=$b is_equal from t1; select a,left(b,40) from t1 natural join t2;
connection default; connection default;
disconnect a; disconnect a;
disconnect b; disconnect b;
drop table t1; drop table t1;
drop table t2;
eval set global innodb_file_per_table=$per_table; eval set global innodb_file_per_table=$per_table;
eval set global innodb_file_format=$format; eval set global innodb_file_format=$format;
...@@ -865,6 +865,97 @@ row_upd_ext_fetch( ...@@ -865,6 +865,97 @@ row_upd_ext_fetch(
return(buf); return(buf);
} }
/***************************************************************
Replaces the new column value stored in the update vector in
the given index entry field. */
static
void
row_upd_index_replace_new_col_val(
/*==============================*/
dfield_t* dfield, /* in/out: data field
of the index entry */
const dict_field_t* field, /* in: index field */
const dict_col_t* col, /* in: field->col */
const upd_field_t* uf, /* in: update field */
ulint zip_size)/* in: compressed page
size of the table, or 0 */
{
ulint len;
const byte* data;
dfield_copy_data(dfield, &uf->new_val);
if (dfield_is_null(dfield)) {
return;
}
len = dfield_get_len(dfield);
data = dfield_get_data(dfield);
if (field->prefix_len > 0) {
ibool fetch_ext = dfield_is_ext(dfield)
&& len < (ulint) field->prefix_len
+ BTR_EXTERN_FIELD_REF_SIZE;
if (fetch_ext) {
ulint l = len;
len = field->prefix_len;
data = row_upd_ext_fetch(data, l, zip_size,
&len, heap);
}
len = dtype_get_at_most_n_mbchars(col->prtype,
col->mbminlen, col->mbmaxlen,
field->prefix_len, len,
(const char*) data);
dfield_set_data(dfield, data, len);
if (!fetch_ext) {
dfield_dup(dfield, heap);
}
return;
}
switch (uf->orig_len) {
byte* buf;
case BTR_EXTERN_FIELD_REF_SIZE:
/* Restore the original locally stored
part of the column. In the undo log,
InnoDB writes a longer prefix of externally
stored columns, so that column prefixes
in secondary indexes can be reconstructed. */
dfield_set_data(dfield,
data + len - BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_ext(dfield);
/* fall through */
case 0:
dfield_dup(dfield, heap);
break;
default:
/* Reconstruct the original locally
stored part of the column. The data
will have to be copied. */
ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
buf = mem_heap_alloc(heap, uf->orig_len);
/* Copy the locally stored prefix. */
memcpy(buf, data,
uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
/* Copy the BLOB pointer. */
memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
data + len - BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_data(dfield, buf, uf->orig_len);
dfield_set_ext(dfield);
break;
}
}
/*************************************************************** /***************************************************************
Replaces the new column values stored in the update vector to the index entry Replaces the new column values stored in the update vector to the index entry
given. */ given. */
...@@ -885,18 +976,12 @@ row_upd_index_replace_new_col_vals_index_pos( ...@@ -885,18 +976,12 @@ row_upd_index_replace_new_col_vals_index_pos(
/* in: if TRUE, limit the replacement to /* in: if TRUE, limit the replacement to
ordering fields of index; note that this ordering fields of index; note that this
does not work for non-clustered indexes. */ does not work for non-clustered indexes. */
mem_heap_t* heap, /* in: memory heap to which we allocate and mem_heap_t* heap, /* in: memory heap for allocating and
copy the new values, set this as NULL if you copying the new values */
do not want allocation */
mem_heap_t* ext_heap)/* in: memory heap where to allocate
column prefixes of externally stored
columns, may be NULL if the index
record does not contain externally
stored columns or column prefixes */
{ {
ulint j;
ulint i; ulint i;
ulint n_fields; ulint n_fields;
const ulint zip_size = dict_table_zip_size(index->table);
ut_ad(index); ut_ad(index);
...@@ -908,80 +993,19 @@ row_upd_index_replace_new_col_vals_index_pos( ...@@ -908,80 +993,19 @@ row_upd_index_replace_new_col_vals_index_pos(
n_fields = dict_index_get_n_fields(index); n_fields = dict_index_get_n_fields(index);
} }
for (j = 0; j < n_fields; j++) { for (i = 0; i < n_fields; i++) {
const dict_field_t* field;
dict_field_t* field const dict_col_t* col;
= dict_index_get_nth_field(index, j); const upd_field_t* uf;
const dict_col_t* col
= dict_field_get_col(field);
for (i = 0; i < upd_get_n_fields(update); i++) {
upd_field_t* upd_field;
dfield_t* dfield;
upd_field = upd_get_nth_field(update, i);
if (upd_field->field_no != j) {
continue;
}
dfield = dtuple_get_nth_field(entry, j);
dfield_copy_data(dfield, &upd_field->new_val);
if (dfield_is_null(dfield)) {
break;
}
if (field->prefix_len > 0) {
ulint len
= dfield_get_len(dfield);
const byte* data
= dfield_get_data(dfield);
ibool fetch_ext
= dfield_is_ext(dfield)
&& len < (ulint) field->prefix_len
+ BTR_EXTERN_FIELD_REF_SIZE;
if (fetch_ext) {
ulint l
= len;
ulint zip_size
= dict_table_zip_size(
index->table);
ut_a(ext_heap);
len = field->prefix_len;
data = row_upd_ext_fetch(data, l,
zip_size,
&len,
ext_heap);
}
len = dtype_get_at_most_n_mbchars(
col->prtype,
col->mbminlen,
col->mbmaxlen,
field->prefix_len,
len, (const char*) data);
dfield_set_data(dfield, data, len);
if (fetch_ext && heap && heap == ext_heap) {
/* Skip the dfield_dup() below,
as the column prefix has already
been allocated from ext_heap. */
break;
}
}
if (heap) { field = dict_index_get_nth_field(index, i);
dfield_dup(dfield, heap); col = dict_field_get_col(field);
} uf = upd_get_field_by_field_no(update, i);
break; if (uf) {
row_upd_index_replace_new_col_val(
dtuple_get_nth_field(entry, i),
field, col, uf, zip_size);
} }
} }
} }
...@@ -1002,101 +1026,31 @@ row_upd_index_replace_new_col_vals( ...@@ -1002,101 +1026,31 @@ row_upd_index_replace_new_col_vals(
const upd_t* update, /* in: an update vector built for the const upd_t* update, /* in: an update vector built for the
CLUSTERED index so that the field number in CLUSTERED index so that the field number in
an upd_field is the clustered index position */ an upd_field is the clustered index position */
mem_heap_t* heap, /* in: memory heap to which we allocate and mem_heap_t* heap) /* in: memory heap for allocating and
copy the new values, set this as NULL if you copying the new values */
do not want allocation */
mem_heap_t* ext_heap)/* in: memory heap where to allocate
column prefixes of externally stored
columns, may be NULL if the index
record does not contain externally
stored columns or column prefixes */
{ {
ulint j; ulint i;
ulint i; const dict_index_t* clust_index
dict_index_t* clust_index; = dict_table_get_first_index(index->table);
const ulint zip_size
ut_ad(index); = dict_table_zip_size(index->table);
clust_index = dict_table_get_first_index(index->table);
dtuple_set_info_bits(entry, update->info_bits); dtuple_set_info_bits(entry, update->info_bits);
for (j = 0; j < dict_index_get_n_fields(index); j++) { for (i = 0; i < dict_index_get_n_fields(index); i++) {
const dict_field_t* field;
dict_field_t* field const dict_col_t* col;
= dict_index_get_nth_field(index, j); const upd_field_t* uf;
const dict_col_t* col
= dict_field_get_col(field);
const ulint clust_pos
= dict_col_get_clust_pos(col, clust_index);
for (i = 0; i < upd_get_n_fields(update); i++) {
upd_field_t* upd_field;
dfield_t* dfield;
upd_field = upd_get_nth_field(update, i);
if (upd_field->field_no != clust_pos) {
continue;
}
dfield = dtuple_get_nth_field(entry, j);
dfield_copy_data(dfield, &upd_field->new_val);
if (dfield_is_null(dfield)) {
break;
}
if (field->prefix_len > 0) {
ulint len
= dfield_get_len(dfield);
const byte* data
= dfield_get_data(dfield);
ibool fetch_ext
= dfield_is_ext(dfield)
&& len < (ulint) field->prefix_len
+ BTR_EXTERN_FIELD_REF_SIZE;
if (fetch_ext) {
ulint l
= len;
ulint zip_size
= dict_table_zip_size(
index->table);
ut_a(ext_heap);
len = field->prefix_len;
data = row_upd_ext_fetch(data, l,
zip_size,
&len,
ext_heap);
}
len = dtype_get_at_most_n_mbchars(
col->prtype,
col->mbminlen,
col->mbmaxlen,
field->prefix_len,
len, (const char*) data);
dfield_set_data(dfield, data, len);
if (fetch_ext && heap && heap == ext_heap) {
/* Skip the dfield_dup() below,
as the column prefix has already
been allocated from ext_heap. */
break;
}
}
if (heap) { field = dict_index_get_nth_field(index, i);
dfield_dup(dfield, heap); col = dict_field_get_col(field);
} uf = upd_get_field_by_field_no(
update, dict_col_get_clust_pos(col, clust_index));
break; if (uf) {
row_upd_index_replace_new_col_val(
dtuple_get_nth_field(entry, i),
field, col, uf);
} }
} }
} }
......
...@@ -1552,8 +1552,7 @@ trx_undo_prev_version_build( ...@@ -1552,8 +1552,7 @@ trx_undo_prev_version_build(
/* The page containing the clustered index record /* The page containing the clustered index record
corresponding to entry is latched in mtr. Thus the corresponding to entry is latched in mtr. Thus the
following call is safe. */ following call is safe. */
row_upd_index_replace_new_col_vals(entry, index, update, row_upd_index_replace_new_col_vals(entry, index, update, heap);
heap, heap);
buf = mem_heap_alloc(heap, rec_get_converted_size(index, entry, buf = mem_heap_alloc(heap, rec_get_converted_size(index, entry,
n_ext)); n_ext));
......
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