do_arrow(
    unsigned int usx, unsigned int usy,   /* start point */
    unsigned int uex, unsigned int uey,   /* end point (point of arrowhead) */
    int headstyle)
{
    /* Clipping and angle calculations do not work if coords are unsigned! */
    int sx = (int)usx;
    int sy = (int)usy;
    int ex = (int)uex;
    int ey = (int)uey;

    struct termentry *t = term;
    double len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
    /* average of tic sizes */
    /* (dx,dy) : vector from end to start */
    double dx = sx - ex;
    double dy = sy - ey;
    double len_arrow = sqrt(dx * dx + dy * dy);
    gpiPoint head_points[5];
    int xm = 0, ym = 0;
    BoundingBox *clip_save;

    /* The arrow shaft was clipped already in draw_clip_arrow() but we still */
    /* need to clip the head here. */
    clip_save = clip_area;
    if (term->flags & TERM_CAN_CLIP)
	clip_area = NULL;
    else
	clip_area = &canvas;

    /* Calculate and draw arrow heads.
     * Draw no head for arrows with length = 0, or, to be more specific,
     * length < DBL_EPSILON, because len_arrow will almost always be != 0.
     */
    if ((headstyle & BOTH_HEADS) != NOHEAD && fabs(len_arrow) >= DBL_EPSILON) {
	int x1, y1, x2, y2;
	if (curr_arrow_headlength <= 0) {
	    /* An arrow head with the default size and angles */
	    double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
	    double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
	    double head_coeff = GPMAX(coeff_shortest,
				      GPMIN(HEAD_COEFF, coeff_longest));
	    /* we put the arrowhead marks at 15 degrees to line */
	    x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
	    y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
	    x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
	    y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
	    /* backangle defaults to 90 deg */
	    xm = (int) ((x1 + x2)/2);
	    ym = (int) ((y1 + y2)/2);
	} else {
	    /* An arrow head with the length + angle specified explicitly.	*/
	    /* Assume that if the arrow is shorter than the arrowhead, this is	*/
	    /* because of foreshortening in a 3D plot.                  	*/
	    double alpha = curr_arrow_headangle * DEG2RAD;
	    double beta = curr_arrow_headbackangle * DEG2RAD;
	    double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
	    double backlen, effective_length;
	    double dx2, dy2;

	    effective_length = curr_arrow_headlength;
	    if (!curr_arrow_headfixedsize && (curr_arrow_headlength > len_arrow/2.)) {
		effective_length = len_arrow/2.;
		alpha = atan(tan(alpha)*((double)curr_arrow_headlength/effective_length));
		beta = atan(tan(beta)*((double)curr_arrow_headlength/effective_length));
	    }
	    backlen = sin(alpha) / sin(beta);

	    /* anticlock-wise head segment */
	    x1 = -(int)(effective_length * cos( alpha - phi ));
	    y1 =  (int)(effective_length * sin( alpha - phi ));
	    /* clock-wise head segment */
	    dx2 = -effective_length * cos( phi + alpha );
	    dy2 = -effective_length * sin( phi + alpha );
	    x2 = (int) (dx2);
	    y2 = (int) (dy2);
	    /* back point */
	    xm = (int) (dx2 + backlen*effective_length * cos( phi + beta ));
	    ym = (int) (dy2 + backlen*effective_length * sin( phi + beta ));
	}

	if ((headstyle & END_HEAD) && !clip_point(ex, ey)) {
	    head_points[0].x = ex + xm;
	    head_points[0].y = ey + ym;
	    head_points[1].x = ex + x1;
	    head_points[1].y = ey + y1;
	    head_points[2].x = ex;
	    head_points[2].y = ey;
	    head_points[3].x = ex + x2;
	    head_points[3].y = ey + y2;
	    head_points[4].x = ex + xm;
	    head_points[4].y = ey + ym;
	    if (!((headstyle & SHAFT_ONLY))) {
		if (curr_arrow_headfilled >= AS_FILLED) {
		    /* draw filled forward arrow head */
		    head_points->style = FS_OPAQUE;
		    if (t->filled_polygon)
			(*t->filled_polygon) (5, head_points);
		}
		/* draw outline of forward arrow head */
		if (curr_arrow_headfilled == AS_NOFILL) {
		    draw_clip_polygon(3, head_points+1);
		} else if (curr_arrow_headfilled != AS_NOBORDER) {
		    draw_clip_polygon(5, head_points);
		}
	    }
	}

	/* backward arrow head */
	if ((headstyle & BACKHEAD) && !clip_point(sx,sy)) {
	    head_points[0].x = sx - xm;
	    head_points[0].y = sy - ym;
	    head_points[1].x = sx - x1;
	    head_points[1].y = sy - y1;
	    head_points[2].x = sx;
	    head_points[2].y = sy;
	    head_points[3].x = sx - x2;
	    head_points[3].y = sy - y2;
	    head_points[4].x = sx - xm;
	    head_points[4].y = sy - ym;
	    if (!((headstyle & SHAFT_ONLY))) {
		if (curr_arrow_headfilled >= AS_FILLED) {
		    /* draw filled backward arrow head */
		    head_points->style = FS_OPAQUE;
		    if (t->filled_polygon)
			(*t->filled_polygon) (5, head_points);
		}
		/* draw outline of backward arrow head */
		if (curr_arrow_headfilled == AS_NOFILL) {
		    draw_clip_polygon(3, head_points+1);
		} else if (curr_arrow_headfilled != AS_NOBORDER) {
		    draw_clip_polygon(5, head_points);
		}
	    }
	}
    }

    /* Adjust the length of the shaft so that it doesn't overlap the head */
    if ((headstyle & BACKHEAD)
    &&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
	sx -= xm;
	sy -= ym;
    }
    if ((headstyle & END_HEAD)
    &&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
	ex += xm;
	ey += ym;
    }

    /* Draw the line for the arrow. */
    if (!((headstyle & HEADS_ONLY)))
	draw_clip_line(sx, sy, ex, ey);

    /* Restore previous clipping box */
    clip_area = clip_save;
}


null_layer(t_termlayer layer)
{
    (void) layer;               /* avoid -Wunused warning */
}

Better error handling for faulty font syntax

A missing close-quote in an enhanced text font specification could
cause a segfault.
Bug #2303


join_ft_read_first(JOIN_TAB *tab)
{
  int error;
  TABLE *table= tab->table;

  if (!table->file->inited &&
      (error= table->file->ha_index_init(tab->ref.key, 1)))
  {
    (void) report_error(table, error);
    return 1;
  }

  table->file->ft_init();

  if (unlikely((error= table->file->ha_ft_read(table->record[0]))))
    return report_error(table, error);
  return 0;
}

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.


join_tab_cmp_straight(const void *dummy, const void* ptr1, const void* ptr2)
{
  JOIN_TAB *jt1= *(JOIN_TAB**) ptr1;
  JOIN_TAB *jt2= *(JOIN_TAB**) ptr2;

  /*
    We don't do subquery flattening if the parent or child select has
    STRAIGHT_JOIN modifier. It is complicated to implement and the semantics
    is hardly useful.
  */
  DBUG_ASSERT(!jt1->emb_sj_nest);
  DBUG_ASSERT(!jt2->emb_sj_nest);

  int cmp;
  if ((cmp= compare_embedding_subqueries(jt1, jt2)) != 0)
    return cmp;

  if (jt1->dependent & jt2->table->map)
    return 1;
  if (jt2->dependent & jt1->table->map)
    return -1;
  return jt1 > jt2 ? 1 : (jt1 < jt2 ? -1 : 0);
}


bool JOIN::rollup_process_const_fields()
{
  ORDER *group_tmp;
  Item *item;
  List_iterator<Item> it(all_fields);

  for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next)
  {
    if (!(*group_tmp->item)->const_item())
      continue;
    while ((item= it++))
    {
      if (*group_tmp->item == item)
      {
        Item* new_item= new (thd->mem_root) Item_func_rollup_const(thd, item);
        if (!new_item)
          return 1;
        new_item->fix_fields(thd, (Item **) 0);
        thd->change_item_tree(it.ref(), new_item);
        for (ORDER *tmp= group_tmp; tmp; tmp= tmp->next)
        {
          if (*tmp->item == item)
            thd->change_item_tree(tmp->item, new_item);
        }
        break;
      }
    }
    it.rewind();
  }
  return 0;
}


setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
		  Ref_ptr_array ref_pointer_array,
		  List<Item> &res_selected_fields, List<Item> &res_all_fields,
		  uint elements, List<Item> &all_fields)
{
  Item *pos;
  List_iterator_fast<Item> li(all_fields);
  Copy_field *copy= NULL;
  Copy_field *copy_start __attribute__((unused));
  res_selected_fields.empty();
  res_all_fields.empty();
  List_iterator_fast<Item> itr(res_all_fields);
  List<Item> extra_funcs;
  uint i, border= all_fields.elements - elements;
  DBUG_ENTER("setup_copy_fields");

  if (param->field_count &&
      !(copy=param->copy_field= new (thd->mem_root) Copy_field[param->field_count]))
    goto err2;

  param->copy_funcs.empty();
  copy_start= copy;
  for (i= 0; (pos= li++); i++)
  {
    Field *field;
    uchar *tmp;
    Item *real_pos= pos->real_item();
    /*
      Aggregate functions can be substituted for fields (by e.g. temp tables).
      We need to filter those substituted fields out.
    */
    if (real_pos->type() == Item::FIELD_ITEM &&
        !(real_pos != pos &&
          ((Item_ref *)pos)->ref_type() == Item_ref::AGGREGATE_REF))
    {
      Item_field *item;
      if (!(item= new (thd->mem_root) Item_field(thd, ((Item_field*) real_pos))))
	goto err;
      if (pos->type() == Item::REF_ITEM)
      {
        /* preserve the names of the ref when dereferncing */
        Item_ref *ref= (Item_ref *) pos;
        item->db_name= ref->db_name;
        item->table_name= ref->table_name;
        item->name= ref->name;
      }
      pos= item;
      if (item->field->flags & BLOB_FLAG)
      {
	if (!(pos= new (thd->mem_root) Item_copy_string(thd, pos)))
	  goto err;
       /*
         Item_copy_string::copy for function can call
         Item_copy_string::val_int for blob via Item_ref.
         But if Item_copy_string::copy for blob isn't called before,
         it's value will be wrong
         so let's insert Item_copy_string for blobs in the beginning of
         copy_funcs
         (to see full test case look at having.test, BUG #4358)
       */
	if (param->copy_funcs.push_front(pos, thd->mem_root))
	  goto err;
      }
      else
      {
	/*
	   set up save buffer and change result_field to point at
	   saved value
	*/
	field= item->field;
	item->result_field=field->make_new_field(thd->mem_root,
                                                 field->table, 1);
        /*
          We need to allocate one extra byte for null handling and
          another extra byte to not get warnings from purify in
          Field_string::val_int
        */
	if (!(tmp= (uchar*) thd->alloc(field->pack_length()+2)))
	  goto err;
        if (copy)
        {
          DBUG_ASSERT (param->field_count > (uint) (copy - copy_start));
          copy->set(tmp, item->result_field);
          item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
#ifdef HAVE_valgrind
          copy->to_ptr[copy->from_length]= 0;
#endif
          copy++;
        }
      }
    }
    else if ((real_pos->type() == Item::FUNC_ITEM ||
	      real_pos->real_type() == Item::SUBSELECT_ITEM ||
	      real_pos->type() == Item::CACHE_ITEM ||
	      real_pos->type() == Item::COND_ITEM) &&
	     !real_pos->with_sum_func)
    {						// Save for send fields
      LEX_CSTRING real_name= pos->name;
      pos= real_pos;
      pos->name= real_name;
      /* TODO:
	 In most cases this result will be sent to the user.
	 This should be changed to use copy_int or copy_real depending
	 on how the value is to be used: In some cases this may be an
	 argument in a group function, like: IF(ISNULL(col),0,COUNT(*))
      */
      if (!(pos=new (thd->mem_root) Item_copy_string(thd, pos)))
	goto err;
      if (i < border)                           // HAVING, ORDER and GROUP BY
      {
        if (extra_funcs.push_back(pos, thd->mem_root))
          goto err;
      }
      else if (param->copy_funcs.push_back(pos, thd->mem_root))
	goto err;
    }
    res_all_fields.push_back(pos, thd->mem_root);
    ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
      pos;
  }
  param->copy_field_end= copy;

  for (i= 0; i < border; i++)
    itr++;
  itr.sublist(res_selected_fields, elements);
  /*
    Put elements from HAVING, ORDER BY and GROUP BY last to ensure that any
    reference used in these will resolve to a item that is already calculated
  */
  param->copy_funcs.append(&extra_funcs);

  DBUG_RETURN(0);

 err:
  if (copy)
    delete [] param->copy_field;			// This is never 0
  param->copy_field= 0;
err2:
  DBUG_RETURN(TRUE);
}


int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
{
  int error;
  if (!table->file->inited)
  {
    error= table->file->ha_index_init(table_ref->key, tab ? tab->sorted : TRUE);
    if (unlikely(error))
    {
      (void) report_error(table, error);
      return 1;
    }
  }

  /*
    The following is needed when one makes ref (or eq_ref) access from row
    comparisons: one must call row->bring_value() to get the new values.
  */
  if (tab && tab->bush_children)
  {
    TABLE_LIST *emb_sj_nest= tab->bush_children->start->emb_sj_nest;
    emb_sj_nest->sj_subq_pred->left_expr->bring_value();
  }

  /* TODO: Why don't we do "Late NULLs Filtering" here? */

  if (cmp_buffer_with_ref(thd, table, table_ref) ||
      (table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
  {
    if (table_ref->key_err)
    {
      table->status=STATUS_NOT_FOUND;
      return -1;
    }
    /*
      Moving away from the current record. Unlock the row
      in the handler if it did not match the partial WHERE.
    */
    if (tab && tab->ref.has_record && tab->ref.use_count == 0)
    {
      tab->read_record.table->file->unlock_row();
      table_ref->has_record= FALSE;
    }
    error=table->file->ha_index_read_map(table->record[0],
                                  table_ref->key_buff,
                                  make_prev_keypart_map(table_ref->key_parts),
                                  HA_READ_KEY_EXACT);
    if (unlikely(error) &&
        error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
      return report_error(table, error);

    if (likely(!error))
    {
      table_ref->has_record= TRUE;
      table_ref->use_count= 1;
    }
  }
  else if (table->status == 0)
  {
    DBUG_ASSERT(table_ref->has_record);
    table_ref->use_count++;
  }
  table->null_row=0;
  return table->status ? -1 : 0;
}


void JOIN_TAB::cleanup()
{
  DBUG_ENTER("JOIN_TAB::cleanup");

  DBUG_PRINT("enter", ("tab: %p  table %s.%s",
                       this,
                       (table ? table->s->db.str : "?"),
                       (table ? table->s->table_name.str : "?")));
  delete select;
  select= 0;
  delete quick;
  quick= 0;
  if (cache)
  {
    cache->free();
    cache= 0;
  }
  limit= 0;
  // Free select that was created for filesort outside of create_sort_index
  if (filesort && filesort->select && !filesort->own_select)
    delete filesort->select;
  delete filesort;
  filesort= NULL;
  /* Skip non-existing derived tables/views result tables */
  if (table &&
      (table->s->tmp_table != INTERNAL_TMP_TABLE || table->is_created()))
  {
    table->file->ha_end_keyread();
    table->file->ha_index_or_rnd_end();
  }
  if (table)
  {
    table->file->ha_end_keyread();
    if (type == JT_FT)
      table->file->ha_ft_end();
    else
      table->file->ha_index_or_rnd_end();
    preread_init_done= FALSE;
    if (table->pos_in_table_list &&
        table->pos_in_table_list->jtbm_subselect)
    {
      if (table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab)
      {
        /*
          Set this to NULL so that cleanup_empty_jtbm_semi_joins() doesn't
          attempt to make another free_tmp_table call.
        */
        table->pos_in_table_list->table= NULL;
        free_tmp_table(join->thd, table);
        table= NULL;
      }
      else
      {
        TABLE_LIST *tmp= table->pos_in_table_list;
        end_read_record(&read_record);
        tmp->jtbm_subselect->cleanup();
        /*
          The above call freed the materializedd temptable. Set it to NULL so
          that we don't attempt to touch it if JOIN_TAB::cleanup() is invoked
          multiple times (it may be)
        */
        tmp->table= NULL;
        table= NULL;
      }
      DBUG_VOID_RETURN;
    }
    /*
      We need to reset this for next select
      (Tested in part_of_refkey)
    */
    table->reginfo.join_tab= 0;
  }
  end_read_record(&read_record);
  explain_plan= NULL;
  DBUG_VOID_RETURN;
}


void JOIN::restore_query_plan(Join_plan_state *restore_from)
{
  DYNAMIC_ARRAY tmp_keyuse;
  tmp_keyuse= keyuse;
  keyuse= restore_from->keyuse;
  restore_from->keyuse= tmp_keyuse;

  for (uint i= 0; i < table_count; i++)
  {
    join_tab[i].keyuse= restore_from->join_tab_keyuse[i];
    join_tab[i].checked_keys= restore_from->join_tab_checked_keys[i];
  }

  memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions,
         sizeof(POSITION) * (table_count + 1));
  /* Restore SJM nests */
  List_iterator<TABLE_LIST> it(select_lex->sj_nests);
  TABLE_LIST *tlist;
  SJ_MATERIALIZATION_INFO **p_info= restore_from->sj_mat_info;
  while ((tlist= it++))
  {
    tlist->sj_mat_info= *(p_info++);
  }
}


JOIN::reoptimize(Item *added_where, table_map join_tables,
                 Join_plan_state *save_to)
{
  DYNAMIC_ARRAY added_keyuse;
  SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */
  uint org_keyuse_elements;

  /* Re-run the REF optimizer to take into account the new conditions. */
  if (update_ref_and_keys(thd, &added_keyuse, join_tab, table_count, added_where,
                          ~outer_join, select_lex, &sargables))
  {
    delete_dynamic(&added_keyuse);
    return REOPT_ERROR;
  }

  if (!added_keyuse.elements)
  {
    delete_dynamic(&added_keyuse);
    return REOPT_OLD_PLAN;
  }

  if (save_to)
    save_query_plan(save_to);
  else
    reset_query_plan();

  if (!keyuse.buffer &&
      my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64,
                            MYF(MY_THREAD_SPECIFIC)))
  {
    delete_dynamic(&added_keyuse);
    return REOPT_ERROR;
  }

  org_keyuse_elements= save_to ? save_to->keyuse.elements : keyuse.elements;
  allocate_dynamic(&keyuse, org_keyuse_elements + added_keyuse.elements);

  /* If needed, add the access methods from the original query plan. */
  if (save_to)
  {
    DBUG_ASSERT(!keyuse.elements);
    keyuse.elements= save_to->keyuse.elements;
    if (size_t e= keyuse.elements)
      memcpy(keyuse.buffer,
             save_to->keyuse.buffer, e * keyuse.size_of_element);
  }

  /* Add the new access methods to the keyuse array. */
  memcpy(keyuse.buffer + keyuse.elements * keyuse.size_of_element,
         added_keyuse.buffer,
         (size_t) added_keyuse.elements * added_keyuse.size_of_element);
  keyuse.elements+= added_keyuse.elements;
  /* added_keyuse contents is copied, and it is no longer needed. */
  delete_dynamic(&added_keyuse);

  if (sort_and_filter_keyuse(thd, &keyuse, true))
    return REOPT_ERROR;
  optimize_keyuse(this, &keyuse);

  if (optimize_semijoin_nests(this, join_tables))
    return REOPT_ERROR;

  /* Re-run the join optimizer to compute a new query plan. */
  if (choose_plan(this, join_tables))
    return REOPT_ERROR;

  return REOPT_NEW_PLAN;
}


static void add_not_null_conds(JOIN *join)
{
  JOIN_TAB *tab;
  DBUG_ENTER("add_not_null_conds");

  for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
       tab;
       tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
  {
    if (tab->type == JT_REF || tab->type == JT_EQ_REF ||
        tab->type == JT_REF_OR_NULL)
    {
      for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++)
      {
        if (tab->ref.null_rejecting & ((key_part_map)1 << keypart))
        {
          Item *item= tab->ref.items[keypart];
          Item *notnull;
          Item *real= item->real_item();
	  if (real->const_item() && real->type() != Item::FIELD_ITEM &&
              !real->is_expensive())
          {
            /*
              It could be constant instead of field after constant
              propagation.
            */
            continue;
          }
          DBUG_ASSERT(real->type() == Item::FIELD_ITEM);
          Item_field *not_null_item= (Item_field*)real;
          JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab;
          /*
            For UPDATE queries such as:
            UPDATE t1 SET t1.f2=(SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1);
            not_null_item is the t1.f1, but it's referred_tab is 0.
          */
          if (!(notnull= new (join->thd->mem_root)
                Item_func_isnotnull(join->thd, item)))
            DBUG_VOID_RETURN;
          /*
            We need to do full fix_fields() call here in order to have correct
            notnull->const_item(). This is needed e.g. by test_quick_select
            when it is called from make_join_select after this function is
            called.
          */
          if (notnull->fix_fields(join->thd, &notnull))
            DBUG_VOID_RETURN;

          DBUG_EXECUTE("where",print_where(notnull,
                                            (referred_tab ?
                                            referred_tab->table->alias.c_ptr() :
                                            "outer_ref_cond"),
                                            QT_ORDINARY););
          if (!tab->first_inner)
          {
            COND *new_cond= (referred_tab && referred_tab->join == join) ?
                              referred_tab->select_cond :
                              join->outer_ref_cond;
            add_cond_and_fix(join->thd, &new_cond, notnull);
            if (referred_tab && referred_tab->join == join)
              referred_tab->set_select_cond(new_cond, __LINE__);
            else
              join->outer_ref_cond= new_cond;
          }
          else
            add_cond_and_fix(join->thd, tab->first_inner->on_expr_ref, notnull);
        }
      }
    }
  }
  DBUG_VOID_RETURN;
}


static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
                                             COND *cond,
                                             COND_EQUAL *cond_equal,
                                             void *table_join_idx)
{
  Item_equal *item_equal;
  COND *org_cond= cond;                 // Return this in case of fatal error

  if (cond->type() == Item::COND_ITEM)
  {
    List<Item> *cond_list= ((Item_cond*) cond)->argument_list();

    bool and_level= ((Item_cond*) cond)->functype() ==
                      Item_func::COND_AND_FUNC;
    if (and_level)
    {
      cond_equal= &((Item_cond_and *) cond)->m_cond_equal;
      cond_list->disjoin((List<Item> *) &cond_equal->current_level);/* remove Item_equal objects from the AND. */

      List_iterator_fast<Item_equal> it(cond_equal->current_level);
      while ((item_equal= it++))
      {
        item_equal->sort(&compare_fields_by_table_order, table_join_idx);
      }
    }

    List_iterator<Item> li(*cond_list);
    Item *item;
    while ((item= li++))
    {
      Item *new_item= substitute_for_best_equal_field(thd, context_tab,
                                                      item, cond_equal,
                                                      table_join_idx);
      /*
        This works OK with PS/SP re-execution as changes are made to
        the arguments of AND/OR items only
      */
      if (new_item && new_item != item)
        li.replace(new_item);
    }

    if (and_level)
    {
      COND *eq_cond= 0;
      List_iterator_fast<Item_equal> it(cond_equal->current_level);
      bool false_eq_cond= FALSE;
      while ((item_equal= it++))
      {
        eq_cond= eliminate_item_equal(thd, eq_cond, cond_equal->upper_levels,
                                      item_equal);
        if (!eq_cond)
	{
          eq_cond= 0;
          break;
        }
        else if (eq_cond->type() == Item::INT_ITEM && !eq_cond->val_bool())
	{
          /*
            This occurs when eliminate_item_equal() founds that cond is
            always false and substitutes it with Item_int 0.
            Due to this, value of item_equal will be 0, so just return it.
	  */
          cond= eq_cond;
          false_eq_cond= TRUE;
          break;
        }
      }
      if (eq_cond && !false_eq_cond)
      {
        /* Insert the generated equalities before all other conditions */
        if (eq_cond->type() == Item::COND_ITEM)
          ((Item_cond *) cond)->add_at_head(
                                  ((Item_cond *) eq_cond)->argument_list());
        else
	{
          if (cond_list->is_empty())
            cond= eq_cond;
          else
	  {
             /* Do not add an equality condition if it's always true */
             if (eq_cond->type() != Item::INT_ITEM &&
                 cond_list->push_front(eq_cond, thd->mem_root))
               eq_cond= 0;
          }
	}
      }
      if (!eq_cond)
      {
        /*
          We are out of memory doing the transformation.
          This is a fatal error now. However we bail out by returning the
          original condition that we had before we started the transformation.
	*/
	cond_list->append((List<Item> *) &cond_equal->current_level);
      }
    }
  }
  else if (cond->type() == Item::FUNC_ITEM &&
           ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
  {
    item_equal= (Item_equal *) cond;
    item_equal->sort(&compare_fields_by_table_order, table_join_idx);
    cond_equal= item_equal->upper_levels;
    if (cond_equal && cond_equal->current_level.head() == item_equal)
      cond_equal= cond_equal->upper_levels;
    cond= eliminate_item_equal(thd, 0, cond_equal, item_equal);
    return cond ? cond : org_cond;
  }
  else
  {
    while (cond_equal)
    {
      List_iterator_fast<Item_equal> it(cond_equal->current_level);
      while((item_equal= it++))
      {
        REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab};
        if (!(cond= cond->transform(thd, &Item::replace_equal_field,
                                    (uchar *) &arg)))
          return 0;
      }
      cond_equal= cond_equal->upper_levels;
    }
  }
  return cond;
}


setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
	    List<Item> &fields, List<Item> &all_fields, ORDER *order,
	    bool *hidden_group_fields, bool from_window_spec)
{
  enum_parsing_place context_analysis_place=
                     thd->lex->current_select->context_analysis_place;
  *hidden_group_fields=0;
  ORDER *ord;

  if (!order)
    return 0;				/* Everything is ok */

  uint org_fields=all_fields.elements;

  thd->where="group statement";
  for (ord= order; ord; ord= ord->next)
  {
    if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields,
                           all_fields, true, true, from_window_spec))
      return 1;
    (*ord->item)->marker= UNDEF_POS;		/* Mark found */
    if ((*ord->item)->with_sum_func && context_analysis_place == IN_GROUP_BY)
    {
      my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name());
      return 1;
    }
    if ((*ord->item)->with_window_func)
    {
      if (context_analysis_place == IN_GROUP_BY)
        my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
      else
        my_error(ER_WINDOW_FUNCTION_IN_WINDOW_SPEC, MYF(0));
      return 1;
    }
    if (from_window_spec && (*ord->item)->with_sum_func &&
        (*ord->item)->type() != Item::SUM_FUNC_ITEM)
      (*ord->item)->split_sum_func(thd, ref_pointer_array,
                                   all_fields, SPLIT_SUM_SELECT);
  }
  if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
      context_analysis_place == IN_GROUP_BY)
  {
    /*
      Don't allow one to use fields that is not used in GROUP BY
      For each select a list of field references that aren't under an
      aggregate function is created. Each field in this list keeps the
      position of the select list expression which it belongs to.

      First we check an expression from the select list against the GROUP BY
      list. If it's found there then it's ok. It's also ok if this expression
      is a constant or an aggregate function. Otherwise we scan the list
      of non-aggregated fields and if we'll find at least one field reference
      that belongs to this expression and doesn't occur in the GROUP BY list
      we throw an error. If there are no fields in the created list for a
      select list expression this means that all fields in it are used under
      aggregate functions.
    */
    Item *item;
    Item_field *field;
    int cur_pos_in_select_list= 0;
    List_iterator<Item> li(fields);
    List_iterator<Item_field> naf_it(thd->lex->current_select->join->non_agg_fields);

    field= naf_it++;
    while (field && (item=li++))
    {
      if (item->type() != Item::SUM_FUNC_ITEM && item->marker >= 0 &&
          !item->const_item() &&
          !(item->real_item()->type() == Item::FIELD_ITEM &&
            item->used_tables() & OUTER_REF_TABLE_BIT))
      {
        while (field)
        {
          /* Skip fields from previous expressions. */
          if (field->marker < cur_pos_in_select_list)
            goto next_field;
          /* Found a field from the next expression. */
          if (field->marker > cur_pos_in_select_list)
            break;
          /*
            Check whether the field occur in the GROUP BY list.
            Throw the error later if the field isn't found.
          */
          for (ord= order; ord; ord= ord->next)
            if ((*ord->item)->eq((Item*)field, 0))
              goto next_field;
          /*
            TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed
            ER_NON_GROUPING_FIELD_USED
          */
          my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), field->full_name());
          return 1;
next_field:
          field= naf_it++;
        }
      }
      cur_pos_in_select_list++;
    }
  }
  if (org_fields != all_fields.elements)
    *hidden_group_fields=1;			// group fields is not used
  return 0;
}


bool skip_setup_conds(THD *thd)
{
  return (!thd->stmt_arena->is_conventional()
          && !thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
         || thd->lex->is_view_context_analysis();
}


void rr_unlock_row(st_join_table *tab)
{
  READ_RECORD *info= &tab->read_record;
  info->table->file->unlock_row();
}


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

  ORDER *order,**prev_ptr, *tmp_order;
  table_map UNINIT_VAR(first_table); /* protected by first_is_base_table */
  table_map not_const_tables= ~join->const_table_map;
  table_map ref;
  bool first_is_base_table= FALSE;
  DBUG_ENTER("remove_const");

  /*
    Join tab is set after make_join_statistics() has been called.
    In case of one table with GROUP BY this function is called before
    join_tab is set for the GROUP_BY expression
  */
  if (join->join_tab)
  {
    if (join->join_tab[join->const_tables].table)
    {
      first_table= join->join_tab[join->const_tables].table->map;
      first_is_base_table= TRUE;
    }

    /*
      Cleanup to avoid interference of calls of this function for
      ORDER BY and GROUP BY
    */
    for (JOIN_TAB *tab= join->join_tab + join->const_tables;
         tab < join->join_tab + join->table_count;
         tab++)
      tab->cached_eq_ref_table= FALSE;

    *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1;
  }
  else
  {
    first_is_base_table= FALSE;
    first_table= 0;                     // Not used, for gcc
  }

  prev_ptr= &first_order;

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

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

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

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

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

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

            /*
              TODO: equality substitution in the context of ORDER BY is
              sometimes allowed when it is not allowed in the general case.

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

          if (!can_subst_to_first_table)
          {
            *simple_order=0;			// Must do a temp table to sort
          }
	}
      }
    }
    /* Remove ORDER BY entries that we have seen before */
    for (tmp_order= first_order;
         tmp_order != order;
         tmp_order= tmp_order->next)
    {
      if (tmp_order->item[0]->eq(order->item[0],1))
        break;
    }
    if (tmp_order != order)
      continue;                                // Duplicate order by. Remove

    if (change_list)
      *prev_ptr= order;				// use this entry
    prev_ptr= &order->next;
  }
  if (change_list)
    *prev_ptr=0;
  if (prev_ptr == &first_order)			// Nothing to sort/group
    *simple_order=1;
#ifndef DBUG_OFF
  if (unlikely(join->thd->is_error()))
    DBUG_PRINT("error",("Error from remove_const"));
#endif
  DBUG_PRINT("exit",("simple_order: %d",(int) *simple_order));
  DBUG_RETURN(first_order);
}


static bool check_simple_equality(THD *thd, const Item::Context &ctx,
                                  Item *left_item, Item *right_item,
                                  COND_EQUAL *cond_equal)
{
  Item *orig_left_item= left_item;
  Item *orig_right_item= right_item;
  if (left_item->type() == Item::REF_ITEM &&
      ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF)
  {
    if (((Item_ref*)left_item)->get_depended_from())
      return FALSE;
    if (((Item_direct_view_ref*)left_item)->get_null_ref_table() !=
        NO_NULL_TABLE && !left_item->real_item()->used_tables())
      return FALSE;
    left_item= left_item->real_item();
  }
  if (right_item->type() == Item::REF_ITEM &&
      ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF)
  {
    if (((Item_ref*)right_item)->get_depended_from())
      return FALSE;
    if (((Item_direct_view_ref*)right_item)->get_null_ref_table() !=
        NO_NULL_TABLE && !right_item->real_item()->used_tables())
      return FALSE;
    right_item= right_item->real_item();
  }
  if (left_item->type() == Item::FIELD_ITEM &&
      right_item->type() == Item::FIELD_ITEM &&
      !((Item_field*)left_item)->get_depended_from() &&
      !((Item_field*)right_item)->get_depended_from())
  {
    /* The predicate the form field1=field2 is processed */

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

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

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

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

    if (left_item_equal && left_item_equal == right_item_equal)
    {
      /*
        The equality predicate is inference of one of the existing
        multiple equalities, i.e the condition is already covered
        by upper level equalities
      */
       return TRUE;
    }

    /* Copy the found multiple equalities at the current level if needed */
    if (left_copyfl)
    {
      /* left_item_equal of an upper level contains left_item */
      left_item_equal= new (thd->mem_root) Item_equal(thd, left_item_equal);
      left_item_equal->set_context_field(((Item_field*) left_item));
      cond_equal->current_level.push_back(left_item_equal, thd->mem_root);
    }
    if (right_copyfl)
    {
      /* right_item_equal of an upper level contains right_item */
      right_item_equal= new (thd->mem_root) Item_equal(thd, right_item_equal);
      right_item_equal->set_context_field(((Item_field*) right_item));
      cond_equal->current_level.push_back(right_item_equal, thd->mem_root);
    }

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

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

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

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

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

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

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


bool TABLE_LIST::is_active_sjm()
{
  return sj_mat_info && sj_mat_info->is_used;
}


static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct)
{
  Item_sum *func;
  DBUG_ENTER("prepare_sum_aggregators");
  while ((func= *(func_ptr++)))
  {
    if (func->set_aggregator(need_distinct && func->has_with_distinct() ?
                             Aggregator::DISTINCT_AGGREGATOR :
                             Aggregator::SIMPLE_AGGREGATOR))
      DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
}


bool Virtual_tmp_table::sp_find_field_by_name(uint *idx,
                                              const LEX_CSTRING &name) const
{
  Field *f;
  for (uint i= 0; (f= field[i]); i++)
  {
    // Use the same comparison style with sp_context::find_variable()
    if (!my_strnncoll(system_charset_info,
                      (const uchar *) f->field_name.str,
                      f->field_name.length,
                      (const uchar *) name.str, name.length))
    {
      *idx= i;
      return false;
    }
  }
  return true;
}


AGGR_OP::prepare_tmp_table()
{
  TABLE *table= join_tab->table;
  JOIN *join= join_tab->join;
  int rc= 0;

  if (!join_tab->table->is_created())
  {
    if (instantiate_tmp_table(table, join_tab->tmp_table_param->keyinfo,
                              join_tab->tmp_table_param->start_recinfo,
                              &join_tab->tmp_table_param->recinfo,
                              join->select_options))
      return true;
    (void) table->file->extra(HA_EXTRA_WRITE_CACHE);
  }
  /* If it wasn't already, start index scan for grouping using table index. */
  if (!table->file->inited && table->group &&
      join_tab->tmp_table_param->sum_func_count && table->s->keys)
    rc= table->file->ha_index_init(0, 0);
  else
  {
    /* Start index scan in scanning mode */
    rc= table->file->ha_rnd_init(true);
  }
  if (rc)
  {
    table->file->print_error(rc, MYF(0));
    return true;
  }
  return false;
}


void JOIN::get_prefix_cost_and_fanout(uint n_tables,
                                      double *read_time_arg,
                                      double *record_count_arg)
{
  double record_count= 1;
  double read_time= 0.0;
  for (uint i= const_tables; i < n_tables + const_tables ; i++)
  {
    if (best_positions[i].records_read)
    {
      record_count= COST_MULT(record_count, best_positions[i].records_read);
      read_time= COST_ADD(read_time, best_positions[i].read_time);
    }
    /* TODO: Take into account condition selectivities here */
  }
  *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE;
  *record_count_arg= record_count;
}


static void add_key_fields_for_nj(JOIN *join, TABLE_LIST *nested_join_table,
                                  KEY_FIELD **end, uint *and_level,
                                  SARGABLE_PARAM **sargables)
{
  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_map tables= 0;
  TABLE_LIST *table;
  DBUG_ASSERT(nested_join_table->nested_join);

  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
        add_key_fields_for_nj(join, table, end, and_level, sargables);
    }
    else
      if (!table->on_expr)
        tables |= table->table->map;
  }
  if (nested_join_table->on_expr)
    nested_join_table->on_expr->add_key_fields(join, end, and_level, tables,
                                               sargables);
}


bool vers_select_conds_t::init_from_sysvar(THD *thd)
{
  vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp;
  type= (vers_system_time_t) in.type;
  delete_history= false;
  start.unit= VERS_TIMESTAMP;
  if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL)
  {
    DBUG_ASSERT(type == SYSTEM_TIME_AS_OF);
    MYSQL_TIME ltime;
    thd->variables.time_zone->gmt_sec_to_TIME(&ltime, in.unix_time);
    ltime.second_part = in.second_part;

    start.item= new (thd->mem_root)
        Item_datetime_literal(thd, &ltime, TIME_SECOND_PART_DIGITS);
    if (!start.item)
      return true;
  }
  else
    start.item= NULL;
  end.empty();
  return false;
}


COND *Item_cond::build_equal_items(THD *thd,
                                   COND_EQUAL *inherited,
                                   bool link_item_fields,
                                   COND_EQUAL **cond_equal_ref)
{
  List<Item> *cond_args= argument_list();

  List_iterator<Item> li(*cond_args);
  Item *item;

  DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
  /*
     Make replacement of equality predicates for lower levels
     of the condition expression.
     Update used_tables_cache and const_item_cache on the way.
  */
  used_tables_and_const_cache_init();
  while ((item= li++))
  {
    Item *new_item;
    if ((new_item= item->build_equal_items(thd, inherited, false, NULL))
        != item)
    {
      /* This replacement happens only for standalone equalities */
      /*
        This is ok with PS/SP as the replacement is done for
        arguments of an AND/OR item, which are restored for each
        execution of PS/SP.
      */
      li.replace(new_item);
    }
    used_tables_and_const_cache_join(new_item);
  }
  return this;
}


make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond,
                              table_map tables, table_map used_table,
                              int join_tab_idx_arg,
                              bool exclude_expensive_cond __attribute__
                              ((unused)),
                              bool retain_ref_cond,
                              bool is_top_and_level)

{
  table_map rand_table_bit= (table_map) RAND_TABLE_BIT;

  if (used_table && !(cond->used_tables() & used_table))
    return (COND*) 0;				// Already checked

  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++))
      {
        /*
          Special handling of top level conjuncts with RAND_TABLE_BIT:
          if such a conjunct contains a reference to a field that is not
          an outer field then it is pushed to the corresponding table by
          the same rule as all other conjuncts. Otherwise, if the conjunct
          is used in WHERE is is pushed to the last joined table, if is it
          is used in ON condition of an outer join it is pushed into the
          last inner table of the outer join. Such conjuncts are pushed in
          a call of make_cond_for_table_from_pred() with the
          parameter 'used_table' equal to PSEUDO_TABLE_BITS.
        */
        if (is_top_and_level && used_table == rand_table_bit &&
            (item->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit)
        {
          /* The conjunct with RAND_TABLE_BIT has been allready pushed */
          continue;
        }
	Item *fix=make_cond_for_table_from_pred(thd, root_cond, item,
                                                tables, used_table,
                                                join_tab_idx_arg,
                                                exclude_expensive_cond,
                                                retain_ref_cond, false);
	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:
	/*
          Call fix_fields to propagate all properties of the children to
          the new parent Item. This should not be expensive because all
	  children of Item_cond_and should be fixed by now.
	*/
	if (new_cond->fix_fields(thd, 0))
          return (COND*) 0;
	new_cond->used_tables_cache=
	  ((Item_cond_and*) cond)->used_tables_cache &
	  tables;
	return new_cond;
      }
    }
    else
    {						// Or list
      if (is_top_and_level && used_table == rand_table_bit &&
          (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit)
      {
        /* This top level formula with RAND_TABLE_BIT has been already pushed */
        return (COND*) 0;
      }

      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_for_table_from_pred(thd, root_cond, item,
                                                tables, 0L,
                                                join_tab_idx_arg,
                                                exclude_expensive_cond,
                                                retain_ref_cond, false);
	if (!fix)
	  return (COND*) 0;			// Always true
	new_cond->argument_list()->push_back(fix, thd->mem_root);
      }
      /*
        Call fix_fields to propagate all properties of the children to
        the new parent Item. This should not be expensive because all
        children of Item_cond_and should be fixed by now.
      */
      new_cond->fix_fields(thd, 0);
      new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache;
      new_cond->top_level_item();
      return new_cond;
    }
  }

  if (is_top_and_level && used_table == rand_table_bit &&
      (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit)
  {
    /* This top level formula with RAND_TABLE_BIT has been already pushed */
    return (COND*) 0;
  }

  /*
    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 && !retain_ref_cond) ||
      (cond->used_tables() & ~tables))
    return (COND*) 0;				// Can't check this yet

  if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
  {
    cond->set_join_tab_idx(join_tab_idx_arg);
    return cond;				// Not boolean op
  }

  if (cond->type() == Item::FUNC_ITEM &&
      ((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 && !retain_ref_cond &&
	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 && !retain_ref_cond &&
	test_if_ref(root_cond, (Item_field*) right_item,left_item))
    {
      cond->marker=3;			// Checked when read
      return (COND*) 0;
    }
    /*
      If cond is an equality injected for split optimization then
      a. when retain_ref_cond == false : cond is removed unconditionally
         (cond that supports ref access is removed by the preceding code)
      b. when retain_ref_cond == true : cond is removed if it does not
         support ref access
    */
    if (left_item->type() == Item::FIELD_ITEM &&
        is_eq_cond_injected_for_split_opt((Item_func_eq *) cond) &&
        (!retain_ref_cond ||
         !test_if_ref(root_cond, (Item_field*) left_item,right_item)))
    {
      cond->marker=3;
      return (COND*) 0;
    }
  }
  cond->marker=2;
  cond->set_join_tab_idx(join_tab_idx_arg);
  return cond;
}


bool TABLE_REF::is_access_triggered()
{
  for (uint i = 0; i < key_parts; i++)
  {
    if (cond_guards[i])
      return TRUE;
  }
  return FALSE;
}


bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
                                 table_map prefix_tables,
                                 bool distinct_arg, JOIN_TAB *first_top_tab)
{
  int quick_type;
  CHARSET_INFO *cs= system_charset_info;
  THD *thd=      join->thd;
  TABLE_LIST *table_list= table->pos_in_table_list;
  QUICK_SELECT_I *cur_quick= NULL;
  my_bool key_read;
  char table_name_buffer[SAFE_NAME_LEN];
  KEY *key_info= 0;
  uint key_len= 0;
  quick_type= -1;

  explain_plan= eta;
  eta->key.clear();
  eta->quick_info= NULL;

  SQL_SELECT *tab_select;
  /*
    We assume that if this table does pre-sorting, then it doesn't do filtering
    with SQL_SELECT.
  */
  DBUG_ASSERT(!(select && filesort));
  tab_select= (filesort)? filesort->select : select;

  if (filesort)
  {
    if (!(eta->pre_join_sort=
          new (thd->mem_root) Explain_aggr_filesort(thd->mem_root,
                                                    thd->lex->analyze_stmt,
                                                    filesort)))
      return 1;
  }

  tracker= &eta->tracker;
  jbuf_tracker= &eta->jbuf_tracker;

  /* Enable the table access time tracker only for "ANALYZE stmt" */
  if (thd->lex->analyze_stmt)
    table->file->set_time_tracker(&eta->op_tracker);

  /* No need to save id and select_type here, they are kept in Explain_select */

  /* table */
  if (table->derived_select_number)
  {
    /* Derived table name generation */
    size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
                         "<derived%u>",
                         table->derived_select_number);
    eta->table_name.copy(table_name_buffer, len, cs);
  }
  else if (bush_children)
  {
    JOIN_TAB *ctab= bush_children->start;
    /* table */
    size_t len= my_snprintf(table_name_buffer,
                         sizeof(table_name_buffer)-1,
                         "<subquery%d>",
                         ctab->emb_sj_nest->sj_subq_pred->get_identifier());
    eta->table_name.copy(table_name_buffer, len, cs);
  }
  else
  {
    TABLE_LIST *real_table= table->pos_in_table_list;
    /*
      When multi-table UPDATE/DELETE does updates/deletes to a VIEW, the view
      is merged in a certain particular way (grep for DT_MERGE_FOR_INSERT).

      As a result, view's underlying tables have $tbl->pos_in_table_list={view}.
      We don't want to print view name in EXPLAIN, we want underlying table's
      alias (like specified in the view definition).
    */
    if (real_table->merged_for_insert)
    {
      TABLE_LIST *view_child= real_table->view->select_lex.table_list.first;
      for (;view_child; view_child= view_child->next_local)
      {
        if (view_child->table == table)
        {
          real_table= view_child;
          break;
        }
      }
    }
    eta->table_name.copy(real_table->alias.str, real_table->alias.length, cs);
  }

  /* "partitions" column */
  {
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info;
    if (!table->derived_select_number &&
        (part_info= table->part_info))
    { //TODO: all thd->mem_root here should be fixed
      make_used_partitions_str(thd->mem_root, part_info, &eta->used_partitions,
                               eta->used_partitions_list);
      eta->used_partitions_set= true;
    }
    else
      eta->used_partitions_set= false;
#else
    /* just produce empty column if partitioning is not compiled in */
    eta->used_partitions_set= false;
#endif
  }

  /* "type" column */
  enum join_type tab_type= type;
  if ((type == JT_ALL || type == JT_HASH) &&
       tab_select && tab_select->quick && use_quick != 2)
  {
    cur_quick= tab_select->quick;
    quick_type= cur_quick->get_type();
    if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
        (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
        (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
        (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
      tab_type= type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
    else
      tab_type= type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
  }
  eta->type= tab_type;

  /* Build "possible_keys" value */
  // psergey-todo: why does this use thd MEM_ROOT??? Doesn't this
  // break ANALYZE ? thd->mem_root will be freed, and after that we will
  // attempt to print the query plan?
  if (append_possible_keys(thd->mem_root, eta->possible_keys, table, keys))
    return 1;
  // psergey-todo: ^ check for error return code

  /* Build "key", "key_len", and "ref" */
  if (tab_type == JT_NEXT)
  {
    key_info= table->key_info+index;
    key_len= key_info->key_length;
  }
  else if (ref.key_parts)
  {
    key_info= get_keyinfo_by_key_no(ref.key);
    key_len= ref.key_length;
  }

  /*
    In STRAIGHT_JOIN queries, there can be join tabs with JT_CONST type
    that still have quick selects.
  */
  if (tab_select && tab_select->quick && tab_type != JT_CONST)
  {
    if (!(eta->quick_info= tab_select->quick->get_explain(thd->mem_root)))
      return 1;
  }

  if (key_info) /* 'index' or 'ref' access */
  {
    eta->key.set(thd->mem_root, key_info, key_len);

    if (ref.key_parts && tab_type != JT_FT)
    {
      store_key **key_ref= ref.key_copy;
      for (uint kp= 0; kp < ref.key_parts; kp++)
      {
        if ((key_part_map(1) << kp) & ref.const_ref_part_map)
        {
          if (!(eta->ref_list.append_str(thd->mem_root, "const")))
            return 1;
          /*
            create_ref_for_key() handles keypart=const equalities as follows:
              - non-EXPLAIN execution will copy the "const" to lookup tuple
                immediately and will not add an element to ref.key_copy
              - EXPLAIN will put an element into ref.key_copy. Since we've
                just printed "const" for it, we should skip it here
          */
          if (thd->lex->describe)
            key_ref++;
        }
        else
        {
          if (!(eta->ref_list.append_str(thd->mem_root, (*key_ref)->name())))
            return 1;
          key_ref++;
        }
      }
    }
  }

  if (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
  {
    eta->hash_next_key.set(thd->mem_root,
                           & table->key_info[index],
                           table->key_info[index].key_length);
    // psergey-todo: ^ is the above correct? are we necessarily joining on all
    // columns?
  }

  if (!key_info)
  {
    if (table_list && /* SJM bushes don't have table_list */
        table_list->schema_table &&
        table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
    {
      IS_table_read_plan *is_table_read_plan= table_list->is_table_read_plan;
      const char *tmp_buff;
      int f_idx;
      StringBuffer<64> key_name_buf;
      if (is_table_read_plan->trivial_show_command ||
          is_table_read_plan->has_db_lookup_value())
      {
        /* The "key" has the name of the column referring to the database */
        f_idx= table_list->schema_table->idx_field1;
        tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
        key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
      }
      if (is_table_read_plan->trivial_show_command ||
          is_table_read_plan->has_table_lookup_value())
      {
        if (is_table_read_plan->trivial_show_command ||
            is_table_read_plan->has_db_lookup_value())
          key_name_buf.append(',');

        f_idx= table_list->schema_table->idx_field2;
        tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
        key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
      }

      if (key_name_buf.length())
        eta->key.set_pseudo_key(thd->mem_root, key_name_buf.c_ptr_safe());
    }
  }

  /* "rows" */
  if (table_list /* SJM bushes don't have table_list */ &&
      table_list->schema_table)
  {
    /* I_S tables have rows=extra=NULL */
    eta->rows_set= false;
    eta->filtered_set= false;
  }
  else
  {
    ha_rows examined_rows= get_examined_rows();

    eta->rows_set= true;
    eta->rows= examined_rows;

    /* "filtered"  */
    float f= 0.0;
    if (examined_rows)
    {
      double pushdown_cond_selectivity= cond_selectivity;
      if (pushdown_cond_selectivity == 1.0)
        f= (float) (100.0 * records_read / examined_rows);
      else
        f= (float) (100.0 * pushdown_cond_selectivity);
    }
    set_if_smaller(f, 100.0);
    eta->filtered_set= true;
    eta->filtered= f;
  }

  /* Build "Extra" field and save it */
  key_read= table->file->keyread_enabled();
  if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
      table->covering_keys.is_set(index))
    key_read=1;
  if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
      !((QUICK_ROR_INTERSECT_SELECT*)cur_quick)->need_to_fetch_row)
    key_read=1;

  if (info)
  {
    eta->push_extra(info);
  }
  else if (packed_info & TAB_INFO_HAVE_VALUE)
  {
    if (packed_info & TAB_INFO_USING_INDEX)
      eta->push_extra(ET_USING_INDEX);
    if (packed_info & TAB_INFO_USING_WHERE)
      eta->push_extra(ET_USING_WHERE);
    if (packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
      eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
  }
  else
  {
    uint keyno= MAX_KEY;
    if (ref.key_parts)
      keyno= ref.key;
    else if (tab_select && cur_quick)
      keyno = cur_quick->index;

    if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
        table->file->pushed_idx_cond)
    {
      eta->push_extra(ET_USING_INDEX_CONDITION);
      eta->pushed_index_cond= table->file->pushed_idx_cond;
    }
    else if (cache_idx_cond)
    {
      eta->push_extra(ET_USING_INDEX_CONDITION_BKA);
      eta->pushed_index_cond= cache_idx_cond;
    }

    if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
        quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
        quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
        quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
    {
      eta->push_extra(ET_USING);
    }
    if (tab_select)
    {
      if (use_quick == 2)
      {
        eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
        eta->range_checked_fer= new (thd->mem_root) Explain_range_checked_fer;
        if (eta->range_checked_fer)
          eta->range_checked_fer->
            append_possible_keys_stat(thd->mem_root, table, keys);
      }
      else if (tab_select->cond ||
               (cache_select && cache_select->cond))
      {
        const COND *pushed_cond= table->file->pushed_cond;

        if ((table->file->ha_table_flags() &
              HA_CAN_TABLE_CONDITION_PUSHDOWN) &&
            pushed_cond)
        {
          eta->push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION);
        }
        else
        {
          eta->where_cond= tab_select->cond;
          eta->cache_cond= cache_select? cache_select->cond : NULL;
          eta->push_extra(ET_USING_WHERE);
        }
      }
    }
    if (table_list /* SJM bushes don't have table_list */ &&
        table_list->schema_table &&
        table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
    {
      if (!table_list->table_open_method)
        eta->push_extra(ET_SKIP_OPEN_TABLE);
      else if (table_list->table_open_method == OPEN_FRM_ONLY)
        eta->push_extra(ET_OPEN_FRM_ONLY);
      else
        eta->push_extra(ET_OPEN_FULL_TABLE);
      /* psergey-note: the following has a bug.*/
      if (table_list->is_table_read_plan->trivial_show_command ||
          (table_list->is_table_read_plan->has_db_lookup_value() &&
           table_list->is_table_read_plan->has_table_lookup_value()))
        eta->push_extra(ET_SCANNED_0_DATABASES);
      else if (table_list->is_table_read_plan->has_db_lookup_value() ||
               table_list->is_table_read_plan->has_table_lookup_value())
        eta->push_extra(ET_SCANNED_1_DATABASE);
      else
        eta->push_extra(ET_SCANNED_ALL_DATABASES);
    }
    if (key_read)
    {
      if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
      {
        QUICK_GROUP_MIN_MAX_SELECT *qgs=
          (QUICK_GROUP_MIN_MAX_SELECT *) tab_select->quick;
        eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY);
        eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning();
      }
      else
        eta->push_extra(ET_USING_INDEX);
    }
    if (table->reginfo.not_exists_optimize)
      eta->push_extra(ET_NOT_EXISTS);

    if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
    {
      explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab_select->quick),
                              &eta->mrr_type);
      if (eta->mrr_type.length() > 0)
        eta->push_extra(ET_USING_MRR);
    }

    if (shortcut_for_distinct)
      eta->push_extra(ET_DISTINCT);

    if (loosescan_match_tab)
    {
      eta->push_extra(ET_LOOSESCAN);
    }

    if (first_weedout_table)
    {
      eta->start_dups_weedout= true;
      eta->push_extra(ET_START_TEMPORARY);
    }
    if (check_weed_out_table)
    {
      eta->push_extra(ET_END_TEMPORARY);
      eta->end_dups_weedout= true;
    }

    else if (do_firstmatch)
    {
      if (do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
        eta->push_extra(ET_FIRST_MATCH);
      else
      {
        eta->push_extra(ET_FIRST_MATCH);
        TABLE *prev_table=do_firstmatch->table;
        if (prev_table->derived_select_number)
        {
          char namebuf[NAME_LEN];
          /* Derived table name generation */
          size_t len= my_snprintf(namebuf, sizeof(namebuf)-1,
                               "<derived%u>",
                               prev_table->derived_select_number);
          eta->firstmatch_table_name.append(namebuf, len);
        }
        else
          eta->firstmatch_table_name.append(&prev_table->pos_in_table_list->alias);
      }
    }

    for (uint part= 0; part < ref.key_parts; part++)
    {
      if (ref.cond_guards[part])
      {
        eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
        eta->full_scan_on_null_key= true;
        break;
      }
    }

    if (cache)
    {
      eta->push_extra(ET_USING_JOIN_BUFFER);
      if (cache->save_explain_data(&eta->bka_type))
        return 1;
    }
  }

  /*
    In case this is a derived table, here we remember the number of
    subselect that used to produce it.
  */
  if (!(table_list && table_list->is_with_table_recursive_reference()))
    eta->derived_select_number= table->derived_select_number;

  /* The same for non-merged semi-joins */
  eta->non_merged_sjm_number = get_non_merged_semijoin_select();

  return 0;
}


bool JOIN::change_result(select_result *new_result, select_result *old_result)
{
  DBUG_ENTER("JOIN::change_result");
  if (old_result == NULL || result == old_result)
  {
    result= new_result;
    if (result->prepare(fields_list, select_lex->master_unit()) ||
        result->prepare2(this))
      DBUG_RETURN(true); /* purecov: inspected */
    DBUG_RETURN(false);
  }
  DBUG_RETURN(result->change_result(new_result));
}


void set_join_cache_denial(JOIN_TAB *join_tab)
{
  if (join_tab->cache)
  {
    /*
      If there is a previous cache linked to this cache through the
      next_cache pointer: remove the link.
    */
    if (join_tab->cache->prev_cache)
      join_tab->cache->prev_cache->next_cache= 0;
    /*
      Same for the next_cache
    */
    if (join_tab->cache->next_cache)
      join_tab->cache->next_cache->prev_cache= 0;

    join_tab->cache->free();
    join_tab->cache= 0;
  }
  if (join_tab->use_join_cache)
  {
    join_tab->use_join_cache= FALSE;
    join_tab->used_join_cache_level= 0;
    /*
      It could be only sub_select(). It could not be sub_seject_sjm because we
      don't do join buffering for the first table in sjm nest.
    */
    join_tab[-1].next_select= sub_select;
    if (join_tab->type == JT_REF && join_tab->is_ref_for_hash_join())
    {
      join_tab->type= JT_ALL;
      join_tab->ref.key_parts= 0;
    }
    join_tab->join->return_tab= join_tab;
  }
}


  static void operator delete(void *ptr __attribute__((unused)),
                              size_t size __attribute__((unused)))
  { TRASH_FREE(ptr, size); }


static void choose_initial_table_order(JOIN *join)
{
  TABLE_LIST *emb_subq;
  JOIN_TAB **tab= join->best_ref + join->const_tables;
  JOIN_TAB **tabs_end= tab + join->table_count - join->const_tables;
  DBUG_ENTER("choose_initial_table_order");
  /* Find where the top-level JOIN_TABs end and subquery JOIN_TABs start */
  for (; tab != tabs_end; tab++)
  {
    if ((emb_subq= get_emb_subq(*tab)))
      break;
  }
  uint n_subquery_tabs= (uint)(tabs_end - tab);

  if (!n_subquery_tabs)
    DBUG_VOID_RETURN;

  /* Copy the subquery JOIN_TABs to a separate array */
  JOIN_TAB *subquery_tabs[MAX_TABLES];
  memcpy(subquery_tabs, tab, sizeof(JOIN_TAB*) * n_subquery_tabs);

  JOIN_TAB **last_top_level_tab= tab;
  JOIN_TAB **subq_tab= subquery_tabs;
  JOIN_TAB **subq_tabs_end= subquery_tabs + n_subquery_tabs;
  TABLE_LIST *cur_subq_nest= NULL;
  for (; subq_tab < subq_tabs_end; subq_tab++)
  {
    if (get_emb_subq(*subq_tab)!= cur_subq_nest)
    {
      /*
        Reached the part of subquery_tabs that covers tables in some subquery.
      */
      cur_subq_nest= get_emb_subq(*subq_tab);

      /* Determine how many tables the subquery has */
      JOIN_TAB **last_tab_for_subq;
      for (last_tab_for_subq= subq_tab;
           last_tab_for_subq < subq_tabs_end &&
           get_emb_subq(*last_tab_for_subq) == cur_subq_nest;
           last_tab_for_subq++) {}
      uint n_subquery_tables= (uint)(last_tab_for_subq - subq_tab);

      /*
        Walk the original array and find where this subquery would have been
        attached to
      */
      table_map need_tables= cur_subq_nest->original_subq_pred_used_tables;
      need_tables &= ~(join->const_table_map | PSEUDO_TABLE_BITS);
      for (JOIN_TAB **top_level_tab= join->best_ref + join->const_tables;
           top_level_tab < last_top_level_tab;
           //top_level_tab < join->best_ref + join->table_count;
           top_level_tab++)
      {
        need_tables &= ~(*top_level_tab)->table->map;
        /* Check if this is the place where subquery should be attached */
        if (!need_tables)
        {
          /* Move away the top-level tables that are after top_level_tab */
          size_t top_tail_len= last_top_level_tab - top_level_tab - 1;
          memmove(top_level_tab + 1 + n_subquery_tables, top_level_tab + 1,
                  sizeof(JOIN_TAB*)*top_tail_len);
          last_top_level_tab += n_subquery_tables;
          memcpy(top_level_tab + 1, subq_tab, sizeof(JOIN_TAB*)*n_subquery_tables);
          break;
        }
      }
      DBUG_ASSERT(!need_tables);
      subq_tab += n_subquery_tables - 1;
    }
  }
  DBUG_VOID_RETURN;
}


inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2)
{
  if (*e1)
  {
    if (!e2)
      return;
    Item *res;
    if ((res= new (thd->mem_root) Item_cond_and(thd, *e1, e2)))
    {
      res->fix_fields(thd, 0);
      res->update_used_tables();
      *e1= res;
    }
  }
  else
    *e1= e2;
}


uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
                         ha_rows limit, ha_rows *scanned_limit,
                         bool *need_sort, bool *reverse)
{
  if (!order)
  {
    *need_sort= FALSE;
    if (select && select->quick)
      return select->quick->index; // index or MAX_KEY, use quick select as is
    else
      return table->file->key_used_on_scan; // MAX_KEY or index for some engines
  }

  if (!is_simple_order(order)) // just to cut further expensive checks
  {
    *need_sort= TRUE;
    return MAX_KEY;
  }

  if (select && select->quick)
  {
    if (select->quick->index == MAX_KEY)
    {
      *need_sort= TRUE;
      return MAX_KEY;
    }

    uint used_key_parts;
    switch (test_if_order_by_key(NULL, order, table, select->quick->index,
                                 &used_key_parts)) {
    case 1: // desired order
      *need_sort= FALSE;
      *scanned_limit= MY_MIN(limit, select->quick->records);
      return select->quick->index;
    case 0: // unacceptable order
      *need_sort= TRUE;
      return MAX_KEY;
    case -1: // desired order, but opposite direction
      {
        QUICK_SELECT_I *reverse_quick;
        if ((reverse_quick=
               select->quick->make_reverse(used_key_parts)))
        {
          select->set_quick(reverse_quick);
          *need_sort= FALSE;
          *scanned_limit= MY_MIN(limit, select->quick->records);
          return select->quick->index;
        }
        else
        {
          *need_sort= TRUE;
          return MAX_KEY;
        }
      }
    }
    DBUG_ASSERT(0);
  }
  else if (limit != HA_POS_ERROR)
  { // check if some index scan & LIMIT is more efficient than filesort

    /*
      Update quick_condition_rows since single table UPDATE/DELETE procedures
      don't call make_join_statistics() and leave this variable uninitialized.
    */
    table->quick_condition_rows= table->stat_records();

    int key, direction;
    if (test_if_cheaper_ordering(NULL, order, table,
                                 table->keys_in_use_for_order_by, -1,
                                 limit,
                                 &key, &direction, &limit) &&
        !is_key_used(table, key, table->write_set))
    {
      *need_sort= FALSE;
      *scanned_limit= limit;
      *reverse= (direction < 0);
      return key;
    }
  }
  *need_sort= TRUE;
  return MAX_KEY;
}


add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
{
  List<Item_field> indexed_fields;
  List_iterator<Item_field> indexed_fields_it(indexed_fields);
  ORDER      *cur_group;
  Item_field *cur_item;
  key_map possible_keys(0);

  if (join->group_list)
  { /* Collect all query fields referenced in the GROUP clause. */
    for (cur_group= join->group_list; cur_group; cur_group= cur_group->next)
      (*cur_group->item)->walk(&Item::collect_item_field_processor, 0,
                               &indexed_fields);
  }
  else if (join->select_distinct)
  { /* Collect all query fields referenced in the SELECT clause. */
    List<Item> &select_items= join->fields_list;
    List_iterator<Item> select_items_it(select_items);
    Item *item;
    while ((item= select_items_it++))
      item->walk(&Item::collect_item_field_processor, 0, &indexed_fields);
  }
  else if (join->tmp_table_param.sum_func_count &&
           is_indexed_agg_distinct(join, &indexed_fields))
  {
    join->sort_and_group= 1;
  }
  else
    return;

  if (indexed_fields.elements == 0)
    return;

  /* Intersect the keys of all group fields. */
  cur_item= indexed_fields_it++;
  possible_keys.merge(cur_item->field->part_of_key);
  while ((cur_item= indexed_fields_it++))
  {
    possible_keys.intersect(cur_item->field->part_of_key);
  }

  if (!possible_keys.is_clear_all())
    join_tab->const_keys.merge(possible_keys);
}


void JOIN_TAB::calc_used_field_length(bool max_fl)
{
  uint null_fields,blobs,fields;
  ulong rec_length;
  Field **f_ptr,*field;
  uint uneven_bit_fields;
  MY_BITMAP *read_set= table->read_set;

  uneven_bit_fields= null_fields= blobs= fields= rec_length=0;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
    if (bitmap_is_set(read_set, field->field_index))
    {
      uint flags=field->flags;
      fields++;
      rec_length+=field->pack_length();
      if (flags & BLOB_FLAG)
	blobs++;
      if (!(flags & NOT_NULL_FLAG))
	null_fields++;
      if (field->type() == MYSQL_TYPE_BIT &&
          ((Field_bit*)field)->bit_len)
        uneven_bit_fields++;
    }
  }
  if (null_fields || uneven_bit_fields)
    rec_length+=(table->s->null_fields+7)/8;
  if (table->maybe_null)
    rec_length+=sizeof(my_bool);

  /* Take into account that DuplicateElimination may need to store rowid */
  uint rowid_add_size= 0;
  if (keep_current_rowid)
  {
    rowid_add_size= table->file->ref_length;
    rec_length += rowid_add_size;
    fields++;
  }

  if (max_fl)
  {
    // TODO: to improve this estimate for max expected length
    if (blobs)
    {
      ulong blob_length= table->file->stats.mean_rec_length;
      if (ULONG_MAX - rec_length > blob_length)
        rec_length+=  blob_length;
      else
        rec_length= ULONG_MAX;
    }
    max_used_fieldlength= rec_length;
  }
  else if (table->file->stats.mean_rec_length)
    set_if_smaller(rec_length, table->file->stats.mean_rec_length + rowid_add_size);

  used_fields=fields;
  used_fieldlength=rec_length;
  used_blobs=blobs;
  used_null_fields= null_fields;
  used_uneven_bit_fields= uneven_bit_fields;
}


setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
{
  setup_tmp_table_column_bitmaps(table, bitmaps, table->s->fields);
}


sub_select_postjoin_aggr(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
{
  enum_nested_loop_state rc;
  AGGR_OP *aggr= join_tab->aggr;

  /* This function cannot be called if join_tab has no associated aggregation */
  DBUG_ASSERT(aggr != NULL);

  DBUG_ENTER("sub_select_aggr_tab");

  if (join->thd->killed)
  {
    /* The user has aborted the execution of the query */
    join->thd->send_kill_message();
    DBUG_RETURN(NESTED_LOOP_KILLED);
  }

  if (end_of_records)
  {
    rc= aggr->end_send();
    if (rc >= NESTED_LOOP_OK)
      rc= sub_select(join, join_tab, end_of_records);
    DBUG_RETURN(rc);
  }

  rc= aggr->put_record();

  DBUG_RETURN(rc);
}


greedy_search(JOIN      *join,
              table_map remaining_tables,
              uint      search_depth,
              uint      prune_level,
              uint      use_cond_selectivity)
{
  double    record_count= 1.0;
  double    read_time=    0.0;
  uint      idx= join->const_tables; // index into 'join->best_ref'
  uint      best_idx;
  uint      size_remain;    // cardinality of remaining_tables
  POSITION  best_pos;
  JOIN_TAB  *best_table; // the next plan node to be added to the curr QEP
  // ==join->tables or # tables in the sj-mat nest we're optimizing
  uint      n_tables __attribute__((unused));
  DBUG_ENTER("greedy_search");

  /* number of tables that remain to be optimized */
  n_tables= size_remain= my_count_bits(remaining_tables &
                                       (join->emb_sjm_nest?
                                         (join->emb_sjm_nest->sj_inner_tables &
                                          ~join->const_table_map)
                                         :
                                         ~(table_map)0));

  do {
    /* Find the extension of the current QEP with the lowest cost */
    join->best_read= DBL_MAX;
    if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
                                         read_time, search_depth, prune_level,
                                         use_cond_selectivity))
      DBUG_RETURN(TRUE);
    /*
      'best_read < DBL_MAX' means that optimizer managed to find
      some plan and updated 'best_positions' array accordingly.
    */
    DBUG_ASSERT(join->best_read < DBL_MAX);

    if (size_remain <= search_depth)
    {
      /*
        'join->best_positions' contains a complete optimal extension of the
        current partial QEP.
      */
      DBUG_EXECUTE("opt", print_plan(join, n_tables,
                                     record_count, read_time, read_time,
                                     "optimal"););
      DBUG_RETURN(FALSE);
    }

    /* select the first table in the optimal extension as most promising */
    best_pos= join->best_positions[idx];
    best_table= best_pos.table;
    /*
      Each subsequent loop of 'best_extension_by_limited_search' uses
      'join->positions' for cost estimates, therefore we have to update its
      value.
    */
    join->positions[idx]= best_pos;

    /*
      Update the interleaving state after extending the current partial plan
      with a new table.
      We are doing this here because best_extension_by_limited_search reverts
      the interleaving state to the one of the non-extended partial plan
      on exit.
    */
    bool is_interleave_error __attribute__((unused))=
      check_interleaving_with_nj (best_table);
    /* This has been already checked by best_extension_by_limited_search */
    DBUG_ASSERT(!is_interleave_error);

    /* find the position of 'best_table' in 'join->best_ref' */
    best_idx= idx;
    JOIN_TAB *pos= join->best_ref[best_idx];
    while (pos && best_table != pos)
      pos= join->best_ref[++best_idx];
    DBUG_ASSERT((pos != NULL)); // should always find 'best_table'
    /* move 'best_table' at the first free position in the array of joins */
    swap_variables(JOIN_TAB*, join->best_ref[idx], join->best_ref[best_idx]);

    /* compute the cost of the new plan extended with 'best_table' */
    record_count= COST_MULT(record_count, join->positions[idx].records_read);
    read_time= COST_ADD(read_time,
                         COST_ADD(join->positions[idx].read_time,
                                  record_count / (double) TIME_FOR_COMPARE));

    remaining_tables&= ~(best_table->table->map);
    --size_remain;
    ++idx;

    DBUG_EXECUTE("opt", print_plan(join, idx,
                                   record_count, read_time, read_time,
                                   "extended"););
  } while (TRUE);
}


int report_error(TABLE *table, int error)
{
  if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
  {
    table->status= STATUS_GARBAGE;
    return -1;					// key not found; ok
  }
  /*
    Locking reads can legally return also these errors, do not
    print them to the .err log
  */
  if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT
      && error != HA_ERR_TABLE_DEF_CHANGED && !table->in_use->killed)
    sql_print_error("Got error %d when reading table '%s'",
		    error, table->s->path.str);
  table->file->print_error(error,MYF(0));
  return 1;
}


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

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

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

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

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

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

  join->return_tab= join_tab;

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

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

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

  if (rc != NESTED_LOOP_NO_MORE_ROWS &&
      (rc= join_tab_execution_startup(join_tab)) < 0)
    DBUG_RETURN(rc);

  if (join_tab->loosescan_match_tab)
    join_tab->loosescan_match_tab->found_match= FALSE;

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

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

    error= info->read_record();

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

    if (join_tab->keep_current_rowid && likely(!error))
      join_tab->table->file->position(join_tab->table->record[0]);

    rc= evaluate_join_record(join, join_tab, error);
  }

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

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


JOIN_TAB::remove_duplicates()

{
  bool error;
  ulong keylength= 0;
  uint field_count;
  List<Item> *fields= (this-1)->fields;
  THD *thd= join->thd;

  DBUG_ENTER("remove_duplicates");

  DBUG_ASSERT(join->aggr_tables > 0 && table->s->tmp_table != NO_TMP_TABLE);
  THD_STAGE_INFO(join->thd, stage_removing_duplicates);

  //join->explain->ops_tracker.report_duplicate_removal();

  table->reginfo.lock_type=TL_WRITE;

  /* Calculate how many saved fields there is in list */
  field_count=0;
  List_iterator<Item> it(*fields);
  Item *item;
  while ((item=it++))
  {
    if (item->get_tmp_table_field() && ! item->const_item())
      field_count++;
  }

  if (!field_count && !(join->select_options & OPTION_FOUND_ROWS) && !having)
  {                    // only const items with no OPTION_FOUND_ROWS
    join->unit->select_limit_cnt= 1;		// Only send first row
    DBUG_RETURN(false);
  }

  Field **first_field=table->field+table->s->fields - field_count;
  for (Field **ptr=first_field; *ptr; ptr++)
    keylength+= (*ptr)->sort_length() + (*ptr)->maybe_null();

  /*
    Disable LIMIT ROWS EXAMINED in order to avoid interrupting prematurely
    duplicate removal, and produce a possibly incomplete query result.
  */
  thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
  if (thd->killed == ABORT_QUERY)
    thd->reset_killed();

  table->file->info(HA_STATUS_VARIABLE);
  if (table->s->db_type() == heap_hton ||
      (!table->s->blob_fields &&
       ((ALIGN_SIZE(keylength) + HASH_OVERHEAD) * table->file->stats.records <
	thd->variables.sortbuff_size)))
    error=remove_dup_with_hash_index(join->thd, table, field_count, first_field,
				     keylength, having);
  else
    error=remove_dup_with_compare(join->thd, table, first_field, having);

  if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
    thd->lex->set_limit_rows_examined();
  free_blobs(first_field);
  DBUG_RETURN(error);
}


add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
{
  Field *field=key_field->field;
  TABLE *form= field->table;

  if (key_field->eq_func && !(key_field->optimize & KEY_OPTIMIZE_EXISTS))
  {
    for (uint key=0 ; key < form->s->keys ; key++)
    {
      if (!(form->keys_in_use_for_query.is_set(key)))
	continue;
      if (form->key_info[key].flags & (HA_FULLTEXT | HA_SPATIAL))
	continue;    // ToDo: ft-keys in non-ft queries.   SerG

      KEY *keyinfo= form->key_info+key;
      uint key_parts= form->actual_n_key_parts(keyinfo);
      for (uint part=0 ; part <  key_parts ; part++)
      {
        if (field->eq(form->key_info[key].key_part[part].field) &&
            field->can_optimize_keypart_ref(key_field->cond, key_field->val))
	{
          if (add_keyuse(keyuse_array, key_field, key, part))
            return TRUE;
	}
      }
    }
    if (field->hash_join_is_possible() &&
        (key_field->optimize & KEY_OPTIMIZE_EQ) &&
        key_field->val->used_tables())
    {
      if (!field->can_optimize_hash_join(key_field->cond, key_field->val))
        return false;
      if (form->is_splittable())
        form->add_splitting_info_for_key_field(key_field);
      /*
        If a key use is extracted from an equi-join predicate then it is
        added not only as a key use for every index whose component can
        be evalusted utilizing this key use, but also as a key use for
        hash join. Such key uses are marked with a special key number.
      */
      if (add_keyuse(keyuse_array, key_field, get_hash_join_key_no(), 0))
        return TRUE;
    }
  }
  return FALSE;
}


join_read_first(JOIN_TAB *tab)
{
  int error= 0;
  TABLE *table=tab->table;
  DBUG_ENTER("join_read_first");

  DBUG_ASSERT(table->no_keyread ||
              !table->covering_keys.is_set(tab->index) ||
              table->file->keyread == tab->index);
  tab->table->status=0;
  tab->read_record.read_record_func= join_read_next;
  tab->read_record.table=table;
  if (!table->file->inited)
    error= table->file->ha_index_init(tab->index, tab->sorted);
  if (likely(!error))
    error= table->file->prepare_index_scan();
  if (unlikely(error) ||
      unlikely(error= tab->table->file->ha_index_first(tab->table->record[0])))
  {
    if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
      report_error(table, error);
    DBUG_RETURN(-1);
  }
  DBUG_RETURN(0);
}


ulong check_selectivity(THD *thd,
                        ulong rows_to_read,
                        TABLE *table,
                        List<COND_STATISTIC> *conds)
{
  ulong count= 0;
  COND_STATISTIC *cond;
  List_iterator_fast<COND_STATISTIC> it(*conds);
  handler *file= table->file;
  uchar *record= table->record[0];
  int error= 0;
  DBUG_ENTER("check_selectivity");

  DBUG_ASSERT(rows_to_read > 0);
  while ((cond= it++))
  {
    DBUG_ASSERT(cond->cond);
    DBUG_ASSERT(cond->cond->used_tables() == table->map);
    cond->positive= 0;
  }
  it.rewind();

  if (unlikely(file->ha_rnd_init_with_error(1)))
    DBUG_RETURN(0);
  do
  {
    error= file->ha_rnd_next(record);

    if (unlikely(thd->killed))
    {
      thd->send_kill_message();
      count= 0;
      goto err;
    }
    if (unlikely(error))
    {
      if (error == HA_ERR_END_OF_FILE)
	break;
      goto err;
    }

    count++;
    while ((cond= it++))
    {
      if (cond->cond->val_bool())
        cond->positive++;
    }
    it.rewind();

  } while (count < rows_to_read);

  file->ha_rnd_end();
  DBUG_RETURN(count);

err:
  DBUG_PRINT("error", ("error %d", error));
  file->ha_rnd_end();
  DBUG_RETURN(0);
}