[Commits] Rev 2836: DS-MRR improvements: review feedback in file:///home/psergey/dev2/maria-5.3-dsmrr-cpk-r5/

Sergey Petrunya psergey at askmonty.org
Tue Sep 21 19:19:59 EEST 2010


At file:///home/psergey/dev2/maria-5.3-dsmrr-cpk-r5/

------------------------------------------------------------
revno: 2836
revision-id: psergey at askmonty.org-20100921161954-me95qq05g07l9rgd
parent: psergey at askmonty.org-20100920191328-j51z10wao8848tw2
committer: Sergey Petrunya <psergey at askmonty.org>
branch nick: maria-5.3-dsmrr-cpk-r5
timestamp: Tue 2010-09-21 20:19:54 +0400
message:
  DS-MRR improvements: review feedback
  - Switch from one bi-directional buffer class to two 
    virtual inheritance-based forward and backward buffer classes.
=== modified file 'mysql-test/r/join_nested_jcl6.result'
--- a/mysql-test/r/join_nested_jcl6.result	2010-08-14 14:56:37 +0000
+++ b/mysql-test/r/join_nested_jcl6.result	2010-09-21 16:19:54 +0000
@@ -865,12 +865,12 @@
 (t1,t2)
 ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
 a	b	a	b	a	b
-4	2	1	2	4	2
-4	2	1	2	3	2
-4	2	1	2	4	2
-4	2	1	2	3	2
-4	2	1	2	4	2
-4	2	1	2	3	2
+4	2	1	2	3	2
+4	2	1	2	4	2
+4	2	1	2	3	2
+4	2	1	2	4	2
+4	2	1	2	3	2
+4	2	1	2	4	2
 NULL	NULL	2	2	3	2
 NULL	NULL	2	2	4	2
 EXPLAIN EXTENDED
@@ -1105,8 +1105,8 @@
 (t8.b=t9.b OR t8.c IS NULL) AND
 (t9.a=1);
 a	b	a	b	a	b	a	b	a	b	a	b	a	b	a	b	a	b	a	b
+1	2	3	2	4	2	1	2	3	2	2	2	6	2	2	2	0	2	1	2
 1	2	3	2	4	2	1	2	4	2	2	2	6	2	2	2	0	2	1	2
-1	2	3	2	4	2	1	2	3	2	2	2	6	2	2	2	0	2	1	2
 1	2	3	2	4	2	1	2	3	2	3	1	6	2	1	1	NULL	NULL	1	1
 1	2	3	2	4	2	1	2	4	2	3	1	6	2	1	1	NULL	NULL	1	1
 1	2	3	2	4	2	1	2	3	2	3	1	6	2	1	1	NULL	NULL	1	2
@@ -1785,8 +1785,8 @@
 ON t6.b >= 2 AND t5.b=t7.b AND
 (t8.a > 0 OR t8.c IS NULL);
 a	b	a	b	a	b	a	b
-2	2	3	2	2	2	1	2
 2	2	1	2	2	2	1	2
+2	2	3	2	2	2	1	2
 1	1	1	2	1	1	NULL	NULL
 1	1	3	2	1	1	NULL	NULL
 3	3	NULL	NULL	NULL	NULL	NULL	NULL

=== modified file 'mysql-test/r/join_outer_jcl6.result'
--- a/mysql-test/r/join_outer_jcl6.result	2010-08-11 10:54:34 +0000
+++ b/mysql-test/r/join_outer_jcl6.result	2010-09-21 16:19:54 +0000
@@ -352,14 +352,14 @@
 Lilliana Angelovska	NULL	NULL	NULL
 select t1.name, t2.name, t2.id,t3.id from t1 right join t2 on (t1.id = t2.owner) right join t1 as t3 on t3.id=t2.owner;
 name	name	id	id
+Antonio Paz	El Gato	1	1
 Antonio Paz	Perrito	2	1
-Antonio Paz	El Gato	1	1
 Thimble Smith	Happy	3	3
 NULL	NULL	NULL	2
 select t1.name, t2.name, t2.id, t2.owner, t3.id from t1 left join t2 on (t1.id = t2.owner) right join t1 as t3 on t3.id=t2.owner;
 name	name	id	owner	id
+Antonio Paz	El Gato	1	1	1
 Antonio Paz	Perrito	2	1	1
-Antonio Paz	El Gato	1	1	1
 Thimble Smith	Happy	3	3	3
 NULL	NULL	NULL	NULL	2
 drop table t1,t2;
@@ -413,9 +413,9 @@
 select t1.*, t2.* from t1 left join t2 on t1.n = t2.n and
 t1.m = t2.m where t1.n = 1;
 n	m	o	n	m	o
+1	2	11	1	2	3
+1	2	7	1	2	3
 1	2	9	1	2	3
-1	2	7	1	2	3
-1	2	11	1	2	3
 1	3	9	NULL	NULL	NULL
 select t1.*, t2.* from t1 left join t2 on t1.n = t2.n and
 t1.m = t2.m where t1.n = 1 order by t1.o;

=== modified file 'sql/multi_range_read.cc'
--- a/sql/multi_range_read.cc	2010-09-20 09:23:51 +0000
+++ b/sql/multi_range_read.cc	2010-09-21 16:19:54 +0000
@@ -285,131 +285,6 @@
 
 
 /****************************************************************************
- * SimpleBuffer class implementation (used by DS-MRR code)
- ***************************************************************************/
-void SimpleBuffer::setup_writing(uchar **data1, size_t len1, 
-                                 uchar **data2, size_t len2)
-{
-  write_ptr1= data1;
-  size1= len1;
-
-  write_ptr2= data2;
-  size2= len2;
-}
-
-
-void SimpleBuffer::write()
-{
-  if (is_reverse() && write_ptr2)
-    write(*write_ptr2, size2);
-
-  write(*write_ptr1, size1);
-
-  if (!is_reverse() && write_ptr2)
-    write(*write_ptr2, size2);
-}
-
-
-void SimpleBuffer::write(const uchar *data, size_t bytes)
-{
-  DBUG_ASSERT(have_space_for(bytes));
-
-  if (direction == -1)
-    write_pos -= bytes;
-
-  memcpy(write_pos, data, bytes);
-
-  if (direction == 1)
-    write_pos += bytes;
-}
-
-
-bool SimpleBuffer::can_write()
-{
-  return have_space_for(size1 + (write_ptr2 ? size2 : 0));
-}
-
-
-bool SimpleBuffer::have_space_for(size_t bytes)
-{
-  if (direction == 1)
-    return (write_pos + bytes < end);
-  else
-    return (write_pos - bytes >= start);
-}
-
-
-size_t SimpleBuffer::used_size()
-{
-  return (direction == 1)? write_pos - read_pos : read_pos - write_pos;
-}
-
-
-void SimpleBuffer::setup_reading(uchar **data1, size_t len1, 
-                                 uchar **data2, size_t len2)
-{
-  read_ptr1= data1;
-  DBUG_ASSERT(len1 == size1);
-
-  read_ptr2= data2;
-  DBUG_ASSERT(len2 == size2);
-}
-
-
-bool SimpleBuffer::read()
-{
-  if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
-    return TRUE;
-  *read_ptr1= read(size1);
-  if (read_ptr2)
-    *read_ptr2= read(size2);
-  return FALSE;
-}
-
-
-uchar *SimpleBuffer::read(size_t bytes)
-{
-  DBUG_ASSERT(have_data(bytes));
-  uchar *res;
-  if (direction == 1)
-  {
-    res= read_pos;
-    read_pos += bytes;
-    return res;
-  }
-  else
-  {
-    read_pos= read_pos - bytes;
-    return read_pos;
-  }
-}
-
-
-bool SimpleBuffer::have_data(size_t bytes)
-{
-  return (direction == 1)? (write_pos - read_pos >= (ptrdiff_t)bytes) : 
-                           (read_pos - write_pos >= (ptrdiff_t)bytes);
-}
-
-
-void SimpleBuffer::reset_for_writing()
-{
-  if (direction == 1)
-    write_pos= read_pos= start;
-  else
-    write_pos= read_pos= end;
-}
-
-
-uchar *SimpleBuffer::end_of_space()
-{
-  if (direction == 1)
-    return start;
-  else
-    return end;
-}
-
-/****************************************************************************
  * DS-MRR implementation 
  ***************************************************************************/
 
@@ -492,7 +367,7 @@
   */
   full_buf= buf->buffer;
   full_buf_end= buf->buffer_end;
-  rowid_buffer.set_buffer_space(full_buf, full_buf_end, SimpleBuffer::FORWARD);
+  rowid_buffer.set_buffer_space(full_buf, full_buf_end);
   
   if (do_sort_keys)
   {
@@ -504,7 +379,7 @@
     dsmrr_fill_key_buffer();
     
     if (dsmrr_eof && !do_rndpos_scan)
-      buf->end_of_used_area= key_buffer.end_of_space();
+      buf->end_of_used_area= key_buffer->end_of_space();
   }
 
   if (!do_rndpos_scan)
@@ -646,9 +521,9 @@
 }
 
 
-static int rowid_cmp(void *h, uchar *a, uchar *b)
+static int rowid_cmp_reverse(void *h, uchar *a, uchar *b)
 {
-  return ((handler*)h)->cmp_ref(a, b);
+  return - ((handler*)h)->cmp_ref(a, b);
 }
 
 
@@ -667,8 +542,6 @@
   post-condition:
    rowid buffer is not empty, or key source is exhausted.
 
-  @param h  Table handler
-
   @retval 0      OK, the next portion of rowids is in the buffer,
                  properly ordered
   @retval other  Error
@@ -689,8 +562,8 @@
 
   last_identical_rowid= NULL;
 
-  if (do_sort_keys && key_buffer.is_reverse())
-    key_buffer.flip();
+  //if (do_sort_keys && key_buffer.is_reverse())
+  //  key_buffer.flip();
 
   while (rowid_buffer.can_write())
   {
@@ -721,7 +594,7 @@
     dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
 
   /* Sort the buffer contents by rowid */
-  rowid_buffer.sort((qsort2_cmp)rowid_cmp, (void*)h);
+  rowid_buffer.sort((qsort2_cmp)rowid_cmp_reverse, (void*)h);
 
   rowid_buffer.setup_reading(&rowid, h->ref_length,
                              is_mrr_assoc? (uchar**)&rowids_range_id: NULL, sizeof(void*));
@@ -729,14 +602,6 @@
 }
 
 
-void SimpleBuffer::sort(qsort2_cmp cmp_func, void *cmp_func_arg)
-{
-  uint elem_size= size1 + (write_ptr2 ? size2 : 0);
-  uint n_elements= used_size() / elem_size;
-  my_qsort2(used_area(), n_elements, elem_size, cmp_func, cmp_func_arg);
-}
-
-
 /* 
   my_qsort2-compatible function to compare key tuples 
 */
@@ -787,6 +652,10 @@
   return 0;
 }
 
+int DsMrr_impl::key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2)
+{
+  return -key_tuple_cmp(arg, key1, key2);
+}
 
 /*
   Setup key/rowid buffer sizes based on sample_key
@@ -812,12 +681,13 @@
                               my_count_bits(sample_key->keypart_map));
   if (!do_rndpos_scan)
   {
-    /* Give all space to key buffer. */
-    key_buffer.set_buffer_space(full_buf, full_buf_end, SimpleBuffer::FORWARD);
+    /* Give all space to forward key buffer. */
+    key_buffer= &forward_key_buf;
+    identical_key_it= &forward_key_it;
+    key_buffer->set_buffer_space(full_buf, full_buf_end);
 
     /* Just in case, tell rowid buffer that it has zero size: */
-    rowid_buffer.set_buffer_space(full_buf_end, full_buf_end, 
-                                  SimpleBuffer::FORWARD);
+    rowid_buffer.set_buffer_space(full_buf_end, full_buf_end);
     return;
   }
   
@@ -860,10 +730,10 @@
   }
 
   rowid_buffer_end= full_buf + bytes_for_rowids;
-  rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end, 
-                                SimpleBuffer::FORWARD);
-  key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end, 
-                              SimpleBuffer::BACKWARD); 
+  rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
+  key_buffer= &backward_key_buf;
+  identical_key_it= &backward_key_it;
+  key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end); 
 }
 
 
@@ -892,7 +762,7 @@
   uchar **range_info_ptr= (uchar**)&cur_range.ptr;
   DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer");
 
-  DBUG_ASSERT(!know_key_tuple_params || key_buffer.is_empty());
+  DBUG_ASSERT(!know_key_tuple_params || key_buffer->is_empty());
 
   uchar *key_ptr;
   if (know_key_tuple_params)
@@ -903,18 +773,18 @@
         We're using two buffers and both of them are empty now. Restore the
         original sizes
       */
-      rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end,
-                                    SimpleBuffer::FORWARD);
-      key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end,
-                                  SimpleBuffer::BACKWARD);
+      rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
+      key_buffer= &backward_key_buf;
+      identical_key_it= &backward_key_it;
+      key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
     }
-    key_buffer.reset_for_writing();
-    key_buffer.setup_writing(&key_ptr, key_size_in_keybuf,
-                             is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
-                             sizeof(uchar*));
+    key_buffer->reset_for_writing();
+    key_buffer->setup_writing(&key_ptr, key_size_in_keybuf,
+                              is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
+                              sizeof(uchar*));
   }
 
-  while ((!know_key_tuple_params || key_buffer.can_write()) && 
+  while ((!know_key_tuple_params || key_buffer->can_write()) && 
          !(res= h->mrr_funcs.next(h->mrr_iter, &cur_range)))
   {
     DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
@@ -923,10 +793,10 @@
       /* This only happens when we've just started filling the buffer */
       setup_buffer_sizes(&cur_range.start_key);
       know_key_tuple_params= TRUE;
-      key_buffer.setup_writing(&key_ptr, key_size_in_keybuf,
+      key_buffer->setup_writing(&key_ptr, key_size_in_keybuf,
                                is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
                                sizeof(uchar*));
-      DBUG_ASSERT(key_buffer.can_write());
+      DBUG_ASSERT(key_buffer->can_write());
     }
     
     /* Put key, or {key, range_id} pair into the buffer */
@@ -935,16 +805,19 @@
     else
       key_ptr=(uchar*) cur_range.start_key.key;
 
-    key_buffer.write();
+    key_buffer->write();
   }
 
   dsmrr_eof= test(res);
 
-  key_buffer.sort((qsort2_cmp)DsMrr_impl::key_tuple_cmp, (void*)this);
+  key_buffer->sort((key_buffer->type() == Lifo_buffer::FORWARD)? 
+                     (qsort2_cmp)DsMrr_impl::key_tuple_cmp_reverse : 
+                     (qsort2_cmp)DsMrr_impl::key_tuple_cmp, 
+                   (void*)this);
   
-  key_buffer.setup_reading(&cur_index_tuple, key_size_in_keybuf,
-                           is_mrr_assoc? (uchar**)&cur_range_info: NULL,
-                           sizeof(void*));
+  key_buffer->setup_reading(&cur_index_tuple, key_size_in_keybuf,
+                            is_mrr_assoc? (uchar**)&cur_range_info: NULL,
+                            sizeof(void*));
 
   last_identical_key_ptr= NULL;
   in_identical_keys_range= FALSE;
@@ -959,7 +832,7 @@
 void DsMrr_impl::reallocate_buffer_space()
 {
   uchar *unused_start, *unused_end;
-  key_buffer.remove_unused_space(&unused_start, &unused_end);
+  key_buffer->remove_unused_space(&unused_start, &unused_end);
   rowid_buffer.grow(unused_start, unused_end);
 }
 
@@ -1000,7 +873,7 @@
   while (in_identical_keys_range)
   {
     /* This will read to (cur_index_tuple, cur_range_info): */
-    res2= identical_key_it.read_next();
+    res2= identical_key_it->read_next();
     DBUG_ASSERT(!res2);
 
     if (cur_index_tuple == last_identical_key_ptr)
@@ -1038,7 +911,7 @@
     if (last_identical_key_ptr)
     {
       in_identical_keys_range= TRUE;
-      identical_key_it.init(&key_buffer);
+      identical_key_it->init(key_buffer);
       cur_range_info= first_identical_range_info;
     }
 
@@ -1053,12 +926,12 @@
     if (last_identical_key_ptr)
     {
       /* key_buffer.read() reads to (cur_index_tuple, cur_range_info) */
-      while (!key_buffer.read() && (cur_index_tuple != last_identical_key_ptr)) {}
+      while (!key_buffer->read() && (cur_index_tuple != last_identical_key_ptr)) {}
       last_identical_key_ptr= NULL;
     }
 
     /* First, make sure we have a range at start of the buffer */
-    if (key_buffer.is_empty())
+    if (key_buffer->is_empty())
     {
       if (dsmrr_eof)
       {
@@ -1072,7 +945,7 @@
       if (!do_rndpos_scan)
         dsmrr_fill_key_buffer();
 
-      if (key_buffer.is_empty())
+      if (key_buffer->is_empty())
       {
         res= HA_ERR_END_OF_FILE;
         goto end;
@@ -1088,7 +961,7 @@
       reallocate_buffer_space();
 
     /* Get the next range to scan */
-    key_buffer.read(); // reads to (cur_index_tuple, cur_range_info)
+    key_buffer->read(); // reads to (cur_index_tuple, cur_range_info)
     key_in_buf= cur_index_tuple;
 
     if (use_key_pointers)
@@ -1106,9 +979,9 @@
     /* Check if subsequent keys in the key buffer are the same as this one */
     {
       char *save_cur_range_info= cur_range_info;
-      identical_key_it.init(&key_buffer);
+      identical_key_it->init(key_buffer);
       last_identical_key_ptr= NULL;
-      while (!identical_key_it.read_next())
+      while (!identical_key_it->read_next())
       {
         if (key_tuple_cmp(this, key_in_buf, cur_index_tuple))
           break;
@@ -1119,7 +992,7 @@
       if (last_identical_key_ptr)
       {
         in_identical_keys_range= TRUE;
-        identical_key_it.init(&key_buffer);
+        identical_key_it->init(key_buffer);
         first_identical_range_info= cur_range_info;
       }
     }
@@ -1178,7 +1051,7 @@
     {
       if (do_sort_keys)
       {
-        if (!key_buffer.is_empty() || in_index_range) 
+        if (!key_buffer->is_empty() || in_index_range) 
         {
           /* There are some sorted keys left. Use them to get rowids */
           if ((res= dsmrr_fill_rowid_buffer()))
@@ -1239,9 +1112,9 @@
         Note: this implies that SQL layer doesn't touch table->record[0]
         between calls.
       */
-      SimpleBuffer::PeekIterator identical_rowid_it;
-      identical_rowid_it.init(&rowid_buffer);
-      while (!identical_rowid_it.read_next()) // reads to (rowid, ...)
+      Forward_iterator it;
+      it.init(&rowid_buffer);
+      while (!it.read_next()) // reads to (rowid, ...)
       {
         if (h2->cmp_ref(rowid, cur_rowid))
           break;

=== modified file 'sql/multi_range_read.h'
--- a/sql/multi_range_read.h	2010-09-20 19:13:28 +0000
+++ b/sql/multi_range_read.h	2010-09-21 16:19:54 +0000
@@ -46,64 +46,12 @@
   storage and has better performance when reading data in rowid order.
 */
 
-
-/*
-  An in-memory buffer used by DS-MRR implementation. 
-  - The buffer contains fixed-size elements. The elements are either atomic
-    byte sequences or pairs.
-  - The buffer resides in memory provided by the user. It is possible to
-     = dynamically (ie. between write operations) add ajacent memory space to
-       the buffer
-     = dynamically remove unused space from the buffer.
-  - Buffer can be set to be either "forward" or "backward". 
-
-  The intent of the last two properties is to allow to have two buffers on
-  adjacent memory space, one is being read from (and so its space shrinks)
-  while the other is being written to (and so it needs more and more space).
-
-  Illustration of forward buffer operation:
-
-                         +-- next read will read from here
-                         |
-                         |               +-- next write will write to here
-                         v               v
-        *--------------*===============*----------------*
-        |       ^      |          ^    |                |
-        |       |      read_pos   |    write_pos        |
-        start   |                 |                     end
-                |                 |            
-              usused space         user data
-  
-  For reverse buffer, start/end have the same meaning, but reading and 
-  writing is done from end to start.
-*/
-
-class SimpleBuffer
+class Forward_lifo_buffer;
+class Backward_lifo_buffer;
+
+class Lifo_buffer 
 {
-public:
-
-  enum enum_direction {
-    BACKWARD=-1, /* buffer is filled/read from bigger to smaller memory addresses */
-    FORWARD=1  /* buffer is filled/read from smaller to bigger memory addresses */
-  };
-
-private:
-  enum_direction direction;
-
-  uchar *start; /* points to start of buffer space */
-  uchar *end;   /* points to just beyond the end of buffer space */
-  /*
-    Forward buffer: points to the start of the data that will be read next
-    Backward buffer: points to just beyond the end of the data that will be 
-    read next.
-  */
-  uchar *read_pos;
-  /*
-    Forward buffer: points to just after the end of the used area.
-    Backward buffer: points to the start of used area.
-  */
-  uchar *write_pos;
-
+protected:
   /* 
     Data to be written. write() call will assume that (*write_ptr1) points to 
     size1 bytes of data to be written.
@@ -123,108 +71,78 @@
   uchar **read_ptr1;
   uchar **read_ptr2;
 
+  uchar *start; /* points to start of buffer space */
+  uchar *end;   /* points to just beyond the end of buffer space */
 public:
-  /* Write-mode functions */
-  void setup_writing(uchar **data1, size_t len1, 
-                     uchar **data2, size_t len2);
-  void reset_for_writing();
-  bool can_write();
-  void write();
-
-  /* Read-mode functions */
-  bool is_empty() { return used_size() == 0; }
-  void setup_reading(uchar **data1, size_t len1, 
-                     uchar **data2, size_t len2);
-  bool read();
-
-  /* Misc functions */
-  void sort(qsort2_cmp cmp_func, void *cmp_func_arg);
-  bool is_reverse() { return direction == BACKWARD; }
-  uchar *end_of_space();
+
+  enum enum_direction {
+    BACKWARD=-1, /* buffer is filled/read from bigger to smaller memory addresses */
+    FORWARD=1  /* buffer is filled/read from smaller to bigger memory addresses */
+  };
+
+  virtual enum_direction type() = 0;
 
   /* Buffer space control functions */
-  void set_buffer_space(uchar *start_arg, uchar *end_arg, enum_direction direction_arg) 
+  void set_buffer_space(uchar *start_arg, uchar *end_arg) 
   {
     start= start_arg;
     end= end_arg;
-    direction= direction_arg;
     TRASH(start, end - start);
     reset_for_writing();
   }
 
-  /*
-    Stop using/return the unneded space (the one that we have already wrote 
-    to read from).
-  */
-  void remove_unused_space(uchar **unused_start, uchar **unused_end)
-  {
-    if (direction == 1)
-    {
-      *unused_start= start;
-      *unused_end= read_pos;
-      start= read_pos;
-    }
-    else
-    {
-      *unused_start= read_pos;
-      *unused_end= end;
-      end= read_pos;
-    }
-  }
-
-  void flip()
-  {
-    uchar *tmp= read_pos;
-    read_pos= write_pos;
-    write_pos= tmp;
-    direction= (direction == FORWARD)? BACKWARD: FORWARD;
-  }
-
-  void grow(uchar *unused_start, uchar *unused_end)
-  {
-    /*
-      Passed memory area can be meaningfully used for growing the buffer if:
-      - it is adjacent to buffer space we're using
-      - it is on the end towards which we grow.
-    */
-    DBUG_ASSERT(unused_end >= unused_start);
-    TRASH(unused_start, unused_end - unused_start);
-    if (direction == 1 && end == unused_start)
-    {
-      end= unused_end;
-    }
-    else if (direction == -1 && start == unused_end)
-    {
-      start= unused_start;
-    }
-    else
-      DBUG_ASSERT(0); /* Attempt to grow buffer in wrong direction */
-  }
-  
-  /*
-    An iterator to do look at what we're about to read from the buffer without
-    actually reading it.
-  */
-  class PeekIterator
-  {
-    SimpleBuffer *buf; /* The buffer we're iterating over*/
-    /*
-      if buf->direction==FORWARD  : pointer to what to return next
-      if buf->direction==BACKWARD : pointer to the end of what is to be 
-                                   returned next
-    */
-    uchar *pos;
+  void setup_writing(uchar **data1, size_t len1, uchar **data2, size_t len2)
+  {
+    write_ptr1= data1;
+    size1= len1;
+    write_ptr2= data2;
+    size2= len2;
+  }
+
+  void setup_reading(uchar **data1, size_t len1, uchar **data2, size_t len2)
+  {
+    read_ptr1= data1;
+    DBUG_ASSERT(len1 == size1);
+    read_ptr2= data2;
+    DBUG_ASSERT(len2 == size2);
+  }
+  
+  //virtual void write_bytes(const uchar *data, size_t bytes)=0;
+
+  virtual bool read() = 0;
+  virtual void write() = 0;
+  bool can_write()
+  {
+    return have_space_for(size1 + (write_ptr2 ? size2 : 0));
+  }
+  
+  bool is_empty() { return used_size() == 0; }
+  virtual size_t used_size() = 0;
+
+  void sort(qsort2_cmp cmp_func, void *cmp_func_arg)
+  {
+    uint elem_size= size1 + (write_ptr2 ? size2 : 0);
+    uint n_elements= used_size() / elem_size;
+    my_qsort2(used_area(), n_elements, elem_size, cmp_func, cmp_func_arg);
+  }
+
+
+  virtual void reset_for_writing() = 0;
+  virtual uchar *end_of_space() = 0;
+  bool have_data(size_t bytes)
+  {
+    return (used_size() >= bytes);
+  }
+  virtual bool have_space_for(size_t bytes) = 0;
+  //virtual uchar *read_bytes(size_t bytes) = 0;
+
+  virtual void remove_unused_space(uchar **unused_start, uchar **unused_end)=0;
+  virtual uchar *used_area() = 0;
+
+  class Iterator
+  {
   public:
-    /* 
-      Initialize the iterator. After intiialization, the first read_next() call
-      will read what buf_arg->read() would read.
-    */
-    void init(SimpleBuffer *buf_arg)
-    {
-      buf= buf_arg;
-      pos= buf->read_pos;
-    }
-    
+    virtual void init(Lifo_buffer *buf) = 0;
     /*
       Read the next value. The calling convention is the same as buf->read()
       has.
@@ -233,57 +151,284 @@
         FALSE - Ok
         TRUE  - EOF, reached the end of the buffer
     */
-    bool read_next()
-    {
-      /* 
-        Always read the first component first (if the buffer is backwards, we
-        have written the second component first).
-      */
-      uchar *res;
+    virtual bool read_next()= 0;
+    virtual ~Iterator() {}
+  protected:
+    Lifo_buffer *buf;
+    virtual uchar *get_next(size_t nbytes)=0;
+  };
+  virtual ~Lifo_buffer() {};
+
+  friend class Forward_iterator;
+  friend class Backward_iterator;
+};
+
+
+class Forward_lifo_buffer: public Lifo_buffer
+{
+  uchar *pos;
+public:
+  enum_direction type() { return FORWARD; }
+  size_t used_size()
+  {
+    return pos - start;
+  }
+  void reset_for_writing()
+  {
+    pos= start;
+  }
+  uchar *end_of_space() { return pos; }
+  bool have_space_for(size_t bytes)
+  {
+    return (pos + bytes < end);
+  }
+
+  void write()
+  {
+    write_bytes(*write_ptr1, size1);
+    if (write_ptr2)
+      write_bytes(*write_ptr2, size2);
+  }
+  void write_bytes(const uchar *data, size_t bytes)
+  {
+    DBUG_ASSERT(have_space_for(bytes));
+    memcpy(pos, data, bytes);
+    pos += bytes;
+  }
+  uchar *read_bytes(size_t bytes)
+  {
+    DBUG_ASSERT(have_data(bytes));
+    pos= pos - bytes;
+    return pos;
+  }
+  bool read()
+  {
+    if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
+      return TRUE;
+    if (read_ptr2)
+      *read_ptr2= read_bytes(size2);
+    *read_ptr1= read_bytes(size1);
+    return FALSE;
+  }
+  /*
+    Stop using/return the unneded space (the one that we have already wrote 
+    to read from).
+  */
+  void remove_unused_space(uchar **unused_start, uchar **unused_end)
+  {
+    DBUG_ASSERT(0); /* Don't need this yet */
+  }
+  void grow(uchar *unused_start, uchar *unused_end)
+  {
+    /*
+      Passed memory area can be meaningfully used for growing the buffer if:
+      - it is adjacent to buffer space we're using
+      - it is on the end towards which we grow.
+    */
+    DBUG_ASSERT(unused_end >= unused_start);
+    TRASH(unused_start, unused_end - unused_start);
+    DBUG_ASSERT(end == unused_start);
+    end= unused_end;
+  }
+  /* Return pointer to start of the memory area that is occupied by the data */
+  uchar *used_area() { return start; }
+  friend class Forward_iterator;
+};
+
+
+class Forward_iterator : public Lifo_buffer::Iterator
+{
+  uchar *pos;
+
+  /* Return pointer to next chunk of nbytes bytes and avance over it */
+  uchar *get_next(size_t nbytes)
+  {
+    if (pos - nbytes < ((Forward_lifo_buffer*)buf)->start)
+      return NULL;
+    pos -= nbytes;
+    return pos;
+  }
+public:
+  bool read_next()
+  {
+    uchar *res;
+    if (buf->read_ptr2)
+    {
+      if ((res= get_next(buf->size2)))
+      {
+        *(buf->read_ptr2)= res;
+        *buf->read_ptr1= get_next(buf->size1);
+        return FALSE;
+      }
+    }
+    else
+    {
       if ((res= get_next(buf->size1)))
       {
         *(buf->read_ptr1)= res;
-        if (buf->read_ptr2)
-          *buf->read_ptr2= get_next(buf->size2);
         return FALSE;
       }
-      return TRUE; /* EOF */
     }
-  private:
-    /* Return pointer to next chunk of nbytes bytes and avance over it */
-    uchar *get_next(size_t nbytes)
+    return TRUE; /* EOF */
+  }
+
+  void init(Lifo_buffer *buf_arg)
+  {
+    DBUG_ASSERT(buf_arg->type() == Lifo_buffer::FORWARD);
+    buf= buf_arg;
+    pos= ((Forward_lifo_buffer*)buf)->pos;
+  }
+};
+
+
+class Backward_lifo_buffer: public Lifo_buffer
+{
+  uchar *pos;
+public:
+  enum_direction type() { return BACKWARD; }
+ 
+  size_t used_size()
+  {
+    return end - pos;
+  }
+  void reset_for_writing()
+  {
+    pos= end;
+  }
+  uchar *end_of_space() { return end; }
+  bool have_space_for(size_t bytes)
+  {
+    return (pos - bytes >= start);
+  }
+  void write()
+  {
+    if (write_ptr2)
+      write_bytes(*write_ptr2, size2);
+    write_bytes(*write_ptr1, size1);
+  }
+  void write_bytes(const uchar *data, size_t bytes)
+  {
+    DBUG_ASSERT(have_space_for(bytes));
+    pos -= bytes;
+    memcpy(pos, data, bytes);
+  }
+  bool read()
+  {
+    if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
+      return TRUE;
+    *read_ptr1= read_bytes(size1);
+    if (read_ptr2)
+      *read_ptr2= read_bytes(size2);
+    return FALSE;
+  }
+  uchar *read_bytes(size_t bytes)
+  {
+    DBUG_ASSERT(have_data(bytes));
+    uchar *ret= pos;
+    pos= pos + bytes;
+    return ret;
+  }
+  /*
+    Stop using/return the unneded space (the one that we have already wrote 
+    to and have read from).
+  */
+  void remove_unused_space(uchar **unused_start, uchar **unused_end)
+  {
+    *unused_start= start;
+    *unused_end= pos;
+    start= pos;
+  }
+  void grow(uchar *unused_start, uchar *unused_end)
+  {
+    /*
+      Passed memory area can be meaningfully used for growing the buffer if:
+      - it is adjacent to buffer space we're using
+      - it is on the end towards which we grow.
+    */
+    /*
+    DBUG_ASSERT(unused_end >= unused_start);
+    TRASH(unused_start, unused_end - unused_start);
+    DBUG_ASSERT(start == unused_end);
+    start= unused_start;
+    */
+    DBUG_ASSERT(0); //Not used
+  }
+  /* Return pointer to start of the memory area that is occupied by the data */
+  uchar *used_area() { return pos; }
+  friend class Backward_iterator;
+};
+
+
+class Backward_iterator : public Lifo_buffer::Iterator
+{
+  uchar *pos;
+  /* Return pointer to next chunk of nbytes bytes and advance over it */
+  uchar *get_next(size_t nbytes)
+  {
+    if (pos + nbytes > ((Backward_lifo_buffer*)buf)->end)
+      return NULL;
+    uchar *res= pos;
+    pos += nbytes;
+    return res;
+  }
+public:
+  bool read_next()
+  {
+    /*
+      Always read the first component first (if the buffer is backwards, we
+      have written the second component first).
+    */
+    uchar *res;
+    if ((res= get_next(buf->size1)))
     {
-      if (buf->direction == 1)
-      {
-        if (pos + nbytes > buf->write_pos)
-          return NULL;
-        uchar *res= pos;
-        pos += nbytes;
-        return res;
-      }
-      else
-      {
-        if (pos - nbytes < buf->write_pos)
-          return NULL;
-        pos -= nbytes;
-        return pos;
-      }
+      *(buf->read_ptr1)= res;
+      if (buf->read_ptr2)
+        *buf->read_ptr2= get_next(buf->size2);
+      return FALSE;
     }
-  };
-
-private:
-  bool have_space_for(size_t bytes);
-  /* Return pointer to start of the memory area that is occupied by the data */
-  uchar *used_area() { return (direction == FORWARD)? read_pos : write_pos; }
-  size_t used_size();
-
-  void write(const uchar *data, size_t bytes);
-  uchar *read(size_t bytes);
-  bool have_data(size_t bytes);
+    return TRUE; /* EOF */
+  }
+  void init(Lifo_buffer *buf_arg)
+  {
+    DBUG_ASSERT(buf_arg->type() == Lifo_buffer::BACKWARD);
+    buf= buf_arg;
+    pos= ((Backward_lifo_buffer*)buf)->pos;
+  }
 };
 
 
 /*
+  An in-memory buffer used by DS-MRR implementation. 
+  - The buffer contains fixed-size elements. The elements are either atomic
+    byte sequences or pairs.
+  - The buffer resides in memory provided by the user. It is possible to
+     = dynamically (ie. between write operations) add ajacent memory space to
+       the buffer
+     = dynamically remove unused space from the buffer.
+  - Buffer can be set to be either "forward" or "backward". 
+
+  The intent of the last two properties is to allow to have two buffers on
+  adjacent memory space, one is being read from (and so its space shrinks)
+  while the other is being written to (and so it needs more and more space).
+
+  Illustration of forward buffer operation:
+
+                         +-- next read will read from here
+                         |
+                         |               +-- next write will write to here
+                         v               v
+        *--------------*===============*----------------*
+        |       ^      |          ^    |                |
+        |       |      read_pos   |    write_pos        |
+        start   |                 |                     end
+                |                 |            
+              usused space         user data
+  
+  For reverse buffer, start/end have the same meaning, but reading and 
+  writing is done from end to start.
+*/
+
+/*
   DS-MRR implementation for one table. Create/use one object of this class for
   each ha_{myisam/innobase/etc} object. That object will be further referred to
   as "the handler"
@@ -439,8 +584,18 @@
   /* TRUE<=> we're in a middle of enumerating records for a key range */
   bool in_index_range;
   
+  /*
+    One of the following two is used for key buffer: forward is used when 
+    we only need key buffer, backward is used when we need both key and rowid
+    buffers.
+  */
+  Forward_lifo_buffer forward_key_buf;
+  Forward_iterator forward_key_it;
+  Backward_lifo_buffer backward_key_buf;
+  Backward_iterator backward_key_it;
+
   /* Buffer to store (key, range_id) pairs */
-  SimpleBuffer key_buffer;
+  Lifo_buffer *key_buffer;
    
   /* key_buffer.read() reads */
   uchar *cur_index_tuple;
@@ -489,7 +644,7 @@
     and do that by walking from current buffer read position until we get
     last_identical_key_ptr.
   */
-  SimpleBuffer::PeekIterator identical_key_it;
+  Lifo_buffer::Iterator *identical_key_it;
 
 
   /** rnd_pos() scan and rowid buffer-related members **/
@@ -498,7 +653,7 @@
     Buffer to store (rowid, range_id) pairs, or just rowids if 
     is_mrr_assoc==FALSE
   */
-  SimpleBuffer rowid_buffer;
+  Forward_lifo_buffer rowid_buffer;
   
   /* rowid_buffer.read() will set the following:  */
   uchar *rowid;
@@ -522,6 +677,7 @@
                                uint *buffer_size, COST_VECT *cost);
   bool check_cpk_scan(THD *thd, uint keyno, uint mrr_flags);
   static int key_tuple_cmp(void* arg, uchar* key1, uchar* key2);
+  static int key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2);
   int dsmrr_fill_rowid_buffer();
   void dsmrr_fill_key_buffer();
   int dsmrr_next_from_index(char **range_info);



More information about the commits mailing list