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