Commit message: 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.
CVE: CVE-2021-46662
CWE: NVD-CWE-noinfo
Vulnerability description: MariaDB through 10.5.9 allows a set_var.cc application crash via certain uses of an UPDATE statement in conjunction with a nested subquery.

Project: server

const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
                          Item **const_item)
{
  DBUG_ASSERT((comp_item == NULL) ^ (comp_field == NULL));

  Item *intermediate= NULL;
  if (const_item == NULL)
    const_item= &intermediate;

  if (cond->type() == Item::COND_ITEM)
  {
    bool and_level= (((Item_cond*) cond)->functype()
		     == Item_func::COND_AND_FUNC);
    List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
    Item *item;
    while ((item=li++))
    {
      bool res=const_expression_in_where(item, comp_item, comp_field,
                                         const_item);
      if (res)					// Is a const value
      {
	if (and_level)
	  return 1;
      }
      else if (!and_level)
	return 0;
    }
    return and_level ? 0 : 1;
  }
  else if (cond->eq_cmp_result() != Item::COND_OK)
  {						// boolean compare function
    Item_func* func= (Item_func*) cond;
    if (func->functype() != Item_func::EQUAL_FUNC &&
	func->functype() != Item_func::EQ_FUNC)
      return 0;
    Item *left_item=	((Item_func*) cond)->arguments()[0];
    Item *right_item= ((Item_func*) cond)->arguments()[1];
    if (equal(left_item, comp_item, comp_field))
    {
      if (test_if_equality_guarantees_uniqueness (left_item, right_item))
      {
	if (*const_item)
	  return right_item->eq(*const_item, 1);
	*const_item=right_item;
	return 1;
      }
    }
    else if (equal(right_item, comp_item, comp_field))
    {
      if (test_if_equality_guarantees_uniqueness (right_item, left_item))
      {
	if (*const_item)
	  return left_item->eq(*const_item, 1);
	*const_item=left_item;
	return 1;
      }
    }
  }
  return 0;
}

Project: server

make_cond_after_sjm(THD *thd, Item *root_cond, Item *cond, table_map tables,
                    table_map sjm_tables, bool inside_or_clause)
{
  /*
    We assume that conditions that refer to only join prefix tables or 
    sjm_tables have already been checked.
  */
  if (!inside_or_clause)
  {
    table_map cond_used_tables= cond->used_tables();
    if((!(cond_used_tables & ~tables) ||
       !(cond_used_tables & ~sjm_tables)))
      return (COND*) 0;				// Already checked
  }

  /* AND/OR recursive descent */
  if (cond->type() == Item::COND_ITEM)
  {
    if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
    {
      /* Create new top level AND item */
      Item_cond_and *new_cond= new (thd->mem_root) Item_cond_and(thd);
      if (!new_cond)
	return (COND*) 0;			// OOM /* purecov: inspected */
      List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
      Item *item;
      while ((item=li++))
      {
        Item *fix=make_cond_after_sjm(thd, root_cond, item, tables, sjm_tables,
                                      inside_or_clause);
	if (fix)
	  new_cond->argument_list()->push_back(fix, thd->mem_root);
      }
      switch (new_cond->argument_list()->elements) {
      case 0:
	return (COND*) 0;			// Always true
      case 1:
	return new_cond->argument_list()->head();
      default:
	/*
	  Item_cond_and do not need fix_fields for execution, its parameters
	  are fixed or do not need fix_fields, too
	*/
	new_cond->quick_fix_field();
	new_cond->used_tables_cache=
	  ((Item_cond_and*) cond)->used_tables_cache &
	  tables;
	return new_cond;
      }
    }
    else
    {						// Or list
      Item_cond_or *new_cond= new (thd->mem_root) Item_cond_or(thd);
      if (!new_cond)
	return (COND*) 0;			// OOM /* purecov: inspected */
      List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
      Item *item;
      while ((item=li++))
      {
        Item *fix= make_cond_after_sjm(thd, root_cond, item, tables, sjm_tables,
                                       /*inside_or_clause= */TRUE);
	if (!fix)
	  return (COND*) 0;			// Always true
	new_cond->argument_list()->push_back(fix, thd->mem_root);
      }
      /*
	Item_cond_or do not need fix_fields for execution, its parameters
	are fixed or do not need fix_fields, too
      */
      new_cond->quick_fix_field();
      new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache;
      new_cond->top_level_item();
      return new_cond;
    }
  }

  /*
    Because the following test takes a while and it can be done
    table_count times, we mark each item that we have examined with the result
    of the test
  */

  if (cond->marker == 3 || (cond->used_tables() & ~(tables | sjm_tables)))
    return (COND*) 0;				// Can't check this yet
  if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
    return cond;				// Not boolean op

  /* 
    Remove equalities that are guaranteed to be true by use of 'ref' access
    method
  */
  if (((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
  {
    Item *left_item= ((Item_func*) cond)->arguments()[0]->real_item();
    Item *right_item= ((Item_func*) cond)->arguments()[1]->real_item();
    if (left_item->type() == Item::FIELD_ITEM &&
	test_if_ref(root_cond, (Item_field*) left_item,right_item))
    {
      cond->marker=3;			// Checked when read
      return (COND*) 0;
    }
    if (right_item->type() == Item::FIELD_ITEM &&
	test_if_ref(root_cond, (Item_field*) right_item,left_item))
    {
      cond->marker=3;			// Checked when read
      return (COND*) 0;
    }
  }
  cond->marker=2;
  return cond;
}

Project: server

void JOIN::clear()
{
  clear_tables(this, 0);
  copy_fields(&tmp_table_param);

  if (sum_funcs)
  {
    Item_sum *func, **func_ptr= sum_funcs;
    while ((func= *(func_ptr++)))
      func->clear();
  }
}

Project: server

void Virtual_tmp_table::setup_field_pointers()
{
  uchar *null_pos= record[0];
  uchar *field_pos= null_pos + s->null_bytes;
  uint null_bit= 1;

  for (Field **cur_ptr= field; *cur_ptr; ++cur_ptr)
  {
    Field *cur_field= *cur_ptr;
    if ((cur_field->flags & NOT_NULL_FLAG))
      cur_field->move_field(field_pos);
    else
    {
      cur_field->move_field(field_pos, (uchar*) null_pos, null_bit);
      null_bit<<= 1;
      if (null_bit == (uint)1 << 8)
      {
        ++null_pos;
        null_bit= 1;
      }
    }
    if (cur_field->type() == MYSQL_TYPE_BIT &&
        cur_field->key_type() == HA_KEYTYPE_BIT)
    {
      /* This is a Field_bit since key_type is HA_KEYTYPE_BIT */
      static_cast<Field_bit*>(cur_field)->set_bit_ptr(null_pos, null_bit);
      null_bit+= cur_field->field_length & 7;
      if (null_bit > 7)
      {
        null_pos++;
        null_bit-= 8;
      }
    }
    cur_field->reset();
    field_pos+= cur_field->pack_length();
  }
}

Project: server

bool JOIN::alloc_func_list()
{
  uint func_count, group_parts;
  DBUG_ENTER("alloc_func_list");

  func_count= tmp_table_param.sum_func_count;
  /*
    If we are using rollup, we need a copy of the summary functions for
    each level
  */
  if (rollup.state != ROLLUP::STATE_NONE)
    func_count*= (send_group_parts+1);

  group_parts= send_group_parts;
  /*
    If distinct, reserve memory for possible
    disctinct->group_by optimization
  */
  if (select_distinct)
  {
    group_parts+= fields_list.elements;
    /*
      If the ORDER clause is specified then it's possible that
      it also will be optimized, so reserve space for it too
    */
    if (order)
    {
      ORDER *ord;
      for (ord= order; ord; ord= ord->next)
        group_parts++;
    }
  }

  /* This must use calloc() as rollup_make_fields depends on this */
  sum_funcs= (Item_sum**) thd->calloc(sizeof(Item_sum**) * (func_count+1) +
				      sizeof(Item_sum***) * (group_parts+1));
  sum_funcs_end= (Item_sum***) (sum_funcs+func_count+1);
  DBUG_RETURN(sum_funcs == 0);
}

Project: server

test_if_subpart(ORDER *a,ORDER *b)
{
  for (; a && b; a=a->next,b=b->next)
  {
    if ((*a->item)->eq(*b->item,1))
      a->direction=b->direction;
    else
      return 0;
  }
  return MY_TEST(!b);
}

Project: server

end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
	  bool end_of_records)
{
  TABLE *const table= join_tab->table;
  DBUG_ENTER("end_write");

  if (!end_of_records)
  {
    copy_fields(join_tab->tmp_table_param);
    if (copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd))
      DBUG_RETURN(NESTED_LOOP_ERROR);           /* purecov: inspected */

    if (likely(!join_tab->having || join_tab->having->val_int()))
    {
      int error;
      join->found_records++;
      if ((error= table->file->ha_write_tmp_row(table->record[0])))
      {
        if (likely(!table->file->is_fatal_error(error, HA_CHECK_DUP)))
	  goto end;                             // Ignore duplicate keys
        bool is_duplicate;
	if (create_internal_tmp_table_from_heap(join->thd, table, 
                                                join_tab->tmp_table_param->start_recinfo,
                                                &join_tab->tmp_table_param->recinfo,
                                                error, 1, &is_duplicate))
	  DBUG_RETURN(NESTED_LOOP_ERROR);        // Not a table_is_full error
        if (is_duplicate)
          goto end;
	table->s->uniques=0;			// To ensure rows are the same
      }
      if (++join_tab->send_records >=
            join_tab->tmp_table_param->end_write_records &&
	  join->do_send_rows)
      {
	if (!(join->select_options & OPTION_FOUND_ROWS))
	  DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
	join->do_send_rows=0;
	join->unit->select_limit_cnt = HA_POS_ERROR;
      }
    }
  }
end:
  if (unlikely(join->thd->check_killed()))
  {
    DBUG_RETURN(NESTED_LOOP_KILLED);             /* purecov: inspected */
  }
  DBUG_RETURN(NESTED_LOOP_OK);
}

Project: server

change_refs_to_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, *new_item;
  res_selected_fields.empty();
  res_all_fields.empty();

  uint i, border= all_fields.elements - elements;
  for (i= 0; (item= it++); i++)
  {
    if (item->type() == Item::SUM_FUNC_ITEM && item->const_item())
      new_item= item;
    else
    {
      if (!(new_item= item->get_tmp_table_item(thd)))
        return 1;
    }

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

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

  return thd->is_fatal_error;
}

Project: server

static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr)
{
  Item_sum *func;
  DBUG_ENTER("setup_sum_funcs");
  while ((func= *(func_ptr++)))
  {
    if (func->aggregator_setup(thd))
      DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
}

Project: server

alloc_group_fields(JOIN *join,ORDER *group)
{
  if (group)
  {
    for (; group ; group=group->next)
    {
      Cached_item *tmp=new_Cached_item(join->thd, *group->item, TRUE);
      if (!tmp || join->group_fields.push_front(tmp))
	return TRUE;
    }
  }
  join->sort_and_group=1;			/* Mark for do_select */
  return FALSE;
}

Project: server

COND *Item_func::build_equal_items(THD *thd, COND_EQUAL *inherited,
                                   bool link_item_fields,
                                   COND_EQUAL **cond_equal_ref)
{
  /* 
    For each field reference in cond, not from equal item predicates,
    set a pointer to the multiple equality it belongs to (if there is any)
    as soon the field is not of a string type or the field reference is
    an argument of a comparison predicate. 
  */ 
  COND *cond= propagate_equal_fields(thd, Context_boolean(), inherited);
  cond->update_used_tables();
  DBUG_ASSERT(cond == this);
  DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
  return cond;
}

Project: server

is_subkey(KEY_PART_INFO *key_part, KEY_PART_INFO *ref_key_part,
	  KEY_PART_INFO *ref_key_part_end)
{
  for (; ref_key_part < ref_key_part_end; key_part++, ref_key_part++)
    if (!key_part->field->eq(ref_key_part->field))
      return 0;
  return 1;
}

Project: server

void propagate_new_equalities(THD *thd, Item *cond,
                              List<Item_equal> *new_equalities,
                              COND_EQUAL *inherited,
                              bool *is_simplifiable_cond)
{
  if (cond->type() == Item::COND_ITEM)
  {
    bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
    if (and_level)
    {
      Item_cond_and *cond_and= (Item_cond_and *) cond; 
      List<Item_equal> *cond_equalities= &cond_and->m_cond_equal.current_level;
      cond_and->m_cond_equal.upper_levels= inherited;
      if (!cond_equalities->is_empty() && cond_equalities != new_equalities)
      {
        Item_equal *equal_item;
        List_iterator<Item_equal> it(*new_equalities);
	while ((equal_item= it++))
	{
          equal_item->merge_into_list(thd, cond_equalities, true, true);
        }
        List_iterator<Item_equal> ei(*cond_equalities);
        while ((equal_item= ei++))
	{
          if (equal_item->const_item() && !equal_item->val_int())
	  {
            *is_simplifiable_cond= true;
            return;
          }
        }
      }
    }

    Item *item;
    List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
    while ((item= li++))
    {
      COND_EQUAL *new_inherited= and_level && item->type() == Item::COND_ITEM ?
                                   &((Item_cond_and *) cond)->m_cond_equal :
                                   inherited;
      propagate_new_equalities(thd, item, new_equalities, new_inherited,
                               is_simplifiable_cond);
    }
  }
  else if (cond->type() == Item::FUNC_ITEM && 
           ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
  {
    Item_equal *equal_item;
    List_iterator<Item_equal> it(*new_equalities);
    Item_equal *equality= (Item_equal *) cond;
    equality->upper_levels= inherited;
    while ((equal_item= it++))
    {
      equality->merge_with_check(thd, equal_item, true);
    }
    if (equality->const_item() && !equality->val_int())
      *is_simplifiable_cond= true;
  }
  else
  {
    cond= cond->propagate_equal_fields(thd,
                                       Item::Context_boolean(), inherited);
    cond->update_used_tables();
  }          
}

Project: server

void revise_cache_usage(JOIN_TAB *join_tab)
{
  JOIN_TAB *tab;
  JOIN_TAB *first_inner;

  if (join_tab->first_inner)
  {
    JOIN_TAB *end_tab= join_tab;
    for (first_inner= join_tab->first_inner; 
         first_inner;
         first_inner= first_inner->first_upper)           
    {
      for (tab= end_tab; tab >= first_inner; tab--)
        set_join_cache_denial(tab);
      end_tab= first_inner;
    }
  }
  else if (join_tab->first_sj_inner_tab)
  {
    first_inner= join_tab->first_sj_inner_tab;
    for (tab= join_tab; tab >= first_inner; tab--)
    {
      set_join_cache_denial(tab);
    }
  }
  else set_join_cache_denial(join_tab);
}

Project: server

join_read_prev_same(READ_RECORD *info)
{
  int error;
  TABLE *table= info->table;
  JOIN_TAB *tab=table->reginfo.join_tab;

  if (unlikely((error= table->file->ha_index_prev(table->record[0]))))
    return report_error(table, error);
  if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
                      tab->ref.key_length))
  {
    table->status=STATUS_NOT_FOUND;
    error= -1;
  }
  return error;
}

Project: server

Item_func_trig_cond::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
                                    uint *and_level, table_map usable_tables,
                                    SARGABLE_PARAM **sargables)
{
  /* 
    Subquery optimization: Conditions that are pushed down into subqueries
    are wrapped into Item_func_trig_cond. We process the wrapped condition
    but need to set cond_guard for KEYUSE elements generated from it.
  */
  if (!join->group_list && !join->order &&
      join->unit->item && 
      join->unit->item->substype() == Item_subselect::IN_SUBS &&
      !join->unit->is_unit_op())
  {
    KEY_FIELD *save= *key_fields;
    args[0]->add_key_fields(join, key_fields, and_level, usable_tables,
                            sargables);
    // Indicate that this ref access candidate is for subquery lookup:
    for (; save != *key_fields; save++)
      save->cond_guard= get_trig_var();
  }
}

Project: server

int rr_sequential_and_unpack(READ_RECORD *info)
{
  int error;
  if (unlikely((error= rr_sequential(info))))
    return error;
  
  for (Copy_field *cp= info->copy_field; cp != info->copy_field_end; cp++)
    (*cp->do_copy)(cp);

  return error;
}

Project: server

Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
                            bool *inherited_fl)
{
  Item_equal *item= 0;
  bool in_upper_level= FALSE;
  while (cond_equal)
  {
    List_iterator_fast<Item_equal> li(cond_equal->current_level);
    while ((item= li++))
    {
      if (item->contains(field))
        goto finish;
    }
    in_upper_level= TRUE;
    cond_equal= cond_equal->upper_levels;
  }
  in_upper_level= FALSE;
finish:
  *inherited_fl= in_upper_level;
  return item;
}

Project: server

void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
{
  KEYUSE *end,*keyuse= dynamic_element(keyuse_array, 0, KEYUSE*);

  for (end= keyuse+ keyuse_array->elements ; keyuse < end ; keyuse++)
  {
    table_map map;
    /*
      If we find a ref, assume this table matches a proportional
      part of this table.
      For example 100 records matching a table with 5000 records
      gives 5000/100 = 50 records per key
      Constant tables are ignored.
      To avoid bad matches, we don't make ref_table_rows less than 100.
    */
    keyuse->ref_table_rows= ~(ha_rows) 0;	// If no ref
    if (keyuse->used_tables &
	(map= (keyuse->used_tables & ~join->const_table_map &
	       ~OUTER_REF_TABLE_BIT)))
    {
      uint n_tables= my_count_bits(map);
      if (n_tables == 1)			// Only one table
      {
        DBUG_ASSERT(!(map & PSEUDO_TABLE_BITS)); // Must be a real table
        Table_map_iterator it(map);
        int tablenr= it.next_bit();
        DBUG_ASSERT(tablenr != Table_map_iterator::BITMAP_END);
	TABLE *tmp_table=join->table[tablenr];
        if (tmp_table) // already created
          keyuse->ref_table_rows= MY_MAX(tmp_table->file->stats.records, 100);
      }
    }
    /*
      Outer reference (external field) is constant for single executing
      of subquery
    */
    if (keyuse->used_tables == OUTER_REF_TABLE_BIT)
      keyuse->ref_table_rows= 1;
  }
}

Project: server

is_local_field (Item *field)
{
  return field->real_item()->type() == Item::FIELD_ITEM
     && !(field->used_tables() & OUTER_REF_TABLE_BIT)
    && !((Item_field *)field->real_item())->get_depended_from();
}

Project: server

uint find_shortest_key(TABLE *table, const key_map *usable_keys)
{
  double min_cost= DBL_MAX;
  uint best= MAX_KEY;
  if (!usable_keys->is_clear_all())
  {
    for (uint nr=0; nr < table->s->keys ; nr++)
    {
      if (usable_keys->is_set(nr))
      {
        double cost= table->file->keyread_time(nr, 1, table->file->records());
        if (cost < min_cost)
        {
          min_cost= cost;
          best=nr;
        }
        DBUG_ASSERT(best < MAX_KEY);
      }
    }
  }
  return best;
}

Project: server

Item::remove_eq_conds(THD *thd, Item::cond_result *cond_value, bool top_level_arg)
{
  if (const_item() && !is_expensive())
  {
    *cond_value= eval_const_cond() ? Item::COND_TRUE : Item::COND_FALSE;
    return (COND*) 0;
  }
  *cond_value= Item::COND_OK;
  return this;                                        // Point at next and level
}

Project: server

void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
{
  join->positions[idx].table= table;
  join->positions[idx].key=key;
  join->positions[idx].records_read=1.0;	/* This is a const table */
  join->positions[idx].cond_selectivity= 1.0;
  join->positions[idx].ref_depend_map= 0;

//  join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
  join->positions[idx].sj_strategy= SJ_OPT_NONE;
  join->positions[idx].use_join_buffer= FALSE;

  /* Move the const table as down as possible in best_ref */
  JOIN_TAB **pos=join->best_ref+idx+1;
  JOIN_TAB *next=join->best_ref[idx];
  for (;next != table ; pos++)
  {
    JOIN_TAB *tmp=pos[0];
    pos[0]=next;
    next=tmp;
  }
  join->best_ref[idx]=table;
  join->positions[idx].spl_plan= 0;
}

Project: server

static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables)
{
  if (jtab->bush_root_tab)
  {
    /*
      jtab is inside execution join nest. We may not refer to outside tables,
      except the const tables.
    */
    table_map local_tables= jtab->emb_sj_nest->nested_join->used_tables |
                            jtab->join->const_table_map |
                            OUTER_REF_TABLE_BIT;
    return !MY_TEST(used_tables & ~local_tables);
  }

  /* 
    If we got here then jtab is at top level. 
     - all other tables at top level are accessible,
     - tables in join nests are accessible too, because all their columns that 
       are needed at top level will be unpacked when scanning the
       materialization table.
  */
  return TRUE;
}

Project: server

void JOIN::drop_unused_derived_keys()
{
  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))
  {
    
    TABLE *tmp_tbl= tab->table;
    if (!tmp_tbl)
      continue;
    if (!tmp_tbl->pos_in_table_list->is_materialized_derived())
      continue;
    if (tmp_tbl->max_keys > 1 && !tab->is_ref_for_hash_join())
      tmp_tbl->use_index(tab->ref.key);
    if (tmp_tbl->s->keys)
    {
      if (tab->ref.key >= 0 && tab->ref.key < MAX_KEY)
        tab->ref.key= 0;
      else
        tmp_tbl->s->keys= 0;
    }
    tab->keys= (key_map) (tmp_tbl->s->keys ? 1 : 0);
  }
}

Project: server

test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
                         key_map usable_keys,  int ref_key,
                         ha_rows select_limit_arg,
                         int *new_key, int *new_key_direction,
                         ha_rows *new_select_limit, uint *new_used_key_parts,
                         uint *saved_best_key_parts)
{
  DBUG_ENTER("test_if_cheaper_ordering");
  /*
    Check whether there is an index compatible with the given order
    usage of which is cheaper than usage of the ref_key index (ref_key>=0)
    or a table scan.
    It may be the case if ORDER/GROUP BY is used with LIMIT.
  */
  ha_rows best_select_limit= HA_POS_ERROR;
  JOIN *join= tab ? tab->join : NULL;
  uint nr;
  key_map keys;
  uint best_key_parts= 0;
  int best_key_direction= 0;
  ha_rows best_records= 0;
  double read_time;
  int best_key= -1;
  bool is_best_covering= FALSE;
  double fanout= 1;
  ha_rows table_records= table->stat_records();
  bool group= join && join->group && order == join->group_list;
  ha_rows refkey_rows_estimate= table->quick_condition_rows;
  const bool has_limit= (select_limit_arg != HA_POS_ERROR);

  /*
    If not used with LIMIT, only use keys if the whole query can be
    resolved with a key;  This is because filesort() is usually faster than
    retrieving all rows through an index.
  */
  if (select_limit_arg >= table_records)
  {
    keys= *table->file->keys_to_use_for_scanning();
    keys.merge(table->covering_keys);

    /*
      We are adding here also the index specified in FORCE INDEX clause, 
      if any.
      This is to allow users to use index in ORDER BY.
    */
    if (table->force_index) 
      keys.merge(group ? table->keys_in_use_for_group_by :
                         table->keys_in_use_for_order_by);
    keys.intersect(usable_keys);
  }
  else
    keys= usable_keys;

  if (join)
  {
    uint tablenr= (uint)(tab - join->join_tab);
    read_time= join->best_positions[tablenr].read_time;
    for (uint i= tablenr+1; i < join->table_count; i++)
      fanout*= join->best_positions[i].records_read; // fanout is always >= 1
  }
  else
    read_time= table->file->scan_time();
  
  /*
    TODO: add cost of sorting here.
  */
  read_time += COST_EPS;

  /*
    Calculate the selectivity of the ref_key for REF_ACCESS. For
    RANGE_ACCESS we use table->quick_condition_rows.
  */
  if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
  {
    /*
      If ref access uses keypart=const for all its key parts,
      and quick select uses the same # of key parts, then they are equivalent.
      Reuse #rows estimate from quick select as it is more precise.
    */
    if (tab->ref.const_ref_part_map ==
        make_prev_keypart_map(tab->ref.key_parts) &&
        table->quick_keys.is_set(ref_key) &&
        table->quick_key_parts[ref_key] == tab->ref.key_parts)
      refkey_rows_estimate= table->quick_rows[ref_key];
    else
    {
      const KEY *ref_keyinfo= table->key_info + ref_key;
      refkey_rows_estimate= ref_keyinfo->rec_per_key[tab->ref.key_parts - 1];
    }
    set_if_bigger(refkey_rows_estimate, 1);
  }

  for (nr=0; nr < table->s->keys ; nr++)
  {
    int direction;
    ha_rows select_limit= select_limit_arg;
    uint used_key_parts= 0;

    if (keys.is_set(nr) &&
        (direction= test_if_order_by_key(join, order, table, nr,
                                         &used_key_parts)))
    {
      /*
        At this point we are sure that ref_key is a non-ordering
        key (where "ordering key" is a key that will return rows
        in the order required by ORDER BY).
      */
      DBUG_ASSERT (ref_key != (int) nr);

      bool is_covering= (table->covering_keys.is_set(nr) ||
                         (table->file->index_flags(nr, 0, 1) &
                          HA_CLUSTERED_INDEX));
      /* 
        Don't use an index scan with ORDER BY without limit.
        For GROUP BY without limit always use index scan
        if there is a suitable index. 
        Why we hold to this asymmetry hardly can be explained
        rationally. It's easy to demonstrate that using
        temporary table + filesort could be cheaper for grouping
        queries too.
      */ 
      if (is_covering ||
          select_limit != HA_POS_ERROR || 
          (ref_key < 0 && (group || table->force_index)))
      { 
        double rec_per_key;
        double index_scan_time;
        KEY *keyinfo= table->key_info+nr;
        if (select_limit == HA_POS_ERROR)
          select_limit= table_records;
        if (group)
        {
          /* 
            Used_key_parts can be larger than keyinfo->user_defined_key_parts
            when using a secondary index clustered with a primary 
            key (e.g. as in Innodb). 
            See Bug #28591 for details.
          */  
          uint used_index_parts= keyinfo->user_defined_key_parts;
          uint used_pk_parts= 0;
          if (used_key_parts > used_index_parts)
            used_pk_parts= used_key_parts-used_index_parts;
          rec_per_key= used_key_parts ?
	               keyinfo->actual_rec_per_key(used_key_parts-1) : 1;
          /* Take into account the selectivity of the used pk prefix */
          if (used_pk_parts)
	  {
            KEY *pkinfo=tab->table->key_info+table->s->primary_key;
            /*
              If the values of of records per key for the prefixes
              of the primary key are considered unknown we assume
              they are equal to 1.
	    */
            if (used_key_parts == pkinfo->user_defined_key_parts ||
                pkinfo->rec_per_key[0] == 0)
              rec_per_key= 1;                 
            if (rec_per_key > 1)
	    {
              rec_per_key*= pkinfo->actual_rec_per_key(used_pk_parts-1);
              rec_per_key/= pkinfo->actual_rec_per_key(0);
              /* 
                The value of rec_per_key for the extended key has
                to be adjusted accordingly if some components of
                the secondary key are included in the primary key.
	      */
               for(uint i= 1; i < used_pk_parts; i++)
	      {
	        if (pkinfo->key_part[i].field->key_start.is_set(nr))
	        {
                  /* 
                    We presume here that for any index rec_per_key[i] != 0
                    if rec_per_key[0] != 0.
	          */
                  DBUG_ASSERT(pkinfo->actual_rec_per_key(i));
                  rec_per_key*= pkinfo->actual_rec_per_key(i-1);
                  rec_per_key/= pkinfo->actual_rec_per_key(i);
                }
	      }
            }    
          }
          set_if_bigger(rec_per_key, 1);
          /*
            With a grouping query each group containing on average
            rec_per_key records produces only one row that will
            be included into the result set.
          */  
          if (select_limit > table_records/rec_per_key)
            select_limit= table_records;
          else
            select_limit= (ha_rows) (select_limit*rec_per_key);
        } /* group */

        /* 
          If tab=tk is not the last joined table tn then to get first
          L records from the result set we can expect to retrieve
          only L/fanout(tk,tn) where fanout(tk,tn) says how many
          rows in the record set on average will match each row tk.
          Usually our estimates for fanouts are too pessimistic.
          So the estimate for L/fanout(tk,tn) will be too optimistic
          and as result we'll choose an index scan when using ref/range
          access + filesort will be cheaper.
        */
        select_limit= (ha_rows) (select_limit < fanout ?
                                 1 : select_limit/fanout);
        /*
          We assume that each of the tested indexes is not correlated
          with ref_key. Thus, to select first N records we have to scan
          N/selectivity(ref_key) index entries. 
          selectivity(ref_key) = #scanned_records/#table_records =
          refkey_rows_estimate/table_records.
          In any case we can't select more than #table_records.
          N/(refkey_rows_estimate/table_records) > table_records
          <=> N > refkey_rows_estimate.
         */
        if (select_limit > refkey_rows_estimate)
          select_limit= table_records;
        else
          select_limit= (ha_rows) (select_limit *
                                   (double) table_records /
                                    refkey_rows_estimate);
        rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1);
        set_if_bigger(rec_per_key, 1);
        /*
          Here we take into account the fact that rows are
          accessed in sequences rec_per_key records in each.
          Rows in such a sequence are supposed to be ordered
          by rowid/primary key. When reading the data
          in a sequence we'll touch not more pages than the
          table file contains.
          TODO. Use the formula for a disk sweep sequential access
          to calculate the cost of accessing data rows for one 
          index entry.
        */
        index_scan_time= select_limit/rec_per_key *
                         MY_MIN(rec_per_key, table->file->scan_time());
        double range_scan_time;
        if (get_range_limit_read_cost(tab, table, nr, select_limit, 
                                       &range_scan_time))
        {
          if (range_scan_time < index_scan_time)
            index_scan_time= range_scan_time;
        }

        if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
            index_scan_time < read_time)
        {
          ha_rows quick_records= table_records;
          ha_rows refkey_select_limit= (ref_key >= 0 &&
                                        !is_hash_join_key_no(ref_key) &&
                                        table->covering_keys.is_set(ref_key)) ?
                                        refkey_rows_estimate :
                                        HA_POS_ERROR;
          if ((is_best_covering && !is_covering) ||
              (is_covering && refkey_select_limit < select_limit))
            continue;
          if (table->quick_keys.is_set(nr))
            quick_records= table->quick_rows[nr];
          if (best_key < 0 ||
              (select_limit <= MY_MIN(quick_records,best_records) ?
               keyinfo->user_defined_key_parts < best_key_parts :
               quick_records < best_records) ||
              (!is_best_covering && is_covering))
          {
            best_key= nr;
            best_key_parts= keyinfo->user_defined_key_parts;
            if (saved_best_key_parts)
              *saved_best_key_parts= used_key_parts;
            best_records= quick_records;
            is_best_covering= is_covering;
            best_key_direction= direction; 
            best_select_limit= select_limit;
          }
        }   
      }      
    }
  }

  if (best_key < 0 || best_key == ref_key)
    DBUG_RETURN(FALSE);
  
  *new_key= best_key;
  *new_key_direction= best_key_direction;
  *new_select_limit= has_limit ? best_select_limit : table_records;
  if (new_used_key_parts != NULL)
    *new_used_key_parts= best_key_parts;

  DBUG_RETURN(TRUE);
}

Project: server

bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
                                  List<Item> *eq_list)
{
  Item *left_item= arguments()[0];
  Item *right_item= arguments()[1];

  if (left_item->type() == Item::ROW_ITEM &&
      right_item->type() == Item::ROW_ITEM)
  {
    /*
      Item_splocal::type() for ROW variables returns Item::ROW_ITEM.
      Distinguish ROW-type Item_splocal from Item_row.
      Example query:
        SELECT 1 FROM DUAL WHERE row_sp_variable=ROW(100,200);
    */
    if (left_item->get_item_splocal() ||
        right_item->get_item_splocal())
      return false;
    return check_row_equality(thd,
                              cmp.subcomparators(),
                              (Item_row *) left_item,
                              (Item_row *) right_item,
                              cond_equal, eq_list);
  }
  return check_simple_equality(thd,
                               Context(ANY_SUBST,
                                       compare_type_handler(),
                                       compare_collation()),
                               left_item, right_item, cond_equal);
}

Project: server

add_key_equal_fields(JOIN *join, KEY_FIELD **key_fields, uint and_level,
                     Item_bool_func *cond, Item *field_item,
                     bool eq_func, Item **val,
                     uint num_values, table_map usable_tables,
                     SARGABLE_PARAM **sargables, uint row_col_no= 0)
{
  Field *field= ((Item_field *) (field_item->real_item()))->field;
  add_key_field(join, key_fields, and_level, cond, field,
                eq_func, val, num_values, usable_tables, sargables,
                row_col_no);
  Item_equal *item_equal= field_item->get_item_equal();
  if (item_equal)
  { 
    /*
      Add to the set of possible key values every substitution of
      the field for an equal field included into item_equal
    */
    Item_equal_fields_iterator it(*item_equal);
    while (it++)
    {
      Field *equal_field= it.get_curr_field();
      if (!field->eq(equal_field))
      {
        add_key_field(join, key_fields, and_level, cond, equal_field,
                      eq_func, val, num_values, usable_tables,
                      sargables, row_col_no);
      }
    }
  }
}

Project: server

evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab)
{
  /*
    The table join_tab is the first inner table of a outer join operation
    and no matches has been found for the current outer row.
  */
  JOIN_TAB *last_inner_tab= join_tab->last_inner;
  /* Cache variables for faster loop */
  COND *select_cond;
  for ( ; join_tab <= last_inner_tab ; join_tab++)
  {
    /* Change the the values of guard predicate variables. */
    join_tab->found= 1;
    join_tab->not_null_compl= 0;
    /* The outer row is complemented by nulls for each inner tables */
    restore_record(join_tab->table,s->default_values);  // Make empty record
    mark_as_null_row(join_tab->table);       // For group by without error
    select_cond= join_tab->select_cond;
    /* Check all attached conditions for inner table rows. */
    if (select_cond && !select_cond->val_int())
      return NESTED_LOOP_OK;
  }
  join_tab--;
  /*
    The row complemented by nulls might be the first row
    of embedding outer joins.
    If so, perform the same actions as in the code
    for the first regular outer join row above.
  */
  for ( ; ; )
  {
    JOIN_TAB *first_unmatched= join_tab->first_unmatched;
    if ((first_unmatched= first_unmatched->first_upper) &&
        first_unmatched->last_inner != join_tab)
      first_unmatched= 0;
    join_tab->first_unmatched= first_unmatched;
    if (!first_unmatched)
      break;
    first_unmatched->found= 1;
    for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
    {
      if (tab->select_cond && !tab->select_cond->val_int())
      {
        join->return_tab= tab;
        return NESTED_LOOP_OK;
      }
    }
  }
  /*
    The row complemented by nulls satisfies all conditions
    attached to inner tables.
  */
  if (join_tab->check_weed_out_table)
  {
    int res= join_tab->check_weed_out_table->sj_weedout_check_row(join->thd);
    if (res == -1)
      return NESTED_LOOP_ERROR;
    else if (res == 1)
      return NESTED_LOOP_OK;
  }
  else if (join_tab->do_firstmatch)
  {
    /* 
      We should return to the join_tab->do_firstmatch after we have 
      enumerated all the suffixes for current prefix row combination
    */
    if (join_tab->do_firstmatch < join->return_tab)
      join->return_tab= join_tab->do_firstmatch;
  }

  /*
    Send the row complemented by nulls to be joined with the
    remaining tables.
  */
  return (*join_tab->next_select)(join, join_tab+1, 0);
}

Project: server

void free_underlaid_joins(THD *thd, SELECT_LEX *select)
{
  for (SELECT_LEX_UNIT *unit= select->first_inner_unit();
       unit;
       unit= unit->next_unit())
    unit->cleanup();
}

Project: server

static TABLE_LIST* get_emb_subq(JOIN_TAB *tab)
{
  TABLE_LIST *tlist= tab->table->pos_in_table_list;
  if (tlist->jtbm_subselect)
    return tlist;
  TABLE_LIST *embedding= tlist->embedding;
  if (!embedding || !embedding->sj_subq_pred)
    return NULL;
  return embedding;
}

Project: server

static int compare_fields_by_table_order(Item *field1,
                                         Item *field2,
                                         void *table_join_idx)
{
  int cmp= 0;
  bool outer_ref= 0;
  Item *field1_real= field1->real_item();
  Item *field2_real= field2->real_item();

  if (field1->const_item() || field1_real->const_item())
    return -1;
  if (field2->const_item() || field2_real->const_item())
    return 1;
  Item_field *f1= (Item_field *) field1_real;
  Item_field *f2= (Item_field *) field2_real;
  if (f1->used_tables() & OUTER_REF_TABLE_BIT)
  {
    outer_ref= 1;
    cmp= -1;
  }
  if (f2->used_tables() & OUTER_REF_TABLE_BIT)
  {  
    outer_ref= 1;
    cmp++;
  }
  if (outer_ref)
    return cmp;
  JOIN_TAB **idx= (JOIN_TAB **) table_join_idx;
  
  JOIN_TAB *tab1= idx[f1->field->table->tablenr];
  JOIN_TAB *tab2= idx[f2->field->table->tablenr];
  
  /* 
    if one of the table is inside a merged SJM nest and another one isn't,
    compare SJM bush roots of the tables.
  */
  if (tab1->bush_root_tab != tab2->bush_root_tab)
  {
    if (tab1->bush_root_tab)
      tab1= tab1->bush_root_tab;

    if (tab2->bush_root_tab)
      tab2= tab2->bush_root_tab;
  }
  
  cmp= (int)(tab1 - tab2);

  if (!cmp)
  {
    /* Fields f1, f2 belong to the same table */

    JOIN_TAB *tab= idx[f1->field->table->tablenr];
    uint keyno= MAX_KEY;
    if (tab->ref.key_parts)
      keyno= tab->ref.key;
    else if (tab->select && tab->select->quick)
       keyno = tab->select->quick->index;
    if (keyno != MAX_KEY)
    {
      if (f1->field->part_of_key.is_set(keyno))
        cmp= -1;
      if (f2->field->part_of_key.is_set(keyno))
        cmp++;
      /*
        Here:
        if both f1, f2 are components of the key tab->ref.key then cmp==0,
        if only f1 is a component of the key then cmp==-1 (f1 is better),
        if only f2 is a component of the key then cmp==1, (f2 is better),
        if none of f1,f1 is component of the key cmp==0.
      */  
      if (!cmp)
      {
        KEY *key_info= tab->table->key_info + keyno;
        for (uint i= 0; i < key_info->user_defined_key_parts; i++)
	{
          Field *fld= key_info->key_part[i].field;
          if (fld->eq(f1->field))
	  {
	    cmp= -1; // f1 is better
            break;
          }
          if (fld->eq(f2->field))
	  {
	    cmp= 1;  // f2 is better
            break;
          }
        }
      }              
    }              
    if (!cmp)   
      cmp= f1->field->field_index-f2->field->field_index;
  }
  return cmp < 0 ? -1 : (cmp ? 1 : 0);
}

Project: server

bool Virtual_tmp_table::open()
{
  // Make sure that we added all the fields we planned to:
  DBUG_ASSERT(s->fields == m_alloced_field_count);
  field[s->fields]= NULL;            // mark the end of the list
  s->blob_field[s->blob_fields]= 0;  // mark the end of the list

  uint null_pack_length= (s->null_fields + 7) / 8; // NULL-bit array length
  s->reclength+= null_pack_length;
  s->rec_buff_length= ALIGN_SIZE(s->reclength + 1);
  if (!(record[0]= (uchar*) in_use->alloc(s->rec_buff_length)))
    return true;
  if (null_pack_length)
  {
    null_flags= (uchar*) record[0];
    s->null_bytes= s->null_bytes_for_compare= null_pack_length;
  }
  setup_field_pointers();
  return false;
}

Project: server

find_field_in_item_list (Field *field, void *data)
{
  List<Item> *fields= (List<Item> *) data;
  bool part_found= 0;
  List_iterator<Item> li(*fields);
  Item *item;

  while ((item= li++))
  {
    if (item->real_item()->type() == Item::FIELD_ITEM &&
	((Item_field*) (item->real_item()))->field->eq(field))
    {
      part_found= 1;
      break;
    }
  }
  return part_found;
}

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->type() == MYSQL_TYPE_DATE) ||
         (field->type() == MYSQL_TYPE_DATETIME)) &&
         (field->flags & NOT_NULL_FLAG))
    {
      /* 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_int(thd, (longlong) 0, 1);
      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->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

create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array,
                      ORDER *order_list, List<Item> &fields,
                      List<Item> &all_fields,
		      bool *all_order_by_fields_used)
{
  List_iterator<Item> li(fields);
  Item *item;
  Ref_ptr_array orig_ref_pointer_array= ref_pointer_array;
  ORDER *order,*group,**prev;
  uint idx= 0;

  *all_order_by_fields_used= 1;
  while ((item=li++))
    item->marker=0;			/* Marker that field is not used */

  prev= &group;  group=0;
  for (order=order_list ; order; order=order->next)
  {
    if (order->in_field_list)
    {
      ORDER *ord=(ORDER*) thd->memdup((char*) order,sizeof(ORDER));
      if (!ord)
	return 0;
      *prev=ord;
      prev= &ord->next;
      (*ord->item)->marker=1;
    }
    else
      *all_order_by_fields_used= 0;
  }

  li.rewind();
  while ((item=li++))
  {
    if (!item->const_item() && !item->with_sum_func && !item->marker)
    {
      /* 
        Don't put duplicate columns from the SELECT list into the 
        GROUP BY list.
      */
      ORDER *ord_iter;
      for (ord_iter= group; ord_iter; ord_iter= ord_iter->next)
        if ((*ord_iter->item)->eq(item, 1))
          goto next_item;
      
      ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER));
      if (!ord)
	return 0;

      if (item->type() == Item::FIELD_ITEM &&
          item->field_type() == MYSQL_TYPE_BIT)
      {
        /*
          Because HEAP tables can't index BIT fields we need to use an
          additional hidden field for grouping because later it will be
          converted to a LONG field. Original field will remain of the
          BIT type and will be returned [el]client.
        */
        Item_field *new_item= new (thd->mem_root) Item_field(thd, (Item_field*)item);
        if (!new_item)
          return 0;
        int el= all_fields.elements;
        orig_ref_pointer_array[el]= new_item;
        all_fields.push_front(new_item, thd->mem_root);
        ord->item=&orig_ref_pointer_array[el]; 
     }
      else
      {
        /*
          We have here only field_list (not all_field_list), so we can use
          simple indexing of ref_pointer_array (order in the array and in the
          list are same)
        */
        ord->item= &ref_pointer_array[idx];
      }
      ord->direction= ORDER::ORDER_ASC;
      *prev=ord;
      prev= &ord->next;
    }
next_item:
    idx++;
  }
  *prev=0;
  return group;
}

Project: server

Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
                           Item_equal *item_equal)
{
  List<Item> eq_list;
  Item_func_eq *eq_item= 0;
  if (((Item *) item_equal)->const_item() && !item_equal->val_int())
    return new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
  Item *item_const= item_equal->get_const();
  Item_equal_fields_iterator it(*item_equal);
  Item *head;
  TABLE_LIST *current_sjm= NULL;
  Item *current_sjm_head= NULL;

  DBUG_ASSERT(!cond ||
              cond->type() == Item::INT_ITEM ||
              (cond->type() == Item::FUNC_ITEM &&
               ((Item_func *) cond)->functype() == Item_func::EQ_FUNC) ||  
              (cond->type() == Item::COND_ITEM  && 
               ((Item_func *) cond)->functype() == Item_func::COND_AND_FUNC));
       
  /* 
    Pick the "head" item: the constant one or the first in the join order
    (if the first in the join order happends to be inside an SJM nest, that's
    ok, because this is where the value will be unpacked after
    materialization).
  */
  if (item_const)
    head= item_const;
  else
  {
    TABLE_LIST *emb_nest;
    head= item_equal->get_first(NO_PARTICULAR_TAB, NULL);
    it++;
    if ((emb_nest= embedding_sjm(head)))
    {
      current_sjm= emb_nest;
      current_sjm_head= head;
    }
  }

  Item *field_item;
  /*
    For each other item, generate "item=head" equality (except the tables that 
    are within SJ-Materialization nests, for those "head" is defined
    differently)
  */
  while ((field_item= it++))
  {
    Item_equal *upper= field_item->find_item_equal(upper_levels);
    Item *item= field_item;
    TABLE_LIST *field_sjm= embedding_sjm(field_item);
    if (!field_sjm)
    { 
      current_sjm= NULL;
      current_sjm_head= NULL;
    }      

    /* 
      Check if "field_item=head" equality is already guaranteed to be true 
      on upper AND-levels.
    */
    if (upper)
    {
      TABLE_LIST *native_sjm= embedding_sjm(item_equal->context_field);
      Item *upper_const= upper->get_const();
      if (item_const && upper_const)
      {
        /* 
          Upper item also has "field_item=const".
          Don't produce equality if const is equal to item_const.
        */
        Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, item_const, upper_const);
        func->set_cmp_func();
        func->quick_fix_field();
        if (func->val_int())
          item= 0;
      }
      else
      {
        Item_equal_fields_iterator li(*item_equal);
        while ((item= li++) != field_item)
        {
          if (embedding_sjm(item) == field_sjm && 
              item->find_item_equal(upper_levels) == upper)
            break;
        }
      }
      if (embedding_sjm(field_item) != native_sjm)
        item= NULL; /* Don't produce equality */
    }
    
    bool produce_equality= MY_TEST(item == field_item);
    if (!item_const && field_sjm && field_sjm != current_sjm)
    {
      /* Entering an SJM nest */
      current_sjm_head= field_item;
      if (!field_sjm->sj_mat_info->is_sj_scan)
        produce_equality= FALSE;
    }

    if (produce_equality)
    {
      if (eq_item && eq_list.push_back(eq_item, thd->mem_root))
        return 0;
      
      /*
        If we're inside an SJM-nest (current_sjm!=NULL), and the multi-equality
        doesn't include a constant, we should produce equality with the first
        of the equal items in this SJM (except for the first element inside the
        SJM. For that, we produce the equality with the "head" item).

        In other cases, get the "head" item, which is either first of the
        equals on top level, or the constant.
      */
      Item *head_item= (!item_const && current_sjm && 
                        current_sjm_head != field_item) ? current_sjm_head: head;
      Item *head_real_item=  head_item->real_item();
      if (head_real_item->type() == Item::FIELD_ITEM)
        head_item= head_real_item;
      
      eq_item= new (thd->mem_root) Item_func_eq(thd, field_item->real_item(), head_item);

      if (!eq_item || eq_item->set_cmp_func())
        return 0;
      eq_item->quick_fix_field();
    }
    current_sjm= field_sjm;
  }

  /*
    We have produced zero, one, or more pair-wise equalities eq_i. We want to
    return an expression in form:

      cond AND eq_1 AND eq_2 AND eq_3 AND ...
    
    'cond' is a parameter for this function, which may be NULL, an Item_int(1),
    or an Item_func_eq or an Item_cond_and.

    We want to return a well-formed condition: no nested Item_cond_and objects,
    or Item_cond_and with a single child:
    - if 'cond' is an Item_cond_and, we add eq_i as its tail
    - if 'cond' is Item_int(1), we return eq_i
    - otherwise, we create our own Item_cond_and and put 'cond' at the front of
      it.
    - if we have only one condition to return, we don't create an Item_cond_and
  */

  if (eq_item && eq_list.push_back(eq_item, thd->mem_root))
    return 0;
  COND *res= 0;
  switch (eq_list.elements)
  {
  case 0:
    res= cond ? cond : new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
    break;
  case 1:
    if (!cond || cond->type() ==  Item::INT_ITEM)
      res= eq_item;
    break;
  default:
    break;
  }
  if (!res) 
  {
    if (cond)
    {
      if (cond->type() == Item::COND_ITEM)
      {
        res= cond;
        ((Item_cond *) res)->add_at_end(&eq_list);
      }
      else if (eq_list.push_front(cond, thd->mem_root))
        return 0;
    }
  }  
  if (!res)
    res= new (thd->mem_root) Item_cond_and(thd, eq_list);
  if (res)
  {
    res->quick_fix_field();
    res->update_used_tables();
  }

  return res;
}

Project: server

part_of_refkey(TABLE *table,Field *field)
{
  JOIN_TAB *join_tab= table->reginfo.join_tab;
  if (!join_tab)
    return (Item*) 0;             // field from outer non-select (UPDATE,...)

  uint ref_parts= join_tab->ref.key_parts;
  if (ref_parts) /* if it's ref/eq_ref/ref_or_null */
  {
    uint key= join_tab->ref.key;
    KEY *key_info= join_tab->get_keyinfo_by_key_no(key);
    KEY_PART_INFO *key_part= key_info->key_part;

    for (uint part=0 ; part < ref_parts ; part++,key_part++)
    {
      if (field->eq(key_part->field))
      {
        /*
          Found the field in the key. Check that 
           1. ref_or_null doesn't alternate this component between a value and
              a NULL
           2. index fully covers the key
        */
        if (part != join_tab->ref.null_ref_part &&            // (1)
            !(key_part->key_part_flag & HA_PART_KEY_SEG))     // (2)
        {
          return join_tab->ref.items[part];
        }
        break;
      }
    }
  }
  return (Item*) 0;
}

Project: server

join_read_key_unlock_row(st_join_table *tab)
{
  DBUG_ASSERT(tab->ref.use_count);
  if (tab->ref.use_count)
    tab->ref.use_count--;
}

Project: server

static COND *build_equal_items(JOIN *join, COND *cond,
                               COND_EQUAL *inherited,
                               List<TABLE_LIST> *join_list,
                               bool ignore_on_conds,
                               COND_EQUAL **cond_equal_ref,
                               bool link_equal_fields)
{
  THD *thd= join->thd;

  *cond_equal_ref= NULL;

  if (cond) 
  {
    cond= cond->build_equal_items(thd, inherited, link_equal_fields,
                                  cond_equal_ref);
    if (*cond_equal_ref)
    {
      (*cond_equal_ref)->upper_levels= inherited;
      inherited= *cond_equal_ref;
    }
  }

  if (join_list && !ignore_on_conds)
  {
    TABLE_LIST *table;
    List_iterator<TABLE_LIST> li(*join_list);

    while ((table= li++))
    {
      if (table->on_expr)
      {
        List<TABLE_LIST> *nested_join_list= table->nested_join ?
          &table->nested_join->join_list : NULL;
        /*
          We can modify table->on_expr because its old value will
          be restored before re-execution of PS/SP.
        */
        table->on_expr= build_equal_items(join, table->on_expr, inherited,
                                          nested_join_list, ignore_on_conds,
                                          &table->cond_equal);
      }
    }
  }

  return cond;
}

Project: server

JOIN::destroy()
{
  DBUG_ENTER("JOIN::destroy");
  select_lex->join= 0;

  cond_equal= 0;
  having_equal= 0;

  cleanup(1);

  if (join_tab)
  {
    for (JOIN_TAB *tab= first_linear_tab(this, WITH_BUSH_ROOTS,
                                         WITH_CONST_TABLES);
         tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
    {
      if (tab->aggr)
      {
        free_tmp_table(thd, tab->table);
        delete tab->tmp_table_param;
        tab->tmp_table_param= NULL;
        tab->aggr= NULL;
      }
      tab->table= NULL;
    }
  }

  /* Cleanup items referencing temporary table columns */
  cleanup_item_list(tmp_all_fields1);
  cleanup_item_list(tmp_all_fields3);
  destroy_sj_tmp_tables(this);
  delete_dynamic(&keyuse);
  if (save_qep)
    delete(save_qep);
  if (ext_keyuses_for_splitting)
    delete(ext_keyuses_for_splitting);
  delete procedure;
  DBUG_RETURN(error);
}

Project: server

merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
		 uint and_level)
{
  if (start == new_fields)
    return start;				// Impossible or
  if (new_fields == end)
    return start;				// No new fields, skip all

  KEY_FIELD *first_free=new_fields;

  /* Mark all found fields in old array */
  for (; new_fields != end ; new_fields++)
  {
    for (KEY_FIELD *old=start ; old != first_free ; old++)
    {
      if (old->field == new_fields->field)
      {
        /*
          NOTE: below const_item() call really works as "!used_tables()", i.e.
          it can return FALSE where it is feasible to make it return TRUE.
          
          The cause is as follows: Some of the tables are already known to be
          const tables (the detection code is in make_join_statistics(),
          above the update_ref_and_keys() call), but we didn't propagate 
          information about this: TABLE::const_table is not set to TRUE, and
          Item::update_used_tables() hasn't been called for each item.
          The result of this is that we're missing some 'ref' accesses.
          TODO: OptimizerTeam: Fix this
        */
	if (!new_fields->val->const_item())
	{
	  /*
	    If the value matches, we can use the key reference.
	    If not, we keep it until we have examined all new values
	  */
	  if (old->val->eq(new_fields->val, old->field->binary()))
	  {
	    old->level= and_level;
	    old->optimize= ((old->optimize & new_fields->optimize &
			     KEY_OPTIMIZE_EXISTS) |
			    ((old->optimize | new_fields->optimize) &
			     KEY_OPTIMIZE_REF_OR_NULL));
            old->null_rejecting= (old->null_rejecting &&
                                  new_fields->null_rejecting);
	  }
	}
	else if (old->eq_func && new_fields->eq_func &&
                 old->val->eq_by_collation(new_fields->val, 
                                           old->field->binary(),
                                           old->field->charset()))

	{
	  old->level= and_level;
	  old->optimize= ((old->optimize & new_fields->optimize &
			   KEY_OPTIMIZE_EXISTS) |
			  ((old->optimize | new_fields->optimize) &
			   KEY_OPTIMIZE_REF_OR_NULL));
          old->null_rejecting= (old->null_rejecting &&
                                new_fields->null_rejecting);
	}
	else if (old->eq_func && new_fields->eq_func &&
		 ((old->val->const_item() && !old->val->is_expensive() &&
                   old->val->is_null()) ||
                  (!new_fields->val->is_expensive() &&
                   new_fields->val->is_null())))
	{
	  /* field = expression OR field IS NULL */
	  old->level= and_level;
          if (old->field->maybe_null())
	  {
	    old->optimize= KEY_OPTIMIZE_REF_OR_NULL;
            /* The referred expression can be NULL: */ 
            old->null_rejecting= 0;
	  }
	  /*
            Remember the NOT NULL value unless the value does not depend
            on other tables.
          */
	  if (!old->val->used_tables() && !old->val->is_expensive() &&
              old->val->is_null())
	    old->val= new_fields->val;
	}
	else
	{
	  /*
	    We are comparing two different const.  In this case we can't
	    use a key-lookup on this so it's better to remove the value
	    and let the range optimzier handle it
	  */
	  if (old == --first_free)		// If last item
	    break;
	  *old= *first_free;			// Remove old value
	  old--;				// Retry this value
	}
      }
    }
  }
  /* Remove all not used items */
  for (KEY_FIELD *old=start ; old != first_free ;)
  {
    if (old->level != and_level)
    {						// Not used in all levels
      if (old == --first_free)
	break;
      *old= *first_free;			// Remove old value
      continue;
    }
    old++;
  }
  return first_free;
}

Project: server

double JOIN_TAB::scan_time()
{
  double res;
  if (table->is_created())
  {
    if (table->is_filled_at_execution())
    {
      get_delayed_table_estimates(table, &records, &read_time,
                                    &startup_cost);
      found_records= records;
      table->quick_condition_rows= records;
    }
    else
    {
      found_records= records= table->stat_records();
      read_time= table->file->scan_time();
      /*
        table->quick_condition_rows has already been set to
        table->file->stats.records
      */
    }
    res= read_time;
  }
  else
  {
    found_records= records=table->stat_records();
    read_time= found_records ? (double)found_records: 10.0;// TODO:fix this stub
    res= read_time;
  }
  return res;
}

Project: server

static uint reset_nj_counters(JOIN *join, List<TABLE_LIST> *join_list)
{
  List_iterator<TABLE_LIST> li(*join_list);
  TABLE_LIST *table;
  DBUG_ENTER("reset_nj_counters");
  uint n=0;
  while ((table= li++))
  {
    NESTED_JOIN *nested_join;
    bool is_eliminated_nest= FALSE;
    if ((nested_join= table->nested_join))
    {
      nested_join->counter= 0;
      nested_join->n_tables= reset_nj_counters(join, &nested_join->join_list);
      if (!nested_join->n_tables)
        is_eliminated_nest= TRUE;
    }
    const table_map removed_tables= join->eliminated_tables |
                                    join->const_table_map;

    if ((table->nested_join && !is_eliminated_nest) ||
        (!table->nested_join && (table->table->map & ~removed_tables)))
      n++;
  }
  DBUG_RETURN(n);
}

Project: server

do_select(JOIN *join, Procedure *procedure)
{
  int rc= 0;
  enum_nested_loop_state error= NESTED_LOOP_OK;
  DBUG_ENTER("do_select");

  if (join->pushdown_query)
  {
    /* Select fields are in the temporary table */
    join->fields= &join->tmp_fields_list1;
    /* Setup HAVING to work with fields in temporary table */
    join->set_items_ref_array(join->items1);
    /* The storage engine will take care of the group by query result */
    int res= join->pushdown_query->execute(join);

    if (res)
      DBUG_RETURN(res);

    if (join->pushdown_query->store_data_in_temp_table)
    {
      JOIN_TAB *last_tab= join->join_tab + join->table_count -
                          join->exec_join_tab_cnt();      
      last_tab->next_select= end_send;

      enum_nested_loop_state state= last_tab->aggr->end_send();
      if (state >= NESTED_LOOP_OK)
        state= sub_select(join, last_tab, true);

      if (state < NESTED_LOOP_OK)
        res= 1;

      if (join->result->send_eof())
        res= 1;
    }
    DBUG_RETURN(res);
  }
  
  join->procedure= procedure;
  join->duplicate_rows= join->send_records=0;
  if (join->only_const_tables() && !join->need_tmp)
  {
    Next_select_func end_select= setup_end_select_func(join, NULL);

    /*
      HAVING will be checked after processing aggregate functions,
      But WHERE should checked here (we alredy have read tables).
      Notice that make_join_select() splits all conditions in this case
      into two groups exec_const_cond and outer_ref_cond.
      If join->table_count == join->const_tables then it is
      sufficient to check only the condition pseudo_bits_cond.
    */
    DBUG_ASSERT(join->outer_ref_cond == NULL);
    if (!join->pseudo_bits_cond || join->pseudo_bits_cond->val_int())
    {
      // HAVING will be checked by end_select
      error= (*end_select)(join, 0, 0);
      if (error >= NESTED_LOOP_OK)
	error= (*end_select)(join, 0, 1);

      /*
        If we don't go through evaluate_join_record(), do the counting
        here.  join->send_records is increased on success in end_send(),
        so we don't touch it here.
      */
      join->join_examined_rows++;
      DBUG_ASSERT(join->join_examined_rows <= 1);
    }
    else if (join->send_row_on_empty_set())
    {
      table_map cleared_tables= (table_map) 0;
      if (end_select == end_send_group)
      {
        /*
          Was a grouping query but we did not find any rows. In this case
          we clear all tables to get null in any referenced fields,
          like in case of:
          SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL
        */
        clear_tables(join, &cleared_tables);
      }
      if (!join->having || join->having->val_int())
      {
        List<Item> *columns_list= (procedure ? &join->procedure_fields_list :
                                   join->fields);
        rc= join->result->send_data(*columns_list) > 0;
      }
      /*
        We have to remove the null markings from the tables as this table
        may be part of a sub query that is re-evaluated
      */
      if (cleared_tables)
        unclear_tables(join, &cleared_tables);
    }
    /*
      An error can happen when evaluating the conds 
      (the join condition and piece of where clause 
      relevant to this join table).
    */
    if (unlikely(join->thd->is_error()))
      error= NESTED_LOOP_ERROR;
  }
  else
  {
    DBUG_EXECUTE_IF("show_explain_probe_do_select", 
                    if (dbug_user_var_equals_int(join->thd, 
                                                 "show_explain_probe_select_id", 
                                                 join->select_lex->select_number))
                          dbug_serve_apcs(join->thd, 1);
                   );

    JOIN_TAB *join_tab= join->join_tab +
                        (join->tables_list ? join->const_tables : 0);
    if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
      error= NESTED_LOOP_NO_MORE_ROWS;
    else
      error= join->first_select(join,join_tab,0);
    if (error >= NESTED_LOOP_OK && likely(join->thd->killed != ABORT_QUERY))
      error= join->first_select(join,join_tab,1);
  }

  join->thd->limit_found_rows= join->send_records - join->duplicate_rows;

  if (error == NESTED_LOOP_NO_MORE_ROWS ||
      unlikely(join->thd->killed == ABORT_QUERY))
    error= NESTED_LOOP_OK;

  /*
    For "order by with limit", we cannot rely on send_records, but need
    to use the rowcount read originally into the join_tab applying the
    filesort. There cannot be any post-filtering conditions, nor any
    following join_tabs in this case, so this rowcount properly represents
    the correct number of qualifying rows.
  */
  if (join->order)
  {
    // Save # of found records prior to cleanup
    JOIN_TAB *sort_tab;
    JOIN_TAB *join_tab= join->join_tab;
    uint const_tables= join->const_tables;

    // Take record count from first non constant table or from last tmp table
    if (join->aggr_tables > 0)
      sort_tab= join_tab + join->top_join_tab_count + join->aggr_tables - 1;
    else
    {
      DBUG_ASSERT(!join->only_const_tables());
      sort_tab= join_tab + const_tables;
    }
    if (sort_tab->filesort &&
        join->select_options & OPTION_FOUND_ROWS &&
        sort_tab->filesort->sortorder &&
        sort_tab->filesort->limit != HA_POS_ERROR)
    {
      join->thd->limit_found_rows= sort_tab->records;
    }
  }

  {
    /*
      The following will unlock all cursors if the command wasn't an
      update command
    */
    join->join_free();			// Unlock all cursors
  }
  if (error == NESTED_LOOP_OK)
  {
    /*
      Sic: this branch works even if rc != 0, e.g. when
      send_data above returns an error.
    */
    if (unlikely(join->result->send_eof()))
      rc= 1;                                  // Don't send error
    DBUG_PRINT("info",("%ld records output", (long) join->send_records));
  }
  else
    rc= -1;
#ifndef DBUG_OFF
  if (rc)
  {
    DBUG_PRINT("error",("Error: do_select() failed"));
  }
#endif
  rc= join->thd->is_error() ? -1 : rc;
  DBUG_RETURN(rc);
}

Project: server

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

  unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
                                    select_lex->offset_limit->val_uint() : 0);

  first_record= false;
  group_sent= false;
  cleaned= false;

  if (aggr_tables)
  {
    JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
    JOIN_TAB *end_tab= curr_tab + aggr_tables;
    for ( ; curr_tab < end_tab; curr_tab++)
    {
      TABLE *tmp_table= curr_tab->table;
      if (!tmp_table->is_created())
        continue;
      tmp_table->file->extra(HA_EXTRA_RESET_STATE);
      tmp_table->file->ha_delete_all_rows();
    }
  }
  clear_sj_tmp_tables(this);
  if (current_ref_ptrs != items0)
  {
    set_items_ref_array(items0);
    set_group_rpa= false;
  }

  /* need to reset ref access state (see join_read_key) */
  if (join_tab)
  {
    JOIN_TAB *tab;
    for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab;
         tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
    {
      tab->ref.key_err= TRUE;
    }
  }

  /* Reset of sum functions */
  if (sum_funcs)
  {
    Item_sum *func, **func_ptr= sum_funcs;
    while ((func= *(func_ptr++)))
      func->clear();
  }

  if (no_rows_in_result_called)
  {
    /* Reset effect of possible no_rows_in_result() */
    List_iterator_fast<Item> it(fields_list);
    Item *item;
    no_rows_in_result_called= 0;
    while ((item= it++))
      item->restore_to_before_no_rows_in_result();
  }

  if (!(select_options & SELECT_DESCRIBE))
    if (init_ftfuncs(thd, select_lex, MY_TEST(order)))
      DBUG_RETURN(1);

  DBUG_RETURN(0);
}

Project: server

Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab)
{
  TMP_TABLE_PARAM *tmp_tbl= tab ? tab->tmp_table_param : &join->tmp_table_param;

  /* 
     Choose method for presenting result to user. Use end_send_group
     if the query requires grouping (has a GROUP BY clause and/or one or
     more aggregate functions). Use end_send if the query should not
     be grouped.
   */
  if (join->sort_and_group && !tmp_tbl->precomputed_group_by)
  {
    DBUG_PRINT("info",("Using end_send_group"));
    return end_send_group;
  }
  DBUG_PRINT("info",("Using end_send"));
  return end_send;
}

Project: server

bool JOIN::inject_cond_into_where(Item *injected_cond)
{
  Item *where_item= injected_cond;
  List<Item> *and_args= NULL;
  if (conds && conds->type() == Item::COND_ITEM &&
      ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
  {
    and_args= ((Item_cond*) conds)->argument_list();
    if (cond_equal)
      and_args->disjoin((List<Item> *) &cond_equal->current_level);
  }

  where_item= and_items(thd, conds, where_item);
  if (where_item->fix_fields_if_needed(thd, 0))
    return true;
  thd->change_item_tree(&select_lex->where, where_item);
  select_lex->where->top_level_item();
  conds= select_lex->where;

  if (and_args && cond_equal)
  {
    and_args= ((Item_cond*) conds)->argument_list();
    List_iterator<Item_equal> li(cond_equal->current_level);
    Item_equal *elem;
    while ((elem= li++))
    {
      and_args->push_back(elem, thd->mem_root);
    }
  }

  return false;

}

Project: server

bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, 
                               TMP_ENGINE_COLUMNDEF *start_recinfo,
                               TMP_ENGINE_COLUMNDEF **recinfo,
                               ulonglong options)
{
  int error;
  MI_KEYDEF keydef;
  MI_UNIQUEDEF uniquedef;
  TABLE_SHARE *share= table->s;
  DBUG_ENTER("create_internal_tmp_table");

  if (share->keys)
  {						// Get keys for ni_create
    bool using_unique_constraint=0;
    HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root,
                                            sizeof(*seg) * keyinfo->user_defined_key_parts);
    if (!seg)
      goto err;

    bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
    /*
       Note that a similar check is performed during
       subquery_types_allow_materialization. See MDEV-7122 for more details as
       to why. Whenever this changes, it must be updated there as well, for
       all tmp_table engines.
    */
    if (keyinfo->key_length > table->file->max_key_length() ||
	keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
	share->uniques)
    {
      /* Can't create a key; Make a unique constraint instead of a key */
      share->keys=    0;
      share->uniques= 1;
      using_unique_constraint=1;
      bzero((char*) &uniquedef,sizeof(uniquedef));
      uniquedef.keysegs=keyinfo->user_defined_key_parts;
      uniquedef.seg=seg;
      uniquedef.null_are_equal=1;

      /* Create extra column for hash value */
      bzero((uchar*) *recinfo,sizeof(**recinfo));
      (*recinfo)->type= FIELD_CHECK;
      (*recinfo)->length=MI_UNIQUE_HASH_LENGTH;
      (*recinfo)++;
      /* Avoid warnings from valgrind */
      bzero(table->record[0]+ share->reclength, MI_UNIQUE_HASH_LENGTH);
      bzero(share->default_values+ share->reclength, MI_UNIQUE_HASH_LENGTH);
      share->reclength+= MI_UNIQUE_HASH_LENGTH;
    }
    else
    {
      /* Create an unique key */
      bzero((char*) &keydef,sizeof(keydef));
      keydef.flag= ((keyinfo->flags & HA_NOSAME) | HA_BINARY_PACK_KEY |
                    HA_PACK_KEY);
      keydef.keysegs=  keyinfo->user_defined_key_parts;
      keydef.seg= seg;
    }
    for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++)
    {
      Field *field=keyinfo->key_part[i].field;
      seg->flag=     0;
      seg->language= field->charset()->number;
      seg->length=   keyinfo->key_part[i].length;
      seg->start=    keyinfo->key_part[i].offset;
      if (field->flags & BLOB_FLAG)
      {
	seg->type=
	((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
	 HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2);
	seg->bit_start= (uint8)(field->pack_length() - portable_sizeof_char_ptr);
	seg->flag= HA_BLOB_PART;
	seg->length=0;			// Whole blob in unique constraint
      }
      else
      {
	seg->type= keyinfo->key_part[i].type;
        /* Tell handler if it can do suffic space compression */
	if (field->real_type() == MYSQL_TYPE_STRING &&
	    keyinfo->key_part[i].length > 4)
	  seg->flag|= HA_SPACE_PACK;
      }
      if (!(field->flags & NOT_NULL_FLAG))
      {
	seg->null_bit= field->null_bit;
	seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
	/*
	  We are using a GROUP BY on something that contains NULL
	  In this case we have to tell MyISAM that two NULL should
	  on INSERT be regarded at the same value
	*/
	if (!using_unique_constraint)
	  keydef.flag|= HA_NULL_ARE_EQUAL;
      }
    }
  }
  MI_CREATE_INFO create_info;
  bzero((char*) &create_info,sizeof(create_info));
  create_info.data_file_length= table->in_use->variables.tmp_disk_table_size;

  if (unlikely((error= mi_create(share->path.str, share->keys, &keydef,
		                 (uint) (*recinfo-start_recinfo),
                                 start_recinfo,
		                 share->uniques, &uniquedef,
                                 &create_info,
		                 HA_CREATE_TMP_TABLE |
                                 HA_CREATE_INTERNAL_TABLE |
                                 ((share->db_create_options &
                                   HA_OPTION_PACK_RECORD) ?
                                  HA_PACK_RECORD : 0)
                                 ))))
  {
    table->file->print_error(error,MYF(0));	/* purecov: inspected */
    table->db_stat=0;
    goto err;
  }
  table->in_use->inc_status_created_tmp_disk_tables();
  table->in_use->inc_status_created_tmp_tables();
  share->db_record_offset= 1;
  table->set_created();
  DBUG_RETURN(0);
 err:
  DBUG_RETURN(1);
}

Project: server

uint get_next_field_for_derived_key_simple(uchar *arg)
{
  KEYUSE *keyuse= *(KEYUSE **) arg;
  if (!keyuse)
    return (uint) (-1);
  TABLE *table= keyuse->table;
  uint key= keyuse->key;
  uint fldno= keyuse->keypart;
  for ( ;
        keyuse->table == table && keyuse->key == key && keyuse->keypart == fldno;
        keyuse++)
    ;
  if (keyuse->key != key)
    keyuse= 0;
  *((KEYUSE **) arg)= keyuse;
  return fldno;
}

Project: server

copy_funcs(Item **func_ptr, const THD *thd)
{
  Item *func;
  for (; (func = *func_ptr) ; func_ptr++)
  {
    if (func->type() == Item::FUNC_ITEM &&
        ((Item_func *) func)->with_window_func)
      continue;
    func->save_in_result_field(1);
    /*
      Need to check the THD error state because Item::val_xxx() don't
      return error code, but can generate errors
      TODO: change it for a real status check when Item::val_xxx()
      are extended to return status code.
    */  
    if (unlikely(thd->is_error()))
      return TRUE;
  }
  return FALSE;
}

Project: server

bool error_if_full_join(JOIN *join)
{
  for (JOIN_TAB *tab=first_top_level_tab(join, WITH_CONST_TABLES); tab;
       tab= next_top_level_tab(join, tab))
  {
    if (tab->type == JT_ALL && (!tab->select || !tab->select->quick))
    {
      my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
                 ER_THD(join->thd,
                        ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
      return(1);
    }
  }
  return(0);
}

Project: server

void JOIN::cleanup_item_list(List<Item> &items) const
{
  DBUG_ENTER("JOIN::cleanup_item_list");
  if (!items.is_empty())
  {
    List_iterator_fast<Item> it(items);
    Item *item;
    while ((item= it++))
      item->cleanup();
  }
  DBUG_VOID_RETURN;
}

Project: server

uint get_next_field_for_derived_key(uchar *arg)
{
  KEYUSE *keyuse= *(KEYUSE **) arg;
  if (!keyuse)
    return (uint) (-1);
  TABLE *table= keyuse->table;
  uint key= keyuse->key;
  uint fldno= keyuse->keypart; 
  uint keypart= keyuse->keypart_map == (key_part_map) 1 ?
                                         0 : (keyuse-1)->keypart+1;
  for ( ; 
        keyuse->table == table && keyuse->key == key && keyuse->keypart == fldno;
        keyuse++)
    keyuse->keypart= keypart;
  if (keyuse->key != key)
    keyuse= 0;
  *((KEYUSE **) arg)= keyuse;
  return fldno;
}

Project: server

make_outerjoin_info(JOIN *join)
{
  DBUG_ENTER("make_outerjoin_info");
  
  /*
    Create temp. tables for merged SJ-Materialization nests. We need to do
    this now, because further code relies on tab->table and
    tab->table->pos_in_table_list being set.
  */
  JOIN_TAB *tab;
  for (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_part1(tab))
        DBUG_RETURN(TRUE);
      tab->table->reginfo.join_tab= tab;
    }
  }

  for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab; 
       tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
    TABLE *table= tab->table;
    TABLE_LIST *tbl= table->pos_in_table_list;
    TABLE_LIST *embedding= tbl->embedding;

    if (tbl->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
    {
      /* 
        Table tab is the only one inner table for outer join.
        (Like table t4 for the table reference t3 LEFT JOIN t4 ON t3.a=t4.a
        is in the query above.)
      */
      tab->last_inner= tab->first_inner= tab;
      tab->on_expr_ref= &tbl->on_expr;
      tab->cond_equal= tbl->cond_equal;
      if (embedding && !embedding->is_active_sjm())
        tab->first_upper= embedding->nested_join->first_nested;
    }
    else if (!embedding)
      tab->table->reginfo.not_exists_optimize= 0;
          
    for ( ; embedding ; embedding= embedding->embedding)
    {
      if (embedding->is_active_sjm())
      {
        /* We're trying to walk out of an SJ-Materialization nest. Don't do this.  */
        break;
      }
      /* Ignore sj-nests: */
      if (!(embedding->on_expr && embedding->outer_join))
      {
        tab->table->reginfo.not_exists_optimize= 0;
        continue;
      }
      NESTED_JOIN *nested_join= embedding->nested_join;
      if (!nested_join->counter)
      {
        /* 
          Table tab is the first inner table for nested_join.
          Save reference to it in the nested join structure.
        */ 
        nested_join->first_nested= tab;
        tab->on_expr_ref= &embedding->on_expr;
        tab->cond_equal= tbl->cond_equal;
        if (embedding->embedding)
          tab->first_upper= embedding->embedding->nested_join->first_nested;
      }
      if (!tab->first_inner)  
        tab->first_inner= nested_join->first_nested;
      if (++nested_join->counter < nested_join->n_tables)
        break;
      /* Table tab is the last inner table for nested join. */
      nested_join->first_nested->last_inner= tab;
    }
  }
  DBUG_RETURN(FALSE);
}

Project: server

mysql_select(THD *thd,
	     TABLE_LIST *tables, uint wild_num, List<Item> &fields,
	     COND *conds, uint og_num,  ORDER *order, ORDER *group,
	     Item *having, ORDER *proc_param, ulonglong select_options,
	     select_result *result, SELECT_LEX_UNIT *unit,
	     SELECT_LEX *select_lex)
{
  int err= 0;
  bool free_join= 1;
  DBUG_ENTER("mysql_select");

  if (!fields.is_empty())
    select_lex->context.resolve_in_select_list= true;
  JOIN *join;
  if (select_lex->join != 0)
  {
    join= select_lex->join;
    /*
      is it single SELECT in derived table, called in derived table
      creation
    */
    if (select_lex->linkage != DERIVED_TABLE_TYPE ||
	(select_options & SELECT_DESCRIBE))
    {
      if (select_lex->linkage != GLOBAL_OPTIONS_TYPE)
      {
        /*
          Original join tabs might be overwritten at first
          subselect execution. So we need to restore them.
        */
        Item_subselect *subselect= select_lex->master_unit()->item;
        if (subselect && subselect->is_uncacheable() && join->reinit())
          DBUG_RETURN(TRUE);
      }
      else
      {
        if ((err= join->prepare( tables, wild_num,
                                conds, og_num, order, false, group, having,
                                proc_param, select_lex, unit)))
	{
	  goto err;
	}
      }
    }
    free_join= 0;
    join->select_options= select_options;
  }
  else
  {
    if (thd->lex->describe)
      select_options|= SELECT_DESCRIBE;

    /*
      When in EXPLAIN, delay deleting the joins so that they are still
      available when we're producing EXPLAIN EXTENDED warning text.
    */
    if (select_options & SELECT_DESCRIBE)
      free_join= 0;

    if (!(join= new (thd->mem_root) JOIN(thd, fields, select_options, result)))
	DBUG_RETURN(TRUE);
    THD_STAGE_INFO(thd, stage_init);
    thd->lex->used_tables=0;
    if ((err= join->prepare(tables, wild_num,
                            conds, og_num, order, false, group, having, proc_param,
                            select_lex, unit)))
    {
      goto err;
    }
  }

  if ((err= join->optimize()))
  {
    goto err;					// 1
  }

  if (thd->lex->describe & DESCRIBE_EXTENDED)
  {
    join->conds_history= join->conds;
    join->having_history= (join->having?join->having:join->tmp_having);
  }

  if (unlikely(thd->is_error()))
    goto err;

  join->exec();

  if (thd->lex->describe & DESCRIBE_EXTENDED)
  {
    select_lex->where= join->conds_history;
    select_lex->having= join->having_history;
  }

err:
  if (free_join)
  {
    THD_STAGE_INFO(thd, stage_end);
    err|= (int)(select_lex->cleanup());
    DBUG_RETURN(err || thd->is_error());
  }
  DBUG_RETURN(join->error ? join->error: err);
}

Project: server

choose_plan(JOIN *join, table_map join_tables)
{
  uint search_depth= join->thd->variables.optimizer_search_depth;
  uint prune_level=  join->thd->variables.optimizer_prune_level;
  uint use_cond_selectivity= 
         join->thd->variables.optimizer_use_condition_selectivity;
  bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
  DBUG_ENTER("choose_plan");

  join->cur_embedding_map= 0;
  reset_nj_counters(join, join->join_list);
  qsort2_cmp jtab_sort_func;

  if (join->emb_sjm_nest)
  {
    /* We're optimizing semi-join materialization nest, so put the 
       tables from this semi-join as first
    */
    jtab_sort_func= join_tab_cmp_embedded_first;
  }
  else
  {
    /*
      if (SELECT_STRAIGHT_JOIN option is set)
        reorder tables so dependent tables come after tables they depend 
        on, otherwise keep tables in the order they were specified in the query 
      else
        Apply heuristic: pre-sort all access plans with respect to the number of
        records accessed.
    */
    jtab_sort_func= straight_join ? join_tab_cmp_straight : join_tab_cmp;
  }

  /*
    psergey-todo: if we're not optimizing an SJM nest, 
     - sort that outer tables are first, and each sjm nest follows
     - then, put each [sjm_table1, ... sjm_tableN] sub-array right where 
       WHERE clause pushdown would have put it.
  */
  my_qsort2(join->best_ref + join->const_tables,
            join->table_count - join->const_tables, sizeof(JOIN_TAB*),
            jtab_sort_func, (void*)join->emb_sjm_nest);

  if (!join->emb_sjm_nest)
  {
    choose_initial_table_order(join);
  }
  join->cur_sj_inner_tables= 0;

  if (straight_join)
  {
    optimize_straight_join(join, join_tables);
  }
  else
  {
    DBUG_ASSERT(search_depth <= MAX_TABLES + 1);
    if (search_depth == 0)
      /* Automatically determine a reasonable value for 'search_depth' */
      search_depth= determine_search_depth(join);
    if (greedy_search(join, join_tables, search_depth, prune_level,
                      use_cond_selectivity))
      DBUG_RETURN(TRUE);
  }

  /* 
    Store the cost of this query into a user variable
    Don't update last_query_cost for statements that are not "flat joins" :
    i.e. they have subqueries, unions or call stored procedures.
    TODO: calculate a correct cost for a query with subqueries and UNIONs.
  */
  if (join->thd->lex->is_single_level_stmt())
    join->thd->status_var.last_query_cost= join->best_read;
  DBUG_RETURN(FALSE);
}

Project: server

update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
                    uint tables, COND *cond, table_map normal_tables,
                    SELECT_LEX *select_lex, SARGABLE_PARAM **sargables)
{
  uint	and_level,i;
  KEY_FIELD *key_fields, *end, *field;
  uint sz;
  uint m= MY_MAX(select_lex->max_equal_elems,1);
  DBUG_ENTER("update_ref_and_keys");
  DBUG_PRINT("enter", ("normal_tables: %llx", normal_tables));

  SELECT_LEX *sel=thd->lex->current_select; 
  sel->cond_count= 0;
  sel->between_count= 0; 
  if (cond)
    cond->walk(&Item::count_sargable_conds, 0, sel);
  for (i=0 ; i < tables ; i++)
  {
    if (*join_tab[i].on_expr_ref)
      (*join_tab[i].on_expr_ref)->walk(&Item::count_sargable_conds, 0, sel);
  }
  {
    List_iterator<TABLE_LIST> li(*join_tab->join->join_list);
    TABLE_LIST *table;
    while ((table= li++))
    {
      if (table->nested_join)
        count_cond_for_nj(sel, table);
    }
  }
  
  /* 
    We use the same piece of memory to store both  KEY_FIELD 
    and SARGABLE_PARAM structure.
    KEY_FIELD values are placed at the beginning this memory
    while  SARGABLE_PARAM values are put at the end.
    All predicates that are used to fill arrays of KEY_FIELD
    and SARGABLE_PARAM structures have at most 2 arguments
    except BETWEEN predicates that have 3 arguments and 
    IN predicates.
    This any predicate if it's not BETWEEN/IN can be used 
    directly to fill at most 2 array elements, either of KEY_FIELD
    or SARGABLE_PARAM type. For a BETWEEN predicate 3 elements
    can be filled as this predicate is considered as
    saragable with respect to each of its argument.
    An IN predicate can require at most 1 element as currently
    it is considered as sargable only for its first argument.
    Multiple equality can add  elements that are filled after
    substitution of field arguments by equal fields. There
    can be not more than select_lex->max_equal_elems such 
    substitutions.
  */ 
  sz= MY_MAX(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))*
    ((sel->cond_count*2 + sel->between_count)*m+1);
  if (!(key_fields=(KEY_FIELD*)	thd->alloc(sz)))
    DBUG_RETURN(TRUE); /* purecov: inspected */
  and_level= 0;
  field= end= key_fields;
  *sargables= (SARGABLE_PARAM *) key_fields + 
                (sz - sizeof((*sargables)[0].field))/sizeof(SARGABLE_PARAM);
  /* set a barrier for the array of SARGABLE_PARAM */
  (*sargables)[0].field= 0; 

  if (my_init_dynamic_array2(keyuse, sizeof(KEYUSE),
                             thd->alloc(sizeof(KEYUSE) * 20), 20, 64,
                             MYF(MY_THREAD_SPECIFIC)))
    DBUG_RETURN(TRUE);

  if (cond)
  {
    KEY_FIELD *saved_field= field;
    cond->add_key_fields(join_tab->join, &end, &and_level, normal_tables,
                         sargables);
    for (; field != end ; field++)
    {

      /* Mark that we can optimize LEFT JOIN */
      if (field->val->type() == Item::NULL_ITEM &&
	  !field->field->real_maybe_null())
	field->field->table->reginfo.not_exists_optimize=1;
    }
    field= saved_field;
  }
  for (i=0 ; i < tables ; i++)
  {
    /*
      Block the creation of keys for inner tables of outer joins.
      Here only the outer joins that can not be converted to
      inner joins are left and all nests that can be eliminated
      are flattened.
      In the future when we introduce conditional accesses
      for inner tables in outer joins these keys will be taken
      into account as well.
    */ 
    if (*join_tab[i].on_expr_ref)
      (*join_tab[i].on_expr_ref)->add_key_fields(join_tab->join, &end,
                                                 &and_level, 
                                                 join_tab[i].table->map,
                                                 sargables);
  }

  /* Process ON conditions for the nested joins */
  {
    List_iterator<TABLE_LIST> li(*join_tab->join->join_list);
    TABLE_LIST *table;
    while ((table= li++))
    {
      if (table->nested_join)
        add_key_fields_for_nj(join_tab->join, table, &end, &and_level, 
                              sargables);
    }
  }

  /* fill keyuse with found key parts */
  for ( ; field != end ; field++)
  {
    if (add_key_part(keyuse,field))
      DBUG_RETURN(TRUE);
  }

  if (select_lex->ftfunc_list->elements)
  {
    if (add_ft_keys(keyuse,join_tab,cond,normal_tables))
      DBUG_RETURN(TRUE);
  }

  DBUG_RETURN(FALSE);
}

Project: server

create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
                                    TMP_ENGINE_COLUMNDEF *start_recinfo,
                                    TMP_ENGINE_COLUMNDEF **recinfo, 
                                    int error,
                                    bool ignore_last_dupp_key_error,
                                    bool *is_duplicate)
{
  TABLE new_table;
  TABLE_SHARE share;
  const char *save_proc_info;
  int write_err= 0;
  DBUG_ENTER("create_internal_tmp_table_from_heap");
  if (is_duplicate)
    *is_duplicate= FALSE;

  if (table->s->db_type() != heap_hton || error != HA_ERR_RECORD_FILE_FULL)
  {
    /*
      We don't want this error to be converted to a warning, e.g. in case of
      INSERT IGNORE ... SELECT.
    */
    table->file->print_error(error, MYF(ME_FATALERROR));
    DBUG_RETURN(1);
  }
  new_table= *table;
  share= *table->s;
  new_table.s= &share;
  new_table.s->db_plugin= ha_lock_engine(thd, TMP_ENGINE_HTON);
  if (unlikely(!(new_table.file= get_new_handler(&share, &new_table.mem_root,
                                                 new_table.s->db_type()))))
    DBUG_RETURN(1);				// End of memory

  if (unlikely(new_table.file->set_ha_share_ref(&share.ha_share)))
  {
    delete new_table.file;
    DBUG_RETURN(1);
  }

  save_proc_info=thd->proc_info;
  THD_STAGE_INFO(thd, stage_converting_heap_to_myisam);

  new_table.no_rows= table->no_rows;
  if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo,
                                recinfo,
                                thd->lex->select_lex.options | 
			        thd->variables.option_bits))
    goto err2;
  if (open_tmp_table(&new_table))
    goto err1;
  if (table->file->indexes_are_disabled())
    new_table.file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
  table->file->ha_index_or_rnd_end();
  if (table->file->ha_rnd_init_with_error(1))
    DBUG_RETURN(1);
  if (new_table.no_rows)
    new_table.file->extra(HA_EXTRA_NO_ROWS);
  else
  {
    /* update table->file->stats.records */
    table->file->info(HA_STATUS_VARIABLE);
    new_table.file->ha_start_bulk_insert(table->file->stats.records);
  }

  /*
    copy all old rows from heap table to MyISAM table
    This is the only code that uses record[1] to read/write but this
    is safe as this is a temporary MyISAM table without timestamp/autoincrement
    or partitioning.
  */
  while (!table->file->ha_rnd_next(new_table.record[1]))
  {
    write_err= new_table.file->ha_write_tmp_row(new_table.record[1]);
    DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
    if (write_err)
      goto err;
    if (unlikely(thd->check_killed()))
      goto err_killed;
  }
  if (!new_table.no_rows && new_table.file->ha_end_bulk_insert())
    goto err;
  /* copy row that filled HEAP table */
  if (unlikely((write_err=new_table.file->ha_write_tmp_row(table->record[0]))))
  {
    if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
	!ignore_last_dupp_key_error)
      goto err;
    if (is_duplicate)
      *is_duplicate= TRUE;
  }
  else
  {
    if (is_duplicate)
      *is_duplicate= FALSE;
  }

  /* remove heap table and change to use myisam table */
  (void) table->file->ha_rnd_end();
  (void) table->file->ha_close();          // This deletes the table !
  delete table->file;
  table->file=0;
  plugin_unlock(0, table->s->db_plugin);
  share.db_plugin= my_plugin_lock(0, share.db_plugin);
  new_table.s= table->s;                       // Keep old share
  *table= new_table;
  *table->s= share;
  
  table->file->change_table_ptr(table, table->s);
  table->use_all_columns();
  if (save_proc_info)
    thd_proc_info(thd, (!strcmp(save_proc_info,"Copying to tmp table") ?
                  "Copying to tmp table on disk" : save_proc_info));
  DBUG_RETURN(0);

 err:
  DBUG_PRINT("error",("Got error: %d",write_err));
  table->file->print_error(write_err, MYF(0));
err_killed:
  (void) table->file->ha_rnd_end();
  (void) new_table.file->ha_close();
 err1:
  new_table.file->ha_delete_table(new_table.s->path.str);
 err2:
  delete new_table.file;
  thd_proc_info(thd, save_proc_info);
  table->mem_root= new_table.mem_root;
  DBUG_RETURN(1);
}

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");
  
  /* 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->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

void count_cond_for_nj(SELECT_LEX *sel, TABLE_LIST *nested_join_table)
{
  List_iterator<TABLE_LIST> li(nested_join_table->nested_join->join_list);
  List_iterator<TABLE_LIST> li2(nested_join_table->nested_join->join_list);
  bool have_another = FALSE;
  TABLE_LIST *table;

  while ((table= li++) || (have_another && (li=li2, have_another=FALSE,
                                            (table= li++))))
  if (table->nested_join)
  {
    if (!table->on_expr)
    {
      /* It's a semi-join nest. Walk into it as if it wasn't a nest */
      have_another= TRUE;
      li2= li;
      li= List_iterator<TABLE_LIST>(table->nested_join->join_list); 
    }
    else
      count_cond_for_nj(sel, table); 
  }
  if (nested_join_table->on_expr)
    nested_join_table->on_expr->walk(&Item::count_sargable_conds, 0, sel);
    
}

Project: server

bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
{
  Field *field=left_item->field;
  JOIN_TAB *join_tab= field->table->reginfo.join_tab;
  // No need to change const test
  if (!field->table->const_table && join_tab &&
      !join_tab->is_ref_for_hash_join() &&
      (!join_tab->first_inner ||
       *join_tab->first_inner->on_expr_ref == root_cond))
  {
    /*
      If ref access uses "Full scan on NULL key" (i.e. it actually alternates
      between ref access and full table scan), then no equality can be
      guaranteed to be true.
    */
    if (join_tab->ref.is_access_triggered())
      return FALSE;

    Item *ref_item=part_of_refkey(field->table,field);
    if (ref_item && (ref_item->eq(right_item,1) || 
		     ref_item->real_item()->eq(right_item,1)))
    {
      right_item= right_item->real_item();
      if (right_item->type() == Item::FIELD_ITEM)
	return (field->eq_def(((Item_field *) right_item)->field));
      /* remove equalities injected by IN->EXISTS transformation */
      else if (right_item->type() == Item::CACHE_ITEM)
        return ((Item_cache *)right_item)->eq_def (field);
      if (right_item->const_item() && !(right_item->is_null()))
      {
	/*
	  We can remove binary fields and numerical fields except float,
	  as float comparison isn't 100 % safe
	  We have to keep normal strings to be able to check for end spaces
	*/
	if (field->binary() &&
	    field->real_type() != MYSQL_TYPE_STRING &&
	    field->real_type() != MYSQL_TYPE_VARCHAR &&
	    (field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
	{
	  return !right_item->save_in_field_no_warnings(field, 1);
	}
      }
    }
  }
  return 0;					// keep test
}

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 (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

free_tmp_table(THD *thd, TABLE *entry)
{
  MEM_ROOT own_root= entry->mem_root;
  const char *save_proc_info;
  DBUG_ENTER("free_tmp_table");
  DBUG_PRINT("enter",("table: %s  alias: %s",entry->s->table_name.str,
                      entry->alias.c_ptr()));

  save_proc_info=thd->proc_info;
  THD_STAGE_INFO(thd, stage_removing_tmp_table);

  if (entry->file && entry->is_created())
  {
    entry->file->ha_index_or_rnd_end();
    if (entry->db_stat)
    {
      entry->file->info(HA_STATUS_VARIABLE);
      thd->tmp_tables_size+= (entry->file->stats.data_file_length +
                              entry->file->stats.index_file_length);
      entry->file->ha_drop_table(entry->s->path.str);
    }
    else
      entry->file->ha_delete_table(entry->s->path.str);
    delete entry->file;
  }

  /* free blobs */
  for (Field **ptr=entry->field ; *ptr ; ptr++)
    (*ptr)->free();

  if (entry->temp_pool_slot != MY_BIT_NONE)
    bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot);

  plugin_unlock(0, entry->s->db_plugin);
  entry->alias.free();

  if (entry->pos_in_table_list && entry->pos_in_table_list->table)
  {
    DBUG_ASSERT(entry->pos_in_table_list->table == entry);
    entry->pos_in_table_list->table= NULL;
  }

  free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
  thd_proc_info(thd, save_proc_info);

  DBUG_VOID_RETURN;
}

Project: server

pick_table_access_method(JOIN_TAB *tab)
{
  switch (tab->type) 
  {
  case JT_REF:
    tab->read_first_record= join_read_always_key;
    tab->read_record.read_record_func= join_read_next_same;
    break;

  case JT_REF_OR_NULL:
    tab->read_first_record= join_read_always_key_or_null;
    tab->read_record.read_record_func= join_read_next_same_or_null;
    break;

  case JT_CONST:
    tab->read_first_record= join_read_const;
    tab->read_record.read_record_func= join_no_more_records;
    break;

  case JT_EQ_REF:
    tab->read_first_record= join_read_key;
    tab->read_record.read_record_func= join_no_more_records;
    break;

  case JT_FT:
    tab->read_first_record= join_ft_read_first;
    tab->read_record.read_record_func= join_ft_read_next;
    break;

  case JT_SYSTEM:
    tab->read_first_record= join_read_system;
    tab->read_record.read_record_func= join_no_more_records;
    break;

  /* keep gcc happy */  
  default:
    break;  
  }
}

Project: server

join_init_quick_read_record(JOIN_TAB *tab)
{
  if (test_if_quick_select(tab) == -1)
    return -1;					/* No possible records */
  return join_init_read_record(tab);
}

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, type %s, message %s",
		      join->select_lex, 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->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->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->linkage;
    xpl_sel->is_lateral= ((select_lex->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);
  }

  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

bool cond_is_datetime_is_null(Item *cond)
{
  if (cond->type() == Item::FUNC_ITEM &&
      ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
  {
    return ((Item_func_isnull*) cond)->arg_is_datetime_notnull_field();
  }
  return false;
}

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->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

fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
               Ref_ptr_array ref_pointer_array)
{
  Item_outer_ref *ref;

  /*
    Mark the references from  the inner_refs_list that are occurred in
    the group by expressions. Those references will contain direct
    references to the referred fields. The markers are set in 
    the found_in_group_by field of the references from the list.
  */
  List_iterator_fast <Item_outer_ref> ref_it(select->inner_refs_list);
  for (ORDER *group= select->join->group_list; group;  group= group->next)
  {
    (*group->item)->walk(&Item::check_inner_refs_processor, TRUE, &ref_it);
  } 
    
  while ((ref= ref_it++))
  {
    bool direct_ref= false;
    Item *item= ref->outer_ref;
    Item **item_ref= ref->ref;
    Item_ref *new_ref;
    /*
      TODO: this field item already might be present in the select list.
      In this case instead of adding new field item we could use an
      existing one. The change will lead to less operations for copying fields,
      smaller temporary tables and less data passed through filesort.
    */
    if (!ref_pointer_array.is_null() && !ref->found_in_select_list)
    {
      int el= all_fields.elements;
      ref_pointer_array[el]= item;
      /* Add the field item to the select list of the current select. */
      all_fields.push_front(item, thd->mem_root);
      /*
        If it's needed reset each Item_ref item that refers this field with
        a new reference taken from ref_pointer_array.
      */
      item_ref= &ref_pointer_array[el];
    }

    if (ref->in_sum_func)
    {
      Item_sum *sum_func;
      if (ref->in_sum_func->nest_level > select->nest_level)
        direct_ref= TRUE;
      else
      {
        for (sum_func= ref->in_sum_func; sum_func &&
             sum_func->aggr_level >= select->nest_level;
             sum_func= sum_func->in_sum_func)
        {
          if (sum_func->aggr_level == select->nest_level)
          {
            direct_ref= TRUE;
            break;
          }
        }
      }
    }
    else if (ref->found_in_group_by)
      direct_ref= TRUE;

    new_ref= direct_ref ?
              new (thd->mem_root) Item_direct_ref(thd, ref->context, item_ref, ref->table_name,
                          &ref->field_name, ref->alias_name_used) :
              new (thd->mem_root) Item_ref(thd, ref->context, item_ref, ref->table_name,
                          &ref->field_name, ref->alias_name_used);
    if (!new_ref)
      return TRUE;
    ref->outer_ref= new_ref;
    ref->ref= &ref->outer_ref;

    if (ref->fix_fields_if_needed(thd, 0))
      return TRUE;
    thd->lex->used_tables|= item->used_tables();
    thd->lex->current_select->select_list_tables|= item->used_tables();
  }
  return false;
}

Project: server

bool JOIN_TAB::preread_init()
{
  TABLE_LIST *derived= table->pos_in_table_list;
  DBUG_ENTER("JOIN_TAB::preread_init");

  if (!derived || !derived->is_materialized_derived())
  {
    preread_init_done= TRUE;
    DBUG_RETURN(FALSE);
  }

  /* Materialize derived table/view. */
  if ((!derived->get_unit()->executed  ||
       derived->is_recursive_with_table() ||
       derived->get_unit()->uncacheable) &&
      mysql_handle_single_derived(join->thd->lex,
                                    derived, DT_CREATE | DT_FILL))
    DBUG_RETURN(TRUE);

  if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) ||
      derived->is_nonrecursive_derived_with_rec_ref())
    preread_init_done= TRUE;
  if (select && select->quick)
    select->quick->replace_handler(table->file);

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

  /* init ftfuns for just initialized derived table */
  if (table->fulltext_searched)
    if (init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order)))
      DBUG_RETURN(TRUE);

  DBUG_RETURN(FALSE);
}

Project: server

JOIN::optimize_inner()
{
  DBUG_ENTER("JOIN::optimize_inner");
  subq_exit_fl= false;
  do_send_rows = (unit->select_limit_cnt) ? 1 : 0;

  DEBUG_SYNC(thd, "before_join_optimize");

  THD_STAGE_INFO(thd, stage_optimizing);

  set_allowed_join_cache_types();
  need_distinct= TRUE;

  /*
    Needed in case optimizer short-cuts,
    set properly in make_aggr_tables_info()
  */
  fields= &select_lex->item_list;

  if (select_lex->first_cond_optimization)
  {
    //Do it only for the first execution
    /* Merge all mergeable derived tables/views in this SELECT. */
    if (select_lex->handle_derived(thd->lex, DT_MERGE))
      DBUG_RETURN(TRUE);  
    table_count= select_lex->leaf_tables.elements;
  }

  if (select_lex->first_cond_optimization &&
      transform_in_predicates_into_in_subq(thd))
    DBUG_RETURN(1);

  // Update used tables after all handling derived table procedures
  select_lex->update_used_tables();

  /*
    In fact we transform underlying subqueries after their 'prepare' phase and
    before 'optimize' from upper query 'optimize' to allow semijoin
    conversion happened (which done in the same way.
  */
  if (select_lex->first_cond_optimization &&
      conds && conds->walk(&Item::exists2in_processor, 0, thd))
    DBUG_RETURN(1);
  /*
    TODO
    make view to decide if it is possible to write to WHERE directly or make Semi-Joins able to process ON condition if it is possible
  for (TABLE_LIST *tbl= tables_list; tbl; tbl= tbl->next_local)
  {
    if (tbl->on_expr &&
        tbl->on_expr->walk(&Item::exists2in_processor, 0, thd))
      DBUG_RETURN(1);
  }
  */

  if (transform_max_min_subquery())
    DBUG_RETURN(1); /* purecov: inspected */

  if (select_lex->first_cond_optimization)
  {
    /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */
    if (convert_join_subqueries_to_semijoins(this))
      DBUG_RETURN(1); /* purecov: inspected */
    /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */
    select_lex->update_used_tables();
  }
  
  eval_select_list_used_tables();

  table_count= select_lex->leaf_tables.elements;

  if (select_lex->options & OPTION_SCHEMA_TABLE &&
      optimize_schema_tables_memory_usage(select_lex->leaf_tables))
    DBUG_RETURN(1);

  if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */
    DBUG_RETURN(-1);

  row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
	      unit->select_limit_cnt);
  /* select_limit is used to decide if we are likely to scan the whole table */
  select_limit= unit->select_limit_cnt;
  if (having || (select_options & OPTION_FOUND_ROWS))
    select_limit= HA_POS_ERROR;
#ifdef HAVE_REF_TO_FIELDS			// Not done yet
  /* Add HAVING to WHERE if possible */
  if (having && !group_list && !sum_func_count)
  {
    if (!conds)
    {
      conds= having;
      having= 0;
    }
    else if ((conds=new (thd->mem_root) Item_cond_and(conds,having)))
    {
      /*
        Item_cond_and can't be fixed after creation, so we do not check
        conds->fixed
      */
      conds->fix_fields(thd, &conds);
      conds->change_ref_to_fields(thd, tables_list);
      conds->top_level_item();
      having= 0;
    }
  }
#endif

  SELECT_LEX *sel= select_lex;
  if (sel->first_cond_optimization)
  {
    /*
      The following code will allocate the new items in a permanent
      MEMROOT for prepared statements and stored procedures.

      But first we need to ensure that thd->lex->explain is allocated
      in the execution arena
    */
    create_explain_query_if_not_exists(thd->lex, thd->mem_root);

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

    sel->first_cond_optimization= 0;

    /* Convert all outer joins to inner joins if possible */
    conds= simplify_joins(this, join_list, conds, TRUE, FALSE);
    if (thd->is_error() || select_lex->save_leaf_tables(thd))
    {
      if (arena)
        thd->restore_active_arena(arena, &backup);
      DBUG_RETURN(1);
    }
    build_bitmap_for_nested_joins(join_list, 0);

    sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;

    sel->where= conds;

    select_lex->update_used_tables();

    if (arena)
      thd->restore_active_arena(arena, &backup);
  }
  
  if (optimize_constant_subqueries())
    DBUG_RETURN(1);

  if (conds && conds->with_subquery())
    (void) conds->walk(&Item::cleanup_is_expensive_cache_processor,
                       0, (void *) 0);
  if (having && having->with_subquery())
    (void) having->walk(&Item::cleanup_is_expensive_cache_processor,
			0, (void *) 0);

  if (setup_jtbm_semi_joins(this, join_list, &conds))
    DBUG_RETURN(1);

  if (select_lex->cond_pushed_into_where)
  {
    conds= and_conds(thd, conds, select_lex->cond_pushed_into_where);
    if (conds && conds->fix_fields(thd, &conds))
      DBUG_RETURN(1);
  }
  if (select_lex->cond_pushed_into_having)
  {
    having= and_conds(thd, having, select_lex->cond_pushed_into_having);
    if (having)
    {
      select_lex->having_fix_field= 1;
      select_lex->having_fix_field_for_pushed_cond= 1;
      if (having->fix_fields(thd, &having))
        DBUG_RETURN(1);
      select_lex->having_fix_field= 0;
      select_lex->having_fix_field_for_pushed_cond= 0;
    }
  }
  
  bool ignore_on_expr= false;
  /*
    PS/SP note: on_expr of versioned table can not be reallocated
    (see build_equal_items() below) because it can be not rebuilt
    at second invocation.
  */
  if (!thd->stmt_arena->is_conventional() && thd->mem_root != thd->stmt_arena->mem_root)
    for (TABLE_LIST *tbl= tables_list; tbl; tbl= tbl->next_local)
      if (tbl->table && tbl->on_expr && tbl->table->versioned())
      {
        ignore_on_expr= true;
        break;
      }
  conds= optimize_cond(this, conds, join_list, ignore_on_expr,
                       &cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
  
  if (thd->is_error())
  {
    error= 1;
    DBUG_PRINT("error",("Error from optimize_cond"));
    DBUG_RETURN(1);
  }

  if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED))
  {
    TABLE_LIST *tbl;
    List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
    while ((tbl= li++))
    {
      /* 
        Do not push conditions from where into materialized inner tables
        of outer joins: this is not valid.
      */
      if (tbl->is_materialized_derived())
      {
        JOIN *join= tbl->get_unit()->first_select()->join;
        if (join &&
            join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE &&
            join->with_two_phase_optimization)
          continue;
        /*
          Do not push conditions from where into materialized inner tables
          of outer joins: this is not valid.
        */
        if (!tbl->is_inner_table_of_outer_join())
	{
          if (pushdown_cond_for_derived(thd, conds, tbl))
	    DBUG_RETURN(1);
        }
	if (mysql_handle_single_derived(thd->lex, tbl, DT_OPTIMIZE))
	  DBUG_RETURN(1);
      }
    }
  }
  else
  {
    /* Run optimize phase for all derived tables/views used in this SELECT. */
    if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
      DBUG_RETURN(1);
  }

  {
    having= optimize_cond(this, having, join_list, TRUE,
                          &having_value, &having_equal);

    if (unlikely(thd->is_error()))
    {
      error= 1;
      DBUG_PRINT("error",("Error from optimize_cond"));
      DBUG_RETURN(1);
    }
    if (select_lex->where)
    {
      select_lex->cond_value= cond_value;
      if (sel->where != conds && cond_value == Item::COND_OK)
        thd->change_item_tree(&sel->where, conds);
    }  
    if (select_lex->having)
    {
      select_lex->having_value= having_value;
      if (sel->having != having && having_value == Item::COND_OK)
        thd->change_item_tree(&sel->having, having);    
    }
    if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE || 
        (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
    {						/* Impossible cond */
      if (unit->select_limit_cnt)
      {
        DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
                              "Impossible HAVING" : "Impossible WHERE"));
        zero_result_cause=  having_value == Item::COND_FALSE ?
                             "Impossible HAVING" : "Impossible WHERE";
      }
      else
      {
        DBUG_PRINT("info", ("Zero limit"));
        zero_result_cause= "Zero limit";
      }
      table_count= top_join_tab_count= 0;
      handle_implicit_grouping_with_window_funcs();
      error= 0;
      subq_exit_fl= true;
      goto setup_subq_exit;
    }
  }

#ifdef WITH_PARTITION_STORAGE_ENGINE
  {
    TABLE_LIST *tbl;
    List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
    while ((tbl= li++))
    {
      Item **prune_cond= get_sargable_cond(this, tbl->table);
      tbl->table->all_partitions_pruned_away=
        prune_partitions(thd, tbl->table, *prune_cond);
    }
  }
#endif

  /* 
     Try to optimize count(*), MY_MIN() and MY_MAX() to const fields if
     there is implicit grouping (aggregate functions but no
     group_list). In this case, the result set shall only contain one
     row. 
  */
  if (tables_list && implicit_grouping)
  {
    int res;
    /*
      opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match
      to the WHERE conditions,
      or 1 if all items were resolved (optimized away),
      or 0, or an error number HA_ERR_...

      If all items were resolved by opt_sum_query, there is no need to
      open any tables.
    */
    if ((res=opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds)))
    {
      DBUG_ASSERT(res >= 0);
      if (res == HA_ERR_KEY_NOT_FOUND)
      {
        DBUG_PRINT("info",("No matching min/max row"));
	zero_result_cause= "No matching min/max row";
        table_count= top_join_tab_count= 0;
	error=0;
        subq_exit_fl= true;
        handle_implicit_grouping_with_window_funcs();
        goto setup_subq_exit;
      }
      if (res > 1)
      {
        error= res;
        DBUG_PRINT("error",("Error from opt_sum_query"));
        DBUG_RETURN(1);
      }

      DBUG_PRINT("info",("Select tables optimized away"));
      if (!select_lex->have_window_funcs())
        zero_result_cause= "Select tables optimized away";
      tables_list= 0;				// All tables resolved
      select_lex->min_max_opt_list.empty();
      const_tables= top_join_tab_count= table_count;
      handle_implicit_grouping_with_window_funcs();
      /*
        Extract all table-independent conditions and replace the WHERE
        clause with them. All other conditions were computed by opt_sum_query
        and the MIN/MAX/COUNT function(s) have been replaced by constants,
        so there is no need to compute the whole WHERE clause again.
        Notice that make_cond_for_table() will always succeed to remove all
        computed conditions, because opt_sum_query() is applicable only to
        conjunctions.
        Preserve conditions for EXPLAIN.
      */
      if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
      {
        COND *table_independent_conds=
          make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, -1,
                              FALSE, FALSE);
        DBUG_EXECUTE("where",
                     print_where(table_independent_conds,
                                 "where after opt_sum_query()",
                                 QT_ORDINARY););
        conds= table_independent_conds;
      }
    }
  }
  if (!tables_list)
  {
    DBUG_PRINT("info",("No tables"));
    error= 0;
    subq_exit_fl= true;
    goto setup_subq_exit;
  }
  error= -1;					// Error is sent to client
  /* get_sort_by_table() call used to be here: */
  MEM_UNDEFINED(&sort_by_table, sizeof(sort_by_table));

  /*
    We have to remove constants and duplicates from group_list before
    calling make_join_statistics() as this may call get_best_group_min_max()
    which needs a simplfied group_list.
  */
  if (group_list && table_count == 1)
  {
    group_list= remove_const(this, group_list, conds,
                             rollup.state == ROLLUP::STATE_NONE,
                             &simple_group);
    if (unlikely(thd->is_error()))
    {
      error= 1;
      DBUG_RETURN(1);
    }
    if (!group_list)
    {
      /* The output has only one row */
      order=0;
      simple_order=1;
      group_optimized_away= 1;
      select_distinct=0;
    }
  }
  
  /* Calculate how to do the join */
  THD_STAGE_INFO(thd, stage_statistics);
  result->prepare_to_read_rows();
  if (unlikely(make_join_statistics(this, select_lex->leaf_tables,
                                    &keyuse)) ||
      unlikely(thd->is_fatal_error))
  {
    DBUG_PRINT("error",("Error: make_join_statistics() failed"));
    DBUG_RETURN(1);
  }

  /*
    If a splittable materialized derived/view dt_i is embedded into
    into another splittable materialized derived/view dt_o then
    splitting plans for dt_i and dt_o are evaluated independently.
    First the optimizer looks for the best splitting plan sp_i for dt_i.
    It happens when non-splitting plans for dt_o are evaluated.
    The cost of sp_i is considered as the cost of materialization of dt_i
    when evaluating any splitting plan for dt_o.
  */
  if (fix_all_splittings_in_plan())
    DBUG_RETURN(1);

setup_subq_exit:
  with_two_phase_optimization= check_two_phase_optimization(thd);
  if (with_two_phase_optimization)
    optimization_state= JOIN::OPTIMIZATION_PHASE_1_DONE;
  else
  {
    if (optimize_stage2())
      DBUG_RETURN(1);
  }
  DBUG_RETURN(0);
}

Project: server

join_read_const(JOIN_TAB *tab)
{
  int error;
  TABLE *table= tab->table;
  if (table->status & STATUS_GARBAGE)		// If first read
  {
    table->status= 0;
    if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
      error=HA_ERR_KEY_NOT_FOUND;
    else
    {
      error= table->file->ha_index_read_idx_map(table->record[0],tab->ref.key,
                                                (uchar*) tab->ref.key_buff,
                                                make_prev_keypart_map(tab->ref.key_parts),
                                                HA_READ_KEY_EXACT);
    }
    if (unlikely(error))
    {
      table->status= STATUS_NOT_FOUND;
      mark_as_null_row(tab->table);
      empty_record(table);
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
	return report_error(table, error);
      return -1;
    }
    store_record(table,record[1]);
  }
  else if (!(table->status & ~STATUS_NULL_ROW))	// Only happens with left join
  {
    table->status=0;
    restore_record(table,record[1]);			// restore old record
  }
  table->null_row=0;
  return table->status ? -1 : 0;
}

Project: server

void mark_join_nest_as_const(JOIN *join,
                             TABLE_LIST *join_nest,
                             table_map *found_const_table_map,
                             uint *const_count)
{
  List_iterator<TABLE_LIST> it(join_nest->nested_join->join_list);
  TABLE_LIST *tbl;
  while ((tbl= it++))
  {
    if (tbl->nested_join)
    {
      mark_join_nest_as_const(join, tbl, found_const_table_map, const_count);
      continue;
    }
    JOIN_TAB *tab= tbl->table->reginfo.join_tab;

    if (!(join->const_table_map & tab->table->map))
    {
      tab->type= JT_CONST;
      tab->info= ET_IMPOSSIBLE_ON_CONDITION;
      tab->table->const_table= 1;

      join->const_table_map|= tab->table->map;
      *found_const_table_map|= tab->table->map;
      set_position(join,(*const_count)++,tab,(KEYUSE*) 0);
      mark_as_null_row(tab->table);		// All fields are NULL
    }
  }
}

Project: server

sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
{
  enum_nested_loop_state rc;
  JOIN_CACHE *cache= join_tab->cache;
  DBUG_ENTER("sub_select_cache");

  /*
    This function cannot be called if join_tab has no associated join
    buffer
  */
  DBUG_ASSERT(cache != NULL);

  join_tab->cache->reset_join(join);

  if (end_of_records)
  {
    rc= cache->join_records(FALSE);
    if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS ||
        rc == NESTED_LOOP_QUERY_LIMIT)
      rc= sub_select(join, join_tab, end_of_records);
    DBUG_RETURN(rc);
  }
  if (unlikely(join->thd->check_killed()))
  {
    /* The user has aborted the execution of the query */
    DBUG_RETURN(NESTED_LOOP_KILLED);
  }
  if (!test_if_use_dynamic_range_scan(join_tab))
  {
    if (!cache->put_record())
      DBUG_RETURN(NESTED_LOOP_OK); 
    /* 
      We has decided that after the record we've just put into the buffer
      won't add any more records. Now try to find all the matching 
      extensions for all records in the buffer.
    */ 
    rc= cache->join_records(FALSE);
    DBUG_RETURN(rc);
  }
  /*
     TODO: Check whether we really need the call below and we can't do
           without it. If it's not the case remove it.
  */ 
  rc= cache->join_records(TRUE);
  if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS ||
      rc == NESTED_LOOP_QUERY_LIMIT)
    rc= sub_select(join, join_tab, end_of_records);
  DBUG_RETURN(rc);
}

Project: server

bool JOIN::check_two_phase_optimization(THD *thd)
{
  if (check_for_splittable_materialized())
    return true;
  return false;
}

Project: server

evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
                     int error)
{
  bool shortcut_for_distinct= join_tab->shortcut_for_distinct;
  ha_rows found_records=join->found_records;
  COND *select_cond= join_tab->select_cond;
  bool select_cond_result= TRUE;

  DBUG_ENTER("evaluate_join_record");
  DBUG_PRINT("enter",
             ("evaluate_join_record join: %p join_tab: %p"
              " cond: %p error: %d  alias %s",
              join, join_tab, select_cond, error,
              join_tab->table->alias.ptr()));

  if (error > 0 || unlikely(join->thd->is_error())) // Fatal error
    DBUG_RETURN(NESTED_LOOP_ERROR);
  if (error < 0)
    DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
  if (unlikely(join->thd->check_killed()))       // Aborted by user
  {
    DBUG_RETURN(NESTED_LOOP_KILLED);            /* purecov: inspected */
  }

  join_tab->tracker->r_rows++;

  if (select_cond)
  {
    select_cond_result= MY_TEST(select_cond->val_int());

    /* check for errors evaluating the condition */
    if (unlikely(join->thd->is_error()))
      DBUG_RETURN(NESTED_LOOP_ERROR);
  }

  if (!select_cond || select_cond_result)
  {
    /*
      There is no select condition or the attached pushed down
      condition is true => a match is found.
    */
    join_tab->tracker->r_rows_after_where++;

    bool found= 1;
    while (join_tab->first_unmatched && found)
    {
      /*
        The while condition is always false if join_tab is not
        the last inner join table of an outer join operation.
      */
      JOIN_TAB *first_unmatched= join_tab->first_unmatched;
      /*
        Mark that a match for current outer table is found.
        This activates push down conditional predicates attached
        to the all inner tables of the outer join.
      */
      first_unmatched->found= 1;
      for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
      {
        /*
          Check whether 'not exists' optimization can be used here.
          If  tab->table->reginfo.not_exists_optimize is set to true
          then WHERE contains a conjunctive predicate IS NULL over
          a non-nullable field of tab. When activated this predicate
          will filter out all records with matches for the left part
          of the outer join whose inner tables start from the
          first_unmatched table and include table tab. To safely use
          'not exists' optimization we have to check that the
          IS NULL predicate is really activated, i.e. all guards
          that wrap it are in the 'open' state. 
	*/  
	bool not_exists_opt_is_applicable=
               tab->table->reginfo.not_exists_optimize;
	for (JOIN_TAB *first_upper= first_unmatched->first_upper;
             not_exists_opt_is_applicable && first_upper;
             first_upper= first_upper->first_upper)
        {
          if (!first_upper->found)
            not_exists_opt_is_applicable= false;
        }
        /* Check all predicates that has just been activated. */
        /*
          Actually all predicates non-guarded by first_unmatched->found
          will be re-evaluated again. It could be fixed, but, probably,
          it's not worth doing now.
        */
        if (tab->select_cond && !tab->select_cond->val_int())
        {
          /* The condition attached to table tab is false */
          if (tab == join_tab)
          {
            found= 0;
            if (not_exists_opt_is_applicable)
              DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
          }            
          else
          {
            /*
              Set a return point if rejected predicate is attached
              not to the last table of the current nest level.
            */
            join->return_tab= tab;
            if (not_exists_opt_is_applicable)
              DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
            else
              DBUG_RETURN(NESTED_LOOP_OK);
          }
        }
      }
      /*
        Check whether join_tab is not the last inner table
        for another embedding outer join.
      */
      if ((first_unmatched= first_unmatched->first_upper) &&
          first_unmatched->last_inner != join_tab)
        first_unmatched= 0;
      join_tab->first_unmatched= first_unmatched;
    }

    JOIN_TAB *return_tab= join->return_tab;
    join_tab->found_match= TRUE;

    if (join_tab->check_weed_out_table && found)
    {
      int res= join_tab->check_weed_out_table->sj_weedout_check_row(join->thd);
      DBUG_PRINT("info", ("weedout_check: %d", res));
      if (res == -1)
        DBUG_RETURN(NESTED_LOOP_ERROR);
      else if (res == 1)
        found= FALSE;
    }
    else if (join_tab->do_firstmatch)
    {
      /* 
        We should return to the join_tab->do_firstmatch after we have 
        enumerated all the suffixes for current prefix row combination
      */
      return_tab= join_tab->do_firstmatch;
    }

    /*
      It was not just a return to lower loop level when one
      of the newly activated predicates is evaluated as false
      (See above join->return_tab= tab).
    */
    join->join_examined_rows++;
    DBUG_PRINT("counts", ("join->examined_rows++: %lu  found: %d",
                          (ulong) join->join_examined_rows, (int) found));

    if (found)
    {
      enum enum_nested_loop_state rc;
      /* A match from join_tab is found for the current partial join. */
      rc= (*join_tab->next_select)(join, join_tab+1, 0);
      join->thd->get_stmt_da()->inc_current_row_for_warning();
      if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
        DBUG_RETURN(rc);
      if (return_tab < join->return_tab)
        join->return_tab= return_tab;

      /* check for errors evaluating the condition */
      if (unlikely(join->thd->is_error()))
        DBUG_RETURN(NESTED_LOOP_ERROR);

      if (join->return_tab < join_tab)
        DBUG_RETURN(NESTED_LOOP_OK);
      /*
        Test if this was a SELECT DISTINCT query on a table that
        was not in the field list;  In this case we can abort if
        we found a row, as no new rows can be added to the result.
      */
      if (shortcut_for_distinct && found_records != join->found_records)
        DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
    }
    else
    {
      join->thd->get_stmt_da()->inc_current_row_for_warning();
      join_tab->read_record.unlock_row(join_tab);
    }
  }
  else
  {
    /*
      The condition pushed down to the table join_tab rejects all rows
      with the beginning coinciding with the current partial join.
    */
    join->join_examined_rows++;
    join->thd->get_stmt_da()->inc_current_row_for_warning();
    join_tab->read_record.unlock_row(join_tab);
  }
  DBUG_RETURN(NESTED_LOOP_OK);
}

Project: server

static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list, 
                                          uint first_unused)
{
  List_iterator<TABLE_LIST> li(*join_list);
  TABLE_LIST *table;
  DBUG_ENTER("build_bitmap_for_nested_joins");
  while ((table= li++))
  {
    NESTED_JOIN *nested_join;
    if ((nested_join= table->nested_join))
    {
      /*
        It is guaranteed by simplify_joins() function that a nested join
        that has only one child represents a single table VIEW (and the child
        is an underlying table). We don't assign bits to such nested join
        structures because 
        1. it is redundant (a "sequence" of one table cannot be interleaved 
            with anything)
        2. we could run out bits in nested_join_map otherwise.
      */
      if (nested_join->n_tables != 1)
      {
        /* Don't assign bits to sj-nests */
        if (table->on_expr)
          nested_join->nj_map= (nested_join_map) 1 << first_unused++;
        first_unused= build_bitmap_for_nested_joins(&nested_join->join_list,
                                                    first_unused);
      }
    }
  }
  DBUG_RETURN(first_unused);
}

Project: server

bool JOIN::build_explain()
{
  have_query_plan= QEP_AVAILABLE;

  /*
    explain data must be created on the Explain_query::mem_root. Because it's
    just a memroot, not an arena, explain data must not contain any Items
  */
  MEM_ROOT *old_mem_root= thd->mem_root;
  Item *old_free_list __attribute__((unused))= thd->free_list;
  thd->mem_root= thd->lex->explain->mem_root;
  bool res= save_explain_data(thd->lex->explain, false /* can overwrite */,
                        need_tmp,
                        !skip_sort_order && !no_order && (order || group_list),
                        select_distinct);
  thd->mem_root= old_mem_root;
  DBUG_ASSERT(thd->free_list == old_free_list); // no Items were created
  if (res)
    return 1;

  uint select_nr= select_lex->select_number;
  JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
  for (uint i= 0; i < aggr_tables; i++, curr_tab++)
  {
    if (select_nr == INT_MAX)
    {
      /* this is a fake_select_lex of a union */
      select_nr= select_lex->master_unit()->first_select()->select_number;
      curr_tab->tracker= thd->lex->explain->get_union(select_nr)->
                         get_tmptable_read_tracker();
    }
    else
    {
      curr_tab->tracker= thd->lex->explain->get_select(select_nr)->
                         get_using_temporary_read_tracker();
    }
  }
  return 0;
}

Project: server

inline void JOIN::eval_select_list_used_tables()
{
  select_list_used_tables= 0;
  Item *item;
  List_iterator_fast<Item> it(fields_list);
  while ((item= it++))
  {
    select_list_used_tables|= item->used_tables();
  }
  Item_outer_ref *ref;
  List_iterator_fast<Item_outer_ref> ref_it(select_lex->inner_refs_list);
  while ((ref= ref_it++))
  {
    item= ref->outer_ref;
    select_list_used_tables|= item->used_tables();
  }
}

Project: server

JOIN::prepare(TABLE_LIST *tables_init,
	      uint wild_num, COND *conds_init, uint og_num,
	      ORDER *order_init, bool skip_order_by,
              ORDER *group_init, Item *having_init,
	      ORDER *proc_param_init, SELECT_LEX *select_lex_arg,
	      SELECT_LEX_UNIT *unit_arg)
{
  DBUG_ENTER("JOIN::prepare");

  // to prevent double initialization on EXPLAIN
  if (optimization_state != JOIN::NOT_OPTIMIZED)
    DBUG_RETURN(0);

  conds= conds_init;
  order= order_init;
  group_list= group_init;
  having= having_init;
  proc_param= proc_param_init;
  tables_list= tables_init;
  select_lex= select_lex_arg;
  select_lex->join= this;
  join_list= &select_lex->top_join_list;
  union_part= unit_arg->is_unit_op();

  // simple check that we got usable conds
  dbug_print_item(conds);

  if (select_lex->handle_derived(thd->lex, DT_PREPARE))
    DBUG_RETURN(-1);

  thd->lex->current_select->context_analysis_place= NO_MATTER;
  thd->lex->current_select->is_item_list_lookup= 1;
  /*
    If we have already executed SELECT, then it have not sense to prevent
    its table from update (see unique_table())
    Affects only materialized derived tables.
  */
  /* Check that all tables, fields, conds and order are ok */
  if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
      setup_tables_and_check_access(thd, &select_lex->context, join_list,
                                    tables_list, select_lex->leaf_tables,
                                    FALSE, SELECT_ACL, SELECT_ACL, FALSE))
      DBUG_RETURN(-1);

  /* System Versioning: handle FOR SYSTEM_TIME clause. */
  if (select_lex->vers_setup_conds(thd, tables_list) < 0)
    DBUG_RETURN(-1);

  /*
    TRUE if the SELECT list mixes elements with and without grouping,
    and there is no GROUP BY clause. Mixing non-aggregated fields with
    aggregate functions in the SELECT list is a MySQL extenstion that
    is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set.
  */
  mixed_implicit_grouping= false;
  if ((~thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) &&
      select_lex->with_sum_func && !group_list)
  {
    List_iterator_fast <Item> select_it(fields_list);
    Item *select_el; /* Element of the SELECT clause, can be an expression. */
    bool found_field_elem= false;
    bool found_sum_func_elem= false;

    while ((select_el= select_it++))
    {
      if (select_el->with_sum_func)
        found_sum_func_elem= true;
      if (select_el->with_field)
        found_field_elem= true;
      if (found_sum_func_elem && found_field_elem)
      {
        mixed_implicit_grouping= true;
        break;
      }
    }
  }

  table_count= select_lex->leaf_tables.elements;

  TABLE_LIST *tbl;
  List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
  while ((tbl= li++))
  {
    /*
      If the query uses implicit grouping where the select list contains both
      aggregate functions and non-aggregate fields, any non-aggregated field
      may produce a NULL value. Set all fields of each table as nullable before
      semantic analysis to take into account this change of nullability.

      Note: this loop doesn't touch tables inside merged semi-joins, because
      subquery-to-semijoin conversion has not been done yet. This is intended.
    */
    if (mixed_implicit_grouping && tbl->table)
      tbl->table->maybe_null= 1;
  }
 
  uint real_og_num= og_num;
  if (skip_order_by && 
      select_lex != select_lex->master_unit()->global_parameters())
    real_og_num+= select_lex->order_list.elements;

  DBUG_ASSERT(select_lex->hidden_bit_fields == 0);
  if (setup_wild(thd, tables_list, fields_list, &all_fields, wild_num,
                 &select_lex->hidden_bit_fields))
    DBUG_RETURN(-1);
  if (select_lex->setup_ref_array(thd, real_og_num))
    DBUG_RETURN(-1);

  ref_ptrs= ref_ptr_array_slice(0);
  
  enum_parsing_place save_place=
                     thd->lex->current_select->context_analysis_place;
  thd->lex->current_select->context_analysis_place= SELECT_LIST;
  if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ,
                   &all_fields, &select_lex->pre_fix, 1))
    DBUG_RETURN(-1);
  thd->lex->current_select->context_analysis_place= save_place;

  if (setup_without_group(thd, ref_ptrs, tables_list,
                          select_lex->leaf_tables, fields_list,
                          all_fields, &conds, order, group_list,
                          select_lex->window_specs,
                          select_lex->window_funcs,
                          &hidden_group_fields,
                          &select_lex->select_n_reserved))
    DBUG_RETURN(-1);

  /*
    Permanently remove redundant parts from the query if
      1) This is a subquery
      2) This is the first time this query is optimized (since the
         transformation is permanent
      3) Not normalizing a view. Removal should take place when a
         query involving a view is optimized, not when the view
         is created
  */
  if (select_lex->master_unit()->item &&                               // 1)
      select_lex->first_cond_optimization &&                           // 2)
      !thd->lex->is_view_context_analysis())                           // 3)
  {
    remove_redundant_subquery_clauses(select_lex);
  }

  /* Resolve the ORDER BY that was skipped, then remove it. */
  if (skip_order_by && select_lex !=
                       select_lex->master_unit()->global_parameters())
  {
    nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
    thd->lex->allow_sum_func.set_bit(select_lex->nest_level);
    thd->where= "order clause";
    for (ORDER *order= select_lex->order_list.first; order; order= order->next)
    {
      /* Don't add the order items to all fields. Just resolve them to ensure
         the query is valid, we'll drop them immediately after. */
      if (find_order_in_list(thd, ref_ptrs, tables_list, order,
                             fields_list, all_fields, false, false, false))
        DBUG_RETURN(-1);
    }
    thd->lex->allow_sum_func= save_allow_sum_func;
    select_lex->order_list.empty();
  }

  if (having)
  {
    nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
    thd->where="having clause";
    thd->lex->allow_sum_func.set_bit(select_lex_arg->nest_level);
    select_lex->having_fix_field= 1;
    /*
      Wrap alone field in HAVING clause in case it will be outer field
      of subquery which need persistent pointer on it, but having
      could be changed by optimizer
    */
    if (having->type() == Item::REF_ITEM &&
        ((Item_ref *)having)->ref_type() == Item_ref::REF)
      wrap_ident(thd, &having);
    bool having_fix_rc= having->fix_fields_if_needed_for_bool(thd, &having);
    select_lex->having_fix_field= 0;

    if (unlikely(having_fix_rc || thd->is_error()))
      DBUG_RETURN(-1);				/* purecov: inspected */
    thd->lex->allow_sum_func= save_allow_sum_func;

    if (having->with_window_func)
    {
      my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
      DBUG_RETURN(-1); 
    }
  }

  /*
     After setting up window functions, we may have discovered additional
     used tables from the PARTITION BY and ORDER BY list. Update all items
     that contain window functions.
  */
  if (select_lex->have_window_funcs())
  {
    List_iterator_fast<Item> it(select_lex->item_list);
    Item *item;
    while ((item= it++))
    {
      if (item->with_window_func)
        item->update_used_tables();
    }
  }

  With_clause *with_clause=select_lex->get_with_clause();
  if (with_clause && with_clause->prepare_unreferenced_elements(thd))
    DBUG_RETURN(1);

  With_element *with_elem= select_lex->get_with_element();
  if (with_elem &&
      select_lex->check_unrestricted_recursive(
                      thd->variables.only_standard_compliant_cte))
    DBUG_RETURN(-1);
  if (!(select_lex->changed_elements & TOUCHED_SEL_COND))
    select_lex->check_subqueries_with_recursive_references();
  
  int res= check_and_do_in_subquery_rewrites(this);

  select_lex->fix_prepare_information(thd, &conds, &having);
  
  if (res)
    DBUG_RETURN(res);

  if (order)
  {
    bool real_order= FALSE;
    ORDER *ord;
    for (ord= order; ord; ord= ord->next)
    {
      Item *item= *ord->item;
      /*
        Disregard sort order if there's only 
        zero length NOT NULL fields (e.g. {VAR}CHAR(0) NOT NULL") or
        zero length NOT NULL string functions there.
        Such tuples don't contain any data to sort.
      */
      if (!real_order &&
           /* Not a zero length NOT NULL field */
          ((item->type() != Item::FIELD_ITEM ||
            ((Item_field *) item)->field->maybe_null() ||
            ((Item_field *) item)->field->sort_length()) &&
           /* AND not a zero length NOT NULL string function. */
           (item->type() != Item::FUNC_ITEM ||
            item->maybe_null ||
            item->result_type() != STRING_RESULT ||
            item->max_length)))
        real_order= TRUE;

      if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
          item->with_window_func)
        item->split_sum_func(thd, ref_ptrs, all_fields, SPLIT_SUM_SELECT);
    }
    if (!real_order)
      order= NULL;
  }

  if (having && having->with_sum_func)
    having->split_sum_func2(thd, ref_ptrs, all_fields,
                            &having, SPLIT_SUM_SKIP_REGISTERED);
  if (select_lex->inner_sum_func_list)
  {
    Item_sum *end=select_lex->inner_sum_func_list;
    Item_sum *item_sum= end;  
    do
    { 
      item_sum= item_sum->next;
      item_sum->split_sum_func2(thd, ref_ptrs,
                                all_fields, item_sum->ref_by, 0);
    } while (item_sum != end);
  }

  if (select_lex->inner_refs_list.elements &&
      fix_inner_refs(thd, all_fields, select_lex, ref_ptrs))
    DBUG_RETURN(-1);

  if (group_list)
  {
    /*
      Because HEAP tables can't index BIT fields we need to use an
      additional hidden field for grouping because later it will be
      converted to a LONG field. Original field will remain of the
      BIT type and will be returned to a client.
    */
    for (ORDER *ord= group_list; ord; ord= ord->next)
    {
      if ((*ord->item)->type() == Item::FIELD_ITEM &&
          (*ord->item)->field_type() == MYSQL_TYPE_BIT)
      {
        Item_field *field= new (thd->mem_root) Item_field(thd, *(Item_field**)ord->item);
        if (!field)
          DBUG_RETURN(-1);
        int el= all_fields.elements;
        ref_ptrs[el]= field;
        all_fields.push_front(field, thd->mem_root);
        ord->item= &ref_ptrs[el];
      }
    }
  }

  /*
    Check if there are references to un-aggregated columns when computing 
    aggregate functions with implicit grouping (there is no GROUP BY).
  */
  if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list &&
      !(select_lex->master_unit()->item &&
        select_lex->master_unit()->item->is_in_predicate() &&
        ((Item_in_subselect*)select_lex->master_unit()->item)->
        test_set_strategy(SUBS_MAXMIN_INJECTED)) &&
      select_lex->non_agg_field_used() &&
      select_lex->agg_func_used())
  {
    my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
               ER_THD(thd, ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0));
    DBUG_RETURN(-1);
  }
  {
    /* Caclulate the number of groups */
    send_group_parts= 0;
    for (ORDER *group_tmp= group_list ; group_tmp ; group_tmp= group_tmp->next)
      send_group_parts++;
  }
  
  procedure= setup_procedure(thd, proc_param, result, fields_list, &error);
  if (unlikely(error))
    goto err;					/* purecov: inspected */
  if (procedure)
  {
    if (setup_new_fields(thd, fields_list, all_fields,
			 procedure->param_fields))
	goto err;				/* purecov: inspected */
    if (procedure->group)
    {
      if (!test_if_subpart(procedure->group,group_list))
      {						/* purecov: inspected */
	my_message(ER_DIFF_GROUPS_PROC, ER_THD(thd, ER_DIFF_GROUPS_PROC),
                   MYF(0));                     /* purecov: inspected */
	goto err;				/* purecov: inspected */
      }
    }
    if (order && (procedure->flags & PROC_NO_SORT))
    {						/* purecov: inspected */
      my_message(ER_ORDER_WITH_PROC, ER_THD(thd, ER_ORDER_WITH_PROC),
                 MYF(0));                       /* purecov: inspected */
      goto err;					/* purecov: inspected */
    }
    if (thd->lex->derived_tables)
    {
      /*
        Queries with derived tables and PROCEDURE are not allowed.
        Many of such queries are disallowed grammatically, but there
        are still some complex cases:
          SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE()
      */
      my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", 
               thd->lex->derived_tables & DERIVED_VIEW ?
               "view" : "subquery"); 
      goto err;
    }
    if (thd->lex->sql_command != SQLCOM_SELECT)
    {
      // EXPLAIN SELECT * FROM t1 PROCEDURE ANALYSE()
      my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "non-SELECT");
      goto err;
    }
  }

  if (!procedure && result && result->prepare(fields_list, unit_arg))
    goto err;					/* purecov: inspected */

  unit= unit_arg;
  if (prepare_stage2())
    goto err;

  DBUG_RETURN(0); // All OK

err:
  delete procedure;                /* purecov: inspected */
  procedure= 0;
  DBUG_RETURN(-1);                /* purecov: inspected */
}

Project: server

add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field,
          uint key, uint part)
{
  KEYUSE keyuse;
  Field *field= key_field->field;

  keyuse.table= field->table;
  keyuse.val= key_field->val;
  keyuse.key= key;
  if (!is_hash_join_key_no(key))
  {
    keyuse.keypart=part;
    keyuse.keypart_map= (key_part_map) 1 << part;
  }
  else
  {
    keyuse.keypart= field->field_index;
    keyuse.keypart_map= (key_part_map) 0;
  }
  keyuse.used_tables= key_field->val->used_tables();
  keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
  keyuse.ref_table_rows= 0;
  keyuse.null_rejecting= key_field->null_rejecting;
  keyuse.cond_guard= key_field->cond_guard;
  keyuse.sj_pred_no= key_field->sj_pred_no;
  keyuse.validity_ref= 0;
  return (insert_dynamic(keyuse_array,(uchar*) &keyuse));
}

Project: server

make_cond_for_table(THD *thd, Item *cond, table_map tables,
                    table_map used_table,
                    int join_tab_idx_arg,
                    bool exclude_expensive_cond __attribute__((unused)),
		    bool retain_ref_cond)
{
  return make_cond_for_table_from_pred(thd, cond, cond, tables, used_table,
                                       join_tab_idx_arg,
                                       exclude_expensive_cond,
                                       retain_ref_cond, true);
}

Project: server

static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
                                         Item ***copy_func, bool modify_item)
{
  DBUG_ASSERT(thd == table->in_use);
  Field* new_field= item->create_tmp_field(false, table);
  if (new_field)
    create_tmp_field_from_item_finalize(thd, new_field, item,
                                        copy_func, modify_item);
  return new_field;
}

Project: server

ha_rows JOIN_TAB::get_examined_rows()
{
  double examined_rows;
  SQL_SELECT *sel= filesort? filesort->select : this->select;

  if (sel && sel->quick && use_quick != 2)
    examined_rows= (double)sel->quick->records;
  else if (type == JT_NEXT || type == JT_ALL ||
           type == JT_HASH || type ==JT_HASH_NEXT)
  {
    if (limit)
    {
      /*
        @todo This estimate is wrong, a LIMIT query may examine much more rows
        than the LIMIT itself.
      */
      examined_rows= (double)limit;
    }
    else
    {
      if (table->is_filled_at_execution())
        examined_rows= (double)records;
      else
      {
        /*
          handler->info(HA_STATUS_VARIABLE) has been called in
          make_join_statistics()
        */
        examined_rows= (double)table->stat_records();
      }
    }
  }
  else
    examined_rows= records_read;

  if (examined_rows >= (double) HA_ROWS_MAX)
    return HA_ROWS_MAX;
  return (ha_rows) examined_rows;
}

Project: server

add_found_match_trig_cond(THD *thd, JOIN_TAB *tab, COND *cond,
                          JOIN_TAB *root_tab)
{
  COND *tmp;
  DBUG_ASSERT(cond != 0);
  if (tab == root_tab)
    return cond;
  if ((tmp= add_found_match_trig_cond(thd, tab->first_upper, cond, root_tab)))
    tmp= new (thd->mem_root) Item_func_trig_cond(thd, tmp, &tab->found);
  if (tmp)
  {
    tmp->quick_fix_field();
    tmp->update_used_tables();
  }
  return tmp;
}

Project: server

static uint get_semi_join_select_list_index(Field *field)
{
  uint res= UINT_MAX;
  TABLE_LIST *emb_sj_nest;
  if ((emb_sj_nest= field->table->pos_in_table_list->embedding) &&
      emb_sj_nest->sj_on_expr)
  {
    Item_in_subselect *subq_pred= emb_sj_nest->sj_subq_pred;
    st_select_lex *subq_lex= subq_pred->unit->first_select();
    if (subq_pred->left_expr->cols() == 1)
    {
      Item *sel_item= subq_lex->ref_pointer_array[0];
      if (sel_item->type() == Item::FIELD_ITEM &&
          ((Item_field*)sel_item)->field->eq(field))
      {
        res= 0;
      }
    }
    else
    {
      for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
      {
        Item *sel_item= subq_lex->ref_pointer_array[i];
        if (sel_item->type() == Item::FIELD_ITEM &&
            ((Item_field*)sel_item)->field->eq(field))
        {
          res= i;
          break;
        }
      }
    }
  }
  return res;
}