summaryrefslogtreecommitdiff
path: root/sql/item_windowfunc.cc
diff options
context:
space:
mode:
authorGalina Shalygina <galashalygina@gmail.com>2016-05-08 23:04:41 +0300
committerGalina Shalygina <galashalygina@gmail.com>2016-05-08 23:04:41 +0300
commitbe1d06c8a5f843e775374e5ec148aaee56970bdc (patch)
treebd7a95e771ca3b405583dccab8b468dd6fb4509f /sql/item_windowfunc.cc
parente09b1f2a226bf2763b211f74908a6486b83ebed1 (diff)
downloadmariadb-git-be1d06c8a5f843e775374e5ec148aaee56970bdc.tar.gz
Merge branch '10.2' into 10.2-mdev9864
Diffstat (limited to 'sql/item_windowfunc.cc')
-rw-r--r--sql/item_windowfunc.cc242
1 files changed, 242 insertions, 0 deletions
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
new file mode 100644
index 00000000000..d157d545dad
--- /dev/null
+++ b/sql/item_windowfunc.cc
@@ -0,0 +1,242 @@
+#include "item_windowfunc.h"
+#include "my_dbug.h"
+#include "my_global.h"
+#include "sql_select.h" // test if group changed
+
+
+bool
+Item_window_func::resolve_window_name(THD *thd)
+{
+ if (window_spec)
+ {
+ /* The window name has been already resolved */
+ return false;
+ }
+ DBUG_ASSERT(window_name != NULL && window_spec == NULL);
+ char *ref_name= window_name->str;
+
+ /* !TODO: Add the code to resolve ref_name in outer queries */
+ /*
+ First look for the deinition of the window with 'window_name'
+ in the current select
+ */
+ List<Window_spec> curr_window_specs=
+ List<Window_spec> (thd->lex->current_select->window_specs);
+ List_iterator_fast<Window_spec> it(curr_window_specs);
+ Window_spec *win_spec;
+ while((win_spec= it++))
+ {
+ char *win_spec_name= win_spec->name();
+ if (win_spec_name &&
+ my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
+ {
+ window_spec= win_spec;
+ break;
+ }
+ }
+
+ if (!window_spec)
+ {
+ my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);
+ return true;
+ }
+
+ return false;
+}
+
+
+void
+Item_window_func::update_used_tables()
+{
+ used_tables_cache= 0;
+ window_func()->update_used_tables();
+ used_tables_cache|= window_func()->used_tables();
+ for (ORDER *ord= window_spec->partition_list->first; ord; ord=ord->next)
+ {
+ Item *item= *ord->item;
+ item->update_used_tables();
+ used_tables_cache|= item->used_tables();
+ }
+ for (ORDER *ord= window_spec->order_list->first; ord; ord=ord->next)
+ {
+ Item *item= *ord->item;
+ item->update_used_tables();
+ used_tables_cache|= item->used_tables();
+ }
+}
+
+
+bool
+Item_window_func::fix_fields(THD *thd, Item **ref)
+{
+ DBUG_ASSERT(fixed == 0);
+
+ enum_parsing_place place= thd->lex->current_select->parsing_place;
+
+ if (!(place == SELECT_LIST || place == IN_ORDER_BY))
+ {
+ my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
+ return true;
+ }
+
+ if (window_name && resolve_window_name(thd))
+ return true;
+
+ if (window_spec->window_frame && is_frame_prohibited())
+ {
+ my_error(ER_NOT_ALLOWED_WINDOW_FRAME, MYF(0), window_func()->func_name());
+ return true;
+ }
+
+ if (window_spec->order_list->elements == 0 && is_order_list_mandatory())
+ {
+ my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name());
+ return true;
+ }
+ /*
+ TODO: why the last parameter is 'ref' in this call? What if window_func
+ decides to substitute itself for something else and does *ref=.... ?
+ This will substitute *this (an Item_window_func object) with Item_sum
+ object. Is this the intent?
+ */
+ if (window_func()->fix_fields(thd, ref))
+ return true;
+
+ const_item_cache= false;
+ with_window_func= true;
+ with_sum_func= false;
+
+ fix_length_and_dec();
+
+ max_length= window_func()->max_length;
+ maybe_null= window_func()->maybe_null;
+
+ fixed= 1;
+ set_phase_to_initial();
+ return false;
+}
+
+
+/*
+ @detail
+ Window function evaluates its arguments when it is scanning the temporary
+ table in partition/order-by order. That is, arguments should be read from
+ the temporary table, not from the original base columns.
+
+ In order for this to work, we need to call "split_sum_func" for each
+ argument. The effect of the call is:
+ 1. the argument is added into ref_pointer_array. This will cause the
+ argument to be saved in the temp.table
+ 2. argument item is replaced with an Item_ref object. this object refers
+ the argument through the ref_pointer_array.
+
+ then, change_to_use_tmp_fields() will replace ref_pointer_array with an
+ array that points to the temp.table fields.
+ This way, when window_func attempts to evaluate its arguments, it will use
+ Item_ref objects which will read data from the temp.table.
+
+ Note: Before window functions, aggregate functions never needed to do such
+ transformations on their arguments. This is because grouping operation
+ does not need to read from the temp.table.
+ (Q: what happens when we first sort and then do grouping in a
+ group-after-group mode? dont group by items read from temp.table, then?)
+*/
+
+void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
+ List<Item> &fields, uint flags)
+{
+ for (uint i=0; i < window_func()->argument_count(); i++)
+ {
+ Item **p_item= &window_func()->arguments()[i];
+ (*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags);
+ }
+}
+
+
+/*
+ This must be called before advance_window() can be called.
+
+ @detail
+ If we attempt to do it in fix_fields(), partition_fields will refer
+ to the original window function arguments.
+ We need it to refer to temp.table columns.
+*/
+
+void Item_window_func::setup_partition_border_check(THD *thd)
+{
+ partition_tracker.init(thd, window_spec->partition_list);
+ window_func()->setup_window_func(thd, window_spec);
+}
+
+
+void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
+{
+ /* TODO: move this into Item_window_func? */
+ peer_tracker.init(thd, window_spec->order_list);
+ clear();
+}
+
+void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
+{
+ /* TODO: consider moving this && Item_sum_rank's implementation */
+ peer_tracker.init(thd, window_spec->order_list);
+ clear();
+}
+
+bool Item_sum_dense_rank::add()
+{
+ if (peer_tracker.check_if_next_group() || first_add)
+ {
+ first_add= false;
+ dense_rank++;
+ }
+
+ return false;
+}
+
+
+bool Item_sum_rank::add()
+{
+ row_number++;
+ if (peer_tracker.check_if_next_group())
+ {
+ /* Row value changed */
+ cur_rank= row_number;
+ }
+ return false;
+}
+
+bool Item_window_func::check_if_partition_changed()
+{
+ return partition_tracker.check_if_next_group();
+}
+
+void Item_window_func::advance_window()
+{
+ if (check_if_partition_changed())
+ {
+ /* Next partition */
+ window_func()->clear();
+ }
+ window_func()->add();
+}
+
+bool Item_sum_percent_rank::add()
+{
+ row_number++;
+ if (peer_tracker.check_if_next_group())
+ {
+ /* Row value changed. */
+ cur_rank= row_number;
+ }
+ return false;
+}
+
+void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
+{
+ /* TODO: move this into Item_window_func? */
+ peer_tracker.init(thd, window_spec->order_list);
+ clear();
+}
+
+