diff options
-rw-r--r-- | mysql-test/suite/plugins/r/qc_info.result | 15 | ||||
-rw-r--r-- | mysql-test/suite/plugins/r/qc_info_priv.result | 23 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/qc_info.test | 8 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/qc_info_init.inc | 12 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/qc_info_init.opt | 2 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/qc_info_priv.test | 15 | ||||
-rw-r--r-- | mysql-test/t/mysqld--help.test | 7 | ||||
-rw-r--r-- | plugin/qc_info/CMakeLists.txt | 4 | ||||
-rw-r--r-- | plugin/qc_info/qc_info.cc | 213 | ||||
-rw-r--r-- | sql/sql_cache.cc | 12 | ||||
-rw-r--r-- | sql/sql_cache.h | 12 |
11 files changed, 309 insertions, 14 deletions
diff --git a/mysql-test/suite/plugins/r/qc_info.result b/mysql-test/suite/plugins/r/qc_info.result new file mode 100644 index 00000000000..3e967d55f43 --- /dev/null +++ b/mysql-test/suite/plugins/r/qc_info.result @@ -0,0 +1,15 @@ +set global query_cache_size=1355776; +create table t1 (a int not null); +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +select statement_schema, statement_text, result_blocks_count, result_blocks_size from information_schema.query_cache_info; +statement_schema statement_text result_blocks_count result_blocks_size +test select * from t1 1 512 +drop table t1; +select statement_schema, statement_text, result_blocks_count, result_blocks_size from information_schema.query_cache_info; +statement_schema statement_text result_blocks_count result_blocks_size +set global query_cache_size= default; diff --git a/mysql-test/suite/plugins/r/qc_info_priv.result b/mysql-test/suite/plugins/r/qc_info_priv.result new file mode 100644 index 00000000000..c723eca1ec0 --- /dev/null +++ b/mysql-test/suite/plugins/r/qc_info_priv.result @@ -0,0 +1,23 @@ +set global query_cache_size=1355776; +create table t1 (a int not null); +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +select statement_schema, statement_text, result_blocks_count, result_blocks_size from information_schema.query_cache_info; +statement_schema statement_text result_blocks_count result_blocks_size +test select * from t1 1 512 +create user mysqltest; +select a from t1; +a +1 +2 +3 +select count(*) from information_schema.query_cache_info; +count(*) +0 +drop user mysqltest; +drop table t1; +set global query_cache_size= default; diff --git a/mysql-test/suite/plugins/t/qc_info.test b/mysql-test/suite/plugins/t/qc_info.test new file mode 100644 index 00000000000..fc6c9d5af3a --- /dev/null +++ b/mysql-test/suite/plugins/t/qc_info.test @@ -0,0 +1,8 @@ +--source qc_info_init.inc + +drop table t1; +# the query was invalidated +select statement_schema, statement_text, result_blocks_count, result_blocks_size from information_schema.query_cache_info; + +set global query_cache_size= default; + diff --git a/mysql-test/suite/plugins/t/qc_info_init.inc b/mysql-test/suite/plugins/t/qc_info_init.inc new file mode 100644 index 00000000000..9422bd7f141 --- /dev/null +++ b/mysql-test/suite/plugins/t/qc_info_init.inc @@ -0,0 +1,12 @@ +if (`select count(*) = 0 from information_schema.plugins where plugin_name = 'query_cache_info' and plugin_status='active'`) +{ + --skip QUERY_CACHE_INFO plugin is not active +} + +set global query_cache_size=1355776; + +create table t1 (a int not null); +insert into t1 values (1),(2),(3); +select * from t1; +select statement_schema, statement_text, result_blocks_count, result_blocks_size from information_schema.query_cache_info; + diff --git a/mysql-test/suite/plugins/t/qc_info_init.opt b/mysql-test/suite/plugins/t/qc_info_init.opt new file mode 100644 index 00000000000..663de4da7d7 --- /dev/null +++ b/mysql-test/suite/plugins/t/qc_info_init.opt @@ -0,0 +1,2 @@ +--loose-query_cache_info +--plugin-load=$QUERY_CACHE_INFO_SO diff --git a/mysql-test/suite/plugins/t/qc_info_priv.test b/mysql-test/suite/plugins/t/qc_info_priv.test new file mode 100644 index 00000000000..31c394107c1 --- /dev/null +++ b/mysql-test/suite/plugins/t/qc_info_priv.test @@ -0,0 +1,15 @@ +--source include/not_embedded.inc +--source qc_info_init.inc + +# try an unprivileged user +create user mysqltest; +connect (conn1,localhost,mysqltest,,); +connection conn1; +select a from t1; +select count(*) from information_schema.query_cache_info; +connection default; +drop user mysqltest; +drop table t1; + +set global query_cache_size= default; + diff --git a/mysql-test/t/mysqld--help.test b/mysql-test/t/mysqld--help.test index 806e6f7f8d1..4b5572c2bbe 100644 --- a/mysql-test/t/mysqld--help.test +++ b/mysql-test/t/mysqld--help.test @@ -23,8 +23,11 @@ perl; datadir slave-load-tmpdir tmpdir socket/; # Plugins which may or may not be there: - @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster feedback debug temp-pool ssl des-key-file - xtradb thread-concurrency super-large-pages mutex-deadlock-detector null-audit maria aria pbxt oqgraph sphinx thread-handling thread-pool/; + @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster + feedback debug temp-pool ssl des-key-file + xtradb thread-concurrency super-large-pages + mutex-deadlock-detector null-audit maria aria pbxt oqgraph + sphinx thread-handling thread-pool query-cache-info/; # And substitute the content some environment variables with their # names: diff --git a/plugin/qc_info/CMakeLists.txt b/plugin/qc_info/CMakeLists.txt new file mode 100644 index 00000000000..f9c58f77466 --- /dev/null +++ b/plugin/qc_info/CMakeLists.txt @@ -0,0 +1,4 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/extra/yassl/include) + +MYSQL_ADD_PLUGIN(QUERY_CACHE_INFO qc_info.cc) diff --git a/plugin/qc_info/qc_info.cc b/plugin/qc_info/qc_info.cc new file mode 100644 index 00000000000..e20b4dac5ea --- /dev/null +++ b/plugin/qc_info/qc_info.cc @@ -0,0 +1,213 @@ +/* + Copyright (c) 2008, Roland Bouman + http://rpbouman.blogspot.com/ + roland.bouman@gmail.com + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Roland Bouman nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * TODO: report query cache flags + */ +#ifndef MYSQL_SERVER +#define MYSQL_SERVER +#endif + +#include <sql_cache.h> +#include <sql_parse.h> // check_global_access +#include <sql_acl.h> // PROCESS_ACL +#include <sql_class.h> // THD +#include <table.h> // ST_SCHEMA_TABLE +#include <mysql/plugin.h> + +class Accessible_Query_Cache : public Query_cache { +public: + HASH *get_queries() + { + return &this->queries; + } +} *qc; + +bool schema_table_store_record(THD *thd, TABLE *table); + +#define MAX_STATEMENT_TEXT_LENGTH 32767 +#define COLUMN_STATEMENT_SCHEMA 0 +#define COLUMN_STATEMENT_TEXT 1 +#define COLUMN_RESULT_BLOCKS_COUNT 2 +#define COLUMN_RESULT_BLOCKS_SIZE 3 +#define COLUMN_RESULT_BLOCKS_SIZE_USED 4 + +/* ST_FIELD_INFO is defined in table.h */ +static ST_FIELD_INFO qc_info_fields[]= +{ + {"STATEMENT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"STATEMENT_TEXT", MAX_STATEMENT_TEXT_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0}, + {"RESULT_BLOCKS_COUNT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, 0}, + {"RESULT_BLOCKS_SIZE", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {"RESULT_BLOCKS_SIZE_USED", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} +}; + +static int qc_info_fill_table(THD *thd, TABLE_LIST *tables, + COND *cond) +{ + int status= 1; + CHARSET_INFO *scs= system_charset_info; + TABLE *table= tables->table; + HASH *queries = qc->get_queries(); + + /* one must have PROCESS privilege to see others' queries */ + if (check_global_access(thd, PROCESS_ACL, true)) + return 0; + + if (qc->try_lock(thd)) + return status; + + /* loop through all queries in the query cache */ + for (uint i= 0; i < queries->records; i++) + { + const uchar *query_cache_block_raw; + Query_cache_block* query_cache_block; + Query_cache_query* query_cache_query; + uint result_blocks_count; + ulonglong result_blocks_size; + ulonglong result_blocks_size_used; + Query_cache_block *first_result_block; + Query_cache_block *result_block; + const char *statement_text; + size_t statement_text_length; + const char *key, *db; + size_t key_length, db_length; + + query_cache_block_raw = my_hash_element(queries, i); + query_cache_block = (Query_cache_block*)query_cache_block_raw; + if (query_cache_block->type != Query_cache_block::QUERY) + continue; + + query_cache_query = query_cache_block->query(); + + /* Get the actual SQL statement for this query cache query */ + statement_text = (const char*)query_cache_query->query(); + statement_text_length = strlen(statement_text); + /* We truncate SQL statements up to MAX_STATEMENT_TEXT_LENGTH in our I_S table */ + table->field[COLUMN_STATEMENT_TEXT]->store((char*)statement_text, + min(statement_text_length, MAX_STATEMENT_TEXT_LENGTH), scs); + + /* get the entire key that identifies this query cache query */ + key = (const char*)query_cache_query_get_key(query_cache_block_raw, + &key_length, 0); + /* The database against which the statement is executed is part of the + query cache query key + */ + compile_time_assert(QUERY_CACHE_DB_LENGTH_SIZE == 2); + db= key + statement_text_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE; + db_length= uint2korr(db - QUERY_CACHE_DB_LENGTH_SIZE); + + table->field[COLUMN_STATEMENT_SCHEMA]->store(db, db_length, scs); + + /* If we have result blocks, process them */ + first_result_block= query_cache_query->result(); + if(first_result_block) + { + /* initialize so we can loop over the result blocks*/ + result_block= first_result_block; + result_blocks_count = 1; + result_blocks_size = result_block->length; + result_blocks_size_used = result_block->used; + + /* loop over the result blocks*/ + while((result_block= result_block->next)!=first_result_block) + { + /* calculate total number of result blocks */ + result_blocks_count++; + /* calculate total size of result blocks */ + result_blocks_size += result_block->length; + /* calculate total of used size of result blocks */ + result_blocks_size_used += result_block->used; + } + } + else + { + result_blocks_count = 0; + result_blocks_size = 0; + result_blocks_size_used = 0; + } + table->field[COLUMN_RESULT_BLOCKS_COUNT]->store(result_blocks_count, 0); + table->field[COLUMN_RESULT_BLOCKS_SIZE]->store(result_blocks_size, 0); + table->field[COLUMN_RESULT_BLOCKS_SIZE_USED]->store(result_blocks_size_used, 0); + + if (schema_table_store_record(thd, table)) + goto cleanup; + } + status = 0; + +cleanup: + qc->unlock(); + return status; +} + +static int qc_info_plugin_init(void *p) +{ + ST_SCHEMA_TABLE *schema= (ST_SCHEMA_TABLE *)p; + + schema->fields_info= qc_info_fields; + schema->fill_table= qc_info_fill_table; + +#ifdef _WIN32 + qc = (Accessible_Query_Cache *) + GetProcAddress(GetModuleHandle(NULL), "?query_cache@@3VQuery_cache@@A"); +#else + qc = (Accessible_Query_Cache *)&query_cache; +#endif + + return qc == 0; +} + + +static struct st_mysql_information_schema qc_info_plugin= +{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; + +/* + Plugin library descriptor +*/ + +maria_declare_plugin(query_cache_info) +{ + MYSQL_INFORMATION_SCHEMA_PLUGIN, + &qc_info_plugin, + "QUERY_CACHE_INFO", + "Roland Bouman", + "Lists all queries in the query cache.", + PLUGIN_LICENSE_BSD, + qc_info_plugin_init, /* Plugin Init */ + 0, /* Plugin Deinit */ + 0x0100, /* version, hex */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* version as a string */ + MariaDB_PLUGIN_MATURITY_ALPHA +} +maria_declare_plugin_end; + diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 016973ab51a..ab08b496dcd 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -828,18 +828,18 @@ void Query_cache_block::destroy() DBUG_VOID_RETURN; } -inline uint Query_cache_block::headers_len() +uint Query_cache_block::headers_len() { return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) + ALIGN_SIZE(sizeof(Query_cache_block))); } -inline uchar* Query_cache_block::data(void) +uchar* Query_cache_block::data(void) { return (uchar*)( ((uchar*)this) + headers_len() ); } -inline Query_cache_query * Query_cache_block::query() +Query_cache_query * Query_cache_block::query() { #ifndef DBUG_OFF if (type != QUERY) @@ -848,7 +848,7 @@ inline Query_cache_query * Query_cache_block::query() return (Query_cache_query *) data(); } -inline Query_cache_table * Query_cache_block::table() +Query_cache_table * Query_cache_block::table() { #ifndef DBUG_OFF if (type != TABLE) @@ -857,7 +857,7 @@ inline Query_cache_table * Query_cache_block::table() return (Query_cache_table *) data(); } -inline Query_cache_result * Query_cache_block::result() +Query_cache_result * Query_cache_block::result() { #ifndef DBUG_OFF if (type != RESULT && type != RES_CONT && type != RES_BEG && @@ -867,7 +867,7 @@ inline Query_cache_result * Query_cache_block::result() return (Query_cache_result *) data(); } -inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) +Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) { return ((Query_cache_block_table *) (((uchar*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) + diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 7444d444cf9..f35ac889b23 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -141,12 +141,12 @@ struct Query_cache_block inline bool is_free(void) { return type == FREE; } void init(ulong length); void destroy(); - inline uint headers_len(); - inline uchar* data(void); - inline Query_cache_query *query(); - inline Query_cache_table *table(); - inline Query_cache_result *result(); - inline Query_cache_block_table *table(TABLE_COUNTER_TYPE n); + uint headers_len(); + uchar* data(void); + Query_cache_query *query(); + Query_cache_table *table(); + Query_cache_result *result(); + Query_cache_block_table *table(TABLE_COUNTER_TYPE n); }; struct Query_cache_query |