Skip to content

Commit adebf43

Browse files
committed
Bug#36104667: Assertion failure in Sort_param::make_sortkey with
hypergraph_optimizer When using the hypergraph optimizer, an assertion failure was sometimes seen when sorting NULL-complemented rows from a left or right outer join. The problem was that Filesort did not check table->has_null_row() before saving or restoring values of non-nullable columns. If the row was NULL-complemented, this could lead to attempts to store garbage in the sort buffer, and possibly writing garbage beyond the bounds of the sort buffer. This did not happen with the old optimizer, because it always streams the rows through a temporary table before sorting rows after a join, so Filesort wouldn't see a NULL-complemented row in this case, but rather an ordinary row with NULL values in a temporary table. Also, it did not happen when Filesort used packed addon fields, as that code path already took NULL-complemented rows into account. It only happened with non-packed addon fields. Fixed by handling NULL-complemented rows the same way for non-packed addon fields as for packed addon fields. Change-Id: Ia2d2dc2f579546db3dd79c150f993f3f08ba652a
1 parent 57f2dd1 commit adebf43

File tree

4 files changed

+68
-12
lines changed

4 files changed

+68
-12
lines changed

mysql-test/r/filesort.result

+26
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,29 @@ a LENGTH(b)
216216
1 40001
217217
SET sort_buffer_size=DEFAULT;
218218
DROP TABLE t1;
219+
#
220+
# Bug#36104667: Assertion failure in Sort_param::make_sortkey with
221+
# hypergraph_optimizer
222+
#
223+
CREATE TABLE t1 (i INT);
224+
INSERT INTO t1 VALUES (), ();
225+
CREATE TABLE t2 (
226+
pk INT PRIMARY KEY,
227+
vc VARCHAR(1) NOT NULL,
228+
gc INT GENERATED ALWAYS AS (1));
229+
INSERT INTO t2 VALUES (1, 'a', DEFAULT), (2, 'b', DEFAULT);
230+
ANALYZE TABLE t1, t2;
231+
Table Op Msg_type Msg_text
232+
test.t1 analyze status OK
233+
test.t2 analyze status OK
234+
SELECT gc FROM t2;
235+
gc
236+
1
237+
1
238+
SELECT 1
239+
FROM t1,
240+
t2 RIGHT OUTER JOIN t1 AS t3 ON t2.pk = t3.i
241+
WHERE (SELECT 1 FROM t1 WHERE t2.vc = 'x')
242+
ORDER BY t2.vc;
243+
1
244+
DROP TABLE t1, t2;

mysql-test/t/filesort.test

+31
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,34 @@ SELECT a, LENGTH(b) FROM t1 ORDER BY a DESC;
211211
SET sort_buffer_size=DEFAULT;
212212

213213
DROP TABLE t1;
214+
215+
--echo #
216+
--echo # Bug#36104667: Assertion failure in Sort_param::make_sortkey with
217+
--echo # hypergraph_optimizer
218+
--echo #
219+
220+
CREATE TABLE t1 (i INT);
221+
222+
INSERT INTO t1 VALUES (), ();
223+
224+
CREATE TABLE t2 (
225+
pk INT PRIMARY KEY,
226+
vc VARCHAR(1) NOT NULL,
227+
gc INT GENERATED ALWAYS AS (1));
228+
229+
INSERT INTO t2 VALUES (1, 'a', DEFAULT), (2, 'b', DEFAULT);
230+
231+
ANALYZE TABLE t1, t2;
232+
233+
# The next query doesn't fail unless we do this first. (It fills the
234+
# vc column in t2's record buffer with garbage.)
235+
SELECT gc FROM t2;
236+
237+
# Used to hit an assertion failure with the hypergraph optimizer.
238+
SELECT 1
239+
FROM t1,
240+
t2 RIGHT OUTER JOIN t1 AS t3 ON t2.pk = t3.i
241+
WHERE (SELECT 1 FROM t1 WHERE t2.vc = 'x')
242+
ORDER BY t2.vc;
243+
244+
DROP TABLE t1, t2;

sql/filesort.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,9 @@ uint Sort_param::make_sortkey(Bounds_checked_array<uchar> dst,
15231523
if (static_cast<size_t>(to_end - to) < addonf.max_length) {
15241524
return UINT_MAX;
15251525
}
1526-
if (addonf.null_bit && field->is_null()) {
1526+
if (field->table->has_null_row()) {
1527+
assert(field->table->is_nullable());
1528+
} else if (addonf.null_bit && field->is_null()) {
15271529
nulls[addonf.null_offset] |= addonf.null_bit;
15281530
} else {
15291531
uchar *ptr [[maybe_unused]] =

sql/iterators/sorting_iterator.cc

+8-11
Original file line numberDiff line numberDiff line change
@@ -556,25 +556,22 @@ inline void Filesort_info::unpack_addon_fields(
556556
}
557557

558558
// Unpack the actual addon fields (if any).
559-
const uchar *start_of_record = buff + addon_fields->first_addon_offset();
559+
const uchar *next_record = buff + addon_fields->first_addon_offset();
560560
for (const Sort_addon_field &addonf : *addon_fields) {
561561
Field *field = addonf.field;
562+
const uchar *end_of_record = next_record;
562563
const bool is_null =
563564
addonf.null_bit && (addonf.null_bit & nulls[addonf.null_offset]);
564565
if (is_null) {
565566
field->set_null();
567+
} else if (!field->table->has_null_row()) {
568+
field->set_notnull();
569+
end_of_record = field->unpack(next_record);
566570
}
567-
if (Packed_addon_fields) {
568-
if (!is_null && !field->table->has_null_row()) {
569-
field->set_notnull();
570-
start_of_record = field->unpack(start_of_record);
571-
}
571+
if constexpr (Packed_addon_fields) {
572+
next_record = end_of_record;
572573
} else {
573-
if (!is_null) {
574-
field->set_notnull();
575-
field->unpack(start_of_record);
576-
}
577-
start_of_record += addonf.max_length;
574+
next_record += addonf.max_length;
578575
}
579576
}
580577
}

0 commit comments

Comments
 (0)