mirror of
https://github.com/MariaDB/server.git
synced 2025-02-01 19:41:47 +01:00
a4948dafcd
For InnoDB tables, adding, dropping and reordering columns has required a rebuild of the table and all its indexes. Since MySQL 5.6 (and MariaDB 10.0) this has been supported online (LOCK=NONE), allowing concurrent modification of the tables. This work revises the InnoDB ROW_FORMAT=REDUNDANT, ROW_FORMAT=COMPACT and ROW_FORMAT=DYNAMIC so that columns can be appended instantaneously, with only minor changes performed to the table structure. The counter innodb_instant_alter_column in INFORMATION_SCHEMA.GLOBAL_STATUS is incremented whenever a table rebuild operation is converted into an instant ADD COLUMN operation. ROW_FORMAT=COMPRESSED tables will not support instant ADD COLUMN. Some usability limitations will be addressed in subsequent work: MDEV-13134 Introduce ALTER TABLE attributes ALGORITHM=NOCOPY and ALGORITHM=INSTANT MDEV-14016 Allow instant ADD COLUMN, ADD INDEX, LOCK=NONE The format of the clustered index (PRIMARY KEY) is changed as follows: (1) The FIL_PAGE_TYPE of the root page will be FIL_PAGE_TYPE_INSTANT, and a new field PAGE_INSTANT will contain the original number of fields in the clustered index ('core' fields). If instant ADD COLUMN has not been used or the table becomes empty, or the very first instant ADD COLUMN operation is rolled back, the fields PAGE_INSTANT and FIL_PAGE_TYPE will be reset to 0 and FIL_PAGE_INDEX. (2) A special 'default row' record is inserted into the leftmost leaf, between the page infimum and the first user record. This record is distinguished by the REC_INFO_MIN_REC_FLAG, and it is otherwise in the same format as records that contain values for the instantly added columns. This 'default row' always has the same number of fields as the clustered index according to the table definition. The values of 'core' fields are to be ignored. For other fields, the 'default row' will contain the default values as they were during the ALTER TABLE statement. (If the column default values are changed later, those values will only be stored in the .frm file. The 'default row' will contain the original evaluated values, which must be the same for every row.) The 'default row' must be completely hidden from higher-level access routines. Assertions have been added to ensure that no 'default row' is ever present in the adaptive hash index or in locked records. The 'default row' is never delete-marked. (3) In clustered index leaf page records, the number of fields must reside between the number of 'core' fields (dict_index_t::n_core_fields introduced in this work) and dict_index_t::n_fields. If the number of fields is less than dict_index_t::n_fields, the missing fields are replaced with the column value of the 'default row'. Note: The number of fields in the record may shrink if some of the last instantly added columns are updated to the value that is in the 'default row'. The function btr_cur_trim() implements this 'compression' on update and rollback; dtuple::trim() implements it on insert. (4) In ROW_FORMAT=COMPACT and ROW_FORMAT=DYNAMIC records, the new status value REC_STATUS_COLUMNS_ADDED will indicate the presence of a new record header that will encode n_fields-n_core_fields-1 in 1 or 2 bytes. (In ROW_FORMAT=REDUNDANT records, the record header always explicitly encodes the number of fields.) We introduce the undo log record type TRX_UNDO_INSERT_DEFAULT for covering the insert of the 'default row' record when instant ADD COLUMN is used for the first time. Subsequent instant ADD COLUMN can use TRX_UNDO_UPD_EXIST_REC. This is joint work with Vin Chen (陈福荣) from Tencent. The design that was discussed in April 2017 would not have allowed import or export of data files, because instead of the 'default row' it would have introduced a data dictionary table. The test rpl.rpl_alter_instant is exactly as contributed in pull request #408. The test innodb.instant_alter is based on a contributed test. The redo log record format changes for ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPACT are as contributed. (With this change present, crash recovery from MariaDB 10.3.1 will fail in spectacular ways!) Also the semantics of higher-level redo log records that modify the PAGE_INSTANT field is changed. The redo log format version identifier was already changed to LOG_HEADER_FORMAT_CURRENT=103 in MariaDB 10.3.1. Everything else has been rewritten by me. Thanks to Elena Stepanova, the code has been tested extensively. When rolling back an instant ADD COLUMN operation, we must empty the PAGE_FREE list after deleting or shortening the 'default row' record, by calling either btr_page_empty() or btr_page_reorganize(). We must know the size of each entry in the PAGE_FREE list. If rollback left a freed copy of the 'default row' in the PAGE_FREE list, we would be unable to determine its size (if it is in ROW_FORMAT=COMPACT or ROW_FORMAT=DYNAMIC) because it would contain more fields than the rolled-back definition of the clustered index. UNIV_SQL_DEFAULT: A new special constant that designates an instantly added column that is not present in the clustered index record. len_is_stored(): Check if a length is an actual length. There are two magic length values: UNIV_SQL_DEFAULT, UNIV_SQL_NULL. dict_col_t::def_val: The 'default row' value of the column. If the column is not added instantly, def_val.len will be UNIV_SQL_DEFAULT. dict_col_t: Add the accessors is_virtual(), is_nullable(), is_instant(), instant_value(). dict_col_t::remove_instant(): Remove the 'instant ADD' status of a column. dict_col_t::name(const dict_table_t& table): Replaces dict_table_get_col_name(). dict_index_t::n_core_fields: The original number of fields. For secondary indexes and if instant ADD COLUMN has not been used, this will be equal to dict_index_t::n_fields. dict_index_t::n_core_null_bytes: Number of bytes needed to represent the null flags; usually equal to UT_BITS_IN_BYTES(n_nullable). dict_index_t::NO_CORE_NULL_BYTES: Magic value signalling that n_core_null_bytes was not initialized yet from the clustered index root page. dict_index_t: Add the accessors is_instant(), is_clust(), get_n_nullable(), instant_field_value(). dict_index_t::instant_add_field(): Adjust clustered index metadata for instant ADD COLUMN. dict_index_t::remove_instant(): Remove the 'instant ADD' status of a clustered index when the table becomes empty, or the very first instant ADD COLUMN operation is rolled back. dict_table_t: Add the accessors is_instant(), is_temporary(), supports_instant(). dict_table_t::instant_add_column(): Adjust metadata for instant ADD COLUMN. dict_table_t::rollback_instant(): Adjust metadata on the rollback of instant ADD COLUMN. prepare_inplace_alter_table_dict(): First create the ctx->new_table, and only then decide if the table really needs to be rebuilt. We must split the creation of table or index metadata from the creation of the dictionary table records and the creation of the data. In this way, we can transform a table-rebuilding operation into an instant ADD COLUMN operation. Dictionary objects will only be added to cache when table rebuilding or index creation is needed. The ctx->instant_table will never be added to cache. dict_table_t::add_to_cache(): Modified and renamed from dict_table_add_to_cache(). Do not modify the table metadata. Let the callers invoke dict_table_add_system_columns() and if needed, set can_be_evicted. dict_create_sys_tables_tuple(), dict_create_table_step(): Omit the system columns (which will now exist in the dict_table_t object already at this point). dict_create_table_step(): Expect the callers to invoke dict_table_add_system_columns(). pars_create_table(): Before creating the table creation execution graph, invoke dict_table_add_system_columns(). row_create_table_for_mysql(): Expect all callers to invoke dict_table_add_system_columns(). create_index_dict(): Replaces row_merge_create_index_graph(). innodb_update_n_cols(): Renamed from innobase_update_n_virtual(). Call my_error() if an error occurs. btr_cur_instant_init(), btr_cur_instant_init_low(), btr_cur_instant_root_init(): Load additional metadata from the clustered index and set dict_index_t::n_core_null_bytes. This is invoked when table metadata is first loaded into the data dictionary. dict_boot(): Initialize n_core_null_bytes for the four hard-coded dictionary tables. dict_create_index_step(): Initialize n_core_null_bytes. This is executed as part of CREATE TABLE. dict_index_build_internal_clust(): Initialize n_core_null_bytes to NO_CORE_NULL_BYTES if table->supports_instant(). row_create_index_for_mysql(): Initialize n_core_null_bytes for CREATE TEMPORARY TABLE. commit_cache_norebuild(): Call the code to rename or enlarge columns in the cache only if instant ADD COLUMN is not being used. (Instant ADD COLUMN would copy all column metadata from instant_table to old_table, including the names and lengths.) PAGE_INSTANT: A new 13-bit field for storing dict_index_t::n_core_fields. This is repurposing the 16-bit field PAGE_DIRECTION, of which only the least significant 3 bits were used. The original byte containing PAGE_DIRECTION will be accessible via the new constant PAGE_DIRECTION_B. page_get_instant(), page_set_instant(): Accessors for the PAGE_INSTANT. page_ptr_get_direction(), page_get_direction(), page_ptr_set_direction(): Accessors for PAGE_DIRECTION. page_direction_reset(): Reset PAGE_DIRECTION, PAGE_N_DIRECTION. page_direction_increment(): Increment PAGE_N_DIRECTION and set PAGE_DIRECTION. rec_get_offsets(): Use the 'leaf' parameter for non-debug purposes, and assume that heap_no is always set. Initialize all dict_index_t::n_fields for ROW_FORMAT=REDUNDANT records, even if the record contains fewer fields. rec_offs_make_valid(): Add the parameter 'leaf'. rec_copy_prefix_to_dtuple(): Assert that the tuple is only built on the core fields. Instant ADD COLUMN only applies to the clustered index, and we should never build a search key that has more than the PRIMARY KEY and possibly DB_TRX_ID,DB_ROLL_PTR. All these columns are always present. dict_index_build_data_tuple(): Remove assertions that would be duplicated in rec_copy_prefix_to_dtuple(). rec_init_offsets(): Support ROW_FORMAT=REDUNDANT records whose number of fields is between n_core_fields and n_fields. cmp_rec_rec_with_match(): Implement the comparison between two MIN_REC_FLAG records. trx_t::in_rollback: Make the field available in non-debug builds. trx_start_for_ddl_low(): Remove dangerous error-tolerance. A dictionary transaction must be flagged as such before it has generated any undo log records. This is because trx_undo_assign_undo() will mark the transaction as a dictionary transaction in the undo log header right before the very first undo log record is being written. btr_index_rec_validate(): Account for instant ADD COLUMN row_undo_ins_remove_clust_rec(): On the rollback of an insert into SYS_COLUMNS, revert instant ADD COLUMN in the cache by removing the last column from the table and the clustered index. row_search_on_row_ref(), row_undo_mod_parse_undo_rec(), row_undo_mod(), trx_undo_update_rec_get_update(): Handle the 'default row' as a special case. dtuple_t::trim(index): Omit a redundant suffix of an index tuple right before insert or update. After instant ADD COLUMN, if the last fields of a clustered index tuple match the 'default row', there is no need to store them. While trimming the entry, we must hold a page latch, so that the table cannot be emptied and the 'default row' be deleted. btr_cur_optimistic_update(), btr_cur_pessimistic_update(), row_upd_clust_rec_by_insert(), row_ins_clust_index_entry_low(): Invoke dtuple_t::trim() if needed. row_ins_clust_index_entry(): Restore dtuple_t::n_fields after calling row_ins_clust_index_entry_low(). rec_get_converted_size(), rec_get_converted_size_comp(): Allow the number of fields to be between n_core_fields and n_fields. Do not support infimum,supremum. They are never supposed to be stored in dtuple_t, because page creation nowadays uses a lower-level method for initializing them. rec_convert_dtuple_to_rec_comp(): Assign the status bits based on the number of fields. btr_cur_trim(): In an update, trim the index entry as needed. For the 'default row', handle rollback specially. For user records, omit fields that match the 'default row'. btr_cur_optimistic_delete_func(), btr_cur_pessimistic_delete(): Skip locking and adaptive hash index for the 'default row'. row_log_table_apply_convert_mrec(): Replace 'default row' values if needed. In the temporary file that is applied by row_log_table_apply(), we must identify whether the records contain the extra header for instantly added columns. For now, we will allocate an additional byte for this for ROW_T_INSERT and ROW_T_UPDATE records when the source table has been subject to instant ADD COLUMN. The ROW_T_DELETE records are fine, as they will be converted and will only contain 'core' columns (PRIMARY KEY and some system columns) that are converted from dtuple_t. rec_get_converted_size_temp(), rec_init_offsets_temp(), rec_convert_dtuple_to_temp(): Add the parameter 'status'. REC_INFO_DEFAULT_ROW = REC_INFO_MIN_REC_FLAG | REC_STATUS_COLUMNS_ADDED: An info_bits constant for distinguishing the 'default row' record. rec_comp_status_t: An enum of the status bit values. rec_leaf_format: An enum that replaces the bool parameter of rec_init_offsets_comp_ordinary().
690 lines
22 KiB
C++
690 lines
22 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
|
Copyright (c) 2017, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/********************************************************************//**
|
|
@file include/data0data.h
|
|
SQL data field and tuple
|
|
|
|
Created 5/30/1994 Heikki Tuuri
|
|
*************************************************************************/
|
|
|
|
#ifndef data0data_h
|
|
#define data0data_h
|
|
|
|
#include "univ.i"
|
|
|
|
#include "data0types.h"
|
|
#include "data0type.h"
|
|
#include "mem0mem.h"
|
|
#include "dict0types.h"
|
|
|
|
#include <ostream>
|
|
|
|
/** Storage for overflow data in a big record, that is, a clustered
|
|
index record which needs external storage of data fields */
|
|
struct big_rec_t;
|
|
struct upd_t;
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/*********************************************************************//**
|
|
Gets pointer to the type struct of SQL data field.
|
|
@return pointer to the type struct */
|
|
UNIV_INLINE
|
|
dtype_t*
|
|
dfield_get_type(
|
|
/*============*/
|
|
const dfield_t* field) /*!< in: SQL data field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Gets pointer to the data in a field.
|
|
@return pointer to data */
|
|
UNIV_INLINE
|
|
void*
|
|
dfield_get_data(
|
|
/*============*/
|
|
const dfield_t* field) /*!< in: field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
#else /* UNIV_DEBUG */
|
|
# define dfield_get_type(field) (&(field)->type)
|
|
# define dfield_get_data(field) ((field)->data)
|
|
#endif /* UNIV_DEBUG */
|
|
/*********************************************************************//**
|
|
Sets the type struct of SQL data field. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_type(
|
|
/*============*/
|
|
dfield_t* field, /*!< in: SQL data field */
|
|
const dtype_t* type); /*!< in: pointer to data type struct */
|
|
|
|
/*********************************************************************//**
|
|
Gets length of field data.
|
|
@return length of data; UNIV_SQL_NULL if SQL null data */
|
|
UNIV_INLINE
|
|
ulint
|
|
dfield_get_len(
|
|
/*===========*/
|
|
const dfield_t* field) /*!< in: field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Sets length in a field. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_len(
|
|
/*===========*/
|
|
dfield_t* field, /*!< in: field */
|
|
ulint len) /*!< in: length or UNIV_SQL_NULL */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*********************************************************************//**
|
|
Determines if a field is SQL NULL
|
|
@return nonzero if SQL null data */
|
|
UNIV_INLINE
|
|
ulint
|
|
dfield_is_null(
|
|
/*===========*/
|
|
const dfield_t* field) /*!< in: field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Determines if a field is externally stored
|
|
@return nonzero if externally stored */
|
|
UNIV_INLINE
|
|
ulint
|
|
dfield_is_ext(
|
|
/*==========*/
|
|
const dfield_t* field) /*!< in: field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Sets the "external storage" flag */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_ext(
|
|
/*===========*/
|
|
dfield_t* field) /*!< in/out: field */
|
|
MY_ATTRIBUTE((nonnull));
|
|
|
|
/** Gets spatial status for "external storage"
|
|
@param[in,out] field field */
|
|
UNIV_INLINE
|
|
spatial_status_t
|
|
dfield_get_spatial_status(
|
|
const dfield_t* field);
|
|
|
|
/** Sets spatial status for "external storage"
|
|
@param[in,out] field field
|
|
@param[in] spatial_status spatial status */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_spatial_status(
|
|
dfield_t* field,
|
|
spatial_status_t spatial_status);
|
|
|
|
/*********************************************************************//**
|
|
Sets pointer to the data and length in a field. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_data(
|
|
/*============*/
|
|
dfield_t* field, /*!< in: field */
|
|
const void* data, /*!< in: data */
|
|
ulint len) /*!< in: length or UNIV_SQL_NULL */
|
|
MY_ATTRIBUTE((nonnull(1)));
|
|
/*********************************************************************//**
|
|
Sets pointer to the data and length in a field. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_write_mbr(
|
|
/*=============*/
|
|
dfield_t* field, /*!< in: field */
|
|
const double* mbr) /*!< in: data */
|
|
MY_ATTRIBUTE((nonnull(1)));
|
|
/*********************************************************************//**
|
|
Sets a data field to SQL NULL. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_set_null(
|
|
/*============*/
|
|
dfield_t* field) /*!< in/out: field */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/**********************************************************************//**
|
|
Writes an SQL null field full of zeros. */
|
|
UNIV_INLINE
|
|
void
|
|
data_write_sql_null(
|
|
/*================*/
|
|
byte* data, /*!< in: pointer to a buffer of size len */
|
|
ulint len) /*!< in: SQL null size in bytes */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*********************************************************************//**
|
|
Copies the data and len fields. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_copy_data(
|
|
/*=============*/
|
|
dfield_t* field1, /*!< out: field to copy to */
|
|
const dfield_t* field2); /*!< in: field to copy from */
|
|
|
|
/*********************************************************************//**
|
|
Copies a data field to another. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_copy(
|
|
/*========*/
|
|
dfield_t* field1, /*!< out: field to copy to */
|
|
const dfield_t* field2) /*!< in: field to copy from */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*********************************************************************//**
|
|
Copies the data pointed to by a data field. */
|
|
UNIV_INLINE
|
|
void
|
|
dfield_dup(
|
|
/*=======*/
|
|
dfield_t* field, /*!< in/out: data field */
|
|
mem_heap_t* heap) /*!< in: memory heap where allocated */
|
|
MY_ATTRIBUTE((nonnull));
|
|
|
|
/*********************************************************************//**
|
|
Tests if two data fields are equal.
|
|
If len==0, tests the data length and content for equality.
|
|
If len>0, tests the first len bytes of the content for equality.
|
|
@return TRUE if both fields are NULL or if they are equal */
|
|
UNIV_INLINE
|
|
ibool
|
|
dfield_datas_are_binary_equal(
|
|
/*==========================*/
|
|
const dfield_t* field1, /*!< in: field */
|
|
const dfield_t* field2, /*!< in: field */
|
|
ulint len) /*!< in: maximum prefix to compare,
|
|
or 0 to compare the whole field length */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Tests if dfield data length and content is equal to the given.
|
|
@return TRUE if equal */
|
|
UNIV_INLINE
|
|
ibool
|
|
dfield_data_is_binary_equal(
|
|
/*========================*/
|
|
const dfield_t* field, /*!< in: field */
|
|
ulint len, /*!< in: data length or UNIV_SQL_NULL */
|
|
const byte* data) /*!< in: data */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Gets number of fields in a data tuple.
|
|
@return number of fields */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_n_fields(
|
|
/*================*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/** Gets number of virtual fields in a data tuple.
|
|
@param[in] tuple dtuple to check
|
|
@return number of fields */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_n_v_fields(
|
|
const dtuple_t* tuple);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Gets nth field of a tuple.
|
|
@param[in] tuple tuple
|
|
@param[in] n index of field
|
|
@return nth field */
|
|
UNIV_INLINE
|
|
dfield_t*
|
|
dtuple_get_nth_field(
|
|
const dtuple_t* tuple,
|
|
ulint n);
|
|
/** Gets nth virtual field of a tuple.
|
|
@param[in] tuple tuple
|
|
@oaran[in] n the nth field to get
|
|
@return nth field */
|
|
UNIV_INLINE
|
|
dfield_t*
|
|
dtuple_get_nth_v_field(
|
|
const dtuple_t* tuple,
|
|
ulint n);
|
|
#else /* UNIV_DEBUG */
|
|
# define dtuple_get_nth_field(tuple, n) ((tuple)->fields + (n))
|
|
# define dtuple_get_nth_v_field(tuple, n) ((tuple)->fields + (tuple)->n_fields + (n))
|
|
#endif /* UNIV_DEBUG */
|
|
/*********************************************************************//**
|
|
Gets info bits in a data tuple.
|
|
@return info bits */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_info_bits(
|
|
/*=================*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Sets info bits in a data tuple. */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_set_info_bits(
|
|
/*=================*/
|
|
dtuple_t* tuple, /*!< in: tuple */
|
|
ulint info_bits) /*!< in: info bits */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*********************************************************************//**
|
|
Gets number of fields used in record comparisons.
|
|
@return number of fields used in comparisons in rem0cmp.* */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_n_fields_cmp(
|
|
/*====================*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/*********************************************************************//**
|
|
Gets number of fields used in record comparisons. */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_set_n_fields_cmp(
|
|
/*====================*/
|
|
dtuple_t* tuple, /*!< in: tuple */
|
|
ulint n_fields_cmp) /*!< in: number of fields used in
|
|
comparisons in rem0cmp.* */
|
|
MY_ATTRIBUTE((nonnull));
|
|
|
|
/* Estimate the number of bytes that are going to be allocated when
|
|
creating a new dtuple_t object */
|
|
#define DTUPLE_EST_ALLOC(n_fields) \
|
|
(sizeof(dtuple_t) + (n_fields) * sizeof(dfield_t))
|
|
|
|
/** Creates a data tuple from an already allocated chunk of memory.
|
|
The size of the chunk must be at least DTUPLE_EST_ALLOC(n_fields).
|
|
The default value for number of fields used in record comparisons
|
|
for this tuple is n_fields.
|
|
@param[in,out] buf buffer to use
|
|
@param[in] buf_size buffer size
|
|
@param[in] n_fields number of field
|
|
@param[in] n_v_fields number of fields on virtual columns
|
|
@return created tuple (inside buf) */
|
|
UNIV_INLINE
|
|
dtuple_t*
|
|
dtuple_create_from_mem(
|
|
void* buf,
|
|
ulint buf_size,
|
|
ulint n_fields,
|
|
ulint n_v_fields)
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
|
|
/**********************************************************//**
|
|
Creates a data tuple to a memory heap. The default value for number
|
|
of fields used in record comparisons for this tuple is n_fields.
|
|
@return own: created tuple */
|
|
UNIV_INLINE
|
|
dtuple_t*
|
|
dtuple_create(
|
|
/*==========*/
|
|
mem_heap_t* heap, /*!< in: memory heap where the tuple
|
|
is created, DTUPLE_EST_ALLOC(n_fields)
|
|
bytes will be allocated from this heap */
|
|
ulint n_fields)/*!< in: number of fields */
|
|
MY_ATTRIBUTE((nonnull, malloc));
|
|
|
|
/** Initialize the virtual field data in a dtuple_t
|
|
@param[in,out] vrow dtuple contains the virtual fields */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_init_v_fld(
|
|
const dtuple_t* vrow);
|
|
|
|
/** Duplicate the virtual field data in a dtuple_t
|
|
@param[in,out] vrow dtuple contains the virtual fields
|
|
@param[in] heap heap memory to use */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_dup_v_fld(
|
|
const dtuple_t* vrow,
|
|
mem_heap_t* heap);
|
|
|
|
/** Creates a data tuple with possible virtual columns to a memory heap.
|
|
@param[in] heap memory heap where the tuple is created
|
|
@param[in] n_fields number of fields
|
|
@param[in] n_v_fields number of fields on virtual col
|
|
@return own: created tuple */
|
|
UNIV_INLINE
|
|
dtuple_t*
|
|
dtuple_create_with_vcol(
|
|
mem_heap_t* heap,
|
|
ulint n_fields,
|
|
ulint n_v_fields);
|
|
|
|
/*********************************************************************//**
|
|
Sets number of fields used in a tuple. Normally this is set in
|
|
dtuple_create, but if you want later to set it smaller, you can use this. */
|
|
void
|
|
dtuple_set_n_fields(
|
|
/*================*/
|
|
dtuple_t* tuple, /*!< in: tuple */
|
|
ulint n_fields) /*!< in: number of fields */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/** Copies a data tuple's virtaul fields to another. This is a shallow copy;
|
|
@param[in,out] d_tuple destination tuple
|
|
@param[in] s_tuple source tuple */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_copy_v_fields(
|
|
dtuple_t* d_tuple,
|
|
const dtuple_t* s_tuple);
|
|
/*********************************************************************//**
|
|
Copies a data tuple to another. This is a shallow copy; if a deep copy
|
|
is desired, dfield_dup() will have to be invoked on each field.
|
|
@return own: copy of tuple */
|
|
UNIV_INLINE
|
|
dtuple_t*
|
|
dtuple_copy(
|
|
/*========*/
|
|
const dtuple_t* tuple, /*!< in: tuple to copy from */
|
|
mem_heap_t* heap) /*!< in: memory heap
|
|
where the tuple is created */
|
|
MY_ATTRIBUTE((nonnull, malloc));
|
|
/**********************************************************//**
|
|
The following function returns the sum of data lengths of a tuple. The space
|
|
occupied by the field structs or the tuple struct is not counted.
|
|
@return sum of data lens */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_data_size(
|
|
/*=================*/
|
|
const dtuple_t* tuple, /*!< in: typed data tuple */
|
|
ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*********************************************************************//**
|
|
Computes the number of externally stored fields in a data tuple.
|
|
@return number of fields */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_get_n_ext(
|
|
/*=============*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/** Compare two data tuples.
|
|
@param[in] tuple1 first data tuple
|
|
@param[in] tuple2 second data tuple
|
|
@return positive, 0, negative if tuple1 is greater, equal, less, than tuple2,
|
|
respectively */
|
|
int
|
|
dtuple_coll_cmp(
|
|
const dtuple_t* tuple1,
|
|
const dtuple_t* tuple2)
|
|
MY_ATTRIBUTE((warn_unused_result));
|
|
/** Fold a prefix given as the number of fields of a tuple.
|
|
@param[in] tuple index record
|
|
@param[in] n_fields number of complete fields to fold
|
|
@param[in] n_bytes number of bytes to fold in the last field
|
|
@param[in] index_id index tree ID
|
|
@return the folded value */
|
|
UNIV_INLINE
|
|
ulint
|
|
dtuple_fold(
|
|
const dtuple_t* tuple,
|
|
ulint n_fields,
|
|
ulint n_bytes,
|
|
index_id_t tree_id)
|
|
MY_ATTRIBUTE((warn_unused_result));
|
|
/*******************************************************************//**
|
|
Sets types of fields binary in a tuple. */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_set_types_binary(
|
|
/*====================*/
|
|
dtuple_t* tuple, /*!< in: data tuple */
|
|
ulint n) /*!< in: number of fields to set */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/**********************************************************************//**
|
|
Checks if a dtuple contains an SQL null value.
|
|
@return TRUE if some field is SQL null */
|
|
UNIV_INLINE
|
|
ibool
|
|
dtuple_contains_null(
|
|
/*=================*/
|
|
const dtuple_t* tuple) /*!< in: dtuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/**********************************************************//**
|
|
Checks that a data field is typed. Asserts an error if not.
|
|
@return TRUE if ok */
|
|
ibool
|
|
dfield_check_typed(
|
|
/*===============*/
|
|
const dfield_t* field) /*!< in: data field */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
/**********************************************************//**
|
|
Checks that a data tuple is typed. Asserts an error if not.
|
|
@return TRUE if ok */
|
|
ibool
|
|
dtuple_check_typed(
|
|
/*===============*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
#ifdef UNIV_DEBUG
|
|
/**********************************************************//**
|
|
Validates the consistency of a tuple which must be complete, i.e,
|
|
all fields must have been set.
|
|
@return TRUE if ok */
|
|
ibool
|
|
dtuple_validate(
|
|
/*============*/
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
#endif /* UNIV_DEBUG */
|
|
/*************************************************************//**
|
|
Pretty prints a dfield value according to its data type. */
|
|
void
|
|
dfield_print(
|
|
/*=========*/
|
|
const dfield_t* dfield) /*!< in: dfield */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/*************************************************************//**
|
|
Pretty prints a dfield value according to its data type. Also the hex string
|
|
is printed if a string contains non-printable characters. */
|
|
void
|
|
dfield_print_also_hex(
|
|
/*==================*/
|
|
const dfield_t* dfield) /*!< in: dfield */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/**********************************************************//**
|
|
The following function prints the contents of a tuple. */
|
|
void
|
|
dtuple_print(
|
|
/*=========*/
|
|
FILE* f, /*!< in: output stream */
|
|
const dtuple_t* tuple) /*!< in: tuple */
|
|
MY_ATTRIBUTE((nonnull));
|
|
|
|
/** Print the contents of a tuple.
|
|
@param[out] o output stream
|
|
@param[in] field array of data fields
|
|
@param[in] n number of data fields */
|
|
void
|
|
dfield_print(
|
|
std::ostream& o,
|
|
const dfield_t* field,
|
|
ulint n);
|
|
/** Print the contents of a tuple.
|
|
@param[out] o output stream
|
|
@param[in] tuple data tuple */
|
|
void
|
|
dtuple_print(
|
|
std::ostream& o,
|
|
const dtuple_t* tuple);
|
|
|
|
/** Print the contents of a tuple.
|
|
@param[out] o output stream
|
|
@param[in] tuple data tuple */
|
|
inline
|
|
std::ostream&
|
|
operator<<(std::ostream& o, const dtuple_t& tuple)
|
|
{
|
|
dtuple_print(o, &tuple);
|
|
return(o);
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Moves parts of long fields in entry to the big record vector so that
|
|
the size of tuple drops below the maximum record size allowed in the
|
|
database. Moves data only from those fields which are not necessary
|
|
to determine uniquely the insertion place of the tuple in the index.
|
|
@return own: created big record vector, NULL if we are not able to
|
|
shorten the entry enough, i.e., if there are too many fixed-length or
|
|
short fields in entry or the index is clustered */
|
|
big_rec_t*
|
|
dtuple_convert_big_rec(
|
|
/*===================*/
|
|
dict_index_t* index, /*!< in: index */
|
|
upd_t* upd, /*!< in/out: update vector */
|
|
dtuple_t* entry, /*!< in/out: index entry */
|
|
ulint* n_ext) /*!< in/out: number of
|
|
externally stored columns */
|
|
MY_ATTRIBUTE((malloc, warn_unused_result));
|
|
/**************************************************************//**
|
|
Puts back to entry the data stored in vector. Note that to ensure the
|
|
fields in entry can accommodate the data, vector must have been created
|
|
from entry with dtuple_convert_big_rec. */
|
|
void
|
|
dtuple_convert_back_big_rec(
|
|
/*========================*/
|
|
dict_index_t* index, /*!< in: index */
|
|
dtuple_t* entry, /*!< in: entry whose data was put to vector */
|
|
big_rec_t* vector) /*!< in, own: big rec vector; it is
|
|
freed in this function */
|
|
MY_ATTRIBUTE((nonnull));
|
|
/**************************************************************//**
|
|
Frees the memory in a big rec vector. */
|
|
UNIV_INLINE
|
|
void
|
|
dtuple_big_rec_free(
|
|
/*================*/
|
|
big_rec_t* vector) /*!< in, own: big rec vector; it is
|
|
freed in this function */
|
|
MY_ATTRIBUTE((nonnull));
|
|
|
|
/*######################################################################*/
|
|
|
|
/** Structure for an SQL data field */
|
|
struct dfield_t{
|
|
void* data; /*!< pointer to data */
|
|
unsigned ext:1; /*!< TRUE=externally stored, FALSE=local */
|
|
unsigned spatial_status:2;
|
|
/*!< spatial status of externally stored field
|
|
in undo log for purge */
|
|
unsigned len; /*!< data length; UNIV_SQL_NULL if SQL null */
|
|
dtype_t type; /*!< type of data */
|
|
|
|
/** Create a deep copy of this object.
|
|
@param[in,out] heap memory heap in which the clone will be created
|
|
@return the cloned object */
|
|
dfield_t* clone(mem_heap_t* heap) const;
|
|
};
|
|
|
|
/** Structure for an SQL data tuple of fields (logical record) */
|
|
struct dtuple_t {
|
|
ulint info_bits; /*!< info bits of an index record:
|
|
the default is 0; this field is used
|
|
if an index record is built from
|
|
a data tuple */
|
|
ulint n_fields; /*!< number of fields in dtuple */
|
|
ulint n_fields_cmp; /*!< number of fields which should
|
|
be used in comparison services
|
|
of rem0cmp.*; the index search
|
|
is performed by comparing only these
|
|
fields, others are ignored; the
|
|
default value in dtuple creation is
|
|
the same value as n_fields */
|
|
dfield_t* fields; /*!< fields */
|
|
ulint n_v_fields; /*!< number of virtual fields */
|
|
dfield_t* v_fields; /*!< fields on virtual column */
|
|
UT_LIST_NODE_T(dtuple_t) tuple_list;
|
|
/*!< data tuples can be linked into a
|
|
list using this field */
|
|
#ifdef UNIV_DEBUG
|
|
ulint magic_n; /*!< magic number, used in
|
|
debug assertions */
|
|
/** Value of dtuple_t::magic_n */
|
|
# define DATA_TUPLE_MAGIC_N 65478679
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/** Trim the tail of an index tuple before insert or update.
|
|
After instant ADD COLUMN, if the last fields of a clustered index tuple
|
|
match the 'default row', there will be no need to store them.
|
|
NOTE: A page latch in the index must be held, so that the index
|
|
may not lose 'instantness' before the trimmed tuple has been
|
|
inserted or updated.
|
|
@param[in] index index possibly with instantly added columns */
|
|
void trim(const dict_index_t& index);
|
|
};
|
|
|
|
/** A slot for a field in a big rec vector */
|
|
struct big_rec_field_t {
|
|
|
|
/** Constructor.
|
|
@param[in] field_no_ the field number
|
|
@param[in] len_ the data length
|
|
@param[in] data_ the data */
|
|
big_rec_field_t(ulint field_no_, ulint len_, const void* data_)
|
|
: field_no(field_no_),
|
|
len(len_),
|
|
data(data_)
|
|
{}
|
|
|
|
ulint field_no; /*!< field number in record */
|
|
ulint len; /*!< stored data length, in bytes */
|
|
const void* data; /*!< stored data */
|
|
};
|
|
|
|
/** Storage format for overflow data in a big record, that is, a
|
|
clustered index record which needs external storage of data fields */
|
|
struct big_rec_t {
|
|
mem_heap_t* heap; /*!< memory heap from which
|
|
allocated */
|
|
const ulint capacity; /*!< fields array size */
|
|
ulint n_fields; /*!< number of stored fields */
|
|
big_rec_field_t*fields; /*!< stored fields */
|
|
|
|
/** Constructor.
|
|
@param[in] max the capacity of the array of fields. */
|
|
explicit big_rec_t(const ulint max)
|
|
: heap(0),
|
|
capacity(max),
|
|
n_fields(0),
|
|
fields(0)
|
|
{}
|
|
|
|
/** Append one big_rec_field_t object to the end of array of fields */
|
|
void append(const big_rec_field_t& field)
|
|
{
|
|
ut_ad(n_fields < capacity);
|
|
fields[n_fields] = field;
|
|
n_fields++;
|
|
}
|
|
|
|
/** Allocate a big_rec_t object in the given memory heap, and for
|
|
storing n_fld number of fields.
|
|
@param[in] heap memory heap in which this object is allocated
|
|
@param[in] n_fld maximum number of fields that can be stored in
|
|
this object
|
|
@return the allocated object */
|
|
static big_rec_t* alloc(
|
|
mem_heap_t* heap,
|
|
ulint n_fld);
|
|
};
|
|
|
|
#include "data0data.ic"
|
|
|
|
#endif
|