Skip to content

Commit 16e196b

Browse files
committed
MDEV-24931: Fix Bitmap<64>::is_prefix assertion with >64-column NATURAL JOIN on derived table
When a NATURAL JOIN operates on a derived table (or view with derived_merge=off) having more than 64 columns, the optimizer's generate_derived_keys_for_table() would: 1) Overflow a 32-bit shift: (key_part_map)(1 << parts) when parts >= 32 2) Create a derived key with more parts than Bitmap<64>/key_part_map can hold 3) Crash on DBUG_ASSERT(prefix_size <= width) in Bitmap<64>::is_prefix(65) Fix: cap derived keys at sizeof(key_part_map)*8 (64) parts. Excess columns are excluded from the key (keyuse->key = MAX_KEY). Also fix the shift to cast before shifting: (key_part_map) 1 << parts.
1 parent c43bf13 commit 16e196b

3 files changed

Lines changed: 85 additions & 2 deletions

File tree

mysql-test/main/derived_opt.result

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,4 +567,36 @@ DROP TABLE t1, t2;
567567
#
568568
# End of 10.3 tests
569569
#
570+
#
571+
# MDEV-24931: Assertion `prefix_size <= width' failed in
572+
# Bitmap<64>::is_prefix with >64-column NATURAL JOIN on derived table
573+
#
574+
CREATE TABLE t1 (
575+
c1 INT, c2 INT, c3 INT, c4 INT, c5 INT, c6 INT, c7 INT, c8 INT,
576+
c9 INT, c10 INT, c11 INT, c12 INT, c13 INT, c14 INT, c15 INT, c16 INT,
577+
c17 INT, c18 INT, c19 INT, c20 INT, c21 INT, c22 INT, c23 INT, c24 INT,
578+
c25 INT, c26 INT, c27 INT, c28 INT, c29 INT, c30 INT, c31 INT, c32 INT,
579+
c33 INT, c34 INT, c35 INT, c36 INT, c37 INT, c38 INT, c39 INT, c40 INT,
580+
c41 INT, c42 INT, c43 INT, c44 INT, c45 INT, c46 INT, c47 INT, c48 INT,
581+
c49 INT, c50 INT, c51 INT, c52 INT, c53 INT, c54 INT, c55 INT, c56 INT,
582+
c57 INT, c58 INT, c59 INT, c60 INT, c61 INT, c62 INT, c63 INT, c64 INT,
583+
c65 INT
584+
);
585+
CREATE VIEW v1 AS SELECT * FROM t1;
586+
set optimizer_switch='derived_merge=off';
587+
# Derived table subquery
588+
SELECT * FROM t1 AS a NATURAL JOIN (SELECT * FROM t1) AS b;
589+
c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c23 c24 c25 c26 c27 c28 c29 c30 c31 c32 c33 c34 c35 c36 c37 c38 c39 c40 c41 c42 c43 c44 c45 c46 c47 c48 c49 c50 c51 c52 c53 c54 c55 c56 c57 c58 c59 c60 c61 c62 c63 c64 c65
590+
# View
591+
SELECT * FROM v1 NATURAL JOIN t1;
592+
c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c23 c24 c25 c26 c27 c28 c29 c30 c31 c32 c33 c34 c35 c36 c37 c38 c39 c40 c41 c42 c43 c44 c45 c46 c47 c48 c49 c50 c51 c52 c53 c54 c55 c56 c57 c58 c59 c60 c61 c62 c63 c64 c65
593+
# CTE
594+
WITH cte AS (SELECT * FROM t1) SELECT * FROM t1 NATURAL JOIN cte;
595+
c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c23 c24 c25 c26 c27 c28 c29 c30 c31 c32 c33 c34 c35 c36 c37 c38 c39 c40 c41 c42 c43 c44 c45 c46 c47 c48 c49 c50 c51 c52 c53 c54 c55 c56 c57 c58 c59 c60 c61 c62 c63 c64 c65
596+
set optimizer_switch= @save_optimizer_switch;
597+
DROP VIEW v1;
598+
DROP TABLE t1;
599+
#
600+
# End of 10.11 tests
601+
#
570602
set optimizer_switch=@exit_optimizer_switch;

mysql-test/main/derived_opt.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,5 +439,43 @@ DROP TABLE t1, t2;
439439
--echo # End of 10.3 tests
440440
--echo #
441441

442+
--echo #
443+
--echo # MDEV-24931: Assertion `prefix_size <= width' failed in
444+
--echo # Bitmap<64>::is_prefix with >64-column NATURAL JOIN on derived table
445+
--echo #
446+
447+
CREATE TABLE t1 (
448+
c1 INT, c2 INT, c3 INT, c4 INT, c5 INT, c6 INT, c7 INT, c8 INT,
449+
c9 INT, c10 INT, c11 INT, c12 INT, c13 INT, c14 INT, c15 INT, c16 INT,
450+
c17 INT, c18 INT, c19 INT, c20 INT, c21 INT, c22 INT, c23 INT, c24 INT,
451+
c25 INT, c26 INT, c27 INT, c28 INT, c29 INT, c30 INT, c31 INT, c32 INT,
452+
c33 INT, c34 INT, c35 INT, c36 INT, c37 INT, c38 INT, c39 INT, c40 INT,
453+
c41 INT, c42 INT, c43 INT, c44 INT, c45 INT, c46 INT, c47 INT, c48 INT,
454+
c49 INT, c50 INT, c51 INT, c52 INT, c53 INT, c54 INT, c55 INT, c56 INT,
455+
c57 INT, c58 INT, c59 INT, c60 INT, c61 INT, c62 INT, c63 INT, c64 INT,
456+
c65 INT
457+
);
458+
459+
CREATE VIEW v1 AS SELECT * FROM t1;
460+
461+
set optimizer_switch='derived_merge=off';
462+
463+
--echo # Derived table subquery
464+
SELECT * FROM t1 AS a NATURAL JOIN (SELECT * FROM t1) AS b;
465+
466+
--echo # View
467+
SELECT * FROM v1 NATURAL JOIN t1;
468+
469+
--echo # CTE
470+
WITH cte AS (SELECT * FROM t1) SELECT * FROM t1 NATURAL JOIN cte;
471+
472+
set optimizer_switch= @save_optimizer_switch;
473+
DROP VIEW v1;
474+
DROP TABLE t1;
475+
476+
--echo #
477+
--echo # End of 10.11 tests
478+
--echo #
479+
442480
# The following command must be the last one the file
443481
set optimizer_switch=@exit_optimizer_switch;

sql/sql_select.cc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14071,26 +14071,39 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
1407114071
KEYUSE *first_keyuse= keyuse;
1407214072
uint prev_part= keyuse->keypart;
1407314073
uint parts= 0;
14074+
uint max_parts= table->file->max_key_parts();
1407414075
uint i= 0;
1407514076

1407614077
for ( ; i < count && key_count < keys; )
1407714078
{
1407814079
do
1407914080
{
1408014081
keyuse->key= table->s->keys;
14081-
keyuse->keypart_map= (key_part_map) (1 << parts);
14082+
keyuse->keypart_map= (key_part_map) 1 << parts;
1408214083
keyuse++;
1408314084
i++;
1408414085
}
1408514086
while (i < count && keyuse->used_tables == first_keyuse->used_tables &&
1408614087
keyuse->keypart == prev_part);
1408714088
parts++;
14088-
if (i < count && keyuse->used_tables == first_keyuse->used_tables)
14089+
if (i < count && keyuse->used_tables == first_keyuse->used_tables
14090+
&& parts < max_parts)
1408914091
{
1409014092
prev_part= keyuse->keypart;
1409114093
}
1409214094
else
1409314095
{
14096+
/*
14097+
Cap derived keys at max_key_parts to avoid shift overflow and
14098+
Bitmap<64> assertion failures (MDEV-24931). Mark any remaining
14099+
keyuses for the same table as unused.
14100+
*/
14101+
while (i < count && keyuse->used_tables == first_keyuse->used_tables)
14102+
{
14103+
keyuse->key= MAX_KEY;
14104+
keyuse++;
14105+
i++;
14106+
}
1409414107
KEYUSE *save_first_keyuse= first_keyuse;
1409514108
if (table->check_tmp_key(table->s->keys, parts,
1409614109
get_next_field_for_derived_key_simple,

0 commit comments

Comments
 (0)