Skip to content

Commit ba4927e

Browse files
committed
MDEV-19398: Assertion `item1->type() == Item::FIELD_ITEM ...
Window Functions code tries to minimize the number of times it needs to sort the select's resultset by finding "compatible" OVER (PARTITION BY ... ORDER BY ...) clauses. This employs compare_order_elements(). That function assumed that the order expressions are Item_field-derived objects (that refer to a temp.table). But this is not always the case: one can construct queries order expressions are arbitrary item expressions. Add handling for such expressions: sort them according to the window specification they appeared in. This means we cannot detect that two compatible PARTITION BY clauses that use expressions can share the sorting step. But at least we won't crash.
1 parent 794bebf commit ba4927e

File tree

5 files changed

+139
-10
lines changed

5 files changed

+139
-10
lines changed

mysql-test/r/win.result

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4238,5 +4238,47 @@ SELECT 1 UNION SELECT a FROM t1 ORDER BY (row_number() over ());
42384238
ERROR HY000: Expression #1 of ORDER BY contains aggregate function and applies to a UNION
42394239
DROP TABLE t1;
42404240
#
4241+
# MDEV-19398: Assertion `item1->type() == Item::FIELD_ITEM &&
4242+
# item2->type() == Item::FIELD_ITEM' failed in compare_order_elements
4243+
#
4244+
CREATE TABLE t1 ( id varchar(10));
4245+
INSERT INTO t1 values (1),(2),(3);
4246+
SELECT
4247+
dense_rank() over (ORDER BY avg(1)+3),
4248+
rank() over (ORDER BY avg(1))
4249+
FROM t1
4250+
GROUP BY nullif(id, 15532);
4251+
dense_rank() over (ORDER BY avg(1)+3) rank() over (ORDER BY avg(1))
4252+
1 1
4253+
1 1
4254+
1 1
4255+
SELECT
4256+
dense_rank() over (ORDER BY avg(1)),
4257+
rank() over (ORDER BY avg(1))
4258+
FROM t1
4259+
GROUP BY nullif(id, 15532);
4260+
dense_rank() over (ORDER BY avg(1)) rank() over (ORDER BY avg(1))
4261+
1 1
4262+
1 1
4263+
1 1
4264+
drop table t1;
4265+
CREATE TABLE t1 ( a char(25), b text);
4266+
INSERT INTO t1 VALUES ('foo','bar');
4267+
SELECT
4268+
SUM(b) OVER (PARTITION BY a),
4269+
ROW_NUMBER() OVER (PARTITION BY b)
4270+
FROM t1
4271+
GROUP BY
4272+
LEFT((SYSDATE()), 'foo')
4273+
WITH ROLLUP;
4274+
SUM(b) OVER (PARTITION BY a) ROW_NUMBER() OVER (PARTITION BY b)
4275+
NULL 1
4276+
NULL 1
4277+
Warnings:
4278+
Warning 1292 Truncated incorrect INTEGER value: 'foo'
4279+
Warning 1292 Truncated incorrect INTEGER value: 'foo'
4280+
drop table t1;
4281+
#
4282+
#
42414283
# End of 10.2 tests
42424284
#

mysql-test/t/win.test

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,6 +2740,39 @@ INSERT INTO t1 VALUES (1),(1),(1),(1),(1),(2),(2),(2),(2),(2),(2);
27402740
SELECT 1 UNION SELECT a FROM t1 ORDER BY (row_number() over ());
27412741
DROP TABLE t1;
27422742

2743+
--echo #
2744+
--echo # MDEV-19398: Assertion `item1->type() == Item::FIELD_ITEM &&
2745+
--echo # item2->type() == Item::FIELD_ITEM' failed in compare_order_elements
2746+
--echo #
2747+
CREATE TABLE t1 ( id varchar(10));
2748+
INSERT INTO t1 values (1),(2),(3);
2749+
2750+
SELECT
2751+
dense_rank() over (ORDER BY avg(1)+3),
2752+
rank() over (ORDER BY avg(1))
2753+
FROM t1
2754+
GROUP BY nullif(id, 15532);
2755+
2756+
SELECT
2757+
dense_rank() over (ORDER BY avg(1)),
2758+
rank() over (ORDER BY avg(1))
2759+
FROM t1
2760+
GROUP BY nullif(id, 15532);
2761+
drop table t1;
2762+
2763+
CREATE TABLE t1 ( a char(25), b text);
2764+
INSERT INTO t1 VALUES ('foo','bar');
2765+
2766+
SELECT
2767+
SUM(b) OVER (PARTITION BY a),
2768+
ROW_NUMBER() OVER (PARTITION BY b)
2769+
FROM t1
2770+
GROUP BY
2771+
LEFT((SYSDATE()), 'foo')
2772+
WITH ROLLUP;
2773+
drop table t1;
2774+
2775+
--echo #
27432776
--echo #
27442777
--echo # End of 10.2 tests
27452778
--echo #

sql/sql_parse.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8624,6 +8624,7 @@ bool st_select_lex::add_window_def(THD *thd,
86248624
fields_in_window_functions+= win_part_list_ptr->elements +
86258625
win_order_list_ptr->elements;
86268626
}
8627+
win_def->win_spec_number= window_specs.elements;
86278628
return (win_def == NULL || window_specs.push_back(win_def));
86288629
}
86298630

@@ -8651,6 +8652,7 @@ bool st_select_lex::add_window_spec(THD *thd,
86518652
win_order_list_ptr->elements;
86528653
}
86538654
thd->lex->win_spec= win_spec;
8655+
win_spec->win_spec_number= window_specs.elements;
86548656
return (win_spec == NULL || window_specs.push_back(win_spec));
86558657
}
86568658

sql/sql_window.cc

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,49 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
312312
#define CMP_GT 2 // Greater then
313313

314314
static
315-
int compare_order_elements(ORDER *ord1, ORDER *ord2)
315+
int compare_order_elements(ORDER *ord1, int weight1,
316+
ORDER *ord2, int weight2)
316317
{
317318
if (*ord1->item == *ord2->item && ord1->direction == ord2->direction)
318319
return CMP_EQ;
319320
Item *item1= (*ord1->item)->real_item();
320321
Item *item2= (*ord2->item)->real_item();
321-
DBUG_ASSERT(item1->type() == Item::FIELD_ITEM &&
322-
item2->type() == Item::FIELD_ITEM);
323-
ptrdiff_t cmp= ((Item_field *) item1)->field - ((Item_field *) item2)->field;
322+
323+
bool item1_field= (item1->type() == Item::FIELD_ITEM);
324+
bool item2_field= (item2->type() == Item::FIELD_ITEM);
325+
326+
ptrdiff_t cmp;
327+
if (item1_field && item2_field)
328+
{
329+
DBUG_ASSERT(((Item_field *) item1)->field->table ==
330+
((Item_field *) item2)->field->table);
331+
cmp= ((Item_field *) item1)->field->field_index -
332+
((Item_field *) item2)->field->field_index;
333+
}
334+
else if (item1_field && !item2_field)
335+
return CMP_LT;
336+
else if (!item1_field && item2_field)
337+
return CMP_LT;
338+
else
339+
{
340+
/*
341+
Ok, item1_field==NULL and item2_field==NULL.
342+
We're not able to compare Item expressions. Order them according to
343+
their passed "weight" (which comes from Window_spec::win_spec_number):
344+
*/
345+
if (weight1 != weight2)
346+
cmp= weight1 - weight2;
347+
else
348+
{
349+
/*
350+
The weight is the same. That is, the elements come from the same
351+
window specification... This shouldn't happen.
352+
*/
353+
DBUG_ASSERT(0);
354+
cmp= item1 - item2;
355+
}
356+
}
357+
324358
if (cmp == 0)
325359
{
326360
if (ord1->direction == ord2->direction)
@@ -333,7 +367,9 @@ int compare_order_elements(ORDER *ord1, ORDER *ord2)
333367

334368
static
335369
int compare_order_lists(SQL_I_List<ORDER> *part_list1,
336-
SQL_I_List<ORDER> *part_list2)
370+
int spec_number1,
371+
SQL_I_List<ORDER> *part_list2,
372+
int spec_number2)
337373
{
338374
if (part_list1 == part_list2)
339375
return CMP_EQ;
@@ -358,7 +394,8 @@ int compare_order_lists(SQL_I_List<ORDER> *part_list1,
358394
if (!elem1 || !elem2)
359395
break;
360396

361-
if ((cmp= compare_order_elements(elem1, elem2)))
397+
if ((cmp= compare_order_elements(elem1, spec_number1,
398+
elem2, spec_number2)))
362399
return cmp;
363400
}
364401
if (elem1)
@@ -453,7 +490,9 @@ int compare_window_spec_joined_lists(Window_spec *win_spec1,
453490
win_spec1->join_partition_and_order_lists();
454491
win_spec2->join_partition_and_order_lists();
455492
int cmp= compare_order_lists(win_spec1->partition_list,
456-
win_spec2->partition_list);
493+
win_spec1->win_spec_number,
494+
win_spec2->partition_list,
495+
win_spec2->win_spec_number);
457496
win_spec1->disjoin_partition_and_order_lists();
458497
win_spec2->disjoin_partition_and_order_lists();
459498
return cmp;
@@ -471,7 +510,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1,
471510
if (win_spec1 == win_spec2)
472511
return CMP_EQ;
473512
cmp= compare_order_lists(win_spec1->partition_list,
474-
win_spec2->partition_list);
513+
win_spec1->win_spec_number,
514+
win_spec2->partition_list,
515+
win_spec2->win_spec_number);
475516
if (cmp == CMP_EQ)
476517
{
477518
/*
@@ -490,7 +531,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1,
490531
}
491532

492533
cmp= compare_order_lists(win_spec1->order_list,
493-
win_spec2->order_list);
534+
win_spec1->win_spec_number,
535+
win_spec2->order_list,
536+
win_spec2->win_spec_number);
494537

495538
if (cmp != CMP_EQ)
496539
return cmp;
@@ -587,7 +630,9 @@ void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list)
587630
int cmp;
588631
if (win_spec_prev->partition_list == win_spec_curr->partition_list)
589632
cmp= compare_order_lists(win_spec_prev->order_list,
590-
win_spec_curr->order_list);
633+
win_spec_prev->win_spec_number,
634+
win_spec_curr->order_list,
635+
win_spec_curr->win_spec_number);
591636
else
592637
cmp= compare_window_spec_joined_lists(win_spec_prev, win_spec_curr);
593638
if (!(CMP_LT_C <= cmp && cmp <= CMP_GT_C))

sql/sql_window.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ class Window_spec : public Sql_alloc
108108

109109
Window_spec *referenced_win_spec;
110110

111+
/*
112+
Window_spec objects are numbered by the number of their appearance in the
113+
query. This is used by compare_order_elements() to provide a predictable
114+
ordering of PARTITION/ORDER BY clauses.
115+
*/
116+
int win_spec_number;
117+
111118
Window_spec(LEX_STRING *win_ref,
112119
SQL_I_List<ORDER> *part_list,
113120
SQL_I_List<ORDER> *ord_list,

0 commit comments

Comments
 (0)