summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/item_sum.h3
-rw-r--r--sql/item_windowfunc.cc218
-rw-r--r--sql/item_windowfunc.h152
-rw-r--r--sql/lex.h5
-rw-r--r--sql/sql_select.cc10
-rw-r--r--sql/sql_window.cc162
-rw-r--r--sql/sql_yacc.yy50
7 files changed, 531 insertions, 69 deletions
diff --git a/sql/item_sum.h b/sql/item_sum.h
index d7d583a6fed..b9075db0196 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -350,7 +350,8 @@ public:
AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC,
VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC,
ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC,
- CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC
+ CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC,
+ NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC
};
Item **ref_by; /* pointer to a ref to the object used to register it */
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index 5a7ee522d44..ccdbdd3294f 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -229,16 +229,220 @@ bool Item_sum_first_value::add()
For this usecase we can actually get rid of arg_cache. arg_cache is just
for running a comparison function. */
value_added= true;
- arg_cache->cache_value();
- value->store(arg_cache);
- null_value= arg_cache->null_value;
+ Item_sum_hybrid_simple::add();
return false;
}
-bool Item_sum_last_value::add()
+bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref)
{
- arg_cache->cache_value();
- value->store(arg_cache);
- null_value= arg_cache->null_value;
+ DBUG_ASSERT(fixed == 0);
+
+ if (init_sum_func_check(thd))
+ return TRUE;
+
+ for (uint i= 0; i < arg_count; i++)
+ {
+ Item *item= args[i];
+ // 'item' can be changed during fix_fields
+ if ((!item->fixed && item->fix_fields(thd, args)) ||
+ (item= args[i])->check_cols(1))
+ return TRUE;
+ }
+ Type_std_attributes::set(args[0]);
+ for (uint i= 0; i < arg_count && !with_subselect; i++)
+ with_subselect= with_subselect || args[i]->with_subselect;
+
+ Item *item2= args[0]->real_item();
+ if (item2->type() == Item::FIELD_ITEM)
+ set_handler_by_field_type(((Item_field*) item2)->field->type());
+ else if (args[0]->cmp_type() == TIME_RESULT)
+ set_handler_by_field_type(item2->field_type());
+ else
+ set_handler_by_result_type(item2->result_type(),
+ max_length, collation.collation);
+
+ switch (Item_sum_hybrid_simple::result_type()) {
+ case INT_RESULT:
+ case DECIMAL_RESULT:
+ case STRING_RESULT:
+ break;
+ case REAL_RESULT:
+ max_length= float_length(decimals);
+ break;
+ case ROW_RESULT:
+ case TIME_RESULT:
+ DBUG_ASSERT(0); // XXX(cvicentiu) Should this never happen?
+ return TRUE;
+ };
+ setup_hybrid(thd, args[0]);
+ /* MIN/MAX can return NULL for empty set indepedent of the used column */
+ maybe_null= 1;
+ result_field=0;
+ null_value=1;
+ fix_length_and_dec();
+
+ if (check_sum_func(thd, ref))
+ return TRUE;
+ for (uint i= 0; i < arg_count; i++)
+ {
+ orig_args[i]= args[i];
+ }
+ fixed= 1;
+ return FALSE;
+}
+
+bool Item_sum_hybrid_simple::add()
+{
+ value->store(args[0]);
+ value->cache_value();
+ null_value= value->null_value;
return false;
}
+
+void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item)
+{
+ if (!(value= Item_cache::get_cache(thd, item, item->cmp_type())))
+ return;
+ value->setup(thd, item);
+ value->store(item);
+ if (!item->const_item())
+ value->set_used_tables(RAND_TABLE_BIT);
+ collation.set(item->collation);
+}
+
+double Item_sum_hybrid_simple::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0.0;
+ double retval= value->val_real();
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == 0.0);
+ return retval;
+}
+
+longlong Item_sum_hybrid_simple::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0;
+ longlong retval= value->val_int();
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == 0);
+ return retval;
+}
+
+my_decimal *Item_sum_hybrid_simple::val_decimal(my_decimal *val)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0;
+ my_decimal *retval= value->val_decimal(val);
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == NULL);
+ return retval;
+}
+
+String *
+Item_sum_hybrid_simple::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0;
+ String *retval= value->val_str(str);
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == NULL);
+ return retval;
+}
+
+Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
+{
+ DBUG_ASSERT(0);
+ return NULL;
+}
+
+void Item_sum_hybrid_simple::reset_field()
+{
+ switch(Item_sum_hybrid_simple::result_type()) {
+ case STRING_RESULT:
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff),result_field->charset()),*res;
+
+ res=args[0]->val_str(&tmp);
+ if (args[0]->null_value)
+ {
+ result_field->set_null();
+ result_field->reset();
+ }
+ else
+ {
+ result_field->set_notnull();
+ result_field->store(res->ptr(),res->length(),tmp.charset());
+ }
+ break;
+ }
+ case INT_RESULT:
+ {
+ longlong nr=args[0]->val_int();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr, unsigned_flag);
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double nr= args[0]->val_real();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0.0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr);
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff);
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ result_field->set_null();
+ else
+ result_field->set_notnull();
+ }
+ /*
+ We must store zero in the field as we will use the field value in
+ add()
+ */
+ if (!arg_dec) // Null
+ arg_dec= &decimal_zero;
+ result_field->store_decimal(arg_dec);
+ break;
+ }
+ case ROW_RESULT:
+ case TIME_RESULT:
+ DBUG_ASSERT(0);
+ }
+}
+
+void Item_sum_hybrid_simple::update_field()
+{
+ DBUG_ASSERT(0);
+}
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index cf484e3129d..b3d0b7a15f3 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -279,23 +279,59 @@ class Item_sum_dense_rank: public Item_sum_int
{ return get_item_copy<Item_sum_dense_rank>(thd, mem_root, this); }
};
+class Item_sum_hybrid_simple : public Item_sum,
+ public Type_handler_hybrid_field_type
+{
+ public:
+ Item_sum_hybrid_simple(THD *thd, Item *arg):
+ Item_sum(thd, arg),
+ Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG),
+ value(NULL)
+ { collation.set(&my_charset_bin); }
+
+ Item_sum_hybrid_simple(THD *thd, Item *arg1, Item *arg2):
+ Item_sum(thd, arg1, arg2),
+ Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG),
+ value(NULL)
+ { collation.set(&my_charset_bin); }
+
+ bool add();
+ bool fix_fields(THD *, Item **);
+ void setup_hybrid(THD *thd, Item *item);
+ double val_real();
+ longlong val_int();
+ my_decimal *val_decimal(my_decimal *);
+ void reset_field();
+ String *val_str(String *);
+ /* TODO(cvicentiu) copied from Item_sum_hybrid, what does it do? */
+ bool keep_field_type(void) const { return 1; }
+ enum Item_result result_type() const
+ { return Type_handler_hybrid_field_type::result_type(); }
+ enum Item_result cmp_type() const
+ { return Type_handler_hybrid_field_type::cmp_type(); }
+ enum enum_field_types field_type() const
+ { return Type_handler_hybrid_field_type::field_type(); }
+ void update_field();
+ Field *create_tmp_field(bool group, TABLE *table);
+ void clear()
+ {
+ value->clear();
+ null_value= 1;
+ }
+
+ private:
+ Item_cache *value;
+};
+
/*
This item will remember the first value added to it. It will not update
the value unless it is cleared.
-
- TODO(cvicentiu) Item_sum_hybrid is a pretty heavyweight class. It holds
- logic that allows comparing values. It was generally thought out for MIN/MAX
- functions, but we can use it here as well.
- Refactor Item_sum_hybrid to only include basic field handling and
- make a more specialized class for min/max. It might be useful if we'd like
- to optimize how min/max is computed as a window function. We can potentially
- implement a PQ within the specialized class to support removal.
*/
-class Item_sum_first_value : public Item_sum_hybrid
+class Item_sum_first_value : public Item_sum_hybrid_simple
{
public:
Item_sum_first_value(THD* thd, Item* arg_expr) :
- Item_sum_hybrid(thd, arg_expr, -1 /* This cmp parameter is not needed */),
+ Item_sum_hybrid_simple(thd, arg_expr),
value_added(false) {}
bool add();
@@ -303,7 +339,7 @@ class Item_sum_first_value : public Item_sum_hybrid
void clear()
{
value_added= false;
- Item_sum_hybrid::clear();
+ Item_sum_hybrid_simple::clear();
}
enum Sumfunctype sum_func () const
@@ -329,14 +365,13 @@ class Item_sum_first_value : public Item_sum_hybrid
This item does not support removal, and can be cleared only by calling
clear().
*/
-class Item_sum_last_value : public Item_sum_hybrid
+class Item_sum_last_value : public Item_sum_hybrid_simple
{
public:
Item_sum_last_value(THD* thd, Item* arg_expr) :
- Item_sum_hybrid(thd, arg_expr, -1 /* This cmp parameter is not needed */) {}
+ Item_sum_hybrid_simple(thd, arg_expr) {}
- bool add();
- enum Sumfunctype sum_func () const
+ enum Sumfunctype sum_func() const
{
return LAST_VALUE_FUNC;
}
@@ -350,6 +385,91 @@ class Item_sum_last_value : public Item_sum_hybrid
{ return get_item_copy<Item_sum_last_value>(thd, mem_root, this); }
};
+class Item_sum_nth_value : public Item_sum_last_value
+{
+ public:
+ Item_sum_nth_value(THD *thd, Item *arg_expr, Item* offset_expr) :
+ Item_sum_last_value(thd, arg_expr) {
+ /* TODO(cvicentiu) This is messy. Item_args starts with 2 args by chance.
+ Clean this up by pulling out the common code from Item_sum_hybrid! */
+ arg_count= 2;
+ args[1]= offset_expr;
+ }
+
+ bool fix_fields(THD *thd, Item **ref)
+ {
+ Item *offset= args[1];
+ if (offset->fix_fields(thd, args))
+ return true;
+ /* Fix fields for the second argument as well. */
+ orig_args[1]= offset;
+ /* Item_sum_last_value fixes fields for first argument only. */
+ if (Item_sum_last_value::fix_fields(thd, ref))
+ return true;
+
+ return false;
+ }
+
+ bool add()
+ {
+ Item_sum_last_value::add();
+ return false;
+ }
+
+ enum Sumfunctype sum_func() const
+ {
+ return NTH_VALUE_FUNC;
+ }
+
+ const char*func_name() const
+ {
+ return "nth_value";
+ }
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_nth_value>(thd, mem_root, this); }
+};
+
+class Item_sum_lead : public Item_sum_hybrid_simple
+{
+ public:
+ Item_sum_lead(THD *thd, Item *arg_expr, Item* offset_expr) :
+ Item_sum_hybrid_simple(thd, arg_expr, offset_expr) {}
+
+ enum Sumfunctype sum_func() const
+ {
+ return LEAD_FUNC;
+ }
+
+ const char*func_name() const
+ {
+ return "lead";
+ }
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_lead>(thd, mem_root, this); }
+};
+
+class Item_sum_lag : public Item_sum_hybrid_simple
+{
+ public:
+ Item_sum_lag(THD *thd, Item *arg_expr, Item* offset_expr) :
+ Item_sum_hybrid_simple(thd, arg_expr, offset_expr) {}
+
+ enum Sumfunctype sum_func() const
+ {
+ return LAG_FUNC;
+ }
+
+ const char*func_name() const
+ {
+ return "lag";
+ }
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_lag>(thd, mem_root, this); }
+};
+
/*
A base window function (aggregate) that also holds a counter for the number
of rows.
@@ -668,6 +788,8 @@ public:
switch (window_func()->sum_func()) {
case Item_sum::FIRST_VALUE_FUNC:
case Item_sum::LAST_VALUE_FUNC:
+ case Item_sum::LAG_FUNC:
+ case Item_sum::LEAD_FUNC:
return true;
default:
return false;
diff --git a/sql/lex.h b/sql/lex.h
index 90dac2eb294..003e88d2d9d 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -697,11 +697,14 @@ static SYMBOL sql_functions[] = {
{ "EXTRACT", SYM(EXTRACT_SYM)},
{ "FIRST_VALUE", SYM(FIRST_VALUE_SYM)},
{ "GROUP_CONCAT", SYM(GROUP_CONCAT_SYM)},
+ { "LAG", SYM(LAG_SYM)},
+ { "LEAD", SYM(LEAD_SYM)},
{ "MAX", SYM(MAX_SYM)},
{ "MID", SYM(SUBSTRING)}, /* unireg function */
{ "MIN", SYM(MIN_SYM)},
{ "NOW", SYM(NOW_SYM)},
- { "NTILE", SYM(NTILE_SYM)},
+ { "NTH_VALUE", SYM(NTH_VALUE_SYM)},
+ { "NTILE", SYM(NTILE_SYM)},
{ "POSITION", SYM(POSITION_SYM)},
{ "PERCENT_RANK", SYM(PERCENT_RANK_SYM)},
{ "RANK", SYM(RANK_SYM)},
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 761f6c4b130..1f79010e993 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -26243,7 +26243,15 @@ AGGR_OP::end_send()
corresponding temp table fields. Do this for each row in the table.
*/
if (join_tab->window_funcs_step)
- copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd);
+ {
+ Item **func_ptr= join_tab->tmp_table_param->items_to_copy;
+ Item *func;
+ for (; (func = *func_ptr) ; func_ptr++)
+ {
+ if (func->with_window_func)
+ func->save_in_result_field(true);
+ }
+ }
rc= evaluate_join_record(join, join_tab, 0);
}
}
diff --git a/sql/sql_window.cc b/sql/sql_window.cc
index 15045924e83..1a111e52c75 100644
--- a/sql/sql_window.cc
+++ b/sql/sql_window.cc
@@ -273,7 +273,7 @@ int compare_order_lists(SQL_I_List<ORDER> *part_list1,
return CMP_GT_C;
if (elem2)
return CMP_LT_C;
- return CMP_EQ;
+ return CMP_EQ;
}
@@ -686,7 +686,17 @@ public:
if ((res= Table_read_cursor::next()) ||
(res= fetch()))
+ {
+ /* TODO(cvicentiu) This does not consider table read failures.
+ Perhaps assuming end of table like this is fine in that case. */
+
+ /* This row is the final row in the table. To maintain semantics
+ that cursors always point to the last valid row, move back one step,
+ but mark end_of_partition as true. */
+ Table_read_cursor::prev();
+ end_of_partition= true;
return res;
+ }
if (bound_tracker.compare_with_cache())
{
@@ -1886,19 +1896,25 @@ private:
/* A cursor that follows a target cursor. Each time a new row is added,
the window functions are cleared and only have the row at which the target
is point at added to them.
+
+ The window functions are cleared if the bounds or the position cursors are
+ outside computational bounds.
*/
class Frame_positional_cursor : public Frame_cursor
{
public:
Frame_positional_cursor(const Frame_cursor &position_cursor) :
- position_cursor(position_cursor), bound(NULL), offset(NULL),
+ position_cursor(position_cursor), top_bound(NULL),
+ bottom_bound(NULL), offset(NULL), overflowed(false),
negative_offset(false) {}
Frame_positional_cursor(const Frame_cursor &position_cursor,
- const Frame_cursor &bound,
+ const Frame_cursor &top_bound,
+ const Frame_cursor &bottom_bound,
Item &offset,
bool negative_offset) :
- position_cursor(position_cursor), bound(&bound), offset(&offset),
+ position_cursor(position_cursor), top_bound(&top_bound),
+ bottom_bound(&bottom_bound), offset(&offset),
negative_offset(negative_offset) {}
void init(READ_RECORD *info)
@@ -1908,35 +1924,26 @@ class Frame_positional_cursor : public Frame_cursor
void pre_next_partition(ha_rows rownum)
{
- clear_sum_functions();
+ /* The offset is dependant on the current row values. We can only get
+ * it here accurately. When fetching other rows, it changes. */
+ save_offset_value();
}
void next_partition(ha_rows rownum)
{
- ha_rows position= get_current_position();
- if (position_is_within_bounds(position))
- {
- cursor.move_to(position);
- cursor.fetch();
- add_value_to_items();
- }
+ save_positional_value();
}
void pre_next_row()
{
+ /* The offset is dependant on the current row values. We can only get
+ * it here accurately. When fetching other rows, it changes. */
+ save_offset_value();
}
void next_row()
{
- ha_rows position= get_current_position();
- if (!position_is_within_bounds(position))
- clear_sum_functions();
- else
- {
- cursor.move_to(position_cursor.get_curr_rownum());
- cursor.fetch();
- add_value_to_items();
- }
+ save_positional_value();
}
ha_rows get_curr_rownum() const
@@ -1947,30 +1954,25 @@ class Frame_positional_cursor : public Frame_cursor
private:
/* Check if a our position is within bounds.
* The position is passed as a parameter to avoid recalculating it. */
- bool position_is_within_bounds(ha_rows position)
+ bool position_is_within_bounds()
{
if (!offset)
return !position_cursor.is_outside_computation_bounds();
+ if (overflowed)
+ return false;
+
/* No valid bound to compare to. */
if (position_cursor.is_outside_computation_bounds() ||
- bound->is_outside_computation_bounds())
+ top_bound->is_outside_computation_bounds() ||
+ bottom_bound->is_outside_computation_bounds())
return false;
- if (negative_offset)
- {
- if (position_cursor.get_curr_rownum() < position)
- return false; /* Overflow below 0. */
- if (position < bound->get_curr_rownum()) /* We are over the bound. */
- return false;
- }
- else
- {
- if (position_cursor.get_curr_rownum() > position)
- return false; /* Overflow over MAX_HA_ROWS. */
- if (position > bound->get_curr_rownum()) /* We are over the bound. */
- return false;
- }
+ /* We are over the bound. */
+ if (position < top_bound->get_curr_rownum())
+ return false;
+ if (position > bottom_bound->get_curr_rownum())
+ return false;
return true;
}
@@ -1978,18 +1980,55 @@ private:
/* Get the current position, accounting for the offset value, if present.
NOTE: This function does not check over/underflow.
*/
- ha_rows get_current_position()
+ void get_current_position()
{
- ha_rows position = position_cursor.get_curr_rownum();
+ position = position_cursor.get_curr_rownum();
+ overflowed= false;
if (offset)
- position += offset->val_int() * (negative_offset ? -1 : 1);
- return position;
+ {
+ if (offset_value < 0 &&
+ position + offset_value > position)
+ {
+ overflowed= true;
+ }
+ if (offset_value > 0 &&
+ position + offset_value < position)
+ {
+ overflowed= true;
+ }
+ position += offset_value;
+ }
+ }
+
+ void save_offset_value()
+ {
+ if (offset)
+ offset_value= offset->val_int() * (negative_offset ? -1 : 1);
+ else
+ offset_value= 0;
+ }
+
+ void save_positional_value()
+ {
+ get_current_position();
+ if (!position_is_within_bounds())
+ clear_sum_functions();
+ else
+ {
+ cursor.move_to(position);
+ cursor.fetch();
+ add_value_to_items();
+ }
}
const Frame_cursor &position_cursor;
- const Frame_cursor *bound;
+ const Frame_cursor *top_bound;
+ const Frame_cursor *bottom_bound;
Item *offset;
Table_read_cursor cursor;
+ ha_rows position;
+ longlong offset_value;
+ bool overflowed;
bool negative_offset;
};
@@ -2107,6 +2146,7 @@ void add_special_frame_cursors(THD *thd, Cursor_manager *cursor_manager,
{
Window_spec *spec= window_func->window_spec;
Item_sum *item_sum= window_func->window_func();
+ DBUG_PRINT("info", ("Get arg count: %d", item_sum->get_arg_count()));
Frame_cursor *fc;
switch (item_sum->sum_func())
{
@@ -2135,6 +2175,44 @@ void add_special_frame_cursors(THD *thd, Cursor_manager *cursor_manager,
fc->add_sum_func(item_sum);
cursor_manager->add_cursor(fc);
break;
+ case Item_sum::LEAD_FUNC:
+ case Item_sum::LAG_FUNC:
+ {
+ Frame_cursor *bottom_bound= new Frame_unbounded_following(thd,
+ spec->partition_list,
+ spec->order_list);
+ Frame_cursor *top_bound= new Frame_unbounded_preceding(thd,
+ spec->partition_list,
+ spec->order_list);
+ Frame_cursor *current_row_pos= new Frame_rows_current_row_bottom;
+ cursor_manager->add_cursor(bottom_bound);
+ cursor_manager->add_cursor(top_bound);
+ cursor_manager->add_cursor(current_row_pos);
+ DBUG_ASSERT(item_sum->fixed);
+ bool negative_offset= item_sum->sum_func() == Item_sum::LAG_FUNC;
+ fc= new Frame_positional_cursor(*current_row_pos,
+ *top_bound, *bottom_bound,
+ *item_sum->get_arg(1),
+ negative_offset);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+ break;
+ }
+ case Item_sum::NTH_VALUE_FUNC:
+ {
+ Frame_cursor *bottom_bound= get_frame_cursor(thd, spec, false);
+ Frame_cursor *top_bound= get_frame_cursor(thd, spec, true);
+ cursor_manager->add_cursor(bottom_bound);
+ cursor_manager->add_cursor(top_bound);
+ DBUG_ASSERT(item_sum->fixed);
+ fc= new Frame_positional_cursor(*top_bound,
+ *top_bound, *bottom_bound,
+ *item_sum->get_arg(1),
+ false);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+ break;
+ }
default:
fc= new Frame_unbounded_preceding(
thd, spec->partition_list, spec->order_list);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0d6bccf159c..b833ade350f 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1245,7 +1245,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FAULTS_SYM
%token FETCH_SYM /* SQL-2003-R */
%token FILE_SYM
-%token FIRST_VALUE_SYM /* SQL-2012 */
+%token FIRST_VALUE_SYM /* SQL-2011 */
%token FIRST_SYM /* SQL-2003-N */
%token FIXED_SYM
%token FLOAT_NUM
@@ -1273,6 +1273,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token GRANTS
%token GROUP_SYM /* SQL-2003-R */
%token GROUP_CONCAT_SYM
+%token LAG_SYM /* SQL-2011 */
+%token LEAD_SYM /* SQL-2011 */
%token HANDLER_SYM
%token HARD_SYM
%token HASH_SYM
@@ -1431,6 +1433,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token NUM
%token NUMBER_SYM /* SQL-2003-N */
%token NUMERIC_SYM /* SQL-2003-R */
+%token NTH_VALUE_SYM /* SQL-2011 */
%token NVARCHAR_SYM
%token OFFSET_SYM
%token OLD_PASSWORD_SYM
@@ -10518,7 +10521,50 @@ simple_window_func:
if ($$ == NULL)
MYSQL_YYABORT;
}
- ;
+ |
+ NTH_VALUE_SYM '(' expr ',' expr ')'
+ {
+ $$= new (thd->mem_root) Item_sum_nth_value(thd, $3, $5);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ |
+ LEAD_SYM '(' expr ')'
+ {
+ /* No second argument defaults to 1. */
+ Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
+ if (item_offset == NULL)
+ MYSQL_YYABORT;
+ $$= new (thd->mem_root) Item_sum_lead(thd, $3, item_offset);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ |
+ LEAD_SYM '(' expr ',' expr ')'
+ {
+ $$= new (thd->mem_root) Item_sum_lead(thd, $3, $5);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ |
+ LAG_SYM '(' expr ')'
+ {
+ /* No second argument defaults to 1. */
+ Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
+ if (item_offset == NULL)
+ MYSQL_YYABORT;
+ $$= new (thd->mem_root) Item_sum_lag(thd, $3, item_offset);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ |
+ LAG_SYM '(' expr ',' expr ')'
+ {
+ $$= new (thd->mem_root) Item_sum_lag(thd, $3, $5);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
window_name:
ident