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