1/***************************************************************************** 2 3Copyright (c) 2016, 2020, Oracle and/or its affiliates. All Rights Reserved. 4 5This program is free software; you can redistribute it and/or modify it under 6the terms of the GNU General Public License, version 2.0, as published by the 7Free Software Foundation. 8 9This program is also distributed with certain software (including but not 10limited to OpenSSL) that is licensed under separate terms, as designated in a 11particular file or component or in included license documentation. The authors 12of MySQL hereby grant you an additional permission to link the program and 13your derivative works with the separately licensed software that they have 14included with MySQL. 15 16This program is distributed in the hope that it will be useful, but WITHOUT 17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25*****************************************************************************/ 26 27/** @file include/dict0dd.ic 28 Data dictionary access 29 30 Created 10/2016 Jimmy Yamg 31 *******************************************************/ 32 33#include "sql_table.h" 34#ifndef UNIV_HOTBACKUP 35#include "dict0crea.h" 36#include "dict0priv.h" 37#include "field.h" 38#include "sql/table.h" 39#include "sql_base.h" 40 41/** Get the explicit dd::Tablespace::id of a partition. 42@param[in] partition partition or subpartition 43@return the explicit dd::Tablespace::id 44@retval dd::INVALID_OBJECT_ID if there is no explicit tablespace */ 45inline dd::Object_id dd_get_space_id(const dd::Partition &partition) { 46 ut_ad(dd_part_is_stored(&partition)); 47 ut_ad(dd_table_is_partitioned(partition.table())); 48 ut_ad((partition.table().subpartition_type() == dd::Table::ST_NONE) == 49 (partition.parent() == nullptr)); 50 51 dd::Object_id id = partition.tablespace_id(); 52 if (id == dd::INVALID_OBJECT_ID) { 53 if (const dd::Partition *parent = partition.parent()) { 54 /* If there is no explicit TABLESPACE for the 55 subpartition, fall back to the TABLESPACE 56 of the partition. */ 57 id = parent->tablespace_id(); 58 } 59 } 60 if (id == dd::INVALID_OBJECT_ID) { 61 /* If there is no explicit TABLESPACE for the partition, 62 fall back to the TABLESPACE of the table. */ 63 id = partition.table().tablespace_id(); 64 } 65 return (id); 66} 67 68/** Look up a column in a table using the system_charset_info collation. 69@param[in] dd_table data dictionary table 70@param[in] name column name 71@return the column 72@retval nullptr if not found */ 73inline const dd::Column *dd_find_column(const dd::Table *dd_table, 74 const char *name) { 75 for (const dd::Column *c : dd_table->columns()) { 76 if (!my_strcasecmp(system_charset_info, c->name().c_str(), name)) { 77 return (c); 78 } 79 } 80 return (nullptr); 81} 82 83/** Add a hidden column when creating a table. 84@param[in,out] dd_table table containing user columns and indexes 85@param[in] name hidden column name 86@param[in] length length of the column, in bytes 87@param[in] type column type 88@return the added column, or NULL if there already was a column by that name */ 89inline dd::Column *dd_add_hidden_column(dd::Table *dd_table, const char *name, 90 uint length, 91 dd::enum_column_types type) { 92 if (const dd::Column *c = dd_find_column(dd_table, name)) { 93 my_error(ER_WRONG_COLUMN_NAME, MYF(0), c->name().c_str()); 94 return (nullptr); 95 } 96 97 dd::Column *col = dd_table->add_column(); 98 col->set_hidden(dd::Column::enum_hidden_type::HT_HIDDEN_SE); 99 col->set_name(name); 100 col->set_type(type); 101 col->set_nullable(false); 102 col->set_char_length(length); 103 col->set_collation_id(my_charset_bin.number); 104 105 return (col); 106} 107 108/** Add a hidden index element at the end. 109@param[in,out] index created index metadata 110@param[in] column column of the index */ 111inline void dd_add_hidden_element(dd::Index *index, const dd::Column *column) { 112 dd::Index_element *e = index->add_element(const_cast<dd::Column *>(column)); 113 e->set_hidden(true); 114 e->set_order(dd::Index_element::ORDER_ASC); 115} 116 117/** Initialize a hidden unique B-tree index. 118@param[in,out] index created index metadata 119@param[in] name name of the index 120@param[in] column column of the index 121@return the initialized index */ 122inline dd::Index *dd_set_hidden_unique_index(dd::Index *index, const char *name, 123 const dd::Column *column) { 124 index->set_name(name); 125 index->set_hidden(true); 126 index->set_algorithm(dd::Index::IA_BTREE); 127 index->set_type(dd::Index::IT_UNIQUE); 128 index->set_engine(innobase_hton_name); 129 dd_add_hidden_element(index, column); 130 return (index); 131} 132 133/** Returns a cached table object based on table id. 134@param[in] table_id table id 135@param[in] dict_locked TRUE=data dictionary locked 136@return table, NULL if does not exist */ 137inline dict_table_t *dd_table_open_on_id_in_mem(table_id_t table_id, 138 bool dict_locked) { 139 dict_table_t *table; 140 141 if (!dict_locked) { 142 mutex_enter(&dict_sys->mutex); 143 } 144 145 ut_ad(mutex_own(&dict_sys->mutex)); 146 147 /* Look for the table ID in the hash table */ 148 ulint fold = ut_fold_ull(table_id); 149 150 HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t *, table, 151 ut_ad(table->cached), table->id == table_id); 152 153 ut_ad(table == nullptr || table->cached); 154 155 if (table != nullptr) { 156 if (table->can_be_evicted) { 157 dict_move_to_mru(table); 158 } 159 160 table->acquire(); 161 } 162 163 if (!dict_locked) { 164 mutex_exit(&dict_sys->mutex); 165 } 166 167 return (table); 168} 169 170/** Returns a cached table object based on table name. 171@param[in] name table name 172@param[in] dict_locked TRUE=data dictionary locked 173@return table, NULL if does not exist */ 174inline dict_table_t *dd_table_open_on_name_in_mem(const char *name, 175 ibool dict_locked) { 176 dict_table_t *table = nullptr; 177 178 if (!dict_locked) { 179 mutex_enter(&dict_sys->mutex); 180 } 181 182 table = dict_table_check_if_in_cache_low(name); 183 184 ut_ad(!table || table->cached); 185 186 if (table != nullptr) { 187 if (table->can_be_evicted) { 188 dict_move_to_mru(table); 189 } 190 191 table->acquire(); 192 } 193 194 if (!dict_locked) { 195 mutex_exit(&dict_sys->mutex); 196 } 197 198 return (table); 199} 200 201/** Check whether there exist a column named as "FTS_DOC_ID", which is 202reserved for InnoDB FTS Doc ID 203@param[in] thd MySQL thread handle 204@param[in] form information on table 205 columns and indexes 206@param[out] doc_id_col Doc ID column number if 207 there exist a FTS_DOC_ID column, 208 ULINT_UNDEFINED if column is of the 209 wrong type/name/size 210@return true if there exist a "FTS_DOC_ID" column */ 211inline bool create_table_check_doc_id_col(THD *thd, const TABLE *form, 212 ulint *doc_id_col) { 213 for (ulint i = 0; i < form->s->fields; i++) { 214 const Field *field; 215 ulint col_type; 216 ulint col_len; 217 ulint unsigned_type; 218 219 field = form->field[i]; 220 if (!field->stored_in_db) continue; 221 222 col_type = get_innobase_type_from_mysql_type(&unsigned_type, field); 223 224 col_len = field->pack_length(); 225 226 if (innobase_strcasecmp(field->field_name, FTS_DOC_ID_COL_NAME) == 0) { 227 /* Note the name is case sensitive due to 228 our internal query parser */ 229 if (col_type == DATA_INT && !field->is_nullable() && 230 col_len == sizeof(doc_id_t) && 231 (strcmp(field->field_name, FTS_DOC_ID_COL_NAME) == 0)) { 232 *doc_id_col = i; 233 } else { 234 push_warning_printf(thd, Sql_condition::SL_WARNING, 235 ER_ILLEGAL_HA_CREATE_OPTION, 236 "InnoDB: FTS_DOC_ID column must be" 237 " of BIGINT NOT NULL type, and named" 238 " in all capitalized characters"); 239 my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name); 240 *doc_id_col = ULINT_UNDEFINED; 241 } 242 243 return (true); 244 } 245 } 246 247 return (false); 248} 249 250/** Return a display name for the row format 251@param[in] row_format Row Format 252@return row format name */ 253inline const char *get_row_format_name(enum row_type row_format) { 254 switch (row_format) { 255 case ROW_TYPE_COMPACT: 256 return ("ROW_FORMAT=COMPACT"); 257 case ROW_TYPE_COMPRESSED: 258 return ("ROW_FORMAT=COMPRESSED"); 259 case ROW_TYPE_DYNAMIC: 260 return ("ROW_FORMAT=DYNAMIC"); 261 case ROW_TYPE_REDUNDANT: 262 return ("ROW_FORMAT=REDUNDANT"); 263 case ROW_TYPE_DEFAULT: 264 return ("ROW_FORMAT=DEFAULT"); 265 case ROW_TYPE_FIXED: 266 return ("ROW_FORMAT=FIXED"); 267 case ROW_TYPE_PAGED: 268 return ("ROW_FORMAT=PAGED"); 269 case ROW_TYPE_NOT_USED: 270 break; 271 } 272 return ("ROW_FORMAT"); 273} 274 275/** Acquire a shared metadata lock. 276@param[in,out] thd current thread 277@param[out] mdl metadata lock 278@param[in] db schema name 279@param[in] table table name 280@retval false if acquired 281@retval true if failed (my_error() will have been called) */ 282UNIV_INLINE MY_ATTRIBUTE((warn_unused_result)) bool dd_mdl_acquire( 283 THD *thd, MDL_ticket **mdl, const char *db, const char *table) { 284 bool ret = false; 285 char table_name[MAX_TABLE_NAME_LEN + 1]; 286 const char *namep = table; 287 288 if (innobase_get_lower_case_table_names() == 2) { 289 ulint len = strlen(table); 290 ut_memcpy(table_name, table, len); 291 table_name[len] = '\0'; 292 innobase_casedn_str(table_name); 293 namep = table_name; 294 } else { 295#ifndef _WIN32 296 if (innobase_get_lower_case_table_names() == 1) { 297 ulint len = strlen(table); 298 ut_memcpy(table_name, table, len); 299 table_name[len] = '\0'; 300 innobase_casedn_str(table_name); 301 namep = table_name; 302 } 303#endif /* !_WIN32 */ 304 } 305 306 ret = dd::acquire_shared_table_mdl(thd, db, namep, false, mdl); 307 308 return (ret); 309} 310 311/** Get the version attribute. 312@param[in] dd_table dd::Table 313@return table dynamic metadata version if exists, otherwise 0 */ 314inline uint64_t dd_get_version(const dd::Table *dd_table) { 315 uint64_t version = 0; 316 const dd::Properties &p = dd_table->se_private_data(); 317 318 if (!p.exists(dd_table_key_strings[DD_TABLE_VERSION]) || 319 p.get(dd_table_key_strings[DD_TABLE_VERSION], 320 reinterpret_cast<uint64 *>(&version))) { 321 return (0); 322 } 323 324 return (version); 325} 326#endif /* !UNIV_HOTBACKUP */ 327