<div dir="ltr">Hi Sergey,<div><br></div><div>This is the patch that performs the refactoring for computing multiple window functions in a single scan.</div><div><br></div><div>I know the patch is large, the main points changed are the following:</div><div><br></div><div>1. Frame_cursors are now initialized at construction. The init function just sets the READ_RECORD.</div><div>2. We no longer have a partition tracker within Item_window_func. Instead we create it during the values computation stage.</div><div>3. Cursor_manager is a new class that owns cursors belonging to 1 window function. Through this class (and only this class) we advance the cursors.</div><div>4. Window_func_runner deals with computing all the window function values belonging to a Window_funcs_sort.</div><div><br></div><div>I recommend you start with Window_func_runner::exec as that's where everything begins to branch out.</div><div><br></div><div>Vicentiu</div><div><br></div><div><br></div><div><div class="gmail_quote"><div dir="ltr">On Tue, 6 Sep 2016 at 19:51 vicentiu <<a href="mailto:vicentiu@mariadb.org">vicentiu@mariadb.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">revision-id: 78d3bd5e0b8827d21c4fe907aa56dcda85d7dd89 (mariadb-10.2.1-21-g78d3bd5)<br>
parent(s): 31a8cf54c8a7913338480a0571feaf32143b5f64<br>
author: Vicențiu Ciorbaru<br>
committer: Vicențiu Ciorbaru<br>
timestamp: 2016-09-06 19:50:50 +0300<br>
message:<br>
<br>
MDEV-10059: Compute window functions with same sorting criteria simultaneously<br>
<br>
Perform only one table scan for each window function present. We do this<br>
by keeping keeping cursors for each window function frame bound and<br>
running them for each function for every row.<br>
<br>
---<br>
 sql/item_windowfunc.cc |  43 +--<br>
 sql/item_windowfunc.h  |  90 ++---<br>
 sql/sql_window.cc      | 884 +++++++++++++++++++++++++++++--------------------<br>
 sql/sql_window.h       |  37 +--<br>
 4 files changed, 607 insertions(+), 447 deletions(-)<br>
<br>
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc<br>
index d157d54..c8ea979 100644<br>
--- a/sql/item_windowfunc.cc<br>
+++ b/sql/item_windowfunc.cc<br>
@@ -41,7 +41,7 @@ Item_window_func::resolve_window_name(THD *thd)<br>
     return true;<br>
   }<br>
<br>
-  return false;<br>
+  return false;<br>
 }<br>
<br>
<br>
@@ -154,7 +154,7 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,<br>
<br>
<br>
 /*<br>
-  This must be called before advance_window() can be called.<br>
+  This must be called before attempting to compute the window function values.<br>
<br>
   @detail<br>
     If we attempt to do it in fix_fields(), partition_fields will refer<br>
@@ -162,30 +162,25 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,<br>
     We need it to refer to temp.table columns.<br>
 */<br>
<br>
-void Item_window_func::setup_partition_border_check(THD *thd)<br>
-{<br>
-  partition_tracker.init(thd, window_spec->partition_list);<br>
-  window_func()->setup_window_func(thd, window_spec);<br>
-}<br>
-<br>
-<br>
 void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)<br>
 {<br>
   /* TODO: move this into Item_window_func? */<br>
-  peer_tracker.init(thd, window_spec->order_list);<br>
+  peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);<br>
+  peer_tracker->init();<br>
   clear();<br>
 }<br>
<br>
 void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)<br>
 {<br>
   /* TODO: consider moving this && Item_sum_rank's implementation */<br>
-  peer_tracker.init(thd, window_spec->order_list);<br>
+  peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);<br>
+  peer_tracker->init();<br>
   clear();<br>
 }<br>
<br>
 bool Item_sum_dense_rank::add()<br>
 {<br>
-  if (peer_tracker.check_if_next_group() || first_add)<br>
+  if (peer_tracker->check_if_next_group() || first_add)<br>
   {<br>
     first_add= false;<br>
     dense_rank++;<br>
@@ -198,7 +193,7 @@ bool Item_sum_dense_rank::add()<br>
 bool Item_sum_rank::add()<br>
 {<br>
   row_number++;<br>
-  if (peer_tracker.check_if_next_group())<br>
+  if (peer_tracker->check_if_next_group())<br>
   {<br>
     /* Row value changed */<br>
     cur_rank= row_number;<br>
@@ -206,25 +201,10 @@ bool Item_sum_rank::add()<br>
   return false;<br>
 }<br>
<br>
-bool Item_window_func::check_if_partition_changed()<br>
-{<br>
-  return partition_tracker.check_if_next_group();<br>
-}<br>
-<br>
-void Item_window_func::advance_window()<br>
-{<br>
-  if (check_if_partition_changed())<br>
-  {<br>
-    /* Next partition */<br>
-    window_func()->clear();<br>
-  }<br>
-  window_func()->add();<br>
-}<br>
-<br>
 bool Item_sum_percent_rank::add()<br>
 {<br>
   row_number++;<br>
-  if (peer_tracker.check_if_next_group())<br>
+  if (peer_tracker->check_if_next_group())<br>
   {<br>
     /* Row value changed. */<br>
     cur_rank= row_number;<br>
@@ -235,8 +215,7 @@ bool Item_sum_percent_rank::add()<br>
 void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)<br>
 {<br>
   /* TODO: move this into Item_window_func? */<br>
-  peer_tracker.init(thd, window_spec->order_list);<br>
+  peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);<br>
+  peer_tracker->init();<br>
   clear();<br>
 }<br>
-<br>
-<br>
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h<br>
index 9d2fa13..5b352a4 100644<br>
--- a/sql/item_windowfunc.h<br>
+++ b/sql/item_windowfunc.h<br>
@@ -12,25 +12,19 @@ int test_if_group_changed(List<Cached_item> &list);<br>
 /* A wrapper around test_if_group_changed */<br>
 class Group_bound_tracker<br>
 {<br>
-  List<Cached_item> group_fields;<br>
-  /*<br>
-    During the first check_if_next_group, the list of cached_items is not<br>
-    initialized. The compare function will return that the items match if<br>
-    the field's value is the same as the Cached_item's default value (0).<br>
-    This flag makes sure that we always return true during the first check.<br>
-<br>
-    XXX This is better to be implemented within test_if_group_changed, but<br>
-    since it is used in other parts of the codebase, we keep it here for now.<br>
-  */<br>
-   bool first_check;<br>
 public:<br>
-  void init(THD *thd, SQL_I_List<ORDER> *list)<br>
+<br>
+  Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list)<br>
   {<br>
     for (ORDER *curr = list->first; curr; curr=curr->next)<br>
     {<br>
       Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);<br>
       group_fields.push_back(tmp);<br>
     }<br>
+  }<br>
+<br>
+  void init()<br>
+  {<br>
     first_check= true;<br>
   }<br>
<br>
@@ -76,6 +70,19 @@ class Group_bound_tracker<br>
     }<br>
     return 0;<br>
   }<br>
+<br>
+private:<br>
+  List<Cached_item> group_fields;<br>
+  /*<br>
+    During the first check_if_next_group, the list of cached_items is not<br>
+    initialized. The compare function will return that the items match if<br>
+    the field's value is the same as the Cached_item's default value (0).<br>
+    This flag makes sure that we always return true during the first check.<br>
+<br>
+    XXX This is better to be implemented within test_if_group_changed, but<br>
+    since it is used in other parts of the codebase, we keep it here for now.<br>
+  */<br>
+   bool first_check;<br>
 };<br>
<br>
 /*<br>
@@ -92,19 +99,22 @@ class Item_sum_row_number: public Item_sum_int<br>
   longlong count;<br>
<br>
 public:<br>
+<br>
+  Item_sum_row_number(THD *thd)<br>
+    : Item_sum_int(thd),  count(0) {}<br>
+<br>
   void clear()<br>
   {<br>
     count= 0;<br>
   }<br>
-  bool add()<br>
+<br>
+  bool add()<br>
   {<br>
     count++;<br>
-    return false;<br>
+    return false;<br>
   }<br>
-  void update_field() {}<br>
<br>
-  Item_sum_row_number(THD *thd)<br>
-    : Item_sum_int(thd),  count(0) {}<br>
+  void update_field() {}<br>
<br>
   enum Sumfunctype sum_func() const<br>
   {<br>
@@ -119,7 +129,6 @@ class Item_sum_row_number: public Item_sum_int<br>
   {<br>
     return "row_number(";<br>
   }<br>
-<br>
 };<br>
<br>
<br>
@@ -145,9 +154,12 @@ class Item_sum_rank: public Item_sum_int<br>
 protected:<br>
   longlong row_number; // just ROW_NUMBER()<br>
   longlong cur_rank;   // current value<br>
-<br>
-  Group_bound_tracker peer_tracker;<br>
+<br>
+  Group_bound_tracker *peer_tracker;<br>
 public:<br>
+<br>
+  Item_sum_rank(THD *thd) : Item_sum_int(thd), peer_tracker(NULL) {}<br>
+<br>
   void clear()<br>
   {<br>
     /* This is called on partition start */<br>
@@ -168,10 +180,6 @@ class Item_sum_rank: public Item_sum_int<br>
     TODO: ^^ what does this do ? It is not called ever?<br>
   */<br>
<br>
-public:<br>
-  Item_sum_rank(THD *thd)<br>
-    : Item_sum_int(thd) {}<br>
-<br>
   enum Sumfunctype sum_func () const<br>
   {<br>
     return RANK_FUNC;<br>
@@ -183,9 +191,12 @@ class Item_sum_rank: public Item_sum_int<br>
   }<br>
<br>
   void setup_window_func(THD *thd, Window_spec *window_spec);<br>
+<br>
   void cleanup()<br>
   {<br>
-    peer_tracker.cleanup();<br>
+    if (peer_tracker)<br>
+      peer_tracker->cleanup();<br>
+    delete peer_tracker;<br>
     Item_sum_int::cleanup();<br>
   }<br>
 };<br>
@@ -214,7 +225,7 @@ class Item_sum_dense_rank: public Item_sum_int<br>
 {<br>
   longlong dense_rank;<br>
   bool first_add;<br>
-  Group_bound_tracker peer_tracker;<br>
+  Group_bound_tracker *peer_tracker;<br>
  public:<br>
   /*<br>
      XXX(cvicentiu) This class could potentially be implemented in the rank<br>
@@ -233,7 +244,7 @@ class Item_sum_dense_rank: public Item_sum_int<br>
   }<br>
<br>
   Item_sum_dense_rank(THD *thd)<br>
-    : Item_sum_int(thd), dense_rank(0), first_add(true) {}<br>
+    : Item_sum_int(thd), dense_rank(0), first_add(true), peer_tracker(NULL) {}<br>
   enum Sumfunctype sum_func () const<br>
   {<br>
     return DENSE_RANK_FUNC;<br>
@@ -248,7 +259,11 @@ class Item_sum_dense_rank: public Item_sum_int<br>
<br>
   void cleanup()<br>
   {<br>
-    peer_tracker.cleanup();<br>
+    if (peer_tracker)<br>
+    {<br>
+      peer_tracker->cleanup();<br>
+      delete peer_tracker;<br>
+    }<br>
     Item_sum_int::cleanup();<br>
   }<br>
 };<br>
@@ -289,7 +304,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count<br>
 {<br>
  public:<br>
   Item_sum_percent_rank(THD *thd)<br>
-    : Item_sum_window_with_row_count(thd), cur_rank(1) {}<br>
+    : Item_sum_window_with_row_count(thd), cur_rank(1), peer_tracker(NULL) {}<br>
<br>
   longlong val_int()<br>
   {<br>
@@ -347,11 +362,15 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count<br>
   longlong cur_rank;   // Current rank of the current row.<br>
   longlong row_number; // Value if this were ROW_NUMBER() function.<br>
<br>
-  Group_bound_tracker peer_tracker;<br>
+  Group_bound_tracker *peer_tracker;<br>
<br>
   void cleanup()<br>
   {<br>
-    peer_tracker.cleanup();<br>
+    if (peer_tracker)<br>
+    {<br>
+      peer_tracker->cleanup();<br>
+      delete peer_tracker;<br>
+    }<br>
     Item_sum_num::cleanup();<br>
   }<br>
 };<br>
@@ -502,12 +521,6 @@ class Item_window_func : public Item_func_or_sum<br>
 public:<br>
   Window_spec *window_spec;<br>
<br>
-  /*<br>
-    This stores the data about the partition we're currently in.<br>
-    advance_window() uses this to tell when we've left one partition and<br>
-    entered another<br>
-  */<br>
-  Group_bound_tracker partition_tracker;<br>
 public:<br>
   Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)<br>
     : Item_func_or_sum(thd, (Item *) win_func),<br>
@@ -600,9 +613,6 @@ class Item_window_func : public Item_func_or_sum<br>
   */<br>
   void setup_partition_border_check(THD *thd);<br>
<br>
-  void advance_window();<br>
-  bool check_if_partition_changed();<br>
-<br>
   enum_field_types field_type() const<br>
   {<br>
     return ((Item_sum *) args[0])->field_type();<br>
diff --git a/sql/sql_window.cc b/sql/sql_window.cc<br>
index 4c5ef53..a862821 100644<br>
--- a/sql/sql_window.cc<br>
+++ b/sql/sql_window.cc<br>
@@ -37,12 +37,12 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)<br>
       if (win_spec->order_list->elements && order_list->elements)<br>
       {<br>
         my_error(ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0), ref_name);<br>
-        return true;<br>
+        return true;<br>
       }<br>
-      if (win_spec->window_frame)<br>
+      if (win_spec->window_frame)<br>
       {<br>
         my_error(ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC, MYF(0), ref_name);<br>
-        return true;<br>
+        return true;<br>
       }<br>
       referenced_win_spec= win_spec;<br>
       if (partition_list->elements == 0)<br>
@@ -54,7 +54,7 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)<br>
   if (ref_name && !referenced_win_spec)<br>
   {<br>
     my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);<br>
-    return true;<br>
+    return true;<br>
   }<br>
   window_names_are_checked= true;<br>
   return false;<br>
@@ -73,7 +73,7 @@ Window_frame::check_frame_bounds()<br>
        top_bound->precedence_type == Window_frame_bound::FOLLOWING))<br>
   {<br>
     my_error(ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS, MYF(0));<br>
-    return true;<br>
+    return true;<br>
   }<br>
<br>
   return false;<br>
@@ -86,7 +86,7 @@ Window_frame::check_frame_bounds()<br>
<br>
 int<br>
 setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
-             List<Item> &fields, List<Item> &all_fields,<br>
+              List<Item> &fields, List<Item> &all_fields,<br>
               List<Window_spec> &win_specs, List<Item_window_func> &win_funcs)<br>
 {<br>
   Window_spec *win_spec;<br>
@@ -116,7 +116,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
   it.rewind();<br>
<br>
   List_iterator_fast<Window_spec> itp(win_specs);<br>
-<br>
+<br>
   while ((win_spec= it++))<br>
   {<br>
     bool hidden_group_fields;<br>
@@ -131,7 +131,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
     {<br>
       DBUG_RETURN(1);<br>
     }<br>
-<br>
+<br>
     if (win_spec->window_frame &&<br>
         win_spec->window_frame->exclusion != Window_frame::EXCL_NONE)<br>
     {<br>
@@ -188,7 +188,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
         }<br>
       }<br>
     }<br>
-<br>
+<br>
     /* "ROWS PRECEDING|FOLLOWING $n" must have a numeric $n */<br>
     if (win_spec->window_frame &&<br>
         win_spec->window_frame->units == Window_frame::UNITS_ROWS)<br>
@@ -219,7 +219,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
   {<br>
     win_func_item->update_used_tables();<br>
   }<br>
-<br>
+<br>
   DBUG_RETURN(0);<br>
 }<br>
<br>
@@ -445,7 +445,7 @@ typedef int (*Item_window_func_cmp)(Item_window_func *f1,<br>
   @brief<br>
     Sort window functions so that those that can be computed together are<br>
     adjacent.<br>
-<br>
+<br>
   @detail<br>
     Sort window functions by their<br>
      - required sorting order,<br>
@@ -498,48 +498,15 @@ void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list)<br>
     }<br>
     else if (win_spec_prev->window_frame != win_spec_curr->window_frame)<br>
       curr->marker|= FRAME_CHANGE_FLAG;<br>
-<br>
-    prev= curr;<br>
-  }<br>
+<br>
+    prev= curr;<br>
+  }<br>
 }<br>
<br>
<br>
 /////////////////////////////////////////////////////////////////////////////<br>
<br>
<br>
-/*<br>
-  Do a pass over sorted table and compute window function values.<br>
-<br>
-  This function is for handling window functions that can be computed on the<br>
-  fly. Examples are RANK() and ROW_NUMBER().<br>
-*/<br>
-bool compute_window_func_values(Item_window_func *item_win,<br>
-                                TABLE *tbl, READ_RECORD *info)<br>
-{<br>
-  int err;<br>
-  while (!(err=info->read_record(info)))<br>
-  {<br>
-    store_record(tbl,record[1]);<br>
-<br>
-    /*<br>
-      This will cause window function to compute its value for the<br>
-      current row :<br>
-    */<br>
-    item_win->advance_window();<br>
-<br>
-    /*<br>
-      Put the new value into temptable's field<br>
-      TODO: Should this use item_win->update_field() call?<br>
-      Regular aggegate function implementations seem to implement it.<br>
-    */<br>
-    item_win->save_in_field(item_win->result_field, true);<br>
-    err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);<br>
-    if (err && err != HA_ERR_RECORD_IS_THE_SAME)<br>
-      return true;<br>
-  }<br>
-  return false;<br>
-}<br>
-<br>
 /////////////////////////////////////////////////////////////////////////////<br>
 // Window Frames support<br>
 /////////////////////////////////////////////////////////////////////////////<br>
@@ -571,11 +538,6 @@ bool clone_read_record(const READ_RECORD *src, READ_RECORD *dst)<br>
<br>
 class Rowid_seq_cursor<br>
 {<br>
-  uchar *cache_start;<br>
-  uchar *cache_pos;<br>
-  uchar *cache_end;<br>
-  uint ref_length;<br>
-<br>
 public:<br>
   virtual ~Rowid_seq_cursor() {}<br>
<br>
@@ -595,17 +557,17 @@ class Rowid_seq_cursor<br>
     cache_pos+= ref_length;<br>
     return 0;<br>
   }<br>
-<br>
+<br>
   ha_rows get_rownum()<br>
   {<br>
     return (cache_pos - cache_start) / ref_length;<br>
   }<br>
<br>
-  // will be called by ROWS n FOLLOWING to catch up.<br>
   void move_to(ha_rows row_number)<br>
   {<br>
     cache_pos= cache_start + row_number * ref_length;<br>
   }<br>
+<br>
 protected:<br>
   bool at_eof() { return (cache_pos == cache_end); }<br>
<br>
@@ -618,6 +580,12 @@ class Rowid_seq_cursor<br>
   }<br>
<br>
   uchar *get_curr_rowid() { return cache_pos; }<br>
+<br>
+private:<br>
+  uchar *cache_start;<br>
+  uchar *cache_pos;<br>
+  uchar *cache_end;<br>
+  uint ref_length;<br>
 };<br>
<br>
<br>
@@ -627,11 +595,6 @@ class Rowid_seq_cursor<br>
<br>
 class Table_read_cursor : public Rowid_seq_cursor<br>
 {<br>
-  /*<br>
-    Note: we don't own *read_record, somebody else is using it.<br>
-    We only look at the constant part of it, e.g. table, record buffer, etc.<br>
-  */<br>
-  READ_RECORD *read_record;<br>
 public:<br>
   virtual ~Table_read_cursor() {}<br>
<br>
@@ -668,7 +631,14 @@ class Table_read_cursor : public Rowid_seq_cursor<br>
     return false; // didn't restore<br>
   }<br>
<br>
-  // todo: should move_to() also read row here?<br>
+private:<br>
+  /*<br>
+    Note: we don't own *read_record, somebody else is using it.<br>
+    We only look at the constant part of it, e.g. table, record buffer, etc.<br>
+  */<br>
+  READ_RECORD *read_record;<br>
+<br>
+  // TODO(spetrunia): should move_to() also read row here?<br>
 };<br>
<br>
<br>
@@ -679,14 +649,14 @@ class Table_read_cursor : public Rowid_seq_cursor<br>
<br>
 class Partition_read_cursor<br>
 {<br>
-  Table_read_cursor tbl_cursor;<br>
-  Group_bound_tracker bound_tracker;<br>
-  bool end_of_partition;<br>
 public:<br>
-  void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list)<br>
+  Partition_read_cursor(THD *thd, SQL_I_List<ORDER> *partition_list) :<br>
+    bound_tracker(thd, partition_list) {}<br>
+<br>
+  void init(READ_RECORD *info)<br>
   {<br>
     tbl_cursor.init(info);<br>
-    bound_tracker.init(thd, partition_list);<br>
+    bound_tracker.init();<br>
     end_of_partition= false;<br>
   }<br>
<br>
@@ -704,7 +674,7 @@ class Partition_read_cursor<br>
   }<br>
<br>
   /*<br>
-    Moves to a new row. The row is assumed to be within the current partition<br>
+    Moves to a new row. The row is assumed to be within the current partition.<br>
   */<br>
   void move_to(ha_rows rownum) { tbl_cursor.move_to(rownum); }<br>
<br>
@@ -731,14 +701,18 @@ class Partition_read_cursor<br>
   {<br>
     return tbl_cursor.restore_last_row();<br>
   }<br>
+<br>
+private:<br>
+  Table_read_cursor tbl_cursor;<br>
+  Group_bound_tracker bound_tracker;<br>
+  bool end_of_partition;<br>
 };<br>
<br>
 /////////////////////////////////////////////////////////////////////////////<br>
<br>
-<br>
 /*<br>
   Window frame bound cursor. Abstract interface.<br>
-<br>
+<br>
   @detail<br>
     The cursor moves within the partition that the current row is in.<br>
     It may be ahead or behind the current row.<br>
@@ -775,11 +749,12 @@ class Partition_read_cursor<br>
 class Frame_cursor : public Sql_alloc<br>
 {<br>
 public:<br>
-  virtual void init(THD *thd, READ_RECORD *info,<br>
-                    SQL_I_List<ORDER> *partition_list,<br>
-                    SQL_I_List<ORDER> *order_list)<br>
-  {}<br>
+  virtual void init(READ_RECORD *info) {};<br>
<br>
+  bool add_sum_func(Item_sum* item)<br>
+  {<br>
+    return sum_functions.push_back(item);<br>
+  }<br>
   /*<br>
     Current row has moved to the next partition and is positioned on the first<br>
     row there. Position the frame bound accordingly.<br>
@@ -796,20 +771,95 @@ class Frame_cursor : public Sql_alloc<br>
       - The callee may move tbl->file and tbl->record[0] to point to some other<br>
         row.<br>
   */<br>
-  virtual void pre_next_partition(ha_rows rownum, Item_sum* item){};<br>
-  virtual void next_partition(ha_rows rownum, Item_sum* item)=0;<br>
-<br>
+  virtual void pre_next_partition(ha_rows rownum) {};<br>
+  virtual void next_partition(ha_rows rownum)=0;<br>
+<br>
   /*<br>
     The current row has moved one row forward.<br>
     Move this frame bound accordingly, and update the value of aggregate<br>
     function as necessary.<br>
   */<br>
-  virtual void pre_next_row(Item_sum* item){};<br>
-  virtual void next_row(Item_sum* item)=0;<br>
-<br>
-  virtual ~Frame_cursor(){}<br>
+  virtual void pre_next_row() {};<br>
+  virtual void next_row()=0;<br>
+<br>
+  virtual ~Frame_cursor() {}<br>
+<br>
+protected:<br>
+  inline void add_value_to_items()<br>
+  {<br>
+    List_iterator_fast<Item_sum> it(sum_functions);<br>
+    Item_sum *item_sum;<br>
+    while ((item_sum= it++))<br>
+    {<br>
+      item_sum->add();<br>
+    }<br>
+  }<br>
+  inline void remove_value_from_items()<br>
+  {<br>
+    List_iterator_fast<Item_sum> it(sum_functions);<br>
+    Item_sum *item_sum;<br>
+    while ((item_sum= it++))<br>
+    {<br>
+      item_sum->remove();<br>
+    }<br>
+  }<br>
+<br>
+  /* Sum functions that this cursor handles. */<br>
+  List<Item_sum> sum_functions;<br>
+};<br>
+<br>
+/*<br>
+  A class that owns cursor objects associated with a specific window function.<br>
+*/<br>
+class Cursor_manager<br>
+{<br>
+public:<br>
+  bool add_cursor(Frame_cursor *cursor)<br>
+  {<br>
+    return cursors.push_back(cursor);<br>
+  }<br>
+<br>
+  void initialize_cursors(READ_RECORD *info)<br>
+  {<br>
+    List_iterator_fast<Frame_cursor> iter(cursors);<br>
+    Frame_cursor *fc;<br>
+    while ((fc= iter++))<br>
+      fc->init(info);<br>
+  }<br>
+<br>
+  void notify_cursors_partition_changed(ha_rows rownum)<br>
+  {<br>
+    List_iterator_fast<Frame_cursor> iter(cursors);<br>
+    Frame_cursor *cursor;<br>
+    while ((cursor= iter++))<br>
+      cursor->pre_next_partition(rownum);<br>
+<br>
+    iter.rewind();<br>
+    while ((cursor= iter++))<br>
+      cursor->next_partition(rownum);<br>
+  }<br>
+<br>
+  void notify_cursors_next_row()<br>
+  {<br>
+    List_iterator_fast<Frame_cursor> iter(cursors);<br>
+    Frame_cursor *cursor;<br>
+    while ((cursor= iter++))<br>
+      cursor->pre_next_row();<br>
+<br>
+    iter.rewind();<br>
+    while ((cursor= iter++))<br>
+      cursor->next_row();<br>
+  }<br>
+<br>
+  ~Cursor_manager() { cursors.delete_elements(); }<br>
+<br>
+private:<br>
+  /* List of the cursors that this manager owns. */<br>
+  List<Frame_cursor> cursors;<br>
 };<br>
<br>
+<br>
+<br>
 //////////////////////////////////////////////////////////////////////////////<br>
 // RANGE-type frames<br>
 //////////////////////////////////////////////////////////////////////////////<br>
@@ -841,16 +891,12 @@ class Frame_range_n_top : public Frame_cursor<br>
   */<br>
   int order_direction;<br>
 public:<br>
-  Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) :<br>
+  Frame_range_n_top(THD *thd,<br>
+                    SQL_I_List<ORDER> *partition_list,<br>
+                    SQL_I_List<ORDER> *order_list,<br>
+                    bool is_preceding_arg, Item *n_val_arg) :<br>
     n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)<br>
-  {}<br>
-<br>
-  void init(THD *thd, READ_RECORD *info,<br>
-            SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
   {<br>
-    cursor.init(info);<br>
-<br>
     DBUG_ASSERT(order_list->elements == 1);<br>
     Item *src_expr= order_list->first->item[0];<br>
     if (order_list->first->direction == ORDER::ORDER_ASC)<br>
@@ -872,24 +918,30 @@ class Frame_range_n_top : public Frame_cursor<br>
     item_add->fix_fields(thd, &item_add);<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void init(READ_RECORD *info)<br>
+  {<br>
+    cursor.init(info);<br>
+<br>
+  }<br>
+<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     // Save the value of FUNC(current_row)<br>
     range_expr->fetch_value_from(item_add);<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     cursor.move_to(rownum);<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
-  void pre_next_row(Item_sum* item)<br>
+  void pre_next_row()<br>
   {<br>
     range_expr->fetch_value_from(item_add);<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     /*<br>
       Ok, our cursor is at the first row R where<br>
@@ -900,19 +952,19 @@ class Frame_range_n_top : public Frame_cursor<br>
     {<br>
       if (order_direction * range_expr->cmp_read_only() <= 0)<br>
         return;<br>
-      item->remove();<br>
+      remove_value_from_items();<br>
     }<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
 private:<br>
-  void walk_till_non_peer(Item_sum* item)<br>
+  void walk_till_non_peer()<br>
   {<br>
     while (!cursor.get_next())<br>
     {<br>
       if (order_direction * range_expr->cmp_read_only() <= 0)<br>
         break;<br>
-      item->remove();<br>
+      remove_value_from_items();<br>
     }<br>
   }<br>
 };<br>
@@ -950,16 +1002,13 @@ class Frame_range_n_bottom: public Frame_cursor<br>
   */<br>
   int order_direction;<br>
 public:<br>
-  Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) :<br>
-    n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)<br>
-  {}<br>
-<br>
-  void init(THD *thd, READ_RECORD *info,<br>
-            SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  Frame_range_n_bottom(THD *thd,<br>
+                       SQL_I_List<ORDER> *partition_list,<br>
+                       SQL_I_List<ORDER> *order_list,<br>
+                       bool is_preceding_arg, Item *n_val_arg) :<br>
+    cursor(thd, partition_list), n_val(n_val_arg), item_add(NULL),<br>
+    is_preceding(is_preceding_arg)<br>
   {<br>
-    cursor.init(thd, info, partition_list);<br>
-<br>
     DBUG_ASSERT(order_list->elements == 1);<br>
     Item *src_expr= order_list->first->item[0];<br>
<br>
@@ -982,7 +1031,12 @@ class Frame_range_n_bottom: public Frame_cursor<br>
     item_add->fix_fields(thd, &item_add);<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void init(READ_RECORD *info)<br>
+  {<br>
+    cursor.init(info);<br>
+  }<br>
+<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     // Save the value of FUNC(current_row)<br>
     range_expr->fetch_value_from(item_add);<br>
@@ -991,20 +1045,20 @@ class Frame_range_n_bottom: public Frame_cursor<br>
     end_of_partition= false;<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     cursor.move_to(rownum);<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
-  void pre_next_row(Item_sum* item)<br>
+  void pre_next_row()<br>
   {<br>
     if (end_of_partition)<br>
       return;<br>
     range_expr->fetch_value_from(item_add);<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     if (end_of_partition)<br>
       return;<br>
@@ -1017,20 +1071,20 @@ class Frame_range_n_bottom: public Frame_cursor<br>
     {<br>
       if (order_direction * range_expr->cmp_read_only() < 0)<br>
         return;<br>
-      item->add();<br>
+      add_value_to_items();<br>
     }<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
 private:<br>
-  void walk_till_non_peer(Item_sum* item)<br>
+  void walk_till_non_peer()<br>
   {<br>
     int res;<br>
     while (!(res= cursor.get_next()))<br>
     {<br>
       if (order_direction * range_expr->cmp_read_only() < 0)<br>
         break;<br>
-      item->add();<br>
+      add_value_to_items();<br>
     }<br>
     if (res)<br>
       end_of_partition= true;<br>
@@ -1043,11 +1097,11 @@ class Frame_range_n_bottom: public Frame_cursor<br>
      ...<br>
    | peer1<br>
    | peer2  <----- current_row<br>
-   | peer3<br>
+   | peer3<br>
    +-peer4  <----- the cursor points here. peer4 itself is included.<br>
      nonpeer1<br>
      nonpeer2<br>
-<br>
+<br>
   This bound moves in front of the current_row. It should be a the first row<br>
   that is still a peer of the current row.<br>
 */<br>
@@ -1060,15 +1114,20 @@ class Frame_range_current_row_bottom: public Frame_cursor<br>
<br>
   bool dont_move;<br>
 public:<br>
-  void init(THD *thd, READ_RECORD *info,<br>
-            SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  Frame_range_current_row_bottom(THD *thd,<br>
+                                 SQL_I_List<ORDER> *partition_list,<br>
+                                 SQL_I_List<ORDER> *order_list) :<br>
+    cursor(thd, partition_list), peer_tracker(thd, order_list)<br>
+  {<br>
+  }<br>
+<br>
+  void init(READ_RECORD *info)<br>
   {<br>
-    cursor.init(thd, info, partition_list);<br>
-    peer_tracker.init(thd, order_list);<br>
+    cursor.init(info);<br>
+    peer_tracker.init();<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     // Save the value of the current_row<br>
     peer_tracker.check_if_next_group();<br>
@@ -1076,23 +1135,23 @@ class Frame_range_current_row_bottom: public Frame_cursor<br>
     if (rownum != 0)<br>
     {<br>
       // Add the current row now because our cursor has already seen it<br>
-      item->add();<br>
+      add_value_to_items();<br>
     }<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
-  void pre_next_row(Item_sum* item)<br>
+  void pre_next_row()<br>
   {<br>
     dont_move= !peer_tracker.check_if_next_group();<br>
     if (!dont_move)<br>
-      item->add();<br>
+      add_value_to_items();<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     // Check if our cursor is pointing at a peer of the current row.<br>
     // If not, move forward until that becomes true<br>
@@ -1104,11 +1163,11 @@ class Frame_range_current_row_bottom: public Frame_cursor<br>
       */<br>
       return;<br>
     }<br>
-    walk_till_non_peer(item);<br>
+    walk_till_non_peer();<br>
   }<br>
<br>
 private:<br>
-  void walk_till_non_peer(Item_sum* item)<br>
+  void walk_till_non_peer()<br>
   {<br>
     /*<br>
       Walk forward until we've met first row that's not a peer of the current<br>
@@ -1118,7 +1177,7 @@ class Frame_range_current_row_bottom: public Frame_cursor<br>
     {<br>
       if (peer_tracker.compare_with_cache())<br>
         break;<br>
-      item->add();<br>
+      add_value_to_items();<br>
     }<br>
   }<br>
 };<br>
@@ -1148,33 +1207,38 @@ class Frame_range_current_row_top : public Frame_cursor<br>
<br>
   bool move;<br>
 public:<br>
-  void init(THD *thd, READ_RECORD *info,<br>
-            SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  Frame_range_current_row_top(THD *thd,<br>
+                              SQL_I_List<ORDER> *partition_list,<br>
+                              SQL_I_List<ORDER> *order_list) :<br>
+    bound_tracker(thd, partition_list), cursor(), peer_tracker(thd, order_list),<br>
+    move(false)<br>
+  {}<br>
+<br>
+  void init(READ_RECORD *info)<br>
   {<br>
-    bound_tracker.init(thd, partition_list);<br>
+    bound_tracker.init();<br>
<br>
     cursor.init(info);<br>
-    peer_tracker.init(thd, order_list);<br>
+    peer_tracker.init();<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     // Fetch the value from the first row<br>
     peer_tracker.check_if_next_group();<br>
     cursor.move_to(rownum+1);<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item) {}<br>
+  void next_partition(ha_rows rownum) {}<br>
<br>
-  void pre_next_row(Item_sum* item)<br>
+  void pre_next_row()<br>
   {<br>
     // Check if the new current_row is a peer of the row that our cursor is<br>
     // pointing to.<br>
     move= peer_tracker.check_if_next_group();<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     if (move)<br>
     {<br>
@@ -1187,7 +1251,7 @@ class Frame_range_current_row_top : public Frame_cursor<br>
         // todo: need the following check ?<br>
         if (!peer_tracker.compare_with_cache())<br>
           return;<br>
-        item->remove();<br>
+        remove_value_from_items();<br>
       }<br>
<br>
       do<br>
@@ -1196,7 +1260,7 @@ class Frame_range_current_row_top : public Frame_cursor<br>
           return;<br>
         if (!peer_tracker.compare_with_cache())<br>
           return;<br>
-        item->remove();<br>
+        remove_value_from_items();<br>
       }<br>
       while (1);<br>
     }<br>
@@ -1214,7 +1278,14 @@ class Frame_range_current_row_top : public Frame_cursor<br>
 class Frame_unbounded_preceding : public Frame_cursor<br>
 {<br>
 public:<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  Frame_unbounded_preceding(THD *thd,<br>
+                            SQL_I_List<ORDER> *partition_list,<br>
+                            SQL_I_List<ORDER> *order_list)<br>
+  {}<br>
+<br>
+  void init(READ_RECORD *info) {}<br>
+<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     /*<br>
       UNBOUNDED PRECEDING frame end just stays on the first row.<br>
@@ -1222,7 +1293,7 @@ class Frame_unbounded_preceding : public Frame_cursor<br>
     */<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     /* Do nothing, UNBOUNDED PRECEDING frame end doesn't move. */<br>
   }<br>
@@ -1239,18 +1310,22 @@ class Frame_unbounded_following : public Frame_cursor<br>
   Partition_read_cursor cursor;<br>
<br>
 public:<br>
-  void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  Frame_unbounded_following(THD *thd,<br>
+      SQL_I_List<ORDER> *partition_list,<br>
+      SQL_I_List<ORDER> *order_list) :<br>
+    cursor(thd, partition_list) {}<br>
+<br>
+  void init(READ_RECORD *info)<br>
   {<br>
-    cursor.init(thd, info, partition_list);<br>
+    cursor.init(info);<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     cursor.on_next_partition(rownum);<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     if (!rownum)<br>
     {<br>
@@ -1258,16 +1333,16 @@ class Frame_unbounded_following : public Frame_cursor<br>
       if (cursor.get_next())<br>
         return;<br>
     }<br>
-    item->add();<br>
+    add_value_to_items();<br>
<br>
     /* Walk to the end of the partition, updating the SUM function */<br>
     while (!cursor.get_next())<br>
     {<br>
-      item->add();<br>
+      add_value_to_items();<br>
     }<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     /* Do nothing, UNBOUNDED FOLLOWING frame end doesn't move */<br>
   }<br>
@@ -1277,9 +1352,12 @@ class Frame_unbounded_following : public Frame_cursor<br>
 class Frame_unbounded_following_set_count : public Frame_unbounded_following<br>
 {<br>
 public:<br>
-  // pre_next_partition is inherited<br>
+  Frame_unbounded_following_set_count(<br>
+      THD *thd,<br>
+      SQL_I_List<ORDER> *partition_list, SQL_I_List<ORDER> *order_list) :<br>
+    Frame_unbounded_following(thd, partition_list, order_list) {}<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     ha_rows num_rows_in_partition= 0;<br>
     if (!rownum)<br>
@@ -1292,13 +1370,16 @@ class Frame_unbounded_following_set_count : public Frame_unbounded_following<br>
<br>
     /* Walk to the end of the partition, find how many rows there are. */<br>
     while (!cursor.get_next())<br>
-    {<br>
       num_rows_in_partition++;<br>
-    }<br>
<br>
-    Item_sum_window_with_row_count* item_with_row_count =<br>
-      static_cast<Item_sum_window_with_row_count *>(item);<br>
-    item_with_row_count->set_row_count(num_rows_in_partition);<br>
+    List_iterator_fast<Item_sum> it(sum_functions);<br>
+    Item_sum* item;<br>
+    while ((item= it++))<br>
+    {<br>
+      Item_sum_window_with_row_count* item_with_row_count =<br>
+        static_cast<Item_sum_window_with_row_count *>(item);<br>
+      item_with_row_count->set_row_count(num_rows_in_partition);<br>
+    }<br>
   }<br>
 };<br>
<br>
@@ -1324,13 +1405,12 @@ class Frame_n_rows_preceding : public Frame_cursor<br>
     is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)<br>
   {}<br>
<br>
-  void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  void init(READ_RECORD *info)<br>
   {<br>
     cursor.init(info);<br>
   }<br>
<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     /*<br>
       Position our cursor to point at the first row in the new partition<br>
@@ -1357,12 +1437,12 @@ class Frame_n_rows_preceding : public Frame_cursor<br>
     if (n_rows_to_skip == ha_rows(-1))<br>
     {<br>
       cursor.get_next();<br>
-      item->add();<br>
+      add_value_to_items();<br>
       n_rows_to_skip= 0;<br>
     }<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     if (n_rows_to_skip)<br>
     {<br>
@@ -1374,9 +1454,9 @@ class Frame_n_rows_preceding : public Frame_cursor<br>
       return;  // this is not expected to happen.<br>
<br>
     if (is_top_bound) // this is frame start endpoint<br>
-      item->remove();<br>
+      remove_value_from_items();<br>
     else<br>
-      item->add();<br>
+      add_value_to_items();<br>
   }<br>
 };<br>
<br>
@@ -1391,17 +1471,18 @@ class Frame_n_rows_preceding : public Frame_cursor<br>
 class Frame_rows_current_row_bottom : public Frame_cursor<br>
 {<br>
 public:<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
-    item->add();<br>
+    add_value_to_items();<br>
   }<br>
-  void next_partition(ha_rows rownum, Item_sum* item) {}<br>
-  void pre_next_row(Item_sum* item)<br>
+  void next_partition(ha_rows rownum) {}<br>
+  void pre_next_row()<br>
   {<br>
     /* Temp table's current row is current_row. Add it to the window func */<br>
-    item->add();<br>
+    add_value_to_items();<br>
   }<br>
-  void next_row(Item_sum* item) {};<br>
+  void next_row() {};<br>
 };<br>
<br>
<br>
@@ -1443,20 +1524,23 @@ class Frame_n_rows_following : public Frame_cursor<br>
   Partition_read_cursor cursor;<br>
   bool at_partition_end;<br>
 public:<br>
-  Frame_n_rows_following(bool is_top_bound_arg, ha_rows n_rows_arg) :<br>
-    is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)<br>
+  Frame_n_rows_following(THD *thd,<br>
+            SQL_I_List<ORDER> *partition_list,<br>
+            SQL_I_List<ORDER> *order_list,<br>
+            bool is_top_bound_arg, ha_rows n_rows_arg) :<br>
+    is_top_bound(is_top_bound_arg), n_rows(n_rows_arg),<br>
+    cursor(thd, partition_list)<br>
   {<br>
     DBUG_ASSERT(n_rows > 0);<br>
   }<br>
<br>
-  void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,<br>
-            SQL_I_List<ORDER> *order_list)<br>
+  void init(READ_RECORD *info)<br>
   {<br>
-    cursor.init(thd, info, partition_list);<br>
+    cursor.init(info);<br>
     at_partition_end= false;<br>
   }<br>
<br>
-  void pre_next_partition(ha_rows rownum, Item_sum* item)<br>
+  void pre_next_partition(ha_rows rownum)<br>
   {<br>
     at_partition_end= false;<br>
<br>
@@ -1469,39 +1553,39 @@ class Frame_n_rows_following : public Frame_cursor<br>
<br>
       // Current row points at the first row in the partition<br>
       if (is_top_bound) // this is frame top endpoint<br>
-        item->remove();<br>
+        remove_value_from_items();<br>
       else<br>
-        item->add();<br>
+        add_value_to_items();<br>
     }<br>
   }<br>
<br>
   /* Move our cursor to be n_rows ahead.  */<br>
-  void next_partition(ha_rows rownum, Item_sum* item)<br>
+  void next_partition(ha_rows rownum)<br>
   {<br>
     ha_rows i_end= n_rows + ((rownum==0)?1:0)- is_top_bound;<br>
     for (ha_rows i= 0; i < i_end; i++)<br>
     {<br>
-      if (next_row_intern(item))<br>
+      if (next_row_intern())<br>
         break;<br>
     }<br>
   }<br>
<br>
-  void next_row(Item_sum* item)<br>
+  void next_row()<br>
   {<br>
     if (at_partition_end)<br>
       return;<br>
-    next_row_intern(item);<br>
+    next_row_intern();<br>
   }<br>
<br>
 private:<br>
-  bool next_row_intern(Item_sum *item)<br>
+  bool next_row_intern()<br>
   {<br>
     if (!cursor.get_next())<br>
     {<br>
       if (is_top_bound) // this is frame start endpoint<br>
-        item->remove();<br>
+        remove_value_from_items();<br>
       else<br>
-        item->add();<br>
+        add_value_to_items();<br>
     }<br>
     else<br>
       at_partition_end= true;<br>
@@ -1513,8 +1597,9 @@ class Frame_n_rows_following : public Frame_cursor<br>
 /*<br>
   Get a Frame_cursor for a frame bound. This is a "factory function".<br>
 */<br>
-Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)<br>
+Frame_cursor *get_frame_cursor(THD *thd, Window_spec *spec, bool is_top_bound)<br>
 {<br>
+  Window_frame *frame= spec->window_frame;<br>
   if (!frame)<br>
   {<br>
     /*<br>
@@ -1536,9 +1621,13 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)<br>
         so again the same frame bounds can be used.<br>
     */<br>
     if (is_top_bound)<br>
-      return new Frame_unbounded_preceding;<br>
+      return new Frame_unbounded_preceding(thd,<br>
+                                           spec->partition_list,<br>
+                                           spec->order_list);<br>
     else<br>
-      return new Frame_range_current_row_bottom;<br>
+      return new Frame_range_current_row_bottom(thd,<br>
+                                                spec->partition_list,<br>
+                                                spec->order_list);<br>
   }<br>
<br>
   Window_frame_bound *bound= is_top_bound? frame->top_bound :<br>
@@ -1554,9 +1643,13 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)<br>
     {<br>
       /* The following serve both RANGE and ROWS: */<br>
       if (is_preceding)<br>
-        return new Frame_unbounded_preceding;<br>
-      else<br>
-        return new Frame_unbounded_following;<br>
+        return new Frame_unbounded_preceding(thd,<br>
+                                             spec->partition_list,<br>
+                                             spec->order_list);<br>
+<br>
+      return new Frame_unbounded_following(thd,<br>
+                                           spec->partition_list,<br>
+                                           spec->order_list);<br>
     }<br>
<br>
     if (frame->units == Window_frame::UNITS_ROWS)<br>
@@ -1567,15 +1660,21 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)<br>
       DBUG_ASSERT((longlong) n_rows >= 0);<br>
       if (is_preceding)<br>
         return new Frame_n_rows_preceding(is_top_bound, n_rows);<br>
-      else<br>
-        return new Frame_n_rows_following(is_top_bound, n_rows);<br>
+<br>
+      return new Frame_n_rows_following(<br>
+          thd, spec->partition_list, spec->order_list,<br>
+          is_top_bound, n_rows);<br>
     }<br>
     else<br>
     {<br>
       if (is_top_bound)<br>
-        return new Frame_range_n_top(is_preceding, bound->offset);<br>
-      else<br>
-        return new Frame_range_n_bottom(is_preceding, bound->offset);<br>
+        return new Frame_range_n_top(<br>
+            thd, spec->partition_list, spec->order_list,<br>
+            is_preceding, bound->offset);<br>
+<br>
+      return new Frame_range_n_bottom(thd,<br>
+          spec->partition_list, spec->order_list,<br>
+          is_preceding, bound->offset);<br>
     }<br>
   }<br>
<br>
@@ -1585,67 +1684,154 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)<br>
     {<br>
       if (is_top_bound)<br>
         return new Frame_rows_current_row_top;<br>
-      else<br>
-        return new Frame_rows_current_row_bottom;<br>
+<br>
+      return new Frame_rows_current_row_bottom;<br>
     }<br>
     else<br>
     {<br>
       if (is_top_bound)<br>
-        return new Frame_range_current_row_top;<br>
-      else<br>
-        return new Frame_range_current_row_bottom;<br>
+        return new Frame_range_current_row_top(<br>
+            thd, spec->partition_list, spec->order_list);<br>
+<br>
+      return new Frame_range_current_row_bottom(<br>
+          thd, spec->partition_list, spec->order_list);<br>
     }<br>
   }<br>
   return NULL;<br>
 }<br>
<br>
-void add_extra_frame_cursors(List<Frame_cursor> *cursors,<br>
-                             const Item_sum *window_func)<br>
+void add_extra_frame_cursors(THD *thd, Cursor_manager *cursor_manager,<br>
+                             Item_window_func *window_func)<br>
 {<br>
-  switch (window_func->sum_func())<br>
+  Window_spec *spec= window_func->window_spec;<br>
+  Item_sum *item_sum= window_func->window_func();<br>
+  Frame_cursor *fc;<br>
+  switch (item_sum->sum_func())<br>
   {<br>
     case Item_sum::CUME_DIST_FUNC:<br>
-      cursors->push_back(new Frame_unbounded_preceding);<br>
-      cursors->push_back(new Frame_range_current_row_bottom);<br>
+      fc= new Frame_unbounded_preceding(thd,<br>
+                                        spec->partition_list,<br>
+                                        spec->order_list);<br>
+      fc->add_sum_func(item_sum);<br>
+      cursor_manager->add_cursor(fc);<br>
+      fc= new Frame_range_current_row_bottom(thd,<br>
+                                             spec->partition_list,<br>
+                                             spec->order_list);<br>
+      fc->add_sum_func(item_sum);<br>
+      cursor_manager->add_cursor(fc);<br>
       break;<br>
     default:<br>
-      cursors->push_back(new Frame_unbounded_preceding);<br>
-      cursors->push_back(new Frame_rows_current_row_bottom);<br>
+      fc= new Frame_unbounded_preceding(<br>
+              thd, spec->partition_list, spec->order_list);<br>
+      fc->add_sum_func(item_sum);<br>
+      cursor_manager->add_cursor(fc);<br>
+<br>
+      fc= new Frame_rows_current_row_bottom;<br>
+      fc->add_sum_func(item_sum);<br>
+      cursor_manager->add_cursor(fc);<br>
   }<br>
 }<br>
<br>
-void get_window_func_required_cursors(<br>
-    List<Frame_cursor> *result, const Item_window_func* item_win)<br>
+<br>
+/*<br>
+   Create required frame cursors for the list of window functions.<br>
+   Register all functions to their appropriate cursors.<br>
+   If the window functions share the same frame specification,<br>
+   those window functions will be registered to the same cursor.<br>
+*/<br>
+void get_window_functions_required_cursors(<br>
+    THD *thd,<br>
+    List<Item_window_func>& window_functions,<br>
+    List<Cursor_manager> *cursor_managers)<br>
 {<br>
-  if (item_win->requires_partition_size())<br>
-    result->push_back(new Frame_unbounded_following_set_count);<br>
+  List_iterator_fast<Item_window_func> it(window_functions);<br>
+  Item_window_func* item_win_func;<br>
+  Item_sum *sum_func;<br>
+  while ((item_win_func= it++))<br>
+  {<br>
+    Cursor_manager *cursor_manager = new Cursor_manager();<br>
+    sum_func = item_win_func->window_func();<br>
+    Frame_cursor *fc;<br>
+    /*<br>
+      Some window functions require the partition size for computing values.<br>
+      Add a cursor that retrieves it as the first one in the list if necessary.<br>
+    */<br>
+    if (item_win_func->requires_partition_size())<br>
+    {<br>
+      fc= new Frame_unbounded_following_set_count(thd,<br>
+                item_win_func->window_spec->partition_list,<br>
+                item_win_func->window_spec->order_list);<br>
+      fc->add_sum_func(sum_func);<br>
+      cursor_manager->add_cursor(fc);<br>
+    }<br>
<br>
-  /*<br>
-    If it is not a regular window function that follows frame specifications,<br>
-    specific cursors are required.<br>
-  */<br>
-  if (item_win->is_frame_prohibited())<br>
+    /*<br>
+      If it is not a regular window function that follows frame specifications,<br>
+      specific cursors are required. ROW_NUM, RANK, NTILE and others follow<br>
+      such rules. Check is_frame_prohibited check for the full list.<br>
+    */<br>
+    if (item_win_func->is_frame_prohibited())<br>
+    {<br>
+      add_extra_frame_cursors(thd, cursor_manager, item_win_func);<br>
+      cursor_managers->push_back(cursor_manager);<br>
+      continue;<br>
+    }<br>
+<br>
+    Frame_cursor *frame_bottom= get_frame_cursor(thd,<br>
+        item_win_func->window_spec, false);<br>
+    Frame_cursor *frame_top= get_frame_cursor(thd,<br>
+        item_win_func->window_spec, true);<br>
+<br>
+    frame_bottom->add_sum_func(sum_func);<br>
+    frame_top->add_sum_func(sum_func);<br>
+<br>
+    /*<br>
+       The order of these cursors is important. A sum function<br>
+       must first add values (via frame_bottom) then remove them via<br>
+       frame_top. Removing items first doesn't make sense in the case of all<br>
+       window functions.<br>
+    */<br>
+    cursor_manager->add_cursor(frame_bottom);<br>
+    cursor_manager->add_cursor(frame_top);<br>
+    cursor_managers->push_back(cursor_manager);<br>
+  }<br>
+}<br>
+<br>
+/**<br>
+  Helper function that takes a list of window functions and writes<br>
+  their values in the current table record.<br>
+*/<br>
+static<br>
+bool save_window_function_values(List<Item_window_func>& window_functions,<br>
+                                 TABLE *tbl, uchar *rowid_buf)<br>
+{<br>
+  List_iterator_fast<Item_window_func> iter(window_functions);<br>
+  tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);<br>
+  store_record(tbl, record[1]);<br>
+  while (Item_window_func *item_win= iter++)<br>
   {<br>
-    add_extra_frame_cursors(result, item_win->window_func());<br>
-    return;<br>
+    int err;<br>
+    item_win->save_in_field(item_win->result_field, true);<br>
+    // TODO check if this can be placed outside the loop.<br>
+    err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);<br>
+    if (err && err != HA_ERR_RECORD_IS_THE_SAME)<br>
+      return true;<br>
   }<br>
<br>
-  /* A regular window function follows the frame specification. */<br>
-  result->push_back(get_frame_cursor(item_win->window_spec->window_frame,<br>
-                                     false));<br>
-  result->push_back(get_frame_cursor(item_win->window_spec->window_frame,<br>
-                                     true));<br>
+  return false;<br>
 }<br>
<br>
 /*<br>
+  TODO(cvicentiu) update this comment to reflect the new execution.<br>
+<br>
   Streamed window function computation with window frames.<br>
<br>
   We make a single pass over the ordered temp.table, but we're using three<br>
-  cursors:<br>
+  cursors:<br>
    - current row - the row that we're computing window func value for)<br>
    - start_bound - the start of the frame<br>
    - bottom_bound   - the end of the frame<br>
-<br>
+<br>
   All three cursors move together.<br>
<br>
   @todo<br>
@@ -1655,7 +1841,7 @@ void get_window_func_required_cursors(<br>
   @detail<br>
     ROWS BETWEEN 3 PRECEDING  -- frame start<br>
               AND 3 FOLLOWING  -- frame end<br>
-<br>
+<br>
                                     /------ frame end (aka BOTTOM)<br>
     Dataset start                   |<br>
      --------====*=======[*]========*========-------->> dataset end<br>
@@ -1663,7 +1849,7 @@ void get_window_func_required_cursors(<br>
                  |         +-------- current row<br>
                  |<br>
                  \-------- frame start ("TOP")<br>
-<br>
+<br>
     - frame_end moves forward and adds rows into the aggregate function.<br>
     - frame_start follows behind and removes rows from the aggregate function.<br>
     - current_row is the row where the value of aggregate function is stored.<br>
@@ -1672,97 +1858,90 @@ void get_window_func_required_cursors(<br>
   condition (Others can catch up by counting rows?)<br>
<br>
 */<br>
-<br>
-bool compute_window_func_with_frames(Item_window_func *item_win,<br>
-                                     TABLE *tbl, READ_RECORD *info)<br>
+bool compute_window_func(THD *thd,<br>
+                         List<Item_window_func>& window_functions,<br>
+                         List<Cursor_manager>& cursor_managers,<br>
+                         TABLE *tbl,<br>
+                         SORT_INFO *filesort_result)<br>
 {<br>
-  THD *thd= tbl->in_use;<br>
-  int err= 0;<br>
+  List_iterator_fast<Item_window_func> iter_win_funcs(window_functions);<br>
+  List_iterator_fast<Cursor_manager> iter_cursor_managers(cursor_managers);<br>
+  uint err;<br>
<br>
-  Item_sum *sum_func= item_win->window_func();<br>
-  /* This algorithm doesn't support DISTINCT aggregator */<br>
-  sum_func->set_aggregator(Aggregator::SIMPLE_AGGREGATOR);<br>
+  READ_RECORD info;<br>
<br>
-  List<Frame_cursor> cursors;<br>
-  get_window_func_required_cursors(&cursors, item_win);<br>
+  if (init_read_record(&info, current_thd, tbl, NULL/*select*/, filesort_result,<br>
+                       0, 1, FALSE))<br>
+    return true;<br>
<br>
-  List_iterator_fast<Frame_cursor> it(cursors);<br>
-  Frame_cursor *c;<br>
-  while((c= it++))<br>
+  Cursor_manager *cursor_manager;<br>
+  while ((cursor_manager= iter_cursor_managers++))<br>
+    cursor_manager->initialize_cursors(&info);<br>
+<br>
+  /* One partition tracker for each window function. */<br>
+  List<Group_bound_tracker> partition_trackers;<br>
+  Item_window_func *win_func;<br>
+  while ((win_func= iter_win_funcs++))<br>
   {<br>
-    c->init(thd, info, item_win->window_spec->partition_list,<br>
-            item_win->window_spec->order_list);<br>
+    Group_bound_tracker *tracker= new Group_bound_tracker(thd,<br>
+                                        win_func->window_spec->partition_list);<br>
+    // TODO(cvicentiu) This should be removed and placed in constructor.<br>
+    tracker->init();<br>
+    partition_trackers.push_back(tracker);<br>
   }<br>
<br>
-  bool is_error= false;<br>
+  List_iterator_fast<Group_bound_tracker> iter_part_trackers(partition_trackers);<br>
   ha_rows rownum= 0;<br>
   uchar *rowid_buf= (uchar*) my_malloc(tbl->file->ref_length, MYF(0));<br>
<br>
   while (true)<br>
   {<br>
-    /* Move the current_row */<br>
-    if ((err=info->read_record(info)))<br>
-    {<br>
-      break; /* End of file */<br>
-    }<br>
-    bool partition_changed= item_win->check_if_partition_changed();<br>
+    if ((err= info.read_record(&info)))<br>
+      break; // End of file.<br>
<br>
+    /* Remember current row so that we can restore it before computing<br>
+       each window function. */<br>
     tbl->file->position(tbl->record[0]);<br>
     memcpy(rowid_buf, tbl->file->ref, tbl->file->ref_length);<br>
<br>
-    if (partition_changed || (rownum == 0))<br>
-    {<br>
-      sum_func->clear();<br>
-      /*<br>
-        pre_XXX functions assume that tbl->record[0] contains current_row, and<br>
-        they may not change it.<br>
-      */<br>
-      it.rewind();<br>
-      while ((c= it++))<br>
-        c->pre_next_partition(rownum, sum_func);<br>
-      /*<br>
-        We move bottom_bound first, because we want rows to be added into the<br>
-        aggregate before top_bound attempts to remove them.<br>
-      */<br>
-      it.rewind();<br>
-      while ((c= it++))<br>
-        c->next_partition(rownum, sum_func);<br>
-    }<br>
-    else<br>
-    {<br>
-      /* Again, both pre_XXX function can find current_row in tbl->record[0] */<br>
-      it.rewind();<br>
-      while ((c= it++))<br>
-        c->pre_next_row(sum_func);<br>
-<br>
-      /* These make no assumptions about tbl->record[0] and may change it */<br>
-      it.rewind();<br>
-      while ((c= it++))<br>
-        c->next_row(sum_func);<br>
-    }<br>
-    rownum++;<br>
+    iter_win_funcs.rewind();<br>
+    iter_part_trackers.rewind();<br>
+    iter_cursor_managers.rewind();<br>
<br>
-    /*<br>
-      Frame cursors may have made tbl->record[0] to point to some record other<br>
-      than current_row. This applies to tbl->file's internal state, too.<br>
-      Fix this by reading the current row again.<br>
-    */<br>
-    tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);<br>
-    store_record(tbl,record[1]);<br>
-    item_win->save_in_field(item_win->result_field, true);<br>
-    err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);<br>
-    if (err && err != HA_ERR_RECORD_IS_THE_SAME)<br>
+    Group_bound_tracker *tracker;<br>
+    while ((win_func= iter_win_funcs++) &&<br>
+           (tracker= iter_part_trackers++) &&<br>
+           (cursor_manager= iter_cursor_managers++))<br>
     {<br>
-      is_error= true;<br>
-      break;<br>
+      if (tracker->check_if_next_group() || (rownum == 0))<br>
+      {<br>
+        /* TODO(cvicentiu)<br>
+           Clearing window functions should happen through cursors. */<br>
+        win_func->window_func()->clear();<br>
+        cursor_manager->notify_cursors_partition_changed(rownum);<br>
+      }<br>
+      else<br>
+      {<br>
+        cursor_manager->notify_cursors_next_row();<br>
+      }<br>
+      /* Return to current row after notifying cursors for each window<br>
+         function. */<br>
+      tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);<br>
     }<br>
+<br>
+    /* We now have computed values for each window function. They can now<br>
+       be saved in the current row. */<br>
+    save_window_function_values(window_functions, tbl, rowid_buf);<br>
+<br>
+    rownum++;<br>
   }<br>
<br>
   my_free(rowid_buf);<br>
-  cursors.delete_elements();<br>
-  return is_error? true: false;<br>
-}<br>
+  partition_trackers.delete_elements();<br>
+  end_read_record(&info);<br>
<br>
+  return false;<br>
+}<br>
<br>
 /* Make a list that is a concation of two lists of ORDER elements */<br>
<br>
@@ -1799,25 +1978,18 @@ static ORDER* concat_order_lists(MEM_ROOT *mem_root, ORDER *list1, ORDER *list2)<br>
   return res;<br>
 }<br>
<br>
-<br>
-bool Window_func_runner::setup(THD *thd)<br>
+bool Window_func_runner::add_function_to_run(Item_window_func *win_func)<br>
 {<br>
-  win_func->setup_partition_border_check(thd);<br>
+<br>
+  Item_sum *sum_func= win_func->window_func();<br>
+  sum_func->setup_window_func(current_thd, win_func->window_spec);<br>
<br>
   Item_sum::Sumfunctype type= win_func->window_func()->sum_func();<br>
-  switch (type)<br>
+  switch (type)<br>
   {<br>
     case Item_sum::ROW_NUMBER_FUNC:<br>
     case Item_sum::RANK_FUNC:<br>
     case Item_sum::DENSE_RANK_FUNC:<br>
-    {<br>
-      /*<br>
-        One-pass window function computation, walk through the rows and<br>
-        assign values.<br>
-      */<br>
-      compute_func= compute_window_func_values;<br>
-      break;<br>
-    }<br>
     case Item_sum::COUNT_FUNC:<br>
     case Item_sum::SUM_BIT_FUNC:<br>
     case Item_sum::SUM_FUNC:<br>
@@ -1825,43 +1997,49 @@ bool Window_func_runner::setup(THD *thd)<br>
     case Item_sum::PERCENT_RANK_FUNC:<br>
     case Item_sum::CUME_DIST_FUNC:<br>
     case Item_sum::NTILE_FUNC:<br>
-    {<br>
-      /*<br>
-        Frame-aware window function computation. It does one pass, but<br>
-        uses three cursors -frame_start, current_row, and frame_end.<br>
-      */<br>
-      compute_func= compute_window_func_with_frames;<br>
       break;<br>
-    }<br>
+<br>
     default:<br>
-      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "This aggregate as window function");<br>
+      my_error(ER_NOT_SUPPORTED_YET, MYF(0),<br>
+               "This aggregate as window function");<br>
       return true;<br>
   }<br>
<br>
-  return false;<br>
+  return window_functions.push_back(win_func);<br>
 }<br>
<br>
<br>
 /*<br>
   Compute the value of window function for all rows.<br>
 */<br>
-bool Window_func_runner::exec(TABLE *tbl, SORT_INFO *filesort_result)<br>
+bool Window_func_runner::exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result)<br>
 {<br>
-  THD *thd= current_thd;<br>
-  win_func->set_phase_to_computation();<br>
-<br>
-  /* Go through the sorted array and compute the window function */<br>
-  READ_RECORD info;<br>
-<br>
-  if (init_read_record(&info, thd, tbl, NULL/*select*/, filesort_result,<br>
-                       0, 1, FALSE))<br>
-    return true;<br>
+  List_iterator_fast<Item_window_func> it(window_functions);<br>
+  Item_window_func *win_func;<br>
+  while ((win_func= it++))<br>
+  {<br>
+    win_func->set_phase_to_computation();<br>
+    // TODO(cvicentiu) Setting the aggregator should probably be done during<br>
+    // setup of Window_funcs_sort.<br>
+    win_func->window_func()->set_aggregator(Aggregator::SIMPLE_AGGREGATOR);<br>
+  }<br>
+  it.rewind();<br>
<br>
-  bool is_error= compute_func(win_func, tbl, &info);<br>
+  List<Cursor_manager> cursor_managers;<br>
+  get_window_functions_required_cursors(thd, window_functions,<br>
+                                        &cursor_managers);<br>
<br>
-  win_func->set_phase_to_retrieval();<br>
+  /* Go through the sorted array and compute the window function */<br>
+  bool is_error= compute_window_func(thd,<br>
+                                     window_functions,<br>
+                                     cursor_managers,<br>
+                                     tbl, filesort_result);<br>
+  while ((win_func= it++))<br>
+  {<br>
+    win_func->set_phase_to_retrieval();<br>
+  }<br>
<br>
-  end_read_record(&info);<br>
+  cursor_managers.delete_elements();<br>
<br>
   return is_error;<br>
 }<br>
@@ -1872,21 +2050,15 @@ bool Window_funcs_sort::exec(JOIN *join)<br>
   THD *thd= join->thd;<br>
   JOIN_TAB *join_tab= &join->join_tab[join->top_join_tab_count];<br>
<br>
+  /* Sort the table based on the most specific sorting criteria of<br>
+     the window functions. */<br>
   if (create_sort_index(thd, join, join_tab, filesort))<br>
     return true;<br>
<br>
   TABLE *tbl= join_tab->table;<br>
   SORT_INFO *filesort_result= join_tab->filesort_result;<br>
<br>
-  bool is_error= false;<br>
-  List_iterator<Window_func_runner> it(runners);<br>
-  Window_func_runner *runner;<br>
-<br>
-  while ((runner= it++))<br>
-  {<br>
-    if ((is_error= runner->exec(tbl, filesort_result)))<br>
-      break;<br>
-  }<br>
+  bool is_error= runner.exec(thd, tbl, filesort_result);<br>
<br>
   delete join_tab->filesort_result;<br>
   join_tab->filesort_result= NULL;<br>
@@ -1894,30 +2066,32 @@ bool Window_funcs_sort::exec(JOIN *join)<br>
 }<br>
<br>
<br>
-bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,<br>
-                             List_iterator<Item_window_func> &it)<br>
+bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,<br>
+                              List_iterator<Item_window_func> &it)<br>
 {<br>
   Item_window_func *win_func= it.peek();<br>
   Item_window_func *prev_win_func;<br>
<br>
+  /* The iterator should point to a valid function at the start of execution. */<br>
+  DBUG_ASSERT(win_func);<br>
   do<br>
   {<br>
-    Window_func_runner *runner;<br>
-    if (!(runner= new Window_func_runner(win_func)) ||<br>
-        runner->setup(thd))<br>
-    {<br>
+    if (runner.add_function_to_run(win_func))<br>
       return true;<br>
-    }<br>
-    runners.push_back(runner);<br>
     it++;<br>
     prev_win_func= win_func;<br>
-  } while ((win_func= it.peek()) && !(win_func->marker & SORTORDER_CHANGE_FLAG));<br>
-<br>
+  } while ((win_func= it.peek()) &&<br>
+           !(win_func->marker & SORTORDER_CHANGE_FLAG));<br>
+<br>
   /*<br>
     The sort criteria must be taken from the last win_func in the group of<br>
-    adjacent win_funcs that do not have SORTORDER_CHANGE_FLAG.<br>
+    adjacent win_funcs that do not have SORTORDER_CHANGE_FLAG. This is<br>
+    because the sort order must be the most specific sorting criteria defined<br>
+    within the window function group. This ensures that we sort the table<br>
+    in a way that the result is valid for all window functions belonging to<br>
+    this Window_funcs_sort.<br>
   */<br>
-  Window_spec *spec = prev_win_func->window_spec;<br>
+  Window_spec *spec= prev_win_func->window_spec;<br>
<br>
   ORDER* sort_order= concat_order_lists(thd->mem_root,<br>
                                         spec->partition_list->first,<br>
@@ -1932,8 +2106,8 @@ bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,<br>
<br>
<br>
 bool Window_funcs_computation::setup(THD *thd,<br>
-                                          List<Item_window_func> *window_funcs,<br>
-                                          JOIN_TAB *tab)<br>
+                                     List<Item_window_func> *window_funcs,<br>
+                                     JOIN_TAB *tab)<br>
 {<br>
   order_window_funcs_by_window_specs(window_funcs);<br>
<br>
diff --git a/sql/sql_window.h b/sql/sql_window.h<br>
index 54e39d8..c384724 100644<br>
--- a/sql/sql_window.h<br>
+++ b/sql/sql_window.h<br>
@@ -154,9 +154,7 @@ int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,<br>
 // Classes that make window functions computation a part of SELECT's query plan<br>
 //////////////////////////////////////////////////////////////////////////////<br>
<br>
-typedef bool (*window_compute_func_t)(Item_window_func *item_win,<br>
-                                      TABLE *tbl, READ_RECORD *info);<br>
-<br>
+class Frame_cursor;<br>
 /*<br>
   This handles computation of one window function.<br>
<br>
@@ -165,21 +163,17 @@ typedef bool (*window_compute_func_t)(Item_window_func *item_win,<br>
<br>
 class Window_func_runner : public Sql_alloc<br>
 {<br>
-  Item_window_func *win_func;<br>
-<br>
-  /* The function to use for computation*/<br>
-  window_compute_func_t compute_func;<br>
-<br>
 public:<br>
-  Window_func_runner(Item_window_func *win_func_arg) :<br>
-    win_func(win_func_arg)<br>
-  {}<br>
+  /* Add the function to be computed during the execution pass  */<br>
+  bool add_function_to_run(Item_window_func *win_func);<br>
<br>
-  // Set things up. Create filesort structures, etc<br>
-  bool setup(THD *thd);<br>
-<br>
-  // This sorts and runs the window function.<br>
-  bool exec(TABLE *tbl, SORT_INFO *filesort_result);<br>
+  /* Compute and fill the fields in the table. */<br>
+  bool exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result);<br>
+<br>
+private:<br>
+  /* A list of window functions for which this Window_func_runner will compute<br>
+     values during the execution phase. */<br>
+  List<Item_window_func> window_functions;<br>
 };<br>
<br>
<br>
@@ -191,21 +185,24 @@ class Window_func_runner : public Sql_alloc<br>
<br>
 class Window_funcs_sort : public Sql_alloc<br>
 {<br>
-  List<Window_func_runner> runners;<br>
-<br>
-  /* Window functions can be computed over this sorting */<br>
-  Filesort *filesort;<br>
 public:<br>
   bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it);<br>
   bool exec(JOIN *join);<br>
   void cleanup() { delete filesort; }<br>
<br>
   friend class Window_funcs_computation;<br>
+<br>
+private:<br>
+  Window_func_runner runner;<br>
+<br>
+  /* Window functions can be computed over this sorting */<br>
+  Filesort *filesort;<br>
 };<br>
<br>
<br>
 struct st_join_table;<br>
 class Explain_aggr_window_funcs;<br>
+<br>
 /*<br>
   This is a "window function computation phase": a single object of this class<br>
   takes care of computing all window functions in a SELECT.<br>
</blockquote></div></div></div>