Commit message: MDEV-28094 Window function in expression in ORDER BY

call item->split_sum_func() in setup_order() just as
it's done in setup_fields()
CVE: CVE-2022-27451
CWE: NVD-CWE-noinfo
Vulnerability description: MariaDB Server v10.9 and below was discovered to contain a segmentation fault via the component sql/field_conv.cc.

Project: server

int print_explain_message_line(select_result_sink *result, 
                               uint8 options, bool is_analyze,
                               uint select_number,
                               const char *select_type,
                               ha_rows *rows,
                               const char *message)
{
  THD *thd= result->thd;
  MEM_ROOT *mem_root= thd->mem_root;
  Item *item_null= new (mem_root) Item_null(thd);
  List<Item> item_list;

  item_list.push_back(new (mem_root) Item_int(thd, (int32) select_number),
                      mem_root);
  item_list.push_back(new (mem_root) Item_string_sys(thd, select_type),
                      mem_root);
  /* `table` */
  item_list.push_back(item_null, mem_root);
  
  /* `partitions` */
  if (options & DESCRIBE_PARTITIONS)
    item_list.push_back(item_null, mem_root);
  
  /* type, possible_keys, key, key_len, ref */
  for (uint i=0 ; i < 5; i++)
    item_list.push_back(item_null, mem_root);

  /* `rows` */
  StringBuffer<64> rows_str;
  if (rows)
  {
    rows_str.append_ulonglong((ulonglong)(*rows));
    item_list.push_back(new (mem_root)
                        Item_string_sys(thd, rows_str.ptr(),
                                        rows_str.length()), mem_root);
  }
  else
    item_list.push_back(item_null, mem_root);

  /* `r_rows` */
  if (is_analyze)
    item_list.push_back(item_null, mem_root);

  /* `filtered` */
  if (is_analyze || options & DESCRIBE_EXTENDED)
    item_list.push_back(item_null, mem_root);
  
  /* `r_filtered` */
  if (is_analyze)
    item_list.push_back(item_null, mem_root);

  /* `Extra` */
  if (message)
    item_list.push_back(new (mem_root) Item_string_sys(thd, message),
                        mem_root);
  else
    item_list.push_back(item_null, mem_root);

  if (unlikely(thd->is_fatal_error) || unlikely(result->send_data(item_list)))
    return 1;
  return 0;
}

Project: server

static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
{
  DBUG_ENTER("add_ref_to_table_cond");
  if (!join_tab->ref.key_parts)
    DBUG_RETURN(FALSE);

  Item_cond_and *cond= new (thd->mem_root) Item_cond_and(thd);
  TABLE *table=join_tab->table;
  int error= 0;
  if (!cond)
    DBUG_RETURN(TRUE);

  for (uint i=0 ; i < join_tab->ref.key_parts ; i++)
  {
    Field *field=table->field[table->key_info[join_tab->ref.key].key_part[i].
			      fieldnr-1];
    Item *value=join_tab->ref.items[i];
    cond->add(new (thd->mem_root)
              Item_func_equal(thd, new (thd->mem_root) Item_field(thd, field),
                              value),
              thd->mem_root);
  }
  if (unlikely(thd->is_fatal_error))
    DBUG_RETURN(TRUE);
  if (!cond->is_fixed())
  {
    Item *tmp_item= (Item*) cond;
    cond->fix_fields(thd, &tmp_item);
    DBUG_ASSERT(cond == tmp_item);
  }
  if (join_tab->select)
  {
    Item *UNINIT_VAR(cond_copy);
    if (join_tab->select->pre_idx_push_select_cond)
      cond_copy= cond->copy_andor_structure(thd);
    if (join_tab->select->cond)
      error=(int) cond->add(join_tab->select->cond, thd->mem_root);
    join_tab->select->cond= cond;
    if (join_tab->select->pre_idx_push_select_cond)
    {
      Item *new_cond= and_conds(thd, cond_copy,
                                join_tab->select->pre_idx_push_select_cond);
      if (new_cond->fix_fields_if_needed(thd, &new_cond))
        error= 1;
      join_tab->pre_idx_push_select_cond=
        join_tab->select->pre_idx_push_select_cond= new_cond;
    }
    join_tab->set_select_cond(cond, __LINE__);
  }
  else if ((join_tab->select= make_select(join_tab->table, 0, 0, cond,
                                          (SORT_INFO*) 0, 0, &error)))
    join_tab->set_select_cond(cond, __LINE__);

  DBUG_RETURN(error ? TRUE : FALSE);
}

Project: server

static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
			    bool distinct,const char *message)
{
  THD *thd=join->thd;
  select_result *result=join->result;
  DBUG_ENTER("select_describe");

  if (join->select_lex->pushdown_select)
  {
    /*
      The whole statement was pushed down to a Smart Storage Engine. Do not
      attempt to produce a query plan locally.
    */
    DBUG_VOID_RETURN;
  }

  /* Update the QPF with latest values of using_temporary, using_filesort */
  for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
       unit;
       unit= unit->next_unit())
  {
    /*
      This fix_fields() call is to handle an edge case like this:
       
        SELECT ... UNION SELECT ... ORDER BY (SELECT ...)
      
      for such queries, we'll get here before having called
      subquery_expr->fix_fields(), which will cause failure to
    */
    if (unit->item && !unit->item->is_fixed())
    {
      Item *ref= unit->item;
      if (unit->item->fix_fields(thd, &ref))
        DBUG_VOID_RETURN;
      DBUG_ASSERT(ref == unit->item);
    }

    /* 
      Save plans for child subqueries, when
      (1) they are not parts of eliminated WHERE/ON clauses.
      (2) they are not VIEWs that were "merged for INSERT".
      (3) they are not hanging CTEs (they are needed for execution)
    */
    if (!(unit->item && unit->item->eliminated) &&                     // (1)
        !(unit->derived && unit->derived->merged_for_insert) &&        // (2)
        (!unit->with_element ||
          (unit->derived &&
           unit->derived->derived_result &&
           !unit->with_element->is_hanging_recursive())))              // (3)
    {
      if (mysql_explain_union(thd, unit, result))
        DBUG_VOID_RETURN;
    }
  }
  DBUG_VOID_RETURN;
}

Project: server

bool JOIN::get_best_combination()
{
  uint tablenr;
  table_map used_tables;
  JOIN_TAB *j;
  KEYUSE *keyuse;
  DBUG_ENTER("get_best_combination");

   /*
    Additional plan nodes for postjoin tmp tables:
      1? + // For GROUP BY
      1? + // For DISTINCT
      1? + // For aggregation functions aggregated in outer query
           // when used with distinct
      1? + // For ORDER BY
      1?   // buffer result
    Up to 2 tmp tables are actually used, but it's hard to tell exact number
    at this stage.
  */ 
  uint aggr_tables= (group_list ? 1 : 0) +
                    (select_distinct ?
                     (tmp_table_param.using_outer_summary_function ? 2 : 1) : 0) +
                    (order ? 1 : 0) +
       (select_options & (SELECT_BIG_RESULT | OPTION_BUFFER_RESULT) ? 1 : 0) ;
  
  if (aggr_tables == 0)
    aggr_tables= 1; /* For group by pushdown */

  if (select_lex->window_specs.elements)
    aggr_tables++;

  if (aggr_tables > 2)
    aggr_tables= 2;
  if (!(join_tab= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*
                                        (top_join_tab_count + aggr_tables))))
    DBUG_RETURN(TRUE);

  full_join=0;
  hash_join= FALSE;

  fix_semijoin_strategies_for_picked_join_order(this);

  if (inject_splitting_cond_for_all_tables_with_split_opt())
    DBUG_RETURN(TRUE);
   
  JOIN_TAB_RANGE *root_range;
  if (!(root_range= new (thd->mem_root) JOIN_TAB_RANGE))
    DBUG_RETURN(TRUE);
   root_range->start= join_tab;
  /* root_range->end will be set later */
  join_tab_ranges.empty();

  if (join_tab_ranges.push_back(root_range, thd->mem_root))
    DBUG_RETURN(TRUE);

  JOIN_TAB *sjm_nest_end= NULL;
  JOIN_TAB *sjm_nest_root= NULL;

  for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++)
  {
    TABLE *form;
    POSITION *cur_pos= &best_positions[tablenr];
    if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE || 
        cur_pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
    {
      /*
        Ok, we've entered an SJ-Materialization semi-join (note that this can't
        be done recursively, semi-joins are not allowed to be nested).
        1. Put into main join order a JOIN_TAB that represents a lookup or scan
           in the temptable.
      */
      bzero((void*)j, sizeof(JOIN_TAB));
      j->join= this;
      j->table= NULL; //temporary way to tell SJM tables from others.
      j->ref.key = -1;
      j->on_expr_ref= (Item**) &null_ptr;
      j->keys= key_map(1); /* The unique index is always in 'possible keys' in EXPLAIN */

      /*
        2. Proceed with processing SJM nest's join tabs, putting them into the
           sub-order
      */
      SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info;
      j->records_read= (sjm->is_sj_scan? sjm->rows : 1);
      j->records= (ha_rows) j->records_read;
      j->cond_selectivity= 1.0;
      JOIN_TAB *jt;
      JOIN_TAB_RANGE *jt_range;
      if (!(jt= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*sjm->tables)) ||
          !(jt_range= new JOIN_TAB_RANGE))
        DBUG_RETURN(TRUE);
      jt_range->start= jt;
      jt_range->end= jt + sjm->tables;
      join_tab_ranges.push_back(jt_range, thd->mem_root);
      j->bush_children= jt_range;
      sjm_nest_end= jt + sjm->tables;
      sjm_nest_root= j;

      j= jt;
    }
    
    *j= *best_positions[tablenr].table;

    j->bush_root_tab= sjm_nest_root;

    form= table[tablenr]= j->table;
    form->reginfo.join_tab=j;
    DBUG_PRINT("info",("type: %d", j->type));
    if (j->type == JT_CONST)
      goto loop_end;					// Handled in make_join_stat..

    j->loosescan_match_tab= NULL;  //non-nulls will be set later
    j->inside_loosescan_range= FALSE;
    j->ref.key = -1;
    j->ref.key_parts=0;

    if (j->type == JT_SYSTEM)
      goto loop_end;
    if ( !(keyuse= best_positions[tablenr].key))
    {
      j->type=JT_ALL;
      if (best_positions[tablenr].use_join_buffer &&
          tablenr != const_tables)
	full_join= 1;
    }

    /*if (best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN)
    {
      DBUG_ASSERT(!keyuse || keyuse->key ==
                             best_positions[tablenr].loosescan_picker.loosescan_key);
      j->index= best_positions[tablenr].loosescan_picker.loosescan_key;
    }*/

    if ((j->type == JT_REF || j->type == JT_EQ_REF) &&
        is_hash_join_key_no(j->ref.key))
      hash_join= TRUE; 

    j->range_rowid_filter_info= best_positions[tablenr].range_rowid_filter_info;

  loop_end:
    /* 
      Save records_read in JOIN_TAB so that select_describe()/etc don't have
      to access join->best_positions[]. 
    */
    j->records_read= best_positions[tablenr].records_read;
    j->cond_selectivity= best_positions[tablenr].cond_selectivity;
    map2table[j->table->tablenr]= j;

    /* If we've reached the end of sjm nest, switch back to main sequence */
    if (j + 1 == sjm_nest_end)
    {
      j->last_leaf_in_bush= TRUE;
      j= sjm_nest_root;
      sjm_nest_root= NULL;
      sjm_nest_end= NULL;
    }
  }
  root_range->end= j;

  used_tables= OUTER_REF_TABLE_BIT;		// Outer row is already read
  for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++)
  {
    if (j->bush_children)
      j= j->bush_children->start;

    used_tables|= j->table->map;
    if (j->type != JT_CONST && j->type != JT_SYSTEM)
    {
      if ((keyuse= best_positions[tablenr].key) &&
          create_ref_for_key(this, j, keyuse, TRUE, used_tables))
        DBUG_RETURN(TRUE);              // Something went wrong
    }
    if (j->last_leaf_in_bush)
      j= j->bush_root_tab;
  }
 
  top_join_tab_count= (uint)(join_tab_ranges.head()->end - 
                      join_tab_ranges.head()->start);

  if (unlikely(thd->trace_started()))
    print_final_join_order(this);

  update_depend_map(this);
  DBUG_RETURN(0);
}

Project: server

int SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("SELECT_LEX::period_setup_conds");
  const bool update_conds= !skip_setup_conds(thd);

  Query_arena backup;
  Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);

  DBUG_ASSERT(!tables->next_local && tables->table);

  Item *result= NULL;
  for (TABLE_LIST *table= tables; table; table= table->next_local)
  {
    if (!table->table)
      continue;
    vers_select_conds_t &conds= table->period_conditions;
    if (!table->table->s->period.name.streq(conds.name))
    {
      my_error(ER_PERIOD_NOT_FOUND, MYF(0), conds.name.str);
      if (arena)
        thd->restore_active_arena(arena, &backup);
      DBUG_RETURN(-1);
    }

    if (update_conds)
    {
      conds.period= &table->table->s->period;
      result= and_items(thd, result,
                        period_get_condition(thd, table, this, &conds, true));
    }
  }
  if (update_conds)
    where= and_items(thd, where, result);

  if (arena)
    thd->restore_active_arena(arena, &backup);

  DBUG_RETURN(0);
}

Project: server

int JOIN::save_explain_data_intern(Explain_query *output, 
                                   bool need_tmp_table_arg,
                                   bool need_order_arg, bool distinct_arg, 
                                   const char *message)
{
  JOIN *join= this; /* Legacy: this code used to be a non-member function */
  DBUG_ENTER("JOIN::save_explain_data_intern");
  DBUG_PRINT("info", ("Select %p (%u), type %s, message %s",
		      join->select_lex,  join->select_lex->select_number,
                      join->select_lex->type,
		      message ? message : "NULL"));
  DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
  /* fake_select_lex is created/printed by Explain_union */
  DBUG_ASSERT(join->select_lex != join->unit->fake_select_lex);

  /* There should be no attempts to save query plans for merged selects */
  DBUG_ASSERT(!join->select_lex->master_unit()->derived ||
              join->select_lex->master_unit()->derived->is_materialized_derived() ||
              join->select_lex->master_unit()->derived->is_with_table());

  /* Don't log this into the slow query log */

  if (message)
  {
    if (!(explain= new (output->mem_root)
          Explain_select(output->mem_root,
                         thd->lex->analyze_stmt)))
      DBUG_RETURN(1);
#ifndef DBUG_OFF
    explain->select_lex= select_lex;
#endif
    join->select_lex->set_explain_type(true);

    explain->select_id= join->select_lex->select_number;
    explain->select_type= join->select_lex->type;
    explain->linkage= select_lex->get_linkage();
    explain->using_temporary= need_tmp;
    explain->using_filesort=  need_order_arg;
    /* Setting explain->message means that all other members are invalid */
    explain->message= message;

    if (select_lex->master_unit()->derived)
      explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
    if (save_agg_explain_data(this, explain))
      DBUG_RETURN(1);

    output->add_node(explain);
  }
  else if (pushdown_query)
  {
    if (!(explain= new (output->mem_root)
          Explain_select(output->mem_root,
                         thd->lex->analyze_stmt)))
      DBUG_RETURN(1);
    select_lex->set_explain_type(true);

    explain->select_id=   select_lex->select_number;
    explain->select_type= select_lex->type;
    explain->linkage= select_lex->get_linkage();
    explain->using_temporary= need_tmp;
    explain->using_filesort=  need_order_arg;
    explain->message= "Storage engine handles GROUP BY";

    if (select_lex->master_unit()->derived)
      explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
    output->add_node(explain);
  }
  else
  {
    Explain_select *xpl_sel;
    explain= xpl_sel= 
      new (output->mem_root) Explain_select(output->mem_root, 
                                            thd->lex->analyze_stmt);
    if (!explain)
      DBUG_RETURN(1);

    table_map used_tables=0;

    join->select_lex->set_explain_type(true);
    xpl_sel->select_id= join->select_lex->select_number;
    xpl_sel->select_type= join->select_lex->type;
    xpl_sel->linkage= select_lex->get_linkage();
    xpl_sel->is_lateral= ((select_lex->get_linkage() == DERIVED_TABLE_TYPE) &&
                          (select_lex->uncacheable & UNCACHEABLE_DEPENDENT));
    if (select_lex->master_unit()->derived)
      xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
    
    if (save_agg_explain_data(this, xpl_sel))
      DBUG_RETURN(1);

    xpl_sel->exec_const_cond= exec_const_cond;
    xpl_sel->outer_ref_cond= outer_ref_cond;
    xpl_sel->pseudo_bits_cond= pseudo_bits_cond;
    if (tmp_having)
      xpl_sel->having= tmp_having;
    else
      xpl_sel->having= having;
    xpl_sel->having_value= having_value;

    JOIN_TAB* const first_top_tab= join->first_breadth_first_tab();
    JOIN_TAB* prev_bush_root_tab= NULL;

    Explain_basic_join *cur_parent= xpl_sel;
    
    for (JOIN_TAB *tab= first_explain_order_tab(join); tab;
         tab= next_explain_order_tab(join, tab))
    {
      JOIN_TAB *saved_join_tab= NULL;
      TABLE *cur_table= tab->table;

      /* Don't show eliminated tables */
      if (cur_table->map & join->eliminated_tables)
      {
        used_tables|= cur_table->map;
        continue;
      }


      Explain_table_access *eta= (new (output->mem_root)
                                  Explain_table_access(output->mem_root));

      if (!eta)
        DBUG_RETURN(1);
      if (tab->bush_root_tab != prev_bush_root_tab)
      {
        if (tab->bush_root_tab)
        {
          /* 
            We've entered an SJ-Materialization nest. Create an object for it.
          */
          if (!(cur_parent=
                new (output->mem_root) Explain_basic_join(output->mem_root)))
            DBUG_RETURN(1);

          JOIN_TAB *first_child= tab->bush_root_tab->bush_children->start;
          cur_parent->select_id=
            first_child->emb_sj_nest->sj_subq_pred->get_identifier();
        }
        else
        {
          /* 
            We've just left an SJ-Materialization nest. We are at the join tab
            that 'embeds the nest'
          */
          DBUG_ASSERT(tab->bush_children);
          eta->sjm_nest= cur_parent;
          cur_parent= xpl_sel;
        }
      }
      prev_bush_root_tab= tab->bush_root_tab;

      cur_parent->add_table(eta, output);
      if (tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab))
        DBUG_RETURN(1);

      if (saved_join_tab)
        tab= saved_join_tab;

      // For next iteration
      used_tables|= cur_table->map;
    }
    output->add_node(xpl_sel);
  }

  /*
    Don't try to add query plans for child selects if this select was pushed
    down into a Smart Storage Engine:
    - the entire statement was pushed down ("PUSHED SELECT"), or
    - this derived table was pushed down ("PUSHED DERIVED")
  */
  if (!select_lex->pushdown_select && select_lex->type != pushed_derived_text)
  {
    for (SELECT_LEX_UNIT *tmp_unit= join->select_lex->first_inner_unit();
         tmp_unit;
         tmp_unit= tmp_unit->next_unit())
    {
      /*
        Display subqueries only if
        (1) they are not parts of ON clauses that were eliminated by table
            elimination.
        (2) they are not merged derived tables
        (3) they are not hanging CTEs (they are needed for execution)
      */
      if (!(tmp_unit->item && tmp_unit->item->eliminated) &&    // (1)
          (!tmp_unit->derived ||
           tmp_unit->derived->is_materialized_derived()) &&     // (2)
          (!tmp_unit->with_element  ||
           (tmp_unit->derived &&
            tmp_unit->derived->derived_result &&
            !tmp_unit->with_element->is_hanging_recursive())))  // (3)
      {
        explain->add_child(tmp_unit->first_select()->select_number);
      }
    }
  }

  if (select_lex->is_top_level_node())
    output->query_plan_ready();

  DBUG_RETURN(0);
}

Project: server

Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
                                  bool top_level_arg)
{
  Item *real_item= args[0]->real_item();
  if (real_item->type() == Item::FIELD_ITEM)
  {
    Field *field= ((Item_field*) real_item)->field;

    if ((field->flags & NOT_NULL_FLAG) &&
        field->type_handler()->cond_notnull_field_isnull_to_field_eq_zero())
    {
      /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
      /*
        See BUG#12594011
        Documentation says that
        SELECT datetime_notnull d FROM t1 WHERE d IS NULL
        shall return rows where d=='0000-00-00'

        Thus, for DATE and DATETIME columns defined as NOT NULL,
        "date_notnull IS NULL" has to be modified to
        "date_notnull IS NULL OR date_notnull == 0" (if outer join)
        "date_notnull == 0"                         (otherwise)

      */

      Item *item0= new(thd->mem_root) Item_bool(thd, false);
      Item *eq_cond= new(thd->mem_root) Item_func_eq(thd, args[0], item0);
      if (!eq_cond)
        return this;

      COND *cond= this;
      if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
      {
        // outer join: transform "col IS NULL" to "col IS NULL or col=0"
        Item *or_cond= new(thd->mem_root) Item_cond_or(thd, eq_cond, this);
        if (!or_cond)
          return this;
        cond= or_cond;
      }
      else
      {
        // not outer join: transform "col IS NULL" to "col=0"
        cond= eq_cond;
      }

      cond->fix_fields(thd, &cond);
      /*
        Note: although args[0] is a field, cond can still be a constant
        (in case field is a part of a dependent subquery).

        Note: we call cond->Item::remove_eq_conds() non-virtually (statically)
        for performance purpose.
        A non-qualified call, i.e. just cond->remove_eq_conds(),
        would call Item_bool_func2::remove_eq_conds() instead, which would
        try to do some extra job to detect if args[0] and args[1] are
        equivalent items. We know they are not (we have field=0 here).
      */
      return cond->Item::remove_eq_conds(thd, cond_value, false);
    }

    /*
      Handles this special case for some ODBC applications:
      The are requesting the row that was just updated with a auto_increment
      value with this construct:

      SELECT * from table_name where auto_increment_column IS NULL
      This will be changed to:
      SELECT * from table_name where auto_increment_column = LAST_INSERT_ID

      Note, this substitution is done if the NULL test is the only condition!
      If the NULL test is a part of a more complex condition, it is not
      substituted and is treated normally:
        WHERE auto_increment IS NULL AND something_else
    */

    if (top_level_arg) // "auto_increment_column IS NULL" is the only condition
    {
      if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
          (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
          (thd->first_successful_insert_id_in_prev_stmt > 0 &&
           thd->substitute_null_with_insert_id))
      {
  #ifdef HAVE_QUERY_CACHE
        query_cache_abort(thd, &thd->query_cache_tls);
  #endif
        COND *new_cond, *cond= this;
        /* If this fails, we will catch it later before executing query */
        if ((new_cond= new (thd->mem_root) Item_func_eq(thd, args[0],
                                        new (thd->mem_root) Item_int(thd, "last_insert_id()",
                                                     thd->read_first_successful_insert_id_in_prev_stmt(),
                                                     MY_INT64_NUM_DECIMAL_DIGITS))))
        {
          cond= new_cond;
          /*
            Item_func_eq can't be fixed after creation so we do not check
            cond->is_fixed(), also it do not need tables so we use 0 as second
            argument.
          */
          cond->fix_fields(thd, &cond);
        }
        /*
          IS NULL should be mapped to LAST_INSERT_ID only for first row, so
          clear for next row
        */
        thd->substitute_null_with_insert_id= FALSE;

        *cond_value= Item::COND_OK;
        return cond;
      }
    }
  }
  return Item::remove_eq_conds(thd, cond_value, top_level_arg);
}

Project: server

bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
                             bool need_tmp_table, bool need_order, 
                             bool distinct)
{
  DBUG_ENTER("JOIN::save_explain_data");
  DBUG_PRINT("enter", ("Save explain Select_lex: %u (%p)  parent lex: %p  stmt_lex: %p  present select: %u (%p)",
                        select_lex->select_number, select_lex,
                        select_lex->parent_lex, thd->lex->stmt_lex,
                        (output->get_select(select_lex->select_number) ?
                         select_lex->select_number : 0),
                        (output->get_select(select_lex->select_number) ?
                         output->get_select(select_lex->select_number)
                         ->select_lex : NULL)));
  /*
    If there is SELECT in this statement with the same number it must be the
    same SELECT
  */
  DBUG_SLOW_ASSERT(select_lex->select_number == UINT_MAX ||
              select_lex->select_number == INT_MAX ||
              !output ||
              !output->get_select(select_lex->select_number) ||
              output->get_select(select_lex->select_number)->select_lex ==
                select_lex);

  if (select_lex->select_number != UINT_MAX && 
      select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && 
      have_query_plan != JOIN::QEP_NOT_PRESENT_YET && 
      have_query_plan != JOIN::QEP_DELETED &&  // this happens when there was 
                                               // no QEP ever, but then
                                               //cleanup() is called multiple times
      output && // for "SET" command in SPs.
      (can_overwrite? true: !output->get_select(select_lex->select_number)))
  {
    const char *message= NULL;
    if (!table_count || !tables_list || zero_result_cause)
    {
      /* It's a degenerate join */
      message= zero_result_cause ? zero_result_cause : "No tables used";
    }
    bool rc= save_explain_data_intern(thd->lex->explain, need_tmp_table,
                                      need_order, distinct, message);
    DBUG_RETURN(rc);
  }
  
  /*
    Can have join_tab==NULL for degenerate cases (e.g. SELECT .. UNION ... SELECT LIMIT 0)
  */
  if (select_lex == select_lex->master_unit()->fake_select_lex && join_tab)
  {
    /* 
      This is fake_select_lex. It has no query plan, but we need to set up a
      tracker for ANALYZE 
    */
    uint nr= select_lex->master_unit()->first_select()->select_number;
    Explain_union *eu= output->get_union(nr);
    explain= &eu->fake_select_lex_explain;
    join_tab[0].tracker= eu->get_fake_select_lex_tracker();
    for (uint i=0 ; i < exec_join_tab_cnt() + aggr_tables; i++)
    {
      if (join_tab[i].filesort)
      {
        if (!(join_tab[i].filesort->tracker=
              new Filesort_tracker(thd->lex->analyze_stmt)))
          DBUG_RETURN(1);
      }
    }
  }
  DBUG_RETURN(0);
}

Project: server

static void update_const_equal_items(THD *thd, COND *cond, JOIN_TAB *tab,
                                     bool const_key)
{
  if (!(cond->used_tables() & tab->table->map))
    return;

  if (cond->type() == Item::COND_ITEM)
  {
    List<Item> *cond_list= ((Item_cond*) cond)->argument_list(); 
    List_iterator_fast<Item> li(*cond_list);
    Item *item;
    while ((item= li++))
      update_const_equal_items(thd, item, tab,
                               (((Item_cond*) cond)->top_level() &&
                                ((Item_cond*) cond)->functype() ==
                                Item_func::COND_AND_FUNC));
  }
  else if (cond->type() == Item::FUNC_ITEM && 
           ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
  {
    Item_equal *item_equal= (Item_equal *) cond;
    bool contained_const= item_equal->get_const() != NULL;
    item_equal->update_const(thd);
    if (!contained_const && item_equal->get_const())
    {
      /* Update keys for range analysis */
      Item_equal_fields_iterator it(*item_equal);
      while (it++)
      {
        Field *field= it.get_curr_field();
        JOIN_TAB *stat= field->table->reginfo.join_tab;
        key_map possible_keys= field->key_start;
        possible_keys.intersect(field->table->keys_in_use_for_query);
        stat[0].const_keys.merge(possible_keys);

        /*
          For each field in the multiple equality (for which we know that it 
          is a constant) we have to find its corresponding key part, and set 
          that key part in const_key_parts.
        */  
        if (!possible_keys.is_clear_all())
        {
          TABLE *field_tab= field->table;
          KEYUSE *use;
          for (use= stat->keyuse; use && use->table == field_tab; use++)
            if (const_key &&
                !use->is_for_hash_join() && possible_keys.is_set(use->key) && 
                field_tab->key_info[use->key].key_part[use->keypart].field ==
                field)
              field_tab->const_key_parts[use->key]|= use->keypart_map;
        }
      }
    }
  }
}

Project: server

remove_const(JOIN *join,ORDER *first_order, COND *cond,
             bool change_list, bool *simple_order)
{
  *simple_order= join->rollup.state == ROLLUP::STATE_NONE;
  if (join->only_const_tables())
    return change_list ? 0 : first_order;		// No need to sort

  ORDER *order,**prev_ptr, *tmp_order;
  table_map UNINIT_VAR(first_table); /* protected by first_is_base_table */
  table_map not_const_tables= ~join->const_table_map;
  table_map ref;
  bool first_is_base_table= FALSE;
  DBUG_ENTER("remove_const");
  
  /*
    Join tab is set after make_join_statistics() has been called.
    In case of one table with GROUP BY this function is called before
    join_tab is set for the GROUP_BY expression
  */
  if (join->join_tab)
  {
    if (join->join_tab[join->const_tables].table)
    {
      first_table= join->join_tab[join->const_tables].table->map;
      first_is_base_table= TRUE;
    }
  
    /*
      Cleanup to avoid interference of calls of this function for
      ORDER BY and GROUP BY
    */
    for (JOIN_TAB *tab= join->join_tab + join->const_tables;
         tab < join->join_tab + join->table_count;
         tab++)
      tab->cached_eq_ref_table= FALSE;

    JOIN_TAB *head= join->join_tab + join->const_tables;
    *simple_order= head->on_expr_ref[0] == NULL;
    if (*simple_order && head->table->file->ha_table_flags() & HA_SLOW_RND_POS)
    {
      uint u1, u2, u3;
      /*
        normally the condition is (see filesort_use_addons())

          length + sortlength <= max_length_for_sort_data

        but for HA_SLOW_RND_POS tables we relax it a bit, as the alternative
        is to use a temporary table, which is rather expensive.

        TODO proper cost estimations
      */
      *simple_order= filesort_use_addons(head->table, 0, &u1, &u2, &u3);
    }
  }
  else
  {
    first_is_base_table= FALSE;
    first_table= 0;                     // Not used, for gcc
  }

  prev_ptr= &first_order;

  /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */

  update_depend_map_for_order(join, first_order);
  for (order=first_order; order ; order=order->next)
  {
    table_map order_tables=order->item[0]->used_tables();
    if (order->item[0]->with_sum_func() ||
        order->item[0]->with_window_func ||
        /*
          If the outer table of an outer join is const (either by itself or
          after applying WHERE condition), grouping on a field from such a
          table will be optimized away and filesort without temporary table
          will be used unless we prevent that now. Filesort is not fit to
          handle joins and the join condition is not applied. We can't detect
          the case without an expensive test, however, so we force temporary
          table for all queries containing more than one table, ROLLUP, and an
          outer join.
         */
        (join->table_count > 1 && join->rollup.state == ROLLUP::STATE_INITED &&
        join->outer_join))
      *simple_order=0;				// Must do a temp table to sort
    else if (!(order_tables & not_const_tables))
    {
      if (order->item[0]->with_subquery())
      {
        /*
          Delay the evaluation of constant ORDER and/or GROUP expressions that
          contain subqueries until the execution phase.
        */
        join->exec_const_order_group_cond.push_back(order->item[0],
                                                    join->thd->mem_root);
      }
      DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
      continue;
    }
    else
    {
      if (order_tables & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))
	*simple_order=0;
      else
      {
	if (cond && const_expression_in_where(cond,order->item[0]))
	{
	  DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
	  continue;
	}
	if (first_is_base_table &&
            (ref=order_tables & (not_const_tables ^ first_table)))
	{
	  if (!(order_tables & first_table) &&
              only_eq_ref_tables(join,first_order, ref))
	  {
	    DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
	    continue;
	  }
          /*
            UseMultipleEqualitiesToRemoveTempTable:
            Can use multiple-equalities here to check that ORDER BY columns
            can be used without tmp. table.
          */
          bool can_subst_to_first_table= false;
          bool first_is_in_sjm_nest= false;
          if (first_is_base_table)
          {
            TABLE_LIST *tbl_for_first=
              join->join_tab[join->const_tables].table->pos_in_table_list;
            first_is_in_sjm_nest= tbl_for_first->sj_mat_info &&
                                  tbl_for_first->sj_mat_info->is_used;
          }
          /*
            Currently we do not employ the optimization that uses multiple
            equalities for ORDER BY to remove tmp table in the case when
            the first table happens to be the result of materialization of
            a semi-join nest ( <=> first_is_in_sjm_nest == true).

            When a semi-join nest is materialized and scanned to look for
            possible matches in the remaining tables for every its row
            the fields from the result of materialization are copied
            into the record buffers of tables from the semi-join nest.
            So these copies are used to access the remaining tables rather
            than the fields from the result of materialization.

            Unfortunately now this so-called 'copy back' technique is
            supported only if the rows  are scanned with the rr_sequential
            function, but not with other rr_* functions that are employed
            when the result of materialization is required to be sorted.

            TODO: either to support 'copy back' technique for the above case,
                  or to get rid of this technique altogether.
          */
          if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_ORDERBY_EQ_PROP) &&
              first_is_base_table && !first_is_in_sjm_nest &&
              order->item[0]->real_item()->type() == Item::FIELD_ITEM &&
              join->cond_equal)
          {
            table_map first_table_bit=
              join->join_tab[join->const_tables].table->map;

            Item *item= order->item[0];

            /*
              TODO: equality substitution in the context of ORDER BY is 
              sometimes allowed when it is not allowed in the general case.
              
              We make the below call for its side effect: it will locate the
              multiple equality the item belongs to and set item->item_equal
              accordingly.
            */
            Item *res= item->propagate_equal_fields(join->thd,
                                                    Value_source::
                                                    Context_identity(),
                                                    join->cond_equal);
            Item_equal *item_eq;
            if ((item_eq= res->get_item_equal()))
            {
              Item *first= item_eq->get_first(NO_PARTICULAR_TAB, NULL);
              if (first->const_item() || first->used_tables() ==
                                         first_table_bit)
              {
                can_subst_to_first_table= true;
              }
            }
          }

          if (!can_subst_to_first_table)
          {
            *simple_order=0;			// Must do a temp table to sort
          }
	}
      }
    }
    /* Remove ORDER BY entries that we have seen before */
    for (tmp_order= first_order;
         tmp_order != order;
         tmp_order= tmp_order->next)
    {
      if (tmp_order->item[0]->eq(order->item[0],1))
        break;
    }
    if (tmp_order != order)
      continue;                                // Duplicate order by. Remove
    
    if (change_list)
      *prev_ptr= order;				// use this entry
    prev_ptr= &order->next;
  }
  if (change_list)
    *prev_ptr=0;
  if (prev_ptr == &first_order)			// Nothing to sort/group
    *simple_order=1;
#ifndef DBUG_OFF
  if (unlikely(join->thd->is_error()))
    DBUG_PRINT("error",("Error from remove_const"));
#endif
  DBUG_PRINT("exit",("simple_order: %d",(int) *simple_order));
  DBUG_RETURN(first_order);
}

Project: server

test_if_quick_select(JOIN_TAB *tab)
{
  DBUG_EXECUTE_IF("show_explain_probe_test_if_quick_select", 
                  if (dbug_user_var_equals_int(tab->join->thd, 
                                               "show_explain_probe_select_id", 
                                               tab->join->select_lex->select_number))
                        dbug_serve_apcs(tab->join->thd, 1);
                 );


  delete tab->select->quick;
  tab->select->quick=0;

  if (tab->table->file->inited != handler::NONE)
    tab->table->file->ha_index_or_rnd_end();

  int res= tab->select->test_quick_select(tab->join->thd, tab->keys,
                                          (table_map) 0, HA_POS_ERROR, 0,
                                          FALSE, /*remove where parts*/FALSE,
                                          FALSE);
  if (tab->explain_plan && tab->explain_plan->range_checked_fer)
    tab->explain_plan->range_checked_fer->collect_data(tab->select->quick);

  return res;
}

Project: server

setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps, uint field_count)
{
  uint bitmap_size= bitmap_buffer_size(field_count);

  DBUG_ASSERT(table->s->virtual_fields == 0);

  my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
              FALSE);
  bitmaps+= bitmap_size;
  my_bitmap_init(&table->tmp_set,
                 (my_bitmap_map*) bitmaps, field_count, FALSE);
  bitmaps+= bitmap_size;
  my_bitmap_init(&table->eq_join_set,
                 (my_bitmap_map*) bitmaps, field_count, FALSE);
  bitmaps+= bitmap_size;
  my_bitmap_init(&table->cond_set,
                 (my_bitmap_map*) bitmaps, field_count, FALSE);
  bitmaps+= bitmap_size;
  my_bitmap_init(&table->has_value_set,
                 (my_bitmap_map*) bitmaps, field_count, FALSE);
  /* write_set and all_set are copies of read_set */
  table->def_write_set= table->def_read_set;
  table->s->all_set= table->def_read_set;
  bitmap_set_all(&table->s->all_set);
  table->default_column_bitmaps();
}

Project: server

best_extension_by_limited_search(JOIN      *join,
                                 table_map remaining_tables,
                                 uint      idx,
                                 double    record_count,
                                 double    read_time,
                                 uint      search_depth,
                                 uint      prune_level,
                                 uint      use_cond_selectivity)
{
  DBUG_ENTER("best_extension_by_limited_search");

  THD *thd= join->thd;

  DBUG_EXECUTE_IF("show_explain_probe_best_ext_lim_search", 
                  if (dbug_user_var_equals_int(thd, 
                                               "show_explain_probe_select_id", 
                                               join->select_lex->select_number))
                        dbug_serve_apcs(thd, 1);
                 );

  if (unlikely(thd->check_killed()))  // Abort
    DBUG_RETURN(TRUE);

  DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
                                 "SOFAR:"););

  /* 
     'join' is a partial plan with lower cost than the best plan so far,
     so continue expanding it further with the tables in 'remaining_tables'.
  */
  JOIN_TAB *s;
  double best_record_count= DBL_MAX;
  double best_read_time=    DBL_MAX;
  bool disable_jbuf= join->thd->variables.join_cache_level == 0;

  DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time,
                                "part_plan"););

  /* 
    If we are searching for the execution plan of a materialized semi-join nest
    then allowed_tables contains bits only for the tables from this nest.
  */
  table_map allowed_tables= ~(table_map)0;
  if (join->emb_sjm_nest)
    allowed_tables= join->emb_sjm_nest->sj_inner_tables & ~join->const_table_map;

  for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
  {
    table_map real_table_bit= s->table->map;
    if ((remaining_tables & real_table_bit) && 
        (allowed_tables & real_table_bit) &&
        !(remaining_tables & s->dependent) && 
        (!idx || !check_interleaving_with_nj(s)))
    {
      double current_record_count, current_read_time;
      POSITION *position= join->positions + idx;

      Json_writer_object trace_one_table(thd);
      if (unlikely(thd->trace_started()))
      {
        trace_plan_prefix(join, idx, remaining_tables);
        trace_one_table.add_table_name(s);
      }

      /* Find the best access method from 's' to the current partial plan */
      POSITION loose_scan_pos;
      best_access_path(join, s, remaining_tables, join->positions, idx,
                       disable_jbuf, record_count, position, &loose_scan_pos);

      /* Compute the cost of extending the plan with 's' */
      current_record_count= COST_MULT(record_count, position->records_read);
      const double filter_cmp_gain= position->range_rowid_filter_info
        ? position->range_rowid_filter_info->get_cmp_gain(current_record_count)
        : 0;
      current_read_time=COST_ADD(read_time,
                                 COST_ADD(position->read_time -
                                          filter_cmp_gain,
                                          current_record_count /
                                          (double) TIME_FOR_COMPARE));

      if (unlikely(thd->trace_started()))
      {
        trace_one_table.add("rows_for_plan", current_record_count);
        trace_one_table.add("cost_for_plan", current_read_time);
      }
      advance_sj_state(join, remaining_tables, idx, &current_record_count,
                       &current_read_time, &loose_scan_pos);

      /* Expand only partial plans with lower cost than the best QEP so far */
      if (current_read_time >= join->best_read)
      {
        DBUG_EXECUTE("opt", print_plan(join, idx+1,
                                       current_record_count,
                                       read_time,
                                       current_read_time,
                                       "prune_by_cost"););
        trace_one_table.add("pruned_by_cost", true);
        restore_prev_nj_state(s);
        restore_prev_sj_state(remaining_tables, s, idx);
        continue;
      }

      /*
        Prune some less promising partial plans. This heuristic may miss
        the optimal QEPs, thus it results in a non-exhaustive search.
      */
      if (prune_level == 1)
      {
        if (best_record_count > current_record_count ||
            best_read_time > current_read_time ||
            (idx == join->const_tables &&  // 's' is the first table in the QEP
            s->table == join->sort_by_table))
        {
          if (best_record_count >= current_record_count &&
              best_read_time >= current_read_time &&
              /* TODO: What is the reasoning behind this condition? */
              (!(s->key_dependent & allowed_tables & remaining_tables) ||
               join->positions[idx].records_read < 2.0))
          {
            best_record_count= current_record_count;
            best_read_time=    current_read_time;
          }
        }
        else
        {
          DBUG_EXECUTE("opt", print_plan(join, idx+1,
                                         current_record_count,
                                         read_time,
                                         current_read_time,
                                         "pruned_by_heuristic"););
          trace_one_table.add("pruned_by_heuristic", true);
          restore_prev_nj_state(s);
          restore_prev_sj_state(remaining_tables, s, idx);
          continue;
        }
      }

      double pushdown_cond_selectivity= 1.0;
      if (use_cond_selectivity > 1)
        pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
				                          remaining_tables &
                                                          ~real_table_bit);
      join->positions[idx].cond_selectivity= pushdown_cond_selectivity;

      if (unlikely(thd->trace_started()) && pushdown_cond_selectivity < 1.0)
        trace_one_table.add("selectivity", pushdown_cond_selectivity);

      double partial_join_cardinality= current_record_count *
                                        pushdown_cond_selectivity;
      if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
      { /* Recursively expand the current partial plan */
        swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
        Json_writer_array trace_rest(thd, "rest_of_plan");
        if (best_extension_by_limited_search(join,
                                             remaining_tables & ~real_table_bit,
                                             idx + 1,
                                             partial_join_cardinality,
                                             current_read_time,
                                             search_depth - 1,
                                             prune_level,
                                             use_cond_selectivity))
          DBUG_RETURN(TRUE);
        swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
      }
      else
      { /*
          'join' is either the best partial QEP with 'search_depth' relations,
          or the best complete QEP so far, whichever is smaller.
        */
        if (join->sort_by_table &&
            join->sort_by_table !=
            join->positions[join->const_tables].table->table)
        {
          /*
             We may have to make a temp table, note that this is only a
             heuristic since we cannot know for sure at this point.
             Hence it may be wrong.
          */
          trace_one_table.add("cost_for_sorting", current_record_count);
          current_read_time= COST_ADD(current_read_time, current_record_count);
        }
        trace_one_table.add("estimated_join_cardinality",
                            partial_join_cardinality);
        if (current_read_time < join->best_read)
        {
          memcpy((uchar*) join->best_positions, (uchar*) join->positions,
                 sizeof(POSITION) * (idx + 1));
          join->join_record_count= partial_join_cardinality;
          join->best_read= current_read_time - 0.001;
        }
        DBUG_EXECUTE("opt", print_plan(join, idx+1,
                                       current_record_count,
                                       read_time,
                                       current_read_time,
                                       "full_plan"););
      }
      restore_prev_nj_state(s);
      restore_prev_sj_state(remaining_tables, s, idx);
    }
  }
  DBUG_RETURN(FALSE);
}

Project: server

static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
				      TABLE *table,
				      const key_map *keys,ha_rows limit)
{
  int error;
  DBUG_ENTER("get_quick_record_count");
  uchar buff[STACK_BUFF_ALLOC];
  if (unlikely(check_stack_overrun(thd, STACK_MIN_SIZE, buff)))
    DBUG_RETURN(0);                           // Fatal error flag is set
  if (select)
  {
    select->head=table;
    table->reginfo.impossible_range=0;
    if (likely((error=
                select->test_quick_select(thd, *(key_map *)keys,
                                          (table_map) 0,
                                          limit, 0, FALSE,
                                          TRUE,     /* remove_where_parts*/
                                          FALSE)) ==
               1))
      DBUG_RETURN(select->quick->records);
    if (unlikely(error == -1))
    {
      table->reginfo.impossible_range=1;
      DBUG_RETURN(0);
    }
    DBUG_PRINT("warning",("Couldn't use record count on const keypart"));
  }
  DBUG_RETURN(HA_POS_ERROR);			/* This shouldn't happend */
}

Project: server

bool JOIN::rollup_init()
{
  uint i,j;
  Item **ref_array;

  tmp_table_param.quick_group= 0;	// Can't create groups in tmp table
  rollup.state= ROLLUP::STATE_INITED;

  /*
    Create pointers to the different sum function groups
    These are updated by rollup_make_fields()
  */
  tmp_table_param.group_parts= send_group_parts;

  Item_null_result **null_items=
    static_cast<Item_null_result**>(thd->alloc(sizeof(Item*)*send_group_parts));

  rollup.null_items= Item_null_array(null_items, send_group_parts);
  rollup.ref_pointer_arrays=
    static_cast<Ref_ptr_array*>
    (thd->alloc((sizeof(Ref_ptr_array) +
                 all_fields.elements * sizeof(Item*)) * send_group_parts));
  rollup.fields=
    static_cast<List<Item>*>(thd->alloc(sizeof(List<Item>) * send_group_parts));

  if (!null_items || !rollup.ref_pointer_arrays || !rollup.fields)
    return true;

  ref_array= (Item**) (rollup.ref_pointer_arrays+send_group_parts);


  /*
    Prepare space for field list for the different levels
    These will be filled up in rollup_make_fields()
  */
  for (i= 0 ; i < send_group_parts ; i++)
  {
    if (!(rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd)))
      return true;

    List<Item> *rollup_fields= &rollup.fields[i];
    rollup_fields->empty();
    rollup.ref_pointer_arrays[i]= Ref_ptr_array(ref_array, all_fields.elements);
    ref_array+= all_fields.elements;
  }
  for (i= 0 ; i < send_group_parts; i++)
  {
    for (j=0 ; j < fields_list.elements ; j++)
      rollup.fields[i].push_back(rollup.null_items[i], thd->mem_root);
  }
  List_iterator<Item> it(all_fields);
  Item *item;
  while ((item= it++))
  {
    ORDER *group_tmp;
    bool found_in_group= 0;

    for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next)
    {
      if (*group_tmp->item == item)
      {
        item->maybe_null= 1;
        item->in_rollup= 1;
        found_in_group= 1;
        break;
      }
    }
    if (item->type() == Item::FUNC_ITEM && !found_in_group)
    {
      bool changed= FALSE;
      if (change_group_ref(thd, (Item_func *) item, group_list, &changed))
        return 1;
      /*
        We have to prevent creation of a field in a temporary table for
        an expression that contains GROUP BY attributes.
        Marking the expression item as 'with_sum_func' will ensure this.
      */ 
      if (changed)
        item->get_with_sum_func_cache()->set_with_sum_func();
    }
  }
  return 0;
}

Project: server

test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
			bool no_changes, const key_map *map)
{
  int ref_key;
  uint UNINIT_VAR(ref_key_parts);
  int order_direction= 0;
  uint used_key_parts= 0;
  TABLE *table=tab->table;
  SQL_SELECT *select=tab->select;
  key_map usable_keys;
  QUICK_SELECT_I *save_quick= select ? select->quick : 0;
  Item *orig_cond= 0;
  bool orig_cond_saved= false;
  int best_key= -1;
  bool changed_key= false;
  DBUG_ENTER("test_if_skip_sort_order");

  /* Check that we are always called with first non-const table */
  DBUG_ASSERT(tab == tab->join->join_tab + tab->join->const_tables);

  /*
    Keys disabled by ALTER TABLE ... DISABLE KEYS should have already
    been taken into account.
  */
  usable_keys= *map;
  
  /* Find indexes that cover all ORDER/GROUP BY fields */
  for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next)
  {
    Item *item= (*tmp_order->item)->real_item();
    if (item->type() != Item::FIELD_ITEM)
    {
      usable_keys.clear_all();
      DBUG_RETURN(0);
    }

    /*
      Take multiple-equalities into account. Suppose we have
        ORDER BY col1, col10
      and there are
         multiple-equal(col1, col2, col3),
         multiple-equal(col10, col11).

      Then, 
      - when item=col1, we find the set of indexes that cover one of {col1,
        col2, col3}
      - when item=col10, we find the set of indexes that cover one of {col10,
        col11}

      And we compute an intersection of these sets to find set of indexes that
      cover all ORDER BY components.
    */
    key_map col_keys;
    compute_part_of_sort_key_for_equals(tab->join, table, (Item_field*)item,
                                        &col_keys);
    usable_keys.intersect(col_keys);
    if (usable_keys.is_clear_all())
      goto use_filesort;                        // No usable keys
  }

  ref_key= -1;
  /* Test if constant range in WHERE */
  if (tab->ref.key >= 0 && tab->ref.key_parts)
  {
    ref_key=	   tab->ref.key;
    ref_key_parts= tab->ref.key_parts;
    /* 
      todo: why does JT_REF_OR_NULL mean filesort? We could find another index
      that satisfies the ordering. I would just set ref_key=MAX_KEY here...
    */
    if (tab->type == JT_REF_OR_NULL || tab->type == JT_FT ||
        tab->ref.uses_splitting)
      goto use_filesort;
  }
  else if (select && select->quick)		// Range found by opt_range
  {
    int quick_type= select->quick->get_type();
    /* 
      assume results are not ordered when index merge is used 
      TODO: sergeyp: Results of all index merge selects actually are ordered 
      by clustered PK values.
    */
  
    if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
        quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
        quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || 
        quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT)
    {
      /*
        we set ref_key=MAX_KEY instead of -1, because test_if_cheaper ordering
        assumes that "ref_key==-1" means doing full index scan. 
        (This is not very straightforward and we got into this situation for 
         historical reasons. Should be fixed at some point).
      */
      ref_key= MAX_KEY;
    }
    else
    {
      ref_key= select->quick->index;
      ref_key_parts= select->quick->used_key_parts;
    }
  }

  if (ref_key >= 0 && ref_key != MAX_KEY)
  {
    /* Current access method uses index ref_key with ref_key_parts parts */
    if (!usable_keys.is_set(ref_key))
    {
      /* However, ref_key doesn't match the needed ordering */
      uint new_ref_key;

      /*
	If using index only read, only consider other possible index only
	keys
      */
      if (table->covering_keys.is_set(ref_key))
	usable_keys.intersect(table->covering_keys);
      if (tab->pre_idx_push_select_cond)
      {
        orig_cond= tab->set_cond(tab->pre_idx_push_select_cond);
        orig_cond_saved= true;
      }

      if ((new_ref_key= test_if_subkey(order, table, ref_key, ref_key_parts,
				       &usable_keys)) < MAX_KEY)
      {
        /*
          Index new_ref_key 
          - produces the required ordering, 
          - also has the same columns as ref_key for #ref_key_parts (this
            means we will read the same number of rows as with ref_key).
        */

        /*
          If new_ref_key allows to construct a quick select which uses more key
          parts than ref(new_ref_key) would, do that.

          Otherwise, construct a ref access (todo: it's not clear what is the
          win in using ref access when we could use quick select also?)
        */
        if ((table->quick_keys.is_set(new_ref_key) && 
             table->quick_key_parts[new_ref_key] > ref_key_parts) ||
             !(tab->ref.key >= 0))
	{
          /*
            The range optimizer constructed QUICK_RANGE for ref_key, and
            we want to use instead new_ref_key as the index. We can't
            just change the index of the quick select, because this may
            result in an inconsistent QUICK_SELECT object. Below we
            create a new QUICK_SELECT from scratch so that all its
            parameters are set correctly by the range optimizer.
           */
          key_map new_ref_key_map;
          COND *save_cond;
          bool res;
          new_ref_key_map.clear_all();  // Force the creation of quick select
          new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key.

          /* Reset quick;  This will be restored in 'use_filesort' if needed */
          select->quick= 0;
          save_cond= select->cond;
          if (select->pre_idx_push_select_cond)
            select->cond= select->pre_idx_push_select_cond;
          res= select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
                                         (tab->join->select_options &
                                          OPTION_FOUND_ROWS) ?
                                          HA_POS_ERROR :
                                          tab->join->unit->select_limit_cnt,TRUE,
                                         TRUE, FALSE, FALSE) <= 0;
          if (res)
          {
            select->cond= save_cond;
            goto use_filesort;
          }
          DBUG_ASSERT(tab->select->quick);
          tab->type= JT_ALL;
          tab->ref.key= -1;
          tab->ref.key_parts= 0;
          tab->use_quick= 1;
          best_key= new_ref_key;
          /*
            We don't restore select->cond as we want to use the
            original condition as index condition pushdown is not
            active for the new index.
            todo: why not perform index condition pushdown for the new index?
          */
	}
        else
	{
          /*
            We'll use ref access method on key new_ref_key. In general case 
            the index search tuple for new_ref_key will be different (e.g.
            when one index is defined as (part1, part2, ...) and another as
            (part1, part2(N), ...) and the WHERE clause contains 
            "part1 = const1 AND part2=const2". 
            So we build tab->ref from scratch here.
          */
          KEYUSE *keyuse= tab->keyuse;
          while (keyuse->key != new_ref_key && keyuse->table == tab->table)
            keyuse++;
          if (create_ref_for_key(tab->join, tab, keyuse, FALSE,
                                 (tab->join->const_table_map |
                                  OUTER_REF_TABLE_BIT)))
            goto use_filesort;

          pick_table_access_method(tab);
	}

        ref_key= new_ref_key;
        changed_key= true;
     }
    }
    /* Check if we get the rows in requested sorted order by using the key */
    if (usable_keys.is_set(ref_key) &&
        (order_direction= test_if_order_by_key(tab->join, order,table,ref_key,
					       &used_key_parts)))
      goto check_reverse_order;
  }
  {
    uint UNINIT_VAR(best_key_parts);
    uint saved_best_key_parts= 0;
    int best_key_direction= 0;
    JOIN *join= tab->join;
    ha_rows table_records= table->stat_records();

    test_if_cheaper_ordering(tab, order, table, usable_keys,
                             ref_key, select_limit,
                             &best_key, &best_key_direction,
                             &select_limit, &best_key_parts,
                             &saved_best_key_parts);

    /*
      filesort() and join cache are usually faster than reading in 
      index order and not using join cache, except in case that chosen
      index is clustered key.
    */
    if (best_key < 0 ||
        ((select_limit >= table_records) &&
         (tab->type == JT_ALL &&
         tab->join->table_count > tab->join->const_tables + 1) &&
         !(table->file->index_flags(best_key, 0, 1) & HA_CLUSTERED_INDEX)))
      goto use_filesort;

    if (select && // psergey:  why doesn't this use a quick?
        table->quick_keys.is_set(best_key) && best_key != ref_key)
    {
      key_map tmp_map;
      tmp_map.clear_all();       // Force the creation of quick select
      tmp_map.set_bit(best_key); // only best_key.
      select->quick= 0;

      bool cond_saved= false;
      Item *saved_cond;

      /*
        Index Condition Pushdown may have removed parts of the condition for
        this table. Temporarily put them back because we want the whole
        condition for the range analysis.
      */
      if (select->pre_idx_push_select_cond)
      {
        saved_cond= select->cond;
        select->cond= select->pre_idx_push_select_cond;
        cond_saved= true;
      }

      select->test_quick_select(join->thd, tmp_map, 0,
                                join->select_options & OPTION_FOUND_ROWS ?
                                HA_POS_ERROR :
                                join->unit->select_limit_cnt,
                                TRUE, FALSE, FALSE, FALSE);

      if (cond_saved)
        select->cond= saved_cond;
    }
    order_direction= best_key_direction;
    /*
      saved_best_key_parts is actual number of used keyparts found by the
      test_if_order_by_key function. It could differ from keyinfo->user_defined_key_parts,
      thus we have to restore it in case of desc order as it affects
      QUICK_SELECT_DESC behaviour.
    */
    used_key_parts= (order_direction == -1) ?
      saved_best_key_parts :  best_key_parts;
    changed_key= true;
  }

check_reverse_order:                  
  DBUG_ASSERT(order_direction != 0);

  if (order_direction == -1)		// If ORDER BY ... DESC
  {
    int quick_type;
    if (select && select->quick)
    {
      /*
	Don't reverse the sort order, if it's already done.
        (In some cases test_if_order_by_key() can be called multiple times
      */
      if (select->quick->reverse_sorted())
        goto skipped_filesort;

      quick_type= select->quick->get_type();
      if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
          quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
          quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
          quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
          quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
      {
        tab->limit= 0;
        goto use_filesort;               // Use filesort
      }
    }
  }

  /*
    Update query plan with access pattern for doing ordered access
    according to what we have decided above.
  */
  if (!no_changes) // We are allowed to update QEP
  {
    if (best_key >= 0)
    {
      bool quick_created= 
        (select && select->quick && select->quick!=save_quick);

      /* 
         If ref_key used index tree reading only ('Using index' in EXPLAIN),
         and best_key doesn't, then revert the decision.
      */
      if (table->covering_keys.is_set(best_key))
        table->file->ha_start_keyread(best_key);
      else
        table->file->ha_end_keyread();

      if (!quick_created)
      {
        if (select)                  // Throw any existing quick select
          select->quick= 0;          // Cleanup either reset to save_quick,
                                     // or 'delete save_quick'
        tab->index= best_key;
        tab->read_first_record= order_direction > 0 ?
                                join_read_first:join_read_last;
        tab->type=JT_NEXT;           // Read with index_first(), index_next()

        /*
          Currently usage of rowid filters is not supported in InnoDB
          if the table is accessed by the primary key
        */
        if (tab->rowid_filter &&
            tab->index == table->s->primary_key &&
            table->file->primary_key_is_clustered())
	{
          tab->range_rowid_filter_info= 0;
          delete tab->rowid_filter;
          tab->rowid_filter= 0;
        }

        if (tab->pre_idx_push_select_cond)
        {
          tab->set_cond(tab->pre_idx_push_select_cond);
          /*
            orig_cond is a part of pre_idx_push_cond,
            no need to restore it.
          */
          orig_cond= 0;
          orig_cond_saved= false;
        }

        table->file->ha_index_or_rnd_end();
        if (tab->join->select_options & SELECT_DESCRIBE)
        {
          tab->ref.key= -1;
          tab->ref.key_parts= 0;
          if (select_limit < table->stat_records())
            tab->limit= select_limit;
          table->file->ha_end_keyread();
        }
      }
      else if (tab->type != JT_ALL || tab->select->quick)
      {
        /*
          We're about to use a quick access to the table.
          We need to change the access method so as the quick access
          method is actually used.
        */
        DBUG_ASSERT(tab->select->quick);
        tab->type=JT_ALL;
        tab->use_quick=1;
        tab->ref.key= -1;
        tab->ref.key_parts=0;		// Don't use ref key.
        tab->range_rowid_filter_info= 0;
        if (tab->rowid_filter)
	{
          delete tab->rowid_filter;
          tab->rowid_filter= 0;
        }
        tab->read_first_record= join_init_read_record;
        if (tab->is_using_loose_index_scan())
          tab->join->tmp_table_param.precomputed_group_by= TRUE;

        /*
          Restore the original condition as changes done by pushdown
          condition are not relevant anymore
        */
        if (tab->select && tab->select->pre_idx_push_select_cond)
	{
          tab->set_cond(tab->select->pre_idx_push_select_cond);
           tab->table->file->cancel_pushed_idx_cond();
        }
        /*
          TODO: update the number of records in join->best_positions[tablenr]
        */
      }
    } // best_key >= 0

    if (order_direction == -1)		// If ORDER BY ... DESC
    {
      if (select && select->quick)
      {
        /* ORDER BY range_key DESC */
        QUICK_SELECT_I *tmp= select->quick->make_reverse(used_key_parts);
        if (!tmp)
        {
          tab->limit= 0;
          goto use_filesort;           // Reverse sort failed -> filesort
        }
        /*
          Cancel Pushed Index Condition, as it doesn't work for reverse scans.
        */
        if (tab->select && tab->select->pre_idx_push_select_cond)
	{
          tab->set_cond(tab->select->pre_idx_push_select_cond);
           tab->table->file->cancel_pushed_idx_cond();
        }
        if (select->quick == save_quick)
          save_quick= 0;                // make_reverse() consumed it
        select->set_quick(tmp);
        /* Cancel "Range checked for each record" */
        if (tab->use_quick == 2)
        {
          tab->use_quick= 1;
          tab->read_first_record= join_init_read_record;
        }
      }
      else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
               tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
      {
        /*
          SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC

          Use a traversal function that starts by reading the last row
          with key part (A) and then traverse the index backwards.
        */
        tab->read_first_record= join_read_last_key;
        tab->read_record.read_record_func= join_read_prev_same;
        /* Cancel "Range checked for each record" */
        if (tab->use_quick == 2)
        {
          tab->use_quick= 1;
          tab->read_first_record= join_init_read_record;
        }
        /*
          Cancel Pushed Index Condition, as it doesn't work for reverse scans.
        */
        if (tab->select && tab->select->pre_idx_push_select_cond)
	{
          tab->set_cond(tab->select->pre_idx_push_select_cond);
           tab->table->file->cancel_pushed_idx_cond();
        }
      }
    }
    else if (select && select->quick)
    {
      /* Cancel "Range checked for each record" */
      if (tab->use_quick == 2)
      {
        tab->use_quick= 1;
        tab->read_first_record= join_init_read_record;
      }
      select->quick->need_sorted_output();
    }

    tab->read_record.unlock_row= (tab->type == JT_EQ_REF) ?
                                 join_read_key_unlock_row : rr_unlock_row;

  } // QEP has been modified

  /*
    Cleanup:
    We may have both a 'select->quick' and 'save_quick' (original)
    at this point. Delete the one that we wan't use.
  */

skipped_filesort:
  // Keep current (ordered) select->quick 
  if (select && save_quick != select->quick)
  {
    delete save_quick;
    save_quick= NULL;
  }
  if (orig_cond_saved && !changed_key)
    tab->set_cond(orig_cond);
  if (!no_changes && changed_key && table->file->pushed_idx_cond)
    table->file->cancel_pushed_idx_cond();

  DBUG_RETURN(1);

use_filesort:
  // Restore original save_quick
  if (select && select->quick != save_quick)
  {
    delete select->quick;
    select->quick= save_quick;
  }
  if (orig_cond_saved)
    tab->set_cond(orig_cond);

  DBUG_RETURN(0);
}

Project: server

void JOIN::cleanup(bool full)
{
  DBUG_ENTER("JOIN::cleanup");
  DBUG_PRINT("enter", ("select: %d (%p)  join: %p  full: %u",
                       select_lex->select_number, select_lex, this,
                       (uint) full));

  if (full)
    have_query_plan= QEP_DELETED;

  if (original_join_tab)
  {
    /* Free the original optimized join created for the group_by_handler */
    join_tab= original_join_tab;
    original_join_tab= 0;
    table_count= original_table_count;
  }

  if (join_tab)
  {
    JOIN_TAB *tab;

    if (full)
    {
      /*
        Call cleanup() on join tabs used by the join optimization
        (join->join_tab may now be pointing to result of make_simple_join
         reading from the temporary table)

        We also need to check table_count to handle various degenerate joins
        w/o tables: they don't have some members initialized and
        WALK_OPTIMIZATION_TABS may not work correctly for them.
      */
      if (top_join_tab_count && tables_list)
      {
        for (tab= first_breadth_first_tab(); tab;
             tab= next_breadth_first_tab(first_breadth_first_tab(),
                                         top_join_tab_count, tab))
        {
          tab->cleanup();
          delete tab->filesort_result;
          tab->filesort_result= NULL;
        }
      }
      cleaned= true;
      //psergey2: added (Q: why not in the above loop?)
      {
        JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
        for (uint i= 0; i < aggr_tables; i++, curr_tab++)
        {
          if (curr_tab->aggr)
          {
            free_tmp_table(thd, curr_tab->table);
            curr_tab->table= NULL;
            delete curr_tab->tmp_table_param;
            curr_tab->tmp_table_param= NULL;
            curr_tab->aggr= NULL;

            delete curr_tab->filesort_result;
            curr_tab->filesort_result= NULL;
          }
        }
        aggr_tables= 0; // psergey3
      }
    }
    else
    {
      for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab;
           tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
      {
        tab->partial_cleanup();
      }
    }
  }
  if (full)
  {
    cleanup_empty_jtbm_semi_joins(this, join_list);

    // Run Cached_item DTORs!
    group_fields.delete_elements();

    /*
      We can't call delete_elements() on copy_funcs as this will cause
      problems in free_elements() as some of the elements are then deleted.
    */
    tmp_table_param.copy_funcs.empty();
    /*
      If we have tmp_join and 'this' JOIN is not tmp_join and
      tmp_table_param.copy_field's  of them are equal then we have to remove
      pointer to  tmp_table_param.copy_field from tmp_join, because it will
      be removed in tmp_table_param.cleanup().
    */
    tmp_table_param.cleanup();

    delete pushdown_query;
    pushdown_query= 0;

    if (!join_tab)
    {
      List_iterator<TABLE_LIST> li(*join_list);
      TABLE_LIST *table_ref;
      while ((table_ref= li++))
      {
        if (table_ref->table &&
            table_ref->jtbm_subselect &&
            table_ref->jtbm_subselect->is_jtbm_const_tab)
        {
          free_tmp_table(thd, table_ref->table);
          table_ref->table= NULL;
        }
      }
    }
  }
  /* Restore ref array to original state */
  if (current_ref_ptrs != items0)
  {
    set_items_ref_array(items0);
    set_group_rpa= false;
  }
  DBUG_VOID_RETURN;
}

Project: server

optimize_straight_join(JOIN *join, table_map join_tables)
{
  JOIN_TAB *s;
  uint idx= join->const_tables;
  bool disable_jbuf= join->thd->variables.join_cache_level == 0;
  double    record_count= 1.0;
  double    read_time=    0.0;
  uint use_cond_selectivity= 
         join->thd->variables.optimizer_use_condition_selectivity;
  POSITION  loose_scan_pos;
  THD *thd= join->thd;

  for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
  {
    POSITION *position= join->positions + idx;
    Json_writer_object trace_one_table(thd);
    if (unlikely(thd->trace_started()))
    {
      trace_plan_prefix(join, idx, join_tables);
      trace_one_table.add_table_name(s);
    }
    /* Find the best access method from 's' to the current partial plan */
    best_access_path(join, s, join_tables, join->positions, idx,
                     disable_jbuf, record_count,
                     position, &loose_scan_pos);

    /* compute the cost of the new plan extended with 's' */
    record_count= COST_MULT(record_count, position->records_read);
    const double filter_cmp_gain= position->range_rowid_filter_info
      ? position->range_rowid_filter_info->get_cmp_gain(record_count)
      : 0;
    read_time+= COST_ADD(read_time - filter_cmp_gain,
                         COST_ADD(position->read_time,
                                  record_count / (double) TIME_FOR_COMPARE));
    advance_sj_state(join, join_tables, idx, &record_count, &read_time,
                     &loose_scan_pos);

    join_tables&= ~(s->table->map);
    double pushdown_cond_selectivity= 1.0;
    if (use_cond_selectivity > 1)
      pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
                                                        join_tables);
    position->cond_selectivity= pushdown_cond_selectivity;
    ++idx;
  }

  if (join->sort_by_table &&
      join->sort_by_table != join->positions[join->const_tables].table->table)
    read_time+= record_count;  // We have to make a temp table
  memcpy((uchar*) join->best_positions, (uchar*) join->positions,
         sizeof(POSITION)*idx);
  join->join_record_count= record_count;
  join->best_read= read_time - 0.001;
}

Project: server

Field *Item_ref::create_tmp_field_ex(TABLE *table,
                                     Tmp_field_src *src,
                                     const Tmp_field_param *param)
{
  Item *item= real_item();
  DBUG_ASSERT(is_result_field());
  if (item->type() == Item::FIELD_ITEM)
  {
    Field *result;
    Item_field *field= (Item_field*) item;
    Tmp_field_param prm2(*param);
    prm2.set_modify_item(false);
    src->set_field(field->field);
    if (!(result= field->create_tmp_field_from_item_field(table, this, &prm2)))
      return NULL;
    if (param->modify_item())
      result_field= result;
    return result;
  }
  return Item_result_field::create_tmp_field_ex(table, src, param);
}

Project: server

sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
  DBUG_ENTER("sub_select");

  if (join_tab->last_inner)
  {
    JOIN_TAB *last_inner_tab= join_tab->last_inner;
    for (JOIN_TAB  *jt= join_tab; jt <= last_inner_tab; jt++)
      jt->table->null_row= 0;
  }
  else
    join_tab->table->null_row=0;

  if (end_of_records)
  {
    enum_nested_loop_state nls=
      (*join_tab->next_select)(join,join_tab+1,end_of_records);
    DBUG_RETURN(nls);
  }
  join_tab->tracker->r_scans++;

  int error;
  enum_nested_loop_state rc= NESTED_LOOP_OK;
  READ_RECORD *info= &join_tab->read_record;


  for (SJ_TMP_TABLE *flush_dups_table= join_tab->flush_weedout_table;
       flush_dups_table;
       flush_dups_table= flush_dups_table->next_flush_table)
  {
    flush_dups_table->sj_weedout_delete_rows();
  }

  if (!join_tab->preread_init_done && join_tab->preread_init())
    DBUG_RETURN(NESTED_LOOP_ERROR);

  join_tab->build_range_rowid_filter_if_needed();

  join->return_tab= join_tab;

  if (join_tab->last_inner)
  {
    /* join_tab is the first inner table for an outer join operation. */

    /* Set initial state of guard variables for this table.*/
    join_tab->found=0;
    join_tab->not_null_compl= 1;

    /* Set first_unmatched for the last inner table of this group */
    join_tab->last_inner->first_unmatched= join_tab;
    if (join_tab->on_precond && !join_tab->on_precond->val_int())
      rc= NESTED_LOOP_NO_MORE_ROWS;
  }
  join->thd->get_stmt_da()->reset_current_row_for_warning();

  if (rc != NESTED_LOOP_NO_MORE_ROWS && 
      (rc= join_tab_execution_startup(join_tab)) < 0)
    DBUG_RETURN(rc);
  
  if (join_tab->loosescan_match_tab)
    join_tab->loosescan_match_tab->found_match= FALSE;

  if (rc != NESTED_LOOP_NO_MORE_ROWS)
  {
    error= (*join_tab->read_first_record)(join_tab);
    if (!error && join_tab->keep_current_rowid)
      join_tab->table->file->position(join_tab->table->record[0]);    
    rc= evaluate_join_record(join, join_tab, error);
  }

  /* 
    Note: psergey has added the 2nd part of the following condition; the 
    change should probably be made in 5.1, too.
  */
  bool skip_over= FALSE;
  while (rc == NESTED_LOOP_OK && join->return_tab >= join_tab)
  {
    if (join_tab->loosescan_match_tab && 
        join_tab->loosescan_match_tab->found_match)
    {
      KEY *key= join_tab->table->key_info + join_tab->loosescan_key;
      key_copy(join_tab->loosescan_buf, join_tab->table->record[0], key, 
               join_tab->loosescan_key_len);
      skip_over= TRUE;
    }

    error= info->read_record();

    if (skip_over && likely(!error))
    {
      if (!key_cmp(join_tab->table->key_info[join_tab->loosescan_key].key_part,
                   join_tab->loosescan_buf, join_tab->loosescan_key_len))
      {
        /* 
          This is the LooseScan action: skip over records with the same key
          value if we already had a match for them.
        */
        continue;
      }
      join_tab->loosescan_match_tab->found_match= FALSE;
      skip_over= FALSE;
    }

    if (join_tab->keep_current_rowid && likely(!error))
      join_tab->table->file->position(join_tab->table->record[0]);
    
    rc= evaluate_join_record(join, join_tab, error);
  }

  if (rc == NESTED_LOOP_NO_MORE_ROWS &&
      join_tab->last_inner && !join_tab->found)
    rc= evaluate_null_complemented_join_record(join, join_tab);

  if (rc == NESTED_LOOP_NO_MORE_ROWS)
    rc= NESTED_LOOP_OK;
  DBUG_RETURN(rc);
}

Project: server

bool check_simple_equality(THD *thd, const Item::Context &ctx,
                           Item *left_item, Item *right_item,
                           COND_EQUAL *cond_equal)
{
  Item *orig_left_item= left_item;
  Item *orig_right_item= right_item;
  if (left_item->type() == Item::REF_ITEM)
  {
    Item_ref::Ref_Type left_ref= ((Item_ref*)left_item)->ref_type();

    if (left_ref == Item_ref::VIEW_REF ||
        left_ref == Item_ref::REF)
    {
      if (((Item_ref*)left_item)->get_depended_from())
        return FALSE;
      if (left_ref == Item_ref::VIEW_REF &&
          ((Item_direct_view_ref*)left_item)->get_null_ref_table() !=
           NO_NULL_TABLE &&
          !left_item->real_item()->used_tables())
        return FALSE;
      left_item= left_item->real_item();
    }
  }
  if (right_item->type() == Item::REF_ITEM)
  {
    Item_ref::Ref_Type right_ref= ((Item_ref*)right_item)->ref_type();
    if (right_ref == Item_ref::VIEW_REF ||
       (right_ref == Item_ref::REF))
    {
      if (((Item_ref*)right_item)->get_depended_from())
        return FALSE;
      if (right_ref == Item_ref::VIEW_REF &&
          ((Item_direct_view_ref*)right_item)->get_null_ref_table() !=
           NO_NULL_TABLE &&
          !right_item->real_item()->used_tables())
        return FALSE;
      right_item= right_item->real_item();
    }
  }
  if (left_item->type() == Item::FIELD_ITEM &&
      right_item->type() == Item::FIELD_ITEM &&
      !((Item_field*)left_item)->get_depended_from() &&
      !((Item_field*)right_item)->get_depended_from())
  {
    /* The predicate the form field1=field2 is processed */

    Field *left_field= ((Item_field*) left_item)->field;
    Field *right_field= ((Item_field*) right_item)->field;

    if (!left_field->eq_def(right_field))
      return FALSE;

    /* Search for multiple equalities containing field1 and/or field2 */
    bool left_copyfl, right_copyfl;
    Item_equal *left_item_equal=
               find_item_equal(cond_equal, left_field, &left_copyfl);
    Item_equal *right_item_equal= 
               find_item_equal(cond_equal, right_field, &right_copyfl);

    /* As (NULL=NULL) != TRUE we can't just remove the predicate f=f */
    if (left_field->eq(right_field)) /* f = f */
      return (!(left_field->maybe_null() && !left_item_equal)); 

    if (left_item_equal && left_item_equal == right_item_equal)
    {
      /* 
        The equality predicate is inference of one of the existing
        multiple equalities, i.e the condition is already covered
        by upper level equalities
      */
       return TRUE;
    }
      
    /* Copy the found multiple equalities at the current level if needed */
    if (left_copyfl)
    {
      /* left_item_equal of an upper level contains left_item */
      left_item_equal= new (thd->mem_root) Item_equal(thd, left_item_equal);
      left_item_equal->set_context_field(((Item_field*) left_item));
      cond_equal->current_level.push_back(left_item_equal, thd->mem_root);
    }
    if (right_copyfl)
    {
      /* right_item_equal of an upper level contains right_item */
      right_item_equal= new (thd->mem_root) Item_equal(thd, right_item_equal);
      right_item_equal->set_context_field(((Item_field*) right_item));
      cond_equal->current_level.push_back(right_item_equal, thd->mem_root);
    }

    if (left_item_equal)
    { 
      /* left item was found in the current or one of the upper levels */
      if (! right_item_equal)
        left_item_equal->add(orig_right_item, thd->mem_root);
      else
      {
        /* Merge two multiple equalities forming a new one */
        left_item_equal->merge(thd, right_item_equal);
        /* Remove the merged multiple equality from the list */
        List_iterator<Item_equal> li(cond_equal->current_level);
        while ((li++) != right_item_equal) ;
        li.remove();
      }
    }
    else
    { 
      /* left item was not found neither the current nor in upper levels  */
      if (right_item_equal)
        right_item_equal->add(orig_left_item, thd->mem_root);
      else 
      {
        /* None of the fields was found in multiple equalities */
        Type_handler_hybrid_field_type
          tmp(orig_left_item->type_handler_for_comparison());
        if (tmp.aggregate_for_comparison(orig_right_item->
                                         type_handler_for_comparison()))
          return false;
        Item_equal *item_equal=
          new (thd->mem_root) Item_equal(thd, tmp.type_handler(),
                                         orig_left_item, orig_right_item,
                                         false);
        item_equal->set_context_field((Item_field*)left_item);
        cond_equal->current_level.push_back(item_equal, thd->mem_root);
      }
    }
    return TRUE;
  }

  {
    /* The predicate of the form field=const/const=field is processed */
    Item *const_item= 0;
    Item_field *field_item= 0;
    Item *orig_field_item= 0;
    if (left_item->type() == Item::FIELD_ITEM &&
        !((Item_field*)left_item)->get_depended_from() &&
        right_item->const_item() && !right_item->is_expensive())
    {
      orig_field_item= orig_left_item;
      field_item= (Item_field *) left_item;
      const_item= right_item;
    }
    else if (right_item->type() == Item::FIELD_ITEM &&
             !((Item_field*)right_item)->get_depended_from() &&
             left_item->const_item() && !left_item->is_expensive())
    {
      orig_field_item= orig_right_item;
      field_item= (Item_field *) right_item;
      const_item= left_item;
    }

    if (const_item &&
        field_item->field->test_if_equality_guarantees_uniqueness(const_item))
    {
      /*
        field_item and const_item are arguments of a scalar or a row
        comparison function:
          WHERE column=constant
          WHERE (column, ...) = (constant, ...)

        The owner comparison function has previously called fix_fields(),
        so field_item and const_item should be directly comparable items,
        field_item->cmp_context and const_item->cmp_context should be set.
        In case of string comparison, charsets and collations of
        field_item and const_item should have already be aggregated
        for comparison, all necessary character set converters installed
        and fixed.

        In case of string comparison, const_item can be either:
        - a weaker constant that does not need to be converted to field_item:
            WHERE latin1_field = 'latin1_const'
            WHERE varbinary_field = 'latin1_const'
            WHERE latin1_bin_field = 'latin1_general_ci_const'
        - a stronger constant that does not need to be converted to field_item:
            WHERE latin1_field = binary 0xDF
            WHERE latin1_field = 'a' COLLATE latin1_bin
        - a result of conversion (e.g. from the session character set)
          to the character set of field_item:
            WHERE latin1_field = 'utf8_string_with_latin1_repertoire'
      */
      bool copyfl;

      Item_equal *item_equal = find_item_equal(cond_equal,
                                               field_item->field, &copyfl);
      if (copyfl)
      {
        item_equal= new (thd->mem_root) Item_equal(thd, item_equal);
        cond_equal->current_level.push_back(item_equal, thd->mem_root);
        item_equal->set_context_field(field_item);
      }
      Item *const_item2= field_item->field->get_equal_const_item(thd, ctx,
                                                                 const_item);
      if (!const_item2)
        return false;

      if (item_equal)
      {
        /* 
          The flag cond_false will be set to 1 after this, if item_equal
          already contains a constant and its value is  not equal to
          the value of const_item.
        */
        item_equal->add_const(thd, const_item2);
      }
      else
      {
        Type_handler_hybrid_field_type
          tmp(orig_left_item->type_handler_for_comparison());
        if (tmp.aggregate_for_comparison(orig_right_item->
                                         type_handler_for_comparison()))
          return false;
        item_equal= new (thd->mem_root) Item_equal(thd, tmp.type_handler(),
                                                   const_item2,
                                                   orig_field_item, true);
        item_equal->set_context_field(field_item);
        cond_equal->current_level.push_back(item_equal, thd->mem_root);
      }
      return TRUE;
    }
  }
  return FALSE;
}

Project: server

JOIN::init_range_rowid_filters()
{
  DBUG_ENTER("init_range_rowid_filters");

  JOIN_TAB *tab;

  for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
  {
    if (!tab->rowid_filter)
      continue;
    if (tab->rowid_filter->get_container()->alloc())
    {
      delete tab->rowid_filter;
      tab->rowid_filter= 0;
      continue;
    }
    tab->table->file->rowid_filter_push(tab->rowid_filter);
    tab->is_rowid_filter_built= false;
  }
  DBUG_RETURN(0);
}

Project: server

int JOIN::optimize()
{
  int res= 0;
  create_explain_query_if_not_exists(thd->lex, thd->mem_root);
  join_optimization_state init_state= optimization_state;
  if (select_lex->pushdown_select)
  {
    if (!(select_options & SELECT_DESCRIBE))
    {
      /* Prepare to execute the query pushed into a foreign engine */
      res= select_lex->pushdown_select->init();
    }
    with_two_phase_optimization= false;
  }
  else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
    res= optimize_stage2();
  else
  {
    // to prevent double initialization on EXPLAIN
    if (optimization_state != JOIN::NOT_OPTIMIZED)
      return FALSE;
    optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS;
    res= optimize_inner();
  }
  if (!with_two_phase_optimization ||
      init_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
  {
    if (!res && have_query_plan != QEP_DELETED)
      res= build_explain();
    optimization_state= JOIN::OPTIMIZATION_DONE;
  }
  return res;
}

Project: server

static bool get_range_limit_read_cost(const JOIN_TAB *tab, 
                                      const TABLE *table, 
                                      ha_rows table_records,
                                      uint keynr,
                                      ha_rows rows_limit,
                                      double *read_time)
{
  bool res= false;
  /* 
    We need to adjust the estimates if we had a quick select (or ref(const)) on
    index keynr.
  */
  if (table->quick_keys.is_set(keynr))
  {
    /*
      Start from quick select's rows and cost. These are always cheaper than
      full index scan/cost.
    */
    double best_rows= (double)table->quick_rows[keynr];
    double best_cost= (double)table->quick_costs[keynr];
    
    /*
      Check if ref(const) access was possible on this index. 
    */
    if (tab)
    {
      key_part_map map= 1;
      uint kp;
      /* Find how many key parts would be used by ref(const) */
      for (kp=0; kp < MAX_REF_PARTS; map=map << 1, kp++)
      {
        if (!(table->const_key_parts[keynr] & map))
          break;
      }
      
      if (kp > 0)
      {
        ha_rows ref_rows;
        /*
          Two possible cases:
          1. ref(const) uses the same #key parts as range access. 
          2. ref(const) uses fewer key parts, becasue there is a
            range_cond(key_part+1).
        */
        if (kp == table->quick_key_parts[keynr])
          ref_rows= table->quick_rows[keynr];
        else
          ref_rows= (ha_rows) table->key_info[keynr].actual_rec_per_key(kp-1);

        if (ref_rows > 0)
        {
          double tmp= (double)ref_rows;
          /* Reuse the cost formula from best_access_path: */
          set_if_smaller(tmp, (double) tab->join->thd->variables.max_seeks_for_key);
          if (table->covering_keys.is_set(keynr))
            tmp= table->file->keyread_time(keynr, 1, (ha_rows) tmp);
          else
            tmp= table->file->read_time(keynr, 1,
                                        (ha_rows) MY_MIN(tmp,tab->worst_seeks));
          if (tmp < best_cost)
          {
            best_cost= tmp;
            best_rows= (double)ref_rows;
          }
        }
      }
    }

    /*
      Consider an example:

        SELECT *
        FROM t1
        WHERE key1 BETWEEN 10 AND 20 AND col2='foo'
        ORDER BY key1 LIMIT 10

      If we were using a full index scan on key1, we would need to read this
      many rows to get 10 matches:

        10 / selectivity(key1 BETWEEN 10 AND 20 AND col2='foo')

      This is the number we get in rows_limit.
      But we intend to use range access on key1. The rows returned by quick
      select will satisfy the range part of the condition,
      "key1 BETWEEN 10 and 20". We will still need to filter them with
      the remainder condition, (col2='foo').

      The selectivity of the range access is (best_rows/table_records). We need
      to discount it from the rows_limit:
    */
    double rows_limit_for_quick= rows_limit * (best_rows / table_records);

    if (best_rows > rows_limit_for_quick)
    {
      /*
        LIMIT clause specifies that we will need to read fewer records than
        quick select will return. Assume that quick select's cost is
        proportional to the number of records we need to return (e.g. if we 
        only need 1/3rd of records, it will cost us 1/3rd of quick select's
        read time)
      */
      best_cost *= rows_limit_for_quick / best_rows;
    }
    *read_time= best_cost;
    res= true;
  }
  return res;
}

Project: server

int setup_order(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
                List<Item> &fields, List<Item> &all_fields, ORDER *order,
                bool from_window_spec)
{ 
  SELECT_LEX *select = thd->lex->current_select;
  enum_parsing_place context_analysis_place=
                     thd->lex->current_select->context_analysis_place;
  thd->where="order clause";
  const bool for_union= select->master_unit()->is_unit_op() &&
    select == select->master_unit()->fake_select_lex;
  for (uint number = 1; order; order=order->next, number++)
  {
    if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
                           all_fields, false, true, from_window_spec))
      return 1;
    Item * const item= *order->item;
    if (item->with_window_func && context_analysis_place != IN_ORDER_BY)
    {
      my_error(ER_WINDOW_FUNCTION_IN_WINDOW_SPEC, MYF(0));
      return 1;
    }

    /*
      UNION queries cannot be used with an aggregate function in
      an ORDER BY clause
    */

    if (for_union && (item->with_sum_func() || item->with_window_func))
    {
      my_error(ER_AGGREGATE_ORDER_FOR_UNION, MYF(0), number);
      return 1;
    }

    if ((from_window_spec && item->with_sum_func() &&
         item->type() != Item::SUM_FUNC_ITEM) || item->with_window_func)
    {
      item->split_sum_func(thd, ref_pointer_array,
                           all_fields, SPLIT_SUM_SELECT);
    }
  }
  return 0;
}

Project: server

POSITION::POSITION()
{
  table= 0;
  records_read= cond_selectivity= read_time= 0.0;
  prefix_record_count= 0.0;
  key= 0;
  use_join_buffer= 0;
  sj_strategy= SJ_OPT_NONE;
  n_sj_tables= 0;
  spl_plan= 0;
  range_rowid_filter_info= 0;
  ref_depend_map= dups_producing_tables= 0;
  inner_tables_handled_with_other_sjs= 0;
  dups_weedout_picker.set_empty();
  firstmatch_picker.set_empty();
  loosescan_picker.set_empty();
  sjmat_picker.set_empty();
}

Project: server

Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select,
                              vers_select_conds_t *conds, bool timestamp)
{
  DBUG_ASSERT(table);
  DBUG_ASSERT(table->table);
#define newx new (thd->mem_root)
  TABLE_SHARE *share= table->table->s;
  const TABLE_SHARE::period_info_t *period= conds->period;

  const LEX_CSTRING &fstart= period->start_field(share)->field_name;
  const LEX_CSTRING &fend= period->end_field(share)->field_name;

  conds->field_start= newx Item_field(thd, &select->context,
                                      table->db.str, table->alias.str,
                                      thd->make_clex_string(fstart));
  conds->field_end=   newx Item_field(thd, &select->context,
                                      table->db.str, table->alias.str,
                                      thd->make_clex_string(fend));

  Item *cond1= NULL, *cond2= NULL, *cond3= NULL, *curr= NULL;
  if (timestamp)
  {
    MYSQL_TIME max_time;
    switch (conds->type)
    {
    case SYSTEM_TIME_UNSPECIFIED:
    case SYSTEM_TIME_HISTORY:
    {
      thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE);
      max_time.second_part= TIME_MAX_SECOND_PART;
      Datetime dt(&max_time);
      curr= newx Item_datetime_literal(thd, &dt, TIME_SECOND_PART_DIGITS);
      if (conds->type == SYSTEM_TIME_UNSPECIFIED)
        cond1= newx Item_func_eq(thd, conds->field_end, curr);
      else
        cond1= newx Item_func_lt(thd, conds->field_end, curr);
      break;
    }
    case SYSTEM_TIME_AS_OF:
      cond1= newx Item_func_le(thd, conds->field_start, conds->start.item);
      cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
      break;
    case SYSTEM_TIME_FROM_TO:
      cond1= newx Item_func_lt(thd, conds->field_start, conds->end.item);
      cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
      cond3= newx Item_func_lt(thd, conds->start.item, conds->end.item);
      break;
    case SYSTEM_TIME_BETWEEN:
      cond1= newx Item_func_le(thd, conds->field_start, conds->end.item);
      cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
      cond3= newx Item_func_le(thd, conds->start.item, conds->end.item);
      break;
    case SYSTEM_TIME_BEFORE:
      cond1= newx Item_func_history(thd, conds->field_end);
      cond2= newx Item_func_lt(thd, conds->field_end, conds->start.item);
      break;
    default:
      DBUG_ASSERT(0);
    }
  }
  else
  {
    DBUG_ASSERT(table->table->s && table->table->s->db_plugin);

    Item *trx_id0= conds->start.item;
    Item *trx_id1= conds->end.item;
    if (conds->start.item && conds->start.unit == VERS_TIMESTAMP)
    {
      bool backwards= conds->type != SYSTEM_TIME_AS_OF;
      trx_id0= newx Item_func_trt_id(thd, conds->start.item,
                                     TR_table::FLD_TRX_ID, backwards);
    }
    if (conds->end.item && conds->end.unit == VERS_TIMESTAMP)
    {
      trx_id1= newx Item_func_trt_id(thd, conds->end.item,
                                     TR_table::FLD_TRX_ID, false);
    }

    switch (conds->type)
    {
    case SYSTEM_TIME_UNSPECIFIED:
    case SYSTEM_TIME_HISTORY:
      curr= newx Item_int(thd, ULONGLONG_MAX);
      if (conds->type == SYSTEM_TIME_UNSPECIFIED)
        cond1= newx Item_func_eq(thd, conds->field_end, curr);
      else
        cond1= newx Item_func_lt(thd, conds->field_end, curr);
      break;
      DBUG_ASSERT(!conds->start.item);
      DBUG_ASSERT(!conds->end.item);
      break;
    case SYSTEM_TIME_AS_OF:
      cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id0, conds->field_start);
      cond2= newx Item_func_trt_trx_sees(thd, conds->field_end, trx_id0);
      DBUG_ASSERT(!conds->end.item);
      break;
    case SYSTEM_TIME_FROM_TO:
      cond1= newx Item_func_trt_trx_sees(thd, trx_id1, conds->field_start);
      cond2= newx Item_func_trt_trx_sees_eq(thd, conds->field_end, trx_id0);
      cond3= newx Item_func_lt(thd, conds->start.item, conds->end.item);
      break;
    case SYSTEM_TIME_BETWEEN:
      cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id1, conds->field_start);
      cond2= newx Item_func_trt_trx_sees_eq(thd, conds->field_end, trx_id0);
      cond3= newx Item_func_le(thd, conds->start.item, conds->end.item);
      break;
    case SYSTEM_TIME_BEFORE:
      cond1= newx Item_func_history(thd, conds->field_end);
      cond2= newx Item_func_trt_trx_sees(thd, trx_id0, conds->field_end);
      break;
    default:
      DBUG_ASSERT(0);
    }
  }

  if (cond1)
  {
    cond1= and_items(thd, cond2, cond1);
    cond1= and_items(thd, cond3, cond1);
  }
  return cond1;
}

Project: server

copy_fields(TMP_TABLE_PARAM *param)
{
  Copy_field *ptr=param->copy_field;
  Copy_field *end=param->copy_field_end;

  DBUG_ASSERT((ptr != NULL && end >= ptr) || (ptr == NULL && end == NULL));

  for (; ptr != end; ptr++)
    (*ptr->do_copy)(ptr);

  List_iterator_fast<Item> it(param->copy_funcs);
  Item_copy *item;
  while ((item= (Item_copy*) it++))
    item->copy();
}