1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3/* -*- mode: C; c-basic-offset: 4 -*- */
4#ident "$Id$"
5/*======
6This file is part of TokuDB
7
8
9Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
10
11 TokuDBis is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License, version 2,
13 as published by the Free Software Foundation.
14
15 TokuDB is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TokuDB. If not, see <http://www.gnu.org/licenses/>.
22
23======= */
24
25#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
26
27#include "hatoku_hton.h"
28#include "tokudb_information_schema.h"
29#include "sql_time.h"
30#include "tokudb_background.h"
31
32
33namespace tokudb {
34namespace information_schema {
35
36static void field_store_time_t(Field* field, time_t time) {
37 MYSQL_TIME my_time;
38 struct tm tm_time;
39
40 if (time) {
41 localtime_r(&time, &tm_time);
42 localtime_to_TIME(&my_time, &tm_time);
43 my_time.time_type = MYSQL_TIMESTAMP_DATETIME;
44#ifdef MARIA_PLUGIN_INTERFACE_VERSION
45 field->store_time(&my_time);
46#else
47 field->store_time(&my_time, MYSQL_TIMESTAMP_DATETIME);
48#endif
49 field->set_notnull();
50 } else {
51 field->set_null();
52 }
53}
54
55st_mysql_information_schema trx_information_schema = {
56 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
57};
58
59ST_FIELD_INFO trx_field_info[] = {
60 {"trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
61 {"trx_mysql_thread_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
62 {"trx_time", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
63 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
64};
65
66struct trx_extra_t {
67 THD *thd;
68 TABLE *table;
69};
70
71int trx_callback(
72 DB_TXN* txn,
73 iterate_row_locks_callback iterate_locks,
74 void* locks_extra,
75 void *extra) {
76
77 uint64_t txn_id = txn->id64(txn);
78 uint64_t client_id;
79 txn->get_client_id(txn, &client_id, NULL);
80 uint64_t start_time = txn->get_start_time(txn);
81 trx_extra_t* e = reinterpret_cast<struct trx_extra_t*>(extra);
82 THD* thd = e->thd;
83 TABLE* table = e->table;
84 table->field[0]->store(txn_id, false);
85 table->field[1]->store(client_id, false);
86 uint64_t tnow = (uint64_t) ::time(NULL);
87 table->field[2]->store(tnow >= start_time ? tnow - start_time : 0, false);
88 int error = schema_table_store_record(thd, table);
89 if (!error && thd_kill_level(thd))
90 error = ER_QUERY_INTERRUPTED;
91 return error;
92}
93
94#if MYSQL_VERSION_ID >= 50600
95int trx_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
96#else
97int trx_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
98#endif
99 TOKUDB_DBUG_ENTER("");
100 int error;
101
102 rwlock_t_lock_read(tokudb_hton_initialized_lock);
103
104 if (!tokudb_hton_initialized) {
105 error = ER_PLUGIN_IS_NOT_LOADED;
106 my_error(error, MYF(0), tokudb_hton_name);
107 } else {
108 trx_extra_t e = { thd, tables->table };
109 error = db_env->iterate_live_transactions(db_env, trx_callback, &e);
110 if (error)
111 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
112 }
113
114 tokudb_hton_initialized_lock.unlock();
115 TOKUDB_DBUG_RETURN(error);
116}
117
118int trx_init(void* p) {
119 ST_SCHEMA_TABLE *schema = (ST_SCHEMA_TABLE*) p;
120 schema->fields_info = trx_field_info;
121 schema->fill_table = trx_fill_table;
122 return 0;
123}
124
125int trx_done(void* p) {
126 return 0;
127}
128
129st_mysql_plugin trx = {
130 MYSQL_INFORMATION_SCHEMA_PLUGIN,
131 &trx_information_schema,
132 "TokuDB_trx",
133 "Percona",
134 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
135 PLUGIN_LICENSE_GPL,
136 trx_init, /* plugin init */
137 trx_done, /* plugin deinit */
138 TOKUDB_PLUGIN_VERSION,
139 NULL, /* status variables */
140 NULL, /* system variables */
141#ifdef MARIA_PLUGIN_INTERFACE_VERSION
142 tokudb::sysvars::version,
143 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
144#else
145 NULL, /* config options */
146 0, /* flags */
147#endif
148};
149
150
151
152st_mysql_information_schema lock_waits_information_schema = {
153 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
154};
155
156ST_FIELD_INFO lock_waits_field_info[] = {
157 {"requesting_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
158 {"blocking_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
159 {"lock_waits_dname", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
160 {"lock_waits_key_left", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
161 {"lock_waits_key_right", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
162 {"lock_waits_start_time", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
163 {"lock_waits_table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
164 {"lock_waits_table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
165 {"lock_waits_table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
166 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
167};
168
169struct lock_waits_extra_t {
170 THD* thd;
171 TABLE* table;
172};
173
174int lock_waits_callback(
175 DB* db,
176 uint64_t requesting_txnid,
177 const DBT* left_key,
178 const DBT* right_key,
179 uint64_t blocking_txnid,
180 uint64_t start_time,
181 void *extra) {
182
183 lock_waits_extra_t* e =
184 reinterpret_cast<struct lock_waits_extra_t*>(extra);
185 THD* thd = e->thd;
186 TABLE* table = e->table;
187 table->field[0]->store(requesting_txnid, false);
188 table->field[1]->store(blocking_txnid, false);
189 const char* dname = tokudb_get_index_name(db);
190 size_t dname_length = strlen(dname);
191 table->field[2]->store(dname, dname_length, system_charset_info);
192 String left_str;
193 tokudb_pretty_left_key(db, left_key, &left_str);
194 table->field[3]->store(
195 left_str.ptr(),
196 left_str.length(),
197 system_charset_info);
198 String right_str;
199 tokudb_pretty_right_key(db, right_key, &right_str);
200 table->field[4]->store(
201 right_str.ptr(),
202 right_str.length(),
203 system_charset_info);
204 table->field[5]->store(start_time, false);
205
206 String database_name, table_name, dictionary_name;
207 tokudb_split_dname(dname, database_name, table_name, dictionary_name);
208 table->field[6]->store(
209 database_name.c_ptr(),
210 database_name.length(),
211 system_charset_info);
212 table->field[7]->store(
213 table_name.c_ptr(),
214 table_name.length(),
215 system_charset_info);
216 table->field[8]->store(
217 dictionary_name.c_ptr(),
218 dictionary_name.length(),
219 system_charset_info);
220
221 int error = schema_table_store_record(thd, table);
222
223 if (!error && thd_kill_level(thd))
224 error = ER_QUERY_INTERRUPTED;
225
226 return error;
227}
228
229#if MYSQL_VERSION_ID >= 50600
230int lock_waits_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
231#else
232int lock_waits_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
233#endif
234 TOKUDB_DBUG_ENTER("");
235 int error;
236
237 rwlock_t_lock_read(tokudb_hton_initialized_lock);
238
239 if (!tokudb_hton_initialized) {
240 error = ER_PLUGIN_IS_NOT_LOADED;
241 my_error(error, MYF(0), tokudb_hton_name);
242 } else {
243 lock_waits_extra_t e = { thd, tables->table };
244 error = db_env->iterate_pending_lock_requests(
245 db_env,
246 lock_waits_callback,
247 &e);
248 if (error)
249 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
250 }
251
252 tokudb_hton_initialized_lock.unlock();
253 TOKUDB_DBUG_RETURN(error);
254}
255
256int lock_waits_init(void* p) {
257 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
258 schema->fields_info = lock_waits_field_info;
259 schema->fill_table = lock_waits_fill_table;
260 return 0;
261}
262
263int lock_waits_done(void *p) {
264 return 0;
265}
266
267st_mysql_plugin lock_waits = {
268 MYSQL_INFORMATION_SCHEMA_PLUGIN,
269 &lock_waits_information_schema,
270 "TokuDB_lock_waits",
271 "Percona",
272 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
273 PLUGIN_LICENSE_GPL,
274 lock_waits_init, /* plugin init */
275 lock_waits_done, /* plugin deinit */
276 TOKUDB_PLUGIN_VERSION,
277 NULL, /* status variables */
278 NULL, /* system variables */
279#ifdef MARIA_PLUGIN_INTERFACE_VERSION
280 tokudb::sysvars::version,
281 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
282#else
283 NULL, /* config options */
284 0, /* flags */
285#endif
286};
287
288
289
290st_mysql_information_schema locks_information_schema = {
291 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
292};
293
294 ST_FIELD_INFO locks_field_info[] = {
295 {"locks_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
296 {"locks_mysql_thread_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
297 {"locks_dname", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
298 {"locks_key_left", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
299 {"locks_key_right", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
300 {"locks_table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
301 {"locks_table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
302 {"locks_table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
303 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
304};
305
306struct locks_extra_t {
307 THD* thd;
308 TABLE* table;
309};
310
311int locks_callback(
312 DB_TXN* txn,
313 iterate_row_locks_callback iterate_locks,
314 void* locks_extra,
315 void* extra) {
316
317 uint64_t txn_id = txn->id64(txn);
318 uint64_t client_id;
319 txn->get_client_id(txn, &client_id, NULL);
320 locks_extra_t* e = reinterpret_cast<struct locks_extra_t*>(extra);
321 THD* thd = e->thd;
322 TABLE* table = e->table;
323 int error = 0;
324 DB* db;
325 DBT left_key, right_key;
326 while (error == 0 &&
327 iterate_locks(&db, &left_key, &right_key, locks_extra) == 0) {
328 table->field[0]->store(txn_id, false);
329 table->field[1]->store(client_id, false);
330
331 const char* dname = tokudb_get_index_name(db);
332 size_t dname_length = strlen(dname);
333 table->field[2]->store(dname, dname_length, system_charset_info);
334
335 String left_str;
336 tokudb_pretty_left_key(db, &left_key, &left_str);
337 table->field[3]->store(
338 left_str.ptr(),
339 left_str.length(),
340 system_charset_info);
341
342 String right_str;
343 tokudb_pretty_right_key(db, &right_key, &right_str);
344 table->field[4]->store(
345 right_str.ptr(),
346 right_str.length(),
347 system_charset_info);
348
349 String database_name, table_name, dictionary_name;
350 tokudb_split_dname(dname, database_name, table_name, dictionary_name);
351 table->field[5]->store(
352 database_name.c_ptr(),
353 database_name.length(),
354 system_charset_info);
355 table->field[6]->store(
356 table_name.c_ptr(),
357 table_name.length(),
358 system_charset_info);
359 table->field[7]->store(
360 dictionary_name.c_ptr(),
361 dictionary_name.length(),
362 system_charset_info);
363
364 error = schema_table_store_record(thd, table);
365
366 if (!error && thd_kill_level(thd))
367 error = ER_QUERY_INTERRUPTED;
368 }
369 return error;
370}
371
372#if MYSQL_VERSION_ID >= 50600
373int locks_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
374#else
375int locks_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
376#endif
377 TOKUDB_DBUG_ENTER("");
378 int error;
379
380 rwlock_t_lock_read(tokudb_hton_initialized_lock);
381
382 if (!tokudb_hton_initialized) {
383 error = ER_PLUGIN_IS_NOT_LOADED;
384 my_error(error, MYF(0), tokudb_hton_name);
385 } else {
386 locks_extra_t e = { thd, tables->table };
387 error = db_env->iterate_live_transactions(db_env, locks_callback, &e);
388 if (error)
389 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
390 }
391
392 tokudb_hton_initialized_lock.unlock();
393 TOKUDB_DBUG_RETURN(error);
394}
395
396int locks_init(void* p) {
397 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
398 schema->fields_info = locks_field_info;
399 schema->fill_table = locks_fill_table;
400 return 0;
401}
402
403int locks_done(void* p) {
404 return 0;
405}
406
407st_mysql_plugin locks = {
408 MYSQL_INFORMATION_SCHEMA_PLUGIN,
409 &locks_information_schema,
410 "TokuDB_locks",
411 "Percona",
412 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
413 PLUGIN_LICENSE_GPL,
414 locks_init, /* plugin init */
415 locks_done, /* plugin deinit */
416 TOKUDB_PLUGIN_VERSION,
417 NULL, /* status variables */
418 NULL, /* system variables */
419#ifdef MARIA_PLUGIN_INTERFACE_VERSION
420 tokudb::sysvars::version,
421 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
422#else
423 NULL, /* config options */
424 0, /* flags */
425#endif
426};
427
428
429
430st_mysql_information_schema file_map_information_schema = {
431 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
432};
433
434ST_FIELD_INFO file_map_field_info[] = {
435 {"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
436 {"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
437 {"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
438 {"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
439 {"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
440 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
441};
442
443int report_file_map(TABLE* table, THD* thd) {
444 int error;
445 DB_TXN* txn = NULL;
446 DBC* tmp_cursor = NULL;
447 DBT curr_key;
448 DBT curr_val;
449 memset(&curr_key, 0, sizeof curr_key);
450 memset(&curr_val, 0, sizeof curr_val);
451 error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
452 if (error) {
453 goto cleanup;
454 }
455 error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
456 if (error) {
457 goto cleanup;
458 }
459 while (error == 0) {
460 error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
461 if (!error) {
462 // We store the NULL terminator in the directory so it's included
463 // in the size.
464 // See #5789
465 // Recalculate and check just to be safe.
466 const char *dname = (const char *) curr_key.data;
467 size_t dname_len = strlen(dname);
468 assert(dname_len == curr_key.size - 1);
469 table->field[0]->store(dname, dname_len, system_charset_info);
470
471 const char *iname = (const char *) curr_val.data;
472 size_t iname_len = strlen(iname);
473 assert(iname_len == curr_val.size - 1);
474 table->field[1]->store(iname, iname_len, system_charset_info);
475
476 // split the dname
477 String database_name, table_name, dictionary_name;
478 tokudb_split_dname(
479 dname,
480 database_name,
481 table_name,
482 dictionary_name);
483 table->field[2]->store(
484 database_name.c_ptr(),
485 database_name.length(),
486 system_charset_info);
487 table->field[3]->store(
488 table_name.c_ptr(),
489 table_name.length(),
490 system_charset_info);
491 table->field[4]->store(
492 dictionary_name.c_ptr(),
493 dictionary_name.length(),
494 system_charset_info);
495
496 error = schema_table_store_record(thd, table);
497 }
498 if (!error && thd_kill_level(thd))
499 error = ER_QUERY_INTERRUPTED;
500 }
501 if (error == DB_NOTFOUND) {
502 error = 0;
503 }
504cleanup:
505 if (tmp_cursor) {
506 int r = tmp_cursor->c_close(tmp_cursor);
507 assert(r == 0);
508 }
509 if (txn) {
510 commit_txn(txn, 0);
511 }
512 return error;
513}
514
515#if MYSQL_VERSION_ID >= 50600
516int file_map_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
517#else
518int file_map_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
519#endif
520 TOKUDB_DBUG_ENTER("");
521 int error;
522 TABLE* table = tables->table;
523
524 rwlock_t_lock_read(tokudb_hton_initialized_lock);
525
526 if (!tokudb_hton_initialized) {
527 error = ER_PLUGIN_IS_NOT_LOADED;
528 my_error(error, MYF(0), tokudb_hton_name);
529 } else {
530 error = report_file_map(table, thd);
531 if (error)
532 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
533 }
534
535 tokudb_hton_initialized_lock.unlock();
536 TOKUDB_DBUG_RETURN(error);
537}
538
539int file_map_init(void* p) {
540 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
541 schema->fields_info = file_map_field_info;
542 schema->fill_table = file_map_fill_table;
543 return 0;
544}
545
546int file_map_done(void* p) {
547 return 0;
548}
549
550st_mysql_plugin file_map = {
551 MYSQL_INFORMATION_SCHEMA_PLUGIN,
552 &file_map_information_schema,
553 "TokuDB_file_map",
554 "Percona",
555 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
556 PLUGIN_LICENSE_GPL,
557 file_map_init, /* plugin init */
558 file_map_done, /* plugin deinit */
559 TOKUDB_PLUGIN_VERSION,
560 NULL, /* status variables */
561 NULL, /* system variables */
562#ifdef MARIA_PLUGIN_INTERFACE_VERSION
563 tokudb::sysvars::version,
564 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
565#else
566 NULL, /* config options */
567 0, /* flags */
568#endif
569};
570
571
572
573st_mysql_information_schema fractal_tree_info_information_schema = {
574 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
575};
576
577ST_FIELD_INFO fractal_tree_info_field_info[] = {
578 {"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
579 {"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
580 {"bt_num_blocks_allocated", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
581 {"bt_num_blocks_in_use", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
582 {"bt_size_allocated", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
583 {"bt_size_in_use", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
584 {"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
585 {"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
586 {"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
587 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
588};
589
590int report_fractal_tree_info_for_db(
591 const DBT* dname,
592 const DBT* iname,
593 TABLE* table,
594 THD* thd) {
595
596 int error;
597 uint64_t bt_num_blocks_allocated;
598 uint64_t bt_num_blocks_in_use;
599 uint64_t bt_size_allocated;
600 uint64_t bt_size_in_use;
601
602 DB *db = NULL;
603 error = db_create(&db, db_env, 0);
604 if (error) {
605 goto exit;
606 }
607 error = db->open(db, NULL, (char *)dname->data, NULL, DB_BTREE, 0, 0666);
608 if (error) {
609 goto exit;
610 }
611 error = db->get_fractal_tree_info64(
612 db,
613 &bt_num_blocks_allocated,
614 &bt_num_blocks_in_use,
615 &bt_size_allocated,
616 &bt_size_in_use);
617 if (error) {
618 goto exit;
619 }
620
621 // We store the NULL terminator in the directory so it's included in the
622 // size.
623 // See #5789
624 // Recalculate and check just to be safe.
625 {
626 size_t dname_len = strlen((const char*)dname->data);
627 assert(dname_len == dname->size - 1);
628 table->field[0]->store(
629 (char*)dname->data,
630 dname_len,
631 system_charset_info);
632 size_t iname_len = strlen((const char*)iname->data);
633 assert(iname_len == iname->size - 1);
634 table->field[1]->store(
635 (char*)iname->data,
636 iname_len,
637 system_charset_info);
638 }
639 table->field[2]->store(bt_num_blocks_allocated, false);
640 table->field[3]->store(bt_num_blocks_in_use, false);
641 table->field[4]->store(bt_size_allocated, false);
642 table->field[5]->store(bt_size_in_use, false);
643
644 // split the dname
645 {
646 String database_name, table_name, dictionary_name;
647 tokudb_split_dname(
648 (const char*)dname->data,
649 database_name,
650 table_name,
651 dictionary_name);
652 table->field[6]->store(
653 database_name.c_ptr(),
654 database_name.length(),
655 system_charset_info);
656 table->field[7]->store(
657 table_name.c_ptr(),
658 table_name.length(),
659 system_charset_info);
660 table->field[8]->store(
661 dictionary_name.c_ptr(),
662 dictionary_name.length(),
663 system_charset_info);
664 }
665 error = schema_table_store_record(thd, table);
666
667exit:
668 if (db) {
669 int close_error = db->close(db, 0);
670 if (error == 0)
671 error = close_error;
672 }
673 return error;
674}
675
676int report_fractal_tree_info(TABLE* table, THD* thd) {
677 int error;
678 DB_TXN* txn = NULL;
679 DBC* tmp_cursor = NULL;
680 DBT curr_key;
681 DBT curr_val;
682 memset(&curr_key, 0, sizeof curr_key);
683 memset(&curr_val, 0, sizeof curr_val);
684 error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
685 if (error) {
686 goto cleanup;
687 }
688 error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
689 if (error) {
690 goto cleanup;
691 }
692 while (error == 0) {
693 error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
694 if (!error) {
695 error = report_fractal_tree_info_for_db(
696 &curr_key,
697 &curr_val,
698 table,
699 thd);
700 if (error)
701 error = 0; // ignore read uncommitted errors
702 }
703 if (!error && thd_kill_level(thd))
704 error = ER_QUERY_INTERRUPTED;
705 }
706 if (error == DB_NOTFOUND) {
707 error = 0;
708 }
709cleanup:
710 if (tmp_cursor) {
711 int r = tmp_cursor->c_close(tmp_cursor);
712 assert(r == 0);
713 }
714 if (txn) {
715 commit_txn(txn, 0);
716 }
717 return error;
718}
719
720#if MYSQL_VERSION_ID >= 50600
721int fractal_tree_info_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
722#else
723int fractal_tree_info_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
724#endif
725 TOKUDB_DBUG_ENTER("");
726 int error;
727 TABLE* table = tables->table;
728
729 // 3938: Get a read lock on the status flag, since we must
730 // read it before safely proceeding
731 rwlock_t_lock_read(tokudb_hton_initialized_lock);
732
733 if (!tokudb_hton_initialized) {
734 error = ER_PLUGIN_IS_NOT_LOADED;
735 my_error(error, MYF(0), tokudb_hton_name);
736 } else {
737 error = report_fractal_tree_info(table, thd);
738 if (error)
739 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
740 }
741
742 //3938: unlock the status flag lock
743 tokudb_hton_initialized_lock.unlock();
744 TOKUDB_DBUG_RETURN(error);
745}
746
747int fractal_tree_info_init(void* p) {
748 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
749 schema->fields_info = fractal_tree_info_field_info;
750 schema->fill_table = fractal_tree_info_fill_table;
751 return 0;
752}
753
754int fractal_tree_info_done(void* p) {
755 return 0;
756}
757
758st_mysql_plugin fractal_tree_info = {
759 MYSQL_INFORMATION_SCHEMA_PLUGIN,
760 &fractal_tree_info_information_schema,
761 "TokuDB_fractal_tree_info",
762 "Percona",
763 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
764 PLUGIN_LICENSE_GPL,
765 fractal_tree_info_init, /* plugin init */
766 fractal_tree_info_done, /* plugin deinit */
767 TOKUDB_PLUGIN_VERSION,
768 NULL, /* status variables */
769 NULL, /* system variables */
770#ifdef MARIA_PLUGIN_INTERFACE_VERSION
771 tokudb::sysvars::version,
772 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
773#else
774 NULL, /* config options */
775 0, /* flags */
776#endif
777};
778
779
780
781st_mysql_information_schema fractal_tree_block_map_information_schema = {
782 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
783};
784
785ST_FIELD_INFO fractal_tree_block_map_field_info[] = {
786 {"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
787 {"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
788 {"checkpoint_count", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
789 {"blocknum", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
790 {"offset", 0, MYSQL_TYPE_LONGLONG, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
791 {"size", 0, MYSQL_TYPE_LONGLONG, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
792 {"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
793 {"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
794 {"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
795 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
796};
797
798struct report_fractal_tree_block_map_iterator_extra_t {
799 int64_t num_rows;
800 int64_t i;
801 uint64_t* checkpoint_counts;
802 int64_t* blocknums;
803 int64_t* diskoffs;
804 int64_t* sizes;
805};
806
807// This iterator is called while holding the blocktable lock.
808// We should be as quick as possible.
809// We don't want to do one call to get the number of rows, release the
810// blocktable lock, and then do another call to get all the rows because
811// the number of rows may change if we don't hold the lock.
812// As a compromise, we'll do some mallocs inside the lock on the first call,
813// but everything else should be fast.
814int report_fractal_tree_block_map_iterator(
815 uint64_t checkpoint_count,
816 int64_t num_rows,
817 int64_t blocknum,
818 int64_t diskoff,
819 int64_t size,
820 void* iter_extra) {
821
822 struct report_fractal_tree_block_map_iterator_extra_t* e =
823 static_cast<struct report_fractal_tree_block_map_iterator_extra_t*>(iter_extra);
824
825 assert(num_rows > 0);
826 if (e->num_rows == 0) {
827 e->checkpoint_counts =
828 (uint64_t*)tokudb::memory::malloc(
829 num_rows * (sizeof *e->checkpoint_counts),
830 MYF(MY_WME|MY_ZEROFILL|MY_FAE));
831 e->blocknums =
832 (int64_t*)tokudb::memory::malloc(
833 num_rows * (sizeof *e->blocknums),
834 MYF(MY_WME|MY_ZEROFILL|MY_FAE));
835 e->diskoffs =
836 (int64_t*)tokudb::memory::malloc(
837 num_rows * (sizeof *e->diskoffs),
838 MYF(MY_WME|MY_ZEROFILL|MY_FAE));
839 e->sizes =
840 (int64_t*)tokudb::memory::malloc(
841 num_rows * (sizeof *e->sizes),
842 MYF(MY_WME|MY_ZEROFILL|MY_FAE));
843 e->num_rows = num_rows;
844 }
845
846 e->checkpoint_counts[e->i] = checkpoint_count;
847 e->blocknums[e->i] = blocknum;
848 e->diskoffs[e->i] = diskoff;
849 e->sizes[e->i] = size;
850 ++(e->i);
851
852 return 0;
853}
854
855int report_fractal_tree_block_map_for_db(
856 const DBT* dname,
857 const DBT* iname,
858 TABLE* table,
859 THD* thd) {
860
861 int error;
862 DB* db;
863 // avoid struct initializers so that we can compile with older gcc versions
864 report_fractal_tree_block_map_iterator_extra_t e = {};
865
866 error = db_create(&db, db_env, 0);
867 if (error) {
868 goto exit;
869 }
870 error = db->open(db, NULL, (char *)dname->data, NULL, DB_BTREE, 0, 0666);
871 if (error) {
872 goto exit;
873 }
874 error = db->iterate_fractal_tree_block_map(
875 db,
876 report_fractal_tree_block_map_iterator,
877 &e);
878 {
879 int close_error = db->close(db, 0);
880 if (!error) {
881 error = close_error;
882 }
883 }
884 if (error) {
885 goto exit;
886 }
887
888 // If not, we should have gotten an error and skipped this section of code
889 assert(e.i == e.num_rows);
890 for (int64_t i = 0; error == 0 && i < e.num_rows; ++i) {
891 // We store the NULL terminator in the directory so it's included in the size.
892 // See #5789
893 // Recalculate and check just to be safe.
894 size_t dname_len = strlen((const char*)dname->data);
895 assert(dname_len == dname->size - 1);
896 table->field[0]->store(
897 (char*)dname->data,
898 dname_len,
899 system_charset_info);
900
901 size_t iname_len = strlen((const char*)iname->data);
902 assert(iname_len == iname->size - 1);
903 table->field[1]->store(
904 (char*)iname->data,
905 iname_len,
906 system_charset_info);
907
908 table->field[2]->store(e.checkpoint_counts[i], false);
909 table->field[3]->store(e.blocknums[i], false);
910 static const int64_t freelist_null = -1;
911 static const int64_t diskoff_unused = -2;
912 if (e.diskoffs[i] == diskoff_unused || e.diskoffs[i] == freelist_null) {
913 table->field[4]->set_null();
914 } else {
915 table->field[4]->set_notnull();
916 table->field[4]->store(e.diskoffs[i], false);
917 }
918 static const int64_t size_is_free = -1;
919 if (e.sizes[i] == size_is_free) {
920 table->field[5]->set_null();
921 } else {
922 table->field[5]->set_notnull();
923 table->field[5]->store(e.sizes[i], false);
924 }
925
926 // split the dname
927 String database_name, table_name, dictionary_name;
928 tokudb_split_dname(
929 (const char*)dname->data,
930 database_name,
931 table_name,
932 dictionary_name);
933 table->field[6]->store(
934 database_name.c_ptr(),
935 database_name.length(),
936 system_charset_info);
937 table->field[7]->store(
938 table_name.c_ptr(),
939 table_name.length(),
940 system_charset_info);
941 table->field[8]->store(
942 dictionary_name.c_ptr(),
943 dictionary_name.length(),
944 system_charset_info);
945
946 error = schema_table_store_record(thd, table);
947 }
948
949exit:
950 if (e.checkpoint_counts != NULL) {
951 tokudb::memory::free(e.checkpoint_counts);
952 e.checkpoint_counts = NULL;
953 }
954 if (e.blocknums != NULL) {
955 tokudb::memory::free(e.blocknums);
956 e.blocknums = NULL;
957 }
958 if (e.diskoffs != NULL) {
959 tokudb::memory::free(e.diskoffs);
960 e.diskoffs = NULL;
961 }
962 if (e.sizes != NULL) {
963 tokudb::memory::free(e.sizes);
964 e.sizes = NULL;
965 }
966 return error;
967}
968
969int report_fractal_tree_block_map(TABLE* table, THD* thd) {
970 int error;
971 DB_TXN* txn = NULL;
972 DBC* tmp_cursor = NULL;
973 DBT curr_key;
974 DBT curr_val;
975 memset(&curr_key, 0, sizeof curr_key);
976 memset(&curr_val, 0, sizeof curr_val);
977 error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
978 if (error) {
979 goto cleanup;
980 }
981 error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
982 if (error) {
983 goto cleanup;
984 }
985 while (error == 0) {
986 error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
987 if (!error) {
988 error = report_fractal_tree_block_map_for_db(
989 &curr_key,
990 &curr_val,
991 table,
992 thd);
993 }
994 if (!error && thd_kill_level(thd))
995 error = ER_QUERY_INTERRUPTED;
996 }
997 if (error == DB_NOTFOUND) {
998 error = 0;
999 }
1000cleanup:
1001 if (tmp_cursor) {
1002 int r = tmp_cursor->c_close(tmp_cursor);
1003 assert(r == 0);
1004 }
1005 if (txn) {
1006 commit_txn(txn, 0);
1007 }
1008 return error;
1009}
1010
1011#if MYSQL_VERSION_ID >= 50600
1012int fractal_tree_block_map_fill_table(
1013 THD* thd,
1014 TABLE_LIST* tables,
1015 Item* cond) {
1016#else
1017int fractal_tree_block_map_fill_table(
1018 THD* thd,
1019 TABLE_LIST* tables,
1020 COND* cond) {
1021#endif
1022 TOKUDB_DBUG_ENTER("");
1023 int error;
1024 TABLE* table = tables->table;
1025
1026 // 3938: Get a read lock on the status flag, since we must
1027 // read it before safely proceeding
1028 rwlock_t_lock_read(tokudb_hton_initialized_lock);
1029
1030 if (!tokudb_hton_initialized) {
1031 error = ER_PLUGIN_IS_NOT_LOADED;
1032 my_error(error, MYF(0), tokudb_hton_name);
1033 } else {
1034 error = report_fractal_tree_block_map(table, thd);
1035 if (error)
1036 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
1037 }
1038
1039 //3938: unlock the status flag lock
1040 tokudb_hton_initialized_lock.unlock();
1041 TOKUDB_DBUG_RETURN(error);
1042}
1043
1044int fractal_tree_block_map_init(void* p) {
1045 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
1046 schema->fields_info = fractal_tree_block_map_field_info;
1047 schema->fill_table = fractal_tree_block_map_fill_table;
1048 return 0;
1049}
1050
1051int fractal_tree_block_map_done(void *p) {
1052 return 0;
1053}
1054
1055st_mysql_plugin fractal_tree_block_map = {
1056 MYSQL_INFORMATION_SCHEMA_PLUGIN,
1057 &fractal_tree_block_map_information_schema,
1058 "TokuDB_fractal_tree_block_map",
1059 "Percona",
1060 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
1061 PLUGIN_LICENSE_GPL,
1062 fractal_tree_block_map_init, /* plugin init */
1063 fractal_tree_block_map_done, /* plugin deinit */
1064 TOKUDB_PLUGIN_VERSION,
1065 NULL, /* status variables */
1066 NULL, /* system variables */
1067#ifdef MARIA_PLUGIN_INTERFACE_VERSION
1068 tokudb::sysvars::version,
1069 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
1070#else
1071 NULL, /* config options */
1072 0, /* flags */
1073#endif
1074};
1075
1076
1077st_mysql_information_schema background_job_status_information_schema = {
1078 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
1079};
1080
1081ST_FIELD_INFO background_job_status_field_info[] = {
1082 {"id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
1083 {"database_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
1084 {"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
1085 {"job_type", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
1086 {"job_params", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
1087 {"scheduler", 32, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
1088 {"scheduled_time", 0, MYSQL_TYPE_DATETIME, 0, 0, NULL, SKIP_OPEN_TABLE },
1089 {"started_time", 0, MYSQL_TYPE_DATETIME, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
1090 {"status", 1024, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, SKIP_OPEN_TABLE },
1091 {NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
1092};
1093
1094struct background_job_status_extra {
1095 THD* thd;
1096 TABLE* table;
1097};
1098
1099void background_job_status_callback(
1100 tokudb::background::job_manager_t::job_t* job,
1101 void* extra) {
1102
1103 background_job_status_extra* e =
1104 reinterpret_cast<background_job_status_extra*>(extra);
1105
1106 THD* thd = e->thd;
1107 TABLE* table = e->table;
1108 const char* tmp = NULL;
1109
1110 table->field[0]->store(job->id(), false);
1111
1112 tmp = job->database();
1113 table->field[1]->store(tmp, strlen(tmp), system_charset_info);
1114
1115 tmp = job->table();
1116 table->field[2]->store(tmp, strlen(tmp), system_charset_info);
1117
1118 tmp = job->type();
1119 table->field[3]->store(tmp, strlen(tmp), system_charset_info);
1120
1121 tmp = job->parameters();
1122 table->field[4]->store(tmp, strlen(tmp), system_charset_info);
1123
1124 if (job->user_scheduled())
1125 table->field[5]->store("USER", strlen("USER"), system_charset_info);
1126 else
1127 table->field[5]->store("AUTO", strlen("AUTO"), system_charset_info);
1128
1129 field_store_time_t(table->field[6], job->scheduled_time());
1130 field_store_time_t(table->field[7], job->started_time());
1131
1132 tmp = job->status();
1133 if (tmp && tmp[0] != '\0') {
1134 table->field[8]->store(tmp, strlen(tmp), system_charset_info);
1135 table->field[8]->set_notnull();
1136 } else {
1137 table->field[8]->store(NULL, 0, system_charset_info);
1138 table->field[8]->set_null();
1139 }
1140
1141 schema_table_store_record(thd, table);
1142}
1143
1144int report_background_job_status(TABLE *table, THD *thd) {
1145 int error = 0;
1146 background_job_status_extra extra = {
1147 thd,
1148 table
1149 };
1150 tokudb::background::_job_manager->iterate_jobs(
1151 background_job_status_callback,
1152 &extra);
1153 return error;
1154}
1155
1156#if MYSQL_VERSION_ID >= 50600
1157int background_job_status_fill_table(THD *thd, TABLE_LIST *tables, Item *cond) {
1158#else
1159int background_job_status_fill_table(THD *thd, TABLE_LIST *tables, COND *cond) {
1160#endif
1161 TOKUDB_DBUG_ENTER("");
1162 int error;
1163 TABLE* table = tables->table;
1164
1165 rwlock_t_lock_read(tokudb_hton_initialized_lock);
1166
1167 if (!tokudb_hton_initialized) {
1168 error = ER_PLUGIN_IS_NOT_LOADED;
1169 my_error(error, MYF(0), tokudb_hton_name);
1170 } else {
1171 error = report_background_job_status(table, thd);
1172 if (error)
1173 my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
1174 }
1175
1176 tokudb_hton_initialized_lock.unlock();
1177 TOKUDB_DBUG_RETURN(error);
1178}
1179
1180int background_job_status_init(void* p) {
1181 ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
1182 schema->fields_info = background_job_status_field_info;
1183 schema->fill_table = background_job_status_fill_table;
1184 return 0;
1185}
1186
1187int background_job_status_done(void* p) {
1188 return 0;
1189}
1190
1191st_mysql_plugin background_job_status = {
1192 MYSQL_INFORMATION_SCHEMA_PLUGIN,
1193 &background_job_status_information_schema,
1194 "TokuDB_background_job_status",
1195 "Percona",
1196 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
1197 PLUGIN_LICENSE_GPL,
1198 background_job_status_init, /* plugin init */
1199 background_job_status_done, /* plugin deinit */
1200 TOKUDB_PLUGIN_VERSION,
1201 NULL, /* status variables */
1202 NULL, /* system variables */
1203#ifdef MARIA_PLUGIN_INTERFACE_VERSION
1204 tokudb::sysvars::version,
1205 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
1206#else
1207 NULL, /* config options */
1208 0, /* flags */
1209#endif
1210};
1211
1212} // namespace information_schema
1213} // namespace tokudb
1214