diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 34bf7a30c3d..8d886249e2b 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -1177,6 +1177,44 @@ pk c cnt 9 2 0 10 2 0 drop table t0, t1; +# +# Error checking for frame bounds +# +create table t1 (a int, b int, c varchar(32)); +insert into t1 values (1,1,'foo'); +insert into t1 values (2,2,'bar'); +select +count(*) over (order by a,b +range between unbounded preceding and current row) +from t1; +ERROR HY000: RANGE-type frame requires ORDER BY clause with single sort key +select +count(*) over (order by c +range between unbounded preceding and current row) +from t1; +ERROR HY000: Numeric datatype is required for RANGE-type frame +select +count(*) over (order by a +range between 'abcd' preceding and current row) +from t1; +ERROR HY000: Numeric datatype is required for RANGE-type frame +select +count(*) over (order by a +range between current row and 'foo' following) +from t1; +ERROR HY000: Numeric datatype is required for RANGE-type frame +# Try range frame with invalid bounds +select +count(*) over (order by a +rows between 0.5 preceding and current row) +from t1; +ERROR HY000: Integer is required for ROWS-type frame +select +count(*) over (order by a +rows between current row and 3.14 following) +from t1; +ERROR HY000: Integer is required for ROWS-type frame +drop table t1; # # Window function in grouping query # diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index 182e4715538..27e6e7244db 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -697,6 +697,51 @@ select from t1; drop table t0, t1; +--echo # +--echo # Error checking for frame bounds +--echo # + +create table t1 (a int, b int, c varchar(32)); +insert into t1 values (1,1,'foo'); +insert into t1 values (2,2,'bar'); +--error ER_RANGE_FRAME_NEEDS_SIMPLE_ORDERBY +select + count(*) over (order by a,b + range between unbounded preceding and current row) +from t1; + +--error ER_WRONG_TYPE_FOR_RANGE_FRAME +select + count(*) over (order by c + range between unbounded preceding and current row) +from t1; + +--error ER_WRONG_TYPE_FOR_RANGE_FRAME +select + count(*) over (order by a + range between 'abcd' preceding and current row) +from t1; + +--error ER_WRONG_TYPE_FOR_RANGE_FRAME +select + count(*) over (order by a + range between current row and 'foo' following) +from t1; + +--echo # Try range frame with invalid bounds +--error ER_WRONG_TYPE_FOR_ROWS_FRAME +select + count(*) over (order by a + rows between 0.5 preceding and current row) +from t1; + +--error ER_WRONG_TYPE_FOR_ROWS_FRAME +select + count(*) over (order by a + rows between current row and 3.14 following) +from t1; + +drop table t1; --echo # --echo # Window function in grouping query @@ -742,3 +787,8 @@ drop table t1; + + + + + diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 01c90e6be53..b1614930ab1 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7148,4 +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_RANGE_FRAME_NEEDS_SIMPLE_ORDERBY + eng "RANGE-type frame requires ORDER BY clause with single sort key" +ER_WRONG_TYPE_FOR_ROWS_FRAME + eng "Integer is required for ROWS-type frame" +ER_WRONG_TYPE_FOR_RANGE_FRAME + eng "Numeric datatype is required for RANGE-type frame" diff --git a/sql/sql_window.cc b/sql/sql_window.cc index f09241c3ff4..0420ca33b06 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -76,6 +76,10 @@ Window_frame::check_frame_bounds() } +/* + Setup window functions in a select +*/ + int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, List &fields, List &all_fields, @@ -122,6 +126,80 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, { DBUG_RETURN(1); } + + /* + For "win_func() OVER (ORDER BY order_list RANGE BETWEEN ...)", + - ORDER BY order_list must not be ommitted + - the list must have a single element. + */ + if (win_spec->window_frame && + win_spec->window_frame->units == Window_frame::UNITS_RANGE) + { + if (!win_spec->order_list || win_spec->order_list->elements != 1) + { + my_error(ER_RANGE_FRAME_NEEDS_SIMPLE_ORDERBY, MYF(0)); + DBUG_RETURN(1); + } + + /* + "The declared type of SK shall be numeric, datetime, or interval" + we don't support datetime or interval, yet. + */ + Item_result rtype= win_spec->order_list->first->item[0]->result_type(); + if (rtype != REAL_RESULT && rtype != INT_RESULT && + rtype != DECIMAL_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_RANGE_FRAME, MYF(0)); + DBUG_RETURN(1); + } + + /* + "The declared type of UVS shall be numeric if the declared type of SK + is numeric; otherwise, it shall be an interval type that may be added + to or subtracted from the declared type of SK" + */ + Window_frame_bound *bounds[]= {win_spec->window_frame->top_bound, + win_spec->window_frame->bottom_bound, + NULL}; + for (Window_frame_bound **pbound= &bounds[0]; *pbound; pbound++) + { + if (!(*pbound)->is_unbounded() && + ((*pbound)->precedence_type == Window_frame_bound::FOLLOWING || + (*pbound)->precedence_type == Window_frame_bound::PRECEDING)) + { + Item_result rtype= (*pbound)->offset->result_type(); + if (rtype != REAL_RESULT && rtype != INT_RESULT && + rtype != DECIMAL_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_RANGE_FRAME, MYF(0)); + DBUG_RETURN(1); + } + } + } + } + + /* "ROWS PRECEDING|FOLLOWING $n" must have a numeric $n */ + if (win_spec->window_frame && + win_spec->window_frame->units == Window_frame::UNITS_ROWS) + { + Window_frame_bound *bounds[]= {win_spec->window_frame->top_bound, + win_spec->window_frame->bottom_bound, + NULL}; + for (Window_frame_bound **pbound= &bounds[0]; *pbound; pbound++) + { + if (!(*pbound)->is_unbounded() && + ((*pbound)->precedence_type == Window_frame_bound::FOLLOWING || + (*pbound)->precedence_type == Window_frame_bound::PRECEDING)) + { + Item *offset= (*pbound)->offset; + if (offset->result_type() != INT_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_ROWS_FRAME, MYF(0)); + DBUG_RETURN(1); + } + } + } + } } DBUG_RETURN(0); } @@ -1123,6 +1201,9 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound) if (frame->units == Window_frame::UNITS_ROWS) { longlong n_rows= bound->offset->val_int(); + /* These should be handled in the parser */ + DBUG_ASSERT(!bound->offset->null_value); + DBUG_ASSERT(n_rows > 0); if (is_preceding) return new Frame_n_rows_preceding(is_top_bound, n_rows); else