sql_select.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:288k
- if (item->maybe_null)
- {
- Field *field=item->get_tmp_table_field();
- field->ptr[-1]= (byte) (field->is_null() ? 1 : 0);
- }
- }
- }
- #endif
- if (!join->having || join->having->val_int())
- {
- join->found_records++;
- if ((error=table->file->write_row(table->record[0])))
- {
- if (error == HA_ERR_FOUND_DUPP_KEY ||
- error == HA_ERR_FOUND_DUPP_UNIQUE)
- goto end;
- if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
- error,1))
- DBUG_RETURN(-1); // Not a table_is_full error
- table->uniques=0; // To ensure rows are the same
- }
- if (++join->send_records >= join->tmp_table_param.end_write_records &&
- join->do_send_rows)
- {
- if (!(join->select_options & OPTION_FOUND_ROWS))
- DBUG_RETURN(-3);
- join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
- DBUG_RETURN(0);
- }
- }
- }
- end:
- DBUG_RETURN(0);
- }
- /* Group by searching after group record and updating it if possible */
- /* ARGSUSED */
- static int
- end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- TABLE *table=join->tmp_table;
- ORDER *group;
- int error;
- DBUG_ENTER("end_update");
- if (end_of_records)
- DBUG_RETURN(0);
- if (join->thd->killed) // Aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-2); /* purecov: inspected */
- }
- join->found_records++;
- copy_fields(&join->tmp_table_param); // Groups are copied twice.
- /* Make a key of group index */
- for (group=table->group ; group ; group=group->next)
- {
- Item *item= *group->item;
- item->save_org_in_field(group->field);
- #ifdef EMBEDDED_LIBRARY
- join->thd->net.last_errno= 0;
- #endif
- /* Store in the used key if the field was 0 */
- if (item->maybe_null)
- group->buff[-1]=item->null_value ? 1 : 0;
- }
- if (!table->file->index_read(table->record[1],
- join->tmp_table_param.group_buff,0,
- HA_READ_KEY_EXACT))
- { /* Update old record */
- restore_record(table,record[1]);
- update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
- }
- DBUG_RETURN(0);
- }
- /* The null bits are already set */
- KEY_PART_INFO *key_part;
- for (group=table->group,key_part=table->key_info[0].key_part;
- group ;
- group=group->next,key_part++)
- memcpy(table->record[0]+key_part->offset, group->buff, key_part->length);
- init_tmptable_sum_functions(join->sum_funcs);
- copy_funcs(join->tmp_table_param.items_to_copy);
- if ((error=table->file->write_row(table->record[0])))
- {
- if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
- error, 0))
- DBUG_RETURN(-1); // Not a table_is_full error
- /* Change method to update rows */
- table->file->ha_index_init(0);
- join->join_tab[join->tables-1].next_select=end_unique_update;
- }
- join->send_records++;
- DBUG_RETURN(0);
- }
- /* Like end_update, but this is done with unique constraints instead of keys */
- static int
- end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- TABLE *table=join->tmp_table;
- int error;
- DBUG_ENTER("end_unique_update");
- if (end_of_records)
- DBUG_RETURN(0);
- if (join->thd->killed) // Aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-2); /* purecov: inspected */
- }
- init_tmptable_sum_functions(join->sum_funcs);
- copy_fields(&join->tmp_table_param); // Groups are copied twice.
- copy_funcs(join->tmp_table_param.items_to_copy);
- if (!(error=table->file->write_row(table->record[0])))
- join->send_records++; // New group
- else
- {
- if ((int) table->file->get_dup_key(error) < 0)
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
- }
- if (table->file->rnd_pos(table->record[1],table->file->dupp_ref))
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
- }
- restore_record(table,record[1]);
- update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
- {
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
- }
- }
- DBUG_RETURN(0);
- }
- /* ARGSUSED */
- static int
- end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
- bool end_of_records)
- {
- TABLE *table=join->tmp_table;
- int error;
- int idx= -1;
- DBUG_ENTER("end_write_group");
- if (join->thd->killed)
- { // Aborted by user
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-2); /* purecov: inspected */
- }
- 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();
- int send_group_parts= join->send_group_parts;
- if (idx < send_group_parts)
- {
- if (!join->first_record)
- {
- /* No matching rows for group function */
- join->clear();
- }
- copy_sum_funcs(join->sum_funcs,
- join->sum_funcs_end[send_group_parts]);
- if (join->having && join->having->val_int() == 0)
- error= -1;
- else if ((error=table->file->write_row(table->record[0])))
- {
- if (create_myisam_from_heap(join->thd, table,
- &join->tmp_table_param,
- error, 0))
- DBUG_RETURN(-1);
- /*
- If table->file->write_row() was failed because of 'out of memory'
- and tmp table succesfully created, reset error.
- */
- error=0;
- }
- if (join->rollup.state != ROLLUP::STATE_NONE && error <= 0)
- {
- if (join->rollup_write_data((uint) (idx+1), table))
- error= 1;
- }
- if (error > 0)
- DBUG_RETURN(-1);
- if (end_of_records)
- DBUG_RETURN(0);
- }
- }
- 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);
- copy_funcs(join->tmp_table_param.items_to_copy);
- 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);
- }
- /*****************************************************************************
- Remove calculation with tables that aren't yet read. Remove also tests
- against fields that are read through key where the table is not a
- outer join table.
- We can't remove tests that are made against columns which are stored
- in sorted order.
- *****************************************************************************/
- /* Return 1 if right_item is used removable reference key on left_item */
- static bool test_if_ref(Item_field *left_item,Item *right_item)
- {
- Field *field=left_item->field;
- // No need to change const test. We also have to keep tests on LEFT JOIN
- if (!field->table->const_table && !field->table->maybe_null)
- {
- Item *ref_item=part_of_refkey(field->table,field);
- if (ref_item && ref_item->eq(right_item,1))
- {
- if (right_item->type() == Item::FIELD_ITEM)
- return (field->eq_def(((Item_field *) right_item)->field));
- if (right_item->const_item() && !(right_item->is_null()))
- {
- /*
- We can remove binary fields and numerical fields except float,
- as float comparison isn't 100 % secure
- We have to keep binary strings to be able to check for end spaces
- */
- if (field->binary() &&
- field->real_type() != FIELD_TYPE_STRING &&
- field->real_type() != FIELD_TYPE_VAR_STRING &&
- (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0))
- {
- return !store_val_in_field(field,right_item);
- }
- }
- }
- }
- return 0; // keep test
- }
- static COND *
- make_cond_for_table(COND *cond, table_map tables, table_map used_table)
- {
- if (used_table && !(cond->used_tables() & used_table))
- return (COND*) 0; // Already checked
- if (cond->type() == Item::COND_ITEM)
- {
- if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
- {
- /* Create new top level AND item */
- Item_cond_and *new_cond=new Item_cond_and;
- if (!new_cond)
- return (COND*) 0; // OOM /* purecov: inspected */
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- while ((item=li++))
- {
- Item *fix=make_cond_for_table(item,tables,used_table);
- if (fix)
- new_cond->argument_list()->push_back(fix);
- }
- switch (new_cond->argument_list()->elements) {
- case 0:
- return (COND*) 0; // Always true
- case 1:
- return new_cond->argument_list()->head();
- default:
- /*
- Item_cond_and do not need fix_fields for execution, its parameters
- are fixed or do not need fix_fields, too
- */
- new_cond->quick_fix_field();
- new_cond->used_tables_cache=
- ((Item_cond_and*) cond)->used_tables_cache &
- tables;
- return new_cond;
- }
- }
- else
- { // Or list
- Item_cond_or *new_cond=new Item_cond_or;
- if (!new_cond)
- return (COND*) 0; // OOM /* purecov: inspected */
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- while ((item=li++))
- {
- Item *fix=make_cond_for_table(item,tables,0L);
- if (!fix)
- return (COND*) 0; // Always true
- new_cond->argument_list()->push_back(fix);
- }
- /*
- Item_cond_and do not need fix_fields for execution, its parameters
- are fixed or do not need fix_fields, too
- */
- new_cond->quick_fix_field();
- new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache;
- new_cond->top_level_item();
- return new_cond;
- }
- }
- /*
- Because the following test takes a while and it can be done
- table_count times, we mark each item that we have examined with the result
- of the test
- */
- if (cond->marker == 3 || (cond->used_tables() & ~tables))
- return (COND*) 0; // Can't check this yet
- if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
- return cond; // Not boolean op
- if (((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
- {
- Item *left_item= ((Item_func*) cond)->arguments()[0];
- Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->type() == Item::FIELD_ITEM &&
- test_if_ref((Item_field*) left_item,right_item))
- {
- cond->marker=3; // Checked when read
- return (COND*) 0;
- }
- if (right_item->type() == Item::FIELD_ITEM &&
- test_if_ref((Item_field*) right_item,left_item))
- {
- cond->marker=3; // Checked when read
- return (COND*) 0;
- }
- }
- cond->marker=2;
- return cond;
- }
- static Item *
- part_of_refkey(TABLE *table,Field *field)
- {
- if (!table->reginfo.join_tab)
- return (Item*) 0; // field from outer non-select (UPDATE,...)
- uint ref_parts=table->reginfo.join_tab->ref.key_parts;
- if (ref_parts)
- {
- KEY_PART_INFO *key_part=
- table->key_info[table->reginfo.join_tab->ref.key].key_part;
- for (uint part=0 ; part < ref_parts ; part++,key_part++)
- if (field->eq(key_part->field) &&
- !(key_part->key_part_flag & HA_PART_KEY_SEG))
- return table->reginfo.join_tab->ref.items[part];
- }
- return (Item*) 0;
- }
- /*****************************************************************************
- Test if one can use the key to resolve ORDER BY
- SYNOPSIS
- test_if_order_by_key()
- order Sort order
- table Table to sort
- idx Index to check
- used_key_parts Return value for used key parts.
- NOTES
- used_key_parts is set to correct key parts used if return value != 0
- (On other cases, used_key_part may be changed)
- RETURN
- 1 key is ok.
- 0 Key can't be used
- -1 Reverse key can be used
- *****************************************************************************/
- static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts)
- {
- KEY_PART_INFO *key_part,*key_part_end;
- key_part=table->key_info[idx].key_part;
- key_part_end=key_part+table->key_info[idx].key_parts;
- key_part_map const_key_parts=table->const_key_parts[idx];
- int reverse=0;
- DBUG_ENTER("test_if_order_by_key");
- for (; order ; order=order->next, const_key_parts>>=1)
- {
- Field *field=((Item_field*) (*order->item))->field;
- int flag;
- /*
- Skip key parts that are constants in the WHERE clause.
- These are already skipped in the ORDER BY by const_expression_in_where()
- */
- for (; const_key_parts & 1 ; const_key_parts>>= 1)
- key_part++;
- if (key_part == key_part_end || key_part->field != field)
- DBUG_RETURN(0);
- /* set flag to 1 if we can use read-next on key, else to -1 */
- flag= ((order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT)) ?
- 1 : -1);
- if (reverse && flag != reverse)
- DBUG_RETURN(0);
- reverse=flag; // Remember if reverse
- key_part++;
- }
- *used_key_parts= (uint) (key_part - table->key_info[idx].key_part);
- if (reverse == -1 && !(table->file->index_flags(idx, *used_key_parts-1, 1) &
- HA_READ_PREV))
- reverse= 0; // Index can't be used
- DBUG_RETURN(reverse);
- }
- static uint find_shortest_key(TABLE *table, const key_map *usable_keys)
- {
- uint min_length= (uint) ~0;
- uint best= MAX_KEY;
- if (!usable_keys->is_clear_all())
- {
- for (uint nr=0; nr < table->keys ; nr++)
- {
- if (usable_keys->is_set(nr))
- {
- if (table->key_info[nr].key_length < min_length)
- {
- min_length=table->key_info[nr].key_length;
- best=nr;
- }
- }
- }
- }
- return best;
- }
- /*
- Test if a second key is the subkey of the first one.
- SYNOPSIS
- is_subkey()
- key_part First key parts
- ref_key_part Second key parts
- ref_key_part_end Last+1 part of the second key
- NOTE
- Second key MUST be shorter than the first one.
- RETURN
- 1 is a subkey
- 0 no sub key
- */
- inline bool
- is_subkey(KEY_PART_INFO *key_part, KEY_PART_INFO *ref_key_part,
- KEY_PART_INFO *ref_key_part_end)
- {
- for (; ref_key_part < ref_key_part_end; key_part++, ref_key_part++)
- if (!key_part->field->eq(ref_key_part->field))
- return 0;
- return 1;
- }
- /*
- Test if we can use one of the 'usable_keys' instead of 'ref' key for sorting
- SYNOPSIS
- test_if_subkey()
- ref Number of key, used for WHERE clause
- usable_keys Keys for testing
- RETURN
- MAX_KEY If we can't use other key
- the number of found key Otherwise
- */
- static uint
- test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
- const key_map *usable_keys)
- {
- uint nr;
- uint min_length= (uint) ~0;
- uint best= MAX_KEY;
- uint not_used;
- KEY_PART_INFO *ref_key_part= table->key_info[ref].key_part;
- KEY_PART_INFO *ref_key_part_end= ref_key_part + ref_key_parts;
- for (nr= 0 ; nr < table->keys ; nr++)
- {
- if (usable_keys->is_set(nr) &&
- table->key_info[nr].key_length < min_length &&
- table->key_info[nr].key_parts >= ref_key_parts &&
- is_subkey(table->key_info[nr].key_part, ref_key_part,
- ref_key_part_end) &&
- test_if_order_by_key(order, table, nr, ¬_used))
- {
- min_length= table->key_info[nr].key_length;
- best= nr;
- }
- }
- return best;
- }
- /*
- Test if we can skip the ORDER BY by using an index.
- If we can use an index, the JOIN_TAB / tab->select struct
- is changed to use the index.
- Return:
- 0 We have to use filesort to do the sorting
- 1 We can use an index.
- */
- static bool
- test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
- bool no_changes)
- {
- int ref_key;
- uint ref_key_parts;
- TABLE *table=tab->table;
- SQL_SELECT *select=tab->select;
- key_map usable_keys;
- DBUG_ENTER("test_if_skip_sort_order");
- LINT_INIT(ref_key_parts);
- /*
- Check which keys can be used to resolve ORDER BY.
- We must not try to use disabled keys.
- */
- usable_keys= table->keys_in_use;
- for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next)
- {
- if ((*tmp_order->item)->type() != Item::FIELD_ITEM)
- {
- usable_keys.clear_all();
- DBUG_RETURN(0);
- }
- usable_keys.intersect(((Item_field*) (*tmp_order->item))->
- field->part_of_sortkey);
- if (usable_keys.is_clear_all())
- DBUG_RETURN(0); // No usable keys
- }
- ref_key= -1;
- /* Test if constant range in WHERE */
- if (tab->ref.key >= 0 && tab->ref.key_parts)
- {
- ref_key= tab->ref.key;
- ref_key_parts= tab->ref.key_parts;
- if (tab->type == JT_REF_OR_NULL || tab->type == JT_FT)
- DBUG_RETURN(0);
- }
- else if (select && select->quick) // Range found by opt_range
- {
- ref_key= select->quick->index;
- ref_key_parts= select->quick->used_key_parts;
- }
- if (ref_key >= 0)
- {
- /*
- We come here when there is a REF key.
- */
- int order_direction;
- uint used_key_parts;
- if (!usable_keys.is_set(ref_key))
- {
- /*
- We come here when ref_key is not among usable_keys
- */
- uint new_ref_key;
- /*
- If using index only read, only consider other possible index only
- keys
- */
- if (table->used_keys.is_set(ref_key))
- usable_keys.intersect(table->used_keys);
- if ((new_ref_key= test_if_subkey(order, table, ref_key, ref_key_parts,
- &usable_keys)) < MAX_KEY)
- {
- /* Found key that can be used to retrieve data in sorted order */
- if (tab->ref.key >= 0)
- {
- /*
- We'll use ref access method on key new_ref_key. In general case
- the index search tuple for new_ref_key will be different (e.g.
- when one index is defined as (part1, part2, ...) and another as
- (part1, part2(N), ...) and the WHERE clause contains
- "part1 = const1 AND part2=const2".
- So we build tab->ref from scratch here.
- */
- KEYUSE *keyuse= tab->keyuse;
- while (keyuse->key != new_ref_key && keyuse->table == tab->table)
- keyuse++;
- if (create_ref_for_key(tab->join, tab, keyuse,
- tab->join->const_table_map))
- DBUG_RETURN(0);
- }
- else
- {
- /*
- The range optimizer constructed QUICK_RANGE for ref_key, and
- we want to use instead new_ref_key as the index. We can't
- just change the index of the quick select, because this may
- result in an incosistent QUICK_SELECT object. Below we
- create a new QUICK_SELECT from scratch so that all its
- parameres are set correctly by the range optimizer.
- */
- key_map new_ref_key_map;
- new_ref_key_map.clear_all(); // Force the creation of quick select
- new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key.
- if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
- (tab->join->select_options &
- OPTION_FOUND_ROWS) ?
- HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,0) <=
- 0)
- DBUG_RETURN(0);
- }
- ref_key= new_ref_key;
- }
- }
- /* Check if we get the rows in requested sorted order by using the key */
- if (usable_keys.is_set(ref_key) &&
- (order_direction = test_if_order_by_key(order,table,ref_key,
- &used_key_parts)))
- {
- if (order_direction == -1) // If ORDER BY ... DESC
- {
- if (select && select->quick)
- {
- /*
- Don't reverse the sort order, if it's already done.
- (In some cases test_if_order_by_key() can be called multiple times
- */
- if (!select->quick->reverse_sorted())
- {
- // ORDER BY range_key DESC
- QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick,
- used_key_parts);
- if (!tmp || tmp->error)
- {
- delete tmp;
- DBUG_RETURN(0); // Reverse sort not supported
- }
- select->quick=tmp;
- }
- DBUG_RETURN(1);
- }
- if (tab->ref.key_parts < used_key_parts)
- {
- /*
- SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
- Use a traversal function that starts by reading the last row
- with key part (A) and then traverse the index backwards.
- */
- tab->read_first_record= join_read_last_key;
- tab->read_record.read_record= join_read_prev_same;
- /* fall through */
- }
- }
- else if (select && select->quick)
- select->quick->sorted= 1;
- DBUG_RETURN(1); /* No need to sort */
- }
- }
- else
- {
- /* check if we can use a key to resolve the group */
- /* Tables using JT_NEXT are handled here */
- uint nr;
- key_map keys;
- /*
- If not used with LIMIT, only use keys if the whole query can be
- resolved with a key; This is because filesort() is usually faster than
- retrieving all rows through an index.
- */
- if (select_limit >= table->file->records)
- {
- keys= *table->file->keys_to_use_for_scanning();
- keys.merge(table->used_keys);
- /*
- We are adding here also the index specified in FORCE INDEX clause,
- if any.
- This is to allow users to use index in ORDER BY.
- */
- if (table->force_index)
- keys.merge(table->keys_in_use_for_query);
- keys.intersect(usable_keys);
- }
- else
- keys= usable_keys;
- for (nr=0; nr < table->keys ; nr++)
- {
- uint not_used;
- if (keys.is_set(nr))
- {
- int flag;
- if ((flag= test_if_order_by_key(order, table, nr, ¬_used)))
- {
- if (!no_changes)
- {
- tab->index=nr;
- tab->read_first_record= (flag > 0 ? join_read_first:
- join_read_last);
- tab->type=JT_NEXT; // Read with index_first(), index_next()
- if (table->used_keys.is_set(nr))
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- }
- DBUG_RETURN(1);
- }
- }
- }
- }
- DBUG_RETURN(0); // Can't use index.
- }
- /*
- If not selecting by given key, create an index how records should be read
- SYNOPSIS
- create_sort_index()
- thd Thread handler
- tab Table to sort (in join structure)
- order How table should be sorted
- filesort_limit Max number of rows that needs to be sorted
- select_limit Max number of rows in final output
- Used to decide if we should use index or not
- IMPLEMENTATION
- - If there is an index that can be used, 'tab' is modified to use
- this index.
- - If no index, create with filesort() an index file that can be used to
- retrieve rows in order (should be done with 'read_record').
- The sorted data is stored in tab->table and will be freed when calling
- free_io_cache(tab->table).
- RETURN VALUES
- 0 ok
- -1 Some fatal error
- 1 No records
- */
- static int
- create_sort_index(THD *thd, JOIN *join, ORDER *order,
- ha_rows filesort_limit, ha_rows select_limit)
- {
- SORT_FIELD *sortorder;
- uint length;
- ha_rows examined_rows;
- TABLE *table;
- SQL_SELECT *select;
- JOIN_TAB *tab;
- DBUG_ENTER("create_sort_index");
- if (join->tables == join->const_tables)
- DBUG_RETURN(0); // One row, no need to sort
- tab= join->join_tab + join->const_tables;
- table= tab->table;
- select= tab->select;
- if (test_if_skip_sort_order(tab,order,select_limit,0))
- DBUG_RETURN(0);
- if (!(sortorder=make_unireg_sortorder(order,&length)))
- goto err; /* purecov: inspected */
- /* It's not fatal if the following alloc fails */
- table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_WME | MY_ZEROFILL));
- table->status=0; // May be wrong if quick_select
- // If table has a range, move it to select
- if (select && !select->quick && tab->ref.key >= 0)
- {
- if (tab->quick)
- {
- select->quick=tab->quick;
- tab->quick=0;
- /* We can only use 'Only index' if quick key is same as ref_key */
- if (table->key_read && (uint) tab->ref.key != select->quick->index)
- {
- table->key_read=0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
- }
- else
- {
- /*
- We have a ref on a const; Change this to a range that filesort
- can use.
- For impossible ranges (like when doing a lookup on NULL on a NOT NULL
- field, quick will contain an empty record set.
- */
- if (!(select->quick= (tab->type == JT_FT ?
- new FT_SELECT(thd, table, tab->ref.key) :
- get_quick_select_for_ref(thd, table, &tab->ref))))
- goto err;
- }
- }
- if (table->tmp_table)
- table->file->info(HA_STATUS_VARIABLE); // Get record count
- table->sort.found_records=filesort(thd, table,sortorder, length,
- select, filesort_limit, &examined_rows);
- tab->records= table->sort.found_records; // For SQL_CALC_ROWS
- if (select)
- {
- select->cleanup(); // filesort did select
- tab->select= 0;
- }
- tab->select_cond=0;
- tab->type=JT_ALL; // Read with normal read_record
- tab->read_first_record= join_init_read_record;
- tab->join->examined_rows+=examined_rows;
- if (table->key_read) // Restore if we used indexes
- {
- table->key_read=0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
- DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
- err:
- DBUG_RETURN(-1);
- }
- /*
- Add the HAVING criteria to table->select
- */
- #ifdef NOT_YET
- static bool fix_having(JOIN *join, Item **having)
- {
- (*having)->update_used_tables(); // Some tables may have been const
- JOIN_TAB *table=&join->join_tab[join->const_tables];
- table_map used_tables= join->const_table_map | table->table->map;
- DBUG_EXECUTE("where",print_where(*having,"having"););
- Item* sort_table_cond=make_cond_for_table(*having,used_tables,used_tables);
- if (sort_table_cond)
- {
- if (!table->select)
- if (!(table->select=new SQL_SELECT))
- return 1;
- if (!table->select->cond)
- table->select->cond=sort_table_cond;
- else // This should never happen
- if (!(table->select->cond= new Item_cond_and(table->select->cond,
- sort_table_cond)) ||
- table->select->cond->fix_fields(join->thd, join->tables_list,
- &table->select->cond))
- return 1;
- table->select_cond=table->select->cond;
- table->select_cond->top_level_item();
- DBUG_EXECUTE("where",print_where(table->select_cond,
- "select and having"););
- *having=make_cond_for_table(*having,~ (table_map) 0,~used_tables);
- DBUG_EXECUTE("where",print_where(*having,"having after make_cond"););
- }
- return 0;
- }
- #endif
- /*****************************************************************************
- Remove duplicates from tmp table
- This should be recoded to add a unique index to the table and remove
- duplicates
- Table is a locked single thread table
- fields is the number of fields to check (from the end)
- *****************************************************************************/
- static bool compare_record(TABLE *table, Field **ptr)
- {
- for (; *ptr ; ptr++)
- {
- if ((*ptr)->cmp_offset(table->rec_buff_length))
- return 1;
- }
- return 0;
- }
- static bool copy_blobs(Field **ptr)
- {
- for (; *ptr ; ptr++)
- {
- if ((*ptr)->flags & BLOB_FLAG)
- if (((Field_blob *) (*ptr))->copy())
- return 1; // Error
- }
- return 0;
- }
- static void free_blobs(Field **ptr)
- {
- for (; *ptr ; ptr++)
- {
- if ((*ptr)->flags & BLOB_FLAG)
- ((Field_blob *) (*ptr))->free();
- }
- }
- static int
- remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
- {
- int error;
- ulong reclength,offset;
- uint field_count;
- THD *thd= join->thd;
- DBUG_ENTER("remove_duplicates");
- entry->reginfo.lock_type=TL_WRITE;
- /* Calculate how many saved fields there is in list */
- field_count=0;
- List_iterator<Item> it(fields);
- Item *item;
- while ((item=it++))
- {
- if (item->get_tmp_table_field() && ! item->const_item())
- field_count++;
- }
- if (!field_count && !(join->select_options & OPTION_FOUND_ROWS))
- { // only const items with no OPTION_FOUND_ROWS
- join->unit->select_limit_cnt= 1; // Only send first row
- DBUG_RETURN(0);
- }
- Field **first_field=entry->field+entry->fields - field_count;
- offset= field_count ?
- entry->field[entry->fields - field_count]->offset() : 0;
- reclength=entry->reclength-offset;
- free_io_cache(entry); // Safety
- entry->file->info(HA_STATUS_VARIABLE);
- if (entry->db_type == DB_TYPE_HEAP ||
- (!entry->blob_fields &&
- ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->records <
- thd->variables.sortbuff_size)))
- error=remove_dup_with_hash_index(join->thd, entry,
- field_count, first_field,
- reclength, having);
- else
- error=remove_dup_with_compare(join->thd, entry, first_field, offset,
- having);
- free_blobs(first_field);
- DBUG_RETURN(error);
- }
- static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
- ulong offset, Item *having)
- {
- handler *file=table->file;
- char *org_record,*new_record;
- byte *record;
- int error;
- ulong reclength=table->reclength-offset;
- DBUG_ENTER("remove_dup_with_compare");
- org_record=(char*) (record=table->record[0])+offset;
- new_record=(char*) table->record[1]+offset;
- file->ha_rnd_init(1);
- error=file->rnd_next(record);
- for (;;)
- {
- if (thd->killed)
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
- error=0;
- goto err;
- }
- if (error)
- {
- if (error == HA_ERR_RECORD_DELETED)
- continue;
- if (error == HA_ERR_END_OF_FILE)
- break;
- goto err;
- }
- if (having && !having->val_int())
- {
- if ((error=file->delete_row(record)))
- goto err;
- error=file->rnd_next(record);
- continue;
- }
- if (copy_blobs(first_field))
- {
- my_error(ER_OUTOFMEMORY,MYF(0));
- error=0;
- goto err;
- }
- memcpy(new_record,org_record,reclength);
- /* Read through rest of file and mark duplicated rows deleted */
- bool found=0;
- for (;;)
- {
- if ((error=file->rnd_next(record)))
- {
- if (error == HA_ERR_RECORD_DELETED)
- continue;
- if (error == HA_ERR_END_OF_FILE)
- break;
- goto err;
- }
- if (compare_record(table, first_field) == 0)
- {
- if ((error=file->delete_row(record)))
- goto err;
- }
- else if (!found)
- {
- found=1;
- file->position(record); // Remember position
- }
- }
- if (!found)
- break; // End of file
- /* Restart search on next row */
- error=file->restart_rnd_next(record,file->ref);
- }
- file->extra(HA_EXTRA_NO_CACHE);
- DBUG_RETURN(0);
- err:
- file->extra(HA_EXTRA_NO_CACHE);
- if (error)
- file->print_error(error,MYF(0));
- DBUG_RETURN(1);
- }
- /*
- Generate a hash index for each row to quickly find duplicate rows
- Note that this will not work on tables with blobs!
- */
- static int remove_dup_with_hash_index(THD *thd, TABLE *table,
- uint field_count,
- Field **first_field,
- ulong key_length,
- Item *having)
- {
- byte *key_buffer, *key_pos, *record=table->record[0];
- int error;
- handler *file= table->file;
- ulong extra_length= ALIGN_SIZE(key_length)-key_length;
- uint *field_lengths,*field_length;
- HASH hash;
- DBUG_ENTER("remove_dup_with_hash_index");
- if (!my_multi_malloc(MYF(MY_WME),
- &key_buffer,
- (uint) ((key_length + extra_length) *
- (long) file->records),
- &field_lengths,
- (uint) (field_count*sizeof(*field_lengths)),
- NullS))
- DBUG_RETURN(1);
- {
- Field **ptr;
- ulong total_length= 0;
- for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
- {
- uint length= (*ptr)->pack_length();
- (*field_length++)= length;
- total_length+= length;
- }
- DBUG_PRINT("info",("field_count: %u key_length: %lu total_length: %lu",
- field_count, key_length, total_length));
- DBUG_ASSERT(total_length <= key_length);
- key_length= total_length;
- extra_length= ALIGN_SIZE(key_length)-key_length;
- }
- if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0,
- key_length, (hash_get_key) 0, 0, 0))
- {
- my_free((char*) key_buffer,MYF(0));
- DBUG_RETURN(1);
- }
- file->ha_rnd_init(1);
- key_pos=key_buffer;
- for (;;)
- {
- byte *org_key_pos;
- if (thd->killed)
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
- error=0;
- goto err;
- }
- if ((error=file->rnd_next(record)))
- {
- if (error == HA_ERR_RECORD_DELETED)
- continue;
- if (error == HA_ERR_END_OF_FILE)
- break;
- goto err;
- }
- if (having && !having->val_int())
- {
- if ((error=file->delete_row(record)))
- goto err;
- continue;
- }
- /* copy fields to key buffer */
- org_key_pos= key_pos;
- field_length=field_lengths;
- for (Field **ptr= first_field ; *ptr ; ptr++)
- {
- (*ptr)->sort_string((char*) key_pos,*field_length);
- key_pos+= *field_length++;
- }
- /* Check if it exists before */
- if (hash_search(&hash, org_key_pos, key_length))
- {
- /* Duplicated found ; Remove the row */
- if ((error=file->delete_row(record)))
- goto err;
- }
- else
- (void) my_hash_insert(&hash, org_key_pos);
- key_pos+=extra_length;
- }
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
- file->extra(HA_EXTRA_NO_CACHE);
- (void) file->ha_rnd_end();
- DBUG_RETURN(0);
- err:
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
- file->extra(HA_EXTRA_NO_CACHE);
- (void) file->ha_rnd_end();
- if (error)
- file->print_error(error,MYF(0));
- DBUG_RETURN(1);
- }
- SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length)
- {
- uint count;
- SORT_FIELD *sort,*pos;
- DBUG_ENTER("make_unireg_sortorder");
- count=0;
- for (ORDER *tmp = order; tmp; tmp=tmp->next)
- count++;
- pos=sort=(SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1));
- if (!pos)
- return 0;
- for (;order;order=order->next,pos++)
- {
- pos->field=0; pos->item=0;
- if (order->item[0]->type() == Item::FIELD_ITEM)
- pos->field= ((Item_field*) (*order->item))->field;
- else if (order->item[0]->type() == Item::SUM_FUNC_ITEM &&
- !order->item[0]->const_item())
- pos->field= ((Item_sum*) order->item[0])->get_tmp_table_field();
- else if (order->item[0]->type() == Item::COPY_STR_ITEM)
- { // Blob patch
- pos->item= ((Item_copy_string*) (*order->item))->item;
- }
- else
- pos->item= *order->item;
- pos->reverse=! order->asc;
- }
- *length=count;
- DBUG_RETURN(sort);
- }
- /*****************************************************************************
- Fill join cache with packed records
- Records are stored in tab->cache.buffer and last record in
- last record is stored with pointers to blobs to support very big
- records
- ******************************************************************************/
- static int
- join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
- {
- reg1 uint i;
- uint length,blobs,size;
- CACHE_FIELD *copy,**blob_ptr;
- JOIN_CACHE *cache;
- JOIN_TAB *join_tab;
- DBUG_ENTER("join_init_cache");
- cache= &tables[table_count].cache;
- cache->fields=blobs=0;
- join_tab=tables;
- for (i=0 ; i < table_count ; i++,join_tab++)
- {
- if (!join_tab->used_fieldlength) /* Not calced yet */
- calc_used_field_length(thd, join_tab);
- cache->fields+=join_tab->used_fields;
- blobs+=join_tab->used_blobs;
- }
- if (!(cache->field=(CACHE_FIELD*)
- sql_alloc(sizeof(CACHE_FIELD)*(cache->fields+table_count*2)+(blobs+1)*
- sizeof(CACHE_FIELD*))))
- {
- my_free((gptr) cache->buff,MYF(0)); /* purecov: inspected */
- cache->buff=0; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- copy=cache->field;
- blob_ptr=cache->blob_ptr=(CACHE_FIELD**)
- (cache->field+cache->fields+table_count*2);
- length=0;
- for (i=0 ; i < table_count ; i++)
- {
- uint null_fields=0,used_fields;
- Field **f_ptr,*field;
- for (f_ptr=tables[i].table->field,used_fields=tables[i].used_fields ;
- used_fields ;
- f_ptr++)
- {
- field= *f_ptr;
- if (field->query_id == thd->query_id)
- {
- used_fields--;
- length+=field->fill_cache_field(copy);
- if (copy->blob_field)
- (*blob_ptr++)=copy;
- if (field->maybe_null())
- null_fields++;
- copy++;
- }
- }
- /* Copy null bits from table */
- if (null_fields && tables[i].table->null_fields)
- { /* must copy null bits */
- copy->str=(char*) tables[i].table->null_flags;
- copy->length=tables[i].table->null_bytes;
- copy->strip=0;
- copy->blob_field=0;
- length+=copy->length;
- copy++;
- cache->fields++;
- }
- /* If outer join table, copy null_row flag */
- if (tables[i].table->maybe_null)
- {
- copy->str= (char*) &tables[i].table->null_row;
- copy->length=sizeof(tables[i].table->null_row);
- copy->strip=0;
- copy->blob_field=0;
- length+=copy->length;
- copy++;
- cache->fields++;
- }
- }
- cache->length=length+blobs*sizeof(char*);
- cache->blobs=blobs;
- *blob_ptr=0; /* End sequentel */
- size=max(thd->variables.join_buff_size, cache->length);
- if (!(cache->buff=(uchar*) my_malloc(size,MYF(0))))
- DBUG_RETURN(1); /* Don't use cache */ /* purecov: inspected */
- cache->end=cache->buff+size;
- reset_cache_write(cache);
- DBUG_RETURN(0);
- }
- static ulong
- used_blob_length(CACHE_FIELD **ptr)
- {
- uint length,blob_length;
- for (length=0 ; *ptr ; ptr++)
- {
- (*ptr)->blob_length=blob_length=(*ptr)->blob_field->get_length();
- length+=blob_length;
- (*ptr)->blob_field->get_ptr(&(*ptr)->str);
- }
- return length;
- }
- static bool
- store_record_in_cache(JOIN_CACHE *cache)
- {
- uint length;
- uchar *pos;
- CACHE_FIELD *copy,*end_field;
- bool last_record;
- pos=cache->pos;
- end_field=cache->field+cache->fields;
- length=cache->length;
- if (cache->blobs)
- length+=used_blob_length(cache->blob_ptr);
- if ((last_record=(length+cache->length > (uint) (cache->end - pos))))
- cache->ptr_record=cache->records;
- /*
- There is room in cache. Put record there
- */
- cache->records++;
- for (copy=cache->field ; copy < end_field; copy++)
- {
- if (copy->blob_field)
- {
- if (last_record)
- {
- copy->blob_field->get_image((char*) pos,copy->length+sizeof(char*),
- copy->blob_field->charset());
- pos+=copy->length+sizeof(char*);
- }
- else
- {
- copy->blob_field->get_image((char*) pos,copy->length, // blob length
- copy->blob_field->charset());
- memcpy(pos+copy->length,copy->str,copy->blob_length); // Blob data
- pos+=copy->length+copy->blob_length;
- }
- }
- else
- {
- if (copy->strip)
- {
- char *str,*end;
- for (str=copy->str,end= str+copy->length;
- end > str && end[-1] == ' ' ;
- end--) ;
- length=(uint) (end-str);
- memcpy(pos+2, str, length);
- int2store(pos, length);
- pos+= length+2;
- }
- else
- {
- memcpy(pos,copy->str,copy->length);
- pos+=copy->length;
- }
- }
- }
- cache->pos=pos;
- return last_record || (uint) (cache->end -pos) < cache->length;
- }
- static void
- reset_cache_read(JOIN_CACHE *cache)
- {
- cache->record_nr=0;
- cache->pos=cache->buff;
- }
- static void reset_cache_write(JOIN_CACHE *cache)
- {
- reset_cache_read(cache);
- cache->records= 0;
- cache->ptr_record= (uint) ~0;
- }
- static void
- read_cached_record(JOIN_TAB *tab)
- {
- uchar *pos;
- uint length;
- bool last_record;
- CACHE_FIELD *copy,*end_field;
- last_record=tab->cache.record_nr++ == tab->cache.ptr_record;
- pos=tab->cache.pos;
- for (copy=tab->cache.field,end_field=copy+tab->cache.fields ;
- copy < end_field;
- copy++)
- {
- if (copy->blob_field)
- {
- if (last_record)
- {
- copy->blob_field->set_image((char*) pos,copy->length+sizeof(char*),
- copy->blob_field->charset());
- pos+=copy->length+sizeof(char*);
- }
- else
- {
- copy->blob_field->set_ptr((char*) pos,(char*) pos+copy->length);
- pos+=copy->length+copy->blob_field->get_length();
- }
- }
- else
- {
- if (copy->strip)
- {
- length= uint2korr(pos);
- memcpy(copy->str, pos+2, length);
- memset(copy->str+length, ' ', copy->length-length);
- pos+= 2 + length;
- }
- else
- {
- memcpy(copy->str,pos,copy->length);
- pos+=copy->length;
- }
- }
- }
- tab->cache.pos=pos;
- return;
- }
- static bool
- cmp_buffer_with_ref(JOIN_TAB *tab)
- {
- bool diff;
- if (!(diff=tab->ref.key_err))
- {
- memcpy(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length);
- }
- if ((tab->ref.key_err= cp_buffer_from_ref(tab->join->thd, &tab->ref)) ||
- diff)
- return 1;
- return memcmp(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length)
- != 0;
- }
- bool
- cp_buffer_from_ref(THD *thd, TABLE_REF *ref)
- {
- enum enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- for (store_key **copy=ref->key_copy ; *copy ; copy++)
- {
- if ((*copy)->copy() & 1)
- {
- thd->count_cuted_fields= save_count_cuted_fields;
- return 1; // Something went wrong
- }
- }
- thd->count_cuted_fields= save_count_cuted_fields;
- return 0;
- }
- /*****************************************************************************
- Group and order functions
- *****************************************************************************/
- /*
- Find order/group item in requested columns and change the item to point at
- it. If item doesn't exists, add it first in the field list
- Return 0 if ok.
- */
- static int
- find_order_in_list(THD *thd, Item **ref_pointer_array,
- TABLE_LIST *tables,ORDER *order, List<Item> &fields,
- List<Item> &all_fields)
- {
- Item *it= *order->item;
- if (it->type() == Item::INT_ITEM)
- { /* Order by position */
- uint count= (uint) it->val_int();
- if (!count || count > fields.elements)
- {
- my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
- MYF(0), it->full_name(), thd->where);
- return 1;
- }
- order->item= ref_pointer_array + count-1;
- order->in_field_list= 1;
- return 0;
- }
- uint counter;
- bool unaliased;
- Item **item= find_item_in_list(it, fields, &counter,
- REPORT_EXCEPT_NOT_FOUND, &unaliased);
- if (!item)
- return 1;
- if (item != (Item **)not_found_item)
- {
- /*
- If we have found field not by its alias in select list but by its
- original field name, we should additionaly check if we have conflict
- for this name (in case if we would perform lookup in all tables).
- */
- if (unaliased && !it->fixed && it->fix_fields(thd, tables, order->item))
- return 1;
- order->item= ref_pointer_array + counter;
- order->in_field_list=1;
- return 0;
- }
- order->in_field_list=0;
- /*
- We check it->fixed because Item_func_group_concat can put
- arguments for which fix_fields already was called.
- 'it' reassigned in if condition because fix_field can change it.
- */
- thd->lex->current_select->is_item_list_lookup= 1;
- if (!it->fixed &&
- (it->fix_fields(thd, tables, order->item) ||
- (it= *order->item)->check_cols(1) ||
- thd->is_fatal_error))
- {
- thd->lex->current_select->is_item_list_lookup= 0;
- return 1; // Wrong field
- }
- thd->lex->current_select->is_item_list_lookup= 0;
- uint el= all_fields.elements;
- all_fields.push_front(it); // Add new field to field list
- ref_pointer_array[el]= it;
- order->item= ref_pointer_array + el;
- return 0;
- }
- /*
- Change order to point at item in select list. If item isn't a number
- and doesn't exits in the select list, add it the the field list.
- */
- int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
- List<Item> &fields, List<Item> &all_fields, ORDER *order)
- {
- thd->where="order clause";
- for (; order; order=order->next)
- {
- if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
- all_fields))
- return 1;
- }
- return 0;
- }
- /*
- Intitialize the GROUP BY list.
- SYNOPSIS
- setup_group()
- thd Thread handler
- ref_pointer_array We store references to all fields that was not in
- 'fields' here.
- fields All fields in the select part. Any item in 'order'
- that is part of these list is replaced by a pointer
- to this fields.
- all_fields Total list of all unique fields used by the select.
- All items in 'order' that was not part of fields will
- be added first to this list.
- order The fields we should do GROUP BY on.
- hidden_group_fields Pointer to flag that is set to 1 if we added any fields
- to all_fields.
- RETURN
- 0 ok
- 1 error (probably out of memory)
- */
- int
- setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
- List<Item> &fields, List<Item> &all_fields, ORDER *order,
- bool *hidden_group_fields)
- {
- *hidden_group_fields=0;
- if (!order)
- return 0; /* Everything is ok */
- if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
- {
- Item *item;
- List_iterator<Item> li(fields);
- while ((item=li++))
- item->marker=0; /* Marker that field is not used */
- }
- uint org_fields=all_fields.elements;
- thd->where="group statement";
- for (; order; order=order->next)
- {
- if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
- all_fields))
- return 1;
- (*order->item)->marker=1; /* Mark found */
- if ((*order->item)->with_sum_func)
- {
- my_printf_error(ER_WRONG_GROUP_FIELD, ER(ER_WRONG_GROUP_FIELD),MYF(0),
- (*order->item)->full_name());
- return 1;
- }
- }
- if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
- {
- /* Don't allow one to use fields that is not used in GROUP BY */
- Item *item;
- List_iterator<Item> li(fields);
- while ((item=li++))
- {
- if (item->type() != Item::SUM_FUNC_ITEM && !item->marker &&
- !item->const_item())
- {
- my_printf_error(ER_WRONG_FIELD_WITH_GROUP,
- ER(ER_WRONG_FIELD_WITH_GROUP),
- MYF(0),item->full_name());
- return 1;
- }
- }
- }
- if (org_fields != all_fields.elements)
- *hidden_group_fields=1; // group fields is not used
- return 0;
- }
- /*
- Add fields with aren't used at start of field list. Return FALSE if ok
- */
- static bool
- setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields,
- List<Item> &all_fields, ORDER *new_field)
- {
- Item **item;
- DBUG_ENTER("setup_new_fields");
- thd->set_query_id=1; // Not really needed, but...
- uint counter;
- bool not_used;
- for (; new_field ; new_field= new_field->next)
- {
- if ((item= find_item_in_list(*new_field->item, fields, &counter,
- IGNORE_ERRORS, ¬_used)))
- new_field->item=item; /* Change to shared Item */
- else
- {
- thd->where="procedure list";
- if ((*new_field->item)->fix_fields(thd, tables, new_field->item))
- DBUG_RETURN(1); /* purecov: inspected */
- all_fields.push_front(*new_field->item);
- new_field->item=all_fields.head_ref();
- }
- }
- DBUG_RETURN(0);
- }
- /*
- Create a group by that consist of all non const fields. Try to use
- the fields in the order given by 'order' to allow one to optimize
- away 'order by'.
- */
- static ORDER *
- create_distinct_group(THD *thd, Item **ref_pointer_array,
- ORDER *order_list, List<Item> &fields,
- bool *all_order_by_fields_used)
- {
- List_iterator<Item> li(fields);
- Item *item;
- ORDER *order,*group,**prev;
- uint index= 0;
- *all_order_by_fields_used= 1;
- while ((item=li++))
- item->marker=0; /* Marker that field is not used */
- prev= &group; group=0;
- for (order=order_list ; order; order=order->next)
- {
- if (order->in_field_list)
- {
- ORDER *ord=(ORDER*) thd->memdup((char*) order,sizeof(ORDER));
- if (!ord)
- return 0;
- *prev=ord;
- prev= &ord->next;
- (*ord->item)->marker=1;
- }
- else
- *all_order_by_fields_used= 0;
- }
- li.rewind();
- while ((item=li++))
- {
- if (!item->const_item() && !item->with_sum_func && !item->marker)
- {
- ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER));
- if (!ord)
- return 0;
- /*
- We have here only field_list (not all_field_list), so we can use
- simple indexing of ref_pointer_array (order in the array and in the
- list are same)
- */
- ord->item= ref_pointer_array + index;
- ord->asc=1;
- *prev=ord;
- prev= &ord->next;
- }
- index++;
- }
- *prev=0;
- return group;
- }
- /*****************************************************************************
- Update join with count of the different type of fields
- *****************************************************************************/
- void
- count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
- bool reset_with_sum_func)
- {
- List_iterator<Item> li(fields);
- Item *field;
- param->field_count=param->sum_func_count=param->func_count=
- param->hidden_field_count=0;
- param->quick_group=1;
- while ((field=li++))
- {
- Item::Type type=field->type();
- if (type == Item::FIELD_ITEM)
- param->field_count++;
- else if (type == Item::SUM_FUNC_ITEM)
- {
- if (! field->const_item())
- {
- Item_sum *sum_item=(Item_sum*) field;
- if (!sum_item->quick_group)
- param->quick_group=0; // UDF SUM function
- param->sum_func_count++;
- for (uint i=0 ; i < sum_item->arg_count ; i++)
- {
- if (sum_item->args[0]->type() == Item::FIELD_ITEM)
- param->field_count++;
- else
- param->func_count++;
- }
- }
- }
- else
- {
- param->func_count++;
- if (reset_with_sum_func)
- field->with_sum_func=0;
- }
- }
- }
- /*
- Return 1 if second is a subpart of first argument
- If first parts has different direction, change it to second part
- (group is sorted like order)
- */
- static bool
- test_if_subpart(ORDER *a,ORDER *b)
- {
- for (; a && b; a=a->next,b=b->next)
- {
- if ((*a->item)->eq(*b->item,1))
- a->asc=b->asc;
- else
- return 0;
- }
- return test(!b);
- }
- /*
- Return table number if there is only one table in sort order
- and group and order is compatible
- else return 0;
- */
- static TABLE *
- get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables)
- {
- table_map map= (table_map) 0;
- DBUG_ENTER("get_sort_by_table");
- if (!a)
- a=b; // Only one need to be given
- else if (!b)
- b=a;
- for (; a && b; a=a->next,b=b->next)
- {
- if (!(*a->item)->eq(*b->item,1))
- DBUG_RETURN(0);
- map|=a->item[0]->used_tables();
- }
- if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT)))
- DBUG_RETURN(0);
- for (; !(map & tables->table->map) ; tables=tables->next) ;
- if (map != tables->table->map)
- DBUG_RETURN(0); // More than one table
- DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr));
- DBUG_RETURN(tables->table);
- }
- /* calc how big buffer we need for comparing group entries */
- static void
- calc_group_buffer(JOIN *join,ORDER *group)
- {
- uint key_length=0, parts=0, null_parts=0;
- if (group)
- join->group= 1;
- for (; group ; group=group->next)
- {
- Field *field=(*group->item)->get_tmp_table_field();
- if (field)
- {
- if (field->type() == FIELD_TYPE_BLOB)
- key_length+=MAX_BLOB_WIDTH; // Can't be used as a key
- else
- key_length+=field->pack_length();
- }
- else if ((*group->item)->result_type() == REAL_RESULT)
- key_length+=sizeof(double);
- else if ((*group->item)->result_type() == INT_RESULT)
- key_length+=sizeof(longlong);
- else
- key_length+=(*group->item)->max_length;
- parts++;
- if ((*group->item)->maybe_null)
- null_parts++;
- }
- join->tmp_table_param.group_length=key_length+null_parts;
- join->tmp_table_param.group_parts=parts;
- join->tmp_table_param.group_null_parts=null_parts;
- }
- /*
- allocate group fields or take prepared (cached)
- SYNOPSIS
- make_group_fields()
- main_join - join of current select
- curr_join - current join (join of current select or temporary copy of it)
- RETURN
- 0 - ok
- 1 - failed
- */
- static bool
- make_group_fields(JOIN *main_join, JOIN *curr_join)
- {
- if (main_join->group_fields_cache.elements)
- {
- curr_join->group_fields= main_join->group_fields_cache;
- curr_join->sort_and_group= 1;
- }
- else
- {
- if (alloc_group_fields(curr_join, curr_join->group_list))
- return (1);
- main_join->group_fields_cache= curr_join->group_fields;
- }
- return (0);
- }
- /*
- Get a list of buffers for saveing last group
- Groups are saved in reverse order for easyer check loop
- */
- static bool
- alloc_group_fields(JOIN *join,ORDER *group)
- {
- if (group)
- {
- for (; group ; group=group->next)
- {
- Item_buff *tmp=new_Item_buff(join->thd, *group->item);
- if (!tmp || join->group_fields.push_front(tmp))
- return TRUE;
- }
- }
- join->sort_and_group=1; /* Mark for do_select */
- return FALSE;
- }
- static int
- test_if_group_changed(List<Item_buff> &list)
- {
- DBUG_ENTER("test_if_group_changed");
- List_iterator<Item_buff> li(list);
- int idx= -1,i;
- Item_buff *buff;
- for (i=(int) list.elements-1 ; (buff=li++) ; i--)
- {
- if (buff->cmp())
- idx=i;
- }
- DBUG_PRINT("info", ("idx: %d", idx));
- DBUG_RETURN(idx);
- }
- /*
- Setup copy_fields to save fields at start of new group
- setup_copy_fields()
- thd - THD pointer
- param - temporary table parameters
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
- DESCRIPTION
- Setup copy_fields to save fields at start of new group
- Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
- Change old item_field to use a new field with points at saved fieldvalue
- This function is only called before use of send_fields
-
- RETURN
- 0 - ok
- !=0 - error
- */
- bool
- setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
- Item **ref_pointer_array,
- List<Item> &res_selected_fields, List<Item> &res_all_fields,
- uint elements, List<Item> &all_fields)
- {
- Item *pos;
- List_iterator_fast<Item> li(all_fields);
- Copy_field *copy= NULL;
- res_selected_fields.empty();
- res_all_fields.empty();
- List_iterator_fast<Item> itr(res_all_fields);
- List<Item> extra_funcs;
- uint i, border= all_fields.elements - elements;
- DBUG_ENTER("setup_copy_fields");
- if (param->field_count &&
- !(copy=param->copy_field= new Copy_field[param->field_count]))
- goto err2;
- param->copy_funcs.empty();
- for (i= 0; (pos= li++); i++)
- {
- if (pos->type() == Item::FIELD_ITEM)
- {
- Item_field *item;
- if (!(item= new Item_field(thd, ((Item_field*) pos))))
- goto err;
- pos= item;
- if (item->field->flags & BLOB_FLAG)
- {
- if (!(pos= new Item_copy_string(pos)))
- goto err;
- /*
- Item_copy_string::copy for function can call
- Item_copy_string::val_int for blob via Item_ref.
- But if Item_copy_string::copy for blob isn't called before,
- it's value will be wrong
- so let's insert Item_copy_string for blobs in the beginning of
- copy_funcs
- (to see full test case look at having.test, BUG #4358)
- */
- if (param->copy_funcs.push_front(pos))
- goto err;
- }
- else
- {
- /*
- set up save buffer and change result_field to point at
- saved value
- */
- Field *field= item->field;
- item->result_field=field->new_field(thd->mem_root,field->table);
- char *tmp=(char*) sql_alloc(field->pack_length()+1);
- if (!tmp)
- goto err;
- copy->set(tmp, item->result_field);
- item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
- copy++;
- }
- }
- else if ((pos->type() == Item::FUNC_ITEM ||
- pos->type() == Item::SUBSELECT_ITEM ||
- pos->type() == Item::CACHE_ITEM ||
- pos->type() == Item::COND_ITEM) &&
- !pos->with_sum_func)
- { // Save for send fields
- /* TODO:
- In most cases this result will be sent to the user.
- This should be changed to use copy_int or copy_real depending
- on how the value is to be used: In some cases this may be an
- argument in a group function, like: IF(ISNULL(col),0,COUNT(*))
- */
- if (!(pos=new Item_copy_string(pos)))
- goto err;
- if (i < border) // HAVING, ORDER and GROUP BY
- {
- if (extra_funcs.push_back(pos))
- goto err;
- }
- else if (param->copy_funcs.push_back(pos))
- goto err;
- }
- res_all_fields.push_back(pos);
- ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
- pos;
- }
- param->copy_field_end= copy;
- for (i= 0; i < border; i++)
- itr++;
- itr.sublist(res_selected_fields, elements);
- /*
- Put elements from HAVING, ORDER BY and GROUP BY last to ensure that any
- reference used in these will resolve to a item that is already calculated
- */
- param->copy_funcs.concat(&extra_funcs);
- DBUG_RETURN(0);
- err:
- if (copy)
- delete [] param->copy_field;
- param->copy_field=0;
- err2:
- DBUG_RETURN(TRUE);
- }
- /*
- Make a copy of all simple SELECT'ed items
- This is done at the start of a new group so that we can retrieve
- these later when the group changes.
- */
- void
- copy_fields(TMP_TABLE_PARAM *param)
- {
- Copy_field *ptr=param->copy_field;
- Copy_field *end=param->copy_field_end;
- for (; ptr != end; ptr++)
- (*ptr->do_copy)(ptr);
- List_iterator_fast<Item> it(param->copy_funcs);
- Item_copy_string *item;
- while ((item = (Item_copy_string*) it++))
- item->copy();
- }
- /*
- Make an array of pointers to sum_functions to speed up sum_func calculation
- SYNOPSIS
- alloc_func_list()
- RETURN
- 0 ok
- 1 Error
- */
- bool JOIN::alloc_func_list()
- {
- uint func_count, group_parts;
- DBUG_ENTER("alloc_func_list");
- func_count= tmp_table_param.sum_func_count;
- /*
- If we are using rollup, we need a copy of the summary functions for
- each level
- */
- if (rollup.state != ROLLUP::STATE_NONE)
- func_count*= (send_group_parts+1);
- group_parts= send_group_parts;
- /*
- If distinct, reserve memory for possible
- disctinct->group_by optimization
- */
- if (select_distinct)
- group_parts+= fields_list.elements;
- /* This must use calloc() as rollup_make_fields depends on this */
- sum_funcs= (Item_sum**) thd->calloc(sizeof(Item_sum**) * (func_count+1) +
- sizeof(Item_sum***) * (group_parts+1));
- sum_funcs_end= (Item_sum***) (sum_funcs+func_count+1);
- DBUG_RETURN(sum_funcs == 0);
- }
- /*
- Initialize 'sum_funcs' array with all Item_sum objects
- SYNOPSIS
- make_sum_func_list()
- field_list All items
- send_fields Items in select list
- before_group_by Set to 1 if this is called before GROUP BY handling
- NOTES
- Calls ::setup() for all item_sum objects in field_list
- RETURN
- 0 ok
- 1 error
- */
- bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
- bool before_group_by)
- {
- List_iterator_fast<Item> it(field_list);
- Item_sum **func;
- Item *item;
- DBUG_ENTER("make_sum_func_list");
- func= sum_funcs;
- while ((item=it++))
- {
- if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item())
- {
- *func++= (Item_sum*) item;
- /* let COUNT(DISTINCT) create the temporary table */
- if (((Item_sum*) item)->setup(thd))
- DBUG_RETURN(TRUE);
- }
- }
- if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
- {
- rollup.state= ROLLUP::STATE_READY;
- if (rollup_make_fields(field_list, send_fields, &func))
- DBUG_RETURN(TRUE); // Should never happen
- }
- else if (rollup.state == ROLLUP::STATE_NONE)
- {
- for (uint i=0 ; i <= send_group_parts ;i++)
- sum_funcs_end[i]= func;
- }
- else if (rollup.state == ROLLUP::STATE_READY)
- DBUG_RETURN(FALSE); // Don't put end marker
- *func=0; // End marker
- DBUG_RETURN(FALSE);
- }
- /*
- Change all funcs and sum_funcs to fields in tmp table, and create
- new list of all items.
- change_to_use_tmp_fields()
- thd - THD pointer
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
- RETURN
- 0 - ok
- !=0 - error
- */
- static bool
- change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
- List<Item> &res_selected_fields,
- List<Item> &res_all_fields,
- uint elements, List<Item> &all_fields)
- {
- List_iterator_fast<Item> it(all_fields);
- Item *item_field,*item;
- res_selected_fields.empty();
- res_all_fields.empty();
- uint i, border= all_fields.elements - elements;
- for (i= 0; (item= it++); i++)
- {
- Field *field;
-
- if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
- item_field= item;
- else
- {
- if (item->type() == Item::FIELD_ITEM)
- {
- item_field= item->get_tmp_table_item(thd);
- }
- else if ((field= item->get_tmp_table_field()))
- {
- if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
- item_field= ((Item_sum*) item)->result_item(field);
- else
- item_field= (Item*) new Item_field(field);
- if (!item_field)
- return TRUE; // Fatal error
- item_field->name= item->name; /*lint -e613 */
- #ifndef DBUG_OFF
- if (_db_on_ && !item_field->name)
- {
- char buff[256];
- String str(buff,sizeof(buff),&my_charset_bin);
- str.length(0);
- item->print(&str);
- item_field->name= sql_strmake(str.ptr(),str.length());
- }
- #endif
- }
- else
- item_field= item;
- }
- res_all_fields.push_back(item_field);
- ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
- item_field;
- }
- List_iterator_fast<Item> itr(res_all_fields);
- for (i= 0; i < border; i++)
- itr++;
- itr.sublist(res_selected_fields, elements);
- return FALSE;
- }
- /*
- Change all sum_func refs to fields to point at fields in tmp table
- Change all funcs to be fields in tmp table
- change_refs_to_tmp_fields()
- thd - THD pointer
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
- RETURN
- 0 ok
- 1 error
- */
- static bool
- change_refs_to_tmp_fields(THD *thd, Item **ref_pointer_array,
- List<Item> &res_selected_fields,
- List<Item> &res_all_fields, uint elements,
- List<Item> &all_fields)
- {
- List_iterator_fast<Item> it(all_fields);
- Item *item, *new_item;
- res_selected_fields.empty();
- res_all_fields.empty();
- uint i, border= all_fields.elements - elements;
- for (i= 0; (item= it++); i++)
- {
- res_all_fields.push_back(new_item= item->get_tmp_table_item(thd));
- ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
- new_item;
- }
- List_iterator_fast<Item> itr(res_all_fields);
- for (i= 0; i < border; i++)
- itr++;
- itr.sublist(res_selected_fields, elements);
- return thd->is_fatal_error;
- }
- /******************************************************************************
- Code for calculating functions
- ******************************************************************************/
- static void
- init_tmptable_sum_functions(Item_sum **func_ptr)
- {
- Item_sum *func;
- while ((func= *(func_ptr++)))
- func->reset_field();
- }
- /* Update record 0 in tmp_table from record 1 */
- static void
- update_tmptable_sum_func(Item_sum **func_ptr,
- TABLE *tmp_table __attribute__((unused)))
- {
- Item_sum *func;
- while ((func= *(func_ptr++)))
- func->update_field();
- }
- /* Copy result of sum functions to record in tmp_table */
- static void
- copy_sum_funcs(Item_sum **func_ptr, Item_sum **end_ptr)
- {
- for (; func_ptr != end_ptr ; func_ptr++)
- (void) (*func_ptr)->save_in_result_field(1);
- return;
- }
- static bool
- init_sum_functions(Item_sum **func_ptr, Item_sum **end_ptr)
- {
- for (; func_ptr != end_ptr ;func_ptr++)
- {
- if ((*func_ptr)->reset())
- return 1;
- }
- /* If rollup, calculate the upper sum levels */
- for ( ; *func_ptr ; func_ptr++)
- {
- if ((*func_ptr)->add())
- return 1;
- }
- return 0;
- }
- static bool
- update_sum_func(Item_sum **func_ptr)
- {
- Item_sum *func;
- for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
- if (func->add())
- return 1;
- return 0;
- }
- /* Copy result of functions to record in tmp_table */
- void
- copy_funcs(Item **func_ptr)
- {
- Item *func;
- for (; (func = *func_ptr) ; func_ptr++)
- func->save_in_result_field(1);
- }
- /*
- Create a condition for a const reference and add this to the
- currenct select for the table
- */
- static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
- {
- DBUG_ENTER("add_ref_to_table_cond");
- if (!join_tab->ref.key_parts)
- DBUG_RETURN(FALSE);
- Item_cond_and *cond=new Item_cond_and();
- TABLE *table=join_tab->table;
- int error;
- if (!cond)
- DBUG_RETURN(TRUE);
- for (uint i=0 ; i < join_tab->ref.key_parts ; i++)
- {
- Field *field=table->field[table->key_info[join_tab->ref.key].key_part[i].
- fieldnr-1];
- Item *value=join_tab->ref.items[i];
- cond->add(new Item_func_equal(new Item_field(field), value));
- }
- if (thd->is_fatal_error)
- DBUG_RETURN(TRUE);
- if (!cond->fixed)
- cond->fix_fields(thd,(TABLE_LIST *) 0, (Item**)&cond);
- if (join_tab->select)
- {
- error=(int) cond->add(join_tab->select->cond);
- join_tab->select_cond=join_tab->select->cond=cond;
- }
- else if ((join_tab->select=make_select(join_tab->table, 0, 0, cond,&error)))
- join_tab->select_cond=cond;
- DBUG_RETURN(error ? TRUE : FALSE);
- }
- /*
- Free joins of subselect of this select.
- free_underlaid_joins()
- thd - THD pointer
- select - pointer to st_select_lex which subselects joins we will free
- */
- void free_underlaid_joins(THD *thd, SELECT_LEX *select)
- {
- for (SELECT_LEX_UNIT *unit= select->first_inner_unit();
- unit;
- unit= unit->next_unit())
- unit->cleanup();
- }
- /****************************************************************************
- ROLLUP handling
- ****************************************************************************/
- /*
- Replace occurences of group by fields in an expression by ref items
- SYNOPSIS
- change_group_ref()
- thd reference to the context
- expr expression to make replacement
- group_list list of references to group by items
- changed out: returns 1 if item contains a replaced field item
-
- DESCRIPTION
- The function replaces occurrences of group by fields in expr
- by ref objects for these fields unless they are under aggregate
- functions.
- The function also corrects value of the the maybe_null attribute
- for the items of all subexpressions containing group by fields.
- IMPLEMENTATION
- The function recursively traverses the tree of the expr expression,
- looks for occurrences of the group by fields that are not under
- aggregate functions and replaces them for the corresponding ref items.
- NOTES
- This substitution is needed GROUP BY queries with ROLLUP if
- SELECT list contains expressions over group by attributes.
- TODO: Some functions are not null-preserving. For those functions
- updating of the maybe_null attribute is an overkill.
- EXAMPLES
- SELECT a+1 FROM t1 GROUP BY a WITH ROLLUP
- SELECT SUM(a)+a FROM t1 GROUP BY a WITH ROLLUP
-
- RETURN
- 0 if ok
- 1 on error
- */
- static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list,
- bool *changed)
- {
- if (expr->arg_count)
- {
- Item **arg,**arg_end;
- bool arg_changed= FALSE;
- for (arg= expr->arguments(),
- arg_end= expr->arguments()+expr->arg_count;
- arg != arg_end; arg++)
- {
- Item *item= *arg;
- if (item->type() == Item::FIELD_ITEM || item->type() == Item::REF_ITEM)
- {
- ORDER *group_tmp;
- for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next)
- {
- if (item->eq(*group_tmp->item,0))
- {
- Item *new_item;
- if(!(new_item= new Item_ref(group_tmp->item, 0, item->name)))
- return 1; // fatal_error is set
- thd->change_item_tree(arg, new_item);
- arg_changed= TRUE;
- }
- }
- }
- else if (item->type() == Item::FUNC_ITEM)
- {
- if (change_group_ref(thd, (Item_func *) item, group_list, &arg_changed))
- return 1;
- }
- }
- if (arg_changed)
- {
- expr->maybe_null= 1;
- *changed= TRUE;
- }
- }
- return 0;
- }
- /* Allocate memory needed for other rollup functions */
- bool JOIN::rollup_init()
- {
- uint i,j;
- Item **ref_array;
- tmp_table_param.quick_group= 0; // Can't create groups in tmp table
- rollup.state= ROLLUP::STATE_INITED;
- /*
- Create pointers to the different sum function groups
- These are updated by rollup_make_fields()
- */
- tmp_table_param.group_parts= send_group_parts;
- if (!(rollup.null_items= (Item_null_result**) thd->alloc((sizeof(Item*) +
- sizeof(Item**) +
- sizeof(List<Item>) +
- ref_pointer_array_size)
- * send_group_parts )))
- return 1;
-
- rollup.fields= (List<Item>*) (rollup.null_items + send_group_parts);
- rollup.ref_pointer_arrays= (Item***) (rollup.fields + send_group_parts);
- ref_array= (Item**) (rollup.ref_pointer_arrays+send_group_parts);
- /*
- Prepare space for field list for the different levels
- These will be filled up in rollup_make_fields()
- */
- for (i= 0 ; i < send_group_parts ; i++)
- {
- rollup.null_items[i]= new (thd->mem_root) Item_null_result();
- List<Item> *rollup_fields= &rollup.fields[i];
- rollup_fields->empty();
- rollup.ref_pointer_arrays[i]= ref_array;
- ref_array+= all_fields.elements;
- }
- for (i= 0 ; i < send_group_parts; i++)
- {
- for (j=0 ; j < fields_list.elements ; j++)
- rollup.fields[i].push_back(rollup.null_items[i]);
- }
- List_iterator_fast<Item> it(all_fields);
- Item *item;
- while ((item= it++))
- {
- ORDER *group_tmp;
- for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next)
- {
- if (*group_tmp->item == item)
- item->maybe_null= 1;
- }
- if (item->type() == Item::FUNC_ITEM)
- {
- bool changed= FALSE;
- if (change_group_ref(thd, (Item_func *) item, group_list, &changed))
- return 1;
- /*
- We have to prevent creation of a field in a temporary table for
- an expression that contains GROUP BY attributes.
- Marking the expression item as 'with_sum_func' will ensure this.
- */
- if (changed)
- item->with_sum_func= 1;
- }
- }
- return 0;
- }
-
- /*
- Fill up rollup structures with pointers to fields to use
- SYNOPSIS
- rollup_make_fields()
- fields_arg List of all fields (hidden and real ones)
- sel_fields Pointer to selected fields
- func Store here a pointer to all fields
- IMPLEMENTATION:
- Creates copies of item_sum items for each sum level
- RETURN
- 0 if ok
- In this case func is pointing to next not used element.
- 1 on error
- */
- bool JOIN::rollup_make_fields(List<Item> &fields_arg, List<Item> &sel_fields,
- Item_sum ***func)
- {
- List_iterator_fast<Item> it(fields_arg);
- Item *first_field= sel_fields.head();
- uint level;
- /*
- Create field lists for the different levels
- The idea here is to have a separate field list for each rollup level to
- avoid all runtime checks of which columns should be NULL.
- The list is stored in reverse order to get sum function in such an order
- in func that it makes it easy to reset them with init_sum_functions()
- Assuming: SELECT a, b, c SUM(b) FROM t1 GROUP BY a,b WITH ROLLUP
- rollup.fields[0] will contain list where a,b,c is NULL
- rollup.fields[1] will contain list where b,c is NULL
- ...
- rollup.ref_pointer_array[#] points to fields for rollup.fields[#]
- ...
- sum_funcs_end[0] points to all sum functions
- sum_funcs_end[1] points to all sum functions, except grand totals
- ...
- */
- for (level=0 ; level < send_group_parts ; level++)
- {
- uint i;
- uint pos= send_group_parts - level -1;
- bool real_fields= 0;
- Item *item;
- List_iterator<Item> new_it(rollup.fields[pos]);
- Item **ref_array_start= rollup.ref_pointer_arrays[pos];
- ORDER *start_group;
- /* Point to first hidden field */
- Item **ref_array= ref_array_start + fields_arg.elements-1;
- /* Remember where the sum functions ends for the previous level */
- sum_funcs_end[pos+1]= *func;
- /* Find the start of the group for this level */
- for (i= 0, start_group= group_list ;
- i++ < pos ;
- start_group= start_group->next)
- ;
- it.rewind();
- while ((item= it++))
- {
- if (item == first_field)
- {
- real_fields= 1; // End of hidden fields
- ref_array= ref_array_start;
- }
- if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item())
- {
- /*
- This is a top level summary function that must be replaced with
- a sum function that is reset for this level.
- NOTE: This code creates an object which is not that nice in a
- sub select. Fortunately it's not common to have rollup in
- sub selects.
- */
- item= item->copy_or_same(thd);
- ((Item_sum*) item)->make_unique();
- if (((Item_sum*) item)->setup(thd))
- return 1;
- *(*func)= (Item_sum*) item;
- (*func)++;
- }
- else
- {
- /* Check if this is something that is part of this group by */
- ORDER *group_tmp;
- for (group_tmp= start_group, i= pos ;
- group_tmp ; group_tmp= group_tmp->next, i++)
- {
- if (*group_tmp->item == item)
- {
- /*
- This is an element that is used by the GROUP BY and should be
- set to NULL in this level
- */
- Item_null_result *null_item;
- item->maybe_null= 1; // Value will be null sometimes
- null_item= rollup.null_items[i];
- null_item->result_field= item->get_tmp_table_field();
- item= null_item;
- break;
- }
- }
- }
- *ref_array= item;
- if (real_fields)
- {
- (void) new_it++; // Point to next item
- new_it.replace(item); // Replace previous
- ref_array++;
- }
- else
- ref_array--;
- }
- }
- sum_funcs_end[0]= *func; // Point to last function
- return 0;
- }
- /*
- Send all rollup levels higher than the current one to the client
- SYNOPSIS:
- rollup_send_data()
- idx Level we are on:
- 0 = Total sum level
- 1 = First group changed (a)
- 2 = Second group changed (a,b)
- SAMPLE
- SELECT a, b, c SUM(b) FROM t1 GROUP BY a,b WITH ROLLUP
- RETURN
- 0 ok
- 1 If send_data_failed()
- */
- int JOIN::rollup_send_data(uint idx)
- {
- uint i;
- for (i= send_group_parts ; i-- > idx ; )
- {
- /* Get reference pointers to sum functions in place */
- memcpy((char*) ref_pointer_array,
- (char*) rollup.ref_pointer_arrays[i],
- ref_pointer_array_size);
- if ((!having || having->val_int()))
- {
- if (send_records < unit->select_limit_cnt && do_send_rows &&
- result->send_data(rollup.fields[i]))
- return 1;
- send_records++;
- }
- }
- /* Restore ref_pointer_array */
- set_items_ref_array(current_ref_pointer_array);
- return 0;
- }
- /*
- Write all rollup levels higher than the current one to a temp table
- SYNOPSIS:
- rollup_write_data()
- idx Level we are on:
- 0 = Total sum level
- 1 = First group changed (a)
- 2 = Second group changed (a,b)
- table reference to temp table
- SAMPLE
- SELECT a, b, SUM(c) FROM t1 GROUP BY a,b WITH ROLLUP
- RETURN
- 0 ok
- 1 if write_data_failed()
- */
- int JOIN::rollup_write_data(uint idx, TABLE *table)
- {
- uint i;
- for (i= send_group_parts ; i-- > idx ; )
- {
- /* Get reference pointers to sum functions in place */
- memcpy((char*) ref_pointer_array,
- (char*) rollup.ref_pointer_arrays[i],
- ref_pointer_array_size);
- if ((!having || having->val_int()))
- {
- int error;
- Item *item;
- List_iterator_fast<Item> it(rollup.fields[i]);
- while ((item= it++))
- {
- if (item->type() == Item::NULL_ITEM && item->is_result_field())
- item->save_in_result_field(1);
- }
- copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
- if ((error= table->file->write_row(table->record[0])))
- {
- if (create_myisam_from_heap(thd, table, &tmp_table_param,
- error, 0))
- return 1;
- }
- }
- }
- /* Restore ref_pointer_array */
- set_items_ref_array(current_ref_pointer_array);
- return 0;
- }
- /*
- clear results if there are not rows found for group
- (end_send_group/end_write_group)
- SYNOPSYS
- JOIN::clear()
- */
- void JOIN::clear()
- {
- clear_tables(this);
- copy_fields(&tmp_table_param);
- if (sum_funcs)
- {
- Item_sum *func, **func_ptr= sum_funcs;
- while ((func= *(func_ptr++)))
- func->clear();
- }
- }
- /****************************************************************************
- EXPLAIN handling
- Send a description about what how the select will be done to stdout
- ****************************************************************************/
- static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
- bool distinct,const char *message)
- {
- List<Item> field_list;
- List<Item> item_list;
- THD *thd=join->thd;
- select_result *result=join->result;
- Item *item_null= new Item_null();
- CHARSET_INFO *cs= system_charset_info;
- DBUG_ENTER("select_describe");
- DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
- (ulong)join->select_lex, join->select_lex->type,
- message ? message : "NULL"));
- /* Don't log this into the slow query log */
- thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
- join->unit->offset_limit_cnt= 0;
- if (message)
- {
- item_list.push_back(new Item_int((int32)
- join->select_lex->select_number));
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type), cs));
- for (uint i=0 ; i < 7; i++)
- item_list.push_back(item_null);
- item_list.push_back(new Item_string(message,strlen(message),cs));
- if (result->send_data(item_list))
- join->error= 1;
- }
- else if (join->select_lex == join->unit->fake_select_lex)
- {
- /*
- here we assume that the query will return at least two rows, so we
- show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
- and no filesort will be actually done, but executing all selects in
- the UNION to provide precise EXPLAIN information will hardly be
- appreciated :)
- */
- char table_name_buffer[NAME_LEN];
- item_list.empty();
- /* id */
- item_list.push_back(new Item_null);
- /* select_type */
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type),
- cs));
- /* table */
- {
- SELECT_LEX *sl= join->unit->first_select();
- uint len= 6, lastop= 0;
- memcpy(table_name_buffer, "<union", 6);
- for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
- {
- len+= lastop;
- lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
- "%u,", sl->select_number);
- }
- if (sl || len + lastop >= NAME_LEN)
- {
- memcpy(table_name_buffer + len, "...>", 5);
- len+= 4;
- }
- else
- {
- len+= lastop;
- table_name_buffer[len - 1]= '>'; // change ',' to '>'
- }
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
- }
- /* type */
- item_list.push_back(new Item_string(join_type_str[JT_ALL],
- strlen(join_type_str[JT_ALL]),
- cs));
- /* possible_keys */
- item_list.push_back(item_null);
- /* key*/
- item_list.push_back(item_null);
- /* key_len */
- item_list.push_back(item_null);
- /* ref */
- item_list.push_back(item_null);
- /* rows */
- item_list.push_back(item_null);
- /* extra */
- if (join->unit->global_parameters->order_list.first)
- item_list.push_back(new Item_string("Using filesort",
- 14, cs));
- else
- item_list.push_back(new Item_string("", 0, cs));
- if (result->send_data(item_list))
- join->error= 1;
- }
- else
- {
- table_map used_tables=0;
- for (uint i=0 ; i < join->tables ; i++)
- {
- JOIN_TAB *tab=join->join_tab+i;
- TABLE *table=tab->table;
- char buff[512],*buff_ptr=buff;
- char buff1[512], buff2[512];
- char table_name_buffer[NAME_LEN];
- String tmp1(buff1,sizeof(buff1),cs);
- String tmp2(buff2,sizeof(buff2),cs);
- tmp1.length(0);
- tmp2.length(0);
- item_list.empty();
- /* id */
- item_list.push_back(new Item_uint((uint32)
- join->select_lex->select_number));
- /* select_type */
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type),
- cs));
- if (tab->type == JT_ALL && tab->select && tab->select->quick)
- tab->type= JT_RANGE;
- /* table */
- if (table->derived_select_number)
- {
- /* Derived table name generation */
- int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
- "<derived%u>",
- table->derived_select_number);
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
- }
- else
- item_list.push_back(new Item_string(table->table_name,
- strlen(table->table_name),
- cs));
- /* type */
- item_list.push_back(new Item_string(join_type_str[tab->type],
- strlen(join_type_str[tab->type]),
- cs));
- uint j;
- /* possible_keys */
- if (!tab->keys.is_clear_all())
- {
- for (j=0 ; j < table->keys ; j++)
- {
- if (tab->keys.is_set(j))
- {
- if (tmp1.length())
- tmp1.append(',');
- tmp1.append(table->key_info[j].name,
- strlen(table->key_info[j].name),
- system_charset_info);
- }
- }
- }
- if (tmp1.length())
- item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
- else
- item_list.push_back(item_null);
- /* key key_len ref */
- if (tab->ref.key_parts)
- {
- KEY *key_info=table->key_info+ tab->ref.key;
- item_list.push_back(new Item_string(key_info->name,
- strlen(key_info->name),
- system_charset_info));
- item_list.push_back(new Item_int((int32) tab->ref.key_length));
- for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
- {
- if (tmp2.length())
- tmp2.append(',');
- tmp2.append((*ref)->name(), strlen((*ref)->name()),
- system_charset_info);
- }
- item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
- }
- else if (tab->type == JT_NEXT)
- {
- KEY *key_info=table->key_info+ tab->index;
- item_list.push_back(new Item_string(key_info->name,
- strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) key_info->key_length));
- item_list.push_back(item_null);
- }
- else if (tab->select && tab->select->quick)
- {
- KEY *key_info=table->key_info+ tab->select->quick->index;
- item_list.push_back(new Item_string(key_info->name,
- strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) tab->select->quick->
- max_used_key_length));
- item_list.push_back(item_null);
- }
- else
- {
- item_list.push_back(item_null);
- item_list.push_back(item_null);
- item_list.push_back(item_null);
- }
- /* rows */
- item_list.push_back(new Item_int((longlong) (ulonglong)
- join->best_positions[i]. records_read,
- 21));
- /* extra */
- my_bool key_read=table->key_read;
- if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
- table->used_keys.is_set(tab->index))
- key_read=1;
- if (tab->info)
- item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
- else
- {
- if (tab->select)
- {
- if (tab->use_quick == 2)
- {
- char buf[MAX_KEY/8+1];
- sprintf(buff_ptr,"; Range checked for each record (index map: 0x%s)",
- tab->keys.print(buf));
- buff_ptr=strend(buff_ptr);
- }
- else
- buff_ptr=strmov(buff_ptr,"; Using where");
- }
- if (key_read)
- buff_ptr= strmov(buff_ptr,"; Using index");
- if (table->reginfo.not_exists_optimize)
- buff_ptr= strmov(buff_ptr,"; Not exists");
- if (need_tmp_table)
- {
- need_tmp_table=0;
- buff_ptr= strmov(buff_ptr,"; Using temporary");
- }
- if (need_order)
- {
- need_order=0;
- buff_ptr= strmov(buff_ptr,"; Using filesort");
- }
- if (distinct & test_all_bits(used_tables,thd->used_tables))
- buff_ptr= strmov(buff_ptr,"; Distinct");
- if (buff_ptr == buff)
- buff_ptr+= 2; // Skip inital "; "
- item_list.push_back(new Item_string(buff+2,(uint) (buff_ptr - buff)-2,
- cs));
- }
- // For next iteration
- used_tables|=table->map;
- if (result->send_data(item_list))
- join->error= 1;
- }
- }
- for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
- unit;
- unit= unit->next_unit())
- {
- if (mysql_explain_union(thd, unit, result))
- DBUG_VOID_RETURN;
- }
- DBUG_VOID_RETURN;
- }
- int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
- {
- DBUG_ENTER("mysql_explain_union");
- int res= 0;
- SELECT_LEX *first= unit->first_select();
- for (SELECT_LEX *sl= first;
- sl;
- sl= sl->next_select())
- {
- // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only
- uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN);
- sl->type= (((&thd->lex->select_lex)==sl)?
- ((thd->lex->all_selects_list != sl) ?
- primary_key_name : "SIMPLE"):
- ((sl == first)?
- ((sl->linkage == DERIVED_TABLE_TYPE) ?
- "DERIVED":
- ((uncacheable & UNCACHEABLE_DEPENDENT) ?
- "DEPENDENT SUBQUERY":
- (uncacheable?"UNCACHEABLE SUBQUERY":
- "SUBQUERY"))):
- ((uncacheable & UNCACHEABLE_DEPENDENT) ?
- "DEPENDENT UNION":
- uncacheable?"UNCACHEABLE UNION":
- "UNION")));
- sl->options|= SELECT_DESCRIBE;
- }
- if (first->next_select())
- {
- unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
- unit->fake_select_lex->type= "UNION RESULT";
- unit->fake_select_lex->options|= SELECT_DESCRIBE;
- if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE,
- "")))
- res= unit->exec();
- res|= unit->cleanup();
- }
- else
- {
- thd->lex->current_select= first;
- res= mysql_select(thd, &first->ref_pointer_array,
- (TABLE_LIST*) first->table_list.first,
- first->with_wild, first->item_list,
- first->where,
- first->order_list.elements +
- first->group_list.elements,
- (ORDER*) first->order_list.first,
- (ORDER*) first->group_list.first,
- first->having,
- (ORDER*) thd->lex->proc_list.first,
- first->options | thd->options | SELECT_DESCRIBE,
- result, unit, first);
- }
- if (res > 0 || thd->net.report_error)
- res= -1; // mysql_explain_select do not report error
- DBUG_RETURN(res);
- }
- void st_select_lex::print(THD *thd, String *str)
- {
- if (!thd)
- thd= current_thd;
- str->append("select ", 7);
-
- //options
- if (options & SELECT_STRAIGHT_JOIN)
- str->append("straight_join ", 14);
- if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&
- (this == &thd->lex->select_lex))
- str->append("high_priority ", 14);
- if (options & SELECT_DISTINCT)
- str->append("distinct ", 9);
- if (options & SELECT_SMALL_RESULT)
- str->append("sql_small_result ", 17);
- if (options & SELECT_BIG_RESULT)
- str->append("sql_big_result ", 15);
- if (options & OPTION_BUFFER_RESULT)
- str->append("sql_buffer_result ", 18);
- if (options & OPTION_FOUND_ROWS)
- str->append("sql_calc_found_rows ", 20);
- if (!thd->lex->safe_to_cache_query)
- str->append("sql_no_cache ", 13);
- if (options & OPTION_TO_QUERY_CACHE)
- str->append("sql_cache ", 10);
- //Item List
- bool first= 1;
- List_iterator_fast<Item> it(item_list);
- Item *item;
- while ((item= it++))
- {
- if (first)
- first= 0;
- else
- str->append(',');
- item->print_item_w_name(str);
- }
- /*
- from clause
- TODO: support USING/FORCE/IGNORE index
- */
- if (table_list.elements)
- {
- str->append(" from ", 6);
- Item *next_on= 0;
- for (TABLE_LIST *table= (TABLE_LIST *) table_list.first;
- table;
- table= table->next)
- {
- if (table->derived)
- {
- str->append('(');
- table->derived->print(str);
- str->append(") ");
- str->append(table->alias);
- }
- else
- {
- str->append(table->db);
- str->append('.');
- str->append(table->real_name);
- if (my_strcasecmp(table_alias_charset, table->real_name, table->alias))
- {
- str->append(' ');
- str->append(table->alias);
- }
- }
- if (table->on_expr && ((table->outer_join & JOIN_TYPE_LEFT) ||
- !(table->outer_join & JOIN_TYPE_RIGHT)))
- next_on= table->on_expr;
- if (next_on)
- {
- str->append(" on(", 4);
- next_on->print(str);
- str->append(')');
- next_on= 0;
- }
- TABLE_LIST *next_table;
- if ((next_table= table->next))
- {
- if (table->outer_join & JOIN_TYPE_RIGHT)
- {
- str->append(" right join ", 12);
- if (!(table->outer_join & JOIN_TYPE_LEFT) &&
- table->on_expr)
- next_on= table->on_expr;
- }
- else if (next_table->straight)
- str->append(" straight_join ", 15);
- else if (next_table->outer_join & JOIN_TYPE_LEFT)
- str->append(" left join ", 11);
- else
- str->append(" join ", 6);
- }
- }
- }
- // Where
- Item *cur_where= where;
- if (join)
- cur_where= join->conds;
- if (cur_where)
- {
- str->append(" where ", 7);
- cur_where->print(str);
- }
- // group by & olap
- if (group_list.elements)
- {
- str->append(" group by ", 10);
- print_order(str, (ORDER *) group_list.first);
- switch (olap)
- {
- case CUBE_TYPE:
- str->append(" with cube", 10);
- break;
- case ROLLUP_TYPE:
- str->append(" with rollup", 12);
- break;
- default:
- ; //satisfy compiler
- }
- }
- // having
- Item *cur_having= having;
- if (join)
- cur_having= join->having;
- if (cur_having)
- {
- str->append(" having ", 8);
- cur_having->print(str);
- }
- if (order_list.elements)
- {
- str->append(" order by ", 10);
- print_order(str, (ORDER *) order_list.first);
- }
- // limit
- print_limit(thd, str);
- // PROCEDURE unsupported here
- }
- /*
- change select_result object of JOIN
- SYNOPSIS
- JOIN::change_result()
- res new select_result object
- RETURN
- 0 - OK
- -1 - error
- */
- int JOIN::change_result(select_result *res)
- {
- DBUG_ENTER("JOIN::change_result");
- result= res;
- if (!procedure && result->prepare(fields_list, select_lex->master_unit()))
- {
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }