763 lines
24 KiB
C++
763 lines
24 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including but not
|
|
limited to OpenSSL) that is licensed under separate terms, as designated in a
|
|
particular file or component or in included license documentation. The authors
|
|
of MySQL hereby grant you an additional permission to link the program and
|
|
your derivative works with the separately licensed software that they have
|
|
included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/** @file dict/dict0crea.cc
|
|
Database object creation
|
|
|
|
Created 1/8/1996 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "dict0crea.h"
|
|
#include "btr0btr.h"
|
|
#include "btr0pcur.h"
|
|
#include "dict0boot.h"
|
|
#include "dict0dd.h"
|
|
#include "dict0dict.h"
|
|
#include "dict0priv.h"
|
|
#include "dict0stats.h"
|
|
#include "fsp0space.h"
|
|
#include "fsp0sysspace.h"
|
|
#include "fts0priv.h"
|
|
#include "ha_prototypes.h"
|
|
#include "mach0data.h"
|
|
|
|
#include "my_dbug.h"
|
|
|
|
#include "dict0upgrade.h"
|
|
#include "page0page.h"
|
|
#include "pars0pars.h"
|
|
#include "que0que.h"
|
|
#include "row0ins.h"
|
|
#include "row0mysql.h"
|
|
#include "srv0start.h"
|
|
#include "trx0roll.h"
|
|
#include "usr0sess.h"
|
|
#include "ut0vec.h"
|
|
|
|
#include "lizard0dict.h"
|
|
|
|
/** Build a table definition without updating SYSTEM TABLES
|
|
@param[in,out] table dict table object
|
|
@param[in,out] trx transaction instance
|
|
@return DB_SUCCESS or error code */
|
|
dberr_t dict_build_table_def(dict_table_t *table, trx_t *trx) {
|
|
char db_buf[NAME_LEN + 1];
|
|
char tbl_buf[NAME_LEN + 1];
|
|
|
|
dd_parse_tbl_name(table->name.m_name, db_buf, tbl_buf, nullptr, nullptr,
|
|
nullptr);
|
|
|
|
bool is_dd_table = dd::get_dictionary()->is_dd_table_name(db_buf, tbl_buf);
|
|
|
|
/** In-memory counter used for assigning table_id
|
|
of data dictionary table. This counter is only used
|
|
during bootstrap or upgrade */
|
|
static uint32_t dd_table_id = 1;
|
|
|
|
if (is_dd_table) {
|
|
table->id = dd_table_id++;
|
|
table->is_dd_table = true;
|
|
|
|
ut_ad(strcmp(tbl_buf, innodb_dd_table[table->id - 1].name) == 0);
|
|
|
|
} else {
|
|
dict_table_assign_new_id(table, trx);
|
|
}
|
|
|
|
dberr_t err = dict_build_tablespace_for_table(table, trx);
|
|
|
|
return (err);
|
|
}
|
|
|
|
/** Build a tablespace to store various objects.
|
|
@param[in,out] trx DD transaction
|
|
@param[in,out] tablespace Tablespace object describing what to build.
|
|
@return DB_SUCCESS or error code. */
|
|
dberr_t dict_build_tablespace(trx_t *trx, Tablespace *tablespace) {
|
|
dberr_t err = DB_SUCCESS;
|
|
mtr_t mtr;
|
|
space_id_t space = 0;
|
|
ut_d(static uint32_t crash_injection_after_create_counter = 1;);
|
|
|
|
ut_ad(mutex_own(&dict_sys->mutex));
|
|
ut_ad(tablespace);
|
|
|
|
DBUG_EXECUTE_IF("out_of_tablespace_disk", return (DB_OUT_OF_FILE_SPACE););
|
|
/* Get a new space id. */
|
|
dict_hdr_get_new_id(NULL, NULL, &space, NULL, false);
|
|
if (space == SPACE_UNKNOWN) {
|
|
return (DB_ERROR);
|
|
}
|
|
tablespace->set_space_id(space);
|
|
|
|
Datafile *datafile = tablespace->first_datafile();
|
|
|
|
/* If file already exists we cannot write delete space to ddl log. */
|
|
os_file_type_t type;
|
|
bool exists;
|
|
if (os_file_status(datafile->filepath(), &exists, &type)) {
|
|
if (exists) {
|
|
return DB_TABLESPACE_EXISTS;
|
|
}
|
|
} else {
|
|
return DB_IO_ERROR;
|
|
}
|
|
|
|
err = log_ddl->write_delete_space_log(trx, NULL, space, datafile->filepath(),
|
|
false, true);
|
|
if (err != DB_SUCCESS) {
|
|
return err;
|
|
}
|
|
|
|
/* We create a new generic empty tablespace.
|
|
We initially let it be 4 pages:
|
|
- page 0 is the fsp header and an extent descriptor page,
|
|
- page 1 is an ibuf bitmap page,
|
|
- page 2 is the first inode page,
|
|
- page 3 will contain the root of the clustered index of the
|
|
first table we create here. */
|
|
|
|
err = fil_ibd_create(space, tablespace->name(), datafile->filepath(),
|
|
tablespace->flags(), FIL_IBD_FILE_INITIAL_SIZE);
|
|
|
|
DBUG_INJECT_CRASH("ddl_crash_after_create_tablespace",
|
|
crash_injection_after_create_counter++);
|
|
|
|
if (err != DB_SUCCESS) {
|
|
return (err);
|
|
}
|
|
|
|
DBUG_EXECUTE_IF("innodb_fail_to_update_tablespace_dict",
|
|
return (DB_INTERRUPTED););
|
|
|
|
mtr_start(&mtr);
|
|
|
|
/* Once we allow temporary general tablespaces, we must do this;
|
|
mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); */
|
|
ut_a(!FSP_FLAGS_GET_TEMPORARY(tablespace->flags()));
|
|
|
|
bool ret = fsp_header_init(space, FIL_IBD_FILE_INITIAL_SIZE, &mtr, false);
|
|
mtr_commit(&mtr);
|
|
|
|
DBUG_EXECUTE_IF("fil_ibd_create_log",
|
|
log_write_up_to(*log_sys, mtr.commit_lsn(), true);
|
|
DBUG_SUICIDE(););
|
|
|
|
if (!ret) {
|
|
return (DB_ERROR);
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
/** Builds a tablespace to contain a table, using file-per-table=1.
|
|
@param[in,out] table Table to build in its own tablespace.
|
|
@param[in,out] trx Transaction
|
|
@return DB_SUCCESS or error code */
|
|
dberr_t dict_build_tablespace_for_table(dict_table_t *table, trx_t *trx) {
|
|
dberr_t err = DB_SUCCESS;
|
|
mtr_t mtr;
|
|
space_id_t space = 0;
|
|
bool needs_file_per_table;
|
|
char *filepath;
|
|
ut_d(static uint32_t crash_injection_after_create_counter = 1;);
|
|
|
|
ut_ad(!mutex_own(&dict_sys->mutex));
|
|
|
|
needs_file_per_table =
|
|
DICT_TF2_FLAG_IS_SET(table, DICT_TF2_USE_FILE_PER_TABLE);
|
|
|
|
if (needs_file_per_table) {
|
|
/* Temporary table would always reside in the same
|
|
shared temp tablespace. */
|
|
ut_ad(!table->is_temporary());
|
|
/* This table will need a new tablespace. */
|
|
|
|
ut_ad(DICT_TF_GET_ZIP_SSIZE(table->flags) == 0 ||
|
|
dict_table_has_atomic_blobs(table));
|
|
|
|
/* Get a new tablespace ID */
|
|
dict_hdr_get_new_id(NULL, NULL, &space, table, false);
|
|
|
|
DBUG_EXECUTE_IF("ib_create_table_fail_out_of_space_ids",
|
|
space = SPACE_UNKNOWN;);
|
|
|
|
if (space == SPACE_UNKNOWN) {
|
|
return (DB_ERROR);
|
|
}
|
|
table->space = space;
|
|
|
|
/* Determine the tablespace flags. */
|
|
uint32_t fsp_flags = dict_tf_to_fsp_flags(table->flags);
|
|
|
|
/* For file-per-table tablespace, set encryption flag */
|
|
if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE)) {
|
|
fsp_flags_set_encryption(fsp_flags);
|
|
}
|
|
|
|
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
|
|
std::string path;
|
|
|
|
path = dict_table_get_datadir(table);
|
|
|
|
filepath = Fil_path::make(path, table->name.m_name, IBD, true);
|
|
} else {
|
|
filepath = Fil_path::make_ibd_from_table_name(table->name.m_name);
|
|
}
|
|
|
|
/* If file already exists we cannot write delete space to ddl log. */
|
|
os_file_type_t type;
|
|
bool exists;
|
|
if (os_file_status(filepath, &exists, &type)) {
|
|
if (exists) {
|
|
ut_free(filepath);
|
|
return DB_TABLESPACE_EXISTS;
|
|
}
|
|
} else {
|
|
ut_free(filepath);
|
|
return DB_IO_ERROR;
|
|
}
|
|
|
|
err = log_ddl->write_delete_space_log(trx, table, space, filepath, false,
|
|
false);
|
|
if (err != DB_SUCCESS) {
|
|
ut_free(filepath);
|
|
return err;
|
|
}
|
|
|
|
/* We create a new single-table tablespace for the table.
|
|
We initially let it be 4 pages:
|
|
- page 0 is the fsp header and an extent descriptor page,
|
|
- page 1 is an ibuf bitmap page,
|
|
- page 2 is the first inode page,
|
|
- page 3 will contain the root of the clustered index of
|
|
the table we create here. */
|
|
|
|
std::string tablespace_name;
|
|
|
|
dd_filename_to_spacename(table->name.m_name, &tablespace_name);
|
|
|
|
err = fil_ibd_create(space, tablespace_name.c_str(), filepath, fsp_flags,
|
|
FIL_IBD_FILE_INITIAL_SIZE);
|
|
|
|
ut_free(filepath);
|
|
|
|
DBUG_INJECT_CRASH("ddl_crash_after_create_tablespace",
|
|
crash_injection_after_create_counter++);
|
|
|
|
if (err != DB_SUCCESS) {
|
|
return (err);
|
|
}
|
|
|
|
mtr_start(&mtr);
|
|
|
|
bool ret =
|
|
fsp_header_init(table->space, FIL_IBD_FILE_INITIAL_SIZE, &mtr, false);
|
|
mtr_commit(&mtr);
|
|
|
|
DBUG_EXECUTE_IF("fil_ibd_create_log",
|
|
log_write_up_to(*log_sys, mtr.commit_lsn(), true);
|
|
DBUG_SUICIDE(););
|
|
|
|
if (!ret) {
|
|
return (DB_ERROR);
|
|
}
|
|
|
|
err = btr_sdi_create_index(table->space, false);
|
|
return (err);
|
|
|
|
} else {
|
|
/* We do not need to build a tablespace for this table. It
|
|
is already built. Just find the correct tablespace ID. */
|
|
|
|
if (DICT_TF_HAS_SHARED_SPACE(table->flags)) {
|
|
ut_ad(table->tablespace != NULL);
|
|
|
|
ut_ad(table->space == fil_space_get_id_by_name(table->tablespace()));
|
|
} else if (table->is_temporary()) {
|
|
/* Use the shared temporary tablespace.
|
|
Note: The temp tablespace supports all non-Compressed
|
|
row formats whereas the system tablespace only
|
|
supports Redundant and Compact */
|
|
ut_ad(dict_tf_get_rec_format(table->flags) != REC_FORMAT_COMPRESSED);
|
|
|
|
innodb_session_t *innodb_session = thd_to_innodb_session(trx->mysql_thd);
|
|
ibt::Tablespace *tblsp = nullptr;
|
|
|
|
bool is_slave_thd = thd_is_replication_slave_thread(trx->mysql_thd);
|
|
if (is_slave_thd) {
|
|
tblsp = ibt::get_rpl_slave_tblsp();
|
|
} else if (table->is_intrinsic()) {
|
|
tblsp = innodb_session->get_instrinsic_temp_tblsp();
|
|
} else {
|
|
tblsp = innodb_session->get_usr_temp_tblsp();
|
|
}
|
|
|
|
/* Session temporary tablespace couldn't be allocated. This means,
|
|
we have run out of disk space */
|
|
if (tblsp == nullptr) {
|
|
return (DB_NO_SESSION_TEMP);
|
|
}
|
|
|
|
table->space = tblsp->space_id();
|
|
|
|
} else {
|
|
/* Create in the system tablespace. */
|
|
ut_ad(table->space == TRX_SYS_SPACE);
|
|
}
|
|
|
|
DBUG_EXECUTE_IF("ib_ddl_crash_during_tablespace_alloc", DBUG_SUICIDE(););
|
|
}
|
|
|
|
return (DB_SUCCESS);
|
|
}
|
|
|
|
/** Builds an index definition
|
|
@return DB_SUCCESS or error code */
|
|
void dict_build_index_def(const dict_table_t *table, /*!< in: table */
|
|
dict_index_t *index, /*!< in/out: index */
|
|
trx_t *trx) /*!< in/out: InnoDB transaction handle */
|
|
{
|
|
ut_ad(!mutex_own(&dict_sys->mutex));
|
|
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0) || index->is_clustered());
|
|
|
|
if (!table->is_intrinsic()) {
|
|
if (srv_is_upgrade_mode) {
|
|
index->id = dd_upgrade_indexes_num++;
|
|
ut_ad(index->id <= dd_get_total_indexes_num());
|
|
} else {
|
|
dict_hdr_get_new_id(NULL, &index->id, NULL, table, false);
|
|
}
|
|
|
|
} else {
|
|
/* Index are re-loaded in process of creation using id.
|
|
If same-id is used for all indexes only first index will always
|
|
be retrieved when expected is iterative return of all indexes*/
|
|
if (UT_LIST_GET_LEN(table->indexes) > 0) {
|
|
index->id = UT_LIST_GET_LAST(table->indexes)->id + 1;
|
|
} else {
|
|
index->id = 1;
|
|
}
|
|
}
|
|
|
|
/* Inherit the space id from the table; we store all indexes of a
|
|
table in the same tablespace */
|
|
|
|
index->space = table->space;
|
|
|
|
/* Note that the index was created by this transaction. */
|
|
index->trx_id = trx->id;
|
|
}
|
|
|
|
/** Creates an index tree for the index if it is not a member of a cluster.
|
|
@param[in,out] index InnoDB index object
|
|
@param[in,out] trx transaction
|
|
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
|
|
dberr_t dict_create_index_tree_in_mem(dict_index_t *index, trx_t *trx) {
|
|
mtr_t mtr;
|
|
ulint page_no = FIL_NULL;
|
|
|
|
ut_ad(!mutex_own(&dict_sys->mutex));
|
|
|
|
DBUG_EXECUTE_IF("ib_dict_create_index_tree_fail", return (DB_OUT_OF_MEMORY););
|
|
|
|
if (index->type == DICT_FTS) {
|
|
/* FTS index does not need an index tree */
|
|
return (DB_SUCCESS);
|
|
}
|
|
|
|
const bool missing =
|
|
index->table->ibd_file_missing || dict_table_is_discarded(index->table);
|
|
|
|
if (missing) {
|
|
index->page = FIL_NULL;
|
|
index->trx_id = trx->id;
|
|
|
|
return (DB_SUCCESS);
|
|
}
|
|
|
|
mtr_start(&mtr);
|
|
|
|
if (index->table->is_temporary()) {
|
|
mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO);
|
|
}
|
|
|
|
dberr_t err = DB_SUCCESS;
|
|
|
|
/* Assign txn undo in advance during DDL operation */
|
|
err = lizard::dd_index_init_txn_desc(index, trx);
|
|
|
|
if (err == DB_SUCCESS) {
|
|
page_no =
|
|
btr_create(index->type, index->space,
|
|
dict_table_page_size(index->table), index->id, index, &mtr);
|
|
|
|
if (page_no == FIL_NULL) err = DB_OUT_OF_FILE_SPACE;
|
|
}
|
|
|
|
index->page = page_no;
|
|
index->trx_id = trx->id;
|
|
|
|
mtr_commit(&mtr);
|
|
if (err == DB_SUCCESS) {
|
|
/* FIXME: Now writing ddl log after the index has been created,
|
|
so if server crashes before the redo log gets persisted,
|
|
there is no way to find the resources(two segments, etc.)
|
|
allocated to this index. Since this is a rare case, living
|
|
with it is acceptable */
|
|
/* FIXME: if it's part of CREATE TABLE, and file_per_table is
|
|
true, skip ddl log, because during rollback, the whole
|
|
tablespace would be dropped */
|
|
|
|
/* During upgrade, etc., the log_ddl may haven't been
|
|
initialized and we don't need to write DDL logs too.
|
|
This can only happen for CREATE TABLE. */
|
|
if (log_ddl != nullptr) {
|
|
err = log_ddl->write_free_tree_log(trx, index, false);
|
|
}
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
/** Drop an index tree belonging to a temporary table.
|
|
@param[in] index index in a temporary table
|
|
@param[in] root_page_no index root page number */
|
|
void dict_drop_temporary_table_index(const dict_index_t *index,
|
|
page_no_t root_page_no) {
|
|
ut_ad(mutex_own(&dict_sys->mutex) || index->table->is_intrinsic());
|
|
ut_ad(index->table->is_temporary());
|
|
ut_ad(index->page == FIL_NULL);
|
|
|
|
space_id_t space = index->space;
|
|
bool found;
|
|
const page_size_t page_size(fil_space_get_page_size(space, &found));
|
|
|
|
/* If tree has already been freed or it is a single table
|
|
tablespace and the .ibd file is missing do nothing,
|
|
else free the all the pages */
|
|
if (root_page_no != FIL_NULL && found) {
|
|
btr_free(page_id_t(space, root_page_no), page_size);
|
|
}
|
|
}
|
|
|
|
/** Check whether a column is in an index by the column name
|
|
@param[in] col_name column name for the column to be checked
|
|
@param[in] index the index to be searched
|
|
@return true if this column is in the index, otherwise, false */
|
|
static bool dict_index_has_col_by_name(const char *col_name,
|
|
const dict_index_t *index) {
|
|
for (ulint i = 0; i < index->n_fields; i++) {
|
|
dict_field_t *field = index->get_field(i);
|
|
|
|
if (strcmp(field->name, col_name) == 0) {
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/** Check whether the foreign constraint could be on a column that is
|
|
part of a virtual index (index contains virtual column) in the table
|
|
@param[in] fk_col_name FK column name to be checked
|
|
@param[in] table the table
|
|
@return true if this column is indexed with other virtual columns */
|
|
bool dict_foreign_has_col_in_v_index(const char *fk_col_name,
|
|
const dict_table_t *table) {
|
|
/* virtual column can't be Primary Key, so start with secondary index */
|
|
for (const dict_index_t *index = table->first_index()->next(); index;
|
|
index = index->next()) {
|
|
if (dict_index_has_virtual(index)) {
|
|
if (dict_index_has_col_by_name(fk_col_name, index)) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/** Check whether the foreign constraint could be on a column that is
|
|
a base column of some indexed virtual columns.
|
|
@param[in] col_name column name for the column to be checked
|
|
@param[in] table the table
|
|
@return true if this column is a base column, otherwise, false */
|
|
bool dict_foreign_has_col_as_base_col(const char *col_name,
|
|
const dict_table_t *table) {
|
|
/* Loop through each virtual column and check if its base column has
|
|
the same name as the column name being checked */
|
|
for (ulint i = 0; i < table->n_v_cols; i++) {
|
|
dict_v_col_t *v_col = dict_table_get_nth_v_col(table, i);
|
|
|
|
/* Only check if the virtual column is indexed */
|
|
if (!v_col->m_col.ord_part) {
|
|
continue;
|
|
}
|
|
|
|
for (ulint j = 0; j < v_col->num_base; j++) {
|
|
if (strcmp(col_name, table->get_col_name(v_col->base_col[j]->ind)) == 0) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/** Check if a foreign constraint is on the given column name.
|
|
@param[in] col_name column name to be searched for fk constraint
|
|
@param[in] table table to which foreign key constraint belongs
|
|
@return true if fk constraint is present on the table, false otherwise. */
|
|
static bool dict_foreign_base_for_stored(const char *col_name,
|
|
const dict_table_t *table) {
|
|
/* Loop through each stored column and check if its base column has
|
|
the same name as the column name being checked */
|
|
dict_s_col_list::const_iterator it;
|
|
for (it = table->s_cols->begin(); it != table->s_cols->end(); ++it) {
|
|
dict_s_col_t s_col = *it;
|
|
|
|
for (ulint j = 0; j < s_col.num_base; j++) {
|
|
/** If the stored column can refer to virtual column
|
|
or stored column then it can points to NULL. */
|
|
|
|
if (s_col.base_col[j] == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(col_name, table->get_col_name(s_col.base_col[j]->ind)) == 0) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/** Check if a foreign constraint is on columns served as base columns
|
|
of any stored column. This is to prevent creating SET NULL or CASCADE
|
|
constraint on such columns
|
|
@param[in] local_fk_set set of foreign key objects, to be added to
|
|
the dictionary tables
|
|
@param[in] table table to which the foreign key objects in
|
|
local_fk_set belong to
|
|
@return true if yes, otherwise, false */
|
|
bool dict_foreigns_has_s_base_col(const dict_foreign_set &local_fk_set,
|
|
const dict_table_t *table) {
|
|
dict_foreign_t *foreign;
|
|
|
|
if (table->s_cols == NULL) {
|
|
return (false);
|
|
}
|
|
|
|
for (dict_foreign_set::const_iterator it = local_fk_set.begin();
|
|
it != local_fk_set.end(); ++it) {
|
|
foreign = *it;
|
|
ulint type = foreign->type;
|
|
|
|
type &=
|
|
~(DICT_FOREIGN_ON_DELETE_NO_ACTION | DICT_FOREIGN_ON_UPDATE_NO_ACTION);
|
|
|
|
if (type == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (ulint i = 0; i < foreign->n_fields; i++) {
|
|
/* Check if the constraint is on a column that
|
|
is a base column of any stored column */
|
|
if (dict_foreign_base_for_stored(foreign->foreign_col_names[i], table)) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/** Check if a column is in foreign constraint with CASCADE properties or
|
|
SET NULL
|
|
@param[in] table table
|
|
@param[in] col_name name for the column to be checked
|
|
@return true if the column is in foreign constraint, otherwise, false */
|
|
bool dict_foreigns_has_this_col(const dict_table_t *table,
|
|
const char *col_name) {
|
|
dict_foreign_t *foreign;
|
|
const dict_foreign_set *local_fk_set = &table->foreign_set;
|
|
|
|
for (dict_foreign_set::const_iterator it = local_fk_set->begin();
|
|
it != local_fk_set->end(); ++it) {
|
|
foreign = *it;
|
|
ut_ad(foreign->id != NULL);
|
|
ulint type = foreign->type;
|
|
|
|
type &=
|
|
~(DICT_FOREIGN_ON_DELETE_NO_ACTION | DICT_FOREIGN_ON_UPDATE_NO_ACTION);
|
|
|
|
if (type == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (ulint i = 0; i < foreign->n_fields; i++) {
|
|
if (strcmp(foreign->foreign_col_names[i], col_name) == 0) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/** Assign a new table ID and put it into the table cache and the transaction.
|
|
@param[in,out] table Table that needs an ID
|
|
@param[in,out] trx Transaction */
|
|
void dict_table_assign_new_id(dict_table_t *table, trx_t *trx) {
|
|
if (table->is_intrinsic()) {
|
|
/* There is no significance of this table->id (if table is
|
|
intrinsic) so assign it default instead of something meaningful
|
|
to avoid confusion.*/
|
|
table->id = ULINT_UNDEFINED;
|
|
} else {
|
|
dict_hdr_get_new_id(&table->id, NULL, NULL, table, false);
|
|
}
|
|
}
|
|
|
|
/** Create in-memory tablespace dictionary index & table
|
|
@param[in] space tablespace id
|
|
@param[in] space_discarded true if space is discarded
|
|
@param[in] in_flags space flags to use when space_discarded is true
|
|
@param[in] is_create true when creating SDI index
|
|
@return in-memory index structure for tablespace dictionary or NULL */
|
|
dict_index_t *dict_sdi_create_idx_in_mem(space_id_t space, bool space_discarded,
|
|
uint32_t in_flags, bool is_create) {
|
|
uint32_t flags = space_discarded ? in_flags : fil_space_get_flags(space);
|
|
|
|
/* This means the tablespace is evicted from cache */
|
|
if (flags == UINT32_UNDEFINED) {
|
|
return (NULL);
|
|
}
|
|
|
|
ut_ad(fsp_flags_is_valid(flags));
|
|
|
|
mutex_exit(&dict_sys->mutex);
|
|
|
|
rec_format_t rec_format;
|
|
|
|
ulint zip_ssize = FSP_FLAGS_GET_ZIP_SSIZE(flags);
|
|
ulint atomic_blobs = FSP_FLAGS_HAS_ATOMIC_BLOBS(flags);
|
|
bool has_data_dir = FSP_FLAGS_HAS_DATA_DIR(flags);
|
|
bool has_shared_space = FSP_FLAGS_GET_SHARED(flags);
|
|
|
|
if (zip_ssize > 0) {
|
|
rec_format = REC_FORMAT_COMPRESSED;
|
|
} else if (atomic_blobs) {
|
|
rec_format = REC_FORMAT_DYNAMIC;
|
|
} else {
|
|
rec_format = REC_FORMAT_COMPACT;
|
|
}
|
|
|
|
uint32_t table_flags = 0;
|
|
dict_tf_set(&table_flags, rec_format, zip_ssize, has_data_dir,
|
|
has_shared_space);
|
|
|
|
/* 18 = strlen(SDI) + Max digits of 4 byte spaceid (10) + 1 */
|
|
char table_name[18];
|
|
mem_heap_t *heap = mem_heap_create(DICT_HEAP_SIZE);
|
|
snprintf(table_name, sizeof(table_name), "SDI_" SPACE_ID_PF, space);
|
|
|
|
dict_table_t *table =
|
|
dict_mem_table_create(table_name, space, 5, 0, 0, table_flags, 0);
|
|
|
|
dict_mem_table_add_col(table, heap, "type", DATA_INT,
|
|
DATA_NOT_NULL | DATA_UNSIGNED, 4);
|
|
dict_mem_table_add_col(table, heap, "id", DATA_INT,
|
|
DATA_NOT_NULL | DATA_UNSIGNED, 8);
|
|
dict_mem_table_add_col(table, heap, "compressed_len", DATA_INT,
|
|
DATA_NOT_NULL | DATA_UNSIGNED, 4);
|
|
dict_mem_table_add_col(table, heap, "uncompressed_len", DATA_INT,
|
|
DATA_NOT_NULL | DATA_UNSIGNED, 4);
|
|
dict_mem_table_add_col(table, heap, "data", DATA_BLOB, DATA_NOT_NULL, 0);
|
|
|
|
table->id = dict_sdi_get_table_id(space);
|
|
|
|
/* Disable persistent statistics on the table */
|
|
dict_stats_set_persistent(table, false, true);
|
|
|
|
dict_table_add_system_columns(table, heap);
|
|
|
|
const char *index_name = "CLUST_IND_SDI";
|
|
|
|
dict_index_t *temp_index =
|
|
dict_mem_index_create(table_name, index_name, space,
|
|
DICT_CLUSTERED | DICT_UNIQUE | DICT_SDI, 2);
|
|
ut_ad(temp_index);
|
|
|
|
temp_index->add_field("type", 0, true);
|
|
temp_index->add_field("id", 0, true);
|
|
|
|
temp_index->table = table;
|
|
|
|
/* Disable AHI on SDI tables */
|
|
temp_index->disable_ahi = true;
|
|
|
|
page_no_t index_root_page_num;
|
|
|
|
/* When we do DISCARD TABLESPACE, there will be no fil_space_t
|
|
for the tablespace. In this case, we should not use fil_space_*()
|
|
methods */
|
|
if (!space_discarded && !is_create) {
|
|
mtr_t mtr;
|
|
mtr.start();
|
|
|
|
index_root_page_num =
|
|
fsp_sdi_get_root_page_num(space, page_size_t(flags), &mtr);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
} else {
|
|
index_root_page_num = FIL_NULL;
|
|
}
|
|
|
|
temp_index->id = dict_sdi_get_index_id();
|
|
|
|
dberr_t error =
|
|
dict_index_add_to_cache(table, temp_index, index_root_page_num, false);
|
|
ut_a(error == DB_SUCCESS);
|
|
|
|
mutex_enter(&dict_sys->mutex);
|
|
|
|
/* After re-acquiring dict_sys mutex, check if there is already
|
|
a table created by other threads. Just keep one copy in memory */
|
|
dict_table_t *exist = dict_table_check_if_in_cache_low(table->name.m_name);
|
|
if (exist != nullptr) {
|
|
dict_index_remove_from_cache(table, table->first_index());
|
|
dict_mem_table_free(table);
|
|
table = exist;
|
|
} else {
|
|
dict_table_add_to_cache(table, TRUE, heap);
|
|
}
|
|
|
|
mem_heap_free(heap);
|
|
return (table->first_index());
|
|
}
|