1/*
2 Copyright (c) 2008, Roland Bouman
3 http://rpbouman.blogspot.com/
4 roland.bouman@gmail.com
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of the Roland Bouman nor the
15 names of the contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30
31#ifndef MYSQL_SERVER
32#define MYSQL_SERVER
33#endif
34
35#include <my_global.h>
36#include <sql_parse.h> // check_global_access
37#include <sql_acl.h> // PROCESS_ACL
38#include <sql_class.h> // THD
39#include <sql_cache.h>
40#include <table.h> // ST_SCHEMA_TABLE
41#include <set_var.h> // sql_mode_string_representation
42#include <tztime.h>
43#include <mysql/plugin.h>
44
45class Accessible_Query_Cache : public Query_cache {
46public:
47 HASH *get_queries()
48 {
49 return &this->queries;
50 }
51} *qc;
52
53bool schema_table_store_record(THD *thd, TABLE *table);
54
55#define MAX_STATEMENT_TEXT_LENGTH 32767
56#define COLUMN_STATEMENT_SCHEMA 0
57#define COLUMN_STATEMENT_TEXT 1
58#define COLUMN_RESULT_BLOCKS_COUNT 2
59#define COLUMN_RESULT_BLOCKS_SIZE 3
60#define COLUMN_RESULT_BLOCKS_SIZE_USED 4
61#define COLUMN_LIMIT 5
62#define COLUMN_MAX_SORT_LENGTH 6
63#define COLUMN_GROUP_CONCAT_MAX_LENGTH 7
64#define COLUMN_CHARACTER_SET_CLIENT 8
65#define COLUMN_CHARACTER_SET_RESULT 9
66#define COLUMN_COLLATION 10
67#define COLUMN_TIMEZONE 11
68#define COLUMN_DEFAULT_WEEK_FORMAT 12
69#define COLUMN_DIV_PRECISION_INCREMENT 13
70#define COLUMN_SQL_MODE 14
71#define COLUMN_LC_TIME_NAMES 15
72
73#define COLUMN_CLIENT_LONG_FLAG 16
74#define COLUMN_CLIENT_PROTOCOL_41 17
75#define COLUMN_PROTOCOL_TYPE 18
76#define COLUMN_MORE_RESULTS_EXISTS 19
77#define COLUMN_IN_TRANS 20
78#define COLUMN_AUTOCOMMIT 21
79#define COLUMN_PKT_NR 22
80#define COLUMN_HITS 23
81
82/* ST_FIELD_INFO is defined in table.h */
83static ST_FIELD_INFO qc_info_fields[]=
84{
85 {"STATEMENT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
86 {"STATEMENT_TEXT", MAX_STATEMENT_TEXT_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, 0},
87 {"RESULT_BLOCKS_COUNT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, 0, 0},
88 {"RESULT_BLOCKS_SIZE", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0, 0},
89 {"RESULT_BLOCKS_SIZE_USED", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0, 0},
90 {"LIMIT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0, 0},
91 {"MAX_SORT_LENGTH", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0, 0},
92 {"GROUP_CONCAT_MAX_LENGTH", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, 0, 0},
93 {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, 0},
94 {"CHARACTER_SET_RESULT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, 0},
95 {"COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, 0},
96 {"TIMEZONE", 50, MYSQL_TYPE_STRING, 0, 0, 0, 0},
97 {"DEFAULT_WEEK_FORMAT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, 0, 0},
98 {"DIV_PRECISION_INCREMENT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, 0, 0},
99 {"SQL_MODE", 250, MYSQL_TYPE_STRING, 0, 0, 0, 0},
100 {"LC_TIME_NAMES", 100, MYSQL_TYPE_STRING, 0, 0, 0, 0},
101 {"CLIENT_LONG_FLAG", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
102 {"CLIENT_PROTOCOL_41", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
103 {"PROTOCOL_TYPE", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
104 {"MORE_RESULTS_EXISTS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
105 {"IN_TRANS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
106 {"AUTOCOMMIT", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
107 {"PACKET_NUMBER", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_TINY, 0, 0, 0, 0},
108 {"HITS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, 0},
109 {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
110};
111
112
113static const char unknown[]= "#UNKNOWN#";
114
115static int qc_info_fill_table(THD *thd, TABLE_LIST *tables,
116 COND *cond)
117{
118 int status= 1;
119 CHARSET_INFO *scs= system_charset_info;
120 TABLE *table= tables->table;
121 HASH *queries = qc->get_queries();
122
123 /* one must have PROCESS privilege to see others' queries */
124 if (check_global_access(thd, PROCESS_ACL, true))
125 return 0;
126
127 if (qc->try_lock(thd))
128 return 0; // QC is or is being disabled
129
130 /* loop through all queries in the query cache */
131 for (uint i= 0; i < queries->records; i++)
132 {
133 const uchar *query_cache_block_raw;
134 Query_cache_block* query_cache_block;
135 Query_cache_query* query_cache_query;
136 Query_cache_query_flags flags;
137 uint result_blocks_count;
138 ulonglong result_blocks_size;
139 ulonglong result_blocks_size_used;
140 Query_cache_block *first_result_block;
141 Query_cache_block *result_block;
142 const char *statement_text;
143 size_t statement_text_length;
144 size_t flags_length;
145 const char *key, *db;
146 size_t key_length, db_length;
147 LEX_CSTRING sql_mode_str;
148 const String *tz;
149 CHARSET_INFO *cs_client;
150 CHARSET_INFO *cs_result;
151 CHARSET_INFO *collation;
152
153 query_cache_block_raw = my_hash_element(queries, i);
154 query_cache_block = (Query_cache_block*)query_cache_block_raw;
155 if (unlikely(!query_cache_block ||
156 query_cache_block->type != Query_cache_block::QUERY))
157 continue;
158
159 query_cache_query = query_cache_block->query();
160
161 /* Get the actual SQL statement for this query cache query */
162 statement_text = (const char*)query_cache_query->query();
163 statement_text_length = strlen(statement_text);
164 /* We truncate SQL statements up to MAX_STATEMENT_TEXT_LENGTH in our I_S table */
165 table->field[COLUMN_STATEMENT_TEXT]->store((char*)statement_text,
166 MY_MIN(statement_text_length, MAX_STATEMENT_TEXT_LENGTH), scs);
167
168 /* get the entire key that identifies this query cache query */
169 key = (const char*)query_cache_query_get_key(query_cache_block_raw,
170 &key_length, 0);
171 /* get and store the flags */
172 flags_length= key_length - QUERY_CACHE_FLAGS_SIZE;
173 memcpy(&flags, key+flags_length, QUERY_CACHE_FLAGS_SIZE);
174 table->field[COLUMN_LIMIT]->store(flags.limit, 0);
175 table->field[COLUMN_MAX_SORT_LENGTH]->store(flags.max_sort_length, 0);
176 table->field[COLUMN_GROUP_CONCAT_MAX_LENGTH]->store(flags.group_concat_max_len, 0);
177
178 cs_client= get_charset(flags.character_set_client_num, MYF(MY_WME));
179 if (likely(cs_client))
180 table->field[COLUMN_CHARACTER_SET_CLIENT]->
181 store(cs_client->csname, strlen(cs_client->csname), scs);
182 else
183 table->field[COLUMN_CHARACTER_SET_CLIENT]->
184 store(STRING_WITH_LEN(unknown), scs);
185
186 cs_result= get_charset(flags.character_set_results_num, MYF(MY_WME));
187 if (likely(cs_result))
188 table->field[COLUMN_CHARACTER_SET_RESULT]->
189 store(cs_result->csname, strlen(cs_result->csname), scs);
190 else
191 table->field[COLUMN_CHARACTER_SET_RESULT]->
192 store(STRING_WITH_LEN(unknown), scs);
193
194 collation= get_charset(flags.collation_connection_num, MYF(MY_WME));
195 if (likely(collation))
196 table->field[COLUMN_COLLATION]->
197 store(collation->name, strlen(collation->name), scs);
198 else
199 table->field[COLUMN_COLLATION]-> store(STRING_WITH_LEN(unknown), scs);
200
201 tz= flags.time_zone->get_name();
202 if (likely(tz))
203 table->field[COLUMN_TIMEZONE]->store(tz->ptr(), tz->length(), scs);
204 else
205 table->field[COLUMN_TIMEZONE]-> store(STRING_WITH_LEN(unknown), scs);
206 table->field[COLUMN_DEFAULT_WEEK_FORMAT]->store(flags.default_week_format, 0);
207 table->field[COLUMN_DIV_PRECISION_INCREMENT]->store(flags.div_precision_increment, 0);
208
209 sql_mode_string_representation(thd, flags.sql_mode, &sql_mode_str);
210 table->field[COLUMN_SQL_MODE]->store(sql_mode_str.str, sql_mode_str.length, scs);
211
212 table->field[COLUMN_LC_TIME_NAMES]->store(flags.lc_time_names->name,strlen(flags.lc_time_names->name), scs);
213
214 table->field[COLUMN_CLIENT_LONG_FLAG]->store(flags.client_long_flag, 0);
215 table->field[COLUMN_CLIENT_PROTOCOL_41]->store(flags.client_protocol_41, 0);
216 table->field[COLUMN_PROTOCOL_TYPE]->store(flags.protocol_type, 0);
217 table->field[COLUMN_MORE_RESULTS_EXISTS]->store(flags.more_results_exists, 0);
218 table->field[COLUMN_IN_TRANS]->store(flags.in_trans, 0);
219 table->field[COLUMN_AUTOCOMMIT]->store(flags.autocommit, 0);
220 table->field[COLUMN_PKT_NR]->store(flags.pkt_nr, 0);
221 table->field[COLUMN_HITS]->store(query_cache_query->hits(), 0);
222
223 /* The database against which the statement is executed is part of the
224 query cache query key
225 */
226 compile_time_assert(QUERY_CACHE_DB_LENGTH_SIZE == 2);
227 db= key + statement_text_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE;
228 db_length= uint2korr(db - QUERY_CACHE_DB_LENGTH_SIZE);
229
230 table->field[COLUMN_STATEMENT_SCHEMA]->store(db, db_length, scs);
231
232 /* If we have result blocks, process them */
233 first_result_block= query_cache_query->result();
234 if(query_cache_query->is_results_ready() &&
235 first_result_block)
236 {
237 /* initialize so we can loop over the result blocks*/
238 result_block= first_result_block;
239 result_blocks_count = 1;
240 result_blocks_size = result_block->length;
241 result_blocks_size_used = result_block->used;
242
243 /* loop over the result blocks*/
244 while((result_block= result_block->next)!=first_result_block)
245 {
246 /* calculate total number of result blocks */
247 result_blocks_count++;
248 /* calculate total size of result blocks */
249 result_blocks_size += result_block->length;
250 /* calculate total of used size of result blocks */
251 result_blocks_size_used += result_block->used;
252 }
253 }
254 else
255 {
256 result_blocks_count = 0;
257 result_blocks_size = 0;
258 result_blocks_size_used = 0;
259 }
260 table->field[COLUMN_RESULT_BLOCKS_COUNT]->store(result_blocks_count, 0);
261 table->field[COLUMN_RESULT_BLOCKS_SIZE]->store(result_blocks_size, 0);
262 table->field[COLUMN_RESULT_BLOCKS_SIZE_USED]->
263 store(result_blocks_size_used, 0);
264
265 if (schema_table_store_record(thd, table))
266 goto cleanup;
267 }
268 status = 0;
269
270cleanup:
271 qc->unlock();
272 return status;
273}
274
275static int qc_info_plugin_init(void *p)
276{
277 ST_SCHEMA_TABLE *schema= (ST_SCHEMA_TABLE *)p;
278
279 schema->fields_info= qc_info_fields;
280 schema->fill_table= qc_info_fill_table;
281
282#ifdef _WIN32
283 qc = (Accessible_Query_Cache *)
284 GetProcAddress(GetModuleHandle(NULL), "?query_cache@@3VQuery_cache@@A");
285#else
286 qc = (Accessible_Query_Cache *)&query_cache;
287#endif
288
289 return qc == 0;
290}
291
292
293static struct st_mysql_information_schema qc_info_plugin=
294{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };
295
296/*
297 Plugin library descriptor
298*/
299
300maria_declare_plugin(query_cache_info)
301{
302 MYSQL_INFORMATION_SCHEMA_PLUGIN,
303 &qc_info_plugin,
304 "QUERY_CACHE_INFO",
305 "Roland Bouman, Daniel Black",
306 "Lists all queries in the query cache.",
307 PLUGIN_LICENSE_BSD,
308 qc_info_plugin_init, /* Plugin Init */
309 0, /* Plugin Deinit */
310 0x0101, /* version, hex */
311 NULL, /* status variables */
312 NULL, /* system variables */
313 "1.1", /* version as a string */
314 MariaDB_PLUGIN_MATURITY_STABLE
315}
316maria_declare_plugin_end;
317
318