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