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