item_timefunc.cc 73.4 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17 18 19
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


/* This file defines all time functions */

20
#ifdef USE_PRAGMA_IMPLEMENTATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21 22
#pragma implementation				// gcc: Class implementation
#endif
23 24

#include "mysql_priv.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
25 26 27
#include <m_ctype.h>
#include <time.h>

28
/* TODO: Move month and days to language files */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29

30 31
#define MAX_DAY_NUMBER 3652424L

32 33 34 35
static const char *month_names[]=
{
  "January", "February", "March", "April", "May", "June", "July", "August",
  "September", "October", "November", "December", NullS
36
};
37 38

TYPELIB month_names_typelib=
39
{ array_elements(month_names)-1,"", month_names, NULL };
40 41 42 43 44 45 46 47

static const char *day_names[]=
{
  "Monday", "Tuesday", "Wednesday",
  "Thursday", "Friday", "Saturday" ,"Sunday", NullS
};

TYPELIB day_names_typelib=
48
{ array_elements(day_names)-1,"", day_names, NULL};
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69


/*
  OPTIMIZATION TODO:
   - Replace the switch with a function that should be called for each
     date type.
   - Remove sprintf and opencode the conversion, like we do in
     Field_datetime.

  The reason for this functions existence is that as we don't have a
  way to know if a datetime/time value has microseconds in them
  we are now only adding microseconds to the output if the
  value has microseconds.

  We can't use a standard make_date_time() for this as we don't know
  if someone will use %f in the format specifier in which case we would get
  the microseconds twice.
*/

static bool make_datetime(date_time_format_types format, TIME *ltime,
			  String *str)
70
{
71 72 73
  char *buff;
  CHARSET_INFO *cs= &my_charset_bin;
  uint length= 30;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
74

75 76 77
  if (str->alloc(length))
    return 1;
  buff= (char*) str->ptr();
78

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  switch (format) {
  case TIME_ONLY:
    length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d",
			       ltime->neg ? "-" : "",
			       ltime->hour, ltime->minute, ltime->second);
    break;
  case TIME_MICROSECOND:
    length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d.%06d",
			       ltime->neg ? "-" : "",
			       ltime->hour, ltime->minute, ltime->second,
			       ltime->second_part);
    break;
  case DATE_ONLY:
    length= cs->cset->snprintf(cs, buff, length, "%04d-%02d-%02d",
			       ltime->year, ltime->month, ltime->day);
    break;
  case DATE_TIME:
    length= cs->cset->snprintf(cs, buff, length,
			       "%04d-%02d-%02d %02d:%02d:%02d",
			       ltime->year, ltime->month, ltime->day,
			       ltime->hour, ltime->minute, ltime->second);
    break;
  case DATE_TIME_MICROSECOND:
    length= cs->cset->snprintf(cs, buff, length,
			       "%04d-%02d-%02d %02d:%02d:%02d.%06d",
			       ltime->year, ltime->month, ltime->day,
			       ltime->hour, ltime->minute, ltime->second,
			       ltime->second_part);
    break;
108
  }
109 110 111 112

  str->length(length);
  str->set_charset(cs);
  return 0;
113
}
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
114 115


116 117 118 119 120 121 122
/*
  Date formats corresponding to compound %r and %T conversion specifiers

  Note: We should init at least first element of "positions" array
        (first member) or hpux11 compiler will die horribly.
*/
static DATE_TIME_FORMAT time_ampm_format= {{0}, '\0', 0,
123
                                           {(char *)"%I:%i:%S %p", 11}};
124
static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
125 126
                                            {(char *)"%H:%i:%S", 8}};

127
/*
128 129
  Extract datetime value to TIME struct from string value
  according to format string. 
130 131 132 133 134 135 136

  SYNOPSIS
    extract_date_time()
    format		date/time format specification
    val			String to decode
    length		Length of string
    l_time		Store result here
137 138 139
    cached_timestamp_type 
                       It uses to get an appropriate warning
                       in the case when the value is truncated.
140 141 142 143 144 145 146 147 148 149 150
    sub_pattern_end    if non-zero then we are parsing string which
                       should correspond compound specifier (like %T or
                       %r) and this parameter is pointer to place where
                       pointer to end of string matching this specifier
                       should be stored.
    NOTE
     Possibility to parse strings matching to patterns equivalent to compound
     specifiers is mainly intended for use from inside of this function in
     order to understand %T and %r conversion specifiers, so number of
     conversion specifiers that can be used in such sub-patterns is limited.
     Also most of checks are skipped in this case.
151

152 153 154
     If one adds new format specifiers to this function he should also
     consider adding them to get_date_time_result_type() function.

155 156 157
    RETURN
      0	ok
      1	error
158
*/
159 160

static bool extract_date_time(DATE_TIME_FORMAT *format,
161
			      const char *val, uint length, TIME *l_time,
162 163
                              timestamp_type cached_timestamp_type,
                              const char **sub_pattern_end)
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
164
{
165
  int weekday= 0, yearday= 0, daypart= 0;
166
  int week_number= -1;
167
  int error= 0;
168
  int  strict_week_number_year= -1;
169
  int frac_part;
170 171 172 173
  bool usa_time= 0;
  bool sunday_first_n_first_week_non_iso;
  bool strict_week_number;
  bool strict_week_number_year_type;
174
  const char *val_begin= val;
175 176
  const char *val_end= val + length;
  const char *ptr= format->format.str;
177
  const char *end= ptr + format->format.length;
178
  CHARSET_INFO *cs= &my_charset_bin;
179
  DBUG_ENTER("extract_date_time");
180

181
  LINT_INIT(strict_week_number);
182 183 184
  /* Remove valgrind varnings when using gcc 3.3 and -O1 */
  PURIFY_OR_LINT_INIT(strict_week_number_year_type);
  PURIFY_OR_LINT_INIT(sunday_first_n_first_week_non_iso);
185 186 187

  if (!sub_pattern_end)
    bzero((char*) l_time, sizeof(*l_time));
188 189

  for (; ptr != end && val != val_end; ptr++)
190
  {
191

192 193
    if (*ptr == '%' && ptr+1 != end)
    {
194 195 196 197
      int val_len;
      char *tmp;

      /* Skip pre-space between each argument */
198
      while (val != val_end && my_isspace(cs, *val))
199 200 201
	val++;

      val_len= (uint) (val_end - val);
202
      switch (*++ptr) {
203
	/* Year */
204
      case 'Y':
205 206 207
	tmp= (char*) val + min(4, val_len);
	l_time->year= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
208 209
	break;
      case 'y':
210 211 212
	tmp= (char*) val + min(2, val_len);
	l_time->year= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
213 214
	l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
	break;
215 216

	/* Month */
217
      case 'm':
218 219 220 221 222 223 224 225 226 227
      case 'c':
	tmp= (char*) val + min(2, val_len);
	l_time->month= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
	break;
      case 'M':
      case 'b':
	if ((l_time->month= check_word(&month_names_typelib,
				       val, val_end, &val)) <= 0)
	  goto err;
228
	break;
229
	/* Day */
230
      case 'd':
231 232 233 234
      case 'e':
	tmp= (char*) val + min(2, val_len);
	l_time->day= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
235 236
	break;
      case 'D':
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	tmp= (char*) val + min(2, val_len);
	l_time->day= (int) my_strtoll10(val, &tmp, &error);
	/* Skip 'st, 'nd, 'th .. */
	val= tmp + min((int) (end-tmp), 2);
	break;

	/* Hour */
      case 'h':
      case 'I':
      case 'l':
	usa_time= 1;
	/* fall through */
      case 'k':
      case 'H':
	tmp= (char*) val + min(2, val_len);
	l_time->hour= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
254
	break;
255 256

	/* Minute */
257
      case 'i':
258 259 260
	tmp= (char*) val + min(2, val_len);
	l_time->minute= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
261
	break;
262 263

	/* Second */
264 265
      case 's':
      case 'S':
266 267 268
	tmp= (char*) val + min(2, val_len);
	l_time->second= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
269
	break;
270 271 272 273

	/* Second part */
      case 'f':
	tmp= (char*) val_end;
274 275
	if (tmp - val > 6)
	  tmp= (char*) val + 6;
monty@mysql.com's avatar
monty@mysql.com committed
276
	l_time->second_part= (int) my_strtoll10(val, &tmp, &error);
277 278 279
	frac_part= 6 - (tmp - val);
	if (frac_part > 0)
	  l_time->second_part*= (ulong) log_10_int[frac_part];
280
	val= tmp;
281
	break;
282 283 284 285 286 287 288 289 290 291 292 293 294

	/* AM / PM */
      case 'p':
	if (val_len < 2 || ! usa_time)
	  goto err;
	if (!my_strnncoll(&my_charset_latin1,
			  (const uchar *) val, 2, 
			  (const uchar *) "PM", 2))
	  daypart= 12;
	else if (my_strnncoll(&my_charset_latin1,
			      (const uchar *) val, 2, 
			      (const uchar *) "AM", 2))
	  goto err;
295
	val+= 2;
296
	break;
297 298

	/* Exotic things */
299 300
      case 'W':
      case 'a':
301 302
	if ((weekday= check_word(&day_names_typelib, val, val_end, &val)) <= 0)
	  goto err;
303 304
	break;
      case 'w':
305
	tmp= (char*) val + 1;
306
	if ((weekday= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
307 308
	    weekday >= 7)
	  goto err;
309 310 311
        /* We should use the same 1 - 7 scale for %w as for %W */
        if (!weekday)
          weekday= 7;
312
	val= tmp;
313 314
	break;
      case 'j':
315 316 317
	tmp= (char*) val + min(val_len, 3);
	yearday= (int) my_strtoll10(val, &tmp, &error);
	val= tmp;
318
	break;
319

320 321
        /* Week numbers */
      case 'V':
322
      case 'U':
323
      case 'v':
324
      case 'u':
325 326
        sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V');
        strict_week_number= (*ptr=='V' || *ptr=='v');
327
	tmp= (char*) val + min(val_len, 2);
328 329 330 331
	if ((week_number= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
            strict_week_number && !week_number ||
            week_number > 53)
          goto err;
332
	val= tmp;
333
	break;
334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
        /* Year used with 'strict' %V and %v week numbers */
      case 'X':
      case 'x':
        strict_week_number_year_type= (*ptr=='X');
        tmp= (char*) val + min(4, val_len);
        strict_week_number_year= (int) my_strtoll10(val, &tmp, &error);
        val= tmp;
        break;

        /* Time in AM/PM notation */
      case 'r':
        error= extract_date_time(&time_ampm_format, val,
                                 (uint)(val_end - val), l_time,
                                 cached_timestamp_type, &val);
        break;

        /* Time in 24-hour notation */
      case 'T':
        error= extract_date_time(&time_24hrs_format, val,
                                 (uint)(val_end - val), l_time,
                                 cached_timestamp_type, &val);
        break;

        /* Conversion specifiers that match classes of characters */
359 360 361 362 363 364 365 366 367 368 369 370
      case '.':
	while (my_ispunct(cs, *val) && val != val_end)
	  val++;
	break;
      case '@':
	while (my_isalpha(cs, *val) && val != val_end)
	  val++;
	break;
      case '#':
	while (my_isdigit(cs, *val) && val != val_end)
	  val++;
	break;
371
      default:
372
	goto err;
373
      }
374 375
      if (error)				// Error from my_strtoll10
	goto err;
376
    }
377
    else if (!my_isspace(cs, *ptr))
378
    {
379 380 381
      if (*val != *ptr)
	goto err;
      val++;
382 383 384 385 386
    }
  }
  if (usa_time)
  {
    if (l_time->hour > 12 || l_time->hour < 1)
387
      goto err;
388 389
    l_time->hour= l_time->hour%12+daypart;
  }
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
390

391 392 393 394 395 396 397 398 399 400
  /*
    If we are recursively called for parsing string matching compound
    specifiers we are already done.
  */
  if (sub_pattern_end)
  {
    *sub_pattern_end= val;
    DBUG_RETURN(0);
  }

401 402 403
  if (yearday > 0)
  {
    uint days= calc_daynr(l_time->year,1,1) +  yearday - 1;
404 405 406
    if (days <= 0 || days >= MAX_DAY_NUMBER)
      goto err;
    get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
407
  }
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
408

409 410
  if (week_number >= 0 && weekday)
  {
411
    int days;
412 413
    uint weekday_b;

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    /*
      %V,%v require %X,%x resprectively,
      %U,%u should be used with %Y and not %X or %x
    */
    if (strict_week_number &&
        (strict_week_number_year < 0 ||
         strict_week_number_year_type != sunday_first_n_first_week_non_iso) ||
        !strict_week_number && strict_week_number_year >= 0)
      goto err;

    /* Number of days since year 0 till 1st Jan of this year */
    days= calc_daynr((strict_week_number ? strict_week_number_year :
                                           l_time->year),
                     1, 1);
    /* Which day of week is 1st Jan of this year */
    weekday_b= calc_weekday(days, sunday_first_n_first_week_non_iso);

    /*
      Below we are going to sum:
      1) number of days since year 0 till 1st day of 1st week of this year
      2) number of days between 1st week and our week
      3) and position of our day in the week
    */
    if (sunday_first_n_first_week_non_iso)
438
    {
439 440 441
      days+= ((weekday_b == 0) ? 0 : 7) - weekday_b +
             (week_number - 1) * 7 +
             weekday % 7;
442 443 444
    }
    else
    {
445 446 447
      days+= ((weekday_b <= 3) ? 0 : 7) - weekday_b +
             (week_number - 1) * 7 +
             (weekday - 1);
448
    }
449

450 451 452
    if (days <= 0 || days >= MAX_DAY_NUMBER)
      goto err;
    get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
453 454
  }

455 456
  if (l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 || 
      l_time->minute > 59 || l_time->second > 59)
457
    goto err;
458

459 460 461 462 463 464 465 466 467 468 469 470
  if (val != val_end)
  {
    do
    {
      if (!my_isspace(&my_charset_latin1,*val))
      {
	make_truncated_value_warning(current_thd, val_begin, length,
				     cached_timestamp_type);
	break;
      }
    } while (++val != val_end);
  }
471 472
  DBUG_RETURN(0);

473 474 475
err:
  DBUG_RETURN(1);
}
476 477 478


/*
479
  Create a formated date/time value in a string
480 481
*/

482 483
bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time,
		    timestamp_type type, String *str)
484 485 486 487 488 489
{
  char intbuff[15];
  uint days_i;
  uint hours_i;
  uint weekday;
  ulong length;
490 491 492 493 494
  const char *ptr, *end;

  str->length(0);
  str->set_charset(&my_charset_bin);

495 496
  if (l_time->neg)
    str->append("-", 1);
497 498
  
  end= (ptr= format->format.str) + format->format.length;
499 500 501 502 503 504 505 506 507
  for (; ptr != end ; ptr++)
  {
    if (*ptr != '%' || ptr+1 == end)
      str->append(*ptr);
    else
    {
      switch (*++ptr) {
      case 'M':
	if (!l_time->month)
508
	  return 1;
509 510 511 512
	str->append(month_names[l_time->month-1]);
	break;
      case 'b':
	if (!l_time->month)
513 514
	  return 1;
	str->append(month_names[l_time->month-1],3);
515 516
	break;
      case 'W':
517
	if (type == MYSQL_TIMESTAMP_TIME)
518 519 520
	  return 1;
	weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
					 l_time->day),0);
521 522 523
	str->append(day_names[weekday]);
	break;
      case 'a':
524
	if (type == MYSQL_TIMESTAMP_TIME)
525 526 527 528
	  return 1;
	weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
					l_time->day),0);
	str->append(day_names[weekday],3);
529 530
	break;
      case 'D':
531
	if (type == MYSQL_TIMESTAMP_TIME)
532
	  return 1;
533 534 535
	length= int10_to_str(l_time->day, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	if (l_time->day >= 10 &&  l_time->day <= 19)
536
	  str->append("th", 2);
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
	else
	{
	  switch (l_time->day %10) {
	  case 1:
	    str->append("st",2);
	    break;
	  case 2:
	    str->append("nd",2);
	    break;
	  case 3:
	    str->append("rd",2);
	    break;
	  default:
	    str->append("th",2);
	    break;
	  }
	}
	break;
      case 'Y':
	length= int10_to_str(l_time->year, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 4, '0');
	break;
      case 'y':
	length= int10_to_str(l_time->year%100, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'm':
	length= int10_to_str(l_time->month, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'c':
	length= int10_to_str(l_time->month, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	break;
      case 'd':
	length= int10_to_str(l_time->day, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'e':
	length= int10_to_str(l_time->day, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	break;
      case 'f':
	length= int10_to_str(l_time->second_part, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 6, '0');
	break;
      case 'H':
	length= int10_to_str(l_time->hour, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'h':
      case 'I':
	days_i= l_time->hour/24;
	hours_i= (l_time->hour%24 + 11)%12+1 + 24*days_i;
	length= int10_to_str(hours_i, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'i':					/* minutes */
	length= int10_to_str(l_time->minute, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'j':
599
	if (type == MYSQL_TIMESTAMP_TIME)
600 601 602
	  return 1;
	length= int10_to_str(calc_daynr(l_time->year,l_time->month,
					l_time->day) - 
603 604 605 606 607 608 609 610 611
		     calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 3, '0');
	break;
      case 'k':
	length= int10_to_str(l_time->hour, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	break;
      case 'l':
	days_i= l_time->hour/24;
612
	hours_i= (l_time->hour%24 + 11)%12+1;
613 614 615 616 617 618 619 620 621 622
	length= int10_to_str(hours_i, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	break;
      case 'p':
	hours_i= l_time->hour%24;
	str->append(hours_i < 12 ? "AM" : "PM",2);
	break;
      case 'r':
	length= my_sprintf(intbuff, 
		   (intbuff, 
623 624
		    ((l_time->hour % 24) < 12) ?
                    "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
		    (l_time->hour+11)%12+1,
		    l_time->minute,
		    l_time->second));
	str->append(intbuff, length);
	break;
      case 'S':
      case 's':
	length= int10_to_str(l_time->second, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 2, '0');
	break;
      case 'T':
	length= my_sprintf(intbuff, 
		   (intbuff, 
		    "%02d:%02d:%02d", 
		    l_time->hour, 
		    l_time->minute,
		    l_time->second));
	str->append(intbuff, length);
	break;
      case 'U':
      case 'u':
      {
	uint year;
648
	if (type == MYSQL_TIMESTAMP_TIME)
649
	  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
650 651 652 653 654
	length= int10_to_str(calc_week(l_time,
				       (*ptr) == 'U' ?
				       WEEK_FIRST_WEEKDAY : WEEK_MONDAY_FIRST,
				       &year),
			     intbuff, 10) - intbuff;
655 656 657 658 659 660 661
	str->append_with_prefill(intbuff, length, 2, '0');
      }
      break;
      case 'v':
      case 'V':
      {
	uint year;
662
	if (type == MYSQL_TIMESTAMP_TIME)
663
	  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
664 665 666 667 668 669
	length= int10_to_str(calc_week(l_time,
				       ((*ptr) == 'V' ?
					(WEEK_YEAR | WEEK_FIRST_WEEKDAY) :
					(WEEK_YEAR | WEEK_MONDAY_FIRST)),
				       &year),
			     intbuff, 10) - intbuff;
670 671 672 673 674 675 676
	str->append_with_prefill(intbuff, length, 2, '0');
      }
      break;
      case 'x':
      case 'X':
      {
	uint year;
677
	if (type == MYSQL_TIMESTAMP_TIME)
678
	  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
679 680 681 682 683
	(void) calc_week(l_time,
			 ((*ptr) == 'X' ?
			  WEEK_YEAR | WEEK_FIRST_WEEKDAY :
			  WEEK_YEAR | WEEK_MONDAY_FIRST),
			 &year);
684 685 686 687 688
	length= int10_to_str(year, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 4, '0');
      }
      break;
      case 'w':
689
	if (type == MYSQL_TIMESTAMP_TIME)
690 691 692
	  return 1;
	weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
					l_time->day),1);
693 694 695
	length= int10_to_str(weekday, intbuff, 10) - intbuff;
	str->append_with_prefill(intbuff, length, 1, '0');
	break;
696

697 698 699 700 701 702
      default:
	str->append(*ptr);
	break;
      }
    }
  }
703
  return 0;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
704 705
}

706

bk@work.mysql.com's avatar
bk@work.mysql.com committed
707
/*
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
  Get a array of positive numbers from a string object.
  Each number is separated by 1 non digit character
  Return error if there is too many numbers.
  If there is too few numbers, assume that the numbers are left out
  from the high end. This allows one to give:
  DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds.

  SYNOPSIS
    str:            string value
    length:         length of str
    cs:             charset of str
    values:         array of results
    count:          count of elements in result array
    transform_msec: if value is true we suppose
                    that the last part of string value is microseconds
                    and we should transform value to six digit value.
                    For example, '1.1' -> '1.100000'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
725 726
*/

monty@mysql.com's avatar
monty@mysql.com committed
727
static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
monty@mysql.com's avatar
monty@mysql.com committed
728 729
                              uint count, ulonglong *values,
                              bool transform_msec)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
730 731 732
{
  const char *end=str+length;
  uint i;
733
  while (str != end && !my_isdigit(cs,*str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
734 735 736 737
    str++;

  for (i=0 ; i < count ; i++)
  {
738
    longlong value;
739
    const char *start= str;
740
    for (value=0; str != end && my_isdigit(cs,*str) ; str++)
monty@mysql.com's avatar
monty@mysql.com committed
741
      value= value*LL(10) + (longlong) (*str - '0');
742 743 744 745 746 747
    if (transform_msec && i == count - 1) // microseconds always last
    {
      long msec_length= 6 - (str - start);
      if (msec_length > 0)
	value*= (long) log_10_int[msec_length];
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
748
    values[i]= value;
749
    while (str != end && !my_isdigit(cs,*str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
750 751 752 753 754 755
      str++;
    if (str == end && i != count-1)
    {
      i++;
      /* Change values[0...i-1] -> values[0...count-1] */
      bmove_upp((char*) (values+count), (char*) (values+i),
756 757
		sizeof(*values)*i);
      bzero((char*) values, sizeof(*values)*(count-i));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
758 759 760 761 762 763
      break;
    }
  }
  return (str != end);
}

monty@mysql.com's avatar
monty@mysql.com committed
764

765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
/*
  Calculate difference between two datetime values as seconds + microseconds.

  SYNOPSIS
    calc_time_diff()
      l_time1         - TIME/DATE/DATETIME value
      l_time2         - TIME/DATE/DATETIME value
      l_sign          - 1 absolute values are substracted,
                        -1 absolute values are added.
      seconds_out     - Out parameter where difference between
                        l_time1 and l_time2 in seconds is stored.
      microseconds_out- Out parameter where microsecond part of difference
                        between l_time1 and l_time2 is stored.

  NOTE
    This function calculates difference between l_time1 and l_time2 absolute
    values. So one should set l_sign and correct result if he want to take
    signs into account (i.e. for TIME values).

  RETURN VALUES
    Returns sign of difference.
    1 means negative result
    0 means positive result

*/

static bool calc_time_diff(TIME *l_time1, TIME *l_time2, int l_sign,
                           longlong *seconds_out, long *microseconds_out)
{
  long days;
  bool neg;
  longlong microseconds;

  /*
    We suppose that if first argument is MYSQL_TIMESTAMP_TIME
    the second argument should be TIMESTAMP_TIME also.
    We should check it before calc_time_diff call.
  */
  if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value
804
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
805 806 807 808 809 810
  else
  {
    days= calc_daynr((uint) l_time1->year,
		     (uint) l_time1->month,
		     (uint) l_time1->day);
    if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
811
      days-= l_sign * (long)l_time2->day;
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
    else
      days-= l_sign*calc_daynr((uint) l_time2->year,
			       (uint) l_time2->month,
			       (uint) l_time2->day);
  }

  microseconds= ((longlong)days*LL(86400) +
                 (longlong)(l_time1->hour*3600L +
                            l_time1->minute*60L +
                            l_time1->second) -
                 l_sign*(longlong)(l_time2->hour*3600L +
                                   l_time2->minute*60L +
                                   l_time2->second)) * LL(1000000) +
                (longlong)l_time1->second_part -
                l_sign*(longlong)l_time2->second_part;

  neg= 0;
  if (microseconds < 0)
  {
    microseconds= -microseconds;
    neg= 1;
  }
  *seconds_out= microseconds/1000000L;
  *microseconds_out= (long) (microseconds%1000000L);
  return neg;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
840 841
longlong Item_func_period_add::val_int()
{
842
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
843 844 845 846 847 848 849 850 851 852 853 854 855 856
  ulong period=(ulong) args[0]->val_int();
  int months=(int) args[1]->val_int();

  if ((null_value=args[0]->null_value || args[1]->null_value) ||
      period == 0L)
    return 0; /* purecov: inspected */
  return (longlong)
    convert_month_to_period((uint) ((int) convert_period_to_month(period)+
				    months));
}


longlong Item_func_period_diff::val_int()
{
857
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
858 859 860 861 862 863 864 865 866 867 868 869 870
  ulong period1=(ulong) args[0]->val_int();
  ulong period2=(ulong) args[1]->val_int();

  if ((null_value=args[0]->null_value || args[1]->null_value))
    return 0; /* purecov: inspected */
  return (longlong) ((long) convert_period_to_month(period1)-
		     (long) convert_period_to_month(period2));
}



longlong Item_func_to_days::val_int()
{
871
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
872 873 874 875 876 877 878 879
  TIME ltime;
  if (get_arg0_date(&ltime,0))
    return 0;
  return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
}

longlong Item_func_dayofyear::val_int()
{
880
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
881 882 883 884 885 886 887 888 889
  TIME ltime;
  if (get_arg0_date(&ltime,0))
    return 0;
  return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) -
    calc_daynr(ltime.year,1,1) + 1;
}

longlong Item_func_dayofmonth::val_int()
{
890
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
891 892 893 894 895 896 897
  TIME ltime;
  (void) get_arg0_date(&ltime,1);
  return (longlong) ltime.day;
}

longlong Item_func_month::val_int()
{
898
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
899 900 901 902 903
  TIME ltime;
  (void) get_arg0_date(&ltime,1);
  return (longlong) ltime.month;
}

904

bk@work.mysql.com's avatar
bk@work.mysql.com committed
905 906
String* Item_func_monthname::val_str(String* str)
{
907
  DBUG_ASSERT(fixed == 1);
908
  const char *month_name;
909
  uint   month=(uint) Item_func_month::val_int();
910

911
  if (!month)					// This is also true for NULL
912 913
  {
    null_value=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
914
    return (String*) 0;
915 916
  }
  null_value=0;
917 918
  month_name= month_names[month-1];
  str->set(month_name, strlen(month_name), system_charset_info);
919
  return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
920 921
}

922

bk@work.mysql.com's avatar
bk@work.mysql.com committed
923 924 925 926
// Returns the quarter of the year

longlong Item_func_quarter::val_int()
{
927
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
928 929 930 931 932 933 934
  TIME ltime;
  (void) get_arg0_date(&ltime,1);
  return (longlong) ((ltime.month+2)/3);
}

longlong Item_func_hour::val_int()
{
935
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
936 937 938 939 940 941 942
  TIME ltime;
  (void) get_arg0_time(&ltime);
  return ltime.hour;
}

longlong Item_func_minute::val_int()
{
943
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
944 945 946 947 948 949 950 951
  TIME ltime;
  (void) get_arg0_time(&ltime);
  return ltime.minute;
}
// Returns the second in time_exp in the range of 0 - 59

longlong Item_func_second::val_int()
{
952
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
953 954 955 956 957 958
  TIME ltime;
  (void) get_arg0_time(&ltime);
  return ltime.second;
}


959 960 961 962 963 964 965
uint week_mode(uint mode)
{
  uint week_format= (mode & 7);
  if (!(week_format & WEEK_MONDAY_FIRST))
    week_format^= WEEK_FIRST_WEEKDAY;
  return week_format;
}
966

967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
/*
  The bits in week_format(for calc_week() function) has the following meaning:
   WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
      		   	  If set	Monday is first day of week
   WEEK_YEAR (1)	  If not set	Week is in range 0-53

   	Week 0 is returned for the the last week of the previous year (for
	a date at start of january) In this case one can get 53 for the
	first week of next year.  This flag ensures that the week is
	relevant for the given year. Note that this flag is only
	releveant if WEEK_JANUARY is not set.

			  If set	 Week is in range 1-53.

	In this case one may get week 53 for a date in January (when
	the week is that last week of previous year) and week 1 for a
	date in December.

  WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
			   		to ISO 8601:1988
			  If set	The week that contains the first
					'first-day-of-week' is week 1.
	
	ISO 8601:1988 means that if the week containing January 1 has
	four or more days in the new year, then it is week 1;
	Otherwise it is the last week of the previous year, and the
	next week is week 1.
994
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
995 996 997

longlong Item_func_week::val_int()
{
998
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
999 1000 1001 1002
  uint year;
  TIME ltime;
  if (get_arg0_date(&ltime,0))
    return 0;
1003 1004
  return (longlong) calc_week(&ltime,
			      week_mode((uint) args[1]->val_int()),
1005
			      &year);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1006 1007 1008 1009 1010
}


longlong Item_func_yearweek::val_int()
{
1011
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1012 1013 1014 1015
  uint year,week;
  TIME ltime;
  if (get_arg0_date(&ltime,0))
    return 0;
1016 1017 1018
  week= calc_week(&ltime, 
		  (week_mode((uint) args[1]->val_int()) | WEEK_YEAR),
		  &year);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1019 1020 1021 1022 1023 1024 1025 1026
  return week+year*100;
}


/* weekday() has a automatic to_days() on item */

longlong Item_func_weekday::val_int()
{
1027
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1028 1029 1030 1031 1032 1033 1034
  ulong tmp_value=(ulong) args[0]->val_int();
  if ((null_value=(args[0]->null_value || !tmp_value)))
    return 0; /* purecov: inspected */

  return (longlong) calc_weekday(tmp_value,odbc_type)+test(odbc_type);
}

1035

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1036 1037
String* Item_func_dayname::val_str(String* str)
{
1038
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1039
  uint weekday=(uint) val_int();		// Always Item_func_daynr()
1040 1041
  const char *name;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1042 1043
  if (null_value)
    return (String*) 0;
1044
  
1045 1046
  name= day_names[weekday];
  str->set(name, strlen(name), system_charset_info);
1047
  return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1048 1049 1050 1051 1052
}


longlong Item_func_year::val_int()
{
1053
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1054 1055 1056 1057 1058 1059 1060 1061
  TIME ltime;
  (void) get_arg0_date(&ltime,1);
  return (longlong) ltime.year;
}


longlong Item_func_unix_timestamp::val_int()
{
1062 1063 1064
  TIME ltime;
  bool not_used;
  
1065
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1066
  if (arg_count == 0)
1067
    return (longlong) current_thd->query_start();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1068 1069 1070 1071
  if (args[0]->type() == FIELD_ITEM)
  {						// Optimize timestamp field
    Field *field=((Item_field*) args[0])->field;
    if (field->type() == FIELD_TYPE_TIMESTAMP)
1072
      return ((Field_timestamp*) field)->get_timestamp(&null_value);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1073
  }
1074 1075
  
  if (get_arg0_date(&ltime, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1076
  {
1077 1078 1079 1080 1081 1082 1083
    /*
      We have to set null_value again because get_arg0_date will also set it
      to true if we have wrong datetime parameter (and we should return 0 in 
      this case).
    */
    null_value= args[0]->null_value;
    return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1084
  }
1085 1086
  
  return (longlong) TIME_to_timestamp(current_thd, &ltime, &not_used);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1087 1088 1089 1090 1091
}


longlong Item_func_time_to_sec::val_int()
{
1092
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1093
  TIME ltime;
1094
  longlong seconds;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095
  (void) get_arg0_time(&ltime);
1096 1097
  seconds=ltime.hour*3600L+ltime.minute*60+ltime.second;
  return ltime.neg ? -seconds : seconds;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1098 1099 1100 1101
}


/*
1102 1103
  Convert a string to a interval value
  To make code easy, allow interval objects without separators.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1104 1105 1106
*/

static bool get_interval_value(Item *args,interval_type int_type,
1107
			       String *str_value, INTERVAL *interval)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1108
{
monty@mysql.com's avatar
monty@mysql.com committed
1109
  ulonglong array[5];
1110
  longlong value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1111 1112
  const char *str;
  uint32 length;
1113
  CHARSET_INFO *cs=str_value->charset();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1114

1115 1116 1117 1118 1119
  LINT_INIT(value);
  LINT_INIT(str);
  LINT_INIT(length);

  bzero((char*) interval,sizeof(*interval));
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1120
  if ((int) int_type <= INTERVAL_MICROSECOND)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1121
  {
1122
    value= args->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1123 1124 1125 1126
    if (args->null_value)
      return 1;
    if (value < 0)
    {
1127
      interval->neg=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1128 1129 1130 1131 1132 1133 1134 1135 1136
      value= -value;
    }
  }
  else
  {
    String *res;
    if (!(res=args->val_str(str_value)))
      return (1);

1137
    /* record negative intervalls in interval->neg */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1138 1139
    str=res->ptr();
    const char *end=str+res->length();
1140
    while (str != end && my_isspace(cs,*str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1141 1142 1143
      str++;
    if (str != end && *str == '-')
    {
1144
      interval->neg=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1145 1146 1147 1148 1149 1150 1151
      str++;
    }
    length=(uint32) (end-str);		// Set up pointers to new str
  }

  switch (int_type) {
  case INTERVAL_YEAR:
monty@mysql.com's avatar
monty@mysql.com committed
1152
    interval->year= (ulong) value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1153 1154
    break;
  case INTERVAL_MONTH:
monty@mysql.com's avatar
monty@mysql.com committed
1155
    interval->month= (ulong) value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1156 1157
    break;
  case INTERVAL_DAY:
monty@mysql.com's avatar
monty@mysql.com committed
1158
    interval->day= (ulong) value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1159 1160
    break;
  case INTERVAL_HOUR:
monty@mysql.com's avatar
monty@mysql.com committed
1161
    interval->hour= (ulong) value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1162
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1163
  case INTERVAL_MICROSECOND:
1164
    interval->second_part=value;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1165
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1166
  case INTERVAL_MINUTE:
1167
    interval->minute=value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1168 1169
    break;
  case INTERVAL_SECOND:
1170
    interval->second=value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1171 1172
    break;
  case INTERVAL_YEAR_MONTH:			// Allow YEAR-MONTH YYYYYMM
1173
    if (get_interval_info(str,length,cs,2,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1174
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1175 1176
    interval->year=  (ulong) array[0];
    interval->month= (ulong) array[1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1177 1178
    break;
  case INTERVAL_DAY_HOUR:
1179
    if (get_interval_info(str,length,cs,2,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1180
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1181 1182
    interval->day=  (ulong) array[0];
    interval->hour= (ulong) array[1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1183
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1184
  case INTERVAL_DAY_MICROSECOND:
1185
    if (get_interval_info(str,length,cs,5,array,1))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1186
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1187 1188 1189 1190 1191
    interval->day=    (ulong) array[0];
    interval->hour=   (ulong) array[1];
    interval->minute= array[2];
    interval->second= array[3];
    interval->second_part= array[4];
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1192
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1193
  case INTERVAL_DAY_MINUTE:
1194
    if (get_interval_info(str,length,cs,3,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1195
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1196 1197 1198
    interval->day=    (ulong) array[0];
    interval->hour=   (ulong) array[1];
    interval->minute= array[2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1199 1200
    break;
  case INTERVAL_DAY_SECOND:
1201
    if (get_interval_info(str,length,cs,4,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1202
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1203 1204 1205 1206
    interval->day=    (ulong) array[0];
    interval->hour=   (ulong) array[1];
    interval->minute= array[2];
    interval->second= array[3];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1207
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1208
  case INTERVAL_HOUR_MICROSECOND:
1209
    if (get_interval_info(str,length,cs,4,array,1))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1210
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1211 1212 1213 1214
    interval->hour=   (ulong) array[0];
    interval->minute= array[1];
    interval->second= array[2];
    interval->second_part= array[3];
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1215
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1216
  case INTERVAL_HOUR_MINUTE:
1217
    if (get_interval_info(str,length,cs,2,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1218
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1219 1220
    interval->hour=   (ulong) array[0];
    interval->minute= array[1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222
    break;
  case INTERVAL_HOUR_SECOND:
1223
    if (get_interval_info(str,length,cs,3,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1224
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1225 1226 1227
    interval->hour=   (ulong) array[0];
    interval->minute= array[1];
    interval->second= array[2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1228
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1229
  case INTERVAL_MINUTE_MICROSECOND:
1230
    if (get_interval_info(str,length,cs,3,array,1))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1231
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1232 1233 1234
    interval->minute= array[0];
    interval->second= array[1];
    interval->second_part= array[2];
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1235
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1236
  case INTERVAL_MINUTE_SECOND:
1237
    if (get_interval_info(str,length,cs,2,array,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1238
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1239 1240
    interval->minute= array[0];
    interval->second= array[1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1241
    break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1242
  case INTERVAL_SECOND_MICROSECOND:
1243
    if (get_interval_info(str,length,cs,2,array,1))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1244
      return (1);
monty@mysql.com's avatar
monty@mysql.com committed
1245 1246
    interval->second= array[0];
    interval->second_part= array[1];
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1247
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1248 1249 1250 1251 1252 1253 1254
  }
  return 0;
}


String *Item_date::val_str(String *str)
{
1255
  DBUG_ASSERT(fixed == 1);
1256
  TIME ltime;
1257 1258
  if (get_date(&ltime, TIME_FUZZY_DATE))
    return (String *) 0;
1259 1260 1261 1262 1263 1264 1265
  if (str->alloc(11))
  {
    null_value= 1;
    return (String *) 0;
  }
  make_date((DATE_TIME_FORMAT *) 0, &ltime, str);
  return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1266 1267 1268
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1269
int Item_date::save_in_field(Field *field, bool no_conversions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1270 1271
{
  TIME ltime;
1272
  if (get_date(&ltime, TIME_FUZZY_DATE))
1273
    return set_field_to_null(field);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1274
  field->set_notnull();
1275
  field->store_time(&ltime, MYSQL_TIMESTAMP_DATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1276 1277 1278 1279
  return 0;
}


1280 1281
longlong Item_date::val_int()
{
1282
  DBUG_ASSERT(fixed == 1);
1283 1284 1285 1286 1287 1288 1289 1290
  TIME ltime;
  if (get_date(&ltime, TIME_FUZZY_DATE))
    return 0;
  return (longlong) (ltime.year*10000L+ltime.month*100+ltime.day);
}


bool Item_func_from_days::get_date(TIME *ltime, uint fuzzy_date)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1291 1292 1293
{
  longlong value=args[0]->val_int();
  if ((null_value=args[0]->null_value))
1294
    return 1;
1295
  bzero(ltime, sizeof(TIME));
1296
  get_date_from_daynr((long) value, &ltime->year, &ltime->month, &ltime->day);
1297
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
1298
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1299 1300 1301 1302 1303
}


void Item_func_curdate::fix_length_and_dec()
{
1304
  collation.set(&my_charset_bin);
1305
  decimals=0; 
1306
  max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
1307

1308
  store_now_in_TIME(&ltime);
1309
  
1310 1311
  /* We don't need to set second_part and neg because they already 0 */
  ltime.hour= ltime.minute= ltime.second= 0;
1312
  ltime.time_type= MYSQL_TIMESTAMP_DATE;
1313
  value= (longlong) TIME_to_ulonglong_date(&ltime);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1314 1315
}

1316 1317
String *Item_func_curdate::val_str(String *str)
{
1318
  DBUG_ASSERT(fixed == 1);
1319 1320 1321 1322 1323 1324 1325 1326
  if (str->alloc(11))
  {
    null_value= 1;
    return (String *) 0;
  }
  make_date((DATE_TIME_FORMAT *) 0, &ltime, str);
  return str;
}
1327

1328 1329 1330 1331 1332
/*
    Converts current time in my_time_t to TIME represenatation for local
    time zone. Defines time zone (local) used for whole CURDATE function.
*/
void Item_func_curdate_local::store_now_in_TIME(TIME *now_time)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1333
{
1334 1335 1336 1337
  THD *thd= current_thd;
  thd->variables.time_zone->gmt_sec_to_TIME(now_time, 
                                             (my_time_t)thd->query_start());
  thd->time_zone_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1338 1339
}

1340 1341

/*
1342 1343
    Converts current time in my_time_t to TIME represenatation for UTC
    time zone. Defines time zone (UTC) used for whole UTC_DATE function.
1344
*/
1345
void Item_func_curdate_utc::store_now_in_TIME(TIME *now_time)
1346
{
1347 1348 1349 1350 1351 1352
  my_tz_UTC->gmt_sec_to_TIME(now_time, 
                             (my_time_t)(current_thd->query_start()));
  /* 
    We are not flagging this query as using time zone, since it uses fixed
    UTC-SYSTEM time-zone.
  */
1353 1354 1355
}


1356 1357
bool Item_func_curdate::get_date(TIME *res,
				 uint fuzzy_date __attribute__((unused)))
1358
{
1359 1360
  *res=ltime;
  return 0;
1361 1362 1363
}


1364
String *Item_func_curtime::val_str(String *str)
1365 1366
{
  DBUG_ASSERT(fixed == 1);
1367
  str_value.set(buff, buff_length, &my_charset_bin);
1368 1369 1370
  return &str_value;
}

1371

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1372 1373
void Item_func_curtime::fix_length_and_dec()
{
1374
  TIME ltime;
1375

1376 1377 1378
  decimals=0;
  collation.set(&my_charset_bin);
  store_now_in_TIME(&ltime);
1379
  value= TIME_to_ulonglong_time(&ltime);
1380 1381
  buff_length= (uint) my_time_to_str(&ltime, buff);
  max_length= buff_length;
1382 1383 1384 1385
}


/*
1386 1387
    Converts current time in my_time_t to TIME represenatation for local
    time zone. Defines time zone (local) used for whole CURTIME function.
1388
*/
1389
void Item_func_curtime_local::store_now_in_TIME(TIME *now_time)
1390
{
1391 1392 1393 1394
  THD *thd= current_thd;
  thd->variables.time_zone->gmt_sec_to_TIME(now_time, 
                                             (my_time_t)thd->query_start());
  thd->time_zone_used= 1;
1395 1396 1397 1398
}


/*
1399 1400
    Converts current time in my_time_t to TIME represenatation for UTC
    time zone. Defines time zone (UTC) used for whole UTC_TIME function.
1401
*/
1402
void Item_func_curtime_utc::store_now_in_TIME(TIME *now_time)
1403
{
1404 1405 1406 1407 1408 1409
  my_tz_UTC->gmt_sec_to_TIME(now_time, 
                             (my_time_t)(current_thd->query_start()));
  /* 
    We are not flagging this query as using time zone, since it uses fixed
    UTC-SYSTEM time-zone.
  */
1410 1411
}

1412

1413 1414
String *Item_func_now::val_str(String *str)
{
1415
  DBUG_ASSERT(fixed == 1);
1416
  str_value.set(buff,buff_length, &my_charset_bin);
1417
  return &str_value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1418 1419
}

1420

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1421 1422
void Item_func_now::fix_length_and_dec()
{
1423
  decimals=0;
1424 1425
  collation.set(&my_charset_bin);

1426
  store_now_in_TIME(&ltime);
1427
  value= (longlong) TIME_to_ulonglong_datetime(&ltime);
1428

1429 1430
  buff_length= (uint) my_datetime_to_str(&ltime, buff);
  max_length= buff_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1431 1432
}

1433

1434 1435 1436 1437 1438
/*
    Converts current time in my_time_t to TIME represenatation for local
    time zone. Defines time zone (local) used for whole NOW function.
*/
void Item_func_now_local::store_now_in_TIME(TIME *now_time)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1439
{
1440 1441 1442 1443
  THD *thd= current_thd;
  thd->variables.time_zone->gmt_sec_to_TIME(now_time, 
                                             (my_time_t)thd->query_start());
  thd->time_zone_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1444 1445 1446
}


1447 1448 1449 1450 1451
/*
    Converts current time in my_time_t to TIME represenatation for UTC
    time zone. Defines time zone (UTC) used for whole UTC_TIMESTAMP function.
*/
void Item_func_now_utc::store_now_in_TIME(TIME *now_time)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1452
{
1453 1454 1455 1456 1457 1458
  my_tz_UTC->gmt_sec_to_TIME(now_time, 
                             (my_time_t)(current_thd->query_start()));
  /* 
    We are not flagging this query as using time zone, since it uses fixed
    UTC-SYSTEM time-zone.
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1459 1460 1461
}


1462 1463
bool Item_func_now::get_date(TIME *res,
			     uint fuzzy_date __attribute__((unused)))
1464
{
1465 1466
  *res=ltime;
  return 0;
1467 1468 1469
}


1470
int Item_func_now::save_in_field(Field *to, bool no_conversions)
1471
{
1472
  to->set_notnull();
1473
  to->store_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
1474
  return 0;
1475 1476 1477
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1478 1479
String *Item_func_sec_to_time::val_str(String *str)
{
1480
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1481
  longlong seconds=(longlong) args[0]->val_int();
1482 1483 1484
  uint sec;
  TIME ltime;

1485 1486 1487 1488 1489
  if ((null_value=args[0]->null_value) || str->alloc(19))
  {
    null_value= 1;
    return (String*) 0;
  }
1490 1491

  ltime.neg= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1492 1493 1494
  if (seconds < 0)
  {
    seconds= -seconds;
1495
    ltime.neg= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1496
  }
1497 1498 1499

  sec= (uint) ((ulonglong) seconds % 3600);
  ltime.day= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1500
  ltime.hour= (uint) (seconds/3600);
1501 1502 1503
  ltime.minute= sec/60;
  ltime.second= sec % 60;

1504 1505
  make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
  return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1506 1507 1508 1509 1510
}


longlong Item_func_sec_to_time::val_int()
{
1511
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
  longlong seconds=args[0]->val_int();
  longlong sign=1;
  if ((null_value=args[0]->null_value))
    return 0;
  if (seconds < 0)
  {
    seconds= -seconds;
    sign= -1;
  }
  return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60));
}


void Item_func_date_format::fix_length_and_dec()
{
  decimals=0;
1528
  collation.set(&my_charset_bin);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1529 1530 1531
  if (args[1]->type() == STRING_ITEM)
  {						// Optimize the normal case
    fixed_length=1;
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541

    /*
      Force case sensitive collation on format string.
      This needed because format modifiers with different case,
      for example %m and %M, have different meaning. Thus eq()
      will distinguish them.
    */
    args[1]->collation.set(
        get_charset_by_csname(args[1]->collation.collation->csname,
                              MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE);
1542 1543 1544 1545 1546
    /*
      The result is a binary string (no reason to use collation->mbmaxlen
      This is becasue make_date_time() only returns binary strings
    */
    max_length= format_length(((Item_string*) args[1])->const_string());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1547 1548 1549 1550
  }
  else
  {
    fixed_length=0;
1551
    /* The result is a binary string (no reason to use collation->mbmaxlen */
1552
    max_length=min(args[1]->max_length,MAX_BLOB_WIDTH) * 10;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
    set_if_smaller(max_length,MAX_BLOB_WIDTH);
  }
  maybe_null=1;					// If wrong date
}


uint Item_func_date_format::format_length(const String *format)
{
  uint size=0;
  const char *ptr=format->ptr();
  const char *end=ptr+format->length();

  for (; ptr != end ; ptr++)
  {
    if (*ptr != '%' || ptr == end-1)
      size++;
    else
    {
      switch(*++ptr) {
      case 'M': /* month, textual */
      case 'W': /* day (of the week), textual */
	size += 9;
	break;
      case 'D': /* day (of the month), numeric plus english suffix */
      case 'Y': /* year, numeric, 4 digits */
      case 'x': /* Year, used with 'v' */
      case 'X': /* Year, used with 'v, where week starts with Monday' */
	size += 4;
	break;
      case 'a': /* locale's abbreviated weekday name (Sun..Sat) */
      case 'b': /* locale's abbreviated month name (Jan.Dec) */
      case 'j': /* day of year (001..366) */
	size += 3;
	break;
      case 'U': /* week (00..52) */
      case 'u': /* week (00..52), where week starts with Monday */
      case 'V': /* week 1..53 used with 'x' */
      case 'v': /* week 1..53 used with 'x', where week starts with Monday */
      case 'H': /* hour (00..23) */
      case 'y': /* year, numeric, 2 digits */
      case 'm': /* month, numeric */
      case 'd': /* day (of the month), numeric */
      case 'h': /* hour (01..12) */
      case 'I': /* --||-- */
      case 'i': /* minutes, numeric */
      case 'k': /* hour ( 0..23) */
      case 'l': /* hour ( 1..12) */
      case 'p': /* locale's AM or PM */
      case 'S': /* second (00..61) */
      case 's': /* seconds, numeric */
      case 'c': /* month (0..12) */
      case 'e': /* day (0..31) */
	size += 2;
	break;
      case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */
	size += 11;
	break;
      case 'T': /* time, 24-hour (hh:mm:ss) */
	size += 8;
	break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1613 1614 1615
      case 'f': /* microseconds */
	size += 6;
	break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
      case 'w': /* day (of the week), numeric */
      case '%':
      default:
	size++;
	break;
      }
    }
  }
  return size;
}


String *Item_func_date_format::val_str(String *str)
{
  String *format;
  TIME l_time;
1632
  uint size;
1633
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1634

1635
  if (!is_time_format)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1636 1637 1638 1639 1640 1641 1642
  {
    if (get_arg0_date(&l_time,1))
      return 0;
  }
  else
  {
    String *res;
1643
    if (!(res=args[0]->val_str(str)) ||
1644
	(str_to_time_with_warn(res->ptr(), res->length(), &l_time)))
1645 1646
      goto null_date;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1647 1648 1649 1650 1651
    l_time.year=l_time.month=l_time.day=0;
    null_value=0;
  }

  if (!(format = args[1]->val_str(str)) || !format->length())
1652
    goto null_date;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1653 1654 1655 1656 1657 1658

  if (fixed_length)
    size=max_length;
  else
    size=format_length(format);
  if (format == str)
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1659
    str= &value;				// Save result here
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1660
  if (str->alloc(size))
1661 1662
    goto null_date;

1663 1664 1665
  DATE_TIME_FORMAT date_time_format;
  date_time_format.format.str=    (char*) format->ptr();
  date_time_format.format.length= format->length(); 
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1666 1667

  /* Create the result string */
1668
  if (!make_date_time(&date_time_format, &l_time,
1669 1670 1671
                      is_time_format ? MYSQL_TIMESTAMP_TIME :
                                       MYSQL_TIMESTAMP_DATE,
                      str))
1672
    return str;
1673

1674 1675 1676
null_date:
  null_value=1;
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1677 1678 1679
}


1680 1681 1682 1683 1684 1685
void Item_func_from_unixtime::fix_length_and_dec()
{ 
  thd= current_thd;
  collation.set(&my_charset_bin);
  decimals=0;
  max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
1686
  maybe_null= 1;
1687 1688 1689 1690
  thd->time_zone_used= 1;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1691 1692
String *Item_func_from_unixtime::val_str(String *str)
{
1693
  TIME time_tmp;
1694

1695
  DBUG_ASSERT(fixed == 1);
1696 1697

  if (get_date(&time_tmp, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1698
    return 0;
1699

1700
  if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
1701 1702 1703 1704 1705
  {
    null_value= 1;
    return 0;
  }

1706
  make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
1707
  return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1708 1709 1710 1711 1712
}


longlong Item_func_from_unixtime::val_int()
{
1713
  TIME time_tmp;
1714

1715
  DBUG_ASSERT(fixed == 1);
1716

1717
  if (get_date(&time_tmp, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1718
    return 0;
1719

1720
  return (longlong) TIME_to_ulonglong_datetime(&time_tmp);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1721 1722 1723
}

bool Item_func_from_unixtime::get_date(TIME *ltime,
1724
				       uint fuzzy_date __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1725
{
1726
  ulonglong tmp= (ulonglong)(args[0]->val_int());
1727
  /*
1728 1729
    "tmp > TIMESTAMP_MAX_VALUE" check also covers case of negative
    from_unixtime() argument since tmp is unsigned.
1730
  */
1731
  if ((null_value= (args[0]->null_value || tmp > TIMESTAMP_MAX_VALUE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1732
    return 1;
1733 1734

  thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)tmp);
1735 1736 1737 1738 1739 1740

  return 0;
}


void Item_func_convert_tz::fix_length_and_dec()
1741
{
1742 1743 1744
  collation.set(&my_charset_bin);
  decimals= 0;
  max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
1745
  maybe_null= 1;
1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756
}


bool
Item_func_convert_tz::fix_fields(THD *thd_arg, TABLE_LIST *tables_arg, Item **ref)
{
  String str;
  if (Item_date_func::fix_fields(thd_arg, tables_arg, ref))
    return 1;

  tz_tables= thd_arg->lex->time_zone_tables_used;
1757

1758
  return 0;
1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
}


String *Item_func_convert_tz::val_str(String *str)
{
  TIME time_tmp;

  if (get_date(&time_tmp, 0))
    return 0;
  
  if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
  {
    null_value= 1;
    return 0;
  }
  
  make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
  return str;
}


longlong Item_func_convert_tz::val_int()
{
  TIME time_tmp;

  if (get_date(&time_tmp, 0))
    return 0;
  
  return (longlong)TIME_to_ulonglong_datetime(&time_tmp);
}


bool Item_func_convert_tz::get_date(TIME *ltime,
				       uint fuzzy_date __attribute__((unused)))
{
  my_time_t my_time_tmp;
  bool not_used;
  String str;
1797 1798 1799

  if (!from_tz_cached)
  {
1800
    from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
1801 1802 1803 1804 1805
    from_tz_cached= args[1]->const_item();
  }

  if (!to_tz_cached)
  {
1806
    to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
1807 1808 1809
    to_tz_cached= args[2]->const_item();
  }

1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826
  if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0))
  {
    null_value= 1;
    return 1;
  }

  /* Check if we in range where we treat datetime values as non-UTC */
  if (ltime->year < TIMESTAMP_MAX_YEAR && ltime->year > TIMESTAMP_MIN_YEAR ||
      ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 ||
      ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31)
  {
    my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, &not_used);
    if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE)
      to_tz->gmt_sec_to_TIME(ltime, my_time_tmp);
  }
  
  null_value= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1827 1828 1829
  return 0;
}

1830

1831 1832 1833 1834 1835 1836 1837
void Item_func_convert_tz::cleanup()
{
  from_tz_cached= to_tz_cached= 0;
  Item_date_func::cleanup();
}


1838 1839 1840
void Item_date_add_interval::fix_length_and_dec()
{
  enum_field_types arg0_field_type;
1841 1842

  collation.set(&my_charset_bin);
1843
  maybe_null=1;
1844 1845
  max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
  value.alloc(max_length);
1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864

  /*
    The field type for the result of an Item_date function is defined as
    follows:

    - If first arg is a MYSQL_TYPE_DATETIME result is MYSQL_TYPE_DATETIME
    - If first arg is a MYSQL_TYPE_DATE and the interval type uses hours,
      minutes or seconds then type is MYSQL_TYPE_DATETIME.
    - Otherwise the result is MYSQL_TYPE_STRING
      (This is because you can't know if the string contains a DATE, TIME or
      DATETIME argument)
  */
  cached_field_type= MYSQL_TYPE_STRING;
  arg0_field_type= args[0]->field_type();
  if (arg0_field_type == MYSQL_TYPE_DATETIME ||
      arg0_field_type == MYSQL_TYPE_TIMESTAMP)
    cached_field_type= MYSQL_TYPE_DATETIME;
  else if (arg0_field_type == MYSQL_TYPE_DATE)
  {
1865
    if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH)
1866 1867 1868 1869 1870 1871 1872 1873
      cached_field_type= arg0_field_type;
    else
      cached_field_type= MYSQL_TYPE_DATETIME;
  }
}


/* Here arg[1] is a Item_interval object */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1874

1875
bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1876 1877 1878
{
  long period,sign;
  INTERVAL interval;
1879

1880
  ltime->neg= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890
  if (args[0]->get_date(ltime,0) ||
      get_interval_value(args[1],int_type,&value,&interval))
    goto null_date;
  sign= (interval.neg ? -1 : 1);
  if (date_sub_interval)
    sign = -sign;

  null_value=0;
  switch (int_type) {
  case INTERVAL_SECOND:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1891 1892
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1893 1894
  case INTERVAL_MINUTE:
  case INTERVAL_HOUR:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1895
  case INTERVAL_MINUTE_MICROSECOND:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1896
  case INTERVAL_MINUTE_SECOND:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1897
  case INTERVAL_HOUR_MICROSECOND:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1898 1899
  case INTERVAL_HOUR_SECOND:
  case INTERVAL_HOUR_MINUTE:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1900
  case INTERVAL_DAY_MICROSECOND:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1901 1902 1903
  case INTERVAL_DAY_SECOND:
  case INTERVAL_DAY_MINUTE:
  case INTERVAL_DAY_HOUR:
monty@mysql.com's avatar
monty@mysql.com committed
1904 1905
  {
    longlong sec, days, daynr, microseconds, extra_sec;
1906
    ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1907 1908 1909
    microseconds= ltime->second_part + sign*interval.second_part;
    extra_sec= microseconds/1000000L;
    microseconds= microseconds%1000000L;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1910 1911 1912

    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
	 ltime->second +
monty@mysql.com's avatar
monty@mysql.com committed
1913
	 sign* (longlong) (interval.day*3600*24L +
monty@mysql.com's avatar
monty@mysql.com committed
1914
                           interval.hour*LL(3600)+interval.minute*LL(60)+
monty@mysql.com's avatar
monty@mysql.com committed
1915
                           interval.second))+ extra_sec;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1916 1917
    if (microseconds < 0)
    {
monty@mysql.com's avatar
monty@mysql.com committed
1918
      microseconds+= LL(1000000);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1919 1920
      sec--;
    }
1921 1922
    days= sec/(3600*LL(24));
    sec-= days*3600*LL(24);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1923 1924 1925
    if (sec < 0)
    {
      days--;
monty@mysql.com's avatar
monty@mysql.com committed
1926
      sec+= 3600*LL(24);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1927
    }
monty@mysql.com's avatar
monty@mysql.com committed
1928
    ltime->second_part= (uint) microseconds;
monty@mysql.com's avatar
monty@mysql.com committed
1929 1930 1931
    ltime->second= (uint) (sec % 60);
    ltime->minute= (uint) (sec/60 % 60);
    ltime->hour=   (uint) (sec/3600);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1932
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
monty@mysql.com's avatar
monty@mysql.com committed
1933 1934
    /* Day number from year 0 to 9999-12-31 */
    if ((ulonglong) daynr >= MAX_DAY_NUMBER)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1935
      goto null_date;
monty@mysql.com's avatar
monty@mysql.com committed
1936 1937
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
                        &ltime->day);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1938
    break;
monty@mysql.com's avatar
monty@mysql.com committed
1939
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1940
  case INTERVAL_DAY:
monty@mysql.com's avatar
monty@mysql.com committed
1941 1942 1943 1944
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
             sign * (long) interval.day);
    /* Daynumber from year 0 to 9999-12-31 */
    if ((ulong) period >= MAX_DAY_NUMBER)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1945 1946 1947 1948
      goto null_date;
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
    break;
  case INTERVAL_YEAR:
monty@mysql.com's avatar
monty@mysql.com committed
1949 1950
    ltime->year+= sign * (long) interval.year;
    if ((ulong) ltime->year >= 10000L)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1951 1952 1953 1954 1955 1956 1957
      goto null_date;
    if (ltime->month == 2 && ltime->day == 29 &&
	calc_days_in_year(ltime->year) != 366)
      ltime->day=28;				// Was leap-year
    break;
  case INTERVAL_YEAR_MONTH:
  case INTERVAL_MONTH:
monty@mysql.com's avatar
monty@mysql.com committed
1958 1959 1960
    period= (ltime->year*12 + sign * (long) interval.year*12 +
	     ltime->month-1 + sign * (long) interval.month);
    if ((ulong) period >= 120000L)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983
      goto null_date;
    ltime->year= (uint) (period / 12);
    ltime->month= (uint) (period % 12L)+1;
    /* Adjust day if the new month doesn't have enough days */
    if (ltime->day > days_in_month[ltime->month-1])
    {
      ltime->day = days_in_month[ltime->month-1];
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
	ltime->day++;				// Leap-year
    }
    break;
  default:
    goto null_date;
  }
  return 0;					// Ok

 null_date:
  return (null_value=1);
}


String *Item_date_add_interval::val_str(String *str)
{
1984
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1985
  TIME ltime;
1986
  enum date_time_format_types format;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1987 1988 1989

  if (Item_date_add_interval::get_date(&ltime,0))
    return 0;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1990

1991
  if (ltime.time_type == MYSQL_TIMESTAMP_DATE)
1992 1993 1994 1995 1996 1997 1998
    format= DATE_ONLY;
  else if (ltime.second_part)
    format= DATE_TIME_MICROSECOND;
  else
    format= DATE_TIME;

  if (!make_datetime(format, &ltime, str))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1999
    return str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2000 2001 2002 2003 2004

  null_value=1;
  return 0;
}

2005

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2006 2007
longlong Item_date_add_interval::val_int()
{
2008
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2009
  TIME ltime;
2010
  longlong date;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2011 2012
  if (Item_date_add_interval::get_date(&ltime,0))
    return (longlong) 0;
2013
  date = (ltime.year*100L + ltime.month)*100L + ltime.day;
2014
  return ltime.time_type == MYSQL_TIMESTAMP_DATE ? date :
2015
    ((date*100L + ltime.hour)*100L+ ltime.minute)*100L + ltime.second;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2016 2017
}

2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033
static const char *interval_names[]=
{
  "year", "month", "day", "hour", "minute",
  "second", "microsecond", "year_month",
  "day_hour", "day_minute", "day_second",
  "hour_minute", "hour_second", "minute_second",
  "day_microsecond", "hour_microsecond",
  "minute_microsecond", "second_microsecond"
};

void Item_date_add_interval::print(String *str)
{
  str->append('(');
  args[0]->print(str);
  str->append(date_sub_interval?" - interval ":" + interval ");
  args[1]->print(str);
2034
  str->append(' ');
2035 2036 2037 2038 2039 2040
  str->append(interval_names[int_type]);
  str->append(')');
}

void Item_extract::print(String *str)
{
2041
  str->append("extract(", 8);
2042
  str->append(interval_names[int_type]);
2043
  str->append(" from ", 6);
2044 2045 2046 2047
  args[0]->print(str);
  str->append(')');
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066
void Item_extract::fix_length_and_dec()
{
  value.alloc(32);				// alloc buffer

  maybe_null=1;					// If wrong date
  switch (int_type) {
  case INTERVAL_YEAR:		max_length=4; date_value=1; break;
  case INTERVAL_YEAR_MONTH:	max_length=6; date_value=1; break;
  case INTERVAL_MONTH:		max_length=2; date_value=1; break;
  case INTERVAL_DAY:		max_length=2; date_value=1; break;
  case INTERVAL_DAY_HOUR:	max_length=9; date_value=0; break;
  case INTERVAL_DAY_MINUTE:	max_length=11; date_value=0; break;
  case INTERVAL_DAY_SECOND:	max_length=13; date_value=0; break;
  case INTERVAL_HOUR:		max_length=2; date_value=0; break;
  case INTERVAL_HOUR_MINUTE:	max_length=4; date_value=0; break;
  case INTERVAL_HOUR_SECOND:	max_length=6; date_value=0; break;
  case INTERVAL_MINUTE:		max_length=2; date_value=0; break;
  case INTERVAL_MINUTE_SECOND:	max_length=4; date_value=0; break;
  case INTERVAL_SECOND:		max_length=2; date_value=0; break;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2067 2068 2069 2070 2071
  case INTERVAL_MICROSECOND:	max_length=2; date_value=0; break;
  case INTERVAL_DAY_MICROSECOND: max_length=20; date_value=0; break;
  case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break;
  case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break;
  case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2072 2073 2074 2075 2076 2077
  }
}


longlong Item_extract::val_int()
{
2078
  DBUG_ASSERT(fixed == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089
  TIME ltime;
  long neg;
  if (date_value)
  {
    if (get_arg0_date(&ltime,1))
      return 0;
    neg=1;
  }
  else
  {
    String *res= args[0]->val_str(&value);
2090
    if (!res || str_to_time_with_warn(res->ptr(), res->length(), &ltime))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117
    {
      null_value=1;
      return 0;
    }
    neg= ltime.neg ? -1 : 1;
    null_value=0;
  }
  switch (int_type) {
  case INTERVAL_YEAR:		return ltime.year;
  case INTERVAL_YEAR_MONTH:	return ltime.year*100L+ltime.month;
  case INTERVAL_MONTH:		return ltime.month;
  case INTERVAL_DAY:		return ltime.day;
  case INTERVAL_DAY_HOUR:	return (long) (ltime.day*100L+ltime.hour)*neg;
  case INTERVAL_DAY_MINUTE:	return (long) (ltime.day*10000L+
					       ltime.hour*100L+
					       ltime.minute)*neg;
  case INTERVAL_DAY_SECOND:	 return ((longlong) ltime.day*1000000L+
					 (longlong) (ltime.hour*10000L+
						     ltime.minute*100+
						     ltime.second))*neg;
  case INTERVAL_HOUR:		return (long) ltime.hour*neg;
  case INTERVAL_HOUR_MINUTE:	return (long) (ltime.hour*100+ltime.minute)*neg;
  case INTERVAL_HOUR_SECOND:	return (long) (ltime.hour*10000+ltime.minute*100+
					       ltime.second)*neg;
  case INTERVAL_MINUTE:		return (long) ltime.minute*neg;
  case INTERVAL_MINUTE_SECOND:	return (long) (ltime.minute*100+ltime.second)*neg;
  case INTERVAL_SECOND:		return (long) ltime.second*neg;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132
  case INTERVAL_MICROSECOND:	return (long) ltime.second_part*neg;
  case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L +
					  (longlong)ltime.hour*10000L +
					  ltime.minute*100 +
					  ltime.second)*1000000L +
					 ltime.second_part)*neg;
  case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L +
					   ltime.minute*100 +
					   ltime.second)*1000000L +
					  ltime.second_part)*neg;
  case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+
							ltime.second))*1000000L+
					    ltime.second_part)*neg;
  case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+
					    ltime.second_part)*neg;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2133 2134 2135
  }
  return 0;					// Impossible
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2136

hf@deer.(none)'s avatar
hf@deer.(none) committed
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152
bool Item_extract::eq(const Item *item, bool binary_cmp) const
{
  if (this == item)
    return 1;
  if (item->type() != FUNC_ITEM ||
      func_name() != ((Item_func*)item)->func_name())
    return 0;

  Item_extract* ie= (Item_extract*)item;
  if (ie->int_type != int_type)
    return 0;

  if (!args[0]->eq(ie->args[0], binary_cmp))
      return 0;
  return 1;
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2153

2154

2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172
bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const
{
  if (this == item)
    return 1;
  if (item->type() != FUNC_ITEM ||
      func_name() != ((Item_func*)item)->func_name())
    return 0;

  Item_char_typecast *cast= (Item_char_typecast*)item;
  if (cast_length != cast->cast_length ||
      cast_cs     != cast->cast_cs)
    return 0;

  if (!args[0]->eq(cast->args[0], binary_cmp))
      return 0;
  return 1;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2173 2174
void Item_typecast::print(String *str)
{
2175
  str->append("cast(", 5);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2176
  args[0]->print(str);
2177
  str->append(" as ", 4);
2178 2179 2180 2181
  str->append(cast_type());
  str->append(')');
}

2182

2183 2184
void Item_char_typecast::print(String *str)
{
2185
  str->append("cast(", 5);
2186
  args[0]->print(str);
2187
  str->append(" as char", 8);
2188 2189 2190
  if (cast_length >= 0)
  {
    str->append('(');
2191
    char buffer[20];
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2192 2193 2194
    // my_charset_bin is good enough for numbers
    String st(buffer, sizeof(buffer), &my_charset_bin);
    st.set((ulonglong)cast_length, &my_charset_bin);
2195
    str->append(st);
2196 2197 2198 2199
    str->append(')');
  }
  if (cast_cs)
  {
2200
    str->append(" charset ", 9);
2201 2202
    str->append(cast_cs->name);
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2203 2204
  str->append(')');
}
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2205

2206 2207
String *Item_char_typecast::val_str(String *str)
{
2208
  DBUG_ASSERT(fixed == 1);
2209
  String *res;
2210 2211
  uint32 length;

2212
  if (!charset_conversion)
2213
  {
2214 2215 2216 2217 2218
    if (!(res= args[0]->val_str(str)))
    {
      null_value= 1;
      return 0;
    }
2219 2220 2221 2222
  }
  else
  {
    // Convert character set if differ
2223
    uint dummy_errors;
2224
    if (!(res= args[0]->val_str(&tmp_value)) ||
2225 2226
        str->copy(res->ptr(), res->length(), from_cs,
        cast_cs, &dummy_errors))
2227 2228 2229 2230 2231 2232
    {
      null_value= 1;
      return 0;
    }
    res= str;
  }
2233 2234

  res->set_charset(cast_cs);
2235

2236 2237 2238 2239 2240
  /*
     Cut the tail if cast with length
     and the result is longer than cast length, e.g.
     CAST('string' AS CHAR(1))
  */
2241
  if (cast_length >= 0 &&
2242 2243 2244 2245 2246 2247 2248 2249
      (res->length() > (length= (uint32) res->charpos(cast_length))))
  {						// Safe even if const arg
    if (!res->alloced_length())
    {						// Don't change const str
      str_value= *res;				// Not malloced string
      res= &str_value;
    }
    res->length((uint) length);
2250
  }
2251 2252 2253 2254 2255 2256 2257
  null_value= 0;
  return res;
}

void Item_char_typecast::fix_length_and_dec()
{
  uint32 char_length;
bar@mysql.com's avatar
bar@mysql.com committed
2258 2259 2260 2261 2262 2263 2264
  /* 
     We always force character set conversion if cast_cs
     is a multi-byte character set. It garantees that the
     result of CAST is a well-formed string.
     For single-byte character sets we allow just to copy
     from the argument. A single-byte character sets string
     is always well-formed. 
2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278
     
     There is a special trick to convert form a number to ucs2.
     As numbers have my_charset_bin as their character set,
     it wouldn't do conversion to ucs2 without an additional action.
     To force conversion, we should pretend to be non-binary.
     Let's choose from_cs this way:
     - If the argument in a number and cast_cs is ucs2 (i.e. mbminlen > 1),
       then from_cs is set to latin1, to perform latin1 -> ucs2 conversion.
     - If the argument is a number and cast_cs is ASCII-compatible
       (i.e. mbminlen == 1), then from_cs is set to cast_cs,
       which allows just to take over the args[0]->val_str() result
       and thus avoid unnecessary character set conversion.
     - If the argument is not a number, then from_cs is set to
       the argument's charset.
bar@mysql.com's avatar
bar@mysql.com committed
2279
  */
2280 2281 2282 2283
  from_cs= (args[0]->result_type() == INT_RESULT || 
            args[0]->result_type() == REAL_RESULT) ?
           (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) :
           args[0]->collation.collation;
bar@mysql.com's avatar
bar@mysql.com committed
2284
  charset_conversion= (cast_cs->mbmaxlen > 1) ||
2285 2286
                      !my_charset_same(from_cs, cast_cs) &&
                      from_cs != &my_charset_bin &&
bar@mysql.com's avatar
bar@mysql.com committed
2287
                      cast_cs != &my_charset_bin;
2288 2289
  collation.set(cast_cs, DERIVATION_IMPLICIT);
  char_length= (cast_length >= 0) ? cast_length : 
2290
	       args[0]->max_length/from_cs->mbmaxlen;
2291 2292 2293
  max_length= char_length * cast_cs->mbmaxlen;
}

2294

2295 2296
String *Item_datetime_typecast::val_str(String *str)
{
2297
  DBUG_ASSERT(fixed == 1);
2298 2299
  TIME ltime;
  if (!get_arg0_date(&ltime,1) &&
2300 2301
      !make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, 
		     &ltime, str))
monty@mysql.com's avatar
monty@mysql.com committed
2302
    return str;
2303 2304 2305 2306 2307 2308 2309 2310 2311

  null_value=1;
  return 0;
}


bool Item_time_typecast::get_time(TIME *ltime)
{
  bool res= get_arg0_time(ltime);
2312 2313 2314 2315 2316 2317
  /*
    For MYSQL_TIMESTAMP_TIME value we can have non-zero day part,
    which we should not lose.
  */
  if (ltime->time_type == MYSQL_TIMESTAMP_DATETIME)
    ltime->year= ltime->month= ltime->day= 0;
2318
  ltime->time_type= MYSQL_TIMESTAMP_TIME;
2319 2320 2321 2322 2323 2324
  return res;
}


String *Item_time_typecast::val_str(String *str)
{
2325
  DBUG_ASSERT(fixed == 1);
2326 2327 2328
  TIME ltime;

  if (!get_arg0_time(&ltime) &&
2329 2330
      !make_datetime(ltime.second_part ? TIME_MICROSECOND : TIME_ONLY,
		     &ltime, str))
2331 2332 2333 2334 2335 2336 2337
    return str;

  null_value=1;
  return 0;
}


2338
bool Item_date_typecast::get_date(TIME *ltime, uint fuzzy_date)
2339 2340
{
  bool res= get_arg0_date(ltime,1);
2341
  ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
2342
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
2343 2344 2345 2346 2347 2348
  return res;
}


String *Item_date_typecast::val_str(String *str)
{
2349
  DBUG_ASSERT(fixed == 1);
2350 2351
  TIME ltime;

2352 2353
  if (!get_arg0_date(&ltime,1) && !str->alloc(11))
  {
2354
    make_date((DATE_TIME_FORMAT *) 0, &ltime, str);
2355 2356
    return str;
  }
2357 2358 2359 2360 2361

  null_value=1;
  return 0;
}

2362

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2363 2364 2365 2366 2367 2368 2369
/*
  MAKEDATE(a,b) is a date function that creates a date value 
  from a year and day value.
*/

String *Item_func_makedate::val_str(String *str)
{
2370
  DBUG_ASSERT(fixed == 1);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2371
  TIME l_time;
monty@mysql.com's avatar
monty@mysql.com committed
2372 2373
  long daynr=  (long) args[1]->val_int();
  long yearnr= (long) args[0]->val_int();
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2374 2375 2376 2377
  long days;

  if (args[0]->null_value || args[1]->null_value ||
      yearnr < 0 || daynr <= 0)
2378
    goto err;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2379 2380

  days= calc_daynr(yearnr,1,1) + daynr - 1;
monty@mysql.com's avatar
monty@mysql.com committed
2381
  /* Day number from year 0 to 9999-12-31 */
2382
  if (days >= 0 && days < MAX_DAY_NUMBER)
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2383 2384 2385
  {
    null_value=0;
    get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day);
2386 2387 2388 2389
    if (str->alloc(11))
      goto err;
    make_date((DATE_TIME_FORMAT *) 0, &l_time, str);
    return str;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2390 2391
  }

2392
err:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2393 2394 2395 2396 2397 2398 2399 2400 2401
  null_value=1;
  return 0;
}


void Item_func_add_time::fix_length_and_dec()
{
  enum_field_types arg0_field_type;
  decimals=0;
2402
  max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
monty@mysql.com's avatar
monty@mysql.com committed
2403
  maybe_null= 1;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2404 2405

  /*
2406 2407
    The field type for the result of an Item_func_add_time function is defined
    as follows:
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2408 2409 2410 2411 2412 2413 2414 2415 2416

    - If first arg is a MYSQL_TYPE_DATETIME or MYSQL_TYPE_TIMESTAMP 
      result is MYSQL_TYPE_DATETIME
    - If first arg is a MYSQL_TYPE_TIME result is MYSQL_TYPE_TIME
    - Otherwise the result is MYSQL_TYPE_STRING
  */

  cached_field_type= MYSQL_TYPE_STRING;
  arg0_field_type= args[0]->field_type();
2417 2418
  if (arg0_field_type == MYSQL_TYPE_DATE ||
      arg0_field_type == MYSQL_TYPE_DATETIME ||
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2419 2420 2421 2422 2423 2424 2425
      arg0_field_type == MYSQL_TYPE_TIMESTAMP)
    cached_field_type= MYSQL_TYPE_DATETIME;
  else if (arg0_field_type == MYSQL_TYPE_TIME)
    cached_field_type= MYSQL_TYPE_TIME;
}

/*
2426 2427
  ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a
  time/datetime value 
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2428 2429 2430 2431 2432 2433 2434 2435 2436

  t: time_or_datetime_expression
  a: time_expression
  
  Result: Time value or datetime value
*/

String *Item_func_add_time::val_str(String *str)
{
2437
  DBUG_ASSERT(fixed == 1);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2438
  TIME l_time1, l_time2, l_time3;
2439
  bool is_time= 0;
2440 2441
  long days, microseconds;
  longlong seconds;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2442 2443 2444
  int l_sign= sign;

  null_value=0;
2445 2446 2447 2448
  if (is_date)                        // TIMESTAMP function
  {
    if (get_arg0_date(&l_time1,1) || 
        args[1]->get_time(&l_time2) ||
2449 2450
        l_time1.time_type == MYSQL_TIMESTAMP_TIME || 
        l_time2.time_type != MYSQL_TIMESTAMP_TIME)
2451 2452 2453
      goto null_date;
  }
  else                                // ADDTIME function
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2454
  {
2455 2456
    if (args[0]->get_time(&l_time1) || 
        args[1]->get_time(&l_time2) ||
2457
        l_time2.time_type == MYSQL_TIMESTAMP_DATETIME)
2458
      goto null_date;
2459
    is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2460 2461 2462 2463
  }
  if (l_time1.neg != l_time2.neg)
    l_sign= -l_sign;

2464 2465
  l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
			      &seconds, &microseconds);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2466

2467 2468 2469 2470 2471 2472
  /*
    If first argument was negative and diff between arguments
    is non-zero we need to swap sign to get proper result.
  */
  if (l_time1.neg && (seconds || microseconds))
    l_time3.neg= 1-l_time3.neg;         // Swap sign of result
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2473

2474 2475 2476 2477 2478 2479
  if (!is_time && l_time3.neg)
    goto null_date;

  days= (long)(seconds/86400L);

  calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2480 2481 2482 2483
  if (!is_time)
  {
    get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day);
    if (l_time3.day &&
2484 2485 2486
	!make_datetime(l_time1.second_part || l_time2.second_part ?
		       DATE_TIME_MICROSECOND : DATE_TIME,
		       &l_time3, str))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2487 2488 2489
      return str;
    goto null_date;
  }
2490
  
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2491
  l_time3.hour+= days*24;
2492 2493 2494
  if (!make_datetime(l_time1.second_part || l_time2.second_part ?
		     TIME_MICROSECOND : TIME_ONLY,
		     &l_time3, str))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2495 2496 2497 2498 2499 2500 2501
    return str;

null_date:
  null_value=1;
  return 0;
}

2502 2503 2504 2505 2506 2507

void Item_func_add_time::print(String *str)
{
  if (is_date)
  {
    DBUG_ASSERT(sign > 0);
2508
    str->append("timestamp(", 10);
2509 2510 2511 2512
  }
  else
  {
    if (sign > 0)
2513
      str->append("addtime(", 8);
2514
    else
2515
      str->append("subtime(", 8);
2516 2517 2518 2519 2520 2521 2522 2523
  }
  args[0]->print(str);
  str->append(',');
  args[0]->print(str);
  str->append(')');
}


gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2524 2525 2526 2527 2528 2529 2530 2531 2532 2533
/*
  TIMEDIFF(t,s) is a time function that calculates the 
  time value between a start and end time.

  t and s: time_or_datetime_expression
  Result: Time value
*/

String *Item_func_timediff::val_str(String *str)
{
2534
  DBUG_ASSERT(fixed == 1);
2535 2536
  longlong seconds;
  long microseconds;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548
  int l_sign= 1;
  TIME l_time1 ,l_time2, l_time3;

  null_value= 0;  
  if (args[0]->get_time(&l_time1) ||
      args[1]->get_time(&l_time2) ||
      l_time1.time_type != l_time2.time_type)
    goto null_date;

  if (l_time1.neg != l_time2.neg)
    l_sign= -l_sign;

2549 2550 2551 2552 2553 2554 2555 2556 2557 2558
  l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign,
			      &seconds, &microseconds);

  /*
    For MYSQL_TIMESTAMP_TIME only:
      If first argument was negative and diff between arguments
      is non-zero we need to swap sign to get proper result.
  */
  if (l_time1.neg && (seconds || microseconds))
    l_time3.neg= 1-l_time3.neg;         // Swap sign of result
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2559

2560
  calc_time_from_sec(&l_time3, (long) seconds, microseconds);
2561

2562 2563 2564
  if (!make_datetime(l_time1.second_part || l_time2.second_part ?
		     TIME_MICROSECOND : TIME_ONLY,
		     &l_time3, str))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579
    return str;

null_date:
  null_value=1;
  return 0;
}

/*
  MAKETIME(h,m,s) is a time function that calculates a time value 
  from the total number of hours, minutes, and seconds.
  Result: Time value
*/

String *Item_func_maketime::val_str(String *str)
{
2580
  DBUG_ASSERT(fixed == 1);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2581 2582
  TIME ltime;

monty@mysql.com's avatar
monty@mysql.com committed
2583 2584 2585
  long hour=   (long) args[0]->val_int();
  long minute= (long) args[1]->val_int();
  long second= (long) args[2]->val_int();
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2586 2587 2588 2589 2590

  if ((null_value=(args[0]->null_value || 
		   args[1]->null_value ||
		   args[2]->null_value || 
		   minute > 59 || minute < 0 || 
2591 2592 2593
		   second > 59 || second < 0 ||
		   str->alloc(19))))
    return 0;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2594 2595 2596 2597 2598 2599 2600

  ltime.neg= 0;
  if (hour < 0)
  {
    ltime.neg= 1;
    hour= -hour;
  }
monty@mysql.com's avatar
monty@mysql.com committed
2601 2602 2603
  ltime.hour=   (ulong) hour;
  ltime.minute= (ulong) minute;
  ltime.second= (ulong) second;
2604 2605
  make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
  return str;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2606 2607
}

2608

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2609
/*
2610 2611
  MICROSECOND(a) is a function ( extraction) that extracts the microseconds
  from a.
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2612 2613 2614 2615

  a: Datetime or time value
  Result: int value
*/
2616

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2617 2618
longlong Item_func_microsecond::val_int()
{
2619
  DBUG_ASSERT(fixed == 1);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2620 2621 2622 2623 2624
  TIME ltime;
  if (!get_arg0_time(&ltime))
    return ltime.second_part;
  return 0;
}
2625 2626 2627 2628


String *Item_func_get_format::val_str(String *str)
{
2629
  DBUG_ASSERT(fixed == 1);
2630 2631 2632 2633
  const char *format_name;
  KNOWN_DATE_TIME_FORMAT *format;
  String *val= args[0]->val_str(str);
  ulong val_len;
2634

2635 2636 2637 2638 2639 2640 2641
  if ((null_value= args[0]->null_value))
    return 0;    

  val_len= val->length();
  for (format= &known_date_time_formats[0];
       (format_name= format->format_name);
       format++)
2642
  {
2643 2644 2645 2646 2647 2648
    uint format_name_len;
    format_name_len= strlen(format_name);
    if (val_len == format_name_len &&
	!my_strnncoll(&my_charset_latin1, 
		      (const uchar *) val->ptr(), val_len, 
		      (const uchar *) format_name, val_len))
2649
    {
2650 2651 2652
      const char *format_str= get_date_time_format_str(format, type);
      str->set(format_str, strlen(format_str), &my_charset_bin);
      return str;
2653 2654 2655
    }
  }

2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666
  null_value= 1;
  return 0;
}


void Item_func_get_format::print(String *str)
{
  str->append(func_name());
  str->append('(');

  switch (type) {
2667
  case MYSQL_TIMESTAMP_DATE:
2668 2669
    str->append("DATE, ");
    break;
2670
  case MYSQL_TIMESTAMP_DATETIME:
2671 2672
    str->append("DATETIME, ");
    break;
2673
  case MYSQL_TIMESTAMP_TIME:
2674 2675 2676 2677 2678 2679 2680 2681 2682 2683
    str->append("TIME, ");
    break;
  default:
    DBUG_ASSERT(0);
  }
  args[0]->print(str);
  str->append(')');
}


2684
/*
2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
  Get type of datetime value (DATE/TIME/...) which will be produced
  according to format string.

  SYNOPSIS
    get_date_time_result_type()
      format - format string
      length - length of format string

  NOTE
    We don't process day format's characters('D', 'd', 'e') because day
    may be a member of all date/time types.

    Format specifiers supported by this function should be in sync with
    specifiers supported by extract_date_time() function.

  RETURN VALUE
    One of date_time_format_types values:
    DATE_TIME_MICROSECOND, DATE_TIME, DATE_ONLY, TIME_MICROSECOND, TIME_ONLY
2703 2704
*/

2705 2706
static date_time_format_types
get_date_time_result_type(const char *format, uint length)
2707 2708
{
  const char *time_part_frms= "HISThiklrs";
2709
  const char *date_part_frms= "MVUXYWabcjmvuxyw";
2710 2711 2712 2713 2714 2715 2716 2717 2718 2719
  bool date_part_used= 0, time_part_used= 0, frac_second_used= 0;
  
  const char *val= format;
  const char *end= format + length;

  for (; val != end && val != end; val++)
  {
    if (*val == '%' && val+1 != end)
    {
      val++;
2720 2721 2722
      if (*val == 'f')
        frac_second_used= time_part_used= 1;
      else if (!time_part_used && strchr(time_part_frms, *val))
2723 2724 2725
	time_part_used= 1;
      else if (!date_part_used && strchr(date_part_frms, *val))
	date_part_used= 1;
2726 2727 2728 2729 2730 2731
      if (date_part_used && frac_second_used)
      {
        /*
          frac_second_used implies time_part_used, and thus we already
          have all types of date-time components and can end our search.
        */
2732
	return DATE_TIME_MICROSECOND;
2733
      }
2734 2735 2736
    }
  }

2737 2738 2739
  /* We don't have all three types of date-time components */
  if (frac_second_used)
    return TIME_MICROSECOND;
2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764
  if (time_part_used)
  {
    if (date_part_used)
      return DATE_TIME;
    return TIME_ONLY;
  }
  return DATE_ONLY;
}


Field *Item_func_str_to_date::tmp_table_field(TABLE *t_arg)
{
  if (cached_field_type == MYSQL_TYPE_TIME)
    return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
  if (cached_field_type == MYSQL_TYPE_DATE)
    return (new Field_date(maybe_null, name, t_arg, &my_charset_bin));
  if (cached_field_type == MYSQL_TYPE_DATETIME)
    return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
  return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin));
}


void Item_func_str_to_date::fix_length_and_dec()
{
  char format_buff[64];
monty@mysql.com's avatar
monty@mysql.com committed
2765 2766
  String format_str(format_buff, sizeof(format_buff), &my_charset_bin);
  String *format;
2767 2768 2769 2770
  maybe_null= 1;
  decimals=0;
  cached_field_type= MYSQL_TYPE_STRING;
  max_length= MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
2771
  cached_timestamp_type= MYSQL_TIMESTAMP_NONE;
gvb@phoenix.(none)'s avatar
gvb@phoenix.(none) committed
2772
  format= args[1]->val_str(&format_str);
gvb@phoenix.(none)'s avatar
gvb@phoenix.(none) committed
2773
  if (!args[1]->null_value && (const_item= args[1]->const_item()))
2774
  {
2775 2776
    cached_format_type= get_date_time_result_type(format->ptr(),
                                                  format->length());
2777 2778
    switch (cached_format_type) {
    case DATE_ONLY:
2779
      cached_timestamp_type= MYSQL_TIMESTAMP_DATE;
2780 2781 2782 2783 2784
      cached_field_type= MYSQL_TYPE_DATE; 
      max_length= MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
      break;
    case TIME_ONLY:
    case TIME_MICROSECOND:
2785
      cached_timestamp_type= MYSQL_TIMESTAMP_TIME;
2786 2787 2788 2789
      cached_field_type= MYSQL_TYPE_TIME; 
      max_length= MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
      break;
    default:
2790
      cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME;
2791 2792 2793 2794 2795 2796
      cached_field_type= MYSQL_TYPE_DATETIME; 
      break;
    }
  }
}

2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808
bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date)
{
  DATE_TIME_FORMAT date_time_format;
  char val_buff[64], format_buff[64];
  String val_str(val_buff, sizeof(val_buff), &my_charset_bin), *val;
  String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format;

  val=    args[0]->val_str(&val_str);
  format= args[1]->val_str(&format_str);
  if (args[0]->null_value || args[1]->null_value)
    goto null_date;

2809
  null_value= 0;
2810 2811 2812 2813
  bzero((char*) ltime, sizeof(ltime));
  date_time_format.format.str=    (char*) format->ptr();
  date_time_format.format.length= format->length();
  if (extract_date_time(&date_time_format, val->ptr(), val->length(),
2814
			ltime, cached_timestamp_type, 0))
2815
    goto null_date;
2816
  if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day)
2817 2818 2819 2820 2821 2822 2823 2824 2825
  {
    /*
      Day part for time type can be nonzero value and so 
      we should add hours from day part to hour part to
      keep valid time value.
    */
    ltime->hour+= ltime->day*24;
    ltime->day= 0;
  }
2826 2827 2828 2829
  return 0;

null_date:
  return (null_value=1);
2830 2831 2832 2833 2834
}


String *Item_func_str_to_date::val_str(String *str)
{
2835
  DBUG_ASSERT(fixed == 1);
2836 2837
  TIME ltime;

2838 2839
  if (Item_func_str_to_date::get_date(&ltime, TIME_FUZZY_DATE))
    return 0;
2840

2841 2842
  if (!make_datetime((const_item ? cached_format_type :
		     (ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME)),
2843 2844
		     &ltime, str))
    return str;
2845 2846
  return 0;
}
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2847 2848


2849
bool Item_func_last_day::get_date(TIME *ltime, uint fuzzy_date)
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2850
{
2851
  if (get_arg0_date(ltime, fuzzy_date & ~TIME_FUZZY_DATE))
2852 2853 2854 2855 2856
    return 1;
  uint month_idx= ltime->month-1;
  ltime->day= days_in_month[month_idx];
  if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366)
    ltime->day= 29;
2857
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
2858 2859
  return 0;
}