mirror of
https://github.com/MariaDB/server.git
synced 2025-07-27 18:02:13 +03:00
bugfix: UPDATE and virtual BLOBs
When updating a table with virtual BLOB columns, the following might happen: - an old record is read from the table, it has no virtual blob values - update_virtual_fields() is run, vcol blob gets its value into the record. But only a pointer to the value is in the table->record[0], the value is in Field_blob::value String (but it doesn't have to be! it can be in the record, if the column is just a copy of another columns: ... b VARCHAR, c BLOB AS (b) ...) - store_record(table,record[1]), old record now is in record[1] - fill_record() prepares new values in record[0], vcol blob is updated, new value replaces the old one in the Field_blob::value - now both record[1] and record[0] have a pointer that points to the *new* vcol blob value. Or record[1] has a pointer to nowhere if Field_blob::value had to realloc. To resolve this we unlink vcol blobs from the pointer to the data (in the record[1]). Because the value is not *always* in the Field_blob::value String, we need to remember what blobs were unlinked. The orphan memory must be freed manually. To complicate the matter, ha_update_row() is also used in multi-update, in REPLACE, in INSERT ... ON DUPLICATE KEY UPDATE, also on REPLACE ... SELECT, REPLACE DELAYED, and LOAD DATA REPLACE, etc
This commit is contained in:
@ -201,6 +201,99 @@ typedef struct st_user_var_events
|
||||
bool unsigned_flag;
|
||||
} BINLOG_USER_VAR_EVENT;
|
||||
|
||||
|
||||
/*
|
||||
When updating a table with virtual BLOB columns, the following might happen:
|
||||
- an old record is read from the table, it has no vcol blob.
|
||||
- update_virtual_fields() is run, vcol blob gets its value into the
|
||||
record. But only a pointer to the value is in the table->record[0],
|
||||
the value is in Field_blob::value String (or, it can be elsewhere!)
|
||||
- store_record(table,record[1]), old record now is in record[1]
|
||||
- fill_record() prepares new values in record[0], vcol blob is updated,
|
||||
new value replaces the old one in the Field_blob::value
|
||||
- now both record[1] and record[0] have a pointer that points to the
|
||||
*new* vcol blob value. Or record[1] has a pointer to nowhere if
|
||||
Field_blob::value had to realloc.
|
||||
|
||||
To resolve this we unlink vcol blobs from the pointer to the
|
||||
data (in the record[1]). The orphan memory must be freed manually
|
||||
(but, again, only if it was owned by Field_blob::value String).
|
||||
|
||||
With REPLACE and INSERT ... ON DUP KEY UPATE it's even more complex.
|
||||
There is no store_record(table,record[1]), instead the row is read
|
||||
directly into record[1].
|
||||
*/
|
||||
struct BLOB_VALUE_ORPHANAGE {
|
||||
MY_BITMAP map;
|
||||
TABLE *table;
|
||||
BLOB_VALUE_ORPHANAGE() { map.bitmap= NULL; }
|
||||
~BLOB_VALUE_ORPHANAGE() { free(); }
|
||||
bool init(TABLE *table_arg)
|
||||
{
|
||||
table= table_arg;
|
||||
if (table->s->virtual_fields && table->s->blob_fields)
|
||||
return bitmap_init(&map, NULL, table->s->virtual_fields, FALSE);
|
||||
map.bitmap= NULL;
|
||||
return 0;
|
||||
}
|
||||
void free() { bitmap_free(&map); }
|
||||
|
||||
/** Remove blob's ownership from blob value memory
|
||||
|
||||
@note the memory becomes orphaned, it needs to be freed using
|
||||
free_orphans() or re-attached back to blobs using adopt_orphans()
|
||||
*/
|
||||
void make_orphans()
|
||||
{
|
||||
DBUG_ASSERT(!table || !table->s->virtual_fields || !table->s->blob_fields || map.bitmap);
|
||||
if (!map.bitmap)
|
||||
return;
|
||||
for (Field **ptr=table->vfield; *ptr; ptr++)
|
||||
{
|
||||
Field_blob *vb= (Field_blob*)(*ptr);
|
||||
if (!(vb->flags & BLOB_FLAG) || !vb->owns_ptr(vb->get_ptr()))
|
||||
continue;
|
||||
bitmap_set_bit(&map, ptr - table->vfield);
|
||||
vb->clear_temporary();
|
||||
}
|
||||
}
|
||||
|
||||
/** Frees orphaned blob values
|
||||
|
||||
@note It is assumed that value pointers are in table->record[1], while
|
||||
Field_blob::ptr's point to table->record[0] as usual
|
||||
*/
|
||||
void free_orphans()
|
||||
{
|
||||
DBUG_ASSERT(!table || !table->s->virtual_fields || !table->s->blob_fields || map.bitmap);
|
||||
if (!map.bitmap)
|
||||
return;
|
||||
for (Field **ptr=table->vfield; *ptr; ptr++)
|
||||
{
|
||||
Field_blob *vb= (Field_blob*)(*ptr);
|
||||
if (vb->flags & BLOB_FLAG && bitmap_fast_test_and_clear(&map, ptr - table->vfield))
|
||||
my_free(vb->get_ptr(table->s->rec_buff_length));
|
||||
}
|
||||
DBUG_ASSERT(bitmap_is_clear_all(&map));
|
||||
}
|
||||
|
||||
/** Restores blob's ownership over previously orphaned values */
|
||||
void adopt_orphans()
|
||||
{
|
||||
DBUG_ASSERT(!table || !table->s->virtual_fields || !table->s->blob_fields || map.bitmap);
|
||||
if (!map.bitmap)
|
||||
return;
|
||||
for (Field **ptr=table->vfield; *ptr; ptr++)
|
||||
{
|
||||
Field_blob *vb= (Field_blob*)(*ptr);
|
||||
if (vb->flags & BLOB_FLAG && bitmap_fast_test_and_clear(&map, ptr - table->vfield))
|
||||
vb->own_value_ptr();
|
||||
}
|
||||
DBUG_ASSERT(bitmap_is_clear_all(&map));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
The COPY_INFO structure is used by INSERT/REPLACE code.
|
||||
The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
|
||||
@ -213,7 +306,7 @@ typedef struct st_user_var_events
|
||||
of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row
|
||||
was actually changed or not.
|
||||
*/
|
||||
typedef struct st_copy_info {
|
||||
struct COPY_INFO {
|
||||
ha_rows records; /**< Number of processed records */
|
||||
ha_rows deleted; /**< Number of deleted records */
|
||||
ha_rows updated; /**< Number of updated records */
|
||||
@ -229,7 +322,8 @@ typedef struct st_copy_info {
|
||||
/* for VIEW ... WITH CHECK OPTION */
|
||||
TABLE_LIST *view;
|
||||
TABLE_LIST *table_list; /* Normal table */
|
||||
} COPY_INFO;
|
||||
BLOB_VALUE_ORPHANAGE vblobs0, vblobs1; // vcol blobs of record[0] and record[1]
|
||||
};
|
||||
|
||||
|
||||
class Key_part_spec :public Sql_alloc {
|
||||
@ -5317,6 +5411,7 @@ class multi_update :public select_result_interceptor
|
||||
TABLE_LIST *update_tables, *table_being_updated;
|
||||
TABLE **tmp_tables, *main_table, *table_to_update;
|
||||
TMP_TABLE_PARAM *tmp_table_param;
|
||||
BLOB_VALUE_ORPHANAGE *vblobs;
|
||||
ha_rows updated, found;
|
||||
List <Item> *fields, *values;
|
||||
List <Item> **fields_for_table, **values_for_table;
|
||||
|
Reference in New Issue
Block a user