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, 2018, 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, Suite 500, 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 "ut0crc32.h" |
38 | #include "lock0lock.h" |
39 | #include "sync0sync.h" |
40 | #include "row0row.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[] */ |
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 | /** An interger randomly initialized at startup used to make a temporary |
86 | table name as unuique as possible. */ |
87 | static ib_uint32_t dict_temp_file_num; |
88 | |
89 | /** Display an identifier. |
90 | @param[in,out] s output stream |
91 | @param[in] id_name SQL identifier (other than table name) |
92 | @return the output stream */ |
93 | std::ostream& |
94 | operator<<( |
95 | std::ostream& s, |
96 | const id_name_t& id_name) |
97 | { |
98 | const char q = '`'; |
99 | const char* c = id_name; |
100 | s << q; |
101 | for (; *c != 0; c++) { |
102 | if (*c == q) { |
103 | s << *c; |
104 | } |
105 | s << *c; |
106 | } |
107 | s << q; |
108 | return(s); |
109 | } |
110 | |
111 | /** Display a table name. |
112 | @param[in,out] s output stream |
113 | @param[in] table_name table name |
114 | @return the output stream */ |
115 | std::ostream& |
116 | operator<<( |
117 | std::ostream& s, |
118 | const table_name_t& table_name) |
119 | { |
120 | return(s << ut_get_name(NULL, table_name.m_name)); |
121 | } |
122 | |
123 | /**********************************************************************//** |
124 | Creates a table memory object. |
125 | @return own: table object */ |
126 | dict_table_t* |
127 | dict_mem_table_create( |
128 | /*==================*/ |
129 | const char* name, /*!< in: table name */ |
130 | fil_space_t* space, /*!< in: tablespace */ |
131 | ulint n_cols, /*!< in: total number of columns including |
132 | virtual and non-virtual columns */ |
133 | ulint n_v_cols,/*!< in: number of virtual columns */ |
134 | ulint flags, /*!< in: table flags */ |
135 | ulint flags2) /*!< in: table flags2 */ |
136 | { |
137 | dict_table_t* table; |
138 | mem_heap_t* heap; |
139 | |
140 | ut_ad(name); |
141 | ut_ad(!space |
142 | || space->purpose == FIL_TYPE_TABLESPACE |
143 | || space->purpose == FIL_TYPE_TEMPORARY |
144 | || space->purpose == FIL_TYPE_IMPORT); |
145 | ut_a(dict_tf2_is_valid(flags, flags2)); |
146 | ut_a(!(flags2 & DICT_TF2_UNUSED_BIT_MASK)); |
147 | |
148 | heap = mem_heap_create(DICT_HEAP_SIZE); |
149 | |
150 | table = static_cast<dict_table_t*>( |
151 | mem_heap_zalloc(heap, sizeof(*table))); |
152 | |
153 | lock_table_lock_list_init(&table->locks); |
154 | |
155 | UT_LIST_INIT(table->indexes, &dict_index_t::indexes); |
156 | |
157 | table->heap = heap; |
158 | |
159 | ut_d(table->magic_n = DICT_TABLE_MAGIC_N); |
160 | |
161 | table->flags = (unsigned int) flags; |
162 | table->flags2 = (unsigned int) flags2; |
163 | table->name.m_name = mem_strdup(name); |
164 | table->is_system_db = dict_mem_table_is_system(table->name.m_name); |
165 | table->space = space; |
166 | table->space_id = space ? space->id : ULINT_UNDEFINED; |
167 | table->n_t_cols = unsigned(n_cols + DATA_N_SYS_COLS); |
168 | table->n_v_cols = (unsigned int) (n_v_cols); |
169 | table->n_cols = unsigned(table->n_t_cols - table->n_v_cols); |
170 | |
171 | table->cols = static_cast<dict_col_t*>( |
172 | mem_heap_alloc(heap, table->n_cols * sizeof(dict_col_t))); |
173 | table->v_cols = static_cast<dict_v_col_t*>( |
174 | mem_heap_alloc(heap, n_v_cols * sizeof(*table->v_cols))); |
175 | |
176 | /* true means that the stats latch will be enabled - |
177 | dict_table_stats_lock() will not be noop. */ |
178 | dict_table_stats_latch_create(table, true); |
179 | |
180 | table->autoinc_lock = static_cast<ib_lock_t*>( |
181 | mem_heap_alloc(heap, lock_get_size())); |
182 | |
183 | /* lazy creation of table autoinc latch */ |
184 | dict_table_autoinc_create_lazy(table); |
185 | |
186 | /* If the table has an FTS index or we are in the process |
187 | of building one, create the table->fts */ |
188 | if (dict_table_has_fts_index(table) |
189 | || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) |
190 | || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) { |
191 | table->fts = fts_create(table); |
192 | table->fts->cache = fts_cache_create(table); |
193 | } else { |
194 | table->fts = NULL; |
195 | } |
196 | |
197 | new(&table->foreign_set) dict_foreign_set(); |
198 | new(&table->referenced_set) dict_foreign_set(); |
199 | |
200 | return(table); |
201 | } |
202 | |
203 | /****************************************************************//** |
204 | Free a table memory object. */ |
205 | void |
206 | dict_mem_table_free( |
207 | /*================*/ |
208 | dict_table_t* table) /*!< in: table */ |
209 | { |
210 | ut_ad(table); |
211 | ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
212 | ut_d(table->cached = FALSE); |
213 | |
214 | if (dict_table_has_fts_index(table) |
215 | || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) |
216 | || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) { |
217 | if (table->fts) { |
218 | fts_optimize_remove_table(table); |
219 | |
220 | fts_free(table); |
221 | } |
222 | } |
223 | |
224 | dict_table_autoinc_destroy(table); |
225 | dict_mem_table_free_foreign_vcol_set(table); |
226 | dict_table_stats_latch_destroy(table); |
227 | |
228 | table->foreign_set.~dict_foreign_set(); |
229 | table->referenced_set.~dict_foreign_set(); |
230 | |
231 | ut_free(table->name.m_name); |
232 | table->name.m_name = NULL; |
233 | |
234 | /* Clean up virtual index info structures that are registered |
235 | with virtual columns */ |
236 | for (ulint i = 0; i < table->n_v_def; i++) { |
237 | dict_v_col_t* vcol |
238 | = dict_table_get_nth_v_col(table, i); |
239 | |
240 | UT_DELETE(vcol->v_indexes); |
241 | } |
242 | |
243 | if (table->s_cols != NULL) { |
244 | UT_DELETE(table->s_cols); |
245 | } |
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* |
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 |
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 | ulint i; |
312 | |
313 | ut_ad(table); |
314 | ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
315 | ut_ad(!heap == !name); |
316 | |
317 | ut_ad(!(prtype & DATA_VIRTUAL)); |
318 | |
319 | i = table->n_def++; |
320 | |
321 | table->n_t_def++; |
322 | |
323 | if (name) { |
324 | if (table->n_def == table->n_cols) { |
325 | heap = table->heap; |
326 | } |
327 | if (i && !table->col_names) { |
328 | /* All preceding column names are empty. */ |
329 | char* s = static_cast<char*>( |
330 | mem_heap_zalloc(heap, table->n_def)); |
331 | |
332 | table->col_names = s; |
333 | } |
334 | |
335 | table->col_names = dict_add_col_name(table->col_names, |
336 | i, name, heap); |
337 | } |
338 | |
339 | col = dict_table_get_nth_col(table, i); |
340 | |
341 | dict_mem_fill_column_struct(col, i, mtype, prtype, len); |
342 | |
343 | switch (prtype & DATA_VERSIONED) { |
344 | case DATA_VERS_START: |
345 | ut_ad(!table->vers_start); |
346 | table->vers_start = i; |
347 | break; |
348 | case DATA_VERS_END: |
349 | ut_ad(!table->vers_end); |
350 | table->vers_end = i; |
351 | } |
352 | } |
353 | |
354 | /** Adds a virtual column definition to a table. |
355 | @param[in,out] table table |
356 | @param[in,out] heap temporary memory heap, or NULL. It is |
357 | used to store name when we have not finished |
358 | adding all columns. When all columns are |
359 | added, the whole name will copy to memory from |
360 | table->heap |
361 | @param[in] name column name |
362 | @param[in] mtype main datatype |
363 | @param[in] prtype precise type |
364 | @param[in] len length |
365 | @param[in] pos position in a table |
366 | @param[in] num_base number of base columns |
367 | @return the virtual column definition */ |
368 | dict_v_col_t* |
369 | dict_mem_table_add_v_col( |
370 | dict_table_t* table, |
371 | mem_heap_t* heap, |
372 | const char* name, |
373 | ulint mtype, |
374 | ulint prtype, |
375 | ulint len, |
376 | ulint pos, |
377 | ulint num_base) |
378 | { |
379 | dict_v_col_t* v_col; |
380 | ulint i; |
381 | |
382 | ut_ad(table); |
383 | ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
384 | ut_ad(!heap == !name); |
385 | |
386 | ut_ad(prtype & DATA_VIRTUAL); |
387 | |
388 | i = table->n_v_def++; |
389 | |
390 | table->n_t_def++; |
391 | |
392 | if (name != NULL) { |
393 | if (table->n_v_def == table->n_v_cols) { |
394 | heap = table->heap; |
395 | } |
396 | |
397 | if (i && !table->v_col_names) { |
398 | /* All preceding column names are empty. */ |
399 | char* s = static_cast<char*>( |
400 | mem_heap_zalloc(heap, table->n_v_def)); |
401 | |
402 | table->v_col_names = s; |
403 | } |
404 | |
405 | table->v_col_names = dict_add_col_name(table->v_col_names, |
406 | i, name, heap); |
407 | } |
408 | |
409 | v_col = &table->v_cols[i]; |
410 | |
411 | dict_mem_fill_column_struct(&v_col->m_col, pos, mtype, prtype, len); |
412 | v_col->v_pos = i; |
413 | |
414 | if (num_base != 0) { |
415 | v_col->base_col = static_cast<dict_col_t**>(mem_heap_zalloc( |
416 | table->heap, num_base * sizeof( |
417 | *v_col->base_col))); |
418 | } else { |
419 | v_col->base_col = NULL; |
420 | } |
421 | |
422 | v_col->num_base = num_base; |
423 | |
424 | /* Initialize the index list for virtual columns */ |
425 | v_col->v_indexes = UT_NEW_NOKEY(dict_v_idx_list()); |
426 | |
427 | return(v_col); |
428 | } |
429 | |
430 | /** Adds a stored column definition to a table. |
431 | @param[in] table table |
432 | @param[in] num_base number of base columns. */ |
433 | void |
434 | dict_mem_table_add_s_col( |
435 | dict_table_t* table, |
436 | ulint num_base) |
437 | { |
438 | unsigned i = unsigned(table->n_def) - 1; |
439 | dict_col_t* col = dict_table_get_nth_col(table, i); |
440 | dict_s_col_t s_col; |
441 | |
442 | ut_ad(col != NULL); |
443 | |
444 | if (table->s_cols == NULL) { |
445 | table->s_cols = UT_NEW_NOKEY(dict_s_col_list()); |
446 | } |
447 | |
448 | s_col.m_col = col; |
449 | s_col.s_pos = i + table->n_v_def; |
450 | |
451 | if (num_base != 0) { |
452 | s_col.base_col = static_cast<dict_col_t**>(mem_heap_zalloc( |
453 | table->heap, num_base * sizeof(dict_col_t*))); |
454 | } else { |
455 | s_col.base_col = NULL; |
456 | } |
457 | |
458 | s_col.num_base = num_base; |
459 | table->s_cols->push_back(s_col); |
460 | } |
461 | |
462 | /**********************************************************************//** |
463 | Renames a column of a table in the data dictionary cache. */ |
464 | static MY_ATTRIBUTE((nonnull)) |
465 | void |
466 | dict_mem_table_col_rename_low( |
467 | /*==========================*/ |
468 | dict_table_t* table, /*!< in/out: table */ |
469 | unsigned i, /*!< in: column offset corresponding to s */ |
470 | const char* to, /*!< in: new column name */ |
471 | const char* s, /*!< in: pointer to table->col_names */ |
472 | bool is_virtual) |
473 | /*!< in: if this is a virtual column */ |
474 | { |
475 | char* t_col_names = const_cast<char*>( |
476 | is_virtual ? table->v_col_names : table->col_names); |
477 | ulint n_col = is_virtual ? table->n_v_def : table->n_def; |
478 | |
479 | size_t from_len = strlen(s), to_len = strlen(to); |
480 | |
481 | ut_ad(i < table->n_def || is_virtual); |
482 | ut_ad(i < table->n_v_def || !is_virtual); |
483 | |
484 | ut_ad(from_len <= NAME_LEN); |
485 | ut_ad(to_len <= NAME_LEN); |
486 | |
487 | char from[NAME_LEN + 1]; |
488 | strncpy(from, s, NAME_LEN + 1); |
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 | /* if is_virtual and that in field->col does |
535 | not match, continue */ |
536 | if ((!is_virtual) != |
537 | (!field->col->is_virtual())) { |
538 | continue; |
539 | } |
540 | |
541 | ulint name_ofs |
542 | = ulint(field->name - t_col_names); |
543 | if (name_ofs <= prefix_len) { |
544 | field->name = col_names + name_ofs; |
545 | } else { |
546 | ut_a(name_ofs < full_len); |
547 | field->name = col_names |
548 | + name_ofs + to_len - from_len; |
549 | } |
550 | } |
551 | } |
552 | |
553 | if (is_virtual) { |
554 | table->v_col_names = col_names; |
555 | } else { |
556 | table->col_names = col_names; |
557 | } |
558 | } |
559 | |
560 | /* Virtual columns are not allowed for foreign key */ |
561 | if (is_virtual) { |
562 | return; |
563 | } |
564 | |
565 | dict_foreign_t* foreign; |
566 | |
567 | /* Replace the field names in every foreign key constraint. */ |
568 | for (dict_foreign_set::iterator it = table->foreign_set.begin(); |
569 | it != table->foreign_set.end(); |
570 | ++it) { |
571 | |
572 | foreign = *it; |
573 | |
574 | if (foreign->foreign_index == NULL) { |
575 | /* We may go here when we set foreign_key_checks to 0, |
576 | and then try to rename a column and modify the |
577 | corresponding foreign key constraint. The index |
578 | would have been dropped, we have to find an equivalent |
579 | one */ |
580 | for (unsigned f = 0; f < foreign->n_fields; f++) { |
581 | if (strcmp(foreign->foreign_col_names[f], from) |
582 | == 0) { |
583 | |
584 | char** rc = const_cast<char**>( |
585 | foreign->foreign_col_names |
586 | + f); |
587 | |
588 | if (to_len <= strlen(*rc)) { |
589 | memcpy(*rc, to, to_len + 1); |
590 | } else { |
591 | *rc = static_cast<char*>( |
592 | mem_heap_dup( |
593 | foreign->heap, |
594 | to, |
595 | to_len + 1)); |
596 | } |
597 | } |
598 | } |
599 | |
600 | dict_index_t* new_index = dict_foreign_find_index( |
601 | foreign->foreign_table, NULL, |
602 | foreign->foreign_col_names, |
603 | foreign->n_fields, NULL, true, false, |
604 | NULL, NULL, NULL); |
605 | /* There must be an equivalent index in this case. */ |
606 | ut_ad(new_index != NULL); |
607 | |
608 | foreign->foreign_index = new_index; |
609 | |
610 | } else { |
611 | |
612 | for (unsigned f = 0; f < foreign->n_fields; f++) { |
613 | /* These can point straight to |
614 | table->col_names, because the foreign key |
615 | constraints will be freed at the same time |
616 | when the table object is freed. */ |
617 | foreign->foreign_col_names[f] |
618 | = dict_index_get_nth_field( |
619 | foreign->foreign_index, |
620 | f)->name; |
621 | } |
622 | } |
623 | } |
624 | |
625 | for (dict_foreign_set::iterator it = table->referenced_set.begin(); |
626 | it != table->referenced_set.end(); |
627 | ++it) { |
628 | |
629 | foreign = *it; |
630 | |
631 | ut_ad(foreign->referenced_index != NULL); |
632 | |
633 | for (unsigned f = 0; f < foreign->n_fields; f++) { |
634 | /* foreign->referenced_col_names[] need to be |
635 | copies, because the constraint may become |
636 | orphan when foreign_key_checks=0 and the |
637 | parent table is dropped. */ |
638 | |
639 | const char* col_name = dict_index_get_nth_field( |
640 | foreign->referenced_index, f)->name; |
641 | |
642 | if (strcmp(foreign->referenced_col_names[f], |
643 | col_name)) { |
644 | char** rc = const_cast<char**>( |
645 | foreign->referenced_col_names + f); |
646 | size_t col_name_len_1 = strlen(col_name) + 1; |
647 | |
648 | if (col_name_len_1 <= strlen(*rc) + 1) { |
649 | memcpy(*rc, col_name, col_name_len_1); |
650 | } else { |
651 | *rc = static_cast<char*>( |
652 | mem_heap_dup( |
653 | foreign->heap, |
654 | col_name, |
655 | col_name_len_1)); |
656 | } |
657 | } |
658 | } |
659 | } |
660 | } |
661 | |
662 | /**********************************************************************//** |
663 | Renames a column of a table in the data dictionary cache. */ |
664 | void |
665 | dict_mem_table_col_rename( |
666 | /*======================*/ |
667 | dict_table_t* table, /*!< in/out: table */ |
668 | ulint nth_col,/*!< in: column index */ |
669 | const char* from, /*!< in: old column name */ |
670 | const char* to, /*!< in: new column name */ |
671 | bool is_virtual) |
672 | /*!< in: if this is a virtual column */ |
673 | { |
674 | const char* s = is_virtual ? table->v_col_names : table->col_names; |
675 | |
676 | ut_ad((!is_virtual && nth_col < table->n_def) |
677 | || (is_virtual && nth_col < table->n_v_def)); |
678 | |
679 | for (ulint i = 0; i < nth_col; i++) { |
680 | size_t len = strlen(s); |
681 | ut_ad(len > 0); |
682 | s += len + 1; |
683 | } |
684 | |
685 | /* This could fail if the data dictionaries are out of sync. |
686 | Proceed with the renaming anyway. */ |
687 | ut_ad(!strcmp(from, s)); |
688 | |
689 | dict_mem_table_col_rename_low(table, static_cast<unsigned>(nth_col), |
690 | to, s, is_virtual); |
691 | } |
692 | |
693 | /**********************************************************************//** |
694 | This function populates a dict_col_t memory structure with |
695 | supplied information. */ |
696 | void |
697 | dict_mem_fill_column_struct( |
698 | /*========================*/ |
699 | dict_col_t* column, /*!< out: column struct to be |
700 | filled */ |
701 | ulint col_pos, /*!< in: column position */ |
702 | ulint mtype, /*!< in: main data type */ |
703 | ulint prtype, /*!< in: precise type */ |
704 | ulint col_len) /*!< in: column length */ |
705 | { |
706 | ulint mbminlen; |
707 | ulint mbmaxlen; |
708 | |
709 | column->ind = (unsigned int) col_pos; |
710 | column->ord_part = 0; |
711 | column->max_prefix = 0; |
712 | column->mtype = (unsigned int) mtype; |
713 | column->prtype = (unsigned int) prtype; |
714 | column->len = (unsigned int) col_len; |
715 | dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); |
716 | column->mbminlen = mbminlen; |
717 | column->mbmaxlen = mbmaxlen; |
718 | column->def_val.data = NULL; |
719 | column->def_val.len = UNIV_SQL_DEFAULT; |
720 | } |
721 | |
722 | /**********************************************************************//** |
723 | Creates an index memory object. |
724 | @return own: index object */ |
725 | dict_index_t* |
726 | dict_mem_index_create( |
727 | /*==================*/ |
728 | dict_table_t* table, /*!< in: table */ |
729 | const char* index_name, /*!< in: index name */ |
730 | ulint type, /*!< in: DICT_UNIQUE, |
731 | DICT_CLUSTERED, ... ORed */ |
732 | ulint n_fields) /*!< in: number of fields */ |
733 | { |
734 | dict_index_t* index; |
735 | mem_heap_t* heap; |
736 | |
737 | ut_ad(!table || table->magic_n == DICT_TABLE_MAGIC_N); |
738 | ut_ad(index_name); |
739 | |
740 | heap = mem_heap_create(DICT_HEAP_SIZE); |
741 | |
742 | index = static_cast<dict_index_t*>( |
743 | mem_heap_zalloc(heap, sizeof(*index))); |
744 | index->table = table; |
745 | |
746 | dict_mem_fill_index_struct(index, heap, index_name, type, n_fields); |
747 | |
748 | dict_index_zip_pad_mutex_create_lazy(index); |
749 | |
750 | if (type & DICT_SPATIAL) { |
751 | mutex_create(LATCH_ID_RTR_SSN_MUTEX, &index->rtr_ssn.mutex); |
752 | index->rtr_track = static_cast<rtr_info_track_t*>( |
753 | mem_heap_alloc( |
754 | heap, |
755 | sizeof(*index->rtr_track))); |
756 | mutex_create(LATCH_ID_RTR_ACTIVE_MUTEX, |
757 | &index->rtr_track->rtr_active_mutex); |
758 | index->rtr_track->rtr_active = UT_NEW_NOKEY(rtr_info_active()); |
759 | } |
760 | |
761 | return(index); |
762 | } |
763 | |
764 | /**********************************************************************//** |
765 | Creates and initializes a foreign constraint memory object. |
766 | @return own: foreign constraint struct */ |
767 | dict_foreign_t* |
768 | dict_mem_foreign_create(void) |
769 | /*=========================*/ |
770 | { |
771 | dict_foreign_t* foreign; |
772 | mem_heap_t* heap; |
773 | DBUG_ENTER("dict_mem_foreign_create" ); |
774 | |
775 | heap = mem_heap_create(100); |
776 | |
777 | foreign = static_cast<dict_foreign_t*>( |
778 | mem_heap_zalloc(heap, sizeof(dict_foreign_t))); |
779 | |
780 | foreign->heap = heap; |
781 | |
782 | foreign->v_cols = NULL; |
783 | |
784 | DBUG_PRINT("dict_mem_foreign_create" , ("heap: %p" , heap)); |
785 | |
786 | DBUG_RETURN(foreign); |
787 | } |
788 | |
789 | /**********************************************************************//** |
790 | Sets the foreign_table_name_lookup pointer based on the value of |
791 | lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup |
792 | will point to foreign_table_name. If 2, then another string is |
793 | allocated from foreign->heap and set to lower case. */ |
794 | void |
795 | dict_mem_foreign_table_name_lookup_set( |
796 | /*===================================*/ |
797 | dict_foreign_t* foreign, /*!< in/out: foreign struct */ |
798 | ibool do_alloc) /*!< in: is an alloc needed */ |
799 | { |
800 | if (innobase_get_lower_case_table_names() == 2) { |
801 | if (do_alloc) { |
802 | ulint len; |
803 | |
804 | len = strlen(foreign->foreign_table_name) + 1; |
805 | |
806 | foreign->foreign_table_name_lookup = |
807 | static_cast<char*>( |
808 | mem_heap_alloc(foreign->heap, len)); |
809 | } |
810 | strcpy(foreign->foreign_table_name_lookup, |
811 | foreign->foreign_table_name); |
812 | innobase_casedn_str(foreign->foreign_table_name_lookup); |
813 | } else { |
814 | foreign->foreign_table_name_lookup |
815 | = foreign->foreign_table_name; |
816 | } |
817 | } |
818 | |
819 | /**********************************************************************//** |
820 | Sets the referenced_table_name_lookup pointer based on the value of |
821 | lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup |
822 | will point to referenced_table_name. If 2, then another string is |
823 | allocated from foreign->heap and set to lower case. */ |
824 | void |
825 | dict_mem_referenced_table_name_lookup_set( |
826 | /*======================================*/ |
827 | dict_foreign_t* foreign, /*!< in/out: foreign struct */ |
828 | ibool do_alloc) /*!< in: is an alloc needed */ |
829 | { |
830 | if (innobase_get_lower_case_table_names() == 2) { |
831 | if (do_alloc) { |
832 | ulint len; |
833 | |
834 | len = strlen(foreign->referenced_table_name) + 1; |
835 | |
836 | foreign->referenced_table_name_lookup = |
837 | static_cast<char*>( |
838 | mem_heap_alloc(foreign->heap, len)); |
839 | } |
840 | strcpy(foreign->referenced_table_name_lookup, |
841 | foreign->referenced_table_name); |
842 | innobase_casedn_str(foreign->referenced_table_name_lookup); |
843 | } else { |
844 | foreign->referenced_table_name_lookup |
845 | = foreign->referenced_table_name; |
846 | } |
847 | } |
848 | |
849 | /** Fill the virtual column set with virtual column information |
850 | present in the given virtual index. |
851 | @param[in] index virtual index |
852 | @param[out] v_cols virtual column set. */ |
853 | static |
854 | void |
855 | dict_mem_fill_vcol_has_index( |
856 | const dict_index_t* index, |
857 | dict_vcol_set** v_cols) |
858 | { |
859 | for (ulint i = 0; i < index->table->n_v_cols; i++) { |
860 | dict_v_col_t* v_col = dict_table_get_nth_v_col( |
861 | index->table, i); |
862 | if (!v_col->m_col.ord_part) { |
863 | continue; |
864 | } |
865 | |
866 | dict_v_idx_list::iterator it; |
867 | for (it = v_col->v_indexes->begin(); |
868 | it != v_col->v_indexes->end(); ++it) { |
869 | dict_v_idx_t v_idx = *it; |
870 | |
871 | if (v_idx.index != index) { |
872 | continue; |
873 | } |
874 | |
875 | if (*v_cols == NULL) { |
876 | *v_cols = UT_NEW_NOKEY(dict_vcol_set()); |
877 | } |
878 | |
879 | (*v_cols)->insert(v_col); |
880 | } |
881 | } |
882 | } |
883 | |
884 | /** Fill the virtual column set with the virtual column of the index |
885 | if the index contains given column name. |
886 | @param[in] col_name column name |
887 | @param[in] table innodb table object |
888 | @param[out] v_cols set of virtual column information. */ |
889 | static |
890 | void |
891 | dict_mem_fill_vcol_from_v_indexes( |
892 | const char* col_name, |
893 | const dict_table_t* table, |
894 | dict_vcol_set** v_cols) |
895 | { |
896 | /* virtual column can't be Primary Key, so start with |
897 | secondary index */ |
898 | for (dict_index_t* index = dict_table_get_next_index( |
899 | dict_table_get_first_index(table)); |
900 | index; |
901 | index = dict_table_get_next_index(index)) { |
902 | |
903 | /* Skip if the index have newly added |
904 | virtual column because field name is NULL. |
905 | Later virtual column set will be |
906 | refreshed during loading of table. */ |
907 | if (!dict_index_has_virtual(index) |
908 | || index->has_new_v_col) { |
909 | continue; |
910 | } |
911 | |
912 | for (ulint i = 0; i < index->n_fields; i++) { |
913 | dict_field_t* field = |
914 | dict_index_get_nth_field(index, i); |
915 | |
916 | if (strcmp(field->name, col_name) == 0) { |
917 | dict_mem_fill_vcol_has_index( |
918 | index, v_cols); |
919 | } |
920 | } |
921 | } |
922 | } |
923 | |
924 | /** Fill the virtual column set with virtual columns which have base columns |
925 | as the given col_name |
926 | @param[in] col_name column name |
927 | @param[in] table table object |
928 | @param[out] v_cols set of virtual columns. */ |
929 | static |
930 | void |
931 | dict_mem_fill_vcol_set_for_base_col( |
932 | const char* col_name, |
933 | const dict_table_t* table, |
934 | dict_vcol_set** v_cols) |
935 | { |
936 | for (ulint i = 0; i < table->n_v_cols; i++) { |
937 | dict_v_col_t* v_col = dict_table_get_nth_v_col(table, i); |
938 | |
939 | if (!v_col->m_col.ord_part) { |
940 | continue; |
941 | } |
942 | |
943 | for (ulint j = 0; j < v_col->num_base; j++) { |
944 | if (strcmp(col_name, dict_table_get_col_name( |
945 | table, |
946 | v_col->base_col[j]->ind)) == 0) { |
947 | |
948 | if (*v_cols == NULL) { |
949 | *v_cols = UT_NEW_NOKEY(dict_vcol_set()); |
950 | } |
951 | |
952 | (*v_cols)->insert(v_col); |
953 | } |
954 | } |
955 | } |
956 | } |
957 | |
958 | /** Fills the dependent virtual columns in a set. |
959 | Reason for being dependent are |
960 | 1) FK can be present on base column of virtual columns |
961 | 2) FK can be present on column which is a part of virtual index |
962 | @param[in,out] foreign foreign key information. */ |
963 | void |
964 | dict_mem_foreign_fill_vcol_set( |
965 | dict_foreign_t* foreign) |
966 | { |
967 | ulint type = foreign->type; |
968 | |
969 | if (type == 0) { |
970 | return; |
971 | } |
972 | |
973 | for (ulint i = 0; i < foreign->n_fields; i++) { |
974 | /** FK can be present on base columns |
975 | of virtual columns. */ |
976 | dict_mem_fill_vcol_set_for_base_col( |
977 | foreign->foreign_col_names[i], |
978 | foreign->foreign_table, |
979 | &foreign->v_cols); |
980 | |
981 | /** FK can be present on the columns |
982 | which can be a part of virtual index. */ |
983 | dict_mem_fill_vcol_from_v_indexes( |
984 | foreign->foreign_col_names[i], |
985 | foreign->foreign_table, |
986 | &foreign->v_cols); |
987 | } |
988 | } |
989 | |
990 | /** Fill virtual columns set in each fk constraint present in the table. |
991 | @param[in,out] table innodb table object. */ |
992 | void |
993 | dict_mem_table_fill_foreign_vcol_set( |
994 | dict_table_t* table) |
995 | { |
996 | dict_foreign_set fk_set = table->foreign_set; |
997 | dict_foreign_t* foreign; |
998 | |
999 | dict_foreign_set::iterator it; |
1000 | for (it = fk_set.begin(); it != fk_set.end(); ++it) { |
1001 | foreign = *it; |
1002 | |
1003 | dict_mem_foreign_fill_vcol_set(foreign); |
1004 | } |
1005 | } |
1006 | |
1007 | /** Free the vcol_set from all foreign key constraint on the table. |
1008 | @param[in,out] table innodb table object. */ |
1009 | void |
1010 | dict_mem_table_free_foreign_vcol_set( |
1011 | dict_table_t* table) |
1012 | { |
1013 | dict_foreign_set fk_set = table->foreign_set; |
1014 | dict_foreign_t* foreign; |
1015 | |
1016 | dict_foreign_set::iterator it; |
1017 | for (it = fk_set.begin(); it != fk_set.end(); ++it) { |
1018 | |
1019 | foreign = *it; |
1020 | |
1021 | if (foreign->v_cols != NULL) { |
1022 | UT_DELETE(foreign->v_cols); |
1023 | foreign->v_cols = NULL; |
1024 | } |
1025 | } |
1026 | } |
1027 | |
1028 | /**********************************************************************//** |
1029 | Adds a field definition to an index. NOTE: does not take a copy |
1030 | of the column name if the field is a column. The memory occupied |
1031 | by the column name may be released only after publishing the index. */ |
1032 | void |
1033 | dict_mem_index_add_field( |
1034 | /*=====================*/ |
1035 | dict_index_t* index, /*!< in: index */ |
1036 | const char* name, /*!< in: column name */ |
1037 | ulint prefix_len) /*!< in: 0 or the column prefix length |
1038 | in a MySQL index like |
1039 | INDEX (textcol(25)) */ |
1040 | { |
1041 | dict_field_t* field; |
1042 | |
1043 | ut_ad(index); |
1044 | ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
1045 | |
1046 | index->n_def++; |
1047 | |
1048 | field = dict_index_get_nth_field(index, unsigned(index->n_def) - 1); |
1049 | |
1050 | field->name = name; |
1051 | field->prefix_len = (unsigned int) prefix_len; |
1052 | } |
1053 | |
1054 | /**********************************************************************//** |
1055 | Frees an index memory object. */ |
1056 | void |
1057 | dict_mem_index_free( |
1058 | /*================*/ |
1059 | dict_index_t* index) /*!< in: index */ |
1060 | { |
1061 | ut_ad(index); |
1062 | ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
1063 | |
1064 | dict_index_zip_pad_mutex_destroy(index); |
1065 | |
1066 | if (dict_index_is_spatial(index)) { |
1067 | rtr_info_active::iterator it; |
1068 | rtr_info_t* rtr_info; |
1069 | |
1070 | for (it = index->rtr_track->rtr_active->begin(); |
1071 | it != index->rtr_track->rtr_active->end(); ++it) { |
1072 | rtr_info = *it; |
1073 | |
1074 | rtr_info->index = NULL; |
1075 | } |
1076 | |
1077 | mutex_destroy(&index->rtr_ssn.mutex); |
1078 | mutex_destroy(&index->rtr_track->rtr_active_mutex); |
1079 | UT_DELETE(index->rtr_track->rtr_active); |
1080 | } |
1081 | |
1082 | dict_index_remove_from_v_col_list(index); |
1083 | mem_heap_free(index->heap); |
1084 | } |
1085 | |
1086 | /** Create a temporary tablename like "#sql-ibtid-inc where |
1087 | tid = the Table ID |
1088 | inc = a randomly initialized number that is incremented for each file |
1089 | The table ID is a 64 bit integer, can use up to 20 digits, and is |
1090 | initialized at bootstrap. The second number is 32 bits, can use up to 10 |
1091 | digits, and is initialized at startup to a randomly distributed number. |
1092 | It is hoped that the combination of these two numbers will provide a |
1093 | reasonably unique temporary file name. |
1094 | @param[in] heap A memory heap |
1095 | @param[in] dbtab Table name in the form database/table name |
1096 | @param[in] id Table id |
1097 | @return A unique temporary tablename suitable for InnoDB use */ |
1098 | char* |
1099 | dict_mem_create_temporary_tablename( |
1100 | mem_heap_t* heap, |
1101 | const char* dbtab, |
1102 | table_id_t id) |
1103 | { |
1104 | size_t size; |
1105 | char* name; |
1106 | const char* dbend = strchr(dbtab, '/'); |
1107 | ut_ad(dbend); |
1108 | size_t dblen = size_t(dbend - dbtab) + 1; |
1109 | |
1110 | /* Increment a randomly initialized number for each temp file. */ |
1111 | my_atomic_add32((int32*) &dict_temp_file_num, 1); |
1112 | |
1113 | size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20 + 1 + 10); |
1114 | name = static_cast<char*>(mem_heap_alloc(heap, size)); |
1115 | memcpy(name, dbtab, dblen); |
1116 | snprintf(name + dblen, size - dblen, |
1117 | TEMP_FILE_PREFIX_INNODB UINT64PF "-" UINT32PF, |
1118 | id, dict_temp_file_num); |
1119 | |
1120 | return(name); |
1121 | } |
1122 | |
1123 | /** Initialize dict memory variables */ |
1124 | void |
1125 | dict_mem_init(void) |
1126 | { |
1127 | /* Initialize a randomly distributed temporary file number */ |
1128 | ib_uint32_t now = static_cast<ib_uint32_t>(ut_time()); |
1129 | |
1130 | const byte* buf = reinterpret_cast<const byte*>(&now); |
1131 | |
1132 | dict_temp_file_num = ut_crc32(buf, sizeof(now)); |
1133 | |
1134 | DBUG_PRINT("dict_mem_init" , |
1135 | ("Starting Temporary file number is " UINT32PF, |
1136 | dict_temp_file_num)); |
1137 | } |
1138 | |
1139 | /** Validate the search order in the foreign key set. |
1140 | @param[in] fk_set the foreign key set to be validated |
1141 | @return true if search order is fine in the set, false otherwise. */ |
1142 | bool |
1143 | dict_foreign_set_validate( |
1144 | const dict_foreign_set& fk_set) |
1145 | { |
1146 | dict_foreign_not_exists not_exists(fk_set); |
1147 | |
1148 | dict_foreign_set::const_iterator it = std::find_if( |
1149 | fk_set.begin(), fk_set.end(), not_exists); |
1150 | |
1151 | if (it == fk_set.end()) { |
1152 | return(true); |
1153 | } |
1154 | |
1155 | dict_foreign_t* foreign = *it; |
1156 | std::cerr << "Foreign key lookup failed: " << *foreign; |
1157 | std::cerr << fk_set; |
1158 | ut_ad(0); |
1159 | return(false); |
1160 | } |
1161 | |
1162 | /** Validate the search order in the foreign key sets of the table |
1163 | (foreign_set and referenced_set). |
1164 | @param[in] table table whose foreign key sets are to be validated |
1165 | @return true if foreign key sets are fine, false otherwise. */ |
1166 | bool |
1167 | dict_foreign_set_validate( |
1168 | const dict_table_t& table) |
1169 | { |
1170 | return(dict_foreign_set_validate(table.foreign_set) |
1171 | && dict_foreign_set_validate(table.referenced_set)); |
1172 | } |
1173 | |
1174 | std::ostream& |
1175 | operator<< (std::ostream& out, const dict_foreign_t& foreign) |
1176 | { |
1177 | out << "[dict_foreign_t: id='" << foreign.id << "'" ; |
1178 | |
1179 | if (foreign.foreign_table_name != NULL) { |
1180 | out << ",for: '" << foreign.foreign_table_name << "'" ; |
1181 | } |
1182 | |
1183 | out << "]" ; |
1184 | return(out); |
1185 | } |
1186 | |
1187 | std::ostream& |
1188 | operator<< (std::ostream& out, const dict_foreign_set& fk_set) |
1189 | { |
1190 | out << "[dict_foreign_set:" ; |
1191 | std::for_each(fk_set.begin(), fk_set.end(), dict_foreign_print(out)); |
1192 | out << "]" << std::endl; |
1193 | return(out); |
1194 | } |
1195 | |
1196 | /** Adjust clustered index metadata for instant ADD COLUMN. |
1197 | @param[in] clustered index definition after instant ADD COLUMN */ |
1198 | inline void dict_index_t::instant_add_field(const dict_index_t& instant) |
1199 | { |
1200 | DBUG_ASSERT(is_primary()); |
1201 | DBUG_ASSERT(instant.is_primary()); |
1202 | DBUG_ASSERT(!instant.is_instant()); |
1203 | DBUG_ASSERT(n_def == n_fields); |
1204 | DBUG_ASSERT(instant.n_def == instant.n_fields); |
1205 | |
1206 | DBUG_ASSERT(type == instant.type); |
1207 | DBUG_ASSERT(trx_id_offset == instant.trx_id_offset); |
1208 | DBUG_ASSERT(n_user_defined_cols == instant.n_user_defined_cols); |
1209 | DBUG_ASSERT(n_uniq == instant.n_uniq); |
1210 | DBUG_ASSERT(instant.n_fields > n_fields); |
1211 | DBUG_ASSERT(instant.n_def > n_def); |
1212 | DBUG_ASSERT(instant.n_nullable >= n_nullable); |
1213 | DBUG_ASSERT(instant.n_core_fields >= n_core_fields); |
1214 | DBUG_ASSERT(instant.n_core_null_bytes >= n_core_null_bytes); |
1215 | |
1216 | n_fields = instant.n_fields; |
1217 | n_def = instant.n_def; |
1218 | n_nullable = instant.n_nullable; |
1219 | fields = static_cast<dict_field_t*>( |
1220 | mem_heap_dup(heap, instant.fields, n_fields * sizeof *fields)); |
1221 | |
1222 | ut_d(unsigned n_null = 0); |
1223 | |
1224 | for (unsigned i = 0; i < n_fields; i++) { |
1225 | DBUG_ASSERT(fields[i].same(instant.fields[i])); |
1226 | const dict_col_t* icol = instant.fields[i].col; |
1227 | DBUG_ASSERT(!icol->is_virtual()); |
1228 | dict_col_t* col = fields[i].col = &table->cols[ |
1229 | icol - instant.table->cols]; |
1230 | fields[i].name = col->name(*table); |
1231 | ut_d(n_null += col->is_nullable()); |
1232 | } |
1233 | |
1234 | ut_ad(n_null == n_nullable); |
1235 | } |
1236 | |
1237 | /** Adjust metadata for instant ADD COLUMN. |
1238 | @param[in] table table definition after instant ADD COLUMN */ |
1239 | void dict_table_t::instant_add_column(const dict_table_t& table) |
1240 | { |
1241 | DBUG_ASSERT(!table.cached); |
1242 | DBUG_ASSERT(table.n_def == table.n_cols); |
1243 | DBUG_ASSERT(table.n_t_def == table.n_t_cols); |
1244 | DBUG_ASSERT(n_def == n_cols); |
1245 | DBUG_ASSERT(n_t_def == n_t_cols); |
1246 | DBUG_ASSERT(table.n_cols > n_cols); |
1247 | ut_ad(mutex_own(&dict_sys->mutex)); |
1248 | |
1249 | const char* end = table.col_names; |
1250 | for (unsigned i = table.n_cols; i--; ) end += strlen(end) + 1; |
1251 | |
1252 | col_names = static_cast<char*>( |
1253 | mem_heap_dup(heap, table.col_names, |
1254 | ulint(end - table.col_names))); |
1255 | const dict_col_t* const old_cols = cols; |
1256 | const dict_col_t* const old_cols_end = cols + n_cols; |
1257 | cols = static_cast<dict_col_t*>(mem_heap_dup(heap, table.cols, |
1258 | table.n_cols |
1259 | * sizeof *cols)); |
1260 | |
1261 | /* Preserve the default values of previously instantly |
1262 | added columns. */ |
1263 | for (unsigned i = unsigned(n_cols) - DATA_N_SYS_COLS; i--; ) { |
1264 | cols[i].def_val = old_cols[i].def_val; |
1265 | } |
1266 | |
1267 | /* Copy the new default values to this->heap. */ |
1268 | for (unsigned i = n_cols; i < table.n_cols; i++) { |
1269 | dict_col_t& c = cols[i - DATA_N_SYS_COLS]; |
1270 | DBUG_ASSERT(c.is_instant()); |
1271 | if (c.def_val.len == 0) { |
1272 | c.def_val.data = field_ref_zero; |
1273 | } else if (const void*& d = c.def_val.data) { |
1274 | d = mem_heap_dup(heap, d, c.def_val.len); |
1275 | } else { |
1276 | DBUG_ASSERT(c.def_val.len == UNIV_SQL_NULL); |
1277 | } |
1278 | } |
1279 | |
1280 | const unsigned old_n_cols = n_cols; |
1281 | const unsigned n_add = unsigned(table.n_cols - n_cols); |
1282 | |
1283 | n_t_def += n_add; |
1284 | n_t_cols += n_add; |
1285 | n_cols = table.n_cols; |
1286 | n_def = n_cols; |
1287 | |
1288 | for (unsigned i = n_v_def; i--; ) { |
1289 | const dict_v_col_t& v = v_cols[i]; |
1290 | for (ulint n = v.num_base; n--; ) { |
1291 | dict_col_t*& base = v.base_col[n]; |
1292 | if (!base->is_virtual()) { |
1293 | DBUG_ASSERT(base >= old_cols); |
1294 | size_t n = size_t(base - old_cols); |
1295 | DBUG_ASSERT(n + DATA_N_SYS_COLS < old_n_cols); |
1296 | base = &cols[n]; |
1297 | } |
1298 | } |
1299 | } |
1300 | |
1301 | dict_index_t* index = dict_table_get_first_index(this); |
1302 | |
1303 | index->instant_add_field(*dict_table_get_first_index(&table)); |
1304 | |
1305 | while ((index = dict_table_get_next_index(index)) != NULL) { |
1306 | for (unsigned i = 0; i < index->n_fields; i++) { |
1307 | dict_field_t& field = index->fields[i]; |
1308 | if (field.col < old_cols |
1309 | || field.col >= old_cols_end) { |
1310 | DBUG_ASSERT(field.col->is_virtual()); |
1311 | } else { |
1312 | /* Secondary indexes may contain user |
1313 | columns and DB_ROW_ID (if there is |
1314 | GEN_CLUST_INDEX instead of PRIMARY KEY), |
1315 | but not DB_TRX_ID,DB_ROLL_PTR. */ |
1316 | DBUG_ASSERT(field.col >= old_cols); |
1317 | size_t n = size_t(field.col - old_cols); |
1318 | DBUG_ASSERT(n + DATA_N_SYS_COLS <= old_n_cols); |
1319 | if (n + DATA_N_SYS_COLS >= old_n_cols) { |
1320 | /* Replace DB_ROW_ID */ |
1321 | n += n_add; |
1322 | } |
1323 | field.col = &cols[n]; |
1324 | DBUG_ASSERT(!field.col->is_virtual()); |
1325 | field.name = field.col->name(*this); |
1326 | } |
1327 | } |
1328 | } |
1329 | } |
1330 | |
1331 | /** Roll back instant_add_column(). |
1332 | @param[in] old_n_cols original n_cols |
1333 | @param[in] old_cols original cols |
1334 | @param[in] old_col_names original col_names */ |
1335 | void |
1336 | dict_table_t::rollback_instant( |
1337 | unsigned old_n_cols, |
1338 | dict_col_t* old_cols, |
1339 | const char* old_col_names) |
1340 | { |
1341 | ut_ad(mutex_own(&dict_sys->mutex)); |
1342 | dict_index_t* index = indexes.start; |
1343 | /* index->is_instant() does not necessarily hold here, because |
1344 | the table may have been emptied */ |
1345 | DBUG_ASSERT(old_n_cols >= DATA_N_SYS_COLS); |
1346 | DBUG_ASSERT(n_cols >= old_n_cols); |
1347 | DBUG_ASSERT(n_cols == n_def); |
1348 | DBUG_ASSERT(index->n_def == index->n_fields); |
1349 | |
1350 | const unsigned n_remove = n_cols - old_n_cols; |
1351 | |
1352 | for (unsigned i = index->n_fields - n_remove; i < index->n_fields; |
1353 | i++) { |
1354 | if (index->fields[i].col->is_nullable()) { |
1355 | index->n_nullable--; |
1356 | } |
1357 | } |
1358 | |
1359 | index->n_fields -= n_remove; |
1360 | index->n_def = index->n_fields; |
1361 | if (index->n_core_fields > index->n_fields) { |
1362 | index->n_core_fields = index->n_fields; |
1363 | index->n_core_null_bytes |
1364 | = UT_BITS_IN_BYTES(unsigned(index->n_nullable)); |
1365 | } |
1366 | |
1367 | const dict_col_t* const new_cols = cols; |
1368 | const dict_col_t* const new_cols_end = cols + n_cols; |
1369 | |
1370 | cols = old_cols; |
1371 | col_names = old_col_names; |
1372 | n_cols = old_n_cols; |
1373 | n_def = old_n_cols; |
1374 | n_t_def -= n_remove; |
1375 | n_t_cols -= n_remove; |
1376 | |
1377 | for (unsigned i = n_v_def; i--; ) { |
1378 | const dict_v_col_t& v = v_cols[i]; |
1379 | for (ulint n = v.num_base; n--; ) { |
1380 | dict_col_t*& base = v.base_col[n]; |
1381 | if (!base->is_virtual()) { |
1382 | base = &cols[base - new_cols]; |
1383 | } |
1384 | } |
1385 | } |
1386 | |
1387 | do { |
1388 | for (unsigned i = 0; i < index->n_fields; i++) { |
1389 | dict_field_t& field = index->fields[i]; |
1390 | if (field.col < new_cols |
1391 | || field.col >= new_cols_end) { |
1392 | DBUG_ASSERT(field.col->is_virtual()); |
1393 | } else { |
1394 | DBUG_ASSERT(field.col >= new_cols); |
1395 | size_t n = size_t(field.col - new_cols); |
1396 | DBUG_ASSERT(n <= n_cols); |
1397 | if (n + DATA_N_SYS_COLS >= n_cols) { |
1398 | n -= n_remove; |
1399 | } |
1400 | field.col = &cols[n]; |
1401 | DBUG_ASSERT(!field.col->is_virtual()); |
1402 | field.name = field.col->name(*this); |
1403 | } |
1404 | } |
1405 | } while ((index = dict_table_get_next_index(index)) != NULL); |
1406 | } |
1407 | |
1408 | /** Trim the instantly added columns when an insert into SYS_COLUMNS |
1409 | is rolled back during ALTER TABLE or recovery. |
1410 | @param[in] n number of surviving non-system columns */ |
1411 | void dict_table_t::rollback_instant(unsigned n) |
1412 | { |
1413 | ut_ad(mutex_own(&dict_sys->mutex)); |
1414 | dict_index_t* index = indexes.start; |
1415 | DBUG_ASSERT(index->is_instant()); |
1416 | DBUG_ASSERT(index->n_def == index->n_fields); |
1417 | DBUG_ASSERT(n_cols == n_def); |
1418 | DBUG_ASSERT(n >= index->n_uniq); |
1419 | DBUG_ASSERT(n_cols > n + DATA_N_SYS_COLS); |
1420 | const unsigned n_remove = n_cols - n - DATA_N_SYS_COLS; |
1421 | |
1422 | char* names = const_cast<char*>(dict_table_get_col_name(this, n)); |
1423 | const char* sys = names; |
1424 | for (unsigned i = n_remove; i--; ) { |
1425 | sys += strlen(sys) + 1; |
1426 | } |
1427 | static const char system[] = "DB_ROW_ID\0DB_TRX_ID\0DB_ROLL_PTR" ; |
1428 | DBUG_ASSERT(!memcmp(sys, system, sizeof system)); |
1429 | for (unsigned i = index->n_fields - n_remove; i < index->n_fields; |
1430 | i++) { |
1431 | if (index->fields[i].col->is_nullable()) { |
1432 | index->n_nullable--; |
1433 | } |
1434 | } |
1435 | index->n_fields -= n_remove; |
1436 | index->n_def = index->n_fields; |
1437 | memmove(names, sys, sizeof system); |
1438 | memmove(cols + n, cols + n_cols - DATA_N_SYS_COLS, |
1439 | DATA_N_SYS_COLS * sizeof *cols); |
1440 | n_cols -= n_remove; |
1441 | n_def = n_cols; |
1442 | n_t_cols -= n_remove; |
1443 | n_t_def -= n_remove; |
1444 | |
1445 | for (unsigned i = DATA_N_SYS_COLS; i--; ) { |
1446 | cols[n_cols - i].ind--; |
1447 | } |
1448 | |
1449 | if (dict_index_is_auto_gen_clust(index)) { |
1450 | DBUG_ASSERT(index->n_uniq == 1); |
1451 | dict_field_t* field = index->fields; |
1452 | field->name = sys; |
1453 | field->col = dict_table_get_sys_col(this, DATA_ROW_ID); |
1454 | field++; |
1455 | field->name = sys + sizeof "DB_ROW_ID" ; |
1456 | field->col = dict_table_get_sys_col(this, DATA_TRX_ID); |
1457 | field++; |
1458 | field->name = sys + sizeof "DB_ROW_ID\0DB_TRX_ID" ; |
1459 | field->col = dict_table_get_sys_col(this, DATA_ROLL_PTR); |
1460 | |
1461 | /* Replace the DB_ROW_ID column in secondary indexes. */ |
1462 | while ((index = dict_table_get_next_index(index)) != NULL) { |
1463 | field = &index->fields[index->n_fields - 1]; |
1464 | DBUG_ASSERT(field->col->mtype == DATA_SYS); |
1465 | DBUG_ASSERT(field->col->prtype |
1466 | == DATA_NOT_NULL + DATA_TRX_ID); |
1467 | field->col--; |
1468 | field->name = sys; |
1469 | } |
1470 | |
1471 | return; |
1472 | } |
1473 | |
1474 | dict_field_t* field = &index->fields[index->n_uniq]; |
1475 | field->name = sys + sizeof "DB_ROW_ID" ; |
1476 | field->col = dict_table_get_sys_col(this, DATA_TRX_ID); |
1477 | field++; |
1478 | field->name = sys + sizeof "DB_ROW_ID\0DB_TRX_ID" ; |
1479 | field->col = dict_table_get_sys_col(this, DATA_ROLL_PTR); |
1480 | } |
1481 | |
1482 | |
1483 | /** Check if record in clustered index is historical row. |
1484 | @param[in] rec clustered row |
1485 | @param[in] offsets offsets |
1486 | @return true if row is historical */ |
1487 | bool |
1488 | dict_index_t::vers_history_row( |
1489 | const rec_t* rec, |
1490 | const ulint* offsets) |
1491 | { |
1492 | ut_ad(is_primary()); |
1493 | |
1494 | ulint len; |
1495 | dict_col_t& col= table->cols[table->vers_end]; |
1496 | ut_ad(col.vers_sys_end()); |
1497 | ulint nfield = dict_col_get_clust_pos(&col, this); |
1498 | const byte *data = rec_get_nth_field(rec, offsets, nfield, &len); |
1499 | if (col.vers_native()) { |
1500 | ut_ad(len == sizeof trx_id_max_bytes); |
1501 | return 0 != memcmp(data, trx_id_max_bytes, len); |
1502 | } |
1503 | ut_ad(len == sizeof timestamp_max_bytes); |
1504 | return 0 != memcmp(data, timestamp_max_bytes, len); |
1505 | } |
1506 | |
1507 | /** Check if record in secondary index is historical row. |
1508 | @param[in] rec record in a secondary index |
1509 | @param[out] history_row true if row is historical |
1510 | @return true on error */ |
1511 | bool |
1512 | dict_index_t::vers_history_row( |
1513 | const rec_t* rec, |
1514 | bool &history_row) |
1515 | { |
1516 | ut_ad(!is_primary()); |
1517 | |
1518 | bool error = false; |
1519 | mem_heap_t* heap = NULL; |
1520 | dict_index_t* clust_index = NULL; |
1521 | ulint offsets_[REC_OFFS_NORMAL_SIZE]; |
1522 | ulint* offsets = offsets_; |
1523 | rec_offs_init(offsets_); |
1524 | |
1525 | mtr_t mtr; |
1526 | mtr.start(); |
1527 | |
1528 | rec_t* clust_rec = |
1529 | row_get_clust_rec(BTR_SEARCH_LEAF, rec, this, &clust_index, &mtr); |
1530 | if (clust_rec) { |
1531 | offsets = rec_get_offsets(clust_rec, clust_index, offsets, true, |
1532 | ULINT_UNDEFINED, &heap); |
1533 | |
1534 | history_row = clust_index->vers_history_row(clust_rec, offsets); |
1535 | } else { |
1536 | ib::error() << "foreign constraints: secondary index is out of " |
1537 | "sync" ; |
1538 | ut_ad(!"secondary index is out of sync" ); |
1539 | error = true; |
1540 | } |
1541 | mtr.commit(); |
1542 | if (heap) { |
1543 | mem_heap_free(heap); |
1544 | } |
1545 | return(error); |
1546 | } |
1547 | |