diff options
Diffstat (limited to 'plugin/qc_info/qc_info.cc')
-rw-r--r-- | plugin/qc_info/qc_info.cc | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/plugin/qc_info/qc_info.cc b/plugin/qc_info/qc_info.cc new file mode 100644 index 00000000..41b3c7b4 --- /dev/null +++ b/plugin/qc_info/qc_info.cc @@ -0,0 +1,316 @@ +/* + 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. + */ + + +#ifndef MYSQL_SERVER +#define MYSQL_SERVER +#endif + +#include <my_global.h> +#include <sql_parse.h> // check_global_access +#include <sql_acl.h> // PROCESS_ACL +#include <sql_class.h> // THD +#include <sql_cache.h> +#include <sql_i_s.h> // ST_SCHEMA_TABLE +#include <set_var.h> // sql_mode_string_representation +#include <tztime.h> +#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 +#define COLUMN_LIMIT 5 +#define COLUMN_MAX_SORT_LENGTH 6 +#define COLUMN_GROUP_CONCAT_MAX_LENGTH 7 +#define COLUMN_CHARACTER_SET_CLIENT 8 +#define COLUMN_CHARACTER_SET_RESULT 9 +#define COLUMN_COLLATION 10 +#define COLUMN_TIMEZONE 11 +#define COLUMN_DEFAULT_WEEK_FORMAT 12 +#define COLUMN_DIV_PRECISION_INCREMENT 13 +#define COLUMN_SQL_MODE 14 +#define COLUMN_LC_TIME_NAMES 15 + +#define COLUMN_CLIENT_LONG_FLAG 16 +#define COLUMN_CLIENT_PROTOCOL_41 17 +#define COLUMN_PROTOCOL_TYPE 18 +#define COLUMN_MORE_RESULTS_EXISTS 19 +#define COLUMN_IN_TRANS 20 +#define COLUMN_AUTOCOMMIT 21 +#define COLUMN_PKT_NR 22 +#define COLUMN_HITS 23 + + +namespace Show { + +/* ST_FIELD_INFO is defined in table.h */ +static ST_FIELD_INFO qc_info_fields[]= +{ + Column("STATEMENT_SCHEMA", Varchar(NAME_LEN), NOT_NULL), + Column("STATEMENT_TEXT", Longtext(MAX_STATEMENT_TEXT_LENGTH),NOT_NULL), + Column("RESULT_BLOCKS_COUNT", SLong(), NOT_NULL), + Column("RESULT_BLOCKS_SIZE", SLonglong(MY_INT32_NUM_DECIMAL_DIGITS),NOT_NULL), + Column("RESULT_BLOCKS_SIZE_USED",SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("LIMIT", SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("MAX_SORT_LENGTH", SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("GROUP_CONCAT_MAX_LENGTH",SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("CHARACTER_SET_CLIENT", CSName(), NOT_NULL), + Column("CHARACTER_SET_RESULT", CSName(), NOT_NULL), + Column("COLLATION", CSName(), NOT_NULL), + Column("TIMEZONE", Varchar(50), NOT_NULL), + Column("DEFAULT_WEEK_FORMAT", SLong(), NOT_NULL), + Column("DIV_PRECISION_INCREMENT",SLong(), NOT_NULL), + Column("SQL_MODE", Varchar(250), NOT_NULL), + Column("LC_TIME_NAMES", Varchar(100), NOT_NULL), + Column("CLIENT_LONG_FLAG", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("CLIENT_PROTOCOL_41", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("PROTOCOL_TYPE", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("MORE_RESULTS_EXISTS", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("IN_TRANS", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("AUTOCOMMIT", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("PACKET_NUMBER", STiny(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + Column("HITS", SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL), + CEnd() +}; + +} // namespace Show + + +static const char unknown[]= "#UNKNOWN#"; + +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 0; // QC is or is being disabled + + /* 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; + Query_cache_query_flags flags; + 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; + size_t flags_length; + const char *key, *db; + size_t key_length, db_length; + LEX_CSTRING sql_mode_str; + const String *tz; + CHARSET_INFO *cs_client; + CHARSET_INFO *cs_result; + CHARSET_INFO *collation; + + query_cache_block_raw = my_hash_element(queries, i); + query_cache_block = (Query_cache_block*)query_cache_block_raw; + if (unlikely(!query_cache_block || + 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, + MY_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); + /* get and store the flags */ + flags_length= key_length - QUERY_CACHE_FLAGS_SIZE; + memcpy(&flags, key+flags_length, QUERY_CACHE_FLAGS_SIZE); + table->field[COLUMN_LIMIT]->store(flags.limit, 0); + table->field[COLUMN_MAX_SORT_LENGTH]->store(flags.max_sort_length, 0); + table->field[COLUMN_GROUP_CONCAT_MAX_LENGTH]->store(flags.group_concat_max_len, 0); + + cs_client= get_charset(flags.character_set_client_num, MYF(MY_WME)); + if (likely(cs_client)) + table->field[COLUMN_CHARACTER_SET_CLIENT]-> + store(cs_client->csname, strlen(cs_client->csname), scs); + else + table->field[COLUMN_CHARACTER_SET_CLIENT]-> + store(STRING_WITH_LEN(unknown), scs); + + cs_result= get_charset(flags.character_set_results_num, MYF(MY_WME)); + if (likely(cs_result)) + table->field[COLUMN_CHARACTER_SET_RESULT]-> + store(cs_result->csname, strlen(cs_result->csname), scs); + else + table->field[COLUMN_CHARACTER_SET_RESULT]-> + store(STRING_WITH_LEN(unknown), scs); + + collation= get_charset(flags.collation_connection_num, MYF(MY_WME)); + if (likely(collation)) + table->field[COLUMN_COLLATION]-> + store(collation->name, strlen(collation->name), scs); + else + table->field[COLUMN_COLLATION]-> store(STRING_WITH_LEN(unknown), scs); + + tz= flags.time_zone->get_name(); + if (likely(tz)) + table->field[COLUMN_TIMEZONE]->store(tz->ptr(), tz->length(), scs); + else + table->field[COLUMN_TIMEZONE]-> store(STRING_WITH_LEN(unknown), scs); + table->field[COLUMN_DEFAULT_WEEK_FORMAT]->store(flags.default_week_format, 0); + table->field[COLUMN_DIV_PRECISION_INCREMENT]->store(flags.div_precision_increment, 0); + + sql_mode_string_representation(thd, flags.sql_mode, &sql_mode_str); + table->field[COLUMN_SQL_MODE]->store(sql_mode_str.str, sql_mode_str.length, scs); + + table->field[COLUMN_LC_TIME_NAMES]->store(flags.lc_time_names->name,strlen(flags.lc_time_names->name), scs); + + table->field[COLUMN_CLIENT_LONG_FLAG]->store(flags.client_long_flag, 0); + table->field[COLUMN_CLIENT_PROTOCOL_41]->store(flags.client_protocol_41, 0); + table->field[COLUMN_PROTOCOL_TYPE]->store(flags.protocol_type, 0); + table->field[COLUMN_MORE_RESULTS_EXISTS]->store(flags.more_results_exists, 0); + table->field[COLUMN_IN_TRANS]->store(flags.in_trans, 0); + table->field[COLUMN_AUTOCOMMIT]->store(flags.autocommit, 0); + table->field[COLUMN_PKT_NR]->store(flags.pkt_nr, 0); + table->field[COLUMN_HITS]->store(query_cache_query->hits(), 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(query_cache_query->is_results_ready() && + 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= Show::qc_info_fields; + schema->fill_table= qc_info_fill_table; + qc = (Accessible_Query_Cache *)&query_cache; + + 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, Daniel Black", + "Lists all queries in the query cache.", + PLUGIN_LICENSE_BSD, + qc_info_plugin_init, /* Plugin Init */ + 0, /* Plugin Deinit */ + 0x0101, /* version, hex */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.1", /* version as a string */ + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; + |