MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:ingo Date:June 9 2006 4:35pm
Subject:bk commit into 5.0 tree (ingo:1.2132) BUG#16218
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of mydev. When mydev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository

  1.2132 06/06/09 18:35:31 ingo@stripped +2 -0
  Bug#16218 - Crash on insert delayed
  INSERT DELAYED crashed in 5.0 on a table with a varchar that 
  could be NULL and was created pre-5.0.
  In case of INSERT DELAYED the open table is copied from the
  delayed insert thread to be able to create a record for the 
  queue. When copying the fields, a method was used that did 
  not set up some pointers into the record buffer of the table.
  For Bug 13707 (Server crash with INSERT DELAYED on MyISAM table)
  I did already set up one of the pointers in that method.
  But now it turned out that more pointers are required.
  Since they are set up in another field method, I undid the
  change of Bug 13707. I do now use the other method instead.
  No test case as this is a migration problem. One needs to
  create a table with 4.x and use it with 5.x. I added two
  test scripts to the bug report.

    1.188 06/06/09 18:35:19 ingo@stripped +73 -12
    Bug#16218 - Crash on insert delayed
    Changed the copying of the fields to use Field::new_key_field(). 
    This method initializes the required field elements 'ptr',
    'null_ptr', and 'null_bit'. 

    1.305 06/06/09 18:35:19 ingo@stripped +2 -11
    Bug#16218 - Crash on insert delayed
    Undid the change from Bug 13707 (Server crash with INSERT 
    DELAYED on MyISAM table).
    I solved both bugs in sql/ by copying the
    fields with Field::new_key_field(). This method initializes
    the required field elements 'ptr', 'null_ptr', and 'null_bit'. 
    There is no need to do this in Field::new_field() too.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	ingo
# Host:	chilla.local
# Root:	/home/mydev/mysql-5.0-bug16218

--- 1.304/sql/	2006-04-10 23:29:55 +02:00
+++ 1.305/sql/	2006-06-09 18:35:19 +02:00
@@ -6236,17 +6236,8 @@ Field *Field_string::new_field(MEM_ROOT 
     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());

--- 1.187/sql/	2006-04-11 20:46:20 +02:00
+++ 1.188/sql/	2006-06-09 18:35:19 +02:00
@@ -17,6 +17,44 @@
 /* Insert of records */
+  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"
@@ -1466,6 +1504,7 @@ TABLE *delayed_insert::get_local_table(T
   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 */
@@ -1489,31 +1528,53 @@ TABLE *delayed_insert::get_local_table(T
+  /*
+    Allocate memory for the TABLE object, the field pointers array,
+    and one record of reclength size. Normally a table has three
+    records of rec_buff_length size. Since the table copy is used
+    for creating one record only, the other records are unnecessary.
+  */
   client_thd->proc_info="allocating local table";
   copy= (TABLE*) client_thd->alloc(sizeof(*copy)+
   if (!copy)
     goto error;
+  /* Copy the TABLE object. */
   *copy= *table;
   copy->s= &copy->share_not_to_be_used;
   // No name hashing
   bzero((char*) &copy->s->name_hash,sizeof(copy->s->name_hash));
   /* We don't need to change the file handler here */
-  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);
+  /* 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 */
-  adjust_ptrs=PTR_BYTE_DIFF(copy->record[0],table->record[0]);
+  /*
+    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++)
+  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;
+    /*
+      Using new_field() is not safe here as it can null the pointers
+      when converting field types (e.g. old varchar -> new varchar).
+    */
+    if (!(*field= (*org_field)->new_key_field(client_thd->mem_root, copy,
+                                              (*org_field)->ptr,
+                                              (*org_field)->null_ptr,
+                                              (*org_field)->null_bit)))
+      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)
@@ -1540,14 +1601,14 @@ TABLE *delayed_insert::get_local_table(T
   /* Adjust lock_count. This table object is not part of a lock. */
   copy->lock_count= 0;
-  return copy;
+  DBUG_RETURN(copy);
   /* Got fatal error */
   pthread_cond_signal(&cond);			// Inform thread about abort
-  return 0;
bk commit into 5.0 tree (ingo:1.2132) BUG#16218ingo9 Jun