Bug #18252 Disk space leak in updates of InnoDB BLOB rows in 5.0 and 5.1
Submitted: 15 Mar 2006 11:36 Modified: 11 Apr 2006 4:24
Reporter: Marko Mäkelä
Status: Closed
Category:Server: InnoDB Severity:S2 (Serious)
Version:5.0.3 and later OS:Any (all)
Assigned to: Marko Mäkelä Target Version:

[15 Mar 2006 11:36] Marko Mäkelä
Description:
With the introduction of ROW_FORMAT=COMPACT, random access to the fields of a record
became complicated. To simplify and to speed up the logic, we compute an array of offsets
to the fields and pass this array to the access functions.

When a record is marked to contain an externally stored field (BLOB), InnoDB will not set
the corresponding flag in the array of offsets. In some code paths, InnoDB trusts the
flags in the array of offsets to be valid.

How to repeat:
Update BLOB columns. Observe that disk space will remain allocated to garbage BLOBs until
DROP TABLE, OPTIMIZE TABLE or ALTER TABLE is executed.

Suggested fix:
Set the "external storage" flag in offsets when marking a column to be externally stored.
[15 Mar 2006 15:25] Heikki Tuuri
A more precise description of the leak problem: if you have an InnoDB table created in the
default format of 5.0, that is, the compact format, and you update a row so that a long
column is updated or the length of some column changes, then InnoDB will later fail to
reclaim the BLOB storage space if the row is deleted.

DROP TABLE, ALTER TABLE, and TRUNCATE TABLE do free the BLOB space.
[15 Mar 2006 15:36] Heikki Tuuri
A correction: the bug also affects old format tables created with 4.1 and earlier.

Fix:

btr0cur.c:
"
        if (rec) {
                offsets = rec_get_offsets(rec, index, offsets,
                                        ULINT_UNDEFINED, &heap);

                lock_rec_restore_from_page_infimum(rec, page);
                rec_set_field_extern_bits(rec, index,
                                                ext_vect, n_ext_vect, mtr);

                if (!rec_get_deleted_flag(rec, rec_offs_comp(offsets))) {
                        /* The new inserted record owns its possible externally
                        stored fields */
                        btr_cur_unmark_extern_fields(rec, mtr, offsets);
                }

                btr_cur_compress_if_useful(cursor, mtr);

                err = DB_SUCCESS;
                goto return_after_reservations;
        }
"

Move the calculation of 'offsets' AFTER rec_set_field_extern_bits().
[5 Apr 2006 21:18] Elliot Murphy
Fixed in InnoDB snapshot368; fixes are in 5.0.20.
[11 Apr 2006 4:24] Paul DuBois
Noted in 5.0.20 changelog.