MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Patrick Galbraith Date:June 8 2006 1:59am
Subject:bk commit into 5.0 tree (patg:1.2133) BUG#18764
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of patg. When patg 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
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2133 06/06/07 18:59:09 patg@stripped +4 -0
  BUG #19773
  
  Crash when using multi-table updates with federated tables
  
  BUG #18764
  
  Delete conditions causing inconsistencies in Federated tables
  
  See comments in each individual file for details of changes

  sql/ha_federated.h
    1.25 06/06/07 18:59:03 patg@stripped +11 -6
    BUG #19773
    BUG #18764
    

  sql/ha_federated.cc
    1.61 06/06/07 18:59:03 patg@stripped +539 -178
    BUG #19773
    BUG #18764
    
    - Rewrote logic in update_row
      * using ptr_diff for dealing with old and new record
      * logic for determining there is a primary key or not and if so use it
      * logic to see if column value has changed
      * more comments
    - Hash by entire connection scheme
    - Separated result set for table scan and index scan
    - Rewrote ::position and ::rnd_pos to store position - if primary key use 
      primary key, if not, use record buffer.
    - Rewrote get_share to store hash with connect string
    - Rewrote records_in_range to use explain to get realistic number
    - delete_row rewrote logic similar to how update_row handles indexes in query 
      construction
    - delete_row added subtration of "records" by affected->rows
    - Added read_next to handle what rnd_next used to do (converting raw record
      to query and vice versa)
    

  mysql-test/t/federated.test
    1.23 06/06/07 18:59:03 patg@stripped +54 -0
    BUG #19773
    
    Results for multi-table updates and deletes

  mysql-test/r/federated.result
    1.27 06/06/07 18:59:03 patg@stripped +90 -0
    BUG #19773 Added multi-table deletes and updates (Monty designed this test)

# 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:	patg
# Host:	govinda.patg.net
# Root:	/home/patg/mysql-build/mysql-5.0

--- 1.26/mysql-test/r/federated.result	2006-02-28 02:17:35 -08:00
+++ 1.27/mysql-test/r/federated.result	2006-06-07 18:59:03 -07:00
@@ -1601,6 +1601,96 @@
 5	Torkel	0	0
 DROP TABLE federated.t1;
 DROP TABLE federated.bug_17377_table;
+drop table if exists t1;
+drop table if exists t2;
+drop table if exists federated.t1;
+drop table if exists federated.t2;
+create table federated.t1 (i1 int, i2 int, i3 int);
+create table federated.t2 (id int, c1 varchar(20), c2 varchar(20));
+create table federated.t1 (i1 int, i2 int, i3 int) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:9308/federated/t1';
+create table federated.t2 (id int, c1 varchar(20), c2 varchar(20)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:9308/federated/t2';
+insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2);
+insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test");
+select * from federated.t1 order by i1;
+i1	i2	i3
+1	5	10
+2	2	2
+3	7	12
+4	5	2
+9	10	15
+select * from federated.t2;
+id	c1	c2
+9	abc	def
+5	opq	lmn
+2	test t	t test
+update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id;
+select * from federated.t1 order by i1;
+i1	i2	i3
+1	5	10
+2	15	2
+3	7	12
+4	5	2
+9	15	15
+select * from federated.t2 order by id;
+id	c1	c2
+2	test t	ppc
+5	opq	lmn
+9	abc	ppc
+delete   federated.t1.*,federated.t2.* from federated.t1,federated.t2 where t1.i2=t2.id;
+select * from federated.t1 order by i1;
+i1	i2	i3
+2	15	2
+3	7	12
+9	15	15
+select * from federated.t2 order by id;
+id	c1	c2
+2	test t	ppc
+9	abc	ppc
+drop table federated.t1, federated.t2;
+drop table federated.t1, federated.t2;
+create table federated.t1 (i1 int, i2 int, i3 int, primary key (i1));
+create table federated.t2 (id int, c1 varchar(20), c2 varchar(20), primary key (id));
+create table federated.t1 (i1 int auto_increment not null, i2 int, i3 int, primary key (i1)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:9308/federated/t1';
+create table federated.t2 (id int auto_increment not null, c1 varchar(20), c2 varchar(20), primary key(id)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:9308/federated/t2';
+insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2);
+insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test");
+select * from federated.t1 order by i1;
+i1	i2	i3
+1	5	10
+2	2	2
+3	7	12
+4	5	2
+9	10	15
+select * from federated.t2 order by id;
+id	c1	c2
+2	test t	t test
+5	opq	lmn
+9	abc	def
+update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id;
+select * from federated.t1 order by i1;
+i1	i2	i3
+1	5	10
+2	15	2
+3	7	12
+4	5	2
+9	15	15
+select * from federated.t2 order by id;
+id	c1	c2
+2	test t	ppc
+5	opq	lmn
+9	abc	ppc
+delete federated.t1.*,federated.t2.* from federated.t1,federated.t2 where t1.i2=t2.id;
+select * from federated.t1 order by i1;
+i1	i2	i3
+2	15	2
+3	7	12
+9	15	15
+select * from federated.t2 order by id;
+id	c1	c2
+2	test t	ppc
+9	abc	ppc
+drop table federated.t1, federated.t2;
+drop table federated.t1, federated.t2;
 DROP TABLE IF EXISTS federated.t1;
 DROP DATABASE IF EXISTS federated;
 DROP TABLE IF EXISTS federated.t1;

--- 1.22/mysql-test/t/federated.test	2006-02-28 02:17:35 -08:00
+++ 1.23/mysql-test/t/federated.test	2006-06-07 18:59:03 -07:00
@@ -1309,5 +1309,59 @@
 connection slave;
 DROP TABLE federated.bug_17377_table;
 
+#
+# BUG 19773 Crash when using multi-table updates, deletes
+# with federated tables
+#
+connection slave;
+--disable_warnings
+drop table if exists t1;
+drop table if exists t2;
+drop table if exists federated.t1;
+drop table if exists federated.t2;
+--enable_warnings
+
+create table federated.t1 (i1 int, i2 int, i3 int);
+create table federated.t2 (id int, c1 varchar(20), c2 varchar(20));
+
+connection master;
+eval create table federated.t1 (i1 int, i2 int, i3 int) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:$SLAVE_MYPORT/federated/t1';
+eval create table federated.t2 (id int, c1 varchar(20), c2 varchar(20)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:$SLAVE_MYPORT/federated/t2';
+insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2);
+insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test");
+select * from federated.t1 order by i1;
+select * from federated.t2;
+update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id;
+select * from federated.t1 order by i1;
+select * from federated.t2 order by id;
+delete   federated.t1.*,federated.t2.* from federated.t1,federated.t2 where t1.i2=t2.id;
+select * from federated.t1 order by i1;
+select * from federated.t2 order by id;
+drop table federated.t1, federated.t2;
+connection slave;
+drop table federated.t1, federated.t2;
+
+# Test multi updates and deletes with keys
+connection slave;
+create table federated.t1 (i1 int, i2 int, i3 int, primary key (i1));
+create table federated.t2 (id int, c1 varchar(20), c2 varchar(20), primary key (id));
+
+connection master;
+eval create table federated.t1 (i1 int auto_increment not null, i2 int, i3 int, primary key (i1)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:$SLAVE_MYPORT/federated/t1';
+eval create table federated.t2 (id int auto_increment not null, c1 varchar(20), c2 varchar(20), primary key(id)) ENGINE=FEDERATED CONNECTION='mysql://root@stripped:$SLAVE_MYPORT/federated/t2';
+insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2);
+insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test");
+select * from federated.t1 order by i1;
+select * from federated.t2 order by id;
+update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id;
+select * from federated.t1 order by i1;
+select * from federated.t2 order by id;
+delete federated.t1.*,federated.t2.* from federated.t1,federated.t2 where t1.i2=t2.id;
+select * from federated.t1 order by i1;
+select * from federated.t2 order by id;
+drop table federated.t1, federated.t2;
+
+connection slave;
+drop table federated.t1, federated.t2;
 
 source include/federated_cleanup.inc;

--- 1.60/sql/ha_federated.cc	2006-02-28 02:17:35 -08:00
+++ 1.61/sql/ha_federated.cc	2006-06-07 18:59:03 -07:00
@@ -395,8 +395,8 @@
 static byte *federated_get_key(FEDERATED_SHARE *share, uint *length,
                                my_bool not_used __attribute__ ((unused)))
 {
-  *length= share->table_name_length;
-  return (byte*) share->table_name;
+  *length= share->connect_string_length;
+  return (byte*) share->scheme;
 }
 
 /*
@@ -416,7 +416,7 @@
   DBUG_ENTER("federated_db_init");
   if (pthread_mutex_init(&federated_mutex, MY_MUTEX_INIT_FAST))
     goto error;
-  if (hash_init(&federated_open_tables, system_charset_info, 32, 0, 0,
+  if (hash_init(&federated_open_tables, &my_charset_bin, 32, 0, 0,
                     (hash_get_key) federated_get_key, 0, 0))
   {
     VOID(pthread_mutex_destroy(&federated_mutex));
@@ -531,16 +531,18 @@
     query.append(FEDERATED_STAR);
     query.append(FEDERATED_FROM);
     query.append(FEDERATED_BTICK);
-    escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_name,
+    escape_string_for_mysql(&my_charset_bin, (char*)escaped_table_name,
                             sizeof(escaped_table_name),
                             share->table_name,
                             share->table_name_length);
-    query.append(escaped_table_name);
+    DBUG_PRINT("info", ("escaped_table_name %s query %s",
+                        escaped_table_name, query.c_ptr_quick()));
+    query.append(escaped_table_name, strlen(escaped_table_name));
     query.append(FEDERATED_BTICK);
     query.append(FEDERATED_WHERE);
     query.append(FEDERATED_FALSE);
 
-    DBUG_PRINT("info", ("check_foreign_data_source query %s", 
+    DBUG_PRINT("info", ("ha_federated SQL QUERY query %s", 
                         query.c_ptr_quick()));
     if (mysql_real_query(mysql, query.ptr(), query.length()))
     {
@@ -634,11 +636,12 @@
                       table->s->connect_string.str));
   share->scheme= my_strdup_with_length((const byte*)table->s->
                                        connect_string.str, 
-                                       table->s->connect_string.length,
+                                       table->s->connect_string.length + 1,
                                        MYF(0));
 
   // Add a null for later termination of table name
   share->scheme[table->s->connect_string.length]= 0;
+  share->connect_string_length= table->s->connect_string.length;
   DBUG_PRINT("info",("parse_url alloced share->scheme %lx", share->scheme));
 
   /*
@@ -704,7 +707,7 @@
   share->table_name++;
 
   share->table_name_length= strlen(share->table_name);
-      
+ 
   /* make sure there's not an extra / */
   if ((strchr(share->table_name, '/')))
     goto error;
@@ -740,8 +743,8 @@
 
 ha_federated::ha_federated(TABLE *table_arg)
   :handler(&federated_hton, table_arg),
-  mysql(0), stored_result(0),
-  ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0)
+  mysql(0), table_scan_result(0), index_scan_result(0),
+  status_up_to_date(0), full_table_scan(0)
 {}
 
 
@@ -752,6 +755,7 @@
     convert_row_to_internal_format()
       record    Byte pointer to record
       row       MySQL result set row from fetchrow()
+      result
 
   DESCRIPTION
     This method simply iterates through a row returned via fetchrow with
@@ -764,14 +768,17 @@
     0   After fields have had field values stored from record
  */
 
-uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row)
+uint ha_federated::convert_row_to_internal_format(
+                                                  byte *record,
+                                                  MYSQL_ROW row,
+                                                  MYSQL_RES *result)
 {
   ulong *lengths;
   Field **field;
   DBUG_ENTER("ha_federated::convert_row_to_internal_format");
 
-  lengths= mysql_fetch_lengths(stored_result);
-  memset(record, 0, table->s->null_bytes);
+  lengths= mysql_fetch_lengths(result);
+  memset(record, 0xff, table->s->null_bytes);
 
   for (field= table->field; *field; field++)
   {
@@ -1299,12 +1306,12 @@
 
 static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table)
 {
-  char *select_query, *tmp_table_name;
+  char *select_query;
   char query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   uint tmp_table_name_length;
   Field **field;
   String query(query_buffer, sizeof(query_buffer), &my_charset_bin);
-  FEDERATED_SHARE *share;
+  FEDERATED_SHARE *share= NULL, tmp_share;
   /*
     In order to use this string, we must first zero it's length,
     or it will contain garbage
@@ -1312,12 +1319,13 @@
   query.length(0);
 
   pthread_mutex_lock(&federated_mutex);
-  tmp_table_name= (char *)table->s->table_name;
-  tmp_table_name_length= (uint) strlen(tmp_table_name);
+
+  if (parse_url(&tmp_share, table, 0))
+    goto error;
 
   if (!(share= (FEDERATED_SHARE *) hash_search(&federated_open_tables,
-                                               (byte*) table_name,
-                                               strlen(table_name))))
+                                         (byte*) tmp_share.scheme,
+                                         tmp_share.connect_string_length)))
   {
     query.set_charset(system_charset_info);
     query.append(FEDERATED_SELECT);
@@ -1335,19 +1343,14 @@
     if (!(share= (FEDERATED_SHARE *)
           my_multi_malloc(MYF(MY_WME),
                           &share, sizeof(*share),
-                          &tmp_table_name, tmp_table_name_length+ 1,
                           &select_query,
                           query.length()+table->s->connect_string.length+1,
                           NullS)))
-    {
-      pthread_mutex_unlock(&federated_mutex);
-      return NULL;
-    }
-
-    if (parse_url(share, table, 0))
       goto error;
 
-    query.append(share->table_name, share->table_name_length);
+    memcpy(share, &tmp_share, sizeof(tmp_share));
+
+    query.append(share->table_name);
     query.append(FEDERATED_BTICK);
     share->select_query= select_query;
     strmov(share->select_query, query.ptr());
@@ -1367,12 +1370,8 @@
   return share;
 
 error:
-  pthread_mutex_unlock(&federated_mutex);
-  if (share->scheme)
-  {
-    my_free((gptr) share->scheme, MYF(0));
-    share->scheme= 0;
-  }
+  my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR));
+  my_free((gptr) share, MYF(MY_ALLOW_ZERO_PTR));
   return NULL;
 }
 
@@ -1391,8 +1390,11 @@
   if (!--share->use_count)
   {
     hash_delete(&federated_open_tables, (byte*) share);
-    my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR));
-    share->scheme= 0;
+    if (share->scheme)
+    {
+      my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR));
+      share->scheme= 0;
+    }
     if (share->socket)
     {
       my_free((gptr) share->socket, MYF(MY_ALLOW_ZERO_PTR));
@@ -1409,7 +1411,7 @@
 }
 
 
-ha_rows ha_federated::records_in_range(uint inx, key_range *start_key,
+ha_rows ha_federated::records_in_range(uint i, key_range *start_key,
                                    key_range *end_key)
 {
   /*
@@ -1419,8 +1421,63 @@
   force the issue
 
 */
+  int error;
+  ha_rows num_records= records;
+  char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
+  MYSQL_RES *result= 0;
+  MYSQL_ROW row;
+  String sql_query(sql_query_buffer,
+                   sizeof(sql_query_buffer),
+                   &my_charset_bin);
+
   DBUG_ENTER("ha_federated::records_in_range");
-  DBUG_RETURN(FEDERATED_RECORDS_IN_RANGE);
+
+  if (start_key == NULL && end_key == NULL)
+    goto error;
+
+  sql_query.length(0);
+  sql_query.append(FEDERATED_EXPLAIN);
+  sql_query.append(share->select_query);
+  create_where_from_key(&sql_query,
+                        &table->key_info[i],
+                        start_key, end_key, 0);
+
+  DBUG_PRINT("info", ("ha_federated SQL QUERY sql_query %s",
+                      sql_query.c_ptr_quick()));
+  if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length()))
+    goto error;
+
+  sql_query.length(0);
+
+  result= mysql_store_result(mysql);
+  DBUG_PRINT("info", ("ha_federated result stored %lx", result));
+
+  if (!result)
+    goto error;
+
+  if (!mysql_num_rows(result))
+    goto error;
+
+  while ((row= mysql_fetch_row(result)))
+  {
+    /* number of rows this query will produce */
+    if (row[8] != NULL)
+    {
+      ha_rows rows= (ha_rows) my_strtoll10(row[8], (char**)0, &error);
+      /* set num_records to the value of rows only if rows < num_records */
+      if (rows < num_records)
+        num_records= rows;
+    }
+  }
+
+error:
+  if (result)
+  {
+    DBUG_PRINT("info", ("error: ha_federated result freed %lx", result));
+    mysql_free_result(result);
+  }
+
+  DBUG_RETURN(num_records);
 }
 /*
   If frm_error() is called then we will use this to to find out
@@ -1476,6 +1533,11 @@
     with transactions
   */
   mysql->reconnect= 1;
+
+  ref_length= table->s->primary_key != MAX_KEY ?
+    table->key_info[table->s->primary_key].key_length :
+    table->s->reclength;
+
   DBUG_RETURN(0);
 }
 
@@ -1497,15 +1559,26 @@
   DBUG_ENTER("ha_federated::close");
 
   /* free the result set */
-  if (stored_result)
+  if (table_scan_result)
   {
     DBUG_PRINT("info",
-               ("mysql_free_result result at address %lx", stored_result));
-    mysql_free_result(stored_result);
-    stored_result= 0;
+               ("ha_federated table_scan_result freed  %lx",
+                table_scan_result));
+    mysql_free_result(table_scan_result);
+    table_scan_result= 0;
+  }
+  if (index_scan_result)
+  {
+    DBUG_PRINT("info",
+               ("ha_federated index_scan_result freed  %lx",
+                index_scan_result));
+    mysql_free_result(index_scan_result);
+    index_scan_result= 0;
   }
   /* Disconnect from mysql */
-  mysql_close(mysql);
+  if (mysql)
+    mysql_close(mysql);
+
   retval= free_share(share);
   DBUG_RETURN(retval);
 
@@ -1697,6 +1770,8 @@
 
   DBUG_PRINT("info", ("insert query %s", insert_string.c_ptr_quick()));
 
+  DBUG_PRINT("info", ("ha_federated SQL QUERY insert_string %s",
+                      insert_string.c_ptr_quick()));
   if (mysql_real_query(mysql, insert_string.ptr(), insert_string.length()))
   {
     DBUG_RETURN(stash_remote_error());
@@ -1744,6 +1819,8 @@
   query.append(share->table_name, share->table_name_length);
   query.append(FEDERATED_BTICK);
 
+  DBUG_PRINT("info", ("ha_federated SQL QUERY query %s",
+                      query.c_ptr_quick()));
   if (mysql_real_query(mysql, query.ptr(), query.length()))
   {
     DBUG_RETURN(stash_remote_error());
@@ -1773,6 +1850,8 @@
   if (check_opt->sql_flags & TT_USEFRM)
     query.append(FEDERATED_USE_FRM);
       
+  DBUG_PRINT("info", ("ha_federated SQL QUERY query %s",
+                      query.c_ptr_quick()));
   if (mysql_real_query(mysql, query.ptr(), query.length()))
   {
     DBUG_RETURN(stash_remote_error());
@@ -1801,19 +1880,12 @@
 
 int ha_federated::update_row(const byte *old_data, byte *new_data)
 {
-  /*
-    This used to control how the query was built. If there was a
-    primary key, the query would be built such that there was a where
-    clause with only that column as the condition. This is flawed,
-    because if we have a multi-part primary key, it would only use the
-    first part! We don't need to do this anyway, because
-    read_range_first will retrieve the correct record, which is what
-    is used to build the WHERE clause. We can however use this to
-    append a LIMIT to the end if there is NOT a primary key. Why do
-    this? Because we only are updating one record, and LIMIT enforces
-    this.
-  */
-  bool has_a_primary_key= (table->s->primary_key == 0 ? TRUE : FALSE);
+  bool no_primary_key= (table->s->primary_key == MAX_KEY), column_has_changed;
+  /* difference between old_data and table->record[0] */
+  my_ptrdiff_t old_ptr= (my_ptrdiff_t)(old_data - table->record[0]);
+  /* difference between new_data and table->record[0] */
+  my_ptrdiff_t new_ptr= (my_ptrdiff_t)(new_data - table->record[0]);
+  my_ptrdiff_t old_new_ptr_diff = (my_ptrdiff_t)(old_data - new_data);
   /* 
     buffers for following strings
   */
@@ -1852,6 +1924,11 @@
   update_string.append(share->table_name);
   update_string.append(FEDERATED_BTICK);
   update_string.append(FEDERATED_SET);
+  /*
+    at this point, we have
+
+    "UPDATE `tablename` SET "
+  */
 
 /*
   In this loop, we want to match column names to values being inserted
@@ -1865,66 +1942,195 @@
 
   for (Field **field= table->field; *field; field++)
   {
-    where_string.append((*field)->field_name);
-    update_string.append((*field)->field_name);
-    update_string.append(FEDERATED_EQ);
-
-    if ((*field)->is_null())
-      new_field_value.append(FEDERATED_NULL);
-    else
+    /* get the field value from pointed to by new_ptr */
+    (*field)->move_field(new_ptr);
+    /*
+      if the value of this column is changing, or if there is no primary key
+      start by appending the column name, because we know that at least
+      we will be appending some where clause condition for that column
+    */
+    if ((column_has_changed=
+         (*field)->cmp_offset(old_new_ptr_diff) != 0) ||
+        no_primary_key)
     {
-      /* otherwise = */
-      (*field)->val_str(&new_field_value);
-      (*field)->quote_data(&new_field_value);
+      where_string.append((*field)->field_name);
+      /*
+        where string now has "columnname"
+      */
+      /*
+        if the value of the column has changed, then we know that the
+        what we append to update string will be "columnname ="
+      */
+      if (column_has_changed)
+      {
+        update_string.append((*field)->field_name);
+        update_string.append(FEDERATED_EQ);
+        /*
+          update string now has
+        "UPDATE `tablename` SET "columnname = "
+        */
+      }
 
-      if (!field_in_record_is_null(table, *field, (char*) old_data))
-        where_string.append(FEDERATED_EQ);
-    }
+      /*
+        if field is null, append " NULL " into the value that will
+        be appended to the update string
+      */
+      if ((*field)->is_null())
+        new_field_value.append(FEDERATED_NULL);
+      else
+      {
+        /*
+          otherwise append the value that is will be appended to the
+          update string
+        */
+        (*field)->val_str(&new_field_value);
+        (*field)->quote_data(&new_field_value);
+        /*
+          if the value of the existing record (old_data) is not null, we
+          know to append an ' = ' to the where clause
+        */
+        if (!field_in_record_is_null(table, *field, (char*) old_data))
+          /* WHERE string now has "columnname = " */
+          where_string.append(FEDERATED_EQ);
+      }
+      /*
+        if the existing record's value for this column is NULL, then
+        we need to append " IS NULL " to the WHERE clause string
+      */
+      if (field_in_record_is_null(table, *field, (char*) old_data))
+        /* WHERE string is now "columnname IS NULL" */
+        where_string.append(FEDERATED_ISNULL);
+      /*
+        if the existing record's value for this column has a value,
+        then append the value of that column to the WHERE clause string
+        via the ***magic*** of val_str
+      */
+      else
+      {
+        (*field)->val_str(&old_field_value,
+                          (char*) (old_data + (*field)->offset()));
+        (*field)->quote_data(&old_field_value);
+        /*
+          WHERE string is now "columnname = 'someval'" (not quoted if numeric)
+        */
+        where_string.append(old_field_value);
+      }
 
-    if (field_in_record_is_null(table, *field, (char*) old_data))
-      where_string.append(FEDERATED_ISNULL);
-    else
-    {
-      (*field)->val_str(&old_field_value,
-                        (char*) (old_data + (*field)->offset()));
-      (*field)->quote_data(&old_field_value);
-      where_string.append(old_field_value);
+      /*
+        if the value of the column has changed, append that value
+        to the update string
+      */
+      if (column_has_changed)
+      {
+        /*
+          update string now has
+        "UPDATE `tablename` SET columnname = 'somevalue'"
+
+        Note: "somevalue" could be numeric, char, or NULL
+        */
+        update_string.append(new_field_value);
+        update_string.append(FEDERATED_COMMA);
+      }
+      where_string.append(FEDERATED_AND);
+      /* WHERE string is "columname = 'someval' AND " */
+
+      /* reset these temp field value strings to 0 */
+      new_field_value.length(0);
+      old_field_value.length(0);
     }
+    /* reset field */
+    (*field)->move_field(-new_ptr);
+  }
+  /* get rid of trailing comma by decrementing length of string by that much */
+  update_string.length(update_string.length() - (FEDERATED_COMMA_LEN - 1));
+  /* get rid of trailing AND by decrementing length of string by that much */
+  where_string.length(where_string.length() - (FEDERATED_AND_LEN - 1));
+  /*
+    update string now
+   "UPDATE `tablename` SET columnname = 'someval1',..., columnN = 'somevalueN'"
+    N being number of fields, even if 1
+  */
+  update_string.append(FEDERATED_WHERE);
 
-    update_string.append(new_field_value);
-    new_field_value.length(0);
+  /*
+    if there is a primary key, go through all the parts of the primary key
+    and build a WHERE clause out of those column(s)
+  */
+  if (!no_primary_key)
+  {
+    /* the primary key */
+    KEY *key= table->key_info + table->s->primary_key;
+    uint count= key->usable_key_parts;
 
-    /*
-      Only append conjunctions if we have another field in which
-      to iterate
-    */
-    if (*(field + 1))
+    /* if there is some content in the where clause */
+    if (where_string.length())
+      where_string.append(FEDERATED_AND);
+
+    /* get every part of the key */
+    for (KEY_PART_INFO *part= key->key_part; count; part++, count--)
     {
-      update_string.append(FEDERATED_COMMA);
+      Field *cur_field= part->field;
+      old_field_value.length(0);
+      where_string.append(cur_field->field_name);
+
+      cur_field->move_field(old_ptr);
+
+      if (cur_field->is_null())
+      {
+        where_string.append(FEDERATED_IS);
+        old_field_value.append(FEDERATED_NULL);
+      }
+      else
+      {
+        where_string.append(FEDERATED_EQ);
+        cur_field->val_str(&old_field_value);
+        cur_field->quote_data(&old_field_value);
+        DBUG_PRINT("info", ("old_field_value %s",
+                            old_field_value.c_ptr_quick()));
+      }
+      /* restore field */
+      cur_field->move_field(-old_ptr);
+      where_string.append(old_field_value);
       where_string.append(FEDERATED_AND);
     }
-    old_field_value.length(0);
+    /*
+      Get rid of trailing AND by decrementing length of string by that much
+    */
+    where_string.length(where_string.length() - (FEDERATED_AND_LEN - 1));
+    /*
+      Now, add the WHERE string we've built to the update string for a
+      complete UPDATE SQL statement
+    */
   }
-  update_string.append(FEDERATED_WHERE);
+
   update_string.append(where_string);
   /*
-    If this table has not a primary key, then we could possibly
-    update multiple rows. We want to make sure to only update one!
+    If this table has not a primary key, then we could possibly update
+    multiple rows. We want to make sure to only update one!
   */
-  if (!has_a_primary_key)
+  if (no_primary_key)
     update_string.append(FEDERATED_LIMIT1);
 
+  DBUG_PRINT("info", ("ha_federated SQL QUERY update_string %s",
+                      update_string.c_ptr_quick()));
   if (mysql_real_query(mysql, update_string.ptr(), update_string.length()))
-  {
     DBUG_RETURN(stash_remote_error());
-  }
-  DBUG_RETURN(0);
+
+    if (mysql->affected_rows != 0)
+    {
+      if (mysql->affected_rows > 1)
+        sql_print_warning("%d rows affected on slave", mysql->affected_rows);
+
+      DBUG_RETURN(0);
+    }
+    sql_print_warning("row not found or row not changed");
+    DBUG_RETURN(my_errno=HA_ERR_RECORD_CHANGED);
 }
 
 /*
   This will delete a row. 'buf' will contain a copy of the row to be =deleted.
   The server will call this right after the current row has been called (from
-  either a previous rnd_nexT() or index call).
+  either a previous rnd_next() or index call).
   If you keep a pointer to the last row or can access a primary key it will
   make doing the deletion quite a bit easier.
   Keep in mind that the server does no guarentee consecutive deletions.
@@ -1938,6 +2144,8 @@
 
 int ha_federated::delete_row(const byte *buf)
 {
+  bool no_primary_key= (table->s->primary_key == MAX_KEY);
+  my_ptrdiff_t old_ptr= (my_ptrdiff_t) (buf - table->record[0]);
   char delete_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   char data_buffer[FEDERATED_QUERY_BUFFER_SIZE];
 
@@ -1949,48 +2157,97 @@
   delete_string.append(FEDERATED_DELETE);
   delete_string.append(FEDERATED_FROM);
   delete_string.append(FEDERATED_BTICK);
-  delete_string.append(share->table_name);
+  delete_string.append(share->table_name, share->table_name_length);
   delete_string.append(FEDERATED_BTICK);
   delete_string.append(FEDERATED_WHERE);
 
-  for (Field **field= table->field; *field; field++)
+  if (no_primary_key)
   {
-    Field *cur_field= *field;
-    data_string.length(0);
-    delete_string.append(cur_field->field_name);
-
-    if (cur_field->is_null())
+    for (Field **field= table->field; *field; field++)
     {
-      delete_string.append(FEDERATED_IS);
-      data_string.append(FEDERATED_NULL);
+      Field *cur_field= *field;
+      data_string.length(0);
+      delete_string.append(cur_field->field_name);
+
+      cur_field->move_field(old_ptr);
+
+      if (cur_field->is_null())
+      {
+        delete_string.append(FEDERATED_IS);
+        data_string.append(FEDERATED_NULL);
+      }
+      else
+      {
+        delete_string.append(FEDERATED_EQ);
+        cur_field->val_str(&data_string);
+        cur_field->quote_data(&data_string);
+      }
+      cur_field->move_field(-old_ptr);
+
+      delete_string.append(data_string);
+      delete_string.append(FEDERATED_AND);
     }
-    else
+    /* Remove trailing AND */
+    delete_string.length(delete_string.length() - (FEDERATED_AND_LEN - 1));
+  }
+  else
+  {
+    /* there is a primary key, so use it for the WHERE clause string */
+    KEY *key= table->key_info + table->s->primary_key;
+    uint count= key->usable_key_parts;
+    for (KEY_PART_INFO *part= key->key_part; count; part++, count--)
     {
-      delete_string.append(FEDERATED_EQ);
-      cur_field->val_str(&data_string);
-      cur_field->quote_data(&data_string);
-    }
+      Field *cur_field= part->field;
+      data_string.length(0);
+      delete_string.append(cur_field->field_name);
+
+      cur_field->move_field(old_ptr);
+      if (cur_field->is_null())
+      {
+        /* append " IS NULL " to WHERE string */
+        delete_string.append(FEDERATED_IS);
+        data_string.append(FEDERATED_NULL);
+      }
+      else
+      {
+        /* append " = 'somevalue' " to WHERE string */
+        delete_string.append(FEDERATED_EQ);
+        cur_field->val_str(&data_string);
+      }
+      /* restore field */
+      cur_field->move_field(-old_ptr);
 
-    delete_string.append(data_string);
-    delete_string.append(FEDERATED_AND);
+      delete_string.append(data_string);
+      delete_string.append(FEDERATED_AND);
+    }
+    /* drop the trailing " AND " */
+    delete_string.length(delete_string.length() - (FEDERATED_AND_LEN - 1));
   }
-  delete_string.length(delete_string.length()-5); // Remove trailing AND
 
   delete_string.append(FEDERATED_LIMIT1);
   DBUG_PRINT("info",
-             ("Delete sql: %s", delete_string.c_ptr_quick()));
+             ("ha_federated SQL QUERY Delete sql: %s", delete_string.c_ptr_quick()));
   if (mysql_real_query(mysql, delete_string.ptr(), delete_string.length()))
   {
     DBUG_RETURN(stash_remote_error());
   }
   deleted+= mysql->affected_rows;
+  records -= mysql->affected_rows;
   DBUG_PRINT("info",
              ("rows deleted %d rows deleted for all time %d",
              int(mysql->affected_rows), deleted));
 
-  DBUG_RETURN(0);
-}
+  if (mysql->affected_rows != 0)
+  {
+    if (mysql->affected_rows > 1)
+      sql_print_warning("%d rows affected on slave", mysql->affected_rows);
+
+    DBUG_RETURN(0);
+  }
 
+  sql_print_warning("row not found or row changed");
+  DBUG_RETURN(my_errno= HA_ERR_RECORD_CHANGED);
+}
 
 /*
   Positions an index cursor to the index specified in the handle. Fetches the
@@ -2025,7 +2282,7 @@
   char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   char index_value[STRING_BUFFER_USUAL_SIZE];
   char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
-  String index_string(index_value, 
+  String index_string(index_value,
                       sizeof(index_value),
                       &my_charset_bin);
   String sql_query(sql_query_buffer,
@@ -2055,15 +2312,15 @@
               index, (char*) key, index_string.c_ptr_quick(),
               index_string.length()));
 
-  DBUG_PRINT("info",
-             ("current position %d sql_query %s", current_position,
-              sql_query.c_ptr_quick()));
-
-  if (stored_result)
+  if (index_scan_result)
   {
-    mysql_free_result(stored_result);
-    stored_result= 0;
+    DBUG_PRINT("info",
+               ("ha_federated index_scan_result freed  %lx", index_scan_result));
+    mysql_free_result(index_scan_result);
+    index_scan_result= 0;
   }
+  DBUG_PRINT("info",
+             ("ha_federated SQL QUERY sql_query %s", sql_query.c_ptr_quick()));
   if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length()))
   {
     my_sprintf(error_buffer, (error_buffer, "error: %d '%s'",
@@ -2071,9 +2328,10 @@
     retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
     goto error;
   }
-  stored_result= mysql_store_result(mysql);
+  index_scan_result= mysql_store_result(mysql);
+  DBUG_PRINT("info", ("ha_federated index_scan_result stored %lx", index_scan_result));
 
-  if (!stored_result)
+  if (!index_scan_result)
   {
     retval= HA_ERR_END_OF_FILE;
     goto error;
@@ -2085,14 +2343,17 @@
  */
   table->status= 0;
 
-  retval= rnd_next(buf);
+  retval= read_next(buf, index_scan_result);
+  table->status= retval ? STATUS_NOT_FOUND : 0;
   DBUG_RETURN(retval);
 
 error:
-  if (stored_result)
+  if (index_scan_result)
   {
-    mysql_free_result(stored_result);
-    stored_result= 0;
+    DBUG_PRINT("info",
+               ("ha_federated index_scan_result freed  %lx", index_scan_result));
+    mysql_free_result(index_scan_result);
+    index_scan_result= 0;
   }
   table->status= STATUS_NOT_FOUND;
   my_error(retval, MYF(0), error_buffer);
@@ -2135,6 +2396,7 @@
                         &table->key_info[active_index],
                         start_key, end_key, 0);
 
+  DBUG_PRINT("info", ("ha_federated SQL QUERY sql_query %s", sql_query.c_ptr_quick()));
   if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length()))
   {
     retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
@@ -2142,34 +2404,38 @@
   }
   sql_query.length(0);
 
-  if (stored_result)
+  if (index_scan_result)
   {
     DBUG_PRINT("info",
-               ("mysql_free_result address %lx", stored_result));
-    mysql_free_result(stored_result);
-    stored_result= 0;
+               ("ha_federated index_scan_result freed %lx", index_scan_result));
+    mysql_free_result(index_scan_result);
+    index_scan_result= 0;
   }
-  stored_result= mysql_store_result(mysql);
+  index_scan_result= mysql_store_result(mysql);
+  DBUG_PRINT("info", ("ha_federated index_scan_result stored %lx", index_scan_result));
 
-  if (!stored_result)
+  if (!index_scan_result)
   {
     retval= HA_ERR_END_OF_FILE;
     goto error;
   }
- 
+
   /* This was successful, please let it be known! */
   table->status= 0;
 
-  retval= rnd_next(table->record[0]);
+  retval= read_next(table->record[0], index_scan_result);
+  table->status= retval ? STATUS_NOT_FOUND : 0;
   DBUG_RETURN(retval);
 
 error:
     table->status= STATUS_NOT_FOUND;
-    if (stored_result)
+    if (index_scan_result)
     {
-      DBUG_PRINT("info", ("mysql_free_result address %lx", stored_result));
-      mysql_free_result(stored_result);
-      stored_result= 0;
+      DBUG_PRINT("info",
+                 ("ha_federated index_scan_result freed %lx",
+                  index_scan_result));
+      mysql_free_result(index_scan_result);
+      index_scan_result= 0;
     }
     DBUG_RETURN(retval);
 }
@@ -2178,7 +2444,8 @@
 {
   int retval;
   DBUG_ENTER("ha_federated::read_range_next");
-  retval= rnd_next(table->record[0]);
+  retval= read_next(table->record[0], index_scan_result);
+  table->status= retval ? STATUS_NOT_FOUND : 0;
   DBUG_RETURN(retval);
 }
 
@@ -2190,7 +2457,8 @@
   DBUG_ENTER("ha_federated::index_next");
   statistic_increment(table->in_use->status_var.ha_read_next_count,
 		      &LOCK_status);
-  retval= rnd_next(buf);
+  retval= read_next(table->record[0], index_scan_result);
+  table->status= retval ? STATUS_NOT_FOUND : 0;
   DBUG_RETURN(retval);
 }
 /*
@@ -2247,21 +2515,26 @@
   if (scan)
   {
     DBUG_PRINT("info", ("share->select_query %s", share->select_query));
-    if (stored_result)
+    if (table_scan_result)
     {
       DBUG_PRINT("info",
-                 ("mysql_free_result address %lx", stored_result));
-      mysql_free_result(stored_result);
-      stored_result= 0;
+                 ("ha_federated table_scan_result freed %lx",
+                  table_scan_result));
+      mysql_free_result(table_scan_result);
+      table_scan_result= 0;
     }
+    full_table_scan= 1;
 
+    DBUG_PRINT("info", ("ha_federated SQL QUERY share->select_query %s",
+                        share->select_query));
     if (mysql_real_query(mysql,
                          share->select_query,
                          strlen(share->select_query)))
       goto error;
 
-    stored_result= mysql_store_result(mysql);
-    if (!stored_result)
+    table_scan_result= mysql_store_result(mysql);
+    DBUG_PRINT("info", ("ha_federated table_scan_result stored %lx", table_scan_result));
+    if (!table_scan_result)
       goto error;
   }
   DBUG_RETURN(0);
@@ -2272,23 +2545,31 @@
 
 int ha_federated::rnd_end()
 {
-  int retval;
   DBUG_ENTER("ha_federated::rnd_end");
 
-  if (stored_result)
+  if (table_scan_result)
   {
-    DBUG_PRINT("info", ("mysql_free_result address %lx", stored_result));
-    mysql_free_result(stored_result);
-    stored_result= 0;
+    DBUG_PRINT("info", ("free table_scan_result address %lx",
+                        table_scan_result));
+    DBUG_PRINT("info", ("ha_federated table_scan_result freed %lx", table_scan_result));
+    mysql_free_result(table_scan_result);
+    table_scan_result= 0;
   }
-  retval= index_end();
-  DBUG_RETURN(retval);
+  full_table_scan= 0;
+  DBUG_RETURN(0);
 }
 
 int ha_federated::index_end(void)
 {
   DBUG_ENTER("ha_federated::index_end");
   active_index= MAX_KEY;
+  if (index_scan_result)
+  {
+    DBUG_PRINT("info", ("ha_federated index_scan_result freed %lx", 
+                        index_scan_result));
+    mysql_free_result(index_scan_result);
+    index_scan_result= 0;
+  }
   DBUG_RETURN(0);
 }
 
@@ -2305,26 +2586,56 @@
 int ha_federated::rnd_next(byte *buf)
 {
   int retval;
-  MYSQL_ROW row;
   DBUG_ENTER("ha_federated::rnd_next");
 
-  if (stored_result == 0)
+  statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
+                      &LOCK_status);
+
+  retval= read_next(buf, table_scan_result);
+  table->status= retval ? STATUS_NOT_FOUND : 0;
+  DBUG_RETURN(retval);
+}
+
+int ha_federated::read_next(byte *buf, MYSQL_RES *resultset)
+{
+  int retval;
+  my_ulonglong num_rows;
+  MYSQL_ROW row;
+  DBUG_ENTER("ha_federated::read_next");
+
+  DBUG_PRINT("info", ("resultset hex %x dec %d", resultset, resultset));
+
+  num_rows = mysql_num_rows(resultset);
+  DBUG_PRINT("info", ("num_rows %d", num_rows));
+  if (num_rows < 1)
+  {
+    DBUG_PRINT("info", ("num_rows %d HA_ERR_END_OF_FILE",
+                        num_rows));
+    DBUG_RETURN(HA_ERR_END_OF_FILE);
+  }
+
+  /*
+  if (resultset == 0);
   {
+*/
     /*
       Return value of rnd_init is not always checked (see records.cc),
       so we can get here _even_ if there is _no_ pre-fetched result-set!
       TODO: fix it.
       */
-    DBUG_RETURN(1);
+  /*
+    DBUG_PRINT("info", ("resultset 0 HA_ERR_END_OF_FILE"));
+    DBUG_RETURN(HA_ERR_END_OF_FILE);
   }
- 
+  */
+
   /* Fetch a row, insert it back in a row format. */
-  current_position= stored_result->data_cursor;
-  DBUG_PRINT("info", ("current position %d", current_position));
-  if (!(row= mysql_fetch_row(stored_result)))
+  row= mysql_fetch_row(resultset);
+
+  if (!row)
     DBUG_RETURN(HA_ERR_END_OF_FILE);
 
-  retval= convert_row_to_internal_format(buf, row);
+  retval= convert_row_to_internal_format(buf, row, resultset);
   DBUG_RETURN(retval);
 }
 
@@ -2334,6 +2645,7 @@
   ordered. You can do something like the following to store the position:
   my_store_ptr(ref, ref_length, current_position);
 
+
   The server uses ref to store data. ref_length in the above case is the size
   needed to store current_position. ref is just a byte array that the server
   will maintain. If you are using offsets to mark rows, then current_position
@@ -2346,8 +2658,17 @@
 void ha_federated::position(const byte *record)
 {
   DBUG_ENTER("ha_federated::position");
-  /* my_store_ptr Add seek storage */
-  *(MYSQL_ROW_OFFSET *) ref= current_position;  // ref is always aligned
+  if (table->s->primary_key != MAX_KEY)
+  {
+    DBUG_PRINT("info", ("has a primary_key"));
+    key_copy(ref, (byte *)record, table->key_info + table->s->primary_key,
+             ref_length);
+  }
+  else
+  {
+    DBUG_PRINT("info", ("has not a primary key"));
+    memcpy(ref, record, ref_length);
+  }
   DBUG_VOID_RETURN;
 }
 
@@ -2364,14 +2685,38 @@
 */
 int ha_federated::rnd_pos(byte *buf, byte *pos)
 {
+  int result= 0;
+  MYSQL_RES *saved_resultset;
   DBUG_ENTER("ha_federated::rnd_pos");
 
   statistic_increment(table->in_use->status_var.ha_read_rnd_count,
                       &LOCK_status);
-  memcpy_fixed(&current_position, pos, sizeof(MYSQL_ROW_OFFSET));
-  stored_result->current_row= 0;
-  stored_result->data_cursor= current_position;
-  DBUG_RETURN(rnd_next(buf));
+  /*
+    since index_read_ix uses and alters index result set, then we want to
+    make sure rnd_pos only alters table scan result set, so save the index
+    result set, set index result set to table scan result set
+  */
+  saved_resultset= index_scan_result;
+  index_scan_result= table_scan_result;
+  full_table_scan= 0;
+  /* no primary key, so use index_read_idx to  */
+  if (table->s->primary_key != MAX_KEY)
+    result= index_read_idx(buf, table->s->primary_key, pos,
+                           ref_length, HA_READ_KEY_EXACT);
+  /* otherwise, get the position from ref as obtained in ::position */
+  else
+    memcpy(buf, pos, ref_length);
+  /*
+    now set table scan result set to index scan result set and set index
+    scan result set to its original result set (that we backed up above)
+  */
+  table_scan_result= index_scan_result;
+  index_scan_result= saved_resultset;
+
+  /* set table status to ensure joins work */
+  table->status= result ? STATUS_NOT_FOUND : 0;
+  DBUG_PRINT("info", ("returning result %d", result));
+  DBUG_RETURN(result);
 }
 
 
@@ -2433,7 +2778,8 @@
 
   error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
   /* we want not to show table status if not needed to do so */
-  if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST))
+  if (!status_up_to_date &&
+      flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST))
   {
     status_query_string.length(0);
     status_query_string.append(FEDERATED_INFO);
@@ -2446,6 +2792,8 @@
     status_query_string.append(escaped_table_name);
     status_query_string.append(FEDERATED_SQUOTE);
 
+    DBUG_PRINT("info", ("ha_federated SQL QUERY status_query_string %s",
+                        status_query_string.c_ptr_quick()));
     if (mysql_real_query(mysql, status_query_string.ptr(),
                          status_query_string.length()))
       goto error;
@@ -2453,6 +2801,7 @@
     status_query_string.length(0);
 
     result= mysql_store_result(mysql);
+    DBUG_PRINT("info", ("ha_federated result stored %lx", result));
     if (!result)
       goto error;
 
@@ -2476,28 +2825,37 @@
         delete_length = ?
       */
       if (row[4] != NULL)
-        records=         (ha_rows) my_strtoll10(row[4], (char**) 0, &error);
-      if (row[5] != NULL)
-        mean_rec_length= (ha_rows) my_strtoll10(row[5], (char**) 0, &error);
+        records= (ha_rows) my_strtoll10(row[4], (char**) 0, &error);
+
+      mean_rec_length= table->s->reclength;
+      data_file_length= records * mean_rec_length;
+
       if (row[12] != NULL)
-        update_time=      (ha_rows) my_strtoll10(row[12], (char**) 0, &error);
+        update_time= (ha_rows) my_strtoll10(row[12], (char**) 0, &error);
       if (row[13] != NULL)
-        check_time=      (ha_rows) my_strtoll10(row[13], (char**) 0, &error);
-    }
-    if (flag & HA_STATUS_CONST)
-    {
-      block_size= 4096;
+        check_time= (ha_rows) my_strtoll10(row[13], (char**) 0, &error);
     }
+    status_up_to_date= 1;
+  }
+  if (flag & HA_STATUS_CONST)
+  {
+    block_size= 4096;
   }
 
   if (result)
+  {
+    DBUG_PRINT("info", ("ha_federated result freed %lx", result));
     mysql_free_result(result);
+  }
 
   DBUG_VOID_RETURN;
 
 error:
   if (result)
+  {
+    DBUG_PRINT("info", ("error: ha_federated result freed %lx", result));
     mysql_free_result(result);
+  }
   my_sprintf(error_buffer, (error_buffer, ": %d : %s",
                             mysql_errno(mysql), mysql_error(mysql)));
   my_error(error_code, MYF(0), error_buffer);
@@ -2534,6 +2892,8 @@
   /*
     TRUNCATE won't return anything in mysql_affected_rows
   */
+  DBUG_PRINT("info", ("ha_federated SQL QUERY query %s",
+                      query.c_ptr_quick()));
   if (mysql_real_query(mysql, query.ptr(), query.length()))
   {
     DBUG_RETURN(stash_remote_error());
@@ -2603,6 +2963,7 @@
       lock_type= TL_READ;
 
     lock.type= lock_type;
+    status_up_to_date= 0;
   }
 
   *to++= &lock;

--- 1.24/sql/ha_federated.h	2006-02-28 02:17:35 -08:00
+++ 1.25/sql/ha_federated.h	2006-06-07 18:59:03 -07:00
@@ -78,7 +78,7 @@
 #define FEDERATED_VALUES_LEN sizeof(FEDERATED_VALUES)
 #define FEDERATED_UPDATE "UPDATE "
 #define FEDERATED_UPDATE_LEN sizeof(FEDERATED_UPDATE)
-#define FEDERATED_SET "SET "
+#define FEDERATED_SET " SET "
 #define FEDERATED_SET_LEN sizeof(FEDERATED_SET)
 #define FEDERATED_AND " AND "
 #define FEDERATED_AND_LEN sizeof(FEDERATED_AND)
@@ -116,6 +116,9 @@
 #define FEDERATED_EQ_LEN sizeof(FEDERATED_EQ)
 #define FEDERATED_FALSE " 1=0"
 #define FEDERATED_FALSE_LEN sizeof(FEDERATED_FALSE)
+#define FEDERATED_EXPLAIN "EXPLAIN "
+#define FEDERATED_EXPLAIN_LEN sizeof(FEDERATED_EXPLAIN)
+
 
 /*
   FEDERATED_SHARE is a structure that will be shared amoung all open handlers
@@ -130,6 +133,7 @@
     remote host info, parse_url supplies
   */
   char *scheme;
+  char *connect_string;
   char *hostname;
   char *username;
   char *password;
@@ -139,7 +143,7 @@
   char *socket;
   char *sport;
   ushort port;
-  uint table_name_length, use_count;
+  uint table_name_length, connect_string_length, use_count;
   pthread_mutex_t mutex;
   THR_LOCK lock;
 } FEDERATED_SHARE;
@@ -152,24 +156,25 @@
   THR_LOCK_DATA lock;      /* MySQL lock */
   FEDERATED_SHARE *share;    /* Shared lock info */
   MYSQL *mysql; /* MySQL connection */
-  MYSQL_RES *stored_result;
-  uint ref_length;
+  /* index result, table scan result cursors */
+  MYSQL_RES *index_scan_result, *table_scan_result;
   uint fetch_num; // stores the fetch num
-  MYSQL_ROW_OFFSET current_position;  // Current position used by ::position()
   int remote_error_number;
   char remote_error_buf[FEDERATED_QUERY_BUFFER_SIZE];
+  bool status_up_to_date, full_table_scan;
 
 private:
   /*
       return 0 on success
       return errorcode otherwise
   */
-  uint convert_row_to_internal_format(byte *buf, MYSQL_ROW row);
+  uint convert_row_to_internal_format(byte *buf, MYSQL_ROW row, MYSQL_RES *resultset);
   bool create_where_from_key(String *to, KEY *key_info, 
                              const key_range *start_key,
                              const key_range *end_key,
                              bool records_in_range);
   int stash_remote_error();
+  int read_next(byte *buf, MYSQL_RES *resultset);
 
 public:
   ha_federated(TABLE *table_arg);
Thread
bk commit into 5.0 tree (patg:1.2133) BUG#18764Patrick Galbraith8 Jun