1 /*****************************************************************************
2
3 Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2012, Facebook Inc.
5 Copyright (c) 2013, 2021, MariaDB Corporation.
6
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free Software
9 Foundation; version 2 of the License.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along with
16 this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
18
19 *****************************************************************************/
20
21 /******************************************************************//**
22 @file dict/dict0mem.cc
23 Data dictionary memory object creation
24
25 Created 1/8/1996 Heikki Tuuri
26 ***********************************************************************/
27
28 #include "ha_prototypes.h"
29 #include <mysql_com.h>
30
31 #include "dict0mem.h"
32 #include "rem0rec.h"
33 #include "data0type.h"
34 #include "mach0data.h"
35 #include "dict0dict.h"
36 #include "fts0priv.h"
37 #include "lock0lock.h"
38 #include "sync0sync.h"
39 #include "row0row.h"
40 #include "sql_string.h"
41 #include <iostream>
42
43 #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when
44 creating a table or index object */
45
46 /** System databases */
47 static const char* innobase_system_databases[] = {
48 "mysql/",
49 "information_schema/",
50 "performance_schema/",
51 NullS
52 };
53
54 /** Determine if a table belongs to innobase_system_databases[]
55 @param[in] name database_name/table_name
56 @return whether the database_name is in innobase_system_databases[] */
dict_mem_table_is_system(const char * name)57 static bool dict_mem_table_is_system(const char *name)
58 {
59 /* table has the following format: database/table
60 and some system table are of the form SYS_* */
61 if (!strchr(name, '/')) {
62 return true;
63 }
64 size_t table_len = strlen(name);
65 const char *system_db;
66 int i = 0;
67 while ((system_db = innobase_system_databases[i++])
68 && (system_db != NullS)) {
69 size_t len = strlen(system_db);
70 if (table_len > len && !strncmp(name, system_db, len)) {
71 return true;
72 }
73 }
74 return false;
75 }
76
77 /** The start of the table basename suffix for partitioned tables */
78 const char table_name_t::part_suffix[4]
79 #ifdef _WIN32
80 = "#p#";
81 #else
82 = "#P#";
83 #endif
84
85 /** Display an identifier.
86 @param[in,out] s output stream
87 @param[in] id_name SQL identifier (other than table name)
88 @return the output stream */
89 std::ostream&
operator <<(std::ostream & s,const id_name_t & id_name)90 operator<<(
91 std::ostream& s,
92 const id_name_t& id_name)
93 {
94 const char q = '`';
95 const char* c = id_name;
96 s << q;
97 for (; *c != 0; c++) {
98 if (*c == q) {
99 s << *c;
100 }
101 s << *c;
102 }
103 s << q;
104 return(s);
105 }
106
107 /** Display a table name.
108 @param[in,out] s output stream
109 @param[in] table_name table name
110 @return the output stream */
111 std::ostream&
operator <<(std::ostream & s,const table_name_t & table_name)112 operator<<(
113 std::ostream& s,
114 const table_name_t& table_name)
115 {
116 return(s << ut_get_name(NULL, table_name.m_name));
117 }
118
same_encoding(uint16_t a,uint16_t b)119 bool dict_col_t::same_encoding(uint16_t a, uint16_t b)
120 {
121 if (const CHARSET_INFO *acs= get_charset(a, MYF(MY_WME)))
122 if (const CHARSET_INFO *bcs= get_charset(b, MYF(MY_WME)))
123 return Charset(bcs).encoding_allows_reinterpret_as(acs);
124 return false;
125 }
126
127 /** Create a table memory object.
128 @param name table name
129 @param space tablespace
130 @param n_cols total number of columns (both virtual and non-virtual)
131 @param n_v_cols number of virtual columns
132 @param flags table flags
133 @param flags2 table flags2
134 @return own: table object */
dict_mem_table_create(const char * name,fil_space_t * space,ulint n_cols,ulint n_v_cols,ulint flags,ulint flags2)135 dict_table_t *dict_mem_table_create(const char *name, fil_space_t *space,
136 ulint n_cols, ulint n_v_cols, ulint flags,
137 ulint flags2)
138 {
139 dict_table_t* table;
140 mem_heap_t* heap;
141
142 ut_ad(name);
143 ut_ad(!space
144 || space->purpose == FIL_TYPE_TABLESPACE
145 || space->purpose == FIL_TYPE_TEMPORARY
146 || space->purpose == FIL_TYPE_IMPORT);
147 ut_a(dict_tf2_is_valid(flags, flags2));
148 ut_a(!(flags2 & DICT_TF2_UNUSED_BIT_MASK));
149
150 heap = mem_heap_create(DICT_HEAP_SIZE);
151
152 table = static_cast<dict_table_t*>(
153 mem_heap_zalloc(heap, sizeof(*table)));
154
155 lock_table_lock_list_init(&table->locks);
156
157 UT_LIST_INIT(table->indexes, &dict_index_t::indexes);
158 #ifdef BTR_CUR_HASH_ADAPT
159 UT_LIST_INIT(table->freed_indexes, &dict_index_t::indexes);
160 #endif /* BTR_CUR_HASH_ADAPT */
161
162 table->heap = heap;
163
164 ut_d(table->magic_n = DICT_TABLE_MAGIC_N);
165
166 table->flags = static_cast<unsigned>(flags)
167 & ((1U << DICT_TF_BITS) - 1);
168 table->flags2 = static_cast<unsigned>(flags2)
169 & ((1U << DICT_TF2_BITS) - 1);
170 table->name.m_name = mem_strdup(name);
171 table->is_system_db = dict_mem_table_is_system(table->name.m_name);
172 table->space = space;
173 table->space_id = space ? space->id : ULINT_UNDEFINED;
174 table->n_t_cols = static_cast<unsigned>(n_cols + DATA_N_SYS_COLS)
175 & dict_index_t::MAX_N_FIELDS;
176 table->n_v_cols = static_cast<unsigned>(n_v_cols)
177 & dict_index_t::MAX_N_FIELDS;
178 table->n_cols = static_cast<unsigned>(
179 table->n_t_cols - table->n_v_cols)
180 & dict_index_t::MAX_N_FIELDS;
181
182 table->cols = static_cast<dict_col_t*>(
183 mem_heap_alloc(heap, table->n_cols * sizeof(dict_col_t)));
184 table->v_cols = static_cast<dict_v_col_t*>(
185 mem_heap_alloc(heap, n_v_cols * sizeof(*table->v_cols)));
186 for (ulint i = n_v_cols; i--; ) {
187 new (&table->v_cols[i]) dict_v_col_t();
188 }
189
190 table->autoinc_lock = static_cast<ib_lock_t*>(
191 mem_heap_alloc(heap, lock_get_size()));
192
193 /* If the table has an FTS index or we are in the process
194 of building one, create the table->fts */
195 if (dict_table_has_fts_index(table)
196 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
197 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) {
198 table->fts = fts_create(table);
199 table->fts->cache = fts_cache_create(table);
200 }
201
202 new(&table->foreign_set) dict_foreign_set();
203 new(&table->referenced_set) dict_foreign_set();
204
205 return(table);
206 }
207
208 /****************************************************************//**
209 Free a table memory object. */
210 void
dict_mem_table_free(dict_table_t * table)211 dict_mem_table_free(
212 /*================*/
213 dict_table_t* table) /*!< in: table */
214 {
215 ut_ad(table);
216 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
217 ut_ad(UT_LIST_GET_LEN(table->indexes) == 0);
218 #ifdef BTR_CUR_HASH_ADAPT
219 ut_ad(UT_LIST_GET_LEN(table->freed_indexes) == 0);
220 #endif /* BTR_CUR_HASH_ADAPT */
221 ut_d(table->cached = FALSE);
222
223 if (dict_table_has_fts_index(table)
224 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
225 || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) {
226 if (table->fts) {
227 fts_free(table);
228 }
229 }
230
231 dict_mem_table_free_foreign_vcol_set(table);
232
233 table->foreign_set.~dict_foreign_set();
234 table->referenced_set.~dict_foreign_set();
235
236 ut_free(table->name.m_name);
237 table->name.m_name = NULL;
238
239 /* Clean up virtual index info structures that are registered
240 with virtual columns */
241 for (ulint i = 0; i < table->n_v_def; i++) {
242 dict_table_get_nth_v_col(table, i)->~dict_v_col_t();
243 }
244
245 UT_DELETE(table->s_cols);
246
247 mem_heap_free(table->heap);
248 }
249
250 /****************************************************************//**
251 Append 'name' to 'col_names'. @see dict_table_t::col_names
252 @return new column names array */
253 static
254 const char*
dict_add_col_name(const char * col_names,ulint cols,const char * name,mem_heap_t * heap)255 dict_add_col_name(
256 /*==============*/
257 const char* col_names, /*!< in: existing column names, or
258 NULL */
259 ulint cols, /*!< in: number of existing columns */
260 const char* name, /*!< in: new column name */
261 mem_heap_t* heap) /*!< in: heap */
262 {
263 ulint old_len;
264 ulint new_len;
265 ulint total_len;
266 char* res;
267
268 ut_ad(!cols == !col_names);
269
270 /* Find out length of existing array. */
271 if (col_names) {
272 const char* s = col_names;
273 ulint i;
274
275 for (i = 0; i < cols; i++) {
276 s += strlen(s) + 1;
277 }
278
279 old_len = unsigned(s - col_names);
280 } else {
281 old_len = 0;
282 }
283
284 new_len = strlen(name) + 1;
285 total_len = old_len + new_len;
286
287 res = static_cast<char*>(mem_heap_alloc(heap, total_len));
288
289 if (old_len > 0) {
290 memcpy(res, col_names, old_len);
291 }
292
293 memcpy(res + old_len, name, new_len);
294
295 return(res);
296 }
297
298 /**********************************************************************//**
299 Adds a column definition to a table. */
300 void
dict_mem_table_add_col(dict_table_t * table,mem_heap_t * heap,const char * name,ulint mtype,ulint prtype,ulint len)301 dict_mem_table_add_col(
302 /*===================*/
303 dict_table_t* table, /*!< in: table */
304 mem_heap_t* heap, /*!< in: temporary memory heap, or NULL */
305 const char* name, /*!< in: column name, or NULL */
306 ulint mtype, /*!< in: main datatype */
307 ulint prtype, /*!< in: precise type */
308 ulint len) /*!< in: precision */
309 {
310 dict_col_t* col;
311 unsigned i;
312
313 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
314 ut_ad(!heap == !name);
315
316 ut_ad(!(prtype & DATA_VIRTUAL));
317
318 i = table->n_def++;
319
320 table->n_t_def++;
321
322 if (name) {
323 if (table->n_def == table->n_cols) {
324 heap = table->heap;
325 }
326 if (i && !table->col_names) {
327 /* All preceding column names are empty. */
328 char* s = static_cast<char*>(
329 mem_heap_zalloc(heap, table->n_def));
330
331 table->col_names = s;
332 }
333
334 table->col_names = dict_add_col_name(table->col_names,
335 i, name, heap);
336 }
337
338 col = dict_table_get_nth_col(table, i);
339
340 dict_mem_fill_column_struct(col, i, mtype, prtype, len);
341
342 switch (prtype & DATA_VERSIONED) {
343 case DATA_VERS_START:
344 ut_ad(!table->vers_start);
345 table->vers_start = i & dict_index_t::MAX_N_FIELDS;
346 break;
347 case DATA_VERS_END:
348 ut_ad(!table->vers_end);
349 table->vers_end = i & dict_index_t::MAX_N_FIELDS;
350 }
351 }
352
353 /** Adds a virtual column definition to a table.
354 @param[in,out] table table
355 @param[in,out] heap temporary memory heap, or NULL. It is
356 used to store name when we have not finished
357 adding all columns. When all columns are
358 added, the whole name will copy to memory from
359 table->heap
360 @param[in] name column name
361 @param[in] mtype main datatype
362 @param[in] prtype precise type
363 @param[in] len length
364 @param[in] pos position in a table
365 @param[in] num_base number of base columns
366 @return the virtual column definition */
367 dict_v_col_t*
dict_mem_table_add_v_col(dict_table_t * table,mem_heap_t * heap,const char * name,ulint mtype,ulint prtype,ulint len,ulint pos,ulint num_base)368 dict_mem_table_add_v_col(
369 dict_table_t* table,
370 mem_heap_t* heap,
371 const char* name,
372 ulint mtype,
373 ulint prtype,
374 ulint len,
375 ulint pos,
376 ulint num_base)
377 {
378 dict_v_col_t* v_col;
379
380 ut_ad(table);
381 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
382 ut_ad(!heap == !name);
383
384 ut_ad(prtype & DATA_VIRTUAL);
385
386 unsigned i = table->n_v_def++;
387
388 table->n_t_def++;
389
390 if (name != NULL) {
391 if (table->n_v_def == table->n_v_cols) {
392 heap = table->heap;
393 }
394
395 if (i && !table->v_col_names) {
396 /* All preceding column names are empty. */
397 char* s = static_cast<char*>(
398 mem_heap_zalloc(heap, table->n_v_def));
399
400 table->v_col_names = s;
401 }
402
403 table->v_col_names = dict_add_col_name(table->v_col_names,
404 i, name, heap);
405 }
406
407 v_col = &table->v_cols[i];
408
409 dict_mem_fill_column_struct(&v_col->m_col, pos, mtype, prtype, len);
410 v_col->v_pos = i & dict_index_t::MAX_N_FIELDS;
411
412 if (num_base != 0) {
413 v_col->base_col = static_cast<dict_col_t**>(mem_heap_zalloc(
414 table->heap, num_base * sizeof(
415 *v_col->base_col)));
416 } else {
417 v_col->base_col = NULL;
418 }
419
420 v_col->num_base = static_cast<unsigned>(num_base)
421 & dict_index_t::MAX_N_FIELDS;
422
423 /* Initialize the index list for virtual columns */
424 ut_ad(v_col->v_indexes.empty());
425
426 return(v_col);
427 }
428
429 /** Adds a stored column definition to a table.
430 @param[in] table table
431 @param[in] num_base number of base columns. */
432 void
dict_mem_table_add_s_col(dict_table_t * table,ulint num_base)433 dict_mem_table_add_s_col(
434 dict_table_t* table,
435 ulint num_base)
436 {
437 unsigned i = unsigned(table->n_def) - 1;
438 dict_col_t* col = dict_table_get_nth_col(table, i);
439 dict_s_col_t s_col;
440
441 ut_ad(col != NULL);
442
443 if (table->s_cols == NULL) {
444 table->s_cols = UT_NEW_NOKEY(dict_s_col_list());
445 }
446
447 s_col.m_col = col;
448 s_col.s_pos = i + table->n_v_def;
449
450 if (num_base != 0) {
451 s_col.base_col = static_cast<dict_col_t**>(mem_heap_zalloc(
452 table->heap, num_base * sizeof(dict_col_t*)));
453 } else {
454 s_col.base_col = NULL;
455 }
456
457 s_col.num_base = num_base;
458 table->s_cols->push_front(s_col);
459 }
460
461 /**********************************************************************//**
462 Renames a column of a table in the data dictionary cache. */
463 static MY_ATTRIBUTE((nonnull))
464 void
dict_mem_table_col_rename_low(dict_table_t * table,unsigned i,const char * to,const char * s,bool is_virtual)465 dict_mem_table_col_rename_low(
466 /*==========================*/
467 dict_table_t* table, /*!< in/out: table */
468 unsigned i, /*!< in: column offset corresponding to s */
469 const char* to, /*!< in: new column name */
470 const char* s, /*!< in: pointer to table->col_names */
471 bool is_virtual)
472 /*!< in: if this is a virtual column */
473 {
474 char* t_col_names = const_cast<char*>(
475 is_virtual ? table->v_col_names : table->col_names);
476 ulint n_col = is_virtual ? table->n_v_def : table->n_def;
477
478 size_t from_len = strlen(s), to_len = strlen(to);
479
480 ut_ad(i < table->n_def || is_virtual);
481 ut_ad(i < table->n_v_def || !is_virtual);
482
483 ut_ad(from_len <= NAME_LEN);
484 ut_ad(to_len <= NAME_LEN);
485
486 char from[NAME_LEN + 1];
487 strncpy(from, s, sizeof from - 1);
488 from[sizeof from - 1] = '\0';
489
490 if (from_len == to_len) {
491 /* The easy case: simply replace the column name in
492 table->col_names. */
493 strcpy(const_cast<char*>(s), to);
494 } else {
495 /* We need to adjust all affected index->field
496 pointers, as in dict_index_add_col(). First, copy
497 table->col_names. */
498 ulint prefix_len = ulint(s - t_col_names);
499
500 for (; i < n_col; i++) {
501 s += strlen(s) + 1;
502 }
503
504 ulint full_len = ulint(s - t_col_names);
505 char* col_names;
506
507 if (to_len > from_len) {
508 col_names = static_cast<char*>(
509 mem_heap_alloc(
510 table->heap,
511 full_len + to_len - from_len));
512
513 memcpy(col_names, t_col_names, prefix_len);
514 } else {
515 col_names = const_cast<char*>(t_col_names);
516 }
517
518 memcpy(col_names + prefix_len, to, to_len);
519 memmove(col_names + prefix_len + to_len,
520 t_col_names + (prefix_len + from_len),
521 full_len - (prefix_len + from_len));
522
523 /* Replace the field names in every index. */
524 for (dict_index_t* index = dict_table_get_first_index(table);
525 index != NULL;
526 index = dict_table_get_next_index(index)) {
527 ulint n_fields = dict_index_get_n_fields(index);
528
529 for (ulint i = 0; i < n_fields; i++) {
530 dict_field_t* field
531 = dict_index_get_nth_field(
532 index, i);
533
534 ut_ad(!field->name
535 == field->col->is_dropped());
536 if (!field->name) {
537 /* dropped columns lack a name */
538 ut_ad(index->is_instant());
539 continue;
540 }
541
542 /* if is_virtual and that in field->col does
543 not match, continue */
544 if ((!is_virtual) !=
545 (!field->col->is_virtual())) {
546 continue;
547 }
548
549 ulint name_ofs
550 = ulint(field->name - t_col_names);
551 if (name_ofs <= prefix_len) {
552 field->name = col_names + name_ofs;
553 } else {
554 ut_a(name_ofs < full_len);
555 field->name = col_names
556 + name_ofs + to_len - from_len;
557 }
558 }
559 }
560
561 if (is_virtual) {
562 table->v_col_names = col_names;
563 } else {
564 table->col_names = col_names;
565 }
566 }
567
568 /* Virtual columns are not allowed for foreign key */
569 if (is_virtual) {
570 return;
571 }
572
573 dict_foreign_t* foreign;
574
575 /* Replace the field names in every foreign key constraint. */
576 for (dict_foreign_set::iterator it = table->foreign_set.begin();
577 it != table->foreign_set.end();
578 ++it) {
579
580 foreign = *it;
581
582 if (foreign->foreign_index == NULL) {
583 /* We may go here when we set foreign_key_checks to 0,
584 and then try to rename a column and modify the
585 corresponding foreign key constraint. The index
586 would have been dropped, we have to find an equivalent
587 one */
588 for (unsigned f = 0; f < foreign->n_fields; f++) {
589 if (strcmp(foreign->foreign_col_names[f], from)
590 == 0) {
591
592 char** rc = const_cast<char**>(
593 foreign->foreign_col_names
594 + f);
595
596 if (to_len <= strlen(*rc)) {
597 memcpy(*rc, to, to_len + 1);
598 } else {
599 *rc = static_cast<char*>(
600 mem_heap_dup(
601 foreign->heap,
602 to,
603 to_len + 1));
604 }
605 }
606 }
607
608 /* New index can be null if InnoDB already dropped
609 the foreign index when FOREIGN_KEY_CHECKS is
610 disabled */
611 foreign->foreign_index = dict_foreign_find_index(
612 foreign->foreign_table, NULL,
613 foreign->foreign_col_names,
614 foreign->n_fields, NULL, true, false,
615 NULL, NULL, NULL);
616
617 } else {
618
619 for (unsigned f = 0; f < foreign->n_fields; f++) {
620 /* These can point straight to
621 table->col_names, because the foreign key
622 constraints will be freed at the same time
623 when the table object is freed. */
624 foreign->foreign_col_names[f]
625 = dict_index_get_nth_field(
626 foreign->foreign_index,
627 f)->name;
628 }
629 }
630 }
631
632 for (dict_foreign_set::iterator it = table->referenced_set.begin();
633 it != table->referenced_set.end();
634 ++it) {
635
636 foreign = *it;
637
638 if (!foreign->referenced_index) {
639 /* Referenced index could have been dropped
640 when foreign_key_checks is disabled. In that case,
641 rename the corresponding referenced_col_names and
642 find the equivalent referenced index also */
643 for (unsigned f = 0; f < foreign->n_fields; f++) {
644
645 const char*& rc =
646 foreign->referenced_col_names[f];
647 if (strcmp(rc, from)) {
648 continue;
649 }
650
651 if (to_len <= strlen(rc)) {
652 memcpy(const_cast<char*>(rc), to,
653 to_len + 1);
654 } else {
655 rc = static_cast<char*>(
656 mem_heap_dup(
657 foreign->heap,
658 to, to_len + 1));
659 }
660 }
661
662 /* New index can be null if InnoDB already dropped
663 the referenced index when FOREIGN_KEY_CHECKS is
664 disabled */
665 foreign->referenced_index = dict_foreign_find_index(
666 foreign->referenced_table, NULL,
667 foreign->referenced_col_names,
668 foreign->n_fields, NULL, true, false,
669 NULL, NULL, NULL);
670 return;
671 }
672
673
674 for (unsigned f = 0; f < foreign->n_fields; f++) {
675 /* foreign->referenced_col_names[] need to be
676 copies, because the constraint may become
677 orphan when foreign_key_checks=0 and the
678 parent table is dropped. */
679
680 const char* col_name = dict_index_get_nth_field(
681 foreign->referenced_index, f)->name;
682
683 if (strcmp(foreign->referenced_col_names[f],
684 col_name)) {
685 char** rc = const_cast<char**>(
686 foreign->referenced_col_names + f);
687 size_t col_name_len_1 = strlen(col_name) + 1;
688
689 if (col_name_len_1 <= strlen(*rc) + 1) {
690 memcpy(*rc, col_name, col_name_len_1);
691 } else {
692 *rc = static_cast<char*>(
693 mem_heap_dup(
694 foreign->heap,
695 col_name,
696 col_name_len_1));
697 }
698 }
699 }
700 }
701 }
702
703 /**********************************************************************//**
704 Renames a column of a table in the data dictionary cache. */
705 void
dict_mem_table_col_rename(dict_table_t * table,ulint nth_col,const char * from,const char * to,bool is_virtual)706 dict_mem_table_col_rename(
707 /*======================*/
708 dict_table_t* table, /*!< in/out: table */
709 ulint nth_col,/*!< in: column index */
710 const char* from, /*!< in: old column name */
711 const char* to, /*!< in: new column name */
712 bool is_virtual)
713 /*!< in: if this is a virtual column */
714 {
715 const char* s = is_virtual ? table->v_col_names : table->col_names;
716
717 ut_ad((!is_virtual && nth_col < table->n_def)
718 || (is_virtual && nth_col < table->n_v_def));
719
720 for (ulint i = 0; i < nth_col; i++) {
721 size_t len = strlen(s);
722 ut_ad(len > 0);
723 s += len + 1;
724 }
725
726 ut_ad(!my_strcasecmp(system_charset_info, from, s));
727
728 dict_mem_table_col_rename_low(table, static_cast<unsigned>(nth_col),
729 to, s, is_virtual);
730 }
731
732 /**********************************************************************//**
733 This function populates a dict_col_t memory structure with
734 supplied information. */
735 void
dict_mem_fill_column_struct(dict_col_t * column,ulint col_pos,ulint mtype,ulint prtype,ulint col_len)736 dict_mem_fill_column_struct(
737 /*========================*/
738 dict_col_t* column, /*!< out: column struct to be
739 filled */
740 ulint col_pos, /*!< in: column position */
741 ulint mtype, /*!< in: main data type */
742 ulint prtype, /*!< in: precise type */
743 ulint col_len) /*!< in: column length */
744 {
745 unsigned mbminlen, mbmaxlen;
746
747 column->ind = static_cast<unsigned>(col_pos)
748 & dict_index_t::MAX_N_FIELDS;
749 column->ord_part = 0;
750 column->max_prefix = 0;
751 column->mtype = static_cast<uint8_t>(mtype);
752 column->prtype = static_cast<unsigned>(prtype);
753 column->len = static_cast<uint16_t>(col_len);
754 dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen);
755 column->mbminlen = mbminlen & 7;
756 column->mbmaxlen = mbmaxlen & 7;
757 column->def_val.data = NULL;
758 column->def_val.len = UNIV_SQL_DEFAULT;
759 ut_ad(!column->is_dropped());
760 }
761
762 /**********************************************************************//**
763 Creates an index memory object.
764 @return own: index object */
765 dict_index_t*
dict_mem_index_create(dict_table_t * table,const char * index_name,ulint type,ulint n_fields)766 dict_mem_index_create(
767 /*==================*/
768 dict_table_t* table, /*!< in: table */
769 const char* index_name, /*!< in: index name */
770 ulint type, /*!< in: DICT_UNIQUE,
771 DICT_CLUSTERED, ... ORed */
772 ulint n_fields) /*!< in: number of fields */
773 {
774 dict_index_t* index;
775 mem_heap_t* heap;
776
777 ut_ad(!table || table->magic_n == DICT_TABLE_MAGIC_N);
778 ut_ad(index_name);
779
780 heap = mem_heap_create(DICT_HEAP_SIZE);
781
782 index = static_cast<dict_index_t*>(
783 mem_heap_zalloc(heap, sizeof(*index)));
784 index->table = table;
785
786 dict_mem_fill_index_struct(index, heap, index_name, type, n_fields);
787
788 new (&index->zip_pad.mutex) std::mutex();
789
790 if (type & DICT_SPATIAL) {
791 index->rtr_track = new
792 (mem_heap_alloc(heap, sizeof *index->rtr_track))
793 rtr_info_track_t();
794 mutex_create(LATCH_ID_RTR_ACTIVE_MUTEX,
795 &index->rtr_track->rtr_active_mutex);
796 }
797
798 return(index);
799 }
800
801 /**********************************************************************//**
802 Creates and initializes a foreign constraint memory object.
803 @return own: foreign constraint struct */
804 dict_foreign_t*
dict_mem_foreign_create(void)805 dict_mem_foreign_create(void)
806 /*=========================*/
807 {
808 dict_foreign_t* foreign;
809 mem_heap_t* heap;
810 DBUG_ENTER("dict_mem_foreign_create");
811
812 heap = mem_heap_create(100);
813
814 foreign = static_cast<dict_foreign_t*>(
815 mem_heap_zalloc(heap, sizeof(dict_foreign_t)));
816
817 foreign->heap = heap;
818
819 foreign->v_cols = NULL;
820
821 DBUG_PRINT("dict_mem_foreign_create", ("heap: %p", heap));
822
823 DBUG_RETURN(foreign);
824 }
825
826 /**********************************************************************//**
827 Sets the foreign_table_name_lookup pointer based on the value of
828 lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup
829 will point to foreign_table_name. If 2, then another string is
830 allocated from foreign->heap and set to lower case. */
831 void
dict_mem_foreign_table_name_lookup_set(dict_foreign_t * foreign,ibool do_alloc)832 dict_mem_foreign_table_name_lookup_set(
833 /*===================================*/
834 dict_foreign_t* foreign, /*!< in/out: foreign struct */
835 ibool do_alloc) /*!< in: is an alloc needed */
836 {
837 if (innobase_get_lower_case_table_names() == 2) {
838 if (do_alloc) {
839 ulint len;
840
841 len = strlen(foreign->foreign_table_name) + 1;
842
843 foreign->foreign_table_name_lookup =
844 static_cast<char*>(
845 mem_heap_alloc(foreign->heap, len));
846 }
847 strcpy(foreign->foreign_table_name_lookup,
848 foreign->foreign_table_name);
849 innobase_casedn_str(foreign->foreign_table_name_lookup);
850 } else {
851 foreign->foreign_table_name_lookup
852 = foreign->foreign_table_name;
853 }
854 }
855
856 /**********************************************************************//**
857 Sets the referenced_table_name_lookup pointer based on the value of
858 lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup
859 will point to referenced_table_name. If 2, then another string is
860 allocated from foreign->heap and set to lower case. */
861 void
dict_mem_referenced_table_name_lookup_set(dict_foreign_t * foreign,ibool do_alloc)862 dict_mem_referenced_table_name_lookup_set(
863 /*======================================*/
864 dict_foreign_t* foreign, /*!< in/out: foreign struct */
865 ibool do_alloc) /*!< in: is an alloc needed */
866 {
867 if (innobase_get_lower_case_table_names() == 2) {
868 if (do_alloc) {
869 ulint len;
870
871 len = strlen(foreign->referenced_table_name) + 1;
872
873 foreign->referenced_table_name_lookup =
874 static_cast<char*>(
875 mem_heap_alloc(foreign->heap, len));
876 }
877 strcpy(foreign->referenced_table_name_lookup,
878 foreign->referenced_table_name);
879 innobase_casedn_str(foreign->referenced_table_name_lookup);
880 } else {
881 foreign->referenced_table_name_lookup
882 = foreign->referenced_table_name;
883 }
884 }
885
886 /** Fill the virtual column set with virtual column information
887 present in the given virtual index.
888 @param[in] index virtual index
889 @param[out] v_cols virtual column set. */
890 static
891 void
dict_mem_fill_vcol_has_index(const dict_index_t * index,dict_vcol_set ** v_cols)892 dict_mem_fill_vcol_has_index(
893 const dict_index_t* index,
894 dict_vcol_set** v_cols)
895 {
896 for (ulint i = 0; i < index->table->n_v_cols; i++) {
897 dict_v_col_t* v_col = dict_table_get_nth_v_col(
898 index->table, i);
899 if (!v_col->m_col.ord_part) {
900 continue;
901 }
902
903 for (const auto& v_idx : v_col->v_indexes) {
904 if (v_idx.index != index) {
905 continue;
906 }
907
908 if (*v_cols == NULL) {
909 *v_cols = UT_NEW_NOKEY(dict_vcol_set());
910 }
911
912 (*v_cols)->insert(v_col);
913 }
914 }
915 }
916
917 /** Fill the virtual column set with the virtual column of the index
918 if the index contains given column name.
919 @param[in] col_name column name
920 @param[in] table innodb table object
921 @param[out] v_cols set of virtual column information. */
922 static
923 void
dict_mem_fill_vcol_from_v_indexes(const char * col_name,const dict_table_t * table,dict_vcol_set ** v_cols)924 dict_mem_fill_vcol_from_v_indexes(
925 const char* col_name,
926 const dict_table_t* table,
927 dict_vcol_set** v_cols)
928 {
929 /* virtual column can't be Primary Key, so start with
930 secondary index */
931 for (dict_index_t* index = dict_table_get_next_index(
932 dict_table_get_first_index(table));
933 index;
934 index = dict_table_get_next_index(index)) {
935
936 /* Skip if the index have newly added
937 virtual column because field name is NULL.
938 Later virtual column set will be
939 refreshed during loading of table. */
940 if (!dict_index_has_virtual(index)
941 || index->has_new_v_col()) {
942 continue;
943 }
944
945 for (ulint i = 0; i < index->n_fields; i++) {
946 dict_field_t* field =
947 dict_index_get_nth_field(index, i);
948
949 if (strcmp(field->name, col_name) == 0) {
950 dict_mem_fill_vcol_has_index(
951 index, v_cols);
952 }
953 }
954 }
955 }
956
957 /** Fill the virtual column set with virtual columns which have base columns
958 as the given col_name
959 @param[in] col_name column name
960 @param[in] table table object
961 @param[out] v_cols set of virtual columns. */
962 static
963 void
dict_mem_fill_vcol_set_for_base_col(const char * col_name,const dict_table_t * table,dict_vcol_set ** v_cols)964 dict_mem_fill_vcol_set_for_base_col(
965 const char* col_name,
966 const dict_table_t* table,
967 dict_vcol_set** v_cols)
968 {
969 for (ulint i = 0; i < table->n_v_cols; i++) {
970 dict_v_col_t* v_col = dict_table_get_nth_v_col(table, i);
971
972 if (!v_col->m_col.ord_part) {
973 continue;
974 }
975
976 for (ulint j = 0; j < unsigned{v_col->num_base}; j++) {
977 if (strcmp(col_name, dict_table_get_col_name(
978 table,
979 v_col->base_col[j]->ind)) == 0) {
980
981 if (*v_cols == NULL) {
982 *v_cols = UT_NEW_NOKEY(dict_vcol_set());
983 }
984
985 (*v_cols)->insert(v_col);
986 }
987 }
988 }
989 }
990
991 /** Fills the dependent virtual columns in a set.
992 Reason for being dependent are
993 1) FK can be present on base column of virtual columns
994 2) FK can be present on column which is a part of virtual index
995 @param[in,out] foreign foreign key information. */
996 void
dict_mem_foreign_fill_vcol_set(dict_foreign_t * foreign)997 dict_mem_foreign_fill_vcol_set(
998 dict_foreign_t* foreign)
999 {
1000 ulint type = foreign->type;
1001
1002 if (type == 0) {
1003 return;
1004 }
1005
1006 for (ulint i = 0; i < foreign->n_fields; i++) {
1007 /** FK can be present on base columns
1008 of virtual columns. */
1009 dict_mem_fill_vcol_set_for_base_col(
1010 foreign->foreign_col_names[i],
1011 foreign->foreign_table,
1012 &foreign->v_cols);
1013
1014 /** FK can be present on the columns
1015 which can be a part of virtual index. */
1016 dict_mem_fill_vcol_from_v_indexes(
1017 foreign->foreign_col_names[i],
1018 foreign->foreign_table,
1019 &foreign->v_cols);
1020 }
1021 }
1022
1023 /** Fill virtual columns set in each fk constraint present in the table.
1024 @param[in,out] table innodb table object. */
1025 void
dict_mem_table_fill_foreign_vcol_set(dict_table_t * table)1026 dict_mem_table_fill_foreign_vcol_set(
1027 dict_table_t* table)
1028 {
1029 dict_foreign_set fk_set = table->foreign_set;
1030 dict_foreign_t* foreign;
1031
1032 dict_foreign_set::iterator it;
1033 for (it = fk_set.begin(); it != fk_set.end(); ++it) {
1034 foreign = *it;
1035
1036 dict_mem_foreign_fill_vcol_set(foreign);
1037 }
1038 }
1039
1040 /** Free the vcol_set from all foreign key constraint on the table.
1041 @param[in,out] table innodb table object. */
1042 void
dict_mem_table_free_foreign_vcol_set(dict_table_t * table)1043 dict_mem_table_free_foreign_vcol_set(
1044 dict_table_t* table)
1045 {
1046 dict_foreign_set fk_set = table->foreign_set;
1047 dict_foreign_t* foreign;
1048
1049 dict_foreign_set::iterator it;
1050 for (it = fk_set.begin(); it != fk_set.end(); ++it) {
1051
1052 foreign = *it;
1053
1054 if (foreign->v_cols != NULL) {
1055 UT_DELETE(foreign->v_cols);
1056 foreign->v_cols = NULL;
1057 }
1058 }
1059 }
1060
1061 /**********************************************************************//**
1062 Adds a field definition to an index. NOTE: does not take a copy
1063 of the column name if the field is a column. The memory occupied
1064 by the column name may be released only after publishing the index. */
1065 void
dict_mem_index_add_field(dict_index_t * index,const char * name,ulint prefix_len)1066 dict_mem_index_add_field(
1067 /*=====================*/
1068 dict_index_t* index, /*!< in: index */
1069 const char* name, /*!< in: column name */
1070 ulint prefix_len) /*!< in: 0 or the column prefix length
1071 in a MySQL index like
1072 INDEX (textcol(25)) */
1073 {
1074 dict_field_t* field;
1075
1076 ut_ad(index);
1077 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
1078
1079 index->n_def++;
1080
1081 field = dict_index_get_nth_field(index, unsigned(index->n_def) - 1);
1082
1083 field->name = name;
1084 field->prefix_len = prefix_len & ((1U << 12) - 1);
1085 }
1086
1087 /**********************************************************************//**
1088 Frees an index memory object. */
1089 void
dict_mem_index_free(dict_index_t * index)1090 dict_mem_index_free(
1091 /*================*/
1092 dict_index_t* index) /*!< in: index */
1093 {
1094 ut_ad(index);
1095 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
1096
1097 index->zip_pad.mutex.~mutex();
1098
1099 if (dict_index_is_spatial(index)) {
1100 for (auto& rtr_info : index->rtr_track->rtr_active) {
1101 rtr_info->index = NULL;
1102 }
1103
1104 mutex_destroy(&index->rtr_track->rtr_active_mutex);
1105 index->rtr_track->~rtr_info_track_t();
1106 }
1107
1108 index->detach_columns();
1109 mem_heap_free(index->heap);
1110 }
1111
1112 /** Create a temporary tablename like "#sql-ibNNN".
1113 @param[in] heap A memory heap
1114 @param[in] dbtab Table name in the form database/table name
1115 @param[in] id Table id
1116 @return A unique temporary tablename suitable for InnoDB use */
1117 char*
dict_mem_create_temporary_tablename(mem_heap_t * heap,const char * dbtab,table_id_t id)1118 dict_mem_create_temporary_tablename(
1119 mem_heap_t* heap,
1120 const char* dbtab,
1121 table_id_t id)
1122 {
1123 size_t size;
1124 char* name;
1125 const char* dbend = strchr(dbtab, '/');
1126 ut_ad(dbend);
1127 size_t dblen = size_t(dbend - dbtab) + 1;
1128
1129 size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20);
1130 name = static_cast<char*>(mem_heap_alloc(heap, size));
1131 memcpy(name, dbtab, dblen);
1132 snprintf(name + dblen, size - dblen,
1133 TEMP_FILE_PREFIX_INNODB UINT64PF, id);
1134
1135 return(name);
1136 }
1137
1138 /** Validate the search order in the foreign key set.
1139 @param[in] fk_set the foreign key set to be validated
1140 @return true if search order is fine in the set, false otherwise. */
1141 bool
dict_foreign_set_validate(const dict_foreign_set & fk_set)1142 dict_foreign_set_validate(
1143 const dict_foreign_set& fk_set)
1144 {
1145 dict_foreign_not_exists not_exists(fk_set);
1146
1147 dict_foreign_set::const_iterator it = std::find_if(
1148 fk_set.begin(), fk_set.end(), not_exists);
1149
1150 if (it == fk_set.end()) {
1151 return(true);
1152 }
1153
1154 dict_foreign_t* foreign = *it;
1155 std::cerr << "Foreign key lookup failed: " << *foreign;
1156 std::cerr << fk_set;
1157 ut_ad(0);
1158 return(false);
1159 }
1160
1161 /** Validate the search order in the foreign key sets of the table
1162 (foreign_set and referenced_set).
1163 @param[in] table table whose foreign key sets are to be validated
1164 @return true if foreign key sets are fine, false otherwise. */
1165 bool
dict_foreign_set_validate(const dict_table_t & table)1166 dict_foreign_set_validate(
1167 const dict_table_t& table)
1168 {
1169 return(dict_foreign_set_validate(table.foreign_set)
1170 && dict_foreign_set_validate(table.referenced_set));
1171 }
1172
1173 std::ostream&
operator <<(std::ostream & out,const dict_foreign_t & foreign)1174 operator<< (std::ostream& out, const dict_foreign_t& foreign)
1175 {
1176 out << "[dict_foreign_t: id='" << foreign.id << "'";
1177
1178 if (foreign.foreign_table_name != NULL) {
1179 out << ",for: '" << foreign.foreign_table_name << "'";
1180 }
1181
1182 out << "]";
1183 return(out);
1184 }
1185
1186 std::ostream&
operator <<(std::ostream & out,const dict_foreign_set & fk_set)1187 operator<< (std::ostream& out, const dict_foreign_set& fk_set)
1188 {
1189 out << "[dict_foreign_set:";
1190 std::for_each(fk_set.begin(), fk_set.end(), dict_foreign_print(out));
1191 out << "]" << std::endl;
1192 return(out);
1193 }
1194
1195 /** Check whether fulltext index gets affected by foreign
1196 key constraint. */
affects_fulltext() const1197 bool dict_foreign_t::affects_fulltext() const
1198 {
1199 if (foreign_table == referenced_table || !foreign_table->fts)
1200 return false;
1201
1202 for (ulint i= 0; i < n_fields; i++)
1203 {
1204 const dict_col_t *col= dict_index_get_nth_col(foreign_index, i);
1205 if (dict_table_is_fts_column(foreign_table->fts->indexes, col->ind,
1206 col->is_virtual()) != ULINT_UNDEFINED)
1207 return true;
1208 }
1209
1210 return false;
1211 }
1212
1213 /** Reconstruct the clustered index fields.
1214 @return whether metadata is incorrect */
reconstruct_fields()1215 inline bool dict_index_t::reconstruct_fields()
1216 {
1217 DBUG_ASSERT(is_primary());
1218
1219 n_fields = (n_fields + table->instant->n_dropped)
1220 & dict_index_t::MAX_N_FIELDS;
1221 n_def = (n_def + table->instant->n_dropped)
1222 & dict_index_t::MAX_N_FIELDS;
1223
1224 const unsigned n_first = first_user_field();
1225
1226 dict_field_t* tfields = static_cast<dict_field_t*>(
1227 mem_heap_zalloc(heap, n_fields * sizeof *fields));
1228
1229 memcpy(tfields, fields, n_first * sizeof *fields);
1230
1231 n_nullable = 0;
1232 ulint n_core_null = 0;
1233 const bool comp = dict_table_is_comp(table);
1234 const auto* field_map_it = table->instant->field_map;
1235 for (unsigned i = n_first, j = 0; i < n_fields; ) {
1236 dict_field_t& f = tfields[i++];
1237 auto c = *field_map_it++;
1238 if (c.is_dropped()) {
1239 f.col = &table->instant->dropped[j++];
1240 DBUG_ASSERT(f.col->is_dropped());
1241 f.fixed_len = dict_col_get_fixed_size(f.col, comp)
1242 & ((1U << 10) - 1);
1243 } else {
1244 DBUG_ASSERT(!c.is_not_null());
1245 const auto old = std::find_if(
1246 fields + n_first, fields + n_fields,
1247 [c](const dict_field_t& o)
1248 { return o.col->ind == c.ind(); });
1249
1250 if (old >= fields + n_fields
1251 || old->prefix_len
1252 || old->col != &table->cols[c.ind()]) {
1253 return true;
1254 }
1255
1256 ut_ad(old >= &fields[n_first]);
1257 f = *old;
1258 }
1259
1260 f.col->clear_instant();
1261 if (f.col->is_nullable()) {
1262 n_nullable++;
1263 n_core_null += i <= n_core_fields;
1264 }
1265 }
1266
1267 fields = tfields;
1268 n_core_null_bytes = static_cast<byte>(UT_BITS_IN_BYTES(n_core_null));
1269
1270 return false;
1271 }
1272
1273 /** Reconstruct dropped or reordered columns.
1274 @param[in] metadata data from serialise_columns()
1275 @param[in] len length of the metadata, in bytes
1276 @return whether parsing the metadata failed */
deserialise_columns(const byte * metadata,ulint len)1277 bool dict_table_t::deserialise_columns(const byte* metadata, ulint len)
1278 {
1279 DBUG_ASSERT(!instant);
1280
1281 unsigned num_non_pk_fields = mach_read_from_4(metadata);
1282 metadata += 4;
1283
1284 if (num_non_pk_fields >= REC_MAX_N_FIELDS - 3) {
1285 return true;
1286 }
1287
1288 dict_index_t* index = UT_LIST_GET_FIRST(indexes);
1289
1290 if (num_non_pk_fields < unsigned(index->n_fields)
1291 - index->first_user_field()) {
1292 return true;
1293 }
1294
1295 field_map_element_t* field_map = static_cast<field_map_element_t*>(
1296 mem_heap_alloc(heap,
1297 num_non_pk_fields * sizeof *field_map));
1298
1299 unsigned n_dropped_cols = 0;
1300
1301 for (unsigned i = 0; i < num_non_pk_fields; i++) {
1302 auto c = field_map[i] = mach_read_from_2(metadata);
1303 metadata += 2;
1304
1305 if (field_map[i].is_dropped()) {
1306 if (c.ind() > DICT_MAX_FIXED_COL_LEN + 1) {
1307 return true;
1308 }
1309 n_dropped_cols++;
1310 } else if (c >= n_cols) {
1311 return true;
1312 }
1313 }
1314
1315 dict_col_t* dropped_cols = static_cast<dict_col_t*>(mem_heap_zalloc(
1316 heap, n_dropped_cols * sizeof(dict_col_t)));
1317 instant = new (mem_heap_alloc(heap, sizeof *instant)) dict_instant_t();
1318 instant->n_dropped = n_dropped_cols;
1319 instant->dropped = dropped_cols;
1320 instant->field_map = field_map;
1321
1322 dict_col_t* col = dropped_cols;
1323 for (unsigned i = 0; i < num_non_pk_fields; i++) {
1324 if (field_map[i].is_dropped()) {
1325 auto fixed_len = field_map[i].ind();
1326 DBUG_ASSERT(fixed_len <= DICT_MAX_FIXED_COL_LEN + 1);
1327 (col++)->set_dropped(field_map[i].is_not_null(),
1328 fixed_len == 1,
1329 fixed_len > 1 ? fixed_len - 1
1330 : 0);
1331 }
1332 }
1333 DBUG_ASSERT(col == &dropped_cols[n_dropped_cols]);
1334
1335 return UT_LIST_GET_FIRST(indexes)->reconstruct_fields();
1336 }
1337
1338 /** Check if record in clustered index is historical row.
1339 @param[in] rec clustered row
1340 @param[in] offsets offsets
1341 @return true if row is historical */
1342 bool
vers_history_row(const rec_t * rec,const rec_offs * offsets)1343 dict_index_t::vers_history_row(
1344 const rec_t* rec,
1345 const rec_offs* offsets)
1346 {
1347 ut_ad(is_primary());
1348
1349 ulint len;
1350 dict_col_t& col= table->cols[table->vers_end];
1351 ut_ad(col.vers_sys_end());
1352 ulint nfield = dict_col_get_clust_pos(&col, this);
1353 const byte *data = rec_get_nth_field(rec, offsets, nfield, &len);
1354 if (col.vers_native()) {
1355 ut_ad(len == sizeof trx_id_max_bytes);
1356 return 0 != memcmp(data, trx_id_max_bytes, len);
1357 }
1358 ut_ad(len == sizeof timestamp_max_bytes);
1359 return 0 != memcmp(data, timestamp_max_bytes, len);
1360 }
1361
1362 /** Check if record in secondary index is historical row.
1363 @param[in] rec record in a secondary index
1364 @param[out] history_row true if row is historical
1365 @return true on error */
1366 bool
vers_history_row(const rec_t * rec,bool & history_row)1367 dict_index_t::vers_history_row(
1368 const rec_t* rec,
1369 bool &history_row)
1370 {
1371 ut_ad(!is_primary());
1372
1373 bool error = false;
1374 mem_heap_t* heap = NULL;
1375 dict_index_t* clust_index = NULL;
1376 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1377 rec_offs* offsets = offsets_;
1378 rec_offs_init(offsets_);
1379
1380 mtr_t mtr;
1381 mtr.start();
1382
1383 rec_t* clust_rec =
1384 row_get_clust_rec(BTR_SEARCH_LEAF, rec, this, &clust_index, &mtr);
1385 if (clust_rec) {
1386 offsets = rec_get_offsets(clust_rec, clust_index, offsets,
1387 clust_index->n_core_fields,
1388 ULINT_UNDEFINED, &heap);
1389
1390 history_row = clust_index->vers_history_row(clust_rec, offsets);
1391 } else {
1392 ib::error() << "foreign constraints: secondary index is out of "
1393 "sync";
1394 ut_ad("secondary index is out of sync" == 0);
1395 error = true;
1396 }
1397 mtr.commit();
1398 if (heap) {
1399 mem_heap_free(heap);
1400 }
1401 return(error);
1402 }
1403