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
29#define TOKU_METADB_NAME "tokudb_meta"
30
31static pfs_key_t tokudb_map_mutex_key;
32
33static PSI_mutex_info all_tokudb_mutexes[] = {
34 {&tokudb_map_mutex_key, "tokudb_map_mutex", 0},
35 {&ha_tokudb_mutex_key, "ha_tokudb_mutex", 0},
36};
37
38static PSI_rwlock_info all_tokudb_rwlocks[] = {
39 {&num_DBs_lock_key, "num_DBs_lock", 0},
40};
41
42typedef struct savepoint_info {
43 DB_TXN* txn;
44 tokudb_trx_data* trx;
45 bool in_sub_stmt;
46} *SP_INFO, SP_INFO_T;
47
48static handler* tokudb_create_handler(
49 handlerton* hton,
50 TABLE_SHARE* table,
51 MEM_ROOT* mem_root);
52
53static void tokudb_print_error(
54 const DB_ENV* db_env,
55 const char* db_errpfx,
56 const char* buffer);
57static void tokudb_cleanup_log_files(void);
58static int tokudb_end(handlerton* hton, ha_panic_function type);
59static bool tokudb_flush_logs(handlerton* hton);
60static bool tokudb_show_status(
61 handlerton* hton,
62 THD* thd,
63 stat_print_fn* print,
64 enum ha_stat_type);
65#if defined(TOKU_INCLUDE_HANDLERTON_HANDLE_FATAL_SIGNAL)
66static void tokudb_handle_fatal_signal(handlerton* hton, THD* thd, int sig);
67#endif
68static int tokudb_close_connection(handlerton* hton, THD* thd);
69static void tokudb_kill_connection(handlerton *hton, THD *thd, enum thd_kill_levels level);
70static int tokudb_commit(handlerton* hton, THD* thd, bool all);
71static int tokudb_rollback(handlerton* hton, THD* thd, bool all);
72#if TOKU_INCLUDE_XA
73static int tokudb_xa_prepare(handlerton* hton, THD* thd, bool all);
74static int tokudb_xa_recover(handlerton* hton, XID* xid_list, uint len);
75static int tokudb_commit_by_xid(handlerton* hton, XID* xid);
76static int tokudb_rollback_by_xid(handlerton* hton, XID* xid);
77#endif
78
79static int tokudb_rollback_to_savepoint(
80 handlerton* hton,
81 THD* thd,
82 void* savepoint);
83static int tokudb_savepoint(handlerton* hton, THD* thd, void* savepoint);
84static int tokudb_release_savepoint(
85 handlerton* hton,
86 THD* thd,
87 void* savepoint);
88#if 100000 <= MYSQL_VERSION_ID
89static int tokudb_discover_table(handlerton *hton, THD* thd, TABLE_SHARE *ts);
90static int tokudb_discover_table_existence(
91 handlerton* hton,
92 const char* db,
93 const char* name);
94#endif
95static int tokudb_discover(
96 handlerton* hton,
97 THD* thd,
98 const char* db,
99 const char* name,
100 uchar** frmblob,
101 size_t* frmlen);
102static int tokudb_discover2(
103 handlerton* hton,
104 THD* thd,
105 const char* db,
106 const char* name,
107 bool translate_name,
108 uchar** frmblob,
109 size_t* frmlen);
110static int tokudb_discover3(
111 handlerton* hton,
112 THD* thd,
113 const char* db,
114 const char* name,
115 const char* path,
116 uchar** frmblob,
117 size_t* frmlen);
118handlerton* tokudb_hton;
119
120const char* ha_tokudb_ext = ".tokudb";
121DB_ENV* db_env;
122
123static tokudb::thread::mutex_t tokudb_map_mutex;
124#if TOKU_THDVAR_MEMALLOC_BUG
125static TREE tokudb_map;
126struct tokudb_map_pair {
127 THD* thd;
128 char *last_lock_timeout;
129};
130#if 50500 <= MYSQL_VERSION_ID && MYSQL_VERSION_ID <= 50599
131static int tokudb_map_pair_cmp(void *custom_arg, const void *a, const void *b) {
132#else
133static int tokudb_map_pair_cmp(
134 const void* custom_arg,
135 const void* a,
136 const void* b) {
137#endif
138
139 const struct tokudb_map_pair *a_key = (const struct tokudb_map_pair *) a;
140 const struct tokudb_map_pair *b_key = (const struct tokudb_map_pair *) b;
141 if (a_key->thd < b_key->thd)
142 return -1;
143 else if (a_key->thd > b_key->thd)
144 return +1;
145 else
146 return 0;
147};
148#endif
149
150static PARTITIONED_COUNTER tokudb_primary_key_bytes_inserted;
151void toku_hton_update_primary_key_bytes_inserted(uint64_t row_size) {
152 increment_partitioned_counter(tokudb_primary_key_bytes_inserted, row_size);
153}
154
155static void tokudb_lock_timeout_callback(
156 DB* db,
157 uint64_t requesting_txnid,
158 const DBT* left_key,
159 const DBT* right_key,
160 uint64_t blocking_txnid);
161
162static void tokudb_lock_wait_needed_callback(
163 void* arg,
164 uint64_t requesting_txnid,
165 uint64_t blocking_txnid);
166
167#define ASSERT_MSGLEN 1024
168
169void toku_hton_assert_fail(
170 const char* expr_as_string,
171 const char* fun,
172 const char* file,
173 int line,
174 int caller_errno) {
175
176 char msg[ASSERT_MSGLEN];
177 if (db_env) {
178 snprintf(msg, ASSERT_MSGLEN, "Handlerton: %s ", expr_as_string);
179 db_env->crash(db_env, msg, fun, file, line,caller_errno);
180 } else {
181 snprintf(
182 msg,
183 ASSERT_MSGLEN,
184 "Handlerton assertion failed, no env, %s, %d, %s, %s (errno=%d)\n",
185 file,
186 line,
187 fun,
188 expr_as_string,
189 caller_errno);
190 perror(msg);
191 fflush(stderr);
192 }
193 abort();
194}
195
196//my_bool tokudb_shared_data = false;
197static uint32_t tokudb_init_flags =
198 DB_CREATE | DB_THREAD | DB_PRIVATE |
199 DB_INIT_LOCK |
200 DB_INIT_MPOOL |
201 DB_INIT_TXN |
202 DB_INIT_LOG |
203 DB_RECOVER;
204static uint32_t tokudb_env_flags = 0;
205// static uint32_t tokudb_lock_type = DB_LOCK_DEFAULT;
206// static ulong tokudb_log_buffer_size = 0;
207// static ulong tokudb_log_file_size = 0;
208static char* tokudb_home;
209// static long tokudb_lock_scan_time = 0;
210// static ulong tokudb_region_size = 0;
211// static ulong tokudb_cache_parts = 1;
212const char* tokudb_hton_name = "TokuDB";
213
214#if defined(_WIN32)
215extern "C" {
216#include "ydb.h"
217}
218#endif
219
220// A flag set if the handlerton is in an initialized, usable state,
221// plus a reader-write lock to protect it without serializing reads.
222// Since we don't have static initializers for the opaque rwlock type,
223// use constructor and destructor functions to create and destroy
224// the lock before and after main(), respectively.
225int tokudb_hton_initialized;
226
227// tokudb_hton_initialized_lock can not be instrumented as it must be
228// initialized before mysql_mutex_register() call to protect
229// some globals from race condition.
230tokudb::thread::rwlock_t tokudb_hton_initialized_lock;
231
232static SHOW_VAR *toku_global_status_variables = NULL;
233static uint64_t toku_global_status_max_rows;
234static TOKU_ENGINE_STATUS_ROW_S* toku_global_status_rows = NULL;
235
236static void handle_ydb_error(int error) {
237 switch (error) {
238 case TOKUDB_HUGE_PAGES_ENABLED:
239 sql_print_error("************************************************************");
240 sql_print_error(" ");
241 sql_print_error(" @@@@@@@@@@@ ");
242 sql_print_error(" @@' '@@ ");
243 sql_print_error(" @@ _ _ @@ ");
244 sql_print_error(" | (.) (.) | ");
245 sql_print_error(" | ` | ");
246 sql_print_error(" | > ' | ");
247 sql_print_error(" | .----. | ");
248 sql_print_error(" .. |.----.| .. ");
249 sql_print_error(" .. ' ' .. ");
250 sql_print_error(" .._______,. ");
251 sql_print_error(" ");
252 sql_print_error("%s will not run with transparent huge pages enabled. ", tokudb_hton_name);
253 sql_print_error("Please disable them to continue. ");
254 sql_print_error("(echo never > /sys/kernel/mm/transparent_hugepage/enabled) ");
255 sql_print_error(" ");
256 sql_print_error("************************************************************");
257 break;
258 case TOKUDB_UPGRADE_FAILURE:
259 sql_print_error(
260 "%s upgrade failed. A clean shutdown of the previous version is "
261 "required.",
262 tokudb_hton_name);
263 break;
264 default:
265 sql_print_error("%s unknown error %d", tokudb_hton_name, error);
266 break;
267 }
268}
269
270static int tokudb_set_product_name(void) {
271 size_t n = strlen(tokudb_hton_name);
272 char tokudb_product_name[n+1];
273 memset(tokudb_product_name, 0, sizeof tokudb_product_name);
274 for (size_t i = 0; i < n; i++)
275 tokudb_product_name[i] = tolower(tokudb_hton_name[i]);
276 int r = db_env_set_toku_product_name(tokudb_product_name);
277 return r;
278}
279
280static int tokudb_init_func(void *p) {
281 TOKUDB_DBUG_ENTER("%p", p);
282 int r;
283
284 // 3938: lock the handlerton's initialized status flag for writing
285 rwlock_t_lock_write(tokudb_hton_initialized_lock);
286
287#ifdef HAVE_PSI_INTERFACE
288 /* Register TokuDB mutex keys with MySQL performance schema */
289 int count;
290
291 count = array_elements(all_tokudb_mutexes);
292 mysql_mutex_register("tokudb", all_tokudb_mutexes, count);
293
294 count = array_elements(all_tokudb_rwlocks);
295 mysql_rwlock_register("tokudb", all_tokudb_rwlocks, count);
296
297 tokudb_map_mutex.reinit(tokudb_map_mutex_key);
298#endif /* HAVE_PSI_INTERFACE */
299
300 db_env = NULL;
301 tokudb_hton = (handlerton*)p;
302
303 if (tokudb::sysvars::check_jemalloc) {
304 typedef int (*mallctl_type)(
305 const char*,
306 void*,
307 size_t*,
308 void*,
309 size_t);
310 mallctl_type mallctl_func;
311 mallctl_func= (mallctl_type)dlsym(RTLD_DEFAULT, "mallctl");
312 if (!mallctl_func) {
313 sql_print_error(
314 "%s is not initialized because jemalloc is not loaded",
315 tokudb_hton_name);
316 goto error;
317 }
318 char *ver;
319 size_t len = sizeof(ver);
320 mallctl_func("version", &ver, &len, NULL, 0);
321 /* jemalloc 2.2.5 crashes mysql-test */
322 if (strcmp(ver, "2.3.") < 0) {
323 sql_print_error(
324 "%s is not initialized because jemalloc is older than 2.3.0",
325 tokudb_hton_name);
326 goto error;
327 }
328 }
329
330 r = tokudb_set_product_name();
331 if (r) {
332 sql_print_error(
333 "%s can not set product name error %d",
334 tokudb_hton_name,
335 r);
336 goto error;
337 }
338
339 TOKUDB_SHARE::static_init();
340 tokudb::background::initialize();
341
342 tokudb_hton->state = SHOW_OPTION_YES;
343 // tokudb_hton->flags= HTON_CAN_RECREATE; // QQQ this came from skeleton
344 tokudb_hton->flags = HTON_CLOSE_CURSORS_AT_COMMIT | HTON_SUPPORTS_EXTENDED_KEYS;
345
346#if defined(TOKU_INCLUDE_EXTENDED_KEYS) && TOKU_INCLUDE_EXTENDED_KEYS
347#if defined(HTON_SUPPORTS_EXTENDED_KEYS)
348 tokudb_hton->flags |= HTON_SUPPORTS_EXTENDED_KEYS;
349#endif
350#if defined(HTON_EXTENDED_KEYS)
351 tokudb_hton->flags |= HTON_EXTENDED_KEYS;
352#endif
353#endif
354#if defined(HTON_SUPPORTS_CLUSTERED_KEYS)
355 tokudb_hton->flags |= HTON_SUPPORTS_CLUSTERED_KEYS;
356#endif
357
358#if defined(TOKU_USE_DB_TYPE_TOKUDB) && TOKU_USE_DB_TYPE_TOKUDB
359 tokudb_hton->db_type = DB_TYPE_TOKUDB;
360#elif defined(TOKU_USE_DB_TYPE_UNKNOWN) && TOKU_USE_DB_TYPE_UNKNOWN
361 tokudb_hton->db_type = DB_TYPE_UNKNOWN;
362#else
363#error
364#endif
365
366 tokudb_hton->create = tokudb_create_handler;
367 tokudb_hton->close_connection = tokudb_close_connection;
368 tokudb_hton->kill_query = tokudb_kill_connection;
369
370 tokudb_hton->savepoint_offset = sizeof(SP_INFO_T);
371 tokudb_hton->savepoint_set = tokudb_savepoint;
372 tokudb_hton->savepoint_rollback = tokudb_rollback_to_savepoint;
373 tokudb_hton->savepoint_release = tokudb_release_savepoint;
374
375#if 100000 <= MYSQL_VERSION_ID
376 tokudb_hton->discover_table = tokudb_discover_table;
377 tokudb_hton->discover_table_existence = tokudb_discover_table_existence;
378#else
379 tokudb_hton->discover = tokudb_discover;
380#if defined(MYSQL_HANDLERTON_INCLUDE_DISCOVER2)
381 tokudb_hton->discover2 = tokudb_discover2;
382#endif
383#endif
384 tokudb_hton->commit = tokudb_commit;
385 tokudb_hton->rollback = tokudb_rollback;
386#if TOKU_INCLUDE_XA
387 tokudb_hton->prepare = tokudb_xa_prepare;
388 tokudb_hton->recover = tokudb_xa_recover;
389 tokudb_hton->commit_by_xid = tokudb_commit_by_xid;
390 tokudb_hton->rollback_by_xid = tokudb_rollback_by_xid;
391#endif
392
393 tokudb_hton->panic = tokudb_end;
394 tokudb_hton->flush_logs = tokudb_flush_logs;
395 tokudb_hton->show_status = tokudb_show_status;
396#if defined(TOKU_INCLUDE_HANDLERTON_HANDLE_FATAL_SIGNAL)
397 tokudb_hton->handle_fatal_signal = tokudb_handle_fatal_signal;
398#endif
399
400#if TOKU_INCLUDE_OPTION_STRUCTS
401 tokudb_hton->table_options = tokudb::sysvars::tokudb_table_options;
402 tokudb_hton->index_options = tokudb::sysvars::tokudb_index_options;
403#endif
404
405 if (!tokudb_home)
406 tokudb_home = mysql_real_data_home;
407 DBUG_PRINT("info", ("tokudb_home: %s", tokudb_home));
408
409 if ((r = db_env_create(&db_env, 0))) {
410 DBUG_PRINT("info", ("db_env_create %d\n", r));
411 handle_ydb_error(r);
412 goto error;
413 }
414
415 DBUG_PRINT("info", ("tokudb_env_flags: 0x%x\n", tokudb_env_flags));
416 r = db_env->set_flags(db_env, tokudb_env_flags, 1);
417 if (r) { // QQQ
418 TOKUDB_TRACE_FOR_FLAGS(
419 TOKUDB_DEBUG_INIT,
420 "WARNING: flags=%x r=%d",
421 tokudb_env_flags,
422 r);
423 // goto error;
424 }
425
426 // config error handling
427 db_env->set_errcall(db_env, tokudb_print_error);
428 db_env->set_errpfx(db_env, tokudb_hton_name);
429
430 // Handle deprecated options
431 if (tokudb::sysvars::pk_insert_mode(NULL) != 1) {
432 TOKUDB_TRACE("Using tokudb_pk_insert_mode is deprecated and the "
433 "parameter may be removed in future releases. "
434 "tokudb_pk_insert_mode=0 is now forbidden. "
435 "See documentation and release notes for details");
436 if (tokudb::sysvars::pk_insert_mode(NULL) < 1)
437 tokudb::sysvars::set_pk_insert_mode(NULL, 1);
438 }
439
440 //
441 // set default comparison functions
442 //
443 r = db_env->set_default_bt_compare(db_env, tokudb_cmp_dbt_key);
444 if (r) {
445 DBUG_PRINT("info", ("set_default_bt_compare%d\n", r));
446 goto error;
447 }
448
449 {
450 char* tmp_dir = tokudb::sysvars::tmp_dir;
451 char* data_dir = tokudb::sysvars::data_dir;
452 if (data_dir == 0) {
453 data_dir = mysql_data_home;
454 }
455 if (tmp_dir == 0) {
456 tmp_dir = data_dir;
457 }
458 DBUG_PRINT("info", ("tokudb_data_dir: %s\n", data_dir));
459 db_env->set_data_dir(db_env, data_dir);
460 DBUG_PRINT("info", ("tokudb_tmp_dir: %s\n", tmp_dir));
461 db_env->set_tmp_dir(db_env, tmp_dir);
462 }
463
464 if (tokudb::sysvars::log_dir) {
465 DBUG_PRINT("info", ("tokudb_log_dir: %s\n", tokudb::sysvars::log_dir));
466 db_env->set_lg_dir(db_env, tokudb::sysvars::log_dir);
467 }
468
469 // config the cache table size to min(1/2 of physical memory, 1/8 of the
470 // process address space)
471 if (tokudb::sysvars::cache_size == 0) {
472 uint64_t physmem, maxdata;
473 physmem = toku_os_get_phys_memory_size();
474 tokudb::sysvars::cache_size = physmem / 2;
475 r = toku_os_get_max_process_data_size(&maxdata);
476 if (r == 0) {
477 if (tokudb::sysvars::cache_size > maxdata / 8)
478 tokudb::sysvars::cache_size = maxdata / 8;
479 }
480 }
481 if (tokudb::sysvars::cache_size) {
482 DBUG_PRINT(
483 "info",
484 ("tokudb_cache_size: %lld\n", tokudb::sysvars::cache_size));
485 r = db_env->set_cachesize(
486 db_env,
487 (uint32_t)(tokudb::sysvars::cache_size >> 30),
488 (uint32_t)(tokudb::sysvars::cache_size %
489 (1024L * 1024L * 1024L)), 1);
490 if (r) {
491 DBUG_PRINT("info", ("set_cachesize %d\n", r));
492 goto error;
493 }
494 }
495 if (tokudb::sysvars::max_lock_memory == 0) {
496 tokudb::sysvars::max_lock_memory = tokudb::sysvars::cache_size/8;
497 }
498 if (tokudb::sysvars::max_lock_memory) {
499 DBUG_PRINT(
500 "info",
501 ("tokudb_max_lock_memory: %lld\n",
502 tokudb::sysvars::max_lock_memory));
503 r = db_env->set_lk_max_memory(
504 db_env,
505 tokudb::sysvars::max_lock_memory);
506 if (r) {
507 DBUG_PRINT("info", ("set_lk_max_memory %d\n", r));
508 goto error;
509 }
510 }
511
512 uint32_t gbytes, bytes; int parts;
513 r = db_env->get_cachesize(db_env, &gbytes, &bytes, &parts);
514 TOKUDB_TRACE_FOR_FLAGS(
515 TOKUDB_DEBUG_INIT,
516 "tokudb_cache_size=%lld r=%d",
517 ((unsigned long long) gbytes << 30) + bytes,
518 r);
519
520 r = db_env->set_client_pool_threads(
521 db_env,
522 tokudb::sysvars::client_pool_threads);
523 if (r) {
524 DBUG_PRINT("info", ("set_client_pool_threads %d\n", r));
525 goto error;
526 }
527
528 r = db_env->set_cachetable_pool_threads(
529 db_env,
530 tokudb::sysvars::cachetable_pool_threads);
531 if (r) {
532 DBUG_PRINT("info", ("set_cachetable_pool_threads %d\n", r));
533 goto error;
534 }
535
536 r = db_env->set_checkpoint_pool_threads(
537 db_env,
538 tokudb::sysvars::checkpoint_pool_threads);
539 if (r) {
540 DBUG_PRINT("info", ("set_checkpoint_pool_threads %d\n", r));
541 goto error;
542 }
543
544 if (db_env->set_redzone) {
545 r = db_env->set_redzone(db_env, tokudb::sysvars::fs_reserve_percent);
546 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_INIT, "set_redzone r=%d", r);
547 }
548 TOKUDB_TRACE_FOR_FLAGS(
549 TOKUDB_DEBUG_INIT,
550 "env open:flags=%x",
551 tokudb_init_flags);
552
553 r = db_env->set_generate_row_callback_for_put(db_env, generate_row_for_put);
554 assert_always(r == 0);
555
556 r = db_env->set_generate_row_callback_for_del(db_env, generate_row_for_del);
557 assert_always(r == 0);
558
559 db_env->set_update(db_env, tokudb_update_fun);
560
561 db_env_set_direct_io(tokudb::sysvars::directio == TRUE);
562
563 db_env_set_compress_buffers_before_eviction(
564 tokudb::sysvars::compress_buffers_before_eviction == TRUE);
565
566 db_env->change_fsync_log_period(db_env, tokudb::sysvars::fsync_log_period);
567
568 db_env->set_lock_timeout_callback(db_env, tokudb_lock_timeout_callback);
569 db_env->set_dir_per_db(db_env, tokudb::sysvars::dir_per_db);
570 db_env->set_lock_wait_callback(db_env, tokudb_lock_wait_needed_callback);
571
572 db_env->set_loader_memory_size(
573 db_env,
574 tokudb_get_loader_memory_size_callback);
575
576 db_env->set_check_thp(db_env, tokudb::sysvars::check_jemalloc);
577
578 r = db_env->open(
579 db_env,
580 tokudb_home,
581 tokudb_init_flags,
582 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
583
584 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_INIT, "env opened:return=%d", r);
585
586 if (r) {
587 DBUG_PRINT("info", ("env->open %d", r));
588 handle_ydb_error(r);
589 goto error;
590 }
591
592 r = db_env->checkpointing_set_period(
593 db_env,
594 tokudb::sysvars::checkpointing_period);
595 assert_always(r == 0);
596
597 r = db_env->cleaner_set_period(db_env, tokudb::sysvars::cleaner_period);
598 assert_always(r == 0);
599
600 r = db_env->cleaner_set_iterations(
601 db_env,
602 tokudb::sysvars::cleaner_iterations);
603 assert_always(r == 0);
604
605 r = db_env->set_lock_timeout(
606 db_env,
607 DEFAULT_TOKUDB_LOCK_TIMEOUT,
608 tokudb_get_lock_wait_time_callback);
609 assert_always(r == 0);
610
611 r = db_env->evictor_set_enable_partial_eviction(
612 db_env,
613 tokudb::sysvars::enable_partial_eviction);
614 assert_always(r == 0);
615
616 db_env->set_killed_callback(
617 db_env,
618 DEFAULT_TOKUDB_KILLED_TIME,
619 tokudb_get_killed_time_callback,
620 tokudb_killed_callback);
621
622 r = db_env->get_engine_status_num_rows(
623 db_env,
624 &toku_global_status_max_rows);
625 assert_always(r == 0);
626
627 {
628 const myf mem_flags =
629 MY_FAE|MY_WME|MY_ZEROFILL|MY_ALLOW_ZERO_PTR|MY_FREE_ON_ERROR;
630 toku_global_status_variables =
631 (SHOW_VAR*)tokudb::memory::malloc(
632 sizeof(*toku_global_status_variables) *
633 toku_global_status_max_rows,
634 mem_flags);
635 toku_global_status_rows =
636 (TOKU_ENGINE_STATUS_ROW_S*)tokudb::memory::malloc(
637 sizeof(*toku_global_status_rows)*
638 toku_global_status_max_rows,
639 mem_flags);
640 }
641
642 tokudb_primary_key_bytes_inserted = create_partitioned_counter();
643
644#if TOKU_THDVAR_MEMALLOC_BUG
645 init_tree(&tokudb_map, 0, 0, 0, tokudb_map_pair_cmp, true, NULL, NULL);
646#endif
647
648 if (tokudb::sysvars::strip_frm_data) {
649 r = tokudb::metadata::strip_frm_data(db_env);
650 if (r) {
651 DBUG_PRINT("info", ("env->open %d", r));
652 handle_ydb_error(r);
653 goto error;
654 }
655 }
656
657 //3938: succeeded, set the init status flag and unlock
658 tokudb_hton_initialized = 1;
659 tokudb_hton_initialized_lock.unlock();
660 DBUG_RETURN(false);
661
662error:
663 if (db_env) {
664 int rr= db_env->close(db_env, 0);
665 assert_always(rr==0);
666 db_env = 0;
667 }
668
669 // 3938: failed to initialized, drop the flag and lock
670 tokudb_hton_initialized = 0;
671 tokudb_hton_initialized_lock.unlock();
672 DBUG_RETURN(true);
673}
674
675static int tokudb_done_func(void* p) {
676 TOKUDB_DBUG_ENTER("");
677 tokudb::memory::free(toku_global_status_variables);
678 toku_global_status_variables = NULL;
679 tokudb::memory::free(toku_global_status_rows);
680 toku_global_status_rows = NULL;
681 TOKUDB_DBUG_RETURN(0);
682}
683
684static handler* tokudb_create_handler(
685 handlerton* hton,
686 TABLE_SHARE* table,
687 MEM_ROOT* mem_root) {
688 return new(mem_root) ha_tokudb(hton, table);
689}
690
691int tokudb_end(handlerton* hton, ha_panic_function type) {
692 TOKUDB_DBUG_ENTER("");
693 int error = 0;
694
695 // 3938: if we finalize the storage engine plugin, it is no longer
696 // initialized. grab a writer lock for the duration of the
697 // call, so we can drop the flag and destroy the mutexes
698 // in isolation.
699 rwlock_t_lock_write(tokudb_hton_initialized_lock);
700 assert_always(tokudb_hton_initialized);
701
702 tokudb::background::destroy();
703 TOKUDB_SHARE::static_destroy();
704
705 if (db_env) {
706 if (tokudb_init_flags & DB_INIT_LOG)
707 tokudb_cleanup_log_files();
708
709 // count the total number of prepared txn's that we discard
710 long total_prepared = 0;
711#if TOKU_INCLUDE_XA
712 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "begin XA cleanup");
713 while (1) {
714 // get xid's
715 const long n_xid = 1;
716 TOKU_XA_XID xids[n_xid];
717 long n_prepared = 0;
718 error = db_env->txn_xa_recover(
719 db_env,
720 xids,
721 n_xid,
722 &n_prepared,
723 total_prepared == 0 ? DB_FIRST : DB_NEXT);
724 assert_always(error == 0);
725 if (n_prepared == 0)
726 break;
727 // discard xid's
728 for (long i = 0; i < n_xid; i++) {
729 DB_TXN *txn = NULL;
730 error = db_env->get_txn_from_xid(db_env, &xids[i], &txn);
731 assert_always(error == 0);
732 error = txn->discard(txn, 0);
733 assert_always(error == 0);
734 }
735 total_prepared += n_prepared;
736 }
737 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "end XA cleanup");
738#endif
739 error = db_env->close(
740 db_env,
741 total_prepared > 0 ? TOKUFT_DIRTY_SHUTDOWN : 0);
742#if TOKU_INCLUDE_XA
743 if (error != 0 && total_prepared > 0) {
744 sql_print_error(
745 "%s: %ld prepared txns still live, please shutdown, error %d",
746 tokudb_hton_name,
747 total_prepared,
748 error);
749 } else
750#endif
751 assert_always(error == 0);
752 db_env = NULL;
753 }
754
755 if (tokudb_primary_key_bytes_inserted) {
756 destroy_partitioned_counter(tokudb_primary_key_bytes_inserted);
757 tokudb_primary_key_bytes_inserted = NULL;
758 }
759
760#if TOKU_THDVAR_MEMALLOC_BUG
761 delete_tree(&tokudb_map);
762#endif
763
764 // 3938: drop the initialized flag and unlock
765 tokudb_hton_initialized = 0;
766 tokudb_hton_initialized_lock.unlock();
767
768 TOKUDB_DBUG_RETURN(error);
769}
770
771static int tokudb_close_connection(handlerton* hton, THD* thd) {
772 int error = 0;
773 tokudb_trx_data* trx = (tokudb_trx_data*)thd_get_ha_data(thd, tokudb_hton);
774 if (trx && trx->checkpoint_lock_taken) {
775 error = db_env->checkpointing_resume(db_env);
776 }
777 tokudb::memory::free(trx);
778#if TOKU_THDVAR_MEMALLOC_BUG
779 mutex_t_lock(tokudb_map_mutex);
780 struct tokudb_map_pair key = {thd, NULL};
781 struct tokudb_map_pair* found_key =
782 (struct tokudb_map_pair*)tree_search(&tokudb_map, &key, NULL);
783
784 if (found_key) {
785 tokudb::memory::free(found_key->last_lock_timeout);
786 tree_delete(&tokudb_map, found_key, sizeof(*found_key), NULL);
787 }
788 mutex_t_unlock(tokudb_map_mutex);
789#endif
790 return error;
791}
792
793void tokudb_kill_connection(handlerton *hton, THD *thd,
794 enum thd_kill_levels level) {
795 TOKUDB_DBUG_ENTER("");
796 db_env->kill_waiter(db_env, thd);
797 DBUG_VOID_RETURN;
798}
799
800bool tokudb_flush_logs(handlerton * hton) {
801 TOKUDB_DBUG_ENTER("");
802 int error;
803 bool result = 0;
804
805 if (tokudb::sysvars::checkpoint_on_flush_logs) {
806 //
807 // take the checkpoint
808 //
809 error = db_env->txn_checkpoint(db_env, 0, 0, 0);
810 if (error) {
811 my_error(ER_ERROR_DURING_CHECKPOINT, MYF(0), error);
812 result = 1;
813 goto exit;
814 }
815 }
816 else {
817 error = db_env->log_flush(db_env, NULL);
818 assert_always(error == 0);
819 }
820
821 result = 0;
822exit:
823 TOKUDB_DBUG_RETURN(result);
824}
825
826
827typedef struct txn_progress_info {
828 char status[200];
829 THD* thd;
830} *TXN_PROGRESS_INFO;
831
832static void txn_progress_func(TOKU_TXN_PROGRESS progress, void* extra) {
833 TXN_PROGRESS_INFO progress_info = (TXN_PROGRESS_INFO)extra;
834 int r = sprintf(
835 progress_info->status,
836 "%sprocessing %s of transaction, %" PRId64 " out of %" PRId64,
837 progress->stalled_on_checkpoint ? "Writing committed changes to disk, " : "",
838 progress->is_commit ? "commit" : "abort",
839 progress->entries_processed,
840 progress->entries_total);
841 assert_always(r >= 0);
842 thd_proc_info(progress_info->thd, progress_info->status);
843}
844
845static void commit_txn_with_progress(DB_TXN* txn, uint32_t flags, THD* thd) {
846 const char *orig_proc_info = tokudb_thd_get_proc_info(thd);
847 struct txn_progress_info info;
848 info.thd = thd;
849 int r = txn->commit_with_progress(txn, flags, txn_progress_func, &info);
850 if (r != 0) {
851 sql_print_error(
852 "%s: tried committing transaction %p and got error code %d",
853 tokudb_hton_name,
854 txn,
855 r);
856 }
857 assert_always(r == 0);
858 thd_proc_info(thd, orig_proc_info);
859}
860
861static void abort_txn_with_progress(DB_TXN* txn, THD* thd) {
862 const char *orig_proc_info = tokudb_thd_get_proc_info(thd);
863 struct txn_progress_info info;
864 info.thd = thd;
865 int r = txn->abort_with_progress(txn, txn_progress_func, &info);
866 if (r != 0) {
867 sql_print_error(
868 "%s: tried aborting transaction %p and got error code %d",
869 tokudb_hton_name,
870 txn,
871 r);
872 }
873 assert_always(r == 0);
874 thd_proc_info(thd, orig_proc_info);
875}
876
877static void tokudb_cleanup_handlers(tokudb_trx_data *trx, DB_TXN *txn) {
878 LIST *e;
879 while ((e = trx->handlers)) {
880 trx->handlers = list_delete(trx->handlers, e);
881 ha_tokudb *handler = (ha_tokudb *) e->data;
882 handler->cleanup_txn(txn);
883 }
884}
885
886#if MYSQL_VERSION_ID >= 50600
887extern "C" enum durability_properties thd_get_durability_property(
888 const MYSQL_THD thd);
889#endif
890
891// Determine if an fsync is used when a transaction is committed.
892static bool tokudb_sync_on_commit(THD* thd, tokudb_trx_data* trx, DB_TXN* txn) {
893#if MYSQL_VERSION_ID >= 50600
894 // Check the client durability property which is set during 2PC
895 if (thd_get_durability_property(thd) == HA_IGNORE_DURABILITY)
896 return false;
897#endif
898#if defined(MARIADB_BASE_VERSION)
899 // Check is the txn is prepared and the binlog is open
900 if (txn->is_prepared(txn) && mysql_bin_log.is_open())
901 return false;
902#endif
903 if (tokudb::sysvars::fsync_log_period > 0)
904 return false;
905 return tokudb::sysvars::commit_sync(thd) != 0;
906}
907
908static int tokudb_commit(handlerton * hton, THD * thd, bool all) {
909 TOKUDB_DBUG_ENTER("%u", all);
910 DBUG_PRINT("trans", ("ending transaction %s", all ? "all" : "stmt"));
911 tokudb_trx_data *trx = (tokudb_trx_data *) thd_get_ha_data(thd, hton);
912 DB_TXN **txn = all ? &trx->all : &trx->stmt;
913 DB_TXN *this_txn = *txn;
914 if (this_txn) {
915 uint32_t syncflag =
916 tokudb_sync_on_commit(thd, trx, this_txn) ? 0 : DB_TXN_NOSYNC;
917 TOKUDB_TRACE_FOR_FLAGS(
918 TOKUDB_DEBUG_TXN,
919 "commit trx %u txn %p %" PRIu64 " syncflag %u",
920 all,
921 this_txn, this_txn->id64(this_txn),
922 syncflag);
923 // test hook to induce a crash on a debug build
924 DBUG_EXECUTE_IF("tokudb_crash_commit_before", DBUG_SUICIDE(););
925 tokudb_cleanup_handlers(trx, this_txn);
926 commit_txn_with_progress(this_txn, syncflag, thd);
927 // test hook to induce a crash on a debug build
928 DBUG_EXECUTE_IF("tokudb_crash_commit_after", DBUG_SUICIDE(););
929 *txn = NULL;
930 trx->sub_sp_level = NULL;
931 if (this_txn == trx->sp_level || trx->all == NULL) {
932 trx->sp_level = NULL;
933 }
934 } else {
935 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_TXN, "nothing to commit %d", all);
936 }
937 reset_stmt_progress(&trx->stmt_progress);
938 TOKUDB_DBUG_RETURN(0);
939}
940
941static int tokudb_rollback(handlerton * hton, THD * thd, bool all) {
942 TOKUDB_DBUG_ENTER("%u", all);
943 DBUG_PRINT("trans", ("aborting transaction %s", all ? "all" : "stmt"));
944 tokudb_trx_data *trx = (tokudb_trx_data *) thd_get_ha_data(thd, hton);
945 DB_TXN **txn = all ? &trx->all : &trx->stmt;
946 DB_TXN *this_txn = *txn;
947 if (this_txn) {
948 TOKUDB_TRACE_FOR_FLAGS(
949 TOKUDB_DEBUG_TXN,
950 "rollback %u txn %p %" PRIu64,
951 all,
952 this_txn, this_txn->id64(this_txn));
953 tokudb_cleanup_handlers(trx, this_txn);
954 abort_txn_with_progress(this_txn, thd);
955 *txn = NULL;
956 trx->sub_sp_level = NULL;
957 if (this_txn == trx->sp_level || trx->all == NULL) {
958 trx->sp_level = NULL;
959 }
960 } else {
961 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_TXN, "abort0");
962 }
963 reset_stmt_progress(&trx->stmt_progress);
964 TOKUDB_DBUG_RETURN(0);
965}
966
967#if TOKU_INCLUDE_XA
968static bool tokudb_sync_on_prepare(void) {
969 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "enter");
970 // skip sync of log if fsync log period > 0
971 if (tokudb::sysvars::fsync_log_period > 0) {
972 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit");
973 return false;
974 } else {
975 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit");
976 return true;
977 }
978}
979
980static int tokudb_xa_prepare(handlerton* hton, THD* thd, bool all) {
981 TOKUDB_DBUG_ENTER("%u", all);
982 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "enter");
983 int r = 0;
984
985 // if tokudb_support_xa is disable, just return
986 if (!tokudb::sysvars::support_xa(thd)) {
987 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %d", r);
988 TOKUDB_DBUG_RETURN(r);
989 }
990
991 DBUG_PRINT("trans", ("preparing transaction %s", all ? "all" : "stmt"));
992 tokudb_trx_data *trx = (tokudb_trx_data *) thd_get_ha_data(thd, hton);
993 DB_TXN* txn = all ? trx->all : trx->stmt;
994 if (txn) {
995 uint32_t syncflag = tokudb_sync_on_prepare() ? 0 : DB_TXN_NOSYNC;
996 TOKUDB_TRACE_FOR_FLAGS(
997 TOKUDB_DEBUG_XA,
998 "doing txn prepare:%d:%p %" PRIu64,
999 all,
1000 txn, txn->id64(txn));
1001 // a TOKU_XA_XID is identical to a MYSQL_XID
1002 TOKU_XA_XID thd_xid;
1003 thd_get_xid(thd, (MYSQL_XID*) &thd_xid);
1004 // test hook to induce a crash on a debug build
1005 DBUG_EXECUTE_IF("tokudb_crash_prepare_before", DBUG_SUICIDE(););
1006 r = txn->xa_prepare(txn, &thd_xid, syncflag);
1007 // test hook to induce a crash on a debug build
1008 DBUG_EXECUTE_IF("tokudb_crash_prepare_after", DBUG_SUICIDE(););
1009
1010 // XA log entries can be interleaved in the binlog since XA prepare on the master
1011 // flushes to the binlog. There can be log entries from different clients pushed
1012 // into the binlog before XA commit is executed on the master. Therefore, the slave
1013 // thread must be able to juggle multiple XA transactions. Tokudb does this by
1014 // zapping the client transaction context on the slave when executing the XA prepare
1015 // and expecting to process XA commit with commit_by_xid (which supplies the XID so
1016 // that the transaction can be looked up and committed).
1017 if (r == 0 && all && thd->slave_thread) {
1018 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "zap txn context %u", thd_sql_command(thd));
1019 if (thd_sql_command(thd) == SQLCOM_XA_PREPARE) {
1020 trx->all = NULL;
1021 trx->sub_sp_level = NULL;
1022 trx->sp_level = NULL;
1023 }
1024 }
1025 } else {
1026 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "nothing to prepare %d", all);
1027 }
1028 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %d", r);
1029 TOKUDB_DBUG_RETURN(r);
1030}
1031
1032static int tokudb_xa_recover(handlerton* hton, XID* xid_list, uint len) {
1033 TOKUDB_DBUG_ENTER("");
1034 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "enter");
1035 int r = 0;
1036 if (len == 0 || xid_list == NULL) {
1037 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %d", 0);
1038 TOKUDB_DBUG_RETURN(0);
1039 }
1040 long num_returned = 0;
1041 r = db_env->txn_xa_recover(
1042 db_env,
1043 (TOKU_XA_XID*)xid_list,
1044 len,
1045 &num_returned,
1046 DB_NEXT);
1047 assert_always(r == 0);
1048 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %ld", num_returned);
1049 TOKUDB_DBUG_RETURN((int)num_returned);
1050}
1051
1052static int tokudb_commit_by_xid(handlerton* hton, XID* xid) {
1053 TOKUDB_DBUG_ENTER("");
1054 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "enter");
1055 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "xid %p", xid);
1056 int r = 0;
1057 DB_TXN* txn = NULL;
1058 TOKU_XA_XID* toku_xid = (TOKU_XA_XID*)xid;
1059
1060 r = db_env->get_txn_from_xid(db_env, toku_xid, &txn);
1061 if (r) { goto cleanup; }
1062
1063 r = txn->commit(txn, 0);
1064 if (r) { goto cleanup; }
1065
1066 r = 0;
1067cleanup:
1068 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %d", r);
1069 TOKUDB_DBUG_RETURN(r);
1070}
1071
1072static int tokudb_rollback_by_xid(handlerton* hton, XID* xid) {
1073 TOKUDB_DBUG_ENTER("");
1074 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "enter");
1075 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "xid %p", xid);
1076 int r = 0;
1077 DB_TXN* txn = NULL;
1078 TOKU_XA_XID* toku_xid = (TOKU_XA_XID*)xid;
1079
1080 r = db_env->get_txn_from_xid(db_env, toku_xid, &txn);
1081 if (r) { goto cleanup; }
1082
1083 r = txn->abort(txn);
1084 if (r) { goto cleanup; }
1085
1086 r = 0;
1087cleanup:
1088 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_XA, "exit %d", r);
1089 TOKUDB_DBUG_RETURN(r);
1090}
1091
1092#endif
1093
1094static int tokudb_savepoint(handlerton * hton, THD * thd, void *savepoint) {
1095 TOKUDB_DBUG_ENTER("%p", savepoint);
1096 int error;
1097 SP_INFO save_info = (SP_INFO)savepoint;
1098 tokudb_trx_data *trx = (tokudb_trx_data *) thd_get_ha_data(thd, hton);
1099 if (thd->in_sub_stmt) {
1100 assert_always(trx->stmt);
1101 error = txn_begin(
1102 db_env,
1103 trx->sub_sp_level,
1104 &(save_info->txn),
1105 DB_INHERIT_ISOLATION,
1106 thd);
1107 if (error) {
1108 goto cleanup;
1109 }
1110 trx->sub_sp_level = save_info->txn;
1111 save_info->in_sub_stmt = true;
1112 } else {
1113 error = txn_begin(
1114 db_env,
1115 trx->sp_level,
1116 &(save_info->txn),
1117 DB_INHERIT_ISOLATION,
1118 thd);
1119 if (error) {
1120 goto cleanup;
1121 }
1122 trx->sp_level = save_info->txn;
1123 save_info->in_sub_stmt = false;
1124 }
1125 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_TXN, "begin txn %p", save_info->txn);
1126 save_info->trx = trx;
1127 error = 0;
1128cleanup:
1129 TOKUDB_DBUG_RETURN(error);
1130}
1131
1132static int tokudb_rollback_to_savepoint(
1133 handlerton* hton,
1134 THD* thd,
1135 void* savepoint) {
1136
1137 TOKUDB_DBUG_ENTER("%p", savepoint);
1138 int error;
1139 SP_INFO save_info = (SP_INFO)savepoint;
1140 DB_TXN* parent = NULL;
1141 DB_TXN* txn_to_rollback = save_info->txn;
1142
1143 tokudb_trx_data* trx = (tokudb_trx_data*)thd_get_ha_data(thd, hton);
1144 parent = txn_to_rollback->parent;
1145 TOKUDB_TRACE_FOR_FLAGS(
1146 TOKUDB_DEBUG_TXN,
1147 "rollback txn %p",
1148 txn_to_rollback);
1149 if (!(error = txn_to_rollback->abort(txn_to_rollback))) {
1150 if (save_info->in_sub_stmt) {
1151 trx->sub_sp_level = parent;
1152 }
1153 else {
1154 trx->sp_level = parent;
1155 }
1156 error = tokudb_savepoint(hton, thd, savepoint);
1157 }
1158 TOKUDB_DBUG_RETURN(error);
1159}
1160
1161static int tokudb_release_savepoint(
1162 handlerton* hton,
1163 THD* thd,
1164 void* savepoint) {
1165
1166 TOKUDB_DBUG_ENTER("%p", savepoint);
1167 int error = 0;
1168 SP_INFO save_info = (SP_INFO)savepoint;
1169 DB_TXN* parent = NULL;
1170 DB_TXN* txn_to_commit = save_info->txn;
1171
1172 tokudb_trx_data *trx = (tokudb_trx_data *) thd_get_ha_data(thd, hton);
1173 parent = txn_to_commit->parent;
1174 TOKUDB_TRACE_FOR_FLAGS(TOKUDB_DEBUG_TXN, "commit txn %p", txn_to_commit);
1175 DB_TXN *child = txn_to_commit->get_child(txn_to_commit);
1176 if (child == NULL && !(error = txn_to_commit->commit(txn_to_commit, 0))) {
1177 if (save_info->in_sub_stmt) {
1178 trx->sub_sp_level = parent;
1179 }
1180 else {
1181 trx->sp_level = parent;
1182 }
1183 }
1184 save_info->txn = NULL;
1185 TOKUDB_DBUG_RETURN(error);
1186}
1187
1188#if 100000 <= MYSQL_VERSION_ID
1189static int tokudb_discover_table(handlerton *hton, THD* thd, TABLE_SHARE *ts) {
1190 uchar *frmblob = 0;
1191 size_t frmlen;
1192 int res= tokudb_discover3(
1193 hton,
1194 thd,
1195 ts->db.str,
1196 ts->table_name.str,
1197 ts->normalized_path.str,
1198 &frmblob,
1199 &frmlen);
1200 if (!res)
1201 res= ts->init_from_binary_frm_image(thd, true, frmblob, frmlen);
1202
1203 my_free(frmblob);
1204 // discover_table should returns HA_ERR_NO_SUCH_TABLE for "not exists"
1205 return res == ENOENT ? HA_ERR_NO_SUCH_TABLE : res;
1206}
1207
1208static int tokudb_discover_table_existence(
1209 handlerton* hton,
1210 const char* db,
1211 const char* name) {
1212
1213 uchar *frmblob = 0;
1214 size_t frmlen;
1215 int res= tokudb_discover(hton, current_thd, db, name, &frmblob, &frmlen);
1216 my_free(frmblob);
1217 return res != ENOENT;
1218}
1219#endif
1220
1221static int tokudb_discover(
1222 handlerton* hton,
1223 THD* thd,
1224 const char* db,
1225 const char* name,
1226 uchar** frmblob,
1227 size_t* frmlen) {
1228
1229 return tokudb_discover2(hton, thd, db, name, true, frmblob, frmlen);
1230}
1231
1232static int tokudb_discover2(
1233 handlerton* hton,
1234 THD* thd,
1235 const char* db,
1236 const char* name,
1237 bool translate_name,
1238 uchar** frmblob,
1239 size_t*frmlen) {
1240
1241 char path[FN_REFLEN + 1];
1242 build_table_filename(
1243 path,
1244 sizeof(path) - 1,
1245 db,
1246 name,
1247 "",
1248 translate_name ? 0 : FN_IS_TMP);
1249 return tokudb_discover3(hton, thd, db, name, path, frmblob, frmlen);
1250}
1251
1252static int tokudb_discover3(
1253 handlerton* hton,
1254 THD* thd,
1255 const char* db,
1256 const char* name,
1257 const char* path,
1258 uchar** frmblob,
1259 size_t* frmlen) {
1260
1261 TOKUDB_DBUG_ENTER("%s %s %s", db, name, path);
1262 int error;
1263 DB* status_db = NULL;
1264 DB_TXN* txn = NULL;
1265 HA_METADATA_KEY curr_key = hatoku_frm_data;
1266 DBT key = {};
1267 DBT value = {};
1268 bool do_commit = false;
1269
1270#if 100000 <= MYSQL_VERSION_ID
1271 tokudb_trx_data* trx = (tokudb_trx_data*)thd_get_ha_data(thd, tokudb_hton);
1272 if (thd_sql_command(thd) == SQLCOM_CREATE_TABLE &&
1273 trx &&
1274 trx->sub_sp_level) {
1275 do_commit = false;
1276 txn = trx->sub_sp_level;
1277 } else {
1278 error = txn_begin(db_env, 0, &txn, 0, thd);
1279 if (error) { goto cleanup; }
1280 do_commit = true;
1281 }
1282#else
1283 error = txn_begin(db_env, 0, &txn, 0, thd);
1284 if (error) { goto cleanup; }
1285 do_commit = true;
1286#endif
1287
1288 error = open_status_dictionary(&status_db, path, txn);
1289 if (error) { goto cleanup; }
1290
1291 key.data = &curr_key;
1292 key.size = sizeof(curr_key);
1293
1294 error = status_db->getf_set(
1295 status_db,
1296 txn,
1297 0,
1298 &key,
1299 smart_dbt_callback_verify_frm,
1300 &value);
1301 if (error) {
1302 goto cleanup;
1303 }
1304
1305 *frmblob = (uchar *)value.data;
1306 *frmlen = value.size;
1307
1308 error = 0;
1309cleanup:
1310 if (status_db) {
1311 status_db->close(status_db,0);
1312 }
1313 if (do_commit && txn) {
1314 commit_txn(txn, 0);
1315 }
1316 TOKUDB_DBUG_RETURN(error);
1317}
1318
1319
1320#define STATPRINT(legend, val) if (legend != NULL && val != NULL) \
1321 stat_print(thd, \
1322 tokudb_hton_name, \
1323 strlen(tokudb_hton_name), \
1324 legend, \
1325 strlen(legend), \
1326 val, \
1327 strlen(val))
1328
1329extern sys_var* intern_find_sys_var(
1330 const char* str,
1331 uint length,
1332 bool no_error);
1333
1334static bool tokudb_show_engine_status(THD * thd, stat_print_fn * stat_print) {
1335 TOKUDB_DBUG_ENTER("");
1336 int error;
1337 uint64_t panic;
1338 const int panic_string_len = 1024;
1339 char panic_string[panic_string_len] = {'\0'};
1340 uint64_t num_rows;
1341 uint64_t max_rows;
1342 fs_redzone_state redzone_state;
1343 const int bufsiz = 1024;
1344 char buf[bufsiz];
1345
1346#if MYSQL_VERSION_ID < 50500
1347 {
1348 sys_var* version = intern_find_sys_var("version", 0, false);
1349 snprintf(
1350 buf,
1351 bufsiz,
1352 "%s",
1353 version->value_ptr(thd,
1354 (enum_var_type)0,
1355 (LEX_STRING*)NULL));
1356 STATPRINT("Version", buf);
1357 }
1358#endif
1359 error = db_env->get_engine_status_num_rows (db_env, &max_rows);
1360 TOKU_ENGINE_STATUS_ROW_S mystat[max_rows];
1361 error = db_env->get_engine_status(
1362 db_env,
1363 mystat,
1364 max_rows,
1365 &num_rows,
1366 &redzone_state,
1367 &panic,
1368 panic_string,
1369 panic_string_len,
1370 TOKU_ENGINE_STATUS);
1371
1372 if (strlen(panic_string)) {
1373 STATPRINT("Environment panic string", panic_string);
1374 }
1375 if (error == 0) {
1376 if (panic) {
1377 snprintf(buf, bufsiz, "%" PRIu64, panic);
1378 STATPRINT("Environment panic", buf);
1379 }
1380
1381 if(redzone_state == FS_BLOCKED) {
1382 STATPRINT(
1383 "*** URGENT WARNING ***", "FILE SYSTEM IS COMPLETELY FULL");
1384 snprintf(buf, bufsiz, "FILE SYSTEM IS COMPLETELY FULL");
1385 } else if (redzone_state == FS_GREEN) {
1386 snprintf(
1387 buf,
1388 bufsiz,
1389 "more than %d percent of total file system space",
1390 2 * tokudb::sysvars::fs_reserve_percent);
1391 } else if (redzone_state == FS_YELLOW) {
1392 snprintf(
1393 buf,
1394 bufsiz,
1395 "*** WARNING *** FILE SYSTEM IS GETTING FULL (less than %d "
1396 "percent free)",
1397 2 * tokudb::sysvars::fs_reserve_percent);
1398 } else if (redzone_state == FS_RED){
1399 snprintf(
1400 buf,
1401 bufsiz,
1402 "*** WARNING *** FILE SYSTEM IS GETTING VERY FULL (less than "
1403 "%d percent free): INSERTS ARE PROHIBITED",
1404 tokudb::sysvars::fs_reserve_percent);
1405 } else {
1406 snprintf(
1407 buf,
1408 bufsiz,
1409 "information unavailable, unknown redzone state %d",
1410 redzone_state);
1411 }
1412 STATPRINT ("disk free space", buf);
1413
1414 for (uint64_t row = 0; row < num_rows; row++) {
1415 switch (mystat[row].type) {
1416 case FS_STATE:
1417 snprintf(buf, bufsiz, "%" PRIu64 "", mystat[row].value.num);
1418 break;
1419 case UINT64:
1420 snprintf(buf, bufsiz, "%" PRIu64 "", mystat[row].value.num);
1421 break;
1422 case CHARSTR:
1423 snprintf(buf, bufsiz, "%s", mystat[row].value.str);
1424 break;
1425 case UNIXTIME: {
1426 time_t t = mystat[row].value.num;
1427 char tbuf[26];
1428 snprintf(buf, bufsiz, "%.24s", ctime_r(&t, tbuf));
1429 break;
1430 }
1431 case TOKUTIME: {
1432 double t = tokutime_to_seconds(mystat[row].value.num);
1433 snprintf(buf, bufsiz, "%.6f", t);
1434 break;
1435 }
1436 case PARCOUNT: {
1437 uint64_t v = read_partitioned_counter(
1438 mystat[row].value.parcount);
1439 snprintf(buf, bufsiz, "%" PRIu64, v);
1440 break;
1441 }
1442 case DOUBLE:
1443 snprintf(buf, bufsiz, "%.6f", mystat[row].value.dnum);
1444 break;
1445 default:
1446 snprintf(
1447 buf,
1448 bufsiz,
1449 "UNKNOWN STATUS TYPE: %d",
1450 mystat[row].type);
1451 break;
1452 }
1453 STATPRINT(mystat[row].legend, buf);
1454 }
1455 uint64_t bytes_inserted = read_partitioned_counter(
1456 tokudb_primary_key_bytes_inserted);
1457 snprintf(buf, bufsiz, "%" PRIu64, bytes_inserted);
1458 STATPRINT("handlerton: primary key bytes inserted", buf);
1459 }
1460 if (error) { my_errno = error; }
1461 TOKUDB_DBUG_RETURN(error);
1462}
1463
1464void tokudb_checkpoint_lock(THD * thd) {
1465 int error;
1466 const char *old_proc_info;
1467 tokudb_trx_data* trx = (tokudb_trx_data*)thd_get_ha_data(thd, tokudb_hton);
1468 if (!trx) {
1469 error = create_tokudb_trx_data_instance(&trx);
1470 //
1471 // can only fail due to memory allocation, so ok to assert
1472 //
1473 assert_always(!error);
1474 thd_set_ha_data(thd, tokudb_hton, trx);
1475 }
1476
1477 if (trx->checkpoint_lock_taken) {
1478 goto cleanup;
1479 }
1480 //
1481 // This can only fail if environment is not created, which is not possible
1482 // in handlerton
1483 //
1484 old_proc_info = tokudb_thd_get_proc_info(thd);
1485 thd_proc_info(thd, "Trying to grab checkpointing lock.");
1486 error = db_env->checkpointing_postpone(db_env);
1487 assert_always(!error);
1488 thd_proc_info(thd, old_proc_info);
1489
1490 trx->checkpoint_lock_taken = true;
1491cleanup:
1492 return;
1493}
1494
1495void tokudb_checkpoint_unlock(THD * thd) {
1496 int error;
1497 const char *old_proc_info;
1498 tokudb_trx_data* trx = (tokudb_trx_data*)thd_get_ha_data(thd, tokudb_hton);
1499 if (!trx) {
1500 error = 0;
1501 goto cleanup;
1502 }
1503 if (!trx->checkpoint_lock_taken) {
1504 error = 0;
1505 goto cleanup;
1506 }
1507 //
1508 // at this point, we know the checkpoint lock has been taken
1509 //
1510 old_proc_info = tokudb_thd_get_proc_info(thd);
1511 thd_proc_info(thd, "Trying to release checkpointing lock.");
1512 error = db_env->checkpointing_resume(db_env);
1513 assert_always(!error);
1514 thd_proc_info(thd, old_proc_info);
1515
1516 trx->checkpoint_lock_taken = false;
1517
1518cleanup:
1519 return;
1520}
1521
1522static bool tokudb_show_status(
1523 handlerton* hton,
1524 THD* thd,
1525 stat_print_fn* stat_print,
1526 enum ha_stat_type stat_type) {
1527
1528 switch (stat_type) {
1529 case HA_ENGINE_STATUS:
1530 return tokudb_show_engine_status(thd, stat_print);
1531 break;
1532 default:
1533 break;
1534 }
1535 return false;
1536}
1537
1538#if defined(TOKU_INCLUDE_HANDLERTON_HANDLE_FATAL_SIGNAL)
1539static void tokudb_handle_fatal_signal(
1540 TOKUDB_UNUSED(handlerton* hton),
1541 TOKUDB_UNUSD(THD* thd),
1542 int sig) {
1543
1544 if (tokudb_gdb_on_fatal) {
1545 db_env_try_gdb_stack_trace(tokudb_gdb_path);
1546 }
1547}
1548#endif
1549
1550static void tokudb_print_error(
1551 const DB_ENV* db_env,
1552 const char* db_errpfx,
1553 const char* buffer) {
1554 sql_print_error("%s: %s", db_errpfx, buffer);
1555}
1556
1557static void tokudb_cleanup_log_files(void) {
1558 TOKUDB_DBUG_ENTER("");
1559 char **names;
1560 int error;
1561
1562 if ((error = db_env->txn_checkpoint(db_env, 0, 0, 0)))
1563 my_error(ER_ERROR_DURING_CHECKPOINT, MYF(0), error);
1564
1565 if ((error = db_env->log_archive(db_env, &names, 0)) != 0) {
1566 DBUG_PRINT("error", ("log_archive failed (error %d)", error));
1567 db_env->err(db_env, error, "log_archive");
1568 DBUG_VOID_RETURN;
1569 }
1570
1571 if (names) {
1572 char **np;
1573 for (np = names; *np; ++np) {
1574#if 1
1575 if (TOKUDB_UNLIKELY(tokudb::sysvars::debug))
1576 TOKUDB_TRACE("cleanup:%s", *np);
1577#else
1578 my_delete(*np, MYF(MY_WME));
1579#endif
1580 }
1581
1582 free(names);
1583 }
1584
1585 DBUG_VOID_RETURN;
1586}
1587
1588// Split ./database/table-dictionary into database, table and dictionary strings
1589void tokudb_split_dname(
1590 const char* dname,
1591 String& database_name,
1592 String& table_name,
1593 String& dictionary_name) {
1594
1595 const char *splitter = strchr(dname, '/');
1596 if (splitter) {
1597 const char *database_ptr = splitter+1;
1598 const char *table_ptr = strchr(database_ptr, '/');
1599 if (table_ptr) {
1600 database_name.append(database_ptr, table_ptr - database_ptr);
1601 table_ptr += 1;
1602 const char *dictionary_ptr = strchr(table_ptr, '-');
1603 if (dictionary_ptr) {
1604 table_name.append(table_ptr, dictionary_ptr - table_ptr);
1605 dictionary_ptr += 1;
1606 dictionary_name.append(dictionary_ptr);
1607 } else {
1608 table_name.append(table_ptr);
1609 }
1610 } else {
1611 database_name.append(database_ptr);
1612 }
1613 }
1614}
1615
1616struct st_mysql_storage_engine tokudb_storage_engine = {
1617 MYSQL_HANDLERTON_INTERFACE_VERSION
1618};
1619
1620#if defined(TOKU_INCLUDE_LOCK_TIMEOUT_QUERY_STRING)
1621struct tokudb_search_txn_extra {
1622 bool match_found;
1623 uint64_t match_txn_id;
1624 uint64_t match_client_id;
1625};
1626
1627static int tokudb_search_txn_callback(
1628 DB_TXN* txn,
1629 iterate_row_locks_callback iterate_locks,
1630 void* locks_extra,
1631 void* extra) {
1632
1633 uint64_t txn_id = txn->id64(txn);
1634 uint64_t client_id;
1635 void *client_extra;
1636 txn->get_client_id(txn, &client_id, &client_extra);
1637 struct tokudb_search_txn_extra* e =
1638 reinterpret_cast<struct tokudb_search_txn_extra*>(extra);
1639 if (e->match_txn_id == txn_id) {
1640 e->match_found = true;
1641 e->match_client_id = client_id;
1642 return 1;
1643 }
1644 return 0;
1645}
1646
1647static bool tokudb_txn_id_to_client_id(
1648 THD* thd,
1649 uint64_t blocking_txnid,
1650 uint64_t* blocking_client_id) {
1651
1652 struct tokudb_search_txn_extra e = {
1653 false,
1654 blocking_txnid,
1655 0
1656 };
1657 db_env->iterate_live_transactions(db_env, tokudb_search_txn_callback, &e);
1658 if (e.match_found) {
1659 *blocking_client_id = e.match_client_id;
1660 }
1661 return e.match_found;
1662}
1663#endif
1664
1665static void tokudb_pretty_key(
1666 const DB* db,
1667 const DBT* key,
1668 const char* default_key,
1669 String* out) {
1670
1671 if (key->data == NULL) {
1672 out->append(default_key);
1673 } else {
1674 bool do_hexdump = true;
1675 if (do_hexdump) {
1676 // hexdump the key
1677 const unsigned char* data =
1678 reinterpret_cast<const unsigned char*>(key->data);
1679 for (size_t i = 0; i < key->size; i++) {
1680 char str[3];
1681 snprintf(str, sizeof str, "%2.2x", data[i]);
1682 out->append(str);
1683 }
1684 }
1685 }
1686}
1687
1688void tokudb_pretty_left_key(const DB* db, const DBT* key, String* out) {
1689 tokudb_pretty_key(db, key, "-infinity", out);
1690}
1691
1692void tokudb_pretty_right_key(const DB* db, const DBT* key, String* out) {
1693 tokudb_pretty_key(db, key, "+infinity", out);
1694}
1695
1696const char* tokudb_get_index_name(DB* db) {
1697 if (db != NULL) {
1698 return db->get_dname(db);
1699 } else {
1700 return "$ydb_internal";
1701 }
1702}
1703
1704static int tokudb_equal_key(const DBT *left_key, const DBT *right_key) {
1705 if (left_key->data == NULL || right_key->data == NULL ||
1706 left_key->size != right_key->size)
1707 return 0;
1708 else
1709 return memcmp(left_key->data, right_key->data, left_key->size) == 0;
1710}
1711
1712static void tokudb_lock_timeout_callback(
1713 DB* db,
1714 uint64_t requesting_txnid,
1715 const DBT* left_key,
1716 const DBT* right_key,
1717 uint64_t blocking_txnid) {
1718
1719 THD* thd = current_thd;
1720 if (!thd)
1721 return;
1722 ulong lock_timeout_debug = tokudb::sysvars::lock_timeout_debug(thd);
1723 if (lock_timeout_debug != 0) {
1724 // generate a JSON document with the lock timeout info
1725 String log_str;
1726 log_str.append("{");
1727 uint64_t mysql_thread_id = thd->thread_id;
1728 log_str.append("\"mysql_thread_id\":");
1729 log_str.append_ulonglong(mysql_thread_id);
1730 log_str.append(", \"dbname\":");
1731 log_str.append("\"");
1732 log_str.append(tokudb_get_index_name(db));
1733 log_str.append("\"");
1734 log_str.append(", \"requesting_txnid\":");
1735 log_str.append_ulonglong(requesting_txnid);
1736 log_str.append(", \"blocking_txnid\":");
1737 log_str.append_ulonglong(blocking_txnid);
1738 if (tokudb_equal_key(left_key, right_key)) {
1739 String key_str;
1740 tokudb_pretty_key(db, left_key, "?", &key_str);
1741 log_str.append(", \"key\":");
1742 log_str.append("\"");
1743 log_str.append(key_str);
1744 log_str.append("\"");
1745 } else {
1746 String left_str;
1747 tokudb_pretty_left_key(db, left_key, &left_str);
1748 log_str.append(", \"key_left\":");
1749 log_str.append("\"");
1750 log_str.append(left_str);
1751 log_str.append("\"");
1752 String right_str;
1753 tokudb_pretty_right_key(db, right_key, &right_str);
1754 log_str.append(", \"key_right\":");
1755 log_str.append("\"");
1756 log_str.append(right_str);
1757 log_str.append("\"");
1758 }
1759 log_str.append("}");
1760 // set last_lock_timeout
1761 if (lock_timeout_debug & 1) {
1762 char* old_lock_timeout = tokudb::sysvars::last_lock_timeout(thd);
1763 char* new_lock_timeout =
1764 tokudb::memory::strdup(log_str.c_ptr(), MY_FAE);
1765 tokudb::sysvars::set_last_lock_timeout(thd, new_lock_timeout);
1766#if TOKU_THDVAR_MEMALLOC_BUG
1767 mutex_t_lock(tokudb_map_mutex);
1768 struct tokudb_map_pair old_key = {thd, old_lock_timeout};
1769 tree_delete(&tokudb_map, &old_key, sizeof old_key, NULL);
1770 struct tokudb_map_pair new_key = {thd, new_lock_timeout};
1771 tree_insert(&tokudb_map, &new_key, sizeof new_key, NULL);
1772 mutex_t_unlock(tokudb_map_mutex);
1773#endif
1774 tokudb::memory::free(old_lock_timeout);
1775 }
1776 // dump to stderr
1777 if (lock_timeout_debug & 2) {
1778 sql_print_error(
1779 "%s: lock timeout %s",
1780 tokudb_hton_name,
1781 log_str.c_ptr());
1782 LEX_STRING *qs = thd_query_string(thd);
1783 sql_print_error(
1784 "%s: requesting_thread_id:%" PRIu64 " q:%.*s",
1785 tokudb_hton_name,
1786 mysql_thread_id,
1787 (int)qs->length,
1788 qs->str);
1789#if defined(TOKU_INCLUDE_LOCK_TIMEOUT_QUERY_STRING)
1790 uint64_t blocking_thread_id = 0;
1791 if (tokudb_txn_id_to_client_id(
1792 thd,
1793 blocking_txnid,
1794 &blocking_thread_id)) {
1795
1796 String blocking_qs;
1797 if (get_thread_query_string(
1798 blocking_thread_id,
1799 blocking_qs) == 0) {
1800
1801 sql_print_error(
1802 "%s: blocking_thread_id:%" PRIu64 " q:%.*s",
1803 tokudb_hton_name,
1804 blocking_thread_id,
1805 blocking_qs.length(),
1806 blocking_qs.c_ptr());
1807 }
1808 }
1809#endif
1810 }
1811 }
1812}
1813
1814extern "C" int thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd);
1815
1816struct tokudb_search_txn_thd {
1817 bool match_found;
1818 uint64_t match_txn_id;
1819 THD *match_client_thd;
1820};
1821
1822static int tokudb_search_txn_thd_callback(
1823 DB_TXN* txn,
1824 iterate_row_locks_callback iterate_locks,
1825 void* locks_extra,
1826 void* extra) {
1827
1828 uint64_t txn_id = txn->id64(txn);
1829 uint64_t client_id;
1830 void *client_extra;
1831 txn->get_client_id(txn, &client_id, &client_extra);
1832 struct tokudb_search_txn_thd* e =
1833 reinterpret_cast<struct tokudb_search_txn_thd*>(extra);
1834 if (e->match_txn_id == txn_id) {
1835 e->match_found = true;
1836 e->match_client_thd = reinterpret_cast<THD *>(client_extra);
1837 return 1;
1838 }
1839 return 0;
1840}
1841
1842static bool tokudb_txn_id_to_thd(
1843 uint64_t txnid,
1844 THD **out_thd) {
1845
1846 struct tokudb_search_txn_thd e = {
1847 false,
1848 txnid,
1849 0
1850 };
1851 db_env->iterate_live_transactions(db_env, tokudb_search_txn_thd_callback, &e);
1852 if (e.match_found) {
1853 *out_thd = e.match_client_thd;
1854 }
1855 return e.match_found;
1856}
1857
1858static void tokudb_lock_wait_needed_callback(
1859 void *arg,
1860 uint64_t requesting_txnid,
1861 uint64_t blocking_txnid) {
1862
1863 THD *requesting_thd;
1864 THD *blocking_thd;
1865 if (tokudb_txn_id_to_thd(requesting_txnid, &requesting_thd) &&
1866 tokudb_txn_id_to_thd(blocking_txnid, &blocking_thd)) {
1867 thd_rpl_deadlock_check (requesting_thd, blocking_thd);
1868 }
1869}
1870
1871// Retrieves variables for information_schema.global_status.
1872// Names (columnname) are automatically converted to upper case,
1873// and prefixed with "TOKUDB_"
1874static int show_tokudb_vars(THD *thd, SHOW_VAR *var, char *buff) {
1875 TOKUDB_DBUG_ENTER("");
1876
1877 int error;
1878 uint64_t panic;
1879 const int panic_string_len = 1024;
1880 char panic_string[panic_string_len] = {'\0'};
1881 fs_redzone_state redzone_state;
1882
1883 uint64_t num_rows;
1884 error = db_env->get_engine_status(
1885 db_env,
1886 toku_global_status_rows,
1887 toku_global_status_max_rows,
1888 &num_rows,
1889 &redzone_state,
1890 &panic,
1891 panic_string,
1892 panic_string_len,
1893 TOKU_GLOBAL_STATUS);
1894 //TODO: Maybe do something with the panic output?
1895 if (error == 0) {
1896 assert_always(num_rows <= toku_global_status_max_rows);
1897 //TODO: Maybe enable some of the items here: (copied from engine status
1898
1899 //TODO: (optionally) add redzone state, panic, panic string, etc.
1900 //Right now it's being ignored.
1901
1902 for (uint64_t row = 0; row < num_rows; row++) {
1903 SHOW_VAR &status_var = toku_global_status_variables[row];
1904 TOKU_ENGINE_STATUS_ROW_S &status_row = toku_global_status_rows[row];
1905
1906 status_var.name = status_row.columnname;
1907 switch (status_row.type) {
1908 case FS_STATE:
1909 case UINT64:
1910 status_var.type = SHOW_LONGLONG;
1911 status_var.value = (char*)&status_row.value.num;
1912 break;
1913 case CHARSTR:
1914 status_var.type = SHOW_CHAR;
1915 status_var.value = (char*)status_row.value.str;
1916 break;
1917 case UNIXTIME: {
1918 status_var.type = SHOW_CHAR;
1919 time_t t = status_row.value.num;
1920 char tbuf[26];
1921 // Reuse the memory in status_row. (It belongs to us).
1922 snprintf(
1923 status_row.value.datebuf,
1924 sizeof(status_row.value.datebuf),
1925 "%.24s",
1926 ctime_r(&t, tbuf));
1927 status_var.value = (char*)&status_row.value.datebuf[0];
1928 break;
1929 }
1930 case TOKUTIME:
1931 status_var.type = SHOW_DOUBLE;
1932 // Reuse the memory in status_row. (It belongs to us).
1933 status_row.value.dnum = tokutime_to_seconds(status_row.value.num);
1934 status_var.value = (char*)&status_row.value.dnum;
1935 break;
1936 case PARCOUNT: {
1937 status_var.type = SHOW_LONGLONG;
1938 uint64_t v = read_partitioned_counter(status_row.value.parcount);
1939 // Reuse the memory in status_row. (It belongs to us).
1940 status_row.value.num = v;
1941 status_var.value = (char*)&status_row.value.num;
1942 break;
1943 }
1944 case DOUBLE:
1945 status_var.type = SHOW_DOUBLE;
1946 status_var.value = (char*) &status_row.value.dnum;
1947 break;
1948 default:
1949 status_var.type = SHOW_CHAR;
1950 // Reuse the memory in status_row.datebuf. (It belongs to us).
1951 // UNKNOWN TYPE: %d fits in 26 bytes (sizeof datebuf) for any integer.
1952 snprintf(
1953 status_row.value.datebuf,
1954 sizeof(status_row.value.datebuf),
1955 "UNKNOWN TYPE: %d",
1956 status_row.type);
1957 status_var.value = (char*)&status_row.value.datebuf[0];
1958 break;
1959 }
1960 }
1961 // Sentinel value at end.
1962 toku_global_status_variables[num_rows].type = SHOW_LONG;
1963 toku_global_status_variables[num_rows].value = (char*)NullS;
1964 toku_global_status_variables[num_rows].name = (char*)NullS;
1965
1966 var->type= SHOW_ARRAY;
1967 var->value= (char *) toku_global_status_variables;
1968 }
1969 if (error) { my_errno = error; }
1970 TOKUDB_DBUG_RETURN(error);
1971}
1972
1973static SHOW_VAR toku_global_status_variables_export[]= {
1974 {"Tokudb", (char*)&show_tokudb_vars, SHOW_FUNC},
1975 {NullS, NullS, SHOW_LONG}
1976};
1977
1978#if TOKU_INCLUDE_BACKTRACE
1979#include <execinfo.h>
1980static void tokudb_backtrace(void) {
1981 const int N_POINTERS = 30;
1982 void *backtrace_pointers[N_POINTERS];
1983 int n = backtrace(backtrace_pointers, N_POINTERS);
1984 backtrace_symbols_fd(backtrace_pointers, n, fileno(stderr));
1985}
1986#endif
1987
1988#ifdef MARIA_PLUGIN_INTERFACE_VERSION
1989maria_declare_plugin(tokudb)
1990#else
1991mysql_declare_plugin(tokudb)
1992#endif
1993 {
1994 MYSQL_STORAGE_ENGINE_PLUGIN,
1995 &tokudb_storage_engine,
1996 tokudb_hton_name,
1997 "Percona",
1998 "Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
1999 PLUGIN_LICENSE_GPL,
2000 tokudb_init_func, /* plugin init */
2001 tokudb_done_func, /* plugin deinit */
2002 TOKUDB_PLUGIN_VERSION,
2003 toku_global_status_variables_export, /* status variables */
2004 tokudb::sysvars::system_variables, /* system variables */
2005#ifdef MARIA_PLUGIN_INTERFACE_VERSION
2006 tokudb::sysvars::version,
2007 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
2008#else
2009 NULL, /* config options */
2010 0, /* flags */
2011#endif
2012 },
2013 tokudb::information_schema::trx,
2014 tokudb::information_schema::lock_waits,
2015 tokudb::information_schema::locks,
2016 tokudb::information_schema::file_map,
2017 tokudb::information_schema::fractal_tree_info,
2018 tokudb::information_schema::fractal_tree_block_map,
2019 tokudb::information_schema::background_job_status
2020#ifdef MARIA_PLUGIN_INTERFACE_VERSION
2021maria_declare_plugin_end;
2022#else
2023mysql_declare_plugin_end;
2024#endif
2025