Commit 4106dfe8 authored by Sergei Petrunia's avatar Sergei Petrunia

Merge branch 'bb-10.1-explain-analyze' into 10.1

parents ec68494b 77e16ce7
...@@ -7,12 +7,15 @@ EXPLAIN ...@@ -7,12 +7,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "t0", "table_name": "t0",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 10, "rows": 10,
"r_rows": 10, "r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 30, "r_filtered": 30,
"attached_condition": "(t0.a < 3)" "attached_condition": "(t0.a < 3)"
...@@ -32,12 +35,15 @@ EXPLAIN ...@@ -32,12 +35,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "t0", "table_name": "t0",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 10, "rows": 10,
"r_rows": 10, "r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 0, "r_filtered": 0,
"attached_condition": "((t0.a > 9) and (t0.a is not null))" "attached_condition": "((t0.a > 9) and (t0.a is not null))"
...@@ -69,12 +75,15 @@ EXPLAIN ...@@ -69,12 +75,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "t0", "table_name": "t0",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 10, "rows": 10,
"r_rows": 10, "r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 100, "r_filtered": 100,
"attached_condition": "(t0.a is not null)" "attached_condition": "(t0.a is not null)"
...@@ -90,6 +99,7 @@ EXPLAIN ...@@ -90,6 +99,7 @@ EXPLAIN
"r_loops": 10, "r_loops": 10,
"rows": 1, "rows": 1,
"r_rows": 1, "r_rows": 1,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 40, "r_filtered": 40,
"attached_condition": "(t1.b < 4)" "attached_condition": "(t1.b < 4)"
...@@ -107,12 +117,15 @@ EXPLAIN ...@@ -107,12 +117,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "tbl1", "table_name": "tbl1",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 100, "rows": 100,
"r_rows": 100, "r_rows": 100,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 20, "r_filtered": 20,
"attached_condition": "(tbl1.b < 20)" "attached_condition": "(tbl1.b < 20)"
...@@ -124,6 +137,7 @@ EXPLAIN ...@@ -124,6 +137,7 @@ EXPLAIN
"r_loops": 1, "r_loops": 1,
"rows": 100, "rows": 100,
"r_rows": 100, "r_rows": 100,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 60, "r_filtered": 60,
"attached_condition": "(tbl2.b < 60)" "attached_condition": "(tbl2.b < 60)"
...@@ -140,12 +154,15 @@ EXPLAIN ...@@ -140,12 +154,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "tbl1", "table_name": "tbl1",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 100, "rows": 100,
"r_rows": 100, "r_rows": 100,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 20, "r_filtered": 20,
"attached_condition": "(tbl1.b < 20)" "attached_condition": "(tbl1.b < 20)"
...@@ -157,6 +174,7 @@ EXPLAIN ...@@ -157,6 +174,7 @@ EXPLAIN
"r_loops": 1, "r_loops": 1,
"rows": 100, "rows": 100,
"r_rows": 100, "r_rows": 100,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 60, "r_filtered": 60,
"attached_condition": "(tbl2.b < 60)" "attached_condition": "(tbl2.b < 60)"
...@@ -182,12 +200,15 @@ EXPLAIN ...@@ -182,12 +200,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "t1", "table_name": "t1",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 10, "rows": 10,
"r_rows": 10, "r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 100, "r_filtered": 100,
"attached_condition": "(t1.a is not null)" "attached_condition": "(t1.a is not null)"
...@@ -203,6 +224,7 @@ EXPLAIN ...@@ -203,6 +224,7 @@ EXPLAIN
"r_loops": 10, "r_loops": 10,
"rows": 2, "rows": 2,
"r_rows": 0.2, "r_rows": 0.2,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 100, "r_filtered": 100,
"using_index": true "using_index": true
...@@ -226,12 +248,15 @@ EXPLAIN ...@@ -226,12 +248,15 @@ EXPLAIN
{ {
"query_block": { "query_block": {
"select_id": 1, "select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": { "table": {
"table_name": "t1", "table_name": "t1",
"access_type": "ALL", "access_type": "ALL",
"r_loops": 1, "r_loops": 1,
"rows": 10, "rows": 10,
"r_rows": 10, "r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100, "filtered": 100,
"r_filtered": 50, "r_filtered": 50,
"attached_condition": "(test.t1.a < 5)" "attached_condition": "(test.t1.a < 5)"
......
...@@ -9,28 +9,34 @@ create table t0 (a int); ...@@ -9,28 +9,34 @@ create table t0 (a int);
INSERT INTO t0 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); INSERT INTO t0 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
--echo # r_filtered=30%, because 3 rows match: 0,1,2 --echo # r_filtered=30%, because 3 rows match: 0,1,2
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json select * from t0 where a<3; analyze format=json select * from t0 where a<3;
create table t1 (a int, b int, c int, key(a)); create table t1 (a int, b int, c int, key(a));
insert into t1 select A.a*10 + B.a, A.a*10 + B.a, A.a*10 + B.a from t0 A, t0 B; insert into t1 select A.a*10 + B.a, A.a*10 + B.a, A.a*10 + B.a from t0 A, t0 B;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze analyze
select * from t0, t1 where t1.a=t0.a and t0.a > 9; select * from t0, t1 where t1.a=t0.a and t0.a > 9;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json analyze format=json
select * from t0, t1 where t1.a=t0.a and t0.a > 9; select * from t0, t1 where t1.a=t0.a and t0.a > 9;
analyze analyze
select * from t0, t1 where t1.a=t0.a and t1.b<4; select * from t0, t1 where t1.a=t0.a and t1.b<4;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json analyze format=json
select * from t0, t1 where t1.a=t0.a and t1.b<4; select * from t0, t1 where t1.a=t0.a and t1.b<4;
analyze analyze
select * from t1 tbl1, t1 tbl2 where tbl1.b<2 and tbl2.b>5; select * from t1 tbl1, t1 tbl2 where tbl1.b<2 and tbl2.b>5;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json analyze format=json
select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60; select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json analyze format=json
select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60 and tbl1.c > tbl2.c; select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60 and tbl1.c > tbl2.c;
...@@ -47,6 +53,7 @@ insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); ...@@ -47,6 +53,7 @@ insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int, key(a)); create table t2 (a int, key(a));
insert into t2 values (0),(1); insert into t2 values (0),(1);
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json select * from t1 straight_join t2 force index(a) where t2.a=t1.a; analyze format=json select * from t1 straight_join t2 force index(a) where t2.a=t1.a;
drop table t1,t2; drop table t1,t2;
...@@ -62,6 +69,7 @@ select database(); ...@@ -62,6 +69,7 @@ select database();
connect (con1,localhost,root,,*NO-ONE*); connect (con1,localhost,root,,*NO-ONE*);
connection con1; connection con1;
select database(); select database();
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json select * from test.t1 where t1.a<5; analyze format=json select * from test.t1 where t1.a<5;
disconnect con1; disconnect con1;
connection default; connection default;
......
...@@ -105,6 +105,7 @@ SET (SQL_SOURCE ...@@ -105,6 +105,7 @@ SET (SQL_SOURCE
# added in MariaDB: # added in MariaDB:
sql_explain.h sql_explain.cc sql_explain.h sql_explain.cc
sql_analyze_stmt.h
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
create_options.cc multi_range_read.cc create_options.cc multi_range_read.cc
opt_index_cond_pushdown.cc opt_subselect.cc opt_index_cond_pushdown.cc opt_subselect.cc
......
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
#include "debug_sync.h" // DEBUG_SYNC #include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h" #include "sql_audit.h"
#include "sql_analyze_stmt.h" // tracker in TABLE_IO_WAIT
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h" #include "ha_partition.h"
#endif #endif
...@@ -54,6 +56,17 @@ ...@@ -54,6 +56,17 @@
#include "wsrep_mysqld.h" #include "wsrep_mysqld.h"
#include "wsrep.h" #include "wsrep.h"
#define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \
{ \
if (unlikely(tracker)) \
tracker->start_tracking(); \
\
MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD); \
\
if (unlikely(tracker)) \
tracker->stop_tracking(); \
}
/* /*
While we have legacy_db_type, we have this array to While we have legacy_db_type, we have this array to
check for dups and to find handlerton from legacy_db_type. check for dups and to find handlerton from legacy_db_type.
...@@ -2566,12 +2579,38 @@ int handler::ha_close(void) ...@@ -2566,12 +2579,38 @@ int handler::ha_close(void)
status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read); status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read);
PSI_CALL_close_table(m_psi); PSI_CALL_close_table(m_psi);
m_psi= NULL; /* instrumentation handle, invalid after close_table() */ m_psi= NULL; /* instrumentation handle, invalid after close_table() */
/* Detach from ANALYZE tracker */
tracker= NULL;
DBUG_ASSERT(m_lock_type == F_UNLCK); DBUG_ASSERT(m_lock_type == F_UNLCK);
DBUG_ASSERT(inited == NONE); DBUG_ASSERT(inited == NONE);
DBUG_RETURN(close()); DBUG_RETURN(close());
} }
inline int handler::ha_write_tmp_row(uchar *buf)
{
int error;
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_write_count);
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
return error;
}
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
int error;
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_update_count);
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
int handler::ha_rnd_next(uchar *buf) int handler::ha_rnd_next(uchar *buf)
{ {
int result; int result;
...@@ -2580,7 +2619,7 @@ int handler::ha_rnd_next(uchar *buf) ...@@ -2580,7 +2619,7 @@ int handler::ha_rnd_next(uchar *buf)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited == RND); DBUG_ASSERT(inited == RND);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
{ result= rnd_next(buf); }) { result= rnd_next(buf); })
if (!result) if (!result)
{ {
...@@ -2605,7 +2644,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos) ...@@ -2605,7 +2644,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos)
/* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */ /* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */
/* DBUG_ASSERT(inited == RND); */ /* DBUG_ASSERT(inited == RND); */
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
{ result= rnd_pos(buf, pos); }) { result= rnd_pos(buf, pos); })
increment_statistics(&SSV::ha_read_rnd_count); increment_statistics(&SSV::ha_read_rnd_count);
if (!result) if (!result)
...@@ -2624,7 +2663,7 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key, ...@@ -2624,7 +2663,7 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key,
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_read_map(buf, key, keypart_map, find_flag); }) { result= index_read_map(buf, key, keypart_map, find_flag); })
increment_statistics(&SSV::ha_read_key_count); increment_statistics(&SSV::ha_read_key_count);
if (!result) if (!result)
...@@ -2648,7 +2687,7 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key, ...@@ -2648,7 +2687,7 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(end_range == NULL); DBUG_ASSERT(end_range == NULL);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, index, 0,
{ result= index_read_idx_map(buf, index, key, keypart_map, find_flag); }) { result= index_read_idx_map(buf, index, key, keypart_map, find_flag); })
increment_statistics(&SSV::ha_read_key_count); increment_statistics(&SSV::ha_read_key_count);
if (!result) if (!result)
...@@ -2668,7 +2707,7 @@ int handler::ha_index_next(uchar * buf) ...@@ -2668,7 +2707,7 @@ int handler::ha_index_next(uchar * buf)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_next(buf); }) { result= index_next(buf); })
increment_statistics(&SSV::ha_read_next_count); increment_statistics(&SSV::ha_read_next_count);
if (!result) if (!result)
...@@ -2685,7 +2724,7 @@ int handler::ha_index_prev(uchar * buf) ...@@ -2685,7 +2724,7 @@ int handler::ha_index_prev(uchar * buf)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_prev(buf); }) { result= index_prev(buf); })
increment_statistics(&SSV::ha_read_prev_count); increment_statistics(&SSV::ha_read_prev_count);
if (!result) if (!result)
...@@ -2701,7 +2740,7 @@ int handler::ha_index_first(uchar * buf) ...@@ -2701,7 +2740,7 @@ int handler::ha_index_first(uchar * buf)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_first(buf); }) { result= index_first(buf); })
increment_statistics(&SSV::ha_read_first_count); increment_statistics(&SSV::ha_read_first_count);
if (!result) if (!result)
...@@ -2717,7 +2756,7 @@ int handler::ha_index_last(uchar * buf) ...@@ -2717,7 +2756,7 @@ int handler::ha_index_last(uchar * buf)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_last(buf); }) { result= index_last(buf); })
increment_statistics(&SSV::ha_read_last_count); increment_statistics(&SSV::ha_read_last_count);
if (!result) if (!result)
...@@ -2733,7 +2772,7 @@ int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen) ...@@ -2733,7 +2772,7 @@ int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
m_lock_type != F_UNLCK); m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX); DBUG_ASSERT(inited==INDEX);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
{ result= index_next_same(buf, key, keylen); }) { result= index_next_same(buf, key, keylen); })
increment_statistics(&SSV::ha_read_next_count); increment_statistics(&SSV::ha_read_next_count);
if (!result) if (!result)
...@@ -5857,7 +5896,7 @@ int handler::ha_write_row(uchar *buf) ...@@ -5857,7 +5896,7 @@ int handler::ha_write_row(uchar *buf)
mark_trx_read_write(); mark_trx_read_write();
increment_statistics(&SSV::ha_write_count); increment_statistics(&SSV::ha_write_count);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
{ error= write_row(buf); }) { error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error); MYSQL_INSERT_ROW_DONE(error);
...@@ -5890,7 +5929,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) ...@@ -5890,7 +5929,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
mark_trx_read_write(); mark_trx_read_write();
increment_statistics(&SSV::ha_update_count); increment_statistics(&SSV::ha_update_count);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
{ error= update_row(old_data, new_data);}) { error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error); MYSQL_UPDATE_ROW_DONE(error);
...@@ -5918,7 +5957,7 @@ int handler::ha_delete_row(const uchar *buf) ...@@ -5918,7 +5957,7 @@ int handler::ha_delete_row(const uchar *buf)
mark_trx_read_write(); mark_trx_read_write();
increment_statistics(&SSV::ha_delete_count); increment_statistics(&SSV::ha_delete_count);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_DELETE_ROW, active_index, 0, TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
{ error= delete_row(buf);}) { error= delete_row(buf);})
MYSQL_DELETE_ROW_DONE(error); MYSQL_DELETE_ROW_DONE(error);
if (unlikely(error)) if (unlikely(error))
......
...@@ -1571,6 +1571,7 @@ typedef struct { ...@@ -1571,6 +1571,7 @@ typedef struct {
#define UNDEF_NODEGROUP 65535 #define UNDEF_NODEGROUP 65535
class Item; class Item;
class Exec_time_tracker;
struct st_table_log_memory_entry; struct st_table_log_memory_entry;
class partition_info; class partition_info;
...@@ -2594,6 +2595,9 @@ public: ...@@ -2594,6 +2595,9 @@ public:
ulonglong rows_changed; ulonglong rows_changed;
/* One bigger than needed to avoid to test if key == MAX_KEY */ /* One bigger than needed to avoid to test if key == MAX_KEY */
ulonglong index_rows_read[MAX_KEY+1]; ulonglong index_rows_read[MAX_KEY+1];
/* ANALYZE time tracker, if present */
Exec_time_tracker *tracker;
Item *pushed_idx_cond; Item *pushed_idx_cond;
uint pushed_idx_cond_keyno; /* The index which the above condition is for */ uint pushed_idx_cond_keyno; /* The index which the above condition is for */
...@@ -2648,6 +2652,7 @@ public: ...@@ -2648,6 +2652,7 @@ public:
ft_handler(0), inited(NONE), ft_handler(0), inited(NONE),
implicit_emptied(0), implicit_emptied(0),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
tracker(NULL),
pushed_idx_cond(NULL), pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY), pushed_idx_cond_keyno(MAX_KEY),
auto_inc_intervals_count(0), auto_inc_intervals_count(0),
......
...@@ -314,6 +314,9 @@ arg_cmp_func Arg_comparator::comparator_matrix[6][2] = ...@@ -314,6 +314,9 @@ arg_cmp_func Arg_comparator::comparator_matrix[6][2] =
{&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}, {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal},
{&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}}; {&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}};
/* Timer info to be used by the SQL layer */
MY_TIMER_INFO sys_timer_info;
/* static variables */ /* static variables */
#ifdef HAVE_PSI_INTERFACE #ifdef HAVE_PSI_INTERFACE
...@@ -5461,6 +5464,7 @@ int mysqld_main(int argc, char **argv) ...@@ -5461,6 +5464,7 @@ int mysqld_main(int argc, char **argv)
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
pfs_param.m_pfs_instrument= const_cast<char*>(""); pfs_param.m_pfs_instrument= const_cast<char*>("");
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
my_timer_init(&sys_timer_info);
int ho_error __attribute__((unused))= handle_early_options(); int ho_error __attribute__((unused))= handle_early_options();
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "sql_cmd.h" #include "sql_cmd.h"
#include <my_rnd.h> #include <my_rnd.h>
#include "my_pthread.h" #include "my_pthread.h"
#include "my_rdtsc.h"
class THD; class THD;
struct handlerton; struct handlerton;
...@@ -60,6 +61,8 @@ typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */ ...@@ -60,6 +61,8 @@ typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
#define OPT_SESSION SHOW_OPT_SESSION #define OPT_SESSION SHOW_OPT_SESSION
#define OPT_GLOBAL SHOW_OPT_GLOBAL #define OPT_GLOBAL SHOW_OPT_GLOBAL
extern MY_TIMER_INFO sys_timer_info;
/* /*
Values for --slave-parallel-mode Values for --slave-parallel-mode
Must match order in slave_parallel_mode_typelib in sys_vars.cc. Must match order in slave_parallel_mode_typelib in sys_vars.cc.
......
/*
Copyright (c) 2015 MariaDB Corporation Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
A class for tracking time it takes to do a certain action
*/
class Exec_time_tracker
{
ulonglong count;
ulonglong cycles;
ulonglong last_start;
public:
Exec_time_tracker() : count(0), cycles(0) {}
// interface for collecting time
void start_tracking()
{
last_start= my_timer_cycles();
}
void stop_tracking()
{
ulonglong last_end= my_timer_cycles();
count++;
cycles += last_end - last_start;
}
// interface for getting the time
ulonglong get_loops() { return count; }
double get_time_ms()
{
// convert 'cycles' to milliseconds.
return 1000 * ((double)cycles) / sys_timer_info.cycles.frequency;
}
};
...@@ -5271,28 +5271,6 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key) ...@@ -5271,28 +5271,6 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
return error; return error;
} }
inline int handler::ha_write_tmp_row(uchar *buf)
{
int error;
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_write_count);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
return error;
}
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
int error;
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_update_count);
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
extern pthread_attr_t *get_connection_attrib(void); extern pthread_attr_t *get_connection_attrib(void);
/** /**
......
...@@ -732,7 +732,12 @@ void Explain_select::print_explain_json(Explain_query *query, ...@@ -732,7 +732,12 @@ void Explain_select::print_explain_json(Explain_query *query,
*/ */
writer->add_member("query_block").start_object(); writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id); writer->add_member("select_id").add_ll(select_id);
if (is_analyze && time_tracker.get_loops())
{
writer->add_member("r_loops").add_ll(time_tracker.get_loops());
writer->add_member("r_total_time_ms").add_double(time_tracker.get_time_ms());
}
if (exec_const_cond) if (exec_const_cond)
{ {
writer->add_member("const_condition"); writer->add_member("const_condition");
...@@ -1289,6 +1294,12 @@ void Explain_table_access::print_explain_json(Explain_query *query, ...@@ -1289,6 +1294,12 @@ void Explain_table_access::print_explain_json(Explain_query *query,
} }
else else
writer->add_null(); writer->add_null();
if (op_tracker.get_loops())
{
writer->add_member("r_total_time_ms").
add_double(op_tracker.get_time_ms());
}
} }
/* `filtered` */ /* `filtered` */
......
...@@ -14,6 +14,44 @@ ...@@ -14,6 +14,44 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "sql_analyze_stmt.h"
/*
== EXPLAIN/ANALYZE architecture ==
=== [SHOW] EXPLAIN data ===
Query optimization produces two data structures:
1. execution data structures themselves (eg. JOINs, JOIN_TAB, etc, etc)
2. Explain data structures.
#2 are self contained set of data structures that has sufficient info to
produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or
ANALYZE [FORMAT=JSON], without accessing the execution data structures.
(the only exception is that Explain data structures keep Item* pointers,
and we require that one might call item->print(QT_EXPLAIN) when printing
FORMAT=JSON output)
=== ANALYZE data ===
EXPLAIN data structures have embedded ANALYZE data structures. These are
objects that are used to track how the parts of query plan were executed:
how many times each part of query plan was invoked, how many rows were
read/returned, etc.
Each execution data structure keeps a direct pointer to its ANALYZE data
structure. It is needed so that execution code can quickly increment the
counters.
(note that this increases the set of data that is frequently accessed
during the execution. What is the impact of this?)
Since ANALYZE/EXPLAIN data structures are separated from execution data
structures, it is easy to have them survive until the end of the query,
where we can return ANALYZE [FORMAT=JSON] output to the user, or print
it into the slow query log.
*/
class String_list: public List<char> class String_list: public List<char>
{ {
...@@ -21,12 +59,16 @@ public: ...@@ -21,12 +59,16 @@ public:
bool append_str(MEM_ROOT *mem_root, const char *str); bool append_str(MEM_ROOT *mem_root, const char *str);
}; };
class Json_writer;
/* /*
A class for collecting read statistics. A class for collecting read statistics.
The idea is that we run several scans. Each scans gets rows, and then filters The idea is that we run several scans. Each scans gets rows, and then filters
some of them out. We count scans, rows, and rows left after filtering. some of them out. We count scans, rows, and rows left after filtering.
(note: at the moment, the class is not actually tied to a physical table.
It can be used to track reading from files, buffers, etc).
*/ */
class Table_access_tracker class Table_access_tracker
...@@ -39,7 +81,6 @@ public: ...@@ -39,7 +81,6 @@ public:
ha_rows r_scans; /* How many scans were ran on this join_tab */ ha_rows r_scans; /* How many scans were ran on this join_tab */
ha_rows r_rows; /* How many rows we've got after that */ ha_rows r_rows; /* How many rows we've got after that */
// ha_rows r_rows_after_table_cond; /* Rows after applying the table condition */
ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */ ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
bool has_scans() { return (r_scans != 0); } bool has_scans() { return (r_scans != 0); }
...@@ -65,6 +106,39 @@ public: ...@@ -65,6 +106,39 @@ public:
inline void on_record_after_where() { r_rows_after_where++; } inline void on_record_after_where() { r_rows_after_where++; }
}; };
#if 0
/*
A class to track operations (currently, row reads) on a PSI_table.
*/
class Table_op_tracker
{
PSI_table *psi_table;
/* Table counter values at start. Sum is in picoseconds */
ulonglong start_sum;
ulonglong start_count;
/* Table counter values at end */
ulonglong end_sum;
ulonglong end_count;
public:
void start_tracking(TABLE *table);
// At the moment, print_json will call end_tracking.
void end_tracking();
// this may print nothing if the table was not tracked.
void print_json(Json_writer *writer);
};
#endif
#define ANALYZE_START_TRACKING(tracker) \
if (tracker) \
{ tracker->start_tracking(); }
#define ANALYZE_STOP_TRACKING(tracker) \
if (tracker) \
{ tracker->stop_tracking(); }
/************************************************************************************** /**************************************************************************************
...@@ -82,8 +156,6 @@ const int FAKE_SELECT_LEX_ID= (int)UINT_MAX; ...@@ -82,8 +156,6 @@ const int FAKE_SELECT_LEX_ID= (int)UINT_MAX;
class Explain_query; class Explain_query;
class Json_writer;
/* /*
A node can be either a SELECT, or a UNION. A node can be either a SELECT, or a UNION.
*/ */
...@@ -231,6 +303,9 @@ public: ...@@ -231,6 +303,9 @@ public:
/* Global join attributes. In tabular form, they are printed on the first row */ /* Global join attributes. In tabular form, they are printed on the first row */
bool using_temporary; bool using_temporary;
bool using_filesort; bool using_filesort;
/* ANALYZE members */
Exec_time_tracker time_tracker;
int print_explain(Explain_query *query, select_result_sink *output, int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags, bool is_analyze); uint8 explain_flags, bool is_analyze);
...@@ -639,7 +714,7 @@ public: ...@@ -639,7 +714,7 @@ public:
// Valid if ET_USING tag is present // Valid if ET_USING tag is present
Explain_quick_select *quick_info; Explain_quick_select *quick_info;
/* Non-NULL values means this tab uses "range checked for each record" */ /* Non-NULL value means this tab uses "range checked for each record" */
Explain_range_checked_fer *range_checked_fer; Explain_range_checked_fer *range_checked_fer;
bool full_scan_on_null_key; bool full_scan_on_null_key;
...@@ -666,6 +741,7 @@ public: ...@@ -666,6 +741,7 @@ public:
/* Tracker for reading the table */ /* Tracker for reading the table */
Table_access_tracker tracker; Table_access_tracker tracker;
Exec_time_tracker op_tracker;
Table_access_tracker jbuf_tracker; Table_access_tracker jbuf_tracker;
int print_explain(select_result_sink *output, uint8 explain_flags, int print_explain(select_result_sink *output, uint8 explain_flags,
...@@ -735,6 +811,7 @@ public: ...@@ -735,6 +811,7 @@ public:
/* ANALYZE members and methods */ /* ANALYZE members and methods */
Table_access_tracker tracker; Table_access_tracker tracker;
//psergey-todo: io-tracker here.
virtual int print_explain(Explain_query *query, select_result_sink *output, virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags, bool is_analyze); uint8 explain_flags, bool is_analyze);
......
...@@ -2390,7 +2390,9 @@ void JOIN::exec() ...@@ -2390,7 +2390,9 @@ void JOIN::exec()
select_lex->select_number)) select_lex->select_number))
dbug_serve_apcs(thd, 1); dbug_serve_apcs(thd, 1);
); );
ANALYZE_START_TRACKING(tracker);
exec_inner(); exec_inner();
ANALYZE_STOP_TRACKING(tracker);
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end", DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
if (dbug_user_var_equals_int(thd, if (dbug_user_var_equals_int(thd,
...@@ -23404,6 +23406,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab ...@@ -23404,6 +23406,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
tab->tracker= &eta->tracker; tab->tracker= &eta->tracker;
tab->jbuf_tracker= &eta->jbuf_tracker; tab->jbuf_tracker= &eta->jbuf_tracker;
tab->table->file->tracker= &eta->op_tracker;
/* id and select_type are kept in Explain_select */ /* id and select_type are kept in Explain_select */
/* table */ /* table */
...@@ -23842,6 +23845,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, ...@@ -23842,6 +23845,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
Explain_select *xpl_sel; Explain_select *xpl_sel;
explain_node= xpl_sel= new (output->mem_root) Explain_select(output->mem_root); explain_node= xpl_sel= new (output->mem_root) Explain_select(output->mem_root);
table_map used_tables=0; table_map used_tables=0;
tracker= &xpl_sel->time_tracker;
join->select_lex->set_explain_type(true); join->select_lex->set_explain_type(true);
xpl_sel->select_id= join->select_lex->select_number; xpl_sel->select_id= join->select_lex->select_number;
......
...@@ -252,6 +252,7 @@ typedef struct st_join_table { ...@@ -252,6 +252,7 @@ typedef struct st_join_table {
enum explain_extra_tag info; enum explain_extra_tag info;
Table_access_tracker *tracker; Table_access_tracker *tracker;
Table_access_tracker *jbuf_tracker; Table_access_tracker *jbuf_tracker;
/* /*
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
...@@ -1260,6 +1261,8 @@ public: ...@@ -1260,6 +1261,8 @@ public:
OPTIMIZATION_DONE=2}; OPTIMIZATION_DONE=2};
bool optimized; ///< flag to avoid double optimization in EXPLAIN bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls bool initialized; ///< flag to avoid double init_execution calls
Exec_time_tracker *tracker;
enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan; enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
...@@ -1354,6 +1357,8 @@ public: ...@@ -1354,6 +1357,8 @@ public:
no_rows_in_result_called= 0; no_rows_in_result_called= 0;
positions= best_positions= 0; positions= best_positions= 0;
tracker= NULL;
all_fields= fields_arg; all_fields= fields_arg;
if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ if (&fields_list != &fields_arg) /* Avoid valgrind-warning */
fields_list= fields_arg; fields_list= fields_arg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment