1 /***********************************************************************
2
3 Copyright (c) 2011, 2021, Oracle and/or its affiliates.
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[0].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 } else {
1700 return 0;
1701 }
1702
1703 return(strlen(buf));
1704 }
1705
1706
1707 /*******************************************************************//**
1708 Free value assocaited with key */
1709 static
1710 void
innodb_free_item(void * item)1711 innodb_free_item(
1712 /*=====================*/
1713 void* item) /*!< in: Item to be freed */
1714 {
1715
1716 mci_item_t* result = (mci_item_t*) item;
1717 if (result->extra_col_value) {
1718 for (int i = 0; i < result->n_extra_col; i++) {
1719 if(result->extra_col_value[i].allocated)
1720 free(result->extra_col_value[i].value_str);
1721 }
1722 free(result->extra_col_value);
1723 result->extra_col_value=NULL;
1724 }
1725 if (result->col_value[MCI_COL_VALUE].allocated) {
1726 free(result->col_value[MCI_COL_VALUE].value_str);
1727 result->col_value[MCI_COL_VALUE].allocated =
1728 false;
1729 }
1730 }
1731 /*******************************************************************//**
1732 Support memcached "GET" command, fetch the value according to key
1733 @return ENGINE_SUCCESS if successfully, otherwise error code */
1734 static
1735 ENGINE_ERROR_CODE
innodb_get(ENGINE_HANDLE * handle,const void * cookie,item ** item,const void * key,const int nkey,uint16_t vbucket)1736 innodb_get(
1737 /*=======*/
1738 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
1739 const void* cookie, /*!< in: connection cookie */
1740 item** item, /*!< out: item to fill */
1741 const void* key, /*!< in: search key */
1742 const int nkey, /*!< in: key length */
1743 uint16_t vbucket __attribute__((unused)))
1744 /*!< in: bucket, used by default
1745 engine only */
1746 {
1747 struct innodb_engine* innodb_eng = innodb_handle(handle);
1748 ib_crsr_t crsr;
1749 ib_err_t err = DB_SUCCESS;
1750 mci_item_t* result = NULL;
1751 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
1752 innodb_conn_data_t* conn_data = NULL;
1753 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
1754 int option_length;
1755 const char* option_delimiter;
1756 size_t key_len = nkey;
1757 int lock_mode;
1758 bool report_table_switch = false;
1759
1760 if (meta_info->get_option == META_CACHE_OPT_DISABLE) {
1761 return(ENGINE_KEY_ENOENT);
1762 }
1763
1764 if (meta_info->get_option == META_CACHE_OPT_DEFAULT
1765 || meta_info->get_option == META_CACHE_OPT_MIX) {
1766 *item = item_get(default_handle(innodb_eng), key, nkey);
1767
1768 if (*item != NULL) {
1769 return(ENGINE_SUCCESS);
1770 }
1771
1772 if (meta_info->get_option == META_CACHE_OPT_DEFAULT) {
1773 return(ENGINE_KEY_ENOENT);
1774 }
1775 }
1776
1777 /* Check if we need to switch table mapping */
1778 err_ret = check_key_name_for_map_switch(handle, cookie, key, &key_len);
1779
1780 /* If specified new table map does not exist, or table does not
1781 qualify for InnoDB memcached, return error */
1782 if (err_ret != ENGINE_SUCCESS) {
1783 goto err_exit;
1784 }
1785
1786 /* If only the new mapping name is provided, and no key value,
1787 return here */
1788 if (key_len <= 0) {
1789 /* If this is a command in the form of "get @@new_table_map",
1790 for the purpose of switching to the specified table with
1791 the table map name, if the switch is successful, we will
1792 return the table name as result */
1793 if (nkey > 0) {
1794 report_table_switch = true;
1795
1796 goto search_done;
1797 }
1798
1799 err_ret = ENGINE_KEY_ENOENT;
1800 goto err_exit;
1801 }
1802
1803 lock_mode = (innodb_eng->trx_level == IB_TRX_SERIALIZABLE
1804 && innodb_eng->read_batch_size == 1)
1805 ? IB_LOCK_S
1806 : IB_LOCK_NONE;
1807
1808 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_READ,
1809 lock_mode, false, NULL);
1810
1811 if (!conn_data) {
1812 return(ENGINE_TMPFAIL);
1813 }
1814
1815 result = (mci_item_t*)(conn_data->result);
1816
1817 err = innodb_api_search(conn_data, &crsr, key + nkey - key_len,
1818 key_len, result, NULL, true);
1819
1820 if (err != DB_SUCCESS) {
1821 err_ret = ENGINE_KEY_ENOENT;
1822 goto func_exit;
1823 }
1824
1825 search_done:
1826 if (report_table_switch) {
1827 char table_name[MAX_TABLE_NAME_LEN
1828 + MAX_DATABASE_NAME_LEN];
1829 char* name;
1830 char* dbname;
1831
1832 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
1833 assert(nkey > 0);
1834
1835 name = conn_data->conn_meta->col_info[CONTAINER_TABLE].col_name;
1836 dbname = conn_data->conn_meta->col_info[CONTAINER_DB].col_name;
1837 #ifdef __WIN__
1838 sprintf(table_name, "%s\%s", dbname, name);
1839 #else
1840 snprintf(table_name, sizeof(table_name),
1841 "%s/%s", dbname, name);
1842 #endif
1843
1844 assert(!conn_data->result_in_use);
1845 conn_data->result_in_use = true;
1846 result = (mci_item_t*)(conn_data->result);
1847
1848 memset(result, 0, sizeof(*result));
1849
1850 memcpy(conn_data->row_buf, table_name, strlen(table_name));
1851
1852 result->col_value[MCI_COL_VALUE].value_str = conn_data->row_buf;
1853 result->col_value[MCI_COL_VALUE].value_len = strlen(table_name);
1854 result->col_value[MCI_COL_VALUE].is_str = true;
1855 result->col_value[MCI_COL_VALUE].is_valid = true;
1856 }
1857
1858 result->col_value[MCI_COL_KEY].value_str = (char*)key;
1859 result->col_value[MCI_COL_KEY].value_len = nkey;
1860
1861 /* Only if expiration field is enabled, and the value is not zero,
1862 we will check whether the item is expired */
1863 if (result->col_value[MCI_COL_EXP].is_valid
1864 && result->col_value[MCI_COL_EXP].value_int) {
1865 uint64_t time;
1866 time = mci_get_time();
1867 if (time > result->col_value[MCI_COL_EXP].value_int) {
1868 innodb_free_item(result);
1869 err_ret = ENGINE_KEY_ENOENT;
1870 goto func_exit;
1871 }
1872 }
1873
1874 if (result->extra_col_value) {
1875 int i;
1876 char* c_value;
1877 char* value_end;
1878 unsigned int total_len = 0;
1879 char int_buf[MAX_INT_CHAR_LEN];
1880
1881 GET_OPTION(meta_info, OPTION_ID_COL_SEP, option_delimiter,
1882 option_length);
1883
1884 assert(option_length > 0 && option_delimiter);
1885
1886 for (i = 0; i < result->n_extra_col; i++) {
1887 mci_column_t* mci_item = &result->extra_col_value[i];
1888
1889 if (mci_item->value_len == 0) {
1890 total_len += option_length;
1891 continue;
1892 }
1893
1894 if (!mci_item->is_str) {
1895 memset(int_buf, 0, sizeof int_buf);
1896 assert(!mci_item->value_str);
1897
1898 total_len += convert_to_char(
1899 int_buf, sizeof int_buf,
1900 &mci_item->value_int,
1901 mci_item->value_len,
1902 mci_item->is_unsigned);
1903 } else {
1904 total_len += result->extra_col_value[i].value_len;
1905 }
1906
1907 total_len += option_length;
1908 }
1909
1910 /* No need to add the last separator */
1911 total_len -= option_length;
1912
1913 if (total_len > conn_data->mul_col_buf_len) {
1914 if (conn_data->mul_col_buf) {
1915 free(conn_data->mul_col_buf);
1916 }
1917
1918 conn_data->mul_col_buf = malloc(total_len + 1);
1919 conn_data->mul_col_buf_len = total_len;
1920 }
1921
1922 c_value = conn_data->mul_col_buf;
1923 value_end = conn_data->mul_col_buf + total_len;
1924
1925 for (i = 0; i < result->n_extra_col; i++) {
1926 mci_column_t* col_value;
1927
1928 col_value = &result->extra_col_value[i];
1929
1930 if (col_value->value_len != 0) {
1931 if (!col_value->is_str) {
1932 int int_len;
1933 memset(int_buf, 0, sizeof int_buf);
1934
1935 int_len = convert_to_char(
1936 int_buf,
1937 sizeof int_buf,
1938 &col_value->value_int,
1939 col_value->value_len,
1940 col_value->is_unsigned);
1941
1942 assert(int_len <= conn_data->mul_col_buf_len);
1943
1944 memcpy(c_value, int_buf, int_len);
1945 c_value += int_len;
1946 } else {
1947 memcpy(c_value,
1948 col_value->value_str,
1949 col_value->value_len);
1950 c_value += col_value->value_len;
1951 }
1952 }
1953
1954 if (i < result->n_extra_col - 1 ) {
1955 memcpy(c_value, option_delimiter, option_length);
1956 c_value += option_length;
1957 }
1958
1959 assert(c_value <= value_end);
1960
1961 if (col_value->allocated) {
1962 free(col_value->value_str);
1963 }
1964 }
1965
1966 result->col_value[MCI_COL_VALUE].value_str = conn_data->mul_col_buf;
1967 result->col_value[MCI_COL_VALUE].value_len = total_len;
1968 result->col_value[MCI_COL_VALUE].is_str = true;
1969 result->col_value[MCI_COL_VALUE].is_valid = true;
1970 ((char*)result->col_value[MCI_COL_VALUE].value_str)[total_len] = 0;
1971
1972 free(result->extra_col_value);
1973 } else if (!result->col_value[MCI_COL_VALUE].is_str
1974 && result->col_value[MCI_COL_VALUE].value_len != 0) {
1975 unsigned int int_len;
1976 char int_buf[MAX_INT_CHAR_LEN];
1977
1978 memset(int_buf, 0, sizeof int_buf);
1979 int_len = convert_to_char(
1980 int_buf, sizeof int_buf,
1981 &result->col_value[MCI_COL_VALUE].value_int,
1982 result->col_value[MCI_COL_VALUE].value_len,
1983 result->col_value[MCI_COL_VALUE].is_unsigned);
1984
1985 if (int_len > conn_data->mul_col_buf_len) {
1986 if (conn_data->mul_col_buf) {
1987 free(conn_data->mul_col_buf);
1988 }
1989
1990 conn_data->mul_col_buf = malloc(int_len + 1);
1991 conn_data->mul_col_buf_len = int_len;
1992 }
1993
1994 memcpy(conn_data->mul_col_buf, int_buf, int_len);
1995 result->col_value[MCI_COL_VALUE].value_str =
1996 conn_data->mul_col_buf;
1997
1998 result->col_value[MCI_COL_VALUE].value_len = int_len;
1999 result->col_value[MCI_COL_VALUE].is_str = true;
2000 result->col_value[MCI_COL_VALUE].is_valid = true;
2001 }
2002
2003 *item = result;
2004
2005 func_exit:
2006
2007 if (!report_table_switch) {
2008 innodb_api_cursor_reset(innodb_eng, conn_data,
2009 CONN_OP_READ, true);
2010 }
2011
2012 err_exit:
2013
2014 /* If error return, memcached will not call InnoDB Memcached's
2015 callback function "innodb_release" to reset the result_in_use
2016 value. So we reset it here */
2017 if (err_ret != ENGINE_SUCCESS && conn_data) {
2018 conn_data->result_in_use = false;
2019 }
2020 return(err_ret);
2021 }
2022
2023 /*******************************************************************//**
2024 Get statistics info
2025 @return ENGINE_SUCCESS if successfully, otherwise error code */
2026 static
2027 ENGINE_ERROR_CODE
innodb_get_stats(ENGINE_HANDLE * handle,const void * cookie,const char * stat_key,int nkey,ADD_STAT add_stat)2028 innodb_get_stats(
2029 /*=============*/
2030 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2031 const void* cookie, /*!< in: connection cookie */
2032 const char* stat_key, /*!< in: statistics key */
2033 int nkey, /*!< in: key length */
2034 ADD_STAT add_stat) /*!< out: stats to fill */
2035 {
2036 struct innodb_engine* innodb_eng = innodb_handle(handle);
2037 struct default_engine *def_eng = default_handle(innodb_eng);
2038 return(def_eng->engine.get_stats(innodb_eng->default_engine, cookie,
2039 stat_key, nkey, add_stat));
2040 }
2041
2042 /*******************************************************************//**
2043 reset statistics
2044 @return ENGINE_SUCCESS if successfully, otherwise error code */
2045 static
2046 void
innodb_reset_stats(ENGINE_HANDLE * handle,const void * cookie)2047 innodb_reset_stats(
2048 /*===============*/
2049 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2050 const void* cookie) /*!< in: connection cookie */
2051 {
2052 struct innodb_engine* innodb_eng = innodb_handle(handle);
2053 struct default_engine *def_eng = default_handle(innodb_eng);
2054 def_eng->engine.reset_stats(innodb_eng->default_engine, cookie);
2055 }
2056
2057 /*******************************************************************//**
2058 API interface for memcached's "SET", "ADD", "REPLACE", "APPEND"
2059 "PREPENT" and "CAS" commands
2060 @return ENGINE_SUCCESS if successfully, otherwise error code */
2061 static
2062 ENGINE_ERROR_CODE
innodb_store(ENGINE_HANDLE * handle,const void * cookie,item * item,uint64_t * cas,ENGINE_STORE_OPERATION op,uint16_t vbucket)2063 innodb_store(
2064 /*=========*/
2065 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2066 const void* cookie, /*!< in: connection cookie */
2067 item* item, /*!< out: result to fill */
2068 uint64_t* cas, /*!< in: cas value */
2069 ENGINE_STORE_OPERATION op, /*!< in: type of operation */
2070 uint16_t vbucket __attribute__((unused)))
2071 /*!< in: bucket, used by default
2072 engine only */
2073 {
2074 struct innodb_engine* innodb_eng = innodb_handle(handle);
2075 uint16_t len = hash_item_get_key_len(item);
2076 char* value = hash_item_get_key(item);
2077 uint64_t exptime = hash_item_get_exp(item);
2078 uint64_t flags = hash_item_get_flag(item);
2079 ENGINE_ERROR_CODE result;
2080 uint64_t input_cas;
2081 innodb_conn_data_t* conn_data;
2082 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2083 uint32_t val_len = ((hash_item*)item)->nbytes;
2084 size_t key_len = len;
2085 ENGINE_ERROR_CODE err_ret = ENGINE_SUCCESS;
2086
2087 if (meta_info->set_option == META_CACHE_OPT_DISABLE) {
2088 return(ENGINE_SUCCESS);
2089 }
2090
2091 if (meta_info->set_option == META_CACHE_OPT_DEFAULT
2092 || meta_info->set_option == META_CACHE_OPT_MIX) {
2093 result = store_item(default_handle(innodb_eng), item, cas,
2094 op, cookie);
2095
2096 if (meta_info->set_option == META_CACHE_OPT_DEFAULT) {
2097 return(result);
2098 }
2099 }
2100
2101 err_ret = check_key_name_for_map_switch(handle, cookie,
2102 value, &key_len);
2103
2104 if (err_ret != ENGINE_SUCCESS) {
2105 return(err_ret);
2106 }
2107
2108 /* If no key is provided, return here */
2109 if (key_len <= 0) {
2110 return(ENGINE_NOT_STORED);
2111 }
2112
2113 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2114 IB_LOCK_X, false, NULL);
2115
2116 if (!conn_data) {
2117 return(ENGINE_NOT_STORED);
2118 }
2119
2120 input_cas = hash_item_get_cas(item);
2121
2122 result = innodb_api_store(innodb_eng, conn_data, value + len - key_len,
2123 key_len, val_len, exptime, cas, input_cas,
2124 flags, op);
2125
2126 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_WRITE,
2127 result == ENGINE_SUCCESS);
2128 return(result);
2129 }
2130
2131 /*******************************************************************//**
2132 Support memcached "INCR" and "DECR" command, add or subtract a "delta"
2133 value from an integer key value
2134 @return ENGINE_SUCCESS if successfully, otherwise error code */
2135 static
2136 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)2137 innodb_arithmetic(
2138 /*==============*/
2139 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2140 const void* cookie, /*!< in: connection cookie */
2141 const void* key, /*!< in: key for the value to add */
2142 const int nkey, /*!< in: key length */
2143 const bool increment, /*!< in: whether to increment
2144 or decrement */
2145 const bool create, /*!< in: whether to create the key
2146 value pair if can't find */
2147 const uint64_t delta, /*!< in: value to add/substract */
2148 const uint64_t initial, /*!< in: initial */
2149 const rel_time_t exptime, /*!< in: expiration time */
2150 uint64_t* cas, /*!< out: new cas value */
2151 uint64_t* result, /*!< out: result value */
2152 uint16_t vbucket) /*!< in: bucket, used by default
2153 engine only */
2154 {
2155 struct innodb_engine* innodb_eng = innodb_handle(handle);
2156 struct default_engine* def_eng = default_handle(innodb_eng);
2157 innodb_conn_data_t* conn_data;
2158 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2159 ENGINE_ERROR_CODE err_ret;
2160
2161 if (meta_info->set_option == META_CACHE_OPT_DISABLE) {
2162 return(ENGINE_SUCCESS);
2163 }
2164
2165 if (meta_info->set_option == META_CACHE_OPT_DEFAULT
2166 || meta_info->set_option == META_CACHE_OPT_MIX) {
2167 /* For cache-only, forward this to the
2168 default engine */
2169 err_ret = def_eng->engine.arithmetic(
2170 innodb_eng->default_engine, cookie, key, nkey,
2171 increment, create, delta, initial, exptime, cas,
2172 result, vbucket);
2173
2174 if (meta_info->set_option == META_CACHE_OPT_DEFAULT) {
2175 return(err_ret);
2176 }
2177 }
2178
2179 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2180 IB_LOCK_X, false, NULL);
2181
2182 if (!conn_data) {
2183 return(ENGINE_NOT_STORED);
2184 }
2185
2186 err_ret = innodb_api_arithmetic(innodb_eng, conn_data, key, nkey,
2187 delta, increment, cas, exptime,
2188 create, initial, result);
2189
2190 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_WRITE,
2191 true);
2192
2193 return(err_ret);
2194 }
2195
2196 /*******************************************************************//**
2197 Cleanup idle connections if "clear_all" is false, and clean up all
2198 connections if "clear_all" is true.
2199 @return number of connection cleaned */
2200 static
2201 bool
innodb_flush_sync_conn(innodb_engine_t * engine,const void * cookie,bool flush_flag)2202 innodb_flush_sync_conn(
2203 /*===================*/
2204 innodb_engine_t* engine, /*!< in/out: InnoDB memcached
2205 engine */
2206 const void* cookie, /*!< in: connection cookie */
2207 bool flush_flag) /*!< in: flush is running or not */
2208 {
2209 innodb_conn_data_t* conn_data = NULL;
2210 innodb_conn_data_t* curr_conn_data;
2211 bool ret = true;
2212
2213 curr_conn_data = engine->server.cookie->get_engine_specific(cookie);
2214 assert(curr_conn_data);
2215
2216 conn_data = UT_LIST_GET_FIRST(engine->conn_data);
2217
2218 while (conn_data) {
2219 if (conn_data != curr_conn_data && (!conn_data->is_stale)) {
2220 if (conn_data->thd) {
2221 handler_thd_attach(conn_data->thd, NULL);
2222 }
2223 LOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2224 if (flush_flag == false) {
2225 conn_data->is_flushing = flush_flag;
2226 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2227 conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
2228 continue;
2229 }
2230 if (!conn_data->in_use) {
2231 /* Set flushing flag to conn_data for preventing
2232 it is get by other request. */
2233 conn_data->is_flushing = flush_flag;
2234 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2235 } else {
2236 ret = false;
2237 UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
2238 break;
2239 }
2240 }
2241 conn_data = UT_LIST_GET_NEXT(conn_list, conn_data);
2242 }
2243
2244 if (curr_conn_data->thd) {
2245 handler_thd_attach(curr_conn_data->thd, NULL);
2246 }
2247
2248 return(ret);
2249 }
2250
2251 /*******************************************************************//**
2252 Support memcached "FLUSH_ALL" command, clean up storage (trunate InnoDB Table)
2253 @return ENGINE_SUCCESS if successfully, otherwise error code */
2254 static
2255 ENGINE_ERROR_CODE
innodb_flush(ENGINE_HANDLE * handle,const void * cookie,time_t when)2256 innodb_flush(
2257 /*=========*/
2258 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2259 const void* cookie, /*!< in: connection cookie */
2260 time_t when) /*!< in: when to flush, not used by
2261 InnoDB */
2262 {
2263 struct innodb_engine* innodb_eng = innodb_handle(handle);
2264 struct default_engine* def_eng = default_handle(innodb_eng);
2265 ENGINE_ERROR_CODE err = ENGINE_SUCCESS;
2266 meta_cfg_info_t* meta_info = innodb_eng->meta_info;
2267 ib_err_t ib_err = DB_SUCCESS;
2268 innodb_conn_data_t* conn_data;
2269
2270 if (meta_info->flush_option == META_CACHE_OPT_DISABLE) {
2271 return(ENGINE_SUCCESS);
2272 }
2273
2274 if (meta_info->flush_option == META_CACHE_OPT_DEFAULT
2275 || meta_info->flush_option == META_CACHE_OPT_MIX) {
2276 /* default engine flush */
2277 err = def_eng->engine.flush(innodb_eng->default_engine,
2278 cookie, when);
2279
2280 if (meta_info->flush_option == META_CACHE_OPT_DEFAULT) {
2281 return(err);
2282 }
2283 }
2284
2285 /* Lock the whole engine, so no other connection can start
2286 new opeartion */
2287 pthread_mutex_lock(&innodb_eng->conn_mutex);
2288
2289 /* Lock the flush_mutex for blocking other DMLs. */
2290 pthread_mutex_lock(&innodb_eng->flush_mutex);
2291
2292 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
2293
2294 if (conn_data) {
2295 /* Commit any work on this connection */
2296 innodb_api_cursor_reset(innodb_eng, conn_data,
2297 CONN_OP_FLUSH, true);
2298 }
2299
2300 conn_data = innodb_conn_init(innodb_eng, cookie, CONN_MODE_WRITE,
2301 IB_LOCK_TABLE_X, true, NULL);
2302
2303 if (!conn_data) {
2304 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2305 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2306 return(ENGINE_SUCCESS);
2307 }
2308
2309 /* Commit any previous work on this connection */
2310 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_FLUSH, true);
2311
2312 if (!innodb_flush_sync_conn(innodb_eng, cookie, true)) {
2313 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2314 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2315 innodb_flush_sync_conn(innodb_eng, cookie, false);
2316 return(ENGINE_TMPFAIL);
2317 }
2318
2319 ib_err = innodb_api_flush(innodb_eng, conn_data,
2320 meta_info->col_info[CONTAINER_DB].col_name,
2321 meta_info->col_info[CONTAINER_TABLE].col_name);
2322
2323 /* Commit work and release the MDL table. */
2324 innodb_api_cursor_reset(innodb_eng, conn_data, CONN_OP_FLUSH, true);
2325 innodb_conn_clean_data(conn_data, false, false);
2326
2327 pthread_mutex_unlock(&innodb_eng->flush_mutex);
2328 pthread_mutex_unlock(&innodb_eng->conn_mutex);
2329
2330 innodb_flush_sync_conn(innodb_eng, cookie, false);
2331
2332 return((ib_err == DB_SUCCESS) ? ENGINE_SUCCESS : ENGINE_TMPFAIL);
2333 }
2334
2335 /*******************************************************************//**
2336 Deal with unknown command. Currently not used
2337 @return ENGINE_SUCCESS if successfully processed, otherwise error code */
2338 static
2339 ENGINE_ERROR_CODE
innodb_unknown_command(ENGINE_HANDLE * handle,const void * cookie,protocol_binary_request_header * request,ADD_RESPONSE response)2340 innodb_unknown_command(
2341 /*===================*/
2342 ENGINE_HANDLE* handle, /*!< in: Engine Handle */
2343 const void* cookie, /*!< in: connection cookie */
2344 protocol_binary_request_header *request, /*!< in: request */
2345 ADD_RESPONSE response) /*!< out: respondse */
2346 {
2347 struct innodb_engine* innodb_eng = innodb_handle(handle);
2348 struct default_engine *def_eng = default_handle(innodb_eng);
2349
2350 return(def_eng->engine.unknown_command(innodb_eng->default_engine,
2351 cookie, request, response));
2352 }
2353
2354 /*******************************************************************//**
2355 Callback functions used by Memcached's process_command() function
2356 to get the result key/value information
2357 @return true if info fetched */
2358 static
2359 bool
innodb_get_item_info(ENGINE_HANDLE * handle,const void * cookie,const item * item,item_info * item_info)2360 innodb_get_item_info(
2361 /*=================*/
2362 ENGINE_HANDLE* handle __attribute__((unused)),
2363 /*!< in: Engine Handle */
2364 const void* cookie __attribute__((unused)),
2365 /*!< in: connection cookie */
2366 const item* item, /*!< in: item in question */
2367 item_info* item_info) /*!< out: item info got */
2368 {
2369 struct innodb_engine* innodb_eng = innodb_handle(handle);
2370 innodb_conn_data_t* conn_data;
2371
2372 conn_data = innodb_eng->server.cookie->get_engine_specific(cookie);
2373
2374 if (!conn_data || !conn_data->result_in_use) {
2375 hash_item* it;
2376
2377 if (item_info->nvalue < 1) {
2378 return(false);
2379 }
2380
2381 /* Use a hash item */
2382 it = (hash_item*) item;
2383 item_info->cas = hash_item_get_cas(it);
2384 item_info->exptime = it->exptime;
2385 item_info->nbytes = it->nbytes;
2386 item_info->flags = it->flags;
2387 item_info->clsid = it->slabs_clsid;
2388 item_info->nkey = it->nkey;
2389 item_info->nvalue = 1;
2390 item_info->key = hash_item_get_key(it);
2391 item_info->value[0].iov_base = hash_item_get_data(it);
2392 item_info->value[0].iov_len = it->nbytes;
2393 } else {
2394 mci_item_t* it;
2395
2396 if (item_info->nvalue < 1) {
2397 return(false);
2398 }
2399
2400 /* Use a hash item */
2401 it = (mci_item_t*) item;
2402 if (it->col_value[MCI_COL_CAS].is_valid) {
2403 item_info->cas = it->col_value[MCI_COL_CAS].value_int;
2404 } else {
2405 item_info->cas = 0;
2406 }
2407
2408 if (it->col_value[MCI_COL_EXP].is_valid) {
2409 item_info->exptime = it->col_value[MCI_COL_EXP].value_int;
2410 } else {
2411 item_info->exptime = 0;
2412 }
2413
2414 item_info->nbytes = it->col_value[MCI_COL_VALUE].value_len;
2415
2416 if (it->col_value[MCI_COL_FLAG].is_valid) {
2417 item_info->flags = ntohl(
2418 it->col_value[MCI_COL_FLAG].value_int);
2419 } else {
2420 item_info->flags = 0;
2421 }
2422
2423 item_info->clsid = 1;
2424
2425 item_info->nkey = it->col_value[MCI_COL_KEY].value_len;
2426
2427 item_info->nvalue = 1;
2428
2429 item_info->key = it->col_value[MCI_COL_KEY].value_str;
2430
2431 item_info->value[0].iov_base = it->col_value[
2432 MCI_COL_VALUE].value_str;;
2433
2434 item_info->value[0].iov_len = it->col_value[
2435 MCI_COL_VALUE].value_len;
2436
2437 }
2438
2439 return(true);
2440 }
2441