1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2008, 2009 Google Inc. |
5 | Copyright (c) 2009, Percona Inc. |
6 | Copyright (c) 2012, Facebook Inc. |
7 | Copyright (c) 2013, 2018, MariaDB Corporation. |
8 | |
9 | Portions of this file contain modifications contributed and copyrighted by |
10 | Google, Inc. Those modifications are gratefully acknowledged and are described |
11 | briefly in the InnoDB documentation. The contributions by Google are |
12 | incorporated with their permission, and subject to the conditions contained in |
13 | the file COPYING.Google. |
14 | |
15 | Portions of this file contain modifications contributed and copyrighted |
16 | by Percona Inc.. Those modifications are |
17 | gratefully acknowledged and are described briefly in the InnoDB |
18 | documentation. The contributions by Percona Inc. are incorporated with |
19 | their permission, and subject to the conditions contained in the file |
20 | COPYING.Percona. |
21 | |
22 | This program is free software; you can redistribute it and/or modify it under |
23 | the terms of the GNU General Public License as published by the Free Software |
24 | Foundation; version 2 of the License. |
25 | |
26 | This program is distributed in the hope that it will be useful, but WITHOUT |
27 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
28 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
29 | |
30 | You should have received a copy of the GNU General Public License along with |
31 | this program; if not, write to the Free Software Foundation, Inc., |
32 | 51 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 | |
122 | extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all); |
123 | unsigned long long thd_get_query_id(const MYSQL_THD thd); |
124 | TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len, |
125 | const char *table, size_t table_len); |
126 | MYSQL_THD create_thd(); |
127 | void destroy_thd(MYSQL_THD thd); |
128 | void reset_thd(MYSQL_THD thd); |
129 | TABLE *open_purge_table(THD *thd, const char *db, size_t dblen, |
130 | const char *tb, size_t tblen); |
131 | TABLE *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 | |
153 | extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log; |
154 | |
155 | static inline wsrep_ws_handle_t* |
156 | wsrep_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 | |
161 | extern TC_LOG* tc_log; |
162 | extern void wsrep_cleanup_transaction(THD *thd); |
163 | static int |
164 | wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd, |
165 | my_bool signal); |
166 | static void |
167 | wsrep_fake_trx_id(handlerton* hton, THD *thd); |
168 | static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid); |
169 | static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid); |
170 | #endif /* WITH_WSREP */ |
171 | |
172 | /** to protect innobase_open_files */ |
173 | static mysql_mutex_t innobase_share_mutex; |
174 | /** to force correct commit order in binlog */ |
175 | static ulong commit_threads = 0; |
176 | static mysql_cond_t commit_cond; |
177 | static mysql_mutex_t commit_cond_m; |
178 | static mysql_mutex_t pending_checkpoint_mutex; |
179 | |
180 | #define INSIDE_HA_INNOBASE_CC |
181 | |
182 | #define EQ_CURRENT_THD(thd) ((thd) == current_thd) |
183 | |
184 | struct handlerton* innodb_hton_ptr; |
185 | |
186 | static const long AUTOINC_OLD_STYLE_LOCKING = 0; |
187 | static const long AUTOINC_NEW_STYLE_LOCKING = 1; |
188 | static const long AUTOINC_NO_LOCKING = 2; |
189 | |
190 | static ulong innobase_open_files; |
191 | static long innobase_autoinc_lock_mode; |
192 | static ulong innobase_commit_concurrency; |
193 | |
194 | static ulonglong innobase_buffer_pool_size; |
195 | |
196 | /** Percentage of the buffer pool to reserve for 'old' blocks. |
197 | Connected to buf_LRU_old_ratio. */ |
198 | static uint innobase_old_blocks_pct; |
199 | |
200 | static char* innobase_data_file_path; |
201 | static char* innobase_temp_data_file_path; |
202 | |
203 | /* The default values for the following char* start-up parameters |
204 | are determined in innodb_init_params(). */ |
205 | |
206 | static char* innobase_data_home_dir; |
207 | static char* innobase_enable_monitor_counter; |
208 | static char* innobase_disable_monitor_counter; |
209 | static char* innobase_reset_monitor_counter; |
210 | static char* innobase_reset_all_monitor_counter; |
211 | |
212 | static ulong innodb_change_buffering; |
213 | static ulong innodb_flush_method; |
214 | |
215 | /* This variable can be set in the server configure file, specifying |
216 | stopword table to be used */ |
217 | static char* innobase_server_stopword_table; |
218 | |
219 | /* Below we have boolean-valued start-up parameters, and their default |
220 | values */ |
221 | |
222 | static my_bool innobase_use_atomic_writes; |
223 | static my_bool innobase_use_checksums; |
224 | static my_bool innobase_locks_unsafe_for_binlog; |
225 | static my_bool innobase_rollback_on_timeout; |
226 | static my_bool innobase_create_status_file; |
227 | my_bool innobase_stats_on_metadata; |
228 | static my_bool innodb_optimize_fulltext_only; |
229 | |
230 | static char* innodb_version_str = (char*) INNODB_VERSION_STR; |
231 | |
232 | extern uint srv_fil_crypt_rotate_key_age; |
233 | extern uint srv_n_fil_crypt_iops; |
234 | |
235 | extern my_bool srv_immediate_scrub_data_uncompressed; |
236 | extern my_bool srv_background_scrub_data_uncompressed; |
237 | extern my_bool srv_background_scrub_data_compressed; |
238 | extern uint srv_background_scrub_data_interval; |
239 | extern uint srv_background_scrub_data_check_interval; |
240 | #ifdef UNIV_DEBUG |
241 | extern my_bool srv_scrub_force_testing; |
242 | #endif |
243 | |
244 | /** Note we cannot use rec_format_enum because we do not allow |
245 | COMPRESSED row format for innodb_default_row_format option. */ |
246 | enum default_row_format_enum { |
247 | DEFAULT_ROW_FORMAT_REDUNDANT = 0, |
248 | DEFAULT_ROW_FORMAT_COMPACT = 1, |
249 | DEFAULT_ROW_FORMAT_DYNAMIC = 2, |
250 | }; |
251 | |
252 | static |
253 | void set_my_errno(int err) |
254 | { |
255 | errno = err; |
256 | } |
257 | |
258 | static 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 */ |
267 | static |
268 | char* |
269 | is_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 |
279 | running in innodb_read_only mode, srv_read_only_mode) */ |
280 | st_my_thread_var *srv_running; |
281 | /** Service thread that waits for the server shutdown and stops purge threads. |
282 | Purge workers have THDs that are needed to calculate virtual columns. |
283 | This THDs must be destroyed rather early in the server shutdown sequence. |
284 | This service thread creates a THD and idly waits for it to get a signal to |
285 | die. Then it notifies all purge workers to shutdown. |
286 | */ |
287 | static pthread_t thd_destructor_thread; |
288 | |
289 | pthread_handler_t |
290 | thd_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. */ |
341 | static |
342 | rec_format_t |
343 | get_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 | |
359 | static ulong innodb_default_row_format = DEFAULT_ROW_FORMAT_DYNAMIC; |
360 | |
361 | /** Possible values for system variable "innodb_stats_method". The values |
362 | are defined the same as its corresponding MyISAM system variable |
363 | "myisam_stats_method"(see "myisam_stats_method_names"), for better usability */ |
364 | static 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. |
372 | This is the same as "myisam_stats_method_typelib" */ |
373 | static 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 */ |
381 | const 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 |
392 | innodb_checksum_algorithm. */ |
393 | TYPELIB 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". */ |
401 | static 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 |
409 | innodb_default_row_format. */ |
410 | static 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 */ |
418 | static 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 |
425 | innodb_lock_schedule_algorithm. */ |
426 | static 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 */ |
434 | const 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 */ |
450 | TYPELIB 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 |
458 | about server activity: in case of normal DML ops it is not |
459 | sensible to call srv_active_wake_master_thread after each |
460 | operation, we only do it every INNOBASE_WAKE_INTERVAL'th step. */ |
461 | |
462 | #define INNOBASE_WAKE_INTERVAL 32 |
463 | static ulong innobase_active_counter = 0; |
464 | |
465 | static hash_table_t* innobase_open_tables; |
466 | |
467 | /** Allowed values of innodb_change_buffering */ |
468 | static 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 */ |
479 | static 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 |
487 | of m_prebuilt->fts_doc_id |
488 | @param[in,out] fts_hdl FTS handler |
489 | @return the relevance ranking value */ |
490 | static |
491 | float |
492 | innobase_fts_retrieve_ranking( |
493 | FT_INFO* fts_hdl); |
494 | /** Free the memory for the FTS handler |
495 | @param[in,out] fts_hdl FTS handler */ |
496 | static |
497 | void |
498 | innobase_fts_close_ranking( |
499 | FT_INFO* fts_hdl); |
500 | /** Find and Retrieve the FTS Relevance Ranking result for doc with doc_id |
501 | of m_prebuilt->fts_doc_id |
502 | @param[in,out] fts_hdl FTS handler |
503 | @return the relevance ranking value */ |
504 | static |
505 | float |
506 | innobase_fts_find_ranking( |
507 | FT_INFO* fts_hdl, |
508 | uchar*, |
509 | uint); |
510 | |
511 | /* Call back function array defined by MySQL and used to |
512 | retrieve FTS results. */ |
513 | const 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 */ |
520 | static |
521 | uint |
522 | innobase_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 */ |
531 | static |
532 | ulonglong |
533 | innobase_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 */ |
541 | static |
542 | ulonglong |
543 | innobase_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 */ |
549 | static |
550 | ulonglong |
551 | innobase_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 | |
563 | const 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 |
574 | performance schema */ |
575 | static mysql_pfs_key_t innobase_share_mutex_key; |
576 | static mysql_pfs_key_t commit_cond_mutex_key; |
577 | static mysql_pfs_key_t commit_cond_key; |
578 | static mysql_pfs_key_t pending_checkpoint_mutex_key; |
579 | static mysql_pfs_key_t thd_destructor_thread_key; |
580 | |
581 | static 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 | |
587 | static 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 |
593 | performance schema instrumented if "UNIV_PFS_MUTEX" |
594 | is defined */ |
595 | static 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 |
656 | performance schema instrumented if "UNIV_PFS_RWLOCK" |
657 | is defined */ |
658 | static 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 |
682 | performance schema instrumented if "UNIV_PFS_THREAD" |
683 | is defined */ |
684 | static 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 |
707 | performance schema instrumented if "UNIV_PFS_IO" is defined */ |
708 | static 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 | |
716 | static void innodb_remember_check_sysvar_funcs(); |
717 | mysql_var_check_func check_sysvar_enum; |
718 | mysql_var_check_func check_sysvar_int; |
719 | |
720 | // should page compression be used by default for new tables |
721 | static MYSQL_THDVAR_BOOL(compression_default, PLUGIN_VAR_OPCMDARG, |
722 | "Is compression the default for new tables" , |
723 | NULL, NULL, FALSE); |
724 | |
725 | static 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 | |
738 | ha_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 | /*************************************************************//** |
755 | Check whether valid argument given to innodb_ft_*_stopword_table. |
756 | This function is registered as a callback with MySQL. |
757 | @return 0 for valid stopword table */ |
758 | static |
759 | int |
760 | innodb_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 | |
769 | static bool is_mysql_datadir_path(const char *path); |
770 | |
771 | /** Validate passed-in "value" is a valid directory name. |
772 | This 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 */ |
778 | static |
779 | int |
780 | innodb_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 | /******************************************************************//** |
875 | Maps a MySQL trx isolation level code to the InnoDB isolation level code |
876 | @return InnoDB isolation level */ |
877 | static inline |
878 | ulint |
879 | innobase_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 */ |
887 | static inline |
888 | uint |
889 | get_field_offset( |
890 | const TABLE* table, |
891 | const Field* field) |
892 | { |
893 | return field->offset(table->record[0]); |
894 | } |
895 | |
896 | |
897 | /*************************************************************//** |
898 | Check for a valid value of innobase_compression_algorithm. |
899 | @return 0 for valid innodb_compression_algorithm. */ |
900 | static |
901 | int |
902 | innodb_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 | |
911 | static ibool innodb_have_lzo=IF_LZO(1, 0); |
912 | static ibool innodb_have_lz4=IF_LZ4(1, 0); |
913 | static ibool innodb_have_lzma=IF_LZMA(1, 0); |
914 | static ibool innodb_have_bzip2=IF_BZIP2(1, 0); |
915 | static ibool innodb_have_snappy=IF_SNAPPY(1, 0); |
916 | static ibool innodb_have_punch_hole=IF_PUNCH_HOLE(1, 0); |
917 | |
918 | static |
919 | int |
920 | innodb_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 | |
929 | static const char innobase_hton_name[]= "InnoDB" ; |
930 | |
931 | static 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 | |
936 | static MYSQL_THDVAR_BOOL(strict_mode, PLUGIN_VAR_OPCMDARG, |
937 | "Use strict mode when evaluating create options." , |
938 | NULL, NULL, TRUE); |
939 | |
940 | static MYSQL_THDVAR_BOOL(ft_enable_stopword, PLUGIN_VAR_OPCMDARG, |
941 | "Create FTS index with stopword." , |
942 | NULL, NULL, |
943 | /* default */ TRUE); |
944 | |
945 | static 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 | |
949 | static 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 | |
954 | static 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 | |
959 | static 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 | /************************************************************************//** |
1202 | Handling the shared INNOBASE_SHARE structure that is needed to provide table |
1203 | locking. Register the table name if it doesn't exist in the hash table. */ |
1204 | static |
1205 | INNOBASE_SHARE* |
1206 | get_share( |
1207 | /*======*/ |
1208 | const char* table_name); /*!< in: table to lookup */ |
1209 | |
1210 | /************************************************************************//** |
1211 | Free the shared object that was registered with get_share(). */ |
1212 | static |
1213 | void |
1214 | free_share( |
1215 | /*=======*/ |
1216 | INNOBASE_SHARE* share); /*!< in/own: share to free */ |
1217 | |
1218 | /*****************************************************************//** |
1219 | Frees a possible InnoDB trx object associated with the current THD. |
1220 | @return 0 or error number */ |
1221 | static |
1222 | int |
1223 | innobase_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() */ |
1231 | static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels); |
1232 | static void innobase_commit_ordered(handlerton *hton, THD* thd, bool all); |
1233 | |
1234 | /*****************************************************************//** |
1235 | Commits a transaction in an InnoDB database or marks an SQL statement |
1236 | ended. |
1237 | @return 0 */ |
1238 | static |
1239 | int |
1240 | innobase_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 | /*****************************************************************//** |
1251 | Rolls back a transaction to a savepoint. |
1252 | @return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the |
1253 | given name */ |
1254 | static |
1255 | int |
1256 | innobase_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 | /*****************************************************************//** |
1267 | Rolls back a transaction to a savepoint. |
1268 | @return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the |
1269 | given name */ |
1270 | static |
1271 | int |
1272 | innobase_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 | /*****************************************************************//** |
1281 | Check whether innodb state allows to safely release MDL locks after |
1282 | rollback to savepoint. |
1283 | @return true if it is safe, false if its not safe. */ |
1284 | static |
1285 | bool |
1286 | innobase_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 | /*****************************************************************//** |
1294 | Sets a transaction savepoint. |
1295 | @return always 0, that is, always succeeds */ |
1296 | static |
1297 | int |
1298 | innobase_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 | /*****************************************************************//** |
1307 | Release transaction savepoint name. |
1308 | @return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the |
1309 | given name */ |
1310 | static |
1311 | int |
1312 | innobase_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 | |
1320 | static void innobase_checkpoint_request(handlerton *hton, void *cookie); |
1321 | |
1322 | /** @brief Initialize the default value of innodb_commit_concurrency. |
1323 | |
1324 | Once InnoDB is running, the innodb_commit_concurrency must not change |
1325 | from zero to nonzero. (Bug #42101) |
1326 | |
1327 | The initial default value is 0, and without this extra initialization, |
1328 | SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter |
1329 | to 0, even if it was initially set to nonzero at the command line |
1330 | or configuration file. */ |
1331 | static |
1332 | void |
1333 | innobase_commit_concurrency_init_default(); |
1334 | /*=======================================*/ |
1335 | |
1336 | /** @brief Adjust some InnoDB startup parameters based on file contents |
1337 | or innodb_page_size. */ |
1338 | static |
1339 | void |
1340 | innodb_params_adjust(); |
1341 | |
1342 | /*******************************************************************//** |
1343 | This function is used to prepare an X/Open XA distributed transaction. |
1344 | @return 0 or error number */ |
1345 | static |
1346 | int |
1347 | innobase_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 | /*******************************************************************//** |
1357 | This function is used to recover X/Open XA distributed transactions. |
1358 | @return number of prepared transactions stored in xid_list */ |
1359 | static |
1360 | int |
1361 | innobase_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 | /*******************************************************************//** |
1367 | This function is used to commit one X/Open XA distributed transaction |
1368 | which is in the prepared state |
1369 | @return 0 or error number */ |
1370 | static |
1371 | int |
1372 | innobase_commit_by_xid( |
1373 | /*===================*/ |
1374 | handlerton* hton, /*!< in: InnoDB handlerton */ |
1375 | XID* xid); /*!< in: X/Open XA transaction |
1376 | identification */ |
1377 | /*******************************************************************//** |
1378 | This function is used to rollback one X/Open XA distributed transaction |
1379 | which is in the prepared state |
1380 | @return 0 or error number */ |
1381 | static |
1382 | int |
1383 | innobase_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 |
1392 | directory in the path is used as the database name. |
1393 | For example, in 'mysql/data/test' the database name is 'test'. */ |
1394 | static |
1395 | void |
1396 | innobase_drop_database( |
1397 | handlerton* hton, |
1398 | char* path); |
1399 | |
1400 | /** Shut down the InnoDB storage engine. |
1401 | @return 0 */ |
1402 | static |
1403 | int |
1404 | innobase_end(handlerton*, ha_panic_function); |
1405 | |
1406 | /*****************************************************************//** |
1407 | Creates an InnoDB transaction struct for the thd if it does not yet have one. |
1408 | Starts a new InnoDB transaction if a transaction is not yet started. And |
1409 | assigns a new snapshot for a consistent read if the transaction does not yet |
1410 | have one. |
1411 | @return 0 */ |
1412 | static |
1413 | int |
1414 | innobase_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 |
1424 | group commit during flush stage, false in other cases. |
1425 | @return false */ |
1426 | static |
1427 | bool |
1428 | innobase_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 |
1462 | group commit during flush stage, false in other cases. |
1463 | @return false */ |
1464 | static |
1465 | bool |
1466 | innobase_flush_logs( |
1467 | handlerton* hton) |
1468 | { |
1469 | return innobase_flush_logs(hton, true); |
1470 | } |
1471 | |
1472 | /************************************************************************//** |
1473 | Implements the SHOW ENGINE INNODB STATUS command. Sends the output of the |
1474 | InnoDB Monitor to the client. |
1475 | @return 0 on success */ |
1476 | static |
1477 | int |
1478 | innodb_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 | /************************************************************************//** |
1485 | Return 0 on success and non-zero on failure. Note: the bool return type |
1486 | seems to be abused here, should be an int. */ |
1487 | static |
1488 | bool |
1489 | innobase_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 | /****************************************************************//** |
1498 | Parse and enable InnoDB monitor counters during server startup. |
1499 | User can enable monitor counters/groups by specifying |
1500 | "loose-innodb_monitor_enable = monitor_name1;monitor_name2..." |
1501 | in server configuration file or at the command line. */ |
1502 | static |
1503 | void |
1504 | innodb_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 */ |
1512 | static |
1513 | void |
1514 | innobase_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 | /*************************************************************//** |
1528 | Check for a valid value of innobase_commit_concurrency. |
1529 | @return 0 for valid innodb_commit_concurrency */ |
1530 | static |
1531 | int |
1532 | innobase_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 | /*******************************************************************//** |
1558 | Function for constructing an InnoDB table handler instance. */ |
1559 | static |
1560 | handler* |
1561 | innobase_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. |
1573 | If correct, set the associated page_size_shift which is the power of 2 |
1574 | for this page size. |
1575 | @param[in] page_size Page Size to evaluate |
1576 | @return an associated page_size_shift if valid, 0 if invalid. */ |
1577 | inline |
1578 | ulong |
1579 | innodb_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 | /******************************************************************//** |
1598 | Returns true if the thread is the replication thread on the slave |
1599 | server. Used in srv_conc_enter_innodb() to determine if the thread |
1600 | should be allowed to enter InnoDB - the replication thread is treated |
1601 | differently than other threads. Also used in |
1602 | srv_conc_force_exit_innodb(). |
1603 | @return true if thd is the replication thread */ |
1604 | ibool |
1605 | thd_is_replication_slave_thread( |
1606 | /*============================*/ |
1607 | THD* thd) /*!< in: thread handle */ |
1608 | { |
1609 | return thd && ((ibool) thd_slave_thread(thd)); |
1610 | } |
1611 | |
1612 | /******************************************************************//** |
1613 | Returns true if transaction should be flagged as read-only. |
1614 | @return true if the thd is marked as read-only */ |
1615 | bool |
1616 | thd_trx_is_read_only( |
1617 | /*=================*/ |
1618 | THD* thd) /*!< in: thread handle */ |
1619 | { |
1620 | return(thd != 0 && thd_tx_is_read_only(thd)); |
1621 | } |
1622 | |
1623 | static 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 */ |
1631 | MYSQL_THD |
1632 | innobase_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 */ |
1644 | void |
1645 | innobase_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 */ |
1658 | void |
1659 | innobase_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 | /******************************************************************//** |
1676 | Check if the transaction is an auto-commit transaction. TRUE also |
1677 | implies that it is a SELECT (read-only) transaction. |
1678 | @return true if the transaction is an auto commit read-only transaction. */ |
1679 | ibool |
1680 | thd_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 | |
1691 | extern "C" time_t thd_start_time(const THD* thd); |
1692 | |
1693 | /******************************************************************//** |
1694 | Get the thread start time. |
1695 | @return the thread start time in seconds since the epoch. */ |
1696 | ulint 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 |
1704 | allowed, else the thread is put into sleep. |
1705 | @param[in,out] prebuilt row prebuilt handler */ |
1706 | static inline |
1707 | void |
1708 | innobase_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 |
1743 | any spare tickets. |
1744 | @param[in,out] m_prebuilt row prebuilt handler */ |
1745 | static inline |
1746 | void |
1747 | innobase_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 | /******************************************************************//** |
1770 | Force a thread to leave InnoDB even if it has spare tickets. */ |
1771 | static inline |
1772 | void |
1773 | innobase_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 | /******************************************************************//** |
1786 | Returns the NUL terminated value of glob_hostname. |
1787 | @return pointer to glob_hostname. */ |
1788 | const char* |
1789 | server_get_hostname() |
1790 | /*=================*/ |
1791 | { |
1792 | return(glob_hostname); |
1793 | } |
1794 | |
1795 | /******************************************************************//** |
1796 | Returns true if the transaction this thread is processing has edited |
1797 | non-transactional tables. Used by the deadlock detector when deciding |
1798 | which transaction to rollback in case of a deadlock - we try to avoid |
1799 | rolling back transactions that have edited non-transactional tables. |
1800 | @return true if non-transactional tables have been edited */ |
1801 | ibool |
1802 | thd_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 */ |
1810 | UNIV_INTERN |
1811 | unsigned long long |
1812 | thd_query_start_micro( |
1813 | const THD* thd) /*!< in: thread handle */ |
1814 | { |
1815 | return thd_start_utime(thd); |
1816 | } |
1817 | |
1818 | /******************************************************************//** |
1819 | Returns true if the thread is executing a SELECT statement. |
1820 | @return true if thd is executing SELECT */ |
1821 | ibool |
1822 | thd_is_select( |
1823 | /*==========*/ |
1824 | const THD* thd) /*!< in: thread handle */ |
1825 | { |
1826 | return(thd_sql_command(thd) == SQLCOM_SELECT); |
1827 | } |
1828 | |
1829 | /******************************************************************//** |
1830 | Returns the lock wait timeout for the current connection. |
1831 | @return the lock wait timeout, in seconds */ |
1832 | ulong |
1833 | thd_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="" */ |
1847 | const char* |
1848 | thd_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 */ |
1865 | static 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 | /********************************************************************//** |
1872 | Obtain the InnoDB transaction id of a MySQL thread. |
1873 | @return transaction id */ |
1874 | __attribute__((warn_unused_result, nonnull)) |
1875 | ulonglong |
1876 | thd_to_trx_id( |
1877 | THD* thd) /*!< in: MySQL thread */ |
1878 | { |
1879 | return(thd_to_trx(thd)->id); |
1880 | } |
1881 | #endif /* WITH_WSREP */ |
1882 | |
1883 | /********************************************************************//** |
1884 | Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth |
1885 | time calls srv_active_wake_master_thread. This function should be used |
1886 | when a single database operation may introduce a small need for |
1887 | server utility activity, like checkpointing. */ |
1888 | inline |
1889 | void |
1890 | innobase_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 | /********************************************************************//** |
1901 | Converts an InnoDB error code to a MySQL error code and also tells to MySQL |
1902 | about a possible transaction rollback inside InnoDB caused by a lock wait |
1903 | timeout or a deadlock. |
1904 | @return MySQL error code */ |
1905 | static int |
1906 | convert_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 | /*************************************************************//** |
2108 | Prints info of a THD object (== user session thread) to the given file. */ |
2109 | void |
2110 | innobase_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 | /******************************************************************//** |
2125 | Get the variable length bounds of the given character set. */ |
2126 | void |
2127 | innobase_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 | /******************************************************************//** |
2167 | Converts an identifier to a table name. */ |
2168 | void |
2169 | innobase_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 | /********************************************************************** |
2182 | Check if the length of the identifier exceeds the maximum allowed. |
2183 | return true when length of identifier is too long. */ |
2184 | my_bool |
2185 | innobase_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 | /******************************************************************//** |
2206 | Converts an identifier to UTF-8. */ |
2207 | void |
2208 | innobase_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 | /******************************************************************//** |
2221 | Compares NUL-terminated UTF-8 strings case insensitively. |
2222 | @return 0 if a=b, <0 if a<b, >1 if a>b */ |
2223 | int |
2224 | innobase_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 | /******************************************************************//** |
2243 | Compares NUL-terminated UTF-8 strings case insensitively. The |
2244 | second string contains wildcards. |
2245 | @return 0 if a match is found, 1 if not */ |
2246 | static |
2247 | int |
2248 | innobase_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 */ |
2259 | const char* |
2260 | innobase_basename( |
2261 | const char* path_name) |
2262 | { |
2263 | const char* name = base_name(path_name); |
2264 | |
2265 | return((name) ? name : "null" ); |
2266 | } |
2267 | |
2268 | /******************************************************************//** |
2269 | Makes all characters in a NUL-terminated UTF-8 string lower case. */ |
2270 | void |
2271 | innobase_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 | /**********************************************************************//** |
2279 | Determines the connection character set. |
2280 | @return connection character set */ |
2281 | CHARSET_INFO* |
2282 | innobase_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. |
2290 | Thread 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 */ |
2294 | const char* |
2295 | innobase_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. |
2309 | Thread safe, can be called from any thread as the string is copied |
2310 | into 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 */ |
2315 | size_t |
2316 | innobase_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 | /**********************************************************************//** |
2325 | Get the current setting of the tdc_size global parameter. We do |
2326 | a dirty read because for one there is no synchronization object and |
2327 | secondly there is little harm in doing so even if we get a torn read. |
2328 | @return value of tdc_size */ |
2329 | ulint |
2330 | innobase_get_table_cache_size(void) |
2331 | /*===============================*/ |
2332 | { |
2333 | return(tdc_size); |
2334 | } |
2335 | |
2336 | /**********************************************************************//** |
2337 | Get the current setting of the lower_case_table_names global parameter from |
2338 | mysqld.cc. We do a dirty read because for one there is no synchronization |
2339 | object and secondly there is little harm in doing so even if we get a torn |
2340 | read. |
2341 | @return value of lower_case_table_names */ |
2342 | ulint |
2343 | innobase_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 | */ |
2358 | static 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 | |
2382 | static 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 |
2394 | path. 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 */ |
2397 | os_file_t |
2398 | innobase_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 | |
2458 | error: |
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 | /*********************************************************************//** |
2469 | Wrapper around MySQL's copy_and_convert function. |
2470 | @return number of bytes copied to 'to' */ |
2471 | static |
2472 | ulint |
2473 | innobase_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 | /*******************************************************************//** |
2493 | Formats the raw data in "data" (in InnoDB on-disk format) that is of |
2494 | type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "charset_coll" and writes |
2495 | the result to "buf". The result is converted to "system_charset_info". |
2496 | Not more than "buf_size" bytes are written to "buf". |
2497 | The result is always NUL-terminated (provided buf_size > 0) and the |
2498 | number of bytes that were written to "buf" is returned (including the |
2499 | terminating NUL). |
2500 | @return number of bytes that were written */ |
2501 | ulint |
2502 | innobase_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 | /*********************************************************************//** |
2530 | Compute the next autoinc value. |
2531 | |
2532 | For MySQL replication the autoincrement values can be partitioned among |
2533 | the nodes. The offset is the start or origin of the autoincrement value |
2534 | for a particular node. For n nodes the increment will be n and the offset |
2535 | will be in the interval [1, n]. The formula tries to allocate the next |
2536 | value for a particular node. |
2537 | |
2538 | Note: This function is also called with increment set to the number of |
2539 | values we want to reserve for multi-value inserts e.g., |
2540 | |
2541 | INSERT INTO T VALUES(), (), (); |
2542 | |
2543 | innobase_next_autoinc() will be called with increment set to 3 where |
2544 | autoinc_lock_mode != TRADITIONAL because we want to reserve 3 values for |
2545 | the multi-value INSERT above. |
2546 | @return the next value */ |
2547 | ulonglong |
2548 | innobase_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 | /********************************************************************//** |
2640 | Reset the autoinc value in the table. |
2641 | @return DB_SUCCESS if all went well else error code */ |
2642 | UNIV_INTERN |
2643 | dberr_t |
2644 | ha_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 | /*******************************************************************//** |
2663 | Reset the auto-increment counter to the given value, i.e. the next row |
2664 | inserted will get the given value. This is called e.g. after TRUNCATE |
2665 | is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is |
2666 | returned by storage engines that don't support this operation. |
2667 | @return 0 or error code */ |
2668 | UNIV_INTERN |
2669 | int |
2670 | ha_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 | /*********************************************************************//** |
2698 | Initializes some fields in an InnoDB transaction object. */ |
2699 | static |
2700 | void |
2701 | innobase_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 | /*********************************************************************//** |
2719 | Allocates an InnoDB transaction for a MySQL handler object for DML. |
2720 | @return InnoDB transaction handle */ |
2721 | trx_t* |
2722 | innobase_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 | /*********************************************************************//** |
2742 | Gets the InnoDB transaction handle for a MySQL handler object, creates |
2743 | an InnoDB transaction struct if the corresponding MySQL thread struct still |
2744 | lacks one. |
2745 | @return InnoDB transaction handle */ |
2746 | static inline |
2747 | trx_t* |
2748 | check_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 | |
2770 | trx_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 | /*********************************************************************//** |
2781 | Note that a transaction has been registered with MySQL. |
2782 | @return true if transaction is registered with MySQL 2PC coordinator */ |
2783 | static inline |
2784 | bool |
2785 | trx_is_registered_for_2pc( |
2786 | /*======================*/ |
2787 | const trx_t* trx) /* in: transaction */ |
2788 | { |
2789 | return(trx->is_registered == 1); |
2790 | } |
2791 | |
2792 | /*********************************************************************//** |
2793 | Note that innobase_commit_ordered() was run. */ |
2794 | static inline |
2795 | void |
2796 | trx_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 | /*********************************************************************//** |
2805 | Note that a transaction has been registered with MySQL 2PC coordinator. */ |
2806 | static inline |
2807 | void |
2808 | trx_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 | /*********************************************************************//** |
2817 | Note that a transaction has been deregistered. */ |
2818 | static inline |
2819 | void |
2820 | trx_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 | /*********************************************************************//** |
2829 | Check whether a transaction has active_commit_ordered set */ |
2830 | static inline |
2831 | bool |
2832 | trx_is_active_commit_ordered( |
2833 | /*=========================*/ |
2834 | const trx_t* trx) /* in: transaction */ |
2835 | { |
2836 | return(trx->active_commit_ordered == 1); |
2837 | } |
2838 | |
2839 | /*********************************************************************//** |
2840 | Copy table flags from MySQL's HA_CREATE_INFO into an InnoDB table object. |
2841 | Those flags are stored in .frm file and end up in the MySQL table object, |
2842 | but are frequently used inside InnoDB so we keep their copies into the |
2843 | InnoDB table object. */ |
2844 | static |
2845 | void |
2846 | innobase_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 | /*********************************************************************//** |
2877 | Copy table flags from MySQL's TABLE_SHARE into an InnoDB table object. |
2878 | Those flags are stored in .frm file and end up in the MySQL table object, |
2879 | but are frequently used inside InnoDB so we keep their copies into the |
2880 | InnoDB table object. */ |
2881 | void |
2882 | innobase_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 | /*********************************************************************//** |
2912 | Construct ha_innobase handler. */ |
2913 | |
2914 | ha_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 | /*********************************************************************//** |
2950 | Destruct ha_innobase handler. */ |
2951 | |
2952 | ha_innobase::~ha_innobase() |
2953 | /*======================*/ |
2954 | { |
2955 | } |
2956 | |
2957 | /*********************************************************************//** |
2958 | Updates the user_thd field in a handle and also allocates a new InnoDB |
2959 | transaction handle if needed, and updates the transaction fields in the |
2960 | m_prebuilt struct. */ |
2961 | void |
2962 | ha_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 | /*********************************************************************//** |
2992 | Updates the user_thd field in a handle and also allocates a new InnoDB |
2993 | transaction handle if needed, and updates the transaction fields in the |
2994 | m_prebuilt struct. */ |
2995 | |
2996 | void |
2997 | ha_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 | /*********************************************************************//** |
3007 | Registers an InnoDB transaction with the MySQL 2PC coordinator, so that |
3008 | the MySQL XA code knows to call the InnoDB prepare and commit, or rollback |
3009 | for the transaction. This MUST be called for every transaction for which |
3010 | the user may call commit or rollback. Calling this several times to register |
3011 | the same transaction is allowed, too. This function also registers the |
3012 | current SQL statement. */ |
3013 | static inline |
3014 | void |
3015 | innobase_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 | |
3042 | 1) The use of the query cache for TBL is disabled when there is an |
3043 | uncommitted change to TBL. |
3044 | |
3045 | 2) When a change to TBL commits, InnoDB stores the current value of |
3046 | its global trx id counter, let us denote it by INV_TRX_ID, to the table object |
3047 | in the InnoDB data dictionary, and does only allow such transactions whose |
3048 | id <= INV_TRX_ID to use the query cache. |
3049 | |
3050 | 3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit |
3051 | modification because an ON DELETE CASCADE, we invalidate the MySQL query cache |
3052 | of TBL immediately. |
3053 | |
3054 | How this is implemented inside InnoDB: |
3055 | |
3056 | 1) Since every modification always sets an IX type table lock on the InnoDB |
3057 | table, it is easy to check if there can be uncommitted modifications for a |
3058 | table: just check if there are locks in the lock list of the table. |
3059 | |
3060 | 2) When a transaction inside InnoDB commits, it reads the global trx id |
3061 | counter and stores the value INV_TRX_ID to the tables on which it had a lock. |
3062 | |
3063 | 3) If there is an implicit table change from ON DELETE CASCADE or SET NULL, |
3064 | InnoDB calls an invalidate method for the MySQL query cache for that table. |
3065 | |
3066 | How this is implemented inside sql_cache.cc: |
3067 | |
3068 | 1) The query cache for an InnoDB table TBL is invalidated immediately at an |
3069 | INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay |
3070 | invalidation to the transaction commit. |
3071 | |
3072 | 2) To store or retrieve a value from the query cache of an InnoDB table TBL, |
3073 | any query must first ask InnoDB's permission. We must pass the thd as a |
3074 | parameter because InnoDB will look at the trx id, if any, associated with |
3075 | that thd. Also the full_name which is used as key to search for the table |
3076 | object. The full_name is a string containing the normalized path to the |
3077 | table in the canonical format. |
3078 | |
3079 | 3) Use of the query cache for InnoDB tables is now allowed also when |
3080 | AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer |
3081 | put restrictions on the use of the query cache. |
3082 | */ |
3083 | |
3084 | /******************************************************************//** |
3085 | The MySQL query cache uses this to check from InnoDB if the query cache at |
3086 | the moment is allowed to operate on an InnoDB table. The SQL query must |
3087 | be a non-locking SELECT. |
3088 | |
3089 | The query cache is allowed to operate on certain query only if this function |
3090 | returns TRUE for all tables in the query. |
3091 | |
3092 | If thd is not in the autocommit state, this function also starts a new |
3093 | transaction for thd if there is no active trx yet, and assigns a consistent |
3094 | read view to it if there is no read view yet. |
3095 | |
3096 | Why a deadlock of threads is not possible: the query cache calls this function |
3097 | at the start of a SELECT processing. Then the calling thread cannot be |
3098 | holding any InnoDB semaphores. The calling thread is holding the |
3099 | query cache mutex, and this function will reserve the InnoDB trx_sys.mutex. |
3100 | Thus, the 'rank' in sync0mutex.h of the MySQL query cache mutex is above |
3101 | the InnoDB trx_sys.mutex. |
3102 | @return TRUE if permitted, FALSE if not; note that the value FALSE |
3103 | does not mean we should invalidate the query cache: invalidation is |
3104 | called explicitly */ |
3105 | static |
3106 | my_bool |
3107 | innobase_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 | /*****************************************************************//** |
3163 | Invalidates the MySQL query cache for the table. */ |
3164 | void |
3165 | innobase_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 */ |
3214 | void |
3215 | innobase_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 */ |
3244 | std::string |
3245 | innobase_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) |
3267 | and 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 */ |
3274 | static |
3275 | char* |
3276 | innobase_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 | /*****************************************************************//** |
3306 | Convert a table name to the MySQL system_charset_info (UTF-8). |
3307 | @return pointer to the end of buf */ |
3308 | char* |
3309 | innobase_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 | /*****************************************************************//** |
3342 | A wrapper function of innobase_convert_name(), convert a table name |
3343 | to the MySQL system_charset_info (UTF-8) and quote it if needed. |
3344 | @return pointer to the end of buf */ |
3345 | void |
3346 | innobase_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 | /**********************************************************************//** |
3362 | Determines if the currently running transaction has been interrupted. |
3363 | @return true if interrupted */ |
3364 | bool |
3365 | trx_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 | /**********************************************************************//** |
3373 | Determines if the currently running transaction is in strict mode. |
3374 | @return TRUE if strict */ |
3375 | ibool |
3376 | trx_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 | /**************************************************************//** |
3384 | Resets some fields of a m_prebuilt struct. The template is used in fast |
3385 | retrieval of just those column values MySQL needs in its processing. */ |
3386 | void |
3387 | ha_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 | /*****************************************************************//** |
3415 | Call this when you have opened a new table handle in HANDLER, before you |
3416 | call index_read_map() etc. Actually, we can let the cursor stay open even |
3417 | over a transaction commit! Then you should call this before every operation, |
3418 | fetch next etc. This function inits the necessary things even after a |
3419 | transaction commit. */ |
3420 | |
3421 | void |
3422 | ha_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 | /*********************************************************************//** |
3471 | Free tablespace resources allocated. */ |
3472 | static |
3473 | void |
3474 | innobase_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 */ |
3498 | static 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 |
3506 | corresponding 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 */ |
3510 | static inline |
3511 | bool |
3512 | innodb_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 | /****************************************************************//** |
3546 | Gives the file extension of an InnoDB single-table tablespace. */ |
3547 | static 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 */ |
3558 | static 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. */ |
3582 | static 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 */ |
3647 | static 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 */ |
4064 | static 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 */ |
4272 | static |
4273 | int |
4274 | innobase_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 | /*****************************************************************//** |
4317 | Commits a transaction in an InnoDB database. */ |
4318 | void |
4319 | innobase_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 | /*****************************************************************//** |
4351 | Creates an InnoDB transaction struct for the thd if it does not yet have one. |
4352 | Starts a new InnoDB transaction if a transaction is not yet started. And |
4353 | assigns a new snapshot for a consistent read if the transaction does not yet |
4354 | have one. |
4355 | @return 0 */ |
4356 | static |
4357 | int |
4358 | innobase_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 | |
4403 | static |
4404 | void |
4405 | innobase_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 | /*****************************************************************//** |
4478 | Perform the first, fast part of InnoDB commit. |
4479 | |
4480 | Doing it in this call ensures that we get the same commit order here |
4481 | as in binlog and any other participating transactional storage engines. |
4482 | |
4483 | Note that we want to do as little as really needed here, as we run |
4484 | under a global mutex. The expensive fsync() is done later, in |
4485 | innobase_commit(), without a lock so group commit can take place. |
4486 | |
4487 | Note also that this method can be called from a different thread than |
4488 | the one handling the rest of the transaction. */ |
4489 | static |
4490 | void |
4491 | innobase_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 | /*****************************************************************//** |
4524 | Commits a transaction in an InnoDB database or marks an SQL statement |
4525 | ended. |
4526 | @return 0 or deadlock error if the transaction was aborted by another |
4527 | higher priority transaction. */ |
4528 | static |
4529 | int |
4530 | innobase_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 | /*****************************************************************//** |
4618 | Rolls back a transaction or the latest SQL statement. |
4619 | @return 0 or error number */ |
4620 | static |
4621 | int |
4622 | innobase_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 | /*****************************************************************//** |
4673 | Rolls back a transaction |
4674 | @return 0 or error number */ |
4675 | static |
4676 | int |
4677 | innobase_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 | |
4701 | struct pending_checkpoint { |
4702 | struct pending_checkpoint *next; |
4703 | handlerton *hton; |
4704 | void *cookie; |
4705 | ib_uint64_t lsn; |
4706 | }; |
4707 | static struct pending_checkpoint *pending_checkpoint_list; |
4708 | static struct pending_checkpoint *pending_checkpoint_list_end; |
4709 | |
4710 | /*****************************************************************//** |
4711 | Handle a commit checkpoint request from server layer. |
4712 | We put the request in a queue, so that we can notify upper layer about |
4713 | checkpoint complete when we have flushed the redo log. |
4714 | If we have already flushed all relevant redo log, we notify immediately.*/ |
4715 | static |
4716 | void |
4717 | innobase_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 | /*****************************************************************//** |
4770 | Log code calls this whenever log has been written and/or flushed up |
4771 | to a new position. We use this to notify upper layer of a new commit |
4772 | checkpoint when necessary.*/ |
4773 | UNIV_INTERN |
4774 | void |
4775 | innobase_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 | /*****************************************************************//** |
4841 | Rolls back a transaction to a savepoint. |
4842 | @return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the |
4843 | given name */ |
4844 | static |
4845 | int |
4846 | innobase_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 | /*****************************************************************//** |
4881 | Check whether innodb state allows to safely release MDL locks after |
4882 | rollback to savepoint. |
4883 | When binlog is on, MDL locks acquired after savepoint unit are not |
4884 | released if there are any locks held in InnoDB. |
4885 | @return true if it is safe, false if its not safe. */ |
4886 | static |
4887 | bool |
4888 | innobase_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 | /*****************************************************************//** |
4911 | Release transaction savepoint name. |
4912 | @return 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the |
4913 | given name */ |
4914 | static |
4915 | int |
4916 | innobase_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 | /*****************************************************************//** |
4947 | Sets a transaction savepoint. |
4948 | @return always 0, that is, always succeeds */ |
4949 | static |
4950 | int |
4951 | innobase_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 | /*****************************************************************//** |
4986 | Frees a possible InnoDB trx object associated with the current THD. |
4987 | @return 0 or error number */ |
4988 | static |
4989 | int |
4990 | innobase_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 { |
5046 | rollback_and_free: |
5047 | innobase_rollback_trx(trx); |
5048 | trx_free(trx); |
5049 | } |
5050 | } |
5051 | |
5052 | DBUG_RETURN(0); |
5053 | } |
5054 | |
5055 | UNIV_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() */ |
5059 | static 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, |
5090 | ROW_TYPE_COMPRESSED, ROW_TYPE_DYNAMIC */ |
5091 | |
5092 | enum row_type |
5093 | ha_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 | /****************************************************************//** |
5114 | Get the table flags to use for the statement. |
5115 | @return table flags */ |
5116 | |
5117 | handler::Table_flags |
5118 | ha_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 | /****************************************************************//** |
5135 | Returns the table type (storage engine name). |
5136 | @return table type */ |
5137 | |
5138 | const char* |
5139 | ha_innobase::table_type() const |
5140 | /*===========================*/ |
5141 | { |
5142 | return(innobase_hton_name); |
5143 | } |
5144 | |
5145 | /****************************************************************//** |
5146 | Returns the index type. |
5147 | @return index type */ |
5148 | |
5149 | const char* |
5150 | ha_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 | /****************************************************************//** |
5166 | Returns the table file name extension. |
5167 | @return file extension string */ |
5168 | |
5169 | const char** |
5170 | ha_innobase::bas_ext() const |
5171 | /*========================*/ |
5172 | { |
5173 | return(ha_innobase_exts); |
5174 | } |
5175 | |
5176 | /****************************************************************//** |
5177 | Returns the operations supported for indexes. |
5178 | @return flags of supported operations */ |
5179 | |
5180 | ulong |
5181 | ha_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 = 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 | /****************************************************************//** |
5213 | Returns the maximum number of keys. |
5214 | @return MAX_KEY */ |
5215 | |
5216 | uint |
5217 | ha_innobase::max_supported_keys() const |
5218 | /*===================================*/ |
5219 | { |
5220 | return(MAX_KEY); |
5221 | } |
5222 | |
5223 | /****************************************************************//** |
5224 | Returns the maximum key length. |
5225 | @return maximum supported key length, in bytes */ |
5226 | |
5227 | uint |
5228 | ha_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 | /****************************************************************//** |
5261 | Returns the key map of keys that are usable for scanning. |
5262 | @return key_map_full */ |
5263 | |
5264 | const key_map* |
5265 | ha_innobase::keys_to_use_for_scanning() |
5266 | /*===================================*/ |
5267 | { |
5268 | return(&key_map_full); |
5269 | } |
5270 | |
5271 | /****************************************************************//** |
5272 | Ensures that if there's a concurrent inplace ADD INDEX, being-indexed virtual |
5273 | columns are computed. They are not marked as indexed in the old table, so the |
5274 | server won't add them to the vcol_set automatically */ |
5275 | void |
5276 | ha_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 | /****************************************************************//** |
5302 | Determines if table caching is supported. |
5303 | @return HA_CACHE_TBL_ASKTRANSACT */ |
5304 | |
5305 | uint8 |
5306 | ha_innobase::table_cache_type() |
5307 | /*===========================*/ |
5308 | { |
5309 | return(HA_CACHE_TBL_ASKTRANSACT); |
5310 | } |
5311 | |
5312 | /****************************************************************//** |
5313 | Determines if the primary key is clustered index. |
5314 | @return true */ |
5315 | |
5316 | bool |
5317 | ha_innobase::primary_key_is_clustered() |
5318 | /*===================================*/ |
5319 | { |
5320 | return(true); |
5321 | } |
5322 | |
5323 | /** Normalizes a table name string. |
5324 | A normalized name consists of the database name catenated to '/' |
5325 | and table name. For example: test/mytable. |
5326 | On Windows, normalization puts both the database name and the |
5327 | table 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. */ |
5331 | void |
5332 | normalize_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. |
5392 | A normalized name consists of the database name catenated to '/' |
5393 | and table name. For example: test/mytable. |
5394 | On Windows, normalization puts both the database name and the |
5395 | table 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. */ |
5399 | void |
5400 | create_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 | /********************************************************************* |
5410 | Test normalize_table_name_low(). */ |
5411 | static |
5412 | void |
5413 | test_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 | /********************************************************************* |
5475 | Test ut_format_name(). */ |
5476 | static |
5477 | void |
5478 | test_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. |
5537 | This function checks whether the index column information |
5538 | is 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. */ |
5542 | bool |
5543 | innobase_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 | */ |
5631 | static |
5632 | void |
5633 | innobase_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 |
5683 | is 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 */ |
5690 | void |
5691 | innobase_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 | /*******************************************************************//** |
5832 | This function builds a translation table in INNOBASE_SHARE |
5833 | structure for fast index location with mysql array number from its |
5834 | table->key_info structure. This also provides the necessary translation |
5835 | between the key order in mysql key_info and InnoDB ib_table->indexes if |
5836 | they are not fully matched with each other. |
5837 | Note we do not have any mutex protecting the translation table |
5838 | building based on the assumption that there is no concurrent |
5839 | index creation/drop and DMLs that requires index lookup. All table |
5840 | handle will be closed before the index creation/drop. |
5841 | @return true if index translation table built successfully */ |
5842 | static |
5843 | bool |
5844 | innobase_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 | |
5937 | func_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 | /*******************************************************************//** |
5955 | This function uses index translation table to quickly locate the |
5956 | requested index structure. |
5957 | Note we do not have mutex protection for the index translatoin table |
5958 | access, it is based on the assumption that there is no concurrent |
5959 | translation table rebuild (fter create/drop index) and DMLs that |
5960 | require index lookup. |
5961 | @return dict_index_t structure for requested index. NULL if |
5962 | fail to locate the index structure. */ |
5963 | static |
5964 | dict_index_t* |
5965 | innobase_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 | /********************************************************************//** |
5980 | Get the upper limit of the MySQL integral and floating-point type. |
5981 | @return maximum allowed value for the field */ |
5982 | UNIV_INTERN |
5983 | ulonglong |
5984 | innobase_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 | |
6043 | Since a partial table definition for a persistent table can already be |
6044 | present in the InnoDB dict_sys cache before it is accessed from SQL, |
6045 | we have to initialize the AUTO_INCREMENT counter on the first |
6046 | ha_innobase::open(). |
6047 | |
6048 | @param[in,out] table persistent table |
6049 | @param[in] field the AUTO_INCREMENT column */ |
6050 | static |
6051 | void |
6052 | initialize_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 */ |
6095 | int |
6096 | ha_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 | } |
6135 | no_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[] */ |
6387 | unsigned |
6388 | innodb_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 |
6403 | try alternative lower/upper case names to support moving data files across |
6404 | platforms. |
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 */ |
6410 | dict_table_t* |
6411 | ha_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 | |
6494 | handler* |
6495 | ha_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 | |
6516 | uint |
6517 | ha_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 | /******************************************************************//** |
6526 | Closes a handle to an InnoDB table. |
6527 | @return 0 */ |
6528 | |
6529 | int |
6530 | ha_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 |
6559 | UNIV_INTERN |
6560 | ulint |
6561 | wsrep_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 | /******************************************************************//** |
6670 | compare two character string according to their charset. */ |
6671 | int |
6672 | innobase_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 | /******************************************************************//** |
6688 | compare two character string case insensitively according to their charset. */ |
6689 | int |
6690 | innobase_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 | /******************************************************************//** |
6711 | Get the first character's code position for FTS index partition. */ |
6712 | ulint |
6713 | innobase_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 | /******************************************************************//** |
6739 | compare two character string according to their charset. */ |
6740 | int |
6741 | innobase_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 | /******************************************************************//** |
6762 | Makes all characters in a string lower case. */ |
6763 | size_t |
6764 | innobase_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 | /*************************************************************//** |
6788 | Get the next token from the given string and store it in *token. |
6789 | It is mostly copied from MyISAM's doc parsing function ft_simple_get_word() |
6790 | @return length of string processed */ |
6791 | ulint |
6792 | innobase_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 |
6857 | the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1 |
6858 | VARCHAR 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 |
6860 | ENUM and SET, and unsigned integer types are 'unsigned types' |
6861 | @param[in] f MySQL Field |
6862 | @return DATA_BINARY, DATA_VARCHAR, ... */ |
6863 | ulint |
6864 | get_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 | /*******************************************************************//** |
6967 | Reads an unsigned integer value < 64k from 2 bytes, in the little-endian |
6968 | storage format. |
6969 | @return value */ |
6970 | static inline |
6971 | uint |
6972 | innobase_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 | /*******************************************************************//** |
6981 | Stores a key value for a row to a buffer. |
6982 | @return key value length as stored in buff */ |
6983 | UNIV_INTERN |
6984 | uint |
6985 | wsrep_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 | /**************************************************************//** |
7284 | Determines if a field is needed in a m_prebuilt struct 'template'. |
7285 | @return field to use, or NULL if the field is not needed */ |
7286 | static |
7287 | const Field* |
7288 | build_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 | /**************************************************************//** |
7346 | Determines if a field is needed in a m_prebuilt struct 'template'. |
7347 | @return whether the field is needed for index condition pushdown */ |
7348 | inline |
7349 | bool |
7350 | build_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 | /**************************************************************//** |
7368 | Adds a field to a m_prebuilt struct 'template'. |
7369 | @return the field template */ |
7370 | static |
7371 | mysql_row_templ_t* |
7372 | build_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 | /**************************************************************//** |
7546 | Builds a 'template' to the m_prebuilt struct. The template is used in fast |
7547 | retrieval of just those column values MySQL needs in its processing. */ |
7548 | |
7549 | void |
7550 | ha_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 { |
7833 | no_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 | /********************************************************************//** |
7914 | This special handling is really to overcome the limitations of MySQL's |
7915 | binlogging. We need to eliminate the non-determinism that will arise in |
7916 | INSERT ... SELECT type of statements, since MySQL binlog only stores the |
7917 | min value of the autoinc interval. Once that is fixed we can get rid of |
7918 | the special lock handling. |
7919 | @return DB_SUCCESS if all OK else error code */ |
7920 | |
7921 | dberr_t |
7922 | ha_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 | /********************************************************************//** |
7983 | Store the autoinc value in the table. The autoinc value is only set if |
7984 | it's greater than the existing autoinc value in the table. |
7985 | @return DB_SUCCESS if all went well else error code */ |
7986 | |
7987 | dberr_t |
7988 | ha_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 | /********************************************************************//** |
8007 | Stores a row in an InnoDB database, to the table specified in this |
8008 | handle. |
8009 | @return error code */ |
8010 | |
8011 | int |
8012 | ha_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) { |
8180 | set_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 | |
8214 | report_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 | } |
8239 | wsrep_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 | |
8246 | func_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, |
8253 | but indexed columns. Such columns could stil present in the virtual |
8254 | index rec fields even if they are not updated (some other fields updated), |
8255 | so 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() */ |
8264 | static |
8265 | byte* |
8266 | innodb_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 |
8294 | between 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 */ |
8304 | static |
8305 | dberr_t |
8306 | calc_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 |
8703 | static |
8704 | int |
8705 | wsrep_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 | /** |
8785 | Updates a row given as a parameter to a new value. Note that we are given |
8786 | whole rows, not just the fields which are updated: this incurs some |
8787 | overhead for CPU when we check which fields are actually updated. |
8788 | TODO: currently InnoDB does not prevent the 'Halloween problem': |
8789 | in a searched update a single row can get updated several times |
8790 | if 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 | |
8795 | int |
8796 | ha_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 | |
8914 | func_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 | } |
8942 | wsrep_error: |
8943 | #endif /* WITH_WSREP */ |
8944 | |
8945 | |
8946 | DBUG_RETURN(err); |
8947 | } |
8948 | |
8949 | /**********************************************************************//** |
8950 | Deletes a row given as the parameter. |
8951 | @return error number or 0 */ |
8952 | |
8953 | int |
8954 | ha_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 | } |
9005 | wsrep_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 | |
9014 | int |
9015 | ha_innobase::delete_all_rows() |
9016 | { |
9017 | DBUG_ENTER("ha_innobase::delete_all_rows" ); |
9018 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
9019 | } |
9020 | |
9021 | /**********************************************************************//** |
9022 | Removes a new lock set on a row, if it was not read optimistically. This can |
9023 | be called after a row has been read in the processing of an UPDATE or a DELETE |
9024 | query, if the option innodb_locks_unsafe_for_binlog is set. */ |
9025 | |
9026 | void |
9027 | ha_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 | |
9059 | bool |
9060 | ha_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 | |
9068 | void |
9069 | ha_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 | /******************************************************************//** |
9092 | Initializes a handle to use an index. |
9093 | @return 0 or error number */ |
9094 | |
9095 | int |
9096 | ha_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 | /******************************************************************//** |
9107 | Currently does nothing. |
9108 | @return 0 */ |
9109 | |
9110 | int |
9111 | ha_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 | /*********************************************************************//** |
9126 | Converts a search mode flag understood by MySQL to a flag understood |
9127 | by InnoDB. */ |
9128 | page_cur_mode_t |
9129 | convert_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 | --------------------------------------------------- |
9171 | The following does not cover all the details, but explains how we determine |
9172 | the start of a new SQL statement, and what is associated with it. |
9173 | |
9174 | For each table in the database the MySQL interpreter may have several |
9175 | table handle instances in use, also in a single SQL query. For each table |
9176 | handle instance there is an InnoDB 'm_prebuilt' struct which contains most |
9177 | of 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 |
9182 | the table of the handle instance. There we set |
9183 | m_prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set |
9184 | true if we are taking this table handle instance to use in a new SQL |
9185 | statement 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 |
9188 | instructions 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 |
9192 | allocate a new consistent read view for the trx if it does not yet have one, |
9193 | or in the case of a locking read, set an InnoDB 'intention' table level |
9194 | lock on the table. |
9195 | |
9196 | 4) We do the SELECT. MySQL may repeatedly call ::index_read for the |
9197 | same table handle instance, if it is a join. |
9198 | |
9199 | 5) When the SELECT ends, MySQL removes its intention table level locks |
9200 | in ::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 |
9203 | have for this SQL statement. The MySQL interpreter does NOT execute |
9204 | autocommit for pure read transactions, though it should. That is why the |
9205 | table 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 |
9208 | does NOT call ::external_lock at the start of the statement. To determine |
9209 | when 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 |
9211 | table handle instance was used. If it has changed, we know we are at the |
9212 | start of a new SQL statement. Since the query id can theoretically |
9213 | overwrap, we use this test only as a secondary way of determining the |
9214 | start of a new SQL statement. */ |
9215 | |
9216 | |
9217 | /**********************************************************************//** |
9218 | Positions an index cursor to the index specified in the handle. Fetches the |
9219 | row if any. |
9220 | @return 0, HA_ERR_KEY_NOT_FOUND, or error number */ |
9221 | |
9222 | int |
9223 | ha_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 | /*******************************************************************//** |
9386 | The following functions works like index_read, but it find the last |
9387 | row with the current key value or prefix. |
9388 | @return 0, HA_ERR_KEY_NOT_FOUND, or an error code */ |
9389 | |
9390 | int |
9391 | ha_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 | /********************************************************************//** |
9403 | Get the index for a handle. Does not change active index. |
9404 | @return NULL or index instance. */ |
9405 | |
9406 | dict_index_t* |
9407 | ha_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 | /********************************************************************//** |
9481 | Changes the active index of a handle. |
9482 | @return 0 or error code */ |
9483 | |
9484 | int |
9485 | ha_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 | /***********************************************************************//** |
9595 | Reads the next or previous row from a cursor, which must have previously been |
9596 | positioned using index_read. |
9597 | @return 0, HA_ERR_END_OF_FILE, or error number */ |
9598 | |
9599 | int |
9600 | ha_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 | /***********************************************************************//** |
9683 | Reads the next row from a cursor, which must have previously been |
9684 | positioned using index_read. |
9685 | @return 0, HA_ERR_END_OF_FILE, or error number */ |
9686 | |
9687 | int |
9688 | ha_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 | /*******************************************************************//** |
9697 | Reads the next row matching to the key value given as the parameter. |
9698 | @return 0, HA_ERR_END_OF_FILE, or error number */ |
9699 | |
9700 | int |
9701 | ha_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 | /***********************************************************************//** |
9710 | Reads the previous row from a cursor, which must have previously been |
9711 | positioned using index_read. |
9712 | @return 0, HA_ERR_END_OF_FILE, or error number */ |
9713 | |
9714 | int |
9715 | ha_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 | /********************************************************************//** |
9723 | Positions a cursor on the first record in an index and reads the |
9724 | corresponding row to buf. |
9725 | @return 0, HA_ERR_END_OF_FILE, or error code */ |
9726 | |
9727 | int |
9728 | ha_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 | /********************************************************************//** |
9746 | Positions a cursor on the last record in an index and reads the |
9747 | corresponding row to buf. |
9748 | @return 0, HA_ERR_END_OF_FILE, or error code */ |
9749 | |
9750 | int |
9751 | ha_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 | /****************************************************************//** |
9769 | Initialize a table scan. |
9770 | @return 0 or error number */ |
9771 | |
9772 | int |
9773 | ha_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 | /*****************************************************************//** |
9801 | Ends a table scan. |
9802 | @return 0 or error number */ |
9803 | |
9804 | int |
9805 | ha_innobase::rnd_end(void) |
9806 | /*======================*/ |
9807 | { |
9808 | return(index_end()); |
9809 | } |
9810 | |
9811 | /*****************************************************************//** |
9812 | Reads the next row in a table scan (also used to read the FIRST row |
9813 | in a table scan). |
9814 | @return 0, HA_ERR_END_OF_FILE, or error number */ |
9815 | |
9816 | int |
9817 | ha_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 | /**********************************************************************//** |
9842 | Fetches a row from the table based on a row reference. |
9843 | @return 0, HA_ERR_KEY_NOT_FOUND, or error code */ |
9844 | |
9845 | int |
9846 | ha_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 | /**********************************************************************//** |
9872 | Initialize FT index scan |
9873 | @return 0 or error number */ |
9874 | |
9875 | int |
9876 | ha_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 | /**********************************************************************//** |
9896 | Initialize FT index scan |
9897 | @return FT_INFO structure if successful or NULL */ |
9898 | |
9899 | FT_INFO* |
9900 | ha_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 | /*****************************************************************//** |
10020 | Set up search tuple for a query through FTS_DOC_ID_INDEX on |
10021 | supplied Doc ID. This is used by MySQL to retrieve the documents |
10022 | once the search result (Doc IDs) is available */ |
10023 | static |
10024 | void |
10025 | innobase_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 | /**********************************************************************//** |
10064 | Fetch next result from the FT result set |
10065 | @return error code */ |
10066 | |
10067 | int |
10068 | ha_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 | |
10102 | next_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 | |
10215 | void |
10216 | ha_innobase::ft_end() |
10217 | { |
10218 | ib::info() << "ft_end()" ; |
10219 | |
10220 | rnd_end(); |
10221 | } |
10222 | #ifdef WITH_WSREP |
10223 | extern dict_index_t* |
10224 | wsrep_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 | |
10235 | extern dberr_t |
10236 | wsrep_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 | |
10416 | static int |
10417 | wsrep_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 | |
10474 | static bool |
10475 | referenced_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 | |
10499 | int |
10500 | ha_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 | /*********************************************************************//** |
10649 | Stores a reference to the current row to 'ref' field of the handle. Note |
10650 | that in the case where we have generated the clustered index for the |
10651 | table, the function parameter is illogical: we MUST ASSUME that 'record' |
10652 | is the current 'position' of the handle, because if row ref is actually |
10653 | the row id internally generated in InnoDB, then 'record' does not contain |
10654 | it. We just guess that the row id must be for the record where the handle |
10655 | was positioned the last time. */ |
10656 | |
10657 | void |
10658 | ha_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 | /*****************************************************************//** |
10687 | Check whether there exist a column named as "FTS_DOC_ID", which is |
10688 | reserved for InnoDB FTS Doc ID |
10689 | @return true if there exist a "FTS_DOC_ID" column */ |
10690 | static |
10691 | bool |
10692 | create_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. |
10753 | This is returned as a bitmap, in field->table->tmp_set. |
10754 | Works 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 */ |
10758 | template <typename T> |
10759 | void |
10760 | prepare_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 */ |
10792 | void |
10793 | innodb_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 */ |
10830 | void |
10831 | innodb_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 */ |
10872 | inline MY_ATTRIBUTE((warn_unused_result)) |
10873 | int |
10874 | create_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); |
11064 | err_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 | /*****************************************************************//** |
11207 | Creates an index in an InnoDB database. */ |
11208 | inline |
11209 | int |
11210 | create_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 */ |
11362 | static |
11363 | const char* |
11364 | get_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. */ |
11389 | bool |
11390 | create_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, |
11424 | ROW_FORMAT, DATA DIRECTORY, TEMPORARY are compatible with |
11425 | each other and other settings. These CREATE OPTIONS are not validated |
11426 | here unless innodb_strict_mode is on. With strict mode, this function |
11427 | will report each problem it finds using a custom message with error |
11428 | code ER_ILLEGAL_HA_CREATE_OPTION, not its built-in message. |
11429 | @return NULL if valid, string name of bad option if not. */ |
11430 | const char* |
11431 | create_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 | /*****************************************************************//** |
11581 | Check engine specific table options not handled by SQL-parser. |
11582 | @return NULL if valid, string if not */ |
11583 | const char* |
11584 | create_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 | /*****************************************************************//** |
11731 | Update create_info. Used in SHOW CREATE TABLE et al. */ |
11732 | |
11733 | void |
11734 | ha_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 | /*****************************************************************//** |
11756 | Initialize the table FTS stopword list |
11757 | @return TRUE if success */ |
11758 | ibool |
11759 | innobase_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 */ |
11774 | int |
11775 | create_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. |
11841 | If strict_mode=OFF, this will adjust the flags to what should be assumed. |
11842 | @retval true if successful, false if error */ |
11843 | bool |
11844 | create_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)) { |
11903 | index_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. */ |
12077 | static |
12078 | ulint |
12079 | innobase_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 |
12111 | in dictionary. |
12112 | @param[in] thd connection |
12113 | @param[in,out] table target table |
12114 | @param[in] table_share table definition */ |
12115 | void |
12116 | ( |
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. */ |
12243 | void |
12244 | create_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 */ |
12270 | int |
12271 | create_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. */ |
12301 | bool |
12302 | create_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 */ |
12327 | int |
12328 | create_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 */ |
12374 | int |
12375 | create_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 */ |
12550 | int |
12551 | create_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. */ |
12636 | void |
12637 | create_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. */ |
12650 | int |
12651 | ha_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 | |
12715 | cleanup: |
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 | /*****************************************************************//** |
12724 | Discards or imports an InnoDB tablespace. |
12725 | @return 0 == success, -1 == error */ |
12726 | |
12727 | int |
12728 | ha_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 | /*****************************************************************//** |
12844 | Deletes all rows of an InnoDB table. |
12845 | @return error number */ |
12846 | |
12847 | int |
12848 | ha_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 | /*****************************************************************//** |
12900 | Drops a table from an InnoDB database. Before calling this function, |
12901 | MySQL calls innobase_commit to commit the transaction of the current user. |
12902 | Then the current user cannot have locks set on the table. Drop table |
12903 | operation inside InnoDB will remove all locks any user has on the table |
12904 | inside InnoDB. |
12905 | @return error number */ |
12906 | |
12907 | int |
12908 | ha_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 |
13088 | directory in the path is used as the database name. |
13089 | For example, in 'mysql/data/test' the database name is 'test'. */ |
13090 | |
13091 | static |
13092 | void |
13093 | innobase_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 | /*********************************************************************//** |
13157 | Renames an InnoDB table. |
13158 | @return DB_SUCCESS or error code */ |
13159 | inline MY_ATTRIBUTE((warn_unused_result)) |
13160 | dberr_t |
13161 | innobase_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 | /*********************************************************************//** |
13250 | Renames an InnoDB table. |
13251 | @return 0 or error code */ |
13252 | |
13253 | int |
13254 | ha_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 | /*********************************************************************//** |
13324 | Estimates the number of index records in a range. |
13325 | @return estimated number of rows */ |
13326 | |
13327 | ha_rows |
13328 | ha_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 | |
13442 | func_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 | /*********************************************************************//** |
13460 | Gives an UPPER BOUND to the number of rows in a table. This is used in |
13461 | filesort.cc. |
13462 | @return upper bound of rows */ |
13463 | |
13464 | ha_rows |
13465 | ha_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 | /*********************************************************************//** |
13512 | How many seeks it will take to read through the table. This is to be |
13513 | comparable to the number returned by records_in_range so that we can |
13514 | decide if we should scan the table or use keys. |
13515 | @return estimated time measured in disk seeks */ |
13516 | |
13517 | double |
13518 | ha_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 | /******************************************************************//** |
13551 | Calculate the time it takes to read a set of ranges through an index |
13552 | This enables us to optimise reads for clustered indexes. |
13553 | @return estimated time measured in disk seeks */ |
13554 | |
13555 | double |
13556 | ha_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 |
13583 | buffer pool size. |
13584 | @param[in] buf_pool_size given value of buffer pool size.*/ |
13585 | void |
13586 | innodb_set_buf_pool_size(ulonglong buf_pool_size) |
13587 | { |
13588 | innobase_buffer_pool_size = buf_pool_size; |
13589 | } |
13590 | |
13591 | /*********************************************************************//** |
13592 | Calculates the key number used inside MySQL for an Innobase index. We will |
13593 | first check the "index translation table" for a match of the index to get |
13594 | the index number. If there does not exist an "index translation table", |
13595 | or not able to find the index in the translation table, then we will fall back |
13596 | to the traditional way of looping through dict_index_t list to find a |
13597 | match. In this case, we have to take into account if we generated a |
13598 | default clustered index for the table |
13599 | @return the key number used inside MySQL */ |
13600 | static |
13601 | unsigned |
13602 | innobase_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 | /*********************************************************************//** |
13691 | Calculate Record Per Key value. Need to exclude the NULL value if |
13692 | innodb_stats_method is set to "nulls_ignored" |
13693 | @return estimated record per key value */ |
13694 | rec_per_key_t |
13695 | innodb_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 |
13766 | tablespace without running out of space. Start with a space object that has |
13767 | been 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 */ |
13770 | static uintmax_t |
13771 | fsp_get_available_space_in_free_extents(const fil_space_t& space) |
13772 | { |
13773 | ulint = 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 | /*********************************************************************//** |
13808 | Returns statistics information of the table to the MySQL interpreter, |
13809 | in various fields of the handle object. |
13810 | @return HA_ERR_* error code or 0 */ |
13811 | |
13812 | int |
13813 | ha_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 | |
14138 | func_exit: |
14139 | m_prebuilt->trx->op_info = (char*)"" ; |
14140 | |
14141 | DBUG_RETURN(0); |
14142 | } |
14143 | |
14144 | /*********************************************************************//** |
14145 | Returns statistics information of the table to the MySQL interpreter, |
14146 | in various fields of the handle object. |
14147 | @return HA_ERR_* error code or 0 */ |
14148 | |
14149 | int |
14150 | ha_innobase::info( |
14151 | /*==============*/ |
14152 | uint flag) /*!< in: what information is requested */ |
14153 | { |
14154 | return(info_low(flag, false /* not ANALYZE */)); |
14155 | } |
14156 | |
14157 | /* |
14158 | Updates index cardinalities of the table, based on random dives into |
14159 | each index tree. This does NOT calculate exact statistics on the table. |
14160 | @return HA_ADMIN_* error code or HA_ADMIN_OK */ |
14161 | |
14162 | int |
14163 | ha_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 | /*****************************************************************//** |
14179 | Defragment table. |
14180 | @return error number */ |
14181 | UNIV_INTERN |
14182 | int |
14183 | ha_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 | /**********************************************************************//** |
14293 | This is mapped to "ALTER TABLE tablename ENGINE=InnoDB", which rebuilds |
14294 | the table in MySQL. */ |
14295 | |
14296 | int |
14297 | ha_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 | /*******************************************************************//** |
14346 | Tries to check that an InnoDB table is not corrupted. If corruption is |
14347 | noticed, prints to stderr information about it. In case of corruption |
14348 | may also assert a failure and crash the server. |
14349 | @return HA_ADMIN_CORRUPT or HA_ADMIN_OK */ |
14350 | |
14351 | int |
14352 | ha_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 | /*************************************************************//** |
14604 | Adds information about free space in the InnoDB tablespace to a table comment |
14605 | which is printed out when a user calls SHOW TABLE STATUS. Adds also info on |
14606 | foreign keys. |
14607 | @return table comment + InnoDB free space + info on foreign keys */ |
14608 | UNIV_INTERN |
14609 | char* |
14610 | ha_innobase::( |
14611 | /*==============================*/ |
14612 | const char* )/*!< 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 | /*******************************************************************//** |
14668 | Gets 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 |
14670 | CREATE TABLE statement, MUST be freed with |
14671 | ha_innobase::free_foreign_key_create_info */ |
14672 | |
14673 | char* |
14674 | ha_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 | /***********************************************************************//** |
14714 | Maps a InnoDB foreign key constraint to a equivalent MySQL foreign key info. |
14715 | @return pointer to foreign key info */ |
14716 | static |
14717 | FOREIGN_KEY_INFO* |
14718 | get_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 | /*******************************************************************//** |
14850 | Gets the list of foreign keys in this table. |
14851 | @return always 0, that is, always succeeds */ |
14852 | |
14853 | int |
14854 | ha_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 | /*******************************************************************//** |
14888 | Gets the set of foreign keys where this table is the referenced table. |
14889 | @return always 0, that is, always succeeds */ |
14890 | |
14891 | int |
14892 | ha_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 |
14926 | and name. It is used by get_cascade_foreign_key_table_list to store |
14927 | the intermediate result for fetching the table set. */ |
14928 | struct 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 |
14936 | db and tablename. It is used in the ordering of cascade_fk_set. |
14937 | It returns true if the first argument precedes the second argument |
14938 | and false otherwise. */ |
14939 | struct 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. */ |
14957 | static |
14958 | void |
14959 | get_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 |
14985 | the '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. */ |
14990 | int |
14991 | ha_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 | /*****************************************************************//** |
15091 | Checks if ALTER TABLE may change the storage engine of the table. |
15092 | Changing storage engines is not allowed for tables for which there |
15093 | are foreign key constraints (parent or child tables). |
15094 | @return TRUE if can switch engines */ |
15095 | |
15096 | bool |
15097 | ha_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 | /*******************************************************************//** |
15119 | Checks if a table is referenced by a foreign key. The MySQL manual states that |
15120 | a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a |
15121 | delete is then allowed internally to resolve a duplicate key conflict in |
15122 | REPLACE, not an update. |
15123 | @return > 0 if referenced by a FOREIGN KEY */ |
15124 | |
15125 | uint |
15126 | ha_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 | /*******************************************************************//** |
15138 | Frees the foreign key create info for a table stored in InnoDB, if it is |
15139 | non-NULL. */ |
15140 | |
15141 | void |
15142 | ha_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 | /*******************************************************************//** |
15152 | Tells something additional to the handler about how to do things. |
15153 | @return 0 or error number */ |
15154 | |
15155 | int |
15156 | ha_innobase::( |
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 | /** |
15233 | MySQL calls this method at the end of each statement. This method |
15234 | exists for readability only. ha_innobase::reset() doesn't give any |
15235 | clue about the method. */ |
15236 | |
15237 | int |
15238 | ha_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 | /** |
15258 | MySQL calls this method at the end of each statement */ |
15259 | |
15260 | int |
15261 | ha_innobase::reset() |
15262 | { |
15263 | return(end_stmt()); |
15264 | } |
15265 | |
15266 | /******************************************************************//** |
15267 | MySQL calls this function at the start of each SQL statement inside LOCK |
15268 | TABLES. Inside LOCK TABLES the ::external_lock method does not work to |
15269 | mark SQL statement borders. Note also a special case: if a temporary table |
15270 | is created inside LOCK TABLES, MySQL has not called external_lock() at all |
15271 | on that table. |
15272 | MySQL-5.0 also calls this before each statement in an execution of a stored |
15273 | procedure. To make the execution more deterministic for binlogging, MySQL-5.0 |
15274 | locks all tables involved in a stored procedure with full explicit table |
15275 | locks (thd_in_lock_tables(thd) holds in store_lock()) before executing the |
15276 | procedure. |
15277 | @return 0 or error code */ |
15278 | |
15279 | int |
15280 | ha_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 | /******************************************************************//** |
15370 | Maps a MySQL trx isolation level code to the InnoDB isolation level code |
15371 | @return InnoDB isolation level */ |
15372 | static inline |
15373 | ulint |
15374 | innobase_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 | /******************************************************************//** |
15395 | As MySQL will execute an external lock for every new table it uses when it |
15396 | starts to process an SQL statement (an exception is when MySQL calls |
15397 | start_stmt for the handle) we can use this function to store the pointer to |
15398 | the THD in the handle. We will also use this function to communicate |
15399 | to InnoDB that a new SQL statement has started and that we must store a |
15400 | savepoint to our transaction handle, so that we are able to roll back |
15401 | the SQL statement in case of an error. |
15402 | @return 0 */ |
15403 | |
15404 | int |
15405 | ha_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 | /************************************************************************//** |
15645 | Here we export InnoDB status variables to MySQL. */ |
15646 | static |
15647 | void |
15648 | innodb_export_status() |
15649 | /*==================*/ |
15650 | { |
15651 | if (srv_was_started) { |
15652 | srv_export_innodb_status(); |
15653 | } |
15654 | } |
15655 | |
15656 | /************************************************************************//** |
15657 | Implements the SHOW ENGINE INNODB STATUS command. Sends the output of the |
15658 | InnoDB Monitor to the client. |
15659 | @return 0 on success */ |
15660 | static |
15661 | int |
15662 | innodb_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 */ |
15764 | struct 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. |
15896 | The table structure is like so: Engine | Mutex Name | Status |
15897 | We store the metrics in the "Status" column as: |
15898 | |
15899 | spins=N,waits=N,calls=N" |
15900 | |
15901 | The 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. */ |
15905 | bool |
15906 | ShowStatus::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. */ |
15952 | static |
15953 | int |
15954 | innodb_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. */ |
15983 | static |
15984 | int |
15985 | innodb_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. */ |
16084 | static |
16085 | int |
16086 | innodb_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 | /************************************************************************//** |
16101 | Return 0 on success and non-zero on failure. Note: the bool return type |
16102 | seems to be abused here, should be an int. */ |
16103 | static |
16104 | bool |
16105 | innobase_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 | /************************************************************************//** |
16133 | Handling the shared INNOBASE_SHARE structure that is needed to provide table |
16134 | locking. Register the table name if it doesn't exist in the hash table. */ |
16135 | static |
16136 | INNOBASE_SHARE* |
16137 | get_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 | /************************************************************************//** |
16186 | Free the shared object that was registered with get_share(). */ |
16187 | static |
16188 | void |
16189 | free_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 | /*********************************************************************//** |
16230 | Returns number of THR_LOCK locks used for one instance of InnoDB table. |
16231 | InnoDB no longer relies on THR_LOCK locks so 0 value is returned. |
16232 | Instead of THR_LOCK locks InnoDB relies on combination of metadata locks |
16233 | (e.g. for LOCK TABLES and DDL) and its own locking subsystem. |
16234 | Note that even though this method returns 0, SQL-layer still calls |
16235 | ::store_lock(), ::start_stmt() and ::external_lock() methods for InnoDB |
16236 | tables. */ |
16237 | |
16238 | uint |
16239 | ha_innobase::lock_count(void) const |
16240 | /*===============================*/ |
16241 | { |
16242 | return 0; |
16243 | } |
16244 | |
16245 | /*****************************************************************//** |
16246 | Supposed to convert a MySQL table lock stored in the 'lock' field of the |
16247 | handle to a proper type before storing pointer to the lock into an array |
16248 | of pointers. |
16249 | In practice, since InnoDB no longer relies on THR_LOCK locks and its |
16250 | lock_count() method returns 0 it just informs storage engine about type |
16251 | of THR_LOCK which SQL-layer would have acquired for this specific statement |
16252 | on this specific table. |
16253 | MySQL also calls this if it wants to reset some table locks to a not-locked |
16254 | state during the processing of an SQL query. An example is that during a |
16255 | SELECT the read lock is released early on the 'const' tables where we only |
16256 | fetch one row. MySQL does not call this when it releases all locks at the |
16257 | end of an SQL statement. |
16258 | @return pointer to the current element in the 'to' array. */ |
16259 | |
16260 | THR_LOCK_DATA** |
16261 | ha_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 | /*********************************************************************//** |
16498 | Read the next autoinc value. Acquire the relevant locks before reading |
16499 | the AUTOINC value. If SUCCESS then the table AUTOINC mutex will be locked |
16500 | on return and all relevant locks acquired. |
16501 | @return DB_SUCCESS or error code */ |
16502 | |
16503 | dberr_t |
16504 | ha_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 | /*******************************************************************//** |
16528 | This function reads the global auto-inc counter. It doesn't use the |
16529 | AUTOINC lock even if the lock mode is set to TRADITIONAL. |
16530 | @return the autoinc value */ |
16531 | |
16532 | ulonglong |
16533 | ha_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 | /*********************************************************************//** |
16559 | Returns the value of the auto-inc counter in *first_value and ~0 on failure. */ |
16560 | |
16561 | void |
16562 | ha_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 | /*******************************************************************//** |
16728 | See comment in handler.cc */ |
16729 | |
16730 | bool |
16731 | ha_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 |
16750 | duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY. |
16751 | |
16752 | If any of the names is not available, then this method will return |
16753 | false 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 |
16761 | corresponding out parameters. |
16762 | @retval false table and key names were not available, the out parameters |
16763 | were not touched. */ |
16764 | bool |
16765 | ha_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 | /*******************************************************************//** |
16809 | Compares two 'refs'. A 'ref' is the (internal) primary key value of the row. |
16810 | If there is no explicitly declared non-null unique key or a primary key, then |
16811 | InnoDB internally uses the row id as the primary key. |
16812 | @return < 0 if ref1 < ref2, 0 if equal, else > 0 */ |
16813 | |
16814 | int |
16815 | ha_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 | /*******************************************************************//** |
16878 | Ask InnoDB if a query to a table can be cached. |
16879 | @return TRUE if query caching of the table is permitted */ |
16880 | |
16881 | my_bool |
16882 | ha_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 | /******************************************************************//** |
16905 | This function is used to find the storage length in bytes of the first n |
16906 | characters for prefix indexes using a multibyte character set. The function |
16907 | finds charset information and returns length of prefix_len characters in the |
16908 | index field in bytes. |
16909 | @return number of bytes occupied by the first n characters */ |
16910 | ulint |
16911 | innobase_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 | /*******************************************************************//** |
16974 | This function is used to prepare an X/Open XA distributed transaction. |
16975 | @return 0 or error number */ |
16976 | static |
16977 | int |
16978 | innobase_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 | /*******************************************************************//** |
17049 | This function is used to recover X/Open XA distributed transactions. |
17050 | @return number of prepared transactions stored in xid_list */ |
17051 | static |
17052 | int |
17053 | innobase_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 | /*******************************************************************//** |
17070 | This function is used to commit one X/Open XA distributed transaction |
17071 | which is in the prepared state |
17072 | @return 0 or error number */ |
17073 | static |
17074 | int |
17075 | innobase_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 | /*******************************************************************//** |
17101 | This function is used to rollback one X/Open XA distributed transaction |
17102 | which is in the prepared state |
17103 | @return 0 or error number */ |
17104 | static |
17105 | int |
17106 | innobase_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 | |
17130 | bool |
17131 | ha_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 | /****************************************************************//** |
17179 | Update the system variable innodb_io_capacity_max using the "saved" |
17180 | value. This function is registered as a callback with MySQL. */ |
17181 | static |
17182 | void |
17183 | innodb_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 | /****************************************************************//** |
17211 | Update the system variable innodb_io_capacity using the "saved" |
17212 | value. This function is registered as a callback with MySQL. */ |
17213 | static |
17214 | void |
17215 | innodb_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 | /****************************************************************//** |
17243 | Update the system variable innodb_max_dirty_pages_pct using the "saved" |
17244 | value. This function is registered as a callback with MySQL. */ |
17245 | static |
17246 | void |
17247 | innodb_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 | /****************************************************************//** |
17274 | Update the system variable innodb_max_dirty_pages_pct_lwm using the |
17275 | "saved" value. This function is registered as a callback with MySQL. */ |
17276 | static |
17277 | void |
17278 | innodb_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 | /*************************************************************//** |
17304 | Don't allow to set innodb_fast_shutdown=0 if purge threads are |
17305 | already down. |
17306 | @return 0 if innodb_fast_shutdown can be set */ |
17307 | static |
17308 | int |
17309 | fast_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 | /*************************************************************//** |
17335 | Check whether valid argument given to innobase_*_stopword_table. |
17336 | This function is registered as a callback with MySQL. |
17337 | @return 0 for valid stopword table */ |
17338 | static |
17339 | int |
17340 | innodb_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" |
17377 | value. This function is registered as a callback with MySQL. |
17378 | @param[in] save immediate result from check function */ |
17379 | static |
17380 | void |
17381 | innodb_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 | /*************************************************************//** |
17396 | Check whether valid argument given to "innodb_fts_internal_tbl_name" |
17397 | This function is registered as a callback with MySQL. |
17398 | @return 0 for valid stopword table */ |
17399 | static |
17400 | int |
17401 | innodb_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 | /****************************************************************//** |
17446 | Update global variable "fts_internal_tbl_name" with the "saved" |
17447 | stopword table name value. This function is registered as a callback |
17448 | with MySQL. */ |
17449 | static |
17450 | void |
17451 | innodb_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 | /****************************************************************//** |
17480 | Update the system variable innodb_adaptive_hash_index using the "saved" |
17481 | value. This function is registered as a callback with MySQL. */ |
17482 | static |
17483 | void |
17484 | innodb_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 | /****************************************************************//** |
17496 | Update the system variable innodb_cmp_per_index using the "saved" |
17497 | value. This function is registered as a callback with MySQL. */ |
17498 | static |
17499 | void |
17500 | innodb_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 | /****************************************************************//** |
17512 | Update the system variable innodb_old_blocks_pct using the "saved" |
17513 | value. This function is registered as a callback with MySQL. */ |
17514 | static |
17515 | void |
17516 | innodb_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 | /****************************************************************//** |
17524 | Update the system variable innodb_old_blocks_pct using the "saved" |
17525 | value. This function is registered as a callback with MySQL. */ |
17526 | static |
17527 | void |
17528 | innodb_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 |
17537 | static ulong srv_fil_make_page_dirty_debug = 0; |
17538 | static ulong srv_saved_page_number_debug = 0; |
17539 | |
17540 | /****************************************************************//** |
17541 | Save an InnoDB page number. */ |
17542 | static |
17543 | void |
17544 | innodb_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 | /****************************************************************//** |
17553 | Make the first page of given user tablespace dirty. */ |
17554 | static |
17555 | void |
17556 | innodb_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 | /*************************************************************//** |
17593 | Just emit a warning that the usage of the variable is deprecated. |
17594 | @return 0 */ |
17595 | static |
17596 | void |
17597 | innodb_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 | /****************************************************************//** |
17620 | Update the monitor counter according to the "set_option", turn |
17621 | on/off or reset specified monitor counter. */ |
17622 | static |
17623 | void |
17624 | innodb_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 | /****************************************************************//** |
17694 | Find matching InnoDB monitor counters and update their status |
17695 | according to the "set_option", turn on/off or reset specified |
17696 | monitor counter. */ |
17697 | static |
17698 | void |
17699 | innodb_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 | /*************************************************************//** |
17756 | Given a configuration variable name, find corresponding monitor counter |
17757 | and return its monitor ID if found. |
17758 | @return monitor ID if found, MONITOR_NO_MATCH if there is no match */ |
17759 | static |
17760 | ulint |
17761 | innodb_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 | /*************************************************************//** |
17787 | Validate that the passed in monitor name matches at least one |
17788 | monitor counter name with wildcard compare. |
17789 | @return TRUE if at least one monitor name matches */ |
17790 | static |
17791 | ibool |
17792 | innodb_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 | /*************************************************************//** |
17806 | Validate the passed in monitor name, find and save the |
17807 | corresponding monitor name in the function parameter "save". |
17808 | @return 0 if monitor name is valid */ |
17809 | static |
17810 | int |
17811 | innodb_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 | /*************************************************************//** |
17869 | Validate passed-in "value" is a valid monitor counter name. |
17870 | This function is registered as a callback with MySQL. |
17871 | @return 0 for valid name */ |
17872 | static |
17873 | int |
17874 | innodb_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 | /****************************************************************//** |
17919 | Update the system variable innodb_enable(disable/reset/reset_all)_monitor |
17920 | according to the "set_option" and turn on/off or reset specified monitor |
17921 | counter. */ |
17922 | static |
17923 | void |
17924 | innodb_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 | } |
18011 | exit: |
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 | /*************************************************************//** |
18029 | Validate if passed-in "value" is a valid value for |
18030 | innodb_buffer_pool_filename. On Windows, file names with colon (:) |
18031 | are not allowed. |
18032 | |
18033 | @return 0 for valid name */ |
18034 | static |
18035 | int |
18036 | innodb_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 |
18072 | static char* srv_buffer_pool_evict; |
18073 | |
18074 | /****************************************************************//** |
18075 | Evict all uncompressed pages of compressed tables from the buffer pool. |
18076 | Keep the compressed pages in the buffer pool. |
18077 | @return whether all uncompressed pages were evicted */ |
18078 | static MY_ATTRIBUTE((warn_unused_result)) |
18079 | bool |
18080 | innodb_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 | /****************************************************************//** |
18114 | Called on SET GLOBAL innodb_buffer_pool_evict=... |
18115 | Handles some values specially, to evict pages from the buffer pool. |
18116 | SET GLOBAL innodb_buffer_pool_evict='uncompressed' |
18117 | evicts all uncompressed page frames of compressed tablespaces. */ |
18118 | static |
18119 | void |
18120 | innodb_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 | /****************************************************************//** |
18141 | Update the system variable innodb_monitor_enable and enable |
18142 | specified monitor counter. |
18143 | This function is registered as a callback with MySQL. */ |
18144 | static |
18145 | void |
18146 | innodb_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 | /****************************************************************//** |
18159 | Update the system variable innodb_monitor_disable and turn |
18160 | off specified monitor counter. */ |
18161 | static |
18162 | void |
18163 | innodb_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 | /****************************************************************//** |
18176 | Update the system variable innodb_monitor_reset and reset |
18177 | specified monitor counter(s). |
18178 | This function is registered as a callback with MySQL. */ |
18179 | static |
18180 | void |
18181 | innodb_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 | /****************************************************************//** |
18194 | Update the system variable innodb_monitor_reset_all and reset |
18195 | all value related monitor counter. |
18196 | This function is registered as a callback with MySQL. */ |
18197 | static |
18198 | void |
18199 | innodb_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 | |
18212 | static |
18213 | void |
18214 | innodb_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 | |
18222 | static 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 | /****************************************************************//** |
18232 | Parse and enable InnoDB monitor counters during server startup. |
18233 | User can list the monitor counters/groups to be enable by specifying |
18234 | "loose-innodb_monitor_enable=monitor_name1;monitor_name2..." |
18235 | in server configuration file or at the command line. The string |
18236 | separate could be ";", "," or empty space. */ |
18237 | static |
18238 | void |
18239 | innodb_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 | /****************************************************************//** |
18267 | Callback function for accessing the InnoDB variables from MySQL: |
18268 | SHOW VARIABLES. */ |
18269 | static 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 | /****************************************************************//** |
18280 | This function checks each index name for a table against reserved |
18281 | system default primary index name 'GEN_CLUST_INDEX'. If a name |
18282 | matches, this function pushes an warning message to the client, |
18283 | and returns true. |
18284 | @return true if the index name matches the reserved name */ |
18285 | bool |
18286 | innobase_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 |
18322 | of m_prebuilt->fts_doc_id |
18323 | @param[in,out] fts_hdl FTS handler |
18324 | @return the relevance ranking value */ |
18325 | static |
18326 | float |
18327 | innobase_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 */ |
18345 | static |
18346 | void |
18347 | innobase_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 |
18360 | of m_prebuilt->fts_doc_id |
18361 | @param[in,out] fts_hdl FTS handler |
18362 | @return the relevance ranking value */ |
18363 | static |
18364 | float |
18365 | innobase_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 |
18379 | static my_bool innodb_background_drop_list_empty = TRUE; |
18380 | static my_bool innodb_log_checkpoint_now = TRUE; |
18381 | static my_bool innodb_buf_flush_list_now = TRUE; |
18382 | static uint innodb_merge_threshold_set_all_debug |
18383 | = DICT_INDEX_MERGE_THRESHOLD_DEFAULT; |
18384 | |
18385 | /** Wait for the background drop list to become empty. */ |
18386 | static |
18387 | void |
18388 | wait_background_drop_list_empty(THD*, st_mysql_sys_var*, void*, const void*) |
18389 | { |
18390 | row_wait_for_background_drop_list_empty(); |
18391 | } |
18392 | |
18393 | /****************************************************************//** |
18394 | Force innodb to checkpoint. */ |
18395 | static |
18396 | void |
18397 | checkpoint_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 | /****************************************************************//** |
18418 | Force a dirty pages flush now. */ |
18419 | static |
18420 | void |
18421 | buf_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 |
18429 | now. |
18430 | @param[in] save immediate result from check function */ |
18431 | static |
18432 | void |
18433 | innodb_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 */ |
18446 | static |
18447 | ulonglong |
18448 | innobase_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 |
18469 | dummies that are needed by the MySQL infrastructure to call |
18470 | buffer_pool_dump_now(), buffer_pool_load_now() and buffer_pool_load_abort() |
18471 | by 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; |
18475 | Their values are read by MySQL and displayed to the user when the variables |
18476 | are queried, e.g.: |
18477 | SELECT @@innodb_buffer_pool_dump_now; |
18478 | SELECT @@innodb_buffer_pool_load_now; |
18479 | SELECT @@innodb_buffer_pool_load_abort; */ |
18480 | static my_bool innodb_buffer_pool_dump_now = FALSE; |
18481 | static my_bool innodb_buffer_pool_load_now = FALSE; |
18482 | static my_bool innodb_buffer_pool_load_abort = FALSE; |
18483 | |
18484 | /****************************************************************//** |
18485 | Trigger a dump of the buffer pool if innodb_buffer_pool_dump_now is set |
18486 | to ON. This function is registered as a callback with MySQL. */ |
18487 | static |
18488 | void |
18489 | buffer_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 | /****************************************************************//** |
18508 | Trigger a load of the buffer pool if innodb_buffer_pool_load_now is set |
18509 | to ON. This function is registered as a callback with MySQL. */ |
18510 | static |
18511 | void |
18512 | buffer_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 | /****************************************************************//** |
18531 | Abort a load of the buffer pool if innodb_buffer_pool_load_abort |
18532 | is set to ON. This function is registered as a callback with MySQL. */ |
18533 | static |
18534 | void |
18535 | buffer_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 | /****************************************************************//** |
18554 | Update the system variable innodb_log_write_ahead_size using the "saved" |
18555 | value. This function is registered as a callback with MySQL. */ |
18556 | static |
18557 | void |
18558 | innodb_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, |
18599 | which control InnoDB "status monitor" output to the error log. |
18600 | @param[out] var_ptr current value |
18601 | @param[in] save to-be-assigned value */ |
18602 | static |
18603 | void |
18604 | innodb_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 | /****************************************************************** |
18613 | Update the system variable innodb_encryption_threads */ |
18614 | static |
18615 | void |
18616 | innodb_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 | /****************************************************************** |
18623 | Update the system variable innodb_encryption_rotate_key_age */ |
18624 | static |
18625 | void |
18626 | innodb_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 | /****************************************************************** |
18633 | Update the system variable innodb_encryption_rotation_iops */ |
18634 | static |
18635 | void |
18636 | innodb_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 | /****************************************************************** |
18643 | Update the system variable innodb_encrypt_tables*/ |
18644 | static |
18645 | void |
18646 | innodb_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 */ |
18655 | static |
18656 | void |
18657 | innodb_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 | |
18664 | static SHOW_VAR innodb_status_variables_export[]= { |
18665 | {"Innodb" , (char*) &show_innodb_vars, SHOW_FUNC}, |
18666 | {NullS, NullS, SHOW_LONG} |
18667 | }; |
18668 | |
18669 | static struct st_mysql_storage_engine innobase_storage_engine= |
18670 | { MYSQL_HANDLERTON_INTERFACE_VERSION }; |
18671 | |
18672 | #ifdef WITH_WSREP |
18673 | void |
18674 | wsrep_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 | /*******************************************************************//** |
18689 | This function is used to kill one transaction in BF. */ |
18690 | UNIV_INTERN |
18691 | int |
18692 | wsrep_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 | |
18909 | static |
18910 | int |
18911 | wsrep_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 | |
18946 | static |
18947 | int |
18948 | innobase_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 | |
18965 | static |
18966 | int |
18967 | innobase_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 | |
18977 | static 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 | |
18989 | static 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 | |
19013 | static 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 | |
19018 | static 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 | |
19026 | static 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 | |
19031 | static 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 | |
19037 | static 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 | |
19047 | static 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 | |
19053 | static 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 | |
19058 | static 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 | |
19065 | static 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 |
19073 | static 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 | |
19079 | static 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 | |
19084 | static 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 | |
19089 | static 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 | |
19098 | static 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 | |
19106 | static 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 | |
19114 | static 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 | |
19122 | static 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 | |
19128 | static 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 | |
19133 | static 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 | |
19140 | static 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 | |
19145 | static 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 | |
19159 | static 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 | |
19165 | static 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 | |
19170 | static 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 | |
19177 | static 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 */ |
19183 | static |
19184 | void |
19185 | innodb_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 | |
19190 | static 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 | |
19196 | static 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 | |
19201 | static 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 | |
19207 | static 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 | |
19213 | static 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 | |
19218 | static 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 | |
19223 | static 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 | |
19229 | static 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 | |
19234 | static 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 | |
19242 | static 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 | |
19247 | static 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 | |
19252 | static 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 | |
19258 | static 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 | |
19263 | static 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 | |
19270 | static 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 | |
19276 | static 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 | |
19284 | static 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 | |
19291 | static 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 | |
19296 | static 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 |
19302 | static 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. |
19309 | Each partition is protected by its own latch and so we have parts number |
19310 | of latches protecting complete search system. */ |
19311 | static 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 | |
19317 | static 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 | |
19323 | static 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 | |
19329 | static 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 | |
19338 | static 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 |
19345 | memory 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 | */ |
19352 | static |
19353 | int |
19354 | innodb_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 |
19361 | BUF_POOL_SIZE_THRESHOLD (srv/srv0start.cc), then srv_buf_pool_instances_default |
19362 | can be removed and 8 used instead. The problem with the current setup is that |
19363 | with 128MiB default buffer pool size and 8 instances by default we would emit |
19364 | a warning when no options are specified. */ |
19365 | static 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 | |
19374 | static 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 |
19383 | static 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 | |
19388 | static 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 | |
19394 | static 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 | |
19406 | static 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 | |
19411 | static 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 | |
19416 | static 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 | |
19421 | static 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 | |
19426 | static 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. */ |
19433 | static 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 | |
19438 | static 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 | |
19444 | static 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 | |
19449 | static 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 */ |
19455 | static 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 | |
19460 | static 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 | |
19468 | static 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 | |
19474 | static 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 | |
19482 | static 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 | |
19492 | static 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 | |
19502 | static 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 | |
19514 | static 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 | |
19519 | static 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 | |
19527 | static 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 | |
19532 | static 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 | |
19537 | static 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 | |
19544 | static 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 | |
19549 | static 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 | |
19554 | static 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 | |
19559 | static 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 | |
19565 | static 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 | |
19570 | static 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 | |
19575 | static 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 | |
19580 | static 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 | |
19585 | static 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 | |
19590 | static 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 | |
19595 | static 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 | |
19600 | static 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 | |
19605 | static 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 | |
19610 | static 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 | |
19615 | static 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 | |
19620 | static 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 | |
19625 | static 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 | |
19630 | static 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 | |
19636 | static 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 | |
19641 | static 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, |
19646 | but fil_space_t is being used for the redo log, and it uses data pages. */ |
19647 | |
19648 | static 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 | |
19653 | static 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 | |
19660 | static 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 | |
19665 | static 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 | |
19672 | static 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 | |
19677 | static 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 | |
19682 | static 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 | |
19687 | static 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 | |
19692 | static 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 | |
19701 | static 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 | |
19707 | static 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 | |
19716 | static 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 | |
19721 | static 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 | |
19726 | static 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 | |
19731 | static 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 | |
19739 | static 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 | |
19747 | static 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 | |
19754 | static 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 | |
19761 | static 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. */ |
19767 | static 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 | |
19775 | static 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 | |
19786 | static MYSQL_SYSVAR_STR(version, innodb_version_str, |
19787 | PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY, |
19788 | "InnoDB version" , NULL, NULL, INNODB_VERSION_STR); |
19789 | |
19790 | static 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 |
19796 | static 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 | |
19802 | static 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 | |
19807 | static 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 | |
19815 | static 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 |
19823 | static 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 | |
19828 | static 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 | |
19835 | static 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. */ |
19849 | static my_bool innobase_disallow_writes = FALSE; |
19850 | |
19851 | /************************************************************************** |
19852 | An "update" method for innobase_disallow_writes variable. */ |
19853 | static |
19854 | void |
19855 | innobase_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 | |
19867 | static 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 | |
19873 | static 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 | |
19878 | static 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 | |
19884 | static 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 | |
19890 | static 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 | |
19896 | static 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 | |
19902 | static 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 | |
19908 | static 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 | |
19912 | static 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 | |
19917 | static 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 | |
19922 | static 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 | |
19929 | static 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 | |
19935 | static 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 | |
19940 | static 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 | |
19946 | static 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 |
19955 | static 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 | |
19960 | static 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 | |
19965 | static 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 | |
19972 | static 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 | |
19978 | static 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 | |
19983 | static 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 | |
19988 | static 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 | |
19993 | static 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 | |
19999 | static 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 | |
20004 | static 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 | |
20010 | static 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 | |
20016 | static 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 | |
20022 | static 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 | |
20028 | static const char *page_compression_algorithms[]= { "none" , "zlib" , "lz4" , "lzo" , "lzma" , "bzip2" , "snappy" , 0 }; |
20029 | static TYPELIB page_compression_algorithms_typelib= |
20030 | { |
20031 | array_elements(page_compression_algorithms) - 1, 0, |
20032 | page_compression_algorithms, 0 |
20033 | }; |
20034 | static 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 | |
20044 | static 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 | |
20053 | static const char* srv_encrypt_tables_names[] = { "OFF" , "ON" , "FORCE" , 0 }; |
20054 | static TYPELIB srv_encrypt_tables_typelib = { |
20055 | array_elements(srv_encrypt_tables_names)-1, 0, srv_encrypt_tables_names, |
20056 | NULL |
20057 | }; |
20058 | static 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 | |
20067 | static 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 | |
20075 | static 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 | |
20086 | static 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 | |
20093 | static 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 | |
20098 | static 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 | |
20106 | static 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 | |
20111 | static 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 | |
20117 | static 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 | |
20124 | static 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 | |
20131 | static 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 | |
20142 | static 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 |
20153 | static 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 | |
20160 | static 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 | |
20367 | maria_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 | }, |
20383 | i_s_innodb_trx, |
20384 | i_s_innodb_locks, |
20385 | i_s_innodb_lock_waits, |
20386 | i_s_innodb_cmp, |
20387 | i_s_innodb_cmp_reset, |
20388 | i_s_innodb_cmpmem, |
20389 | i_s_innodb_cmpmem_reset, |
20390 | i_s_innodb_cmp_per_index, |
20391 | i_s_innodb_cmp_per_index_reset, |
20392 | i_s_innodb_buffer_page, |
20393 | i_s_innodb_buffer_page_lru, |
20394 | i_s_innodb_buffer_stats, |
20395 | i_s_innodb_metrics, |
20396 | i_s_innodb_ft_default_stopword, |
20397 | i_s_innodb_ft_deleted, |
20398 | i_s_innodb_ft_being_deleted, |
20399 | i_s_innodb_ft_config, |
20400 | i_s_innodb_ft_index_cache, |
20401 | i_s_innodb_ft_index_table, |
20402 | i_s_innodb_sys_tables, |
20403 | i_s_innodb_sys_tablestats, |
20404 | i_s_innodb_sys_indexes, |
20405 | i_s_innodb_sys_columns, |
20406 | i_s_innodb_sys_fields, |
20407 | i_s_innodb_sys_foreign, |
20408 | i_s_innodb_sys_foreign_cols, |
20409 | i_s_innodb_sys_tablespaces, |
20410 | i_s_innodb_sys_datafiles, |
20411 | i_s_innodb_sys_virtual, |
20412 | i_s_innodb_mutexes, |
20413 | i_s_innodb_sys_semaphore_waits, |
20414 | i_s_innodb_tablespaces_encryption, |
20415 | i_s_innodb_tablespaces_scrubbing |
20416 | maria_declare_plugin_end; |
20417 | |
20418 | /** @brief Initialize the default value of innodb_commit_concurrency. |
20419 | |
20420 | Once InnoDB is running, the innodb_commit_concurrency must not change |
20421 | from zero to nonzero. (Bug #42101) |
20422 | |
20423 | The initial default value is 0, and without this extra initialization, |
20424 | SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter |
20425 | to 0, even if it was initially set to nonzero at the command line |
20426 | or configuration file. */ |
20427 | static |
20428 | void |
20429 | innobase_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 |
20437 | or innodb_page_size. */ |
20438 | static |
20439 | void |
20440 | innodb_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 | /** |
20462 | Multi Range Read interface, DS-MRR calls */ |
20463 | int |
20464 | ha_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 | |
20475 | int |
20476 | ha_innobase::multi_range_read_next( |
20477 | range_id_t* range_info) |
20478 | { |
20479 | return(m_ds_mrr.dsmrr_next(range_info)); |
20480 | } |
20481 | |
20482 | ha_rows |
20483 | ha_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 | |
20504 | ha_rows |
20505 | ha_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 | |
20520 | int |
20521 | ha_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 | /** |
20530 | Index Condition Pushdown interface implementation */ |
20531 | |
20532 | /*************************************************************//** |
20533 | InnoDB index push-down condition check |
20534 | @return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */ |
20535 | ICP_RESULT |
20536 | innobase_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 */ |
20548 | static TABLE * |
20549 | innobase_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 */ |
20608 | void |
20609 | innobase_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 |
20632 | dbname and tbname to be renamed. */ |
20633 | void |
20634 | innobase_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 |
20669 | given 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 */ |
20674 | static |
20675 | dfield_t* |
20676 | innobase_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 |
20721 | to store the value in passed in "my_rec" */ |
20722 | dfield_t* |
20723 | innobase_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 | |
20895 | class Item* |
20896 | ha_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 | /******************************************************************//** |
20918 | Use this when the args are passed to the format string from |
20919 | errmsg-utf8.txt directly as is. |
20920 | |
20921 | Push a warning message to the client, it is a wrapper around: |
20922 | |
20923 | void push_warning_printf( |
20924 | THD *thd, Sql_condition::enum_condition_level level, |
20925 | uint code, const char *format, ...); |
20926 | */ |
20927 | void |
20928 | ib_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 | /******************************************************************//** |
20982 | Use this when the args are first converted to a formatted string and then |
20983 | passed to the format string from errmsg-utf8.txt. The error message format |
20984 | must be: "Some string ... %s". |
20985 | |
20986 | Push a warning message to the client, it is a wrapper around: |
20987 | |
20988 | void push_warning_printf( |
20989 | THD *thd, Sql_condition::enum_condition_level level, |
20990 | uint code, const char *format, ...); |
20991 | */ |
20992 | void |
20993 | ib_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 |
21046 | as an offset from this.*/ |
21047 | const char* TROUBLESHOOTING_MSG = |
21048 | "Please refer to " REFMAN "innodb-troubleshooting.html" |
21049 | " for how to resolve the issue." ; |
21050 | |
21051 | const char* TROUBLESHOOT_DATADICT_MSG = |
21052 | "Please refer to " REFMAN "innodb-troubleshooting-datadict.html" |
21053 | " for how to resolve the issue." ; |
21054 | |
21055 | const char* BUG_REPORT_MSG = |
21056 | "Submit a detailed bug report to https://jira.mariadb.org/" ; |
21057 | |
21058 | const 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 | |
21063 | const char* ERROR_CREATING_MSG = |
21064 | "Please refer to " REFMAN "error-creating-innodb.html" ; |
21065 | |
21066 | const 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 | |
21070 | const char* FOREIGN_KEY_CONSTRAINTS_MSG = |
21071 | "Please refer to https://mariadb.com/kb/en/library/foreign-keys/" |
21072 | " for correct foreign key definition." ; |
21073 | |
21074 | const char* SET_TRANSACTION_MSG = |
21075 | "Please refer to https://mariadb.com/kb/en/library/set-transaction/" ; |
21076 | |
21077 | const char* INNODB_PARAMETERS_MSG = |
21078 | "Please refer to https://mariadb.com/kb/en/library/xtradbinnodb-server-system-variables/" ; |
21079 | |
21080 | /********************************************************************** |
21081 | Converts an identifier from my_charset_filename to UTF-8 charset. |
21082 | @return result string length, as returned by strconvert() */ |
21083 | uint |
21084 | innobase_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 | /********************************************************************** |
21100 | Converts an identifier from my_charset_filename to UTF-8 charset. |
21101 | @return result string length, as returned by strconvert() */ |
21102 | uint |
21103 | innobase_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 | /********************************************************************** |
21119 | Issue a warning that the row is too big. */ |
21120 | void |
21121 | ib_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 |
21144 | memory 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 | */ |
21150 | static |
21151 | int |
21152 | innodb_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 | /*************************************************************//** |
21243 | Check for a valid value of innobase_compression_algorithm. |
21244 | @return 0 for valid innodb_compression_algorithm. */ |
21245 | static |
21246 | int |
21247 | innodb_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 | |
21323 | static |
21324 | int |
21325 | innodb_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 | |
21362 | static 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 | /********************************************************************//** |
21373 | Helper function to push warnings from InnoDB internals to SQL-layer. */ |
21374 | UNIV_INTERN |
21375 | void |
21376 | ib_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 | /********************************************************************//** |
21401 | Helper function to push warnings from InnoDB internals to SQL-layer. */ |
21402 | UNIV_INTERN |
21403 | void |
21404 | ib_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 | /********************************************************************//** |
21433 | Helper function to push frm mismatch error to error log and |
21434 | if needed to sql-layer. */ |
21435 | UNIV_INTERN |
21436 | void |
21437 | ib_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 | |