Commit 0c6d7537 authored by Igor Babaev's avatar Igor Babaev

Fixed a problems in the parser.

Resolved window names.
Checked some constraints for window frames.
Added test cases for window name resolution.
parent be158582
......@@ -253,3 +253,173 @@ pk c CNT
9 2 2
10 2 1
drop table t0,t1;
#
# Resolution of window names
#
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (pk int, c int);
insert into t1 select a+1,1 from t0;
update t1 set c=2 where pk not in (1,2,3,4);
select * from t1;
pk c
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 2
9 2
10 2
select
pk, c,
count(*) over w1 as CNT
from t1
window w1 as (partition by c order by pk
rows between 2 preceding and 2 following);
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
select
pk, c,
count(*) over (w1 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c order by pk);
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
select
pk, c,
count(*) over (w1 order by pk rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c);
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w1 order by pk);
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
select
pk, c,
count(*) over w3 as CNT
from t1
window
w1 as (partition by c),
w2 as (w1 order by pk),
w3 as (w2 rows between 2 preceding and 2 following);
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
select
pk, c,
count(*) over w as CNT
from t1
window w1 as (partition by c order by pk
rows between 2 preceding and 2 following);
ERROR HY000: Window specification with name 'w' is not defined
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w1 as (order by pk);
ERROR HY000: Multiple window specifications with the same name 'w1'
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w partition by c order by pk);
ERROR HY000: Window specification with name 'w' is not defined
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w1 partition by c order by pk);
ERROR HY000: Window specification referencing another one 'w1' cannot contain partition list
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c order by pk), w2 as (w1 order by pk);
ERROR HY000: Referenced window specification 'w1' already contains order list
select
pk, c,
count(*) over w3 as CNT
from t1
window
w1 as (partition by c),
w2 as (w1 order by pk rows between 3 preceding and 2 following),
w3 as (w2 rows between 2 preceding and 2 following);
ERROR HY000: Referenced window specification 'w2' cannot contain window frame
select
pk, c,
count(*) over w1 as CNT
from t1
window w1 as (partition by c order by pk
rows between unbounded following and 2 following);
ERROR HY000: Unacceptable combination of window frame bound specifications
select
pk, c,
count(*) over (w1 rows between 2 preceding and unbounded preceding) as CNT
from t1
window w1 as (partition by c order by pk);
ERROR HY000: Unacceptable combination of window frame bound specifications
select
pk, c,
count(*) over (w1 order by pk rows between current row and 2 preceding) as CNT
from t1
window w1 as (partition by c);
ERROR HY000: Unacceptable combination of window frame bound specifications
select
pk, c,
count(*) over (w2 rows between 2 following and current row) as CNT
from t1
window w1 as (partition by c), w2 as (w1 order by pk);
ERROR HY000: Unacceptable combination of window frame bound specifications
drop table t0,t1;
......@@ -157,5 +157,134 @@ select
count(*) over (partition by c order by pk
rows between current row and 1 following) as CNT
from t1;
drop table t0,t1;
--echo #
--echo # Resolution of window names
--echo #
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (pk int, c int);
insert into t1 select a+1,1 from t0;
update t1 set c=2 where pk not in (1,2,3,4);
select * from t1;
select
pk, c,
count(*) over w1 as CNT
from t1
window w1 as (partition by c order by pk
rows between 2 preceding and 2 following);
select
pk, c,
count(*) over (w1 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c order by pk);
select
pk, c,
count(*) over (w1 order by pk rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c);
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w1 order by pk);
select
pk, c,
count(*) over w3 as CNT
from t1
window
w1 as (partition by c),
w2 as (w1 order by pk),
w3 as (w2 rows between 2 preceding and 2 following);
--error ER_WRONG_WINDOW_SPEC_NAME
select
pk, c,
count(*) over w as CNT
from t1
window w1 as (partition by c order by pk
rows between 2 preceding and 2 following);
--error ER_DUP_WINDOW_NAME
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w1 as (order by pk);
--error ER_WRONG_WINDOW_SPEC_NAME
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w partition by c order by pk);
--error ER_PARTITION_LIST_IN_REFERENCING_WINDOW_SPEC
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c), w2 as (w1 partition by c order by pk);
--error ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC
select
pk, c,
count(*) over (w2 rows between 2 preceding and 2 following) as CNT
from t1
window w1 as (partition by c order by pk), w2 as (w1 order by pk);
--error ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC
select
pk, c,
count(*) over w3 as CNT
from t1
window
w1 as (partition by c),
w2 as (w1 order by pk rows between 3 preceding and 2 following),
w3 as (w2 rows between 2 preceding and 2 following);
--error ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS
select
pk, c,
count(*) over w1 as CNT
from t1
window w1 as (partition by c order by pk
rows between unbounded following and 2 following);
--error ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS
select
pk, c,
count(*) over (w1 rows between 2 preceding and unbounded preceding) as CNT
from t1
window w1 as (partition by c order by pk);
--error ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS
select
pk, c,
count(*) over (w1 order by pk rows between current row and 2 preceding) as CNT
from t1
window w1 as (partition by c);
--error ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS
select
pk, c,
count(*) over (w2 rows between 2 following and current row) as CNT
from t1
window w1 as (partition by c), w2 as (w1 order by pk);
drop table t0,t1;
......@@ -4,10 +4,48 @@
#include "sql_select.h" // test if group changed
bool
Item_window_func::resolve_window_name(THD *thd)
{
DBUG_ASSERT(window_name != NULL && window_spec == NULL);
char *ref_name= window_name->str;
/* !TODO: Add the code to resolve ref_name in outer queries */
/*
First look for the deinition of the window with 'window_name'
in the current select
*/
List<Window_spec> curr_window_specs=thd->lex->current_select->window_specs;
List_iterator_fast<Window_spec> it(curr_window_specs);
Window_spec *win_spec;
while((win_spec= it++))
{
char *win_spec_name= win_spec->name();
if (win_spec_name &&
my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
{
window_spec= win_spec;
break;
}
}
if (!window_spec)
{
my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);
return true;
}
return false;
}
bool
Item_window_func::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
if (window_name && resolve_window_name(thd))
return true;
/*
TODO: why the last parameter is 'ref' in this call? What if window_func
......@@ -16,14 +54,14 @@ Item_window_func::fix_fields(THD *thd, Item **ref)
object. Is this the intent?
*/
if (window_func->fix_fields(thd, ref))
return TRUE;
return true;
max_length= window_func->max_length;
fixed= 1;
force_return_blank= true;
read_value_from_result_field= false;
return FALSE;
return false;
}
......
......@@ -386,6 +386,9 @@ class Item_window_func : public Item_result_field
const char* func_name() const { return "WF"; }
bool fix_fields(THD *thd, Item **ref);
bool resolve_window_name(THD *thd);
};
......
......@@ -7146,4 +7146,6 @@ ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC
eng "Referenced window specification '%s' already contains order list"
ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC
eng "Referenced window specification '%s' cannot contain window frame"
ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS
eng "Unacceptable combination of window frame bound specifications"
......@@ -10,44 +10,45 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
{
char *name= this->name();
char *ref_name= window_reference();
bool win_ref_is_resolved= false;
it.rewind();
Window_spec *win_spec;
while((win_spec= it++) && win_spec != this)
{
char *win_spec_name= win_spec->name();
if (win_spec_name)
if (!win_spec_name)
break;
if (name && my_strcasecmp(system_charset_info, name, win_spec_name) == 0)
{
my_error(ER_DUP_WINDOW_NAME, MYF(0), name);
return true;
}
if (ref_name &&
my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
{
if (name && my_strcasecmp(system_charset_info, name, win_spec_name) == 0)
if (partition_list.elements)
{
my_error(ER_DUP_WINDOW_NAME, MYF(0), name);
my_error(ER_PARTITION_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0),
ref_name);
return true;
}
if (ref_name &&
my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
if (win_spec->order_list.elements && order_list.elements)
{
if (win_spec->partition_list.elements)
{
my_error(ER_PARTITION_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0),
ref_name);
return true;
}
if (win_spec->order_list.elements && order_list.elements)
{
my_error(ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0), ref_name);
return true;
}
if (win_spec->window_frame)
{
my_error(ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC, MYF(0), ref_name);
return true;
}
referenced_win_spec=win_spec;
win_ref_is_resolved= true;
my_error(ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0), ref_name);
return true;
}
if (win_spec->window_frame)
{
my_error(ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC, MYF(0), ref_name);
return true;
}
referenced_win_spec= win_spec;
if (partition_list.elements == 0)
partition_list= win_spec->partition_list;
if (order_list.elements == 0)
order_list= win_spec->order_list;
}
}
if (ref_name && !win_ref_is_resolved)
if (ref_name && !referenced_win_spec)
{
my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);
return true;
......@@ -55,6 +56,25 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
return false;
}
bool
Window_frame::check_frame_bounds()
{
if ((top_bound->is_unbounded() &&
top_bound->precedence_type == Window_frame_bound::FOLLOWING) ||
(bottom_bound->is_unbounded() &&
bottom_bound->precedence_type == Window_frame_bound::PRECEDING) ||
(top_bound->precedence_type == Window_frame_bound::CURRENT &&
bottom_bound->precedence_type == Window_frame_bound::PRECEDING) ||
(bottom_bound->precedence_type == Window_frame_bound::CURRENT &&
top_bound->precedence_type == Window_frame_bound::FOLLOWING))
{
my_error(ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS, MYF(0));
return true;
}
return false;
}
int
setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
......@@ -64,6 +84,24 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
Window_spec *win_spec;
DBUG_ENTER("setup_windows");
List_iterator<Window_spec> it(win_specs);
/*
Move all unnamed specifications after the named ones.
We could have avoided it if we had built two separate lists for
named and unnamed specifications.
*/
uint i = 0;
uint elems= win_specs.elements;
while ((win_spec= it++) && i++ < elems)
{
if (win_spec->name() == NULL)
{
it.remove();
win_specs.push_back(win_spec);
}
}
it.rewind();
List_iterator_fast<Window_spec> itp(win_specs);
while ((win_spec= it++))
......@@ -73,7 +111,9 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
setup_group(thd, ref_pointer_array, tables, fields, all_fields,
win_spec->partition_list.first, &hidden_group_fields) ||
setup_order(thd, ref_pointer_array, tables, fields, all_fields,
win_spec->order_list.first))
win_spec->order_list.first) ||
(win_spec->window_frame &&
win_spec->window_frame->check_frame_bounds()))
{
DBUG_RETURN(1);
}
......
......@@ -38,7 +38,9 @@ class Window_frame_bound : public Sql_alloc
Window_frame_bound(Bound_precedence_type prec_type,
Item *offset_val)
: precedence_type(prec_type), offset(offset_val) {}
bool is_unbounded() { return offset == NULL; }
};
......@@ -76,6 +78,8 @@ class Window_frame : public Sql_alloc
: units(win_frame_units), top_bound(win_frame_top_bound),
bottom_bound(win_frame_bottom_bound), exclusion(win_frame_exclusion) {}
bool check_frame_bounds();
};
class Window_spec : public Sql_alloc
......@@ -99,11 +103,12 @@ class Window_spec : public Sql_alloc
: window_ref(win_ref), partition_list(part_list), order_list(ord_list),
window_frame(win_frame), referenced_win_spec(NULL) {}
virtual char *name() { return NULL; }
virtual char *name() { return NULL; }
bool check_window_names(List_iterator_fast<Window_spec> &it);
bool check_window_names(List_iterator_fast<Window_spec> &it);
char *window_reference() { return window_ref ? window_ref->str : NULL; }
char *window_reference() { return window_ref ? window_ref->str : NULL; }
};
class Window_def : public Window_spec
......@@ -120,7 +125,7 @@ class Window_def : public Window_spec
: Window_spec(win_ref, part_list, ord_list, win_frame),
window_name(win_name) {}
char *name() { return window_name->str; }
char *name() { return window_name->str; }
};
......
......@@ -8489,19 +8489,20 @@ select_part2:
opt_where_clause
opt_group_clause
opt_having_clause
opt_window_clause
opt_order_clause
opt_limit_clause
opt_procedure_clause
opt_into
opt_select_lock_type
{
if ($2 && $10)
if ($2 && $11)
{
/* double "INTO" clause */
my_error(ER_WRONG_USAGE, MYF(0), "INTO", "INTO");
MYSQL_YYABORT;
}
if ($9 && ($2 || $10))
if ($10 && ($2 || $11))
{
/* "INTO" with "PROCEDURE ANALYSE" */
my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "INTO");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment