bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
                            bool skip_unprefixed_keyparts)
{
  KEYUSE key_end, *prev, *save_pos, *use;
  uint found_eq_constant, i;

  DBUG_ASSERT(keyuse->elements);

  my_qsort(keyuse->buffer, keyuse->elements, sizeof(KEYUSE),
           (qsort_cmp) sort_keyuse);

  bzero((char*) &key_end, sizeof(key_end));    /* Add for easy testing */
  if (insert_dynamic(keyuse, (uchar*) &key_end))
    return TRUE;

  if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS))
    generate_derived_keys(keyuse);

  use= save_pos= dynamic_element(keyuse,0,KEYUSE*);
  prev= &key_end;
  found_eq_constant= 0;
  for (i=0 ; i < keyuse->elements-1 ; i++,use++)
  {
    if (!use->is_for_hash_join())
    {
      if (!(use->used_tables & ~OUTER_REF_TABLE_BIT) &&
          use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
        use->table->const_key_parts[use->key]|= use->keypart_map;
      if (use->keypart != FT_KEYPART)
      {
        if (use->key == prev->key && use->table == prev->table)
        {
          if ((prev->keypart+1 < use->keypart && skip_unprefixed_keyparts) ||
              (prev->keypart == use->keypart && found_eq_constant))
            continue;				/* remove */
        }
        else if (use->keypart != 0 && skip_unprefixed_keyparts)
          continue; /* remove - first found must be 0 */
      }

      prev= use;
      found_eq_constant= !use->used_tables;
      use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
    }
    /*
      Old gcc used a memcpy(), which is undefined if save_pos==use:
      http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19410
      http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39480
      This also disables a valgrind warning, so better to have the test.
    */
    if (save_pos != use)
      *save_pos= *use;
    /* Save ptr to first use */
    if (!use->table->reginfo.join_tab->keyuse)
      use->table->reginfo.join_tab->keyuse= save_pos;
    save_pos++;
  }
  i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
  (void) set_dynamic(keyuse,(uchar*) &key_end,i);
  keyuse->elements= i;

  return FALSE;
}


make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
{
  JOIN_TAB *tab;
  uint i;
  DBUG_ENTER("make_join_readinfo");

  bool statistics= MY_TEST(!(join->select_options & SELECT_DESCRIBE));
  bool sorted= 1;

  join->complex_firstmatch_tables= table_map(0);

  if (!join->select_lex->sj_nests.is_empty() &&
      setup_semijoin_dups_elimination(join, options, no_jbuf_after))
    DBUG_RETURN(TRUE); /* purecov: inspected */

  /* For const tables, set partial_join_cardinality to 1. */
  for (tab= join->join_tab; tab != join->join_tab + join->const_tables; tab++)
    tab->partial_join_cardinality= 1;

  JOIN_TAB *prev_tab= NULL;
  i= join->const_tables;
  for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       prev_tab=tab, tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
    /*
      The approximation below for partial join cardinality is not good because
        - it does not take into account some pushdown predicates
        - it does not differentiate between inner joins, outer joins and
        semi-joins.
      Later it should be improved.
    */

    if (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab)
      prev_tab= NULL;
    DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table);

    tab->partial_join_cardinality= join->best_positions[i].records_read *
                                   (prev_tab? prev_tab->partial_join_cardinality : 1);
    if (!tab->bush_children)
      i++;
  }

  check_join_cache_usage_for_tables(join, options, no_jbuf_after);

  JOIN_TAB *first_tab;
  for (tab= first_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
    if (tab->bush_children)
    {
      if (setup_sj_materialization_part2(tab))
        return TRUE;
    }

    TABLE *table=tab->table;
    uint jcl= tab->used_join_cache_level;
    tab->read_record.table= table;
    tab->read_record.unlock_row= rr_unlock_row;
    tab->sorted= sorted;
    sorted= 0;                                  // only first must be sorted

    /*
      We should not set tab->next_select for the last table in the
      SMJ-nest, as setup_sj_materialization() has already set it to
      end_sj_materialize.
    */
    if (!(tab->bush_root_tab &&
          tab->bush_root_tab->bush_children->end == tab + 1))
    {
      tab->next_select=sub_select;		/* normal select */
    }

    if (tab->loosescan_match_tab)
    {
      if (!(tab->loosescan_buf= (uchar*)join->thd->alloc(tab->
                                                         loosescan_key_len)))
        return TRUE; /* purecov: inspected */
      tab->sorted= TRUE;
    }
    table->status=STATUS_NO_RECORD;
    pick_table_access_method (tab);

    if (jcl)
       tab[-1].next_select=sub_select_cache;

    if (tab->cache && tab->cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG)
      tab->type= JT_HASH;

    switch (tab->type) {
    case JT_SYSTEM:				// Only happens with left join
    case JT_CONST:				// Only happens with left join
      /* Only happens with outer joins */
      tab->read_first_record= tab->type == JT_SYSTEM ? join_read_system
                                                     : join_read_const;
      if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
        table->file->ha_start_keyread(tab->ref.key);
      else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
        push_index_cond(tab, tab->ref.key);
      break;
    case JT_EQ_REF:
      tab->read_record.unlock_row= join_read_key_unlock_row;
      /* fall through */
      if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
        table->file->ha_start_keyread(tab->ref.key);
      else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
        push_index_cond(tab, tab->ref.key);
      break;
    case JT_REF_OR_NULL:
    case JT_REF:
      if (tab->select)
      {
	delete tab->select->quick;
	tab->select->quick=0;
      }
      delete tab->quick;
      tab->quick=0;
      if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
        table->file->ha_start_keyread(tab->ref.key);
      else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
        push_index_cond(tab, tab->ref.key);
      break;
    case JT_ALL:
    case JT_HASH:
      /*
	If previous table use cache
        If the incoming data set is already sorted don't use cache.
        Also don't use cache if this is the first table in semi-join
          materialization nest.
      */
      /* These init changes read_record */
      if (tab->use_quick == 2)
      {
        join->thd->set_status_no_good_index_used();
	tab->read_first_record= join_init_quick_read_record;
	if (statistics)
	  join->thd->inc_status_select_range_check();
      }
      else
      {
        if (!tab->bush_children)
          tab->read_first_record= join_init_read_record;
	if (tab == first_tab)
	{
	  if (tab->select && tab->select->quick)
	  {
	    if (statistics)
	      join->thd->inc_status_select_range();
	  }
	  else
	  {
            join->thd->set_status_no_index_used();
	    if (statistics)
	    {
              join->thd->inc_status_select_scan();
	      join->thd->query_plan_flags|= QPLAN_FULL_SCAN;
	    }
	  }
	}
	else
	{
	  if (tab->select && tab->select->quick)
	  {
	    if (statistics)
              join->thd->inc_status_select_full_range_join();
	  }
	  else
	  {
            join->thd->set_status_no_index_used();
	    if (statistics)
	    {
              join->thd->inc_status_select_full_join();
	      join->thd->query_plan_flags|= QPLAN_FULL_JOIN;
	    }
	  }
	}
	if (!table->no_keyread)
	{
	  if (tab->select && tab->select->quick &&
              tab->select->quick->index != MAX_KEY && //not index_merge
	      table->covering_keys.is_set(tab->select->quick->index))
            table->file->ha_start_keyread(tab->select->quick->index);
	  else if (!table->covering_keys.is_clear_all() &&
		   !(tab->select && tab->select->quick))
	  {					// Only read index tree
            if (tab->loosescan_match_tab)
              tab->index= tab->loosescan_key;
            else
            {
#ifdef BAD_OPTIMIZATION
              /*
                It has turned out that the below change, while speeding things
                up for disk-bound loads, slows them down for cases when the data
                is in disk cache (see BUG#35850):
                See bug #26447: "Using the clustered index for a table scan
                is always faster than using a secondary index".
              */
              if (table->s->primary_key != MAX_KEY &&
                  table->file->primary_key_is_clustered())
                tab->index= table->s->primary_key;
              else
#endif
                tab->index=find_shortest_key(table, & table->covering_keys);
            }
	    tab->read_first_record= join_read_first;
            /* Read with index_first / index_next */
	    tab->type= tab->type == JT_ALL ? JT_NEXT : JT_HASH_NEXT;
	  }
	}
        if (tab->select && tab->select->quick &&
            tab->select->quick->index != MAX_KEY &&
            !tab->table->file->keyread_enabled())
          push_index_cond(tab, tab->select->quick->index);
      }
      break;
    case JT_FT:
      break;
      /* purecov: begin deadcode */
    default:
      DBUG_PRINT("error",("Table type %d found",tab->type));
      break;
    case JT_UNKNOWN:
    case JT_MAYBE_REF:
      abort();
      /* purecov: end */
    }

    DBUG_EXECUTE("where",
                 char buff[256];
                 String str(buff,sizeof(buff),system_charset_info);
                 str.length(0);
                 str.append(tab->table? tab->table->alias.c_ptr() :"<no_table_name>");
                 str.append(" final_pushdown_cond");
                 print_where(tab->select_cond, str.c_ptr_safe(), QT_ORDINARY););
  }
  uint n_top_tables= (uint)(join->join_tab_ranges.head()->end -
                     join->join_tab_ranges.head()->start);

  join->join_tab[n_top_tables - 1].next_select=0;  /* Set by do_select */

  /*
    If a join buffer is used to join a table the ordering by an index
    for the first non-constant table cannot be employed anymore.
  */
  for (tab= join->join_tab + join->const_tables ;
       tab != join->join_tab + n_top_tables ; tab++)
  {
    if (tab->use_join_cache)
    {
       JOIN_TAB *sort_by_tab= join->group && join->simple_group &&
                              join->group_list ?
			       join->join_tab+join->const_tables :
                               join->get_sort_by_join_tab();
      /*
        It could be that sort_by_tab==NULL, and the plan is to use filesort()
        on the first table.
      */
      if (join->order)
      {
        join->simple_order= 0;
        join->need_tmp= 1;
      }

      if (join->group && !join->group_optimized_away)
      {
        join->need_tmp= 1;
        join->simple_group= 0;
      }

      if (sort_by_tab)
      {
        join->need_tmp= 1;
        join->simple_order= join->simple_group= 0;
        if (sort_by_tab->type == JT_NEXT &&
            !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
        {
          sort_by_tab->type= JT_ALL;
          sort_by_tab->read_first_record= join_init_read_record;
        }
        else if (sort_by_tab->type == JT_HASH_NEXT &&
                 !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
        {
          sort_by_tab->type= JT_HASH;
          sort_by_tab->read_first_record= join_init_read_record;
        }
      }
      break;
    }
  }

  DBUG_RETURN(FALSE);
}

MDEV-22464 Server crash on UPDATE with nested subquery

Uninitialized ref_pointer_array[] because setup_fields() got empty
fields list.  mysql_multi_update() for some reason does that by
substituting the fields list with empty total_list for the
mysql_select() call (looks like wrong merge since total_list is not
used anywhere else and is always empty). The fix would be to return
back the original fields list. But this fails update_use_source.test
case:

  --error ER_BAD_FIELD_ERROR
  update v1 set t1c1=2 order by 1;

Actually not failing the above seems to be ok.

The other fix would be to keep resolve_in_select_list false (and that
keeps outer context from being resolved in
Item_ref::fix_fields()). This fix is more consistent with how SELECT
behaves:

  --error ER_SUBQUERY_NO_1_ROW
  select a from t1 where a= (select 2 from t1 having (a = 3));

So this patch implements this fix.


bool JOIN::add_fields_for_current_rowid(JOIN_TAB *cur, List<Item> *table_fields)
{
  /*
    this will not walk into semi-join materialization nests but this is ok
    because we will never need to save current rowids for those.
  */
  for (JOIN_TAB *tab=join_tab; tab < cur; tab++)
  {
    if (!tab->keep_current_rowid)
      continue;
    Item *item= new (thd->mem_root) Item_temptable_rowid(tab->table);
    item->fix_fields(thd, 0);
    table_fields->push_back(item, thd->mem_root);
    cur->tmp_table_param->func_count++;
  }
  return 0;
}


void dbug_serve_apcs(THD *thd, int n_calls)
{
  const char *save_proc_info= thd->proc_info;

  /* Busy-wait for n_calls APC requests to arrive and be processed */
  int n_apcs= thd->apc_target.n_calls_processed + n_calls;
  while (thd->apc_target.n_calls_processed < n_apcs)
  {
    /* This is so that mysqltest knows we're ready to serve requests: */
    thd_proc_info(thd, "show_explain_trap");
    my_sleep(30000);
    thd_proc_info(thd, save_proc_info);
    if (unlikely(thd->check_killed(1)))
      break;
  }
}


bool JOIN::prepare_result(List<Item> **columns_list)
{
  DBUG_ENTER("JOIN::prepare_result");

  error= 0;
  /* Create result tables for materialized views. */
  if (!zero_result_cause &&
      select_lex->handle_derived(thd->lex, DT_CREATE))
    goto err;

  if (result->prepare2(this))
    goto err;

  if ((select_lex->options & OPTION_SCHEMA_TABLE) &&
      get_schema_tables_result(this, PROCESSED_BY_JOIN_EXEC))
    goto err;

  DBUG_RETURN(FALSE);

err:
  error= 1;
  DBUG_RETURN(TRUE);
}


copy_sum_funcs(Item_sum **func_ptr, Item_sum **end_ptr)
{
  for (; func_ptr != end_ptr ; func_ptr++)
    (void) (*func_ptr)->save_in_result_field(1);
  return;
}


Index_hint::print(THD *thd, String *str)
{
  switch (type)
  {
    case INDEX_HINT_IGNORE: str->append(STRING_WITH_LEN("IGNORE INDEX")); break;
    case INDEX_HINT_USE:    str->append(STRING_WITH_LEN("USE INDEX")); break;
    case INDEX_HINT_FORCE:  str->append(STRING_WITH_LEN("FORCE INDEX")); break;
  }
  str->append (STRING_WITH_LEN(" ("));
  if (key_name.length)
  {
    if (thd && !my_strnncoll(system_charset_info,
                             (const uchar *)key_name.str, key_name.length,
                             (const uchar *)primary_key_name,
                             strlen(primary_key_name)))
      str->append(primary_key_name);
    else
      append_identifier(thd, str, &key_name);
}
  str->append(')');
}


static uint make_join_orderinfo(JOIN *join)
{
  /*
    This function needs to be fixed to take into account that we now have SJM
    nests.
  */
  DBUG_ASSERT(0);

  JOIN_TAB *tab;
  if (join->need_tmp)
    return join->table_count;
  tab= join->get_sort_by_join_tab();
  return tab ? (uint)(tab-join->join_tab) : join->table_count;
}


make_group_fields(JOIN *main_join, JOIN *curr_join)
{
  if (main_join->group_fields_cache.elements)
  {
    curr_join->group_fields= main_join->group_fields_cache;
    curr_join->sort_and_group= 1;
  }
  else
  {
    if (alloc_group_fields(curr_join, curr_join->group_list))
      return (1);
    main_join->group_fields_cache= curr_join->group_fields;
  }
  return (0);
}


void check_join_cache_usage_for_tables(JOIN *join, ulonglong options,
                                       uint no_jbuf_after)
{
  JOIN_TAB *tab;
  JOIN_TAB *prev_tab;

  for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
    tab->used_join_cache_level= join->max_allowed_join_cache_level;
  }

  uint idx= join->const_tables;
  for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
restart:
    tab->icp_other_tables_ok= TRUE;
    tab->idx_cond_fact_out= TRUE;

    /*
      Check if we have a preceding join_tab, as something that will feed us
      records that we could buffer. We don't have it, if
       - this is the first non-const table in the join order,
       - this is the first table inside an SJM nest.
    */
    prev_tab= tab - 1;
    if (tab == join->join_tab + join->const_tables ||
        (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab))
      prev_tab= NULL;

    switch (tab->type) {
    case JT_SYSTEM:
    case JT_CONST:
    case JT_EQ_REF:
    case JT_REF:
    case JT_REF_OR_NULL:
    case JT_ALL:
      tab->used_join_cache_level= check_join_cache_usage(tab, options,
                                                         no_jbuf_after,
                                                         idx,
                                                         prev_tab);
      tab->use_join_cache= MY_TEST(tab->used_join_cache_level);
      /*
        psergey-merge: todo: raise the question that this is really stupid that
        we can first allocate a join buffer, then decide not to use it and free
        it.
      */
      if (join->return_tab)
      {
        tab= join->return_tab;
        goto restart;
      }
      break;
    default:
      tab->used_join_cache_level= 0;
    }
    if (!tab->bush_children)
      idx++;
  }
}


join_ft_read_next(READ_RECORD *info)
{
  int error;
  if (unlikely((error= info->table->file->ha_ft_read(info->record()))))
    return report_error(info->table, error);
  return 0;
}


init_tmptable_sum_functions(Item_sum **func_ptr)
{
  Item_sum *func;
  while ((func= *(func_ptr++)))
    func->reset_field();
}


double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
                                     uint use_cond_selectivity)
{
  ha_rows records;
  double dbl_records;

  if (use_cond_selectivity > 1)
  {
    TABLE *table= s->table;
    double sel= table->cond_selectivity;
    double table_records= (double)table->stat_records();
    dbl_records= table_records * sel;
    return dbl_records;
  }

  records = s->found_records;

  /*
    If there is a filtering condition on the table (i.e. ref analyzer found
    at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
    preceding this table in the join order we're now considering), then
    assume that 25% of the rows will be filtered out by this condition.

    This heuristic is supposed to force tables used in exprZ to be before
    this table in join order.
  */
  if (with_found_constraint)
    records-= records/4;

    /*
      If applicable, get a more accurate estimate. Don't use the two
      heuristics at once.
    */
  if (s->table->quick_condition_rows != s->found_records)
    records= s->table->quick_condition_rows;

  dbl_records= (double)records;
  return dbl_records;
}


Item_func_in::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
                             uint *and_level, table_map usable_tables,
                             SARGABLE_PARAM **sargables)
{
  if (is_local_field(args[0]) && !(used_tables() & OUTER_REF_TABLE_BIT))
  {
    DBUG_ASSERT(arg_count != 2);
    add_key_equal_fields(join, key_fields, *and_level, this,
                         (Item_field*) (args[0]->real_item()), false,
                         args + 1, arg_count - 1, usable_tables, sargables);
  }
  else if (key_item()->type() == Item::ROW_ITEM &&
           !(used_tables() & OUTER_REF_TABLE_BIT))
  {
    Item_row *key_row= (Item_row *) key_item();
    Item **key_col= key_row->addr(0);
    uint row_cols= key_row->cols();
    for (uint i= 0; i < row_cols; i++, key_col++)
    {
      if (is_local_field(*key_col))
      {
        Item_field *field_item= (Item_field *)((*key_col)->real_item());
        add_key_equal_fields(join, key_fields, *and_level, this,
                             field_item, false, args + 1, arg_count - 1,
                             usable_tables, sargables, i + 1);
      }
    }
  }

}


create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
{
  TABLE *table;
  SQL_SELECT *select;
  bool quick_created= FALSE;
  SORT_INFO *file_sort= 0;
  DBUG_ENTER("create_sort_index");

  if (fsort == NULL)
    fsort= tab->filesort;

  table=  tab->table;
  select= fsort->select;

  table->status=0;				// May be wrong if quick_select

  if (!tab->preread_init_done && tab->preread_init())
    goto err;

  // If table has a range, move it to select
  if (select && tab->ref.key >= 0)
  {
    if (!select->quick)
    {
      if (tab->quick)
      {
        select->quick= tab->quick;
        tab->quick= NULL;
      /*
        We can only use 'Only index' if quick key is same as ref_key
        and in index_merge 'Only index' cannot be used
      */
      if (((uint) tab->ref.key != select->quick->index))
        table->file->ha_end_keyread();
      }
      else
      {
        /*
	  We have a ref on a const;  Change this to a range that filesort
	  can use.
	  For impossible ranges (like when doing a lookup on NULL on a NOT NULL
	  field, quick will contain an empty record set.
        */
        if (!(select->quick= (tab->type == JT_FT ?
			      get_ft_select(thd, table, tab->ref.key) :
			      get_quick_select_for_ref(thd, table, &tab->ref,
                                                       tab->found_records))))
	  goto err;
        quick_created= TRUE;
      }
      fsort->own_select= true;
    }
    else
    {
      DBUG_ASSERT(tab->type == JT_REF || tab->type == JT_EQ_REF);
      // Update ref value
      if (unlikely(cp_buffer_from_ref(thd, table, &tab->ref) &&
                   thd->is_fatal_error))
        goto err;                                   // out of memory
    }
  }

  /* Fill schema tables with data before filesort if it's necessary */
  if ((join->select_lex->options & OPTION_SCHEMA_TABLE) &&
      unlikely(get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX)))
    goto err;

  if (table->s->tmp_table)
    table->file->info(HA_STATUS_VARIABLE);	// Get record count
  file_sort= filesort(thd, table, fsort, fsort->tracker, join, tab->table->map);
  DBUG_ASSERT(tab->filesort_result == 0);
  tab->filesort_result= file_sort;
  tab->records= 0;
  if (file_sort)
  {
    tab->records= join->select_options & OPTION_FOUND_ROWS ?
      file_sort->found_rows : file_sort->return_rows;
    tab->join->join_examined_rows+= file_sort->examined_rows;
  }

  if (quick_created)
  {
    /* This will delete the quick select. */
    select->cleanup();
  }

  table->file->ha_end_keyread();
  if (tab->type == JT_FT)
    table->file->ha_ft_end();
  else
    table->file->ha_index_or_rnd_end();

  DBUG_RETURN(file_sort == 0);
err:
  DBUG_RETURN(-1);
}


void set_postjoin_aggr_write_func(JOIN_TAB *tab)
{
  JOIN *join= tab->join;
  TABLE *table= tab->table;
  AGGR_OP *aggr= tab->aggr;
  TMP_TABLE_PARAM *tmp_tbl= tab->tmp_table_param;

  DBUG_ASSERT(table && aggr);

  if (table->group && tmp_tbl->sum_func_count &&
      !tmp_tbl->precomputed_group_by)
  {
    /*
      Note for MyISAM tmp tables: if uniques is true keys won't be
      created.
    */
    if (table->s->keys && !table->s->uniques)
    {
      DBUG_PRINT("info",("Using end_update"));
      aggr->set_write_func(end_update);
    }
    else
    {
      DBUG_PRINT("info",("Using end_unique_update"));
      aggr->set_write_func(end_unique_update);
    }
  }
  else if (join->sort_and_group && !tmp_tbl->precomputed_group_by &&
           !join->sort_and_group_aggr_tab && join->tables_list &&
           join->top_join_tab_count)
  {
    DBUG_PRINT("info",("Using end_write_group"));
    aggr->set_write_func(end_write_group);
    join->sort_and_group_aggr_tab= tab;
  }
  else
  {
    DBUG_PRINT("info",("Using end_write"));
    aggr->set_write_func(end_write);
    if (tmp_tbl->precomputed_group_by)
    {
      /*
        A preceding call to create_tmp_table in the case when loose
        index scan is used guarantees that
        TMP_TABLE_PARAM::items_to_copy has enough space for the group
        by functions. It is OK here to use memcpy since we copy
        Item_sum pointers into an array of Item pointers.
      */
      memcpy(tmp_tbl->items_to_copy + tmp_tbl->func_count,
             join->sum_funcs,
             sizeof(Item*)*tmp_tbl->sum_func_count);
      tmp_tbl->items_to_copy[tmp_tbl->func_count+tmp_tbl->sum_func_count]= 0;
    }
  }
}


simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
               bool in_sj)
{
  TABLE_LIST *table;
  NESTED_JOIN *nested_join;
  TABLE_LIST *prev_table= 0;
  List_iterator<TABLE_LIST> li(*join_list);
  bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
  DBUG_ENTER("simplify_joins");

  /*
    Try to simplify join operations from join_list.
    The most outer join operation is checked for conversion first.
  */
  while ((table= li++))
  {
    table_map used_tables;
    table_map not_null_tables= (table_map) 0;

    if ((nested_join= table->nested_join))
    {
      /*
         If the element of join_list is a nested join apply
         the procedure to its nested join list first.
      */
      if (table->on_expr)
      {
        Item *expr= table->on_expr;
        /*
           If an on expression E is attached to the table,
           check all null rejected predicates in this expression.
           If such a predicate over an attribute belonging to
           an inner table of an embedded outer join is found,
           the outer join is converted to an inner join and
           the corresponding on expression is added to E.
	*/
        expr= simplify_joins(join, &nested_join->join_list,
                             expr, FALSE, in_sj || table->sj_on_expr);

        if (!table->prep_on_expr || expr != table->on_expr)
        {
          DBUG_ASSERT(expr);

          table->on_expr= expr;
          table->prep_on_expr= expr->copy_andor_structure(join->thd);
        }
      }
      nested_join->used_tables= (table_map) 0;
      nested_join->not_null_tables=(table_map) 0;
      conds= simplify_joins(join, &nested_join->join_list, conds, top,
                            in_sj || table->sj_on_expr);
      used_tables= nested_join->used_tables;
      not_null_tables= nested_join->not_null_tables;
      /* The following two might become unequal after table elimination: */
      nested_join->n_tables= nested_join->join_list.elements;
    }
    else
    {
      if (!table->prep_on_expr)
        table->prep_on_expr= table->on_expr;
      used_tables= table->get_map();
      if (conds)
        not_null_tables= conds->not_null_tables();
    }

    if (table->embedding)
    {
      table->embedding->nested_join->used_tables|= used_tables;
      table->embedding->nested_join->not_null_tables|= not_null_tables;
    }

    if (!(table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) ||
        (used_tables & not_null_tables))
    {
      /*
        For some of the inner tables there are conjunctive predicates
        that reject nulls => the outer join can be replaced by an inner join.
      */
      if (table->outer_join && !table->embedding && table->table)
        table->table->maybe_null= FALSE;
      table->outer_join= 0;
      if (!(straight_join || table->straight))
      {
        table->dep_tables= 0;
        TABLE_LIST *embedding= table->embedding;
        while (embedding)
        {
          if (embedding->nested_join->join_list.head()->outer_join)
          {
            if (!embedding->sj_subq_pred)
              table->dep_tables= embedding->dep_tables;
            break;
          }
          embedding= embedding->embedding;
        }
      }
      if (table->on_expr)
      {
        /* Add ON expression to the WHERE or upper-level ON condition. */
        if (conds)
        {
          conds= and_conds(join->thd, conds, table->on_expr);
          conds->top_level_item();
          /* conds is always a new item as both cond and on_expr existed */
          DBUG_ASSERT(!conds->fixed);
          conds->fix_fields(join->thd, &conds);
        }
        else
          conds= table->on_expr;
        table->prep_on_expr= table->on_expr= 0;
      }
    }

    /*
      Only inner tables of non-convertible outer joins
      remain with on_expr.
    */
    if (table->on_expr)
    {
      table_map table_on_expr_used_tables= table->on_expr->used_tables();
      table->dep_tables|= table_on_expr_used_tables;
      if (table->embedding)
      {
        table->dep_tables&= ~table->embedding->nested_join->used_tables;
        /*
           Embedding table depends on tables used
           in embedded on expressions.
        */
        table->embedding->on_expr_dep_tables|= table_on_expr_used_tables;
      }
      else
        table->dep_tables&= ~table->get_map();
    }

    if (prev_table)
    {
      /* The order of tables is reverse: prev_table follows table */
      if (prev_table->straight || straight_join)
        prev_table->dep_tables|= used_tables;
      if (prev_table->on_expr)
      {
        prev_table->dep_tables|= table->on_expr_dep_tables;
        table_map prev_used_tables= prev_table->nested_join ?
	                            prev_table->nested_join->used_tables :
	                            prev_table->get_map();
        /*
          If on expression contains only references to inner tables
          we still make the inner tables dependent on the outer tables.
          It would be enough to set dependency only on one outer table
          for them. Yet this is really a rare case.
          Note:
          RAND_TABLE_BIT mask should not be counted as it
          prevents update of inner table dependences.
          For example it might happen if RAND() function
          is used in JOIN ON clause.
	*/
        if (!((prev_table->on_expr->used_tables() &
               ~(OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)) &
              ~prev_used_tables))
          prev_table->dep_tables|= used_tables;
      }
    }
    prev_table= table;
  }

  /*
    Flatten nested joins that can be flattened.
    no ON expression and not a semi-join => can be flattened.
  */
  li.rewind();
  while ((table= li++))
  {
    nested_join= table->nested_join;
    if (table->sj_on_expr && !in_sj)
    {
      /*
        If this is a semi-join that is not contained within another semi-join
        leave it intact (otherwise it is flattened)
      */
      /*
        Make sure that any semi-join appear in
        the join->select_lex->sj_nests list only once
      */
      List_iterator_fast<TABLE_LIST> sj_it(join->select_lex->sj_nests);
      TABLE_LIST *sj_nest;
      while ((sj_nest= sj_it++))
      {
        if (table == sj_nest)
          break;
      }
      if (sj_nest)
        continue;
      join->select_lex->sj_nests.push_back(table, join->thd->mem_root);

      /*
        Also, walk through semi-join children and mark those that are now
        top-level
      */
      TABLE_LIST *tbl;
      List_iterator<TABLE_LIST> it(nested_join->join_list);
      while ((tbl= it++))
      {
        if (!tbl->on_expr && tbl->table)
          tbl->table->maybe_null= FALSE;
      }
    }
    else if (nested_join && !table->on_expr)
    {
      TABLE_LIST *tbl;
      List_iterator<TABLE_LIST> it(nested_join->join_list);
      List<TABLE_LIST> repl_list;
      while ((tbl= it++))
      {
        tbl->embedding= table->embedding;
        if (!tbl->embedding && !tbl->on_expr && tbl->table)
          tbl->table->maybe_null= FALSE;
        tbl->join_list= table->join_list;
        repl_list.push_back(tbl, join->thd->mem_root);
        tbl->dep_tables|= table->dep_tables;
      }
      li.replace(repl_list);
    }
  }
  DBUG_RETURN(conds);
}


change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
			 List<Item> &res_selected_fields,
			 List<Item> &res_all_fields,
			 uint elements, List<Item> &all_fields)
{
  List_iterator_fast<Item> it(all_fields);
  Item *item_field,*item;
  DBUG_ENTER("change_to_use_tmp_fields");

  res_selected_fields.empty();
  res_all_fields.empty();

  uint border= all_fields.elements - elements;
  for (uint i= 0; (item= it++); i++)
  {
    Field *field;
    if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
       item->with_window_func)
      item_field= item;
    else if (item->type() == Item::FIELD_ITEM)
    {
      if (!(item_field= item->get_tmp_table_item(thd)))
        DBUG_RETURN(true);
    }
    else if (item->type() == Item::FUNC_ITEM &&
             ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC)
    {
      field= item->get_tmp_table_field();
      if (field != NULL)
      {
        /*
          Replace "@:=<expression>" with "@:=<tmp table
          column>". Otherwise, we would re-evaluate <expression>, and
          if expression were a subquery, this would access
          already-unlocked tables.
         */
        Item_func_set_user_var* suv=
          new (thd->mem_root) Item_func_set_user_var(thd, (Item_func_set_user_var*) item);
        Item_field *new_field= new (thd->mem_root) Item_temptable_field(thd, field);
        if (!suv || !new_field)
          DBUG_RETURN(true);                  // Fatal error
        List<Item> list;
        list.push_back(new_field, thd->mem_root);
        suv->set_arguments(thd, list);
        item_field= suv;
      }
      else
        item_field= item;
    }
    else if ((field= item->get_tmp_table_field()))
    {
      if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
        item_field= ((Item_sum*) item)->result_item(thd, field);
      else
        item_field= (Item *) new (thd->mem_root) Item_temptable_field(thd, field);
      if (!item_field)
        DBUG_RETURN(true);                    // Fatal error

      if (item->real_item()->type() != Item::FIELD_ITEM)
        field->orig_table= 0;
      item_field->name= item->name;
      if (item->type() == Item::REF_ITEM)
      {
        Item_field *ifield= (Item_field *) item_field;
        Item_ref *iref= (Item_ref *) item;
        ifield->table_name= iref->table_name;
        ifield->db_name= iref->db_name;
      }
#ifndef DBUG_OFF
      if (!item_field->name.str)
      {
        char buff[256];
        String str(buff,sizeof(buff),&my_charset_bin);
        str.length(0);
        str.extra_allocation(1024);
        item->print(&str, QT_ORDINARY);
        item_field->name.str= thd->strmake(str.ptr(), str.length());
        item_field->name.length= str.length();
      }
#endif
    }
    else
      item_field= item;

    res_all_fields.push_back(item_field, thd->mem_root);
    ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
      item_field;
  }

  List_iterator_fast<Item> itr(res_all_fields);
  for (uint i= 0; i < border; i++)
    itr++;
  itr.sublist(res_selected_fields, elements);
  DBUG_RETURN(false);
}


JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab)
{
  tab= next_breadth_first_tab(join->first_breadth_first_tab(),
                              join->top_join_tab_count, tab);
  if (tab && tab->bush_root_tab)
    tab= NULL;
  return tab;
}


Item_cond::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
                          uint *and_level, table_map usable_tables,
                          SARGABLE_PARAM **sargables)
{
  List_iterator_fast<Item> li(*argument_list());
  KEY_FIELD *org_key_fields= *key_fields;

  (*and_level)++;
  (li++)->add_key_fields(join, key_fields, and_level, usable_tables,
                         sargables);
  Item *item;
  while ((item=li++))
  {
    KEY_FIELD *start_key_fields= *key_fields;
    (*and_level)++;
    item->add_key_fields(join, key_fields, and_level, usable_tables,
                         sargables);
    *key_fields= merge_key_fields(org_key_fields,start_key_fields,
                                  *key_fields, ++(*and_level));
  }
}


AGGR_OP::end_send()
{
  enum_nested_loop_state rc= NESTED_LOOP_OK;
  TABLE *table= join_tab->table;
  JOIN *join= join_tab->join;

  // All records were stored, send them further
  int tmp, new_errno= 0;

  if ((rc= put_record(true)) < NESTED_LOOP_OK)
    return rc;

  if ((tmp= table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    DBUG_PRINT("error",("extra(HA_EXTRA_NO_CACHE) failed"));
    new_errno= tmp;
  }
  if ((tmp= table->file->ha_index_or_rnd_end()))
  {
    DBUG_PRINT("error",("ha_index_or_rnd_end() failed"));
    new_errno= tmp;
  }
  if (new_errno)
  {
    table->file->print_error(new_errno,MYF(0));
    return NESTED_LOOP_ERROR;
  }

  // Update ref array
  join_tab->join->set_items_ref_array(*join_tab->ref_array);
  bool keep_last_filesort_result = join_tab->filesort ? false : true;
  if (join_tab->window_funcs_step)
  {
    if (join_tab->window_funcs_step->exec(join, keep_last_filesort_result))
      return NESTED_LOOP_ERROR;
  }

  table->reginfo.lock_type= TL_UNLOCK;

  bool in_first_read= true;
  while (rc == NESTED_LOOP_OK)
  {
    int error;
    if (in_first_read)
    {
      in_first_read= false;
      error= join_init_read_record(join_tab);
    }
    else
      error= join_tab->read_record.read_record();

    if (unlikely(error > 0 || (join->thd->is_error())))   // Fatal error
      rc= NESTED_LOOP_ERROR;
    else if (error < 0)
      break;
    else if (unlikely(join->thd->killed))		  // Aborted by user
    {
      join->thd->send_kill_message();
      rc= NESTED_LOOP_KILLED;
    }
    else
    {
      rc= evaluate_join_record(join, join_tab, 0);
    }
  }

  if (keep_last_filesort_result)
  {
    delete join_tab->filesort_result;
    join_tab->filesort_result= NULL;
  }

  // Finish rnd scn after sending records
  if (join_tab->table->file->inited)
    join_tab->table->file->ha_rnd_end();

  return rc;
}


JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab,
                          enum enum_with_bush_roots include_bush_roots)
{
  if (include_bush_roots == WITH_BUSH_ROOTS && tab->bush_children)
  {
    /* This JOIN_TAB is a SJM nest; Start from first table in nest */
    return tab->bush_children->start;
  }

  DBUG_ASSERT(!tab->last_leaf_in_bush || tab->bush_root_tab);

  if (tab->bush_root_tab)       /* Are we inside an SJM nest */
  {
    /* Inside SJM nest */
    if (!tab->last_leaf_in_bush)
      return tab+1;              /* Return next in nest */
    /* Continue from the sjm on the top level */
    tab= tab->bush_root_tab;
  }

  /* If no more JOIN_TAB's on the top level */
  if (++tab >= join->join_tab + join->exec_join_tab_cnt() + join->aggr_tables)
    return NULL;

  if (include_bush_roots == WITHOUT_BUSH_ROOTS && tab->bush_children)
  {
    /* This JOIN_TAB is a SJM nest; Start from first table in nest */
    tab= tab->bush_children->start;
  }
  return tab;
}


bool JOIN::setup_subquery_caches()
{
  DBUG_ENTER("JOIN::setup_subquery_caches");

  /*
    We have to check all this condition together because items created in
    one of this clauses can be moved to another one by optimizer
  */
  if (select_lex->expr_cache_may_be_used[IN_WHERE] ||
      select_lex->expr_cache_may_be_used[IN_HAVING] ||
      select_lex->expr_cache_may_be_used[IN_ON] ||
      select_lex->expr_cache_may_be_used[NO_MATTER])
  {
    JOIN_TAB *tab;
    if (conds &&
        !(conds= conds->transform(thd, &Item::expr_cache_insert_transformer,
                                  NULL)))
      DBUG_RETURN(TRUE);
    for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
         tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
    {
      if (tab->select_cond &&
          !(tab->select_cond=
            tab->select_cond->transform(thd,
                                        &Item::expr_cache_insert_transformer,
                                        NULL)))
	DBUG_RETURN(TRUE);
      if (tab->cache_select && tab->cache_select->cond)
        if (!(tab->cache_select->cond=
              tab->cache_select->
              cond->transform(thd, &Item::expr_cache_insert_transformer,
                              NULL)))
          DBUG_RETURN(TRUE);
    }

    if (having &&
        !(having= having->transform(thd,
                                    &Item::expr_cache_insert_transformer,
                                    NULL)))
      DBUG_RETURN(TRUE);

    if (tmp_having)
    {
      DBUG_ASSERT(having == NULL);
      if (!(tmp_having=
            tmp_having->transform(thd,
                                  &Item::expr_cache_insert_transformer,
                                  NULL)))
	DBUG_RETURN(TRUE);
    }
  }
  if (select_lex->expr_cache_may_be_used[SELECT_LIST] ||
      select_lex->expr_cache_may_be_used[IN_GROUP_BY] ||
      select_lex->expr_cache_may_be_used[NO_MATTER])
  {
    List_iterator<Item> li(all_fields);
    Item *item;
    while ((item= li++))
    {
      Item *new_item;
      if (!(new_item=
            item->transform(thd, &Item::expr_cache_insert_transformer,
                            NULL)))
        DBUG_RETURN(TRUE);
      if (new_item != item)
      {
        thd->change_item_tree(li.ref(), new_item);
      }
    }
    for (ORDER *tmp_group= group_list; tmp_group ; tmp_group= tmp_group->next)
    {
      if (!(*tmp_group->item=
            (*tmp_group->item)->transform(thd,
                                          &Item::expr_cache_insert_transformer,
                                          NULL)))
        DBUG_RETURN(TRUE);
    }
  }
  if (select_lex->expr_cache_may_be_used[NO_MATTER])
  {
    for (ORDER *ord= order; ord; ord= ord->next)
    {
      if (!(*ord->item=
            (*ord->item)->transform(thd,
                                    &Item::expr_cache_insert_transformer,
                                    NULL)))
	DBUG_RETURN(TRUE);
    }
  }
  DBUG_RETURN(FALSE);
}


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;

  for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
  {
    /* 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,
                     join->positions + idx, &loose_scan_pos);

    /* compute the cost of the new plan extended with 's' */
    record_count= COST_MULT(record_count, join->positions[idx].records_read);
    read_time= COST_ADD(read_time,
                        COST_ADD(join->positions[idx].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);
    join->positions[idx].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;
}


Item_bool_func2::add_key_fields_optimize_op(JOIN *join, KEY_FIELD **key_fields,
                                            uint *and_level,
                                            table_map usable_tables,
                                            SARGABLE_PARAM **sargables,
                                            bool equal_func)
{
  /* If item is of type 'field op field/constant' add it to key_fields */
  if (is_local_field(args[0]))
  {
    add_key_equal_fields(join, key_fields, *and_level, this,
                         (Item_field*) args[0]->real_item(), equal_func,
                         args + 1, 1, usable_tables, sargables);
  }
  if (is_local_field(args[1]))
  {
    add_key_equal_fields(join, key_fields, *and_level, this,
                         (Item_field*) args[1]->real_item(), equal_func,
                         args, 1, usable_tables, sargables);
  }
}


void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
{
  char mrr_str_buf[128];
  mrr_str_buf[0]=0;
  int len;
  handler *h= quick->head->file;
  len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf,
                                        sizeof(mrr_str_buf));
  if (len > 0)
  {
    //res->append(STRING_WITH_LEN("; "));
    res->append(mrr_str_buf, len);
  }
}


bool cond_has_datetime_is_null(Item *cond)
{
  if (cond_is_datetime_is_null(cond))
    return true;

  if (cond->type() == Item::COND_ITEM)
  {
    List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
    List_iterator<Item> li(*cond_arg_list);
    Item *item;
    while ((item= li++))
    {
      if (cond_has_datetime_is_null(item))
        return true;
    }
  }
  return false;
}


bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array)
{
  KEYUSE *keyuse= dynamic_element(keyuse_array, 0, KEYUSE*);
  uint elements= keyuse_array->elements;
  TABLE *prev_table= 0;
  for (uint i= 0; i < elements; i++, keyuse++)
  {
    if (!keyuse->table)
      break;
    KEYUSE *first_table_keyuse= NULL;
    table_map last_used_tables= 0;
    uint count= 0;
    uint keys= 0;
    TABLE_LIST *derived= NULL;
    if (keyuse->table != prev_table)
      derived= keyuse->table->pos_in_table_list;
    while (derived && derived->is_materialized_derived())
    {
      if (keyuse->table != prev_table)
      {
        prev_table= keyuse->table;
        while (keyuse->table == prev_table && keyuse->key != MAX_KEY)
	{
          keyuse++;
          i++;
        }
        if (keyuse->table != prev_table)
	{
          keyuse--;
          i--;
          derived= NULL;
          continue;
        }
        first_table_keyuse= keyuse;
        last_used_tables= keyuse->used_tables;
        count= 0;
        keys= 0;
      }
      else if (keyuse->used_tables != last_used_tables)
      {
        keys++;
        last_used_tables= keyuse->used_tables;
      }
      count++;
      keyuse++;
      i++;
      if (keyuse->table != prev_table)
      {
        if (generate_derived_keys_for_table(first_table_keyuse, count, ++keys))
          return TRUE;
        keyuse--;
        i--;
	derived= NULL;
      }
    }
  }
  return FALSE;
}


bool JOIN::shrink_join_buffers(JOIN_TAB *jt,
                               ulonglong curr_space,
                               ulonglong needed_space)
{
  JOIN_TAB *tab;
  JOIN_CACHE *cache;
  for (tab= first_linear_tab(this, WITHOUT_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab != jt;
       tab= next_linear_tab(this, tab, WITHOUT_BUSH_ROOTS))
  {
    cache= tab->cache;
    if (cache)
    {
      size_t buff_size;
      if (needed_space < cache->get_min_join_buffer_size())
        return TRUE;
      if (cache->shrink_join_buffer_in_ratio(curr_space, needed_space))
      {
        revise_cache_usage(tab);
        return TRUE;
      }
      buff_size= cache->get_join_buffer_size();
      curr_space-= buff_size;
      needed_space-= buff_size;
    }
  }

  cache= jt->cache;
  DBUG_ASSERT(cache);
  if (needed_space < cache->get_min_join_buffer_size())
    return TRUE;
  cache->set_join_buffer_size((size_t)needed_space);

  return FALSE;
}


void JOIN::exec()
{
  DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
                  if (dbug_user_var_equals_int(thd,
                                               "show_explain_probe_select_id",
                                               select_lex->select_number))
                        dbug_serve_apcs(thd, 1);
                 );
  ANALYZE_START_TRACKING(&explain->time_tracker);
  exec_inner();
  ANALYZE_STOP_TRACKING(&explain->time_tracker);

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


bool JOIN::make_aggr_tables_info()
{
  List<Item> *curr_all_fields= &all_fields;
  List<Item> *curr_fields_list= &fields_list;
  JOIN_TAB *curr_tab= join_tab + const_tables;
  TABLE *exec_tmp_table= NULL;
  bool distinct= false;
  bool keep_row_order= false;
  bool is_having_added_as_table_cond= false;
  DBUG_ENTER("JOIN::make_aggr_tables_info");

  const bool has_group_by= this->group;

  sort_and_group_aggr_tab= NULL;

  if (group_optimized_away)
    implicit_grouping= true;

  bool implicit_grouping_with_window_funcs= implicit_grouping &&
                                            select_lex->have_window_funcs();
  bool implicit_grouping_without_tables= implicit_grouping &&
                                         !tables_list;

  /*
    Setup last table to provide fields and all_fields lists to the next
    node in the plan.
  */
  if (join_tab && top_join_tab_count && tables_list)
  {
    join_tab[top_join_tab_count - 1].fields= &fields_list;
    join_tab[top_join_tab_count - 1].all_fields= &all_fields;
  }

  /*
    All optimization is done. Check if we can use the storage engines
    group by handler to evaluate the group by.
    Some storage engines, like spider can also do joins, group by and
    distinct in the engine, so we do this for all queries, not only
    GROUP BY queries.
  */
  if (tables_list && top_join_tab_count && !procedure)
  {
    /*
      At the moment we only support push down for queries where
      all tables are in the same storage engine
    */
    TABLE_LIST *tbl= tables_list;
    handlerton *ht= tbl && tbl->table ? tbl->table->file->partition_ht() : 0;
    for (tbl= tbl->next_local; ht && tbl; tbl= tbl->next_local)
    {
      if (!tbl->table || tbl->table->file->partition_ht() != ht)
        ht= 0;
    }

    if (ht && ht->create_group_by)
    {
      /* Check if the storage engine can intercept the query */
      Query query= {&all_fields, select_distinct, tables_list, conds,
                    group_list, order ? order : group_list, having};
      group_by_handler *gbh= ht->create_group_by(thd, &query);

      if (gbh)
      {
        if (!(pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh)))
          DBUG_RETURN(1);
        /*
          We must store rows in the tmp table if we need to do an ORDER BY
          or DISTINCT and the storage handler can't handle it.
        */
        need_tmp= query.order_by || query.group_by || query.distinct;
        distinct= query.distinct;
        keep_row_order= query.order_by || query.group_by;

        order= query.order_by;

        aggr_tables++;
        curr_tab= join_tab + exec_join_tab_cnt();
        bzero((void*)curr_tab, sizeof(JOIN_TAB));
        curr_tab->ref.key= -1;
        curr_tab->join= this;

        if (!(curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param)))
          DBUG_RETURN(1);
        TABLE* table= create_tmp_table(thd, curr_tab->tmp_table_param,
                                       all_fields,
                                       NULL, query.distinct,
                                       TRUE, select_options, HA_POS_ERROR,
                                       &empty_clex_str, !need_tmp,
                                       query.order_by || query.group_by);
        if (!table)
          DBUG_RETURN(1);

        if (!(curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab)))
          DBUG_RETURN(1);
        curr_tab->aggr->set_write_func(::end_send);
        curr_tab->table= table;
        /*
          Setup reference fields, used by summary functions and group by fields,
          to point to the temporary table.
          The actual switching to the temporary tables fields for HAVING
          and ORDER BY is done in do_select() by calling
          set_items_ref_array(items1).
        */
        init_items_ref_array();
        items1= ref_ptr_array_slice(2);
        //items1= items0 + all_fields.elements;
        if (change_to_use_tmp_fields(thd, items1,
                                     tmp_fields_list1, tmp_all_fields1,
                                     fields_list.elements, all_fields))
          DBUG_RETURN(1);

        /* Give storage engine access to temporary table */
        gbh->table= table;
        pushdown_query->store_data_in_temp_table= need_tmp;
        pushdown_query->having= having;

        /*
          Group by and having is calculated by the group_by handler.
          Reset the group by and having
        */
        DBUG_ASSERT(query.group_by == NULL);
        group= 0; group_list= 0;
        having= tmp_having= 0;
        /*
          Select distinct is handled by handler or by creating an unique index
          over all fields in the temporary table
        */
        select_distinct= 0;
        order= query.order_by;
        tmp_table_param.field_count+= tmp_table_param.sum_func_count;
        tmp_table_param.sum_func_count= 0;

        fields= curr_fields_list;

        //todo: new:
        curr_tab->ref_array= &items1;
        curr_tab->all_fields= &tmp_all_fields1;
        curr_tab->fields= &tmp_fields_list1;

        DBUG_RETURN(thd->is_fatal_error);
      }
    }
  }

  /*
    The loose index scan access method guarantees that all grouping or
    duplicate row elimination (for distinct) is already performed
    during data retrieval, and that all MIN/MAX functions are already
    computed for each group. Thus all MIN/MAX functions should be
    treated as regular functions, and there is no need to perform
    grouping in the main execution loop.
    Notice that currently loose index scan is applicable only for
    single table queries, thus it is sufficient to test only the first
    join_tab element of the plan for its access method.
  */
  if (join_tab && top_join_tab_count && tables_list &&
      join_tab->is_using_loose_index_scan())
    tmp_table_param.precomputed_group_by=
      !join_tab->is_using_agg_loose_index_scan();

  group_list_for_estimates= group_list;
  /* Create a tmp table if distinct or if the sort is too complicated */
  if (need_tmp)
  {
    aggr_tables++;
    curr_tab= join_tab + exec_join_tab_cnt();
    bzero((void*)curr_tab, sizeof(JOIN_TAB));
    curr_tab->ref.key= -1;
    if (only_const_tables())
      first_select= sub_select_postjoin_aggr;

    /*
      Create temporary table on first execution of this join.
      (Will be reused if this is a subquery that is executed several times.)
    */
    init_items_ref_array();

    ORDER *tmp_group= (ORDER *) 0;
    if (!simple_group && !procedure && !(test_flags & TEST_NO_KEY_GROUP))
      tmp_group= group_list;

    tmp_table_param.hidden_field_count=
      all_fields.elements - fields_list.elements;

    distinct= select_distinct && !group_list &&
              !select_lex->have_window_funcs();
    keep_row_order= false;
    bool save_sum_fields= (group_list && simple_group) ||
                           implicit_grouping_with_window_funcs;
    if (create_postjoin_aggr_table(curr_tab,
                                   &all_fields, tmp_group,
                                   save_sum_fields,
                                   distinct, keep_row_order))
      DBUG_RETURN(true);
    exec_tmp_table= curr_tab->table;

    if (exec_tmp_table->distinct)
      optimize_distinct();

   /* Change sum_fields reference to calculated fields in tmp_table */
    items1= ref_ptr_array_slice(2);
    if ((sort_and_group || curr_tab->table->group ||
         tmp_table_param.precomputed_group_by) &&
         !implicit_grouping_without_tables)
    {
      if (change_to_use_tmp_fields(thd, items1,
                                   tmp_fields_list1, tmp_all_fields1,
                                   fields_list.elements, all_fields))
        DBUG_RETURN(true);
    }
    else
    {
      if (change_refs_to_tmp_fields(thd, items1,
                                    tmp_fields_list1, tmp_all_fields1,
                                    fields_list.elements, all_fields))
        DBUG_RETURN(true);
    }
    curr_all_fields= &tmp_all_fields1;
    curr_fields_list= &tmp_fields_list1;
    // Need to set them now for correct group_fields setup, reset at the end.
    set_items_ref_array(items1);
    curr_tab->ref_array= &items1;
    curr_tab->all_fields= &tmp_all_fields1;
    curr_tab->fields= &tmp_fields_list1;
    set_postjoin_aggr_write_func(curr_tab);

    /*
      If having is not handled here, it will be checked before the row is sent
      to the client.
    */
    if (tmp_having &&
        (sort_and_group || (exec_tmp_table->distinct && !group_list) ||
	 select_lex->have_window_funcs()))
    {
      /*
        If there is no select distinct and there are no window functions
        then move the having to table conds of tmp table.
        NOTE : We cannot apply having after distinct or window functions
               If columns of having are not part of select distinct,
               then distinct may remove rows which can satisfy having.
               In the case of window functions we *must* make sure to not
               store any rows which don't match HAVING within the temp table,
               as rows will end up being used during their computation.
      */
      if (!select_distinct && !select_lex->have_window_funcs() &&
          add_having_as_table_cond(curr_tab))
        DBUG_RETURN(true);
      is_having_added_as_table_cond= tmp_having != having;

      /*
        Having condition which we are not able to add as tmp table conds are
        kept as before. And, this will be applied before storing the rows in
        tmp table.
      */
      curr_tab->having= having;
      having= NULL; // Already done
    }

    tmp_table_param.func_count= 0;
    tmp_table_param.field_count+= tmp_table_param.func_count;
    if (sort_and_group || curr_tab->table->group)
    {
      tmp_table_param.field_count+= tmp_table_param.sum_func_count;
      tmp_table_param.sum_func_count= 0;
    }

    if (exec_tmp_table->group)
    {						// Already grouped
      if (!order && !no_order && !skip_sort_order)
        order= group_list;  /* order by group */
      group_list= NULL;
    }

    /*
      If we have different sort & group then we must sort the data by group
      and copy it to another tmp table
      This code is also used if we are using distinct something
      we haven't been able to store in the temporary table yet
      like SEC_TO_TIME(SUM(...)).
    */
    if ((group_list &&
         (!test_if_subpart(group_list, order) || select_distinct)) ||
        (select_distinct && tmp_table_param.using_outer_summary_function))
    {					/* Must copy to another table */
      DBUG_PRINT("info",("Creating group table"));

      calc_group_buffer(this, group_list);
      count_field_types(select_lex, &tmp_table_param, tmp_all_fields1,
                        select_distinct && !group_list);
      tmp_table_param.hidden_field_count=
        tmp_all_fields1.elements - tmp_fields_list1.elements;

      curr_tab++;
      aggr_tables++;
      bzero((void*)curr_tab, sizeof(JOIN_TAB));
      curr_tab->ref.key= -1;

      /* group data to new table */
      /*
        If the access method is loose index scan then all MIN/MAX
        functions are precomputed, and should be treated as regular
        functions. See extended comment above.
      */
      if (join_tab->is_using_loose_index_scan())
        tmp_table_param.precomputed_group_by= TRUE;

      tmp_table_param.hidden_field_count=
        curr_all_fields->elements - curr_fields_list->elements;
      ORDER *dummy= NULL; //TODO can use table->group here also

      if (create_postjoin_aggr_table(curr_tab, curr_all_fields, dummy, true,
                                     distinct, keep_row_order))
	DBUG_RETURN(true);

      if (group_list)
      {
        if (!only_const_tables())        // No need to sort a single row
        {
          if (add_sorting_to_table(curr_tab - 1, group_list))
            DBUG_RETURN(true);
        }

        if (make_group_fields(this, this))
          DBUG_RETURN(true);
      }

      // Setup sum funcs only when necessary, otherwise we might break info
      // for the first table
      if (group_list || tmp_table_param.sum_func_count)
      {
        if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true, true))
          DBUG_RETURN(true);
        if (prepare_sum_aggregators(sum_funcs,
                                    !join_tab->is_using_agg_loose_index_scan()))
          DBUG_RETURN(true);
        group_list= NULL;
        if (setup_sum_funcs(thd, sum_funcs))
          DBUG_RETURN(true);
      }
      // No sum funcs anymore
      DBUG_ASSERT(items2.is_null());

      items2= ref_ptr_array_slice(3);
      if (change_to_use_tmp_fields(thd, items2,
                                   tmp_fields_list2, tmp_all_fields2,
                                   fields_list.elements, tmp_all_fields1))
        DBUG_RETURN(true);

      curr_fields_list= &tmp_fields_list2;
      curr_all_fields= &tmp_all_fields2;
      set_items_ref_array(items2);
      curr_tab->ref_array= &items2;
      curr_tab->all_fields= &tmp_all_fields2;
      curr_tab->fields= &tmp_fields_list2;
      set_postjoin_aggr_write_func(curr_tab);

      tmp_table_param.field_count+= tmp_table_param.sum_func_count;
      tmp_table_param.sum_func_count= 0;
    }
    if (curr_tab->table->distinct)
      select_distinct= false;               /* Each row is unique */

    if (select_distinct && !group_list)
    {
      if (having)
      {
        curr_tab->having= having;
        having->update_used_tables();
      }
      /*
        We only need DISTINCT operation if the join is not degenerate.
        If it is, we must not request DISTINCT processing, because
        remove_duplicates() assumes there is a preceding computation step (and
        in the degenerate join, there's none)
      */
      if (top_join_tab_count && tables_list)
        curr_tab->distinct= true;

      having= NULL;
      select_distinct= false;
    }
    /* Clean tmp_table_param for the next tmp table. */
    tmp_table_param.field_count= tmp_table_param.sum_func_count=
      tmp_table_param.func_count= 0;

    tmp_table_param.copy_field= tmp_table_param.copy_field_end=0;
    first_record= sort_and_group=0;

    if (!group_optimized_away || implicit_grouping_with_window_funcs)
    {
      group= false;
    }
    else
    {
      /*
        If grouping has been optimized away, a temporary table is
        normally not needed unless we're explicitly requested to create
        one (e.g. due to a SQL_BUFFER_RESULT hint or INSERT ... SELECT).

        In this case (grouping was optimized away), temp_table was
        created without a grouping expression and JOIN::exec() will not
        perform the necessary grouping (by the use of end_send_group()
        or end_write_group()) if JOIN::group is set to false.
      */
      // the temporary table was explicitly requested
      DBUG_ASSERT(MY_TEST(select_options & OPTION_BUFFER_RESULT));
      // the temporary table does not have a grouping expression
      DBUG_ASSERT(!curr_tab->table->group);
    }
    calc_group_buffer(this, group_list);
    count_field_types(select_lex, &tmp_table_param, *curr_all_fields, false);
  }

  if (group ||
      (implicit_grouping  && !implicit_grouping_with_window_funcs) ||
      tmp_table_param.sum_func_count)
  {
    if (make_group_fields(this, this))
      DBUG_RETURN(true);

    DBUG_ASSERT(items3.is_null());

    if (items0.is_null())
      init_items_ref_array();
    items3= ref_ptr_array_slice(4);
    setup_copy_fields(thd, &tmp_table_param,
                      items3, tmp_fields_list3, tmp_all_fields3,
                      curr_fields_list->elements, *curr_all_fields);

    curr_fields_list= &tmp_fields_list3;
    curr_all_fields= &tmp_all_fields3;
    set_items_ref_array(items3);
    if (join_tab)
    {
      JOIN_TAB *last_tab= join_tab + top_join_tab_count + aggr_tables - 1;
      // Set grouped fields on the last table
      last_tab->ref_array= &items3;
      last_tab->all_fields= &tmp_all_fields3;
      last_tab->fields= &tmp_fields_list3;
    }
    if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true, true))
      DBUG_RETURN(true);
    if (prepare_sum_aggregators(sum_funcs,
                                !join_tab ||
                                !join_tab-> is_using_agg_loose_index_scan()))
      DBUG_RETURN(true);
    if (unlikely(setup_sum_funcs(thd, sum_funcs) || thd->is_fatal_error))
      DBUG_RETURN(true);
  }
  if (group_list || order)
  {
    DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
    THD_STAGE_INFO(thd, stage_sorting_result);
    /* If we have already done the group, add HAVING to sorted table */
    if (tmp_having && !is_having_added_as_table_cond &&
        !group_list && !sort_and_group)
    {
      if (add_having_as_table_cond(curr_tab))
        DBUG_RETURN(true);
    }

    if (group)
      select_limit= HA_POS_ERROR;
    else if (!need_tmp)
    {
      /*
        We can abort sorting after thd->select_limit rows if there are no
        filter conditions for any tables after the sorted one.
        Filter conditions come in several forms:
         1. as a condition item attached to the join_tab, or
         2. as a keyuse attached to the join_tab (ref access).
      */
      for (uint i= const_tables + 1; i < top_join_tab_count; i++)
      {
        JOIN_TAB *const tab= join_tab + i;
        if (tab->select_cond ||                                // 1
            (tab->keyuse && !tab->first_inner))                // 2
        {
          /* We have to sort all rows */
          select_limit= HA_POS_ERROR;
          break;
        }
      }
    }
    /*
      Here we add sorting stage for ORDER BY/GROUP BY clause, if the
      optimiser chose FILESORT to be faster than INDEX SCAN or there is
      no suitable index present.
      OPTION_FOUND_ROWS supersedes LIMIT and is taken into account.
    */
    DBUG_PRINT("info",("Sorting for order by/group by"));
    ORDER *order_arg= group_list ?  group_list : order;
    if (top_join_tab_count + aggr_tables > const_tables &&
        ordered_index_usage !=
        (group_list ? ordered_index_group_by : ordered_index_order_by) &&
        curr_tab->type != JT_CONST &&
        curr_tab->type != JT_EQ_REF) // Don't sort 1 row
    {
      // Sort either first non-const table or the last tmp table
      JOIN_TAB *sort_tab= curr_tab;

      if (add_sorting_to_table(sort_tab, order_arg))
        DBUG_RETURN(true);
      /*
        filesort_limit:	 Return only this many rows from filesort().
        We can use select_limit_cnt only if we have no group_by and 1 table.
        This allows us to use Bounded_queue for queries like:
          "select SQL_CALC_FOUND_ROWS * from t1 order by b desc limit 1;"
        m_select_limit == HA_POS_ERROR (we need a full table scan)
        unit->select_limit_cnt == 1 (we only need one row in the result set)
      */
      sort_tab->filesort->limit=
        (has_group_by || (join_tab + table_count > curr_tab + 1)) ?
         select_limit : unit->select_limit_cnt;
    }
    if (!only_const_tables() &&
        !join_tab[const_tables].filesort &&
        !(select_options & SELECT_DESCRIBE))
    {
      /*
        If no IO cache exists for the first table then we are using an
        INDEX SCAN and no filesort. Thus we should not remove the sorted
        attribute on the INDEX SCAN.
      */
      skip_sort_order= true;
    }
  }

  /*
    Window functions computation step should be attached to the last join_tab
    that's doing aggregation.
    The last join_tab reads the data from the temp. table.  It also may do
    - sorting
    - duplicate value removal
    Both of these operations are done after window function computation step.
  */
  curr_tab= join_tab + total_join_tab_cnt();
  if (select_lex->window_funcs.elements)
  {
    if (!(curr_tab->window_funcs_step= new Window_funcs_computation))
      DBUG_RETURN(true);
    if (curr_tab->window_funcs_step->setup(thd, &select_lex->window_funcs,
                                           curr_tab))
      DBUG_RETURN(true);
    /* Count that we're using window functions. */
    status_var_increment(thd->status_var.feature_window_functions);
  }
  if (select_lex->custom_agg_func_used())
    status_var_increment(thd->status_var.feature_custom_aggregate_functions);

  fields= curr_fields_list;
  // Reset before execution
  set_items_ref_array(items0);
  if (join_tab)
    join_tab[exec_join_tab_cnt() + aggr_tables - 1].next_select=
      setup_end_select_func(this, NULL);
  group= has_group_by;

  DBUG_RETURN(false);
}


int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABLE *table_arg)
{
  uint i;
  for (i= send_group_parts ; i-- > idx ; )
  {
    /* Get reference pointers to sum functions in place */
    copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]);
    if ((!having || having->val_int()))
    {
      int write_error;
      Item *item;
      List_iterator_fast<Item> it(rollup.fields[i]);
      while ((item= it++))
      {
        if (item->type() == Item::NULL_ITEM && item->is_result_field())
          item->save_in_result_field(1);
      }
      copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
      if (unlikely((write_error=
                    table_arg->file->ha_write_tmp_row(table_arg->record[0]))))
      {
	if (create_internal_tmp_table_from_heap(thd, table_arg,
                                                tmp_table_param_arg->start_recinfo,
                                                &tmp_table_param_arg->recinfo,
                                                write_error, 0, NULL))
	  return 1;
      }
    }
  }
  /* Restore ref_pointer_array */
  set_items_ref_array(current_ref_ptrs);
  return 0;
}


static JOIN_TAB *next_breadth_first_tab(JOIN_TAB *first_top_tab,
                                        uint n_top_tabs_count, JOIN_TAB *tab)
{
  n_top_tabs_count += tab->join->aggr_tables;
  if (!tab->bush_root_tab)
  {
    /* We're at top level. Get the next top-level tab */
    tab++;
    if (tab < first_top_tab + n_top_tabs_count)
      return tab;

    /* No more top-level tabs. Switch to enumerating SJM nest children */
    tab= first_top_tab;
  }
  else
  {
    /* We're inside of an SJM nest */
    if (!tab->last_leaf_in_bush)
    {
      /* There's one more table in the nest, return it. */
      return ++tab;
    }
    else
    {
      /*
        There are no more tables in this nest. Get out of it and then we'll
        proceed to the next nest.
      */
      tab= tab->bush_root_tab + 1;
    }
  }

  /*
    Ok, "tab" points to a top-level table, and we need to find the next SJM
    nest and enter it.
  */
  for (; tab < first_top_tab + n_top_tabs_count; tab++)
  {
    if (tab->bush_children)
      return tab->bush_children->start;
  }
  return NULL;
}


join_read_record_no_init(JOIN_TAB *tab)
{
  Copy_field *save_copy, *save_copy_end;

  /*
    init_read_record resets all elements of tab->read_record().
    Remember things that we don't want to have reset.
  */
  save_copy=     tab->read_record.copy_field;
  save_copy_end= tab->read_record.copy_field_end;

  init_read_record(&tab->read_record, tab->join->thd, tab->table,
		   tab->select, tab->filesort_result, 1, 1, FALSE);

  tab->read_record.copy_field=     save_copy;
  tab->read_record.copy_field_end= save_copy_end;
  tab->read_record.read_record_func= rr_sequential_and_unpack;

  return tab->read_record.read_record();
}


static void update_depend_map(JOIN *join)
{
  JOIN_TAB *join_tab;
  for (join_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITH_CONST_TABLES);
       join_tab;
       join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS))
  {
    TABLE_REF *ref= &join_tab->ref;
    table_map depend_map=0;
    Item **item=ref->items;
    uint i;
    for (i=0 ; i < ref->key_parts ; i++,item++)
      depend_map|=(*item)->used_tables();
    depend_map&= ~OUTER_REF_TABLE_BIT;
    ref->depend_map= depend_map;
    for (JOIN_TAB **tab=join->map2table;
         depend_map ;
         tab++,depend_map>>=1 )
    {
      if (depend_map & 1)
        ref->depend_map|=(*tab)->ref.depend_map;
    }
  }
}


void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
  DBUG_ASSERT(thd);

  if (tvc)
  {
    tvc->print(thd, str, query_type);
    return;
  }

  if ((query_type & QT_SHOW_SELECT_NUMBER) &&
      thd->lex->all_selects_list &&
      thd->lex->all_selects_list->link_next &&
      select_number != UINT_MAX &&
      select_number != INT_MAX)
  {
    str->append("/* select#");
    str->append_ulonglong(select_number);
    str->append(" */ ");
  }

  str->append(STRING_WITH_LEN("select "));

  if (join && join->cleaned)
  {
    /*
      JOIN already cleaned up so it is dangerous to print items
      because temporary tables they pointed on could be freed.
    */
    str->append('#');
    str->append(select_number);
    return;
  }

  /* First add options */
  if (options & SELECT_STRAIGHT_JOIN)
    str->append(STRING_WITH_LEN("straight_join "));
  if (options & SELECT_HIGH_PRIORITY)
    str->append(STRING_WITH_LEN("high_priority "));
  if (options & SELECT_DISTINCT)
    str->append(STRING_WITH_LEN("distinct "));
  if (options & SELECT_SMALL_RESULT)
    str->append(STRING_WITH_LEN("sql_small_result "));
  if (options & SELECT_BIG_RESULT)
    str->append(STRING_WITH_LEN("sql_big_result "));
  if (options & OPTION_BUFFER_RESULT)
    str->append(STRING_WITH_LEN("sql_buffer_result "));
  if (options & OPTION_FOUND_ROWS)
    str->append(STRING_WITH_LEN("sql_calc_found_rows "));
  switch (sql_cache)
  {
    case SQL_NO_CACHE:
      str->append(STRING_WITH_LEN("sql_no_cache "));
      break;
    case SQL_CACHE:
      str->append(STRING_WITH_LEN("sql_cache "));
      break;
    case SQL_CACHE_UNSPECIFIED:
      break;
    default:
      DBUG_ASSERT(0);
  }

  //Item List
  bool first= 1;
  List_iterator_fast<Item> it(item_list);
  Item *item;
  while ((item= it++))
  {
    if (first)
      first= 0;
    else
      str->append(',');

    if (is_subquery_function() && item->is_autogenerated_name)
    {
      /*
        Do not print auto-generated aliases in subqueries. It has no purpose
        in a view definition or other contexts where the query is printed.
      */
      item->print(str, query_type);
    }
    else
      item->print_item_w_name(str, query_type);
  }

  /*
    from clause
    TODO: support USING/FORCE/IGNORE index
  */
  if (table_list.elements)
  {
    str->append(STRING_WITH_LEN(" from "));
    /* go through join tree */
    print_join(thd, join? join->eliminated_tables: 0, str, &top_join_list, query_type);
  }
  else if (where)
  {
    /*
      "SELECT 1 FROM DUAL WHERE 2" should not be printed as
      "SELECT 1 WHERE 2": the 1st syntax is valid, but the 2nd is not.
    */
    str->append(STRING_WITH_LEN(" from DUAL "));
  }

  // Where
  Item *cur_where= where;
  if (join)
    cur_where= join->conds;
  if (cur_where || cond_value != Item::COND_UNDEF)
  {
    str->append(STRING_WITH_LEN(" where "));
    if (cur_where)
      cur_where->print(str, query_type);
    else
      str->append(cond_value != Item::COND_FALSE ? "1" : "0");
  }

  // group by & olap
  if (group_list.elements)
  {
    str->append(STRING_WITH_LEN(" group by "));
    print_order(str, group_list.first, query_type);
    switch (olap)
    {
      case CUBE_TYPE:
	str->append(STRING_WITH_LEN(" with cube"));
	break;
      case ROLLUP_TYPE:
	str->append(STRING_WITH_LEN(" with rollup"));
	break;
      default:
	;  //satisfy compiler
    }
  }

  // having
  Item *cur_having= having;
  if (join)
    cur_having= join->having;

  if (cur_having || having_value != Item::COND_UNDEF)
  {
    str->append(STRING_WITH_LEN(" having "));
    if (cur_having)
      cur_having->print(str, query_type);
    else
      str->append(having_value != Item::COND_FALSE ? "1" : "0");
  }

  if (order_list.elements)
  {
    str->append(STRING_WITH_LEN(" order by "));
    print_order(str, order_list.first, query_type);
  }

  // limit
  print_limit(thd, str, query_type);

  // lock type
  if (lock_type == TL_READ_WITH_SHARED_LOCKS)
    str->append(" lock in share mode");
  else if (lock_type == TL_WRITE)
    str->append(" for update");

  // PROCEDURE unsupported here
}


bool Virtual_tmp_table::sp_set_all_fields_from_item(THD *thd, Item *value)
{
  DBUG_ASSERT(value->fixed);
  DBUG_ASSERT(value->cols() == s->fields);
  for (uint i= 0; i < value->cols(); i++)
  {
    if (field[i]->sp_prepare_and_store_item(thd, value->addr(i)))
      return true;
  }
  return false;
}


bool handle_select(THD *thd, LEX *lex, select_result *result,
                   ulong setup_tables_done_option)
{
  bool res;
  SELECT_LEX *select_lex = &lex->select_lex;
  DBUG_ENTER("handle_select");
  MYSQL_SELECT_START(thd->query());

  if (select_lex->master_unit()->is_unit_op() ||
      select_lex->master_unit()->fake_select_lex)
    res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option);
  else
  {
    SELECT_LEX_UNIT *unit= &lex->unit;
    unit->set_limit(unit->global_parameters());
    /*
      'options' of mysql_select will be set in JOIN, as far as JOIN for
      every PS/SP execution new, we will not need reset this flag if
      setup_tables_done_option changed for next rexecution
    */
    res= mysql_select(thd,
		      select_lex->table_list.first,
		      select_lex->with_wild, select_lex->item_list,
		      select_lex->where,
		      select_lex->order_list.elements +
		      select_lex->group_list.elements,
		      select_lex->order_list.first,
		      select_lex->group_list.first,
		      select_lex->having,
		      lex->proc_list.first,
		      select_lex->options | thd->variables.option_bits |
                      setup_tables_done_option,
		      result, unit, select_lex);
  }
  DBUG_PRINT("info",("res: %d  report_error: %d", res,
		     thd->is_error()));
  res|= thd->is_error();
  if (unlikely(res))
    result->abort_result_set();
  if (unlikely(thd->killed == ABORT_QUERY && !thd->no_errors))
  {
    /*
      If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
      continue with normal processing and produce an incomplete query result.
    */
    bool saved_abort_on_warning= thd->abort_on_warning;
    thd->abort_on_warning= false;
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                        ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
                        ER_THD(thd, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
                        thd->accessed_rows_and_keys,
                        thd->lex->limit_rows_examined->val_uint());
    thd->abort_on_warning= saved_abort_on_warning;
    thd->reset_killed();
  }
  /* Disable LIMIT ROWS EXAMINED after query execution. */
  thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;

  MYSQL_SELECT_DONE((int) res, (ulong) thd->limit_found_rows);
  DBUG_RETURN(res);
}