mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Fixed a problems in the parser.
Resolved window names. Checked some constraints for window frames. Added test cases for window name resolution.
This commit is contained in:
@@ -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 @@ public:
|
||||
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)
|
||||
{
|
||||
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 (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 @@ public:
|
||||
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 @@ public:
|
||||
: 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");
|
||||
|
Reference in New Issue
Block a user