diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index e97db21..9fc0a9f 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -330,6 +330,7 @@ void Single_line_formatting_helper::flush_on_one_line()
     ptr++;
   }
   owner->output.append(']');
+  buf_ptr= buffer; // psergey
 }
 
 
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 0d92d43..8c6071b 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -121,6 +121,8 @@
 #include "sql_statistics.h"
 #include "filesort.h"         // filesort_free_buffers
 
+#include "my_json_writer.h"
+
 #ifndef EXTRA_DEBUG
 #define test_rb_tree(A,B) {}
 #define test_use_count(A) {}
@@ -1898,7 +1900,7 @@ inline void SEL_ARG::make_root()
   left=right= &null_element;
 }
 
-SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, 
+SEL_ARG *SEL_ARG::clone_(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, 
                         SEL_ARG **next_arg)
 {
   SEL_ARG *tmp;
@@ -1924,7 +1926,7 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
     tmp->parent=new_parent;
     tmp->next_key_part=next_key_part;
     if (left != &null_element)
-      if (!(tmp->left=left->clone(param, tmp, next_arg)))
+      if (!(tmp->left=left->clone_(param, tmp, next_arg)))
 	return 0;				// OOM
 
     tmp->prev= *next_arg;			// Link into next/prev chain
@@ -1932,7 +1934,7 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
     (*next_arg)= tmp;
 
     if (right != &null_element)
-      if (!(tmp->right= right->clone(param, tmp, next_arg)))
+      if (!(tmp->right= right->clone_(param, tmp, next_arg)))
 	return 0;				// OOM
   }
   increment_use_count(1);
@@ -2028,7 +2030,7 @@ SEL_ARG *SEL_ARG::clone_tree(RANGE_OPT_PARAM *param)
 {
   SEL_ARG tmp_link,*next_arg,*root;
   next_arg= &tmp_link;
-  if (!(root= clone(param, (SEL_ARG *) 0, &next_arg)))
+  if (!(root= clone_(param, (SEL_ARG *) 0, &next_arg)))
     return 0;
   next_arg->next=0;				// Fix last link
   tmp_link.next->prev=0;			// Fix first link
@@ -2331,6 +2333,227 @@ static int fill_used_fields_bitmap(PARAM *param)
 }
 
 
+static void
+dbug_print_value_to_str(KEY_PART_INFO *key_part, const uchar *key, 
+                        uint used_length, String *str)
+{
+  StringBuffer<80> tmp;
+  TABLE *table= key_part->field->table;
+  my_bitmap_map *old_sets[2];
+  dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set);
+
+  Field *field=      key_part->field;
+  bool print_value= true;
+  if (field->real_maybe_null())
+  {
+    if (*key)
+    {
+      str->append("NULL", 4);
+      print_value= false;
+    }
+    key++;					// Skip null byte
+  }
+  if (print_value)
+  {
+    field->set_key_image(key, key_part->length);
+    if (field->type() == MYSQL_TYPE_BIT)
+      (void) field->val_int_as_str(&tmp, 1);
+    else
+      field->val_str(&tmp);
+    str->append(tmp.ptr(), tmp.length());
+  }
+  dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
+}
+
+void dbug_dump_sel_arg_tree(RANGE_OPT_PARAM *param,
+                       KEY_PART_INFO *part,
+                       SEL_ARG *arg,
+                       Json_writer *writer);
+
+// psergey:
+/*
+  This adds a STRING VALUE in human-readable form, like
+    "const1 < X < const2"
+    "X=const"
+    etc
+*/
+void dbug_dump_sel_arg_interval(RANGE_OPT_PARAM *param,
+                       KEY_PART_INFO *part,
+                       SEL_ARG *arg,
+                       Json_writer *writer)
+{
+  if (!(arg->min_flag & NO_MIN_RANGE) &&
+      !(arg->max_flag & NO_MAX_RANGE) &&
+      !(arg->min_flag & NEAR_MIN) &&
+      !(arg->max_flag & NEAR_MAX))
+  {
+    if (!memcmp(arg->min_value, arg->max_value, part->store_length))
+    {
+      // This key=const range.
+      StringBuffer<80> key_buf;
+      dbug_print_value_to_str(part, arg->min_value, part->store_length, &key_buf);
+      if (arg->next_key_part)
+      {
+        writer->start_object();
+        writer->add_member(key_buf.c_ptr());
+        dbug_dump_sel_arg_tree(param, 
+                          part + (arg->next_key_part->part - arg->part),
+                          arg->next_key_part,
+                          writer);
+        writer->end_object();
+      }
+      else
+        writer->add_str(key_buf.c_ptr());
+      return;
+    }
+  }
+
+  StringBuffer<80> res;
+  if (!(arg->min_flag & NO_MIN_RANGE))
+  {
+    StringBuffer<80> key_buf;
+    dbug_print_value_to_str(part, arg->min_value, part->store_length, &key_buf);
+    res.append(key_buf);
+    if (arg->min_flag & NEAR_MIN)
+      res.append(" < ");
+    else
+      res.append(" <= ");
+  }
+  res.append(part->field->field_name);
+  if (!(arg->max_flag & NO_MAX_RANGE))
+  {
+    if (arg->max_flag & NEAR_MAX)
+      res.append(" < ");
+    else
+      res.append(" <= ");
+
+    StringBuffer<80> key_buf;
+    dbug_print_value_to_str(part, arg->max_value, part->store_length, &key_buf);
+    res.append(key_buf);
+  }
+
+  if (arg->next_key_part)
+  {
+    writer->start_object();
+    writer->add_member(res.c_ptr());
+    dbug_dump_sel_arg_tree(param, 
+                      part + (arg->next_key_part->part - arg->part),
+                      arg->next_key_part,
+                      writer);
+    writer->end_object();
+  }
+  else
+    writer->add_str(res.c_ptr());
+}
+
+
+void dbug_dump_sel_arg_tree(RANGE_OPT_PARAM *param,
+                       KEY_PART_INFO *part,
+                       SEL_ARG *arg,
+                       Json_writer *writer)
+{
+  bool dump_in_array= false;
+  if (arg->first() != arg || arg->next)
+    dump_in_array= true;
+
+  if (dump_in_array)
+    writer->start_array();
+  for (arg= arg->first(); arg && arg != &null_element; arg=arg->next)
+  {
+    dbug_dump_sel_arg_interval(param, part, arg, writer);
+  }
+  if (dump_in_array)
+    writer->end_array();
+}
+
+
+void dbug_dump_sel_tree_rec(RANGE_OPT_PARAM *param, Json_writer *writer,
+                            SEL_TREE *tree)
+{
+  writer->add_member("tree").start_object();
+
+  bool have_ranges= false;
+  uint i;
+
+  for (i= 0; i < param->keys; i++)
+  {
+    if (tree->keys[i])
+      have_ranges= true;
+  }
+  
+  if (have_ranges)
+  {
+    writer->add_member("ranges").start_object();
+    for (i= 0; i < param->keys; i++)
+    {
+      SEL_ARG *arg;
+      if ((arg= tree->keys[i]))
+      {
+        const char *key_name= param->table->key_info[param->real_keynr[i]].name; 
+        KEY_PART_INFO *kp= 
+          param->table->key_info[param->real_keynr[i]].key_part;
+        writer->add_member(key_name);
+        dbug_dump_sel_arg_tree(param, kp + arg->part, arg, writer);
+      }
+    }
+    writer->end_object();
+  }
+  if (tree->merges.elements)
+  {
+    List_iterator<SEL_IMERGE> it(tree->merges);
+    SEL_IMERGE *imerge;
+    if (tree->merges.elements > 1)
+      writer->add_member("merges").start_array();
+
+    while ((imerge= it++))
+    {
+      if (tree->merges.elements > 1)
+        writer->start_object();
+
+      writer->add_member("index_merge").start_object();
+      for (SEL_TREE **ptree= imerge->trees;
+           ptree != imerge->trees_next;
+           ptree++)
+      {
+        dbug_dump_sel_tree_rec(param, writer, *ptree);
+      }
+      writer->end_object();
+
+      if (tree->merges.elements > 1)
+        writer->end_object();
+    }
+
+    if (tree->merges.elements > 1)
+      writer->end_array();
+  }
+
+  writer->end_object();
+}
+
+static int dump_no=0;
+
+void dbug_dump_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree)
+{
+  FILE *out;
+  char filename[100];
+  snprintf(filename, sizeof(filename), "/tmp/sel_tree_%d_%d.txt", 
+           (int)getpid(), ++dump_no);
+  out= fopen(filename, "wt");
+
+  fprintf(out, "%s\n", current_thd->query());
+  Json_writer writer;
+  if (!tree)
+  {
+    fprintf(out, "AAA: tree=null\n");
+    return;
+  }
+
+  dbug_dump_sel_tree_rec(param, &writer, tree);
+  
+  fprintf(out, "AAA:\n%s", writer.output.c_ptr_safe());
+  fclose(out);
+}
+
 /*
   Test if a key can be used in different ranges
 
@@ -2556,6 +2779,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
           read_time= (double) HA_POS_ERROR;
           goto free_mem;
         }
+        dbug_dump_sel_tree(&param, tree);
         /*
           If the tree can't be used for range scans, proceed anyway, as we
           can construct a group-min-max quick select
@@ -14427,9 +14651,9 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
 ** This should be changed to use a String to store each row instead
 ** of locking the DEBUG stream !
 *****************************************************************************/
-
-static void
-print_key(KEY_PART *key_part, const uchar *key, uint used_length)
+static void 
+print_key_to_str(KEY_PART *key_part, const uchar *key, uint used_length,
+                 String *str)
 {
   char buff[1024];
   const uchar *key_end= key+used_length;
@@ -14449,7 +14673,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
     {
       if (*key)
       {
-	fwrite("NULL",sizeof(char),4,DBUG_FILE);
+        str->append("NULL", 4);
 	continue;
       }
       key++;					// Skip null byte
@@ -14460,13 +14684,22 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
       (void) field->val_int_as_str(&tmp, 1);
     else
       field->val_str(&tmp);
-    fwrite(tmp.ptr(),sizeof(char),tmp.length(),DBUG_FILE);
+    str->append(tmp.ptr(), tmp.length());
     if (key+store_length < key_end)
       fputc('/',DBUG_FILE);
   }
   dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
 }
 
+static void
+print_key2(KEY_PART *key_part, const uchar *key, uint used_length)
+{
+  char buf[MAX_KEY_LENGTH];
+  String str(buf, sizeof(buf), &my_charset_bin);
+  str.length(0);
+
+  print_key_to_str(key_part, key, used_length, &str);
+}
 
 static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg)
 {
@@ -14507,7 +14740,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
       range= *pr;
       if (!(range->flag & NO_MIN_RANGE))
       {
-        print_key(key_parts, range->min_key, range->min_length);
+        print_key2(key_parts, range->min_key, range->min_length);
         if (range->flag & NEAR_MIN)
 	  fputs(" < ",DBUG_FILE);
         else
@@ -14521,7 +14754,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
 	  fputs(" < ",DBUG_FILE);
         else
 	  fputs(" <= ",DBUG_FILE);
-        print_key(key_parts, range->max_key, range->max_length);
+        print_key2(key_parts, range->max_key, range->max_length);
       }
       fputs("\n",DBUG_FILE);
     }
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 0c49563..2d7ec46 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -360,7 +360,7 @@ class SEL_ARG :public Sql_alloc
     return new SEL_ARG(field, part, min_value, arg->max_value,
 		       min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
   }
-  SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
+  SEL_ARG *clone_(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
 
   bool copy_min(SEL_ARG* arg)
   {						// Get overlapping range
