2590 lines
94 KiB
C++
2590 lines
94 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
Copyright (c) 2012, Facebook Inc.
|
|
|
|
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 include/dict0mem.h
|
|
Data dictionary memory object creation
|
|
|
|
Created 1/8/1996 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#ifndef dict0mem_h
|
|
#define dict0mem_h
|
|
|
|
#include "sql/dd/object_id.h"
|
|
#include "sql/dd/types/column.h"
|
|
#include "univ.i"
|
|
#ifdef UNIV_HOTBACKUP
|
|
#include "sql/dd/types/spatial_reference_system.h"
|
|
#endif /* UNIV_HOTBACKUP */
|
|
#include "btr0types.h"
|
|
#include "data0type.h"
|
|
#include "dict0types.h"
|
|
#include "mem0mem.h"
|
|
#include "rem0types.h"
|
|
#include "row0types.h"
|
|
#ifndef UNIV_HOTBACKUP
|
|
#include "lock0types.h"
|
|
#include "que0types.h"
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
#include "hash0hash.h"
|
|
#include "sync0rw.h"
|
|
#include "trx0types.h"
|
|
#include "ut0byte.h"
|
|
#include "ut0mem.h"
|
|
#include "ut0rnd.h"
|
|
#ifndef UNIV_HOTBACKUP
|
|
#include "fts0fts.h"
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
#include "buf0buf.h"
|
|
#include "gis0type.h"
|
|
#ifndef UNIV_HOTBACKUP
|
|
#include "os0once.h"
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
#include "dict/mem.h"
|
|
#include "ut0new.h"
|
|
|
|
#include "sql/sql_const.h" /* MAX_KEY_LENGTH */
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <memory> /* std::unique_ptr */
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include "lizard0data0types.h"
|
|
#include "lizard0undo0types.h"
|
|
|
|
/* Forward declaration. */
|
|
struct ib_rbt_t;
|
|
|
|
/** Type flags of an index: OR'ing of the flags is allowed to define a
|
|
combination of types */
|
|
/* @{ */
|
|
#define DICT_CLUSTERED \
|
|
1 /*!< clustered index; for other than \
|
|
auto-generated clustered indexes, \
|
|
also DICT_UNIQUE will be set */
|
|
#define DICT_UNIQUE 2 /*!< unique index */
|
|
#define DICT_IBUF 8 /*!< insert buffer tree */
|
|
#define DICT_CORRUPT \
|
|
16 /*!< bit to store the corrupted flag \
|
|
in SYS_INDEXES.TYPE */
|
|
#define DICT_FTS \
|
|
32 /* FTS index; can't be combined with the \
|
|
other flags */
|
|
#define DICT_SPATIAL \
|
|
64 /* SPATIAL index; can't be combined with the \
|
|
other flags */
|
|
#define DICT_VIRTUAL 128 /* Index on Virtual column */
|
|
|
|
#define DICT_SDI \
|
|
256 /* Tablespace dictionary Index. Set only in \
|
|
in-memory index structure. */
|
|
#define DICT_MULTI_VALUE 512 /* Multi-value index */
|
|
|
|
#define DICT_IT_BITS \
|
|
10 /*!< number of bits used for \
|
|
SYS_INDEXES.TYPE */
|
|
/* @} */
|
|
|
|
#if 0 /* not implemented, retained for history */
|
|
/** Types for a table object */
|
|
#define DICT_TABLE_ORDINARY 1 /*!< ordinary table */
|
|
#define DICT_TABLE_CLUSTER_MEMBER 2
|
|
#define DICT_TABLE_CLUSTER \
|
|
3 /* this means that the table is \
|
|
really a cluster definition */
|
|
#endif
|
|
|
|
/* Table and tablespace flags are generally not used for the Antelope file
|
|
format except for the low order bit, which is used differently depending on
|
|
where the flags are stored.
|
|
|
|
==================== Low order flags bit =========================
|
|
| REDUNDANT | COMPACT | COMPRESSED and DYNAMIC
|
|
SYS_TABLES.TYPE | 1 | 1 | 1
|
|
dict_table_t::flags | 0 | 1 | 1
|
|
FSP_SPACE_FLAGS | 0 | 0 | 1
|
|
fil_space_t::flags | 0 | 0 | 1
|
|
|
|
Before the 5.1 plugin, SYS_TABLES.TYPE was always DICT_TABLE_ORDINARY (1)
|
|
and the tablespace flags field was always 0. In the 5.1 plugin, these fields
|
|
were repurposed to identify compressed and dynamic row formats.
|
|
|
|
The following types and constants describe the flags found in dict_table_t
|
|
and SYS_TABLES.TYPE. Similar flags found in fil_space_t and FSP_SPACE_FLAGS
|
|
are described in fsp0fsp.h. */
|
|
|
|
/* @{ */
|
|
/** dict_table_t::flags bit 0 is equal to 0 if the row format = Redundant */
|
|
#define DICT_TF_REDUNDANT 0 /*!< Redundant row format. */
|
|
/** dict_table_t::flags bit 0 is equal to 1 if the row format = Compact */
|
|
#define DICT_TF_COMPACT 1 /*!< Compact row format. */
|
|
|
|
/** This bitmask is used in SYS_TABLES.N_COLS to set and test whether
|
|
the Compact page format is used, i.e ROW_FORMAT != REDUNDANT */
|
|
#define DICT_N_COLS_COMPACT 0x80000000UL
|
|
|
|
/** Width of the COMPACT flag */
|
|
#define DICT_TF_WIDTH_COMPACT 1
|
|
|
|
/** Width of the ZIP_SSIZE flag */
|
|
#define DICT_TF_WIDTH_ZIP_SSIZE 4
|
|
|
|
/** Width of the ATOMIC_BLOBS flag. The ROW_FORMAT=REDUNDANT and
|
|
ROW_FORMAT=COMPACT broke up BLOB and TEXT fields, storing the first 768 bytes
|
|
in the clustered index. ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPRESSED
|
|
store the whole blob or text field off-page atomically.
|
|
Secondary indexes are created from this external data using row_ext_t
|
|
to cache the BLOB prefixes. */
|
|
#define DICT_TF_WIDTH_ATOMIC_BLOBS 1
|
|
|
|
/** If a table is created with the MYSQL option DATA DIRECTORY and
|
|
innodb-file-per-table, an older engine will not be able to find that table.
|
|
This flag prevents older engines from attempting to open the table and
|
|
allows InnoDB to update_create_info() accordingly. */
|
|
#define DICT_TF_WIDTH_DATA_DIR 1
|
|
|
|
/** Width of the SHARED tablespace flag.
|
|
It is used to identify tables that exist inside a shared general tablespace.
|
|
If a table is created with the TABLESPACE=tsname option, an older engine will
|
|
not be able to find that table. This flag prevents older engines from attempting
|
|
to open the table and allows InnoDB to quickly find the tablespace. */
|
|
|
|
#define DICT_TF_WIDTH_SHARED_SPACE 1
|
|
|
|
/** Width of all the currently known table flags */
|
|
#define DICT_TF_BITS \
|
|
(DICT_TF_WIDTH_COMPACT + DICT_TF_WIDTH_ZIP_SSIZE + \
|
|
DICT_TF_WIDTH_ATOMIC_BLOBS + DICT_TF_WIDTH_DATA_DIR + \
|
|
DICT_TF_WIDTH_SHARED_SPACE)
|
|
|
|
/** A mask of all the known/used bits in table flags */
|
|
#define DICT_TF_BIT_MASK (~(~0 << DICT_TF_BITS))
|
|
|
|
/** Zero relative shift position of the COMPACT field */
|
|
#define DICT_TF_POS_COMPACT 0
|
|
/** Zero relative shift position of the ZIP_SSIZE field */
|
|
#define DICT_TF_POS_ZIP_SSIZE (DICT_TF_POS_COMPACT + DICT_TF_WIDTH_COMPACT)
|
|
/** Zero relative shift position of the ATOMIC_BLOBS field */
|
|
#define DICT_TF_POS_ATOMIC_BLOBS \
|
|
(DICT_TF_POS_ZIP_SSIZE + DICT_TF_WIDTH_ZIP_SSIZE)
|
|
/** Zero relative shift position of the DATA_DIR field */
|
|
#define DICT_TF_POS_DATA_DIR \
|
|
(DICT_TF_POS_ATOMIC_BLOBS + DICT_TF_WIDTH_ATOMIC_BLOBS)
|
|
/** Zero relative shift position of the SHARED TABLESPACE field */
|
|
#define DICT_TF_POS_SHARED_SPACE (DICT_TF_POS_DATA_DIR + DICT_TF_WIDTH_DATA_DIR)
|
|
/** Zero relative shift position of the start of the UNUSED bits */
|
|
#define DICT_TF_POS_UNUSED \
|
|
(DICT_TF_POS_SHARED_SPACE + DICT_TF_WIDTH_SHARED_SPACE)
|
|
|
|
/** Bit mask of the COMPACT field */
|
|
#define DICT_TF_MASK_COMPACT \
|
|
((~(~0U << DICT_TF_WIDTH_COMPACT)) << DICT_TF_POS_COMPACT)
|
|
/** Bit mask of the ZIP_SSIZE field */
|
|
#define DICT_TF_MASK_ZIP_SSIZE \
|
|
((~(~0U << DICT_TF_WIDTH_ZIP_SSIZE)) << DICT_TF_POS_ZIP_SSIZE)
|
|
/** Bit mask of the ATOMIC_BLOBS field */
|
|
#define DICT_TF_MASK_ATOMIC_BLOBS \
|
|
((~(~0U << DICT_TF_WIDTH_ATOMIC_BLOBS)) << DICT_TF_POS_ATOMIC_BLOBS)
|
|
/** Bit mask of the DATA_DIR field */
|
|
#define DICT_TF_MASK_DATA_DIR \
|
|
((~(~0U << DICT_TF_WIDTH_DATA_DIR)) << DICT_TF_POS_DATA_DIR)
|
|
/** Bit mask of the SHARED_SPACE field */
|
|
#define DICT_TF_MASK_SHARED_SPACE \
|
|
((~(~0U << DICT_TF_WIDTH_SHARED_SPACE)) << DICT_TF_POS_SHARED_SPACE)
|
|
|
|
/** Return the value of the COMPACT field */
|
|
#define DICT_TF_GET_COMPACT(flags) \
|
|
((flags & DICT_TF_MASK_COMPACT) >> DICT_TF_POS_COMPACT)
|
|
/** Return the value of the ZIP_SSIZE field */
|
|
#define DICT_TF_GET_ZIP_SSIZE(flags) \
|
|
((flags & DICT_TF_MASK_ZIP_SSIZE) >> DICT_TF_POS_ZIP_SSIZE)
|
|
/** Return the value of the ATOMIC_BLOBS field */
|
|
#define DICT_TF_HAS_ATOMIC_BLOBS(flags) \
|
|
((flags & DICT_TF_MASK_ATOMIC_BLOBS) >> DICT_TF_POS_ATOMIC_BLOBS)
|
|
/** Return the value of the DATA_DIR field */
|
|
#define DICT_TF_HAS_DATA_DIR(flags) \
|
|
((flags & DICT_TF_MASK_DATA_DIR) >> DICT_TF_POS_DATA_DIR)
|
|
/** Return the value of the SHARED_SPACE field */
|
|
#define DICT_TF_HAS_SHARED_SPACE(flags) \
|
|
((flags & DICT_TF_MASK_SHARED_SPACE) >> DICT_TF_POS_SHARED_SPACE)
|
|
/** Return the contents of the UNUSED bits */
|
|
#define DICT_TF_GET_UNUSED(flags) (flags >> DICT_TF_POS_UNUSED)
|
|
/* @} */
|
|
|
|
/** @brief Table Flags set number 2.
|
|
|
|
These flags will be stored in SYS_TABLES.MIX_LEN. All unused flags
|
|
will be written as 0. The column may contain garbage for tables
|
|
created with old versions of InnoDB that only implemented
|
|
ROW_FORMAT=REDUNDANT. InnoDB engines do not check these flags
|
|
for unknown bits in order to protect backward incompatibility. */
|
|
/* @{ */
|
|
/** Total number of bits in table->flags2. */
|
|
#define DICT_TF2_BITS 11
|
|
#define DICT_TF2_UNUSED_BIT_MASK (~0U << DICT_TF2_BITS)
|
|
#define DICT_TF2_BIT_MASK ~DICT_TF2_UNUSED_BIT_MASK
|
|
|
|
/** TEMPORARY; TRUE for tables from CREATE TEMPORARY TABLE. */
|
|
#define DICT_TF2_TEMPORARY 1
|
|
|
|
/** The table has an internal defined DOC ID column */
|
|
#define DICT_TF2_FTS_HAS_DOC_ID 2
|
|
|
|
/** The table has an FTS index */
|
|
#define DICT_TF2_FTS 4
|
|
|
|
/** Need to add Doc ID column for FTS index build.
|
|
This is a transient bit for index build */
|
|
#define DICT_TF2_FTS_ADD_DOC_ID 8
|
|
|
|
/** This bit is used during table creation to indicate that it will
|
|
use its own tablespace instead of the system tablespace. */
|
|
#define DICT_TF2_USE_FILE_PER_TABLE 16
|
|
|
|
/** Set when we discard/detach the tablespace */
|
|
#define DICT_TF2_DISCARDED 32
|
|
|
|
/** Intrinsic table bit
|
|
Intrinsic table is table created internally by MySQL modules viz. Optimizer,
|
|
FTS, etc.... Intrinsic table has all the properties of the normal table except
|
|
it is not created by user and so not visible to end-user. */
|
|
#define DICT_TF2_INTRINSIC 128
|
|
|
|
/** Encryption table bit for innodb_file-per-table only. */
|
|
#define DICT_TF2_ENCRYPTION_FILE_PER_TABLE 256
|
|
|
|
/** FTS AUX hidden table bit. */
|
|
#define DICT_TF2_AUX 512
|
|
|
|
/** Table is opened by resurrected trx during crash recovery. */
|
|
#define DICT_TF2_RESURRECT_PREPARED 1024
|
|
/* @} */
|
|
|
|
#define DICT_TF2_FLAG_SET(table, flag) (table->flags2 |= (flag))
|
|
|
|
#define DICT_TF2_FLAG_IS_SET(table, flag) (table->flags2 & (flag))
|
|
|
|
#define DICT_TF2_FLAG_UNSET(table, flag) (table->flags2 &= ~(flag))
|
|
|
|
/** Tables could be chained together with Foreign key constraint. When
|
|
first load the parent table, we would load all of its descedents.
|
|
This could result in rescursive calls and out of stack error eventually.
|
|
DICT_FK_MAX_RECURSIVE_LOAD defines the maximum number of recursive loads,
|
|
when exceeded, the child table will not be loaded. It will be loaded when
|
|
the foreign constraint check needs to be run. */
|
|
#define DICT_FK_MAX_RECURSIVE_LOAD 20
|
|
|
|
/** Similarly, when tables are chained together with foreign key constraints
|
|
with on cascading delete/update clause, delete from parent table could
|
|
result in recursive cascading calls. This defines the maximum number of
|
|
such cascading deletes/updates allowed. When exceeded, the delete from
|
|
parent table will fail, and user has to drop excessive foreign constraint
|
|
before proceeds. */
|
|
#define FK_MAX_CASCADE_DEL 15
|
|
|
|
/** Adds a virtual column definition to a table.
|
|
@param[in,out] table table
|
|
@param[in] heap temporary memory heap, or NULL. It is
|
|
used to store name when we have not finished
|
|
adding all columns. When all columns are
|
|
added, the whole name will copy to memory from
|
|
table->heap
|
|
@param[in] name column name
|
|
@param[in] mtype main datatype
|
|
@param[in] prtype precise type
|
|
@param[in] len length
|
|
@param[in] pos position in a table
|
|
@param[in] num_base number of base columns
|
|
@return the virtual column definition */
|
|
dict_v_col_t *dict_mem_table_add_v_col(dict_table_t *table, mem_heap_t *heap,
|
|
const char *name, ulint mtype,
|
|
ulint prtype, ulint len, ulint pos,
|
|
ulint num_base);
|
|
|
|
/** Adds a stored column definition to a table.
|
|
@param[in,out] table table
|
|
@param[in] num_base number of base columns. */
|
|
void dict_mem_table_add_s_col(dict_table_t *table, ulint num_base);
|
|
|
|
/** Renames a column of a table in the data dictionary cache. */
|
|
void dict_mem_table_col_rename(dict_table_t *table, /*!< in/out: table */
|
|
ulint nth_col, /*!< in: column index */
|
|
const char *from, /*!< in: old column name */
|
|
const char *to, /*!< in: new column name */
|
|
bool is_virtual);
|
|
/*!< in: if this is a virtual column */
|
|
|
|
/** This function poplulates a dict_index_t index memory structure with
|
|
supplied information.
|
|
@param[out] index index to be filled
|
|
@param[in] heap memory heap
|
|
@param[in] table_name table name
|
|
@param[in] index_name index name
|
|
@param[in] space space where the index tree is placed, the
|
|
clustered type ignored if the index is of the
|
|
clustered type
|
|
@param[in] type DICT_UNIQUE, DICT_CLUSTERED, ... ORed
|
|
@param[in] n_fields number of fields */
|
|
UNIV_INLINE
|
|
void dict_mem_fill_index_struct(dict_index_t *index, mem_heap_t *heap,
|
|
const char *table_name, const char *index_name,
|
|
ulint space, ulint type, ulint n_fields);
|
|
|
|
/** Frees an index memory object. */
|
|
void dict_mem_index_free(dict_index_t *index); /*!< in: index */
|
|
/** Creates and initializes a foreign constraint memory object.
|
|
@return own: foreign constraint struct */
|
|
dict_foreign_t *dict_mem_foreign_create(void);
|
|
|
|
/** 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 the heap and set to lower case. */
|
|
void dict_mem_foreign_table_name_lookup_set(
|
|
dict_foreign_t *foreign, /*!< in/out: foreign struct */
|
|
ibool do_alloc); /*!< in: is an alloc needed */
|
|
|
|
/** 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 the heap and set to lower case. */
|
|
void dict_mem_referenced_table_name_lookup_set(
|
|
dict_foreign_t *foreign, /*!< in/out: foreign struct */
|
|
ibool do_alloc); /*!< in: is an alloc needed */
|
|
|
|
/** Fills the dependent virtual columns in a set.
|
|
Reason for being dependent are
|
|
1) FK can be present on base column of virtual columns
|
|
2) FK can be present on column which is a part of virtual index
|
|
@param[in,out] foreign foreign key information. */
|
|
void dict_mem_foreign_fill_vcol_set(dict_foreign_t *foreign);
|
|
|
|
/** Fill virtual columns set in each fk constraint present in the table.
|
|
@param[in,out] table innodb table object. */
|
|
void dict_mem_table_fill_foreign_vcol_set(dict_table_t *table);
|
|
|
|
/** Free the vcol_set from all foreign key constraint on the table.
|
|
@param[in,out] table innodb table object. */
|
|
void dict_mem_table_free_foreign_vcol_set(dict_table_t *table);
|
|
|
|
/** 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 */
|
|
char *dict_mem_create_temporary_tablename(mem_heap_t *heap, const char *dbtab,
|
|
table_id_t id);
|
|
|
|
/** Initialize dict memory variables */
|
|
void dict_mem_init(void);
|
|
|
|
/** SQL identifier name wrapper for pretty-printing */
|
|
class id_name_t {
|
|
public:
|
|
/** Default constructor */
|
|
id_name_t() : m_name() {}
|
|
/** Constructor
|
|
@param[in] name identifier to assign */
|
|
explicit id_name_t(const char *name) : m_name(name) {}
|
|
|
|
/** Assignment operator
|
|
@param[in] name identifier to assign */
|
|
id_name_t &operator=(const char *name) {
|
|
m_name = name;
|
|
return (*this);
|
|
}
|
|
|
|
/** Implicit type conversion
|
|
@return the name */
|
|
operator const char *() const { return (m_name); }
|
|
|
|
/** Explicit type conversion
|
|
@return the name */
|
|
const char *operator()() const { return (m_name); }
|
|
|
|
private:
|
|
/** The name in internal representation */
|
|
const char *m_name;
|
|
};
|
|
|
|
/** Table name wrapper for pretty-printing */
|
|
struct table_name_t {
|
|
/** The name in internal representation */
|
|
char *m_name;
|
|
};
|
|
|
|
/** Data structure for default value of a column in a table */
|
|
struct dict_col_default_t {
|
|
/** Pointer to the column itself */
|
|
dict_col_t *col;
|
|
/** Default value in bytes */
|
|
byte *value;
|
|
/** Length of default value */
|
|
size_t len;
|
|
};
|
|
|
|
/** Data structure for a column in a table */
|
|
struct dict_col_t {
|
|
/*----------------------*/
|
|
/** The following are copied from dtype_t,
|
|
so that all bit-fields can be packed tightly. */
|
|
/* @{ */
|
|
|
|
/** Default value when this column was added instantly.
|
|
If this is not a instantly added column then this is nullptr. */
|
|
dict_col_default_t *instant_default;
|
|
|
|
unsigned prtype : 32; /*!< precise type; MySQL data
|
|
type, charset code, flags to
|
|
indicate nullability,
|
|
signedness, whether this is a
|
|
binary string, whether this is
|
|
a true VARCHAR where MySQL
|
|
uses 2 bytes to store the length */
|
|
unsigned mtype : 8; /*!< main data type */
|
|
|
|
/* the remaining fields do not affect alphabetical ordering: */
|
|
|
|
unsigned len : 16; /*!< length; for MySQL data this
|
|
is field->pack_length(),
|
|
except that for a >= 5.0.3
|
|
type true VARCHAR this is the
|
|
maximum byte length of the
|
|
string data (in addition to
|
|
the string, MySQL uses 1 or 2
|
|
bytes to store the string length) */
|
|
|
|
unsigned mbminmaxlen : 5; /*!< minimum and maximum length of a
|
|
character, in bytes;
|
|
DATA_MBMINMAXLEN(mbminlen,mbmaxlen);
|
|
mbminlen=DATA_MBMINLEN(mbminmaxlen);
|
|
mbmaxlen=DATA_MBMINLEN(mbminmaxlen) */
|
|
/*----------------------*/
|
|
/* End of definitions copied from dtype_t */
|
|
/* @} */
|
|
|
|
unsigned ind : 10; /*!< table column position
|
|
(starting from 0) */
|
|
unsigned ord_part : 1; /*!< nonzero if this column
|
|
appears in the ordering fields
|
|
of an index */
|
|
unsigned max_prefix : 12; /*!< maximum index prefix length on
|
|
this column. Our current max limit is
|
|
3072 (REC_VERSION_56_MAX_INDEX_COL_LEN)
|
|
bytes. */
|
|
|
|
/** Returns the minimum size of the column.
|
|
@return minimum size */
|
|
ulint get_min_size() const {
|
|
return (dtype_get_min_size_low(mtype, prtype, len, mbminmaxlen));
|
|
}
|
|
|
|
/** Returns the maximum size of the column.
|
|
@return maximum size */
|
|
ulint get_max_size() const { return (dtype_get_max_size_low(mtype, len)); }
|
|
|
|
/** Check if a column is a virtual column
|
|
@return true if it is a virtual column, false otherwise */
|
|
bool is_virtual() const { return (prtype & DATA_VIRTUAL); }
|
|
|
|
/** Check if a column is a multi-value virtual column
|
|
@return true if it is a multi-value virtual column, false otherwise */
|
|
bool is_multi_value() const { return ((prtype & DATA_MULTI_VALUE) != 0); }
|
|
|
|
/** Check if a column is nullable
|
|
@return true if it is nullable, otherwise false */
|
|
bool is_nullable() const { return ((prtype & DATA_NOT_NULL) == 0); }
|
|
|
|
/** Gets the column data type.
|
|
@param[out] type data type */
|
|
void copy_type(dtype_t *type) const {
|
|
ut_ad(type != NULL);
|
|
|
|
type->mtype = mtype;
|
|
type->prtype = prtype;
|
|
type->len = len;
|
|
type->mbminmaxlen = mbminmaxlen;
|
|
}
|
|
|
|
/** Gets the minimum number of bytes per character.
|
|
@return minimum multi-byte char size, in bytes */
|
|
ulint get_mbminlen() const { return (DATA_MBMINLEN(mbminmaxlen)); }
|
|
|
|
/** Gets the maximum number of bytes per character.
|
|
@return maximum multi-byte char size, in bytes */
|
|
ulint get_mbmaxlen() const { return (DATA_MBMAXLEN(mbminmaxlen)); }
|
|
|
|
/** Sets the minimum and maximum number of bytes per character.
|
|
@param[in] mbminlen minimum multi byte character size, in bytes
|
|
@param[in] mbmaxlen mAXimum multi-byte character size, in bytes */
|
|
void set_mbminmaxlen(ulint mbminlen, ulint mbmaxlen) {
|
|
ut_ad(mbminlen < DATA_MBMAX);
|
|
ut_ad(mbmaxlen < DATA_MBMAX);
|
|
ut_ad(mbminlen <= mbmaxlen);
|
|
|
|
mbminmaxlen = DATA_MBMINMAXLEN(mbminlen, mbmaxlen);
|
|
}
|
|
|
|
/** Returns the size of a fixed size column, 0 if not a fixed size column.
|
|
@param[in] comp nonzero=ROW_FORMAT=COMPACT
|
|
@return fixed size, or 0 */
|
|
ulint get_fixed_size(ulint comp) const {
|
|
return (dtype_get_fixed_size_low(mtype, prtype, len, mbminmaxlen, comp));
|
|
}
|
|
|
|
/** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a column.
|
|
For fixed length types it is the fixed length of the type, otherwise 0.
|
|
@param[in] comp nonzero=ROW_FORMAT=COMPACT
|
|
@return SQL null storage size in ROW_FORMAT=REDUNDANT */
|
|
ulint get_null_size(ulint comp) const { return (get_fixed_size(comp)); }
|
|
|
|
/** Check whether the col is used in spatial index or regular index.
|
|
@return spatial status */
|
|
spatial_status_t get_spatial_status() const {
|
|
spatial_status_t spatial_status = SPATIAL_NONE;
|
|
|
|
/* Column is not a part of any index. */
|
|
if (!ord_part) {
|
|
return (spatial_status);
|
|
}
|
|
|
|
if (DATA_GEOMETRY_MTYPE(mtype)) {
|
|
if (max_prefix == 0) {
|
|
spatial_status = SPATIAL_ONLY;
|
|
} else {
|
|
/* Any regular index on a geometry column
|
|
should have a prefix. */
|
|
spatial_status = SPATIAL_MIXED;
|
|
}
|
|
}
|
|
|
|
return (spatial_status);
|
|
}
|
|
|
|
/** Set default value
|
|
@param[in] value Default value
|
|
@param[in] length Default value length
|
|
@param[in,out] heap Heap to allocate memory */
|
|
void set_default(const byte *value, size_t length, mem_heap_t *heap);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Assert that a column and a data type match.
|
|
param[in] type data type
|
|
@return true */
|
|
bool assert_equal(const dtype_t *type) const {
|
|
ut_ad(type);
|
|
|
|
ut_ad(mtype == type->mtype);
|
|
ut_ad(prtype == type->prtype);
|
|
// ut_ad(col->len == type->len);
|
|
#ifndef UNIV_HOTBACKUP
|
|
ut_ad(mbminmaxlen == type->mbminmaxlen);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
return true;
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
};
|
|
|
|
/** Index information put in a list of virtual column structure. Index
|
|
id and virtual column position in the index will be logged.
|
|
There can be multiple entries for a given index, with a different position. */
|
|
struct dict_v_idx_t {
|
|
/** active index on the column */
|
|
dict_index_t *index;
|
|
|
|
/** position in this index */
|
|
ulint nth_field;
|
|
};
|
|
|
|
/** Index list to put in dict_v_col_t */
|
|
typedef std::list<dict_v_idx_t, ut_allocator<dict_v_idx_t>> dict_v_idx_list;
|
|
|
|
/** Data structure for a virtual column in a table */
|
|
struct dict_v_col_t {
|
|
/** column structure */
|
|
dict_col_t m_col;
|
|
|
|
/** array of base column ptr */
|
|
dict_col_t **base_col;
|
|
|
|
/** number of base column */
|
|
ulint num_base;
|
|
|
|
/** column pos in table */
|
|
ulint v_pos;
|
|
|
|
/** Virtual index list, and column position in the index,
|
|
the allocated memory is not from table->heap, nor it is
|
|
tracked by dict_sys->size */
|
|
dict_v_idx_list *v_indexes;
|
|
};
|
|
|
|
/** Data structure for newly added virtual column in a table */
|
|
struct dict_add_v_col_t {
|
|
/** number of new virtual column */
|
|
ulint n_v_col;
|
|
|
|
/** column structures */
|
|
const dict_v_col_t *v_col;
|
|
|
|
/** new col names */
|
|
const char **v_col_name;
|
|
};
|
|
|
|
/** Data structure for a stored column in a table. */
|
|
struct dict_s_col_t {
|
|
/** Stored column ptr */
|
|
dict_col_t *m_col;
|
|
/** array of base col ptr */
|
|
dict_col_t **base_col;
|
|
/** number of base columns */
|
|
ulint num_base;
|
|
/** column pos in table */
|
|
ulint s_pos;
|
|
};
|
|
|
|
/** list to put stored column for dict_table_t */
|
|
typedef std::list<dict_s_col_t, ut_allocator<dict_s_col_t>> dict_s_col_list;
|
|
|
|
/** @brief DICT_ANTELOPE_MAX_INDEX_COL_LEN is measured in bytes and
|
|
is the maximum indexed column length (or indexed prefix length) in
|
|
ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT. Also, in any format,
|
|
any fixed-length field that is longer than this will be encoded as
|
|
a variable-length field.
|
|
|
|
It is set to 3*256, so that one can create a column prefix index on
|
|
256 characters of a TEXT or VARCHAR column also in the UTF-8
|
|
charset. In that charset, a character may take at most 3 bytes. This
|
|
constant MUST NOT BE CHANGED, or the compatibility of InnoDB data
|
|
files would be at risk! */
|
|
#define DICT_ANTELOPE_MAX_INDEX_COL_LEN REC_ANTELOPE_MAX_INDEX_COL_LEN
|
|
|
|
/** Find out maximum indexed column length by its table format.
|
|
For ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT, the maximum
|
|
field length is REC_ANTELOPE_MAX_INDEX_COL_LEN - 1 (767). For
|
|
ROW_FORMAT=COMPRESSED and ROW_FORMAT=DYNAMIC, the length could
|
|
be REC_VERSION_56_MAX_INDEX_COL_LEN (3072) bytes */
|
|
#define DICT_MAX_FIELD_LEN_BY_FORMAT(table) \
|
|
(dict_table_has_atomic_blobs(table) ? REC_VERSION_56_MAX_INDEX_COL_LEN \
|
|
: REC_ANTELOPE_MAX_INDEX_COL_LEN - 1)
|
|
|
|
#define DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags) \
|
|
(DICT_TF_HAS_ATOMIC_BLOBS(flags) ? REC_VERSION_56_MAX_INDEX_COL_LEN \
|
|
: REC_ANTELOPE_MAX_INDEX_COL_LEN - 1)
|
|
|
|
/** Defines the maximum fixed length column size */
|
|
#define DICT_MAX_FIXED_COL_LEN DICT_ANTELOPE_MAX_INDEX_COL_LEN
|
|
|
|
/** Data structure for a field in an index */
|
|
struct dict_field_t {
|
|
dict_field_t() : col(nullptr), prefix_len(0), fixed_len(0), is_ascending(0) {}
|
|
|
|
dict_col_t *col; /*!< pointer to the table column */
|
|
id_name_t name; /*!< name of the column */
|
|
unsigned prefix_len : 12; /*!< 0 or the length of the column
|
|
prefix in bytes in a MySQL index of
|
|
type, e.g., INDEX (textcol(25));
|
|
must be smaller than
|
|
DICT_MAX_FIELD_LEN_BY_FORMAT;
|
|
NOTE that in the UTF-8 charset, MySQL
|
|
sets this to (mbmaxlen * the prefix len)
|
|
in UTF-8 chars */
|
|
unsigned fixed_len : 10; /*!< 0 or the fixed length of the
|
|
column if smaller than
|
|
DICT_ANTELOPE_MAX_INDEX_COL_LEN */
|
|
unsigned is_ascending : 1; /*!< 0=DESC, 1=ASC */
|
|
};
|
|
|
|
/** PADDING HEURISTIC BASED ON LINEAR INCREASE OF PADDING TO AVOID
|
|
COMPRESSION FAILURES
|
|
(Note: this is relevant only for compressed indexes)
|
|
GOAL: Avoid compression failures by maintaining information about the
|
|
compressibility of data. If data is not very compressible then leave
|
|
some extra space 'padding' in the uncompressed page making it more
|
|
likely that compression of less than fully packed uncompressed page will
|
|
succeed.
|
|
|
|
This padding heuristic works by increasing the pad linearly until the
|
|
desired failure rate is reached. A "round" is a fixed number of
|
|
compression operations.
|
|
After each round, the compression failure rate for that round is
|
|
computed. If the failure rate is too high, then padding is incremented
|
|
by a fixed value, otherwise it's left intact.
|
|
If the compression failure is lower than the desired rate for a fixed
|
|
number of consecutive rounds, then the padding is decreased by a fixed
|
|
value. This is done to prevent overshooting the padding value,
|
|
and to accommodate the possible change in data compressibility. */
|
|
|
|
/** Number of zip ops in one round. */
|
|
#define ZIP_PAD_ROUND_LEN (128)
|
|
|
|
/** Number of successful rounds after which the padding is decreased */
|
|
#define ZIP_PAD_SUCCESSFUL_ROUND_LIMIT (5)
|
|
|
|
/** Amount by which padding is increased. */
|
|
#define ZIP_PAD_INCR (128)
|
|
|
|
/** Percentage of compression failures that are allowed in a single
|
|
round */
|
|
extern ulong zip_failure_threshold_pct;
|
|
|
|
/** Maximum percentage of a page that can be allowed as a pad to avoid
|
|
compression failures */
|
|
extern ulong zip_pad_max;
|
|
|
|
/** Data structure to hold information about about how much space in
|
|
an uncompressed page should be left as padding to avoid compression
|
|
failures. This estimate is based on a self-adapting heuristic. */
|
|
struct zip_pad_info_t {
|
|
SysMutex *mutex; /*!< mutex protecting the info */
|
|
ulint pad; /*!< number of bytes used as pad */
|
|
ulint success; /*!< successful compression ops during
|
|
current round */
|
|
ulint failure; /*!< failed compression ops during
|
|
current round */
|
|
ulint n_rounds; /*!< number of currently successful
|
|
rounds */
|
|
#ifndef UNIV_HOTBACKUP
|
|
volatile os_once::state_t mutex_created;
|
|
/*!< Creation state of mutex member */
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
};
|
|
|
|
/** If key is fixed length key then cache the record offsets on first
|
|
computation. This will help save computation cycle that generate same
|
|
redundant data. */
|
|
class rec_cache_t {
|
|
public:
|
|
/** Constructor */
|
|
rec_cache_t()
|
|
: rec_size(),
|
|
offsets(),
|
|
sz_of_offsets(),
|
|
fixed_len_key(),
|
|
offsets_cached(),
|
|
key_has_null_cols() {
|
|
/* Do Nothing. */
|
|
}
|
|
|
|
public:
|
|
/** Record size. (for fixed length key record size is constant) */
|
|
ulint rec_size;
|
|
|
|
/** Holds reference to cached offsets for record. */
|
|
ulint *offsets;
|
|
|
|
/** Size of offset array */
|
|
uint32_t sz_of_offsets;
|
|
|
|
/** If true, then key is fixed length key. */
|
|
bool fixed_len_key;
|
|
|
|
/** If true, then offset has been cached for re-use. */
|
|
bool offsets_cached;
|
|
|
|
/** If true, then key part can have columns that can take
|
|
NULL values. */
|
|
bool key_has_null_cols;
|
|
};
|
|
|
|
/** Cache position of last inserted or selected record by caching record
|
|
and holding reference to the block where record resides.
|
|
Note: We don't commit mtr and hold it beyond a transaction lifetime as this is
|
|
a special case (intrinsic table) that are not shared accross connection. */
|
|
class last_ops_cur_t {
|
|
public:
|
|
/** Constructor */
|
|
last_ops_cur_t() : rec(), block(), mtr(), disable_caching(), invalid() {
|
|
/* Do Nothing. */
|
|
}
|
|
|
|
/* Commit mtr and re-initialize cache record and block to NULL. */
|
|
void release() {
|
|
if (mtr.is_active()) {
|
|
mtr_commit(&mtr);
|
|
}
|
|
rec = NULL;
|
|
block = NULL;
|
|
invalid = false;
|
|
}
|
|
|
|
public:
|
|
/** last inserted/selected record. */
|
|
rec_t *rec;
|
|
|
|
/** block where record reside. */
|
|
buf_block_t *block;
|
|
|
|
/** active mtr that will be re-used for next insert/select. */
|
|
mtr_t mtr;
|
|
|
|
/** disable caching. (disabled when table involves blob/text.) */
|
|
bool disable_caching;
|
|
|
|
/** If index structure is undergoing structural change viz.
|
|
split then invalidate the cached position as it would be no more
|
|
remain valid. Will be re-cached on post-split insert. */
|
|
bool invalid;
|
|
};
|
|
|
|
/** "GEN_CLUST_INDEX" is the name reserved for InnoDB default
|
|
system clustered index when there is no primary key. */
|
|
const char innobase_index_reserve_name[] = "GEN_CLUST_INDEX";
|
|
|
|
namespace dd {
|
|
class Spatial_reference_system;
|
|
}
|
|
|
|
/** Data structure for an index. Most fields will be
|
|
initialized to 0, NULL or FALSE in dict_mem_index_create(). */
|
|
struct dict_index_t {
|
|
space_index_t id; /*!< id of the index */
|
|
mem_heap_t *heap; /*!< memory heap */
|
|
id_name_t name; /*!< index name */
|
|
const char *table_name; /*!< table name */
|
|
dict_table_t *table; /*!< back pointer to table */
|
|
unsigned space : 32;
|
|
/*!< space where the index tree is placed */
|
|
unsigned page : 32; /*!< index tree root page number */
|
|
unsigned merge_threshold : 6;
|
|
/*!< In the pessimistic delete, if the page
|
|
data size drops below this limit in percent,
|
|
merging it to a neighbor is tried */
|
|
#define DICT_INDEX_MERGE_THRESHOLD_DEFAULT 50
|
|
unsigned type : DICT_IT_BITS;
|
|
/*!< index type (DICT_CLUSTERED, DICT_UNIQUE,
|
|
DICT_IBUF, DICT_CORRUPT) */
|
|
#define MAX_KEY_LENGTH_BITS 12
|
|
unsigned trx_id_offset : MAX_KEY_LENGTH_BITS;
|
|
/*!< position of the trx id column
|
|
in a clustered index record, if the fields
|
|
before it are known to be of a fixed size,
|
|
0 otherwise */
|
|
#if (1 << MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
|
|
#error(1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
|
|
#endif
|
|
unsigned n_user_defined_cols : 10;
|
|
/*!< number of columns the user defined to
|
|
be in the index: in the internal
|
|
representation we add more columns */
|
|
unsigned allow_duplicates : 1;
|
|
/*!< if true, allow duplicate values
|
|
even if index is created with unique
|
|
constraint */
|
|
unsigned nulls_equal : 1;
|
|
/*!< if true, SQL NULL == SQL NULL */
|
|
unsigned disable_ahi : 1;
|
|
/*!< if true, then disable AHI. Currently
|
|
limited to intrinsic temporary table and SDI
|
|
table as index id is not unique for such table
|
|
which is one of the validation criterion for
|
|
ahi. */
|
|
unsigned n_uniq : 10; /*!< number of fields from the beginning
|
|
which are enough to determine an index
|
|
entry uniquely */
|
|
unsigned n_def : 10; /*!< number of fields defined so far */
|
|
unsigned n_fields : 10; /*!< number of fields in the index */
|
|
unsigned n_nullable : 10; /*!< number of nullable fields */
|
|
unsigned n_instant_nullable : 10;
|
|
/*!< number of nullable fields before first
|
|
instant ADD COLUMN applied to this table.
|
|
This is valid only when has_instant_cols() is true */
|
|
unsigned cached : 1; /*!< TRUE if the index object is in the
|
|
dictionary cache */
|
|
unsigned to_be_dropped : 1;
|
|
/*!< TRUE if the index is to be dropped;
|
|
protected by dict_operation_lock */
|
|
unsigned online_status : 2;
|
|
/*!< enum online_index_status.
|
|
Transitions from ONLINE_INDEX_COMPLETE (to
|
|
ONLINE_INDEX_CREATION) are protected
|
|
by dict_operation_lock and
|
|
dict_sys->mutex. Other changes are
|
|
protected by index->lock. */
|
|
unsigned uncommitted : 1;
|
|
/*!< a flag that is set for secondary indexes
|
|
that have not been committed to the
|
|
data dictionary yet */
|
|
unsigned instant_cols : 1;
|
|
/*!< TRUE if the index is clustered index and it has some
|
|
instant columns */
|
|
uint32_t srid; /* spatial reference id */
|
|
bool srid_is_valid;
|
|
/* says whether SRID is valid - it cane be
|
|
undefined */
|
|
std::unique_ptr<dd::Spatial_reference_system> rtr_srs;
|
|
/*!< Cached spatial reference system dictionary
|
|
entry used by R-tree indexes. */
|
|
|
|
#ifdef UNIV_DEBUG
|
|
uint32_t magic_n; /*!< magic number */
|
|
/** Value of dict_index_t::magic_n */
|
|
#define DICT_INDEX_MAGIC_N 76789786
|
|
#endif
|
|
dict_field_t *fields; /*!< array of field descriptions */
|
|
#ifndef UNIV_HOTBACKUP
|
|
st_mysql_ftparser *parser; /*!< fulltext parser plugin */
|
|
bool is_ngram;
|
|
/*!< true if it's ngram parser */
|
|
bool has_new_v_col;
|
|
/*!< whether it has a newly added virtual
|
|
column in ALTER */
|
|
bool hidden; /*!< if the index is an hidden index */
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
UT_LIST_NODE_T(dict_index_t)
|
|
indexes; /*!< list of indexes of the table */
|
|
btr_search_t *search_info;
|
|
/*!< info used in optimistic searches */
|
|
#ifndef UNIV_HOTBACKUP
|
|
row_log_t *online_log;
|
|
/*!< the log of modifications
|
|
during online index creation;
|
|
valid when online_status is
|
|
ONLINE_INDEX_CREATION */
|
|
/*----------------------*/
|
|
/** Statistics for query optimization */
|
|
/* @{ */
|
|
ib_uint64_t *stat_n_diff_key_vals;
|
|
/*!< approximate number of different
|
|
key values for this index, for each
|
|
n-column prefix where 1 <= n <=
|
|
dict_get_n_unique(index) (the array is
|
|
indexed from 0 to n_uniq-1); we
|
|
periodically calculate new
|
|
estimates */
|
|
ib_uint64_t *stat_n_sample_sizes;
|
|
/*!< number of pages that were sampled
|
|
to calculate each of stat_n_diff_key_vals[],
|
|
e.g. stat_n_sample_sizes[3] pages were sampled
|
|
to get the number stat_n_diff_key_vals[3]. */
|
|
ib_uint64_t *stat_n_non_null_key_vals;
|
|
/* approximate number of non-null key values
|
|
for this index, for each column where
|
|
1 <= n <= dict_get_n_unique(index) (the array
|
|
is indexed from 0 to n_uniq-1); This
|
|
is used when innodb_stats_method is
|
|
"nulls_ignored". */
|
|
ulint stat_index_size;
|
|
/*!< approximate index size in
|
|
database pages */
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
ulint stat_n_leaf_pages;
|
|
/*!< approximate number of leaf pages in the
|
|
index tree */
|
|
/* @} */
|
|
last_ops_cur_t *last_ins_cur;
|
|
/*!< cache the last insert position.
|
|
Currently limited to auto-generated
|
|
clustered index on intrinsic table only. */
|
|
last_ops_cur_t *last_sel_cur;
|
|
/*!< cache the last selected position
|
|
Currently limited to intrinsic table only. */
|
|
rec_cache_t rec_cache;
|
|
/*!< cache the field that needs to be
|
|
re-computed on each insert.
|
|
Limited to intrinsic table as this is common
|
|
share and can't be used without protection
|
|
if table is accessible to multiple-threads. */
|
|
rtr_ssn_t rtr_ssn; /*!< Node sequence number for RTree */
|
|
rtr_info_track_t *rtr_track; /*!< tracking all R-Tree search cursors */
|
|
trx_id_t trx_id; /*!< id of the transaction that created this
|
|
index, or 0 if the index existed
|
|
when InnoDB was started up */
|
|
zip_pad_info_t zip_pad; /*!< Information about state of
|
|
compression failures and successes */
|
|
rw_lock_t lock; /*!< read-write lock protecting the
|
|
upper levels of the index tree */
|
|
bool fill_dd; /*!< Flag whether need to fill dd tables
|
|
when it's a fulltext index. */
|
|
|
|
/*!< lIZARD: The transaction description of the creator trx.
|
|
Only used to find the real SCN of the creator trx.
|
|
uba: Point to the real undo header, no matterr what the state
|
|
is, because the real state of the transaction is stored
|
|
in undo header.
|
|
scn: The real scn of the creator trx. It's always set as the
|
|
actual value from undo header when it is SCN_NULL */
|
|
txn_index_t txn;
|
|
|
|
|
|
/** Determine if the index has been committed to the
|
|
data dictionary.
|
|
@return whether the index definition has been committed */
|
|
bool is_committed() const {
|
|
ut_ad(!uncommitted || !(type & DICT_CLUSTERED));
|
|
return (UNIV_LIKELY(!uncommitted));
|
|
}
|
|
|
|
/** Flag an index committed or uncommitted.
|
|
@param[in] committed whether the index is committed */
|
|
void set_committed(bool committed) {
|
|
ut_ad(!to_be_dropped);
|
|
ut_ad(committed || !(type & DICT_CLUSTERED));
|
|
uncommitted = !committed;
|
|
}
|
|
|
|
/** Get the next index.
|
|
@return next index
|
|
@retval NULL if this was the last index */
|
|
const dict_index_t *next() const {
|
|
const dict_index_t *next = UT_LIST_GET_NEXT(indexes, this);
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
return (next);
|
|
}
|
|
/** Get the next index.
|
|
@return next index
|
|
@retval NULL if this was the last index */
|
|
dict_index_t *next() {
|
|
return (const_cast<dict_index_t *>(
|
|
const_cast<const dict_index_t *>(this)->next()));
|
|
}
|
|
|
|
/** Check whether the index is corrupted.
|
|
@return true if index is corrupted, otherwise false */
|
|
bool is_corrupted() const {
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
return (type & DICT_CORRUPT);
|
|
}
|
|
|
|
/* Check whether the index is the clustered index
|
|
@return nonzero for clustered index, zero for other indexes */
|
|
|
|
bool is_clustered() const {
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
return (type & DICT_CLUSTERED);
|
|
}
|
|
|
|
/** Check whether the index is the multi-value index
|
|
@return nonzero for multi-value index, zero for other indexes */
|
|
bool is_multi_value() const {
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
return (type & DICT_MULTI_VALUE);
|
|
}
|
|
|
|
/** Returns the minimum data size of an index record.
|
|
@return minimum data size in bytes */
|
|
ulint get_min_size() const {
|
|
ulint size = 0;
|
|
|
|
for (unsigned i = 0; i < n_fields; i++) {
|
|
size += get_col(i)->get_min_size();
|
|
}
|
|
|
|
return (size);
|
|
}
|
|
|
|
/** Check whether index can be used by transaction
|
|
@param[in] trx transaction*/
|
|
bool is_usable(const trx_t *trx);
|
|
|
|
/** Check whether index can be used by an as-of query
|
|
@param[in] trx transaction
|
|
@param[in] as_of_scn as of scn
|
|
@param[in] as_of_gcn as of gcn */
|
|
bool is_usable_as_of(const trx_t *trx, const scn_t scn, const gcn_t gcn);
|
|
|
|
/** Check whether index has any instantly added columns
|
|
@return true if this is instant affected, otherwise false */
|
|
bool has_instant_cols() const { return (instant_cols); }
|
|
|
|
/** Returns the number of nullable fields before specified
|
|
nth field
|
|
@param[in] nth nth field to check */
|
|
uint32_t get_n_nullable_before(uint32_t nth) const {
|
|
uint32_t nullable = n_nullable;
|
|
|
|
ut_ad(nth <= n_fields);
|
|
|
|
for (uint32_t i = nth; i < n_fields; ++i) {
|
|
if (get_field(i)->col->is_nullable()) {
|
|
--nullable;
|
|
}
|
|
}
|
|
|
|
return (nullable);
|
|
}
|
|
|
|
/** Returns the number of fields before first instant ADD COLUMN */
|
|
uint32_t get_instant_fields() const;
|
|
|
|
/** 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.
|
|
@param[in] name_arg column name
|
|
@param[in] prefix_len 0 or the column prefix length in a MySQL index
|
|
like INDEX (textcol(25))
|
|
@param[in] is_ascending true=ASC, false=DESC */
|
|
void add_field(const char *name_arg, ulint prefix_len, bool is_ascending) {
|
|
dict_field_t *field;
|
|
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
n_def++;
|
|
|
|
field = get_field(n_def - 1);
|
|
|
|
field->name = name_arg;
|
|
field->prefix_len = (unsigned int)prefix_len;
|
|
field->is_ascending = is_ascending;
|
|
}
|
|
|
|
/** Gets the nth field of an index.
|
|
@param[in] pos position of field
|
|
@return pointer to field object */
|
|
dict_field_t *get_field(ulint pos) const {
|
|
ut_ad(pos < n_def);
|
|
ut_ad(magic_n == DICT_INDEX_MAGIC_N);
|
|
|
|
return (fields + pos);
|
|
}
|
|
|
|
/** Gets pointer to the nth column in an index.
|
|
@param[in] pos position of the field
|
|
@return column */
|
|
const dict_col_t *get_col(ulint pos) const { return (get_field(pos)->col); }
|
|
|
|
/** Gets the column number the nth field in an index.
|
|
@param[in] pos position of the field
|
|
@return column number */
|
|
ulint get_col_no(ulint pos) const;
|
|
|
|
/** Returns the position of a system column in an index.
|
|
@param[in] type DATA_ROW_ID, ...
|
|
@return position, ULINT_UNDEFINED if not contained */
|
|
ulint get_sys_col_pos(ulint type) const;
|
|
|
|
/** Looks for column n in an index.
|
|
@param[in] n column number
|
|
@param[in] inc_prefix true=consider column prefixes too
|
|
@param[in] is_virtual true==virtual column
|
|
@return position in internal representation of the index;
|
|
ULINT_UNDEFINED if not contained */
|
|
ulint get_col_pos(ulint n, bool inc_prefix = false,
|
|
bool is_virtual = false) const;
|
|
|
|
/** Get the default value of nth field and its length if exists.
|
|
If not exists, both the return value is nullptr and length is 0.
|
|
@param[in] nth nth field to get
|
|
@param[in,out] length length of the default value
|
|
@return the default value data of nth field */
|
|
const byte *get_nth_default(ulint nth, ulint *length) const {
|
|
ut_ad(nth < n_fields);
|
|
ut_ad(get_instant_fields() <= nth);
|
|
const dict_col_t *col = get_col(nth);
|
|
if (col->instant_default == nullptr) {
|
|
*length = 0;
|
|
return (nullptr);
|
|
}
|
|
|
|
*length = col->instant_default->len;
|
|
ut_ad(*length == 0 || *length == UNIV_SQL_NULL ||
|
|
col->instant_default->value != nullptr);
|
|
return (col->instant_default->value);
|
|
}
|
|
|
|
/** Sets srid and srid_is_valid values
|
|
@param[in] srid_value value of SRID, may be garbage
|
|
if srid_is_valid_value = false
|
|
@param[in] srid_is_valid_value value of srid_is_valid */
|
|
void fill_srid_value(uint32_t srid_value, bool srid_is_valid_value) {
|
|
srid_is_valid = srid_is_valid_value;
|
|
srid = srid_value;
|
|
}
|
|
|
|
/** Check if the underlying table is compressed.
|
|
@return true if compressed, false otherwise. */
|
|
bool is_compressed() const;
|
|
|
|
/** Check if a multi-value index is built on specified multi-value
|
|
virtual column. Please note that there could be only one multi-value
|
|
virtual column on the multi-value index, but not necessary the first
|
|
field of the index.
|
|
@param[in] mv_col multi-value virtual column
|
|
@return non-zero means the column is on the index and this is the
|
|
nth position of the column, zero means it's not on the index */
|
|
uint32_t has_multi_value_col(const dict_v_col_t *mv_col) const {
|
|
ut_ad(is_multi_value());
|
|
for (uint32_t i = 0; i < n_fields; ++i) {
|
|
const dict_col_t *col = get_col(i);
|
|
if (mv_col->m_col.ind == col->ind) {
|
|
return (i + 1);
|
|
}
|
|
|
|
/* Only one multi-value field, if not match then no match. */
|
|
if (col->is_multi_value()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
};
|
|
|
|
/** The status of online index creation */
|
|
enum online_index_status {
|
|
/** the index is complete and ready for access */
|
|
ONLINE_INDEX_COMPLETE = 0,
|
|
/** the index is being created, online
|
|
(allowing concurrent modifications) */
|
|
ONLINE_INDEX_CREATION,
|
|
/** secondary index creation was aborted and the index
|
|
should be dropped as soon as index->table->n_ref_count reaches 0,
|
|
or online table rebuild was aborted and the clustered index
|
|
of the original table should soon be restored to
|
|
ONLINE_INDEX_COMPLETE */
|
|
ONLINE_INDEX_ABORTED,
|
|
/** the online index creation was aborted, the index was
|
|
dropped from the data dictionary and the tablespace, and it
|
|
should be dropped from the data dictionary cache as soon as
|
|
index->table->n_ref_count reaches 0. */
|
|
ONLINE_INDEX_ABORTED_DROPPED
|
|
};
|
|
|
|
/** Set to store the virtual columns which are affected by Foreign
|
|
key constraint. */
|
|
typedef std::set<dict_v_col_t *, std::less<dict_v_col_t *>,
|
|
ut_allocator<dict_v_col_t *>>
|
|
dict_vcol_set;
|
|
|
|
/** Data structure for a foreign key constraint; an example:
|
|
FOREIGN KEY (A, B) REFERENCES TABLE2 (C, D). Most fields will be
|
|
initialized to 0, NULL or FALSE in dict_mem_foreign_create(). */
|
|
struct dict_foreign_t {
|
|
mem_heap_t *heap; /*!< this object is allocated from
|
|
this memory heap */
|
|
char *id; /*!< id of the constraint as a
|
|
null-terminated string */
|
|
unsigned n_fields : 10; /*!< number of indexes' first fields
|
|
for which the foreign key
|
|
constraint is defined: we allow the
|
|
indexes to contain more fields than
|
|
mentioned in the constraint, as long
|
|
as the first fields are as mentioned */
|
|
unsigned type : 6; /*!< 0 or DICT_FOREIGN_ON_DELETE_CASCADE
|
|
or DICT_FOREIGN_ON_DELETE_SET_NULL */
|
|
char *foreign_table_name; /*!< foreign table name */
|
|
char *foreign_table_name_lookup;
|
|
/*!< foreign table name used for dict lookup */
|
|
dict_table_t *foreign_table; /*!< table where the foreign key is */
|
|
const char **foreign_col_names; /*!< names of the columns in the
|
|
foreign key */
|
|
char *referenced_table_name; /*!< referenced table name */
|
|
char *referenced_table_name_lookup;
|
|
/*!< referenced table name for dict lookup*/
|
|
dict_table_t *referenced_table; /*!< table where the referenced key
|
|
is */
|
|
const char **referenced_col_names; /*!< names of the referenced
|
|
columns in the referenced table */
|
|
dict_index_t *foreign_index; /*!< foreign index; we require that
|
|
both tables contain explicitly defined
|
|
indexes for the constraint: InnoDB
|
|
does not generate new indexes
|
|
implicitly */
|
|
dict_index_t *referenced_index; /*!< referenced index */
|
|
|
|
dict_vcol_set *v_cols; /*!< set of virtual columns affected
|
|
by foreign key constraint. */
|
|
};
|
|
|
|
std::ostream &operator<<(std::ostream &out, const dict_foreign_t &foreign);
|
|
|
|
struct dict_foreign_print {
|
|
dict_foreign_print(std::ostream &out) : m_out(out) {}
|
|
|
|
void operator()(const dict_foreign_t *foreign) { m_out << *foreign; }
|
|
|
|
private:
|
|
std::ostream &m_out;
|
|
};
|
|
|
|
/** Compare two dict_foreign_t objects using their ids. Used in the ordering
|
|
of dict_table_t::foreign_set and dict_table_t::referenced_set. It returns
|
|
true if the first argument is considered to go before the second in the
|
|
strict weak ordering it defines, and false otherwise. */
|
|
struct dict_foreign_compare {
|
|
bool operator()(const dict_foreign_t *lhs, const dict_foreign_t *rhs) const {
|
|
return (ut_strcmp(lhs->id, rhs->id) < 0);
|
|
}
|
|
};
|
|
|
|
/** A function object to find a foreign key with the given index as the
|
|
referenced index. Return the foreign key with matching criteria or NULL */
|
|
struct dict_foreign_with_index {
|
|
dict_foreign_with_index(const dict_index_t *index) : m_index(index) {}
|
|
|
|
bool operator()(const dict_foreign_t *foreign) const {
|
|
return (foreign->referenced_index == m_index);
|
|
}
|
|
|
|
const dict_index_t *m_index;
|
|
};
|
|
|
|
/* A function object to check if the foreign constraint is between different
|
|
tables. Returns true if foreign key constraint is between different tables,
|
|
false otherwise. */
|
|
struct dict_foreign_different_tables {
|
|
bool operator()(const dict_foreign_t *foreign) const {
|
|
return (foreign->foreign_table != foreign->referenced_table);
|
|
}
|
|
};
|
|
|
|
/** A function object to check if the foreign key constraint has the same
|
|
name as given. If the full name of the foreign key constraint doesn't match,
|
|
then, check if removing the database name from the foreign key constraint
|
|
matches. Return true if it matches, false otherwise. */
|
|
struct dict_foreign_matches_id {
|
|
dict_foreign_matches_id(const char *id) : m_id(id) {}
|
|
|
|
bool operator()(const dict_foreign_t *foreign) const {
|
|
if (0 == innobase_strcasecmp(foreign->id, m_id)) {
|
|
return (true);
|
|
}
|
|
if (const char *pos = strchr(foreign->id, '/')) {
|
|
if (0 == innobase_strcasecmp(m_id, pos + 1)) {
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
const char *m_id;
|
|
};
|
|
|
|
typedef std::set<dict_foreign_t *, dict_foreign_compare,
|
|
ut_allocator<dict_foreign_t *>>
|
|
dict_foreign_set;
|
|
|
|
std::ostream &operator<<(std::ostream &out, const dict_foreign_set &fk_set);
|
|
|
|
/** Function object to check if a foreign key object is there
|
|
in the given foreign key set or not. It returns true if the
|
|
foreign key is not found, false otherwise */
|
|
struct dict_foreign_not_exists {
|
|
dict_foreign_not_exists(const dict_foreign_set &obj_) : m_foreigns(obj_) {}
|
|
|
|
/* Return true if the given foreign key is not found */
|
|
bool operator()(dict_foreign_t *const &foreign) const {
|
|
return (m_foreigns.find(foreign) == m_foreigns.end());
|
|
}
|
|
|
|
private:
|
|
const dict_foreign_set &m_foreigns;
|
|
};
|
|
|
|
/** 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);
|
|
|
|
/** 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);
|
|
|
|
/** Frees a foreign key struct. */
|
|
inline void dict_foreign_free(
|
|
dict_foreign_t *foreign) /*!< in, own: foreign key struct */
|
|
{
|
|
if (foreign->v_cols != NULL) {
|
|
UT_DELETE(foreign->v_cols);
|
|
}
|
|
|
|
mem_heap_free(foreign->heap);
|
|
}
|
|
|
|
/** The destructor will free all the foreign key constraints in the set
|
|
by calling dict_foreign_free() on each of the foreign key constraints.
|
|
This is used to free the allocated memory when a local set goes out
|
|
of scope. */
|
|
struct dict_foreign_set_free {
|
|
dict_foreign_set_free(const dict_foreign_set &foreign_set)
|
|
: m_foreign_set(foreign_set) {}
|
|
|
|
~dict_foreign_set_free() {
|
|
std::for_each(m_foreign_set.begin(), m_foreign_set.end(),
|
|
dict_foreign_free);
|
|
}
|
|
|
|
const dict_foreign_set &m_foreign_set;
|
|
};
|
|
|
|
/** The flags for ON_UPDATE and ON_DELETE can be ORed; the default is that
|
|
a foreign key constraint is enforced, therefore RESTRICT just means no flag */
|
|
/* @{ */
|
|
#define DICT_FOREIGN_ON_DELETE_CASCADE 1 /*!< ON DELETE CASCADE */
|
|
#define DICT_FOREIGN_ON_DELETE_SET_NULL 2 /*!< ON DELETE SET NULL */
|
|
#define DICT_FOREIGN_ON_UPDATE_CASCADE 4 /*!< ON UPDATE CASCADE */
|
|
#define DICT_FOREIGN_ON_UPDATE_SET_NULL 8 /*!< ON UPDATE SET NULL */
|
|
#define DICT_FOREIGN_ON_DELETE_NO_ACTION 16 /*!< ON DELETE NO ACTION */
|
|
#define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32 /*!< ON UPDATE NO ACTION */
|
|
/* @} */
|
|
|
|
/** Display an identifier.
|
|
@param[in,out] s output stream
|
|
@param[in] id_name SQL identifier (other than table name)
|
|
@return the output stream */
|
|
std::ostream &operator<<(std::ostream &s, const id_name_t &id_name);
|
|
|
|
/** Display a table name.
|
|
@param[in,out] s output stream
|
|
@param[in] table_name table name
|
|
@return the output stream */
|
|
std::ostream &operator<<(std::ostream &s, const table_name_t &table_name);
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** List of locks that different transactions have acquired on a table. This
|
|
list has a list node that is embedded in a nested union/structure. We have to
|
|
generate a specific template for it. */
|
|
|
|
typedef ut_list_base<lock_t, ut_list_node<lock_t> lock_table_t::*>
|
|
table_lock_list_t;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** mysql template structure defined in row0mysql.cc */
|
|
struct mysql_row_templ_t;
|
|
|
|
/** Structure defines template related to virtual columns and
|
|
their base columns */
|
|
struct dict_vcol_templ_t {
|
|
/** number of regular columns */
|
|
ulint n_col;
|
|
|
|
/** number of virtual columns */
|
|
ulint n_v_col;
|
|
|
|
/** array of templates for virtual col and their base columns */
|
|
mysql_row_templ_t **vtempl;
|
|
|
|
/** table's database name */
|
|
std::string db_name;
|
|
|
|
/** table name */
|
|
std::string tb_name;
|
|
|
|
/** share->table_name */
|
|
std::string share_name;
|
|
|
|
/** MySQL record length */
|
|
ulint rec_len;
|
|
|
|
/** default column value if any */
|
|
byte *default_rec;
|
|
};
|
|
|
|
/** The dirty status of tables, used to indicate if a table has some
|
|
dynamic metadata changed to be written back */
|
|
enum table_dirty_status {
|
|
/** Some persistent metadata is now dirty in memory, need to be
|
|
written back to DDTableBuffer table and(or directly to) DD table.
|
|
There could be some exceptions, when it's marked as dirty, but
|
|
the metadata has already been written back to DDTableBuffer.
|
|
For example, if a corrupted index is found and marked as corrupted,
|
|
then it gets dropped. At this time, the dirty_status is still of
|
|
this dirty value. Also a concurrent checkpoint make this bit
|
|
out-of-date for other working threads, which still think the
|
|
status is dirty and write-back is necessary.
|
|
There could be either one row or no row for this table in
|
|
DDTableBuffer table */
|
|
METADATA_DIRTY = 0,
|
|
/** Some persistent metadata is buffered in DDTableBuffer table,
|
|
need to be written back to DD table. There is must be one row in
|
|
DDTableBuffer table for this table */
|
|
METADATA_BUFFERED,
|
|
/** All persistent metadata are up to date. There is no row
|
|
for this table in DDTableBuffer table */
|
|
METADATA_CLEAN
|
|
};
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** A vector to collect prebuilt from different readers working on the same
|
|
temp table */
|
|
typedef std::vector<row_prebuilt_t *> temp_prebuilt_vec;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Data structure for a database table. Most fields will be
|
|
initialized to 0, NULL or FALSE in dict_mem_table_create(). */
|
|
struct dict_table_t {
|
|
/** Check if the table is compressed.
|
|
@return true if compressed, false otherwise. */
|
|
bool is_compressed() const { return (DICT_TF_GET_ZIP_SSIZE(flags) != 0); }
|
|
|
|
/** Get reference count.
|
|
@return current value of n_ref_count */
|
|
inline uint64_t get_ref_count() const;
|
|
|
|
/** Acquire the table handle. */
|
|
inline void acquire();
|
|
|
|
/** Acquire the table handle, with lock() and unlock() the table.
|
|
This function needs to be called for opening table when the table
|
|
is in memory and later the stats information would be initialized */
|
|
inline void acquire_with_lock();
|
|
|
|
/** Release the table handle. */
|
|
inline void release();
|
|
|
|
/** Lock the table handle. */
|
|
inline void lock();
|
|
|
|
/** Unlock the table handle. */
|
|
inline void unlock();
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** Mutex of the table for concurrency access. */
|
|
ib_mutex_t *mutex;
|
|
|
|
/** Creation state of mutex. */
|
|
volatile os_once::state_t mutex_created;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Id of the table. */
|
|
table_id_t id;
|
|
|
|
/** Memory heap. If you allocate from this heap after the table has
|
|
been created then be sure to account the allocation into
|
|
dict_sys->size. When closing the table we do something like
|
|
dict_sys->size -= mem_heap_get_size(table->heap) and if that is going
|
|
to become negative then we would assert. Something like this should do:
|
|
old_size = mem_heap_get_size()
|
|
mem_heap_alloc()
|
|
new_size = mem_heap_get_size()
|
|
dict_sys->size += new_size - old_size. */
|
|
mem_heap_t *heap;
|
|
|
|
/** Table name. */
|
|
table_name_t name;
|
|
|
|
/** Truncate name. */
|
|
table_name_t trunc_name;
|
|
|
|
/** NULL or the directory path specified by DATA DIRECTORY. */
|
|
char *data_dir_path;
|
|
|
|
/** NULL or the tablespace name that this table is assigned to,
|
|
specified by the TABLESPACE option.*/
|
|
id_name_t tablespace;
|
|
|
|
/** Space where the clustered index of the table is placed. */
|
|
space_id_t space;
|
|
|
|
/** dd::Tablespace::id of the table */
|
|
dd::Object_id dd_space_id;
|
|
|
|
/** Stores information about:
|
|
1 row format (redundant or compact),
|
|
2 compressed page size (zip shift size),
|
|
3 whether using atomic blobs,
|
|
4 whether the table has been created with the option DATA DIRECTORY.
|
|
Use DICT_TF_GET_COMPACT(), DICT_TF_GET_ZIP_SSIZE(),
|
|
DICT_TF_HAS_ATOMIC_BLOBS() and DICT_TF_HAS_DATA_DIR() to parse this
|
|
flag. */
|
|
unsigned flags : DICT_TF_BITS;
|
|
|
|
/** Stores information about:
|
|
1 whether the table has been created using CREATE TEMPORARY TABLE,
|
|
2 whether the table has an internally defined DOC ID column,
|
|
3 whether the table has a FTS index,
|
|
4 whether DOC ID column need to be added to the FTS index,
|
|
5 whether the table is being created its own tablespace,
|
|
6 whether the table has been DISCARDed,
|
|
7 whether the aux FTS tables names are in hex.
|
|
8 whether the table is instinc table.
|
|
9 whether the table has encryption setting.
|
|
Use DICT_TF2_FLAG_IS_SET() to parse this flag. */
|
|
unsigned flags2 : DICT_TF2_BITS;
|
|
|
|
/** TRUE if the table is an intermediate table during copy alter
|
|
operation or a partition/subpartition which is required for copying
|
|
data and skip the undo log for insertion of row in the table.
|
|
This variable will be set and unset during extra(), or during the
|
|
process of altering partitions */
|
|
unsigned skip_alter_undo : 1;
|
|
|
|
/** TRUE if this is in a single-table tablespace and the .ibd file is
|
|
missing. Then we must return in ha_innodb.cc an error if the user
|
|
tries to query such an orphaned table. */
|
|
unsigned ibd_file_missing : 1;
|
|
|
|
/** TRUE if the table object has been added to the dictionary cache. */
|
|
unsigned cached : 1;
|
|
|
|
/** TRUE if the table is to be dropped, but not yet actually dropped
|
|
(could in the background drop list). It is turned on at the beginning
|
|
of row_drop_table_for_mysql() and turned off just before we start to
|
|
update system tables for the drop. It is protected by
|
|
dict_operation_lock. */
|
|
unsigned to_be_dropped : 1;
|
|
|
|
/** Number of non-virtual columns defined so far. */
|
|
unsigned n_def : 10;
|
|
|
|
/** Number of non-virtual columns. */
|
|
unsigned n_cols : 10;
|
|
|
|
/** Number of non-virtual columns before first instant ADD COLUMN,
|
|
including the system columns like n_cols. */
|
|
unsigned n_instant_cols : 10;
|
|
|
|
/** Number of total columns (inlcude virtual and non-virtual) */
|
|
unsigned n_t_cols : 10;
|
|
|
|
/** Number of total columns defined so far. */
|
|
unsigned n_t_def : 10;
|
|
|
|
/** Number of virtual columns defined so far. */
|
|
unsigned n_v_def : 10;
|
|
|
|
/** Number of virtual columns. */
|
|
unsigned n_v_cols : 10;
|
|
|
|
/** Number of multi-value virtual columns. */
|
|
unsigned n_m_v_cols : 10;
|
|
|
|
/** TRUE if this table is expected to be kept in memory. This table
|
|
could be a table that has FK relationships or is undergoing DDL */
|
|
unsigned can_be_evicted : 1;
|
|
|
|
/** TRUE if this table is not evictable(can_be_evicted) and this is
|
|
because of DDL operation */
|
|
unsigned ddl_not_evictable : 1;
|
|
|
|
/** TRUE if some indexes should be dropped after ONLINE_INDEX_ABORTED
|
|
or ONLINE_INDEX_ABORTED_DROPPED. */
|
|
unsigned drop_aborted : 1;
|
|
|
|
/** Array of column descriptions. */
|
|
dict_col_t *cols;
|
|
|
|
/** Array of virtual column descriptions. */
|
|
dict_v_col_t *v_cols;
|
|
|
|
/** List of stored column descriptions. It is used only for foreign key
|
|
check during create table and copy alter operations.
|
|
During copy alter, s_cols list is filled during create table operation
|
|
and need to preserve till rename table operation. That is the
|
|
reason s_cols is a part of dict_table_t */
|
|
dict_s_col_list *s_cols;
|
|
|
|
/** Column names packed in a character string
|
|
"name1\0name2\0...nameN\0". Until the string contains n_cols, it will
|
|
be allocated from a temporary heap. The final string will be allocated
|
|
from table->heap. */
|
|
const char *col_names;
|
|
|
|
/** Virtual column names */
|
|
const char *v_col_names;
|
|
|
|
/** Hash chain node. */
|
|
hash_node_t name_hash;
|
|
|
|
/** Hash chain node. */
|
|
hash_node_t id_hash;
|
|
|
|
/** The FTS_DOC_ID_INDEX, or NULL if no fulltext indexes exist */
|
|
dict_index_t *fts_doc_id_index;
|
|
|
|
/** List of indexes of the table. */
|
|
UT_LIST_BASE_NODE_T(dict_index_t) indexes;
|
|
|
|
/** List of foreign key constraints in the table. These refer to
|
|
columns in other tables. */
|
|
UT_LIST_BASE_NODE_T(dict_foreign_t) foreign_list;
|
|
|
|
/** List of foreign key constraints which refer to this table. */
|
|
UT_LIST_BASE_NODE_T(dict_foreign_t) referenced_list;
|
|
|
|
/** Node of the LRU list of tables. */
|
|
UT_LIST_NODE_T(dict_table_t) table_LRU;
|
|
|
|
/** metadata version number of dd::Table::se_private_data() */
|
|
uint64_t version;
|
|
|
|
/** table dynamic metadata status, protected by dict_persist->mutex */
|
|
std::atomic<table_dirty_status> dirty_status;
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** Node of the dirty table list of tables, which is protected
|
|
by dict_persist->mutex */
|
|
UT_LIST_NODE_T(dict_table_t) dirty_dict_tables;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** This field is used to mark if a table is in the
|
|
dirty_dict_tables_list. if the dirty_status is not of
|
|
METADATA_CLEAN, the table should be in the list, otherwise not.
|
|
This field should be protected by dict_persist->mutex too. */
|
|
bool in_dirty_dict_tables_list;
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/** Maximum recursive level we support when loading tables chained
|
|
together with FK constraints. If exceeds this level, we will stop
|
|
loading child table into memory along with its parent table. */
|
|
unsigned fk_max_recusive_level : 8;
|
|
|
|
/** Count of how many foreign key check operations are currently being
|
|
performed on the table. We cannot drop the table while there are
|
|
foreign key checks running on it. */
|
|
ulint n_foreign_key_checks_running;
|
|
|
|
/** Transaction id that last touched the table definition. Either when
|
|
loading the definition or CREATE TABLE, or ALTER TABLE (prepare,
|
|
commit, and rollback phases). */
|
|
trx_id_t def_trx_id;
|
|
|
|
/*!< set of foreign key constraints in the table; these refer to
|
|
columns in other tables */
|
|
dict_foreign_set foreign_set;
|
|
|
|
/*!< set of foreign key constraints which refer to this table */
|
|
dict_foreign_set referenced_set;
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** This field is used to specify in simulations tables which are so
|
|
big that disk should be accessed. Disk access is simulated by putting
|
|
the thread to sleep for a while. NOTE that this flag is not stored to
|
|
the data dictionary on disk, and the database will forget about value
|
|
TRUE if it has to reload the table definition from disk. */
|
|
ibool does_not_fit_in_memory;
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/** TRUE if the maximum length of a single row exceeds BIG_ROW_SIZE.
|
|
Initialized in dict_table_add_to_cache(). */
|
|
unsigned big_rows : 1;
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** Statistics for query optimization. @{ */
|
|
|
|
/** Creation state of 'stats_latch'. */
|
|
volatile os_once::state_t stats_latch_created;
|
|
|
|
/** This latch protects:
|
|
"dict_table_t::stat_initialized",
|
|
"dict_table_t::stat_n_rows (*)",
|
|
"dict_table_t::stat_clustered_index_size",
|
|
"dict_table_t::stat_sum_of_other_index_sizes",
|
|
"dict_table_t::stat_modified_counter (*)",
|
|
"dict_table_t::indexes*::stat_n_diff_key_vals[]",
|
|
"dict_table_t::indexes*::stat_index_size",
|
|
"dict_table_t::indexes*::stat_n_leaf_pages".
|
|
(*) Those are not always protected for
|
|
performance reasons. */
|
|
rw_lock_t *stats_latch;
|
|
|
|
/** TRUE if statistics have been calculated the first time after
|
|
database startup or table creation. */
|
|
unsigned stat_initialized : 1;
|
|
|
|
/** Timestamp of last recalc of the stats. */
|
|
ib_time_monotonic_t stats_last_recalc;
|
|
|
|
/** The two bits below are set in the 'stat_persistent' member. They
|
|
have the following meaning:
|
|
1. _ON=0, _OFF=0, no explicit persistent stats setting for this table,
|
|
the value of the global srv_stats_persistent is used to determine
|
|
whether the table has persistent stats enabled or not
|
|
2. _ON=0, _OFF=1, persistent stats are explicitly disabled for this
|
|
table, regardless of the value of the global srv_stats_persistent
|
|
3. _ON=1, _OFF=0, persistent stats are explicitly enabled for this
|
|
table, regardless of the value of the global srv_stats_persistent
|
|
4. _ON=1, _OFF=1, not allowed, we assert if this ever happens. */
|
|
#define DICT_STATS_PERSISTENT_ON (1 << 1)
|
|
#define DICT_STATS_PERSISTENT_OFF (1 << 2)
|
|
|
|
/** Indicates whether the table uses persistent stats or not. See
|
|
DICT_STATS_PERSISTENT_ON and DICT_STATS_PERSISTENT_OFF. */
|
|
ib_uint32_t stat_persistent;
|
|
|
|
/** The two bits below are set in the 'stats_auto_recalc' member. They
|
|
have the following meaning:
|
|
1. _ON=0, _OFF=0, no explicit auto recalc setting for this table, the
|
|
value of the global srv_stats_persistent_auto_recalc is used to
|
|
determine whether the table has auto recalc enabled or not
|
|
2. _ON=0, _OFF=1, auto recalc is explicitly disabled for this table,
|
|
regardless of the value of the global srv_stats_persistent_auto_recalc
|
|
3. _ON=1, _OFF=0, auto recalc is explicitly enabled for this table,
|
|
regardless of the value of the global srv_stats_persistent_auto_recalc
|
|
4. _ON=1, _OFF=1, not allowed, we assert if this ever happens. */
|
|
#define DICT_STATS_AUTO_RECALC_ON (1 << 1)
|
|
#define DICT_STATS_AUTO_RECALC_OFF (1 << 2)
|
|
|
|
/** Indicates whether the table uses automatic recalc for persistent
|
|
stats or not. See DICT_STATS_AUTO_RECALC_ON and
|
|
DICT_STATS_AUTO_RECALC_OFF. */
|
|
ib_uint32_t stats_auto_recalc;
|
|
|
|
/** The number of pages to sample for this table during persistent
|
|
stats estimation. If this is 0, then the value of the global
|
|
srv_stats_persistent_sample_pages will be used instead. */
|
|
ulint stats_sample_pages;
|
|
|
|
/** Approximate number of rows in the table. We periodically calculate
|
|
new estimates. */
|
|
ib_uint64_t stat_n_rows;
|
|
|
|
/** Approximate clustered index size in database pages. */
|
|
ulint stat_clustered_index_size;
|
|
|
|
/** Approximate size of other indexes in database pages. */
|
|
ulint stat_sum_of_other_index_sizes;
|
|
|
|
/** If FTS AUX table, parent table id */
|
|
table_id_t parent_id;
|
|
|
|
/** How many rows are modified since last stats recalc. When a row is
|
|
inserted, updated, or deleted, we add 1 to this number; we calculate
|
|
new estimates for the table and the indexes if the table has changed
|
|
too much, see row_update_statistics_if_needed(). The counter is reset
|
|
to zero at statistics calculation. This counter is not protected by
|
|
any latch, because this is only used for heuristics. */
|
|
ib_uint64_t stat_modified_counter;
|
|
|
|
/** Background stats thread is not working on this table. */
|
|
#define BG_STAT_NONE 0
|
|
|
|
/** Set in 'stats_bg_flag' when the background stats code is working
|
|
on this table. The DROP TABLE code waits for this to be cleared before
|
|
proceeding. */
|
|
#define BG_STAT_IN_PROGRESS (1 << 0)
|
|
|
|
/** Set in 'stats_bg_flag' when DROP TABLE starts waiting on
|
|
BG_STAT_IN_PROGRESS to be cleared. The background stats thread will
|
|
detect this and will eventually quit sooner. */
|
|
#define BG_STAT_SHOULD_QUIT (1 << 1)
|
|
|
|
/** The state of the background stats thread wrt this table.
|
|
See BG_STAT_NONE, BG_STAT_IN_PROGRESS and BG_STAT_SHOULD_QUIT.
|
|
Writes are covered by dict_sys->mutex. Dirty reads are possible. */
|
|
byte stats_bg_flag;
|
|
|
|
/* @} */
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** AUTOINC related members. @{ */
|
|
|
|
/* The actual collection of tables locked during AUTOINC read/write is
|
|
kept in trx_t. In order to quickly determine whether a transaction has
|
|
locked the AUTOINC lock we keep a pointer to the transaction here in
|
|
the 'autoinc_trx' member. This is to avoid acquiring the
|
|
lock_sys_t::mutex and scanning the vector in trx_t.
|
|
When an AUTOINC lock has to wait, the corresponding lock instance is
|
|
created on the trx lock heap rather than use the pre-allocated instance
|
|
in autoinc_lock below. */
|
|
|
|
/** A buffer for an AUTOINC lock for this table. We allocate the
|
|
memory here so that individual transactions can get it and release it
|
|
without a need to allocate space from the lock heap of the trx:
|
|
otherwise the lock heap would grow rapidly if we do a large insert
|
|
from a select. */
|
|
#ifndef UNIV_HOTBACKUP
|
|
lock_t *autoinc_lock;
|
|
|
|
/** Creation state of autoinc_mutex member */
|
|
volatile os_once::state_t autoinc_mutex_created;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Mutex protecting the autoincrement counter. */
|
|
ib_mutex_t *autoinc_mutex;
|
|
|
|
/** Autoinc counter value to give to the next inserted row. */
|
|
ib_uint64_t autoinc;
|
|
|
|
/** Mutex protecting the persisted autoincrement counter. */
|
|
ib_mutex_t *autoinc_persisted_mutex;
|
|
|
|
/** Autoinc counter value that has been persisted in redo logs or
|
|
DDTableBuffer. It's mainly used when we want to write counter back
|
|
to DDTableBuffer.
|
|
This is different from the 'autoinc' above, which could be bigger
|
|
than this one, because 'autoinc' will get updated right after
|
|
some counters are allocated, but we will write the counter to redo
|
|
logs and update this counter later. Once all allocated counters
|
|
have been written to redo logs, 'autoinc' should be exact the next
|
|
counter of this persisted one.
|
|
We want this counter because when we need to write the counter back
|
|
to DDTableBuffer, we had better keep it consistency with the counter
|
|
that has been written to redo logs. Besides, we can't read the 'autoinc'
|
|
directly easily, because the autoinc_lock is required and there could
|
|
be a deadlock.
|
|
This variable is protected by autoinc_persisted_mutex. */
|
|
ib_uint64_t autoinc_persisted;
|
|
|
|
/** The position of autoinc counter field in clustered index. This would
|
|
be set when CREATE/ALTER/OPEN TABLE and IMPORT TABLESPACE, and used in
|
|
modifications to clustered index, such as INSERT/UPDATE. There should
|
|
be no conflict to access it, so no protection is needed. */
|
|
ulint autoinc_field_no;
|
|
|
|
/** The transaction that currently holds the the AUTOINC lock on this
|
|
table. Protected by lock_sys->mutex. */
|
|
const trx_t *autoinc_trx;
|
|
|
|
/* @} */
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** FTS specific state variables. */
|
|
fts_t *fts;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Quiescing states, protected by the dict_index_t::lock. ie. we can
|
|
only change the state if we acquire all the latches (dict_index_t::lock)
|
|
in X mode of this table's indexes. */
|
|
ib_quiesce_t quiesce;
|
|
|
|
/** Count of the number of record locks on this table. We use this to
|
|
determine whether we can evict the table from the dictionary cache.
|
|
It is protected by lock_sys->mutex. */
|
|
ulint n_rec_locks;
|
|
|
|
#ifndef UNIV_DEBUG
|
|
private:
|
|
#endif
|
|
/** Count of how many handles are opened to this table. Dropping of the
|
|
table is NOT allowed until this count gets to zero. MySQL does NOT
|
|
itself check the number of open handles at DROP. */
|
|
std::atomic<uint64_t> n_ref_count;
|
|
|
|
public:
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** List of locks on the table. Protected by lock_sys->mutex. */
|
|
table_lock_list_t locks;
|
|
/** count_by_mode[M] = number of locks in this->locks with
|
|
lock->type_mode&LOCK_MODE_MASK == M.
|
|
Used to quickly verify that there are no LOCK_S or LOCK_X, which are the only
|
|
modes incompatible with LOCK_IS and LOCK_IX, to avoid costly iteration over
|
|
this->locks when adding LOCK_IS or LOCK_IX.
|
|
We use count_by_mode[LOCK_AUTO_INC] to track the number of granted and pending
|
|
autoinc locks on this table. This value is set after acquiring the
|
|
lock_sys_t::mutex but we peek the contents to determine whether other
|
|
transactions have acquired the AUTOINC lock or not. Of course only one
|
|
transaction can be granted the lock but there can be multiple
|
|
waiters.
|
|
Protected by lock_sys->mutex. */
|
|
ulong count_by_mode[LOCK_NUM];
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Timestamp of the last modification of this table. */
|
|
time_t update_time;
|
|
|
|
/** row-id counter for use by intrinsic table for getting row-id.
|
|
Given intrinsic table semantics, row-id can be locally maintained
|
|
instead of getting it from central generator which involves mutex
|
|
locking. */
|
|
ib_uint64_t sess_row_id;
|
|
|
|
/** trx_id counter for use by intrinsic table for getting trx-id.
|
|
Intrinsic table are not shared so don't need a central trx-id
|
|
but just need a increased counter to track consistent view while
|
|
proceeding SELECT as part of UPDATE. */
|
|
ib_uint64_t sess_trx_id;
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Value of 'magic_n'. */
|
|
#define DICT_TABLE_MAGIC_N 76333786
|
|
|
|
/** Magic number. */
|
|
ulint magic_n;
|
|
#endif /* UNIV_DEBUG */
|
|
/** mysql_row_templ_t for base columns used for compute the virtual
|
|
columns */
|
|
dict_vcol_templ_t *vc_templ;
|
|
|
|
/** encryption key, it's only for export/import */
|
|
byte *encryption_key;
|
|
|
|
/** encryption iv, it's only for export/import */
|
|
byte *encryption_iv;
|
|
|
|
/** remove the dict_table_t from cache after DDL operation */
|
|
bool discard_after_ddl;
|
|
|
|
/** refresh/reload FK info */
|
|
bool refresh_fk;
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** multiple cursors can be active on this temporary table */
|
|
temp_prebuilt_vec *temp_prebuilt;
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** TRUE only for dictionary tables like mysql/tables,
|
|
mysql/columns, mysql/tablespaces, etc. This flag is used
|
|
to do non-locking reads on DD tables. */
|
|
bool is_dd_table;
|
|
|
|
/** true if this table is explicitly put to non-LRU list
|
|
during table creation */
|
|
bool explicitly_non_lru;
|
|
|
|
/** @return the clustered index */
|
|
const dict_index_t *first_index() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
const dict_index_t *first = UT_LIST_GET_FIRST(indexes);
|
|
return (first);
|
|
}
|
|
/** @return the clustered index */
|
|
dict_index_t *first_index() {
|
|
return (const_cast<dict_index_t *>(
|
|
const_cast<const dict_table_t *>(this)->first_index()));
|
|
}
|
|
|
|
/** @return if there was any instantly added column.
|
|
This will be true after one or more instant ADD COLUMN, however,
|
|
it would become false after ALTER TABLE which rebuilds or copies
|
|
the old table.
|
|
If this is true, all instantly added columns should have default
|
|
values, and records in the table may have REC_INFO_INSTANT_FLAG set. */
|
|
bool has_instant_cols() const {
|
|
ut_ad(n_instant_cols <= n_cols);
|
|
|
|
return (n_instant_cols < n_cols);
|
|
}
|
|
|
|
/** Set the number of columns when the first instant ADD COLUMN happens.
|
|
@param[in] instant_cols number of fields when first instant
|
|
ADD COLUMN happens, without system
|
|
columns */
|
|
void set_instant_cols(uint16_t instant_cols) {
|
|
n_instant_cols = static_cast<unsigned>(instant_cols) + get_n_sys_cols();
|
|
}
|
|
|
|
/** Get the number of user columns when the first instant ADD COLUMN
|
|
happens.
|
|
@return the number of user columns as described above */
|
|
uint16_t get_instant_cols() const {
|
|
return static_cast<uint16_t>(n_instant_cols - get_n_sys_cols());
|
|
}
|
|
|
|
/** Check whether the table is corrupted.
|
|
@return true if the table is corrupted, otherwise false */
|
|
bool is_corrupted() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
const dict_index_t *index = first_index();
|
|
|
|
/* It is possible that this table is only half created, in which case
|
|
the clustered index may be NULL. If the clustered index is corrupted,
|
|
the table is corrupt. We do not consider the table corrupt if only
|
|
a secondary index is corrupt. */
|
|
ut_ad(index == NULL || index->is_clustered());
|
|
|
|
return (index != NULL && index->type & DICT_CORRUPT);
|
|
}
|
|
|
|
/** Returns a column's name.
|
|
@param[in] col_nr column number
|
|
@return column name. NOTE: not guaranteed to stay valid if table is
|
|
modified in any way (columns added, etc.). */
|
|
const char *get_col_name(ulint col_nr) const {
|
|
ut_ad(col_nr < n_def);
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
const char *s = col_names;
|
|
if (s) {
|
|
for (ulint i = 0; i < col_nr; i++) {
|
|
s += strlen(s) + 1;
|
|
}
|
|
}
|
|
|
|
return (s);
|
|
}
|
|
|
|
/**Gets the nth column of a table.
|
|
@param[in] pos position of column
|
|
@return pointer to column object */
|
|
dict_col_t *get_col(ulint pos) const {
|
|
ut_ad(pos < n_def);
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
return (cols + pos);
|
|
}
|
|
|
|
/** Gets the number of user-defined non-virtual columns in a table
|
|
in the dictionary cache.
|
|
@return number of user-defined (e.g., not ROW_ID) non-virtual columns
|
|
of a table */
|
|
uint16_t get_n_user_cols() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
return (static_cast<uint16_t>(n_cols) - get_n_sys_cols());
|
|
}
|
|
|
|
/** Gets the number of system columns in a table.
|
|
For intrinsic table on ROW_ID column is added for all other
|
|
tables TRX_ID and ROLL_PTR are all also appeneded.
|
|
@return number of system (e.g., ROW_ID) columns of a table */
|
|
uint16_t get_n_sys_cols() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
return (is_intrinsic() ? DATA_ITT_N_SYS_COLS + DATA_ITT_N_LIZARD_COLS
|
|
: DATA_N_SYS_COLS + DATA_N_LIZARD_COLS);
|
|
}
|
|
|
|
/** Gets the number of all non-virtual columns (also system) in a table
|
|
in the dictionary cache.
|
|
@return number of non-virtual columns of a table */
|
|
ulint get_n_cols() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
return (n_cols);
|
|
}
|
|
|
|
/** Gets the given system column of a table.
|
|
@param[in] sys DATA_ROW_ID, ...
|
|
@return pointer to column object */
|
|
dict_col_t *get_sys_col(ulint sys) const {
|
|
dict_col_t *col;
|
|
|
|
ut_ad(sys < get_n_sys_cols());
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
|
|
col = get_col(n_cols - get_n_sys_cols() + sys);
|
|
ut_ad(col->mtype == DATA_SYS);
|
|
ut_ad(col->prtype == (sys | DATA_NOT_NULL));
|
|
|
|
return (col);
|
|
}
|
|
|
|
/** Determine if this is a temporary table. */
|
|
bool is_temporary() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
return (flags2 & DICT_TF2_TEMPORARY);
|
|
}
|
|
|
|
/** Determine if this is a FTS AUX table. */
|
|
bool is_fts_aux() const {
|
|
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
|
|
return (flags2 & DICT_TF2_AUX);
|
|
}
|
|
|
|
/** Determine whether the table is intrinsic.
|
|
An intrinsic table is a special kind of temporary table that
|
|
is invisible to the end user. It can be created internally by InnoDB,
|
|
the MySQL server layer or other modules connected to InnoDB in order
|
|
to gather and use data as part of a larger task. Since access to it
|
|
must be as fast as possible, it does not need UNDO semantics, system
|
|
fields DB_TRX_ID & DB_ROLL_PTR, doublewrite, checksum, insert buffer,
|
|
use of the shared data dictionary, locking, or even a transaction.
|
|
In short, these are not ACID tables at all, just temporary data stored
|
|
and manipulated during a larger process.*/
|
|
bool is_intrinsic() const {
|
|
if (flags2 & DICT_TF2_INTRINSIC) {
|
|
ut_ad(is_temporary());
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/* GAP locks are skipped for DD tables and SDI tables
|
|
@return true if table is DD table or SDI table, else false */
|
|
inline bool skip_gap_locks() const;
|
|
|
|
/** Determine if the table can support instant ADD COLUMN */
|
|
inline bool support_instant_add() const;
|
|
};
|
|
|
|
inline bool dict_index_t::is_compressed() const {
|
|
return (table->is_compressed());
|
|
}
|
|
|
|
/** Persistent dynamic metadata type, there should be 1 to 1
|
|
relationship between the metadata and the type. Please keep them in order
|
|
so that we can iterate over it */
|
|
enum persistent_type_t {
|
|
/** The smallest type, which should be 1 less than the first
|
|
true type */
|
|
PM_SMALLEST_TYPE = 0,
|
|
|
|
/** Persistent Metadata type for corrupted indexes */
|
|
PM_INDEX_CORRUPTED = 1,
|
|
|
|
/** Persistent Metadata type for autoinc counter */
|
|
PM_TABLE_AUTO_INC = 2,
|
|
|
|
/* TODO: Will add following types
|
|
PM_TABLE_UPDATE_TIME = 3,
|
|
Maybe something tablespace related
|
|
PM_TABLESPACE_SIZE = 4,
|
|
PM_TABLESPACE_MAX_TRX_ID = 5, */
|
|
|
|
/** The biggest type, which should be 1 bigger than the last
|
|
true type */
|
|
PM_BIGGEST_TYPE = 3
|
|
};
|
|
|
|
typedef std::vector<index_id_t, ut_allocator<index_id_t>> corrupted_ids_t;
|
|
|
|
/** Persistent dynamic metadata for a table */
|
|
class PersistentTableMetadata {
|
|
public:
|
|
/** Constructor
|
|
@param[in] id table id
|
|
@param[in] version table dynamic metadata version */
|
|
PersistentTableMetadata(table_id_t id, uint64 version)
|
|
: m_id(id), m_version(version), m_corrupted_ids(), m_autoinc(0) {}
|
|
|
|
/** Get the corrupted indexes' IDs
|
|
@return the vector of indexes' IDs */
|
|
const corrupted_ids_t &get_corrupted_indexes() const {
|
|
return (m_corrupted_ids);
|
|
}
|
|
|
|
/** Add a corrupted index id and space id
|
|
@param[in] id corrupted index id */
|
|
void add_corrupted_index(const index_id_t id) {
|
|
m_corrupted_ids.push_back(id);
|
|
}
|
|
|
|
/** Set the dynamic metadata version.
|
|
@param[in] version dynamic metadata version */
|
|
void set_version(uint64_t version) { m_version = version; }
|
|
|
|
/** Get the dynamic metadata version */
|
|
uint64_t get_version() const { return (m_version); }
|
|
|
|
/** Get the table id of the metadata
|
|
@return table id */
|
|
table_id_t get_table_id() const { return (m_id); }
|
|
|
|
/** Set the autoinc counter of the table if it's bigger
|
|
@param[in] autoinc autoinc counter */
|
|
void set_autoinc_if_bigger(uint64_t autoinc) {
|
|
/* We only set the biggest autoinc counter. Callers don't
|
|
guarantee passing a bigger number in. */
|
|
if (autoinc > m_autoinc) {
|
|
m_autoinc = autoinc;
|
|
}
|
|
}
|
|
|
|
/** Set the autoinc counter of the table
|
|
@param[in] autoinc autoinc counter */
|
|
void set_autoinc(uint64_t autoinc) { m_autoinc = autoinc; }
|
|
|
|
/** Get the autoinc counter of the table
|
|
@return the autoinc counter */
|
|
uint64_t get_autoinc() const { return (m_autoinc); }
|
|
|
|
private:
|
|
/** Table ID which this metadata belongs to */
|
|
table_id_t m_id;
|
|
|
|
/** Table dynamic metadata version of the change */
|
|
uint64_t m_version;
|
|
|
|
/** Storing the corrupted indexes' ID if exist, or else empty */
|
|
corrupted_ids_t m_corrupted_ids;
|
|
|
|
/** Autoinc counter of the table */
|
|
uint64_t m_autoinc;
|
|
|
|
/* TODO: We will add update_time, etc. here and APIs accordingly */
|
|
};
|
|
|
|
/** Interface for persistent dynamic table metadata. */
|
|
class Persister {
|
|
public:
|
|
/** Virtual desctructor */
|
|
virtual ~Persister() {}
|
|
|
|
/** Write the dynamic metadata of a table, we can pre-calculate
|
|
the size by calling get_write_size()
|
|
@param[in] metadata persistent data
|
|
@param[out] buffer write buffer
|
|
@param[in] size size of write buffer, should be
|
|
at least get_write_size()
|
|
@return the length of bytes written */
|
|
virtual ulint write(const PersistentTableMetadata &metadata, byte *buffer,
|
|
ulint size) const = 0;
|
|
|
|
/** Pre-calculate the size of metadata to be written
|
|
@param[in] metadata metadata to be written
|
|
@return the size of metadata */
|
|
virtual ulint get_write_size(
|
|
const PersistentTableMetadata &metadata) const = 0;
|
|
|
|
/** Read the dynamic metadata from buffer, and store them to
|
|
metadata object
|
|
@param[out] metadata metadata where we store the read data
|
|
@param[in] buffer buffer to read
|
|
@param[in] size size of buffer
|
|
@param[out] corrupt true if we found something wrong in
|
|
the buffer except incomplete buffer,
|
|
otherwise false
|
|
@return the bytes we read from the buffer if the buffer data
|
|
is complete and we get everything, 0 if the buffer is incompleted */
|
|
virtual ulint read(PersistentTableMetadata &metadata, const byte *buffer,
|
|
ulint size, bool *corrupt) const = 0;
|
|
|
|
/** Write MLOG_TABLE_DYNAMIC_META for persistent dynamic
|
|
metadata of table
|
|
@param[in] id table id
|
|
@param[in] metadata metadata used to write the log
|
|
@param[in,out] mtr mini-transaction */
|
|
void write_log(table_id_t id, const PersistentTableMetadata &metadata,
|
|
mtr_t *mtr) const;
|
|
};
|
|
|
|
/** Persister used for corrupted indexes */
|
|
class CorruptedIndexPersister : public Persister {
|
|
public:
|
|
/** Write the corrupted indexes of a table, we can pre-calculate
|
|
the size by calling get_write_size()
|
|
@param[in] metadata persistent metadata
|
|
@param[out] buffer write buffer
|
|
@param[in] size size of write buffer, should be
|
|
at least get_write_size()
|
|
@return the length of bytes written */
|
|
ulint write(const PersistentTableMetadata &metadata, byte *buffer,
|
|
ulint size) const;
|
|
|
|
/** Pre-calculate the size of metadata to be written
|
|
@param[in] metadata metadata to be written
|
|
@return the size of metadata */
|
|
ulint get_write_size(const PersistentTableMetadata &metadata) const;
|
|
|
|
/** Read the corrupted indexes from buffer, and store them to
|
|
metadata object
|
|
@param[out] metadata metadata where we store the read data
|
|
@param[in] buffer buffer to read
|
|
@param[in] size size of buffer
|
|
@param[out] corrupt true if we found something wrong in
|
|
the buffer except incomplete buffer,
|
|
otherwise false
|
|
@return the bytes we read from the buffer if the buffer data
|
|
is complete and we get everything, 0 if the buffer is incomplete */
|
|
ulint read(PersistentTableMetadata &metadata, const byte *buffer, ulint size,
|
|
bool *corrupt) const;
|
|
|
|
private:
|
|
/** The length of index_id_t we will write */
|
|
static const size_t INDEX_ID_LENGTH = 12;
|
|
};
|
|
|
|
/** Persister used for autoinc counters */
|
|
class AutoIncPersister : public Persister {
|
|
public:
|
|
/** Write the autoinc counter of a table, we can pre-calculate
|
|
the size by calling get_write_size()
|
|
@param[in] metadata persistent metadata
|
|
@param[out] buffer write buffer
|
|
@param[in] size size of write buffer, should be
|
|
at least get_write_size()
|
|
@return the length of bytes written */
|
|
ulint write(const PersistentTableMetadata &metadata, byte *buffer,
|
|
ulint size) const;
|
|
|
|
/** Pre-calculate the size of metadata to be written
|
|
@param[in] metadata metadata to be written
|
|
@return the size of metadata */
|
|
inline ulint get_write_size(const PersistentTableMetadata &metadata) const {
|
|
/* We just return the max possible size that would be used
|
|
if the counter exists, so we don't calculate every time.
|
|
Here we need 1 byte for dynamic metadata type and 11 bytes
|
|
for the max possible size of counter. */
|
|
return (12);
|
|
}
|
|
|
|
/** Read the autoinc counter from buffer, and store them to
|
|
metadata object
|
|
@param[out] metadata metadata where we store the read data
|
|
@param[in] buffer buffer to read
|
|
@param[in] size size of buffer
|
|
@param[out] corrupt true if we found something wrong in
|
|
the buffer except incomplete buffer,
|
|
otherwise false
|
|
@return the bytes we read from the buffer if the buffer data
|
|
is complete and we get everything, 0 if the buffer is incomplete */
|
|
ulint read(PersistentTableMetadata &metadata, const byte *buffer, ulint size,
|
|
bool *corrupt) const;
|
|
};
|
|
|
|
/** Container of persisters used in the system. Currently we don't need
|
|
to protect this object since we only initialize it at very beginning and
|
|
destroy it in the end. During the server running, we only get the persisters */
|
|
class Persisters {
|
|
typedef std::map<
|
|
persistent_type_t, Persister *, std::less<persistent_type_t>,
|
|
ut_allocator<std::pair<const persistent_type_t, Persister *>>>
|
|
persisters_t;
|
|
|
|
public:
|
|
/** Constructor */
|
|
Persisters() : m_persisters() {}
|
|
|
|
/** Destructor */
|
|
~Persisters();
|
|
|
|
/** Get the persister object with specified type
|
|
@param[in] type persister type
|
|
@return Persister object required or NULL if not found */
|
|
Persister *get(persistent_type_t type) const;
|
|
|
|
/** Add a specified persister of type, we will allocate the Persister
|
|
if there is no such persister exist, otherwise do nothing and return
|
|
the existing one
|
|
@param[in] type persister type
|
|
@return the persister of type */
|
|
Persister *add(persistent_type_t type);
|
|
|
|
/** Remove a specified persister of type, we will free the Persister
|
|
@param[in] type persister type */
|
|
void remove(persistent_type_t type);
|
|
|
|
/** Serialize the metadata to a buffer
|
|
@param[in] metadata metadata to serialize
|
|
@param[out] buffer buffer to store the serialized metadata
|
|
@return the length of serialized metadata */
|
|
size_t write(PersistentTableMetadata &metadata, byte *buffer);
|
|
|
|
private:
|
|
/** A map to store all persisters needed */
|
|
persisters_t m_persisters;
|
|
};
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** Initialise the table lock list. */
|
|
void lock_table_lock_list_init(
|
|
table_lock_list_t *locks); /*!< List to initialise */
|
|
|
|
/** A function object to add the foreign key constraint to the referenced set
|
|
of the referenced table, if it exists in the dictionary cache. */
|
|
struct dict_foreign_add_to_referenced_table {
|
|
void operator()(dict_foreign_t *foreign) const {
|
|
if (dict_table_t *table = foreign->referenced_table) {
|
|
std::pair<dict_foreign_set::iterator, bool> ret =
|
|
table->referenced_set.insert(foreign);
|
|
ut_a(ret.second);
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Request for lazy creation of the mutex of a given table.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] table table whose mutex is to be created */
|
|
inline void dict_table_mutex_create_lazy(dict_table_t *table) {
|
|
table->mutex = nullptr;
|
|
table->mutex_created = os_once::NEVER_DONE;
|
|
}
|
|
|
|
/** Destroy the mutex of a given table.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] table table whose mutex is to be created */
|
|
inline void dict_table_mutex_destroy(dict_table_t *table) {
|
|
if (table->mutex_created == os_once::DONE) {
|
|
if (table->mutex != nullptr) {
|
|
mutex_free(table->mutex);
|
|
UT_DELETE(table->mutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Destroy the autoinc latch of the given table.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] table table whose stats latch to destroy */
|
|
inline void dict_table_autoinc_destroy(dict_table_t *table) {
|
|
if (table->autoinc_mutex_created == os_once::DONE) {
|
|
if (table->autoinc_mutex != NULL) {
|
|
mutex_free(table->autoinc_mutex);
|
|
UT_DELETE(table->autoinc_mutex);
|
|
}
|
|
|
|
if (table->autoinc_persisted_mutex != NULL) {
|
|
mutex_free(table->autoinc_persisted_mutex);
|
|
UT_DELETE(table->autoinc_persisted_mutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Request for lazy creation of the autoinc latch of a given table.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] table table whose autoinc latch is to be created. */
|
|
inline void dict_table_autoinc_create_lazy(dict_table_t *table) {
|
|
table->autoinc_mutex = NULL;
|
|
table->autoinc_persisted_mutex = NULL;
|
|
table->autoinc_mutex_created = os_once::NEVER_DONE;
|
|
}
|
|
|
|
/** Request a lazy creation of dict_index_t::zip_pad::mutex.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] index index whose zip_pad mutex is to be created */
|
|
inline void dict_index_zip_pad_mutex_create_lazy(dict_index_t *index) {
|
|
index->zip_pad.mutex = NULL;
|
|
index->zip_pad.mutex_created = os_once::NEVER_DONE;
|
|
}
|
|
|
|
/** Destroy the zip_pad_mutex of the given index.
|
|
This function is only called from either single threaded environment
|
|
or from a thread that has not shared the table object with other threads.
|
|
@param[in,out] index index whose stats latch to destroy */
|
|
inline void dict_index_zip_pad_mutex_destroy(dict_index_t *index) {
|
|
if (index->zip_pad.mutex_created == os_once::DONE &&
|
|
index->zip_pad.mutex != NULL) {
|
|
mutex_free(index->zip_pad.mutex);
|
|
UT_DELETE(index->zip_pad.mutex);
|
|
}
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Release the zip_pad_mutex of a given index.
|
|
@param[in,out] index index whose zip_pad_mutex is to be released */
|
|
inline void dict_index_zip_pad_unlock(dict_index_t *index) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
mutex_exit(index->zip_pad.mutex);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
}
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Check if the current thread owns the autoinc_mutex of a given table.
|
|
@param[in] table the autoinc_mutex belongs to this table
|
|
@return true, if the current thread owns the autoinc_mutex, false otherwise.*/
|
|
inline bool dict_table_autoinc_own(const dict_table_t *table) {
|
|
return (mutex_own(table->autoinc_mutex));
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
#include "dict0mem.ic"
|
|
|
|
#endif /* dict0mem_h */
|