1 /***********************************************************************
2
3 Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License along
22 with this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25 ***********************************************************************/
26
27 /**************************************************//**
28 @file innodb_engine.c
29 InnoDB Memcached Engine code
30
31 Extracted and modified from NDB memcached project
32 04/12/2011 Jimmy Yang
33 *******************************************************/
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <pthread.h>
39 #include <arpa/inet.h>
40 #include "default_engine.h"
41 #include <memcached/util.h>
42 #include <memcached/config_parser.h>
43 #include <unistd.h>
44
45 #include "innodb_engine.h"
46 #include "innodb_engine_private.h"
47 #include "innodb_api.h"
48 #include "hash_item_util.h"
49 #include "innodb_cb_api.h"
50
51 /** Define also present in daemon/memcached.h */
52 #define KEY_MAX_LENGTH 250
53
54 /** Time (in seconds) that background thread sleeps before it wakes
55 up and commit idle connection transactions */
56 #define BK_COMMIT_THREAD_SLEEP_INTERVAL 5
57
58 /** Maximum number of connections that background thread processes each
59 time */
60 #define BK_MAX_PROCESS_COMMIT 5
61
62 /** Minimum time (in seconds) that a connection has been idle, that makes
63 it candidate for background thread to commit it */
64 #define CONN_IDLE_TIME_TO_BK_COMMIT 5
65
66 /** Tells whether memcached plugin is being shutdown */
67 static bool memcached_shutdown = false;
68
69 /** Tells whether the background thread is exited */
70 static bool bk_thd_exited = true;
71
72 /** Tells whether all connections need to release MDL locks */
73 bool release_mdl_lock = false;
74
75 /** InnoDB Memcached engine configuration info */
76 typedef struct eng_config_info {
77 char* option_string; /*!< memcached config option
78 string */
79 void* cb_ptr; /*!< call back function ptr */
80 unsigned int eng_read_batch_size; /*!< read batch size */
81 unsigned int eng_write_batch_size; /*!< write batch size */
82 bool eng_enable_binlog; /*!< whether binlog is
83 enabled specifically for
84 this memcached engine */
85 } eng_config_info_t;
86
87 extern option_t config_option_names[];
88
89 /** Check if global read lock is active */
90 extern
91 bool
92 handler_check_global_read_lock_active();
93
94 /** Check the input key name implies a table mapping switch. The name
95 would start with "@@", and in the format of "@@new_table_mapping.key"
96 or simply "@@new_table_mapping" */
97
98
99 /**********************************************************************//**
100 Unlock a table and commit the transaction
101 return 0 if fail to commit the transaction */
102 extern
103 int
104 handler_unlock_table(
105 /*=================*/
106 void* my_thd, /*!< in: thread */
107 void* my_table, /*!< in: Table metadata */
108 int my_lock_mode); /*!< in: lock mode */
109
110 /*******************************************************************//**
111 Get InnoDB Memcached engine handle
112 @return InnoDB Memcached engine handle */
113 static inline
114 struct innodb_engine*
innodb_handle(ENGINE_HANDLE * handle)115 innodb_handle(
116 /*==========*/
117 ENGINE_HANDLE* handle) /*!< in: Generic engine handle */
118 {
119 return((struct innodb_engine*) handle);
120 }
121
122 /*******************************************************************//**
123 Cleanup idle connections if "clear_all" is false, and clean up all
124 connections if "clear_all" is true.
125 @return number of connection cleaned */
126 static
127 void
128 innodb_conn_clean_data(
129 /*===================*/
130 innodb_conn_data_t* conn_data,
131 bool has_lock,
132 bool free_all);
133
134 /*******************************************************************//**
135 Get default Memcached engine handle
136 @return default Memcached engine handle */
137 static inline
138 struct default_engine*
default_handle(struct innodb_engine * eng)139 default_handle(
140 /*===========*/
141 struct innodb_engine* eng)
142 {
143 return((struct default_engine*) eng->default_engine);
144 }
145
146 /****** Gateway to the default_engine's create_instance() function */
147 ENGINE_ERROR_CODE
148 create_my_default_instance(
149 /*=======================*/
150 uint64_t,
151 GET_SERVER_API,
152 ENGINE_HANDLE **);
153
154 /*********** FUNCTIONS IMPLEMENTING THE PUBLISHED API BEGIN HERE ********/
155
156 /*******************************************************************//**
157 Create InnoDB Memcached Engine.
158 @return ENGINE_SUCCESS if successful, otherwise, error code */
159 ENGINE_ERROR_CODE
create_instance(uint64_t interface,GET_SERVER_API get_server_api,ENGINE_HANDLE ** handle)160 create_instance(
161 /*============*/
162 uint64_t interface, /*!< in: protocol version,
163 currently always 1 */
164 GET_SERVER_API get_server_api, /*!< in: Callback the engines
165 may call to get the public
166 server interface */
167 ENGINE_HANDLE** handle ) /*!< out: Engine handle */
168 {
169 ENGINE_ERROR_CODE err_ret;
170 struct innodb_engine* innodb_eng;
171
172 SERVER_HANDLE_V1 *api = get_server_api();
173
174 if (interface != 1 || api == NULL) {
175 return(ENGINE_ENOTSUP);
176 }
177
178 innodb_eng = malloc(sizeof(struct innodb_engine));
179
180 if (innodb_eng == NULL) {
181 return(ENGINE_ENOMEM);
182 }
183
184 memset(innodb_eng, 0, sizeof(*innodb_eng));
185 innodb_eng->engine.interface.interface = 1;
186 innodb_eng->engine.get_info = innodb_get_info;
187 innodb_eng->engine.initialize = innodb_initialize;
188 innodb_eng->engine.destroy = innodb_destroy;
189 innodb_eng->engine.allocate = innodb_allocate;
190 innodb_eng->engine.remove = innodb_remove;
191 innodb_eng->engine.release = innodb_release;
192 innodb_eng->engine.clean_engine= innodb_clean_engine;
193 innodb_eng->engine.get = innodb_get;
194 innodb_eng->engine.get_stats = innodb_get_stats;
195 innodb_eng->engine.reset_stats = innodb_reset_stats;
196 innodb_eng->engine.store = innodb_store;
197 innodb_eng->engine.arithmetic = innodb_arithmetic;
198 innodb_eng->engine.flush = innodb_flush;
199 innodb_eng->engine.unknown_command = innodb_unknown_command;
200 innodb_eng->engine.item_set_cas = item_set_cas;
201 innodb_eng->engine.get_item_info = innodb_get_item_info;
202 innodb_eng->engine.get_stats_struct = NULL;
203 innodb_eng->engine.errinfo = NULL;
204 innodb_eng->engine.bind = innodb_bind;
205
206 innodb_eng->server = *api;
207 innodb_eng->get_server_api = get_server_api;
208
209 /* configuration, with default values*/
210 innodb_eng->info.info.description = "InnoDB Memcache " VERSION;
211 innodb_eng->info.info.num_features = 3;
212 innodb_eng->info.info.features[0].feature = ENGINE_FEATURE_CAS;
213 innodb_eng->info.info.features[1].feature =
214 ENGINE_FEATURE_PERSISTENT_STORAGE;
215 innodb_eng->info.info.features[2].feature = ENGINE_FEATURE_LRU;
216
217 /* Now call create_instace() for the default engine */
218 err_ret = create_my_default_instance(interface, get_server_api,
219 &(innodb_eng->default_engine));
220
221 if (err_ret != ENGINE_SUCCESS) {
222 free(innodb_eng);
223 return(err_ret);
224 }
225
226 innodb_eng->clean_stale_conn = false;
227 innodb_eng->initialized = true;
228
229 *handle = (ENGINE_HANDLE*) &innodb_eng->engine;
230
231 return(ENGINE_SUCCESS);
232 }
233
234 /*******************************************************************//**
235 background thread to commit trx.
236 @return dummy parameter */
237 static
238 void*
innodb_bk_thread(void * arg)239 innodb_bk_thread(
240 /*=============*/
241 void* arg)
242 {
243 ENGINE_HANDLE* handle;
244 struct innodb_engine* innodb_eng;
245 innodb_conn_data_t* conn_data;
246 void* thd = NULL;
247
248 bk_thd_exited = false;
249
250 handle = (ENGINE_HANDLE*) (arg);
251 innodb_eng = innodb_handle(handle);
252
253 if (innodb_eng->enable_binlog) {
254 /* This thread will commit the transactions
255 on behalf of the other threads. It will "pretend"
256 to be each connection thread while doing it. */
257 thd = handler_create_thd(true);
258 }
259
260 conn_data = UT_LIST_GET_FIRST(innodb_eng->conn_data);
261
262 while(!memcached_shutdown) {
263 innodb_conn_data_t* next_conn_data;
264 uint64_t time;
265 uint64_t trx_start = 0;
266 uint64_t processed_count = 0;
267
268 if (handler_check_global_read_lock_active()) {
269 release_mdl_lock = true;
270 } else {
271 release_mdl_lock = false;
272 }
273
274 /* Do the cleanup every innodb_eng->bk_commit_interval
275 seconds. We also check if the plugin is being shutdown
276 every second */
277 for (uint i = 0; i < innodb_eng->bk_commit_interval; i++) {
278 sleep(1);
279
280 /* If memcached is being shutdown, break */
281 if (memcached_shutdown) {
282 break;
283 }
284 }
285
286 time = mci_get_time();
287
288 if (UT_LIST_GET_LEN(innodb_eng->conn_data) == 0) {
289 continue;
290 }
291
292
293 if (!conn_data) {
294 conn_data = UT_LIST_GET_FIRST(innodb_eng->conn_data);
295 }
296
297 if (conn_data) {
298 next_conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
299 } else {
300 next_conn_data = NULL;
301 }
302
303 /* Set the clean_stale_conn to prevent force clean in
304 innodb_conn_clean. */
305 LOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
306 innodb_eng->clean_stale_conn = true;
307 UNLOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
308
309 while (conn_data) {
310 if (release_mdl_lock && !conn_data->is_stale) {
311 int err;
312
313 if(conn_data->is_waiting_for_mdl) {
314 goto next_item;
315 }
316
317 err = LOCK_CURRENT_CONN_TRYLOCK(conn_data);
318 if (err != 0) {
319 goto next_item;
320 }
321 /* We have got the lock here */
322 } else {
323 LOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
324 }
325
326 if (conn_data->is_stale) {
327 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
328 false, conn_data);
329 LOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
330 UT_LIST_REMOVE(conn_list, innodb_eng->conn_data,
331 conn_data);
332 UNLOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
333 innodb_conn_clean_data(conn_data, false, true);
334 goto next_item;
335 }
336
337 if (release_mdl_lock) {
338 if (conn_data->thd) {
339 handler_thd_attach(conn_data->thd, NULL);
340 }
341
342 if (conn_data->in_use) {
343 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
344 goto next_item;
345 }
346
347 innodb_reset_conn(conn_data, true, true,
348 innodb_eng->enable_binlog);
349 if(conn_data->mysql_tbl) {
350 handler_unlock_table(conn_data->thd,
351 conn_data->mysql_tbl,
352 HDL_READ);
353 conn_data->mysql_tbl = NULL;
354 }
355
356 /*Close the data cursor */
357 if (conn_data->crsr) {
358 innodb_cb_cursor_close(conn_data->crsr);
359 conn_data->crsr = NULL;
360 }
361 if(conn_data->crsr_trx != NULL) {
362 ib_cb_trx_release(conn_data->crsr_trx);
363 conn_data->crsr_trx = NULL;
364 }
365
366 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
367 goto next_item;
368 }
369
370 if (conn_data->crsr_trx) {
371 trx_start = ib_cb_trx_get_start_time(
372 conn_data->crsr_trx);
373 }
374
375 /* Check the trx, if it is qualified for
376 reset and commit */
377 if ((conn_data->n_writes_since_commit > 0
378 || conn_data->n_reads_since_commit > 0)
379 && trx_start
380 && (time - trx_start > CONN_IDLE_TIME_TO_BK_COMMIT)
381 && !conn_data->in_use) {
382 /* binlog is running, make the thread
383 attach to conn_data->thd for binlog
384 committing */
385 if (thd && conn_data->thd) {
386 handler_thd_attach(
387 conn_data->thd, NULL);
388 }
389
390 innodb_reset_conn(conn_data, true, true,
391 innodb_eng->enable_binlog);
392 processed_count++;
393 }
394
395 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
396
397 next_item:
398 conn_data = next_conn_data;
399
400 /* Process BK_MAX_PROCESS_COMMIT (5) trx at a time */
401 if (!release_mdl_lock &&
402 processed_count > BK_MAX_PROCESS_COMMIT) {
403 break;
404 }
405
406 if (conn_data) {
407 next_conn_data = UT_LIST_GET_NEXT(
408 conn_list, conn_data);
409 }
410 }
411
412 /* Set the clean_stale_conn back. */
413 LOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
414 innodb_eng->clean_stale_conn = false;
415 UNLOCK_CONN_IF_NOT_LOCKED(false, innodb_eng);
416 }
417
418 bk_thd_exited = true;
419
420 /* Change to its original state before close the MySQL THD */
421 if (thd) {
422 handler_thd_attach(thd, NULL);
423 handler_close_thd(thd);
424 }
425
426 pthread_detach(pthread_self());
427 pthread_exit(NULL);
428
429 return((void*) 0);
430 }
431
432 /*******************************************************************//**
433 Get engine info.
434 @return engine info */
435 static
436 const engine_info*
innodb_get_info(ENGINE_HANDLE * handle)437 innodb_get_info(
438 /*============*/
439 ENGINE_HANDLE* handle) /*!< in: Engine handle */
440 {
441 return(&innodb_handle(handle)->info.info);
442 }
443
444 /*******************************************************************//**
445 Initialize InnoDB Memcached Engine.
446 @return ENGINE_SUCCESS if successful */
447 static
448 ENGINE_ERROR_CODE
innodb_initialize(ENGINE_HANDLE * handle,const char * config_str)449 innodb_initialize(
450 /*==============*/
451 ENGINE_HANDLE* handle, /*!< in/out: InnoDB memcached
452 engine */
453 const char* config_str) /*!< in: configure string */
454 {
455 ENGINE_ERROR_CODE return_status = ENGINE_SUCCESS;
456 struct innodb_engine* innodb_eng = innodb_handle(handle);
457 struct default_engine* def_eng = default_handle(innodb_eng);
458 eng_config_info_t* my_eng_config;
459 pthread_attr_t attr;
460
461 my_eng_config = (eng_config_info_t*) config_str;
462
463 /* If no call back function registered (InnoDB engine failed to load),
464 load InnoDB Memcached engine should fail too */
465 if (!my_eng_config->cb_ptr) {
466 return(ENGINE_TMPFAIL);
467 }
468
469 /* Register the call back function */
470 register_innodb_cb((void*) my_eng_config->cb_ptr);
471
472 innodb_eng->read_batch_size = (my_eng_config->eng_read_batch_size
473 ? my_eng_config->eng_read_batch_size
474 : CONN_NUM_READ_COMMIT);
475
476 innodb_eng->write_batch_size = (my_eng_config->eng_write_batch_size
477 ? my_eng_config->eng_write_batch_size
478 : CONN_NUM_WRITE_COMMIT);
479
480 innodb_eng->enable_binlog = my_eng_config->eng_enable_binlog;
481
482 innodb_eng->cfg_status = innodb_cb_get_cfg();
483
484 /* If binlog is not enabled by InnoDB memcached plugin, let's
485 check whether innodb_direct_access_enable_binlog is turned on */
486 if (!innodb_eng->enable_binlog) {
487 innodb_eng->enable_binlog = innodb_eng->cfg_status
488 & IB_CFG_BINLOG_ENABLED;
489 }
490
491 innodb_eng->enable_mdl = innodb_eng->cfg_status & IB_CFG_MDL_ENABLED;
492 innodb_eng->trx_level = ib_cb_cfg_trx_level();
493 innodb_eng->bk_commit_interval = ib_cb_cfg_bk_commit_interval();
494
495 UT_LIST_INIT(innodb_eng->conn_data);
496 pthread_mutex_init(&innodb_eng->conn_mutex, NULL);
497 pthread_mutex_init(&innodb_eng->cas_mutex, NULL);
498 pthread_mutex_init(&innodb_eng->flush_mutex, NULL);
499
500 /* Fetch InnoDB specific settings */
501 innodb_eng->meta_info = innodb_config(
502 NULL, 0, &innodb_eng->meta_hash);
503
504 if (!innodb_eng->meta_info) {
505 return(ENGINE_TMPFAIL);
506 }
507
508 if (innodb_eng->default_engine) {
509 return_status = def_eng->engine.initialize(
510 innodb_eng->default_engine,
511 my_eng_config->option_string);
512 }
513
514 memcached_shutdown = false;
515 pthread_attr_init(&attr);
516 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
517 pthread_create(&innodb_eng->bk_thd_for_commit, &attr, innodb_bk_thread,
518 handle);
519
520 return(return_status);
521 }
522
523 extern void handler_close_thd(void*);
524
525 /*******************************************************************//**
526 Close table using handler functions.
527 @param conn_data cursor information of connection */
528 void
innodb_close_mysql_table(innodb_conn_data_t * conn_data)529 innodb_close_mysql_table(
530 /*=====================*/
531 innodb_conn_data_t* conn_data) /*!< in: connection
532 cursor*/
533 {
534 if (conn_data->mysql_tbl) {
535 assert(conn_data->thd);
536 handler_unlock_table(conn_data->thd,
537 conn_data->mysql_tbl,
538 HDL_READ);
539 conn_data->mysql_tbl = NULL;
540 }
541
542 if (conn_data->thd) {
543 handler_close_thd(conn_data->thd);
544 conn_data->thd = NULL;
545 }
546 }
547
548 /*******************************************************************//**
549 Cleanup idle connections if "clear_all" is false, and clean up all
550 connections if "clear_all" is true.
551 @return number of connection cleaned */
552 static
553 void
innodb_conn_clean_data(innodb_conn_data_t * conn_data,bool has_lock,bool free_all)554 innodb_conn_clean_data(
555 /*===================*/
556 innodb_conn_data_t* conn_data,
557 bool has_lock,
558 bool free_all)
559 {
560 if (!conn_data) {
561 return;
562 }
563
564 LOCK_CURRENT_CONN_IF_NOT_LOCKED(has_lock, conn_data);
565
566 if (conn_data->idx_crsr) {
567 innodb_cb_cursor_close(conn_data->idx_crsr);
568 conn_data->idx_crsr = NULL;
569 }
570
571 if (conn_data->idx_read_crsr) {
572 innodb_cb_cursor_close(conn_data->idx_read_crsr);
573 conn_data->idx_read_crsr = NULL;
574 }
575
576 if (conn_data->crsr) {
577 innodb_cb_cursor_close(conn_data->crsr);
578 conn_data->crsr = NULL;
579 }
580
581 if (conn_data->read_crsr) {
582 innodb_cb_cursor_close(conn_data->read_crsr);
583 conn_data->read_crsr = NULL;
584 }
585
586 if (conn_data->crsr_trx) {
587 ib_err_t err;
588 innodb_cb_trx_commit(conn_data->crsr_trx);
589 err = ib_cb_trx_release(conn_data->crsr_trx);
590 assert(err == DB_SUCCESS);
591 conn_data->crsr_trx = NULL;
592 }
593
594 innodb_close_mysql_table(conn_data);
595
596 if (conn_data->tpl) {
597 ib_cb_tuple_delete(conn_data->tpl);
598 conn_data->tpl = NULL;
599 }
600
601 if (conn_data->idx_tpl) {
602 ib_cb_tuple_delete(conn_data->idx_tpl);
603 conn_data->idx_tpl = NULL;
604 }
605
606 if (conn_data->read_tpl) {
607 ib_cb_tuple_delete(conn_data->read_tpl);
608 conn_data->read_tpl = NULL;
609 }
610
611 if (conn_data->sel_tpl) {
612 ib_cb_tuple_delete(conn_data->sel_tpl);
613 conn_data->sel_tpl = NULL;
614 }
615
616 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(has_lock, conn_data);
617
618 if (free_all) {
619 if (conn_data->result) {
620 free(conn_data->result);
621 conn_data->result = NULL;
622 }
623
624 if (conn_data->row_buf) {
625 free(conn_data->row_buf);
626 conn_data->row_buf = NULL;
627 conn_data->row_buf_len = 0;
628 }
629
630 if (conn_data->cmd_buf) {
631 free(conn_data->cmd_buf);
632 conn_data->cmd_buf = NULL;
633 conn_data->cmd_buf_len = 0;
634 }
635
636 if (conn_data->mul_col_buf) {
637 free(conn_data->mul_col_buf);
638 conn_data->mul_col_buf = NULL;
639 conn_data->mul_col_buf_len = 0;
640 }
641
642 pthread_mutex_destroy(&conn_data->curr_conn_mutex);
643 free(conn_data);
644 }
645 }
646
647 /*******************************************************************//**
648 Cleanup idle connections if "clear_all" is false, and clean up all
649 connections if "clear_all" is true.
650 @return number of connection cleaned */
651 static
652 int
innodb_conn_clean(innodb_engine_t * engine,bool clear_all,bool has_lock)653 innodb_conn_clean(
654 /*==============*/
655 innodb_engine_t* engine, /*!< in/out: InnoDB memcached
656 engine */
657 bool clear_all, /*!< in: Clear all connection */
658 bool has_lock) /*!< in: Has engine mutext */
659 {
660 innodb_conn_data_t* conn_data;
661 innodb_conn_data_t* next_conn_data;
662 int num_freed = 0;
663 void* thd = NULL;
664
665 if (engine->enable_binlog && clear_all) {
666 thd = handler_create_thd(true);
667 }
668
669 LOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
670
671 conn_data = UT_LIST_GET_FIRST(engine->conn_data);
672
673 while (conn_data) {
674 void* cookie = conn_data->conn_cookie;
675
676 next_conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
677
678 if (!clear_all && !conn_data->in_use) {
679 innodb_conn_data_t* check_data;
680 check_data = engine->server.cookie->get_engine_specific(
681 cookie);
682
683 /* The check data is the original conn_data stored
684 in connection "cookie", it can be set to NULL if
685 connection closed, or to a new conn_data if it is
686 closed and reopened. So verify and see if our
687 current conn_data is stale */
688 if (!check_data || check_data != conn_data) {
689 assert(conn_data->is_stale);
690 }
691 }
692
693 /* If current conn is stale or clear_all is true,
694 clean up it.*/
695 if (conn_data->is_stale) {
696 /* If bk thread is doing the same thing, stop
697 the loop to avoid confliction.*/
698 if (engine->clean_stale_conn)
699 break;
700
701 UT_LIST_REMOVE(conn_list, engine->conn_data,
702 conn_data);
703 innodb_conn_clean_data(conn_data, false, true);
704 num_freed++;
705 } else {
706 if (clear_all) {
707 UT_LIST_REMOVE(conn_list, engine->conn_data,
708 conn_data);
709
710 if (thd && conn_data->thd ) {
711 handler_thd_attach(conn_data->thd,
712 NULL);
713 }
714
715 innodb_reset_conn(conn_data, false, true,
716 engine->enable_binlog);
717 if (conn_data->thd) {
718 handler_thd_attach(
719 conn_data->thd, NULL);
720 }
721 innodb_conn_clean_data(conn_data, false, true);
722
723 engine->server.cookie->store_engine_specific(
724 cookie, NULL);
725 num_freed++;
726 }
727 }
728
729 conn_data = next_conn_data;
730 }
731
732 assert(!clear_all || engine->conn_data.count == 0);
733
734 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
735
736 if (thd) {
737 handler_thd_attach(thd, NULL);
738 handler_close_thd(thd);
739 }
740
741 return(num_freed);
742 }
743
744 /*******************************************************************//**
745 Destroy and Free InnoDB Memcached engine */
746 static
747 void
innodb_destroy(ENGINE_HANDLE * handle,bool force)748 innodb_destroy(
749 /*===========*/
750 ENGINE_HANDLE* handle, /*!< in: Destroy the engine instance */
751 bool force) /*!< in: Force to destroy */
752 {
753 struct innodb_engine* innodb_eng = innodb_handle(handle);
754 struct default_engine *def_eng = default_handle(innodb_eng);
755
756 memcached_shutdown = true;
757
758 /* Wait for the background thread to exit */
759 while (!bk_thd_exited) {
760 sleep(1);
761 }
762
763 innodb_conn_clean(innodb_eng, true, false);
764
765 if (innodb_eng->meta_hash) {
766 HASH_CLEANUP(innodb_eng->meta_hash, meta_cfg_info_t*);
767 }
768
769 pthread_mutex_destroy(&innodb_eng->conn_mutex);
770 pthread_mutex_destroy(&innodb_eng->cas_mutex);
771 pthread_mutex_destroy(&innodb_eng->flush_mutex);
772
773 if (innodb_eng->default_engine) {
774 def_eng->engine.destroy(innodb_eng->default_engine, force);
775 }
776
777 free(innodb_eng);
778 }
779
780 /** Defines for connection initialization to indicate if we will
781 do a read or write operation, or in the case of CONN_MODE_NONE, just get
782 the connection's conn_data structure */
783 enum conn_mode {
784 CONN_MODE_READ,
785 CONN_MODE_WRITE,
786 CONN_MODE_NONE
787 };
788
789
790 /*******************************************************************//**
791 Opens mysql table if enable_binlog or enable_mdl is set
792 @param conn_data connection cursor data
793 @param conn_optioin read or write operation
794 @param engine Innodb memcached engine
795 @returns DB_SUCCESS on success and DB_ERROR on failure */
796 ib_err_t
innodb_open_mysql_table(innodb_conn_data_t * conn_data,int conn_option,innodb_engine_t * engine)797 innodb_open_mysql_table(
798 /*====================*/
799 innodb_conn_data_t* conn_data, /*!< in/out:Connection cursor data */
800 int conn_option, /*!< in: Read or write operation */
801 innodb_engine_t* engine) /*!< in: InnoDB memcached engine */
802 {
803 meta_cfg_info_t* meta_info;
804 meta_info = conn_data->conn_meta;
805 conn_data->is_waiting_for_mdl = true;
806
807 /* Close the table before opening it again */
808 innodb_close_mysql_table(conn_data);
809
810 if (conn_option == CONN_MODE_READ) {
811 conn_data->is_waiting_for_mdl = false;
812 return (DB_SUCCESS);
813 }
814
815 if (!conn_data->thd) {
816 conn_data->thd = handler_create_thd(engine->enable_binlog);
817 if (!conn_data->thd) {
818 return(DB_ERROR);
819 }
820 }
821
822 if (!conn_data->mysql_tbl) {
823 conn_data->mysql_tbl =
824 handler_open_table(
825 conn_data->thd,
826 meta_info->col_info[CONTAINER_DB].col_name,
827 meta_info->col_info[CONTAINER_TABLE].col_name,
828 HDL_WRITE);
829 }
830 conn_data->is_waiting_for_mdl = false;
831
832 if(!conn_data->mysql_tbl) {
833 return(DB_LOCK_WAIT);
834 }
835
836 return (DB_SUCCESS);
837 }
838
839
840 /*******************************************************************//**
841 Cleanup connections
842 @return number of connection cleaned */
843 /* Initialize a connection's cursor and transactions
844 @return the connection's conn_data structure */
845 static
846 innodb_conn_data_t*
innodb_conn_init(innodb_engine_t * engine,const void * cookie,int conn_option,ib_lck_mode_t lock_mode,bool has_lock,meta_cfg_info_t * new_meta_info)847 innodb_conn_init(
848 /*=============*/
849 innodb_engine_t* engine, /*!< in/out: InnoDB memcached
850 engine */
851 const void* cookie, /*!< in: This connection's
852 cookie */
853 int conn_option, /*!< in: whether it is
854 for read or write operation*/
855 ib_lck_mode_t lock_mode, /*!< in: Table lock mode */
856 bool has_lock, /*!< in: Has engine mutex */
857 meta_cfg_info_t* new_meta_info) /*!< in: meta info for
858 table to open or NULL */
859 {
860 innodb_conn_data_t* conn_data;
861 meta_cfg_info_t* meta_info;
862 meta_index_t* meta_index;
863 ib_err_t err = DB_SUCCESS;
864 ib_crsr_t crsr;
865 ib_crsr_t read_crsr;
866 ib_crsr_t idx_crsr;
867 bool trx_updated = false;
868
869 /* Get this connection's conn_data */
870 conn_data = engine->server.cookie->get_engine_specific(cookie);
871
872 assert(!conn_data || !conn_data->in_use);
873
874 if (!conn_data) {
875 LOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
876 conn_data = engine->server.cookie->get_engine_specific(cookie);
877
878 if (conn_data) {
879 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
880 goto have_conn;
881 }
882
883 if (UT_LIST_GET_LEN(engine->conn_data) > 2048) {
884 /* Some of conn_data can be stale, recycle them */
885 innodb_conn_clean(engine, false, true);
886 }
887
888 conn_data = malloc(sizeof(*conn_data));
889
890 if (!conn_data) {
891 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
892 return(NULL);
893 }
894
895 memset(conn_data, 0, sizeof(*conn_data));
896 conn_data->result = malloc(sizeof(mci_item_t));
897 if (!conn_data->result) {
898 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
899 free(conn_data);
900 conn_data = NULL;
901 return(NULL);
902 }
903 conn_data->conn_meta = new_meta_info
904 ? new_meta_info
905 : engine->meta_info;
906 conn_data->row_buf = malloc(1024);
907 if (!conn_data->row_buf) {
908 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
909 free(conn_data->result);
910 free(conn_data);
911 conn_data = NULL;
912 return(NULL);
913 }
914 conn_data->row_buf_len = 1024;
915
916 conn_data->cmd_buf = malloc(1024);
917 if (!conn_data->cmd_buf) {
918 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
919 free(conn_data->row_buf);
920 free(conn_data->result);
921 free(conn_data);
922 conn_data = NULL;
923 return(NULL);
924 }
925 conn_data->cmd_buf_len = 1024;
926
927 conn_data->is_flushing = false;
928
929 conn_data->conn_cookie = (void*) cookie;
930
931 /* Add connection to the list after all memory allocations */
932 UT_LIST_ADD_LAST(conn_list, engine->conn_data, conn_data);
933 engine->server.cookie->store_engine_specific(
934 cookie, conn_data);
935
936 pthread_mutex_init(&conn_data->curr_conn_mutex, NULL);
937 UNLOCK_CONN_IF_NOT_LOCKED(has_lock, engine);
938 }
939 have_conn:
940 meta_info = conn_data->conn_meta;
941 meta_index = &meta_info->index_info;
942
943 assert(engine->conn_data.count > 0);
944
945 if (conn_option == CONN_MODE_NONE) {
946 return(conn_data);
947 }
948
949 LOCK_CURRENT_CONN_IF_NOT_LOCKED(has_lock, conn_data);
950
951 /* If flush is running, then wait for it complete. */
952 if (conn_data->is_flushing) {
953 /* Request flush_mutex for waiting for flush
954 completed. */
955 pthread_mutex_lock(&engine->flush_mutex);
956 pthread_mutex_unlock(&engine->flush_mutex);
957 }
958
959 /* This special case added to facilitate unlocking
960 of MDL lock during FLUSH TABLE WITH READ LOCK */
961 if (engine &&
962 release_mdl_lock &&
963 (engine->enable_binlog || engine->enable_mdl)) {
964 if ( DB_SUCCESS !=
965 innodb_open_mysql_table(conn_data,
966 conn_option,
967 engine)){
968 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
969 has_lock, conn_data);
970 return NULL;
971 }
972 }
973
974 conn_data->in_use = true;
975
976 crsr = conn_data->crsr;
977 read_crsr = conn_data->read_crsr;
978
979 if (lock_mode == IB_LOCK_TABLE_X) {
980 if(!conn_data->crsr_trx) {
981 conn_data->crsr_trx = ib_cb_trx_begin(
982 engine->trx_level, true, false);
983 } else {
984 /* Write cursor transaction exists.
985 Reuse this transaction.*/
986 if (ib_cb_trx_read_only(conn_data->crsr_trx)) {
987 innodb_cb_trx_commit(
988 conn_data->crsr_trx);
989 }
990
991 err = ib_cb_trx_start(conn_data->crsr_trx,
992 engine->trx_level,
993 true, false, NULL);
994 assert(err == DB_SUCCESS);
995
996 }
997
998 err = innodb_api_begin(
999 engine,
1000 meta_info->col_info[CONTAINER_DB].col_name,
1001 meta_info->col_info[CONTAINER_TABLE].col_name,
1002 conn_data, conn_data->crsr_trx,
1003 &conn_data->crsr, &conn_data->idx_crsr,
1004 lock_mode);
1005
1006 if (err != DB_SUCCESS) {
1007 innodb_cb_cursor_close(
1008 conn_data->crsr);
1009 conn_data->crsr = NULL;
1010 innodb_cb_trx_commit(
1011 conn_data->crsr_trx);
1012 err = ib_cb_trx_release(conn_data->crsr_trx);
1013 assert(err == DB_SUCCESS);
1014 conn_data->crsr_trx = NULL;
1015 conn_data->in_use = false;
1016 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1017 has_lock, conn_data);
1018 return(NULL);
1019 }
1020
1021 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(has_lock, conn_data);
1022 return(conn_data);
1023 }
1024
1025 /* Write operation */
1026 if (conn_option == CONN_MODE_WRITE) {
1027 if (!crsr) {
1028 if (!conn_data->crsr_trx) {
1029 conn_data->crsr_trx = ib_cb_trx_begin(
1030 engine->trx_level, true, false);
1031 trx_updated = true;
1032 } else {
1033 if (ib_cb_trx_read_only(conn_data->crsr_trx)) {
1034 innodb_cb_trx_commit(
1035 conn_data->crsr_trx);
1036 }
1037
1038 ib_cb_trx_start(conn_data->crsr_trx,
1039 engine->trx_level,
1040 true, false, NULL);
1041 }
1042
1043 err = innodb_api_begin(
1044 engine,
1045 meta_info->col_info[CONTAINER_DB].col_name,
1046 meta_info->col_info[CONTAINER_TABLE].col_name,
1047 conn_data, conn_data->crsr_trx,
1048 &conn_data->crsr, &conn_data->idx_crsr,
1049 lock_mode);
1050
1051 if (err != DB_SUCCESS) {
1052 innodb_cb_cursor_close(
1053 conn_data->crsr);
1054 conn_data->crsr = NULL;
1055 innodb_cb_trx_commit(
1056 conn_data->crsr_trx);
1057 err = ib_cb_trx_release(conn_data->crsr_trx);
1058 assert(err == DB_SUCCESS);
1059 conn_data->crsr_trx = NULL;
1060 conn_data->in_use = false;
1061
1062 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1063 has_lock, conn_data);
1064 return(NULL);
1065 }
1066
1067 } else if (!conn_data->crsr_trx) {
1068
1069 /* There exists a cursor, just need update
1070 with a new transaction */
1071 conn_data->crsr_trx = ib_cb_trx_begin(
1072 engine->trx_level, true, false);
1073
1074 innodb_cb_cursor_new_trx(crsr, conn_data->crsr_trx);
1075 trx_updated = true;
1076
1077 err = innodb_cb_cursor_lock(engine, crsr, lock_mode);
1078
1079 if (err != DB_SUCCESS) {
1080 innodb_cb_cursor_close(
1081 conn_data->crsr);
1082 conn_data->crsr = NULL;
1083 innodb_cb_trx_commit(
1084 conn_data->crsr_trx);
1085 err = ib_cb_trx_release(conn_data->crsr_trx);
1086 assert(err == DB_SUCCESS);
1087 conn_data->crsr_trx = NULL;
1088 conn_data->in_use = false;
1089 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1090 has_lock, conn_data);
1091 return(NULL);
1092 }
1093
1094 if (meta_index->srch_use_idx == META_USE_SECONDARY) {
1095
1096 idx_crsr = conn_data->idx_crsr;
1097 innodb_cb_cursor_new_trx(
1098 idx_crsr, conn_data->crsr_trx);
1099 innodb_cb_cursor_lock(
1100 engine, idx_crsr, lock_mode);
1101 }
1102 } else {
1103
1104 if (ib_cb_trx_read_only(conn_data->crsr_trx)) {
1105 innodb_cb_trx_commit(
1106 conn_data->crsr_trx);
1107 }
1108
1109 ib_cb_trx_start(conn_data->crsr_trx,
1110 engine->trx_level,
1111 true, false, NULL);
1112 ib_cb_cursor_stmt_begin(crsr);
1113 err = innodb_cb_cursor_lock(engine, crsr, lock_mode);
1114
1115 if (err != DB_SUCCESS) {
1116 innodb_cb_cursor_close(
1117 conn_data->crsr);
1118 conn_data->crsr = NULL;
1119 innodb_cb_trx_commit(
1120 conn_data->crsr_trx);
1121 err = ib_cb_trx_release(conn_data->crsr_trx);
1122 assert(err == DB_SUCCESS);
1123 conn_data->crsr_trx = NULL;
1124 conn_data->in_use = false;
1125 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1126 has_lock, conn_data);
1127 return(NULL);
1128 }
1129 }
1130
1131 if (trx_updated) {
1132 if (conn_data->read_crsr) {
1133 innodb_cb_cursor_new_trx(
1134 conn_data->read_crsr,
1135 conn_data->crsr_trx);
1136 }
1137
1138 if (conn_data->idx_read_crsr) {
1139 innodb_cb_cursor_new_trx(
1140 conn_data->idx_read_crsr,
1141 conn_data->crsr_trx);
1142 }
1143 }
1144 } else {
1145 bool auto_commit = (engine->read_batch_size == 1 &&
1146 !(engine->cfg_status & IB_CFG_DISABLE_ROWLOCK)) ? true : false;
1147
1148 assert(conn_option == CONN_MODE_READ);
1149
1150 if (!read_crsr) {
1151 if (!conn_data->crsr_trx) {
1152 /* This is read operation, start a trx
1153 with "read_write" parameter set to false */
1154 conn_data->crsr_trx = ib_cb_trx_begin(
1155 engine->trx_level, false, auto_commit);
1156 trx_updated = true;
1157 } else {
1158 ib_cb_trx_start(conn_data->crsr_trx,
1159 engine->trx_level,
1160 false, auto_commit, NULL);
1161 }
1162
1163 err = innodb_api_begin(
1164 engine,
1165 meta_info->col_info[CONTAINER_DB].col_name,
1166 meta_info->col_info[CONTAINER_TABLE].col_name,
1167 conn_data,
1168 conn_data->crsr_trx,
1169 &conn_data->read_crsr,
1170 &conn_data->idx_read_crsr,
1171 lock_mode);
1172
1173 if (err != DB_SUCCESS) {
1174 innodb_cb_cursor_close(
1175 conn_data->read_crsr);
1176 innodb_cb_trx_commit(
1177 conn_data->crsr_trx);
1178 err = ib_cb_trx_release(conn_data->crsr_trx);
1179 assert(err == DB_SUCCESS);
1180 conn_data->crsr_trx = NULL;
1181 conn_data->read_crsr = NULL;
1182 conn_data->in_use = false;
1183 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1184 has_lock, conn_data);
1185
1186 return(NULL);
1187 }
1188
1189 } else if (!conn_data->crsr_trx) {
1190 /* This is read operation, start a trx
1191 with "read_write" parameter set to false */
1192 conn_data->crsr_trx = ib_cb_trx_begin(
1193 engine->trx_level, false, auto_commit);
1194
1195 trx_updated = true;
1196
1197 innodb_cb_cursor_new_trx(
1198 conn_data->read_crsr,
1199 conn_data->crsr_trx);
1200
1201 if (conn_data->crsr) {
1202 innodb_cb_cursor_new_trx(
1203 conn_data->crsr,
1204 conn_data->crsr_trx);
1205 }
1206
1207 err = innodb_cb_cursor_lock(
1208 engine, conn_data->read_crsr, lock_mode);
1209
1210 if (err != DB_SUCCESS) {
1211 innodb_cb_cursor_close(
1212 conn_data->read_crsr);
1213 innodb_cb_trx_commit(
1214 conn_data->crsr_trx);
1215 err = ib_cb_trx_release(conn_data->crsr_trx);
1216 assert(err == DB_SUCCESS);
1217 conn_data->crsr_trx = NULL;
1218 conn_data->read_crsr = NULL;
1219 conn_data->in_use = false;
1220 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1221 has_lock, conn_data);
1222
1223 return(NULL);
1224 }
1225
1226 if (meta_index->srch_use_idx == META_USE_SECONDARY) {
1227 ib_crsr_t idx_crsr = conn_data->idx_read_crsr;
1228
1229 innodb_cb_cursor_new_trx(
1230 idx_crsr, conn_data->crsr_trx);
1231 innodb_cb_cursor_lock(
1232 engine, idx_crsr, lock_mode);
1233 }
1234 } else {
1235 /* This is read operation, start a trx
1236 with "read_write" parameter set to false */
1237 ib_cb_trx_start(conn_data->crsr_trx,
1238 engine->trx_level,
1239 false, auto_commit,
1240 NULL);
1241
1242 ib_cb_cursor_stmt_begin(conn_data->read_crsr);
1243
1244 err = innodb_cb_cursor_lock(
1245 engine, conn_data->read_crsr, lock_mode);
1246
1247 if (err != DB_SUCCESS) {
1248 innodb_cb_cursor_close(
1249 conn_data->read_crsr);
1250 innodb_cb_trx_commit(
1251 conn_data->crsr_trx);
1252 err = ib_cb_trx_release(conn_data->crsr_trx);
1253 assert(err == DB_SUCCESS);
1254 conn_data->crsr_trx = NULL;
1255 conn_data->read_crsr = NULL;
1256 conn_data->in_use = false;
1257 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(
1258 has_lock, conn_data);
1259
1260 return(NULL);
1261 }
1262
1263 if (meta_index->srch_use_idx == META_USE_SECONDARY) {
1264 ib_crsr_t idx_crsr = conn_data->idx_read_crsr;
1265 ib_cb_cursor_stmt_begin(idx_crsr);
1266 innodb_cb_cursor_lock(
1267 engine, idx_crsr, lock_mode);
1268 }
1269 }
1270
1271 if (trx_updated) {
1272 if (conn_data->crsr) {
1273 innodb_cb_cursor_new_trx(
1274 conn_data->crsr,
1275 conn_data->crsr_trx);
1276 }
1277
1278 if (conn_data->idx_crsr) {
1279 innodb_cb_cursor_new_trx(
1280 conn_data->idx_crsr,
1281 conn_data->crsr_trx);
1282 }
1283 }
1284 }
1285
1286 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED( has_lock, conn_data);
1287
1288 return(conn_data);
1289 }
1290
1291 /*** allocate ***/
1292
1293 /*******************************************************************//**
1294 Allocate gets a struct item from the slab allocator, and fills in
1295 everything but the value. It seems like we can just pass this on to
1296 the default engine; we'll intercept it later in store(). */
1297 static
1298 ENGINE_ERROR_CODE
innodb_allocate(ENGINE_HANDLE * handle,const void * cookie,item ** item,const void * key,const size_t nkey,const size_t nbytes,const int flags,const rel_time_t exptime)1299 innodb_allocate(
1300 /*============*/
1301 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1302 const void* cookie, /*!< in: connection cookie */
1303 item ** item, /*!< out: item to allocate */
1304 const void* key, /*!< in: key */
1305 const size_t nkey, /*!< in: key length */
1306 const size_t nbytes, /*!< in: estimated value length */
1307 const int flags, /*!< in: flag */
1308 const rel_time_t exptime) /*!< in: expiration time */
1309 {
1310 size_t len;
1311 struct innodb_engine* innodb_eng = innodb_handle(handle);
1312 struct default_engine* def_eng = default_handle(innodb_eng);
1313 innodb_conn_data_t* conn_data;
1314 hash_item* it = NULL;
1315 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
1316
1317
1318 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
1319
1320 if (!conn_data) {
1321 conn_data = innodb_conn_init(innodb_eng, cookie,
1322 CONN_MODE_WRITE,
1323 IB_LOCK_X, false, NULL);
1324 if (!conn_data) {
1325 return(ENGINE_TMPFAIL);
1326 }
1327 }
1328
1329 /* If system configured to use Memcached default engine (instead
1330 of InnoDB engine), continue to use Memcached's default memory
1331 allocation */
1332 if (meta_info->set_option == META_CACHE_OPT_DEFAULT
1333 || meta_info->set_option == META_CACHE_OPT_MIX) {
1334 conn_data->use_default_mem = true;
1335 conn_data->in_use = false;
1336 return(def_eng->engine.allocate(
1337 innodb_eng->default_engine,
1338 cookie, item, key, nkey, nbytes,
1339 flags, exptime));
1340 }
1341
1342 conn_data->use_default_mem = false;
1343 len = sizeof(*it) + nkey + nbytes + sizeof(uint64_t);
1344 if (len > conn_data->cmd_buf_len) {
1345 free(conn_data->cmd_buf);
1346 conn_data->cmd_buf = malloc(len);
1347 conn_data->cmd_buf_len = len;
1348 }
1349
1350 it = (hash_item*) conn_data->cmd_buf;
1351
1352 it->next = it->prev = it->h_next = 0;
1353 it->refcount = 1;
1354 it->iflag = def_eng->config.use_cas ? ITEM_WITH_CAS : 0;
1355 it->nkey = nkey;
1356 it->nbytes = nbytes;
1357 it->flags = flags;
1358 it->slabs_clsid = 1;
1359 /* item_get_key() is a memcached code, here we cast away const return */
1360 memcpy((void*) item_get_key(it), key, nkey);
1361 it->exptime = exptime;
1362
1363 *item = it;
1364 conn_data->in_use = false;
1365
1366 return(ENGINE_SUCCESS);
1367 }
1368 /*******************************************************************//**
1369 Cleanup connections
1370 @return number of connection cleaned */
1371 static
1372 ENGINE_ERROR_CODE
innodb_remove(ENGINE_HANDLE * handle,const void * cookie,const void * key,const size_t nkey,uint64_t cas,uint16_t vbucket)1373 innodb_remove(
1374 /*==========*/
1375 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1376 const void* cookie, /*!< in: connection cookie */
1377 const void* key, /*!< in: key */
1378 const size_t nkey, /*!< in: key length */
1379 uint64_t cas __attribute__((unused)),
1380 /*!< in: cas */
1381 uint16_t vbucket __attribute__((unused)))
1382 /*!< in: bucket, used by default
1383 engine only */
1384 {
1385 struct innodb_engine* innodb_eng = innodb_handle(handle);
1386 struct default_engine* def_eng = default_handle(innodb_eng);
1387 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
1388 innodb_conn_data_t* conn_data;
1389 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
1390 ENGINE_ERROR_CODE cacher_err = ENGINE_KEY_ENOENT;
1391
1392 if (meta_info->del_option == META_CACHE_OPT_DISABLE) {
1393 return(ENGINE_SUCCESS);
1394 }
1395
1396 if (meta_info->del_option == META_CACHE_OPT_DEFAULT
1397 || meta_info->del_option == META_CACHE_OPT_MIX) {
1398 hash_item* item = item_get(def_eng, key, nkey);
1399
1400 if (item != NULL) {
1401 item_unlink(def_eng, item);
1402 item_release(def_eng, item);
1403 cacher_err = ENGINE_SUCCESS;
1404 }
1405
1406 if (meta_info->del_option == META_CACHE_OPT_DEFAULT) {
1407 return(cacher_err);
1408 }
1409 }
1410
1411 conn_data = innodb_conn_init(innodb_eng, cookie,
1412 CONN_MODE_WRITE, IB_LOCK_X, false,
1413 NULL);
1414
1415 if (!conn_data) {
1416 return(ENGINE_TMPFAIL);
1417 }
1418
1419 /* In the binary protocol there is such a thing as a CAS delete.
1420 This is the CAS check. If we will also be deleting from the database,
1421 there are two possibilities:
1422 1: The CAS matches; perform the delete.
1423 2: The CAS doesn't match; delete the item because it's stale.
1424 Therefore we skip the check altogether if(do_db_delete) */
1425
1426 err_ret = innodb_api_delete(innodb_eng, conn_data, key, nkey);
1427
1428 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_DELETE,
1429 err_ret == ENGINE_SUCCESS);
1430
1431 return((cacher_err == ENGINE_SUCCESS) ? ENGINE_SUCCESS : err_ret);
1432 }
1433
1434 /*******************************************************************//**
1435 Switch the table mapping. Open the new table specified in "@@new_table_map.key"
1436 string.
1437 @return ENGINE_SUCCESS if successful, otherwise error code */
1438 static
1439 ENGINE_ERROR_CODE
innodb_switch_mapping(ENGINE_HANDLE * handle,const void * cookie,const char * name,size_t * name_len,bool has_prefix)1440 innodb_switch_mapping(
1441 /*==================*/
1442 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1443 const void* cookie, /*!< in: connection cookie */
1444 const char* name, /*!< in: full name contains
1445 table map name, and possible
1446 key value */
1447 size_t* name_len, /*!< in/out: name length,
1448 out with length excludes
1449 the table map name */
1450 bool has_prefix) /*!< in: whether the name has
1451 "@@" prefix */
1452 {
1453 struct innodb_engine* innodb_eng = innodb_handle(handle);
1454 innodb_conn_data_t* conn_data;
1455 char new_name[KEY_MAX_LENGTH];
1456 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
1457 char* new_map_name;
1458 unsigned int new_map_name_len = 0;
1459 char* last;
1460 meta_cfg_info_t* new_meta_info;
1461 int sep_len = 0;
1462
1463 if (has_prefix) {
1464 char* sep = NULL;
1465
1466 assert(*name_len > 2 && name[0] == '@' && name[1] == '@');
1467 assert(*name_len < KEY_MAX_LENGTH);
1468
1469 memcpy(new_name, &name[2], (*name_len) - 2);
1470
1471 new_name[*name_len - 2] = 0;
1472
1473 GET_OPTION(meta_info, OPTION_ID_TBL_MAP_SEP, sep, sep_len);
1474
1475 assert(sep_len > 0);
1476
1477 new_map_name = strtok_r(new_name, sep, &last);
1478
1479 if (new_map_name == NULL) {
1480 return(ENGINE_KEY_ENOENT);
1481 }
1482
1483 new_map_name_len = strlen(new_map_name);
1484 } else {
1485 /* This is used in the "bind" command, and without the
1486 "@@" prefix. */
1487 if (name == NULL) {
1488 return(ENGINE_KEY_ENOENT);
1489 }
1490
1491 new_map_name = (char*) name;
1492 new_map_name_len = *name_len;
1493 }
1494
1495 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
1496
1497 /* Check if we are getting the same configure setting as existing one */
1498 if (conn_data && conn_data->conn_meta
1499 && (new_map_name_len
1500 == conn_data->conn_meta->col_info[CONTAINER_NAME].col_name_len)
1501 && (strcmp(
1502 new_map_name,
1503 conn_data->conn_meta->col_info[CONTAINER_NAME].col_name) == 0)) {
1504 goto get_key_name;
1505 }
1506
1507 new_meta_info = innodb_config(
1508 new_map_name, new_map_name_len, &innodb_eng->meta_hash);
1509
1510 if (!new_meta_info) {
1511 return(ENGINE_KEY_ENOENT);
1512 }
1513
1514 /* Clean up the existing connection metadata if exists */
1515 if (conn_data) {
1516 innodb_conn_clean_data(conn_data, false, false);
1517
1518 /* Point to the new metadata */
1519 conn_data->conn_meta = new_meta_info;
1520 }
1521
1522 conn_data = innodb_conn_init(innodb_eng, cookie,
1523 CONN_MODE_NONE, 0, false,
1524 new_meta_info);
1525
1526 assert(conn_data->conn_meta == new_meta_info);
1527
1528 get_key_name:
1529 /* Now calculate name length exclude the table mapping name,
1530 this is the length for the remaining key portion */
1531 if (has_prefix) {
1532 assert(*name_len >= strlen(new_map_name) + 2);
1533
1534 if (*name_len >= strlen(new_map_name) + 2 + sep_len) {
1535 *name_len -= strlen(new_map_name) + 2 + sep_len;
1536 } else {
1537 /* the name does not even contain a delimiter,
1538 so there will be no keys either */
1539 *name_len = 0;
1540 }
1541 }
1542
1543 return(ENGINE_SUCCESS);
1544 }
1545
1546 /*******************************************************************//**
1547 check whether a table mapping switch is needed, if so, switch the table
1548 mapping
1549 @return ENGINE_SUCCESS if successful otherwise error code */
1550 static inline
1551 ENGINE_ERROR_CODE
check_key_name_for_map_switch(ENGINE_HANDLE * handle,const void * cookie,const void * key,size_t * nkey)1552 check_key_name_for_map_switch(
1553 /*==========================*/
1554 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
1555 const void* cookie, /*!< in: connection cookie */
1556 const void* key, /*!< in: search key */
1557 size_t* nkey) /*!< in/out: key length */
1558 {
1559 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
1560
1561 if ((*nkey) > 3 && ((char*)key)[0] == '@'
1562 && ((char*)key)[1] == '@') {
1563 err_ret = innodb_switch_mapping(handle, cookie, key, nkey, true);
1564 }
1565
1566 return(err_ret);
1567 }
1568
1569 /*******************************************************************//**
1570 Function to support the "bind" command, bind the connection to a new
1571 table mapping.
1572 @return ENGINE_SUCCESS if successful, otherwise error code */
1573 static
1574 ENGINE_ERROR_CODE
innodb_bind(ENGINE_HANDLE * handle,const void * cookie,const void * name,size_t name_len)1575 innodb_bind(
1576 /*========*/
1577 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1578 const void* cookie, /*!< in: connection cookie */
1579 const void* name, /*!< in: table ID name */
1580 size_t name_len) /*!< in: name length */
1581 {
1582 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
1583
1584 err_ret = innodb_switch_mapping(handle, cookie, name, &name_len, false);
1585
1586 return(err_ret);
1587 }
1588
1589 /*******************************************************************//**
1590 Release the connection, free resource allocated in innodb_allocate */
1591 static
1592 void
innodb_clean_engine(ENGINE_HANDLE * handle,const void * cookie,void * conn)1593 innodb_clean_engine(
1594 /*================*/
1595 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1596 const void* cookie __attribute__((unused)),
1597 /*!< in: connection cookie */
1598 void* conn) /*!< in: item to free */
1599 {
1600 innodb_conn_data_t* conn_data = (innodb_conn_data_t*)conn;
1601 struct innodb_engine* engine = innodb_handle(handle);
1602 void* orignal_thd;
1603
1604 LOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
1605 if (conn_data->thd) {
1606 handler_thd_attach(conn_data->thd, &orignal_thd);
1607 }
1608 innodb_reset_conn(conn_data, true, true, engine->enable_binlog);
1609 innodb_conn_clean_data(conn_data, true, false);
1610 conn_data->is_stale = true;
1611 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
1612 }
1613
1614 /*******************************************************************//**
1615 Release the connection, free resource allocated in innodb_allocate */
1616 static
1617 void
innodb_release(ENGINE_HANDLE * handle,const void * cookie,item * item)1618 innodb_release(
1619 /*===========*/
1620 ENGINE_HANDLE* handle, /*!< in: Engine handle */
1621 const void* cookie __attribute__((unused)),
1622 /*!< in: connection cookie */
1623 item* item __attribute__((unused)))
1624 /*!< in: item to free */
1625 {
1626 struct innodb_engine* innodb_eng = innodb_handle(handle);
1627 innodb_conn_data_t* conn_data;
1628
1629 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
1630
1631 if (!conn_data) {
1632 return;
1633 }
1634
1635 conn_data->result_in_use = false;
1636
1637 /* If item's memory comes from Memcached default engine, release it
1638 through Memcached APIs */
1639 if (conn_data->use_default_mem) {
1640 struct default_engine* def_eng = default_handle(innodb_eng);
1641
1642 item_release(def_eng, (hash_item *) item);
1643 conn_data->use_default_mem = false;
1644 }
1645
1646 return;
1647 }
1648
1649 /* maximum number of characters that an 8 bytes integer can convert to */
1650 #define MAX_INT_CHAR_LEN 21
1651
1652 /*******************************************************************//**
1653 Convert an bit int to string
1654 @return length of string */
1655 static
1656 int
convert_to_char(char * buf,int buf_len,void * value,int value_len,bool is_unsigned)1657 convert_to_char(
1658 /*============*/
1659 char* buf, /*!< out: converted integer value */
1660 int buf_len, /*!< in: buffer len */
1661 void* value, /*!< in: int value */
1662 int value_len, /*!< in: int len */
1663 bool is_unsigned) /*!< in: whether it is unsigned */
1664 {
1665 assert(buf && buf_len);
1666
1667 if (value_len == 8) {
1668 if (is_unsigned) {
1669 uint64_t int_val = *(uint64_t*)value;
1670 snprintf(buf, buf_len, "%" PRIu64, int_val);
1671 } else {
1672 int64_t int_val = *(int64_t*)value;
1673 snprintf(buf, buf_len, "%" PRIi64, int_val);
1674 }
1675 } else if (value_len == 4) {
1676 if (is_unsigned) {
1677 uint32_t int_val = *(uint32_t*)value;
1678 snprintf(buf, buf_len, "%" PRIu32, int_val);
1679 } else {
1680 int32_t int_val = *(int32_t*)value;
1681 snprintf(buf, buf_len, "%" PRIi32, int_val);
1682 }
1683 } else if (value_len == 2) {
1684 if (is_unsigned) {
1685 uint16_t int_val = *(uint16_t*)value;
1686 snprintf(buf, buf_len, "%" PRIu16, int_val);
1687 } else {
1688 int16_t int_val = *(int16_t*)value;
1689 snprintf(buf, buf_len, "%" PRIi16, int_val);
1690 }
1691 } else if (value_len == 1) {
1692 if (is_unsigned) {
1693 uint8_t int_val = *(uint8_t*)value;
1694 snprintf(buf, buf_len, "%" PRIu8, int_val);
1695 } else {
1696 int8_t int_val = *(int8_t*)value;
1697 snprintf(buf, buf_len, "%" PRIi8, int_val);
1698 }
1699 }
1700
1701 return(strlen(buf));
1702 }
1703
1704
1705 /*******************************************************************//**
1706 Free value assocaited with key */
1707 static
1708 void
innodb_free_item(void * item)1709 innodb_free_item(
1710 /*=====================*/
1711 void* item) /*!< in: Item to be freed */
1712 {
1713
1714 mci_item_t* result = (mci_item_t*) item;
1715 if (result->extra_col_value) {
1716 for (int i = 0; i < result->n_extra_col; i++) {
1717 if(result->extra_col_value[i].allocated)
1718 free(result->extra_col_value[i].value_str);
1719 }
1720 free(result->extra_col_value);
1721 result->extra_col_value=NULL;
1722 }
1723 if (result->col_value[MCI_COL_VALUE].allocated) {
1724 free(result->col_value[MCI_COL_VALUE].value_str);
1725 result->col_value[MCI_COL_VALUE].allocated =
1726 false;
1727 }
1728 }
1729 /*******************************************************************//**
1730 Support memcached "GET" command, fetch the value according to key
1731 @return ENGINE_SUCCESS if successfully, otherwise error code */
1732 static
1733 ENGINE_ERROR_CODE
innodb_get(ENGINE_HANDLE * handle,const void * cookie,item ** item,const void * key,const int nkey,uint16_t vbucket)1734 innodb_get(
1735 /*=======*/
1736 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
1737 const void* cookie, /*!< in: connection cookie */
1738 item** item, /*!< out: item to fill */
1739 const void* key, /*!< in: search key */
1740 const int nkey, /*!< in: key length */
1741 uint16_t vbucket __attribute__((unused)))
1742 /*!< in: bucket, used by default
1743 engine only */
1744 {
1745 struct innodb_engine* innodb_eng = innodb_handle(handle);
1746 ib_crsr_t crsr;
1747 ib_err_t err = DB_SUCCESS;
1748 mci_item_t* result = NULL;
1749 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
1750 innodb_conn_data_t* conn_data = NULL;
1751 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
1752 int option_length;
1753 const char* option_delimiter;
1754 size_t key_len = nkey;
1755 int lock_mode;
1756 bool report_table_switch = false;
1757
1758 if (meta_info->get_option == META_CACHE_OPT_DISABLE) {
1759 return(ENGINE_KEY_ENOENT);
1760 }
1761
1762 if (meta_info->get_option == META_CACHE_OPT_DEFAULT
1763 || meta_info->get_option == META_CACHE_OPT_MIX) {
1764 *item = item_get(default_handle(innodb_eng), key, nkey);
1765
1766 if (*item != NULL) {
1767 return(ENGINE_SUCCESS);
1768 }
1769
1770 if (meta_info->get_option == META_CACHE_OPT_DEFAULT) {
1771 return(ENGINE_KEY_ENOENT);
1772 }
1773 }
1774
1775 /* Check if we need to switch table mapping */
1776 err_ret = check_key_name_for_map_switch(handle, cookie, key, &key_len);
1777
1778 /* If specified new table map does not exist, or table does not
1779 qualify for InnoDB memcached, return error */
1780 if (err_ret != ENGINE_SUCCESS) {
1781 goto err_exit;
1782 }
1783
1784 /* If only the new mapping name is provided, and no key value,
1785 return here */
1786 if (key_len <= 0) {
1787 /* If this is a command in the form of "get @@new_table_map",
1788 for the purpose of switching to the specified table with
1789 the table map name, if the switch is successful, we will
1790 return the table name as result */
1791 if (nkey > 0) {
1792 report_table_switch = true;
1793
1794 goto search_done;
1795 }
1796
1797 err_ret = ENGINE_KEY_ENOENT;
1798 goto err_exit;
1799 }
1800
1801 lock_mode = (innodb_eng->trx_level == IB_TRX_SERIALIZABLE
1802 && innodb_eng->read_batch_size == 1)
1803 ? IB_LOCK_S
1804 : IB_LOCK_NONE;
1805
1806 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_READ,
1807 lock_mode, false, NULL);
1808
1809 if (!conn_data) {
1810 return(ENGINE_TMPFAIL);
1811 }
1812
1813 result = (mci_item_t*)(conn_data->result);
1814
1815 err = innodb_api_search(conn_data, &crsr, key + nkey - key_len,
1816 key_len, result, NULL, true);
1817
1818 if (err != DB_SUCCESS) {
1819 err_ret = ENGINE_KEY_ENOENT;
1820 goto func_exit;
1821 }
1822
1823 search_done:
1824 if (report_table_switch) {
1825 char table_name[MAX_TABLE_NAME_LEN
1826 + MAX_DATABASE_NAME_LEN];
1827 char* name;
1828 char* dbname;
1829
1830 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
1831 assert(nkey > 0);
1832
1833 name = conn_data->conn_meta->col_info[CONTAINER_TABLE].col_name;
1834 dbname = conn_data->conn_meta->col_info[CONTAINER_DB].col_name;
1835 #ifdef __WIN__
1836 sprintf(table_name, "%s\%s", dbname, name);
1837 #else
1838 snprintf(table_name, sizeof(table_name),
1839 "%s/%s", dbname, name);
1840 #endif
1841
1842 assert(!conn_data->result_in_use);
1843 conn_data->result_in_use = true;
1844 result = (mci_item_t*)(conn_data->result);
1845
1846 memset(result, 0, sizeof(*result));
1847
1848 memcpy(conn_data->row_buf, table_name, strlen(table_name));
1849
1850 result->col_value[MCI_COL_VALUE].value_str = conn_data->row_buf;
1851 result->col_value[MCI_COL_VALUE].value_len = strlen(table_name);
1852 result->col_value[MCI_COL_VALUE].is_str = true;
1853 }
1854
1855 result->col_value[MCI_COL_KEY].value_str = (char*)key;
1856 result->col_value[MCI_COL_KEY].value_len = nkey;
1857
1858 /* Only if expiration field is enabled, and the value is not zero,
1859 we will check whether the item is expired */
1860 if (result->col_value[MCI_COL_EXP].is_valid
1861 && result->col_value[MCI_COL_EXP].value_int) {
1862 uint64_t time;
1863 time = mci_get_time();
1864 if (time > result->col_value[MCI_COL_EXP].value_int) {
1865 innodb_free_item(result);
1866 err_ret = ENGINE_KEY_ENOENT;
1867 goto func_exit;
1868 }
1869 }
1870
1871 if (result->extra_col_value) {
1872 int i;
1873 char* c_value;
1874 char* value_end;
1875 unsigned int total_len = 0;
1876 char int_buf[MAX_INT_CHAR_LEN];
1877
1878 GET_OPTION(meta_info, OPTION_ID_COL_SEP, option_delimiter,
1879 option_length);
1880
1881 assert(option_length > 0 && option_delimiter);
1882
1883 for (i = 0; i < result->n_extra_col; i++) {
1884 mci_column_t* mci_item = &result->extra_col_value[i];
1885
1886 if (mci_item->value_len == 0) {
1887 total_len += option_length;
1888 continue;
1889 }
1890
1891 if (!mci_item->is_str) {
1892 memset(int_buf, 0, sizeof int_buf);
1893 assert(!mci_item->value_str);
1894
1895 total_len += convert_to_char(
1896 int_buf, sizeof int_buf,
1897 &mci_item->value_int,
1898 mci_item->value_len,
1899 mci_item->is_unsigned);
1900 } else {
1901 total_len += result->extra_col_value[i].value_len;
1902 }
1903
1904 total_len += option_length;
1905 }
1906
1907 /* No need to add the last separator */
1908 total_len -= option_length;
1909
1910 if (total_len > conn_data->mul_col_buf_len) {
1911 if (conn_data->mul_col_buf) {
1912 free(conn_data->mul_col_buf);
1913 }
1914
1915 conn_data->mul_col_buf = malloc(total_len + 1);
1916 conn_data->mul_col_buf_len = total_len;
1917 }
1918
1919 c_value = conn_data->mul_col_buf;
1920 value_end = conn_data->mul_col_buf + total_len;
1921
1922 for (i = 0; i < result->n_extra_col; i++) {
1923 mci_column_t* col_value;
1924
1925 col_value = &result->extra_col_value[i];
1926
1927 if (col_value->value_len != 0) {
1928 if (!col_value->is_str) {
1929 int int_len;
1930 memset(int_buf, 0, sizeof int_buf);
1931
1932 int_len = convert_to_char(
1933 int_buf,
1934 sizeof int_buf,
1935 &col_value->value_int,
1936 col_value->value_len,
1937 col_value->is_unsigned);
1938
1939 assert(int_len <= conn_data->mul_col_buf_len);
1940
1941 memcpy(c_value, int_buf, int_len);
1942 c_value += int_len;
1943 } else {
1944 memcpy(c_value,
1945 col_value->value_str,
1946 col_value->value_len);
1947 c_value += col_value->value_len;
1948 }
1949 }
1950
1951 if (i < result->n_extra_col - 1 ) {
1952 memcpy(c_value, option_delimiter, option_length);
1953 c_value += option_length;
1954 }
1955
1956 assert(c_value <= value_end);
1957
1958 if (col_value->allocated) {
1959 free(col_value->value_str);
1960 }
1961 }
1962
1963 result->col_value[MCI_COL_VALUE].value_str = conn_data->mul_col_buf;
1964 result->col_value[MCI_COL_VALUE].value_len = total_len;
1965 ((char*)result->col_value[MCI_COL_VALUE].value_str)[total_len] = 0;
1966
1967 free(result->extra_col_value);
1968 } else if (!result->col_value[MCI_COL_VALUE].is_str
1969 && result->col_value[MCI_COL_VALUE].value_len != 0) {
1970 unsigned int int_len;
1971 char int_buf[MAX_INT_CHAR_LEN];
1972
1973 int_len = convert_to_char(
1974 int_buf, sizeof int_buf,
1975 &result->col_value[MCI_COL_VALUE].value_int,
1976 result->col_value[MCI_COL_VALUE].value_len,
1977 result->col_value[MCI_COL_VALUE].is_unsigned);
1978
1979 if (int_len > conn_data->mul_col_buf_len) {
1980 if (conn_data->mul_col_buf) {
1981 free(conn_data->mul_col_buf);
1982 }
1983
1984 conn_data->mul_col_buf = malloc(int_len + 1);
1985 conn_data->mul_col_buf_len = int_len;
1986 }
1987
1988 if (int_len > 0)
1989 memcpy(conn_data->mul_col_buf, int_buf, int_len);
1990 result->col_value[MCI_COL_VALUE].value_str =
1991 conn_data->mul_col_buf;
1992
1993 result->col_value[MCI_COL_VALUE].value_len = int_len;
1994 }
1995
1996 *item = result;
1997
1998 func_exit:
1999
2000 if (!report_table_switch) {
2001 innodb_api_cursor_reset(innodb_eng, conn_data,
2002 CONN_OP_READ, true);
2003 }
2004
2005 err_exit:
2006
2007 /* If error return, memcached will not call InnoDB Memcached's
2008 callback function "innodb_release" to reset the result_in_use
2009 value. So we reset it here */
2010 if (err_ret != ENGINE_SUCCESS && conn_data) {
2011 conn_data->result_in_use = false;
2012 }
2013 return(err_ret);
2014 }
2015
2016 /*******************************************************************//**
2017 Get statistics info
2018 @return ENGINE_SUCCESS if successfully, otherwise error code */
2019 static
2020 ENGINE_ERROR_CODE
innodb_get_stats(ENGINE_HANDLE * handle,const void * cookie,const char * stat_key,int nkey,ADD_STAT add_stat)2021 innodb_get_stats(
2022 /*=============*/
2023 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2024 const void* cookie, /*!< in: connection cookie */
2025 const char* stat_key, /*!< in: statistics key */
2026 int nkey, /*!< in: key length */
2027 ADD_STAT add_stat) /*!< out: stats to fill */
2028 {
2029 struct innodb_engine* innodb_eng = innodb_handle(handle);
2030 struct default_engine *def_eng = default_handle(innodb_eng);
2031 return(def_eng->engine.get_stats(innodb_eng->default_engine, cookie,
2032 stat_key, nkey, add_stat));
2033 }
2034
2035 /*******************************************************************//**
2036 reset statistics
2037 @return ENGINE_SUCCESS if successfully, otherwise error code */
2038 static
2039 void
innodb_reset_stats(ENGINE_HANDLE * handle,const void * cookie)2040 innodb_reset_stats(
2041 /*===============*/
2042 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2043 const void* cookie) /*!< in: connection cookie */
2044 {
2045 struct innodb_engine* innodb_eng = innodb_handle(handle);
2046 struct default_engine *def_eng = default_handle(innodb_eng);
2047 def_eng->engine.reset_stats(innodb_eng->default_engine, cookie);
2048 }
2049
2050 /*******************************************************************//**
2051 API interface for memcached's "SET", "ADD", "REPLACE", "APPEND"
2052 "PREPENT" and "CAS" commands
2053 @return ENGINE_SUCCESS if successfully, otherwise error code */
2054 static
2055 ENGINE_ERROR_CODE
innodb_store(ENGINE_HANDLE * handle,const void * cookie,item * item,uint64_t * cas,ENGINE_STORE_OPERATION op,uint16_t vbucket)2056 innodb_store(
2057 /*=========*/
2058 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2059 const void* cookie, /*!< in: connection cookie */
2060 item* item, /*!< out: result to fill */
2061 uint64_t* cas, /*!< in: cas value */
2062 ENGINE_STORE_OPERATION op, /*!< in: type of operation */
2063 uint16_t vbucket __attribute__((unused)))
2064 /*!< in: bucket, used by default
2065 engine only */
2066 {
2067 struct innodb_engine* innodb_eng = innodb_handle(handle);
2068 uint16_t len = hash_item_get_key_len(item);
2069 char* value = hash_item_get_key(item);
2070 uint64_t exptime = hash_item_get_exp(item);
2071 uint64_t flags = hash_item_get_flag(item);
2072 ENGINE_ERROR_CODE result;
2073 uint64_t input_cas;
2074 innodb_conn_data_t* conn_data;
2075 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2076 uint32_t val_len = ((hash_item*)item)->nbytes;
2077 size_t key_len = len;
2078 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
2079
2080 if (meta_info->set_option == META_CACHE_OPT_DISABLE) {
2081 return(ENGINE_SUCCESS);
2082 }
2083
2084 if (meta_info->set_option == META_CACHE_OPT_DEFAULT
2085 || meta_info->set_option == META_CACHE_OPT_MIX) {
2086 result = store_item(default_handle(innodb_eng), item, cas,
2087 op, cookie);
2088
2089 if (meta_info->set_option == META_CACHE_OPT_DEFAULT) {
2090 return(result);
2091 }
2092 }
2093
2094 err_ret = check_key_name_for_map_switch(handle, cookie,
2095 value, &key_len);
2096
2097 if (err_ret != ENGINE_SUCCESS) {
2098 return(err_ret);
2099 }
2100
2101 /* If no key is provided, return here */
2102 if (key_len <= 0) {
2103 return(ENGINE_NOT_STORED);
2104 }
2105
2106 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2107 IB_LOCK_X, false, NULL);
2108
2109 if (!conn_data) {
2110 return(ENGINE_NOT_STORED);
2111 }
2112
2113 input_cas = hash_item_get_cas(item);
2114
2115 result = innodb_api_store(innodb_eng, conn_data, value + len - key_len,
2116 key_len, val_len, exptime, cas, input_cas,
2117 flags, op);
2118
2119 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_WRITE,
2120 result == ENGINE_SUCCESS);
2121 return(result);
2122 }
2123
2124 /*******************************************************************//**
2125 Support memcached "INCR" and "DECR" command, add or subtract a "delta"
2126 value from an integer key value
2127 @return ENGINE_SUCCESS if successfully, otherwise error code */
2128 static
2129 ENGINE_ERROR_CODE
innodb_arithmetic(ENGINE_HANDLE * handle,const void * cookie,const void * key,const int nkey,const bool increment,const bool create,const uint64_t delta,const uint64_t initial,const rel_time_t exptime,uint64_t * cas,uint64_t * result,uint16_t vbucket)2130 innodb_arithmetic(
2131 /*==============*/
2132 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2133 const void* cookie, /*!< in: connection cookie */
2134 const void* key, /*!< in: key for the value to add */
2135 const int nkey, /*!< in: key length */
2136 const bool increment, /*!< in: whether to increment
2137 or decrement */
2138 const bool create, /*!< in: whether to create the key
2139 value pair if can't find */
2140 const uint64_t delta, /*!< in: value to add/substract */
2141 const uint64_t initial, /*!< in: initial */
2142 const rel_time_t exptime, /*!< in: expiration time */
2143 uint64_t* cas, /*!< out: new cas value */
2144 uint64_t* result, /*!< out: result value */
2145 uint16_t vbucket) /*!< in: bucket, used by default
2146 engine only */
2147 {
2148 struct innodb_engine* innodb_eng = innodb_handle(handle);
2149 struct default_engine* def_eng = default_handle(innodb_eng);
2150 innodb_conn_data_t* conn_data;
2151 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2152 ENGINE_ERROR_CODE err_ret;
2153
2154 if (meta_info->set_option == META_CACHE_OPT_DISABLE) {
2155 return(ENGINE_SUCCESS);
2156 }
2157
2158 if (meta_info->set_option == META_CACHE_OPT_DEFAULT
2159 || meta_info->set_option == META_CACHE_OPT_MIX) {
2160 /* For cache-only, forward this to the
2161 default engine */
2162 err_ret = def_eng->engine.arithmetic(
2163 innodb_eng->default_engine, cookie, key, nkey,
2164 increment, create, delta, initial, exptime, cas,
2165 result, vbucket);
2166
2167 if (meta_info->set_option == META_CACHE_OPT_DEFAULT) {
2168 return(err_ret);
2169 }
2170 }
2171
2172 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2173 IB_LOCK_X, false, NULL);
2174
2175 if (!conn_data) {
2176 return(ENGINE_NOT_STORED);
2177 }
2178
2179 err_ret = innodb_api_arithmetic(innodb_eng, conn_data, key, nkey,
2180 delta, increment, cas, exptime,
2181 create, initial, result);
2182
2183 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_WRITE,
2184 true);
2185
2186 return(err_ret);
2187 }
2188
2189 /*******************************************************************//**
2190 Cleanup idle connections if "clear_all" is false, and clean up all
2191 connections if "clear_all" is true.
2192 @return number of connection cleaned */
2193 static
2194 bool
innodb_flush_sync_conn(innodb_engine_t * engine,const void * cookie,bool flush_flag)2195 innodb_flush_sync_conn(
2196 /*===================*/
2197 innodb_engine_t* engine, /*!< in/out: InnoDB memcached
2198 engine */
2199 const void* cookie, /*!< in: connection cookie */
2200 bool flush_flag) /*!< in: flush is running or not */
2201 {
2202 innodb_conn_data_t* conn_data = NULL;
2203 innodb_conn_data_t* curr_conn_data;
2204 bool ret = true;
2205
2206 curr_conn_data = engine->server.cookie->get_engine_specific(cookie);
2207 assert(curr_conn_data);
2208
2209 conn_data = UT_LIST_GET_FIRST(engine->conn_data);
2210
2211 while (conn_data) {
2212 if (conn_data != curr_conn_data && (!conn_data->is_stale)) {
2213 if (conn_data->thd) {
2214 handler_thd_attach(conn_data->thd, NULL);
2215 }
2216 LOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2217 if (flush_flag == false) {
2218 conn_data->is_flushing = flush_flag;
2219 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2220 conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
2221 continue;
2222 }
2223 if (!conn_data->in_use) {
2224 /* Set flushing flag to conn_data for preventing
2225 it is get by other request. */
2226 conn_data->is_flushing = flush_flag;
2227 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2228 } else {
2229 ret = false;
2230 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2231 break;
2232 }
2233 }
2234 conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
2235 }
2236
2237 if (curr_conn_data->thd) {
2238 handler_thd_attach(curr_conn_data->thd, NULL);
2239 }
2240
2241 return(ret);
2242 }
2243
2244 /*******************************************************************//**
2245 Support memcached "FLUSH_ALL" command, clean up storage (trunate InnoDB Table)
2246 @return ENGINE_SUCCESS if successfully, otherwise error code */
2247 static
2248 ENGINE_ERROR_CODE
innodb_flush(ENGINE_HANDLE * handle,const void * cookie,time_t when)2249 innodb_flush(
2250 /*=========*/
2251 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2252 const void* cookie, /*!< in: connection cookie */
2253 time_t when) /*!< in: when to flush, not used by
2254 InnoDB */
2255 {
2256 struct innodb_engine* innodb_eng = innodb_handle(handle);
2257 struct default_engine* def_eng = default_handle(innodb_eng);
2258 ENGINE_ERROR_CODE err = ENGINE_SUCCESS;
2259 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2260 ib_err_t ib_err = DB_SUCCESS;
2261 innodb_conn_data_t* conn_data;
2262
2263 if (meta_info->flush_option == META_CACHE_OPT_DISABLE) {
2264 return(ENGINE_SUCCESS);
2265 }
2266
2267 if (meta_info->flush_option == META_CACHE_OPT_DEFAULT
2268 || meta_info->flush_option == META_CACHE_OPT_MIX) {
2269 /* default engine flush */
2270 err = def_eng->engine.flush(innodb_eng->default_engine,
2271 cookie, when);
2272
2273 if (meta_info->flush_option == META_CACHE_OPT_DEFAULT) {
2274 return(err);
2275 }
2276 }
2277
2278 /* Lock the whole engine, so no other connection can start
2279 new opeartion */
2280 pthread_mutex_lock(&innodb_eng->conn_mutex);
2281
2282 /* Lock the flush_mutex for blocking other DMLs. */
2283 pthread_mutex_lock(&innodb_eng->flush_mutex);
2284
2285 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
2286
2287 if (conn_data) {
2288 /* Commit any work on this connection */
2289 innodb_api_cursor_reset(innodb_eng, conn_data,
2290 CONN_OP_FLUSH, true);
2291 }
2292
2293 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2294 IB_LOCK_TABLE_X, true, NULL);
2295
2296 if (!conn_data) {
2297 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2298 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2299 return(ENGINE_SUCCESS);
2300 }
2301
2302 /* Commit any previous work on this connection */
2303 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_FLUSH, true);
2304
2305 if (!innodb_flush_sync_conn(innodb_eng, cookie, true)) {
2306 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2307 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2308 innodb_flush_sync_conn(innodb_eng, cookie, false);
2309 return(ENGINE_TMPFAIL);
2310 }
2311
2312 ib_err = innodb_api_flush(innodb_eng, conn_data,
2313 meta_info->col_info[CONTAINER_DB].col_name,
2314 meta_info->col_info[CONTAINER_TABLE].col_name);
2315
2316 /* Commit work and release the MDL table. */
2317 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_FLUSH, true);
2318 innodb_conn_clean_data(conn_data, false, false);
2319
2320 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2321 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2322
2323 innodb_flush_sync_conn(innodb_eng, cookie, false);
2324
2325 return((ib_err == DB_SUCCESS) ? ENGINE_SUCCESS : ENGINE_TMPFAIL);
2326 }
2327
2328 /*******************************************************************//**
2329 Deal with unknown command. Currently not used
2330 @return ENGINE_SUCCESS if successfully processed, otherwise error code */
2331 static
2332 ENGINE_ERROR_CODE
innodb_unknown_command(ENGINE_HANDLE * handle,const void * cookie,protocol_binary_request_header * request,ADD_RESPONSE response)2333 innodb_unknown_command(
2334 /*===================*/
2335 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2336 const void* cookie, /*!< in: connection cookie */
2337 protocol_binary_request_header *request, /*!< in: request */
2338 ADD_RESPONSE response) /*!< out: respondse */
2339 {
2340 struct innodb_engine* innodb_eng = innodb_handle(handle);
2341 struct default_engine *def_eng = default_handle(innodb_eng);
2342
2343 return(def_eng->engine.unknown_command(innodb_eng->default_engine,
2344 cookie, request, response));
2345 }
2346
2347 /*******************************************************************//**
2348 Callback functions used by Memcached's process_command() function
2349 to get the result key/value information
2350 @return true if info fetched */
2351 static
2352 bool
innodb_get_item_info(ENGINE_HANDLE * handle,const void * cookie,const item * item,item_info * item_info)2353 innodb_get_item_info(
2354 /*=================*/
2355 ENGINE_HANDLE* handle __attribute__((unused)),
2356 /*!< in: Engine Handle */
2357 const void* cookie __attribute__((unused)),
2358 /*!< in: connection cookie */
2359 const item* item, /*!< in: item in question */
2360 item_info* item_info) /*!< out: item info got */
2361 {
2362 struct innodb_engine* innodb_eng = innodb_handle(handle);
2363 innodb_conn_data_t* conn_data;
2364
2365 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
2366
2367 if (!conn_data || !conn_data->result_in_use) {
2368 hash_item* it;
2369
2370 if (item_info->nvalue < 1) {
2371 return(false);
2372 }
2373
2374 /* Use a hash item */
2375 it = (hash_item*) item;
2376 item_info->cas = hash_item_get_cas(it);
2377 item_info->exptime = it->exptime;
2378 item_info->nbytes = it->nbytes;
2379 item_info->flags = it->flags;
2380 item_info->clsid = it->slabs_clsid;
2381 item_info->nkey = it->nkey;
2382 item_info->nvalue = 1;
2383 item_info->key = hash_item_get_key(it);
2384 item_info->value[0].iov_base = hash_item_get_data(it);
2385 item_info->value[0].iov_len = it->nbytes;
2386 } else {
2387 mci_item_t* it;
2388
2389 if (item_info->nvalue < 1) {
2390 return(false);
2391 }
2392
2393 /* Use a hash item */
2394 it = (mci_item_t*) item;
2395 if (it->col_value[MCI_COL_CAS].is_valid) {
2396 item_info->cas = it->col_value[MCI_COL_CAS].value_int;
2397 } else {
2398 item_info->cas = 0;
2399 }
2400
2401 if (it->col_value[MCI_COL_EXP].is_valid) {
2402 item_info->exptime = it->col_value[MCI_COL_EXP].value_int;
2403 } else {
2404 item_info->exptime = 0;
2405 }
2406
2407 item_info->nbytes = it->col_value[MCI_COL_VALUE].value_len;
2408
2409 if (it->col_value[MCI_COL_FLAG].is_valid) {
2410 item_info->flags = ntohl(
2411 it->col_value[MCI_COL_FLAG].value_int);
2412 } else {
2413 item_info->flags = 0;
2414 }
2415
2416 item_info->clsid = 1;
2417
2418 item_info->nkey = it->col_value[MCI_COL_KEY].value_len;
2419
2420 item_info->nvalue = 1;
2421
2422 item_info->key = it->col_value[MCI_COL_KEY].value_str;
2423
2424 item_info->value[0].iov_base = it->col_value[
2425 MCI_COL_VALUE].value_str;;
2426
2427 item_info->value[0].iov_len = it->col_value[
2428 MCI_COL_VALUE].value_len;
2429
2430 }
2431
2432 return(true);
2433 }
2434