midenok / mariadb

MariaDB server is a community developed fork of MySQL server. Started by core members of the original MySQL team, MariaDB actively works with outside developers to deliver the most featureful, stable, and sanely licensed open SQL server in the industry.
GNU General Public License v2.0
0 stars 0 forks source link

Bug#16916596: "ORDER BY...LIMIT" OPTIMIZATION CHOSE #123

Open midenok opened 4 months ago

midenok commented 4 months ago

order_by test segfault

Result

#0  0x00006151b1d68420 in Item_equal_iterator<List_iterator_fast, Item>::Item_equal_iterator (this=0x7638046194d8, item_eq=...) at ../src/sql/item_cmpfunc.h:3288
#1  0x00006151b1d59cbd in Item_equal_fields_iterator::Item_equal_fields_iterator (this=0x7638046194d8, item_eq=...) at ../src/sql/item_cmpfunc.h:3321
#2  0x00006151b1d51bfd in Item_equal::contains (this=0xa5a5a5a500000000, field=0x7637f4101880) at ../src/sql/item_cmpfunc.cc:6679
#3  0x00006151b21dd188 in test_if_order_by_key (join=0x7637f40178f0, order=0x7637f4017108, table=0x7637f4146198, idx=0, used_key_parts=0x0) at ../src/sql/sql_select.cc:22940
#4  0x00006151b21a2492 in make_join_select (join=0x7637f40178f0, select=0x7637f401a1b8, cond=0x7637f4016418) at ../src/sql/sql_select.cc:11742
#5  0x00006151b2193945 in JOIN::optimize_stage2 (this=0x7637f40178f0) at ../src/sql/sql_select.cc:2593
#6  0x00006151b2197b19 in JOIN::optimize_inner (this=0x7637f40178f0) at ../src/sql/sql_select.cc:2343
#7  0x00006151b2192378 in JOIN::optimize (this=0x7637f40178f0) at ../src/sql/sql_select.cc:1658
#8  0x00006151b2189cf6 in mysql_select (thd=0x7637f4000d38, tables=0x7637f4015b88, wild_num=0, fields=..., conds=0x7637f4016418, og_num=2, order=0x7637f4017108, group=0x7637f4016768, having=0x0, proc_param=0x0, select_options=2147748612, result=0x7637f4017278, unit=0x7637f4004c78, select_lex=0x7637f40153b8) at ../src/sql/sql_select.cc:4733
#9  0x00006151b21da077 in mysql_explain_union (thd=0x7637f4000d38, unit=0x7637f4004c78, result=0x7637f4017278) at ../src/sql/sql_select.cc:27333
#10 0x00006151b2129387 in execute_sqlcom_select (thd=0x7637f4000d38, all_tables=0x7637f4015b88) at ../src/sql/sql_parse.cc:6421
#11 0x00006151b211ec77 in mysql_execute_command (thd=0x7637f4000d38) at ../src/sql/sql_parse.cc:3980
#12 0x00006151b2117d4e in mysql_parse (thd=0x7637f4000d38, rawbuf=0x7637f40152a0 "explain\nselect b, count(*) num_cnt from t1 \nwhere a > 9750 group by b order by num_cnt limit 1", length=94, parser_state=0x76380461d5d0, is_com_multi=false, is_next_command=false) at ../src/sql/sql_parse.cc:8032

frame 3

22939         if (item_field->item_equal &&
22940             item_field->item_equal->contains(key_part->field))
22941           field= key_part->field;
(rr) p item_field
$6 = (Item_field *) 0x7637f40159f8
(rr) p item_field->item_equal
$7 = (Item_equal *) 0xa5a5a5a500000000
(rr) p item_field->field
$8 = (Field *) 0x7637f4015ae8
(rr) p item_field->field->field_name
$9 = {
  str = 0xa5a5a5a5a5a5a5a5 <error: Cannot access memory at address 0xa5a5a5a5a5a5a5a5>,
  length = 0
}
(rr) p *order
$13 = {
  next = 0x0,
  item = 0x7637f4017e78,
  item_ptr = 0x7637f4016fe0,
  fast_field_copier_func = 0xa5a5a5a5a5a5a5a5,
  fast_field_copier_setup = 0x0,
  counter = -1515870811,
  direction = st_order::ORDER_ASC,
  in_field_list = true,
  counter_used = false,
  field = 0xa5a5a5a5a5a5a5a5,
  buff = 0xa5a5a5a5a5a5a5a5 <error: Cannot access memory at address 0xa5a5a5a5a5a5a5a5>,
  used = 0,
  depend_map = 11936128518282651045
}
(rr) p order
$17 = (ORDER *) 0x7637f4017108

frame 4

(rr) p join->order
$14 = (ORDER *) 0x7637f4017108

Hypothesis

join->order is not yet initialized correctly?

midenok commented 4 months ago

This change from 4bf48e8bf0d fixes the result of main.distinct:

diff --git sql/opt_costconstants.cc sql/opt_costconstants.cc
index d825fafba4c..2ab0ce4144e 100644
--- sql/opt_costconstants.cc
+++ sql/opt_costconstants.cc
@@ -142,7 +142,7 @@ cost_constant_error Server_cost_constants::set(const LEX_CSTRING &name,
 */

 // The cost of reading a block from a main memory buffer pool
-const double SE_cost_constants::MEMORY_BLOCK_READ_COST= 1.0;
+const double SE_cost_constants::MEMORY_BLOCK_READ_COST= 0.25;

 // The cost of reading a block from an IO device (disk)
@@ -5224,18 +5224,18 @@ INSERT INTO `cc` VALUES
 EXPLAIN SELECT `varchar_nokey` g1  FROM cc  WHERE `int_nokey` AND `int_key`  <= 4
 HAVING g1  ORDER  BY `varchar_key` LIMIT  6   ;
 id     select_type     table   partitions      type    possible_keys   key     key_len ref     rows    filtered             Extra
-1      SIMPLE  cc      NULL    ALL     int_key NULL    NULL    NULL    20      45.00   Using where; Using filesort
+1      SIMPLE  cc      NULL    range   int_key int_key 4       NULL    10      90.00   Using where; Using filesort

MEMORY_BLOCK_READ_COST was introduced (splits IO_BLOCK_READ_COST())

commit 07cad655133
Author: Olav Sandstaa <olav.sandstaa@oracle.com>
Date:   Sun Jun 28 11:38:26 2015 +0200

    WL#7340 IO aware cost estimate function for data access
  double buffer_block_read_cost(double blocks) const {
    // memory_block_read_cost() returns MEMORY_BLOCK_READ_COST
    return blocks * m_se_cost_constants->memory_block_read_cost();
  }
  double io_block_read_cost(double blocks) const {
    // same for IO_BLOCK_READ_COST
    return blocks * m_se_cost_constants->io_block_read_cost();
  }

buffer_block_read_cost() usage

double Cost_model_table::page_read_cost(double pages) const {
  assert(m_initialized);
  assert(pages >= 0.0);

  const double in_mem = m_table->file->table_in_memory_estimate();

  const double pages_in_mem = pages * in_mem;
  const double pages_on_disk = pages - pages_in_mem;
  assert(pages_on_disk >= 0.0);

  const double cost =
      buffer_block_read_cost(pages_in_mem) + io_block_read_cost(pages_on_disk);

  return cost;
}

double Cost_model_table::page_read_cost_index(uint index, double pages) const {
  assert(m_initialized);
  assert(pages >= 0.0);

  double in_mem = m_table->file->index_in_memory_estimate(index);

  const double pages_in_mem = pages * in_mem;
  const double pages_on_disk = pages - pages_in_mem;

  const double cost =
      buffer_block_read_cost(pages_in_mem) + io_block_read_cost(pages_on_disk);

  return cost;
}

io_block_read_cost() has a bit more usages

disk_seek_base_cost(), disk_seek_prop_cost(), page_read_cost() (again), page_read_cost_index() (again), get_merge_buffers_cost(), get_use_cost()

commit dc1c0eb8b6e (HEAD)
Author: Olav Sandstaa <olav.sandstaa@oracle.com>
Date:   Fri Feb 28 09:54:21 2014 +0100

    WL#7182 Optimizer Cost Model API

io_block_read_cost() introduced and 1.0 replaced with it. io_block_read_cost() is added to get_merge_cost(), etc.

commit 250d5fb8bed
Author: Olav Sandstaa <olav.sandstaa@oracle.com>
Date:   Wed Jan 8 11:32:25 2014 +0100

    WL#7209 Handler interface for new cost model

IO_BLOCK_READ_COST() introduced (replaces index_only_read_time())

commit f33b6d722a5 (HEAD)
Author: Jorgen Loland <jorgen.loland@oracle.com>
Date:   Tue Nov 8 12:37:54 2011 +0100

    WL#5860: Make COST_VECT a properly encapsulated C++ class

index_only_read_time() introduced (replaces get_index_only_read_time())

commit 33fcd89d9ef
Author: sergefp@mysql.com <>
Date:   Sat Mar 10 00:08:24 2007 +0300

    WL#2474 "Multi Range Read: Change the default MRR implementation to implement new MRR interface"
    WL#2475 "Batched range read functions for MyISAM/InnoDb"
            "Index condition pushdown for MyISAM/InnoDB"

get_index_only_read_time() introduced (replaces block_size/2 ... calculations)

commit 67c6d5113c7
Author: sergefp@mysql.com <>
Date:   Thu Dec 18 06:08:00 2003 +0300

    Precise read time estimates for index_merge/Unique
midenok commented 2 months ago

TODO

Check this

commit c321e4c0179
Author: Jorgen Loland <jorgen.loland@oracle.com>
Date:   Thu Jun 27 16:46:44 2013 +0200

    BUG#16916596 Post push fix

diff --git sql/sql_select.cc sql/sql_select.cc
index fd73fb5f4da..0e52452bbab 100644
--- sql/sql_select.cc
+++ sql/sql_select.cc
@@ -3476,7 +3476,16 @@ int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,

   for (; order ; order=order->next, const_key_parts>>=1)
   {
-    Field *field=((Item_field*) (*order->item)->real_item())->field;
+
+    /*
+      Since only fields can be indexed, ORDER BY <something> that is
+      not a field cannot be resolved by using an index.
+    */
+    Item *real_itm= (*order->item)->real_item();
+    if (real_itm->type() != Item::FIELD_ITEM)
+      DBUG_RETURN(0);
+
+    Field *field= static_cast<Item_field*>(real_itm)->field;
     int flag;

     /*

In my version it is:

@@ -22891,6 +22891,9 @@ static int test_if_order_by_key(JOIN *join,

   for (; order ; order=order->next, const_key_parts>>=1)
   {
+    if ((*order->item)->real_type() != Item::FIELD_ITEM)
+      continue;
+
     Item_field *item_field= ((Item_field*) (*order->item)->real_item());
     Field *field= item_field->field;
     int flag;