1/*****************************************************************************
2
3Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2008, 2009 Google Inc.
5Copyright (c) 2009, Percona Inc.
6Copyright (c) 2012, Facebook Inc.
7Copyright (c) 2013, 2018, MariaDB Corporation.
8
9Portions of this file contain modifications contributed and copyrighted by
10Google, Inc. Those modifications are gratefully acknowledged and are described
11briefly in the InnoDB documentation. The contributions by Google are
12incorporated with their permission, and subject to the conditions contained in
13the file COPYING.Google.
14
15Portions of this file contain modifications contributed and copyrighted
16by Percona Inc.. Those modifications are
17gratefully acknowledged and are described briefly in the InnoDB
18documentation. The contributions by Percona Inc. are incorporated with
19their permission, and subject to the conditions contained in the file
20COPYING.Percona.
21
22This program is free software; you can redistribute it and/or modify it under
23the terms of the GNU General Public License as published by the Free Software
24Foundation; version 2 of the License.
25
26This program is distributed in the hope that it will be useful, but WITHOUT
27ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
28FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
29
30You should have received a copy of the GNU General Public License along with
31this program; if not, write to the Free Software Foundation, Inc.,
3251 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
33
34*****************************************************************************/
35
36/** @file ha_innodb.cc */
37
38#include "univ.i"
39
40/* Include necessary SQL headers */
41#include "ha_prototypes.h"
42#include <debug_sync.h>
43#include <gstream.h>
44#include <log.h>
45#include <mysys_err.h>
46#include <innodb_priv.h>
47#include <strfunc.h>
48#include <sql_acl.h>
49#include <sql_class.h>
50#include <sql_show.h>
51#include <sql_table.h>
52#include <table_cache.h>
53#include <my_check_opt.h>
54#include <my_bitmap.h>
55#include <mysql/service_thd_alloc.h>
56#include <mysql/service_thd_wait.h>
57
58// MYSQL_PLUGIN_IMPORT extern my_bool lower_case_file_system;
59// MYSQL_PLUGIN_IMPORT extern char mysql_unpacked_real_data_home[];
60
61#include <my_service_manager.h>
62#include <key.h>
63
64/* Include necessary InnoDB headers */
65#include "btr0btr.h"
66#include "btr0cur.h"
67#include "btr0bulk.h"
68#include "btr0sea.h"
69#include "buf0dblwr.h"
70#include "buf0dump.h"
71#include "buf0flu.h"
72#include "buf0lru.h"
73#include "dict0boot.h"
74#include "btr0defragment.h"
75#include "dict0crea.h"
76#include "dict0dict.h"
77#include "dict0stats.h"
78#include "dict0stats_bg.h"
79#include "fil0fil.h"
80#include "fsp0fsp.h"
81#include "fsp0space.h"
82#include "fsp0sysspace.h"
83#include "fts0fts.h"
84#include "fts0plugin.h"
85#include "fts0priv.h"
86#include "fts0types.h"
87#include "ibuf0ibuf.h"
88#include "lock0lock.h"
89#include "log0crypt.h"
90#include "mem0mem.h"
91#include "mtr0mtr.h"
92#include "os0file.h"
93#include "os0thread.h"
94#include "page0zip.h"
95#include "pars0pars.h"
96#include "rem0types.h"
97#include "row0import.h"
98#include "row0ins.h"
99#include "row0merge.h"
100#include "row0mysql.h"
101#include "row0quiesce.h"
102#include "row0sel.h"
103#include "row0trunc.h"
104#include "row0upd.h"
105#include "fil0crypt.h"
106#include "ut0timer.h"
107#include "srv0mon.h"
108#include "srv0srv.h"
109#include "srv0start.h"
110#ifdef UNIV_DEBUG
111#include "trx0purge.h"
112#endif /* UNIV_DEBUG */
113#include "trx0roll.h"
114#include "trx0rseg.h"
115#include "trx0trx.h"
116#include "fil0pagecompress.h"
117#include "ut0mem.h"
118#include "row0ext.h"
119
120#define thd_get_trx_isolation(X) ((enum_tx_isolation)thd_tx_isolation(X))
121
122extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all);
123unsigned long long thd_get_query_id(const MYSQL_THD thd);
124TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len,
125 const char *table, size_t table_len);
126MYSQL_THD create_thd();
127void destroy_thd(MYSQL_THD thd);
128void reset_thd(MYSQL_THD thd);
129TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
130 const char *tb, size_t tblen);
131TABLE *get_purge_table(THD *thd);
132
133#ifdef MYSQL_DYNAMIC_PLUGIN
134#define tc_size 400
135#define tdc_size 400
136#endif
137
138#include "ha_innodb.h"
139#include "i_s.h"
140#include "sync0sync.h"
141
142#include <string>
143#include <sstream>
144
145#include <mysql/plugin.h>
146#include <mysql/service_wsrep.h>
147
148#ifdef WITH_WSREP
149#include "dict0priv.h"
150#include "ut0byte.h"
151#include <mysql/service_md5.h>
152
153extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
154
155static inline wsrep_ws_handle_t*
156wsrep_ws_handle(THD* thd, const trx_t* trx) {
157 return wsrep_ws_handle_for_trx(wsrep_thd_ws_handle(thd),
158 (wsrep_trx_id_t)trx->id);
159}
160
161extern TC_LOG* tc_log;
162extern void wsrep_cleanup_transaction(THD *thd);
163static int
164wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
165 my_bool signal);
166static void
167wsrep_fake_trx_id(handlerton* hton, THD *thd);
168static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid);
169static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid);
170#endif /* WITH_WSREP */
171
172/** to protect innobase_open_files */
173static mysql_mutex_t innobase_share_mutex;
174/** to force correct commit order in binlog */
175static ulong commit_threads = 0;
176static mysql_cond_t commit_cond;
177static mysql_mutex_t commit_cond_m;
178static mysql_mutex_t pending_checkpoint_mutex;
179
180#define INSIDE_HA_INNOBASE_CC
181
182#define EQ_CURRENT_THD(thd) ((thd) == current_thd)
183
184struct handlerton* innodb_hton_ptr;
185
186static const long AUTOINC_OLD_STYLE_LOCKING = 0;
187static const long AUTOINC_NEW_STYLE_LOCKING = 1;
188static const long AUTOINC_NO_LOCKING = 2;
189
190static ulong innobase_open_files;
191static long innobase_autoinc_lock_mode;
192static ulong innobase_commit_concurrency;
193
194static ulonglong innobase_buffer_pool_size;
195
196/** Percentage of the buffer pool to reserve for 'old' blocks.
197Connected to buf_LRU_old_ratio. */
198static uint innobase_old_blocks_pct;
199
200static char* innobase_data_file_path;
201static char* innobase_temp_data_file_path;
202
203/* The default values for the following char* start-up parameters
204are determined in innodb_init_params(). */
205
206static char* innobase_data_home_dir;
207static char* innobase_enable_monitor_counter;
208static char* innobase_disable_monitor_counter;
209static char* innobase_reset_monitor_counter;
210static char* innobase_reset_all_monitor_counter;
211
212static ulong innodb_change_buffering;
213static ulong innodb_flush_method;
214
215/* This variable can be set in the server configure file, specifying
216stopword table to be used */
217static char* innobase_server_stopword_table;
218
219/* Below we have boolean-valued start-up parameters, and their default
220values */
221
222static my_bool innobase_use_atomic_writes;
223static my_bool innobase_use_checksums;
224static my_bool innobase_locks_unsafe_for_binlog;
225static my_bool innobase_rollback_on_timeout;
226static my_bool innobase_create_status_file;
227my_bool innobase_stats_on_metadata;
228static my_bool innodb_optimize_fulltext_only;
229
230static char* innodb_version_str = (char*) INNODB_VERSION_STR;
231
232extern uint srv_fil_crypt_rotate_key_age;
233extern uint srv_n_fil_crypt_iops;
234
235extern my_bool srv_immediate_scrub_data_uncompressed;
236extern my_bool srv_background_scrub_data_uncompressed;
237extern my_bool srv_background_scrub_data_compressed;
238extern uint srv_background_scrub_data_interval;
239extern uint srv_background_scrub_data_check_interval;
240#ifdef UNIV_DEBUG
241extern my_bool srv_scrub_force_testing;
242#endif
243
244/** Note we cannot use rec_format_enum because we do not allow
245COMPRESSED row format for innodb_default_row_format option. */
246enum default_row_format_enum {
247 DEFAULT_ROW_FORMAT_REDUNDANT = 0,
248 DEFAULT_ROW_FORMAT_COMPACT = 1,
249 DEFAULT_ROW_FORMAT_DYNAMIC = 2,
250};
251
252static
253void set_my_errno(int err)
254{
255 errno = err;
256}
257
258static uint mysql_fields(const TABLE *table)
259{
260 return table->s->frm_version < FRM_VER_EXPRESSSIONS
261 ? table->s->stored_fields : table->s->fields;
262}
263
264/** Checks whether the file name belongs to a partition of a table.
265@param[in] file_name file name
266@return pointer to the end of the table name part of the file name, or NULL */
267static
268char*
269is_partition(
270/*=========*/
271 char* file_name)
272{
273 /* We look for pattern #P# to see if the table is partitioned
274 MariaDB table. */
275 return strstr(file_name, table_name_t::part_suffix);
276}
277
278/** Signal to shut down InnoDB (NULL if shutdown was signaled, or if
279running in innodb_read_only mode, srv_read_only_mode) */
280st_my_thread_var *srv_running;
281/** Service thread that waits for the server shutdown and stops purge threads.
282Purge workers have THDs that are needed to calculate virtual columns.
283This THDs must be destroyed rather early in the server shutdown sequence.
284This service thread creates a THD and idly waits for it to get a signal to
285die. Then it notifies all purge workers to shutdown.
286*/
287static pthread_t thd_destructor_thread;
288
289pthread_handler_t
290thd_destructor_proxy(void *)
291{
292 mysql_mutex_t thd_destructor_mutex;
293 mysql_cond_t thd_destructor_cond;
294
295 my_thread_init();
296 mysql_mutex_init(PSI_NOT_INSTRUMENTED, &thd_destructor_mutex, 0);
297 mysql_cond_init(PSI_NOT_INSTRUMENTED, &thd_destructor_cond, 0);
298
299 st_my_thread_var *myvar= _my_thread_var();
300 THD *thd= create_thd();
301 thd_proc_info(thd, "InnoDB shutdown handler");
302
303 myvar->current_mutex = &thd_destructor_mutex;
304 myvar->current_cond = &thd_destructor_cond;
305
306 mysql_mutex_lock(&thd_destructor_mutex);
307 my_atomic_storeptr_explicit(reinterpret_cast<void**>(&srv_running),
308 myvar,
309 MY_MEMORY_ORDER_RELAXED);
310 /* wait until the server wakes the THD to abort and die */
311 while (!srv_running->abort)
312 mysql_cond_wait(&thd_destructor_cond, &thd_destructor_mutex);
313 mysql_mutex_unlock(&thd_destructor_mutex);
314 my_atomic_storeptr_explicit(reinterpret_cast<void**>(&srv_running),
315 NULL,
316 MY_MEMORY_ORDER_RELAXED);
317
318 while (srv_fast_shutdown == 0 &&
319 (trx_sys.any_active_transactions() ||
320 (uint)thread_count > srv_n_purge_threads + 1)) {
321 thd_proc_info(thd, "InnoDB slow shutdown wait");
322 os_thread_sleep(1000);
323 }
324
325 /* Some background threads might generate undo pages that will
326 need to be purged, so they have to be shut down before purge
327 threads if slow shutdown is requested. */
328 srv_shutdown_bg_undo_sources();
329 srv_purge_wakeup();
330
331 destroy_thd(thd);
332 mysql_cond_destroy(&thd_destructor_cond);
333 mysql_mutex_destroy(&thd_destructor_mutex);
334 my_thread_end();
335 return 0;
336}
337
338/** Return the InnoDB ROW_FORMAT enum value
339@param[in] row_format row_format from "innodb_default_row_format"
340@return InnoDB ROW_FORMAT value from rec_format_t enum. */
341static
342rec_format_t
343get_row_format(
344 ulong row_format)
345{
346 switch(row_format) {
347 case DEFAULT_ROW_FORMAT_REDUNDANT:
348 return(REC_FORMAT_REDUNDANT);
349 case DEFAULT_ROW_FORMAT_COMPACT:
350 return(REC_FORMAT_COMPACT);
351 case DEFAULT_ROW_FORMAT_DYNAMIC:
352 return(REC_FORMAT_DYNAMIC);
353 default:
354 ut_ad(0);
355 return(REC_FORMAT_DYNAMIC);
356 }
357}
358
359static ulong innodb_default_row_format = DEFAULT_ROW_FORMAT_DYNAMIC;
360
361/** Possible values for system variable "innodb_stats_method". The values
362are defined the same as its corresponding MyISAM system variable
363"myisam_stats_method"(see "myisam_stats_method_names"), for better usability */
364static const char* innodb_stats_method_names[] = {
365 "nulls_equal",
366 "nulls_unequal",
367 "nulls_ignored",
368 NullS
369};
370
371/** Used to define an enumerate type of the system variable innodb_stats_method.
372This is the same as "myisam_stats_method_typelib" */
373static TYPELIB innodb_stats_method_typelib = {
374 array_elements(innodb_stats_method_names) - 1,
375 "innodb_stats_method_typelib",
376 innodb_stats_method_names,
377 NULL
378};
379
380/** Possible values of the parameter innodb_checksum_algorithm */
381const char* innodb_checksum_algorithm_names[] = {
382 "crc32",
383 "strict_crc32",
384 "innodb",
385 "strict_innodb",
386 "none",
387 "strict_none",
388 NullS
389};
390
391/** Used to define an enumerate type of the system variable
392innodb_checksum_algorithm. */
393TYPELIB innodb_checksum_algorithm_typelib = {
394 array_elements(innodb_checksum_algorithm_names) - 1,
395 "innodb_checksum_algorithm_typelib",
396 innodb_checksum_algorithm_names,
397 NULL
398};
399
400/** Possible values for system variable "innodb_default_row_format". */
401static const char* innodb_default_row_format_names[] = {
402 "redundant",
403 "compact",
404 "dynamic",
405 NullS
406};
407
408/** Used to define an enumerate type of the system variable
409innodb_default_row_format. */
410static TYPELIB innodb_default_row_format_typelib = {
411 array_elements(innodb_default_row_format_names) - 1,
412 "innodb_default_row_format_typelib",
413 innodb_default_row_format_names,
414 NULL
415};
416
417/** Possible values of the parameter innodb_lock_schedule_algorithm */
418static const char* innodb_lock_schedule_algorithm_names[] = {
419 "fcfs",
420 "vats",
421 NullS
422};
423
424/** Used to define an enumerate type of the system variable
425innodb_lock_schedule_algorithm. */
426static TYPELIB innodb_lock_schedule_algorithm_typelib = {
427 array_elements(innodb_lock_schedule_algorithm_names) - 1,
428 "innodb_lock_schedule_algorithm_typelib",
429 innodb_lock_schedule_algorithm_names,
430 NULL
431};
432
433/** Names of allowed values of innodb_flush_method */
434const char* innodb_flush_method_names[] = {
435 "fsync",
436 "O_DSYNC",
437 "littlesync",
438 "nosync",
439 "O_DIRECT",
440 "O_DIRECT_NO_FSYNC",
441#ifdef _WIN32
442 "unbuffered",
443 "async_unbuffered" /* alias for "unbuffered" */,
444 "normal" /* alias for "fsync" */,
445#endif
446 NullS
447};
448
449/** Enumeration of innodb_flush_method */
450TYPELIB innodb_flush_method_typelib = {
451 array_elements(innodb_flush_method_names) - 1,
452 "innodb_flush_method_typelib",
453 innodb_flush_method_names,
454 NULL
455};
456
457/* The following counter is used to convey information to InnoDB
458about server activity: in case of normal DML ops it is not
459sensible to call srv_active_wake_master_thread after each
460operation, we only do it every INNOBASE_WAKE_INTERVAL'th step. */
461
462#define INNOBASE_WAKE_INTERVAL 32
463static ulong innobase_active_counter = 0;
464
465static hash_table_t* innobase_open_tables;
466
467/** Allowed values of innodb_change_buffering */
468static const char* innodb_change_buffering_names[] = {
469 "none", /* IBUF_USE_NONE */
470 "inserts", /* IBUF_USE_INSERT */
471 "deletes", /* IBUF_USE_DELETE_MARK */
472 "changes", /* IBUF_USE_INSERT_DELETE_MARK */
473 "purges", /* IBUF_USE_DELETE */
474 "all", /* IBUF_USE_ALL */
475 NullS
476};
477
478/** Enumeration of innodb_change_buffering */
479static TYPELIB innodb_change_buffering_typelib = {
480 array_elements(innodb_change_buffering_names) - 1,
481 "innodb_change_buffering_typelib",
482 innodb_change_buffering_names,
483 NULL
484};
485
486/** Retrieve the FTS Relevance Ranking result for doc with doc_id
487of m_prebuilt->fts_doc_id
488@param[in,out] fts_hdl FTS handler
489@return the relevance ranking value */
490static
491float
492innobase_fts_retrieve_ranking(
493 FT_INFO* fts_hdl);
494/** Free the memory for the FTS handler
495@param[in,out] fts_hdl FTS handler */
496static
497void
498innobase_fts_close_ranking(
499 FT_INFO* fts_hdl);
500/** Find and Retrieve the FTS Relevance Ranking result for doc with doc_id
501of m_prebuilt->fts_doc_id
502@param[in,out] fts_hdl FTS handler
503@return the relevance ranking value */
504static
505float
506innobase_fts_find_ranking(
507 FT_INFO* fts_hdl,
508 uchar*,
509 uint);
510
511/* Call back function array defined by MySQL and used to
512retrieve FTS results. */
513const struct _ft_vft ft_vft_result = {NULL,
514 innobase_fts_find_ranking,
515 innobase_fts_close_ranking,
516 innobase_fts_retrieve_ranking,
517 NULL};
518
519/** @return version of the extended FTS API */
520static
521uint
522innobase_fts_get_version()
523{
524 /* Currently this doesn't make much sense as returning
525 HA_CAN_FULLTEXT_EXT automatically mean this version is supported.
526 This supposed to ease future extensions. */
527 return(2);
528}
529
530/** @return Which part of the extended FTS API is supported */
531static
532ulonglong
533innobase_fts_flags()
534{
535 return(FTS_ORDERED_RESULT | FTS_DOCID_IN_RESULT);
536}
537
538/** Find and Retrieve the FTS doc_id for the current result row
539@param[in,out] fts_hdl FTS handler
540@return the document ID */
541static
542ulonglong
543innobase_fts_retrieve_docid(
544 FT_INFO_EXT* fts_hdl);
545
546/** Find and retrieve the size of the current result
547@param[in,out] fts_hdl FTS handler
548@return number of matching rows */
549static
550ulonglong
551innobase_fts_count_matches(
552 FT_INFO_EXT* fts_hdl) /*!< in: FTS handler */
553{
554 NEW_FT_INFO* handle = reinterpret_cast<NEW_FT_INFO*>(fts_hdl);
555
556 if (handle->ft_result->rankings_by_id != NULL) {
557 return(rbt_size(handle->ft_result->rankings_by_id));
558 } else {
559 return(0);
560 }
561}
562
563const struct _ft_vft_ext ft_vft_ext_result = {innobase_fts_get_version,
564 innobase_fts_flags,
565 innobase_fts_retrieve_docid,
566 innobase_fts_count_matches};
567
568#ifdef HAVE_PSI_INTERFACE
569# define PSI_KEY(n) {&n##_key, #n, 0}
570/* All RWLOCK used in Innodb are SX-locks */
571# define PSI_RWLOCK_KEY(n) {&n##_key, #n, PSI_RWLOCK_FLAG_SX}
572
573/* Keys to register pthread mutexes/cond in the current file with
574performance schema */
575static mysql_pfs_key_t innobase_share_mutex_key;
576static mysql_pfs_key_t commit_cond_mutex_key;
577static mysql_pfs_key_t commit_cond_key;
578static mysql_pfs_key_t pending_checkpoint_mutex_key;
579static mysql_pfs_key_t thd_destructor_thread_key;
580
581static PSI_mutex_info all_pthread_mutexes[] = {
582 PSI_KEY(commit_cond_mutex),
583 PSI_KEY(pending_checkpoint_mutex),
584 PSI_KEY(innobase_share_mutex)
585};
586
587static PSI_cond_info all_innodb_conds[] = {
588 PSI_KEY(commit_cond)
589};
590
591# ifdef UNIV_PFS_MUTEX
592/* all_innodb_mutexes array contains mutexes that are
593performance schema instrumented if "UNIV_PFS_MUTEX"
594is defined */
595static PSI_mutex_info all_innodb_mutexes[] = {
596 PSI_KEY(autoinc_mutex),
597# ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
598 PSI_KEY(buffer_block_mutex),
599# endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
600 PSI_KEY(buf_pool_mutex),
601 PSI_KEY(buf_pool_zip_mutex),
602 PSI_KEY(cache_last_read_mutex),
603 PSI_KEY(dict_foreign_err_mutex),
604 PSI_KEY(dict_sys_mutex),
605 PSI_KEY(recalc_pool_mutex),
606 PSI_KEY(fil_system_mutex),
607 PSI_KEY(flush_list_mutex),
608 PSI_KEY(fts_bg_threads_mutex),
609 PSI_KEY(fts_delete_mutex),
610 PSI_KEY(fts_optimize_mutex),
611 PSI_KEY(fts_doc_id_mutex),
612 PSI_KEY(log_flush_order_mutex),
613 PSI_KEY(hash_table_mutex),
614 PSI_KEY(ibuf_bitmap_mutex),
615 PSI_KEY(ibuf_mutex),
616 PSI_KEY(ibuf_pessimistic_insert_mutex),
617 PSI_KEY(log_sys_mutex),
618 PSI_KEY(log_sys_write_mutex),
619 PSI_KEY(mutex_list_mutex),
620 PSI_KEY(page_zip_stat_per_index_mutex),
621 PSI_KEY(purge_sys_pq_mutex),
622 PSI_KEY(recv_sys_mutex),
623 PSI_KEY(recv_writer_mutex),
624 PSI_KEY(redo_rseg_mutex),
625 PSI_KEY(noredo_rseg_mutex),
626# ifdef UNIV_DEBUG
627 PSI_KEY(rw_lock_debug_mutex),
628# endif /* UNIV_DEBUG */
629 PSI_KEY(rw_lock_list_mutex),
630 PSI_KEY(rw_lock_mutex),
631 PSI_KEY(srv_innodb_monitor_mutex),
632 PSI_KEY(srv_misc_tmpfile_mutex),
633 PSI_KEY(srv_monitor_file_mutex),
634 PSI_KEY(buf_dblwr_mutex),
635 PSI_KEY(trx_pool_mutex),
636 PSI_KEY(trx_pool_manager_mutex),
637 PSI_KEY(srv_sys_mutex),
638 PSI_KEY(lock_mutex),
639 PSI_KEY(lock_wait_mutex),
640 PSI_KEY(trx_mutex),
641 PSI_KEY(srv_threads_mutex),
642# ifndef PFS_SKIP_EVENT_MUTEX
643 PSI_KEY(event_mutex),
644# endif /* PFS_SKIP_EVENT_MUTEX */
645 PSI_KEY(rtr_active_mutex),
646 PSI_KEY(rtr_match_mutex),
647 PSI_KEY(rtr_path_mutex),
648 PSI_KEY(rtr_ssn_mutex),
649 PSI_KEY(trx_sys_mutex),
650 PSI_KEY(zip_pad_mutex)
651};
652# endif /* UNIV_PFS_MUTEX */
653
654# ifdef UNIV_PFS_RWLOCK
655/* all_innodb_rwlocks array contains rwlocks that are
656performance schema instrumented if "UNIV_PFS_RWLOCK"
657is defined */
658static PSI_rwlock_info all_innodb_rwlocks[] = {
659 PSI_RWLOCK_KEY(btr_search_latch),
660# ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
661 PSI_RWLOCK_KEY(buf_block_lock),
662# endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
663# ifdef UNIV_DEBUG
664 PSI_RWLOCK_KEY(buf_block_debug_latch),
665# endif /* UNIV_DEBUG */
666 PSI_RWLOCK_KEY(dict_operation_lock),
667 PSI_RWLOCK_KEY(fil_space_latch),
668 PSI_RWLOCK_KEY(checkpoint_lock),
669 PSI_RWLOCK_KEY(fts_cache_rw_lock),
670 PSI_RWLOCK_KEY(fts_cache_init_rw_lock),
671 PSI_RWLOCK_KEY(trx_i_s_cache_lock),
672 PSI_RWLOCK_KEY(trx_purge_latch),
673 PSI_RWLOCK_KEY(index_tree_rw_lock),
674 PSI_RWLOCK_KEY(index_online_log),
675 PSI_RWLOCK_KEY(dict_table_stats),
676 PSI_RWLOCK_KEY(hash_table_locks)
677};
678# endif /* UNIV_PFS_RWLOCK */
679
680# ifdef UNIV_PFS_THREAD
681/* all_innodb_threads array contains threads that are
682performance schema instrumented if "UNIV_PFS_THREAD"
683is defined */
684static PSI_thread_info all_innodb_threads[] = {
685 PSI_KEY(buf_dump_thread),
686 PSI_KEY(dict_stats_thread),
687 PSI_KEY(io_handler_thread),
688 PSI_KEY(io_ibuf_thread),
689 PSI_KEY(io_log_thread),
690 PSI_KEY(io_read_thread),
691 PSI_KEY(io_write_thread),
692 PSI_KEY(page_cleaner_thread),
693 PSI_KEY(recv_writer_thread),
694 PSI_KEY(srv_error_monitor_thread),
695 PSI_KEY(srv_lock_timeout_thread),
696 PSI_KEY(srv_master_thread),
697 PSI_KEY(srv_monitor_thread),
698 PSI_KEY(srv_purge_thread),
699 PSI_KEY(srv_worker_thread),
700 PSI_KEY(trx_rollback_clean_thread),
701 PSI_KEY(thd_destructor_thread),
702};
703# endif /* UNIV_PFS_THREAD */
704
705# ifdef UNIV_PFS_IO
706/* all_innodb_files array contains the type of files that are
707performance schema instrumented if "UNIV_PFS_IO" is defined */
708static PSI_file_info all_innodb_files[] = {
709 PSI_KEY(innodb_data_file),
710 PSI_KEY(innodb_log_file),
711 PSI_KEY(innodb_temp_file)
712};
713# endif /* UNIV_PFS_IO */
714#endif /* HAVE_PSI_INTERFACE */
715
716static void innodb_remember_check_sysvar_funcs();
717mysql_var_check_func check_sysvar_enum;
718mysql_var_check_func check_sysvar_int;
719
720// should page compression be used by default for new tables
721static MYSQL_THDVAR_BOOL(compression_default, PLUGIN_VAR_OPCMDARG,
722 "Is compression the default for new tables",
723 NULL, NULL, FALSE);
724
725static MYSQL_THDVAR_UINT(default_encryption_key_id, PLUGIN_VAR_RQCMDARG,
726 "Default encryption key id used for table encryption.",
727 NULL, NULL,
728 FIL_DEFAULT_ENCRYPTION_KEY, 1, UINT_MAX32, 0);
729
730/**
731 Structure for CREATE TABLE options (table options).
732 It needs to be called ha_table_option_struct.
733
734 The option values can be specified in the CREATE TABLE at the end:
735 CREATE TABLE ( ... ) *here*
736*/
737
738ha_create_table_option innodb_table_option_list[]=
739{
740 /* With this option user can enable page compression feature for the
741 table */
742 HA_TOPTION_SYSVAR("PAGE_COMPRESSED", page_compressed, compression_default),
743 /* With this option user can set zip compression level for page
744 compression for this table*/
745 HA_TOPTION_NUMBER("PAGE_COMPRESSION_LEVEL", page_compression_level, 0, 1, 9, 1),
746 /* With this option the user can enable encryption for the table */
747 HA_TOPTION_ENUM("ENCRYPTED", encryption, "DEFAULT,YES,NO", 0),
748 /* With this option the user defines the key identifier using for the encryption */
749 HA_TOPTION_SYSVAR("ENCRYPTION_KEY_ID", encryption_key_id, default_encryption_key_id),
750
751 HA_TOPTION_END
752};
753
754/*************************************************************//**
755Check whether valid argument given to innodb_ft_*_stopword_table.
756This function is registered as a callback with MySQL.
757@return 0 for valid stopword table */
758static
759int
760innodb_stopword_table_validate(
761/*===========================*/
762 THD* thd, /*!< in: thread handle */
763 struct st_mysql_sys_var* var, /*!< in: pointer to system
764 variable */
765 void* save, /*!< out: immediate result
766 for update function */
767 struct st_mysql_value* value); /*!< in: incoming string */
768
769static bool is_mysql_datadir_path(const char *path);
770
771/** Validate passed-in "value" is a valid directory name.
772This function is registered as a callback with MySQL.
773@param[in,out] thd thread handle
774@param[in] var pointer to system variable
775@param[out] save immediate result for update
776@param[in] value incoming string
777@return 0 for valid name */
778static
779int
780innodb_tmpdir_validate(
781 THD* thd,
782 struct st_mysql_sys_var*,
783 void* save,
784 struct st_mysql_value* value)
785{
786
787 char* alter_tmp_dir;
788 char* innodb_tmp_dir;
789 char buff[OS_FILE_MAX_PATH];
790 int len = sizeof(buff);
791 char tmp_abs_path[FN_REFLEN + 2];
792
793 ut_ad(save != NULL);
794 ut_ad(value != NULL);
795
796 if (check_global_access(thd, FILE_ACL)) {
797 push_warning_printf(
798 thd, Sql_condition::WARN_LEVEL_WARN,
799 ER_WRONG_ARGUMENTS,
800 "InnoDB: FILE Permissions required");
801 *static_cast<const char**>(save) = NULL;
802 return(1);
803 }
804
805 alter_tmp_dir = (char*) value->val_str(value, buff, &len);
806
807 if (!alter_tmp_dir) {
808 *static_cast<const char**>(save) = alter_tmp_dir;
809 return(0);
810 }
811
812 if (strlen(alter_tmp_dir) > FN_REFLEN) {
813 push_warning_printf(
814 thd, Sql_condition::WARN_LEVEL_WARN,
815 ER_WRONG_ARGUMENTS,
816 "Path length should not exceed %d bytes", FN_REFLEN);
817 *static_cast<const char**>(save) = NULL;
818 return(1);
819 }
820
821 os_normalize_path(alter_tmp_dir);
822 my_realpath(tmp_abs_path, alter_tmp_dir, 0);
823 size_t tmp_abs_len = strlen(tmp_abs_path);
824
825 if (my_access(tmp_abs_path, F_OK)) {
826
827 push_warning_printf(
828 thd, Sql_condition::WARN_LEVEL_WARN,
829 ER_WRONG_ARGUMENTS,
830 "InnoDB: Path doesn't exist.");
831 *static_cast<const char**>(save) = NULL;
832 return(1);
833 } else if (my_access(tmp_abs_path, R_OK | W_OK)) {
834 push_warning_printf(
835 thd, Sql_condition::WARN_LEVEL_WARN,
836 ER_WRONG_ARGUMENTS,
837 "InnoDB: Server doesn't have permission in "
838 "the given location.");
839 *static_cast<const char**>(save) = NULL;
840 return(1);
841 }
842
843 MY_STAT stat_info_dir;
844
845 if (my_stat(tmp_abs_path, &stat_info_dir, MYF(0))) {
846 if ((stat_info_dir.st_mode & S_IFDIR) != S_IFDIR) {
847
848 push_warning_printf(
849 thd, Sql_condition::WARN_LEVEL_WARN,
850 ER_WRONG_ARGUMENTS,
851 "Given path is not a directory. ");
852 *static_cast<const char**>(save) = NULL;
853 return(1);
854 }
855 }
856
857 if (!is_mysql_datadir_path(tmp_abs_path)) {
858
859 push_warning_printf(
860 thd, Sql_condition::WARN_LEVEL_WARN,
861 ER_WRONG_ARGUMENTS,
862 "InnoDB: Path Location should not be same as "
863 "mysql data directory location.");
864 *static_cast<const char**>(save) = NULL;
865 return(1);
866 }
867
868 innodb_tmp_dir = static_cast<char*>(
869 thd_memdup(thd, tmp_abs_path, tmp_abs_len + 1));
870 *static_cast<const char**>(save) = innodb_tmp_dir;
871 return(0);
872}
873
874/******************************************************************//**
875Maps a MySQL trx isolation level code to the InnoDB isolation level code
876@return InnoDB isolation level */
877static inline
878ulint
879innobase_map_isolation_level(
880/*=========================*/
881 enum_tx_isolation iso); /*!< in: MySQL isolation level code */
882
883/** Gets field offset for a field in a table.
884@param[in] table MySQL table object
885@param[in] field MySQL field object (from table->field array)
886@return offset */
887static inline
888uint
889get_field_offset(
890 const TABLE* table,
891 const Field* field)
892{
893 return field->offset(table->record[0]);
894}
895
896
897/*************************************************************//**
898Check for a valid value of innobase_compression_algorithm.
899@return 0 for valid innodb_compression_algorithm. */
900static
901int
902innodb_compression_algorithm_validate(
903/*==================================*/
904 THD* thd, /*!< in: thread handle */
905 struct st_mysql_sys_var* var, /*!< in: pointer to system
906 variable */
907 void* save, /*!< out: immediate result
908 for update function */
909 struct st_mysql_value* value); /*!< in: incoming string */
910
911static ibool innodb_have_lzo=IF_LZO(1, 0);
912static ibool innodb_have_lz4=IF_LZ4(1, 0);
913static ibool innodb_have_lzma=IF_LZMA(1, 0);
914static ibool innodb_have_bzip2=IF_BZIP2(1, 0);
915static ibool innodb_have_snappy=IF_SNAPPY(1, 0);
916static ibool innodb_have_punch_hole=IF_PUNCH_HOLE(1, 0);
917
918static
919int
920innodb_encrypt_tables_validate(
921/*==================================*/
922 THD* thd, /*!< in: thread handle */
923 struct st_mysql_sys_var* var, /*!< in: pointer to system
924 variable */
925 void* save, /*!< out: immediate result
926 for update function */
927 struct st_mysql_value* value); /*!< in: incoming string */
928
929static const char innobase_hton_name[]= "InnoDB";
930
931static MYSQL_THDVAR_BOOL(table_locks, PLUGIN_VAR_OPCMDARG,
932 "Enable InnoDB locking in LOCK TABLES",
933 /* check_func */ NULL, /* update_func */ NULL,
934 /* default */ TRUE);
935
936static MYSQL_THDVAR_BOOL(strict_mode, PLUGIN_VAR_OPCMDARG,
937 "Use strict mode when evaluating create options.",
938 NULL, NULL, TRUE);
939
940static MYSQL_THDVAR_BOOL(ft_enable_stopword, PLUGIN_VAR_OPCMDARG,
941 "Create FTS index with stopword.",
942 NULL, NULL,
943 /* default */ TRUE);
944
945static MYSQL_THDVAR_ULONG(lock_wait_timeout, PLUGIN_VAR_RQCMDARG,
946 "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back. Values above 100000000 disable the timeout.",
947 NULL, NULL, 50, 0, 1024 * 1024 * 1024, 0);
948
949static MYSQL_THDVAR_STR(ft_user_stopword_table,
950 PLUGIN_VAR_OPCMDARG|PLUGIN_VAR_MEMALLOC,
951 "User supplied stopword table name, effective in the session level.",
952 innodb_stopword_table_validate, NULL, NULL);
953
954static MYSQL_THDVAR_STR(tmpdir,
955 PLUGIN_VAR_OPCMDARG|PLUGIN_VAR_MEMALLOC,
956 "Directory for temporary non-tablespace files.",
957 innodb_tmpdir_validate, NULL, NULL);
958
959static SHOW_VAR innodb_status_variables[]= {
960 {"buffer_pool_dump_status",
961 (char*) &export_vars.innodb_buffer_pool_dump_status, SHOW_CHAR},
962 {"buffer_pool_load_status",
963 (char*) &export_vars.innodb_buffer_pool_load_status, SHOW_CHAR},
964 {"buffer_pool_resize_status",
965 (char*) &export_vars.innodb_buffer_pool_resize_status, SHOW_CHAR},
966 {"buffer_pool_load_incomplete",
967 &export_vars.innodb_buffer_pool_load_incomplete, SHOW_BOOL},
968 {"buffer_pool_pages_data",
969 (char*) &export_vars.innodb_buffer_pool_pages_data, SHOW_LONG},
970 {"buffer_pool_bytes_data",
971 (char*) &export_vars.innodb_buffer_pool_bytes_data, SHOW_LONG},
972 {"buffer_pool_pages_dirty",
973 (char*) &export_vars.innodb_buffer_pool_pages_dirty, SHOW_LONG},
974 {"buffer_pool_bytes_dirty",
975 (char*) &export_vars.innodb_buffer_pool_bytes_dirty, SHOW_LONG},
976 {"buffer_pool_pages_flushed",
977 (char*) &export_vars.innodb_buffer_pool_pages_flushed, SHOW_LONG},
978 {"buffer_pool_pages_free",
979 (char*) &export_vars.innodb_buffer_pool_pages_free, SHOW_LONG},
980#ifdef UNIV_DEBUG
981 {"buffer_pool_pages_latched",
982 (char*) &export_vars.innodb_buffer_pool_pages_latched, SHOW_LONG},
983#endif /* UNIV_DEBUG */
984 {"buffer_pool_pages_misc",
985 (char*) &export_vars.innodb_buffer_pool_pages_misc, SHOW_LONG},
986 {"buffer_pool_pages_total",
987 (char*) &export_vars.innodb_buffer_pool_pages_total, SHOW_LONG},
988 {"buffer_pool_read_ahead_rnd",
989 (char*) &export_vars.innodb_buffer_pool_read_ahead_rnd, SHOW_LONG},
990 {"buffer_pool_read_ahead",
991 (char*) &export_vars.innodb_buffer_pool_read_ahead, SHOW_LONG},
992 {"buffer_pool_read_ahead_evicted",
993 (char*) &export_vars.innodb_buffer_pool_read_ahead_evicted, SHOW_LONG},
994 {"buffer_pool_read_requests",
995 (char*) &export_vars.innodb_buffer_pool_read_requests, SHOW_LONG},
996 {"buffer_pool_reads",
997 (char*) &export_vars.innodb_buffer_pool_reads, SHOW_LONG},
998 {"buffer_pool_wait_free",
999 (char*) &export_vars.innodb_buffer_pool_wait_free, SHOW_LONG},
1000 {"buffer_pool_write_requests",
1001 (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
1002 {"data_fsyncs",
1003 (char*) &export_vars.innodb_data_fsyncs, SHOW_LONG},
1004 {"data_pending_fsyncs",
1005 (char*) &export_vars.innodb_data_pending_fsyncs, SHOW_LONG},
1006 {"data_pending_reads",
1007 (char*) &export_vars.innodb_data_pending_reads, SHOW_LONG},
1008 {"data_pending_writes",
1009 (char*) &export_vars.innodb_data_pending_writes, SHOW_LONG},
1010 {"data_read",
1011 (char*) &export_vars.innodb_data_read, SHOW_LONG},
1012 {"data_reads",
1013 (char*) &export_vars.innodb_data_reads, SHOW_LONG},
1014 {"data_writes",
1015 (char*) &export_vars.innodb_data_writes, SHOW_LONG},
1016 {"data_written",
1017 (char*) &export_vars.innodb_data_written, SHOW_LONG},
1018 {"dblwr_pages_written",
1019 (char*) &export_vars.innodb_dblwr_pages_written, SHOW_LONG},
1020 {"dblwr_writes",
1021 (char*) &export_vars.innodb_dblwr_writes, SHOW_LONG},
1022 {"log_waits",
1023 (char*) &export_vars.innodb_log_waits, SHOW_LONG},
1024 {"log_write_requests",
1025 (char*) &export_vars.innodb_log_write_requests, SHOW_LONG},
1026 {"log_writes",
1027 (char*) &export_vars.innodb_log_writes, SHOW_LONG},
1028 {"os_log_fsyncs",
1029 (char*) &export_vars.innodb_os_log_fsyncs, SHOW_LONG},
1030 {"os_log_pending_fsyncs",
1031 (char*) &export_vars.innodb_os_log_pending_fsyncs, SHOW_LONG},
1032 {"os_log_pending_writes",
1033 (char*) &export_vars.innodb_os_log_pending_writes, SHOW_LONG},
1034 {"os_log_written",
1035 (char*) &export_vars.innodb_os_log_written, SHOW_LONGLONG},
1036 {"page_size",
1037 (char*) &export_vars.innodb_page_size, SHOW_LONG},
1038 {"pages_created",
1039 (char*) &export_vars.innodb_pages_created, SHOW_LONG},
1040 {"pages_read",
1041 (char*) &export_vars.innodb_pages_read, SHOW_LONG},
1042 {"pages0_read",
1043 (char*) &export_vars.innodb_page0_read, SHOW_LONG},
1044 {"pages_written",
1045 (char*) &export_vars.innodb_pages_written, SHOW_LONG},
1046 {"row_lock_current_waits",
1047 (char*) &export_vars.innodb_row_lock_current_waits, SHOW_LONG},
1048 {"row_lock_time",
1049 (char*) &export_vars.innodb_row_lock_time, SHOW_LONGLONG},
1050 {"row_lock_time_avg",
1051 (char*) &export_vars.innodb_row_lock_time_avg, SHOW_LONG},
1052 {"row_lock_time_max",
1053 (char*) &export_vars.innodb_row_lock_time_max, SHOW_LONG},
1054 {"row_lock_waits",
1055 (char*) &export_vars.innodb_row_lock_waits, SHOW_LONG},
1056 {"rows_deleted",
1057 (char*) &export_vars.innodb_rows_deleted, SHOW_LONG},
1058 {"rows_inserted",
1059 (char*) &export_vars.innodb_rows_inserted, SHOW_LONG},
1060 {"rows_read",
1061 (char*) &export_vars.innodb_rows_read, SHOW_LONG},
1062 {"rows_updated",
1063 (char*) &export_vars.innodb_rows_updated, SHOW_LONG},
1064 {"system_rows_deleted",
1065 (char*) &export_vars.innodb_system_rows_deleted, SHOW_LONG},
1066 {"system_rows_inserted",
1067 (char*) &export_vars.innodb_system_rows_inserted, SHOW_LONG},
1068 {"system_rows_read",
1069 (char*) &export_vars.innodb_system_rows_read, SHOW_LONG},
1070 {"system_rows_updated",
1071 (char*) &export_vars.innodb_system_rows_updated, SHOW_LONG},
1072 {"num_open_files",
1073 (char*) &export_vars.innodb_num_open_files, SHOW_LONG},
1074 {"truncated_status_writes",
1075 (char*) &export_vars.innodb_truncated_status_writes, SHOW_LONG},
1076 {"available_undo_logs",
1077 (char*) &export_vars.innodb_available_undo_logs, SHOW_LONG},
1078#ifdef UNIV_DEBUG
1079 {"ahi_drop_lookups",
1080 (char*) &export_vars.innodb_ahi_drop_lookups, SHOW_LONG},
1081#endif /* UNIV_DEBUG */
1082
1083 /* Status variables for page compression */
1084 {"page_compression_saved",
1085 (char*) &export_vars.innodb_page_compression_saved, SHOW_LONGLONG},
1086 {"num_index_pages_written",
1087 (char*) &export_vars.innodb_index_pages_written, SHOW_LONGLONG},
1088 {"num_non_index_pages_written",
1089 (char*) &export_vars.innodb_non_index_pages_written, SHOW_LONGLONG},
1090 {"num_pages_page_compressed",
1091 (char*) &export_vars.innodb_pages_page_compressed, SHOW_LONGLONG},
1092 {"num_page_compressed_trim_op",
1093 (char*) &export_vars.innodb_page_compressed_trim_op, SHOW_LONGLONG},
1094 {"num_pages_page_decompressed",
1095 (char*) &export_vars.innodb_pages_page_decompressed, SHOW_LONGLONG},
1096 {"num_pages_page_compression_error",
1097 (char*) &export_vars.innodb_pages_page_compression_error, SHOW_LONGLONG},
1098 {"num_pages_encrypted",
1099 (char*) &export_vars.innodb_pages_encrypted, SHOW_LONGLONG},
1100 {"num_pages_decrypted",
1101 (char*) &export_vars.innodb_pages_decrypted, SHOW_LONGLONG},
1102 {"have_lz4",
1103 (char*) &innodb_have_lz4, SHOW_BOOL},
1104 {"have_lzo",
1105 (char*) &innodb_have_lzo, SHOW_BOOL},
1106 {"have_lzma",
1107 (char*) &innodb_have_lzma, SHOW_BOOL},
1108 {"have_bzip2",
1109 (char*) &innodb_have_bzip2, SHOW_BOOL},
1110 {"have_snappy",
1111 (char*) &innodb_have_snappy, SHOW_BOOL},
1112 {"have_punch_hole",
1113 (char*) &innodb_have_punch_hole, SHOW_BOOL},
1114
1115 /* Defragmentation */
1116 {"defragment_compression_failures",
1117 (char*) &export_vars.innodb_defragment_compression_failures, SHOW_LONG},
1118 {"defragment_failures",
1119 (char*) &export_vars.innodb_defragment_failures, SHOW_LONG},
1120 {"defragment_count",
1121 (char*) &export_vars.innodb_defragment_count, SHOW_LONG},
1122
1123 {"instant_alter_column",
1124 (char*) &export_vars.innodb_instant_alter_column, SHOW_LONG},
1125
1126 /* Online alter table status variables */
1127 {"onlineddl_rowlog_rows",
1128 (char*) &export_vars.innodb_onlineddl_rowlog_rows, SHOW_LONG},
1129 {"onlineddl_rowlog_pct_used",
1130 (char*) &export_vars.innodb_onlineddl_rowlog_pct_used, SHOW_LONG},
1131 {"onlineddl_pct_progress",
1132 (char*) &export_vars.innodb_onlineddl_pct_progress, SHOW_LONG},
1133
1134 /* Times secondary index lookup triggered cluster lookup and
1135 times prefix optimization avoided triggering cluster lookup */
1136 {"secondary_index_triggered_cluster_reads",
1137 (char*) &export_vars.innodb_sec_rec_cluster_reads, SHOW_LONG},
1138 {"secondary_index_triggered_cluster_reads_avoided",
1139 (char*) &export_vars.innodb_sec_rec_cluster_reads_avoided, SHOW_LONG},
1140
1141 /* Encryption */
1142 {"encryption_rotation_pages_read_from_cache",
1143 (char*) &export_vars.innodb_encryption_rotation_pages_read_from_cache,
1144 SHOW_LONG},
1145 {"encryption_rotation_pages_read_from_disk",
1146 (char*) &export_vars.innodb_encryption_rotation_pages_read_from_disk,
1147 SHOW_LONG},
1148 {"encryption_rotation_pages_modified",
1149 (char*) &export_vars.innodb_encryption_rotation_pages_modified,
1150 SHOW_LONG},
1151 {"encryption_rotation_pages_flushed",
1152 (char*) &export_vars.innodb_encryption_rotation_pages_flushed,
1153 SHOW_LONG},
1154 {"encryption_rotation_estimated_iops",
1155 (char*) &export_vars.innodb_encryption_rotation_estimated_iops,
1156 SHOW_LONG},
1157 {"encryption_key_rotation_list_length",
1158 (char*)&export_vars.innodb_key_rotation_list_length,
1159 SHOW_LONGLONG},
1160 {"encryption_n_merge_blocks_encrypted",
1161 (char*)&export_vars.innodb_n_merge_blocks_encrypted,
1162 SHOW_LONGLONG},
1163 {"encryption_n_merge_blocks_decrypted",
1164 (char*)&export_vars.innodb_n_merge_blocks_decrypted,
1165 SHOW_LONGLONG},
1166 {"encryption_n_rowlog_blocks_encrypted",
1167 (char*)&export_vars.innodb_n_rowlog_blocks_encrypted,
1168 SHOW_LONGLONG},
1169 {"encryption_n_rowlog_blocks_decrypted",
1170 (char*)&export_vars.innodb_n_rowlog_blocks_decrypted,
1171 SHOW_LONGLONG},
1172
1173 /* scrubing */
1174 {"scrub_background_page_reorganizations",
1175 (char*) &export_vars.innodb_scrub_page_reorganizations,
1176 SHOW_LONG},
1177 {"scrub_background_page_splits",
1178 (char*) &export_vars.innodb_scrub_page_splits,
1179 SHOW_LONG},
1180 {"scrub_background_page_split_failures_underflow",
1181 (char*) &export_vars.innodb_scrub_page_split_failures_underflow,
1182 SHOW_LONG},
1183 {"scrub_background_page_split_failures_out_of_filespace",
1184 (char*) &export_vars.innodb_scrub_page_split_failures_out_of_filespace,
1185 SHOW_LONG},
1186 {"scrub_background_page_split_failures_missing_index",
1187 (char*) &export_vars.innodb_scrub_page_split_failures_missing_index,
1188 SHOW_LONG},
1189 {"scrub_background_page_split_failures_unknown",
1190 (char*) &export_vars.innodb_scrub_page_split_failures_unknown,
1191 SHOW_LONG},
1192 {"scrub_log",
1193 (char*) &export_vars.innodb_scrub_log,
1194 SHOW_LONGLONG},
1195 {"encryption_num_key_requests",
1196 (char*) &export_vars.innodb_encryption_key_requests, SHOW_LONGLONG},
1197
1198 {NullS, NullS, SHOW_LONG}
1199};
1200
1201/************************************************************************//**
1202Handling the shared INNOBASE_SHARE structure that is needed to provide table
1203locking. Register the table name if it doesn't exist in the hash table. */
1204static
1205INNOBASE_SHARE*
1206get_share(
1207/*======*/
1208 const char* table_name); /*!< in: table to lookup */
1209
1210/************************************************************************//**
1211Free the shared object that was registered with get_share(). */
1212static
1213void
1214free_share(
1215/*=======*/
1216 INNOBASE_SHARE* share); /*!< in/own: share to free */
1217
1218/*****************************************************************//**
1219Frees a possible InnoDB trx object associated with the current THD.
1220@return 0 or error number */
1221static
1222int
1223innobase_close_connection(
1224/*======================*/
1225 handlerton* hton, /*!< in/out: InnoDB handlerton */
1226 THD* thd); /*!< in: MySQL thread handle for
1227 which to close the connection */
1228
1229/** Cancel any pending lock request associated with the current THD.
1230@sa THD::awake() @sa ha_kill_query() */
1231static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels);
1232static void innobase_commit_ordered(handlerton *hton, THD* thd, bool all);
1233
1234/*****************************************************************//**
1235Commits a transaction in an InnoDB database or marks an SQL statement
1236ended.
1237@return 0 */
1238static
1239int
1240innobase_commit(
1241/*============*/
1242 handlerton* hton, /*!< in/out: InnoDB handlerton */
1243 THD* thd, /*!< in: MySQL thread handle of the
1244 user for whom the transaction should
1245 be committed */
1246 bool commit_trx); /*!< in: true - commit transaction
1247 false - the current SQL statement
1248 ended */
1249
1250/*****************************************************************//**
1251Rolls back a transaction to a savepoint.
1252@return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the
1253given name */
1254static
1255int
1256innobase_rollback(
1257/*==============*/
1258 handlerton* hton, /*!< in/out: InnoDB handlerton */
1259 THD* thd, /*!< in: handle to the MySQL thread
1260 of the user whose transaction should
1261 be rolled back */
1262 bool rollback_trx); /*!< in: TRUE - rollback entire
1263 transaction FALSE - rollback the current
1264 statement only */
1265
1266/*****************************************************************//**
1267Rolls back a transaction to a savepoint.
1268@return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the
1269given name */
1270static
1271int
1272innobase_rollback_to_savepoint(
1273/*===========================*/
1274 handlerton* hton, /*!< in/out: InnoDB handlerton */
1275 THD* thd, /*!< in: handle to the MySQL thread of
1276 the user whose XA transaction should
1277 be rolled back to savepoint */
1278 void* savepoint); /*!< in: savepoint data */
1279
1280/*****************************************************************//**
1281Check whether innodb state allows to safely release MDL locks after
1282rollback to savepoint.
1283@return true if it is safe, false if its not safe. */
1284static
1285bool
1286innobase_rollback_to_savepoint_can_release_mdl(
1287/*===========================================*/
1288 handlerton* hton, /*!< in/out: InnoDB handlerton */
1289 THD* thd); /*!< in: handle to the MySQL thread of
1290 the user whose XA transaction should
1291 be rolled back to savepoint */
1292
1293/*****************************************************************//**
1294Sets a transaction savepoint.
1295@return always 0, that is, always succeeds */
1296static
1297int
1298innobase_savepoint(
1299/*===============*/
1300 handlerton* hton, /*!< in/out: InnoDB handlerton */
1301 THD* thd, /*!< in: handle to the MySQL thread of
1302 the user's XA transaction for which
1303 we need to take a savepoint */
1304 void* savepoint); /*!< in: savepoint data */
1305
1306/*****************************************************************//**
1307Release transaction savepoint name.
1308@return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the
1309given name */
1310static
1311int
1312innobase_release_savepoint(
1313/*=======================*/
1314 handlerton* hton, /*!< in/out: handlerton for InnoDB */
1315 THD* thd, /*!< in: handle to the MySQL thread
1316 of the user whose transaction's
1317 savepoint should be released */
1318 void* savepoint); /*!< in: savepoint data */
1319
1320static void innobase_checkpoint_request(handlerton *hton, void *cookie);
1321
1322/** @brief Initialize the default value of innodb_commit_concurrency.
1323
1324Once InnoDB is running, the innodb_commit_concurrency must not change
1325from zero to nonzero. (Bug #42101)
1326
1327The initial default value is 0, and without this extra initialization,
1328SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter
1329to 0, even if it was initially set to nonzero at the command line
1330or configuration file. */
1331static
1332void
1333innobase_commit_concurrency_init_default();
1334/*=======================================*/
1335
1336/** @brief Adjust some InnoDB startup parameters based on file contents
1337or innodb_page_size. */
1338static
1339void
1340innodb_params_adjust();
1341
1342/*******************************************************************//**
1343This function is used to prepare an X/Open XA distributed transaction.
1344@return 0 or error number */
1345static
1346int
1347innobase_xa_prepare(
1348/*================*/
1349 handlerton* hton, /*!< in: InnoDB handlerton */
1350 THD* thd, /*!< in: handle to the MySQL thread of
1351 the user whose XA transaction should
1352 be prepared */
1353 bool all); /*!< in: true - prepare transaction
1354 false - the current SQL statement
1355 ended */
1356/*******************************************************************//**
1357This function is used to recover X/Open XA distributed transactions.
1358@return number of prepared transactions stored in xid_list */
1359static
1360int
1361innobase_xa_recover(
1362/*================*/
1363 handlerton* hton, /*!< in: InnoDB handlerton */
1364 XID* xid_list, /*!< in/out: prepared transactions */
1365 uint len); /*!< in: number of slots in xid_list */
1366/*******************************************************************//**
1367This function is used to commit one X/Open XA distributed transaction
1368which is in the prepared state
1369@return 0 or error number */
1370static
1371int
1372innobase_commit_by_xid(
1373/*===================*/
1374 handlerton* hton, /*!< in: InnoDB handlerton */
1375 XID* xid); /*!< in: X/Open XA transaction
1376 identification */
1377/*******************************************************************//**
1378This function is used to rollback one X/Open XA distributed transaction
1379which is in the prepared state
1380@return 0 or error number */
1381static
1382int
1383innobase_rollback_by_xid(
1384/*=====================*/
1385 handlerton* hton, /*!< in: InnoDB handlerton */
1386 XID* xid); /*!< in: X/Open XA transaction
1387 identification */
1388
1389/** Remove all tables in the named database inside InnoDB.
1390@param[in] hton handlerton from InnoDB
1391@param[in] path Database path; Inside InnoDB the name of the last
1392directory in the path is used as the database name.
1393For example, in 'mysql/data/test' the database name is 'test'. */
1394static
1395void
1396innobase_drop_database(
1397 handlerton* hton,
1398 char* path);
1399
1400/** Shut down the InnoDB storage engine.
1401@return 0 */
1402static
1403int
1404innobase_end(handlerton*, ha_panic_function);
1405
1406/*****************************************************************//**
1407Creates an InnoDB transaction struct for the thd if it does not yet have one.
1408Starts a new InnoDB transaction if a transaction is not yet started. And
1409assigns a new snapshot for a consistent read if the transaction does not yet
1410have one.
1411@return 0 */
1412static
1413int
1414innobase_start_trx_and_assign_read_view(
1415/*====================================*/
1416 handlerton* hton, /* in: InnoDB handlerton */
1417 THD* thd); /* in: MySQL thread handle of the
1418 user for whom the transaction should
1419 be committed */
1420
1421/** Flush InnoDB redo logs to the file system.
1422@param[in] hton InnoDB handlerton
1423@param[in] binlog_group_flush true if we got invoked by binlog
1424group commit during flush stage, false in other cases.
1425@return false */
1426static
1427bool
1428innobase_flush_logs(
1429 handlerton* hton,
1430 bool binlog_group_flush)
1431{
1432 DBUG_ENTER("innobase_flush_logs");
1433 DBUG_ASSERT(hton == innodb_hton_ptr);
1434
1435 if (srv_read_only_mode) {
1436 DBUG_RETURN(false);
1437 }
1438
1439 /* If !binlog_group_flush, we got invoked by FLUSH LOGS or similar.
1440 Else, we got invoked by binlog group commit during flush stage. */
1441
1442 if (binlog_group_flush && srv_flush_log_at_trx_commit == 0) {
1443 /* innodb_flush_log_at_trx_commit=0
1444 (write and sync once per second).
1445 Do not flush the redo log during binlog group commit. */
1446 DBUG_RETURN(false);
1447 }
1448
1449 /* Flush the redo log buffer to the redo log file.
1450 Sync it to disc if we are in FLUSH LOGS, or if
1451 innodb_flush_log_at_trx_commit=1
1452 (write and sync at each commit). */
1453 log_buffer_flush_to_disk(!binlog_group_flush
1454 || srv_flush_log_at_trx_commit == 1);
1455
1456 DBUG_RETURN(false);
1457}
1458
1459/** Flush InnoDB redo logs to the file system.
1460@param[in] hton InnoDB handlerton
1461@param[in] binlog_group_flush true if we got invoked by binlog
1462group commit during flush stage, false in other cases.
1463@return false */
1464static
1465bool
1466innobase_flush_logs(
1467 handlerton* hton)
1468{
1469 return innobase_flush_logs(hton, true);
1470}
1471
1472/************************************************************************//**
1473Implements the SHOW ENGINE INNODB STATUS command. Sends the output of the
1474InnoDB Monitor to the client.
1475@return 0 on success */
1476static
1477int
1478innodb_show_status(
1479/*===============*/
1480 handlerton* hton, /*!< in: the innodb handlerton */
1481 THD* thd, /*!< in: the MySQL query thread of
1482 the caller */
1483 stat_print_fn* stat_print);
1484/************************************************************************//**
1485Return 0 on success and non-zero on failure. Note: the bool return type
1486seems to be abused here, should be an int. */
1487static
1488bool
1489innobase_show_status(
1490/*=================*/
1491 handlerton* hton, /*!< in: the innodb handlerton */
1492 THD* thd, /*!< in: the MySQL query thread of
1493 the caller */
1494 stat_print_fn* stat_print,
1495 enum ha_stat_type stat_type);
1496
1497/****************************************************************//**
1498Parse and enable InnoDB monitor counters during server startup.
1499User can enable monitor counters/groups by specifying
1500"loose-innodb_monitor_enable = monitor_name1;monitor_name2..."
1501in server configuration file or at the command line. */
1502static
1503void
1504innodb_enable_monitor_at_startup(
1505/*=============================*/
1506 char* str); /*!< in: monitor counter enable list */
1507
1508#ifdef MYSQL_STORE_FTS_DOC_ID
1509/** Store doc_id value into FTS_DOC_ID field
1510@param[in,out] tbl table containing FULLTEXT index
1511@param[in] doc_id FTS_DOC_ID value */
1512static
1513void
1514innobase_fts_store_docid(
1515 TABLE* tbl,
1516 ulonglong doc_id)
1517{
1518 my_bitmap_map* old_map
1519 = dbug_tmp_use_all_columns(tbl, tbl->write_set);
1520
1521 tbl->fts_doc_id_field->store(static_cast<longlong>(doc_id), true);
1522
1523 dbug_tmp_restore_column_map(tbl->write_set, old_map);
1524}
1525#endif
1526
1527/*************************************************************//**
1528Check for a valid value of innobase_commit_concurrency.
1529@return 0 for valid innodb_commit_concurrency */
1530static
1531int
1532innobase_commit_concurrency_validate(
1533/*=================================*/
1534 THD*, st_mysql_sys_var*,
1535 void* save, /*!< out: immediate result
1536 for update function */
1537 struct st_mysql_value* value) /*!< in: incoming string */
1538{
1539 long long intbuf;
1540 ulong commit_concurrency;
1541
1542 DBUG_ENTER("innobase_commit_concurrency_validate");
1543
1544 if (value->val_int(value, &intbuf)) {
1545 /* The value is NULL. That is invalid. */
1546 DBUG_RETURN(1);
1547 }
1548
1549 *reinterpret_cast<ulong*>(save) = commit_concurrency
1550 = static_cast<ulong>(intbuf);
1551
1552 /* Allow the value to be updated, as long as it remains zero
1553 or nonzero. */
1554 DBUG_RETURN(!(!commit_concurrency == !innobase_commit_concurrency));
1555}
1556
1557/*******************************************************************//**
1558Function for constructing an InnoDB table handler instance. */
1559static
1560handler*
1561innobase_create_handler(
1562/*====================*/
1563 handlerton* hton, /*!< in: InnoDB handlerton */
1564 TABLE_SHARE* table,
1565 MEM_ROOT* mem_root)
1566{
1567 return(new (mem_root) ha_innobase(hton, table));
1568}
1569
1570/* General functions */
1571
1572/** Check that a page_size is correct for InnoDB.
1573If correct, set the associated page_size_shift which is the power of 2
1574for this page size.
1575@param[in] page_size Page Size to evaluate
1576@return an associated page_size_shift if valid, 0 if invalid. */
1577inline
1578ulong
1579innodb_page_size_validate(
1580 ulong page_size)
1581{
1582 ulong n;
1583
1584 DBUG_ENTER("innodb_page_size_validate");
1585
1586 for (n = UNIV_PAGE_SIZE_SHIFT_MIN;
1587 n <= UNIV_PAGE_SIZE_SHIFT_MAX;
1588 n++) {
1589 if (page_size == static_cast<ulong>(1 << n)) {
1590 DBUG_RETURN(n);
1591 }
1592 }
1593
1594 DBUG_RETURN(0);
1595}
1596
1597/******************************************************************//**
1598Returns true if the thread is the replication thread on the slave
1599server. Used in srv_conc_enter_innodb() to determine if the thread
1600should be allowed to enter InnoDB - the replication thread is treated
1601differently than other threads. Also used in
1602srv_conc_force_exit_innodb().
1603@return true if thd is the replication thread */
1604ibool
1605thd_is_replication_slave_thread(
1606/*============================*/
1607 THD* thd) /*!< in: thread handle */
1608{
1609 return thd && ((ibool) thd_slave_thread(thd));
1610}
1611
1612/******************************************************************//**
1613Returns true if transaction should be flagged as read-only.
1614@return true if the thd is marked as read-only */
1615bool
1616thd_trx_is_read_only(
1617/*=================*/
1618 THD* thd) /*!< in: thread handle */
1619{
1620 return(thd != 0 && thd_tx_is_read_only(thd));
1621}
1622
1623static MYSQL_THDVAR_BOOL(background_thread,
1624 PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_NOSYSVAR,
1625 "Internal (not user visible) flag to mark "
1626 "background purge threads", NULL, NULL, 0);
1627
1628/** Create a MYSQL_THD for a background thread and mark it as such.
1629@param name thread info for SHOW PROCESSLIST
1630@return new MYSQL_THD */
1631MYSQL_THD
1632innobase_create_background_thd(const char* name)
1633/*============================*/
1634{
1635 MYSQL_THD thd= create_thd();
1636 thd_proc_info(thd, name);
1637 THDVAR(thd, background_thread) = true;
1638 return thd;
1639}
1640
1641
1642/** Destroy a background purge thread THD.
1643@param[in] thd MYSQL_THD to destroy */
1644void
1645innobase_destroy_background_thd(
1646/*============================*/
1647 MYSQL_THD thd)
1648{
1649 /* need to close the connection explicitly, the server won't do it
1650 if innodb is in the PLUGIN_IS_DYING state */
1651 innobase_close_connection(innodb_hton_ptr, thd);
1652 thd_set_ha_data(thd, innodb_hton_ptr, NULL);
1653 destroy_thd(thd);
1654}
1655
1656/** Close opened tables, free memory, delete items for a MYSQL_THD.
1657@param[in] thd MYSQL_THD to reset */
1658void
1659innobase_reset_background_thd(MYSQL_THD thd)
1660{
1661 if (!thd) {
1662 thd = current_thd;
1663 }
1664
1665 ut_ad(thd);
1666 ut_ad(THDVAR(thd, background_thread));
1667
1668 /* background purge thread */
1669 const char *proc_info= thd_proc_info(thd, "reset");
1670 reset_thd(thd);
1671 thd_proc_info(thd, proc_info);
1672}
1673
1674
1675/******************************************************************//**
1676Check if the transaction is an auto-commit transaction. TRUE also
1677implies that it is a SELECT (read-only) transaction.
1678@return true if the transaction is an auto commit read-only transaction. */
1679ibool
1680thd_trx_is_auto_commit(
1681/*===================*/
1682 THD* thd) /*!< in: thread handle, can be NULL */
1683{
1684 return(thd != NULL
1685 && !thd_test_options(
1686 thd,
1687 OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)
1688 && thd_is_select(thd));
1689}
1690
1691extern "C" time_t thd_start_time(const THD* thd);
1692
1693/******************************************************************//**
1694Get the thread start time.
1695@return the thread start time in seconds since the epoch. */
1696ulint thd_start_time_in_secs(THD*)
1697{
1698 // FIXME: This function should be added to the server code.
1699 //return(thd_start_time(thd));
1700 return(ulint(ut_time()));
1701}
1702
1703/** Enter InnoDB engine after checking the max number of user threads
1704allowed, else the thread is put into sleep.
1705@param[in,out] prebuilt row prebuilt handler */
1706static inline
1707void
1708innobase_srv_conc_enter_innodb(
1709 row_prebuilt_t* prebuilt)
1710{
1711#ifdef WITH_WSREP
1712 if (wsrep_on(prebuilt->trx->mysql_thd) &&
1713 wsrep_thd_is_BF(prebuilt->trx->mysql_thd, FALSE)) {
1714 return;
1715 }
1716#endif /* WITH_WSREP */
1717
1718 trx_t* trx = prebuilt->trx;
1719
1720 if (srv_thread_concurrency) {
1721 if (trx->n_tickets_to_enter_innodb > 0) {
1722
1723 /* If trx has 'free tickets' to enter the engine left,
1724 then use one such ticket */
1725
1726 --trx->n_tickets_to_enter_innodb;
1727
1728 } else if (trx->mysql_thd != NULL
1729 && thd_is_replication_slave_thread(trx->mysql_thd)) {
1730
1731 UT_WAIT_FOR(
1732 srv_conc_get_active_threads()
1733 < srv_thread_concurrency,
1734 srv_replication_delay * 1000);
1735
1736 } else {
1737 srv_conc_enter_innodb(prebuilt);
1738 }
1739 }
1740}
1741
1742/** Note that the thread wants to leave InnoDB only if it doesn't have
1743any spare tickets.
1744@param[in,out] m_prebuilt row prebuilt handler */
1745static inline
1746void
1747innobase_srv_conc_exit_innodb(
1748 row_prebuilt_t* prebuilt)
1749{
1750 ut_ad(!sync_check_iterate(sync_check()));
1751
1752#ifdef WITH_WSREP
1753 if (wsrep_on(prebuilt->trx->mysql_thd) &&
1754 wsrep_thd_is_BF(prebuilt->trx->mysql_thd, FALSE)) {
1755 return;
1756 }
1757#endif /* WITH_WSREP */
1758
1759 trx_t* trx = prebuilt->trx;
1760
1761 /* This is to avoid making an unnecessary function call. */
1762 if (trx->declared_to_be_inside_innodb
1763 && trx->n_tickets_to_enter_innodb == 0) {
1764
1765 srv_conc_force_exit_innodb(trx);
1766 }
1767}
1768
1769/******************************************************************//**
1770Force a thread to leave InnoDB even if it has spare tickets. */
1771static inline
1772void
1773innobase_srv_conc_force_exit_innodb(
1774/*================================*/
1775 trx_t* trx) /*!< in: transaction handle */
1776{
1777 ut_ad(!sync_check_iterate(sync_check()));
1778
1779 /* This is to avoid making an unnecessary function call. */
1780 if (trx->declared_to_be_inside_innodb) {
1781 srv_conc_force_exit_innodb(trx);
1782 }
1783}
1784
1785/******************************************************************//**
1786Returns the NUL terminated value of glob_hostname.
1787@return pointer to glob_hostname. */
1788const char*
1789server_get_hostname()
1790/*=================*/
1791{
1792 return(glob_hostname);
1793}
1794
1795/******************************************************************//**
1796Returns true if the transaction this thread is processing has edited
1797non-transactional tables. Used by the deadlock detector when deciding
1798which transaction to rollback in case of a deadlock - we try to avoid
1799rolling back transactions that have edited non-transactional tables.
1800@return true if non-transactional tables have been edited */
1801ibool
1802thd_has_edited_nontrans_tables(
1803/*===========================*/
1804 THD* thd) /*!< in: thread handle */
1805{
1806 return((ibool) thd_non_transactional_update(thd));
1807}
1808
1809/* Return high resolution timestamp for the start of the current query */
1810UNIV_INTERN
1811unsigned long long
1812thd_query_start_micro(
1813 const THD* thd) /*!< in: thread handle */
1814{
1815 return thd_start_utime(thd);
1816}
1817
1818/******************************************************************//**
1819Returns true if the thread is executing a SELECT statement.
1820@return true if thd is executing SELECT */
1821ibool
1822thd_is_select(
1823/*==========*/
1824 const THD* thd) /*!< in: thread handle */
1825{
1826 return(thd_sql_command(thd) == SQLCOM_SELECT);
1827}
1828
1829/******************************************************************//**
1830Returns the lock wait timeout for the current connection.
1831@return the lock wait timeout, in seconds */
1832ulong
1833thd_lock_wait_timeout(
1834/*==================*/
1835 THD* thd) /*!< in: thread handle, or NULL to query
1836 the global innodb_lock_wait_timeout */
1837{
1838 /* According to <mysql/plugin.h>, passing thd == NULL
1839 returns the global value of the session variable. */
1840 return(THDVAR(thd, lock_wait_timeout));
1841}
1842
1843/** Get the value of innodb_tmpdir.
1844@param[in] thd thread handle, or NULL to query
1845 the global innodb_tmpdir.
1846@retval NULL if innodb_tmpdir="" */
1847const char*
1848thd_innodb_tmpdir(
1849 THD* thd)
1850{
1851 ut_ad(!sync_check_iterate(sync_check()));
1852
1853 const char* tmp_dir = THDVAR(thd, tmpdir);
1854
1855 if (tmp_dir != NULL && *tmp_dir == '\0') {
1856 tmp_dir = NULL;
1857 }
1858
1859 return(tmp_dir);
1860}
1861
1862/** Obtain the InnoDB transaction of a MySQL thread.
1863@param[in,out] thd thread handle
1864@return reference to transaction pointer */
1865static trx_t* thd_to_trx(THD* thd)
1866{
1867 return *reinterpret_cast<trx_t**>(thd_ha_data(thd, innodb_hton_ptr));
1868}
1869
1870#ifdef WITH_WSREP
1871/********************************************************************//**
1872Obtain the InnoDB transaction id of a MySQL thread.
1873@return transaction id */
1874__attribute__((warn_unused_result, nonnull))
1875ulonglong
1876thd_to_trx_id(
1877 THD* thd) /*!< in: MySQL thread */
1878{
1879 return(thd_to_trx(thd)->id);
1880}
1881#endif /* WITH_WSREP */
1882
1883/********************************************************************//**
1884Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
1885time calls srv_active_wake_master_thread. This function should be used
1886when a single database operation may introduce a small need for
1887server utility activity, like checkpointing. */
1888inline
1889void
1890innobase_active_small(void)
1891/*=======================*/
1892{
1893 innobase_active_counter++;
1894
1895 if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
1896 srv_active_wake_master_thread();
1897 }
1898}
1899
1900/********************************************************************//**
1901Converts an InnoDB error code to a MySQL error code and also tells to MySQL
1902about a possible transaction rollback inside InnoDB caused by a lock wait
1903timeout or a deadlock.
1904@return MySQL error code */
1905static int
1906convert_error_code_to_mysql(
1907/*========================*/
1908 dberr_t error, /*!< in: InnoDB error code */
1909 ulint flags, /*!< in: InnoDB table flags, or 0 */
1910 THD* thd) /*!< in: user thread handle or NULL */
1911{
1912 switch (error) {
1913 case DB_SUCCESS:
1914 return(0);
1915
1916 case DB_INTERRUPTED:
1917 return(HA_ERR_ABORTED_BY_USER);
1918
1919 case DB_FOREIGN_EXCEED_MAX_CASCADE:
1920 ut_ad(thd);
1921 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
1922 HA_ERR_ROW_IS_REFERENCED,
1923 "InnoDB: Cannot delete/update "
1924 "rows with cascading foreign key "
1925 "constraints that exceed max "
1926 "depth of %d. Please "
1927 "drop extra constraints and try "
1928 "again", DICT_FK_MAX_RECURSIVE_LOAD);
1929 return(HA_ERR_FK_DEPTH_EXCEEDED);
1930
1931 case DB_CANT_CREATE_GEOMETRY_OBJECT:
1932 my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
1933 return(HA_ERR_NULL_IN_SPATIAL);
1934
1935 case DB_ERROR:
1936 default:
1937 return(HA_ERR_GENERIC); /* unspecified error */
1938
1939 case DB_DUPLICATE_KEY:
1940 /* Be cautious with returning this error, since
1941 mysql could re-enter the storage layer to get
1942 duplicated key info, the operation requires a
1943 valid table handle and/or transaction information,
1944 which might not always be available in the error
1945 handling stage. */
1946 return(HA_ERR_FOUND_DUPP_KEY);
1947
1948 case DB_READ_ONLY:
1949 return(HA_ERR_TABLE_READONLY);
1950
1951 case DB_FOREIGN_DUPLICATE_KEY:
1952 return(HA_ERR_FOREIGN_DUPLICATE_KEY);
1953
1954 case DB_MISSING_HISTORY:
1955 return(HA_ERR_TABLE_DEF_CHANGED);
1956
1957 case DB_RECORD_NOT_FOUND:
1958 return(HA_ERR_NO_ACTIVE_RECORD);
1959
1960 case DB_DEADLOCK:
1961 /* Since we rolled back the whole transaction, we must
1962 tell it also to MySQL so that MySQL knows to empty the
1963 cached binlog for this transaction */
1964
1965 if (thd != NULL) {
1966 thd_mark_transaction_to_rollback(thd, 1);
1967 }
1968
1969 return(HA_ERR_LOCK_DEADLOCK);
1970
1971 case DB_LOCK_WAIT_TIMEOUT:
1972 /* Starting from 5.0.13, we let MySQL just roll back the
1973 latest SQL statement in a lock wait timeout. Previously, we
1974 rolled back the whole transaction. */
1975
1976 if (thd) {
1977 thd_mark_transaction_to_rollback(
1978 thd, (bool) row_rollback_on_timeout);
1979 }
1980
1981 return(HA_ERR_LOCK_WAIT_TIMEOUT);
1982
1983 case DB_NO_REFERENCED_ROW:
1984 return(HA_ERR_NO_REFERENCED_ROW);
1985
1986 case DB_ROW_IS_REFERENCED:
1987 return(HA_ERR_ROW_IS_REFERENCED);
1988
1989 case DB_NO_FK_ON_S_BASE_COL:
1990 case DB_CANNOT_ADD_CONSTRAINT:
1991 case DB_CHILD_NO_INDEX:
1992 case DB_PARENT_NO_INDEX:
1993 return(HA_ERR_CANNOT_ADD_FOREIGN);
1994
1995 case DB_CANNOT_DROP_CONSTRAINT:
1996
1997 return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
1998 misleading, a new MySQL error
1999 code should be introduced */
2000
2001 case DB_CORRUPTION:
2002 return(HA_ERR_CRASHED);
2003
2004 case DB_OUT_OF_FILE_SPACE:
2005 return(HA_ERR_RECORD_FILE_FULL);
2006
2007 case DB_TEMP_FILE_WRITE_FAIL:
2008 my_error(ER_GET_ERRMSG, MYF(0),
2009 DB_TEMP_FILE_WRITE_FAIL,
2010 ut_strerr(DB_TEMP_FILE_WRITE_FAIL),
2011 "InnoDB");
2012 return(HA_ERR_INTERNAL_ERROR);
2013
2014 case DB_TABLE_IN_FK_CHECK:
2015 return(HA_ERR_TABLE_IN_FK_CHECK);
2016
2017 case DB_TABLE_IS_BEING_USED:
2018 return(HA_ERR_WRONG_COMMAND);
2019
2020 case DB_TABLE_NOT_FOUND:
2021 return(HA_ERR_NO_SUCH_TABLE);
2022
2023 case DB_DECRYPTION_FAILED:
2024 return(HA_ERR_DECRYPTION_FAILED);
2025
2026 case DB_TABLESPACE_NOT_FOUND:
2027 return(HA_ERR_TABLESPACE_MISSING);
2028
2029 case DB_TOO_BIG_RECORD: {
2030 /* If prefix is true then a 768-byte prefix is stored
2031 locally for BLOB fields. Refer to dict_table_get_format().
2032 We limit max record size to 16k for 64k page size. */
2033 bool prefix = !DICT_TF_HAS_ATOMIC_BLOBS(flags);
2034 bool comp = !!(flags & DICT_TF_COMPACT);
2035 ulint free_space = page_get_free_space_of_empty(comp) / 2;
2036
2037 if (free_space >= ulint(comp ? COMPRESSED_REC_MAX_DATA_SIZE :
2038 REDUNDANT_REC_MAX_DATA_SIZE)) {
2039 free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
2040 REDUNDANT_REC_MAX_DATA_SIZE) - 1;
2041 }
2042
2043 my_printf_error(ER_TOO_BIG_ROWSIZE,
2044 "Row size too large (> " ULINTPF "). Changing some columns "
2045 "to TEXT or BLOB %smay help. In current row "
2046 "format, BLOB prefix of %d bytes is stored inline.",
2047 MYF(0),
2048 free_space,
2049 prefix
2050 ? "or using ROW_FORMAT=DYNAMIC or"
2051 " ROW_FORMAT=COMPRESSED "
2052 : "",
2053 prefix
2054 ? DICT_MAX_FIXED_COL_LEN
2055 : 0);
2056 return(HA_ERR_TO_BIG_ROW);
2057 }
2058
2059 case DB_TOO_BIG_INDEX_COL:
2060 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
2061 (ulong) DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
2062 return(HA_ERR_INDEX_COL_TOO_LONG);
2063
2064 case DB_NO_SAVEPOINT:
2065 return(HA_ERR_NO_SAVEPOINT);
2066
2067 case DB_LOCK_TABLE_FULL:
2068 /* Since we rolled back the whole transaction, we must
2069 tell it also to MySQL so that MySQL knows to empty the
2070 cached binlog for this transaction */
2071
2072 if (thd) {
2073 thd_mark_transaction_to_rollback(thd, 1);
2074 }
2075
2076 return(HA_ERR_LOCK_TABLE_FULL);
2077
2078 case DB_FTS_INVALID_DOCID:
2079 return(HA_FTS_INVALID_DOCID);
2080 case DB_FTS_EXCEED_RESULT_CACHE_LIMIT:
2081 return(HA_ERR_OUT_OF_MEM);
2082 case DB_TOO_MANY_CONCURRENT_TRXS:
2083 return(HA_ERR_TOO_MANY_CONCURRENT_TRXS);
2084 case DB_UNSUPPORTED:
2085 return(HA_ERR_UNSUPPORTED);
2086 case DB_INDEX_CORRUPT:
2087 return(HA_ERR_INDEX_CORRUPT);
2088 case DB_UNDO_RECORD_TOO_BIG:
2089 return(HA_ERR_UNDO_REC_TOO_BIG);
2090 case DB_OUT_OF_MEMORY:
2091 return(HA_ERR_OUT_OF_MEM);
2092 case DB_TABLESPACE_EXISTS:
2093 return(HA_ERR_TABLESPACE_EXISTS);
2094 case DB_TABLESPACE_DELETED:
2095 return(HA_ERR_TABLESPACE_MISSING);
2096 case DB_IDENTIFIER_TOO_LONG:
2097 return(HA_ERR_INTERNAL_ERROR);
2098 case DB_TABLE_CORRUPT:
2099 return(HA_ERR_TABLE_CORRUPT);
2100 case DB_FTS_TOO_MANY_WORDS_IN_PHRASE:
2101 return(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE);
2102 case DB_COMPUTE_VALUE_FAILED:
2103 return(HA_ERR_GENERIC); // impossible
2104 }
2105}
2106
2107/*************************************************************//**
2108Prints info of a THD object (== user session thread) to the given file. */
2109void
2110innobase_mysql_print_thd(
2111/*=====================*/
2112 FILE* f, /*!< in: output stream */
2113 THD* thd, /*!< in: MySQL THD object */
2114 uint max_query_len) /*!< in: max query length to print, or 0 to
2115 use the default max length */
2116{
2117 char buffer[1024];
2118
2119 fputs(thd_get_error_context_description(thd, buffer, sizeof buffer,
2120 max_query_len), f);
2121 putc('\n', f);
2122}
2123
2124/******************************************************************//**
2125Get the variable length bounds of the given character set. */
2126void
2127innobase_get_cset_width(
2128/*====================*/
2129 ulint cset, /*!< in: MySQL charset-collation code */
2130 ulint* mbminlen, /*!< out: minimum length of a char (in bytes) */
2131 ulint* mbmaxlen) /*!< out: maximum length of a char (in bytes) */
2132{
2133 CHARSET_INFO* cs;
2134 ut_ad(cset <= MAX_CHAR_COLL_NUM);
2135 ut_ad(mbminlen);
2136 ut_ad(mbmaxlen);
2137
2138 cs = all_charsets[cset];
2139 if (cs) {
2140 *mbminlen = cs->mbminlen;
2141 *mbmaxlen = cs->mbmaxlen;
2142 ut_ad(*mbminlen < DATA_MBMAX);
2143 ut_ad(*mbmaxlen < DATA_MBMAX);
2144 } else {
2145 THD* thd = current_thd;
2146
2147 if (thd && thd_sql_command(thd) == SQLCOM_DROP_TABLE) {
2148
2149 /* Fix bug#46256: allow tables to be dropped if the
2150 collation is not found, but issue a warning. */
2151 if (cset != 0) {
2152
2153 sql_print_warning(
2154 "Unknown collation #" ULINTPF ".",
2155 cset);
2156 }
2157 } else {
2158
2159 ut_a(cset == 0);
2160 }
2161
2162 *mbminlen = *mbmaxlen = 0;
2163 }
2164}
2165
2166/******************************************************************//**
2167Converts an identifier to a table name. */
2168void
2169innobase_convert_from_table_id(
2170/*===========================*/
2171 CHARSET_INFO* cs, /*!< in: the 'from' character set */
2172 char* to, /*!< out: converted identifier */
2173 const char* from, /*!< in: identifier to convert */
2174 ulint len) /*!< in: length of 'to', in bytes */
2175{
2176 uint errors;
2177
2178 strconvert(cs, from, FN_REFLEN, &my_charset_filename, to, (uint) len, &errors);
2179}
2180
2181/**********************************************************************
2182Check if the length of the identifier exceeds the maximum allowed.
2183return true when length of identifier is too long. */
2184my_bool
2185innobase_check_identifier_length(
2186/*=============================*/
2187 const char* id) /* in: FK identifier to check excluding the
2188 database portion. */
2189{
2190 int well_formed_error = 0;
2191 CHARSET_INFO *cs = system_charset_info;
2192 DBUG_ENTER("innobase_check_identifier_length");
2193
2194 size_t len = my_well_formed_length(
2195 cs, id, id + strlen(id),
2196 NAME_CHAR_LEN, &well_formed_error);
2197
2198 if (well_formed_error || len == NAME_CHAR_LEN) {
2199 my_error(ER_TOO_LONG_IDENT, MYF(0), id);
2200 DBUG_RETURN(true);
2201 }
2202 DBUG_RETURN(false);
2203}
2204
2205/******************************************************************//**
2206Converts an identifier to UTF-8. */
2207void
2208innobase_convert_from_id(
2209/*=====================*/
2210 CHARSET_INFO* cs, /*!< in: the 'from' character set */
2211 char* to, /*!< out: converted identifier */
2212 const char* from, /*!< in: identifier to convert */
2213 ulint len) /*!< in: length of 'to', in bytes */
2214{
2215 uint errors;
2216
2217 strconvert(cs, from, FN_REFLEN, system_charset_info, to, (uint) len, &errors);
2218}
2219
2220/******************************************************************//**
2221Compares NUL-terminated UTF-8 strings case insensitively.
2222@return 0 if a=b, <0 if a<b, >1 if a>b */
2223int
2224innobase_strcasecmp(
2225/*================*/
2226 const char* a, /*!< in: first string to compare */
2227 const char* b) /*!< in: second string to compare */
2228{
2229 if (!a) {
2230 if (!b) {
2231 return(0);
2232 } else {
2233 return(-1);
2234 }
2235 } else if (!b) {
2236 return(1);
2237 }
2238
2239 return(my_strcasecmp(system_charset_info, a, b));
2240}
2241
2242/******************************************************************//**
2243Compares NUL-terminated UTF-8 strings case insensitively. The
2244second string contains wildcards.
2245@return 0 if a match is found, 1 if not */
2246static
2247int
2248innobase_wildcasecmp(
2249/*=================*/
2250 const char* a, /*!< in: string to compare */
2251 const char* b) /*!< in: wildcard string to compare */
2252{
2253 return(wild_case_compare(system_charset_info, a, b));
2254}
2255
2256/** Strip dir name from a full path name and return only the file name
2257@param[in] path_name full path name
2258@return file name or "null" if no file name */
2259const char*
2260innobase_basename(
2261 const char* path_name)
2262{
2263 const char* name = base_name(path_name);
2264
2265 return((name) ? name : "null");
2266}
2267
2268/******************************************************************//**
2269Makes all characters in a NUL-terminated UTF-8 string lower case. */
2270void
2271innobase_casedn_str(
2272/*================*/
2273 char* a) /*!< in/out: string to put in lower case */
2274{
2275 my_casedn_str(system_charset_info, a);
2276}
2277
2278/**********************************************************************//**
2279Determines the connection character set.
2280@return connection character set */
2281CHARSET_INFO*
2282innobase_get_charset(
2283/*=================*/
2284 THD* mysql_thd) /*!< in: MySQL thread handle */
2285{
2286 return(thd_charset(mysql_thd));
2287}
2288
2289/** Determines the current SQL statement.
2290Thread unsafe, can only be called from the thread owning the THD.
2291@param[in] thd MySQL thread handle
2292@param[out] length Length of the SQL statement
2293@return SQL statement string */
2294const char*
2295innobase_get_stmt_unsafe(
2296 THD* thd,
2297 size_t* length)
2298{
2299 if (const LEX_STRING *stmt = thd_query_string(thd)) {
2300 *length = stmt->length;
2301 return stmt->str;
2302 }
2303
2304 *length = 0;
2305 return NULL;
2306}
2307
2308/** Determines the current SQL statement.
2309Thread safe, can be called from any thread as the string is copied
2310into the provided buffer.
2311@param[in] thd MySQL thread handle
2312@param[out] buf Buffer containing SQL statement
2313@param[in] buflen Length of provided buffer
2314@return Length of the SQL statement */
2315size_t
2316innobase_get_stmt_safe(
2317 THD* thd,
2318 char* buf,
2319 size_t buflen)
2320{
2321 return thd_query_safe(thd, buf, buflen);
2322}
2323
2324/**********************************************************************//**
2325Get the current setting of the tdc_size global parameter. We do
2326a dirty read because for one there is no synchronization object and
2327secondly there is little harm in doing so even if we get a torn read.
2328@return value of tdc_size */
2329ulint
2330innobase_get_table_cache_size(void)
2331/*===============================*/
2332{
2333 return(tdc_size);
2334}
2335
2336/**********************************************************************//**
2337Get the current setting of the lower_case_table_names global parameter from
2338mysqld.cc. We do a dirty read because for one there is no synchronization
2339object and secondly there is little harm in doing so even if we get a torn
2340read.
2341@return value of lower_case_table_names */
2342ulint
2343innobase_get_lower_case_table_names(void)
2344/*=====================================*/
2345{
2346 return(lower_case_table_names);
2347}
2348
2349/**
2350 Test a file path whether it is same as mysql data directory path.
2351
2352 @param path null terminated character string
2353
2354 @return
2355 @retval TRUE The path is different from mysql data directory.
2356 @retval FALSE The path is same as mysql data directory.
2357*/
2358static bool is_mysql_datadir_path(const char *path)
2359{
2360 if (path == NULL)
2361 return false;
2362
2363 char mysql_data_dir[FN_REFLEN], path_dir[FN_REFLEN];
2364 convert_dirname(path_dir, path, NullS);
2365 convert_dirname(mysql_data_dir, mysql_unpacked_real_data_home, NullS);
2366 size_t mysql_data_home_len= dirname_length(mysql_data_dir);
2367 size_t path_len = dirname_length(path_dir);
2368
2369 if (path_len < mysql_data_home_len)
2370 return true;
2371
2372 if (!lower_case_file_system)
2373 return(memcmp(mysql_data_dir, path_dir, mysql_data_home_len));
2374
2375 return(files_charset_info->coll->strnncoll(files_charset_info,
2376 (uchar *) path_dir, path_len,
2377 (uchar *) mysql_data_dir,
2378 mysql_data_home_len,
2379 TRUE));
2380}
2381
2382static int mysql_tmpfile_path(const char *path, const char *prefix)
2383{
2384 DBUG_ASSERT(path != NULL);
2385 DBUG_ASSERT((strlen(path) + strlen(prefix)) <= FN_REFLEN);
2386
2387 char filename[FN_REFLEN];
2388 File fd = create_temp_file(filename, path, prefix, O_BINARY | O_SEQUENTIAL,
2389 MYF(MY_WME | MY_TEMPORARY));
2390 return fd;
2391}
2392
2393/** Creates a temporary file in the location specified by the parameter
2394path. If the path is NULL, then it will be created in tmpdir.
2395@param[in] path location for creating temporary file
2396@return temporary file descriptor, or < 0 on error */
2397os_file_t
2398innobase_mysql_tmpfile(
2399 const char* path)
2400{
2401#ifdef WITH_INNODB_DISALLOW_WRITES
2402 os_event_wait(srv_allow_writes_event);
2403#endif /* WITH_INNODB_DISALLOW_WRITES */
2404 File fd;
2405
2406 DBUG_EXECUTE_IF(
2407 "innobase_tmpfile_creation_failure",
2408 return(OS_FILE_CLOSED);
2409 );
2410
2411 if (path == NULL) {
2412 fd = mysql_tmpfile("ib");
2413 } else {
2414 fd = mysql_tmpfile_path(path, "ib");
2415 }
2416
2417 if (fd < 0)
2418 return OS_FILE_CLOSED;
2419
2420 /* Copy the file descriptor, so that the additional resources
2421 allocated by create_temp_file() can be freed by invoking
2422 my_close().
2423
2424 Because the file descriptor returned by this function
2425 will be passed to fdopen(), it will be closed by invoking
2426 fclose(), which in turn will invoke close() instead of
2427 my_close(). */
2428
2429#ifdef _WIN32
2430 /* Note that on Windows, the integer returned by mysql_tmpfile
2431 has no relation to C runtime file descriptor. Here, we need
2432 to call my_get_osfhandle to get the HANDLE and then convert it
2433 to C runtime filedescriptor. */
2434
2435 HANDLE hFile = my_get_osfhandle(fd);
2436 HANDLE hDup;
2437 BOOL bOK = DuplicateHandle(
2438 GetCurrentProcess(),
2439 hFile, GetCurrentProcess(),
2440 &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS);
2441 my_close(fd, MYF(MY_WME));
2442
2443 if (!bOK) {
2444 my_osmaperr(GetLastError());
2445 goto error;
2446 }
2447 return hDup;
2448#else
2449 int fd2 = dup(fd);
2450 my_close(fd, MYF(MY_WME));
2451 if (fd2 < 0) {
2452 set_my_errno(errno);
2453 goto error;
2454 }
2455 return fd2;
2456#endif
2457
2458error:
2459 char errbuf[MYSYS_STRERROR_SIZE];
2460
2461 my_error(EE_OUT_OF_FILERESOURCES,
2462 MYF(0),
2463 "ib*", errno,
2464 my_strerror(errbuf, sizeof(errbuf), errno));
2465 return (OS_FILE_CLOSED);
2466}
2467
2468/*********************************************************************//**
2469Wrapper around MySQL's copy_and_convert function.
2470@return number of bytes copied to 'to' */
2471static
2472ulint
2473innobase_convert_string(
2474/*====================*/
2475 void* to, /*!< out: converted string */
2476 ulint to_length, /*!< in: number of bytes reserved
2477 for the converted string */
2478 CHARSET_INFO* to_cs, /*!< in: character set to convert to */
2479 const void* from, /*!< in: string to convert */
2480 ulint from_length, /*!< in: number of bytes to convert */
2481 CHARSET_INFO* from_cs, /*!< in: character set to convert
2482 from */
2483 uint* errors) /*!< out: number of errors encountered
2484 during the conversion */
2485{
2486 return(copy_and_convert(
2487 (char*) to, (uint32) to_length, to_cs,
2488 (const char*) from, (uint32) from_length, from_cs,
2489 errors));
2490}
2491
2492/*******************************************************************//**
2493Formats the raw data in "data" (in InnoDB on-disk format) that is of
2494type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "charset_coll" and writes
2495the result to "buf". The result is converted to "system_charset_info".
2496Not more than "buf_size" bytes are written to "buf".
2497The result is always NUL-terminated (provided buf_size > 0) and the
2498number of bytes that were written to "buf" is returned (including the
2499terminating NUL).
2500@return number of bytes that were written */
2501ulint
2502innobase_raw_format(
2503/*================*/
2504 const char* data, /*!< in: raw data */
2505 ulint data_len, /*!< in: raw data length
2506 in bytes */
2507 ulint charset_coll, /*!< in: charset collation */
2508 char* buf, /*!< out: output buffer */
2509 ulint buf_size) /*!< in: output buffer size
2510 in bytes */
2511{
2512 /* XXX we use a hard limit instead of allocating
2513 but_size bytes from the heap */
2514 CHARSET_INFO* data_cs;
2515 char buf_tmp[8192];
2516 ulint buf_tmp_used;
2517 uint num_errors;
2518
2519 data_cs = all_charsets[charset_coll];
2520
2521 buf_tmp_used = innobase_convert_string(buf_tmp, sizeof(buf_tmp),
2522 system_charset_info,
2523 data, data_len, data_cs,
2524 &num_errors);
2525
2526 return(ut_str_sql_format(buf_tmp, buf_tmp_used, buf, buf_size));
2527}
2528
2529/*********************************************************************//**
2530Compute the next autoinc value.
2531
2532For MySQL replication the autoincrement values can be partitioned among
2533the nodes. The offset is the start or origin of the autoincrement value
2534for a particular node. For n nodes the increment will be n and the offset
2535will be in the interval [1, n]. The formula tries to allocate the next
2536value for a particular node.
2537
2538Note: This function is also called with increment set to the number of
2539values we want to reserve for multi-value inserts e.g.,
2540
2541 INSERT INTO T VALUES(), (), ();
2542
2543innobase_next_autoinc() will be called with increment set to 3 where
2544autoinc_lock_mode != TRADITIONAL because we want to reserve 3 values for
2545the multi-value INSERT above.
2546@return the next value */
2547ulonglong
2548innobase_next_autoinc(
2549/*==================*/
2550 ulonglong current, /*!< in: Current value */
2551 ulonglong need, /*!< in: count of values needed */
2552 ulonglong step, /*!< in: AUTOINC increment step */
2553 ulonglong offset, /*!< in: AUTOINC offset */
2554 ulonglong max_value) /*!< in: max value for type */
2555{
2556 ulonglong next_value;
2557 ulonglong block = need * step;
2558
2559 /* Should never be 0. */
2560 ut_a(need > 0);
2561 ut_a(block > 0);
2562 ut_a(max_value > 0);
2563
2564 /*
2565 Allow auto_increment to go over max_value up to max ulonglong.
2566 This allows us to detect that all values are exhausted.
2567 If we don't do this, we will return max_value several times
2568 and get duplicate key errors instead of auto increment value
2569 out of range.
2570 */
2571 max_value= (~(ulonglong) 0);
2572
2573 /* According to MySQL documentation, if the offset is greater than
2574 the step then the offset is ignored. */
2575 if (offset > block) {
2576 offset = 0;
2577 }
2578
2579 /* Check for overflow. Current can be > max_value if the value is
2580 in reality a negative value.The visual studio compilers converts
2581 large double values automatically into unsigned long long datatype
2582 maximum value */
2583
2584 if (block >= max_value
2585 || offset > max_value
2586 || current >= max_value
2587 || max_value - offset <= offset) {
2588
2589 next_value = max_value;
2590 } else {
2591 ut_a(max_value > current);
2592
2593 ulonglong free = max_value - current;
2594
2595 if (free < offset || free - offset <= block) {
2596 next_value = max_value;
2597 } else {
2598 next_value = 0;
2599 }
2600 }
2601
2602 if (next_value == 0) {
2603 ulonglong next;
2604
2605 if (current >= offset) {
2606 next = (current - offset) / step;
2607 } else {
2608 next = 0;
2609 block -= step;
2610 }
2611
2612 ut_a(max_value > next);
2613 next_value = next * step;
2614 /* Check for multiplication overflow. */
2615 ut_a(next_value >= next);
2616 ut_a(max_value > next_value);
2617
2618 /* Check for overflow */
2619 if (max_value - next_value >= block) {
2620
2621 next_value += block;
2622
2623 if (max_value - next_value >= offset) {
2624 next_value += offset;
2625 } else {
2626 next_value = max_value;
2627 }
2628 } else {
2629 next_value = max_value;
2630 }
2631 }
2632
2633 ut_a(next_value != 0);
2634 ut_a(next_value <= max_value);
2635
2636 return(next_value);
2637}
2638
2639/********************************************************************//**
2640Reset the autoinc value in the table.
2641@return DB_SUCCESS if all went well else error code */
2642UNIV_INTERN
2643dberr_t
2644ha_innobase::innobase_reset_autoinc(
2645/*================================*/
2646 ulonglong autoinc) /*!< in: value to store */
2647{
2648 dberr_t error;
2649
2650 error = innobase_lock_autoinc();
2651
2652 if (error == DB_SUCCESS) {
2653
2654 dict_table_autoinc_initialize(m_prebuilt->table, autoinc);
2655
2656 dict_table_autoinc_unlock(m_prebuilt->table);
2657 }
2658
2659 return(error);
2660}
2661
2662/*******************************************************************//**
2663Reset the auto-increment counter to the given value, i.e. the next row
2664inserted will get the given value. This is called e.g. after TRUNCATE
2665is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is
2666returned by storage engines that don't support this operation.
2667@return 0 or error code */
2668UNIV_INTERN
2669int
2670ha_innobase::reset_auto_increment(
2671/*==============================*/
2672 ulonglong value) /*!< in: new value for table autoinc */
2673{
2674 DBUG_ENTER("ha_innobase::reset_auto_increment");
2675
2676 dberr_t error;
2677
2678 update_thd(ha_thd());
2679
2680 error = row_lock_table_autoinc_for_mysql(m_prebuilt);
2681
2682 if (error != DB_SUCCESS) {
2683 DBUG_RETURN(convert_error_code_to_mysql(
2684 error, m_prebuilt->table->flags, m_user_thd));
2685 }
2686
2687 /* The next value can never be 0. */
2688 if (value == 0) {
2689 value = 1;
2690 }
2691
2692 innobase_reset_autoinc(value);
2693
2694 DBUG_RETURN(0);
2695}
2696
2697/*********************************************************************//**
2698Initializes some fields in an InnoDB transaction object. */
2699static
2700void
2701innobase_trx_init(
2702/*==============*/
2703 THD* thd, /*!< in: user thread handle */
2704 trx_t* trx) /*!< in/out: InnoDB transaction handle */
2705{
2706 DBUG_ENTER("innobase_trx_init");
2707 DBUG_ASSERT(thd == trx->mysql_thd);
2708
2709 trx->check_foreigns = !thd_test_options(
2710 thd, OPTION_NO_FOREIGN_KEY_CHECKS);
2711
2712 trx->check_unique_secondary = !thd_test_options(
2713 thd, OPTION_RELAXED_UNIQUE_CHECKS);
2714
2715 DBUG_VOID_RETURN;
2716}
2717
2718/*********************************************************************//**
2719Allocates an InnoDB transaction for a MySQL handler object for DML.
2720@return InnoDB transaction handle */
2721trx_t*
2722innobase_trx_allocate(
2723/*==================*/
2724 THD* thd) /*!< in: user thread handle */
2725{
2726 trx_t* trx;
2727
2728 DBUG_ENTER("innobase_trx_allocate");
2729 DBUG_ASSERT(thd != NULL);
2730 DBUG_ASSERT(EQ_CURRENT_THD(thd));
2731
2732 trx = trx_create();
2733
2734 trx->mysql_thd = thd;
2735
2736 innobase_trx_init(thd, trx);
2737
2738 DBUG_RETURN(trx);
2739}
2740
2741/*********************************************************************//**
2742Gets the InnoDB transaction handle for a MySQL handler object, creates
2743an InnoDB transaction struct if the corresponding MySQL thread struct still
2744lacks one.
2745@return InnoDB transaction handle */
2746static inline
2747trx_t*
2748check_trx_exists(
2749/*=============*/
2750 THD* thd) /*!< in: user thread handle */
2751{
2752 if (trx_t* trx = thd_to_trx(thd)) {
2753 ut_a(trx->magic_n == TRX_MAGIC_N);
2754 innobase_trx_init(thd, trx);
2755 return trx;
2756 } else {
2757 trx = innobase_trx_allocate(thd);
2758 thd_set_ha_data(thd, innodb_hton_ptr, trx);
2759 return trx;
2760 }
2761}
2762
2763/**
2764 Gets current trx.
2765
2766 This function may be called during InnoDB initialisation, when
2767 innodb_hton_ptr->slot is not yet set to meaningful value.
2768*/
2769
2770trx_t *current_trx()
2771{
2772 THD *thd=current_thd;
2773 if (likely(thd != 0) && innodb_hton_ptr->slot != HA_SLOT_UNDEF) {
2774 return thd_to_trx(thd);
2775 } else {
2776 return(NULL);
2777 }
2778}
2779
2780/*********************************************************************//**
2781Note that a transaction has been registered with MySQL.
2782@return true if transaction is registered with MySQL 2PC coordinator */
2783static inline
2784bool
2785trx_is_registered_for_2pc(
2786/*======================*/
2787 const trx_t* trx) /* in: transaction */
2788{
2789 return(trx->is_registered == 1);
2790}
2791
2792/*********************************************************************//**
2793Note that innobase_commit_ordered() was run. */
2794static inline
2795void
2796trx_set_active_commit_ordered(
2797/*==========================*/
2798 trx_t* trx) /* in: transaction */
2799{
2800 ut_a(trx_is_registered_for_2pc(trx));
2801 trx->active_commit_ordered = 1;
2802}
2803
2804/*********************************************************************//**
2805Note that a transaction has been registered with MySQL 2PC coordinator. */
2806static inline
2807void
2808trx_register_for_2pc(
2809/*==================*/
2810 trx_t* trx) /* in: transaction */
2811{
2812 trx->is_registered = 1;
2813 ut_ad(trx->active_commit_ordered == 0);
2814}
2815
2816/*********************************************************************//**
2817Note that a transaction has been deregistered. */
2818static inline
2819void
2820trx_deregister_from_2pc(
2821/*====================*/
2822 trx_t* trx) /* in: transaction */
2823{
2824 trx->is_registered = 0;
2825 trx->active_commit_ordered = 0;
2826}
2827
2828/*********************************************************************//**
2829Check whether a transaction has active_commit_ordered set */
2830static inline
2831bool
2832trx_is_active_commit_ordered(
2833/*=========================*/
2834 const trx_t* trx) /* in: transaction */
2835{
2836 return(trx->active_commit_ordered == 1);
2837}
2838
2839/*********************************************************************//**
2840Copy table flags from MySQL's HA_CREATE_INFO into an InnoDB table object.
2841Those flags are stored in .frm file and end up in the MySQL table object,
2842but are frequently used inside InnoDB so we keep their copies into the
2843InnoDB table object. */
2844static
2845void
2846innobase_copy_frm_flags_from_create_info(
2847/*=====================================*/
2848 dict_table_t* innodb_table, /*!< in/out: InnoDB table */
2849 const HA_CREATE_INFO* create_info) /*!< in: create info */
2850{
2851 ibool ps_on;
2852 ibool ps_off;
2853
2854 if (innodb_table->is_temporary()
2855 || innodb_table->no_rollback()) {
2856 /* Temp tables do not use persistent stats. */
2857 ps_on = FALSE;
2858 ps_off = TRUE;
2859 } else {
2860 ps_on = create_info->table_options
2861 & HA_OPTION_STATS_PERSISTENT;
2862 ps_off = create_info->table_options
2863 & HA_OPTION_NO_STATS_PERSISTENT;
2864 }
2865
2866 dict_stats_set_persistent(innodb_table, ps_on, ps_off);
2867
2868 dict_stats_auto_recalc_set(
2869 innodb_table,
2870 create_info->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON,
2871 create_info->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF);
2872
2873 innodb_table->stats_sample_pages = create_info->stats_sample_pages;
2874}
2875
2876/*********************************************************************//**
2877Copy table flags from MySQL's TABLE_SHARE into an InnoDB table object.
2878Those flags are stored in .frm file and end up in the MySQL table object,
2879but are frequently used inside InnoDB so we keep their copies into the
2880InnoDB table object. */
2881void
2882innobase_copy_frm_flags_from_table_share(
2883/*=====================================*/
2884 dict_table_t* innodb_table, /*!< in/out: InnoDB table */
2885 const TABLE_SHARE* table_share) /*!< in: table share */
2886{
2887 ibool ps_on;
2888 ibool ps_off;
2889
2890 if (innodb_table->is_temporary()) {
2891 /* Temp tables do not use persistent stats */
2892 ps_on = FALSE;
2893 ps_off = TRUE;
2894 } else {
2895 ps_on = table_share->db_create_options
2896 & HA_OPTION_STATS_PERSISTENT;
2897 ps_off = table_share->db_create_options
2898 & HA_OPTION_NO_STATS_PERSISTENT;
2899 }
2900
2901 dict_stats_set_persistent(innodb_table, ps_on, ps_off);
2902
2903 dict_stats_auto_recalc_set(
2904 innodb_table,
2905 table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON,
2906 table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF);
2907
2908 innodb_table->stats_sample_pages = table_share->stats_sample_pages;
2909}
2910
2911/*********************************************************************//**
2912Construct ha_innobase handler. */
2913
2914ha_innobase::ha_innobase(
2915/*=====================*/
2916 handlerton* hton,
2917 TABLE_SHARE* table_arg)
2918 :handler(hton, table_arg),
2919 m_prebuilt(),
2920 m_prebuilt_ptr(&m_prebuilt),
2921 m_user_thd(),
2922 m_int_table_flags(HA_REC_NOT_IN_SEQ
2923 | HA_NULL_IN_KEY
2924 | HA_CAN_VIRTUAL_COLUMNS
2925 | HA_CAN_INDEX_BLOBS
2926 | HA_CAN_SQL_HANDLER
2927 | HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
2928 | HA_PRIMARY_KEY_REQUIRED_FOR_POSITION
2929 | HA_PRIMARY_KEY_IN_READ_INDEX
2930 | HA_BINLOG_ROW_CAPABLE
2931 | HA_CAN_GEOMETRY
2932 | HA_PARTIAL_COLUMN_READ
2933 | HA_TABLE_SCAN_ON_INDEX
2934 | HA_CAN_FULLTEXT
2935 | HA_CAN_FULLTEXT_EXT
2936 /* JAN: TODO: MySQL 5.7
2937 | HA_CAN_FULLTEXT_HINTS
2938 */
2939 | HA_CAN_EXPORT
2940 | HA_CAN_RTREEKEYS
2941 | HA_CAN_TABLES_WITHOUT_ROLLBACK
2942 | HA_CONCURRENT_OPTIMIZE
2943 | (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0)
2944 ),
2945 m_start_of_scan(),
2946 m_mysql_has_locked()
2947{}
2948
2949/*********************************************************************//**
2950Destruct ha_innobase handler. */
2951
2952ha_innobase::~ha_innobase()
2953/*======================*/
2954{
2955}
2956
2957/*********************************************************************//**
2958Updates the user_thd field in a handle and also allocates a new InnoDB
2959transaction handle if needed, and updates the transaction fields in the
2960m_prebuilt struct. */
2961void
2962ha_innobase::update_thd(
2963/*====================*/
2964 THD* thd) /*!< in: thd to use the handle */
2965{
2966 DBUG_ENTER("ha_innobase::update_thd");
2967 DBUG_PRINT("ha_innobase::update_thd", ("user_thd: %p -> %p",
2968 m_user_thd, thd));
2969
2970 /* The table should have been opened in ha_innobase::open(). */
2971 ut_ad(m_prebuilt->table->n_ref_count > 0);
2972
2973 trx_t* trx = check_trx_exists(thd);
2974
2975 ut_ad(trx->dict_operation_lock_mode == 0);
2976 ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
2977
2978 if (m_prebuilt->trx != trx) {
2979
2980 row_update_prebuilt_trx(m_prebuilt, trx);
2981 }
2982
2983 m_user_thd = thd;
2984
2985 DBUG_ASSERT(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
2986 DBUG_ASSERT(m_prebuilt->trx == thd_to_trx(m_user_thd));
2987
2988 DBUG_VOID_RETURN;
2989}
2990
2991/*********************************************************************//**
2992Updates the user_thd field in a handle and also allocates a new InnoDB
2993transaction handle if needed, and updates the transaction fields in the
2994m_prebuilt struct. */
2995
2996void
2997ha_innobase::update_thd()
2998/*=====================*/
2999{
3000 THD* thd = ha_thd();
3001
3002 ut_ad(EQ_CURRENT_THD(thd));
3003 update_thd(thd);
3004}
3005
3006/*********************************************************************//**
3007Registers an InnoDB transaction with the MySQL 2PC coordinator, so that
3008the MySQL XA code knows to call the InnoDB prepare and commit, or rollback
3009for the transaction. This MUST be called for every transaction for which
3010the user may call commit or rollback. Calling this several times to register
3011the same transaction is allowed, too. This function also registers the
3012current SQL statement. */
3013static inline
3014void
3015innobase_register_trx(
3016/*==================*/
3017 handlerton* hton, /* in: Innobase handlerton */
3018 THD* thd, /* in: MySQL thd (connection) object */
3019 trx_t* trx) /* in: transaction to register */
3020{
3021 /* JAN: TODO: MySQL 5.7 PSI
3022 const ulonglong trx_id = static_cast<const ulonglong>(
3023 trx_get_id_for_print(trx));
3024
3025 trans_register_ha(thd, FALSE, hton, &trx_id);
3026 */
3027 trans_register_ha(thd, FALSE, hton);
3028
3029 if (!trx_is_registered_for_2pc(trx)
3030 && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
3031
3032 //trans_register_ha(thd, TRUE, hton, &trx_id);
3033 trans_register_ha(thd, TRUE, hton);
3034 }
3035
3036 trx_register_for_2pc(trx);
3037}
3038
3039/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
3040 ------------------------------------------------------------
3041
30421) The use of the query cache for TBL is disabled when there is an
3043uncommitted change to TBL.
3044
30452) When a change to TBL commits, InnoDB stores the current value of
3046its global trx id counter, let us denote it by INV_TRX_ID, to the table object
3047in the InnoDB data dictionary, and does only allow such transactions whose
3048id <= INV_TRX_ID to use the query cache.
3049
30503) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit
3051modification because an ON DELETE CASCADE, we invalidate the MySQL query cache
3052of TBL immediately.
3053
3054How this is implemented inside InnoDB:
3055
30561) Since every modification always sets an IX type table lock on the InnoDB
3057table, it is easy to check if there can be uncommitted modifications for a
3058table: just check if there are locks in the lock list of the table.
3059
30602) When a transaction inside InnoDB commits, it reads the global trx id
3061counter and stores the value INV_TRX_ID to the tables on which it had a lock.
3062
30633) If there is an implicit table change from ON DELETE CASCADE or SET NULL,
3064InnoDB calls an invalidate method for the MySQL query cache for that table.
3065
3066How this is implemented inside sql_cache.cc:
3067
30681) The query cache for an InnoDB table TBL is invalidated immediately at an
3069INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay
3070invalidation to the transaction commit.
3071
30722) To store or retrieve a value from the query cache of an InnoDB table TBL,
3073any query must first ask InnoDB's permission. We must pass the thd as a
3074parameter because InnoDB will look at the trx id, if any, associated with
3075that thd. Also the full_name which is used as key to search for the table
3076object. The full_name is a string containing the normalized path to the
3077table in the canonical format.
3078
30793) Use of the query cache for InnoDB tables is now allowed also when
3080AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer
3081put restrictions on the use of the query cache.
3082*/
3083
3084/******************************************************************//**
3085The MySQL query cache uses this to check from InnoDB if the query cache at
3086the moment is allowed to operate on an InnoDB table. The SQL query must
3087be a non-locking SELECT.
3088
3089The query cache is allowed to operate on certain query only if this function
3090returns TRUE for all tables in the query.
3091
3092If thd is not in the autocommit state, this function also starts a new
3093transaction for thd if there is no active trx yet, and assigns a consistent
3094read view to it if there is no read view yet.
3095
3096Why a deadlock of threads is not possible: the query cache calls this function
3097at the start of a SELECT processing. Then the calling thread cannot be
3098holding any InnoDB semaphores. The calling thread is holding the
3099query cache mutex, and this function will reserve the InnoDB trx_sys.mutex.
3100Thus, the 'rank' in sync0mutex.h of the MySQL query cache mutex is above
3101the InnoDB trx_sys.mutex.
3102@return TRUE if permitted, FALSE if not; note that the value FALSE
3103does not mean we should invalidate the query cache: invalidation is
3104called explicitly */
3105static
3106my_bool
3107innobase_query_caching_of_table_permitted(
3108/*======================================*/
3109 THD* thd, /*!< in: thd of the user who is trying to
3110 store a result to the query cache or
3111 retrieve it */
3112 const char* full_name, /*!< in: normalized path to the table */
3113 uint full_name_len, /*!< in: length of the normalized path
3114 to the table */
3115 ulonglong *)
3116{
3117 char norm_name[1000];
3118 trx_t* trx = check_trx_exists(thd);
3119
3120 ut_a(full_name_len < 999);
3121
3122 if (trx->isolation_level == TRX_ISO_SERIALIZABLE) {
3123 /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
3124 plain SELECT if AUTOCOMMIT is not on. */
3125
3126 return(false);
3127 }
3128
3129 innobase_srv_conc_force_exit_innodb(trx);
3130
3131 if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)
3132 && trx->n_mysql_tables_in_use == 0) {
3133 /* We are going to retrieve the query result from the query
3134 cache. This cannot be a store operation to the query cache
3135 because then MySQL would have locks on tables already.
3136
3137 TODO: if the user has used LOCK TABLES to lock the table,
3138 then we open a transaction in the call of row_.. below.
3139 That trx can stay open until UNLOCK TABLES. The same problem
3140 exists even if we do not use the query cache. MySQL should be
3141 modified so that it ALWAYS calls some cleanup function when
3142 the processing of a query ends!
3143
3144 We can imagine we instantaneously serialize this consistent
3145 read trx to the current trx id counter. If trx2 would have
3146 changed the tables of a query result stored in the cache, and
3147 trx2 would have already committed, making the result obsolete,
3148 then trx2 would have already invalidated the cache. Thus we
3149 can trust the result in the cache is ok for this query. */
3150
3151 return(true);
3152 }
3153
3154 /* Normalize the table name to InnoDB format */
3155 normalize_table_name(norm_name, full_name);
3156
3157 innobase_register_trx(innodb_hton_ptr, thd, trx);
3158
3159 return(row_search_check_if_query_cache_permitted(trx, norm_name));
3160}
3161
3162/*****************************************************************//**
3163Invalidates the MySQL query cache for the table. */
3164void
3165innobase_invalidate_query_cache(
3166/*============================*/
3167 trx_t* trx, /*!< in: transaction which
3168 modifies the table */
3169 const char* full_name) /*!< in: concatenation of
3170 database name, path separator,
3171 table name, null char NUL;
3172 NOTE that in Windows this is
3173 always in LOWER CASE! */
3174{
3175 /* Note that the sync0mutex.h rank of the query cache mutex is just
3176 above the InnoDB trx_sys_t->lock. The caller of this function must
3177 not have latches of a lower rank. */
3178
3179#ifdef HAVE_QUERY_CACHE
3180 char qcache_key_name[2 * (NAME_LEN + 1)];
3181 char db_name[NAME_CHAR_LEN * MY_CS_MBMAXLEN + 1];
3182 const char *key_ptr;
3183 size_t tabname_len;
3184
3185 // Extract the database name.
3186 key_ptr= strchr(full_name, '/');
3187 DBUG_ASSERT(key_ptr != NULL); // Database name should be present
3188 size_t dbname_len= size_t(key_ptr - full_name);
3189 memcpy(db_name, full_name, dbname_len);
3190 db_name[dbname_len]= '\0';
3191
3192 /* Construct the key("db-name\0table$name\0") for the query cache using
3193 the path name("db@002dname\0table@0024name\0") of the table in its
3194 canonical form. */
3195 dbname_len = filename_to_tablename(db_name, qcache_key_name,
3196 sizeof(qcache_key_name));
3197 tabname_len = filename_to_tablename(++key_ptr,
3198 (qcache_key_name + dbname_len + 1),
3199 sizeof(qcache_key_name) -
3200 dbname_len - 1);
3201
3202 /* Argument TRUE below means we are using transactions */
3203 mysql_query_cache_invalidate4(trx->mysql_thd,
3204 qcache_key_name,
3205 uint(dbname_len + tabname_len + 2),
3206 TRUE);
3207#endif
3208}
3209
3210/** Quote a standard SQL identifier like index or column name.
3211@param[in] file output stream
3212@param[in] trx InnoDB transaction, or NULL
3213@param[in] id identifier to quote */
3214void
3215innobase_quote_identifier(
3216 FILE* file,
3217 trx_t* trx,
3218 const char* id)
3219{
3220 const int q = trx != NULL && trx->mysql_thd != NULL
3221 ? get_quote_char_for_identifier(trx->mysql_thd, id, strlen(id))
3222 : '`';
3223
3224 if (q == EOF) {
3225 fputs(id, file);
3226 } else {
3227 putc(q, file);
3228
3229 while (int c = *id++) {
3230 if (c == q) {
3231 putc(c, file);
3232 }
3233 putc(c, file);
3234 }
3235
3236 putc(q, file);
3237 }
3238}
3239
3240/** Quote a standard SQL identifier like tablespace, index or column name.
3241@param[in] trx InnoDB transaction, or NULL
3242@param[in] id identifier to quote
3243@return quoted identifier */
3244std::string
3245innobase_quote_identifier(
3246/*======================*/
3247 trx_t* trx,
3248 const char* id)
3249{
3250 std::string quoted_identifier;
3251 const int q = trx != NULL && trx->mysql_thd != NULL
3252 ? get_quote_char_for_identifier(trx->mysql_thd, id, strlen(id))
3253 : '`';
3254
3255 if (q == EOF) {
3256 quoted_identifier.append(id);
3257 } else {
3258 quoted_identifier += char(q);
3259 quoted_identifier.append(id);
3260 quoted_identifier += char(q);
3261 }
3262
3263 return (quoted_identifier);
3264}
3265
3266/** Convert a table name to the MySQL system_charset_info (UTF-8)
3267and quote it.
3268@param[out] buf buffer for converted identifier
3269@param[in] buflen length of buf, in bytes
3270@param[in] id identifier to convert
3271@param[in] idlen length of id, in bytes
3272@param[in] thd MySQL connection thread, or NULL
3273@return pointer to the end of buf */
3274static
3275char*
3276innobase_convert_identifier(
3277 char* buf,
3278 ulint buflen,
3279 const char* id,
3280 ulint idlen,
3281 THD* thd)
3282{
3283 const char* s = id;
3284
3285 char nz[MAX_TABLE_NAME_LEN + 1];
3286 char nz2[MAX_TABLE_NAME_LEN + 1];
3287
3288 /* Decode the table name. The MySQL function expects
3289 a NUL-terminated string. The input and output strings
3290 buffers must not be shared. */
3291 ut_a(idlen <= MAX_TABLE_NAME_LEN);
3292 memcpy(nz, id, idlen);
3293 nz[idlen] = 0;
3294
3295 s = nz2;
3296 idlen = explain_filename(thd, nz, nz2, sizeof nz2,
3297 EXPLAIN_PARTITIONS_AS_COMMENT);
3298 if (idlen > buflen) {
3299 idlen = buflen;
3300 }
3301 memcpy(buf, s, idlen);
3302 return(buf + idlen);
3303}
3304
3305/*****************************************************************//**
3306Convert a table name to the MySQL system_charset_info (UTF-8).
3307@return pointer to the end of buf */
3308char*
3309innobase_convert_name(
3310/*==================*/
3311 char* buf, /*!< out: buffer for converted identifier */
3312 ulint buflen, /*!< in: length of buf, in bytes */
3313 const char* id, /*!< in: table name to convert */
3314 ulint idlen, /*!< in: length of id, in bytes */
3315 THD* thd) /*!< in: MySQL connection thread, or NULL */
3316{
3317 char* s = buf;
3318 const char* bufend = buf + buflen;
3319
3320 const char* slash = (const char*) memchr(id, '/', idlen);
3321
3322 if (slash == NULL) {
3323 return(innobase_convert_identifier(
3324 buf, buflen, id, idlen, thd));
3325 }
3326
3327 /* Print the database name and table name separately. */
3328 s = innobase_convert_identifier(s, ulint(bufend - s),
3329 id, ulint(slash - id), thd);
3330 if (s < bufend) {
3331 *s++ = '.';
3332 s = innobase_convert_identifier(s, ulint(bufend - s),
3333 slash + 1, idlen
3334 - ulint(slash - id) - 1,
3335 thd);
3336 }
3337
3338 return(s);
3339}
3340
3341/*****************************************************************//**
3342A wrapper function of innobase_convert_name(), convert a table name
3343to the MySQL system_charset_info (UTF-8) and quote it if needed.
3344@return pointer to the end of buf */
3345void
3346innobase_format_name(
3347/*==================*/
3348 char* buf, /*!< out: buffer for converted identifier */
3349 ulint buflen, /*!< in: length of buf, in bytes */
3350 const char* name) /*!< in: table name to format */
3351{
3352 const char* bufend;
3353
3354 bufend = innobase_convert_name(buf, buflen, name, strlen(name), NULL);
3355
3356 ut_ad((ulint) (bufend - buf) < buflen);
3357
3358 buf[bufend - buf] = '\0';
3359}
3360
3361/**********************************************************************//**
3362Determines if the currently running transaction has been interrupted.
3363@return true if interrupted */
3364bool
3365trx_is_interrupted(
3366/*===============*/
3367 const trx_t* trx) /*!< in: transaction */
3368{
3369 return(trx && trx->mysql_thd && thd_kill_level(trx->mysql_thd));
3370}
3371
3372/**********************************************************************//**
3373Determines if the currently running transaction is in strict mode.
3374@return TRUE if strict */
3375ibool
3376trx_is_strict(
3377/*==========*/
3378 trx_t* trx) /*!< in: transaction */
3379{
3380 return(trx && trx->mysql_thd && THDVAR(trx->mysql_thd, strict_mode));
3381}
3382
3383/**************************************************************//**
3384Resets some fields of a m_prebuilt struct. The template is used in fast
3385retrieval of just those column values MySQL needs in its processing. */
3386void
3387ha_innobase::reset_template(void)
3388/*=============================*/
3389{
3390 ut_ad(m_prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
3391 ut_ad(m_prebuilt->magic_n2 == m_prebuilt->magic_n);
3392
3393 /* Force table to be freed in close_thread_table(). */
3394 DBUG_EXECUTE_IF("free_table_in_fts_query",
3395 if (m_prebuilt->in_fts_query) {
3396 table->m_needs_reopen = true;
3397 }
3398 );
3399
3400 m_prebuilt->keep_other_fields_on_keyread = false;
3401 m_prebuilt->read_just_key = 0;
3402 m_prebuilt->in_fts_query = 0;
3403
3404 /* Reset index condition pushdown state. */
3405 if (m_prebuilt->idx_cond) {
3406 m_prebuilt->idx_cond = NULL;
3407 m_prebuilt->idx_cond_n_cols = 0;
3408 /* Invalidate m_prebuilt->mysql_template
3409 in ha_innobase::write_row(). */
3410 m_prebuilt->template_type = ROW_MYSQL_NO_TEMPLATE;
3411 }
3412}
3413
3414/*****************************************************************//**
3415Call this when you have opened a new table handle in HANDLER, before you
3416call index_read_map() etc. Actually, we can let the cursor stay open even
3417over a transaction commit! Then you should call this before every operation,
3418fetch next etc. This function inits the necessary things even after a
3419transaction commit. */
3420
3421void
3422ha_innobase::init_table_handle_for_HANDLER(void)
3423/*============================================*/
3424{
3425 /* If current thd does not yet have a trx struct, create one.
3426 If the current handle does not yet have a m_prebuilt struct, create
3427 one. Update the trx pointers in the m_prebuilt struct. Normally
3428 this operation is done in external_lock. */
3429
3430 update_thd(ha_thd());
3431
3432 /* Initialize the m_prebuilt struct much like it would be inited in
3433 external_lock */
3434
3435 innobase_srv_conc_force_exit_innodb(m_prebuilt->trx);
3436
3437 /* If the transaction is not started yet, start it */
3438
3439 trx_start_if_not_started_xa(m_prebuilt->trx, false);
3440
3441 /* Assign a read view if the transaction does not have it yet */
3442
3443 m_prebuilt->trx->read_view.open(m_prebuilt->trx);
3444
3445 innobase_register_trx(ht, m_user_thd, m_prebuilt->trx);
3446
3447 /* We did the necessary inits in this function, no need to repeat them
3448 in row_search_for_mysql */
3449
3450 m_prebuilt->sql_stat_start = FALSE;
3451
3452 /* We let HANDLER always to do the reads as consistent reads, even
3453 if the trx isolation level would have been specified as SERIALIZABLE */
3454
3455 m_prebuilt->select_lock_type = LOCK_NONE;
3456 m_prebuilt->stored_select_lock_type = LOCK_NONE;
3457
3458 /* Always fetch all columns in the index record */
3459
3460 m_prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
3461
3462 /* We want always to fetch all columns in the whole row? Or do
3463 we???? */
3464
3465 m_prebuilt->used_in_HANDLER = TRUE;
3466
3467 reset_template();
3468}
3469
3470/*********************************************************************//**
3471Free tablespace resources allocated. */
3472static
3473void
3474innobase_space_shutdown()
3475/*=====================*/
3476{
3477 DBUG_ENTER("innobase_space_shutdown");
3478
3479 if (fil_system.temp_space) {
3480 fil_system.temp_space->close();
3481 }
3482
3483 srv_sys_space.shutdown();
3484 if (srv_tmp_space.get_sanity_check_status()) {
3485 srv_tmp_space.delete_files();
3486 }
3487 srv_tmp_space.shutdown();
3488
3489#ifdef WITH_INNODB_DISALLOW_WRITES
3490 os_event_destroy(srv_allow_writes_event);
3491#endif /* WITH_INNODB_DISALLOW_WRITES */
3492
3493 DBUG_VOID_RETURN;
3494}
3495
3496/** Free any resources that were allocated and return failure.
3497@return always return 1 */
3498static int innodb_init_abort()
3499{
3500 DBUG_ENTER("innodb_init_abort");
3501 innobase_space_shutdown();
3502 DBUG_RETURN(1);
3503}
3504
3505/** Update log_checksum_algorithm_ptr with a pointer to the function
3506corresponding to whether checksums are enabled.
3507@param[in,out] thd client session, or NULL if at startup
3508@param[in] check whether redo log block checksums are enabled
3509@return whether redo log block checksums are enabled */
3510static inline
3511bool
3512innodb_log_checksums_func_update(THD* thd, bool check)
3513{
3514 static const char msg[] = "innodb_encrypt_log implies"
3515 " innodb_log_checksums";
3516
3517 ut_ad(!thd == !srv_was_started);
3518
3519 if (!check) {
3520 check = srv_encrypt_log;
3521 if (!check) {
3522 } else if (thd) {
3523 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
3524 HA_ERR_UNSUPPORTED, msg);
3525 } else {
3526 sql_print_warning(msg);
3527 }
3528 }
3529
3530 if (thd) {
3531 log_mutex_enter();
3532 log_checksum_algorithm_ptr = check
3533 ? log_block_calc_checksum_crc32
3534 : log_block_calc_checksum_none;
3535 log_mutex_exit();
3536 } else {
3537 log_checksum_algorithm_ptr = check
3538 ? log_block_calc_checksum_crc32
3539 : log_block_calc_checksum_none;
3540 }
3541
3542 return(check);
3543}
3544
3545/****************************************************************//**
3546Gives the file extension of an InnoDB single-table tablespace. */
3547static const char* ha_innobase_exts[] = {
3548 dot_ext[IBD],
3549 dot_ext[ISL],
3550 NullS
3551};
3552
3553/** Determine if system-versioned data was modified by the transaction.
3554@param[in,out] thd current session
3555@param[out] trx_id transaction start ID
3556@return transaction commit ID
3557@retval 0 if no system-versioned data was affected by the transaction */
3558static ulonglong innodb_prepare_commit_versioned(THD* thd, ulonglong *trx_id)
3559{
3560 if (const trx_t* trx = thd_to_trx(thd)) {
3561 *trx_id = trx->id;
3562
3563 for (trx_mod_tables_t::const_iterator t
3564 = trx->mod_tables.begin();
3565 t != trx->mod_tables.end(); t++) {
3566 if (t->second.is_versioned()) {
3567 DBUG_ASSERT(t->first->versioned_by_id());
3568 DBUG_ASSERT(trx->rsegs.m_redo.rseg);
3569
3570 return trx_sys.get_new_trx_id();
3571 }
3572 }
3573
3574 return 0;
3575 }
3576
3577 *trx_id = 0;
3578 return 0;
3579}
3580
3581/** Initialize and normalize innodb_buffer_pool_size. */
3582static void innodb_buffer_pool_size_init()
3583{
3584 if (srv_buf_pool_size >= BUF_POOL_SIZE_THRESHOLD) {
3585
3586 if (srv_buf_pool_instances == srv_buf_pool_instances_default) {
3587#if defined(_WIN32) && !defined(_WIN64)
3588 /* Do not allocate too large of a buffer pool on
3589 Windows 32-bit systems, which can have trouble
3590 allocating larger single contiguous memory blocks. */
3591 srv_buf_pool_size = ulint(
3592 ut_uint64_align_up(srv_buf_pool_size,
3593 srv_buf_pool_chunk_unit));
3594 srv_buf_pool_instances = std::min<ulong>(
3595 MAX_BUFFER_POOLS,
3596 ulong(srv_buf_pool_size
3597 / srv_buf_pool_chunk_unit));
3598#else /* defined(_WIN32) && !defined(_WIN64) */
3599 /* Default to 8 instances when size > 1GB. */
3600 srv_buf_pool_instances = 8;
3601#endif /* defined(_WIN32) && !defined(_WIN64) */
3602 }
3603 } else {
3604 /* If buffer pool is less than 1 GiB, assume fewer
3605 threads. Also use only one buffer pool instance. */
3606 if (srv_buf_pool_instances != srv_buf_pool_instances_default
3607 && srv_buf_pool_instances != 1) {
3608 /* We can't distinguish whether the user has explicitly
3609 started mysqld with --innodb-buffer-pool-instances=0,
3610 (srv_buf_pool_instances_default is 0) or has not
3611 specified that option at all. Thus we have the
3612 limitation that if the user started with =0, we
3613 will not emit a warning here, but we should actually
3614 do so. */
3615 ib::info()
3616 << "Adjusting innodb_buffer_pool_instances"
3617 " from " << srv_buf_pool_instances << " to 1"
3618 " since innodb_buffer_pool_size is less than "
3619 << BUF_POOL_SIZE_THRESHOLD / (1024 * 1024)
3620 << " MiB";
3621 }
3622
3623 srv_buf_pool_instances = 1;
3624 }
3625
3626 if (srv_buf_pool_chunk_unit * srv_buf_pool_instances
3627 > srv_buf_pool_size) {
3628 /* Size unit of buffer pool is larger than srv_buf_pool_size.
3629 adjust srv_buf_pool_chunk_unit for srv_buf_pool_size. */
3630 srv_buf_pool_chunk_unit
3631 = static_cast<ulong>(srv_buf_pool_size)
3632 / srv_buf_pool_instances;
3633 if (srv_buf_pool_size % srv_buf_pool_instances != 0) {
3634 ++srv_buf_pool_chunk_unit;
3635 }
3636 }
3637
3638 srv_buf_pool_size = buf_pool_size_align(srv_buf_pool_size);
3639 innobase_buffer_pool_size = srv_buf_pool_size;
3640}
3641
3642/** Initialize, validate and normalize the InnoDB startup parameters.
3643@return failure code
3644@retval 0 on success
3645@retval HA_ERR_OUT_OF_MEM when out of memory
3646@retval HA_ERR_INITIALIZATION when some parameters are out of range */
3647static int innodb_init_params()
3648{
3649 DBUG_ENTER("innodb_init_params");
3650
3651 static char current_dir[3];
3652 char *default_path;
3653 ulong num_pll_degree;
3654
3655 /* Check that values don't overflow on 32-bit systems. */
3656 if (sizeof(ulint) == 4) {
3657 if (innobase_buffer_pool_size > UINT_MAX32) {
3658 sql_print_error(
3659 "innodb_buffer_pool_size can't be over 4GB"
3660 " on 32-bit systems");
3661 DBUG_RETURN(HA_ERR_OUT_OF_MEM);
3662 }
3663 }
3664
3665 /* The buffer pool needs to be able to accommodate enough many
3666 pages, even for larger pages */
3667 if (srv_page_size > UNIV_PAGE_SIZE_DEF
3668 && innobase_buffer_pool_size < (24 * 1024 * 1024)) {
3669 ib::info() << "innodb_page_size="
3670 << srv_page_size << " requires "
3671 << "innodb_buffer_pool_size > 24M current "
3672 << innobase_buffer_pool_size;
3673 DBUG_RETURN(HA_ERR_INITIALIZATION);
3674 }
3675
3676#ifdef WITH_WSREP
3677 /* Currently, Galera does not support VATS lock schedule algorithm. */
3678 if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
3679 && global_system_variables.wsrep_on) {
3680 ib::info() << "For Galera, using innodb_lock_schedule_algorithm=fcfs";
3681 innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS;
3682 }
3683#endif /* WITH_WSREP */
3684
3685#ifndef HAVE_LZ4
3686 if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) {
3687 sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
3688 "InnoDB: liblz4 is not installed. \n",
3689 innodb_compression_algorithm);
3690 DBUG_RETURN(HA_ERR_INITIALIZATION);
3691 }
3692#endif
3693
3694#ifndef HAVE_LZO
3695 if (innodb_compression_algorithm == PAGE_LZO_ALGORITHM) {
3696 sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
3697 "InnoDB: liblzo is not installed. \n",
3698 innodb_compression_algorithm);
3699 DBUG_RETURN(HA_ERR_INITIALIZATION);
3700 }
3701#endif
3702
3703#ifndef HAVE_LZMA
3704 if (innodb_compression_algorithm == PAGE_LZMA_ALGORITHM) {
3705 sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
3706 "InnoDB: liblzma is not installed. \n",
3707 innodb_compression_algorithm);
3708 DBUG_RETURN(HA_ERR_INITIALIZATION);
3709 }
3710#endif
3711
3712#ifndef HAVE_BZIP2
3713 if (innodb_compression_algorithm == PAGE_BZIP2_ALGORITHM) {
3714 sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
3715 "InnoDB: libbz2 is not installed. \n",
3716 innodb_compression_algorithm);
3717 DBUG_RETURN(HA_ERR_INITIALIZATION);
3718 }
3719#endif
3720
3721#ifndef HAVE_SNAPPY
3722 if (innodb_compression_algorithm == PAGE_SNAPPY_ALGORITHM) {
3723 sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
3724 "InnoDB: libsnappy is not installed. \n",
3725 innodb_compression_algorithm);
3726 DBUG_RETURN(HA_ERR_INITIALIZATION);
3727 }
3728#endif
3729
3730 if ((srv_encrypt_tables || srv_encrypt_log)
3731 && !encryption_key_id_exists(FIL_DEFAULT_ENCRYPTION_KEY)) {
3732 sql_print_error("InnoDB: cannot enable encryption, "
3733 "encryption plugin is not available");
3734 DBUG_RETURN(HA_ERR_INITIALIZATION);
3735 }
3736
3737#ifdef _WIN32
3738 if (!is_filename_allowed(srv_buf_dump_filename,
3739 strlen(srv_buf_dump_filename), FALSE)) {
3740 sql_print_error("InnoDB: innodb_buffer_pool_filename"
3741 " cannot have colon (:) in the file name.");
3742 DBUG_RETURN(HA_ERR_INITIALIZATION);
3743 }
3744#endif
3745
3746 /* First calculate the default path for innodb_data_home_dir etc.,
3747 in case the user has not given any value.
3748
3749 Note that when using the embedded server, the datadirectory is not
3750 necessarily the current directory of this program. */
3751
3752 if (mysqld_embedded) {
3753 default_path = mysql_real_data_home;
3754 } else {
3755 /* It's better to use current lib, to keep paths short */
3756 current_dir[0] = FN_CURLIB;
3757 current_dir[1] = FN_LIBCHAR;
3758 current_dir[2] = 0;
3759 default_path = current_dir;
3760 }
3761
3762 ut_a(default_path);
3763
3764 fil_path_to_mysql_datadir = default_path;
3765
3766 /* Set InnoDB initialization parameters according to the values
3767 read from MySQL .cnf file */
3768
3769 /* The default dir for data files is the datadir of MySQL */
3770
3771 srv_data_home = innobase_data_home_dir
3772 ? innobase_data_home_dir : default_path;
3773
3774 /*--------------- Shared tablespaces -------------------------*/
3775
3776 /* Check that the value of system variable innodb_page_size was
3777 set correctly. Its value was put into srv_page_size. If valid,
3778 return the associated srv_page_size_shift. */
3779 srv_page_size_shift = innodb_page_size_validate(srv_page_size);
3780 if (!srv_page_size_shift) {
3781 sql_print_error("InnoDB: Invalid page size=%lu.\n",
3782 srv_page_size);
3783 DBUG_RETURN(HA_ERR_INITIALIZATION);
3784 }
3785
3786 /* This is the first time univ_page_size is used.
3787 It was initialized to 16k pages before srv_page_size was set */
3788 univ_page_size.copy_from(
3789 page_size_t(srv_page_size, srv_page_size, false));
3790
3791 srv_sys_space.set_space_id(TRX_SYS_SPACE);
3792 srv_sys_space.set_flags(FSP_FLAGS_PAGE_SSIZE());
3793 srv_sys_space.set_name("innodb_system");
3794 srv_sys_space.set_path(srv_data_home);
3795
3796 /* Supports raw devices */
3797 if (!srv_sys_space.parse_params(innobase_data_file_path, true)) {
3798 ib::error() << "Unable to parse innodb_data_file_path="
3799 << innobase_data_file_path;
3800 DBUG_RETURN(HA_ERR_INITIALIZATION);
3801 }
3802
3803 srv_tmp_space.set_name("innodb_temporary");
3804 srv_tmp_space.set_path(srv_data_home);
3805 srv_tmp_space.set_flags(FSP_FLAGS_PAGE_SSIZE());
3806
3807 if (!srv_tmp_space.parse_params(innobase_temp_data_file_path, false)) {
3808 ib::error() << "Unable to parse innodb_temp_data_file_path="
3809 << innobase_temp_data_file_path;
3810 DBUG_RETURN(HA_ERR_INITIALIZATION);
3811 }
3812
3813 /* Perform all sanity check before we take action of deleting files*/
3814 if (srv_sys_space.intersection(&srv_tmp_space)) {
3815 sql_print_error("%s and %s file names seem to be the same.",
3816 srv_tmp_space.name(), srv_sys_space.name());
3817 DBUG_RETURN(HA_ERR_INITIALIZATION);
3818 }
3819
3820 srv_sys_space.normalize_size();
3821 srv_tmp_space.normalize_size();
3822
3823 /* ------------ UNDO tablespaces files ---------------------*/
3824 if (!srv_undo_dir) {
3825 srv_undo_dir = default_path;
3826 }
3827
3828 os_normalize_path(srv_undo_dir);
3829
3830 if (strchr(srv_undo_dir, ';')) {
3831 sql_print_error("syntax error in innodb_undo_directory");
3832 DBUG_RETURN(HA_ERR_INITIALIZATION);
3833 }
3834
3835 /* -------------- All log files ---------------------------*/
3836
3837 /* The default dir for log files is the datadir of MySQL */
3838
3839 if (!srv_log_group_home_dir) {
3840 srv_log_group_home_dir = default_path;
3841 }
3842
3843 os_normalize_path(srv_log_group_home_dir);
3844
3845 if (strchr(srv_log_group_home_dir, ';')) {
3846 sql_print_error("syntax error in innodb_log_group_home_dir");
3847 DBUG_RETURN(HA_ERR_INITIALIZATION);
3848 }
3849
3850 if (srv_n_log_files * srv_log_file_size
3851 >= 512ULL * 1024ULL * 1024ULL * 1024ULL) {
3852 /* log_block_convert_lsn_to_no() limits the returned block
3853 number to 1G and given that OS_FILE_LOG_BLOCK_SIZE is 512
3854 bytes, then we have a limit of 512 GB. If that limit is to
3855 be raised, then log_block_convert_lsn_to_no() must be
3856 modified. */
3857 ib::error() << "Combined size of log files must be < 512 GB";
3858 DBUG_RETURN(HA_ERR_INITIALIZATION);
3859 }
3860
3861 DBUG_ASSERT(innodb_change_buffering <= IBUF_USE_ALL);
3862 ibuf_use = ibuf_use_t(innodb_change_buffering);
3863
3864 /* Check that interdependent parameters have sane values. */
3865 if (srv_max_buf_pool_modified_pct < srv_max_dirty_pages_pct_lwm) {
3866 sql_print_warning("InnoDB: innodb_max_dirty_pages_pct_lwm"
3867 " cannot be set higher than"
3868 " innodb_max_dirty_pages_pct.\n"
3869 "InnoDB: Setting"
3870 " innodb_max_dirty_pages_pct_lwm to %lf\n",
3871 srv_max_buf_pool_modified_pct);
3872
3873 srv_max_dirty_pages_pct_lwm = srv_max_buf_pool_modified_pct;
3874 }
3875
3876 if (srv_max_io_capacity == SRV_MAX_IO_CAPACITY_DUMMY_DEFAULT) {
3877
3878 if (srv_io_capacity >= SRV_MAX_IO_CAPACITY_LIMIT / 2) {
3879 /* Avoid overflow. */
3880 srv_max_io_capacity = SRV_MAX_IO_CAPACITY_LIMIT;
3881 } else {
3882 /* The user has not set the value. We should
3883 set it based on innodb_io_capacity. */
3884 srv_max_io_capacity =
3885 ut_max(2 * srv_io_capacity, 2000UL);
3886 }
3887
3888 } else if (srv_max_io_capacity < srv_io_capacity) {
3889 sql_print_warning("InnoDB: innodb_io_capacity"
3890 " cannot be set higher than"
3891 " innodb_io_capacity_max."
3892 "Setting innodb_io_capacity=%lu",
3893 srv_max_io_capacity);
3894
3895 srv_io_capacity = srv_max_io_capacity;
3896 }
3897
3898 if (UNIV_PAGE_SIZE_DEF != srv_page_size) {
3899 ib::info() << "innodb_page_size=" << srv_page_size;
3900
3901 srv_max_undo_log_size = std::max(
3902 srv_max_undo_log_size,
3903 ulonglong(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)
3904 << srv_page_size_shift);
3905 }
3906
3907 if (srv_log_write_ahead_size > srv_page_size) {
3908 srv_log_write_ahead_size = srv_page_size;
3909 } else {
3910 ulong srv_log_write_ahead_size_tmp = OS_FILE_LOG_BLOCK_SIZE;
3911
3912 while (srv_log_write_ahead_size_tmp
3913 < srv_log_write_ahead_size) {
3914 srv_log_write_ahead_size_tmp
3915 = srv_log_write_ahead_size_tmp * 2;
3916 }
3917 if (srv_log_write_ahead_size_tmp
3918 != srv_log_write_ahead_size) {
3919 srv_log_write_ahead_size
3920 = srv_log_write_ahead_size_tmp / 2;
3921 }
3922 }
3923
3924 srv_buf_pool_size = ulint(innobase_buffer_pool_size);
3925
3926 if (!innobase_use_checksums) {
3927 ib::warn() << "Setting innodb_checksums to OFF is DEPRECATED."
3928 " This option may be removed in future releases. You"
3929 " should set innodb_checksum_algorithm=NONE instead.";
3930 srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_NONE;
3931 }
3932
3933 innodb_log_checksums = innodb_log_checksums_func_update(
3934 NULL, innodb_log_checksums);
3935
3936#ifdef HAVE_LINUX_LARGE_PAGES
3937 if ((os_use_large_pages = my_use_large_pages)) {
3938 os_large_page_size = opt_large_page_size;
3939 }
3940#endif
3941
3942 row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
3943
3944 srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
3945 if (innobase_locks_unsafe_for_binlog) {
3946 ib::warn() << "Using innodb_locks_unsafe_for_binlog is"
3947 " DEPRECATED. This option may be removed in future"
3948 " releases. Please use READ COMMITTED transaction"
3949 " isolation level instead; " << SET_TRANSACTION_MSG;
3950 }
3951
3952 if (innobase_open_files < 10) {
3953 innobase_open_files = 300;
3954 if (srv_file_per_table && tc_size > 300 && tc_size < open_files_limit) {
3955 innobase_open_files = tc_size;
3956 }
3957 }
3958
3959 if (innobase_open_files > open_files_limit) {
3960 ib::warn() << "innodb_open_files " << innobase_open_files
3961 << " should not be greater"
3962 << "than the open_files_limit " << open_files_limit;
3963 if (innobase_open_files > tc_size) {
3964 innobase_open_files = tc_size;
3965 }
3966 }
3967
3968 srv_max_n_open_files = innobase_open_files;
3969 srv_innodb_status = (ibool) innobase_create_status_file;
3970
3971 srv_print_verbose_log = mysqld_embedded ? 0 : 1;
3972
3973 /* Round up fts_sort_pll_degree to nearest power of 2 number */
3974 for (num_pll_degree = 1;
3975 num_pll_degree < fts_sort_pll_degree;
3976 num_pll_degree <<= 1) {
3977
3978 /* No op */
3979 }
3980
3981 fts_sort_pll_degree = num_pll_degree;
3982
3983 /* Store the default charset-collation number of this MySQL
3984 installation */
3985
3986 data_mysql_default_charset_coll = (ulint) default_charset_info->number;
3987
3988 innobase_commit_concurrency_init_default();
3989
3990 srv_use_atomic_writes
3991 = innobase_use_atomic_writes && my_may_have_atomic_write;
3992 if (srv_use_atomic_writes && !srv_file_per_table)
3993 {
3994 fprintf(stderr, "InnoDB: Disabling atomic_writes as file_per_table is not used.\n");
3995 srv_use_atomic_writes= 0;
3996 }
3997
3998 if (srv_use_atomic_writes) {
3999 fprintf(stderr, "InnoDB: using atomic writes.\n");
4000 /*
4001 Force O_DIRECT on Unixes (on Windows writes are always
4002 unbuffered)
4003 */
4004#ifndef _WIN32
4005 switch (innodb_flush_method) {
4006 case SRV_O_DIRECT:
4007 case SRV_O_DIRECT_NO_FSYNC:
4008 break;
4009 default:
4010 innodb_flush_method = SRV_O_DIRECT;
4011 fprintf(stderr, "InnoDB: using O_DIRECT due to atomic writes.\n");
4012 }
4013#endif
4014 }
4015
4016 if (srv_read_only_mode) {
4017 ib::info() << "Started in read only mode";
4018 srv_use_doublewrite_buf = FALSE;
4019 }
4020
4021#ifdef LINUX_NATIVE_AIO
4022 if (srv_use_native_aio) {
4023 ib::info() << "Using Linux native AIO";
4024 }
4025#elif !defined _WIN32
4026 /* Currently native AIO is supported only on windows and linux
4027 and that also when the support is compiled in. In all other
4028 cases, we ignore the setting of innodb_use_native_aio. */
4029 srv_use_native_aio = FALSE;
4030#endif
4031
4032#ifndef _WIN32
4033 ut_ad(innodb_flush_method <= SRV_O_DIRECT_NO_FSYNC);
4034#else
4035 switch (innodb_flush_method) {
4036 case SRV_ALL_O_DIRECT_FSYNC + 1 /* "async_unbuffered"="unbuffered" */:
4037 innodb_flush_method = SRV_ALL_O_DIRECT_FSYNC;
4038 break;
4039 case SRV_ALL_O_DIRECT_FSYNC + 2 /* "normal"="fsync" */:
4040 innodb_flush_method = SRV_FSYNC;
4041 break;
4042 default:
4043 ut_ad(innodb_flush_method <= SRV_ALL_O_DIRECT_FSYNC);
4044 }
4045#endif
4046 srv_file_flush_method = srv_flush_t(innodb_flush_method);
4047
4048 innodb_buffer_pool_size_init();
4049
4050 if (srv_n_page_cleaners > srv_buf_pool_instances) {
4051 /* limit of page_cleaner parallelizability
4052 is number of buffer pool instances. */
4053 srv_n_page_cleaners = srv_buf_pool_instances;
4054 }
4055
4056 srv_lock_table_size = 5 * (srv_buf_pool_size >> srv_page_size_shift);
4057 DBUG_RETURN(0);
4058}
4059
4060/** Initialize the InnoDB storage engine plugin.
4061@param[in,out] p InnoDB handlerton
4062@return error code
4063@retval 0 on success */
4064static int innodb_init(void* p)
4065{
4066 DBUG_ENTER("innodb_init");
4067 handlerton* innobase_hton= static_cast<handlerton*>(p);
4068 innodb_hton_ptr = innobase_hton;
4069
4070 innobase_hton->state = SHOW_OPTION_YES;
4071 innobase_hton->db_type = DB_TYPE_INNODB;
4072 innobase_hton->savepoint_offset = sizeof(trx_named_savept_t);
4073 innobase_hton->close_connection = innobase_close_connection;
4074 innobase_hton->kill_query = innobase_kill_query;
4075 innobase_hton->savepoint_set = innobase_savepoint;
4076 innobase_hton->savepoint_rollback = innobase_rollback_to_savepoint;
4077
4078 innobase_hton->savepoint_rollback_can_release_mdl =
4079 innobase_rollback_to_savepoint_can_release_mdl;
4080
4081 innobase_hton->savepoint_release = innobase_release_savepoint;
4082 innobase_hton->prepare_ordered= NULL;
4083 innobase_hton->commit_ordered= innobase_commit_ordered;
4084 innobase_hton->commit = innobase_commit;
4085 innobase_hton->rollback = innobase_rollback;
4086 innobase_hton->prepare = innobase_xa_prepare;
4087 innobase_hton->recover = innobase_xa_recover;
4088 innobase_hton->commit_by_xid = innobase_commit_by_xid;
4089 innobase_hton->rollback_by_xid = innobase_rollback_by_xid;
4090 innobase_hton->commit_checkpoint_request=innobase_checkpoint_request;
4091 innobase_hton->create = innobase_create_handler;
4092
4093 innobase_hton->drop_database = innobase_drop_database;
4094 innobase_hton->panic = innobase_end;
4095
4096 innobase_hton->start_consistent_snapshot =
4097 innobase_start_trx_and_assign_read_view;
4098
4099 innobase_hton->flush_logs = innobase_flush_logs;
4100 innobase_hton->show_status = innobase_show_status;
4101 innobase_hton->flags =
4102 HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS
4103 | HTON_NATIVE_SYS_VERSIONING;
4104
4105#ifdef WITH_WSREP
4106 innobase_hton->abort_transaction=wsrep_abort_transaction;
4107 innobase_hton->set_checkpoint=innobase_wsrep_set_checkpoint;
4108 innobase_hton->get_checkpoint=innobase_wsrep_get_checkpoint;
4109 innobase_hton->fake_trx_id=wsrep_fake_trx_id;
4110#endif /* WITH_WSREP */
4111
4112 innobase_hton->tablefile_extensions = ha_innobase_exts;
4113 innobase_hton->table_options = innodb_table_option_list;
4114
4115 /* System Versioning */
4116 innobase_hton->prepare_commit_versioned
4117 = innodb_prepare_commit_versioned;
4118
4119 innodb_remember_check_sysvar_funcs();
4120
4121 compile_time_assert(DATA_MYSQL_TRUE_VARCHAR == MYSQL_TYPE_VARCHAR);
4122
4123#ifndef DBUG_OFF
4124 static const char test_filename[] = "-@";
4125 char test_tablename[sizeof test_filename
4126 + sizeof(srv_mysql50_table_name_prefix) - 1];
4127 DBUG_ASSERT(sizeof test_tablename - 1
4128 == filename_to_tablename(test_filename,
4129 test_tablename,
4130 sizeof test_tablename, true));
4131 DBUG_ASSERT(!strncmp(test_tablename,
4132 srv_mysql50_table_name_prefix,
4133 sizeof srv_mysql50_table_name_prefix - 1));
4134 DBUG_ASSERT(!strcmp(test_tablename
4135 + sizeof srv_mysql50_table_name_prefix - 1,
4136 test_filename));
4137#endif /* DBUG_OFF */
4138
4139 os_file_set_umask(my_umask);
4140
4141 /* Setup the memory alloc/free tracing mechanisms before calling
4142 any functions that could possibly allocate memory. */
4143 ut_new_boot();
4144
4145 if (int error = innodb_init_params()) {
4146 DBUG_RETURN(error);
4147 }
4148
4149 /* After this point, error handling has to use
4150 innodb_init_abort(). */
4151
4152#ifdef HAVE_PSI_INTERFACE
4153 /* Register keys with MySQL performance schema */
4154 int count;
4155
4156 count = array_elements(all_pthread_mutexes);
4157 mysql_mutex_register("innodb", all_pthread_mutexes, count);
4158
4159# ifdef UNIV_PFS_MUTEX
4160 count = array_elements(all_innodb_mutexes);
4161 mysql_mutex_register("innodb", all_innodb_mutexes, count);
4162# endif /* UNIV_PFS_MUTEX */
4163
4164# ifdef UNIV_PFS_RWLOCK
4165 count = array_elements(all_innodb_rwlocks);
4166 mysql_rwlock_register("innodb", all_innodb_rwlocks, count);
4167# endif /* UNIV_PFS_MUTEX */
4168
4169# ifdef UNIV_PFS_THREAD
4170 count = array_elements(all_innodb_threads);
4171 mysql_thread_register("innodb", all_innodb_threads, count);
4172# endif /* UNIV_PFS_THREAD */
4173
4174# ifdef UNIV_PFS_IO
4175 count = array_elements(all_innodb_files);
4176 mysql_file_register("innodb", all_innodb_files, count);
4177# endif /* UNIV_PFS_IO */
4178
4179 count = array_elements(all_innodb_conds);
4180 mysql_cond_register("innodb", all_innodb_conds, count);
4181#endif /* HAVE_PSI_INTERFACE */
4182
4183 bool create_new_db = false;
4184
4185 /* Check whether the data files exist. */
4186 dberr_t err = srv_sys_space.check_file_spec(&create_new_db, 5U << 20);
4187
4188 if (err != DB_SUCCESS) {
4189 DBUG_RETURN(innodb_init_abort());
4190 }
4191
4192 err = srv_start(create_new_db);
4193
4194 if (err != DB_SUCCESS) {
4195 innodb_shutdown();
4196 DBUG_RETURN(innodb_init_abort());
4197 } else if (!srv_read_only_mode) {
4198 mysql_thread_create(thd_destructor_thread_key,
4199 &thd_destructor_thread,
4200 NULL, thd_destructor_proxy, NULL);
4201 while (!my_atomic_loadptr_explicit(reinterpret_cast<void**>
4202 (&srv_running),
4203 MY_MEMORY_ORDER_RELAXED))
4204 os_thread_sleep(20);
4205 }
4206
4207 srv_was_started = true;
4208 innodb_params_adjust();
4209
4210 innobase_old_blocks_pct = static_cast<uint>(
4211 buf_LRU_old_ratio_update(innobase_old_blocks_pct, TRUE));
4212
4213 ibuf_max_size_update(srv_change_buffer_max_size);
4214
4215 innobase_open_tables = hash_create(200);
4216 mysql_mutex_init(innobase_share_mutex_key,
4217 &innobase_share_mutex,
4218 MY_MUTEX_INIT_FAST);
4219 mysql_mutex_init(commit_cond_mutex_key,
4220 &commit_cond_m, MY_MUTEX_INIT_FAST);
4221 mysql_cond_init(commit_cond_key, &commit_cond, 0);
4222 mysql_mutex_init(pending_checkpoint_mutex_key,
4223 &pending_checkpoint_mutex,
4224 MY_MUTEX_INIT_FAST);
4225#ifdef MYSQL_DYNAMIC_PLUGIN
4226 if (innobase_hton != p) {
4227 innobase_hton = reinterpret_cast<handlerton*>(p);
4228 *innobase_hton = *innodb_hton_ptr;
4229 }
4230#endif /* MYSQL_DYNAMIC_PLUGIN */
4231
4232 /* Currently, monitor counter information are not persistent. */
4233 memset(monitor_set_tbl, 0, sizeof monitor_set_tbl);
4234
4235 memset(innodb_counter_value, 0, sizeof innodb_counter_value);
4236
4237 /* Do this as late as possible so server is fully starts up,
4238 since we might get some initial stats if user choose to turn
4239 on some counters from start up */
4240 if (innobase_enable_monitor_counter) {
4241 innodb_enable_monitor_at_startup(
4242 innobase_enable_monitor_counter);
4243 }
4244
4245 /* Turn on monitor counters that are default on */
4246 srv_mon_default_on();
4247
4248 /* Unit Tests */
4249#ifdef UNIV_ENABLE_UNIT_TEST_GET_PARENT_DIR
4250 unit_test_os_file_get_parent_dir();
4251#endif /* UNIV_ENABLE_UNIT_TEST_GET_PARENT_DIR */
4252
4253#ifdef UNIV_ENABLE_UNIT_TEST_MAKE_FILEPATH
4254 test_make_filepath();
4255#endif /*UNIV_ENABLE_UNIT_TEST_MAKE_FILEPATH */
4256
4257#ifdef UNIV_ENABLE_DICT_STATS_TEST
4258 test_dict_stats_all();
4259#endif /*UNIV_ENABLE_DICT_STATS_TEST */
4260
4261#ifdef UNIV_ENABLE_UNIT_TEST_ROW_RAW_FORMAT_INT
4262# ifdef HAVE_UT_CHRONO_T
4263 test_row_raw_format_int();
4264# endif /* HAVE_UT_CHRONO_T */
4265#endif /* UNIV_ENABLE_UNIT_TEST_ROW_RAW_FORMAT_INT */
4266
4267 DBUG_RETURN(0);
4268}
4269
4270/** Shut down the InnoDB storage engine.
4271@return 0 */
4272static
4273int
4274innobase_end(handlerton*, ha_panic_function)
4275{
4276 DBUG_ENTER("innobase_end");
4277
4278 if (srv_was_started) {
4279 THD *thd= current_thd;
4280 if (thd) { // may be UNINSTALL PLUGIN statement
4281 trx_t* trx = thd_to_trx(thd);
4282 if (trx) {
4283 trx_free(trx);
4284 }
4285 }
4286
4287 hash_table_free(innobase_open_tables);
4288 innobase_open_tables = NULL;
4289
4290 st_my_thread_var* running = reinterpret_cast<st_my_thread_var*>(
4291 my_atomic_loadptr_explicit(
4292 reinterpret_cast<void**>(&srv_running),
4293 MY_MEMORY_ORDER_RELAXED));
4294 if (!abort_loop && running) {
4295 // may be UNINSTALL PLUGIN statement
4296 running->abort = 1;
4297 mysql_cond_broadcast(running->current_cond);
4298 }
4299
4300 if (!srv_read_only_mode) {
4301 pthread_join(thd_destructor_thread, NULL);
4302 }
4303
4304 innodb_shutdown();
4305 innobase_space_shutdown();
4306
4307 mysql_mutex_destroy(&innobase_share_mutex);
4308 mysql_mutex_destroy(&commit_cond_m);
4309 mysql_cond_destroy(&commit_cond);
4310 mysql_mutex_destroy(&pending_checkpoint_mutex);
4311 }
4312
4313 DBUG_RETURN(0);
4314}
4315
4316/*****************************************************************//**
4317Commits a transaction in an InnoDB database. */
4318void
4319innobase_commit_low(
4320/*================*/
4321 trx_t* trx) /*!< in: transaction handle */
4322{
4323#ifdef WITH_WSREP
4324 THD* thd = (THD*)trx->mysql_thd;
4325 const char* tmp = 0;
4326 if (thd && wsrep_on(thd)) {
4327#ifdef WSREP_PROC_INFO
4328 char info[64];
4329 info[sizeof(info) - 1] = '\0';
4330 snprintf(info, sizeof(info) - 1,
4331 "innobase_commit_low():trx_commit_for_mysql(%lld)",
4332 (long long) wsrep_thd_trx_seqno(thd));
4333 tmp = thd_proc_info(thd, info);
4334
4335#else
4336 tmp = thd_proc_info(thd, "innobase_commit_low()");
4337#endif /* WSREP_PROC_INFO */
4338 }
4339#endif /* WITH_WSREP */
4340 if (trx_is_started(trx)) {
4341
4342 trx_commit_for_mysql(trx);
4343 }
4344 trx->will_lock = 0;
4345#ifdef WITH_WSREP
4346 if (thd && wsrep_on(thd)) { thd_proc_info(thd, tmp); }
4347#endif /* WITH_WSREP */
4348}
4349
4350/*****************************************************************//**
4351Creates an InnoDB transaction struct for the thd if it does not yet have one.
4352Starts a new InnoDB transaction if a transaction is not yet started. And
4353assigns a new snapshot for a consistent read if the transaction does not yet
4354have one.
4355@return 0 */
4356static
4357int
4358innobase_start_trx_and_assign_read_view(
4359/*====================================*/
4360 handlerton* hton, /*!< in: InnoDB handlerton */
4361 THD* thd) /*!< in: MySQL thread handle of the user for
4362 whom the transaction should be committed */
4363{
4364 DBUG_ENTER("innobase_start_trx_and_assign_read_view");
4365 DBUG_ASSERT(hton == innodb_hton_ptr);
4366
4367 /* Create a new trx struct for thd, if it does not yet have one */
4368
4369 trx_t* trx = check_trx_exists(thd);
4370
4371 innobase_srv_conc_force_exit_innodb(trx);
4372
4373 /* The transaction should not be active yet, start it */
4374
4375 ut_ad(!trx_is_started(trx));
4376
4377 trx_start_if_not_started_xa(trx, false);
4378
4379 /* Assign a read view if the transaction does not have it yet.
4380 Do this only if transaction is using REPEATABLE READ isolation
4381 level. */
4382 trx->isolation_level = innobase_map_isolation_level(
4383 thd_get_trx_isolation(thd));
4384
4385 if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
4386 trx->read_view.open(trx);
4387 } else {
4388 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
4389 HA_ERR_UNSUPPORTED,
4390 "InnoDB: WITH CONSISTENT SNAPSHOT"
4391 " was ignored because this phrase"
4392 " can only be used with"
4393 " REPEATABLE READ isolation level.");
4394 }
4395
4396 /* Set the MySQL flag to mark that there is an active transaction */
4397
4398 innobase_register_trx(hton, current_thd, trx);
4399
4400 DBUG_RETURN(0);
4401}
4402
4403static
4404void
4405innobase_commit_ordered_2(
4406/*======================*/
4407 trx_t* trx, /*!< in: Innodb transaction */
4408 THD* thd) /*!< in: MySQL thread handle */
4409{
4410 DBUG_ENTER("innobase_commit_ordered_2");
4411
4412 bool read_only = trx->read_only || trx->id == 0;
4413
4414 if (!read_only) {
4415
4416 while (innobase_commit_concurrency > 0) {
4417
4418 mysql_mutex_lock(&commit_cond_m);
4419
4420 ++commit_threads;
4421
4422 if (commit_threads
4423 <= innobase_commit_concurrency) {
4424
4425 mysql_mutex_unlock(&commit_cond_m);
4426 break;
4427 }
4428
4429 --commit_threads;
4430
4431 mysql_cond_wait(&commit_cond, &commit_cond_m);
4432
4433 mysql_mutex_unlock(&commit_cond_m);
4434 }
4435
4436 /* The following call reads the binary log position of
4437 the transaction being committed.
4438
4439 Binary logging of other engines is not relevant to
4440 InnoDB as all InnoDB requires is that committing
4441 InnoDB transactions appear in the same order in the
4442 MySQL binary log as they appear in InnoDB logs, which
4443 is guaranteed by the server.
4444
4445 If the binary log is not enabled, or the transaction
4446 is not written to the binary log, the file name will
4447 be a NULL pointer. */
4448 thd_binlog_pos(thd, &trx->mysql_log_file_name,
4449 &trx->mysql_log_offset);
4450
4451 /* Don't do write + flush right now. For group commit
4452 to work we want to do the flush later. */
4453 trx->flush_log_later = true;
4454 }
4455
4456 innobase_commit_low(trx);
4457
4458 if (!read_only) {
4459 trx->flush_log_later = false;
4460
4461 if (innobase_commit_concurrency > 0) {
4462
4463 mysql_mutex_lock(&commit_cond_m);
4464
4465 ut_ad(commit_threads > 0);
4466 --commit_threads;
4467
4468 mysql_cond_signal(&commit_cond);
4469
4470 mysql_mutex_unlock(&commit_cond_m);
4471 }
4472 }
4473
4474 DBUG_VOID_RETURN;
4475}
4476
4477/*****************************************************************//**
4478Perform the first, fast part of InnoDB commit.
4479
4480Doing it in this call ensures that we get the same commit order here
4481as in binlog and any other participating transactional storage engines.
4482
4483Note that we want to do as little as really needed here, as we run
4484under a global mutex. The expensive fsync() is done later, in
4485innobase_commit(), without a lock so group commit can take place.
4486
4487Note also that this method can be called from a different thread than
4488the one handling the rest of the transaction. */
4489static
4490void
4491innobase_commit_ordered(
4492/*====================*/
4493 handlerton *hton, /*!< in: Innodb handlerton */
4494 THD* thd, /*!< in: MySQL thread handle of the user for whom
4495 the transaction should be committed */
4496 bool all) /*!< in: TRUE - commit transaction
4497 FALSE - the current SQL statement ended */
4498{
4499 trx_t* trx;
4500 DBUG_ENTER("innobase_commit_ordered");
4501 DBUG_ASSERT(hton == innodb_hton_ptr);
4502
4503 trx = check_trx_exists(thd);
4504
4505 if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
4506 /* We cannot throw error here; instead we will catch this error
4507 again in innobase_commit() and report it from there. */
4508 DBUG_VOID_RETURN;
4509 }
4510
4511 /* commit_ordered is only called when committing the whole transaction
4512 (or an SQL statement when autocommit is on). */
4513 DBUG_ASSERT(all ||
4514 (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)));
4515
4516 innobase_commit_ordered_2(trx, thd);
4517
4518 trx_set_active_commit_ordered(trx);
4519
4520 DBUG_VOID_RETURN;
4521}
4522
4523/*****************************************************************//**
4524Commits a transaction in an InnoDB database or marks an SQL statement
4525ended.
4526@return 0 or deadlock error if the transaction was aborted by another
4527 higher priority transaction. */
4528static
4529int
4530innobase_commit(
4531/*============*/
4532 handlerton* hton, /*!< in: InnoDB handlerton */
4533 THD* thd, /*!< in: MySQL thread handle of the
4534 user for whom the transaction should
4535 be committed */
4536 bool commit_trx) /*!< in: true - commit transaction
4537 false - the current SQL statement
4538 ended */
4539{
4540 DBUG_ENTER("innobase_commit");
4541 DBUG_PRINT("enter", ("commit_trx: %d", commit_trx));
4542 DBUG_ASSERT(hton == innodb_hton_ptr);
4543 DBUG_PRINT("trans", ("ending transaction"));
4544
4545 trx_t* trx = check_trx_exists(thd);
4546
4547 ut_ad(trx->dict_operation_lock_mode == 0);
4548 ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
4549
4550 /* Transaction is deregistered only in a commit or a rollback. If
4551 it is deregistered we know there cannot be resources to be freed
4552 and we could return immediately. For the time being, we play safe
4553 and do the cleanup though there should be nothing to clean up. */
4554
4555 if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
4556
4557 sql_print_error("Transaction not registered for MariaDB 2PC,"
4558 " but transaction is active");
4559 }
4560
4561 bool read_only = trx->read_only || trx->id == 0;
4562 DBUG_PRINT("info", ("readonly: %d", read_only));
4563
4564 if (commit_trx
4565 || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
4566
4567 DBUG_EXECUTE_IF("crash_innodb_before_commit",
4568 DBUG_SUICIDE(););
4569
4570 /* Run the fast part of commit if we did not already. */
4571 if (!trx_is_active_commit_ordered(trx)) {
4572 innobase_commit_ordered_2(trx, thd);
4573
4574 }
4575
4576 /* We were instructed to commit the whole transaction, or
4577 this is an SQL statement end and autocommit is on */
4578
4579 /* At this point commit order is fixed and transaction is
4580 visible to others. So we can wakeup other commits waiting for
4581 this one, to allow then to group commit with us. */
4582 thd_wakeup_subsequent_commits(thd, 0);
4583
4584 /* Now do a write + flush of logs. */
4585 trx_commit_complete_for_mysql(trx);
4586
4587 trx_deregister_from_2pc(trx);
4588 } else {
4589 /* We just mark the SQL statement ended and do not do a
4590 transaction commit */
4591
4592 /* If we had reserved the auto-inc lock for some
4593 table in this SQL statement we release it now */
4594
4595 if (!read_only) {
4596 lock_unlock_table_autoinc(trx);
4597 }
4598
4599 /* Store the current undo_no of the transaction so that we
4600 know where to roll back if we have to roll back the next
4601 SQL statement */
4602
4603 trx_mark_sql_stat_end(trx);
4604 }
4605
4606 /* Reset the number AUTO-INC rows required */
4607 trx->n_autoinc_rows = 0;
4608
4609 /* This is a statement level variable. */
4610 trx->fts_next_doc_id = 0;
4611
4612 innobase_srv_conc_force_exit_innodb(trx);
4613
4614 DBUG_RETURN(0);
4615}
4616
4617/*****************************************************************//**
4618Rolls back a transaction or the latest SQL statement.
4619@return 0 or error number */
4620static
4621int
4622innobase_rollback(
4623/*==============*/
4624 handlerton* hton, /*!< in: InnoDB handlerton */
4625 THD* thd, /*!< in: handle to the MySQL thread
4626 of the user whose transaction should
4627 be rolled back */
4628 bool rollback_trx) /*!< in: TRUE - rollback entire
4629 transaction FALSE - rollback the current
4630 statement only */
4631{
4632 DBUG_ENTER("innobase_rollback");
4633 DBUG_ASSERT(hton == innodb_hton_ptr);
4634 DBUG_PRINT("trans", ("aborting transaction"));
4635
4636 trx_t* trx = check_trx_exists(thd);
4637
4638 ut_ad(trx->dict_operation_lock_mode == 0);
4639 ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
4640
4641 innobase_srv_conc_force_exit_innodb(trx);
4642
4643 /* Reset the number AUTO-INC rows required */
4644
4645 trx->n_autoinc_rows = 0;
4646
4647 /* If we had reserved the auto-inc lock for some table (if
4648 we come here to roll back the latest SQL statement) we
4649 release it now before a possibly lengthy rollback */
4650 lock_unlock_table_autoinc(trx);
4651
4652 /* This is a statement level variable. */
4653
4654 trx->fts_next_doc_id = 0;
4655
4656 dberr_t error;
4657
4658 if (rollback_trx
4659 || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
4660
4661 error = trx_rollback_for_mysql(trx);
4662
4663 trx_deregister_from_2pc(trx);
4664 } else {
4665
4666 error = trx_rollback_last_sql_stat_for_mysql(trx);
4667 }
4668
4669 DBUG_RETURN(convert_error_code_to_mysql(error, 0, trx->mysql_thd));
4670}
4671
4672/*****************************************************************//**
4673Rolls back a transaction
4674@return 0 or error number */
4675static
4676int
4677innobase_rollback_trx(
4678/*==================*/
4679 trx_t* trx) /*!< in: transaction */
4680{
4681 DBUG_ENTER("innobase_rollback_trx");
4682 DBUG_PRINT("trans", ("aborting transaction"));
4683
4684 innobase_srv_conc_force_exit_innodb(trx);
4685
4686 /* If we had reserved the auto-inc lock for some table (if
4687 we come here to roll back the latest SQL statement) we
4688 release it now before a possibly lengthy rollback */
4689 lock_unlock_table_autoinc(trx);
4690
4691 if (!trx->has_logged()) {
4692 trx->will_lock = 0;
4693 DBUG_RETURN(0);
4694 }
4695
4696 DBUG_RETURN(convert_error_code_to_mysql(trx_rollback_for_mysql(trx),
4697 0, trx->mysql_thd));
4698}
4699
4700
4701struct pending_checkpoint {
4702 struct pending_checkpoint *next;
4703 handlerton *hton;
4704 void *cookie;
4705 ib_uint64_t lsn;
4706};
4707static struct pending_checkpoint *pending_checkpoint_list;
4708static struct pending_checkpoint *pending_checkpoint_list_end;
4709
4710/*****************************************************************//**
4711Handle a commit checkpoint request from server layer.
4712We put the request in a queue, so that we can notify upper layer about
4713checkpoint complete when we have flushed the redo log.
4714If we have already flushed all relevant redo log, we notify immediately.*/
4715static
4716void
4717innobase_checkpoint_request(
4718 handlerton *hton,
4719 void *cookie)
4720{
4721 ib_uint64_t lsn;
4722 ib_uint64_t flush_lsn;
4723 struct pending_checkpoint * entry;
4724
4725 /* Do the allocation outside of lock to reduce contention. The normal
4726 case is that not everything is flushed, so we will need to enqueue. */
4727 entry = static_cast<struct pending_checkpoint *>
4728 (my_malloc(sizeof(*entry), MYF(MY_WME)));
4729 if (!entry) {
4730 sql_print_error("Failed to allocate %u bytes."
4731 " Commit checkpoint will be skipped.",
4732 static_cast<unsigned>(sizeof(*entry)));
4733 return;
4734 }
4735
4736 entry->next = NULL;
4737 entry->hton = hton;
4738 entry->cookie = cookie;
4739
4740 mysql_mutex_lock(&pending_checkpoint_mutex);
4741 lsn = log_get_lsn();
4742 flush_lsn = log_get_flush_lsn();
4743 if (lsn > flush_lsn) {
4744 /* Put the request in queue.
4745 When the log gets flushed past the lsn, we will remove the
4746 entry from the queue and notify the upper layer. */
4747 entry->lsn = lsn;
4748 if (pending_checkpoint_list_end) {
4749 pending_checkpoint_list_end->next = entry;
4750 /* There is no need to order the entries in the list
4751 by lsn. The upper layer can accept notifications in
4752 any order, and short delays in notifications do not
4753 significantly impact performance. */
4754 } else {
4755 pending_checkpoint_list = entry;
4756 }
4757 pending_checkpoint_list_end = entry;
4758 entry = NULL;
4759 }
4760 mysql_mutex_unlock(&pending_checkpoint_mutex);
4761
4762 if (entry) {
4763 /* We are already flushed. Notify the checkpoint immediately. */
4764 commit_checkpoint_notify_ha(entry->hton, entry->cookie);
4765 my_free(entry);
4766 }
4767}
4768
4769/*****************************************************************//**
4770Log code calls this whenever log has been written and/or flushed up
4771to a new position. We use this to notify upper layer of a new commit
4772checkpoint when necessary.*/
4773UNIV_INTERN
4774void
4775innobase_mysql_log_notify(
4776/*======================*/
4777 ib_uint64_t flush_lsn) /*!< in: LSN flushed to disk */
4778{
4779 struct pending_checkpoint * pending;
4780 struct pending_checkpoint * entry;
4781 struct pending_checkpoint * last_ready;
4782
4783 /* It is safe to do a quick check for NULL first without lock.
4784 Even if we should race, we will at most skip one checkpoint and
4785 take the next one, which is harmless. */
4786 if (!pending_checkpoint_list)
4787 return;
4788
4789 mysql_mutex_lock(&pending_checkpoint_mutex);
4790 pending = pending_checkpoint_list;
4791 if (!pending)
4792 {
4793 mysql_mutex_unlock(&pending_checkpoint_mutex);
4794 return;
4795 }
4796
4797 last_ready = NULL;
4798 for (entry = pending; entry != NULL; entry = entry -> next)
4799 {
4800 /* Notify checkpoints up until the first entry that has not
4801 been fully flushed to the redo log. Since we do not maintain
4802 the list ordered, in principle there could be more entries
4803 later than were also flushed. But there is no harm in
4804 delaying notifications for those a bit. And in practise, the
4805 list is unlikely to have more than one element anyway, as we
4806 flush the redo log at least once every second. */
4807 if (entry->lsn > flush_lsn)
4808 break;
4809 last_ready = entry;
4810 }
4811
4812 if (last_ready)
4813 {
4814 /* We found some pending checkpoints that are now flushed to
4815 disk. So remove them from the list. */
4816 pending_checkpoint_list = entry;
4817 if (!entry)
4818 pending_checkpoint_list_end = NULL;
4819 }
4820
4821 mysql_mutex_unlock(&pending_checkpoint_mutex);
4822
4823 if (!last_ready)
4824 return;
4825
4826 /* Now that we have released the lock, notify upper layer about all
4827 commit checkpoints that have now completed. */
4828 for (;;) {
4829 entry = pending;
4830 pending = pending->next;
4831
4832 commit_checkpoint_notify_ha(entry->hton, entry->cookie);
4833
4834 my_free(entry);
4835 if (entry == last_ready)
4836 break;
4837 }
4838}
4839
4840/*****************************************************************//**
4841Rolls back a transaction to a savepoint.
4842@return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the
4843given name */
4844static
4845int
4846innobase_rollback_to_savepoint(
4847/*===========================*/
4848 handlerton* hton, /*!< in: InnoDB handlerton */
4849 THD* thd, /*!< in: handle to the MySQL thread
4850 of the user whose transaction should
4851 be rolled back to savepoint */
4852 void* savepoint) /*!< in: savepoint data */
4853{
4854
4855 DBUG_ENTER("innobase_rollback_to_savepoint");
4856 DBUG_ASSERT(hton == innodb_hton_ptr);
4857
4858 trx_t* trx = check_trx_exists(thd);
4859
4860 innobase_srv_conc_force_exit_innodb(trx);
4861
4862 /* TODO: use provided savepoint data area to store savepoint data */
4863
4864 char name[64];
4865
4866 longlong2str(longlong(savepoint), name, 36);
4867
4868 int64_t mysql_binlog_cache_pos;
4869
4870 dberr_t error = trx_rollback_to_savepoint_for_mysql(
4871 trx, name, &mysql_binlog_cache_pos);
4872
4873 if (error == DB_SUCCESS && trx->fts_trx != NULL) {
4874 fts_savepoint_rollback(trx, name);
4875 }
4876
4877 DBUG_RETURN(convert_error_code_to_mysql(error, 0, NULL));
4878}
4879
4880/*****************************************************************//**
4881Check whether innodb state allows to safely release MDL locks after
4882rollback to savepoint.
4883When binlog is on, MDL locks acquired after savepoint unit are not
4884released if there are any locks held in InnoDB.
4885@return true if it is safe, false if its not safe. */
4886static
4887bool
4888innobase_rollback_to_savepoint_can_release_mdl(
4889/*===========================================*/
4890 handlerton* hton, /*!< in: InnoDB handlerton */
4891 THD* thd) /*!< in: handle to the MySQL thread
4892 of the user whose transaction should
4893 be rolled back to savepoint */
4894{
4895 DBUG_ENTER("innobase_rollback_to_savepoint_can_release_mdl");
4896 DBUG_ASSERT(hton == innodb_hton_ptr);
4897
4898 trx_t* trx = check_trx_exists(thd);
4899
4900 /* If transaction has not acquired any locks then it is safe
4901 to release MDL after rollback to savepoint */
4902 if (UT_LIST_GET_LEN(trx->lock.trx_locks) == 0) {
4903
4904 DBUG_RETURN(true);
4905 }
4906
4907 DBUG_RETURN(false);
4908}
4909
4910/*****************************************************************//**
4911Release transaction savepoint name.
4912@return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the
4913given name */
4914static
4915int
4916innobase_release_savepoint(
4917/*=======================*/
4918 handlerton* hton, /*!< in: handlerton for InnoDB */
4919 THD* thd, /*!< in: handle to the MySQL thread
4920 of the user whose transaction's
4921 savepoint should be released */
4922 void* savepoint) /*!< in: savepoint data */
4923{
4924 dberr_t error;
4925 trx_t* trx;
4926 char name[64];
4927
4928 DBUG_ENTER("innobase_release_savepoint");
4929 DBUG_ASSERT(hton == innodb_hton_ptr);
4930
4931 trx = check_trx_exists(thd);
4932
4933 /* TODO: use provided savepoint data area to store savepoint data */
4934
4935 longlong2str(longlong(savepoint), name, 36);
4936
4937 error = trx_release_savepoint_for_mysql(trx, name);
4938
4939 if (error == DB_SUCCESS && trx->fts_trx != NULL) {
4940 fts_savepoint_release(trx, name);
4941 }
4942
4943 DBUG_RETURN(convert_error_code_to_mysql(error, 0, NULL));
4944}
4945
4946/*****************************************************************//**
4947Sets a transaction savepoint.
4948@return always 0, that is, always succeeds */
4949static
4950int
4951innobase_savepoint(
4952/*===============*/
4953 handlerton* hton, /*!< in: handle to the InnoDB handlerton */
4954 THD* thd, /*!< in: handle to the MySQL thread */
4955 void* savepoint)/*!< in: savepoint data */
4956{
4957 DBUG_ENTER("innobase_savepoint");
4958 DBUG_ASSERT(hton == innodb_hton_ptr);
4959
4960 /* In the autocommit mode there is no sense to set a savepoint
4961 (unless we are in sub-statement), so SQL layer ensures that
4962 this method is never called in such situation. */
4963
4964 trx_t* trx = check_trx_exists(thd);
4965
4966 innobase_srv_conc_force_exit_innodb(trx);
4967
4968 /* Cannot happen outside of transaction */
4969 DBUG_ASSERT(trx_is_registered_for_2pc(trx));
4970
4971 /* TODO: use provided savepoint data area to store savepoint data */
4972 char name[64];
4973
4974 longlong2str(longlong(savepoint), name, 36);
4975
4976 dberr_t error = trx_savepoint_for_mysql(trx, name, 0);
4977
4978 if (error == DB_SUCCESS && trx->fts_trx != NULL) {
4979 fts_savepoint_take(trx->fts_trx, name);
4980 }
4981
4982 DBUG_RETURN(convert_error_code_to_mysql(error, 0, NULL));
4983}
4984
4985/*****************************************************************//**
4986Frees a possible InnoDB trx object associated with the current THD.
4987@return 0 or error number */
4988static
4989int
4990innobase_close_connection(
4991/*======================*/
4992 handlerton* hton, /*!< in: innobase handlerton */
4993 THD* thd) /*!< in: handle to the MySQL thread of the user
4994 whose resources should be free'd */
4995{
4996
4997 DBUG_ENTER("innobase_close_connection");
4998 DBUG_ASSERT(hton == innodb_hton_ptr);
4999
5000 trx_t* trx = thd_to_trx(thd);
5001
5002 /* During server initialization MySQL layer will try to open
5003 some of the master-slave tables those residing in InnoDB.
5004 After MySQL layer is done with needed checks these tables
5005 are closed followed by invocation of close_connection on the
5006 associated thd.
5007
5008 close_connection rolls back the trx and then frees it.
5009 Once trx is freed thd should avoid maintaining reference to
5010 it else it can be classified as stale reference.
5011
5012 Re-invocation of innodb_close_connection on same thd should
5013 get trx as NULL. */
5014
5015 if (trx) {
5016
5017 if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
5018
5019 sql_print_error("Transaction not registered for MariaDB 2PC, "
5020 "but transaction is active");
5021 }
5022
5023 /* Disconnect causes rollback in the following cases:
5024 - trx is not started, or
5025 - trx is in *not* in PREPARED state, or
5026 - trx has not updated any persistent data.
5027 TODO/FIXME: it does not make sense to initiate rollback
5028 in the 1st and 3rd case. */
5029 if (trx_is_started(trx)) {
5030 if (trx_state_eq(trx, TRX_STATE_PREPARED)) {
5031 if (trx->has_logged_persistent()) {
5032 trx_disconnect_prepared(trx);
5033 } else {
5034 trx_deregister_from_2pc(trx);
5035 goto rollback_and_free;
5036 }
5037 } else {
5038 sql_print_warning(
5039 "MariaDB is closing a connection that has an active "
5040 "InnoDB transaction. " TRX_ID_FMT " row modifications "
5041 "will roll back.",
5042 trx->undo_no);
5043 goto rollback_and_free;
5044 }
5045 } else {
5046rollback_and_free:
5047 innobase_rollback_trx(trx);
5048 trx_free(trx);
5049 }
5050 }
5051
5052 DBUG_RETURN(0);
5053}
5054
5055UNIV_INTERN void lock_cancel_waiting_and_release(lock_t* lock);
5056
5057/** Cancel any pending lock request associated with the current THD.
5058@sa THD::awake() @sa ha_kill_query() */
5059static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
5060{
5061 DBUG_ENTER("innobase_kill_query");
5062#ifdef WITH_WSREP
5063 if (wsrep_thd_get_conflict_state(thd) != NO_CONFLICT) {
5064 /* if victim has been signaled by BF thread and/or aborting
5065 is already progressing, following query aborting is not necessary
5066 any more.
5067 Also, BF thread should own trx mutex for the victim, which would
5068 conflict with trx_mutex_enter() below
5069 */
5070 DBUG_VOID_RETURN;
5071 }
5072#endif /* WITH_WSREP */
5073
5074 if (trx_t* trx = thd_to_trx(thd)) {
5075 ut_ad(trx->mysql_thd == thd);
5076 /* Cancel a pending lock request if there are any */
5077 lock_trx_handle_wait(trx);
5078 }
5079
5080 DBUG_VOID_RETURN;
5081}
5082
5083
5084/*************************************************************************//**
5085** InnoDB database tables
5086*****************************************************************************/
5087
5088/** Get the record format from the data dictionary.
5089@return one of ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT,
5090ROW_TYPE_COMPRESSED, ROW_TYPE_DYNAMIC */
5091
5092enum row_type
5093ha_innobase::get_row_type() const
5094{
5095 if (m_prebuilt && m_prebuilt->table) {
5096 const ulint flags = m_prebuilt->table->flags;
5097
5098 switch (dict_tf_get_rec_format(flags)) {
5099 case REC_FORMAT_REDUNDANT:
5100 return(ROW_TYPE_REDUNDANT);
5101 case REC_FORMAT_COMPACT:
5102 return(ROW_TYPE_COMPACT);
5103 case REC_FORMAT_COMPRESSED:
5104 return(ROW_TYPE_COMPRESSED);
5105 case REC_FORMAT_DYNAMIC:
5106 return(ROW_TYPE_DYNAMIC);
5107 }
5108 }
5109 ut_ad(0);
5110 return(ROW_TYPE_NOT_USED);
5111}
5112
5113/****************************************************************//**
5114Get the table flags to use for the statement.
5115@return table flags */
5116
5117handler::Table_flags
5118ha_innobase::table_flags() const
5119/*============================*/
5120{
5121 THD* thd = ha_thd();
5122 handler::Table_flags flags = m_int_table_flags;
5123
5124 /* Need to use tx_isolation here since table flags is (also)
5125 called before prebuilt is inited. */
5126
5127 if (thd_tx_isolation(thd) <= ISO_READ_COMMITTED) {
5128 return(flags);
5129 }
5130
5131 return(flags | HA_BINLOG_STMT_CAPABLE);
5132}
5133
5134/****************************************************************//**
5135Returns the table type (storage engine name).
5136@return table type */
5137
5138const char*
5139ha_innobase::table_type() const
5140/*===========================*/
5141{
5142 return(innobase_hton_name);
5143}
5144
5145/****************************************************************//**
5146Returns the index type.
5147@return index type */
5148
5149const char*
5150ha_innobase::index_type(
5151/*====================*/
5152 uint keynr) /*!< : index number */
5153{
5154 dict_index_t* index = innobase_get_index(keynr);
5155
5156 if (index && index->type & DICT_FTS) {
5157 return("FULLTEXT");
5158 } else if (dict_index_is_spatial(index)) {
5159 return("SPATIAL");
5160 } else {
5161 return("BTREE");
5162 }
5163}
5164
5165/****************************************************************//**
5166Returns the table file name extension.
5167@return file extension string */
5168
5169const char**
5170ha_innobase::bas_ext() const
5171/*========================*/
5172{
5173 return(ha_innobase_exts);
5174}
5175
5176/****************************************************************//**
5177Returns the operations supported for indexes.
5178@return flags of supported operations */
5179
5180ulong
5181ha_innobase::index_flags(
5182/*=====================*/
5183 uint key,
5184 uint,
5185 bool) const
5186{
5187 if (table_share->key_info[key].algorithm == HA_KEY_ALG_FULLTEXT) {
5188 return(0);
5189 }
5190
5191 ulong extra_flag= 0;
5192
5193 if (table && key == table->s->primary_key) {
5194 extra_flag= HA_CLUSTERED_INDEX;
5195 }
5196
5197 ulong flags = HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER
5198 | HA_READ_RANGE | HA_KEYREAD_ONLY
5199 | extra_flag
5200 | HA_DO_INDEX_COND_PUSHDOWN;
5201
5202 /* For spatial index, we don't support descending scan
5203 and ICP so far. */
5204 if (table_share->key_info[key].flags & HA_SPATIAL) {
5205 flags = HA_READ_NEXT | HA_READ_ORDER| HA_READ_RANGE
5206 | HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR;
5207 }
5208
5209 return(flags);
5210}
5211
5212/****************************************************************//**
5213Returns the maximum number of keys.
5214@return MAX_KEY */
5215
5216uint
5217ha_innobase::max_supported_keys() const
5218/*===================================*/
5219{
5220 return(MAX_KEY);
5221}
5222
5223/****************************************************************//**
5224Returns the maximum key length.
5225@return maximum supported key length, in bytes */
5226
5227uint
5228ha_innobase::max_supported_key_length() const
5229/*=========================================*/
5230{
5231 /* An InnoDB page must store >= 2 keys; a secondary key record
5232 must also contain the primary key value. Therefore, if both
5233 the primary key and the secondary key are at this maximum length,
5234 it must be less than 1/4th of the free space on a page including
5235 record overhead.
5236
5237 MySQL imposes its own limit to this number; MAX_KEY_LENGTH = 3072.
5238
5239 For page sizes = 16k, InnoDB historically reported 3500 bytes here,
5240 But the MySQL limit of 3072 was always used through the handler
5241 interface.
5242
5243 Note: Handle 16k and 32k pages the same here since the limits
5244 are higher than imposed by MySQL. */
5245
5246 switch (srv_page_size) {
5247 case 4096:
5248 return(768);
5249 case 8192:
5250 return(1536);
5251 default:
5252#ifdef WITH_WSREP
5253 return(3500);
5254#else
5255 return(3500);
5256#endif
5257 }
5258}
5259
5260/****************************************************************//**
5261Returns the key map of keys that are usable for scanning.
5262@return key_map_full */
5263
5264const key_map*
5265ha_innobase::keys_to_use_for_scanning()
5266/*===================================*/
5267{
5268 return(&key_map_full);
5269}
5270
5271/****************************************************************//**
5272Ensures that if there's a concurrent inplace ADD INDEX, being-indexed virtual
5273columns are computed. They are not marked as indexed in the old table, so the
5274server won't add them to the vcol_set automatically */
5275void
5276ha_innobase::column_bitmaps_signal()
5277/*================================*/
5278{
5279 if (!table->vfield || table->current_lock != F_WRLCK) {
5280 return;
5281 }
5282
5283 dict_index_t* clust_index = dict_table_get_first_index(m_prebuilt->table);
5284 uint num_v = 0;
5285 for (uint j = 0; j < table->s->virtual_fields; j++) {
5286 if (table->vfield[j]->stored_in_db()) {
5287 continue;
5288 }
5289
5290 dict_col_t* col = &m_prebuilt->table->v_cols[num_v].m_col;
5291 if (col->ord_part ||
5292 (dict_index_is_online_ddl(clust_index) &&
5293 row_log_col_is_indexed(clust_index, num_v))) {
5294 table->mark_virtual_col(table->vfield[j]);
5295 }
5296 num_v++;
5297 }
5298}
5299
5300
5301/****************************************************************//**
5302Determines if table caching is supported.
5303@return HA_CACHE_TBL_ASKTRANSACT */
5304
5305uint8
5306ha_innobase::table_cache_type()
5307/*===========================*/
5308{
5309 return(HA_CACHE_TBL_ASKTRANSACT);
5310}
5311
5312/****************************************************************//**
5313Determines if the primary key is clustered index.
5314@return true */
5315
5316bool
5317ha_innobase::primary_key_is_clustered()
5318/*===================================*/
5319{
5320 return(true);
5321}
5322
5323/** Normalizes a table name string.
5324A normalized name consists of the database name catenated to '/'
5325and table name. For example: test/mytable.
5326On Windows, normalization puts both the database name and the
5327table name always to lower case if "set_lower_case" is set to TRUE.
5328@param[out] norm_name Normalized name, null-terminated.
5329@param[in] name Name to normalize.
5330@param[in] set_lower_case True if we also should fold to lower case. */
5331void
5332normalize_table_name_c_low(
5333/*=======================*/
5334 char* norm_name, /* out: normalized name as a
5335 null-terminated string */
5336 const char* name, /* in: table name string */
5337 ibool set_lower_case) /* in: TRUE if we want to set
5338 name to lower case */
5339{
5340 char* name_ptr;
5341 ulint name_len;
5342 char* db_ptr;
5343 ulint db_len;
5344 char* ptr;
5345 ulint norm_len;
5346
5347 /* Scan name from the end */
5348
5349 ptr = strend(name) - 1;
5350
5351 /* seek to the last path separator */
5352 while (ptr >= name && *ptr != '\\' && *ptr != '/') {
5353 ptr--;
5354 }
5355
5356 name_ptr = ptr + 1;
5357 name_len = strlen(name_ptr);
5358
5359 /* skip any number of path separators */
5360 while (ptr >= name && (*ptr == '\\' || *ptr == '/')) {
5361 ptr--;
5362 }
5363
5364 DBUG_ASSERT(ptr >= name);
5365
5366 /* seek to the last but one path separator or one char before
5367 the beginning of name */
5368 db_len = 0;
5369 while (ptr >= name && *ptr != '\\' && *ptr != '/') {
5370 ptr--;
5371 db_len++;
5372 }
5373
5374 db_ptr = ptr + 1;
5375
5376 norm_len = db_len + name_len + sizeof "/";
5377 ut_a(norm_len < FN_REFLEN - 1);
5378
5379 memcpy(norm_name, db_ptr, db_len);
5380
5381 norm_name[db_len] = '/';
5382
5383 /* Copy the name and null-byte. */
5384 memcpy(norm_name + db_len + 1, name_ptr, name_len + 1);
5385
5386 if (set_lower_case) {
5387 innobase_casedn_str(norm_name);
5388 }
5389}
5390
5391/** Normalizes a table name string.
5392A normalized name consists of the database name catenated to '/'
5393and table name. For example: test/mytable.
5394On Windows, normalization puts both the database name and the
5395table name always to lower case if "set_lower_case" is set to TRUE.
5396@param[out] norm_name Normalized name, null-terminated.
5397@param[in] name Name to normalize.
5398@param[in] set_lower_case True if we also should fold to lower case. */
5399void
5400create_table_info_t::normalize_table_name_low(
5401 char* norm_name,
5402 const char* name,
5403 ibool set_lower_case)
5404{
5405 normalize_table_name_c_low(norm_name, name, set_lower_case);
5406}
5407
5408#if !defined(DBUG_OFF)
5409/*********************************************************************
5410Test normalize_table_name_low(). */
5411static
5412void
5413test_normalize_table_name_low()
5414/*===========================*/
5415{
5416 char norm_name[FN_REFLEN];
5417 const char* test_data[][2] = {
5418 /* input, expected result */
5419 {"./mysqltest/t1", "mysqltest/t1"},
5420 {"./test/#sql-842b_2", "test/#sql-842b_2"},
5421 {"./test/#sql-85a3_10", "test/#sql-85a3_10"},
5422 {"./test/#sql2-842b-2", "test/#sql2-842b-2"},
5423 {"./test/bug29807", "test/bug29807"},
5424 {"./test/foo", "test/foo"},
5425 {"./test/innodb_bug52663", "test/innodb_bug52663"},
5426 {"./test/t", "test/t"},
5427 {"./test/t1", "test/t1"},
5428 {"./test/t10", "test/t10"},
5429 {"/a/b/db/table", "db/table"},
5430 {"/a/b/db///////table", "db/table"},
5431 {"/a/b////db///////table", "db/table"},
5432 {"/var/tmp/mysqld.1/#sql842b_2_10", "mysqld.1/#sql842b_2_10"},
5433 {"db/table", "db/table"},
5434 {"ddd/t", "ddd/t"},
5435 {"d/ttt", "d/ttt"},
5436 {"d/t", "d/t"},
5437 {".\\mysqltest\\t1", "mysqltest/t1"},
5438 {".\\test\\#sql-842b_2", "test/#sql-842b_2"},
5439 {".\\test\\#sql-85a3_10", "test/#sql-85a3_10"},
5440 {".\\test\\#sql2-842b-2", "test/#sql2-842b-2"},
5441 {".\\test\\bug29807", "test/bug29807"},
5442 {".\\test\\foo", "test/foo"},
5443 {".\\test\\innodb_bug52663", "test/innodb_bug52663"},
5444 {".\\test\\t", "test/t"},
5445 {".\\test\\t1", "test/t1"},
5446 {".\\test\\t10", "test/t10"},
5447 {"C:\\a\\b\\db\\table", "db/table"},
5448 {"C:\\a\\b\\db\\\\\\\\\\\\\\table", "db/table"},
5449 {"C:\\a\\b\\\\\\\\db\\\\\\\\\\\\\\table", "db/table"},
5450 {"C:\\var\\tmp\\mysqld.1\\#sql842b_2_10", "mysqld.1/#sql842b_2_10"},
5451 {"db\\table", "db/table"},
5452 {"ddd\\t", "ddd/t"},
5453 {"d\\ttt", "d/ttt"},
5454 {"d\\t", "d/t"},
5455 };
5456
5457 for (size_t i = 0; i < UT_ARR_SIZE(test_data); i++) {
5458 printf("test_normalize_table_name_low():"
5459 " testing \"%s\", expected \"%s\"... ",
5460 test_data[i][0], test_data[i][1]);
5461
5462 create_table_info_t::normalize_table_name_low(
5463 norm_name, test_data[i][0], FALSE);
5464
5465 if (strcmp(norm_name, test_data[i][1]) == 0) {
5466 printf("ok\n");
5467 } else {
5468 printf("got \"%s\"\n", norm_name);
5469 ut_error;
5470 }
5471 }
5472}
5473
5474/*********************************************************************
5475Test ut_format_name(). */
5476static
5477void
5478test_ut_format_name()
5479/*=================*/
5480{
5481 char buf[NAME_LEN * 3];
5482
5483 struct {
5484 const char* name;
5485 ulint buf_size;
5486 const char* expected;
5487 } test_data[] = {
5488 {"test/t1", sizeof(buf), "`test`.`t1`"},
5489 {"test/t1", 12, "`test`.`t1`"},
5490 {"test/t1", 11, "`test`.`t1"},
5491 {"test/t1", 10, "`test`.`t"},
5492 {"test/t1", 9, "`test`.`"},
5493 {"test/t1", 8, "`test`."},
5494 {"test/t1", 7, "`test`"},
5495 {"test/t1", 6, "`test"},
5496 {"test/t1", 5, "`tes"},
5497 {"test/t1", 4, "`te"},
5498 {"test/t1", 3, "`t"},
5499 {"test/t1", 2, "`"},
5500 {"test/t1", 1, ""},
5501 {"test/t1", 0, "BUF_NOT_CHANGED"},
5502 {"table", sizeof(buf), "`table`"},
5503 {"ta'le", sizeof(buf), "`ta'le`"},
5504 {"ta\"le", sizeof(buf), "`ta\"le`"},
5505 {"ta`le", sizeof(buf), "`ta``le`"},
5506 };
5507
5508 for (size_t i = 0; i < UT_ARR_SIZE(test_data); i++) {
5509
5510 memcpy(buf, "BUF_NOT_CHANGED", strlen("BUF_NOT_CHANGED") + 1);
5511
5512 char* ret;
5513
5514 ret = ut_format_name(test_data[i].name,
5515 buf,
5516 test_data[i].buf_size);
5517
5518 ut_a(ret == buf);
5519
5520 if (strcmp(buf, test_data[i].expected) == 0) {
5521 ib::info() << "ut_format_name(" << test_data[i].name
5522 << ", buf, " << test_data[i].buf_size << "),"
5523 " expected " << test_data[i].expected
5524 << ", OK";
5525 } else {
5526 ib::error() << "ut_format_name(" << test_data[i].name
5527 << ", buf, " << test_data[i].buf_size << "),"
5528 " expected " << test_data[i].expected
5529 << ", ERROR: got " << buf;
5530 ut_error;
5531 }
5532 }
5533}
5534#endif /* !DBUG_OFF */
5535
5536/** Match index columns between MySQL and InnoDB.
5537This function checks whether the index column information
5538is consistent between KEY info from mysql and that from innodb index.
5539@param[in] key_info Index info from mysql
5540@param[in] index_info Index info from InnoDB
5541@return true if all column types match. */
5542bool
5543innobase_match_index_columns(
5544 const KEY* key_info,
5545 const dict_index_t* index_info)
5546{
5547 const KEY_PART_INFO* key_part;
5548 const KEY_PART_INFO* key_end;
5549 const dict_field_t* innodb_idx_fld;
5550 const dict_field_t* innodb_idx_fld_end;
5551
5552 DBUG_ENTER("innobase_match_index_columns");
5553
5554 /* Check whether user defined index column count matches */
5555 if (key_info->user_defined_key_parts !=
5556 index_info->n_user_defined_cols) {
5557 DBUG_RETURN(FALSE);
5558 }
5559
5560 key_part = key_info->key_part;
5561 key_end = key_part + key_info->user_defined_key_parts;
5562 innodb_idx_fld = index_info->fields;
5563 innodb_idx_fld_end = index_info->fields + index_info->n_fields;
5564
5565 /* Check each index column's datatype. We do not check
5566 column name because there exists case that index
5567 column name got modified in mysql but such change does not
5568 propagate to InnoDB.
5569 One hidden assumption here is that the index column sequences
5570 are matched up between those in mysql and InnoDB. */
5571 for (; key_part != key_end; ++key_part) {
5572 ulint col_type;
5573 ibool is_unsigned;
5574 ulint mtype = innodb_idx_fld->col->mtype;
5575
5576 /* Need to translate to InnoDB column type before
5577 comparison. */
5578 col_type = get_innobase_type_from_mysql_type(
5579 &is_unsigned, key_part->field);
5580
5581 /* Ignore InnoDB specific system columns. */
5582 while (mtype == DATA_SYS) {
5583 innodb_idx_fld++;
5584
5585 if (innodb_idx_fld >= innodb_idx_fld_end) {
5586 DBUG_RETURN(FALSE);
5587 }
5588 }
5589
5590 /* MariaDB-5.5 compatibility */
5591 if ((key_part->field->real_type() == MYSQL_TYPE_ENUM ||
5592 key_part->field->real_type() == MYSQL_TYPE_SET) &&
5593 mtype == DATA_FIXBINARY) {
5594 col_type= DATA_FIXBINARY;
5595 }
5596
5597 if (col_type != mtype) {
5598 /* If the col_type we get from mysql type is a geometry
5599 data type, we should check if mtype is a legacy type
5600 from 5.6, either upgraded to DATA_GEOMETRY or not.
5601 This is indeed not an accurate check, but should be
5602 safe, since DATA_BLOB would be upgraded once we create
5603 spatial index on it and we intend to use DATA_GEOMETRY
5604 for legacy GIS data types which are of var-length. */
5605 switch (col_type) {
5606 case DATA_GEOMETRY:
5607 if (mtype == DATA_BLOB) {
5608 break;
5609 }
5610 /* Fall through */
5611 default:
5612 /* Column type mismatches */
5613 DBUG_RETURN(false);
5614 }
5615 }
5616
5617 innodb_idx_fld++;
5618 }
5619
5620 DBUG_RETURN(TRUE);
5621}
5622
5623/** Build a template for a base column for a virtual column
5624@param[in] table MySQL TABLE
5625@param[in] clust_index InnoDB clustered index
5626@param[in] field field in MySQL table
5627@param[in] col InnoDB column
5628@param[in,out] templ template to fill
5629@param[in] col_no field index for virtual col
5630*/
5631static
5632void
5633innobase_vcol_build_templ(
5634 const TABLE* table,
5635 dict_index_t* clust_index,
5636 Field* field,
5637 const dict_col_t* col,
5638 mysql_row_templ_t* templ,
5639 ulint col_no)
5640{
5641 if (col->is_virtual()) {
5642 templ->is_virtual = true;
5643 templ->col_no = col_no;
5644 templ->clust_rec_field_no = ULINT_UNDEFINED;
5645 templ->rec_field_no = col->ind;
5646 } else {
5647 templ->is_virtual = false;
5648 templ->col_no = col_no;
5649 templ->clust_rec_field_no = dict_col_get_clust_pos(
5650 col, clust_index);
5651 ut_a(templ->clust_rec_field_no != ULINT_UNDEFINED);
5652
5653 templ->rec_field_no = templ->clust_rec_field_no;
5654 }
5655
5656 if (field->real_maybe_null()) {
5657 templ->mysql_null_byte_offset =
5658 field->null_offset();
5659
5660 templ->mysql_null_bit_mask = (ulint) field->null_bit;
5661 } else {
5662 templ->mysql_null_bit_mask = 0;
5663 }
5664
5665 templ->mysql_col_offset = static_cast<ulint>(
5666 get_field_offset(table, field));
5667 templ->mysql_col_len = static_cast<ulint>(field->pack_length());
5668 templ->type = col->mtype;
5669 templ->mysql_type = static_cast<ulint>(field->type());
5670
5671 if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
5672 templ->mysql_length_bytes = static_cast<ulint>(
5673 ((Field_varstring*) field)->length_bytes);
5674 }
5675
5676 templ->charset = dtype_get_charset_coll(col->prtype);
5677 templ->mbminlen = dict_col_get_mbminlen(col);
5678 templ->mbmaxlen = dict_col_get_mbmaxlen(col);
5679 templ->is_unsigned = col->prtype & DATA_UNSIGNED;
5680}
5681
5682/** Build template for the virtual columns and their base columns. This
5683is done when the table first opened.
5684@param[in] table MySQL TABLE
5685@param[in] ib_table InnoDB dict_table_t
5686@param[in,out] s_templ InnoDB template structure
5687@param[in] add_v new virtual columns added along with
5688 add index call
5689@param[in] locked true if dict_sys mutex is held */
5690void
5691innobase_build_v_templ(
5692 const TABLE* table,
5693 const dict_table_t* ib_table,
5694 dict_vcol_templ_t* s_templ,
5695 const dict_add_v_col_t* add_v,
5696 bool locked)
5697{
5698 ulint ncol = unsigned(ib_table->n_cols) - DATA_N_SYS_COLS;
5699 ulint n_v_col = ib_table->n_v_cols;
5700 bool marker[REC_MAX_N_FIELDS];
5701
5702 ut_ad(ncol < REC_MAX_N_FIELDS);
5703
5704 if (add_v != NULL) {
5705 n_v_col += add_v->n_v_col;
5706 }
5707
5708 ut_ad(n_v_col > 0);
5709
5710 if (!locked) {
5711 mutex_enter(&dict_sys->mutex);
5712 }
5713
5714 if (s_templ->vtempl) {
5715 if (!locked) {
5716 mutex_exit(&dict_sys->mutex);
5717 }
5718 return;
5719 }
5720
5721 memset(marker, 0, sizeof(bool) * ncol);
5722
5723 s_templ->vtempl = static_cast<mysql_row_templ_t**>(
5724 ut_zalloc_nokey((ncol + n_v_col)
5725 * sizeof *s_templ->vtempl));
5726 s_templ->n_col = ncol;
5727 s_templ->n_v_col = n_v_col;
5728 s_templ->rec_len = table->s->reclength;
5729 s_templ->default_rec = table->s->default_values;
5730
5731 /* Mark those columns could be base columns */
5732 for (ulint i = 0; i < ib_table->n_v_cols; i++) {
5733 const dict_v_col_t* vcol = dict_table_get_nth_v_col(
5734 ib_table, i);
5735
5736 for (ulint j = 0; j < vcol->num_base; j++) {
5737 ulint col_no = vcol->base_col[j]->ind;
5738 marker[col_no] = true;
5739 }
5740 }
5741
5742 if (add_v) {
5743 for (ulint i = 0; i < add_v->n_v_col; i++) {
5744 const dict_v_col_t* vcol = &add_v->v_col[i];
5745
5746 for (ulint j = 0; j < vcol->num_base; j++) {
5747 ulint col_no = vcol->base_col[j]->ind;
5748 marker[col_no] = true;
5749 }
5750 }
5751 }
5752
5753 ulint j = 0;
5754 ulint z = 0;
5755
5756 dict_index_t* clust_index = dict_table_get_first_index(ib_table);
5757
5758 for (ulint i = 0; i < table->s->fields; i++) {
5759 Field* field = table->field[i];
5760
5761 /* Build template for virtual columns */
5762 if (innobase_is_v_fld(field)) {
5763#ifdef UNIV_DEBUG
5764 const char* name;
5765
5766 if (z >= ib_table->n_v_def) {
5767 name = add_v->v_col_name[z - ib_table->n_v_def];
5768 } else {
5769 name = dict_table_get_v_col_name(ib_table, z);
5770 }
5771
5772 ut_ad(!ut_strcmp(name, field->field_name.str));
5773#endif
5774 const dict_v_col_t* vcol;
5775
5776 if (z >= ib_table->n_v_def) {
5777 vcol = &add_v->v_col[z - ib_table->n_v_def];
5778 } else {
5779 vcol = dict_table_get_nth_v_col(ib_table, z);
5780 }
5781
5782 s_templ->vtempl[z + s_templ->n_col]
5783 = static_cast<mysql_row_templ_t*>(
5784 ut_malloc_nokey(
5785 sizeof *s_templ->vtempl[j]));
5786
5787 innobase_vcol_build_templ(
5788 table, clust_index, field,
5789 &vcol->m_col,
5790 s_templ->vtempl[z + s_templ->n_col],
5791 z);
5792 z++;
5793 continue;
5794 }
5795
5796 ut_ad(j < ncol);
5797
5798 /* Build template for base columns */
5799 if (marker[j]) {
5800 dict_col_t* col = dict_table_get_nth_col(
5801 ib_table, j);
5802
5803#ifdef UNIV_DEBUG
5804 const char* name = dict_table_get_col_name(
5805 ib_table, j);
5806
5807 ut_ad(!ut_strcmp(name, field->field_name.str));
5808#endif
5809
5810 s_templ->vtempl[j] = static_cast<
5811 mysql_row_templ_t*>(
5812 ut_malloc_nokey(
5813 sizeof *s_templ->vtempl[j]));
5814
5815 innobase_vcol_build_templ(
5816 table, clust_index, field, col,
5817 s_templ->vtempl[j], j);
5818 }
5819
5820 j++;
5821 }
5822
5823 if (!locked) {
5824 mutex_exit(&dict_sys->mutex);
5825 }
5826
5827 s_templ->db_name = table->s->db.str;
5828 s_templ->tb_name = table->s->table_name.str;
5829}
5830
5831/*******************************************************************//**
5832This function builds a translation table in INNOBASE_SHARE
5833structure for fast index location with mysql array number from its
5834table->key_info structure. This also provides the necessary translation
5835between the key order in mysql key_info and InnoDB ib_table->indexes if
5836they are not fully matched with each other.
5837Note we do not have any mutex protecting the translation table
5838building based on the assumption that there is no concurrent
5839index creation/drop and DMLs that requires index lookup. All table
5840handle will be closed before the index creation/drop.
5841@return true if index translation table built successfully */
5842static
5843bool
5844innobase_build_index_translation(
5845/*=============================*/
5846 const TABLE* table, /*!< in: table in MySQL data
5847 dictionary */
5848 dict_table_t* ib_table,/*!< in: table in InnoDB data
5849 dictionary */
5850 INNOBASE_SHARE* share) /*!< in/out: share structure
5851 where index translation table
5852 will be constructed in. */
5853{
5854 DBUG_ENTER("innobase_build_index_translation");
5855
5856 bool ret = true;
5857
5858 mutex_enter(&dict_sys->mutex);
5859
5860 ulint mysql_num_index = table->s->keys;
5861 ulint ib_num_index = UT_LIST_GET_LEN(ib_table->indexes);
5862 dict_index_t** index_mapping = share->idx_trans_tbl.index_mapping;
5863
5864 /* If there exists inconsistency between MySQL and InnoDB dictionary
5865 (metadata) information, the number of index defined in MySQL
5866 could exceed that in InnoDB, do not build index translation
5867 table in such case */
5868 if (ib_num_index < mysql_num_index) {
5869 ret = false;
5870 goto func_exit;
5871 }
5872
5873 /* If index entry count is non-zero, nothing has
5874 changed since last update, directly return TRUE */
5875 if (share->idx_trans_tbl.index_count) {
5876 /* Index entry count should still match mysql_num_index */
5877 ut_a(share->idx_trans_tbl.index_count == mysql_num_index);
5878 goto func_exit;
5879 }
5880
5881 /* The number of index increased, rebuild the mapping table */
5882 if (mysql_num_index > share->idx_trans_tbl.array_size) {
5883
5884 index_mapping = reinterpret_cast<dict_index_t**>(
5885 ut_realloc(index_mapping,
5886 mysql_num_index * sizeof(*index_mapping)));
5887
5888 if (index_mapping == NULL) {
5889 /* Report an error if index_mapping continues to be
5890 NULL and mysql_num_index is a non-zero value */
5891 sql_print_error("InnoDB: fail to allocate memory for "
5892 "index translation table. Number of "
5893 "Index: " ULINTPF
5894 ", array size:" ULINTPF,
5895 mysql_num_index,
5896 share->idx_trans_tbl.array_size);
5897 ret = false;
5898 goto func_exit;
5899 }
5900
5901 share->idx_trans_tbl.array_size = mysql_num_index;
5902 }
5903
5904 /* For each index in the mysql key_info array, fetch its
5905 corresponding InnoDB index pointer into index_mapping
5906 array. */
5907 for (ulint count = 0; count < mysql_num_index; count++) {
5908
5909 /* Fetch index pointers into index_mapping according to mysql
5910 index sequence */
5911 index_mapping[count] = dict_table_get_index_on_name(
5912 ib_table, table->key_info[count].name.str);
5913
5914 if (index_mapping[count] == 0) {
5915 sql_print_error("Cannot find index %s in InnoDB"
5916 " index dictionary.",
5917 table->key_info[count].name.str);
5918 ret = false;
5919 goto func_exit;
5920 }
5921
5922 /* Double check fetched index has the same
5923 column info as those in mysql key_info. */
5924 if (!innobase_match_index_columns(&table->key_info[count],
5925 index_mapping[count])) {
5926 sql_print_error("Found index %s whose column info"
5927 " does not match that of MariaDB.",
5928 table->key_info[count].name.str);
5929 ret = false;
5930 goto func_exit;
5931 }
5932 }
5933
5934 /* Successfully built the translation table */
5935 share->idx_trans_tbl.index_count = mysql_num_index;
5936
5937func_exit:
5938 if (!ret) {
5939 /* Build translation table failed. */
5940 ut_free(index_mapping);
5941
5942 share->idx_trans_tbl.array_size = 0;
5943 share->idx_trans_tbl.index_count = 0;
5944 index_mapping = NULL;
5945 }
5946
5947 share->idx_trans_tbl.index_mapping = index_mapping;
5948
5949 mutex_exit(&dict_sys->mutex);
5950
5951 DBUG_RETURN(ret);
5952}
5953
5954/*******************************************************************//**
5955This function uses index translation table to quickly locate the
5956requested index structure.
5957Note we do not have mutex protection for the index translatoin table
5958access, it is based on the assumption that there is no concurrent
5959translation table rebuild (fter create/drop index) and DMLs that
5960require index lookup.
5961@return dict_index_t structure for requested index. NULL if
5962fail to locate the index structure. */
5963static
5964dict_index_t*
5965innobase_index_lookup(
5966/*==================*/
5967 INNOBASE_SHARE* share, /*!< in: share structure for index
5968 translation table. */
5969 uint keynr) /*!< in: index number for the requested
5970 index */
5971{
5972 if (share->idx_trans_tbl.index_mapping == NULL
5973 || keynr >= share->idx_trans_tbl.index_count) {
5974 return(NULL);
5975 }
5976
5977 return(share->idx_trans_tbl.index_mapping[keynr]);
5978}
5979/********************************************************************//**
5980Get the upper limit of the MySQL integral and floating-point type.
5981@return maximum allowed value for the field */
5982UNIV_INTERN
5983ulonglong
5984innobase_get_int_col_max_value(
5985/*===========================*/
5986 const Field* field) /*!< in: MySQL field */
5987{
5988 ulonglong max_value = 0;
5989
5990 switch (field->key_type()) {
5991 /* TINY */
5992 case HA_KEYTYPE_BINARY:
5993 max_value = 0xFFULL;
5994 break;
5995 case HA_KEYTYPE_INT8:
5996 max_value = 0x7FULL;
5997 break;
5998 /* SHORT */
5999 case HA_KEYTYPE_USHORT_INT:
6000 max_value = 0xFFFFULL;
6001 break;
6002 case HA_KEYTYPE_SHORT_INT:
6003 max_value = 0x7FFFULL;
6004 break;
6005 /* MEDIUM */
6006 case HA_KEYTYPE_UINT24:
6007 max_value = 0xFFFFFFULL;
6008 break;
6009 case HA_KEYTYPE_INT24:
6010 max_value = 0x7FFFFFULL;
6011 break;
6012 /* LONG */
6013 case HA_KEYTYPE_ULONG_INT:
6014 max_value = 0xFFFFFFFFULL;
6015 break;
6016 case HA_KEYTYPE_LONG_INT:
6017 max_value = 0x7FFFFFFFULL;
6018 break;
6019 /* BIG */
6020 case HA_KEYTYPE_ULONGLONG:
6021 max_value = 0xFFFFFFFFFFFFFFFFULL;
6022 break;
6023 case HA_KEYTYPE_LONGLONG:
6024 max_value = 0x7FFFFFFFFFFFFFFFULL;
6025 break;
6026 case HA_KEYTYPE_FLOAT:
6027 /* We use the maximum as per IEEE754-2008 standard, 2^24 */
6028 max_value = 0x1000000ULL;
6029 break;
6030 case HA_KEYTYPE_DOUBLE:
6031 /* We use the maximum as per IEEE754-2008 standard, 2^53 */
6032 max_value = 0x20000000000000ULL;
6033 break;
6034 default:
6035 ut_error;
6036 }
6037
6038 return(max_value);
6039}
6040
6041/** Initialize the AUTO_INCREMENT column metadata.
6042
6043Since a partial table definition for a persistent table can already be
6044present in the InnoDB dict_sys cache before it is accessed from SQL,
6045we have to initialize the AUTO_INCREMENT counter on the first
6046ha_innobase::open().
6047
6048@param[in,out] table persistent table
6049@param[in] field the AUTO_INCREMENT column */
6050static
6051void
6052initialize_auto_increment(dict_table_t* table, const Field* field)
6053{
6054 ut_ad(!table->is_temporary());
6055
6056 const unsigned col_no = innodb_col_no(field);
6057
6058 dict_table_autoinc_lock(table);
6059
6060 table->persistent_autoinc = 1
6061 + dict_table_get_nth_col_pos(table, col_no, NULL);
6062
6063 if (table->autoinc) {
6064 /* Already initialized. Our caller checked
6065 table->persistent_autoinc without
6066 dict_table_autoinc_lock(), and there might be multiple
6067 ha_innobase::open() executing concurrently. */
6068 } else if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
6069 /* If the recovery level is set so high that writes
6070 are disabled we force the AUTOINC counter to 0
6071 value effectively disabling writes to the table.
6072 Secondly, we avoid reading the table in case the read
6073 results in failure due to a corrupted table/index.
6074
6075 We will not return an error to the client, so that the
6076 tables can be dumped with minimal hassle. If an error
6077 were returned in this case, the first attempt to read
6078 the table would fail and subsequent SELECTs would succeed. */
6079 } else if (table->persistent_autoinc) {
6080 table->autoinc = innobase_next_autoinc(
6081 btr_read_autoinc_with_fallback(table, col_no),
6082 1 /* need */,
6083 1 /* auto_increment_increment */,
6084 0 /* auto_increment_offset */,
6085 innobase_get_int_col_max_value(field));
6086 }
6087
6088 dict_table_autoinc_unlock(table);
6089}
6090
6091/** Open an InnoDB table
6092@param[in] name table name
6093@return error code
6094@retval 0 on success */
6095int
6096ha_innobase::open(const char* name, int, uint)
6097{
6098 dict_table_t* ib_table;
6099 char norm_name[FN_REFLEN];
6100 dict_err_ignore_t ignore_err = DICT_ERR_IGNORE_NONE;
6101
6102 DBUG_ENTER("ha_innobase::open");
6103
6104 normalize_table_name(norm_name, name);
6105
6106 m_user_thd = NULL;
6107
6108 if (!(m_share = get_share(name))) {
6109
6110 DBUG_RETURN(1);
6111 }
6112
6113 /* Will be allocated if it is needed in ::update_row() */
6114 m_upd_buf = NULL;
6115 m_upd_buf_size = 0;
6116
6117 char* is_part = is_partition(norm_name);
6118 THD* thd = ha_thd();
6119
6120 /* Check whether FOREIGN_KEY_CHECKS is set to 0. If so, the table
6121 can be opened even if some FK indexes are missing. If not, the table
6122 can't be opened in the same situation */
6123 if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
6124 ignore_err = DICT_ERR_IGNORE_FK_NOKEY;
6125 }
6126
6127 ib_table = open_dict_table(name, norm_name, is_part, ignore_err);
6128
6129 if (NULL == ib_table) {
6130
6131 if (is_part) {
6132 sql_print_error("Failed to open table %s.\n",
6133 norm_name);
6134 }
6135no_such_table:
6136 free_share(m_share);
6137 set_my_errno(ENOENT);
6138
6139 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
6140 }
6141
6142 size_t n_fields = mysql_fields(table);
6143 size_t n_cols = dict_table_get_n_user_cols(ib_table)
6144 + dict_table_get_n_v_cols(ib_table)
6145 - !!DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID);
6146
6147 if (n_cols != n_fields) {
6148 ib::warn() << "Table " << norm_name << " contains "
6149 << n_cols << " user"
6150 " defined columns in InnoDB, but " << n_fields
6151 << " columns in MariaDB. Please check"
6152 " INFORMATION_SCHEMA.INNODB_SYS_COLUMNS and " REFMAN
6153 "innodb-troubleshooting.html for how to resolve the"
6154 " issue.";
6155
6156 /* Mark this table as corrupted, so the drop table
6157 or force recovery can still use it, but not others. */
6158 ib_table->file_unreadable = true;
6159 ib_table->corrupted = true;
6160 dict_table_close(ib_table, FALSE, FALSE);
6161 goto no_such_table;
6162 }
6163
6164 innobase_copy_frm_flags_from_table_share(ib_table, table->s);
6165
6166 /* No point to init any statistics if tablespace is still encrypted. */
6167 if (ib_table->is_readable()) {
6168 dict_stats_init(ib_table);
6169 } else {
6170 ib_table->stat_initialized = 1;
6171 }
6172
6173 MONITOR_INC(MONITOR_TABLE_OPEN);
6174
6175 if ((ib_table->flags2 & DICT_TF2_DISCARDED)) {
6176
6177 ib_senderrf(thd,
6178 IB_LOG_LEVEL_WARN, ER_TABLESPACE_DISCARDED,
6179 table->s->table_name.str);
6180
6181 /* Allow an open because a proper DISCARD should have set
6182 all the flags and index root page numbers to FIL_NULL that
6183 should prevent any DML from running but it should allow DDL
6184 operations. */
6185 } else if (!ib_table->is_readable()) {
6186 const fil_space_t* space = ib_table->space;
6187 if (!space) {
6188 ib_senderrf(
6189 thd, IB_LOG_LEVEL_WARN,
6190 ER_TABLESPACE_MISSING, norm_name);
6191 }
6192
6193 if (!thd_tablespace_op(thd)) {
6194 free_share(m_share);
6195 set_my_errno(ENOENT);
6196 int ret_err = HA_ERR_NO_SUCH_TABLE;
6197
6198 if (space && space->crypt_data
6199 && space->crypt_data->is_encrypted()) {
6200 push_warning_printf(
6201 thd,
6202 Sql_condition::WARN_LEVEL_WARN,
6203 HA_ERR_DECRYPTION_FAILED,
6204 "Table %s in file %s is encrypted"
6205 " but encryption service or"
6206 " used key_id %u is not available. "
6207 " Can't continue reading table.",
6208 table_share->table_name.str,
6209 space->chain.start->name,
6210 space->crypt_data->key_id);
6211 ret_err = HA_ERR_DECRYPTION_FAILED;
6212 }
6213
6214 dict_table_close(ib_table, FALSE, FALSE);
6215 DBUG_RETURN(ret_err);
6216 }
6217 }
6218
6219 m_prebuilt = row_create_prebuilt(ib_table, table->s->reclength);
6220
6221 m_prebuilt->default_rec = table->s->default_values;
6222 ut_ad(m_prebuilt->default_rec);
6223
6224 m_prebuilt->m_mysql_table = table;
6225
6226 /* Looks like MySQL-3.23 sometimes has primary key number != 0 */
6227 m_primary_key = table->s->primary_key;
6228
6229 key_used_on_scan = m_primary_key;
6230
6231 if (ib_table->n_v_cols) {
6232 mutex_enter(&dict_sys->mutex);
6233 if (ib_table->vc_templ == NULL) {
6234 ib_table->vc_templ = UT_NEW_NOKEY(dict_vcol_templ_t());
6235 } else if (ib_table->get_ref_count() == 1) {
6236 /* Clean and refresh the template if no one else
6237 get hold on it */
6238 dict_free_vc_templ(ib_table->vc_templ);
6239 ib_table->vc_templ->vtempl = NULL;
6240 }
6241
6242 if (ib_table->vc_templ->vtempl == NULL) {
6243 innobase_build_v_templ(
6244 table, ib_table, ib_table->vc_templ, NULL,
6245 true);
6246 }
6247
6248 mutex_exit(&dict_sys->mutex);
6249 }
6250
6251 if (!innobase_build_index_translation(table, ib_table, m_share)) {
6252 sql_print_error("Build InnoDB index translation table for"
6253 " Table %s failed", name);
6254 }
6255
6256 /* Allocate a buffer for a 'row reference'. A row reference is
6257 a string of bytes of length ref_length which uniquely specifies
6258 a row in our table. Note that MySQL may also compare two row
6259 references for equality by doing a simple memcmp on the strings
6260 of length ref_length! */
6261 if (!(m_prebuilt->clust_index_was_generated
6262 = dict_index_is_auto_gen_clust(ib_table->indexes.start))) {
6263 if (m_primary_key >= MAX_KEY) {
6264 ib_table->dict_frm_mismatch = DICT_FRM_NO_PK;
6265
6266 /* This mismatch could cause further problems
6267 if not attended, bring this to the user's attention
6268 by printing a warning in addition to log a message
6269 in the errorlog */
6270
6271 ib_push_frm_error(thd, ib_table, table, 0, true);
6272
6273 /* If m_primary_key >= MAX_KEY, its (m_primary_key)
6274 value could be out of bound if continue to index
6275 into key_info[] array. Find InnoDB primary index,
6276 and assign its key_length to ref_length.
6277 In addition, since MySQL indexes are sorted starting
6278 with primary index, unique index etc., initialize
6279 ref_length to the first index key length in
6280 case we fail to find InnoDB cluster index.
6281
6282 Please note, this will not resolve the primary
6283 index mismatch problem, other side effects are
6284 possible if users continue to use the table.
6285 However, we allow this table to be opened so
6286 that user can adopt necessary measures for the
6287 mismatch while still being accessible to the table
6288 date. */
6289 if (!table->key_info) {
6290 ut_ad(!table->s->keys);
6291 ref_length = 0;
6292 } else {
6293 ref_length = table->key_info[0].key_length;
6294 }
6295
6296 /* Find corresponding cluster index
6297 key length in MySQL's key_info[] array */
6298 for (uint i = 0; i < table->s->keys; i++) {
6299 dict_index_t* index;
6300 index = innobase_get_index(i);
6301 if (dict_index_is_clust(index)) {
6302 ref_length =
6303 table->key_info[i].key_length;
6304 }
6305 }
6306 } else {
6307 /* MySQL allocates the buffer for ref.
6308 key_info->key_length includes space for all key
6309 columns + one byte for each column that may be
6310 NULL. ref_length must be as exact as possible to
6311 save space, because all row reference buffers are
6312 allocated based on ref_length. */
6313
6314 ref_length = table->key_info[m_primary_key].key_length;
6315 }
6316 } else {
6317 if (m_primary_key != MAX_KEY) {
6318
6319 ib_table->dict_frm_mismatch = DICT_NO_PK_FRM_HAS;
6320
6321 /* This mismatch could cause further problems
6322 if not attended, bring this to the user attention
6323 by printing a warning in addition to log a message
6324 in the errorlog */
6325 ib_push_frm_error(thd, ib_table, table, 0, true);
6326 }
6327
6328 ref_length = DATA_ROW_ID_LEN;
6329
6330 /* If we automatically created the clustered index, then
6331 MySQL does not know about it, and MySQL must NOT be aware
6332 of the index used on scan, to make it avoid checking if we
6333 update the column of the index. That is why we assert below
6334 that key_used_on_scan is the undefined value MAX_KEY.
6335 The column is the row id in the automatical generation case,
6336 and it will never be updated anyway. */
6337
6338 if (key_used_on_scan != MAX_KEY) {
6339 sql_print_warning(
6340 "Table %s key_used_on_scan is %u even "
6341 "though there is no primary key inside "
6342 "InnoDB.", name, key_used_on_scan);
6343 }
6344 }
6345
6346 /* Index block size in InnoDB: used by MySQL in query optimization */
6347 stats.block_size = srv_page_size;
6348
6349 /* Init table lock structure */
6350 thr_lock_data_init(&m_share->lock, &lock, NULL);
6351
6352 if (m_prebuilt->table == NULL
6353 || m_prebuilt->table->is_temporary()
6354 || m_prebuilt->table->persistent_autoinc
6355 || !m_prebuilt->table->is_readable()) {
6356 } else if (const Field* ai = table->found_next_number_field) {
6357 initialize_auto_increment(m_prebuilt->table, ai);
6358 }
6359
6360 /* Set plugin parser for fulltext index */
6361 for (uint i = 0; i < table->s->keys; i++) {
6362 if (table->key_info[i].flags & HA_USES_PARSER) {
6363 dict_index_t* index = innobase_get_index(i);
6364 plugin_ref parser = table->key_info[i].parser;
6365
6366 ut_ad(index->type & DICT_FTS);
6367 index->parser =
6368 static_cast<st_mysql_ftparser *>(
6369 plugin_decl(parser)->info);
6370
6371 DBUG_EXECUTE_IF("fts_instrument_use_default_parser",
6372 index->parser = &fts_default_parser;);
6373 }
6374 }
6375
6376 if (table && m_prebuilt->table) {
6377 ut_ad(table->versioned() == m_prebuilt->table->versioned());
6378 }
6379
6380 info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST | HA_STATUS_OPEN);
6381 DBUG_RETURN(0);
6382}
6383
6384/** Convert MySQL column number to dict_table_t::cols[] offset.
6385@param[in] field non-virtual column
6386@return column number relative to dict_table_t::cols[] */
6387unsigned
6388innodb_col_no(const Field* field)
6389{
6390 ut_ad(!innobase_is_s_fld(field));
6391 const TABLE* table = field->table;
6392 unsigned col_no = 0;
6393 ut_ad(field == table->field[field->field_index]);
6394 for (unsigned i = 0; i < field->field_index; i++) {
6395 if (table->field[i]->stored_in_db()) {
6396 col_no++;
6397 }
6398 }
6399 return(col_no);
6400}
6401
6402/** Opens dictionary table object using table name. For partition, we need to
6403try alternative lower/upper case names to support moving data files across
6404platforms.
6405@param[in] table_name name of the table/partition
6406@param[in] norm_name normalized name of the table/partition
6407@param[in] is_partition if this is a partition of a table
6408@param[in] ignore_err error to ignore for loading dictionary object
6409@return dictionary table object or NULL if not found */
6410dict_table_t*
6411ha_innobase::open_dict_table(
6412 const char*
6413#ifdef _WIN32
6414 table_name
6415#endif
6416 ,
6417 const char* norm_name,
6418 bool is_partition,
6419 dict_err_ignore_t ignore_err)
6420{
6421 DBUG_ENTER("ha_innobase::open_dict_table");
6422 dict_table_t* ib_table = dict_table_open_on_name(norm_name, FALSE,
6423 TRUE, ignore_err);
6424
6425 if (NULL == ib_table && is_partition) {
6426 /* MySQL partition engine hard codes the file name
6427 separator as "#P#". The text case is fixed even if
6428 lower_case_table_names is set to 1 or 2. This is true
6429 for sub-partition names as well. InnoDB always
6430 normalises file names to lower case on Windows, this
6431 can potentially cause problems when copying/moving
6432 tables between platforms.
6433
6434 1) If boot against an installation from Windows
6435 platform, then its partition table name could
6436 be in lower case in system tables. So we will
6437 need to check lower case name when load table.
6438
6439 2) If we boot an installation from other case
6440 sensitive platform in Windows, we might need to
6441 check the existence of table name without lower
6442 case in the system table. */
6443 if (innobase_get_lower_case_table_names() == 1) {
6444 char par_case_name[FN_REFLEN];
6445
6446#ifndef _WIN32
6447 /* Check for the table using lower
6448 case name, including the partition
6449 separator "P" */
6450 strcpy(par_case_name, norm_name);
6451 innobase_casedn_str(par_case_name);
6452#else
6453 /* On Windows platfrom, check
6454 whether there exists table name in
6455 system table whose name is
6456 not being normalized to lower case */
6457 create_table_info_t::
6458 normalize_table_name_low(
6459 par_case_name,
6460 table_name, FALSE);
6461#endif
6462 ib_table = dict_table_open_on_name(
6463 par_case_name, FALSE, TRUE,
6464 ignore_err);
6465 }
6466
6467 if (ib_table != NULL) {
6468#ifndef _WIN32
6469 sql_print_warning("Partition table %s opened"
6470 " after converting to lower"
6471 " case. The table may have"
6472 " been moved from a case"
6473 " in-sensitive file system."
6474 " Please recreate table in"
6475 " the current file system\n",
6476 norm_name);
6477#else
6478 sql_print_warning("Partition table %s opened"
6479 " after skipping the step to"
6480 " lower case the table name."
6481 " The table may have been"
6482 " moved from a case sensitive"
6483 " file system. Please"
6484 " recreate table in the"
6485 " current file system\n",
6486 norm_name);
6487#endif
6488 }
6489 }
6490
6491 DBUG_RETURN(ib_table);
6492}
6493
6494handler*
6495ha_innobase::clone(
6496/*===============*/
6497 const char* name, /*!< in: table name */
6498 MEM_ROOT* mem_root) /*!< in: memory context */
6499{
6500 DBUG_ENTER("ha_innobase::clone");
6501
6502 ha_innobase* new_handler = static_cast<ha_innobase*>(
6503 handler::clone(name, mem_root));
6504
6505 if (new_handler != NULL) {
6506 DBUG_ASSERT(new_handler->m_prebuilt != NULL);
6507
6508 new_handler->m_prebuilt->select_lock_type
6509 = m_prebuilt->select_lock_type;
6510 }
6511
6512 DBUG_RETURN(new_handler);
6513}
6514
6515
6516uint
6517ha_innobase::max_supported_key_part_length() const
6518/*==============================================*/
6519{
6520 /* A table format specific index column length check will be performed
6521 at ha_innobase::add_index() and row_create_index_for_mysql() */
6522 return(REC_VERSION_56_MAX_INDEX_COL_LEN);
6523}
6524
6525/******************************************************************//**
6526Closes a handle to an InnoDB table.
6527@return 0 */
6528
6529int
6530ha_innobase::close()
6531/*================*/
6532{
6533 DBUG_ENTER("ha_innobase::close");
6534
6535 row_prebuilt_free(m_prebuilt, FALSE);
6536
6537 if (m_upd_buf != NULL) {
6538 ut_ad(m_upd_buf_size != 0);
6539 my_free(m_upd_buf);
6540 m_upd_buf = NULL;
6541 m_upd_buf_size = 0;
6542 }
6543
6544 free_share(m_share);
6545
6546 MONITOR_INC(MONITOR_TABLE_CLOSE);
6547
6548 /* Tell InnoDB server that there might be work for
6549 utility threads: */
6550
6551 srv_active_wake_master_thread();
6552
6553 DBUG_RETURN(0);
6554}
6555
6556/* The following accessor functions should really be inside MySQL code! */
6557
6558#ifdef WITH_WSREP
6559UNIV_INTERN
6560ulint
6561wsrep_innobase_mysql_sort(
6562/*======================*/
6563 /* out: str contains sort string */
6564 int mysql_type, /* in: MySQL type */
6565 uint charset_number, /* in: number of the charset */
6566 unsigned char* str, /* in: data field */
6567 unsigned int str_length, /* in: data field length,
6568 not UNIV_SQL_NULL */
6569 unsigned int buf_length) /* in: total str buffer length */
6570
6571{
6572 CHARSET_INFO* charset;
6573 enum_field_types mysql_tp;
6574 ulint ret_length = str_length;
6575
6576 DBUG_ASSERT(str_length != UNIV_SQL_NULL);
6577
6578 mysql_tp = (enum_field_types) mysql_type;
6579
6580 switch (mysql_tp) {
6581
6582 case MYSQL_TYPE_BIT:
6583 case MYSQL_TYPE_STRING:
6584 case MYSQL_TYPE_VAR_STRING:
6585 case MYSQL_TYPE_TINY_BLOB:
6586 case MYSQL_TYPE_MEDIUM_BLOB:
6587 case MYSQL_TYPE_BLOB:
6588 case MYSQL_TYPE_LONG_BLOB:
6589 case MYSQL_TYPE_VARCHAR:
6590 {
6591 uchar tmp_str[REC_VERSION_56_MAX_INDEX_COL_LEN] = {'\0'};
6592 uint tmp_length = REC_VERSION_56_MAX_INDEX_COL_LEN;
6593
6594 /* Use the charset number to pick the right charset struct for
6595 the comparison. Since the MySQL function get_charset may be
6596 slow before Bar removes the mutex operation there, we first
6597 look at 2 common charsets directly. */
6598
6599 if (charset_number == default_charset_info->number) {
6600 charset = default_charset_info;
6601 } else if (charset_number == my_charset_latin1.number) {
6602 charset = &my_charset_latin1;
6603 } else {
6604 charset = get_charset(charset_number, MYF(MY_WME));
6605
6606 if (charset == NULL) {
6607 sql_print_error("InnoDB needs charset %lu for doing "
6608 "a comparison, but MariaDB cannot "
6609 "find that charset.",
6610 (ulong) charset_number);
6611 ut_a(0);
6612 }
6613 }
6614
6615 ut_a(str_length <= tmp_length);
6616 memcpy(tmp_str, str, str_length);
6617
6618 tmp_length = charset->coll->strnxfrm(charset, str, str_length,
6619 str_length, tmp_str,
6620 tmp_length, 0);
6621 DBUG_ASSERT(tmp_length <= str_length);
6622 if (wsrep_protocol_version < 3) {
6623 tmp_length = charset->coll->strnxfrm(
6624 charset, str, str_length,
6625 str_length, tmp_str, tmp_length, 0);
6626 DBUG_ASSERT(tmp_length <= str_length);
6627 } else {
6628 /* strnxfrm will expand the destination string,
6629 protocols < 3 truncated the sorted sring
6630 protocols >= 3 gets full sorted sring
6631 */
6632 tmp_length = charset->coll->strnxfrm(
6633 charset, str, buf_length,
6634 str_length, tmp_str, str_length, 0);
6635 DBUG_ASSERT(tmp_length <= buf_length);
6636 ret_length = tmp_length;
6637 }
6638
6639 break;
6640 }
6641 case MYSQL_TYPE_DECIMAL :
6642 case MYSQL_TYPE_TINY :
6643 case MYSQL_TYPE_SHORT :
6644 case MYSQL_TYPE_LONG :
6645 case MYSQL_TYPE_FLOAT :
6646 case MYSQL_TYPE_DOUBLE :
6647 case MYSQL_TYPE_NULL :
6648 case MYSQL_TYPE_TIMESTAMP :
6649 case MYSQL_TYPE_LONGLONG :
6650 case MYSQL_TYPE_INT24 :
6651 case MYSQL_TYPE_DATE :
6652 case MYSQL_TYPE_TIME :
6653 case MYSQL_TYPE_DATETIME :
6654 case MYSQL_TYPE_YEAR :
6655 case MYSQL_TYPE_NEWDATE :
6656 case MYSQL_TYPE_NEWDECIMAL :
6657 case MYSQL_TYPE_ENUM :
6658 case MYSQL_TYPE_SET :
6659 case MYSQL_TYPE_GEOMETRY :
6660 break;
6661 default:
6662 break;
6663 }
6664
6665 return ret_length;
6666}
6667#endif /* WITH_WSREP */
6668
6669/******************************************************************//**
6670compare two character string according to their charset. */
6671int
6672innobase_fts_text_cmp(
6673/*==================*/
6674 const void* cs, /*!< in: Character set */
6675 const void* p1, /*!< in: key */
6676 const void* p2) /*!< in: node */
6677{
6678 const CHARSET_INFO* charset = (const CHARSET_INFO*) cs;
6679 const fts_string_t* s1 = (const fts_string_t*) p1;
6680 const fts_string_t* s2 = (const fts_string_t*) p2;
6681
6682 return(ha_compare_text(
6683 charset, s1->f_str, static_cast<uint>(s1->f_len),
6684 s2->f_str, static_cast<uint>(s2->f_len), 0));
6685}
6686
6687/******************************************************************//**
6688compare two character string case insensitively according to their charset. */
6689int
6690innobase_fts_text_case_cmp(
6691/*=======================*/
6692 const void* cs, /*!< in: Character set */
6693 const void* p1, /*!< in: key */
6694 const void* p2) /*!< in: node */
6695{
6696 const CHARSET_INFO* charset = (const CHARSET_INFO*) cs;
6697 const fts_string_t* s1 = (const fts_string_t*) p1;
6698 const fts_string_t* s2 = (const fts_string_t*) p2;
6699 ulint newlen;
6700
6701 my_casedn_str(charset, (char*) s2->f_str);
6702
6703 newlen = strlen((const char*) s2->f_str);
6704
6705 return(ha_compare_text(
6706 charset, s1->f_str, static_cast<uint>(s1->f_len),
6707 s2->f_str, static_cast<uint>(newlen), 0));
6708}
6709
6710/******************************************************************//**
6711Get the first character's code position for FTS index partition. */
6712ulint
6713innobase_strnxfrm(
6714/*==============*/
6715 const CHARSET_INFO*
6716 cs, /*!< in: Character set */
6717 const uchar* str, /*!< in: string */
6718 const ulint len) /*!< in: string length */
6719{
6720 uchar mystr[2];
6721 ulint value;
6722
6723 if (!str || len == 0) {
6724 return(0);
6725 }
6726
6727 my_strnxfrm(cs, (uchar*) mystr, 2, str, len);
6728
6729 value = mach_read_from_2(mystr);
6730
6731 if (value > 255) {
6732 value = value / 256;
6733 }
6734
6735 return(value);
6736}
6737
6738/******************************************************************//**
6739compare two character string according to their charset. */
6740int
6741innobase_fts_text_cmp_prefix(
6742/*=========================*/
6743 const void* cs, /*!< in: Character set */
6744 const void* p1, /*!< in: prefix key */
6745 const void* p2) /*!< in: value to compare */
6746{
6747 const CHARSET_INFO* charset = (const CHARSET_INFO*) cs;
6748 const fts_string_t* s1 = (const fts_string_t*) p1;
6749 const fts_string_t* s2 = (const fts_string_t*) p2;
6750 int result;
6751
6752 result = ha_compare_text(
6753 charset, s2->f_str, static_cast<uint>(s2->f_len),
6754 s1->f_str, static_cast<uint>(s1->f_len), 1);
6755
6756 /* We switched s1, s2 position in ha_compare_text. So we need
6757 to negate the result */
6758 return(-result);
6759}
6760
6761/******************************************************************//**
6762Makes all characters in a string lower case. */
6763size_t
6764innobase_fts_casedn_str(
6765/*====================*/
6766 CHARSET_INFO* cs, /*!< in: Character set */
6767 char* src, /*!< in: string to put in lower case */
6768 size_t src_len,/*!< in: input string length */
6769 char* dst, /*!< in: buffer for result string */
6770 size_t dst_len)/*!< in: buffer size */
6771{
6772 if (cs->casedn_multiply == 1) {
6773 memcpy(dst, src, src_len);
6774 dst[src_len] = 0;
6775 my_casedn_str(cs, dst);
6776
6777 return(strlen(dst));
6778 } else {
6779 return(cs->cset->casedn(cs, src, src_len, dst, dst_len));
6780 }
6781}
6782
6783#define true_word_char(c, ch) ((c) & (_MY_U | _MY_L | _MY_NMR) || (ch) == '_')
6784
6785#define misc_word_char(X) 0
6786
6787/*************************************************************//**
6788Get the next token from the given string and store it in *token.
6789It is mostly copied from MyISAM's doc parsing function ft_simple_get_word()
6790@return length of string processed */
6791ulint
6792innobase_mysql_fts_get_token(
6793/*=========================*/
6794 CHARSET_INFO* cs, /*!< in: Character set */
6795 const byte* start, /*!< in: start of text */
6796 const byte* end, /*!< in: one character past end of
6797 text */
6798 fts_string_t* token) /*!< out: token's text */
6799{
6800 int mbl;
6801 const uchar* doc = start;
6802
6803 ut_a(cs);
6804
6805 token->f_n_char = token->f_len = 0;
6806 token->f_str = NULL;
6807
6808 for (;;) {
6809
6810 if (doc >= end) {
6811 return ulint(doc - start);
6812 }
6813
6814 int ctype;
6815
6816 mbl = cs->cset->ctype(
6817 cs, &ctype, doc, (const uchar*) end);
6818
6819 if (true_word_char(ctype, *doc)) {
6820 break;
6821 }
6822
6823 doc += mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1);
6824 }
6825
6826 ulint mwc = 0;
6827 ulint length = 0;
6828
6829 token->f_str = const_cast<byte*>(doc);
6830
6831 while (doc < end) {
6832
6833 int ctype;
6834
6835 mbl = cs->cset->ctype(
6836 cs, &ctype, (uchar*) doc, (uchar*) end);
6837 if (true_word_char(ctype, *doc)) {
6838 mwc = 0;
6839 } else if (!misc_word_char(*doc) || mwc) {
6840 break;
6841 } else {
6842 ++mwc;
6843 }
6844
6845 ++length;
6846
6847 doc += mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1);
6848 }
6849
6850 token->f_len = (uint) (doc - token->f_str) - mwc;
6851 token->f_n_char = length;
6852
6853 return ulint(doc - start);
6854}
6855
6856/** Converts a MySQL type to an InnoDB type. Note that this function returns
6857the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
6858VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'.
6859@param[out] unsigned_flag DATA_UNSIGNED if an 'unsigned type'; at least
6860ENUM and SET, and unsigned integer types are 'unsigned types'
6861@param[in] f MySQL Field
6862@return DATA_BINARY, DATA_VARCHAR, ... */
6863ulint
6864get_innobase_type_from_mysql_type(
6865 ulint* unsigned_flag,
6866 const void* f)
6867{
6868 const class Field* field = reinterpret_cast<const class Field*>(f);
6869
6870 /* The following asserts try to check that the MySQL type code fits in
6871 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to
6872 the type */
6873
6874 DBUG_ASSERT((ulint)MYSQL_TYPE_STRING < 256);
6875 DBUG_ASSERT((ulint)MYSQL_TYPE_VAR_STRING < 256);
6876 DBUG_ASSERT((ulint)MYSQL_TYPE_DOUBLE < 256);
6877 DBUG_ASSERT((ulint)MYSQL_TYPE_FLOAT < 256);
6878 DBUG_ASSERT((ulint)MYSQL_TYPE_DECIMAL < 256);
6879
6880 if (field->flags & UNSIGNED_FLAG) {
6881
6882 *unsigned_flag = DATA_UNSIGNED;
6883 } else {
6884 *unsigned_flag = 0;
6885 }
6886
6887 if (field->real_type() == MYSQL_TYPE_ENUM
6888 || field->real_type() == MYSQL_TYPE_SET) {
6889
6890 /* MySQL has field->type() a string type for these, but the
6891 data is actually internally stored as an unsigned integer
6892 code! */
6893
6894 *unsigned_flag = DATA_UNSIGNED; /* MySQL has its own unsigned
6895 flag set to zero, even though
6896 internally this is an unsigned
6897 integer type */
6898 return(DATA_INT);
6899 }
6900
6901 switch (field->type()) {
6902 /* NOTE that we only allow string types in DATA_MYSQL and
6903 DATA_VARMYSQL */
6904 case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
6905 case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */
6906 if (field->binary()) {
6907 return(DATA_BINARY);
6908 } else if (field->charset() == &my_charset_latin1) {
6909 return(DATA_VARCHAR);
6910 } else {
6911 return(DATA_VARMYSQL);
6912 }
6913 case MYSQL_TYPE_BIT:
6914 case MYSQL_TYPE_STRING:
6915 if (field->binary()) {
6916 return(DATA_FIXBINARY);
6917 } else if (field->charset() == &my_charset_latin1) {
6918 return(DATA_CHAR);
6919 } else {
6920 return(DATA_MYSQL);
6921 }
6922 case MYSQL_TYPE_NEWDECIMAL:
6923 return(DATA_FIXBINARY);
6924 case MYSQL_TYPE_LONG:
6925 case MYSQL_TYPE_LONGLONG:
6926 case MYSQL_TYPE_TINY:
6927 case MYSQL_TYPE_SHORT:
6928 case MYSQL_TYPE_INT24:
6929 case MYSQL_TYPE_DATE:
6930 case MYSQL_TYPE_YEAR:
6931 case MYSQL_TYPE_NEWDATE:
6932 return(DATA_INT);
6933 case MYSQL_TYPE_TIME:
6934 case MYSQL_TYPE_DATETIME:
6935 case MYSQL_TYPE_TIMESTAMP:
6936 if (field->key_type() == HA_KEYTYPE_BINARY) {
6937 return(DATA_FIXBINARY);
6938 } else {
6939 return(DATA_INT);
6940 }
6941 case MYSQL_TYPE_FLOAT:
6942 return(DATA_FLOAT);
6943 case MYSQL_TYPE_DOUBLE:
6944 return(DATA_DOUBLE);
6945 case MYSQL_TYPE_DECIMAL:
6946 return(DATA_DECIMAL);
6947 case MYSQL_TYPE_GEOMETRY:
6948 return(DATA_GEOMETRY);
6949 case MYSQL_TYPE_TINY_BLOB:
6950 case MYSQL_TYPE_MEDIUM_BLOB:
6951 case MYSQL_TYPE_BLOB:
6952 case MYSQL_TYPE_LONG_BLOB:
6953 return(DATA_BLOB);
6954 case MYSQL_TYPE_NULL:
6955 /* MySQL currently accepts "NULL" datatype, but will
6956 reject such datatype in the next release. We will cope
6957 with it and not trigger assertion failure in 5.1 */
6958 break;
6959 default:
6960 ut_error;
6961 }
6962
6963 return(0);
6964}
6965
6966/*******************************************************************//**
6967Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
6968storage format.
6969@return value */
6970static inline
6971uint
6972innobase_read_from_2_little_endian(
6973/*===============================*/
6974 const uchar* buf) /*!< in: from where to read */
6975{
6976 return((uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1]))));
6977}
6978
6979#ifdef WITH_WSREP
6980/*******************************************************************//**
6981Stores a key value for a row to a buffer.
6982@return key value length as stored in buff */
6983UNIV_INTERN
6984uint
6985wsrep_store_key_val_for_row(
6986/*=========================*/
6987 THD* thd,
6988 TABLE* table,
6989 uint keynr, /*!< in: key number */
6990 char* buff, /*!< in/out: buffer for the key value (in MySQL
6991 format) */
6992 uint buff_len,/*!< in: buffer length */
6993 const uchar* record,
6994 ibool* key_is_null)/*!< out: full key was null */
6995{
6996 KEY* key_info = table->key_info + keynr;
6997 KEY_PART_INFO* key_part = key_info->key_part;
6998 KEY_PART_INFO* end = key_part + key_info->user_defined_key_parts;
6999 char* buff_start = buff;
7000 enum_field_types mysql_type;
7001 Field* field;
7002 uint buff_space = buff_len;
7003
7004 DBUG_ENTER("wsrep_store_key_val_for_row");
7005
7006 memset(buff, 0, buff_len);
7007 *key_is_null = TRUE;
7008
7009 for (; key_part != end; key_part++) {
7010
7011 uchar sorted[REC_VERSION_56_MAX_INDEX_COL_LEN] = {'\0'};
7012 ibool part_is_null = FALSE;
7013
7014 if (key_part->null_bit) {
7015 if (buff_space > 0) {
7016 if (record[key_part->null_offset]
7017 & key_part->null_bit) {
7018 *buff = 1;
7019 part_is_null = TRUE;
7020 } else {
7021 *buff = 0;
7022 }
7023 buff++;
7024 buff_space--;
7025 } else {
7026 fprintf (stderr, "WSREP: key truncated: %s\n",
7027 wsrep_thd_query(thd));
7028 }
7029 }
7030 if (!part_is_null) *key_is_null = FALSE;
7031
7032 field = key_part->field;
7033 mysql_type = field->type();
7034
7035 if (mysql_type == MYSQL_TYPE_VARCHAR) {
7036 /* >= 5.0.3 true VARCHAR */
7037 ulint lenlen;
7038 ulint len;
7039 const byte* data;
7040 ulint key_len;
7041 ulint true_len;
7042 const CHARSET_INFO* cs;
7043 int error=0;
7044
7045 key_len = key_part->length;
7046
7047 if (part_is_null) {
7048 true_len = key_len + 2;
7049 if (true_len > buff_space) {
7050 fprintf (stderr,
7051 "WSREP: key truncated: %s\n",
7052 wsrep_thd_query(thd));
7053 true_len = buff_space;
7054 }
7055 buff += true_len;
7056 buff_space -= true_len;
7057 continue;
7058 }
7059 cs = field->charset();
7060
7061 lenlen = (ulint)
7062 (((Field_varstring*)field)->length_bytes);
7063
7064 data = row_mysql_read_true_varchar(&len,
7065 (byte*) (record
7066 + (ulint)get_field_offset(table, field)),
7067 lenlen);
7068
7069 true_len = len;
7070
7071 /* For multi byte character sets we need to calculate
7072 the true length of the key */
7073
7074 if (len > 0 && cs->mbmaxlen > 1) {
7075 true_len = (ulint) my_well_formed_length(cs,
7076 (const char *) data,
7077 (const char *) data + len,
7078 (uint) (key_len /
7079 cs->mbmaxlen),
7080 &error);
7081 }
7082
7083 /* In a column prefix index, we may need to truncate
7084 the stored value: */
7085
7086 if (true_len > key_len) {
7087 true_len = key_len;
7088 }
7089
7090 memcpy(sorted, data, true_len);
7091 true_len = wsrep_innobase_mysql_sort(
7092 mysql_type, cs->number, sorted, true_len,
7093 REC_VERSION_56_MAX_INDEX_COL_LEN);
7094 if (wsrep_protocol_version > 1) {
7095 /* Note that we always reserve the maximum possible
7096 length of the true VARCHAR in the key value, though
7097 only len first bytes after the 2 length bytes contain
7098 actual data. The rest of the space was reset to zero
7099 in the bzero() call above. */
7100 if (true_len > buff_space) {
7101 fprintf (stderr,
7102 "WSREP: key truncated: %s\n",
7103 wsrep_thd_query(thd));
7104 true_len = buff_space;
7105 }
7106 memcpy(buff, sorted, true_len);
7107 buff += true_len;
7108 buff_space -= true_len;
7109 } else {
7110 buff += key_len;
7111 }
7112 } else if (mysql_type == MYSQL_TYPE_TINY_BLOB
7113 || mysql_type == MYSQL_TYPE_MEDIUM_BLOB
7114 || mysql_type == MYSQL_TYPE_BLOB
7115 || mysql_type == MYSQL_TYPE_LONG_BLOB
7116 /* MYSQL_TYPE_GEOMETRY data is treated
7117 as BLOB data in innodb. */
7118 || mysql_type == MYSQL_TYPE_GEOMETRY) {
7119
7120 const CHARSET_INFO* cs;
7121 ulint key_len;
7122 ulint true_len;
7123 int error=0;
7124 ulint blob_len;
7125 const byte* blob_data;
7126
7127 ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
7128
7129 key_len = key_part->length;
7130
7131 if (part_is_null) {
7132 true_len = key_len + 2;
7133 if (true_len > buff_space) {
7134 fprintf (stderr,
7135 "WSREP: key truncated: %s\n",
7136 wsrep_thd_query(thd));
7137 true_len = buff_space;
7138 }
7139 buff += true_len;
7140 buff_space -= true_len;
7141
7142 continue;
7143 }
7144
7145 cs = field->charset();
7146
7147 blob_data = row_mysql_read_blob_ref(&blob_len,
7148 (byte*) (record
7149 + (ulint)get_field_offset(table, field)),
7150 (ulint) field->pack_length());
7151
7152 true_len = blob_len;
7153
7154 ut_a(get_field_offset(table, field)
7155 == key_part->offset);
7156
7157 /* For multi byte character sets we need to calculate
7158 the true length of the key */
7159
7160 if (blob_len > 0 && cs->mbmaxlen > 1) {
7161 true_len = (ulint) my_well_formed_length(cs,
7162 (const char *) blob_data,
7163 (const char *) blob_data
7164 + blob_len,
7165 (uint) (key_len /
7166 cs->mbmaxlen),
7167 &error);
7168 }
7169
7170 /* All indexes on BLOB and TEXT are column prefix
7171 indexes, and we may need to truncate the data to be
7172 stored in the key value: */
7173
7174 if (true_len > key_len) {
7175 true_len = key_len;
7176 }
7177
7178 memcpy(sorted, blob_data, true_len);
7179 true_len = wsrep_innobase_mysql_sort(
7180 mysql_type, cs->number, sorted, true_len,
7181 REC_VERSION_56_MAX_INDEX_COL_LEN);
7182
7183
7184 /* Note that we always reserve the maximum possible
7185 length of the BLOB prefix in the key value. */
7186 if (wsrep_protocol_version > 1) {
7187 if (true_len > buff_space) {
7188 fprintf (stderr,
7189 "WSREP: key truncated: %s\n",
7190 wsrep_thd_query(thd));
7191 true_len = buff_space;
7192 }
7193 buff += true_len;
7194 buff_space -= true_len;
7195 } else {
7196 buff += key_len;
7197 }
7198 memcpy(buff, sorted, true_len);
7199 } else {
7200 /* Here we handle all other data types except the
7201 true VARCHAR, BLOB and TEXT. Note that the column
7202 value we store may be also in a column prefix
7203 index. */
7204
7205 const CHARSET_INFO* cs = NULL;
7206 ulint true_len;
7207 ulint key_len;
7208 const uchar* src_start;
7209 int error=0;
7210 enum_field_types real_type;
7211
7212 key_len = key_part->length;
7213
7214 if (part_is_null) {
7215 true_len = key_len;
7216 if (true_len > buff_space) {
7217 fprintf (stderr,
7218 "WSREP: key truncated: %s\n",
7219 wsrep_thd_query(thd));
7220 true_len = buff_space;
7221 }
7222 buff += true_len;
7223 buff_space -= true_len;
7224
7225 continue;
7226 }
7227
7228 src_start = record + key_part->offset;
7229 real_type = field->real_type();
7230 true_len = key_len;
7231
7232 /* Character set for the field is defined only
7233 to fields whose type is string and real field
7234 type is not enum or set. For these fields check
7235 if character set is multi byte. */
7236
7237 if (real_type != MYSQL_TYPE_ENUM
7238 && real_type != MYSQL_TYPE_SET
7239 && ( mysql_type == MYSQL_TYPE_VAR_STRING
7240 || mysql_type == MYSQL_TYPE_STRING)) {
7241
7242 cs = field->charset();
7243
7244 /* For multi byte character sets we need to
7245 calculate the true length of the key */
7246
7247 if (key_len > 0 && cs->mbmaxlen > 1) {
7248
7249 true_len = (ulint)
7250 my_well_formed_length(cs,
7251 (const char *)src_start,
7252 (const char *)src_start
7253 + key_len,
7254 (uint) (key_len /
7255 cs->mbmaxlen),
7256 &error);
7257 }
7258 memcpy(sorted, src_start, true_len);
7259 true_len = wsrep_innobase_mysql_sort(
7260 mysql_type, cs->number, sorted, true_len,
7261 REC_VERSION_56_MAX_INDEX_COL_LEN);
7262
7263 if (true_len > buff_space) {
7264 fprintf (stderr,
7265 "WSREP: key truncated: %s\n",
7266 wsrep_thd_query(thd));
7267 true_len = buff_space;
7268 }
7269 memcpy(buff, sorted, true_len);
7270 } else {
7271 memcpy(buff, src_start, true_len);
7272 }
7273 buff += true_len;
7274 buff_space -= true_len;
7275 }
7276 }
7277
7278 ut_a(buff <= buff_start + buff_len);
7279
7280 DBUG_RETURN((uint)(buff - buff_start));
7281}
7282#endif /* WITH_WSREP */
7283/**************************************************************//**
7284Determines if a field is needed in a m_prebuilt struct 'template'.
7285@return field to use, or NULL if the field is not needed */
7286static
7287const Field*
7288build_template_needs_field(
7289/*=======================*/
7290 ibool index_contains, /*!< in:
7291 dict_index_contains_col_or_prefix(
7292 index, i) */
7293 ibool read_just_key, /*!< in: TRUE when MySQL calls
7294 ha_innobase::extra with the
7295 argument HA_EXTRA_KEYREAD; it is enough
7296 to read just columns defined in
7297 the index (i.e., no read of the
7298 clustered index record necessary) */
7299 ibool fetch_all_in_key,
7300 /*!< in: true=fetch all fields in
7301 the index */
7302 ibool fetch_primary_key_cols,
7303 /*!< in: true=fetch the
7304 primary key columns */
7305 dict_index_t* index, /*!< in: InnoDB index to use */
7306 const TABLE* table, /*!< in: MySQL table object */
7307 ulint i, /*!< in: field index in InnoDB table */
7308 ulint num_v) /*!< in: num virtual column so far */
7309{
7310 const Field* field = table->field[i];
7311
7312 if (!index_contains) {
7313 if (read_just_key) {
7314 /* If this is a 'key read', we do not need
7315 columns that are not in the key */
7316
7317 return(NULL);
7318 }
7319 } else if (fetch_all_in_key) {
7320 /* This field is needed in the query */
7321
7322 return(field);
7323 }
7324
7325 if (bitmap_is_set(table->read_set, static_cast<uint>(i))
7326 || bitmap_is_set(table->write_set, static_cast<uint>(i))) {
7327 /* This field is needed in the query */
7328
7329 return(field);
7330 }
7331
7332 ut_ad(i >= num_v);
7333 if (fetch_primary_key_cols
7334 && dict_table_col_in_clustered_key(index->table, i - num_v)) {
7335 /* This field is needed in the query */
7336
7337 return(field);
7338 }
7339
7340 /* This field is not needed in the query, skip it */
7341
7342 return(NULL);
7343}
7344
7345/**************************************************************//**
7346Determines if a field is needed in a m_prebuilt struct 'template'.
7347@return whether the field is needed for index condition pushdown */
7348inline
7349bool
7350build_template_needs_field_in_icp(
7351/*==============================*/
7352 const dict_index_t* index, /*!< in: InnoDB index */
7353 const row_prebuilt_t* prebuilt,/*!< in: row fetch template */
7354 bool contains,/*!< in: whether the index contains
7355 column i */
7356 ulint i, /*!< in: column number */
7357 bool is_virtual)
7358 /*!< in: a virtual column or not */
7359{
7360 ut_ad(contains == dict_index_contains_col_or_prefix(index, i, is_virtual));
7361
7362 return(index == prebuilt->index
7363 ? contains
7364 : dict_index_contains_col_or_prefix(prebuilt->index, i, is_virtual));
7365}
7366
7367/**************************************************************//**
7368Adds a field to a m_prebuilt struct 'template'.
7369@return the field template */
7370static
7371mysql_row_templ_t*
7372build_template_field(
7373/*=================*/
7374 row_prebuilt_t* prebuilt, /*!< in/out: template */
7375 dict_index_t* clust_index, /*!< in: InnoDB clustered index */
7376 dict_index_t* index, /*!< in: InnoDB index to use */
7377 TABLE* table, /*!< in: MySQL table object */
7378 const Field* field, /*!< in: field in MySQL table */
7379 ulint i, /*!< in: field index in InnoDB table */
7380 ulint v_no) /*!< in: field index for virtual col */
7381{
7382 mysql_row_templ_t* templ;
7383 const dict_col_t* col;
7384
7385 ut_ad(clust_index->table == index->table);
7386
7387 templ = prebuilt->mysql_template + prebuilt->n_template++;
7388 UNIV_MEM_INVALID(templ, sizeof *templ);
7389
7390 if (innobase_is_v_fld(field)) {
7391 templ->is_virtual = true;
7392 col = &dict_table_get_nth_v_col(index->table, v_no)->m_col;
7393 } else {
7394 templ->is_virtual = false;
7395 col = dict_table_get_nth_col(index->table, i);
7396 }
7397
7398 if (!templ->is_virtual) {
7399 templ->col_no = i;
7400 templ->clust_rec_field_no = dict_col_get_clust_pos(
7401 col, clust_index);
7402 /* If clustered index record field is not found, lets print out
7403 field names and all the rest to understand why field is not found. */
7404 if (templ->clust_rec_field_no == ULINT_UNDEFINED) {
7405 const char* tb_col_name = dict_table_get_col_name(clust_index->table, i);
7406 dict_field_t* field=NULL;
7407 size_t size = 0;
7408
7409 for(ulint j=0; j < clust_index->n_user_defined_cols; j++) {
7410 dict_field_t* ifield = &(clust_index->fields[j]);
7411 if (ifield && !memcmp(tb_col_name, ifield->name,
7412 strlen(tb_col_name))) {
7413 field = ifield;
7414 break;
7415 }
7416 }
7417
7418 ib::info() << "Looking for field " << i << " name "
7419 << (tb_col_name ? tb_col_name : "NULL")
7420 << " from table " << clust_index->table->name;
7421
7422
7423 for(ulint j=0; j < clust_index->n_user_defined_cols; j++) {
7424 dict_field_t* ifield = &(clust_index->fields[j]);
7425 ib::info() << "InnoDB Table "
7426 << clust_index->table->name
7427 << "field " << j << " name "
7428 << (ifield ? ifield->name() : "NULL");
7429 }
7430
7431 for(ulint j=0; j < table->s->stored_fields; j++) {
7432 ib::info() << "MySQL table "
7433 << table->s->table_name.str
7434 << " field " << j << " name "
7435 << table->field[j]->field_name.str;
7436 }
7437
7438 ib::error() << "Clustered record field for column " << i
7439 << " not found table n_user_defined "
7440 << clust_index->n_user_defined_cols
7441 << " index n_user_defined "
7442 << clust_index->table->n_cols - DATA_N_SYS_COLS
7443 << " InnoDB table "
7444 << clust_index->table->name
7445 << " field name "
7446 << (field ? field->name() : "NULL")
7447 << " MySQL table "
7448 << table->s->table_name.str
7449 << " field name "
7450 << (tb_col_name ? tb_col_name : "NULL")
7451 << " n_fields "
7452 << table->s->stored_fields
7453 << " query "
7454 << innobase_get_stmt_unsafe(current_thd, &size);
7455
7456 ut_a(templ->clust_rec_field_no != ULINT_UNDEFINED);
7457 }
7458 templ->rec_field_is_prefix = FALSE;
7459 templ->rec_prefix_field_no = ULINT_UNDEFINED;
7460
7461 if (dict_index_is_clust(index)) {
7462 templ->rec_field_no = templ->clust_rec_field_no;
7463 } else {
7464 /* If we're in a secondary index, keep track
7465 * of the original index position even if this
7466 * is just a prefix index; we will use this
7467 * later to avoid a cluster index lookup in
7468 * some cases.*/
7469
7470 templ->rec_field_no = dict_index_get_nth_col_pos(index, i,
7471 &templ->rec_prefix_field_no);
7472 }
7473 } else {
7474 templ->clust_rec_field_no = v_no;
7475 templ->rec_prefix_field_no = ULINT_UNDEFINED;
7476
7477 if (dict_index_is_clust(index)) {
7478 templ->rec_field_no = templ->clust_rec_field_no;
7479 } else {
7480 templ->rec_field_no
7481 = dict_index_get_nth_col_or_prefix_pos(
7482 index, v_no, FALSE, true,
7483 &templ->rec_prefix_field_no);
7484 }
7485 templ->icp_rec_field_no = ULINT_UNDEFINED;
7486 }
7487
7488 if (field->real_maybe_null()) {
7489 templ->mysql_null_byte_offset =
7490 field->null_offset();
7491
7492 templ->mysql_null_bit_mask = (ulint) field->null_bit;
7493 } else {
7494 templ->mysql_null_bit_mask = 0;
7495 }
7496
7497
7498 templ->mysql_col_offset = (ulint) get_field_offset(table, field);
7499 templ->mysql_col_len = (ulint) field->pack_length();
7500 templ->type = col->mtype;
7501 templ->mysql_type = (ulint) field->type();
7502
7503 if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
7504 templ->mysql_length_bytes = (ulint)
7505 (((Field_varstring*) field)->length_bytes);
7506 } else {
7507 templ->mysql_length_bytes = 0;
7508 }
7509
7510 templ->charset = dtype_get_charset_coll(col->prtype);
7511 templ->mbminlen = dict_col_get_mbminlen(col);
7512 templ->mbmaxlen = dict_col_get_mbmaxlen(col);
7513 templ->is_unsigned = col->prtype & DATA_UNSIGNED;
7514
7515 if (!dict_index_is_clust(index)
7516 && templ->rec_field_no == ULINT_UNDEFINED) {
7517 prebuilt->need_to_access_clustered = TRUE;
7518
7519 if (templ->rec_prefix_field_no != ULINT_UNDEFINED) {
7520 dict_field_t* field = dict_index_get_nth_field(
7521 index,
7522 templ->rec_prefix_field_no);
7523 templ->rec_field_is_prefix = (field->prefix_len != 0);
7524 }
7525 }
7526
7527 /* For spatial index, we need to access cluster index. */
7528 if (dict_index_is_spatial(index)) {
7529 prebuilt->need_to_access_clustered = TRUE;
7530 }
7531
7532 if (prebuilt->mysql_prefix_len < templ->mysql_col_offset
7533 + templ->mysql_col_len) {
7534 prebuilt->mysql_prefix_len = templ->mysql_col_offset
7535 + templ->mysql_col_len;
7536 }
7537
7538 if (DATA_LARGE_MTYPE(templ->type)) {
7539 prebuilt->templ_contains_blob = TRUE;
7540 }
7541
7542 return(templ);
7543}
7544
7545/**************************************************************//**
7546Builds a 'template' to the m_prebuilt struct. The template is used in fast
7547retrieval of just those column values MySQL needs in its processing. */
7548
7549void
7550ha_innobase::build_template(
7551/*========================*/
7552 bool whole_row) /*!< in: true=ROW_MYSQL_WHOLE_ROW,
7553 false=ROW_MYSQL_REC_FIELDS */
7554{
7555 dict_index_t* index;
7556 dict_index_t* clust_index;
7557 ulint n_fields;
7558 ibool fetch_all_in_key = FALSE;
7559 ibool fetch_primary_key_cols = FALSE;
7560 ulint i;
7561
7562 if (m_prebuilt->select_lock_type == LOCK_X || m_prebuilt->table->no_rollback()) {
7563 /* We always retrieve the whole clustered index record if we
7564 use exclusive row level locks, for example, if the read is
7565 done in an UPDATE statement or if we are using a no rollback
7566 table */
7567
7568 whole_row = true;
7569 } else if (!whole_row) {
7570 if (m_prebuilt->hint_need_to_fetch_extra_cols
7571 == ROW_RETRIEVE_ALL_COLS) {
7572
7573 /* We know we must at least fetch all columns in the
7574 key, or all columns in the table */
7575
7576 if (m_prebuilt->read_just_key) {
7577 /* MySQL has instructed us that it is enough
7578 to fetch the columns in the key; looks like
7579 MySQL can set this flag also when there is
7580 only a prefix of the column in the key: in
7581 that case we retrieve the whole column from
7582 the clustered index */
7583
7584 fetch_all_in_key = TRUE;
7585 } else {
7586 whole_row = true;
7587 }
7588 } else if (m_prebuilt->hint_need_to_fetch_extra_cols
7589 == ROW_RETRIEVE_PRIMARY_KEY) {
7590 /* We must at least fetch all primary key cols. Note
7591 that if the clustered index was internally generated
7592 by InnoDB on the row id (no primary key was
7593 defined), then row_search_for_mysql() will always
7594 retrieve the row id to a special buffer in the
7595 m_prebuilt struct. */
7596
7597 fetch_primary_key_cols = TRUE;
7598 }
7599 }
7600
7601 clust_index = dict_table_get_first_index(m_prebuilt->table);
7602
7603 index = whole_row ? clust_index : m_prebuilt->index;
7604
7605 m_prebuilt->versioned_write = table->versioned_write(VERS_TRX_ID);
7606 m_prebuilt->need_to_access_clustered = (index == clust_index);
7607
7608 /* Either m_prebuilt->index should be a secondary index, or it
7609 should be the clustered index. */
7610 ut_ad(dict_index_is_clust(index) == (index == clust_index));
7611
7612 /* Below we check column by column if we need to access
7613 the clustered index. */
7614
7615 n_fields = (ulint) mysql_fields(table);
7616
7617 if (!m_prebuilt->mysql_template) {
7618 m_prebuilt->mysql_template = (mysql_row_templ_t*)
7619 ut_malloc_nokey(n_fields * sizeof(mysql_row_templ_t));
7620 }
7621
7622 m_prebuilt->template_type = whole_row
7623 ? ROW_MYSQL_WHOLE_ROW : ROW_MYSQL_REC_FIELDS;
7624 m_prebuilt->null_bitmap_len = table->s->null_bytes;
7625
7626 /* Prepare to build m_prebuilt->mysql_template[]. */
7627 m_prebuilt->templ_contains_blob = FALSE;
7628 m_prebuilt->mysql_prefix_len = 0;
7629 m_prebuilt->n_template = 0;
7630 m_prebuilt->idx_cond_n_cols = 0;
7631
7632 /* Note that in InnoDB, i is the column number in the table.
7633 MySQL calls columns 'fields'. */
7634
7635 if (active_index != MAX_KEY
7636 && active_index == pushed_idx_cond_keyno) {
7637 ulint num_v = 0;
7638
7639 /* Push down an index condition or an end_range check. */
7640 for (i = 0; i < n_fields; i++) {
7641 ibool index_contains;
7642
7643 if (innobase_is_v_fld(table->field[i])) {
7644 index_contains = dict_index_contains_col_or_prefix(
7645 index, num_v, true);
7646 if (index_contains)
7647 {
7648 m_prebuilt->n_template = 0;
7649 goto no_icp;
7650 }
7651 } else {
7652 index_contains = dict_index_contains_col_or_prefix(
7653 index, i - num_v, false);
7654 }
7655
7656 /* Test if an end_range or an index condition
7657 refers to the field. Note that "index" and
7658 "index_contains" may refer to the clustered index.
7659 Index condition pushdown is relative to
7660 m_prebuilt->index (the index that is being
7661 looked up first). */
7662
7663 /* When join_read_always_key() invokes this
7664 code via handler::ha_index_init() and
7665 ha_innobase::index_init(), end_range is not
7666 yet initialized. Because of that, we must
7667 always check for index_contains, instead of
7668 the subset
7669 field->part_of_key.is_set(active_index)
7670 which would be acceptable if end_range==NULL. */
7671 bool is_v = innobase_is_v_fld(table->field[i]);
7672 if (build_template_needs_field_in_icp(
7673 index, m_prebuilt, index_contains,
7674 is_v ? num_v : i - num_v, is_v)) {
7675 /* Needed in ICP */
7676 const Field* field;
7677 mysql_row_templ_t* templ;
7678
7679 if (whole_row) {
7680 field = table->field[i];
7681 } else {
7682 field = build_template_needs_field(
7683 index_contains,
7684 m_prebuilt->read_just_key,
7685 fetch_all_in_key,
7686 fetch_primary_key_cols,
7687 index, table, i, num_v);
7688 if (!field) {
7689 if (innobase_is_v_fld(
7690 table->field[i])) {
7691 num_v++;
7692 }
7693 continue;
7694 }
7695 }
7696
7697 templ = build_template_field(
7698 m_prebuilt, clust_index, index,
7699 table, field, i - num_v, 0);
7700
7701 ut_ad(!templ->is_virtual);
7702
7703 m_prebuilt->idx_cond_n_cols++;
7704 ut_ad(m_prebuilt->idx_cond_n_cols
7705 == m_prebuilt->n_template);
7706
7707 if (index == m_prebuilt->index) {
7708 templ->icp_rec_field_no
7709 = templ->rec_field_no;
7710 } else {
7711 templ->icp_rec_field_no
7712 = dict_index_get_nth_col_pos(
7713 m_prebuilt->index,
7714 i - num_v,
7715 &templ->rec_prefix_field_no);
7716 }
7717
7718 if (dict_index_is_clust(m_prebuilt->index)) {
7719 ut_ad(templ->icp_rec_field_no
7720 != ULINT_UNDEFINED);
7721 /* If the primary key includes
7722 a column prefix, use it in
7723 index condition pushdown,
7724 because the condition is
7725 evaluated before fetching any
7726 off-page (externally stored)
7727 columns. */
7728 if (templ->icp_rec_field_no
7729 < m_prebuilt->index->n_uniq) {
7730 /* This is a key column;
7731 all set. */
7732 continue;
7733 }
7734 } else if (templ->icp_rec_field_no
7735 != ULINT_UNDEFINED) {
7736 continue;
7737 }
7738
7739 /* This is a column prefix index.
7740 The column prefix can be used in
7741 an end_range comparison. */
7742
7743 templ->icp_rec_field_no
7744 = dict_index_get_nth_col_or_prefix_pos(
7745 m_prebuilt->index, i - num_v,
7746 true, false,
7747 &templ->rec_prefix_field_no);
7748 ut_ad(templ->icp_rec_field_no
7749 != ULINT_UNDEFINED);
7750
7751 /* Index condition pushdown can be used on
7752 all columns of a secondary index, and on
7753 the PRIMARY KEY columns. On the clustered
7754 index, it must never be used on other than
7755 PRIMARY KEY columns, because those columns
7756 may be stored off-page, and we will not
7757 fetch externally stored columns before
7758 checking the index condition. */
7759 /* TODO: test the above with an assertion
7760 like this. Note that index conditions are
7761 currently pushed down as part of the
7762 "optimizer phase" while end_range is done
7763 as part of the execution phase. Therefore,
7764 we were unable to use an accurate condition
7765 for end_range in the "if" condition above,
7766 and the following assertion would fail.
7767 ut_ad(!dict_index_is_clust(m_prebuilt->index)
7768 || templ->rec_field_no
7769 < m_prebuilt->index->n_uniq);
7770 */
7771 }
7772 if (innobase_is_v_fld(table->field[i])) {
7773 num_v++;
7774 }
7775 }
7776
7777 ut_ad(m_prebuilt->idx_cond_n_cols > 0);
7778 ut_ad(m_prebuilt->idx_cond_n_cols == m_prebuilt->n_template);
7779
7780 num_v = 0;
7781
7782 /* Include the fields that are not needed in index condition
7783 pushdown. */
7784 for (i = 0; i < n_fields; i++) {
7785 mysql_row_templ_t* templ;
7786 ibool index_contains;
7787
7788 if (innobase_is_v_fld(table->field[i])) {
7789 index_contains = dict_index_contains_col_or_prefix(
7790 index, num_v, true);
7791 } else {
7792 index_contains = dict_index_contains_col_or_prefix(
7793 index, i - num_v, false);
7794 }
7795
7796 bool is_v = innobase_is_v_fld(table->field[i]);
7797
7798 if (!build_template_needs_field_in_icp(
7799 index, m_prebuilt, index_contains,
7800 is_v ? num_v : i - num_v, is_v)) {
7801 /* Not needed in ICP */
7802 const Field* field;
7803
7804 if (whole_row) {
7805 field = table->field[i];
7806 } else {
7807 field = build_template_needs_field(
7808 index_contains,
7809 m_prebuilt->read_just_key,
7810 fetch_all_in_key,
7811 fetch_primary_key_cols,
7812 index, table, i, num_v);
7813 if (!field) {
7814 if (innobase_is_v_fld(table->field[i])) {
7815 num_v++;
7816 }
7817 continue;
7818 }
7819 }
7820
7821 templ = build_template_field(
7822 m_prebuilt, clust_index, index,
7823 table, field, i - num_v, num_v);
7824
7825 if (templ->is_virtual) {
7826 num_v++;
7827 }
7828 }
7829 }
7830
7831 m_prebuilt->idx_cond = this;
7832 } else {
7833no_icp:
7834 mysql_row_templ_t* templ;
7835 ulint num_v = 0;
7836 /* No index condition pushdown */
7837 m_prebuilt->idx_cond = NULL;
7838
7839 for (i = 0; i < n_fields; i++) {
7840 const Field* field;
7841
7842 if (whole_row) {
7843 /* Even this is whole_row, if the seach is
7844 on a virtual column, and read_just_key is
7845 set, and field is not in this index, we
7846 will not try to fill the value since they
7847 are not stored in such index nor in the
7848 cluster index. */
7849 if (innobase_is_v_fld(table->field[i])
7850 && m_prebuilt->read_just_key
7851 && !dict_index_contains_col_or_prefix(
7852 m_prebuilt->index, num_v, true))
7853 {
7854 /* Turn off ROW_MYSQL_WHOLE_ROW */
7855 m_prebuilt->template_type =
7856 ROW_MYSQL_REC_FIELDS;
7857 num_v++;
7858 continue;
7859 }
7860
7861 field = table->field[i];
7862 } else {
7863 ibool contain;
7864
7865 if (!innobase_is_v_fld(table->field[i])) {
7866 contain = dict_index_contains_col_or_prefix(
7867 index, i - num_v,
7868 false);
7869 } else if (dict_index_is_clust(index)) {
7870 num_v++;
7871 continue;
7872 } else {
7873 contain = dict_index_contains_col_or_prefix(
7874 index, num_v, true);
7875 }
7876
7877 field = build_template_needs_field(
7878 contain,
7879 m_prebuilt->read_just_key,
7880 fetch_all_in_key,
7881 fetch_primary_key_cols,
7882 index, table, i, num_v);
7883 if (!field) {
7884 if (innobase_is_v_fld(table->field[i])) {
7885 num_v++;
7886 }
7887 continue;
7888 }
7889 }
7890
7891 templ = build_template_field(
7892 m_prebuilt, clust_index, index,
7893 table, field, i - num_v, num_v);
7894 if (templ->is_virtual) {
7895 num_v++;
7896 }
7897 }
7898 }
7899
7900 if (index != clust_index && m_prebuilt->need_to_access_clustered) {
7901 /* Change rec_field_no's to correspond to the clustered index
7902 record */
7903 for (i = 0; i < m_prebuilt->n_template; i++) {
7904
7905 mysql_row_templ_t* templ
7906 = &m_prebuilt->mysql_template[i];
7907
7908 templ->rec_field_no = templ->clust_rec_field_no;
7909 }
7910 }
7911}
7912
7913/********************************************************************//**
7914This special handling is really to overcome the limitations of MySQL's
7915binlogging. We need to eliminate the non-determinism that will arise in
7916INSERT ... SELECT type of statements, since MySQL binlog only stores the
7917min value of the autoinc interval. Once that is fixed we can get rid of
7918the special lock handling.
7919@return DB_SUCCESS if all OK else error code */
7920
7921dberr_t
7922ha_innobase::innobase_lock_autoinc(void)
7923/*====================================*/
7924{
7925 DBUG_ENTER("ha_innobase::innobase_lock_autoinc");
7926 dberr_t error = DB_SUCCESS;
7927
7928 ut_ad(!srv_read_only_mode);
7929
7930 switch (innobase_autoinc_lock_mode) {
7931 case AUTOINC_NO_LOCKING:
7932 /* Acquire only the AUTOINC mutex. */
7933 dict_table_autoinc_lock(m_prebuilt->table);
7934 break;
7935
7936 case AUTOINC_NEW_STYLE_LOCKING:
7937 /* For simple (single/multi) row INSERTs/REPLACEs and RBR
7938 events, we fallback to the old style only if another
7939 transaction has already acquired the AUTOINC lock on
7940 behalf of a LOAD FILE or INSERT ... SELECT etc. type of
7941 statement. */
7942 if (thd_sql_command(m_user_thd) == SQLCOM_INSERT
7943 || thd_sql_command(m_user_thd) == SQLCOM_REPLACE
7944 || thd_sql_command(m_user_thd) == SQLCOM_END // RBR event
7945 ) {
7946
7947 /* Acquire the AUTOINC mutex. */
7948 dict_table_autoinc_lock(m_prebuilt->table);
7949
7950 /* We need to check that another transaction isn't
7951 already holding the AUTOINC lock on the table. */
7952 if (m_prebuilt->table->n_waiting_or_granted_auto_inc_locks) {
7953 /* Release the mutex to avoid deadlocks and
7954 fall back to old style locking. */
7955 dict_table_autoinc_unlock(m_prebuilt->table);
7956 } else {
7957 /* Do not fall back to old style locking. */
7958 break;
7959 }
7960 }
7961 /* Use old style locking. */
7962 /* fall through */
7963 case AUTOINC_OLD_STYLE_LOCKING:
7964 DBUG_EXECUTE_IF("die_if_autoinc_old_lock_style_used",
7965 ut_ad(0););
7966 error = row_lock_table_autoinc_for_mysql(m_prebuilt);
7967
7968 if (error == DB_SUCCESS) {
7969
7970 /* Acquire the AUTOINC mutex. */
7971 dict_table_autoinc_lock(m_prebuilt->table);
7972 }
7973 break;
7974
7975 default:
7976 ut_error;
7977 }
7978
7979 DBUG_RETURN(error);
7980}
7981
7982/********************************************************************//**
7983Store the autoinc value in the table. The autoinc value is only set if
7984it's greater than the existing autoinc value in the table.
7985@return DB_SUCCESS if all went well else error code */
7986
7987dberr_t
7988ha_innobase::innobase_set_max_autoinc(
7989/*==================================*/
7990 ulonglong auto_inc) /*!< in: value to store */
7991{
7992 dberr_t error;
7993
7994 error = innobase_lock_autoinc();
7995
7996 if (error == DB_SUCCESS) {
7997
7998 dict_table_autoinc_update_if_greater(m_prebuilt->table, auto_inc);
7999
8000 dict_table_autoinc_unlock(m_prebuilt->table);
8001 }
8002
8003 return(error);
8004}
8005
8006/********************************************************************//**
8007Stores a row in an InnoDB database, to the table specified in this
8008handle.
8009@return error code */
8010
8011int
8012ha_innobase::write_row(
8013/*===================*/
8014 uchar* record) /*!< in: a row in MySQL format */
8015{
8016 dberr_t error;
8017#ifdef WITH_WSREP
8018 ibool auto_inc_inserted= FALSE; /* if NULL was inserted */
8019#endif
8020 int error_result = 0;
8021 bool auto_inc_used = false;
8022
8023 DBUG_ENTER("ha_innobase::write_row");
8024
8025 trx_t* trx = thd_to_trx(m_user_thd);
8026
8027 /* Validation checks before we commence write_row operation. */
8028 if (high_level_read_only) {
8029 ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
8030 DBUG_RETURN(HA_ERR_TABLE_READONLY);
8031 }
8032
8033 ut_a(m_prebuilt->trx == trx);
8034
8035 if (!trx_is_started(trx)) {
8036 ++trx->will_lock;
8037 }
8038
8039 ins_mode_t vers_set_fields;
8040 /* Handling of Auto-Increment Columns. */
8041 if (table->next_number_field && record == table->record[0]) {
8042
8043 /* Reset the error code before calling
8044 innobase_get_auto_increment(). */
8045 m_prebuilt->autoinc_error = DB_SUCCESS;
8046
8047#ifdef WITH_WSREP
8048 auto_inc_inserted= (table->next_number_field->val_int() == 0);
8049#endif
8050
8051 if ((error_result = update_auto_increment())) {
8052 /* We don't want to mask autoinc overflow errors. */
8053
8054 /* Handle the case where the AUTOINC sub-system
8055 failed during initialization. */
8056 if (m_prebuilt->autoinc_error == DB_UNSUPPORTED) {
8057 error_result = ER_AUTOINC_READ_FAILED;
8058 /* Set the error message to report too. */
8059 my_error(ER_AUTOINC_READ_FAILED, MYF(0));
8060 goto func_exit;
8061 } else if (m_prebuilt->autoinc_error != DB_SUCCESS) {
8062 error = m_prebuilt->autoinc_error;
8063 goto report_error;
8064 }
8065
8066 /* MySQL errors are passed straight back. */
8067 goto func_exit;
8068 }
8069
8070 auto_inc_used = true;
8071 }
8072
8073 /* Prepare INSERT graph that will be executed for actual INSERT
8074 (This is a one time operation) */
8075 if (m_prebuilt->mysql_template == NULL
8076 || m_prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
8077
8078 /* Build the template used in converting quickly between
8079 the two database formats */
8080
8081 build_template(true);
8082 }
8083
8084 innobase_srv_conc_enter_innodb(m_prebuilt);
8085
8086 vers_set_fields = table->versioned_write(VERS_TRX_ID) ?
8087 ROW_INS_VERSIONED : ROW_INS_NORMAL;
8088
8089 /* Execute insert graph that will result in actual insert. */
8090 error = row_insert_for_mysql((byte*) record, m_prebuilt, vers_set_fields);
8091
8092 DEBUG_SYNC(m_user_thd, "ib_after_row_insert");
8093
8094 /* Handling of errors related to auto-increment. */
8095 if (auto_inc_used) {
8096 ulonglong auto_inc;
8097 ulonglong col_max_value;
8098
8099 /* Note the number of rows processed for this statement, used
8100 by get_auto_increment() to determine the number of AUTO-INC
8101 values to reserve. This is only useful for a mult-value INSERT
8102 and is a statement level counter. */
8103 if (trx->n_autoinc_rows > 0) {
8104 --trx->n_autoinc_rows;
8105 }
8106
8107 /* We need the upper limit of the col type to check for
8108 whether we update the table autoinc counter or not. */
8109 col_max_value = innobase_get_int_col_max_value(table->next_number_field);
8110
8111 /* Get the value that MySQL attempted to store in the table.*/
8112 auto_inc = table->next_number_field->val_uint();
8113
8114 switch (error) {
8115 case DB_DUPLICATE_KEY:
8116
8117 /* A REPLACE command and LOAD DATA INFILE REPLACE
8118 handle a duplicate key error themselves, but we
8119 must update the autoinc counter if we are performing
8120 those statements. */
8121
8122 switch (thd_sql_command(m_user_thd)) {
8123 case SQLCOM_LOAD:
8124 if (!trx->duplicates) {
8125 break;
8126 }
8127
8128 case SQLCOM_REPLACE:
8129 case SQLCOM_INSERT_SELECT:
8130 case SQLCOM_REPLACE_SELECT:
8131 goto set_max_autoinc;
8132
8133#ifdef WITH_WSREP
8134 /* workaround for LP bug #355000, retrying the insert */
8135 case SQLCOM_INSERT:
8136
8137 WSREP_DEBUG("DUPKEY error for autoinc\n"
8138 "THD %ld, value %llu, off %llu inc %llu",
8139 thd_get_thread_id(m_user_thd),
8140 auto_inc,
8141 m_prebuilt->autoinc_offset,
8142 m_prebuilt->autoinc_increment);
8143
8144 if (wsrep_on(m_user_thd) &&
8145 auto_inc_inserted &&
8146 wsrep_drupal_282555_workaround &&
8147 wsrep_thd_retry_counter(m_user_thd) == 0 &&
8148 !thd_test_options(m_user_thd,
8149 OPTION_NOT_AUTOCOMMIT |
8150 OPTION_BEGIN)) {
8151 WSREP_DEBUG(
8152 "retrying insert: %s",
8153 (*wsrep_thd_query(m_user_thd)) ?
8154 wsrep_thd_query(m_user_thd) :
8155 (char *)"void");
8156 error= DB_SUCCESS;
8157 wsrep_thd_set_conflict_state(
8158 m_user_thd, MUST_ABORT);
8159 innobase_srv_conc_exit_innodb(m_prebuilt);
8160 /* jump straight to func exit over
8161 * later wsrep hooks */
8162 goto func_exit;
8163 }
8164 break;
8165#endif /* WITH_WSREP */
8166
8167 default:
8168 break;
8169 }
8170
8171 break;
8172
8173 case DB_SUCCESS:
8174 /* If the actual value inserted is greater than
8175 the upper limit of the interval, then we try and
8176 update the table upper limit. Note: last_value
8177 will be 0 if get_auto_increment() was not called. */
8178
8179 if (auto_inc >= m_prebuilt->autoinc_last_value) {
8180set_max_autoinc:
8181 /* This should filter out the negative
8182 values set explicitly by the user. */
8183 if (auto_inc <= col_max_value) {
8184 ut_a(m_prebuilt->autoinc_increment > 0);
8185
8186 ulonglong offset;
8187 ulonglong increment;
8188 dberr_t err;
8189
8190 offset = m_prebuilt->autoinc_offset;
8191 increment = m_prebuilt->autoinc_increment;
8192
8193 auto_inc = innobase_next_autoinc(
8194 auto_inc,
8195 1, increment, offset,
8196 col_max_value);
8197
8198 err = innobase_set_max_autoinc(
8199 auto_inc);
8200
8201 if (err != DB_SUCCESS) {
8202 error = err;
8203 }
8204 }
8205 }
8206 break;
8207 default:
8208 break;
8209 }
8210 }
8211
8212 innobase_srv_conc_exit_innodb(m_prebuilt);
8213
8214report_error:
8215 /* Cleanup and exit. */
8216 if (error == DB_TABLESPACE_DELETED) {
8217 ib_senderrf(
8218 trx->mysql_thd, IB_LOG_LEVEL_ERROR,
8219 ER_TABLESPACE_DISCARDED,
8220 table->s->table_name.str);
8221 }
8222
8223 error_result = convert_error_code_to_mysql(
8224 error, m_prebuilt->table->flags, m_user_thd);
8225
8226#ifdef WITH_WSREP
8227 if (!error_result &&
8228 wsrep_thd_exec_mode(m_user_thd) == LOCAL_STATE &&
8229 wsrep_on(m_user_thd) &&
8230 !wsrep_consistency_check(m_user_thd) &&
8231 !wsrep_thd_ignore_table(m_user_thd)) {
8232 if (wsrep_append_keys(m_user_thd, false, record, NULL))
8233 {
8234 DBUG_PRINT("wsrep", ("row key failed"));
8235 error_result = HA_ERR_INTERNAL_ERROR;
8236 goto wsrep_error;
8237 }
8238 }
8239wsrep_error:
8240#endif /* WITH_WSREP */
8241
8242 if (error_result == HA_FTS_INVALID_DOCID) {
8243 my_error(HA_FTS_INVALID_DOCID, MYF(0));
8244 }
8245
8246func_exit:
8247 innobase_active_small();
8248
8249 DBUG_RETURN(error_result);
8250}
8251
8252/** Fill the update vector's "old_vrow" field for those non-updated,
8253but indexed columns. Such columns could stil present in the virtual
8254index rec fields even if they are not updated (some other fields updated),
8255so needs to be logged.
8256@param[in] prebuilt InnoDB prebuilt struct
8257@param[in,out] vfield field to filled
8258@param[in] o_len actual column length
8259@param[in,out] col column to be filled
8260@param[in] old_mysql_row_col MySQL old field ptr
8261@param[in] col_pack_len MySQL field col length
8262@param[in,out] buf buffer for a converted integer value
8263@return used buffer ptr from row_mysql_store_col_in_innobase_format() */
8264static
8265byte*
8266innodb_fill_old_vcol_val(
8267 row_prebuilt_t* prebuilt,
8268 dfield_t* vfield,
8269 ulint o_len,
8270 dict_col_t* col,
8271 const byte* old_mysql_row_col,
8272 ulint col_pack_len,
8273 byte* buf)
8274{
8275 dict_col_copy_type(
8276 col, dfield_get_type(vfield));
8277 if (o_len != UNIV_SQL_NULL) {
8278
8279 buf = row_mysql_store_col_in_innobase_format(
8280 vfield,
8281 buf,
8282 TRUE,
8283 old_mysql_row_col,
8284 col_pack_len,
8285 dict_table_is_comp(prebuilt->table));
8286 } else {
8287 dfield_set_null(vfield);
8288 }
8289
8290 return(buf);
8291}
8292
8293/** Calculate an update vector corresponding to the changes
8294between old_row and new_row.
8295@param[out] uvect update vector
8296@param[in] old_row current row in MySQL format
8297@param[in] new_row intended updated row in MySQL format
8298@param[in] table MySQL table handle
8299@param[in,out] upd_buff buffer to use for converted values
8300@param[in] buff_len length of upd_buff
8301@param[in,out] prebuilt InnoDB execution context
8302@param[out] auto_inc updated AUTO_INCREMENT value, or 0 if none
8303@return DB_SUCCESS or error code */
8304static
8305dberr_t
8306calc_row_difference(
8307 upd_t* uvect,
8308 const uchar* old_row,
8309 const uchar* new_row,
8310 TABLE* table,
8311 uchar* upd_buff,
8312 ulint buff_len,
8313 row_prebuilt_t* prebuilt,
8314 ib_uint64_t& auto_inc)
8315{
8316 uchar* original_upd_buff = upd_buff;
8317 Field* field;
8318 enum_field_types field_mysql_type;
8319 ulint o_len;
8320 ulint n_len;
8321 ulint col_pack_len;
8322 const byte* new_mysql_row_col;
8323 const byte* old_mysql_row_col;
8324 const byte* o_ptr;
8325 const byte* n_ptr;
8326 byte* buf;
8327 upd_field_t* ufield;
8328 ulint col_type;
8329 ulint n_changed = 0;
8330 dfield_t dfield;
8331 dict_index_t* clust_index;
8332 uint i;
8333 ibool changes_fts_column = FALSE;
8334 ibool changes_fts_doc_col = FALSE;
8335 trx_t* const trx = prebuilt->trx;
8336 doc_id_t doc_id = FTS_NULL_DOC_ID;
8337 ulint num_v = 0;
8338 uint n_fields = mysql_fields(table);
8339
8340 ut_ad(!srv_read_only_mode);
8341
8342 clust_index = dict_table_get_first_index(prebuilt->table);
8343 auto_inc = 0;
8344
8345 /* We use upd_buff to convert changed fields */
8346 buf = (byte*) upd_buff;
8347
8348 for (i = 0; i < n_fields; i++) {
8349 field = table->field[i];
8350 bool is_virtual = innobase_is_v_fld(field);
8351 dict_col_t* col;
8352
8353 if (is_virtual) {
8354 col = &prebuilt->table->v_cols[num_v].m_col;
8355 } else {
8356 col = &prebuilt->table->cols[i - num_v];
8357 }
8358
8359 o_ptr = (const byte*) old_row + get_field_offset(table, field);
8360 n_ptr = (const byte*) new_row + get_field_offset(table, field);
8361
8362 /* Use new_mysql_row_col and col_pack_len save the values */
8363
8364 new_mysql_row_col = n_ptr;
8365 old_mysql_row_col = o_ptr;
8366 col_pack_len = field->pack_length();
8367
8368 o_len = col_pack_len;
8369 n_len = col_pack_len;
8370
8371 /* We use o_ptr and n_ptr to dig up the actual data for
8372 comparison. */
8373
8374 field_mysql_type = field->type();
8375
8376 col_type = col->mtype;
8377
8378 switch (col_type) {
8379
8380 case DATA_BLOB:
8381 case DATA_GEOMETRY:
8382 o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
8383 n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
8384
8385 break;
8386
8387 case DATA_VARCHAR:
8388 case DATA_BINARY:
8389 case DATA_VARMYSQL:
8390 if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
8391 /* This is a >= 5.0.3 type true VARCHAR where
8392 the real payload data length is stored in
8393 1 or 2 bytes */
8394
8395 o_ptr = row_mysql_read_true_varchar(
8396 &o_len, o_ptr,
8397 (ulint)
8398 (((Field_varstring*) field)->length_bytes));
8399
8400 n_ptr = row_mysql_read_true_varchar(
8401 &n_len, n_ptr,
8402 (ulint)
8403 (((Field_varstring*) field)->length_bytes));
8404 }
8405
8406 break;
8407 default:
8408 ;
8409 }
8410
8411 if (field_mysql_type == MYSQL_TYPE_LONGLONG
8412 && prebuilt->table->fts
8413 && innobase_strcasecmp(
8414 field->field_name.str, FTS_DOC_ID_COL_NAME) == 0) {
8415 doc_id = (doc_id_t) mach_read_from_n_little_endian(
8416 n_ptr, 8);
8417 if (doc_id == 0) {
8418 return(DB_FTS_INVALID_DOCID);
8419 }
8420 }
8421
8422 if (field->real_maybe_null()) {
8423 if (field->is_null_in_record(old_row)) {
8424 o_len = UNIV_SQL_NULL;
8425 }
8426
8427 if (field->is_null_in_record(new_row)) {
8428 n_len = UNIV_SQL_NULL;
8429 }
8430 }
8431
8432#ifdef UNIV_DEBUG
8433 bool online_ord_part = false;
8434#endif
8435
8436 if (is_virtual) {
8437 /* If the virtual column is not indexed,
8438 we shall ignore it for update */
8439 if (!col->ord_part) {
8440 /* Check whether there is a table-rebuilding
8441 online ALTER TABLE in progress, and this
8442 virtual column could be newly indexed, thus
8443 it will be materialized. Then we will have
8444 to log its update.
8445 Note, we do not support online dropping virtual
8446 column while adding new index, nor with
8447 online alter column order while adding index,
8448 so the virtual column sequence must not change
8449 if it is online operation */
8450 if (dict_index_is_online_ddl(clust_index)
8451 && row_log_col_is_indexed(clust_index,
8452 num_v)) {
8453#ifdef UNIV_DEBUG
8454 online_ord_part = true;
8455#endif
8456 } else {
8457 num_v++;
8458 continue;
8459 }
8460 }
8461
8462 if (!uvect->old_vrow) {
8463 uvect->old_vrow = dtuple_create_with_vcol(
8464 uvect->heap, 0, prebuilt->table->n_v_cols);
8465 }
8466
8467 ulint max_field_len = DICT_MAX_FIELD_LEN_BY_FORMAT(
8468 prebuilt->table);
8469
8470 /* for virtual columns, we only materialize
8471 its index, and index field length would not
8472 exceed max_field_len. So continue if the
8473 first max_field_len bytes are matched up */
8474 if (o_len != UNIV_SQL_NULL
8475 && n_len != UNIV_SQL_NULL
8476 && o_len >= max_field_len
8477 && n_len >= max_field_len
8478 && memcmp(o_ptr, n_ptr, max_field_len) == 0) {
8479 dfield_t* vfield = dtuple_get_nth_v_field(
8480 uvect->old_vrow, num_v);
8481 buf = innodb_fill_old_vcol_val(
8482 prebuilt, vfield, o_len,
8483 col, old_mysql_row_col,
8484 col_pack_len, buf);
8485 num_v++;
8486 continue;
8487 }
8488 }
8489
8490 if (o_len != n_len || (o_len != 0 && o_len != UNIV_SQL_NULL
8491 && 0 != memcmp(o_ptr, n_ptr, o_len))) {
8492 /* The field has changed */
8493
8494 ufield = uvect->fields + n_changed;
8495 UNIV_MEM_INVALID(ufield, sizeof *ufield);
8496
8497 /* Let us use a dummy dfield to make the conversion
8498 from the MySQL column format to the InnoDB format */
8499
8500
8501 /* If the length of new geometry object is 0, means
8502 this object is invalid geometry object, we need
8503 to block it. */
8504 if (DATA_GEOMETRY_MTYPE(col_type)
8505 && o_len != 0 && n_len == 0) {
8506 return(DB_CANT_CREATE_GEOMETRY_OBJECT);
8507 }
8508
8509 if (n_len != UNIV_SQL_NULL) {
8510 dict_col_copy_type(
8511 col, dfield_get_type(&dfield));
8512
8513 buf = row_mysql_store_col_in_innobase_format(
8514 &dfield,
8515 (byte*) buf,
8516 TRUE,
8517 new_mysql_row_col,
8518 col_pack_len,
8519 dict_table_is_comp(prebuilt->table));
8520 dfield_copy(&ufield->new_val, &dfield);
8521 } else {
8522 dict_col_copy_type(
8523 col, dfield_get_type(&ufield->new_val));
8524 dfield_set_null(&ufield->new_val);
8525 }
8526
8527 ufield->exp = NULL;
8528 ufield->orig_len = 0;
8529 if (is_virtual) {
8530 dfield_t* vfield = dtuple_get_nth_v_field(
8531 uvect->old_vrow, num_v);
8532 upd_fld_set_virtual_col(ufield);
8533 ufield->field_no = num_v;
8534
8535 ut_ad(col->ord_part || online_ord_part);
8536 ufield->old_v_val = static_cast<dfield_t*>(
8537 mem_heap_alloc(
8538 uvect->heap,
8539 sizeof *ufield->old_v_val));
8540
8541 if (!field->is_null_in_record(old_row)) {
8542 if (n_len == UNIV_SQL_NULL) {
8543 dict_col_copy_type(
8544 col, dfield_get_type(
8545 &dfield));
8546 }
8547
8548 buf = row_mysql_store_col_in_innobase_format(
8549 &dfield,
8550 (byte*) buf,
8551 TRUE,
8552 old_mysql_row_col,
8553 col_pack_len,
8554 dict_table_is_comp(
8555 prebuilt->table));
8556 dfield_copy(ufield->old_v_val,
8557 &dfield);
8558 dfield_copy(vfield, &dfield);
8559 } else {
8560 dict_col_copy_type(
8561 col, dfield_get_type(
8562 ufield->old_v_val));
8563 dfield_set_null(ufield->old_v_val);
8564 dfield_set_null(vfield);
8565 }
8566 num_v++;
8567 ut_ad(field != table->found_next_number_field);
8568 } else {
8569 ufield->field_no = dict_col_get_clust_pos(
8570 &prebuilt->table->cols[i - num_v],
8571 clust_index);
8572 ufield->old_v_val = NULL;
8573 if (field != table->found_next_number_field
8574 || dfield_is_null(&ufield->new_val)) {
8575 } else {
8576 auto_inc = field->val_uint();
8577 }
8578 }
8579 n_changed++;
8580
8581 /* If an FTS indexed column was changed by this
8582 UPDATE then we need to inform the FTS sub-system.
8583
8584 NOTE: Currently we re-index all FTS indexed columns
8585 even if only a subset of the FTS indexed columns
8586 have been updated. That is the reason we are
8587 checking only once here. Later we will need to
8588 note which columns have been updated and do
8589 selective processing. */
8590 if (prebuilt->table->fts != NULL && !is_virtual) {
8591 ulint offset;
8592 dict_table_t* innodb_table;
8593
8594 innodb_table = prebuilt->table;
8595
8596 if (!changes_fts_column) {
8597 offset = row_upd_changes_fts_column(
8598 innodb_table, ufield);
8599
8600 if (offset != ULINT_UNDEFINED) {
8601 changes_fts_column = TRUE;
8602 }
8603 }
8604
8605 if (!changes_fts_doc_col) {
8606 changes_fts_doc_col =
8607 row_upd_changes_doc_id(
8608 innodb_table, ufield);
8609 }
8610 }
8611 } else if (is_virtual) {
8612 dfield_t* vfield = dtuple_get_nth_v_field(
8613 uvect->old_vrow, num_v);
8614 buf = innodb_fill_old_vcol_val(
8615 prebuilt, vfield, o_len,
8616 col, old_mysql_row_col,
8617 col_pack_len, buf);
8618 ut_ad(col->ord_part || online_ord_part);
8619 num_v++;
8620 }
8621 }
8622
8623 /* If the update changes a column with an FTS index on it, we
8624 then add an update column node with a new document id to the
8625 other changes. We piggy back our changes on the normal UPDATE
8626 to reduce processing and IO overhead. */
8627 if (!prebuilt->table->fts) {
8628 trx->fts_next_doc_id = 0;
8629 } else if (changes_fts_column || changes_fts_doc_col) {
8630 dict_table_t* innodb_table = prebuilt->table;
8631
8632 ufield = uvect->fields + n_changed;
8633
8634 if (!DICT_TF2_FLAG_IS_SET(
8635 innodb_table, DICT_TF2_FTS_HAS_DOC_ID)) {
8636
8637 /* If Doc ID is managed by user, and if any
8638 FTS indexed column has been updated, its corresponding
8639 Doc ID must also be updated. Otherwise, return
8640 error */
8641 if (changes_fts_column && !changes_fts_doc_col) {
8642 ib::warn() << "A new Doc ID must be supplied"
8643 " while updating FTS indexed columns.";
8644 return(DB_FTS_INVALID_DOCID);
8645 }
8646
8647 /* Doc ID must monotonically increase */
8648 ut_ad(innodb_table->fts->cache);
8649 if (doc_id < prebuilt->table->fts->cache->next_doc_id) {
8650
8651 ib::warn() << "FTS Doc ID must be larger than "
8652 << innodb_table->fts->cache->next_doc_id
8653 - 1 << " for table "
8654 << innodb_table->name;
8655
8656 return(DB_FTS_INVALID_DOCID);
8657 } else if ((doc_id
8658 - prebuilt->table->fts->cache->next_doc_id)
8659 >= FTS_DOC_ID_MAX_STEP) {
8660
8661 ib::warn() << "Doc ID " << doc_id << " is too"
8662 " big. Its difference with largest"
8663 " Doc ID used " << prebuilt->table->fts
8664 ->cache->next_doc_id - 1
8665 << " cannot exceed or equal to "
8666 << FTS_DOC_ID_MAX_STEP;
8667 }
8668
8669
8670 trx->fts_next_doc_id = doc_id;
8671 } else {
8672 /* If the Doc ID is a hidden column, it can't be
8673 changed by user */
8674 ut_ad(!changes_fts_doc_col);
8675
8676 /* Doc ID column is hidden, a new Doc ID will be
8677 generated by following fts_update_doc_id() call */
8678 trx->fts_next_doc_id = 0;
8679 }
8680
8681 fts_update_doc_id(
8682 innodb_table, ufield, &trx->fts_next_doc_id);
8683
8684 ++n_changed;
8685 } else {
8686 /* We have a Doc ID column, but none of FTS indexed
8687 columns are touched, nor the Doc ID column, so set
8688 fts_next_doc_id to UINT64_UNDEFINED, which means do not
8689 update the Doc ID column */
8690 trx->fts_next_doc_id = UINT64_UNDEFINED;
8691 }
8692
8693 uvect->n_fields = n_changed;
8694 uvect->info_bits = 0;
8695
8696 ut_a(buf <= (byte*) original_upd_buff + buff_len);
8697
8698 ut_ad(uvect->validate());
8699 return(DB_SUCCESS);
8700}
8701
8702#ifdef WITH_WSREP
8703static
8704int
8705wsrep_calc_row_hash(
8706/*================*/
8707 byte* digest, /*!< in/out: md5 sum */
8708 const uchar* row, /*!< in: row in MySQL format */
8709 TABLE* table, /*!< in: table in MySQL data
8710 dictionary */
8711 row_prebuilt_t* prebuilt) /*!< in: InnoDB prebuilt struct */
8712{
8713 Field* field;
8714 enum_field_types field_mysql_type;
8715 uint n_fields;
8716 ulint len;
8717 const byte* ptr;
8718 ulint col_type;
8719 uint i;
8720
8721 void *ctx = alloca(my_md5_context_size());
8722 my_md5_init(ctx);
8723
8724 n_fields = mysql_fields(table);
8725
8726 for (i = 0; i < n_fields; i++) {
8727 byte null_byte=0;
8728 byte true_byte=1;
8729
8730 field = table->field[i];
8731
8732 ptr = (const byte*) row + get_field_offset(table, field);
8733 len = field->pack_length();
8734
8735 field_mysql_type = field->type();
8736
8737 col_type = prebuilt->table->cols[i].mtype;
8738
8739 switch (col_type) {
8740
8741 case DATA_BLOB:
8742 ptr = row_mysql_read_blob_ref(&len, ptr, len);
8743
8744 break;
8745
8746 case DATA_VARCHAR:
8747 case DATA_BINARY:
8748 case DATA_VARMYSQL:
8749 if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
8750 /* This is a >= 5.0.3 type true VARCHAR where
8751 the real payload data length is stored in
8752 1 or 2 bytes */
8753
8754 ptr = row_mysql_read_true_varchar(
8755 &len, ptr,
8756 (ulint)
8757 (((Field_varstring*)field)->length_bytes));
8758
8759 }
8760
8761 break;
8762 default:
8763 ;
8764 }
8765 /*
8766 if (field->null_ptr &&
8767 field_in_record_is_null(table, field, (char*) row)) {
8768 */
8769
8770 if (field->is_null_in_record(row)) {
8771 my_md5_input(ctx, &null_byte, 1);
8772 } else {
8773 my_md5_input(ctx, &true_byte, 1);
8774 my_md5_input(ctx, ptr, len);
8775 }
8776 }
8777
8778 my_md5_result(ctx, digest);
8779
8780 return(0);
8781}
8782#endif /* WITH_WSREP */
8783
8784/**
8785Updates a row given as a parameter to a new value. Note that we are given
8786whole rows, not just the fields which are updated: this incurs some
8787overhead for CPU when we check which fields are actually updated.
8788TODO: currently InnoDB does not prevent the 'Halloween problem':
8789in a searched update a single row can get updated several times
8790if its index columns are updated!
8791@param[in] old_row Old row contents in MySQL format
8792@param[out] new_row Updated row contents in MySQL format
8793@return error number or 0 */
8794
8795int
8796ha_innobase::update_row(
8797 const uchar* old_row,
8798 const uchar* new_row)
8799{
8800 int err;
8801
8802 dberr_t error;
8803 trx_t* trx = thd_to_trx(m_user_thd);
8804
8805 DBUG_ENTER("ha_innobase::update_row");
8806
8807 ut_a(m_prebuilt->trx == trx);
8808
8809 if (high_level_read_only) {
8810 ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
8811 DBUG_RETURN(HA_ERR_TABLE_READONLY);
8812 } else if (!trx_is_started(trx)) {
8813 ++trx->will_lock;
8814 }
8815
8816 if (m_upd_buf == NULL) {
8817 ut_ad(m_upd_buf_size == 0);
8818
8819 /* Create a buffer for packing the fields of a record. Why
8820 table->reclength did not work here? Obviously, because char
8821 fields when packed actually became 1 byte longer, when we also
8822 stored the string length as the first byte. */
8823
8824 m_upd_buf_size = table->s->reclength + table->s->max_key_length
8825 + MAX_REF_PARTS * 3;
8826
8827 m_upd_buf = reinterpret_cast<uchar*>(
8828 my_malloc(//PSI_INSTRUMENT_ME,
8829 m_upd_buf_size,
8830 MYF(MY_WME)));
8831
8832 if (m_upd_buf == NULL) {
8833 m_upd_buf_size = 0;
8834 DBUG_RETURN(HA_ERR_OUT_OF_MEM);
8835 }
8836 }
8837
8838 upd_t* uvect = row_get_prebuilt_update_vector(m_prebuilt);
8839 ib_uint64_t autoinc;
8840
8841 /* Build an update vector from the modified fields in the rows
8842 (uses m_upd_buf of the handle) */
8843
8844 error = calc_row_difference(
8845 uvect, old_row, new_row, table, m_upd_buf, m_upd_buf_size,
8846 m_prebuilt, autoinc);
8847
8848 if (error != DB_SUCCESS) {
8849 goto func_exit;
8850 }
8851
8852 if (!uvect->n_fields) {
8853 /* This is the same as success, but instructs
8854 MySQL that the row is not really updated and it
8855 should not increase the count of updated rows.
8856 This is fix for http://bugs.mysql.com/29157 */
8857 DBUG_RETURN(HA_ERR_RECORD_IS_THE_SAME);
8858 } else {
8859 const bool vers_set_fields = m_prebuilt->versioned_write
8860 && m_prebuilt->upd_node->update->affects_versioned();
8861 const bool vers_ins_row = vers_set_fields
8862 && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE;
8863
8864 /* This is not a delete */
8865 m_prebuilt->upd_node->is_delete =
8866 (vers_set_fields && !vers_ins_row) ||
8867 (thd_sql_command(m_user_thd) == SQLCOM_DELETE &&
8868 table->versioned(VERS_TIMESTAMP))
8869 ? VERSIONED_DELETE
8870 : NO_DELETE;
8871
8872 innobase_srv_conc_enter_innodb(m_prebuilt);
8873
8874 error = row_update_for_mysql(m_prebuilt);
8875
8876 if (error == DB_SUCCESS && vers_ins_row
8877 /* Multiple UPDATE of same rows in single transaction create
8878 historical rows only once. */
8879 && trx->id != table->vers_start_id()) {
8880 error = row_insert_for_mysql((byte*) old_row,
8881 m_prebuilt,
8882 ROW_INS_HISTORICAL);
8883 }
8884 }
8885
8886 if (error == DB_SUCCESS && autoinc) {
8887 /* A value for an AUTO_INCREMENT column
8888 was specified in the UPDATE statement. */
8889
8890 autoinc = innobase_next_autoinc(
8891 autoinc, 1,
8892 m_prebuilt->autoinc_increment,
8893 m_prebuilt->autoinc_offset,
8894 innobase_get_int_col_max_value(
8895 table->found_next_number_field));
8896
8897 error = innobase_set_max_autoinc(autoinc);
8898
8899 if (m_prebuilt->table->persistent_autoinc) {
8900 /* Update the PAGE_ROOT_AUTO_INC. Yes, we do
8901 this even if dict_table_t::autoinc already was
8902 greater than autoinc, because we cannot know
8903 if any INSERT actually used (and wrote to
8904 PAGE_ROOT_AUTO_INC) a value bigger than our
8905 autoinc. */
8906 btr_write_autoinc(dict_table_get_first_index(
8907 m_prebuilt->table),
8908 autoinc);
8909 }
8910 }
8911
8912 innobase_srv_conc_exit_innodb(m_prebuilt);
8913
8914func_exit:
8915 if (error == DB_FTS_INVALID_DOCID) {
8916 err = HA_FTS_INVALID_DOCID;
8917 my_error(HA_FTS_INVALID_DOCID, MYF(0));
8918 } else {
8919 err = convert_error_code_to_mysql(
8920 error, m_prebuilt->table->flags, m_user_thd);
8921 }
8922
8923 /* Tell InnoDB server that there might be work for
8924 utility threads: */
8925
8926 innobase_active_small();
8927
8928#ifdef WITH_WSREP
8929 if (error == DB_SUCCESS &&
8930 wsrep_thd_exec_mode(m_user_thd) == LOCAL_STATE &&
8931 wsrep_on(m_user_thd) &&
8932 !wsrep_thd_ignore_table(m_user_thd)) {
8933 DBUG_PRINT("wsrep", ("update row key"));
8934
8935 if (wsrep_append_keys(m_user_thd, false, old_row, new_row)) {
8936 WSREP_DEBUG("WSREP: UPDATE_ROW_KEY FAILED");
8937 DBUG_PRINT("wsrep", ("row key failed"));
8938 err = HA_ERR_INTERNAL_ERROR;
8939 goto wsrep_error;
8940 }
8941 }
8942wsrep_error:
8943#endif /* WITH_WSREP */
8944
8945
8946 DBUG_RETURN(err);
8947}
8948
8949/**********************************************************************//**
8950Deletes a row given as the parameter.
8951@return error number or 0 */
8952
8953int
8954ha_innobase::delete_row(
8955/*====================*/
8956 const uchar* record) /*!< in: a row in MySQL format */
8957{
8958 dberr_t error;
8959 trx_t* trx = thd_to_trx(m_user_thd);
8960
8961 DBUG_ENTER("ha_innobase::delete_row");
8962
8963 ut_a(m_prebuilt->trx == trx);
8964
8965 if (high_level_read_only) {
8966 ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
8967 DBUG_RETURN(HA_ERR_TABLE_READONLY);
8968 } else if (!trx_is_started(trx)) {
8969 ++trx->will_lock;
8970 }
8971
8972 if (!m_prebuilt->upd_node) {
8973 row_get_prebuilt_update_vector(m_prebuilt);
8974 }
8975
8976 /* This is a delete */
8977 m_prebuilt->upd_node->is_delete = table->versioned_write(VERS_TRX_ID)
8978 && table->vers_end_field()->is_max()
8979 && trx->id != table->vers_start_id()
8980 ? VERSIONED_DELETE
8981 : PLAIN_DELETE;
8982
8983 innobase_srv_conc_enter_innodb(m_prebuilt);
8984
8985 error = row_update_for_mysql(m_prebuilt);
8986
8987 innobase_srv_conc_exit_innodb(m_prebuilt);
8988
8989 /* Tell the InnoDB server that there might be work for
8990 utility threads: */
8991
8992 innobase_active_small();
8993
8994#ifdef WITH_WSREP
8995 if (error == DB_SUCCESS &&
8996 wsrep_thd_exec_mode(m_user_thd) == LOCAL_STATE &&
8997 wsrep_on(m_user_thd) &&
8998 !wsrep_thd_ignore_table(m_user_thd)) {
8999 if (wsrep_append_keys(m_user_thd, false, record, NULL)) {
9000 DBUG_PRINT("wsrep", ("delete fail"));
9001 error = (dberr_t) HA_ERR_INTERNAL_ERROR;
9002 goto wsrep_error;
9003 }
9004 }
9005wsrep_error:
9006#endif /* WITH_WSREP */
9007 DBUG_RETURN(convert_error_code_to_mysql(
9008 error, m_prebuilt->table->flags, m_user_thd));
9009}
9010
9011/** Delete all rows from the table.
9012@return error number or 0 */
9013
9014int
9015ha_innobase::delete_all_rows()
9016{
9017 DBUG_ENTER("ha_innobase::delete_all_rows");
9018 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
9019}
9020
9021/**********************************************************************//**
9022Removes a new lock set on a row, if it was not read optimistically. This can
9023be called after a row has been read in the processing of an UPDATE or a DELETE
9024query, if the option innodb_locks_unsafe_for_binlog is set. */
9025
9026void
9027ha_innobase::unlock_row(void)
9028/*=========================*/
9029{
9030 DBUG_ENTER("ha_innobase::unlock_row");
9031
9032 if (m_prebuilt->select_lock_type == LOCK_NONE) {
9033 DBUG_VOID_RETURN;
9034 }
9035
9036 ut_ad(trx_state_eq(m_prebuilt->trx, TRX_STATE_ACTIVE, true));
9037
9038 switch (m_prebuilt->row_read_type) {
9039 case ROW_READ_WITH_LOCKS:
9040 if (!srv_locks_unsafe_for_binlog
9041 && m_prebuilt->trx->isolation_level
9042 > TRX_ISO_READ_COMMITTED) {
9043 break;
9044 }
9045 /* fall through */
9046 case ROW_READ_TRY_SEMI_CONSISTENT:
9047 row_unlock_for_mysql(m_prebuilt, FALSE);
9048 break;
9049 case ROW_READ_DID_SEMI_CONSISTENT:
9050 m_prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
9051 break;
9052 }
9053
9054 DBUG_VOID_RETURN;
9055}
9056
9057/* See handler.h and row0mysql.h for docs on this function. */
9058
9059bool
9060ha_innobase::was_semi_consistent_read(void)
9061/*=======================================*/
9062{
9063 return(m_prebuilt->row_read_type == ROW_READ_DID_SEMI_CONSISTENT);
9064}
9065
9066/* See handler.h and row0mysql.h for docs on this function. */
9067
9068void
9069ha_innobase::try_semi_consistent_read(bool yes)
9070/*===========================================*/
9071{
9072 ut_a(m_prebuilt->trx == thd_to_trx(ha_thd()));
9073
9074 /* Row read type is set to semi consistent read if this was
9075 requested by the MySQL and either innodb_locks_unsafe_for_binlog
9076 option is used or this session is using READ COMMITTED isolation
9077 level. */
9078
9079 if (yes
9080 && (srv_locks_unsafe_for_binlog
9081 || m_prebuilt->trx->isolation_level
9082 <= TRX_ISO_READ_COMMITTED)) {
9083
9084 m_prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
9085
9086 } else {
9087 m_prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
9088 }
9089}
9090
9091/******************************************************************//**
9092Initializes a handle to use an index.
9093@return 0 or error number */
9094
9095int
9096ha_innobase::index_init(
9097/*====================*/
9098 uint keynr, /*!< in: key (index) number */
9099 bool)
9100{
9101 DBUG_ENTER("index_init");
9102
9103 DBUG_RETURN(change_active_index(keynr));
9104}
9105
9106/******************************************************************//**
9107Currently does nothing.
9108@return 0 */
9109
9110int
9111ha_innobase::index_end(void)
9112/*========================*/
9113{
9114 DBUG_ENTER("index_end");
9115
9116 active_index = MAX_KEY;
9117
9118 in_range_check_pushed_down = FALSE;
9119
9120 m_ds_mrr.dsmrr_close();
9121
9122 DBUG_RETURN(0);
9123}
9124
9125/*********************************************************************//**
9126Converts a search mode flag understood by MySQL to a flag understood
9127by InnoDB. */
9128page_cur_mode_t
9129convert_search_mode_to_innobase(
9130/*============================*/
9131 ha_rkey_function find_flag)
9132{
9133 switch (find_flag) {
9134 case HA_READ_KEY_EXACT:
9135 /* this does not require the index to be UNIQUE */
9136 case HA_READ_KEY_OR_NEXT:
9137 return(PAGE_CUR_GE);
9138 case HA_READ_AFTER_KEY:
9139 return(PAGE_CUR_G);
9140 case HA_READ_BEFORE_KEY:
9141 return(PAGE_CUR_L);
9142 case HA_READ_KEY_OR_PREV:
9143 case HA_READ_PREFIX_LAST:
9144 case HA_READ_PREFIX_LAST_OR_PREV:
9145 return(PAGE_CUR_LE);
9146 case HA_READ_MBR_CONTAIN:
9147 return(PAGE_CUR_CONTAIN);
9148 case HA_READ_MBR_INTERSECT:
9149 return(PAGE_CUR_INTERSECT);
9150 case HA_READ_MBR_WITHIN:
9151 return(PAGE_CUR_WITHIN);
9152 case HA_READ_MBR_DISJOINT:
9153 return(PAGE_CUR_DISJOINT);
9154 case HA_READ_MBR_EQUAL:
9155 return(PAGE_CUR_MBR_EQUAL);
9156 case HA_READ_PREFIX:
9157 return(PAGE_CUR_UNSUPP);
9158 /* do not use "default:" in order to produce a gcc warning:
9159 enumeration value '...' not handled in switch
9160 (if -Wswitch or -Wall is used) */
9161 }
9162
9163 my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "this functionality");
9164
9165 return(PAGE_CUR_UNSUPP);
9166}
9167
9168/*
9169 BACKGROUND INFO: HOW A SELECT SQL QUERY IS EXECUTED
9170 ---------------------------------------------------
9171The following does not cover all the details, but explains how we determine
9172the start of a new SQL statement, and what is associated with it.
9173
9174For each table in the database the MySQL interpreter may have several
9175table handle instances in use, also in a single SQL query. For each table
9176handle instance there is an InnoDB 'm_prebuilt' struct which contains most
9177of the InnoDB data associated with this table handle instance.
9178
9179 A) if the user has not explicitly set any MySQL table level locks:
9180
9181 1) MySQL calls ::external_lock to set an 'intention' table level lock on
9182the table of the handle instance. There we set
9183m_prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set
9184true if we are taking this table handle instance to use in a new SQL
9185statement issued by the user. We also increment trx->n_mysql_tables_in_use.
9186
9187 2) If m_prebuilt->sql_stat_start == TRUE we 'pre-compile' the MySQL search
9188instructions to m_prebuilt->template of the table handle instance in
9189::index_read. The template is used to save CPU time in large joins.
9190
9191 3) In row_search_for_mysql, if m_prebuilt->sql_stat_start is true, we
9192allocate a new consistent read view for the trx if it does not yet have one,
9193or in the case of a locking read, set an InnoDB 'intention' table level
9194lock on the table.
9195
9196 4) We do the SELECT. MySQL may repeatedly call ::index_read for the
9197same table handle instance, if it is a join.
9198
9199 5) When the SELECT ends, MySQL removes its intention table level locks
9200in ::external_lock. When trx->n_mysql_tables_in_use drops to zero,
9201 (a) we execute a COMMIT there if the autocommit is on,
9202 (b) we also release possible 'SQL statement level resources' InnoDB may
9203have for this SQL statement. The MySQL interpreter does NOT execute
9204autocommit for pure read transactions, though it should. That is why the
9205table handler in that case has to execute the COMMIT in ::external_lock.
9206
9207 B) If the user has explicitly set MySQL table level locks, then MySQL
9208does NOT call ::external_lock at the start of the statement. To determine
9209when we are at the start of a new SQL statement we at the start of
9210::index_read also compare the query id to the latest query id where the
9211table handle instance was used. If it has changed, we know we are at the
9212start of a new SQL statement. Since the query id can theoretically
9213overwrap, we use this test only as a secondary way of determining the
9214start of a new SQL statement. */
9215
9216
9217/**********************************************************************//**
9218Positions an index cursor to the index specified in the handle. Fetches the
9219row if any.
9220@return 0, HA_ERR_KEY_NOT_FOUND, or error number */
9221
9222int
9223ha_innobase::index_read(
9224/*====================*/
9225 uchar* buf, /*!< in/out: buffer for the returned
9226 row */
9227 const uchar* key_ptr, /*!< in: key value; if this is NULL
9228 we position the cursor at the
9229 start or end of index; this can
9230 also contain an InnoDB row id, in
9231 which case key_len is the InnoDB
9232 row id length; the key value can
9233 also be a prefix of a full key value,
9234 and the last column can be a prefix
9235 of a full column */
9236 uint key_len,/*!< in: key value length */
9237 enum ha_rkey_function find_flag)/*!< in: search flags from my_base.h */
9238{
9239 DBUG_ENTER("index_read");
9240 DEBUG_SYNC_C("ha_innobase_index_read_begin");
9241
9242 ut_a(m_prebuilt->trx == thd_to_trx(m_user_thd));
9243 ut_ad(key_len != 0 || find_flag != HA_READ_KEY_EXACT);
9244
9245 dict_index_t* index = m_prebuilt->index;
9246
9247 if (index == NULL || index->is_corrupted()) {
9248 m_prebuilt->index_usable = FALSE;
9249 DBUG_RETURN(HA_ERR_CRASHED);
9250 }
9251
9252 if (!m_prebuilt->index_usable) {
9253 DBUG_RETURN(index->is_corrupted()
9254 ? HA_ERR_INDEX_CORRUPT
9255 : HA_ERR_TABLE_DEF_CHANGED);
9256 }
9257
9258 if (index->type & DICT_FTS) {
9259 DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
9260 }
9261
9262 /* For R-Tree index, we will always place the page lock to
9263 pages being searched */
9264 if (dict_index_is_spatial(index)) {
9265 ++m_prebuilt->trx->will_lock;
9266 }
9267
9268 /* Note that if the index for which the search template is built is not
9269 necessarily m_prebuilt->index, but can also be the clustered index */
9270
9271 if (m_prebuilt->sql_stat_start) {
9272 build_template(false);
9273 }
9274
9275 if (key_ptr != NULL) {
9276 /* Convert the search key value to InnoDB format into
9277 m_prebuilt->search_tuple */
9278
9279 row_sel_convert_mysql_key_to_innobase(
9280 m_prebuilt->search_tuple,
9281 m_prebuilt->srch_key_val1,
9282 m_prebuilt->srch_key_val_len,
9283 index,
9284 (byte*) key_ptr,
9285 (ulint) key_len);
9286
9287 DBUG_ASSERT(m_prebuilt->search_tuple->n_fields > 0);
9288 } else {
9289 /* We position the cursor to the last or the first entry
9290 in the index */
9291
9292 dtuple_set_n_fields(m_prebuilt->search_tuple, 0);
9293 }
9294
9295 page_cur_mode_t mode = convert_search_mode_to_innobase(find_flag);
9296
9297 ulint match_mode = 0;
9298
9299 if (find_flag == HA_READ_KEY_EXACT) {
9300
9301 match_mode = ROW_SEL_EXACT;
9302
9303 } else if (find_flag == HA_READ_PREFIX_LAST) {
9304
9305 match_mode = ROW_SEL_EXACT_PREFIX;
9306 }
9307
9308 m_last_match_mode = (uint) match_mode;
9309
9310 dberr_t ret;
9311
9312 if (mode != PAGE_CUR_UNSUPP) {
9313
9314 innobase_srv_conc_enter_innodb(m_prebuilt);
9315
9316 ret = row_search_mvcc(
9317 buf, mode, m_prebuilt, match_mode, 0);
9318
9319 innobase_srv_conc_exit_innodb(m_prebuilt);
9320 } else {
9321
9322 ret = DB_UNSUPPORTED;
9323 }
9324
9325 DBUG_EXECUTE_IF("ib_select_query_failure", ret = DB_ERROR;);
9326
9327 int error;
9328
9329 switch (ret) {
9330 case DB_SUCCESS:
9331 error = 0;
9332 table->status = 0;
9333 if (m_prebuilt->table->is_system_db) {
9334 srv_stats.n_system_rows_read.add(
9335 thd_get_thread_id(m_prebuilt->trx->mysql_thd), 1);
9336 } else {
9337 srv_stats.n_rows_read.add(
9338 thd_get_thread_id(m_prebuilt->trx->mysql_thd), 1);
9339 }
9340 break;
9341
9342 case DB_RECORD_NOT_FOUND:
9343 error = HA_ERR_KEY_NOT_FOUND;
9344 table->status = STATUS_NOT_FOUND;
9345 break;
9346
9347 case DB_END_OF_INDEX:
9348 error = HA_ERR_KEY_NOT_FOUND;
9349 table->status = STATUS_NOT_FOUND;
9350 break;
9351
9352 case DB_TABLESPACE_DELETED:
9353 ib_senderrf(
9354 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
9355 ER_TABLESPACE_DISCARDED,
9356 table->s->table_name.str);
9357
9358 table->status = STATUS_NOT_FOUND;
9359 error = HA_ERR_NO_SUCH_TABLE;
9360 break;
9361
9362 case DB_TABLESPACE_NOT_FOUND:
9363
9364 ib_senderrf(
9365 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
9366 ER_TABLESPACE_MISSING,
9367 table->s->table_name.str);
9368
9369 table->status = STATUS_NOT_FOUND;
9370 //error = HA_ERR_TABLESPACE_MISSING;
9371 error = HA_ERR_NO_SUCH_TABLE;
9372 break;
9373
9374 default:
9375 error = convert_error_code_to_mysql(
9376 ret, m_prebuilt->table->flags, m_user_thd);
9377
9378 table->status = STATUS_NOT_FOUND;
9379 break;
9380 }
9381
9382 DBUG_RETURN(error);
9383}
9384
9385/*******************************************************************//**
9386The following functions works like index_read, but it find the last
9387row with the current key value or prefix.
9388@return 0, HA_ERR_KEY_NOT_FOUND, or an error code */
9389
9390int
9391ha_innobase::index_read_last(
9392/*=========================*/
9393 uchar* buf, /*!< out: fetched row */
9394 const uchar* key_ptr,/*!< in: key value, or a prefix of a full
9395 key value */
9396 uint key_len)/*!< in: length of the key val or prefix
9397 in bytes */
9398{
9399 return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
9400}
9401
9402/********************************************************************//**
9403Get the index for a handle. Does not change active index.
9404@return NULL or index instance. */
9405
9406dict_index_t*
9407ha_innobase::innobase_get_index(
9408/*============================*/
9409 uint keynr) /*!< in: use this index; MAX_KEY means always
9410 clustered index, even if it was internally
9411 generated by InnoDB */
9412{
9413 KEY* key;
9414 dict_index_t* index;
9415
9416 DBUG_ENTER("innobase_get_index");
9417
9418 if (keynr != MAX_KEY && table->s->keys > 0) {
9419
9420 key = table->key_info + keynr;
9421
9422 index = innobase_index_lookup(m_share, keynr);
9423
9424 if (index != NULL) {
9425 if (!key || ut_strcmp(index->name, key->name.str) != 0) {
9426 ib::error() << " Index for key no " << keynr
9427 << " mysql name " << (key ? key->name.str : "NULL")
9428 << " InnoDB name " << index->name()
9429 << " for table " << m_prebuilt->table->name.m_name;
9430
9431 for(uint i=0; i < table->s->keys; i++) {
9432 index = innobase_index_lookup(m_share, i);
9433 key = table->key_info + keynr;
9434
9435 if (index) {
9436 ib::info() << " Index for key no " << keynr
9437 << " mysql name " << (key ? key->name.str : "NULL")
9438 << " InnoDB name " << index->name()
9439 << " for table " << m_prebuilt->table->name.m_name;
9440 }
9441 }
9442
9443 }
9444
9445 ut_a(ut_strcmp(index->name, key->name.str) == 0);
9446 } else {
9447 /* Can't find index with keynr in the translation
9448 table. Only print message if the index translation
9449 table exists */
9450 if (m_share->idx_trans_tbl.index_mapping != NULL) {
9451 sql_print_warning("InnoDB could not find"
9452 " index %s key no %u for"
9453 " table %s through its"
9454 " index translation table",
9455 key ? key->name.str : "NULL",
9456 keynr,
9457 m_prebuilt->table->name
9458 .m_name);
9459 }
9460
9461 index = dict_table_get_index_on_name(
9462 m_prebuilt->table, key->name.str);
9463 }
9464 } else {
9465 key = 0;
9466 index = dict_table_get_first_index(m_prebuilt->table);
9467 }
9468
9469 if (index == NULL) {
9470 sql_print_error(
9471 "InnoDB could not find key no %u with name %s"
9472 " from dict cache for table %s",
9473 keynr, key ? key->name.str : "NULL",
9474 m_prebuilt->table->name.m_name);
9475 }
9476
9477 DBUG_RETURN(index);
9478}
9479
9480/********************************************************************//**
9481Changes the active index of a handle.
9482@return 0 or error code */
9483
9484int
9485ha_innobase::change_active_index(
9486/*=============================*/
9487 uint keynr) /*!< in: use this index; MAX_KEY means always clustered
9488 index, even if it was internally generated by
9489 InnoDB */
9490{
9491 DBUG_ENTER("change_active_index");
9492
9493 ut_ad(m_user_thd == ha_thd());
9494 ut_a(m_prebuilt->trx == thd_to_trx(m_user_thd));
9495
9496 active_index = keynr;
9497
9498 m_prebuilt->index = innobase_get_index(keynr);
9499
9500 if (m_prebuilt->index == NULL) {
9501 sql_print_warning("InnoDB: change_active_index(%u) failed",
9502 keynr);
9503 m_prebuilt->index_usable = FALSE;
9504 DBUG_RETURN(1);
9505 }
9506
9507 m_prebuilt->index_usable = row_merge_is_index_usable(
9508 m_prebuilt->trx, m_prebuilt->index);
9509
9510 if (!m_prebuilt->index_usable) {
9511 if (m_prebuilt->index->is_corrupted()) {
9512 char table_name[MAX_FULL_NAME_LEN + 1];
9513
9514 innobase_format_name(
9515 table_name, sizeof table_name,
9516 m_prebuilt->index->table->name.m_name);
9517
9518 if (m_prebuilt->index->is_primary()) {
9519 ut_ad(m_prebuilt->index->table->corrupted);
9520 push_warning_printf(
9521 m_user_thd, Sql_condition::WARN_LEVEL_WARN,
9522 ER_TABLE_CORRUPT,
9523 "InnoDB: Table %s is corrupted.",
9524 table_name);
9525 DBUG_RETURN(ER_TABLE_CORRUPT);
9526 } else {
9527 push_warning_printf(
9528 m_user_thd, Sql_condition::WARN_LEVEL_WARN,
9529 HA_ERR_INDEX_CORRUPT,
9530 "InnoDB: Index %s for table %s is"
9531 " marked as corrupted",
9532 m_prebuilt->index->name(),
9533 table_name);
9534 DBUG_RETURN(HA_ERR_INDEX_CORRUPT);
9535 }
9536 } else {
9537 push_warning_printf(
9538 m_user_thd, Sql_condition::WARN_LEVEL_WARN,
9539 HA_ERR_TABLE_DEF_CHANGED,
9540 "InnoDB: insufficient history for index %u",
9541 keynr);
9542 }
9543
9544 /* The caller seems to ignore this. Thus, we must check
9545 this again in row_search_for_mysql(). */
9546 DBUG_RETURN(convert_error_code_to_mysql(DB_MISSING_HISTORY,
9547 0, NULL));
9548 }
9549
9550 ut_a(m_prebuilt->search_tuple != 0);
9551
9552 /* Initialization of search_tuple is not needed for FT index
9553 since FT search returns rank only. In addition engine should
9554 be able to retrieve FTS_DOC_ID column value if necessary. */
9555 if ((m_prebuilt->index->type & DICT_FTS)) {
9556#ifdef MYSQL_STORE_FTS_DOC_ID
9557 if (table->fts_doc_id_field
9558 && bitmap_is_set(table->read_set,
9559 table->fts_doc_id_field->field_index
9560 && m_prebuilt->read_just_key)) {
9561 m_prebuilt->fts_doc_id_in_read_set = 1;
9562 }
9563#endif
9564 } else {
9565 dtuple_set_n_fields(m_prebuilt->search_tuple,
9566 m_prebuilt->index->n_fields);
9567
9568 dict_index_copy_types(
9569 m_prebuilt->search_tuple, m_prebuilt->index,
9570 m_prebuilt->index->n_fields);
9571
9572 /* If it's FTS query and FTS_DOC_ID exists FTS_DOC_ID field is
9573 always added to read_set. */
9574
9575#ifdef MYSQL_STORE_FTS_DOC_ID
9576 m_prebuilt->fts_doc_id_in_read_set =
9577 (m_prebuilt->read_just_key && table->fts_doc_id_field
9578 && m_prebuilt->in_fts_query);
9579#endif
9580
9581 }
9582
9583 /* MySQL changes the active index for a handle also during some
9584 queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
9585 and then calculates the sum. Previously we played safe and used
9586 the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
9587 copying. Starting from MySQL-4.1 we use a more efficient flag here. */
9588
9589 build_template(false);
9590
9591 DBUG_RETURN(0);
9592}
9593
9594/***********************************************************************//**
9595Reads the next or previous row from a cursor, which must have previously been
9596positioned using index_read.
9597@return 0, HA_ERR_END_OF_FILE, or error number */
9598
9599int
9600ha_innobase::general_fetch(
9601/*=======================*/
9602 uchar* buf, /*!< in/out: buffer for next row in MySQL
9603 format */
9604 uint direction, /*!< in: ROW_SEL_NEXT or ROW_SEL_PREV */
9605 uint match_mode) /*!< in: 0, ROW_SEL_EXACT, or
9606 ROW_SEL_EXACT_PREFIX */
9607{
9608 DBUG_ENTER("general_fetch");
9609
9610 const trx_t* trx = m_prebuilt->trx;
9611
9612 ut_ad(trx == thd_to_trx(m_user_thd));
9613
9614 if (m_prebuilt->table->is_readable()) {
9615 } else if (m_prebuilt->table->corrupted) {
9616 DBUG_RETURN(HA_ERR_CRASHED);
9617 } else {
9618 DBUG_RETURN(m_prebuilt->table->space
9619 ? HA_ERR_DECRYPTION_FAILED
9620 : HA_ERR_NO_SUCH_TABLE);
9621 }
9622
9623 innobase_srv_conc_enter_innodb(m_prebuilt);
9624
9625 dberr_t ret = row_search_mvcc(
9626 buf, PAGE_CUR_UNSUPP, m_prebuilt, match_mode, direction);
9627
9628 innobase_srv_conc_exit_innodb(m_prebuilt);
9629
9630 int error;
9631
9632 switch (ret) {
9633 case DB_SUCCESS:
9634 error = 0;
9635 table->status = 0;
9636 if (m_prebuilt->table->is_system_db) {
9637 srv_stats.n_system_rows_read.add(
9638 thd_get_thread_id(trx->mysql_thd), 1);
9639 } else {
9640 srv_stats.n_rows_read.add(
9641 thd_get_thread_id(trx->mysql_thd), 1);
9642 }
9643 break;
9644 case DB_RECORD_NOT_FOUND:
9645 error = HA_ERR_END_OF_FILE;
9646 table->status = STATUS_NOT_FOUND;
9647 break;
9648 case DB_END_OF_INDEX:
9649 error = HA_ERR_END_OF_FILE;
9650 table->status = STATUS_NOT_FOUND;
9651 break;
9652 case DB_TABLESPACE_DELETED:
9653 ib_senderrf(
9654 trx->mysql_thd, IB_LOG_LEVEL_ERROR,
9655 ER_TABLESPACE_DISCARDED,
9656 table->s->table_name.str);
9657
9658 table->status = STATUS_NOT_FOUND;
9659 error = HA_ERR_NO_SUCH_TABLE;
9660 break;
9661 case DB_TABLESPACE_NOT_FOUND:
9662
9663 ib_senderrf(
9664 trx->mysql_thd, IB_LOG_LEVEL_ERROR,
9665 ER_TABLESPACE_MISSING,
9666 table->s->table_name.str);
9667
9668 table->status = STATUS_NOT_FOUND;
9669 error = HA_ERR_TABLESPACE_MISSING;
9670 break;
9671 default:
9672 error = convert_error_code_to_mysql(
9673 ret, m_prebuilt->table->flags, m_user_thd);
9674
9675 table->status = STATUS_NOT_FOUND;
9676 break;
9677 }
9678
9679 DBUG_RETURN(error);
9680}
9681
9682/***********************************************************************//**
9683Reads the next row from a cursor, which must have previously been
9684positioned using index_read.
9685@return 0, HA_ERR_END_OF_FILE, or error number */
9686
9687int
9688ha_innobase::index_next(
9689/*====================*/
9690 uchar* buf) /*!< in/out: buffer for next row in MySQL
9691 format */
9692{
9693 return(general_fetch(buf, ROW_SEL_NEXT, 0));
9694}
9695
9696/*******************************************************************//**
9697Reads the next row matching to the key value given as the parameter.
9698@return 0, HA_ERR_END_OF_FILE, or error number */
9699
9700int
9701ha_innobase::index_next_same(
9702/*=========================*/
9703 uchar* buf, /*!< in/out: buffer for the row */
9704 const uchar*, uint)
9705{
9706 return(general_fetch(buf, ROW_SEL_NEXT, m_last_match_mode));
9707}
9708
9709/***********************************************************************//**
9710Reads the previous row from a cursor, which must have previously been
9711positioned using index_read.
9712@return 0, HA_ERR_END_OF_FILE, or error number */
9713
9714int
9715ha_innobase::index_prev(
9716/*====================*/
9717 uchar* buf) /*!< in/out: buffer for previous row in MySQL format */
9718{
9719 return(general_fetch(buf, ROW_SEL_PREV, 0));
9720}
9721
9722/********************************************************************//**
9723Positions a cursor on the first record in an index and reads the
9724corresponding row to buf.
9725@return 0, HA_ERR_END_OF_FILE, or error code */
9726
9727int
9728ha_innobase::index_first(
9729/*=====================*/
9730 uchar* buf) /*!< in/out: buffer for the row */
9731{
9732 DBUG_ENTER("index_first");
9733
9734 int error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
9735
9736 /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
9737
9738 if (error == HA_ERR_KEY_NOT_FOUND) {
9739 error = HA_ERR_END_OF_FILE;
9740 }
9741
9742 DBUG_RETURN(error);
9743}
9744
9745/********************************************************************//**
9746Positions a cursor on the last record in an index and reads the
9747corresponding row to buf.
9748@return 0, HA_ERR_END_OF_FILE, or error code */
9749
9750int
9751ha_innobase::index_last(
9752/*====================*/
9753 uchar* buf) /*!< in/out: buffer for the row */
9754{
9755 DBUG_ENTER("index_last");
9756
9757 int error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
9758
9759 /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
9760
9761 if (error == HA_ERR_KEY_NOT_FOUND) {
9762 error = HA_ERR_END_OF_FILE;
9763 }
9764
9765 DBUG_RETURN(error);
9766}
9767
9768/****************************************************************//**
9769Initialize a table scan.
9770@return 0 or error number */
9771
9772int
9773ha_innobase::rnd_init(
9774/*==================*/
9775 bool scan) /*!< in: true if table/index scan FALSE otherwise */
9776{
9777 int err;
9778
9779 /* Store the active index value so that we can restore the original
9780 value after a scan */
9781
9782 if (m_prebuilt->clust_index_was_generated) {
9783 err = change_active_index(MAX_KEY);
9784 } else {
9785 err = change_active_index(m_primary_key);
9786 }
9787
9788 /* Don't use semi-consistent read in random row reads (by position).
9789 This means we must disable semi_consistent_read if scan is false */
9790
9791 if (!scan) {
9792 try_semi_consistent_read(0);
9793 }
9794
9795 m_start_of_scan = true;
9796
9797 return(err);
9798}
9799
9800/*****************************************************************//**
9801Ends a table scan.
9802@return 0 or error number */
9803
9804int
9805ha_innobase::rnd_end(void)
9806/*======================*/
9807{
9808 return(index_end());
9809}
9810
9811/*****************************************************************//**
9812Reads the next row in a table scan (also used to read the FIRST row
9813in a table scan).
9814@return 0, HA_ERR_END_OF_FILE, or error number */
9815
9816int
9817ha_innobase::rnd_next(
9818/*==================*/
9819 uchar* buf) /*!< in/out: returns the row in this buffer,
9820 in MySQL format */
9821{
9822 int error;
9823
9824 DBUG_ENTER("rnd_next");
9825
9826 if (m_start_of_scan) {
9827 error = index_first(buf);
9828
9829 if (error == HA_ERR_KEY_NOT_FOUND) {
9830 error = HA_ERR_END_OF_FILE;
9831 }
9832
9833 m_start_of_scan = false;
9834 } else {
9835 error = general_fetch(buf, ROW_SEL_NEXT, 0);
9836 }
9837
9838 DBUG_RETURN(error);
9839}
9840
9841/**********************************************************************//**
9842Fetches a row from the table based on a row reference.
9843@return 0, HA_ERR_KEY_NOT_FOUND, or error code */
9844
9845int
9846ha_innobase::rnd_pos(
9847/*=================*/
9848 uchar* buf, /*!< in/out: buffer for the row */
9849 uchar* pos) /*!< in: primary key value of the row in the
9850 MySQL format, or the row id if the clustered
9851 index was internally generated by InnoDB; the
9852 length of data in pos has to be ref_length */
9853{
9854 DBUG_ENTER("rnd_pos");
9855 DBUG_DUMP("key", pos, ref_length);
9856
9857 ut_a(m_prebuilt->trx == thd_to_trx(ha_thd()));
9858
9859 /* Note that we assume the length of the row reference is fixed
9860 for the table, and it is == ref_length */
9861
9862 int error = index_read(buf, pos, (uint)ref_length, HA_READ_KEY_EXACT);
9863
9864 if (error != 0) {
9865 DBUG_PRINT("error", ("Got error: %d", error));
9866 }
9867
9868 DBUG_RETURN(error);
9869}
9870
9871/**********************************************************************//**
9872Initialize FT index scan
9873@return 0 or error number */
9874
9875int
9876ha_innobase::ft_init()
9877/*==================*/
9878{
9879 DBUG_ENTER("ft_init");
9880
9881 trx_t* trx = check_trx_exists(ha_thd());
9882
9883 /* FTS queries are not treated as autocommit non-locking selects.
9884 This is because the FTS implementation can acquire locks behind
9885 the scenes. This has not been verified but it is safer to treat
9886 them as regular read only transactions for now. */
9887
9888 if (!trx_is_started(trx)) {
9889 ++trx->will_lock;
9890 }
9891
9892 DBUG_RETURN(rnd_init(false));
9893}
9894
9895/**********************************************************************//**
9896Initialize FT index scan
9897@return FT_INFO structure if successful or NULL */
9898
9899FT_INFO*
9900ha_innobase::ft_init_ext(
9901/*=====================*/
9902 uint flags, /* in: */
9903 uint keynr, /* in: */
9904 String* key) /* in: */
9905{
9906 NEW_FT_INFO* fts_hdl = NULL;
9907 dict_index_t* index;
9908 fts_result_t* result;
9909 char buf_tmp[8192];
9910 ulint buf_tmp_used;
9911 uint num_errors;
9912 ulint query_len = key->length();
9913 const CHARSET_INFO* char_set = key->charset();
9914 const char* query = key->ptr();
9915
9916 if (fts_enable_diag_print) {
9917 {
9918 ib::info out;
9919 out << "keynr=" << keynr << ", '";
9920 out.write(key->ptr(), key->length());
9921 }
9922
9923 if (flags & FT_BOOL) {
9924 ib::info() << "BOOL search";
9925 } else {
9926 ib::info() << "NL search";
9927 }
9928 }
9929
9930 /* FIXME: utf32 and utf16 are not compatible with some
9931 string function used. So to convert them to uft8 before
9932 we proceed. */
9933 if (strcmp(char_set->csname, "utf32") == 0
9934 || strcmp(char_set->csname, "utf16") == 0) {
9935
9936 buf_tmp_used = innobase_convert_string(
9937 buf_tmp, sizeof(buf_tmp) - 1,
9938 &my_charset_utf8_general_ci,
9939 query, query_len, (CHARSET_INFO*) char_set,
9940 &num_errors);
9941
9942 buf_tmp[buf_tmp_used] = 0;
9943 query = buf_tmp;
9944 query_len = buf_tmp_used;
9945 }
9946
9947 trx_t* trx = m_prebuilt->trx;
9948
9949 /* FTS queries are not treated as autocommit non-locking selects.
9950 This is because the FTS implementation can acquire locks behind
9951 the scenes. This has not been verified but it is safer to treat
9952 them as regular read only transactions for now. */
9953
9954 if (!trx_is_started(trx)) {
9955 ++trx->will_lock;
9956 }
9957
9958 dict_table_t* ft_table = m_prebuilt->table;
9959
9960 /* Table does not have an FTS index */
9961 if (!ft_table->fts || ib_vector_is_empty(ft_table->fts->indexes)) {
9962 my_error(ER_TABLE_HAS_NO_FT, MYF(0));
9963 return(NULL);
9964 }
9965
9966 /* If tablespace is discarded, we should return here */
9967 if (!ft_table->space) {
9968 my_error(ER_NO_SUCH_TABLE, MYF(0), table->s->db.str,
9969 table->s->table_name.str);
9970 return(NULL);
9971 }
9972
9973 if (keynr == NO_SUCH_KEY) {
9974 /* FIXME: Investigate the NO_SUCH_KEY usage */
9975 index = reinterpret_cast<dict_index_t*>
9976 (ib_vector_getp(ft_table->fts->indexes, 0));
9977 } else {
9978 index = innobase_get_index(keynr);
9979 }
9980
9981 if (index == NULL || index->type != DICT_FTS) {
9982 my_error(ER_TABLE_HAS_NO_FT, MYF(0));
9983 return(NULL);
9984 }
9985
9986 if (!(ft_table->fts->fts_status & ADDED_TABLE_SYNCED)) {
9987 fts_init_index(ft_table, FALSE);
9988
9989 ft_table->fts->fts_status |= ADDED_TABLE_SYNCED;
9990 }
9991
9992 const byte* q = reinterpret_cast<const byte*>(
9993 const_cast<char*>(query));
9994
9995 // FIXME: support ft_init_ext_with_hints(), pass LIMIT
9996 // FIXME: use trx
9997 dberr_t error = fts_query(index, flags, q, query_len, &result);
9998
9999 if (error != DB_SUCCESS) {
10000 my_error(convert_error_code_to_mysql(error, 0, NULL), MYF(0));
10001 return(NULL);
10002 }
10003
10004 /* Allocate FTS handler, and instantiate it before return */
10005 fts_hdl = reinterpret_cast<NEW_FT_INFO*>(
10006 my_malloc(/*PSI_INSTRUMENT_ME,*/ sizeof(NEW_FT_INFO), MYF(0)));
10007
10008 fts_hdl->please = const_cast<_ft_vft*>(&ft_vft_result);
10009 fts_hdl->could_you = const_cast<_ft_vft_ext*>(&ft_vft_ext_result);
10010 fts_hdl->ft_prebuilt = m_prebuilt;
10011 fts_hdl->ft_result = result;
10012
10013 /* FIXME: Re-evaluate the condition when Bug 14469540 is resolved */
10014 m_prebuilt->in_fts_query = true;
10015
10016 return(reinterpret_cast<FT_INFO*>(fts_hdl));
10017}
10018
10019/*****************************************************************//**
10020Set up search tuple for a query through FTS_DOC_ID_INDEX on
10021supplied Doc ID. This is used by MySQL to retrieve the documents
10022once the search result (Doc IDs) is available */
10023static
10024void
10025innobase_fts_create_doc_id_key(
10026/*===========================*/
10027 dtuple_t* tuple, /* in/out: m_prebuilt->search_tuple */
10028 const dict_index_t*
10029 index, /* in: index (FTS_DOC_ID_INDEX) */
10030 doc_id_t* doc_id) /* in/out: doc id to search, value
10031 could be changed to storage format
10032 used for search. */
10033{
10034 doc_id_t temp_doc_id;
10035 dfield_t* dfield = dtuple_get_nth_field(tuple, 0);
10036
10037 ut_a(dict_index_get_n_unique(index) == 1);
10038
10039 dtuple_set_n_fields(tuple, index->n_fields);
10040 dict_index_copy_types(tuple, index, index->n_fields);
10041
10042#ifdef UNIV_DEBUG
10043 /* The unique Doc ID field should be an eight-bytes integer */
10044 dict_field_t* field = dict_index_get_nth_field(index, 0);
10045 ut_a(field->col->mtype == DATA_INT);
10046 ut_ad(sizeof(*doc_id) == field->fixed_len);
10047 ut_ad(!strcmp(index->name, FTS_DOC_ID_INDEX_NAME));
10048#endif /* UNIV_DEBUG */
10049
10050 /* Convert to storage byte order */
10051 mach_write_to_8(reinterpret_cast<byte*>(&temp_doc_id), *doc_id);
10052 *doc_id = temp_doc_id;
10053 dfield_set_data(dfield, doc_id, sizeof(*doc_id));
10054
10055 dtuple_set_n_fields_cmp(tuple, 1);
10056
10057 for (ulint i = 1; i < index->n_fields; i++) {
10058 dfield = dtuple_get_nth_field(tuple, i);
10059 dfield_set_null(dfield);
10060 }
10061}
10062
10063/**********************************************************************//**
10064Fetch next result from the FT result set
10065@return error code */
10066
10067int
10068ha_innobase::ft_read(
10069/*=================*/
10070 uchar* buf) /*!< in/out: buf contain result row */
10071{
10072 row_prebuilt_t* ft_prebuilt;
10073
10074 ft_prebuilt = reinterpret_cast<NEW_FT_INFO*>(ft_handler)->ft_prebuilt;
10075
10076 ut_a(ft_prebuilt == m_prebuilt);
10077
10078 fts_result_t* result;
10079
10080 result = reinterpret_cast<NEW_FT_INFO*>(ft_handler)->ft_result;
10081
10082 if (result->current == NULL) {
10083 /* This is the case where the FTS query did not
10084 contain and matching documents. */
10085 if (result->rankings_by_id != NULL) {
10086 /* Now that we have the complete result, we
10087 need to sort the document ids on their rank
10088 calculation. */
10089
10090 fts_query_sort_result_on_rank(result);
10091
10092 result->current = const_cast<ib_rbt_node_t*>(
10093 rbt_first(result->rankings_by_rank));
10094 } else {
10095 ut_a(result->current == NULL);
10096 }
10097 } else {
10098 result->current = const_cast<ib_rbt_node_t*>(
10099 rbt_next(result->rankings_by_rank, result->current));
10100 }
10101
10102next_record:
10103
10104 if (result->current != NULL) {
10105 doc_id_t search_doc_id;
10106 dtuple_t* tuple = m_prebuilt->search_tuple;
10107
10108 /* If we only need information from result we can return
10109 without fetching the table row */
10110 if (ft_prebuilt->read_just_key) {
10111#ifdef MYSQL_STORE_FTS_DOC_ID
10112 if (m_prebuilt->fts_doc_id_in_read_set) {
10113 fts_ranking_t* ranking;
10114 ranking = rbt_value(fts_ranking_t,
10115 result->current);
10116 innobase_fts_store_docid(
10117 table, ranking->doc_id);
10118 }
10119#endif
10120 table->status= 0;
10121 return(0);
10122 }
10123
10124 dict_index_t* index;
10125
10126 index = m_prebuilt->table->fts_doc_id_index;
10127
10128 /* Must find the index */
10129 ut_a(index != NULL);
10130
10131 /* Switch to the FTS doc id index */
10132 m_prebuilt->index = index;
10133
10134 fts_ranking_t* ranking = rbt_value(
10135 fts_ranking_t, result->current);
10136
10137 search_doc_id = ranking->doc_id;
10138
10139 /* We pass a pointer of search_doc_id because it will be
10140 converted to storage byte order used in the search
10141 tuple. */
10142 innobase_fts_create_doc_id_key(tuple, index, &search_doc_id);
10143
10144 innobase_srv_conc_enter_innodb(m_prebuilt);
10145
10146 dberr_t ret = row_search_for_mysql(
10147 (byte*) buf, PAGE_CUR_GE, m_prebuilt, ROW_SEL_EXACT, 0);
10148
10149 innobase_srv_conc_exit_innodb(m_prebuilt);
10150
10151 int error;
10152
10153 switch (ret) {
10154 case DB_SUCCESS:
10155 error = 0;
10156 table->status = 0;
10157 break;
10158 case DB_RECORD_NOT_FOUND:
10159 result->current = const_cast<ib_rbt_node_t*>(
10160 rbt_next(result->rankings_by_rank,
10161 result->current));
10162
10163 if (!result->current) {
10164 /* exhaust the result set, should return
10165 HA_ERR_END_OF_FILE just like
10166 ha_innobase::general_fetch() and/or
10167 ha_innobase::index_first() etc. */
10168 error = HA_ERR_END_OF_FILE;
10169 table->status = STATUS_NOT_FOUND;
10170 } else {
10171 goto next_record;
10172 }
10173 break;
10174 case DB_END_OF_INDEX:
10175 error = HA_ERR_END_OF_FILE;
10176 table->status = STATUS_NOT_FOUND;
10177 break;
10178 case DB_TABLESPACE_DELETED:
10179
10180 ib_senderrf(
10181 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
10182 ER_TABLESPACE_DISCARDED,
10183 table->s->table_name.str);
10184
10185 table->status = STATUS_NOT_FOUND;
10186 error = HA_ERR_NO_SUCH_TABLE;
10187 break;
10188 case DB_TABLESPACE_NOT_FOUND:
10189
10190 ib_senderrf(
10191 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
10192 ER_TABLESPACE_MISSING,
10193 table->s->table_name.str);
10194
10195 table->status = STATUS_NOT_FOUND;
10196 error = HA_ERR_TABLESPACE_MISSING;
10197 break;
10198 default:
10199 error = convert_error_code_to_mysql(
10200 ret, 0, m_user_thd);
10201
10202 table->status = STATUS_NOT_FOUND;
10203 break;
10204 }
10205
10206 return(error);
10207 }
10208
10209 return(HA_ERR_END_OF_FILE);
10210}
10211
10212/*************************************************************************
10213*/
10214
10215void
10216ha_innobase::ft_end()
10217{
10218 ib::info() << "ft_end()";
10219
10220 rnd_end();
10221}
10222#ifdef WITH_WSREP
10223extern dict_index_t*
10224wsrep_dict_foreign_find_index(
10225/*==========================*/
10226 dict_table_t* table,
10227 const char** col_names,
10228 const char** columns,
10229 ulint n_cols,
10230 dict_index_t* types_idx,
10231 ibool check_charsets,
10232 ulint check_null);
10233
10234
10235extern dberr_t
10236wsrep_append_foreign_key(
10237/*===========================*/
10238 trx_t* trx, /*!< in: trx */
10239 dict_foreign_t* foreign, /*!< in: foreign key constraint */
10240 const rec_t* rec, /*!<in: clustered index record */
10241 dict_index_t* index, /*!<in: clustered index */
10242 ibool referenced, /*!<in: is check for referenced table */
10243 ibool shared) /*!<in: is shared access */
10244{
10245 ut_a(trx);
10246 THD* thd = (THD*)trx->mysql_thd;
10247 ulint rcode = DB_SUCCESS;
10248 char cache_key[513] = {'\0'};
10249 int cache_key_len=0;
10250 bool const copy = true;
10251
10252 if (!wsrep_on(trx->mysql_thd) ||
10253 wsrep_thd_exec_mode(thd) != LOCAL_STATE) {
10254 return DB_SUCCESS;
10255 }
10256
10257 if (!thd || !foreign ||
10258 (!foreign->referenced_table && !foreign->foreign_table)) {
10259 WSREP_INFO("FK: %s missing in: %s",
10260 (!thd) ? "thread" :
10261 ((!foreign) ? "constraint" :
10262 ((!foreign->referenced_table) ?
10263 "referenced table" : "foreign table")),
10264 (thd && wsrep_thd_query(thd)) ?
10265 wsrep_thd_query(thd) : "void");
10266 return DB_ERROR;
10267 }
10268
10269 if ( !((referenced) ?
10270 foreign->referenced_table : foreign->foreign_table)) {
10271 WSREP_DEBUG("pulling %s table into cache",
10272 (referenced) ? "referenced" : "foreign");
10273 mutex_enter(&(dict_sys->mutex));
10274
10275 if (referenced) {
10276 foreign->referenced_table =
10277 dict_table_get_low(
10278 foreign->referenced_table_name_lookup);
10279 if (foreign->referenced_table) {
10280 foreign->referenced_index =
10281 wsrep_dict_foreign_find_index(
10282 foreign->referenced_table, NULL,
10283 foreign->referenced_col_names,
10284 foreign->n_fields,
10285 foreign->foreign_index,
10286 TRUE, FALSE);
10287 }
10288 } else {
10289 foreign->foreign_table =
10290 dict_table_get_low(
10291 foreign->foreign_table_name_lookup);
10292
10293 if (foreign->foreign_table) {
10294 foreign->foreign_index =
10295 wsrep_dict_foreign_find_index(
10296 foreign->foreign_table, NULL,
10297 foreign->foreign_col_names,
10298 foreign->n_fields,
10299 foreign->referenced_index,
10300 TRUE, FALSE);
10301 }
10302 }
10303 mutex_exit(&(dict_sys->mutex));
10304 }
10305
10306 if ( !((referenced) ?
10307 foreign->referenced_table : foreign->foreign_table)) {
10308 WSREP_WARN("FK: %s missing in query: %s",
10309 (!foreign->referenced_table) ?
10310 "referenced table" : "foreign table",
10311 (wsrep_thd_query(thd)) ?
10312 wsrep_thd_query(thd) : "void");
10313 return DB_ERROR;
10314 }
10315
10316 byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
10317 ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
10318
10319 dict_index_t *idx_target = (referenced) ?
10320 foreign->referenced_index : index;
10321 dict_index_t *idx = (referenced) ?
10322 UT_LIST_GET_FIRST(foreign->referenced_table->indexes) :
10323 UT_LIST_GET_FIRST(foreign->foreign_table->indexes);
10324 int i = 0;
10325
10326 while (idx != NULL && idx != idx_target) {
10327 if (innobase_strcasecmp (idx->name, innobase_index_reserve_name) != 0) {
10328 i++;
10329 }
10330 idx = UT_LIST_GET_NEXT(indexes, idx);
10331 }
10332
10333 ut_a(idx);
10334 key[0] = byte(i);
10335
10336 rcode = wsrep_rec_get_foreign_key(
10337 &key[1], &len, rec, index, idx,
10338 wsrep_protocol_version > 1);
10339
10340 if (rcode != DB_SUCCESS) {
10341 WSREP_ERROR(
10342 "FK key set failed: " ULINTPF
10343 " (" ULINTPF " " ULINTPF "), index: %s %s, %s",
10344 rcode, referenced, shared,
10345 (index) ? index->name() : "void index",
10346 (index && index->table) ? index->table->name.m_name :
10347 "void table",
10348 wsrep_thd_query(thd));
10349 return DB_ERROR;
10350 }
10351
10352 strncpy(cache_key,
10353 (wsrep_protocol_version > 1) ?
10354 ((referenced) ?
10355 foreign->referenced_table->name.m_name :
10356 foreign->foreign_table->name.m_name) :
10357 foreign->foreign_table->name.m_name, sizeof(cache_key) - 1);
10358 cache_key_len = strlen(cache_key);
10359
10360#ifdef WSREP_DEBUG_PRINT
10361 ulint j;
10362 fprintf(stderr, "FK parent key, table: %s %s len: %lu ",
10363 cache_key, (shared) ? "shared" : "exclusive", len+1);
10364 for (j=0; j<len+1; j++) {
10365 fprintf(stderr, " %hhX, ", key[j]);
10366 }
10367 fprintf(stderr, "\n");
10368#endif
10369 char *p = strchr(cache_key, '/');
10370
10371 if (p) {
10372 *p = '\0';
10373 } else {
10374 WSREP_WARN("unexpected foreign key table %s %s",
10375 foreign->referenced_table->name.m_name,
10376 foreign->foreign_table->name.m_name);
10377 }
10378
10379 wsrep_buf_t wkey_part[3];
10380 wsrep_key_t wkey = {wkey_part, 3};
10381
10382 if (!wsrep_prepare_key(
10383 (const uchar*)cache_key,
10384 cache_key_len + 1,
10385 (const uchar*)key, len+1,
10386 wkey_part,
10387 (size_t*)&wkey.key_parts_num)) {
10388 WSREP_WARN("key prepare failed for cascaded FK: %s",
10389 (wsrep_thd_query(thd)) ?
10390 wsrep_thd_query(thd) : "void");
10391 return DB_ERROR;
10392 }
10393
10394 wsrep_t *wsrep= get_wsrep();
10395
10396 rcode = (int)wsrep->append_key(
10397 wsrep,
10398 wsrep_ws_handle(thd, trx),
10399 &wkey,
10400 1,
10401 shared ? WSREP_KEY_SHARED : WSREP_KEY_EXCLUSIVE,
10402 copy);
10403
10404 if (rcode) {
10405 DBUG_PRINT("wsrep", ("row key failed: " ULINTPF, rcode));
10406 WSREP_ERROR("Appending cascaded fk row key failed: %s, "
10407 ULINTPF,
10408 (wsrep_thd_query(thd)) ?
10409 wsrep_thd_query(thd) : "void", rcode);
10410 return DB_ERROR;
10411 }
10412
10413 return DB_SUCCESS;
10414}
10415
10416static int
10417wsrep_append_key(
10418/*=============*/
10419 THD *thd,
10420 trx_t *trx,
10421 TABLE_SHARE *table_share,
10422 const char* key,
10423 uint16_t key_len,
10424 bool shared
10425)
10426{
10427 DBUG_ENTER("wsrep_append_key");
10428 bool const copy = true;
10429#ifdef WSREP_DEBUG_PRINT
10430 fprintf(stderr, "%s conn %ld, trx %llu, keylen %d, table %s\n Query: %s ",
10431 (shared) ? "Shared" : "Exclusive",
10432 thd_get_thread_id(thd), (long long)trx->id, key_len,
10433 table_share->table_name.str, wsrep_thd_query(thd));
10434 for (int i=0; i<key_len; i++) {
10435 fprintf(stderr, "%hhX, ", key[i]);
10436 }
10437 fprintf(stderr, "\n");
10438#endif
10439 wsrep_buf_t wkey_part[3];
10440 wsrep_key_t wkey = {wkey_part, 3};
10441
10442 if (!wsrep_prepare_key(
10443 (const uchar*)table_share->table_cache_key.str,
10444 table_share->table_cache_key.length,
10445 (const uchar*)key, key_len,
10446 wkey_part,
10447 (size_t*)&wkey.key_parts_num)) {
10448 WSREP_WARN("key prepare failed for: %s",
10449 (wsrep_thd_query(thd)) ?
10450 wsrep_thd_query(thd) : "void");
10451 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
10452 }
10453
10454 wsrep_t *wsrep= get_wsrep();
10455
10456 int rcode = (int)wsrep->append_key(
10457 wsrep,
10458 wsrep_ws_handle(thd, trx),
10459 &wkey,
10460 1,
10461 shared ? WSREP_KEY_SHARED : WSREP_KEY_EXCLUSIVE,
10462 copy);
10463 if (rcode) {
10464 DBUG_PRINT("wsrep", ("row key failed: %d", rcode));
10465 WSREP_WARN("Appending row key failed: %s, %d",
10466 (wsrep_thd_query(thd)) ?
10467 wsrep_thd_query(thd) : "void", rcode);
10468 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
10469 }
10470
10471 DBUG_RETURN(0);
10472}
10473
10474static bool
10475referenced_by_foreign_key2(
10476/*=======================*/
10477 dict_table_t* table,
10478 dict_index_t* index)
10479{
10480 ut_ad(table != NULL);
10481 ut_ad(index != NULL);
10482
10483 const dict_foreign_set* fks = &table->referenced_set;
10484
10485 for (dict_foreign_set::const_iterator it = fks->begin();
10486 it != fks->end();
10487 ++it) {
10488 dict_foreign_t* foreign = *it;
10489
10490 if (foreign->referenced_index != index) {
10491 continue;
10492 }
10493 ut_ad(table == foreign->referenced_table);
10494 return true;
10495 }
10496 return false;
10497}
10498
10499int
10500ha_innobase::wsrep_append_keys(
10501/*===========================*/
10502 THD *thd,
10503 bool shared,
10504 const uchar* record0, /* in: row in MySQL format */
10505 const uchar* record1) /* in: row in MySQL format */
10506{
10507 int rcode;
10508 DBUG_ENTER("wsrep_append_keys");
10509
10510 bool key_appended = false;
10511 trx_t *trx = thd_to_trx(thd);
10512
10513 if (table_share && table_share->tmp_table != NO_TMP_TABLE) {
10514 WSREP_DEBUG("skipping tmp table DML: THD: %lu tmp: %d SQL: %s",
10515 thd_get_thread_id(thd),
10516 table_share->tmp_table,
10517 (wsrep_thd_query(thd)) ?
10518 wsrep_thd_query(thd) : "void");
10519 DBUG_RETURN(0);
10520 }
10521
10522 if (wsrep_protocol_version == 0) {
10523 uint len;
10524 char keyval[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
10525 char *key = &keyval[0];
10526 ibool is_null;
10527
10528 len = wsrep_store_key_val_for_row(
10529 thd, table, 0, key, WSREP_MAX_SUPPORTED_KEY_LENGTH,
10530 record0, &is_null);
10531
10532 if (!is_null) {
10533 rcode = wsrep_append_key(
10534 thd, trx, table_share, keyval,
10535 len, shared);
10536
10537 if (rcode) {
10538 DBUG_RETURN(rcode);
10539 }
10540 } else {
10541 WSREP_DEBUG("NULL key skipped (proto 0): %s",
10542 wsrep_thd_query(thd));
10543 }
10544 } else {
10545 ut_a(table->s->keys <= 256);
10546 uint i;
10547 bool hasPK= false;
10548
10549 for (i=0; i<table->s->keys; ++i) {
10550 KEY* key_info = table->key_info + i;
10551 if (key_info->flags & HA_NOSAME) {
10552 hasPK = true;
10553 }
10554 }
10555
10556 for (i=0; i<table->s->keys; ++i) {
10557 uint len;
10558 char keyval0[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
10559 char keyval1[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
10560 char* key0 = &keyval0[1];
10561 char* key1 = &keyval1[1];
10562 KEY* key_info = table->key_info + i;
10563 ibool is_null;
10564
10565 dict_index_t* idx = innobase_get_index(i);
10566 dict_table_t* tab = (idx) ? idx->table : NULL;
10567
10568 keyval0[0] = (char)i;
10569 keyval1[0] = (char)i;
10570
10571 if (!tab) {
10572 WSREP_WARN("MariaDB-InnoDB key mismatch %s %s",
10573 table->s->table_name.str,
10574 key_info->name.str);
10575 }
10576 /* !hasPK == table with no PK, must append all non-unique keys */
10577 if (!hasPK || key_info->flags & HA_NOSAME ||
10578 ((tab &&
10579 referenced_by_foreign_key2(tab, idx)) ||
10580 (!tab && referenced_by_foreign_key()))) {
10581
10582 len = wsrep_store_key_val_for_row(
10583 thd, table, i, key0,
10584 WSREP_MAX_SUPPORTED_KEY_LENGTH,
10585 record0, &is_null);
10586 if (!is_null) {
10587 rcode = wsrep_append_key(
10588 thd, trx, table_share,
10589 keyval0, len+1, shared);
10590
10591 if (rcode) {
10592 DBUG_RETURN(rcode);
10593 }
10594
10595 if (key_info->flags & HA_NOSAME || shared)
10596 key_appended = true;
10597 } else {
10598 WSREP_DEBUG("NULL key skipped: %s",
10599 wsrep_thd_query(thd));
10600 }
10601
10602 if (record1) {
10603 len = wsrep_store_key_val_for_row(
10604 thd, table, i, key1,
10605 WSREP_MAX_SUPPORTED_KEY_LENGTH,
10606 record1, &is_null);
10607
10608 if (!is_null && memcmp(key0, key1, len)) {
10609 rcode = wsrep_append_key(
10610 thd, trx, table_share,
10611 keyval1, len+1, shared);
10612 if (rcode) DBUG_RETURN(rcode);
10613 }
10614 }
10615 }
10616 }
10617 }
10618
10619 /* if no PK, calculate hash of full row, to be the key value */
10620 if (!key_appended && wsrep_certify_nonPK) {
10621 uchar digest[16];
10622 int rcode;
10623
10624 wsrep_calc_row_hash(digest, record0, table, m_prebuilt);
10625
10626 if ((rcode = wsrep_append_key(thd, trx, table_share,
10627 (const char*) digest, 16,
10628 shared))) {
10629 DBUG_RETURN(rcode);
10630 }
10631
10632 if (record1) {
10633 wsrep_calc_row_hash(
10634 digest, record1, table, m_prebuilt);
10635 if ((rcode = wsrep_append_key(thd, trx, table_share,
10636 (const char*) digest,
10637 16, shared))) {
10638 DBUG_RETURN(rcode);
10639 }
10640 }
10641 DBUG_RETURN(0);
10642 }
10643
10644 DBUG_RETURN(0);
10645}
10646#endif /* WITH_WSREP */
10647
10648/*********************************************************************//**
10649Stores a reference to the current row to 'ref' field of the handle. Note
10650that in the case where we have generated the clustered index for the
10651table, the function parameter is illogical: we MUST ASSUME that 'record'
10652is the current 'position' of the handle, because if row ref is actually
10653the row id internally generated in InnoDB, then 'record' does not contain
10654it. We just guess that the row id must be for the record where the handle
10655was positioned the last time. */
10656
10657void
10658ha_innobase::position(
10659/*==================*/
10660 const uchar* record) /*!< in: row in MySQL format */
10661{
10662 uint len;
10663
10664 ut_a(m_prebuilt->trx == thd_to_trx(ha_thd()));
10665
10666 if (m_prebuilt->clust_index_was_generated) {
10667 /* No primary key was defined for the table and we
10668 generated the clustered index from row id: the
10669 row reference will be the row id, not any key value
10670 that MySQL knows of */
10671
10672 len = DATA_ROW_ID_LEN;
10673
10674 memcpy(ref, m_prebuilt->row_id, len);
10675 } else {
10676
10677 /* Copy primary key as the row reference */
10678 KEY* key_info = table->key_info + m_primary_key;
10679 key_copy(ref, (uchar*)record, key_info, key_info->key_length);
10680 len = key_info->key_length;
10681 }
10682
10683 ut_ad(len == ref_length);
10684}
10685
10686/*****************************************************************//**
10687Check whether there exist a column named as "FTS_DOC_ID", which is
10688reserved for InnoDB FTS Doc ID
10689@return true if there exist a "FTS_DOC_ID" column */
10690static
10691bool
10692create_table_check_doc_id_col(
10693/*==========================*/
10694 trx_t* trx, /*!< in: InnoDB transaction handle */
10695 const TABLE* form, /*!< in: information on table
10696 columns and indexes */
10697 ulint* doc_id_col) /*!< out: Doc ID column number if
10698 there exist a FTS_DOC_ID column,
10699 ULINT_UNDEFINED if column is of the
10700 wrong type/name/size */
10701{
10702 uint n_fields = mysql_fields(form);
10703
10704 for (ulint i = 0; i < n_fields; i++) {
10705 const Field* field;
10706 ulint col_type;
10707 ulint col_len;
10708 ulint unsigned_type;
10709
10710 field = form->field[i];
10711 if (!field->stored_in_db()) {
10712 continue;
10713 }
10714
10715 col_type = get_innobase_type_from_mysql_type(
10716 &unsigned_type, field);
10717
10718 col_len = field->pack_length();
10719
10720 if (innobase_strcasecmp(field->field_name.str,
10721 FTS_DOC_ID_COL_NAME) == 0) {
10722
10723 /* Note the name is case sensitive due to
10724 our internal query parser */
10725 if (col_type == DATA_INT
10726 && !field->real_maybe_null()
10727 && col_len == sizeof(doc_id_t)
10728 && (strcmp(field->field_name.str,
10729 FTS_DOC_ID_COL_NAME) == 0)) {
10730 *doc_id_col = i;
10731 } else {
10732 push_warning_printf(
10733 trx->mysql_thd,
10734 Sql_condition::WARN_LEVEL_WARN,
10735 ER_ILLEGAL_HA_CREATE_OPTION,
10736 "InnoDB: FTS_DOC_ID column must be"
10737 " of BIGINT NOT NULL type, and named"
10738 " in all capitalized characters");
10739 my_error(ER_WRONG_COLUMN_NAME, MYF(0),
10740 field->field_name.str);
10741 *doc_id_col = ULINT_UNDEFINED;
10742 }
10743
10744 return(true);
10745 }
10746 }
10747
10748 return(false);
10749}
10750
10751
10752/** Finds all base columns needed to compute a given generated column.
10753This is returned as a bitmap, in field->table->tmp_set.
10754Works for both dict_v_col_t and dict_s_col_t columns.
10755@param[in] table InnoDB table
10756@param[in] field MySQL field
10757@param[in,out] col virtual or stored column */
10758template <typename T>
10759void
10760prepare_vcol_for_base_setup(
10761/*========================*/
10762 const dict_table_t* table,
10763 const Field* field,
10764 T* col)
10765{
10766 ut_ad(col->num_base == 0);
10767 ut_ad(col->base_col == NULL);
10768
10769 MY_BITMAP *old_read_set = field->table->read_set;
10770 MY_BITMAP *old_vcol_set = field->table->vcol_set;
10771
10772 field->table->read_set = field->table->vcol_set = &field->table->tmp_set;
10773
10774 bitmap_clear_all(&field->table->tmp_set);
10775 field->vcol_info->expr->walk(
10776 &Item::register_field_in_read_map, 1, field->table);
10777 col->num_base= bitmap_bits_set(&field->table->tmp_set);
10778 if (col->num_base != 0) {
10779 col->base_col = static_cast<dict_col_t**>(mem_heap_zalloc(
10780 table->heap, col->num_base * sizeof(
10781 * col->base_col)));
10782 }
10783 field->table->read_set= old_read_set;
10784 field->table->vcol_set= old_vcol_set;
10785}
10786
10787
10788/** Set up base columns for virtual column
10789@param[in] table InnoDB table
10790@param[in] field MySQL field
10791@param[in,out] v_col virtual column */
10792void
10793innodb_base_col_setup(
10794 dict_table_t* table,
10795 const Field* field,
10796 dict_v_col_t* v_col)
10797{
10798 ulint n = 0;
10799
10800 prepare_vcol_for_base_setup(table, field, v_col);
10801
10802 for (uint i= 0; i < field->table->s->fields; ++i) {
10803 const Field* base_field = field->table->field[i];
10804 if (base_field->stored_in_db()
10805 && bitmap_is_set(&field->table->tmp_set, i)) {
10806 ulint z;
10807
10808 for (z = 0; z < table->n_cols; z++) {
10809 const char* name = dict_table_get_col_name(table, z);
10810 if (!innobase_strcasecmp(name,
10811 base_field->field_name.str)) {
10812 break;
10813 }
10814 }
10815
10816 ut_ad(z != table->n_cols);
10817
10818 v_col->base_col[n] = dict_table_get_nth_col(table, z);
10819 ut_ad(v_col->base_col[n]->ind == z);
10820 n++;
10821 }
10822 }
10823 v_col->num_base= n;
10824}
10825
10826/** Set up base columns for stored column
10827@param[in] table InnoDB table
10828@param[in] field MySQL field
10829@param[in,out] s_col stored column */
10830void
10831innodb_base_col_setup_for_stored(
10832 const dict_table_t* table,
10833 const Field* field,
10834 dict_s_col_t* s_col)
10835{
10836 ulint n = 0;
10837
10838 prepare_vcol_for_base_setup(table, field, s_col);
10839
10840 for (uint i= 0; i < field->table->s->fields; ++i) {
10841 const Field* base_field = field->table->field[i];
10842
10843 if (!innobase_is_s_fld(base_field)
10844 && !innobase_is_v_fld(base_field)
10845 && bitmap_is_set(&field->table->tmp_set,
10846 i)) {
10847 ulint z;
10848 for (z = 0; z < table->n_cols; z++) {
10849 const char* name = dict_table_get_col_name(
10850 table, z);
10851 if (!innobase_strcasecmp(
10852 name, base_field->field_name.str)) {
10853 break;
10854 }
10855 }
10856
10857 ut_ad(z != table->n_cols);
10858
10859 s_col->base_col[n] = dict_table_get_nth_col(table, z);
10860 n++;
10861
10862 if (n == s_col->num_base) {
10863 break;
10864 }
10865 }
10866 }
10867 s_col->num_base= n;
10868}
10869
10870/** Create a table definition to an InnoDB database.
10871@return ER_* level error */
10872inline MY_ATTRIBUTE((warn_unused_result))
10873int
10874create_table_info_t::create_table_def()
10875{
10876 dict_table_t* table;
10877 ulint n_cols;
10878 ulint col_type;
10879 ulint col_len;
10880 ulint nulls_allowed;
10881 ulint unsigned_type;
10882 ulint binary_type;
10883 ulint long_true_varchar;
10884 ulint charset_no;
10885 ulint i;
10886 ulint j = 0;
10887 ulint doc_id_col = 0;
10888 ibool has_doc_id_col = FALSE;
10889 mem_heap_t* heap;
10890 ulint num_v = 0;
10891 ulint actual_n_cols;
10892 ha_table_option_struct *options= m_form->s->option_struct;
10893 dberr_t err = DB_SUCCESS;
10894
10895 DBUG_ENTER("create_table_def");
10896 DBUG_PRINT("enter", ("table_name: %s", m_table_name));
10897
10898 DBUG_ASSERT(m_trx->mysql_thd == m_thd);
10899
10900 /* MySQL does the name length check. But we do additional check
10901 on the name length here */
10902 const size_t table_name_len = strlen(m_table_name);
10903 if (table_name_len > MAX_FULL_NAME_LEN) {
10904 push_warning_printf(
10905 m_thd, Sql_condition::WARN_LEVEL_WARN,
10906 ER_TABLE_NAME,
10907 "InnoDB: Table Name or Database Name is too long");
10908
10909 DBUG_RETURN(ER_TABLE_NAME);
10910 }
10911
10912 if (m_table_name[table_name_len - 1] == '/') {
10913 push_warning_printf(
10914 m_thd, Sql_condition::WARN_LEVEL_WARN,
10915 ER_TABLE_NAME,
10916 "InnoDB: Table name is empty");
10917
10918 DBUG_RETURN(ER_WRONG_TABLE_NAME);
10919 }
10920
10921 n_cols = m_form->s->fields;
10922
10923 /* Find out any virtual column */
10924 for (i = 0; i < n_cols; i++) {
10925 Field* field = m_form->field[i];
10926
10927 if (innobase_is_v_fld(field)) {
10928 num_v++;
10929 }
10930 }
10931
10932 ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED));
10933
10934 /* Check whether there already exists a FTS_DOC_ID column */
10935 if (create_table_check_doc_id_col(m_trx, m_form, &doc_id_col)){
10936
10937 /* Raise error if the Doc ID column is of wrong type or name */
10938 if (doc_id_col == ULINT_UNDEFINED) {
10939 DBUG_RETURN(HA_ERR_GENERIC);
10940 } else {
10941 has_doc_id_col = TRUE;
10942 }
10943 }
10944
10945 /* Adjust the number of columns for the FTS hidden field */
10946 actual_n_cols = n_cols;
10947 if (m_flags2 & DICT_TF2_FTS && !has_doc_id_col) {
10948 actual_n_cols += 1;
10949 }
10950
10951 table = dict_mem_table_create(m_table_name, NULL,
10952 actual_n_cols, num_v, m_flags, m_flags2);
10953
10954 /* Set the hidden doc_id column. */
10955 if (m_flags2 & DICT_TF2_FTS) {
10956 table->fts->doc_col = has_doc_id_col
10957 ? doc_id_col : n_cols - num_v;
10958 }
10959
10960 if (DICT_TF_HAS_DATA_DIR(m_flags)) {
10961 ut_a(strlen(m_remote_path));
10962
10963 table->data_dir_path = mem_heap_strdup(
10964 table->heap, m_remote_path);
10965
10966 } else {
10967 table->data_dir_path = NULL;
10968 }
10969
10970 heap = mem_heap_create(1000);
10971
10972 for (i = 0; i < n_cols; i++) {
10973 ulint is_virtual;
10974 bool is_stored = false;
10975
10976 Field* field = m_form->field[i];
10977 ulint vers_row = 0;
10978
10979 if (m_form->versioned()) {
10980 if (i == m_form->s->row_start_field) {
10981 vers_row = DATA_VERS_START;
10982 } else if (i == m_form->s->row_end_field) {
10983 vers_row = DATA_VERS_END;
10984 } else if (!(field->flags
10985 & VERS_UPDATE_UNVERSIONED_FLAG)) {
10986 vers_row = DATA_VERSIONED;
10987 }
10988 }
10989
10990 col_type = get_innobase_type_from_mysql_type(
10991 &unsigned_type, field);
10992
10993 if (!col_type) {
10994 push_warning_printf(
10995 m_thd, Sql_condition::WARN_LEVEL_WARN,
10996 ER_CANT_CREATE_TABLE,
10997 "Error creating table '%s' with"
10998 " column '%s'. Please check its"
10999 " column type and try to re-create"
11000 " the table with an appropriate"
11001 " column type.",
11002 table->name.m_name, field->field_name.str);
11003 goto err_col;
11004 }
11005
11006 nulls_allowed = field->real_maybe_null() ? 0 : DATA_NOT_NULL;
11007 binary_type = field->binary() ? DATA_BINARY_TYPE : 0;
11008
11009 charset_no = 0;
11010
11011 if (dtype_is_string_type(col_type)) {
11012
11013 charset_no = (ulint) field->charset()->number;
11014
11015 DBUG_EXECUTE_IF("simulate_max_char_col",
11016 charset_no = MAX_CHAR_COLL_NUM + 1;
11017 );
11018
11019 if (charset_no > MAX_CHAR_COLL_NUM) {
11020 /* in data0type.h we assume that the
11021 number fits in one byte in prtype */
11022 push_warning_printf(
11023 m_thd, Sql_condition::WARN_LEVEL_WARN,
11024 ER_CANT_CREATE_TABLE,
11025 "In InnoDB, charset-collation codes"
11026 " must be below 256."
11027 " Unsupported code " ULINTPF ".",
11028 charset_no);
11029 mem_heap_free(heap);
11030 dict_mem_table_free(table);
11031
11032 ut_ad(trx_state_eq(
11033 m_trx, TRX_STATE_NOT_STARTED));
11034
11035 DBUG_RETURN(ER_CANT_CREATE_TABLE);
11036 }
11037 }
11038
11039 col_len = field->pack_length();
11040
11041 /* The MySQL pack length contains 1 or 2 bytes length field
11042 for a true VARCHAR. Let us subtract that, so that the InnoDB
11043 column length in the InnoDB data dictionary is the real
11044 maximum byte length of the actual data. */
11045
11046 long_true_varchar = 0;
11047
11048 if (field->type() == MYSQL_TYPE_VARCHAR) {
11049 col_len -= ((Field_varstring*) field)->length_bytes;
11050
11051 if (((Field_varstring*) field)->length_bytes == 2) {
11052 long_true_varchar = DATA_LONG_TRUE_VARCHAR;
11053 }
11054 }
11055
11056 is_virtual = (innobase_is_v_fld(field)) ? DATA_VIRTUAL : 0;
11057 is_stored = innobase_is_s_fld(field);
11058
11059 /* First check whether the column to be added has a
11060 system reserved name. */
11061 if (dict_col_name_is_reserved(field->field_name.str)){
11062 my_error(ER_WRONG_COLUMN_NAME, MYF(0),
11063 field->field_name.str);
11064err_col:
11065 dict_mem_table_free(table);
11066 mem_heap_free(heap);
11067 ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED));
11068 DBUG_RETURN(HA_ERR_GENERIC);
11069 }
11070
11071 if (!is_virtual) {
11072 dict_mem_table_add_col(table, heap,
11073 field->field_name.str, col_type,
11074 dtype_form_prtype(
11075 (ulint) field->type()
11076 | nulls_allowed | unsigned_type
11077 | binary_type | long_true_varchar
11078 | vers_row,
11079 charset_no),
11080 col_len);
11081 } else {
11082 dict_mem_table_add_v_col(table, heap,
11083 field->field_name.str, col_type,
11084 dtype_form_prtype(
11085 (ulint) field->type()
11086 | nulls_allowed | unsigned_type
11087 | binary_type | long_true_varchar
11088 | vers_row
11089 | is_virtual,
11090 charset_no),
11091 col_len, i, 0);
11092 }
11093
11094 if (is_stored) {
11095 ut_ad(!is_virtual);
11096 /* Added stored column in m_s_cols list. */
11097 dict_mem_table_add_s_col(
11098 table, 0);
11099 }
11100 }
11101
11102 if (num_v) {
11103 for (i = 0; i < n_cols; i++) {
11104 dict_v_col_t* v_col;
11105
11106 Field* field = m_form->field[i];
11107
11108 if (!innobase_is_v_fld(field)) {
11109 continue;
11110 }
11111
11112 v_col = dict_table_get_nth_v_col(table, j);
11113
11114 j++;
11115
11116 innodb_base_col_setup(table, field, v_col);
11117 }
11118 }
11119
11120 /** Fill base columns for the stored column present in the list. */
11121 if (table->s_cols && table->s_cols->size()) {
11122
11123 for (i = 0; i < n_cols; i++) {
11124 Field* field = m_form->field[i];
11125
11126 if (!innobase_is_s_fld(field)) {
11127 continue;
11128 }
11129
11130 dict_s_col_list::iterator it;
11131 for (it = table->s_cols->begin();
11132 it != table->s_cols->end(); ++it) {
11133 dict_s_col_t s_col = *it;
11134
11135 if (s_col.s_pos == i) {
11136 innodb_base_col_setup_for_stored(
11137 table, field, &s_col);
11138 break;
11139 }
11140 }
11141 }
11142 }
11143
11144 /* Add the FTS doc_id hidden column. */
11145 if (m_flags2 & DICT_TF2_FTS && !has_doc_id_col) {
11146 fts_add_doc_id_column(table, heap);
11147 }
11148
11149 dict_table_add_system_columns(table, heap);
11150
11151 ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED));
11152
11153 if (table->is_temporary()) {
11154 /* Get a new table ID. FIXME: Make this a private
11155 sequence, not shared with persistent tables! */
11156 dict_table_assign_new_id(table, m_trx);
11157 ut_ad(dict_tf_get_rec_format(table->flags)
11158 != REC_FORMAT_COMPRESSED);
11159 table->space_id = SRV_TMP_SPACE_ID;
11160 table->space = fil_system.temp_space;
11161 table->add_to_cache();
11162 } else {
11163 if (err == DB_SUCCESS) {
11164 err = row_create_table_for_mysql(
11165 table, m_trx,
11166 (fil_encryption_t)options->encryption,
11167 (uint32_t)options->encryption_key_id);
11168 }
11169
11170 DBUG_EXECUTE_IF("ib_crash_during_create_for_encryption",
11171 DBUG_SUICIDE(););
11172 }
11173
11174 mem_heap_free(heap);
11175
11176 DBUG_EXECUTE_IF("ib_create_err_tablespace_exist",
11177 err = DB_TABLESPACE_EXISTS;);
11178
11179 switch (err) {
11180 case DB_SUCCESS:
11181 ut_ad(table);
11182 m_table = table;
11183 if (m_flags2 & DICT_TF2_FTS) {
11184 fts_optimize_add_table(table);
11185 }
11186 DBUG_RETURN(0);
11187 default:
11188 break;
11189 case DB_DUPLICATE_KEY:
11190 case DB_TABLESPACE_EXISTS:
11191 char display_name[FN_REFLEN];
11192 char* buf_end = innobase_convert_identifier(
11193 display_name, sizeof(display_name) - 1,
11194 m_table_name, strlen(m_table_name),
11195 m_thd);
11196
11197 *buf_end = '\0';
11198
11199 my_error(err == DB_DUPLICATE_KEY
11200 ? ER_TABLE_EXISTS_ERROR
11201 : ER_TABLESPACE_EXISTS, MYF(0), display_name);
11202 }
11203
11204 DBUG_RETURN(convert_error_code_to_mysql(err, m_flags, m_thd));}
11205
11206/*****************************************************************//**
11207Creates an index in an InnoDB database. */
11208inline
11209int
11210create_index(
11211/*=========*/
11212 trx_t* trx, /*!< in: InnoDB transaction handle */
11213 const TABLE* form, /*!< in: information on table
11214 columns and indexes */
11215 dict_table_t* table, /*!< in,out: table */
11216 uint key_num) /*!< in: index number */
11217{
11218 dict_index_t* index;
11219 int error;
11220 const KEY* key;
11221 ulint* field_lengths;
11222
11223 DBUG_ENTER("create_index");
11224
11225 key = form->key_info + key_num;
11226
11227 /* Assert that "GEN_CLUST_INDEX" cannot be used as non-primary index */
11228 ut_a(innobase_strcasecmp(key->name.str, innobase_index_reserve_name) != 0);
11229
11230 if (key->flags & (HA_SPATIAL | HA_FULLTEXT)) {
11231 /* Only one of these can be specified at a time. */
11232 ut_ad(~key->flags & (HA_SPATIAL | HA_FULLTEXT));
11233 ut_ad(!(key->flags & HA_NOSAME));
11234 index = dict_mem_index_create(table, key->name.str,
11235 (key->flags & HA_SPATIAL)
11236 ? DICT_SPATIAL : DICT_FTS,
11237 key->user_defined_key_parts);
11238
11239 for (ulint i = 0; i < key->user_defined_key_parts; i++) {
11240 KEY_PART_INFO* key_part = key->key_part + i;
11241
11242 /* We do not support special (Fulltext or Spatial)
11243 index on virtual columns */
11244 if (innobase_is_v_fld(key_part->field)) {
11245 ut_ad(0);
11246 DBUG_RETURN(HA_ERR_UNSUPPORTED);
11247 }
11248
11249 dict_mem_index_add_field(
11250 index, key_part->field->field_name.str, 0);
11251 }
11252
11253 DBUG_RETURN(convert_error_code_to_mysql(
11254 row_create_index_for_mysql(
11255 index, trx, NULL),
11256 table->flags, NULL));
11257 }
11258
11259 ulint ind_type = 0;
11260
11261 if (key_num == form->s->primary_key) {
11262 ind_type |= DICT_CLUSTERED;
11263 }
11264
11265 if (key->flags & HA_NOSAME) {
11266 ind_type |= DICT_UNIQUE;
11267 }
11268
11269 field_lengths = (ulint*) my_malloc(//PSI_INSTRUMENT_ME,
11270 key->user_defined_key_parts * sizeof *
11271 field_lengths, MYF(MY_FAE));
11272
11273 /* We pass 0 as the space id, and determine at a lower level the space
11274 id where to store the table */
11275
11276 index = dict_mem_index_create(table, key->name.str,
11277 ind_type, key->user_defined_key_parts);
11278
11279 for (ulint i = 0; i < key->user_defined_key_parts; i++) {
11280 KEY_PART_INFO* key_part = key->key_part + i;
11281 ulint prefix_len;
11282 ulint col_type;
11283 ulint is_unsigned;
11284
11285
11286 /* (The flag HA_PART_KEY_SEG denotes in MySQL a
11287 column prefix field in an index: we only store a
11288 specified number of first bytes of the column to
11289 the index field.) The flag does not seem to be
11290 properly set by MySQL. Let us fall back on testing
11291 the length of the key part versus the column.
11292 We first reach to the table's column; if the index is on a
11293 prefix, key_part->field is not the table's column (it's a
11294 "fake" field forged in open_table_from_share() with length
11295 equal to the length of the prefix); so we have to go to
11296 form->fied. */
11297 Field* field= form->field[key_part->field->field_index];
11298 if (field == NULL)
11299 ut_error;
11300
11301 const char* field_name = key_part->field->field_name.str;
11302
11303 col_type = get_innobase_type_from_mysql_type(
11304 &is_unsigned, key_part->field);
11305
11306 if (DATA_LARGE_MTYPE(col_type)
11307 || (key_part->length < field->pack_length()
11308 && field->type() != MYSQL_TYPE_VARCHAR)
11309 || (field->type() == MYSQL_TYPE_VARCHAR
11310 && key_part->length < field->pack_length()
11311 - ((Field_varstring*) field)->length_bytes)) {
11312
11313 switch (col_type) {
11314 default:
11315 prefix_len = key_part->length;
11316 break;
11317 case DATA_INT:
11318 case DATA_FLOAT:
11319 case DATA_DOUBLE:
11320 case DATA_DECIMAL:
11321 sql_print_error(
11322 "MariaDB is trying to create a column"
11323 " prefix index field, on an"
11324 " inappropriate data type. Table"
11325 " name %s, column name %s.",
11326 form->s->table_name.str,
11327 key_part->field->field_name.str);
11328
11329 prefix_len = 0;
11330 }
11331 } else {
11332 prefix_len = 0;
11333 }
11334
11335 field_lengths[i] = key_part->length;
11336
11337 if (innobase_is_v_fld(key_part->field)) {
11338 index->type |= DICT_VIRTUAL;
11339 }
11340
11341 dict_mem_index_add_field(index, field_name, prefix_len);
11342 }
11343
11344 ut_ad(key->flags & HA_FULLTEXT || !(index->type & DICT_FTS));
11345
11346 /* Even though we've defined max_supported_key_part_length, we
11347 still do our own checking using field_lengths to be absolutely
11348 sure we don't create too long indexes. */
11349
11350 error = convert_error_code_to_mysql(
11351 row_create_index_for_mysql(index, trx, field_lengths),
11352 table->flags, NULL);
11353
11354 my_free(field_lengths);
11355
11356 DBUG_RETURN(error);
11357}
11358
11359/** Return a display name for the row format
11360@param[in] row_format Row Format
11361@return row format name */
11362static
11363const char*
11364get_row_format_name(
11365 enum row_type row_format)
11366{
11367 switch (row_format) {
11368 case ROW_TYPE_COMPACT:
11369 return("COMPACT");
11370 case ROW_TYPE_COMPRESSED:
11371 return("COMPRESSED");
11372 case ROW_TYPE_DYNAMIC:
11373 return("DYNAMIC");
11374 case ROW_TYPE_REDUNDANT:
11375 return("REDUNDANT");
11376 case ROW_TYPE_DEFAULT:
11377 return("DEFAULT");
11378 case ROW_TYPE_FIXED:
11379 return("FIXED");
11380 case ROW_TYPE_PAGE:
11381 case ROW_TYPE_NOT_USED:
11382 break;
11383 }
11384 return("NOT USED");
11385}
11386
11387/** Validate DATA DIRECTORY option.
11388@return true if valid, false if not. */
11389bool
11390create_table_info_t::create_option_data_directory_is_valid()
11391{
11392 bool is_valid = true;
11393
11394 ut_ad(m_create_info->data_file_name
11395 && m_create_info->data_file_name[0] != '\0');
11396
11397 /* Use DATA DIRECTORY only with file-per-table. */
11398 if (!m_allow_file_per_table) {
11399 push_warning(
11400 m_thd, Sql_condition::WARN_LEVEL_WARN,
11401 ER_ILLEGAL_HA_CREATE_OPTION,
11402 "InnoDB: DATA DIRECTORY requires"
11403 " innodb_file_per_table.");
11404 is_valid = false;
11405 }
11406
11407 /* Do not use DATA DIRECTORY with TEMPORARY TABLE. */
11408 if (m_create_info->options & HA_LEX_CREATE_TMP_TABLE) {
11409 push_warning(
11410 m_thd, Sql_condition::WARN_LEVEL_WARN,
11411 ER_ILLEGAL_HA_CREATE_OPTION,
11412 "InnoDB: DATA DIRECTORY cannot be used"
11413 " for TEMPORARY tables.");
11414 is_valid = false;
11415 }
11416
11417 /* We check for a DATA DIRECTORY mixed with TABLESPACE in
11418 create_option_tablespace_is_valid(), no need to here. */
11419
11420 return(is_valid);
11421}
11422
11423/** Validate the create options. Check that the options KEY_BLOCK_SIZE,
11424ROW_FORMAT, DATA DIRECTORY, TEMPORARY are compatible with
11425each other and other settings. These CREATE OPTIONS are not validated
11426here unless innodb_strict_mode is on. With strict mode, this function
11427will report each problem it finds using a custom message with error
11428code ER_ILLEGAL_HA_CREATE_OPTION, not its built-in message.
11429@return NULL if valid, string name of bad option if not. */
11430const char*
11431create_table_info_t::create_options_are_invalid()
11432{
11433 bool has_key_block_size = (m_create_info->key_block_size != 0);
11434
11435 const char* ret = NULL;
11436 enum row_type row_format = m_create_info->row_type;
11437 const bool is_temp
11438 = m_create_info->options & HA_LEX_CREATE_TMP_TABLE;
11439
11440 ut_ad(m_thd != NULL);
11441
11442 /* If innodb_strict_mode is not set don't do any more validation. */
11443 if (!THDVAR(m_thd, strict_mode)) {
11444 return(NULL);
11445 }
11446
11447 /* Check if a non-zero KEY_BLOCK_SIZE was specified. */
11448 if (has_key_block_size) {
11449 if (is_temp) {
11450 my_error(ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE,
11451 MYF(0));
11452 return("KEY_BLOCK_SIZE");
11453 }
11454
11455 switch (m_create_info->key_block_size) {
11456 ulint kbs_max;
11457 case 1:
11458 case 2:
11459 case 4:
11460 case 8:
11461 case 16:
11462 /* The maximum KEY_BLOCK_SIZE (KBS) is
11463 UNIV_PAGE_SIZE_MAX. But if srv_page_size is
11464 smaller than UNIV_PAGE_SIZE_MAX, the maximum
11465 KBS is also smaller. */
11466 kbs_max = ut_min(
11467 1U << (UNIV_PAGE_SSIZE_MAX - 1),
11468 1U << (PAGE_ZIP_SSIZE_MAX - 1));
11469 if (m_create_info->key_block_size > kbs_max) {
11470 push_warning_printf(
11471 m_thd, Sql_condition::WARN_LEVEL_WARN,
11472 ER_ILLEGAL_HA_CREATE_OPTION,
11473 "InnoDB: KEY_BLOCK_SIZE=%ld"
11474 " cannot be larger than %ld.",
11475 m_create_info->key_block_size,
11476 kbs_max);
11477 ret = "KEY_BLOCK_SIZE";
11478 }
11479
11480 /* Valid KEY_BLOCK_SIZE, check its dependencies. */
11481 if (!m_allow_file_per_table) {
11482 push_warning(
11483 m_thd, Sql_condition::WARN_LEVEL_WARN,
11484 ER_ILLEGAL_HA_CREATE_OPTION,
11485 "InnoDB: KEY_BLOCK_SIZE requires"
11486 " innodb_file_per_table.");
11487 ret = "KEY_BLOCK_SIZE";
11488 }
11489 break;
11490 default:
11491 push_warning_printf(
11492 m_thd, Sql_condition::WARN_LEVEL_WARN,
11493 ER_ILLEGAL_HA_CREATE_OPTION,
11494 "InnoDB: invalid KEY_BLOCK_SIZE = %u."
11495 " Valid values are [1, 2, 4, 8, 16]",
11496 (uint) m_create_info->key_block_size);
11497 ret = "KEY_BLOCK_SIZE";
11498 break;
11499 }
11500 }
11501
11502 /* Check for a valid InnoDB ROW_FORMAT specifier and
11503 other incompatibilities. */
11504 switch (row_format) {
11505 case ROW_TYPE_COMPRESSED:
11506 if (is_temp) {
11507 my_error(ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE,
11508 MYF(0));
11509 return("ROW_FORMAT");
11510 }
11511 if (!m_allow_file_per_table) {
11512 push_warning_printf(
11513 m_thd, Sql_condition::WARN_LEVEL_WARN,
11514 ER_ILLEGAL_HA_CREATE_OPTION,
11515 "InnoDB: ROW_FORMAT=%s requires"
11516 " innodb_file_per_table.",
11517 get_row_format_name(row_format));
11518 ret = "ROW_FORMAT";
11519 }
11520 break;
11521 case ROW_TYPE_DYNAMIC:
11522 case ROW_TYPE_COMPACT:
11523 case ROW_TYPE_REDUNDANT:
11524 if (has_key_block_size) {
11525 push_warning_printf(
11526 m_thd, Sql_condition::WARN_LEVEL_WARN,
11527 ER_ILLEGAL_HA_CREATE_OPTION,
11528 "InnoDB: cannot specify ROW_FORMAT = %s"
11529 " with KEY_BLOCK_SIZE.",
11530 get_row_format_name(row_format));
11531 ret = "KEY_BLOCK_SIZE";
11532 }
11533 break;
11534 case ROW_TYPE_DEFAULT:
11535 break;
11536 case ROW_TYPE_FIXED:
11537 case ROW_TYPE_PAGE:
11538 case ROW_TYPE_NOT_USED:
11539 push_warning(
11540 m_thd, Sql_condition::WARN_LEVEL_WARN,
11541 ER_ILLEGAL_HA_CREATE_OPTION,
11542 "InnoDB: invalid ROW_FORMAT specifier.");
11543 ret = "ROW_TYPE";
11544 break;
11545 }
11546
11547 if (m_create_info->data_file_name
11548 && m_create_info->data_file_name[0] != '\0'
11549 && !create_option_data_directory_is_valid()) {
11550 ret = "DATA DIRECTORY";
11551 }
11552
11553 /* Do not allow INDEX_DIRECTORY */
11554 if (m_create_info->index_file_name) {
11555 push_warning_printf(
11556 m_thd, Sql_condition::WARN_LEVEL_WARN,
11557 ER_ILLEGAL_HA_CREATE_OPTION,
11558 "InnoDB: INDEX DIRECTORY is not supported");
11559 ret = "INDEX DIRECTORY";
11560 }
11561
11562 /* Don't support compressed table when page size > 16k. */
11563 if ((has_key_block_size || row_format == ROW_TYPE_COMPRESSED)
11564 && srv_page_size > UNIV_PAGE_SIZE_DEF) {
11565 push_warning(m_thd, Sql_condition::WARN_LEVEL_WARN,
11566 ER_ILLEGAL_HA_CREATE_OPTION,
11567 "InnoDB: Cannot create a COMPRESSED table"
11568 " when innodb_page_size > 16k.");
11569
11570 if (has_key_block_size) {
11571 ret = "KEY_BLOCK_SIZE";
11572 } else {
11573 ret = "ROW_TYPE";
11574 }
11575 }
11576
11577 return(ret);
11578}
11579
11580/*****************************************************************//**
11581Check engine specific table options not handled by SQL-parser.
11582@return NULL if valid, string if not */
11583const char*
11584create_table_info_t::check_table_options()
11585{
11586 enum row_type row_format = m_form->s->row_type;
11587 ha_table_option_struct *options= m_form->s->option_struct;
11588 fil_encryption_t encrypt = (fil_encryption_t)options->encryption;
11589 bool should_encrypt = (encrypt == FIL_ENCRYPTION_ON);
11590
11591 /* Currently we do not support encryption for
11592 spatial indexes thus do not allow creating table with forced
11593 encryption */
11594 for(ulint i = 0; i < m_form->s->keys; i++) {
11595 const KEY* key = m_form->key_info + i;
11596 if (key->flags & HA_SPATIAL && should_encrypt) {
11597 push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN,
11598 HA_ERR_UNSUPPORTED,
11599 "InnoDB: ENCRYPTED=ON not supported for table because "
11600 "it contains spatial index.");
11601 return "ENCRYPTED";
11602 }
11603 }
11604
11605 if (encrypt != FIL_ENCRYPTION_DEFAULT && !m_allow_file_per_table) {
11606 push_warning(
11607 m_thd, Sql_condition::WARN_LEVEL_WARN,
11608 HA_WRONG_CREATE_OPTION,
11609 "InnoDB: ENCRYPTED requires innodb_file_per_table");
11610 return "ENCRYPTED";
11611 }
11612
11613 if (encrypt == FIL_ENCRYPTION_OFF && srv_encrypt_tables == 2) {
11614 push_warning(
11615 m_thd, Sql_condition::WARN_LEVEL_WARN,
11616 HA_WRONG_CREATE_OPTION,
11617 "InnoDB: ENCRYPTED=OFF cannot be used when innodb_encrypt_tables=FORCE");
11618 return "ENCRYPTED";
11619 }
11620
11621 /* Check page compression requirements */
11622 if (options->page_compressed) {
11623
11624 if (row_format == ROW_TYPE_COMPRESSED) {
11625 push_warning(
11626 m_thd, Sql_condition::WARN_LEVEL_WARN,
11627 HA_WRONG_CREATE_OPTION,
11628 "InnoDB: PAGE_COMPRESSED table can't have"
11629 " ROW_TYPE=COMPRESSED");
11630 return "PAGE_COMPRESSED";
11631 }
11632
11633 if (row_format == ROW_TYPE_REDUNDANT) {
11634 push_warning(
11635 m_thd, Sql_condition::WARN_LEVEL_WARN,
11636 HA_WRONG_CREATE_OPTION,
11637 "InnoDB: PAGE_COMPRESSED table can't have"
11638 " ROW_TYPE=REDUNDANT");
11639 return "PAGE_COMPRESSED";
11640 }
11641
11642 if (!m_allow_file_per_table) {
11643 push_warning(
11644 m_thd, Sql_condition::WARN_LEVEL_WARN,
11645 HA_WRONG_CREATE_OPTION,
11646 "InnoDB: PAGE_COMPRESSED requires"
11647 " innodb_file_per_table.");
11648 return "PAGE_COMPRESSED";
11649 }
11650
11651 if (m_create_info->key_block_size) {
11652 push_warning(
11653 m_thd, Sql_condition::WARN_LEVEL_WARN,
11654 HA_WRONG_CREATE_OPTION,
11655 "InnoDB: PAGE_COMPRESSED table can't have"
11656 " key_block_size");
11657 return "PAGE_COMPRESSED";
11658 }
11659 }
11660
11661 /* Check page compression level requirements, some of them are
11662 already checked above */
11663 if (options->page_compression_level != 0) {
11664 if (options->page_compressed == false) {
11665 push_warning(
11666 m_thd, Sql_condition::WARN_LEVEL_WARN,
11667 HA_WRONG_CREATE_OPTION,
11668 "InnoDB: PAGE_COMPRESSION_LEVEL requires"
11669 " PAGE_COMPRESSED");
11670 return "PAGE_COMPRESSION_LEVEL";
11671 }
11672
11673 if (options->page_compression_level < 1 || options->page_compression_level > 9) {
11674 push_warning_printf(
11675 m_thd, Sql_condition::WARN_LEVEL_WARN,
11676 HA_WRONG_CREATE_OPTION,
11677 "InnoDB: invalid PAGE_COMPRESSION_LEVEL = %lu."
11678 " Valid values are [1, 2, 3, 4, 5, 6, 7, 8, 9]",
11679 options->page_compression_level);
11680 return "PAGE_COMPRESSION_LEVEL";
11681 }
11682 }
11683
11684 /* If encryption is set up make sure that used key_id is found */
11685 if (encrypt == FIL_ENCRYPTION_ON ||
11686 (encrypt == FIL_ENCRYPTION_DEFAULT && srv_encrypt_tables)) {
11687 if (!encryption_key_id_exists((unsigned int)options->encryption_key_id)) {
11688 push_warning_printf(
11689 m_thd, Sql_condition::WARN_LEVEL_WARN,
11690 HA_WRONG_CREATE_OPTION,
11691 "InnoDB: ENCRYPTION_KEY_ID %u not available",
11692 (uint)options->encryption_key_id
11693 );
11694 return "ENCRYPTION_KEY_ID";
11695 }
11696 }
11697
11698 /* Ignore nondefault key_id if encryption is set off */
11699 if (encrypt == FIL_ENCRYPTION_OFF &&
11700 options->encryption_key_id != THDVAR(m_thd, default_encryption_key_id)) {
11701 push_warning_printf(
11702 m_thd, Sql_condition::WARN_LEVEL_WARN,
11703 HA_WRONG_CREATE_OPTION,
11704 "InnoDB: Ignored ENCRYPTION_KEY_ID %u when encryption is disabled",
11705 (uint)options->encryption_key_id
11706 );
11707 options->encryption_key_id = FIL_DEFAULT_ENCRYPTION_KEY;
11708 }
11709
11710 /* If default encryption is used make sure that used kay is found
11711 from key file. */
11712 if (encrypt == FIL_ENCRYPTION_DEFAULT &&
11713 !srv_encrypt_tables &&
11714 options->encryption_key_id != FIL_DEFAULT_ENCRYPTION_KEY) {
11715 if (!encryption_key_id_exists((unsigned int)options->encryption_key_id)) {
11716 push_warning_printf(
11717 m_thd, Sql_condition::WARN_LEVEL_WARN,
11718 HA_WRONG_CREATE_OPTION,
11719 "InnoDB: ENCRYPTION_KEY_ID %u not available",
11720 (uint)options->encryption_key_id
11721 );
11722 return "ENCRYPTION_KEY_ID";
11723
11724 }
11725 }
11726
11727 return NULL;
11728}
11729
11730/*****************************************************************//**
11731Update create_info. Used in SHOW CREATE TABLE et al. */
11732
11733void
11734ha_innobase::update_create_info(
11735/*============================*/
11736 HA_CREATE_INFO* create_info) /*!< in/out: create info */
11737{
11738 if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
11739 info(HA_STATUS_AUTO);
11740 create_info->auto_increment_value = stats.auto_increment_value;
11741 }
11742
11743 if (m_prebuilt->table->is_temporary()) {
11744 return;
11745 }
11746
11747 /* Update the DATA DIRECTORY name from SYS_DATAFILES. */
11748 dict_get_and_save_data_dir_path(m_prebuilt->table, false);
11749
11750 if (m_prebuilt->table->data_dir_path) {
11751 create_info->data_file_name = m_prebuilt->table->data_dir_path;
11752 }
11753}
11754
11755/*****************************************************************//**
11756Initialize the table FTS stopword list
11757@return TRUE if success */
11758ibool
11759innobase_fts_load_stopword(
11760/*=======================*/
11761 dict_table_t* table, /*!< in: Table has the FTS */
11762 trx_t* trx, /*!< in: transaction */
11763 THD* thd) /*!< in: current thread */
11764{
11765 return(fts_load_stopword(table, trx,
11766 innobase_server_stopword_table,
11767 THDVAR(thd, ft_user_stopword_table),
11768 THDVAR(thd, ft_enable_stopword), FALSE));
11769}
11770
11771/** Parse the table name into normal name and remote path if needed.
11772@param[in] name Table name (db/table or full path).
11773@return 0 if successful, otherwise, error number */
11774int
11775create_table_info_t::parse_table_name(
11776 const char*
11777#ifdef _WIN32
11778 name
11779#endif
11780 )
11781{
11782 DBUG_ENTER("parse_table_name");
11783
11784#ifdef _WIN32
11785 /* Names passed in from server are in two formats:
11786 1. <database_name>/<table_name>: for normal table creation
11787 2. full path: for temp table creation, or DATA DIRECTORY.
11788
11789 When srv_file_per_table is on and mysqld_embedded is off,
11790 check for full path pattern, i.e.
11791 X:\dir\..., X is a driver letter, or
11792 \\dir1\dir2\..., UNC path
11793 returns error if it is in full path format, but not creating a temp.
11794 table. Currently InnoDB does not support symbolic link on Windows. */
11795
11796 if (m_innodb_file_per_table
11797 && !mysqld_embedded
11798 && !(m_create_info->options & HA_LEX_CREATE_TMP_TABLE)) {
11799
11800 if ((name[1] == ':')
11801 || (name[0] == '\\' && name[1] == '\\')) {
11802 sql_print_error("Cannot create table %s\n", name);
11803 DBUG_RETURN(HA_ERR_GENERIC);
11804 }
11805 }
11806#endif
11807
11808 m_remote_path[0] = '\0';
11809
11810 /* Make sure DATA DIRECTORY is compatible with other options
11811 and set the remote path. In the case of either;
11812 CREATE TEMPORARY TABLE ... DATA DIRECTORY={path} ... ;
11813 CREATE TABLE ... DATA DIRECTORY={path} TABLESPACE={name}... ;
11814 we ignore the DATA DIRECTORY. */
11815 if (m_create_info->data_file_name
11816 && m_create_info->data_file_name[0] != '\0') {
11817 if (!create_option_data_directory_is_valid()) {
11818 push_warning_printf(
11819 m_thd, Sql_condition::WARN_LEVEL_WARN,
11820 WARN_OPTION_IGNORED,
11821 ER_DEFAULT(WARN_OPTION_IGNORED),
11822 "DATA DIRECTORY");
11823
11824 m_flags &= ~DICT_TF_MASK_DATA_DIR;
11825 } else {
11826 strncpy(m_remote_path,
11827 m_create_info->data_file_name,
11828 FN_REFLEN - 1);
11829 }
11830 }
11831
11832 if (m_create_info->index_file_name) {
11833 my_error(WARN_OPTION_IGNORED, ME_JUST_WARNING,
11834 "INDEX DIRECTORY");
11835 }
11836
11837 DBUG_RETURN(0);
11838}
11839
11840/** Determine InnoDB table flags.
11841If strict_mode=OFF, this will adjust the flags to what should be assumed.
11842@retval true if successful, false if error */
11843bool
11844create_table_info_t::innobase_table_flags()
11845{
11846 DBUG_ENTER("innobase_table_flags");
11847
11848 const char* fts_doc_id_index_bad = NULL;
11849 ulint zip_ssize = 0;
11850 enum row_type row_type;
11851 rec_format_t innodb_row_format =
11852 get_row_format(innodb_default_row_format);
11853 const bool is_temp
11854 = m_create_info->options & HA_LEX_CREATE_TMP_TABLE;
11855 bool zip_allowed
11856 = !is_temp;
11857
11858 const ulint zip_ssize_max =
11859 ut_min(static_cast<ulint>(UNIV_PAGE_SSIZE_MAX),
11860 static_cast<ulint>(PAGE_ZIP_SSIZE_MAX));
11861
11862 /* Cache the value of innobase_compression_level, in case it is
11863 modified by another thread while the table is being created. */
11864 const ulint default_compression_level = page_zip_level;
11865
11866 ha_table_option_struct *options= m_form->s->option_struct;
11867
11868 m_flags = 0;
11869 m_flags2 = 0;
11870
11871 /* Check if there are any FTS indexes defined on this table. */
11872 for (uint i = 0; i < m_form->s->keys; i++) {
11873 const KEY* key = &m_form->key_info[i];
11874
11875 if (key->flags & HA_FULLTEXT) {
11876 m_flags2 |= DICT_TF2_FTS;
11877
11878 /* We don't support FTS indexes in temporary
11879 tables. */
11880 if (is_temp) {
11881 my_error(ER_INNODB_NO_FT_TEMP_TABLE, MYF(0));
11882 DBUG_RETURN(false);
11883 }
11884
11885 if (fts_doc_id_index_bad) {
11886 goto index_bad;
11887 }
11888 }
11889
11890 if (innobase_strcasecmp(key->name.str, FTS_DOC_ID_INDEX_NAME)) {
11891 continue;
11892 }
11893
11894 /* Do a pre-check on FTS DOC ID index */
11895 if (!(key->flags & HA_NOSAME)
11896 || strcmp(key->name.str, FTS_DOC_ID_INDEX_NAME)
11897 || strcmp(key->key_part[0].field->field_name.str,
11898 FTS_DOC_ID_COL_NAME)) {
11899 fts_doc_id_index_bad = key->name.str;
11900 }
11901
11902 if (fts_doc_id_index_bad && (m_flags2 & DICT_TF2_FTS)) {
11903index_bad:
11904 my_error(ER_INNODB_FT_WRONG_DOCID_INDEX, MYF(0),
11905 fts_doc_id_index_bad);
11906 DBUG_RETURN(false);
11907 }
11908 }
11909
11910 if (m_create_info->key_block_size > 0) {
11911 /* The requested compressed page size (key_block_size)
11912 is given in kilobytes. If it is a valid number, store
11913 that value as the number of log2 shifts from 512 in
11914 zip_ssize. Zero means it is not compressed. */
11915 ulint zssize; /* Zip Shift Size */
11916 ulint kbsize; /* Key Block Size */
11917 for (zssize = kbsize = 1;
11918 zssize <= zip_ssize_max;
11919 zssize++, kbsize <<= 1) {
11920 if (kbsize == m_create_info->key_block_size) {
11921 zip_ssize = zssize;
11922 break;
11923 }
11924 }
11925
11926 /* Make sure compressed row format is allowed. */
11927 if (is_temp) {
11928 push_warning(
11929 m_thd, Sql_condition::WARN_LEVEL_WARN,
11930 ER_ILLEGAL_HA_CREATE_OPTION,
11931 "InnoDB: KEY_BLOCK_SIZE is ignored"
11932 " for TEMPORARY TABLE.");
11933 zip_allowed = false;
11934 } else if (!m_allow_file_per_table) {
11935 push_warning(
11936 m_thd, Sql_condition::WARN_LEVEL_WARN,
11937 ER_ILLEGAL_HA_CREATE_OPTION,
11938 "InnoDB: KEY_BLOCK_SIZE requires"
11939 " innodb_file_per_table.");
11940 zip_allowed = false;
11941 }
11942
11943 if (!zip_allowed
11944 || zssize > zip_ssize_max) {
11945 push_warning_printf(
11946 m_thd, Sql_condition::WARN_LEVEL_WARN,
11947 ER_ILLEGAL_HA_CREATE_OPTION,
11948 "InnoDB: ignoring KEY_BLOCK_SIZE=%u.",
11949 (uint) m_create_info->key_block_size);
11950 }
11951 }
11952
11953 row_type = m_form->s->row_type;
11954
11955 if (zip_ssize && zip_allowed) {
11956 /* if ROW_FORMAT is set to default,
11957 automatically change it to COMPRESSED. */
11958 if (row_type == ROW_TYPE_DEFAULT) {
11959 row_type = ROW_TYPE_COMPRESSED;
11960 } else if (row_type != ROW_TYPE_COMPRESSED) {
11961 /* ROW_FORMAT other than COMPRESSED
11962 ignores KEY_BLOCK_SIZE. It does not
11963 make sense to reject conflicting
11964 KEY_BLOCK_SIZE and ROW_FORMAT, because
11965 such combinations can be obtained
11966 with ALTER TABLE anyway. */
11967 push_warning_printf(
11968 m_thd, Sql_condition::WARN_LEVEL_WARN,
11969 ER_ILLEGAL_HA_CREATE_OPTION,
11970 "InnoDB: ignoring KEY_BLOCK_SIZE=%u"
11971 " unless ROW_FORMAT=COMPRESSED.",
11972 (uint) m_create_info->key_block_size);
11973 zip_allowed = false;
11974 }
11975 } else {
11976 /* zip_ssize == 0 means no KEY_BLOCK_SIZE. */
11977 if (row_type == ROW_TYPE_COMPRESSED && zip_allowed) {
11978 /* ROW_FORMAT=COMPRESSED without KEY_BLOCK_SIZE
11979 implies half the maximum KEY_BLOCK_SIZE(*1k) or
11980 srv_page_size, whichever is less. */
11981 zip_ssize = zip_ssize_max - 1;
11982 }
11983 }
11984
11985 /* Validate the row format. Correct it if necessary */
11986
11987 switch (row_type) {
11988 case ROW_TYPE_REDUNDANT:
11989 innodb_row_format = REC_FORMAT_REDUNDANT;
11990 break;
11991 case ROW_TYPE_COMPACT:
11992 innodb_row_format = REC_FORMAT_COMPACT;
11993 break;
11994 case ROW_TYPE_COMPRESSED:
11995 if (is_temp) {
11996 push_warning_printf(
11997 m_thd, Sql_condition::WARN_LEVEL_WARN,
11998 ER_ILLEGAL_HA_CREATE_OPTION,
11999 "InnoDB: ROW_FORMAT=%s is ignored for"
12000 " TEMPORARY TABLE.",
12001 get_row_format_name(row_type));
12002 } else if (!m_allow_file_per_table) {
12003 push_warning_printf(
12004 m_thd, Sql_condition::WARN_LEVEL_WARN,
12005 ER_ILLEGAL_HA_CREATE_OPTION,
12006 "InnoDB: ROW_FORMAT=COMPRESSED requires"
12007 " innodb_file_per_table.");
12008 } else {
12009 innodb_row_format = REC_FORMAT_COMPRESSED;
12010 break;
12011 }
12012 zip_allowed = false;
12013 /* Set ROW_FORMAT = COMPACT */
12014 /* fall through */
12015 case ROW_TYPE_NOT_USED:
12016 case ROW_TYPE_FIXED:
12017 case ROW_TYPE_PAGE:
12018 push_warning(
12019 m_thd, Sql_condition::WARN_LEVEL_WARN,
12020 ER_ILLEGAL_HA_CREATE_OPTION,
12021 "InnoDB: assuming ROW_FORMAT=DYNAMIC.");
12022 /* fall through */
12023 case ROW_TYPE_DYNAMIC:
12024 innodb_row_format = REC_FORMAT_DYNAMIC;
12025 break;
12026 case ROW_TYPE_DEFAULT:
12027 ;
12028 }
12029
12030 /* Don't support compressed table when page size > 16k. */
12031 if (zip_allowed && zip_ssize && srv_page_size > UNIV_PAGE_SIZE_DEF) {
12032 push_warning(m_thd, Sql_condition::WARN_LEVEL_WARN,
12033 ER_ILLEGAL_HA_CREATE_OPTION,
12034 "InnoDB: Cannot create a COMPRESSED table"
12035 " when innodb_page_size > 16k."
12036 " Assuming ROW_FORMAT=DYNAMIC.");
12037 zip_allowed = false;
12038 }
12039
12040 ut_ad(!is_temp || !zip_allowed);
12041 ut_ad(!is_temp || innodb_row_format != REC_FORMAT_COMPRESSED);
12042
12043 /* Set the table flags */
12044 if (!zip_allowed) {
12045 zip_ssize = 0;
12046 }
12047
12048 if (is_temp) {
12049 m_flags2 |= DICT_TF2_TEMPORARY;
12050 } else if (m_use_file_per_table) {
12051 m_flags2 |= DICT_TF2_USE_FILE_PER_TABLE;
12052 }
12053
12054 /* Set the table flags */
12055 dict_tf_set(&m_flags, innodb_row_format, zip_ssize,
12056 m_use_data_dir,
12057 options->page_compressed,
12058 options->page_compression_level == 0 ?
12059 default_compression_level : ulint(options->page_compression_level));
12060
12061 if (m_form->s->table_type == TABLE_TYPE_SEQUENCE) {
12062 m_flags |= DICT_TF_MASK_NO_ROLLBACK;
12063 }
12064
12065 /* Set the flags2 when create table or alter tables */
12066 m_flags2 |= DICT_TF2_FTS_AUX_HEX_NAME;
12067 DBUG_EXECUTE_IF("innodb_test_wrong_fts_aux_table_name",
12068 m_flags2 &= ~DICT_TF2_FTS_AUX_HEX_NAME;);
12069
12070 DBUG_RETURN(true);
12071}
12072
12073/** Parse MERGE_THRESHOLD value from the string.
12074@param[in] thd connection
12075@param[in] str string which might include 'MERGE_THRESHOLD='
12076@return value parsed. 0 means not found or invalid value. */
12077static
12078ulint
12079innobase_parse_merge_threshold(
12080 THD* thd,
12081 const char* str)
12082{
12083 static const char* label = "MERGE_THRESHOLD=";
12084 static const size_t label_len = strlen(label);
12085 const char* pos = str;
12086
12087 pos = strstr(str, label);
12088
12089 if (pos == NULL) {
12090 return(0);
12091 }
12092
12093 pos += label_len;
12094
12095 lint ret = atoi(pos);
12096
12097 if (ret > 0 && ret <= 50) {
12098 return(static_cast<ulint>(ret));
12099 }
12100
12101 push_warning_printf(
12102 thd, Sql_condition::WARN_LEVEL_WARN,
12103 ER_ILLEGAL_HA_CREATE_OPTION,
12104 "InnoDB: Invalid value for MERGE_THRESHOLD in the CREATE TABLE"
12105 " statement. The value is ignored.");
12106
12107 return(0);
12108}
12109
12110/** Parse hint for table and its indexes, and update the information
12111in dictionary.
12112@param[in] thd connection
12113@param[in,out] table target table
12114@param[in] table_share table definition */
12115void
12116innobase_parse_hint_from_comment(
12117 THD* thd,
12118 dict_table_t* table,
12119 const TABLE_SHARE* table_share)
12120{
12121 ulint merge_threshold_table;
12122 ulint merge_threshold_index[MAX_KEY];
12123 bool is_found[MAX_KEY];
12124
12125 if (table_share->comment.str != NULL) {
12126 merge_threshold_table
12127 = innobase_parse_merge_threshold(
12128 thd, table_share->comment.str);
12129 } else {
12130 merge_threshold_table = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
12131 }
12132
12133 if (merge_threshold_table == 0) {
12134 merge_threshold_table = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
12135 }
12136
12137 for (uint i = 0; i < table_share->keys; i++) {
12138 KEY* key_info = &table_share->key_info[i];
12139
12140 ut_ad(i < sizeof(merge_threshold_index)
12141 / sizeof(merge_threshold_index[0]));
12142
12143 if (key_info->flags & HA_USES_COMMENT
12144 && key_info->comment.str != NULL) {
12145 merge_threshold_index[i]
12146 = innobase_parse_merge_threshold(
12147 thd, key_info->comment.str);
12148 } else {
12149 merge_threshold_index[i] = merge_threshold_table;
12150 }
12151
12152 if (merge_threshold_index[i] == 0) {
12153 merge_threshold_index[i] = merge_threshold_table;
12154 }
12155 }
12156
12157 /* update SYS_INDEX table */
12158 if (!table->is_temporary()) {
12159 for (uint i = 0; i < table_share->keys; i++) {
12160 is_found[i] = false;
12161 }
12162
12163 for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
12164 index != NULL;
12165 index = UT_LIST_GET_NEXT(indexes, index)) {
12166
12167 if (dict_index_is_auto_gen_clust(index)) {
12168
12169 /* GEN_CLUST_INDEX should use
12170 merge_threshold_table */
12171 dict_index_set_merge_threshold(
12172 index, merge_threshold_table);
12173 continue;
12174 }
12175
12176 for (uint i = 0; i < table_share->keys; i++) {
12177 if (is_found[i]) {
12178 continue;
12179 }
12180
12181 KEY* key_info = &table_share->key_info[i];
12182
12183 if (innobase_strcasecmp(
12184 index->name, key_info->name.str) == 0) {
12185
12186 dict_index_set_merge_threshold(
12187 index,
12188 merge_threshold_index[i]);
12189 is_found[i] = true;
12190 break;
12191 }
12192 }
12193 }
12194 }
12195
12196 for (uint i = 0; i < table_share->keys; i++) {
12197 is_found[i] = false;
12198 }
12199
12200 /* update in memory */
12201 for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
12202 index != NULL;
12203 index = UT_LIST_GET_NEXT(indexes, index)) {
12204
12205 if (dict_index_is_auto_gen_clust(index)) {
12206
12207 /* GEN_CLUST_INDEX should use merge_threshold_table */
12208
12209 /* x-lock index is needed to exclude concurrent
12210 pessimistic tree operations */
12211 rw_lock_x_lock(dict_index_get_lock(index));
12212 index->merge_threshold = merge_threshold_table;
12213 rw_lock_x_unlock(dict_index_get_lock(index));
12214
12215 continue;
12216 }
12217
12218 for (uint i = 0; i < table_share->keys; i++) {
12219 if (is_found[i]) {
12220 continue;
12221 }
12222
12223 KEY* key_info = &table_share->key_info[i];
12224
12225 if (innobase_strcasecmp(
12226 index->name, key_info->name.str) == 0) {
12227
12228 /* x-lock index is needed to exclude concurrent
12229 pessimistic tree operations */
12230 rw_lock_x_lock(dict_index_get_lock(index));
12231 index->merge_threshold
12232 = merge_threshold_index[i];
12233 rw_lock_x_unlock(dict_index_get_lock(index));
12234 is_found[i] = true;
12235
12236 break;
12237 }
12238 }
12239 }
12240}
12241
12242/** Set m_use_* flags. */
12243void
12244create_table_info_t::set_tablespace_type(
12245 bool table_being_altered_is_file_per_table)
12246{
12247 /** Allow file_per_table for this table either because:
12248 1) the setting innodb_file_per_table=on,
12249 2) the table being altered is currently file_per_table */
12250 m_allow_file_per_table =
12251 m_innodb_file_per_table
12252 || table_being_altered_is_file_per_table;
12253
12254 /* Ignore the current innodb-file-per-table setting if we are
12255 creating a temporary table. */
12256 m_use_file_per_table =
12257 m_allow_file_per_table
12258 && !(m_create_info->options & HA_LEX_CREATE_TMP_TABLE);
12259
12260 /* DATA DIRECTORY must have m_use_file_per_table but cannot be
12261 used with TEMPORARY tables. */
12262 m_use_data_dir =
12263 m_use_file_per_table
12264 && (m_create_info->data_file_name != NULL)
12265 && (m_create_info->data_file_name[0] != '\0');
12266}
12267
12268/** Initialize the create_table_info_t object.
12269@return error number */
12270int
12271create_table_info_t::initialize()
12272{
12273 DBUG_ENTER("create_table_info_t::initialize");
12274
12275 ut_ad(m_thd != NULL);
12276 ut_ad(m_create_info != NULL);
12277
12278 if (m_form->s->fields > REC_MAX_N_USER_FIELDS) {
12279 DBUG_RETURN(HA_ERR_TOO_MANY_FIELDS);
12280 }
12281
12282 /* Check for name conflicts (with reserved name) for
12283 any user indices to be created. */
12284 if (innobase_index_name_is_reserved(m_thd, m_form->key_info,
12285 m_form->s->keys)) {
12286 DBUG_RETURN(HA_ERR_WRONG_INDEX);
12287 }
12288
12289 ut_ad(m_form->s->row_type == m_create_info->row_type);
12290
12291 /* Get the transaction associated with the current thd, or create one
12292 if not yet created */
12293
12294 check_trx_exists(m_thd);
12295
12296 DBUG_RETURN(0);
12297}
12298
12299
12300/** Check if a virtual column is part of a fulltext or spatial index. */
12301bool
12302create_table_info_t::gcols_in_fulltext_or_spatial()
12303{
12304 for (ulint i = 0; i < m_form->s->keys; i++) {
12305 const KEY* key = m_form->key_info + i;
12306 if (key->flags & (HA_SPATIAL | HA_FULLTEXT)) {
12307 for (ulint j = 0; j < key->user_defined_key_parts; j++) {
12308 const KEY_PART_INFO* key_part = key->key_part + j;
12309
12310 /* We do not support special (Fulltext or
12311 Spatial) index on virtual columns */
12312 if (innobase_is_v_fld(key_part->field)) {
12313 my_error(ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN, MYF(0));
12314 return true;
12315 }
12316 }
12317 }
12318
12319 }
12320 return false;
12321}
12322
12323
12324/** Prepare to create a new table to an InnoDB database.
12325@param[in] name Table name
12326@return error number */
12327int
12328create_table_info_t::prepare_create_table(
12329 const char* name)
12330{
12331 DBUG_ENTER("prepare_create_table");
12332
12333 ut_ad(m_thd != NULL);
12334 ut_ad(m_create_info != NULL);
12335
12336 ut_ad(m_form->s->row_type == m_create_info->row_type);
12337
12338 set_tablespace_type(false);
12339
12340 normalize_table_name(m_table_name, name);
12341
12342 /* Validate table options not handled by the SQL-parser */
12343 if (check_table_options()) {
12344 DBUG_RETURN(HA_WRONG_CREATE_OPTION);
12345 }
12346
12347 /* Validate the create options if innodb_strict_mode is set.
12348 Do not use the regular message for ER_ILLEGAL_HA_CREATE_OPTION
12349 because InnoDB might actually support the option, but not under
12350 the current conditions. The messages revealing the specific
12351 problems are reported inside this function. */
12352 if (create_options_are_invalid()) {
12353 DBUG_RETURN(HA_WRONG_CREATE_OPTION);
12354 }
12355
12356 /* Create the table flags and flags2 */
12357 if (!innobase_table_flags()) {
12358 DBUG_RETURN(HA_WRONG_CREATE_OPTION);
12359 }
12360
12361 if (high_level_read_only) {
12362 DBUG_RETURN(HA_ERR_TABLE_READONLY);
12363 }
12364
12365 if (gcols_in_fulltext_or_spatial()) {
12366 DBUG_RETURN(HA_ERR_UNSUPPORTED);
12367 }
12368
12369 DBUG_RETURN(parse_table_name(name));
12370}
12371
12372/** Create a new table to an InnoDB database.
12373@return error number */
12374int
12375create_table_info_t::create_table()
12376{
12377 int error;
12378 int primary_key_no;
12379 uint i;
12380 const char* stmt;
12381 size_t stmt_len;
12382
12383 DBUG_ENTER("create_table");
12384
12385 /* Look for a primary key */
12386 primary_key_no = (m_form->s->primary_key != MAX_KEY ?
12387 (int) m_form->s->primary_key : -1);
12388
12389 /* Our function innobase_get_mysql_key_number_for_index assumes
12390 the primary key is always number 0, if it exists */
12391 ut_a(primary_key_no == -1 || primary_key_no == 0);
12392
12393 error = create_table_def();
12394
12395 if (error) {
12396 DBUG_RETURN(error);
12397 }
12398
12399 /* Create the keys */
12400
12401 if (m_form->s->keys == 0 || primary_key_no == -1) {
12402 /* Create an index which is used as the clustered index;
12403 order the rows by their row id which is internally generated
12404 by InnoDB */
12405 dict_index_t* index = dict_mem_index_create(
12406 m_table, innobase_index_reserve_name,
12407 DICT_CLUSTERED, 0);
12408 error = convert_error_code_to_mysql(
12409 row_create_index_for_mysql(index, m_trx, NULL),
12410 m_table->flags, m_thd);
12411 if (error) {
12412 DBUG_RETURN(error);
12413 }
12414 }
12415
12416 if (primary_key_no != -1) {
12417 /* In InnoDB the clustered index must always be created
12418 first */
12419 if ((error = create_index(m_trx, m_form, m_table,
12420 (uint) primary_key_no))) {
12421 DBUG_RETURN(error);
12422 }
12423 }
12424
12425 /* Create the ancillary tables that are common to all FTS indexes on
12426 this table. */
12427 if (m_flags2 & DICT_TF2_FTS) {
12428 fts_doc_id_index_enum ret;
12429
12430 /* Check whether there already exists FTS_DOC_ID_INDEX */
12431 ret = innobase_fts_check_doc_id_index_in_def(
12432 m_form->s->keys, m_form->key_info);
12433
12434 switch (ret) {
12435 case FTS_INCORRECT_DOC_ID_INDEX:
12436 push_warning_printf(m_thd,
12437 Sql_condition::WARN_LEVEL_WARN,
12438 ER_WRONG_NAME_FOR_INDEX,
12439 " InnoDB: Index name %s is reserved"
12440 " for the unique index on"
12441 " FTS_DOC_ID column for FTS"
12442 " Document ID indexing"
12443 " on table %s. Please check"
12444 " the index definition to"
12445 " make sure it is of correct"
12446 " type\n",
12447 FTS_DOC_ID_INDEX_NAME,
12448 m_table->name.m_name);
12449
12450 if (m_table->fts) {
12451 fts_free(m_table);
12452 }
12453
12454 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
12455 FTS_DOC_ID_INDEX_NAME);
12456 error = -1;
12457 DBUG_RETURN(error);
12458 case FTS_EXIST_DOC_ID_INDEX:
12459 case FTS_NOT_EXIST_DOC_ID_INDEX:
12460 break;
12461 }
12462
12463 dberr_t err = fts_create_common_tables(
12464 m_trx, m_table,
12465 (ret == FTS_EXIST_DOC_ID_INDEX));
12466
12467 error = convert_error_code_to_mysql(err, 0, NULL);
12468
12469 if (error) {
12470 trx_rollback_to_savepoint(m_trx, NULL);
12471 m_trx->error_state = DB_SUCCESS;
12472
12473 row_drop_table_for_mysql(m_table_name, m_trx, true, FALSE);
12474
12475 m_trx->error_state = DB_SUCCESS;
12476 DBUG_RETURN(error);
12477 }
12478 }
12479
12480 for (i = 0; i < m_form->s->keys; i++) {
12481 if (i != uint(primary_key_no)
12482 && (error = create_index(m_trx, m_form, m_table, i))) {
12483 DBUG_RETURN(error);
12484 }
12485 }
12486
12487 /* Cache all the FTS indexes on this table in the FTS specific
12488 structure. They are used for FTS indexed column update handling. */
12489 if (m_flags2 & DICT_TF2_FTS) {
12490 fts_t* fts = m_table->fts;
12491
12492 ut_a(fts != NULL);
12493
12494 dict_table_get_all_fts_indexes(m_table, fts->indexes);
12495 }
12496
12497 stmt = innobase_get_stmt_unsafe(m_thd, &stmt_len);
12498
12499 if (stmt) {
12500 dberr_t err = row_table_add_foreign_constraints(
12501 m_trx, stmt, stmt_len, m_table_name,
12502 m_create_info->options & HA_LEX_CREATE_TMP_TABLE);
12503
12504 switch (err) {
12505
12506 case DB_PARENT_NO_INDEX:
12507 push_warning_printf(
12508 m_thd, Sql_condition::WARN_LEVEL_WARN,
12509 HA_ERR_CANNOT_ADD_FOREIGN,
12510 "Create table '%s' with foreign key constraint"
12511 " failed. There is no index in the referenced"
12512 " table where the referenced columns appear"
12513 " as the first columns.\n", m_table_name);
12514 break;
12515
12516 case DB_CHILD_NO_INDEX:
12517 push_warning_printf(
12518 m_thd, Sql_condition::WARN_LEVEL_WARN,
12519 HA_ERR_CANNOT_ADD_FOREIGN,
12520 "Create table '%s' with foreign key constraint"
12521 " failed. There is no index in the referencing"
12522 " table where referencing columns appear"
12523 " as the first columns.\n", m_table_name);
12524 break;
12525 case DB_NO_FK_ON_S_BASE_COL:
12526 push_warning_printf(
12527 m_thd, Sql_condition::WARN_LEVEL_WARN,
12528 HA_ERR_CANNOT_ADD_FOREIGN,
12529 "Create table '%s' with foreign key constraint"
12530 " failed. Cannot add foreign key constraint"
12531 " placed on the base column of stored"
12532 " column. \n",
12533 m_table_name);
12534 default:
12535 break;
12536 }
12537
12538 error = convert_error_code_to_mysql(err, m_flags, NULL);
12539
12540 if (error) {
12541 DBUG_RETURN(error);
12542 }
12543 }
12544
12545 DBUG_RETURN(0);
12546}
12547
12548/** Update a new table in an InnoDB database.
12549@return error number */
12550int
12551create_table_info_t::create_table_update_dict()
12552{
12553 dict_table_t* innobase_table;
12554
12555 DBUG_ENTER("create_table_update_dict");
12556
12557 innobase_table = dict_table_open_on_name(
12558 m_table_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE);
12559
12560 DBUG_ASSERT(innobase_table != 0);
12561 if (innobase_table->fts != NULL) {
12562 if (innobase_table->fts_doc_id_index == NULL) {
12563 innobase_table->fts_doc_id_index
12564 = dict_table_get_index_on_name(
12565 innobase_table, FTS_DOC_ID_INDEX_NAME);
12566 DBUG_ASSERT(innobase_table->fts_doc_id_index != NULL);
12567 } else {
12568 DBUG_ASSERT(innobase_table->fts_doc_id_index
12569 == dict_table_get_index_on_name(
12570 innobase_table,
12571 FTS_DOC_ID_INDEX_NAME));
12572 }
12573 }
12574
12575 DBUG_ASSERT((innobase_table->fts == NULL)
12576 == (innobase_table->fts_doc_id_index == NULL));
12577
12578 innobase_copy_frm_flags_from_create_info(innobase_table, m_create_info);
12579
12580 dict_stats_update(innobase_table, DICT_STATS_EMPTY_TABLE);
12581
12582 /* Load server stopword into FTS cache */
12583 if (m_flags2 & DICT_TF2_FTS) {
12584 if (!innobase_fts_load_stopword(innobase_table, NULL, m_thd)) {
12585 dict_table_close(innobase_table, FALSE, FALSE);
12586 srv_active_wake_master_thread();
12587 trx_free(m_trx);
12588 DBUG_RETURN(-1);
12589 }
12590 }
12591
12592 if (const Field* ai = m_form->found_next_number_field) {
12593 ut_ad(!innobase_is_v_fld(ai));
12594
12595 ib_uint64_t autoinc = m_create_info->auto_increment_value;
12596
12597 if (autoinc == 0) {
12598 autoinc = 1;
12599 }
12600
12601 dict_table_autoinc_lock(innobase_table);
12602 dict_table_autoinc_initialize(innobase_table, autoinc);
12603
12604 if (innobase_table->is_temporary()) {
12605 /* AUTO_INCREMENT is not persistent for
12606 TEMPORARY TABLE. Temporary tables are never
12607 evicted. Keep the counter in memory only. */
12608 } else {
12609 const unsigned col_no = innodb_col_no(ai);
12610
12611 innobase_table->persistent_autoinc = 1
12612 + dict_table_get_nth_col_pos(
12613 innobase_table, col_no, NULL);
12614
12615 /* Persist the "last used" value, which
12616 typically is AUTO_INCREMENT - 1.
12617 In btr_create(), the value 0 was already written. */
12618 if (--autoinc) {
12619 btr_write_autoinc(
12620 dict_table_get_first_index(
12621 innobase_table),
12622 autoinc);
12623 }
12624 }
12625
12626 dict_table_autoinc_unlock(innobase_table);
12627 }
12628
12629 innobase_parse_hint_from_comment(m_thd, innobase_table, m_form->s);
12630
12631 dict_table_close(innobase_table, FALSE, FALSE);
12632 DBUG_RETURN(0);
12633}
12634
12635/** Allocate a new trx. */
12636void
12637create_table_info_t::allocate_trx()
12638{
12639 m_trx = innobase_trx_allocate(m_thd);
12640
12641 m_trx->will_lock++;
12642 m_trx->ddl = true;
12643}
12644
12645/** Create a new table to an InnoDB database.
12646@param[in] name Table name, format: "db/table_name".
12647@param[in] form Table format; columns and index information.
12648@param[in] create_info Create info (including create statement string).
12649@return 0 if success else error number. */
12650int
12651ha_innobase::create(
12652 const char* name,
12653 TABLE* form,
12654 HA_CREATE_INFO* create_info)
12655{
12656 int error;
12657 char norm_name[FN_REFLEN]; /* {database}/{tablename} */
12658 char remote_path[FN_REFLEN]; /* Absolute path of table */
12659 trx_t* trx;
12660 DBUG_ENTER("ha_innobase::create");
12661
12662 DBUG_ASSERT(form->s == table_share);
12663 DBUG_ASSERT(table_share->table_type == TABLE_TYPE_SEQUENCE
12664 || table_share->table_type == TABLE_TYPE_NORMAL);
12665
12666 create_table_info_t info(ha_thd(),
12667 form,
12668 create_info,
12669 norm_name,
12670 remote_path);
12671
12672 /* Initialize the object. */
12673 if ((error = info.initialize())) {
12674 DBUG_RETURN(error);
12675 }
12676
12677 /* Prepare for create and validate options. */
12678 if ((error = info.prepare_create_table(name))) {
12679 DBUG_RETURN(error);
12680 }
12681
12682 info.allocate_trx();
12683
12684 trx = info.trx();
12685
12686 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
12687 or lock waits can happen in it during a table create operation.
12688 Drop table etc. do this latching in row0mysql.cc. */
12689 row_mysql_lock_data_dictionary(trx);
12690
12691 if ((error = info.create_table())) {
12692 goto cleanup;
12693 }
12694
12695 innobase_commit_low(trx);
12696
12697 ut_ad(!srv_read_only_mode);
12698 row_mysql_unlock_data_dictionary(trx);
12699 /* Flush the log to reduce probability that the .frm files and
12700 the InnoDB data dictionary get out-of-sync if the user runs
12701 with innodb_flush_log_at_trx_commit = 0 */
12702 log_buffer_flush_to_disk();
12703
12704 error = info.create_table_update_dict();
12705
12706 /* Tell the InnoDB server that there might be work for
12707 utility threads: */
12708
12709 srv_active_wake_master_thread();
12710
12711 trx_free(trx);
12712
12713 DBUG_RETURN(error);
12714
12715cleanup:
12716 trx_rollback_for_mysql(trx);
12717 row_mysql_unlock_data_dictionary(trx);
12718 trx_free(trx);
12719
12720 DBUG_RETURN(error);
12721}
12722
12723/*****************************************************************//**
12724Discards or imports an InnoDB tablespace.
12725@return 0 == success, -1 == error */
12726
12727int
12728ha_innobase::discard_or_import_tablespace(
12729/*======================================*/
12730 my_bool discard) /*!< in: TRUE if discard, else import */
12731{
12732
12733 DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
12734
12735 ut_a(m_prebuilt->trx != NULL);
12736 ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
12737 ut_a(m_prebuilt->trx == thd_to_trx(ha_thd()));
12738
12739 if (high_level_read_only) {
12740 DBUG_RETURN(HA_ERR_TABLE_READONLY);
12741 }
12742
12743 dict_table_t* dict_table = m_prebuilt->table;
12744
12745 if (dict_table->is_temporary()) {
12746
12747 ib_senderrf(
12748 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
12749 ER_CANNOT_DISCARD_TEMPORARY_TABLE);
12750
12751 DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
12752 }
12753
12754 if (dict_table->space == fil_system.sys_space) {
12755 ib_senderrf(
12756 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
12757 ER_TABLE_IN_SYSTEM_TABLESPACE,
12758 dict_table->name.m_name);
12759
12760 DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
12761 }
12762
12763 trx_start_if_not_started(m_prebuilt->trx, true);
12764
12765 /* Obtain an exclusive lock on the table. */
12766 dberr_t err = row_mysql_lock_table(
12767 m_prebuilt->trx, dict_table, LOCK_X,
12768 discard ? "setting table lock for DISCARD TABLESPACE"
12769 : "setting table lock for IMPORT TABLESPACE");
12770
12771 if (err != DB_SUCCESS) {
12772 /* unable to lock the table: do nothing */
12773 } else if (discard) {
12774
12775 /* Discarding an already discarded tablespace should be an
12776 idempotent operation. Also, if the .ibd file is missing the
12777 user may want to set the DISCARD flag in order to IMPORT
12778 a new tablespace. */
12779
12780 if (!dict_table->is_readable()) {
12781 ib_senderrf(
12782 m_prebuilt->trx->mysql_thd,
12783 IB_LOG_LEVEL_WARN, ER_TABLESPACE_MISSING,
12784 dict_table->name.m_name);
12785 }
12786
12787 err = row_discard_tablespace_for_mysql(
12788 dict_table->name.m_name, m_prebuilt->trx);
12789
12790 } else if (dict_table->is_readable()) {
12791 /* Commit the transaction in order to
12792 release the table lock. */
12793 trx_commit_for_mysql(m_prebuilt->trx);
12794
12795 ib::error() << "Unable to import tablespace "
12796 << dict_table->name << " because it already"
12797 " exists. Please DISCARD the tablespace"
12798 " before IMPORT.";
12799 ib_senderrf(
12800 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
12801 ER_TABLESPACE_EXISTS, dict_table->name.m_name);
12802
12803 DBUG_RETURN(HA_ERR_TABLE_EXIST);
12804 } else {
12805 err = row_import_for_mysql(dict_table, m_prebuilt);
12806
12807 if (err == DB_SUCCESS) {
12808
12809 info(HA_STATUS_TIME
12810 | HA_STATUS_CONST
12811 | HA_STATUS_VARIABLE
12812 | HA_STATUS_AUTO);
12813
12814 fil_crypt_set_encrypt_tables(srv_encrypt_tables);
12815 }
12816 }
12817
12818 /* Commit the transaction in order to release the table lock. */
12819 trx_commit_for_mysql(m_prebuilt->trx);
12820
12821 if (err == DB_SUCCESS && !discard
12822 && dict_stats_is_persistent_enabled(dict_table)) {
12823 dberr_t ret;
12824
12825 /* Adjust the persistent statistics. */
12826 ret = dict_stats_update(dict_table,
12827 DICT_STATS_RECALC_PERSISTENT);
12828
12829 if (ret != DB_SUCCESS) {
12830 push_warning_printf(
12831 ha_thd(),
12832 Sql_condition::WARN_LEVEL_WARN,
12833 ER_ALTER_INFO,
12834 "Error updating stats for table '%s'"
12835 " after table rebuild: %s",
12836 dict_table->name.m_name, ut_strerr(ret));
12837 }
12838 }
12839
12840 DBUG_RETURN(convert_error_code_to_mysql(err, dict_table->flags, NULL));
12841}
12842
12843/*****************************************************************//**
12844Deletes all rows of an InnoDB table.
12845@return error number */
12846
12847int
12848ha_innobase::truncate()
12849/*===================*/
12850{
12851 DBUG_ENTER("ha_innobase::truncate");
12852
12853 if (high_level_read_only) {
12854 DBUG_RETURN(HA_ERR_TABLE_READONLY);
12855 }
12856
12857 /* Get the transaction associated with the current thd, or create one
12858 if not yet created, and update m_prebuilt->trx */
12859
12860 update_thd(ha_thd());
12861
12862 m_prebuilt->trx->ddl = true;
12863 trx_start_if_not_started(m_prebuilt->trx, true);
12864
12865 dberr_t err = row_mysql_lock_table(m_prebuilt->trx, m_prebuilt->table,
12866 LOCK_X, "truncate table");
12867 if (err == DB_SUCCESS) {
12868 err = row_truncate_table_for_mysql(m_prebuilt->table,
12869 m_prebuilt->trx);
12870 }
12871
12872 switch (err) {
12873 case DB_FORCED_ABORT:
12874 case DB_DEADLOCK:
12875 thd_mark_transaction_to_rollback(m_user_thd, 1);
12876 DBUG_RETURN(HA_ERR_LOCK_DEADLOCK);
12877 case DB_LOCK_TABLE_FULL:
12878 thd_mark_transaction_to_rollback(m_user_thd, 1);
12879 DBUG_RETURN(HA_ERR_LOCK_TABLE_FULL);
12880 case DB_LOCK_WAIT_TIMEOUT:
12881 DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
12882 case DB_TABLESPACE_DELETED:
12883 case DB_TABLESPACE_NOT_FOUND:
12884 ib_senderrf(
12885 m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
12886 (err == DB_TABLESPACE_DELETED ?
12887 ER_TABLESPACE_DISCARDED : ER_TABLESPACE_MISSING),
12888 table->s->table_name.str);
12889 table->status = STATUS_NOT_FOUND;
12890 DBUG_RETURN(HA_ERR_TABLESPACE_MISSING);
12891 default:
12892 table->status = STATUS_NOT_FOUND;
12893 DBUG_RETURN(convert_error_code_to_mysql(
12894 err, m_prebuilt->table->flags,
12895 m_user_thd));
12896 }
12897}
12898
12899/*****************************************************************//**
12900Drops a table from an InnoDB database. Before calling this function,
12901MySQL calls innobase_commit to commit the transaction of the current user.
12902Then the current user cannot have locks set on the table. Drop table
12903operation inside InnoDB will remove all locks any user has on the table
12904inside InnoDB.
12905@return error number */
12906
12907int
12908ha_innobase::delete_table(
12909/*======================*/
12910 const char* name) /*!< in: table name */
12911{
12912 dberr_t err;
12913 THD* thd = ha_thd();
12914 char norm_name[FN_REFLEN];
12915
12916 DBUG_ENTER("ha_innobase::delete_table");
12917
12918 DBUG_EXECUTE_IF(
12919 "test_normalize_table_name_low",
12920 test_normalize_table_name_low();
12921 );
12922 DBUG_EXECUTE_IF(
12923 "test_ut_format_name",
12924 test_ut_format_name();
12925 );
12926
12927 /* Strangely, MySQL passes the table name without the '.frm'
12928 extension, in contrast to ::create */
12929 normalize_table_name(norm_name, name);
12930
12931 if (srv_read_only_mode
12932 || srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) {
12933 DBUG_RETURN(HA_ERR_TABLE_READONLY);
12934 }
12935
12936 trx_t* parent_trx = check_trx_exists(thd);
12937
12938 /* Remove the to-be-dropped table from the list of modified tables
12939 by parent_trx. Otherwise we may end up with an orphaned pointer to
12940 the table object from parent_trx::mod_tables. This could happen in:
12941 SET AUTOCOMMIT=0;
12942 CREATE TABLE t (PRIMARY KEY (a)) ENGINE=INNODB SELECT 1 AS a UNION
12943 ALL SELECT 1 AS a; */
12944 trx_mod_tables_t::const_iterator iter;
12945
12946 for (iter = parent_trx->mod_tables.begin();
12947 iter != parent_trx->mod_tables.end();
12948 ++iter) {
12949
12950 dict_table_t* table_to_drop = iter->first;
12951
12952 if (strcmp(norm_name, table_to_drop->name.m_name) == 0) {
12953 parent_trx->mod_tables.erase(table_to_drop);
12954 break;
12955 }
12956 }
12957
12958 trx_t* trx = innobase_trx_allocate(thd);
12959
12960 ulint name_len = strlen(name);
12961
12962 ut_a(name_len < 1000);
12963
12964 /* Either the transaction is already flagged as a locking transaction
12965 or it hasn't been started yet. */
12966
12967 ut_a(!trx_is_started(trx) || trx->will_lock > 0);
12968
12969 /* We are doing a DDL operation. */
12970 ++trx->will_lock;
12971
12972 /* Drop the table in InnoDB */
12973
12974 err = row_drop_table_for_mysql(
12975 norm_name, trx, thd_sql_command(thd) == SQLCOM_DROP_DB,
12976 false);
12977
12978 if (err == DB_TABLE_NOT_FOUND
12979 && innobase_get_lower_case_table_names() == 1) {
12980 char* is_part = is_partition(norm_name);
12981
12982 if (is_part) {
12983 char par_case_name[FN_REFLEN];
12984
12985#ifndef __WIN__
12986 /* Check for the table using lower
12987 case name, including the partition
12988 separator "P" */
12989 strcpy(par_case_name, norm_name);
12990 innobase_casedn_str(par_case_name);
12991#else
12992 /* On Windows platfrom, check
12993 whether there exists table name in
12994 system table whose name is
12995 not being normalized to lower case */
12996 normalize_table_name_c_low(
12997 par_case_name, name, FALSE);
12998#endif
12999 err = row_drop_table_for_mysql(
13000 par_case_name, trx,
13001 thd_sql_command(thd) == SQLCOM_DROP_DB,
13002 FALSE);
13003 }
13004 }
13005
13006 if (err == DB_TABLE_NOT_FOUND) {
13007 /* Test to drop all tables which matches db/tablename + '#'.
13008 Only partitions can have '#' as non-first character in
13009 the table name!
13010
13011 Temporary table names always start with '#', partitions are
13012 the only 'tables' that can have '#' after the first character
13013 and table name must have length > 0. User tables cannot have
13014 '#' since it would be translated to @0023. Therefor this should
13015 only match partitions. */
13016 uint len = (uint) strlen(norm_name);
13017 ulint num_partitions;
13018 ut_a(len < FN_REFLEN);
13019 norm_name[len] = '#';
13020 norm_name[len + 1] = 0;
13021 err = row_drop_database_for_mysql(norm_name, trx,
13022 &num_partitions);
13023 norm_name[len] = 0;
13024 if (num_partitions == 0
13025 && !row_is_mysql_tmp_table_name(norm_name)) {
13026 table_name_t tbl_name;
13027 tbl_name.m_name = norm_name;
13028 ib::error() << "Table " << tbl_name <<
13029 " does not exist in the InnoDB"
13030 " internal data dictionary though MariaDB is"
13031 " trying to drop it. Have you copied the .frm"
13032 " file of the table to the MariaDB database"
13033 " directory from another database? "
13034 << TROUBLESHOOTING_MSG;
13035 }
13036 if (num_partitions == 0) {
13037 err = DB_TABLE_NOT_FOUND;
13038 }
13039 }
13040
13041 /* TODO: remove this when the conversion tool from ha_partition to
13042 native innodb partitioning is completed */
13043 if (err == DB_TABLE_NOT_FOUND
13044 && innobase_get_lower_case_table_names() == 1) {
13045 char* is_part = is_partition(norm_name);
13046
13047 if (is_part != NULL) {
13048 char par_case_name[FN_REFLEN];
13049
13050#ifndef _WIN32
13051 /* Check for the table using lower
13052 case name, including the partition
13053 separator "P" */
13054 strcpy(par_case_name, norm_name);
13055 innobase_casedn_str(par_case_name);
13056#else
13057 /* On Windows platfrom, check
13058 whether there exists table name in
13059 system table whose name is
13060 not being normalized to lower case */
13061 create_table_info_t::normalize_table_name_low(
13062 par_case_name, name, FALSE);
13063#endif /* _WIN32 */
13064 err = row_drop_table_for_mysql(
13065 par_case_name, trx,
13066 thd_sql_command(thd) == SQLCOM_DROP_DB,
13067 true);
13068 }
13069 }
13070
13071 ut_ad(!srv_read_only_mode);
13072 /* Flush the log to reduce probability that the .frm files and
13073 the InnoDB data dictionary get out-of-sync if the user runs
13074 with innodb_flush_log_at_trx_commit = 0 */
13075
13076 log_buffer_flush_to_disk();
13077
13078 innobase_commit_low(trx);
13079
13080 trx_free(trx);
13081
13082 DBUG_RETURN(convert_error_code_to_mysql(err, 0, NULL));
13083}
13084
13085/** Remove all tables in the named database inside InnoDB.
13086@param[in] hton handlerton from InnoDB
13087@param[in] path Database path; Inside InnoDB the name of the last
13088directory in the path is used as the database name.
13089For example, in 'mysql/data/test' the database name is 'test'. */
13090
13091static
13092void
13093innobase_drop_database(
13094 handlerton* hton,
13095 char* path)
13096{
13097 char* namebuf;
13098
13099 /* Get the transaction associated with the current thd, or create one
13100 if not yet created */
13101
13102 DBUG_ASSERT(hton == innodb_hton_ptr);
13103
13104 if (srv_read_only_mode) {
13105 return;
13106 }
13107
13108 THD* thd = current_thd;
13109
13110 ulint len = 0;
13111 char* ptr = strend(path) - 2;
13112
13113 while (ptr >= path && *ptr != '\\' && *ptr != '/') {
13114 ptr--;
13115 len++;
13116 }
13117
13118 ptr++;
13119 namebuf = (char*) my_malloc(/*PSI_INSTRUMENT_ME,*/ (uint) len + 2, MYF(0));
13120
13121 memcpy(namebuf, ptr, len);
13122 namebuf[len] = '/';
13123 namebuf[len + 1] = '\0';
13124
13125#ifdef _WIN32
13126 innobase_casedn_str(namebuf);
13127#endif /* _WIN32 */
13128
13129 trx_t* trx = innobase_trx_allocate(thd);
13130
13131 /* Either the transaction is already flagged as a locking transaction
13132 or it hasn't been started yet. */
13133
13134 ut_a(!trx_is_started(trx) || trx->will_lock > 0);
13135
13136 /* We are doing a DDL operation. */
13137 ++trx->will_lock;
13138
13139 ulint dummy;
13140
13141 row_drop_database_for_mysql(namebuf, trx, &dummy);
13142
13143 my_free(namebuf);
13144
13145 /* Flush the log to reduce probability that the .frm files and
13146 the InnoDB data dictionary get out-of-sync if the user runs
13147 with innodb_flush_log_at_trx_commit = 0 */
13148
13149 log_buffer_flush_to_disk();
13150
13151 innobase_commit_low(trx);
13152
13153 trx_free(trx);
13154}
13155
13156/*********************************************************************//**
13157Renames an InnoDB table.
13158@return DB_SUCCESS or error code */
13159inline MY_ATTRIBUTE((warn_unused_result))
13160dberr_t
13161innobase_rename_table(
13162/*==================*/
13163 trx_t* trx, /*!< in: transaction */
13164 const char* from, /*!< in: old name of the table */
13165 const char* to) /*!< in: new name of the table */
13166{
13167 dberr_t error;
13168 char norm_to[FN_REFLEN];
13169 char norm_from[FN_REFLEN];
13170
13171 DBUG_ENTER("innobase_rename_table");
13172 DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
13173
13174 ut_ad(!srv_read_only_mode);
13175
13176 normalize_table_name(norm_to, to);
13177 normalize_table_name(norm_from, from);
13178
13179 DEBUG_SYNC_C("innodb_rename_table_ready");
13180
13181 trx_start_if_not_started(trx, true);
13182 ut_ad(trx->will_lock > 0);
13183
13184 /* Serialize data dictionary operations with dictionary mutex:
13185 no deadlocks can occur then in these operations. */
13186
13187 row_mysql_lock_data_dictionary(trx);
13188
13189 error = row_rename_table_for_mysql(norm_from, norm_to, trx, TRUE);
13190
13191 if (error != DB_SUCCESS) {
13192 if (error == DB_TABLE_NOT_FOUND
13193 && innobase_get_lower_case_table_names() == 1) {
13194 char* is_part = is_partition(norm_from);
13195
13196 if (is_part) {
13197 char par_case_name[FN_REFLEN];
13198#ifndef _WIN32
13199 /* Check for the table using lower
13200 case name, including the partition
13201 separator "P" */
13202 strcpy(par_case_name, norm_from);
13203 innobase_casedn_str(par_case_name);
13204#else
13205 /* On Windows platfrom, check
13206 whether there exists table name in
13207 system table whose name is
13208 not being normalized to lower case */
13209 create_table_info_t::normalize_table_name_low(
13210 par_case_name, from, FALSE);
13211#endif /* _WIN32 */
13212 trx_start_if_not_started(trx, true);
13213 error = row_rename_table_for_mysql(
13214 par_case_name, norm_to, trx, TRUE);
13215 }
13216 }
13217
13218 if (error == DB_SUCCESS) {
13219#ifndef _WIN32
13220 sql_print_warning("Rename partition table %s"
13221 " succeeds after converting to lower"
13222 " case. The table may have"
13223 " been moved from a case"
13224 " in-sensitive file system.\n",
13225 norm_from);
13226#else
13227 sql_print_warning("Rename partition table %s"
13228 " succeeds after skipping the step to"
13229 " lower case the table name."
13230 " The table may have been"
13231 " moved from a case sensitive"
13232 " file system.\n",
13233 norm_from);
13234#endif /* _WIN32 */
13235 }
13236 }
13237
13238 row_mysql_unlock_data_dictionary(trx);
13239
13240 /* Flush the log to reduce probability that the .frm
13241 files and the InnoDB data dictionary get out-of-sync
13242 if the user runs with innodb_flush_log_at_trx_commit = 0 */
13243
13244 log_buffer_flush_to_disk();
13245
13246 DBUG_RETURN(error);
13247}
13248
13249/*********************************************************************//**
13250Renames an InnoDB table.
13251@return 0 or error code */
13252
13253int
13254ha_innobase::rename_table(
13255/*======================*/
13256 const char* from, /*!< in: old name of the table */
13257 const char* to) /*!< in: new name of the table */
13258{
13259 THD* thd = ha_thd();
13260
13261 DBUG_ENTER("ha_innobase::rename_table");
13262
13263 if (high_level_read_only) {
13264 ib_senderrf(thd, IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
13265 DBUG_RETURN(HA_ERR_TABLE_READONLY);
13266 }
13267
13268 trx_t* trx = innobase_trx_allocate(thd);
13269
13270 /* We are doing a DDL operation. */
13271 ++trx->will_lock;
13272 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
13273
13274 dberr_t error = innobase_rename_table(trx, from, to);
13275
13276 DEBUG_SYNC(thd, "after_innobase_rename_table");
13277
13278 innobase_commit_low(trx);
13279
13280 trx_free(trx);
13281
13282 if (error == DB_SUCCESS) {
13283 char norm_from[MAX_FULL_NAME_LEN];
13284 char norm_to[MAX_FULL_NAME_LEN];
13285 char errstr[512];
13286 dberr_t ret;
13287
13288 normalize_table_name(norm_from, from);
13289 normalize_table_name(norm_to, to);
13290
13291 ret = dict_stats_rename_table(norm_from, norm_to,
13292 errstr, sizeof(errstr));
13293
13294 if (ret != DB_SUCCESS) {
13295 ib::error() << errstr;
13296
13297 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
13298 ER_LOCK_WAIT_TIMEOUT, errstr);
13299 }
13300 }
13301
13302 /* Add a special case to handle the Duplicated Key error
13303 and return DB_ERROR instead.
13304 This is to avoid a possible SIGSEGV error from mysql error
13305 handling code. Currently, mysql handles the Duplicated Key
13306 error by re-entering the storage layer and getting dup key
13307 info by calling get_dup_key(). This operation requires a valid
13308 table handle ('row_prebuilt_t' structure) which could no
13309 longer be available in the error handling stage. The suggested
13310 solution is to report a 'table exists' error message (since
13311 the dup key error here is due to an existing table whose name
13312 is the one we are trying to rename to) and return the generic
13313 error code. */
13314 if (error == DB_DUPLICATE_KEY) {
13315 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), to);
13316
13317 error = DB_ERROR;
13318 }
13319
13320 DBUG_RETURN(convert_error_code_to_mysql(error, 0, NULL));
13321}
13322
13323/*********************************************************************//**
13324Estimates the number of index records in a range.
13325@return estimated number of rows */
13326
13327ha_rows
13328ha_innobase::records_in_range(
13329/*==========================*/
13330 uint keynr, /*!< in: index number */
13331 key_range *min_key, /*!< in: start key value of the
13332 range, may also be 0 */
13333 key_range *max_key) /*!< in: range end key val, may
13334 also be 0 */
13335{
13336 KEY* key;
13337 dict_index_t* index;
13338 dtuple_t* range_start;
13339 dtuple_t* range_end;
13340 ha_rows n_rows;
13341 page_cur_mode_t mode1;
13342 page_cur_mode_t mode2;
13343 mem_heap_t* heap;
13344
13345 DBUG_ENTER("records_in_range");
13346
13347 ut_a(m_prebuilt->trx == thd_to_trx(ha_thd()));
13348
13349 m_prebuilt->trx->op_info = "estimating records in index range";
13350
13351 active_index = keynr;
13352
13353 key = table->key_info + active_index;
13354
13355 index = innobase_get_index(keynr);
13356
13357 /* There exists possibility of not being able to find requested
13358 index due to inconsistency between MySQL and InoDB dictionary info.
13359 Necessary message should have been printed in innobase_get_index() */
13360 if (!m_prebuilt->table->space) {
13361 n_rows = HA_POS_ERROR;
13362 goto func_exit;
13363 }
13364 if (!index) {
13365 n_rows = HA_POS_ERROR;
13366 goto func_exit;
13367 }
13368 if (index->is_corrupted()) {
13369 n_rows = HA_ERR_INDEX_CORRUPT;
13370 goto func_exit;
13371 }
13372 if (!row_merge_is_index_usable(m_prebuilt->trx, index)) {
13373 n_rows = HA_ERR_TABLE_DEF_CHANGED;
13374 goto func_exit;
13375 }
13376
13377 heap = mem_heap_create(2 * (key->ext_key_parts * sizeof(dfield_t)
13378 + sizeof(dtuple_t)));
13379
13380 range_start = dtuple_create(heap, key->ext_key_parts);
13381 dict_index_copy_types(range_start, index, key->ext_key_parts);
13382
13383 range_end = dtuple_create(heap, key->ext_key_parts);
13384 dict_index_copy_types(range_end, index, key->ext_key_parts);
13385
13386 row_sel_convert_mysql_key_to_innobase(
13387 range_start,
13388 m_prebuilt->srch_key_val1,
13389 m_prebuilt->srch_key_val_len,
13390 index,
13391 (byte*) (min_key ? min_key->key : (const uchar*) 0),
13392 (ulint) (min_key ? min_key->length : 0));
13393
13394 DBUG_ASSERT(min_key
13395 ? range_start->n_fields > 0
13396 : range_start->n_fields == 0);
13397
13398 row_sel_convert_mysql_key_to_innobase(
13399 range_end,
13400 m_prebuilt->srch_key_val2,
13401 m_prebuilt->srch_key_val_len,
13402 index,
13403 (byte*) (max_key ? max_key->key : (const uchar*) 0),
13404 (ulint) (max_key ? max_key->length : 0));
13405
13406 DBUG_ASSERT(max_key
13407 ? range_end->n_fields > 0
13408 : range_end->n_fields == 0);
13409
13410 mode1 = convert_search_mode_to_innobase(
13411 min_key ? min_key->flag : HA_READ_KEY_EXACT);
13412
13413 mode2 = convert_search_mode_to_innobase(
13414 max_key ? max_key->flag : HA_READ_KEY_EXACT);
13415
13416 if (mode1 != PAGE_CUR_UNSUPP && mode2 != PAGE_CUR_UNSUPP) {
13417
13418 if (dict_index_is_spatial(index)) {
13419 /*Only min_key used in spatial index. */
13420 n_rows = rtr_estimate_n_rows_in_range(
13421 index, range_start, mode1);
13422 } else {
13423 n_rows = btr_estimate_n_rows_in_range(
13424 index, range_start, mode1, range_end, mode2);
13425 }
13426 } else {
13427
13428 n_rows = HA_POS_ERROR;
13429 }
13430
13431 mem_heap_free(heap);
13432
13433 DBUG_EXECUTE_IF(
13434 "print_btr_estimate_n_rows_in_range_return_value",
13435 push_warning_printf(
13436 ha_thd(), Sql_condition::WARN_LEVEL_WARN,
13437 ER_NO_DEFAULT,
13438 "btr_estimate_n_rows_in_range(): %lld",
13439 (longlong) n_rows);
13440 );
13441
13442func_exit:
13443
13444 m_prebuilt->trx->op_info = (char*)"";
13445
13446 /* The MySQL optimizer seems to believe an estimate of 0 rows is
13447 always accurate and may return the result 'Empty set' based on that.
13448 The accuracy is not guaranteed, and even if it were, for a locking
13449 read we should anyway perform the search to set the next-key lock.
13450 Add 1 to the value to make sure MySQL does not make the assumption! */
13451
13452 if (n_rows == 0) {
13453 n_rows = 1;
13454 }
13455
13456 DBUG_RETURN((ha_rows) n_rows);
13457}
13458
13459/*********************************************************************//**
13460Gives an UPPER BOUND to the number of rows in a table. This is used in
13461filesort.cc.
13462@return upper bound of rows */
13463
13464ha_rows
13465ha_innobase::estimate_rows_upper_bound()
13466/*====================================*/
13467{
13468 const dict_index_t* index;
13469 ulonglong estimate;
13470 ulonglong local_data_file_length;
13471
13472 DBUG_ENTER("estimate_rows_upper_bound");
13473
13474 /* We do not know if MySQL can call this function before calling
13475 external_lock(). To be safe, update the thd of the current table
13476 handle. */
13477
13478 update_thd(ha_thd());
13479
13480 m_prebuilt->trx->op_info = "calculating upper bound for table rows";
13481
13482 index = dict_table_get_first_index(m_prebuilt->table);
13483
13484 ulint stat_n_leaf_pages = index->stat_n_leaf_pages;
13485
13486 ut_a(stat_n_leaf_pages > 0);
13487
13488 local_data_file_length = ulonglong(stat_n_leaf_pages)
13489 << srv_page_size_shift;
13490
13491 /* Calculate a minimum length for a clustered index record and from
13492 that an upper bound for the number of rows. Since we only calculate
13493 new statistics in row0mysql.cc when a table has grown by a threshold
13494 factor, we must add a safety factor 2 in front of the formula below. */
13495
13496 estimate = 2 * local_data_file_length
13497 / dict_index_calc_min_rec_len(index);
13498
13499 m_prebuilt->trx->op_info = "";
13500
13501 /* Set num_rows less than MERGEBUFF to simulate the case where we do
13502 not have enough space to merge the externally sorted file blocks. */
13503 DBUG_EXECUTE_IF("set_num_rows_lt_MERGEBUFF",
13504 estimate = 2;
13505 DBUG_SET("-d,set_num_rows_lt_MERGEBUFF");
13506 );
13507
13508 DBUG_RETURN((ha_rows) estimate);
13509}
13510
13511/*********************************************************************//**
13512How many seeks it will take to read through the table. This is to be
13513comparable to the number returned by records_in_range so that we can
13514decide if we should scan the table or use keys.
13515@return estimated time measured in disk seeks */
13516
13517double
13518ha_innobase::scan_time()
13519/*====================*/
13520{
13521 /* Since MySQL seems to favor table scans too much over index
13522 searches, we pretend that a sequential read takes the same time
13523 as a random disk read, that is, we do not divide the following
13524 by 10, which would be physically realistic. */
13525
13526 /* The locking below is disabled for performance reasons. Without
13527 it we could end up returning uninitialized value to the caller,
13528 which in the worst case could make some query plan go bogus or
13529 issue a Valgrind warning. */
13530 if (m_prebuilt == NULL) {
13531 /* In case of derived table, Optimizer will try to fetch stat
13532 for table even before table is create or open. In such
13533 cases return default value of 1.
13534 TODO: This will be further improved to return some approximate
13535 estimate but that would also needs pre-population of stats
13536 structure. As of now approach is in sync with MyISAM. */
13537 return(ulonglong2double(stats.data_file_length) / IO_SIZE + 2);
13538 }
13539
13540 ulint stat_clustered_index_size;
13541
13542 ut_a(m_prebuilt->table->stat_initialized);
13543
13544 stat_clustered_index_size =
13545 m_prebuilt->table->stat_clustered_index_size;
13546
13547 return((double) stat_clustered_index_size);
13548}
13549
13550/******************************************************************//**
13551Calculate the time it takes to read a set of ranges through an index
13552This enables us to optimise reads for clustered indexes.
13553@return estimated time measured in disk seeks */
13554
13555double
13556ha_innobase::read_time(
13557/*===================*/
13558 uint index, /*!< in: key number */
13559 uint ranges, /*!< in: how many ranges */
13560 ha_rows rows) /*!< in: estimated number of rows in the ranges */
13561{
13562 ha_rows total_rows;
13563
13564 if (index != table->s->primary_key) {
13565 /* Not clustered */
13566 return(handler::read_time(index, ranges, rows));
13567 }
13568
13569 /* Assume that the read time is proportional to the scan time for all
13570 rows + at most one seek per range. */
13571
13572 double time_for_scan = scan_time();
13573
13574 if ((total_rows = estimate_rows_upper_bound()) < rows) {
13575
13576 return(time_for_scan);
13577 }
13578
13579 return(ranges + (double) rows / (double) total_rows * time_for_scan);
13580}
13581
13582/** Update the system variable with the given value of the InnoDB
13583buffer pool size.
13584@param[in] buf_pool_size given value of buffer pool size.*/
13585void
13586innodb_set_buf_pool_size(ulonglong buf_pool_size)
13587{
13588 innobase_buffer_pool_size = buf_pool_size;
13589}
13590
13591/*********************************************************************//**
13592Calculates the key number used inside MySQL for an Innobase index. We will
13593first check the "index translation table" for a match of the index to get
13594the index number. If there does not exist an "index translation table",
13595or not able to find the index in the translation table, then we will fall back
13596to the traditional way of looping through dict_index_t list to find a
13597match. In this case, we have to take into account if we generated a
13598default clustered index for the table
13599@return the key number used inside MySQL */
13600static
13601unsigned
13602innobase_get_mysql_key_number_for_index(
13603/*====================================*/
13604 INNOBASE_SHARE* share, /*!< in: share structure for index
13605 translation table. */
13606 const TABLE* table, /*!< in: table in MySQL data
13607 dictionary */
13608 dict_table_t* ib_table,/*!< in: table in InnoDB data
13609 dictionary */
13610 const dict_index_t* index) /*!< in: index */
13611{
13612 const dict_index_t* ind;
13613 unsigned int i;
13614
13615 /* If index does not belong to the table object of share structure
13616 (ib_table comes from the share structure) search the index->table
13617 object instead */
13618 if (index->table != ib_table) {
13619 i = 0;
13620 ind = dict_table_get_first_index(index->table);
13621
13622 while (index != ind) {
13623 ind = dict_table_get_next_index(ind);
13624 i++;
13625 }
13626
13627 if (dict_index_is_auto_gen_clust(index)) {
13628 ut_a(i > 0);
13629 i--;
13630 }
13631
13632 return(i);
13633 }
13634
13635 /* If index translation table exists, we will first check
13636 the index through index translation table for a match. */
13637 if (share->idx_trans_tbl.index_mapping != NULL) {
13638 for (i = 0; i < share->idx_trans_tbl.index_count; i++) {
13639 if (share->idx_trans_tbl.index_mapping[i] == index) {
13640 return(i);
13641 }
13642 }
13643
13644 /* Print an error message if we cannot find the index
13645 in the "index translation table". */
13646 if (index->is_committed()) {
13647 sql_print_error("Cannot find index %s in InnoDB index"
13648 " translation table.", index->name());
13649 }
13650 }
13651
13652 /* If we do not have an "index translation table", or not able
13653 to find the index in the translation table, we'll directly find
13654 matching index with information from mysql TABLE structure and
13655 InnoDB dict_index_t list */
13656 for (i = 0; i < table->s->keys; i++) {
13657 ind = dict_table_get_index_on_name(
13658 ib_table, table->key_info[i].name.str);
13659
13660 if (index == ind) {
13661 return(i);
13662 }
13663 }
13664
13665 /* Loop through each index of the table and lock them */
13666 for (ind = dict_table_get_first_index(ib_table);
13667 ind != NULL;
13668 ind = dict_table_get_next_index(ind)) {
13669 if (index == ind) {
13670 /* Temp index is internal to InnoDB, that is
13671 not present in the MySQL index list, so no
13672 need to print such mismatch warning. */
13673 if (index->is_committed()) {
13674 sql_print_warning(
13675 "Found index %s in InnoDB index list"
13676 " but not its MariaDB index number."
13677 " It could be an InnoDB internal"
13678 " index.",
13679 index->name());
13680 }
13681 return(~0U);
13682 }
13683 }
13684
13685 ut_error;
13686
13687 return(~0U);
13688}
13689
13690/*********************************************************************//**
13691Calculate Record Per Key value. Need to exclude the NULL value if
13692innodb_stats_method is set to "nulls_ignored"
13693@return estimated record per key value */
13694rec_per_key_t
13695innodb_rec_per_key(
13696/*===============*/
13697 dict_index_t* index, /*!< in: dict_index_t structure */
13698 ulint i, /*!< in: the column we are
13699 calculating rec per key */
13700 ha_rows records) /*!< in: estimated total records */
13701{
13702 rec_per_key_t rec_per_key;
13703 ib_uint64_t n_diff;
13704
13705 ut_a(index->table->stat_initialized);
13706
13707 ut_ad(i < dict_index_get_n_unique(index));
13708 ut_ad(!dict_index_is_spatial(index));
13709
13710 if (records == 0) {
13711 /* "Records per key" is meaningless for empty tables.
13712 Return 1.0 because that is most convenient to the Optimizer. */
13713 return(1.0);
13714 }
13715
13716 n_diff = index->stat_n_diff_key_vals[i];
13717
13718 if (n_diff == 0) {
13719
13720 rec_per_key = static_cast<rec_per_key_t>(records);
13721 } else if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED) {
13722 ib_uint64_t n_null;
13723 ib_uint64_t n_non_null;
13724
13725 n_non_null = index->stat_n_non_null_key_vals[i];
13726
13727 /* In theory, index->stat_n_non_null_key_vals[i]
13728 should always be less than the number of records.
13729 Since this is statistics value, the value could
13730 have slight discrepancy. But we will make sure
13731 the number of null values is not a negative number. */
13732 if (records < n_non_null) {
13733 n_null = 0;
13734 } else {
13735 n_null = records - n_non_null;
13736 }
13737
13738 /* If the number of NULL values is the same as or
13739 large than that of the distinct values, we could
13740 consider that the table consists mostly of NULL value.
13741 Set rec_per_key to 1. */
13742 if (n_diff <= n_null) {
13743 rec_per_key = 1.0;
13744 } else {
13745 /* Need to exclude rows with NULL values from
13746 rec_per_key calculation */
13747 rec_per_key
13748 = static_cast<rec_per_key_t>(records - n_null)
13749 / (n_diff - n_null);
13750 }
13751 } else {
13752 DEBUG_SYNC_C("after_checking_for_0");
13753 rec_per_key = static_cast<rec_per_key_t>(records) / n_diff;
13754 }
13755
13756 if (rec_per_key < 1.0) {
13757 /* Values below 1.0 are meaningless and must be due to the
13758 stats being imprecise. */
13759 rec_per_key = 1.0;
13760 }
13761
13762 return(rec_per_key);
13763}
13764
13765/** Calculate how many KiB of new data we will be able to insert to the
13766tablespace without running out of space. Start with a space object that has
13767been acquired by the caller who holds it for the calculation,
13768@param[in] space tablespace object from fil_space_acquire()
13769@return available space in KiB */
13770static uintmax_t
13771fsp_get_available_space_in_free_extents(const fil_space_t& space)
13772{
13773 ulint size_in_header = space.size_in_header;
13774 if (size_in_header < FSP_EXTENT_SIZE) {
13775 return 0; /* TODO: count free frag pages and
13776 return a value based on that */
13777 }
13778
13779 /* Below we play safe when counting free extents above the free limit:
13780 some of them will contain extent descriptor pages, and therefore
13781 will not be free extents */
13782 ut_ad(size_in_header >= space.free_limit);
13783 ulint n_free_up =
13784 (size_in_header - space.free_limit) / FSP_EXTENT_SIZE;
13785
13786 const ulint size = page_size_t(space.flags).physical();
13787 if (n_free_up > 0) {
13788 n_free_up--;
13789 n_free_up -= n_free_up / (size / FSP_EXTENT_SIZE);
13790 }
13791
13792 /* We reserve 1 extent + 0.5 % of the space size to undo logs
13793 and 1 extent + 0.5 % to cleaning operations; NOTE: this source
13794 code is duplicated in the function above! */
13795
13796 ulint reserve = 2 + ((size_in_header / FSP_EXTENT_SIZE) * 2) / 200;
13797 ulint n_free = space.free_len + n_free_up;
13798
13799 if (reserve > n_free) {
13800 return(0);
13801 }
13802
13803 return(static_cast<uintmax_t>(n_free - reserve)
13804 * FSP_EXTENT_SIZE * (size / 1024));
13805}
13806
13807/*********************************************************************//**
13808Returns statistics information of the table to the MySQL interpreter,
13809in various fields of the handle object.
13810@return HA_ERR_* error code or 0 */
13811
13812int
13813ha_innobase::info_low(
13814/*==================*/
13815 uint flag, /*!< in: what information is requested */
13816 bool is_analyze)
13817{
13818 dict_table_t* ib_table;
13819 ib_uint64_t n_rows;
13820 char path[FN_REFLEN];
13821 os_file_stat_t stat_info;
13822
13823 DBUG_ENTER("info");
13824
13825 DEBUG_SYNC_C("ha_innobase_info_low");
13826
13827 /* If we are forcing recovery at a high level, we will suppress
13828 statistics calculation on tables, because that may crash the
13829 server if an index is badly corrupted. */
13830
13831 /* We do not know if MySQL can call this function before calling
13832 external_lock(). To be safe, update the thd of the current table
13833 handle. */
13834
13835 update_thd(ha_thd());
13836
13837 m_prebuilt->trx->op_info = "returning various info to MariaDB";
13838
13839 ib_table = m_prebuilt->table;
13840 ut_ad(ib_table->n_ref_count > 0);
13841
13842 if (flag & HA_STATUS_TIME) {
13843 if (is_analyze || innobase_stats_on_metadata) {
13844
13845 dict_stats_upd_option_t opt;
13846 dberr_t ret;
13847
13848 m_prebuilt->trx->op_info = "updating table statistics";
13849
13850 if (dict_stats_is_persistent_enabled(ib_table)) {
13851
13852 if (is_analyze) {
13853 opt = DICT_STATS_RECALC_PERSISTENT;
13854 } else {
13855 /* This is e.g. 'SHOW INDEXES', fetch
13856 the persistent stats from disk. */
13857 opt = DICT_STATS_FETCH_ONLY_IF_NOT_IN_MEMORY;
13858 }
13859 } else {
13860 opt = DICT_STATS_RECALC_TRANSIENT;
13861 }
13862
13863 ut_ad(!mutex_own(&dict_sys->mutex));
13864 ret = dict_stats_update(ib_table, opt);
13865
13866 if (ret != DB_SUCCESS) {
13867 m_prebuilt->trx->op_info = "";
13868 DBUG_RETURN(HA_ERR_GENERIC);
13869 }
13870
13871 m_prebuilt->trx->op_info =
13872 "returning various info to MariaDB";
13873 }
13874
13875
13876 stats.update_time = (ulong) ib_table->update_time;
13877 }
13878
13879 if (flag & HA_STATUS_VARIABLE) {
13880
13881 ulint stat_clustered_index_size;
13882 ulint stat_sum_of_other_index_sizes;
13883
13884 if (!(flag & HA_STATUS_NO_LOCK)) {
13885 dict_table_stats_lock(ib_table, RW_S_LATCH);
13886 }
13887
13888 ut_a(ib_table->stat_initialized);
13889
13890 n_rows = ib_table->stat_n_rows;
13891
13892 stat_clustered_index_size
13893 = ib_table->stat_clustered_index_size;
13894
13895 stat_sum_of_other_index_sizes
13896 = ib_table->stat_sum_of_other_index_sizes;
13897
13898 if (!(flag & HA_STATUS_NO_LOCK)) {
13899 dict_table_stats_unlock(ib_table, RW_S_LATCH);
13900 }
13901
13902 /*
13903 The MySQL optimizer seems to assume in a left join that n_rows
13904 is an accurate estimate if it is zero. Of course, it is not,
13905 since we do not have any locks on the rows yet at this phase.
13906 Since SHOW TABLE STATUS seems to call this function with the
13907 HA_STATUS_TIME flag set, while the left join optimizer does not
13908 set that flag, we add one to a zero value if the flag is not
13909 set. That way SHOW TABLE STATUS will show the best estimate,
13910 while the optimizer never sees the table empty. */
13911
13912 if (n_rows == 0 && !(flag & (HA_STATUS_TIME | HA_STATUS_OPEN))) {
13913 n_rows++;
13914 }
13915
13916 /* Fix bug#40386: Not flushing query cache after truncate.
13917 n_rows can not be 0 unless the table is empty, set to 1
13918 instead. The original problem of bug#29507 is actually
13919 fixed in the server code. */
13920 if (thd_sql_command(m_user_thd) == SQLCOM_TRUNCATE) {
13921
13922 n_rows = 1;
13923
13924 /* We need to reset the m_prebuilt value too, otherwise
13925 checks for values greater than the last value written
13926 to the table will fail and the autoinc counter will
13927 not be updated. This will force write_row() into
13928 attempting an update of the table's AUTOINC counter. */
13929
13930 m_prebuilt->autoinc_last_value = 0;
13931 }
13932
13933 stats.records = (ha_rows) n_rows;
13934 stats.deleted = 0;
13935 if (fil_space_t* space = ib_table->space) {
13936 const ulint size = page_size_t(space->flags)
13937 .physical();
13938 stats.data_file_length
13939 = ulonglong(stat_clustered_index_size)
13940 * size;
13941 stats.index_file_length
13942 = ulonglong(stat_sum_of_other_index_sizes)
13943 * size;
13944 stats.delete_length = 1024
13945 * fsp_get_available_space_in_free_extents(
13946 *space);
13947 }
13948 stats.check_time = 0;
13949 stats.mrr_length_per_rec= (uint)ref_length + 8; // 8 = max(sizeof(void *));
13950
13951 if (stats.records == 0) {
13952 stats.mean_rec_length = 0;
13953 } else {
13954 stats.mean_rec_length = (ulong)
13955 (stats.data_file_length / stats.records);
13956 }
13957 }
13958
13959 if (flag & HA_STATUS_CONST) {
13960 ulong i;
13961 /* Verify the number of index in InnoDB and MySQL
13962 matches up. If m_prebuilt->clust_index_was_generated
13963 holds, InnoDB defines GEN_CLUST_INDEX internally */
13964 ulint num_innodb_index = UT_LIST_GET_LEN(ib_table->indexes)
13965 - m_prebuilt->clust_index_was_generated;
13966 if (table->s->keys < num_innodb_index) {
13967 /* If there are too many indexes defined
13968 inside InnoDB, ignore those that are being
13969 created, because MySQL will only consider
13970 the fully built indexes here. */
13971
13972 for (const dict_index_t* index
13973 = UT_LIST_GET_FIRST(ib_table->indexes);
13974 index != NULL;
13975 index = UT_LIST_GET_NEXT(indexes, index)) {
13976
13977 /* First, online index creation is
13978 completed inside InnoDB, and then
13979 MySQL attempts to upgrade the
13980 meta-data lock so that it can rebuild
13981 the .frm file. If we get here in that
13982 time frame, dict_index_is_online_ddl()
13983 would not hold and the index would
13984 still not be included in TABLE_SHARE. */
13985 if (!index->is_committed()) {
13986 num_innodb_index--;
13987 }
13988 }
13989
13990 if (table->s->keys < num_innodb_index
13991 && innobase_fts_check_doc_id_index(
13992 ib_table, NULL, NULL)
13993 == FTS_EXIST_DOC_ID_INDEX) {
13994 num_innodb_index--;
13995 }
13996 }
13997
13998 if (table->s->keys != num_innodb_index) {
13999 ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
14000 ib_push_frm_error(m_user_thd, ib_table, table, num_innodb_index, true);
14001 }
14002
14003 if (!(flag & HA_STATUS_NO_LOCK)) {
14004 dict_table_stats_lock(ib_table, RW_S_LATCH);
14005 }
14006
14007 ut_a(ib_table->stat_initialized);
14008
14009 for (i = 0; i < table->s->keys; i++) {
14010 ulong j;
14011
14012 /* We could get index quickly through internal
14013 index mapping with the index translation table.
14014 The identity of index (match up index name with
14015 that of table->key_info[i]) is already verified in
14016 innobase_get_index(). */
14017 dict_index_t* index = innobase_get_index(i);
14018
14019 if (index == NULL) {
14020 ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
14021 ib_push_frm_error(m_user_thd, ib_table, table, num_innodb_index, true);
14022 break;
14023 }
14024
14025 KEY* key = &table->key_info[i];
14026
14027 for (j = 0; j < key->ext_key_parts; j++) {
14028
14029 if ((key->flags & HA_FULLTEXT)
14030 || (key->flags & HA_SPATIAL)) {
14031
14032 /* The record per key does not apply to
14033 FTS or Spatial indexes. */
14034 /*
14035 key->rec_per_key[j] = 1;
14036 key->set_records_per_key(j, 1.0);
14037 */
14038 continue;
14039 }
14040
14041 if (j + 1 > index->n_uniq) {
14042 sql_print_error(
14043 "Index %s of %s has %u columns"
14044 " unique inside InnoDB, but "
14045 "MySQL is asking statistics for"
14046 " %lu columns. Have you mixed "
14047 "up .frm files from different "
14048 " installations? %s",
14049 index->name(),
14050 ib_table->name.m_name,
14051 index->n_uniq, j + 1,
14052 TROUBLESHOOTING_MSG);
14053 break;
14054 }
14055
14056 /* innodb_rec_per_key() will use
14057 index->stat_n_diff_key_vals[] and the value we
14058 pass index->table->stat_n_rows. Both are
14059 calculated by ANALYZE and by the background
14060 stats gathering thread (which kicks in when too
14061 much of the table has been changed). In
14062 addition table->stat_n_rows is adjusted with
14063 each DML (e.g. ++ on row insert). Those
14064 adjustments are not MVCC'ed and not even
14065 reversed on rollback. So,
14066 index->stat_n_diff_key_vals[] and
14067 index->table->stat_n_rows could have been
14068 calculated at different time. This is
14069 acceptable. */
14070
14071 ulong rec_per_key_int = static_cast<ulong>(
14072 innodb_rec_per_key(index, j,
14073 stats.records));
14074
14075 /* Since MySQL seems to favor table scans
14076 too much over index searches, we pretend
14077 index selectivity is 2 times better than
14078 our estimate: */
14079
14080 rec_per_key_int = rec_per_key_int / 2;
14081
14082 if (rec_per_key_int == 0) {
14083 rec_per_key_int = 1;
14084 }
14085
14086 key->rec_per_key[j] = rec_per_key_int;
14087 }
14088 }
14089
14090 if (!(flag & HA_STATUS_NO_LOCK)) {
14091 dict_table_stats_unlock(ib_table, RW_S_LATCH);
14092 }
14093
14094 snprintf(path, sizeof(path), "%s/%s%s",
14095 mysql_data_home, table->s->normalized_path.str,
14096 reg_ext);
14097
14098 unpack_filename(path,path);
14099
14100 /* Note that we do not know the access time of the table,
14101 nor the CHECK TABLE time, nor the UPDATE or INSERT time. */
14102
14103 if (os_file_get_status(
14104 path, &stat_info, false,
14105 srv_read_only_mode) == DB_SUCCESS) {
14106 stats.create_time = (ulong) stat_info.ctime;
14107 }
14108 }
14109
14110 if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
14111
14112 goto func_exit;
14113
14114 } else if (flag & HA_STATUS_ERRKEY) {
14115 const dict_index_t* err_index;
14116
14117 ut_a(m_prebuilt->trx);
14118 ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
14119
14120 err_index = trx_get_error_info(m_prebuilt->trx);
14121
14122 if (err_index) {
14123 errkey = innobase_get_mysql_key_number_for_index(
14124 m_share, table, ib_table, err_index);
14125 } else {
14126 errkey = (unsigned int) (
14127 (m_prebuilt->trx->error_key_num
14128 == ULINT_UNDEFINED)
14129 ? ~0U
14130 : m_prebuilt->trx->error_key_num);
14131 }
14132 }
14133
14134 if ((flag & HA_STATUS_AUTO) && table->found_next_number_field) {
14135 stats.auto_increment_value = innobase_peek_autoinc();
14136 }
14137
14138func_exit:
14139 m_prebuilt->trx->op_info = (char*)"";
14140
14141 DBUG_RETURN(0);
14142}
14143
14144/*********************************************************************//**
14145Returns statistics information of the table to the MySQL interpreter,
14146in various fields of the handle object.
14147@return HA_ERR_* error code or 0 */
14148
14149int
14150ha_innobase::info(
14151/*==============*/
14152 uint flag) /*!< in: what information is requested */
14153{
14154 return(info_low(flag, false /* not ANALYZE */));
14155}
14156
14157/*
14158Updates index cardinalities of the table, based on random dives into
14159each index tree. This does NOT calculate exact statistics on the table.
14160@return HA_ADMIN_* error code or HA_ADMIN_OK */
14161
14162int
14163ha_innobase::analyze(THD*, HA_CHECK_OPT*)
14164{
14165 /* Simply call info_low() with all the flags
14166 and request recalculation of the statistics */
14167 int ret = info_low(
14168 HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE,
14169 true /* this is ANALYZE */);
14170
14171 if (ret != 0) {
14172 return(HA_ADMIN_FAILED);
14173 }
14174
14175 return(HA_ADMIN_OK);
14176}
14177
14178/*****************************************************************//**
14179Defragment table.
14180@return error number */
14181UNIV_INTERN
14182int
14183ha_innobase::defragment_table(
14184/*==========================*/
14185 const char* name, /*!< in: table name */
14186 const char* index_name, /*!< in: index name */
14187 bool async) /*!< in: whether to wait until finish */
14188{
14189 char norm_name[FN_REFLEN];
14190 dict_table_t* table = NULL;
14191 dict_index_t* index = NULL;
14192 ibool one_index = (index_name != 0);
14193 int ret = 0;
14194 dberr_t err = DB_SUCCESS;
14195
14196 if (!srv_defragment) {
14197 return ER_FEATURE_DISABLED;
14198 }
14199
14200 normalize_table_name(norm_name, name);
14201
14202 table = dict_table_open_on_name(norm_name, FALSE,
14203 FALSE, DICT_ERR_IGNORE_NONE);
14204
14205 for (index = dict_table_get_first_index(table); index;
14206 index = dict_table_get_next_index(index)) {
14207
14208 if (index->is_corrupted()) {
14209 continue;
14210 }
14211
14212 if (dict_index_is_spatial(index)) {
14213 /* Do not try to defragment spatial indexes,
14214 because doing it properly would require
14215 appropriate logic around the SSN (split
14216 sequence number). */
14217 continue;
14218 }
14219
14220 if (index->page == FIL_NULL) {
14221 /* Do not defragment auxiliary tables related
14222 to FULLTEXT INDEX. */
14223 ut_ad(index->type & DICT_FTS);
14224 continue;
14225 }
14226
14227 if (one_index && strcasecmp(index_name, index->name) != 0) {
14228 continue;
14229 }
14230
14231 if (btr_defragment_find_index(index)) {
14232 // We borrow this error code. When the same index is
14233 // already in the defragmentation queue, issue another
14234 // defragmentation only introduces overhead. We return
14235 // an error here to let the user know this is not
14236 // necessary. Note that this will fail a query that's
14237 // trying to defragment a full table if one of the
14238 // indicies in that table is already in defragmentation.
14239 // We choose this behavior so user is aware of this
14240 // rather than silently defragment other indicies of
14241 // that table.
14242 ret = ER_SP_ALREADY_EXISTS;
14243 break;
14244 }
14245
14246 os_event_t event = btr_defragment_add_index(index, async, &err);
14247
14248 if (err != DB_SUCCESS) {
14249 push_warning_printf(
14250 current_thd,
14251 Sql_condition::WARN_LEVEL_WARN,
14252 ER_NO_SUCH_TABLE,
14253 "Table %s is encrypted but encryption service or"
14254 " used key_id is not available. "
14255 " Can't continue checking table.",
14256 index->table->name.m_name);
14257
14258 ret = convert_error_code_to_mysql(err, 0, current_thd);
14259 break;
14260 }
14261
14262 if (!async && event) {
14263 while(os_event_wait_time(event, 1000000)) {
14264 if (thd_killed(current_thd)) {
14265 btr_defragment_remove_index(index);
14266 ret = ER_QUERY_INTERRUPTED;
14267 break;
14268 }
14269 }
14270 os_event_destroy(event);
14271 }
14272
14273 if (ret) {
14274 break;
14275 }
14276
14277 if (one_index) {
14278 one_index = FALSE;
14279 break;
14280 }
14281 }
14282
14283 dict_table_close(table, FALSE, FALSE);
14284
14285 if (ret == 0 && one_index) {
14286 ret = ER_NO_SUCH_INDEX;
14287 }
14288
14289 return ret;
14290}
14291
14292/**********************************************************************//**
14293This is mapped to "ALTER TABLE tablename ENGINE=InnoDB", which rebuilds
14294the table in MySQL. */
14295
14296int
14297ha_innobase::optimize(
14298/*==================*/
14299 THD* thd, /*!< in: connection thread handle */
14300 HA_CHECK_OPT*)
14301{
14302
14303 /* FTS-FIXME: Since MySQL doesn't support engine-specific commands,
14304 we have to hijack some existing command in order to be able to test
14305 the new admin commands added in InnoDB's FTS support. For now, we
14306 use MySQL's OPTIMIZE command, normally mapped to ALTER TABLE in
14307 InnoDB (so it recreates the table anew), and map it to OPTIMIZE.
14308
14309 This works OK otherwise, but MySQL locks the entire table during
14310 calls to OPTIMIZE, which is undesirable. */
14311
14312 if (srv_defragment) {
14313 int err= defragment_table(
14314 m_prebuilt->table->name.m_name, NULL, false);
14315
14316 if (err == 0) {
14317 return (HA_ADMIN_OK);
14318 } else {
14319 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
14320 uint(err),
14321 "InnoDB: Cannot defragment table %s: returned error code %d\n",
14322 m_prebuilt->table->name, err);
14323
14324 if (err == ER_SP_ALREADY_EXISTS) {
14325 return (HA_ADMIN_OK);
14326 } else {
14327 return (HA_ADMIN_TRY_ALTER);
14328 }
14329 }
14330 }
14331
14332 if (innodb_optimize_fulltext_only) {
14333 if (m_prebuilt->table->fts && m_prebuilt->table->fts->cache
14334 && m_prebuilt->table->space) {
14335 fts_sync_table(m_prebuilt->table, false, true, false);
14336 fts_optimize_table(m_prebuilt->table);
14337 }
14338 return(HA_ADMIN_OK);
14339 } else {
14340
14341 return(HA_ADMIN_TRY_ALTER);
14342 }
14343}
14344
14345/*******************************************************************//**
14346Tries to check that an InnoDB table is not corrupted. If corruption is
14347noticed, prints to stderr information about it. In case of corruption
14348may also assert a failure and crash the server.
14349@return HA_ADMIN_CORRUPT or HA_ADMIN_OK */
14350
14351int
14352ha_innobase::check(
14353/*===============*/
14354 THD* thd, /*!< in: user thread handle */
14355 HA_CHECK_OPT* check_opt) /*!< in: check options */
14356{
14357 dict_index_t* index;
14358 ulint n_rows;
14359 ulint n_rows_in_table = ULINT_UNDEFINED;
14360 bool is_ok = true;
14361 ulint old_isolation_level;
14362 dberr_t ret;
14363
14364 DBUG_ENTER("ha_innobase::check");
14365 DBUG_ASSERT(thd == ha_thd());
14366 ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
14367 ut_a(m_prebuilt->trx == thd_to_trx(thd));
14368
14369 if (m_prebuilt->mysql_template == NULL) {
14370 /* Build the template; we will use a dummy template
14371 in index scans done in checking */
14372
14373 build_template(true);
14374 }
14375
14376 if (!m_prebuilt->table->space) {
14377
14378 ib_senderrf(
14379 thd,
14380 IB_LOG_LEVEL_ERROR,
14381 ER_TABLESPACE_DISCARDED,
14382 table->s->table_name.str);
14383
14384 DBUG_RETURN(HA_ADMIN_CORRUPT);
14385
14386 } else if (!m_prebuilt->table->is_readable() &&
14387 !m_prebuilt->table->space) {
14388
14389 ib_senderrf(
14390 thd, IB_LOG_LEVEL_ERROR,
14391 ER_TABLESPACE_MISSING,
14392 table->s->table_name.str);
14393
14394 DBUG_RETURN(HA_ADMIN_CORRUPT);
14395 }
14396
14397 m_prebuilt->trx->op_info = "checking table";
14398
14399 if (m_prebuilt->table->corrupted) {
14400 /* If some previous operation has marked the table as
14401 corrupted in memory, and has not propagated such to
14402 clustered index, we will do so here */
14403 index = dict_table_get_first_index(m_prebuilt->table);
14404
14405 if (!index->is_corrupted()) {
14406 dict_set_corrupted(
14407 index, m_prebuilt->trx, "CHECK TABLE");
14408 }
14409
14410 push_warning_printf(m_user_thd,
14411 Sql_condition::WARN_LEVEL_WARN,
14412 HA_ERR_INDEX_CORRUPT,
14413 "InnoDB: Index %s is marked as"
14414 " corrupted",
14415 index->name());
14416
14417 /* Now that the table is already marked as corrupted,
14418 there is no need to check any index of this table */
14419 m_prebuilt->trx->op_info = "";
14420
14421 DBUG_RETURN(HA_ADMIN_CORRUPT);
14422 }
14423
14424 old_isolation_level = m_prebuilt->trx->isolation_level;
14425
14426 /* We must run the index record counts at an isolation level
14427 >= READ COMMITTED, because a dirty read can see a wrong number
14428 of records in some index; to play safe, we use always
14429 REPEATABLE READ here */
14430 m_prebuilt->trx->isolation_level = TRX_ISO_REPEATABLE_READ;
14431
14432 ut_ad(!m_prebuilt->table->corrupted);
14433
14434 for (index = dict_table_get_first_index(m_prebuilt->table);
14435 index != NULL;
14436 index = dict_table_get_next_index(index)) {
14437 /* If this is an index being created or dropped, skip */
14438 if (!index->is_committed()) {
14439 continue;
14440 }
14441
14442 if (!(check_opt->flags & T_QUICK)
14443 && !index->is_corrupted()) {
14444 /* Enlarge the fatal lock wait timeout during
14445 CHECK TABLE. */
14446 my_atomic_addlong(
14447 &srv_fatal_semaphore_wait_threshold,
14448 SRV_SEMAPHORE_WAIT_EXTENSION);
14449
14450 dberr_t err = btr_validate_index(
14451 index, m_prebuilt->trx, false);
14452
14453 /* Restore the fatal lock wait timeout after
14454 CHECK TABLE. */
14455 my_atomic_addlong(
14456 &srv_fatal_semaphore_wait_threshold,
14457 -SRV_SEMAPHORE_WAIT_EXTENSION);
14458
14459 if (err != DB_SUCCESS) {
14460 is_ok = false;
14461
14462 if (err == DB_DECRYPTION_FAILED) {
14463 push_warning_printf(
14464 thd,
14465 Sql_condition::WARN_LEVEL_WARN,
14466 ER_NO_SUCH_TABLE,
14467 "Table %s is encrypted but encryption service or"
14468 " used key_id is not available. "
14469 " Can't continue checking table.",
14470 index->table->name.m_name);
14471 } else {
14472 push_warning_printf(
14473 thd,
14474 Sql_condition::WARN_LEVEL_WARN,
14475 ER_NOT_KEYFILE,
14476 "InnoDB: The B-tree of"
14477 " index %s is corrupted.",
14478 index->name());
14479 }
14480
14481 continue;
14482 }
14483 }
14484
14485 /* Instead of invoking change_active_index(), set up
14486 a dummy template for non-locking reads, disabling
14487 access to the clustered index. */
14488 m_prebuilt->index = index;
14489
14490 m_prebuilt->index_usable = row_merge_is_index_usable(
14491 m_prebuilt->trx, m_prebuilt->index);
14492
14493 DBUG_EXECUTE_IF(
14494 "dict_set_index_corrupted",
14495 if (!index->is_primary()) {
14496 m_prebuilt->index_usable = FALSE;
14497 // row_mysql_lock_data_dictionary(m_prebuilt->trx);
14498 dict_set_corrupted(index, m_prebuilt->trx, "dict_set_index_corrupted");
14499 // row_mysql_unlock_data_dictionary(m_prebuilt->trx);
14500 });
14501
14502 if (UNIV_UNLIKELY(!m_prebuilt->index_usable)) {
14503 if (index->is_corrupted()) {
14504 push_warning_printf(
14505 m_user_thd,
14506 Sql_condition::WARN_LEVEL_WARN,
14507 HA_ERR_INDEX_CORRUPT,
14508 "InnoDB: Index %s is marked as"
14509 " corrupted",
14510 index->name());
14511 is_ok = false;
14512 } else {
14513 push_warning_printf(
14514 m_user_thd,
14515 Sql_condition::WARN_LEVEL_WARN,
14516 HA_ERR_TABLE_DEF_CHANGED,
14517 "InnoDB: Insufficient history for"
14518 " index %s",
14519 index->name());
14520 }
14521 continue;
14522 }
14523
14524 m_prebuilt->sql_stat_start = TRUE;
14525 m_prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE;
14526 m_prebuilt->n_template = 0;
14527 m_prebuilt->need_to_access_clustered = FALSE;
14528
14529 dtuple_set_n_fields(m_prebuilt->search_tuple, 0);
14530
14531 m_prebuilt->select_lock_type = LOCK_NONE;
14532
14533 /* Scan this index. */
14534 if (dict_index_is_spatial(index)) {
14535 ret = row_count_rtree_recs(m_prebuilt, &n_rows);
14536 } else {
14537 ret = row_scan_index_for_mysql(
14538 m_prebuilt, index, &n_rows);
14539 }
14540
14541 DBUG_EXECUTE_IF(
14542 "dict_set_index_corrupted",
14543 if (!index->is_primary()) {
14544 ret = DB_CORRUPTION;
14545 });
14546
14547 if (ret == DB_INTERRUPTED || thd_killed(m_user_thd)) {
14548 /* Do not report error since this could happen
14549 during shutdown */
14550 break;
14551 }
14552 if (ret != DB_SUCCESS) {
14553 /* Assume some kind of corruption. */
14554 push_warning_printf(
14555 thd, Sql_condition::WARN_LEVEL_WARN,
14556 ER_NOT_KEYFILE,
14557 "InnoDB: The B-tree of"
14558 " index %s is corrupted.",
14559 index->name());
14560 is_ok = false;
14561 dict_set_corrupted(
14562 index, m_prebuilt->trx, "CHECK TABLE-check index");
14563 }
14564
14565
14566 if (index == dict_table_get_first_index(m_prebuilt->table)) {
14567 n_rows_in_table = n_rows;
14568 } else if (!(index->type & DICT_FTS)
14569 && (n_rows != n_rows_in_table)) {
14570 push_warning_printf(
14571 thd, Sql_condition::WARN_LEVEL_WARN,
14572 ER_NOT_KEYFILE,
14573 "InnoDB: Index '%-.200s' contains " ULINTPF
14574 " entries, should be " ULINTPF ".",
14575 index->name(), n_rows, n_rows_in_table);
14576 is_ok = false;
14577 dict_set_corrupted(
14578 index, m_prebuilt->trx,
14579 "CHECK TABLE; Wrong count");
14580 }
14581 }
14582
14583 /* Restore the original isolation level */
14584 m_prebuilt->trx->isolation_level = old_isolation_level;
14585#ifdef BTR_CUR_HASH_ADAPT
14586# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
14587 /* We validate the whole adaptive hash index for all tables
14588 at every CHECK TABLE only when QUICK flag is not present. */
14589
14590 if (!(check_opt->flags & T_QUICK) && !btr_search_validate()) {
14591 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
14592 ER_NOT_KEYFILE,
14593 "InnoDB: The adaptive hash index is corrupted.");
14594 is_ok = false;
14595 }
14596# endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */
14597#endif /* BTR_CUR_HASH_ADAPT */
14598 m_prebuilt->trx->op_info = "";
14599
14600 DBUG_RETURN(is_ok ? HA_ADMIN_OK : HA_ADMIN_CORRUPT);
14601}
14602
14603/*************************************************************//**
14604Adds information about free space in the InnoDB tablespace to a table comment
14605which is printed out when a user calls SHOW TABLE STATUS. Adds also info on
14606foreign keys.
14607@return table comment + InnoDB free space + info on foreign keys */
14608UNIV_INTERN
14609char*
14610ha_innobase::update_table_comment(
14611/*==============================*/
14612 const char* comment)/*!< in: table comment defined by user */
14613{
14614 uint length = (uint) strlen(comment);
14615 char* str=0;
14616 size_t flen;
14617 std::string fk_str;
14618
14619 /* We do not know if MySQL can call this function before calling
14620 external_lock(). To be safe, update the thd of the current table
14621 handle. */
14622
14623 if (length > 64000 - 3) {
14624 return((char*) comment); /* string too long */
14625 }
14626
14627 update_thd(ha_thd());
14628
14629 m_prebuilt->trx->op_info = "returning table comment";
14630
14631#define SSTR( x ) reinterpret_cast< std::ostringstream & >( \
14632 ( std::ostringstream() << std::dec << x ) ).str()
14633
14634 if (m_prebuilt->table->space) {
14635 fk_str.append("InnoDB free: ");
14636 fk_str.append(SSTR(fsp_get_available_space_in_free_extents(
14637 *m_prebuilt->table->space)));
14638 }
14639
14640 fk_str.append(dict_print_info_on_foreign_keys(
14641 FALSE, m_prebuilt->trx,
14642 m_prebuilt->table));
14643
14644 flen = fk_str.length();
14645
14646 if (length + flen + 3 > 64000) {
14647 flen = 64000 - 3 - length;
14648 }
14649 /* allocate buffer for the full string */
14650 str = (char*) my_malloc(length + flen + 3, MYF(0));
14651 if (str) {
14652 char* pos = str + length;
14653 if (length) {
14654 memcpy(str, comment, length);
14655 *pos++ = ';';
14656 *pos++ = ' ';
14657 }
14658 memcpy(pos, fk_str.c_str(), flen);
14659 pos[flen] = 0;
14660 }
14661
14662 m_prebuilt->trx->op_info = (char*)"";
14663
14664 return(str ? str : (char*) comment);
14665}
14666
14667/*******************************************************************//**
14668Gets the foreign key create info for a table stored in InnoDB.
14669@return own: character string in the form which can be inserted to the
14670CREATE TABLE statement, MUST be freed with
14671ha_innobase::free_foreign_key_create_info */
14672
14673char*
14674ha_innobase::get_foreign_key_create_info(void)
14675/*==========================================*/
14676{
14677 ut_a(m_prebuilt != NULL);
14678
14679 /* We do not know if MySQL can call this function before calling
14680 external_lock(). To be safe, update the thd of the current table
14681 handle. */
14682
14683 update_thd(ha_thd());
14684
14685 m_prebuilt->trx->op_info = "getting info on foreign keys";
14686
14687 /* Output the data to a temporary string */
14688 std::string str = dict_print_info_on_foreign_keys(
14689 TRUE, m_prebuilt->trx,
14690 m_prebuilt->table);
14691
14692 m_prebuilt->trx->op_info = "";
14693
14694 /* Allocate buffer for the string */
14695 char* fk_str = (char*) my_malloc(str.length() + 1, MYF(0));
14696
14697 /* JAN: TODO: MySQL 5.7
14698 fk_str = reinterpret_cast<char*>(
14699 my_malloc(PSI_INSTRUMENT_ME, str.length() + 1, MYF(0)));
14700 */
14701
14702
14703
14704 if (fk_str) {
14705 memcpy(fk_str, str.c_str(), str.length());
14706 fk_str[str.length()]='\0';
14707 }
14708
14709 return(fk_str);
14710}
14711
14712
14713/***********************************************************************//**
14714Maps a InnoDB foreign key constraint to a equivalent MySQL foreign key info.
14715@return pointer to foreign key info */
14716static
14717FOREIGN_KEY_INFO*
14718get_foreign_key_info(
14719/*=================*/
14720 THD* thd, /*!< in: user thread handle */
14721 dict_foreign_t* foreign)/*!< in: foreign key constraint */
14722{
14723 FOREIGN_KEY_INFO f_key_info;
14724 FOREIGN_KEY_INFO* pf_key_info;
14725 uint i = 0;
14726 size_t len;
14727 char tmp_buff[NAME_LEN+1];
14728 char name_buff[NAME_LEN+1];
14729 const char* ptr;
14730 LEX_CSTRING* referenced_key_name;
14731 LEX_CSTRING* name = NULL;
14732
14733 ptr = dict_remove_db_name(foreign->id);
14734 f_key_info.foreign_id = thd_make_lex_string(
14735 thd, 0, ptr, strlen(ptr), 1);
14736
14737 /* Name format: database name, '/', table name, '\0' */
14738
14739 /* Referenced (parent) database name */
14740 len = dict_get_db_name_len(foreign->referenced_table_name);
14741 ut_a(len < sizeof(tmp_buff));
14742 ut_memcpy(tmp_buff, foreign->referenced_table_name, len);
14743 tmp_buff[len] = 0;
14744
14745 len = filename_to_tablename(tmp_buff, name_buff, sizeof(name_buff));
14746 f_key_info.referenced_db = thd_make_lex_string(
14747 thd, 0, name_buff, len, 1);
14748
14749 /* Referenced (parent) table name */
14750 ptr = dict_remove_db_name(foreign->referenced_table_name);
14751 len = filename_to_tablename(ptr, name_buff, sizeof(name_buff));
14752 f_key_info.referenced_table = thd_make_lex_string(
14753 thd, 0, name_buff, len, 1);
14754
14755 /* Dependent (child) database name */
14756 len = dict_get_db_name_len(foreign->foreign_table_name);
14757 ut_a(len < sizeof(tmp_buff));
14758 ut_memcpy(tmp_buff, foreign->foreign_table_name, len);
14759 tmp_buff[len] = 0;
14760
14761 len = filename_to_tablename(tmp_buff, name_buff, sizeof(name_buff));
14762 f_key_info.foreign_db = thd_make_lex_string(
14763 thd, 0, name_buff, len, 1);
14764
14765 /* Dependent (child) table name */
14766 ptr = dict_remove_db_name(foreign->foreign_table_name);
14767 len = filename_to_tablename(ptr, name_buff, sizeof(name_buff));
14768 f_key_info.foreign_table = thd_make_lex_string(
14769 thd, 0, name_buff, len, 1);
14770
14771 do {
14772 ptr = foreign->foreign_col_names[i];
14773 name = thd_make_lex_string(thd, name, ptr,
14774 strlen(ptr), 1);
14775 f_key_info.foreign_fields.push_back(name);
14776 ptr = foreign->referenced_col_names[i];
14777 name = thd_make_lex_string(thd, name, ptr,
14778 strlen(ptr), 1);
14779 f_key_info.referenced_fields.push_back(name);
14780 } while (++i < foreign->n_fields);
14781
14782 if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
14783 f_key_info.delete_method = FK_OPTION_CASCADE;
14784 } else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
14785 f_key_info.delete_method = FK_OPTION_SET_NULL;
14786 } else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
14787 f_key_info.delete_method = FK_OPTION_NO_ACTION;
14788 } else {
14789 f_key_info.delete_method = FK_OPTION_RESTRICT;
14790 }
14791
14792
14793 if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
14794 f_key_info.update_method = FK_OPTION_CASCADE;
14795 } else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
14796 f_key_info.update_method = FK_OPTION_SET_NULL;
14797 } else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
14798 f_key_info.update_method = FK_OPTION_NO_ACTION;
14799 } else {
14800 f_key_info.update_method = FK_OPTION_RESTRICT;
14801 }
14802
14803 /* Load referenced table to update FK referenced key name. */
14804 if (foreign->referenced_table == NULL) {
14805
14806 dict_table_t* ref_table;
14807
14808 ut_ad(mutex_own(&dict_sys->mutex));
14809 ref_table = dict_table_open_on_name(
14810 foreign->referenced_table_name_lookup,
14811 TRUE, FALSE, DICT_ERR_IGNORE_NONE);
14812
14813 if (ref_table == NULL) {
14814
14815 if (!thd_test_options(
14816 thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
14817 ib::info()
14818 << "Foreign Key referenced table "
14819 << foreign->referenced_table_name
14820 << " not found for foreign table "
14821 << foreign->foreign_table_name;
14822 }
14823 } else {
14824
14825 dict_table_close(ref_table, TRUE, FALSE);
14826 }
14827 }
14828
14829 if (foreign->referenced_index
14830 && foreign->referenced_index->name != NULL) {
14831 referenced_key_name = thd_make_lex_string(
14832 thd,
14833 f_key_info.referenced_key_name,
14834 foreign->referenced_index->name,
14835 strlen(foreign->referenced_index->name),
14836 1);
14837 } else {
14838 referenced_key_name = NULL;
14839 }
14840
14841 f_key_info.referenced_key_name = referenced_key_name;
14842
14843 pf_key_info = (FOREIGN_KEY_INFO*) thd_memdup(thd, &f_key_info,
14844 sizeof(FOREIGN_KEY_INFO));
14845
14846 return(pf_key_info);
14847}
14848
14849/*******************************************************************//**
14850Gets the list of foreign keys in this table.
14851@return always 0, that is, always succeeds */
14852
14853int
14854ha_innobase::get_foreign_key_list(
14855/*==============================*/
14856 THD* thd, /*!< in: user thread handle */
14857 List<FOREIGN_KEY_INFO>* f_key_list) /*!< out: foreign key list */
14858{
14859 update_thd(ha_thd());
14860
14861 m_prebuilt->trx->op_info = "getting list of foreign keys";
14862
14863 mutex_enter(&dict_sys->mutex);
14864
14865 for (dict_foreign_set::iterator it
14866 = m_prebuilt->table->foreign_set.begin();
14867 it != m_prebuilt->table->foreign_set.end();
14868 ++it) {
14869
14870 FOREIGN_KEY_INFO* pf_key_info;
14871 dict_foreign_t* foreign = *it;
14872
14873 pf_key_info = get_foreign_key_info(thd, foreign);
14874
14875 if (pf_key_info != NULL) {
14876 f_key_list->push_back(pf_key_info);
14877 }
14878 }
14879
14880 mutex_exit(&dict_sys->mutex);
14881
14882 m_prebuilt->trx->op_info = "";
14883
14884 return(0);
14885}
14886
14887/*******************************************************************//**
14888Gets the set of foreign keys where this table is the referenced table.
14889@return always 0, that is, always succeeds */
14890
14891int
14892ha_innobase::get_parent_foreign_key_list(
14893/*=====================================*/
14894 THD* thd, /*!< in: user thread handle */
14895 List<FOREIGN_KEY_INFO>* f_key_list) /*!< out: foreign key list */
14896{
14897 update_thd(ha_thd());
14898
14899 m_prebuilt->trx->op_info = "getting list of referencing foreign keys";
14900
14901 mutex_enter(&dict_sys->mutex);
14902
14903 for (dict_foreign_set::iterator it
14904 = m_prebuilt->table->referenced_set.begin();
14905 it != m_prebuilt->table->referenced_set.end();
14906 ++it) {
14907
14908 FOREIGN_KEY_INFO* pf_key_info;
14909 dict_foreign_t* foreign = *it;
14910
14911 pf_key_info = get_foreign_key_info(thd, foreign);
14912
14913 if (pf_key_info != NULL) {
14914 f_key_list->push_back(pf_key_info);
14915 }
14916 }
14917
14918 mutex_exit(&dict_sys->mutex);
14919
14920 m_prebuilt->trx->op_info = "";
14921
14922 return(0);
14923}
14924
14925/** Table list item structure is used to store only the table
14926and name. It is used by get_cascade_foreign_key_table_list to store
14927the intermediate result for fetching the table set. */
14928struct table_list_item {
14929 /** InnoDB table object */
14930 const dict_table_t* table;
14931 /** Table name */
14932 const char* name;
14933};
14934
14935/** Structure to compare two st_tablename objects using their
14936db and tablename. It is used in the ordering of cascade_fk_set.
14937It returns true if the first argument precedes the second argument
14938and false otherwise. */
14939struct tablename_compare {
14940
14941 bool operator()(const st_handler_tablename lhs,
14942 const st_handler_tablename rhs) const
14943 {
14944 int cmp = strcmp(lhs.db, rhs.db);
14945 if (cmp == 0) {
14946 cmp = strcmp(lhs.tablename, rhs.tablename);
14947 }
14948
14949 return(cmp < 0);
14950 }
14951};
14952
14953/** Get the table name and database name for the given table.
14954@param[in,out] thd user thread handle
14955@param[out] f_key_info pointer to table_name_info object
14956@param[in] foreign foreign key constraint. */
14957static
14958void
14959get_table_name_info(
14960 THD* thd,
14961 st_handler_tablename* f_key_info,
14962 const dict_foreign_t* foreign)
14963{
14964#define FILENAME_CHARSET_MBMAXLEN 5
14965 char tmp_buff[NAME_CHAR_LEN * FILENAME_CHARSET_MBMAXLEN + 1];
14966 char name_buff[NAME_CHAR_LEN * FILENAME_CHARSET_MBMAXLEN + 1];
14967 const char* ptr;
14968
14969 size_t len = dict_get_db_name_len(
14970 foreign->referenced_table_name_lookup);
14971 ut_memcpy(tmp_buff, foreign->referenced_table_name_lookup, len);
14972 tmp_buff[len] = 0;
14973
14974 ut_ad(len < sizeof(tmp_buff));
14975
14976 len = filename_to_tablename(tmp_buff, name_buff, sizeof(name_buff));
14977 f_key_info->db = thd_strmake(thd, name_buff, len);
14978
14979 ptr = dict_remove_db_name(foreign->referenced_table_name_lookup);
14980 len = filename_to_tablename(ptr, name_buff, sizeof(name_buff));
14981 f_key_info->tablename = thd_strmake(thd, name_buff, len);
14982}
14983
14984/** Get the list of tables ordered by the dependency on the other tables using
14985the 'CASCADE' foreign key constraint.
14986@param[in,out] thd user thread handle
14987@param[out] fk_table_list set of tables name info for the
14988 dependent table
14989@retval 0 for success. */
14990int
14991ha_innobase::get_cascade_foreign_key_table_list(
14992 THD* thd,
14993 List<st_handler_tablename>* fk_table_list)
14994{
14995 m_prebuilt->trx->op_info = "getting cascading foreign keys";
14996
14997 std::list<table_list_item, ut_allocator<table_list_item> > table_list;
14998
14999 typedef std::set<st_handler_tablename, tablename_compare,
15000 ut_allocator<st_handler_tablename> > cascade_fk_set;
15001
15002 cascade_fk_set fk_set;
15003
15004 mutex_enter(&dict_sys->mutex);
15005
15006 /* Initialize the table_list with prebuilt->table name. */
15007 struct table_list_item item = {m_prebuilt->table,
15008 m_prebuilt->table->name.m_name};
15009
15010 table_list.push_back(item);
15011
15012 /* Get the parent table, grand parent table info from the
15013 table list by depth-first traversal. */
15014 do {
15015 const dict_table_t* parent_table;
15016 dict_table_t* parent = NULL;
15017 std::pair<cascade_fk_set::iterator,bool> ret;
15018
15019 item = table_list.back();
15020 table_list.pop_back();
15021 parent_table = item.table;
15022
15023 if (parent_table == NULL) {
15024
15025 ut_ad(item.name != NULL);
15026
15027 parent_table = parent = dict_table_open_on_name(
15028 item.name, TRUE, FALSE,
15029 DICT_ERR_IGNORE_NONE);
15030
15031 if (parent_table == NULL) {
15032 /* foreign_key_checks is or was probably
15033 disabled; ignore the constraint */
15034 continue;
15035 }
15036 }
15037
15038 for (dict_foreign_set::const_iterator it =
15039 parent_table->foreign_set.begin();
15040 it != parent_table->foreign_set.end(); ++it) {
15041
15042 const dict_foreign_t* foreign = *it;
15043 st_handler_tablename f1;
15044
15045 /* Skip the table if there is no
15046 cascading operation. */
15047 if (0 == (foreign->type
15048 & ~(DICT_FOREIGN_ON_DELETE_NO_ACTION
15049 | DICT_FOREIGN_ON_UPDATE_NO_ACTION))) {
15050 continue;
15051 }
15052
15053 if (foreign->referenced_table_name_lookup != NULL) {
15054 get_table_name_info(thd, &f1, foreign);
15055 ret = fk_set.insert(f1);
15056
15057 /* Ignore the table if it is already
15058 in the set. */
15059 if (!ret.second) {
15060 continue;
15061 }
15062
15063 struct table_list_item item1 = {
15064 foreign->referenced_table,
15065 foreign->referenced_table_name_lookup};
15066
15067 table_list.push_back(item1);
15068
15069 st_handler_tablename* fk_table =
15070 (st_handler_tablename*) thd_memdup(
15071 thd, &f1, sizeof(*fk_table));
15072
15073 fk_table_list->push_back(fk_table);
15074 }
15075 }
15076
15077 if (parent != NULL) {
15078 dict_table_close(parent, true, false);
15079 }
15080
15081 } while(!table_list.empty());
15082
15083 mutex_exit(&dict_sys->mutex);
15084
15085 m_prebuilt->trx->op_info = "";
15086
15087 return(0);
15088}
15089
15090/*****************************************************************//**
15091Checks if ALTER TABLE may change the storage engine of the table.
15092Changing storage engines is not allowed for tables for which there
15093are foreign key constraints (parent or child tables).
15094@return TRUE if can switch engines */
15095
15096bool
15097ha_innobase::can_switch_engines(void)
15098/*=================================*/
15099{
15100 DBUG_ENTER("ha_innobase::can_switch_engines");
15101
15102 update_thd();
15103
15104 m_prebuilt->trx->op_info =
15105 "determining if there are foreign key constraints";
15106
15107 row_mysql_freeze_data_dictionary(m_prebuilt->trx);
15108
15109 bool can_switch = m_prebuilt->table->referenced_set.empty()
15110 && m_prebuilt->table->foreign_set.empty();
15111
15112 row_mysql_unfreeze_data_dictionary(m_prebuilt->trx);
15113 m_prebuilt->trx->op_info = "";
15114
15115 DBUG_RETURN(can_switch);
15116}
15117
15118/*******************************************************************//**
15119Checks if a table is referenced by a foreign key. The MySQL manual states that
15120a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
15121delete is then allowed internally to resolve a duplicate key conflict in
15122REPLACE, not an update.
15123@return > 0 if referenced by a FOREIGN KEY */
15124
15125uint
15126ha_innobase::referenced_by_foreign_key(void)
15127/*========================================*/
15128{
15129 if (dict_table_is_referenced_by_foreign_key(m_prebuilt->table)) {
15130
15131 return(1);
15132 }
15133
15134 return(0);
15135}
15136
15137/*******************************************************************//**
15138Frees the foreign key create info for a table stored in InnoDB, if it is
15139non-NULL. */
15140
15141void
15142ha_innobase::free_foreign_key_create_info(
15143/*======================================*/
15144 char* str) /*!< in, own: create info string to free */
15145{
15146 if (str != NULL) {
15147 my_free(str);
15148 }
15149}
15150
15151/*******************************************************************//**
15152Tells something additional to the handler about how to do things.
15153@return 0 or error number */
15154
15155int
15156ha_innobase::extra(
15157/*===============*/
15158 enum ha_extra_function operation)
15159 /*!< in: HA_EXTRA_FLUSH or some other flag */
15160{
15161 check_trx_exists(ha_thd());
15162
15163 /* Warning: since it is not sure that MySQL calls external_lock
15164 before calling this function, the trx field in m_prebuilt can be
15165 obsolete! */
15166
15167 switch (operation) {
15168 case HA_EXTRA_FLUSH:
15169 if (m_prebuilt->blob_heap) {
15170 row_mysql_prebuilt_free_blob_heap(m_prebuilt);
15171 }
15172 break;
15173 case HA_EXTRA_RESET_STATE:
15174 reset_template();
15175 thd_to_trx(ha_thd())->duplicates = 0;
15176 break;
15177 case HA_EXTRA_NO_KEYREAD:
15178 m_prebuilt->read_just_key = 0;
15179 break;
15180 case HA_EXTRA_KEYREAD:
15181 m_prebuilt->read_just_key = 1;
15182 break;
15183 case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
15184 m_prebuilt->keep_other_fields_on_keyread = 1;
15185 break;
15186
15187 /* IMPORTANT: m_prebuilt->trx can be obsolete in
15188 this method, because it is not sure that MySQL
15189 calls external_lock before this method with the
15190 parameters below. We must not invoke update_thd()
15191 either, because the calling threads may change.
15192 CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */
15193 case HA_EXTRA_INSERT_WITH_UPDATE:
15194 thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE;
15195 break;
15196 case HA_EXTRA_NO_IGNORE_DUP_KEY:
15197 thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_IGNORE;
15198 break;
15199 case HA_EXTRA_WRITE_CAN_REPLACE:
15200 thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE;
15201 break;
15202 case HA_EXTRA_WRITE_CANNOT_REPLACE:
15203 thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE;
15204 break;
15205 case HA_EXTRA_BEGIN_ALTER_COPY:
15206 m_prebuilt->table->skip_alter_undo = 1;
15207 if (m_prebuilt->table->is_temporary()
15208 || !m_prebuilt->table->versioned_by_id()) {
15209 break;
15210 }
15211 trx_start_if_not_started(m_prebuilt->trx, true);
15212 m_prebuilt->trx->mod_tables.insert(
15213 trx_mod_tables_t::value_type(
15214 const_cast<dict_table_t*>(m_prebuilt->table),
15215 0))
15216 .first->second.set_versioned(0);
15217 break;
15218 case HA_EXTRA_END_ALTER_COPY:
15219 m_prebuilt->table->skip_alter_undo = 0;
15220 break;
15221 case HA_EXTRA_FAKE_START_STMT:
15222 trx_register_for_2pc(m_prebuilt->trx);
15223 m_prebuilt->sql_stat_start = true;
15224 break;
15225 default:/* Do nothing */
15226 ;
15227 }
15228
15229 return(0);
15230}
15231
15232/**
15233MySQL calls this method at the end of each statement. This method
15234exists for readability only. ha_innobase::reset() doesn't give any
15235clue about the method. */
15236
15237int
15238ha_innobase::end_stmt()
15239{
15240 if (m_prebuilt->blob_heap) {
15241 row_mysql_prebuilt_free_blob_heap(m_prebuilt);
15242 }
15243
15244 reset_template();
15245
15246 m_ds_mrr.dsmrr_close();
15247
15248 /* TODO: This should really be reset in reset_template() but for now
15249 it's safer to do it explicitly here. */
15250
15251 /* This is a statement level counter. */
15252 m_prebuilt->autoinc_last_value = 0;
15253
15254 return(0);
15255}
15256
15257/**
15258MySQL calls this method at the end of each statement */
15259
15260int
15261ha_innobase::reset()
15262{
15263 return(end_stmt());
15264}
15265
15266/******************************************************************//**
15267MySQL calls this function at the start of each SQL statement inside LOCK
15268TABLES. Inside LOCK TABLES the ::external_lock method does not work to
15269mark SQL statement borders. Note also a special case: if a temporary table
15270is created inside LOCK TABLES, MySQL has not called external_lock() at all
15271on that table.
15272MySQL-5.0 also calls this before each statement in an execution of a stored
15273procedure. To make the execution more deterministic for binlogging, MySQL-5.0
15274locks all tables involved in a stored procedure with full explicit table
15275locks (thd_in_lock_tables(thd) holds in store_lock()) before executing the
15276procedure.
15277@return 0 or error code */
15278
15279int
15280ha_innobase::start_stmt(
15281/*====================*/
15282 THD* thd, /*!< in: handle to the user thread */
15283 thr_lock_type lock_type)
15284{
15285 trx_t* trx = m_prebuilt->trx;
15286
15287 DBUG_ENTER("ha_innobase::start_stmt");
15288
15289 update_thd(thd);
15290
15291 ut_ad(m_prebuilt->table != NULL);
15292
15293 trx = m_prebuilt->trx;
15294
15295 innobase_srv_conc_force_exit_innodb(trx);
15296
15297 /* Reset the AUTOINC statement level counter for multi-row INSERTs. */
15298 trx->n_autoinc_rows = 0;
15299
15300 m_prebuilt->sql_stat_start = TRUE;
15301 m_prebuilt->hint_need_to_fetch_extra_cols = 0;
15302 reset_template();
15303
15304 if (m_prebuilt->table->is_temporary()
15305 && m_mysql_has_locked
15306 && m_prebuilt->select_lock_type == LOCK_NONE) {
15307 dberr_t error;
15308
15309 switch (thd_sql_command(thd)) {
15310 case SQLCOM_INSERT:
15311 case SQLCOM_UPDATE:
15312 case SQLCOM_DELETE:
15313 case SQLCOM_REPLACE:
15314 init_table_handle_for_HANDLER();
15315 m_prebuilt->select_lock_type = LOCK_X;
15316 m_prebuilt->stored_select_lock_type = LOCK_X;
15317 error = row_lock_table(m_prebuilt);
15318
15319 if (error != DB_SUCCESS) {
15320 int st = convert_error_code_to_mysql(
15321 error, 0, thd);
15322 DBUG_RETURN(st);
15323 }
15324 break;
15325 }
15326 }
15327
15328 if (!m_mysql_has_locked) {
15329 /* This handle is for a temporary table created inside
15330 this same LOCK TABLES; since MySQL does NOT call external_lock
15331 in this case, we must use x-row locks inside InnoDB to be
15332 prepared for an update of a row */
15333
15334 m_prebuilt->select_lock_type = LOCK_X;
15335
15336 } else if (trx->isolation_level != TRX_ISO_SERIALIZABLE
15337 && thd_sql_command(thd) == SQLCOM_SELECT
15338 && lock_type == TL_READ) {
15339
15340 /* For other than temporary tables, we obtain
15341 no lock for consistent read (plain SELECT). */
15342
15343 m_prebuilt->select_lock_type = LOCK_NONE;
15344 } else {
15345 /* Not a consistent read: restore the
15346 select_lock_type value. The value of
15347 stored_select_lock_type was decided in:
15348 1) ::store_lock(),
15349 2) ::external_lock(),
15350 3) ::init_table_handle_for_HANDLER(). */
15351
15352 ut_a(m_prebuilt->stored_select_lock_type != LOCK_NONE_UNSET);
15353
15354 m_prebuilt->select_lock_type =
15355 m_prebuilt->stored_select_lock_type;
15356 }
15357
15358 *trx->detailed_error = 0;
15359
15360 innobase_register_trx(ht, thd, trx);
15361
15362 if (!trx_is_started(trx)) {
15363 ++trx->will_lock;
15364 }
15365
15366 DBUG_RETURN(0);
15367}
15368
15369/******************************************************************//**
15370Maps a MySQL trx isolation level code to the InnoDB isolation level code
15371@return InnoDB isolation level */
15372static inline
15373ulint
15374innobase_map_isolation_level(
15375/*=========================*/
15376 enum_tx_isolation iso) /*!< in: MySQL isolation level code */
15377{
15378 if (UNIV_UNLIKELY(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN)
15379 || UNIV_UNLIKELY(srv_read_only_mode)) {
15380 return TRX_ISO_READ_UNCOMMITTED;
15381 }
15382 switch (iso) {
15383 case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ);
15384 case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
15385 case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
15386 case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
15387 }
15388
15389 ut_error;
15390
15391 return(0);
15392}
15393
15394/******************************************************************//**
15395As MySQL will execute an external lock for every new table it uses when it
15396starts to process an SQL statement (an exception is when MySQL calls
15397start_stmt for the handle) we can use this function to store the pointer to
15398the THD in the handle. We will also use this function to communicate
15399to InnoDB that a new SQL statement has started and that we must store a
15400savepoint to our transaction handle, so that we are able to roll back
15401the SQL statement in case of an error.
15402@return 0 */
15403
15404int
15405ha_innobase::external_lock(
15406/*=======================*/
15407 THD* thd, /*!< in: handle to the user thread */
15408 int lock_type) /*!< in: lock type */
15409{
15410 DBUG_ENTER("ha_innobase::external_lock");
15411 DBUG_PRINT("enter",("lock_type: %d", lock_type));
15412
15413 update_thd(thd);
15414
15415 trx_t* trx = m_prebuilt->trx;
15416
15417 ut_ad(m_prebuilt->table);
15418
15419 /* Statement based binlogging does not work in isolation level
15420 READ UNCOMMITTED and READ COMMITTED since the necessary
15421 locks cannot be taken. In this case, we print an
15422 informative error message and return with an error.
15423 Note: decide_logging_format would give the same error message,
15424 except it cannot give the extra details. */
15425
15426 if (lock_type == F_WRLCK
15427 && !(table_flags() & HA_BINLOG_STMT_CAPABLE)
15428 && thd_binlog_format(thd) == BINLOG_FORMAT_STMT
15429 && thd_binlog_filter_ok(thd)
15430 && thd_sqlcom_can_generate_row_events(thd)) {
15431
15432 bool skip = false;
15433
15434 /* used by test case */
15435 DBUG_EXECUTE_IF("no_innodb_binlog_errors", skip = true;);
15436
15437 if (!skip) {
15438#ifdef WITH_WSREP
15439 if (!wsrep_on(thd) || wsrep_thd_exec_mode(thd) == LOCAL_STATE)
15440 {
15441#endif /* WITH_WSREP */
15442 my_error(ER_BINLOG_STMT_MODE_AND_ROW_ENGINE, MYF(0),
15443 " InnoDB is limited to row-logging when"
15444 " transaction isolation level is"
15445 " READ COMMITTED or READ UNCOMMITTED.");
15446
15447 DBUG_RETURN(HA_ERR_LOGGING_IMPOSSIBLE);
15448#ifdef WITH_WSREP
15449 }
15450#endif /* WITH_WSREP */
15451 }
15452 }
15453
15454 /* Check for UPDATEs in read-only mode. */
15455 if (srv_read_only_mode) {
15456 switch (thd_sql_command(thd)) {
15457 case SQLCOM_CREATE_TABLE:
15458 if (lock_type != F_WRLCK) {
15459 break;
15460 }
15461 /* fall through */
15462 case SQLCOM_UPDATE:
15463 case SQLCOM_INSERT:
15464 case SQLCOM_REPLACE:
15465 case SQLCOM_DROP_TABLE:
15466 case SQLCOM_ALTER_TABLE:
15467 case SQLCOM_OPTIMIZE:
15468 case SQLCOM_CREATE_INDEX:
15469 case SQLCOM_DROP_INDEX:
15470 case SQLCOM_CREATE_SEQUENCE:
15471 case SQLCOM_DROP_SEQUENCE:
15472 case SQLCOM_DELETE:
15473 ib_senderrf(thd, IB_LOG_LEVEL_WARN,
15474 ER_READ_ONLY_MODE);
15475 DBUG_RETURN(HA_ERR_TABLE_READONLY);
15476 }
15477 }
15478
15479 m_prebuilt->sql_stat_start = TRUE;
15480 m_prebuilt->hint_need_to_fetch_extra_cols = 0;
15481
15482 reset_template();
15483
15484 switch (m_prebuilt->table->quiesce) {
15485 case QUIESCE_START:
15486 /* Check for FLUSH TABLE t WITH READ LOCK; */
15487 if (!srv_read_only_mode
15488 && thd_sql_command(thd) == SQLCOM_FLUSH
15489 && lock_type == F_RDLCK) {
15490
15491 if (!m_prebuilt->table->space) {
15492 ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_ERROR,
15493 ER_TABLESPACE_DISCARDED,
15494 table->s->table_name.str);
15495
15496 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
15497 }
15498
15499 row_quiesce_table_start(m_prebuilt->table, trx);
15500
15501 /* Use the transaction instance to track UNLOCK
15502 TABLES. It can be done via START TRANSACTION; too
15503 implicitly. */
15504
15505 ++trx->flush_tables;
15506 }
15507 break;
15508
15509 case QUIESCE_COMPLETE:
15510 /* Check for UNLOCK TABLES; implicit or explicit
15511 or trx interruption. */
15512 if (trx->flush_tables > 0
15513 && (lock_type == F_UNLCK || trx_is_interrupted(trx))) {
15514
15515 row_quiesce_table_complete(m_prebuilt->table, trx);
15516
15517 ut_a(trx->flush_tables > 0);
15518 --trx->flush_tables;
15519 }
15520
15521 break;
15522
15523 case QUIESCE_NONE:
15524 break;
15525 }
15526
15527 if (lock_type == F_WRLCK) {
15528
15529 /* If this is a SELECT, then it is in UPDATE TABLE ...
15530 or SELECT ... FOR UPDATE */
15531 m_prebuilt->select_lock_type = LOCK_X;
15532 m_prebuilt->stored_select_lock_type = LOCK_X;
15533 }
15534
15535 if (lock_type != F_UNLCK) {
15536 /* MySQL is setting a new table lock */
15537
15538 *trx->detailed_error = 0;
15539
15540 innobase_register_trx(ht, thd, trx);
15541
15542 if (trx->isolation_level == TRX_ISO_SERIALIZABLE
15543 && m_prebuilt->select_lock_type == LOCK_NONE
15544 && thd_test_options(
15545 thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
15546
15547 /* To get serializable execution, we let InnoDB
15548 conceptually add 'LOCK IN SHARE MODE' to all SELECTs
15549 which otherwise would have been consistent reads. An
15550 exception is consistent reads in the AUTOCOMMIT=1 mode:
15551 we know that they are read-only transactions, and they
15552 can be serialized also if performed as consistent
15553 reads. */
15554
15555 m_prebuilt->select_lock_type = LOCK_S;
15556 m_prebuilt->stored_select_lock_type = LOCK_S;
15557 }
15558
15559 /* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
15560 TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
15561 an InnoDB table lock if it is released immediately at the end
15562 of LOCK TABLES, and InnoDB's table locks in that case cause
15563 VERY easily deadlocks.
15564
15565 We do not set InnoDB table locks if user has not explicitly
15566 requested a table lock. Note that thd_in_lock_tables(thd)
15567 can hold in some cases, e.g., at the start of a stored
15568 procedure call (SQLCOM_CALL). */
15569
15570 if (m_prebuilt->select_lock_type != LOCK_NONE) {
15571
15572 if (thd_sql_command(thd) == SQLCOM_LOCK_TABLES
15573 && THDVAR(thd, table_locks)
15574 && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT)
15575 && thd_in_lock_tables(thd)) {
15576
15577 dberr_t error = row_lock_table(m_prebuilt);
15578
15579 if (error != DB_SUCCESS) {
15580
15581 DBUG_RETURN(
15582 convert_error_code_to_mysql(
15583 error, 0, thd));
15584 }
15585 }
15586
15587 trx->mysql_n_tables_locked++;
15588 }
15589
15590 trx->n_mysql_tables_in_use++;
15591 m_mysql_has_locked = true;
15592
15593 if (!trx_is_started(trx)
15594 && (m_prebuilt->select_lock_type != LOCK_NONE
15595 || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
15596
15597 ++trx->will_lock;
15598 }
15599
15600 DBUG_RETURN(0);
15601 } else {
15602 DEBUG_SYNC_C("ha_innobase_end_statement");
15603 }
15604
15605 /* MySQL is releasing a table lock */
15606
15607 trx->n_mysql_tables_in_use--;
15608 m_mysql_has_locked = false;
15609
15610 innobase_srv_conc_force_exit_innodb(trx);
15611
15612 /* If the MySQL lock count drops to zero we know that the current SQL
15613 statement has ended */
15614
15615 if (trx->n_mysql_tables_in_use == 0) {
15616
15617 trx->mysql_n_tables_locked = 0;
15618 m_prebuilt->used_in_HANDLER = FALSE;
15619
15620 if (!thd_test_options(
15621 thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
15622
15623 if (trx_is_started(trx)) {
15624
15625 innobase_commit(ht, thd, TRUE);
15626 }
15627
15628 } else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED) {
15629 trx->read_view.close();
15630 }
15631 }
15632
15633 if (!trx_is_started(trx)
15634 && lock_type != F_UNLCK
15635 && (m_prebuilt->select_lock_type != LOCK_NONE
15636 || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
15637
15638 ++trx->will_lock;
15639 }
15640
15641 DBUG_RETURN(0);
15642}
15643
15644/************************************************************************//**
15645Here we export InnoDB status variables to MySQL. */
15646static
15647void
15648innodb_export_status()
15649/*==================*/
15650{
15651 if (srv_was_started) {
15652 srv_export_innodb_status();
15653 }
15654}
15655
15656/************************************************************************//**
15657Implements the SHOW ENGINE INNODB STATUS command. Sends the output of the
15658InnoDB Monitor to the client.
15659@return 0 on success */
15660static
15661int
15662innodb_show_status(
15663/*===============*/
15664 handlerton* hton, /*!< in: the innodb handlerton */
15665 THD* thd, /*!< in: the MySQL query thread of the caller */
15666 stat_print_fn* stat_print)
15667{
15668 static const char truncated_msg[] = "... truncated...\n";
15669 const long MAX_STATUS_SIZE = 1048576;
15670 ulint trx_list_start = ULINT_UNDEFINED;
15671 ulint trx_list_end = ULINT_UNDEFINED;
15672 bool ret_val;
15673
15674 DBUG_ENTER("innodb_show_status");
15675 DBUG_ASSERT(hton == innodb_hton_ptr);
15676
15677 /* We don't create the temp files or associated
15678 mutexes in read-only-mode */
15679
15680 if (srv_read_only_mode) {
15681 DBUG_RETURN(0);
15682 }
15683
15684 srv_wake_purge_thread_if_not_active();
15685
15686 trx_t* trx = check_trx_exists(thd);
15687
15688 innobase_srv_conc_force_exit_innodb(trx);
15689
15690 /* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
15691 bytes of text. */
15692
15693 char* str;
15694 size_t flen;
15695
15696 mutex_enter(&srv_monitor_file_mutex);
15697 rewind(srv_monitor_file);
15698
15699 srv_printf_innodb_monitor(srv_monitor_file, FALSE,
15700 &trx_list_start, &trx_list_end);
15701
15702 os_file_set_eof(srv_monitor_file);
15703
15704 flen = size_t(ftell(srv_monitor_file));
15705 if (ssize_t(flen) < 0) {
15706 flen = 0;
15707 }
15708
15709 size_t usable_len;
15710
15711 if (flen > MAX_STATUS_SIZE) {
15712 usable_len = MAX_STATUS_SIZE;
15713 srv_truncated_status_writes++;
15714 } else {
15715 usable_len = flen;
15716 }
15717
15718 /* allocate buffer for the string, and
15719 read the contents of the temporary file */
15720
15721 if (!(str = (char*) my_malloc(//PSI_INSTRUMENT_ME,
15722 usable_len + 1, MYF(0)))) {
15723 mutex_exit(&srv_monitor_file_mutex);
15724 DBUG_RETURN(1);
15725 }
15726
15727 rewind(srv_monitor_file);
15728
15729 if (flen < MAX_STATUS_SIZE) {
15730 /* Display the entire output. */
15731 flen = fread(str, 1, flen, srv_monitor_file);
15732 } else if (trx_list_end < flen
15733 && trx_list_start < trx_list_end
15734 && trx_list_start + flen - trx_list_end
15735 < MAX_STATUS_SIZE - sizeof truncated_msg - 1) {
15736
15737 /* Omit the beginning of the list of active transactions. */
15738 size_t len = fread(str, 1, trx_list_start, srv_monitor_file);
15739
15740 memcpy(str + len, truncated_msg, sizeof truncated_msg - 1);
15741 len += sizeof truncated_msg - 1;
15742 usable_len = (MAX_STATUS_SIZE - 1) - len;
15743 fseek(srv_monitor_file, long(flen - usable_len), SEEK_SET);
15744 len += fread(str + len, 1, usable_len, srv_monitor_file);
15745 flen = len;
15746 } else {
15747 /* Omit the end of the output. */
15748 flen = fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
15749 }
15750
15751 mutex_exit(&srv_monitor_file_mutex);
15752
15753 ret_val= stat_print(
15754 thd, innobase_hton_name,
15755 static_cast<uint>(strlen(innobase_hton_name)),
15756 STRING_WITH_LEN(""), str, static_cast<uint>(flen));
15757
15758 my_free(str);
15759
15760 DBUG_RETURN(ret_val);
15761}
15762
15763/** Callback for collecting mutex statistics */
15764struct ShowStatus {
15765
15766 /** For tracking the mutex metrics */
15767 struct Value {
15768
15769 /** Constructor
15770 @param[in] name Name of the mutex
15771 @param[in] spins Number of spins
15772 @param[in] os_waits OS waits so far
15773 @param[in] calls Number of calls to enter() */
15774 Value(const char* name,
15775 ulint spins,
15776 uint64_t waits,
15777 uint64_t calls)
15778 :
15779 m_name(name),
15780 m_spins(spins),
15781 m_waits(waits),
15782 m_calls(calls)
15783 {
15784 /* No op */
15785 }
15786
15787 /** Mutex name */
15788 std::string m_name;
15789
15790 /** Spins so far */
15791 ulint m_spins;
15792
15793 /** Waits so far */
15794 uint64_t m_waits;
15795
15796 /** Number of calls so far */
15797 uint64_t m_calls;
15798 };
15799
15800 /** Order by m_waits, in descending order. */
15801 struct OrderByWaits: public std::binary_function<Value, Value, bool>
15802 {
15803 /** @return true if rhs < lhs */
15804 bool operator()(
15805 const Value& lhs,
15806 const Value& rhs) const
15807 UNIV_NOTHROW
15808 {
15809 return(rhs.m_waits < lhs.m_waits);
15810 }
15811 };
15812
15813 typedef std::vector<Value, ut_allocator<Value> > Values;
15814
15815 /** Collect the individual latch counts */
15816 struct GetCount {
15817 typedef latch_meta_t::CounterType::Count Count;
15818
15819 /** Constructor
15820 @param[in] name Latch name
15821 @param[in,out] values Put the values here */
15822 GetCount(
15823 const char* name,
15824 Values* values)
15825 UNIV_NOTHROW
15826 :
15827 m_name(name),
15828 m_values(values)
15829 {
15830 /* No op */
15831 }
15832
15833 /** Collect the latch metrics. Ignore entries where the
15834 spins and waits are zero.
15835 @param[in] count The latch metrics */
15836 void operator()(Count* count)
15837 UNIV_NOTHROW
15838 {
15839 if (count->m_spins > 0 || count->m_waits > 0) {
15840
15841 m_values->push_back(Value(
15842 m_name,
15843 count->m_spins,
15844 count->m_waits,
15845 count->m_calls));
15846 }
15847 }
15848
15849 /** The latch name */
15850 const char* m_name;
15851
15852 /** For collecting the active mutex stats. */
15853 Values* m_values;
15854 };
15855
15856 /** Constructor */
15857 ShowStatus() { }
15858
15859 /** Callback for collecting the stats
15860 @param[in] latch_meta Latch meta data
15861 @return always returns true */
15862 bool operator()(latch_meta_t& latch_meta)
15863 UNIV_NOTHROW
15864 {
15865 latch_meta_t::CounterType* counter;
15866
15867 counter = latch_meta.get_counter();
15868
15869 GetCount get_count(latch_meta.get_name(), &m_values);
15870
15871 counter->iterate(get_count);
15872
15873 return(true);
15874 }
15875
15876 /** Implements the SHOW MUTEX STATUS command, for mutexes.
15877 The table structure is like so: Engine | Mutex Name | Status
15878 We store the metrics in the "Status" column as:
15879
15880 spins=N,waits=N,calls=N"
15881
15882 The user has to parse the dataunfortunately
15883 @param[in,out] thd the MySQL query thread of the caller
15884 @param[in,out] stat_print function for printing statistics
15885 @return true on success. */
15886 bool to_string(
15887 THD* thd,
15888 stat_print_fn* stat_print)
15889 UNIV_NOTHROW;
15890
15891 /** For collecting the active mutex stats. */
15892 Values m_values;
15893};
15894
15895/** Implements the SHOW MUTEX STATUS command, for mutexes.
15896The table structure is like so: Engine | Mutex Name | Status
15897We store the metrics in the "Status" column as:
15898
15899 spins=N,waits=N,calls=N"
15900
15901The user has to parse the dataunfortunately
15902@param[in,out] thd the MySQL query thread of the caller
15903@param[in,out] stat_print function for printing statistics
15904@return true on success. */
15905bool
15906ShowStatus::to_string(
15907 THD* thd,
15908 stat_print_fn* stat_print)
15909 UNIV_NOTHROW
15910{
15911 uint hton_name_len = (uint) strlen(innobase_hton_name);
15912
15913 std::sort(m_values.begin(), m_values.end(), OrderByWaits());
15914
15915 Values::iterator end = m_values.end();
15916
15917 for (Values::iterator it = m_values.begin(); it != end; ++it) {
15918
15919 int name_len;
15920 char name_buf[IO_SIZE];
15921
15922 name_len = snprintf(
15923 name_buf, sizeof(name_buf), "%s", it->m_name.c_str());
15924
15925 int status_len;
15926 char status_buf[IO_SIZE];
15927
15928 status_len = snprintf(
15929 status_buf, sizeof(status_buf),
15930 "spins=%lu,waits=%lu,calls=%llu",
15931 static_cast<ulong>(it->m_spins),
15932 static_cast<long>(it->m_waits),
15933 (ulonglong) it->m_calls);
15934
15935 if (stat_print(thd, innobase_hton_name,
15936 hton_name_len,
15937 name_buf, static_cast<uint>(name_len),
15938 status_buf, static_cast<uint>(status_len))) {
15939
15940 return(false);
15941 }
15942 }
15943
15944 return(true);
15945}
15946
15947/** Implements the SHOW MUTEX STATUS command, for mutexes.
15948@param[in,out] hton the innodb handlerton
15949@param[in,out] thd the MySQL query thread of the caller
15950@param[in,out] stat_print function for printing statistics
15951@return 0 on success. */
15952static
15953int
15954innodb_show_mutex_status(
15955 handlerton*
15956#ifndef DBUG_OFF
15957 hton
15958#endif
15959 ,
15960 THD* thd,
15961 stat_print_fn* stat_print)
15962{
15963 DBUG_ENTER("innodb_show_mutex_status");
15964
15965 ShowStatus collector;
15966
15967 DBUG_ASSERT(hton == innodb_hton_ptr);
15968
15969 mutex_monitor.iterate(collector);
15970
15971 if (!collector.to_string(thd, stat_print)) {
15972 DBUG_RETURN(1);
15973 }
15974
15975 DBUG_RETURN(0);
15976}
15977
15978/** Implements the SHOW MUTEX STATUS command.
15979@param[in,out] hton the innodb handlerton
15980@param[in,out] thd the MySQL query thread of the caller
15981@param[in,out] stat_print function for printing statistics
15982@return 0 on success. */
15983static
15984int
15985innodb_show_rwlock_status(
15986 handlerton*
15987#ifndef DBUG_OFF
15988 hton
15989#endif
15990 ,
15991 THD* thd,
15992 stat_print_fn* stat_print)
15993{
15994 DBUG_ENTER("innodb_show_rwlock_status");
15995
15996 rw_lock_t* block_rwlock = NULL;
15997 ulint block_rwlock_oswait_count = 0;
15998 uint hton_name_len = (uint) strlen(innobase_hton_name);
15999
16000 DBUG_ASSERT(hton == innodb_hton_ptr);
16001
16002 mutex_enter(&rw_lock_list_mutex);
16003
16004 for (rw_lock_t* rw_lock = UT_LIST_GET_FIRST(rw_lock_list);
16005 rw_lock != NULL;
16006 rw_lock = UT_LIST_GET_NEXT(list, rw_lock)) {
16007
16008 if (rw_lock->count_os_wait == 0) {
16009 continue;
16010 }
16011
16012 int buf1len;
16013 char buf1[IO_SIZE];
16014
16015 if (rw_lock->is_block_lock) {
16016
16017 block_rwlock = rw_lock;
16018 block_rwlock_oswait_count += rw_lock->count_os_wait;
16019
16020 continue;
16021 }
16022
16023 buf1len = snprintf(
16024 buf1, sizeof buf1, "rwlock: %s:%u",
16025 innobase_basename(rw_lock->cfile_name),
16026 rw_lock->cline);
16027
16028 int buf2len;
16029 char buf2[IO_SIZE];
16030
16031 buf2len = snprintf(
16032 buf2, sizeof buf2, "waits=%u",
16033 rw_lock->count_os_wait);
16034
16035 if (stat_print(thd, innobase_hton_name,
16036 hton_name_len,
16037 buf1, static_cast<uint>(buf1len),
16038 buf2, static_cast<uint>(buf2len))) {
16039
16040 mutex_exit(&rw_lock_list_mutex);
16041
16042 DBUG_RETURN(1);
16043 }
16044 }
16045
16046 if (block_rwlock != NULL) {
16047
16048 int buf1len;
16049 char buf1[IO_SIZE];
16050
16051 buf1len = snprintf(
16052 buf1, sizeof buf1, "sum rwlock: %s:%u",
16053 innobase_basename(block_rwlock->cfile_name),
16054 block_rwlock->cline);
16055
16056 int buf2len;
16057 char buf2[IO_SIZE];
16058
16059 buf2len = snprintf(
16060 buf2, sizeof buf2, "waits=" ULINTPF,
16061 block_rwlock_oswait_count);
16062
16063 if (stat_print(thd, innobase_hton_name,
16064 hton_name_len,
16065 buf1, static_cast<uint>(buf1len),
16066 buf2, static_cast<uint>(buf2len))) {
16067
16068 mutex_exit(&rw_lock_list_mutex);
16069
16070 DBUG_RETURN(1);
16071 }
16072 }
16073
16074 mutex_exit(&rw_lock_list_mutex);
16075
16076 DBUG_RETURN(0);
16077}
16078
16079/** Implements the SHOW MUTEX STATUS command.
16080@param[in,out] hton the innodb handlerton
16081@param[in,out] thd the MySQL query thread of the caller
16082@param[in,out] stat_print function for printing statistics
16083@return 0 on success. */
16084static
16085int
16086innodb_show_latch_status(
16087 handlerton* hton,
16088 THD* thd,
16089 stat_print_fn* stat_print)
16090{
16091 int ret = innodb_show_mutex_status(hton, thd, stat_print);
16092
16093 if (ret != 0) {
16094 return(ret);
16095 }
16096
16097 return(innodb_show_rwlock_status(hton, thd, stat_print));
16098}
16099
16100/************************************************************************//**
16101Return 0 on success and non-zero on failure. Note: the bool return type
16102seems to be abused here, should be an int. */
16103static
16104bool
16105innobase_show_status(
16106/*=================*/
16107 handlerton* hton, /*!< in: the innodb handlerton */
16108 THD* thd, /*!< in: the MySQL query thread
16109 of the caller */
16110 stat_print_fn* stat_print,
16111 enum ha_stat_type stat_type)
16112{
16113 DBUG_ASSERT(hton == innodb_hton_ptr);
16114
16115 switch (stat_type) {
16116 case HA_ENGINE_STATUS:
16117 /* Non-zero return value means there was an error. */
16118 return(innodb_show_status(hton, thd, stat_print) != 0);
16119
16120 case HA_ENGINE_MUTEX:
16121 return(innodb_show_latch_status(hton, thd, stat_print) != 0);
16122
16123 case HA_ENGINE_LOGS:
16124 /* Not handled */
16125 break;
16126 }
16127
16128 /* Success */
16129 return(false);
16130}
16131
16132/************************************************************************//**
16133Handling the shared INNOBASE_SHARE structure that is needed to provide table
16134locking. Register the table name if it doesn't exist in the hash table. */
16135static
16136INNOBASE_SHARE*
16137get_share(
16138/*======*/
16139 const char* table_name)
16140{
16141 INNOBASE_SHARE* share;
16142
16143 mysql_mutex_lock(&innobase_share_mutex);
16144
16145 ulint fold = ut_fold_string(table_name);
16146
16147 HASH_SEARCH(table_name_hash, innobase_open_tables, fold,
16148 INNOBASE_SHARE*, share,
16149 ut_ad(share->use_count > 0),
16150 !strcmp(share->table_name, table_name));
16151
16152 if (share == NULL) {
16153
16154 uint length = (uint) strlen(table_name);
16155
16156 /* TODO: invoke HASH_MIGRATE if innobase_open_tables
16157 grows too big */
16158
16159 share = reinterpret_cast<INNOBASE_SHARE*>(
16160 my_malloc(//PSI_INSTRUMENT_ME,
16161 sizeof(*share) + length + 1,
16162 MYF(MY_FAE | MY_ZEROFILL)));
16163
16164 share->table_name = reinterpret_cast<char*>(
16165 memcpy(share + 1, table_name, length + 1));
16166
16167 HASH_INSERT(INNOBASE_SHARE, table_name_hash,
16168 innobase_open_tables, fold, share);
16169
16170 thr_lock_init(&share->lock);
16171
16172 /* Index translation table initialization */
16173 share->idx_trans_tbl.index_mapping = NULL;
16174 share->idx_trans_tbl.index_count = 0;
16175 share->idx_trans_tbl.array_size = 0;
16176 }
16177
16178 ++share->use_count;
16179
16180 mysql_mutex_unlock(&innobase_share_mutex);
16181
16182 return(share);
16183}
16184
16185/************************************************************************//**
16186Free the shared object that was registered with get_share(). */
16187static
16188void
16189free_share(
16190/*=======*/
16191 INNOBASE_SHARE* share) /*!< in/own: table share to free */
16192{
16193 mysql_mutex_lock(&innobase_share_mutex);
16194
16195#ifdef UNIV_DEBUG
16196 INNOBASE_SHARE* share2;
16197 ulint fold = ut_fold_string(share->table_name);
16198
16199 HASH_SEARCH(table_name_hash, innobase_open_tables, fold,
16200 INNOBASE_SHARE*, share2,
16201 ut_ad(share->use_count > 0),
16202 !strcmp(share->table_name, share2->table_name));
16203
16204 ut_a(share2 == share);
16205#endif /* UNIV_DEBUG */
16206
16207 --share->use_count;
16208
16209 if (share->use_count == 0) {
16210 ulint fold = ut_fold_string(share->table_name);
16211
16212 HASH_DELETE(INNOBASE_SHARE, table_name_hash,
16213 innobase_open_tables, fold, share);
16214
16215 thr_lock_delete(&share->lock);
16216
16217 /* Free any memory from index translation table */
16218 ut_free(share->idx_trans_tbl.index_mapping);
16219
16220 my_free(share);
16221
16222 /* TODO: invoke HASH_MIGRATE if innobase_open_tables
16223 shrinks too much */
16224 }
16225
16226 mysql_mutex_unlock(&innobase_share_mutex);
16227}
16228
16229/*********************************************************************//**
16230Returns number of THR_LOCK locks used for one instance of InnoDB table.
16231InnoDB no longer relies on THR_LOCK locks so 0 value is returned.
16232Instead of THR_LOCK locks InnoDB relies on combination of metadata locks
16233(e.g. for LOCK TABLES and DDL) and its own locking subsystem.
16234Note that even though this method returns 0, SQL-layer still calls
16235::store_lock(), ::start_stmt() and ::external_lock() methods for InnoDB
16236tables. */
16237
16238uint
16239ha_innobase::lock_count(void) const
16240/*===============================*/
16241{
16242 return 0;
16243}
16244
16245/*****************************************************************//**
16246Supposed to convert a MySQL table lock stored in the 'lock' field of the
16247handle to a proper type before storing pointer to the lock into an array
16248of pointers.
16249In practice, since InnoDB no longer relies on THR_LOCK locks and its
16250lock_count() method returns 0 it just informs storage engine about type
16251of THR_LOCK which SQL-layer would have acquired for this specific statement
16252on this specific table.
16253MySQL also calls this if it wants to reset some table locks to a not-locked
16254state during the processing of an SQL query. An example is that during a
16255SELECT the read lock is released early on the 'const' tables where we only
16256fetch one row. MySQL does not call this when it releases all locks at the
16257end of an SQL statement.
16258@return pointer to the current element in the 'to' array. */
16259
16260THR_LOCK_DATA**
16261ha_innobase::store_lock(
16262/*====================*/
16263 THD* thd, /*!< in: user thread handle */
16264 THR_LOCK_DATA** to, /*!< in: pointer to the current
16265 element in an array of pointers
16266 to lock structs;
16267 only used as return value */
16268 thr_lock_type lock_type) /*!< in: lock type to store in
16269 'lock'; this may also be
16270 TL_IGNORE */
16271{
16272 /* Note that trx in this function is NOT necessarily m_prebuilt->trx
16273 because we call update_thd() later, in ::external_lock()! Failure to
16274 understand this caused a serious memory corruption bug in 5.1.11. */
16275
16276 trx_t* trx = check_trx_exists(thd);
16277
16278 /* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
16279 Be careful to ignore TL_IGNORE if we are going to do something with
16280 only 'real' locks! */
16281
16282 /* If no MySQL table is in use, we need to set the isolation level
16283 of the transaction. */
16284
16285 if (lock_type != TL_IGNORE
16286 && trx->n_mysql_tables_in_use == 0) {
16287 trx->isolation_level = innobase_map_isolation_level(
16288 (enum_tx_isolation) thd_tx_isolation(thd));
16289
16290 if (trx->isolation_level <= TRX_ISO_READ_COMMITTED) {
16291
16292 /* At low transaction isolation levels we let
16293 each consistent read set its own snapshot */
16294 trx->read_view.close();
16295 }
16296 }
16297
16298 DBUG_ASSERT(EQ_CURRENT_THD(thd));
16299 const bool in_lock_tables = thd_in_lock_tables(thd);
16300 const int sql_command = thd_sql_command(thd);
16301
16302 if (srv_read_only_mode
16303 && (sql_command == SQLCOM_UPDATE
16304 || sql_command == SQLCOM_INSERT
16305 || sql_command == SQLCOM_REPLACE
16306 || sql_command == SQLCOM_DROP_TABLE
16307 || sql_command == SQLCOM_ALTER_TABLE
16308 || sql_command == SQLCOM_OPTIMIZE
16309 || (sql_command == SQLCOM_CREATE_TABLE
16310 && (lock_type >= TL_WRITE_CONCURRENT_INSERT
16311 && lock_type <= TL_WRITE))
16312 || sql_command == SQLCOM_CREATE_INDEX
16313 || sql_command == SQLCOM_DROP_INDEX
16314 || sql_command == SQLCOM_CREATE_SEQUENCE
16315 || sql_command == SQLCOM_DROP_SEQUENCE
16316 || sql_command == SQLCOM_DELETE)) {
16317
16318 ib_senderrf(trx->mysql_thd,
16319 IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
16320
16321 } else if (sql_command == SQLCOM_FLUSH
16322 && lock_type == TL_READ_NO_INSERT) {
16323
16324 /* Check for FLUSH TABLES ... WITH READ LOCK */
16325
16326 /* Note: This call can fail, but there is no way to return
16327 the error to the caller. We simply ignore it for now here
16328 and push the error code to the caller where the error is
16329 detected in the function. */
16330
16331 dberr_t err = row_quiesce_set_state(
16332 m_prebuilt->table, QUIESCE_START, trx);
16333
16334 ut_a(err == DB_SUCCESS || err == DB_UNSUPPORTED);
16335
16336 if (trx->isolation_level == TRX_ISO_SERIALIZABLE) {
16337 m_prebuilt->select_lock_type = LOCK_S;
16338 m_prebuilt->stored_select_lock_type = LOCK_S;
16339 } else {
16340 m_prebuilt->select_lock_type = LOCK_NONE;
16341 m_prebuilt->stored_select_lock_type = LOCK_NONE;
16342 }
16343
16344 /* Check for DROP TABLE */
16345 } else if (sql_command == SQLCOM_DROP_TABLE ||
16346 sql_command == SQLCOM_DROP_SEQUENCE) {
16347
16348 /* MySQL calls this function in DROP TABLE though this table
16349 handle may belong to another thd that is running a query. Let
16350 us in that case skip any changes to the m_prebuilt struct. */
16351
16352 /* Check for LOCK TABLE t1,...,tn WITH SHARED LOCKS */
16353 } else if ((lock_type == TL_READ && in_lock_tables)
16354 || (lock_type == TL_READ_HIGH_PRIORITY && in_lock_tables)
16355 || lock_type == TL_READ_WITH_SHARED_LOCKS
16356 || lock_type == TL_READ_NO_INSERT
16357 || (lock_type != TL_IGNORE
16358 && sql_command != SQLCOM_SELECT)) {
16359
16360 /* The OR cases above are in this order:
16361 1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
16362 are processing a stored procedure or function, or
16363 2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
16364 3) this is a SELECT ... IN SHARE MODE, or
16365 4) we are doing a complex SQL statement like
16366 INSERT INTO ... SELECT ... and the logical logging (MySQL
16367 binlog) requires the use of a locking read, or
16368 MySQL is doing LOCK TABLES ... READ.
16369 5) we let InnoDB do locking reads for all SQL statements that
16370 are not simple SELECTs; note that select_lock_type in this
16371 case may get strengthened in ::external_lock() to LOCK_X.
16372 Note that we MUST use a locking read in all data modifying
16373 SQL statements, because otherwise the execution would not be
16374 serializable, and also the results from the update could be
16375 unexpected if an obsolete consistent read view would be
16376 used. */
16377
16378 /* Use consistent read for checksum table */
16379
16380 if (sql_command == SQLCOM_CHECKSUM
16381 || sql_command == SQLCOM_CREATE_SEQUENCE
16382 || (sql_command == SQLCOM_ANALYZE && lock_type == TL_READ)
16383 || ((srv_locks_unsafe_for_binlog
16384 || trx->isolation_level <= TRX_ISO_READ_COMMITTED)
16385 && trx->isolation_level != TRX_ISO_SERIALIZABLE
16386 && (lock_type == TL_READ
16387 || lock_type == TL_READ_NO_INSERT)
16388 && (sql_command == SQLCOM_INSERT_SELECT
16389 || sql_command == SQLCOM_REPLACE_SELECT
16390 || sql_command == SQLCOM_UPDATE
16391 || sql_command == SQLCOM_CREATE_SEQUENCE
16392 || sql_command == SQLCOM_CREATE_TABLE))) {
16393
16394 /* If we either have innobase_locks_unsafe_for_binlog
16395 option set or this session is using READ COMMITTED
16396 isolation level and isolation level of the transaction
16397 is not set to serializable and MySQL is doing
16398 INSERT INTO...SELECT or REPLACE INTO...SELECT
16399 or UPDATE ... = (SELECT ...) or CREATE ...
16400 SELECT... without FOR UPDATE or IN SHARE
16401 MODE in select, then we use consistent read
16402 for select. */
16403
16404 m_prebuilt->select_lock_type = LOCK_NONE;
16405 m_prebuilt->stored_select_lock_type = LOCK_NONE;
16406 } else {
16407 m_prebuilt->select_lock_type = LOCK_S;
16408 m_prebuilt->stored_select_lock_type = LOCK_S;
16409 }
16410
16411 } else if (lock_type != TL_IGNORE) {
16412
16413 /* We set possible LOCK_X value in external_lock, not yet
16414 here even if this would be SELECT ... FOR UPDATE */
16415
16416 m_prebuilt->select_lock_type = LOCK_NONE;
16417 m_prebuilt->stored_select_lock_type = LOCK_NONE;
16418 }
16419
16420 if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
16421
16422 /* Starting from 5.0.7, we weaken also the table locks
16423 set at the start of a MySQL stored procedure call, just like
16424 we weaken the locks set at the start of an SQL statement.
16425 MySQL does set in_lock_tables TRUE there, but in reality
16426 we do not need table locks to make the execution of a
16427 single transaction stored procedure call deterministic
16428 (if it does not use a consistent read). */
16429
16430 if (lock_type == TL_READ
16431 && sql_command == SQLCOM_LOCK_TABLES) {
16432 /* We come here if MySQL is processing LOCK TABLES
16433 ... READ LOCAL. MyISAM under that table lock type
16434 reads the table as it was at the time the lock was
16435 granted (new inserts are allowed, but not seen by the
16436 reader). To get a similar effect on an InnoDB table,
16437 we must use LOCK TABLES ... READ. We convert the lock
16438 type here, so that for InnoDB, READ LOCAL is
16439 equivalent to READ. This will change the InnoDB
16440 behavior in mysqldump, so that dumps of InnoDB tables
16441 are consistent with dumps of MyISAM tables. */
16442
16443 lock_type = TL_READ_NO_INSERT;
16444 }
16445
16446 /* If we are not doing a LOCK TABLE, DISCARD/IMPORT
16447 TABLESPACE or TRUNCATE TABLE then allow multiple
16448 writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
16449 < TL_WRITE_CONCURRENT_INSERT.
16450
16451 We especially allow multiple writers if MySQL is at the
16452 start of a stored procedure call (SQLCOM_CALL) or a
16453 stored function call (MySQL does have in_lock_tables
16454 TRUE there). */
16455
16456 if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
16457 && lock_type <= TL_WRITE)
16458 && !(in_lock_tables
16459 && sql_command == SQLCOM_LOCK_TABLES)
16460 && !thd_tablespace_op(thd)
16461 && sql_command != SQLCOM_TRUNCATE
16462 && sql_command != SQLCOM_OPTIMIZE
16463 && sql_command != SQLCOM_CREATE_TABLE) {
16464
16465 lock_type = TL_WRITE_ALLOW_WRITE;
16466 }
16467
16468 /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
16469 MySQL would use the lock TL_READ_NO_INSERT on t2, and that
16470 would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
16471 to t2. Convert the lock to a normal read lock to allow
16472 concurrent inserts to t2.
16473
16474 We especially allow concurrent inserts if MySQL is at the
16475 start of a stored procedure call (SQLCOM_CALL)
16476 (MySQL does have thd_in_lock_tables() TRUE there). */
16477
16478 if (lock_type == TL_READ_NO_INSERT
16479 && sql_command != SQLCOM_LOCK_TABLES) {
16480
16481 lock_type = TL_READ;
16482 }
16483
16484 lock.type = lock_type;
16485 }
16486
16487 if (!trx_is_started(trx)
16488 && (m_prebuilt->select_lock_type != LOCK_NONE
16489 || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
16490
16491 ++trx->will_lock;
16492 }
16493
16494 return(to);
16495}
16496
16497/*********************************************************************//**
16498Read the next autoinc value. Acquire the relevant locks before reading
16499the AUTOINC value. If SUCCESS then the table AUTOINC mutex will be locked
16500on return and all relevant locks acquired.
16501@return DB_SUCCESS or error code */
16502
16503dberr_t
16504ha_innobase::innobase_get_autoinc(
16505/*==============================*/
16506 ulonglong* value) /*!< out: autoinc value */
16507{
16508 *value = 0;
16509
16510 m_prebuilt->autoinc_error = innobase_lock_autoinc();
16511
16512 if (m_prebuilt->autoinc_error == DB_SUCCESS) {
16513
16514 /* Determine the first value of the interval */
16515 *value = dict_table_autoinc_read(m_prebuilt->table);
16516
16517 /* It should have been initialized during open. */
16518 if (*value == 0) {
16519 m_prebuilt->autoinc_error = DB_UNSUPPORTED;
16520 dict_table_autoinc_unlock(m_prebuilt->table);
16521 }
16522 }
16523
16524 return(m_prebuilt->autoinc_error);
16525}
16526
16527/*******************************************************************//**
16528This function reads the global auto-inc counter. It doesn't use the
16529AUTOINC lock even if the lock mode is set to TRADITIONAL.
16530@return the autoinc value */
16531
16532ulonglong
16533ha_innobase::innobase_peek_autoinc(void)
16534/*====================================*/
16535{
16536 ulonglong auto_inc;
16537 dict_table_t* innodb_table;
16538
16539 ut_a(m_prebuilt != NULL);
16540 ut_a(m_prebuilt->table != NULL);
16541
16542 innodb_table = m_prebuilt->table;
16543
16544 dict_table_autoinc_lock(innodb_table);
16545
16546 auto_inc = dict_table_autoinc_read(innodb_table);
16547
16548 if (auto_inc == 0) {
16549 ib::info() << "AUTOINC next value generation is disabled for"
16550 " '" << innodb_table->name << "'";
16551 }
16552
16553 dict_table_autoinc_unlock(innodb_table);
16554
16555 return(auto_inc);
16556}
16557
16558/*********************************************************************//**
16559Returns the value of the auto-inc counter in *first_value and ~0 on failure. */
16560
16561void
16562ha_innobase::get_auto_increment(
16563/*============================*/
16564 ulonglong offset, /*!< in: table autoinc offset */
16565 ulonglong increment, /*!< in: table autoinc
16566 increment */
16567 ulonglong nb_desired_values, /*!< in: number of values
16568 reqd */
16569 ulonglong* first_value, /*!< out: the autoinc value */
16570 ulonglong* nb_reserved_values) /*!< out: count of reserved
16571 values */
16572{
16573 trx_t* trx;
16574 dberr_t error;
16575 ulonglong autoinc = 0;
16576
16577 /* Prepare m_prebuilt->trx in the table handle */
16578 update_thd(ha_thd());
16579
16580 error = innobase_get_autoinc(&autoinc);
16581
16582 if (error != DB_SUCCESS) {
16583 *first_value = (~(ulonglong) 0);
16584 return;
16585 }
16586
16587 /* This is a hack, since nb_desired_values seems to be accurate only
16588 for the first call to get_auto_increment() for multi-row INSERT and
16589 meaningless for other statements e.g, LOAD etc. Subsequent calls to
16590 this method for the same statement results in different values which
16591 don't make sense. Therefore we store the value the first time we are
16592 called and count down from that as rows are written (see write_row()).
16593 */
16594
16595 trx = m_prebuilt->trx;
16596
16597 /* Note: We can't rely on *first_value since some MySQL engines,
16598 in particular the partition engine, don't initialize it to 0 when
16599 invoking this method. So we are not sure if it's guaranteed to
16600 be 0 or not. */
16601
16602 /* We need the upper limit of the col type to check for
16603 whether we update the table autoinc counter or not. */
16604 ulonglong col_max_value = innobase_get_int_col_max_value(table->next_number_field);
16605
16606 /** The following logic is needed to avoid duplicate key error
16607 for autoincrement column.
16608
16609 (1) InnoDB gives the current autoincrement value with respect
16610 to increment and offset value.
16611
16612 (2) Basically it does compute_next_insert_id() logic inside InnoDB
16613 to avoid the current auto increment value changed by handler layer.
16614
16615 (3) It is restricted only for insert operations. */
16616
16617 if (increment > 1 && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE
16618 && autoinc < col_max_value) {
16619
16620 ulonglong prev_auto_inc = autoinc;
16621
16622 autoinc = ((autoinc - 1) + increment - offset)/ increment;
16623
16624 autoinc = autoinc * increment + offset;
16625
16626 /* If autoinc exceeds the col_max_value then reset
16627 to old autoinc value. Because in case of non-strict
16628 sql mode, boundary value is not considered as error. */
16629
16630 if (autoinc >= col_max_value) {
16631 autoinc = prev_auto_inc;
16632 }
16633
16634 ut_ad(autoinc > 0);
16635 }
16636
16637 /* Called for the first time ? */
16638 if (trx->n_autoinc_rows == 0) {
16639
16640 trx->n_autoinc_rows = (ulint) nb_desired_values;
16641
16642 /* It's possible for nb_desired_values to be 0:
16643 e.g., INSERT INTO T1(C) SELECT C FROM T2; */
16644 if (nb_desired_values == 0) {
16645
16646 trx->n_autoinc_rows = 1;
16647 }
16648
16649 set_if_bigger(*first_value, autoinc);
16650 /* Not in the middle of a mult-row INSERT. */
16651 } else if (m_prebuilt->autoinc_last_value == 0) {
16652 set_if_bigger(*first_value, autoinc);
16653 }
16654
16655 if (*first_value > col_max_value) {
16656 /* Out of range number. Let handler::update_auto_increment()
16657 take care of this */
16658 m_prebuilt->autoinc_last_value = 0;
16659 dict_table_autoinc_unlock(m_prebuilt->table);
16660 *nb_reserved_values= 0;
16661 return;
16662 }
16663
16664 *nb_reserved_values = trx->n_autoinc_rows;
16665
16666 /* With old style AUTOINC locking we only update the table's
16667 AUTOINC counter after attempting to insert the row. */
16668 if (innobase_autoinc_lock_mode != AUTOINC_OLD_STYLE_LOCKING) {
16669 ulonglong current;
16670 ulonglong next_value;
16671
16672 current = *first_value;
16673
16674 if (m_prebuilt->autoinc_increment != increment) {
16675
16676 WSREP_DEBUG("autoinc decrease: %llu -> %llu\n"
16677 "THD: %ld, current: %llu, autoinc: %llu",
16678 m_prebuilt->autoinc_increment,
16679 increment,
16680 thd_get_thread_id(ha_thd()),
16681 current, autoinc);
16682
16683 if (!wsrep_on(ha_thd())) {
16684 current = autoinc - m_prebuilt->autoinc_increment;
16685 }
16686
16687 current = innobase_next_autoinc(
16688 current, 1, increment, offset, col_max_value);
16689
16690 dict_table_autoinc_initialize(
16691 m_prebuilt->table, current);
16692
16693 *first_value = current;
16694 }
16695
16696 /* Compute the last value in the interval */
16697 next_value = innobase_next_autoinc(
16698 current, *nb_reserved_values, increment, offset,
16699 col_max_value);
16700
16701 m_prebuilt->autoinc_last_value = next_value;
16702
16703 if (m_prebuilt->autoinc_last_value < *first_value) {
16704 *first_value = (~(ulonglong) 0);
16705 } else {
16706 /* Update the table autoinc variable */
16707 dict_table_autoinc_update_if_greater(
16708 m_prebuilt->table,
16709 m_prebuilt->autoinc_last_value);
16710 }
16711 } else {
16712 /* This will force write_row() into attempting an update
16713 of the table's AUTOINC counter. */
16714 m_prebuilt->autoinc_last_value = 0;
16715 }
16716
16717 /* The increment to be used to increase the AUTOINC value, we use
16718 this in write_row() and update_row() to increase the autoinc counter
16719 for columns that are filled by the user. We need the offset and
16720 the increment. */
16721 m_prebuilt->autoinc_offset = offset;
16722 m_prebuilt->autoinc_increment = increment;
16723
16724 dict_table_autoinc_unlock(m_prebuilt->table);
16725}
16726
16727/*******************************************************************//**
16728See comment in handler.cc */
16729
16730bool
16731ha_innobase::get_error_message(
16732/*===========================*/
16733 int error,
16734 String* buf)
16735{
16736 trx_t* trx = check_trx_exists(ha_thd());
16737
16738 if (error == HA_ERR_DECRYPTION_FAILED) {
16739 const char *msg = "Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.";
16740 buf->copy(msg, (uint)strlen(msg), system_charset_info);
16741 } else {
16742 buf->copy(trx->detailed_error, (uint) strlen(trx->detailed_error),
16743 system_charset_info);
16744 }
16745
16746 return(FALSE);
16747}
16748
16749/** Retrieves the names of the table and the key for which there was a
16750duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.
16751
16752If any of the names is not available, then this method will return
16753false and will not change any of child_table_name or child_key_name.
16754
16755@param[out] child_table_name Table name
16756@param[in] child_table_name_len Table name buffer size
16757@param[out] child_key_name Key name
16758@param[in] child_key_name_len Key name buffer size
16759
16760@retval true table and key names were available and were written into the
16761corresponding out parameters.
16762@retval false table and key names were not available, the out parameters
16763were not touched. */
16764bool
16765ha_innobase::get_foreign_dup_key(
16766/*=============================*/
16767 char* child_table_name,
16768 uint child_table_name_len,
16769 char* child_key_name,
16770 uint child_key_name_len)
16771{
16772 const dict_index_t* err_index;
16773
16774 ut_a(m_prebuilt->trx != NULL);
16775 ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
16776
16777 err_index = trx_get_error_info(m_prebuilt->trx);
16778
16779 if (err_index == NULL) {
16780 return(false);
16781 }
16782 /* else */
16783
16784 /* copy table name (and convert from filename-safe encoding to
16785 system_charset_info) */
16786 char* p = strchr(err_index->table->name.m_name, '/');
16787
16788 /* strip ".../" prefix if any */
16789 if (p != NULL) {
16790 p++;
16791 } else {
16792 p = err_index->table->name.m_name;
16793 }
16794
16795 size_t len;
16796
16797 len = filename_to_tablename(p, child_table_name, child_table_name_len);
16798
16799 child_table_name[len] = '\0';
16800
16801 /* copy index name */
16802 snprintf(child_key_name, child_key_name_len, "%s",
16803 err_index->name());
16804
16805 return(true);
16806}
16807
16808/*******************************************************************//**
16809Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
16810If there is no explicitly declared non-null unique key or a primary key, then
16811InnoDB internally uses the row id as the primary key.
16812@return < 0 if ref1 < ref2, 0 if equal, else > 0 */
16813
16814int
16815ha_innobase::cmp_ref(
16816/*=================*/
16817 const uchar* ref1, /*!< in: an (internal) primary key value in the
16818 MySQL key value format */
16819 const uchar* ref2) /*!< in: an (internal) primary key value in the
16820 MySQL key value format */
16821{
16822 enum_field_types mysql_type;
16823 Field* field;
16824 KEY_PART_INFO* key_part;
16825 KEY_PART_INFO* key_part_end;
16826 uint len1;
16827 uint len2;
16828 int result;
16829
16830 if (m_prebuilt->clust_index_was_generated) {
16831 /* The 'ref' is an InnoDB row id */
16832
16833 return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
16834 }
16835
16836 /* Do a type-aware comparison of primary key fields. PK fields
16837 are always NOT NULL, so no checks for NULL are performed. */
16838
16839 key_part = table->key_info[table->s->primary_key].key_part;
16840
16841 key_part_end = key_part
16842 + table->key_info[table->s->primary_key].user_defined_key_parts;
16843
16844 for (; key_part != key_part_end; ++key_part) {
16845 field = key_part->field;
16846 mysql_type = field->type();
16847
16848 if (mysql_type == MYSQL_TYPE_TINY_BLOB
16849 || mysql_type == MYSQL_TYPE_MEDIUM_BLOB
16850 || mysql_type == MYSQL_TYPE_BLOB
16851 || mysql_type == MYSQL_TYPE_LONG_BLOB) {
16852
16853 /* In the MySQL key value format, a column prefix of
16854 a BLOB is preceded by a 2-byte length field */
16855
16856 len1 = innobase_read_from_2_little_endian(ref1);
16857 len2 = innobase_read_from_2_little_endian(ref2);
16858
16859 result = ((Field_blob*) field)->cmp(
16860 ref1 + 2, len1, ref2 + 2, len2);
16861 } else {
16862 result = field->key_cmp(ref1, ref2);
16863 }
16864
16865 if (result) {
16866
16867 return(result);
16868 }
16869
16870 ref1 += key_part->store_length;
16871 ref2 += key_part->store_length;
16872 }
16873
16874 return(0);
16875}
16876
16877/*******************************************************************//**
16878Ask InnoDB if a query to a table can be cached.
16879@return TRUE if query caching of the table is permitted */
16880
16881my_bool
16882ha_innobase::register_query_cache_table(
16883/*====================================*/
16884 THD* thd, /*!< in: user thread handle */
16885 const char* table_key, /*!< in: normalized path to the
16886 table */
16887 uint key_length, /*!< in: length of the normalized
16888 path to the table */
16889 qc_engine_callback*
16890 call_back, /*!< out: pointer to function for
16891 checking if query caching
16892 is permitted */
16893 ulonglong *engine_data) /*!< in/out: data to call_back */
16894{
16895 *engine_data = 0;
16896 *call_back = innobase_query_caching_of_table_permitted;
16897
16898 return(innobase_query_caching_of_table_permitted(
16899 thd, table_key,
16900 static_cast<uint>(key_length),
16901 engine_data));
16902}
16903
16904/******************************************************************//**
16905This function is used to find the storage length in bytes of the first n
16906characters for prefix indexes using a multibyte character set. The function
16907finds charset information and returns length of prefix_len characters in the
16908index field in bytes.
16909@return number of bytes occupied by the first n characters */
16910ulint
16911innobase_get_at_most_n_mbchars(
16912/*===========================*/
16913 ulint charset_id, /*!< in: character set id */
16914 ulint prefix_len, /*!< in: prefix length in bytes of the index
16915 (this has to be divided by mbmaxlen to get the
16916 number of CHARACTERS n in the prefix) */
16917 ulint data_len, /*!< in: length of the string in bytes */
16918 const char* str) /*!< in: character string */
16919{
16920 ulint char_length; /*!< character length in bytes */
16921 ulint n_chars; /*!< number of characters in prefix */
16922 CHARSET_INFO* charset; /*!< charset used in the field */
16923
16924 charset = get_charset((uint) charset_id, MYF(MY_WME));
16925
16926 ut_ad(charset);
16927 ut_ad(charset->mbmaxlen);
16928
16929 /* Calculate how many characters at most the prefix index contains */
16930
16931 n_chars = prefix_len / charset->mbmaxlen;
16932
16933 /* If the charset is multi-byte, then we must find the length of the
16934 first at most n chars in the string. If the string contains less
16935 characters than n, then we return the length to the end of the last
16936 character. */
16937
16938 if (charset->mbmaxlen > 1) {
16939 /* my_charpos() returns the byte length of the first n_chars
16940 characters, or a value bigger than the length of str, if
16941 there were not enough full characters in str.
16942
16943 Why does the code below work:
16944 Suppose that we are looking for n UTF-8 characters.
16945
16946 1) If the string is long enough, then the prefix contains at
16947 least n complete UTF-8 characters + maybe some extra
16948 characters + an incomplete UTF-8 character. No problem in
16949 this case. The function returns the pointer to the
16950 end of the nth character.
16951
16952 2) If the string is not long enough, then the string contains
16953 the complete value of a column, that is, only complete UTF-8
16954 characters, and we can store in the column prefix index the
16955 whole string. */
16956
16957 char_length= my_charpos(charset, str, str + data_len, n_chars);
16958 if (char_length > data_len) {
16959 char_length = data_len;
16960 }
16961 } else if (data_len < prefix_len) {
16962
16963 char_length = data_len;
16964
16965 } else {
16966
16967 char_length = prefix_len;
16968 }
16969
16970 return(char_length);
16971}
16972
16973/*******************************************************************//**
16974This function is used to prepare an X/Open XA distributed transaction.
16975@return 0 or error number */
16976static
16977int
16978innobase_xa_prepare(
16979/*================*/
16980 handlerton* hton, /*!< in: InnoDB handlerton */
16981 THD* thd, /*!< in: handle to the MySQL thread of
16982 the user whose XA transaction should
16983 be prepared */
16984 bool prepare_trx) /*!< in: true - prepare transaction
16985 false - the current SQL statement
16986 ended */
16987{
16988 trx_t* trx = check_trx_exists(thd);
16989
16990 DBUG_ASSERT(hton == innodb_hton_ptr);
16991
16992 thd_get_xid(thd, (MYSQL_XID*) trx->xid);
16993
16994 innobase_srv_conc_force_exit_innodb(trx);
16995
16996 if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
16997
16998 sql_print_error("Transaction not registered for MariaDB 2PC,"
16999 " but transaction is active");
17000 }
17001
17002 if (prepare_trx
17003 || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
17004
17005 /* We were instructed to prepare the whole transaction, or
17006 this is an SQL statement end and autocommit is on */
17007
17008 ut_ad(trx_is_registered_for_2pc(trx));
17009
17010 trx_prepare_for_mysql(trx);
17011 } else {
17012 /* We just mark the SQL statement ended and do not do a
17013 transaction prepare */
17014
17015 /* If we had reserved the auto-inc lock for some
17016 table in this SQL statement we release it now */
17017
17018 lock_unlock_table_autoinc(trx);
17019
17020 /* Store the current undo_no of the transaction so that we
17021 know where to roll back if we have to roll back the next
17022 SQL statement */
17023
17024 trx_mark_sql_stat_end(trx);
17025 }
17026
17027 if (thd_sql_command(thd) != SQLCOM_XA_PREPARE
17028 && (prepare_trx
17029 || !thd_test_options(
17030 thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
17031
17032 /* For mysqlbackup to work the order of transactions in binlog
17033 and InnoDB must be the same. Consider the situation
17034
17035 thread1> prepare; write to binlog; ...
17036 <context switch>
17037 thread2> prepare; write to binlog; commit
17038 thread1> ... commit
17039
17040 The server guarantees that writes to the binary log
17041 and commits are in the same order, so we do not have
17042 to handle this case. */
17043 }
17044
17045 return(0);
17046}
17047
17048/*******************************************************************//**
17049This function is used to recover X/Open XA distributed transactions.
17050@return number of prepared transactions stored in xid_list */
17051static
17052int
17053innobase_xa_recover(
17054/*================*/
17055 handlerton* hton, /*!< in: InnoDB handlerton */
17056 XID* xid_list,/*!< in/out: prepared transactions */
17057 uint len) /*!< in: number of slots in xid_list */
17058{
17059 DBUG_ASSERT(hton == innodb_hton_ptr);
17060
17061 if (len == 0 || xid_list == NULL) {
17062
17063 return(0);
17064 }
17065
17066 return(trx_recover_for_mysql(xid_list, len));
17067}
17068
17069/*******************************************************************//**
17070This function is used to commit one X/Open XA distributed transaction
17071which is in the prepared state
17072@return 0 or error number */
17073static
17074int
17075innobase_commit_by_xid(
17076/*===================*/
17077 handlerton* hton,
17078 XID* xid) /*!< in: X/Open XA transaction identification */
17079{
17080 DBUG_ASSERT(hton == innodb_hton_ptr);
17081
17082 if (high_level_read_only) {
17083 return(XAER_RMFAIL);
17084 }
17085
17086 if (trx_t* trx = trx_get_trx_by_xid(xid)) {
17087 /* use cases are: disconnected xa, slave xa, recovery */
17088 innobase_commit_low(trx);
17089 ut_ad(trx->mysql_thd == NULL);
17090 trx_deregister_from_2pc(trx);
17091 ut_ad(!trx->will_lock); /* trx cache requirement */
17092 trx_free(trx);
17093
17094 return(XA_OK);
17095 } else {
17096 return(XAER_NOTA);
17097 }
17098}
17099
17100/*******************************************************************//**
17101This function is used to rollback one X/Open XA distributed transaction
17102which is in the prepared state
17103@return 0 or error number */
17104static
17105int
17106innobase_rollback_by_xid(
17107/*=====================*/
17108 handlerton* hton, /*!< in: InnoDB handlerton */
17109 XID* xid) /*!< in: X/Open XA transaction
17110 identification */
17111{
17112 DBUG_ASSERT(hton == innodb_hton_ptr);
17113
17114 if (high_level_read_only) {
17115 return(XAER_RMFAIL);
17116 }
17117
17118 if (trx_t* trx = trx_get_trx_by_xid(xid)) {
17119 int ret = innobase_rollback_trx(trx);
17120 trx_deregister_from_2pc(trx);
17121 ut_ad(!trx->will_lock);
17122 trx_free(trx);
17123
17124 return(ret);
17125 } else {
17126 return(XAER_NOTA);
17127 }
17128}
17129
17130bool
17131ha_innobase::check_if_incompatible_data(
17132/*====================================*/
17133 HA_CREATE_INFO* info,
17134 uint table_changes)
17135{
17136 ha_table_option_struct *param_old, *param_new;
17137
17138 /* Cache engine specific options */
17139 param_new = info->option_struct;
17140 param_old = table->s->option_struct;
17141
17142 innobase_copy_frm_flags_from_create_info(m_prebuilt->table, info);
17143
17144 if (table_changes != IS_EQUAL_YES) {
17145
17146 return(COMPATIBLE_DATA_NO);
17147 }
17148
17149 /* Check that auto_increment value was not changed */
17150 if ((info->used_fields & HA_CREATE_USED_AUTO)
17151 && info->auto_increment_value != 0) {
17152
17153 return(COMPATIBLE_DATA_NO);
17154 }
17155
17156 /* Check that row format didn't change */
17157 if ((info->used_fields & HA_CREATE_USED_ROW_FORMAT)
17158 && info->row_type != get_row_type()) {
17159
17160 return(COMPATIBLE_DATA_NO);
17161 }
17162
17163 /* Specifying KEY_BLOCK_SIZE requests a rebuild of the table. */
17164 if (info->used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE) {
17165 return(COMPATIBLE_DATA_NO);
17166 }
17167
17168 /* Changes on engine specific table options requests a rebuild of the table. */
17169 if (param_new->page_compressed != param_old->page_compressed ||
17170 param_new->page_compression_level != param_old->page_compression_level)
17171 {
17172 return(COMPATIBLE_DATA_NO);
17173 }
17174
17175 return(COMPATIBLE_DATA_YES);
17176}
17177
17178/****************************************************************//**
17179Update the system variable innodb_io_capacity_max using the "saved"
17180value. This function is registered as a callback with MySQL. */
17181static
17182void
17183innodb_io_capacity_max_update(
17184/*===========================*/
17185 THD* thd, /*!< in: thread handle */
17186 st_mysql_sys_var*, void*,
17187 const void* save) /*!< in: immediate result
17188 from check function */
17189{
17190 ulong in_val = *static_cast<const ulong*>(save);
17191
17192 if (in_val < srv_io_capacity) {
17193 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17194 ER_WRONG_ARGUMENTS,
17195 "Setting innodb_io_capacity_max %lu"
17196 " lower than innodb_io_capacity %lu.",
17197 in_val, srv_io_capacity);
17198
17199 srv_io_capacity = in_val;
17200
17201 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17202 ER_WRONG_ARGUMENTS,
17203 "Setting innodb_io_capacity to %lu",
17204 srv_io_capacity);
17205 }
17206
17207 srv_max_io_capacity = in_val;
17208}
17209
17210/****************************************************************//**
17211Update the system variable innodb_io_capacity using the "saved"
17212value. This function is registered as a callback with MySQL. */
17213static
17214void
17215innodb_io_capacity_update(
17216/*======================*/
17217 THD* thd, /*!< in: thread handle */
17218 st_mysql_sys_var*, void*,
17219 const void* save) /*!< in: immediate result
17220 from check function */
17221{
17222 ulong in_val = *static_cast<const ulong*>(save);
17223
17224 if (in_val > srv_max_io_capacity) {
17225 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17226 ER_WRONG_ARGUMENTS,
17227 "Setting innodb_io_capacity to %lu"
17228 " higher than innodb_io_capacity_max %lu",
17229 in_val, srv_max_io_capacity);
17230
17231 srv_max_io_capacity = in_val * 2;
17232
17233 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17234 ER_WRONG_ARGUMENTS,
17235 "Setting innodb_max_io_capacity to %lu",
17236 srv_max_io_capacity);
17237 }
17238
17239 srv_io_capacity = in_val;
17240}
17241
17242/****************************************************************//**
17243Update the system variable innodb_max_dirty_pages_pct using the "saved"
17244value. This function is registered as a callback with MySQL. */
17245static
17246void
17247innodb_max_dirty_pages_pct_update(
17248/*==============================*/
17249 THD* thd, /*!< in: thread handle */
17250 st_mysql_sys_var*, void*,
17251 const void* save) /*!< in: immediate result
17252 from check function */
17253{
17254 double in_val = *static_cast<const double*>(save);
17255 if (in_val < srv_max_dirty_pages_pct_lwm) {
17256 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17257 ER_WRONG_ARGUMENTS,
17258 "innodb_max_dirty_pages_pct cannot be"
17259 " set lower than"
17260 " innodb_max_dirty_pages_pct_lwm.");
17261 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17262 ER_WRONG_ARGUMENTS,
17263 "Lowering"
17264 " innodb_max_dirty_page_pct_lwm to %lf",
17265 in_val);
17266
17267 srv_max_dirty_pages_pct_lwm = in_val;
17268 }
17269
17270 srv_max_buf_pool_modified_pct = in_val;
17271}
17272
17273/****************************************************************//**
17274Update the system variable innodb_max_dirty_pages_pct_lwm using the
17275"saved" value. This function is registered as a callback with MySQL. */
17276static
17277void
17278innodb_max_dirty_pages_pct_lwm_update(
17279/*==================================*/
17280 THD* thd, /*!< in: thread handle */
17281 st_mysql_sys_var*, void*,
17282 const void* save) /*!< in: immediate result
17283 from check function */
17284{
17285 double in_val = *static_cast<const double*>(save);
17286 if (in_val > srv_max_buf_pool_modified_pct) {
17287 in_val = srv_max_buf_pool_modified_pct;
17288 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17289 ER_WRONG_ARGUMENTS,
17290 "innodb_max_dirty_pages_pct_lwm"
17291 " cannot be set higher than"
17292 " innodb_max_dirty_pages_pct.");
17293 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
17294 ER_WRONG_ARGUMENTS,
17295 "Setting innodb_max_dirty_page_pct_lwm"
17296 " to %lf",
17297 in_val);
17298 }
17299
17300 srv_max_dirty_pages_pct_lwm = in_val;
17301}
17302
17303/*************************************************************//**
17304Don't allow to set innodb_fast_shutdown=0 if purge threads are
17305already down.
17306@return 0 if innodb_fast_shutdown can be set */
17307static
17308int
17309fast_shutdown_validate(
17310/*=============================*/
17311 THD* thd, /*!< in: thread handle */
17312 struct st_mysql_sys_var* var, /*!< in: pointer to system
17313 variable */
17314 void* save, /*!< out: immediate result
17315 for update function */
17316 struct st_mysql_value* value) /*!< in: incoming string */
17317{
17318 if (check_sysvar_int(thd, var, save, value)) {
17319 return(1);
17320 }
17321
17322 uint new_val = *reinterpret_cast<uint*>(save);
17323
17324 if (srv_fast_shutdown && !new_val
17325 && !my_atomic_loadptr_explicit(reinterpret_cast<void**>
17326 (&srv_running),
17327 MY_MEMORY_ORDER_RELAXED)) {
17328 return(1);
17329 }
17330
17331 return(0);
17332}
17333
17334/*************************************************************//**
17335Check whether valid argument given to innobase_*_stopword_table.
17336This function is registered as a callback with MySQL.
17337@return 0 for valid stopword table */
17338static
17339int
17340innodb_stopword_table_validate(
17341/*===========================*/
17342 THD* thd, /*!< in: thread handle */
17343 st_mysql_sys_var*,
17344 void* save, /*!< out: immediate result
17345 for update function */
17346 struct st_mysql_value* value) /*!< in: incoming string */
17347{
17348 const char* stopword_table_name;
17349 char buff[STRING_BUFFER_USUAL_SIZE];
17350 int len = sizeof(buff);
17351 trx_t* trx;
17352 int ret = 1;
17353
17354 ut_a(save != NULL);
17355 ut_a(value != NULL);
17356
17357 stopword_table_name = value->val_str(value, buff, &len);
17358
17359 trx = check_trx_exists(thd);
17360
17361 row_mysql_lock_data_dictionary(trx);
17362
17363 /* Validate the stopword table's (if supplied) existence and
17364 of the right format */
17365 if (!stopword_table_name
17366 || fts_valid_stopword_table(stopword_table_name)) {
17367 *static_cast<const char**>(save) = stopword_table_name;
17368 ret = 0;
17369 }
17370
17371 row_mysql_unlock_data_dictionary(trx);
17372
17373 return(ret);
17374}
17375
17376/** Update the system variable innodb_buffer_pool_size using the "saved"
17377value. This function is registered as a callback with MySQL.
17378@param[in] save immediate result from check function */
17379static
17380void
17381innodb_buffer_pool_size_update(THD*,st_mysql_sys_var*,void*, const void* save)
17382{
17383 longlong in_val = *static_cast<const longlong*>(save);
17384
17385 snprintf(export_vars.innodb_buffer_pool_resize_status,
17386 sizeof(export_vars.innodb_buffer_pool_resize_status),
17387 "Requested to resize buffer pool.");
17388
17389 os_event_set(srv_buf_resize_event);
17390
17391 ib::info() << export_vars.innodb_buffer_pool_resize_status
17392 << " (new size: " << in_val << " bytes)";
17393}
17394
17395/*************************************************************//**
17396Check whether valid argument given to "innodb_fts_internal_tbl_name"
17397This function is registered as a callback with MySQL.
17398@return 0 for valid stopword table */
17399static
17400int
17401innodb_internal_table_validate(
17402/*===========================*/
17403 THD*, st_mysql_sys_var*,
17404 void* save, /*!< out: immediate result
17405 for update function */
17406 struct st_mysql_value* value) /*!< in: incoming string */
17407{
17408 const char* table_name;
17409 char buff[STRING_BUFFER_USUAL_SIZE];
17410 int len = sizeof(buff);
17411 int ret = 1;
17412 dict_table_t* user_table;
17413
17414 ut_a(save != NULL);
17415 ut_a(value != NULL);
17416
17417 table_name = value->val_str(value, buff, &len);
17418
17419 if (!table_name) {
17420 *static_cast<const char**>(save) = NULL;
17421 return(0);
17422 }
17423
17424 user_table = dict_table_open_on_name(
17425 table_name, FALSE, TRUE, DICT_ERR_IGNORE_NONE);
17426
17427 if (user_table) {
17428 if (dict_table_has_fts_index(user_table)) {
17429 *static_cast<const char**>(save) = table_name;
17430 ret = 0;
17431 }
17432
17433 dict_table_close(user_table, FALSE, TRUE);
17434
17435 DBUG_EXECUTE_IF("innodb_evict_autoinc_table",
17436 mutex_enter(&dict_sys->mutex);
17437 dict_table_remove_from_cache_low(user_table, TRUE);
17438 mutex_exit(&dict_sys->mutex);
17439 );
17440 }
17441
17442 return(ret);
17443}
17444
17445/****************************************************************//**
17446Update global variable "fts_internal_tbl_name" with the "saved"
17447stopword table name value. This function is registered as a callback
17448with MySQL. */
17449static
17450void
17451innodb_internal_table_update(
17452/*=========================*/
17453 THD*, st_mysql_sys_var*,
17454 void* var_ptr,/*!< out: where the
17455 formal string goes */
17456 const void* save) /*!< in: immediate result
17457 from check function */
17458{
17459 const char* table_name;
17460 char* old;
17461
17462 ut_a(save != NULL);
17463 ut_a(var_ptr != NULL);
17464
17465 table_name = *static_cast<const char*const*>(save);
17466 old = *(char**) var_ptr;
17467 *(char**) var_ptr = table_name ? my_strdup(table_name, MYF(0)) : NULL;
17468 my_free(old);
17469
17470 fts_internal_tbl_name2 = *(char**) var_ptr;
17471 if (fts_internal_tbl_name2 == NULL) {
17472 fts_internal_tbl_name = const_cast<char*>("default");
17473 } else {
17474 fts_internal_tbl_name = fts_internal_tbl_name2;
17475 }
17476}
17477
17478#ifdef BTR_CUR_HASH_ADAPT
17479/****************************************************************//**
17480Update the system variable innodb_adaptive_hash_index using the "saved"
17481value. This function is registered as a callback with MySQL. */
17482static
17483void
17484innodb_adaptive_hash_index_update(THD*, st_mysql_sys_var*, void*,
17485 const void* save)
17486{
17487 if (*(my_bool*) save) {
17488 btr_search_enable();
17489 } else {
17490 btr_search_disable(true);
17491 }
17492}
17493#endif /* BTR_CUR_HASH_ADAPT */
17494
17495/****************************************************************//**
17496Update the system variable innodb_cmp_per_index using the "saved"
17497value. This function is registered as a callback with MySQL. */
17498static
17499void
17500innodb_cmp_per_index_update(THD*, st_mysql_sys_var*, void*, const void* save)
17501{
17502 /* Reset the stats whenever we enable the table
17503 INFORMATION_SCHEMA.innodb_cmp_per_index. */
17504 if (!srv_cmp_per_index_enabled && *(my_bool*) save) {
17505 page_zip_reset_stat_per_index();
17506 }
17507
17508 srv_cmp_per_index_enabled = !!(*(my_bool*) save);
17509}
17510
17511/****************************************************************//**
17512Update the system variable innodb_old_blocks_pct using the "saved"
17513value. This function is registered as a callback with MySQL. */
17514static
17515void
17516innodb_old_blocks_pct_update(THD*, st_mysql_sys_var*, void*, const void* save)
17517{
17518 innobase_old_blocks_pct = static_cast<uint>(
17519 buf_LRU_old_ratio_update(
17520 *static_cast<const uint*>(save), TRUE));
17521}
17522
17523/****************************************************************//**
17524Update the system variable innodb_old_blocks_pct using the "saved"
17525value. This function is registered as a callback with MySQL. */
17526static
17527void
17528innodb_change_buffer_max_size_update(THD*, st_mysql_sys_var*, void*,
17529 const void* save)
17530{
17531 srv_change_buffer_max_size =
17532 (*static_cast<const uint*>(save));
17533 ibuf_max_size_update(srv_change_buffer_max_size);
17534}
17535
17536#ifdef UNIV_DEBUG
17537static ulong srv_fil_make_page_dirty_debug = 0;
17538static ulong srv_saved_page_number_debug = 0;
17539
17540/****************************************************************//**
17541Save an InnoDB page number. */
17542static
17543void
17544innodb_save_page_no(THD*, st_mysql_sys_var*, void*, const void* save)
17545{
17546 srv_saved_page_number_debug = *static_cast<const ulong*>(save);
17547
17548 ib::info() << "Saving InnoDB page number: "
17549 << srv_saved_page_number_debug;
17550}
17551
17552/****************************************************************//**
17553Make the first page of given user tablespace dirty. */
17554static
17555void
17556innodb_make_page_dirty(THD*, st_mysql_sys_var*, void*, const void* save)
17557{
17558 mtr_t mtr;
17559 ulong space_id = *static_cast<const ulong*>(save);
17560 fil_space_t* space = fil_space_acquire_silent(space_id);
17561
17562 if (space == NULL) {
17563 return;
17564 }
17565
17566 if (srv_saved_page_number_debug >= space->size) {
17567 space->release();
17568 return;
17569 }
17570
17571 mtr.start();
17572 mtr.set_named_space(space);
17573
17574 buf_block_t* block = buf_page_get(
17575 page_id_t(space_id, srv_saved_page_number_debug),
17576 page_size_t(space->flags), RW_X_LATCH, &mtr);
17577
17578 if (block != NULL) {
17579 byte* page = block->frame;
17580
17581 ib::info() << "Dirtying page: " << page_id_t(
17582 page_get_space_id(page), page_get_page_no(page));
17583
17584 mlog_write_ulint(page + FIL_PAGE_TYPE,
17585 fil_page_get_type(page),
17586 MLOG_2BYTES, &mtr);
17587 }
17588 mtr.commit();
17589 space->release();
17590}
17591#endif // UNIV_DEBUG
17592/*************************************************************//**
17593Just emit a warning that the usage of the variable is deprecated.
17594@return 0 */
17595static
17596void
17597innodb_stats_sample_pages_update(
17598/*=============================*/
17599 THD* thd, /*!< in: thread handle */
17600 st_mysql_sys_var*, void*,
17601 const void* save) /*!< in: immediate result
17602 from check function */
17603{
17604
17605 const char* STATS_SAMPLE_PAGES_DEPRECATED_MSG =
17606 "Using innodb_stats_sample_pages is deprecated and"
17607 " the variable may be removed in future releases."
17608 " Please use innodb_stats_transient_sample_pages instead.";
17609
17610 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
17611 HA_ERR_WRONG_COMMAND, STATS_SAMPLE_PAGES_DEPRECATED_MSG);
17612
17613 ib::warn() << STATS_SAMPLE_PAGES_DEPRECATED_MSG;
17614
17615 srv_stats_transient_sample_pages =
17616 *static_cast<const unsigned long long*>(save);
17617}
17618
17619/****************************************************************//**
17620Update the monitor counter according to the "set_option", turn
17621on/off or reset specified monitor counter. */
17622static
17623void
17624innodb_monitor_set_option(
17625/*======================*/
17626 const monitor_info_t* monitor_info,/*!< in: monitor info for the monitor
17627 to set */
17628 mon_option_t set_option) /*!< in: Turn on/off reset the
17629 counter */
17630{
17631 monitor_id_t monitor_id = monitor_info->monitor_id;
17632
17633 /* If module type is MONITOR_GROUP_MODULE, it cannot be
17634 turned on/off individually. It should never use this
17635 function to set options */
17636 ut_a(!(monitor_info->monitor_type & MONITOR_GROUP_MODULE));
17637
17638 switch (set_option) {
17639 case MONITOR_TURN_ON:
17640 MONITOR_ON(monitor_id);
17641 MONITOR_INIT(monitor_id);
17642 MONITOR_SET_START(monitor_id);
17643
17644 /* If the monitor to be turned on uses
17645 exisitng monitor counter (status variable),
17646 make special processing to remember existing
17647 counter value. */
17648 if (monitor_info->monitor_type & MONITOR_EXISTING) {
17649 srv_mon_process_existing_counter(
17650 monitor_id, MONITOR_TURN_ON);
17651 }
17652
17653 if (MONITOR_IS_ON(MONITOR_LATCHES)) {
17654
17655 mutex_monitor.enable();
17656 }
17657 break;
17658
17659 case MONITOR_TURN_OFF:
17660 if (monitor_info->monitor_type & MONITOR_EXISTING) {
17661 srv_mon_process_existing_counter(
17662 monitor_id, MONITOR_TURN_OFF);
17663 }
17664
17665 MONITOR_OFF(monitor_id);
17666 MONITOR_SET_OFF(monitor_id);
17667
17668 if (!MONITOR_IS_ON(MONITOR_LATCHES)) {
17669
17670 mutex_monitor.disable();
17671 }
17672 break;
17673
17674 case MONITOR_RESET_VALUE:
17675 srv_mon_reset(monitor_id);
17676
17677 if (monitor_id == (MONITOR_LATCHES)) {
17678
17679 mutex_monitor.reset();
17680 }
17681 break;
17682
17683 case MONITOR_RESET_ALL_VALUE:
17684 srv_mon_reset_all(monitor_id);
17685 mutex_monitor.reset();
17686 break;
17687
17688 default:
17689 ut_error;
17690 }
17691}
17692
17693/****************************************************************//**
17694Find matching InnoDB monitor counters and update their status
17695according to the "set_option", turn on/off or reset specified
17696monitor counter. */
17697static
17698void
17699innodb_monitor_update_wildcard(
17700/*===========================*/
17701 const char* name, /*!< in: monitor name to match */
17702 mon_option_t set_option) /*!< in: the set option, whether
17703 to turn on/off or reset the counter */
17704{
17705 ut_a(name);
17706
17707 for (ulint use = 0; use < NUM_MONITOR; use++) {
17708 ulint type;
17709 monitor_id_t monitor_id = static_cast<monitor_id_t>(use);
17710 monitor_info_t* monitor_info;
17711
17712 if (!innobase_wildcasecmp(
17713 srv_mon_get_name(monitor_id), name)) {
17714 monitor_info = srv_mon_get_info(monitor_id);
17715
17716 type = monitor_info->monitor_type;
17717
17718 /* If the monitor counter is of MONITOR_MODULE
17719 type, skip it. Except for those also marked with
17720 MONITOR_GROUP_MODULE flag, which can be turned
17721 on only as a module. */
17722 if (!(type & MONITOR_MODULE)
17723 && !(type & MONITOR_GROUP_MODULE)) {
17724 innodb_monitor_set_option(monitor_info,
17725 set_option);
17726 }
17727
17728 /* Need to special handle counters marked with
17729 MONITOR_GROUP_MODULE, turn on the whole module if
17730 any one of it comes here. Currently, only
17731 "module_buf_page" is marked with MONITOR_GROUP_MODULE */
17732 if (type & MONITOR_GROUP_MODULE) {
17733 if ((monitor_id >= MONITOR_MODULE_BUF_PAGE)
17734 && (monitor_id < MONITOR_MODULE_OS)) {
17735 if (set_option == MONITOR_TURN_ON
17736 && MONITOR_IS_ON(
17737 MONITOR_MODULE_BUF_PAGE)) {
17738 continue;
17739 }
17740
17741 srv_mon_set_module_control(
17742 MONITOR_MODULE_BUF_PAGE,
17743 set_option);
17744 } else {
17745 /* If new monitor is added with
17746 MONITOR_GROUP_MODULE, it needs
17747 to be added here. */
17748 ut_ad(0);
17749 }
17750 }
17751 }
17752 }
17753}
17754
17755/*************************************************************//**
17756Given a configuration variable name, find corresponding monitor counter
17757and return its monitor ID if found.
17758@return monitor ID if found, MONITOR_NO_MATCH if there is no match */
17759static
17760ulint
17761innodb_monitor_id_by_name_get(
17762/*==========================*/
17763 const char* name) /*!< in: monitor counter namer */
17764{
17765 ut_a(name);
17766
17767 /* Search for wild character '%' in the name, if
17768 found, we treat it as a wildcard match. We do not search for
17769 single character wildcard '_' since our monitor names already contain
17770 such character. To avoid confusion, we request user must include
17771 at least one '%' character to activate the wildcard search. */
17772 if (strchr(name, '%')) {
17773 return(MONITOR_WILDCARD_MATCH);
17774 }
17775
17776 /* Not wildcard match, check for an exact match */
17777 for (ulint i = 0; i < NUM_MONITOR; i++) {
17778 if (!innobase_strcasecmp(
17779 name, srv_mon_get_name(static_cast<monitor_id_t>(i)))) {
17780 return(i);
17781 }
17782 }
17783
17784 return(MONITOR_NO_MATCH);
17785}
17786/*************************************************************//**
17787Validate that the passed in monitor name matches at least one
17788monitor counter name with wildcard compare.
17789@return TRUE if at least one monitor name matches */
17790static
17791ibool
17792innodb_monitor_validate_wildcard_name(
17793/*==================================*/
17794 const char* name) /*!< in: monitor counter namer */
17795{
17796 for (ulint i = 0; i < NUM_MONITOR; i++) {
17797 if (!innobase_wildcasecmp(
17798 srv_mon_get_name(static_cast<monitor_id_t>(i)), name)) {
17799 return(TRUE);
17800 }
17801 }
17802
17803 return(FALSE);
17804}
17805/*************************************************************//**
17806Validate the passed in monitor name, find and save the
17807corresponding monitor name in the function parameter "save".
17808@return 0 if monitor name is valid */
17809static
17810int
17811innodb_monitor_valid_byname(
17812/*========================*/
17813 void* save, /*!< out: immediate result
17814 for update function */
17815 const char* name) /*!< in: incoming monitor name */
17816{
17817 ulint use;
17818 monitor_info_t* monitor_info;
17819
17820 if (!name) {
17821 return(1);
17822 }
17823
17824 use = innodb_monitor_id_by_name_get(name);
17825
17826 /* No monitor name matches, nor it is wildcard match */
17827 if (use == MONITOR_NO_MATCH) {
17828 return(1);
17829 }
17830
17831 if (use < NUM_MONITOR) {
17832 monitor_info = srv_mon_get_info((monitor_id_t) use);
17833
17834 /* If the monitor counter is marked with
17835 MONITOR_GROUP_MODULE flag, then this counter
17836 cannot be turned on/off individually, instead
17837 it shall be turned on/off as a group using
17838 its module name */
17839 if ((monitor_info->monitor_type & MONITOR_GROUP_MODULE)
17840 && (!(monitor_info->monitor_type & MONITOR_MODULE))) {
17841 sql_print_warning(
17842 "Monitor counter '%s' cannot"
17843 " be turned on/off individually."
17844 " Please use its module name"
17845 " to turn on/off the counters"
17846 " in the module as a group.\n",
17847 name);
17848
17849 return(1);
17850 }
17851
17852 } else {
17853 ut_a(use == MONITOR_WILDCARD_MATCH);
17854
17855 /* For wildcard match, if there is not a single monitor
17856 counter name that matches, treat it as an invalid
17857 value for the system configuration variables */
17858 if (!innodb_monitor_validate_wildcard_name(name)) {
17859 return(1);
17860 }
17861 }
17862
17863 /* Save the configure name for innodb_monitor_update() */
17864 *static_cast<const char**>(save) = name;
17865
17866 return(0);
17867}
17868/*************************************************************//**
17869Validate passed-in "value" is a valid monitor counter name.
17870This function is registered as a callback with MySQL.
17871@return 0 for valid name */
17872static
17873int
17874innodb_monitor_validate(
17875/*====================*/
17876 THD*, st_mysql_sys_var*,
17877 void* save, /*!< out: immediate result
17878 for update function */
17879 struct st_mysql_value* value) /*!< in: incoming string */
17880{
17881 const char* name;
17882 char* monitor_name;
17883 char buff[STRING_BUFFER_USUAL_SIZE];
17884 int len = sizeof(buff);
17885 int ret;
17886
17887 ut_a(save != NULL);
17888 ut_a(value != NULL);
17889
17890 name = value->val_str(value, buff, &len);
17891
17892 /* monitor_name could point to memory from MySQL
17893 or buff[]. Always dup the name to memory allocated
17894 by InnoDB, so we can access it in another callback
17895 function innodb_monitor_update() and free it appropriately */
17896 if (name) {
17897 monitor_name = my_strdup(//PSI_INSTRUMENT_ME,
17898 name, MYF(0));
17899 } else {
17900 return(1);
17901 }
17902
17903 ret = innodb_monitor_valid_byname(save, monitor_name);
17904
17905 if (ret) {
17906 /* Validation failed */
17907 my_free(monitor_name);
17908 } else {
17909 /* monitor_name will be freed in separate callback function
17910 innodb_monitor_update(). Assert "save" point to
17911 the "monitor_name" variable */
17912 ut_ad(*static_cast<char**>(save) == monitor_name);
17913 }
17914
17915 return(ret);
17916}
17917
17918/****************************************************************//**
17919Update the system variable innodb_enable(disable/reset/reset_all)_monitor
17920according to the "set_option" and turn on/off or reset specified monitor
17921counter. */
17922static
17923void
17924innodb_monitor_update(
17925/*==================*/
17926 THD* thd, /*!< in: thread handle */
17927 void* var_ptr, /*!< out: where the
17928 formal string goes */
17929 const void* save, /*!< in: immediate result
17930 from check function */
17931 mon_option_t set_option, /*!< in: the set option,
17932 whether to turn on/off or
17933 reset the counter */
17934 ibool free_mem) /*!< in: whether we will
17935 need to free the memory */
17936{
17937 monitor_info_t* monitor_info;
17938 ulint monitor_id;
17939 ulint err_monitor = 0;
17940 const char* name;
17941
17942 ut_a(save != NULL);
17943
17944 name = *static_cast<const char*const*>(save);
17945
17946 if (!name) {
17947 monitor_id = MONITOR_DEFAULT_START;
17948 } else {
17949 monitor_id = innodb_monitor_id_by_name_get(name);
17950
17951 /* Double check we have a valid monitor ID */
17952 if (monitor_id == MONITOR_NO_MATCH) {
17953 return;
17954 }
17955 }
17956
17957 if (monitor_id == MONITOR_DEFAULT_START) {
17958 /* If user set the variable to "default", we will
17959 print a message and make this set operation a "noop".
17960 The check is being made here is because "set default"
17961 does not go through validation function */
17962 if (thd) {
17963 push_warning_printf(
17964 thd, Sql_condition::WARN_LEVEL_WARN,
17965 ER_NO_DEFAULT,
17966 "Default value is not defined for"
17967 " this set option. Please specify"
17968 " correct counter or module name.");
17969 } else {
17970 sql_print_error(
17971 "Default value is not defined for"
17972 " this set option. Please specify"
17973 " correct counter or module name.\n");
17974 }
17975
17976 if (var_ptr) {
17977 *(const char**) var_ptr = NULL;
17978 }
17979 } else if (monitor_id == MONITOR_WILDCARD_MATCH) {
17980 innodb_monitor_update_wildcard(name, set_option);
17981 } else {
17982 monitor_info = srv_mon_get_info(
17983 static_cast<monitor_id_t>(monitor_id));
17984
17985 ut_a(monitor_info);
17986
17987 /* If monitor is already truned on, someone could already
17988 collect monitor data, exit and ask user to turn off the
17989 monitor before turn it on again. */
17990 if (set_option == MONITOR_TURN_ON
17991 && MONITOR_IS_ON(monitor_id)) {
17992 err_monitor = monitor_id;
17993 goto exit;
17994 }
17995
17996 if (var_ptr) {
17997 *(const char**) var_ptr = monitor_info->monitor_name;
17998 }
17999
18000 /* Depending on the monitor name is for a module or
18001 a counter, process counters in the whole module or
18002 individual counter. */
18003 if (monitor_info->monitor_type & MONITOR_MODULE) {
18004 srv_mon_set_module_control(
18005 static_cast<monitor_id_t>(monitor_id),
18006 set_option);
18007 } else {
18008 innodb_monitor_set_option(monitor_info, set_option);
18009 }
18010 }
18011exit:
18012 /* Only if we are trying to turn on a monitor that already
18013 been turned on, we will set err_monitor. Print related
18014 information */
18015 if (err_monitor) {
18016 sql_print_warning("InnoDB: Monitor %s is already enabled.",
18017 srv_mon_get_name((monitor_id_t) err_monitor));
18018 }
18019
18020 if (free_mem && name) {
18021 my_free((void*) name);
18022 }
18023
18024 return;
18025}
18026
18027#ifdef _WIN32
18028/*************************************************************//**
18029Validate if passed-in "value" is a valid value for
18030innodb_buffer_pool_filename. On Windows, file names with colon (:)
18031are not allowed.
18032
18033@return 0 for valid name */
18034static
18035int
18036innodb_srv_buf_dump_filename_validate(
18037/*==================================*/
18038 THD* thd, /*!< in: thread handle */
18039 struct st_mysql_sys_var* var, /*!< in: pointer to system
18040 variable */
18041 void* save, /*!< out: immediate result
18042 for update function */
18043 struct st_mysql_value* value) /*!< in: incoming string */
18044{
18045 char buff[OS_FILE_MAX_PATH];
18046 int len = sizeof(buff);
18047
18048 ut_a(save != NULL);
18049 ut_a(value != NULL);
18050
18051 if (const char* buf_name = value->val_str(value, buff, &len)) {
18052 if (is_filename_allowed(buf_name, len, FALSE)){
18053 *static_cast<const char**>(save) = buf_name;
18054 return(0);
18055 } else {
18056 push_warning_printf(thd,
18057 Sql_condition::WARN_LEVEL_WARN,
18058 ER_WRONG_ARGUMENTS,
18059 "InnoDB: innodb_buffer_pool_filename"
18060 " cannot have colon (:) in the file name.");
18061
18062 }
18063 }
18064
18065 return(1);
18066}
18067#else /* _WIN32 */
18068# define innodb_srv_buf_dump_filename_validate NULL
18069#endif /* _WIN32 */
18070
18071#ifdef UNIV_DEBUG
18072static char* srv_buffer_pool_evict;
18073
18074/****************************************************************//**
18075Evict all uncompressed pages of compressed tables from the buffer pool.
18076Keep the compressed pages in the buffer pool.
18077@return whether all uncompressed pages were evicted */
18078static MY_ATTRIBUTE((warn_unused_result))
18079bool
18080innodb_buffer_pool_evict_uncompressed(void)
18081/*=======================================*/
18082{
18083 bool all_evicted = true;
18084
18085 for (ulint i = 0; i < srv_buf_pool_instances; i++) {
18086 buf_pool_t* buf_pool = &buf_pool_ptr[i];
18087
18088 buf_pool_mutex_enter(buf_pool);
18089
18090 for (buf_block_t* block = UT_LIST_GET_LAST(
18091 buf_pool->unzip_LRU);
18092 block != NULL; ) {
18093 buf_block_t* prev_block = UT_LIST_GET_PREV(
18094 unzip_LRU, block);
18095 ut_ad(buf_block_get_state(block)
18096 == BUF_BLOCK_FILE_PAGE);
18097 ut_ad(block->in_unzip_LRU_list);
18098 ut_ad(block->page.in_LRU_list);
18099
18100 if (!buf_LRU_free_page(&block->page, false)) {
18101 all_evicted = false;
18102 }
18103
18104 block = prev_block;
18105 }
18106
18107 buf_pool_mutex_exit(buf_pool);
18108 }
18109
18110 return(all_evicted);
18111}
18112
18113/****************************************************************//**
18114Called on SET GLOBAL innodb_buffer_pool_evict=...
18115Handles some values specially, to evict pages from the buffer pool.
18116SET GLOBAL innodb_buffer_pool_evict='uncompressed'
18117evicts all uncompressed page frames of compressed tablespaces. */
18118static
18119void
18120innodb_buffer_pool_evict_update(THD*, st_mysql_sys_var*, void*,
18121 const void* save)
18122{
18123 if (const char* op = *static_cast<const char*const*>(save)) {
18124 if (!strcmp(op, "uncompressed")) {
18125 for (uint tries = 0; tries < 10000; tries++) {
18126 if (innodb_buffer_pool_evict_uncompressed()) {
18127 return;
18128 }
18129
18130 os_thread_sleep(10000);
18131 }
18132
18133 /* We failed to evict all uncompressed pages. */
18134 ut_ad(0);
18135 }
18136 }
18137}
18138#endif /* UNIV_DEBUG */
18139
18140/****************************************************************//**
18141Update the system variable innodb_monitor_enable and enable
18142specified monitor counter.
18143This function is registered as a callback with MySQL. */
18144static
18145void
18146innodb_enable_monitor_update(
18147/*=========================*/
18148 THD* thd, /*!< in: thread handle */
18149 st_mysql_sys_var*,
18150 void* var_ptr,/*!< out: where the
18151 formal string goes */
18152 const void* save) /*!< in: immediate result
18153 from check function */
18154{
18155 innodb_monitor_update(thd, var_ptr, save, MONITOR_TURN_ON, TRUE);
18156}
18157
18158/****************************************************************//**
18159Update the system variable innodb_monitor_disable and turn
18160off specified monitor counter. */
18161static
18162void
18163innodb_disable_monitor_update(
18164/*==========================*/
18165 THD* thd, /*!< in: thread handle */
18166 st_mysql_sys_var*,
18167 void* var_ptr,/*!< out: where the
18168 formal string goes */
18169 const void* save) /*!< in: immediate result
18170 from check function */
18171{
18172 innodb_monitor_update(thd, var_ptr, save, MONITOR_TURN_OFF, TRUE);
18173}
18174
18175/****************************************************************//**
18176Update the system variable innodb_monitor_reset and reset
18177specified monitor counter(s).
18178This function is registered as a callback with MySQL. */
18179static
18180void
18181innodb_reset_monitor_update(
18182/*========================*/
18183 THD* thd, /*!< in: thread handle */
18184 st_mysql_sys_var*,
18185 void* var_ptr,/*!< out: where the
18186 formal string goes */
18187 const void* save) /*!< in: immediate result
18188 from check function */
18189{
18190 innodb_monitor_update(thd, var_ptr, save, MONITOR_RESET_VALUE, TRUE);
18191}
18192
18193/****************************************************************//**
18194Update the system variable innodb_monitor_reset_all and reset
18195all value related monitor counter.
18196This function is registered as a callback with MySQL. */
18197static
18198void
18199innodb_reset_all_monitor_update(
18200/*============================*/
18201 THD* thd, /*!< in: thread handle */
18202 st_mysql_sys_var*,
18203 void* var_ptr,/*!< out: where the
18204 formal string goes */
18205 const void* save) /*!< in: immediate result
18206 from check function */
18207{
18208 innodb_monitor_update(thd, var_ptr, save, MONITOR_RESET_ALL_VALUE,
18209 TRUE);
18210}
18211
18212static
18213void
18214innodb_defragment_frequency_update(THD*, st_mysql_sys_var*, void*,
18215 const void* save)
18216{
18217 srv_defragment_frequency = (*static_cast<const uint*>(save));
18218 srv_defragment_interval = ut_microseconds_to_timer(
18219 (ulonglong) (1000000.0 / srv_defragment_frequency));
18220}
18221
18222static inline char *my_strtok_r(char *str, const char *delim, char **saveptr)
18223{
18224#if defined _WIN32
18225 return strtok_s(str, delim, saveptr);
18226#else
18227 return strtok_r(str, delim, saveptr);
18228#endif
18229}
18230
18231/****************************************************************//**
18232Parse and enable InnoDB monitor counters during server startup.
18233User can list the monitor counters/groups to be enable by specifying
18234"loose-innodb_monitor_enable=monitor_name1;monitor_name2..."
18235in server configuration file or at the command line. The string
18236separate could be ";", "," or empty space. */
18237static
18238void
18239innodb_enable_monitor_at_startup(
18240/*=============================*/
18241 char* str) /*!< in/out: monitor counter enable list */
18242{
18243 static const char* sep = " ;,";
18244 char* last;
18245
18246 ut_a(str);
18247
18248 /* Walk through the string, and separate each monitor counter
18249 and/or counter group name, and calling innodb_monitor_update()
18250 if successfully updated. Please note that the "str" would be
18251 changed by strtok_r() as it walks through it. */
18252 for (char* option = my_strtok_r(str, sep, &last);
18253 option;
18254 option = my_strtok_r(NULL, sep, &last)) {
18255 char* option_name;
18256 if (!innodb_monitor_valid_byname(&option_name, option)) {
18257 innodb_monitor_update(NULL, NULL, &option,
18258 MONITOR_TURN_ON, FALSE);
18259 } else {
18260 sql_print_warning("Invalid monitor counter"
18261 " name: '%s'", option);
18262 }
18263 }
18264}
18265
18266/****************************************************************//**
18267Callback function for accessing the InnoDB variables from MySQL:
18268SHOW VARIABLES. */
18269static int show_innodb_vars(THD*, SHOW_VAR* var, char*)
18270{
18271 innodb_export_status();
18272 var->type = SHOW_ARRAY;
18273 var->value = (char*) &innodb_status_variables;
18274 //var->scope = SHOW_SCOPE_GLOBAL;
18275
18276 return(0);
18277}
18278
18279/****************************************************************//**
18280This function checks each index name for a table against reserved
18281system default primary index name 'GEN_CLUST_INDEX'. If a name
18282matches, this function pushes an warning message to the client,
18283and returns true.
18284@return true if the index name matches the reserved name */
18285bool
18286innobase_index_name_is_reserved(
18287/*============================*/
18288 THD* thd, /*!< in/out: MySQL connection */
18289 const KEY* key_info, /*!< in: Indexes to be created */
18290 ulint num_of_keys) /*!< in: Number of indexes to
18291 be created. */
18292{
18293 const KEY* key;
18294 uint key_num; /* index number */
18295
18296 for (key_num = 0; key_num < num_of_keys; key_num++) {
18297 key = &key_info[key_num];
18298
18299 if (innobase_strcasecmp(key->name.str,
18300 innobase_index_reserve_name) == 0) {
18301 /* Push warning to mysql */
18302 push_warning_printf(thd,
18303 Sql_condition::WARN_LEVEL_WARN,
18304 ER_WRONG_NAME_FOR_INDEX,
18305 "Cannot Create Index with name"
18306 " '%s'. The name is reserved"
18307 " for the system default primary"
18308 " index.",
18309 innobase_index_reserve_name);
18310
18311 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
18312 innobase_index_reserve_name);
18313
18314 return(true);
18315 }
18316 }
18317
18318 return(false);
18319}
18320
18321/** Retrieve the FTS Relevance Ranking result for doc with doc_id
18322of m_prebuilt->fts_doc_id
18323@param[in,out] fts_hdl FTS handler
18324@return the relevance ranking value */
18325static
18326float
18327innobase_fts_retrieve_ranking(
18328 FT_INFO* fts_hdl)
18329{
18330 fts_result_t* result;
18331 row_prebuilt_t* ft_prebuilt;
18332
18333 result = reinterpret_cast<NEW_FT_INFO*>(fts_hdl)->ft_result;
18334
18335 ft_prebuilt = reinterpret_cast<NEW_FT_INFO*>(fts_hdl)->ft_prebuilt;
18336
18337 fts_ranking_t* ranking = rbt_value(fts_ranking_t, result->current);
18338 ft_prebuilt->fts_doc_id= ranking->doc_id;
18339
18340 return(ranking->rank);
18341}
18342
18343/** Free the memory for the FTS handler
18344@param[in,out] fts_hdl FTS handler */
18345static
18346void
18347innobase_fts_close_ranking(
18348 FT_INFO* fts_hdl)
18349{
18350 fts_result_t* result;
18351
18352 result = reinterpret_cast<NEW_FT_INFO*>(fts_hdl)->ft_result;
18353
18354 fts_query_free_result(result);
18355
18356 my_free((uchar*) fts_hdl);
18357}
18358
18359/** Find and Retrieve the FTS Relevance Ranking result for doc with doc_id
18360of m_prebuilt->fts_doc_id
18361@param[in,out] fts_hdl FTS handler
18362@return the relevance ranking value */
18363static
18364float
18365innobase_fts_find_ranking(FT_INFO* fts_hdl, uchar*, uint)
18366{
18367 fts_result_t* result;
18368 row_prebuilt_t* ft_prebuilt;
18369
18370 ft_prebuilt = reinterpret_cast<NEW_FT_INFO*>(fts_hdl)->ft_prebuilt;
18371 result = reinterpret_cast<NEW_FT_INFO*>(fts_hdl)->ft_result;
18372
18373 /* Retrieve the ranking value for doc_id with value of
18374 m_prebuilt->fts_doc_id */
18375 return(fts_retrieve_ranking(result, ft_prebuilt->fts_doc_id));
18376}
18377
18378#ifdef UNIV_DEBUG
18379static my_bool innodb_background_drop_list_empty = TRUE;
18380static my_bool innodb_log_checkpoint_now = TRUE;
18381static my_bool innodb_buf_flush_list_now = TRUE;
18382static uint innodb_merge_threshold_set_all_debug
18383 = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
18384
18385/** Wait for the background drop list to become empty. */
18386static
18387void
18388wait_background_drop_list_empty(THD*, st_mysql_sys_var*, void*, const void*)
18389{
18390 row_wait_for_background_drop_list_empty();
18391}
18392
18393/****************************************************************//**
18394Force innodb to checkpoint. */
18395static
18396void
18397checkpoint_now_set(THD*, st_mysql_sys_var*, void*, const void* save)
18398{
18399 if (*(my_bool*) save) {
18400 while (log_sys.last_checkpoint_lsn
18401 + SIZE_OF_MLOG_CHECKPOINT
18402 + (log_sys.append_on_checkpoint != NULL
18403 ? log_sys.append_on_checkpoint->size() : 0)
18404 < log_sys.lsn) {
18405 log_make_checkpoint_at(LSN_MAX, TRUE);
18406 fil_flush_file_spaces(FIL_TYPE_LOG);
18407 }
18408
18409 dberr_t err = fil_write_flushed_lsn(log_sys.lsn);
18410
18411 if (err != DB_SUCCESS) {
18412 ib::warn() << "Checkpoint set failed " << err;
18413 }
18414 }
18415}
18416
18417/****************************************************************//**
18418Force a dirty pages flush now. */
18419static
18420void
18421buf_flush_list_now_set(THD*, st_mysql_sys_var*, void*, const void* save)
18422{
18423 if (*(my_bool*) save) {
18424 buf_flush_sync_all_buf_pools();
18425 }
18426}
18427
18428/** Override current MERGE_THRESHOLD setting for all indexes at dictionary
18429now.
18430@param[in] save immediate result from check function */
18431static
18432void
18433innodb_merge_threshold_set_all_debug_update(THD*, st_mysql_sys_var*, void*,
18434 const void* save)
18435{
18436 innodb_merge_threshold_set_all_debug
18437 = (*static_cast<const uint*>(save));
18438 dict_set_merge_threshold_all_debug(
18439 innodb_merge_threshold_set_all_debug);
18440}
18441#endif /* UNIV_DEBUG */
18442
18443/** Find and Retrieve the FTS doc_id for the current result row
18444@param[in,out] fts_hdl FTS handler
18445@return the document ID */
18446static
18447ulonglong
18448innobase_fts_retrieve_docid(
18449 FT_INFO_EXT* fts_hdl)
18450{
18451 fts_result_t* result;
18452 row_prebuilt_t* ft_prebuilt;
18453
18454 ft_prebuilt = reinterpret_cast<NEW_FT_INFO *>(fts_hdl)->ft_prebuilt;
18455 result = reinterpret_cast<NEW_FT_INFO *>(fts_hdl)->ft_result;
18456
18457 if (ft_prebuilt->read_just_key) {
18458
18459 fts_ranking_t* ranking =
18460 rbt_value(fts_ranking_t, result->current);
18461
18462 return(ranking->doc_id);
18463 }
18464
18465 return(ft_prebuilt->fts_doc_id);
18466}
18467
18468/* These variables are never read by InnoDB or changed. They are a kind of
18469dummies that are needed by the MySQL infrastructure to call
18470buffer_pool_dump_now(), buffer_pool_load_now() and buffer_pool_load_abort()
18471by the user by doing:
18472 SET GLOBAL innodb_buffer_pool_dump_now=ON;
18473 SET GLOBAL innodb_buffer_pool_load_now=ON;
18474 SET GLOBAL innodb_buffer_pool_load_abort=ON;
18475Their values are read by MySQL and displayed to the user when the variables
18476are queried, e.g.:
18477 SELECT @@innodb_buffer_pool_dump_now;
18478 SELECT @@innodb_buffer_pool_load_now;
18479 SELECT @@innodb_buffer_pool_load_abort; */
18480static my_bool innodb_buffer_pool_dump_now = FALSE;
18481static my_bool innodb_buffer_pool_load_now = FALSE;
18482static my_bool innodb_buffer_pool_load_abort = FALSE;
18483
18484/****************************************************************//**
18485Trigger a dump of the buffer pool if innodb_buffer_pool_dump_now is set
18486to ON. This function is registered as a callback with MySQL. */
18487static
18488void
18489buffer_pool_dump_now(
18490/*=================*/
18491 THD* thd /*!< in: thread handle */
18492 MY_ATTRIBUTE((unused)),
18493 struct st_mysql_sys_var* var /*!< in: pointer to system
18494 variable */
18495 MY_ATTRIBUTE((unused)),
18496 void* var_ptr /*!< out: where the formal
18497 string goes */
18498 MY_ATTRIBUTE((unused)),
18499 const void* save) /*!< in: immediate result from
18500 check function */
18501{
18502 if (*(my_bool*) save && !srv_read_only_mode) {
18503 buf_dump_start();
18504 }
18505}
18506
18507/****************************************************************//**
18508Trigger a load of the buffer pool if innodb_buffer_pool_load_now is set
18509to ON. This function is registered as a callback with MySQL. */
18510static
18511void
18512buffer_pool_load_now(
18513/*=================*/
18514 THD* thd /*!< in: thread handle */
18515 MY_ATTRIBUTE((unused)),
18516 struct st_mysql_sys_var* var /*!< in: pointer to system
18517 variable */
18518 MY_ATTRIBUTE((unused)),
18519 void* var_ptr /*!< out: where the formal
18520 string goes */
18521 MY_ATTRIBUTE((unused)),
18522 const void* save) /*!< in: immediate result from
18523 check function */
18524{
18525 if (*(my_bool*) save && !srv_read_only_mode) {
18526 buf_load_start();
18527 }
18528}
18529
18530/****************************************************************//**
18531Abort a load of the buffer pool if innodb_buffer_pool_load_abort
18532is set to ON. This function is registered as a callback with MySQL. */
18533static
18534void
18535buffer_pool_load_abort(
18536/*===================*/
18537 THD* thd /*!< in: thread handle */
18538 MY_ATTRIBUTE((unused)),
18539 struct st_mysql_sys_var* var /*!< in: pointer to system
18540 variable */
18541 MY_ATTRIBUTE((unused)),
18542 void* var_ptr /*!< out: where the formal
18543 string goes */
18544 MY_ATTRIBUTE((unused)),
18545 const void* save) /*!< in: immediate result from
18546 check function */
18547{
18548 if (*(my_bool*) save && !srv_read_only_mode) {
18549 buf_load_abort();
18550 }
18551}
18552
18553/****************************************************************//**
18554Update the system variable innodb_log_write_ahead_size using the "saved"
18555value. This function is registered as a callback with MySQL. */
18556static
18557void
18558innodb_log_write_ahead_size_update(
18559/*===============================*/
18560 THD* thd, /*!< in: thread handle */
18561 st_mysql_sys_var*, void*,
18562 const void* save) /*!< in: immediate result
18563 from check function */
18564{
18565 ulong val = OS_FILE_LOG_BLOCK_SIZE;
18566 ulong in_val = *static_cast<const ulong*>(save);
18567
18568 while (val < in_val) {
18569 val = val * 2;
18570 }
18571
18572 if (val > srv_page_size) {
18573 val = srv_page_size;
18574 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
18575 ER_WRONG_ARGUMENTS,
18576 "innodb_log_write_ahead_size cannot"
18577 " be set higher than innodb_page_size.");
18578 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
18579 ER_WRONG_ARGUMENTS,
18580 "Setting innodb_log_write_ahead_size"
18581 " to %lu",
18582 srv_page_size);
18583 } else if (val != in_val) {
18584 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
18585 ER_WRONG_ARGUMENTS,
18586 "innodb_log_write_ahead_size should be"
18587 " set 2^n value and larger than 512.");
18588 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
18589 ER_WRONG_ARGUMENTS,
18590 "Setting innodb_log_write_ahead_size"
18591 " to %lu",
18592 val);
18593 }
18594
18595 srv_log_write_ahead_size = val;
18596}
18597
18598/** Update innodb_status_output or innodb_status_output_locks,
18599which control InnoDB "status monitor" output to the error log.
18600@param[out] var_ptr current value
18601@param[in] save to-be-assigned value */
18602static
18603void
18604innodb_status_output_update(THD*, st_mysql_sys_var*, void* var_ptr,
18605 const void* save)
18606{
18607 *static_cast<my_bool*>(var_ptr) = *static_cast<const my_bool*>(save);
18608 /* Wakeup server monitor thread. */
18609 os_event_set(srv_monitor_event);
18610}
18611
18612/******************************************************************
18613Update the system variable innodb_encryption_threads */
18614static
18615void
18616innodb_encryption_threads_update(THD*, st_mysql_sys_var*, void*,
18617 const void* save)
18618{
18619 fil_crypt_set_thread_cnt(*static_cast<const uint*>(save));
18620}
18621
18622/******************************************************************
18623Update the system variable innodb_encryption_rotate_key_age */
18624static
18625void
18626innodb_encryption_rotate_key_age_update(THD*, st_mysql_sys_var*, void*,
18627 const void* save)
18628{
18629 fil_crypt_set_rotate_key_age(*static_cast<const uint*>(save));
18630}
18631
18632/******************************************************************
18633Update the system variable innodb_encryption_rotation_iops */
18634static
18635void
18636innodb_encryption_rotation_iops_update(THD*, st_mysql_sys_var*, void*,
18637 const void* save)
18638{
18639 fil_crypt_set_rotation_iops(*static_cast<const uint*>(save));
18640}
18641
18642/******************************************************************
18643Update the system variable innodb_encrypt_tables*/
18644static
18645void
18646innodb_encrypt_tables_update(THD*, st_mysql_sys_var*, void*, const void* save)
18647{
18648 fil_crypt_set_encrypt_tables(*static_cast<const ulong*>(save));
18649}
18650
18651/** Update the innodb_log_checksums parameter.
18652@param[in,out] thd client connection
18653@param[out] var_ptr current value
18654@param[in] save immediate result from check function */
18655static
18656void
18657innodb_log_checksums_update(THD* thd, st_mysql_sys_var*, void* var_ptr,
18658 const void* save)
18659{
18660 *static_cast<my_bool*>(var_ptr) = innodb_log_checksums_func_update(
18661 thd, *static_cast<const my_bool*>(save));
18662}
18663
18664static SHOW_VAR innodb_status_variables_export[]= {
18665 {"Innodb", (char*) &show_innodb_vars, SHOW_FUNC},
18666 {NullS, NullS, SHOW_LONG}
18667};
18668
18669static struct st_mysql_storage_engine innobase_storage_engine=
18670{ MYSQL_HANDLERTON_INTERFACE_VERSION };
18671
18672#ifdef WITH_WSREP
18673void
18674wsrep_abort_slave_trx(
18675/*==================*/
18676 wsrep_seqno_t bf_seqno,
18677 wsrep_seqno_t victim_seqno)
18678{
18679 WSREP_ERROR("Trx %lld tries to abort slave trx %lld. This could be "
18680 "caused by:\n\t"
18681 "1) unsupported configuration options combination, please check documentation.\n\t"
18682 "2) a bug in the code.\n\t"
18683 "3) a database corruption.\n Node consistency compromized, "
18684 "need to abort. Restart the node to resync with cluster.",
18685 (long long)bf_seqno, (long long)victim_seqno);
18686 abort();
18687}
18688/*******************************************************************//**
18689This function is used to kill one transaction in BF. */
18690UNIV_INTERN
18691int
18692wsrep_innobase_kill_one_trx(
18693/*========================*/
18694 void * const bf_thd_ptr,
18695 const trx_t * const bf_trx,
18696 trx_t *victim_trx,
18697 ibool signal)
18698{
18699 ut_ad(lock_mutex_own());
18700 ut_ad(trx_mutex_own(victim_trx));
18701 ut_ad(bf_thd_ptr);
18702 ut_ad(victim_trx);
18703
18704 DBUG_ENTER("wsrep_innobase_kill_one_trx");
18705 THD *bf_thd = bf_thd_ptr ? (THD*) bf_thd_ptr : NULL;
18706 THD *thd = (THD *) victim_trx->mysql_thd;
18707 int64_t bf_seqno = (bf_thd) ? wsrep_thd_trx_seqno(bf_thd) : 0;
18708
18709 if (!thd) {
18710 DBUG_PRINT("wsrep", ("no thd for conflicting lock"));
18711 WSREP_WARN("no THD for trx: " TRX_ID_FMT, victim_trx->id);
18712 DBUG_RETURN(1);
18713 }
18714
18715 if (!bf_thd) {
18716 DBUG_PRINT("wsrep", ("no BF thd for conflicting lock"));
18717 WSREP_WARN("no BF THD for trx: " TRX_ID_FMT,
18718 bf_trx ? bf_trx->id : 0);
18719 DBUG_RETURN(1);
18720 }
18721
18722 WSREP_LOG_CONFLICT(bf_thd, thd, TRUE);
18723
18724 WSREP_DEBUG("BF kill (" ULINTPF ", seqno: " INT64PF
18725 "), victim: (%lu) trx: " TRX_ID_FMT,
18726 signal, bf_seqno,
18727 thd_get_thread_id(thd),
18728 victim_trx->id);
18729
18730 WSREP_DEBUG("Aborting query: %s",
18731 (thd && wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void");
18732
18733 wsrep_thd_LOCK(thd);
18734 DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock",
18735 {
18736 const char act[]=
18737 "now "
18738 "wait_for signal.wsrep_after_BF_victim_lock";
18739 DBUG_ASSERT(!debug_sync_set_action(bf_thd,
18740 STRING_WITH_LEN(act)));
18741 };);
18742
18743
18744 if (wsrep_thd_query_state(thd) == QUERY_EXITING) {
18745 WSREP_DEBUG("kill trx EXITING for " TRX_ID_FMT,
18746 victim_trx->id);
18747 wsrep_thd_UNLOCK(thd);
18748 DBUG_RETURN(0);
18749 }
18750
18751 if (wsrep_thd_exec_mode(thd) != LOCAL_STATE) {
18752 WSREP_DEBUG("withdraw for BF trx: " TRX_ID_FMT ", state: %d",
18753 victim_trx->id,
18754 wsrep_thd_get_conflict_state(thd));
18755 }
18756
18757 switch (wsrep_thd_get_conflict_state(thd)) {
18758 case NO_CONFLICT:
18759 wsrep_thd_set_conflict_state(thd, MUST_ABORT);
18760 break;
18761 case MUST_ABORT:
18762 WSREP_DEBUG("victim " TRX_ID_FMT " in MUST ABORT state",
18763 victim_trx->id);
18764 wsrep_thd_UNLOCK(thd);
18765 wsrep_thd_awake(thd, signal);
18766 DBUG_RETURN(0);
18767 break;
18768 case ABORTED:
18769 case ABORTING: // fall through
18770 default:
18771 WSREP_DEBUG("victim " TRX_ID_FMT " in state %d",
18772 victim_trx->id, wsrep_thd_get_conflict_state(thd));
18773 wsrep_thd_UNLOCK(thd);
18774 DBUG_RETURN(0);
18775 break;
18776 }
18777
18778 switch (wsrep_thd_query_state(thd)) {
18779 case QUERY_COMMITTING:
18780 enum wsrep_status rcode;
18781
18782 WSREP_DEBUG("kill query for: %ld",
18783 thd_get_thread_id(thd));
18784 WSREP_DEBUG("kill trx QUERY_COMMITTING for " TRX_ID_FMT,
18785 victim_trx->id);
18786
18787 if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
18788 wsrep_abort_slave_trx(bf_seqno,
18789 wsrep_thd_trx_seqno(thd));
18790 } else {
18791 wsrep_t *wsrep= get_wsrep();
18792 rcode = wsrep->abort_pre_commit(
18793 wsrep, bf_seqno,
18794 (wsrep_trx_id_t)victim_trx->id
18795 );
18796
18797 switch (rcode) {
18798 case WSREP_WARNING:
18799 WSREP_DEBUG("cancel commit warning: "
18800 TRX_ID_FMT,
18801 victim_trx->id);
18802 wsrep_thd_UNLOCK(thd);
18803 wsrep_thd_awake(thd, signal);
18804 DBUG_RETURN(1);
18805 break;
18806 case WSREP_OK:
18807 break;
18808 default:
18809 WSREP_ERROR(
18810 "cancel commit bad exit: %d "
18811 TRX_ID_FMT,
18812 rcode, victim_trx->id);
18813 /* unable to interrupt, must abort */
18814 /* note: kill_mysql() will block, if we cannot.
18815 * kill the lock holder first.
18816 */
18817 abort();
18818 break;
18819 }
18820 }
18821 wsrep_thd_UNLOCK(thd);
18822 wsrep_thd_awake(thd, signal);
18823 break;
18824 case QUERY_EXEC:
18825 /* it is possible that victim trx is itself waiting for some
18826 * other lock. We need to cancel this waiting
18827 */
18828 WSREP_DEBUG("kill trx QUERY_EXEC for " TRX_ID_FMT,
18829 victim_trx->id);
18830
18831 victim_trx->lock.was_chosen_as_deadlock_victim= TRUE;
18832
18833 if (victim_trx->lock.wait_lock) {
18834 WSREP_DEBUG("victim has wait flag: %ld",
18835 thd_get_thread_id(thd));
18836 lock_t* wait_lock = victim_trx->lock.wait_lock;
18837
18838 if (wait_lock) {
18839 WSREP_DEBUG("canceling wait lock");
18840 victim_trx->lock.was_chosen_as_deadlock_victim= TRUE;
18841 lock_cancel_waiting_and_release(wait_lock);
18842 }
18843
18844 wsrep_thd_UNLOCK(thd);
18845 wsrep_thd_awake(thd, signal);
18846 } else {
18847 /* abort currently executing query */
18848 DBUG_PRINT("wsrep",("sending KILL_QUERY to: %lu",
18849 thd_get_thread_id(thd)));
18850 WSREP_DEBUG("kill query for: %ld",
18851 thd_get_thread_id(thd));
18852 /* Note that innobase_kill_query will take lock_mutex
18853 and trx_mutex */
18854 wsrep_thd_UNLOCK(thd);
18855 wsrep_thd_awake(thd, signal);
18856
18857 /* for BF thd, we need to prevent him from committing */
18858 if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
18859 wsrep_abort_slave_trx(bf_seqno,
18860 wsrep_thd_trx_seqno(thd));
18861 }
18862 }
18863 break;
18864 case QUERY_IDLE:
18865 {
18866 WSREP_DEBUG("kill IDLE for " TRX_ID_FMT, victim_trx->id);
18867
18868 if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
18869 WSREP_DEBUG("kill BF IDLE, seqno: %lld",
18870 (long long)wsrep_thd_trx_seqno(thd));
18871 wsrep_thd_UNLOCK(thd);
18872 wsrep_abort_slave_trx(bf_seqno,
18873 wsrep_thd_trx_seqno(thd));
18874 DBUG_RETURN(0);
18875 }
18876 /* This will lock thd from proceeding after net_read() */
18877 wsrep_thd_set_conflict_state(thd, ABORTING);
18878
18879 wsrep_lock_rollback();
18880
18881 if (wsrep_aborting_thd_contains(thd)) {
18882 WSREP_WARN("duplicate thd aborter %lu",
18883 (ulong) thd_get_thread_id(thd));
18884 } else {
18885 wsrep_aborting_thd_enqueue(thd);
18886 DBUG_PRINT("wsrep",("enqueuing trx abort for %lu",
18887 thd_get_thread_id(thd)));
18888 WSREP_DEBUG("enqueuing trx abort for (%lu)",
18889 thd_get_thread_id(thd));
18890 }
18891
18892 DBUG_PRINT("wsrep",("signalling wsrep rollbacker"));
18893 WSREP_DEBUG("signaling aborter");
18894 wsrep_unlock_rollback();
18895 wsrep_thd_UNLOCK(thd);
18896
18897 break;
18898 }
18899 default:
18900 WSREP_WARN("bad wsrep query state: %d",
18901 wsrep_thd_query_state(thd));
18902 wsrep_thd_UNLOCK(thd);
18903 break;
18904 }
18905
18906 DBUG_RETURN(0);
18907}
18908
18909static
18910int
18911wsrep_abort_transaction(
18912/*====================*/
18913 handlerton*,
18914 THD *bf_thd,
18915 THD *victim_thd,
18916 my_bool signal)
18917{
18918 DBUG_ENTER("wsrep_innobase_abort_thd");
18919 trx_t* victim_trx = thd_to_trx(victim_thd);
18920 trx_t* bf_trx = (bf_thd) ? thd_to_trx(bf_thd) : NULL;
18921
18922 WSREP_DEBUG("abort transaction: BF: %s victim: %s",
18923 wsrep_thd_query(bf_thd),
18924 wsrep_thd_query(victim_thd));
18925
18926 if (victim_trx) {
18927 lock_mutex_enter();
18928 trx_mutex_enter(victim_trx);
18929 int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
18930 victim_trx, signal);
18931 lock_mutex_exit();
18932 trx_mutex_exit(victim_trx);
18933 wsrep_srv_conc_cancel_wait(victim_trx);
18934 DBUG_RETURN(rcode);
18935 } else {
18936 WSREP_DEBUG("victim does not have transaction");
18937 wsrep_thd_LOCK(victim_thd);
18938 wsrep_thd_set_conflict_state(victim_thd, MUST_ABORT);
18939 wsrep_thd_UNLOCK(victim_thd);
18940 wsrep_thd_awake(victim_thd, signal);
18941 }
18942
18943 DBUG_RETURN(-1);
18944}
18945
18946static
18947int
18948innobase_wsrep_set_checkpoint(
18949/*==========================*/
18950 handlerton* hton,
18951 const XID* xid)
18952{
18953 DBUG_ASSERT(hton == innodb_hton_ptr);
18954
18955 if (wsrep_is_wsrep_xid(xid)) {
18956
18957 trx_rseg_update_wsrep_checkpoint(xid);
18958 innobase_flush_logs(hton, false);
18959 return 0;
18960 } else {
18961 return 1;
18962 }
18963}
18964
18965static
18966int
18967innobase_wsrep_get_checkpoint(
18968/*==========================*/
18969 handlerton* hton,
18970 XID* xid)
18971{
18972 DBUG_ASSERT(hton == innodb_hton_ptr);
18973 trx_rseg_read_wsrep_checkpoint(*xid);
18974 return 0;
18975}
18976
18977static void wsrep_fake_trx_id(handlerton *, THD *thd)
18978{
18979 trx_id_t trx_id = trx_sys.get_new_trx_id();
18980 WSREP_DEBUG("innodb fake trx id: " TRX_ID_FMT " thd: %s",
18981 trx_id, wsrep_thd_query(thd));
18982 wsrep_ws_handle_for_trx(wsrep_thd_ws_handle(thd), trx_id);
18983}
18984
18985#endif /* WITH_WSREP */
18986
18987/* plugin options */
18988
18989static MYSQL_SYSVAR_ENUM(checksum_algorithm, srv_checksum_algorithm,
18990 PLUGIN_VAR_RQCMDARG,
18991 "The algorithm InnoDB uses for page checksumming. Possible values are"
18992 " CRC32 (hardware accelerated if the CPU supports it)"
18993 " write crc32, allow any of the other checksums to match when reading;"
18994 " STRICT_CRC32"
18995 " write crc32, do not allow other algorithms to match when reading;"
18996 " INNODB"
18997 " write a software calculated checksum, allow any other checksums"
18998 " to match when reading;"
18999 " STRICT_INNODB"
19000 " write a software calculated checksum, do not allow other algorithms"
19001 " to match when reading;"
19002 " NONE"
19003 " write a constant magic number, do not do any checksum verification"
19004 " when reading (same as innodb_checksums=OFF);"
19005 " STRICT_NONE"
19006 " write a constant magic number, do not allow values other than that"
19007 " magic number when reading;"
19008 " Files updated when this option is set to crc32 or strict_crc32 will"
19009 " not be readable by MariaDB versions older than 10.0.4",
19010 NULL, NULL, SRV_CHECKSUM_ALGORITHM_CRC32,
19011 &innodb_checksum_algorithm_typelib);
19012
19013static MYSQL_SYSVAR_BOOL(log_checksums, innodb_log_checksums,
19014 PLUGIN_VAR_RQCMDARG,
19015 "Whether to compute and require checksums for InnoDB redo log blocks",
19016 NULL, innodb_log_checksums_update, TRUE);
19017
19018static MYSQL_SYSVAR_BOOL(checksums, innobase_use_checksums,
19019 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19020 "DEPRECATED. Use innodb_checksum_algorithm=NONE instead of setting"
19021 " this to OFF."
19022 " Enable InnoDB checksums validation (enabled by default)."
19023 " Disable with --skip-innodb-checksums.",
19024 NULL, NULL, TRUE);
19025
19026static MYSQL_SYSVAR_STR(data_home_dir, innobase_data_home_dir,
19027 PLUGIN_VAR_READONLY,
19028 "The common part for InnoDB table spaces.",
19029 NULL, NULL, NULL);
19030
19031static MYSQL_SYSVAR_BOOL(doublewrite, srv_use_doublewrite_buf,
19032 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19033 "Enable InnoDB doublewrite buffer (enabled by default)."
19034 " Disable with --skip-innodb-doublewrite.",
19035 NULL, NULL, TRUE);
19036
19037static MYSQL_SYSVAR_BOOL(use_atomic_writes, innobase_use_atomic_writes,
19038 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19039 "Enable atomic writes, instead of using the doublewrite buffer, for files "
19040 "on devices that supports atomic writes. "
19041 "To use this option one must use "
19042 "innodb_file_per_table=1, innodb_flush_method=O_DIRECT. "
19043 "This option only works on Linux with either FusionIO cards using "
19044 "the directFS filesystem or with Shannon cards using any file system.",
19045 NULL, NULL, TRUE);
19046
19047static MYSQL_SYSVAR_BOOL(stats_include_delete_marked,
19048 srv_stats_include_delete_marked,
19049 PLUGIN_VAR_OPCMDARG,
19050 "Include delete marked records when calculating persistent statistics",
19051 NULL, NULL, FALSE);
19052
19053static MYSQL_SYSVAR_ULONG(io_capacity, srv_io_capacity,
19054 PLUGIN_VAR_RQCMDARG,
19055 "Number of IOPs the server can do. Tunes the background IO rate",
19056 NULL, innodb_io_capacity_update, 200, 100, ~0UL, 0);
19057
19058static MYSQL_SYSVAR_ULONG(io_capacity_max, srv_max_io_capacity,
19059 PLUGIN_VAR_RQCMDARG,
19060 "Limit to which innodb_io_capacity can be inflated.",
19061 NULL, innodb_io_capacity_max_update,
19062 SRV_MAX_IO_CAPACITY_DUMMY_DEFAULT, 100,
19063 SRV_MAX_IO_CAPACITY_LIMIT, 0);
19064
19065static MYSQL_SYSVAR_ULONG(idle_flush_pct,
19066 srv_idle_flush_pct,
19067 PLUGIN_VAR_RQCMDARG,
19068 "Up to what percentage of dirty pages should be flushed when innodb "
19069 "finds it has spare resources to do so.",
19070 NULL, NULL, 100, 0, 100, 0);
19071
19072#ifdef UNIV_DEBUG
19073static MYSQL_SYSVAR_BOOL(background_drop_list_empty,
19074 innodb_background_drop_list_empty,
19075 PLUGIN_VAR_OPCMDARG,
19076 "Wait for the background drop list to become empty",
19077 NULL, wait_background_drop_list_empty, FALSE);
19078
19079static MYSQL_SYSVAR_BOOL(log_checkpoint_now, innodb_log_checkpoint_now,
19080 PLUGIN_VAR_OPCMDARG,
19081 "Force checkpoint now",
19082 NULL, checkpoint_now_set, FALSE);
19083
19084static MYSQL_SYSVAR_BOOL(buf_flush_list_now, innodb_buf_flush_list_now,
19085 PLUGIN_VAR_OPCMDARG,
19086 "Force dirty page flush now",
19087 NULL, buf_flush_list_now_set, FALSE);
19088
19089static MYSQL_SYSVAR_UINT(merge_threshold_set_all_debug,
19090 innodb_merge_threshold_set_all_debug,
19091 PLUGIN_VAR_RQCMDARG,
19092 "Override current MERGE_THRESHOLD setting for all indexes at dictionary"
19093 " cache by the specified value dynamically, at the time.",
19094 NULL, innodb_merge_threshold_set_all_debug_update,
19095 DICT_INDEX_MERGE_THRESHOLD_DEFAULT, 1, 50, 0);
19096#endif /* UNIV_DEBUG */
19097
19098static MYSQL_SYSVAR_ULONG(purge_batch_size, srv_purge_batch_size,
19099 PLUGIN_VAR_OPCMDARG,
19100 "Number of UNDO log pages to purge in one batch from the history list.",
19101 NULL, NULL,
19102 300, /* Default setting */
19103 1, /* Minimum value */
19104 5000, 0); /* Maximum value */
19105
19106static MYSQL_SYSVAR_ULONG(purge_threads, srv_n_purge_threads,
19107 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19108 "Purge threads can be from 1 to 32. Default is 4.",
19109 NULL, NULL,
19110 4, /* Default setting */
19111 1, /* Minimum value */
19112 32, 0); /* Maximum value */
19113
19114static MYSQL_SYSVAR_ULONG(sync_array_size, srv_sync_array_size,
19115 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19116 "Size of the mutex/lock wait array.",
19117 NULL, NULL,
19118 1, /* Default setting */
19119 1, /* Minimum value */
19120 1024, 0); /* Maximum value */
19121
19122static MYSQL_SYSVAR_UINT(fast_shutdown, srv_fast_shutdown,
19123 PLUGIN_VAR_OPCMDARG,
19124 "Speeds up the shutdown process of the InnoDB storage engine. Possible"
19125 " values are 0, 1 (faster), 2 (crash-like), 3 (fastest clean).",
19126 fast_shutdown_validate, NULL, 1, 0, 3, 0);
19127
19128static MYSQL_SYSVAR_BOOL(file_per_table, srv_file_per_table,
19129 PLUGIN_VAR_NOCMDARG,
19130 "Stores each InnoDB table to an .ibd file in the database dir.",
19131 NULL, NULL, TRUE);
19132
19133static MYSQL_SYSVAR_STR(ft_server_stopword_table, innobase_server_stopword_table,
19134 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_MEMALLOC,
19135 "The user supplied stopword table name.",
19136 innodb_stopword_table_validate,
19137 NULL,
19138 NULL);
19139
19140static MYSQL_SYSVAR_UINT(flush_log_at_timeout, srv_flush_log_at_timeout,
19141 PLUGIN_VAR_OPCMDARG,
19142 "Write and flush logs every (n) second.",
19143 NULL, NULL, 1, 0, 2700, 0);
19144
19145static MYSQL_SYSVAR_ULONG(flush_log_at_trx_commit, srv_flush_log_at_trx_commit,
19146 PLUGIN_VAR_OPCMDARG,
19147 "Controls the durability/speed trade-off for commits."
19148 " Set to 0 (write and flush redo log to disk only once per second),"
19149 " 1 (flush to disk at each commit),"
19150 " 2 (write to log at commit but flush to disk only once per second)"
19151 " or 3 (flush to disk at prepare and at commit, slower and usually redundant)."
19152 " 1 and 3 guarantees that after a crash, committed transactions will"
19153 " not be lost and will be consistent with the binlog and other transactional"
19154 " engines. 2 can get inconsistent and lose transactions if there is a"
19155 " power failure or kernel crash but not if mysqld crashes. 0 has no"
19156 " guarantees in case of crash. 0 and 2 can be faster than 1 or 3.",
19157 NULL, NULL, 1, 0, 3, 0);
19158
19159static MYSQL_SYSVAR_ENUM(flush_method, innodb_flush_method,
19160 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19161 "With which method to flush data.",
19162 NULL, NULL, IF_WIN(SRV_ALL_O_DIRECT_FSYNC, SRV_FSYNC),
19163 &innodb_flush_method_typelib);
19164
19165static MYSQL_SYSVAR_BOOL(force_load_corrupted, srv_load_corrupted,
19166 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19167 "Force InnoDB to load metadata of corrupted table.",
19168 NULL, NULL, FALSE);
19169
19170static MYSQL_SYSVAR_BOOL(locks_unsafe_for_binlog, innobase_locks_unsafe_for_binlog,
19171 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19172 "DEPRECATED. This option may be removed in future releases."
19173 " Please use READ COMMITTED transaction isolation level instead."
19174 " Force InnoDB to not use next-key locking, to use only row-level locking.",
19175 NULL, NULL, FALSE);
19176
19177static MYSQL_SYSVAR_STR(log_group_home_dir, srv_log_group_home_dir,
19178 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19179 "Path to InnoDB log files.", NULL, NULL, NULL);
19180
19181/** Update innodb_page_cleaners.
19182@param[in] save the new value of innodb_page_cleaners */
19183static
19184void
19185innodb_page_cleaners_threads_update(THD*, struct st_mysql_sys_var*, void*, const void *save)
19186{
19187 buf_flush_set_page_cleaner_thread_cnt(*static_cast<const ulong*>(save));
19188}
19189
19190static MYSQL_SYSVAR_ULONG(page_cleaners, srv_n_page_cleaners,
19191 PLUGIN_VAR_RQCMDARG,
19192 "Page cleaner threads can be from 1 to 64. Default is 4.",
19193 NULL,
19194 innodb_page_cleaners_threads_update, 4, 1, 64, 0);
19195
19196static MYSQL_SYSVAR_DOUBLE(max_dirty_pages_pct, srv_max_buf_pool_modified_pct,
19197 PLUGIN_VAR_RQCMDARG,
19198 "Percentage of dirty pages allowed in bufferpool.",
19199 NULL, innodb_max_dirty_pages_pct_update, 75.0, 0, 99.999, 0);
19200
19201static MYSQL_SYSVAR_DOUBLE(max_dirty_pages_pct_lwm,
19202 srv_max_dirty_pages_pct_lwm,
19203 PLUGIN_VAR_RQCMDARG,
19204 "Percentage of dirty pages at which flushing kicks in.",
19205 NULL, innodb_max_dirty_pages_pct_lwm_update, 0, 0, 99.999, 0);
19206
19207static MYSQL_SYSVAR_DOUBLE(adaptive_flushing_lwm,
19208 srv_adaptive_flushing_lwm,
19209 PLUGIN_VAR_RQCMDARG,
19210 "Percentage of log capacity below which no adaptive flushing happens.",
19211 NULL, NULL, 10.0, 0.0, 70.0, 0);
19212
19213static MYSQL_SYSVAR_BOOL(adaptive_flushing, srv_adaptive_flushing,
19214 PLUGIN_VAR_NOCMDARG,
19215 "Attempt flushing dirty pages to avoid IO bursts at checkpoints.",
19216 NULL, NULL, TRUE);
19217
19218static MYSQL_SYSVAR_BOOL(flush_sync, srv_flush_sync,
19219 PLUGIN_VAR_NOCMDARG,
19220 "Allow IO bursts at the checkpoints ignoring io_capacity setting.",
19221 NULL, NULL, TRUE);
19222
19223static MYSQL_SYSVAR_ULONG(flushing_avg_loops,
19224 srv_flushing_avg_loops,
19225 PLUGIN_VAR_RQCMDARG,
19226 "Number of iterations over which the background flushing is averaged.",
19227 NULL, NULL, 30, 1, 1000, 0);
19228
19229static MYSQL_SYSVAR_ULONG(max_purge_lag, srv_max_purge_lag,
19230 PLUGIN_VAR_RQCMDARG,
19231 "Desired maximum length of the purge queue (0 = no limit)",
19232 NULL, NULL, 0, 0, ~0UL, 0);
19233
19234static MYSQL_SYSVAR_ULONG(max_purge_lag_delay, srv_max_purge_lag_delay,
19235 PLUGIN_VAR_RQCMDARG,
19236 "Maximum delay of user threads in micro-seconds",
19237 NULL, NULL,
19238 0L, /* Default seting */
19239 0L, /* Minimum value */
19240 10000000UL, 0); /* Maximum value */
19241
19242static MYSQL_SYSVAR_BOOL(rollback_on_timeout, innobase_rollback_on_timeout,
19243 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19244 "Roll back the complete transaction on lock wait timeout, for 4.x compatibility (disabled by default)",
19245 NULL, NULL, FALSE);
19246
19247static MYSQL_SYSVAR_BOOL(status_file, innobase_create_status_file,
19248 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_NOSYSVAR,
19249 "Enable SHOW ENGINE INNODB STATUS output in the innodb_status.<pid> file",
19250 NULL, NULL, FALSE);
19251
19252static MYSQL_SYSVAR_BOOL(stats_on_metadata, innobase_stats_on_metadata,
19253 PLUGIN_VAR_OPCMDARG,
19254 "Enable statistics gathering for metadata commands such as"
19255 " SHOW TABLE STATUS for tables that use transient statistics (off by default)",
19256 NULL, NULL, FALSE);
19257
19258static MYSQL_SYSVAR_ULONGLONG(stats_sample_pages, srv_stats_transient_sample_pages,
19259 PLUGIN_VAR_RQCMDARG,
19260 "Deprecated, use innodb_stats_transient_sample_pages instead",
19261 NULL, innodb_stats_sample_pages_update, 8, 1, ~0ULL, 0);
19262
19263static MYSQL_SYSVAR_ULONGLONG(stats_transient_sample_pages,
19264 srv_stats_transient_sample_pages,
19265 PLUGIN_VAR_RQCMDARG,
19266 "The number of leaf index pages to sample when calculating transient"
19267 " statistics (if persistent statistics are not used, default 8)",
19268 NULL, NULL, 8, 1, ~0ULL, 0);
19269
19270static MYSQL_SYSVAR_BOOL(stats_persistent, srv_stats_persistent,
19271 PLUGIN_VAR_OPCMDARG,
19272 "InnoDB persistent statistics enabled for all tables unless overridden"
19273 " at table level",
19274 NULL, NULL, TRUE);
19275
19276static MYSQL_SYSVAR_BOOL(stats_auto_recalc, srv_stats_auto_recalc,
19277 PLUGIN_VAR_OPCMDARG,
19278 "InnoDB automatic recalculation of persistent statistics enabled for all"
19279 " tables unless overridden at table level (automatic recalculation is only"
19280 " done when InnoDB decides that the table has changed too much and needs a"
19281 " new statistics)",
19282 NULL, NULL, TRUE);
19283
19284static MYSQL_SYSVAR_ULONGLONG(stats_persistent_sample_pages,
19285 srv_stats_persistent_sample_pages,
19286 PLUGIN_VAR_RQCMDARG,
19287 "The number of leaf index pages to sample when calculating persistent"
19288 " statistics (by ANALYZE, default 20)",
19289 NULL, NULL, 20, 1, ~0ULL, 0);
19290
19291static MYSQL_SYSVAR_ULONGLONG(stats_modified_counter, srv_stats_modified_counter,
19292 PLUGIN_VAR_RQCMDARG,
19293 "The number of rows modified before we calculate new statistics (default 0 = current limits)",
19294 NULL, NULL, 0, 0, ~0ULL, 0);
19295
19296static MYSQL_SYSVAR_BOOL(stats_traditional, srv_stats_sample_traditional,
19297 PLUGIN_VAR_RQCMDARG,
19298 "Enable traditional statistic calculation based on number of configured pages (default true)",
19299 NULL, NULL, TRUE);
19300
19301#ifdef BTR_CUR_HASH_ADAPT
19302static MYSQL_SYSVAR_BOOL(adaptive_hash_index, btr_search_enabled,
19303 PLUGIN_VAR_OPCMDARG,
19304 "Enable InnoDB adaptive hash index (enabled by default). "
19305 " Disable with --skip-innodb-adaptive-hash-index.",
19306 NULL, innodb_adaptive_hash_index_update, true);
19307
19308/** Number of distinct partitions of AHI.
19309Each partition is protected by its own latch and so we have parts number
19310of latches protecting complete search system. */
19311static MYSQL_SYSVAR_ULONG(adaptive_hash_index_parts, btr_ahi_parts,
19312 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19313 "Number of InnoDB Adaptive Hash Index Partitions (default 8)",
19314 NULL, NULL, 8, 1, 512, 0);
19315#endif /* BTR_CUR_HASH_ADAPT */
19316
19317static MYSQL_SYSVAR_ULONG(replication_delay, srv_replication_delay,
19318 PLUGIN_VAR_RQCMDARG,
19319 "Replication thread delay (ms) on the slave server if"
19320 " innodb_thread_concurrency is reached (0 by default)",
19321 NULL, NULL, 0, 0, ~0UL, 0);
19322
19323static MYSQL_SYSVAR_UINT(compression_level, page_zip_level,
19324 PLUGIN_VAR_RQCMDARG,
19325 "Compression level used for zlib compression. 0 is no compression"
19326 ", 1 is fastest, 9 is best compression and default is 6.",
19327 NULL, NULL, DEFAULT_COMPRESSION_LEVEL, 0, 9, 0);
19328
19329static MYSQL_SYSVAR_BOOL(log_compressed_pages, page_zip_log_pages,
19330 PLUGIN_VAR_OPCMDARG,
19331 "Enables/disables the logging of entire compressed page images."
19332 " InnoDB logs the compressed pages to prevent corruption if"
19333 " the zlib compression algorithm changes."
19334 " When turned OFF, InnoDB will assume that the zlib"
19335 " compression algorithm doesn't change.",
19336 NULL, NULL, TRUE);
19337
19338static MYSQL_SYSVAR_ULONG(autoextend_increment,
19339 sys_tablespace_auto_extend_increment,
19340 PLUGIN_VAR_RQCMDARG,
19341 "Data file autoextend increment in megabytes",
19342 NULL, NULL, 64L, 1L, 1000L, 0);
19343
19344/** Validate the requested buffer pool size. Also, reserve the necessary
19345memory needed for buffer pool resize.
19346@param[in] thd thread handle
19347@param[in] var pointer to system variable
19348@param[out] save immediate result for update function
19349@param[in] value incoming string
19350@return 0 on success, 1 on failure.
19351*/
19352static
19353int
19354innodb_buffer_pool_size_validate(
19355 THD* thd,
19356 struct st_mysql_sys_var* var,
19357 void* save,
19358 struct st_mysql_value* value);
19359
19360/* If the default value of innodb_buffer_pool_size is increased to be more than
19361BUF_POOL_SIZE_THRESHOLD (srv/srv0start.cc), then srv_buf_pool_instances_default
19362can be removed and 8 used instead. The problem with the current setup is that
19363with 128MiB default buffer pool size and 8 instances by default we would emit
19364a warning when no options are specified. */
19365static MYSQL_SYSVAR_ULONGLONG(buffer_pool_size, innobase_buffer_pool_size,
19366 PLUGIN_VAR_RQCMDARG,
19367 "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.",
19368 innodb_buffer_pool_size_validate,
19369 innodb_buffer_pool_size_update,
19370 srv_buf_pool_def_size,
19371 srv_buf_pool_min_size,
19372 LLONG_MAX, 1024*1024L);
19373
19374static MYSQL_SYSVAR_ULONG(buffer_pool_chunk_size, srv_buf_pool_chunk_unit,
19375 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19376 "Size of a single memory chunk within each buffer pool instance"
19377 " for resizing buffer pool. Online buffer pool resizing happens"
19378 " at this granularity. 0 means disable resizing buffer pool.",
19379 NULL, NULL,
19380 128 * 1024 * 1024, 1024 * 1024, LONG_MAX, 1024 * 1024);
19381
19382#if defined UNIV_DEBUG || defined UNIV_PERF_DEBUG
19383static MYSQL_SYSVAR_ULONG(page_hash_locks, srv_n_page_hash_locks,
19384 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19385 "Number of rw_locks protecting buffer pool page_hash. Rounded up to the next power of 2",
19386 NULL, NULL, 16, 1, MAX_PAGE_HASH_LOCKS, 0);
19387
19388static MYSQL_SYSVAR_ULONG(doublewrite_batch_size, srv_doublewrite_batch_size,
19389 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19390 "Number of pages reserved in doublewrite buffer for batch flushing",
19391 NULL, NULL, 120, 1, 127, 0);
19392#endif /* defined UNIV_DEBUG || defined UNIV_PERF_DEBUG */
19393
19394static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm,
19395 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19396 "The algorithm Innodb uses for deciding which locks to grant next when"
19397 " a lock is released. Possible values are"
19398 " FCFS"
19399 " grant the locks in First-Come-First-Served order;"
19400 " VATS"
19401 " use the Variance-Aware-Transaction-Scheduling algorithm, which"
19402 " uses an Eldest-Transaction-First heuristic.",
19403 NULL, NULL, INNODB_LOCK_SCHEDULE_ALGORITHM_VATS,
19404 &innodb_lock_schedule_algorithm_typelib);
19405
19406static MYSQL_SYSVAR_ULONG(buffer_pool_instances, srv_buf_pool_instances,
19407 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19408 "Number of buffer pool instances, set to higher value on high-end machines to increase scalability",
19409 NULL, NULL, srv_buf_pool_instances_default, 0, MAX_BUFFER_POOLS, 0);
19410
19411static MYSQL_SYSVAR_STR(buffer_pool_filename, srv_buf_dump_filename,
19412 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
19413 "Filename to/from which to dump/load the InnoDB buffer pool",
19414 innodb_srv_buf_dump_filename_validate, NULL, SRV_BUF_DUMP_FILENAME_DEFAULT);
19415
19416static MYSQL_SYSVAR_BOOL(buffer_pool_dump_now, innodb_buffer_pool_dump_now,
19417 PLUGIN_VAR_RQCMDARG,
19418 "Trigger an immediate dump of the buffer pool into a file named @@innodb_buffer_pool_filename",
19419 NULL, buffer_pool_dump_now, FALSE);
19420
19421static MYSQL_SYSVAR_BOOL(buffer_pool_dump_at_shutdown, srv_buffer_pool_dump_at_shutdown,
19422 PLUGIN_VAR_RQCMDARG,
19423 "Dump the buffer pool into a file named @@innodb_buffer_pool_filename",
19424 NULL, NULL, TRUE);
19425
19426static MYSQL_SYSVAR_ULONG(buffer_pool_dump_pct, srv_buf_pool_dump_pct,
19427 PLUGIN_VAR_RQCMDARG,
19428 "Dump only the hottest N% of each buffer pool, defaults to 25",
19429 NULL, NULL, 25, 1, 100, 0);
19430
19431#ifdef UNIV_DEBUG
19432/* Added to test the innodb_buffer_pool_load_incomplete status variable. */
19433static MYSQL_SYSVAR_ULONG(buffer_pool_load_pages_abort, srv_buf_pool_load_pages_abort,
19434 PLUGIN_VAR_RQCMDARG,
19435 "Number of pages during a buffer pool load to process before signaling innodb_buffer_pool_load_abort=1",
19436 NULL, NULL, LONG_MAX, 1, LONG_MAX, 0);
19437
19438static MYSQL_SYSVAR_STR(buffer_pool_evict, srv_buffer_pool_evict,
19439 PLUGIN_VAR_RQCMDARG,
19440 "Evict pages from the buffer pool",
19441 NULL, innodb_buffer_pool_evict_update, "");
19442#endif /* UNIV_DEBUG */
19443
19444static MYSQL_SYSVAR_BOOL(buffer_pool_load_now, innodb_buffer_pool_load_now,
19445 PLUGIN_VAR_RQCMDARG,
19446 "Trigger an immediate load of the buffer pool from a file named @@innodb_buffer_pool_filename",
19447 NULL, buffer_pool_load_now, FALSE);
19448
19449static MYSQL_SYSVAR_BOOL(buffer_pool_load_abort, innodb_buffer_pool_load_abort,
19450 PLUGIN_VAR_RQCMDARG,
19451 "Abort a currently running load of the buffer pool",
19452 NULL, buffer_pool_load_abort, FALSE);
19453
19454/* there is no point in changing this during runtime, thus readonly */
19455static MYSQL_SYSVAR_BOOL(buffer_pool_load_at_startup, srv_buffer_pool_load_at_startup,
19456 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19457 "Load the buffer pool from a file named @@innodb_buffer_pool_filename",
19458 NULL, NULL, TRUE);
19459
19460static MYSQL_SYSVAR_BOOL(defragment, srv_defragment,
19461 PLUGIN_VAR_RQCMDARG,
19462 "Enable/disable InnoDB defragmentation (default FALSE). When set to FALSE, all existing "
19463 "defragmentation will be paused. And new defragmentation command will fail."
19464 "Paused defragmentation commands will resume when this variable is set to "
19465 "true again.",
19466 NULL, NULL, FALSE);
19467
19468static MYSQL_SYSVAR_UINT(defragment_n_pages, srv_defragment_n_pages,
19469 PLUGIN_VAR_RQCMDARG,
19470 "Number of pages considered at once when merging multiple pages to "
19471 "defragment",
19472 NULL, NULL, 7, 2, 32, 0);
19473
19474static MYSQL_SYSVAR_UINT(defragment_stats_accuracy,
19475 srv_defragment_stats_accuracy,
19476 PLUGIN_VAR_RQCMDARG,
19477 "How many defragment stats changes there are before the stats "
19478 "are written to persistent storage. Set to 0 meaning disable "
19479 "defragment stats tracking.",
19480 NULL, NULL, 0, 0, ~0U, 0);
19481
19482static MYSQL_SYSVAR_UINT(defragment_fill_factor_n_recs,
19483 srv_defragment_fill_factor_n_recs,
19484 PLUGIN_VAR_RQCMDARG,
19485 "How many records of space defragmentation should leave on the page. "
19486 "This variable, together with innodb_defragment_fill_factor, is introduced "
19487 "so defragmentation won't pack the page too full and cause page split on "
19488 "the next insert on every page. The variable indicating more defragmentation"
19489 " gain is the one effective.",
19490 NULL, NULL, 20, 1, 100, 0);
19491
19492static MYSQL_SYSVAR_DOUBLE(defragment_fill_factor, srv_defragment_fill_factor,
19493 PLUGIN_VAR_RQCMDARG,
19494 "A number between [0.7, 1] that tells defragmentation how full it should "
19495 "fill a page. Default is 0.9. Number below 0.7 won't make much sense."
19496 "This variable, together with innodb_defragment_fill_factor_n_recs, is "
19497 "introduced so defragmentation won't pack the page too full and cause "
19498 "page split on the next insert on every page. The variable indicating more "
19499 "defragmentation gain is the one effective.",
19500 NULL, NULL, 0.9, 0.7, 1, 0);
19501
19502static MYSQL_SYSVAR_UINT(defragment_frequency, srv_defragment_frequency,
19503 PLUGIN_VAR_RQCMDARG,
19504 "Do not defragment a single index more than this number of time per second."
19505 "This controls the number of time defragmentation thread can request X_LOCK "
19506 "on an index. Defragmentation thread will check whether "
19507 "1/defragment_frequency (s) has passed since it worked on this index last "
19508 "time, and put the index back to the queue if not enough time has passed. "
19509 "The actual frequency can only be lower than this given number.",
19510 NULL, innodb_defragment_frequency_update,
19511 SRV_DEFRAGMENT_FREQUENCY_DEFAULT, 1, 1000, 0);
19512
19513
19514static MYSQL_SYSVAR_ULONG(lru_scan_depth, srv_LRU_scan_depth,
19515 PLUGIN_VAR_RQCMDARG,
19516 "How deep to scan LRU to keep it clean",
19517 NULL, NULL, 1024, 100, ~0UL, 0);
19518
19519static MYSQL_SYSVAR_ULONG(flush_neighbors, srv_flush_neighbors,
19520 PLUGIN_VAR_OPCMDARG,
19521 "Set to 0 (don't flush neighbors from buffer pool),"
19522 " 1 (flush contiguous neighbors from buffer pool)"
19523 " or 2 (flush neighbors from buffer pool),"
19524 " when flushing a block",
19525 NULL, NULL, 1, 0, 2, 0);
19526
19527static MYSQL_SYSVAR_ULONG(commit_concurrency, innobase_commit_concurrency,
19528 PLUGIN_VAR_RQCMDARG,
19529 "Helps in performance tuning in heavily concurrent environments.",
19530 innobase_commit_concurrency_validate, NULL, 0, 0, 1000, 0);
19531
19532static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter,
19533 PLUGIN_VAR_RQCMDARG,
19534 "Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket",
19535 NULL, NULL, 5000L, 1L, ~0UL, 0);
19536
19537static MYSQL_SYSVAR_BOOL(deadlock_detect, innobase_deadlock_detect,
19538 PLUGIN_VAR_NOCMDARG,
19539 "Enable/disable InnoDB deadlock detector (default ON)."
19540 " if set to OFF, deadlock detection is skipped,"
19541 " and we rely on innodb_lock_wait_timeout in case of deadlock.",
19542 NULL, NULL, TRUE);
19543
19544static MYSQL_SYSVAR_UINT(fill_factor, innobase_fill_factor,
19545 PLUGIN_VAR_RQCMDARG,
19546 "Percentage of B-tree page filled during bulk insert",
19547 NULL, NULL, 100, 10, 100, 0);
19548
19549static MYSQL_SYSVAR_BOOL(ft_enable_diag_print, fts_enable_diag_print,
19550 PLUGIN_VAR_OPCMDARG,
19551 "Whether to enable additional FTS diagnostic printout ",
19552 NULL, NULL, FALSE);
19553
19554static MYSQL_SYSVAR_BOOL(disable_sort_file_cache, srv_disable_sort_file_cache,
19555 PLUGIN_VAR_OPCMDARG,
19556 "Whether to disable OS system file cache for sort I/O",
19557 NULL, NULL, FALSE);
19558
19559static MYSQL_SYSVAR_STR(ft_aux_table, fts_internal_tbl_name2,
19560 PLUGIN_VAR_RQCMDARG,
19561 "FTS internal auxiliary table to be checked",
19562 innodb_internal_table_validate,
19563 innodb_internal_table_update, NULL);
19564
19565static MYSQL_SYSVAR_ULONG(ft_cache_size, fts_max_cache_size,
19566 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19567 "InnoDB Fulltext search cache size in bytes",
19568 NULL, NULL, 8000000, 1600000, 80000000, 0);
19569
19570static MYSQL_SYSVAR_ULONG(ft_total_cache_size, fts_max_total_cache_size,
19571 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19572 "Total memory allocated for InnoDB Fulltext Search cache",
19573 NULL, NULL, 640000000, 32000000, 1600000000, 0);
19574
19575static MYSQL_SYSVAR_ULONG(ft_result_cache_limit, fts_result_cache_limit,
19576 PLUGIN_VAR_RQCMDARG,
19577 "InnoDB Fulltext search query result cache limit in bytes",
19578 NULL, NULL, 2000000000L, 1000000L, 4294967295UL, 0);
19579
19580static MYSQL_SYSVAR_ULONG(ft_min_token_size, fts_min_token_size,
19581 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19582 "InnoDB Fulltext search minimum token size in characters",
19583 NULL, NULL, 3, 0, 16, 0);
19584
19585static MYSQL_SYSVAR_ULONG(ft_max_token_size, fts_max_token_size,
19586 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19587 "InnoDB Fulltext search maximum token size in characters",
19588 NULL, NULL, FTS_MAX_WORD_LEN_IN_CHAR, 10, FTS_MAX_WORD_LEN_IN_CHAR, 0);
19589
19590static MYSQL_SYSVAR_ULONG(ft_num_word_optimize, fts_num_word_optimize,
19591 PLUGIN_VAR_OPCMDARG,
19592 "InnoDB Fulltext search number of words to optimize for each optimize table call ",
19593 NULL, NULL, 2000, 1000, 10000, 0);
19594
19595static MYSQL_SYSVAR_ULONG(ft_sort_pll_degree, fts_sort_pll_degree,
19596 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19597 "InnoDB Fulltext search parallel sort degree, will round up to nearest power of 2 number",
19598 NULL, NULL, 2, 1, 16, 0);
19599
19600static MYSQL_SYSVAR_ULONG(sort_buffer_size, srv_sort_buf_size,
19601 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19602 "Memory buffer size for index creation",
19603 NULL, NULL, 1048576, 65536, 64<<20, 0);
19604
19605static MYSQL_SYSVAR_ULONGLONG(online_alter_log_max_size, srv_online_max_size,
19606 PLUGIN_VAR_RQCMDARG,
19607 "Maximum modification log file size for online index creation",
19608 NULL, NULL, 128<<20, 65536, ~0ULL, 0);
19609
19610static MYSQL_SYSVAR_BOOL(optimize_fulltext_only, innodb_optimize_fulltext_only,
19611 PLUGIN_VAR_NOCMDARG,
19612 "Only optimize the Fulltext index of the table",
19613 NULL, NULL, FALSE);
19614
19615static MYSQL_SYSVAR_ULONG(read_io_threads, srv_n_read_io_threads,
19616 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19617 "Number of background read I/O threads in InnoDB.",
19618 NULL, NULL, 4, 1, 64, 0);
19619
19620static MYSQL_SYSVAR_ULONG(write_io_threads, srv_n_write_io_threads,
19621 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19622 "Number of background write I/O threads in InnoDB.",
19623 NULL, NULL, 4, 1, 64, 0);
19624
19625static MYSQL_SYSVAR_ULONG(force_recovery, srv_force_recovery,
19626 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19627 "Helps to save your data in case the disk image of the database becomes corrupt.",
19628 NULL, NULL, 0, 0, 6, 0);
19629
19630static MYSQL_SYSVAR_ULONG(page_size, srv_page_size,
19631 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19632 "Page size to use for all InnoDB tablespaces.",
19633 NULL, NULL, UNIV_PAGE_SIZE_DEF,
19634 UNIV_PAGE_SIZE_MIN, UNIV_PAGE_SIZE_MAX, 0);
19635
19636static MYSQL_SYSVAR_ULONG(log_buffer_size, srv_log_buffer_size,
19637 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19638 "The size of the buffer which InnoDB uses to write log to the log files on disk.",
19639 NULL, NULL, 16L << 20, 256L << 10, LONG_MAX, 1024);
19640
19641static MYSQL_SYSVAR_ULONGLONG(log_file_size, srv_log_file_size,
19642 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19643 "Size of each log file in a log group.",
19644 NULL, NULL, 48 << 20, 1 << 20, 512ULL << 30, UNIV_PAGE_SIZE_MAX);
19645/* OS_FILE_LOG_BLOCK_SIZE would be more appropriate than UNIV_PAGE_SIZE_MAX,
19646but fil_space_t is being used for the redo log, and it uses data pages. */
19647
19648static MYSQL_SYSVAR_ULONG(log_files_in_group, srv_n_log_files,
19649 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19650 "Number of log files in the log group. InnoDB writes to the files in a circular fashion.",
19651 NULL, NULL, 2, 1, SRV_N_LOG_FILES_MAX, 0);
19652
19653static MYSQL_SYSVAR_ULONG(log_write_ahead_size, srv_log_write_ahead_size,
19654 PLUGIN_VAR_RQCMDARG,
19655 "Redo log write ahead unit size to avoid read-on-write,"
19656 " it should match the OS cache block IO size",
19657 NULL, innodb_log_write_ahead_size_update,
19658 8*1024L, OS_FILE_LOG_BLOCK_SIZE, UNIV_PAGE_SIZE_DEF, OS_FILE_LOG_BLOCK_SIZE);
19659
19660static MYSQL_SYSVAR_UINT(old_blocks_pct, innobase_old_blocks_pct,
19661 PLUGIN_VAR_RQCMDARG,
19662 "Percentage of the buffer pool to reserve for 'old' blocks.",
19663 NULL, innodb_old_blocks_pct_update, 100 * 3 / 8, 5, 95, 0);
19664
19665static MYSQL_SYSVAR_UINT(old_blocks_time, buf_LRU_old_threshold_ms,
19666 PLUGIN_VAR_RQCMDARG,
19667 "Move blocks to the 'new' end of the buffer pool if the first access"
19668 " was at least this many milliseconds ago."
19669 " The timeout is disabled if 0.",
19670 NULL, NULL, 1000, 0, UINT_MAX32, 0);
19671
19672static MYSQL_SYSVAR_ULONG(open_files, innobase_open_files,
19673 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19674 "How many files at the maximum InnoDB keeps open at the same time.",
19675 NULL, NULL, 0, 0, LONG_MAX, 0);
19676
19677static MYSQL_SYSVAR_ULONG(sync_spin_loops, srv_n_spin_wait_rounds,
19678 PLUGIN_VAR_RQCMDARG,
19679 "Count of spin-loop rounds in InnoDB mutexes (30 by default)",
19680 NULL, NULL, 30L, 0L, ~0UL, 0);
19681
19682static MYSQL_SYSVAR_UINT(spin_wait_delay, srv_spin_wait_delay,
19683 PLUGIN_VAR_OPCMDARG,
19684 "Maximum delay between polling for a spin lock (4 by default)",
19685 NULL, NULL, 4, 0, 6000, 0);
19686
19687static MYSQL_SYSVAR_ULONG(thread_concurrency, srv_thread_concurrency,
19688 PLUGIN_VAR_RQCMDARG,
19689 "Helps in performance tuning in heavily concurrent environments. Sets the maximum number of threads allowed inside InnoDB. Value 0 will disable the thread throttling.",
19690 NULL, NULL, 0, 0, 1000, 0);
19691
19692static MYSQL_SYSVAR_ULONG(
19693 adaptive_max_sleep_delay, srv_adaptive_max_sleep_delay,
19694 PLUGIN_VAR_RQCMDARG,
19695 "The upper limit of the sleep delay in usec. Value of 0 disables it.",
19696 NULL, NULL,
19697 150000, /* Default setting */
19698 0, /* Minimum value */
19699 1000000, 0); /* Maximum value */
19700
19701static MYSQL_SYSVAR_BOOL(prefix_index_cluster_optimization,
19702 srv_prefix_index_cluster_optimization,
19703 PLUGIN_VAR_OPCMDARG,
19704 "Enable prefix optimization to sometimes avoid cluster index lookups.",
19705 NULL, NULL, FALSE);
19706
19707static MYSQL_SYSVAR_ULONG(thread_sleep_delay, srv_thread_sleep_delay,
19708 PLUGIN_VAR_RQCMDARG,
19709 "Time of innodb thread sleeping before joining InnoDB queue (usec)."
19710 " Value 0 disable a sleep",
19711 NULL, NULL,
19712 10000L,
19713 0L,
19714 1000000L, 0);
19715
19716static MYSQL_SYSVAR_STR(data_file_path, innobase_data_file_path,
19717 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19718 "Path to individual files and their sizes.",
19719 NULL, NULL, "ibdata1:12M:autoextend");
19720
19721static MYSQL_SYSVAR_STR(temp_data_file_path, innobase_temp_data_file_path,
19722 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19723 "Path to files and their sizes making temp-tablespace.",
19724 NULL, NULL, "ibtmp1:12M:autoextend");
19725
19726static MYSQL_SYSVAR_STR(undo_directory, srv_undo_dir,
19727 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19728 "Directory where undo tablespace files live, this path can be absolute.",
19729 NULL, NULL, NULL);
19730
19731static MYSQL_SYSVAR_ULONG(undo_tablespaces, srv_undo_tablespaces,
19732 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19733 "Number of undo tablespaces to use.",
19734 NULL, NULL,
19735 0L, /* Default seting */
19736 0L, /* Minimum value */
19737 TRX_SYS_MAX_UNDO_SPACES, 0); /* Maximum value */
19738
19739static MYSQL_SYSVAR_ULONG(undo_logs, srv_undo_logs,
19740 PLUGIN_VAR_OPCMDARG,
19741 "Number of undo logs to use.",
19742 NULL, NULL,
19743 TRX_SYS_N_RSEGS, /* Default setting */
19744 1, /* Minimum value */
19745 TRX_SYS_N_RSEGS, 0); /* Maximum value */
19746
19747static MYSQL_SYSVAR_ULONGLONG(max_undo_log_size, srv_max_undo_log_size,
19748 PLUGIN_VAR_OPCMDARG,
19749 "Desired maximum UNDO tablespace size in bytes",
19750 NULL, NULL,
19751 10 << 20, 10 << 20,
19752 1ULL << (32 + UNIV_PAGE_SIZE_SHIFT_MAX), 0);
19753
19754static MYSQL_SYSVAR_ULONG(purge_rseg_truncate_frequency,
19755 srv_purge_rseg_truncate_frequency,
19756 PLUGIN_VAR_OPCMDARG,
19757 "Dictates rate at which UNDO records are purged. Value N means"
19758 " purge rollback segment(s) on every Nth iteration of purge invocation",
19759 NULL, NULL, 128, 1, 128, 0);
19760
19761static MYSQL_SYSVAR_BOOL(undo_log_truncate, srv_undo_log_truncate,
19762 PLUGIN_VAR_OPCMDARG,
19763 "Enable or Disable Truncate of UNDO tablespace.",
19764 NULL, NULL, FALSE);
19765
19766/* Alias for innodb_undo_logs, this config variable is deprecated. */
19767static MYSQL_SYSVAR_ULONG(rollback_segments, srv_undo_logs,
19768 PLUGIN_VAR_OPCMDARG,
19769 "Number of undo logs to use (deprecated).",
19770 NULL, NULL,
19771 TRX_SYS_N_RSEGS, /* Default setting */
19772 1, /* Minimum value */
19773 TRX_SYS_N_RSEGS, 0); /* Maximum value */
19774
19775static MYSQL_SYSVAR_LONG(autoinc_lock_mode, innobase_autoinc_lock_mode,
19776 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19777 "The AUTOINC lock modes supported by InnoDB:"
19778 " 0 => Old style AUTOINC locking (for backward compatibility);"
19779 " 1 => New style AUTOINC locking;"
19780 " 2 => No AUTOINC locking (unsafe for SBR)",
19781 NULL, NULL,
19782 AUTOINC_NEW_STYLE_LOCKING, /* Default setting */
19783 AUTOINC_OLD_STYLE_LOCKING, /* Minimum value */
19784 AUTOINC_NO_LOCKING, 0); /* Maximum value */
19785
19786static MYSQL_SYSVAR_STR(version, innodb_version_str,
19787 PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY,
19788 "InnoDB version", NULL, NULL, INNODB_VERSION_STR);
19789
19790static MYSQL_SYSVAR_BOOL(use_native_aio, srv_use_native_aio,
19791 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19792 "Use native AIO if supported on this platform.",
19793 NULL, NULL, TRUE);
19794
19795#ifdef HAVE_LIBNUMA
19796static MYSQL_SYSVAR_BOOL(numa_interleave, srv_numa_interleave,
19797 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
19798 "Use NUMA interleave memory policy to allocate InnoDB buffer pool.",
19799 NULL, NULL, FALSE);
19800#endif /* HAVE_LIBNUMA */
19801
19802static MYSQL_SYSVAR_ENUM(change_buffering, innodb_change_buffering,
19803 PLUGIN_VAR_RQCMDARG,
19804 "Buffer changes to secondary indexes.",
19805 NULL, NULL, IBUF_USE_ALL, &innodb_change_buffering_typelib);
19806
19807static MYSQL_SYSVAR_UINT(change_buffer_max_size,
19808 srv_change_buffer_max_size,
19809 PLUGIN_VAR_RQCMDARG,
19810 "Maximum on-disk size of change buffer in terms of percentage"
19811 " of the buffer pool.",
19812 NULL, innodb_change_buffer_max_size_update,
19813 CHANGE_BUFFER_DEFAULT_SIZE, 0, 50, 0);
19814
19815static MYSQL_SYSVAR_ENUM(stats_method, srv_innodb_stats_method,
19816 PLUGIN_VAR_RQCMDARG,
19817 "Specifies how InnoDB index statistics collection code should"
19818 " treat NULLs. Possible values are NULLS_EQUAL (default),"
19819 " NULLS_UNEQUAL and NULLS_IGNORED",
19820 NULL, NULL, SRV_STATS_NULLS_EQUAL, &innodb_stats_method_typelib);
19821
19822#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
19823static MYSQL_SYSVAR_UINT(change_buffering_debug, ibuf_debug,
19824 PLUGIN_VAR_RQCMDARG,
19825 "Debug flags for InnoDB change buffering (0=none, 2=crash at merge)",
19826 NULL, NULL, 0, 0, 2, 0);
19827
19828static MYSQL_SYSVAR_BOOL(disable_background_merge,
19829 srv_ibuf_disable_background_merge,
19830 PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_RQCMDARG,
19831 "Disable change buffering merges by the master thread",
19832 NULL, NULL, FALSE);
19833#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
19834
19835static MYSQL_SYSVAR_ULONG(buf_dump_status_frequency, srv_buf_dump_status_frequency,
19836 PLUGIN_VAR_RQCMDARG,
19837 "A number between [0, 100] that tells how oftern buffer pool dump status "
19838 "in percentages should be printed. E.g. 10 means that buffer pool dump "
19839 "status is printed when every 10% of number of buffer pool pages are "
19840 "dumped. Default is 0 (only start and end status is printed).",
19841 NULL, NULL, 0, 0, 100, 0);
19842
19843#ifdef WITH_INNODB_DISALLOW_WRITES
19844/*******************************************************
19845 * innobase_disallow_writes variable definition *
19846 *******************************************************/
19847
19848/* Must always init to FALSE. */
19849static my_bool innobase_disallow_writes = FALSE;
19850
19851/**************************************************************************
19852An "update" method for innobase_disallow_writes variable. */
19853static
19854void
19855innobase_disallow_writes_update(THD*, st_mysql_sys_var*,
19856 void* var_ptr, const void* save)
19857{
19858 *(my_bool*)var_ptr = *(my_bool*)save;
19859 ut_a(srv_allow_writes_event);
19860 if (*(my_bool*)var_ptr) {
19861 os_event_reset(srv_allow_writes_event);
19862 } else {
19863 os_event_set(srv_allow_writes_event);
19864 }
19865}
19866
19867static MYSQL_SYSVAR_BOOL(disallow_writes, innobase_disallow_writes,
19868 PLUGIN_VAR_NOCMDOPT,
19869 "Tell InnoDB to stop any writes to disk",
19870 NULL, innobase_disallow_writes_update, FALSE);
19871#endif /* WITH_INNODB_DISALLOW_WRITES */
19872
19873static MYSQL_SYSVAR_BOOL(random_read_ahead, srv_random_read_ahead,
19874 PLUGIN_VAR_NOCMDARG,
19875 "Whether to use read ahead for random access within an extent.",
19876 NULL, NULL, FALSE);
19877
19878static MYSQL_SYSVAR_ULONG(read_ahead_threshold, srv_read_ahead_threshold,
19879 PLUGIN_VAR_RQCMDARG,
19880 "Number of pages that must be accessed sequentially for InnoDB to"
19881 " trigger a readahead.",
19882 NULL, NULL, 56, 0, 64, 0);
19883
19884static MYSQL_SYSVAR_STR(monitor_enable, innobase_enable_monitor_counter,
19885 PLUGIN_VAR_RQCMDARG,
19886 "Turn on a monitor counter",
19887 innodb_monitor_validate,
19888 innodb_enable_monitor_update, NULL);
19889
19890static MYSQL_SYSVAR_STR(monitor_disable, innobase_disable_monitor_counter,
19891 PLUGIN_VAR_RQCMDARG,
19892 "Turn off a monitor counter",
19893 innodb_monitor_validate,
19894 innodb_disable_monitor_update, NULL);
19895
19896static MYSQL_SYSVAR_STR(monitor_reset, innobase_reset_monitor_counter,
19897 PLUGIN_VAR_RQCMDARG,
19898 "Reset a monitor counter",
19899 innodb_monitor_validate,
19900 innodb_reset_monitor_update, NULL);
19901
19902static MYSQL_SYSVAR_STR(monitor_reset_all, innobase_reset_all_monitor_counter,
19903 PLUGIN_VAR_RQCMDARG,
19904 "Reset all values for a monitor counter",
19905 innodb_monitor_validate,
19906 innodb_reset_all_monitor_update, NULL);
19907
19908static MYSQL_SYSVAR_BOOL(status_output, srv_print_innodb_monitor,
19909 PLUGIN_VAR_OPCMDARG, "Enable InnoDB monitor output to the error log.",
19910 NULL, innodb_status_output_update, FALSE);
19911
19912static MYSQL_SYSVAR_BOOL(status_output_locks, srv_print_innodb_lock_monitor,
19913 PLUGIN_VAR_OPCMDARG, "Enable InnoDB lock monitor output to the error log."
19914 " Requires innodb_status_output=ON.",
19915 NULL, innodb_status_output_update, FALSE);
19916
19917static MYSQL_SYSVAR_BOOL(print_all_deadlocks, srv_print_all_deadlocks,
19918 PLUGIN_VAR_OPCMDARG,
19919 "Print all deadlocks to MariaDB error log (off by default)",
19920 NULL, NULL, FALSE);
19921
19922static MYSQL_SYSVAR_ULONG(compression_failure_threshold_pct,
19923 zip_failure_threshold_pct, PLUGIN_VAR_OPCMDARG,
19924 "If the compression failure rate of a table is greater than this number"
19925 " more padding is added to the pages to reduce the failures. A value of"
19926 " zero implies no padding",
19927 NULL, NULL, 5, 0, 100, 0);
19928
19929static MYSQL_SYSVAR_ULONG(compression_pad_pct_max,
19930 zip_pad_max, PLUGIN_VAR_OPCMDARG,
19931 "Percentage of empty space on a data page that can be reserved"
19932 " to make the page compressible.",
19933 NULL, NULL, 50, 0, 75, 0);
19934
19935static MYSQL_SYSVAR_BOOL(read_only, srv_read_only_mode,
19936 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
19937 "Start InnoDB in read only mode (off by default)",
19938 NULL, NULL, FALSE);
19939
19940static MYSQL_SYSVAR_BOOL(cmp_per_index_enabled, srv_cmp_per_index_enabled,
19941 PLUGIN_VAR_OPCMDARG,
19942 "Enable INFORMATION_SCHEMA.innodb_cmp_per_index,"
19943 " may have negative impact on performance (off by default)",
19944 NULL, innodb_cmp_per_index_update, FALSE);
19945
19946static MYSQL_SYSVAR_ENUM(default_row_format, innodb_default_row_format,
19947 PLUGIN_VAR_RQCMDARG,
19948 "The default ROW FORMAT for all innodb tables created without explicit"
19949 " ROW_FORMAT. Possible values are REDUNDANT, COMPACT, and DYNAMIC."
19950 " The ROW_FORMAT value COMPRESSED is not allowed",
19951 NULL, NULL, DEFAULT_ROW_FORMAT_DYNAMIC,
19952 &innodb_default_row_format_typelib);
19953
19954#ifdef UNIV_DEBUG
19955static MYSQL_SYSVAR_UINT(trx_rseg_n_slots_debug, trx_rseg_n_slots_debug,
19956 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_NOCMDOPT,
19957 "Debug flags for InnoDB to limit TRX_RSEG_N_SLOTS for trx_rsegf_undo_find_free()",
19958 NULL, NULL, 0, 0, 1024, 0);
19959
19960static MYSQL_SYSVAR_UINT(limit_optimistic_insert_debug,
19961 btr_cur_limit_optimistic_insert_debug, PLUGIN_VAR_RQCMDARG,
19962 "Artificially limit the number of records per B-tree page (0=unlimited).",
19963 NULL, NULL, 0, 0, UINT_MAX32, 0);
19964
19965static MYSQL_SYSVAR_BOOL(trx_purge_view_update_only_debug,
19966 srv_purge_view_update_only_debug, PLUGIN_VAR_NOCMDOPT,
19967 "Pause actual purging any delete-marked records, but merely update the purge view."
19968 " It is to create artificially the situation the purge view have been updated"
19969 " but the each purges were not done yet.",
19970 NULL, NULL, FALSE);
19971
19972static MYSQL_SYSVAR_UINT(data_file_size_debug,
19973 srv_sys_space_size_debug,
19974 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19975 "InnoDB system tablespace size to be set in recovery.",
19976 NULL, NULL, 0, 0, UINT_MAX32, 0);
19977
19978static MYSQL_SYSVAR_ULONG(fil_make_page_dirty_debug,
19979 srv_fil_make_page_dirty_debug, PLUGIN_VAR_OPCMDARG,
19980 "Make the first page of the given tablespace dirty.",
19981 NULL, innodb_make_page_dirty, 0, 0, UINT_MAX32, 0);
19982
19983static MYSQL_SYSVAR_ULONG(saved_page_number_debug,
19984 srv_saved_page_number_debug, PLUGIN_VAR_OPCMDARG,
19985 "An InnoDB page number.",
19986 NULL, innodb_save_page_no, 0, 0, UINT_MAX32, 0);
19987
19988static MYSQL_SYSVAR_BOOL(disable_resize_buffer_pool_debug,
19989 buf_disable_resize_buffer_pool_debug, PLUGIN_VAR_NOCMDARG,
19990 "Disable resizing buffer pool to make assertion code not expensive.",
19991 NULL, NULL, TRUE);
19992
19993static MYSQL_SYSVAR_BOOL(page_cleaner_disabled_debug,
19994 innodb_page_cleaner_disabled_debug,
19995 PLUGIN_VAR_OPCMDARG,
19996 "Disable page cleaner",
19997 NULL, buf_flush_page_cleaner_disabled_debug_update, FALSE);
19998
19999static MYSQL_SYSVAR_BOOL(sync_debug, srv_sync_debug,
20000 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
20001 "Enable the sync debug checks",
20002 NULL, NULL, FALSE);
20003
20004static MYSQL_SYSVAR_BOOL(dict_stats_disabled_debug,
20005 innodb_dict_stats_disabled_debug,
20006 PLUGIN_VAR_OPCMDARG,
20007 "Disable dict_stats thread",
20008 NULL, dict_stats_disabled_debug_update, FALSE);
20009
20010static MYSQL_SYSVAR_BOOL(master_thread_disabled_debug,
20011 srv_master_thread_disabled_debug,
20012 PLUGIN_VAR_OPCMDARG,
20013 "Disable master thread",
20014 NULL, srv_master_thread_disabled_debug_update, FALSE);
20015
20016static MYSQL_SYSVAR_UINT(simulate_comp_failures, srv_simulate_comp_failures,
20017 PLUGIN_VAR_NOCMDARG,
20018 "Simulate compression failures.",
20019 NULL, NULL, 0, 0, 99, 0);
20020#endif /* UNIV_DEBUG */
20021
20022static MYSQL_SYSVAR_BOOL(force_primary_key,
20023 srv_force_primary_key,
20024 PLUGIN_VAR_OPCMDARG,
20025 "Do not allow to create table without primary key (off by default)",
20026 NULL, NULL, FALSE);
20027
20028static const char *page_compression_algorithms[]= { "none", "zlib", "lz4", "lzo", "lzma", "bzip2", "snappy", 0 };
20029static TYPELIB page_compression_algorithms_typelib=
20030{
20031 array_elements(page_compression_algorithms) - 1, 0,
20032 page_compression_algorithms, 0
20033};
20034static MYSQL_SYSVAR_ENUM(compression_algorithm, innodb_compression_algorithm,
20035 PLUGIN_VAR_OPCMDARG,
20036 "Compression algorithm used on page compression. One of: none, zlib, lz4, lzo, lzma, or bzip2",
20037 innodb_compression_algorithm_validate, NULL,
20038 /* We use here the largest number of supported compression method to
20039 enable all those methods that are available. Availability of compression
20040 method is verified on innodb_compression_algorithm_validate function. */
20041 PAGE_ZLIB_ALGORITHM,
20042 &page_compression_algorithms_typelib);
20043
20044static MYSQL_SYSVAR_ULONG(fatal_semaphore_wait_threshold, srv_fatal_semaphore_wait_threshold,
20045 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
20046 "Maximum number of seconds that semaphore times out in InnoDB.",
20047 NULL, NULL,
20048 DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT, /* Default setting */
20049 1, /* Minimum setting */
20050 UINT_MAX32, /* Maximum setting */
20051 0);
20052
20053static const char* srv_encrypt_tables_names[] = { "OFF", "ON", "FORCE", 0 };
20054static TYPELIB srv_encrypt_tables_typelib = {
20055 array_elements(srv_encrypt_tables_names)-1, 0, srv_encrypt_tables_names,
20056 NULL
20057};
20058static MYSQL_SYSVAR_ENUM(encrypt_tables, srv_encrypt_tables,
20059 PLUGIN_VAR_OPCMDARG,
20060 "Enable encryption for tables. "
20061 "Don't forget to enable --innodb-encrypt-log too",
20062 innodb_encrypt_tables_validate,
20063 innodb_encrypt_tables_update,
20064 0,
20065 &srv_encrypt_tables_typelib);
20066
20067static MYSQL_SYSVAR_UINT(encryption_threads, srv_n_fil_crypt_threads,
20068 PLUGIN_VAR_RQCMDARG,
20069 "Number of threads performing background key rotation and "
20070 "scrubbing",
20071 NULL,
20072 innodb_encryption_threads_update,
20073 srv_n_fil_crypt_threads, 0, UINT_MAX32, 0);
20074
20075static MYSQL_SYSVAR_UINT(encryption_rotate_key_age,
20076 srv_fil_crypt_rotate_key_age,
20077 PLUGIN_VAR_RQCMDARG,
20078 "Key rotation - re-encrypt in background "
20079 "all pages that were encrypted with a key that "
20080 "many (or more) versions behind. Value 0 indicates "
20081 "that key rotation is disabled.",
20082 NULL,
20083 innodb_encryption_rotate_key_age_update,
20084 1, 0, UINT_MAX32, 0);
20085
20086static MYSQL_SYSVAR_UINT(encryption_rotation_iops, srv_n_fil_crypt_iops,
20087 PLUGIN_VAR_RQCMDARG,
20088 "Use this many iops for background key rotation",
20089 NULL,
20090 innodb_encryption_rotation_iops_update,
20091 srv_n_fil_crypt_iops, 0, UINT_MAX32, 0);
20092
20093static MYSQL_SYSVAR_BOOL(scrub_log, srv_scrub_log,
20094 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
20095 "Enable background redo log (ib_logfile0, ib_logfile1...) scrubbing",
20096 0, 0, 0);
20097
20098static MYSQL_SYSVAR_ULONGLONG(scrub_log_speed, innodb_scrub_log_speed,
20099 PLUGIN_VAR_OPCMDARG,
20100 "Background redo log scrubbing speed in bytes/sec",
20101 NULL, NULL,
20102 256, /* 256 bytes/sec, corresponds to 2000 ms scrub_log_interval */
20103 1, /* min */
20104 50000, 0); /* 50Kbyte/sec, corresponds to 10 ms scrub_log_interval */
20105
20106static MYSQL_SYSVAR_BOOL(encrypt_log, srv_encrypt_log,
20107 PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
20108 "Enable redo log encryption",
20109 NULL, NULL, FALSE);
20110
20111static MYSQL_SYSVAR_BOOL(immediate_scrub_data_uncompressed,
20112 srv_immediate_scrub_data_uncompressed,
20113 0,
20114 "Enable scrubbing of data",
20115 NULL, NULL, FALSE);
20116
20117static MYSQL_SYSVAR_BOOL(background_scrub_data_uncompressed,
20118 srv_background_scrub_data_uncompressed,
20119 0,
20120 "Enable scrubbing of uncompressed data by "
20121 "background threads (same as encryption_threads)",
20122 NULL, NULL, FALSE);
20123
20124static MYSQL_SYSVAR_BOOL(background_scrub_data_compressed,
20125 srv_background_scrub_data_compressed,
20126 0,
20127 "Enable scrubbing of compressed data by "
20128 "background threads (same as encryption_threads)",
20129 NULL, NULL, FALSE);
20130
20131static MYSQL_SYSVAR_UINT(background_scrub_data_check_interval,
20132 srv_background_scrub_data_check_interval,
20133 0,
20134 "check if spaces needs scrubbing every "
20135 "innodb_background_scrub_data_check_interval "
20136 "seconds",
20137 NULL, NULL,
20138 srv_background_scrub_data_check_interval,
20139 1,
20140 UINT_MAX32, 0);
20141
20142static MYSQL_SYSVAR_UINT(background_scrub_data_interval,
20143 srv_background_scrub_data_interval,
20144 0,
20145 "scrub spaces that were last scrubbed longer than "
20146 " innodb_background_scrub_data_interval seconds ago",
20147 NULL, NULL,
20148 srv_background_scrub_data_interval,
20149 1,
20150 UINT_MAX32, 0);
20151
20152#ifdef UNIV_DEBUG
20153static MYSQL_SYSVAR_BOOL(debug_force_scrubbing,
20154 srv_scrub_force_testing,
20155 0,
20156 "Perform extra scrubbing to increase test exposure",
20157 NULL, NULL, FALSE);
20158#endif /* UNIV_DEBUG */
20159
20160static struct st_mysql_sys_var* innobase_system_variables[]= {
20161 MYSQL_SYSVAR(autoextend_increment),
20162 MYSQL_SYSVAR(buffer_pool_size),
20163 MYSQL_SYSVAR(buffer_pool_chunk_size),
20164 MYSQL_SYSVAR(buffer_pool_instances),
20165 MYSQL_SYSVAR(buffer_pool_filename),
20166 MYSQL_SYSVAR(buffer_pool_dump_now),
20167 MYSQL_SYSVAR(buffer_pool_dump_at_shutdown),
20168 MYSQL_SYSVAR(buffer_pool_dump_pct),
20169#ifdef UNIV_DEBUG
20170 MYSQL_SYSVAR(buffer_pool_evict),
20171#endif /* UNIV_DEBUG */
20172 MYSQL_SYSVAR(buffer_pool_load_now),
20173 MYSQL_SYSVAR(buffer_pool_load_abort),
20174#ifdef UNIV_DEBUG
20175 MYSQL_SYSVAR(buffer_pool_load_pages_abort),
20176#endif /* UNIV_DEBUG */
20177 MYSQL_SYSVAR(buffer_pool_load_at_startup),
20178 MYSQL_SYSVAR(defragment),
20179 MYSQL_SYSVAR(defragment_n_pages),
20180 MYSQL_SYSVAR(defragment_stats_accuracy),
20181 MYSQL_SYSVAR(defragment_fill_factor),
20182 MYSQL_SYSVAR(defragment_fill_factor_n_recs),
20183 MYSQL_SYSVAR(defragment_frequency),
20184 MYSQL_SYSVAR(lru_scan_depth),
20185 MYSQL_SYSVAR(flush_neighbors),
20186 MYSQL_SYSVAR(checksum_algorithm),
20187 MYSQL_SYSVAR(log_checksums),
20188 MYSQL_SYSVAR(checksums),
20189 MYSQL_SYSVAR(commit_concurrency),
20190 MYSQL_SYSVAR(concurrency_tickets),
20191 MYSQL_SYSVAR(compression_level),
20192 MYSQL_SYSVAR(data_file_path),
20193 MYSQL_SYSVAR(temp_data_file_path),
20194 MYSQL_SYSVAR(data_home_dir),
20195 MYSQL_SYSVAR(doublewrite),
20196 MYSQL_SYSVAR(stats_include_delete_marked),
20197 MYSQL_SYSVAR(use_atomic_writes),
20198 MYSQL_SYSVAR(fast_shutdown),
20199 MYSQL_SYSVAR(read_io_threads),
20200 MYSQL_SYSVAR(write_io_threads),
20201 MYSQL_SYSVAR(file_per_table),
20202 MYSQL_SYSVAR(flush_log_at_timeout),
20203 MYSQL_SYSVAR(flush_log_at_trx_commit),
20204 MYSQL_SYSVAR(flush_method),
20205 MYSQL_SYSVAR(force_recovery),
20206 MYSQL_SYSVAR(fill_factor),
20207 MYSQL_SYSVAR(ft_cache_size),
20208 MYSQL_SYSVAR(ft_total_cache_size),
20209 MYSQL_SYSVAR(ft_result_cache_limit),
20210 MYSQL_SYSVAR(ft_enable_stopword),
20211 MYSQL_SYSVAR(ft_max_token_size),
20212 MYSQL_SYSVAR(ft_min_token_size),
20213 MYSQL_SYSVAR(ft_num_word_optimize),
20214 MYSQL_SYSVAR(ft_sort_pll_degree),
20215 MYSQL_SYSVAR(force_load_corrupted),
20216 MYSQL_SYSVAR(lock_schedule_algorithm),
20217 MYSQL_SYSVAR(locks_unsafe_for_binlog),
20218 MYSQL_SYSVAR(lock_wait_timeout),
20219 MYSQL_SYSVAR(deadlock_detect),
20220 MYSQL_SYSVAR(page_size),
20221 MYSQL_SYSVAR(log_buffer_size),
20222 MYSQL_SYSVAR(log_file_size),
20223 MYSQL_SYSVAR(log_files_in_group),
20224 MYSQL_SYSVAR(log_write_ahead_size),
20225 MYSQL_SYSVAR(log_group_home_dir),
20226 MYSQL_SYSVAR(log_compressed_pages),
20227 MYSQL_SYSVAR(max_dirty_pages_pct),
20228 MYSQL_SYSVAR(max_dirty_pages_pct_lwm),
20229 MYSQL_SYSVAR(adaptive_flushing_lwm),
20230 MYSQL_SYSVAR(adaptive_flushing),
20231 MYSQL_SYSVAR(flush_sync),
20232 MYSQL_SYSVAR(flushing_avg_loops),
20233 MYSQL_SYSVAR(max_purge_lag),
20234 MYSQL_SYSVAR(max_purge_lag_delay),
20235 MYSQL_SYSVAR(old_blocks_pct),
20236 MYSQL_SYSVAR(old_blocks_time),
20237 MYSQL_SYSVAR(open_files),
20238 MYSQL_SYSVAR(optimize_fulltext_only),
20239 MYSQL_SYSVAR(rollback_on_timeout),
20240 MYSQL_SYSVAR(ft_aux_table),
20241 MYSQL_SYSVAR(ft_enable_diag_print),
20242 MYSQL_SYSVAR(ft_server_stopword_table),
20243 MYSQL_SYSVAR(ft_user_stopword_table),
20244 MYSQL_SYSVAR(disable_sort_file_cache),
20245 MYSQL_SYSVAR(stats_on_metadata),
20246 MYSQL_SYSVAR(stats_sample_pages),
20247 MYSQL_SYSVAR(stats_transient_sample_pages),
20248 MYSQL_SYSVAR(stats_persistent),
20249 MYSQL_SYSVAR(stats_persistent_sample_pages),
20250 MYSQL_SYSVAR(stats_auto_recalc),
20251 MYSQL_SYSVAR(stats_modified_counter),
20252 MYSQL_SYSVAR(stats_traditional),
20253#ifdef BTR_CUR_HASH_ADAPT
20254 MYSQL_SYSVAR(adaptive_hash_index),
20255 MYSQL_SYSVAR(adaptive_hash_index_parts),
20256#endif /* BTR_CUR_HASH_ADAPT */
20257 MYSQL_SYSVAR(stats_method),
20258 MYSQL_SYSVAR(replication_delay),
20259 MYSQL_SYSVAR(status_file),
20260 MYSQL_SYSVAR(strict_mode),
20261 MYSQL_SYSVAR(sort_buffer_size),
20262 MYSQL_SYSVAR(online_alter_log_max_size),
20263 MYSQL_SYSVAR(sync_spin_loops),
20264 MYSQL_SYSVAR(spin_wait_delay),
20265 MYSQL_SYSVAR(table_locks),
20266 MYSQL_SYSVAR(thread_concurrency),
20267 MYSQL_SYSVAR(adaptive_max_sleep_delay),
20268 MYSQL_SYSVAR(prefix_index_cluster_optimization),
20269 MYSQL_SYSVAR(thread_sleep_delay),
20270 MYSQL_SYSVAR(tmpdir),
20271 MYSQL_SYSVAR(autoinc_lock_mode),
20272 MYSQL_SYSVAR(version),
20273 MYSQL_SYSVAR(use_native_aio),
20274#ifdef HAVE_LIBNUMA
20275 MYSQL_SYSVAR(numa_interleave),
20276#endif /* HAVE_LIBNUMA */
20277 MYSQL_SYSVAR(change_buffering),
20278 MYSQL_SYSVAR(change_buffer_max_size),
20279#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
20280 MYSQL_SYSVAR(change_buffering_debug),
20281 MYSQL_SYSVAR(disable_background_merge),
20282#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
20283#ifdef WITH_INNODB_DISALLOW_WRITES
20284 MYSQL_SYSVAR(disallow_writes),
20285#endif /* WITH_INNODB_DISALLOW_WRITES */
20286 MYSQL_SYSVAR(random_read_ahead),
20287 MYSQL_SYSVAR(read_ahead_threshold),
20288 MYSQL_SYSVAR(read_only),
20289 MYSQL_SYSVAR(io_capacity),
20290 MYSQL_SYSVAR(io_capacity_max),
20291 MYSQL_SYSVAR(page_cleaners),
20292 MYSQL_SYSVAR(idle_flush_pct),
20293 MYSQL_SYSVAR(monitor_enable),
20294 MYSQL_SYSVAR(monitor_disable),
20295 MYSQL_SYSVAR(monitor_reset),
20296 MYSQL_SYSVAR(monitor_reset_all),
20297 MYSQL_SYSVAR(purge_threads),
20298 MYSQL_SYSVAR(purge_batch_size),
20299#ifdef UNIV_DEBUG
20300 MYSQL_SYSVAR(background_drop_list_empty),
20301 MYSQL_SYSVAR(log_checkpoint_now),
20302 MYSQL_SYSVAR(buf_flush_list_now),
20303 MYSQL_SYSVAR(merge_threshold_set_all_debug),
20304#endif /* UNIV_DEBUG */
20305#if defined UNIV_DEBUG || defined UNIV_PERF_DEBUG
20306 MYSQL_SYSVAR(page_hash_locks),
20307 MYSQL_SYSVAR(doublewrite_batch_size),
20308#endif /* defined UNIV_DEBUG || defined UNIV_PERF_DEBUG */
20309 MYSQL_SYSVAR(status_output),
20310 MYSQL_SYSVAR(status_output_locks),
20311 MYSQL_SYSVAR(print_all_deadlocks),
20312 MYSQL_SYSVAR(cmp_per_index_enabled),
20313 MYSQL_SYSVAR(undo_logs),
20314 MYSQL_SYSVAR(max_undo_log_size),
20315 MYSQL_SYSVAR(purge_rseg_truncate_frequency),
20316 MYSQL_SYSVAR(undo_log_truncate),
20317 MYSQL_SYSVAR(rollback_segments),
20318 MYSQL_SYSVAR(undo_directory),
20319 MYSQL_SYSVAR(undo_tablespaces),
20320 MYSQL_SYSVAR(sync_array_size),
20321 MYSQL_SYSVAR(compression_failure_threshold_pct),
20322 MYSQL_SYSVAR(compression_pad_pct_max),
20323 MYSQL_SYSVAR(default_row_format),
20324#ifdef UNIV_DEBUG
20325 MYSQL_SYSVAR(simulate_comp_failures),
20326 MYSQL_SYSVAR(trx_rseg_n_slots_debug),
20327 MYSQL_SYSVAR(limit_optimistic_insert_debug),
20328 MYSQL_SYSVAR(trx_purge_view_update_only_debug),
20329 MYSQL_SYSVAR(data_file_size_debug),
20330 MYSQL_SYSVAR(fil_make_page_dirty_debug),
20331 MYSQL_SYSVAR(saved_page_number_debug),
20332 MYSQL_SYSVAR(disable_resize_buffer_pool_debug),
20333 MYSQL_SYSVAR(page_cleaner_disabled_debug),
20334 MYSQL_SYSVAR(dict_stats_disabled_debug),
20335 MYSQL_SYSVAR(master_thread_disabled_debug),
20336 MYSQL_SYSVAR(sync_debug),
20337#endif /* UNIV_DEBUG */
20338 MYSQL_SYSVAR(force_primary_key),
20339 MYSQL_SYSVAR(fatal_semaphore_wait_threshold),
20340 /* Table page compression feature */
20341 MYSQL_SYSVAR(compression_default),
20342 MYSQL_SYSVAR(compression_algorithm),
20343 /* Encryption feature */
20344 MYSQL_SYSVAR(encrypt_tables),
20345 MYSQL_SYSVAR(encryption_threads),
20346 MYSQL_SYSVAR(encryption_rotate_key_age),
20347 MYSQL_SYSVAR(encryption_rotation_iops),
20348 MYSQL_SYSVAR(scrub_log),
20349 MYSQL_SYSVAR(scrub_log_speed),
20350 MYSQL_SYSVAR(encrypt_log),
20351 MYSQL_SYSVAR(default_encryption_key_id),
20352 /* Scrubing feature */
20353 MYSQL_SYSVAR(immediate_scrub_data_uncompressed),
20354 MYSQL_SYSVAR(background_scrub_data_uncompressed),
20355 MYSQL_SYSVAR(background_scrub_data_compressed),
20356 MYSQL_SYSVAR(background_scrub_data_interval),
20357 MYSQL_SYSVAR(background_scrub_data_check_interval),
20358#ifdef UNIV_DEBUG
20359 MYSQL_SYSVAR(debug_force_scrubbing),
20360#endif
20361 MYSQL_SYSVAR(buf_dump_status_frequency),
20362 MYSQL_SYSVAR(background_thread),
20363
20364 NULL
20365};
20366
20367maria_declare_plugin(innobase)
20368{
20369 MYSQL_STORAGE_ENGINE_PLUGIN,
20370 &innobase_storage_engine,
20371 innobase_hton_name,
20372 plugin_author,
20373 "Supports transactions, row-level locking, foreign keys and encryption for tables",
20374 PLUGIN_LICENSE_GPL,
20375 innodb_init, /* Plugin Init */
20376 NULL, /* Plugin Deinit */
20377 INNODB_VERSION_SHORT,
20378 innodb_status_variables_export,/* status variables */
20379 innobase_system_variables, /* system variables */
20380 INNODB_VERSION_STR, /* string version */
20381 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
20382},
20383i_s_innodb_trx,
20384i_s_innodb_locks,
20385i_s_innodb_lock_waits,
20386i_s_innodb_cmp,
20387i_s_innodb_cmp_reset,
20388i_s_innodb_cmpmem,
20389i_s_innodb_cmpmem_reset,
20390i_s_innodb_cmp_per_index,
20391i_s_innodb_cmp_per_index_reset,
20392i_s_innodb_buffer_page,
20393i_s_innodb_buffer_page_lru,
20394i_s_innodb_buffer_stats,
20395i_s_innodb_metrics,
20396i_s_innodb_ft_default_stopword,
20397i_s_innodb_ft_deleted,
20398i_s_innodb_ft_being_deleted,
20399i_s_innodb_ft_config,
20400i_s_innodb_ft_index_cache,
20401i_s_innodb_ft_index_table,
20402i_s_innodb_sys_tables,
20403i_s_innodb_sys_tablestats,
20404i_s_innodb_sys_indexes,
20405i_s_innodb_sys_columns,
20406i_s_innodb_sys_fields,
20407i_s_innodb_sys_foreign,
20408i_s_innodb_sys_foreign_cols,
20409i_s_innodb_sys_tablespaces,
20410i_s_innodb_sys_datafiles,
20411i_s_innodb_sys_virtual,
20412i_s_innodb_mutexes,
20413i_s_innodb_sys_semaphore_waits,
20414i_s_innodb_tablespaces_encryption,
20415i_s_innodb_tablespaces_scrubbing
20416maria_declare_plugin_end;
20417
20418/** @brief Initialize the default value of innodb_commit_concurrency.
20419
20420Once InnoDB is running, the innodb_commit_concurrency must not change
20421from zero to nonzero. (Bug #42101)
20422
20423The initial default value is 0, and without this extra initialization,
20424SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter
20425to 0, even if it was initially set to nonzero at the command line
20426or configuration file. */
20427static
20428void
20429innobase_commit_concurrency_init_default()
20430/*======================================*/
20431{
20432 MYSQL_SYSVAR_NAME(commit_concurrency).def_val
20433 = innobase_commit_concurrency;
20434}
20435
20436/** @brief Adjust some InnoDB startup parameters based on file contents
20437or innodb_page_size. */
20438static
20439void
20440innodb_params_adjust()
20441{
20442 /* The default value and the max value of
20443 innodb_undo_logs must be equal to the available undo logs. */
20444 MYSQL_SYSVAR_NAME(undo_logs).max_val
20445 = MYSQL_SYSVAR_NAME(undo_logs).def_val
20446 = srv_available_undo_logs;
20447 MYSQL_SYSVAR_NAME(max_undo_log_size).max_val
20448 = 1ULL << (32U + srv_page_size_shift);
20449 MYSQL_SYSVAR_NAME(max_undo_log_size).min_val
20450 = MYSQL_SYSVAR_NAME(max_undo_log_size).def_val
20451 = ulonglong(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)
20452 << srv_page_size_shift;
20453 MYSQL_SYSVAR_NAME(max_undo_log_size).max_val
20454 = 1ULL << (32U + srv_page_size_shift);
20455}
20456
20457/****************************************************************************
20458 * DS-MRR implementation
20459 ***************************************************************************/
20460
20461/**
20462Multi Range Read interface, DS-MRR calls */
20463int
20464ha_innobase::multi_range_read_init(
20465 RANGE_SEQ_IF* seq,
20466 void* seq_init_param,
20467 uint n_ranges,
20468 uint mode,
20469 HANDLER_BUFFER* buf)
20470{
20471 return(m_ds_mrr.dsmrr_init(this, seq, seq_init_param,
20472 n_ranges, mode, buf));
20473}
20474
20475int
20476ha_innobase::multi_range_read_next(
20477 range_id_t* range_info)
20478{
20479 return(m_ds_mrr.dsmrr_next(range_info));
20480}
20481
20482ha_rows
20483ha_innobase::multi_range_read_info_const(
20484 uint keyno,
20485 RANGE_SEQ_IF* seq,
20486 void* seq_init_param,
20487 uint n_ranges,
20488 uint* bufsz,
20489 uint* flags,
20490 Cost_estimate* cost)
20491{
20492 /* See comments in ha_myisam::multi_range_read_info_const */
20493 m_ds_mrr.init(this, table);
20494
20495 if (m_prebuilt->select_lock_type != LOCK_NONE) {
20496 *flags |= HA_MRR_USE_DEFAULT_IMPL;
20497 }
20498
20499 ha_rows res= m_ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
20500 bufsz, flags, cost);
20501 return res;
20502}
20503
20504ha_rows
20505ha_innobase::multi_range_read_info(
20506 uint keyno,
20507 uint n_ranges,
20508 uint keys,
20509 uint key_parts,
20510 uint* bufsz,
20511 uint* flags,
20512 Cost_estimate* cost)
20513{
20514 m_ds_mrr.init(this, table);
20515 ha_rows res= m_ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz,
20516 flags, cost);
20517 return res;
20518}
20519
20520int
20521ha_innobase::multi_range_read_explain_info(
20522 uint mrr_mode,
20523 char *str,
20524 size_t size)
20525{
20526 return m_ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
20527}
20528
20529/**
20530Index Condition Pushdown interface implementation */
20531
20532/*************************************************************//**
20533InnoDB index push-down condition check
20534@return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */
20535ICP_RESULT
20536innobase_index_cond(
20537/*================*/
20538 void* file) /*!< in/out: pointer to ha_innobase */
20539{
20540 return handler_index_cond_check(file);
20541}
20542
20543
20544/** Find or open a mysql table for the virtual column template
20545@param[in] thd mysql thread handle
20546@param[in,out] table InnoDB table whose virtual column template is to be updated
20547@return TABLE if successful or NULL */
20548static TABLE *
20549innobase_find_mysql_table_for_vc(
20550/*=============================*/
20551 THD* thd,
20552 dict_table_t* table)
20553{
20554 TABLE *mysql_table;
20555 bool bg_thread = THDVAR(thd, background_thread);
20556
20557 if (bg_thread) {
20558 if ((mysql_table = get_purge_table(thd))) {
20559 return mysql_table;
20560 }
20561 } else {
20562 if (table->vc_templ->mysql_table_query_id == thd_get_query_id(thd)) {
20563 return table->vc_templ->mysql_table;
20564 }
20565 }
20566
20567 char dbname[MAX_DATABASE_NAME_LEN + 1];
20568 char tbname[MAX_TABLE_NAME_LEN + 1];
20569 char* name = table->name.m_name;
20570 uint dbnamelen = (uint) dict_get_db_name_len(name);
20571 uint tbnamelen = (uint) strlen(name) - dbnamelen - 1;
20572 char t_dbname[MAX_DATABASE_NAME_LEN + 1];
20573 char t_tbname[MAX_TABLE_NAME_LEN + 1];
20574
20575 strncpy(dbname, name, dbnamelen);
20576 dbname[dbnamelen] = 0;
20577 strncpy(tbname, name + dbnamelen + 1, tbnamelen);
20578 tbname[tbnamelen] =0;
20579
20580 /* For partition table, remove the partition name and use the
20581 "main" table name to build the template */
20582 char* is_part = is_partition(tbname);
20583
20584 if (is_part != NULL) {
20585 *is_part = '\0';
20586 }
20587
20588 dbnamelen = filename_to_tablename(dbname, t_dbname,
20589 MAX_DATABASE_NAME_LEN + 1);
20590 tbnamelen = filename_to_tablename(tbname, t_tbname,
20591 MAX_TABLE_NAME_LEN + 1);
20592
20593 if (bg_thread) {
20594 return open_purge_table(thd, t_dbname, dbnamelen,
20595 t_tbname, tbnamelen);
20596 }
20597
20598 mysql_table = find_fk_open_table(thd, t_dbname, dbnamelen,
20599 t_tbname, tbnamelen);
20600
20601 table->vc_templ->mysql_table = mysql_table;
20602 table->vc_templ->mysql_table_query_id = thd_get_query_id(thd);
20603 return mysql_table;
20604}
20605
20606/** Get the computed value by supplying the base column values.
20607@param[in,out] table table whose virtual column template to be built */
20608void
20609innobase_init_vc_templ(
20610 dict_table_t* table)
20611{
20612 if (table->vc_templ != NULL) {
20613 return;
20614 }
20615
20616 table->vc_templ = UT_NEW_NOKEY(dict_vcol_templ_t());
20617
20618 TABLE *mysql_table= innobase_find_mysql_table_for_vc(current_thd, table);
20619
20620 ut_ad(mysql_table);
20621 if (!mysql_table) {
20622 return;
20623 }
20624
20625 mutex_enter(&dict_sys->mutex);
20626 innobase_build_v_templ(mysql_table, table, table->vc_templ, NULL, true);
20627 mutex_exit(&dict_sys->mutex);
20628}
20629
20630/** Change dbname and table name in table->vc_templ.
20631@param[in,out] table the table whose virtual column template
20632dbname and tbname to be renamed. */
20633void
20634innobase_rename_vc_templ(
20635 dict_table_t* table)
20636{
20637 char dbname[MAX_DATABASE_NAME_LEN + 1];
20638 char tbname[MAX_DATABASE_NAME_LEN + 1];
20639 char* name = table->name.m_name;
20640 ulint dbnamelen = dict_get_db_name_len(name);
20641 ulint tbnamelen = strlen(name) - dbnamelen - 1;
20642 char t_dbname[MAX_DATABASE_NAME_LEN + 1];
20643 char t_tbname[MAX_TABLE_NAME_LEN + 1];
20644
20645 strncpy(dbname, name, dbnamelen);
20646 dbname[dbnamelen] = 0;
20647 strncpy(tbname, name + dbnamelen + 1, tbnamelen);
20648 tbname[tbnamelen] =0;
20649
20650 /* For partition table, remove the partition name and use the
20651 "main" table name to build the template */
20652 char* is_part = is_partition(tbname);
20653
20654 if (is_part != NULL) {
20655 *is_part = '\0';
20656 tbnamelen = ulint(is_part - tbname);
20657 }
20658
20659 dbnamelen = filename_to_tablename(dbname, t_dbname,
20660 MAX_DATABASE_NAME_LEN + 1);
20661 tbnamelen = filename_to_tablename(tbname, t_tbname,
20662 MAX_TABLE_NAME_LEN + 1);
20663
20664 table->vc_templ->db_name = t_dbname;
20665 table->vc_templ->tb_name = t_tbname;
20666}
20667
20668/** Get the updated parent field value from the update vector for the
20669given col_no.
20670@param[in] foreign foreign key information
20671@param[in] update updated parent vector.
20672@param[in] col_no column position of the table
20673@return updated field from the parent update vector, else NULL */
20674static
20675dfield_t*
20676innobase_get_field_from_update_vector(
20677 dict_foreign_t* foreign,
20678 upd_t* update,
20679 ulint col_no)
20680{
20681 dict_table_t* parent_table = foreign->referenced_table;
20682 dict_index_t* parent_index = foreign->referenced_index;
20683 ulint parent_field_no;
20684 ulint parent_col_no;
20685 ulint prefix_col_no;
20686
20687 for (ulint i = 0; i < foreign->n_fields; i++) {
20688
20689 parent_col_no = dict_index_get_nth_col_no(parent_index, i);
20690 parent_field_no = dict_table_get_nth_col_pos(
20691 parent_table, parent_col_no, &prefix_col_no);
20692
20693 for (ulint j = 0; j < update->n_fields; j++) {
20694 upd_field_t* parent_ufield
20695 = &update->fields[j];
20696
20697 if (parent_ufield->field_no == parent_field_no
20698 && parent_col_no == col_no) {
20699 return(&parent_ufield->new_val);
20700 }
20701 }
20702 }
20703
20704 return (NULL);
20705}
20706
20707/** Get the computed value by supplying the base column values.
20708@param[in,out] row the data row
20709@param[in] col virtual column
20710@param[in] index index
20711@param[in,out] local_heap heap memory for processing large data etc.
20712@param[in,out] heap memory heap that copies the actual index row
20713@param[in] ifield index field
20714@param[in] thd MySQL thread handle
20715@param[in,out] mysql_table mysql table object
20716@param[in] old_table during ALTER TABLE, this is the old table
20717 or NULL.
20718@param[in] parent_update update vector for the parent row
20719@param[in] foreign foreign key information
20720@return the field filled with computed value, or NULL if just want
20721to store the value in passed in "my_rec" */
20722dfield_t*
20723innobase_get_computed_value(
20724 const dtuple_t* row,
20725 const dict_v_col_t* col,
20726 const dict_index_t* index,
20727 mem_heap_t** local_heap,
20728 mem_heap_t* heap,
20729 const dict_field_t* ifield,
20730 THD* thd,
20731 TABLE* mysql_table,
20732 const dict_table_t* old_table,
20733 upd_t* parent_update,
20734 dict_foreign_t* foreign)
20735{
20736 byte rec_buf2[REC_VERSION_56_MAX_INDEX_COL_LEN];
20737 byte* mysql_rec;
20738 byte* buf;
20739 dfield_t* field;
20740 ulint len;
20741
20742 const page_size_t page_size = (old_table == NULL)
20743 ? dict_table_page_size(index->table)
20744 : dict_table_page_size(old_table);
20745
20746 ulint ret = 0;
20747
20748 ut_ad(index->table->vc_templ);
20749 ut_ad(thd != NULL);
20750
20751 const mysql_row_templ_t*
20752 vctempl = index->table->vc_templ->vtempl[
20753 index->table->vc_templ->n_col + col->v_pos];
20754
20755 if (!heap || index->table->vc_templ->rec_len
20756 >= REC_VERSION_56_MAX_INDEX_COL_LEN) {
20757 if (*local_heap == NULL) {
20758 *local_heap = mem_heap_create(srv_page_size);
20759 }
20760
20761 buf = static_cast<byte*>(mem_heap_alloc(
20762 *local_heap, index->table->vc_templ->rec_len));
20763 } else {
20764 buf = rec_buf2;
20765 }
20766
20767 if (!mysql_table) {
20768 mysql_table = innobase_find_mysql_table_for_vc(thd, index->table);
20769 }
20770
20771 ut_ad(mysql_table);
20772
20773 mysql_rec = mysql_table->record[0];
20774
20775 for (ulint i = 0; i < col->num_base; i++) {
20776 dict_col_t* base_col = col->base_col[i];
20777 const dfield_t* row_field = NULL;
20778 ulint col_no = base_col->ind;
20779 const mysql_row_templ_t* templ
20780 = index->table->vc_templ->vtempl[col_no];
20781 const byte* data;
20782
20783 if (parent_update != NULL) {
20784 /** Get the updated field from update vector
20785 of the parent table. */
20786 row_field = innobase_get_field_from_update_vector(
20787 foreign, parent_update, col_no);
20788 }
20789
20790 if (row_field == NULL) {
20791 row_field = dtuple_get_nth_field(row, col_no);
20792 }
20793
20794 data = static_cast<const byte*>(row_field->data);
20795 len = row_field->len;
20796
20797 if (row_field->ext) {
20798 if (*local_heap == NULL) {
20799 *local_heap = mem_heap_create(srv_page_size);
20800 }
20801
20802 data = btr_copy_externally_stored_field(
20803 &len, data, page_size,
20804 dfield_get_len(row_field), *local_heap);
20805 }
20806
20807 if (len == UNIV_SQL_NULL) {
20808 mysql_rec[templ->mysql_null_byte_offset]
20809 |= (byte) templ->mysql_null_bit_mask;
20810 memcpy(mysql_rec + templ->mysql_col_offset,
20811 static_cast<const byte*>(
20812 index->table->vc_templ->default_rec
20813 + templ->mysql_col_offset),
20814 templ->mysql_col_len);
20815 } else {
20816
20817 row_sel_field_store_in_mysql_format(
20818 mysql_rec + templ->mysql_col_offset,
20819 templ, index, templ->clust_rec_field_no,
20820 (const byte*)data, len);
20821
20822 if (templ->mysql_null_bit_mask) {
20823 /* It is a nullable column with a
20824 non-NULL value */
20825 mysql_rec[templ->mysql_null_byte_offset]
20826 &= ~(byte) templ->mysql_null_bit_mask;
20827 }
20828 }
20829 }
20830
20831 field = dtuple_get_nth_v_field(row, col->v_pos);
20832
20833 my_bitmap_map* old_write_set = dbug_tmp_use_all_columns(mysql_table, mysql_table->write_set);
20834 my_bitmap_map* old_read_set = dbug_tmp_use_all_columns(mysql_table, mysql_table->read_set);
20835 ret = mysql_table->update_virtual_field(mysql_table->field[col->m_col.ind]);
20836 dbug_tmp_restore_column_map(mysql_table->read_set, old_read_set);
20837 dbug_tmp_restore_column_map(mysql_table->write_set, old_write_set);
20838
20839 if (ret != 0) {
20840#ifdef INNODB_VIRTUAL_DEBUG
20841 ib::warn() << "Compute virtual column values failed ";
20842 fputs("InnoDB: Cannot compute value for following record ",
20843 stderr);
20844 dtuple_print(stderr, row);
20845#endif /* INNODB_VIRTUAL_DEBUG */
20846 return(NULL);
20847 }
20848
20849 if (vctempl->mysql_null_bit_mask
20850 && (mysql_rec[vctempl->mysql_null_byte_offset]
20851 & vctempl->mysql_null_bit_mask)) {
20852 dfield_set_null(field);
20853 field->type.prtype |= DATA_VIRTUAL;
20854 return(field);
20855 }
20856
20857 row_mysql_store_col_in_innobase_format(
20858 field, buf,
20859 TRUE, mysql_rec + vctempl->mysql_col_offset,
20860 vctempl->mysql_col_len, dict_table_is_comp(index->table));
20861 field->type.prtype |= DATA_VIRTUAL;
20862
20863 ulint max_prefix = col->m_col.max_prefix;
20864
20865 if (max_prefix && ifield
20866 && (ifield->prefix_len == 0
20867 || ifield->prefix_len > col->m_col.max_prefix)) {
20868 max_prefix = ifield->prefix_len;
20869 }
20870
20871 /* If this is a prefix index, we only need a portion of the field */
20872 if (max_prefix) {
20873 len = dtype_get_at_most_n_mbchars(
20874 col->m_col.prtype,
20875 col->m_col.mbminlen, col->m_col.mbmaxlen,
20876 max_prefix,
20877 field->len,
20878 static_cast<char*>(dfield_get_data(field)));
20879 dfield_set_len(field, len);
20880 }
20881
20882 if (heap) {
20883 dfield_dup(field, heap);
20884 }
20885
20886 return(field);
20887}
20888
20889
20890/** Attempt to push down an index condition.
20891@param[in] keyno MySQL key number
20892@param[in] idx_cond Index condition to be checked
20893@return Part of idx_cond which the handler will not evaluate */
20894
20895class Item*
20896ha_innobase::idx_cond_push(
20897 uint keyno,
20898 class Item* idx_cond)
20899{
20900 DBUG_ENTER("ha_innobase::idx_cond_push");
20901 DBUG_ASSERT(keyno != MAX_KEY);
20902 DBUG_ASSERT(idx_cond != NULL);
20903
20904 /* We can only evaluate the condition if all columns are stored.*/
20905 dict_index_t* idx = innobase_get_index(keyno);
20906 if (idx && dict_index_has_virtual(idx)) {
20907 DBUG_RETURN(idx_cond);
20908 }
20909
20910 pushed_idx_cond = idx_cond;
20911 pushed_idx_cond_keyno = keyno;
20912 in_range_check_pushed_down = TRUE;
20913 /* We will evaluate the condition entirely */
20914 DBUG_RETURN(NULL);
20915}
20916
20917/******************************************************************//**
20918Use this when the args are passed to the format string from
20919errmsg-utf8.txt directly as is.
20920
20921Push a warning message to the client, it is a wrapper around:
20922
20923void push_warning_printf(
20924 THD *thd, Sql_condition::enum_condition_level level,
20925 uint code, const char *format, ...);
20926*/
20927void
20928ib_senderrf(
20929/*========*/
20930 THD* thd, /*!< in/out: session */
20931 ib_log_level_t level, /*!< in: warning level */
20932 ib_uint32_t code, /*!< MySQL error code */
20933 ...) /*!< Args */
20934{
20935 va_list args;
20936 char* str = NULL;
20937 const char* format = my_get_err_msg(code);
20938
20939 /* If the caller wants to push a message to the client then
20940 the caller must pass a valid session handle. */
20941
20942 ut_a(thd != 0);
20943
20944 /* The error code must exist in the errmsg-utf8.txt file. */
20945 ut_a(format != 0);
20946
20947 va_start(args, code);
20948
20949 myf l = Sql_condition::WARN_LEVEL_NOTE;
20950
20951 switch (level) {
20952 case IB_LOG_LEVEL_INFO:
20953 l = ME_JUST_INFO;
20954 break;
20955 case IB_LOG_LEVEL_WARN:
20956 l = ME_JUST_WARNING;
20957 break;
20958 case IB_LOG_LEVEL_ERROR:
20959 sd_notifyf(0, "STATUS=InnoDB: Error: %s", str);
20960 l = 0;
20961 break;
20962 case IB_LOG_LEVEL_FATAL:
20963 l = 0;
20964 sd_notifyf(0, "STATUS=InnoDB: Fatal: %s", str);
20965 break;
20966 default:
20967 l = 0;
20968 break;
20969 }
20970
20971 my_printv_error(code, format, MYF(l), args);
20972
20973 va_end(args);
20974 free(str);
20975
20976 if (level == IB_LOG_LEVEL_FATAL) {
20977 ut_error;
20978 }
20979}
20980
20981/******************************************************************//**
20982Use this when the args are first converted to a formatted string and then
20983passed to the format string from errmsg-utf8.txt. The error message format
20984must be: "Some string ... %s".
20985
20986Push a warning message to the client, it is a wrapper around:
20987
20988void push_warning_printf(
20989 THD *thd, Sql_condition::enum_condition_level level,
20990 uint code, const char *format, ...);
20991*/
20992void
20993ib_errf(
20994/*====*/
20995 THD* thd, /*!< in/out: session */
20996 ib_log_level_t level, /*!< in: warning level */
20997 ib_uint32_t code, /*!< MySQL error code */
20998 const char* format, /*!< printf format */
20999 ...) /*!< Args */
21000{
21001 char* str = NULL;
21002 va_list args;
21003
21004 /* If the caller wants to push a message to the client then
21005 the caller must pass a valid session handle. */
21006
21007 ut_a(thd != 0);
21008 ut_a(format != 0);
21009
21010 va_start(args, format);
21011
21012#ifdef _WIN32
21013 int size = _vscprintf(format, args) + 1;
21014 if (size > 0) {
21015 str = static_cast<char*>(malloc(size));
21016 }
21017 if (str == NULL) {
21018 va_end(args);
21019 return; /* Watch for Out-Of-Memory */
21020 }
21021 str[size - 1] = 0x0;
21022 vsnprintf(str, size, format, args);
21023#elif HAVE_VASPRINTF
21024 if (vasprintf(&str, format, args) == -1) {
21025 /* In case of failure use a fixed length string */
21026 str = static_cast<char*>(malloc(BUFSIZ));
21027 vsnprintf(str, BUFSIZ, format, args);
21028 }
21029#else
21030 /* Use a fixed length string. */
21031 str = static_cast<char*>(malloc(BUFSIZ));
21032 if (str == NULL) {
21033 va_end(args);
21034 return; /* Watch for Out-Of-Memory */
21035 }
21036 vsnprintf(str, BUFSIZ, format, args);
21037#endif /* _WIN32 */
21038
21039 ib_senderrf(thd, level, code, str);
21040
21041 va_end(args);
21042 free(str);
21043}
21044
21045/* Keep the first 16 characters as-is, since the url is sometimes used
21046as an offset from this.*/
21047const char* TROUBLESHOOTING_MSG =
21048 "Please refer to " REFMAN "innodb-troubleshooting.html"
21049 " for how to resolve the issue.";
21050
21051const char* TROUBLESHOOT_DATADICT_MSG =
21052 "Please refer to " REFMAN "innodb-troubleshooting-datadict.html"
21053 " for how to resolve the issue.";
21054
21055const char* BUG_REPORT_MSG =
21056 "Submit a detailed bug report to https://jira.mariadb.org/";
21057
21058const char* FORCE_RECOVERY_MSG =
21059 "Please refer to "
21060 "https://mariadb.com/kb/en/library/xtradbinnodb-recovery-modes/"
21061 " for information about forcing recovery.";
21062
21063const char* ERROR_CREATING_MSG =
21064 "Please refer to " REFMAN "error-creating-innodb.html";
21065
21066const char* OPERATING_SYSTEM_ERROR_MSG =
21067 "Some operating system error numbers are described at"
21068 " https://mariadb.com/kb/en/library/operating-system-error-codes/";
21069
21070const char* FOREIGN_KEY_CONSTRAINTS_MSG =
21071 "Please refer to https://mariadb.com/kb/en/library/foreign-keys/"
21072 " for correct foreign key definition.";
21073
21074const char* SET_TRANSACTION_MSG =
21075 "Please refer to https://mariadb.com/kb/en/library/set-transaction/";
21076
21077const char* INNODB_PARAMETERS_MSG =
21078 "Please refer to https://mariadb.com/kb/en/library/xtradbinnodb-server-system-variables/";
21079
21080/**********************************************************************
21081Converts an identifier from my_charset_filename to UTF-8 charset.
21082@return result string length, as returned by strconvert() */
21083uint
21084innobase_convert_to_filename_charset(
21085/*=================================*/
21086 char* to, /* out: converted identifier */
21087 const char* from, /* in: identifier to convert */
21088 ulint len) /* in: length of 'to', in bytes */
21089{
21090 uint errors;
21091 CHARSET_INFO* cs_to = &my_charset_filename;
21092 CHARSET_INFO* cs_from = system_charset_info;
21093
21094 return(static_cast<uint>(strconvert(
21095 cs_from, from, uint(strlen(from)),
21096 cs_to, to, static_cast<uint>(len), &errors)));
21097}
21098
21099/**********************************************************************
21100Converts an identifier from my_charset_filename to UTF-8 charset.
21101@return result string length, as returned by strconvert() */
21102uint
21103innobase_convert_to_system_charset(
21104/*===============================*/
21105 char* to, /* out: converted identifier */
21106 const char* from, /* in: identifier to convert */
21107 ulint len, /* in: length of 'to', in bytes */
21108 uint* errors) /* out: error return */
21109{
21110 CHARSET_INFO* cs1 = &my_charset_filename;
21111 CHARSET_INFO* cs2 = system_charset_info;
21112
21113 return(static_cast<uint>(strconvert(
21114 cs1, from, static_cast<uint>(strlen(from)),
21115 cs2, to, static_cast<uint>(len), errors)));
21116}
21117
21118/**********************************************************************
21119Issue a warning that the row is too big. */
21120void
21121ib_warn_row_too_big(const dict_table_t* table)
21122{
21123 /* If prefix is true then a 768-byte prefix is stored
21124 locally for BLOB fields. */
21125 const bool prefix = !dict_table_has_atomic_blobs(table);
21126
21127 const ulint free_space = page_get_free_space_of_empty(
21128 table->flags & DICT_TF_COMPACT) / 2;
21129
21130 THD* thd = current_thd;
21131
21132 push_warning_printf(
21133 thd, Sql_condition::WARN_LEVEL_WARN, HA_ERR_TO_BIG_ROW,
21134 "Row size too large (> " ULINTPF ")."
21135 " Changing some columns to TEXT"
21136 " or BLOB %smay help. In current row format, BLOB prefix of"
21137 " %d bytes is stored inline.", free_space
21138 , prefix ? "or using ROW_FORMAT=DYNAMIC or"
21139 " ROW_FORMAT=COMPRESSED ": ""
21140 , prefix ? DICT_MAX_FIXED_COL_LEN : 0);
21141}
21142
21143/** Validate the requested buffer pool size. Also, reserve the necessary
21144memory needed for buffer pool resize.
21145@param[in] thd thread handle
21146@param[out] save immediate result for update function
21147@param[in] value incoming string
21148@return 0 on success, 1 on failure.
21149*/
21150static
21151int
21152innodb_buffer_pool_size_validate(
21153 THD* thd,
21154 st_mysql_sys_var*,
21155 void* save,
21156 struct st_mysql_value* value)
21157{
21158 longlong intbuf;
21159 value->val_int(value, &intbuf);
21160
21161 if (!srv_was_started) {
21162 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21163 ER_WRONG_ARGUMENTS,
21164 "Cannot update innodb_buffer_pool_size,"
21165 " because InnoDB is not started.");
21166 return(1);
21167 }
21168
21169#ifdef UNIV_DEBUG
21170 if (buf_disable_resize_buffer_pool_debug == TRUE) {
21171 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21172 ER_WRONG_ARGUMENTS,
21173 "Cannot update innodb_buffer_pool_size,"
21174 " because innodb_disable_resize_buffer_pool_debug"
21175 " is set.");
21176 ib::warn() << "Cannot update innodb_buffer_pool_size,"
21177 " because innodb_disable_resize_buffer_pool_debug"
21178 " is set.";
21179 return(1);
21180 }
21181#endif /* UNIV_DEBUG */
21182
21183
21184 buf_pool_mutex_enter_all();
21185
21186 if (srv_buf_pool_old_size != srv_buf_pool_size) {
21187 buf_pool_mutex_exit_all();
21188 my_printf_error(ER_WRONG_ARGUMENTS,
21189 "Another buffer pool resize is already in progress.", MYF(0));
21190 return(1);
21191 }
21192
21193 if (srv_buf_pool_instances > 1 && intbuf < BUF_POOL_SIZE_THRESHOLD) {
21194 buf_pool_mutex_exit_all();
21195
21196 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21197 ER_WRONG_ARGUMENTS,
21198 "Cannot update innodb_buffer_pool_size"
21199 " to less than 1GB if"
21200 " innodb_buffer_pool_instances > 1.");
21201 return(1);
21202 }
21203
21204 ulint requested_buf_pool_size = buf_pool_size_align(ulint(intbuf));
21205
21206 *static_cast<ulonglong*>(save) = requested_buf_pool_size;
21207
21208 if (srv_buf_pool_size == ulint(intbuf)) {
21209 buf_pool_mutex_exit_all();
21210 /* nothing to do */
21211 return(0);
21212 }
21213
21214 if (srv_buf_pool_size == requested_buf_pool_size) {
21215 buf_pool_mutex_exit_all();
21216 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21217 ER_WRONG_ARGUMENTS,
21218 "innodb_buffer_pool_size must be at least"
21219 " innodb_buffer_pool_chunk_size=%lu",
21220 srv_buf_pool_chunk_unit);
21221 /* nothing to do */
21222 return(0);
21223 }
21224
21225 srv_buf_pool_size = requested_buf_pool_size;
21226 buf_pool_mutex_exit_all();
21227
21228 if (intbuf != static_cast<longlong>(requested_buf_pool_size)) {
21229 char buf[64];
21230 int len = 64;
21231 value->val_str(value, buf, &len);
21232 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21233 ER_TRUNCATED_WRONG_VALUE,
21234 "Truncated incorrect %-.32s value: '%-.128s'",
21235 mysql_sysvar_buffer_pool_size.name,
21236 value->val_str(value, buf, &len));
21237 }
21238
21239 return(0);
21240}
21241
21242/*************************************************************//**
21243Check for a valid value of innobase_compression_algorithm.
21244@return 0 for valid innodb_compression_algorithm. */
21245static
21246int
21247innodb_compression_algorithm_validate(
21248/*==================================*/
21249 THD* thd, /*!< in: thread handle */
21250 struct st_mysql_sys_var* var, /*!< in: pointer to system
21251 variable */
21252 void* save, /*!< out: immediate result
21253 for update function */
21254 struct st_mysql_value* value) /*!< in: incoming string */
21255{
21256 ulong compression_algorithm;
21257 DBUG_ENTER("innobase_compression_algorithm_validate");
21258
21259 if (check_sysvar_enum(thd, var, save, value)) {
21260 DBUG_RETURN(1);
21261 }
21262
21263 compression_algorithm = *reinterpret_cast<ulong*>(save);
21264 (void)compression_algorithm;
21265
21266#ifndef HAVE_LZ4
21267 if (compression_algorithm == PAGE_LZ4_ALGORITHM) {
21268 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21269 HA_ERR_UNSUPPORTED,
21270 "InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
21271 "InnoDB: liblz4 is not installed. \n",
21272 compression_algorithm);
21273 DBUG_RETURN(1);
21274 }
21275#endif
21276
21277#ifndef HAVE_LZO
21278 if (compression_algorithm == PAGE_LZO_ALGORITHM) {
21279 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21280 HA_ERR_UNSUPPORTED,
21281 "InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
21282 "InnoDB: liblzo is not installed. \n",
21283 compression_algorithm);
21284 DBUG_RETURN(1);
21285 }
21286#endif
21287
21288#ifndef HAVE_LZMA
21289 if (compression_algorithm == PAGE_LZMA_ALGORITHM) {
21290 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21291 HA_ERR_UNSUPPORTED,
21292 "InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
21293 "InnoDB: liblzma is not installed. \n",
21294 compression_algorithm);
21295 DBUG_RETURN(1);
21296 }
21297#endif
21298
21299#ifndef HAVE_BZIP2
21300 if (compression_algorithm == PAGE_BZIP2_ALGORITHM) {
21301 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21302 HA_ERR_UNSUPPORTED,
21303 "InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
21304 "InnoDB: libbz2 is not installed. \n",
21305 compression_algorithm);
21306 DBUG_RETURN(1);
21307 }
21308#endif
21309
21310#ifndef HAVE_SNAPPY
21311 if (compression_algorithm == PAGE_SNAPPY_ALGORITHM) {
21312 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21313 HA_ERR_UNSUPPORTED,
21314 "InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
21315 "InnoDB: libsnappy is not installed. \n",
21316 compression_algorithm);
21317 DBUG_RETURN(1);
21318 }
21319#endif
21320 DBUG_RETURN(0);
21321}
21322
21323static
21324int
21325innodb_encrypt_tables_validate(
21326/*=================================*/
21327 THD* thd, /*!< in: thread handle */
21328 struct st_mysql_sys_var* var, /*!< in: pointer to system
21329 variable */
21330 void* save, /*!< out: immediate result
21331 for update function */
21332 struct st_mysql_value* value) /*!< in: incoming string */
21333{
21334 if (check_sysvar_enum(thd, var, save, value)) {
21335 return 1;
21336 }
21337
21338 ulong encrypt_tables = *(ulong*)save;
21339
21340 if (encrypt_tables
21341 && !encryption_key_id_exists(FIL_DEFAULT_ENCRYPTION_KEY)) {
21342 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21343 HA_ERR_UNSUPPORTED,
21344 "InnoDB: cannot enable encryption, "
21345 "encryption plugin is not available");
21346 return 1;
21347 }
21348
21349 if (!srv_fil_crypt_rotate_key_age) {
21350 const char *msg = (encrypt_tables ? "enable" : "disable");
21351 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21352 HA_ERR_UNSUPPORTED,
21353 "InnoDB: cannot %s encryption, "
21354 "innodb_encryption_rotate_key_age=0"
21355 " i.e. key rotation disabled", msg);
21356 return 1;
21357 }
21358
21359 return 0;
21360}
21361
21362static void innodb_remember_check_sysvar_funcs()
21363{
21364 /* remember build-in sysvar check functions */
21365 ut_ad((MYSQL_SYSVAR_NAME(checksum_algorithm).flags & 0x1FF) == PLUGIN_VAR_ENUM);
21366 check_sysvar_enum = MYSQL_SYSVAR_NAME(checksum_algorithm).check;
21367
21368 ut_ad((MYSQL_SYSVAR_NAME(flush_log_at_timeout).flags & 15) == PLUGIN_VAR_INT);
21369 check_sysvar_int = MYSQL_SYSVAR_NAME(flush_log_at_timeout).check;
21370}
21371
21372/********************************************************************//**
21373Helper function to push warnings from InnoDB internals to SQL-layer. */
21374UNIV_INTERN
21375void
21376ib_push_warning(
21377 trx_t* trx, /*!< in: trx */
21378 dberr_t error, /*!< in: error code to push as warning */
21379 const char *format,/*!< in: warning message */
21380 ...)
21381{
21382 if (trx && trx->mysql_thd) {
21383 THD *thd = (THD *)trx->mysql_thd;
21384 va_list args;
21385 char *buf;
21386#define MAX_BUF_SIZE 4*1024
21387
21388 va_start(args, format);
21389 buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME));
21390 vsprintf(buf,format, args);
21391
21392 push_warning_printf(
21393 thd, Sql_condition::WARN_LEVEL_WARN,
21394 uint(convert_error_code_to_mysql(error, 0, thd)), buf);
21395 my_free(buf);
21396 va_end(args);
21397 }
21398}
21399
21400/********************************************************************//**
21401Helper function to push warnings from InnoDB internals to SQL-layer. */
21402UNIV_INTERN
21403void
21404ib_push_warning(
21405 void* ithd, /*!< in: thd */
21406 dberr_t error, /*!< in: error code to push as warning */
21407 const char *format,/*!< in: warning message */
21408 ...)
21409{
21410 va_list args;
21411 THD *thd = (THD *)ithd;
21412 char *buf;
21413#define MAX_BUF_SIZE 4*1024
21414
21415 if (ithd == NULL) {
21416 thd = current_thd;
21417 }
21418
21419 if (thd) {
21420 va_start(args, format);
21421 buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME));
21422 vsprintf(buf,format, args);
21423
21424 push_warning_printf(
21425 thd, Sql_condition::WARN_LEVEL_WARN,
21426 uint(convert_error_code_to_mysql(error, 0, thd)), buf);
21427 my_free(buf);
21428 va_end(args);
21429 }
21430}
21431
21432/********************************************************************//**
21433Helper function to push frm mismatch error to error log and
21434if needed to sql-layer. */
21435UNIV_INTERN
21436void
21437ib_push_frm_error(
21438/*==============*/
21439 THD* thd, /*!< in: MySQL thd */
21440 dict_table_t* ib_table, /*!< in: InnoDB table */
21441 TABLE* table, /*!< in: MySQL table */
21442 ulint n_keys, /*!< in: InnoDB #keys */
21443 bool push_warning) /*!< in: print warning ? */
21444{
21445 switch (ib_table->dict_frm_mismatch) {
21446 case DICT_FRM_NO_PK:
21447 sql_print_error("Table %s has a primary key in "
21448 "InnoDB data dictionary, but not "
21449 "in MariaDB!"
21450 " Have you mixed up "
21451 ".frm files from different "
21452 "installations? See "
21453 REFMAN
21454 "innodb-troubleshooting.html\n",
21455 ib_table->name.m_name);
21456
21457 if (push_warning) {
21458 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21459 ER_NO_SUCH_INDEX,
21460 "InnoDB: Table %s has a "
21461 "primary key in InnoDB data "
21462 "dictionary, but not in "
21463 "MariaDB!", ib_table->name.m_name);
21464 }
21465 break;
21466 case DICT_NO_PK_FRM_HAS:
21467 sql_print_error(
21468 "Table %s has no primary key in InnoDB data "
21469 "dictionary, but has one in MariaDB! If you "
21470 "created the table with a MariaDB version < "
21471 "3.23.54 and did not define a primary key, "
21472 "but defined a unique key with all non-NULL "
21473 "columns, then MariaDB internally treats that "
21474 "key as the primary key. You can fix this "
21475 "error by dump + DROP + CREATE + reimport "
21476 "of the table.", ib_table->name.m_name);
21477
21478 if (push_warning) {
21479 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21480 ER_NO_SUCH_INDEX,
21481 "InnoDB: Table %s has no "
21482 "primary key in InnoDB data "
21483 "dictionary, but has one in "
21484 "MariaDB!",
21485 ib_table->name.m_name);
21486 }
21487 break;
21488
21489 case DICT_FRM_INCONSISTENT_KEYS:
21490 sql_print_error("InnoDB: Table %s contains " ULINTPF " "
21491 "indexes inside InnoDB, which "
21492 "is different from the number of "
21493 "indexes %u defined in the MariaDB "
21494 " Have you mixed up "
21495 ".frm files from different "
21496 "installations? See "
21497 REFMAN
21498 "innodb-troubleshooting.html\n",
21499 ib_table->name.m_name, n_keys,
21500 table->s->keys);
21501
21502 if (push_warning) {
21503 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
21504 ER_NO_SUCH_INDEX,
21505 "InnoDB: Table %s contains " ULINTPF " "
21506 "indexes inside InnoDB, which "
21507 "is different from the number of "
21508 "indexes %u defined in the MariaDB ",
21509 ib_table->name.m_name, n_keys,
21510 table->s->keys);
21511 }
21512 break;
21513
21514 case DICT_FRM_CONSISTENT:
21515 default:
21516 sql_print_error("InnoDB: Table %s is consistent "
21517 "on InnoDB data dictionary and MariaDB "
21518 " FRM file.",
21519 ib_table->name.m_name);
21520 ut_error;
21521 break;
21522 }
21523}
21524