summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/main/range.result37
-rw-r--r--mysql-test/main/range.test46
-rw-r--r--mysql-test/main/range_mrr_icp.result37
-rw-r--r--sql/opt_range.cc14
-rw-r--r--sql/sql_prepare.cc21
-rw-r--r--storage/innobase/trx/trx0rec.cc42
-rw-r--r--tests/mysql_client_test.c7
7 files changed, 165 insertions, 39 deletions
diff --git a/mysql-test/main/range.result b/mysql-test/main/range.result
index d607d3b4808..c10ddf9d9fd 100644
--- a/mysql-test/main/range.result
+++ b/mysql-test/main/range.result
@@ -1297,7 +1297,7 @@ SELECT * FROM t1 WHERE
25 <= a AND b = 23 OR
23 <= a;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 5 NULL 2 Using where; Using index
+1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index
SELECT * FROM t1 WHERE
23 <= a AND a <= 25 OR
25 <= a AND b = 23 OR
@@ -1427,7 +1427,7 @@ SELECT * FROM t3 WHERE
a < 5 OR
a < 10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t3 range a a 5 NULL 9 Using where; Using index
+1 SIMPLE t3 index a a 10 NULL 23 Using where; Using index
DROP TABLE t1, t2, t3;
#
# Bug #47123: Endless 100% CPU loop with STRAIGHT_JOIN
@@ -3113,6 +3113,39 @@ a b
set eq_range_index_dive_limit=default;
drop table t1;
#
+# MDEV-24117: Memory management problem in statistics state...
+# (just the testcase)
+#
+create table t0(a int);
+insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t1(a int);
+insert into t1
+select A.a + B.a* 10 + C.a * 100 + D.a * 1000
+from t0 A, t0 B, t0 C, t0 D
+where D.a<4;
+create table t2 (
+a int,
+b int,
+key(a)
+);
+insert into t2 values (1,1),(2,2),(3,3);
+set @query=(select group_concat(a) from t1);
+set @tmp_24117= @@max_session_mem_used;
+#
+# On debug build, the usage was
+# - 2.8M without the bug
+# - 1G with the bug.
+set max_session_mem_used=64*1024*1024;
+set @query=concat('explain select * from t2 where a=1 or a in (', @query, ')');
+prepare s from @query;
+# This should not fail with an error:
+execute s;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL a NULL NULL NULL 3 Using where
+set max_session_mem_used=@tmp_24117;
+deallocate prepare s;
+drop table t0,t1,t2;
+#
# MDEV-23811: Both disjunct of WHERE condition contain range conditions
# for the same index such that the second range condition
# fully covers the first one. Additionally one of the disjuncts
diff --git a/mysql-test/main/range.test b/mysql-test/main/range.test
index 4462aec36b5..65f580698c5 100644
--- a/mysql-test/main/range.test
+++ b/mysql-test/main/range.test
@@ -2093,6 +2093,52 @@ set eq_range_index_dive_limit=default;
drop table t1;
--echo #
+--echo # MDEV-24117: Memory management problem in statistics state...
+--echo # (just the testcase)
+--echo #
+
+create table t0(a int);
+insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+create table t1(a int);
+
+# 4K rows
+insert into t1
+select A.a + B.a* 10 + C.a * 100 + D.a * 1000
+from t0 A, t0 B, t0 C, t0 D
+where D.a<4;
+
+create table t2 (
+ a int,
+ b int,
+ key(a)
+);
+
+insert into t2 values (1,1),(2,2),(3,3);
+
+set @query=(select group_concat(a) from t1);
+
+set @tmp_24117= @@max_session_mem_used;
+
+--echo #
+--echo # On debug build, the usage was
+--echo # - 2.8M without the bug
+--echo # - 1G with the bug.
+
+set max_session_mem_used=64*1024*1024;
+
+set @query=concat('explain select * from t2 where a=1 or a in (', @query, ')');
+
+prepare s from @query;
+
+--echo # This should not fail with an error:
+execute s;
+set max_session_mem_used=@tmp_24117;
+
+deallocate prepare s;
+
+drop table t0,t1,t2;
+--echo #
--echo # MDEV-23811: Both disjunct of WHERE condition contain range conditions
--echo # for the same index such that the second range condition
--echo # fully covers the first one. Additionally one of the disjuncts
diff --git a/mysql-test/main/range_mrr_icp.result b/mysql-test/main/range_mrr_icp.result
index 27daa76e976..826ac621064 100644
--- a/mysql-test/main/range_mrr_icp.result
+++ b/mysql-test/main/range_mrr_icp.result
@@ -1300,7 +1300,7 @@ SELECT * FROM t1 WHERE
25 <= a AND b = 23 OR
23 <= a;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 5 NULL 2 Using where; Using index
+1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index
SELECT * FROM t1 WHERE
23 <= a AND a <= 25 OR
25 <= a AND b = 23 OR
@@ -1430,7 +1430,7 @@ SELECT * FROM t3 WHERE
a < 5 OR
a < 10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t3 range a a 5 NULL 9 Using where; Using index
+1 SIMPLE t3 index a a 10 NULL 23 Using where; Using index
DROP TABLE t1, t2, t3;
#
# Bug #47123: Endless 100% CPU loop with STRAIGHT_JOIN
@@ -3110,6 +3110,39 @@ a b
set eq_range_index_dive_limit=default;
drop table t1;
#
+# MDEV-24117: Memory management problem in statistics state...
+# (just the testcase)
+#
+create table t0(a int);
+insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t1(a int);
+insert into t1
+select A.a + B.a* 10 + C.a * 100 + D.a * 1000
+from t0 A, t0 B, t0 C, t0 D
+where D.a<4;
+create table t2 (
+a int,
+b int,
+key(a)
+);
+insert into t2 values (1,1),(2,2),(3,3);
+set @query=(select group_concat(a) from t1);
+set @tmp_24117= @@max_session_mem_used;
+#
+# On debug build, the usage was
+# - 2.8M without the bug
+# - 1G with the bug.
+set max_session_mem_used=64*1024*1024;
+set @query=concat('explain select * from t2 where a=1 or a in (', @query, ')');
+prepare s from @query;
+# This should not fail with an error:
+execute s;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL a NULL NULL NULL 3 Using where
+set max_session_mem_used=@tmp_24117;
+deallocate prepare s;
+drop table t0,t1,t2;
+#
# MDEV-23811: Both disjunct of WHERE condition contain range conditions
# for the same index such that the second range condition
# fully covers the first one. Additionally one of the disjuncts
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 7a45e59b6e4..043a1e70f61 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -9605,15 +9605,9 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
}
bool no_imerge_from_ranges= FALSE;
- SEL_TREE *rt1= tree1;
- SEL_TREE *rt2= tree2;
/* Build the range part of the tree for the formula (1) */
if (sel_trees_can_be_ored(param, tree1, tree2, &ored_keys))
{
- if (no_merges1)
- rt1= new SEL_TREE(tree1, TRUE, param);
- if (no_merges2)
- rt2= new SEL_TREE(tree2, TRUE, param);
bool must_be_ored= sel_trees_must_be_ored(param, tree1, tree2, ored_keys);
no_imerge_from_ranges= must_be_ored;
@@ -9671,6 +9665,12 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
else if (!no_ranges1 && !no_ranges2 && !no_imerge_from_ranges)
{
/* Build the imerge part of the tree for the formula (1) */
+ SEL_TREE *rt1= tree1;
+ SEL_TREE *rt2= tree2;
+ if (no_merges1)
+ rt1= new SEL_TREE(tree1, TRUE, param);
+ if (no_merges2)
+ rt2= new SEL_TREE(tree2, TRUE, param);
if (!rt1 || !rt2 ||
result->merges.push_back(imerge_from_ranges) ||
imerge_from_ranges->or_sel_tree(param, rt1) ||
@@ -10336,7 +10336,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
if (!tmp->next_key_part)
{
SEL_ARG *key2_next= key2->next;
- if (key2->use_count)
+ if (key2_shared)
{
SEL_ARG *key2_cpy= new SEL_ARG(*key2);
if (!key2_cpy)
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 59a7586364a..849d1895fa1 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -3261,10 +3261,19 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length)
{
uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround
+ DBUG_ENTER("mysqld_stmt_execute_bulk");
+
+ const uint packet_header_lenght= 4 + 2; //ID & 2 bytes of flags
+
+ if (packet_length < packet_header_lenght)
+ {
+ my_error(ER_MALFORMED_PACKET, MYF(0));
+ DBUG_VOID_RETURN;
+ }
+
ulong stmt_id= uint4korr(packet);
uint flags= (uint) uint2korr(packet + 4);
uchar *packet_end= packet + packet_length;
- DBUG_ENTER("mysqld_stmt_execute_bulk");
if (!(thd->client_capabilities &
MARIADB_CLIENT_STMT_BULK_OPERATIONS))
@@ -3272,16 +3281,18 @@ void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_PRINT("error",
("An attempt to execute bulk operation without support"));
my_error(ER_UNSUPPORTED_PS, MYF(0));
+ DBUG_VOID_RETURN;
}
/* Check for implemented parameters */
if (flags & (~STMT_BULK_FLAG_CLIENT_SEND_TYPES))
{
DBUG_PRINT("error", ("unsupported bulk execute flags %x", flags));
my_error(ER_UNSUPPORTED_PS, MYF(0));
+ DBUG_VOID_RETURN;
}
/* stmt id and two bytes of flags */
- packet+= 4 + 2;
+ packet+= packet_header_lenght;
mysql_stmt_execute_common(thd, stmt_id, packet, packet_end, 0, TRUE,
(flags & STMT_BULK_FLAG_CLIENT_SEND_TYPES));
DBUG_VOID_RETURN;
@@ -3358,9 +3369,11 @@ stmt_execute_packet_sanity_check(Prepared_statement *stmt,
{
/*
If there is no parameters, this should be normally already end
- of the packet. If it's not - then error
+ of the packet, but it is not a problem if something left (popular
+ mistake in protocol implementation) because we will not read anymore
+ from the buffer.
*/
- return (packet_end > packet);
+ return false;
}
return false;
}
diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc
index eb7d0967901..bf85121d947 100644
--- a/storage/innobase/trx/trx0rec.cc
+++ b/storage/innobase/trx/trx0rec.cc
@@ -143,15 +143,18 @@ trx_undo_parse_add_undo_rec(
}
/** Calculate the free space left for extending an undo log record.
-@param[in] undo_block undo log page
-@param[in] ptr current end of the undo page
+@param undo_block undo log page
+@param ptr current end of the undo page
@return bytes left */
-static ulint trx_undo_left(const buf_block_t* undo_block, const byte* ptr)
+static ulint trx_undo_left(const buf_block_t *undo_block, const byte *ptr)
{
- /* The 10 is a safety margin, in case we have some small
- calculation error below */
- return srv_page_size - ulint(ptr - undo_block->frame)
- - (10 + FIL_PAGE_DATA_END);
+ ut_ad(ptr >= &undo_block->frame[TRX_UNDO_PAGE_HDR]);
+ ut_ad(ptr <= &undo_block->frame[srv_page_size - 10 - FIL_PAGE_DATA_END]);
+
+ /* The 10 is supposed to be an extra safety margin (and needed for
+ compatibility with older versions) */
+ return srv_page_size - ulint(ptr - undo_block->frame) -
+ (10 + FIL_PAGE_DATA_END);
}
/**********************************************************************//**
@@ -175,9 +178,6 @@ trx_undo_page_set_next_prev_and_add(
that points to the next free
offset value within undo_page.*/
- ut_ad(ptr > undo_block->frame);
- ut_ad(ptr < undo_block->frame + srv_page_size);
-
if (UNIV_UNLIKELY(trx_undo_left(undo_block, ptr) < 2)) {
return(0);
}
@@ -234,17 +234,15 @@ trx_undo_log_v_idx(
ut_ad(!vcol->v_indexes.empty());
- /* Size to reserve, max 5 bytes for each index id and position, plus
- 5 bytes for num of indexes, 2 bytes for write total length.
- 1 byte for undo log record format version marker */
- ulint size = 5 + 2 + (first_v_col ? 1 : 0);
+ ulint size = first_v_col ? 1 + 2 : 2;
const ulint avail = trx_undo_left(undo_block, ptr);
- if (avail < size) {
+ /* The mach_write_compressed(ptr, flen) in
+ trx_undo_page_report_modify() will consume additional 1 to 5 bytes. */
+ if (avail < size + 5) {
return(NULL);
}
- size = 0;
ulint n_idx = 0;
for (const auto& v_index : vcol->v_indexes) {
n_idx++;
@@ -252,12 +250,14 @@ trx_undo_log_v_idx(
size += mach_get_compressed_size(uint32_t(v_index.index->id));
size += mach_get_compressed_size(v_index.nth_field);
}
- size += 2 + mach_get_compressed_size(n_idx);
- if (avail < size) {
+ size += mach_get_compressed_size(n_idx);
+
+ if (avail < size + 5) {
return(NULL);
}
+ ut_d(const byte* orig_ptr = ptr);
if (first_v_col) {
/* write the version marker */
@@ -280,6 +280,8 @@ trx_undo_log_v_idx(
ptr += mach_write_compressed(ptr, v_index.nth_field);
}
+ ut_ad(orig_ptr + size == ptr);
+
mach_write_to_2(old_ptr, ulint(ptr - old_ptr));
return(ptr);
@@ -497,8 +499,6 @@ trx_undo_page_report_insert(
+ undo_block->frame);
ptr = undo_block->frame + first_free;
- ut_ad(first_free <= srv_page_size);
-
if (trx_undo_left(undo_block, ptr) < 2 + 1 + 11 + 11) {
/* Not enough space for writing the general parameters */
return(0);
@@ -905,8 +905,6 @@ trx_undo_page_report_modify(
+ undo_block->frame);
ptr = undo_block->frame + first_free;
- ut_ad(first_free <= srv_page_size);
-
if (trx_undo_left(undo_block, ptr) < 50) {
/* NOTE: the value 50 must be big enough so that the general
fields written below fit on the undo log page */
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 393bf342dd5..31c71c705a2 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -21076,8 +21076,11 @@ static void test_mdev19838()
" VALUES "
"(0x1111111111111111)", -1);
- /* Expecting an error if parameters are sent */
- DIE_UNLESS(rc != 0 || paramCount == 0);
+ /*
+ We allow junk at the end of the packet in case of
+ no parameters. So it will succeed.
+ */
+ DIE_UNLESS(rc == 0);
}
mysql_stmt_close(stmt);