Loading sql/field.cc +13 −18 Original line number Diff line number Diff line Loading @@ -1515,7 +1515,8 @@ bool Field::optimize_range(uint idx, uint part) } Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type __attribute__((unused))) { Field *tmp; if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of()))) Loading @@ -1540,7 +1541,7 @@ Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table, uint new_null_bit) { Field *tmp; if ((tmp= new_field(root, new_table))) if ((tmp= new_field(root, new_table, table == new_table))) { tmp->ptr= new_ptr; tmp->null_ptr= new_null_ptr; Loading Loading @@ -6227,29 +6228,21 @@ uint Field_string::max_packed_col_length(uint max_length) } Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type) { Field *new_field; if (type() != MYSQL_TYPE_VAR_STRING || table == new_table) return Field::new_field(root, new_table); if (type() != MYSQL_TYPE_VAR_STRING || keep_type) return Field::new_field(root, new_table, keep_type); /* Old VARCHAR field which should be modified to a VARCHAR on copy This is done to ensure that ALTER TABLE will convert old VARCHAR fields to now VARCHAR fields. */ if ((new_field= new Field_varstring(field_length, maybe_null(), field_name, new_table, charset()))) { /* delayed_insert::get_local_table() needs a ptr copied from old table. This is what other new_field() methods do too. The above method of Field_varstring sets ptr to NULL. */ new_field->ptr= ptr; } return new_field; return new Field_varstring(field_length, maybe_null(), field_name, new_table, charset()); } /**************************************************************************** Loading Loading @@ -6741,9 +6734,11 @@ int Field_varstring::cmp_binary(const char *a_ptr, const char *b_ptr, } Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type) { Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table); Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, keep_type); if (res) res->length_bytes= length_bytes; return res; Loading sql/field.h +4 −3 Original line number Diff line number Diff line Loading @@ -211,7 +211,8 @@ class Field */ virtual bool can_be_compared_as_longlong() const { return FALSE; } virtual void free() {} virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table); virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, char *new_ptr, uchar *new_null_ptr, uint new_null_bit); Loading Loading @@ -1033,7 +1034,7 @@ class Field_string :public Field_longstr { enum_field_types real_type() const { return FIELD_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table); Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); }; Loading Loading @@ -1105,7 +1106,7 @@ class Field_varstring :public Field_longstr { enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table); Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, char *new_ptr, uchar *new_null_ptr, uint new_null_bit); Loading sql/sql_insert.cc +67 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,44 @@ /* Insert of records */ /* INSERT DELAYED Insert delayed is distinguished from a normal insert by lock_type == TL_WRITE_DELAYED instead of TL_WRITE. It first tries to open a "delayed" table (delayed_get_table()), but falls back to open_and_lock_tables() on error and proceeds as normal insert then. Opening a "delayed" table means to find a delayed insert thread that has the table open already. If this fails, a new thread is created and waited for to open and lock the table. If accessing the thread succeeded, in delayed_insert::get_local_table() the table of the thread is copied for local use. A copy is required because the normal insert logic works on a target table, but the other threads table object must not be used. The insert logic uses the record buffer to create a record. And the delayed insert thread uses the record buffer to pass the record to the table handler. So there must be different objects. Also the copied table is not included in the lock, so that the statement can proceed even if the real table cannot be accessed at this moment. Copying a table object is not a trivial operation. Besides the TABLE object there are the field pointer array, the field objects and the record buffer. After copying the field objects, their pointers into the record must be "moved" to point to the new record buffer. After this setup the normal insert logic is used. Only that for delayed inserts write_delayed() is called instead of write_record(). It inserts the rows into a queue and signals the delayed insert thread instead of writing directly to the table. The delayed insert thread awakes from the signal. It locks the table, inserts the rows from the queue, unlocks the table, and waits for the next signal. It does normally live until a FLUSH TABLES or SHUTDOWN. */ #include "mysql_priv.h" #include "sp_head.h" #include "sql_trigger.h" Loading Loading @@ -1441,6 +1479,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) my_ptrdiff_t adjust_ptrs; Field **field,**org_field, *found_next_number_field; TABLE *copy; DBUG_ENTER("delayed_insert::get_local_table"); /* First request insert thread to get a lock */ status=1; Loading @@ -1464,31 +1503,47 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) } } /* Allocate memory for the TABLE object, the field pointers array, and one record buffer of reclength size. Normally a table has three record buffers of rec_buff_length size, which includes alignment bytes. Since the table copy is used for creating one record only, the other record buffers and alignment are unnecessary. */ client_thd->proc_info="allocating local table"; copy= (TABLE*) client_thd->alloc(sizeof(*copy)+ (table->s->fields+1)*sizeof(Field**)+ table->s->reclength); if (!copy) goto error; /* Copy the TABLE object. */ *copy= *table; copy->s= ©->share_not_to_be_used; // No name hashing bzero((char*) ©->s->name_hash,sizeof(copy->s->name_hash)); /* We don't need to change the file handler here */ /* Assign the pointers for the field pointers array and the record. */ field= copy->field= (Field**) (copy + 1); copy->record[0]= (byte*) (field + table->s->fields + 1); memcpy((char*) copy->record[0],(char*) table->record[0],table->s->reclength); /* Make a copy of all fields */ memcpy((char*) copy->record[0], (char*) table->record[0], table->s->reclength); /* Make a copy of all fields. The copied fields need to point into the copied record. This is done by copying the field objects with their old pointer values and then "move" the pointers by the distance between the original and copied records. That way we preserve the relative positions in the records. */ adjust_ptrs= PTR_BYTE_DIFF(copy->record[0], table->record[0]); found_next_number_field= table->found_next_number_field; for (org_field= table->field; *org_field; org_field++, field++) { if (!(*field= (*org_field)->new_field(client_thd->mem_root,copy))) return 0; if (!(*field= (*org_field)->new_field(client_thd->mem_root, copy, 1))) DBUG_RETURN(0); (*field)->orig_table= copy; // Remove connection (*field)->move_field(adjust_ptrs); // Point at copy->record[0] if (*org_field == found_next_number_field) Loading @@ -1515,14 +1570,14 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) /* Adjust lock_count. This table object is not part of a lock. */ copy->lock_count= 0; return copy; DBUG_RETURN(copy); /* Got fatal error */ error: tables_in_use--; status=1; pthread_cond_signal(&cond); // Inform thread about abort return 0; DBUG_RETURN(0); } Loading sql/sql_select.cc +3 −2 Original line number Diff line number Diff line Loading @@ -8017,7 +8017,8 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field, org_field->field_name, table, org_field->charset()); else new_field= org_field->new_field(thd->mem_root, table); new_field= org_field->new_field(thd->mem_root, table, table == org_field->table); if (new_field) { if (item) Loading Loading @@ -13062,7 +13063,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, saved value */ Field *field= item->field; item->result_field=field->new_field(thd->mem_root,field->table); item->result_field=field->new_field(thd->mem_root,field->table, 1); char *tmp=(char*) sql_alloc(field->pack_length()+1); if (!tmp) goto err; Loading sql/sql_trigger.cc +2 −1 Original line number Diff line number Diff line Loading @@ -747,7 +747,8 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) QQ: it is supposed that it is ok to use this function for field cloning... */ if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) if (!(*old_fld= (*fld)->new_field(&table->mem_root, table, table == (*fld)->table))) return 1; (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - table->record[0])); Loading Loading
sql/field.cc +13 −18 Original line number Diff line number Diff line Loading @@ -1515,7 +1515,8 @@ bool Field::optimize_range(uint idx, uint part) } Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type __attribute__((unused))) { Field *tmp; if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of()))) Loading @@ -1540,7 +1541,7 @@ Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table, uint new_null_bit) { Field *tmp; if ((tmp= new_field(root, new_table))) if ((tmp= new_field(root, new_table, table == new_table))) { tmp->ptr= new_ptr; tmp->null_ptr= new_null_ptr; Loading Loading @@ -6227,29 +6228,21 @@ uint Field_string::max_packed_col_length(uint max_length) } Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type) { Field *new_field; if (type() != MYSQL_TYPE_VAR_STRING || table == new_table) return Field::new_field(root, new_table); if (type() != MYSQL_TYPE_VAR_STRING || keep_type) return Field::new_field(root, new_table, keep_type); /* Old VARCHAR field which should be modified to a VARCHAR on copy This is done to ensure that ALTER TABLE will convert old VARCHAR fields to now VARCHAR fields. */ if ((new_field= new Field_varstring(field_length, maybe_null(), field_name, new_table, charset()))) { /* delayed_insert::get_local_table() needs a ptr copied from old table. This is what other new_field() methods do too. The above method of Field_varstring sets ptr to NULL. */ new_field->ptr= ptr; } return new_field; return new Field_varstring(field_length, maybe_null(), field_name, new_table, charset()); } /**************************************************************************** Loading Loading @@ -6741,9 +6734,11 @@ int Field_varstring::cmp_binary(const char *a_ptr, const char *b_ptr, } Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table) Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type) { Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table); Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, keep_type); if (res) res->length_bytes= length_bytes; return res; Loading
sql/field.h +4 −3 Original line number Diff line number Diff line Loading @@ -211,7 +211,8 @@ class Field */ virtual bool can_be_compared_as_longlong() const { return FALSE; } virtual void free() {} virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table); virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, char *new_ptr, uchar *new_null_ptr, uint new_null_bit); Loading Loading @@ -1033,7 +1034,7 @@ class Field_string :public Field_longstr { enum_field_types real_type() const { return FIELD_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table); Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); }; Loading Loading @@ -1105,7 +1106,7 @@ class Field_varstring :public Field_longstr { enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table); Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, char *new_ptr, uchar *new_null_ptr, uint new_null_bit); Loading
sql/sql_insert.cc +67 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,44 @@ /* Insert of records */ /* INSERT DELAYED Insert delayed is distinguished from a normal insert by lock_type == TL_WRITE_DELAYED instead of TL_WRITE. It first tries to open a "delayed" table (delayed_get_table()), but falls back to open_and_lock_tables() on error and proceeds as normal insert then. Opening a "delayed" table means to find a delayed insert thread that has the table open already. If this fails, a new thread is created and waited for to open and lock the table. If accessing the thread succeeded, in delayed_insert::get_local_table() the table of the thread is copied for local use. A copy is required because the normal insert logic works on a target table, but the other threads table object must not be used. The insert logic uses the record buffer to create a record. And the delayed insert thread uses the record buffer to pass the record to the table handler. So there must be different objects. Also the copied table is not included in the lock, so that the statement can proceed even if the real table cannot be accessed at this moment. Copying a table object is not a trivial operation. Besides the TABLE object there are the field pointer array, the field objects and the record buffer. After copying the field objects, their pointers into the record must be "moved" to point to the new record buffer. After this setup the normal insert logic is used. Only that for delayed inserts write_delayed() is called instead of write_record(). It inserts the rows into a queue and signals the delayed insert thread instead of writing directly to the table. The delayed insert thread awakes from the signal. It locks the table, inserts the rows from the queue, unlocks the table, and waits for the next signal. It does normally live until a FLUSH TABLES or SHUTDOWN. */ #include "mysql_priv.h" #include "sp_head.h" #include "sql_trigger.h" Loading Loading @@ -1441,6 +1479,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) my_ptrdiff_t adjust_ptrs; Field **field,**org_field, *found_next_number_field; TABLE *copy; DBUG_ENTER("delayed_insert::get_local_table"); /* First request insert thread to get a lock */ status=1; Loading @@ -1464,31 +1503,47 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) } } /* Allocate memory for the TABLE object, the field pointers array, and one record buffer of reclength size. Normally a table has three record buffers of rec_buff_length size, which includes alignment bytes. Since the table copy is used for creating one record only, the other record buffers and alignment are unnecessary. */ client_thd->proc_info="allocating local table"; copy= (TABLE*) client_thd->alloc(sizeof(*copy)+ (table->s->fields+1)*sizeof(Field**)+ table->s->reclength); if (!copy) goto error; /* Copy the TABLE object. */ *copy= *table; copy->s= ©->share_not_to_be_used; // No name hashing bzero((char*) ©->s->name_hash,sizeof(copy->s->name_hash)); /* We don't need to change the file handler here */ /* Assign the pointers for the field pointers array and the record. */ field= copy->field= (Field**) (copy + 1); copy->record[0]= (byte*) (field + table->s->fields + 1); memcpy((char*) copy->record[0],(char*) table->record[0],table->s->reclength); /* Make a copy of all fields */ memcpy((char*) copy->record[0], (char*) table->record[0], table->s->reclength); /* Make a copy of all fields. The copied fields need to point into the copied record. This is done by copying the field objects with their old pointer values and then "move" the pointers by the distance between the original and copied records. That way we preserve the relative positions in the records. */ adjust_ptrs= PTR_BYTE_DIFF(copy->record[0], table->record[0]); found_next_number_field= table->found_next_number_field; for (org_field= table->field; *org_field; org_field++, field++) { if (!(*field= (*org_field)->new_field(client_thd->mem_root,copy))) return 0; if (!(*field= (*org_field)->new_field(client_thd->mem_root, copy, 1))) DBUG_RETURN(0); (*field)->orig_table= copy; // Remove connection (*field)->move_field(adjust_ptrs); // Point at copy->record[0] if (*org_field == found_next_number_field) Loading @@ -1515,14 +1570,14 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) /* Adjust lock_count. This table object is not part of a lock. */ copy->lock_count= 0; return copy; DBUG_RETURN(copy); /* Got fatal error */ error: tables_in_use--; status=1; pthread_cond_signal(&cond); // Inform thread about abort return 0; DBUG_RETURN(0); } Loading
sql/sql_select.cc +3 −2 Original line number Diff line number Diff line Loading @@ -8017,7 +8017,8 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field, org_field->field_name, table, org_field->charset()); else new_field= org_field->new_field(thd->mem_root, table); new_field= org_field->new_field(thd->mem_root, table, table == org_field->table); if (new_field) { if (item) Loading Loading @@ -13062,7 +13063,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, saved value */ Field *field= item->field; item->result_field=field->new_field(thd->mem_root,field->table); item->result_field=field->new_field(thd->mem_root,field->table, 1); char *tmp=(char*) sql_alloc(field->pack_length()+1); if (!tmp) goto err; Loading
sql/sql_trigger.cc +2 −1 Original line number Diff line number Diff line Loading @@ -747,7 +747,8 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) QQ: it is supposed that it is ok to use this function for field cloning... */ if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) if (!(*old_fld= (*fld)->new_field(&table->mem_root, table, table == (*fld)->table))) return 1; (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - table->record[0])); Loading