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