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