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