aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-06-13 14:32:42 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-06-13 14:32:42 -0400
commitf807e3410fdfc29ced6590c7c2afa76637e001ad (patch)
tree965ed177cc17ccb95afd8b2afd0a79b1eb79fd21 /src
parent9d97c3408319b43718e4b85bc694697db1af32c6 (diff)
downloadpostgresql-f807e3410fdfc29ced6590c7c2afa76637e001ad.tar.gz
postgresql-f807e3410fdfc29ced6590c7c2afa76637e001ad.zip
Work around portability issue with newer versions of mktime().
Recent glibc versions have made mktime() fail if tm_isdst is inconsistent with the prevailing timezone; in particular it fails for tm_isdst = 1 when the zone is UTC. (This seems wildly inconsistent with the POSIX-mandated treatment of "incorrect" values for the other fields of struct tm, so if you ask me it's a bug, but I bet they'll say it's intentional.) This has been observed to cause cosmetic problems when pg_restore'ing an archive created in a different timezone. To fix, do mktime() using the field values from the archive, and if that fails try again with tm_isdst = -1. This will give a result that's off by the UTC-offset difference from the original zone, but that was true before, too. It's not terribly critical since we don't do anything with the result except possibly print it. (Someday we should flush this entire bit of logic and record a standard-format timestamp in the archive instead. That's not okay for a back-patched bug fix, though.) Also, guard our only other use of mktime() by having initdb's build_time_t() set tm_isdst = -1 not 0. This case could only have an issue in zones that are DST year-round; but I think some do exist, or could in future. Per report from Wells Oliver. Back-patch to all supported versions, since any of them might need to run with a newer glibc. Discussion: https://postgr.es/m/CAOC+FBWDhDHO7G-i1_n_hjRzCnUeFO+H-Czi1y10mFhRWpBrew@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/bin/initdb/findtimezone.c1
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.c31
2 files changed, 27 insertions, 5 deletions
diff --git a/src/bin/initdb/findtimezone.c b/src/bin/initdb/findtimezone.c
index 8fe1e910f96..3c2b8d4e298 100644
--- a/src/bin/initdb/findtimezone.c
+++ b/src/bin/initdb/findtimezone.c
@@ -195,6 +195,7 @@ build_time_t(int year, int month, int day)
tm.tm_mday = day;
tm.tm_mon = month - 1;
tm.tm_year = year - 1900;
+ tm.tm_isdst = -1;
return mktime(&tm);
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 6b046e7734d..24cc0962558 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3703,7 +3703,6 @@ ReadHead(ArchiveHandle *AH)
vmin,
vrev;
int fmt;
- struct tm crtm;
/*
* If we haven't already read the header, do so.
@@ -3771,6 +3770,8 @@ ReadHead(ArchiveHandle *AH)
if (AH->version >= K_VERS_1_4)
{
+ struct tm crtm;
+
crtm.tm_sec = ReadInt(AH);
crtm.tm_min = ReadInt(AH);
crtm.tm_hour = ReadInt(AH);
@@ -3779,12 +3780,32 @@ ReadHead(ArchiveHandle *AH)
crtm.tm_year = ReadInt(AH);
crtm.tm_isdst = ReadInt(AH);
- AH->archdbname = ReadStr(AH);
-
+ /*
+ * Newer versions of glibc have mktime() report failure if tm_isdst is
+ * inconsistent with the prevailing timezone, e.g. tm_isdst = 1 when
+ * TZ=UTC. This is problematic when restoring an archive under a
+ * different timezone setting. If we get a failure, try again with
+ * tm_isdst set to -1 ("don't know").
+ *
+ * XXX with or without this hack, we reconstruct createDate
+ * incorrectly when the prevailing timezone is different from
+ * pg_dump's. Next time we bump the archive version, we should flush
+ * this representation and store a plain seconds-since-the-Epoch
+ * timestamp instead.
+ */
AH->createDate = mktime(&crtm);
-
if (AH->createDate == (time_t) -1)
- pg_log_warning("invalid creation date in header");
+ {
+ crtm.tm_isdst = -1;
+ AH->createDate = mktime(&crtm);
+ if (AH->createDate == (time_t) -1)
+ pg_log_warning("invalid creation date in header");
+ }
+ }
+
+ if (AH->version >= K_VERS_1_4)
+ {
+ AH->archdbname = ReadStr(AH);
}
if (AH->version >= K_VERS_1_10)