diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index b27be97..0c3dd34 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -692,5 +692,248 @@ more bytes needed in record header to store this info. size_t get_extra_bytes_for_temp_redundant(const dict_index_t *index, bool valid_version); #include "rem0rec.ic" +static inline ulint rec_get_nth_field_offs_low_baotiao(const ulint *offsets, ulint n, + ulint *len) { + ulint offs; + ulint length; + ut_ad(n < rec_offs_n_fields(offsets)); + ut_ad(len); + + if (n == 0) { + offs = 0; + } else { + offs = rec_offs_base(offsets)[n] & REC_OFFS_MASK; + } + + length = rec_offs_base(offsets)[1 + n]; + + if (length & REC_OFFS_SQL_NULL) { + length = UNIV_SQL_NULL; + } else if (length & REC_OFFS_DEFAULT) { + length = UNIV_SQL_ADD_COL_DEFAULT; + } else if (length & REC_OFFS_DROP) { + length = UNIV_SQL_INSTANT_DROP_COL; + } else { + length &= REC_OFFS_MASK; + length -= offs; + } + + *len = length; + return (offs); +} +inline ulint rec_get_nth_field_offs(const dict_index_t *index, const ulint *offsets, + ulint n, ulint *len) { + if (index && index->has_row_versions()) { + n = index->get_field_off_pos(n); + } + + return rec_get_nth_field_offs_low_baotiao(offsets, n, len); +} + +inline byte *rec_get_nth_field(const dict_index_t *index, const rec_t *rec, + const ulint *offsets, ulint n, ulint *len) { + byte *field = + const_cast(rec) + rec_get_nth_field_offs(index, offsets, n, len); + return (field); +} + +inline void rec_init_offsets_comp_ordinary(const rec_t *rec, bool temp, + const dict_index_t *index, ulint *offsets) { +#ifdef UNIV_DEBUG + /* We cannot invoke rec_offs_make_valid() here if temp=true. + Similarly, rec_offs_validate() will fail in that case, because + it invokes rec_get_status(). */ + offsets[2] = (ulint)rec; + offsets[3] = (ulint)index; +#endif /* UNIV_DEBUG */ + + const byte *nulls = nullptr; + const byte *lens = nullptr; + uint16_t n_null = 0; + enum REC_INSERT_STATE rec_insert_state = REC_INSERT_STATE::NONE; + uint8_t row_version = UINT8_UNDEFINED; + uint16_t non_default_fields = 0; + + if (temp) { + rec_insert_state = rec_init_null_and_len_temp( + rec, index, &nulls, &lens, &n_null, non_default_fields, row_version); + } else { + rec_insert_state = rec_init_null_and_len_comp( + rec, index, &nulls, &lens, &n_null, non_default_fields, row_version); + } + + ut_ad(temp || dict_table_is_comp(index->table)); + +#if 0 + if (temp) { + if (dict_table_is_comp(index->table)) { + /* No need to do adjust fixed_len=0. We only need to + adjust it for ROW_FORMAT=REDUNDANT. */ + temp = false; + } else { + /* Redundant temp row. Old instant record is logged as version 0. */ + if (rec_insert_state == INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION || + rec_insert_state == INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION) { + rec_insert_state = INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION; + ut_ad(row_version == UINT8_UNDEFINED); + } + } + } +#endif + + /* read the lengths of fields 0..n */ + ulint offs = 0; + ulint any_ext = 0; + ulint null_mask = 1; + uint16_t i = 0; + do { + /* Fields are stored on disk in version they are added in and are + maintained in fields_array in the same order. Get the right field. */ + const dict_field_t *field = index->get_physical_field(i); + const dict_col_t *col = field->col; + uint64_t len; + +#if 1 + if (likely(rec_insert_state == INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION)) { + } else if (rec_insert_state == INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION) { + //ut_ad(!index->has_instant_cols_or_row_versions()); + len = 1; + } else if (rec_insert_state == INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION || + rec_insert_state == INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION) { + //row_version = 0; + offs = 1; + } else if (rec_insert_state == INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION || + rec_insert_state == INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION) { + any_ext = 1; + } else { + null_mask = 1; + } +#else + switch (rec_insert_state) { + case INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION: + ut_ad(!index->has_instant_cols_or_row_versions()); + break; + + case INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION: { + ut_ad(row_version == UINT8_UNDEFINED || row_version == 0); + ut_ad(index->has_row_versions() || temp); + /* Record has to be interpreted in v0. */ + row_version = 0; + } + [[fallthrough]]; + case INSERTED_AFTER_UPGRADE_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION: + case INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION: { + ut_ad(is_valid_row_version(row_version)); + /* A record may have version=0 if it's from upgrade table */ + ut_ad(index->has_row_versions() || + (index->table->is_upgraded_instant() && row_version == 0)); + + /* Based on the record version and column information, see if this + column is there in this record or not. */ + if (col->is_dropped_in_or_before(row_version)) { + /* This columns is dropped before or on this row version so its data + won't be there on row. So no need to store the length. Instead, store + offs ORed with REC_OFFS_DROP to indicate the same. */ + len = offs | REC_OFFS_DROP; + goto resolved; + + /* NOTE : Existing rows, which have data for this column, would still + need to process this column, so don't skip and store the correct + length there. Though it will be skipped while fetching row. */ + } else if (col->is_added_after(row_version)) { + /* This columns is added after this row version. In this case no need + to store the length. Instead store only if it is NULL or DEFAULT + value. */ + len = rec_get_instant_offset(index, i, offs); + + goto resolved; + } + } break; + + case INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION: + case INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION: { + ut_ad(non_default_fields > 0); + ut_ad(index->has_instant_cols()); + ut_ad(!is_valid_row_version(row_version)); + + if (i >= non_default_fields) { + /* This would be the case when column doesn't exists in the row. In + this case we need not to store the length. Instead we store only if + the column is NULL or DEFAULT value. */ + len = rec_get_instant_offset(index, i, offs); + + goto resolved; + } + + /* Note : Even if the column has been dropped, this row in V1 would + definitely have the value of this column. */ + } break; + + default: + ut_ad(false); + } +#endif + + if (!(col->prtype & DATA_NOT_NULL)) { + /* nullable field => read the null flag */ + ut_ad(n_null--); + + if (UNIV_UNLIKELY(!(byte)null_mask)) { + nulls--; + null_mask = 1; + } + + if (*nulls & null_mask) { + null_mask <<= 1; + /* No length is stored for NULL fields. + We do not advance offs, and we set + the length to zero and enable the + SQL NULL flag in offsets[]. */ + len = offs | REC_OFFS_SQL_NULL; + goto resolved; + } + null_mask <<= 1; + } + + if (!field->fixed_len || (temp && !col->get_fixed_size(temp))) { + ut_ad(col->mtype != DATA_POINT); + /* Variable-length field: read the length */ + len = *lens--; + /* If the maximum length of the field is up + to 255 bytes, the actual length is always + stored in one byte. If the maximum length is + more than 255 bytes, the actual length is + stored in one byte for 0..127. The length + will be encoded in two bytes when it is 128 or + more, or when the field is stored externally. */ + if (DATA_BIG_COL(col)) { + if (len & 0x80) { + /* 1exxxxxxx xxxxxxxx */ + len <<= 8; + len |= *lens--; + + offs += len & 0x3fff; + if (UNIV_UNLIKELY(len & 0x4000)) { + ut_ad(index->is_clustered()); + any_ext = REC_OFFS_EXTERNAL; + len = offs | REC_OFFS_EXTERNAL; + } else { + len = offs; + } + + goto resolved; + } + } + + len = offs += len; + } else { + len = offs += field->fixed_len; + } + resolved: + rec_offs_base(offsets)[i + 1] = len; + } while (++i < rec_offs_n_fields(offsets)); + + *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext; +} #endif diff --git a/storage/innobase/rem/rec.cc b/storage/innobase/rem/rec.cc index 3473683..1c50e6e 100644 --- a/storage/innobase/rem/rec.cc +++ b/storage/innobase/rem/rec.cc @@ -526,186 +526,6 @@ void rec_get_offsets_reverse( (lens - extra + REC_N_NEW_EXTRA_BYTES) | REC_OFFS_COMPACT | any_ext; } -void rec_init_offsets_comp_ordinary(const rec_t *rec, bool temp, - const dict_index_t *index, ulint *offsets) { -#ifdef UNIV_DEBUG - /* We cannot invoke rec_offs_make_valid() here if temp=true. - Similarly, rec_offs_validate() will fail in that case, because - it invokes rec_get_status(). */ - offsets[2] = (ulint)rec; - offsets[3] = (ulint)index; -#endif /* UNIV_DEBUG */ - - const byte *nulls = nullptr; - const byte *lens = nullptr; - uint16_t n_null = 0; - enum REC_INSERT_STATE rec_insert_state = REC_INSERT_STATE::NONE; - uint8_t row_version = UINT8_UNDEFINED; - uint16_t non_default_fields = 0; - - if (temp) { - rec_insert_state = rec_init_null_and_len_temp( - rec, index, &nulls, &lens, &n_null, non_default_fields, row_version); - } else { - rec_insert_state = rec_init_null_and_len_comp( - rec, index, &nulls, &lens, &n_null, non_default_fields, row_version); - } - - ut_ad(temp || dict_table_is_comp(index->table)); - - if (temp) { - if (dict_table_is_comp(index->table)) { - /* No need to do adjust fixed_len=0. We only need to - adjust it for ROW_FORMAT=REDUNDANT. */ - temp = false; - } else { - /* Redundant temp row. Old instant record is logged as version 0. */ - if (rec_insert_state == INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION || - rec_insert_state == INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION) { - rec_insert_state = INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION; - ut_ad(row_version == UINT8_UNDEFINED); - } - } - } - - /* read the lengths of fields 0..n */ - ulint offs = 0; - ulint any_ext = 0; - ulint null_mask = 1; - uint16_t i = 0; - do { - /* Fields are stored on disk in version they are added in and are - maintained in fields_array in the same order. Get the right field. */ - const dict_field_t *field = index->get_physical_field(i); - const dict_col_t *col = field->col; - uint64_t len; - - switch (rec_insert_state) { - case INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION: - ut_ad(!index->has_instant_cols_or_row_versions()); - break; - - case INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION: { - ut_ad(row_version == UINT8_UNDEFINED || row_version == 0); - ut_ad(index->has_row_versions() || temp); - /* Record has to be interpreted in v0. */ - row_version = 0; - } - [[fallthrough]]; - case INSERTED_AFTER_UPGRADE_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION: - case INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION: { - ut_ad(is_valid_row_version(row_version)); - /* A record may have version=0 if it's from upgrade table */ - ut_ad(index->has_row_versions() || - (index->table->is_upgraded_instant() && row_version == 0)); - - /* Based on the record version and column information, see if this - column is there in this record or not. */ - if (col->is_dropped_in_or_before(row_version)) { - /* This columns is dropped before or on this row version so its data - won't be there on row. So no need to store the length. Instead, store - offs ORed with REC_OFFS_DROP to indicate the same. */ - len = offs | REC_OFFS_DROP; - goto resolved; - - /* NOTE : Existing rows, which have data for this column, would still - need to process this column, so don't skip and store the correct - length there. Though it will be skipped while fetching row. */ - } else if (col->is_added_after(row_version)) { - /* This columns is added after this row version. In this case no need - to store the length. Instead store only if it is NULL or DEFAULT - value. */ - len = rec_get_instant_offset(index, i, offs); - - goto resolved; - } - } break; - - case INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION: - case INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION: { - ut_ad(non_default_fields > 0); - ut_ad(index->has_instant_cols()); - ut_ad(!is_valid_row_version(row_version)); - - if (i >= non_default_fields) { - /* This would be the case when column doesn't exists in the row. In - this case we need not to store the length. Instead we store only if - the column is NULL or DEFAULT value. */ - len = rec_get_instant_offset(index, i, offs); - - goto resolved; - } - - /* Note : Even if the column has been dropped, this row in V1 would - definitely have the value of this column. */ - } break; - - default: - ut_ad(false); - } - - if (!(col->prtype & DATA_NOT_NULL)) { - /* nullable field => read the null flag */ - ut_ad(n_null--); - - if (UNIV_UNLIKELY(!(byte)null_mask)) { - nulls--; - null_mask = 1; - } - - if (*nulls & null_mask) { - null_mask <<= 1; - /* No length is stored for NULL fields. - We do not advance offs, and we set - the length to zero and enable the - SQL NULL flag in offsets[]. */ - len = offs | REC_OFFS_SQL_NULL; - goto resolved; - } - null_mask <<= 1; - } - - if (!field->fixed_len || (temp && !col->get_fixed_size(temp))) { - ut_ad(col->mtype != DATA_POINT); - /* Variable-length field: read the length */ - len = *lens--; - /* If the maximum length of the field is up - to 255 bytes, the actual length is always - stored in one byte. If the maximum length is - more than 255 bytes, the actual length is - stored in one byte for 0..127. The length - will be encoded in two bytes when it is 128 or - more, or when the field is stored externally. */ - if (DATA_BIG_COL(col)) { - if (len & 0x80) { - /* 1exxxxxxx xxxxxxxx */ - len <<= 8; - len |= *lens--; - - offs += len & 0x3fff; - if (UNIV_UNLIKELY(len & 0x4000)) { - ut_ad(index->is_clustered()); - any_ext = REC_OFFS_EXTERNAL; - len = offs | REC_OFFS_EXTERNAL; - } else { - len = offs; - } - - goto resolved; - } - } - - len = offs += len; - } else { - len = offs += field->fixed_len; - } - resolved: - rec_offs_base(offsets)[i + 1] = len; - } while (++i < rec_offs_n_fields(offsets)); - - *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext; -} - #ifdef UNIV_DEBUG /** Check if the given two record offsets are identical. @param[in] offsets1 field offsets of a record diff --git a/storage/innobase/rem/rec.h b/storage/innobase/rem/rec.h index e4cf03e..0134e88 100644 --- a/storage/innobase/rem/rec.h +++ b/storage/innobase/rem/rec.h @@ -1032,6 +1032,13 @@ static inline enum REC_INSERT_STATE rec_init_null_and_len_comp( /* Position nulls */ *nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); +#if 1 + *n_null = index->n_nullable; + *lens = *nulls - UT_BITS_IN_BYTES(*n_null); + return INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION; +#endif + + const enum REC_INSERT_STATE rec_insert_state = get_rec_insert_state(index, rec, false); diff --git a/storage/innobase/rem/rem0wrec.cc b/storage/innobase/rem/rem0wrec.cc index ecd15cc..e5fd23f 100644 --- a/storage/innobase/rem/rem0wrec.cc +++ b/storage/innobase/rem/rem0wrec.cc @@ -77,12 +77,6 @@ static void validate_rec_offset(const dict_index_t *index, const ulint *offsets, } } -byte *rec_get_nth_field(const dict_index_t *index, const rec_t *rec, - const ulint *offsets, ulint n, ulint *len) { - byte *field = - const_cast(rec) + rec_get_nth_field_offs(index, offsets, n, len); - return (field); -} const byte *rec_get_nth_field_old(const dict_index_t *index, const rec_t *rec, ulint n, ulint *len) { @@ -107,14 +101,6 @@ ulint rec_get_nth_field_size(const dict_index_t *index, const rec_t *rec, return rec_get_nth_field_size_low(rec, n); } -ulint rec_get_nth_field_offs(const dict_index_t *index, const ulint *offsets, - ulint n, ulint *len) { - if (index && index->has_row_versions()) { - n = index->get_field_off_pos(n); - } - - return rec_get_nth_field_offs_low(offsets, n, len); -} ulint rec_get_nth_field_offs_old(const dict_index_t *index, const rec_t *rec, ulint n, ulint *len) { diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index e599601..cfb099f 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -91,6 +91,30 @@ constexpr uint32_t SEL_FOUND = 0; constexpr uint32_t SEL_EXHAUSTED = 1; constexpr uint32_t SEL_RETRY = 2; +static inline void validate_rec_offset_baotiao(const dict_index_t *index, const ulint *offsets, + ulint n, ut::Location L) { + ut_ad(rec_offs_validate(nullptr, nullptr, offsets)); + if (n >= rec_offs_n_fields(offsets)) { +#ifndef UNIV_NO_ERR_MSGS + //dump_metadata_dict_table(index->table); + auto num_fields = static_cast(rec_offs_n_fields(offsets)); + ib::fatal(L, ER_IB_DICT_INVALID_COLUMN_POSITION, ulonglong{n}, num_fields); +#endif /* !UNIV_NO_ERR_MSGS */ + } +} +static inline ulint rec_offs_nth_extern_low_baotiao(const ulint *offsets, ulint n) { + return (rec_offs_base(offsets)[1 + n] & REC_OFFS_EXTERNAL); +} +static inline ulint rec_offs_nth_extern_baotiao(const dict_index_t *index, const ulint *offsets, + ulint n) { + if (index && index->has_row_versions()) { + n = index->get_field_off_pos(n); + } + + validate_rec_offset_baotiao(index, offsets, n, UT_LOCATION_HERE); + return (rec_offs_nth_extern_low_baotiao(offsets, n)); +} + /** Returns true if the user-defined column in a secondary index record is alphabetically the same as the corresponding BLOB column in the clustered index record. @@ -267,7 +291,7 @@ static dberr_t row_sel_sec_rec_is_for_clust_rec( if (ifield->prefix_len > 0 && len != UNIV_SQL_NULL && sec_len != UNIV_SQL_NULL && !col->is_virtual()) { - if (rec_offs_nth_extern(clust_index, clust_offs, clust_pos)) { + if (rec_offs_nth_extern_baotiao(clust_index, clust_offs, clust_pos)) { len -= BTR_EXTERN_FIELD_REF_SIZE; } @@ -278,7 +302,7 @@ static dberr_t row_sel_sec_rec_is_for_clust_rec( /* Check sec index field matches that of cluster index in the case of for table with ATOMIC BLOB, note we also need to check if sec_len is 0 */ - if (rec_offs_nth_extern(clust_index, clust_offs, clust_pos) && + if (rec_offs_nth_extern_baotiao(clust_index, clust_offs, clust_pos) && (len < sec_len || (dict_table_has_atomic_blobs(sec_index->table) && sec_len == 0))) { if (!row_sel_sec_rec_is_for_blob( @@ -304,7 +328,7 @@ static dberr_t row_sel_sec_rec_is_for_clust_rec( /* For externally stored field, we need to get full geo data to generate the MBR for comparing. */ - if (rec_offs_nth_extern(clust_index, clust_offs, clust_pos)) { + if (rec_offs_nth_extern_baotiao(clust_index, clust_offs, clust_pos)) { dptr = lob::btr_copy_externally_stored_field( trx, clust_index, &clust_len, nullptr, dptr, dict_tf_get_page_size(sec_index->table->flags), len, @@ -483,7 +507,7 @@ static void row_sel_fetch_columns(trx_t *trx, dict_index_t *index, field_no = column->field_nos[index_type]; if (field_no != ULINT_UNDEFINED) { - if (UNIV_UNLIKELY(rec_offs_nth_extern(index, offsets, field_no))) { + if (UNIV_UNLIKELY(rec_offs_nth_extern_baotiao(index, offsets, field_no))) { /* Copy an externally stored field to the temporary heap, if possible. */ @@ -2744,7 +2768,7 @@ void row_sel_field_store_in_mysql_format_func( field_no = sec_field_no; } - if (rec_offs_nth_extern(rec_index, offsets, field_no)) { + if (rec_offs_nth_extern_baotiao(rec_index, offsets, field_no)) { /* Copy an externally stored field to a temporary heap */ ut_a(!prebuilt->trx->has_search_latch);