发现很多人在问我在进行insert 操作的时候,其中的 ha_innobase::write_row的参数是table->record[0], 那么此时record[0]中是什么时候将各个field中的数据写入到该record[0]缓存中呢?下面是insert 操作时候的call stack
从上面可以看出,handler::ha_write_row的时候其会调用innodb的write_row来进行真正的数据写人。同时,在Innodb内部会将由每个field所表示的数据的逻辑格式转为innodb接受的物理格式。该项操作由row_mysql_store_col_in_innobase_format 函数来完成。
在fill_record_n_invoke_before_triggers函数中会变量每个Field和Item 从而将由Item所标识的插入的值保存至其对应的Field中。 该项功能由fill_record函数来完成。以int类型为例
insert into xxx values (1,xxx);
此时会将1所对应的Item_long这个对象中获取到其值,int_val,并将该值保存至其Field类型的对象中。
而经过fill_record函数后,那么此时table->record[0]中的数据已经发生变化,变为由各个列中的数据构成了。
bool fill_record(THD *thd, TABLE *table, const mem_root_deque<Item *> &fields,
const mem_root_deque<Item *> &values, MY_BITMAP *bitmap,
MY_BITMAP *insert_into_fields_bitmap,
bool raise_autoinc_has_expl_non_null_val) {
DBUG_TRACE;
assert(CountVisibleFields(fields) == CountVisibleFields(values));
/*
In case when TABLE object comes to fill_record() from Table Cache it
should have autoinc_field_has_explicit_non_null_value flag set to false.
In case when TABLE object comes to fill_record() after processing
previous row this flag should be reset to false by caller.
Code which implements LOAD DATA is the exception to the above rule
as it calls fill_record() to handle SET clause, after values for
the columns directly coming from loaded from file are set and thus
autoinc_field_has_explicit_non_null_value possibly set to true.
*/
assert(table->autoinc_field_has_explicit_non_null_value == false ||
(raise_autoinc_has_expl_non_null_val &&
thd->lex->sql_command == SQLCOM_LOAD));
auto value_it = VisibleFields(values).begin();
for (Item *fld : VisibleFields(fields)) {
Item_field *const field = fld->field_for_view_update();
assert(field != nullptr && field->table_ref->table == table);
Field *const rfield = field->field;
Item *value = *value_it++;
/* If bitmap over wanted fields are set, skip non marked fields. */
if (bitmap && !bitmap_is_set(bitmap, rfield->field_index())) continue;
bitmap_set_bit(table->fields_set_during_insert, rfield->field_index());
if (insert_into_fields_bitmap)
bitmap_set_bit(insert_into_fields_bitmap, rfield->field_index());
/* Generated columns will be filled after all base columns are done. */
if (rfield->is_gcol()) continue;
if (raise_autoinc_has_expl_non_null_val &&
rfield == table->next_number_field)
table->autoinc_field_has_explicit_non_null_value = true;
/*
We handle errors from save_in_field() by first checking the return
value and then testing thd->is_error(). thd->is_error() can be set
even when save_in_field() does not return a negative value.
@todo save_in_field returns an enum which should never be a negative
value. We should change this test to check for correct enum value.
The below call can reset TABLE::autoinc_field_has_explicit_non_null_value
flag depending on value provided (for details please see
set_field_to_null_with_conversions()). So evaluation of this flag can’t
be moved outside of fill_record(), to be done once per statement.
*/
if (value->save_in_field(rfield, false) < 0) {
my_error(ER_UNKNOWN_ERROR, MYF(0));
return true;
}
if (thd->is_error()) return true;
}
if (table->has_gcol() &&
update_generated_write_fields(bitmap ? bitmap : table->write_set, table))
return true;
/*
TABLE::autoinc_field_has_explicit_non_null_value should not be set to
true in raise_autoinc_has_expl_non_null_val == false mode.
*/
assert(table->autoinc_field_has_explicit_non_null_value == false ||
raise_autoinc_has_expl_non_null_val);
return thd->is_error();
}
由fill_record函数中,我们并未发现其有操作table->record[0]的代码,那么Field又是如何与table->record[0]发生关联呢?使得我们value->save_in_field函数中的操作可以之间写人到table->record[0]中呢?总要又一个地方使得Field和Table中的record[0]发生关联?从而使得对应Field中的ptr的操作可以直接反映到对Table中的record[0]的操作。那么这个地方在哪里呢?答案在:
int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag, uint ha_open_flags,
TABLE *outparam, bool is_create_table,
const dd::Table *table_def_param)
这个函数中。 下面我们就看看其是如何发生关联的。
首先在该函数中会在outparam创建一个新的Table对象
然后对该outparam进行初始,其中一项是分配record内存并将其赋值给outparam中的record.
而后,创建N+1个Field对象并将其复制给outparam中的field:
if (!(field_ptr = root->ArrayAlloc<Field *>(share->fields + 1)))
goto err; /* purecov: inspected */
outparam->field = field_ptr;
record = (uchar *)outparam->record[0] – 1; /* Fieldstart = 1 */
outparam->null_flags = (uchar *)record + 1;
然后对outparam中的field进行复制(由table cache中)mysql中的注释已经说明了一切:
/*
We will create fields by cloning TABLE_SHARE’s fields; then we will need
to make all new fields’ pointers point into the new TABLE’s record[0], by
applying an offset to them.
Calculate the “source” offset depending on table type:
– For non-internal temporary tables, source is share->default_values
– For internal tables, source is first TABLE’s record[0], which
happens to be created in same memory block as share->default_values, with
offset 2 * share->rec_buff_length (see create_tmp_table()).
*/
“make all new fields’ pointers point into the new TABLE’s record[0]” 这样就把每个field指向了record[0],因为可以获取到每个field的类型和数据的长度,因而在后续的fill_record函数中我们对Field的store操作,其实质上也是对record[0]的操作。当我们完成对应所有Field的store操作后,那么我们在reocd[0]中或获得了每个列的数据。
record[0] memory layout:
—————————————————–
Field1 | Field2 | … | FieldN
/* Setup copy of fields from share, but use the right alias and record */
for (i = 0; i < share->fields; i++, field_ptr++) {
Field *new_field = share->field[i]->clone(root);
*field_ptr = new_field;
if (new_field == nullptr) goto err;
new_field->init(outparam);
new_field->move_field_offset(move_offset);
/*
Initialize Field::pack_length() number of bytes for new_field->ptr
only if there are no default values for the field.
*/
if (!has_default_values) new_field->reset();
/* Check if FTS_DOC_ID column is present in the table */
if (outparam->file &&
(outparam->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) &&
!strcmp(outparam->field[i]->field_name, FTS_DOC_ID_COL_NAME))
fts_doc_id_field = new_field;
}
(*field_ptr) = nullptr; // End marker