1
0
mirror of https://github.com/MariaDB/server.git synced 2025-04-18 21:44:20 +03:00

MDEV-36015: unrepresentable value in row_parse_int()

row_parse_int(): Refactor the code and define the function static in
one compilation unit. For any negative values, we must return 0.

row_search_get_max_rec(), row_search_max_autoinc(): Moved to the same
compilation unit with row_parse_int().

We also remove a work-around of an internal compiler error when
targeting ARMv8 on GCC 4.8.5, a compiler that is no longer supported.

Reviewed by: Debarun Banerjee
This commit is contained in:
Marko Mäkelä 2025-02-12 10:14:10 +02:00 committed by Sergei Golubchik
parent 44e1f7238a
commit c07e355c40
7 changed files with 151 additions and 194 deletions

View File

@ -190,8 +190,7 @@ a
100000000000
100000000006
CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0),
(20), (30), (31);
INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
SELECT * FROM t11;
a
-10
@ -204,9 +203,22 @@ a
20
30
31
CREATE TABLE t11u(a FLOAT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t11u VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
ERROR 22003: Out of range value for column 'a' at row 5
INSERT INTO t11u VALUES(0), (0), (0), (0), (0), (20), (30), (31);
SELECT * FROM t11u;
a
11
12
13
14
15
20
30
31
CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0),
(20), (30), (31);
INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
SELECT * FROM t12;
a
-10
@ -219,6 +231,20 @@ a
20
30
31
CREATE TABLE t12u(a DOUBLE UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t12u VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
ERROR 22003: Out of range value for column 'a' at row 5
INSERT INTO t12u VALUES(0), (0), (0), (0), (0), (20), (30), (31);
SELECT * FROM t12u;
a
11
12
13
14
15
20
30
31
# Scenario 1: Normal restart, to test if the counters are persisted
# Scenario 2: Delete some values, to test the counters should not be the
# one which is the largest in current table
@ -981,4 +1007,5 @@ a b
10 1
2 2
3 4
DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t30, t32, t33;
DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t11u, t12u,
t30, t32, t33;

View File

@ -82,15 +82,25 @@ INSERT INTO t10 VALUES(0), (0), (0), (0), (8), (10), (0),
SELECT * FROM t10;
CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0),
(20), (30), (31);
INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
SELECT * FROM t11;
CREATE TABLE t11u(a FLOAT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB;
--error ER_WARN_DATA_OUT_OF_RANGE
INSERT INTO t11u VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
INSERT INTO t11u VALUES(0), (0), (0), (0), (0), (20), (30), (31);
SELECT * FROM t11u;
CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB;
INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0),
(20), (30), (31);
INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
SELECT * FROM t12;
CREATE TABLE t12u(a DOUBLE UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB;
--error ER_WARN_DATA_OUT_OF_RANGE
INSERT INTO t12u VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31);
INSERT INTO t12u VALUES(0), (0), (0), (0), (0), (20), (30), (31);
SELECT * FROM t12u;
--echo # Scenario 1: Normal restart, to test if the counters are persisted
--echo # Scenario 2: Delete some values, to test the counters should not be the
--echo # one which is the largest in current table
@ -556,4 +566,5 @@ INSERT INTO t33 VALUES(3, NULL);
SELECT MAX(b) AS `Expect 4` FROM t33;
SELECT * FROM t33;
DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t30, t32, t33;
DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t11u, t12u,
t30, t32, t33;

View File

@ -328,22 +328,6 @@ row_get_clust_rec(
mtr_t* mtr) /*!< in: mtr */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Parse the integer data from specified data, which could be
DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0
and the type is not unsigned then we reset the value to 0
@param[in] data data to read
@param[in] len length of data
@param[in] mtype mtype of data
@param[in] unsigned_type if the data is unsigned
@return the integer value from the data */
inline
ib_uint64_t
row_parse_int(
const byte* data,
ulint len,
ulint mtype,
bool unsigned_type);
/** Result of row_search_index_entry */
enum row_search_result {
ROW_FOUND = 0, /*!< the record was found */

View File

@ -170,52 +170,3 @@ row_build_row_ref_fast(
}
}
}
/** Parse the integer data from specified data, which could be
DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0
and the type is not unsigned then we reset the value to 0
@param[in] data data to read
@param[in] len length of data
@param[in] mtype mtype of data
@param[in] unsigned_type if the data is unsigned
@return the integer value from the data */
ib_uint64_t
row_parse_int(
const byte* data,
ulint len,
ulint mtype,
bool unsigned_type)
{
ib_uint64_t value = 0;
switch (mtype) {
case DATA_INT:
ut_a(len <= sizeof value);
value = mach_read_int_type(data, len, unsigned_type);
break;
case DATA_FLOAT:
ut_a(len == sizeof(float));
value = static_cast<ib_uint64_t>(mach_float_read(data));
break;
case DATA_DOUBLE:
ut_a(len == sizeof(double));
value = static_cast<ib_uint64_t>(mach_double_read(data));
break;
default:
ut_error;
}
if (!unsigned_type && static_cast<int64_t>(value) < 0) {
value = 0;
}
return(value);
}

View File

@ -182,9 +182,8 @@ dberr_t row_check_index(row_prebuilt_t *prebuilt, ulint *n_rows)
@param[in] index index starting with an AUTO_INCREMENT column
@return the largest AUTO_INCREMENT value
@retval 0 if no records were found */
ib_uint64_t
row_search_max_autoinc(dict_index_t* index)
MY_ATTRIBUTE((nonnull, warn_unused_result));
uint64_t row_search_max_autoinc(dict_index_t *index) noexcept
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** A structure for caching column values for prefetched rows */
struct sel_buf_t{

View File

@ -2560,12 +2560,44 @@ row_ins_index_entry_big_rec(
return(error);
}
#if defined __aarch64__&&defined __GNUC__&&__GNUC__==4&&!defined __clang__
/* Avoid GCC 4.8.5 internal compiler error due to srw_mutex::wr_unlock().
We would only need this for row_ins_clust_index_entry_low(),
but GCC 4.8.5 does not support pop_options. */
# pragma GCC optimize ("O0")
#endif
/** Parse the integer data from specified data, which could be
DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0
and the type is not unsigned then we reset the value to 0
@param data data to read
@param len length of data
@param mtype main type of the column
@param prtype precise type of the column
@return the integer value from the data
@retval 0 if the value is negative or the type or length invalid */
static uint64_t row_parse_int(const byte *data, size_t len,
ulint mtype, ulint prtype) noexcept
{
switch (mtype) {
case DATA_FLOAT:
if (len != sizeof(float))
return 0;
{
float f= mach_float_read(data);
return f <= 0.0 ? 0 : uint64_t(f);
}
case DATA_DOUBLE:
if (len != sizeof(double))
return 0;
{
double d= mach_double_read(data);
return d <= 0.0 ? 0 : uint64_t(d);
}
case DATA_INT:
if (len == 0 || len > 8)
return 0;
const ibool unsigned_type{prtype & DATA_UNSIGNED};
uint64_t value= mach_read_int_type(data, len, unsigned_type);
return !unsigned_type && int64_t(value) < 0 ? 0 : value;
}
ut_ad("invalid type" == 0);
return 0;
}
/***************************************************************//**
Tries to insert an entry into a clustered index, ignoring foreign key
@ -2652,8 +2684,7 @@ row_ins_clust_index_entry_low(
dfield->data),
dfield->len,
dfield->type.mtype,
dfield->type.prtype
& DATA_UNSIGNED);
dfield->type.prtype);
if (auto_inc
&& mode != BTR_MODIFY_TREE) {
mode = btr_latch_mode(
@ -3810,3 +3841,65 @@ error_handling:
return(thr);
}
/** Read the AUTOINC column from an index record
@param index index of the record
@param rec the record
@return value read from the first column
@retval 0 if the value would be NULL or negative */
static uint64_t row_read_autoinc(const dict_index_t &index, const rec_t *rec)
noexcept
{
const dict_field_t &field= index.fields[0];
ut_ad(!DATA_BIG_COL(field.col));
ut_ad(!(rec_get_info_bits(rec, index.table->not_redundant()) &
(REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG)));
mem_heap_t *heap= nullptr;
rec_offs offsets_[REC_OFFS_HEADER_SIZE + 2];
rec_offs_init(offsets_);
rec_offs *offsets= rec_get_offsets(rec, &index, offsets_,
index.n_core_fields, 1, &heap);
ut_ad(!heap);
size_t len;
ut_d(size_t first_offset=) rec_get_nth_field_offs(offsets, 0, &len);
ut_ad(!first_offset);
return row_parse_int(rec, len, field.col->mtype, field.col->prtype);
}
/** Get the maximum and non-delete-marked record in an index.
@param index index B-tree
@param mtr mini-transaction (may be committed and restarted)
@return maximum record, page s-latched in mtr
@retval nullptr if there are no records, or if all of them are delete-marked */
static
const rec_t *row_search_get_max_rec(dict_index_t *index, mtr_t *mtr) noexcept
{
btr_pcur_t pcur;
/* Open at the high/right end (false), and init cursor */
if (pcur.open_leaf(false, index, BTR_SEARCH_LEAF, mtr) != DB_SUCCESS)
return nullptr;
do
{
const page_t *page= btr_pcur_get_page(&pcur);
const rec_t *rec= page_find_rec_max_not_deleted(page);
if (page_rec_is_user_rec_low(rec - page))
return rec;
btr_pcur_move_before_first_on_page(&pcur);
}
while (btr_pcur_move_to_prev(&pcur, mtr));
return nullptr;
}
uint64_t row_search_max_autoinc(dict_index_t *index) noexcept
{
uint64_t value= 0;
mtr_t mtr;
mtr.start();
if (const rec_t *rec= row_search_get_max_rec(index, &mtr))
value= row_read_autoinc(*index, rec);
mtr.commit();
return value;
}

View File

@ -6856,111 +6856,3 @@ next_rec:
goto rec_loop;
}
/*******************************************************************//**
Read the AUTOINC column from the current row. If the value is less than
0 and the type is not unsigned then we reset the value to 0.
@return value read from the column */
static
ib_uint64_t
row_search_autoinc_read_column(
/*===========================*/
dict_index_t* index, /*!< in: index to read from */
const rec_t* rec, /*!< in: current rec */
ulint col_no, /*!< in: column number */
ulint mtype, /*!< in: column main type */
ibool unsigned_type) /*!< in: signed or unsigned flag */
{
ulint len;
const byte* data;
ib_uint64_t value;
mem_heap_t* heap = NULL;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
ut_ad(page_rec_is_leaf(rec));
offsets = rec_get_offsets(rec, index, offsets, index->n_core_fields,
col_no + 1, &heap);
if (rec_offs_nth_sql_null(offsets, col_no)) {
/* There is no non-NULL value in the auto-increment column. */
value = 0;
goto func_exit;
}
data = rec_get_nth_field(rec, offsets, col_no, &len);
value = row_parse_int(data, len, mtype, unsigned_type);
func_exit:
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
return(value);
}
/** Get the maximum and non-delete-marked record in an index.
@param[in] index index tree
@param[in,out] mtr mini-transaction (may be committed and restarted)
@return maximum record, page s-latched in mtr
@retval NULL if there are no records, or if all of them are delete-marked */
static
const rec_t*
row_search_get_max_rec(
dict_index_t* index,
mtr_t* mtr)
{
btr_pcur_t pcur;
const rec_t* rec;
/* Open at the high/right end (false), and init cursor */
if (pcur.open_leaf(false, index, BTR_SEARCH_LEAF, mtr) != DB_SUCCESS) {
return nullptr;
}
do {
const page_t* page;
page = btr_pcur_get_page(&pcur);
rec = page_find_rec_max_not_deleted(page);
if (page_rec_is_user_rec(rec)) {
break;
} else {
rec = NULL;
}
btr_pcur_move_before_first_on_page(&pcur);
} while (btr_pcur_move_to_prev(&pcur, mtr));
ut_ad(!rec
|| !(rec_get_info_bits(rec, dict_table_is_comp(index->table))
& (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG)));
return(rec);
}
/** Read the max AUTOINC value from an index.
@param[in] index index starting with an AUTO_INCREMENT column
@return the largest AUTO_INCREMENT value
@retval 0 if no records were found */
ib_uint64_t
row_search_max_autoinc(dict_index_t* index)
{
const dict_field_t* dfield = dict_index_get_nth_field(index, 0);
ib_uint64_t value = 0;
mtr_t mtr;
mtr.start();
if (const rec_t* rec = row_search_get_max_rec(index, &mtr)) {
value = row_search_autoinc_read_column(
index, rec, 0,
dfield->col->mtype,
dfield->col->prtype & DATA_UNSIGNED);
}
mtr.commit();
return(value);
}