1/*****************************************************************************
2
3Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2015, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file row/row0mysql.cc
22Interface between Innobase row operations and MySQL.
23Contains also create table and other data dictionary operations.
24
25Created 9/17/2000 Heikki Tuuri
26*******************************************************/
27
28#include "ha_prototypes.h"
29#include <debug_sync.h>
30#include <gstream.h>
31#include <spatial.h>
32
33#include "row0mysql.h"
34#include "btr0sea.h"
35#include "dict0boot.h"
36#include "dict0crea.h"
37#include <sql_const.h>
38#include "dict0dict.h"
39#include "dict0load.h"
40#include "dict0stats.h"
41#include "dict0stats_bg.h"
42#include "dict0defrag_bg.h"
43#include "btr0defragment.h"
44#include "fil0fil.h"
45#include "fil0crypt.h"
46#include "fsp0file.h"
47#include "fsp0sysspace.h"
48#include "fts0fts.h"
49#include "fts0types.h"
50#include "ibuf0ibuf.h"
51#include "lock0lock.h"
52#include "log0log.h"
53#include "pars0pars.h"
54#include "que0que.h"
55#include "rem0cmp.h"
56#include "row0import.h"
57#include "row0ins.h"
58#include "row0merge.h"
59#include "row0row.h"
60#include "row0sel.h"
61#include "row0upd.h"
62#include "trx0purge.h"
63#include "trx0rec.h"
64#include "trx0roll.h"
65#include "trx0undo.h"
66#include "row0ext.h"
67#include "srv0start.h"
68#include "ut0new.h"
69
70#include <algorithm>
71#include <deque>
72#include <vector>
73
74/** Provide optional 4.x backwards compatibility for 5.0 and above */
75ibool row_rollback_on_timeout = FALSE;
76
77/** Chain node of the list of tables to drop in the background. */
78struct row_mysql_drop_t{
79 table_id_t table_id; /*!< table id */
80 UT_LIST_NODE_T(row_mysql_drop_t)row_mysql_drop_list;
81 /*!< list chain node */
82};
83
84/** @brief List of tables we should drop in background.
85
86ALTER TABLE in MySQL requires that the table handler can drop the
87table in background when there are no queries to it any
88more. Protected by row_drop_list_mutex. */
89static UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
90
91/** Mutex protecting the background table drop list. */
92static ib_mutex_t row_drop_list_mutex;
93
94/** Flag: has row_mysql_drop_list been initialized? */
95static ibool row_mysql_drop_list_inited = FALSE;
96
97/*******************************************************************//**
98Determine if the given name is a name reserved for MySQL system tables.
99@return TRUE if name is a MySQL system table name */
100static
101ibool
102row_mysql_is_system_table(
103/*======================*/
104 const char* name)
105{
106 if (strncmp(name, "mysql/", 6) != 0) {
107
108 return(FALSE);
109 }
110
111 return(0 == strcmp(name + 6, "host")
112 || 0 == strcmp(name + 6, "user")
113 || 0 == strcmp(name + 6, "db"));
114}
115
116#ifdef UNIV_DEBUG
117/** Wait for the background drop list to become empty. */
118void
119row_wait_for_background_drop_list_empty()
120{
121 bool empty = false;
122 while (!empty) {
123 mutex_enter(&row_drop_list_mutex);
124 empty = (UT_LIST_GET_LEN(row_mysql_drop_list) == 0);
125 mutex_exit(&row_drop_list_mutex);
126 os_thread_sleep(100000);
127 }
128}
129#endif /* UNIV_DEBUG */
130
131/*******************************************************************//**
132Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */
133static
134void
135row_mysql_delay_if_needed(void)
136/*===========================*/
137{
138 if (srv_dml_needed_delay) {
139 os_thread_sleep(srv_dml_needed_delay);
140 }
141}
142
143/*******************************************************************//**
144Frees the blob heap in prebuilt when no longer needed. */
145void
146row_mysql_prebuilt_free_blob_heap(
147/*==============================*/
148 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct of a
149 ha_innobase:: table handle */
150{
151 DBUG_ENTER("row_mysql_prebuilt_free_blob_heap");
152
153 DBUG_PRINT("row_mysql_prebuilt_free_blob_heap",
154 ("blob_heap freeing: %p", prebuilt->blob_heap));
155
156 mem_heap_free(prebuilt->blob_heap);
157 prebuilt->blob_heap = NULL;
158 DBUG_VOID_RETURN;
159}
160
161/*******************************************************************//**
162Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row
163format.
164@return pointer to the data, we skip the 1 or 2 bytes at the start
165that are used to store the len */
166byte*
167row_mysql_store_true_var_len(
168/*=========================*/
169 byte* dest, /*!< in: where to store */
170 ulint len, /*!< in: length, must fit in two bytes */
171 ulint lenlen) /*!< in: storage length of len: either 1 or 2 bytes */
172{
173 if (lenlen == 2) {
174 ut_a(len < 256 * 256);
175
176 mach_write_to_2_little_endian(dest, len);
177
178 return(dest + 2);
179 }
180
181 ut_a(lenlen == 1);
182 ut_a(len < 256);
183
184 mach_write_to_1(dest, len);
185
186 return(dest + 1);
187}
188
189/*******************************************************************//**
190Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and
191returns a pointer to the data.
192@return pointer to the data, we skip the 1 or 2 bytes at the start
193that are used to store the len */
194const byte*
195row_mysql_read_true_varchar(
196/*========================*/
197 ulint* len, /*!< out: variable-length field length */
198 const byte* field, /*!< in: field in the MySQL format */
199 ulint lenlen) /*!< in: storage length of len: either 1
200 or 2 bytes */
201{
202 if (lenlen == 2) {
203 *len = mach_read_from_2_little_endian(field);
204
205 return(field + 2);
206 }
207
208 ut_a(lenlen == 1);
209
210 *len = mach_read_from_1(field);
211
212 return(field + 1);
213}
214
215/*******************************************************************//**
216Stores a reference to a BLOB in the MySQL format. */
217void
218row_mysql_store_blob_ref(
219/*=====================*/
220 byte* dest, /*!< in: where to store */
221 ulint col_len,/*!< in: dest buffer size: determines into
222 how many bytes the BLOB length is stored,
223 the space for the length may vary from 1
224 to 4 bytes */
225 const void* data, /*!< in: BLOB data; if the value to store
226 is SQL NULL this should be NULL pointer */
227 ulint len) /*!< in: BLOB length; if the value to store
228 is SQL NULL this should be 0; remember
229 also to set the NULL bit in the MySQL record
230 header! */
231{
232 /* MySQL might assume the field is set to zero except the length and
233 the pointer fields */
234
235 memset(dest, '\0', col_len);
236
237 /* In dest there are 1 - 4 bytes reserved for the BLOB length,
238 and after that 8 bytes reserved for the pointer to the data.
239 In 32-bit architectures we only use the first 4 bytes of the pointer
240 slot. */
241
242 ut_a(col_len - 8 > 1 || len < 256);
243 ut_a(col_len - 8 > 2 || len < 256 * 256);
244 ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
245
246 mach_write_to_n_little_endian(dest, col_len - 8, len);
247
248 memcpy(dest + col_len - 8, &data, sizeof data);
249}
250
251/*******************************************************************//**
252Reads a reference to a BLOB in the MySQL format.
253@return pointer to BLOB data */
254const byte*
255row_mysql_read_blob_ref(
256/*====================*/
257 ulint* len, /*!< out: BLOB length */
258 const byte* ref, /*!< in: BLOB reference in the
259 MySQL format */
260 ulint col_len) /*!< in: BLOB reference length
261 (not BLOB length) */
262{
263 byte* data;
264
265 *len = mach_read_from_n_little_endian(ref, col_len - 8);
266
267 memcpy(&data, ref + col_len - 8, sizeof data);
268
269 return(data);
270}
271
272/*******************************************************************//**
273Converting InnoDB geometry data format to MySQL data format. */
274void
275row_mysql_store_geometry(
276/*=====================*/
277 byte* dest, /*!< in/out: where to store */
278 ulint dest_len, /*!< in: dest buffer size: determines
279 into how many bytes the GEOMETRY length
280 is stored, the space for the length
281 may vary from 1 to 4 bytes */
282 const byte* src, /*!< in: GEOMETRY data; if the value to
283 store is SQL NULL this should be NULL
284 pointer */
285 ulint src_len) /*!< in: GEOMETRY length; if the value
286 to store is SQL NULL this should be 0;
287 remember also to set the NULL bit in
288 the MySQL record header! */
289{
290 /* MySQL might assume the field is set to zero except the length and
291 the pointer fields */
292 UNIV_MEM_ASSERT_RW(src, src_len);
293 UNIV_MEM_ASSERT_W(dest, dest_len);
294 UNIV_MEM_INVALID(dest, dest_len);
295
296 memset(dest, '\0', dest_len);
297
298 /* In dest there are 1 - 4 bytes reserved for the BLOB length,
299 and after that 8 bytes reserved for the pointer to the data.
300 In 32-bit architectures we only use the first 4 bytes of the pointer
301 slot. */
302
303 ut_ad(dest_len - 8 > 1 || src_len < 1<<8);
304 ut_ad(dest_len - 8 > 2 || src_len < 1<<16);
305 ut_ad(dest_len - 8 > 3 || src_len < 1<<24);
306
307 mach_write_to_n_little_endian(dest, dest_len - 8, src_len);
308
309 memcpy(dest + dest_len - 8, &src, sizeof src);
310}
311
312/*******************************************************************//**
313Read geometry data in the MySQL format.
314@return pointer to geometry data */
315static
316const byte*
317row_mysql_read_geometry(
318/*====================*/
319 ulint* len, /*!< out: data length */
320 const byte* ref, /*!< in: geometry data in the
321 MySQL format */
322 ulint col_len) /*!< in: MySQL format length */
323{
324 byte* data;
325
326 *len = mach_read_from_n_little_endian(ref, col_len - 8);
327
328 memcpy(&data, ref + col_len - 8, sizeof data);
329
330 return(data);
331}
332
333/**************************************************************//**
334Pad a column with spaces. */
335void
336row_mysql_pad_col(
337/*==============*/
338 ulint mbminlen, /*!< in: minimum size of a character,
339 in bytes */
340 byte* pad, /*!< out: padded buffer */
341 ulint len) /*!< in: number of bytes to pad */
342{
343 const byte* pad_end;
344
345 switch (UNIV_EXPECT(mbminlen, 1)) {
346 default:
347 ut_error;
348 case 1:
349 /* space=0x20 */
350 memset(pad, 0x20, len);
351 break;
352 case 2:
353 /* space=0x0020 */
354 pad_end = pad + len;
355 ut_a(!(len % 2));
356 while (pad < pad_end) {
357 *pad++ = 0x00;
358 *pad++ = 0x20;
359 };
360 break;
361 case 4:
362 /* space=0x00000020 */
363 pad_end = pad + len;
364 ut_a(!(len % 4));
365 while (pad < pad_end) {
366 *pad++ = 0x00;
367 *pad++ = 0x00;
368 *pad++ = 0x00;
369 *pad++ = 0x20;
370 }
371 break;
372 }
373}
374
375/**************************************************************//**
376Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format.
377The counterpart of this function is row_sel_field_store_in_mysql_format() in
378row0sel.cc.
379@return up to which byte we used buf in the conversion */
380byte*
381row_mysql_store_col_in_innobase_format(
382/*===================================*/
383 dfield_t* dfield, /*!< in/out: dfield where dtype
384 information must be already set when
385 this function is called! */
386 byte* buf, /*!< in/out: buffer for a converted
387 integer value; this must be at least
388 col_len long then! NOTE that dfield
389 may also get a pointer to 'buf',
390 therefore do not discard this as long
391 as dfield is used! */
392 ibool row_format_col, /*!< TRUE if the mysql_data is from
393 a MySQL row, FALSE if from a MySQL
394 key value;
395 in MySQL, a true VARCHAR storage
396 format differs in a row and in a
397 key value: in a key value the length
398 is always stored in 2 bytes! */
399 const byte* mysql_data, /*!< in: MySQL column value, not
400 SQL NULL; NOTE that dfield may also
401 get a pointer to mysql_data,
402 therefore do not discard this as long
403 as dfield is used! */
404 ulint col_len, /*!< in: MySQL column length; NOTE that
405 this is the storage length of the
406 column in the MySQL format row, not
407 necessarily the length of the actual
408 payload data; if the column is a true
409 VARCHAR then this is irrelevant */
410 ulint comp) /*!< in: nonzero=compact format */
411{
412 const byte* ptr = mysql_data;
413 const dtype_t* dtype;
414 ulint type;
415 ulint lenlen;
416
417 dtype = dfield_get_type(dfield);
418
419 type = dtype->mtype;
420
421 if (type == DATA_INT) {
422 /* Store integer data in Innobase in a big-endian format,
423 sign bit negated if the data is a signed integer. In MySQL,
424 integers are stored in a little-endian format. */
425
426 byte* p = buf + col_len;
427
428 for (;;) {
429 p--;
430 *p = *mysql_data;
431 if (p == buf) {
432 break;
433 }
434 mysql_data++;
435 }
436
437 if (!(dtype->prtype & DATA_UNSIGNED)) {
438
439 *buf ^= 128;
440 }
441
442 ptr = buf;
443 buf += col_len;
444 } else if ((type == DATA_VARCHAR
445 || type == DATA_VARMYSQL
446 || type == DATA_BINARY)) {
447
448 if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) {
449 /* The length of the actual data is stored to 1 or 2
450 bytes at the start of the field */
451
452 if (row_format_col) {
453 if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) {
454 lenlen = 2;
455 } else {
456 lenlen = 1;
457 }
458 } else {
459 /* In a MySQL key value, lenlen is always 2 */
460 lenlen = 2;
461 }
462
463 ptr = row_mysql_read_true_varchar(&col_len, mysql_data,
464 lenlen);
465 } else {
466 /* Remove trailing spaces from old style VARCHAR
467 columns. */
468
469 /* Handle Unicode strings differently. */
470 ulint mbminlen = dtype_get_mbminlen(dtype);
471
472 ptr = mysql_data;
473
474 switch (mbminlen) {
475 default:
476 ut_error;
477 case 4:
478 /* space=0x00000020 */
479 /* Trim "half-chars", just in case. */
480 col_len &= ~3U;
481
482 while (col_len >= 4
483 && ptr[col_len - 4] == 0x00
484 && ptr[col_len - 3] == 0x00
485 && ptr[col_len - 2] == 0x00
486 && ptr[col_len - 1] == 0x20) {
487 col_len -= 4;
488 }
489 break;
490 case 2:
491 /* space=0x0020 */
492 /* Trim "half-chars", just in case. */
493 col_len &= ~1U;
494
495 while (col_len >= 2 && ptr[col_len - 2] == 0x00
496 && ptr[col_len - 1] == 0x20) {
497 col_len -= 2;
498 }
499 break;
500 case 1:
501 /* space=0x20 */
502 while (col_len > 0
503 && ptr[col_len - 1] == 0x20) {
504 col_len--;
505 }
506 }
507 }
508 } else if (comp && type == DATA_MYSQL
509 && dtype_get_mbminlen(dtype) == 1
510 && dtype_get_mbmaxlen(dtype) > 1) {
511 /* In some cases we strip trailing spaces from UTF-8 and other
512 multibyte charsets, from FIXED-length CHAR columns, to save
513 space. UTF-8 would otherwise normally use 3 * the string length
514 bytes to store an ASCII string! */
515
516 /* We assume that this CHAR field is encoded in a
517 variable-length character set where spaces have
518 1:1 correspondence to 0x20 bytes, such as UTF-8.
519
520 Consider a CHAR(n) field, a field of n characters.
521 It will contain between n * mbminlen and n * mbmaxlen bytes.
522 We will try to truncate it to n bytes by stripping
523 space padding. If the field contains single-byte
524 characters only, it will be truncated to n characters.
525 Consider a CHAR(5) field containing the string
526 ".a " where "." denotes a 3-byte character represented
527 by the bytes "$%&". After our stripping, the string will
528 be stored as "$%&a " (5 bytes). The string
529 ".abc " will be stored as "$%&abc" (6 bytes).
530
531 The space padding will be restored in row0sel.cc, function
532 row_sel_field_store_in_mysql_format(). */
533
534 ulint n_chars;
535
536 ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype)));
537
538 n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);
539
540 /* Strip space padding. */
541 while (col_len > n_chars && ptr[col_len - 1] == 0x20) {
542 col_len--;
543 }
544 } else if (!row_format_col) {
545 /* if mysql data is from a MySQL key value
546 since the length is always stored in 2 bytes,
547 we need do nothing here. */
548 } else if (type == DATA_BLOB) {
549
550 ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len);
551 } else if (DATA_GEOMETRY_MTYPE(type)) {
552 ptr = row_mysql_read_geometry(&col_len, mysql_data, col_len);
553 }
554
555 dfield_set_data(dfield, ptr, col_len);
556
557 return(buf);
558}
559
560/**************************************************************//**
561Convert a row in the MySQL format to a row in the Innobase format. Note that
562the function to convert a MySQL format key value to an InnoDB dtuple is
563row_sel_convert_mysql_key_to_innobase() in row0sel.cc. */
564static
565void
566row_mysql_convert_row_to_innobase(
567/*==============================*/
568 dtuple_t* row, /*!< in/out: Innobase row where the
569 field type information is already
570 copied there! */
571 row_prebuilt_t* prebuilt, /*!< in: prebuilt struct where template
572 must be of type ROW_MYSQL_WHOLE_ROW */
573 const byte* mysql_rec, /*!< in: row in the MySQL format;
574 NOTE: do not discard as long as
575 row is used, as row may contain
576 pointers to this record! */
577 mem_heap_t** blob_heap) /*!< in: FIX_ME, remove this after
578 server fixes its issue */
579{
580 const mysql_row_templ_t*templ;
581 dfield_t* dfield;
582 ulint i;
583 ulint n_col = 0;
584 ulint n_v_col = 0;
585
586 ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
587 ut_ad(prebuilt->mysql_template);
588
589 for (i = 0; i < prebuilt->n_template; i++) {
590
591 templ = prebuilt->mysql_template + i;
592
593 if (templ->is_virtual) {
594 ut_ad(n_v_col < dtuple_get_n_v_fields(row));
595 dfield = dtuple_get_nth_v_field(row, n_v_col);
596 n_v_col++;
597 } else {
598 dfield = dtuple_get_nth_field(row, n_col);
599 n_col++;
600 }
601
602 if (templ->mysql_null_bit_mask != 0) {
603 /* Column may be SQL NULL */
604
605 if (mysql_rec[templ->mysql_null_byte_offset]
606 & (byte) (templ->mysql_null_bit_mask)) {
607
608 /* It is SQL NULL */
609
610 dfield_set_null(dfield);
611
612 goto next_column;
613 }
614 }
615
616 row_mysql_store_col_in_innobase_format(
617 dfield,
618 prebuilt->ins_upd_rec_buff + templ->mysql_col_offset,
619 TRUE, /* MySQL row format data */
620 mysql_rec + templ->mysql_col_offset,
621 templ->mysql_col_len,
622 dict_table_is_comp(prebuilt->table));
623
624 /* server has issue regarding handling BLOB virtual fields,
625 and we need to duplicate it with our own memory here */
626 if (templ->is_virtual
627 && DATA_LARGE_MTYPE(dfield_get_type(dfield)->mtype)) {
628 if (*blob_heap == NULL) {
629 *blob_heap = mem_heap_create(dfield->len);
630 }
631 dfield_dup(dfield, *blob_heap);
632 }
633next_column:
634 ;
635 }
636
637 /* If there is a FTS doc id column and it is not user supplied (
638 generated by server) then assign it a new doc id. */
639 if (!prebuilt->table->fts) {
640 return;
641 }
642
643 ut_a(prebuilt->table->fts->doc_col != ULINT_UNDEFINED);
644
645 doc_id_t doc_id;
646
647 if (!DICT_TF2_FLAG_IS_SET(prebuilt->table, DICT_TF2_FTS_HAS_DOC_ID)) {
648 if (prebuilt->table->fts->cache->first_doc_id
649 == FTS_NULL_DOC_ID) {
650 fts_get_next_doc_id(prebuilt->table, &doc_id);
651 }
652 return;
653 }
654
655 dfield_t* fts_doc_id = dtuple_get_nth_field(
656 row, prebuilt->table->fts->doc_col);
657
658 if (fts_get_next_doc_id(prebuilt->table, &doc_id) == DB_SUCCESS) {
659 ut_a(doc_id != FTS_NULL_DOC_ID);
660 ut_ad(sizeof(doc_id) == fts_doc_id->type.len);
661 dfield_set_data(fts_doc_id, prebuilt->ins_upd_rec_buff
662 + prebuilt->mysql_row_len, 8);
663 fts_write_doc_id(fts_doc_id->data, doc_id);
664 } else {
665 dfield_set_null(fts_doc_id);
666 }
667}
668
669/****************************************************************//**
670Handles user errors and lock waits detected by the database engine.
671@return true if it was a lock wait and we should continue running the
672query thread and in that case the thr is ALREADY in the running state. */
673bool
674row_mysql_handle_errors(
675/*====================*/
676 dberr_t* new_err,/*!< out: possible new error encountered in
677 lock wait, or if no new error, the value
678 of trx->error_state at the entry of this
679 function */
680 trx_t* trx, /*!< in: transaction */
681 que_thr_t* thr, /*!< in: query thread, or NULL */
682 trx_savept_t* savept) /*!< in: savepoint, or NULL */
683{
684 dberr_t err;
685
686 DBUG_ENTER("row_mysql_handle_errors");
687
688handle_new_error:
689 err = trx->error_state;
690
691 ut_a(err != DB_SUCCESS);
692
693 trx->error_state = DB_SUCCESS;
694
695 DBUG_LOG("trx", "handle error: " << ut_strerr(err)
696 << ";id=" << ib::hex(trx->id) << ", " << trx);
697
698 switch (err) {
699 case DB_LOCK_WAIT_TIMEOUT:
700 if (row_rollback_on_timeout) {
701 trx_rollback_to_savepoint(trx, NULL);
702 break;
703 }
704 /* fall through */
705 case DB_DUPLICATE_KEY:
706 case DB_FOREIGN_DUPLICATE_KEY:
707 case DB_TOO_BIG_RECORD:
708 case DB_UNDO_RECORD_TOO_BIG:
709 case DB_ROW_IS_REFERENCED:
710 case DB_NO_REFERENCED_ROW:
711 case DB_CANNOT_ADD_CONSTRAINT:
712 case DB_TOO_MANY_CONCURRENT_TRXS:
713 case DB_OUT_OF_FILE_SPACE:
714 case DB_READ_ONLY:
715 case DB_FTS_INVALID_DOCID:
716 case DB_INTERRUPTED:
717 case DB_CANT_CREATE_GEOMETRY_OBJECT:
718 case DB_TABLE_NOT_FOUND:
719 case DB_DECRYPTION_FAILED:
720 case DB_COMPUTE_VALUE_FAILED:
721 DBUG_EXECUTE_IF("row_mysql_crash_if_error", {
722 log_buffer_flush_to_disk();
723 DBUG_SUICIDE(); });
724 if (savept) {
725 /* Roll back the latest, possibly incomplete insertion
726 or update */
727
728 trx_rollback_to_savepoint(trx, savept);
729 }
730 /* MySQL will roll back the latest SQL statement */
731 break;
732 case DB_LOCK_WAIT:
733 lock_wait_suspend_thread(thr);
734
735 if (trx->error_state != DB_SUCCESS) {
736 que_thr_stop_for_mysql(thr);
737
738 goto handle_new_error;
739 }
740
741 *new_err = err;
742
743 DBUG_RETURN(true);
744
745 case DB_DEADLOCK:
746 case DB_LOCK_TABLE_FULL:
747 /* Roll back the whole transaction; this resolution was added
748 to version 3.23.43 */
749
750 trx_rollback_to_savepoint(trx, NULL);
751 break;
752
753 case DB_MUST_GET_MORE_FILE_SPACE:
754 ib::fatal() << "The database cannot continue operation because"
755 " of lack of space. You must add a new data file"
756 " to my.cnf and restart the database.";
757 break;
758
759 case DB_CORRUPTION:
760 case DB_PAGE_CORRUPTED:
761 ib::error() << "We detected index corruption in an InnoDB type"
762 " table. You have to dump + drop + reimport the"
763 " table or, in a case of widespread corruption,"
764 " dump all InnoDB tables and recreate the whole"
765 " tablespace. If the mysqld server crashes after"
766 " the startup or when you dump the tables. "
767 << FORCE_RECOVERY_MSG;
768 break;
769 case DB_FOREIGN_EXCEED_MAX_CASCADE:
770 ib::error() << "Cannot delete/update rows with cascading"
771 " foreign key constraints that exceed max depth of "
772 << FK_MAX_CASCADE_DEL << ". Please drop excessive"
773 " foreign constraints and try again";
774 break;
775 case DB_UNSUPPORTED:
776 ib::error() << "Cannot delete/update rows with cascading"
777 " foreign key constraints in timestamp-based temporal"
778 " table. Please drop excessive"
779 " foreign constraints and try again";
780 break;
781 default:
782 ib::fatal() << "Unknown error code " << err << ": "
783 << ut_strerr(err);
784 }
785
786 if (trx->error_state != DB_SUCCESS) {
787 *new_err = trx->error_state;
788 } else {
789 *new_err = err;
790 }
791
792 trx->error_state = DB_SUCCESS;
793
794 DBUG_RETURN(false);
795}
796
797/********************************************************************//**
798Create a prebuilt struct for a MySQL table handle.
799@return own: a prebuilt struct */
800row_prebuilt_t*
801row_create_prebuilt(
802/*================*/
803 dict_table_t* table, /*!< in: Innobase table handle */
804 ulint mysql_row_len) /*!< in: length in bytes of a row in
805 the MySQL format */
806{
807 DBUG_ENTER("row_create_prebuilt");
808
809 row_prebuilt_t* prebuilt;
810 mem_heap_t* heap;
811 dict_index_t* clust_index;
812 dict_index_t* temp_index;
813 dtuple_t* ref;
814 ulint ref_len;
815 uint srch_key_len = 0;
816 ulint search_tuple_n_fields;
817
818 search_tuple_n_fields = 2 * (dict_table_get_n_cols(table)
819 + dict_table_get_n_v_cols(table));
820
821 clust_index = dict_table_get_first_index(table);
822
823 /* Make sure that search_tuple is long enough for clustered index */
824 ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields);
825
826 ref_len = dict_index_get_n_unique(clust_index);
827
828
829 /* Maximum size of the buffer needed for conversion of INTs from
830 little endian format to big endian format in an index. An index
831 can have maximum 16 columns (MAX_REF_PARTS) in it. Therfore
832 Max size for PK: 16 * 8 bytes (BIGINT's size) = 128 bytes
833 Max size Secondary index: 16 * 8 bytes + PK = 256 bytes. */
834#define MAX_SRCH_KEY_VAL_BUFFER 2* (8 * MAX_REF_PARTS)
835
836#define PREBUILT_HEAP_INITIAL_SIZE \
837 ( \
838 sizeof(*prebuilt) \
839 /* allocd in this function */ \
840 + DTUPLE_EST_ALLOC(search_tuple_n_fields) \
841 + DTUPLE_EST_ALLOC(ref_len) \
842 /* allocd in row_prebuild_sel_graph() */ \
843 + sizeof(sel_node_t) \
844 + sizeof(que_fork_t) \
845 + sizeof(que_thr_t) \
846 /* allocd in row_get_prebuilt_update_vector() */ \
847 + sizeof(upd_node_t) \
848 + sizeof(upd_t) \
849 + sizeof(upd_field_t) \
850 * dict_table_get_n_cols(table) \
851 + sizeof(que_fork_t) \
852 + sizeof(que_thr_t) \
853 /* allocd in row_get_prebuilt_insert_row() */ \
854 + sizeof(ins_node_t) \
855 /* mysql_row_len could be huge and we are not \
856 sure if this prebuilt instance is going to be \
857 used in inserts */ \
858 + (mysql_row_len < 256 ? mysql_row_len : 0) \
859 + DTUPLE_EST_ALLOC(dict_table_get_n_cols(table) \
860 + dict_table_get_n_v_cols(table)) \
861 + sizeof(que_fork_t) \
862 + sizeof(que_thr_t) \
863 + sizeof(*prebuilt->pcur) \
864 + sizeof(*prebuilt->clust_pcur) \
865 )
866
867 /* Calculate size of key buffer used to store search key in
868 InnoDB format. MySQL stores INTs in little endian format and
869 InnoDB stores INTs in big endian format with the sign bit
870 flipped. All other field types are stored/compared the same
871 in MySQL and InnoDB, so we must create a buffer containing
872 the INT key parts in InnoDB format.We need two such buffers
873 since both start and end keys are used in records_in_range(). */
874
875 for (temp_index = dict_table_get_first_index(table); temp_index;
876 temp_index = dict_table_get_next_index(temp_index)) {
877 DBUG_EXECUTE_IF("innodb_srch_key_buffer_max_value",
878 ut_a(temp_index->n_user_defined_cols
879 == MAX_REF_PARTS););
880 uint temp_len = 0;
881 for (uint i = 0; i < temp_index->n_uniq; i++) {
882 ulint type = temp_index->fields[i].col->mtype;
883 if (type == DATA_INT) {
884 temp_len +=
885 temp_index->fields[i].fixed_len;
886 }
887 }
888 srch_key_len = std::max(srch_key_len,temp_len);
889 }
890
891 ut_a(srch_key_len <= MAX_SRCH_KEY_VAL_BUFFER);
892
893 DBUG_EXECUTE_IF("innodb_srch_key_buffer_max_value",
894 ut_a(srch_key_len == MAX_SRCH_KEY_VAL_BUFFER););
895
896 /* We allocate enough space for the objects that are likely to
897 be created later in order to minimize the number of malloc()
898 calls */
899 heap = mem_heap_create(PREBUILT_HEAP_INITIAL_SIZE + 2 * srch_key_len);
900
901 prebuilt = static_cast<row_prebuilt_t*>(
902 mem_heap_zalloc(heap, sizeof(*prebuilt)));
903
904 prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
905 prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
906
907 prebuilt->table = table;
908
909 prebuilt->sql_stat_start = TRUE;
910 prebuilt->heap = heap;
911
912 prebuilt->srch_key_val_len = srch_key_len;
913 if (prebuilt->srch_key_val_len) {
914 prebuilt->srch_key_val1 = static_cast<byte*>(
915 mem_heap_alloc(prebuilt->heap,
916 2 * prebuilt->srch_key_val_len));
917 prebuilt->srch_key_val2 = prebuilt->srch_key_val1 +
918 prebuilt->srch_key_val_len;
919 } else {
920 prebuilt->srch_key_val1 = NULL;
921 prebuilt->srch_key_val2 = NULL;
922 }
923
924 prebuilt->pcur = static_cast<btr_pcur_t*>(
925 mem_heap_zalloc(prebuilt->heap,
926 sizeof(btr_pcur_t)));
927 prebuilt->clust_pcur = static_cast<btr_pcur_t*>(
928 mem_heap_zalloc(prebuilt->heap,
929 sizeof(btr_pcur_t)));
930 btr_pcur_reset(prebuilt->pcur);
931 btr_pcur_reset(prebuilt->clust_pcur);
932
933 prebuilt->select_lock_type = LOCK_NONE;
934 prebuilt->stored_select_lock_type = LOCK_NONE_UNSET;
935
936 prebuilt->search_tuple = dtuple_create(heap, search_tuple_n_fields);
937
938 ref = dtuple_create(heap, ref_len);
939
940 dict_index_copy_types(ref, clust_index, ref_len);
941
942 prebuilt->clust_ref = ref;
943
944 prebuilt->autoinc_error = DB_SUCCESS;
945 prebuilt->autoinc_offset = 0;
946
947 /* Default to 1, we will set the actual value later in
948 ha_innobase::get_auto_increment(). */
949 prebuilt->autoinc_increment = 1;
950
951 prebuilt->autoinc_last_value = 0;
952
953 /* During UPDATE and DELETE we need the doc id. */
954 prebuilt->fts_doc_id = 0;
955
956 prebuilt->mysql_row_len = mysql_row_len;
957
958 prebuilt->fts_doc_id_in_read_set = 0;
959 prebuilt->blob_heap = NULL;
960
961 prebuilt->m_no_prefetch = false;
962 prebuilt->m_read_virtual_key = false;
963
964 DBUG_RETURN(prebuilt);
965}
966
967/********************************************************************//**
968Free a prebuilt struct for a MySQL table handle. */
969void
970row_prebuilt_free(
971/*==============*/
972 row_prebuilt_t* prebuilt, /*!< in, own: prebuilt struct */
973 ibool dict_locked) /*!< in: TRUE=data dictionary locked */
974{
975 DBUG_ENTER("row_prebuilt_free");
976
977 ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
978 ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED);
979
980 prebuilt->magic_n = ROW_PREBUILT_FREED;
981 prebuilt->magic_n2 = ROW_PREBUILT_FREED;
982
983 btr_pcur_reset(prebuilt->pcur);
984 btr_pcur_reset(prebuilt->clust_pcur);
985
986 ut_free(prebuilt->mysql_template);
987
988 if (prebuilt->ins_graph) {
989 que_graph_free_recursive(prebuilt->ins_graph);
990 }
991
992 if (prebuilt->sel_graph) {
993 que_graph_free_recursive(prebuilt->sel_graph);
994 }
995
996 if (prebuilt->upd_graph) {
997 que_graph_free_recursive(prebuilt->upd_graph);
998 }
999
1000 if (prebuilt->blob_heap) {
1001 row_mysql_prebuilt_free_blob_heap(prebuilt);
1002 }
1003
1004 if (prebuilt->old_vers_heap) {
1005 mem_heap_free(prebuilt->old_vers_heap);
1006 }
1007
1008 if (prebuilt->fetch_cache[0] != NULL) {
1009 byte* base = prebuilt->fetch_cache[0] - 4;
1010 byte* ptr = base;
1011
1012 for (ulint i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
1013 ulint magic1 = mach_read_from_4(ptr);
1014 ut_a(magic1 == ROW_PREBUILT_FETCH_MAGIC_N);
1015 ptr += 4;
1016
1017 byte* row = ptr;
1018 ut_a(row == prebuilt->fetch_cache[i]);
1019 ptr += prebuilt->mysql_row_len;
1020
1021 ulint magic2 = mach_read_from_4(ptr);
1022 ut_a(magic2 == ROW_PREBUILT_FETCH_MAGIC_N);
1023 ptr += 4;
1024 }
1025
1026 ut_free(base);
1027 }
1028
1029 if (prebuilt->rtr_info) {
1030 rtr_clean_rtr_info(prebuilt->rtr_info, true);
1031 }
1032 if (prebuilt->table) {
1033 dict_table_close(prebuilt->table, dict_locked, TRUE);
1034 }
1035
1036 mem_heap_free(prebuilt->heap);
1037
1038 DBUG_VOID_RETURN;
1039}
1040
1041/*********************************************************************//**
1042Updates the transaction pointers in query graphs stored in the prebuilt
1043struct. */
1044void
1045row_update_prebuilt_trx(
1046/*====================*/
1047 row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct
1048 in MySQL handle */
1049 trx_t* trx) /*!< in: transaction handle */
1050{
1051 ut_a(trx->magic_n == TRX_MAGIC_N);
1052 ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
1053 ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED);
1054
1055 prebuilt->trx = trx;
1056
1057 if (prebuilt->ins_graph) {
1058 prebuilt->ins_graph->trx = trx;
1059 }
1060
1061 if (prebuilt->upd_graph) {
1062 prebuilt->upd_graph->trx = trx;
1063 }
1064
1065 if (prebuilt->sel_graph) {
1066 prebuilt->sel_graph->trx = trx;
1067 }
1068}
1069
1070/*********************************************************************//**
1071Gets pointer to a prebuilt dtuple used in insertions. If the insert graph
1072has not yet been built in the prebuilt struct, then this function first
1073builds it.
1074@return prebuilt dtuple; the column type information is also set in it */
1075static
1076dtuple_t*
1077row_get_prebuilt_insert_row(
1078/*========================*/
1079 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL
1080 handle */
1081{
1082 dict_table_t* table = prebuilt->table;
1083
1084 ut_ad(prebuilt && table && prebuilt->trx);
1085
1086 if (prebuilt->ins_node != 0) {
1087
1088 /* Check if indexes have been dropped or added and we
1089 may need to rebuild the row insert template. */
1090
1091 if (prebuilt->trx_id == table->def_trx_id
1092 && UT_LIST_GET_LEN(prebuilt->ins_node->entry_list)
1093 == UT_LIST_GET_LEN(table->indexes)) {
1094
1095 return(prebuilt->ins_node->row);
1096 }
1097
1098 ut_ad(prebuilt->trx_id < table->def_trx_id);
1099
1100 que_graph_free_recursive(prebuilt->ins_graph);
1101
1102 prebuilt->ins_graph = 0;
1103 }
1104
1105 /* Create an insert node and query graph to the prebuilt struct */
1106
1107 ins_node_t* node;
1108
1109 node = ins_node_create(INS_DIRECT, table, prebuilt->heap);
1110
1111 prebuilt->ins_node = node;
1112
1113 if (prebuilt->ins_upd_rec_buff == 0) {
1114 prebuilt->ins_upd_rec_buff = static_cast<byte*>(
1115 mem_heap_alloc(
1116 prebuilt->heap,
1117 DICT_TF2_FLAG_IS_SET(prebuilt->table,
1118 DICT_TF2_FTS_HAS_DOC_ID)
1119 ? prebuilt->mysql_row_len + 8/* FTS_DOC_ID */
1120 : prebuilt->mysql_row_len));
1121 }
1122
1123 dtuple_t* row;
1124
1125 row = dtuple_create_with_vcol(
1126 prebuilt->heap, dict_table_get_n_cols(table),
1127 dict_table_get_n_v_cols(table));
1128
1129 dict_table_copy_types(row, table);
1130
1131 ins_node_set_new_row(node, row);
1132
1133 prebuilt->ins_graph = static_cast<que_fork_t*>(
1134 que_node_get_parent(
1135 pars_complete_graph_for_exec(
1136 node,
1137 prebuilt->trx, prebuilt->heap, prebuilt)));
1138
1139 prebuilt->ins_graph->state = QUE_FORK_ACTIVE;
1140
1141 prebuilt->trx_id = table->def_trx_id;
1142
1143 return(prebuilt->ins_node->row);
1144}
1145
1146/*********************************************************************//**
1147Sets an AUTO_INC type lock on the table mentioned in prebuilt. The
1148AUTO_INC lock gives exclusive access to the auto-inc counter of the
1149table. The lock is reserved only for the duration of an SQL statement.
1150It is not compatible with another AUTO_INC or exclusive lock on the
1151table.
1152@return error code or DB_SUCCESS */
1153dberr_t
1154row_lock_table_autoinc_for_mysql(
1155/*=============================*/
1156 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in the MySQL
1157 table handle */
1158{
1159 trx_t* trx = prebuilt->trx;
1160 ins_node_t* node = prebuilt->ins_node;
1161 const dict_table_t* table = prebuilt->table;
1162 que_thr_t* thr;
1163 dberr_t err;
1164 ibool was_lock_wait;
1165
1166 /* If we already hold an AUTOINC lock on the table then do nothing.
1167 Note: We peek at the value of the current owner without acquiring
1168 the lock mutex. */
1169 if (trx == table->autoinc_trx) {
1170
1171 return(DB_SUCCESS);
1172 }
1173
1174 trx->op_info = "setting auto-inc lock";
1175
1176 row_get_prebuilt_insert_row(prebuilt);
1177 node = prebuilt->ins_node;
1178
1179 /* We use the insert query graph as the dummy graph needed
1180 in the lock module call */
1181
1182 thr = que_fork_get_first_thr(prebuilt->ins_graph);
1183
1184 que_thr_move_to_run_state_for_mysql(thr, trx);
1185
1186run_again:
1187 thr->run_node = node;
1188 thr->prev_node = node;
1189
1190 /* It may be that the current session has not yet started
1191 its transaction, or it has been committed: */
1192
1193 trx_start_if_not_started_xa(trx, true);
1194
1195 err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr);
1196
1197 trx->error_state = err;
1198
1199 if (err != DB_SUCCESS) {
1200 que_thr_stop_for_mysql(thr);
1201
1202 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
1203
1204 if (was_lock_wait) {
1205 goto run_again;
1206 }
1207
1208 trx->op_info = "";
1209
1210 return(err);
1211 }
1212
1213 que_thr_stop_for_mysql_no_error(thr, trx);
1214
1215 trx->op_info = "";
1216
1217 return(err);
1218}
1219
1220/** Lock a table.
1221@param[in,out] prebuilt table handle
1222@return error code or DB_SUCCESS */
1223dberr_t
1224row_lock_table(row_prebuilt_t* prebuilt)
1225{
1226 trx_t* trx = prebuilt->trx;
1227 que_thr_t* thr;
1228 dberr_t err;
1229 ibool was_lock_wait;
1230
1231 trx->op_info = "setting table lock";
1232
1233 if (prebuilt->sel_graph == NULL) {
1234 /* Build a dummy select query graph */
1235 row_prebuild_sel_graph(prebuilt);
1236 }
1237
1238 /* We use the select query graph as the dummy graph needed
1239 in the lock module call */
1240
1241 thr = que_fork_get_first_thr(prebuilt->sel_graph);
1242
1243 que_thr_move_to_run_state_for_mysql(thr, trx);
1244
1245run_again:
1246 thr->run_node = thr;
1247 thr->prev_node = thr->common.parent;
1248
1249 /* It may be that the current session has not yet started
1250 its transaction, or it has been committed: */
1251
1252 trx_start_if_not_started_xa(trx, false);
1253
1254 err = lock_table(0, prebuilt->table,
1255 static_cast<enum lock_mode>(
1256 prebuilt->select_lock_type),
1257 thr);
1258
1259 trx->error_state = err;
1260
1261 if (err != DB_SUCCESS) {
1262 que_thr_stop_for_mysql(thr);
1263
1264 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
1265
1266 if (was_lock_wait) {
1267 goto run_again;
1268 }
1269
1270 trx->op_info = "";
1271
1272 return(err);
1273 }
1274
1275 que_thr_stop_for_mysql_no_error(thr, trx);
1276
1277 trx->op_info = "";
1278
1279 return(err);
1280}
1281
1282/** Determine is tablespace encrypted but decryption failed, is table corrupted
1283or is tablespace .ibd file missing.
1284@param[in] table Table
1285@param[in] trx Transaction
1286@param[in] push_warning true if we should push warning to user
1287@retval DB_DECRYPTION_FAILED table is encrypted but decryption failed
1288@retval DB_CORRUPTION table is corrupted
1289@retval DB_TABLESPACE_NOT_FOUND tablespace .ibd file not found */
1290static
1291dberr_t
1292row_mysql_get_table_status(
1293 const dict_table_t* table,
1294 trx_t* trx,
1295 bool push_warning = true)
1296{
1297 dberr_t err;
1298 if (const fil_space_t* space = table->space) {
1299 if (space->crypt_data && space->crypt_data->is_encrypted()) {
1300 // maybe we cannot access the table due to failing
1301 // to decrypt
1302 if (push_warning) {
1303 ib_push_warning(trx, DB_DECRYPTION_FAILED,
1304 "Table %s in tablespace %lu encrypted."
1305 "However key management plugin or used key_id is not found or"
1306 " used encryption algorithm or method does not match.",
1307 table->name, table->space);
1308 }
1309
1310 err = DB_DECRYPTION_FAILED;
1311 } else {
1312 if (push_warning) {
1313 ib_push_warning(trx, DB_CORRUPTION,
1314 "Table %s in tablespace %lu corrupted.",
1315 table->name, table->space);
1316 }
1317
1318 err = DB_CORRUPTION;
1319 }
1320 } else {
1321 ib::error() << ".ibd file is missing for table "
1322 << table->name;
1323 err = DB_TABLESPACE_NOT_FOUND;
1324 }
1325
1326 return(err);
1327}
1328
1329/** Writes 8 bytes to nth tuple field
1330@param[in] tuple where to write
1331@param[in] nth index in tuple
1332@param[in] data what to write
1333@param[in] buf field data buffer */
1334static
1335void
1336set_tuple_col_8(dtuple_t* tuple, int col, uint64_t data, byte* buf) {
1337 dfield_t* dfield = dtuple_get_nth_field(tuple, col);
1338 ut_ad(dfield->type.len == 8);
1339 if (dfield->len == UNIV_SQL_NULL) {
1340 dfield_set_data(dfield, buf, 8);
1341 }
1342 ut_ad(dfield->len == dfield->type.len && dfield->data);
1343 mach_write_to_8(dfield->data, data);
1344}
1345
1346/** Does an insert for MySQL.
1347@param[in] mysql_rec row in the MySQL format
1348@param[in,out] prebuilt prebuilt struct in MySQL handle
1349@return error code or DB_SUCCESS */
1350dberr_t
1351row_insert_for_mysql(
1352 const byte* mysql_rec,
1353 row_prebuilt_t* prebuilt,
1354 ins_mode_t ins_mode)
1355{
1356 trx_savept_t savept;
1357 que_thr_t* thr;
1358 dberr_t err;
1359 ibool was_lock_wait;
1360 trx_t* trx = prebuilt->trx;
1361 ins_node_t* node = prebuilt->ins_node;
1362 dict_table_t* table = prebuilt->table;
1363
1364 /* FIX_ME: This blob heap is used to compensate an issue in server
1365 for virtual column blob handling */
1366 mem_heap_t* blob_heap = NULL;
1367
1368 ut_ad(trx);
1369 ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
1370 ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED);
1371
1372 if (!prebuilt->table->space) {
1373
1374 ib::error() << "The table " << prebuilt->table->name
1375 << " doesn't have a corresponding tablespace, it was"
1376 " discarded.";
1377
1378 return(DB_TABLESPACE_DELETED);
1379
1380 } else if (!prebuilt->table->is_readable()) {
1381 return(row_mysql_get_table_status(prebuilt->table, trx, true));
1382 } else if (high_level_read_only) {
1383 return(DB_READ_ONLY);
1384 }
1385
1386 DBUG_EXECUTE_IF("mark_table_corrupted", {
1387 /* Mark the table corrupted for the clustered index */
1388 dict_index_t* index = dict_table_get_first_index(table);
1389 ut_ad(dict_index_is_clust(index));
1390 dict_set_corrupted(index, trx, "INSERT TABLE"); });
1391
1392 if (dict_table_is_corrupted(table)) {
1393
1394 ib::error() << "Table " << table->name << " is corrupt.";
1395 return(DB_TABLE_CORRUPT);
1396 }
1397
1398 trx->op_info = "inserting";
1399
1400 row_mysql_delay_if_needed();
1401
1402 if (!table->no_rollback()) {
1403 trx_start_if_not_started_xa(trx, true);
1404 }
1405
1406 row_get_prebuilt_insert_row(prebuilt);
1407 node = prebuilt->ins_node;
1408
1409 row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec,
1410 &blob_heap);
1411
1412 if (ins_mode != ROW_INS_NORMAL)
1413 {
1414 ut_ad(table->vers_start != table->vers_end);
1415 /* Return back modified fields into mysql_rec, so that
1416 upper logic may benefit from it (f.ex. 'on duplicate key'). */
1417 const mysql_row_templ_t* t = prebuilt->get_template_by_col(table->vers_end);
1418 ut_ad(t);
1419 ut_ad(t->mysql_col_len == 8);
1420
1421 if (ins_mode == ROW_INS_HISTORICAL) {
1422 set_tuple_col_8(node->row, table->vers_end, trx->id, node->vers_end_buf);
1423 }
1424 else /* ROW_INS_VERSIONED */ {
1425 set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX, node->vers_end_buf);
1426 int8store(&mysql_rec[t->mysql_col_offset], TRX_ID_MAX);
1427 t = prebuilt->get_template_by_col(table->vers_start);
1428 ut_ad(t);
1429 ut_ad(t->mysql_col_len == 8);
1430 set_tuple_col_8(node->row, table->vers_start, trx->id, node->vers_start_buf);
1431 int8store(&mysql_rec[t->mysql_col_offset], trx->id);
1432 }
1433 }
1434
1435 savept = trx_savept_take(trx);
1436
1437 thr = que_fork_get_first_thr(prebuilt->ins_graph);
1438
1439 if (prebuilt->sql_stat_start) {
1440 node->state = INS_NODE_SET_IX_LOCK;
1441 prebuilt->sql_stat_start = FALSE;
1442 } else {
1443 node->state = INS_NODE_ALLOC_ROW_ID;
1444 }
1445
1446 que_thr_move_to_run_state_for_mysql(thr, trx);
1447
1448run_again:
1449 thr->run_node = node;
1450 thr->prev_node = node;
1451
1452 row_ins_step(thr);
1453
1454 DEBUG_SYNC_C("ib_after_row_insert_step");
1455
1456 err = trx->error_state;
1457
1458 if (err != DB_SUCCESS) {
1459error_exit:
1460 que_thr_stop_for_mysql(thr);
1461
1462 /* FIXME: What's this ? */
1463 thr->lock_state = QUE_THR_LOCK_ROW;
1464
1465 was_lock_wait = row_mysql_handle_errors(
1466 &err, trx, thr, &savept);
1467
1468 thr->lock_state = QUE_THR_LOCK_NOLOCK;
1469
1470 if (was_lock_wait) {
1471 ut_ad(node->state == INS_NODE_INSERT_ENTRIES
1472 || node->state == INS_NODE_ALLOC_ROW_ID);
1473 goto run_again;
1474 }
1475
1476 node->duplicate = NULL;
1477 trx->op_info = "";
1478
1479 if (blob_heap != NULL) {
1480 mem_heap_free(blob_heap);
1481 }
1482
1483 return(err);
1484 }
1485
1486 node->duplicate = NULL;
1487
1488 if (dict_table_has_fts_index(table)) {
1489 doc_id_t doc_id;
1490
1491 /* Extract the doc id from the hidden FTS column */
1492 doc_id = fts_get_doc_id_from_row(table, node->row);
1493
1494 if (doc_id <= 0) {
1495 ib::error() << "FTS Doc ID must be large than 0";
1496 err = DB_FTS_INVALID_DOCID;
1497 trx->error_state = DB_FTS_INVALID_DOCID;
1498 goto error_exit;
1499 }
1500
1501 if (!DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)) {
1502 doc_id_t next_doc_id
1503 = table->fts->cache->next_doc_id;
1504
1505 if (doc_id < next_doc_id) {
1506
1507 ib::error() << "FTS Doc ID must be large than "
1508 << next_doc_id - 1 << " for table "
1509 << table->name;
1510
1511 err = DB_FTS_INVALID_DOCID;
1512 trx->error_state = DB_FTS_INVALID_DOCID;
1513 goto error_exit;
1514 }
1515
1516 /* Difference between Doc IDs are restricted within
1517 4 bytes integer. See fts_get_encoded_len(). Consecutive
1518 doc_ids difference should not exceed
1519 FTS_DOC_ID_MAX_STEP value. */
1520
1521 if (doc_id - next_doc_id >= FTS_DOC_ID_MAX_STEP) {
1522 ib::error() << "Doc ID " << doc_id
1523 << " is too big. Its difference with"
1524 " largest used Doc ID "
1525 << next_doc_id - 1 << " cannot"
1526 " exceed or equal to "
1527 << FTS_DOC_ID_MAX_STEP;
1528 err = DB_FTS_INVALID_DOCID;
1529 trx->error_state = DB_FTS_INVALID_DOCID;
1530 goto error_exit;
1531 }
1532 }
1533
1534 if (table->skip_alter_undo) {
1535 if (trx->fts_trx == NULL) {
1536 trx->fts_trx = fts_trx_create(trx);
1537 }
1538
1539 fts_trx_table_t ftt;
1540 ftt.table = table;
1541 ftt.fts_trx = trx->fts_trx;
1542
1543 fts_add_doc_from_tuple(&ftt, doc_id, node->row);
1544 } else {
1545 /* Pass NULL for the columns affected, since an INSERT affects
1546 all FTS indexes. */
1547 fts_trx_add_op(trx, table, doc_id, FTS_INSERT, NULL);
1548 }
1549 }
1550
1551 que_thr_stop_for_mysql_no_error(thr, trx);
1552
1553 if (table->is_system_db) {
1554 srv_stats.n_system_rows_inserted.inc(size_t(trx->id));
1555 } else {
1556 srv_stats.n_rows_inserted.inc(size_t(trx->id));
1557 }
1558
1559 /* Not protected by dict_table_stats_lock() for performance
1560 reasons, we would rather get garbage in stat_n_rows (which is
1561 just an estimate anyway) than protecting the following code
1562 with a latch. */
1563 dict_table_n_rows_inc(table);
1564
1565 if (prebuilt->clust_index_was_generated) {
1566 /* set row id to prebuilt */
1567 memcpy(prebuilt->row_id, node->sys_buf, DATA_ROW_ID_LEN);
1568 }
1569
1570 dict_stats_update_if_needed(table);
1571 trx->op_info = "";
1572
1573 if (blob_heap != NULL) {
1574 mem_heap_free(blob_heap);
1575 }
1576
1577 return(err);
1578}
1579
1580/*********************************************************************//**
1581Builds a dummy query graph used in selects. */
1582void
1583row_prebuild_sel_graph(
1584/*===================*/
1585 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL
1586 handle */
1587{
1588 sel_node_t* node;
1589
1590 ut_ad(prebuilt && prebuilt->trx);
1591
1592 if (prebuilt->sel_graph == NULL) {
1593
1594 node = sel_node_create(prebuilt->heap);
1595
1596 prebuilt->sel_graph = static_cast<que_fork_t*>(
1597 que_node_get_parent(
1598 pars_complete_graph_for_exec(
1599 static_cast<sel_node_t*>(node),
1600 prebuilt->trx, prebuilt->heap,
1601 prebuilt)));
1602
1603 prebuilt->sel_graph->state = QUE_FORK_ACTIVE;
1604 }
1605}
1606
1607/*********************************************************************//**
1608Creates an query graph node of 'update' type to be used in the MySQL
1609interface.
1610@return own: update node */
1611upd_node_t*
1612row_create_update_node_for_mysql(
1613/*=============================*/
1614 dict_table_t* table, /*!< in: table to update */
1615 mem_heap_t* heap) /*!< in: mem heap from which allocated */
1616{
1617 upd_node_t* node;
1618
1619 DBUG_ENTER("row_create_update_node_for_mysql");
1620
1621 node = upd_node_create(heap);
1622
1623 node->in_mysql_interface = true;
1624 node->is_delete = NO_DELETE;
1625 node->searched_update = FALSE;
1626 node->select = NULL;
1627 node->pcur = btr_pcur_create_for_mysql();
1628
1629 DBUG_PRINT("info", ("node: %p, pcur: %p", node, node->pcur));
1630
1631 node->table = table;
1632
1633 node->update = upd_create(dict_table_get_n_cols(table)
1634 + dict_table_get_n_v_cols(table), heap);
1635
1636 node->update_n_fields = dict_table_get_n_cols(table);
1637
1638 UT_LIST_INIT(node->columns, &sym_node_t::col_var_list);
1639
1640 node->has_clust_rec_x_lock = TRUE;
1641 node->cmpl_info = 0;
1642
1643 node->table_sym = NULL;
1644 node->col_assign_list = NULL;
1645
1646 DBUG_RETURN(node);
1647}
1648
1649/*********************************************************************//**
1650Gets pointer to a prebuilt update vector used in updates. If the update
1651graph has not yet been built in the prebuilt struct, then this function
1652first builds it.
1653@return prebuilt update vector */
1654upd_t*
1655row_get_prebuilt_update_vector(
1656/*===========================*/
1657 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL
1658 handle */
1659{
1660 if (prebuilt->upd_node == NULL) {
1661
1662 /* Not called before for this handle: create an update node
1663 and query graph to the prebuilt struct */
1664
1665 prebuilt->upd_node = row_create_update_node_for_mysql(
1666 prebuilt->table, prebuilt->heap);
1667
1668 prebuilt->upd_graph = static_cast<que_fork_t*>(
1669 que_node_get_parent(
1670 pars_complete_graph_for_exec(
1671 prebuilt->upd_node,
1672 prebuilt->trx, prebuilt->heap,
1673 prebuilt)));
1674
1675 prebuilt->upd_graph->state = QUE_FORK_ACTIVE;
1676 }
1677
1678 return(prebuilt->upd_node->update);
1679}
1680
1681/********************************************************************
1682Handle an update of a column that has an FTS index. */
1683static
1684void
1685row_fts_do_update(
1686/*==============*/
1687 trx_t* trx, /* in: transaction */
1688 dict_table_t* table, /* in: Table with FTS index */
1689 doc_id_t old_doc_id, /* in: old document id */
1690 doc_id_t new_doc_id) /* in: new document id */
1691{
1692 if(trx->fts_next_doc_id) {
1693 fts_trx_add_op(trx, table, old_doc_id, FTS_DELETE, NULL);
1694 if(new_doc_id != FTS_NULL_DOC_ID)
1695 fts_trx_add_op(trx, table, new_doc_id, FTS_INSERT, NULL);
1696 }
1697}
1698
1699/************************************************************************
1700Handles FTS matters for an update or a delete.
1701NOTE: should not be called if the table does not have an FTS index. .*/
1702static
1703dberr_t
1704row_fts_update_or_delete(
1705/*=====================*/
1706 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
1707 handle */
1708{
1709 trx_t* trx = prebuilt->trx;
1710 dict_table_t* table = prebuilt->table;
1711 upd_node_t* node = prebuilt->upd_node;
1712 doc_id_t old_doc_id = prebuilt->fts_doc_id;
1713
1714 DBUG_ENTER("row_fts_update_or_delete");
1715
1716 ut_a(dict_table_has_fts_index(prebuilt->table));
1717
1718 /* Deletes are simple; get them out of the way first. */
1719 if (node->is_delete == PLAIN_DELETE) {
1720 /* A delete affects all FTS indexes, so we pass NULL */
1721 fts_trx_add_op(trx, table, old_doc_id, FTS_DELETE, NULL);
1722 } else {
1723 doc_id_t new_doc_id;
1724 new_doc_id = fts_read_doc_id((byte*) &trx->fts_next_doc_id);
1725
1726 if (new_doc_id == 0) {
1727 ib::error() << "InnoDB FTS: Doc ID cannot be 0";
1728 return(DB_FTS_INVALID_DOCID);
1729 }
1730 row_fts_do_update(trx, table, old_doc_id, new_doc_id);
1731 }
1732
1733 DBUG_RETURN(DB_SUCCESS);
1734}
1735
1736/*********************************************************************//**
1737Initialize the Doc ID system for FK table with FTS index */
1738static
1739void
1740init_fts_doc_id_for_ref(
1741/*====================*/
1742 dict_table_t* table, /*!< in: table */
1743 ulint* depth) /*!< in: recusive call depth */
1744{
1745 dict_foreign_t* foreign;
1746
1747 table->fk_max_recusive_level = 0;
1748
1749 (*depth)++;
1750
1751 /* Limit on tables involved in cascading delete/update */
1752 if (*depth > FK_MAX_CASCADE_DEL) {
1753 return;
1754 }
1755
1756 /* Loop through this table's referenced list and also
1757 recursively traverse each table's foreign table list */
1758 for (dict_foreign_set::iterator it = table->referenced_set.begin();
1759 it != table->referenced_set.end();
1760 ++it) {
1761
1762 foreign = *it;
1763
1764 ut_ad(foreign->foreign_table != NULL);
1765
1766 if (foreign->foreign_table->fts != NULL) {
1767 fts_init_doc_id(foreign->foreign_table);
1768 }
1769
1770 if (!foreign->foreign_table->referenced_set.empty()
1771 && foreign->foreign_table != table) {
1772 init_fts_doc_id_for_ref(
1773 foreign->foreign_table, depth);
1774 }
1775 }
1776}
1777
1778/** Does an update or delete of a row for MySQL.
1779@param[in,out] prebuilt prebuilt struct in MySQL handle
1780@return error code or DB_SUCCESS */
1781dberr_t
1782row_update_for_mysql(row_prebuilt_t* prebuilt)
1783{
1784 trx_savept_t savept;
1785 dberr_t err;
1786 que_thr_t* thr;
1787 dict_index_t* clust_index;
1788 upd_node_t* node;
1789 dict_table_t* table = prebuilt->table;
1790 trx_t* trx = prebuilt->trx;
1791 ulint fk_depth = 0;
1792 bool got_s_lock = false;
1793
1794 DBUG_ENTER("row_update_for_mysql");
1795
1796 ut_ad(trx);
1797 ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
1798 ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED);
1799 ut_a(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
1800 ut_ad(table->stat_initialized);
1801
1802 if (!table->is_readable()) {
1803 return(row_mysql_get_table_status(table, trx, true));
1804 }
1805
1806 if (high_level_read_only) {
1807 return(DB_READ_ONLY);
1808 }
1809
1810 DEBUG_SYNC_C("innodb_row_update_for_mysql_begin");
1811
1812 trx->op_info = "updating or deleting";
1813
1814 row_mysql_delay_if_needed();
1815
1816 init_fts_doc_id_for_ref(table, &fk_depth);
1817
1818 if (!table->no_rollback()) {
1819 trx_start_if_not_started_xa(trx, true);
1820 }
1821
1822 if (dict_table_is_referenced_by_foreign_key(table)) {
1823 /* Share lock the data dictionary to prevent any
1824 table dictionary (for foreign constraint) change.
1825 This is similar to row_ins_check_foreign_constraint
1826 check protect by the dictionary lock as well.
1827 In the future, this can be removed once the Foreign
1828 key MDL is implemented */
1829 row_mysql_freeze_data_dictionary(trx);
1830 init_fts_doc_id_for_ref(table, &fk_depth);
1831 row_mysql_unfreeze_data_dictionary(trx);
1832 }
1833
1834 node = prebuilt->upd_node;
1835 const bool is_delete = node->is_delete == PLAIN_DELETE;
1836 ut_ad(node->table == table);
1837
1838 clust_index = dict_table_get_first_index(table);
1839
1840 if (prebuilt->pcur->btr_cur.index == clust_index) {
1841 btr_pcur_copy_stored_position(node->pcur, prebuilt->pcur);
1842 } else {
1843 btr_pcur_copy_stored_position(node->pcur,
1844 prebuilt->clust_pcur);
1845 }
1846
1847 ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
1848
1849 /* MySQL seems to call rnd_pos before updating each row it
1850 has cached: we can get the correct cursor position from
1851 prebuilt->pcur; NOTE that we cannot build the row reference
1852 from mysql_rec if the clustered index was automatically
1853 generated for the table: MySQL does not know anything about
1854 the row id used as the clustered index key */
1855
1856 savept = trx_savept_take(trx);
1857
1858 thr = que_fork_get_first_thr(prebuilt->upd_graph);
1859
1860 node->state = UPD_NODE_UPDATE_CLUSTERED;
1861
1862 ut_ad(!prebuilt->sql_stat_start);
1863
1864 que_thr_move_to_run_state_for_mysql(thr, trx);
1865
1866 ut_ad(!prebuilt->versioned_write || node->table->versioned());
1867
1868 if (prebuilt->versioned_write) {
1869 if (node->is_delete == VERSIONED_DELETE) {
1870 node->make_versioned_delete(trx);
1871 } else if (node->update->affects_versioned()) {
1872 node->make_versioned_update(trx);
1873 }
1874 }
1875
1876 for (;;) {
1877 thr->run_node = node;
1878 thr->prev_node = node;
1879 thr->fk_cascade_depth = 0;
1880
1881 row_upd_step(thr);
1882
1883 err = trx->error_state;
1884
1885 if (err == DB_SUCCESS) {
1886 break;
1887 }
1888
1889 que_thr_stop_for_mysql(thr);
1890
1891 if (err == DB_RECORD_NOT_FOUND) {
1892 trx->error_state = DB_SUCCESS;
1893 goto error;
1894 }
1895
1896 thr->lock_state= QUE_THR_LOCK_ROW;
1897
1898 DEBUG_SYNC(trx->mysql_thd, "row_update_for_mysql_error");
1899
1900 bool was_lock_wait = row_mysql_handle_errors(
1901 &err, trx, thr, &savept);
1902 thr->lock_state= QUE_THR_LOCK_NOLOCK;
1903
1904 if (!was_lock_wait) {
1905 goto error;
1906 }
1907 }
1908
1909 que_thr_stop_for_mysql_no_error(thr, trx);
1910
1911 if (dict_table_has_fts_index(table)
1912 && trx->fts_next_doc_id != UINT64_UNDEFINED) {
1913 err = row_fts_update_or_delete(prebuilt);
1914 if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
1915 ut_ad(!"unexpected error");
1916 goto error;
1917 }
1918 }
1919
1920 /* Completed cascading operations (if any) */
1921 if (got_s_lock) {
1922 row_mysql_unfreeze_data_dictionary(trx);
1923 }
1924
1925 bool update_statistics;
1926 ut_ad(is_delete == (node->is_delete == PLAIN_DELETE));
1927
1928 if (is_delete) {
1929 /* Not protected by dict_table_stats_lock() for performance
1930 reasons, we would rather get garbage in stat_n_rows (which is
1931 just an estimate anyway) than protecting the following code
1932 with a latch. */
1933 dict_table_n_rows_dec(prebuilt->table);
1934
1935 if (table->is_system_db) {
1936 srv_stats.n_system_rows_deleted.inc(size_t(trx->id));
1937 } else {
1938 srv_stats.n_rows_deleted.inc(size_t(trx->id));
1939 }
1940
1941 update_statistics = !srv_stats_include_delete_marked;
1942 } else {
1943 if (table->is_system_db) {
1944 srv_stats.n_system_rows_updated.inc(size_t(trx->id));
1945 } else {
1946 srv_stats.n_rows_updated.inc(size_t(trx->id));
1947 }
1948
1949 update_statistics
1950 = !(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE);
1951 }
1952
1953 if (update_statistics) {
1954 dict_stats_update_if_needed(prebuilt->table);
1955 } else {
1956 /* Always update the table modification counter. */
1957 prebuilt->table->stat_modified_counter++;
1958 }
1959
1960 trx->op_info = "";
1961
1962 DBUG_RETURN(err);
1963
1964error:
1965 trx->op_info = "";
1966 if (got_s_lock) {
1967 row_mysql_unfreeze_data_dictionary(trx);
1968 }
1969
1970 DBUG_RETURN(err);
1971}
1972
1973/** This can only be used when srv_locks_unsafe_for_binlog is TRUE or this
1974session is using a READ COMMITTED or READ UNCOMMITTED isolation level.
1975Before calling this function row_search_for_mysql() must have
1976initialized prebuilt->new_rec_locks to store the information which new
1977record locks really were set. This function removes a newly set
1978clustered index record lock under prebuilt->pcur or
1979prebuilt->clust_pcur. Thus, this implements a 'mini-rollback' that
1980releases the latest clustered index record lock we set.
1981@param[in,out] prebuilt prebuilt struct in MySQL handle
1982@param[in] has_latches_on_recs TRUE if called so that we have the
1983 latches on the records under pcur
1984 and clust_pcur, and we do not need
1985 to reposition the cursors. */
1986void
1987row_unlock_for_mysql(
1988 row_prebuilt_t* prebuilt,
1989 ibool has_latches_on_recs)
1990{
1991 btr_pcur_t* pcur = prebuilt->pcur;
1992 btr_pcur_t* clust_pcur = prebuilt->clust_pcur;
1993 trx_t* trx = prebuilt->trx;
1994
1995 ut_ad(prebuilt != NULL);
1996 ut_ad(trx != NULL);
1997
1998 if (UNIV_UNLIKELY
1999 (!srv_locks_unsafe_for_binlog
2000 && trx->isolation_level > TRX_ISO_READ_COMMITTED)) {
2001
2002 ib::error() << "Calling row_unlock_for_mysql though"
2003 " innodb_locks_unsafe_for_binlog is FALSE and this"
2004 " session is not using READ COMMITTED isolation"
2005 " level.";
2006 return;
2007 }
2008 if (dict_index_is_spatial(prebuilt->index)) {
2009 return;
2010 }
2011
2012 trx->op_info = "unlock_row";
2013
2014 if (prebuilt->new_rec_locks >= 1) {
2015
2016 const rec_t* rec;
2017 dict_index_t* index;
2018 trx_id_t rec_trx_id;
2019 mtr_t mtr;
2020
2021 mtr_start(&mtr);
2022
2023 /* Restore the cursor position and find the record */
2024
2025 if (!has_latches_on_recs) {
2026 btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
2027 }
2028
2029 rec = btr_pcur_get_rec(pcur);
2030 index = btr_pcur_get_btr_cur(pcur)->index;
2031
2032 if (prebuilt->new_rec_locks >= 2) {
2033 /* Restore the cursor position and find the record
2034 in the clustered index. */
2035
2036 if (!has_latches_on_recs) {
2037 btr_pcur_restore_position(BTR_SEARCH_LEAF,
2038 clust_pcur, &mtr);
2039 }
2040
2041 rec = btr_pcur_get_rec(clust_pcur);
2042 index = btr_pcur_get_btr_cur(clust_pcur)->index;
2043 }
2044
2045 if (!dict_index_is_clust(index)) {
2046 /* This is not a clustered index record. We
2047 do not know how to unlock the record. */
2048 goto no_unlock;
2049 }
2050
2051 /* If the record has been modified by this
2052 transaction, do not unlock it. */
2053
2054 if (index->trx_id_offset) {
2055 rec_trx_id = trx_read_trx_id(rec
2056 + index->trx_id_offset);
2057 } else {
2058 mem_heap_t* heap = NULL;
2059 ulint offsets_[REC_OFFS_NORMAL_SIZE];
2060 ulint* offsets = offsets_;
2061
2062 rec_offs_init(offsets_);
2063 offsets = rec_get_offsets(rec, index, offsets, true,
2064 ULINT_UNDEFINED, &heap);
2065
2066 rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
2067
2068 if (UNIV_LIKELY_NULL(heap)) {
2069 mem_heap_free(heap);
2070 }
2071 }
2072
2073 if (rec_trx_id != trx->id) {
2074 /* We did not update the record: unlock it */
2075
2076 rec = btr_pcur_get_rec(pcur);
2077
2078 lock_rec_unlock(
2079 trx,
2080 btr_pcur_get_block(pcur),
2081 rec,
2082 static_cast<enum lock_mode>(
2083 prebuilt->select_lock_type));
2084
2085 if (prebuilt->new_rec_locks >= 2) {
2086 rec = btr_pcur_get_rec(clust_pcur);
2087
2088 lock_rec_unlock(
2089 trx,
2090 btr_pcur_get_block(clust_pcur),
2091 rec,
2092 static_cast<enum lock_mode>(
2093 prebuilt->select_lock_type));
2094 }
2095 }
2096no_unlock:
2097 mtr_commit(&mtr);
2098 }
2099
2100 trx->op_info = "";
2101}
2102
2103/*********************************************************************//**
2104Locks the data dictionary in shared mode from modifications, for performing
2105foreign key check, rollback, or other operation invisible to MySQL. */
2106void
2107row_mysql_freeze_data_dictionary_func(
2108/*==================================*/
2109 trx_t* trx, /*!< in/out: transaction */
2110 const char* file, /*!< in: file name */
2111 unsigned line) /*!< in: line number */
2112{
2113 ut_a(trx->dict_operation_lock_mode == 0);
2114
2115 rw_lock_s_lock_inline(dict_operation_lock, 0, file, line);
2116
2117 trx->dict_operation_lock_mode = RW_S_LATCH;
2118}
2119
2120/*********************************************************************//**
2121Unlocks the data dictionary shared lock. */
2122void
2123row_mysql_unfreeze_data_dictionary(
2124/*===============================*/
2125 trx_t* trx) /*!< in/out: transaction */
2126{
2127 ut_ad(lock_trx_has_sys_table_locks(trx) == NULL);
2128
2129 ut_a(trx->dict_operation_lock_mode == RW_S_LATCH);
2130
2131 rw_lock_s_unlock(dict_operation_lock);
2132
2133 trx->dict_operation_lock_mode = 0;
2134}
2135
2136/** Write query start time as SQL field data to a buffer. Needed by InnoDB.
2137@param thd Thread object
2138@param buf Buffer to hold start time data */
2139void thd_get_query_start_data(THD *thd, char *buf);
2140
2141/** Function restores btr_pcur_t, creates dtuple_t from rec_t,
2142sets row_end = CURRENT_TIMESTAMP/trx->id, inserts it to a table and updates
2143table statistics.
2144This is used in UPDATE CASCADE/SET NULL of a system versioning table.
2145@param[in] thr current query thread
2146@param[in] node a node which just updated a row in a foreign table
2147@return DB_SUCCESS or some error */
2148static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
2149{
2150 const trx_t* trx = thr_get_trx(thr);
2151 dict_table_t* table = node->table;
2152 ut_ad(table->versioned());
2153
2154 dtuple_t* row = node->historical_row;
2155 ut_ad(row);
2156 node->historical_row = NULL;
2157
2158 ins_node_t* insert_node =
2159 ins_node_create(INS_DIRECT, table, node->historical_heap);
2160
2161 ins_node_set_new_row(insert_node, row);
2162
2163 dfield_t* row_end = dtuple_get_nth_field(row, table->vers_end);
2164 char row_end_data[8];
2165 if (dict_table_get_nth_col(table, table->vers_end)->vers_native()) {
2166 mach_write_to_8(row_end_data, trx->id);
2167 dfield_set_data(row_end, row_end_data, 8);
2168 } else {
2169 thd_get_query_start_data(trx->mysql_thd, row_end_data);
2170 dfield_set_data(row_end, row_end_data, 7);
2171 }
2172
2173 for (;;) {
2174 thr->run_node = insert_node;
2175 thr->prev_node = insert_node;
2176
2177 row_ins_step(thr);
2178
2179 switch (trx->error_state) {
2180 case DB_LOCK_WAIT:
2181 que_thr_stop_for_mysql(thr);
2182 lock_wait_suspend_thread(thr);
2183
2184 if (trx->error_state == DB_SUCCESS) {
2185 continue;
2186 }
2187
2188 /* fall through */
2189 default:
2190 /* Other errors are handled for the parent node. */
2191 thr->fk_cascade_depth = 0;
2192 goto exit;
2193
2194 case DB_SUCCESS:
2195 srv_stats.n_rows_inserted.inc(
2196 static_cast<size_t>(trx->id));
2197 dict_stats_update_if_needed(table);
2198 goto exit;
2199 }
2200 }
2201exit:
2202 mem_heap_free(node->historical_heap);
2203 node->historical_heap = NULL;
2204 return trx->error_state;
2205}
2206
2207/**********************************************************************//**
2208Does a cascaded delete or set null in a foreign key operation.
2209@return error code or DB_SUCCESS */
2210dberr_t
2211row_update_cascade_for_mysql(
2212/*=========================*/
2213 que_thr_t* thr, /*!< in: query thread */
2214 upd_node_t* node, /*!< in: update node used in the cascade
2215 or set null operation */
2216 dict_table_t* table) /*!< in: table where we do the operation */
2217{
2218 /* Increment fk_cascade_depth to record the recursive call depth on
2219 a single update/delete that affects multiple tables chained
2220 together with foreign key relations. */
2221
2222 if (++thr->fk_cascade_depth > FK_MAX_CASCADE_DEL) {
2223 return(DB_FOREIGN_EXCEED_MAX_CASCADE);
2224 }
2225
2226 const trx_t* trx = thr_get_trx(thr);
2227
2228 if (table->versioned()) {
2229 if (node->is_delete == PLAIN_DELETE) {
2230 node->make_versioned_delete(trx);
2231 } else if (node->update->affects_versioned()) {
2232 dberr_t err = row_update_vers_insert(thr, node);
2233 if (err != DB_SUCCESS) {
2234 return err;
2235 }
2236 node->make_versioned_update(trx);
2237 }
2238 }
2239
2240 for (;;) {
2241 thr->run_node = node;
2242 thr->prev_node = node;
2243
2244 DEBUG_SYNC_C("foreign_constraint_update_cascade");
2245 {
2246 TABLE *mysql_table = thr->prebuilt->m_mysql_table;
2247 thr->prebuilt->m_mysql_table = NULL;
2248 row_upd_step(thr);
2249 thr->prebuilt->m_mysql_table = mysql_table;
2250 }
2251
2252 switch (trx->error_state) {
2253 case DB_LOCK_WAIT:
2254 que_thr_stop_for_mysql(thr);
2255 lock_wait_suspend_thread(thr);
2256
2257 if (trx->error_state == DB_SUCCESS) {
2258 continue;
2259 }
2260
2261 /* fall through */
2262 default:
2263 /* Other errors are handled for the parent node. */
2264 thr->fk_cascade_depth = 0;
2265 return trx->error_state;
2266
2267 case DB_SUCCESS:
2268 thr->fk_cascade_depth = 0;
2269 bool stats;
2270
2271 if (node->is_delete == PLAIN_DELETE) {
2272 /* Not protected by
2273 dict_table_stats_lock() for
2274 performance reasons, we would rather
2275 get garbage in stat_n_rows (which is
2276 just an estimate anyway) than
2277 protecting the following code with a
2278 latch. */
2279 dict_table_n_rows_dec(node->table);
2280
2281 stats = !srv_stats_include_delete_marked;
2282 srv_stats.n_rows_deleted.inc(size_t(trx->id));
2283 } else {
2284 stats = !(node->cmpl_info
2285 & UPD_NODE_NO_ORD_CHANGE);
2286 srv_stats.n_rows_updated.inc(size_t(trx->id));
2287 }
2288
2289 if (stats) {
2290 dict_stats_update_if_needed(node->table);
2291 } else {
2292 /* Always update the table
2293 modification counter. */
2294 node->table->stat_modified_counter++;
2295 }
2296
2297 return(DB_SUCCESS);
2298 }
2299 }
2300}
2301
2302/*********************************************************************//**
2303Locks the data dictionary exclusively for performing a table create or other
2304data dictionary modification operation. */
2305void
2306row_mysql_lock_data_dictionary_func(
2307/*================================*/
2308 trx_t* trx, /*!< in/out: transaction */
2309 const char* file, /*!< in: file name */
2310 unsigned line) /*!< in: line number */
2311{
2312 ut_a(trx->dict_operation_lock_mode == 0
2313 || trx->dict_operation_lock_mode == RW_X_LATCH);
2314
2315 /* Serialize data dictionary operations with dictionary mutex:
2316 no deadlocks or lock waits can occur then in these operations */
2317
2318 rw_lock_x_lock_inline(dict_operation_lock, 0, file, line);
2319 trx->dict_operation_lock_mode = RW_X_LATCH;
2320
2321 mutex_enter(&dict_sys->mutex);
2322}
2323
2324/*********************************************************************//**
2325Unlocks the data dictionary exclusive lock. */
2326void
2327row_mysql_unlock_data_dictionary(
2328/*=============================*/
2329 trx_t* trx) /*!< in/out: transaction */
2330{
2331 ut_ad(lock_trx_has_sys_table_locks(trx) == NULL);
2332
2333 ut_a(trx->dict_operation_lock_mode == RW_X_LATCH);
2334
2335 /* Serialize data dictionary operations with dictionary mutex:
2336 no deadlocks can occur then in these operations */
2337
2338 mutex_exit(&dict_sys->mutex);
2339 rw_lock_x_unlock(dict_operation_lock);
2340
2341 trx->dict_operation_lock_mode = 0;
2342}
2343
2344/*********************************************************************//**
2345Creates a table for MySQL. On failure the transaction will be rolled back
2346and the 'table' object will be freed.
2347@return error code or DB_SUCCESS */
2348dberr_t
2349row_create_table_for_mysql(
2350/*=======================*/
2351 dict_table_t* table, /*!< in, own: table definition
2352 (will be freed, or on DB_SUCCESS
2353 added to the data dictionary cache) */
2354 trx_t* trx, /*!< in/out: transaction */
2355 fil_encryption_t mode, /*!< in: encryption mode */
2356 uint32_t key_id) /*!< in: encryption key_id */
2357{
2358 tab_node_t* node;
2359 mem_heap_t* heap;
2360 que_thr_t* thr;
2361 dberr_t err;
2362
2363 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
2364 ut_ad(mutex_own(&dict_sys->mutex));
2365 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
2366
2367 DBUG_EXECUTE_IF(
2368 "ib_create_table_fail_at_start_of_row_create_table_for_mysql",
2369 goto err_exit;
2370 );
2371
2372 trx->op_info = "creating table";
2373
2374 if (row_mysql_is_system_table(table->name.m_name)) {
2375
2376 ib::error() << "Trying to create a MySQL system table "
2377 << table->name << " of type InnoDB. MySQL system"
2378 " tables must be of the MyISAM type!";
2379#ifndef DBUG_OFF
2380err_exit:
2381#endif /* !DBUG_OFF */
2382 dict_mem_table_free(table);
2383
2384 trx->op_info = "";
2385
2386 return(DB_ERROR);
2387 }
2388
2389 trx_start_if_not_started_xa(trx, true);
2390
2391 heap = mem_heap_create(512);
2392
2393 switch (trx_get_dict_operation(trx)) {
2394 case TRX_DICT_OP_NONE:
2395 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
2396 case TRX_DICT_OP_TABLE:
2397 break;
2398 case TRX_DICT_OP_INDEX:
2399 /* If the transaction was previously flagged as
2400 TRX_DICT_OP_INDEX, we should be creating auxiliary
2401 tables for full-text indexes. */
2402 ut_ad(strstr(table->name.m_name, "/FTS_") != NULL);
2403 }
2404
2405 node = tab_create_graph_create(table, heap, mode, key_id);
2406
2407 thr = pars_complete_graph_for_exec(node, trx, heap, NULL);
2408
2409 ut_a(thr == que_fork_start_command(
2410 static_cast<que_fork_t*>(que_node_get_parent(thr))));
2411
2412 que_run_threads(thr);
2413
2414 err = trx->error_state;
2415
2416 /* Update SYS_TABLESPACES and SYS_DATAFILES if a new file-per-table
2417 tablespace was created. */
2418 if (err == DB_SUCCESS && dict_table_is_file_per_table(table)) {
2419 err = dict_replace_tablespace_in_dictionary(
2420 table->space_id, table->name.m_name,
2421 table->space->flags,
2422 table->space->chain.start->name, trx);
2423
2424 if (err != DB_SUCCESS) {
2425
2426 /* We must delete the link file. */
2427 RemoteDatafile::delete_link_file(table->name.m_name);
2428 }
2429 }
2430
2431 switch (err) {
2432 case DB_SUCCESS:
2433 break;
2434 case DB_OUT_OF_FILE_SPACE:
2435 trx->error_state = DB_SUCCESS;
2436 trx_rollback_to_savepoint(trx, NULL);
2437
2438 ib::warn() << "Cannot create table "
2439 << table->name
2440 << " because tablespace full";
2441
2442 if (dict_table_open_on_name(table->name.m_name, TRUE, FALSE,
2443 DICT_ERR_IGNORE_NONE)) {
2444
2445 dict_table_close_and_drop(trx, table);
2446 } else {
2447 dict_mem_table_free(table);
2448 }
2449
2450 break;
2451
2452 case DB_UNSUPPORTED:
2453 case DB_TOO_MANY_CONCURRENT_TRXS:
2454 /* We already have .ibd file here. it should be deleted. */
2455
2456 if (dict_table_is_file_per_table(table)
2457 && fil_delete_tablespace(table->space->id) != DB_SUCCESS) {
2458 ib::error() << "Cannot delete the file of table "
2459 << table->name;
2460 }
2461 /* fall through */
2462
2463 case DB_DUPLICATE_KEY:
2464 case DB_TABLESPACE_EXISTS:
2465 default:
2466 trx->error_state = DB_SUCCESS;
2467 trx_rollback_to_savepoint(trx, NULL);
2468 dict_mem_table_free(table);
2469 break;
2470 }
2471
2472 que_graph_free((que_t*) que_node_get_parent(thr));
2473
2474 trx->op_info = "";
2475
2476 return(err);
2477}
2478
2479/*********************************************************************//**
2480Does an index creation operation for MySQL. TODO: currently failure
2481to create an index results in dropping the whole table! This is no problem
2482currently as all indexes must be created at the same time as the table.
2483@return error number or DB_SUCCESS */
2484dberr_t
2485row_create_index_for_mysql(
2486/*=======================*/
2487 dict_index_t* index, /*!< in, own: index definition
2488 (will be freed) */
2489 trx_t* trx, /*!< in: transaction handle */
2490 const ulint* field_lengths) /*!< in: if not NULL, must contain
2491 dict_index_get_n_fields(index)
2492 actual field lengths for the
2493 index columns, which are
2494 then checked for not being too
2495 large. */
2496{
2497 ind_node_t* node;
2498 mem_heap_t* heap;
2499 que_thr_t* thr;
2500 dberr_t err;
2501 ulint i;
2502 ulint len;
2503 dict_table_t* table = index->table;
2504
2505 trx->op_info = "creating index";
2506
2507 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
2508 ut_ad(mutex_own(&dict_sys->mutex));
2509
2510
2511 if (!table->is_temporary()) {
2512 trx_start_if_not_started_xa(trx, true);
2513 }
2514
2515 for (i = 0; i < index->n_def; i++) {
2516 /* Check that prefix_len and actual length
2517 < DICT_MAX_INDEX_COL_LEN */
2518
2519 len = dict_index_get_nth_field(index, i)->prefix_len;
2520
2521 if (field_lengths && field_lengths[i]) {
2522 len = ut_max(len, field_lengths[i]);
2523 }
2524
2525 DBUG_EXECUTE_IF(
2526 "ib_create_table_fail_at_create_index",
2527 len = DICT_MAX_FIELD_LEN_BY_FORMAT(table) + 1;
2528 );
2529
2530 /* Column or prefix length exceeds maximum column length */
2531 if (len > (ulint) DICT_MAX_FIELD_LEN_BY_FORMAT(table)) {
2532 err = DB_TOO_BIG_INDEX_COL;
2533
2534 dict_mem_index_free(index);
2535 goto error_handling;
2536 }
2537 }
2538
2539 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
2540
2541 /* For temp-table we avoid insertion into SYSTEM TABLES to
2542 maintain performance and so we have separate path that directly
2543 just updates dictonary cache. */
2544 if (!table->is_temporary()) {
2545 /* Note that the space id where we store the index is
2546 inherited from the table in dict_build_index_def_step()
2547 in dict0crea.cc. */
2548
2549 heap = mem_heap_create(512);
2550 node = ind_create_graph_create(index, table->name.m_name,
2551 heap);
2552
2553 thr = pars_complete_graph_for_exec(node, trx, heap, NULL);
2554
2555 ut_a(thr == que_fork_start_command(
2556 static_cast<que_fork_t*>(
2557 que_node_get_parent(thr))));
2558
2559 que_run_threads(thr);
2560
2561 err = trx->error_state;
2562
2563 index = node->index;
2564
2565 ut_ad(!index == (err != DB_SUCCESS));
2566
2567 que_graph_free((que_t*) que_node_get_parent(thr));
2568
2569 if (index && (index->type & DICT_FTS)) {
2570 err = fts_create_index_tables(trx, index, table->id);
2571 }
2572 } else {
2573 dict_build_index_def(table, index, trx);
2574
2575 /* add index to dictionary cache and also free index object. */
2576 index = dict_index_add_to_cache(
2577 index, FIL_NULL, trx_is_strict(trx), &err);
2578 if (index) {
2579 ut_ad(!index->is_instant());
2580 index->n_core_null_bytes = UT_BITS_IN_BYTES(
2581 unsigned(index->n_nullable));
2582
2583 err = dict_create_index_tree_in_mem(index, trx);
2584
2585 if (err != DB_SUCCESS) {
2586 dict_index_remove_from_cache(table, index);
2587 }
2588 }
2589 }
2590
2591 if (err != DB_SUCCESS) {
2592error_handling:
2593 /* We have special error handling here */
2594
2595 trx->error_state = DB_SUCCESS;
2596
2597 if (trx_is_started(trx)) {
2598
2599 trx_rollback_to_savepoint(trx, NULL);
2600 }
2601
2602 row_drop_table_for_mysql(table->name.m_name, trx, FALSE, true);
2603
2604 if (trx_is_started(trx)) {
2605
2606 trx_commit_for_mysql(trx);
2607 }
2608
2609 trx->error_state = DB_SUCCESS;
2610 }
2611
2612 trx->op_info = "";
2613
2614 return(err);
2615}
2616
2617/*********************************************************************//**
2618Scans a table create SQL string and adds to the data dictionary
2619the foreign key constraints declared in the string. This function
2620should be called after the indexes for a table have been created.
2621Each foreign key constraint must be accompanied with indexes in
2622bot participating tables. The indexes are allowed to contain more
2623fields than mentioned in the constraint.
2624
2625@param[in] trx transaction
2626@param[in] sql_string table create statement where
2627 foreign keys are declared like:
2628 FOREIGN KEY (a, b) REFERENCES table2(c, d),
2629 table2 can be written also with the database
2630 name before it: test.table2; the default
2631 database id the database of parameter name
2632@param[in] sql_length length of sql_string
2633@param[in] name table full name in normalized form
2634@param[in] reject_fks if TRUE, fail with error code
2635 DB_CANNOT_ADD_CONSTRAINT if any
2636 foreign keys are found.
2637@return error code or DB_SUCCESS */
2638dberr_t
2639row_table_add_foreign_constraints(
2640 trx_t* trx,
2641 const char* sql_string,
2642 size_t sql_length,
2643 const char* name,
2644 ibool reject_fks)
2645{
2646 dberr_t err;
2647
2648 DBUG_ENTER("row_table_add_foreign_constraints");
2649
2650 ut_ad(mutex_own(&dict_sys->mutex));
2651 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
2652 ut_a(sql_string);
2653
2654 err = dict_create_foreign_constraints(
2655 trx, sql_string, sql_length, name, reject_fks);
2656
2657 DBUG_EXECUTE_IF("ib_table_add_foreign_fail",
2658 err = DB_DUPLICATE_KEY;);
2659
2660 DEBUG_SYNC_C("table_add_foreign_constraints");
2661
2662 if (err == DB_SUCCESS) {
2663 /* Check that also referencing constraints are ok */
2664 dict_names_t fk_tables;
2665 err = dict_load_foreigns(name, NULL, false, true,
2666 DICT_ERR_IGNORE_NONE, fk_tables);
2667
2668 while (err == DB_SUCCESS && !fk_tables.empty()) {
2669 dict_load_table(fk_tables.front(), true,
2670 DICT_ERR_IGNORE_NONE);
2671 fk_tables.pop_front();
2672 }
2673 }
2674
2675 if (err != DB_SUCCESS) {
2676 /* We have special error handling here */
2677
2678 trx->error_state = DB_SUCCESS;
2679
2680 if (trx_is_started(trx)) {
2681
2682 trx_rollback_to_savepoint(trx, NULL);
2683 }
2684
2685 row_drop_table_for_mysql(name, trx, FALSE, true);
2686
2687 if (trx_is_started(trx)) {
2688
2689 trx_commit_for_mysql(trx);
2690 }
2691
2692 trx->error_state = DB_SUCCESS;
2693 }
2694
2695 DBUG_RETURN(err);
2696}
2697
2698/*********************************************************************//**
2699Drops a table for MySQL as a background operation. MySQL relies on Unix
2700in ALTER TABLE to the fact that the table handler does not remove the
2701table before all handles to it has been removed. Furhermore, the MySQL's
2702call to drop table must be non-blocking. Therefore we do the drop table
2703as a background operation, which is taken care of by the master thread
2704in srv0srv.cc.
2705@return error code or DB_SUCCESS */
2706static
2707dberr_t
2708row_drop_table_for_mysql_in_background(
2709/*===================================*/
2710 const char* name) /*!< in: table name */
2711{
2712 dberr_t error;
2713 trx_t* trx;
2714
2715 trx = trx_create();
2716
2717 /* If the original transaction was dropping a table referenced by
2718 foreign keys, we must set the following to be able to drop the
2719 table: */
2720
2721 trx->check_foreigns = false;
2722
2723 /* Try to drop the table in InnoDB */
2724
2725 error = row_drop_table_for_mysql(name, trx, FALSE, FALSE);
2726
2727 trx_commit_for_mysql(trx);
2728
2729 trx_free(trx);
2730
2731 return(error);
2732}
2733
2734/*********************************************************************//**
2735The master thread in srv0srv.cc calls this regularly to drop tables which
2736we must drop in background after queries to them have ended. Such lazy
2737dropping of tables is needed in ALTER TABLE on Unix.
2738@return how many tables dropped + remaining tables in list */
2739ulint
2740row_drop_tables_for_mysql_in_background(void)
2741/*=========================================*/
2742{
2743 row_mysql_drop_t* drop;
2744 dict_table_t* table;
2745 ulint n_tables;
2746 ulint n_tables_dropped = 0;
2747loop:
2748 mutex_enter(&row_drop_list_mutex);
2749
2750 ut_a(row_mysql_drop_list_inited);
2751next:
2752 drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
2753
2754 n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
2755
2756 mutex_exit(&row_drop_list_mutex);
2757
2758 if (drop == NULL) {
2759 /* All tables dropped */
2760
2761 return(n_tables + n_tables_dropped);
2762 }
2763
2764 /* On fast shutdown, just empty the list without dropping tables. */
2765 table = srv_shutdown_state == SRV_SHUTDOWN_NONE || !srv_fast_shutdown
2766 ? dict_table_open_on_id(drop->table_id, FALSE,
2767 DICT_TABLE_OP_OPEN_ONLY_IF_CACHED)
2768 : NULL;
2769
2770 if (!table) {
2771 n_tables_dropped++;
2772 mutex_enter(&row_drop_list_mutex);
2773 UT_LIST_REMOVE(row_mysql_drop_list, drop);
2774 MONITOR_DEC(MONITOR_BACKGROUND_DROP_TABLE);
2775 ut_free(drop);
2776 goto next;
2777 }
2778
2779 ut_a(!table->can_be_evicted);
2780
2781 if (!table->to_be_dropped) {
2782 dict_table_close(table, FALSE, FALSE);
2783
2784 mutex_enter(&row_drop_list_mutex);
2785 UT_LIST_REMOVE(row_mysql_drop_list, drop);
2786 UT_LIST_ADD_LAST(row_mysql_drop_list, drop);
2787 goto next;
2788 }
2789
2790 dict_table_close(table, FALSE, FALSE);
2791
2792 if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
2793 table->name.m_name)) {
2794 /* If the DROP fails for some table, we return, and let the
2795 main thread retry later */
2796 return(n_tables + n_tables_dropped);
2797 }
2798
2799 goto loop;
2800}
2801
2802/*********************************************************************//**
2803Get the background drop list length. NOTE: the caller must own the
2804drop list mutex!
2805@return how many tables in list */
2806ulint
2807row_get_background_drop_list_len_low(void)
2808/*======================================*/
2809{
2810 ulint len;
2811
2812 mutex_enter(&row_drop_list_mutex);
2813
2814 ut_a(row_mysql_drop_list_inited);
2815
2816 len = UT_LIST_GET_LEN(row_mysql_drop_list);
2817
2818 mutex_exit(&row_drop_list_mutex);
2819
2820 return(len);
2821}
2822
2823/** Drop garbage tables during recovery. */
2824void
2825row_mysql_drop_garbage_tables()
2826{
2827 mem_heap_t* heap = mem_heap_create(FN_REFLEN);
2828 btr_pcur_t pcur;
2829 mtr_t mtr;
2830 trx_t* trx = trx_create();
2831 trx->op_info = "dropping garbage tables";
2832 row_mysql_lock_data_dictionary(trx);
2833
2834 mtr.start();
2835 btr_pcur_open_at_index_side(
2836 true, dict_table_get_first_index(dict_sys->sys_tables),
2837 BTR_SEARCH_LEAF, &pcur, true, 0, &mtr);
2838
2839 for (;;) {
2840 const rec_t* rec;
2841 const byte* field;
2842 ulint len;
2843 const char* table_name;
2844
2845 btr_pcur_move_to_next_user_rec(&pcur, &mtr);
2846
2847 if (!btr_pcur_is_on_user_rec(&pcur)) {
2848 break;
2849 }
2850
2851 rec = btr_pcur_get_rec(&pcur);
2852 if (rec_get_deleted_flag(rec, 0)) {
2853 continue;
2854 }
2855
2856 field = rec_get_nth_field_old(rec, 0/*NAME*/, &len);
2857 if (len == UNIV_SQL_NULL || len == 0) {
2858 /* Corrupted SYS_TABLES.NAME */
2859 continue;
2860 }
2861
2862 table_name = mem_heap_strdupl(
2863 heap,
2864 reinterpret_cast<const char*>(field), len);
2865 if (strstr(table_name, "/" TEMP_FILE_PREFIX "-")) {
2866 btr_pcur_store_position(&pcur, &mtr);
2867 btr_pcur_commit_specify_mtr(&pcur, &mtr);
2868
2869 if (dict_load_table(table_name, true,
2870 DICT_ERR_IGNORE_ALL)) {
2871 row_drop_table_for_mysql(
2872 table_name, trx, FALSE, FALSE);
2873 trx_commit_for_mysql(trx);
2874 }
2875
2876 mtr.start();
2877 btr_pcur_restore_position(BTR_SEARCH_LEAF,
2878 &pcur, &mtr);
2879 }
2880
2881 mem_heap_empty(heap);
2882 }
2883
2884 btr_pcur_close(&pcur);
2885 mtr.commit();
2886 row_mysql_unlock_data_dictionary(trx);
2887 trx_free(trx);
2888 mem_heap_free(heap);
2889}
2890
2891/*********************************************************************//**
2892If a table is not yet in the drop list, adds the table to the list of tables
2893which the master thread drops in background. We need this on Unix because in
2894ALTER TABLE MySQL may call drop table even if the table has running queries on
2895it. Also, if there are running foreign key checks on the table, we drop the
2896table lazily.
2897@return whether background DROP TABLE was scheduled for the first time */
2898static
2899bool
2900row_add_table_to_background_drop_list(table_id_t table_id)
2901{
2902 row_mysql_drop_t* drop;
2903 bool added = true;
2904
2905 mutex_enter(&row_drop_list_mutex);
2906
2907 ut_a(row_mysql_drop_list_inited);
2908
2909 /* Look if the table already is in the drop list */
2910 for (drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
2911 drop != NULL;
2912 drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop)) {
2913
2914 if (drop->table_id == table_id) {
2915 added = false;
2916 goto func_exit;
2917 }
2918 }
2919
2920 drop = static_cast<row_mysql_drop_t*>(ut_malloc_nokey(sizeof *drop));
2921 drop->table_id = table_id;
2922
2923 UT_LIST_ADD_LAST(row_mysql_drop_list, drop);
2924
2925 MONITOR_INC(MONITOR_BACKGROUND_DROP_TABLE);
2926func_exit:
2927 mutex_exit(&row_drop_list_mutex);
2928 return added;
2929}
2930
2931/** Reassigns the table identifier of a table.
2932@param[in,out] table table
2933@param[in,out] trx transaction
2934@param[out] new_id new table id
2935@return error code or DB_SUCCESS */
2936dberr_t
2937row_mysql_table_id_reassign(
2938 dict_table_t* table,
2939 trx_t* trx,
2940 table_id_t* new_id)
2941{
2942 dberr_t err;
2943 pars_info_t* info = pars_info_create();
2944
2945 dict_hdr_get_new_id(new_id, NULL, NULL, table, false);
2946
2947 pars_info_add_ull_literal(info, "old_id", table->id);
2948 pars_info_add_ull_literal(info, "new_id", *new_id);
2949
2950 err = que_eval_sql(
2951 info,
2952 "PROCEDURE RENUMBER_TABLE_PROC () IS\n"
2953 "BEGIN\n"
2954 "UPDATE SYS_TABLES SET ID = :new_id\n"
2955 " WHERE ID = :old_id;\n"
2956 "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
2957 " WHERE TABLE_ID = :old_id;\n"
2958 "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
2959 " WHERE TABLE_ID = :old_id;\n"
2960 "UPDATE SYS_VIRTUAL SET TABLE_ID = :new_id\n"
2961 " WHERE TABLE_ID = :old_id;\n"
2962 "END;\n", FALSE, trx);
2963
2964 return(err);
2965}
2966
2967/*********************************************************************//**
2968Setup the pre-requisites for DISCARD TABLESPACE. It will start the transaction,
2969acquire the data dictionary lock in X mode and open the table.
2970@return table instance or 0 if not found. */
2971static
2972dict_table_t*
2973row_discard_tablespace_begin(
2974/*=========================*/
2975 const char* name, /*!< in: table name */
2976 trx_t* trx) /*!< in: transaction handle */
2977{
2978 trx->op_info = "discarding tablespace";
2979
2980 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
2981
2982 trx_start_if_not_started_xa(trx, true);
2983
2984 /* Serialize data dictionary operations with dictionary mutex:
2985 this is to avoid deadlocks during data dictionary operations */
2986
2987 row_mysql_lock_data_dictionary(trx);
2988
2989 dict_table_t* table;
2990
2991 table = dict_table_open_on_name(
2992 name, TRUE, FALSE, DICT_ERR_IGNORE_NONE);
2993
2994 if (table) {
2995 dict_stats_wait_bg_to_stop_using_table(table, trx);
2996 ut_a(!is_system_tablespace(table->space_id));
2997 ut_ad(!table->n_foreign_key_checks_running);
2998 }
2999
3000 return(table);
3001}
3002
3003/*********************************************************************//**
3004Do the foreign key constraint checks.
3005@return DB_SUCCESS or error code. */
3006static
3007dberr_t
3008row_discard_tablespace_foreign_key_checks(
3009/*======================================*/
3010 const trx_t* trx, /*!< in: transaction handle */
3011 const dict_table_t* table) /*!< in: table to be discarded */
3012{
3013
3014 if (srv_read_only_mode || !trx->check_foreigns) {
3015 return(DB_SUCCESS);
3016 }
3017
3018 /* Check if the table is referenced by foreign key constraints from
3019 some other table (not the table itself) */
3020 dict_foreign_set::const_iterator it
3021 = std::find_if(table->referenced_set.begin(),
3022 table->referenced_set.end(),
3023 dict_foreign_different_tables());
3024
3025 if (it == table->referenced_set.end()) {
3026 return(DB_SUCCESS);
3027 }
3028
3029 const dict_foreign_t* foreign = *it;
3030 FILE* ef = dict_foreign_err_file;
3031
3032 ut_ad(foreign->foreign_table != table);
3033 ut_ad(foreign->referenced_table == table);
3034
3035 /* We only allow discarding a referenced table if
3036 FOREIGN_KEY_CHECKS is set to 0 */
3037
3038 mutex_enter(&dict_foreign_err_mutex);
3039
3040 rewind(ef);
3041
3042 ut_print_timestamp(ef);
3043
3044 fputs(" Cannot DISCARD table ", ef);
3045 ut_print_name(ef, trx, table->name.m_name);
3046 fputs("\n"
3047 "because it is referenced by ", ef);
3048 ut_print_name(ef, trx, foreign->foreign_table_name);
3049 putc('\n', ef);
3050
3051 mutex_exit(&dict_foreign_err_mutex);
3052
3053 return(DB_CANNOT_DROP_CONSTRAINT);
3054}
3055
3056/*********************************************************************//**
3057Cleanup after the DISCARD TABLESPACE operation.
3058@return error code. */
3059static
3060dberr_t
3061row_discard_tablespace_end(
3062/*=======================*/
3063 trx_t* trx, /*!< in/out: transaction handle */
3064 dict_table_t* table, /*!< in/out: table to be discarded */
3065 dberr_t err) /*!< in: error code */
3066{
3067 if (table != 0) {
3068 dict_table_close(table, TRUE, FALSE);
3069 }
3070
3071 DBUG_EXECUTE_IF("ib_discard_before_commit_crash",
3072 log_make_checkpoint_at(LSN_MAX, TRUE);
3073 DBUG_SUICIDE(););
3074
3075 trx_commit_for_mysql(trx);
3076
3077 DBUG_EXECUTE_IF("ib_discard_after_commit_crash",
3078 log_make_checkpoint_at(LSN_MAX, TRUE);
3079 DBUG_SUICIDE(););
3080
3081 row_mysql_unlock_data_dictionary(trx);
3082
3083 trx->op_info = "";
3084
3085 return(err);
3086}
3087
3088/*********************************************************************//**
3089Do the DISCARD TABLESPACE operation.
3090@return DB_SUCCESS or error code. */
3091static
3092dberr_t
3093row_discard_tablespace(
3094/*===================*/
3095 trx_t* trx, /*!< in/out: transaction handle */
3096 dict_table_t* table) /*!< in/out: table to be discarded */
3097{
3098 dberr_t err;
3099
3100 /* How do we prevent crashes caused by ongoing operations on
3101 the table? Old operations could try to access non-existent
3102 pages. MySQL will block all DML on the table using MDL and a
3103 DISCARD will not start unless all existing operations on the
3104 table to be discarded are completed.
3105
3106 1) Acquire the data dictionary latch in X mode. To prevent any
3107 internal operations that MySQL is not aware off and also for
3108 the internal SQL parser.
3109
3110 2) Purge and rollback: we assign a new table id for the
3111 table. Since purge and rollback look for the table based on
3112 the table id, they see the table as 'dropped' and discard
3113 their operations.
3114
3115 3) Insert buffer: we remove all entries for the tablespace in
3116 the insert buffer tree. */
3117
3118 ibuf_delete_for_discarded_space(table->space_id);
3119
3120 table_id_t new_id;
3121
3122 /* Set the TABLESPACE DISCARD flag in the table definition
3123 on disk. */
3124 err = row_import_update_discarded_flag(trx, table->id, true);
3125
3126 if (err != DB_SUCCESS) {
3127 return(err);
3128 }
3129
3130 /* Update the index root pages in the system tables, on disk */
3131 err = row_import_update_index_root(trx, table, true, true);
3132
3133 if (err != DB_SUCCESS) {
3134 return(err);
3135 }
3136
3137 /* Drop all the FTS auxiliary tables. */
3138 if (dict_table_has_fts_index(table)
3139 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)) {
3140
3141 fts_drop_tables(trx, table);
3142 }
3143
3144 /* Assign a new space ID to the table definition so that purge
3145 can ignore the changes. Update the system table on disk. */
3146
3147 err = row_mysql_table_id_reassign(table, trx, &new_id);
3148
3149 if (err != DB_SUCCESS) {
3150 return(err);
3151 }
3152
3153 /* Discard the physical file that is used for the tablespace. */
3154 err = fil_delete_tablespace(table->space_id
3155#ifdef BTR_CUR_HASH_ADAPT
3156 , true
3157#endif /* BTR_CUR_HASH_ADAPT */
3158 );
3159 switch (err) {
3160 case DB_IO_ERROR:
3161 ib::warn() << "ALTER TABLE " << table->name
3162 << " DISCARD TABLESPACE failed to delete file";
3163 break;
3164 case DB_TABLESPACE_NOT_FOUND:
3165 ib::warn() << "ALTER TABLE " << table->name
3166 << " DISCARD TABLESPACE failed to find tablespace";
3167 break;
3168 case DB_SUCCESS:
3169 break;
3170 default:
3171 ut_error;
3172 }
3173
3174 /* All persistent operations successful, update the
3175 data dictionary memory cache. */
3176
3177 table->file_unreadable = true;
3178 table->space = NULL;
3179 table->flags2 |= DICT_TF2_DISCARDED;
3180 dict_table_change_id_in_cache(table, new_id);
3181
3182 /* Reset the root page numbers. */
3183
3184 for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
3185 index != 0;
3186 index = UT_LIST_GET_NEXT(indexes, index)) {
3187 index->page = FIL_NULL;
3188 }
3189 /* If the tablespace did not already exist or we couldn't
3190 write to it, we treat that as a successful DISCARD. It is
3191 unusable anyway. */
3192 return DB_SUCCESS;
3193}
3194
3195/*********************************************************************//**
3196Discards the tablespace of a table which stored in an .ibd file. Discarding
3197means that this function renames the .ibd file and assigns a new table id for
3198the table. Also the file_unreadable flag is set.
3199@return error code or DB_SUCCESS */
3200dberr_t
3201row_discard_tablespace_for_mysql(
3202/*=============================*/
3203 const char* name, /*!< in: table name */
3204 trx_t* trx) /*!< in: transaction handle */
3205{
3206 dberr_t err;
3207 dict_table_t* table;
3208
3209 /* Open the table and start the transaction if not started. */
3210
3211 table = row_discard_tablespace_begin(name, trx);
3212
3213 if (table == 0) {
3214 err = DB_TABLE_NOT_FOUND;
3215 } else if (table->is_temporary()) {
3216
3217 ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_ERROR,
3218 ER_CANNOT_DISCARD_TEMPORARY_TABLE);
3219
3220 err = DB_ERROR;
3221
3222 } else if (table->space_id == TRX_SYS_SPACE) {
3223 char table_name[MAX_FULL_NAME_LEN + 1];
3224
3225 innobase_format_name(
3226 table_name, sizeof(table_name),
3227 table->name.m_name);
3228
3229 ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_ERROR,
3230 ER_TABLE_IN_SYSTEM_TABLESPACE, table_name);
3231
3232 err = DB_ERROR;
3233
3234 } else {
3235 ut_ad(!table->n_foreign_key_checks_running);
3236
3237 /* Do foreign key constraint checks. */
3238
3239 err = row_discard_tablespace_foreign_key_checks(trx, table);
3240
3241 if (err == DB_SUCCESS) {
3242 err = row_discard_tablespace(trx, table);
3243 }
3244 }
3245
3246 return(row_discard_tablespace_end(trx, table, err));
3247}
3248
3249/*********************************************************************//**
3250Sets an exclusive lock on a table.
3251@return error code or DB_SUCCESS */
3252dberr_t
3253row_mysql_lock_table(
3254/*=================*/
3255 trx_t* trx, /*!< in/out: transaction */
3256 dict_table_t* table, /*!< in: table to lock */
3257 enum lock_mode mode, /*!< in: LOCK_X or LOCK_S */
3258 const char* op_info) /*!< in: string for trx->op_info */
3259{
3260 mem_heap_t* heap;
3261 que_thr_t* thr;
3262 dberr_t err;
3263 sel_node_t* node;
3264
3265 ut_ad(trx);
3266 ut_ad(mode == LOCK_X || mode == LOCK_S);
3267
3268 heap = mem_heap_create(512);
3269
3270 trx->op_info = op_info;
3271
3272 node = sel_node_create(heap);
3273 thr = pars_complete_graph_for_exec(node, trx, heap, NULL);
3274 thr->graph->state = QUE_FORK_ACTIVE;
3275
3276 /* We use the select query graph as the dummy graph needed
3277 in the lock module call */
3278
3279 thr = que_fork_get_first_thr(
3280 static_cast<que_fork_t*>(que_node_get_parent(thr)));
3281
3282 que_thr_move_to_run_state_for_mysql(thr, trx);
3283
3284run_again:
3285 thr->run_node = thr;
3286 thr->prev_node = thr->common.parent;
3287
3288 err = lock_table(0, table, mode, thr);
3289
3290 trx->error_state = err;
3291
3292 if (err == DB_SUCCESS) {
3293 que_thr_stop_for_mysql_no_error(thr, trx);
3294 } else {
3295 que_thr_stop_for_mysql(thr);
3296
3297 if (row_mysql_handle_errors(&err, trx, thr, NULL)) {
3298 goto run_again;
3299 }
3300 }
3301
3302 que_graph_free(thr->graph);
3303 trx->op_info = "";
3304
3305 return(err);
3306}
3307
3308static
3309void
3310fil_wait_crypt_bg_threads(
3311 dict_table_t* table)
3312{
3313 time_t start = time(0);
3314 time_t last = start;
3315
3316 while (table->get_ref_count()> 0) {
3317 dict_mutex_exit_for_mysql();
3318 os_thread_sleep(20000);
3319 dict_mutex_enter_for_mysql();
3320 time_t now = time(0);
3321
3322 if (now >= last + 30) {
3323 ib::warn()
3324 << "Waited " << now - start
3325 << " seconds for ref-count on table "
3326 << table->name;
3327 last = now;
3328 }
3329 if (now >= start + 300) {
3330 ib::warn()
3331 << "After " << now - start
3332 << " seconds, gave up waiting "
3333 << "for ref-count on table " << table->name;
3334 break;
3335 }
3336 }
3337}
3338/** Drop ancillary FTS tables as part of dropping a table.
3339@param[in,out] table Table cache entry
3340@param[in,out] trx Transaction handle
3341@return error code or DB_SUCCESS */
3342UNIV_INLINE
3343dberr_t
3344row_drop_ancillary_fts_tables(
3345 dict_table_t* table,
3346 trx_t* trx)
3347{
3348 /* Drop ancillary FTS tables */
3349 if (dict_table_has_fts_index(table)
3350 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)) {
3351
3352 ut_ad(table->get_ref_count() == 0);
3353 ut_ad(trx_is_started(trx));
3354
3355 dberr_t err = fts_drop_tables(trx, table);
3356
3357 if (err != DB_SUCCESS) {
3358 ib::error() << " Unable to remove ancillary FTS"
3359 " tables for table "
3360 << table->name << " : " << ut_strerr(err);
3361
3362 return(err);
3363 }
3364 }
3365
3366 /* The table->fts flag can be set on the table for which
3367 the cluster index is being rebuilt. Such table might not have
3368 DICT_TF2_FTS flag set. So keep this out of above
3369 dict_table_has_fts_index condition */
3370 if (table->fts != NULL) {
3371 /* Need to set TABLE_DICT_LOCKED bit, since
3372 fts_que_graph_free_check_lock would try to acquire
3373 dict mutex lock */
3374 table->fts->fts_status |= TABLE_DICT_LOCKED;
3375
3376 fts_free(table);
3377 }
3378
3379 return(DB_SUCCESS);
3380}
3381
3382/** Drop a table from the memory cache as part of dropping a table.
3383@param[in] tablename A copy of table->name. Used when table == null
3384@param[in,out] table Table cache entry
3385@param[in,out] trx Transaction handle
3386@return error code or DB_SUCCESS */
3387UNIV_INLINE
3388dberr_t
3389row_drop_table_from_cache(
3390 const char* tablename,
3391 dict_table_t* table,
3392 trx_t* trx)
3393{
3394 dberr_t err = DB_SUCCESS;
3395 ut_ad(!table->is_temporary());
3396
3397 /* Remove the pointer to this table object from the list
3398 of modified tables by the transaction because the object
3399 is going to be destroyed below. */
3400 trx->mod_tables.erase(table);
3401
3402 dict_table_remove_from_cache(table);
3403
3404 if (dict_load_table(tablename, true, DICT_ERR_IGNORE_NONE)) {
3405 ib::error() << "Not able to remove table "
3406 << ut_get_name(trx, tablename)
3407 << " from the dictionary cache!";
3408 err = DB_ERROR;
3409 }
3410
3411 return(err);
3412}
3413
3414/** Drop a table for MySQL.
3415If the data dictionary was not already locked by the transaction,
3416the transaction will be committed. Otherwise, the data dictionary
3417will remain locked.
3418@param[in] name Table name
3419@param[in] trx Transaction handle
3420@param[in] drop_db true=dropping whole database
3421@param[in] create_failed TRUE=create table failed
3422 because e.g. foreign key column
3423@param[in] nonatomic Whether it is permitted to release
3424 and reacquire dict_operation_lock
3425@return error code or DB_SUCCESS */
3426dberr_t
3427row_drop_table_for_mysql(
3428 const char* name,
3429 trx_t* trx,
3430 bool drop_db,
3431 ibool create_failed,
3432 bool nonatomic)
3433{
3434 dberr_t err;
3435 dict_foreign_t* foreign;
3436 dict_table_t* table;
3437 char* tablename = NULL;
3438 bool locked_dictionary = false;
3439 pars_info_t* info = NULL;
3440 mem_heap_t* heap = NULL;
3441
3442 DBUG_ENTER("row_drop_table_for_mysql");
3443 DBUG_PRINT("row_drop_table_for_mysql", ("table: '%s'", name));
3444
3445 ut_a(name != NULL);
3446
3447 /* Serialize data dictionary operations with dictionary mutex:
3448 no deadlocks can occur then in these operations */
3449
3450 trx->op_info = "dropping table";
3451
3452 if (trx->dict_operation_lock_mode != RW_X_LATCH) {
3453 /* Prevent foreign key checks etc. while we are
3454 dropping the table */
3455
3456 row_mysql_lock_data_dictionary(trx);
3457
3458 locked_dictionary = true;
3459 nonatomic = true;
3460 }
3461
3462 ut_ad(mutex_own(&dict_sys->mutex));
3463 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
3464
3465 table = dict_table_open_on_name(
3466 name, TRUE, FALSE,
3467 static_cast<dict_err_ignore_t>(
3468 DICT_ERR_IGNORE_INDEX_ROOT
3469 | DICT_ERR_IGNORE_CORRUPT));
3470
3471 if (!table) {
3472 err = DB_TABLE_NOT_FOUND;
3473 goto funct_exit_all_freed;
3474 }
3475
3476 if (table->is_temporary()) {
3477 ut_ad(table->space == fil_system.temp_space);
3478 for (dict_index_t* index = dict_table_get_first_index(table);
3479 index != NULL;
3480 index = dict_table_get_next_index(index)) {
3481 btr_free(page_id_t(SRV_TMP_SPACE_ID, index->page),
3482 univ_page_size);
3483 }
3484 /* Remove the pointer to this table object from the list
3485 of modified tables by the transaction because the object
3486 is going to be destroyed below. */
3487 trx->mod_tables.erase(table);
3488 table->release();
3489 dict_table_remove_from_cache(table);
3490 err = DB_SUCCESS;
3491 goto funct_exit_all_freed;
3492 }
3493
3494 /* This function is called recursively via fts_drop_tables(). */
3495 if (!trx_is_started(trx)) {
3496 trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
3497 }
3498
3499 /* Turn on this drop bit before we could release the dictionary
3500 latch */
3501 table->to_be_dropped = true;
3502
3503 if (nonatomic) {
3504 /* This trx did not acquire any locks on dictionary
3505 table records yet. Thus it is safe to release and
3506 reacquire the data dictionary latches. */
3507 if (table->fts) {
3508 ut_ad(!table->fts->add_wq);
3509 ut_ad(lock_trx_has_sys_table_locks(trx) == 0);
3510
3511 for (;;) {
3512 bool retry = false;
3513 if (dict_fts_index_syncing(table)) {
3514 retry = true;
3515 }
3516 if (!retry) {
3517 break;
3518 }
3519 DICT_BG_YIELD(trx);
3520 }
3521 row_mysql_unlock_data_dictionary(trx);
3522 fts_optimize_remove_table(table);
3523 row_mysql_lock_data_dictionary(trx);
3524 }
3525
3526 dict_stats_wait_bg_to_stop_using_table(table, trx);
3527 }
3528
3529 /* make sure background stats thread is not running on the table */
3530 ut_ad(!(table->stats_bg_flag & BG_STAT_IN_PROGRESS));
3531
3532 /* Delete the link file if used. */
3533 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
3534 RemoteDatafile::delete_link_file(name);
3535 }
3536
3537 if (!table->no_rollback()) {
3538 dict_stats_recalc_pool_del(table);
3539 dict_stats_defrag_pool_del(table, NULL);
3540 if (btr_defragment_thread_active) {
3541 /* During fts_drop_orphaned_tables() in
3542 recv_recovery_rollback_active() the
3543 btr_defragment_mutex has not yet been
3544 initialized by btr_defragment_init(). */
3545 btr_defragment_remove_table(table);
3546 }
3547
3548 /* Remove stats for this table and all of its indexes from the
3549 persistent storage if it exists and if there are stats for this
3550 table in there. This function creates its own trx and commits
3551 it. */
3552 char errstr[1024];
3553 err = dict_stats_drop_table(name, errstr, sizeof(errstr));
3554
3555 if (err != DB_SUCCESS) {
3556 ib::warn() << errstr;
3557 }
3558 }
3559
3560 dict_table_prevent_eviction(table);
3561 dict_table_close(table, TRUE, FALSE);
3562
3563 /* Check if the table is referenced by foreign key constraints from
3564 some other table (not the table itself) */
3565
3566 if (!srv_read_only_mode && trx->check_foreigns) {
3567
3568 for (dict_foreign_set::iterator it
3569 = table->referenced_set.begin();
3570 it != table->referenced_set.end();
3571 ++it) {
3572
3573 foreign = *it;
3574
3575 const bool ref_ok = drop_db
3576 && dict_tables_have_same_db(
3577 name,
3578 foreign->foreign_table_name_lookup);
3579
3580 /* We should allow dropping a referenced table if creating
3581 that referenced table has failed for some reason. For example
3582 if referenced table is created but it column types that are
3583 referenced do not match. */
3584 if (foreign->foreign_table != table &&
3585 !create_failed && !ref_ok) {
3586
3587 FILE* ef = dict_foreign_err_file;
3588
3589 /* We only allow dropping a referenced table
3590 if FOREIGN_KEY_CHECKS is set to 0 */
3591
3592 err = DB_CANNOT_DROP_CONSTRAINT;
3593
3594 mutex_enter(&dict_foreign_err_mutex);
3595 rewind(ef);
3596 ut_print_timestamp(ef);
3597
3598 fputs(" Cannot drop table ", ef);
3599 ut_print_name(ef, trx, name);
3600 fputs("\n"
3601 "because it is referenced by ", ef);
3602 ut_print_name(ef, trx,
3603 foreign->foreign_table_name);
3604 putc('\n', ef);
3605 mutex_exit(&dict_foreign_err_mutex);
3606
3607 goto funct_exit;
3608 }
3609 }
3610 }
3611
3612
3613 DBUG_EXECUTE_IF("row_drop_table_add_to_background", goto defer;);
3614
3615 /* TODO: could we replace the counter n_foreign_key_checks_running
3616 with lock checks on the table? Acquire here an exclusive lock on the
3617 table, and rewrite lock0lock.cc and the lock wait in srv0srv.cc so that
3618 they can cope with the table having been dropped here? Foreign key
3619 checks take an IS or IX lock on the table. */
3620
3621 if (table->n_foreign_key_checks_running > 0) {
3622defer:
3623 if (!strstr(table->name.m_name, "/" TEMP_FILE_PREFIX)) {
3624 heap = mem_heap_create(FN_REFLEN);
3625 const char* tmp_name
3626 = dict_mem_create_temporary_tablename(
3627 heap, table->name.m_name, table->id);
3628 ib::info() << "Deferring DROP TABLE " << table->name
3629 << "; renaming to " << tmp_name;
3630 err = row_rename_table_for_mysql(
3631 table->name.m_name, tmp_name, trx, false);
3632 } else {
3633 err = DB_SUCCESS;
3634 }
3635 if (err == DB_SUCCESS) {
3636 row_add_table_to_background_drop_list(table->id);
3637 }
3638 goto funct_exit;
3639 }
3640
3641 /* Remove all locks that are on the table or its records, if there
3642 are no references to the table but it has record locks, we release
3643 the record locks unconditionally. One use case is:
3644
3645 CREATE TABLE t2 (PRIMARY KEY (a)) SELECT * FROM t1;
3646
3647 If after the user transaction has done the SELECT and there is a
3648 problem in completing the CREATE TABLE operation, MySQL will drop
3649 the table. InnoDB will create a new background transaction to do the
3650 actual drop, the trx instance that is passed to this function. To
3651 preserve existing behaviour we remove the locks but ideally we
3652 shouldn't have to. There should never be record locks on a table
3653 that is going to be dropped. */
3654
3655 /* Wait on background threads to stop using table */
3656 fil_wait_crypt_bg_threads(table);
3657
3658 if (table->get_ref_count() > 0 || table->n_rec_locks > 0
3659 || lock_table_has_locks(table)) {
3660 goto defer;
3661 }
3662
3663 /* The "to_be_dropped" marks table that is to be dropped, but
3664 has not been dropped, instead, was put in the background drop
3665 list due to being used by concurrent DML operations. Clear it
3666 here since there are no longer any concurrent activities on it,
3667 and it is free to be dropped */
3668 table->to_be_dropped = false;
3669
3670 switch (trx_get_dict_operation(trx)) {
3671 case TRX_DICT_OP_NONE:
3672 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
3673 trx->table_id = table->id;
3674 case TRX_DICT_OP_TABLE:
3675 break;
3676 case TRX_DICT_OP_INDEX:
3677 /* If the transaction was previously flagged as
3678 TRX_DICT_OP_INDEX, we should be dropping auxiliary
3679 tables for full-text indexes. */
3680 ut_ad(strstr(table->name.m_name, "/FTS_"));
3681 }
3682
3683 /* Mark all indexes unavailable in the data dictionary cache
3684 before starting to drop the table. */
3685
3686 unsigned* page_no;
3687 unsigned* page_nos;
3688 heap = mem_heap_create(
3689 200 + UT_LIST_GET_LEN(table->indexes) * sizeof *page_nos);
3690 tablename = mem_heap_strdup(heap, name);
3691
3692 page_no = page_nos = static_cast<unsigned*>(
3693 mem_heap_alloc(
3694 heap,
3695 UT_LIST_GET_LEN(table->indexes) * sizeof *page_no));
3696
3697 for (dict_index_t* index = dict_table_get_first_index(table);
3698 index != NULL;
3699 index = dict_table_get_next_index(index)) {
3700 rw_lock_x_lock(dict_index_get_lock(index));
3701 /* Save the page numbers so that we can restore them
3702 if the operation fails. */
3703 *page_no++ = index->page;
3704 /* Mark the index unusable. */
3705 index->page = FIL_NULL;
3706 rw_lock_x_unlock(dict_index_get_lock(index));
3707 }
3708
3709 /* Deleting a row from SYS_INDEXES table will invoke
3710 dict_drop_index_tree(). */
3711 info = pars_info_create();
3712 pars_info_add_str_literal(info, "table_name", name);
3713 err = que_eval_sql(
3714 info,
3715 "PROCEDURE DROP_TABLE_PROC () IS\n"
3716 "sys_foreign_id CHAR;\n"
3717 "table_id CHAR;\n"
3718 "index_id CHAR;\n"
3719 "foreign_id CHAR;\n"
3720 "space_id INT;\n"
3721 "found INT;\n"
3722
3723 "DECLARE CURSOR cur_fk IS\n"
3724 "SELECT ID FROM SYS_FOREIGN\n"
3725 "WHERE FOR_NAME = :table_name\n"
3726 "AND TO_BINARY(FOR_NAME)\n"
3727 " = TO_BINARY(:table_name)\n"
3728 "LOCK IN SHARE MODE;\n"
3729
3730 "DECLARE CURSOR cur_idx IS\n"
3731 "SELECT ID FROM SYS_INDEXES\n"
3732 "WHERE TABLE_ID = table_id\n"
3733 "LOCK IN SHARE MODE;\n"
3734
3735 "BEGIN\n"
3736
3737 "SELECT ID INTO table_id\n"
3738 "FROM SYS_TABLES\n"
3739 "WHERE NAME = :table_name\n"
3740 "LOCK IN SHARE MODE;\n"
3741 "IF (SQL % NOTFOUND) THEN\n"
3742 " RETURN;\n"
3743 "END IF;\n"
3744
3745 "SELECT SPACE INTO space_id\n"
3746 "FROM SYS_TABLES\n"
3747 "WHERE NAME = :table_name;\n"
3748 "IF (SQL % NOTFOUND) THEN\n"
3749 " RETURN;\n"
3750 "END IF;\n"
3751
3752 "found := 1;\n"
3753 "SELECT ID INTO sys_foreign_id\n"
3754 "FROM SYS_TABLES\n"
3755 "WHERE NAME = 'SYS_FOREIGN'\n"
3756 "LOCK IN SHARE MODE;\n"
3757 "IF (SQL % NOTFOUND) THEN\n"
3758 " found := 0;\n"
3759 "END IF;\n"
3760 "IF (:table_name = 'SYS_FOREIGN') THEN\n"
3761 " found := 0;\n"
3762 "END IF;\n"
3763 "IF (:table_name = 'SYS_FOREIGN_COLS') \n"
3764 "THEN\n"
3765 " found := 0;\n"
3766 "END IF;\n"
3767
3768 "OPEN cur_fk;\n"
3769 "WHILE found = 1 LOOP\n"
3770 " FETCH cur_fk INTO foreign_id;\n"
3771 " IF (SQL % NOTFOUND) THEN\n"
3772 " found := 0;\n"
3773 " ELSE\n"
3774 " DELETE FROM \n"
3775 " SYS_FOREIGN_COLS\n"
3776 " WHERE ID = foreign_id;\n"
3777 " DELETE FROM SYS_FOREIGN\n"
3778 " WHERE ID = foreign_id;\n"
3779 " END IF;\n"
3780 "END LOOP;\n"
3781 "CLOSE cur_fk;\n"
3782
3783 "found := 1;\n"
3784 "OPEN cur_idx;\n"
3785 "WHILE found = 1 LOOP\n"
3786 " FETCH cur_idx INTO index_id;\n"
3787 " IF (SQL % NOTFOUND) THEN\n"
3788 " found := 0;\n"
3789 " ELSE\n"
3790 " DELETE FROM SYS_FIELDS\n"
3791 " WHERE INDEX_ID = index_id;\n"
3792 " DELETE FROM SYS_INDEXES\n"
3793 " WHERE ID = index_id\n"
3794 " AND TABLE_ID = table_id;\n"
3795 " END IF;\n"
3796 "END LOOP;\n"
3797 "CLOSE cur_idx;\n"
3798
3799 "DELETE FROM SYS_COLUMNS\n"
3800 "WHERE TABLE_ID = table_id;\n"
3801 "DELETE FROM SYS_TABLES\n"
3802 "WHERE NAME = :table_name;\n"
3803
3804 "DELETE FROM SYS_TABLESPACES\n"
3805 "WHERE SPACE = space_id;\n"
3806 "DELETE FROM SYS_DATAFILES\n"
3807 "WHERE SPACE = space_id;\n"
3808
3809 "DELETE FROM SYS_VIRTUAL\n"
3810 "WHERE TABLE_ID = table_id;\n"
3811 "END;\n",
3812 FALSE, trx);
3813
3814 switch (err) {
3815 fil_space_t* space;
3816 char* filepath;
3817 case DB_SUCCESS:
3818 if (!table->no_rollback()) {
3819 err = row_drop_ancillary_fts_tables(table, trx);
3820 if (err != DB_SUCCESS) {
3821 break;
3822 }
3823 }
3824
3825 space = table->space;
3826 ut_ad(!space || space->id == table->space_id);
3827 /* Determine the tablespace filename before we drop
3828 dict_table_t. */
3829 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
3830 dict_get_and_save_data_dir_path(table, true);
3831 ut_a(table->data_dir_path);
3832 filepath = space ? NULL : fil_make_filepath(
3833 table->data_dir_path,
3834 table->name.m_name, IBD, true);
3835 } else {
3836 filepath = space ? NULL : fil_make_filepath(
3837 NULL, table->name.m_name, IBD, false);
3838 }
3839
3840 /* Free the dict_table_t object. */
3841 err = row_drop_table_from_cache(tablename, table, trx);
3842 if (err != DB_SUCCESS) {
3843 ut_free(filepath);
3844 break;
3845 }
3846
3847 /* Do not attempt to drop known-to-be-missing tablespaces,
3848 nor the system tablespace. */
3849 if (!space) {
3850 fil_delete_file(filepath);
3851 ut_free(filepath);
3852 break;
3853 }
3854
3855 ut_ad(!filepath);
3856
3857 if (space->id != TRX_SYS_SPACE) {
3858 err = fil_delete_tablespace(space->id);
3859 }
3860 break;
3861
3862 case DB_OUT_OF_FILE_SPACE:
3863 err = DB_MUST_GET_MORE_FILE_SPACE;
3864 trx->error_state = err;
3865 row_mysql_handle_errors(&err, trx, NULL, NULL);
3866
3867 /* raise error */
3868 ut_error;
3869 break;
3870
3871 case DB_TOO_MANY_CONCURRENT_TRXS:
3872 /* Cannot even find a free slot for the
3873 the undo log. We can directly exit here
3874 and return the DB_TOO_MANY_CONCURRENT_TRXS
3875 error. */
3876
3877 default:
3878 /* This is some error we do not expect. Print
3879 the error number and rollback the transaction */
3880 ib::error() << "Unknown error code " << err << " while"
3881 " dropping table: "
3882 << ut_get_name(trx, tablename) << ".";
3883
3884 trx->error_state = DB_SUCCESS;
3885 trx_rollback_to_savepoint(trx, NULL);
3886 trx->error_state = DB_SUCCESS;
3887
3888 /* Mark all indexes available in the data dictionary
3889 cache again. */
3890
3891 page_no = page_nos;
3892
3893 for (dict_index_t* index = dict_table_get_first_index(table);
3894 index != NULL;
3895 index = dict_table_get_next_index(index)) {
3896 rw_lock_x_lock(dict_index_get_lock(index));
3897 ut_a(index->page == FIL_NULL);
3898 index->page = *page_no++;
3899 rw_lock_x_unlock(dict_index_get_lock(index));
3900 }
3901 }
3902
3903 if (err != DB_SUCCESS && table != NULL) {
3904 /* Drop table has failed with error but as drop table is not
3905 transaction safe we should mark the table as corrupted to avoid
3906 unwarranted follow-up action on this table that can result
3907 in more serious issues. */
3908
3909 table->corrupted = true;
3910 for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
3911 index != NULL;
3912 index = UT_LIST_GET_NEXT(indexes, index)) {
3913 dict_set_corrupted(index, trx, "DROP TABLE");
3914 }
3915 }
3916
3917funct_exit:
3918 if (heap) {
3919 mem_heap_free(heap);
3920 }
3921
3922funct_exit_all_freed:
3923 if (locked_dictionary) {
3924
3925 if (trx_is_started(trx)) {
3926
3927 trx_commit_for_mysql(trx);
3928 }
3929
3930 row_mysql_unlock_data_dictionary(trx);
3931 }
3932
3933 trx->op_info = "";
3934
3935 srv_wake_master_thread();
3936
3937 DBUG_RETURN(err);
3938}
3939
3940/*******************************************************************//**
3941Drop all foreign keys in a database, see Bug#18942.
3942Called at the end of row_drop_database_for_mysql().
3943@return error code or DB_SUCCESS */
3944static MY_ATTRIBUTE((nonnull, warn_unused_result))
3945dberr_t
3946drop_all_foreign_keys_in_db(
3947/*========================*/
3948 const char* name, /*!< in: database name which ends to '/' */
3949 trx_t* trx) /*!< in: transaction handle */
3950{
3951 pars_info_t* pinfo;
3952 dberr_t err;
3953
3954 ut_a(name[strlen(name) - 1] == '/');
3955
3956 pinfo = pars_info_create();
3957
3958 pars_info_add_str_literal(pinfo, "dbname", name);
3959
3960/** true if for_name is not prefixed with dbname */
3961#define TABLE_NOT_IN_THIS_DB \
3962"SUBSTR(for_name, 0, LENGTH(:dbname)) <> :dbname"
3963
3964 err = que_eval_sql(pinfo,
3965 "PROCEDURE DROP_ALL_FOREIGN_KEYS_PROC () IS\n"
3966 "foreign_id CHAR;\n"
3967 "for_name CHAR;\n"
3968 "found INT;\n"
3969 "DECLARE CURSOR cur IS\n"
3970 "SELECT ID, FOR_NAME FROM SYS_FOREIGN\n"
3971 "WHERE FOR_NAME >= :dbname\n"
3972 "LOCK IN SHARE MODE\n"
3973 "ORDER BY FOR_NAME;\n"
3974 "BEGIN\n"
3975 "found := 1;\n"
3976 "OPEN cur;\n"
3977 "WHILE found = 1 LOOP\n"
3978 " FETCH cur INTO foreign_id, for_name;\n"
3979 " IF (SQL % NOTFOUND) THEN\n"
3980 " found := 0;\n"
3981 " ELSIF (" TABLE_NOT_IN_THIS_DB ") THEN\n"
3982 " found := 0;\n"
3983 " ELSIF (1=1) THEN\n"
3984 " DELETE FROM SYS_FOREIGN_COLS\n"
3985 " WHERE ID = foreign_id;\n"
3986 " DELETE FROM SYS_FOREIGN\n"
3987 " WHERE ID = foreign_id;\n"
3988 " END IF;\n"
3989 "END LOOP;\n"
3990 "CLOSE cur;\n"
3991 "COMMIT WORK;\n"
3992 "END;\n",
3993 FALSE, /* do not reserve dict mutex,
3994 we are already holding it */
3995 trx);
3996
3997 return(err);
3998}
3999
4000/** Drop a database for MySQL.
4001@param[in] name database name which ends at '/'
4002@param[in] trx transaction handle
4003@param[out] found number of dropped tables/partitions
4004@return error code or DB_SUCCESS */
4005dberr_t
4006row_drop_database_for_mysql(
4007 const char* name,
4008 trx_t* trx,
4009 ulint* found)
4010{
4011 dict_table_t* table;
4012 char* table_name;
4013 dberr_t err = DB_SUCCESS;
4014 ulint namelen = strlen(name);
4015 bool is_partition = false;
4016
4017 ut_ad(found != NULL);
4018
4019 DBUG_ENTER("row_drop_database_for_mysql");
4020
4021 DBUG_PRINT("row_drop_database_for_mysql", ("db: '%s'", name));
4022
4023 ut_a(name != NULL);
4024 /* Assert DB name or partition name. */
4025 if (name[namelen - 1] == '#') {
4026 ut_ad(name[namelen - 2] != '/');
4027 is_partition = true;
4028 trx->op_info = "dropping partitions";
4029 } else {
4030 ut_a(name[namelen - 1] == '/');
4031 trx->op_info = "dropping database";
4032 }
4033
4034 *found = 0;
4035
4036 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
4037
4038 trx_start_if_not_started_xa(trx, true);
4039
4040loop:
4041 row_mysql_lock_data_dictionary(trx);
4042
4043 while ((table_name = dict_get_first_table_name_in_db(name))) {
4044 /* Drop parent table if it is a fts aux table, to
4045 avoid accessing dropped fts aux tables in information
4046 scheam when parent table still exists.
4047 Note: Drop parent table will drop fts aux tables. */
4048 char* parent_table_name;
4049 parent_table_name = fts_get_parent_table_name(
4050 table_name, strlen(table_name));
4051
4052 if (parent_table_name != NULL) {
4053 ut_free(table_name);
4054 table_name = parent_table_name;
4055 }
4056
4057 ut_a(memcmp(table_name, name, namelen) == 0);
4058
4059 table = dict_table_open_on_name(
4060 table_name, TRUE, FALSE, static_cast<dict_err_ignore_t>(
4061 DICT_ERR_IGNORE_INDEX_ROOT
4062 | DICT_ERR_IGNORE_CORRUPT));
4063
4064 if (!table) {
4065 ib::error() << "Cannot load table " << table_name
4066 << " from InnoDB internal data dictionary"
4067 " during drop database";
4068 ut_free(table_name);
4069 err = DB_TABLE_NOT_FOUND;
4070 break;
4071
4072 }
4073
4074 if (!row_is_mysql_tmp_table_name(table->name.m_name)) {
4075 /* There could be orphan temp tables left from
4076 interrupted alter table. Leave them, and handle
4077 the rest.*/
4078 if (table->can_be_evicted
4079 && (name[namelen - 1] != '#')) {
4080 ib::warn() << "Orphan table encountered during"
4081 " DROP DATABASE. This is possible if '"
4082 << table->name << ".frm' was lost.";
4083 }
4084
4085 if (!table->is_readable() && !table->space) {
4086 ib::warn() << "Missing .ibd file for table "
4087 << table->name << ".";
4088 }
4089 }
4090
4091 dict_table_close(table, TRUE, FALSE);
4092
4093 /* The dict_table_t object must not be accessed before
4094 dict_table_open() or after dict_table_close(). But this is OK
4095 if we are holding, the dict_sys->mutex. */
4096 ut_ad(mutex_own(&dict_sys->mutex));
4097
4098 /* Disable statistics on the found table. */
4099 if (!dict_stats_stop_bg(table)) {
4100 row_mysql_unlock_data_dictionary(trx);
4101
4102 os_thread_sleep(250000);
4103
4104 ut_free(table_name);
4105
4106 goto loop;
4107 }
4108
4109 /* Wait until MySQL does not have any queries running on
4110 the table */
4111
4112 if (table->get_ref_count() > 0) {
4113 row_mysql_unlock_data_dictionary(trx);
4114
4115 ib::warn() << "MySQL is trying to drop database "
4116 << ut_get_name(trx, name) << " though"
4117 " there are still open handles to table "
4118 << table->name << ".";
4119
4120 os_thread_sleep(1000000);
4121
4122 ut_free(table_name);
4123
4124 goto loop;
4125 }
4126
4127 err = row_drop_table_for_mysql(table_name, trx, TRUE, FALSE);
4128 trx_commit_for_mysql(trx);
4129
4130 if (err != DB_SUCCESS) {
4131 ib::error() << "DROP DATABASE "
4132 << ut_get_name(trx, name) << " failed"
4133 " with error (" << ut_strerr(err) << ") for"
4134 " table " << ut_get_name(trx, table_name);
4135 ut_free(table_name);
4136 break;
4137 }
4138
4139 ut_free(table_name);
4140 (*found)++;
4141 }
4142
4143 /* Partitioning does not yet support foreign keys. */
4144 if (err == DB_SUCCESS && !is_partition) {
4145 /* after dropping all tables try to drop all leftover
4146 foreign keys in case orphaned ones exist */
4147 err = drop_all_foreign_keys_in_db(name, trx);
4148
4149 if (err != DB_SUCCESS) {
4150 const std::string& db = ut_get_name(trx, name);
4151 ib::error() << "DROP DATABASE " << db << " failed with"
4152 " error " << err << " while dropping all"
4153 " foreign keys";
4154 }
4155 }
4156
4157 trx_commit_for_mysql(trx);
4158
4159 row_mysql_unlock_data_dictionary(trx);
4160
4161 trx->op_info = "";
4162
4163 DBUG_RETURN(err);
4164}
4165
4166/*********************************************************************//**
4167Checks if a table name contains the string "/#sql" which denotes temporary
4168tables in MySQL.
4169@return true if temporary table */
4170MY_ATTRIBUTE((warn_unused_result))
4171bool
4172row_is_mysql_tmp_table_name(
4173/*========================*/
4174 const char* name) /*!< in: table name in the form
4175 'database/tablename' */
4176{
4177 return(strstr(name, "/" TEMP_FILE_PREFIX) != NULL);
4178 /* return(strstr(name, "/@0023sql") != NULL); */
4179}
4180
4181/****************************************************************//**
4182Delete a single constraint.
4183@return error code or DB_SUCCESS */
4184static MY_ATTRIBUTE((nonnull, warn_unused_result))
4185dberr_t
4186row_delete_constraint_low(
4187/*======================*/
4188 const char* id, /*!< in: constraint id */
4189 trx_t* trx) /*!< in: transaction handle */
4190{
4191 pars_info_t* info = pars_info_create();
4192
4193 pars_info_add_str_literal(info, "id", id);
4194
4195 return(que_eval_sql(info,
4196 "PROCEDURE DELETE_CONSTRAINT () IS\n"
4197 "BEGIN\n"
4198 "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n"
4199 "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n"
4200 "END;\n"
4201 , FALSE, trx));
4202}
4203
4204/****************************************************************//**
4205Delete a single constraint.
4206@return error code or DB_SUCCESS */
4207static MY_ATTRIBUTE((nonnull, warn_unused_result))
4208dberr_t
4209row_delete_constraint(
4210/*==================*/
4211 const char* id, /*!< in: constraint id */
4212 const char* database_name, /*!< in: database name, with the
4213 trailing '/' */
4214 mem_heap_t* heap, /*!< in: memory heap */
4215 trx_t* trx) /*!< in: transaction handle */
4216{
4217 dberr_t err;
4218
4219 /* New format constraints have ids <databasename>/<constraintname>. */
4220 err = row_delete_constraint_low(
4221 mem_heap_strcat(heap, database_name, id), trx);
4222
4223 if ((err == DB_SUCCESS) && !strchr(id, '/')) {
4224 /* Old format < 4.0.18 constraints have constraint ids
4225 NUMBER_NUMBER. We only try deleting them if the
4226 constraint name does not contain a '/' character, otherwise
4227 deleting a new format constraint named 'foo/bar' from
4228 database 'baz' would remove constraint 'bar' from database
4229 'foo', if it existed. */
4230
4231 err = row_delete_constraint_low(id, trx);
4232 }
4233
4234 return(err);
4235}
4236
4237/*********************************************************************//**
4238Renames a table for MySQL.
4239@return error code or DB_SUCCESS */
4240dberr_t
4241row_rename_table_for_mysql(
4242/*=======================*/
4243 const char* old_name, /*!< in: old table name */
4244 const char* new_name, /*!< in: new table name */
4245 trx_t* trx, /*!< in/out: transaction */
4246 bool commit) /*!< in: whether to commit trx */
4247{
4248 dict_table_t* table = NULL;
4249 ibool dict_locked = FALSE;
4250 dberr_t err = DB_ERROR;
4251 mem_heap_t* heap = NULL;
4252 const char** constraints_to_drop = NULL;
4253 ulint n_constraints_to_drop = 0;
4254 ibool old_is_tmp, new_is_tmp;
4255 pars_info_t* info = NULL;
4256 int retry;
4257 bool aux_fts_rename = false;
4258 char* is_part = NULL;
4259
4260 ut_a(old_name != NULL);
4261 ut_a(new_name != NULL);
4262 ut_ad(trx->state == TRX_STATE_ACTIVE);
4263
4264 if (high_level_read_only) {
4265 return(DB_READ_ONLY);
4266
4267 } else if (row_mysql_is_system_table(new_name)) {
4268
4269 ib::error() << "Trying to create a MySQL system table "
4270 << new_name << " of type InnoDB. MySQL system tables"
4271 " must be of the MyISAM type!";
4272
4273 goto funct_exit;
4274 }
4275
4276 trx->op_info = "renaming table";
4277
4278 old_is_tmp = row_is_mysql_tmp_table_name(old_name);
4279 new_is_tmp = row_is_mysql_tmp_table_name(new_name);
4280
4281 dict_locked = trx->dict_operation_lock_mode == RW_X_LATCH;
4282
4283 table = dict_table_open_on_name(old_name, dict_locked, FALSE,
4284 DICT_ERR_IGNORE_NONE);
4285
4286 /* We look for pattern #P# to see if the table is partitioned
4287 MySQL table. */
4288#ifdef __WIN__
4289 is_part = strstr((char *)old_name, (char *)"#p#");
4290#else
4291 is_part = strstr((char *)old_name, (char *)"#P#");
4292#endif /* __WIN__ */
4293
4294 /* MySQL partition engine hard codes the file name
4295 separator as "#P#". The text case is fixed even if
4296 lower_case_table_names is set to 1 or 2. This is true
4297 for sub-partition names as well. InnoDB always
4298 normalises file names to lower case on Windows, this
4299 can potentially cause problems when copying/moving
4300 tables between platforms.
4301
4302 1) If boot against an installation from Windows
4303 platform, then its partition table name could
4304 be all be in lower case in system tables. So we
4305 will need to check lower case name when load table.
4306
4307 2) If we boot an installation from other case
4308 sensitive platform in Windows, we might need to
4309 check the existence of table name without lowering
4310 case them in the system table. */
4311 if (!table &&
4312 is_part &&
4313 innobase_get_lower_case_table_names() == 1) {
4314 char par_case_name[MAX_FULL_NAME_LEN + 1];
4315#ifndef __WIN__
4316 /* Check for the table using lower
4317 case name, including the partition
4318 separator "P" */
4319 memcpy(par_case_name, old_name,
4320 strlen(old_name));
4321 par_case_name[strlen(old_name)] = 0;
4322 innobase_casedn_str(par_case_name);
4323#else
4324 /* On Windows platfrom, check
4325 whether there exists table name in
4326 system table whose name is
4327 not being normalized to lower case */
4328 normalize_table_name_c_low(
4329 par_case_name, old_name, FALSE);
4330#endif
4331 table = dict_table_open_on_name(par_case_name, dict_locked, FALSE,
4332 DICT_ERR_IGNORE_NONE);
4333 }
4334
4335 if (!table) {
4336 err = DB_TABLE_NOT_FOUND;
4337 goto funct_exit;
4338
4339 } else if (!table->is_readable() && !table->space
4340 && !(table->flags2 & DICT_TF2_DISCARDED)) {
4341
4342 err = DB_TABLE_NOT_FOUND;
4343
4344 ib::error() << "Table " << old_name << " does not have an .ibd"
4345 " file in the database directory. "
4346 << TROUBLESHOOTING_MSG;
4347
4348 goto funct_exit;
4349
4350 } else if (!old_is_tmp && new_is_tmp) {
4351 /* MySQL is doing an ALTER TABLE command and it renames the
4352 original table to a temporary table name. We want to preserve
4353 the original foreign key constraint definitions despite the
4354 name change. An exception is those constraints for which
4355 the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
4356
4357 heap = mem_heap_create(100);
4358
4359 err = dict_foreign_parse_drop_constraints(
4360 heap, trx, table, &n_constraints_to_drop,
4361 &constraints_to_drop);
4362
4363 if (err != DB_SUCCESS) {
4364 goto funct_exit;
4365 }
4366 }
4367
4368 /* Is a foreign key check running on this table? */
4369 for (retry = 0; retry < 100
4370 && table->n_foreign_key_checks_running > 0; ++retry) {
4371 row_mysql_unlock_data_dictionary(trx);
4372 os_thread_yield();
4373 row_mysql_lock_data_dictionary(trx);
4374 }
4375
4376 if (table->n_foreign_key_checks_running > 0) {
4377 ib::error() << "In ALTER TABLE "
4378 << ut_get_name(trx, old_name)
4379 << " a FOREIGN KEY check is running. Cannot rename"
4380 " table.";
4381 err = DB_TABLE_IN_FK_CHECK;
4382 goto funct_exit;
4383 }
4384
4385 if (!table->is_temporary()) {
4386 err = trx_undo_report_rename(trx, table);
4387
4388 if (err != DB_SUCCESS) {
4389 goto funct_exit;
4390 }
4391 }
4392
4393 /* We use the private SQL parser of Innobase to generate the query
4394 graphs needed in updating the dictionary data from system tables. */
4395
4396 info = pars_info_create();
4397
4398 pars_info_add_str_literal(info, "new_table_name", new_name);
4399 pars_info_add_str_literal(info, "old_table_name", old_name);
4400
4401 err = que_eval_sql(info,
4402 "PROCEDURE RENAME_TABLE () IS\n"
4403 "BEGIN\n"
4404 "UPDATE SYS_TABLES"
4405 " SET NAME = :new_table_name\n"
4406 " WHERE NAME = :old_table_name;\n"
4407 "END;\n"
4408 , FALSE, trx);
4409
4410 /* SYS_TABLESPACES and SYS_DATAFILES need to be updated if
4411 the table is in a single-table tablespace. */
4412 if (err == DB_SUCCESS
4413 && dict_table_is_file_per_table(table)) {
4414 /* Make a new pathname to update SYS_DATAFILES. */
4415 /* If old path and new path are the same means tablename
4416 has not changed and only the database name holding the table
4417 has changed so we need to make the complete filepath again. */
4418 char* new_path = dict_tables_have_same_db(old_name, new_name)
4419 ? row_make_new_pathname(table, new_name)
4420 : fil_make_filepath(NULL, new_name, IBD, false);
4421
4422 info = pars_info_create();
4423
4424 pars_info_add_str_literal(info, "new_table_name", new_name);
4425 pars_info_add_str_literal(info, "new_path_name", new_path);
4426 pars_info_add_int4_literal(info, "space_id", table->space_id);
4427
4428 err = que_eval_sql(info,
4429 "PROCEDURE RENAME_SPACE () IS\n"
4430 "BEGIN\n"
4431 "UPDATE SYS_TABLESPACES"
4432 " SET NAME = :new_table_name\n"
4433 " WHERE SPACE = :space_id;\n"
4434 "UPDATE SYS_DATAFILES"
4435 " SET PATH = :new_path_name\n"
4436 " WHERE SPACE = :space_id;\n"
4437 "END;\n"
4438 , FALSE, trx);
4439
4440 ut_free(new_path);
4441 }
4442 if (err != DB_SUCCESS) {
4443 goto end;
4444 }
4445
4446 if (!new_is_tmp) {
4447 /* Rename all constraints. */
4448 char new_table_name[MAX_TABLE_NAME_LEN] = "";
4449 char old_table_utf8[MAX_TABLE_NAME_LEN] = "";
4450 uint errors = 0;
4451
4452 strncpy(old_table_utf8, old_name, MAX_TABLE_NAME_LEN);
4453 innobase_convert_to_system_charset(
4454 strchr(old_table_utf8, '/') + 1,
4455 strchr(old_name, '/') +1,
4456 MAX_TABLE_NAME_LEN, &errors);
4457
4458 if (errors) {
4459 /* Table name could not be converted from charset
4460 my_charset_filename to UTF-8. This means that the
4461 table name is already in UTF-8 (#mysql#50). */
4462 strncpy(old_table_utf8, old_name, MAX_TABLE_NAME_LEN);
4463 }
4464
4465 info = pars_info_create();
4466
4467 pars_info_add_str_literal(info, "new_table_name", new_name);
4468 pars_info_add_str_literal(info, "old_table_name", old_name);
4469 pars_info_add_str_literal(info, "old_table_name_utf8",
4470 old_table_utf8);
4471
4472 strncpy(new_table_name, new_name, MAX_TABLE_NAME_LEN);
4473 innobase_convert_to_system_charset(
4474 strchr(new_table_name, '/') + 1,
4475 strchr(new_name, '/') +1,
4476 MAX_TABLE_NAME_LEN, &errors);
4477
4478 if (errors) {
4479 /* Table name could not be converted from charset
4480 my_charset_filename to UTF-8. This means that the
4481 table name is already in UTF-8 (#mysql#50). */
4482 strncpy(new_table_name, new_name, MAX_TABLE_NAME_LEN);
4483 }
4484
4485 pars_info_add_str_literal(info, "new_table_utf8", new_table_name);
4486
4487 err = que_eval_sql(
4488 info,
4489 "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n"
4490 "gen_constr_prefix CHAR;\n"
4491 "new_db_name CHAR;\n"
4492 "foreign_id CHAR;\n"
4493 "new_foreign_id CHAR;\n"
4494 "old_db_name_len INT;\n"
4495 "old_t_name_len INT;\n"
4496 "new_db_name_len INT;\n"
4497 "id_len INT;\n"
4498 "offset INT;\n"
4499 "found INT;\n"
4500 "BEGIN\n"
4501 "found := 1;\n"
4502 "old_db_name_len := INSTR(:old_table_name, '/')-1;\n"
4503 "new_db_name_len := INSTR(:new_table_name, '/')-1;\n"
4504 "new_db_name := SUBSTR(:new_table_name, 0,\n"
4505 " new_db_name_len);\n"
4506 "old_t_name_len := LENGTH(:old_table_name);\n"
4507 "gen_constr_prefix := CONCAT(:old_table_name_utf8,\n"
4508 " '_ibfk_');\n"
4509 "WHILE found = 1 LOOP\n"
4510 " SELECT ID INTO foreign_id\n"
4511 " FROM SYS_FOREIGN\n"
4512 " WHERE FOR_NAME = :old_table_name\n"
4513 " AND TO_BINARY(FOR_NAME)\n"
4514 " = TO_BINARY(:old_table_name)\n"
4515 " LOCK IN SHARE MODE;\n"
4516 " IF (SQL % NOTFOUND) THEN\n"
4517 " found := 0;\n"
4518 " ELSE\n"
4519 " UPDATE SYS_FOREIGN\n"
4520 " SET FOR_NAME = :new_table_name\n"
4521 " WHERE ID = foreign_id;\n"
4522 " id_len := LENGTH(foreign_id);\n"
4523 " IF (INSTR(foreign_id, '/') > 0) THEN\n"
4524 " IF (INSTR(foreign_id,\n"
4525 " gen_constr_prefix) > 0)\n"
4526 " THEN\n"
4527 " offset := INSTR(foreign_id, '_ibfk_') - 1;\n"
4528 " new_foreign_id :=\n"
4529 " CONCAT(:new_table_utf8,\n"
4530 " SUBSTR(foreign_id, offset,\n"
4531 " id_len - offset));\n"
4532 " ELSE\n"
4533 " new_foreign_id :=\n"
4534 " CONCAT(new_db_name,\n"
4535 " SUBSTR(foreign_id,\n"
4536 " old_db_name_len,\n"
4537 " id_len - old_db_name_len));\n"
4538 " END IF;\n"
4539 " UPDATE SYS_FOREIGN\n"
4540 " SET ID = new_foreign_id\n"
4541 " WHERE ID = foreign_id;\n"
4542 " UPDATE SYS_FOREIGN_COLS\n"
4543 " SET ID = new_foreign_id\n"
4544 " WHERE ID = foreign_id;\n"
4545 " END IF;\n"
4546 " END IF;\n"
4547 "END LOOP;\n"
4548 "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n"
4549 "WHERE REF_NAME = :old_table_name\n"
4550 " AND TO_BINARY(REF_NAME)\n"
4551 " = TO_BINARY(:old_table_name);\n"
4552 "END;\n"
4553 , FALSE, trx);
4554
4555 } else if (n_constraints_to_drop > 0) {
4556 /* Drop some constraints of tmp tables. */
4557
4558 ulint db_name_len = dict_get_db_name_len(old_name) + 1;
4559 char* db_name = mem_heap_strdupl(heap, old_name,
4560 db_name_len);
4561 ulint i;
4562
4563 for (i = 0; i < n_constraints_to_drop; i++) {
4564 err = row_delete_constraint(constraints_to_drop[i],
4565 db_name, heap, trx);
4566
4567 if (err != DB_SUCCESS) {
4568 break;
4569 }
4570 }
4571 }
4572
4573 if (err == DB_SUCCESS
4574 && (dict_table_has_fts_index(table)
4575 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID))
4576 && !dict_tables_have_same_db(old_name, new_name)) {
4577 err = fts_rename_aux_tables(table, new_name, trx);
4578 if (err != DB_TABLE_NOT_FOUND) {
4579 aux_fts_rename = true;
4580 }
4581 }
4582
4583end:
4584 if (err != DB_SUCCESS) {
4585 if (err == DB_DUPLICATE_KEY) {
4586 ib::error() << "Possible reasons:";
4587 ib::error() << "(1) Table rename would cause two"
4588 " FOREIGN KEY constraints to have the same"
4589 " internal name in case-insensitive"
4590 " comparison.";
4591 ib::error() << "(2) Table "
4592 << ut_get_name(trx, new_name)
4593 << " exists in the InnoDB internal data"
4594 " dictionary though MySQL is trying to rename"
4595 " table " << ut_get_name(trx, old_name)
4596 << " to it. Have you deleted the .frm file and"
4597 " not used DROP TABLE?";
4598 ib::info() << TROUBLESHOOTING_MSG;
4599 ib::error() << "If table "
4600 << ut_get_name(trx, new_name)
4601 << " is a temporary table #sql..., then"
4602 " it can be that there are still queries"
4603 " running on the table, and it will be dropped"
4604 " automatically when the queries end. You can"
4605 " drop the orphaned table inside InnoDB by"
4606 " creating an InnoDB table with the same name"
4607 " in another database and copying the .frm file"
4608 " to the current database. Then MySQL thinks"
4609 " the table exists, and DROP TABLE will"
4610 " succeed.";
4611 }
4612 trx->error_state = DB_SUCCESS;
4613 trx_rollback_to_savepoint(trx, NULL);
4614 trx->error_state = DB_SUCCESS;
4615 } else {
4616 /* The following call will also rename the .ibd data file if
4617 the table is stored in a single-table tablespace */
4618
4619 err = dict_table_rename_in_cache(
4620 table, new_name, !new_is_tmp);
4621 if (err != DB_SUCCESS) {
4622 trx->error_state = DB_SUCCESS;
4623 trx_rollback_to_savepoint(trx, NULL);
4624 trx->error_state = DB_SUCCESS;
4625 goto funct_exit;
4626 }
4627
4628 /* In case of copy alter, template db_name and
4629 table_name should be renamed only for newly
4630 created table. */
4631 if (table->vc_templ != NULL && !new_is_tmp) {
4632 innobase_rename_vc_templ(table);
4633 }
4634
4635 /* We only want to switch off some of the type checking in
4636 an ALTER TABLE...ALGORITHM=COPY, not in a RENAME. */
4637 dict_names_t fk_tables;
4638
4639 err = dict_load_foreigns(
4640 new_name, NULL,
4641 false, !old_is_tmp || trx->check_foreigns,
4642 DICT_ERR_IGNORE_NONE, fk_tables);
4643
4644 if (err != DB_SUCCESS) {
4645
4646 if (old_is_tmp) {
4647 ib::error() << "In ALTER TABLE "
4648 << ut_get_name(trx, new_name)
4649 << " has or is referenced in foreign"
4650 " key constraints which are not"
4651 " compatible with the new table"
4652 " definition.";
4653 } else {
4654 ib::error() << "In RENAME TABLE table "
4655 << ut_get_name(trx, new_name)
4656 << " is referenced in foreign key"
4657 " constraints which are not compatible"
4658 " with the new table definition.";
4659 }
4660
4661 ut_a(DB_SUCCESS == dict_table_rename_in_cache(
4662 table, old_name, FALSE));
4663 trx->error_state = DB_SUCCESS;
4664 trx_rollback_to_savepoint(trx, NULL);
4665 trx->error_state = DB_SUCCESS;
4666 }
4667
4668 /* Check whether virtual column or stored column affects
4669 the foreign key constraint of the table. */
4670 if (dict_foreigns_has_s_base_col(
4671 table->foreign_set, table)) {
4672 err = DB_NO_FK_ON_S_BASE_COL;
4673 ut_a(DB_SUCCESS == dict_table_rename_in_cache(
4674 table, old_name, FALSE));
4675 trx->error_state = DB_SUCCESS;
4676 trx_rollback_to_savepoint(trx, NULL);
4677 trx->error_state = DB_SUCCESS;
4678 goto funct_exit;
4679 }
4680
4681 /* Fill the virtual column set in foreign when
4682 the table undergoes copy alter operation. */
4683 dict_mem_table_free_foreign_vcol_set(table);
4684 dict_mem_table_fill_foreign_vcol_set(table);
4685
4686 while (!fk_tables.empty()) {
4687 dict_load_table(fk_tables.front(), true,
4688 DICT_ERR_IGNORE_NONE);
4689 fk_tables.pop_front();
4690 }
4691
4692 table->data_dir_path= NULL;
4693 }
4694
4695funct_exit:
4696 if (aux_fts_rename && err != DB_SUCCESS
4697 && table != NULL && (table->space != 0)) {
4698
4699 char* orig_name = table->name.m_name;
4700 trx_t* trx_bg = trx_create();
4701
4702 /* If the first fts_rename fails, the trx would
4703 be rolled back and committed, we can't use it any more,
4704 so we have to start a new background trx here. */
4705 ut_a(trx_state_eq(trx_bg, TRX_STATE_NOT_STARTED));
4706 trx_bg->op_info = "Revert the failing rename "
4707 "for fts aux tables";
4708 trx_bg->dict_operation_lock_mode = RW_X_LATCH;
4709 trx_start_for_ddl(trx_bg, TRX_DICT_OP_TABLE);
4710
4711 /* If rename fails and table has its own tablespace,
4712 we need to call fts_rename_aux_tables again to
4713 revert the ibd file rename, which is not under the
4714 control of trx. Also notice the parent table name
4715 in cache is not changed yet. If the reverting fails,
4716 the ibd data may be left in the new database, which
4717 can be fixed only manually. */
4718 table->name.m_name = const_cast<char*>(new_name);
4719 fts_rename_aux_tables(table, old_name, trx_bg);
4720 table->name.m_name = orig_name;
4721
4722 trx_bg->dict_operation_lock_mode = 0;
4723 trx_commit_for_mysql(trx_bg);
4724 trx_free(trx_bg);
4725 }
4726
4727 if (table != NULL) {
4728 dict_table_close(table, dict_locked, FALSE);
4729 }
4730
4731 if (commit) {
4732 DEBUG_SYNC(trx->mysql_thd, "before_rename_table_commit");
4733 trx_commit_for_mysql(trx);
4734 }
4735
4736 if (UNIV_LIKELY_NULL(heap)) {
4737 mem_heap_free(heap);
4738 }
4739
4740 trx->op_info = "";
4741
4742 return(err);
4743}
4744
4745/*********************************************************************//**
4746Scans an index for either COUNT(*) or CHECK TABLE.
4747If CHECK TABLE; Checks that the index contains entries in an ascending order,
4748unique constraint is not broken, and calculates the number of index entries
4749in the read view of the current transaction.
4750@return DB_SUCCESS or other error */
4751dberr_t
4752row_scan_index_for_mysql(
4753/*=====================*/
4754 row_prebuilt_t* prebuilt, /*!< in: prebuilt struct
4755 in MySQL handle */
4756 const dict_index_t* index, /*!< in: index */
4757 ulint* n_rows) /*!< out: number of entries
4758 seen in the consistent read */
4759{
4760 dtuple_t* prev_entry = NULL;
4761 ulint matched_fields;
4762 byte* buf;
4763 dberr_t ret;
4764 rec_t* rec;
4765 int cmp;
4766 ibool contains_null;
4767 ulint i;
4768 ulint cnt;
4769 mem_heap_t* heap = NULL;
4770 ulint n_ext;
4771 ulint offsets_[REC_OFFS_NORMAL_SIZE];
4772 ulint* offsets;
4773 rec_offs_init(offsets_);
4774
4775 *n_rows = 0;
4776
4777 /* Don't support RTree Leaf level scan */
4778 ut_ad(!dict_index_is_spatial(index));
4779
4780 if (dict_index_is_clust(index)) {
4781 /* The clustered index of a table is always available.
4782 During online ALTER TABLE that rebuilds the table, the
4783 clustered index in the old table will have
4784 index->online_log pointing to the new table. All
4785 indexes of the old table will remain valid and the new
4786 table will be unaccessible to MySQL until the
4787 completion of the ALTER TABLE. */
4788 } else if (dict_index_is_online_ddl(index)
4789 || (index->type & DICT_FTS)) {
4790 /* Full Text index are implemented by auxiliary tables,
4791 not the B-tree. We also skip secondary indexes that are
4792 being created online. */
4793 return(DB_SUCCESS);
4794 }
4795
4796 ulint bufsize = std::max<ulint>(srv_page_size,
4797 prebuilt->mysql_row_len);
4798 buf = static_cast<byte*>(ut_malloc_nokey(bufsize));
4799 heap = mem_heap_create(100);
4800
4801 cnt = 1000;
4802
4803 ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0);
4804loop:
4805 /* Check thd->killed every 1,000 scanned rows */
4806 if (--cnt == 0) {
4807 if (trx_is_interrupted(prebuilt->trx)) {
4808 ret = DB_INTERRUPTED;
4809 goto func_exit;
4810 }
4811 cnt = 1000;
4812 }
4813
4814 switch (ret) {
4815 case DB_SUCCESS:
4816 break;
4817 case DB_DEADLOCK:
4818 case DB_LOCK_TABLE_FULL:
4819 case DB_LOCK_WAIT_TIMEOUT:
4820 case DB_INTERRUPTED:
4821 goto func_exit;
4822 default:
4823 ib::warn() << "CHECK TABLE on index " << index->name << " of"
4824 " table " << index->table->name << " returned " << ret;
4825 /* (this error is ignored by CHECK TABLE) */
4826 /* fall through */
4827 case DB_END_OF_INDEX:
4828 ret = DB_SUCCESS;
4829func_exit:
4830 ut_free(buf);
4831 mem_heap_free(heap);
4832
4833 return(ret);
4834 }
4835
4836 *n_rows = *n_rows + 1;
4837
4838 /* else this code is doing handler::check() for CHECK TABLE */
4839
4840 /* row_search... returns the index record in buf, record origin offset
4841 within buf stored in the first 4 bytes, because we have built a dummy
4842 template */
4843
4844 rec = buf + mach_read_from_4(buf);
4845
4846 offsets = rec_get_offsets(rec, index, offsets_, true,
4847 ULINT_UNDEFINED, &heap);
4848
4849 if (prev_entry != NULL) {
4850 matched_fields = 0;
4851
4852 cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets,
4853 &matched_fields);
4854 contains_null = FALSE;
4855
4856 /* In a unique secondary index we allow equal key values if
4857 they contain SQL NULLs */
4858
4859 for (i = 0;
4860 i < dict_index_get_n_ordering_defined_by_user(index);
4861 i++) {
4862 if (UNIV_SQL_NULL == dfield_get_len(
4863 dtuple_get_nth_field(prev_entry, i))) {
4864
4865 contains_null = TRUE;
4866 break;
4867 }
4868 }
4869
4870 const char* msg;
4871
4872 if (cmp > 0) {
4873 ret = DB_INDEX_CORRUPT;
4874 msg = "index records in a wrong order in ";
4875not_ok:
4876 ib::error()
4877 << msg << index->name
4878 << " of table " << index->table->name
4879 << ": " << *prev_entry << ", "
4880 << rec_offsets_print(rec, offsets);
4881 /* Continue reading */
4882 } else if (dict_index_is_unique(index)
4883 && !contains_null
4884 && matched_fields
4885 >= dict_index_get_n_ordering_defined_by_user(
4886 index)) {
4887 ret = DB_DUPLICATE_KEY;
4888 msg = "duplicate key in ";
4889 goto not_ok;
4890 }
4891 }
4892
4893 {
4894 mem_heap_t* tmp_heap = NULL;
4895
4896 /* Empty the heap on each round. But preserve offsets[]
4897 for the row_rec_to_index_entry() call, by copying them
4898 into a separate memory heap when needed. */
4899 if (UNIV_UNLIKELY(offsets != offsets_)) {
4900 ulint size = rec_offs_get_n_alloc(offsets)
4901 * sizeof *offsets;
4902
4903 tmp_heap = mem_heap_create(size);
4904
4905 offsets = static_cast<ulint*>(
4906 mem_heap_dup(tmp_heap, offsets, size));
4907 }
4908
4909 mem_heap_empty(heap);
4910
4911 prev_entry = row_rec_to_index_entry(
4912 rec, index, offsets, &n_ext, heap);
4913
4914 if (UNIV_LIKELY_NULL(tmp_heap)) {
4915 mem_heap_free(tmp_heap);
4916 }
4917 }
4918
4919 ret = row_search_for_mysql(
4920 buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT);
4921
4922 goto loop;
4923}
4924
4925/*********************************************************************//**
4926Initialize this module */
4927void
4928row_mysql_init(void)
4929/*================*/
4930{
4931 mutex_create(LATCH_ID_ROW_DROP_LIST, &row_drop_list_mutex);
4932
4933 UT_LIST_INIT(
4934 row_mysql_drop_list,
4935 &row_mysql_drop_t::row_mysql_drop_list);
4936
4937 row_mysql_drop_list_inited = TRUE;
4938}
4939
4940/*********************************************************************//**
4941Close this module */
4942void
4943row_mysql_close(void)
4944/*================*/
4945{
4946 ut_a(UT_LIST_GET_LEN(row_mysql_drop_list) == 0);
4947
4948 if (row_mysql_drop_list_inited) {
4949 mutex_free(&row_drop_list_mutex);
4950 row_mysql_drop_list_inited = FALSE;
4951 }
4952}
4953