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 
126   if (fix_length_and_dec())
127     return TRUE;
128 
129   max_length= window_func()->max_length;
130   maybe_null= window_func()->maybe_null;
131 
132   fixed= 1;
133   set_phase_to_initial();
134   return false;
135 }
136 
137 
138 /*
139   @detail
140     Window function evaluates its arguments when it is scanning the temporary
141     table in partition/order-by order. That is, arguments should be read from
142     the temporary table, not from the original base columns.
143 
144     In order for this to work, we need to call "split_sum_func" for each
145     argument. The effect of the call is:
146      1. the argument is added into ref_pointer_array. This will cause the
147         argument to be saved in the temp.table
148      2. argument item is replaced with an Item_ref object. this object refers
149         the argument through the ref_pointer_array.
150 
151     then, change_to_use_tmp_fields() will replace ref_pointer_array with an
152     array that points to the temp.table fields.
153     This way, when window_func attempts to evaluate its arguments, it will use
154     Item_ref objects which will read data from the temp.table.
155 
156     Note: Before window functions, aggregate functions never needed to do such
157     transformations on their arguments. This is because grouping operation
158     does not need to read from the temp.table.
159     (Q: what happens when we first sort and then do grouping in a
160       group-after-group mode? dont group by items read from temp.table, then?)
161 */
162 
split_sum_func(THD * thd,Ref_ptr_array ref_pointer_array,List<Item> & fields,uint flags)163 void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
164                                       List<Item> &fields, uint flags)
165 {
166   for (uint i=0; i < window_func()->argument_count(); i++)
167   {
168     Item **p_item= &window_func()->arguments()[i];
169     (*p_item)->split_sum_func2(thd, ref_pointer_array, fields, p_item, flags);
170   }
171   window_func()->setup_caches(thd);
172 }
173 
check_result_type_of_order_item()174 bool Item_window_func::check_result_type_of_order_item()
175 {
176   switch (window_func()->sum_func()) {
177   case Item_sum::PERCENTILE_CONT_FUNC:
178   {
179     Item_result rtype= window_spec->order_list->first->item[0]->cmp_type();
180     // TODO (varun) : support date type in percentile_cont function
181     if (rtype != REAL_RESULT && rtype != INT_RESULT &&
182         rtype != DECIMAL_RESULT && rtype != TIME_RESULT)
183     {
184       my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
185       return true;
186     }
187     return false;
188   }
189   case Item_sum::PERCENTILE_DISC_FUNC:
190   {
191     Item *src_item= window_spec->order_list->first->item[0];
192     Item_result rtype= src_item->cmp_type();
193     // TODO-10.5: Fix MDEV-20280 PERCENTILE_DISC() rejects temporal and string input
194     if (rtype != REAL_RESULT && rtype != INT_RESULT && rtype != DECIMAL_RESULT)
195     {
196       my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
197       return true;
198     }
199     Item_sum_percentile_disc *func=
200       static_cast<Item_sum_percentile_disc*>(window_func());
201     func->set_handler(src_item->type_handler());
202     func->Type_std_attributes::set(src_item);
203     Type_std_attributes::set(src_item);
204     return false;
205   }
206   default:
207     break;
208   }
209   return FALSE;
210 }
211 
212 /*
213   This must be called before attempting to compute the window function values.
214   @detail
215     If we attempt to do it in fix_fields(), partition_fields will refer
216     to the original window function arguments.
217     We need it to refer to temp.table columns.
218 */
219 
setup_window_func(THD * thd,Window_spec * window_spec)220 void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
221 {
222   /* TODO: move this into Item_window_func? */
223   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
224   peer_tracker->init();
225   clear();
226 }
227 
setup_window_func(THD * thd,Window_spec * window_spec)228 void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
229 {
230   /* TODO: consider moving this && Item_sum_rank's implementation */
231   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
232   peer_tracker->init();
233   clear();
234 }
235 
setup_window_func(THD * thd,Window_spec * window_spec)236 void Item_sum_percentile_disc::setup_window_func(THD *thd, Window_spec *window_spec)
237 {
238   order_item= window_spec->order_list->first->item[0];
239   if (!(value= order_item->get_cache(thd)))
240     return;
241   value->setup(thd, order_item);
242   value->store(order_item);
243 }
244 
setup_window_func(THD * thd,Window_spec * window_spec)245 void Item_sum_percentile_cont::setup_window_func(THD *thd, Window_spec *window_spec)
246 {
247   order_item= window_spec->order_list->first->item[0];
248   /* TODO(varun): need to discuss and finalise what type should we
249      return for percentile cont functions
250   */
251   if (!(ceil_value= order_item->get_cache(thd)))
252     return;
253   ceil_value->setup(thd, order_item);
254   ceil_value->store(order_item);
255 
256   if (!(floor_value= order_item->get_cache(thd)))
257     return;
258   floor_value->setup(thd, order_item);
259   floor_value->store(order_item);
260 }
fix_fields(THD * thd,Item ** ref)261 bool Item_sum_percentile_cont::fix_fields(THD *thd, Item **ref)
262 {
263   bool res;
264   res= Item_sum_num::fix_fields(thd, ref);
265   if (res)
266     return res;
267 
268   switch(args[0]->cmp_type())
269   {
270     case DECIMAL_RESULT:
271     case REAL_RESULT:
272     case INT_RESULT:
273       break;
274     default:
275       my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
276       return TRUE;
277   }
278   return res;
279 }
fix_fields(THD * thd,Item ** ref)280 bool Item_sum_percentile_disc::fix_fields(THD *thd, Item **ref)
281 {
282   bool res;
283   res= Item_sum_num::fix_fields(thd, ref);
284   if (res)
285     return res;
286 
287   switch(args[0]->cmp_type())
288   {
289     case DECIMAL_RESULT:
290     case REAL_RESULT:
291     case INT_RESULT:
292       break;
293     default:
294       my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
295       return TRUE;
296   }
297   return res;
298 
299 }
300 
add()301 bool Item_sum_dense_rank::add()
302 {
303   if (peer_tracker->check_if_next_group() || first_add)
304   {
305     first_add= false;
306     dense_rank++;
307   }
308 
309   return false;
310 }
311 
312 
add()313 bool Item_sum_rank::add()
314 {
315   row_number++;
316   if (peer_tracker->check_if_next_group())
317   {
318     /* Row value changed */
319     cur_rank= row_number;
320   }
321   return false;
322 }
323 
add()324 bool Item_sum_percent_rank::add()
325 {
326   row_number++;
327   if (peer_tracker->check_if_next_group())
328   {
329     /* Row value changed. */
330     cur_rank= row_number;
331   }
332   return false;
333 }
334 
setup_window_func(THD * thd,Window_spec * window_spec)335 void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
336 {
337   /* TODO: move this into Item_window_func? */
338   peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
339   peer_tracker->init();
340   clear();
341 }
342 
343 
fix_fields(THD * thd,Item ** ref)344 bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref)
345 {
346   DBUG_ASSERT(fixed == 0);
347 
348   if (init_sum_func_check(thd))
349     return TRUE;
350 
351   for (uint i= 0; i < arg_count; i++)
352   {
353     if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
354       return TRUE;
355     with_window_func|= args[i]->with_window_func;
356   }
357 
358   for (uint i= 0; i < arg_count && !m_with_subquery; i++)
359     m_with_subquery|= args[i]->with_subquery();
360 
361   if (fix_length_and_dec())
362     return true;
363 
364   setup_hybrid(thd, args[0]);
365   result_field=0;
366 
367   if (check_sum_func(thd, ref))
368     return TRUE;
369   for (uint i= 0; i < arg_count; i++)
370   {
371     orig_args[i]= args[i];
372   }
373   fixed= 1;
374   return FALSE;
375 }
376 
377 
fix_length_and_dec()378 bool Item_sum_hybrid_simple::fix_length_and_dec()
379 {
380   maybe_null= null_value= true;
381   return args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this);
382 }
383 
384 
add()385 bool Item_sum_hybrid_simple::add()
386 {
387   value->store(args[0]);
388   value->cache_value();
389   null_value= value->null_value;
390   return false;
391 }
392 
setup_hybrid(THD * thd,Item * item)393 void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item)
394 {
395   if (!(value= item->get_cache(thd)))
396     return;
397   value->setup(thd, item);
398   value->store(item);
399   if (!item->const_item())
400     value->set_used_tables(RAND_TABLE_BIT);
401   collation.set(item->collation);
402 }
403 
val_real()404 double Item_sum_hybrid_simple::val_real()
405 {
406   DBUG_ASSERT(fixed == 1);
407   if (null_value)
408     return 0.0;
409   double retval= value->val_real();
410   if ((null_value= value->null_value))
411     DBUG_ASSERT(retval == 0.0);
412   return retval;
413 }
414 
val_int()415 longlong Item_sum_hybrid_simple::val_int()
416 {
417   DBUG_ASSERT(fixed == 1);
418   if (null_value)
419     return 0;
420   longlong retval= value->val_int();
421   if ((null_value= value->null_value))
422     DBUG_ASSERT(retval == 0);
423   return retval;
424 }
425 
val_decimal(my_decimal * val)426 my_decimal *Item_sum_hybrid_simple::val_decimal(my_decimal *val)
427 {
428   DBUG_ASSERT(fixed == 1);
429   if (null_value)
430     return 0;
431   my_decimal *retval= value->val_decimal(val);
432   if ((null_value= value->null_value))
433     DBUG_ASSERT(retval == NULL);
434   return retval;
435 }
436 
437 String *
val_str(String * str)438 Item_sum_hybrid_simple::val_str(String *str)
439 {
440   DBUG_ASSERT(fixed == 1);
441   if (null_value)
442     return 0;
443   String *retval= value->val_str(str);
444   if ((null_value= value->null_value))
445     DBUG_ASSERT(retval == NULL);
446   return retval;
447 }
448 
val_native(THD * thd,Native * to)449 bool Item_sum_hybrid_simple::val_native(THD *thd, Native *to)
450 {
451   DBUG_ASSERT(fixed == 1);
452   if (null_value)
453     return true;
454   return val_native_from_item(thd, value, to);
455 }
456 
get_date(THD * thd,MYSQL_TIME * ltime,date_mode_t fuzzydate)457 bool Item_sum_hybrid_simple::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
458 {
459   DBUG_ASSERT(fixed == 1);
460   if (null_value)
461     return true;
462   bool retval= value->get_date(thd, ltime, fuzzydate);
463   if ((null_value= value->null_value))
464     DBUG_ASSERT(retval == true);
465   return retval;
466 }
467 
create_tmp_field(bool group,TABLE * table)468 Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
469 {
470   DBUG_ASSERT(0);
471   return NULL;
472 }
473 
reset_field()474 void Item_sum_hybrid_simple::reset_field()
475 {
476   switch(result_type()) {
477   case STRING_RESULT:
478   {
479     char buff[MAX_FIELD_WIDTH];
480     String tmp(buff,sizeof(buff),result_field->charset()),*res;
481 
482     res=args[0]->val_str(&tmp);
483     if (args[0]->null_value)
484     {
485       result_field->set_null();
486       result_field->reset();
487     }
488     else
489     {
490       result_field->set_notnull();
491       result_field->store(res->ptr(),res->length(),tmp.charset());
492     }
493     break;
494   }
495   case INT_RESULT:
496   {
497     longlong nr=args[0]->val_int();
498 
499     if (maybe_null)
500     {
501       if (args[0]->null_value)
502       {
503 	nr=0;
504 	result_field->set_null();
505       }
506       else
507 	result_field->set_notnull();
508     }
509     result_field->store(nr, unsigned_flag);
510     break;
511   }
512   case REAL_RESULT:
513   {
514     double nr= args[0]->val_real();
515 
516     if (maybe_null)
517     {
518       if (args[0]->null_value)
519       {
520 	nr=0.0;
521 	result_field->set_null();
522       }
523       else
524 	result_field->set_notnull();
525     }
526     result_field->store(nr);
527     break;
528   }
529   case DECIMAL_RESULT:
530   {
531     VDec arg_dec(args[0]);
532 
533     if (maybe_null)
534     {
535       if (arg_dec.is_null())
536         result_field->set_null();
537       else
538         result_field->set_notnull();
539     }
540     /*
541       We must store zero in the field as we will use the field value in
542       add()
543     */
544     result_field->store_decimal(arg_dec.ptr_or(&decimal_zero));
545     break;
546   }
547   case ROW_RESULT:
548   case TIME_RESULT:
549     DBUG_ASSERT(0);
550   }
551 }
552 
update_field()553 void Item_sum_hybrid_simple::update_field()
554 {
555   DBUG_ASSERT(0);
556 }
557 
print(String * str,enum_query_type query_type)558 void Item_window_func::print(String *str, enum_query_type query_type)
559 {
560   if (only_single_element_order_list())
561   {
562     print_for_percentile_functions(str, query_type);
563     return;
564   }
565   window_func()->print(str, query_type);
566   str->append(" over ");
567   if (!window_spec)
568     str->append(window_name);
569   else
570     window_spec->print(str, query_type);
571 }
print_for_percentile_functions(String * str,enum_query_type query_type)572 void Item_window_func::print_for_percentile_functions(String *str, enum_query_type query_type)
573 {
574   window_func()->print(str, query_type);
575   str->append(" within group ");
576   str->append('(');
577   window_spec->print_order(str,query_type);
578   str->append(')');
579   str->append(" over ");
580   str->append('(');
581   window_spec->print_partition(str,query_type);
582   str->append(')');
583 }
584