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