summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2016-11-23 16:42:09 +0100
committerSergei Golubchik <serg@mariadb.org>2016-12-12 20:27:38 +0100
commitf73bdb685d77b944f4565e0a97778faa30999145 (patch)
tree3d9eb5f5e830757782de891e70865aafe83c84be /sql/sql_update.cc
parentaebb1038aab6cadc3408fc194d1d9331d2b673ff (diff)
downloadmariadb-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.cc18
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> &not_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> &not_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> &not_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;