1 /*
2    Copyright (c) 2016, 2020, MariaDB
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
16 
17 #include "mariadb.h"
18 #include "item_windowfunc.h"
19 #include "sql_select.h" // test if group changed
20 
21 
22 bool
resolve_window_name(THD * thd)23 Item_window_func::resolve_window_name(THD *thd)
24 {
25   if (window_spec)
26   {
27     /* The window name has been already resolved */
28     return false;
29   }
30   DBUG_ASSERT(window_name != NULL && window_spec == NULL);
31   const char *ref_name= window_name->str;
32 
33   /* !TODO: Add the code to resolve ref_name in outer queries */
34   /*
35     First look for the deinition of the window with 'window_name'
36     in the current select
37   */
38   List<Window_spec> curr_window_specs=
39     List<Window_spec> (thd->lex->current_select->window_specs);
40   List_iterator_fast<Window_spec> it(curr_window_specs);
41   Window_spec *win_spec;
42   while((win_spec= it++))
43   {
44     const char *win_spec_name= win_spec->name();
45     if (win_spec_name &&
46         my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
47     {
48       window_spec= win_spec;
49       break;
50     }
51   }
52 
53   if (!window_spec)
54   {
55     my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);
56     return true;
57   }
58 
59   return false;
60 }
61 
62 
63 void
update_used_tables()64 Item_window_func::update_used_tables()
65 {
66   used_tables_cache= 0;
67   window_func()->update_used_tables();
68   used_tables_cache|= window_func()->used_tables();
69   for (ORDER *ord= window_spec->partition_list->first; ord; ord=ord->next)
70   {
71     Item *item= *ord->item;
72     item->update_used_tables();
73     used_tables_cache|= item->used_tables();
74   }
75   for (ORDER *ord= window_spec->order_list->first; ord; ord=ord->next)
76   {
77     Item *item= *ord->item;
78     item->update_used_tables();
79     used_tables_cache|= item->used_tables();
80   }
81 }
82 
83 
84 bool
fix_fields(THD * thd,Item ** ref)85 Item_window_func::fix_fields(THD *thd, Item **ref)
86 {
87   DBUG_ASSERT(fixed == 0);
88 
89   if (!thd->lex->current_select ||
90       (thd->lex->current_select->context_analysis_place != SELECT_LIST &&
91        thd->lex->current_select->context_analysis_place != IN_ORDER_BY))
92   {
93     my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
94     return true;
95   }
96 
97   if (window_name && resolve_window_name(thd))
98     return true;
99 
100   if (window_spec->window_frame && is_frame_prohibited())
101   {
102     my_error(ER_NOT_ALLOWED_WINDOW_FRAME, MYF(0), window_func()->func_name());
103     return true;
104   }
105 
106   if (window_spec->order_list->elements == 0 && is_order_list_mandatory())
107   {
108     my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name());
109     return true;
110   }
111 
112   window_func()->mark_as_window_func_sum_expr();
113 
114   /*
115     TODO: why the last parameter is 'ref' in this call? What if window_func
116     decides to substitute itself for something else and does *ref=.... ?
117     This will substitute *this (an Item_window_func object) with Item_sum
118     object. Is this the intent?
119   */
120   if (window_func()->fix_fields(thd, ref))
121     return true;
122 
123   const_item_cache= false;
124   with_window_func= true;
125   with_sum_func= false;
126 
127   if (fix_length_and_dec())
128     return TRUE;
129 
130   max_length= window_func()->max_length;
131   maybe_null= window_func()->maybe_null;
132 
133   fixed= 1;
134   set_phase_to_initial();
135   return false;
136 }
137 
138 
139 /*
140   @detail
141     Window function evaluates its arguments when it is scanning the temporary
142     table in partition/order-by order. That is, arguments should be read from
143     the temporary table, not from the original base columns.
144 
145     In order for this to work, we need to call "split_sum_func" for each
146     argument. The effect of the call is:
147      1. the argument is added into ref_pointer_array. This will cause the
148         argument to be saved in the temp.table
149      2. argument item is replaced with an Item_ref object. this object refers
150         the argument through the ref_pointer_array.
151 
152     then, change_to_use_tmp_fields() will replace ref_pointer_array with an
153     array that points to the temp.table fields.
154     This way, when window_func attempts to evaluate its arguments, it will use
155     Item_ref objects which will read data from the temp.table.
156 
157     Note: Before window functions, aggregate functions never needed to do such
158     transformations on their arguments. This is because grouping operation
159     does not need to read from the temp.table.
160     (Q: what happens when we first sort and then do grouping in a
161       group-after-group mode? dont group by items read from temp.table, then?)
162 */
163 
split_sum_func(THD * thd,Ref_ptr_array ref_pointer_array,List<Item> & fields,uint flags)164 void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
165                                       List<Item> &fields, uint flags)
166 {
167   for (uint i=0; i < window_func()->argument_count(); i++)
168   {
169     Item **p_item= &window_func()->arguments()[i];
170     (*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags);
171   }
172   window_func()->setup_caches(thd);
173 }
174 
check_result_type_of_order_item()175 bool Item_window_func::check_result_type_of_order_item()
176 {
177   switch (window_func()->sum_func()) {
178   case Item_sum::PERCENTILE_CONT_FUNC:
179   {
180     Item_result rtype= window_spec->order_list->first->item[0]->cmp_type();
181     // TODO (varun) : support date type in percentile_cont function
182     if (rtype != REAL_RESULT && rtype != INT_RESULT &&
183         rtype != DECIMAL_RESULT && rtype != TIME_RESULT)
184     {
185       my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
186       return true;
187     }
188     return false;
189   }
190   case Item_sum::PERCENTILE_DISC_FUNC:
191   {
192     Item *src_item= window_spec->order_list->first->item[0];
193     Item_result rtype= src_item->cmp_type();
194     // TODO-10.5: Fix MDEV-20280 PERCENTILE_DISC() rejects temporal and string input
195     if (rtype != REAL_RESULT && rtype != INT_RESULT && rtype != DECIMAL_RESULT)
196     {
197       my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
198       return true;
199     }
200     Item_sum_percentile_disc *func=
201       static_cast<Item_sum_percentile_disc*>(window_func());
202     func->set_handler(src_item->type_handler());
203     func->Type_std_attributes::set(src_item);
204     Type_std_attributes::set(src_item);
205     return false;
206   }
207   default:
208     break;
209   }
210   return FALSE;
211 }
212 
213 /*
214   This must be called before attempting to compute the window function values.
215   @detail
216     If we attempt to do it in fix_fields(), partition_fields will refer
217     to the original window function arguments.
218     We need it to refer to temp.table columns.
219 */
220 
setup_window_func(THD * thd,Window_spec * window_spec)221 void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
222 {
223   /* TODO: move this into Item_window_func? */
224   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
225   peer_tracker->init();
226   clear();
227 }
228 
setup_window_func(THD * thd,Window_spec * window_spec)229 void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
230 {
231   /* TODO: consider moving this && Item_sum_rank's implementation */
232   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
233   peer_tracker->init();
234   clear();
235 }
236 
setup_window_func(THD * thd,Window_spec * window_spec)237 void Item_sum_percentile_disc::setup_window_func(THD *thd, Window_spec *window_spec)
238 {
239   order_item= window_spec->order_list->first->item[0];
240   if (!(value= order_item->get_cache(thd)))
241     return;
242   value->setup(thd, order_item);
243   value->store(order_item);
244 }
245 
setup_window_func(THD * thd,Window_spec * window_spec)246 void Item_sum_percentile_cont::setup_window_func(THD *thd, Window_spec *window_spec)
247 {
248   order_item= window_spec->order_list->first->item[0];
249   /* TODO(varun): need to discuss and finalise what type should we
250      return for percentile cont functions
251   */
252   if (!(ceil_value= order_item->get_cache(thd)))
253     return;
254   ceil_value->setup(thd, order_item);
255   ceil_value->store(order_item);
256 
257   if (!(floor_value= order_item->get_cache(thd)))
258     return;
259   floor_value->setup(thd, order_item);
260   floor_value->store(order_item);
261 }
fix_fields(THD * thd,Item ** ref)262 bool Item_sum_percentile_cont::fix_fields(THD *thd, Item **ref)
263 {
264   bool res;
265   res= Item_sum_num::fix_fields(thd, ref);
266   if (res)
267     return res;
268 
269   switch(args[0]->cmp_type())
270   {
271     case DECIMAL_RESULT:
272     case REAL_RESULT:
273     case INT_RESULT:
274       break;
275     default:
276       my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
277       return TRUE;
278   }
279   return res;
280 }
fix_fields(THD * thd,Item ** ref)281 bool Item_sum_percentile_disc::fix_fields(THD *thd, Item **ref)
282 {
283   bool res;
284   res= Item_sum_num::fix_fields(thd, ref);
285   if (res)
286     return res;
287 
288   switch(args[0]->cmp_type())
289   {
290     case DECIMAL_RESULT:
291     case REAL_RESULT:
292     case INT_RESULT:
293       break;
294     default:
295       my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
296       return TRUE;
297   }
298   return res;
299 
300 }
301 
add()302 bool Item_sum_dense_rank::add()
303 {
304   if (peer_tracker->check_if_next_group() || first_add)
305   {
306     first_add= false;
307     dense_rank++;
308   }
309 
310   return false;
311 }
312 
313 
add()314 bool Item_sum_rank::add()
315 {
316   row_number++;
317   if (peer_tracker->check_if_next_group())
318   {
319     /* Row value changed */
320     cur_rank= row_number;
321   }
322   return false;
323 }
324 
add()325 bool Item_sum_percent_rank::add()
326 {
327   row_number++;
328   if (peer_tracker->check_if_next_group())
329   {
330     /* Row value changed. */
331     cur_rank= row_number;
332   }
333   return false;
334 }
335 
setup_window_func(THD * thd,Window_spec * window_spec)336 void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
337 {
338   /* TODO: move this into Item_window_func? */
339   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
340   peer_tracker->init();
341   clear();
342 }
343 
344 
fix_fields(THD * thd,Item ** ref)345 bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref)
346 {
347   DBUG_ASSERT(fixed == 0);
348 
349   if (init_sum_func_check(thd))
350     return TRUE;
351 
352   for (uint i= 0; i < arg_count; i++)
353   {
354     if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
355       return TRUE;
356     with_window_func|= args[i]->with_window_func;
357   }
358 
359   for (uint i= 0; i < arg_count && !m_with_subquery; i++)
360     m_with_subquery|= args[i]->with_subquery();
361 
362   if (fix_length_and_dec())
363     return true;
364 
365   setup_hybrid(thd, args[0]);
366   result_field=0;
367 
368   if (check_sum_func(thd, ref))
369     return TRUE;
370   for (uint i= 0; i < arg_count; i++)
371   {
372     orig_args[i]= args[i];
373   }
374   fixed= 1;
375   return FALSE;
376 }
377 
378 
fix_length_and_dec()379 bool Item_sum_hybrid_simple::fix_length_and_dec()
380 {
381   maybe_null= null_value= true;
382   return args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this);
383 }
384 
385 
add()386 bool Item_sum_hybrid_simple::add()
387 {
388   value->store(args[0]);
389   value->cache_value();
390   null_value= value->null_value;
391   return false;
392 }
393 
setup_hybrid(THD * thd,Item * item)394 void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item)
395 {
396   if (!(value= item->get_cache(thd)))
397     return;
398   value->setup(thd, item);
399   value->store(item);
400   if (!item->const_item())
401     value->set_used_tables(RAND_TABLE_BIT);
402   collation.set(item->collation);
403 }
404 
val_real()405 double Item_sum_hybrid_simple::val_real()
406 {
407   DBUG_ASSERT(fixed == 1);
408   if (null_value)
409     return 0.0;
410   double retval= value->val_real();
411   if ((null_value= value->null_value))
412     DBUG_ASSERT(retval == 0.0);
413   return retval;
414 }
415 
val_int()416 longlong Item_sum_hybrid_simple::val_int()
417 {
418   DBUG_ASSERT(fixed == 1);
419   if (null_value)
420     return 0;
421   longlong retval= value->val_int();
422   if ((null_value= value->null_value))
423     DBUG_ASSERT(retval == 0);
424   return retval;
425 }
426 
val_decimal(my_decimal * val)427 my_decimal *Item_sum_hybrid_simple::val_decimal(my_decimal *val)
428 {
429   DBUG_ASSERT(fixed == 1);
430   if (null_value)
431     return 0;
432   my_decimal *retval= value->val_decimal(val);
433   if ((null_value= value->null_value))
434     DBUG_ASSERT(retval == NULL);
435   return retval;
436 }
437 
438 String *
val_str(String * str)439 Item_sum_hybrid_simple::val_str(String *str)
440 {
441   DBUG_ASSERT(fixed == 1);
442   if (null_value)
443     return 0;
444   String *retval= value->val_str(str);
445   if ((null_value= value->null_value))
446     DBUG_ASSERT(retval == NULL);
447   return retval;
448 }
449 
get_date(MYSQL_TIME * ltime,ulonglong fuzzydate)450 bool Item_sum_hybrid_simple::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
451 {
452   DBUG_ASSERT(fixed == 1);
453   if (null_value)
454     return true;
455   bool retval= value->get_date(ltime, fuzzydate);
456   if ((null_value= value->null_value))
457     DBUG_ASSERT(retval == true);
458   return retval;
459 }
460 
create_tmp_field(bool group,TABLE * table)461 Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
462 {
463   DBUG_ASSERT(0);
464   return NULL;
465 }
466 
reset_field()467 void Item_sum_hybrid_simple::reset_field()
468 {
469   switch(result_type()) {
470   case STRING_RESULT:
471   {
472     char buff[MAX_FIELD_WIDTH];
473     String tmp(buff,sizeof(buff),result_field->charset()),*res;
474 
475     res=args[0]->val_str(&tmp);
476     if (args[0]->null_value)
477     {
478       result_field->set_null();
479       result_field->reset();
480     }
481     else
482     {
483       result_field->set_notnull();
484       result_field->store(res->ptr(),res->length(),tmp.charset());
485     }
486     break;
487   }
488   case INT_RESULT:
489   {
490     longlong nr=args[0]->val_int();
491 
492     if (maybe_null)
493     {
494       if (args[0]->null_value)
495       {
496 	nr=0;
497 	result_field->set_null();
498       }
499       else
500 	result_field->set_notnull();
501     }
502     result_field->store(nr, unsigned_flag);
503     break;
504   }
505   case REAL_RESULT:
506   {
507     double nr= args[0]->val_real();
508 
509     if (maybe_null)
510     {
511       if (args[0]->null_value)
512       {
513 	nr=0.0;
514 	result_field->set_null();
515       }
516       else
517 	result_field->set_notnull();
518     }
519     result_field->store(nr);
520     break;
521   }
522   case DECIMAL_RESULT:
523   {
524     my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff);
525 
526     if (maybe_null)
527     {
528       if (args[0]->null_value)
529         result_field->set_null();
530       else
531         result_field->set_notnull();
532     }
533     /*
534       We must store zero in the field as we will use the field value in
535       add()
536     */
537     if (!arg_dec)                               // Null
538       arg_dec= &decimal_zero;
539     result_field->store_decimal(arg_dec);
540     break;
541   }
542   case ROW_RESULT:
543   case TIME_RESULT:
544     DBUG_ASSERT(0);
545   }
546 }
547 
update_field()548 void Item_sum_hybrid_simple::update_field()
549 {
550   DBUG_ASSERT(0);
551 }
552 
print(String * str,enum_query_type query_type)553 void Item_window_func::print(String *str, enum_query_type query_type)
554 {
555   if (only_single_element_order_list())
556   {
557     print_for_percentile_functions(str, query_type);
558     return;
559   }
560   window_func()->print(str, query_type);
561   str->append(" over ");
562   if (!window_spec)
563     str->append(window_name);
564   else
565     window_spec->print(str, query_type);
566 }
print_for_percentile_functions(String * str,enum_query_type query_type)567 void Item_window_func::print_for_percentile_functions(String *str, enum_query_type query_type)
568 {
569   window_func()->print(str, query_type);
570   str->append(" within group ");
571   str->append('(');
572   window_spec->print_order(str,query_type);
573   str->append(')');
574   str->append(" over ");
575   str->append('(');
576   window_spec->print_partition(str,query_type);
577   str->append(')');
578 }
579