Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions mysql-test/main/win_orderby.result
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,158 @@ id select_type table type possible_keys key key_len ref rows Extra
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
drop table t;
#
# MDEV-36356 Server crash in Item::save_int_in_field and UBSAN null-pointer-use
#
(SELECT 1 FROM dual LIMIT 5) ORDER BY ROW_NUMBER() OVER (PARTITION BY 1);
1
1
(SELECT 1 FROM dual LIMIT 5) ORDER BY ROW_NUMBER() OVER ();
1
1
(SELECT 5 a FROM dual LIMIT 1) ORDER BY AVG(a) OVER (ORDER BY 1);
a
5
(SELECT CAST('1973' AS DATETIME) a FROM dual LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
a
NULL
Warnings:
Warning 1292 Incorrect datetime value: '1973'
(SELECT CAST('1973' AS CHAR) a FROM dual LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
a
1973
SET @save_sql_mode= @@sql_mode;
SET sql_mode='';
(SELECT CAST('' AS CHAR) a LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
a

SET sql_mode= @save_sql_mode;
((SELECT 1) FOR UPDATE WAIT + 1) ORDER BY PERCENTILE_DISC(COUNT(*)) WITHIN GROUP(ORDER BY 1) OVER();
1
1
((SELECT 1) FOR UPDATE WAIT + 1) ORDER BY PERCENTILE_DISC(COUNT(*)) WITHIN GROUP(ORDER BY CURRENT_TIMESTAMP) OVER();
1
1
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (3),(1),(2);
CREATE TABLE t2 (a INT, b INT);
INSERT INTO t2 VALUES (3,1),(1,2),(2,1);
SELECT * FROM ((SELECT a FROM t1 UNION SELECT 9 LIMIT 5) ORDER BY ROW_NUMBER() OVER ()) x ORDER BY x.a;
a
1
2
3
9
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY (SELECT MAX(a) FROM t1))) x ORDER BY x.a;
a
1
2
3
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER () + (SELECT MAX(a) FROM t1)) x ORDER BY x.a;
a
1
2
3
# This query used to crash, now it correctly fails with a bad field reference error
(SELECT a FROM t1 LIMIT 5) ORDER BY (SELECT a FROM t2 WHERE t2.a = t1.a LIMIT 1);
ERROR 42S22: Unknown column 't1.a' in 'WHERE'
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER ()) x, ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a)) y ORDER BY x.a, y.a;
a a
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
# The value of the window function must be the one of the derived table form
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (PARTITION BY a)) x ORDER BY x.a;
a
1
2
3
SELECT * FROM (SELECT * FROM (SELECT a FROM t1 LIMIT 5) dt ORDER BY ROW_NUMBER() OVER (PARTITION BY a)) x ORDER BY x.a;
a
1
2
3
# A query expression nested in the ORDER BY must not hide the window
# function that precedes it: all of these must sort descending
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 LIMIT 1) DESC;
a
3
2
1
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 ORDER BY a LIMIT 1) DESC;
a
3
2
1
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT MAX(d.a)*0 FROM (SELECT a FROM t1 ORDER BY a) d) DESC;
a
3
2
1
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 ORDER BY a LIMIT 1) + RANK() OVER (ORDER BY a) DESC;
a
3
2
1
# A window function referring to a window defined inside the parentheses
# is not visible from the ORDER BY that follows them
(SELECT a FROM t1 WINDOW w AS (ORDER BY a) LIMIT 5) ORDER BY ROW_NUMBER() OVER w;
ERROR HY000: Window specification with name 'w' is not defined
# A window function of a subquery of the ORDER BY stays in that subquery
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY (SELECT ROW_NUMBER() OVER () FROM t1 LIMIT 1)) x ORDER BY x.a;
a
1
2
3
# Correlated subqueries of the ORDER BY are re-registered in the wrapper
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY EXISTS (SELECT 1 FROM t2 WHERE t2.a=a AND t2.b=a)) x ORDER BY x.a;
a
1
2
3
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY a IN (SELECT b FROM t2 WHERE t2.a=a)) x ORDER BY x.a;
a
1
2
3
DROP TABLE t1, t2;
CREATE TABLE t1 (c1 INT, c2 TEXT);
INSERT INTO t1 (c1, c2) VALUES (0, 'a');
SELECT * FROM
{
ta1 t1 AS ta2 NATURAL RIGHT OUTER JOIN
(
(
SELECT * FROM
{
ta3 t1 AS ta4 NATURAL STRAIGHT_JOIN t1 AS ta5
}
LIMIT 1234567890 ROWS EXAMINED 1234567890 LOCK IN SHARE MODE SKIP LOCKED
)
ORDER BY FALSE <=> + INTERVAL NOT FALSE=FALSE IN
(
SELECT FALSE <=> FALSE IN (SELECT 'string')
)
SECOND_MICROSECOND + TRUE <=> TRUE IN (SELECT 'string') << ROW_NUMBER () OVER
(
PARTITION BY NOT TRUE <=> FALSE IN (SELECT 'string') DESC
)
IN (SELECT 'string')
)=ta6
NATURAL JOIN t1 AS ta7
};
c1 c2
0 a
Warnings:
Warning 1292 Truncated incorrect DECIMAL value: 'string'
Warning 1292 Truncated incorrect DECIMAL value: 'string'
Warning 1292 Incorrect datetime value: '0000-00-00 00:00:00'
DROP TABLE t1;
#
# End of 10.11 tests
#
81 changes: 81 additions & 0 deletions mysql-test/main/win_orderby.test
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,87 @@ explain update t set c=1 where exists (
from t order by x=avg(row_number() over t) over (order by 1));
drop table t;

--echo #
--echo # MDEV-36356 Server crash in Item::save_int_in_field and UBSAN null-pointer-use
--echo #
(SELECT 1 FROM dual LIMIT 5) ORDER BY ROW_NUMBER() OVER (PARTITION BY 1);
(SELECT 1 FROM dual LIMIT 5) ORDER BY ROW_NUMBER() OVER ();
(SELECT 5 a FROM dual LIMIT 1) ORDER BY AVG(a) OVER (ORDER BY 1);
(SELECT CAST('1973' AS DATETIME) a FROM dual LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
(SELECT CAST('1973' AS CHAR) a FROM dual LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
SET @save_sql_mode= @@sql_mode;
SET sql_mode='';
(SELECT CAST('' AS CHAR) a LIMIT 1) ORDER BY FIRST_VALUE(a) OVER (ORDER BY 1);
SET sql_mode= @save_sql_mode;
((SELECT 1) FOR UPDATE WAIT + 1) ORDER BY PERCENTILE_DISC(COUNT(*)) WITHIN GROUP(ORDER BY 1) OVER();
((SELECT 1) FOR UPDATE WAIT + 1) ORDER BY PERCENTILE_DISC(COUNT(*)) WITHIN GROUP(ORDER BY CURRENT_TIMESTAMP) OVER();

CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (3),(1),(2);
CREATE TABLE t2 (a INT, b INT);
INSERT INTO t2 VALUES (3,1),(1,2),(2,1);

SELECT * FROM ((SELECT a FROM t1 UNION SELECT 9 LIMIT 5) ORDER BY ROW_NUMBER() OVER ()) x ORDER BY x.a;
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY (SELECT MAX(a) FROM t1))) x ORDER BY x.a;
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER () + (SELECT MAX(a) FROM t1)) x ORDER BY x.a;

--echo # This query used to crash, now it correctly fails with a bad field reference error
--error ER_BAD_FIELD_ERROR
(SELECT a FROM t1 LIMIT 5) ORDER BY (SELECT a FROM t2 WHERE t2.a = t1.a LIMIT 1);
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER ()) x, ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a)) y ORDER BY x.a, y.a;

--echo # The value of the window function must be the one of the derived table form
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (PARTITION BY a)) x ORDER BY x.a;
SELECT * FROM (SELECT * FROM (SELECT a FROM t1 LIMIT 5) dt ORDER BY ROW_NUMBER() OVER (PARTITION BY a)) x ORDER BY x.a;

--echo # A query expression nested in the ORDER BY must not hide the window
--echo # function that precedes it: all of these must sort descending
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 LIMIT 1) DESC;
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 ORDER BY a LIMIT 1) DESC;
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT MAX(d.a)*0 FROM (SELECT a FROM t1 ORDER BY a) d) DESC;
(SELECT a FROM t1 LIMIT 5) ORDER BY ROW_NUMBER() OVER (ORDER BY a) + (SELECT 0 FROM t1 ORDER BY a LIMIT 1) + RANK() OVER (ORDER BY a) DESC;

--echo # A window function referring to a window defined inside the parentheses
--echo # is not visible from the ORDER BY that follows them
--error ER_WRONG_WINDOW_SPEC_NAME
(SELECT a FROM t1 WINDOW w AS (ORDER BY a) LIMIT 5) ORDER BY ROW_NUMBER() OVER w;

--echo # A window function of a subquery of the ORDER BY stays in that subquery
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY (SELECT ROW_NUMBER() OVER () FROM t1 LIMIT 1)) x ORDER BY x.a;

--echo # Correlated subqueries of the ORDER BY are re-registered in the wrapper
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY EXISTS (SELECT 1 FROM t2 WHERE t2.a=a AND t2.b=a)) x ORDER BY x.a;
SELECT * FROM ((SELECT a FROM t1 LIMIT 5) ORDER BY a IN (SELECT b FROM t2 WHERE t2.a=a)) x ORDER BY x.a;

DROP TABLE t1, t2;

CREATE TABLE t1 (c1 INT, c2 TEXT);
INSERT INTO t1 (c1, c2) VALUES (0, 'a');
SELECT * FROM
{
ta1 t1 AS ta2 NATURAL RIGHT OUTER JOIN
(
(
SELECT * FROM
{
ta3 t1 AS ta4 NATURAL STRAIGHT_JOIN t1 AS ta5
}
LIMIT 1234567890 ROWS EXAMINED 1234567890 LOCK IN SHARE MODE SKIP LOCKED
)
ORDER BY FALSE <=> + INTERVAL NOT FALSE=FALSE IN
(
SELECT FALSE <=> FALSE IN (SELECT 'string')
)
SECOND_MICROSECOND + TRUE <=> TRUE IN (SELECT 'string') << ROW_NUMBER () OVER
(
PARTITION BY NOT TRUE <=> FALSE IN (SELECT 'string') DESC
)
IN (SELECT 'string')
)=ta6
NATURAL JOIN t1 AS ta7
};
DROP TABLE t1;

--echo #
--echo # End of 10.11 tests
--echo #
122 changes: 116 additions & 6 deletions sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10686,13 +10686,121 @@ SELECT_LEX_UNIT *LEX::add_tail_to_query_expression_body(SELECT_LEX_UNIT *unit,
}


static bool order_list_contains(SQL_I_List<ORDER> *order_list, Item *item)
{
for (ORDER *ord= order_list->first; ord; ord= ord->next)
{
if ((*ord->item)->walk(&Item::find_item_processor, FALSE, item))
return true;
}
return false;
}


static bool remove_window_spec(SELECT_LEX *sel, Window_spec *win_spec)
{
List_iterator<Window_spec> it(sel->window_specs);
Window_spec *cur;
while ((cur= it++))
{
if (cur == win_spec)
{
it.remove();
return true;
}
}
return false;
}


/*
Move to another select what the tail of a query expression registered

The tail (ORDER BY / LIMIT / locking clause) that follows a parenthesized
query expression is parsed while the select inside the parentheses is the
current one, so its window functions, their window specifications and its
subqueries all get registered there. When the query expression is wrapped
into a derived table the tail belongs to the wrapping select, and these
registrations have to follow it.

The tail's window functions are the ones its ORDER BY expressions contain:
they are looked up with Item::walk() and walk_subquery == FALSE, so a window
function that belongs to a subquery of the tail is not found and stays
registered in the select it was parsed in. Each of them takes along the
window specification it introduced; "OVER win_name" has none of its own, it
is looked up by name at fix_fields() time and never reaches window_specs.

The tail's subquery units are the ones registered in front of 'first_unit',
the first unit 'from' had when the tail started to be parsed. There is no
equivalent of the walk above for them: a unit records neither the clause it
came from nor its position in the parsed text.

Note that per-select parse state that is not a registration is deliberately
left behind on 'from', where it is merely over-counted: n_sum_items,
with_sum_func, with_rownum, ftfunc_list, uncacheable and the
select_n_where_fields of the tail's own fields.
*/
static bool move_tail_registrations(THD *thd, SELECT_LEX *from, SELECT_LEX *to,
SQL_I_List<ORDER> *order_list,
SELECT_LEX_UNIT *first_unit)
{
if (order_list)
{
List_iterator<Item_window_func> it(from->window_funcs);
Item_window_func *win_func;
while ((win_func= it++))
{
if (!order_list_contains(order_list, win_func))
continue;
it.remove();
if (to->window_funcs.push_back(win_func, thd->mem_root))
return true;
to->fields_in_window_functions+=
win_func->window_func()->argument_count();

Window_spec *win_spec= win_func->window_spec;
if (!win_spec || !remove_window_spec(from, win_spec))
continue;
if (to->window_specs.push_back(win_spec, thd->mem_root))
return true;
to->fields_in_window_functions+= win_spec->partition_list->elements +
win_spec->order_list->elements;
/*
Item::walk() does not descend into a window specification, so the
items of these lists are not re-targeted by
Lex_order_limit_lock::set_to().
*/
for (ORDER *o= win_spec->partition_list->first; o; o= o->next)
(*o->item)->walk(&Item::change_context_processor, FALSE, &to->context);
for (ORDER *o= win_spec->order_list->first; o; o= o->next)
(*o->item)->walk(&Item::change_context_processor, FALSE, &to->context);
}
}

st_select_lex_unit *next_unit;
for (st_select_lex_unit *unit= from->first_inner_unit();
unit != first_unit; unit= next_unit)
{
next_unit= unit->next_unit();
unit->exclude_from_tree();
unit->cut_next(); // so add_statistics() stops here
to->add_statistics(unit);
to->register_unit(unit, &to->context);
if (unit->item)
unit->item->parent_select= to;
}
return false;
}


/**
Add non-empty tail to a parenthesized query primary
*/

SELECT_LEX_UNIT *
LEX::add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit,
Lex_order_limit_lock *l)
Lex_order_limit_lock *l,
SELECT_LEX_UNIT *first_unit)
{
SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex :
unit->first_select();
Expand All @@ -10706,14 +10814,16 @@ LEX::add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit,
l->order_list= &sel->order_list;
else
{
if (!unit)
return NULL;
SELECT_LEX *inner_sel= sel;
sel= wrap_unit_into_derived(unit);
if (!sel)
return NULL;
if (!create_unit(sel))
return NULL;
}
if (!create_unit(sel))
return NULL;
if (move_tail_registrations(thd, inner_sel, sel, l->order_list,
first_unit))
return NULL;
}
}
l->set_to(sel);
return sel->master_unit();
Expand Down
Loading