diff options
author | Sergei Golubchik <serg@mariadb.org> | 2016-11-23 16:42:09 +0100 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2016-12-12 20:27:38 +0100 |
commit | f73bdb685d77b944f4565e0a97778faa30999145 (patch) | |
tree | 3d9eb5f5e830757782de891e70865aafe83c84be /sql/sql_update.cc | |
parent | aebb1038aab6cadc3408fc194d1d9331d2b673ff (diff) | |
download | mariadb-git-f73bdb685d77b944f4565e0a97778faa30999145.tar.gz |
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
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r-- | sql/sql_update.cc | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 05f7080609a..1f1af7f2660 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -273,6 +273,7 @@ int mysql_update(THD *thd, SORT_INFO *file_sort= 0; READ_RECORD info; SELECT_LEX *select_lex= &thd->lex->select_lex; + BLOB_VALUE_ORPHANAGE vblobs; ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; @@ -724,6 +725,8 @@ int mysql_update(THD *thd, table->reset_default_fields(); + vblobs.init(table); + /* We can use compare_record() to optimize away updates if the table handler is returning all columns OR if @@ -745,6 +748,9 @@ int mysql_update(THD *thd, explain->tracker.on_record_after_where(); store_record(table,record[1]); + + vblobs.make_orphans(); + if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -904,7 +910,9 @@ int mysql_update(THD *thd, error= 1; break; } + vblobs.free_orphans(); } + vblobs.free_orphans(); ANALYZE_STOP_TRACKING(&explain->command_tracker); table->auto_increment_field_not_null= FALSE; dup_key_found= 0; @@ -1752,6 +1760,8 @@ int multi_update::prepare(List<Item> ¬_used_values, table_count); values_for_table= (List_item **) thd->alloc(sizeof(List_item *) * table_count); + vblobs= (BLOB_VALUE_ORPHANAGE *)thd->calloc(sizeof(*vblobs) * table_count); + if (thd->is_fatal_error) DBUG_RETURN(1); for (i=0 ; i < table_count ; i++) @@ -1784,6 +1794,7 @@ int multi_update::prepare(List<Item> ¬_used_values, TABLE *table= ((Item_field*)(fields_for_table[i]->head()))->field->table; switch_to_nullable_trigger_fields(*fields_for_table[i], table); switch_to_nullable_trigger_fields(*values_for_table[i], table); + vblobs[i].init(table); } } copy_field= new Copy_field[max_fields]; @@ -2065,6 +2076,8 @@ multi_update::~multi_update() free_tmp_table(thd, tmp_tables[cnt]); tmp_table_param[cnt].cleanup(); } + vblobs[cnt].free_orphans(); + vblobs[cnt].free(); } } if (copy_field) @@ -2110,7 +2123,9 @@ int multi_update::send_data(List<Item> ¬_used_values) can_compare_record= records_are_comparable(table); table->status|= STATUS_UPDATED; + vblobs[offset].free_orphans(); store_record(table,record[1]); + vblobs[offset].make_orphans(); if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], *values_for_table[offset], 0, @@ -2327,6 +2342,7 @@ int multi_update::do_updates() goto err; } table->file->extra(HA_EXTRA_NO_CACHE); + empty_record(table); check_opt_it.rewind(); while(TABLE *tbl= check_opt_it++) @@ -2400,7 +2416,9 @@ int multi_update::do_updates() goto err2; table->status|= STATUS_UPDATED; + vblobs[offset].free_orphans(); store_record(table,record[1]); + vblobs[offset].make_orphans(); /* Copy data from temporary table to current table */ for (copy_field_ptr=copy_field; |