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