diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c2091e1..d82228e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -1411,3 +1411,3 @@ MYSQL_ADD_EXECUTABLE(mysql_tzinfo_to_sql DEPENDENCIES GenError - LINK_LIBRARIES mysys extra::rapidjson + LINK_LIBRARIES mysys extra::rapidjson absl::time absl::time_zone ) diff --git a/sql/mysql_tzinfo_to_sql.cc b/sql/mysql_tzinfo_to_sql.cc index 1dab705..5bbe1ea 100644 --- a/sql/mysql_tzinfo_to_sql.cc +++ b/sql/mysql_tzinfo_to_sql.cc @@ -25,3 +25,10 @@ +#include +#include +#include +#include + +#include "absl/time/time.h" #include "my_alloc.h" // MEM_ROOT #include "my_io.h" // MY_FOPEN_BINARY +#include "my_sys.h" // my_realpath #include "mysql/psi/mysql_file.h" // MYSQL_FILE @@ -45,2 +52,106 @@ static const char *const MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE = + +namespace { + +constexpr my_time_t MAX_FUTURE_TRANSITION = + static_cast(std::numeric_limits::max()); + +struct FUTURE_TRANSITION { + my_time_t transition_time; + uchar transition_type; +}; + +static int find_transition_type( + const TIME_ZONE_INFO *sp, + const absl::TimeZone::CivilInfo &civil_info) { + const char *const abbreviation = + civil_info.zone_abbr == nullptr ? "" : civil_info.zone_abbr; + + for (unsigned i = 0; i < sp->typecnt; ++i) { + const TRAN_TYPE_INFO &type_info = sp->ttis[i]; + if (type_info.tt_gmtoff == civil_info.offset && + type_info.tt_isdst == static_cast(civil_info.is_dst) && + std::strcmp(sp->chars + type_info.tt_abbrind, abbreviation) == 0) { + return static_cast(i); + } + } + return -1; +} + +static bool append_future_transitions(const char *name, TIME_ZONE_INFO *sp, + MEM_ROOT *storage) { + if (sp->leapcnt != 0) return false; + + char absolute_name[FN_REFLEN + 1]; + if (my_realpath(absolute_name, name, MYF(0))) return true; + + absl::TimeZone time_zone; + if (!absl::LoadTimeZone(absolute_name, &time_zone)) return true; + + const my_time_t last_explicit_transition = + sp->timecnt == 0 + ? static_cast( + std::numeric_limits::min()) - + 1 + : sp->ats[sp->timecnt - 1]; + + std::vector future_transitions; + absl::Time cursor = absl::FromUnixSeconds(last_explicit_transition); + + while (true) { + absl::TimeZone::CivilTransition transition; + if (!time_zone.NextTransition(cursor, &transition)) break; + + const absl::Time transition_time = time_zone.At(transition.to).trans; + const my_time_t epoch = + static_cast(absl::ToUnixSeconds(transition_time)); + + if (epoch <= last_explicit_transition) { + cursor = transition_time + absl::Seconds(1); + continue; + } + if (epoch > MAX_FUTURE_TRANSITION) break; + + const int type_index = + find_transition_type(sp, time_zone.At(transition_time)); + if (type_index < 0) return true; + if (sp->timecnt + future_transitions.size() >= TZ_MAX_TIMES) return true; + + future_transitions.push_back({epoch, static_cast(type_index)}); + cursor = transition_time + absl::Seconds(1); + } + + if (future_transitions.empty()) return false; + + const unsigned new_timecnt = + sp->timecnt + static_cast(future_transitions.size()); + const size_t ats_size = ALIGN_SIZE(new_timecnt * sizeof(my_time_t)); + const size_t types_size = ALIGN_SIZE(new_timecnt); + + char *const transition_buffer = + static_cast(storage->Alloc(ats_size + types_size)); + if (transition_buffer == nullptr) return true; + + my_time_t *const new_ats = reinterpret_cast(transition_buffer); + uchar *const new_types = + reinterpret_cast(transition_buffer + ats_size); + + if (sp->timecnt != 0) { + std::memcpy(new_ats, sp->ats, sp->timecnt * sizeof(my_time_t)); + std::memcpy(new_types, sp->types, sp->timecnt); + } + + for (size_t i = 0; i < future_transitions.size(); ++i) { + new_ats[sp->timecnt + i] = future_transitions[i].transition_time; + new_types[sp->timecnt + i] = future_transitions[i].transition_type; + } + + sp->ats = new_ats; + sp->types = new_types; + sp->timecnt = new_timecnt; + return false; +} + +} // namespace + /* @@ -167,4 +278,4 @@ static const char *const MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE = /* - Since we don't support POSIX style TZ definitions in variables we - don't read further like glibc or elsie code. + The explicit TZif block is parsed above. Future transitions from the + complete TZif file are appended below. */ @@ -172,2 +283,4 @@ static const char *const MAGIC_STRING_FOR_INVALID_ZONEINFO_FILE = + if (append_future_transitions(name, sp, storage)) return true; + return prepare_tz_info(sp, storage); diff --git a/sql/time_zone_common.cc b/sql/time_zone_common.cc index ec836f3..7644af5 100644 --- a/sql/time_zone_common.cc +++ b/sql/time_zone_common.cc @@ -196,3 +196,3 @@ sp->revcnt++; - if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1) + if (sp->revcnt == TZ_MAX_REV_RANGES - 1) break; /* That was too much */ diff --git a/sql/tzfile.h b/sql/tzfile.h index ae23fb8..9b48d87 100644 --- a/sql/tzfile.h +++ b/sql/tzfile.h @@ -88,8 +88,6 @@ /* - The TZ_MAX_TIMES value below is enough to handle a bit more than a - year's worth of solar time (corrected daily to the nearest second) or - 138 years of Pacific Presidential Election time - (where there are three time zone transitions every fourth year). + Keep enough room for explicit 32-bit TZif transitions plus transitions + expanded from the TZif POSIX future rule through UINT32_MAX. */ -#define TZ_MAX_TIMES 370 +#define TZ_MAX_TIMES 512 #endif /* !defined TZ_MAX_TIMES */ @@ -114,3 +112,7 @@ #ifndef TZ_MAX_REV_RANGES -#define TZ_MAX_REV_RANGES (TZ_MAX_TIMES + TZ_MAX_LEAPS + 2) +/* + prepare_tz_info() can emit a spring-gap range and a normal range for one + discontinuity. Reserve two ranges per transition/leap, plus boundaries. +*/ +#define TZ_MAX_REV_RANGES (2 * (TZ_MAX_TIMES + TZ_MAX_LEAPS) + 2) #endif