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