1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

Changed the base class for Item_window_func from Item_result_field to

Item_func_or_sum.
Implemented method update_used_tables for class Item_findow_func.
Added the flag Item::with_window_func.
Made sure that window functions could be used only in SELECT list
and ORDER BY clause.
Added test cases that checked different illegal placements of
window functions.
This commit is contained in:
Igor Babaev
2016-03-23 16:09:58 -07:00
parent a74e8d36dd
commit 82cb35be11
15 changed files with 245 additions and 52 deletions

View File

@ -12,9 +12,9 @@ select rank() from t1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'from t1' at line 1
# Attempt to use window function in the WHERE clause
select * from t1 where 1=rank() over (order by a);
ERROR HY000: Invalid use of group function
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
select * from t1 where 1>row_number() over (partition by b order by a);
ERROR HY000: Invalid use of group function
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
drop table t1;
# ########################################################################
# # Functionality tests
@ -452,6 +452,49 @@ from t1
window w1 as (partition by c), w2 as (w1 order by pk);
ERROR HY000: Unacceptable combination of window frame bound specifications
select
pk, c
from t1 where rank() over w1 > 2
window w1 as (partition by c order by pk);
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
select
c, max(pk) as m
from t1
group by c + rank() over w1
window w1 as (order by m);
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
select
c, max(pk) as m, rank() over w1 as r
from t1
group by c+r
window w1 as (order by m);
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
select
c, max(pk) as m, rank() over w1 as r
from t1
group by c having c+r > 3
window w1 as (order by m);
ERROR HY000: Window function is allowed only in SELECT list and ORDER BY clause
select
c, max(pk) as m, rank() over w1 as r,
rank() over (partition by r+1 order by m)
from t1
group by c
window w1 as (order by m);
ERROR HY000: Window function is not allowed in window specification
select
c, max(pk) as m, rank() over w1 as r,
rank() over (partition by m order by r)
from t1
group by c
window w1 as (order by m);
ERROR HY000: Window function is not allowed in window specification
select
c, max(pk) as m, rank() over w1 as r, dense_rank() over w2 as dr
from t1
group by c
window w1 as (order by m), w2 as (partition by r order by m);
ERROR HY000: Window function is not allowed in window specification
select
pk, c,
row_number() over (partition by c order by pk
range between unbounded preceding and current row) as r

View File

@ -20,9 +20,9 @@ select row_number() from t1;
select rank() from t1;
--echo # Attempt to use window function in the WHERE clause
--error ER_INVALID_GROUP_FUNC_USE
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select * from t1 where 1=rank() over (order by a);
--error ER_INVALID_GROUP_FUNC_USE
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select * from t1 where 1>row_number() over (partition by b order by a);
drop table t1;
@ -289,6 +289,56 @@ select
from t1
window w1 as (partition by c), w2 as (w1 order by pk);
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select
pk, c
from t1 where rank() over w1 > 2
window w1 as (partition by c order by pk);
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select
c, max(pk) as m
from t1
group by c + rank() over w1
window w1 as (order by m);
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select
c, max(pk) as m, rank() over w1 as r
from t1
group by c+r
window w1 as (order by m);
--error ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
select
c, max(pk) as m, rank() over w1 as r
from t1
group by c having c+r > 3
window w1 as (order by m);
--error ER_WINDOW_FUNCTION_IN_WINDOW_SPEC
select
c, max(pk) as m, rank() over w1 as r,
rank() over (partition by r+1 order by m)
from t1
group by c
window w1 as (order by m);
--error ER_WINDOW_FUNCTION_IN_WINDOW_SPEC
select
c, max(pk) as m, rank() over w1 as r,
rank() over (partition by m order by r)
from t1
group by c
window w1 as (order by m);
--error ER_WINDOW_FUNCTION_IN_WINDOW_SPEC
select
c, max(pk) as m, rank() over w1 as r, dense_rank() over w2 as dr
from t1
group by c
window w1 as (order by m), w2 as (partition by r order by m);
--error ER_NOT_ALLOWED_WINDOW_FRAME
select
pk, c,

View File

@ -455,7 +455,7 @@ Item::Item(THD *thd):
{
DBUG_ASSERT(thd);
marker= 0;
maybe_null=null_value=with_sum_func=with_field=0;
maybe_null=null_value=with_sum_func=with_window_func=with_field=0;
in_rollup= 0;
with_subselect= 0;
/* Initially this item is not attached to any JOIN_TAB. */
@ -500,6 +500,7 @@ Item::Item(THD *thd, Item *item):
in_rollup(item->in_rollup),
null_value(item->null_value),
with_sum_func(item->with_sum_func),
with_window_func(item->with_window_func),
with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
@ -6913,6 +6914,7 @@ void Item_ref::set_properties()
split_sum_func() doesn't try to change the reference.
*/
with_sum_func= (*ref)->with_sum_func;
with_window_func= (*ref)->with_window_func;
with_field= (*ref)->with_field;
fixed= 1;
if (alias_name_used)

View File

@ -697,6 +697,7 @@ public:
of a query with ROLLUP */
bool null_value; /* if item is null */
bool with_sum_func; /* True if item contains a sum func */
bool with_window_func; /* True if item contains a window func */
/**
True if any item except Item_sum contains a field. Set during parsing.
*/

View File

@ -132,6 +132,7 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
while ((item= li++))
{
with_sum_func|= item->with_sum_func;
with_window_func|= item->with_window_func;
with_field|= item->with_field;
}
}
@ -226,6 +227,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
maybe_null=1;
with_sum_func= with_sum_func || item->with_sum_func;
with_window_func= with_window_func || item->with_window_func;
with_field= with_field || item->with_field;
used_tables_and_const_cache_join(item);
with_subselect|= item->has_subquery();

View File

@ -45,23 +45,52 @@ Item_window_func::resolve_window_name(THD *thd)
}
void
Item_window_func::update_used_tables()
{
used_tables_cache= 0;
window_func()->update_used_tables();
used_tables_cache|= window_func()->used_tables();
for (ORDER *ord= window_spec->partition_list->first; ord; ord=ord->next)
{
Item *item= *ord->item;
item->update_used_tables();
used_tables_cache|= item->used_tables();
}
for (ORDER *ord= window_spec->order_list->first; ord; ord=ord->next)
{
Item *item= *ord->item;
item->update_used_tables();
used_tables_cache|= item->used_tables();
}
}
bool
Item_window_func::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
enum_parsing_place place= thd->lex->current_select->parsing_place;
if (!(place == SELECT_LIST || place == IN_ORDER_BY))
{
my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
return true;
}
if (window_name && resolve_window_name(thd))
return true;
if (window_spec->window_frame && is_frame_prohibited())
{
my_error(ER_NOT_ALLOWED_WINDOW_FRAME, MYF(0), window_func->func_name());
my_error(ER_NOT_ALLOWED_WINDOW_FRAME, MYF(0), window_func()->func_name());
return true;
}
if (window_spec->order_list->elements == 0 && is_order_list_mandatory())
{
my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func->func_name());
my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name());
return true;
}
/*
@ -70,13 +99,17 @@ Item_window_func::fix_fields(THD *thd, Item **ref)
This will substitute *this (an Item_window_func object) with Item_sum
object. Is this the intent?
*/
if (window_func->fix_fields(thd, ref))
if (window_func()->fix_fields(thd, ref))
return true;
const_item_cache= false;
with_window_func= true;
with_sum_func= false;
fix_length_and_dec();
max_length= window_func->max_length;
maybe_null= window_func->maybe_null;
max_length= window_func()->max_length;
maybe_null= window_func()->maybe_null;
fixed= 1;
force_return_blank= true;
@ -113,9 +146,9 @@ Item_window_func::fix_fields(THD *thd, Item **ref)
void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags)
{
for (uint i=0; i < window_func->argument_count(); i++)
for (uint i=0; i < window_func()->argument_count(); i++)
{
Item **p_item= &window_func->arguments()[i];
Item **p_item= &window_func()->arguments()[i];
(*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags);
}
}
@ -138,7 +171,7 @@ void Item_window_func::setup_partition_border_check(THD *thd)
Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
partition_fields.push_back(tmp);
}
window_func->setup_window_func(thd, window_spec);
window_func()->setup_window_func(thd, window_spec);
}
@ -196,9 +229,9 @@ void Item_window_func::advance_window()
if (changed > -1)
{
/* Next partition */
window_func->clear();
window_func()->clear();
}
window_func->add();
window_func()->add();
}
bool Item_sum_percent_rank::add()

View File

@ -408,37 +408,40 @@ class Item_sum_cume_dist: public Item_sum_percent_rank
};
class Item_window_func : public Item_result_field
class Item_window_func : public Item_func_or_sum
{
/* Window function parameters as we've got them from the parser */
public:
Item_sum *window_func;
LEX_STRING *window_name;
public:
Window_spec *window_spec;
/*
This stores the data bout the partition we're currently in.
This stores the data about the partition we're currently in.
advance_window() uses this to tell when we've left one partition and
entered another.
entered another
*/
List<Cached_item> partition_fields;
public:
Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)
: Item_result_field(thd), window_func(win_func),
: Item_func_or_sum(thd, (Item *) win_func),
window_name(win_name), window_spec(NULL),
force_return_blank(true),
read_value_from_result_field(false) {}
read_value_from_result_field(false) {}
Item_window_func(THD *thd, Item_sum *win_func, Window_spec *win_spec)
: Item_result_field(thd), window_func(win_func),
: Item_func_or_sum(thd, (Item *) win_func),
window_name(NULL), window_spec(win_spec),
force_return_blank(true),
read_value_from_result_field(false) {}
read_value_from_result_field(false) {}
Item_sum *window_func() { return (Item_sum *) args[0]; }
void update_used_tables();
bool is_frame_prohibited()
{
switch (window_func->sum_func()) {
switch (window_func()->sum_func()) {
case Item_sum::ROW_NUMBER_FUNC:
case Item_sum::RANK_FUNC:
case Item_sum::DENSE_RANK_FUNC:
@ -452,7 +455,7 @@ public:
bool is_order_list_mandatory()
{
switch (window_func->sum_func()) {
switch (window_func()->sum_func()) {
case Item_sum::RANK_FUNC:
case Item_sum::DENSE_RANK_FUNC:
case Item_sum::PERCENT_RANK_FUNC:
@ -472,7 +475,10 @@ public:
void advance_window();
int check_partition_bound();
enum_field_types field_type() const { return window_func->field_type(); }
enum_field_types field_type() const
{
return ((Item_sum *) args[0])->field_type();
}
enum Item::Type type() const { return Item::WINDOW_FUNC_ITEM; }
/*
@ -485,7 +491,7 @@ public:
- Phase#2: executor does the scan in {PARTITION, ORDER BY} order of this
window function. It calls appropriate methods to inform the window
function about rows entering/leaving the window.
It calls window_func->val_int() so that current window function value
It calls window_func()->val_int() so that current window function value
can be saved and stored in the temp.table.
- Phase#3: the temporary table is read and passed to query output.
@ -521,8 +527,8 @@ public:
}
else
{
res= window_func->val_real();
null_value= window_func->null_value;
res= window_func()->val_real();
null_value= window_func()->null_value;
}
return res;
}
@ -542,8 +548,8 @@ public:
}
else
{
res= window_func->val_int();
null_value= window_func->null_value;
res= window_func()->val_int();
null_value= window_func()->null_value;
}
return res;
}
@ -566,8 +572,8 @@ public:
}
else
{
res= window_func->val_str(str);
null_value= window_func->null_value;
res= window_func()->val_str(str);
null_value= window_func()->null_value;
}
return res;
}
@ -590,8 +596,8 @@ public:
}
else
{
res= window_func->val_decimal(dec);
null_value= window_func->null_value;
res= window_func()->val_decimal(dec);
null_value= window_func()->null_value;
}
return res;
}
@ -600,7 +606,7 @@ public:
List<Item> &fields, uint flags);
void fix_length_and_dec()
{
decimals = window_func->decimals;
decimals = window_func()->decimals;
}
const char* func_name() const { return "WF"; }

View File

@ -7148,6 +7148,10 @@ 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"
ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION
eng "Window function is allowed only in SELECT list and ORDER BY clause"
ER_WINDOW_FUNCTION_IN_WINDOW_SPEC
eng "Window function is not allowed in window specification"
ER_NOT_ALLOWED_WINDOW_FRAME
eng "Window frame is not allowed with '%s'"
ER_NO_ORDER_LIST_IN_WINDOW_SPEC

View File

@ -1935,6 +1935,7 @@ void st_select_lex::init_query()
m_non_agg_field_used= false;
m_agg_func_used= false;
window_specs.empty();
window_funcs.empty();
}
void st_select_lex::init_select()

View File

@ -48,6 +48,7 @@ class sys_var;
class Item_func_match;
class File_parser;
class Key_part_spec;
class Item_window_func;
struct sql_digest_state;
#define ALLOC_ROOT_SET 1024
@ -1081,6 +1082,11 @@ public:
SQL_I_List<ORDER> win_partition_list,
SQL_I_List<ORDER> win_order_list,
Window_frame *win_frame);
List<Item_window_func> window_funcs;
bool add_window_func(Item_window_func *win_func)
{
return window_funcs.push_back(win_func);
}
private:
bool m_non_agg_field_used;

View File

@ -344,6 +344,7 @@ enum enum_parsing_place
IN_WHERE,
IN_ON,
IN_GROUP_BY,
IN_ORDER_BY,
PARSING_PLACE_SIZE /* always should be the last */
};

View File

@ -622,14 +622,16 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
ORDER *order,
ORDER *group,
List<Window_spec> &win_specs,
List<Item_window_func> &win_funcs,
bool *hidden_group_fields,
uint *reserved)
{
int res;
enum_parsing_place save_place;
st_select_lex *const select= thd->lex->current_select;
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
/*
Need to save the value, so we can turn off only any new non_agg_field_used
Need to stave the value, so we can turn off only any new non_agg_field_used
additions coming from the WHERE
*/
const bool saved_non_agg_field_used= select->non_agg_field_used();
@ -649,14 +651,21 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
select->set_non_agg_field_used(saved_non_agg_field_used);
thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level;
save_place= thd->lex->current_select->parsing_place;
thd->lex->current_select->parsing_place= IN_ORDER_BY;
res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields,
order);
thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
thd->lex->current_select->parsing_place= save_place;
thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
save_place= thd->lex->current_select->parsing_place;
thd->lex->current_select->parsing_place= IN_GROUP_BY;
res= res || setup_group(thd, ref_pointer_array, tables, fields, all_fields,
group, hidden_group_fields);
thd->lex->current_select->parsing_place= save_place;
thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level;
res= res || setup_windows(thd, ref_pointer_array, tables, fields, all_fields,
win_specs);
win_specs, win_funcs);
thd->lex->allow_sum_func= save_allow_sum_func;
DBUG_RETURN(res);
}
@ -791,14 +800,18 @@ JOIN::prepare(TABLE_LIST *tables_init,
ref_ptrs= ref_ptr_array_slice(0);
enum_parsing_place save_place= thd->lex->current_select->parsing_place;
thd->lex->current_select->parsing_place= SELECT_LIST;
if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ,
&all_fields, 1))
DBUG_RETURN(-1);
thd->lex->current_select->parsing_place= save_place;
if (setup_without_group(thd, ref_ptrs, tables_list,
select_lex->leaf_tables, fields_list,
all_fields, &conds, order, group_list,
select_lex->window_specs,
select_lex->window_funcs,
&hidden_group_fields,
&select_lex->select_n_reserved))
DBUG_RETURN(-1);
@ -834,6 +847,12 @@ JOIN::prepare(TABLE_LIST *tables_init,
if (having_fix_rc || thd->is_error())
DBUG_RETURN(-1); /* purecov: inspected */
thd->lex->allow_sum_func= save_allow_sum_func;
if (having->with_window_func)
{
my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
DBUG_RETURN(-1);
}
}
int res= check_and_do_in_subquery_rewrites(this);
@ -21842,12 +21861,18 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables
int setup_order(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
List<Item> &fields, List<Item> &all_fields, ORDER *order)
{
enum_parsing_place parsing_place= thd->lex->current_select->parsing_place;
thd->where="order clause";
for (; order; order=order->next)
{
if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
all_fields, FALSE))
return 1;
if ((*order->item)->with_window_func && parsing_place != IN_ORDER_BY)
{
my_error(ER_WINDOW_FUNCTION_IN_WINDOW_SPEC, MYF(0));
return 1;
}
}
return 0;
}
@ -21884,6 +21909,7 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
List<Item> &fields, List<Item> &all_fields, ORDER *order,
bool *hidden_group_fields)
{
enum_parsing_place parsing_place= thd->lex->current_select->parsing_place;
*hidden_group_fields=0;
ORDER *ord;
@ -21893,22 +21919,26 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
uint org_fields=all_fields.elements;
thd->where="group statement";
enum_parsing_place save_place= thd->lex->current_select->parsing_place;
thd->lex->current_select->parsing_place= IN_GROUP_BY;
for (ord= order; ord; ord= ord->next)
{
if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields,
all_fields, TRUE))
return 1;
(*ord->item)->marker= UNDEF_POS; /* Mark found */
if ((*ord->item)->with_sum_func)
if ((*ord->item)->with_sum_func && parsing_place == IN_GROUP_BY)
{
my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name());
return 1;
}
if ((*ord->item)->with_window_func)
{
if (parsing_place == IN_GROUP_BY)
my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
else
my_error(ER_WINDOW_FUNCTION_IN_WINDOW_SPEC, MYF(0));
return 1;
}
}
thd->lex->current_select->parsing_place= save_place;
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
{
/*

View File

@ -83,7 +83,7 @@ Window_frame::check_frame_bounds()
int
setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
List<Item> &fields, List<Item> &all_fields,
List<Window_spec> &win_specs)
List<Window_spec> &win_specs, List<Item_window_func> &win_funcs)
{
Window_spec *win_spec;
DBUG_ENTER("setup_windows");
@ -207,6 +207,14 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
}
}
}
List_iterator_fast<Item_window_func> li(win_funcs);
Item_window_func *win_func_item;
while ((win_func_item= li++))
{
win_func_item->update_used_tables();
}
DBUG_RETURN(0);
}
@ -1289,7 +1297,7 @@ bool compute_window_func_with_frames(Item_window_func *item_win,
Frame_cursor *top_bound;
Frame_cursor *bottom_bound;
Item_sum *sum_func= item_win->window_func;
Item_sum *sum_func= item_win->window_func();
/* This algorithm doesn't support DISTINCT aggregator */
sum_func->set_aggregator(Aggregator::SIMPLE_AGGREGATOR);
@ -1382,7 +1390,7 @@ bool compute_two_pass_window_functions(Item_window_func *item_win,
bool first_row= true;
clone_read_record(info, info2);
Item_sum_window_with_context *window_func=
static_cast<Item_sum_window_with_context *>(item_win->window_func);
static_cast<Item_sum_window_with_context *>(item_win->window_func());
uchar *rowid_buf= (uchar*) my_malloc(table->file->ref_length, MYF(0));
is_error= window_func->create_window_context();
@ -1653,7 +1661,7 @@ bool JOIN::process_window_functions(List<Item> *curr_fields_list)
item_win->setup_partition_border_check(thd);
Item_sum::Sumfunctype type= item_win->window_func->sum_func();
Item_sum::Sumfunctype type= item_win->window_func()->sum_func();
switch (type) {
case Item_sum::ROW_NUMBER_FUNC:
case Item_sum::RANK_FUNC:

View File

@ -5,6 +5,8 @@
#include "my_global.h"
#include "item.h"
class Item_window_func;
/*
Window functions module.
@ -130,6 +132,6 @@ class Window_def : public Window_spec
int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
List<Item> &fields, List<Item> &all_fields,
List<Window_spec> &win_specs);
List<Window_spec> &win_specs, List<Item_window_func> &win_funcs);
#endif /* SQL_WINDOW_INCLUDED */

View File

@ -10397,6 +10397,8 @@ window_func_expr:
$$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
if (Select->add_window_func((Item_window_func *) $$))
MYSQL_YYABORT;
}
|
window_func OVER_SYM window_spec
@ -10411,6 +10413,8 @@ window_func_expr:
thd->lex->win_spec);
if ($$ == NULL)
MYSQL_YYABORT;
if (Select->add_window_func((Item_window_func *) $$))
MYSQL_YYABORT;
}
;