mirror of
https://github.com/MariaDB/server.git
synced 2025-02-02 03:51:50 +01:00
765a43605a
Problem was that bpage was referenced after it was already freed from LRU. Fixed by adding a new variable encrypted that is passed down to buf_page_check_corrupt() and used in buf_page_get_gen() to stop processing page read. This patch should also address following test failures and bugs: MDEV-12419: IMPORT should not look up tablespace in PageConverter::validate(). This is now removed. MDEV-10099: encryption.innodb_onlinealter_encryption fails sporadically in buildbot MDEV-11420: encryption.innodb_encryption-page-compression failed in buildbot MDEV-11222: encryption.encrypt_and_grep failed in buildbot on P8 Removed dict_table_t::is_encrypted and dict_table_t::ibd_file_missing and replaced these with dict_table_t::file_unreadable. Table ibd file is missing if fil_get_space(space_id) returns NULL and encrypted if not. Removed dict_table_t::is_corrupted field. Ported FilSpace class from 10.2 and using that on buf_page_check_corrupt(), buf_page_decrypt_after_read(), buf_page_encrypt_before_write(), buf_dblwr_process(), buf_read_page(), dict_stats_save_defrag_stats(). Added test cases when enrypted page could be read while doing redo log crash recovery. Also added test case for row compressed blobs. btr_cur_open_at_index_side_func(), btr_cur_open_at_rnd_pos_func(): Avoid referencing block that is NULL. buf_page_get_zip(): Issue error if page read fails. buf_page_get_gen(): Use dberr_t for error detection and do not reference bpage after we hare freed it. buf_mark_space_corrupt(): remove bpage from LRU also when it is encrypted. buf_page_check_corrupt(): @return DB_SUCCESS if page has been read and is not corrupted, DB_PAGE_CORRUPTED if page based on checksum check is corrupted, DB_DECRYPTION_FAILED if page post encryption checksum matches but after decryption normal page checksum does not match. In read case only DB_SUCCESS is possible. buf_page_io_complete(): use dberr_t for error handling. buf_flush_write_block_low(), buf_read_ahead_random(), buf_read_page_async(), buf_read_ahead_linear(), buf_read_ibuf_merge_pages(), buf_read_recv_pages(), fil_aio_wait(): Issue error if page read fails. btr_pcur_move_to_next_page(): Do not reference page if it is NULL. Introduced dict_table_t::is_readable() and dict_index_t::is_readable() that will return true if tablespace exists and pages read from tablespace are not corrupted or page decryption failed. Removed buf_page_t::key_version. After page decryption the key version is not removed from page frame. For unencrypted pages, old key_version is removed at buf_page_encrypt_before_write() dict_stats_update_transient_for_index(), dict_stats_update_transient() Do not continue if table decryption failed or table is corrupted. dict0stats.cc: Introduced a dict_stats_report_error function to avoid code duplication. fil_parse_write_crypt_data(): Check that key read from redo log entry is found from encryption plugin and if it is not, refuse to start. PageConverter::validate(): Removed access to fil_space_t as tablespace is not available during import. Fixed error code on innodb.innodb test. Merged test cased innodb-bad-key-change5 and innodb-bad-key-shutdown to innodb-bad-key-change2. Removed innodb-bad-key-change5 test. Decreased unnecessary complexity on some long lasting tests. Removed fil_inc_pending_ops(), fil_decr_pending_ops(), fil_get_first_space(), fil_get_next_space(), fil_get_first_space_safe(), fil_get_next_space_safe() functions. fil_space_verify_crypt_checksum(): Fixed bug found using ASAN where FIL_PAGE_END_LSN_OLD_CHECKSUM field was incorrectly accessed from row compressed tables. Fixed out of page frame bug for row compressed tables in fil_space_verify_crypt_checksum() found using ASAN. Incorrect function was called for compressed table. Added new tests for discard, rename table and drop (we should allow them even when page decryption fails). Alter table rename is not allowed. Added test for restart with innodb-force-recovery=1 when page read on redo-recovery cant be decrypted. Added test for corrupted table where both page data and FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION is corrupted. Adjusted the test case innodb_bug14147491 so that it does not anymore expect crash. Instead table is just mostly not usable. fil0fil.h: fil_space_acquire_low is not visible function and fil_space_acquire and fil_space_acquire_silent are inline functions. FilSpace class uses fil_space_acquire_low directly. recv_apply_hashed_log_recs() does not return anything.
822 lines
22 KiB
C++
822 lines
22 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
|
Copyright (c) 2012, Facebook Inc.
|
|
Copyright (c) 2013, 2017, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; version 2 of the License.
|
|
|
|
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 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 Street, Suite 500, Boston, MA 02110-1335 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************//**
|
|
@file dict/dict0mem.cc
|
|
Data dictionary memory object creation
|
|
|
|
Created 1/8/1996 Heikki Tuuri
|
|
***********************************************************************/
|
|
|
|
#include "dict0mem.h"
|
|
|
|
#ifdef UNIV_NONINL
|
|
#include "dict0mem.ic"
|
|
#endif
|
|
|
|
#include "rem0rec.h"
|
|
#include "data0type.h"
|
|
#include "mach0data.h"
|
|
#include "dict0dict.h"
|
|
#include "fts0priv.h"
|
|
#include "ut0crc32.h"
|
|
#ifndef UNIV_HOTBACKUP
|
|
# include "ha_prototypes.h" /* innobase_casedn_str(),
|
|
innobase_get_lower_case_table_names */
|
|
# include "mysql_com.h" /* NAME_LEN */
|
|
# include "lock0lock.h"
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
#ifdef UNIV_BLOB_DEBUG
|
|
# include "ut0rbt.h"
|
|
#endif /* UNIV_BLOB_DEBUG */
|
|
#include <iostream>
|
|
|
|
#define DICT_HEAP_SIZE 100 /*!< initial memory heap size when
|
|
creating a table or index object */
|
|
|
|
#ifdef UNIV_PFS_MUTEX
|
|
/* Key to register autoinc_mutex with performance schema */
|
|
UNIV_INTERN mysql_pfs_key_t autoinc_mutex_key;
|
|
#endif /* UNIV_PFS_MUTEX */
|
|
|
|
/** System databases */
|
|
static const char* innobase_system_databases[] = {
|
|
"mysql/",
|
|
"information_schema/",
|
|
"performance_schema/",
|
|
NullS
|
|
};
|
|
|
|
/** An interger randomly initialized at startup used to make a temporary
|
|
table name as unique as possible. */
|
|
static ib_uint32_t dict_temp_file_num;
|
|
|
|
/**********************************************************************//**
|
|
Creates a table memory object.
|
|
@return own: table object */
|
|
UNIV_INTERN
|
|
dict_table_t*
|
|
dict_mem_table_create(
|
|
/*==================*/
|
|
const char* name, /*!< in: table name */
|
|
ulint space, /*!< in: space where the clustered index of
|
|
the table is placed */
|
|
ulint n_cols, /*!< in: number of columns */
|
|
ulint flags, /*!< in: table flags */
|
|
ulint flags2) /*!< in: table flags2 */
|
|
{
|
|
dict_table_t* table;
|
|
mem_heap_t* heap;
|
|
|
|
ut_ad(name);
|
|
ut_a(dict_tf_is_valid(flags));
|
|
ut_a(!(flags2 & ~DICT_TF2_BIT_MASK));
|
|
|
|
heap = mem_heap_create(DICT_HEAP_SIZE);
|
|
|
|
table = static_cast<dict_table_t*>(
|
|
mem_heap_zalloc(heap, sizeof(dict_table_t)));
|
|
|
|
table->heap = heap;
|
|
|
|
table->flags = (unsigned int) flags;
|
|
table->flags2 = (unsigned int) flags2;
|
|
table->name = static_cast<char*>(ut_malloc(strlen(name) + 1));
|
|
memcpy(table->name, name, strlen(name) + 1);
|
|
table->is_system_db = dict_mem_table_is_system(table->name);
|
|
table->space = (unsigned int) space;
|
|
table->n_cols = (unsigned int) (n_cols + DATA_N_SYS_COLS);
|
|
|
|
table->cols = static_cast<dict_col_t*>(
|
|
mem_heap_alloc(heap,
|
|
(n_cols + DATA_N_SYS_COLS)
|
|
* sizeof(dict_col_t)));
|
|
|
|
ut_d(table->magic_n = DICT_TABLE_MAGIC_N);
|
|
|
|
/* true means that the stats latch will be enabled -
|
|
dict_table_stats_lock() will not be noop. */
|
|
dict_table_stats_latch_create(table, true);
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
table->autoinc_lock = static_cast<ib_lock_t*>(
|
|
mem_heap_alloc(heap, lock_get_size()));
|
|
|
|
dict_table_autoinc_create_lazy(table);
|
|
|
|
table->autoinc = 0;
|
|
|
|
/* The number of transactions that are either waiting on the
|
|
AUTOINC lock or have been granted the lock. */
|
|
table->n_waiting_or_granted_auto_inc_locks = 0;
|
|
|
|
/* If the table has an FTS index or we are in the process
|
|
of building one, create the table->fts */
|
|
if (dict_table_has_fts_index(table)
|
|
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
|
|
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) {
|
|
table->fts = fts_create(table);
|
|
table->fts->cache = fts_cache_create(table);
|
|
} else {
|
|
table->fts = NULL;
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
new(&table->foreign_set) dict_foreign_set();
|
|
new(&table->referenced_set) dict_foreign_set();
|
|
|
|
return(table);
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Determines if a table belongs to a system database
|
|
@return */
|
|
UNIV_INTERN
|
|
bool
|
|
dict_mem_table_is_system(
|
|
/*================*/
|
|
char *name) /*!< in: table name */
|
|
{
|
|
ut_ad(name);
|
|
|
|
/* table has the following format: database/table
|
|
and some system table are of the form SYS_* */
|
|
if (strchr(name, '/')) {
|
|
int table_len = strlen(name);
|
|
const char *system_db;
|
|
int i = 0;
|
|
while ((system_db = innobase_system_databases[i++])
|
|
&& (system_db != NullS)) {
|
|
int len = strlen(system_db);
|
|
if (table_len > len && !strncmp(name, system_db, len)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Free a table memory object. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_table_free(
|
|
/*================*/
|
|
dict_table_t* table) /*!< in: table */
|
|
{
|
|
ut_ad(table);
|
|
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
|
|
ut_d(table->cached = FALSE);
|
|
|
|
if (dict_table_has_fts_index(table)
|
|
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
|
|
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID)) {
|
|
if (table->fts) {
|
|
if (table->cached) {
|
|
fts_optimize_remove_table(table);
|
|
}
|
|
|
|
fts_free(table);
|
|
}
|
|
}
|
|
#ifndef UNIV_HOTBACKUP
|
|
dict_table_autoinc_destroy(table);
|
|
#endif /* UNIV_HOTBACKUP */
|
|
|
|
dict_table_stats_latch_destroy(table);
|
|
|
|
table->foreign_set.~dict_foreign_set();
|
|
table->referenced_set.~dict_foreign_set();
|
|
|
|
ut_free(table->name);
|
|
mem_heap_free(table->heap);
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Append 'name' to 'col_names'. @see dict_table_t::col_names
|
|
@return new column names array */
|
|
static
|
|
const char*
|
|
dict_add_col_name(
|
|
/*==============*/
|
|
const char* col_names, /*!< in: existing column names, or
|
|
NULL */
|
|
ulint cols, /*!< in: number of existing columns */
|
|
const char* name, /*!< in: new column name */
|
|
mem_heap_t* heap) /*!< in: heap */
|
|
{
|
|
ulint old_len;
|
|
ulint new_len;
|
|
ulint total_len;
|
|
char* res;
|
|
|
|
ut_ad(!cols == !col_names);
|
|
|
|
/* Find out length of existing array. */
|
|
if (col_names) {
|
|
const char* s = col_names;
|
|
ulint i;
|
|
|
|
for (i = 0; i < cols; i++) {
|
|
s += strlen(s) + 1;
|
|
}
|
|
|
|
old_len = s - col_names;
|
|
} else {
|
|
old_len = 0;
|
|
}
|
|
|
|
new_len = strlen(name) + 1;
|
|
total_len = old_len + new_len;
|
|
|
|
res = static_cast<char*>(mem_heap_alloc(heap, total_len));
|
|
|
|
if (old_len > 0) {
|
|
memcpy(res, col_names, old_len);
|
|
}
|
|
|
|
memcpy(res + old_len, name, new_len);
|
|
|
|
return(res);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Adds a column definition to a table. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_table_add_col(
|
|
/*===================*/
|
|
dict_table_t* table, /*!< in: table */
|
|
mem_heap_t* heap, /*!< in: temporary memory heap, or NULL */
|
|
const char* name, /*!< in: column name, or NULL */
|
|
ulint mtype, /*!< in: main datatype */
|
|
ulint prtype, /*!< in: precise type */
|
|
ulint len) /*!< in: precision */
|
|
{
|
|
dict_col_t* col;
|
|
ulint i;
|
|
|
|
ut_ad(table);
|
|
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
|
|
ut_ad(!heap == !name);
|
|
|
|
i = table->n_def++;
|
|
|
|
if (name) {
|
|
if (UNIV_UNLIKELY(table->n_def == table->n_cols)) {
|
|
heap = table->heap;
|
|
}
|
|
if (UNIV_LIKELY(i != 0) && UNIV_UNLIKELY(table->col_names == NULL)) {
|
|
/* All preceding column names are empty. */
|
|
char* s = static_cast<char*>(
|
|
mem_heap_zalloc(heap, table->n_def));
|
|
|
|
table->col_names = s;
|
|
}
|
|
|
|
table->col_names = dict_add_col_name(table->col_names,
|
|
i, name, heap);
|
|
}
|
|
|
|
col = dict_table_get_nth_col(table, i);
|
|
|
|
dict_mem_fill_column_struct(col, i, mtype, prtype, len);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Renames a column of a table in the data dictionary cache. */
|
|
static MY_ATTRIBUTE((nonnull))
|
|
void
|
|
dict_mem_table_col_rename_low(
|
|
/*==========================*/
|
|
dict_table_t* table, /*!< in/out: table */
|
|
unsigned i, /*!< in: column offset corresponding to s */
|
|
const char* to, /*!< in: new column name */
|
|
const char* s) /*!< in: pointer to table->col_names */
|
|
{
|
|
size_t from_len = strlen(s), to_len = strlen(to);
|
|
|
|
ut_ad(i < table->n_def);
|
|
ut_ad(from_len <= NAME_LEN);
|
|
ut_ad(to_len <= NAME_LEN);
|
|
|
|
char from[NAME_LEN];
|
|
strncpy(from, s, NAME_LEN);
|
|
|
|
if (from_len == to_len) {
|
|
/* The easy case: simply replace the column name in
|
|
table->col_names. */
|
|
strcpy(const_cast<char*>(s), to);
|
|
} else {
|
|
/* We need to adjust all affected index->field
|
|
pointers, as in dict_index_add_col(). First, copy
|
|
table->col_names. */
|
|
ulint prefix_len = s - table->col_names;
|
|
|
|
for (; i < table->n_def; i++) {
|
|
s += strlen(s) + 1;
|
|
}
|
|
|
|
ulint full_len = s - table->col_names;
|
|
char* col_names;
|
|
|
|
if (to_len > from_len) {
|
|
col_names = static_cast<char*>(
|
|
mem_heap_alloc(
|
|
table->heap,
|
|
full_len + to_len - from_len));
|
|
|
|
memcpy(col_names, table->col_names, prefix_len);
|
|
} else {
|
|
col_names = const_cast<char*>(table->col_names);
|
|
}
|
|
|
|
memcpy(col_names + prefix_len, to, to_len);
|
|
memmove(col_names + prefix_len + to_len,
|
|
table->col_names + (prefix_len + from_len),
|
|
full_len - (prefix_len + from_len));
|
|
|
|
/* Replace the field names in every index. */
|
|
for (dict_index_t* index = dict_table_get_first_index(table);
|
|
index != NULL;
|
|
index = dict_table_get_next_index(index)) {
|
|
ulint n_fields = dict_index_get_n_fields(index);
|
|
|
|
for (ulint i = 0; i < n_fields; i++) {
|
|
dict_field_t* field
|
|
= dict_index_get_nth_field(
|
|
index, i);
|
|
ulint name_ofs
|
|
= field->name - table->col_names;
|
|
if (name_ofs <= prefix_len) {
|
|
field->name = col_names + name_ofs;
|
|
} else {
|
|
ut_a(name_ofs < full_len);
|
|
field->name = col_names
|
|
+ name_ofs + to_len - from_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
table->col_names = col_names;
|
|
}
|
|
|
|
dict_foreign_t* foreign;
|
|
|
|
/* Replace the field names in every foreign key constraint. */
|
|
for (dict_foreign_set::iterator it = table->foreign_set.begin();
|
|
it != table->foreign_set.end();
|
|
++it) {
|
|
|
|
foreign = *it;
|
|
|
|
if (foreign->foreign_index == NULL) {
|
|
/* We may go here when we set foreign_key_checks to 0,
|
|
and then try to rename a column and modify the
|
|
corresponding foreign key constraint. The index
|
|
would have been dropped, we have to find an equivalent
|
|
one */
|
|
for (unsigned f = 0; f < foreign->n_fields; f++) {
|
|
if (strcmp(foreign->foreign_col_names[f], from)
|
|
== 0) {
|
|
|
|
char** rc = const_cast<char**>(
|
|
foreign->foreign_col_names
|
|
+ f);
|
|
|
|
if (to_len <= strlen(*rc)) {
|
|
memcpy(*rc, to, to_len + 1);
|
|
} else {
|
|
*rc = static_cast<char*>(
|
|
mem_heap_dup(
|
|
foreign->heap,
|
|
to,
|
|
to_len + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
dict_index_t* new_index = dict_foreign_find_index(
|
|
foreign->foreign_table, NULL,
|
|
foreign->foreign_col_names,
|
|
foreign->n_fields, NULL, true, false,
|
|
NULL, NULL, NULL);
|
|
/* There must be an equivalent index in this case. */
|
|
ut_ad(new_index != NULL);
|
|
|
|
foreign->foreign_index = new_index;
|
|
|
|
} else {
|
|
|
|
for (unsigned f = 0; f < foreign->n_fields; f++) {
|
|
/* These can point straight to
|
|
table->col_names, because the foreign key
|
|
constraints will be freed at the same time
|
|
when the table object is freed. */
|
|
foreign->foreign_col_names[f]
|
|
= dict_index_get_nth_field(
|
|
foreign->foreign_index,
|
|
f)->name;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (dict_foreign_set::iterator it = table->referenced_set.begin();
|
|
it != table->referenced_set.end();
|
|
++it) {
|
|
|
|
foreign = *it;
|
|
|
|
ut_ad(foreign->referenced_index != NULL);
|
|
|
|
for (unsigned f = 0; f < foreign->n_fields; f++) {
|
|
/* foreign->referenced_col_names[] need to be
|
|
copies, because the constraint may become
|
|
orphan when foreign_key_checks=0 and the
|
|
parent table is dropped. */
|
|
|
|
const char* col_name = dict_index_get_nth_field(
|
|
foreign->referenced_index, f)->name;
|
|
|
|
if (strcmp(foreign->referenced_col_names[f],
|
|
col_name)) {
|
|
char** rc = const_cast<char**>(
|
|
foreign->referenced_col_names + f);
|
|
size_t col_name_len_1 = strlen(col_name) + 1;
|
|
|
|
if (col_name_len_1 <= strlen(*rc) + 1) {
|
|
memcpy(*rc, col_name, col_name_len_1);
|
|
} else {
|
|
*rc = static_cast<char*>(
|
|
mem_heap_dup(
|
|
foreign->heap,
|
|
col_name,
|
|
col_name_len_1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Renames a column of a table in the data dictionary cache. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_table_col_rename(
|
|
/*======================*/
|
|
dict_table_t* table, /*!< in/out: table */
|
|
unsigned nth_col,/*!< in: column index */
|
|
const char* from, /*!< in: old column name */
|
|
const char* to) /*!< in: new column name */
|
|
{
|
|
const char* s = table->col_names;
|
|
|
|
ut_ad(nth_col < table->n_def);
|
|
|
|
for (unsigned i = 0; i < nth_col; i++) {
|
|
size_t len = strlen(s);
|
|
ut_ad(len > 0);
|
|
s += len + 1;
|
|
}
|
|
|
|
/* This could fail if the data dictionaries are out of sync.
|
|
Proceed with the renaming anyway. */
|
|
ut_ad(!strcmp(from, s));
|
|
|
|
dict_mem_table_col_rename_low(table, nth_col, to, s);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
This function populates a dict_col_t memory structure with
|
|
supplied information. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_fill_column_struct(
|
|
/*========================*/
|
|
dict_col_t* column, /*!< out: column struct to be
|
|
filled */
|
|
ulint col_pos, /*!< in: column position */
|
|
ulint mtype, /*!< in: main data type */
|
|
ulint prtype, /*!< in: precise type */
|
|
ulint col_len) /*!< in: column length */
|
|
{
|
|
#ifndef UNIV_HOTBACKUP
|
|
ulint mbminlen;
|
|
ulint mbmaxlen;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
column->ind = (unsigned int) col_pos;
|
|
column->ord_part = 0;
|
|
column->max_prefix = 0;
|
|
column->mtype = (unsigned int) mtype;
|
|
column->prtype = (unsigned int) prtype;
|
|
column->len = (unsigned int) col_len;
|
|
#ifndef UNIV_HOTBACKUP
|
|
dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen);
|
|
dict_col_set_mbminmaxlen(column, mbminlen, mbmaxlen);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Creates an index memory object.
|
|
@return own: index object */
|
|
UNIV_INTERN
|
|
dict_index_t*
|
|
dict_mem_index_create(
|
|
/*==================*/
|
|
const char* table_name, /*!< in: table name */
|
|
const char* index_name, /*!< in: index name */
|
|
ulint space, /*!< in: space where the index tree is
|
|
placed, ignored if the index is of
|
|
the clustered type */
|
|
ulint type, /*!< in: DICT_UNIQUE,
|
|
DICT_CLUSTERED, ... ORed */
|
|
ulint n_fields) /*!< in: number of fields */
|
|
{
|
|
dict_index_t* index;
|
|
mem_heap_t* heap;
|
|
|
|
ut_ad(table_name && index_name);
|
|
|
|
heap = mem_heap_create(DICT_HEAP_SIZE);
|
|
|
|
index = static_cast<dict_index_t*>(
|
|
mem_heap_zalloc(heap, sizeof(*index)));
|
|
|
|
dict_mem_fill_index_struct(index, heap, table_name, index_name,
|
|
space, type, n_fields);
|
|
|
|
dict_index_zip_pad_mutex_create_lazy(index);
|
|
return(index);
|
|
}
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/**********************************************************************//**
|
|
Creates and initializes a foreign constraint memory object.
|
|
@return own: foreign constraint struct */
|
|
UNIV_INTERN
|
|
dict_foreign_t*
|
|
dict_mem_foreign_create(void)
|
|
/*=========================*/
|
|
{
|
|
dict_foreign_t* foreign;
|
|
mem_heap_t* heap;
|
|
|
|
heap = mem_heap_create(100);
|
|
|
|
foreign = static_cast<dict_foreign_t*>(
|
|
mem_heap_zalloc(heap, sizeof(dict_foreign_t)));
|
|
|
|
foreign->heap = heap;
|
|
|
|
return(foreign);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Sets the foreign_table_name_lookup pointer based on the value of
|
|
lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup
|
|
will point to foreign_table_name. If 2, then another string is
|
|
allocated from foreign->heap and set to lower case. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_foreign_table_name_lookup_set(
|
|
/*===================================*/
|
|
dict_foreign_t* foreign, /*!< in/out: foreign struct */
|
|
ibool do_alloc) /*!< in: is an alloc needed */
|
|
{
|
|
if (innobase_get_lower_case_table_names() == 2) {
|
|
if (do_alloc) {
|
|
ulint len;
|
|
|
|
len = strlen(foreign->foreign_table_name) + 1;
|
|
|
|
foreign->foreign_table_name_lookup =
|
|
static_cast<char*>(
|
|
mem_heap_alloc(foreign->heap, len));
|
|
}
|
|
strcpy(foreign->foreign_table_name_lookup,
|
|
foreign->foreign_table_name);
|
|
innobase_casedn_str(foreign->foreign_table_name_lookup);
|
|
} else {
|
|
foreign->foreign_table_name_lookup
|
|
= foreign->foreign_table_name;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Sets the referenced_table_name_lookup pointer based on the value of
|
|
lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup
|
|
will point to referenced_table_name. If 2, then another string is
|
|
allocated from foreign->heap and set to lower case. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_referenced_table_name_lookup_set(
|
|
/*======================================*/
|
|
dict_foreign_t* foreign, /*!< in/out: foreign struct */
|
|
ibool do_alloc) /*!< in: is an alloc needed */
|
|
{
|
|
if (innobase_get_lower_case_table_names() == 2) {
|
|
if (do_alloc) {
|
|
ulint len;
|
|
|
|
len = strlen(foreign->referenced_table_name) + 1;
|
|
|
|
foreign->referenced_table_name_lookup =
|
|
static_cast<char*>(
|
|
mem_heap_alloc(foreign->heap, len));
|
|
}
|
|
strcpy(foreign->referenced_table_name_lookup,
|
|
foreign->referenced_table_name);
|
|
innobase_casedn_str(foreign->referenced_table_name_lookup);
|
|
} else {
|
|
foreign->referenced_table_name_lookup
|
|
= foreign->referenced_table_name;
|
|
}
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/**********************************************************************//**
|
|
Adds a field definition to an index. NOTE: does not take a copy
|
|
of the column name if the field is a column. The memory occupied
|
|
by the column name may be released only after publishing the index. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_index_add_field(
|
|
/*=====================*/
|
|
dict_index_t* index, /*!< in: index */
|
|
const char* name, /*!< in: column name */
|
|
ulint prefix_len) /*!< in: 0 or the column prefix length
|
|
in a MySQL index like
|
|
INDEX (textcol(25)) */
|
|
{
|
|
dict_field_t* field;
|
|
|
|
ut_ad(index);
|
|
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
index->n_def++;
|
|
|
|
field = dict_index_get_nth_field(index, index->n_def - 1);
|
|
|
|
field->name = name;
|
|
field->prefix_len = (unsigned int) prefix_len;
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Frees an index memory object. */
|
|
UNIV_INTERN
|
|
void
|
|
dict_mem_index_free(
|
|
/*================*/
|
|
dict_index_t* index) /*!< in: index */
|
|
{
|
|
ut_ad(index);
|
|
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
|
|
#ifdef UNIV_BLOB_DEBUG
|
|
if (index->blobs) {
|
|
mutex_free(&index->blobs_mutex);
|
|
rbt_free(index->blobs);
|
|
}
|
|
#endif /* UNIV_BLOB_DEBUG */
|
|
|
|
dict_index_zip_pad_mutex_destroy(index);
|
|
|
|
mem_heap_free(index->heap);
|
|
}
|
|
|
|
/** Create a temporary tablename like "#sql-ibtid-inc where
|
|
tid = the Table ID
|
|
inc = a randomly initialized number that is incremented for each file
|
|
The table ID is a 64 bit integer, can use up to 20 digits, and is
|
|
initialized at bootstrap. The second number is 32 bits, can use up to 10
|
|
digits, and is initialized at startup to a randomly distributed number.
|
|
It is hoped that the combination of these two numbers will provide a
|
|
reasonably unique temporary file name.
|
|
@param[in] heap A memory heap
|
|
@param[in] dbtab Table name in the form database/table name
|
|
@param[in] id Table id
|
|
@return A unique temporary tablename suitable for InnoDB use */
|
|
UNIV_INTERN
|
|
char*
|
|
dict_mem_create_temporary_tablename(
|
|
mem_heap_t* heap,
|
|
const char* dbtab,
|
|
table_id_t id)
|
|
{
|
|
size_t size;
|
|
char* name;
|
|
const char* dbend = strchr(dbtab, '/');
|
|
ut_ad(dbend);
|
|
size_t dblen = dbend - dbtab + 1;
|
|
|
|
#ifdef HAVE_ATOMIC_BUILTINS
|
|
/* Increment a randomly initialized number for each temp file. */
|
|
os_atomic_increment_uint32(&dict_temp_file_num, 1);
|
|
#else /* HAVE_ATOMIC_BUILTINS */
|
|
dict_temp_file_num++;
|
|
#endif /* HAVE_ATOMIC_BUILTINS */
|
|
|
|
size = tmp_file_prefix_length + 3 + 20 + 1 + 10 + dblen;
|
|
name = static_cast<char*>(mem_heap_alloc(heap, size));
|
|
memcpy(name, dbtab, dblen);
|
|
ut_snprintf(name + dblen, size - dblen,
|
|
TEMP_FILE_PREFIX_INNODB UINT64PF "-" UINT32PF,
|
|
id, dict_temp_file_num);
|
|
|
|
return(name);
|
|
}
|
|
|
|
/** Initialize dict memory variables */
|
|
|
|
void
|
|
dict_mem_init(void)
|
|
{
|
|
/* Initialize a randomly distributed temporary file number */
|
|
ib_uint32_t now = static_cast<ib_uint32_t>(ut_time());
|
|
|
|
const byte* buf = reinterpret_cast<const byte*>(&now);
|
|
ut_ad(ut_crc32 != NULL);
|
|
|
|
dict_temp_file_num = ut_crc32(buf, sizeof(now));
|
|
|
|
DBUG_PRINT("dict_mem_init",
|
|
("Starting Temporary file number is " UINT32PF,
|
|
dict_temp_file_num));
|
|
}
|
|
|
|
/** Validate the search order in the foreign key set.
|
|
@param[in] fk_set the foreign key set to be validated
|
|
@return true if search order is fine in the set, false otherwise. */
|
|
bool
|
|
dict_foreign_set_validate(
|
|
const dict_foreign_set& fk_set)
|
|
{
|
|
dict_foreign_not_exists not_exists(fk_set);
|
|
|
|
dict_foreign_set::const_iterator it = std::find_if(
|
|
fk_set.begin(), fk_set.end(), not_exists);
|
|
|
|
if (it == fk_set.end()) {
|
|
return(true);
|
|
}
|
|
|
|
dict_foreign_t* foreign = *it;
|
|
std::cerr << "Foreign key lookup failed: " << *foreign;
|
|
std::cerr << fk_set;
|
|
ut_ad(0);
|
|
return(false);
|
|
}
|
|
|
|
/** Validate the search order in the foreign key sets of the table
|
|
(foreign_set and referenced_set).
|
|
@param[in] table table whose foreign key sets are to be validated
|
|
@return true if foreign key sets are fine, false otherwise. */
|
|
bool
|
|
dict_foreign_set_validate(
|
|
const dict_table_t& table)
|
|
{
|
|
return(dict_foreign_set_validate(table.foreign_set)
|
|
&& dict_foreign_set_validate(table.referenced_set));
|
|
}
|
|
|
|
std::ostream&
|
|
operator<< (std::ostream& out, const dict_foreign_t& foreign)
|
|
{
|
|
out << "[dict_foreign_t: id='" << foreign.id << "'";
|
|
|
|
if (foreign.foreign_table_name != NULL) {
|
|
out << ",for: '" << foreign.foreign_table_name << "'";
|
|
}
|
|
|
|
out << "]";
|
|
return(out);
|
|
}
|
|
|
|
std::ostream&
|
|
operator<< (std::ostream& out, const dict_foreign_set& fk_set)
|
|
{
|
|
out << "[dict_foreign_set:";
|
|
std::for_each(fk_set.begin(), fk_set.end(), dict_foreign_print(out));
|
|
out << "]" << std::endl;
|
|
return(out);
|
|
}
|
|
|