Commit 8ec95fc7 authored by marko's avatar marko

branches/zip: When the server crashes while freeing an externally stored

column of a compressed table, the BTR_EXTERN_LEN field in the BLOB pointer
will be written as 0.  Tolerate this in the functions that deal with
externally stored columns.  This fixes Issue #80 and was posted at rb://26.

Note that the clustered index record is always deleted or purged last,
after any secondary index records referring to it have been deleted.

btr_free_externally_stored_field(): On an uncompressed table, zero out
the BTR_EXTERN_LEN, so that half-deleted BLOBs can be detected after
crash recovery.

btr_copy_externally_stored_field_prefix(): Return 0 if the BLOB has been
half-deleted.

row_upd_ext_fetch(): Assert that the externally stored column exists.

row_ext_cache_fill(): Allow btr_copy_externally_stored_field_prefix()
to return 0.

row_sel_sec_rec_is_for_blob(): Return FALSE if the BLOB has been half-deleted.
This is correct, because the clustered index record would have been deleted
or purged last, after any secondary index records referring to it had been
deleted.
parent f644f6af
......@@ -4224,14 +4224,8 @@ btr_free_externally_stored_field(
MLOG_4BYTES, &mtr);
}
} else {
ulint extern_len = mach_read_from_4(
field_ref + BTR_EXTERN_LEN + 4);
ulint part_len = btr_blob_get_part_len(
page + FIL_PAGE_DATA);
ut_a(fil_page_get_type(page) == FIL_PAGE_TYPE_BLOB);
ut_a(!page_zip);
ut_a(extern_len >= part_len);
next_page_no = mach_read_from_4(
page + FIL_PAGE_DATA
......@@ -4249,16 +4243,14 @@ btr_free_externally_stored_field(
mlog_write_ulint(field_ref + BTR_EXTERN_PAGE_NO,
next_page_no,
MLOG_4BYTES, &mtr);
/* Zero out the BLOB length. If the server
crashes during the execution of this function,
trx_rollback_or_clean_all_recovered() could
dereference the half-deleted BLOB, fetching a
wrong prefix for the BLOB. */
mlog_write_ulint(field_ref + BTR_EXTERN_LEN + 4,
extern_len - part_len,
0,
MLOG_4BYTES, &mtr);
if (next_page_no == FIL_NULL) {
ut_a(extern_len - part_len == 0);
}
if (extern_len - part_len == 0) {
ut_a(next_page_no == FIL_NULL);
}
}
/* Commit mtr and release the BLOB block to save memory. */
......@@ -4596,7 +4588,9 @@ UNIV_INTERN
ulint
btr_copy_externally_stored_field_prefix(
/*====================================*/
/* out: the length of the copied field */
/* out: the length of the copied field,
or 0 if the column was being or has been
deleted */
byte* buf, /* out: the field, or a prefix of it */
ulint len, /* in: length of buf, in bytes */
ulint zip_size,/* in: nonzero=compressed BLOB page size,
......@@ -4625,6 +4619,14 @@ btr_copy_externally_stored_field_prefix(
ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE));
if (!mach_read_from_4(data + BTR_EXTERN_LEN + 4)) {
/* The externally stored part of the column has been
(partially) deleted. Signal the half-deleted BLOB
to the caller. */
return(0);
}
space_id = mach_read_from_4(data + BTR_EXTERN_SPACE_ID);
page_no = mach_read_from_4(data + BTR_EXTERN_PAGE_NO);
......
......@@ -532,7 +532,9 @@ UNIV_INTERN
ulint
btr_copy_externally_stored_field_prefix(
/*====================================*/
/* out: the length of the copied field */
/* out: the length of the copied field,
or 0 if the column is being or has been
deleted */
byte* buf, /* out: the field, or a prefix of it */
ulint len, /* in: length of buf, in bytes */
ulint zip_size,/* in: nonzero=compressed BLOB page size,
......
......@@ -40,10 +40,13 @@ row_ext_cache_fill(
ext->len[i] = 0;
} else {
/* Fetch at most REC_MAX_INDEX_COL_LEN of the column.
The column must be non-empty. */
The column should be non-empty. However,
trx_rollback_or_clean_all_recovered() may try to
access a half-deleted BLOB if the server previously
crashed during the execution of
btr_free_externally_stored_field(). */
ext->len[i] = btr_copy_externally_stored_field_prefix(
buf, REC_MAX_INDEX_COL_LEN, zip_size, field, f_len);
ut_a(ext->len[i]);
}
}
......
......@@ -86,6 +86,16 @@ row_sel_sec_rec_is_for_blob(
len = btr_copy_externally_stored_field_prefix(buf, sizeof buf,
zip_size,
clust_field, clust_len);
if (UNIV_UNLIKELY(len == 0)) {
/* The BLOB was being deleted as the server crashed.
There should not be any secondary index records
referring to this clustered index record, because
btr_free_externally_stored_field() is called after all
secondary index entries of the row have been purged. */
return(FALSE);
}
len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen,
sec_len, len, (const char*) buf);
......
......@@ -861,6 +861,8 @@ row_upd_ext_fetch(
*len = btr_copy_externally_stored_field_prefix(buf, *len,
zip_size,
data, local_len);
/* We should never update records containing a half-deleted BLOB. */
ut_a(*len);
return(buf);
}
......
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