diff options
Diffstat (limited to 'storage/xtradb/row/row0row.c')
-rw-r--r-- | storage/xtradb/row/row0row.c | 1241 |
1 files changed, 1241 insertions, 0 deletions
diff --git a/storage/xtradb/row/row0row.c b/storage/xtradb/row/row0row.c new file mode 100644 index 00000000000..4343ee2b009 --- /dev/null +++ b/storage/xtradb/row/row0row.c @@ -0,0 +1,1241 @@ +/***************************************************************************** + +Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA + +*****************************************************************************/ + +/****************************************************** +General row routines + +Created 4/20/1996 Heikki Tuuri +*******************************************************/ + +#include "row0row.h" + +#ifdef UNIV_NONINL +#include "row0row.ic" +#endif + +#include "data0type.h" +#include "dict0dict.h" +#include "btr0btr.h" +#include "ha_prototypes.h" +#include "mach0data.h" +#include "trx0rseg.h" +#include "trx0trx.h" +#include "trx0roll.h" +#include "trx0undo.h" +#include "trx0purge.h" +#include "trx0rec.h" +#include "que0que.h" +#include "row0ext.h" +#include "row0upd.h" +#include "rem0cmp.h" +#include "read0read.h" +#include "ut0mem.h" + +/************************************************************************* +Gets the offset of trx id field, in bytes relative to the origin of +a clustered index record. */ +UNIV_INTERN +ulint +row_get_trx_id_offset( +/*==================*/ + /* out: offset of DATA_TRX_ID */ + const rec_t* rec __attribute__((unused)), + /* in: record */ + dict_index_t* index, /* in: clustered index */ + const ulint* offsets)/* in: rec_get_offsets(rec, index) */ +{ + ulint pos; + ulint offset; + ulint len; + + ut_ad(dict_index_is_clust(index)); + ut_ad(rec_offs_validate(rec, index, offsets)); + + pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID); + + offset = rec_get_nth_field_offs(offsets, pos, &len); + + ut_ad(len == DATA_TRX_ID_LEN); + + return(offset); +} + +/********************************************************************* +When an insert or purge to a table is performed, this function builds +the entry to be inserted into or purged from an index on the table. */ +UNIV_INTERN +dtuple_t* +row_build_index_entry( +/*==================*/ + /* out: index entry which should be + inserted or purged, or NULL if the + externally stored columns in the + clustered index record are unavailable + and ext != NULL */ + const dtuple_t* row, /* in: row which should be + inserted or purged */ + row_ext_t* ext, /* in: externally stored column prefixes, + or NULL */ + dict_index_t* index, /* in: index on the table */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the index entry is allocated */ +{ + dtuple_t* entry; + ulint entry_len; + ulint i; + + ut_ad(row && index && heap); + ut_ad(dtuple_check_typed(row)); + + entry_len = dict_index_get_n_fields(index); + entry = dtuple_create(heap, entry_len); + + if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { + dtuple_set_n_fields_cmp(entry, entry_len); + /* There may only be externally stored columns + in a clustered index B-tree of a user table. */ + ut_a(!ext); + } else { + dtuple_set_n_fields_cmp( + entry, dict_index_get_n_unique_in_tree(index)); + } + + for (i = 0; i < entry_len; i++) { + const dict_field_t* ind_field + = dict_index_get_nth_field(index, i); + const dict_col_t* col + = ind_field->col; + ulint col_no + = dict_col_get_no(col); + dfield_t* dfield + = dtuple_get_nth_field(entry, i); + const dfield_t* dfield2 + = dtuple_get_nth_field(row, col_no); + ulint len + = dfield_get_len(dfield2); + + dfield_copy(dfield, dfield2); + + if (dfield_is_null(dfield) || ind_field->prefix_len == 0) { + continue; + } + + /* If a column prefix index, take only the prefix. + Prefix-indexed columns may be externally stored. */ + ut_ad(col->ord_part); + + if (UNIV_LIKELY_NULL(ext)) { + /* See if the column is stored externally. */ + const byte* buf = row_ext_lookup(ext, col_no, + &len); + if (UNIV_LIKELY_NULL(buf)) { + if (UNIV_UNLIKELY(buf == field_ref_zero)) { + return(NULL); + } + dfield_set_data(dfield, buf, len); + } + } else if (dfield_is_ext(dfield)) { + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + len -= BTR_EXTERN_FIELD_REF_SIZE; + ut_a(ind_field->prefix_len <= len + || dict_index_is_clust(index)); + } + + len = dtype_get_at_most_n_mbchars( + col->prtype, col->mbminlen, col->mbmaxlen, + ind_field->prefix_len, len, dfield_get_data(dfield)); + dfield_set_len(dfield, len); + } + + ut_ad(dtuple_check_typed(entry)); + + return(entry); +} + +/*********************************************************************** +An inverse function to row_build_index_entry. Builds a row from a +record in a clustered index. */ +UNIV_INTERN +dtuple_t* +row_build( +/*======*/ + /* out, own: row built; + see the NOTE below! */ + ulint type, /* in: ROW_COPY_POINTERS or + ROW_COPY_DATA; the latter + copies also the data fields to + heap while the first only + places pointers to data fields + on the index page, and thus is + more efficient */ + const dict_index_t* index, /* in: clustered index */ + const rec_t* rec, /* in: record in the clustered + index; NOTE: in the case + ROW_COPY_POINTERS the data + fields in the row will point + directly into this record, + therefore, the buffer page of + this record must be at least + s-latched and the latch held + as long as the row dtuple is used! */ + const ulint* offsets,/* in: rec_get_offsets(rec,index) + or NULL, in which case this function + will invoke rec_get_offsets() */ + const dict_table_t* col_table, + /* in: table, to check which + externally stored columns + occur in the ordering columns + of an index, or NULL if + index->table should be + consulted instead */ + row_ext_t** ext, /* out, own: cache of + externally stored column + prefixes, or NULL */ + mem_heap_t* heap) /* in: memory heap from which + the memory needed is allocated */ +{ + dtuple_t* row; + const dict_table_t* table; + ulint n_fields; + ulint n_ext_cols; + ulint* ext_cols = NULL; /* remove warning */ + ulint len; + ulint row_len; + byte* buf; + ulint i; + ulint j; + mem_heap_t* tmp_heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs_init(offsets_); + + ut_ad(index && rec && heap); + ut_ad(dict_index_is_clust(index)); + + if (!offsets) { + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &tmp_heap); + } else { + ut_ad(rec_offs_validate(rec, index, offsets)); + } + + if (type != ROW_COPY_POINTERS) { + /* Take a copy of rec to heap */ + buf = mem_heap_alloc(heap, rec_offs_size(offsets)); + rec = rec_copy(buf, rec, offsets); + /* Avoid a debug assertion in rec_offs_validate(). */ + rec_offs_make_valid(rec, index, (ulint*) offsets); + } + + table = index->table; + row_len = dict_table_get_n_cols(table); + + row = dtuple_create(heap, row_len); + + dict_table_copy_types(row, table); + + dtuple_set_info_bits(row, rec_get_info_bits( + rec, dict_table_is_comp(table))); + + n_fields = rec_offs_n_fields(offsets); + n_ext_cols = rec_offs_n_extern(offsets); + if (n_ext_cols) { + ext_cols = mem_heap_alloc(heap, n_ext_cols * sizeof *ext_cols); + } + + for (i = j = 0; i < n_fields; i++) { + dict_field_t* ind_field + = dict_index_get_nth_field(index, i); + const dict_col_t* col + = dict_field_get_col(ind_field); + ulint col_no + = dict_col_get_no(col); + dfield_t* dfield + = dtuple_get_nth_field(row, col_no); + + if (ind_field->prefix_len == 0) { + + const byte* field = rec_get_nth_field( + rec, offsets, i, &len); + + dfield_set_data(dfield, field, len); + } + + if (rec_offs_nth_extern(offsets, i)) { + dfield_set_ext(dfield); + + if (UNIV_LIKELY_NULL(col_table)) { + ut_a(col_no + < dict_table_get_n_cols(col_table)); + col = dict_table_get_nth_col( + col_table, col_no); + } + + if (col->ord_part) { + /* We will have to fetch prefixes of + externally stored columns that are + referenced by column prefixes. */ + ext_cols[j++] = col_no; + } + } + } + + ut_ad(dtuple_check_typed(row)); + + if (j) { + *ext = row_ext_create(j, ext_cols, row, + dict_table_zip_size(index->table), + heap); + } else { + *ext = NULL; + } + + if (tmp_heap) { + mem_heap_free(tmp_heap); + } + + return(row); +} + +/*********************************************************************** +Converts an index record to a typed data tuple. */ +UNIV_INTERN +dtuple_t* +row_rec_to_index_entry_low( +/*=======================*/ + /* out: index entry built; does not + set info_bits, and the data fields in + the entry will point directly to rec */ + const rec_t* rec, /* in: record in the index */ + const dict_index_t* index, /* in: index */ + const ulint* offsets,/* in: rec_get_offsets(rec, index) */ + ulint* n_ext, /* out: number of externally + stored columns */ + mem_heap_t* heap) /* in: memory heap from which + the memory needed is allocated */ +{ + dtuple_t* entry; + dfield_t* dfield; + ulint i; + const byte* field; + ulint len; + ulint rec_len; + + ut_ad(rec && heap && index); + /* Because this function may be invoked by row0merge.c + on a record whose header is in different format, the check + rec_offs_validate(rec, index, offsets) must be avoided here. */ + ut_ad(n_ext); + *n_ext = 0; + + rec_len = rec_offs_n_fields(offsets); + + entry = dtuple_create(heap, rec_len); + + dtuple_set_n_fields_cmp(entry, + dict_index_get_n_unique_in_tree(index)); + ut_ad(rec_len == dict_index_get_n_fields(index)); + + dict_index_copy_types(entry, index, rec_len); + + for (i = 0; i < rec_len; i++) { + + dfield = dtuple_get_nth_field(entry, i); + field = rec_get_nth_field(rec, offsets, i, &len); + + dfield_set_data(dfield, field, len); + + if (rec_offs_nth_extern(offsets, i)) { + dfield_set_ext(dfield); + (*n_ext)++; + } + } + + ut_ad(dtuple_check_typed(entry)); + + return(entry); +} + +/*********************************************************************** +Converts an index record to a typed data tuple. NOTE that externally +stored (often big) fields are NOT copied to heap. */ +UNIV_INTERN +dtuple_t* +row_rec_to_index_entry( +/*===================*/ + /* out, own: index entry + built; see the NOTE below! */ + ulint type, /* in: ROW_COPY_DATA, or + ROW_COPY_POINTERS: the former + copies also the data fields to + heap as the latter only places + pointers to data fields on the + index page */ + const rec_t* rec, /* in: record in the index; + NOTE: in the case + ROW_COPY_POINTERS the data + fields in the row will point + directly into this record, + therefore, the buffer page of + this record must be at least + s-latched and the latch held + as long as the dtuple is used! */ + const dict_index_t* index, /* in: index */ + ulint* offsets,/* in/out: rec_get_offsets(rec) */ + ulint* n_ext, /* out: number of externally + stored columns */ + mem_heap_t* heap) /* in: memory heap from which + the memory needed is allocated */ +{ + dtuple_t* entry; + byte* buf; + + ut_ad(rec && heap && index); + ut_ad(rec_offs_validate(rec, index, offsets)); + + if (type == ROW_COPY_DATA) { + /* Take a copy of rec to heap */ + buf = mem_heap_alloc(heap, rec_offs_size(offsets)); + rec = rec_copy(buf, rec, offsets); + /* Avoid a debug assertion in rec_offs_validate(). */ + rec_offs_make_valid(rec, index, offsets); + } + + entry = row_rec_to_index_entry_low(rec, index, offsets, n_ext, heap); + + dtuple_set_info_bits(entry, + rec_get_info_bits(rec, rec_offs_comp(offsets))); + + return(entry); +} + +/*********************************************************************** +Builds from a secondary index record a row reference with which we can +search the clustered index record. */ +UNIV_INTERN +dtuple_t* +row_build_row_ref( +/*==============*/ + /* out, own: row reference built; see the + NOTE below! */ + ulint type, /* in: ROW_COPY_DATA, or ROW_COPY_POINTERS: + the former copies also the data fields to + heap, whereas the latter only places pointers + to data fields on the index page */ + dict_index_t* index, /* in: secondary index */ + const rec_t* rec, /* in: record in the index; + NOTE: in the case ROW_COPY_POINTERS + the data fields in the row will point + directly into this record, therefore, + the buffer page of this record must be + at least s-latched and the latch held + as long as the row reference is used! */ + mem_heap_t* heap) /* in: memory heap from which the memory + needed is allocated */ +{ + dict_table_t* table; + dict_index_t* clust_index; + dfield_t* dfield; + dtuple_t* ref; + const byte* field; + ulint len; + ulint ref_len; + ulint pos; + byte* buf; + ulint clust_col_prefix_len; + ulint i; + mem_heap_t* tmp_heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + rec_offs_init(offsets_); + + ut_ad(index && rec && heap); + ut_ad(!dict_index_is_clust(index)); + + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &tmp_heap); + /* Secondary indexes must not contain externally stored columns. */ + ut_ad(!rec_offs_any_extern(offsets)); + + if (type == ROW_COPY_DATA) { + /* Take a copy of rec to heap */ + + buf = mem_heap_alloc(heap, rec_offs_size(offsets)); + + rec = rec_copy(buf, rec, offsets); + /* Avoid a debug assertion in rec_offs_validate(). */ + rec_offs_make_valid(rec, index, offsets); + } + + table = index->table; + + clust_index = dict_table_get_first_index(table); + + ref_len = dict_index_get_n_unique(clust_index); + + ref = dtuple_create(heap, ref_len); + + dict_index_copy_types(ref, clust_index, ref_len); + + for (i = 0; i < ref_len; i++) { + dfield = dtuple_get_nth_field(ref, i); + + pos = dict_index_get_nth_field_pos(index, clust_index, i); + + ut_a(pos != ULINT_UNDEFINED); + + field = rec_get_nth_field(rec, offsets, pos, &len); + + dfield_set_data(dfield, field, len); + + /* If the primary key contains a column prefix, then the + secondary index may contain a longer prefix of the same + column, or the full column, and we must adjust the length + accordingly. */ + + clust_col_prefix_len = dict_index_get_nth_field( + clust_index, i)->prefix_len; + + if (clust_col_prefix_len > 0) { + if (len != UNIV_SQL_NULL) { + + const dtype_t* dtype + = dfield_get_type(dfield); + + dfield_set_len(dfield, + dtype_get_at_most_n_mbchars( + dtype->prtype, + dtype->mbminlen, + dtype->mbmaxlen, + clust_col_prefix_len, + len, (char*) field)); + } + } + } + + ut_ad(dtuple_check_typed(ref)); + if (tmp_heap) { + mem_heap_free(tmp_heap); + } + + return(ref); +} + +/*********************************************************************** +Builds from a secondary index record a row reference with which we can +search the clustered index record. */ +UNIV_INTERN +void +row_build_row_ref_in_tuple( +/*=======================*/ + dtuple_t* ref, /* in/out: row reference built; + see the NOTE below! */ + const rec_t* rec, /* in: record in the index; + NOTE: the data fields in ref + will point directly into this + record, therefore, the buffer + page of this record must be at + least s-latched and the latch + held as long as the row + reference is used! */ + const dict_index_t* index, /* in: secondary index */ + ulint* offsets,/* in: rec_get_offsets(rec, index) + or NULL */ + trx_t* trx) /* in: transaction */ +{ + const dict_index_t* clust_index; + dfield_t* dfield; + const byte* field; + ulint len; + ulint ref_len; + ulint pos; + ulint clust_col_prefix_len; + ulint i; + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs_init(offsets_); + + ut_a(ref); + ut_a(index); + ut_a(rec); + ut_ad(!dict_index_is_clust(index)); + + if (UNIV_UNLIKELY(!index->table)) { + fputs("InnoDB: table ", stderr); +notfound: + ut_print_name(stderr, trx, TRUE, index->table_name); + fputs(" for index ", stderr); + ut_print_name(stderr, trx, FALSE, index->name); + fputs(" not found\n", stderr); + ut_error; + } + + clust_index = dict_table_get_first_index(index->table); + + if (UNIV_UNLIKELY(!clust_index)) { + fputs("InnoDB: clust index for table ", stderr); + goto notfound; + } + + if (!offsets) { + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap); + } else { + ut_ad(rec_offs_validate(rec, index, offsets)); + } + + /* Secondary indexes must not contain externally stored columns. */ + ut_ad(!rec_offs_any_extern(offsets)); + ref_len = dict_index_get_n_unique(clust_index); + + ut_ad(ref_len == dtuple_get_n_fields(ref)); + + dict_index_copy_types(ref, clust_index, ref_len); + + for (i = 0; i < ref_len; i++) { + dfield = dtuple_get_nth_field(ref, i); + + pos = dict_index_get_nth_field_pos(index, clust_index, i); + + ut_a(pos != ULINT_UNDEFINED); + + field = rec_get_nth_field(rec, offsets, pos, &len); + + dfield_set_data(dfield, field, len); + + /* If the primary key contains a column prefix, then the + secondary index may contain a longer prefix of the same + column, or the full column, and we must adjust the length + accordingly. */ + + clust_col_prefix_len = dict_index_get_nth_field( + clust_index, i)->prefix_len; + + if (clust_col_prefix_len > 0) { + if (len != UNIV_SQL_NULL) { + + const dtype_t* dtype + = dfield_get_type(dfield); + + dfield_set_len(dfield, + dtype_get_at_most_n_mbchars( + dtype->prtype, + dtype->mbminlen, + dtype->mbmaxlen, + clust_col_prefix_len, + len, (char*) field)); + } + } + } + + ut_ad(dtuple_check_typed(ref)); + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } +} + +/*********************************************************************** +From a row build a row reference with which we can search the clustered +index record. */ +UNIV_INTERN +void +row_build_row_ref_from_row( +/*=======================*/ + dtuple_t* ref, /* in/out: row reference built; + see the NOTE below! + ref must have the right number + of fields! */ + const dict_table_t* table, /* in: table */ + const dtuple_t* row) /* in: row + NOTE: the data fields in ref will point + directly into data of this row */ +{ + const dict_index_t* clust_index; + ulint ref_len; + ulint i; + + ut_ad(ref && table && row); + + clust_index = dict_table_get_first_index(table); + + ref_len = dict_index_get_n_unique(clust_index); + + ut_ad(ref_len == dtuple_get_n_fields(ref)); + + for (i = 0; i < ref_len; i++) { + const dict_col_t* col; + const dict_field_t* field; + dfield_t* dfield; + const dfield_t* dfield2; + + dfield = dtuple_get_nth_field(ref, i); + + field = dict_index_get_nth_field(clust_index, i); + + col = dict_field_get_col(field); + + dfield2 = dtuple_get_nth_field(row, dict_col_get_no(col)); + + dfield_copy(dfield, dfield2); + ut_ad(!dfield_is_ext(dfield)); + + if (field->prefix_len > 0 && !dfield_is_null(dfield)) { + + ulint len = dfield_get_len(dfield); + + len = dtype_get_at_most_n_mbchars( + col->prtype, col->mbminlen, col->mbmaxlen, + field->prefix_len, + len, dfield_get_data(dfield)); + + dfield_set_len(dfield, len); + } + } + + ut_ad(dtuple_check_typed(ref)); +} + +/******************************************************************* +Searches the clustered index record for a row, if we have the row reference. */ +UNIV_INTERN +ibool +row_search_on_row_ref( +/*==================*/ + /* out: TRUE if found */ + btr_pcur_t* pcur, /* out: persistent cursor, which must + be closed by the caller */ + ulint mode, /* in: BTR_MODIFY_LEAF, ... */ + const dict_table_t* table, /* in: table */ + const dtuple_t* ref, /* in: row reference */ + mtr_t* mtr) /* in/out: mtr */ +{ + ulint low_match; + rec_t* rec; + dict_index_t* index; + + ut_ad(dtuple_check_typed(ref)); + + index = dict_table_get_first_index(table); + + ut_a(dtuple_get_n_fields(ref) == dict_index_get_n_unique(index)); + + btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr); + + low_match = btr_pcur_get_low_match(pcur); + + rec = btr_pcur_get_rec(pcur); + + if (page_rec_is_infimum(rec)) { + + return(FALSE); + } + + if (low_match != dtuple_get_n_fields(ref)) { + + return(FALSE); + } + + return(TRUE); +} + +/************************************************************************* +Fetches the clustered index record for a secondary index record. The latches +on the secondary index record are preserved. */ +UNIV_INTERN +rec_t* +row_get_clust_rec( +/*==============*/ + /* out: record or NULL, if no record found */ + ulint mode, /* in: BTR_MODIFY_LEAF, ... */ + const rec_t* rec, /* in: record in a secondary index */ + dict_index_t* index, /* in: secondary index */ + dict_index_t** clust_index,/* out: clustered index */ + mtr_t* mtr) /* in: mtr */ +{ + mem_heap_t* heap; + dtuple_t* ref; + dict_table_t* table; + btr_pcur_t pcur; + ibool found; + rec_t* clust_rec; + + ut_ad(!dict_index_is_clust(index)); + + table = index->table; + + heap = mem_heap_create(256); + + ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, heap); + + found = row_search_on_row_ref(&pcur, mode, table, ref, mtr); + + clust_rec = found ? btr_pcur_get_rec(&pcur) : NULL; + + mem_heap_free(heap); + + btr_pcur_close(&pcur); + + *clust_index = dict_table_get_first_index(table); + + return(clust_rec); +} + +/******************************************************************* +Searches an index record. */ +UNIV_INTERN +ibool +row_search_index_entry( +/*===================*/ + /* out: TRUE if found */ + dict_index_t* index, /* in: index */ + const dtuple_t* entry, /* in: index entry */ + ulint mode, /* in: BTR_MODIFY_LEAF, ... */ + btr_pcur_t* pcur, /* in/out: persistent cursor, which must + be closed by the caller */ + mtr_t* mtr) /* in: mtr */ +{ + ulint n_fields; + ulint low_match; + rec_t* rec; + + ut_ad(dtuple_check_typed(entry)); + + btr_pcur_open(index, entry, PAGE_CUR_LE, mode, pcur, mtr); + low_match = btr_pcur_get_low_match(pcur); + + rec = btr_pcur_get_rec(pcur); + + n_fields = dtuple_get_n_fields(entry); + + return(!page_rec_is_infimum(rec) && low_match == n_fields); +} + +#ifndef UNIV_HOTBACKUP + +#include <my_sys.h> + +/*********************************************************************** +Formats the raw data in "data" (in InnoDB on-disk format) that is of +type DATA_INT using "prtype" and writes the result to "buf". +If the data is in unknown format, then nothing is written to "buf", +0 is returned and "format_in_hex" is set to TRUE, otherwise +"format_in_hex" is left untouched. +Not more than "buf_size" bytes are written to "buf". +The result is always '\0'-terminated (provided buf_size > 0) and the +number of bytes that were written to "buf" is returned (including the +terminating '\0'). */ +static +ulint +row_raw_format_int( +/*===============*/ + /* out: number of bytes + that were written */ + const char* data, /* in: raw data */ + ulint data_len, /* in: raw data length + in bytes */ + ulint prtype, /* in: precise type */ + char* buf, /* out: output buffer */ + ulint buf_size, /* in: output buffer size + in bytes */ + ibool* format_in_hex) /* out: should the data be + formated in hex */ +{ + ulint ret; + + if (data_len <= sizeof(ullint)) { + + ullint value; + ibool unsigned_type = prtype & DATA_UNSIGNED; + + value = mach_read_int_type((const byte*) data, + data_len, unsigned_type); + + if (unsigned_type) { + + ret = ut_snprintf(buf, buf_size, "%llu", + value) + 1; + } else { + + ret = ut_snprintf(buf, buf_size, "%lld", + (long long) value) + 1; + } + + } else { + + *format_in_hex = TRUE; + ret = 0; + } + + return(ut_min(ret, buf_size)); +} + +/*********************************************************************** +Formats the raw data in "data" (in InnoDB on-disk format) that is of +type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "prtype" and writes the +result to "buf". +If the data is in binary format, then nothing is written to "buf", +0 is returned and "format_in_hex" is set to TRUE, otherwise +"format_in_hex" is left untouched. +Not more than "buf_size" bytes are written to "buf". +The result is always '\0'-terminated (provided buf_size > 0) and the +number of bytes that were written to "buf" is returned (including the +terminating '\0'). */ +static +ulint +row_raw_format_str( +/*===============*/ + /* out: number of bytes + that were written */ + const char* data, /* in: raw data */ + ulint data_len, /* in: raw data length + in bytes */ + ulint prtype, /* in: precise type */ + char* buf, /* out: output buffer */ + ulint buf_size, /* in: output buffer size + in bytes */ + ibool* format_in_hex) /* out: should the data be + formated in hex */ +{ + ulint charset_coll; + + if (buf_size == 0) { + + return(0); + } + + /* we assume system_charset_info is UTF-8 */ + + charset_coll = dtype_get_charset_coll(prtype); + + if (UNIV_LIKELY(dtype_is_utf8(prtype))) { + + return(ut_str_sql_format(data, data_len, buf, buf_size)); + } + /* else */ + + if (charset_coll == DATA_MYSQL_BINARY_CHARSET_COLL) { + + *format_in_hex = TRUE; + return(0); + } + /* else */ + + return(innobase_raw_format(data, data_len, charset_coll, + buf, buf_size)); +} + +/*********************************************************************** +Formats the raw data in "data" (in InnoDB on-disk format) using +"dict_field" and writes the result to "buf". +Not more than "buf_size" bytes are written to "buf". +The result is always '\0'-terminated (provided buf_size > 0) and the +number of bytes that were written to "buf" is returned (including the +terminating '\0'). */ +UNIV_INTERN +ulint +row_raw_format( +/*===========*/ + /* out: number of bytes + that were written */ + const char* data, /* in: raw data */ + ulint data_len, /* in: raw data length + in bytes */ + const dict_field_t* dict_field, /* in: index field */ + char* buf, /* out: output buffer */ + ulint buf_size) /* in: output buffer size + in bytes */ +{ + ulint mtype; + ulint prtype; + ulint ret; + ibool format_in_hex; + + if (buf_size == 0) { + + return(0); + } + + if (data_len == UNIV_SQL_NULL) { + + ret = ut_snprintf((char*) buf, buf_size, "NULL") + 1; + + return(ut_min(ret, buf_size)); + } + + mtype = dict_field->col->mtype; + prtype = dict_field->col->prtype; + + format_in_hex = FALSE; + + switch (mtype) { + case DATA_INT: + + ret = row_raw_format_int(data, data_len, prtype, + buf, buf_size, &format_in_hex); + break; + case DATA_CHAR: + case DATA_VARCHAR: + case DATA_MYSQL: + case DATA_VARMYSQL: + + ret = row_raw_format_str(data, data_len, prtype, + buf, buf_size, &format_in_hex); + break; + /* XXX support more data types */ + default: + + format_in_hex = TRUE; + } + + if (format_in_hex) { + + if (UNIV_LIKELY(buf_size > 2)) { + + memcpy(buf, "0x", 2); + buf += 2; + buf_size -= 2; + ret = 2 + ut_raw_to_hex(data, data_len, + buf, buf_size); + } else { + + buf[0] = '\0'; + ret = 1; + } + } + + return(ret); +} + +#endif /* !UNIV_HOTBACKUP */ + +#ifdef UNIV_COMPILE_TEST_FUNCS + +#include "ut0dbg.h" + +void +test_row_raw_format_int() +{ + ulint ret; + char buf[128]; + ibool format_in_hex; + +#define CALL_AND_TEST(data, data_len, prtype, buf, buf_size,\ + ret_expected, buf_expected, format_in_hex_expected)\ + do {\ + ibool ok = TRUE;\ + ulint i;\ + memset(buf, 'x', 10);\ + buf[10] = '\0';\ + format_in_hex = FALSE;\ + fprintf(stderr, "TESTING \"\\x");\ + for (i = 0; i < data_len; i++) {\ + fprintf(stderr, "%02hhX", data[i]);\ + }\ + fprintf(stderr, "\", %lu, %lu, %lu\n",\ + (ulint) data_len, (ulint) prtype,\ + (ulint) buf_size);\ + ret = row_raw_format_int(data, data_len, prtype,\ + buf, buf_size, &format_in_hex);\ + if (ret != ret_expected) {\ + fprintf(stderr, "expected ret %lu, got %lu\n",\ + (ulint) ret_expected, ret);\ + ok = FALSE;\ + }\ + if (strcmp((char*) buf, buf_expected) != 0) {\ + fprintf(stderr, "expected buf \"%s\", got \"%s\"\n",\ + buf_expected, buf);\ + ok = FALSE;\ + }\ + if (format_in_hex != format_in_hex_expected) {\ + fprintf(stderr, "expected format_in_hex %d, got %d\n",\ + (int) format_in_hex_expected,\ + (int) format_in_hex);\ + ok = FALSE;\ + }\ + if (ok) {\ + fprintf(stderr, "OK: %lu, \"%s\" %d\n\n",\ + (ulint) ret, buf, (int) format_in_hex);\ + } else {\ + return;\ + }\ + } while (0) + +#if 1 + /* min values for signed 1-8 byte integers */ + + CALL_AND_TEST("\x00", 1, 0, + buf, sizeof(buf), 5, "-128", 0); + + CALL_AND_TEST("\x00\x00", 2, 0, + buf, sizeof(buf), 7, "-32768", 0); + + CALL_AND_TEST("\x00\x00\x00", 3, 0, + buf, sizeof(buf), 9, "-8388608", 0); + + CALL_AND_TEST("\x00\x00\x00\x00", 4, 0, + buf, sizeof(buf), 12, "-2147483648", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, 0, + buf, sizeof(buf), 14, "-549755813888", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, 0, + buf, sizeof(buf), 17, "-140737488355328", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, 0, + buf, sizeof(buf), 19, "-36028797018963968", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, 0, + buf, sizeof(buf), 21, "-9223372036854775808", 0); + + /* min values for unsigned 1-8 byte integers */ + + CALL_AND_TEST("\x00", 1, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00", 2, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00", 3, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00\x00", 4, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, DATA_UNSIGNED, + buf, sizeof(buf), 2, "0", 0); + + /* max values for signed 1-8 byte integers */ + + CALL_AND_TEST("\xFF", 1, 0, + buf, sizeof(buf), 4, "127", 0); + + CALL_AND_TEST("\xFF\xFF", 2, 0, + buf, sizeof(buf), 6, "32767", 0); + + CALL_AND_TEST("\xFF\xFF\xFF", 3, 0, + buf, sizeof(buf), 8, "8388607", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, 0, + buf, sizeof(buf), 11, "2147483647", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, 0, + buf, sizeof(buf), 13, "549755813887", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, 0, + buf, sizeof(buf), 16, "140737488355327", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, 0, + buf, sizeof(buf), 18, "36028797018963967", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, 0, + buf, sizeof(buf), 20, "9223372036854775807", 0); + + /* max values for unsigned 1-8 byte integers */ + + CALL_AND_TEST("\xFF", 1, DATA_UNSIGNED, + buf, sizeof(buf), 4, "255", 0); + + CALL_AND_TEST("\xFF\xFF", 2, DATA_UNSIGNED, + buf, sizeof(buf), 6, "65535", 0); + + CALL_AND_TEST("\xFF\xFF\xFF", 3, DATA_UNSIGNED, + buf, sizeof(buf), 9, "16777215", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, DATA_UNSIGNED, + buf, sizeof(buf), 11, "4294967295", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, DATA_UNSIGNED, + buf, sizeof(buf), 14, "1099511627775", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, DATA_UNSIGNED, + buf, sizeof(buf), 16, "281474976710655", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, DATA_UNSIGNED, + buf, sizeof(buf), 18, "72057594037927935", 0); + + CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, DATA_UNSIGNED, + buf, sizeof(buf), 21, "18446744073709551615", 0); + + /* some random values */ + + CALL_AND_TEST("\x52", 1, 0, + buf, sizeof(buf), 4, "-46", 0); + + CALL_AND_TEST("\x0E", 1, DATA_UNSIGNED, + buf, sizeof(buf), 3, "14", 0); + + CALL_AND_TEST("\x62\xCE", 2, 0, + buf, sizeof(buf), 6, "-7474", 0); + + CALL_AND_TEST("\x29\xD6", 2, DATA_UNSIGNED, + buf, sizeof(buf), 6, "10710", 0); + + CALL_AND_TEST("\x7F\xFF\x90", 3, 0, + buf, sizeof(buf), 5, "-112", 0); + + CALL_AND_TEST("\x00\xA1\x16", 3, DATA_UNSIGNED, + buf, sizeof(buf), 6, "41238", 0); + + CALL_AND_TEST("\x7F\xFF\xFF\xF7", 4, 0, + buf, sizeof(buf), 3, "-9", 0); + + CALL_AND_TEST("\x00\x00\x00\x5C", 4, DATA_UNSIGNED, + buf, sizeof(buf), 3, "92", 0); + + CALL_AND_TEST("\x7F\xFF\xFF\xFF\xFF\xFF\xDC\x63", 8, 0, + buf, sizeof(buf), 6, "-9117", 0); + + CALL_AND_TEST("\x00\x00\x00\x00\x00\x01\x64\x62", 8, DATA_UNSIGNED, + buf, sizeof(buf), 6, "91234", 0); +#endif + + /* speed test */ + + speedo_t speedo; + ulint i; + + speedo_reset(&speedo); + + for (i = 0; i < 1000000; i++) { + row_raw_format_int("\x23", 1, + 0, buf, sizeof(buf), + &format_in_hex); + row_raw_format_int("\x23", 1, + DATA_UNSIGNED, buf, sizeof(buf), + &format_in_hex); + + row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8, + 0, buf, sizeof(buf), + &format_in_hex); + row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8, + DATA_UNSIGNED, buf, sizeof(buf), + &format_in_hex); + } + + speedo_show(&speedo); +} + +#endif /* UNIV_COMPILE_TEST_FUNCS */ |