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