diff options
author | Sergei Petrunia <sergey@mariadb.com> | 2022-05-03 14:06:27 +0300 |
---|---|---|
committer | Sergei Petrunia <sergey@mariadb.com> | 2022-05-04 15:47:45 +0300 |
commit | ba4927e520190bbad763bb5260ae154f29a61231 (patch) | |
tree | b261a4a612505740e071456b649865eceaf9a252 | |
parent | 794bebf9ee18de4138c4d2c93853d49ff3af7f12 (diff) | |
download | mariadb-git-ba4927e520190bbad763bb5260ae154f29a61231.tar.gz |
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.
-rw-r--r-- | mysql-test/r/win.result | 42 | ||||
-rw-r--r-- | mysql-test/t/win.test | 33 | ||||
-rw-r--r-- | sql/sql_parse.cc | 2 | ||||
-rw-r--r-- | sql/sql_window.cc | 65 | ||||
-rw-r--r-- | sql/sql_window.h | 7 |
5 files changed, 139 insertions, 10 deletions
diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 1ddafbd4f8f..b4d918eb437 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -4238,5 +4238,47 @@ SELECT 1 UNION SELECT a FROM t1 ORDER BY (row_number() over ()); ERROR HY000: Expression #1 of ORDER BY contains aggregate function and applies to a UNION DROP TABLE t1; # +# MDEV-19398: Assertion `item1->type() == Item::FIELD_ITEM && +# item2->type() == Item::FIELD_ITEM' failed in compare_order_elements +# +CREATE TABLE t1 ( id varchar(10)); +INSERT INTO t1 values (1),(2),(3); +SELECT +dense_rank() over (ORDER BY avg(1)+3), +rank() over (ORDER BY avg(1)) +FROM t1 +GROUP BY nullif(id, 15532); +dense_rank() over (ORDER BY avg(1)+3) rank() over (ORDER BY avg(1)) +1 1 +1 1 +1 1 +SELECT +dense_rank() over (ORDER BY avg(1)), +rank() over (ORDER BY avg(1)) +FROM t1 +GROUP BY nullif(id, 15532); +dense_rank() over (ORDER BY avg(1)) rank() over (ORDER BY avg(1)) +1 1 +1 1 +1 1 +drop table t1; +CREATE TABLE t1 ( a char(25), b text); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT +SUM(b) OVER (PARTITION BY a), +ROW_NUMBER() OVER (PARTITION BY b) +FROM t1 +GROUP BY +LEFT((SYSDATE()), 'foo') +WITH ROLLUP; +SUM(b) OVER (PARTITION BY a) ROW_NUMBER() OVER (PARTITION BY b) +NULL 1 +NULL 1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'foo' +Warning 1292 Truncated incorrect INTEGER value: 'foo' +drop table t1; +# +# # End of 10.2 tests # diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index 37f09a6e850..ba1008afa60 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -2741,5 +2741,38 @@ SELECT 1 UNION SELECT a FROM t1 ORDER BY (row_number() over ()); DROP TABLE t1; --echo # +--echo # MDEV-19398: Assertion `item1->type() == Item::FIELD_ITEM && +--echo # item2->type() == Item::FIELD_ITEM' failed in compare_order_elements +--echo # +CREATE TABLE t1 ( id varchar(10)); +INSERT INTO t1 values (1),(2),(3); + +SELECT + dense_rank() over (ORDER BY avg(1)+3), + rank() over (ORDER BY avg(1)) +FROM t1 +GROUP BY nullif(id, 15532); + +SELECT + dense_rank() over (ORDER BY avg(1)), + rank() over (ORDER BY avg(1)) +FROM t1 +GROUP BY nullif(id, 15532); +drop table t1; + +CREATE TABLE t1 ( a char(25), b text); +INSERT INTO t1 VALUES ('foo','bar'); + +SELECT + SUM(b) OVER (PARTITION BY a), + ROW_NUMBER() OVER (PARTITION BY b) +FROM t1 +GROUP BY + LEFT((SYSDATE()), 'foo') +WITH ROLLUP; +drop table t1; + +--echo # +--echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 989ca0c8803..457849a7569 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8624,6 +8624,7 @@ bool st_select_lex::add_window_def(THD *thd, fields_in_window_functions+= win_part_list_ptr->elements + win_order_list_ptr->elements; } + win_def->win_spec_number= window_specs.elements; return (win_def == NULL || window_specs.push_back(win_def)); } @@ -8651,6 +8652,7 @@ bool st_select_lex::add_window_spec(THD *thd, win_order_list_ptr->elements; } thd->lex->win_spec= win_spec; + win_spec->win_spec_number= window_specs.elements; return (win_spec == NULL || window_specs.push_back(win_spec)); } diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 3ef751bc5b9..8afdaa1e6da 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -312,15 +312,49 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, #define CMP_GT 2 // Greater then static -int compare_order_elements(ORDER *ord1, ORDER *ord2) +int compare_order_elements(ORDER *ord1, int weight1, + ORDER *ord2, int weight2) { if (*ord1->item == *ord2->item && ord1->direction == ord2->direction) return CMP_EQ; Item *item1= (*ord1->item)->real_item(); Item *item2= (*ord2->item)->real_item(); - DBUG_ASSERT(item1->type() == Item::FIELD_ITEM && - item2->type() == Item::FIELD_ITEM); - ptrdiff_t cmp= ((Item_field *) item1)->field - ((Item_field *) item2)->field; + + bool item1_field= (item1->type() == Item::FIELD_ITEM); + bool item2_field= (item2->type() == Item::FIELD_ITEM); + + ptrdiff_t cmp; + if (item1_field && item2_field) + { + DBUG_ASSERT(((Item_field *) item1)->field->table == + ((Item_field *) item2)->field->table); + cmp= ((Item_field *) item1)->field->field_index - + ((Item_field *) item2)->field->field_index; + } + else if (item1_field && !item2_field) + return CMP_LT; + else if (!item1_field && item2_field) + return CMP_LT; + else + { + /* + Ok, item1_field==NULL and item2_field==NULL. + We're not able to compare Item expressions. Order them according to + their passed "weight" (which comes from Window_spec::win_spec_number): + */ + if (weight1 != weight2) + cmp= weight1 - weight2; + else + { + /* + The weight is the same. That is, the elements come from the same + window specification... This shouldn't happen. + */ + DBUG_ASSERT(0); + cmp= item1 - item2; + } + } + if (cmp == 0) { if (ord1->direction == ord2->direction) @@ -333,7 +367,9 @@ int compare_order_elements(ORDER *ord1, ORDER *ord2) static int compare_order_lists(SQL_I_List<ORDER> *part_list1, - SQL_I_List<ORDER> *part_list2) + int spec_number1, + SQL_I_List<ORDER> *part_list2, + int spec_number2) { if (part_list1 == part_list2) return CMP_EQ; @@ -358,7 +394,8 @@ int compare_order_lists(SQL_I_List<ORDER> *part_list1, if (!elem1 || !elem2) break; - if ((cmp= compare_order_elements(elem1, elem2))) + if ((cmp= compare_order_elements(elem1, spec_number1, + elem2, spec_number2))) return cmp; } if (elem1) @@ -453,7 +490,9 @@ int compare_window_spec_joined_lists(Window_spec *win_spec1, win_spec1->join_partition_and_order_lists(); win_spec2->join_partition_and_order_lists(); int cmp= compare_order_lists(win_spec1->partition_list, - win_spec2->partition_list); + win_spec1->win_spec_number, + win_spec2->partition_list, + win_spec2->win_spec_number); win_spec1->disjoin_partition_and_order_lists(); win_spec2->disjoin_partition_and_order_lists(); return cmp; @@ -471,7 +510,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1, if (win_spec1 == win_spec2) return CMP_EQ; cmp= compare_order_lists(win_spec1->partition_list, - win_spec2->partition_list); + win_spec1->win_spec_number, + win_spec2->partition_list, + win_spec2->win_spec_number); if (cmp == CMP_EQ) { /* @@ -490,7 +531,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1, } cmp= compare_order_lists(win_spec1->order_list, - win_spec2->order_list); + win_spec1->win_spec_number, + win_spec2->order_list, + win_spec2->win_spec_number); if (cmp != CMP_EQ) return cmp; @@ -587,7 +630,9 @@ void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list) int cmp; if (win_spec_prev->partition_list == win_spec_curr->partition_list) cmp= compare_order_lists(win_spec_prev->order_list, - win_spec_curr->order_list); + win_spec_prev->win_spec_number, + win_spec_curr->order_list, + win_spec_curr->win_spec_number); else cmp= compare_window_spec_joined_lists(win_spec_prev, win_spec_curr); if (!(CMP_LT_C <= cmp && cmp <= CMP_GT_C)) diff --git a/sql/sql_window.h b/sql/sql_window.h index 417d0bca12c..b29038fc374 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -108,6 +108,13 @@ class Window_spec : public Sql_alloc Window_spec *referenced_win_spec; + /* + Window_spec objects are numbered by the number of their appearance in the + query. This is used by compare_order_elements() to provide a predictable + ordering of PARTITION/ORDER BY clauses. + */ + int win_spec_number; + Window_spec(LEX_STRING *win_ref, SQL_I_List<ORDER> *part_list, SQL_I_List<ORDER> *ord_list, |