diff options
Diffstat (limited to 'storage/innobase/include')
23 files changed, 803 insertions, 611 deletions
diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index 1d7710a1496..cff8bc7cbc9 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -680,6 +680,20 @@ btr_page_free( buf_block_t* block, /*!< in: block to be freed, x-latched */ mtr_t* mtr) /*!< in: mtr */ MY_ATTRIBUTE((nonnull)); +/** Empty an index page (possibly the root page). @see btr_page_create(). +@param[in,out] block page to be emptied +@param[in,out] page_zip compressed page frame, or NULL +@param[in] index index of the page +@param[in] level B-tree level of the page (0=leaf) +@param[in,out] mtr mini-transaction */ +void +btr_page_empty( + buf_block_t* block, + page_zip_des_t* page_zip, + dict_index_t* index, + ulint level, + mtr_t* mtr) + MY_ATTRIBUTE((nonnull(1, 3, 5))); /**************************************************************//** Creates a new index page (not the root, and also not used in page reorganization). @see btr_page_empty(). */ diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index e62a5e90ce2..0445d0ef59c 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -132,6 +132,24 @@ btr_cur_position( buf_block_t* block, /*!< in: buffer block of rec */ btr_cur_t* cursor);/*!< in: cursor */ +/** Load the instant ALTER TABLE metadata from the clustered index +when loading a table definition. +@param[in,out] table table definition from the data dictionary +@return error code +@retval DB_SUCCESS if no error occurred */ +dberr_t +btr_cur_instant_init(dict_table_t* table) + ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)); + +/** Initialize the n_core_null_bytes on first access to a clustered +index root page. +@param[in] index clustered index that is on its first access +@param[in] page clustered index root page +@return whether the page is corrupted */ +bool +btr_cur_instant_root_init(dict_index_t* index, const page_t* page) + ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)); + /** Optimistically latches the leaf page or pages requested. @param[in] block guessed buffer block @param[in] modify_clock modify clock value diff --git a/storage/innobase/include/btr0cur.ic b/storage/innobase/include/btr0cur.ic index b1e59651a1d..56868cca336 100644 --- a/storage/innobase/include/btr0cur.ic +++ b/storage/innobase/include/btr0cur.ic @@ -28,7 +28,7 @@ Created 10/16/1994 Heikki Tuuri #ifdef UNIV_DEBUG # define LIMIT_OPTIMISTIC_INSERT_DEBUG(NREC, CODE)\ if (btr_cur_limit_optimistic_insert_debug > 1\ - && (NREC) >= (ulint)btr_cur_limit_optimistic_insert_debug) {\ + && (NREC) >= btr_cur_limit_optimistic_insert_debug) {\ CODE;\ } #else @@ -134,7 +134,7 @@ btr_cur_compress_recommendation( page = btr_cur_get_page(cursor); - LIMIT_OPTIMISTIC_INSERT_DEBUG(page_get_n_recs(page) * 2, + LIMIT_OPTIMISTIC_INSERT_DEBUG(page_get_n_recs(page) * 2U, return(FALSE)); if ((page_get_data_size(page) diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index fad0dac93c4..bd1a72fc3ac 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -106,19 +107,16 @@ btr_search_guess_on_hash( ulint has_search_latch, mtr_t* mtr); -/** Moves or deletes hash entries for moved records. If new_page is already -hashed, then the hash index for page, if any, is dropped. If new_page is not -hashed, and page is hashed, then a new hash index is built to new_page with the -same parameters as page (this often happens when a page is split). -@param[in,out] new_block records are copied to this page. -@param[in,out] block index page from which record are copied, and the - copied records will be deleted from this page. -@param[in,out] index record descriptor */ +/** Move or delete hash entries for moved records, usually in a page split. +If new_block is already hashed, then any hash index for block is dropped. +If new_block is not hashed, and block is hashed, then a new hash index is +built to new_block with the same parameters as block. +@param[in,out] new_block destination page +@param[in,out] block source page (subject to deletion later) */ void btr_search_move_or_delete_hash_entries( buf_block_t* new_block, - buf_block_t* block, - dict_index_t* index); + buf_block_t* block); /** Drop any adaptive hash index entries that point to an index page. @param[in,out] block block containing index page, s- or x-latched, or an @@ -252,7 +250,7 @@ btr_get_search_table(const dict_index_t* index); # define btr_search_x_lock(index) # define btr_search_x_unlock(index) # define btr_search_info_update(index, cursor) -# define btr_search_move_or_delete_hash_entries(new_block, block, index) +# define btr_search_move_or_delete_hash_entries(new_block, block) # define btr_search_update_hash_on_insert(cursor) # define btr_search_update_hash_on_delete(cursor) # define btr_search_sys_resize(hash_size) diff --git a/storage/innobase/include/data0data.h b/storage/innobase/include/data0data.h index b6187d46025..a0b3059ad40 100644 --- a/storage/innobase/include/data0data.h +++ b/storage/innobase/include/data0data.h @@ -619,6 +619,15 @@ struct dtuple_t { /** 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 */ diff --git a/storage/innobase/include/data0data.ic b/storage/innobase/include/data0data.ic index 81788885aa5..310902f5166 100644 --- a/storage/innobase/include/data0data.ic +++ b/storage/innobase/include/data0data.ic @@ -94,6 +94,7 @@ dfield_get_len( ut_ad(field); ut_ad((field->len == UNIV_SQL_NULL) || (field->data != &data_error)); + ut_ad(field->len != UNIV_SQL_DEFAULT); return(field->len); } @@ -108,6 +109,7 @@ dfield_set_len( ulint len) /*!< in: length or UNIV_SQL_NULL */ { ut_ad(field); + ut_ad(len != UNIV_SQL_DEFAULT); #ifdef UNIV_VALGRIND_DEBUG if (len != UNIV_SQL_NULL) UNIV_MEM_ASSERT_RW(field->data, len); #endif /* UNIV_VALGRIND_DEBUG */ @@ -326,6 +328,7 @@ dfield_data_is_binary_equal( ulint len, /*!< in: data length or UNIV_SQL_NULL */ const byte* data) /*!< in: data */ { + ut_ad(len != UNIV_SQL_DEFAULT); return(len == dfield_get_len(field) && (len == UNIV_SQL_NULL || !memcmp(dfield_get_data(field), data, len))); diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index a7c7bc92ee9..bd2a15fe881 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -29,6 +29,12 @@ Created 1/16/1996 Heikki Tuuri #include "univ.i" +/** Special length indicating a missing instantly added column */ +#define UNIV_SQL_DEFAULT (UNIV_SQL_NULL - 1) + +/** @return whether a length is actually stored in a field */ +#define len_is_stored(len) (len != UNIV_SQL_NULL && len != UNIV_SQL_DEFAULT) + extern ulint data_mysql_default_charset_coll; #define DATA_MYSQL_BINARY_CHARSET_COLL 63 diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 736419f9dd7..03175936f7e 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -387,15 +387,6 @@ dict_table_add_system_columns( mem_heap_t* heap) /*!< in: temporary heap */ MY_ATTRIBUTE((nonnull)); /**********************************************************************//** -Adds a table object to the dictionary cache. */ -void -dict_table_add_to_cache( -/*====================*/ - dict_table_t* table, /*!< in: table */ - bool can_be_evicted, /*!< in: whether can be evicted*/ - mem_heap_t* heap) /*!< in: temporary heap */ - MY_ATTRIBUTE((nonnull)); -/**********************************************************************//** Removes a table object from the dictionary cache. */ void dict_table_remove_from_cache( @@ -589,16 +580,6 @@ dict_foreign_find_index( happened */ MY_ATTRIBUTE((nonnull(1,3), warn_unused_result)); -/**********************************************************************//** -Returns a column's name. -@return column name. NOTE: not guaranteed to stay valid if table is -modified in any way (columns added, etc.). */ -const char* -dict_table_get_col_name( -/*====================*/ - const dict_table_t* table, /*!< in: table */ - ulint col_nr) /*!< in: column number */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); /** Returns a virtual column's name. @param[in] table table object @@ -920,6 +901,18 @@ dict_table_get_sys_col( /* Get nth virtual columns */ #define dict_table_get_nth_v_col(table, pos) (&(table)->v_cols[pos]) #endif /* UNIV_DEBUG */ +/** Wrapper function. +@see dict_col_t::name() +@param[in] table table +@param[in] col_nr column number in table +@return column name */ +inline +const char* +dict_table_get_col_name(const dict_table_t* table, ulint col_nr) +{ + return(dict_table_get_nth_col(table, col_nr)->name(*table)); +} + /********************************************************************//** Gets the given system column number of a table. @return column number */ @@ -1163,6 +1156,7 @@ dict_index_get_n_fields( representation of index (in the dictionary cache) */ MY_ATTRIBUTE((nonnull, warn_unused_result)); + /********************************************************************//** Gets the number of fields in the internal representation of an index that uniquely determine the position of an index entry in the index, if @@ -1451,22 +1445,13 @@ dict_index_copy_rec_order_prefix( @param[in,out] heap memory heap for allocation @return own: data tuple */ dtuple_t* -dict_index_build_data_tuple_func( +dict_index_build_data_tuple( const rec_t* rec, const dict_index_t* index, -#ifdef UNIV_DEBUG bool leaf, -#endif /* UNIV_DEBUG */ ulint n_fields, mem_heap_t* heap) MY_ATTRIBUTE((nonnull, warn_unused_result)); -#ifdef UNIV_DEBUG -# define dict_index_build_data_tuple(rec, index, leaf, n_fields, heap) \ - dict_index_build_data_tuple_func(rec, index, leaf, n_fields, heap) -#else /* UNIV_DEBUG */ -# define dict_index_build_data_tuple(rec, index, leaf, n_fields, heap) \ - dict_index_build_data_tuple_func(rec, index, n_fields, heap) -#endif /* UNIV_DEBUG */ /*********************************************************************//** Gets the space id of the root of the index tree. @@ -1978,13 +1963,7 @@ dict_index_node_ptr_max_size( /*=========================*/ const dict_index_t* index) /*!< in: index */ MY_ATTRIBUTE((warn_unused_result)); -/** Check if a column is a virtual column -@param[in] col column -@return true if it is a virtual column, false otherwise */ -UNIV_INLINE -bool -dict_col_is_virtual( - const dict_col_t* col); +#define dict_col_is_virtual(col) (col)->is_virtual() /** encode number of columns and number of virtual columns in one 4 bytes value. We could do this because the number of columns in diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index 134a4d63066..06cd2434942 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -89,16 +89,6 @@ dict_col_copy_type( type->len = col->len; type->mbminmaxlen = col->mbminmaxlen; } -/** Check if a column is a virtual column -@param[in] col column -@return true if it is a virtual column, false otherwise */ -UNIV_INLINE -bool -dict_col_is_virtual( - const dict_col_t* col) -{ - return(col->prtype & DATA_VIRTUAL); -} #ifdef UNIV_DEBUG /*********************************************************************//** @@ -296,8 +286,7 @@ dict_index_is_clust( const dict_index_t* index) /*!< in: index */ { ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - - return(index->type & DICT_CLUSTERED); + return(index->is_clust()); } /** Check if index is auto-generated clustered index. @@ -547,8 +536,8 @@ dict_table_get_nth_v_col( ut_ad(table); ut_ad(pos < table->n_v_def); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); - - return(static_cast<dict_v_col_t*>(table->v_cols) + pos); + ut_ad(!table->v_cols[pos].m_col.is_instant()); + return &table->v_cols[pos]; } /********************************************************************//** diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 5c285ef215d..152ff3f2fd6 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -612,6 +612,47 @@ struct dict_col_t{ this column. Our current max limit is 3072 (REC_VERSION_56_MAX_INDEX_COL_LEN) bytes. */ + + /** Data for instantly added columns */ + struct { + /** original default value of instantly added column */ + const void* data; + /** len of data, or UNIV_SQL_DEFAULT if unavailable */ + ulint len; + } def_val; + + /** Retrieve the column name. + @param[in] table table name */ + const char* name(const dict_table_t& table) const; + + /** @return whether this is a virtual column */ + bool is_virtual() const { return prtype & DATA_VIRTUAL; } + /** @return whether NULL is an allowed value for this column */ + bool is_nullable() const { return !(prtype & DATA_NOT_NULL); } + /** @return whether this is an instantly-added column */ + bool is_instant() const + { + DBUG_ASSERT(def_val.len != UNIV_SQL_DEFAULT || !def_val.data); + return def_val.len != UNIV_SQL_DEFAULT; + } + /** Get the default value of an instantly-added column. + @param[out] len value length (in bytes), or UNIV_SQL_NULL + @return default value + @retval NULL if the default value is SQL NULL (len=UNIV_SQL_NULL) */ + const byte* instant_value(ulint* len) const + { + DBUG_ASSERT(is_instant()); + *len = def_val.len; + return static_cast<const byte*>(def_val.data); + } + + /** Remove the 'instant ADD' status of the column */ + void remove_instant() + { + DBUG_ASSERT(is_instant()); + def_val.len = UNIV_SQL_DEFAULT; + def_val.data = NULL; + } }; /** Index information put in a list of virtual column structure. Index @@ -623,6 +664,9 @@ struct dict_v_idx_t { /** position in this index */ ulint nth_field; + + dict_v_idx_t(dict_index_t* index, ulint nth_field) + : index(index), nth_field(nth_field) {} }; /** Index list to put in dict_v_col_t */ @@ -726,6 +770,15 @@ struct dict_field_t{ unsigned fixed_len:10; /*!< 0 or the fixed length of the column if smaller than DICT_ANTELOPE_MAX_INDEX_COL_LEN */ + + /** Check whether two index fields are equivalent. + @param[in] old the other index field + @return whether the index fields are equivalent */ + bool same(const dict_field_t& other) const + { + return(prefix_len == other.prefix_len + && fixed_len == other.fixed_len); + } }; /**********************************************************************//** @@ -844,6 +897,15 @@ struct dict_index_t{ 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_core_fields:10;/*!< number of fields in the index + (before the first time of instant add columns) */ + /** number of bytes of null bits in ROW_FORMAT!=REDUNDANT node pointer + records; usually equal to UT_BITS_IN_BYTES(n_nullable), but + can be less in clustered indexes with instant ADD COLUMN */ + unsigned n_core_null_bytes:8; + /** magic value signalling that n_core_null_bytes was not + initialized yet */ + static const unsigned NO_CORE_NULL_BYTES = 0xff; unsigned cached:1;/*!< TRUE if the index object is in the dictionary cache */ unsigned to_be_dropped:1; @@ -970,6 +1032,63 @@ struct dict_index_t{ and the .ibd file is missing, or a page cannot be read or decrypted */ inline bool is_readable() const; + + /** @return whether instant ADD COLUMN is in effect */ + inline bool is_instant() const; + + /** @return whether the index is the clustered index */ + bool is_clust() const { return type & DICT_CLUSTERED; } + + /** Determine how many fields of a given prefix can be set NULL. + @param[in] n_prefix number of fields in the prefix + @return number of fields 0..n_prefix-1 that can be set NULL */ + unsigned get_n_nullable(ulint n_prefix) const + { + DBUG_ASSERT(is_instant()); + DBUG_ASSERT(n_prefix > 0); + DBUG_ASSERT(n_prefix <= n_fields); + unsigned n = n_nullable; + for (; n_prefix < n_fields; n_prefix++) { + const dict_col_t* col = fields[n_prefix].col; + DBUG_ASSERT(is_dummy || col->is_instant()); + DBUG_ASSERT(!col->is_virtual()); + n -= col->is_nullable(); + } + DBUG_ASSERT(n < n_def); + return n; + } + + /** Get the default value of an instantly-added clustered index field. + @param[in] n instantly added field position + @param[out] len value length (in bytes), or UNIV_SQL_NULL + @return default value + @retval NULL if the default value is SQL NULL (len=UNIV_SQL_NULL) */ + const byte* instant_field_value(uint n, ulint* len) const + { + DBUG_ASSERT(is_instant()); + DBUG_ASSERT(n >= n_core_fields); + DBUG_ASSERT(n < n_fields); + return fields[n].col->instant_value(len); + } + + /** Adjust clustered index metadata for instant ADD COLUMN. + @param[in] clustered index definition after instant ADD COLUMN */ + void instant_add_field(const dict_index_t& instant); + + /** Remove the 'instant ADD' status of a clustered index. + Protected by index root page x-latch or table X-lock. */ + void remove_instant() + { + DBUG_ASSERT(is_clust()); + if (!is_instant()) { + return; + } + for (unsigned i = n_core_fields; i < n_fields; i++) { + fields[i].col->remove_instant(); + } + n_core_fields = n_fields; + n_core_null_bytes = UT_BITS_IN_BYTES(n_nullable); + } }; /** The status of online index creation */ @@ -1331,6 +1450,39 @@ struct dict_table_t { return(UNIV_LIKELY(!file_unreadable)); } + /** @return whether instant ADD COLUMN is in effect */ + bool is_instant() const + { + return(UT_LIST_GET_FIRST(indexes)->is_instant()); + } + + /** @return whether the table supports instant ADD COLUMN */ + bool supports_instant() const + { + return(!(flags & DICT_TF_MASK_ZIP_SSIZE)); + } + + /** Adjust metadata for instant ADD COLUMN. + @param[in] table table definition after instant ADD COLUMN */ + void instant_add_column(const dict_table_t& table); + + /** Roll back instant_add_column(). + @param[in] old_n_cols original n_cols + @param[in] old_cols original cols + @param[in] old_col_names original col_names */ + void rollback_instant( + unsigned old_n_cols, + dict_col_t* old_cols, + const char* old_col_names); + + /** Trim the instantly added columns when an insert into SYS_COLUMNS + is rolled back during ALTER TABLE or recovery. + @param[in] n number of surviving non-system columns */ + void rollback_instant(unsigned n); + + /** Add the table definition to the data dictionary cache */ + void add_to_cache(); + /** Id of the table. */ table_id_t id; @@ -1711,6 +1863,17 @@ inline bool dict_index_t::is_readable() const return(UNIV_LIKELY(!table->file_unreadable)); } +inline bool dict_index_t::is_instant() const +{ + ut_ad(n_core_fields > 0); + ut_ad(n_core_fields <= n_fields); + ut_ad(n_core_fields == n_fields + || (type & ~(DICT_UNIQUE | DICT_CORRUPT)) == DICT_CLUSTERED); + ut_ad(n_core_fields == n_fields || table->supports_instant()); + ut_ad(n_core_fields == n_fields || !table->is_temporary()); + return(n_core_fields != n_fields); +} + /*******************************************************************//** Initialise the table lock list. */ void diff --git a/storage/innobase/include/dict0mem.ic b/storage/innobase/include/dict0mem.ic index da2ac629850..2e3d0f2172a 100644 --- a/storage/innobase/include/dict0mem.ic +++ b/storage/innobase/include/dict0mem.ic @@ -66,6 +66,7 @@ dict_mem_fill_index_struct( index->merge_threshold = DICT_INDEX_MERGE_THRESHOLD_DEFAULT; index->table_name = table_name; index->n_fields = (unsigned int) n_fields; + index->n_core_fields = (unsigned int) n_fields; /* The '1 +' above prevents allocation of an empty mem block */ index->nulls_equal = false; diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index d3336c5f5b5..12395a3f060 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -392,7 +392,7 @@ extern fil_addr_t fil_addr_null; then encrypted */ #define FIL_PAGE_PAGE_COMPRESSED 34354 /*!< page compressed page */ #define FIL_PAGE_INDEX 17855 /*!< B-tree node */ -#define FIL_PAGE_RTREE 17854 /*!< B-tree node */ +#define FIL_PAGE_RTREE 17854 /*!< R-tree node (SPATIAL INDEX) */ #define FIL_PAGE_UNDO_LOG 2 /*!< Undo log page */ #define FIL_PAGE_INODE 3 /*!< Index node */ #define FIL_PAGE_IBUF_FREE_LIST 4 /*!< Insert buffer free list */ @@ -415,15 +415,26 @@ extern fil_addr_t fil_addr_null; //#define FIL_PAGE_ENCRYPTED 15 //#define FIL_PAGE_COMPRESSED_AND_ENCRYPTED 16 //#define FIL_PAGE_ENCRYPTED_RTREE 17 +/** Clustered index root page after instant ADD COLUMN */ +#define FIL_PAGE_TYPE_INSTANT 18 -/** Used by i_s.cc to index into the text description. */ +/** Used by i_s.cc to index into the text description. +Note: FIL_PAGE_TYPE_INSTANT maps to the same as FIL_PAGE_INDEX. */ #define FIL_PAGE_TYPE_LAST FIL_PAGE_TYPE_UNKNOWN /*!< Last page type */ /* @} */ -/** macro to check whether the page type is index (Btree or Rtree) type */ -#define fil_page_type_is_index(page_type) \ - (page_type == FIL_PAGE_INDEX || page_type == FIL_PAGE_RTREE) +/** @return whether the page type is B-tree or R-tree index */ +inline bool fil_page_type_is_index(ulint page_type) +{ + switch (page_type) { + case FIL_PAGE_TYPE_INSTANT: + case FIL_PAGE_INDEX: + case FIL_PAGE_RTREE: + return(true); + } + return(false); +} /** Check whether the page is index page (either regular Btree index or Rtree index */ diff --git a/storage/innobase/include/fil0fil.ic b/storage/innobase/include/fil0fil.ic index 9505cc0bd69..1dd4c64f73e 100644 --- a/storage/innobase/include/fil0fil.ic +++ b/storage/innobase/include/fil0fil.ic @@ -39,6 +39,7 @@ fil_get_page_type_name( return "PAGE_COMPRESSED_ENRYPTED"; case FIL_PAGE_PAGE_COMPRESSED: return "PAGE_COMPRESSED"; + case FIL_PAGE_TYPE_INSTANT: case FIL_PAGE_INDEX: return "INDEX"; case FIL_PAGE_RTREE: @@ -89,6 +90,7 @@ fil_page_type_validate( if (!((page_type == FIL_PAGE_PAGE_COMPRESSED || page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED || page_type == FIL_PAGE_INDEX || + page_type == FIL_PAGE_TYPE_INSTANT || page_type == FIL_PAGE_RTREE || page_type == FIL_PAGE_UNDO_LOG || page_type == FIL_PAGE_INODE || diff --git a/storage/innobase/include/gis0rtree.ic b/storage/innobase/include/gis0rtree.ic index e852ebd8028..4dd05d3b251 100644 --- a/storage/innobase/include/gis0rtree.ic +++ b/storage/innobase/include/gis0rtree.ic @@ -38,7 +38,7 @@ rtr_page_cal_mbr( { page_t* page; rec_t* rec; - byte* field; + const byte* field; ulint len; ulint* offsets = NULL; double bmin, bmax; diff --git a/storage/innobase/include/page0page.h b/storage/innobase/include/page0page.h index 53a58de229d..c2b9a833bda 100644 --- a/storage/innobase/include/page0page.h +++ b/storage/innobase/include/page0page.h @@ -63,9 +63,42 @@ typedef byte page_header_t; #define PAGE_FREE 6 /* pointer to start of page free record list */ #define PAGE_GARBAGE 8 /* number of bytes in deleted records */ #define PAGE_LAST_INSERT 10 /* pointer to the last inserted record, or - NULL if this info has been reset by a delete, + 0 if this info has been reset by a delete, for example */ -#define PAGE_DIRECTION 12 /* last insert direction: PAGE_LEFT, ... */ + +/** This 10-bit field is usually 0. In B-tree index pages of +ROW_FORMAT=REDUNDANT tables, this byte can contain garbage if the .ibd +file was created in MySQL 4.1.0 or if the table resides in the system +tablespace and was created before MySQL 4.1.1 or MySQL 4.0.14. +In this case, the FIL_PAGE_TYPE would be FIL_PAGE_INDEX. + +In ROW_FORMAT=COMPRESSED tables, this field is always 0, because +instant ADD COLUMN is not supported. + +In ROW_FORMAT=COMPACT and ROW_FORMAT=DYNAMIC tables, this field is +always 0, except in the root page of the clustered index after instant +ADD COLUMN. + +Instant ADD COLUMN will change FIL_PAGE_TYPE to FIL_PAGE_TYPE_INSTANT +and initialize the PAGE_INSTANT field to the original number of +fields in the clustered index (dict_index_t::n_core_fields). The most +significant bits are in the first byte, and the least significant 5 +bits are stored in the most significant 5 bits of PAGE_DIRECTION_B. + +These FIL_PAGE_TYPE_INSTANT and PAGE_INSTANT may be assigned even if +instant ADD COLUMN was not committed. Changes to these page header fields +are not undo-logged, but changes to the 'default value record' are. +If the server is killed and restarted, the page header fields could +remain set even though no 'default value record' is present. + +When the table becomes empty, the PAGE_INSTANT field and the +FIL_PAGE_TYPE can be reset and any 'default value record' be removed. */ +#define PAGE_INSTANT 12 + +/** last insert direction: PAGE_LEFT, .... +In ROW_FORMAT=REDUNDANT tables created before MySQL 4.1.1 or MySQL 4.0.14, +this byte can be garbage. */ +#define PAGE_DIRECTION_B 13 #define PAGE_N_DIRECTION 14 /* number of consecutive inserts to the same direction */ #define PAGE_N_RECS 16 /* number of user records on the page */ @@ -251,6 +284,20 @@ page_rec_is_comp(const byte* rec) return(page_is_comp(page_align(rec))); } +# ifdef UNIV_DEBUG +/** Determine if the record is the 'default row' pseudo-record +in the clustered index. +@param[in] rec leaf page record on an index page +@return whether the record is the 'default row' pseudo-record */ +inline +bool +page_rec_is_default_row(const rec_t* rec) +{ + return rec_get_info_bits(rec, page_rec_is_comp(rec)) + & REC_INFO_MIN_REC_FLAG; +} +# endif /* UNIV_DEBUG */ + /** Determine the offset of the infimum record on the page. @param[in] page index page @return offset of the infimum record in record list, relative from page */ @@ -457,7 +504,7 @@ page_header_set_field( Returns the offset stored in the given header field. @return offset from the start of the page, or 0 */ UNIV_INLINE -ulint +uint16_t page_header_get_offs( /*=================*/ const page_t* page, /*!< in: page */ @@ -551,7 +598,7 @@ Gets the number of user records on page (the infimum and supremum records are not user records). @return number of user records */ UNIV_INLINE -ulint +uint16_t page_get_n_recs( /*============*/ const page_t* page); /*!< in: index page */ @@ -569,7 +616,7 @@ page_rec_get_n_recs_before( Gets the number of records in the heap. @return number of user records */ UNIV_INLINE -ulint +uint16_t page_dir_get_n_heap( /*================*/ const page_t* page); /*!< in: index page */ @@ -590,7 +637,7 @@ page_dir_set_n_heap( Gets the number of dir slots in directory. @return number of slots */ UNIV_INLINE -ulint +uint16_t page_dir_get_n_slots( /*=================*/ const page_t* page); /*!< in: index page */ @@ -865,7 +912,7 @@ Returns the sum of the sizes of the records in the record list excluding the infimum and supremum records. @return data in bytes */ UNIV_INLINE -ulint +uint16_t page_get_data_size( /*===============*/ const page_t* page); /*!< in: index page */ @@ -911,6 +958,45 @@ page_mem_free( const dict_index_t* index, /*!< in: index of rec */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ + +/** Read the PAGE_DIRECTION field from a byte. +@param[in] ptr pointer to PAGE_DIRECTION_B +@return the value of the PAGE_DIRECTION field */ +inline +byte +page_ptr_get_direction(const byte* ptr); + +/** Set the PAGE_DIRECTION field. +@param[in] ptr pointer to PAGE_DIRECTION_B +@param[in] dir the value of the PAGE_DIRECTION field */ +inline +void +page_ptr_set_direction(byte* ptr, byte dir); + +/** Read the PAGE_DIRECTION field. +@param[in] page index page +@return the value of the PAGE_DIRECTION field */ +inline +byte +page_get_direction(const page_t* page) +{ + return page_ptr_get_direction(PAGE_HEADER + PAGE_DIRECTION_B + page); +} + +/** Read the PAGE_INSTANT field. +@param[in] page index page +@return the value of the PAGE_INSTANT field */ +inline +uint16_t +page_get_instant(const page_t* page); +/** Assign the PAGE_INSTANT field. +@param[in,out] page clustered index root page +@param[in] n original number of clustered index fields +@param[in,out] mtr mini-transaction */ +inline +void +page_set_instant(page_t* page, unsigned n, mtr_t* mtr); + /**********************************************************//** Create an uncompressed B-tree index page. @return pointer to the page */ @@ -1251,5 +1337,4 @@ page_warn_strict_checksum( #include "page0page.ic" - #endif diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index 0062db56bfa..ee908896050 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/innobase/include/page0page.ic @@ -186,7 +186,7 @@ page_header_set_field( Returns the offset stored in the given header field. @return offset from the start of the page, or 0 */ UNIV_INLINE -ulint +uint16_t page_header_get_offs( /*=================*/ const page_t* page, /*!< in: page */ @@ -464,7 +464,7 @@ Gets the number of user records on page (infimum and supremum records are not user records). @return number of user records */ UNIV_INLINE -ulint +uint16_t page_get_n_recs( /*============*/ const page_t* page) /*!< in: index page */ @@ -477,7 +477,7 @@ page_get_n_recs( Gets the number of dir slots in directory. @return number of slots */ UNIV_INLINE -ulint +uint16_t page_dir_get_n_slots( /*=================*/ const page_t* page) /*!< in: index page */ @@ -502,7 +502,7 @@ page_dir_set_n_slots( Gets the number of records in the heap. @return number of user records */ UNIV_INLINE -ulint +uint16_t page_dir_get_n_heap( /*================*/ const page_t* page) /*!< in: index page */ @@ -868,21 +868,17 @@ Returns the sum of the sizes of the records in the record list, excluding the infimum and supremum records. @return data in bytes */ UNIV_INLINE -ulint +uint16_t page_get_data_size( /*===============*/ const page_t* page) /*!< in: index page */ { - ulint ret; - - ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP) - - (page_is_comp(page) - ? PAGE_NEW_SUPREMUM_END - : PAGE_OLD_SUPREMUM_END) - - page_header_get_field(page, PAGE_GARBAGE)); - + uint16_t ret = page_header_get_field(page, PAGE_HEAP_TOP) + - (page_is_comp(page) + ? PAGE_NEW_SUPREMUM_END + : PAGE_OLD_SUPREMUM_END) + - page_header_get_field(page, PAGE_GARBAGE); ut_ad(ret < UNIV_PAGE_SIZE); - return(ret); } @@ -1078,6 +1074,75 @@ page_mem_free( } } +/** Read the PAGE_DIRECTION field from a byte. +@param[in] ptr pointer to PAGE_DIRECTION_B +@return the value of the PAGE_DIRECTION field */ +inline +byte +page_ptr_get_direction(const byte* ptr) +{ + ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B); + return *ptr & ((1U << 3) - 1); +} + +/** Set the PAGE_DIRECTION field. +@param[in] ptr pointer to PAGE_DIRECTION_B +@param[in] dir the value of the PAGE_DIRECTION field */ +inline +void +page_ptr_set_direction(byte* ptr, byte dir) +{ + ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B); + ut_ad(dir >= PAGE_LEFT); + ut_ad(dir <= PAGE_NO_DIRECTION); + *ptr = (*ptr & ~((1U << 3) - 1)) | dir; +} + +/** Read the PAGE_INSTANT field. +@param[in] page index page +@return the value of the PAGE_INSTANT field */ +inline +uint16_t +page_get_instant(const page_t* page) +{ + uint16_t i = page_header_get_field(page, PAGE_INSTANT); +#ifdef UNIV_DEBUG + switch (fil_page_get_type(page)) { + case FIL_PAGE_TYPE_INSTANT: + ut_ad(page_get_direction(page) <= PAGE_NO_DIRECTION); + ut_ad(i >> 3); + break; + case FIL_PAGE_INDEX: + ut_ad(i <= PAGE_NO_DIRECTION || !page_is_comp(page)); + break; + case FIL_PAGE_RTREE: + ut_ad(i == PAGE_NO_DIRECTION || i == 0); + break; + default: + ut_ad(!"invalid page type"); + break; + } +#endif /* UNIV_DEBUG */ + return(i >> 3); +} + +/** Assign the PAGE_INSTANT field. +@param[in,out] page clustered index root page +@param[in] n original number of clustered index fields +@param[in,out] mtr mini-transaction */ +inline +void +page_set_instant(page_t* page, unsigned n, mtr_t* mtr) +{ + ut_ad(fil_page_get_type(page) == FIL_PAGE_TYPE_INSTANT); + ut_ad(n > 0); + ut_ad(n < REC_MAX_N_FIELDS); + uint16_t i = page_header_get_field(page, PAGE_INSTANT); + ut_ad(i <= PAGE_NO_DIRECTION); + i |= n << 3; + mlog_write_ulint(PAGE_HEADER + PAGE_INSTANT + page, i, + MLOG_2BYTES, mtr); +} #endif /* !UNIV_INNOCHECKSUM */ #ifdef UNIV_MATERIALIZE diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index 6e927da9bd9..58802e23e77 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -33,6 +33,7 @@ Created 5/30/1994 Heikki Tuuri #include "rem0types.h" #include "mtr0types.h" #include "page0types.h" +#include "dict0dict.h" #include "trx0types.h" #endif /*! UNIV_INNOCHECKSUM */ #include <ostream> @@ -54,11 +55,29 @@ in addition to the data and the offsets */ in addition to the data and the offsets */ #define REC_N_NEW_EXTRA_BYTES 5 -/* Record status values */ -#define REC_STATUS_ORDINARY 0 -#define REC_STATUS_NODE_PTR 1 -#define REC_STATUS_INFIMUM 2 -#define REC_STATUS_SUPREMUM 3 +/** Record status values for ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED */ +enum rec_comp_status_t { + /** User record (PAGE_LEVEL=0, heap>=PAGE_HEAP_NO_USER_LOW) */ + REC_STATUS_ORDINARY = 0, + /** Node pointer record (PAGE_LEVEL>=0, heap>=PAGE_HEAP_NO_USER_LOW) */ + REC_STATUS_NODE_PTR = 1, + /** The page infimum pseudo-record (heap=PAGE_HEAP_NO_INFIMUM) */ + REC_STATUS_INFIMUM = 2, + /** The page supremum pseudo-record (heap=PAGE_HEAP_NO_SUPREMUM) */ + REC_STATUS_SUPREMUM = 3, + /** Clustered index record that has been inserted or updated + after instant ADD COLUMN (more than dict_index_t::n_core_fields) */ + REC_STATUS_COLUMNS_ADDED = 4 +}; + +/** The dtuple_t::info_bits of the 'default row' record. +@see rec_is_default_row() */ +static const byte REC_INFO_DEFAULT_ROW + = REC_INFO_MIN_REC_FLAG | REC_STATUS_COLUMNS_ADDED; + +#define REC_NEW_STATUS 3 /* This is single byte bit-field */ +#define REC_NEW_STATUS_MASK 0x7UL +#define REC_NEW_STATUS_SHIFT 0 /* The following four constants are needed in page0zip.cc in order to efficiently compress and decompress pages. */ @@ -94,6 +113,22 @@ offsets[] array, first passed to rec_get_offsets() */ #define REC_OFFS_NORMAL_SIZE OFFS_IN_REC_NORMAL_SIZE #define REC_OFFS_SMALL_SIZE 10 +/** Get the base address of offsets. The extra_size is stored at +this position, and following positions hold the end offsets of +the fields. */ +#define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE) + +/** Compact flag ORed to the extra size returned by rec_get_offsets() */ +const ulint REC_OFFS_COMPACT = ~(ulint(~0) >> 1); +/** SQL NULL flag in offsets returned by rec_get_offsets() */ +const ulint REC_OFFS_SQL_NULL = REC_OFFS_COMPACT; +/** External flag in offsets returned by rec_get_offsets() */ +const ulint REC_OFFS_EXTERNAL = REC_OFFS_COMPACT >> 1; +/** Default value flag in offsets returned by rec_get_offsets() */ +const ulint REC_OFFS_DEFAULT = REC_OFFS_COMPACT >> 2; +/** Mask for offsets returned by rec_get_offsets() */ +const ulint REC_OFFS_MASK = REC_OFFS_DEFAULT - 1; + #ifndef UNIV_INNOCHECKSUM /******************************************************//** The following function is used to get the pointer of the next chained record @@ -252,25 +287,30 @@ rec_set_info_bits_new( rec_t* rec, /*!< in/out: new-style physical record */ ulint bits) /*!< in: info bits */ MY_ATTRIBUTE((nonnull)); -/******************************************************//** -The following function retrieves the status bits of a new-style record. + +/** Determine the status bits of a non-REDUNDANT record. +@param[in] rec ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record @return status bits */ -UNIV_INLINE -ulint -rec_get_status( -/*===========*/ - const rec_t* rec) /*!< in: physical record */ - MY_ATTRIBUTE((warn_unused_result)); +inline +rec_comp_status_t +rec_get_status(const rec_t* rec) +{ + byte bits = rec[-REC_NEW_STATUS] & REC_NEW_STATUS_MASK; + ut_ad(bits <= REC_STATUS_COLUMNS_ADDED); + return static_cast<rec_comp_status_t>(bits); +} -/******************************************************//** -The following function is used to set the status bits of a new-style record. */ -UNIV_INLINE +/** Set the status bits of a non-REDUNDANT record. +@param[in,out] rec ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record +@param[in] bits status bits */ +inline void -rec_set_status( -/*===========*/ - rec_t* rec, /*!< in/out: physical record */ - ulint bits) /*!< in: info bits */ - MY_ATTRIBUTE((nonnull)); +rec_set_status(rec_t* rec, byte bits) +{ + ut_ad(bits <= REC_STATUS_COLUMNS_ADDED); + rec[-REC_NEW_STATUS] = (rec[-REC_NEW_STATUS] & ~REC_NEW_STATUS_MASK) + | bits; +} /******************************************************//** The following function is used to retrieve the info and status @@ -459,9 +499,7 @@ rec_get_offsets_func( const rec_t* rec, const dict_index_t* index, ulint* offsets, -#ifdef UNIV_DEBUG bool leaf, -#endif /* UNIV_DEBUG */ ulint n_fields, #ifdef UNIV_DEBUG const char* file, /*!< in: file name where called */ @@ -471,7 +509,7 @@ rec_get_offsets_func( #ifdef UNIV_DEBUG MY_ATTRIBUTE((nonnull(1,2,6,8),warn_unused_result)); #else /* UNIV_DEBUG */ - MY_ATTRIBUTE((nonnull(1,2,5),warn_unused_result)); + MY_ATTRIBUTE((nonnull(1,2,6),warn_unused_result)); #endif /* UNIV_DEBUG */ #ifdef UNIV_DEBUG @@ -479,7 +517,7 @@ rec_get_offsets_func( rec_get_offsets_func(rec,index,offsets,leaf,n,__FILE__,__LINE__,heap) #else /* UNIV_DEBUG */ # define rec_get_offsets(rec, index, offsets, leaf, n, heap) \ - rec_get_offsets_func(rec, index, offsets, n, heap) + rec_get_offsets_func(rec, index, offsets, leaf, n, heap) #endif /* UNIV_DEBUG */ /******************************************************//** @@ -499,32 +537,31 @@ rec_get_offsets_reverse( offsets[0] allocated elements */ MY_ATTRIBUTE((nonnull)); #ifdef UNIV_DEBUG -/************************************************************//** -Validates offsets returned by rec_get_offsets(). -@return TRUE if valid */ -UNIV_INLINE -ibool +/** Validate offsets returned by rec_get_offsets(). +@param[in] rec record, or NULL +@param[in] index the index that the record belongs in, or NULL +@param[in,out] offsets the offsets of the record +@return true */ +bool rec_offs_validate( -/*==============*/ - const rec_t* rec, /*!< in: record or NULL */ - const dict_index_t* index, /*!< in: record descriptor or NULL */ - const ulint* offsets)/*!< in: array returned by - rec_get_offsets() */ + const rec_t* rec, + const dict_index_t* index, + const ulint* offsets) MY_ATTRIBUTE((nonnull(3), warn_unused_result)); -/************************************************************//** -Updates debug data in offsets, in order to avoid bogus -rec_offs_validate() failures. */ -UNIV_INLINE +/** Update debug data in offsets, in order to tame rec_offs_validate(). +@param[in] rec record +@param[in] index the index that the record belongs in +@param[in] leaf whether the record resides in a leaf page +@param[in,out] offsets offsets from rec_get_offsets() to adjust */ void rec_offs_make_valid( -/*================*/ - const rec_t* rec, /*!< in: record */ - const dict_index_t* index, /*!< in: record descriptor */ - ulint* offsets)/*!< in: array returned by - rec_get_offsets() */ + const rec_t* rec, + const dict_index_t* index, + bool leaf, + ulint* offsets) MY_ATTRIBUTE((nonnull)); #else -# define rec_offs_make_valid(rec, index, offsets) ((void) 0) +# define rec_offs_make_valid(rec, index, leaf, offsets) #endif /* UNIV_DEBUG */ /************************************************************//** @@ -568,26 +605,7 @@ rec_get_nth_field_offs( MY_ATTRIBUTE((nonnull)); #define rec_get_nth_field(rec, offsets, n, len) \ ((rec) + rec_get_nth_field_offs(offsets, n, len)) -/******************************************************//** -Determine if the offsets are for a record in the new -compact format. -@return nonzero if compact format */ -UNIV_INLINE -ulint -rec_offs_comp( -/*==========*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ - MY_ATTRIBUTE((warn_unused_result)); -/******************************************************//** -Determine if the offsets are for a record containing -externally stored columns. -@return nonzero if externally stored */ -UNIV_INLINE -ulint -rec_offs_any_extern( -/*================*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ - MY_ATTRIBUTE((warn_unused_result)); + /******************************************************//** Determine if the offsets are for a record containing null BLOB pointers. @return first field containing a null BLOB pointer, or NULL if none found */ @@ -598,15 +616,16 @@ rec_offs_any_null_extern( const rec_t* rec, /*!< in: record */ const ulint* offsets) /*!< in: rec_get_offsets(rec) */ MY_ATTRIBUTE((warn_unused_result)); + /******************************************************//** Returns nonzero if the extern bit is set in nth field of rec. @return nonzero if externally stored */ UNIV_INLINE ulint -rec_offs_nth_extern( +rec_offs_nth_extern_old( /*================*/ - const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ - ulint n) /*!< in: nth field */ + const rec_t* rec, /*!< in: record */ + ulint n /*!< in: index of the field */) MY_ATTRIBUTE((warn_unused_result)); /** Mark the nth field as externally stored. @@ -616,16 +635,179 @@ void rec_offs_make_nth_extern( ulint* offsets, const ulint n); -/******************************************************//** -Returns nonzero if the SQL NULL bit is set in nth field of rec. -@return nonzero if SQL NULL */ -UNIV_INLINE + +/** Determine the number of allocated elements for an array of offsets. +@param[in] offsets offsets after rec_offs_set_n_alloc() +@return number of elements */ +inline ulint -rec_offs_nth_sql_null( -/*==================*/ - const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ - ulint n) /*!< in: nth field */ - MY_ATTRIBUTE((warn_unused_result)); +rec_offs_get_n_alloc(const ulint* offsets) +{ + ulint n_alloc; + ut_ad(offsets); + n_alloc = offsets[0]; + ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); + UNIV_MEM_ASSERT_W(offsets, n_alloc * sizeof *offsets); + return(n_alloc); +} + +/** Determine the number of fields for which offsets have been initialized. +@param[in] offsets rec_get_offsets() +@return number of fields */ +inline +ulint +rec_offs_n_fields(const ulint* offsets) +{ + ulint n_fields; + ut_ad(offsets); + n_fields = offsets[1]; + ut_ad(n_fields > 0); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); + return(n_fields); +} + +/** Get a flag of a record field. +@param[in] offsets rec_get_offsets() +@param[in] n nth field +@param[in] flag flag to extract +@return the flag of the record field */ +inline +ulint +rec_offs_nth_flag(const ulint* offsets, ulint n, ulint flag) +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + ut_ad(n < rec_offs_n_fields(offsets)); + /* The DEFAULT, NULL, EXTERNAL flags are mutually exclusive. */ + ut_ad(ut_is_2pow(rec_offs_base(offsets)[1 + n] + & (REC_OFFS_DEFAULT + | REC_OFFS_SQL_NULL + | REC_OFFS_EXTERNAL))); + return rec_offs_base(offsets)[1 + n] & flag; +} + +/** Determine if a record field is missing +(should be replaced by dict_index_t::instant_field_value()). +@param[in] offsets rec_get_offsets() +@param[in] n nth field +@return nonzero if default bit is set */ +inline +ulint +rec_offs_nth_default(const ulint* offsets, ulint n) +{ + return rec_offs_nth_flag(offsets, n, REC_OFFS_DEFAULT); +} + +/** Determine if a record field is SQL NULL +(should be replaced by dict_index_t::instant_field_value()). +@param[in] offsets rec_get_offsets() +@param[in] n nth field +@return nonzero if SQL NULL set */ +inline +ulint +rec_offs_nth_sql_null(const ulint* offsets, ulint n) +{ + return rec_offs_nth_flag(offsets, n, REC_OFFS_SQL_NULL); +} + +/** Determine if a record field is stored off-page. +@param[in] offsets rec_get_offsets() +@param[in] n nth field +Returns nonzero if the extern bit is set in nth field of rec. +@return nonzero if externally stored */ +inline +ulint +rec_offs_nth_extern(const ulint* offsets, ulint n) +{ + return rec_offs_nth_flag(offsets, n, REC_OFFS_EXTERNAL); +} + +/** Get a global flag of a record. +@param[in] offsets rec_get_offsets() +@param[in] flag flag to extract +@return the flag of the record field */ +inline +ulint +rec_offs_any_flag(const ulint* offsets, ulint flag) +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + return *rec_offs_base(offsets) & flag; +} + +/** Determine if the offsets are for a record containing off-page columns. +@param[in] offsets rec_get_offsets() +@return nonzero if any off-page columns exist */ +inline +ulint +rec_offs_any_extern(const ulint* offsets) +{ + return rec_offs_any_flag(offsets, REC_OFFS_EXTERNAL); +} + +/** Determine if the offsets are for a record that is missing fields. +@param[in] offsets rec_get_offsets() +@return nonzero if any fields need to be replaced with + dict_index_t::instant_field_value() */ +inline +ulint +rec_offs_any_default(const ulint* offsets) +{ + return rec_offs_any_flag(offsets, REC_OFFS_DEFAULT); +} + +/** Determine if the offsets are for other than ROW_FORMAT=REDUNDANT. +@param[in] offsets rec_get_offsets() +@return nonzero if ROW_FORMAT is COMPACT,DYNAMIC or COMPRESSED +@retval 0 if ROW_FORMAT=REDUNDANT */ +inline +ulint +rec_offs_comp(const ulint* offsets) +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + return(*rec_offs_base(offsets) & REC_OFFS_COMPACT); +} + +/** Determine if the record is the 'default row' pseudo-record +in the clustered index. +@param[in] rec leaf page record +@param[in] index index of the record +@return whether the record is the 'default row' pseudo-record */ +inline +bool +rec_is_default_row(const rec_t* rec, const dict_index_t* index) +{ + bool is = rec_get_info_bits(rec, dict_table_is_comp(index->table)) + & REC_INFO_MIN_REC_FLAG; + ut_ad(!is || index->is_instant()); + ut_ad(!is || !dict_table_is_comp(index->table) + || rec_get_status(rec) == REC_STATUS_COLUMNS_ADDED); + return is; +} + +/** Get the nth field from an index. +@param[in] rec index record +@param[in] index index +@param[in] offsets rec_get_offsets(rec, index) +@param[in] n field number +@param[out] len length of the field in bytes, or UNIV_SQL_NULL +@return a read-only copy of the index field */ +inline +const byte* +rec_get_nth_cfield( + const rec_t* rec, + const dict_index_t* index, + const ulint* offsets, + ulint n, + ulint* len) +{ + ut_ad(rec_offs_validate(rec, index, offsets)); + if (!rec_offs_nth_default(offsets, n)) { + return rec_get_nth_field(rec, offsets, n, len); + } + return index->instant_field_value(n, len); +} + /******************************************************//** Gets the physical size of a field. @return length of field */ @@ -679,16 +861,6 @@ rec_get_data_size_old( const rec_t* rec) /*!< in: physical record */ MY_ATTRIBUTE((warn_unused_result)); /**********************************************************//** -The following function returns the number of allocated elements -for an array of offsets. -@return number of elements */ -UNIV_INLINE -ulint -rec_offs_get_n_alloc( -/*=================*/ - const ulint* offsets)/*!< in: array for rec_get_offsets() */ - MY_ATTRIBUTE((warn_unused_result)); -/**********************************************************//** The following function sets the number of allocated elements for an array of offsets. */ UNIV_INLINE @@ -702,15 +874,6 @@ rec_offs_set_n_alloc( #define rec_offs_init(offsets) \ rec_offs_set_n_alloc(offsets, (sizeof offsets) / sizeof *offsets) /**********************************************************//** -The following function returns the number of fields in a record. -@return number of fields */ -UNIV_INLINE -ulint -rec_offs_n_fields( -/*==============*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ - MY_ATTRIBUTE((warn_unused_result)); -/**********************************************************//** The following function returns the data size of a physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function @@ -785,14 +948,46 @@ rec_copy( @param[in] fields data fields @param[in] n_fields number of data fields @param[out] extra record header size +@param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED @return total size, in bytes */ ulint rec_get_converted_size_temp( const dict_index_t* index, const dfield_t* fields, ulint n_fields, - ulint* extra) - MY_ATTRIBUTE((warn_unused_result, nonnull(1,2))); + ulint* extra, + rec_comp_status_t status = REC_STATUS_ORDINARY) + MY_ATTRIBUTE((warn_unused_result, nonnull)); + +/** Determine the offset to each field in temporary file. +@param[in] rec temporary file record +@param[in] index index of that the record belongs to +@param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets) +@param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED +*/ +void +rec_init_offsets_temp( + const rec_t* rec, + const dict_index_t* index, + ulint* offsets, + rec_comp_status_t status = REC_STATUS_ORDINARY) + MY_ATTRIBUTE((nonnull)); + +/** Convert a data tuple prefix to the temporary file format. +@param[out] rec record in temporary file format +@param[in] index clustered or secondary index +@param[in] fields data fields +@param[in] n_fields number of data fields +@param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED +*/ +void +rec_convert_dtuple_to_temp( + rec_t* rec, + const dict_index_t* index, + const dfield_t* fields, + ulint n_fields, + rec_comp_status_t status = REC_STATUS_ORDINARY) + MY_ATTRIBUTE((nonnull)); /** Determine the converted size of virtual column data in a temporary file. @see rec_convert_dtuple_to_temp_v() @@ -817,29 +1012,6 @@ rec_convert_dtuple_to_temp_v( const dtuple_t* v_entry) MY_ATTRIBUTE((nonnull)); -/******************************************************//** -Determine the offset to each field in temporary file. -@see rec_convert_dtuple_to_temp() */ -void -rec_init_offsets_temp( -/*==================*/ - const rec_t* rec, /*!< in: temporary file record */ - const dict_index_t* index, /*!< in: record descriptor */ - ulint* offsets)/*!< in/out: array of offsets; - in: n=rec_offs_n_fields(offsets) */ - MY_ATTRIBUTE((nonnull)); - -/*********************************************************//** -Builds a temporary file record out of a data tuple. -@see rec_init_offsets_temp() */ -void -rec_convert_dtuple_to_temp( -/*=======================*/ - rec_t* rec, /*!< out: record */ - const dict_index_t* index, /*!< in: record descriptor */ - const dfield_t* fields, /*!< in: array of data fields */ - ulint n_fields); /*!< in: number of fields */ - /**************************************************************//** Copies the first n fields of a physical record to a new physical record in a buffer. @@ -856,22 +1028,6 @@ rec_copy_prefix_to_buf( or NULL */ ulint* buf_size) /*!< in/out: buffer size */ MY_ATTRIBUTE((nonnull)); -/** Fold a prefix of a physical record. -@param[in] rec index record -@param[in] offsets return value of rec_get_offsets() -@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 -rec_fold( - const rec_t* rec, - const ulint* offsets, - ulint n_fields, - ulint n_bytes, - index_id_t tree_id) - MY_ATTRIBUTE((warn_unused_result)); /*********************************************************//** Builds a physical record out of a data tuple and stores it into the given buffer. @@ -919,7 +1075,7 @@ rec_get_converted_size_comp( dict_table_is_comp() is assumed to hold, even if it does not */ - ulint status, /*!< in: status bits of the record */ + rec_comp_status_t status, /*!< in: status bits of the record */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields,/*!< in: number of data fields */ ulint* extra) /*!< out: extra size */ @@ -944,23 +1100,14 @@ The fields are copied into the memory heap. @param[in] n_fields number of fields to copy @param[in,out] heap memory heap */ void -rec_copy_prefix_to_dtuple_func( +rec_copy_prefix_to_dtuple( dtuple_t* tuple, const rec_t* rec, const dict_index_t* index, -#ifdef UNIV_DEBUG bool is_leaf, -#endif /* UNIV_DEBUG */ ulint n_fields, mem_heap_t* heap) MY_ATTRIBUTE((nonnull)); -#ifdef UNIV_DEBUG -# define rec_copy_prefix_to_dtuple(tuple,rec,index,leaf,n_fields,heap) \ - rec_copy_prefix_to_dtuple_func(tuple,rec,index,leaf,n_fields,heap) -#else /* UNIV_DEBUG */ -# define rec_copy_prefix_to_dtuple(tuple,rec,index,leaf,n_fields,heap) \ - rec_copy_prefix_to_dtuple_func(tuple,rec,index,n_fields,heap) -#endif /* UNIV_DEBUG */ /***************************************************************//** Validates the consistency of a physical record. @return TRUE if ok */ diff --git a/storage/innobase/include/rem0rec.ic b/storage/innobase/include/rem0rec.ic index e16eab62181..cc66149945c 100644 --- a/storage/innobase/include/rem0rec.ic +++ b/storage/innobase/include/rem0rec.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2015, 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 @@ -25,19 +26,9 @@ Created 5/30/1994 Heikki Tuuri #include "mach0data.h" #include "ut0byte.h" -#include "dict0dict.h" #include "dict0boot.h" #include "btr0types.h" -/* Compact flag ORed to the extra size returned by rec_get_offsets() */ -#define REC_OFFS_COMPACT ((ulint) 1 << 31) -/* SQL NULL flag in offsets returned by rec_get_offsets() */ -#define REC_OFFS_SQL_NULL ((ulint) 1 << 31) -/* External flag in offsets returned by rec_get_offsets() */ -#define REC_OFFS_EXTERNAL ((ulint) 1 << 30) -/* Mask for offsets returned by rec_get_offsets() */ -#define REC_OFFS_MASK (REC_OFFS_EXTERNAL - 1) - /* Offsets of the bit-fields in an old-style record. NOTE! In the table the most significant bytes and bits are written below less significant. @@ -72,10 +63,11 @@ most significant bytes and bits are written below less significant. relative_offset + offset_of_this_record mod UNIV_PAGE_SIZE 3 3 bits status: - 000=conventional record - 001=node pointer record (inside B-tree) - 010=infimum record - 011=supremum record + 000=REC_STATUS_ORDINARY + 001=REC_STATUS_NODE_PTR + 010=REC_STATUS_INFIMUM + 011=REC_STATUS_SUPREMUM + 100=REC_STATUS_COLUMNS_ADDED 1xx=reserved 5 bits heap number 4 8 bits heap number @@ -98,10 +90,6 @@ and the shift needed to obtain each bit-field of the record. */ #define REC_OLD_N_FIELDS_MASK 0x7FEUL #define REC_OLD_N_FIELDS_SHIFT 1 -#define REC_NEW_STATUS 3 /* This is single byte bit-field */ -#define REC_NEW_STATUS_MASK 0x7UL -#define REC_NEW_STATUS_SHIFT 0 - #define REC_OLD_HEAP_NO 5 #define REC_HEAP_NO_MASK 0xFFF8UL #if 0 /* defined in rem0rec.h for use of page0zip.cc */ @@ -456,26 +444,6 @@ rec_set_n_fields_old( } /******************************************************//** -The following function retrieves the status bits of a new-style record. -@return status bits */ -UNIV_INLINE -ulint -rec_get_status( -/*===========*/ - const rec_t* rec) /*!< in: physical record */ -{ - ulint ret; - - ut_ad(rec); - - ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, - REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); - ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); - - return(ret); -} - -/******************************************************//** The following function is used to get the number of fields in a record. @return number of data fields */ @@ -494,6 +462,7 @@ rec_get_n_fields( } switch (rec_get_status(rec)) { + case REC_STATUS_COLUMNS_ADDED: case REC_STATUS_ORDINARY: return(dict_index_get_n_fields(index)); case REC_STATUS_NODE_PTR: @@ -501,10 +470,10 @@ rec_get_n_fields( case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: return(1); - default: - ut_error; - return(ULINT_UNDEFINED); } + + ut_error; + return(ULINT_UNDEFINED); } /** Confirms the n_fields of the entry is sane with comparing the other @@ -520,13 +489,15 @@ rec_n_fields_is_sane( const rec_t* rec, const dtuple_t* entry) { - return(rec_get_n_fields(rec, index) - == dtuple_get_n_fields(entry) + const ulint n_fields = rec_get_n_fields(rec, index); + + return(n_fields == dtuple_get_n_fields(entry) + || (index->is_instant() + && n_fields >= index->n_core_fields) /* a record for older SYS_INDEXES table (missing merge_threshold column) is acceptable. */ || (index->table->id == DICT_INDEXES_ID - && rec_get_n_fields(rec, index) - == dtuple_get_n_fields(entry) - 1)); + && n_fields == dtuple_get_n_fields(entry) - 1)); } /******************************************************//** @@ -645,19 +616,6 @@ rec_set_info_bits_new( } /******************************************************//** -The following function is used to set the status bits of a new-style record. */ -UNIV_INLINE -void -rec_set_status( -/*===========*/ - rec_t* rec, /*!< in/out: physical record */ - ulint bits) /*!< in: info bits */ -{ - rec_set_bit_field_1(rec, bits, REC_NEW_STATUS, - REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); -} - -/******************************************************//** The following function is used to retrieve the info and status bits of a record. (Only compact records have status bits.) @return info bits */ @@ -924,29 +882,6 @@ rec_2_is_field_extern( return(rec_2_get_field_end_info(rec, n) & REC_2BYTE_EXTERN_MASK); } -/* Get the base address of offsets. The extra_size is stored at -this position, and following positions hold the end offsets of -the fields. */ -#define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE) - -/**********************************************************//** -The following function returns the number of allocated elements -for an array of offsets. -@return number of elements */ -UNIV_INLINE -ulint -rec_offs_get_n_alloc( -/*=================*/ - const ulint* offsets)/*!< in: array for rec_get_offsets() */ -{ - ulint n_alloc; - ut_ad(offsets); - n_alloc = offsets[0]; - ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); - UNIV_MEM_ASSERT_W(offsets, n_alloc * sizeof *offsets); - return(n_alloc); -} - /**********************************************************//** The following function sets the number of allocated elements for an array of offsets. */ @@ -964,102 +899,6 @@ rec_offs_set_n_alloc( offsets[0] = n_alloc; } -/**********************************************************//** -The following function returns the number of fields in a record. -@return number of fields */ -UNIV_INLINE -ulint -rec_offs_n_fields( -/*==============*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ -{ - ulint n_fields; - ut_ad(offsets); - n_fields = offsets[1]; - ut_ad(n_fields > 0); - ut_ad(n_fields <= REC_MAX_N_FIELDS); - ut_ad(n_fields + REC_OFFS_HEADER_SIZE - <= rec_offs_get_n_alloc(offsets)); - return(n_fields); -} - -/************************************************************//** -Validates offsets returned by rec_get_offsets(). -@return TRUE if valid */ -UNIV_INLINE -ibool -rec_offs_validate( -/*==============*/ - const rec_t* rec, /*!< in: record or NULL */ - const dict_index_t* index, /*!< in: record descriptor or NULL */ - const ulint* offsets)/*!< in: array returned by - rec_get_offsets() */ -{ - ulint i = rec_offs_n_fields(offsets); - ulint last = ULINT_MAX; - ulint comp = *rec_offs_base(offsets) & REC_OFFS_COMPACT; - - if (rec) { - ut_ad((ulint) rec == offsets[2]); - if (!comp) { - ut_a(rec_get_n_fields_old(rec) >= i); - } - } - if (index) { - ulint max_n_fields; - ut_ad((ulint) index == offsets[3]); - max_n_fields = ut_max( - dict_index_get_n_fields(index), - dict_index_get_n_unique_in_tree(index) + 1); - if (comp && rec) { - switch (rec_get_status(rec)) { - case REC_STATUS_ORDINARY: - break; - case REC_STATUS_NODE_PTR: - max_n_fields = dict_index_get_n_unique_in_tree( - index) + 1; - break; - case REC_STATUS_INFIMUM: - case REC_STATUS_SUPREMUM: - max_n_fields = 1; - break; - default: - ut_error; - } - } - /* index->n_def == 0 for dummy indexes if !comp */ - ut_a(!comp || index->n_def); - ut_a(!index->n_def || i <= max_n_fields); - } - while (i--) { - ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK; - ut_a(curr <= last); - last = curr; - } - return(TRUE); -} -#ifdef UNIV_DEBUG -/************************************************************//** -Updates debug data in offsets, in order to avoid bogus -rec_offs_validate() failures. */ -UNIV_INLINE -void -rec_offs_make_valid( -/*================*/ - const rec_t* rec, /*!< in: record */ - const dict_index_t* index, /*!< in: record descriptor */ - ulint* offsets)/*!< in: array returned by - rec_get_offsets() */ -{ - ut_ad(rec); - ut_ad(index); - ut_ad(offsets); - ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets)); - offsets[2] = (ulint) rec; - offsets[3] = (ulint) index; -} -#endif /* UNIV_DEBUG */ - /************************************************************//** The following function is used to get an offset to the nth data field in a record. @@ -1071,7 +910,7 @@ rec_get_nth_field_offs( const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n, /*!< in: index of the field */ ulint* len) /*!< out: length of the field; UNIV_SQL_NULL - if SQL null */ + if SQL null; UNIV_SQL_DEFAULT is default value */ { ulint offs; ulint length; @@ -1088,6 +927,8 @@ rec_get_nth_field_offs( if (length & REC_OFFS_SQL_NULL) { length = UNIV_SQL_NULL; + } else if (length & REC_OFFS_DEFAULT) { + length = UNIV_SQL_DEFAULT; } else { length &= REC_OFFS_MASK; length -= offs; @@ -1098,34 +939,6 @@ rec_get_nth_field_offs( } /******************************************************//** -Determine if the offsets are for a record in the new -compact format. -@return nonzero if compact format */ -UNIV_INLINE -ulint -rec_offs_comp( -/*==========*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ -{ - ut_ad(rec_offs_validate(NULL, NULL, offsets)); - return(*rec_offs_base(offsets) & REC_OFFS_COMPACT); -} - -/******************************************************//** -Determine if the offsets are for a record containing -externally stored columns. -@return nonzero if externally stored */ -UNIV_INLINE -ulint -rec_offs_any_extern( -/*================*/ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ -{ - ut_ad(rec_offs_validate(NULL, NULL, offsets)); - return(*rec_offs_base(offsets) & REC_OFFS_EXTERNAL); -} - -/******************************************************//** Determine if the offsets are for a record containing null BLOB pointers. @return first field containing a null BLOB pointer, or NULL if none found */ UNIV_INLINE @@ -1166,29 +979,14 @@ Returns nonzero if the extern bit is set in nth field of rec. @return nonzero if externally stored */ UNIV_INLINE ulint -rec_offs_nth_extern( +rec_offs_nth_extern_old( /*================*/ - const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ - ulint n) /*!< in: nth field */ -{ - ut_ad(rec_offs_validate(NULL, NULL, offsets)); - ut_ad(n < rec_offs_n_fields(offsets)); - return(rec_offs_base(offsets)[1 + n] & REC_OFFS_EXTERNAL); -} - -/******************************************************//** -Returns nonzero if the SQL NULL bit is set in nth field of rec. -@return nonzero if SQL NULL */ -UNIV_INLINE -ulint -rec_offs_nth_sql_null( -/*==================*/ - const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ - ulint n) /*!< in: nth field */ + const rec_t* rec, /*!< in: record */ + ulint n /*!< in: index of the field */) { - ut_ad(rec_offs_validate(NULL, NULL, offsets)); - ut_ad(n < rec_offs_n_fields(offsets)); - return(rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL); + if(rec_get_1byte_offs_flag(rec)) + return 0; + return (rec_2_get_field_end_info(rec,n) & REC_2BYTE_EXTERN_MASK); } /******************************************************//** @@ -1426,6 +1224,7 @@ rec_set_nth_field( ut_ad(rec); ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(!rec_offs_nth_default(offsets, n)); if (len == UNIV_SQL_NULL) { if (!rec_offs_nth_sql_null(offsets, n)) { @@ -1436,7 +1235,7 @@ rec_set_nth_field( return; } - data2 = rec_get_nth_field(rec, offsets, n, &len2); + data2 = (byte*)rec_get_nth_field(rec, offsets, n, &len2); if (len2 == UNIV_SQL_NULL) { ut_ad(!rec_offs_comp(offsets)); rec_set_nth_field_null_bit(rec, n, FALSE); @@ -1517,7 +1316,7 @@ rec_offs_extra_size( { ulint size; ut_ad(rec_offs_validate(NULL, NULL, offsets)); - size = *rec_offs_base(offsets) & ~(REC_OFFS_COMPACT | REC_OFFS_EXTERNAL); + size = *rec_offs_base(offsets) & REC_OFFS_MASK; ut_ad(size < UNIV_PAGE_SIZE); return(size); } @@ -1630,27 +1429,34 @@ rec_get_converted_size( ut_ad(index); ut_ad(dtuple); ut_ad(dtuple_check_typed(dtuple)); - - ut_ad(dict_index_is_ibuf(index) - - || dtuple_get_n_fields(dtuple) - == (((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) - == REC_STATUS_NODE_PTR) - ? dict_index_get_n_unique_in_tree_nonleaf(index) + 1 - : dict_index_get_n_fields(index)) - - /* a record for older SYS_INDEXES table - (missing merge_threshold column) is acceptable. */ - || (index->table->id == DICT_INDEXES_ID - && dtuple_get_n_fields(dtuple) - == dict_index_get_n_fields(index) - 1)); +#ifdef UNIV_DEBUG + if (dict_index_is_ibuf(index)) { + ut_ad(dtuple->n_fields > 1); + } else if ((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) + == REC_STATUS_NODE_PTR) { + ut_ad(dtuple->n_fields + == dict_index_get_n_unique_in_tree_nonleaf(index) + 1); + } else if (index->table->id == DICT_INDEXES_ID) { + /* The column SYS_INDEXES.MERGE_THRESHOLD was + instantly added in MariaDB 10.2.2 (MySQL 5.7). */ + ut_ad(index->n_fields == DICT_NUM_FIELDS__SYS_INDEXES); + ut_ad(dtuple->n_fields == DICT_NUM_FIELDS__SYS_INDEXES + || dtuple->n_fields + == DICT_FLD__SYS_INDEXES__MERGE_THRESHOLD); + } else { + ut_ad(dtuple->n_fields >= index->n_core_fields); + ut_ad(dtuple->n_fields <= index->n_fields); + } +#endif if (dict_table_is_comp(index->table)) { - return(rec_get_converted_size_comp(index, - dtuple_get_info_bits(dtuple) - & REC_NEW_STATUS_MASK, - dtuple->fields, - dtuple->n_fields, NULL)); + return(rec_get_converted_size_comp( + index, + static_cast<rec_comp_status_t>( + dtuple->info_bits + & REC_NEW_STATUS_MASK), + dtuple->fields, + dtuple->n_fields, NULL)); } data_size = dtuple_get_data_size(dtuple, 0); @@ -1658,105 +1464,5 @@ rec_get_converted_size( extra_size = rec_get_converted_extra_size( data_size, dtuple_get_n_fields(dtuple), n_ext); -#if 0 - /* This code is inactive since it may be the wrong place to add - in the size of node pointers used in parent pages AND it is not - currently needed since ha_innobase::max_supported_key_length() - ensures that the key size limit for each page size is well below - the actual limit ((free space on page / 4) - record overhead). - But those limits will need to be raised when InnoDB can - support multiple page sizes. At that time, we will need - to consider the node pointer on these universal btrees. */ - - if (dict_index_is_ibuf(index)) { - /* This is for the insert buffer B-tree. - All fields in the leaf tuple ascend to the - parent node plus the child page pointer. */ - - /* ibuf cannot contain externally stored fields */ - ut_ad(n_ext == 0); - - /* Add the data pointer and recompute extra_size - based on one more field. */ - data_size += REC_NODE_PTR_SIZE; - extra_size = rec_get_converted_extra_size( - data_size, - dtuple_get_n_fields(dtuple) + 1, - 0); - - /* Be sure dtuple->n_fields has this node ptr - accounted for. This function should correspond to - what rec_convert_dtuple_to_rec() needs in storage. - In optimistic insert or update-not-in-place, we will - have to ensure that if the record is converted to a - node pointer, it will not become too large.*/ - } -#endif - return(data_size + extra_size); } - -/** Fold a prefix of a physical record. -@param[in] rec index record -@param[in] offsets return value of rec_get_offsets() -@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 -rec_fold( - const rec_t* rec, - const ulint* offsets, - ulint n_fields, - ulint n_bytes, - index_id_t tree_id) -{ - ulint i; - const byte* data; - ulint len; - ulint fold; - ulint n_fields_rec; - - ut_ad(rec_offs_validate(rec, NULL, offsets)); - ut_ad(rec_validate(rec, offsets)); - ut_ad(n_fields > 0 || n_bytes > 0); - - n_fields_rec = rec_offs_n_fields(offsets); - ut_ad(n_fields <= n_fields_rec); - ut_ad(n_fields < n_fields_rec || n_bytes == 0); - - if (n_fields > n_fields_rec) { - n_fields = n_fields_rec; - } - - if (n_fields == n_fields_rec) { - n_bytes = 0; - } - - fold = ut_fold_ull(tree_id); - - for (i = 0; i < n_fields; i++) { - data = rec_get_nth_field(rec, offsets, i, &len); - - if (len != UNIV_SQL_NULL) { - fold = ut_fold_ulint_pair(fold, - ut_fold_binary(data, len)); - } - } - - if (n_bytes > 0) { - data = rec_get_nth_field(rec, offsets, i, &len); - - if (len != UNIV_SQL_NULL) { - if (len > n_bytes) { - len = n_bytes; - } - - fold = ut_fold_ulint_pair(fold, - ut_fold_binary(data, len)); - } - } - - return(fold); -} diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h index bdfdc2f3c08..b7f9dd02cb0 100644 --- a/storage/innobase/include/row0merge.h +++ b/storage/innobase/include/row0merge.h @@ -263,7 +263,6 @@ row_merge_rename_index_to_drop( MY_ATTRIBUTE((nonnull(1), warn_unused_result)); /** Create the index and load in to the dictionary. -@param[in,out] trx trx (sets error_state) @param[in,out] table the index is on this table @param[in] index_def the index definition @param[in] add_v new virtual columns added along with add @@ -273,7 +272,6 @@ row_merge_rename_index_to_drop( @return index, or NULL on error */ dict_index_t* row_merge_create_index( - trx_t* trx, dict_table_t* table, const index_def_t* index_def, const dict_add_v_col_t* add_v, diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h index ec7995dd096..92b5942966b 100644 --- a/storage/innobase/include/row0upd.h +++ b/storage/innobase/include/row0upd.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -26,7 +27,6 @@ Created 12/27/1996 Heikki Tuuri #ifndef row0upd_h #define row0upd_h -#include "univ.i" #include "data0data.h" #include "row0types.h" #include "btr0types.h" @@ -244,27 +244,19 @@ row_upd_build_difference_binary( mem_heap_t* heap, TABLE* mysql_table) MY_ATTRIBUTE((nonnull(1,2,3,7), warn_unused_result)); -/***********************************************************//** -Replaces the new column values stored in the update vector to the index entry -given. */ +/** Apply an update vector to an index entry. +@param[in,out] entry index entry to be updated; the clustered index record + must be covered by a lock or a page latch to prevent + deletion (rollback or purge) +@param[in] index index of the entry +@param[in] update update vector built for the entry +@param[in,out] heap memory heap for copying off-page columns */ void row_upd_index_replace_new_col_vals_index_pos( -/*=========================================*/ - dtuple_t* entry, /*!< in/out: index entry where replaced; - the clustered index record must be - covered by a lock or a page latch to - prevent deletion (rollback or purge) */ - dict_index_t* index, /*!< in: index; NOTE that this may also be a - non-clustered index */ - const upd_t* update, /*!< in: an update vector built for the index so - that the field number in an upd_field is the - index position */ - ibool order_only, - /*!< in: if TRUE, limit the replacement to - ordering fields of index; note that this - does not work for non-clustered indexes. */ - mem_heap_t* heap) /*!< in: memory heap for allocating and - copying the new values */ + dtuple_t* entry, + const dict_index_t* index, + const upd_t* update, + mem_heap_t* heap) MY_ATTRIBUTE((nonnull)); /***********************************************************//** Replaces the new column values stored in the update vector to the index entry diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index ae00ffe21cd..c471ce5d57d 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -1007,6 +1007,9 @@ struct export_var_t{ ulint innodb_defragment_count; /*!< Number of defragment operations*/ + /** Number of instant ALTER TABLE operations that affect columns */ + ulong innodb_instant_alter_column; + ulint innodb_onlineddl_rowlog_rows; /*!< Online alter rows */ ulint innodb_onlineddl_rowlog_pct_used; /*!< Online alter percentage of used row log buffer */ diff --git a/storage/innobase/include/trx0rec.h b/storage/innobase/include/trx0rec.h index 3dc35c7fda8..a6889696036 100644 --- a/storage/innobase/include/trx0rec.h +++ b/storage/innobase/include/trx0rec.h @@ -309,6 +309,8 @@ trx_undo_read_v_idx( compilation info multiplied by 16 is ORed to this value in an undo log record */ +#define TRX_UNDO_INSERT_DEFAULT 10 /* insert a "default value" + pseudo-record for instant ALTER */ #define TRX_UNDO_INSERT_REC 11 /* fresh insert into clustered index */ #define TRX_UNDO_UPD_EXIST_REC 12 /* update of a non-delete-marked record */ @@ -324,6 +326,9 @@ record */ storage fields: used by purge to free the external storage */ +/** The search tuple corresponding to TRX_UNDO_INSERT_DEFAULT */ +extern const dtuple_t trx_undo_default_rec; + #include "trx0rec.ic" #endif /* trx0rec_h */ diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 69bea016605..133f23081a0 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1177,10 +1177,8 @@ struct trx_t { trx_rsegs_t rsegs; /* rollback segments for undo logging */ undo_no_t roll_limit; /*!< least undo number to undo during a partial rollback; 0 otherwise */ -#ifdef UNIV_DEBUG bool in_rollback; /*!< true when the transaction is executing a partial or full rollback */ -#endif /* UNIV_DEBUG */ ulint pages_undone; /*!< number of undo log pages undone since the last undo log truncation */ /*------------------------------*/ |