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:
parent
44e1f7238a
commit
c07e355c40
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user