sql_select.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:288k
- {
- return new store_key_const_item(thd,
- key_part->field,
- key_buff + maybe_null,
- maybe_null ? key_buff : 0,
- key_part->length,
- keyuse->val);
- }
- else if (keyuse->val->type() == Item::FIELD_ITEM)
- return new store_key_field(thd,
- key_part->field,
- key_buff + maybe_null,
- maybe_null ? key_buff : 0,
- key_part->length,
- ((Item_field*) keyuse->val)->field,
- keyuse->val->full_name());
- return new store_key_item(thd,
- key_part->field,
- key_buff + maybe_null,
- maybe_null ? key_buff : 0,
- key_part->length,
- keyuse->val);
- }
- /*
- This function is only called for const items on fields which are keys
- returns 1 if there was some conversion made when the field was stored.
- */
- bool
- store_val_in_field(Field *field,Item *item)
- {
- bool error;
- THD *thd=current_thd;
- ha_rows cuted_fields=thd->cuted_fields;
- /*
- we should restore old value of count_cuted_fields because
- store_val_in_field can be called from mysql_insert
- with select_insert, which make count_cuted_fields= 1
- */
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_WARN;
- error= item->save_in_field(field, 1);
- thd->count_cuted_fields= old_count_cuted_fields;
- return error || cuted_fields != thd->cuted_fields;
- }
- static bool
- make_simple_join(JOIN *join,TABLE *tmp_table)
- {
- TABLE **tableptr;
- JOIN_TAB *join_tab;
- if (!(tableptr=(TABLE**) join->thd->alloc(sizeof(TABLE*))) ||
- !(join_tab=(JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB))))
- return TRUE;
- join->join_tab=join_tab;
- join->table=tableptr; tableptr[0]=tmp_table;
- join->tables=1;
- join->const_tables=0;
- join->const_table_map=0;
- join->tmp_table_param.field_count= join->tmp_table_param.sum_func_count=
- join->tmp_table_param.func_count=0;
- join->tmp_table_param.copy_field=join->tmp_table_param.copy_field_end=0;
- join->first_record=join->sort_and_group=0;
- join->send_records=(ha_rows) 0;
- join->group=0;
- join->row_limit=join->unit->select_limit_cnt;
- join->do_send_rows = (join->row_limit) ? 1 : 0;
- join_tab->cache.buff=0; /* No caching */
- join_tab->table=tmp_table;
- join_tab->select=0;
- join_tab->select_cond=0;
- join_tab->quick=0;
- join_tab->type= JT_ALL; /* Map through all records */
- join_tab->keys.init();
- join_tab->keys.set_all(); /* test everything in quick */
- join_tab->info=0;
- join_tab->on_expr=0;
- join_tab->ref.key = -1;
- join_tab->not_used_in_distinct=0;
- join_tab->read_first_record= join_init_read_record;
- join_tab->join=join;
- bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record));
- tmp_table->status=0;
- tmp_table->null_row=0;
- return FALSE;
- }
- inline void add_cond_and_fix(Item **e1, Item *e2)
- {
- if (*e1)
- {
- Item *res;
- if ((res= new Item_cond_and(*e1, e2)))
- {
- *e1= res;
- res->quick_fix_field();
- }
- }
- else
- *e1= e2;
- }
- /*
- Add to join_tab->select_cond[i] "table.field IS NOT NULL" conditions we've
- inferred from ref/eq_ref access performed.
- SYNOPSIS
- add_not_null_conds()
- join Join to process
- NOTES
- This function is a part of "Early NULL-values filtering for ref access"
- optimization.
- Example of this optimization:
- For query SELECT * FROM t1,t2 WHERE t2.key=t1.field
- and plan " any-access(t1), ref(t2.key=t1.field) "
- add "t1.field IS NOT NULL" to t1's table condition.
- Description of the optimization:
-
- We look through equalities choosen to perform ref/eq_ref access,
- pick equalities that have form "tbl.part_of_key = othertbl.field"
- (where othertbl is a non-const table and othertbl.field may be NULL)
- and add them to conditions on correspoding tables (othertbl in this
- example).
- Exception from that is the case when referred_tab->join != join.
- I.e. don't add NOT NULL constraints from any embedded subquery.
- Consider this query:
- SELECT A.f2 FROM t1 LEFT JOIN t2 A ON A.f2 = f1
- WHERE A.f3=(SELECT MIN(f3) FROM t2 C WHERE A.f4 = C.f4) OR A.f3 IS NULL;
- Here condition A.f3 IS NOT NULL is going to be added to the WHERE
- condition of the embedding query.
- Another example:
- SELECT * FROM t10, t11 WHERE (t10.a < 10 OR t10.a IS NULL)
- AND t11.b <=> t10.b AND (t11.a = (SELECT MAX(a) FROM t12
- WHERE t12.b = t10.a ));
- Here condition t10.a IS NOT NULL is going to be added.
- In both cases addition of NOT NULL condition will erroneously reject
- some rows of the result set.
- referred_tab->join != join constraint would disallow such additions.
- This optimization doesn't affect the choices that ref, range, or join
- optimizer make. This was intentional because this was added after 4.1
- was GA.
-
- Implementation overview
- 1. update_ref_and_keys() accumulates info about null-rejecting
- predicates in in KEY_FIELD::null_rejecting
- 1.1 add_key_part saves these to KEYUSE.
- 2. create_ref_for_key copies them to TABLE_REF.
- 3. add_not_null_conds adds "x IS NOT NULL" to join_tab->select_cond of
- appropiate JOIN_TAB members.
- */
- static void add_not_null_conds(JOIN *join)
- {
- DBUG_ENTER("add_not_null_conds");
- for (uint i=join->const_tables ; i < join->tables ; i++)
- {
- JOIN_TAB *tab=join->join_tab+i;
- if ((tab->type == JT_REF || tab->type == JT_REF_OR_NULL) &&
- !tab->table->maybe_null)
- {
- for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++)
- {
- if (tab->ref.null_rejecting & (1 << keypart))
- {
- Item *item= tab->ref.items[keypart];
- Item *notnull;
- DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
- Item_field *not_null_item= (Item_field*)item;
- 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 (!referred_tab || referred_tab->join != join)
- continue;
- if (!(notnull= new Item_func_isnotnull(not_null_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, join->tables_list, ¬null))
- DBUG_VOID_RETURN;
- DBUG_EXECUTE("where",print_where(notnull,
- referred_tab->table->table_name););
- add_cond_and_fix(&referred_tab->select_cond, notnull);
- }
- }
- }
- }
- DBUG_VOID_RETURN;
- }
- static bool
- make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
- {
- DBUG_ENTER("make_join_select");
- if (select)
- {
- add_not_null_conds(join);
- table_map used_tables;
- if (join->tables > 1)
- cond->update_used_tables(); // Tablenr may have changed
- if (join->const_tables == join->tables &&
- join->thd->lex->current_select->master_unit() ==
- &join->thd->lex->unit) // not upper level SELECT
- join->const_table_map|=RAND_TABLE_BIT;
- { // Check const tables
- COND *const_cond=
- make_cond_for_table(cond,join->const_table_map,(table_map) 0);
- DBUG_EXECUTE("where",print_where(const_cond,"constants"););
- if (const_cond && !const_cond->val_int())
- {
- DBUG_PRINT("info",("Found impossible WHERE condition"));
- DBUG_RETURN(1); // Impossible const condition
- }
- }
- used_tables=((select->const_tables=join->const_table_map) |
- OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
- for (uint i=join->const_tables ; i < join->tables ; i++)
- {
- JOIN_TAB *tab=join->join_tab+i;
- table_map current_map= tab->table->map;
- /*
- Following force including random expression in last table condition.
- It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5
- */
- if (i == join->tables-1)
- current_map|= OUTER_REF_TABLE_BIT | RAND_TABLE_BIT;
- bool use_quick_range=0;
- used_tables|=current_map;
- if (tab->type == JT_REF && tab->quick &&
- (uint) tab->ref.key == tab->quick->index &&
- tab->ref.key_length < tab->quick->max_used_key_length)
- {
- /* Range uses longer key; Use this instead of ref on key */
- tab->type=JT_ALL;
- use_quick_range=1;
- tab->use_quick=1;
- tab->ref.key= -1;
- tab->ref.key_parts=0; // Don't use ref key.
- join->best_positions[i].records_read= rows2double(tab->quick->records);
- }
- COND *tmp=make_cond_for_table(cond,used_tables,current_map);
- if (!tmp && tab->quick)
- { // Outer join
- /*
- Hack to handle the case where we only refer to a table
- in the ON part of an OUTER JOIN.
- */
- tmp=new Item_int((longlong) 1,1); // Always true
- }
- if (tmp)
- {
- SQL_SELECT *sel=tab->select=(SQL_SELECT*)
- join->thd->memdup((gptr) select, sizeof(SQL_SELECT));
- if (!sel)
- DBUG_RETURN(1); // End of memory
- add_cond_and_fix(&tab->select_cond, tmp);
- sel->cond= tab->select_cond;
- sel->head=tab->table;
- DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name););
- if (tab->quick)
- {
- /* Use quick key read if it's a constant and it's not used
- with key reading */
- if (tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
- && tab->type != JT_FT && (tab->type != JT_REF ||
- (uint) tab->ref.key == tab->quick->index))
- {
- sel->quick=tab->quick; // Use value from get_quick_...
- sel->quick_keys.clear_all();
- sel->needed_reg.clear_all();
- }
- else
- {
- delete tab->quick;
- }
- tab->quick=0;
- }
- uint ref_key=(uint) sel->head->reginfo.join_tab->ref.key+1;
- if (i == join->const_tables && ref_key)
- {
- if (!tab->const_keys.is_clear_all() &&
- tab->table->reginfo.impossible_range)
- DBUG_RETURN(1);
- }
- else if (tab->type == JT_ALL && ! use_quick_range)
- {
- if (!tab->const_keys.is_clear_all() &&
- tab->table->reginfo.impossible_range)
- DBUG_RETURN(1); // Impossible range
- /*
- We plan to scan all rows.
- Check again if we should use an index.
- We could have used an column from a previous table in
- the index if we are using limit and this is the first table
- */
- if ((!tab->keys.is_subset(tab->const_keys) && i > 0) ||
- (!tab->const_keys.is_clear_all() && i == join->const_tables &&
- join->unit->select_limit_cnt <
- join->best_positions[i].records_read &&
- !(join->select_options & OPTION_FOUND_ROWS)))
- {
- /* Join with outer join condition */
- COND *orig_cond=sel->cond;
- sel->cond= and_conds(sel->cond, tab->on_expr);
- /*
- We can't call sel->cond->fix_fields,
- as it will break tab->on_expr if it's AND condition
- (fix_fields currently removes extra AND/OR levels).
- Yet attributes of the just built condition are not needed.
- Thus we call sel->cond->quick_fix_field for safety.
- */
- if (sel->cond && !sel->cond->fixed)
- sel->cond->quick_fix_field();
- if (sel->test_quick_select(join->thd, tab->keys,
- used_tables & ~ current_map,
- (join->select_options &
- OPTION_FOUND_ROWS ?
- HA_POS_ERROR :
- join->unit->select_limit_cnt), 0) < 0)
- {
- /*
- Before reporting "Impossible WHERE" for the whole query
- we have to check isn't it only "impossible ON" instead
- */
- sel->cond=orig_cond;
- if (!tab->on_expr ||
- sel->test_quick_select(join->thd, tab->keys,
- used_tables & ~ current_map,
- (join->select_options &
- OPTION_FOUND_ROWS ?
- HA_POS_ERROR :
- join->unit->select_limit_cnt),0) < 0)
- DBUG_RETURN(1); // Impossible WHERE
- }
- else
- sel->cond=orig_cond;
- /* Fix for EXPLAIN */
- if (sel->quick)
- join->best_positions[i].records_read= sel->quick->records;
- }
- else
- {
- sel->needed_reg=tab->needed_reg;
- sel->quick_keys.clear_all();
- }
- if (!sel->quick_keys.is_subset(tab->checked_keys) ||
- !sel->needed_reg.is_subset(tab->checked_keys))
- {
- tab->keys=sel->quick_keys;
- tab->keys.merge(sel->needed_reg);
- tab->use_quick= (!sel->needed_reg.is_clear_all() &&
- (select->quick_keys.is_clear_all() ||
- (select->quick &&
- (select->quick->records >= 100L)))) ?
- 2 : 1;
- sel->read_tables= used_tables & ~current_map;
- }
- if (i != join->const_tables && tab->use_quick != 2)
- { /* Read with cache */
- if ((tmp=make_cond_for_table(cond,
- join->const_table_map |
- current_map,
- current_map)))
- {
- DBUG_EXECUTE("where",print_where(tmp,"cache"););
- tab->cache.select=(SQL_SELECT*)
- join->thd->memdup((gptr) sel, sizeof(SQL_SELECT));
- tab->cache.select->cond=tmp;
- tab->cache.select->read_tables=join->const_table_map;
- }
- }
- }
- }
- }
- }
- DBUG_RETURN(0);
- }
- static void
- make_join_readinfo(JOIN *join, uint options)
- {
- uint i;
- bool statistics= test(!(join->select_options & SELECT_DESCRIBE));
- DBUG_ENTER("make_join_readinfo");
- for (i=join->const_tables ; i < join->tables ; i++)
- {
- JOIN_TAB *tab=join->join_tab+i;
- TABLE *table=tab->table;
- tab->read_record.table= table;
- tab->read_record.file=table->file;
- tab->next_select=sub_select; /* normal select */
- switch (tab->type) {
- case JT_SYSTEM: // Only happens with left join
- table->status=STATUS_NO_RECORD;
- tab->read_first_record= join_read_system;
- tab->read_record.read_record= join_no_more_records;
- break;
- case JT_CONST: // Only happens with left join
- table->status=STATUS_NO_RECORD;
- tab->read_first_record= join_read_const;
- tab->read_record.read_record= join_no_more_records;
- if (table->used_keys.is_set(tab->ref.key) &&
- !table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- break;
- case JT_EQ_REF:
- table->status=STATUS_NO_RECORD;
- if (tab->select)
- {
- delete tab->select->quick;
- tab->select->quick=0;
- }
- delete tab->quick;
- tab->quick=0;
- tab->read_first_record= join_read_key;
- tab->read_record.read_record= join_no_more_records;
- if (table->used_keys.is_set(tab->ref.key) &&
- !table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- break;
- case JT_REF_OR_NULL:
- case JT_REF:
- table->status=STATUS_NO_RECORD;
- if (tab->select)
- {
- delete tab->select->quick;
- tab->select->quick=0;
- }
- delete tab->quick;
- tab->quick=0;
- if (table->used_keys.is_set(tab->ref.key) &&
- !table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- if (tab->type == JT_REF)
- {
- tab->read_first_record= join_read_always_key;
- tab->read_record.read_record= join_read_next_same;
- }
- else
- {
- tab->read_first_record= join_read_always_key_or_null;
- tab->read_record.read_record= join_read_next_same_or_null;
- }
- break;
- case JT_FT:
- table->status=STATUS_NO_RECORD;
- tab->read_first_record= join_ft_read_first;
- tab->read_record.read_record= join_ft_read_next;
- break;
- case JT_ALL:
- /*
- If previous table use cache
- */
- table->status=STATUS_NO_RECORD;
- if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) &&
- tab->use_quick != 2 && !tab->on_expr)
- {
- if ((options & SELECT_DESCRIBE) ||
- !join_init_cache(join->thd,join->join_tab+join->const_tables,
- i-join->const_tables))
- {
- tab[-1].next_select=sub_select_cache; /* Patch previous */
- }
- }
- /* These init changes read_record */
- if (tab->use_quick == 2)
- {
- join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED;
- tab->read_first_record= join_init_quick_read_record;
- if (statistics)
- statistic_increment(select_range_check_count, &LOCK_status);
- }
- else
- {
- tab->read_first_record= join_init_read_record;
- if (i == join->const_tables)
- {
- if (tab->select && tab->select->quick)
- {
- if (statistics)
- statistic_increment(select_range_count, &LOCK_status);
- }
- else
- {
- join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
- if (statistics)
- statistic_increment(select_scan_count, &LOCK_status);
- }
- }
- else
- {
- if (tab->select && tab->select->quick)
- {
- if (statistics)
- statistic_increment(select_full_range_join_count, &LOCK_status);
- }
- else
- {
- join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
- if (statistics)
- statistic_increment(select_full_join_count, &LOCK_status);
- }
- }
- if (!table->no_keyread)
- {
- if (tab->select && tab->select->quick &&
- table->used_keys.is_set(tab->select->quick->index))
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- else if (!table->used_keys.is_clear_all() &&
- !(tab->select && tab->select->quick))
- { // Only read index tree
- tab->index=find_shortest_key(table, & table->used_keys);
- tab->read_first_record= join_read_first;
- tab->type=JT_NEXT; // Read with index_first / index_next
- }
- }
- }
- break;
- default:
- DBUG_PRINT("error",("Table type %d found",tab->type)); /* purecov: deadcode */
- break; /* purecov: deadcode */
- case JT_UNKNOWN:
- case JT_MAYBE_REF:
- abort(); /* purecov: deadcode */
- }
- }
- join->join_tab[join->tables-1].next_select=0; /* Set by do_select */
- DBUG_VOID_RETURN;
- }
- /*
- Give error if we some tables are done with a full join
- SYNOPSIS
- error_if_full_join()
- join Join condition
- USAGE
- This is used by multi_table_update and multi_table_delete when running
- in safe mode
- RETURN VALUES
- 0 ok
- 1 Error (full join used)
- */
- bool error_if_full_join(JOIN *join)
- {
- for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
- tab < end;
- tab++)
- {
- if (tab->type == JT_ALL && (!tab->select || !tab->select->quick))
- {
- my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0));
- return(1);
- }
- }
- return(0);
- }
- /*
- cleanup JOIN_TAB
- SYNOPSIS
- JOIN_TAB::cleanup()
- */
- void JOIN_TAB::cleanup()
- {
- delete select;
- select= 0;
- delete quick;
- quick= 0;
- x_free(cache.buff);
- cache.buff= 0;
- if (table)
- {
- if (table->key_read)
- {
- table->key_read= 0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
- table->file->ha_index_or_rnd_end();
- /*
- We need to reset this for next select
- (Tested in part_of_refkey)
- */
- table->reginfo.join_tab= 0;
- }
- end_read_record(&read_record);
- }
- /*
- Free resources of given join
- SYNOPSIS
- JOIN::join_free()
- fill - true if we should free all resources, call with full==1 should be
- last, before it this function can be called with full==0
- NOTE: with subquery this function definitely will be called several times,
- but even for simple query it can be called several times.
- */
- void
- JOIN::join_free(bool full)
- {
- JOIN_TAB *tab,*end;
- DBUG_ENTER("JOIN::join_free");
- full= full || (!select_lex->uncacheable &&
- !thd->lex->subqueries &&
- !thd->lex->describe); // do not cleanup too early on EXPLAIN
- if (table)
- {
- /*
- Only a sorted table may be cached. This sorted table is always the
- first non const table in join->table
- */
- if (tables > const_tables) // Test for not-const tables
- {
- free_io_cache(table[const_tables]);
- filesort_free_buffers(table[const_tables]);
- }
- for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); unit;
- unit= unit->next_unit())
- {
- JOIN *join;
- for (SELECT_LEX *sl= unit->first_select_in_union(); sl;
- sl= sl->next_select())
- if ((join= sl->join))
- join->join_free(full);
- }
- if (full)
- {
- for (tab= join_tab, end= tab+tables; tab != end; tab++)
- tab->cleanup();
- table= 0;
- tables= 0;
- }
- else
- {
- for (tab= join_tab, end= tab+tables; tab != end; tab++)
- {
- if (tab->table)
- tab->table->file->ha_index_or_rnd_end();
- }
- }
- }
- /*
- We are not using tables anymore
- Unlock all tables. We may be in an INSERT .... SELECT statement.
- */
- if (full && lock && thd->lock && !(select_options & SELECT_NO_UNLOCK) &&
- !select_lex->subquery_in_having)
- {
- // TODO: unlock tables even if the join isn't top level select in the tree
- if (select_lex == (thd->lex->unit.fake_select_lex ?
- thd->lex->unit.fake_select_lex : &thd->lex->select_lex))
- {
- mysql_unlock_read_tables(thd, lock); // Don't free join->lock
- lock=0;
- }
- }
- if (full)
- {
- group_fields.delete_elements();
- /*
- We can't call delete_elements() on copy_funcs as this will cause
- problems in free_elements() as some of the elements are then deleted.
- */
- tmp_table_param.copy_funcs.empty();
- tmp_table_param.cleanup();
- }
- DBUG_VOID_RETURN;
- }
- /*****************************************************************************
- Remove the following expressions from ORDER BY and GROUP BY:
- Constant expressions
- Expression that only uses tables that are of type EQ_REF and the reference
- is in the ORDER list or if all refereed tables are of the above type.
- In the following, the X field can be removed:
- SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t1.a,t2.X
- SELECT * FROM t1,t2,t3 WHERE t1.a=t2.a AND t2.b=t3.b ORDER BY t1.a,t3.X
- These can't be optimized:
- SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.X,t1.a
- SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b ORDER BY t1.a,t2.c
- SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.b,t1.a
- *****************************************************************************/
- static bool
- eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab)
- {
- if (tab->cached_eq_ref_table) // If cached
- return tab->eq_ref_table;
- tab->cached_eq_ref_table=1;
- if (tab->type == JT_CONST) // We can skip const tables
- return (tab->eq_ref_table=1); /* purecov: inspected */
- if (tab->type != JT_EQ_REF || tab->table->maybe_null)
- return (tab->eq_ref_table=0); // We must use this
- Item **ref_item=tab->ref.items;
- Item **end=ref_item+tab->ref.key_parts;
- uint found=0;
- table_map map=tab->table->map;
- for (; ref_item != end ; ref_item++)
- {
- if (! (*ref_item)->const_item())
- { // Not a const ref
- ORDER *order;
- for (order=start_order ; order ; order=order->next)
- {
- if ((*ref_item)->eq(order->item[0],0))
- break;
- }
- if (order)
- {
- found++;
- DBUG_ASSERT(!(order->used & map));
- order->used|=map;
- continue; // Used in ORDER BY
- }
- if (!only_eq_ref_tables(join,start_order, (*ref_item)->used_tables()))
- return (tab->eq_ref_table=0);
- }
- }
- /* Check that there was no reference to table before sort order */
- for (; found && start_order ; start_order=start_order->next)
- {
- if (start_order->used & map)
- {
- found--;
- continue;
- }
- if (start_order->depend_map & map)
- return (tab->eq_ref_table=0);
- }
- return tab->eq_ref_table=1;
- }
- static bool
- only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
- {
- if (specialflag & SPECIAL_SAFE_MODE)
- return 0; // skip this optimize /* purecov: inspected */
- for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1)
- {
- if (tables & 1 && !eq_ref_table(join, order, *tab))
- return 0;
- }
- return 1;
- }
- /* Update the dependency map for the tables */
- static void update_depend_map(JOIN *join)
- {
- JOIN_TAB *join_tab=join->join_tab, *end=join_tab+join->tables;
- for (; join_tab != end ; join_tab++)
- {
- TABLE_REF *ref= &join_tab->ref;
- table_map depend_map=0;
- Item **item=ref->items;
- uint i;
- for (i=0 ; i < ref->key_parts ; i++,item++)
- depend_map|=(*item)->used_tables();
- ref->depend_map=depend_map & ~OUTER_REF_TABLE_BIT;
- depend_map&= ~OUTER_REF_TABLE_BIT;
- for (JOIN_TAB **tab=join->map2table;
- depend_map ;
- tab++,depend_map>>=1 )
- {
- if (depend_map & 1)
- ref->depend_map|=(*tab)->ref.depend_map;
- }
- }
- }
- /* Update the dependency map for the sort order */
- static void update_depend_map(JOIN *join, ORDER *order)
- {
- for (; order ; order=order->next)
- {
- table_map depend_map;
- order->item[0]->update_used_tables();
- order->depend_map=depend_map=order->item[0]->used_tables();
- // Not item_sum(), RAND() and no reference to table outside of sub select
- if (!(order->depend_map & (OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)))
- {
- for (JOIN_TAB **tab=join->map2table;
- depend_map ;
- tab++, depend_map>>=1)
- {
- if (depend_map & 1)
- order->depend_map|=(*tab)->ref.depend_map;
- }
- }
- }
- }
- /*
- Remove all constants and check if ORDER only contains simple expressions
- SYNOPSIS
- remove_const()
- join Join handler
- first_order List of SORT or GROUP order
- cond WHERE statement
- change_list Set to 1 if we should remove things from list
- If this is not set, then only simple_order is
- calculated
- simple_order Set to 1 if we are only using simple expressions
- RETURN
- Returns new sort order
- simple_order is set to 1 if sort_order only uses fields from head table
- and the head table is not a LEFT JOIN table
- */
- static ORDER *
- remove_const(JOIN *join,ORDER *first_order, COND *cond,
- bool change_list, bool *simple_order)
- {
- if (join->tables == join->const_tables)
- return change_list ? 0 : first_order; // No need to sort
- ORDER *order,**prev_ptr;
- table_map first_table= join->join_tab[join->const_tables].table->map;
- table_map not_const_tables= ~join->const_table_map;
- table_map ref;
- DBUG_ENTER("remove_const");
- prev_ptr= &first_order;
- *simple_order= join->join_tab[join->const_tables].on_expr ? 0 : 1;
- /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */
- update_depend_map(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)
- *simple_order=0; // Must do a temp table to sort
- else if (!(order_tables & not_const_tables))
- {
- DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
- continue; // skip const item
- }
- else
- {
- if (order_tables & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))
- *simple_order=0;
- else
- {
- Item *comp_item=0;
- if (cond && const_expression_in_where(cond,order->item[0], &comp_item))
- {
- DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
- continue;
- }
- if ((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;
- }
- *simple_order=0; // Must do a temp table to sort
- }
- }
- }
- 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;
- DBUG_PRINT("exit",("simple_order: %d",(int) *simple_order));
- DBUG_RETURN(first_order);
- }
- static int
- return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
- List<Item> &fields, bool send_row, uint select_options,
- const char *info, Item *having, Procedure *procedure,
- SELECT_LEX_UNIT *unit)
- {
- DBUG_ENTER("return_zero_rows");
- if (select_options & SELECT_DESCRIBE)
- {
- select_describe(join, FALSE, FALSE, FALSE, info);
- DBUG_RETURN(0);
- }
- join->join_free(0);
- if (send_row)
- {
- for (TABLE_LIST *table=tables; table ; table=table->next)
- mark_as_null_row(table->table); // All fields are NULL
- if (having && having->val_int() == 0)
- send_row=0;
- }
- if (!(result->send_fields(fields,1)))
- {
- if (send_row)
- {
- List_iterator_fast<Item> it(fields);
- Item *item;
- while ((item= it++))
- item->no_rows_in_result();
- result->send_data(fields);
- }
- result->send_eof(); // Should be safe
- }
- /* Update results for FOUND_ROWS */
- join->thd->limit_found_rows= join->thd->examined_row_count= 0;
- DBUG_RETURN(0);
- }
- static void clear_tables(JOIN *join)
- {
- for (uint i=0 ; i < join->tables ; i++)
- mark_as_null_row(join->table[i]); // All fields are NULL
- }
- /*****************************************************************************
- Make som simple condition optimization:
- If there is a test 'field = const' change all refs to 'field' to 'const'
- Remove all dummy tests 'item = item', 'const op const'.
- Remove all 'item is NULL', when item can never be null!
- item->marker should be 0 for all items on entry
- Return in cond_value FALSE if condition is impossible (1 = 2)
- *****************************************************************************/
- class COND_CMP :public ilink {
- public:
- static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); }
- static void operator delete(void *ptr __attribute__((unused)),
- size_t size __attribute__((unused))) {} /*lint -e715 */
- Item *and_level;
- Item_func *cmp_func;
- COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {}
- };
- #ifdef __GNUC__
- template class I_List<COND_CMP>;
- template class I_List_iterator<COND_CMP>;
- template class List<Item_func_match>;
- template class List_iterator<Item_func_match>;
- #endif
- /*
- change field = field to field = const for each found field = const in the
- and_level
- */
- static void
- change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
- Item *and_father, Item *cond,
- Item *field, Item *value)
- {
- if (cond->type() == Item::COND_ITEM)
- {
- bool and_level= ((Item_cond*) cond)->functype() ==
- Item_func::COND_AND_FUNC;
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- while ((item=li++))
- change_cond_ref_to_const(thd, save_list,and_level ? cond : item, item,
- field, value);
- return;
- }
- if (cond->eq_cmp_result() == Item::COND_OK)
- return; // Not a boolean function
- Item_bool_func2 *func= (Item_bool_func2*) cond;
- Item **args= func->arguments();
- Item *left_item= args[0];
- Item *right_item= args[1];
- Item_func::Functype functype= func->functype();
- if (right_item->eq(field,0) && left_item != value &&
- (left_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- left_item->collation.collation == value->collation.collation))
- {
- Item *tmp=value->new_item();
- if (tmp)
- {
- thd->change_item_tree(args + 1, tmp);
- func->update_used_tables();
- if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC)
- && and_father != cond && !left_item->const_item())
- {
- cond->marker=1;
- COND_CMP *tmp2;
- if ((tmp2=new COND_CMP(and_father,func)))
- save_list->push_back(tmp2);
- }
- func->set_cmp_func();
- }
- }
- else if (left_item->eq(field,0) && right_item != value &&
- (right_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- right_item->collation.collation == value->collation.collation))
- {
- Item *tmp=value->new_item();
- if (tmp)
- {
- thd->change_item_tree(args, tmp);
- value= tmp;
- func->update_used_tables();
- if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC)
- && and_father != cond && !right_item->const_item())
- {
- args[0]= args[1]; // For easy check
- thd->change_item_tree(args + 1, value);
- cond->marker=1;
- COND_CMP *tmp2;
- if ((tmp2=new COND_CMP(and_father,func)))
- save_list->push_back(tmp2);
- }
- func->set_cmp_func();
- }
- }
- }
- /*
- Remove additional condition inserted by IN/ALL/ANY transformation
- SYNOPSIS
- remove_additional_cond()
- conds - condition for processing
- RETURN VALUES
- new conditions
- */
- static Item *remove_additional_cond(Item* conds)
- {
- if (conds->name == in_additional_cond)
- return 0;
- if (conds->type() == Item::COND_ITEM)
- {
- Item_cond *cnd= (Item_cond*) conds;
- List_iterator<Item> li(*(cnd->argument_list()));
- Item *item;
- while ((item= li++))
- {
- if (item->name == in_additional_cond)
- {
- li.remove();
- if (cnd->argument_list()->elements == 1)
- return cnd->argument_list()->head();
- return conds;
- }
- }
- }
- return conds;
- }
- static void
- propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
- COND *and_father, COND *cond)
- {
- if (cond->type() == Item::COND_ITEM)
- {
- bool and_level= ((Item_cond*) cond)->functype() ==
- Item_func::COND_AND_FUNC;
- List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- I_List<COND_CMP> save;
- while ((item=li++))
- {
- propagate_cond_constants(thd, &save,and_level ? cond : item, item);
- }
- if (and_level)
- { // Handle other found items
- I_List_iterator<COND_CMP> cond_itr(save);
- COND_CMP *cond_cmp;
- while ((cond_cmp=cond_itr++))
- {
- Item **args= cond_cmp->cmp_func->arguments();
- if (!args[0]->const_item())
- change_cond_ref_to_const(thd, &save,cond_cmp->and_level,
- cond_cmp->and_level, args[0], args[1]);
- }
- }
- }
- else if (and_father != cond && !cond->marker) // In a AND group
- {
- if (cond->type() == Item::FUNC_ITEM &&
- (((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
- ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC))
- {
- Item_func_eq *func=(Item_func_eq*) cond;
- Item **args= func->arguments();
- bool left_const= args[0]->const_item();
- bool right_const= args[1]->const_item();
- if (!(left_const && right_const) &&
- args[0]->result_type() == args[1]->result_type())
- {
- if (right_const)
- {
- resolve_const_item(thd, &args[1], args[0]);
- func->update_used_tables();
- change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[0], args[1]);
- }
- else if (left_const)
- {
- resolve_const_item(thd, &args[0], args[1]);
- func->update_used_tables();
- change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[1], args[0]);
- }
- }
- }
- }
- }
- static COND *
- optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value)
- {
- SELECT_LEX *select= thd->lex->current_select;
- DBUG_ENTER("optimize_cond");
- if (conds)
- {
- DBUG_EXECUTE("where", print_where(conds, "original"););
- /* change field = field to field = const for each found field = const */
- propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
- /*
- Remove all instances of item == item
- Remove all and-levels where CONST item != CONST item
- */
- DBUG_EXECUTE("where", print_where(conds, "after const change"););
- conds= remove_eq_conds(thd, conds, cond_value);
- DBUG_EXECUTE("info", print_where(conds, "after remove"););
- }
- else
- {
- *cond_value= Item::COND_TRUE;
- select->prep_where= 0;
- }
- DBUG_RETURN(conds);
- }
- /*
- Remove const and eq items. Return new item, or NULL if no condition
- cond_value is set to according:
- COND_OK query is possible (field = constant)
- COND_TRUE always true ( 1 = 1 )
- COND_FALSE always false ( 1 = 2 )
- */
- COND *
- remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
- {
- if (cond->type() == Item::COND_ITEM)
- {
- bool and_level= ((Item_cond*) cond)->functype()
- == Item_func::COND_AND_FUNC;
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item::cond_result tmp_cond_value;
- bool should_fix_fields=0;
- *cond_value=Item::COND_UNDEF;
- Item *item;
- while ((item=li++))
- {
- Item *new_item=remove_eq_conds(thd, item, &tmp_cond_value);
- if (!new_item)
- li.remove();
- else if (item != new_item)
- {
- VOID(li.replace(new_item));
- should_fix_fields=1;
- }
- if (*cond_value == Item::COND_UNDEF)
- *cond_value=tmp_cond_value;
- switch (tmp_cond_value) {
- case Item::COND_OK: // Not TRUE or FALSE
- if (and_level || *cond_value == Item::COND_FALSE)
- *cond_value=tmp_cond_value;
- break;
- case Item::COND_FALSE:
- if (and_level)
- {
- *cond_value=tmp_cond_value;
- return (COND*) 0; // Always false
- }
- break;
- case Item::COND_TRUE:
- if (!and_level)
- {
- *cond_value= tmp_cond_value;
- return (COND*) 0; // Always true
- }
- break;
- case Item::COND_UNDEF: // Impossible
- break; /* purecov: deadcode */
- }
- }
- if (should_fix_fields)
- cond->update_used_tables();
- if (!((Item_cond*) cond)->argument_list()->elements ||
- *cond_value != Item::COND_OK)
- return (COND*) 0;
- if (((Item_cond*) cond)->argument_list()->elements == 1)
- { // Remove list
- item= ((Item_cond*) cond)->argument_list()->head();
- ((Item_cond*) cond)->argument_list()->empty();
- return item;
- }
- }
- else if (cond->type() == Item::FUNC_ITEM &&
- ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
- {
- /*
- Handles this special case for some ODBC applications:
- The are requesting the row that was just updated with a auto_increment
- value with this construct:
- SELECT * from table_name where auto_increment_column IS NULL
- This will be changed to:
- SELECT * from table_name where auto_increment_column = LAST_INSERT_ID
- */
- Item_func_isnull *func=(Item_func_isnull*) cond;
- Item **args= func->arguments();
- if (args[0]->type() == Item::FIELD_ITEM)
- {
- Field *field=((Item_field*) args[0])->field;
- if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
- (thd->options & OPTION_AUTO_IS_NULL) &&
- thd->insert_id())
- {
- #ifdef HAVE_QUERY_CACHE
- query_cache_abort(&thd->net);
- #endif
- COND *new_cond;
- if ((new_cond= new Item_func_eq(args[0],
- new Item_int("last_insert_id()",
- thd->insert_id(),
- 21))))
- {
- cond=new_cond;
- cond->fix_fields(thd, 0, &cond);
- }
- thd->insert_id(0); // Clear for next request
- }
- /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
- else if (((field->type() == FIELD_TYPE_DATE) ||
- (field->type() == FIELD_TYPE_DATETIME)) &&
- (field->flags & NOT_NULL_FLAG) &&
- !field->table->maybe_null)
- {
- COND *new_cond;
- if ((new_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2))))
- {
- cond=new_cond;
- cond->fix_fields(thd, 0, &cond);
- }
- }
- }
- }
- else if (cond->const_item())
- {
- *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
- return (COND*) 0;
- }
- else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
- { // boolan compare function
- Item *left_item= ((Item_func*) cond)->arguments()[0];
- Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->eq(right_item,1))
- {
- if (!left_item->maybe_null ||
- ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
- return (COND*) 0; // Compare of identical items
- }
- }
- *cond_value=Item::COND_OK;
- return cond; // Point at next and level
- }
- /*
- Return 1 if the item is a const value in all the WHERE clause
- */
- static bool
- const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
- {
- if (cond->type() == Item::COND_ITEM)
- {
- bool and_level= (((Item_cond*) cond)->functype()
- == Item_func::COND_AND_FUNC);
- List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- while ((item=li++))
- {
- bool res=const_expression_in_where(item, comp_item, const_item);
- if (res) // Is a const value
- {
- if (and_level)
- return 1;
- }
- else if (!and_level)
- return 0;
- }
- return and_level ? 0 : 1;
- }
- else if (cond->eq_cmp_result() != Item::COND_OK)
- { // boolan compare function
- Item_func* func= (Item_func*) cond;
- if (func->functype() != Item_func::EQUAL_FUNC &&
- func->functype() != Item_func::EQ_FUNC)
- return 0;
- Item *left_item= ((Item_func*) cond)->arguments()[0];
- Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->eq(comp_item,1))
- {
- if (right_item->const_item())
- {
- if (*const_item)
- return right_item->eq(*const_item, 1);
- *const_item=right_item;
- return 1;
- }
- }
- else if (right_item->eq(comp_item,1))
- {
- if (left_item->const_item())
- {
- if (*const_item)
- return left_item->eq(*const_item, 1);
- *const_item=left_item;
- return 1;
- }
- }
- }
- return 0;
- }
- /****************************************************************************
- Create internal temporary table
- ****************************************************************************/
- /*
- Create field for temporary table from given field
-
- SYNOPSIS
- create_tmp_field_from_field()
- thd Thread handler
- org_field field from which new field will be created
- name New field name
- item Item to create a field for
- table Temporary table
- item !=NULL if item->result_field should point to new field.
- This is relevant for how fill_record() is going to work:
- If item != NULL then fill_record() will update
- the record in the original table.
- If item == NULL then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
- RETURN
- 0 on error
- new_created field
- */
- static Field* create_tmp_field_from_field(THD *thd, Field* org_field,
- const char *name, TABLE *table,
- Item_field *item,
- uint convert_blob_length)
- {
- Field *new_field;
- if (convert_blob_length && org_field->flags & BLOB_FLAG)
- new_field= new Field_varstring(convert_blob_length, org_field->maybe_null(),
- org_field->field_name, table,
- org_field->charset());
- else
- new_field= org_field->new_field(thd->mem_root, table);
- if (new_field)
- {
- if (item)
- item->result_field= new_field;
- else
- new_field->field_name= name;
- if (org_field->maybe_null() || (item && item->maybe_null))
- new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
- if (org_field->type() == FIELD_TYPE_VAR_STRING)
- table->db_create_options|= HA_OPTION_PACK_RECORD;
- }
- return new_field;
- }
- /*
- Create field for temporary table using type of given item
-
- SYNOPSIS
- create_tmp_field_from_item()
- thd Thread handler
- item Item to create a field for
- table Temporary table
- copy_func If set and item is a function, store copy of item
- in this array
- modify_item 1 if item->result_field should point to new item.
- This is relevent for how fill_record() is going to
- work:
- If modify_item is 1 then fill_record() will update
- the record in the original table.
- If modify_item is 0 then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
- RETURN
- 0 on error
- new_created field
- */
- static Field* create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
- Item ***copy_func, bool modify_item,
- uint convert_blob_length)
- {
- bool maybe_null=item->maybe_null;
- Field *new_field;
- LINT_INIT(new_field);
- switch (item->result_type()) {
- case REAL_RESULT:
- new_field=new Field_double(item->max_length, maybe_null,
- item->name, table, item->decimals);
- break;
- case INT_RESULT:
- new_field=new Field_longlong(item->max_length, maybe_null,
- item->name, table, item->unsigned_flag);
- break;
- case STRING_RESULT:
- DBUG_ASSERT(item->collation.collation);
-
- enum enum_field_types type;
- /*
- DATE/TIME fields have STRING_RESULT result type. To preserve
- type they needed to be handled separately.
- */
- if ((type= item->field_type()) == MYSQL_TYPE_DATETIME ||
- type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE)
- new_field= item->tmp_table_field_from_field_type(table);
- else if (item->max_length/item->collation.collation->mbmaxlen > 255)
- {
- if (convert_blob_length)
- new_field= new Field_varstring(convert_blob_length, maybe_null,
- item->name, table,
- item->collation.collation);
- else
- new_field= new Field_blob(item->max_length, maybe_null, item->name,
- table, item->collation.collation);
- }
- else
- new_field= new Field_string(item->max_length, maybe_null, item->name,
- table, item->collation.collation);
- break;
- case ROW_RESULT:
- default:
- // This case should never be choosen
- DBUG_ASSERT(0);
- new_field= 0; // to satisfy compiler (uninitialized variable)
- break;
- }
- if (copy_func && item->is_result_field())
- *((*copy_func)++) = item; // Save for copy_funcs
- if (modify_item)
- item->set_result_field(new_field);
- return new_field;
- }
- /*
- Create field for temporary table
- SYNOPSIS
- create_tmp_field()
- thd Thread handler
- table Temporary table
- item Item to create a field for
- type Type of item (normally item->type)
- copy_func If set and item is a function, store copy of item
- in this array
- from_field if field will be created using other field as example,
- pointer example field will be written here
- group 1 if we are going to do a relative group by on result
- modify_item 1 if item->result_field should point to new item.
- This is relevent for how fill_record() is going to
- work:
- If modify_item is 1 then fill_record() will update
- the record in the original table.
- If modify_item is 0 then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
- RETURN
- 0 on error
- new_created field
- */
- Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
- Item ***copy_func, Field **from_field,
- bool group, bool modify_item, uint convert_blob_length)
- {
- switch (type) {
- case Item::SUM_FUNC_ITEM:
- {
- Item_sum *item_sum=(Item_sum*) item;
- bool maybe_null=item_sum->maybe_null;
- switch (item_sum->sum_func()) {
- case Item_sum::AVG_FUNC: /* Place for sum & count */
- if (group)
- return new Field_string(sizeof(double)+sizeof(longlong),
- 0, item->name,table,&my_charset_bin);
- else
- return new Field_double(item_sum->max_length,maybe_null,
- item->name, table, item_sum->decimals);
- case Item_sum::VARIANCE_FUNC: /* Place for sum & count */
- case Item_sum::STD_FUNC:
- if (group)
- return new Field_string(sizeof(double)*2+sizeof(longlong),
- 0, item->name,table,&my_charset_bin);
- else
- return new Field_double(item_sum->max_length, maybe_null,
- item->name,table,item_sum->decimals);
- case Item_sum::UNIQUE_USERS_FUNC:
- return new Field_long(9,maybe_null,item->name,table,1);
- case Item_sum::MIN_FUNC:
- case Item_sum::MAX_FUNC:
- if (item_sum->args[0]->type() == Item::FIELD_ITEM)
- {
- *from_field= ((Item_field*) item_sum->args[0])->field;
- return create_tmp_field_from_field(thd, *from_field, item->name, table,
- NULL, convert_blob_length);
- }
- /* fall through */
- default:
- switch (item_sum->result_type()) {
- case REAL_RESULT:
- return new Field_double(item_sum->max_length,maybe_null,
- item->name,table,item_sum->decimals);
- case INT_RESULT:
- return new Field_longlong(item_sum->max_length,maybe_null,
- item->name,table,item->unsigned_flag);
- case STRING_RESULT:
- if (item_sum->max_length > 255)
- {
- if (convert_blob_length)
- return new Field_varstring(convert_blob_length, maybe_null,
- item->name, table,
- item->collation.collation);
- else
- return new Field_blob(item_sum->max_length, maybe_null, item->name,
- table, item->collation.collation);
- }
- return new Field_string(item_sum->max_length,maybe_null,
- item->name,table,item->collation.collation);
- case ROW_RESULT:
- default:
- // This case should never be choosen
- DBUG_ASSERT(0);
- thd->fatal_error();
- return 0;
- }
- }
- /* We never come here */
- }
- case Item::FIELD_ITEM:
- case Item::DEFAULT_VALUE_ITEM:
- {
- Item_field *field= (Item_field*) item;
- return create_tmp_field_from_field(thd, (*from_field= field->field),
- item->name, table,
- modify_item ? (Item_field*) item : NULL,
- convert_blob_length);
- }
- case Item::FUNC_ITEM:
- case Item::COND_ITEM:
- case Item::FIELD_AVG_ITEM:
- case Item::FIELD_STD_ITEM:
- case Item::SUBSELECT_ITEM:
- /* The following can only happen with 'CREATE TABLE ... SELECT' */
- case Item::PROC_ITEM:
- case Item::INT_ITEM:
- case Item::REAL_ITEM:
- case Item::STRING_ITEM:
- case Item::REF_ITEM:
- case Item::NULL_ITEM:
- case Item::VARBIN_ITEM:
- return create_tmp_field_from_item(thd, item, table, copy_func, modify_item,
- convert_blob_length);
- case Item::TYPE_HOLDER:
- return ((Item_type_holder *)item)->make_field_by_type(table);
- default: // Dosen't have to be stored
- return 0;
- }
- }
- /*
- Create a temp table according to a field list.
- Set distinct if duplicates could be removed
- Given fields field pointers are changed to point at tmp_table
- for send_fields
- */
- TABLE *
- create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
- ORDER *group, bool distinct, bool save_sum_fields,
- ulong select_options, ha_rows rows_limit,
- char *table_alias)
- {
- TABLE *table;
- uint i,field_count,reclength,null_count,null_pack_length,
- hidden_null_count, hidden_null_pack_length, hidden_field_count,
- blob_count,group_null_items;
- bool using_unique_constraint=0;
- bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS);
- char *tmpname,path[FN_REFLEN];
- byte *pos,*group_buff;
- uchar *null_flags;
- Field **reg_field, **from_field, **blob_field;
- Copy_field *copy=0;
- KEY *keyinfo;
- KEY_PART_INFO *key_part_info;
- Item **copy_func;
- MI_COLUMNDEF *recinfo;
- uint temp_pool_slot=MY_BIT_NONE;
- DBUG_ENTER("create_tmp_table");
- DBUG_PRINT("enter",("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d",
- (int) distinct, (int) save_sum_fields,
- (ulong) rows_limit,test(group)));
- statistic_increment(created_tmp_tables, &LOCK_status);
- if (use_temp_pool)
- temp_pool_slot = bitmap_set_next(&temp_pool);
- if (temp_pool_slot != MY_BIT_NONE) // we got a slot
- sprintf(path, "%s_%lx_%i", tmp_file_prefix,
- current_pid, temp_pool_slot);
- else // if we run out of slots or we are not using tempool
- sprintf(path,"%s%lx_%lx_%x", tmp_file_prefix,current_pid,
- thd->thread_id, thd->tmp_table++);
- fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, path);
- if (group)
- {
- if (!param->quick_group)
- group=0; // Can't use group key
- else for (ORDER *tmp=group ; tmp ; tmp=tmp->next)
- {
- (*tmp->item)->marker=4; // Store null in key
- if ((*tmp->item)->max_length >= MAX_CHAR_WIDTH)
- using_unique_constraint=1;
- }
- if (param->group_length >= MAX_BLOB_WIDTH)
- using_unique_constraint=1;
- if (group)
- distinct=0; // Can't use distinct
- }
- field_count=param->field_count+param->func_count+param->sum_func_count;
- hidden_field_count=param->hidden_field_count;
- if (!my_multi_malloc(MYF(MY_WME),
- &table,sizeof(*table),
- ®_field, sizeof(Field*)*(field_count+1),
- &blob_field, sizeof(Field*)*(field_count+1),
- &from_field, sizeof(Field*)*field_count,
- ©_func,sizeof(*copy_func)*(param->func_count+1),
- ¶m->keyinfo,sizeof(*param->keyinfo),
- &key_part_info,
- sizeof(*key_part_info)*(param->group_parts+1),
- ¶m->start_recinfo,
- sizeof(*param->recinfo)*(field_count*2+4),
- &tmpname,(uint) strlen(path)+1,
- &group_buff,group && ! using_unique_constraint ?
- param->group_length : 0,
- NullS))
- {
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
- DBUG_RETURN(NULL); /* purecov: inspected */
- }
- if (!(param->copy_field=copy=new Copy_field[field_count]))
- {
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
- my_free((gptr) table,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(NULL); /* purecov: inspected */
- }
- param->items_to_copy= copy_func;
- strmov(tmpname,path);
- /* make table according to fields */
- bzero((char*) table,sizeof(*table));
- bzero((char*) reg_field,sizeof(Field*)*(field_count+1));
- bzero((char*) from_field,sizeof(Field*)*field_count);
- table->field=reg_field;
- table->blob_field= (Field_blob**) blob_field;
- table->real_name=table->path=tmpname;
- table->table_name= table_alias;
- table->reginfo.lock_type=TL_WRITE; /* Will be updated */
- table->db_stat=HA_OPEN_KEYFILE+HA_OPEN_RNDFILE;
- table->blob_ptr_size=mi_portable_sizeof_char_ptr;
- table->map=1;
- table->tmp_table= TMP_TABLE;
- table->db_low_byte_first=1; // True for HEAP and MyISAM
- table->temp_pool_slot = temp_pool_slot;
- table->copy_blobs= 1;
- table->in_use= thd;
- table->keys_for_keyread.init();
- table->keys_in_use.init();
- table->read_only_keys.init();
- table->quick_keys.init();
- table->used_keys.init();
- table->keys_in_use_for_query.init();
- /* Calculate which type of fields we will store in the temporary table */
- reclength=blob_count=null_count=hidden_null_count=group_null_items=0;
- param->using_indirect_summary_function=0;
- List_iterator_fast<Item> li(fields);
- Item *item;
- Field **tmp_from_field=from_field;
- while ((item=li++))
- {
- Item::Type type=item->type();
- if (not_all_columns)
- {
- if (item->with_sum_func && type != Item::SUM_FUNC_ITEM)
- {
- /*
- Mark that the we have ignored an item that refers to a summary
- function. We need to know this if someone is going to use
- DISTINCT on the result.
- */
- param->using_indirect_summary_function=1;
- continue;
- }
- if (item->const_item() && (int) hidden_field_count <= 0)
- continue; // We don't have to store this
- }
- if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields)
- { /* Can't calc group yet */
- ((Item_sum*) item)->result_field=0;
- for (i=0 ; i < ((Item_sum*) item)->arg_count ; i++)
- {
- Item **argp= ((Item_sum*) item)->args + i;
- Item *arg= *argp;
- if (!arg->const_item())
- {
- Field *new_field=
- create_tmp_field(thd, table, arg, arg->type(), ©_func,
- tmp_from_field, group != 0,not_all_columns,
- param->convert_blob_length);
- if (!new_field)
- goto err; // Should be OOM
- tmp_from_field++;
- *(reg_field++)= new_field;
- reclength+=new_field->pack_length();
- if (new_field->flags & BLOB_FLAG)
- {
- *blob_field++= new_field;
- blob_count++;
- }
- thd->change_item_tree(argp, new Item_field(new_field));
- if (!(new_field->flags & NOT_NULL_FLAG))
- {
- null_count++;
- /*
- new_field->maybe_null() is still false, it will be
- changed below. But we have to setup Item_field correctly
- */
- (*argp)->maybe_null=1;
- }
- }
- }
- }
- else
- {
- /*
- The last parameter to create_tmp_field() is a bit tricky:
- We need to set it to 0 in union, to get fill_record() to modify the
- temporary table.
- We need to set it to 1 on multi-table-update and in select to
- write rows to the temporary table.
- We here distinguish between UNION and multi-table-updates by the fact
- that in the later case group is set to the row pointer.
- */
- Field *new_field= create_tmp_field(thd, table, item, type, ©_func,
- tmp_from_field, group != 0,
- not_all_columns || group !=0,
- param->convert_blob_length);
- if (!new_field)
- {
- if (thd->is_fatal_error)
- goto err; // Got OOM
- continue; // Some kindf of const item
- }
- if (type == Item::SUM_FUNC_ITEM)
- ((Item_sum *) item)->result_field= new_field;
- tmp_from_field++;
- reclength+=new_field->pack_length();
- if (!(new_field->flags & NOT_NULL_FLAG))
- null_count++;
- if (new_field->flags & BLOB_FLAG)
- {
- *blob_field++= new_field;
- blob_count++;
- }
- if (item->marker == 4 && item->maybe_null)
- {
- group_null_items++;
- new_field->flags|= GROUP_FLAG;
- }
- *(reg_field++) =new_field;
- }
- if (!--hidden_field_count)
- hidden_null_count=null_count;
- }
- DBUG_ASSERT(field_count >= (uint) (reg_field - table->field));
- field_count= (uint) (reg_field - table->field);
- *blob_field= 0; // End marker
- /* If result table is small; use a heap */
- if (blob_count || using_unique_constraint ||
- (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES ||(select_options & TMP_TABLE_FORCE_MYISAM))
- {
- table->file=get_new_handler(table,table->db_type=DB_TYPE_MYISAM);
- if (group &&
- (param->group_parts > table->file->max_key_parts() ||
- param->group_length > table->file->max_key_length()))
- using_unique_constraint=1;
- }
- else
- {
- table->file=get_new_handler(table,table->db_type=DB_TYPE_HEAP);
- }
- if (!using_unique_constraint)
- reclength+= group_null_items; // null flag is stored separately
- table->blob_fields=blob_count;
- if (blob_count == 0)
- {
- /* We need to ensure that first byte is not 0 for the delete link */
- if (param->hidden_field_count)
- hidden_null_count++;
- else
- null_count++;
- }
- hidden_null_pack_length=(hidden_null_count+7)/8;
- null_pack_length=hidden_null_count+(null_count+7)/8;
- reclength+=null_pack_length;
- if (!reclength)
- reclength=1; // Dummy select
- table->fields=field_count;
- table->reclength=reclength;
- {
- uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
- table->rec_buff_length=alloc_length;
- if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME))))
- goto err;
- table->record[1]= table->record[0]+alloc_length;
- table->default_values= table->record[1]+alloc_length;
- }
- copy_func[0]=0; // End marker
- recinfo=param->start_recinfo;
- null_flags=(uchar*) table->record[0];
- pos=table->record[0]+ null_pack_length;
- if (null_pack_length)
- {
- bzero((byte*) recinfo,sizeof(*recinfo));
- recinfo->type=FIELD_NORMAL;
- recinfo->length=null_pack_length;
- recinfo++;
- bfill(null_flags,null_pack_length,255); // Set null fields
- table->null_flags= (uchar*) table->record[0];
- table->null_fields= null_count+ hidden_null_count;
- table->null_bytes= null_pack_length;
- }
- null_count= (blob_count == 0) ? 1 : 0;
- hidden_field_count=param->hidden_field_count;
- for (i=0,reg_field=table->field; i < field_count; i++,reg_field++,recinfo++)
- {
- Field *field= *reg_field;
- uint length;
- bzero((byte*) recinfo,sizeof(*recinfo));
- if (!(field->flags & NOT_NULL_FLAG))
- {
- if (field->flags & GROUP_FLAG && !using_unique_constraint)
- {
- /*
- We have to reserve one byte here for NULL bits,
- as this is updated by 'end_update()'
- */
- *pos++=0; // Null is stored here
- recinfo->length=1;
- recinfo->type=FIELD_NORMAL;
- recinfo++;
- bzero((byte*) recinfo,sizeof(*recinfo));
- }
- else
- {
- recinfo->null_bit= 1 << (null_count & 7);
- recinfo->null_pos= null_count/8;
- }
- field->move_field((char*) pos,null_flags+null_count/8,
- 1 << (null_count & 7));
- null_count++;
- }
- else
- field->move_field((char*) pos,(uchar*) 0,0);
- field->reset();
- if (from_field[i])
- { /* Not a table Item */
- copy->set(field,from_field[i],save_sum_fields);
- copy++;
- }
- length=field->pack_length();
- pos+= length;
- /* Make entry for create table */
- recinfo->length=length;
- if (field->flags & BLOB_FLAG)
- recinfo->type= (int) FIELD_BLOB;
- else if (!field->zero_pack() &&
- (field->type() == FIELD_TYPE_STRING ||
- field->type() == FIELD_TYPE_VAR_STRING) &&
- length >= 10 && blob_count)
- recinfo->type=FIELD_SKIP_ENDSPACE;
- else
- recinfo->type=FIELD_NORMAL;
- if (!--hidden_field_count)
- null_count=(null_count+7) & ~7; // move to next byte
- // fix table name in field entry
- field->table_name= table->table_name;
- }
- param->copy_field_end=copy;
- param->recinfo=recinfo;
- store_record(table,default_values); // Make empty default record
- if (thd->variables.tmp_table_size == ~(ulong) 0) // No limit
- table->max_rows= ~(ha_rows) 0;
- else
- table->max_rows=(((table->db_type == DB_TYPE_HEAP) ?
- min(thd->variables.tmp_table_size,
- thd->variables.max_heap_table_size) :
- thd->variables.tmp_table_size)/ table->reclength);
- set_if_bigger(table->max_rows,1); // For dummy start options
- keyinfo=param->keyinfo;
- if (group)
- {
- DBUG_PRINT("info",("Creating group key in temporary table"));
- table->group=group; /* Table is grouped by key */
- param->group_buff=group_buff;
- table->keys=1;
- table->uniques= test(using_unique_constraint);
- table->key_info=keyinfo;
- keyinfo->key_part=key_part_info;
- keyinfo->flags=HA_NOSAME;
- keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
- keyinfo->key_length=0;
- keyinfo->rec_per_key=0;
- keyinfo->algorithm= HA_KEY_ALG_UNDEF;
- for (; group ; group=group->next,key_part_info++)
- {
- Field *field=(*group->item)->get_tmp_table_field();
- bool maybe_null=(*group->item)->maybe_null;
- key_part_info->null_bit=0;
- key_part_info->field= field;
- key_part_info->offset= field->offset();
- key_part_info->length= (uint16) field->pack_length();
- key_part_info->type= (uint8) field->key_type();
- key_part_info->key_type =
- ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT) ?
- 0 : FIELDFLAG_BINARY;
- if (!using_unique_constraint)
- {
- group->buff=(char*) group_buff;
- if (!(group->field=field->new_field(thd->mem_root,table)))
- goto err; /* purecov: inspected */
- if (maybe_null)
- {
- /*
- To be able to group on NULL, we reserve place in group_buff
- for the NULL flag just before the column.
- The field data is after this flag.
- The NULL flag is updated by 'end_update()' and 'end_write()'
- */
- keyinfo->flags|= HA_NULL_ARE_EQUAL; // def. that NULL == NULL
- key_part_info->null_bit=field->null_bit;
- key_part_info->null_offset= (uint) (field->null_ptr -
- (uchar*) table->record[0]);
- group->field->move_field((char*) ++group->buff);
- group_buff++;
- }
- else
- group->field->move_field((char*) group_buff);
- group_buff+= key_part_info->length;
- }
- keyinfo->key_length+= key_part_info->length;
- }
- }
- if (distinct)
- {
- /*
- Create an unique key or an unique constraint over all columns
- that should be in the result. In the temporary table, there are
- 'param->hidden_field_count' extra columns, whose null bits are stored
- in the first 'hidden_null_pack_length' bytes of the row.
- */
- DBUG_PRINT("info",("hidden_field_count: %d", param->hidden_field_count));
- null_pack_length-=hidden_null_pack_length;
- keyinfo->key_parts= ((field_count-param->hidden_field_count)+
- test(null_pack_length));
- set_if_smaller(table->max_rows, rows_limit);
- param->end_write_records= rows_limit;
- table->distinct=1;
- table->keys=1;
- if (blob_count)
- {
- using_unique_constraint=1;
- table->uniques=1;
- }
- if (!(key_part_info= (KEY_PART_INFO*)
- sql_calloc((keyinfo->key_parts)*sizeof(KEY_PART_INFO))))
- goto err;
- table->key_info=keyinfo;
- keyinfo->key_part=key_part_info;
- keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL;
- keyinfo->key_length=(uint16) reclength;
- keyinfo->name=(char*) "tmp";
- keyinfo->algorithm= HA_KEY_ALG_UNDEF;
- keyinfo->rec_per_key=0;
- if (null_pack_length)
- {
- key_part_info->null_bit=0;
- key_part_info->offset=hidden_null_pack_length;
- key_part_info->length=null_pack_length;
- key_part_info->field=new Field_string((char*) table->record[0],
- (uint32) key_part_info->length,
- (uchar*) 0,
- (uint) 0,
- Field::NONE,
- NullS, table, &my_charset_bin);
- key_part_info->key_type=FIELDFLAG_BINARY;
- key_part_info->type= HA_KEYTYPE_BINARY;
- key_part_info++;
- }
- /* Create a distinct key over the columns we are going to return */
- for (i=param->hidden_field_count, reg_field=table->field + i ;
- i < field_count;
- i++, reg_field++, key_part_info++)
- {
- key_part_info->null_bit=0;
- key_part_info->field= *reg_field;
- key_part_info->offset= (*reg_field)->offset();
- key_part_info->length= (uint16) (*reg_field)->pack_length();
- key_part_info->type= (uint8) (*reg_field)->key_type();
- key_part_info->key_type =
- ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT) ?
- 0 : FIELDFLAG_BINARY;
- }
- }
- if (thd->is_fatal_error) // If end of memory
- goto err; /* purecov: inspected */
- table->db_record_offset=1;
- if (table->db_type == DB_TYPE_MYISAM)
- {
- if (create_myisam_tmp_table(table,param,select_options))
- goto err;
- }
- if (!open_tmp_table(table))
- DBUG_RETURN(table);
- err:
- /*
- Hack to ensure that free_blobs() doesn't fail if blob_field is not yet
- complete
- */
- *table->blob_field= 0;
- free_tmp_table(thd,table); /* purecov: inspected */
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
- DBUG_RETURN(NULL); /* purecov: inspected */
- }
- static bool open_tmp_table(TABLE *table)
- {
- int error;
- if ((error=table->file->ha_open(table->real_name,O_RDWR,HA_OPEN_TMP_TABLE)))
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- table->db_stat=0;
- return(1);
- }
- (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
- return(0);
- }
- static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
- ulong options)
- {
- int error;
- MI_KEYDEF keydef;
- MI_UNIQUEDEF uniquedef;
- KEY *keyinfo=param->keyinfo;
- DBUG_ENTER("create_myisam_tmp_table");
- if (table->keys)
- { // Get keys for ni_create
- bool using_unique_constraint=0;
- HA_KEYSEG *seg= (HA_KEYSEG*) sql_calloc(sizeof(*seg) *
- keyinfo->key_parts);
- if (!seg)
- goto err;
- if (keyinfo->key_length >= table->file->max_key_length() ||
- keyinfo->key_parts > table->file->max_key_parts() ||
- table->uniques)
- {
- /* Can't create a key; Make a unique constraint instead of a key */
- table->keys=0;
- table->uniques=1;
- using_unique_constraint=1;
- bzero((char*) &uniquedef,sizeof(uniquedef));
- uniquedef.keysegs=keyinfo->key_parts;
- uniquedef.seg=seg;
- uniquedef.null_are_equal=1;
- /* Create extra column for hash value */
- bzero((byte*) param->recinfo,sizeof(*param->recinfo));
- param->recinfo->type= FIELD_CHECK;
- param->recinfo->length=MI_UNIQUE_HASH_LENGTH;
- param->recinfo++;
- table->reclength+=MI_UNIQUE_HASH_LENGTH;
- }
- else
- {
- /* Create an unique key */
- bzero((char*) &keydef,sizeof(keydef));
- keydef.flag=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY;
- keydef.keysegs= keyinfo->key_parts;
- keydef.seg= seg;
- }
- for (uint i=0; i < keyinfo->key_parts ; i++,seg++)
- {
- Field *field=keyinfo->key_part[i].field;
- seg->flag= 0;
- seg->language= field->charset()->number;
- seg->length= keyinfo->key_part[i].length;
- seg->start= keyinfo->key_part[i].offset;
- if (field->flags & BLOB_FLAG)
- {
- seg->type=
- ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
- HA_KEYTYPE_VARBINARY : HA_KEYTYPE_VARTEXT);
- seg->bit_start=seg->length - table->blob_ptr_size;
- seg->flag= HA_BLOB_PART;
- seg->length=0; // Whole blob in unique constraint
- }
- else
- {
- seg->type=
- ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
- HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT);
- if (!(field->flags & ZEROFILL_FLAG) &&
- (field->type() == FIELD_TYPE_STRING ||
- field->type() == FIELD_TYPE_VAR_STRING) &&
- keyinfo->key_part[i].length > 4)
- seg->flag|=HA_SPACE_PACK;
- }
- if (!(field->flags & NOT_NULL_FLAG))
- {
- seg->null_bit= field->null_bit;
- seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
- /*
- We are using a GROUP BY on something that contains NULL
- In this case we have to tell MyISAM that two NULL should
- on INSERT be compared as equal
- */
- if (!using_unique_constraint)
- keydef.flag|= HA_NULL_ARE_EQUAL;
- }
- }
- }
- MI_CREATE_INFO create_info;
- bzero((char*) &create_info,sizeof(create_info));
- if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
- create_info.data_file_length= ~(ulonglong) 0;
- if ((error=mi_create(table->real_name,table->keys,&keydef,
- (uint) (param->recinfo-param->start_recinfo),
- param->start_recinfo,
- table->uniques, &uniquedef,
- &create_info,
- HA_CREATE_TMP_TABLE)))
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- table->db_stat=0;
- goto err;
- }
- statistic_increment(created_tmp_disk_tables, &LOCK_status);
- table->db_record_offset=1;
- DBUG_RETURN(0);
- err:
- DBUG_RETURN(1);
- }
- void
- free_tmp_table(THD *thd, TABLE *entry)
- {
- const char *save_proc_info;
- DBUG_ENTER("free_tmp_table");
- DBUG_PRINT("enter",("table: %s",entry->table_name));
- save_proc_info=thd->proc_info;
- thd->proc_info="removing tmp table";
- free_blobs(entry);
- if (entry->file)
- {
- if (entry->db_stat)
- {
- (void) entry->file->close();
- }
- /*
- We can't call ha_delete_table here as the table may created in mixed case
- here and we have to ensure that delete_table gets the table name in
- the original case.
- */
- if (!(test_flags & TEST_KEEP_TMP_TABLES) || entry->db_type == DB_TYPE_HEAP)
- entry->file->delete_table(entry->real_name);
- delete entry->file;
- }
- /* free blobs */
- for (Field **ptr=entry->field ; *ptr ; ptr++)
- (*ptr)->free();
- my_free((gptr) entry->record[0],MYF(0));
- free_io_cache(entry);
- bitmap_clear_bit(&temp_pool, entry->temp_pool_slot);
- my_free((gptr) entry,MYF(0));
- thd->proc_info=save_proc_info;
- DBUG_VOID_RETURN;
- }
- /*
- * If a HEAP table gets full, create a MyISAM table and copy all rows to this
- */
- bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
- int error, bool ignore_last_dupp_key_error)
- {
- TABLE new_table;
- const char *save_proc_info;
- int write_err;
- DBUG_ENTER("create_myisam_from_heap");
- if (table->db_type != DB_TYPE_HEAP || error != HA_ERR_RECORD_FILE_FULL)
- {
- table->file->print_error(error,MYF(0));
- DBUG_RETURN(1);
- }
- new_table= *table;
- new_table.db_type=DB_TYPE_MYISAM;
- if (!(new_table.file=get_new_handler(&new_table,DB_TYPE_MYISAM)))
- DBUG_RETURN(1); // End of memory
- save_proc_info=thd->proc_info;
- thd->proc_info="converting HEAP to MyISAM";
- if (create_myisam_tmp_table(&new_table,param,
- thd->lex->select_lex.options | thd->options))
- goto err2;
- if (open_tmp_table(&new_table))
- goto err1;
- if (table->file->indexes_are_disabled())
- new_table.file->disable_indexes(HA_KEY_SWITCH_ALL);
- table->file->ha_index_or_rnd_end();
- table->file->ha_rnd_init(1);
- if (table->no_rows)
- {
- new_table.file->extra(HA_EXTRA_NO_ROWS);
- new_table.no_rows=1;
- }
- #ifdef TO_BE_DONE_LATER_IN_4_1
- /*
- To use start_bulk_insert() (which is new in 4.1) we need to find
- all places where a corresponding end_bulk_insert() should be put.
- */
- table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */
- new_table.file->start_bulk_insert(table->file->records);
- #else
- /* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */
- new_table.file->extra(HA_EXTRA_WRITE_CACHE);
- #endif
- /* copy all old rows */
- while (!table->file->rnd_next(new_table.record[1]))
- {
- if ((write_err=new_table.file->write_row(new_table.record[1])))
- goto err;
- }
- /* copy row that filled HEAP table */
- if ((write_err=new_table.file->write_row(table->record[0])))
- {
- if (write_err != HA_ERR_FOUND_DUPP_KEY &&
- write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error)
- goto err;
- }
- /* remove heap table and change to use myisam table */
- (void) table->file->ha_rnd_end();
- (void) table->file->close();
- (void) table->file->delete_table(table->real_name);
- delete table->file;
- table->file=0;
- *table =new_table;
- table->file->change_table_ptr(table);
- thd->proc_info= (!strcmp(save_proc_info,"Copying to tmp table") ?
- "Copying to tmp table on disk" : save_proc_info);
- DBUG_RETURN(0);
- err:
- DBUG_PRINT("error",("Got error: %d",write_err));
- table->file->print_error(error,MYF(0)); // Give table is full error
- (void) table->file->ha_rnd_end();
- (void) new_table.file->close();
- err1:
- new_table.file->delete_table(new_table.real_name);
- delete new_table.file;
- err2:
- thd->proc_info=save_proc_info;
- DBUG_RETURN(1);
- }
- /****************************************************************************
- Make a join of all tables and write it on socket or to table
- Return: 0 if ok
- 1 if error is sent
- -1 if error should be sent
- ****************************************************************************/
- static int
- do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
- {
- int error= 0;
- JOIN_TAB *join_tab;
- int (*end_select)(JOIN *, struct st_join_table *,bool);
- DBUG_ENTER("do_select");
- List<Item> *columns_list= procedure ? &join->procedure_fields_list : fields;
- join->procedure=procedure;
- /*
- Tell the client how many fields there are in a row
- */
- if (!table)
- join->result->send_fields(*columns_list, 1);
- else
- {
- VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
- empty_record(table);
- }
- join->tmp_table= table; /* Save for easy recursion */
- join->fields= fields;
- /* Set up select_end */
- if (table)
- {
- if (table->group && join->tmp_table_param.sum_func_count)
- {
- if (table->keys)
- {
- DBUG_PRINT("info",("Using end_update"));
- end_select=end_update;
- if (!table->file->inited)
- table->file->ha_index_init(0);
- }
- else
- {
- DBUG_PRINT("info",("Using end_unique_update"));
- end_select=end_unique_update;
- }
- }
- else if (join->sort_and_group)
- {
- DBUG_PRINT("info",("Using end_write_group"));
- end_select=end_write_group;
- }
- else
- {
- DBUG_PRINT("info",("Using end_write"));
- end_select=end_write;
- }
- }
- else
- {
- if (join->sort_and_group || (join->procedure &&
- join->procedure->flags & PROC_GROUP))
- end_select=end_send_group;
- else
- end_select=end_send;
- }
- join->join_tab[join->tables-1].next_select=end_select;
- join_tab=join->join_tab+join->const_tables;
- join->send_records=0;
- if (join->tables == join->const_tables)
- {
- /*
- HAVING will be chcked after processing aggregate functions,
- But WHERE should checkd here (we alredy have read tables)
- */
- if (!join->conds || join->conds->val_int())
- {
- if (!(error=(*end_select)(join,join_tab,0)) || error == -3)
- error=(*end_select)(join,join_tab,1);
- }
- else if (join->send_row_on_empty_set())
- error= join->result->send_data(*columns_list);
- }
- else
- {
- error= sub_select(join,join_tab,0);
- if (error >= 0)
- error= sub_select(join,join_tab,1);
- if (error == -3)
- error= 0; /* select_limit used */
- }
- if (error >= 0)
- {
- error=0;
- if (!table) // If sending data to client
- {
- /*
- The following will unlock all cursors if the command wasn't an
- update command
- */
- join->join_free(0); // Unlock all cursors
- if (join->result->send_eof())
- error= 1; // Don't send error
- }
- DBUG_PRINT("info",("%ld records output",join->send_records));
- }
- if (table)
- {
- int tmp;
- if ((tmp=table->file->extra(HA_EXTRA_NO_CACHE)))
- {
- DBUG_PRINT("error",("extra(HA_EXTRA_NO_CACHE) failed"));
- my_errno= tmp;
- error= -1;
- }
- if ((tmp=table->file->ha_index_or_rnd_end()))
- {
- DBUG_PRINT("error",("ha_index_or_rnd_end() failed"));
- my_errno= tmp;
- error= -1;
- }
- if (error == -1)
- table->file->print_error(my_errno,MYF(0));
- }
- #ifndef DBUG_OFF
- if (error)
- {
- DBUG_PRINT("error",("Error: do_select() failed"));
- }
- #endif
- DBUG_RETURN(join->thd->net.report_error ? -1 : error);
- }
- static int
- sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
- {
- int error;
- if (end_of_records)
- {
- if ((error=flush_cached_records(join,join_tab,FALSE)) < 0)
- return error; /* purecov: inspected */
- return sub_select(join,join_tab,end_of_records);
- }
- if (join->thd->killed) // If aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- return -2; /* purecov: inspected */
- }
- if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
- {
- if (!store_record_in_cache(&join_tab->cache))
- return 0; // There is more room in cache
- return flush_cached_records(join,join_tab,FALSE);
- }
- if ((error=flush_cached_records(join,join_tab,TRUE)) < 0)
- return error; /* purecov: inspected */
- return sub_select(join,join_tab,end_of_records); /* Use ordinary select */
- }
- static int
- sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
- {
- join_tab->table->null_row=0;
- if (end_of_records)
- return (*join_tab->next_select)(join,join_tab+1,end_of_records);
- /* Cache variables for faster loop */
- int error;
- bool found=0;
- COND *on_expr=join_tab->on_expr, *select_cond=join_tab->select_cond;
- my_bool *report_error= &(join->thd->net.report_error);
- if (!(error=(*join_tab->read_first_record)(join_tab)))
- {
- bool not_exists_optimize= join_tab->table->reginfo.not_exists_optimize;
- bool not_used_in_distinct=join_tab->not_used_in_distinct;
- ha_rows found_records=join->found_records;
- READ_RECORD *info= &join_tab->read_record;
- join->thd->row_count= 0;
- do
- {
- if (join->thd->killed) // Aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- return -2; /* purecov: inspected */
- }
- join->examined_rows++;
- join->thd->row_count++;
- if (!on_expr || on_expr->val_int())
- {
- found=1;
- if (not_exists_optimize)
- break; // Searching after not null columns
- if (!select_cond || select_cond->val_int())
- {
- if ((error=(*join_tab->next_select)(join,join_tab+1,0)) < 0)
- return error;
- /*
- Test if this was a SELECT DISTINCT query on a table that
- was not in the field list; In this case we can abort if
- we found a row, as no new rows can be added to the result.
- */
- if (not_used_in_distinct && found_records != join->found_records)
- return 0;
- }
- else
- {
- /*
- This row failed selection, release lock on it.
- XXX: There is no table handler in MySQL which makes use of this
- call. It's kept from Gemini times. A lot of new code was added
- recently (i. e. subselects) without having it in mind.
- */
- info->file->unlock_row();
- }
- }
- } while (!(error=info->read_record(info)) && !(*report_error));
- }
- if (error > 0 || (*report_error)) // Fatal error
- return -1;
- if (!found && on_expr)
- { // OUTER JOIN
- restore_record(join_tab->table,default_values); // Make empty record
- mark_as_null_row(join_tab->table); // For group by without error
- if (!select_cond || select_cond->val_int())
- {
- if ((error=(*join_tab->next_select)(join,join_tab+1,0)) < 0)
- return error; /* purecov: inspected */
- }
- }
- return 0;
- }
- static int
- flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
- {
- int error;
- READ_RECORD *info;
- if (!join_tab->cache.records)
- return 0; /* Nothing to do */
- if (skip_last)
- (void) store_record_in_cache(&join_tab->cache); // Must save this for later
- if (join_tab->use_quick == 2)
- {
- if (join_tab->select->quick)
- { /* Used quick select last. reset it */
- delete join_tab->select->quick;
- join_tab->select->quick=0;
- }
- }
- /* read through all records */
- if ((error=join_init_read_record(join_tab)))
- {
- reset_cache_write(&join_tab->cache);
- return -error; /* No records or error */
- }
- for (JOIN_TAB *tmp=join->join_tab; tmp != join_tab ; tmp++)
- {
- tmp->status=tmp->table->status;
- tmp->table->status=0;
- }
- info= &join_tab->read_record;
- do
- {
- if (join->thd->killed)
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- return -2; // Aborted by user /* purecov: inspected */
- }
- SQL_SELECT *select=join_tab->select;
- if (!error && (!join_tab->cache.select ||
- !join_tab->cache.select->skip_record()))
- {
- uint i;
- reset_cache_read(&join_tab->cache);
- for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;)
- {
- read_cached_record(join_tab);
- if (!select || !select->skip_record())
- if ((error=(join_tab->next_select)(join,join_tab+1,0)) < 0)
- {
- reset_cache_write(&join_tab->cache);
- return error; /* purecov: inspected */
- }
- }
- }
- } while (!(error=info->read_record(info)));
- if (skip_last)
- read_cached_record(join_tab); // Restore current record
- reset_cache_write(&join_tab->cache);
- if (error > 0) // Fatal error
- return -1; /* purecov: inspected */
- for (JOIN_TAB *tmp2=join->join_tab; tmp2 != join_tab ; tmp2++)
- tmp2->table->status=tmp2->status;
- return 0;
- }
- /*****************************************************************************
- The different ways to read a record
- Returns -1 if row was not found, 0 if row was found and 1 on errors
- *****************************************************************************/
- /* Help function when we get some an error from the table handler */
- 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)
- sql_print_error("Got error %d when reading table '%s'",
- error, table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- int safe_index_read(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if ((error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length, HA_READ_KEY_EXACT)))
- return report_error(table, error);
- return 0;
- }
- static int
- join_read_const_table(JOIN_TAB *tab, POSITION *pos)
- {
- int error;
- DBUG_ENTER("join_read_const_table");
- TABLE *table=tab->table;
- table->const_table=1;
- table->null_row=0;
- table->status=STATUS_NO_RECORD;
-
- if (tab->type == JT_SYSTEM)
- {
- if ((error=join_read_system(tab)))
- { // Info for DESCRIBE
- tab->info="const row not found";
- /* Mark for EXPLAIN that the row was not found */
- pos->records_read=0.0;
- if (!table->outer_join || error > 0)
- DBUG_RETURN(error);
- }
- }
- else
- {
- if (!table->key_read && table->used_keys.is_set(tab->ref.key) &&
- !table->no_keyread &&
- (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- tab->index= tab->ref.key;
- }
- if ((error=join_read_const(tab)))
- {
- tab->info="unique row not found";
- /* Mark for EXPLAIN that the row was not found */
- pos->records_read=0.0;
- if (!table->outer_join || error > 0)
- DBUG_RETURN(error);
- }
- if (table->key_read)
- {
- table->key_read=0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
- }
- if (tab->on_expr && !table->null_row)
- {
- if ((table->null_row= test(tab->on_expr->val_int() == 0)))
- mark_as_null_row(table);
- }
- if (!table->null_row)
- table->maybe_null=0;
- DBUG_RETURN(0);
- }
- static int
- join_read_system(JOIN_TAB *tab)
- {
- TABLE *table= tab->table;
- int error;
- if (table->status & STATUS_GARBAGE) // If first read
- {
- if ((error=table->file->read_first_row(table->record[0],
- table->primary_key)))
- {
- if (error != HA_ERR_END_OF_FILE)
- return report_error(table, error);
- mark_as_null_row(tab->table);
- empty_record(table); // Make empty record
- return -1;
- }
- store_record(table,record[1]);
- }
- else if (!table->status) // Only happens with left join
- restore_record(table,record[1]); // restore old record
- table->null_row=0;
- return table->status ? -1 : 0;
- }
- static int
- join_read_const(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if (table->status & STATUS_GARBAGE) // If first read
- {
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
- error=HA_ERR_KEY_NOT_FOUND;
- else
- {
- error=table->file->index_read_idx(table->record[0],tab->ref.key,
- (byte*) tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
- }
- if (error)
- {
- mark_as_null_row(tab->table);
- empty_record(table);
- if (error != HA_ERR_KEY_NOT_FOUND)
- return report_error(table, error);
- return -1;
- }
- store_record(table,record[1]);
- }
- else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join
- {
- table->status=0;
- restore_record(table,record[1]); // restore old record
- }
- table->null_row=0;
- return table->status ? -1 : 0;
- }
- static int
- join_read_key(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- if (cmp_buffer_with_ref(tab) ||
- (table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
- {
- if (tab->ref.key_err)
- {
- table->status=STATUS_NOT_FOUND;
- return -1;
- }
- error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
- if (error && error != HA_ERR_KEY_NOT_FOUND)
- return report_error(table, error);
- }
- table->null_row=0;
- return table->status ? -1 : 0;
- }
- static int
- join_read_always_key(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- for (uint i= 0 ; i < tab->ref.key_parts ; i++)
- {
- if ((tab->ref.null_rejecting & 1 << i) && tab->ref.items[i]->is_null())
- return -1;
- }
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
- return -1;
- if ((error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT)))
- {
- if (error != HA_ERR_KEY_NOT_FOUND)
- return report_error(table, error);
- return -1; /* purecov: inspected */
- }
- return 0;
- }
- /*
- This function is used when optimizing away ORDER BY in
- SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
- */
-
- static int
- join_read_last_key(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
- return -1;
- if ((error=table->file->index_read_last(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length)))
- {
- if (error != HA_ERR_KEY_NOT_FOUND)
- return report_error(table, error);
- return -1; /* purecov: inspected */
- }
- return 0;
- }
- /* ARGSUSED */
- static int
- join_no_more_records(READ_RECORD *info __attribute__((unused)))
- {
- return -1;
- }
- static int
- join_read_next_same(READ_RECORD *info)
- {
- int error;
- TABLE *table= info->table;
- JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error=table->file->index_next_same(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length)))
- {
- if (error != HA_ERR_END_OF_FILE)
- return report_error(table, error);
- table->status= STATUS_GARBAGE;
- return -1;
- }
- return 0;
- }
- static int
- join_read_prev_same(READ_RECORD *info)
- {
- int error;
- TABLE *table= info->table;
- JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error=table->file->index_prev(table->record[0])))
- return report_error(table, error);
- if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
- tab->ref.key_length))
- {
- table->status=STATUS_NOT_FOUND;
- error= -1;
- }
- return error;
- }
- static int
- join_init_quick_read_record(JOIN_TAB *tab)
- {
- if (test_if_quick_select(tab) == -1)
- return -1; /* No possible records */
- return join_init_read_record(tab);
- }
- static int
- test_if_quick_select(JOIN_TAB *tab)
- {
- delete tab->select->quick;
- tab->select->quick=0;
- return tab->select->test_quick_select(tab->join->thd, tab->keys,
- (table_map) 0, HA_POS_ERROR, 0);
- }
- static int
- join_init_read_record(JOIN_TAB *tab)
- {
- if (tab->select && tab->select->quick)
- tab->select->quick->reset();
- init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select,1,1);
- return (*tab->read_record.read_record)(&tab->read_record);
- }
- static int
- join_read_first(JOIN_TAB *tab)
- {
- int error;
- TABLE *table=tab->table;
- if (!table->key_read && table->used_keys.is_set(tab->index) &&
- !table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- tab->table->status=0;
- tab->read_record.read_record=join_read_next;
- tab->read_record.table=table;
- tab->read_record.file=table->file;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
- if (!table->file->inited)
- table->file->ha_index_init(tab->index);
- if ((error=tab->table->file->index_first(tab->table->record[0])))
- {
- if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
- report_error(table, error);
- return -1;
- }
- return 0;
- }
- static int
- join_read_next(READ_RECORD *info)
- {
- int error;
- if ((error=info->file->index_next(info->record)))
- return report_error(info->table, error);
- return 0;
- }
- static int
- join_read_last(JOIN_TAB *tab)
- {
- TABLE *table=tab->table;
- int error;
- if (!table->key_read && table->used_keys.is_set(tab->index) &&
- !table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- tab->table->status=0;
- tab->read_record.read_record=join_read_prev;
- tab->read_record.table=table;
- tab->read_record.file=table->file;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
- if (!table->file->inited)
- table->file->ha_index_init(tab->index);
- if ((error= tab->table->file->index_last(tab->table->record[0])))
- return report_error(table, error);
- return 0;
- }
- static int
- join_read_prev(READ_RECORD *info)
- {
- int error;
- if ((error= info->file->index_prev(info->record)))
- return report_error(info->table, error);
- return 0;
- }
- static int
- join_ft_read_first(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- #if NOT_USED_YET
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) // as ft-key doesn't use store_key's
- return -1; // see also FT_SELECT::init()
- #endif
- table->file->ft_init();
- if ((error= table->file->ft_read(table->record[0])))
- return report_error(table, error);
- return 0;
- }
- static int
- join_ft_read_next(READ_RECORD *info)
- {
- int error;
- if ((error= info->file->ft_read(info->table->record[0])))
- return report_error(info->table, error);
- return 0;
- }
- /*
- Reading of key with key reference and one part that may be NULL
- */
- static int
- join_read_always_key_or_null(JOIN_TAB *tab)
- {
- int res;
- /* First read according to key which is NOT NULL */
- *tab->ref.null_ref_key= 0; // Clear null byte
- if ((res= join_read_always_key(tab)) >= 0)
- return res;
- /* Then read key with null value */
- *tab->ref.null_ref_key= 1; // Set null byte
- return safe_index_read(tab);
- }
- static int
- join_read_next_same_or_null(READ_RECORD *info)
- {
- int error;
- if ((error= join_read_next_same(info)) >= 0)
- return error;
- JOIN_TAB *tab= info->table->reginfo.join_tab;
- /* Test if we have already done a read after null key */
- if (*tab->ref.null_ref_key)
- return -1; // All keys read
- *tab->ref.null_ref_key= 1; // Set null byte
- return safe_index_read(tab); // then read null keys
- }
- /*****************************************************************************
- The different end of select functions
- These functions returns < 0 when end is reached, 0 on ok and > 0 if a
- fatal error (like table corruption) was detected
- *****************************************************************************/
- /* ARGSUSED */
- static int
- end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- DBUG_ENTER("end_send");
- if (!end_of_records)
- {
- int error;
- if (join->having && join->having->val_int() == 0)
- DBUG_RETURN(0); // Didn't match having
- error=0;
- if (join->procedure)
- error=join->procedure->send_row(join->procedure_fields_list);
- else if (join->do_send_rows)
- error=join->result->send_data(*join->fields);
- if (error)
- DBUG_RETURN(-1); /* purecov: inspected */
- if (++join->send_records >= join->unit->select_limit_cnt &&
- join->do_send_rows)
- {
- if (join->select_options & OPTION_FOUND_ROWS)
- {
- JOIN_TAB *jt=join->join_tab;
- if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group
- && !join->send_group_parts && !join->having && !jt->select_cond &&
- !(jt->select && jt->select->quick) &&
- !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
- (jt->ref.key < 0))
- {
- /* Join over all rows in table; Return number of found rows */
- TABLE *table=jt->table;
- join->select_options ^= OPTION_FOUND_ROWS;
- if (table->sort.record_pointers ||
- (table->sort.io_cache && my_b_inited(table->sort.io_cache)))
- {
- /* Using filesort */
- join->send_records= table->sort.found_records;
- }
- else
- {
- table->file->info(HA_STATUS_VARIABLE);
- join->send_records = table->file->records;
- }
- }
- else
- {
- join->do_send_rows= 0;
- if (join->unit->fake_select_lex)
- join->unit->fake_select_lex->select_limit= HA_POS_ERROR;
- DBUG_RETURN(0);
- }
- }
- DBUG_RETURN(-3); // Abort nicely
- }
- }
- else
- {
- if (join->procedure && join->procedure->end_of_records())
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
- /* ARGSUSED */
- static int
- end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- int idx= -1;
- DBUG_ENTER("end_send_group");
- if (!join->first_record || end_of_records ||
- (idx=test_if_group_changed(join->group_fields)) >= 0)
- {
- if (join->first_record || (end_of_records && !join->group))
- {
- if (join->procedure)
- join->procedure->end_group();
- if (idx < (int) join->send_group_parts)
- {
- int error=0;
- if (join->procedure)
- {
- if (join->having && join->having->val_int() == 0)
- error= -1; // Didn't satisfy having
- else
- {
- if (join->do_send_rows)
- error=join->procedure->send_row(*join->fields) ? 1 : 0;
- join->send_records++;
- }
- if (end_of_records && join->procedure->end_of_records())
- error= 1; // Fatal error
- }
- else
- {
- if (!join->first_record)
- {
- /* No matching rows for group function */
- join->clear();
- }
- if (join->having && join->having->val_int() == 0)
- error= -1; // Didn't satisfy having
- else
- {
- if (join->do_send_rows)
- error=join->result->send_data(*join->fields) ? 1 : 0;
- join->send_records++;
- }
- if (join->rollup.state != ROLLUP::STATE_NONE && error <= 0)
- {
- if (join->rollup_send_data((uint) (idx+1)))
- error= 1;
- }
- }
- if (error > 0)
- DBUG_RETURN(-1); /* purecov: inspected */
- if (end_of_records)
- DBUG_RETURN(0);
- if (join->send_records >= join->unit->select_limit_cnt &&
- join->do_send_rows)
- {
- if (!(join->select_options & OPTION_FOUND_ROWS))
- DBUG_RETURN(-3); // Abort nicely
- join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
- }
- }
- }
- else
- {
- if (end_of_records)
- DBUG_RETURN(0);
- join->first_record=1;
- VOID(test_if_group_changed(join->group_fields));
- }
- if (idx < (int) join->send_group_parts)
- {
- copy_fields(&join->tmp_table_param);
- if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1]))
- DBUG_RETURN(-1);
- if (join->procedure)
- join->procedure->add();
- DBUG_RETURN(0);
- }
- }
- if (update_sum_func(join->sum_funcs))
- DBUG_RETURN(-1);
- if (join->procedure)
- join->procedure->add();
- DBUG_RETURN(0);
- }
- /* ARGSUSED */
- static int
- end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- TABLE *table=join->tmp_table;
- int error;
- DBUG_ENTER("end_write");
- if (join->thd->killed) // Aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-2); /* purecov: inspected */
- }
- if (!end_of_records)
- {
- copy_fields(&join->tmp_table_param);
- copy_funcs(join->tmp_table_param.items_to_copy);
- #ifdef TO_BE_DELETED
- if (!table->uniques) // If not unique handling
- {
- /* Copy null values from group to row */
- ORDER *group;
- for (group=table->group ; group ; group=group->next)
- {
- Item *item= *group->item;