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, &not_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