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