1 /*****************************************************************************
2
3 Copyright (c) 2017, 2020, Oracle and/or its affiliates. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25 *****************************************************************************/
26
27 /** @file dict/dict0dd.cc
28 Data dictionary interface */
29
30 #ifndef UNIV_HOTBACKUP
31 #include <auto_thd.h>
32 #include <current_thd.h>
33 #include <sql/thd_raii.h>
34 #include <sql_backup_lock.h>
35 #include <sql_class.h>
36 #include <sql_thd_internal_api.h>
37 #include "item.h"
38 #else /* !UNIV_HOTBACKUP */
39 #include <my_base.h>
40 #endif /* !UNIV_HOTBACKUP */
41
42 #include <dd/properties.h>
43 #include "dict0crea.h"
44 #include "dict0dd.h"
45 #include "dict0dict.h"
46 #include "dict0mem.h"
47 #include "dict0priv.h"
48 #ifndef UNIV_HOTBACKUP
49 #include "dict0stats.h"
50 #endif /* !UNIV_HOTBACKUP */
51 #include "data0type.h"
52 #include "dict0dict.h"
53 #include "fil0fil.h"
54 #include "mach0data.h"
55 #include "rem0rec.h"
56 #ifndef UNIV_HOTBACKUP
57 #include "fts0priv.h"
58 #include "gis/rtree_support.h" // fetch_srs
59 #endif /* !UNIV_HOTBACKUP */
60 #include "srv0start.h"
61 #include "ut0crc32.h"
62 #ifndef UNIV_HOTBACKUP
63 #include "btr0sea.h"
64 #include "default_values.h"
65 #include "derror.h"
66 #include "fts0plugin.h"
67 #include "ha_innodb.h"
68 #include "ha_innopart.h"
69 #include "ha_prototypes.h"
70 #include "mysql/plugin.h"
71 #include "query_options.h"
72 #include "sql/mysqld.h" // lower_case_file_system
73 #include "sql_base.h"
74 #include "sql_table.h"
75 #endif /* !UNIV_HOTBACKUP */
76
encode(const byte * stream,size_t in_len,size_t * out_len)77 const char *DD_instant_col_val_coder::encode(const byte *stream, size_t in_len,
78 size_t *out_len) {
79 cleanup();
80
81 m_result = UT_NEW_ARRAY_NOKEY(byte, in_len * 2);
82 char *result = reinterpret_cast<char *>(m_result);
83
84 for (size_t i = 0; i < in_len; ++i) {
85 uint8_t v1 = ((stream[i] & 0xF0) >> 4);
86 uint8_t v2 = (stream[i] & 0x0F);
87
88 result[i * 2] = (v1 < 10 ? '0' + v1 : 'a' + v1 - 10);
89 result[i * 2 + 1] = (v2 < 10 ? '0' + v2 : 'a' + v2 - 10);
90 }
91
92 *out_len = in_len * 2;
93
94 return (result);
95 }
96
decode(const char * stream,size_t in_len,size_t * out_len)97 const byte *DD_instant_col_val_coder::decode(const char *stream, size_t in_len,
98 size_t *out_len) {
99 ut_ad(in_len % 2 == 0);
100
101 cleanup();
102
103 m_result = UT_NEW_ARRAY_NOKEY(byte, in_len / 2);
104
105 for (size_t i = 0; i < in_len / 2; ++i) {
106 char c1 = stream[i * 2];
107 char c2 = stream[i * 2 + 1];
108
109 ut_ad(isdigit(c1) || (c1 >= 'a' && c1 <= 'f'));
110 ut_ad(isdigit(c2) || (c2 >= 'a' && c2 <= 'f'));
111
112 m_result[i] = ((isdigit(c1) ? c1 - '0' : c1 - 'a' + 10) << 4) +
113 ((isdigit(c2) ? c2 - '0' : c2 - 'a' + 10));
114 }
115
116 *out_len = in_len / 2;
117
118 return (m_result);
119 }
120
121 #ifndef UNIV_HOTBACKUP
122 /** Check if the InnoDB index is consistent with dd::Index
123 @param[in] index InnoDB index
124 @param[in] dd_index dd::Index or dd::Partition_index
125 @return true if match
126 @retval false if not match */
127 template <typename Index>
dd_index_match(const dict_index_t * index,const Index * dd_index)128 static bool dd_index_match(const dict_index_t *index, const Index *dd_index) {
129 bool match = true;
130
131 /* Don't check the name for primary index, since internal index
132 name could be variant */
133 if (my_strcasecmp(system_charset_info, index->name(),
134 dd_index->name().c_str()) != 0 &&
135 strcmp(dd_index->name().c_str(), "PRIMARY") != 0) {
136 ib::warn(ER_IB_MSG_162)
137 << "Index name in InnoDB is " << index->name()
138 << " while index name in global DD is " << dd_index->name();
139 match = false;
140 }
141
142 const dd::Properties &p = dd_index->se_private_data();
143 uint64 id = 0;
144 uint32 root = 0;
145 uint64 trx_id = 0;
146 ut_ad(p.exists(dd_index_key_strings[DD_INDEX_ID]));
147 p.get(dd_index_key_strings[DD_INDEX_ID], &id);
148 if (id != index->id) {
149 ib::warn(ER_IB_MSG_163)
150 << "Index id in InnoDB is " << index->id << " while index id in"
151 << " global DD is " << id;
152 match = false;
153 }
154
155 ut_ad(p.exists(dd_index_key_strings[DD_INDEX_ROOT]));
156 p.get(dd_index_key_strings[DD_INDEX_ROOT], &root);
157 if (root != index->page) {
158 ib::warn(ER_IB_MSG_164)
159 << "Index root in InnoDB is " << index->page << " while index root in"
160 << " global DD is " << root;
161 match = false;
162 }
163
164 ut_ad(p.exists(dd_index_key_strings[DD_INDEX_TRX_ID]));
165 p.get(dd_index_key_strings[DD_INDEX_TRX_ID], &trx_id);
166 /* For DD tables, the trx_id=0 is got from get_se_private_id().
167 TODO: index->trx_id is not expected to be 0 once Bug#25730513 is fixed*/
168 if (trx_id != 0 && index->trx_id != 0 && trx_id != index->trx_id) {
169 ib::warn(ER_IB_MSG_165) << "Index transaction id in InnoDB is "
170 << index->trx_id << " while index transaction"
171 << " id in global DD is " << trx_id;
172 match = false;
173 }
174
175 return (match);
176 }
177
178 /** Check if the InnoDB table is consistent with dd::Table
179 @param[in] table InnoDB table
180 @param[in] dd_table dd::Table or dd::Partition
181 @return true if match
182 @retval false if not match */
183 template <typename Table>
dd_table_match(const dict_table_t * table,const Table * dd_table)184 bool dd_table_match(const dict_table_t *table, const Table *dd_table) {
185 /* Temporary table has no metadata written */
186 if (dd_table == nullptr || table->is_temporary()) {
187 return (true);
188 }
189
190 bool match = true;
191
192 if (dd_table->se_private_id() != table->id) {
193 ib::warn(ER_IB_MSG_166)
194 << "Table id in InnoDB is " << table->id
195 << " while the id in global DD is " << dd_table->se_private_id();
196 match = false;
197 }
198
199 /* If tablespace is discarded, no need to check indexes */
200 if (dict_table_is_discarded(table)) {
201 return (match);
202 }
203
204 for (const auto dd_index : dd_table->indexes()) {
205 if (dd_table->tablespace_id() == dict_sys_t::s_dd_sys_space_id &&
206 dd_index->tablespace_id() != dd_table->tablespace_id()) {
207 ib::warn(ER_IB_MSG_167)
208 << "Tablespace id in table is " << dd_table->tablespace_id()
209 << ", while tablespace id in index " << dd_index->name() << " is "
210 << dd_index->tablespace_id();
211 }
212
213 const dict_index_t *index = dd_find_index(table, dd_index);
214 ut_ad(index != nullptr);
215
216 if (!dd_index_match(index, dd_index)) {
217 match = false;
218 }
219 }
220
221 /* Tablespace and options can be checked here too */
222 return (match);
223 }
224
225 template bool dd_table_match<dd::Table>(const dict_table_t *,
226 const dd::Table *);
227 template bool dd_table_match<dd::Partition>(const dict_table_t *,
228 const dd::Partition *);
229
230 /** Release a metadata lock.
231 @param[in,out] thd current thread
232 @param[in,out] mdl metadata lock */
dd_mdl_release(THD * thd,MDL_ticket ** mdl)233 void dd_mdl_release(THD *thd, MDL_ticket **mdl) {
234 if (*mdl == nullptr) {
235 return;
236 }
237
238 dd::release_mdl(thd, *mdl);
239 *mdl = nullptr;
240 }
241
dd_thd_for_undo(const trx_t * trx)242 THD *dd_thd_for_undo(const trx_t *trx) {
243 return trx->mysql_thd == nullptr ? current_thd : trx->mysql_thd;
244 }
245
246 /** Check if current undo needs a MDL or not
247 @param[in] trx transaction
248 @return true if MDL is necessary, otherwise false */
dd_mdl_for_undo(const trx_t * trx)249 bool dd_mdl_for_undo(const trx_t *trx) {
250 /* Try best to find a valid THD for checking, in case in background
251 rollback thread, trx doens't hold a mysql_thd */
252 THD *thd = dd_thd_for_undo(trx);
253
254 /* There are four cases for the undo to check here:
255 1. In recovery phase, binlog recover, there is no concurrent
256 user queries, so MDL is no necessary. In this case, thd is NULL.
257 2. In background rollback thread, there could be concurrent
258 user queries, so MDL is needed. In this case, thd is not NULL
259 3. In runtime transaction rollback, no need for MDL.
260 THD::transaction_rollback_request would be set.
261 4. In runtime asynchronous rollback, no need for MDL.
262 Check TRX_FORCE_ROLLBACK. */
263 return (thd != nullptr && !thd->transaction_rollback_request &&
264 ((trx->in_innodb & TRX_FORCE_ROLLBACK) == 0));
265 }
266
267 /** Determine if a table contains a fulltext index.
268 @param[in] table dd::Table
269 @return whether the table contains any fulltext index */
dd_table_contains_fulltext(const dd::Table & table)270 inline bool dd_table_contains_fulltext(const dd::Table &table) {
271 for (const dd::Index *index : table.indexes()) {
272 if (index->type() == dd::Index::IT_FULLTEXT) {
273 return (true);
274 }
275 }
276 return (false);
277 }
278
get_innobase_type_from_dd(const dd::Column * col,ulint & unsigned_type)279 ulint get_innobase_type_from_dd(const dd::Column *col, ulint &unsigned_type) {
280 unsigned_type = col->is_unsigned() ? DATA_UNSIGNED : 0;
281
282 switch (col->type()) {
283 case dd::enum_column_types::DECIMAL:
284 return DATA_DECIMAL;
285
286 case dd::enum_column_types::LONG:
287 return DATA_INT;
288 case dd::enum_column_types::LONGLONG:
289 return DATA_INT;
290 case dd::enum_column_types::TINY:
291 return DATA_INT;
292 case dd::enum_column_types::SHORT:
293 return DATA_INT;
294 case dd::enum_column_types::INT24:
295 return DATA_INT;
296 case dd::enum_column_types::DATE:
297 return DATA_INT;
298 case dd::enum_column_types::YEAR:
299 return DATA_INT;
300 case dd::enum_column_types::NEWDATE:
301 return DATA_INT;
302 case dd::enum_column_types::TIME:
303 return DATA_INT;
304 case dd::enum_column_types::DATETIME:
305 return DATA_INT;
306 case dd::enum_column_types::TIMESTAMP:
307 return DATA_INT;
308
309 case dd::enum_column_types::FLOAT:
310 return DATA_FLOAT;
311
312 case dd::enum_column_types::DOUBLE:
313 return DATA_DOUBLE;
314
315 case dd::enum_column_types::TYPE_NULL:
316 return 0;
317
318 case dd::enum_column_types::VAR_STRING:
319 case dd::enum_column_types::VARCHAR:
320 if (col->collation_id() == my_charset_bin.number) {
321 return DATA_BINARY;
322 } else if (col->collation_id() == my_charset_latin1.number) {
323 return DATA_VARCHAR;
324 }
325 return DATA_VARMYSQL;
326
327 case dd::enum_column_types::BIT:
328 return DATA_FIXBINARY;
329 case dd::enum_column_types::STRING:
330 if (col->collation_id() == my_charset_bin.number) {
331 return DATA_FIXBINARY;
332 } else if (col->collation_id() == my_charset_latin1.number) {
333 return DATA_CHAR;
334 }
335 return DATA_MYSQL;
336
337 case dd::enum_column_types::NEWDECIMAL:
338 return DATA_FIXBINARY;
339
340 case dd::enum_column_types::TIME2:
341 return DATA_FIXBINARY;
342 case dd::enum_column_types::TIMESTAMP2:
343 return DATA_FIXBINARY;
344 case dd::enum_column_types::DATETIME2:
345 return DATA_FIXBINARY;
346
347 case dd::enum_column_types::ENUM:
348 unsigned_type = DATA_UNSIGNED;
349 return DATA_INT;
350 case dd::enum_column_types::SET:
351 unsigned_type = DATA_UNSIGNED;
352 return DATA_INT;
353
354 case dd::enum_column_types::TINY_BLOB:
355 return DATA_BLOB;
356 case dd::enum_column_types::MEDIUM_BLOB:
357 return DATA_BLOB;
358 case dd::enum_column_types::BLOB:
359 return DATA_BLOB;
360 case dd::enum_column_types::LONG_BLOB:
361 return DATA_BLOB;
362 case dd::enum_column_types::JSON:
363 return DATA_BLOB;
364
365 case dd::enum_column_types::GEOMETRY:
366 return DATA_GEOMETRY;
367
368 default:
369 DBUG_ASSERT(!"Should not hit here"); /* purecov: deadcode */
370 }
371
372 return MYSQL_TYPE_LONG;
373 }
374
dd_table_create_on_dd_obj(const dd::Table * dd_table,const dd::Partition * dd_part,const dd::String_type * schema_name,bool is_implicit,space_id_t space_id)375 dict_table_t *dd_table_create_on_dd_obj(const dd::Table *dd_table,
376 const dd::Partition *dd_part,
377 const dd::String_type *schema_name,
378 bool is_implicit, space_id_t space_id) {
379 using namespace dict_name;
380 mem_heap_t *heap = mem_heap_create(1000);
381
382 char table_name[MAX_SPACE_NAME_LEN + 1];
383 char tmp_schema[MAX_DATABASE_NAME_LEN + 1];
384 char tmp_tablename[MAX_TABLE_NAME_LEN + 1];
385
386 tablename_to_filename(schema_name->c_str(), tmp_schema,
387 MAX_DATABASE_NAME_LEN + 1);
388 tablename_to_filename(dd_table->name().c_str(), tmp_tablename,
389 MAX_TABLE_NAME_LEN + 1);
390
391 if (dd_part) {
392 fil_space_t *space = fil_space_get(space_id);
393 ut_ad(space != nullptr);
394
395 std::string file_table_name(space->name);
396
397 /* 8.0.19 and below can have upper case separators in partition
398 names, 8.0.20 and above have always lower case separators */
399 bool is_upper = false;
400 if (file_table_name.find("#P#") != std::string::npos) {
401 is_upper = true;
402 } else if (file_table_name.find("#p#") != std::string::npos) {
403 is_upper = false;
404 } else {
405 /* This shouldn't happen, a partition should either have have
406 #P# or #p# in its name */
407 ut_ad(0);
408 }
409
410 if (dd_part->parent_partition() == nullptr) {
411 snprintf(table_name, sizeof table_name, "%s/%s%s%s", tmp_schema,
412 tmp_tablename, is_upper ? ALT_PART_SEPARATOR : PART_SEPARATOR,
413 dd_part->name().c_str());
414 } else {
415 snprintf(table_name, sizeof table_name, "%s/%s%s%s%s%s", tmp_schema,
416 tmp_tablename, is_upper ? ALT_PART_SEPARATOR : PART_SEPARATOR,
417 dd_part->name().c_str(),
418 is_upper ? ALT_SUB_PART_SEPARATOR : SUB_PART_SEPARATOR,
419 dd_part->parent_partition()->name().c_str());
420 }
421 } else {
422 snprintf(table_name, sizeof table_name, "%s/%s", tmp_schema, tmp_tablename);
423 }
424
425 bool has_doc_id = false;
426
427 /* First check if dd::Table contains the right hidden column
428 as FTS_DOC_ID */
429 const dd::Column *doc_col;
430 doc_col = dd_find_column(dd_table, FTS_DOC_ID_COL_NAME);
431
432 /* Check weather this is a proper typed FTS_DOC_ID */
433 if (doc_col && doc_col->type() == dd::enum_column_types::LONGLONG &&
434 !doc_col->is_nullable()) {
435 has_doc_id = true;
436 }
437
438 const bool fulltext = dd_table_contains_fulltext(*dd_table);
439
440 /* If there is a fulltext index, then it must have a FTS_DOC_ID */
441 if (fulltext) {
442 ut_ad(has_doc_id);
443 }
444
445 ulint add_doc_id = 0;
446
447 /* Need to add FTS_DOC_ID column if it is not defined by user,
448 since TABLE_SHARE::fields does not contain it if it is a hidden col */
449 if (has_doc_id && doc_col->is_se_hidden()) {
450 add_doc_id = 1;
451 }
452
453 ulint n_cols = 0;
454 for (auto dd_col : dd_table->columns()) {
455 if (!dd_col->is_se_hidden()) n_cols++;
456 }
457 n_cols += add_doc_id;
458
459 ulint n_v_cols = 0;
460 ulint n_m_v_cols = 0;
461 for (auto dd_col : dd_table->columns()) {
462 if (dd_col->is_virtual()) {
463 n_v_cols++;
464 if (dd_col->is_array()) {
465 n_m_v_cols++;
466 }
467 }
468 }
469
470 dict_table_t *table =
471 dict_mem_table_create(table_name, 0, n_cols, n_v_cols, n_m_v_cols, 0, 0);
472
473 if (dd_part) {
474 table->id = dd_part->se_private_id();
475 } else {
476 table->id = dd_table->se_private_id();
477 }
478
479 if (dd_table->se_private_data().exists(
480 dd_table_key_strings[DD_TABLE_DATA_DIRECTORY])) {
481 table->flags |= DICT_TF_MASK_DATA_DIR;
482 }
483
484 /* If the table has instantly added columns, it's necessary to read
485 the number of instant columns for either normal table(from dd::Table),
486 or partitioned table(from dd::Partition). One partition may have no
487 instant columns, which is fine. */
488 if (dd_table_has_instant_cols(*dd_table)) {
489 uint32_t instant_cols;
490
491 if (dd_part == nullptr) {
492 dd_table->se_private_data().get(
493 dd_table_key_strings[DD_TABLE_INSTANT_COLS], &instant_cols);
494 table->set_instant_cols(instant_cols);
495 ut_ad(table->has_instant_cols());
496 } else if (dd_part_has_instant_cols(*dd_part)) {
497 dd_part->se_private_data().get(
498 dd_partition_key_strings[DD_PARTITION_INSTANT_COLS], &instant_cols);
499
500 table->set_instant_cols(instant_cols);
501 ut_ad(table->has_instant_cols());
502 }
503 }
504
505 /* Check if this table is FTS AUX table, if so, set DICT_TF2_AUX flag */
506 fts_aux_table_t aux_table;
507 if (fts_is_aux_table_name(&aux_table, table_name, strlen(table_name))) {
508 DICT_TF2_FLAG_SET(table, DICT_TF2_AUX);
509 table->parent_id = aux_table.parent_id;
510 }
511
512 table->fts = nullptr;
513 if (has_doc_id) {
514 if (fulltext) {
515 DICT_TF2_FLAG_SET(table, DICT_TF2_FTS);
516 }
517
518 if (add_doc_id) {
519 DICT_TF2_FLAG_SET(table, DICT_TF2_FTS_HAS_DOC_ID);
520 }
521
522 if (fulltext || add_doc_id) {
523 table->fts = fts_create(table);
524 table->fts->cache = fts_cache_create(table);
525 }
526 }
527
528 bool is_encrypted = false;
529 bool is_discard = false;
530
531 /* Set encryption option. */
532 dd::String_type encrypt;
533 if (dd_table->table().options().exists("encrypt_type")) {
534 dd_table->table().options().get("encrypt_type", &encrypt);
535 if (!Encryption::is_none(encrypt.c_str())) {
536 ut_ad(innobase_strcasecmp(encrypt.c_str(), "y") == 0);
537 is_encrypted = true;
538 }
539 }
540
541 /* Check discard flag. */
542 const dd::Properties &p = dd_table->table().se_private_data();
543 if (p.exists(dd_table_key_strings[DD_TABLE_DISCARD])) {
544 p.get(dd_table_key_strings[DD_TABLE_DISCARD], &is_discard);
545 }
546
547 if (is_discard) {
548 table->ibd_file_missing = true;
549 DICT_TF2_FLAG_SET(table, DICT_TF2_DISCARDED);
550 }
551
552 bool is_redundant = false;
553 bool blob_prefix = false;
554 rec_format_t rec_format;
555 ulint zip_ssize = 0;
556
557 switch (dd_table->row_format()) {
558 case dd::Table::RF_REDUNDANT:
559 is_redundant = true;
560 blob_prefix = true;
561 rec_format = REC_FORMAT_REDUNDANT;
562 break;
563 case dd::Table::RF_COMPACT:
564 blob_prefix = true;
565 rec_format = REC_FORMAT_COMPACT;
566 break;
567 case dd::Table::RF_COMPRESSED: {
568 uint32 key_block_size = 0;
569 const ulint zip_ssize_max =
570 std::min((ulint)UNIV_PAGE_SSIZE_MAX, (ulint)PAGE_ZIP_SSIZE_MAX);
571 rec_format = REC_FORMAT_COMPRESSED;
572 if (dd_table->table().options().exists("key_block_size")) {
573 dd_table->table().options().get("key_block_size", &key_block_size);
574 if (key_block_size != 0) {
575 for (unsigned kbsize = 1, zssize = 1; zssize <= zip_ssize_max;
576 zssize++, kbsize <<= 1) {
577 if (kbsize == key_block_size) {
578 zip_ssize = zssize;
579 break;
580 }
581 }
582 } else {
583 zip_ssize = zip_ssize_max - 1;
584 }
585 }
586 } break;
587 case dd::Table::RF_DYNAMIC:
588 rec_format = REC_FORMAT_DYNAMIC;
589 break;
590 case dd::Table::RF_FIXED:
591 /* fall through */
592 case dd::Table::RF_PAGED:
593 /* fall through */
594 default:
595 ut_error;
596 break;
597 }
598
599 uint32_t table_flags = 0;
600 dict_tf_set(&table_flags, rec_format, zip_ssize, false /*use_data_dir*/,
601 false /*shared_space*/);
602
603 table->flags |= table_flags;
604
605 if (!is_redundant) {
606 table->flags |= DICT_TF_COMPACT;
607 }
608
609 if (!blob_prefix) {
610 table->flags |= (1 << DICT_TF_POS_ATOMIC_BLOBS);
611 }
612
613 if (is_implicit) {
614 DICT_TF2_FLAG_SET(table, DICT_TF2_USE_FILE_PER_TABLE);
615 } else {
616 table->flags |= (1 << DICT_TF_POS_SHARED_SPACE);
617 }
618
619 /* since DD object ID is always INVALID_OBJECT_ID, xtrabackup is assimung
620 that temporary table name starts with '#' */
621 bool is_temp = (table->name.m_name[0] == '#');
622 if (is_temp) {
623 table->flags2 |= DICT_TF2_TEMPORARY;
624 }
625
626 if (is_encrypted) {
627 /* We don't support encrypt intrinsic and temporary table. */
628 ut_ad(!table->is_intrinsic() && !table->is_temporary());
629 DICT_TF2_FLAG_SET(table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
630 }
631
632 ulint i = 0;
633 for (const auto dd_col : dd_table->columns()) {
634 if (dd_col->is_se_hidden()) continue;
635
636 ulint prtype = 0;
637
638 ++i;
639
640 ulint nulls_allowed;
641 ulint unsigned_type;
642 ulint binary_type;
643 ulint charset_no;
644 ulint long_true_varchar;
645 ulint field_length = column_pack_length(*dd_col);
646 ulint col_len = field_length;
647 enum_field_types field_type = dd_get_old_field_type(dd_col->type());
648 ulint mtype = get_innobase_type_from_dd(dd_col, unsigned_type);
649
650 if (field_type == MYSQL_TYPE_BIT) unsigned_type = DATA_UNSIGNED;
651
652 /* Following are MySQL internal types, never used in protocol. We have
653 field->real_type(), lets convert it to field->type() */
654 switch (field_type) {
655 case MYSQL_TYPE_TIME2:
656 field_type = MYSQL_TYPE_TIME;
657 break;
658 case MYSQL_TYPE_DATETIME2:
659 field_type = MYSQL_TYPE_DATETIME;
660 break;
661 case MYSQL_TYPE_TIMESTAMP2:
662 field_type = MYSQL_TYPE_TIMESTAMP;
663 break;
664 case MYSQL_TYPE_MEDIUM_BLOB:
665 case MYSQL_TYPE_LONG_BLOB:
666 case MYSQL_TYPE_TINY_BLOB:
667 field_type = MYSQL_TYPE_BLOB;
668 break;
669 case MYSQL_TYPE_NEWDATE:
670 field_type = MYSQL_TYPE_DATE;
671 break;
672 case MYSQL_TYPE_ENUM:
673 case MYSQL_TYPE_SET:
674 field_type = MYSQL_TYPE_STRING;
675 break;
676 default:
677 break;
678 }
679
680 long_true_varchar = 0;
681 if (field_type == MYSQL_TYPE_VARCHAR) {
682 byte pack_length = HA_VARCHAR_PACKLENGTH(dd_col->char_length());
683 col_len -= pack_length;
684
685 if (pack_length == 2) {
686 long_true_varchar = DATA_LONG_TRUE_VARCHAR;
687 }
688 }
689
690 nulls_allowed = dd_col->is_nullable() ? 0 : DATA_NOT_NULL;
691
692 /* Convert non nullable fields in FTS AUX tables as nullable.
693 This is because in 5.7, we created FTS AUX tables clustered
694 index with nullable field, although NULLS are not inserted.
695 When fields are nullable, the record layout is dependent on
696 that. When registering FTS AUX Tables with new DD, we cannot
697 register nullable fields as part of Primary Key. Hence we register
698 them as non-nullabe in DD but treat as nullable in InnoDB.
699 This way the compatibility with 5.7 FTS AUX tables is also
700 maintained. */
701 if (table->is_fts_aux()) {
702 const dd::Properties &p = dd_col->se_private_data();
703 if (p.exists("nullable")) {
704 bool nullable;
705 p.get("nullable", &nullable);
706 nulls_allowed = nullable ? 0 : DATA_NOT_NULL;
707 }
708 }
709
710 binary_type = (((dd_col->type() == dd::enum_column_types::VARCHAR) ||
711 (dd_col->type() == dd::enum_column_types::STRING) ||
712 (dd_col->type() == dd::enum_column_types::VAR_STRING) ||
713 (dd_col->type() == dd::enum_column_types::BLOB) ||
714 (dd_col->type() == dd::enum_column_types::TINY_BLOB) ||
715 (dd_col->type() == dd::enum_column_types::ENUM) ||
716 (dd_col->type() == dd::enum_column_types::SET) ||
717 (dd_col->type() == dd::enum_column_types::MEDIUM_BLOB) ||
718 (dd_col->type() == dd::enum_column_types::LONG_BLOB)) &&
719 dd_col->collation_id() != my_charset_bin.number)
720 ? 0
721 : DATA_BINARY_TYPE;
722
723 charset_no = 0;
724
725 if (dd_col->type() == dd::enum_column_types::NEWDECIMAL)
726 charset_no = my_charset_latin1.number;
727 else if (dd_col->type() == dd::enum_column_types::BIT)
728 charset_no = my_charset_bin.number;
729 else if (dd_col->type() == dd::enum_column_types::JSON)
730 charset_no = my_charset_utf8mb4_bin.number;
731 else if (dtype_is_string_type(mtype)) {
732 charset_no = static_cast<ulint>(dd_col->collation_id());
733 }
734
735 ulint is_virtual = (dd_col->is_virtual()) ? DATA_VIRTUAL : 0;
736
737 ulint is_multi_val = (dd_col->is_array()) ? DATA_MULTI_VALUE : 0;
738
739 bool is_stored =
740 !dd_col->is_generation_expression_null() && !dd_col->is_virtual();
741
742 if (is_multi_val) {
743 col_len = field_length;
744 }
745
746 prtype = dtype_form_prtype(
747 (ulint)(field_type) | nulls_allowed | unsigned_type | binary_type |
748 long_true_varchar | is_virtual | is_multi_val,
749 charset_no);
750 if (!is_virtual) {
751 dict_mem_table_add_col(table, heap, dd_col->name().c_str(), mtype, prtype,
752 col_len, true);
753 } else {
754 dict_mem_table_add_v_col(table, heap, dd_col->name().c_str(), mtype,
755 prtype, col_len, i, 0, true);
756 }
757
758 if (is_stored) {
759 ut_ad(!is_virtual);
760 /* Added stored column in m_s_cols list. */
761 dict_mem_table_add_s_col(table, 0);
762 }
763 }
764
765 if (add_doc_id) {
766 /* Add the hidden FTS_DOC_ID column. */
767 fts_add_doc_id_column(table, heap);
768 }
769
770 /* Add system columns to make adding index work */
771 dict_table_add_system_columns(table, heap);
772
773 /* It appears that index list for InnoDB table always starts with
774 primary key */
775 ut_ad(dd_table->indexes().size() > 0);
776 const dd::Index *primary_key = dd_table->indexes()[0];
777
778 uint n_pk_fields = std::count_if(
779 primary_key->elements().begin(), primary_key->elements().end(),
780 [](const dd::Index_element *el) { return el->length() != (uint)(-1); });
781
782 if (n_pk_fields == 0) {
783 /* primary key contains only SE hidden columns like DB_ROW_ID */
784 dict_index_t *index = dict_mem_index_create(
785 table->name.m_name, "GEN_CLUST_INDEX", 0, DICT_CLUSTERED, 0);
786 index->n_uniq = 0;
787
788 dberr_t new_err = dict_index_add_to_cache(table, index, index->page, FALSE);
789 if (new_err != DB_SUCCESS) {
790 return (nullptr);
791 }
792 }
793
794 for (const auto dd_index : dd_table->indexes()) {
795 ulint n_fields = 0;
796
797 if (dd_index->is_hidden()) continue;
798
799 for (const auto *idx_elem : dd_index->elements()) {
800 if (idx_elem->length() == (uint)(-1)) continue;
801
802 n_fields++;
803 }
804
805 ulint n_uniq = n_fields;
806 ulint type = 0;
807
808 if (n_uniq == 0) continue;
809
810 if (dd_index->type() == dd::Index::IT_SPATIAL) {
811 ut_ad(!table->is_intrinsic());
812 type = DICT_SPATIAL;
813 ut_ad(n_fields == 1);
814 } else if (dd_index->type() == dd::Index::IT_FULLTEXT) {
815 ut_ad(!table->is_intrinsic());
816 type = DICT_FTS;
817 n_uniq = 0;
818 } else if (dd_index == primary_key) {
819 ut_ad(dd_index->type() == dd::Index::IT_PRIMARY ||
820 dd_index->type() == dd::Index::IT_UNIQUE);
821 ut_ad(n_uniq > 0);
822 type = DICT_CLUSTERED | DICT_UNIQUE;
823 } else {
824 type = (dd_index->type() == dd::Index::IT_UNIQUE) ? DICT_UNIQUE : 0;
825 }
826
827 dict_index_t *index = dict_mem_index_create(
828 table->name.m_name, dd_index->name().c_str(), 0, type, n_fields);
829
830 index->n_uniq = n_uniq;
831
832 for (auto *idx_elem : dd_index->elements()) {
833 auto dd_col = &idx_elem->column();
834
835 if (idx_elem->length() == (uint)(-1)) continue;
836
837 if (!dd_col->is_generation_expression_null() && dd_col->is_virtual()) {
838 index->type |= DICT_VIRTUAL;
839 }
840
841 uint col_pos = 0;
842 for (auto c : dd_index->table().columns()) {
843 if (c == dd_col) break;
844 if (c->is_virtual() == dd_col->is_virtual()) col_pos++;
845 }
846
847 bool is_asc = (idx_elem->order() == dd::Index_element::ORDER_ASC);
848 ulint prefix_len = 0;
849
850 if (dd_index->type() == dd::Index::IT_SPATIAL) {
851 prefix_len = 0;
852 } else if (dd_index->type() == dd::Index::IT_FULLTEXT) {
853 prefix_len = 0;
854 } else if (idx_elem->length() != (uint)(-1) &&
855 idx_elem->length() != table->cols[col_pos].len) {
856 prefix_len = idx_elem->length();
857 } else {
858 prefix_len = 0;
859 }
860
861 dict_index_add_col(index, table,
862 dd_col->is_virtual() ? &table->v_cols[col_pos].m_col
863 : &table->cols[col_pos],
864 prefix_len, is_asc);
865 }
866
867 if (dict_index_add_to_cache(table, index, 0, FALSE) != DB_SUCCESS) {
868 ut_ad(0);
869 return (nullptr);
870 }
871
872 index = UT_LIST_GET_LAST(table->indexes);
873
874 if (index->type & DICT_FTS) {
875 ut_ad(dd_index->type() == dd::Index::IT_FULLTEXT);
876 ut_ad(index->n_uniq == 0);
877 ut_ad(n_uniq == 0);
878
879 if (table->fts->cache == nullptr) {
880 DICT_TF2_FLAG_SET(table, DICT_TF2_FTS);
881 table->fts->cache = fts_cache_create(table);
882
883 rw_lock_x_lock(&table->fts->cache->init_lock);
884 /* Notify the FTS cache about this index. */
885 fts_cache_index_cache_create(table, index);
886 rw_lock_x_unlock(&table->fts->cache->init_lock);
887 }
888 }
889
890 if (strcmp(index->name, FTS_DOC_ID_INDEX_NAME) == 0) {
891 ut_ad(table->fts_doc_id_index == nullptr);
892 table->fts_doc_id_index = index;
893 }
894
895 if (dict_index_is_spatial(index)) {
896 size_t geom_col_idx;
897 for (geom_col_idx = 0; geom_col_idx < dd_index->elements().size();
898 ++geom_col_idx) {
899 if (!dd_index->elements()[geom_col_idx]->column().is_se_hidden()) break;
900 }
901 const dd::Column &col = dd_index->elements()[geom_col_idx]->column();
902 bool srid_has_value = col.srs_id().has_value();
903 index->fill_srid_value(srid_has_value ? col.srs_id().value() : 0,
904 srid_has_value);
905 }
906 }
907
908 if (dict_table_has_fts_index(table)) {
909 ut_ad(DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS));
910 }
911
912 /* Create the ancillary tables that are common to all FTS indexes on
913 this table. */
914 if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) ||
915 DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS)) {
916 fts_doc_id_index_enum ret;
917
918 ut_ad(!table->is_intrinsic());
919 /* Check whether there already exists FTS_DOC_ID_INDEX */
920 bool has_fts_index = false;
921 for (dict_index_t *index = table->first_index(); index;
922 index = index->next()) {
923 if (!innobase_strcasecmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
924 has_fts_index = true;
925 break;
926 }
927 }
928 if (!has_fts_index) {
929 dict_index_t *doc_id_index;
930 doc_id_index = dict_mem_index_create(
931 table->name.m_name, FTS_DOC_ID_INDEX_NAME, 0, DICT_UNIQUE, 1);
932 doc_id_index->add_field(FTS_DOC_ID_COL_NAME, 0, true);
933
934 dberr_t err = dict_index_add_to_cache(table, doc_id_index,
935 doc_id_index->page, FALSE);
936 if (err != DB_SUCCESS) {
937 return (nullptr);
938 }
939
940 doc_id_index = UT_LIST_GET_LAST(table->indexes);
941 doc_id_index->hidden = true;
942 }
943
944 /* Cache all the FTS indexes on this table in the FTS
945 specific structure. They are used for FTS indexed
946 column update handling. */
947 if (dict_table_has_fts_index(table)) {
948 fts_t *fts = table->fts;
949 ut_a(fts != nullptr);
950
951 dict_table_get_all_fts_indexes(table, table->fts->indexes);
952 }
953
954 ulint fts_doc_id_col = ULINT_UNDEFINED;
955
956 ret = innobase_fts_check_doc_id_index(table, nullptr, &fts_doc_id_col);
957
958 if (ret != FTS_INCORRECT_DOC_ID_INDEX) {
959 ut_ad(table->fts->doc_col == ULINT_UNDEFINED);
960 table->fts->doc_col = fts_doc_id_col;
961 ut_ad(table->fts->doc_col != ULINT_UNDEFINED);
962
963 table->fts_doc_id_index =
964 dict_table_get_index_on_name(table, FTS_DOC_ID_INDEX_NAME);
965 }
966 }
967
968 /* Fill autoincrement data */
969 i = 0;
970 for (auto dd_col : dd_table->columns()) {
971 if (dd_col->is_se_hidden()) continue;
972
973 ++i;
974
975 if (!dd_col->is_auto_increment()) continue;
976
977 const dd::Properties &p = dd_table->table().se_private_data();
978 dict_table_autoinc_set_col_pos(table, i - 1);
979 uint64 version, autoinc = 0;
980 if (p.get(dd_table_key_strings[DD_TABLE_VERSION], &version) ||
981 p.get(dd_table_key_strings[DD_TABLE_AUTOINC], &autoinc)) {
982 ut_ad(!"problem setting AUTO_INCREMENT");
983 return (nullptr);
984 }
985
986 table->version = version;
987 dict_table_autoinc_lock(table);
988 dict_table_autoinc_initialize(table, autoinc + 1);
989 dict_table_autoinc_unlock(table);
990 table->autoinc_persisted = autoinc;
991 }
992
993 /* Now fill the space ID and Root page number for each index */
994 bool first_index = true;
995 i = 0;
996 dict_index_t *index = table->first_index();
997 for (const auto dd_index : dd_table->indexes()) {
998 ut_ad(index != nullptr);
999
1000 const dd::Properties &se_private_data =
1001 dd_part == nullptr ? dd_index->se_private_data()
1002 : dd_part->indexes()[i]->se_private_data();
1003 uint64 id = 0;
1004 uint32 root = 0;
1005 uint32 sid = 0;
1006 uint64 trx_id = 0;
1007 dd::Object_id index_space_id = dd_index->tablespace_id();
1008
1009 if (dd_table->tablespace_id() == dict_sys_t::s_dd_space_id) {
1010 sid = dict_sys_t::s_space_id;
1011 } else if (dd_table->tablespace_id() == dict_sys_t::s_dd_temp_space_id) {
1012 sid = dict_sys_t::s_temp_space_id;
1013 } else {
1014 if (se_private_data.get(dd_index_key_strings[DD_INDEX_SPACE_ID], &sid)) {
1015 return (nullptr);
1016 }
1017 }
1018
1019 if (se_private_data.get(dd_index_key_strings[DD_INDEX_ID], &id) ||
1020 se_private_data.get(dd_index_key_strings[DD_INDEX_ROOT], &root) ||
1021 se_private_data.get(dd_index_key_strings[DD_INDEX_TRX_ID], &trx_id)) {
1022 return (nullptr);
1023 }
1024
1025 if (first_index) {
1026 ut_ad(table->space == 0);
1027 table->space = sid;
1028 table->dd_space_id = index_space_id;
1029 first_index = false;
1030 }
1031
1032 ut_ad(root > 1);
1033 ut_ad(index->type & DICT_FTS || root != FIL_NULL ||
1034 dict_table_is_discarded(table));
1035 ut_ad(id != 0);
1036 index->page = root;
1037 index->space = sid;
1038 index->id = id;
1039 index->trx_id = trx_id;
1040
1041 /** Look up the spatial reference system in the
1042 dictionary. Since this may cause a table open to read the
1043 dictionary tables, it must be done while not holding
1044 &dict_sys->mutex. */
1045 if (dict_index_is_spatial(index))
1046 index->rtr_srs.reset(fetch_srs(index->srid));
1047
1048 index = index->next();
1049 ++i;
1050 }
1051
1052 mutex_enter(&dict_sys->mutex);
1053
1054 dict_table_add_to_cache(table, false, heap);
1055 table->acquire();
1056
1057 mutex_exit(&dict_sys->mutex);
1058
1059 mem_heap_free(heap);
1060
1061 return (table);
1062 }
1063
dd_table_id_and_part(space_id_t space_id,const dd::Table & dd_table,const dd::Partition * & dd_part)1064 table_id_t dd_table_id_and_part(space_id_t space_id, const dd::Table &dd_table,
1065 const dd::Partition *&dd_part) {
1066 uint64 table_id = dd_table.se_private_id();
1067
1068 dd_part = nullptr;
1069
1070 if (table_id == dd::INVALID_OBJECT_ID) {
1071 /* Partitioned table */
1072 ut_ad(dd_table_is_partitioned(dd_table));
1073
1074 for (auto part : dd_table.leaf_partitions()) {
1075 for (auto index : *part->indexes()) {
1076 uint64 prop_space_id;
1077
1078 dd::Properties &p = index->se_private_data();
1079 p.get(dd_index_key_strings[DD_INDEX_SPACE_ID], &prop_space_id);
1080
1081 if (prop_space_id == space_id) {
1082 p.get(dd_index_key_strings[DD_TABLE_ID], &table_id);
1083 dd_part = part;
1084 goto done;
1085 }
1086 }
1087 }
1088 }
1089
1090 done:
1091 ut_ad(table_id != dd::INVALID_OBJECT_ID);
1092
1093 return (table_id);
1094 }
1095
dd_table_load_part(table_id_t table_id,const dd::Table & dd_table,const dd::Partition * dd_part,dict_table_t * & table,THD * thd,const dd::String_type * schema_name,bool implicit,space_id_t space_id)1096 static int dd_table_load_part(table_id_t table_id, const dd::Table &dd_table,
1097 const dd::Partition *dd_part,
1098 dict_table_t *&table, THD *thd,
1099 const dd::String_type *schema_name, bool implicit,
1100 space_id_t space_id) {
1101 const ulint fold = ut_fold_ull(table_id);
1102
1103 ut_ad(table_id != dd::INVALID_OBJECT_ID);
1104
1105 mutex_enter(&dict_sys->mutex);
1106
1107 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *, table,
1108 ut_ad(table->cached), table->id == table_id);
1109
1110 if (table != nullptr) {
1111 table->acquire();
1112 }
1113
1114 mutex_exit(&dict_sys->mutex);
1115
1116 if (table != nullptr) {
1117 return (0);
1118 }
1119
1120 table = dd_table_create_on_dd_obj(&dd_table, dd_part, schema_name, implicit,
1121 space_id);
1122
1123 if (table != nullptr) {
1124 return (0);
1125 }
1126
1127 return (1);
1128 }
1129
dd_table_open_on_dd_obj(dd::cache::Dictionary_client * client,space_id_t space_id,const dd::Table & dd_table,dict_table_t * & table,THD * thd,const dd::String_type * schema_name)1130 int dd_table_open_on_dd_obj(dd::cache::Dictionary_client *client,
1131 space_id_t space_id, const dd::Table &dd_table,
1132 dict_table_t *&table, THD *thd,
1133 const dd::String_type *schema_name) {
1134 const dd::Partition *dd_part = nullptr;
1135 const table_id_t table_id = dd_table_id_and_part(space_id, dd_table, dd_part);
1136 const ulint fold = ut_fold_ull(table_id);
1137
1138 ut_ad(table_id != dd::INVALID_OBJECT_ID);
1139
1140 mutex_enter(&dict_sys->mutex);
1141
1142 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *, table,
1143 ut_ad(table->cached), table->id == table_id);
1144
1145 if (table != nullptr) {
1146 table->acquire();
1147 }
1148
1149 mutex_exit(&dict_sys->mutex);
1150
1151 if (table != nullptr) {
1152 return (0);
1153 }
1154
1155 table = dd_table_create_on_dd_obj(&dd_table, dd_part, schema_name, false,
1156 space_id);
1157
1158 if (table != nullptr) {
1159 return (0);
1160 }
1161
1162 return (1);
1163 }
1164
dd_table_load_on_dd_obj(dd::cache::Dictionary_client * client,space_id_t space_id,const dd::Table & dd_table,dict_table_t * & table,THD * thd,const dd::String_type * schema_name,bool implicit)1165 int dd_table_load_on_dd_obj(dd::cache::Dictionary_client *client,
1166 space_id_t space_id, const dd::Table &dd_table,
1167 dict_table_t *&table, THD *thd,
1168 const dd::String_type *schema_name, bool implicit) {
1169 uint64 table_id = dd_table.se_private_id();
1170
1171 if (table_id == dd::INVALID_OBJECT_ID) {
1172 /* Partitioned table */
1173 ut_ad(dd_table_is_partitioned(dd_table));
1174
1175 for (auto part : dd_table.leaf_partitions()) {
1176 uint64 tid = part->se_private_id();
1177 int ret = dd_table_load_part(tid, dd_table, part, table, thd, schema_name,
1178 implicit, space_id);
1179 if (ret != 0) {
1180 return ret;
1181 }
1182 dd_table_close(table, thd, nullptr, false);
1183 }
1184 return 0;
1185 } else {
1186 const dd::Partition *dd_part = nullptr;
1187 int ret = dd_table_load_part(table_id, dd_table, dd_part, table, thd,
1188 schema_name, implicit, space_id);
1189 if (ret != 0) {
1190 return ret;
1191 }
1192 dd_table_close(table, thd, nullptr, false);
1193 return ret;
1194 }
1195 }
1196
1197 /** Instantiate an InnoDB in-memory table metadata (dict_table_t)
1198 based on a Global DD object.
1199 @param[in,out] client data dictionary client
1200 @param[in] dd_table Global DD table object
1201 @param[in] dd_part Global DD partition or subpartition, or NULL
1202 @param[in] tbl_name table name, or NULL if not known
1203 @param[out] table InnoDB table (NULL if not found or loadable)
1204 @param[in] thd Thread THD
1205 @return error code
1206 @retval 0 on success (DD_SUCCESS) */
dd_table_open_on_dd_obj(dd::cache::Dictionary_client * client,const dd::Table & dd_table,const dd::Partition * dd_part,const char * tbl_name,dict_table_t * & table,THD * thd)1207 int dd_table_open_on_dd_obj(dd::cache::Dictionary_client *client,
1208 const dd::Table &dd_table,
1209 const dd::Partition *dd_part, const char *tbl_name,
1210 dict_table_t *&table, THD *thd) {
1211 #ifdef UNIV_DEBUG
1212 if (dd_part != nullptr) {
1213 ut_ad(&dd_part->table() == &dd_table);
1214 ut_ad(dd_table.se_private_id() == dd::INVALID_OBJECT_ID);
1215 ut_ad(dd_table_is_partitioned(dd_table));
1216
1217 ut_ad(dd_part->parent_partition_id() == dd::INVALID_OBJECT_ID ||
1218 dd_part->parent() != nullptr);
1219
1220 ut_ad(((dd_part->table().subpartition_type() != dd::Table::ST_NONE) ==
1221 (dd_part->parent() != nullptr)));
1222 }
1223 #endif /* UNIV_DEBUG */
1224
1225 int error = 0;
1226 const table_id_t table_id =
1227 dd_part == nullptr ? dd_table.se_private_id() : dd_part->se_private_id();
1228 const ulint fold = ut_fold_ull(table_id);
1229
1230 ut_ad(table_id != dd::INVALID_OBJECT_ID);
1231
1232 mutex_enter(&dict_sys->mutex);
1233
1234 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *, table,
1235 ut_ad(table->cached), table->id == table_id);
1236
1237 if (table != nullptr) {
1238 table->acquire();
1239 }
1240
1241 mutex_exit(&dict_sys->mutex);
1242
1243 if (table != nullptr) {
1244 return (0);
1245 }
1246
1247 #ifdef UNIV_DEBUG
1248 /* If this is a internal temporary table, it's impossible
1249 to verify the MDL against the table name, because both the
1250 database name and table name may be invalid for MDL */
1251 if (tbl_name && !row_is_mysql_tmp_table_name(tbl_name)) {
1252 std::string db_str;
1253 std::string tbl_str;
1254 dict_name::get_table(tbl_name, db_str, tbl_str);
1255
1256 ut_ad(innobase_strcasecmp(dd_table.name().c_str(), tbl_str.c_str()) == 0);
1257 }
1258 #endif /* UNIV_DEBUG */
1259
1260 TABLE_SHARE ts;
1261 dd::Schema *schema;
1262 const char *table_cache_key;
1263 size_t table_cache_key_len;
1264
1265 if (tbl_name != nullptr) {
1266 schema = nullptr;
1267 table_cache_key = tbl_name;
1268 table_cache_key_len = dict_get_db_name_len(tbl_name);
1269 } else {
1270 error = client->acquire_uncached<dd::Schema>(dd_table.schema_id(), &schema);
1271
1272 if (error != 0) {
1273 return (error);
1274 }
1275
1276 table_cache_key = schema->name().c_str();
1277 table_cache_key_len = schema->name().size();
1278 }
1279
1280 init_tmp_table_share(thd, &ts, table_cache_key, table_cache_key_len,
1281 dd_table.name().c_str(), "" /* file name */, nullptr);
1282
1283 error = open_table_def_suppress_invalid_meta_data(thd, &ts, dd_table);
1284
1285 if (error == 0) {
1286 TABLE td;
1287
1288 error = open_table_from_share(thd, &ts, dd_table.name().c_str(), 0,
1289 SKIP_NEW_HANDLER, 0, &td, false, &dd_table);
1290 if (error == 0) {
1291 char tmp_name[MAX_FULL_NAME_LEN + 1];
1292 const char *tab_namep;
1293
1294 if (tbl_name) {
1295 tab_namep = tbl_name;
1296 } else {
1297 char tmp_schema[MAX_DATABASE_NAME_LEN + 1];
1298 char tmp_tablename[MAX_TABLE_NAME_LEN + 1];
1299 tablename_to_filename(schema->name().c_str(), tmp_schema,
1300 MAX_DATABASE_NAME_LEN + 1);
1301 tablename_to_filename(dd_table.name().c_str(), tmp_tablename,
1302 MAX_TABLE_NAME_LEN + 1);
1303 snprintf(tmp_name, sizeof tmp_name, "%s/%s", tmp_schema, tmp_tablename);
1304 tab_namep = tmp_name;
1305 }
1306
1307 if (dd_part == nullptr) {
1308 table = dd_open_table(client, &td, tab_namep, &dd_table, thd);
1309
1310 if (table == nullptr) {
1311 error = HA_ERR_GENERIC;
1312 }
1313
1314 } else {
1315 table = dd_open_table(client, &td, tab_namep, dd_part, thd);
1316 }
1317
1318 closefrm(&td, false);
1319 }
1320 }
1321
1322 free_table_share(&ts);
1323
1324 return (error);
1325 }
1326
1327 /** Load an InnoDB table definition by InnoDB table ID.
1328 @param[in,out] thd current thread
1329 @param[in,out] mdl metadata lock;
1330 nullptr if we are resurrecting table IX locks in recovery
1331 @param[in] table_id InnoDB table or partition ID
1332 @return InnoDB table
1333 @retval nullptr if the table is not found, or there was an error */
dd_table_open_on_id_low(THD * thd,MDL_ticket ** mdl,table_id_t table_id)1334 static dict_table_t *dd_table_open_on_id_low(THD *thd, MDL_ticket **mdl,
1335 table_id_t table_id) {
1336 std::string part_table;
1337 const char *name_to_open = nullptr;
1338
1339 ut_ad(thd == nullptr || thd == current_thd);
1340 #ifdef UNIV_DEBUG
1341 btrsea_sync_check check(false);
1342 ut_ad(!sync_check_iterate(check));
1343 #endif
1344 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
1345
1346 if (thd == nullptr) {
1347 ut_ad(mdl == nullptr);
1348 thd = current_thd;
1349 }
1350
1351 /* During server startup, while recovering XA transaction we don't have THD.
1352 The table should have been already in innodb cache if present in DD while
1353 resurrecting transaction. We assume the table is not in DD and return. We
1354 cannot continue anyway here with NULL THD. */
1355 if (thd == nullptr) {
1356 return (nullptr);
1357 }
1358
1359 const dd::Table *dd_table;
1360 const dd::Partition *dd_part = nullptr;
1361 dd::cache::Dictionary_client *dc = dd::get_dd_client(thd);
1362 dd::cache::Dictionary_client::Auto_releaser releaser(dc);
1363
1364 /* Since we start with table se_private_id, and we do not have
1365 table name, so we cannot MDL lock the table(name). So we will
1366 try to get the table name without MDL protection, and verify later,
1367 after we got the table name and MDL lock it. Thus a loop is needed
1368 in case the verification failed, and another attempt is made until
1369 all things matches */
1370 for (;;) {
1371 dd::String_type schema;
1372 dd::String_type tablename;
1373 if (dc->get_table_name_by_se_private_id(handler_name, table_id, &schema,
1374 &tablename)) {
1375 return (nullptr);
1376 }
1377
1378 const bool not_table = schema.empty();
1379
1380 if (not_table) {
1381 if (dc->get_table_name_by_partition_se_private_id(handler_name, table_id,
1382 &schema, &tablename) ||
1383 schema.empty()) {
1384 return (nullptr);
1385 }
1386 }
1387
1388 /* Now we have tablename, and MDL locked it if necessary. */
1389 if (mdl != nullptr) {
1390 if (*mdl == nullptr &&
1391 dd_mdl_acquire(thd, mdl, schema.c_str(), tablename.c_str())) {
1392 return (nullptr);
1393 }
1394
1395 ut_ad(*mdl != nullptr);
1396 }
1397
1398 if (dc->acquire(schema, tablename, &dd_table) || dd_table == nullptr) {
1399 if (mdl != nullptr) {
1400 dd_mdl_release(thd, mdl);
1401 }
1402 return (nullptr);
1403 }
1404
1405 const bool is_part = dd_table_is_partitioned(*dd_table);
1406
1407 /* Verify facts between dd_table and facts we know
1408 1) Partiton table or not
1409 2) Table ID matches or not
1410 3) Table in InnoDB */
1411 bool same_name = not_table == is_part &&
1412 (not_table || dd_table->se_private_id() == table_id) &&
1413 dd_table->engine() == handler_name;
1414
1415 /* Do more verification for partition table */
1416 if (same_name && is_part) {
1417 auto end = dd_table->leaf_partitions().end();
1418 auto i =
1419 std::search_n(dd_table->leaf_partitions().begin(), end, 1, table_id,
1420 [](const dd::Partition *p, table_id_t id) {
1421 return (p->se_private_id() == id);
1422 });
1423
1424 if (i == end) {
1425 same_name = false;
1426 } else {
1427 dd_part = *i;
1428 ut_ad(dd_part_is_stored(dd_part));
1429
1430 std::string partition;
1431 /* Build the partition name. */
1432 dict_name::build_partition(dd_part, partition);
1433
1434 /* Build the partitioned table name. */
1435 dict_name::build_table(schema.c_str(), tablename.c_str(), partition,
1436 false, true, part_table);
1437 name_to_open = part_table.c_str();
1438 }
1439 }
1440
1441 /* facts do not match, retry */
1442 if (!same_name) {
1443 if (mdl != nullptr) {
1444 dd_mdl_release(thd, mdl);
1445 }
1446 continue;
1447 }
1448
1449 ut_ad(same_name);
1450 break;
1451 }
1452
1453 ut_ad(dd_part != nullptr || dd_table->se_private_id() == table_id);
1454 ut_ad(dd_part == nullptr || dd_table == &dd_part->table());
1455 ut_ad(dd_part == nullptr || dd_part->se_private_id() == table_id);
1456
1457 dict_table_t *ib_table = nullptr;
1458
1459 dd_table_open_on_dd_obj(dc, *dd_table, dd_part, name_to_open, ib_table, thd);
1460
1461 if (mdl && ib_table == nullptr) {
1462 dd_mdl_release(thd, mdl);
1463 }
1464
1465 return (ib_table);
1466 }
1467 #endif /* !UNIV_HOTBACKUP */
1468
1469 /** Check if access to a table should be refused.
1470 @param[in,out] table InnoDB table or partition
1471 @return error code
1472 @retval 0 on success (DD_SUVCCESS) */
dd_check_corrupted(dict_table_t * & table)1473 static MY_ATTRIBUTE((warn_unused_result)) int dd_check_corrupted(
1474 dict_table_t *&table) {
1475 if (table->is_corrupted()) {
1476 if (dict_table_is_sdi(table->id)
1477 #ifndef UNIV_HOTBACKUP
1478 || dict_table_is_system(table->id)
1479 #endif /* !UNIV_HOTBACKUP */
1480 ) {
1481 #ifndef UNIV_HOTBACKUP
1482 my_error(ER_TABLE_CORRUPT, MYF(0), "", table->name.m_name);
1483 #else /* !UNIV_HOTBACKUP */
1484 ib::fatal(ER_IB_MSG_168) << "table is corrupt: " << table->name.m_name;
1485 #endif /* !UNIV_HOTBACKUP */
1486 } else {
1487 #ifndef UNIV_HOTBACKUP
1488 std::string db_str;
1489 std::string tbl_str;
1490 dict_name::get_table(table->name.m_name, db_str, tbl_str);
1491
1492 my_error(ER_TABLE_CORRUPT, MYF(0), db_str.c_str(), tbl_str.c_str());
1493 #else /* !UNIV_HOTBACKUP */
1494 ib::fatal(ER_IB_MSG_169) << "table is corrupt: " << table->name.m_name;
1495 #endif /* !UNIV_HOTBACKUP */
1496 }
1497 table = nullptr;
1498 return (HA_ERR_TABLE_CORRUPT);
1499 }
1500
1501 dict_index_t *index = table->first_index();
1502 if (!dict_table_is_sdi(table->id) && fil_space_get(index->space) == nullptr) {
1503 #ifndef UNIV_HOTBACKUP
1504 my_error(ER_TABLESPACE_MISSING, MYF(0), table->name.m_name);
1505 #else /* !UNIV_HOTBACKUP */
1506 ib::fatal(ER_IB_MSG_170)
1507 << "table space is missing: " << table->name.m_name;
1508 #endif /* !UNIV_HOTBACKUP */
1509 table = nullptr;
1510 return (HA_ERR_TABLESPACE_MISSING);
1511 }
1512
1513 /* Ignore missing tablespaces for secondary indexes. */
1514 while ((index = index->next())) {
1515 if (!index->is_corrupted() && fil_space_get(index->space) == nullptr) {
1516 dict_set_corrupted(index);
1517 }
1518 }
1519
1520 return (0);
1521 }
1522
1523 /** Open a persistent InnoDB table based on InnoDB table id, and
1524 hold Shared MDL lock on it.
1525 @param[in] table_id table identifier
1526 @param[in,out] thd current MySQL connection (for mdl)
1527 @param[in,out] mdl metadata lock (*mdl set if
1528 table_id was found)
1529 @param[in] dict_locked dict_sys mutex is held
1530 @param[in] check_corruption check if the table is corrupted or not.
1531 mdl=NULL if we are resurrecting table IX locks in recovery
1532 @return table
1533 @retval NULL if the table does not exist or cannot be opened */
dd_table_open_on_id(table_id_t table_id,THD * thd,MDL_ticket ** mdl,bool dict_locked,bool check_corruption,bool skip_missing)1534 dict_table_t *dd_table_open_on_id(table_id_t table_id, THD *thd,
1535 MDL_ticket **mdl, bool dict_locked,
1536 bool check_corruption, bool skip_missing) {
1537 dict_table_t *ib_table;
1538 const ulint fold = ut_fold_ull(table_id);
1539 char full_name[MAX_FULL_NAME_LEN + 1];
1540
1541 if (!dict_locked) {
1542 mutex_enter(&dict_sys->mutex);
1543 }
1544
1545 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *, ib_table,
1546 ut_ad(ib_table->cached), ib_table->id == table_id);
1547
1548 reopen:
1549 if (ib_table == nullptr) {
1550 if (skip_missing) {
1551 return nullptr;
1552 }
1553 #ifndef UNIV_HOTBACKUP
1554 if (dict_table_is_sdi(table_id)) {
1555 /* The table is SDI table */
1556 space_id_t space_id = dict_sdi_get_space_id(table_id);
1557
1558 /* Create in-memory table oject for SDI table */
1559 dict_index_t *sdi_index =
1560 dict_sdi_create_idx_in_mem(space_id, false, 0, false);
1561
1562 if (sdi_index == nullptr) {
1563 if (!dict_locked) {
1564 mutex_exit(&dict_sys->mutex);
1565 }
1566 return (nullptr);
1567 }
1568
1569 ib_table = sdi_index->table;
1570
1571 ut_ad(ib_table != nullptr);
1572 ib_table->acquire();
1573
1574 if (!dict_locked) {
1575 mutex_exit(&dict_sys->mutex);
1576 }
1577 } else {
1578 if (!dict_locked) {
1579 mutex_exit(&dict_sys->mutex);
1580 }
1581 ib_table = nullptr;
1582 }
1583 #else /* !UNIV_HOTBACKUP */
1584 /* PRELIMINARY TEMPORARY WORKAROUND: is this ever used? */
1585 bool not_hotbackup = false;
1586 ut_a(not_hotbackup);
1587 #endif /* !UNIV_HOTBACKUP */
1588 } else if (mdl == nullptr || ib_table->is_temporary() ||
1589 dict_table_is_sdi(ib_table->id)) {
1590 if (dd_check_corrupted(ib_table)) {
1591 ut_ad(ib_table == nullptr);
1592 } else {
1593 ib_table->acquire();
1594 }
1595
1596 if (!dict_locked) {
1597 mutex_exit(&dict_sys->mutex);
1598 }
1599 } else {
1600 for (;;) {
1601 #ifndef UNIV_HOTBACKUP
1602 std::string db_str;
1603 std::string tbl_str;
1604 dict_name::get_table(ib_table->name.m_name, db_str, tbl_str);
1605 #endif /* !UNIV_HOTBACKUP */
1606
1607 memset(full_name, 0, MAX_FULL_NAME_LEN + 1);
1608
1609 strcpy(full_name, ib_table->name.m_name);
1610
1611 ut_ad(!ib_table->is_temporary());
1612
1613 mutex_exit(&dict_sys->mutex);
1614
1615 #ifndef UNIV_HOTBACKUP
1616 if (db_str.empty() || tbl_str.empty()) {
1617 if (dict_locked) {
1618 mutex_enter(&dict_sys->mutex);
1619 }
1620 return (nullptr);
1621 }
1622
1623 if (dd_mdl_acquire(thd, mdl, db_str.c_str(), tbl_str.c_str())) {
1624 if (dict_locked) {
1625 mutex_enter(&dict_sys->mutex);
1626 }
1627 return (nullptr);
1628 }
1629 #endif /* !UNIV_HOTBACKUP */
1630
1631 /* Re-lookup the table after acquiring MDL. */
1632 mutex_enter(&dict_sys->mutex);
1633
1634 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *,
1635 ib_table, ut_ad(ib_table->cached), ib_table->id == table_id);
1636
1637 if (ib_table != nullptr) {
1638 ulint namelen = strlen(ib_table->name.m_name);
1639
1640 /* The table could have been renamed. After
1641 we release dict mutex before the old table
1642 name is MDL locked. So we need to go back
1643 to MDL lock the new name. */
1644 if (namelen != strlen(full_name) ||
1645 memcmp(ib_table->name.m_name, full_name, namelen)) {
1646 #ifndef UNIV_HOTBACKUP
1647 dd_mdl_release(thd, mdl);
1648 #endif /* !UNIV_HOTBACKUP */
1649 continue;
1650 } else if (check_corruption && dd_check_corrupted(ib_table)) {
1651 ut_ad(ib_table == nullptr);
1652 } else if (ib_table->discard_after_ddl) {
1653 #ifndef UNIV_HOTBACKUP
1654 btr_drop_ahi_for_table(ib_table);
1655 dict_table_remove_from_cache(ib_table);
1656 #endif /* !UNIV_HOTBACKUP */
1657 ib_table = nullptr;
1658 #ifndef UNIV_HOTBACKUP
1659 dd_mdl_release(thd, mdl);
1660 #endif /* !UNIV_HOTBACKUP */
1661 goto reopen;
1662 } else {
1663 ib_table->acquire_with_lock();
1664 }
1665 }
1666
1667 mutex_exit(&dict_sys->mutex);
1668 break;
1669 }
1670
1671 #ifndef UNIV_HOTBACKUP
1672 ut_ad(*mdl != nullptr);
1673
1674 /* Now the table can't be found, release MDL,
1675 let dd_table_open_on_id_low() do the lock, as table
1676 name could be changed */
1677 if (ib_table == nullptr) {
1678 dd_mdl_release(thd, mdl);
1679 ib_table = dd_table_open_on_id_low(thd, mdl, table_id);
1680
1681 if (ib_table == nullptr && *mdl != nullptr) {
1682 dd_mdl_release(thd, mdl);
1683 }
1684 }
1685 #else /* !UNIV_HOTBACKUP */
1686 /* PRELIMINARY TEMPORARY WORKAROUND: is this ever used? */
1687 bool not_hotbackup = false;
1688 ut_a(not_hotbackup);
1689 #endif /* !UNIV_HOTBACKUP */
1690
1691 if (dict_locked) {
1692 mutex_enter(&dict_sys->mutex);
1693 }
1694 }
1695
1696 ut_ad(dict_locked == mutex_own(&dict_sys->mutex));
1697
1698 return (ib_table);
1699 }
1700
1701 #ifndef UNIV_HOTBACKUP
1702 /** Set the discard flag for a non-partitioned dd table.
1703 @param[in,out] thd current thread
1704 @param[in] table InnoDB table
1705 @param[in,out] table_def MySQL dd::Table to update
1706 @param[in] discard discard flag
1707 @return true if success
1708 @retval false if fail. */
dd_table_discard_tablespace(THD * thd,const dict_table_t * table,dd::Table * table_def,bool discard)1709 bool dd_table_discard_tablespace(THD *thd, const dict_table_t *table,
1710 dd::Table *table_def, bool discard) {
1711 bool ret = false;
1712
1713 DBUG_TRACE;
1714
1715 ut_ad(thd == current_thd);
1716 #ifdef UNIV_DEBUG
1717 btrsea_sync_check check(false);
1718 ut_ad(!sync_check_iterate(check));
1719 #endif /* UNIV_DEBUG */
1720
1721 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
1722
1723 if (table_def->se_private_id() != dd::INVALID_OBJECT_ID) {
1724 ut_ad(table_def->table().leaf_partitions()->empty());
1725
1726 /* For discarding, we need to set new private
1727 id to dd_table */
1728 if (discard) {
1729 /* Set the new private id to dd_table object. */
1730 table_def->set_se_private_id(table->id);
1731 } else {
1732 ut_ad(table_def->se_private_id() == table->id);
1733 }
1734
1735 /* Set index root page. */
1736 for (auto dd_index : *table_def->indexes()) {
1737 const dict_index_t *index = dd_find_index(table, dd_index);
1738 ut_ad(index != nullptr);
1739
1740 dd::Properties &p = dd_index->se_private_data();
1741 p.set(dd_index_key_strings[DD_INDEX_ROOT], index->page);
1742 }
1743
1744 /* Set new table id for dd columns when it's importing
1745 tablespace. */
1746 if (!discard) {
1747 for (auto dd_column : *table_def->columns()) {
1748 dd_column->se_private_data().set(dd_index_key_strings[DD_TABLE_ID],
1749 table->id);
1750 }
1751 }
1752
1753 /* Set discard flag. */
1754 dd_set_discarded(*table_def, discard);
1755
1756 using Client = dd::cache::Dictionary_client;
1757 using Releaser = dd::cache::Dictionary_client::Auto_releaser;
1758
1759 /* Get Tablespace object */
1760 dd::Tablespace *dd_space = nullptr;
1761 Client *client = dd::get_dd_client(thd);
1762 Releaser releaser{client};
1763
1764 dd::Object_id dd_space_id =
1765 (*table_def->indexes()->begin())->tablespace_id();
1766
1767 std::string space_name(table->name.m_name);
1768
1769 dict_name::convert_to_space(space_name);
1770
1771 if (dd_tablespace_get_mdl(space_name.c_str())) {
1772 ut_a(false);
1773 }
1774
1775 if (client->acquire_for_modification(dd_space_id, &dd_space)) {
1776 ut_a(false);
1777 }
1778
1779 ut_a(dd_space != nullptr);
1780
1781 dd_tablespace_set_state(
1782 dd_space, discard ? DD_SPACE_STATE_DISCARDED : DD_SPACE_STATE_NORMAL);
1783
1784 if (client->update(dd_space)) {
1785 ut_ad(0);
1786 }
1787 ret = true;
1788 } else {
1789 ret = false;
1790 }
1791
1792 return ret;
1793 }
1794
1795 /** Open an internal handle to a persistent InnoDB table by name.
1796 @param[in,out] thd current thread
1797 @param[out] mdl metadata lock
1798 @param[in] name InnoDB table name
1799 @param[in] dict_locked has dict_sys mutex locked
1800 @param[in] ignore_err whether to ignore err
1801 @param[out] error pointer to error
1802 @return handle to non-partitioned table
1803 @retval NULL if the table does not exist */
dd_table_open_on_name(THD * thd,MDL_ticket ** mdl,const char * name,bool dict_locked,ulint ignore_err,int * error)1804 dict_table_t *dd_table_open_on_name(THD *thd, MDL_ticket **mdl,
1805 const char *name, bool dict_locked,
1806 ulint ignore_err, int *error) {
1807 DBUG_TRACE;
1808
1809 #ifdef UNIV_DEBUG
1810 btrsea_sync_check check(false);
1811 ut_ad(!sync_check_iterate(check));
1812 #endif
1813 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
1814
1815 dict_table_t *table = nullptr;
1816
1817 /* Get pointer to a table object in InnoDB dictionary cache.
1818 For intrinsic table, get it from session private data */
1819 if (thd) {
1820 // table = thd_to_innodb_session(thd)->lookup_table_handler(name);
1821 }
1822
1823 if (table != nullptr) {
1824 table->acquire();
1825 return table;
1826 }
1827
1828 std::string db_name;
1829 std::string tbl_name;
1830 dict_name::get_table(name, db_name, tbl_name);
1831
1832 if (db_name.empty() || tbl_name.empty()) {
1833 return nullptr;
1834 }
1835
1836 bool skip_mdl = !(thd && mdl);
1837
1838 if (!skip_mdl) {
1839 if (dict_locked) {
1840 /* We cannot acquire MDL while holding dict_sys->mutex. The reason that
1841 the caller has already locked this mutex is so that the dict_table_t
1842 that we will find and return to it will not be dropped while the caller
1843 is using it. So it is safe to exit, get the mdl and enter again before
1844 finding this dict_table_t. */
1845 mutex_exit(&dict_sys->mutex);
1846 }
1847
1848 bool got_mdl = dd_mdl_acquire(thd, mdl, db_name.c_str(), tbl_name.c_str());
1849
1850 if (dict_locked) {
1851 mutex_enter(&dict_sys->mutex);
1852 }
1853
1854 if (got_mdl) {
1855 return nullptr;
1856 }
1857 }
1858
1859 if (!dict_locked) {
1860 mutex_enter(&dict_sys->mutex);
1861 }
1862
1863 table = dict_table_check_if_in_cache_low(name);
1864
1865 if (table != nullptr) {
1866 table->acquire_with_lock();
1867 if (!dict_locked) {
1868 mutex_exit(&dict_sys->mutex);
1869 }
1870 return table;
1871 }
1872
1873 mutex_exit(&dict_sys->mutex);
1874
1875 const dd::Table *dd_table = nullptr;
1876 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
1877 dd::cache::Dictionary_client::Auto_releaser releaser(client);
1878
1879 if (client->acquire(db_name.c_str(), tbl_name.c_str(), &dd_table) ||
1880 dd_table == nullptr || dd_table->engine() != innobase_hton_name) {
1881 /* The checking for engine should be only useful(valid)
1882 for getting table statistics for IS. Two relevant API
1883 functions are:
1884 1. innobase_get_table_statistics
1885 2. innobase_get_index_column_cardinality */
1886 table = nullptr;
1887 } else {
1888 if (dd_table->se_private_id() == dd::INVALID_OBJECT_ID) {
1889 ut_ad(!dd_table->leaf_partitions().empty());
1890
1891 if (dict_name::is_partition(name)) {
1892 const dd::Partition *dd_part = nullptr;
1893
1894 for (auto part : dd_table->leaf_partitions()) {
1895 if (dict_name::match_partition(name, part)) {
1896 dd_part = part;
1897 break;
1898 }
1899 }
1900
1901 /* Safe check for release mode. */
1902 if (dd_part == nullptr) {
1903 ut_ad(false);
1904 table = nullptr;
1905 } else {
1906 dd_table_open_on_dd_obj(client, *dd_table, dd_part, name, table, thd);
1907 }
1908
1909 } else {
1910 /* FIXME: Once FK functions will not open
1911 partitioned table in current improper way,
1912 just assert this false */
1913 table = nullptr;
1914 }
1915 } else {
1916 ut_ad(dd_table->leaf_partitions().empty());
1917 int err =
1918 dd_table_open_on_dd_obj(client, *dd_table, nullptr, name, table, thd);
1919 if (error) {
1920 *error = err;
1921 }
1922 }
1923 }
1924
1925 if (table && table->is_corrupted() &&
1926 !(ignore_err & DICT_ERR_IGNORE_CORRUPT)) {
1927 mutex_enter(&dict_sys->mutex);
1928 table->release();
1929 dict_table_remove_from_cache(table);
1930 table = nullptr;
1931 mutex_exit(&dict_sys->mutex);
1932 }
1933
1934 if (table == nullptr && mdl) {
1935 dd_mdl_release(thd, mdl);
1936 *mdl = nullptr;
1937 }
1938
1939 if (dict_locked) {
1940 mutex_enter(&dict_sys->mutex);
1941 }
1942
1943 return table;
1944 }
1945 #endif /* !UNIV_HOTBACKUP */
1946
1947 /** Close an internal InnoDB table handle.
1948 @param[in,out] table InnoDB table handle
1949 @param[in,out] thd current MySQL connection (for mdl)
1950 @param[in,out] mdl metadata lock (will be set NULL)
1951 @param[in] dict_locked whether we hold dict_sys mutex */
dd_table_close(dict_table_t * table,THD * thd,MDL_ticket ** mdl,bool dict_locked)1952 void dd_table_close(dict_table_t *table, THD *thd, MDL_ticket **mdl,
1953 bool dict_locked) {
1954 dict_table_close(table, dict_locked, false);
1955
1956 #ifndef UNIV_HOTBACKUP
1957 if (mdl != nullptr && *mdl != nullptr) {
1958 ut_ad(!table->is_temporary());
1959 dd_mdl_release(thd, mdl);
1960 }
1961 #endif /* !UNIV_HOTBACKUP */
1962 }
1963
1964 #ifndef UNIV_HOTBACKUP
1965 /** Replace the tablespace name in the file name.
1966 @param[in] dd_file the tablespace file object.
1967 @param[in] new_space_name new table space name to be updated in file name.
1968 It must have already been converted to the
1969 filename_charset such that
1970 `d1/d2\d3`.`t3\t4/t5`
1971 should look like:
1972 d1@002fd2@005cd3/t3@005ct4@002ft5
1973 both on Windows and on Linux. */
replace_space_name_in_file_name(dd::Tablespace_file * dd_file,dd::String_type new_space_name)1974 static void replace_space_name_in_file_name(dd::Tablespace_file *dd_file,
1975 dd::String_type new_space_name) {
1976 ut_ad(std::count(new_space_name.begin(), new_space_name.end(),
1977 Fil_path::DB_SEPARATOR) == 1);
1978
1979 /* Obtain the old tablespace file name. */
1980 dd::String_type old_file_name = dd_file->filename();
1981
1982 /* We assume that old_file_name ends with:
1983 OS_PATH_SEPARATOR + db_name + OS_PATH_SEPARATOR + table_name + dot_ext[IBD],
1984 so on Windows it can look like:
1985 .\d1@002fd2@005cd3\t1@002ft2@005ct3.ibd
1986 and on Linux it could be:
1987 ./d1@002fd2@005cd3/t1@002ft2@005ct3.ibd */
1988 ut_ad(std::count(old_file_name.begin(), old_file_name.end(),
1989 OS_PATH_SEPARATOR) >= 2);
1990 ut_ad(old_file_name.rfind(dot_ext[IBD]) ==
1991 old_file_name.length() - strlen(dot_ext[IBD]));
1992
1993 /* Strip the last two components of the path (keep the slash) */
1994 auto last_separator_pos = old_file_name.find_last_of(OS_PATH_SEPARATOR);
1995 auto previous_separator_pos =
1996 old_file_name.find_last_of(OS_PATH_SEPARATOR, last_separator_pos - 1);
1997 old_file_name.resize(previous_separator_pos + 1);
1998
1999 /* Take care of path separators */
2000 std::replace(new_space_name.begin(), new_space_name.end(),
2001 Fil_path::DB_SEPARATOR, OS_PATH_SEPARATOR);
2002
2003 old_file_name += new_space_name + dot_ext[IBD];
2004
2005 /* Update the file name path */
2006 dd_file->set_filename(old_file_name);
2007 }
2008
dd_tablespace_rename(dd::Object_id dd_space_id,bool is_system_cs,const char * new_space_name,const char * new_path)2009 dberr_t dd_tablespace_rename(dd::Object_id dd_space_id, bool is_system_cs,
2010 const char *new_space_name, const char *new_path) {
2011 THD *thd = current_thd;
2012
2013 DBUG_TRACE;
2014 #ifdef UNIV_DEBUG
2015 btrsea_sync_check check(false);
2016 ut_ad(!sync_check_iterate(check));
2017 #endif /* UNIV_DEBUG */
2018 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
2019
2020 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
2021 dd::cache::Dictionary_client::Auto_releaser releaser(client);
2022
2023 dd::Tablespace *dd_space = nullptr;
2024
2025 /* Get the dd tablespace */
2026 if (client->acquire_uncached_uncommitted<dd::Tablespace>(dd_space_id,
2027 &dd_space) ||
2028 dd_space == nullptr) {
2029 ut_ad(false);
2030 return DB_ERROR;
2031 }
2032
2033 MDL_ticket *src_ticket = nullptr;
2034 if (dd_tablespace_get_mdl(dd_space->name().c_str(), &src_ticket)) {
2035 ut_ad(false);
2036 return DB_ERROR;
2037 }
2038
2039 std::string tablespace_name(new_space_name);
2040 /* Convert if not in system character set. */
2041 if (!is_system_cs) {
2042 dict_name::convert_to_space(tablespace_name);
2043 }
2044
2045 MDL_ticket *dst_ticket = nullptr;
2046 if (dd_tablespace_get_mdl(tablespace_name.c_str(), &dst_ticket)) {
2047 ut_ad(false);
2048 return DB_ERROR;
2049 }
2050
2051 dd::Tablespace *new_space = nullptr;
2052
2053 /* Acquire the new dd tablespace for modification */
2054 if (client->acquire_for_modification<dd::Tablespace>(dd_space_id,
2055 &new_space)) {
2056 ut_ad(false);
2057 return DB_ERROR;
2058 }
2059
2060 ut_ad(new_space->files().size() == 1);
2061
2062 dd::String_type old_space_name = new_space->name();
2063
2064 new_space->set_name(tablespace_name.c_str());
2065
2066 dd::Tablespace_file *dd_file =
2067 const_cast<dd::Tablespace_file *>(*(new_space->files().begin()));
2068
2069 if (new_path != nullptr) {
2070 dd_file->set_filename(new_path);
2071
2072 } else {
2073 replace_space_name_in_file_name(dd_file, new_space_name);
2074 ut_ad(dd_tablespace_get_state_enum(dd_space) == DD_SPACE_STATE_DISCARDED);
2075 }
2076
2077 bool fail = client->update(new_space);
2078 ut_ad(!fail);
2079 dd::rename_tablespace_mdl_hook(thd, src_ticket, dst_ticket);
2080
2081 return fail ? DB_ERROR : DB_SUCCESS;
2082 }
2083
2084 /** Validate the table format options.
2085 @param[in] thd THD instance
2086 @param[in] form MySQL table definition
2087 @param[in] real_type real row type if it's not ROW_TYPE_NOT_USED
2088 @param[in] zip_allowed whether ROW_FORMAT=COMPRESSED is OK
2089 @param[in] strict whether innodb_strict_mode=ON
2090 @param[out] is_redundant whether ROW_FORMAT=REDUNDANT
2091 @param[out] blob_prefix whether ROW_FORMAT=DYNAMIC
2092 or ROW_FORMAT=COMPRESSED
2093 @param[out] zip_ssize log2(compressed page size),
2094 or 0 if not ROW_FORMAT=COMPRESSED
2095 @param[out] is_implicit if tablespace is implicit
2096 @retval true if invalid (my_error will have been called)
2097 @retval false if valid */
format_validate(THD * thd,const TABLE * form,row_type real_type,bool zip_allowed,bool strict,bool * is_redundant,bool * blob_prefix,ulint * zip_ssize,bool is_implicit)2098 static bool format_validate(THD *thd, const TABLE *form, row_type real_type,
2099 bool zip_allowed, bool strict, bool *is_redundant,
2100 bool *blob_prefix, ulint *zip_ssize,
2101 bool is_implicit) {
2102 bool is_temporary = false;
2103 ut_ad(thd != nullptr);
2104 ut_ad(!zip_allowed || srv_page_size <= UNIV_ZIP_SIZE_MAX);
2105
2106 /* 1+log2(compressed_page_size), or 0 if not compressed */
2107 *zip_ssize = 0;
2108 const ulint zip_ssize_max =
2109 std::min((ulint)UNIV_PAGE_SSIZE_MAX, (ulint)PAGE_ZIP_SSIZE_MAX);
2110 const char *zip_refused = zip_allowed ? nullptr
2111 : srv_page_size <= UNIV_ZIP_SIZE_MAX
2112 ? "innodb_file_per_table=OFF"
2113 : "innodb_page_size>16k";
2114 bool invalid = false;
2115
2116 if (real_type == ROW_TYPE_NOT_USED) {
2117 real_type = form->s->real_row_type;
2118 }
2119
2120 if (auto key_block_size = form->s->key_block_size) {
2121 unsigned valid_zssize = 0;
2122 char kbs[MY_INT32_NUM_DECIMAL_DIGITS + sizeof "KEY_BLOCK_SIZE=" + 1];
2123 snprintf(kbs, sizeof kbs, "KEY_BLOCK_SIZE=%u", key_block_size);
2124 for (unsigned kbsize = 1, zssize = 1; zssize <= zip_ssize_max;
2125 zssize++, kbsize <<= 1) {
2126 if (kbsize == key_block_size) {
2127 valid_zssize = zssize;
2128 break;
2129 }
2130 }
2131
2132 if (valid_zssize == 0) {
2133 if (strict) {
2134 my_error(ER_WRONG_VALUE, MYF(0), "KEY_BLOCK_SIZE",
2135 kbs + sizeof "KEY_BLOCK_SIZE");
2136 invalid = true;
2137 } else {
2138 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_WRONG_VALUE,
2139 ER_DEFAULT(ER_WRONG_VALUE), "KEY_BLOCK_SIZE",
2140 kbs + sizeof "KEY_BLOCK_SIZE");
2141 }
2142 } else if (!zip_allowed) {
2143 int error = is_temporary ? ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE
2144 : ER_ILLEGAL_HA_CREATE_OPTION;
2145
2146 if (strict) {
2147 my_error(error, MYF(0), innobase_hton_name, kbs, zip_refused);
2148 invalid = true;
2149 } else {
2150 push_warning_printf(thd, Sql_condition::SL_WARNING, error,
2151 ER_DEFAULT_NONCONST(error), innobase_hton_name, kbs,
2152 zip_refused);
2153 }
2154 } else if (real_type != ROW_TYPE_COMPRESSED) {
2155 /* This could happen when
2156 1. There was an ALTER TABLE ... COPY to move
2157 the table from COMPRESSED into DYNAMIC, etc.
2158 2. For partitioned table, some partitions of which
2159 could be of different row format from the specified
2160 one */
2161 } else if (form->s->row_type == ROW_TYPE_DEFAULT ||
2162 form->s->row_type == ROW_TYPE_COMPRESSED) {
2163 ut_ad(real_type == ROW_TYPE_COMPRESSED);
2164 *zip_ssize = valid_zssize;
2165 } else {
2166 int error = is_temporary ? ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE
2167 : ER_ILLEGAL_HA_CREATE_OPTION;
2168 const char *conflict = get_row_format_name(form->s->row_type);
2169
2170 if (strict) {
2171 my_error(error, MYF(0), innobase_hton_name, kbs, conflict);
2172 invalid = true;
2173 } else {
2174 push_warning_printf(thd, Sql_condition::SL_WARNING, error,
2175 ER_DEFAULT_NONCONST(error), innobase_hton_name, kbs,
2176 conflict);
2177 }
2178 }
2179 } else if (form->s->row_type != ROW_TYPE_COMPRESSED || !is_temporary) {
2180 /* not ROW_FORMAT=COMPRESSED (nor KEY_BLOCK_SIZE),
2181 or not TEMPORARY TABLE */
2182 } else if (strict) {
2183 my_error(ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE, MYF(0));
2184 invalid = true;
2185 } else {
2186 push_warning(thd, Sql_condition::SL_WARNING,
2187 ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE,
2188 ER_THD(thd, ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE));
2189 }
2190
2191 /* Check for a valid InnoDB ROW_FORMAT specifier and
2192 other incompatibilities. */
2193 rec_format_t innodb_row_format = REC_FORMAT_DYNAMIC;
2194
2195 switch (form->s->row_type) {
2196 case ROW_TYPE_DYNAMIC:
2197 ut_ad(*zip_ssize == 0);
2198 /* If non strict_mode, row type can be converted between
2199 COMPRESSED and DYNAMIC */
2200 ut_ad(real_type == ROW_TYPE_DYNAMIC || real_type == ROW_TYPE_COMPRESSED);
2201 break;
2202 case ROW_TYPE_COMPACT:
2203 ut_ad(*zip_ssize == 0);
2204 ut_ad(real_type == ROW_TYPE_COMPACT);
2205 innodb_row_format = REC_FORMAT_COMPACT;
2206 break;
2207 case ROW_TYPE_REDUNDANT:
2208 ut_ad(*zip_ssize == 0);
2209 ut_ad(real_type == ROW_TYPE_REDUNDANT);
2210 innodb_row_format = REC_FORMAT_REDUNDANT;
2211 break;
2212 case ROW_TYPE_FIXED:
2213 case ROW_TYPE_PAGED:
2214 case ROW_TYPE_NOT_USED: {
2215 const char *name = get_row_format_name(form->s->row_type);
2216 if (strict) {
2217 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), innobase_hton_name, name);
2218 invalid = true;
2219 } else {
2220 push_warning_printf(
2221 thd, Sql_condition::SL_WARNING, ER_ILLEGAL_HA_CREATE_OPTION,
2222 ER_DEFAULT(ER_ILLEGAL_HA_CREATE_OPTION), innobase_hton_name, name);
2223 }
2224 }
2225 /* fall through */
2226 case ROW_TYPE_DEFAULT:
2227 switch (real_type) {
2228 case ROW_TYPE_FIXED:
2229 case ROW_TYPE_PAGED:
2230 case ROW_TYPE_NOT_USED:
2231 case ROW_TYPE_DEFAULT:
2232 /* get_real_row_type() should not return these */
2233 ut_ad(0);
2234 /* fall through */
2235 case ROW_TYPE_DYNAMIC:
2236 ut_ad(*zip_ssize == 0);
2237 break;
2238 case ROW_TYPE_COMPACT:
2239 ut_ad(*zip_ssize == 0);
2240 innodb_row_format = REC_FORMAT_COMPACT;
2241 break;
2242 case ROW_TYPE_REDUNDANT:
2243 ut_ad(*zip_ssize == 0);
2244 innodb_row_format = REC_FORMAT_REDUNDANT;
2245 break;
2246 case ROW_TYPE_COMPRESSED:
2247 innodb_row_format = REC_FORMAT_COMPRESSED;
2248 break;
2249 }
2250
2251 if (*zip_ssize == 0) {
2252 /* No valid KEY_BLOCK_SIZE was specified,
2253 so do not imply ROW_FORMAT=COMPRESSED. */
2254 if (innodb_row_format == REC_FORMAT_COMPRESSED) {
2255 innodb_row_format = REC_FORMAT_DYNAMIC;
2256 }
2257 break;
2258 }
2259 /* fall through */
2260 case ROW_TYPE_COMPRESSED:
2261 if (is_temporary) {
2262 if (strict) {
2263 invalid = true;
2264 }
2265 /* ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE
2266 was already reported. */
2267 ut_ad(real_type == ROW_TYPE_DYNAMIC);
2268 break;
2269 } else if (zip_allowed && real_type == ROW_TYPE_COMPRESSED) {
2270 /* ROW_FORMAT=COMPRESSED without KEY_BLOCK_SIZE
2271 implies half the maximum compressed page size. */
2272 if (*zip_ssize == 0) {
2273 *zip_ssize = zip_ssize_max - 1;
2274 }
2275 innodb_row_format = REC_FORMAT_COMPRESSED;
2276 break;
2277 }
2278
2279 if (strict) {
2280 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), innobase_hton_name,
2281 "ROW_FORMAT=COMPRESSED", zip_refused);
2282 invalid = true;
2283 }
2284 }
2285
2286 if (const char *algorithm =
2287 form->s->compress.length > 0 ? form->s->compress.str : nullptr) {
2288 Compression compression;
2289 dberr_t err = Compression::check(algorithm, &compression);
2290
2291 if (err == DB_UNSUPPORTED) {
2292 if (strict) {
2293 my_error(ER_WRONG_VALUE, MYF(0), "COMPRESSION", algorithm);
2294 invalid = true;
2295 } else {
2296 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_WRONG_VALUE,
2297 ER_DEFAULT(ER_WRONG_VALUE), "COMPRESSION",
2298 algorithm);
2299 }
2300 } else if (compression.m_type != Compression::NONE) {
2301 if (*zip_ssize != 0) {
2302 if (strict) {
2303 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), innobase_hton_name,
2304 "COMPRESSION",
2305 form->s->key_block_size ? "KEY_BLOCK_SIZE"
2306 : "ROW_FORMAT=COMPRESSED");
2307 invalid = true;
2308 }
2309 }
2310
2311 if (is_temporary) {
2312 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), innobase_hton_name,
2313 "COMPRESSION", "TEMPORARY");
2314 invalid = true;
2315 } else if (!is_implicit && strict) {
2316 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), innobase_hton_name,
2317 "COMPRESSION", "TABLESPACE");
2318 invalid = true;
2319 }
2320 }
2321 }
2322
2323 /* Check if there are any FTS indexes defined on this table. */
2324 for (uint i = 0; i < form->s->keys; i++) {
2325 const KEY *key = &form->key_info[i];
2326
2327 if ((key->flags & HA_FULLTEXT) && is_temporary) {
2328 /* We don't support FTS indexes in temporary
2329 tables. */
2330 my_error(ER_INNODB_NO_FT_TEMP_TABLE, MYF(0));
2331 return (true);
2332 }
2333 }
2334
2335 ut_ad((*zip_ssize == 0) == (innodb_row_format != REC_FORMAT_COMPRESSED));
2336
2337 *is_redundant = false;
2338 *blob_prefix = false;
2339
2340 switch (innodb_row_format) {
2341 case REC_FORMAT_REDUNDANT:
2342 *is_redundant = true;
2343 *blob_prefix = true;
2344 break;
2345 case REC_FORMAT_COMPACT:
2346 *blob_prefix = true;
2347 break;
2348 case REC_FORMAT_COMPRESSED:
2349 ut_ad(!is_temporary);
2350 break;
2351 case REC_FORMAT_DYNAMIC:
2352 break;
2353 }
2354
2355 return (invalid);
2356 }
2357
2358 /** Set the AUTO_INCREMENT attribute.
2359 @param[in,out] se_private_data dd::Table::se_private_data
2360 @param[in] autoinc the auto-increment value */
dd_set_autoinc(dd::Properties & se_private_data,uint64 autoinc)2361 void dd_set_autoinc(dd::Properties &se_private_data, uint64 autoinc) {
2362 /* The value of "autoinc" here is the AUTO_INCREMENT attribute
2363 specified at table creation. AUTO_INCREMENT=0 will silently
2364 be treated as AUTO_INCREMENT=1. Likewise, if no AUTO_INCREMENT
2365 attribute was specified, the value would be 0. */
2366
2367 if (autoinc > 0) {
2368 /* InnoDB persists the "previous" AUTO_INCREMENT value. */
2369 autoinc--;
2370 }
2371
2372 uint64 version = 0;
2373
2374 if (se_private_data.exists(dd_table_key_strings[DD_TABLE_AUTOINC])) {
2375 /* Increment the dynamic metadata version, so that
2376 any previously buffered persistent dynamic metadata
2377 will be ignored after this transaction commits. */
2378
2379 if (!se_private_data.get(dd_table_key_strings[DD_TABLE_VERSION],
2380 &version)) {
2381 version++;
2382 } else {
2383 /* incomplete se_private_data */
2384 ut_ad(false);
2385 }
2386 }
2387
2388 se_private_data.set(dd_table_key_strings[DD_TABLE_VERSION], version);
2389 se_private_data.set(dd_table_key_strings[DD_TABLE_AUTOINC], autoinc);
2390 }
2391
2392 /** Copy the AUTO_INCREMENT and version attribute if exist.
2393 @param[in] src dd::Table::se_private_data to copy from
2394 @param[out] dest dd::Table::se_private_data to copy to */
dd_copy_autoinc(const dd::Properties & src,dd::Properties & dest)2395 void dd_copy_autoinc(const dd::Properties &src, dd::Properties &dest) {
2396 uint64_t autoinc = 0;
2397 uint64_t version = 0;
2398
2399 if (!src.exists(dd_table_key_strings[DD_TABLE_AUTOINC])) {
2400 return;
2401 }
2402
2403 if (src.get(dd_table_key_strings[DD_TABLE_AUTOINC],
2404 reinterpret_cast<uint64 *>(&autoinc)) ||
2405 src.get(dd_table_key_strings[DD_TABLE_VERSION],
2406 reinterpret_cast<uint64 *>(&version))) {
2407 ut_ad(0);
2408 return;
2409 }
2410
2411 dest.set(dd_table_key_strings[DD_TABLE_VERSION], version);
2412 dest.set(dd_table_key_strings[DD_TABLE_AUTOINC], autoinc);
2413 }
2414
2415 /** Copy the metadata of a table definition if there was an instant
2416 ADD COLUMN happened. This should be done when it's not an ALTER TABLE
2417 with rebuild.
2418 @param[in,out] new_table New table definition
2419 @param[in] old_table Old table definition */
dd_copy_instant_n_cols(dd::Table & new_table,const dd::Table & old_table)2420 void dd_copy_instant_n_cols(dd::Table &new_table, const dd::Table &old_table) {
2421 ut_ad(dd_table_has_instant_cols(old_table));
2422
2423 if (!dd_table_has_instant_cols(new_table)) {
2424 uint32_t cols;
2425 old_table.se_private_data().get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2426 &cols);
2427 new_table.se_private_data().set(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2428 cols);
2429 }
2430 #ifdef UNIV_DEBUG
2431 else {
2432 uint32_t old_cols, new_cols;
2433 old_table.se_private_data().get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2434 &old_cols);
2435 new_table.se_private_data().get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2436 &new_cols);
2437 ut_ad(old_cols == new_cols);
2438 }
2439 #endif /* UNIV_DEBUG */
2440 }
2441
2442 template <typename Table>
dd_copy_private(Table & new_table,const Table & old_table)2443 void dd_copy_private(Table &new_table, const Table &old_table) {
2444 uint64 autoinc = 0;
2445 uint64 version = 0;
2446 bool reset = false;
2447 dd::Properties &se_private_data = new_table.se_private_data();
2448
2449 /* AUTOINC metadata could be set at the beginning for
2450 non-partitioned tables. So already set metadata should be kept */
2451 if (se_private_data.exists(dd_table_key_strings[DD_TABLE_AUTOINC])) {
2452 se_private_data.get(dd_table_key_strings[DD_TABLE_AUTOINC], &autoinc);
2453 se_private_data.get(dd_table_key_strings[DD_TABLE_VERSION], &version);
2454 reset = true;
2455 }
2456
2457 new_table.se_private_data().clear();
2458
2459 new_table.set_se_private_id(old_table.se_private_id());
2460 new_table.set_se_private_data(old_table.se_private_data());
2461
2462 if (reset) {
2463 se_private_data.set(dd_table_key_strings[DD_TABLE_VERSION], version);
2464 se_private_data.set(dd_table_key_strings[DD_TABLE_AUTOINC], autoinc);
2465 }
2466
2467 ut_ad(new_table.indexes()->size() == old_table.indexes().size());
2468
2469 /* Note that server could provide old and new dd::Table with
2470 different index order in this case, so always do a double loop */
2471 for (const auto old_index : old_table.indexes()) {
2472 auto idx = new_table.indexes()->begin();
2473 for (; (*idx)->name() != old_index->name(); ++idx)
2474 ;
2475 ut_ad(idx != new_table.indexes()->end());
2476
2477 auto new_index = *idx;
2478 ut_ad(!old_index->se_private_data().empty());
2479 ut_ad(new_index != nullptr);
2480 ut_ad(new_index->se_private_data().empty());
2481 ut_ad(new_index->name() == old_index->name());
2482
2483 new_index->set_se_private_data(old_index->se_private_data());
2484 new_index->set_tablespace_id(old_index->tablespace_id());
2485 new_index->options().clear();
2486 new_index->set_options(old_index->options());
2487 }
2488
2489 new_table.table().set_row_format(old_table.table().row_format());
2490 new_table.options().clear();
2491 new_table.set_options(old_table.options());
2492 }
2493
2494 template void dd_copy_private<dd::Table>(dd::Table &, const dd::Table &);
2495 template void dd_copy_private<dd::Partition>(dd::Partition &,
2496 const dd::Partition &);
2497
2498 /** Copy the engine-private parts of column definitions of a table.
2499 @param[in,out] new_table Copy of old table
2500 @param[in] old_table Old table */
dd_copy_table_columns(dd::Table & new_table,const dd::Table & old_table)2501 void dd_copy_table_columns(dd::Table &new_table, const dd::Table &old_table) {
2502 /* Columns in new table maybe more than old tables, when this is
2503 called for adding instant columns. Also adding and dropping
2504 virtual columns instantly is another case. */
2505 for (const auto old_col : old_table.columns()) {
2506 dd::Column *new_col = const_cast<dd::Column *>(
2507 dd_find_column(&new_table, old_col->name().c_str()));
2508
2509 if (new_col == nullptr) {
2510 ut_ad(old_col->is_virtual());
2511 continue;
2512 }
2513
2514 if (!old_col->se_private_data().empty()) {
2515 if (!new_col->se_private_data().empty())
2516 new_col->se_private_data().clear();
2517 new_col->set_se_private_data(old_col->se_private_data());
2518 }
2519 }
2520 }
2521
dd_part_adjust_table_id(dd::Table * new_table)2522 void dd_part_adjust_table_id(dd::Table *new_table) {
2523 ut_ad(dd_table_is_partitioned(*new_table));
2524
2525 auto part = new_table->leaf_partitions()->begin();
2526 table_id_t table_id = (*part)->se_private_id();
2527
2528 for (auto dd_column : *new_table->table().columns()) {
2529 dd_column->se_private_data().set(dd_index_key_strings[DD_TABLE_ID],
2530 table_id);
2531 }
2532 }
2533
2534 /** Clear the instant ADD COLUMN information of a table
2535 @param[in,out] dd_table dd::Table */
dd_clear_instant_table(dd::Table & dd_table)2536 void dd_clear_instant_table(dd::Table &dd_table) {
2537 ut_ad(dd_table_has_instant_cols(dd_table));
2538 dd_table.se_private_data().remove(
2539 dd_table_key_strings[DD_TABLE_INSTANT_COLS]);
2540
2541 ut_d(bool found = false);
2542 for (auto col : *dd_table.columns()) {
2543 dd::Properties &col_private = col->se_private_data();
2544 if (col_private.exists(
2545 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL])) {
2546 ut_d(found = true);
2547 col_private.remove(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL]);
2548 } else if (col_private.exists(
2549 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
2550 ut_d(found = true);
2551 col_private.remove(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT]);
2552 }
2553 }
2554
2555 ut_ad(found);
2556 }
2557
2558 /** Clear the instant ADD COLUMN information of a partition, to make it
2559 as a normal partition
2560 @param[in,out] dd_part dd::Partition */
dd_clear_instant_part(dd::Partition & dd_part)2561 void dd_clear_instant_part(dd::Partition &dd_part) {
2562 ut_ad(dd_part_has_instant_cols(dd_part));
2563
2564 dd_part.se_private_data().remove(
2565 dd_partition_key_strings[DD_PARTITION_INSTANT_COLS]);
2566 }
2567
2568 #ifdef UNIV_DEBUG
dd_instant_columns_exist(const dd::Table & dd_table)2569 bool dd_instant_columns_exist(const dd::Table &dd_table) {
2570 uint32_t n_cols = 0;
2571 uint32_t non_instant_cols = 0;
2572 bool found = false;
2573
2574 ut_ad(dd_table.se_private_data().exists(
2575 dd_table_key_strings[DD_TABLE_INSTANT_COLS]));
2576
2577 dd_table.se_private_data().get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2578 &n_cols);
2579
2580 for (auto col : dd_table.columns()) {
2581 if (col->is_virtual() || col->is_se_hidden()) {
2582 continue;
2583 }
2584
2585 const dd::Properties &col_private = col->se_private_data();
2586 if (col_private.exists(
2587 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL]) ||
2588 col_private.exists(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
2589 found = true;
2590 continue;
2591 }
2592
2593 ++non_instant_cols;
2594 }
2595
2596 ut_ad(found);
2597 /* Please note that n_cols could be 0 if the table only had some virtual
2598 columns before instant ADD COLUMN. So below check should be sufficient */
2599 ut_ad(non_instant_cols == n_cols);
2600 return (found && non_instant_cols == n_cols);
2601 }
2602 #endif /* UNIV_DEBUG */
2603
2604 /** Add column default values for new instantly added columns
2605 @param[in] old_table MySQL table as it is before the ALTER operation
2606 @param[in] altered_table MySQL table that is being altered
2607 @param[in,out] new_dd_table New dd::Table
2608 @param[in] new_table New InnoDB table object */
dd_add_instant_columns(const TABLE * old_table,const TABLE * altered_table,dd::Table * new_dd_table,const dict_table_t * new_table)2609 void dd_add_instant_columns(const TABLE *old_table, const TABLE *altered_table,
2610 dd::Table *new_dd_table,
2611 const dict_table_t *new_table) {
2612 DD_instant_col_val_coder coder;
2613 uint32_t old_n_stored_cols = 0;
2614 uint32_t old_cols = 0;
2615 uint32_t new_cols = 0;
2616 ut_d(uint32_t n_stored_checked = 0);
2617 ut_d(uint16_t num_instant_cols = 0);
2618
2619 for (uint32_t i = 0; i < old_table->s->fields; ++i) {
2620 if (!innobase_is_v_fld(old_table->field[i])) {
2621 ++old_n_stored_cols;
2622 }
2623 }
2624
2625 ut_ad(old_n_stored_cols <= old_table->s->fields);
2626 ut_ad(altered_table->s->fields > old_table->s->fields);
2627
2628 /* Note that only the order of stored columns are cared, which means
2629 1. Adding stored columns(along with virtual columns) at the end of the
2630 table is absolutely fine.
2631 2. Adding virtual columns before or after any existing stored columns
2632 is fine.
2633 3. Adding stored columns(along with virtual columns) before existing
2634 trailing virtual columns
2635 (especially for adding stored columns at the end of a table with functional
2636 indexes) is also fine.
2637 So need to find out which is the first stored column to be added. */
2638 while (old_cols < old_table->s->fields &&
2639 new_cols < altered_table->s->fields) {
2640 if (innobase_is_v_fld(old_table->field[old_cols])) {
2641 ++old_cols;
2642 continue;
2643 }
2644
2645 if (innobase_is_v_fld(altered_table->field[new_cols])) {
2646 ++new_cols;
2647 continue;
2648 }
2649
2650 ut_ad(strcmp(old_table->field[old_cols]->field_name,
2651 altered_table->field[new_cols]->field_name) == 0);
2652 ++old_cols;
2653 ++new_cols;
2654 ut_d(++n_stored_checked);
2655 }
2656
2657 ut_ad(old_cols == old_table->s->fields);
2658 ut_ad(new_cols < altered_table->s->fields);
2659 ut_ad(n_stored_checked == old_n_stored_cols);
2660
2661 for (uint32_t i = new_cols; i < altered_table->s->fields; ++i) {
2662 Field *field = altered_table->field[i];
2663
2664 if (innobase_is_v_fld(field)) {
2665 continue;
2666 }
2667
2668 /* The MySQL type code has to fit in 8 bits
2669 in the metadata stored in the InnoDB change buffer. */
2670 ut_ad(field->charset() == nullptr ||
2671 field->charset()->number <= MAX_CHAR_COLL_NUM);
2672 ut_ad(field->charset() == nullptr || field->charset()->number > 0);
2673
2674 dd::Column *column = const_cast<dd::Column *>(
2675 dd_find_column(new_dd_table, field->field_name));
2676 ut_ad(column != nullptr);
2677 dd::Properties &se_private = column->se_private_data();
2678
2679 ut_d(++num_instant_cols);
2680
2681 se_private.set(dd_index_key_strings[DD_TABLE_ID], new_table->id);
2682
2683 if (field->is_real_null()) {
2684 se_private.set(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL],
2685 true);
2686 continue;
2687 }
2688
2689 /* Get the mtype and prtype of this field. Keep this same
2690 with the code in dd_fill_dict_table(), except FTS check */
2691 ulint prtype = 0;
2692 unsigned col_len = field->pack_length();
2693 ulint nulls_allowed;
2694 ulint unsigned_type;
2695 ulint binary_type;
2696 ulint long_true_varchar;
2697 ulint charset_no;
2698 ulint mtype = get_innobase_type_from_mysql_type(&unsigned_type, field);
2699
2700 nulls_allowed = field->is_nullable() ? 0 : DATA_NOT_NULL;
2701
2702 binary_type = field->binary() ? DATA_BINARY_TYPE : 0;
2703
2704 charset_no = 0;
2705 if (dtype_is_string_type(mtype)) {
2706 charset_no = static_cast<ulint>(field->charset()->number);
2707 }
2708
2709 long_true_varchar = 0;
2710 if (field->type() == MYSQL_TYPE_VARCHAR) {
2711 col_len -= field->get_length_bytes();
2712
2713 if (field->get_length_bytes() == 2) {
2714 long_true_varchar = DATA_LONG_TRUE_VARCHAR;
2715 }
2716 }
2717
2718 prtype =
2719 dtype_form_prtype((ulint)field->type() | nulls_allowed | unsigned_type |
2720 binary_type | long_true_varchar,
2721 charset_no);
2722
2723 dict_col_t col;
2724 memset(&col, 0, sizeof(dict_col_t));
2725 /* Set a fake col_pos, since this should be useless */
2726 dict_mem_fill_column_struct(&col, 0, mtype, prtype, col_len, true);
2727 dfield_t dfield;
2728 col.copy_type(dfield_get_type(&dfield));
2729
2730 ulint size = field->pack_length();
2731 uint64_t buf;
2732 const byte *mysql_data = field->field_ptr();
2733
2734 row_mysql_store_col_in_innobase_format(
2735 &dfield, reinterpret_cast<byte *>(&buf), true, mysql_data, size,
2736 dict_table_is_comp(new_table));
2737
2738 size_t length = 0;
2739 const char *value = coder.encode(reinterpret_cast<byte *>(dfield.data),
2740 dfield.len, &length);
2741
2742 dd::String_type default_value;
2743 default_value.assign(dd::String_type(value, length));
2744 se_private.set(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT],
2745 default_value);
2746 }
2747
2748 ut_ad(num_instant_cols > 0);
2749 }
2750
2751 /** Compare the default values between imported column and column defined
2752 in the server. Note that it's absolutely OK if there is no default value
2753 in the column defined in server, since it can be filled in later.
2754 @param[in] dd_col dd::Column
2755 @param[in] col InnoDB column object
2756 @return true The default values match
2757 @retval false Not match */
dd_match_default_value(const dd::Column * dd_col,const dict_col_t * col)2758 bool dd_match_default_value(const dd::Column *dd_col, const dict_col_t *col) {
2759 ut_ad(col->instant_default != nullptr);
2760
2761 const dd::Properties &private_data = dd_col->se_private_data();
2762
2763 if (private_data.exists(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
2764 dd::String_type value;
2765 const byte *default_value;
2766 size_t len;
2767 bool match;
2768 DD_instant_col_val_coder coder;
2769
2770 private_data.get(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT], &value);
2771 default_value = coder.decode(value.c_str(), value.length(), &len);
2772
2773 match = col->instant_default->len == len &&
2774 memcmp(col->instant_default->value, default_value, len) == 0;
2775
2776 return (match);
2777
2778 } else if (private_data.exists(
2779 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL])) {
2780 return (col->instant_default->len == UNIV_SQL_NULL);
2781 }
2782
2783 return (true);
2784 }
2785
2786 /** Write default value of a column to dd::Column
2787 @param[in] col default value of this column to write
2788 @param[in,out] dd_col where to store the default value */
dd_write_default_value(const dict_col_t * col,dd::Column * dd_col)2789 void dd_write_default_value(const dict_col_t *col, dd::Column *dd_col) {
2790 if (col->instant_default->len == UNIV_SQL_NULL) {
2791 dd_col->se_private_data().set(
2792 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL], true);
2793 } else {
2794 dd::String_type default_value;
2795 size_t length = 0;
2796 DD_instant_col_val_coder coder;
2797 const char *value = coder.encode(col->instant_default->value,
2798 col->instant_default->len, &length);
2799
2800 default_value.assign(dd::String_type(value, length));
2801 dd_col->se_private_data().set(
2802 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT], default_value);
2803 }
2804 }
2805
2806 /** Parse the default value from dd::Column::se_private to dict_col_t
2807 @param[in] se_private_data dd::Column::se_private
2808 @param[in,out] col InnoDB column object
2809 @param[in,out] heap Heap to store the default value */
dd_parse_default_value(const dd::Properties & se_private_data,dict_col_t * col,mem_heap_t * heap)2810 static void dd_parse_default_value(const dd::Properties &se_private_data,
2811 dict_col_t *col, mem_heap_t *heap) {
2812 if (se_private_data.exists(
2813 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL])) {
2814 col->set_default(nullptr, UNIV_SQL_NULL, heap);
2815 } else if (se_private_data.exists(
2816 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
2817 const byte *default_value;
2818 size_t len;
2819 dd::String_type value;
2820 DD_instant_col_val_coder coder;
2821
2822 se_private_data.get(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT],
2823 &value);
2824
2825 default_value = coder.decode(value.c_str(), value.length(), &len);
2826
2827 col->set_default(default_value, len, heap);
2828 }
2829 }
2830
2831 /** Import all metadata which is related to instant ADD COLUMN of a table
2832 to dd::Table. This is used for IMPORT.
2833 @param[in] table InnoDB table object
2834 @param[in,out] dd_table dd::Table */
dd_import_instant_add_columns(const dict_table_t * table,dd::Table * dd_table)2835 void dd_import_instant_add_columns(const dict_table_t *table,
2836 dd::Table *dd_table) {
2837 ut_ad(table->has_instant_cols());
2838 ut_ad(dict_table_is_partition(table) == dd_table_is_partitioned(*dd_table));
2839
2840 if (!dd_table_is_partitioned(*dd_table)) {
2841 dd_table->se_private_data().set(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2842 table->get_instant_cols());
2843 } else {
2844 uint32_t instant_cols = std::numeric_limits<uint32_t>::max();
2845
2846 if (dd_table->se_private_data().exists(
2847 dd_table_key_strings[DD_TABLE_INSTANT_COLS])) {
2848 dd_table->se_private_data().get(
2849 dd_table_key_strings[DD_TABLE_INSTANT_COLS], &instant_cols);
2850 }
2851
2852 if (instant_cols > table->get_instant_cols()) {
2853 dd_table->se_private_data().set(
2854 dd_table_key_strings[DD_TABLE_INSTANT_COLS],
2855 table->get_instant_cols());
2856 }
2857
2858 dd::Partition *partition = nullptr;
2859 for (const auto dd_part : *dd_table->leaf_partitions()) {
2860 if (dict_name::match_partition(table->name.m_name, dd_part)) {
2861 partition = dd_part;
2862 break;
2863 }
2864 }
2865
2866 ut_ad(partition != nullptr);
2867
2868 partition->se_private_data().set(
2869 dd_partition_key_strings[DD_PARTITION_INSTANT_COLS],
2870 table->get_instant_cols());
2871 }
2872
2873 /* Copy all default values if necessary */
2874 ut_d(bool first_instant = false);
2875 for (uint16_t i = 0; i < table->get_n_user_cols(); ++i) {
2876 dict_col_t *col = table->get_col(i);
2877 if (col->instant_default == nullptr) {
2878 ut_ad(!first_instant);
2879 continue;
2880 }
2881
2882 ut_d(first_instant = true);
2883
2884 dd::Column *dd_col = const_cast<dd::Column *>(
2885 dd_find_column(dd_table, table->get_col_name(i)));
2886 ut_ad(dd_col != nullptr);
2887
2888 /* Default values mismatch should have been done.
2889 So only write default value when it's not ever recorded */
2890 if (!dd_col->se_private_data().exists(
2891 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL]) &&
2892 !dd_col->se_private_data().exists(
2893 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
2894 dd_write_default_value(col, dd_col);
2895 }
2896 }
2897 }
2898
2899 /** Write metadata of a index to dd::Index
2900 @param[in] dd_space_id Tablespace id, which server allocates
2901 @param[in,out] dd_index dd::Index
2902 @param[in] index InnoDB index object */
2903 template <typename Index>
dd_write_index(dd::Object_id dd_space_id,Index * dd_index,const dict_index_t * index)2904 static void dd_write_index(dd::Object_id dd_space_id, Index *dd_index,
2905 const dict_index_t *index) {
2906 ut_ad(index->id != 0);
2907 ut_ad(index->page >= FSP_FIRST_INODE_PAGE_NO);
2908
2909 dd_index->set_tablespace_id(dd_space_id);
2910
2911 dd::Properties &p = dd_index->se_private_data();
2912 p.set(dd_index_key_strings[DD_INDEX_ID], index->id);
2913 p.set(dd_index_key_strings[DD_INDEX_SPACE_ID], index->space);
2914 p.set(dd_index_key_strings[DD_TABLE_ID], index->table->id);
2915 p.set(dd_index_key_strings[DD_INDEX_ROOT], index->page);
2916 p.set(dd_index_key_strings[DD_INDEX_TRX_ID], index->trx_id);
2917 }
2918
2919 template void dd_write_index<dd::Index>(dd::Object_id, dd::Index *,
2920 const dict_index_t *);
2921 template void dd_write_index<dd::Partition_index>(dd::Object_id,
2922 dd::Partition_index *,
2923 const dict_index_t *);
2924
2925 template <typename Table>
dd_write_table(dd::Object_id dd_space_id,Table * dd_table,const dict_table_t * table)2926 void dd_write_table(dd::Object_id dd_space_id, Table *dd_table,
2927 const dict_table_t *table) {
2928 /* Only set the tablespace id for tables in innodb_system tablespace */
2929 if (dd_space_id == dict_sys_t::s_dd_sys_space_id) {
2930 dd_table->set_tablespace_id(dd_space_id);
2931 }
2932
2933 dd_table->set_se_private_id(table->id);
2934
2935 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
2936 ut_ad(dict_table_is_file_per_table(table));
2937 dd_table->se_private_data().set(
2938 dd_table_key_strings[DD_TABLE_DATA_DIRECTORY], true);
2939 }
2940
2941 for (auto dd_index : *dd_table->indexes()) {
2942 /* Don't assume the index orders are the same, even on
2943 CREATE TABLE. This could be called from TRUNCATE path,
2944 which would do some adjustment on FULLTEXT index, thus
2945 the out-of-sync order */
2946 const dict_index_t *index = dd_find_index(table, dd_index);
2947 ut_ad(index != nullptr);
2948 dd_write_index(dd_space_id, dd_index, index);
2949 }
2950
2951 if (!dd_table_is_partitioned(dd_table->table()) ||
2952 dd_part_is_first(reinterpret_cast<dd::Partition *>(dd_table))) {
2953 for (auto dd_column : *dd_table->table().columns()) {
2954 dd_column->se_private_data().set(dd_index_key_strings[DD_TABLE_ID],
2955 table->id);
2956 }
2957 }
2958 }
2959
2960 template void dd_write_table<dd::Table>(dd::Object_id, dd::Table *,
2961 const dict_table_t *);
2962 template void dd_write_table<dd::Partition>(dd::Object_id, dd::Partition *,
2963 const dict_table_t *);
2964
2965 template <typename Table>
dd_set_table_options(Table * dd_table,const dict_table_t * table)2966 void dd_set_table_options(Table *dd_table, const dict_table_t *table) {
2967 dd::Table *dd_table_def = &(dd_table->table());
2968 enum row_type type = ROW_TYPE_DEFAULT;
2969 dd::Table::enum_row_format format = dd::Table::RF_DYNAMIC;
2970 dd::Properties &options = dd_table_def->options();
2971
2972 switch (dict_tf_get_rec_format(table->flags)) {
2973 case REC_FORMAT_REDUNDANT:
2974 format = dd::Table::RF_REDUNDANT;
2975 type = ROW_TYPE_REDUNDANT;
2976 break;
2977 case REC_FORMAT_COMPACT:
2978 format = dd::Table::RF_COMPACT;
2979 type = ROW_TYPE_COMPACT;
2980 break;
2981 case REC_FORMAT_COMPRESSED:
2982 format = dd::Table::RF_COMPRESSED;
2983 type = ROW_TYPE_COMPRESSED;
2984 break;
2985 case REC_FORMAT_DYNAMIC:
2986 format = dd::Table::RF_DYNAMIC;
2987 type = ROW_TYPE_DYNAMIC;
2988 break;
2989 default:
2990 ut_a(0);
2991 }
2992
2993 if (!dd_table_is_partitioned(*dd_table_def)) {
2994 if (auto zip_ssize = DICT_TF_GET_ZIP_SSIZE(table->flags)) {
2995 uint32 old_size;
2996 if (!options.get("key_block_size", &old_size) && old_size != 0) {
2997 options.set("key_block_size", 1 << (zip_ssize - 1));
2998 }
2999 } else {
3000 options.set("key_block_size", 0);
3001 /* It's possible that InnoDB ignores the specified
3002 key_block_size, so check the block_size for every index.
3003 Server assumes if block_size = 0, there should be no
3004 option found, so remove it when found */
3005 for (auto dd_index : *dd_table_def->indexes()) {
3006 if (dd_index->options().exists("block_size")) {
3007 dd_index->options().remove("block_size");
3008 }
3009 }
3010 }
3011
3012 dd_table_def->set_row_format(format);
3013 if (options.exists("row_type")) {
3014 options.set("row_type", type);
3015 }
3016 } else if (dd_table_def->row_format() != format) {
3017 dd_table->se_private_data().set(
3018 dd_partition_key_strings[DD_PARTITION_ROW_FORMAT], format);
3019 }
3020 }
3021
3022 template void dd_set_table_options<dd::Table>(dd::Table *,
3023 const dict_table_t *);
3024 template void dd_set_table_options<dd::Partition>(dd::Partition *,
3025 const dict_table_t *);
3026
dd_update_v_cols(dd::Table * dd_table,table_id_t id)3027 void dd_update_v_cols(dd::Table *dd_table, table_id_t id) {
3028 for (auto dd_column : *dd_table->columns()) {
3029 #ifdef UNIV_DEBUG
3030 if (dd_column->se_private_data().exists(
3031 dd_index_key_strings[DD_TABLE_ID])) {
3032 table_id_t table_id;
3033 dd_column->se_private_data().get(dd_index_key_strings[DD_TABLE_ID],
3034 reinterpret_cast<uint64 *>(&table_id));
3035 ut_ad(table_id == id);
3036 }
3037 #endif /* UNIV_DEBUG */
3038
3039 if (!dd_column->is_virtual()) {
3040 continue;
3041 }
3042
3043 dd::Properties &p = dd_column->se_private_data();
3044
3045 if (!p.exists(dd_index_key_strings[DD_TABLE_ID])) {
3046 p.set(dd_index_key_strings[DD_TABLE_ID], id);
3047 }
3048 }
3049 }
3050
3051 /** Write metadata of a tablespace to dd::Tablespace
3052 @param[in,out] dd_space dd::Tablespace
3053 @param[in] space_id InnoDB tablespace ID
3054 @param[in] fsp_flags InnoDB tablespace flags
3055 @param[in] state InnoDB tablespace state */
dd_write_tablespace(dd::Tablespace * dd_space,space_id_t space_id,uint32_t fsp_flags,dd_space_states state)3056 void dd_write_tablespace(dd::Tablespace *dd_space, space_id_t space_id,
3057 uint32_t fsp_flags, dd_space_states state) {
3058 dd::Properties &p = dd_space->se_private_data();
3059 p.set(dd_space_key_strings[DD_SPACE_ID], space_id);
3060 p.set(dd_space_key_strings[DD_SPACE_FLAGS], static_cast<uint32>(fsp_flags));
3061 p.set(dd_space_key_strings[DD_SPACE_SERVER_VERSION],
3062 DD_SPACE_CURRENT_SRV_VERSION);
3063 p.set(dd_space_key_strings[DD_SPACE_VERSION], DD_SPACE_CURRENT_SPACE_VERSION);
3064 p.set(dd_space_key_strings[DD_SPACE_STATE], dd_space_state_values[state]);
3065 }
3066
3067 /** Add fts doc id column and index to new table
3068 when old table has hidden fts doc id without fulltext index
3069 @param[in,out] new_table New dd table
3070 @param[in] old_table Old dd table */
dd_add_fts_doc_id_index(dd::Table & new_table,const dd::Table & old_table)3071 void dd_add_fts_doc_id_index(dd::Table &new_table, const dd::Table &old_table) {
3072 if (new_table.columns()->size() == old_table.columns().size()) {
3073 ut_ad(new_table.indexes()->size() == old_table.indexes().size());
3074 return;
3075 }
3076
3077 ut_ad(new_table.columns()->size() + 1 == old_table.columns().size());
3078 ut_ad(new_table.indexes()->size() + 1 == old_table.indexes().size());
3079
3080 /* Add hidden FTS_DOC_ID column */
3081 dd::Column *col = new_table.add_column();
3082 col->set_hidden(dd::Column::enum_hidden_type::HT_HIDDEN_SE);
3083 col->set_name(FTS_DOC_ID_COL_NAME);
3084 col->set_type(dd::enum_column_types::LONGLONG);
3085 col->set_nullable(false);
3086 col->set_unsigned(true);
3087 col->set_collation_id(1);
3088
3089 /* Add hidden FTS_DOC_ID index */
3090 dd_set_hidden_unique_index(new_table.add_index(), FTS_DOC_ID_INDEX_NAME, col);
3091
3092 return;
3093 }
3094
3095 /** Find the specified dd::Index or dd::Partition_index in an InnoDB table
3096 @tparam Index dd::Index or dd::Partition_index
3097 @param[in] table InnoDB table object
3098 @param[in] dd_index Index to search
3099 @return the dict_index_t object related to the index */
3100 template <typename Index>
dd_find_index(const dict_table_t * table,Index * dd_index)3101 const dict_index_t *dd_find_index(const dict_table_t *table, Index *dd_index) {
3102 /* If the name is PRIMARY, return the first index directly,
3103 because the internal index name could be 'GEN_CLUST_INDEX'.
3104 It could be possible that the primary key name is not PRIMARY,
3105 because it's an implicitly upgraded unique index. We have to
3106 search all the indexes */
3107 if (dd_index->name() == "PRIMARY") {
3108 return (table->first_index());
3109 }
3110
3111 /* The order could be different because all unique dd::Index(es)
3112 would be in front of other indexes. */
3113 const dict_index_t *index;
3114 for (index = table->first_index();
3115 (index != nullptr &&
3116 (dd_index->name() != index->name() || !index->is_committed()));
3117 index = index->next()) {
3118 }
3119
3120 ut_ad(index != nullptr);
3121
3122 #ifdef UNIV_DEBUG
3123 /* Never find another index with the same name */
3124 const dict_index_t *next_index = index->next();
3125 for (; (next_index != nullptr && (dd_index->name() != next_index->name() ||
3126 !next_index->is_committed()));
3127 next_index = next_index->next()) {
3128 }
3129 ut_ad(next_index == nullptr);
3130 #endif /* UNIV_DEBUG */
3131
3132 return (index);
3133 }
3134
3135 template const dict_index_t *dd_find_index<dd::Index>(const dict_table_t *,
3136 dd::Index *);
dd_find_index(warn_unused_result)3137 template const dict_index_t *dd_find_index<dd::Partition_index>(
3138 const dict_table_t *, dd::Partition_index *);
3139
3140 /** Create an index.
3141 @param[in] dd_index DD Index
3142 @param[in,out] table InnoDB table
3143 @param[in] strict whether to be strict about the max record size
3144 @param[in] form MySQL table structure
3145 @param[in] key_num key_info[] offset
3146 @return error code
3147 @retval 0 on success
3148 @retval HA_ERR_INDEX_COL_TOO_LONG if a column is too long
3149 @retval HA_ERR_TOO_BIG_ROW if the record is too long */
3150 static MY_ATTRIBUTE((warn_unused_result)) int dd_fill_one_dict_index(
3151 const dd::Index *dd_index, dict_table_t *table, bool strict,
3152 const TABLE_SHARE *form, uint key_num) {
3153 const KEY &key = form->key_info[key_num];
3154 ulint type = 0;
3155 unsigned n_fields = key.user_defined_key_parts;
3156 unsigned n_uniq = n_fields;
3157
3158 ut_ad(!mutex_own(&dict_sys->mutex));
3159 /* This name cannot be used for a non-primary index */
3160 ut_ad(key_num == form->primary_key ||
3161 my_strcasecmp(system_charset_info, key.name, primary_key_name) != 0);
3162 /* PARSER is only valid for FULLTEXT INDEX */
3163 ut_ad((key.flags & (HA_FULLTEXT | HA_USES_PARSER)) != HA_USES_PARSER);
3164 ut_ad(form->fields > 0);
3165 ut_ad(n_fields > 0);
3166
3167 if (key.flags & HA_SPATIAL) {
3168 ut_ad(!table->is_intrinsic());
3169 type = DICT_SPATIAL;
3170 ut_ad(n_fields == 1);
3171 } else if (key.flags & HA_FULLTEXT) {
3172 ut_ad(!table->is_intrinsic());
3173 type = DICT_FTS;
3174 n_uniq = 0;
3175 } else if (key_num == form->primary_key) {
3176 ut_ad(key.flags & HA_NOSAME);
3177 ut_ad(n_uniq > 0);
3178 type = DICT_CLUSTERED | DICT_UNIQUE;
3179 } else {
3180 type = (key.flags & HA_NOSAME) ? DICT_UNIQUE : 0;
3181 }
3182
3183 ut_ad(!!(type & DICT_FTS) == (n_uniq == 0));
3184
3185 dict_index_t *index =
3186 dict_mem_index_create(table->name.m_name, key.name, 0, type, n_fields);
3187
3188 index->n_uniq = n_uniq;
3189
3190 const ulint max_len = DICT_MAX_FIELD_LEN_BY_FORMAT(table);
3191 DBUG_EXECUTE_IF("ib_create_table_fail_at_create_index",
3192 dict_mem_index_free(index);
3193 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_len);
3194 return (HA_ERR_TOO_BIG_ROW););
3195
3196 for (unsigned i = 0; i < key.user_defined_key_parts; i++) {
3197 const KEY_PART_INFO *key_part = &key.key_part[i];
3198 unsigned prefix_len = 0;
3199 const Field *field = key_part->field;
3200 ut_ad(field == form->field[key_part->fieldnr - 1]);
3201 ut_ad(field == form->field[field->field_index()]);
3202
3203 if (field->is_virtual_gcol()) {
3204 index->type |= DICT_VIRTUAL;
3205
3206 /* Whether it is a multi-value index */
3207 if ((field->gcol_info->expr_item &&
3208 field->gcol_info->expr_item->returns_array()) ||
3209 field->is_array()) {
3210 index->type |= DICT_MULTI_VALUE;
3211 }
3212 }
3213
3214 bool is_asc = true;
3215
3216 if (key_part->key_part_flag & HA_REVERSE_SORT) {
3217 is_asc = false;
3218 }
3219
3220 if (key.flags & HA_SPATIAL) {
3221 prefix_len = 0;
3222 } else if (key.flags & HA_FULLTEXT) {
3223 prefix_len = 0;
3224 } else if (key_part->key_part_flag & HA_PART_KEY_SEG) {
3225 /* SPATIAL and FULLTEXT index always are on
3226 full columns. */
3227 ut_ad(!(key.flags & (HA_SPATIAL | HA_FULLTEXT)));
3228 prefix_len = key_part->length;
3229 ut_ad(prefix_len > 0);
3230 } else {
3231 ut_ad(key.flags & (HA_SPATIAL | HA_FULLTEXT) ||
3232 (!is_blob(field->real_type()) &&
3233 field->real_type() != MYSQL_TYPE_GEOMETRY) ||
3234 key_part->length >= (field->type() == MYSQL_TYPE_VARCHAR
3235 ? field->key_length()
3236 : field->pack_length()));
3237 prefix_len = 0;
3238 }
3239
3240 if ((key_part->length > max_len || prefix_len > max_len) &&
3241 !(key.flags & (HA_FULLTEXT))) {
3242 dict_mem_index_free(index);
3243 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_len);
3244 return (HA_ERR_INDEX_COL_TOO_LONG);
3245 }
3246
3247 dict_col_t *col = nullptr;
3248
3249 if (innobase_is_v_fld(field)) {
3250 dict_v_col_t *v_col =
3251 dict_table_get_nth_v_col_mysql(table, field->field_index());
3252 col = reinterpret_cast<dict_col_t *>(v_col);
3253 } else {
3254 ulint t_num_v = 0;
3255 for (ulint z = 0; z < field->field_index(); z++) {
3256 if (innobase_is_v_fld(form->field[z])) {
3257 t_num_v++;
3258 }
3259 }
3260
3261 col = &table->cols[field->field_index() - t_num_v];
3262 }
3263
3264 col->is_visible = !field->is_hidden_from_user();
3265 dict_index_add_col(index, table, col, prefix_len, is_asc);
3266 }
3267
3268 ut_ad(((key.flags & HA_FULLTEXT) == HA_FULLTEXT) ==
3269 !!(index->type & DICT_FTS));
3270
3271 index->n_user_defined_cols = key.user_defined_key_parts;
3272
3273 if (dict_index_add_to_cache(table, index, 0, FALSE) != DB_SUCCESS) {
3274 ut_ad(0);
3275 return (HA_ERR_GENERIC);
3276 }
3277
3278 index = UT_LIST_GET_LAST(table->indexes);
3279
3280 if (index->type & DICT_FTS) {
3281 ut_ad((key.flags & HA_FULLTEXT) == HA_FULLTEXT);
3282 ut_ad(index->n_uniq == 0);
3283 ut_ad(n_uniq == 0);
3284
3285 if (table->fts->cache == nullptr) {
3286 DICT_TF2_FLAG_SET(table, DICT_TF2_FTS);
3287 table->fts->cache = fts_cache_create(table);
3288
3289 rw_lock_x_lock(&table->fts->cache->init_lock);
3290 /* Notify the FTS cache about this index. */
3291 fts_cache_index_cache_create(table, index);
3292 rw_lock_x_unlock(&table->fts->cache->init_lock);
3293 }
3294 }
3295
3296 if (strcmp(index->name, FTS_DOC_ID_INDEX_NAME) == 0) {
3297 ut_ad(table->fts_doc_id_index == nullptr);
3298 table->fts_doc_id_index = index;
3299 }
3300
3301 if (dict_index_is_spatial(index)) {
3302 ut_ad(dd_index->name() == key.name);
3303 size_t geom_col_idx;
3304 for (geom_col_idx = 0; geom_col_idx < dd_index->elements().size();
3305 ++geom_col_idx) {
3306 if (!dd_index->elements()[geom_col_idx]->column().is_se_hidden()) break;
3307 }
3308 const dd::Column &col = dd_index->elements()[geom_col_idx]->column();
3309 bool srid_has_value = col.srs_id().has_value();
3310 index->fill_srid_value(srid_has_value ? col.srs_id().value() : 0,
3311 srid_has_value);
3312 }
3313
3314 return (0);
3315 }
3316
3317 /** Parse MERGE_THRESHOLD value from a comment string.
3318 @param[in] thd connection
3319 @param[in] str string which might include 'MERGE_THRESHOLD='
3320 @return value parsed
3321 @retval dict_index_t::MERGE_THRESHOLD_DEFAULT for missing or invalid value. */
dd_parse_merge_threshold(THD * thd,const char * str)3322 static ulint dd_parse_merge_threshold(THD *thd, const char *str) {
3323 static constexpr char label[] = "MERGE_THRESHOLD=";
3324 const char *pos = strstr(str, label);
3325
3326 if (pos != nullptr) {
3327 pos += (sizeof label) - 1;
3328
3329 int ret = atoi(pos);
3330
3331 if (ret > 0 && unsigned(ret) <= DICT_INDEX_MERGE_THRESHOLD_DEFAULT) {
3332 return (static_cast<ulint>(ret));
3333 }
3334
3335 push_warning_printf(thd, Sql_condition::SL_WARNING, WARN_OPTION_IGNORED,
3336 ER_DEFAULT(WARN_OPTION_IGNORED), "MERGE_THRESHOLD");
3337 }
3338
3339 return (DICT_INDEX_MERGE_THRESHOLD_DEFAULT);
3340 }
3341
3342 /** Copy attributes from MySQL TABLE_SHARE into an InnoDB table object.
3343 @param[in,out] thd thread context
3344 @param[in,out] table InnoDB table
3345 @param[in] table_share TABLE_SHARE */
dd_copy_from_table_share(THD * thd,dict_table_t * table,const TABLE_SHARE * table_share)3346 inline void dd_copy_from_table_share(THD *thd, dict_table_t *table,
3347 const TABLE_SHARE *table_share) {
3348 if (table->is_temporary()) {
3349 dict_stats_set_persistent(table, false, true);
3350 } else {
3351 switch (table_share->db_create_options &
3352 (HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT)) {
3353 default:
3354 /* If a CREATE or ALTER statement contains
3355 STATS_PERSISTENT=0 STATS_PERSISTENT=1,
3356 it will be interpreted as STATS_PERSISTENT=1. */
3357 case HA_OPTION_STATS_PERSISTENT:
3358 dict_stats_set_persistent(table, true, false);
3359 break;
3360 case HA_OPTION_NO_STATS_PERSISTENT:
3361 dict_stats_set_persistent(table, false, true);
3362 break;
3363 case 0:
3364 break;
3365 }
3366 }
3367
3368 dict_stats_auto_recalc_set(
3369 table, table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON,
3370 table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF);
3371
3372 table->stats_sample_pages = table_share->stats_sample_pages;
3373
3374 const ulint merge_threshold_table =
3375 table_share->comment.str
3376 ? dd_parse_merge_threshold(thd, table_share->comment.str)
3377 : DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
3378 dict_index_t *index = table->first_index();
3379
3380 index->merge_threshold = merge_threshold_table;
3381
3382 if (dict_index_is_auto_gen_clust(index)) {
3383 index = index->next();
3384 }
3385
3386 for (uint i = 0; i < table_share->keys; i++) {
3387 const KEY *key_info = &table_share->key_info[i];
3388
3389 ut_ad(index != nullptr);
3390
3391 if (key_info->flags & HA_USES_COMMENT && key_info->comment.str != nullptr) {
3392 index->merge_threshold =
3393 dd_parse_merge_threshold(thd, key_info->comment.str);
3394 } else {
3395 index->merge_threshold = merge_threshold_table;
3396 }
3397
3398 index = index->next();
3399
3400 /* Skip hidden FTS_DOC_ID index */
3401 if (index != nullptr && index->hidden) {
3402 ut_ad(strcmp(index->name, FTS_DOC_ID_INDEX_NAME) == 0);
3403 index = index->next();
3404 }
3405 }
3406
3407 #ifdef UNIV_DEBUG
3408 if (index != nullptr) {
3409 ut_ad(table_share->keys == 0);
3410 ut_ad(index->hidden);
3411 ut_ad(strcmp(index->name, FTS_DOC_ID_INDEX_NAME) == 0);
3412 }
3413 #endif
3414 }
3415
3416 /** Instantiate index related metadata
3417 @param[in,out] dd_table Global DD table metadata
3418 @param[in] m_form MySQL table definition
3419 @param[in,out] m_table InnoDB table definition
3420 @param[in] create_info create table information
3421 @param[in] zip_allowed if compression is allowed
3422 @param[in] strict if report error in strict mode
3423 @param[in] m_thd THD instance
3424 @return 0 if successful, otherwise error number */
dd_fill_dict_index(const dd::Table & dd_table,const TABLE * m_form,dict_table_t * m_table,HA_CREATE_INFO * create_info,bool zip_allowed,bool strict,THD * m_thd)3425 inline int dd_fill_dict_index(const dd::Table &dd_table, const TABLE *m_form,
3426 dict_table_t *m_table,
3427 HA_CREATE_INFO *create_info, bool zip_allowed,
3428 bool strict, THD *m_thd) {
3429 int error = 0;
3430
3431 ut_ad(!mutex_own(&dict_sys->mutex));
3432
3433 /* Create the keys */
3434 if (m_form->s->keys == 0 || m_form->s->primary_key == MAX_KEY) {
3435 /* Create an index which is used as the clustered index;
3436 order the rows by the hidden InnoDB column DB_ROW_ID. */
3437 dict_index_t *index = dict_mem_index_create(
3438 m_table->name.m_name, "GEN_CLUST_INDEX", 0, DICT_CLUSTERED, 0);
3439 index->n_uniq = 0;
3440
3441 dberr_t new_err =
3442 dict_index_add_to_cache(m_table, index, index->page, FALSE);
3443 if (new_err != DB_SUCCESS) {
3444 error = HA_ERR_GENERIC;
3445 goto dd_error;
3446 }
3447 } else {
3448 /* In InnoDB, the clustered index must always be
3449 created first. */
3450 error = dd_fill_one_dict_index(dd_table.indexes()[m_form->s->primary_key],
3451 m_table, strict, m_form->s,
3452 m_form->s->primary_key);
3453 if (error != 0) {
3454 goto dd_error;
3455 }
3456 }
3457
3458 for (uint i = !m_form->s->primary_key; i < m_form->s->keys; i++) {
3459 ulint dd_index_num = i + ((m_form->s->primary_key == MAX_KEY) ? 1 : 0);
3460
3461 error = dd_fill_one_dict_index(dd_table.indexes()[dd_index_num], m_table,
3462 strict, m_form->s, i);
3463 if (error != 0) {
3464 goto dd_error;
3465 }
3466 }
3467
3468 if (dict_table_has_fts_index(m_table)) {
3469 ut_ad(DICT_TF2_FLAG_IS_SET(m_table, DICT_TF2_FTS));
3470 }
3471
3472 /* Create the ancillary tables that are common to all FTS indexes on
3473 this table. */
3474 if (DICT_TF2_FLAG_IS_SET(m_table, DICT_TF2_FTS_HAS_DOC_ID) ||
3475 DICT_TF2_FLAG_IS_SET(m_table, DICT_TF2_FTS)) {
3476 fts_doc_id_index_enum ret;
3477
3478 ut_ad(!m_table->is_intrinsic());
3479 /* Check whether there already exists FTS_DOC_ID_INDEX */
3480 ret = innobase_fts_check_doc_id_index_in_def(m_form->s->keys,
3481 m_form->key_info);
3482
3483 switch (ret) {
3484 case FTS_INCORRECT_DOC_ID_INDEX:
3485 push_warning_printf(m_thd, Sql_condition::SL_WARNING,
3486 ER_WRONG_NAME_FOR_INDEX,
3487 " InnoDB: Index name %s is reserved"
3488 " for the unique index on"
3489 " FTS_DOC_ID column for FTS"
3490 " Document ID indexing"
3491 " on table %s. Please check"
3492 " the index definition to"
3493 " make sure it is of correct"
3494 " type\n",
3495 FTS_DOC_ID_INDEX_NAME, m_table->name.m_name);
3496
3497 if (m_table->fts) {
3498 fts_free(m_table);
3499 }
3500
3501 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), FTS_DOC_ID_INDEX_NAME);
3502 return (HA_ERR_GENERIC);
3503 case FTS_EXIST_DOC_ID_INDEX:
3504 break;
3505 case FTS_NOT_EXIST_DOC_ID_INDEX:
3506 dict_index_t *doc_id_index;
3507 doc_id_index = dict_mem_index_create(
3508 m_table->name.m_name, FTS_DOC_ID_INDEX_NAME, 0, DICT_UNIQUE, 1);
3509 doc_id_index->add_field(FTS_DOC_ID_COL_NAME, 0, true);
3510
3511 dberr_t new_err = dict_index_add_to_cache(m_table, doc_id_index,
3512 doc_id_index->page, FALSE);
3513 if (new_err != DB_SUCCESS) {
3514 error = HA_ERR_GENERIC;
3515 goto dd_error;
3516 }
3517
3518 doc_id_index = UT_LIST_GET_LAST(m_table->indexes);
3519 doc_id_index->hidden = true;
3520 }
3521
3522 /* Cache all the FTS indexes on this table in the FTS
3523 specific structure. They are used for FTS indexed
3524 column update handling. */
3525 if (dict_table_has_fts_index(m_table)) {
3526 fts_t *fts = m_table->fts;
3527 ut_a(fts != nullptr);
3528
3529 dict_table_get_all_fts_indexes(m_table, m_table->fts->indexes);
3530 }
3531
3532 ulint fts_doc_id_col = ULINT_UNDEFINED;
3533
3534 ret = innobase_fts_check_doc_id_index(m_table, nullptr, &fts_doc_id_col);
3535
3536 if (ret != FTS_INCORRECT_DOC_ID_INDEX) {
3537 ut_ad(m_table->fts->doc_col == ULINT_UNDEFINED);
3538 m_table->fts->doc_col = fts_doc_id_col;
3539 ut_ad(m_table->fts->doc_col != ULINT_UNDEFINED);
3540
3541 m_table->fts_doc_id_index =
3542 dict_table_get_index_on_name(m_table, FTS_DOC_ID_INDEX_NAME);
3543 }
3544 }
3545
3546 if (error == 0) {
3547 dd_copy_from_table_share(m_thd, m_table, m_form->s);
3548 ut_ad(!m_table->is_temporary() ||
3549 !dict_table_page_size(m_table).is_compressed());
3550 if (!m_table->is_temporary()) {
3551 dict_table_stats_latch_create(m_table, true);
3552 }
3553 } else {
3554 dd_error:
3555 mutex_enter(&dict_sys->mutex);
3556
3557 for (dict_index_t *f_index = UT_LIST_GET_LAST(m_table->indexes);
3558 f_index != nullptr; f_index = UT_LIST_GET_LAST(m_table->indexes)) {
3559 dict_index_remove_from_cache(m_table, f_index);
3560 }
3561
3562 mutex_exit(&dict_sys->mutex);
3563
3564 dict_mem_table_free(m_table);
3565 }
3566
3567 return (error);
3568 }
3569
3570 /** Read the metadata of default values for all columns added instantly
3571 @param[in] dd_table dd::Table
3572 @param[in,out] table InnoDB table object */
dd_fill_instant_columns(const dd::Table & dd_table,dict_table_t * table)3573 static void dd_fill_instant_columns(const dd::Table &dd_table,
3574 dict_table_t *table) {
3575 ut_ad(table->has_instant_cols());
3576 ut_ad(dd_table_has_instant_cols(dd_table));
3577
3578 #ifdef UNIV_DEBUG
3579 for (uint16_t i = 0; i < table->get_n_cols(); ++i) {
3580 ut_ad(table->get_col(i)->instant_default == nullptr);
3581 }
3582 #endif /* UNIV_DEBUG */
3583
3584 uint32_t skip = 0;
3585
3586 if (dd_table_is_partitioned(dd_table)) {
3587 uint32_t cols;
3588
3589 dd_table.se_private_data().get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
3590 &cols);
3591 ut_ad(cols <= table->get_instant_cols());
3592
3593 /* The dd::Columns should have `cols` default values,
3594 however, this partition table only needs
3595 `table->get_instant_cols()` default values. */
3596 skip = table->get_instant_cols() - cols;
3597 }
3598
3599 /* Assume the order of non-virtual columns are the same */
3600 uint32_t innodb_pos = 0;
3601 for (const auto col : dd_table.columns()) {
3602 if (col->is_virtual() || col->is_se_hidden()) {
3603 continue;
3604 }
3605
3606 dict_col_t *column = table->get_col(innodb_pos++);
3607 ut_ad(!column->is_virtual());
3608
3609 #ifdef UNIV_DEBUG
3610 const char *name = table->col_names;
3611 for (uint32_t i = 0; i < innodb_pos - 1; ++i) {
3612 name += strlen(name) + 1;
3613 }
3614 ut_ad(col->name() == name);
3615 #endif /* UNIV_DEBUG */
3616
3617 const dd::Properties &private_data = col->se_private_data();
3618 if (!private_data.exists(
3619 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL]) &&
3620 !private_data.exists(
3621 dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
3622 continue;
3623 }
3624
3625 if (skip > 0) {
3626 --skip;
3627 continue;
3628 }
3629
3630 /* Note that it's before dict_table_add_to_cache(),
3631 don't worry about the dict_sys->size. */
3632 dd_parse_default_value(private_data, column, table->heap);
3633 }
3634
3635 #ifdef UNIV_DEBUG
3636 uint16_t n_default = 0;
3637 for (uint16_t i = 0; i < table->get_n_user_cols(); ++i) {
3638 if (table->get_col(i)->instant_default != nullptr) {
3639 ++n_default;
3640 }
3641 }
3642
3643 ut_ad(n_default + table->get_instant_cols() == table->get_n_user_cols());
3644 #endif /* UNIV_DEBUG */
3645 }
3646
3647 /** Instantiate in-memory InnoDB table metadata (dict_table_t),
3648 without any indexes.
3649 @tparam Table dd::Table or dd::Partition
3650 @param[in] dd_tab Global Data Dictionary metadata,
3651 or NULL for internal temporary table
3652 @param[in] m_form MySQL TABLE for current table
3653 @param[in] norm_name normalized table name
3654 @param[in] create_info create info
3655 @param[in] zip_allowed whether ROW_FORMAT=COMPRESSED is OK
3656 @param[in] strict whether to use innodb_strict_mode=ON
3657 @param[in] m_thd thread THD
3658 @param[in] is_implicit if it is an implicit tablespace
3659 @return created dict_table_t on success or nullptr */
3660 template <typename Table>
dd_fill_dict_table(const Table * dd_tab,const TABLE * m_form,const char * norm_name,HA_CREATE_INFO * create_info,bool zip_allowed,bool strict,THD * m_thd,bool is_implicit)3661 static inline dict_table_t *dd_fill_dict_table(const Table *dd_tab,
3662 const TABLE *m_form,
3663 const char *norm_name,
3664 HA_CREATE_INFO *create_info,
3665 bool zip_allowed, bool strict,
3666 THD *m_thd, bool is_implicit) {
3667 mem_heap_t *heap;
3668 bool is_encrypted = false;
3669 bool is_discard = false;
3670
3671 ut_ad(dd_tab != nullptr);
3672 ut_ad(m_thd != nullptr);
3673 ut_ad(norm_name != nullptr);
3674 ut_ad(create_info == nullptr || m_form->s->row_type == create_info->row_type);
3675 ut_ad(create_info == nullptr ||
3676 m_form->s->key_block_size == create_info->key_block_size);
3677 ut_ad(dd_tab != nullptr);
3678
3679 if (m_form->s->fields > REC_MAX_N_USER_FIELDS) {
3680 my_error(ER_TOO_MANY_FIELDS, MYF(0));
3681 return (nullptr);
3682 }
3683
3684 /* Set encryption option for file-per-table tablespace. */
3685 dd::String_type encrypt;
3686 if (dd_tab->table().options().exists("encrypt_type")) {
3687 dd_tab->table().options().get("encrypt_type", &encrypt);
3688 if (!Encryption::is_none(encrypt.c_str())) {
3689 ut_ad(innobase_strcasecmp(encrypt.c_str(), "y") == 0);
3690 is_encrypted = true;
3691 }
3692 }
3693
3694 /* Check discard flag. */
3695 is_discard = dd_is_discarded(*dd_tab);
3696
3697 const unsigned n_mysql_cols = m_form->s->fields;
3698
3699 bool has_doc_id = false;
3700
3701 /* First check if dd::Table contains the right hidden column
3702 as FTS_DOC_ID */
3703 const dd::Column *doc_col;
3704 doc_col = dd_find_column(&dd_tab->table(), FTS_DOC_ID_COL_NAME);
3705
3706 /* Check weather this is a proper typed FTS_DOC_ID */
3707 if (doc_col && doc_col->type() == dd::enum_column_types::LONGLONG &&
3708 !doc_col->is_nullable()) {
3709 has_doc_id = true;
3710 }
3711
3712 const bool fulltext =
3713 dd_tab != nullptr && dd_table_contains_fulltext(dd_tab->table());
3714
3715 /* If there is a fulltext index, then it must have a FTS_DOC_ID */
3716 if (fulltext) {
3717 ut_ad(has_doc_id);
3718 }
3719
3720 bool add_doc_id = false;
3721
3722 /* Need to add FTS_DOC_ID column if it is not defined by user,
3723 since TABLE_SHARE::fields does not contain it if it is a hidden col */
3724 if (has_doc_id && doc_col->is_se_hidden()) {
3725 #ifdef UNIV_DEBUG
3726 ulint doc_id_col;
3727 ut_ad(!create_table_check_doc_id_col(m_thd, m_form, &doc_id_col));
3728 #endif
3729 add_doc_id = true;
3730 }
3731
3732 const unsigned n_cols = n_mysql_cols + add_doc_id;
3733
3734 bool is_redundant;
3735 bool blob_prefix;
3736 ulint zip_ssize;
3737 row_type real_type = ROW_TYPE_NOT_USED;
3738
3739 if (dd_table_is_partitioned(dd_tab->table())) {
3740 const dd::Properties &part_p = dd_tab->se_private_data();
3741 if (part_p.exists(dd_partition_key_strings[DD_PARTITION_ROW_FORMAT])) {
3742 dd::Table::enum_row_format format;
3743 part_p.get(dd_partition_key_strings[DD_PARTITION_ROW_FORMAT],
3744 reinterpret_cast<uint32 *>(&format));
3745 switch (format) {
3746 case dd::Table::RF_REDUNDANT:
3747 real_type = ROW_TYPE_REDUNDANT;
3748 break;
3749 case dd::Table::RF_COMPACT:
3750 real_type = ROW_TYPE_COMPACT;
3751 break;
3752 case dd::Table::RF_COMPRESSED:
3753 real_type = ROW_TYPE_COMPRESSED;
3754 break;
3755 case dd::Table::RF_DYNAMIC:
3756 real_type = ROW_TYPE_DYNAMIC;
3757 break;
3758 default:
3759 ut_ad(0);
3760 }
3761 }
3762 }
3763
3764 /* Validate the table format options */
3765 if (format_validate(m_thd, m_form, real_type, zip_allowed, strict,
3766 &is_redundant, &blob_prefix, &zip_ssize, is_implicit)) {
3767 return (nullptr);
3768 }
3769
3770 ulint n_v_cols = 0;
3771 ulint n_m_v_cols = 0;
3772
3773 /* Find out the number of virtual columns */
3774 for (ulint i = 0; i < m_form->s->fields; i++) {
3775 Field *field = m_form->field[i];
3776
3777 ut_ad(!(!innobase_is_v_fld(field) && innobase_is_multi_value_fld(field)));
3778
3779 if (innobase_is_v_fld(field)) {
3780 n_v_cols++;
3781
3782 if (innobase_is_multi_value_fld(field)) {
3783 n_m_v_cols++;
3784 }
3785 }
3786 }
3787
3788 ut_ad(n_v_cols <= n_cols);
3789
3790 /* Create the dict_table_t */
3791 dict_table_t *m_table =
3792 dict_mem_table_create(norm_name, 0, n_cols, n_v_cols, n_m_v_cols, 0, 0);
3793
3794 /* Set up the field in the newly allocated dict_table_t */
3795 m_table->id = dd_tab->se_private_id();
3796
3797 if (dd_tab->se_private_data().exists(
3798 dd_table_key_strings[DD_TABLE_DATA_DIRECTORY])) {
3799 m_table->flags |= DICT_TF_MASK_DATA_DIR;
3800 }
3801
3802 /* If the table has instantly added columns, it's necessary to read
3803 the number of instant columns for either normal table(from dd::Table),
3804 or partitioned table(from dd::Partition). One partition may have no
3805 instant columns, which is fine. */
3806 if (dd_table_has_instant_cols(dd_tab->table())) {
3807 uint32_t instant_cols;
3808
3809 if (!dd_table_is_partitioned(dd_tab->table())) {
3810 const dd::Properties &table_private = dd_tab->table().se_private_data();
3811 table_private.get(dd_table_key_strings[DD_TABLE_INSTANT_COLS],
3812 &instant_cols);
3813 m_table->set_instant_cols(instant_cols);
3814 ut_ad(m_table->has_instant_cols());
3815 } else if (dd_part_has_instant_cols(
3816 *reinterpret_cast<const dd::Partition *>(dd_tab))) {
3817 dd_tab->se_private_data().get(
3818 dd_partition_key_strings[DD_PARTITION_INSTANT_COLS], &instant_cols);
3819
3820 m_table->set_instant_cols(instant_cols);
3821 ut_ad(m_table->has_instant_cols());
3822 }
3823 }
3824
3825 /* Check if this table is FTS AUX table, if so, set DICT_TF2_AUX flag */
3826 fts_aux_table_t aux_table;
3827 if (fts_is_aux_table_name(&aux_table, norm_name, strlen(norm_name))) {
3828 DICT_TF2_FLAG_SET(m_table, DICT_TF2_AUX);
3829 m_table->parent_id = aux_table.parent_id;
3830 }
3831
3832 if (is_discard) {
3833 m_table->ibd_file_missing = true;
3834 m_table->flags2 |= DICT_TF2_DISCARDED;
3835 }
3836
3837 if (!is_redundant) {
3838 m_table->flags |= DICT_TF_COMPACT;
3839 }
3840
3841 if (is_implicit) {
3842 m_table->flags2 |= DICT_TF2_USE_FILE_PER_TABLE;
3843 } else {
3844 m_table->flags |= (1 << DICT_TF_POS_SHARED_SPACE);
3845 }
3846
3847 if (!blob_prefix) {
3848 m_table->flags |= (1 << DICT_TF_POS_ATOMIC_BLOBS);
3849 }
3850
3851 if (zip_ssize != 0) {
3852 m_table->flags |= (zip_ssize << DICT_TF_POS_ZIP_SSIZE);
3853 }
3854
3855 m_table->fts = nullptr;
3856 if (has_doc_id) {
3857 if (fulltext) {
3858 DICT_TF2_FLAG_SET(m_table, DICT_TF2_FTS);
3859 }
3860
3861 if (add_doc_id) {
3862 DICT_TF2_FLAG_SET(m_table, DICT_TF2_FTS_HAS_DOC_ID);
3863 }
3864
3865 if (fulltext || add_doc_id) {
3866 m_table->fts = fts_create(m_table);
3867 m_table->fts->cache = fts_cache_create(m_table);
3868 }
3869 }
3870
3871 bool is_temp = (m_form->s->tmp_table_def != nullptr);
3872 if (is_temp) {
3873 m_table->flags2 |= DICT_TF2_TEMPORARY;
3874 }
3875
3876 if (is_encrypted) {
3877 /* We don't support encrypt intrinsic and temporary table. */
3878 ut_ad(!m_table->is_intrinsic() && !m_table->is_temporary());
3879 /* This flag will be used to set file-per-table tablespace
3880 encryption flag */
3881 DICT_TF2_FLAG_SET(m_table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
3882 }
3883
3884 heap = mem_heap_create(1000);
3885
3886 /* Fill out each column info */
3887 for (unsigned i = 0; i < n_mysql_cols; i++) {
3888 const Field *field = m_form->field[i];
3889 ulint prtype = 0;
3890 unsigned col_len = field->pack_length();
3891
3892 /* The MySQL type code has to fit in 8 bits
3893 in the metadata stored in the InnoDB change buffer. */
3894 ut_ad(field->charset() == nullptr ||
3895 field->charset()->number <= MAX_CHAR_COLL_NUM);
3896 ut_ad(field->charset() == nullptr || field->charset()->number > 0);
3897
3898 ulint nulls_allowed;
3899 ulint unsigned_type;
3900 ulint binary_type;
3901 ulint long_true_varchar;
3902 ulint charset_no;
3903 ulint mtype = get_innobase_type_from_mysql_type(&unsigned_type, field);
3904
3905 nulls_allowed = field->is_nullable() ? 0 : DATA_NOT_NULL;
3906
3907 /* Convert non nullable fields in FTS AUX tables as nullable.
3908 This is because in 5.7, we created FTS AUX tables clustered
3909 index with nullable field, although NULLS are not inserted.
3910 When fields are nullable, the record layout is dependent on
3911 that. When registering FTS AUX Tables with new DD, we cannot
3912 register nullable fields as part of Primary Key. Hence we register
3913 them as non-nullabe in DD but treat as nullable in InnoDB.
3914 This way the compatibility with 5.7 FTS AUX tables is also
3915 maintained. */
3916 if (m_table->is_fts_aux()) {
3917 const dd::Table &dd_table = dd_tab->table();
3918 const dd::Column *dd_col = dd_find_column(&dd_table, field->field_name);
3919 const dd::Properties &p = dd_col->se_private_data();
3920 if (p.exists("nullable")) {
3921 bool nullable;
3922 p.get("nullable", &nullable);
3923 nulls_allowed = nullable ? 0 : DATA_NOT_NULL;
3924 }
3925 }
3926
3927 binary_type = field->binary() ? DATA_BINARY_TYPE : 0;
3928
3929 charset_no = 0;
3930 if (dtype_is_string_type(mtype)) {
3931 charset_no = static_cast<ulint>(field->charset()->number);
3932 }
3933
3934 long_true_varchar = 0;
3935 if (field->type() == MYSQL_TYPE_VARCHAR) {
3936 col_len -= field->get_length_bytes();
3937
3938 if (field->get_length_bytes() == 2) {
3939 long_true_varchar = DATA_LONG_TRUE_VARCHAR;
3940 }
3941 }
3942
3943 ulint is_virtual = (innobase_is_v_fld(field)) ? DATA_VIRTUAL : 0;
3944
3945 ulint is_multi_val =
3946 innobase_is_multi_value_fld(field) ? DATA_MULTI_VALUE : 0;
3947
3948 bool is_stored = innobase_is_s_fld(field);
3949
3950 if (is_multi_val) {
3951 col_len = field->key_length();
3952 }
3953
3954 if (!is_virtual) {
3955 prtype =
3956 dtype_form_prtype((ulint)field->type() | nulls_allowed |
3957 unsigned_type | binary_type | long_true_varchar,
3958 charset_no);
3959 dict_mem_table_add_col(m_table, heap, field->field_name, mtype, prtype,
3960 col_len, !field->is_hidden_from_user());
3961 } else {
3962 prtype = dtype_form_prtype(
3963 (ulint)field->type() | nulls_allowed | unsigned_type | binary_type |
3964 long_true_varchar | is_virtual | is_multi_val,
3965 charset_no);
3966 dict_mem_table_add_v_col(m_table, heap, field->field_name, mtype, prtype,
3967 col_len, i,
3968 field->gcol_info->non_virtual_base_columns(),
3969 !field->is_hidden_from_user());
3970 }
3971
3972 if (is_stored) {
3973 ut_ad(!is_virtual);
3974 /* Added stored column in m_s_cols list. */
3975 dict_mem_table_add_s_col(m_table,
3976 field->gcol_info->non_virtual_base_columns());
3977 }
3978 }
3979
3980 ulint j = 0;
3981
3982 /* For each virtual column, we will need to set up its base column
3983 info */
3984 if (m_table->n_v_cols > 0) {
3985 for (unsigned i = 0; i < n_mysql_cols; i++) {
3986 dict_v_col_t *v_col;
3987
3988 Field *field = m_form->field[i];
3989
3990 if (!innobase_is_v_fld(field)) {
3991 continue;
3992 }
3993
3994 v_col = dict_table_get_nth_v_col(m_table, j);
3995
3996 j++;
3997
3998 innodb_base_col_setup(m_table, field, v_col);
3999 }
4000 }
4001
4002 if (add_doc_id) {
4003 /* Add the hidden FTS_DOC_ID column. */
4004 fts_add_doc_id_column(m_table, heap);
4005 }
4006
4007 /* Add system columns to make adding index work */
4008 dict_table_add_system_columns(m_table, heap);
4009
4010 if (m_table->has_instant_cols()) {
4011 dd_fill_instant_columns(dd_tab->table(), m_table);
4012 }
4013
4014 mem_heap_free(heap);
4015
4016 return (m_table);
4017 }
4018
4019 /* Create metadata for specified tablespace, acquiring exlcusive MDL first
4020 @param[in,out] dd_client data dictionary client
4021 @param[in,out] thd THD
4022 @param[in,out] dd_space_name dd tablespace name
4023 @param[in] space InnoDB tablespace ID
4024 @param[in] flags InnoDB tablespace flags
4025 @param[in] filename filename of this tablespace
4026 @param[in] discarded true if this tablespace was discarded
4027 @param[in,out] dd_space_id dd_space_id
4028 @retval false on success
4029 @retval true on failure */
dd_create_tablespace(dd::cache::Dictionary_client * dd_client,THD * thd,const char * dd_space_name,space_id_t space_id,uint32_t flags,const char * filename,bool discarded,dd::Object_id & dd_space_id)4030 bool dd_create_tablespace(dd::cache::Dictionary_client *dd_client, THD *thd,
4031 const char *dd_space_name, space_id_t space_id,
4032 uint32_t flags, const char *filename, bool discarded,
4033 dd::Object_id &dd_space_id) {
4034 std::unique_ptr<dd::Tablespace> dd_space(dd::create_object<dd::Tablespace>());
4035
4036 if (dd_space_name != nullptr) {
4037 dd_space->set_name(dd_space_name);
4038 }
4039
4040 if (dd_tablespace_get_mdl(dd_space->name().c_str())) {
4041 return (true);
4042 }
4043
4044 dd_space->set_engine(innobase_hton_name);
4045 dd::Properties &p = dd_space->se_private_data();
4046 p.set(dd_space_key_strings[DD_SPACE_ID], static_cast<uint32>(space_id));
4047 p.set(dd_space_key_strings[DD_SPACE_FLAGS], static_cast<uint32>(flags));
4048 p.set(dd_space_key_strings[DD_SPACE_SERVER_VERSION],
4049 DD_SPACE_CURRENT_SRV_VERSION);
4050 p.set(dd_space_key_strings[DD_SPACE_VERSION], DD_SPACE_CURRENT_SPACE_VERSION);
4051
4052 dd_space_states state =
4053 (fsp_is_undo_tablespace(space_id)
4054 ? DD_SPACE_STATE_ACTIVE
4055 : (discarded ? DD_SPACE_STATE_DISCARDED : DD_SPACE_STATE_NORMAL));
4056 p.set(dd_space_key_strings[DD_SPACE_STATE], dd_space_state_values[state]);
4057
4058 dd::Tablespace_file *dd_file = dd_space->add_file();
4059 dd_file->set_filename(filename);
4060 dd_file->se_private_data().set(dd_space_key_strings[DD_SPACE_ID],
4061 static_cast<uint32>(space_id));
4062
4063 dd::Properties &toptions = dd_space->options();
4064 if (!FSP_FLAGS_GET_ENCRYPTION(flags)) {
4065 /* Update DD Option value, for Unencryption */
4066 toptions.set("encryption", "N");
4067
4068 } else {
4069 /* Update DD Option value, for Encryption */
4070 toptions.set("encryption", "Y");
4071 }
4072
4073 if (dd_client->store(dd_space.get())) {
4074 return (true);
4075 }
4076
4077 dd_space_id = dd_space.get()->id();
4078
4079 return (false);
4080 }
4081
4082 /** Create metadata for implicit tablespace
4083 @param[in,out] dd_client data dictionary client
4084 @param[in,out] thd THD
4085 @param[in] space_id InnoDB tablespace ID
4086 @param[in] space_name tablespace name to be set for the
4087 newly created tablespace
4088 @param[in] filename tablespace filename
4089 @param[in] discarded true if this tablespace was discarded
4090 @param[in,out] dd_space_id dd tablespace id
4091 @retval false on success
4092 @retval true on failure */
dd_create_implicit_tablespace(dd::cache::Dictionary_client * dd_client,THD * thd,space_id_t space_id,const char * space_name,const char * filename,bool discarded,dd::Object_id & dd_space_id)4093 bool dd_create_implicit_tablespace(dd::cache::Dictionary_client *dd_client,
4094 THD *thd, space_id_t space_id,
4095 const char *space_name, const char *filename,
4096 bool discarded, dd::Object_id &dd_space_id) {
4097 fil_space_t *space = fil_space_get(space_id);
4098 uint32_t flags = space->flags;
4099
4100 std::string tsn(space_name);
4101 dict_name::convert_to_space(tsn);
4102
4103 bool fail = dd_create_tablespace(dd_client, thd, tsn.c_str(), space_id, flags,
4104 filename, discarded, dd_space_id);
4105
4106 return (fail);
4107 }
4108
4109 /** Drop a tablespace
4110 @param[in,out] dd_client data dictionary client
4111 @param[in,out] thd THD object
4112 @param[in] dd_space_id dd tablespace id
4113 @retval false On success
4114 @retval true On failure */
dd_drop_tablespace(dd::cache::Dictionary_client * dd_client,THD * thd,dd::Object_id dd_space_id)4115 bool dd_drop_tablespace(dd::cache::Dictionary_client *dd_client, THD *thd,
4116 dd::Object_id dd_space_id) {
4117 dd::Tablespace *dd_space = nullptr;
4118
4119 if (dd_client->acquire_uncached_uncommitted(dd_space_id, &dd_space) ||
4120 dd_space == nullptr) {
4121 my_error(ER_INTERNAL_ERROR, MYF(0),
4122 " InnoDB can't get tablespace object"
4123 " for space ",
4124 dd_space_id);
4125
4126 return (true);
4127 }
4128
4129 ut_a(dd_space != nullptr);
4130
4131 if (dd_tablespace_get_mdl(dd_space->name().c_str())) {
4132 my_error(ER_INTERNAL_ERROR, MYF(0),
4133 " InnoDB can't set exclusive MDL on"
4134 " tablespace ",
4135 dd_space->name().c_str());
4136
4137 return (true);
4138 }
4139
4140 bool error = dd_client->drop(dd_space);
4141 DBUG_EXECUTE_IF("fail_while_dropping_dd_object", error = true;);
4142
4143 if (error) {
4144 my_error(ER_INTERNAL_ERROR, MYF(0), " InnoDB can't drop tablespace object",
4145 dd_space->name().c_str());
4146 }
4147
4148 return (error);
4149 }
4150
4151 /** Determine if a tablespace is implicit.
4152 @param[in,out] client data dictionary client
4153 @param[in] dd_space_id dd tablespace id
4154 @param[out] implicit whether the tablespace is implicit tablespace
4155 @param[out] dd_space DD space object
4156 @retval false on success
4157 @retval true on failure */
dd_tablespace_is_implicit(dd::cache::Dictionary_client * client,dd::Object_id dd_space_id,bool * implicit,dd::Tablespace ** dd_space)4158 bool dd_tablespace_is_implicit(dd::cache::Dictionary_client *client,
4159 dd::Object_id dd_space_id, bool *implicit,
4160 dd::Tablespace **dd_space) {
4161 space_id_t id = 0;
4162 uint32 flags;
4163
4164 const bool fail = client->acquire_uncached_uncommitted<dd::Tablespace>(
4165 dd_space_id, dd_space) ||
4166 (*dd_space) == nullptr ||
4167 (*dd_space)->se_private_data().get(
4168 dd_space_key_strings[DD_SPACE_ID], &id);
4169
4170 if (!fail) {
4171 (*dd_space)->se_private_data().get(dd_space_key_strings[DD_SPACE_FLAGS],
4172 &flags);
4173 *implicit = fsp_is_file_per_table(id, flags);
4174 }
4175
4176 return (fail);
4177 }
4178
4179 /** Load foreign key constraint info for the dd::Table object.
4180 @param[out] m_table InnoDB table handle
4181 @param[in] dd_table Global DD table
4182 @param[in] col_names column names, or NULL
4183 @param[in] ignore_err DICT_ERR_IGNORE_FK_NOKEY or DICT_ERR_IGNORE_NONE
4184 @param[in] dict_locked True if dict_sys->mutex is already held,
4185 otherwise false
4186 @return DB_SUCCESS if successfully load FK constraint */
dd_table_load_fk_from_dd(dict_table_t * m_table,const dd::Table * dd_table,const char ** col_names,dict_err_ignore_t ignore_err,bool dict_locked)4187 dberr_t dd_table_load_fk_from_dd(dict_table_t *m_table,
4188 const dd::Table *dd_table,
4189 const char **col_names,
4190 dict_err_ignore_t ignore_err,
4191 bool dict_locked) {
4192 dberr_t err = DB_SUCCESS;
4193
4194 /* Now fill in the foreign key info */
4195 for (const dd::Foreign_key *key : dd_table->foreign_keys()) {
4196 char buf[MAX_FULL_NAME_LEN + 1];
4197
4198 if (*(key->name().c_str()) == '#' && *(key->name().c_str() + 1) == 'f') {
4199 continue;
4200 }
4201
4202 dd::String_type db_name = key->referenced_table_schema_name();
4203 dd::String_type tb_name = key->referenced_table_name();
4204
4205 bool truncated;
4206 build_table_filename(buf, sizeof(buf), db_name.c_str(), tb_name.c_str(),
4207 nullptr, 0, &truncated);
4208
4209 char norm_name[FN_REFLEN * 2];
4210
4211 if (truncated || !normalize_table_name(norm_name, buf)) {
4212 /* purecov: begin inspected */
4213 ut_ad(false);
4214 return (DB_TOO_LONG_PATH);
4215 /* purecov: end */
4216 }
4217
4218 dict_foreign_t *foreign = dict_mem_foreign_create();
4219 foreign->foreign_table_name =
4220 mem_heap_strdup(foreign->heap, m_table->name.m_name);
4221
4222 dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
4223
4224 if (innobase_get_lower_case_table_names() == 2) {
4225 innobase_casedn_str(norm_name);
4226 } else {
4227 #ifndef _WIN32
4228 if (innobase_get_lower_case_table_names() == 1) {
4229 innobase_casedn_str(norm_name);
4230 }
4231 #endif /* !_WIN32 */
4232 }
4233
4234 foreign->referenced_table_name = mem_heap_strdup(foreign->heap, norm_name);
4235 dict_mem_referenced_table_name_lookup_set(foreign, TRUE);
4236 ulint db_len = dict_get_db_name_len(m_table->name.m_name);
4237
4238 ut_ad(db_len > 0);
4239
4240 memcpy(buf, m_table->name.m_name, db_len);
4241
4242 buf[db_len] = '\0';
4243
4244 snprintf(norm_name, sizeof norm_name, "%s/%s", buf, key->name().c_str());
4245
4246 foreign->id = mem_heap_strdup(foreign->heap, norm_name);
4247
4248 switch (key->update_rule()) {
4249 case dd::Foreign_key::RULE_NO_ACTION:
4250 /*
4251 Since SET DEFAULT clause is not supported, ignore it by converting
4252 into the value DICT_FOREIGN_ON_UPDATE_NO_ACTION
4253 */
4254 case dd::Foreign_key::RULE_SET_DEFAULT:
4255 foreign->type = DICT_FOREIGN_ON_UPDATE_NO_ACTION;
4256 break;
4257 case dd::Foreign_key::RULE_RESTRICT:
4258 foreign->type = 0;
4259 break;
4260 case dd::Foreign_key::RULE_CASCADE:
4261 foreign->type = DICT_FOREIGN_ON_UPDATE_CASCADE;
4262 break;
4263 case dd::Foreign_key::RULE_SET_NULL:
4264 foreign->type = DICT_FOREIGN_ON_UPDATE_SET_NULL;
4265 break;
4266 default:
4267 ut_ad(0);
4268 }
4269
4270 switch (key->delete_rule()) {
4271 case dd::Foreign_key::RULE_NO_ACTION:
4272 /*
4273 Since SET DEFAULT clause is not supported, ignore it by converting
4274 into the value DICT_FOREIGN_ON_UPDATE_NO_ACTION
4275 */
4276 case dd::Foreign_key::RULE_SET_DEFAULT:
4277 foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
4278 case dd::Foreign_key::RULE_RESTRICT:
4279 break;
4280 case dd::Foreign_key::RULE_CASCADE:
4281 foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
4282 break;
4283 case dd::Foreign_key::RULE_SET_NULL:
4284 foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
4285 break;
4286 default:
4287 ut_ad(0);
4288 }
4289
4290 foreign->n_fields = key->elements().size();
4291
4292 foreign->foreign_col_names = static_cast<const char **>(
4293 mem_heap_alloc(foreign->heap, foreign->n_fields * sizeof(void *)));
4294
4295 foreign->referenced_col_names = static_cast<const char **>(
4296 mem_heap_alloc(foreign->heap, foreign->n_fields * sizeof(void *)));
4297
4298 ulint num_ref = 0;
4299
4300 for (const dd::Foreign_key_element *key_e : key->elements()) {
4301 dd::String_type ref_col_name = key_e->referenced_column_name();
4302
4303 foreign->referenced_col_names[num_ref] =
4304 mem_heap_strdup(foreign->heap, ref_col_name.c_str());
4305 ut_ad(ref_col_name.c_str());
4306
4307 const dd::Column *f_col = &key_e->column();
4308 foreign->foreign_col_names[num_ref] =
4309 mem_heap_strdup(foreign->heap, f_col->name().c_str());
4310 num_ref++;
4311 }
4312
4313 if (!dict_locked) {
4314 mutex_enter(&dict_sys->mutex);
4315 }
4316 #ifdef UNIV_DEBUG
4317 dict_table_t *for_table;
4318
4319 for_table =
4320 dict_table_check_if_in_cache_low(foreign->foreign_table_name_lookup);
4321
4322 ut_ad(for_table);
4323 #endif
4324 /* Fill in foreign->foreign_table and index, then add to
4325 dict_table_t */
4326 err =
4327 dict_foreign_add_to_cache(foreign, col_names, FALSE, true, ignore_err);
4328 if (!dict_locked) {
4329 mutex_exit(&dict_sys->mutex);
4330 }
4331
4332 if (err != DB_SUCCESS) {
4333 break;
4334 }
4335
4336 /* Set up the FK virtual column info */
4337 dict_mem_table_free_foreign_vcol_set(m_table);
4338 dict_mem_table_fill_foreign_vcol_set(m_table);
4339 }
4340 return (err);
4341 }
4342
4343 /** Load foreign key constraint for the table. Note, it could also open
4344 the foreign table, if this table is referenced by the foreign table
4345 @param[in,out] client data dictionary client
4346 @param[in] tbl_name Table Name
4347 @param[in] col_names column names, or NULL
4348 @param[out] m_table InnoDB table handle
4349 @param[in] dd_table Global DD table
4350 @param[in] thd thread THD
4351 @param[in] dict_locked True if dict_sys->mutex is already held,
4352 otherwise false
4353 @param[in] check_charsets whether to check charset compatibility
4354 @param[in,out] fk_tables name list for tables that refer to this table
4355 @return DB_SUCCESS if successfully load FK constraint */
dd_table_load_fk(dd::cache::Dictionary_client * client,const char * tbl_name,const char ** col_names,dict_table_t * m_table,const dd::Table * dd_table,THD * thd,bool dict_locked,bool check_charsets,dict_names_t * fk_tables)4356 dberr_t dd_table_load_fk(dd::cache::Dictionary_client *client,
4357 const char *tbl_name, const char **col_names,
4358 dict_table_t *m_table, const dd::Table *dd_table,
4359 THD *thd, bool dict_locked, bool check_charsets,
4360 dict_names_t *fk_tables) {
4361 dberr_t err = DB_SUCCESS;
4362 dict_err_ignore_t ignore_err = DICT_ERR_IGNORE_NONE;
4363
4364 /* Check whether FOREIGN_KEY_CHECKS is set to 0. If so, the table
4365 can be opened even if some FK indexes are missing. If not, the table
4366 can't be opened in the same situation */
4367 if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
4368 ignore_err = DICT_ERR_IGNORE_FK_NOKEY;
4369 }
4370
4371 err = dd_table_load_fk_from_dd(m_table, dd_table, col_names, ignore_err,
4372 dict_locked);
4373
4374 if (err != DB_SUCCESS) {
4375 return (err);
4376 }
4377
4378 if (dict_locked) {
4379 mutex_exit(&dict_sys->mutex);
4380 }
4381
4382 DBUG_EXECUTE_IF("enable_stack_overrun_post_alter_commit",
4383 { DBUG_SET("+d,simulate_stack_overrun"); });
4384 err = dd_table_check_for_child(client, tbl_name, col_names, m_table, dd_table,
4385 thd, check_charsets, ignore_err, fk_tables);
4386 DBUG_EXECUTE_IF("enable_stack_overrun_post_alter_commit",
4387 { DBUG_SET("-d,simulate_stack_overrun"); });
4388
4389 if (dict_locked) {
4390 mutex_enter(&dict_sys->mutex);
4391 }
4392
4393 return (err);
4394 }
4395
4396 /** Load foreign key constraint for the table. Note, it could also open
4397 the foreign table, if this table is referenced by the foreign table
4398 @param[in,out] client data dictionary client
4399 @param[in] tbl_name Table Name
4400 @param[in] col_names column names, or NULL
4401 @param[out] m_table InnoDB table handle
4402 @param[in] dd_table Global DD table
4403 @param[in] thd thread THD
4404 @param[in] check_charsets whether to check charset compatibility
4405 @param[in] ignore_err DICT_ERR_IGNORE_FK_NOKEY or DICT_ERR_IGNORE_NONE
4406 @param[in,out] fk_tables name list for tables that refer to this table
4407 @return DB_SUCCESS if successfully load FK constraint */
dd_table_check_for_child(dd::cache::Dictionary_client * client,const char * tbl_name,const char ** col_names,dict_table_t * m_table,const dd::Table * dd_table,THD * thd,bool check_charsets,dict_err_ignore_t ignore_err,dict_names_t * fk_tables)4408 dberr_t dd_table_check_for_child(dd::cache::Dictionary_client *client,
4409 const char *tbl_name, const char **col_names,
4410 dict_table_t *m_table,
4411 const dd::Table *dd_table, THD *thd,
4412 bool check_charsets,
4413 dict_err_ignore_t ignore_err,
4414 dict_names_t *fk_tables) {
4415 dberr_t err = DB_SUCCESS;
4416
4417 /* TODO: NewDD: Temporary ignore DD system table until
4418 WL#6049 inplace */
4419 if (!dict_sys_t::is_dd_table_id(m_table->id) && fk_tables != nullptr) {
4420 std::vector<dd::String_type> child_schema;
4421 std::vector<dd::String_type> child_name;
4422 std::string db_str;
4423 std::string tbl_str;
4424
4425 dict_name::get_table(m_table->name.m_name, db_str, tbl_str);
4426
4427 if (client->fetch_fk_children_uncached(db_str.c_str(), tbl_str.c_str(),
4428 "InnoDB", false, &child_schema,
4429 &child_name)) {
4430 return (DB_ERROR);
4431 }
4432
4433 std::vector<dd::String_type>::iterator it = child_name.begin();
4434 for (auto &db_name : child_schema) {
4435 dd::String_type tb_name = *it;
4436
4437 char buf[2 * NAME_CHAR_LEN * 5 + 2 + 1];
4438 bool truncated;
4439 build_table_filename(buf, sizeof(buf), db_name.c_str(), tb_name.c_str(),
4440 nullptr, 0, &truncated);
4441
4442 char full_name[FN_REFLEN];
4443
4444 if (truncated || !normalize_table_name(full_name, buf)) {
4445 /* purecov: begin inspected */
4446 ut_ad(false);
4447 return (DB_TOO_LONG_PATH);
4448 /* purecov: end */
4449 }
4450
4451 if (innobase_get_lower_case_table_names() == 2) {
4452 innobase_casedn_str(full_name);
4453 } else {
4454 #ifndef _WIN32
4455 if (innobase_get_lower_case_table_names() == 1) {
4456 innobase_casedn_str(full_name);
4457 }
4458 #endif /* !_WIN32 */
4459 }
4460
4461 mutex_enter(&dict_sys->mutex);
4462
4463 /* Load the foreign table first */
4464 dict_table_t *foreign_table =
4465 dd_table_open_on_name_in_mem(full_name, true);
4466
4467 if (foreign_table) {
4468 for (auto &fk : foreign_table->foreign_set) {
4469 if (strcmp(fk->referenced_table_name, tbl_name) != 0) {
4470 continue;
4471 }
4472
4473 if (fk->referenced_table) {
4474 ut_ad(fk->referenced_table == m_table);
4475 } else {
4476 err = dict_foreign_add_to_cache(fk, col_names, check_charsets,
4477 false, ignore_err);
4478 if (err != DB_SUCCESS) {
4479 foreign_table->release();
4480 mutex_exit(&dict_sys->mutex);
4481 return (err);
4482 }
4483 }
4484 }
4485 foreign_table->release();
4486 } else {
4487 /* To avoid recursively loading the tables
4488 related through the foreign key constraints,
4489 the child table name is saved here. The child
4490 table will be loaded later, along with its
4491 foreign key constraint. */
4492 lint old_size = mem_heap_get_size(m_table->heap);
4493
4494 fk_tables->push_back(
4495 mem_heap_strdupl(m_table->heap, full_name, strlen(full_name)));
4496
4497 lint new_size = mem_heap_get_size(m_table->heap);
4498 dict_sys->size += new_size - old_size;
4499 }
4500
4501 mutex_exit(&dict_sys->mutex);
4502
4503 ut_ad(it != child_name.end());
4504 ++it;
4505 }
4506 }
4507
4508 return (err);
4509 }
4510
4511 /** Get tablespace name of dd::Table
4512 @tparam Table dd::Table or dd::Partition
4513 @param[in] dd_table dd table object
4514 @return the tablespace name or nullptr if failed */
4515 template <typename Table>
dd_table_get_space_name(const Table * dd_table)4516 const char *dd_table_get_space_name(const Table *dd_table) {
4517 dd::Tablespace *dd_space = nullptr;
4518 THD *thd = current_thd;
4519 const char *space_name;
4520
4521 DBUG_TRACE;
4522 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
4523
4524 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
4525 dd::cache::Dictionary_client::Auto_releaser releaser(client);
4526
4527 dd::Object_id dd_space_id = (*dd_table->indexes().begin())->tablespace_id();
4528
4529 if (client->acquire_uncached_uncommitted<dd::Tablespace>(dd_space_id,
4530 &dd_space) ||
4531 dd_space == nullptr) {
4532 ut_ad(false);
4533 return nullptr;
4534 }
4535
4536 space_name = dd_space->name().c_str();
4537
4538 return space_name;
4539 }
4540
4541 /** Get the first filepath from mysql.tablespace_datafiles
4542 for a given space_id.
4543 @tparam Table dd::Table or dd::Partition
4544 @param[in,out] heap heap for store file name.
4545 @param[in] table dict table
4546 @param[in] dd_table dd table obj
4547 @return First filepath (caller must invoke ut_free() on it)
4548 @retval nullptr if no mysql.tablespace_datafiles entry was found. */
4549 template <typename Table>
dd_get_first_path(mem_heap_t * heap,dict_table_t * table,Table * dd_table)4550 char *dd_get_first_path(mem_heap_t *heap, dict_table_t *table,
4551 Table *dd_table) {
4552 char *filepath = nullptr;
4553 dd::Tablespace *dd_space = nullptr;
4554 THD *thd = current_thd;
4555 MDL_ticket *mdl = nullptr;
4556 dd::Object_id dd_space_id;
4557
4558 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
4559 ut_ad(!mutex_own(&dict_sys->mutex));
4560
4561 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
4562 dd::cache::Dictionary_client::Auto_releaser releaser(client);
4563
4564 if (dd_table == nullptr) {
4565 std::string db_str;
4566 std::string tbl_str;
4567 dict_name::get_table(table->name.m_name, db_str, tbl_str);
4568
4569 if (db_str.empty() || tbl_str.empty() ||
4570 dd_mdl_acquire(thd, &mdl, db_str.c_str(), tbl_str.c_str())) {
4571 return (nullptr);
4572 }
4573
4574 const dd::Table *table_def = nullptr;
4575 if (client->acquire(db_str.c_str(), tbl_str.c_str(), &table_def) ||
4576 table_def == nullptr) {
4577 dd_mdl_release(thd, &mdl);
4578 return (nullptr);
4579 }
4580
4581 dd_space_id = dd_first_index(table_def)->tablespace_id();
4582
4583 dd_mdl_release(thd, &mdl);
4584 } else {
4585 dd_space_id = dd_first_index(dd_table)->tablespace_id();
4586 }
4587
4588 if (client->acquire_uncached_uncommitted<dd::Tablespace>(dd_space_id,
4589 &dd_space)) {
4590 ut_ad(false);
4591 return (nullptr);
4592 }
4593
4594 if (dd_space != nullptr) {
4595 dd::Tablespace_file *dd_file =
4596 const_cast<dd::Tablespace_file *>(*(dd_space->files().begin()));
4597
4598 filepath = mem_heap_strdup(heap, dd_file->filename().c_str());
4599
4600 return (filepath);
4601 }
4602
4603 return (nullptr);
4604 }
4605
4606 template <typename Table>
dd_get_and_save_data_dir_path(dict_table_t * table,const Table * dd_table,bool dict_mutex_own)4607 void dd_get_and_save_data_dir_path(dict_table_t *table, const Table *dd_table,
4608 bool dict_mutex_own) {
4609 mem_heap_t *heap = nullptr;
4610
4611 if (!DICT_TF_HAS_DATA_DIR(table->flags) || table->data_dir_path != nullptr) {
4612 return;
4613 }
4614
4615 char *path = fil_space_get_first_path(table->space);
4616
4617 if (path == nullptr) {
4618 heap = mem_heap_create(1000);
4619 if (dict_mutex_own) {
4620 dict_mutex_exit_for_mysql();
4621 }
4622 path = dd_get_first_path(heap, table, dd_table);
4623 if (dict_mutex_own) {
4624 dict_mutex_enter_for_mysql();
4625 }
4626 }
4627
4628 if (!dict_mutex_own) {
4629 dict_mutex_enter_for_mysql();
4630 }
4631
4632 if (path != nullptr) {
4633 dict_save_data_dir_path(table, path);
4634 }
4635
4636 if (!dict_mutex_own) {
4637 dict_mutex_exit_for_mysql();
4638 }
4639
4640 if (heap != nullptr) {
4641 mem_heap_free(heap);
4642 } else {
4643 ut_free(path);
4644 }
4645 }
4646
4647 template void dd_get_and_save_data_dir_path<dd::Table>(dict_table_t *,
4648 const dd::Table *, bool);
4649
4650 template void dd_get_and_save_data_dir_path<dd::Partition>(
4651 dict_table_t *, const dd::Partition *, bool);
4652
4653 /** Get the meta-data filename from the table name for a
4654 single-table tablespace.
4655 @param[in,out] table table object
4656 @param[in] dd_table DD table object
4657 @param[out] filename filename
4658 @param[in] max_len filename max length */
dd_get_meta_data_filename(dict_table_t * table,dd::Table * dd_table,char * filename,ulint max_len)4659 void dd_get_meta_data_filename(dict_table_t *table, dd::Table *dd_table,
4660 char *filename, ulint max_len) {
4661 /* Make sure the data_dir_path is set. */
4662 dd_get_and_save_data_dir_path(table, dd_table, false);
4663
4664 std::string path = dict_table_get_datadir(table);
4665
4666 auto filepath = Fil_path::make(path, table->name.m_name, CFG, true);
4667
4668 ut_a(max_len >= strlen(filepath) + 1);
4669
4670 strcpy(filename, filepath);
4671
4672 ut_free(filepath);
4673 }
4674
4675 /** Opens a tablespace for dd_load_table_one()
4676 @tparam Table dd::Table or dd::Partition
4677 @param[in,out] dd_table dd table
4678 @param[in,out] table A table that refers to the tablespace to
4679 open
4680 @param[in,out] heap A memory heap
4681 @param[in] expected_fsp_flags expected flags of tablespace to be
4682 loaded
4683 @param[in] ignore_err Whether to ignore an error. */
4684 template <typename Table>
dd_load_tablespace(const Table * dd_table,dict_table_t * table,mem_heap_t * heap,dict_err_ignore_t ignore_err,uint32_t expected_fsp_flags)4685 void dd_load_tablespace(const Table *dd_table, dict_table_t *table,
4686 mem_heap_t *heap, dict_err_ignore_t ignore_err,
4687 uint32_t expected_fsp_flags) {
4688 ut_ad(!table->is_temporary());
4689 ut_ad(mutex_own(&dict_sys->mutex));
4690
4691 /* The system and temporary tablespaces are preloaded and
4692 always available. */
4693 if (fsp_is_system_or_temp_tablespace(table->space)) {
4694 return;
4695 }
4696
4697 if (table->flags2 & DICT_TF2_DISCARDED) {
4698 ib::warn(ER_IB_MSG_171)
4699 << "Tablespace for table " << table->name << " is set as discarded.";
4700 table->ibd_file_missing = TRUE;
4701 return;
4702 }
4703
4704 /* A general tablespace name is not the same as the table name.
4705 Use the general tablespace name if it can be read from the
4706 dictionary, if not use 'innodb_general_##. */
4707 char *shared_space_name = nullptr;
4708 const char *space_name;
4709 std::string tablespace_name;
4710 const char *tbl_name;
4711
4712 if (DICT_TF_HAS_SHARED_SPACE(table->flags)) {
4713 if (table->space == dict_sys_t::s_space_id) {
4714 shared_space_name = mem_strdup(dict_sys_t::s_dd_space_name);
4715 } else if (srv_sys_tablespaces_open) {
4716 /* For avoiding deadlock, we need to exit
4717 dict_sys->mutex. */
4718 mutex_exit(&dict_sys->mutex);
4719 shared_space_name = mem_strdup(dd_table_get_space_name(dd_table));
4720 mutex_enter(&dict_sys->mutex);
4721 } else {
4722 /* Make the temporary tablespace name. */
4723 shared_space_name =
4724 static_cast<char *>(ut_malloc_nokey(strlen(general_space_name) + 20));
4725
4726 sprintf(shared_space_name, "%s_" ULINTPF, general_space_name,
4727 static_cast<ulint>(table->space));
4728 }
4729
4730 space_name = shared_space_name;
4731 tbl_name = space_name;
4732 } else {
4733 tbl_name = table->name.m_name;
4734
4735 tablespace_name.assign(tbl_name);
4736 dict_name::convert_to_space(tablespace_name);
4737 space_name = tablespace_name.c_str();
4738 }
4739
4740 /* The tablespace may already be open. */
4741 if (fil_space_exists_in_mem(table->space, space_name, false, true, heap,
4742 table->id)) {
4743 dd_get_and_save_data_dir_path(table, dd_table, true);
4744 ut_free(shared_space_name);
4745 return;
4746 }
4747
4748 if (!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)) {
4749 ib::error(ER_IB_MSG_172)
4750 << "Failed to find tablespace for table " << table->name
4751 << " in the cache. Attempting"
4752 " to load the tablespace with space id "
4753 << table->space;
4754 }
4755
4756 /* Try to get the filepath if this space_id is already open.
4757 If the filepath is not found, fil_ibd_open() will make a default
4758 filepath from the tablespace name */
4759 char *filepath = fil_space_get_first_path(table->space);
4760
4761 if (filepath != nullptr) {
4762 /* If space id is already open with a different space name, then skip
4763 loading the space. It can happen because DDL log recovery might not
4764 have happened yet. */
4765 table->ibd_file_missing = TRUE;
4766
4767 ut_free(shared_space_name);
4768 ut_free(filepath);
4769 return;
4770 }
4771
4772 ut_ad(filepath == nullptr);
4773
4774 /* If the space is not open yet, then try to open by dd path. If the file
4775 path is changed then boot_tablespaces() would always open the tablespace. */
4776 mutex_exit(&dict_sys->mutex);
4777 filepath = dd_get_first_path(heap, table, dd_table);
4778 mutex_enter(&dict_sys->mutex);
4779
4780 if (filepath == nullptr) {
4781 ib::warn(ER_IB_MSG_173) << "Could not find the filepath"
4782 << " for table " << table->name << ", space ID "
4783 << table->space << " in the data dictionary.";
4784 }
4785
4786 /* Try to open the tablespace. We set the 2nd param (fix_dict) to
4787 false because we do not have an x-lock on dict_operation_lock */
4788 dberr_t err =
4789 fil_ibd_open(true, FIL_TYPE_TABLESPACE, table->space, expected_fsp_flags,
4790 space_name, tbl_name, filepath, true, false);
4791
4792 if (err == DB_SUCCESS) {
4793 /* This will set the DATA DIRECTORY for SHOW CREATE TABLE. */
4794 dd_get_and_save_data_dir_path(table, dd_table, true);
4795
4796 } else {
4797 /* We failed to find a sensible tablespace file */
4798 table->ibd_file_missing = TRUE;
4799 }
4800
4801 ut_free(shared_space_name);
4802 }
4803
4804 /** Get the space name from mysql.tablespaces for a given space_id.
4805 @tparam Table dd::Table or dd::Partition
4806 @param[in,out] heap heap for store file name.
4807 @param[in] table dict table
4808 @param[in] dd_table dd table obj
4809 @return First filepath (caller must invoke ut_free() on it)
4810 @retval nullptr if no mysql.tablespace_datafiles entry was found. */
4811 template <typename Table>
dd_space_get_name(mem_heap_t * heap,dict_table_t * table,Table * dd_table)4812 char *dd_space_get_name(mem_heap_t *heap, dict_table_t *table,
4813 Table *dd_table) {
4814 dd::Object_id dd_space_id;
4815 THD *thd = current_thd;
4816 dd::Tablespace *dd_space = nullptr;
4817
4818 ut_ad(srv_shutdown_state.load() < SRV_SHUTDOWN_DD);
4819 ut_ad(!mutex_own(&dict_sys->mutex));
4820
4821 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
4822 dd::cache::Dictionary_client::Auto_releaser releaser(client);
4823
4824 if (dd_table == nullptr) {
4825 std::string db_str;
4826 std::string tbl_str;
4827 dict_name::get_table(table->name.m_name, db_str, tbl_str);
4828
4829 MDL_ticket *mdl = nullptr;
4830 if (db_str.empty() || tbl_str.empty() ||
4831 dd_mdl_acquire(thd, &mdl, db_str.c_str(), tbl_str.c_str())) {
4832 return (nullptr);
4833 }
4834
4835 const dd::Table *table_def = nullptr;
4836 if (client->acquire(db_str.c_str(), tbl_str.c_str(), &table_def) ||
4837 table_def == nullptr) {
4838 dd_mdl_release(thd, &mdl);
4839 return (nullptr);
4840 }
4841
4842 dd_space_id = dd_first_index(table_def)->tablespace_id();
4843
4844 dd_mdl_release(thd, &mdl);
4845 } else {
4846 dd_space_id = dd_first_index(dd_table)->tablespace_id();
4847 }
4848
4849 if (client->acquire_uncached_uncommitted<dd::Tablespace>(dd_space_id,
4850 &dd_space) ||
4851 dd_space == nullptr) {
4852 ut_ad(false);
4853 return (nullptr);
4854 }
4855
4856 return (mem_heap_strdup(heap, dd_space->name().c_str()));
4857 }
4858
4859 /** Make sure the tablespace name is saved in dict_table_t if the table
4860 uses a general tablespace.
4861 Try to read it from the fil_system_t first, then from DD.
4862 @param[in] table Table object
4863 @param[in] dd_table Global DD table or partition object
4864 @param[in] dict_mutex_own true if dict_sys->mutex is owned already */
4865 template <typename Table>
dd_get_and_save_space_name(dict_table_t * table,const Table * dd_table,bool dict_mutex_own)4866 void dd_get_and_save_space_name(dict_table_t *table, const Table *dd_table,
4867 bool dict_mutex_own) {
4868 /* Do this only for general tablespaces. */
4869 if (!DICT_TF_HAS_SHARED_SPACE(table->flags)) {
4870 return;
4871 }
4872
4873 bool use_cache = true;
4874 if (table->tablespace != nullptr) {
4875 if (srv_sys_tablespaces_open &&
4876 dict_table_has_temp_general_tablespace_name(table->tablespace)) {
4877 /* We previous saved the temporary name,
4878 get the real one now. */
4879 use_cache = false;
4880 } else {
4881 /* Keep and use this name */
4882 return;
4883 }
4884 }
4885
4886 if (use_cache) {
4887 fil_space_t *space = fil_space_acquire_silent(table->space);
4888
4889 if (space != nullptr) {
4890 /* Use this name unless it is a temporary general
4891 tablespace name and we can now replace it. */
4892 if (!srv_sys_tablespaces_open ||
4893 !dict_table_has_temp_general_tablespace_name(space->name)) {
4894 /* Use this tablespace name */
4895 table->tablespace = mem_heap_strdup(table->heap, space->name);
4896
4897 fil_space_release(space);
4898 return;
4899 }
4900 fil_space_release(space);
4901 }
4902 }
4903
4904 /* Read it from the dictionary. */
4905 if (srv_sys_tablespaces_open) {
4906 if (dict_mutex_own) {
4907 dict_mutex_exit_for_mysql();
4908 }
4909
4910 table->tablespace = dd_space_get_name(table->heap, table, dd_table);
4911
4912 if (dict_mutex_own) {
4913 dict_mutex_enter_for_mysql();
4914 }
4915 }
4916 }
4917
4918 template void dd_get_and_save_space_name<dd::Table>(dict_table_t *,
4919 const dd::Table *, bool);
4920
4921 template void dd_get_and_save_space_name<dd::Partition>(dict_table_t *,
4922 const dd::Partition *,
4923 bool);
4924
4925 /** Open or load a table definition based on a Global DD object.
4926 @tparam Table dd::Table or dd::Partition
4927 @param[in,out] client data dictionary client
4928 @param[in] table MySQL table definition
4929 @param[in] norm_name Table Name
4930 @param[in] dd_table Global DD table or partition object
4931 @param[in] thd thread THD
4932 @param[in,out] fk_list stack of table names which neet to load
4933 @return ptr to dict_table_t filled, otherwise, nullptr */
4934 template <typename Table>
dd_open_table_one(dd::cache::Dictionary_client * client,const TABLE * table,const char * norm_name,const Table * dd_table,THD * thd,dict_names_t & fk_list)4935 dict_table_t *dd_open_table_one(dd::cache::Dictionary_client *client,
4936 const TABLE *table, const char *norm_name,
4937 const Table *dd_table, THD *thd,
4938 dict_names_t &fk_list) {
4939 ut_ad(dd_table != nullptr);
4940
4941 bool implicit;
4942 dd::Tablespace *dd_space = nullptr;
4943
4944 if (dd_table->tablespace_id() == dict_sys_t::s_dd_space_id) {
4945 /* DD tables are in shared DD tablespace */
4946 implicit = false;
4947 } else if (dd_tablespace_is_implicit(
4948 client, dd_first_index(dd_table)->tablespace_id(), &implicit,
4949 &dd_space)) {
4950 /* Tablespace no longer exist, it could be already dropped */
4951 return (nullptr);
4952 }
4953
4954 const bool zip_allowed = srv_page_size <= UNIV_ZIP_SIZE_MAX;
4955 const bool strict = false;
4956 bool first_index = true;
4957
4958 /* Create dict_table_t for the table */
4959 dict_table_t *m_table = dd_fill_dict_table(
4960 dd_table, table, norm_name, nullptr, zip_allowed, strict, thd, implicit);
4961
4962 if (m_table == nullptr) {
4963 return (nullptr);
4964 }
4965
4966 /* Create dict_index_t for the table */
4967 int ret;
4968 ret = dd_fill_dict_index(dd_table->table(), table, m_table, nullptr,
4969 zip_allowed, strict, thd);
4970
4971 if (ret != 0) {
4972 return (nullptr);
4973 }
4974
4975 if (dd_space && !implicit) {
4976 const char *name = dd_space->name().c_str();
4977 if (name) {
4978 m_table->tablespace = mem_heap_strdupl(m_table->heap, name, strlen(name));
4979 }
4980 }
4981
4982 if (Field **autoinc_col = table->s->found_next_number_field) {
4983 const dd::Properties &p = dd_table->table().se_private_data();
4984 dict_table_autoinc_set_col_pos(m_table, (*autoinc_col)->field_index());
4985 uint64 version, autoinc = 0;
4986 if (p.get(dd_table_key_strings[DD_TABLE_VERSION], &version) ||
4987 p.get(dd_table_key_strings[DD_TABLE_AUTOINC], &autoinc)) {
4988 ut_ad(!"problem setting AUTO_INCREMENT");
4989 return (nullptr);
4990 }
4991
4992 m_table->version = version;
4993 dict_table_autoinc_lock(m_table);
4994 dict_table_autoinc_initialize(m_table, autoinc + 1);
4995 dict_table_autoinc_unlock(m_table);
4996 m_table->autoinc_persisted = autoinc;
4997 }
4998
4999 mem_heap_t *heap = mem_heap_create(1000);
5000 bool fail = false;
5001
5002 /* Now fill the space ID and Root page number for each index */
5003 dict_index_t *index = m_table->first_index();
5004 for (const auto dd_index : dd_table->indexes()) {
5005 ut_ad(index != nullptr);
5006
5007 const dd::Properties &se_private_data = dd_index->se_private_data();
5008 uint64 id = 0;
5009 uint32 root = 0;
5010 uint32 sid = 0;
5011 uint64 trx_id = 0;
5012 dd::Object_id index_space_id = dd_index->tablespace_id();
5013 dd::Tablespace *index_space = nullptr;
5014
5015 if (dd_table->tablespace_id() == dict_sys_t::s_dd_space_id) {
5016 sid = dict_sys_t::s_space_id;
5017 } else if (dd_table->tablespace_id() == dict_sys_t::s_dd_temp_space_id) {
5018 sid = dict_sys_t::s_temp_space_id;
5019 } else {
5020 if (client->acquire_uncached_uncommitted<dd::Tablespace>(index_space_id,
5021 &index_space) ||
5022 index_space == nullptr) {
5023 my_error(ER_TABLESPACE_MISSING, MYF(0), m_table->name.m_name);
5024 fail = true;
5025 break;
5026 }
5027
5028 if (index_space->se_private_data().get(dd_space_key_strings[DD_SPACE_ID],
5029 &sid)) {
5030 fail = true;
5031 break;
5032 }
5033 }
5034
5035 if (first_index) {
5036 ut_ad(m_table->space == 0);
5037 m_table->space = sid;
5038 m_table->dd_space_id = index_space_id;
5039
5040 uint32 dd_fsp_flags;
5041 if (dd_table->tablespace_id() == dict_sys_t::s_dd_space_id) {
5042 dd_fsp_flags = dict_tf_to_fsp_flags(m_table->flags);
5043 } else {
5044 ut_ad(dd_space != nullptr);
5045 dd_space->se_private_data().get(dd_space_key_strings[DD_SPACE_FLAGS],
5046 &dd_fsp_flags);
5047 }
5048
5049 mutex_enter(&dict_sys->mutex);
5050 dd_load_tablespace(dd_table, m_table, heap, DICT_ERR_IGNORE_RECOVER_LOCK,
5051 dd_fsp_flags);
5052 mutex_exit(&dict_sys->mutex);
5053 first_index = false;
5054 }
5055
5056 if (se_private_data.get(dd_index_key_strings[DD_INDEX_ID], &id) ||
5057 se_private_data.get(dd_index_key_strings[DD_INDEX_ROOT], &root) ||
5058 se_private_data.get(dd_index_key_strings[DD_INDEX_TRX_ID], &trx_id)) {
5059 fail = true;
5060 break;
5061 }
5062
5063 ut_ad(root > 1);
5064 ut_ad(index->type & DICT_FTS || root != FIL_NULL ||
5065 dict_table_is_discarded(m_table));
5066 ut_ad(id != 0);
5067 index->page = root;
5068 index->space = sid;
5069 index->id = id;
5070 index->trx_id = trx_id;
5071
5072 /** Look up the spatial reference system in the
5073 dictionary. Since this may cause a table open to read the
5074 dictionary tables, it must be done while not holding
5075 &dict_sys->mutex. */
5076 if (dict_index_is_spatial(index))
5077 index->rtr_srs.reset(fetch_srs(index->srid));
5078
5079 index = index->next();
5080 }
5081
5082 if (!implicit) {
5083 dd_get_and_save_space_name(m_table, dd_table, false);
5084 }
5085
5086 mutex_enter(&dict_sys->mutex);
5087
5088 if (fail) {
5089 for (dict_index_t *index = UT_LIST_GET_LAST(m_table->indexes);
5090 index != nullptr; index = UT_LIST_GET_LAST(m_table->indexes)) {
5091 dict_index_remove_from_cache(m_table, index);
5092 }
5093 dict_mem_table_free(m_table);
5094 mutex_exit(&dict_sys->mutex);
5095 mem_heap_free(heap);
5096
5097 return (nullptr);
5098 }
5099
5100 /* Re-check if the table has been opened/added by a concurrent
5101 thread */
5102 dict_table_t *exist = dict_table_check_if_in_cache_low(norm_name);
5103 if (exist != nullptr) {
5104 for (dict_index_t *index = UT_LIST_GET_LAST(m_table->indexes);
5105 index != nullptr; index = UT_LIST_GET_LAST(m_table->indexes)) {
5106 dict_index_remove_from_cache(m_table, index);
5107 }
5108 dict_mem_table_free(m_table);
5109
5110 m_table = exist;
5111 } else {
5112 dict_table_add_to_cache(m_table, TRUE, heap);
5113
5114 if (m_table->fts && dict_table_has_fts_index(m_table)) {
5115 fts_optimize_add_table(m_table);
5116 }
5117
5118 if (dict_sys->dynamic_metadata != nullptr) {
5119 dict_table_load_dynamic_metadata(m_table);
5120 }
5121 }
5122
5123 m_table->acquire();
5124
5125 mutex_exit(&dict_sys->mutex);
5126
5127 /* Check if this is a DD system table */
5128 if (m_table != nullptr) {
5129 std::string db_str;
5130 std::string tbl_str;
5131 dict_name::get_table(m_table->name.m_name, db_str, tbl_str);
5132
5133 m_table->is_dd_table =
5134 dd::get_dictionary()->is_dd_table_name(db_str.c_str(), tbl_str.c_str());
5135 }
5136
5137 /* Load foreign key info. It could also register child table(s) that
5138 refers to current table */
5139 if (exist == nullptr) {
5140 dberr_t error =
5141 dd_table_load_fk(client, norm_name, nullptr, m_table,
5142 &dd_table->table(), thd, false, true, &fk_list);
5143 if (error != DB_SUCCESS) {
5144 dict_table_close(m_table, FALSE, FALSE);
5145 m_table = nullptr;
5146 }
5147 }
5148 mem_heap_free(heap);
5149
5150 return (m_table);
5151 }
5152
5153 /** Open single table with name
5154 @param[in] name table name
5155 @param[in] dict_locked dict_sys mutex is held or not
5156 @param[in,out] fk_list foreign key name list
5157 @param[in] thd thread THD */
dd_open_table_one_on_name(const char * name,bool dict_locked,dict_names_t & fk_list,THD * thd)5158 static void dd_open_table_one_on_name(const char *name, bool dict_locked,
5159 dict_names_t &fk_list, THD *thd) {
5160 dict_table_t *table = nullptr;
5161 const dd::Table *dd_table = nullptr;
5162 MDL_ticket *mdl = nullptr;
5163
5164 if (!dict_locked) {
5165 mutex_enter(&dict_sys->mutex);
5166 }
5167
5168 table = dict_table_check_if_in_cache_low(name);
5169
5170 if (table != nullptr) {
5171 /* If the table is in cached already, do nothing. */
5172 if (!dict_locked) {
5173 mutex_exit(&dict_sys->mutex);
5174 }
5175
5176 return;
5177 } else {
5178 /* Otherwise, open it by dd obj. */
5179
5180 /* Exit sys mutex to access server info */
5181 mutex_exit(&dict_sys->mutex);
5182
5183 std::string db_str;
5184 std::string tbl_str;
5185 dict_name::get_table(name, db_str, tbl_str);
5186
5187 if (db_str.empty() || tbl_str.empty() ||
5188 dd_mdl_acquire(thd, &mdl, db_str.c_str(), tbl_str.c_str())) {
5189 goto func_exit;
5190 }
5191
5192 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
5193 dd::cache::Dictionary_client::Auto_releaser releaser(client);
5194
5195 if (client->acquire(db_str.c_str(), tbl_str.c_str(), &dd_table) ||
5196 dd_table == nullptr) {
5197 goto func_exit;
5198 }
5199
5200 ut_ad(dd_table->se_private_id() != dd::INVALID_OBJECT_ID);
5201
5202 TABLE_SHARE ts;
5203
5204 init_tmp_table_share(thd, &ts, db_str.c_str(), db_str.length(),
5205 dd_table->name().c_str(), "" /* file name */, nullptr);
5206
5207 ulint error =
5208 open_table_def_suppress_invalid_meta_data(thd, &ts, *dd_table);
5209
5210 if (error != 0) {
5211 goto func_exit;
5212 }
5213
5214 TABLE td;
5215
5216 error = open_table_from_share(thd, &ts, dd_table->name().c_str(), 0,
5217 SKIP_NEW_HANDLER, 0, &td, false, dd_table);
5218
5219 if (error != 0) {
5220 free_table_share(&ts);
5221 goto func_exit;
5222 }
5223
5224 table = dd_open_table_one(client, &td, name, dd_table, thd, fk_list);
5225
5226 closefrm(&td, false);
5227 free_table_share(&ts);
5228 }
5229
5230 func_exit:
5231 if (table != nullptr) {
5232 dd_table_close(table, thd, &mdl, false);
5233 } else {
5234 dd_mdl_release(thd, &mdl);
5235 }
5236
5237 if (dict_locked) {
5238 mutex_enter(&dict_sys->mutex);
5239 }
5240 }
5241
5242 /** Open foreign tables reference a table.
5243 @param[in] fk_list foreign key name list
5244 @param[in] dict_locked dict_sys mutex is locked or not
5245 @param[in] thd thread THD */
dd_open_fk_tables(dict_names_t & fk_list,bool dict_locked,THD * thd)5246 void dd_open_fk_tables(dict_names_t &fk_list, bool dict_locked, THD *thd) {
5247 while (!fk_list.empty()) {
5248 char *name = const_cast<char *>(fk_list.front());
5249
5250 if (innobase_get_lower_case_table_names() == 2) {
5251 innobase_casedn_str(name);
5252 } else {
5253 #ifndef _WIN32
5254 if (innobase_get_lower_case_table_names() == 1) {
5255 innobase_casedn_str(name);
5256 }
5257 #endif /* !_WIN32 */
5258 }
5259
5260 dd_open_table_one_on_name(name, dict_locked, fk_list, thd);
5261
5262 fk_list.pop_front();
5263 }
5264 }
5265
5266 /** Open or load a table definition based on a Global DD object.
5267 @tparam Table dd::Table or dd::Partition
5268 @param[in,out] client data dictionary client
5269 @param[in] table MySQL table definition
5270 @param[in] norm_name Table Name
5271 @param[in] dd_table Global DD table or partition object
5272 @param[in] thd thread THD
5273 @return ptr to dict_table_t filled, otherwise, nullptr */
5274 template <typename Table>
dd_open_table(dd::cache::Dictionary_client * client,const TABLE * table,const char * norm_name,const Table * dd_table,THD * thd)5275 dict_table_t *dd_open_table(dd::cache::Dictionary_client *client,
5276 const TABLE *table, const char *norm_name,
5277 const Table *dd_table, THD *thd) {
5278 dict_table_t *m_table = nullptr;
5279 dict_names_t fk_list;
5280
5281 m_table = dd_open_table_one(client, table, norm_name, dd_table, thd, fk_list);
5282
5283 /* If there is foreign table references to this table, we will
5284 try to open them */
5285 if (m_table != nullptr && !fk_list.empty()) {
5286 dd_open_fk_tables(fk_list, false, thd);
5287 }
5288
5289 return (m_table);
5290 }
5291
5292 template dict_table_t *dd_open_table<dd::Table>(dd::cache::Dictionary_client *,
5293 const TABLE *, const char *,
5294 const dd::Table *, THD *);
5295
5296 template dict_table_t *dd_open_table<dd::Partition>(
5297 dd::cache::Dictionary_client *, const TABLE *, const char *,
5298 const dd::Partition *, THD *);
5299
5300 /** Get next record from a new dd system table, like mysql.tables...
5301 @param[in,out] pcur persistent cursor
5302 @param[in] mtr the mini-transaction
5303 @return the next rec of the dd system table */
dd_getnext_system_low(btr_pcur_t * pcur,mtr_t * mtr)5304 static const rec_t *dd_getnext_system_low(btr_pcur_t *pcur, mtr_t *mtr) {
5305 rec_t *rec = nullptr;
5306 bool is_comp = dict_table_is_comp(pcur->index()->table);
5307
5308 while (!rec || rec_get_deleted_flag(rec, is_comp)) {
5309 btr_pcur_move_to_next_user_rec(pcur, mtr);
5310
5311 rec = btr_pcur_get_rec(pcur);
5312
5313 if (!btr_pcur_is_on_user_rec(pcur)) {
5314 /* end of index */
5315 btr_pcur_close(pcur);
5316
5317 return (nullptr);
5318 }
5319 }
5320
5321 /* Get a record, let's save the position */
5322 btr_pcur_store_position(pcur, mtr);
5323
5324 return (rec);
5325 }
5326
5327 /** Get next record of new DD system tables
5328 @param[in,out] pcur persistent cursor
5329 @param[in] mtr the mini-transaction
5330 @retval next record */
dd_getnext_system_rec(btr_pcur_t * pcur,mtr_t * mtr)5331 const rec_t *dd_getnext_system_rec(btr_pcur_t *pcur, mtr_t *mtr) {
5332 /* Restore the position */
5333 btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr);
5334
5335 return (dd_getnext_system_low(pcur, mtr));
5336 }
5337
5338 /** Scan a new dd system table, like mysql.tables...
5339 @param[in] thd thd
5340 @param[in,out] mdl mdl lock
5341 @param[in,out] pcur persistent cursor
5342 @param[in,out] mtr the mini-transaction
5343 @param[in] system_table_name which dd system table to open
5344 @param[in,out] table dict_table_t obj of dd system table
5345 @retval the first rec of the dd system table */
dd_startscan_system(THD * thd,MDL_ticket ** mdl,btr_pcur_t * pcur,mtr_t * mtr,const char * system_table_name,dict_table_t ** table)5346 const rec_t *dd_startscan_system(THD *thd, MDL_ticket **mdl, btr_pcur_t *pcur,
5347 mtr_t *mtr, const char *system_table_name,
5348 dict_table_t **table) {
5349 dict_index_t *clust_index;
5350 const rec_t *rec = nullptr;
5351
5352 *table = dd_table_open_on_name(thd, mdl, system_table_name, true, false);
5353 mtr_commit(mtr);
5354
5355 clust_index = UT_LIST_GET_FIRST((*table)->indexes);
5356
5357 mtr_start(mtr);
5358 btr_pcur_open_at_index_side(true, clust_index, BTR_SEARCH_LEAF, pcur, true, 0,
5359 mtr);
5360
5361 rec = dd_getnext_system_low(pcur, mtr);
5362
5363 return (rec);
5364 }
5365
5366 /**
5367 All DD tables would contain DB_TRX_ID and DB_ROLL_PTR fields
5368 before other fields. This offset indicates the position at
5369 which the first DD column is located.
5370 */
5371 static const int DD_FIELD_OFFSET = 2;
5372
5373 /** Process one mysql.tables record and get the dict_table_t
5374 @param[in] heap temp memory heap
5375 @param[in,out] rec mysql.tables record
5376 @param[in,out] table dict_table_t to fill
5377 @param[in] dd_tables dict_table_t obj of dd system table
5378 @param[in] mdl mdl on the table
5379 @param[in] mtr the mini-transaction
5380 @retval error message, or NULL on success */
dd_process_dd_tables_rec_and_mtr_commit(mem_heap_t * heap,const rec_t * rec,dict_table_t ** table,dict_table_t * dd_tables,MDL_ticket ** mdl,mtr_t * mtr)5381 const char *dd_process_dd_tables_rec_and_mtr_commit(
5382 mem_heap_t *heap, const rec_t *rec, dict_table_t **table,
5383 dict_table_t *dd_tables, MDL_ticket **mdl, mtr_t *mtr) {
5384 ulint len;
5385 const byte *field;
5386 const char *err_msg = nullptr;
5387 ulint table_id;
5388
5389 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_tables)));
5390 ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
5391
5392 ulint *offsets = rec_get_offsets(rec, dd_tables->first_index(), nullptr,
5393 ULINT_UNDEFINED, &heap);
5394
5395 field = rec_get_nth_field(
5396 rec, offsets, dd_tables->field_number("engine") + DD_FIELD_OFFSET, &len);
5397
5398 /* If "engine" field is not "innodb", return. */
5399 if (strncmp((const char *)field, "InnoDB", 6) != 0) {
5400 *table = nullptr;
5401 mtr_commit(mtr);
5402 return (err_msg);
5403 }
5404
5405 /* Get the se_private_id field. */
5406 field = (const byte *)rec_get_nth_field(
5407 rec, offsets, dd_tables->field_number("se_private_id") + DD_FIELD_OFFSET,
5408 &len);
5409
5410 if (len != 8) {
5411 *table = nullptr;
5412 mtr_commit(mtr);
5413 return (err_msg);
5414 }
5415
5416 /* Get the table id */
5417 table_id = mach_read_from_8(field);
5418
5419 /* Skip mysql.* tables. */
5420 if (dict_sys_t::is_dd_table_id(table_id)) {
5421 *table = nullptr;
5422 mtr_commit(mtr);
5423 return (err_msg);
5424 }
5425
5426 /* Commit before load the table again */
5427 mtr_commit(mtr);
5428 THD *thd = current_thd;
5429
5430 *table = dd_table_open_on_id(table_id, thd, mdl, true, false, true);
5431
5432 if (!(*table)) {
5433 err_msg = "Table not found";
5434 }
5435
5436 return (err_msg);
5437 }
5438
5439 /** Process one mysql.table_partitions record and get the dict_table_t
5440 @param[in] heap temp memory heap
5441 @param[in,out] rec mysql.table_partitions record
5442 @param[in,out] table dict_table_t to fill
5443 @param[in] dd_tables dict_table_t obj of dd partition table
5444 @param[in] mdl mdl on the table
5445 @param[in] mtr the mini-transaction
5446 @retval error message, or NULL on success */
dd_process_dd_partitions_rec_and_mtr_commit(mem_heap_t * heap,const rec_t * rec,dict_table_t ** table,dict_table_t * dd_tables,MDL_ticket ** mdl,mtr_t * mtr)5447 const char *dd_process_dd_partitions_rec_and_mtr_commit(
5448 mem_heap_t *heap, const rec_t *rec, dict_table_t **table,
5449 dict_table_t *dd_tables, MDL_ticket **mdl, mtr_t *mtr) {
5450 ulint len;
5451 const byte *field;
5452 const char *err_msg = nullptr;
5453 ulint table_id;
5454
5455 ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
5456
5457 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_tables)));
5458
5459 ulint *offsets = rec_get_offsets(rec, dd_tables->first_index(), nullptr,
5460 ULINT_UNDEFINED, &heap);
5461
5462 /* Get the engine field. */
5463 field = rec_get_nth_field(
5464 rec, offsets, dd_tables->field_number("engine") + DD_FIELD_OFFSET, &len);
5465
5466 /* If "engine" field is not "innodb", return. */
5467 if (strncmp((const char *)field, "InnoDB", 6) != 0) {
5468 *table = nullptr;
5469 mtr_commit(mtr);
5470 return (err_msg);
5471 }
5472
5473 /* Get the se_private_id field. */
5474 field = (const byte *)rec_get_nth_field(
5475 rec, offsets, dd_tables->field_number("se_private_id") + DD_FIELD_OFFSET,
5476 &len);
5477 /* When table is partitioned table, the se_private_id is null. */
5478 if (len != 8) {
5479 *table = nullptr;
5480 mtr_commit(mtr);
5481 return (err_msg);
5482 }
5483
5484 /* Get the table id */
5485 table_id = mach_read_from_8(field);
5486
5487 /* Skip mysql.* tables. */
5488 if (dict_sys_t::is_dd_table_id(table_id)) {
5489 *table = nullptr;
5490 mtr_commit(mtr);
5491 return (err_msg);
5492 }
5493
5494 /* Commit before load the table again */
5495 mtr_commit(mtr);
5496 THD *thd = current_thd;
5497
5498 *table = dd_table_open_on_id(table_id, thd, mdl, true, false, true);
5499
5500 if (!(*table)) {
5501 err_msg = "Table not found";
5502 }
5503
5504 return (err_msg);
5505 }
5506
5507 /** Process one mysql.columns record and get info to dict_col_t
5508 @param[in,out] heap temp memory heap
5509 @param[in] rec mysql.columns record
5510 @param[in,out] col dict_col_t to fill
5511 @param[in,out] table_id table id
5512 @param[in,out] col_name column name
5513 @param[in,out] nth_v_col nth v column
5514 @param[in] dd_columns dict_table_t obj of mysql.columns
5515 @param[in,out] mtr the mini-transaction
5516 @retval true if column is filled */
dd_process_dd_columns_rec(mem_heap_t * heap,const rec_t * rec,dict_col_t * col,table_id_t * table_id,char ** col_name,ulint * nth_v_col,const dict_table_t * dd_columns,mtr_t * mtr)5517 bool dd_process_dd_columns_rec(mem_heap_t *heap, const rec_t *rec,
5518 dict_col_t *col, table_id_t *table_id,
5519 char **col_name, ulint *nth_v_col,
5520 const dict_table_t *dd_columns, mtr_t *mtr) {
5521 ulint len;
5522 const byte *field;
5523 dict_col_t *t_col;
5524 ulint pos;
5525 ulint v_pos = 0;
5526 dd::Column::enum_hidden_type hidden;
5527 bool is_virtual;
5528 dict_v_col_t *vcol = nullptr;
5529
5530 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_columns)));
5531
5532 ulint *offsets = rec_get_offsets(rec, dd_columns->first_index(), nullptr,
5533 ULINT_UNDEFINED, &heap);
5534
5535 const dd::Object_table &dd_object_table = dd::get_dd_table<dd::Column>();
5536
5537 /* Get the hidden attribute, and skip if it's a hidden column. */
5538 field = (const byte *)rec_get_nth_field(
5539 rec, offsets,
5540 dd_object_table.field_number("FIELD_HIDDEN") + DD_FIELD_OFFSET, &len);
5541 hidden = static_cast<dd::Column::enum_hidden_type>(mach_read_from_1(field));
5542 if (hidden == dd::Column::enum_hidden_type::HT_HIDDEN_SE ||
5543 hidden == dd::Column::enum_hidden_type::HT_HIDDEN_SQL) {
5544 mtr_commit(mtr);
5545 return (false);
5546 }
5547
5548 /* Get the column name. */
5549 field = (const byte *)rec_get_nth_field(
5550 rec, offsets,
5551 dd_object_table.field_number("FIELD_NAME") + DD_FIELD_OFFSET, &len);
5552 *col_name = mem_heap_strdupl(heap, (const char *)field, len);
5553
5554 /* Get the position. */
5555 field = (const byte *)rec_get_nth_field(
5556 rec, offsets,
5557 dd_object_table.field_number("FIELD_ORDINAL_POSITION") + DD_FIELD_OFFSET,
5558 &len);
5559 pos = mach_read_from_4(field) - 1;
5560
5561 /* Get the is_virtual attribute. */
5562 field = (const byte *)rec_get_nth_field(rec, offsets, 21, &len);
5563 is_virtual = mach_read_from_1(field) & 0x01;
5564
5565 /* Get the se_private_data field. */
5566 field = (const byte *)rec_get_nth_field(
5567 rec, offsets,
5568 dd_object_table.field_number("FIELD_SE_PRIVATE_DATA") + DD_FIELD_OFFSET,
5569 &len);
5570
5571 if (len == 0 || len == UNIV_SQL_NULL) {
5572 mtr_commit(mtr);
5573 return (false);
5574 }
5575
5576 char *p_ptr = (char *)mem_heap_strdupl(heap, (const char *)field, len);
5577 dd::String_type prop((char *)p_ptr);
5578 dd::Properties *p = dd::Properties::parse_properties(prop);
5579
5580 /* Load the table and get the col. */
5581 if (!p || !p->exists(dd_index_key_strings[DD_TABLE_ID])) {
5582 if (p) {
5583 delete p;
5584 }
5585 mtr_commit(mtr);
5586 return (false);
5587 }
5588
5589 if (!p->get(dd_index_key_strings[DD_TABLE_ID], (uint64 *)table_id)) {
5590 THD *thd = current_thd;
5591 dict_table_t *table;
5592 MDL_ticket *mdl = nullptr;
5593
5594 /* Commit before we try to load the table. */
5595 mtr_commit(mtr);
5596 table = dd_table_open_on_id(*table_id, thd, &mdl, true, true);
5597
5598 if (!table) {
5599 delete p;
5600 return (false);
5601 }
5602
5603 if (is_virtual) {
5604 vcol = dict_table_get_nth_v_col_mysql(table, pos);
5605
5606 if (vcol == nullptr) {
5607 dd_table_close(table, thd, &mdl, true);
5608 delete p;
5609 return (false);
5610 }
5611
5612 /* Copy info. */
5613 col->ind = vcol->m_col.ind;
5614 col->mtype = vcol->m_col.mtype;
5615 col->prtype = vcol->m_col.prtype;
5616 col->len = vcol->m_col.len;
5617
5618 v_pos = dict_create_v_col_pos(vcol->v_pos, vcol->m_col.ind);
5619 } else {
5620 if (table->n_v_cols == 0) {
5621 t_col = table->get_col(pos);
5622 } else {
5623 ulint col_nr;
5624
5625 col_nr = dict_table_has_column(table, *col_name, pos);
5626 t_col = table->get_col(col_nr);
5627 ut_ad(t_col);
5628 }
5629
5630 /* Copy info. */
5631 col->ind = t_col->ind;
5632 col->mtype = t_col->mtype;
5633 col->prtype = t_col->prtype;
5634 col->len = t_col->len;
5635 }
5636
5637 if (p->exists(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL]) ||
5638 p->exists(dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT])) {
5639 dd_parse_default_value(*p, col, heap);
5640 }
5641
5642 dd_table_close(table, thd, &mdl, true);
5643 delete p;
5644 } else {
5645 delete p;
5646 mtr_commit(mtr);
5647 return (false);
5648 }
5649
5650 /* Report the virtual column number */
5651 if (col->prtype & DATA_VIRTUAL) {
5652 ut_ad(vcol != nullptr);
5653 ut_ad(v_pos != 0);
5654 ut_ad(is_virtual);
5655
5656 *nth_v_col = dict_get_v_col_pos(v_pos);
5657 } else {
5658 *nth_v_col = ULINT_UNDEFINED;
5659 }
5660
5661 return (true);
5662 }
5663
5664 /** Process one mysql.columns record for virtual columns
5665 @param[in] heap temp memory heap
5666 @param[in,out] rec mysql.columns record
5667 @param[in,out] table_id table id
5668 @param[in,out] pos position
5669 @param[in,out] base_pos base column position
5670 @param[in,out] n_row number of rows
5671 @param[in] dd_columns dict_table_t obj of mysql.columns
5672 @param[in] mtr the mini-transaction
5673 @retval true if virtual info is filled */
dd_process_dd_virtual_columns_rec(mem_heap_t * heap,const rec_t * rec,table_id_t * table_id,ulint ** pos,ulint ** base_pos,ulint * n_row,dict_table_t * dd_columns,mtr_t * mtr)5674 bool dd_process_dd_virtual_columns_rec(mem_heap_t *heap, const rec_t *rec,
5675 table_id_t *table_id, ulint **pos,
5676 ulint **base_pos, ulint *n_row,
5677 dict_table_t *dd_columns, mtr_t *mtr) {
5678 ulint len;
5679 const byte *field;
5680 ulint origin_pos;
5681 dd::Column::enum_hidden_type hidden;
5682 bool is_virtual;
5683
5684 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_columns)));
5685
5686 ulint *offsets = rec_get_offsets(rec, dd_columns->first_index(), nullptr,
5687 ULINT_UNDEFINED, &heap);
5688
5689 const dd::Object_table &dd_object_table = dd::get_dd_table<dd::Column>();
5690
5691 /* Get the is_virtual attribute, and skip if it's not a virtual column. */
5692 field = (const byte *)rec_get_nth_field(
5693 rec, offsets,
5694 dd_object_table.field_number("FIELD_IS_VIRTUAL") + DD_FIELD_OFFSET, &len);
5695 is_virtual = mach_read_from_1(field) & 0x01;
5696 if (!is_virtual) {
5697 mtr_commit(mtr);
5698 return (false);
5699 }
5700
5701 /* Get the hidden attribute, and skip if it's a hidden column. */
5702 field = (const byte *)rec_get_nth_field(
5703 rec, offsets,
5704 dd_object_table.field_number("FIELD_HIDDEN") + DD_FIELD_OFFSET, &len);
5705 hidden = static_cast<dd::Column::enum_hidden_type>(mach_read_from_1(field));
5706 if (hidden == dd::Column::enum_hidden_type::HT_HIDDEN_SE) {
5707 mtr_commit(mtr);
5708 return (false);
5709 }
5710
5711 /* Get the position. */
5712 field = (const byte *)rec_get_nth_field(
5713 rec, offsets,
5714 dd_object_table.field_number("FIELD_ORDINAL_POSITION") + DD_FIELD_OFFSET,
5715 &len);
5716 origin_pos = mach_read_from_4(field) - 1;
5717
5718 /* Get the se_private_data field. */
5719 field = (const byte *)rec_get_nth_field(
5720 rec, offsets,
5721 dd_object_table.field_number("FIELD_SE_PRIVATE_DATA") + DD_FIELD_OFFSET,
5722 &len);
5723
5724 if (len == 0 || len == UNIV_SQL_NULL) {
5725 mtr_commit(mtr);
5726 return (false);
5727 }
5728
5729 char *p_ptr = (char *)mem_heap_strdupl(heap, (const char *)field, len);
5730 dd::String_type prop((char *)p_ptr);
5731 dd::Properties *p = dd::Properties::parse_properties(prop);
5732
5733 /* Load the table and get the col. */
5734 if (!p || !p->exists(dd_index_key_strings[DD_TABLE_ID])) {
5735 if (p) {
5736 delete p;
5737 }
5738 mtr_commit(mtr);
5739 return (false);
5740 }
5741
5742 if (!p->get(dd_index_key_strings[DD_TABLE_ID], (uint64 *)table_id)) {
5743 THD *thd = current_thd;
5744 dict_table_t *table;
5745 MDL_ticket *mdl = nullptr;
5746 dict_v_col_t *vcol = nullptr;
5747
5748 /* Commit before we try to load the table. */
5749 mtr_commit(mtr);
5750 table = dd_table_open_on_id(*table_id, thd, &mdl, true, true);
5751
5752 if (!table) {
5753 delete p;
5754 return (false);
5755 }
5756
5757 vcol = dict_table_get_nth_v_col_mysql(table, origin_pos);
5758
5759 if (vcol == nullptr || vcol->num_base == 0) {
5760 dd_table_close(table, thd, &mdl, true);
5761 delete p;
5762 return (false);
5763 }
5764
5765 *pos = static_cast<ulint *>(
5766 mem_heap_alloc(heap, vcol->num_base * sizeof(ulint)));
5767 *base_pos = static_cast<ulint *>(
5768 mem_heap_alloc(heap, vcol->num_base * sizeof(ulint)));
5769 *n_row = vcol->num_base;
5770 for (ulint i = 0; i < *n_row; i++) {
5771 (*pos)[i] = dict_create_v_col_pos(vcol->v_pos, vcol->m_col.ind);
5772 (*base_pos)[i] = vcol->base_col[i]->ind;
5773 }
5774
5775 dd_table_close(table, thd, &mdl, true);
5776 delete p;
5777 } else {
5778 delete p;
5779 mtr_commit(mtr);
5780 return (false);
5781 }
5782
5783 return (true);
5784 }
5785 /** Process one mysql.indexes record and get dict_index_t
5786 @param[in] heap temp memory heap
5787 @param[in,out] rec mysql.indexes record
5788 @param[in,out] index dict_index_t to fill
5789 @param[in] mdl mdl on index->table
5790 @param[in,out] parent parent table if it's fts aux table.
5791 @param[in,out] parent_mdl mdl on parent if it's fts aux table.
5792 @param[in] dd_indexes dict_table_t obj of mysql.indexes
5793 @param[in] mtr the mini-transaction
5794 @retval true if index is filled */
dd_process_dd_indexes_rec(mem_heap_t * heap,const rec_t * rec,const dict_index_t ** index,MDL_ticket ** mdl,dict_table_t ** parent,MDL_ticket ** parent_mdl,dict_table_t * dd_indexes,mtr_t * mtr)5795 bool dd_process_dd_indexes_rec(mem_heap_t *heap, const rec_t *rec,
5796 const dict_index_t **index, MDL_ticket **mdl,
5797 dict_table_t **parent, MDL_ticket **parent_mdl,
5798 dict_table_t *dd_indexes, mtr_t *mtr) {
5799 ulint len;
5800 const byte *field;
5801 uint32 index_id;
5802 uint32 space_id;
5803 uint64 table_id;
5804
5805 *index = nullptr;
5806
5807 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_indexes)));
5808
5809 ulint *offsets = rec_get_offsets(rec, dd_indexes->first_index(), nullptr,
5810 ULINT_UNDEFINED, &heap);
5811
5812 const dd::Object_table &dd_object_table = dd::get_dd_table<dd::Index>();
5813
5814 field = rec_get_nth_field(
5815 rec, offsets,
5816 dd_object_table.field_number("FIELD_ENGINE") + DD_FIELD_OFFSET, &len);
5817
5818 /* If "engine" field is not "innodb", return. */
5819 if (strncmp((const char *)field, "InnoDB", 6) != 0) {
5820 mtr_commit(mtr);
5821 return (false);
5822 }
5823
5824 /* Get the se_private_data field. */
5825 field = (const byte *)rec_get_nth_field(
5826 rec, offsets,
5827 dd_object_table.field_number("FIELD_SE_PRIVATE_DATA") + DD_FIELD_OFFSET,
5828 &len);
5829
5830 if (len == 0 || len == UNIV_SQL_NULL) {
5831 mtr_commit(mtr);
5832 return (false);
5833 }
5834
5835 /* Get index id. */
5836 dd::String_type prop((char *)field);
5837 dd::Properties *p = dd::Properties::parse_properties(prop);
5838
5839 if (!p || !p->exists(dd_index_key_strings[DD_INDEX_ID]) ||
5840 !p->exists(dd_index_key_strings[DD_INDEX_SPACE_ID])) {
5841 if (p) {
5842 delete p;
5843 }
5844 mtr_commit(mtr);
5845 return (false);
5846 }
5847
5848 if (p->get(dd_index_key_strings[DD_INDEX_ID], &index_id)) {
5849 delete p;
5850 mtr_commit(mtr);
5851 return (false);
5852 }
5853
5854 /* Get the tablespace id. */
5855 if (p->get(dd_index_key_strings[DD_INDEX_SPACE_ID], &space_id)) {
5856 delete p;
5857 mtr_commit(mtr);
5858 return (false);
5859 }
5860
5861 /* Skip mysql.* indexes. */
5862 if (space_id == dict_sys->s_space_id) {
5863 delete p;
5864 mtr_commit(mtr);
5865 return (false);
5866 }
5867
5868 /* Load the table and get the index. */
5869 if (!p->exists(dd_index_key_strings[DD_TABLE_ID])) {
5870 delete p;
5871 mtr_commit(mtr);
5872 return (false);
5873 }
5874
5875 if (!p->get(dd_index_key_strings[DD_TABLE_ID], &table_id)) {
5876 THD *thd = current_thd;
5877 dict_table_t *table;
5878
5879 /* Commit before load the table */
5880 mtr_commit(mtr);
5881 table = dd_table_open_on_id(table_id, thd, mdl, true, true);
5882
5883 if (!table) {
5884 delete p;
5885 return (false);
5886 }
5887
5888 /* For fts aux table, we need to acquire mdl lock on parent. */
5889 if (table->is_fts_aux()) {
5890 fts_aux_table_t fts_table;
5891
5892 /* Find the parent ID. */
5893 ut_d(bool is_fts =) fts_is_aux_table_name(&fts_table, table->name.m_name,
5894 strlen(table->name.m_name));
5895 ut_ad(is_fts);
5896
5897 table_id_t parent_id = fts_table.parent_id;
5898
5899 dd_table_close(table, thd, mdl, true);
5900
5901 *parent = dd_table_open_on_id(parent_id, thd, parent_mdl, true, true);
5902
5903 if (*parent == nullptr) {
5904 delete p;
5905 return (false);
5906 }
5907
5908 table = dd_table_open_on_id(table_id, thd, mdl, true, true);
5909
5910 if (!table) {
5911 dd_table_close(*parent, thd, parent_mdl, true);
5912 delete p;
5913 return (false);
5914 }
5915 }
5916
5917 for (const dict_index_t *t_index = table->first_index(); t_index != nullptr;
5918 t_index = t_index->next()) {
5919 if (t_index->space == space_id && t_index->id == index_id) {
5920 *index = t_index;
5921 }
5922 }
5923
5924 if (*index == nullptr) {
5925 dd_table_close(table, thd, mdl, true);
5926 if (table->is_fts_aux() && *parent) {
5927 dd_table_close(*parent, thd, parent_mdl, true);
5928 }
5929 delete p;
5930 return (false);
5931 }
5932
5933 delete p;
5934 } else {
5935 delete p;
5936 mtr_commit(mtr);
5937 return (false);
5938 }
5939
5940 return (true);
5941 }
5942
5943 /** Process one mysql.indexes record and get breif info to dict_index_t
5944 @param[in] heap temp memory heap
5945 @param[in,out] rec mysql.indexes record
5946 @param[in,out] index_id index id
5947 @param[in,out] space_id space id
5948 @param[in] dd_indexes dict_table_t obj of mysql.indexes
5949 @retval true if index is filled */
dd_process_dd_indexes_rec_simple(mem_heap_t * heap,const rec_t * rec,space_index_t * index_id,space_id_t * space_id,dict_table_t * dd_indexes)5950 bool dd_process_dd_indexes_rec_simple(mem_heap_t *heap, const rec_t *rec,
5951 space_index_t *index_id,
5952 space_id_t *space_id,
5953 dict_table_t *dd_indexes) {
5954 ulint len;
5955 const byte *field;
5956 uint32 idx_id;
5957
5958 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_indexes)));
5959
5960 ulint *offsets = rec_get_offsets(rec, dd_indexes->first_index(), nullptr,
5961 ULINT_UNDEFINED, &heap);
5962
5963 const dd::Object_table &dd_object_table = dd::get_dd_table<dd::Index>();
5964
5965 field = rec_get_nth_field(
5966 rec, offsets,
5967 dd_object_table.field_number("FIELD_ENGINE") + DD_FIELD_OFFSET, &len);
5968
5969 /* If "engine" field is not "innodb", return. */
5970 if (strncmp((const char *)field, "InnoDB", 6) != 0) {
5971 return (false);
5972 }
5973
5974 /* Get the se_private_data field. */
5975 field = (const byte *)rec_get_nth_field(
5976 rec, offsets,
5977 dd_object_table.field_number("FIELD_SE_PRIVATE_DATA") + DD_FIELD_OFFSET,
5978 &len);
5979
5980 if (len == 0 || len == UNIV_SQL_NULL) {
5981 return (false);
5982 }
5983
5984 /* Get index id. */
5985 dd::String_type prop((char *)field);
5986 dd::Properties *p = dd::Properties::parse_properties(prop);
5987
5988 if (!p || !p->exists(dd_index_key_strings[DD_INDEX_ID]) ||
5989 !p->exists(dd_index_key_strings[DD_INDEX_SPACE_ID])) {
5990 if (p) {
5991 delete p;
5992 }
5993 return (false);
5994 }
5995
5996 if (p->get(dd_index_key_strings[DD_INDEX_ID], &idx_id)) {
5997 delete p;
5998 return (false);
5999 }
6000 *index_id = idx_id;
6001
6002 /* Get the tablespace_id. */
6003 if (p->get(dd_index_key_strings[DD_INDEX_SPACE_ID], space_id)) {
6004 delete p;
6005 return (false);
6006 }
6007
6008 delete p;
6009
6010 return (true);
6011 }
6012
6013 /** Process one mysql.tablespaces record and get info
6014 @param[in] heap temp memory heap
6015 @param[in,out] rec mysql.tablespaces record
6016 @param[in,out] space_id space id
6017 @param[in,out] name space name
6018 @param[in,out] flags space flags
6019 @param[in,out] server_version server version
6020 @param[in,out] space_version space version
6021 @param[in,out] is_encrypted true if tablespace is encrypted
6022 @param[in,out] state space state
6023 @param[in] dd_spaces dict_table_t obj of mysql.tablespaces
6024 @return true if data is retrived */
dd_process_dd_tablespaces_rec(mem_heap_t * heap,const rec_t * rec,space_id_t * space_id,char ** name,uint32_t * flags,uint32 * server_version,uint32 * space_version,bool * is_encrypted,dd::String_type * state,dict_table_t * dd_spaces)6025 bool dd_process_dd_tablespaces_rec(mem_heap_t *heap, const rec_t *rec,
6026 space_id_t *space_id, char **name,
6027 uint32_t *flags, uint32 *server_version,
6028 uint32 *space_version, bool *is_encrypted,
6029 dd::String_type *state,
6030 dict_table_t *dd_spaces) {
6031 ulint len;
6032 const byte *field;
6033 char *prop_str;
6034
6035 ut_ad(!rec_get_deleted_flag(rec, dict_table_is_comp(dd_spaces)));
6036
6037 ulint *offsets = rec_get_offsets(rec, dd_spaces->first_index(), nullptr,
6038 ULINT_UNDEFINED, &heap);
6039
6040 const dd::Object_table &dd_object_table = dd::get_dd_table<dd::Tablespace>();
6041
6042 field = rec_get_nth_field(
6043 rec, offsets,
6044 dd_object_table.field_number("FIELD_ENGINE") + DD_FIELD_OFFSET, &len);
6045
6046 /* If "engine" field is not "innodb", return. */
6047 if (strncmp((const char *)field, "InnoDB", 6) != 0) {
6048 return (false);
6049 }
6050
6051 /* Get name field. */
6052 field = rec_get_nth_field(
6053 rec, offsets,
6054 dd_object_table.field_number("FIELD_NAME") + DD_FIELD_OFFSET, &len);
6055 *name = reinterpret_cast<char *>(mem_heap_zalloc(heap, len + 1));
6056 memcpy(*name, field, len);
6057
6058 /* Get the se_private_data field. */
6059 field = (const byte *)rec_get_nth_field(
6060 rec, offsets,
6061 dd_object_table.field_number("FIELD_SE_PRIVATE_DATA") + DD_FIELD_OFFSET,
6062 &len);
6063
6064 if (len == 0 || len == UNIV_SQL_NULL) {
6065 return (false);
6066 }
6067
6068 prop_str = static_cast<char *>(mem_heap_zalloc(heap, len + 1));
6069 memcpy(prop_str, field, len);
6070 dd::String_type prop(prop_str);
6071 const dd::Properties *p = dd::Properties::parse_properties(prop);
6072
6073 if (!p || !p->exists(dd_space_key_strings[DD_SPACE_ID]) ||
6074 !p->exists(dd_index_key_strings[DD_SPACE_FLAGS])) {
6075 if (p) {
6076 delete p;
6077 }
6078 return (false);
6079 }
6080
6081 /* Get space id. */
6082 if (p->get(dd_space_key_strings[DD_SPACE_ID], space_id)) {
6083 delete p;
6084 return (false);
6085 }
6086
6087 /* Get space flags. */
6088 if (p->get(dd_space_key_strings[DD_SPACE_FLAGS], flags)) {
6089 delete p;
6090 return (false);
6091 }
6092
6093 /* Get server version. */
6094 if (p->get(dd_space_key_strings[DD_SPACE_SERVER_VERSION], server_version)) {
6095 delete p;
6096 return (false);
6097 }
6098
6099 /* Get space version. */
6100 if (p->get(dd_space_key_strings[DD_SPACE_VERSION], space_version)) {
6101 delete p;
6102 return (false);
6103 }
6104
6105 /* Get tablespace state. */
6106 dd_tablespace_get_state(p, state, *space_id);
6107
6108 /* For UNDO tablespaces, encryption is governed by srv_undo_log_encrypt
6109 variable and DD flags are not updated for encryption changes. Following
6110 is a workaround until UNDO tablespace encryption change is done by a DDL. */
6111 if (fsp_is_undo_tablespace(*space_id)) {
6112 *is_encrypted = srv_undo_log_encrypt;
6113 } else if (FSP_FLAGS_GET_ENCRYPTION(*flags)) {
6114 /* Get Encryption. */
6115 *is_encrypted = true;
6116 }
6117
6118 delete p;
6119
6120 return (true);
6121 }
6122
6123 /** Get dd tablespace id for fts table
6124 @param[in] parent_table parent table of fts table
6125 @param[in] table fts table
6126 @param[in,out] dd_space_id dd table space id
6127 @return true on success, false on failure. */
dd_get_fts_tablespace_id(const dict_table_t * parent_table,const dict_table_t * table,dd::Object_id & dd_space_id)6128 bool dd_get_fts_tablespace_id(const dict_table_t *parent_table,
6129 const dict_table_t *table,
6130 dd::Object_id &dd_space_id) {
6131 THD *thd = current_thd;
6132 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
6133 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6134
6135 dd::Object_id space_id = parent_table->dd_space_id;
6136
6137 dd_space_id = dd::INVALID_OBJECT_ID;
6138
6139 if (dict_table_is_file_per_table(table)) {
6140 /* This means user table and file_per_table */
6141 bool ret;
6142 char *filename = fil_space_get_first_path(table->space);
6143
6144 ret = dd_create_implicit_tablespace(client, thd, table->space,
6145 table->name.m_name, filename, false,
6146 dd_space_id);
6147
6148 ut_free(filename);
6149 if (ret) {
6150 return (false);
6151 }
6152
6153 } else if (table->space != TRX_SYS_SPACE &&
6154 table->space != srv_tmp_space.space_id()) {
6155 /* This is a user table that resides in shared tablespace */
6156 ut_ad(!dict_table_is_file_per_table(table));
6157 ut_ad(DICT_TF_HAS_SHARED_SPACE(table->flags));
6158
6159 /* Currently the tablespace id is hard coded as 0 */
6160 dd_space_id = space_id;
6161
6162 const dd::Tablespace *index_space = nullptr;
6163 if (client->acquire<dd::Tablespace>(space_id, &index_space)) {
6164 return (false);
6165 }
6166
6167 uint32 id;
6168 if (index_space == nullptr) {
6169 return (false);
6170 } else if (index_space->se_private_data().get(
6171 dd_space_key_strings[DD_SPACE_ID], &id) ||
6172 id != table->space) {
6173 ut_ad(!"missing or incorrect tablespace id");
6174 return (false);
6175 }
6176 } else if (table->space == TRX_SYS_SPACE) {
6177 /* This is a user table that resides in innodb_system
6178 tablespace */
6179 ut_ad(!dict_table_is_file_per_table(table));
6180 dd_space_id = dict_sys_t::s_dd_sys_space_id;
6181 }
6182
6183 return (true);
6184 }
6185
6186 /** Set table options for fts dd tables according to dict table
6187 @param[in,out] dd_table dd table instance
6188 @param[in] table dict table instance */
dd_set_fts_table_options(dd::Table * dd_table,const dict_table_t * table)6189 void dd_set_fts_table_options(dd::Table *dd_table, const dict_table_t *table) {
6190 dd_table->set_engine(innobase_hton_name);
6191 dd_table->set_hidden(dd::Abstract_table::HT_HIDDEN_SE);
6192 dd_table->set_collation_id(my_charset_bin.number);
6193
6194 dd::Table::enum_row_format row_format = dd::Table::RF_DYNAMIC;
6195 switch (dict_tf_get_rec_format(table->flags)) {
6196 case REC_FORMAT_REDUNDANT:
6197 row_format = dd::Table::RF_REDUNDANT;
6198 break;
6199 case REC_FORMAT_COMPACT:
6200 row_format = dd::Table::RF_COMPACT;
6201 break;
6202 case REC_FORMAT_COMPRESSED:
6203 row_format = dd::Table::RF_COMPRESSED;
6204 break;
6205 case REC_FORMAT_DYNAMIC:
6206 row_format = dd::Table::RF_DYNAMIC;
6207 break;
6208 default:
6209 ut_a(0);
6210 }
6211
6212 dd_table->set_row_format(row_format);
6213
6214 /* FTS AUX tables are always not encrypted/compressed
6215 as it is designed now. So both "compress" and "encrypt_type"
6216 option are not set */
6217
6218 dd::Properties *table_options = &dd_table->options();
6219 table_options->set("pack_record", true);
6220 table_options->set("checksum", false);
6221 table_options->set("delay_key_write", false);
6222 table_options->set("avg_row_length", 0);
6223 table_options->set("stats_sample_pages", 0);
6224 table_options->set("stats_auto_recalc", HA_STATS_AUTO_RECALC_DEFAULT);
6225
6226 if (auto zip_ssize = DICT_TF_GET_ZIP_SSIZE(table->flags)) {
6227 table_options->set("key_block_size", 1 << (zip_ssize - 1));
6228 } else {
6229 table_options->set("key_block_size", 0);
6230 }
6231 }
6232
6233 /** Add nullability info to column se_private_data
6234 @param[in,out] dd_col DD table column
6235 @param[in] col InnoDB table column */
dd_set_fts_nullability(dd::Column * dd_col,const dict_col_t * col)6236 static void dd_set_fts_nullability(dd::Column *dd_col, const dict_col_t *col) {
6237 bool is_nullable = !(col->prtype & DATA_NOT_NULL);
6238 dd::Properties &p = dd_col->se_private_data();
6239 p.set("nullable", is_nullable);
6240 }
6241
6242 /** Create dd table for fts aux index table
6243 @param[in] parent_table parent table of fts table
6244 @param[in,out] table fts table
6245 @param[in] charset fts index charset
6246 @return true on success, false on failure */
dd_create_fts_index_table(const dict_table_t * parent_table,dict_table_t * table,const CHARSET_INFO * charset)6247 bool dd_create_fts_index_table(const dict_table_t *parent_table,
6248 dict_table_t *table,
6249 const CHARSET_INFO *charset) {
6250 ut_ad(charset != nullptr);
6251
6252 std::string db_name;
6253 std::string table_name;
6254 dict_name::get_table(table->name.m_name, db_name, table_name);
6255
6256 /* Create dd::Table object */
6257 THD *thd = current_thd;
6258 dd::Schema_MDL_locker mdl_locker(thd);
6259 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
6260 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6261
6262 const dd::Schema *schema = nullptr;
6263 if (mdl_locker.ensure_locked(db_name.c_str()) ||
6264 client->acquire<dd::Schema>(db_name.c_str(), &schema)) {
6265 return (false);
6266 }
6267
6268 /* Check if schema is nullptr? */
6269 if (schema == nullptr) {
6270 my_error(ER_BAD_DB_ERROR, MYF(0), db_name.c_str());
6271 return (false);
6272 }
6273
6274 std::unique_ptr<dd::Table> dd_table_obj(schema->create_table(thd));
6275 dd::Table *dd_table = dd_table_obj.get();
6276
6277 dd_table->set_name(table_name.c_str());
6278 dd_table->set_schema_id(schema->id());
6279
6280 dd_set_fts_table_options(dd_table, table);
6281
6282 /* FTS AUX tables are always not encrypted/compressed
6283 as it is designed now. So both "compress" and "encrypt_type"
6284 option are not set */
6285
6286 /* Fill columns */
6287 /* 1st column: word */
6288 const char *col_name = nullptr;
6289 dd::Column *col = dd_table->add_column();
6290 col_name = "word";
6291 col->set_name(col_name);
6292 col->set_type(dd::enum_column_types::VARCHAR);
6293 col->set_char_length(FTS_INDEX_WORD_LEN);
6294 col->set_nullable(false);
6295 col->set_collation_id(charset->number);
6296 ut_ad(strcmp(col_name, table->get_col_name(0)) == 0);
6297 dd_set_fts_nullability(col, table->get_col(0));
6298
6299 dd::Column *key_col1 = col;
6300
6301 /* 2nd column: first_doc_id */
6302 col = dd_table->add_column();
6303 col->set_name("first_doc_id");
6304 col->set_type(dd::enum_column_types::LONGLONG);
6305 col->set_char_length(20);
6306 col->set_numeric_scale(0);
6307 col->set_nullable(false);
6308 col->set_unsigned(true);
6309 col->set_collation_id(charset->number);
6310
6311 dd::Column *key_col2 = col;
6312
6313 /* 3rd column: last_doc_id */
6314 col = dd_table->add_column();
6315 col->set_name("last_doc_id");
6316 col->set_type(dd::enum_column_types::LONGLONG);
6317 col->set_char_length(20);
6318 col->set_numeric_scale(0);
6319 col->set_nullable(false);
6320 col->set_unsigned(true);
6321 col->set_collation_id(charset->number);
6322
6323 /* 4th column: doc_count */
6324 col = dd_table->add_column();
6325 col->set_name("doc_count");
6326 col->set_type(dd::enum_column_types::LONG);
6327 col->set_char_length(4);
6328 col->set_numeric_scale(0);
6329 col->set_nullable(false);
6330 col->set_unsigned(true);
6331 col->set_collation_id(charset->number);
6332
6333 /* 5th column: ilist */
6334 col = dd_table->add_column();
6335 col->set_name("ilist");
6336 col->set_type(dd::enum_column_types::BLOB);
6337 col->set_char_length(8);
6338 col->set_nullable(false);
6339 col->set_collation_id(my_charset_bin.number);
6340
6341 /* Fill index */
6342 dd::Index *index = dd_table->add_index();
6343 index->set_name("FTS_INDEX_TABLE_IND");
6344 index->set_algorithm(dd::Index::IA_BTREE);
6345 index->set_algorithm_explicit(false);
6346 index->set_visible(true);
6347 index->set_type(dd::Index::IT_PRIMARY);
6348 index->set_ordinal_position(1);
6349 index->set_generated(false);
6350 index->set_engine(dd_table->engine());
6351
6352 index->options().set("flags", 32);
6353
6354 dd::Index_element *index_elem;
6355 index_elem = index->add_element(key_col1);
6356 index_elem->set_length(FTS_INDEX_WORD_LEN);
6357
6358 index_elem = index->add_element(key_col2);
6359 index_elem->set_length(FTS_INDEX_FIRST_DOC_ID_LEN);
6360
6361 /* Fill table space info, etc */
6362 dd::Object_id dd_space_id;
6363 if (!dd_get_fts_tablespace_id(parent_table, table, dd_space_id)) {
6364 return (false);
6365 }
6366
6367 table->dd_space_id = dd_space_id;
6368
6369 dd_write_table(dd_space_id, dd_table, table);
6370
6371 MDL_ticket *mdl_ticket = nullptr;
6372 if (dd::acquire_exclusive_table_mdl(thd, db_name.c_str(), table_name.c_str(),
6373 false, &mdl_ticket)) {
6374 ut_ad(0);
6375 return (false);
6376 }
6377
6378 /* Store table to dd */
6379 bool fail = client->store(dd_table);
6380 if (fail) {
6381 ut_ad(0);
6382 return (false);
6383 }
6384
6385 return (true);
6386 }
6387
6388 /** Create dd table for fts aux common table
6389 @param[in] parent_table parent table of fts table
6390 @param[in,out] table fts table
6391 @param[in] is_config flag whether it's fts aux configure table
6392 @return true on success, false on failure */
dd_create_fts_common_table(const dict_table_t * parent_table,dict_table_t * table,bool is_config)6393 bool dd_create_fts_common_table(const dict_table_t *parent_table,
6394 dict_table_t *table, bool is_config) {
6395 std::string db_name;
6396 std::string table_name;
6397
6398 dict_name::get_table(table->name.m_name, db_name, table_name);
6399
6400 /* Create dd::Table object */
6401 THD *thd = current_thd;
6402 dd::Schema_MDL_locker mdl_locker(thd);
6403 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
6404 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6405
6406 const dd::Schema *schema = nullptr;
6407 if (mdl_locker.ensure_locked(db_name.c_str()) ||
6408 client->acquire<dd::Schema>(db_name.c_str(), &schema)) {
6409 return (false);
6410 }
6411
6412 /* Check if schema is nullptr */
6413 if (schema == nullptr) {
6414 my_error(ER_BAD_DB_ERROR, MYF(0), db_name.c_str());
6415 return (false);
6416 }
6417
6418 std::unique_ptr<dd::Table> dd_table_obj(schema->create_table(thd));
6419 dd::Table *dd_table = dd_table_obj.get();
6420
6421 dd_table->set_name(table_name.c_str());
6422 dd_table->set_schema_id(schema->id());
6423
6424 dd_set_fts_table_options(dd_table, table);
6425 const char *col_name = nullptr;
6426
6427 /* Fill columns */
6428 if (!is_config) {
6429 /* 1st column: doc_id */
6430 dd::Column *col = dd_table->add_column();
6431 col_name = "doc_id";
6432 col->set_name(col_name);
6433 col->set_type(dd::enum_column_types::LONGLONG);
6434 col->set_char_length(20);
6435 col->set_numeric_scale(0);
6436 col->set_nullable(false);
6437 col->set_unsigned(true);
6438 col->set_collation_id(my_charset_bin.number);
6439 ut_ad(strcmp(col_name, table->get_col_name(0)) == 0);
6440 dd_set_fts_nullability(col, table->get_col(0));
6441
6442 dd::Column *key_col1 = col;
6443
6444 /* Fill index */
6445 dd::Index *index = dd_table->add_index();
6446 index->set_name("FTS_COMMON_TABLE_IND");
6447 index->set_algorithm(dd::Index::IA_BTREE);
6448 index->set_algorithm_explicit(false);
6449 index->set_visible(true);
6450 index->set_type(dd::Index::IT_PRIMARY);
6451 index->set_ordinal_position(1);
6452 index->set_generated(false);
6453 index->set_engine(dd_table->engine());
6454
6455 index->options().set("flags", 32);
6456
6457 dd::Index_element *index_elem;
6458 index_elem = index->add_element(key_col1);
6459 index_elem->set_length(FTS_INDEX_FIRST_DOC_ID_LEN);
6460 } else {
6461 /* Fill columns */
6462 /* 1st column: key */
6463 dd::Column *col = dd_table->add_column();
6464 col_name = "key";
6465 col->set_name(col_name);
6466 col->set_type(dd::enum_column_types::VARCHAR);
6467 col->set_char_length(FTS_CONFIG_TABLE_KEY_COL_LEN);
6468 col->set_nullable(false);
6469 col->set_collation_id(my_charset_latin1.number);
6470 ut_ad(strcmp(col_name, table->get_col_name(0)) == 0);
6471 dd_set_fts_nullability(col, table->get_col(0));
6472
6473 dd::Column *key_col1 = col;
6474
6475 /* 2nd column: value */
6476 col = dd_table->add_column();
6477 col->set_name("value");
6478 col->set_type(dd::enum_column_types::VARCHAR);
6479 col->set_char_length(FTS_CONFIG_TABLE_VALUE_COL_LEN);
6480 col->set_nullable(false);
6481 col->set_collation_id(my_charset_latin1.number);
6482
6483 /* Fill index */
6484 dd::Index *index = dd_table->add_index();
6485 index->set_name("FTS_COMMON_TABLE_IND");
6486 index->set_algorithm(dd::Index::IA_BTREE);
6487 index->set_algorithm_explicit(false);
6488 index->set_visible(true);
6489 index->set_type(dd::Index::IT_PRIMARY);
6490 index->set_ordinal_position(1);
6491 index->set_generated(false);
6492 index->set_engine(dd_table->engine());
6493
6494 index->options().set("flags", 32);
6495
6496 dd::Index_element *index_elem;
6497 index_elem = index->add_element(key_col1);
6498 index_elem->set_length(FTS_CONFIG_TABLE_KEY_COL_LEN);
6499 }
6500
6501 /* Fill table space info, etc */
6502 dd::Object_id dd_space_id;
6503 if (!dd_get_fts_tablespace_id(parent_table, table, dd_space_id)) {
6504 ut_ad(0);
6505 return (false);
6506 }
6507
6508 table->dd_space_id = dd_space_id;
6509
6510 dd_write_table(dd_space_id, dd_table, table);
6511
6512 MDL_ticket *mdl_ticket = nullptr;
6513 if (dd::acquire_exclusive_table_mdl(thd, db_name.c_str(), table_name.c_str(),
6514 false, &mdl_ticket)) {
6515 return (false);
6516 }
6517
6518 /* Store table to dd */
6519 bool fail = client->store(dd_table);
6520 if (fail) {
6521 ut_ad(0);
6522 return (false);
6523 }
6524
6525 return (true);
6526 }
6527
6528 /** Drop dd table & tablespace for fts aux table
6529 @param[in] name table name
6530 @param[in] file_per_table flag whether use file per table
6531 @return true on success, false on failure. */
dd_drop_fts_table(const char * name,bool file_per_table)6532 bool dd_drop_fts_table(const char *name, bool file_per_table) {
6533 std::string db_name;
6534 std::string table_name;
6535
6536 dict_name::get_table(name, db_name, table_name);
6537
6538 /* Create dd::Table object */
6539 THD *thd = current_thd;
6540 dd::Schema_MDL_locker mdl_locker(thd);
6541 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
6542 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6543
6544 MDL_ticket *mdl_ticket = nullptr;
6545 if (dd::acquire_exclusive_table_mdl(thd, db_name.c_str(), table_name.c_str(),
6546 false, &mdl_ticket)) {
6547 return (false);
6548 }
6549
6550 const dd::Table *dd_table = nullptr;
6551 if (client->acquire<dd::Table>(db_name.c_str(), table_name.c_str(),
6552 &dd_table)) {
6553 return (false);
6554 }
6555
6556 if (dd_table == nullptr) {
6557 return (false);
6558 }
6559
6560 if (file_per_table) {
6561 dd::Object_id dd_space_id = (*dd_table->indexes().begin())->tablespace_id();
6562 bool error;
6563 error = dd_drop_tablespace(client, thd, dd_space_id);
6564 ut_a(!error);
6565 }
6566
6567 if (client->drop(dd_table)) {
6568 return (false);
6569 }
6570
6571 return (true);
6572 }
6573
6574 /** Rename dd table & tablespace files for fts aux table
6575 @param[in] table dict table
6576 @param[in] old_name old innodb table name
6577 @return true on success, false on failure. */
dd_rename_fts_table(const dict_table_t * table,const char * old_name)6578 bool dd_rename_fts_table(const dict_table_t *table, const char *old_name) {
6579 std::string new_db;
6580 std::string new_table;
6581 dict_name::get_table(table->name.m_name, new_db, new_table);
6582
6583 std::string old_db;
6584 std::string old_table;
6585 dict_name::get_table(old_name, old_db, old_table);
6586
6587 ut_ad(new_db.compare(old_db) != 0);
6588 ut_ad(new_table.compare(old_table) == 0);
6589
6590 /* Create dd::Table object */
6591 THD *thd = current_thd;
6592 dd::Schema_MDL_locker mdl_locker(thd);
6593 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
6594 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6595
6596 const dd::Schema *to_sch = nullptr;
6597 if (client->acquire<dd::Schema>(new_db.c_str(), &to_sch)) {
6598 return (false);
6599 }
6600
6601 MDL_ticket *mdl_ticket = nullptr;
6602 if (dd::acquire_exclusive_table_mdl(thd, old_db.c_str(), old_table.c_str(),
6603 false, &mdl_ticket)) {
6604 return (false);
6605 }
6606
6607 MDL_ticket *mdl_ticket2 = nullptr;
6608 if (dd::acquire_exclusive_table_mdl(thd, new_db.c_str(), new_table.c_str(),
6609 false, &mdl_ticket2)) {
6610 return (false);
6611 }
6612
6613 dd::Table *dd_table = nullptr;
6614 if (client->acquire_for_modification<dd::Table>(
6615 old_db.c_str(), old_table.c_str(), &dd_table)) {
6616 return (false);
6617 }
6618
6619 // Set schema id
6620 dd_table->set_schema_id(to_sch->id());
6621
6622 /* Rename dd tablespace file */
6623 if (dict_table_is_file_per_table(table)) {
6624 char *new_path = fil_space_get_first_path(table->space);
6625
6626 if (dd_tablespace_rename(table->dd_space_id, false, table->name.m_name,
6627 new_path) != DB_SUCCESS) {
6628 ut_a(false);
6629 }
6630
6631 ut_free(new_path);
6632 }
6633
6634 if (client->update(dd_table)) {
6635 ut_ad(0);
6636 return (false);
6637 }
6638
6639 return (true);
6640 }
6641
6642 /** Set the space_id attribute in se_private_data of tablespace
6643 @param[in,out] dd_space dd::Tablespace object
6644 @param[in] space_id tablespace ID */
dd_tablespace_set_space_id(dd::Tablespace * dd_space,space_id_t space_id)6645 void dd_tablespace_set_space_id(dd::Tablespace *dd_space, space_id_t space_id) {
6646 dd::Properties &p = dd_space->se_private_data();
6647
6648 p.set(dd_space_key_strings[DD_SPACE_ID], space_id);
6649 }
6650
6651 /** Get the space_id attribute value to se_private_data of tablespace
6652 @param[in] dd_space dd::Tablespace object
6653 @param[in,out] space_id tablespace ID
6654 @retval true on error (DD_FAILURE)
6655 @retval false on success (DD_SUCCESS) */
dd_tablespace_get_space_id(const dd::Tablespace * dd_space,space_id_t * space_id)6656 bool dd_tablespace_get_space_id(const dd::Tablespace *dd_space,
6657 space_id_t *space_id) {
6658 const dd::Properties &p = dd_space->se_private_data();
6659 if (p.exists(dd_space_key_strings[DD_SPACE_ID])) {
6660 p.get(dd_space_key_strings[DD_SPACE_ID], space_id);
6661
6662 return (DD_SUCCESS);
6663 }
6664 return (DD_FAILURE);
6665 }
6666
6667 /** Get state attribute value in dd::Tablespace::se_private_data
6668 @param[in,out] dd_space dd::Tablespace object
6669 @param[in] state value to set for key 'state' */
dd_tablespace_set_state(dd::Tablespace * dd_space,dd_space_states state)6670 void dd_tablespace_set_state(dd::Tablespace *dd_space, dd_space_states state) {
6671 dd::Properties &p = dd_space->se_private_data();
6672
6673 p.set(dd_space_key_strings[DD_SPACE_STATE], dd_space_state_values[state]);
6674 }
6675
6676 /** Set Space ID and state attribute in se_private_data of mysql.tablespaces
6677 for the named tablespace.
6678 @param[in] space_name tablespace name
6679 @param[in] space_id tablespace id
6680 @param[in] state value to set for key 'state'
6681 @return DB_SUCCESS or DD_FAILURE. */
dd_tablespace_set_id_and_state(const char * space_name,space_id_t space_id,dd_space_states state)6682 bool dd_tablespace_set_id_and_state(const char *space_name, space_id_t space_id,
6683 dd_space_states state) {
6684 THD *thd = current_thd;
6685 dd::Tablespace *dd_space;
6686
6687 dd::cache::Dictionary_client *dc = dd::get_dd_client(thd);
6688 dd::cache::Dictionary_client::Auto_releaser releaser{dc};
6689 dd::String_type tsn{space_name};
6690
6691 bool dd_result = dc->acquire_for_modification(tsn, &dd_space);
6692 if (dd_space == nullptr) {
6693 return (DD_FAILURE);
6694 }
6695
6696 dd_tablespace_set_space_id(dd_space, space_id);
6697
6698 dd_tablespace_set_state(dd_space, state);
6699
6700 return (dd::commit_or_rollback_tablespace_change(thd, dd_space, dd_result));
6701 }
6702
dd_tablespace_get_state(const dd::Tablespace * dd_space,dd::String_type * state,space_id_t space_id)6703 void dd_tablespace_get_state(const dd::Tablespace *dd_space,
6704 dd::String_type *state, space_id_t space_id) {
6705 const dd::Properties &p = dd_space->se_private_data();
6706
6707 dd_tablespace_get_state(&p, state, space_id);
6708 }
6709
dd_tablespace_get_state(const dd::Properties * p,dd::String_type * state,space_id_t space_id)6710 void dd_tablespace_get_state(const dd::Properties *p, dd::String_type *state,
6711 space_id_t space_id) {
6712 if (p->exists(dd_space_key_strings[DD_SPACE_STATE])) {
6713 p->get(dd_space_key_strings[DD_SPACE_STATE], state);
6714 } else {
6715 /* If this k/v pair is missing then the database may have been created
6716 by an earlier version. So calculate the state. */
6717 dd_space_states state_enum = dd_tablespace_get_state_enum(p, space_id);
6718 *state = dd_space_state_values[state_enum];
6719 }
6720 }
6721
dd_tablespace_get_state_enum(const dd::Tablespace * dd_space,space_id_t space_id)6722 dd_space_states dd_tablespace_get_state_enum(const dd::Tablespace *dd_space,
6723 space_id_t space_id) {
6724 dd_space_states state_enum = DD_SPACE_STATE__LAST;
6725
6726 const dd::Properties &p = dd_space->se_private_data();
6727
6728 if (p.exists(dd_space_key_strings[DD_SPACE_STATE])) {
6729 dd::String_type state;
6730 p.get(dd_space_key_strings[DD_SPACE_STATE], &state);
6731
6732 /* Convert this string to a number. */
6733 for (int s = DD_SPACE_STATE_NORMAL; s < DD_SPACE_STATE__LAST; s++) {
6734 if (state == dd_space_state_values[s]) {
6735 state_enum = (dd_space_states)s;
6736 break;
6737 }
6738 }
6739
6740 } else {
6741 /* If this k/v pair is missing then the database may have been created
6742 by an earlier version. So calculate the state. */
6743 state_enum = dd_tablespace_get_state_enum(&p, space_id);
6744 }
6745
6746 return (state_enum);
6747 }
6748
dd_tablespace_get_state_enum(const dd::Properties * p,space_id_t space_id)6749 dd_space_states dd_tablespace_get_state_enum(const dd::Properties *p,
6750 space_id_t space_id) {
6751 dd_space_states state_enum;
6752
6753 if (space_id == SPACE_UNKNOWN) {
6754 if (p->exists(dd_space_key_strings[DD_SPACE_ID])) {
6755 p->get(dd_space_key_strings[DD_SPACE_ID], &space_id);
6756 } else {
6757 return (DD_SPACE_STATE__LAST);
6758 }
6759 }
6760 ut_ad(space_id != SPACE_UNKNOWN);
6761
6762 if (fsp_is_undo_tablespace(space_id)) {
6763 undo::spaces->s_lock();
6764 undo::Tablespace *undo_space = undo::spaces->find(undo::id2num(space_id));
6765
6766 if (undo_space->is_active()) {
6767 state_enum = DD_SPACE_STATE_ACTIVE;
6768 } else if (undo_space->is_empty()) {
6769 state_enum = DD_SPACE_STATE_EMPTY;
6770 } else {
6771 state_enum = DD_SPACE_STATE_INACTIVE;
6772 }
6773 undo::spaces->s_unlock();
6774
6775 } else {
6776 /* IBD tablespace */
6777 bool is_discarded = false;
6778 if (p->exists(dd_space_key_strings[DD_SPACE_DISCARD])) {
6779 p->get(dd_space_key_strings[DD_SPACE_DISCARD], &is_discarded);
6780 }
6781
6782 state_enum =
6783 is_discarded ? DD_SPACE_STATE_DISCARDED : DD_SPACE_STATE_NORMAL;
6784 }
6785
6786 return (state_enum);
6787 }
6788
6789 /** Get the discarded state from se_private_data of tablespace
6790 @param[in] dd_space dd::Tablespace object */
dd_tablespace_is_discarded(const dd::Tablespace * dd_space)6791 bool dd_tablespace_is_discarded(const dd::Tablespace *dd_space) {
6792 dd::String_type dd_state;
6793
6794 dd_tablespace_get_state(dd_space, &dd_state);
6795
6796 if (dd_state == dd_space_state_values[DD_SPACE_STATE_DISCARDED]) {
6797 return (true);
6798 }
6799
6800 return (false);
6801 }
6802
dd_tablespace_get_mdl(const char * space_name,MDL_ticket ** mdl_ticket,bool foreground)6803 bool dd_tablespace_get_mdl(const char *space_name, MDL_ticket **mdl_ticket,
6804 bool foreground) {
6805 THD *thd = current_thd;
6806 /* Safeguard in release mode if background thread doesn't have THD. */
6807 if (thd == nullptr) {
6808 ut_ad(false);
6809 return (true);
6810 }
6811 /* Explicit duration for background threads. */
6812 bool trx_duration = foreground;
6813
6814 /* Background thread should not block on MDL lock. */
6815 ulong timeout = foreground ? thd->variables.lock_wait_timeout : 0;
6816 bool result = acquire_shared_backup_lock(thd, timeout, trx_duration);
6817
6818 if (!result) {
6819 result = dd::acquire_exclusive_tablespace_mdl(thd, space_name, false,
6820 mdl_ticket, trx_duration);
6821 if (result) {
6822 release_backup_lock(thd);
6823 }
6824 }
6825
6826 /* For background thread, clear timeout error. */
6827 if (result && !foreground && thd->is_error()) {
6828 thd->clear_error();
6829 }
6830 return (result);
6831 }
6832
6833 /** Release the MDL held by the given ticket.
6834 @param[in] mdl_ticket tablespace MDL ticket */
dd_release_mdl(MDL_ticket * mdl_ticket)6835 void dd_release_mdl(MDL_ticket *mdl_ticket) {
6836 dd::release_mdl(current_thd, mdl_ticket);
6837 release_backup_lock(current_thd);
6838 }
6839
6840 #ifdef UNIV_DEBUG
6841 /** @return total number of indexes of all DD tables */
dd_get_total_indexes_num()6842 uint32_t dd_get_total_indexes_num() {
6843 uint32_t indexes_count = 0;
6844 for (uint32_t idx = 0; idx < innodb_dd_table_size; idx++) {
6845 indexes_count += innodb_dd_table[idx].n_indexes;
6846 }
6847 return (indexes_count);
6848 }
6849 #endif /* UNIV_DEBUG */
6850
6851 /** Open a table from its database and table name, this is currently used by
6852 foreign constraint parser to get the referenced table.
6853 @param[in] name foreign key table name
6854 @param[in] database_name table db name
6855 @param[in] database_name_len db name length
6856 @param[in] table_name table db name
6857 @param[in] table_name_len table name length
6858 @param[in,out] table table object or NULL
6859 @param[in,out] mdl mdl on table
6860 @param[in,out] heap heap memory
6861 @return complete table name with database and table name, allocated from
6862 heap memory passed in */
dd_get_referenced_table(const char * name,const char * database_name,ulint database_name_len,const char * table_name,ulint table_name_len,dict_table_t ** table,MDL_ticket ** mdl,mem_heap_t * heap)6863 char *dd_get_referenced_table(const char *name, const char *database_name,
6864 ulint database_name_len, const char *table_name,
6865 ulint table_name_len, dict_table_t **table,
6866 MDL_ticket **mdl, mem_heap_t *heap) {
6867 char *ref;
6868 const char *db_name;
6869
6870 bool is_part = dict_name::is_partition(name);
6871
6872 *table = nullptr;
6873
6874 if (!database_name) {
6875 /* Use the database name of the foreign key table */
6876
6877 db_name = name;
6878 database_name_len = dict_get_db_name_len(name);
6879 } else {
6880 db_name = database_name;
6881 }
6882
6883 /* Copy database_name, '/', table_name, '\0' */
6884 ref = static_cast<char *>(
6885 mem_heap_alloc(heap, database_name_len + table_name_len + 2));
6886
6887 memcpy(ref, db_name, database_name_len);
6888 ref[database_name_len] = '/';
6889 memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
6890
6891 /* Values; 0 = Store and compare as given; case sensitive
6892 1 = Store and compare in lower; case insensitive
6893 2 = Store as given, compare in lower; case semi-sensitive */
6894 if (innobase_get_lower_case_table_names() == 2) {
6895 innobase_casedn_str(ref);
6896 if (!is_part) {
6897 *table = dd_table_open_on_name(current_thd, mdl, ref, true,
6898 DICT_ERR_IGNORE_NONE);
6899 }
6900 memcpy(ref, db_name, database_name_len);
6901 ref[database_name_len] = '/';
6902 memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
6903
6904 } else {
6905 #ifndef _WIN32
6906 if (innobase_get_lower_case_table_names() == 1) {
6907 innobase_casedn_str(ref);
6908 }
6909 #else
6910 innobase_casedn_str(ref);
6911 #endif /* !_WIN32 */
6912 if (!is_part) {
6913 *table = dd_table_open_on_name(current_thd, mdl, ref, true,
6914 DICT_ERR_IGNORE_NONE);
6915 }
6916 }
6917
6918 return (ref);
6919 }
6920
6921 /** Update all InnoDB tablespace cache objects. This step is done post
6922 dictionary trx rollback, binlog recovery and DDL_LOG apply. So DD is
6923 consistent. Update the cached tablespace objects, if they differ from
6924 the dictionary.
6925 @param[in,out] thd thread handle
6926 @retval true on error
6927 @retval false on success */
dd_tablespace_update_cache(THD * thd)6928 bool dd_tablespace_update_cache(THD *thd) {
6929 /* If there are no prepared trxs, then DD reads would have been
6930 already consistent. No need to update cache */
6931 if (!trx_sys->found_prepared_trx) {
6932 return (false);
6933 }
6934
6935 dd::cache::Dictionary_client *dc = dd::get_dd_client(thd);
6936 dd::cache::Dictionary_client::Auto_releaser releaser(dc);
6937 std::vector<const dd::Tablespace *> tablespaces;
6938
6939 space_id_t max_id = 0;
6940
6941 if (dc->fetch_global_components(&tablespaces)) {
6942 return (true);
6943 }
6944
6945 bool fail = false;
6946
6947 for (const dd::Tablespace *t : tablespaces) {
6948 ut_ad(!fail);
6949
6950 if (t->engine() != innobase_hton_name) {
6951 continue;
6952 }
6953
6954 const dd::Properties &p = t->se_private_data();
6955 uint32 id;
6956 uint32 flags = 0;
6957
6958 /* There should be exactly one file name associated
6959 with each InnoDB tablespace, except innodb_system */
6960 fail = p.get(dd_space_key_strings[DD_SPACE_ID], &id) ||
6961 p.get(dd_space_key_strings[DD_SPACE_FLAGS], &flags) ||
6962 (t->files().size() != 1 &&
6963 strcmp(t->name().c_str(), dict_sys_t::s_sys_space_name) != 0);
6964
6965 if (fail) {
6966 break;
6967 }
6968
6969 /* Undo tablespaces may be deleted and re-created at
6970 startup and not registered in DD. So exempt undo tablespaces
6971 from verification */
6972 if (fsp_is_undo_tablespace(id)) {
6973 continue;
6974 }
6975
6976 if (!dict_sys_t::is_reserved(id) && id > max_id) {
6977 /* Currently try to find the max one only, it should
6978 be able to reuse the deleted smaller ones later */
6979 max_id = id;
6980 }
6981
6982 const dd::Tablespace_file *f = *t->files().begin();
6983 fail = f == nullptr;
6984 if (fail) {
6985 break;
6986 }
6987
6988 const char *space_name = t->name().c_str();
6989 fil_space_t *space = fil_space_get(id);
6990
6991 if (space != nullptr) {
6992 /* If the tablespace is already in cache, verify that
6993 the tablespace name matches the name in dictionary.
6994 If it doesn't match, use the name from dictionary. */
6995
6996 /* Exclude Encryption flag as (un)encryption operation might be
6997 rolling forward in background thread. */
6998 ut_ad(!((space->flags ^ flags) & ~(FSP_FLAGS_MASK_ENCRYPTION)));
6999
7000 fil_space_update_name(space, space_name);
7001
7002 } else {
7003 fil_type_t purpose = fsp_is_system_temporary(id) ? FIL_TYPE_TEMPORARY
7004 : FIL_TYPE_TABLESPACE;
7005
7006 const char *filename = f->filename().c_str();
7007
7008 /* If the user tablespace is not in cache, load the
7009 tablespace now, with the name from dictionary */
7010
7011 /* It's safe to pass space_name in tablename charset
7012 because filename is already in filename charset. */
7013 dberr_t err = fil_ibd_open(false, purpose, id, flags, space_name, nullptr,
7014 filename, false, false);
7015 switch (err) {
7016 case DB_SUCCESS:
7017 case DB_CANNOT_OPEN_FILE:
7018 break;
7019 default:
7020 ib::info(ER_IB_MSG_174)
7021 << "Unable to open tablespace " << id << " (flags=" << flags
7022 << ", filename=" << filename << ")."
7023 << " Have you deleted/moved the .IBD";
7024 ut_strerr(err);
7025 }
7026 }
7027 }
7028
7029 fil_set_max_space_id_if_bigger(max_id);
7030 return (fail);
7031 }
7032
7033 /* Check if the table belongs to an encrypted tablespace.
7034 @param[in] table table for which check is to be done
7035 @return true if it does. */
dd_is_table_in_encrypted_tablespace(const dict_table_t * table)7036 bool dd_is_table_in_encrypted_tablespace(const dict_table_t *table) {
7037 fil_space_t *space = fil_space_get(table->space);
7038 if (space != nullptr) {
7039 return (FSP_FLAGS_GET_ENCRYPTION(space->flags));
7040 } else {
7041 /* Its possible that tablespace flag is missing (for ex: after
7042 discard tablespace). In that case get tablespace flags from Data
7043 Dictionary/ */
7044 THD *thd = current_thd;
7045 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
7046 dd::cache::Dictionary_client::Auto_releaser releaser(client);
7047 dd::Tablespace *dd_space = nullptr;
7048
7049 if (!client->acquire_uncached_uncommitted<dd::Tablespace>(
7050 table->dd_space_id, &dd_space) &&
7051 dd_space != nullptr) {
7052 uint32 flags;
7053 dd_space->se_private_data().get(dd_space_key_strings[DD_SPACE_FLAGS],
7054 &flags);
7055
7056 return (FSP_FLAGS_GET_ENCRYPTION(flags));
7057 }
7058 /* We should not reach here */
7059 ut_ad(0);
7060 return false;
7061 }
7062 }
7063
get_table_name(std::string & schema,std::string & table)7064 void dict_table_t::get_table_name(std::string &schema, std::string &table) {
7065 std::string dict_table_name(name.m_name);
7066 dict_name::get_table(dict_table_name, schema, table);
7067 }
7068 #endif /* !UNIV_HOTBACKUP */
7069
7070 #ifndef UNIV_HOTBACKUP
7071 namespace dict_name {
file_to_table(std::string & name,bool quiet)7072 void file_to_table(std::string &name, bool quiet) {
7073 ut_ad(name.length() < FN_REFLEN);
7074 char conv_name[FN_REFLEN + 1];
7075
7076 /* Convert to system character set from file name character set. */
7077 filename_to_tablename(name.c_str(), conv_name, FN_REFLEN, quiet);
7078 name.assign(conv_name);
7079 }
7080
table_to_file(std::string & name)7081 void table_to_file(std::string &name) {
7082 ut_ad(name.length() < FN_REFLEN);
7083 char conv_name[FN_REFLEN + 1];
7084
7085 /* Convert to system character set from file name character set. */
7086 static_cast<void>(tablename_to_filename(name.c_str(), conv_name, FN_REFLEN));
7087 name.assign(conv_name);
7088 }
7089
7090 /** Get partition and sub-partition separator strings.
7091 @param[in] is_57 true, if 5.7 style separator is needed
7092 @param[out] part_sep partition separator
7093 @param[out] sub_part_sep sub-partition separator */
get_partition_separators(bool is_57,std::string & part_sep,std::string & sub_part_sep)7094 static void get_partition_separators(bool is_57, std::string &part_sep,
7095 std::string &sub_part_sep) {
7096 if (!is_57) {
7097 part_sep.assign(PART_SEPARATOR);
7098 sub_part_sep.assign(SUB_PART_SEPARATOR);
7099 return;
7100 }
7101 /* 5.7 style partition separators. */
7102 #ifdef _WIN32
7103 part_sep.assign(PART_SEPARATOR);
7104 sub_part_sep.assign(SUB_PART_SEPARATOR);
7105 #else
7106 part_sep.assign(ALT_PART_SEPARATOR);
7107 sub_part_sep.assign(ALT_SUB_PART_SEPARATOR);
7108 #endif /* _WIN32 */
7109 }
7110
7111 /** Check for partition and sub partition.
7112 @param[in] dict_name name from innodb dictionary
7113 @param[in] sub_part true, if checking sub partition
7114 @param[out] position position of partition in string
7115 @return true, iff partition/sub-partition exists. */
check_partition(const std::string & dict_name,bool sub_part,size_t & position)7116 static bool check_partition(const std::string &dict_name, bool sub_part,
7117 size_t &position) {
7118 std::string part_sep = sub_part ? SUB_PART_SEPARATOR : PART_SEPARATOR;
7119
7120 /* Check for partition separator string. */
7121 position = dict_name.find(part_sep);
7122
7123 if (position != std::string::npos) {
7124 return (true);
7125 }
7126
7127 std::string alt_sep = sub_part ? ALT_SUB_PART_SEPARATOR : ALT_PART_SEPARATOR;
7128
7129 /* Check for alternative partition separator. It is safe check for
7130 release build server and for upgrade. */
7131 position = dict_name.find(alt_sep);
7132
7133 if (position != std::string::npos) {
7134 return (true);
7135 }
7136
7137 return (false);
7138 }
7139
7140 /** Check for TMP extension name.
7141 @param[in] dict_name name from innodb dictionary
7142 @param[out] position position of TMP extension in string
7143 @return true, iff TMP extension exists. */
check_tmp(const std::string & dict_name,size_t & position)7144 static bool check_tmp(const std::string &dict_name, size_t &position) {
7145 position = dict_name.find(TMP_POSTFIX);
7146 return (position != std::string::npos);
7147 }
7148
is_partition(const std::string & dict_name)7149 bool is_partition(const std::string &dict_name) {
7150 size_t position;
7151 return (check_partition(dict_name, false, position));
7152 }
7153
get_table(const std::string & dict_name,std::string & schema,std::string & table)7154 void get_table(const std::string &dict_name, std::string &schema,
7155 std::string &table) {
7156 bool is_tmp;
7157 std::string partition;
7158 get_table(dict_name, true, schema, table, partition, is_tmp);
7159 }
7160
get_table(const std::string & dict_name,bool convert,std::string & schema,std::string & table,std::string & partition,bool & is_tmp)7161 void get_table(const std::string &dict_name, bool convert, std::string &schema,
7162 std::string &table, std::string &partition, bool &is_tmp) {
7163 size_t table_begin = dict_name.find(SCHEMA_SEPARATOR);
7164
7165 /* Check if schema is specified. */
7166 if (table_begin == std::string::npos) {
7167 table_begin = 0;
7168 schema.clear();
7169 } else {
7170 schema.assign(dict_name.substr(0, table_begin));
7171 if (convert) {
7172 /* Perform conversion if requested. Allow invalid conversion
7173 in schema name. For temp table server passes directory name
7174 instead of schema name which might contain "." resulting in
7175 conversion to "?". For temp table this schema name is never used. */
7176 file_to_table(schema, true);
7177 }
7178 ++table_begin;
7179 }
7180
7181 table.assign(dict_name.substr(table_begin));
7182 partition.clear();
7183
7184 /* Check if partitioned table. */
7185 size_t part_begin = std::string::npos;
7186 bool is_part = check_partition(table, false, part_begin);
7187
7188 /* Check if temp extension. */
7189 size_t tmp_begin = std::string::npos;
7190 is_tmp = check_tmp(table, tmp_begin);
7191
7192 if (is_part) {
7193 ut_ad(part_begin > 0);
7194 size_t part_len = std::string::npos;
7195
7196 if (is_tmp && tmp_begin > part_begin) {
7197 part_len = tmp_begin - part_begin;
7198 } else if (is_tmp) {
7199 /* TMP extension must follow partition. */
7200 ut_ad(false);
7201 }
7202 partition.assign(table.substr(part_begin, part_len));
7203 table.assign(table.substr(0, part_begin));
7204
7205 } else if (is_tmp) {
7206 ut_ad(tmp_begin > 0);
7207 table.assign(table.substr(0, tmp_begin));
7208 }
7209
7210 /* Perform conversion if requested. */
7211 if (convert) {
7212 file_to_table(table, false);
7213 }
7214 }
7215
get_partition(const std::string & partition,bool convert,std::string & part,std::string & sub_part)7216 void get_partition(const std::string &partition, bool convert,
7217 std::string &part, std::string &sub_part) {
7218 ut_ad(is_partition(partition));
7219
7220 /* Check if sub-partition exists. */
7221 size_t sub_pos = std::string::npos;
7222 bool is_sub = check_partition(partition, true, sub_pos);
7223
7224 /* Assign partition name. */
7225 size_t part_begin = PART_SEPARATOR_LEN;
7226 size_t part_len = std::string::npos;
7227
7228 if (is_sub) {
7229 ut_ad(sub_pos > part_begin);
7230 part_len = sub_pos - part_begin;
7231 }
7232
7233 part.assign(partition.substr(part_begin, part_len));
7234 if (convert) {
7235 file_to_table(part, false);
7236 }
7237
7238 /* Assign sub-partition name. */
7239 sub_part.clear();
7240 if (!is_sub) {
7241 return;
7242 }
7243
7244 auto sub_begin = sub_pos + SUB_PART_SEPARATOR_LEN;
7245 auto sub_len = std::string::npos;
7246
7247 sub_part.assign(partition.substr(sub_begin, sub_len));
7248
7249 if (convert) {
7250 file_to_table(sub_part, false);
7251 }
7252 }
7253
build_table(const std::string & schema,const std::string & table,const std::string & partition,bool is_tmp,bool convert,std::string & dict_name)7254 void build_table(const std::string &schema, const std::string &table,
7255 const std::string &partition, bool is_tmp, bool convert,
7256 std::string &dict_name) {
7257 dict_name.clear();
7258 std::string conv_str;
7259
7260 /* Check and append schema name. */
7261 if (!schema.empty()) {
7262 conv_str.assign(schema);
7263 /* Convert schema name. */
7264 if (convert) {
7265 table_to_file(conv_str);
7266 }
7267 dict_name.append(conv_str);
7268 dict_name.append(SCHEMA_SEPARATOR);
7269 }
7270
7271 conv_str.assign(table);
7272 /* Convert table name. */
7273 if (convert) {
7274 table_to_file(conv_str);
7275 }
7276 dict_name.append(conv_str);
7277
7278 /* Check and assign partition string. Any conversion for partition
7279 and sub-partition is already done while building partition string. */
7280 if (!partition.empty()) {
7281 dict_name.append(partition);
7282 }
7283
7284 /* Check and append temporary extension. */
7285 if (is_tmp) {
7286 dict_name.append(TMP_POSTFIX);
7287 }
7288 }
7289
7290 /** Build partition string from partition and sub-partition name
7291 @param[in] part partition name
7292 @param[in] sub_part sub-partition name
7293 @param[in] conv callback to convert partition/sub-partition name
7294 @param[in] is_57 if 5.7 style partition name is needed
7295 @param[out] partition partition string for dictionary table name */
build_partition_low(const std::string part,const std::string sub_part,Convert_Func conv,bool is_57,std::string & partition)7296 static void build_partition_low(const std::string part,
7297 const std::string sub_part, Convert_Func conv,
7298 bool is_57, std::string &partition) {
7299 partition.clear();
7300 std::string conv_str;
7301
7302 if (part.empty()) {
7303 ut_ad(false);
7304 return;
7305 }
7306
7307 /* Get partition separator strings */
7308 std::string part_sep;
7309 std::string sub_part_sep;
7310
7311 get_partition_separators(is_57, part_sep, sub_part_sep);
7312
7313 /* Append separator and partition. */
7314 partition.append(part_sep);
7315
7316 conv_str.assign(part);
7317 if (conv) {
7318 conv(conv_str);
7319 }
7320 partition.append(conv_str);
7321
7322 if (sub_part.empty()) {
7323 return;
7324 }
7325
7326 /* Append separator and sub-partition. */
7327 partition.append(sub_part_sep);
7328
7329 conv_str.assign(sub_part);
7330 if (conv) {
7331 conv(conv_str);
7332 }
7333 partition.append(conv_str);
7334 }
7335
7336 /** Convert string to lower case.
7337 @param[in,out] name name to convert */
to_lower(std::string & name)7338 static void to_lower(std::string &name) {
7339 /* Skip empty string. */
7340 if (name.empty()) {
7341 return;
7342 }
7343 ut_ad(name.length() < FN_REFLEN);
7344 char conv_name[FN_REFLEN];
7345 auto len = name.copy(&conv_name[0], FN_REFLEN - 1);
7346 conv_name[len] = '\0';
7347
7348 innobase_casedn_str(&conv_name[0]);
7349 name.assign(&conv_name[0]);
7350 }
7351
7352 /** Get partition and sub-partition name from DD. We convert the names to
7353 lower case.
7354 @param[in] dd_part partition object from DD
7355 @param[in] lower_case convert to lower case name
7356 @param[out] part_name partition name
7357 @param[out] sub_name sub-partition name */
get_part_from_dd(const dd::Partition * dd_part,bool lower_case,std::string & part_name,std::string & sub_name)7358 static void get_part_from_dd(const dd::Partition *dd_part, bool lower_case,
7359 std::string &part_name, std::string &sub_name) {
7360 /* Assume sub-partition and get the parent partition. */
7361 auto sub_part = dd_part;
7362 auto part = sub_part->parent();
7363
7364 /* If parent is null then there is no sub-partition. */
7365 if (part == nullptr) {
7366 part = dd_part;
7367 sub_part = nullptr;
7368 }
7369
7370 ut_ad(part->name().length() < FN_REFLEN);
7371
7372 part_name.assign(part->name().c_str());
7373 /* Convert partition name to lower case. */
7374 if (lower_case) {
7375 to_lower(part_name);
7376 }
7377
7378 sub_name.clear();
7379 if (sub_part != nullptr) {
7380 ut_ad(sub_part->name().length() < FN_REFLEN);
7381
7382 sub_name.assign(sub_part->name().c_str());
7383 /* Convert sub-partition name to lower case. */
7384 if (lower_case) {
7385 to_lower(sub_name);
7386 }
7387 }
7388 }
7389
build_partition(const dd::Partition * dd_part,std::string & partition)7390 void build_partition(const dd::Partition *dd_part, std::string &partition) {
7391 std::string part_name;
7392 std::string sub_name;
7393
7394 /* Extract partition and sub-partition name from DD. */
7395 get_part_from_dd(dd_part, true, part_name, sub_name);
7396
7397 /* Build partition string after converting names. */
7398 build_partition_low(part_name, sub_name, table_to_file, false, partition);
7399 }
7400
build_57_partition(const dd::Partition * dd_part,std::string & partition)7401 void build_57_partition(const dd::Partition *dd_part, std::string &partition) {
7402 std::string part_name;
7403 std::string sub_name;
7404
7405 /* Extract partition and sub-partition name from DD. In 5.7, partition and
7406 sub-partition names are kept in same letter case as given by user. */
7407 bool lower_case = false;
7408
7409 /* On windows, 5.7 partition sub-partition names are in lower case always. */
7410 #ifdef _WIN32
7411 lower_case = true;
7412 #endif /* _WIN32 */
7413
7414 get_part_from_dd(dd_part, lower_case, part_name, sub_name);
7415
7416 /* Build partition string after converting names. */
7417 build_partition_low(part_name, sub_name, table_to_file, true, partition);
7418 }
7419
match_partition(const std::string & dict_name,const dd::Partition * dd_part)7420 bool match_partition(const std::string &dict_name,
7421 const dd::Partition *dd_part) {
7422 std::string dd_partition;
7423
7424 /* Extract partition and sub-partition name from DD. */
7425 build_partition(dd_part, dd_partition);
7426
7427 std::string schema;
7428 std::string table;
7429 bool is_tmp;
7430 std::string partition;
7431
7432 /* Extract schema, table and partition string without conversion. */
7433 get_table(dict_name, false, schema, table, partition, is_tmp);
7434
7435 #ifdef UNIV_DEBUG
7436 /* Innodb dictionary name should already be in lower case. */
7437 ut_ad(partition.length() < FN_REFLEN);
7438
7439 char partition_string[FN_REFLEN];
7440 auto part_len = partition.copy(&partition_string[0], FN_REFLEN - 1);
7441 partition_string[part_len] = '\0';
7442
7443 innobase_casedn_path(&partition_string[0]);
7444 std::string lower_case_str(&partition_string[0]);
7445
7446 ut_ad(partition.compare(lower_case_str) == 0);
7447 #endif /* UNIV_DEBUG */
7448
7449 /* Match the string from DD and innodb dictionary. */
7450 return (dd_partition.compare(partition) == 0);
7451 }
7452
7453 /** Get table and partition string in system cs from dictionary name.
7454 @param[in] dict_name table name in dictionary
7455 @param[out] schema schema name
7456 @param[out] table table name
7457 @param[out] partition partition string
7458 @param[out] is_tmp true, if temporary table created by DDL */
get_table_parts(const std::string & dict_name,std::string & schema,std::string & table,std::string & partition,bool & is_tmp)7459 static void get_table_parts(const std::string &dict_name, std::string &schema,
7460 std::string &table, std::string &partition,
7461 bool &is_tmp) {
7462 /* Extract schema, table and partition string converting to system cs. */
7463 get_table(dict_name, true, schema, table, partition, is_tmp);
7464
7465 if (!partition.empty()) {
7466 std::string part;
7467 std::string sub_part;
7468
7469 /* Extract partition details converting to system cs. */
7470 get_partition(partition, true, part, sub_part);
7471
7472 /* During upgrade from 5.7 it is possible to have upper case
7473 names from SYS tables. */
7474 if (srv_is_upgrade_mode) {
7475 to_lower(part);
7476 to_lower(sub_part);
7477 }
7478
7479
7480 /* Build partition string. No conversion required. */
7481 partition.clear();
7482 build_partition_low(part, sub_part, nullptr, false, partition);
7483 }
7484 }
7485
convert_to_space(std::string & dict_name)7486 void convert_to_space(std::string &dict_name) {
7487 std::string schema;
7488 std::string table;
7489 std::string partition;
7490 bool is_tmp = false;
7491
7492 /* Get all table parts converted to system cs. */
7493 get_table_parts(dict_name, schema, table, partition, is_tmp);
7494
7495 /* For lower case file systems, schema and table name are converted
7496 to lower case before generating tablespace name. Skip for general
7497 table space i.e. schema is empty. */
7498 if (lower_case_file_system && !schema.empty()) {
7499 ut_ad(lower_case_table_names != 0);
7500 to_lower(schema);
7501 to_lower(table);
7502 }
7503
7504 /* Build the space name. No conversion required. */
7505 dict_name.clear();
7506 build_table(schema, table, partition, is_tmp, false, dict_name);
7507
7508 ut_ad(dict_name.length() < dict_name::MAX_SPACE_NAME_LEN);
7509 }
7510
rebuild_space(const std::string & dict_name,std::string & space_name)7511 void rebuild_space(const std::string &dict_name, std::string &space_name) {
7512 std::string schema;
7513 std::string table;
7514 std::string partition;
7515 bool is_tmp = false;
7516
7517 /* Get all table parts converted to system cs. */
7518 get_table_parts(dict_name, schema, table, partition, is_tmp);
7519
7520 if (is_tmp) {
7521 partition.append(TMP_POSTFIX);
7522 }
7523
7524 auto part_len = partition.length();
7525 auto space_len = space_name.length();
7526
7527 ut_ad(space_len > part_len);
7528
7529 if (space_len > part_len) {
7530 auto part_pos = space_len - part_len;
7531
7532 std::string space_part = space_name.substr(part_pos);
7533 if (space_part.compare(partition) == 0) {
7534 return;
7535 }
7536 space_name.replace(part_pos, std::string::npos, partition);
7537 }
7538 }
7539
rebuild(std::string & dict_name)7540 void rebuild(std::string &dict_name) {
7541 std::string schema;
7542 std::string table;
7543 std::string partition;
7544 bool is_tmp = false;
7545
7546 /* Conversion is needed only for partitioned table. */
7547 if (!is_partition(dict_name)) {
7548 return;
7549 }
7550
7551 /* Extract schema, table and partition string without conversion. */
7552 get_table(dict_name, false, schema, table, partition, is_tmp);
7553
7554 if (!partition.empty()) {
7555 std::string part;
7556 std::string sub_part;
7557
7558 /* Extract partition details converting to system cs. */
7559 get_partition(partition, true, part, sub_part);
7560
7561 /* Convert partition names to lower case. */
7562 to_lower(part);
7563 to_lower(sub_part);
7564
7565 /* Build partition string converting to file cs. */
7566 partition.clear();
7567 build_partition_low(part, sub_part, table_to_file, false, partition);
7568 }
7569
7570 /* Re-build the table name. No cs conversion required. */
7571 dict_name.clear();
7572 build_table(schema, table, partition, is_tmp, false, dict_name);
7573 }
7574
7575 } // namespace dict_name
7576 #endif /* !UNIV_HOTBACKUP */
7577