[Commits] Rev 2837: Better comments, move Lifo_buffer to separate file. in file:///home/psergey/dev2/maria-5.3-dsmrr-cpk-r5/

Sergey Petrunya psergey at askmonty.org
Tue Sep 28 11:19:55 EEST 2010


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

------------------------------------------------------------
revno: 2837
revision-id: psergey at askmonty.org-20100928081950-2b8zab8hu5iezxsg
parent: psergey at askmonty.org-20100921161954-me95qq05g07l9rgd
committer: Sergey Petrunya <psergey at askmonty.org>
branch nick: maria-5.3-dsmrr-cpk-r5
timestamp: Tue 2010-09-28 12:19:50 +0400
message:
  Better comments, move Lifo_buffer to separate file.
=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2010-07-10 10:37:30 +0000
+++ b/sql/CMakeLists.txt	2010-09-28 08:19:50 +0000
@@ -63,6 +63,7 @@
                sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h 
                sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc 
                sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc
+               sql_lifo_buffer.h
                sql_join_cache.cc sql_lex.cc sql_list.cc sql_load.cc sql_manager.cc
                sql_map.cc sql_parse.cc  sql_partition.cc sql_plugin.cc
                sql_prepare.cc sql_rename.cc 

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2010-07-10 10:37:30 +0000
+++ b/sql/Makefile.am	2010-09-28 08:19:50 +0000
@@ -66,6 +66,7 @@
 			log.h log_slow.h sql_show.h rpl_rli.h rpl_mi.h \
 			sql_select.h structs.h table.h sql_udf.h hash_filo.h \
 			lex.h lex_symbol.h sql_acl.h sql_crypt.h  \
+                        sql_lifo_buffer.h \
 			sql_repl.h slave.h rpl_filter.h rpl_injector.h \
 			log_event.h rpl_record.h \
 			log_event_old.h rpl_record_old.h \

=== modified file 'sql/handler.h'
--- a/sql/handler.h	2010-09-05 10:32:14 +0000
+++ b/sql/handler.h	2010-09-28 08:19:50 +0000
@@ -1326,6 +1326,10 @@
   The MRR user has materialized range keys somewhere in the user's buffer.
   This can be used for optimization of the procedure that sorts these keys
   since in this case key values don't have to be copied into the MRR buffer.
+
+  In other words, it is guaranteed that after RANGE_SEQ_IF::next() call the 
+  pointer in range->start_key.key will point to a key value that will remain 
+  there until the end of the MRR scan.
 */
 #define HA_MRR_MATERIALIZED_KEYS 256
 

=== modified file 'sql/multi_range_read.cc'
--- a/sql/multi_range_read.cc	2010-09-21 16:19:54 +0000
+++ b/sql/multi_range_read.cc	2010-09-28 08:19:50 +0000
@@ -332,7 +332,7 @@
   is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
   
   /*
-    Figure out what steps we'll need to do
+    Determine whether we'll need to do key sorting and/or rnd_pos() scan
   */
   do_sort_keys= FALSE;
   if ((mode & HA_MRR_SINGLE_POINT) && 
@@ -362,8 +362,9 @@
     status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count);
 
   /* 
-    At start, alloc all of the buffer for rowids. Key sorting code will grab a
-    piece if necessary.
+    At start, alloc all of the buffer for rowids. When/if key sorting code
+    figures how much buffer space it needs, it will call setup_buffer_sizes()
+    to re-distribute the buffer space.
   */
   full_buf= buf->buffer;
   full_buf_end= buf->buffer_end;
@@ -530,22 +531,19 @@
 /**
   DS-MRR: Fill and sort the rowid buffer
 
-  {This is an internal function of DiskSweep MRR implementation}
-
   Scan the MRR ranges and collect ROWIDs (or {ROWID, range_id} pairs) into 
   buffer. When the buffer is full or scan is completed, sort the buffer by 
   rowid and return.
+
+  When this function returns, either rowid buffer is not empty, or the source
+  of lookup keys (i.e. ranges) is exhaused.
   
   dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
   scanning. This function never returns HA_ERR_END_OF_FILE.
 
-  post-condition:
-   rowid buffer is not empty, or key source is exhausted.
-
   @retval 0      OK, the next portion of rowids is in the buffer,
                  properly ordered
   @retval other  Error
-  
 */
 
 int DsMrr_impl::dsmrr_fill_rowid_buffer()
@@ -556,15 +554,13 @@
   DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer");
   
   DBUG_ASSERT(rowid_buffer.is_empty());
-  rowid_buffer.reset_for_writing();
+  rowid_buffer.reset();
   rowid_buffer.setup_writing(&h2->ref, h2->ref_length,
-                             is_mrr_assoc? (uchar**)&range_info_ptr: NULL, sizeof(void*));
+                             is_mrr_assoc? (uchar**)&range_info_ptr: NULL,
+                             sizeof(void*));
 
   last_identical_rowid= NULL;
 
-  //if (do_sort_keys && key_buffer.is_reverse())
-  //  key_buffer.flip();
-
   while (rowid_buffer.can_write())
   {
     if (do_sort_keys)
@@ -652,18 +648,21 @@
   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
-
-  DESCRIPTION
-    Setup key/rowid buffer sizes based on sample_key and its length.
-
-    This function must be called when all buffer space is empty.
+
+/**
+  Setup key/rowid buffer sizes based on sample_key and its length.
+  
+  @param
+    sample_key  A lookup key to use as a sample. It is assumed that
+                all other keys will have the same length/etc.
+  @note
+    This function must be called when all buffers are empty
 */
 
 void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
@@ -737,22 +736,19 @@
 }
 
 
-/*
+/**
   DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
   
-  SYNOPSIS
-    DsMrr_impl::dsmrr_fill_key_buffer()
-
-  DESCRIPTION
-    DS-MRR/CPK: Enumerate the input range (=key) sequence, fill the key buffer
-    (lookup_key, range_id) pairs and sort.
-
+  Enumerate the input range (=key) sequence, fill the key buffer with 
+  (lookup_key, range_id) pairs and sort it.
+
+  When this function returns, either
+   - key buffer is non-empty, or
+   - key buffer is empty and source range sequence is exhausted
+  
+  @note
     dsmrr_eof is set to indicate whether we've exhausted the list of ranges 
     we're scanning.
-
-  post-condition:
-   - key buffer is non-empty
-   - key buffer is empty and source range sequence is exhausted
 */
 
 void DsMrr_impl::dsmrr_fill_key_buffer()
@@ -778,7 +774,7 @@
       identical_key_it= &backward_key_it;
       key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
     }
-    key_buffer->reset_for_writing();
+    key_buffer->reset();
     key_buffer->setup_writing(&key_ptr, key_size_in_keybuf,
                               is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
                               sizeof(uchar*));
@@ -825,8 +821,8 @@
 }
 
 
-/*
-  Take unused space from key buffer and give it to rowid buffer.
+/**
+  Take unused space from the key buffer and give it to the rowid buffer
 */
 
 void DsMrr_impl::reallocate_buffer_space()
@@ -837,30 +833,25 @@
 }
 
 
-/*
+/**
   DS-MRR/CPK: multi_range_read_next() function
-
-  DESCRIPTION
-    DsMrr_impl::dsmrr_next_from_index()
-      range_info  OUT  identifier of range that the returned record belongs to
-
-  DESCRIPTION
-  
-  This function walks over key buffer and does index reads, i.e. it produces
-  {current_record, range_id} pairs.
-
-  The function has the same call contract like multi_range_read_next()'s.
-
-  We actually iterate nested sequences:
-  
-  - a disjoint sequence of index ranges
-    - each range has multiple records
-      - each record goes into multiple identical ranges.
-
-  RETURN
-    0                   OK, next record was successfully read
-    HA_ERR_END_OF_FILE  End of records
-    Other               Some other error
+  
+  @param range_info  OUT  identifier of range that the returned record belongs to
+  
+  @note
+    This function walks over key buffer and does index reads, i.e. it produces
+    {current_record, range_id} pairs.
+
+    The function has the same call contract like multi_range_read_next()'s.
+
+    We actually iterate over nested sequences:
+    - a disjoint sequence of index ranges
+      - each range has multiple records
+        - each record goes into multiple identical ranges.
+
+  @retval 0                   OK, next record was successfully read
+  @retval HA_ERR_END_OF_FILE  End of records
+  @retval Other               Some other error
 */
 
 int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
@@ -1007,7 +998,9 @@
 
 
 /**
-  DS-MRR implementation: multi_range_read_next() function
+  DS-MRR implementation: multi_range_read_next() function.
+
+  Calling convention is like multi_range_read_next() has.
 */
 
 int DsMrr_impl::dsmrr_next(char **range_info)
@@ -1237,17 +1230,12 @@
 /*
   Check if key/flags allow DS-MRR/CPK strategy to be used
   
-  SYNOPSIS
-   DsMrr_impl::check_cpk_scan()
-     keyno      Index that will be used
-     mrr_flags  
+  @param thd
+  @param keyno      Index that will be used
+  @param  mrr_flags  
   
-  DESCRIPTION
-    Check if key/flags allow DS-MRR/CPK strategy to be used. 
- 
-  RETURN
-    TRUE   DS-MRR/CPK should be used
-    FALSE  Otherwise
+  @retval TRUE   DS-MRR/CPK should be used
+  @retval FALSE  Otherwise
 */
 
 bool DsMrr_impl::check_cpk_scan(THD *thd, uint keyno, uint mrr_flags)
@@ -1413,17 +1401,14 @@
 
 /* 
   Get cost of one sort-and-sweep step
-
-  SYNOPSIS
-    get_sort_and_sweep_cost()
-      table       Table being accessed
-      nrows       Number of rows to be sorted and retrieved
-      cost   OUT  The cost
-
-  DESCRIPTION
-    Get cost of these operations:
-     - sort an array of #nrows ROWIDs using qsort
-     - read #nrows records from table in a sweep.
+  
+  It consists of two parts:
+   - sort an array of #nrows ROWIDs using qsort
+   - read #nrows records from table in a sweep.
+
+  @param table       Table being accessed
+  @param nrows       Number of rows to be sorted and retrieved
+  @param cost   OUT  The cost of scan
 */
 
 static 

=== modified file 'sql/multi_range_read.h'
--- a/sql/multi_range_read.h	2010-09-21 16:19:54 +0000
+++ b/sql/multi_range_read.h	2010-09-28 08:19:50 +0000
@@ -1,6 +1,6 @@
-/*
-  This file contains declarations for Disk-Sweep MultiRangeRead (DS-MRR) 
-  implementation
+/**
+  @defgroup DS-MRR declarations
+  @{
 */
 
 /**
@@ -46,387 +46,7 @@
   storage and has better performance when reading data in rowid order.
 */
 
-class Forward_lifo_buffer;
-class Backward_lifo_buffer;
-
-class Lifo_buffer 
-{
-protected:
-  /* 
-    Data to be written. write() call will assume that (*write_ptr1) points to 
-    size1 bytes of data to be written.
-    If write_ptr2 != NULL then the buffer stores pairs, and (*write_ptr2) 
-    points to size2 bytes of data that form the second component.
-  */
-  uchar **write_ptr1;
-  size_t size1;
-  uchar **write_ptr2;
-  size_t size2;
-
-  /*
-    read() will do reading by storing pointer to read data into *read_ptr1 (if
-    the buffer stores atomic elements), or into {*read_ptr1, *read_ptr2} (if
-    the buffer stores pairs).
-  */
-  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:
-
-  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) 
-  {
-    start= start_arg;
-    end= end_arg;
-    TRASH(start, end - start);
-    reset_for_writing();
-  }
-
-  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:
-    virtual void init(Lifo_buffer *buf) = 0;
-    /*
-      Read the next value. The calling convention is the same as buf->read()
-      has.
-
-      RETURN
-        FALSE - Ok
-        TRUE  - EOF, reached the end of the buffer
-    */
-    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;
-        return FALSE;
-      }
-    }
-    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)))
-    {
-      *(buf->read_ptr1)= res;
-      if (buf->read_ptr2)
-        *buf->read_ptr2= get_next(buf->size2);
-      return FALSE;
-    }
-    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.
-*/
+#include "sql_lifo_buffer.h"
 
 /*
   DS-MRR implementation for one table. Create/use one object of this class for
@@ -440,7 +60,7 @@
   - Key-Ordered Retrieval
   - Rowid-Ordered Retrieval
 
-  DsMrr_impl will use one of the above strategies, or combination of them, 
+  DsMrr_impl will use one of the above strategies, or a combination of them, 
   according to the following diagram:
 
          (mrr function calls)
@@ -470,7 +90,7 @@
       (table records and range_ids)
 
   The choice of strategy depends on MRR scan properties, table properties
-  (whether we're scanning clustered primary key), and @@optimizer_flag
+  (whether we're scanning clustered primary key), and @@optimizer_switch
   settings.
   
   Key-Ordered Retrieval
@@ -541,7 +161,7 @@
 
   /*
     Secondary handler object. (created when needed, we need it when we need 
-    to run both index scan and rnd_pos() at the same time)
+    to run both index scan and rnd_pos() scan at the same time)
   */
   handler *h2;
   
@@ -568,14 +188,13 @@
   uchar *full_buf_end;
   
   /* 
-    When using both rowid and key buffers: the bound between key and rowid
+    When using both rowid and key buffers: the boundary between key and rowid
     parts of the buffer. This is the "original" value, actual memory ranges 
     used by key and rowid parts may be different because of dynamic space 
     reallocation between them.
   */
   uchar *rowid_buffer_end;
  
-
   /** Index scaning and key buffer-related members **/
   
   /* TRUE <=> We can get at most one index tuple for a lookup key */
@@ -689,4 +308,7 @@
   static uint key_buf_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
 };
 
+/**
+  @} (end of group DS-MRR declarations)
+*/
 



More information about the commits mailing list