sql_select.cc
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:185k
- ¶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->funcs=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->real_name=table->path=tmpname;
- table->table_name=base_name(tmpname);
- 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=1;
- table->db_low_byte_first=1; // True for HEAP and MyISAM
- table->temp_pool_slot = temp_pool_slot;
- /* 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<Item> li(fields);
- Item *item;
- Field **tmp_from_field=from_field;
- while ((item=li++))
- {
- Item::Type type=item->type();
- 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()) // We don't have to store this
- continue;
- 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 *arg= ((Item_sum*) item)->args[i];
- if (!arg->const_item())
- {
- Field *new_field=
- create_tmp_field(table,arg,arg->type(),©_func,tmp_from_field,
- group != 0,1);
- if (!new_field)
- goto err; // Should be OOM
- tmp_from_field++;
- *(reg_field++)= new_field;
- reclength+=new_field->pack_length();
- if (!(new_field->flags & NOT_NULL_FLAG))
- null_count++;
- if (new_field->flags & BLOB_FLAG)
- blob_count++;
- ((Item_sum*) item)->args[i]= new Item_field(new_field);
- }
- }
- }
- else
- {
- Field *new_field=create_tmp_field(table,item,type,©_func,
- tmp_from_field, group != 0,1);
- if (!new_field)
- {
- if (thd->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_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;
- }
- field_count= (uint) (reg_field - table->field);
- /* 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)
- {
- 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 (hidden_null_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->record[2]= 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
- }
- 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)
- {
- *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_SKIPP_ENDSPACE;
- else
- recinfo->type=FIELD_NORMAL;
- if (!--hidden_field_count)
- null_count=(null_count+7) & ~7; // move to next byte
- }
- param->copy_field_count=(uint) (copy - param->copy_field);
- param->recinfo=recinfo;
- store_record(table,2); // Make empty default record
- table->max_rows=(((table->db_type == DB_TYPE_HEAP) ?
- min(tmp_table_size, max_heap_table_size) : 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;
- for (; group ; group=group->next,key_part_info++)
- {
- Field *field=(*group->item)->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(table)))
- goto err; /* purecov: inspected */
- if (maybe_null)
- {
- /*
- To be able to group on NULL, we move the null bit to be
- just before the column and extend the key to cover the null bit
- */
- *group_buff= 0; // Init null byte
- key_part_info->offset--;
- key_part_info->length++;
- group->field->move_field((char*) group_buff+1, (uchar*) group_buff,
- 1);
- }
- 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.
- */
- null_pack_length-=hidden_null_pack_length;
- keyinfo->key_parts=field_count+ test(null_pack_length);
- if (allow_distinct_limit)
- {
- set_if_smaller(table->max_rows,thd->select_limit);
- param->end_write_records=thd->select_limit;
- }
- else
- param->end_write_records= HA_POS_ERROR;
- 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;
- keyinfo->key_length=(uint16) reclength;
- keyinfo->name=(char*) "tmp";
- 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, (bool) 1);
- key_part_info->key_type=FIELDFLAG_BINARY;
- key_part_info->type= HA_KEYTYPE_BINARY;
- key_part_info++;
- }
- 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->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:
- 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(ha_lock(table,F_WRLCK)); */ /* Single thread table */
- (void) table->file->extra(HA_EXTRA_NO_READCHECK); /* Not needed */
- (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
- return(0);
- }
- static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
- uint 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;
- MI_KEYSEG *seg= (MI_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=MY_CHARSET_CURRENT;
- 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 (using_unique_constraint &&
- !(field->flags & NOT_NULL_FLAG))
- {
- seg->null_bit= field->null_bit;
- seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
- }
- }
- }
- 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";
- if (entry->db_stat && entry->file)
- {
- (void) entry->file->close();
- delete entry->file;
- }
- if (!(test_flags & TEST_KEEP_TMP_TABLES) || entry->db_type == DB_TYPE_HEAP)
- (void) ha_delete_table(entry->db_type,entry->real_name);
- /* free blobs */
- for (Field **ptr=entry->field ; *ptr ; ptr++)
- delete *ptr;
- 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(TABLE *table, TMP_TABLE_PARAM *param, int error,
- bool ignore_last_dupp_key_error)
- {
- TABLE new_table;
- const char *save_proc_info;
- THD *thd=current_thd;
- 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.options | thd->options))
- goto err2;
- if (open_tmp_table(&new_table))
- goto err1;
- table->file->index_end();
- table->file->rnd_init();
- /* 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->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=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->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
- *****************************************************************************/
- static int
- do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
- {
- int error;
- JOIN_TAB *join_tab;
- int (*end_select)(JOIN *, struct st_join_table *,bool);
- DBUG_ENTER("do_select");
- join->procedure=procedure;
- /*
- ** Tell the client how many fields there are in a row
- */
- if (!table)
- join->result->send_fields(*fields,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)
- {
- DBUG_PRINT("info",("Using end_update"));
- if (table->keys)
- {
- end_select=end_update;
- table->file->index_init(0);
- }
- else
- 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)
- {
- if (!(error=(*end_select)(join,join_tab,0)) || error == -3)
- error=(*end_select)(join,join_tab,1);
- }
- 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 (!table) /* If sending data to client */
- {
- if (error < 0)
- join->result->send_error(0,NullS); /* purecov: inspected */
- else
- {
- join_free(join); // Unlock all cursors
- if (join->result->send_eof())
- error= -1;
- }
- }
- else if (error < 0)
- join->result->send_error(0,NullS); /* purecov: inspected */
- if (error >= 0)
- {
- DBUG_PRINT("info",("%ld records output",join->send_records));
- }
- if (table)
- {
- int old_error=error,tmp;
- if ((tmp=table->file->extra(HA_EXTRA_NO_CACHE)))
- {
- my_errno=tmp;
- error= -1;
- }
- if (table->file->index_end())
- {
- my_errno=tmp;
- error= -1;
- }
- if (error != old_error)
- table->file->print_error(my_errno,MYF(0));
- }
- DBUG_RETURN(error < 0);
- }
- 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;
- int (*next_select)(JOIN *,struct st_join_table *,bool)=
- join_tab->next_select;
- 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;
- do
- {
- if (join->thd->killed) // Aborted by user
- {
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
- return -2; /* purecov: inspected */
- }
- 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=(*next_select)(join,join_tab+1,0)) < 0)
- return error;
- if (not_used_in_distinct && found_records != join->found_records)
- return 0;
- }
- }
- } while (!(error=info->read_record(info)));
- if (error > 0) // Fatal error
- return -1;
- }
- else if (error > 0)
- return -1;
- if (!found && on_expr)
- { // OUTER JOIN
- restore_record(join_tab->table,2); // Make empty record
- mark_as_null_row(join_tab->table); // For group by without error
- if (!select_cond || select_cond->val_int())
- {
- if ((error=(*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 skipp_last)
- {
- int error;
- READ_RECORD *info;
- if (!join_tab->cache.records)
- return 0; /* Nothing to do */
- if (skipp_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(&join_tab->cache);
- join_tab->cache.records=0; join_tab->cache.ptr_record= (uint) ~0;
- 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->skipp_record()))
- {
- uint i;
- reset_cache(&join_tab->cache);
- for (i=(join_tab->cache.records- (skipp_last ? 1 : 0)) ; i-- > 0 ;)
- {
- read_cached_record(join_tab);
- if (!select || !select->skipp_record())
- if ((error=(join_tab->next_select)(join,join_tab+1,0)) < 0)
- return error; /* purecov: inspected */
- }
- }
- } while (!(error=info->read_record(info)));
- if (skipp_last)
- read_cached_record(join_tab); // Restore current record
- reset_cache(&join_tab->cache);
- join_tab->cache.records=0; join_tab->cache.ptr_record= (uint) ~0;
- 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
- *****************************************************************************/
- static int
- join_read_const_tables(JOIN *join)
- {
- uint i;
- int error;
- DBUG_ENTER("join_read_const_tables");
- for (i=0 ; i < join->const_tables ; i++)
- {
- TABLE *form=join->table[i];
- form->null_row=0;
- form->status=STATUS_NO_RECORD;
- if (join->join_tab[i].type == JT_SYSTEM)
- {
- if ((error=join_read_system(join->join_tab+i)))
- { // Info for DESCRIBE
- join->join_tab[i].info="const row not found";
- join->best_positions[i].records_read=0.0;
- if (!form->outer_join || error > 0)
- DBUG_RETURN(error);
- }
- }
- else
- {
- if ((error=join_read_const(join->join_tab+i)))
- {
- join->join_tab[i].info="unique row not found";
- join->best_positions[i].records_read=0.0;
- if (!form->outer_join || error > 0)
- DBUG_RETURN(error);
- }
- }
- if (join->join_tab[i].on_expr && !form->null_row)
- {
- if ((form->null_row= test(join->join_tab[i].on_expr->val_int() == 0)))
- empty_record(form);
- }
- if (!form->null_row)
- form->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->rnd_first(table->record[0])))
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- table->file->print_error(error,MYF(0));
- return 1;
- }
- table->null_row=1; // This is ok.
- empty_record(table); // Make empty record
- return -1;
- }
- store_record(table,1);
- }
- else if (!table->status) // Only happens with left join
- restore_record(table,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->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)
- {
- table->null_row=1;
- empty_record(table);
- if (error != HA_ERR_KEY_NOT_FOUND)
- {
- sql_print_error("read_const: Got error %d when reading table %s",
- error, table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- store_record(table,1);
- }
- else if (!table->status) // Only happens with left join
- restore_record(table,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 (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)
- {
- sql_print_error("read_key: Got error %d when reading table '%s'",error,
- table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- }
- table->null_row=0;
- return table->status ? -1 : 0;
- }
- static int
- join_read_always_key(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- if (cp_buffer_from_ref(&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)
- {
- sql_print_error("read_const: Got error %d when reading table %s",error,
- table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- 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(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)
- {
- sql_print_error("read_next: Got error %d when reading table %s",error,
- table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- table->status= STATUS_GARBAGE;
- return -1;
- }
- return 0;
- }
- 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->keys,(table_map) 0,HA_POS_ERROR);
- }
- 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,current_thd, tab->table, tab->select,1,1);
- return (*tab->read_record.read_record)(&tab->read_record);
- }
- static int
- join_init_read_first_with_key(JOIN_TAB *tab)
- {
- int error;
- TABLE *table=tab->table;
- if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)))
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- tab->table->status=0;
- tab->read_record.read_record=join_init_read_next_with_key;
- tab->read_record.table=table;
- tab->read_record.file=table->file;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
- error=tab->table->file->index_first(tab->table->record[0]);
- if (error)
- {
- if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("read_first_with_key: Got error %d when reading table",
- error);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- static int
- join_init_read_next_with_key(READ_RECORD *info)
- {
- int error=info->file->index_next(info->record);
- if (error)
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("read_next_with_key: Got error %d when reading table %s",
- error, info->table->path);
- info->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- static int
- join_init_read_last_with_key(JOIN_TAB *tab)
- {
- TABLE *table=tab->table;
- int error;
- if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)))
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- tab->table->status=0;
- tab->read_record.read_record=join_init_read_prev_with_key;
- tab->read_record.table=table;
- tab->read_record.file=table->file;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
- error=tab->table->file->index_last(tab->table->record[0]);
- if (error)
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("read_last_with_key: Got error %d when reading table",
- error, table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- static int
- join_init_read_prev_with_key(READ_RECORD *info)
- {
- int error=info->file->index_prev(info->record);
- if (error)
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("read_prev_with_key: Got error %d when reading table: %s",
- error,info->table->path);
- info->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- static int
- join_ft_read_first(JOIN_TAB *tab)
- {
- int error;
- TABLE *table= tab->table;
- #if 0
- if (cp_buffer_from_ref(&tab->ref)) // as ft-key doesn't use store_key's
- return -1; // see also FT_SELECT::init()
- #endif
- table->file->ft_init();
- error=table->file->ft_read(table->record[0]);
- if (error)
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("ft_read_first: Got error %d when reading table %s",
- error, table->path);
- table->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- static int
- join_ft_read_next(READ_RECORD *info)
- {
- int error=info->file->ft_read(info->table->record[0]);
- if (error)
- {
- if (error != HA_ERR_END_OF_FILE)
- {
- sql_print_error("ft_read_next: Got error %d when reading table %s",
- error, info->table->path);
- info->file->print_error(error,MYF(0));
- return 1;
- }
- return -1;
- }
- return 0;
- }
- /*****************************************************************************
- ** 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
- if (join->procedure)
- error=join->procedure->send_row(*join->fields);
- else
- error=join->result->send_data(*join->fields);
- if (error)
- DBUG_RETURN(-1); /* purecov: inspected */
- if (++join->send_records >= join->thd->select_limit)
- 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;
- if (join->procedure)
- {
- if (join->having && join->having->val_int() == 0)
- error= -1; // Didn't satisfy having
- else
- error=join->procedure->send_row(*join->fields) ? 1 : 0;
- if (end_of_records && join->procedure->end_of_records())
- error= 1; // Fatal error
- }
- else
- {
- if (!join->first_record)
- clear_tables(join);
- if (join->having && join->having->val_int() == 0)
- error= -1; // Didn't satisfy having
- else
- error=join->result->send_data(*join->fields) ? 1 : 0;
- }
- if (error > 0)
- DBUG_RETURN(-1); /* purecov: inspected */
- if (end_of_records)
- DBUG_RETURN(0);
- if (!error && ++join->send_records >= join->thd->select_limit)
- DBUG_RETURN(-3); /* Abort nicely */
- }
- }
- 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);
- init_sum_functions(join->sum_funcs);
- 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.funcs);
- 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;
- if (item->maybe_null)
- {
- Field *field=item->tmp_table_field();
- field->ptr[-1]= (byte) (field->is_null() ? 0 : 1);
- }
- }
- }
- 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(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)
- DBUG_RETURN(-3);
- }
- }
- }
- 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);
- if (item->maybe_null)
- group->buff[0]=item->null_value ? 0: 1; // Save reversed value
- }
- // table->file->index_init(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,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.funcs);
- if ((error=table->file->write_row(table->record[0])))
- {
- if (create_myisam_from_heap(table, &join->tmp_table_param, error, 0))
- DBUG_RETURN(-1); // Not a table_is_full error
- /* Change method to update rows */
- table->file->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.funcs);
- 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,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();
- if (idx < (int) join->send_group_parts)
- {
- if (!join->first_record)
- clear_tables(join);
- copy_sum_funcs(join->sum_funcs);
- if (!join->having || join->having->val_int())
- {
- if ((error=table->file->write_row(table->record[0])))
- {
- if (create_myisam_from_heap(table, &join->tmp_table_param,
- error, 0))
- DBUG_RETURN(1); // Not a table_is_full error
- }
- else
- join->send_records++;
- }
- if (end_of_records)
- DBUG_RETURN(0);
- }
- }
- else
- {
- 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.funcs);
- init_sum_functions(join->sum_funcs);
- 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;
- if (!field->table->const_table) // No need to change const test
- {
- Item *ref_item=part_of_refkey(field->table,field);
- if (ref_item && ref_item->eq(right_item))
- {
- if (right_item->type() == Item::FIELD_ITEM)
- return (field->eq_def(((Item_field *) right_item)->field) &&
- !field->table->maybe_null);
- if (right_item->const_item())
- {
- // We can remove binary fields and numerical fields except float,
- // as float comparison isn't 100 % secure
- if (field->binary() &&
- (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)
- {
- 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:
- new_cond->used_tables_cache=((Item_cond*) 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);
- }
- new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache;
- 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)
- {
- 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))
- return table->reginfo.join_tab->ref.items[part];
- }
- return (Item*) 0;
- }
- /*****************************************************************************
- ** Test if one can use the key to resolve ORDER BY
- ** Returns: 1 if key is ok.
- ** 0 if key can't be used
- ** -1 if reverse key can be used
- *****************************************************************************/
- static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx)
- {
- 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;
- 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()
- */
- while (const_key_parts & 1)
- {
- key_part++; const_key_parts>>=1;
- }
- if (key_part == key_part_end || key_part->field != field)
- 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)
- return 0;
- reverse=flag; // Remember if reverse
- key_part++;
- }
- return reverse;
- }
- static uint find_shortest_key(TABLE *table, key_map usable_keys)
- {
- uint min_length= (uint) ~0;
- uint best= MAX_KEY;
- for (uint nr=0; usable_keys ; usable_keys>>=1, nr++)
- {
- if (usable_keys & 1)
- {
- if (table->key_info[nr].key_length < min_length)
- {
- min_length=table->key_info[nr].key_length;
- best=nr;
- }
- }
- }
- return best;
- }
- /*****************************************************************************
- ** If not selecting by given key, create a index how records should be read
- ** return: 0 ok
- ** -1 some fatal error
- ** 1 no records
- *****************************************************************************/
- /* Return 1 if we don't have to do file sorting */
- static bool
- test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
- {
- int ref_key;
- TABLE *table=tab->table;
- SQL_SELECT *select=tab->select;
- key_map usable_keys;
- DBUG_ENTER("test_if_skip_sort_order");
- /* Check which keys can be used to resolve ORDER BY */
- usable_keys= ~(key_map) 0;
- for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next)
- {
- if ((*tmp_order->item)->type() != Item::FIELD_ITEM)
- {
- usable_keys=0;
- break;
- }
- usable_keys&=((Item_field*) (*tmp_order->item))->field->part_of_sortkey;
- }
- ref_key= -1;
- if (tab->ref.key >= 0) // Constant range in WHERE
- ref_key=tab->ref.key;
- else if (select && select->quick) // Range found by opt_range
- ref_key=select->quick->index;
- if (ref_key >= 0)
- {
- /* Check if we get the rows in requested sorted order by using the key */
- if ((usable_keys & ((key_map) 1 << ref_key)) &&
- test_if_order_by_key(order,table,ref_key) == 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=usable_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->used_keys;
- for (nr=0; keys ; keys>>=1, nr++)
- {
- if (keys & 1)
- {
- int flag;
- if ((flag=test_if_order_by_key(order,table,nr)))
- {
- tab->index=nr;
- tab->read_first_record= (flag > 0 ? join_init_read_first_with_key:
- join_init_read_last_with_key);
- table->file->index_init(nr);
- tab->type=JT_NEXT; // Read with index_first(), index_next()
- DBUG_RETURN(1);
- }
- }
- }
- }
- DBUG_RETURN(0); // Can't use index.
- }
- static int
- create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
- {
- SORT_FIELD *sortorder;
- uint length;
- TABLE *table=tab->table;
- SQL_SELECT *select=tab->select;
- DBUG_ENTER("create_sort_index");
- if (test_if_skip_sort_order(tab,order,select_limit))
- DBUG_RETURN(0);
- if (!(sortorder=make_unireg_sortorder(order,&length)))
- goto err; /* purecov: inspected */
- /* It's not fatal if the following alloc fails */
- table->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.
- */
- if (!(select->quick=get_ft_or_quick_select_for_ref(table, tab)))
- goto err;
- }
- }
- table->found_records=filesort(&table,sortorder,length,
- select, 0L, select_limit);
- delete select; // 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;
- if (table->key_read) // Restore if we used indexes
- {
- table->key_read=0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
- DBUG_RETURN(table->found_records == HA_POS_ERROR);
- err:
- DBUG_RETURN(-1);
- }
- /*****************************************************************************
- ** Remove duplicates from tmp table
- ** This should be recoded to add a uniuqe index to the table and remove
- ** dupplicates
- ** 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)
- {
- int error;
- ulong reclength,offset;
- uint field_count;
- DBUG_ENTER("remove_duplicates");
- entry->reginfo.lock_type=TL_WRITE;
- entry->file->extra(HA_EXTRA_NO_READCHECK);
- /* Calculate how many saved fields there is in list */
- field_count=0;
- List_iterator<Item> it(fields);
- Item *item;
- while ((item=it++))
- {
- if (item->tmp_table_field() && ! item->const_item())
- field_count++;
- }
- if (!field_count)
- { // only const items
- join->thd->select_limit=1; // Only send first row
- DBUG_RETURN(0);
- }
- Field **first_field=entry->field+entry->fields - field_count;
- offset=entry->field[entry->fields - field_count]->offset();
- 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) +sizeof(HASH_LINK)) * entry->file->records <
- sortbuff_size)))
- error=remove_dup_with_hash_index(join->thd, entry,
- field_count, first_field,
- reclength);
- else
- error=remove_dup_with_compare(join->thd, entry, first_field, offset);
- free_blobs(first_field);
- DBUG_RETURN(error);
- }
- static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
- ulong offset)
- {
- handler *file=table->file;
- char *org_record,*new_record;
- int error;
- ulong reclength=table->reclength-offset;
- DBUG_ENTER("remove_dup_with_compare");
- org_record=(char*) table->record[0]+offset;
- new_record=(char*) table->record[1]+offset;
- file->rnd_init();
- error=file->rnd_next(table->record[0]);
- 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 (copy_blobs(first_field))
- {
- my_error(ER_OUT_OF_SORTMEMORY,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(table->record[0])))
- {
- 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(table->record[0])))
- goto err;
- }
- else if (!found)
- {
- found=1;
- file->position(table->record[0]); // Remember position
- }
- }
- if (!found)
- break; // End of file
- /* Restart search on next row */
- error=file->restart_rnd_next(table->record[0],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)
- {
- 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);
- if (hash_init(&hash, (uint) file->records, 0, key_length,
- (hash_get_key) 0, 0, 0))
- {
- my_free((char*) key_buffer,MYF(0));
- DBUG_RETURN(1);
- }
- {
- Field **ptr;
- for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
- (*field_length++)= (*ptr)->pack_length();
- }
- file->rnd_init();
- key_pos=key_buffer;
- for (;;)
- {
- 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;
- }
- /* copy fields to key buffer */
- 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,key_pos-key_length,key_length))
- {
- /* Duplicated found ; Remove the row */
- if ((error=file->delete_row(record)))
- goto err;
- }
- (void) hash_insert(&hash, key_pos-key_length);
- key_pos+=extra_length;
- }
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
- file->extra(HA_EXTRA_NO_CACHE);
- (void) file->rnd_end();
- DBUG_RETURN(0);
- err:
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
- file->extra(HA_EXTRA_NO_CACHE);
- (void) file->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])->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;
- DBUG_ENTER("join_init_cache");
- cache= &tables[table_count].cache;
- cache->fields=blobs=0;
- for (i=0 ; i < table_count ; i++)
- {
- cache->fields+=tables[i].used_fields;
- blobs+=tables[i].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_fields+7)/8;
- 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->records=0; cache->ptr_record= (uint) ~0;
- cache->length=length+blobs*sizeof(char*);
- cache->blobs=blobs;
- *blob_ptr=0; /* End sequentel */
- size=max(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(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)
- {
- ulong 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*));
- pos+=copy->length+sizeof(char*);
- }
- else
- {
- copy->blob_field->get_image((char*) pos,copy->length); // blob length
- 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+1,str,length);
- *pos=(uchar) length;
- pos+=length+1;
- }
- 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(JOIN_CACHE *cache)
- {
- cache->record_nr=0;
- cache->pos=cache->buff;
- }
- 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*));
- 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)
- {
- memcpy(copy->str,pos+1,length=(uint) *pos);
- memset(copy->str+length,' ',copy->length-length);
- pos+=1+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->ref)) || diff)
- return 1;
- return memcmp(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length)
- != 0;
- }
- bool
- cp_buffer_from_ref(TABLE_REF *ref)
- {
- for (store_key **copy=ref->key_copy ; *copy ; copy++)
- if ((*copy)->copy())
- return 1; // Something went wrong
- 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,TABLE_LIST *tables,ORDER *order,List<Item> &fields,
- List<Item> &all_fields)
- {
- if ((*order->item)->type() == Item::INT_ITEM)
- { /* Order by position */
- Item *item=0;
- List_iterator<Item> li(fields);
- for (uint count= (uint) ((Item_int*) (*order->item))->value ;
- count-- && (item=li++) ;) ;
- if (!item)
- {
- my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
- MYF(0),(*order->item)->full_name(),
- thd->where);
- return 1;
- }
- order->item=li.ref();
- order->in_field_list=1;
- return 0;
- }
- const char *save_where=thd->where;
- thd->where=0; // No error if not found
- Item **item=find_item_in_list(*order->item,fields);
- thd->where=save_where;
- if (item)
- {
- order->item=item; // use it
- order->in_field_list=1;
- return 0;
- }
- order->in_field_list=0;
- if ((*order->item)->fix_fields(thd,tables) || thd->fatal_error)
- return 1; // Wrong field
- all_fields.push_front(*order->item); // Add new field to field list
- order->item=(Item**) all_fields.head_ref();
- 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,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,tables,order,fields,all_fields))
- return 1;
- }
- return 0;
- }
- static int
- setup_group(THD *thd,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->options & OPTION_ANSI_MODE)
- {
- 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,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->options & OPTION_ANSI_MODE)
- {
- /* 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)
- {
- 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...
- thd->where=0; // Don't give error
- for ( ; new_field ; new_field=new_field->next)
- {
- if ((item=find_item_in_list(*new_field->item,fields)))
- new_field->item=item; /* Change to shared Item */
- else
- {
- thd->where="procedure list";
- if ((*new_field->item)->fix_fields(thd,tables))
- DBUG_RETURN(1); /* purecov: inspected */
- thd->where=0;
- 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(ORDER *order_list,List<Item> &fields)
- {
- List_iterator<Item> li(fields);
- Item *item;
- ORDER *order,*group,**prev;
- 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*) sql_memdup(order,sizeof(ORDER));
- if (!ord)
- return 0;
- *prev=ord;
- prev= &ord->next;
- (*ord->item)->marker=1;
- }
- }
- li.rewind();
- while ((item=li++))
- {
- if (item->const_item() || item->with_sum_func)
- continue;
- if (!item->marker)
- {
- ORDER *ord=(ORDER*) sql_calloc(sizeof(ORDER));
- if (!ord)
- return 0;
- ord->item=li.ref();
- ord->asc=1;
- *prev=ord;
- prev= &ord->next;
- }
- }
- *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))
- 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))
- DBUG_RETURN(0);
- map|=a->item[0]->used_tables();
- }
- if (!map || (map & RAND_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;
- if (group)
- join->group= 1;
- for (; group ; group=group->next)
- {
- Field *field=(*group->item)->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)
- key_length++;
- }
- join->tmp_table_param.group_length=key_length;
- join->tmp_table_param.group_parts=parts;
- }
- /*
- ** 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(*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)
- {
- 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;
- }
- return idx;
- }
- /*
- ** 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
- */
- bool
- setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields)
- {
- Item *pos;
- List_iterator<Item> li(fields);
- Copy_field *copy;
- DBUG_ENTER("setup_copy_fields");
- if (!(copy=param->copy_field= new Copy_field[param->field_count]))
- goto err;
- param->copy_funcs.empty();
- while ((pos=li++))
- {
- if (pos->type() == Item::FIELD_ITEM)
- {
- Item_field *item=(Item_field*) pos;
- if (item->field->flags & BLOB_FLAG)
- {
- if (!(pos=new Item_copy_string(pos)))
- goto err;
- VOID(li.replace(pos));
- if (param->copy_funcs.push_back(pos))
- goto err;
- continue;
- }
- /* set up save buffer and change result_field to point at saved value */
- Field *field= item->field;
- item->result_field=field->new_field(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::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;
- VOID(li.replace(pos));
- if (param->copy_funcs.push_back(pos))
- goto err;
- }
- }
- param->copy_field_count= (uint) (copy - param->copy_field);
- DBUG_RETURN(0);
- err:
- delete [] param->copy_field;
- param->copy_field=0;
- DBUG_RETURN(TRUE);
- }
- /*
- ** Copy fields and null values between two tables
- */
- void
- copy_fields(TMP_TABLE_PARAM *param)
- {
- Copy_field *ptr=param->copy_field;
- Copy_field *end=ptr+param->copy_field_count;
- for ( ; ptr != end; ptr++)
- (*ptr->do_copy)(ptr);
- List_iterator<Item> it(param->copy_funcs);
- Item_copy_string *item;
- while ((item = (Item_copy_string*) it++))
- {
- item->copy();
- }
- }
- /*****************************************************************************
- ** Make an array of pointer to sum_functions to speed up sum_func calculation
- *****************************************************************************/
- static bool
- make_sum_func_list(JOIN *join,List<Item> &fields)
- {
- DBUG_ENTER("make_sum_func_list");
- Item_sum **func =
- (Item_sum**) sql_alloc(sizeof(Item_sum*)*
- (join->tmp_table_param.sum_func_count+1));
- if (!func)
- DBUG_RETURN(TRUE);
- List_iterator<Item> it(fields);
- join->sum_funcs=func;
- Item *field;
- while ((field=it++))
- {
- if (field->type() == Item::SUM_FUNC_ITEM && !field->const_item())
- {
- *func++=(Item_sum*) field;
- /* let COUNT(DISTINCT) create the temporary table */
- if (((Item_sum*) field)->setup(join->thd))
- DBUG_RETURN(TRUE);
- }
- }
- *func=0; // End marker
- DBUG_RETURN(FALSE);
- }
- /*
- ** Change all funcs and sum_funcs to fields in tmp table
- */
- static bool
- change_to_use_tmp_fields(List<Item> &items)
- {
- List_iterator<Item> it(items);
- Item *item_field,*item;
- while ((item=it++))
- {
- Field *field;
- if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
- continue;
- if (item->type() == Item::FIELD_ITEM)
- {
- ((Item_field*) item)->field=
- ((Item_field*) item)->result_field;
- }
- else if ((field=item->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));
- str.length(0);
- item->print(&str);
- item_field->name=sql_strmake(str.ptr(),str.length());
- }
- #endif
- #ifdef DELETE_ITEMS
- delete it.replace(item_field); /*lint -e613 */
- #else
- (void) it.replace(item_field); /*lint -e613 */
- #endif
- }
- }
- 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
- */
- static bool
- change_refs_to_tmp_fields(THD *thd,List<Item> &items)
- {
- List_iterator<Item> it(items);
- Item *item;
- while ((item= it++))
- {
- if (item->type() == Item::SUM_FUNC_ITEM)
- {
- if (!item->const_item())
- {
- Item_sum *sum_item= (Item_sum*) item;
- if (sum_item->result_field) // If not a const sum func
- {
- Field *result_field=sum_item->result_field;
- for (uint i=0 ; i < sum_item->arg_count ; i++)
- {
- Item *arg= sum_item->args[i];
- if (!arg->const_item())
- {
- if (arg->type() == Item::FIELD_ITEM)
- ((Item_field*) arg)->field= result_field++;
- else
- sum_item->args[i]= new Item_field(result_field++);
- }
- }
- }
- }
- }
- else if (item->with_sum_func)
- continue;
- else if ((item->type() == Item::FUNC_ITEM ||
- item->type() == Item::COND_ITEM) &&
- !item->const_item())
- { /* All funcs are stored */
- #ifdef DELETE_ITEMS
- delete it.replace(new Item_field(((Item_func*) item)->result_field));
- #else
- (void) it.replace(new Item_field(((Item_func*) item)->result_field));
- #endif
- }
- else if (item->type() == Item::FIELD_ITEM) /* Change refs */
- {
- ((Item_field*)item)->field=((Item_field*) item)->result_field;
- }
- }
- return thd->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(0);
- }
- /* Copy result of sum functions to record in tmp_table */
- static void
- copy_sum_funcs(Item_sum **func_ptr)
- {
- Item_sum *func;
- for (; (func = *func_ptr) ; func_ptr++)
- (void) func->save_in_field(func->result_field);
- return;
- }
- static void
- init_sum_functions(Item_sum **func_ptr)
- {
- Item_sum *func;
- for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
- func->reset();
- }
- 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_result_field **func_ptr)
- {
- Item_result_field *func;
- for (; (func = *func_ptr) ; func_ptr++)
- (void) func->save_in_field(func->result_field);
- return;
- }
- /*****************************************************************************
- ** 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->fatal_error)
- DBUG_RETURN(TRUE);
- /*
- Here we pass 0 as the first argument to fix_fields that don't need
- to do any stack checking (This is already done in the initial fix_fields).
- */
- cond->fix_fields((THD *) 0,(TABLE_LIST *) 0);
- 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);
- }
- /****************************************************************************
- ** 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)
- {
- List<Item> field_list;
- Item *item;
- THD *thd=join->thd;
- DBUG_ENTER("select_describe");
- /* Don't log this into the slow query log */
- join->thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
- field_list.push_back(new Item_empty_string("table",NAME_LEN));
- field_list.push_back(new Item_empty_string("type",10));
- field_list.push_back(item=new Item_empty_string("possible_keys",
- NAME_LEN*MAX_KEY));
- item->maybe_null=1;
- field_list.push_back(item=new Item_empty_string("key",NAME_LEN));
- item->maybe_null=1;
- field_list.push_back(item=new Item_int("key_len",0,3));
- item->maybe_null=1;
- field_list.push_back(item=new Item_empty_string("ref",
- NAME_LEN*MAX_REF_PARTS));
- item->maybe_null=1;
- field_list.push_back(new Item_real("rows",0.0,0,10));
- field_list.push_back(new Item_empty_string("Extra",255));
- if (send_fields(thd,field_list,1))
- return; /* purecov: inspected */
- char buff[512],*buff_ptr;
- String tmp(buff,sizeof(buff)),*packet= &thd->packet;
- table_map used_tables=0;
- for (uint i=0 ; i < join->tables ; i++)
- {
- JOIN_TAB *tab=join->join_tab+i;
- TABLE *table=tab->table;
- if (tab->type == JT_ALL && tab->select && tab->select->quick)
- tab->type= JT_RANGE;
- packet->length(0);
- net_store_data(packet,table->table_name);
- net_store_data(packet,join_type_str[tab->type]);
- tmp.length(0);
- key_map bits;
- uint j;
- for (j=0,bits=tab->keys ; bits ; j++,bits>>=1)
- {
- if (bits & 1)
- {
- if (tmp.length())
- tmp.append(',');
- tmp.append(table->key_info[j].name);
- }
- }
- if (tmp.length())
- net_store_data(packet,tmp.ptr(),tmp.length());
- else
- net_store_null(packet);
- if (tab->ref.key_parts)
- {
- net_store_data(packet,table->key_info[tab->ref.key].name);
- net_store_data(packet,(uint32) tab->ref.key_length);
- tmp.length(0);
- for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
- {
- if (tmp.length())
- tmp.append(',');
- tmp.append((*ref)->name());
- }
- net_store_data(packet,tmp.ptr(),tmp.length());
- }
- else if (tab->type == JT_NEXT)
- {
- net_store_data(packet,table->key_info[tab->index].name);
- net_store_data(packet,(uint32) table->key_info[tab->index].key_length);
- net_store_null(packet);
- }
- else if (tab->select && tab->select->quick)
- {
- net_store_data(packet,table->key_info[tab->select->quick->index].name);;
- net_store_data(packet,(uint32) tab->select->quick->max_used_key_length);
- net_store_null(packet);
- }
- else
- {
- net_store_null(packet);
- net_store_null(packet);
- net_store_null(packet);
- }
- sprintf(buff,"%.0f",join->best_positions[i].records_read);
- net_store_data(packet,buff);
- my_bool key_read=table->key_read;
- if (tab->type == JT_NEXT &&
- ((table->used_keys & ((key_map) 1 << tab->index))))
- key_read=1;
- buff_ptr=buff;
- if (tab->info)
- net_store_data(packet,tab->info);
- else if (tab->select)
- {
- if (tab->use_quick == 2)
- {
- sprintf(buff_ptr,"range checked for each record (index map: %u)",
- tab->keys);
- buff_ptr=strend(buff_ptr);
- }
- else
- buff_ptr=strmov(buff_ptr,"where used");
- }
- if (key_read)
- {
- if (buff != buff_ptr)
- {
- buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
- }
- buff_ptr=strmov(buff_ptr,"Using index");
- }
- if (table->reginfo.not_exists_optimize)
- {
- if (buff != buff_ptr)
- {
- buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
- }
- buff_ptr=strmov(buff_ptr,"Not exists");
- }
- if (need_tmp_table)
- {
- need_tmp_table=0;
- if (buff != buff_ptr)
- {
- buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
- }
- buff_ptr=strmov(buff_ptr,"Using temporary");
- }
- if (need_order)
- {
- need_order=0;
- if (buff != buff_ptr)
- {
- buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
- }
- buff_ptr=strmov(buff_ptr,"Using filesort");
- }
- if (distinct & test_all_bits(used_tables,thd->used_tables))
- {
- if (buff != buff_ptr)
- {
- buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
- }
- buff_ptr=strmov(buff_ptr,"Distinct");
- }
- net_store_data(packet,buff,(uint) (buff_ptr - buff));
- if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
- DBUG_VOID_RETURN; /* purecov: inspected */
- // For next iteration
- used_tables|=table->map;
- }
- send_eof(&thd->net);
- DBUG_VOID_RETURN;
- }
- static void describe_info(THD *thd, const char *info)
- {
- List<Item> field_list;
- String *packet= &thd->packet;
- /* Don't log this into the slow query log */
- thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
- field_list.push_back(new Item_empty_string("Comment",80));
- if (send_fields(thd,field_list,1))
- return; /* purecov: inspected */
- packet->length(0);
- net_store_data(packet,info);
- if (!my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
- send_eof(&thd->net);
- }