InSELECT
,ORDER BY
andGROUP BY
loops, the flag is checked after reading a block of rows. If the kill flag is set, the statement is aborted.
Complete, correct and useful answer is more complex though. Here is correct answer, but not very useful. So, kill flag is checked in the following functions related to SELECT statement processing:
make_join_statistics()
best_extension_by_limited_search()
find_best()
sub_select_cache()
evaluate_join_record()
flush_cached_records()
end_write()
end_update()
end_unique_update()
end_write_group()
remove_dup_with_compare()
remove_dup_with_hash_index()
Continue reading if you are interested in the process of getting correct answer and would like to know how to make it also complete and useful eventually.
Let's just use grep to search for thd->killed usage in source code (of version 5.5.30) that implements SELECT and then check functions with these lines:
[openxs@chief mysql-5.5]$ grep -n 'thd->kill' sql/sql_select.cc
3136: DBUG_RETURN(join->thd->killed || get_best_combination(join));
5463: if (thd->killed) // Abort
5602: if (thd->killed)
11604: if (join->thd->killed) // If aborted by user
11807: if (join->thd->killed) // Aborted by user
12052: if (join->thd->killed)
12915: if (join->thd->killed) // Aborted by user
12968: if (join->thd->killed) // Aborted by user
13048: if (join->thd->killed) // Aborted by user
13095: if (join->thd->killed)
14394: if (thd->killed)
14523: if (thd->killed)
Line 3136 is in the make_join_statistics() function, that is, flag is checked in the process of query optimization also:
3122 /* Find an optimal join order of the non-constant tables. */
3123 if (join->const_tables != join->tables)
3126 if (choose_plan(join, all_table_map & ~join->const_table_map))
3127 goto error;
3131 memcpy((uchar*) join->best_positions,(uchar*) join->positions,
3132 sizeof(POSITION)*join->const_tables);
3133 join->best_read=1.0;
3136 DBUG_RETURN(join->thd->killed || get_best_combination(join));
Line 5463 is in the best_extension_by_limited_search() function, so again flag is checked in the process of query optimization:
5451 static bool
5452 best_extension_by_limited_search(JOIN *join,
5453 table_map remaining_tables,
5454 uint idx,
5457 uint search_depth,
5458 uint prune_level)
5460 DBUG_ENTER("best_extension_by_limited_search");
5462 THD *thd= join->thd;
5464 DBUG_RETURN(TRUE);
5466 DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
5473 JOIN_TAB *s;
Line 5602 is in the find_best() function:
5596 static bool
5600 DBUG_ENTER("find_best");
5601 THD *thd= join->thd;
5603 DBUG_RETURN(TRUE);
5606 DBUG_PRINT("best",("read_time: %g record_count: %g",read_time,
...
So, again it is related to the process of finding optimal join order at the query optimization stage. All these cases are not mentioned in the manual.
Line 11604 is finally related to query execution (reading rows). It is in the sub_select_cache() function:
11592 enum_nested_loop_state
11593 sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
11595 enum_nested_loop_state rc;
11599 rc= flush_cached_records(join,join_tab,FALSE);
11600 if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS)
11601 rc= sub_select(join,join_tab,end_of_records);
11604 if (join->thd->killed) // If aborted by user
11606 join->thd->send_kill_message();
11607 return NESTED_LOOP_KILLED; /* purecov: inspected */
...
Line 11807 is at the beginning of the evaluate_join_record() function:
11794 static enum_nested_loop_state
11798 bool not_used_in_distinct=join_tab->not_used_in_distinct;
11799 ha_rows found_records=join->found_records;
11800 COND *select_cond= join_tab->select_cond;
11801 bool select_cond_result= TRUE;
11803 if (error > 0 || (join->thd->is_error())) // Fatal error
11804 return NESTED_LOOP_ERROR;
11806 return NESTED_LOOP_NO_MORE_ROWS;
11807 if (join->thd->killed) // Aborted by user
11809 join->thd->send_kill_message();
11810 return NESTED_LOOP_KILLED; /* purecov: inspected */
Line 12052 is in the records reading loop (finally) in the flush_cached_records() function (that you see called above on line 11599 in sub_select_cache()):
12036 /* read through all records */
12039 reset_cache_write(&join_tab->cache);
12040 return error < 0 ? NESTED_LOOP_NO_MORE_ROWS: NESTED_LOOP_ERROR;
12049 info= &join_tab->read_record;
12052 if (join->thd->killed)
12054 join->thd->send_kill_message();
12055 return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */
...
12093 } while (!(error=info->read_record(info)));
Line 12915 is in the end_write() function:
12908 static enum_nested_loop_state
12909 end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
12913 DBUG_ENTER("end_write");
12915 if (join->thd->killed) // Aborted by user
12917 join->thd->send_kill_message();
12918 DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
Line 12968 is in the end_update():
12957 static enum_nested_loop_state
12958 end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
12962 ORDER *group;
12963 int error;
12964 DBUG_ENTER("end_update");
12967 DBUG_RETURN(NESTED_LOOP_OK);
12968 if (join->thd->killed) // Aborted by user
12970 join->thd->send_kill_message();
12971 DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
Line 13048 is in the end_unique_update() (it's getting boring, isn't it, a lot of similar looking code is similar named functions):
13038 static enum_nested_loop_state
13039 end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
13043 int error;
13044 DBUG_ENTER("end_unique_update");
13047 DBUG_RETURN(NESTED_LOOP_OK);
13048 if (join->thd->killed) // Aborted by user
13050 join->thd->send_kill_message();
13051 DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
Line 13095 is from something similar also, end_write_group() function (note that comment is placed differently though):
13087 static enum_nested_loop_state
13088 end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
13093 DBUG_ENTER("end_write_group");
13095 if (join->thd->killed)
13097 join->thd->send_kill_message();
13098 DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
We are almost done. Line 14394 is finally closely related to something manual described, "GROUP BY loop" in the remove_dup_with_compare() function:
14377 static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
14383 int error;
14385 DBUG_ENTER("remove_dup_with_compare");
14388 new_record=(char*) table->record[1]+offset;
14390 file->ha_rnd_init(1);
14391 error=file->rnd_next(record);
14398 goto err;
...
14446 file->position(record); // Remember position
14452 error=file->restart_rnd_next(record,file->ref);
14455 file->extra(HA_EXTRA_NO_CACHE);
14456 DBUG_RETURN(0);
14457 err:
14458 file->extra(HA_EXTRA_NO_CACHE);
14460 file->print_error(error,MYF(0));
14461 DBUG_RETURN(1);
Finally, line 14523 is in the loop in the remove_dup_with_hash_index() function:
14518 file->ha_rnd_init(1);
14522 uchar *org_key_pos;
14527 goto err;
...
That's all great, but how these functions are related to each other? MySQL Internals manual will help us to get the general picture of how SELECT is processed:
handle_select()
mysql_select()
JOIN::prepare()
setup_fields()
JOIN::optimize() /* optimizer is from here ... */
optimize_cond()
opt_sum_query()
make_join_statistics()
get_quick_record_count()
choose_plan()
/* Find the best way to access tables */
/* as specified by the user. */
optimize_straight_join()
best_access_path()
/* Find a (sub-)optimal plan among all or subset */
/* of all possible query plans where the user */
/* controls the exhaustiveness of the search. */
greedy_search()
best_extension_by_limited_search()
best_access_path()
/* Perform an exhaustive search for an optimal plan */
find_best()
make_join_select() /* ... to here */
JOIN::exec()
In the diagram above I've highlighted optimizer part with different background and functions where kill flag is checked with bold.
It would be nice to see/describe JOIN::exec() in a way similar to above. I plan to do this in the second part of this post. To be continued...
for years i wanted to get rid of code like this:
ReplyDeleteif (thd->killed)
....
and make it into a function. that would be easier to have ability to inject fake kills into the server code (been many bugs in the passed due to wrong kill handling).
I see in some places we just immediately return when thd->killed is set, while in others we go to some error label at the end of the same function to do cleanup. Not sure if simple single function may help.
ReplyDelete