1 /*
2 Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
23
24 /*
25 Most of the following code and structures were derived from
26 public domain code from ftp://elsie.nci.nih.gov/pub
27 (We will refer to this code as to elsie-code further.)
28 */
29
30 #include <my_global.h>
31 #include <algorithm>
32
33 #if !defined(TZINFO2SQL)
34 #include "tztime.h"
35 #include "sql_time.h" // localtime_to_TIME
36 #include "sql_base.h" // open_trans_system_tables_for_read,
37 // close_trans_system_tables
38 #include "log.h"
39 #else
40 #include <my_time.h>
41 #include "tztime.h"
42 #include <my_sys.h>
43 #endif
44
45 #include "tzfile.h"
46 #include <m_string.h>
47 #include <my_dir.h>
48 #include <mysql/psi/mysql_file.h>
49 #include "debug_sync.h"
50
51 /*
52 Macro for reading 32-bit integer from network byte order (big-endian)
53 from a unaligned memory location.
54 */
55 #define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) | \
56 (((uint32) ((uchar) (A)[2])) << 8) | \
57 (((uint32) ((uchar) (A)[1])) << 16) | \
58 (((uint32) ((uchar) (A)[0])) << 24))
59
60 using std::min;
61
62 /*
63 Now we don't use abbreviations in server but we will do this in future.
64 */
65 #if defined(TZINFO2SQL)
66 #define ABBR_ARE_USED
67 #else
68 #if !defined(DBUG_OFF)
69 /* Let use abbreviations for debug purposes */
70 #undef ABBR_ARE_USED
71 #define ABBR_ARE_USED
72 #endif /* !defined(DBUG_OFF) */
73 #endif /* defined(TZINFO2SQL) */
74
75 /* Structure describing local time type (e.g. Moscow summer time (MSD)) */
76 typedef struct ttinfo
77 {
78 long tt_gmtoff; // Offset from UTC in seconds
79 uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
80 #ifdef ABBR_ARE_USED
81 uint tt_abbrind; // Index of start of abbreviation for this time type.
82 #endif
83 /*
84 We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
85 struct since we don't support POSIX-style TZ descriptions in variables.
86 */
87 } TRAN_TYPE_INFO;
88
89 /* Structure describing leap-second corrections. */
90 typedef struct lsinfo
91 {
92 my_time_t ls_trans; // Transition time
93 long ls_corr; // Correction to apply
94 } LS_INFO;
95
96 /*
97 Structure with information describing ranges of my_time_t shifted to local
98 time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
99 See comments for TIME_to_gmt_sec() for more info.
100 */
101 typedef struct revtinfo
102 {
103 long rt_offset; // Offset of local time from UTC in seconds
104 uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
105 } REVT_INFO;
106
107 #ifdef TZNAME_MAX
108 #define MY_TZNAME_MAX TZNAME_MAX
109 #endif
110 #ifndef TZNAME_MAX
111 #define MY_TZNAME_MAX 255
112 #endif
113
114 /*
115 Structure which fully describes time zone which is
116 described in our db or in zoneinfo files.
117 */
118 typedef struct st_time_zone_info
119 {
120 uint leapcnt; // Number of leap-second corrections
121 uint timecnt; // Number of transitions between time types
122 uint typecnt; // Number of local time types
123 size_t charcnt;// Number of characters used for abbreviations
124 uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
125 /* The following are dynamical arrays are allocated in MEM_ROOT */
126 my_time_t *ats; // Times of transitions between time types
127 uchar *types; // Local time types for transitions
128 TRAN_TYPE_INFO *ttis; // Local time types descriptions
129 #ifdef ABBR_ARE_USED
130 /* Storage for local time types abbreviations. They are stored as ASCIIZ */
131 char *chars;
132 #endif
133 /*
134 Leap seconds corrections descriptions, this array is shared by
135 all time zones who use leap seconds.
136 */
137 LS_INFO *lsis;
138 /*
139 Starting points and descriptions of shifted my_time_t (my_time_t + offset)
140 ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
141 Used for tm -> my_time_t conversion.
142 */
143 my_time_t *revts;
144 REVT_INFO *revtis;
145 /*
146 Time type which is used for times smaller than first transition or if
147 there are no transitions at all.
148 */
149 TRAN_TYPE_INFO *fallback_tti;
150
151 } TIME_ZONE_INFO;
152
153
154 static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
155
156
157 #if defined(TZINFO2SQL)
158
159 #ifdef ABBR_ARE_USED
160 static const char* const MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE=
161 "Local time zone must be set--see zic manual page";
162 #endif
163
164 /*
165 Load time zone description from zoneinfo (TZinfo) file.
166
167 SYNOPSIS
168 tz_load()
169 name - path to zoneinfo file
170 sp - TIME_ZONE_INFO structure to fill
171
172 RETURN VALUES
173 0 - Ok
174 1 - Error
175 */
176 static my_bool
tz_load(const char * name,TIME_ZONE_INFO * sp,MEM_ROOT * storage)177 tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
178 {
179 uchar *p;
180 size_t read_from_file;
181 uint i;
182 MYSQL_FILE *file;
183
184 if (!(file= mysql_file_fopen(0, name, O_RDONLY|O_BINARY, MYF(MY_WME))))
185 return 1;
186 {
187 union
188 {
189 struct tzhead tzhead;
190 uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
191 TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
192 #ifdef ABBR_ARE_USED
193 MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
194 #endif
195 sizeof(LS_INFO) * TZ_MAX_LEAPS];
196 } u;
197 uint ttisstdcnt;
198 uint ttisgmtcnt;
199 char *tzinfo_buf;
200
201 read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
202
203 if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
204 return 1;
205
206 if (read_from_file < sizeof(struct tzhead))
207 return 1;
208
209 ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
210 ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
211 sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
212 sp->timecnt= int4net(u.tzhead.tzh_timecnt);
213 sp->typecnt= int4net(u.tzhead.tzh_typecnt);
214 sp->charcnt= int4net(u.tzhead.tzh_charcnt);
215 p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
216 if (sp->leapcnt > TZ_MAX_LEAPS ||
217 sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
218 sp->timecnt > TZ_MAX_TIMES ||
219 sp->charcnt > TZ_MAX_CHARS ||
220 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
221 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
222 return 1;
223 if ((uint)(read_from_file - (p - u.buf)) <
224 sp->timecnt * 4 + /* ats */
225 sp->timecnt + /* types */
226 sp->typecnt * (4 + 2) + /* ttinfos */
227 sp->charcnt + /* chars */
228 sp->leapcnt * (4 + 4) + /* lsinfos */
229 ttisstdcnt + /* ttisstds */
230 ttisgmtcnt) /* ttisgmts */
231 return 1;
232
233 #ifdef ABBR_ARE_USED
234 size_t start_of_zone_abbrev= sizeof(struct tzhead) +
235 sp->timecnt * 4 + /* ats */
236 sp->timecnt + /* types */
237 sp->typecnt * (4 + 2); /* ttinfos */
238
239 /*
240 Check that timezone file doesn't contain junk timezone data.
241 */
242 if (!memcmp(u.buf + start_of_zone_abbrev,
243 MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE,
244 std::min(sizeof(MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE) - 1,
245 sp->charcnt)))
246 return true;
247
248 size_t abbrs_buf_len= sp->charcnt+1;
249 #endif
250
251 if (!(tzinfo_buf= (char *)alloc_root(storage,
252 ALIGN_SIZE(sp->timecnt *
253 sizeof(my_time_t)) +
254 ALIGN_SIZE(sp->timecnt) +
255 ALIGN_SIZE(sp->typecnt *
256 sizeof(TRAN_TYPE_INFO)) +
257 #ifdef ABBR_ARE_USED
258 ALIGN_SIZE(abbrs_buf_len) +
259 #endif
260 sp->leapcnt * sizeof(LS_INFO))))
261 return 1;
262
263 sp->ats= (my_time_t *)tzinfo_buf;
264 tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
265 sp->types= (uchar *)tzinfo_buf;
266 tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
267 sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
268 tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
269 #ifdef ABBR_ARE_USED
270 sp->chars= tzinfo_buf;
271 tzinfo_buf+= ALIGN_SIZE(abbrs_buf_len);
272 #endif
273 sp->lsis= (LS_INFO *)tzinfo_buf;
274
275 for (i= 0; i < sp->timecnt; i++, p+= 4)
276 sp->ats[i]= int4net(p);
277
278 for (i= 0; i < sp->timecnt; i++)
279 {
280 sp->types[i]= *p++;
281 if (sp->types[i] >= sp->typecnt)
282 return 1;
283 }
284 for (i= 0; i < sp->typecnt; i++)
285 {
286 TRAN_TYPE_INFO * ttisp;
287
288 ttisp= &sp->ttis[i];
289 ttisp->tt_gmtoff= int4net(p);
290 p+= 4;
291 ttisp->tt_isdst= *p++;
292 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
293 return 1;
294 ttisp->tt_abbrind= *p++;
295 if (ttisp->tt_abbrind > sp->charcnt)
296 return 1;
297 }
298 for (i= 0; i < sp->charcnt; i++)
299 sp->chars[i]= *p++;
300 sp->chars[i]= '\0'; /* ensure '\0' at end */
301 for (i= 0; i < sp->leapcnt; i++)
302 {
303 LS_INFO *lsisp;
304
305 lsisp= &sp->lsis[i];
306 lsisp->ls_trans= int4net(p);
307 p+= 4;
308 lsisp->ls_corr= int4net(p);
309 p+= 4;
310 }
311 /*
312 Since we don't support POSIX style TZ definitions in variables we
313 don't read further like glibc or elsie code.
314 */
315 }
316
317 return prepare_tz_info(sp, storage);
318 }
319 #endif /* defined(TZINFO2SQL) */
320
321
322 /*
323 Finish preparation of time zone description for use in TIME_to_gmt_sec()
324 and gmt_sec_to_TIME() functions.
325
326 SYNOPSIS
327 prepare_tz_info()
328 sp - pointer to time zone description
329 storage - pointer to MEM_ROOT where arrays for map allocated
330
331 DESCRIPTION
332 First task of this function is to find fallback time type which will
333 be used if there are no transitions or we have moment in time before
334 any transitions.
335 Second task is to build "shifted my_time_t" -> my_time_t map used in
336 MYSQL_TIME -> my_time_t conversion.
337 Note: See description of TIME_to_gmt_sec() function first.
338 In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
339 which defines "shifted by tz offset and leap seconds my_time_t" ->
340 my_time_t function wich is almost the same (except ranges of ambiguity)
341 as reverse function to piecewise linear function used for my_time_t ->
342 "shifted my_time_t" conversion and which is also specified as table in
343 zoneinfo file or in our db (It is specified as start of time type ranges
344 and time type offsets). So basic idea is very simple - let us iterate
345 through my_time_t space from one point of discontinuity of my_time_t ->
346 "shifted my_time_t" function to another and build our approximation of
347 reverse function. (Actually we iterate through ranges on which
348 my_time_t -> "shifted my_time_t" is linear function).
349
350 RETURN VALUES
351 0 Ok
352 1 Error
353 */
354 static my_bool
prepare_tz_info(TIME_ZONE_INFO * sp,MEM_ROOT * storage)355 prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
356 {
357 my_time_t cur_t= MY_TIME_T_MIN;
358 my_time_t cur_l, end_t, end_l= 0;
359 my_time_t cur_max_seen_l= MY_TIME_T_MIN;
360 long cur_offset, cur_corr, cur_off_and_corr;
361 uint next_trans_idx, next_leap_idx;
362 uint i;
363 /*
364 Temporary arrays where we will store tables. Needed because
365 we don't know table sizes ahead. (Well we can estimate their
366 upper bound but this will take extra space.)
367 */
368 my_time_t revts[TZ_MAX_REV_RANGES];
369 REVT_INFO revtis[TZ_MAX_REV_RANGES];
370
371 /*
372 Let us setup fallback time type which will be used if we have not any
373 transitions or if we have moment of time before first transition.
374 We will find first non-DST local time type and use it (or use first
375 local time type if all of them are DST types).
376 */
377 for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
378 /* no-op */ ;
379 if (i == sp->typecnt)
380 i= 0;
381 sp->fallback_tti= &(sp->ttis[i]);
382
383
384 /*
385 Let us build shifted my_time_t -> my_time_t map.
386 */
387 sp->revcnt= 0;
388
389 /* Let us find initial offset */
390 if (sp->timecnt == 0 || cur_t < sp->ats[0])
391 {
392 /*
393 If we have not any transitions or t is before first transition we are using
394 already found fallback time type which index is already in i.
395 */
396 next_trans_idx= 0;
397 }
398 else
399 {
400 /* cur_t == sp->ats[0] so we found transition */
401 i= sp->types[0];
402 next_trans_idx= 1;
403 }
404
405 cur_offset= sp->ttis[i].tt_gmtoff;
406
407
408 /* let us find leap correction... unprobable, but... */
409 for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
410 cur_t >= sp->lsis[next_leap_idx].ls_trans;
411 ++next_leap_idx)
412 continue;
413
414 if (next_leap_idx > 0)
415 cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
416 else
417 cur_corr= 0;
418
419 /* Iterate trough t space */
420 while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
421 {
422 cur_off_and_corr= cur_offset - cur_corr;
423
424 /*
425 We assuming that cur_t could be only overflowed downwards,
426 we also assume that end_t won't be overflowed in this case.
427 */
428 if (cur_off_and_corr < 0 &&
429 cur_t < MY_TIME_T_MIN - cur_off_and_corr)
430 cur_t= MY_TIME_T_MIN - cur_off_and_corr;
431
432 cur_l= cur_t + cur_off_and_corr;
433
434 /*
435 Let us choose end_t as point before next time type change or leap
436 second correction.
437 */
438 end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
439 MY_TIME_T_MAX,
440 (next_leap_idx < sp->leapcnt) ?
441 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
442 /*
443 again assuming that end_t can be overlowed only in positive side
444 we also assume that end_t won't be overflowed in this case.
445 */
446 if (cur_off_and_corr > 0 &&
447 end_t > MY_TIME_T_MAX - cur_off_and_corr)
448 end_t= MY_TIME_T_MAX - cur_off_and_corr;
449
450 end_l= end_t + cur_off_and_corr;
451
452
453 if (end_l > cur_max_seen_l)
454 {
455 /* We want special handling in the case of first range */
456 if (cur_max_seen_l == MY_TIME_T_MIN)
457 {
458 revts[sp->revcnt]= cur_l;
459 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
460 revtis[sp->revcnt].rt_type= 0;
461 sp->revcnt++;
462 cur_max_seen_l= end_l;
463 }
464 else
465 {
466 if (cur_l > cur_max_seen_l + 1)
467 {
468 /* We have a spring time-gap and we are not at the first range */
469 revts[sp->revcnt]= cur_max_seen_l + 1;
470 revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
471 revtis[sp->revcnt].rt_type= 1;
472 sp->revcnt++;
473 if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
474 break; /* That was too much */
475 cur_max_seen_l= cur_l - 1;
476 }
477
478 /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
479
480 revts[sp->revcnt]= cur_max_seen_l + 1;
481 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
482 revtis[sp->revcnt].rt_type= 0;
483 sp->revcnt++;
484 cur_max_seen_l= end_l;
485 }
486 }
487
488 if (end_t == MY_TIME_T_MAX ||
489 ((cur_off_and_corr > 0) &&
490 (end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
491 /* end of t space */
492 break;
493
494 cur_t= end_t + 1;
495
496 /*
497 Let us find new offset and correction. Because of our choice of end_t
498 cur_t can only be point where new time type starts or/and leap
499 correction is performed.
500 */
501 if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
502 if (next_trans_idx < sp->timecnt &&
503 cur_t == sp->ats[next_trans_idx])
504 {
505 /* We are at offset point */
506 cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
507 ++next_trans_idx;
508 }
509
510 if (next_leap_idx < sp->leapcnt &&
511 cur_t == sp->lsis[next_leap_idx].ls_trans)
512 {
513 /* we are at leap point */
514 cur_corr= sp->lsis[next_leap_idx].ls_corr;
515 ++next_leap_idx;
516 }
517 }
518
519 /* check if we have had enough space */
520 if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
521 return 1;
522
523 /* set maximum end_l as finisher */
524 revts[sp->revcnt]= end_l;
525
526 /* Allocate arrays of proper size in sp and copy result there */
527 if (!(sp->revts= (my_time_t *)alloc_root(storage,
528 sizeof(my_time_t) * (sp->revcnt + 1))) ||
529 !(sp->revtis= (REVT_INFO *)alloc_root(storage,
530 sizeof(REVT_INFO) * sp->revcnt)))
531 return 1;
532
533 memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
534 memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
535
536 return 0;
537 }
538
539
540 #if !defined(TZINFO2SQL)
541
542 static const uint mon_lengths[2][MONS_PER_YEAR]=
543 {
544 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
545 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
546 };
547
548 static const uint mon_starts[2][MONS_PER_YEAR]=
549 {
550 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
551 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
552 };
553
554 static const uint year_lengths[2]=
555 {
556 DAYS_PER_NYEAR, DAYS_PER_LYEAR
557 };
558
559 #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
560
561
562 /*
563 Converts time from my_time_t representation (seconds in UTC since Epoch)
564 to broken down representation using given local time zone offset.
565
566 SYNOPSIS
567 sec_to_TIME()
568 tmp - pointer to structure for broken down representation
569 t - my_time_t value to be converted
570 offset - local time zone offset
571
572 DESCRIPTION
573 Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
574 (from elsie code) because doesn't contain any leap correction and
575 TM_GMTOFF and is_dst setting and contains some MySQL specific
576 initialization. Funny but with removing of these we almost have
577 glibc's offtime function.
578 */
579 static void
sec_to_TIME(MYSQL_TIME * tmp,my_time_t t,long offset)580 sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
581 {
582 long days;
583 long rem;
584 int y;
585 int yleap;
586 const uint *ip;
587
588 days= (long) (t / SECS_PER_DAY);
589 rem= (long) (t % SECS_PER_DAY);
590
591 /*
592 We do this as separate step after dividing t, because this
593 allows us handle times near my_time_t bounds without overflows.
594 */
595 rem+= offset;
596 while (rem < 0)
597 {
598 rem+= SECS_PER_DAY;
599 days--;
600 }
601 while (rem >= SECS_PER_DAY)
602 {
603 rem -= SECS_PER_DAY;
604 days++;
605 }
606 tmp->hour= (uint)(rem / SECS_PER_HOUR);
607 rem= rem % SECS_PER_HOUR;
608 tmp->minute= (uint)(rem / SECS_PER_MIN);
609 /*
610 A positive leap second requires a special
611 representation. This uses "... ??:59:60" et seq.
612 */
613 tmp->second= (uint)(rem % SECS_PER_MIN);
614
615 y= EPOCH_YEAR;
616 while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
617 {
618 int newy;
619
620 newy= y + days / DAYS_PER_NYEAR;
621 if (days < 0)
622 newy--;
623 days-= (newy - y) * DAYS_PER_NYEAR +
624 LEAPS_THRU_END_OF(newy - 1) -
625 LEAPS_THRU_END_OF(y - 1);
626 y= newy;
627 }
628 tmp->year= y;
629
630 ip= mon_lengths[yleap];
631 for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
632 days= days - (long) ip[tmp->month];
633 tmp->month++;
634 tmp->day= (uint)(days + 1);
635
636 /* filling MySQL specific MYSQL_TIME members */
637 tmp->neg= 0; tmp->second_part= 0;
638 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
639 }
640
641
642 /*
643 Find time range wich contains given my_time_t value
644
645 SYNOPSIS
646 find_time_range()
647 t - my_time_t value for which we looking for range
648 range_boundaries - sorted array of range starts.
649 higher_bound - number of ranges
650
651 DESCRIPTION
652 Performs binary search for range which contains given my_time_t value.
653 It has sense if number of ranges is greater than zero and my_time_t value
654 is greater or equal than beginning of first range. It also assumes that
655 t belongs to some range specified or end of last is MY_TIME_T_MAX.
656
657 With this localtime_r on real data may takes less time than with linear
658 search (I've seen 30% speed up).
659
660 RETURN VALUE
661 Index of range to which t belongs
662 */
663 static uint
find_time_range(my_time_t t,const my_time_t * range_boundaries,uint higher_bound)664 find_time_range(my_time_t t, const my_time_t *range_boundaries,
665 uint higher_bound)
666 {
667 uint i, lower_bound= 0;
668
669 /*
670 Function will work without this assertion but result would be meaningless.
671 */
672 DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
673
674 /*
675 Do binary search for minimal interval which contain t. We preserve:
676 range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
677 invariant and decrease this higher_bound - lower_bound gap twice
678 times on each step.
679 */
680
681 while (higher_bound - lower_bound > 1)
682 {
683 i= (lower_bound + higher_bound) >> 1;
684 if (range_boundaries[i] <= t)
685 lower_bound= i;
686 else
687 higher_bound= i;
688 }
689 return lower_bound;
690 }
691
692 /*
693 Find local time transition for given my_time_t.
694
695 SYNOPSIS
696 find_transition_type()
697 t - my_time_t value to be converted
698 sp - pointer to struct with time zone description
699
700 RETURN VALUE
701 Pointer to structure in time zone description describing
702 local time type for given my_time_t.
703 */
704 static
705 const TRAN_TYPE_INFO *
find_transition_type(my_time_t t,const TIME_ZONE_INFO * sp)706 find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
707 {
708 if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
709 {
710 /*
711 If we have not any transitions or t is before first transition let
712 us use fallback time type.
713 */
714 return sp->fallback_tti;
715 }
716
717 /*
718 Do binary search for minimal interval between transitions which
719 contain t. With this localtime_r on real data may takes less
720 time than with linear search (I've seen 30% speed up).
721 */
722 return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
723 }
724
725
726 /*
727 Converts time in my_time_t representation (seconds in UTC since Epoch) to
728 broken down MYSQL_TIME representation in local time zone.
729
730 SYNOPSIS
731 gmt_sec_to_TIME()
732 tmp - pointer to structure for broken down represenatation
733 sec_in_utc - my_time_t value to be converted
734 sp - pointer to struct with time zone description
735
736 TODO
737 We can improve this function by creating joined array of transitions and
738 leap corrections. This will require adding extra field to TRAN_TYPE_INFO
739 for storing number of "extra" seconds to minute occured due to correction
740 (60th and 61st second, look how we calculate them as "hit" in this
741 function).
742 Under realistic assumptions about frequency of transitions the same array
743 can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
744 implement tweaked binary search which will take into account that some
745 MYSQL_TIME has two matching my_time_t ranges and some of them have none.
746 */
747 static void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t sec_in_utc,const TIME_ZONE_INFO * sp)748 gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
749 {
750 const TRAN_TYPE_INFO *ttisp;
751 const LS_INFO *lp;
752 long corr= 0;
753 int hit= 0;
754 int i;
755
756 /*
757 Find proper transition (and its local time type) for our sec_in_utc value.
758 Funny but again by separating this step in function we receive code
759 which very close to glibc's code. No wonder since they obviously use
760 the same base and all steps are sensible.
761 */
762 ttisp= find_transition_type(sec_in_utc, sp);
763
764 /*
765 Let us find leap correction for our sec_in_utc value and number of extra
766 secs to add to this minute.
767 This loop is rarely used because most users will use time zones without
768 leap seconds, and even in case when we have such time zone there won't
769 be many iterations (we have about 22 corrections at this moment (2004)).
770 */
771 for ( i= sp->leapcnt; i-- > 0; )
772 {
773 lp= &sp->lsis[i];
774 if (sec_in_utc >= lp->ls_trans)
775 {
776 if (sec_in_utc == lp->ls_trans)
777 {
778 hit= ((i == 0 && lp->ls_corr > 0) ||
779 lp->ls_corr > sp->lsis[i - 1].ls_corr);
780 if (hit)
781 {
782 while (i > 0 &&
783 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
784 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
785 {
786 hit++;
787 i--;
788 }
789 }
790 }
791 corr= lp->ls_corr;
792 break;
793 }
794 }
795
796 sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
797
798 tmp->second+= hit;
799 }
800
801
802 /*
803 Converts local time in broken down representation to local
804 time zone analog of my_time_t represenation.
805
806 SYNOPSIS
807 sec_since_epoch()
808 year, mon, mday, hour, min, sec - broken down representation.
809
810 DESCRIPTION
811 Converts time in broken down representation to my_time_t representation
812 ignoring time zone. Note that we cannot convert back some valid _local_
813 times near ends of my_time_t range because of my_time_t overflow. But we
814 ignore this fact now since MySQL will never pass such argument.
815
816 RETURN VALUE
817 Seconds since epoch time representation.
818 */
819 static my_time_t
sec_since_epoch(int year,int mon,int mday,int hour,int min,int sec)820 sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
821 {
822 /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
823 DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
824 /*
825 It turns out that only whenever month is normalized or unnormalized
826 plays role.
827 */
828 DBUG_ASSERT(mon > 0 && mon < 13);
829 long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
830 LEAPS_THRU_END_OF(year - 1) -
831 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
832 days+= mon_starts[isleap(year)][mon - 1];
833 days+= mday - 1;
834
835 return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
836 SECS_PER_MIN + sec;
837 }
838
839 /*
840 Converts local time in broken down MYSQL_TIME representation to my_time_t
841 representation.
842
843 SYNOPSIS
844 TIME_to_gmt_sec()
845 t - pointer to structure for broken down represenatation
846 sp - pointer to struct with time zone description
847 in_dst_time_gap - pointer to bool which is set to true if datetime
848 value passed doesn't really exist (i.e. falls into
849 spring time-gap) and is not touched otherwise.
850
851 DESCRIPTION
852 This is mktime analog for MySQL. It is essentially different
853 from mktime (or hypotetical my_mktime) because:
854 - It has no idea about tm_isdst member so if it
855 has two answers it will give the smaller one
856 - If we are in spring time gap then it will return
857 beginning of the gap
858 - It can give wrong results near the ends of my_time_t due to
859 overflows, but we are safe since in MySQL we will never
860 call this function for such dates (its restriction for year
861 between 1970 and 2038 gives us several days of reserve).
862 - By default it doesn't support un-normalized input. But if
863 sec_since_epoch() function supports un-normalized dates
864 then this function should handle un-normalized input right,
865 altough it won't normalize structure TIME.
866
867 Traditional approach to problem of conversion from broken down
868 representation to time_t is iterative. Both elsie's and glibc
869 implementation try to guess what time_t value should correspond to
870 this broken-down value. They perform localtime_r function on their
871 guessed value and then calculate the difference and try to improve
872 their guess. Elsie's code guesses time_t value in bit by bit manner,
873 Glibc's code tries to add difference between broken-down value
874 corresponding to guess and target broken-down value to current guess.
875 It also uses caching of last found correction... So Glibc's approach
876 is essentially faster but introduces some undetermenism (in case if
877 is_dst member of broken-down representation (tm struct) is not known
878 and we have two possible answers).
879
880 We use completely different approach. It is better since it is both
881 faster than iterative implementations and fully determenistic. If you
882 look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
883 of two steps:
884 The first is calculating shifted my_time_t value and the second - TIME
885 calculation from shifted my_time_t value (well it is a bit simplified
886 picture). The part in which we are interested in is my_time_t -> shifted
887 my_time_t conversion. It is piecewise linear function which is defined
888 by combination of transition times as break points and times offset
889 as changing function parameter. The possible inverse function for this
890 converison would be ambiguos but with MySQL's restrictions we can use
891 some function which is the same as inverse function on unambigiuos
892 ranges and coincides with one of branches of inverse function in
893 other ranges. Thus we just need to build table which will determine
894 this shifted my_time_t -> my_time_t conversion similar to existing
895 (my_time_t -> shifted my_time_t table). We do this in
896 prepare_tz_info function.
897
898 TODO
899 If we can even more improve this function. For doing this we will need to
900 build joined map of transitions and leap corrections for gmt_sec_to_TIME()
901 function (similar to revts/revtis). Under realistic assumptions about
902 frequency of transitions we can use the same array for TIME_to_gmt_sec().
903 We need to implement special version of binary search for this. Such step
904 will be beneficial to CPU cache since we will decrease data-set used for
905 conversion twice.
906
907 RETURN VALUE
908 Seconds in UTC since Epoch.
909 0 in case of error.
910 */
911 static my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,const TIME_ZONE_INFO * sp,my_bool * in_dst_time_gap)912 TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
913 my_bool *in_dst_time_gap)
914 {
915 my_time_t local_t;
916 uint saved_seconds;
917 uint i;
918 int shift= 0;
919
920 DBUG_ENTER("TIME_to_gmt_sec");
921
922 if (!validate_timestamp_range(t))
923 DBUG_RETURN(0);
924
925
926 /* We need this for correct leap seconds handling */
927 if (t->second < SECS_PER_MIN)
928 saved_seconds= 0;
929 else
930 saved_seconds= t->second;
931
932 /*
933 NOTE: to convert full my_time_t range we do a shift of the
934 boundary dates here to avoid overflow of my_time_t.
935 We use alike approach in my_system_gmt_sec().
936
937 However in that function we also have to take into account
938 overflow near 0 on some platforms. That's because my_system_gmt_sec
939 uses localtime_r(), which doesn't work with negative values correctly
940 on platforms with unsigned time_t (QNX). Here we don't use localtime()
941 => we negative values of local_t are ok.
942 */
943
944 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
945 {
946 /*
947 We will pass (t->day - shift) to sec_since_epoch(), and
948 want this value to be a positive number, so we shift
949 only dates > 4.01.2038 (to avoid owerflow).
950 */
951 shift= 2;
952 }
953
954
955 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
956 t->hour, t->minute,
957 saved_seconds ? 0 : t->second);
958
959 /* We have at least one range */
960 DBUG_ASSERT(sp->revcnt >= 1);
961
962 if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
963 {
964 /*
965 This means that source time can't be represented as my_time_t due to
966 limited my_time_t range.
967 */
968 DBUG_RETURN(0);
969 }
970
971 /* binary search for our range */
972 i= find_time_range(local_t, sp->revts, sp->revcnt);
973
974 /*
975 As there are no offset switches at the end of TIMESTAMP range,
976 we could simply check for overflow here (and don't need to bother
977 about DST gaps etc)
978 */
979 if (shift)
980 {
981 if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
982 sp->revtis[i].rt_offset - saved_seconds))
983 {
984 DBUG_RETURN(0); /* my_time_t overflow */
985 }
986 local_t+= shift * SECS_PER_DAY;
987 }
988
989 if (sp->revtis[i].rt_type)
990 {
991 /*
992 Oops! We are in spring time gap.
993 May be we should return error here?
994 Now we are returning my_time_t value corresponding to the
995 beginning of the gap.
996 */
997 *in_dst_time_gap= 1;
998 local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
999 }
1000 else
1001 local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
1002
1003 /* check for TIMESTAMP_MAX_VALUE was already done above */
1004 if (local_t < TIMESTAMP_MIN_VALUE)
1005 local_t= 0;
1006
1007 DBUG_RETURN(local_t);
1008 }
1009
1010
1011 /*
1012 End of elsie derived code.
1013 */
1014 #endif /* !defined(TZINFO2SQL) */
1015
1016
1017 #if !defined(TZINFO2SQL)
1018
1019 /*
1020 String with names of SYSTEM time zone.
1021 */
1022 static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
1023
1024
1025 /*
1026 Instance of this class represents local time zone used on this system
1027 (specified by TZ environment variable or via any other system mechanism).
1028 It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1029 and is always available. Because of this it is used by default - if there
1030 were no explicit time zone specified. On the other hand because of this
1031 conversion methods provided by this class is significantly slower and
1032 possibly less multi-threaded-friendly than corresponding Time_zone_db
1033 methods so the latter should be preffered there it is possible.
1034 */
1035 class Time_zone_system : public Time_zone
1036 {
1037 public:
Time_zone_system()1038 Time_zone_system() {} /* Remove gcc warning */
1039 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1040 my_bool *in_dst_time_gap) const;
1041 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1042 virtual const String * get_name() const;
1043 };
1044
1045
1046 /*
1047 Converts local time in system time zone in MYSQL_TIME representation
1048 to its my_time_t representation.
1049
1050 SYNOPSIS
1051 TIME_to_gmt_sec()
1052 t - pointer to MYSQL_TIME structure with local time in
1053 broken-down representation.
1054 in_dst_time_gap - pointer to bool which is set to true if datetime
1055 value passed doesn't really exist (i.e. falls into
1056 spring time-gap) and is not touched otherwise.
1057
1058 DESCRIPTION
1059 This method uses system function (localtime_r()) for conversion
1060 local time in system time zone in MYSQL_TIME structure to its my_time_t
1061 representation. Unlike the same function for Time_zone_db class
1062 it it won't handle unnormalized input properly. Still it will
1063 return lowest possible my_time_t in case of ambiguity or if we
1064 provide time corresponding to the time-gap.
1065
1066 You should call my_init_time() function before using this function.
1067
1068 RETURN VALUE
1069 Corresponding my_time_t value or 0 in case of error
1070 */
1071 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1072 Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1073 {
1074 long not_used;
1075 return my_system_gmt_sec(t, ¬_used, in_dst_time_gap);
1076 }
1077
1078
1079 /*
1080 Converts time from UTC seconds since Epoch (my_time_t) representation
1081 to system local time zone broken-down representation.
1082
1083 SYNOPSIS
1084 gmt_sec_to_TIME()
1085 tmp - pointer to MYSQL_TIME structure to fill-in
1086 t - my_time_t value to be converted
1087
1088 NOTE
1089 We assume that value passed to this function will fit into time_t range
1090 supported by localtime_r. This conversion is putting restriction on
1091 TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1092 for interaction with client then we can extend TIMESTAMP range down to
1093 the 1902 easily.
1094 */
1095 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1096 Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1097 {
1098 struct tm tmp_tm;
1099 time_t tmp_t= (time_t)t;
1100
1101 localtime_r(&tmp_t, &tmp_tm);
1102 localtime_to_TIME(tmp, &tmp_tm);
1103 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1104 adjust_leap_second(tmp);
1105 }
1106
1107
1108 /*
1109 Get name of time zone
1110
1111 SYNOPSIS
1112 get_name()
1113
1114 RETURN VALUE
1115 Name of time zone as String
1116 */
1117 const String *
get_name() const1118 Time_zone_system::get_name() const
1119 {
1120 return &tz_SYSTEM_name;
1121 }
1122
1123
1124 /*
1125 Instance of this class represents UTC time zone. It uses system gmtime_r
1126 function for conversions and is always available. It is used only for
1127 my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1128 intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1129 */
1130 class Time_zone_utc : public Time_zone
1131 {
1132 public:
Time_zone_utc()1133 Time_zone_utc() {} /* Remove gcc warning */
1134 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1135 my_bool *in_dst_time_gap) const;
1136 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1137 virtual const String * get_name() const;
1138 };
1139
1140
1141 /*
1142 Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1143
1144 SYNOPSIS
1145 TIME_to_gmt_sec()
1146 t - pointer to MYSQL_TIME structure with local time
1147 in broken-down representation.
1148 in_dst_time_gap - pointer to bool which is set to true if datetime
1149 value passed doesn't really exist (i.e. falls into
1150 spring time-gap) and is not touched otherwise.
1151
1152 DESCRIPTION
1153 Since Time_zone_utc is used only internally for my_time_t -> TIME
1154 conversions, this function of Time_zone interface is not implemented for
1155 this class and should not be called.
1156
1157 RETURN VALUE
1158 0
1159 */
1160 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1161 Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1162 {
1163 /* Should be never called */
1164 DBUG_ASSERT(0);
1165 return 0;
1166 }
1167
1168
1169 /*
1170 Converts time from UTC seconds since Epoch (my_time_t) representation
1171 to broken-down representation (also in UTC).
1172
1173 SYNOPSIS
1174 gmt_sec_to_TIME()
1175 tmp - pointer to MYSQL_TIME structure to fill-in
1176 t - my_time_t value to be converted
1177
1178 NOTE
1179 See note for apropriate Time_zone_system method.
1180 */
1181 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1182 Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1183 {
1184 struct tm tmp_tm;
1185 time_t tmp_t= (time_t)t;
1186 gmtime_r(&tmp_t, &tmp_tm);
1187 localtime_to_TIME(tmp, &tmp_tm);
1188 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1189 adjust_leap_second(tmp);
1190 }
1191
1192
1193 /*
1194 Get name of time zone
1195
1196 SYNOPSIS
1197 get_name()
1198
1199 DESCRIPTION
1200 Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1201 is not accessible directly, and hence this function of Time_zone
1202 interface is not implemented for this class and should not be called.
1203
1204 RETURN VALUE
1205 0
1206 */
1207 const String *
get_name() const1208 Time_zone_utc::get_name() const
1209 {
1210 /* Should be never called */
1211 DBUG_ASSERT(0);
1212 return 0;
1213 }
1214
1215
1216 /*
1217 Instance of this class represents some time zone which is
1218 described in mysql.time_zone family of tables.
1219 */
1220 class Time_zone_db : public Time_zone
1221 {
1222 public:
1223 Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1224 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1225 my_bool *in_dst_time_gap) const;
1226 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1227 virtual const String * get_name() const;
1228 private:
1229 TIME_ZONE_INFO *tz_info;
1230 const String *tz_name;
1231 };
1232
1233
1234 /*
1235 Initializes object representing time zone described by mysql.time_zone
1236 tables.
1237
1238 SYNOPSIS
1239 Time_zone_db()
1240 tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1241 according to db or other time zone description
1242 (for example by my_tz_init()).
1243 Several Time_zone_db instances can share one
1244 TIME_ZONE_INFO structure.
1245 tz_name_arg - name of time zone.
1246 */
Time_zone_db(TIME_ZONE_INFO * tz_info_arg,const String * tz_name_arg)1247 Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1248 const String *tz_name_arg):
1249 tz_info(tz_info_arg), tz_name(tz_name_arg)
1250 {
1251 }
1252
1253
1254 /*
1255 Converts local time in time zone described from TIME
1256 representation to its my_time_t representation.
1257
1258 SYNOPSIS
1259 TIME_to_gmt_sec()
1260 t - pointer to MYSQL_TIME structure with local time
1261 in broken-down representation.
1262 in_dst_time_gap - pointer to bool which is set to true if datetime
1263 value passed doesn't really exist (i.e. falls into
1264 spring time-gap) and is not touched otherwise.
1265
1266 DESCRIPTION
1267 Please see ::TIME_to_gmt_sec for function description and
1268 parameter restrictions.
1269
1270 RETURN VALUE
1271 Corresponding my_time_t value or 0 in case of error
1272 */
1273 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1274 Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1275 {
1276 return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
1277 }
1278
1279
1280 /*
1281 Converts time from UTC seconds since Epoch (my_time_t) representation
1282 to local time zone described in broken-down representation.
1283
1284 SYNOPSIS
1285 gmt_sec_to_TIME()
1286 tmp - pointer to MYSQL_TIME structure to fill-in
1287 t - my_time_t value to be converted
1288 */
1289 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1290 Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1291 {
1292 ::gmt_sec_to_TIME(tmp, t, tz_info);
1293 adjust_leap_second(tmp);
1294 }
1295
1296
1297 /*
1298 Get name of time zone
1299
1300 SYNOPSIS
1301 get_name()
1302
1303 RETURN VALUE
1304 Name of time zone as ASCIIZ-string
1305 */
1306 const String *
get_name() const1307 Time_zone_db::get_name() const
1308 {
1309 return tz_name;
1310 }
1311
1312
1313 /*
1314 Instance of this class represents time zone which
1315 was specified as offset from UTC.
1316 */
1317 class Time_zone_offset : public Time_zone
1318 {
1319 public:
1320 Time_zone_offset(long tz_offset_arg);
1321 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1322 my_bool *in_dst_time_gap) const;
1323 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1324 virtual const String * get_name() const;
1325 /*
1326 This have to be public because we want to be able to access it from
1327 my_offset_tzs_get_key() function
1328 */
1329 long offset;
1330 private:
1331 /* Extra reserve because of snprintf */
1332 char name_buff[7+16];
1333 String name;
1334 };
1335
1336
1337 /*
1338 Initializes object representing time zone described by its offset from UTC.
1339
1340 SYNOPSIS
1341 Time_zone_offset()
1342 tz_offset_arg - offset from UTC in seconds.
1343 Positive for direction to east.
1344 */
Time_zone_offset(long tz_offset_arg)1345 Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1346 offset(tz_offset_arg)
1347 {
1348 uint hours= abs((int)(offset / SECS_PER_HOUR));
1349 uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1350 size_t length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1351 (offset>=0) ? "+" : "-", hours, minutes);
1352 name.set(name_buff, length, &my_charset_latin1);
1353 }
1354
1355
1356 /*
1357 Converts local time in time zone described as offset from UTC
1358 from MYSQL_TIME representation to its my_time_t representation.
1359
1360 SYNOPSIS
1361 TIME_to_gmt_sec()
1362 t - pointer to MYSQL_TIME structure with local time
1363 in broken-down representation.
1364 in_dst_time_gap - pointer to bool which should be set to true if
1365 datetime value passed doesn't really exist
1366 (i.e. falls into spring time-gap) and is not
1367 touched otherwise.
1368 It is not really used in this class.
1369
1370 RETURN VALUE
1371 Corresponding my_time_t value or 0 in case of error
1372 */
1373 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1374 Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1375 {
1376 my_time_t local_t;
1377 int shift= 0;
1378
1379 /*
1380 Check timestamp range.we have to do this as calling function relies on
1381 us to make all validation checks here.
1382 */
1383 if (!validate_timestamp_range(t))
1384 return 0;
1385
1386 /*
1387 Do a temporary shift of the boundary dates to avoid
1388 overflow of my_time_t if the time value is near it's
1389 maximum range
1390 */
1391 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1392 shift= 2;
1393
1394 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1395 t->hour, t->minute, t->second) -
1396 offset;
1397
1398 if (shift)
1399 {
1400 /* Add back the shifted time */
1401 local_t+= shift * SECS_PER_DAY;
1402 }
1403
1404 if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1405 return local_t;
1406
1407 /* range error*/
1408 return 0;
1409 }
1410
1411
1412 /*
1413 Converts time from UTC seconds since Epoch (my_time_t) representation
1414 to local time zone described as offset from UTC and in broken-down
1415 representation.
1416
1417 SYNOPSIS
1418 gmt_sec_to_TIME()
1419 tmp - pointer to MYSQL_TIME structure to fill-in
1420 t - my_time_t value to be converted
1421 */
1422 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1423 Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1424 {
1425 sec_to_TIME(tmp, t, offset);
1426 }
1427
1428
1429 /*
1430 Get name of time zone
1431
1432 SYNOPSIS
1433 get_name()
1434
1435 RETURN VALUE
1436 Name of time zone as pointer to String object
1437 */
1438 const String *
get_name() const1439 Time_zone_offset::get_name() const
1440 {
1441 return &name;
1442 }
1443
1444
1445 static Time_zone_utc tz_UTC;
1446 static Time_zone_system tz_SYSTEM;
1447 static Time_zone_offset tz_OFFSET0(0);
1448
1449 Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1450 Time_zone *my_tz_UTC= &tz_UTC;
1451 Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1452
1453 static HASH tz_names;
1454 static HASH offset_tzs;
1455 static MEM_ROOT tz_storage;
1456
1457 /*
1458 These mutex protects offset_tzs and tz_storage.
1459 These protection needed only when we are trying to set
1460 time zone which is specified as offset, and searching for existing
1461 time zone in offset_tzs or creating if it didn't existed before in
1462 tz_storage. So contention is low.
1463 */
1464 static mysql_mutex_t tz_LOCK;
1465 static bool tz_inited= 0;
1466
1467 /*
1468 This two static variables are inteded for holding info about leap seconds
1469 shared by all time zones.
1470 */
1471 static uint tz_leapcnt= 0;
1472 static LS_INFO *tz_lsis= 0;
1473
1474 /*
1475 Shows whenever we have found time zone tables during start-up.
1476 Used for avoiding of putting those tables to global table list
1477 for queries that use time zone info.
1478 */
1479 static bool time_zone_tables_exist= 1;
1480
1481
1482 /*
1483 Names of tables (with their lengths) that are needed
1484 for dynamical loading of time zone descriptions.
1485 */
1486
1487 static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1488 {
1489 { C_STRING_WITH_LEN("time_zone_name")},
1490 { C_STRING_WITH_LEN("time_zone")},
1491 { C_STRING_WITH_LEN("time_zone_transition_type")},
1492 { C_STRING_WITH_LEN("time_zone_transition")}
1493 };
1494
1495 /* Name of database to which those tables belong. */
1496
1497 static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1498
1499
1500 class Tz_names_entry: public Sql_alloc
1501 {
1502 public:
1503 String name;
1504 Time_zone *tz;
1505 };
1506
1507
1508 /*
1509 We are going to call both of these functions from C code so
1510 they should obey C calling conventions.
1511 */
1512
1513 extern "C" uchar *
my_tz_names_get_key(Tz_names_entry * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1514 my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1515 my_bool not_used MY_ATTRIBUTE((unused)))
1516 {
1517 *length= entry->name.length();
1518 return (uchar*) entry->name.ptr();
1519 }
1520
1521 extern "C" uchar *
my_offset_tzs_get_key(Time_zone_offset * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1522 my_offset_tzs_get_key(Time_zone_offset *entry,
1523 size_t *length,
1524 my_bool not_used MY_ATTRIBUTE((unused)))
1525 {
1526 *length= sizeof(long);
1527 return (uchar*) &entry->offset;
1528 }
1529
1530
1531 /*
1532 Prepare table list with time zone related tables from preallocated array.
1533
1534 SYNOPSIS
1535 tz_init_table_list()
1536 tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1537 TABLE_LIST objects
1538
1539 DESCRIPTION
1540 This function prepares list of TABLE_LIST objects which can be used
1541 for opening of time zone tables from preallocated array.
1542 */
1543
1544 static void
tz_init_table_list(TABLE_LIST * tz_tabs)1545 tz_init_table_list(TABLE_LIST *tz_tabs)
1546 {
1547 for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1548 {
1549 new (&tz_tabs[i]) TABLE_LIST;
1550 tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
1551 tz_tabs[i].table_name_length= tz_tables_names[i].length;
1552 tz_tabs[i].db= tz_tables_db_name.str;
1553 tz_tabs[i].db_length= tz_tables_db_name.length;
1554 tz_tabs[i].lock_type= TL_READ;
1555
1556 if (i != MY_TZ_TABLES_COUNT - 1)
1557 tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1558 if (i != 0)
1559 tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1560 }
1561 }
1562
1563 static PSI_memory_key key_memory_tz_storage;
1564
1565 #ifdef HAVE_PSI_INTERFACE
1566 static PSI_mutex_key key_tz_LOCK;
1567
1568 static PSI_mutex_info all_tz_mutexes[]=
1569 {
1570 { & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
1571 };
1572
1573 static PSI_memory_info all_tz_memory[]=
1574 {
1575 { &key_memory_tz_storage, "tz_storage", PSI_FLAG_GLOBAL}
1576 };
1577
1578
init_tz_psi_keys(void)1579 static void init_tz_psi_keys(void)
1580 {
1581 const char* category= "sql";
1582 int count;
1583
1584 count= array_elements(all_tz_mutexes);
1585 mysql_mutex_register(category, all_tz_mutexes, count);
1586
1587 count= array_elements(all_tz_memory);
1588 mysql_memory_register(category, all_tz_memory, count);
1589 }
1590 #endif /* HAVE_PSI_INTERFACE */
1591
1592
1593 /*
1594 Initialize time zone support infrastructure.
1595
1596 SYNOPSIS
1597 my_tz_init()
1598 thd - current thread object
1599 default_tzname - default time zone or 0 if none.
1600 bootstrap - indicates whenever we are in bootstrap mode
1601
1602 DESCRIPTION
1603 This function will init memory structures needed for time zone support,
1604 it will register mandatory SYSTEM time zone in them. It will try to open
1605 mysql.time_zone* tables and load information about default time zone and
1606 information which further will be shared among all time zones loaded.
1607 If system tables with time zone descriptions don't exist it won't fail
1608 (unless default_tzname is time zone from tables). If bootstrap parameter
1609 is true then this routine assumes that we are in bootstrap mode and won't
1610 load time zone descriptions unless someone specifies default time zone
1611 which is supposedly stored in those tables.
1612 It'll also set default time zone if it is specified.
1613
1614 RETURN VALUES
1615 0 - ok
1616 1 - Error
1617 */
1618 my_bool
my_tz_init(THD * org_thd,const char * default_tzname,my_bool bootstrap)1619 my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1620 {
1621 THD *thd;
1622 TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1623 TABLE *table;
1624 Tz_names_entry *tmp_tzname;
1625 my_bool return_val= 1;
1626 LEX_CSTRING db= { C_STRING_WITH_LEN("mysql") };
1627 int res;
1628 DBUG_ENTER("my_tz_init");
1629
1630 #ifdef HAVE_PSI_INTERFACE
1631 init_tz_psi_keys();
1632 #endif
1633
1634 /*
1635 To be able to run this from boot, we allocate a temporary THD
1636 */
1637 if (!(thd= new THD))
1638 DBUG_RETURN(1);
1639 thd->thread_stack= (char*) &thd;
1640 thd->store_globals();
1641
1642 /* Init all memory structures that require explicit destruction */
1643 if (my_hash_init(&tz_names, &my_charset_latin1, 20,
1644 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0,
1645 key_memory_tz_storage))
1646 {
1647 sql_print_error("Fatal error: OOM while initializing time zones");
1648 goto end;
1649 }
1650 if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1651 (my_hash_get_key)my_offset_tzs_get_key, 0, 0,
1652 key_memory_tz_storage))
1653 {
1654 sql_print_error("Fatal error: OOM while initializing time zones");
1655 my_hash_free(&tz_names);
1656 goto end;
1657 }
1658 init_sql_alloc(key_memory_tz_storage, &tz_storage, 32 * 1024, 0);
1659 mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
1660 tz_inited= 1;
1661
1662 /* Add 'SYSTEM' time zone to tz_names hash */
1663 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1664 {
1665 sql_print_error("Fatal error: OOM while initializing time zones");
1666 goto end_with_cleanup;
1667 }
1668 tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1669 tmp_tzname->tz= my_tz_SYSTEM;
1670 if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1671 {
1672 sql_print_error("Fatal error: OOM while initializing time zones");
1673 goto end_with_cleanup;
1674 }
1675
1676 if (bootstrap)
1677 {
1678 /* If we are in bootstrap mode we should not load time zone tables */
1679 return_val= time_zone_tables_exist= 0;
1680 goto end_with_setting_default_tz;
1681 }
1682
1683 /*
1684 After this point all memory structures are inited and we even can live
1685 without time zone description tables. Now try to load information about
1686 leap seconds shared by all time zones.
1687 */
1688 thd->set_db(db);
1689 tz_tables[0].alias= tz_tables[0].table_name=
1690 (char*)"time_zone_leap_second";
1691 tz_tables[0].table_name_length= 21;
1692 tz_tables[0].db= db.str;
1693 tz_tables[0].db_length= sizeof(db)-1;
1694 tz_tables[0].lock_type= TL_READ;
1695
1696 tz_init_table_list(tz_tables+1);
1697 tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1698 tz_tables[1].prev_global= &tz_tables[0].next_global;
1699 init_mdl_requests(tz_tables);
1700
1701
1702 /*
1703 We need to open only mysql.time_zone_leap_second, but we try to
1704 open all time zone tables to see if they exist.
1705 */
1706 if (open_trans_system_tables_for_read(thd, tz_tables))
1707 {
1708 sql_print_warning("Can't open and lock time zone table: %s "
1709 "trying to live without them",
1710 thd->get_stmt_da()->message_text());
1711 /* We will try emulate that everything is ok */
1712 return_val= time_zone_tables_exist= 0;
1713 goto end_with_setting_default_tz;
1714 }
1715
1716 for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global)
1717 {
1718 /* Force close at the end of the function to free memory. */
1719 tl->table->m_needs_reopen= TRUE;
1720 }
1721
1722 /*
1723 Now we are going to load leap seconds descriptions that are shared
1724 between all time zones that use them. We are using index for getting
1725 records in proper order. Since we share the same MEM_ROOT between
1726 all time zones we just allocate enough memory for it first.
1727 */
1728 if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1729 sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1730 {
1731 sql_print_error("Fatal error: Out of memory while loading "
1732 "mysql.time_zone_leap_second table");
1733 goto end_with_close;
1734 }
1735
1736 table= tz_tables[0].table;
1737
1738 if (table->file->ha_index_init(0, 1))
1739 goto end_with_close;
1740 table->use_all_columns();
1741
1742 tz_leapcnt= 0;
1743
1744 res= table->file->ha_index_first(table->record[0]);
1745
1746 while (!res)
1747 {
1748 if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1749 {
1750 sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1751 " table: too much leaps");
1752 table->file->ha_index_end();
1753 goto end_with_close;
1754 }
1755
1756 tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1757 tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1758
1759 tz_leapcnt++;
1760
1761 DBUG_PRINT("info",
1762 ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1763 tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1764 tz_lsis[tz_leapcnt-1].ls_corr));
1765
1766 res= table->file->ha_index_next(table->record[0]);
1767 }
1768
1769 (void)table->file->ha_index_end();
1770
1771 if (res != HA_ERR_END_OF_FILE)
1772 {
1773 sql_print_error("Fatal error: Error while loading "
1774 "mysql.time_zone_leap_second table");
1775 goto end_with_close;
1776 }
1777
1778 /*
1779 Loading of info about leap seconds succeeded
1780 */
1781
1782 return_val= 0;
1783
1784 end_with_close:
1785 close_trans_system_tables(thd);
1786
1787 end_with_setting_default_tz:
1788 /* If we have default time zone try to load it */
1789 if (!return_val && default_tzname)
1790 {
1791 String tmp_tzname2(default_tzname, &my_charset_latin1);
1792 /*
1793 Time zone tables may be open here, and my_tz_find() may open
1794 most of them once more, but this is OK for system tables open
1795 for READ.
1796 */
1797 if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1798 {
1799 sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1800 default_tzname);
1801 return_val= 1;
1802 }
1803 }
1804
1805 end_with_cleanup:
1806 /* if there were error free time zone describing structs */
1807 if (return_val)
1808 my_tz_free();
1809
1810 end:
1811 delete thd;
1812 if (org_thd)
1813 org_thd->store_globals(); /* purecov: inspected */
1814
1815 default_tz= default_tz_name ? global_system_variables.time_zone
1816 : my_tz_SYSTEM;
1817
1818 DBUG_RETURN(return_val);
1819 }
1820
1821
1822 /*
1823 Free resources used by time zone support infrastructure.
1824
1825 SYNOPSIS
1826 my_tz_free()
1827 */
1828
my_tz_free()1829 void my_tz_free()
1830 {
1831 if (tz_inited)
1832 {
1833 tz_inited= 0;
1834 mysql_mutex_destroy(&tz_LOCK);
1835 my_hash_free(&offset_tzs);
1836 my_hash_free(&tz_names);
1837 free_root(&tz_storage, MYF(0));
1838 }
1839 }
1840
1841
1842 /*
1843 Load time zone description from system tables.
1844
1845 SYNOPSIS
1846 tz_load_from_open_tables()
1847 tz_name - name of time zone that should be loaded.
1848 tz_tables - list of tables from which time zone description
1849 should be loaded
1850
1851 DESCRIPTION
1852 This function will try to load information about time zone specified
1853 from the list of the already opened and locked tables (first table in
1854 tz_tables should be time_zone_name, next time_zone, then
1855 time_zone_transition_type and time_zone_transition should be last).
1856 It will also update information in hash used for time zones lookup.
1857
1858 RETURN VALUES
1859 Returns pointer to newly created Time_zone object or 0 in case of error.
1860
1861 */
1862
1863 static Time_zone*
tz_load_from_open_tables(const String * tz_name,TABLE_LIST * tz_tables)1864 tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1865 {
1866 TABLE *table= 0;
1867 TIME_ZONE_INFO *tz_info= NULL;
1868 Tz_names_entry *tmp_tzname;
1869 Time_zone *return_val= 0;
1870 int res;
1871 uint tzid, ttid;
1872 my_time_t ttime;
1873 char buff[MAX_FIELD_WIDTH];
1874 String abbr(buff, sizeof(buff), &my_charset_latin1);
1875 char *alloc_buff= NULL;
1876 char *tz_name_buff= NULL;
1877 /*
1878 Temporary arrays that are used for loading of data for filling
1879 TIME_ZONE_INFO structure
1880 */
1881 my_time_t ats[TZ_MAX_TIMES];
1882 uchar types[TZ_MAX_TIMES];
1883 TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1884 #ifdef ABBR_ARE_USED
1885 char chars[MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1886 #endif
1887 /*
1888 Used as a temporary tz_info until we decide that we actually want to
1889 allocate and keep the tz info and tz name in tz_storage.
1890 */
1891 TIME_ZONE_INFO tmp_tz_info;
1892 memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1893
1894 DBUG_ENTER("tz_load_from_open_tables");
1895
1896 /*
1897 Let us find out time zone id by its name (there is only one index
1898 and it is specifically for this purpose).
1899 */
1900 table= tz_tables->table;
1901 tz_tables= tz_tables->next_local;
1902 table->field[0]->store(tz_name->ptr(), tz_name->length(),
1903 &my_charset_latin1);
1904
1905 if (table->file->ha_index_init(0, 1))
1906 goto end;
1907
1908 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1909 HA_WHOLE_KEY, HA_READ_KEY_EXACT);
1910 if (res)
1911 {
1912 /*
1913 Time_zone-related tables are used in autocommit/read-committed/
1914 non-locking mode, therefore we don't expect the error codes
1915 HA_ERR_LOCK_WAIT_TIMEOUT/HA_ERR_LOCK_DEADLOCK on return from read
1916 from storage engine.
1917 */
1918 DBUG_ASSERT(res != HA_ERR_LOCK_WAIT_TIMEOUT &&
1919 res != HA_ERR_LOCK_DEADLOCK);
1920 #ifdef EXTRA_DEBUG
1921 /*
1922 Most probably user has mistyped time zone name, so no need to bark here
1923 unless we need it for debugging.
1924 */
1925 sql_print_error("Can't find description of time zone '%.*s'",
1926 tz_name->length(), tz_name->ptr());
1927 #endif
1928 goto end;
1929 }
1930
1931 tzid= (uint)table->field[1]->val_int();
1932
1933 (void)table->file->ha_index_end();
1934
1935 /*
1936 Now we need to lookup record in mysql.time_zone table in order to
1937 understand whenever this timezone uses leap seconds (again we are
1938 using the only index in this table).
1939 */
1940 table= tz_tables->table;
1941 tz_tables= tz_tables->next_local;
1942 table->field[0]->store((longlong) tzid, TRUE);
1943 if (table->file->ha_index_init(0, 1))
1944 goto end;
1945
1946 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1947 HA_WHOLE_KEY, HA_READ_KEY_EXACT);
1948 if (res)
1949 {
1950 DBUG_ASSERT(res != HA_ERR_LOCK_WAIT_TIMEOUT &&
1951 res != HA_ERR_LOCK_DEADLOCK);
1952
1953 sql_print_error("Can't find description of time zone '%u'", tzid);
1954 goto end;
1955 }
1956
1957 /* If Uses_leap_seconds == 'Y' */
1958 if (table->field[1]->val_int() == 1)
1959 {
1960 tmp_tz_info.leapcnt= tz_leapcnt;
1961 tmp_tz_info.lsis= tz_lsis;
1962 }
1963
1964 (void)table->file->ha_index_end();
1965
1966 /*
1967 Now we will iterate through records for out time zone in
1968 mysql.time_zone_transition_type table. Because we want records
1969 only for our time zone guess what are we doing?
1970 Right - using special index.
1971 */
1972 table= tz_tables->table;
1973 tz_tables= tz_tables->next_local;
1974 table->field[0]->store((longlong) tzid, TRUE);
1975 if (table->file->ha_index_init(0, 1))
1976 goto end;
1977
1978 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1979 (key_part_map)1, HA_READ_KEY_EXACT);
1980 while (!res)
1981 {
1982 ttid= (uint)table->field[1]->val_int();
1983
1984 if (ttid >= TZ_MAX_TYPES)
1985 {
1986 sql_print_error("Error while loading time zone description from "
1987 "mysql.time_zone_transition_type table: too big "
1988 "transition type id");
1989 goto end;
1990 }
1991
1992 ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1993 ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
1994
1995 #ifdef ABBR_ARE_USED
1996 // FIXME should we do something with duplicates here ?
1997 table->field[4]->val_str(&abbr, &abbr);
1998 if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
1999 {
2000 sql_print_error("Error while loading time zone description from "
2001 "mysql.time_zone_transition_type table: not enough "
2002 "room for abbreviations");
2003 goto end;
2004 }
2005 ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
2006 memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
2007 tmp_tz_info.charcnt+= abbr.length();
2008 chars[tmp_tz_info.charcnt]= 0;
2009 tmp_tz_info.charcnt++;
2010
2011 DBUG_PRINT("info",
2012 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
2013 "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
2014 chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
2015 #else
2016 DBUG_PRINT("info",
2017 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
2018 "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
2019 #endif
2020
2021 /* ttid is increasing because we are reading using index */
2022 DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
2023
2024 tmp_tz_info.typecnt= ttid + 1;
2025
2026 res= table->file->ha_index_next_same(table->record[0],
2027 table->field[0]->ptr, 4);
2028 }
2029
2030 if (res != HA_ERR_END_OF_FILE)
2031 {
2032 DBUG_ASSERT(res != HA_ERR_LOCK_WAIT_TIMEOUT &&
2033 res != HA_ERR_LOCK_DEADLOCK);
2034 sql_print_error("Error while loading time zone description from "
2035 "mysql.time_zone_transition_type table");
2036 goto end;
2037 }
2038
2039 (void)table->file->ha_index_end();
2040
2041
2042 /*
2043 At last we are doing the same thing for records in
2044 mysql.time_zone_transition table. Here we additionally need records
2045 in ascending order by index scan also satisfies us.
2046 */
2047 table= tz_tables->table;
2048 table->field[0]->store((longlong) tzid, TRUE);
2049 if (table->file->ha_index_init(0, 1))
2050 goto end;
2051
2052 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
2053 (key_part_map)1, HA_READ_KEY_EXACT);
2054 while (!res)
2055 {
2056 ttime= (my_time_t)table->field[1]->val_int();
2057 ttid= (uint)table->field[2]->val_int();
2058
2059 if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
2060 {
2061 sql_print_error("Error while loading time zone description from "
2062 "mysql.time_zone_transition table: "
2063 "too much transitions");
2064 goto end;
2065 }
2066 if (ttid + 1 > tmp_tz_info.typecnt)
2067 {
2068 sql_print_error("Error while loading time zone description from "
2069 "mysql.time_zone_transition table: "
2070 "bad transition type id");
2071 goto end;
2072 }
2073
2074 ats[tmp_tz_info.timecnt]= ttime;
2075 types[tmp_tz_info.timecnt]= ttid;
2076 tmp_tz_info.timecnt++;
2077
2078 DBUG_PRINT("info",
2079 ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
2080 tzid, (ulong) ttime, ttid));
2081
2082 res= table->file->ha_index_next_same(table->record[0],
2083 table->field[0]->ptr, 4);
2084 }
2085
2086 /*
2087 We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
2088 for example UTC have no transitons.
2089 */
2090 if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
2091 {
2092 DBUG_ASSERT(res != HA_ERR_LOCK_WAIT_TIMEOUT &&
2093 res != HA_ERR_LOCK_DEADLOCK);
2094 sql_print_error("Error while loading time zone description from "
2095 "mysql.time_zone_transition table");
2096 goto end;
2097 }
2098
2099 (void)table->file->ha_index_end();
2100 table= 0;
2101
2102 /*
2103 Let us check how correct our time zone description is. We don't check for
2104 tz->timecnt < 1 since it is ok for GMT.
2105 */
2106 if (tmp_tz_info.typecnt < 1)
2107 {
2108 sql_print_error("loading time zone without transition types");
2109 goto end;
2110 }
2111
2112 /* Allocate memory for the timezone info and timezone name in tz_storage. */
2113 if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
2114 tz_name->length() + 1)))
2115 {
2116 sql_print_error("Out of memory while loading time zone description");
2117 DBUG_RETURN(0);
2118 }
2119
2120 /* Move the temporary tz_info into the allocated area */
2121 tz_info= (TIME_ZONE_INFO *)alloc_buff;
2122 memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
2123 tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
2124 /*
2125 By writing zero to the end we guarantee that we can call ptr()
2126 instead of c_ptr() for time zone name.
2127 */
2128 strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
2129
2130 /*
2131 Now we will allocate memory and init TIME_ZONE_INFO structure.
2132 */
2133 if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2134 ALIGN_SIZE(sizeof(my_time_t) *
2135 tz_info->timecnt) +
2136 ALIGN_SIZE(tz_info->timecnt) +
2137 #ifdef ABBR_ARE_USED
2138 ALIGN_SIZE(tz_info->charcnt) +
2139 #endif
2140 sizeof(TRAN_TYPE_INFO) *
2141 tz_info->typecnt)))
2142 {
2143 sql_print_error("Out of memory while loading time zone description");
2144 goto end;
2145 }
2146
2147 tz_info->ats= (my_time_t *) alloc_buff;
2148 memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2149 alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2150 tz_info->types= (uchar *)alloc_buff;
2151 memcpy(tz_info->types, types, tz_info->timecnt);
2152 alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2153 #ifdef ABBR_ARE_USED
2154 tz_info->chars= alloc_buff;
2155 memcpy(tz_info->chars, chars, tz_info->charcnt);
2156 alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2157 #endif
2158 tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2159 memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2160
2161 /* Build reversed map. */
2162 if (prepare_tz_info(tz_info, &tz_storage))
2163 {
2164 sql_print_error("Unable to build mktime map for time zone");
2165 goto end;
2166 }
2167
2168
2169 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2170 !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2171 &(tmp_tzname->name))) ||
2172 (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2173 &my_charset_latin1),
2174 my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2175 {
2176 sql_print_error("Out of memory while loading time zone");
2177 goto end;
2178 }
2179
2180 /*
2181 Loading of time zone succeeded
2182 */
2183 return_val= tmp_tzname->tz;
2184
2185 end:
2186
2187 if (table && table->file->inited)
2188 (void) table->file->ha_index_end();
2189
2190 DBUG_RETURN(return_val);
2191 }
2192
2193
2194 /*
2195 Parse string that specifies time zone as offset from UTC.
2196
2197 SYNOPSIS
2198 str_to_offset()
2199 str - pointer to string which contains offset
2200 length - length of string
2201 offset - out parameter for storing found offset in seconds.
2202
2203 DESCRIPTION
2204 This function parses string which contains time zone offset
2205 in form similar to '+10:00' and converts found value to
2206 seconds from UTC form (east is positive).
2207
2208 RETURN VALUE
2209 0 - Ok
2210 1 - String doesn't contain valid time zone offset
2211 */
2212 my_bool
str_to_offset(const char * str,size_t length,long * offset)2213 str_to_offset(const char *str, size_t length, long *offset)
2214 {
2215 const char *end= str + length;
2216 my_bool negative;
2217 ulong number_tmp;
2218 long offset_tmp;
2219
2220 if (length < 4)
2221 return 1;
2222
2223 if (*str == '+')
2224 negative= 0;
2225 else if (*str == '-')
2226 negative= 1;
2227 else
2228 return 1;
2229 str++;
2230
2231 number_tmp= 0;
2232
2233 while (str < end && my_isdigit(&my_charset_latin1, *str))
2234 {
2235 number_tmp= number_tmp*10 + *str - '0';
2236 str++;
2237 }
2238
2239 if (str + 1 >= end || *str != ':')
2240 return 1;
2241 str++;
2242
2243 offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2244
2245 while (str < end && my_isdigit(&my_charset_latin1, *str))
2246 {
2247 number_tmp= number_tmp * 10 + *str - '0';
2248 str++;
2249 }
2250
2251 if (str != end)
2252 return 1;
2253
2254 offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2255
2256 if (negative)
2257 offset_tmp= -offset_tmp;
2258
2259 /*
2260 Check if offset is in range prescribed by standard
2261 (from -12:59 to 13:00).
2262 */
2263
2264 if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2265 offset_tmp > 13 * SECS_PER_HOUR)
2266 return 1;
2267
2268 *offset= offset_tmp;
2269
2270 return 0;
2271 }
2272
2273
2274 /*
2275 Get Time_zone object for specified time zone.
2276
2277 SYNOPSIS
2278 my_tz_find()
2279 thd - pointer to thread THD structure
2280 name - time zone specification
2281
2282 DESCRIPTION
2283 This function checks if name is one of time zones described in db,
2284 predefined SYSTEM time zone or valid time zone specification as
2285 offset from UTC (In last case it will create proper Time_zone_offset
2286 object if there were not any.). If name is ok it returns corresponding
2287 Time_zone object.
2288
2289 Clients of this function are not responsible for releasing resources
2290 occupied by returned Time_zone object so they can just forget pointers
2291 to Time_zone object if they are not needed longer.
2292
2293 Other important property of this function: if some Time_zone found once
2294 it will be for sure found later, so this function can also be used for
2295 checking if proper Time_zone object exists (and if there will be error
2296 it will be reported during first call).
2297
2298 If name pointer is 0 then this function returns 0 (this allows to pass 0
2299 values as parameter without additional external check and this property
2300 is used by @@time_zone variable handling code).
2301
2302 It will perform lookup in system tables (mysql.time_zone*),
2303 opening and locking them, and closing afterwards. It won't perform
2304 such lookup if no time zone describing tables were found during
2305 server start up.
2306
2307 RETURN VALUE
2308 Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2309 specification or other error.
2310
2311 */
2312 Time_zone *
my_tz_find(THD * thd,const String * name)2313 my_tz_find(THD *thd, const String *name)
2314 {
2315 Tz_names_entry *tmp_tzname;
2316 Time_zone *result_tz= 0;
2317 long offset;
2318 DBUG_ENTER("my_tz_find");
2319 DBUG_PRINT("enter", ("time zone name='%s'",
2320 name ? ((String *)name)->c_ptr_safe() : "NULL"));
2321
2322 if (!name || name->is_empty())
2323 DBUG_RETURN(0);
2324
2325 mysql_mutex_lock(&tz_LOCK);
2326
2327 if (!str_to_offset(name->ptr(), name->length(), &offset))
2328 {
2329
2330 if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
2331 (const uchar *)&offset,
2332 sizeof(long))))
2333 {
2334 DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2335
2336 if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2337 my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2338 {
2339 result_tz= 0;
2340 sql_print_error("Fatal error: Out of memory "
2341 "while setting new time zone");
2342 }
2343 }
2344 }
2345 else
2346 {
2347 result_tz= 0;
2348 if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names,
2349 (const uchar *)
2350 name->ptr(),
2351 name->length())))
2352 result_tz= tmp_tzname->tz;
2353 else if (time_zone_tables_exist)
2354 {
2355 TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2356
2357 tz_init_table_list(tz_tables);
2358 init_mdl_requests(tz_tables);
2359 DEBUG_SYNC(thd, "my_tz_find");
2360
2361 if (!open_trans_system_tables_for_read(thd, tz_tables))
2362 {
2363 result_tz= tz_load_from_open_tables(name, tz_tables);
2364 close_trans_system_tables(thd);
2365 }
2366 }
2367 }
2368
2369 mysql_mutex_unlock(&tz_LOCK);
2370
2371 DBUG_RETURN(result_tz);
2372 }
2373
2374
2375 /**
2376 Convert leap seconds into non-leap
2377
2378 This function will convert the leap seconds added by the OS to
2379 non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
2380 This check is not checking for years on purpose : although it's not a
2381 complete check this way it doesn't require looking (and having installed)
2382 the leap seconds table.
2383
2384 @param[in,out] t broken down time structure as filled in by the OS
2385 */
2386
adjust_leap_second(MYSQL_TIME * t)2387 void Time_zone::adjust_leap_second(MYSQL_TIME *t)
2388 {
2389 if (t->second == 60 || t->second == 61)
2390 t->second= 59;
2391 }
2392
2393 #endif /* !defined(TZINFO2SQL) */
2394
2395
2396 #ifdef TZINFO2SQL
2397 /*
2398 This code belongs to mysql_tzinfo_to_sql converter command line utility.
2399 This utility should be used by db admin for populating mysql.time_zone
2400 tables.
2401 */
2402
2403
2404 /*
2405 Print info about time zone described by TIME_ZONE_INFO struct as
2406 SQL statements populating mysql.time_zone* tables.
2407
2408 SYNOPSIS
2409 print_tz_as_sql()
2410 tz_name - name of time zone
2411 sp - structure describing time zone
2412 */
2413 void
print_tz_as_sql(const char * tz_name,const TIME_ZONE_INFO * sp)2414 print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2415 {
2416 uint i;
2417
2418 /* Here we assume that all time zones have same leap correction tables */
2419 printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2420 sp->leapcnt ? "Y" : "N");
2421 printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2422 printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2423 ('%s', @time_zone_id);\n", tz_name);
2424
2425 if (sp->timecnt)
2426 {
2427 printf("INSERT INTO time_zone_transition \
2428 (Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2429 for (i= 0; i < sp->timecnt; i++)
2430 printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2431 (uint)sp->types[i]);
2432 printf(";\n");
2433 }
2434
2435 printf("INSERT INTO time_zone_transition_type \
2436 (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2437
2438 for (i= 0; i < sp->typecnt; i++)
2439 /*
2440 Since the column time_zone_transition_type.Abbreviation
2441 is declared as CHAR(8) we have to limit the number of characters
2442 for the column abbreviation in the next output by 8 chars.
2443 */
2444 printf("%s(@time_zone_id, %u, %ld, %d, '%.8s')\n", (i == 0 ? " " : ","), i,
2445 sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2446 sp->chars + sp->ttis[i].tt_abbrind);
2447 printf(";\n");
2448 }
2449
2450
2451 /*
2452 Print info about leap seconds in time zone as SQL statements
2453 populating mysql.time_zone_leap_second table.
2454
2455 SYNOPSIS
2456 print_tz_leaps_as_sql()
2457 sp - structure describing time zone
2458 */
2459 void
print_tz_leaps_as_sql(const TIME_ZONE_INFO * sp)2460 print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2461 {
2462 uint i;
2463
2464 /*
2465 We are assuming that there are only one list of leap seconds
2466 For all timezones.
2467 */
2468 printf("TRUNCATE TABLE time_zone_leap_second;\n");
2469 printf("START TRANSACTION;\n");
2470 if (sp->leapcnt)
2471 {
2472 printf("INSERT INTO time_zone_leap_second \
2473 (Transition_time, Correction) VALUES\n");
2474 for (i= 0; i < sp->leapcnt; i++)
2475 printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2476 sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2477 printf(";\n");
2478 }
2479 printf("COMMIT;\n");
2480 }
2481
2482
2483 /*
2484 Some variables used as temporary or as parameters
2485 in recursive scan_tz_dir() code.
2486 */
2487 TIME_ZONE_INFO tz_info;
2488 MEM_ROOT tz_storage;
2489 char fullname[FN_REFLEN + 1];
2490 char *root_name_end;
2491
2492
2493 /*
2494 Recursively scan zoneinfo directory and print all found time zone
2495 descriptions as SQL.
2496
2497 SYNOPSIS
2498 scan_tz_dir()
2499 name_end - pointer to end of path to directory to be searched.
2500
2501 DESCRIPTION
2502 This auxiliary recursive function also uses several global
2503 variables as in parameters and for storing temporary values.
2504
2505 fullname - path to directory that should be scanned.
2506 root_name_end - pointer to place in fullname where part with
2507 path to initial directory ends.
2508 current_tz_id - last used time zone id
2509
2510 RETURN VALUE
2511 0 - Ok, 1 - Fatal error
2512
2513 */
2514 my_bool
scan_tz_dir(char * name_end)2515 scan_tz_dir(char * name_end)
2516 {
2517 MY_DIR *cur_dir;
2518 char *name_end_tmp;
2519 uint i;
2520
2521 if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
2522 return 1;
2523
2524 name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2525
2526 for (i= 0; i < cur_dir->number_off_files; i++)
2527 {
2528 if (cur_dir->dir_entry[i].name[0] != '.')
2529 {
2530 name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2531 FN_REFLEN - (name_end - fullname));
2532
2533 if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2534 {
2535 if (scan_tz_dir(name_end_tmp))
2536 {
2537 my_dirend(cur_dir);
2538 return 1;
2539 }
2540 }
2541 else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2542 {
2543 init_alloc_root(PSI_NOT_INSTRUMENTED, &tz_storage, 32768, 0);
2544 if (!tz_load(fullname, &tz_info, &tz_storage))
2545 print_tz_as_sql(root_name_end + 1, &tz_info);
2546 else
2547 fprintf(stderr,
2548 "Warning: Unable to load '%s' as time zone. Skipping it.\n",
2549 fullname);
2550 free_root(&tz_storage, MYF(0));
2551 }
2552 else
2553 fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2554 fullname);
2555 }
2556 }
2557
2558 my_dirend(cur_dir);
2559
2560 return 0;
2561 }
2562
2563
2564 int
main(int argc,char ** argv)2565 main(int argc, char **argv)
2566 {
2567 MY_INIT(argv[0]);
2568
2569 if (argc != 2 && argc != 3)
2570 {
2571 fprintf(stderr, "Usage:\n");
2572 fprintf(stderr, " %s timezonedir\n", argv[0]);
2573 fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
2574 fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
2575 return 1;
2576 }
2577
2578 if (argc == 2)
2579 {
2580 root_name_end= strmake(fullname, argv[1], FN_REFLEN);
2581
2582 printf("TRUNCATE TABLE time_zone;\n");
2583 printf("TRUNCATE TABLE time_zone_name;\n");
2584 printf("TRUNCATE TABLE time_zone_transition;\n");
2585 printf("TRUNCATE TABLE time_zone_transition_type;\n");
2586
2587 printf("START TRANSACTION;\n");
2588 if (scan_tz_dir(root_name_end))
2589 {
2590 fprintf(stderr, "There were fatal errors during processing "
2591 "of zoneinfo directory\n");
2592 return 1;
2593 }
2594 printf("COMMIT;\n");
2595 }
2596 else
2597 {
2598 init_alloc_root(PSI_NOT_INSTRUMENTED, &tz_storage, 32768, 0);
2599
2600 if (strcmp(argv[1], "--leap") == 0)
2601 {
2602 if (tz_load(argv[2], &tz_info, &tz_storage))
2603 {
2604 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2605 return 1;
2606 }
2607 print_tz_leaps_as_sql(&tz_info);
2608 }
2609 else
2610 {
2611 if (tz_load(argv[1], &tz_info, &tz_storage))
2612 {
2613 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2614 return 1;
2615 }
2616 printf("START TRANSACTION;\n");
2617 print_tz_as_sql(argv[2], &tz_info);
2618 printf("COMMIT;\n");
2619 }
2620
2621 free_root(&tz_storage, MYF(0));
2622 }
2623
2624 return 0;
2625 }
2626
2627 #endif /* defined(TZINFO2SQL) */
2628