Simplify handling of the timezone GUC by making initdb choose the default.

We were doing some amazingly complicated things in order to avoid running
the very expensive identify_system_timezone() procedure during GUC
initialization.  But there is an obvious fix for that, which is to do it
once during initdb and have initdb install the system-specific default into
postgresql.conf, as it already does for most other GUC variables that need
system-environment-dependent defaults.  This means that the timezone (and
log_timezone) settings no longer have any magic behavior in the server.
Per discussion.
This commit is contained in:
Tom Lane 2011-09-09 17:59:11 -04:00
parent a7801b62f2
commit ca4af308c3
19 changed files with 1373 additions and 1368 deletions

View File

@ -3915,9 +3915,10 @@ FROM pg_stat_activity;
Sets the time zone used for timestamps written in the server log. Sets the time zone used for timestamps written in the server log.
Unlike <xref linkend="guc-timezone">, this value is cluster-wide, Unlike <xref linkend="guc-timezone">, this value is cluster-wide,
so that all sessions will report timestamps consistently. so that all sessions will report timestamps consistently.
If not explicitly set, the server initializes this variable to the The built-in default is <literal>GMT</>, but that is typically
time zone specified by its system environment. See <xref overridden in <filename>postgresql.conf</>; <application>initdb</>
linkend="datatype-timezones"> for more information. will install a setting there corresponding to its system environment.
See <xref linkend="datatype-timezones"> for more information.
This parameter can only be set in the <filename>postgresql.conf</> This parameter can only be set in the <filename>postgresql.conf</>
file or on the server command line. file or on the server command line.
</para> </para>
@ -4963,9 +4964,10 @@ SET XML OPTION { DOCUMENT | CONTENT };
<listitem> <listitem>
<para> <para>
Sets the time zone for displaying and interpreting time stamps. Sets the time zone for displaying and interpreting time stamps.
If not explicitly set, the server initializes this variable to the The built-in default is <literal>GMT</>, but that is typically
time zone specified by its system environment. See <xref overridden in <filename>postgresql.conf</>; <application>initdb</>
linkend="datatype-timezones"> for more information. will install a setting there corresponding to its system environment.
See <xref linkend="datatype-timezones"> for more information.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -2286,7 +2286,7 @@ January 8 04:05:06 1999 PST
but continue to be prone to arbitrary changes, particularly with but continue to be prone to arbitrary changes, particularly with
respect to daylight-savings rules. respect to daylight-savings rules.
<productname>PostgreSQL</productname> uses the widely-used <productname>PostgreSQL</productname> uses the widely-used
<literal>zoneinfo</> time zone database for information about <literal>zoneinfo</> (Olson) time zone database for information about
historical time zone rules. For times in the future, the assumption historical time zone rules. For times in the future, the assumption
is that the latest known rules for a given time zone will is that the latest known rules for a given time zone will
continue to be observed indefinitely far into the future. continue to be observed indefinitely far into the future.
@ -2432,26 +2432,9 @@ January 8 04:05:06 1999 PST
The <xref linkend="guc-timezone"> configuration parameter can The <xref linkend="guc-timezone"> configuration parameter can
be set in the file <filename>postgresql.conf</>, or in any of the be set in the file <filename>postgresql.conf</>, or in any of the
other standard ways described in <xref linkend="runtime-config">. other standard ways described in <xref linkend="runtime-config">.
There are also several special ways to set it: There are also some special ways to set it:
<itemizedlist> <itemizedlist>
<listitem>
<para>
If <varname>timezone</> is not specified in
<filename>postgresql.conf</> or as a server command-line option,
the server attempts to use the value of the <envar>TZ</envar>
environment variable as the default time zone. If <envar>TZ</envar>
is not defined or is not any of the time zone names known to
<productname>PostgreSQL</productname>, the server attempts to
determine the operating system's default time zone by checking the
behavior of the C library function <literal>localtime()</>. The
default time zone is selected as the closest match among
<productname>PostgreSQL</productname>'s known time zones.
(These rules are also used to choose the default value of
<xref linkend="guc-log-timezone">, if not specified.)
</para>
</listitem>
<listitem> <listitem>
<para> <para>
The <acronym>SQL</acronym> command <command>SET TIME ZONE</command> The <acronym>SQL</acronym> command <command>SET TIME ZONE</command>

View File

@ -239,9 +239,7 @@ SELECT setseed(<replaceable>value</replaceable>);
<listitem> <listitem>
<para> <para>
Set the time zone to your local time zone (that is, the Set the time zone to your local time zone (that is, the
server's default value of <varname>timezone</>; if this server's default value of <varname>timezone</>).
has not been explicitly set anywhere, it will be the zone that
the server's operating system defaults to).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -333,10 +333,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
{ {
if (!SelectConfigFiles(userDoption, progname)) if (!SelectConfigFiles(userDoption, progname))
proc_exit(1); proc_exit(1);
/* If timezone is not set, determine what the OS uses */
pg_timezone_initialize();
/* If timezone_abbreviations is not set, select default */
pg_timezone_abbrev_initialize();
} }
/* Validate we have been given a reasonable-looking DataDir */ /* Validate we have been given a reasonable-looking DataDir */

View File

@ -259,23 +259,6 @@ check_timezone(char **newval, void **extra, GucSource source)
char *endptr; char *endptr;
double hours; double hours;
if (*newval == NULL)
{
/*
* The boot_val given for TimeZone in guc.c is NULL. When we see this
* we just do nothing. If this isn't overridden from the config file
* then pg_timezone_initialize() will eventually select a default
* value from the environment. This hack has two purposes: to avoid
* wasting cycles loading values that might soon be overridden from
* the config file, and to avoid trying to read the timezone files
* during InitializeGUCOptions(). The latter doesn't work in an
* EXEC_BACKEND subprocess because my_exec_path hasn't been set yet
* and so we can't locate PGSHAREDIR.
*/
Assert(source == PGC_S_DEFAULT);
return true;
}
/* /*
* Initialize the "extra" struct that will be passed to assign_timezone. * Initialize the "extra" struct that will be passed to assign_timezone.
* We don't want to change any of the three global variables except as * We don't want to change any of the three global variables except as
@ -374,7 +357,7 @@ check_timezone(char **newval, void **extra, GucSource source)
return false; return false;
} }
if (!tz_acceptable(new_tz)) if (!pg_tz_acceptable(new_tz))
{ {
GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
*newval); *newval);
@ -427,10 +410,6 @@ assign_timezone(const char *newval, void *extra)
{ {
timezone_extra *myextra = (timezone_extra *) extra; timezone_extra *myextra = (timezone_extra *) extra;
/* Do nothing for the boot_val default of NULL */
if (!myextra)
return;
session_timezone = myextra->session_timezone; session_timezone = myextra->session_timezone;
CTimeZone = myextra->CTimeZone; CTimeZone = myextra->CTimeZone;
HasCTZSet = myextra->HasCTZSet; HasCTZSet = myextra->HasCTZSet;
@ -490,20 +469,8 @@ check_log_timezone(char **newval, void **extra, GucSource source)
{ {
pg_tz *new_tz; pg_tz *new_tz;
if (*newval == NULL)
{
/*
* The boot_val given for log_timezone in guc.c is NULL. When we see
* this we just do nothing. If this isn't overridden from the config
* file then pg_timezone_initialize() will eventually select a default
* value from the environment.
*/
Assert(source == PGC_S_DEFAULT);
return true;
}
/* /*
* Otherwise assume it is a timezone name, and try to load it. * Assume it is a timezone name, and try to load it.
*/ */
new_tz = pg_tzset(*newval); new_tz = pg_tzset(*newval);
@ -513,7 +480,7 @@ check_log_timezone(char **newval, void **extra, GucSource source)
return false; return false;
} }
if (!tz_acceptable(new_tz)) if (!pg_tz_acceptable(new_tz))
{ {
GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
*newval); *newval);
@ -538,10 +505,6 @@ check_log_timezone(char **newval, void **extra, GucSource source)
void void
assign_log_timezone(const char *newval, void *extra) assign_log_timezone(const char *newval, void *extra)
{ {
/* Do nothing for the boot_val default of NULL */
if (!extra)
return;
log_timezone = *((pg_tz **) extra); log_timezone = *((pg_tz **) extra);
} }

View File

@ -795,21 +795,6 @@ PostmasterMain(int argc, char *argv[])
*/ */
CreateDataDirLockFile(true); CreateDataDirLockFile(true);
/*
* If timezone is not set, determine what the OS uses. (In theory this
* should be done during GUC initialization, but because it can take as
* much as several seconds, we delay it until after we've created the
* postmaster.pid file. This prevents problems with boot scripts that
* expect the pidfile to appear quickly. Also, we avoid problems with
* trying to locate the timezone files too early in initialization.)
*/
pg_timezone_initialize();
/*
* Likewise, init timezone_abbreviations if not already set.
*/
pg_timezone_abbrev_initialize();
/* /*
* Initialize SSL library, if specified. * Initialize SSL library, if specified.
*/ */

View File

@ -3537,10 +3537,6 @@ PostgresMain(int argc, char *argv[], const char *username)
{ {
if (!SelectConfigFiles(userDoption, progname)) if (!SelectConfigFiles(userDoption, progname))
proc_exit(1); proc_exit(1);
/* If timezone is not set, determine what the OS uses */
pg_timezone_initialize();
/* If timezone_abbreviations is not set, select default */
pg_timezone_abbrev_initialize();
} }
/* /*

View File

@ -1805,24 +1805,20 @@ setup_formatted_log_time(void)
{ {
struct timeval tv; struct timeval tv;
pg_time_t stamp_time; pg_time_t stamp_time;
pg_tz *tz;
char msbuf[8]; char msbuf[8];
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
stamp_time = (pg_time_t) tv.tv_sec; stamp_time = (pg_time_t) tv.tv_sec;
/* /*
* Normally we print log timestamps in log_timezone, but during startup we * Note: we expect that guc.c will ensure that log_timezone is set up
* could get here before that's set. If so, fall back to gmt_timezone * (at least with a minimal GMT value) before Log_line_prefix can become
* (which guc.c ensures is set up before Log_line_prefix can become * nonempty or CSV mode can be selected.
* nonempty).
*/ */
tz = log_timezone ? log_timezone : gmt_timezone;
pg_strftime(formatted_log_time, FORMATTED_TS_LEN, pg_strftime(formatted_log_time, FORMATTED_TS_LEN,
/* leave room for milliseconds... */ /* leave room for milliseconds... */
"%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, tz)); pg_localtime(&stamp_time, log_timezone));
/* 'paste' milliseconds into place... */ /* 'paste' milliseconds into place... */
sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000)); sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
@ -1836,19 +1832,15 @@ static void
setup_formatted_start_time(void) setup_formatted_start_time(void)
{ {
pg_time_t stamp_time = (pg_time_t) MyStartTime; pg_time_t stamp_time = (pg_time_t) MyStartTime;
pg_tz *tz;
/* /*
* Normally we print log timestamps in log_timezone, but during startup we * Note: we expect that guc.c will ensure that log_timezone is set up
* could get here before that's set. If so, fall back to gmt_timezone * (at least with a minimal GMT value) before Log_line_prefix can become
* (which guc.c ensures is set up before Log_line_prefix can become * nonempty or CSV mode can be selected.
* nonempty).
*/ */
tz = log_timezone ? log_timezone : gmt_timezone;
pg_strftime(formatted_start_time, FORMATTED_TS_LEN, pg_strftime(formatted_start_time, FORMATTED_TS_LEN,
"%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, tz)); pg_localtime(&stamp_time, log_timezone));
} }
/* /*
@ -1947,14 +1939,11 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
case 't': case 't':
{ {
pg_time_t stamp_time = (pg_time_t) time(NULL); pg_time_t stamp_time = (pg_time_t) time(NULL);
pg_tz *tz;
char strfbuf[128]; char strfbuf[128];
tz = log_timezone ? log_timezone : gmt_timezone;
pg_strftime(strfbuf, sizeof(strfbuf), pg_strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, tz)); pg_localtime(&stamp_time, log_timezone));
appendStringInfoString(buf, strfbuf); appendStringInfoString(buf, strfbuf);
} }
break; break;

View File

@ -292,7 +292,6 @@ ProcessConfigFile(GucContext context)
if (context == PGC_SIGHUP) if (context == PGC_SIGHUP)
{ {
InitializeGUCOptionsFromEnvironment(); InitializeGUCOptionsFromEnvironment();
pg_timezone_initialize();
pg_timezone_abbrev_initialize(); pg_timezone_abbrev_initialize();
/* this selects SQL_ASCII in processes not connected to a database */ /* this selects SQL_ASCII in processes not connected to a database */
SetConfigOption("client_encoding", GetDatabaseEncodingName(), SetConfigOption("client_encoding", GetDatabaseEncodingName(),

View File

@ -187,6 +187,7 @@ static bool check_log_stats(bool *newval, void **extra, GucSource source);
static bool check_canonical_path(char **newval, void **extra, GucSource source); static bool check_canonical_path(char **newval, void **extra, GucSource source);
static bool check_timezone_abbreviations(char **newval, void **extra, GucSource source); static bool check_timezone_abbreviations(char **newval, void **extra, GucSource source);
static void assign_timezone_abbreviations(const char *newval, void *extra); static void assign_timezone_abbreviations(const char *newval, void *extra);
static void pg_timezone_abbrev_initialize(void);
static const char *show_archive_command(void); static const char *show_archive_command(void);
static void assign_tcp_keepalives_idle(int newval, void *extra); static void assign_tcp_keepalives_idle(int newval, void *extra);
static void assign_tcp_keepalives_interval(int newval, void *extra); static void assign_tcp_keepalives_interval(int newval, void *extra);
@ -2547,7 +2548,7 @@ static struct config_string ConfigureNamesString[] =
NULL NULL
}, },
&log_timezone_string, &log_timezone_string,
NULL, "GMT",
check_log_timezone, assign_log_timezone, show_log_timezone check_log_timezone, assign_log_timezone, show_log_timezone
}, },
@ -2827,7 +2828,7 @@ static struct config_string ConfigureNamesString[] =
GUC_REPORT GUC_REPORT
}, },
&timezone_string, &timezone_string,
NULL, "GMT",
check_timezone, assign_timezone, show_timezone check_timezone, assign_timezone, show_timezone
}, },
{ {
@ -3817,7 +3818,7 @@ InitializeGUCOptions(void)
* Before log_line_prefix could possibly receive a nonempty setting, make * Before log_line_prefix could possibly receive a nonempty setting, make
* sure that timezone processing is minimally alive (see elog.c). * sure that timezone processing is minimally alive (see elog.c).
*/ */
pg_timezone_pre_initialize(); pg_timezone_initialize();
/* /*
* Build sorted array of all GUC variables. * Build sorted array of all GUC variables.
@ -4114,6 +4115,15 @@ SelectConfigFiles(const char *userDoption, const char *progname)
*/ */
SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);
/*
* If timezone_abbreviations wasn't set in the configuration file, install
* the default value. We do it this way because we can't safely install
* a "real" value until my_exec_path is set, which may not have happened
* when InitializeGUCOptions runs, so the bootstrap default value cannot
* be the real desired default.
*/
pg_timezone_abbrev_initialize();
/* /*
* Figure out where pg_hba.conf is, and make sure the path is absolute. * Figure out where pg_hba.conf is, and make sure the path is absolute.
*/ */
@ -8444,8 +8454,11 @@ assign_timezone_abbreviations(const char *newval, void *extra)
* This is called after initial loading of postgresql.conf. If no * This is called after initial loading of postgresql.conf. If no
* timezone_abbreviations setting was found therein, select default. * timezone_abbreviations setting was found therein, select default.
* If a non-default value is already installed, nothing will happen. * If a non-default value is already installed, nothing will happen.
*
* This can also be called from ProcessConfigFile to establish the default
* value after a postgresql.conf entry for it is removed.
*/ */
void static void
pg_timezone_abbrev_initialize(void) pg_timezone_abbrev_initialize(void)
{ {
SetConfigOption("timezone_abbreviations", "Default", SetConfigOption("timezone_abbreviations", "Default",

View File

@ -406,7 +406,7 @@
#log_temp_files = -1 # log temporary files equal or larger #log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes; # than the specified size in kilobytes;
# -1 disables, 0 logs all temp files # -1 disables, 0 logs all temp files
#log_timezone = '(defaults to server environment setting)' #log_timezone = 'GMT'
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@ -486,7 +486,7 @@
#datestyle = 'iso, mdy' #datestyle = 'iso, mdy'
#intervalstyle = 'postgres' #intervalstyle = 'postgres'
#timezone = '(defaults to server environment setting)' #timezone = 'GMT'
#timezone_abbreviations = 'Default' # Select the set of available time zone #timezone_abbreviations = 'Default' # Select the set of available time zone
# abbreviations. Currently, there are # abbreviations. Currently, there are
# Default # Default

View File

@ -1,4 +1,5 @@
/encnames.c /encnames.c
/pqsignal.c /pqsignal.c
/localtime.c
/initdb /initdb

View File

@ -16,9 +16,9 @@ subdir = src/bin/initdb
top_builddir = ../../.. top_builddir = ../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) -I$(top_srcdir)/src/timezone $(CPPFLAGS)
OBJS= initdb.o encnames.o pqsignal.o $(WIN32RES) OBJS= initdb.o findtimezone.o localtime.o encnames.o pqsignal.o $(WIN32RES)
all: initdb all: initdb
@ -35,6 +35,11 @@ encnames.c: % : $(top_srcdir)/src/backend/utils/mb/%
pqsignal.c: % : $(top_srcdir)/src/interfaces/libpq/% pqsignal.c: % : $(top_srcdir)/src/interfaces/libpq/%
rm -f $@ && $(LN_S) $< . rm -f $@ && $(LN_S) $< .
# Likewise, pull in localtime.c from src/timezones
localtime.c: % : $(top_srcdir)/src/timezone/%
rm -f $@ && $(LN_S) $< .
install: all installdirs install: all installdirs
$(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(bindir)/initdb$(X)' $(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(bindir)/initdb$(X)'
@ -45,7 +50,7 @@ uninstall:
rm -f '$(DESTDIR)$(bindir)/initdb$(X)' rm -f '$(DESTDIR)$(bindir)/initdb$(X)'
clean distclean maintainer-clean: clean distclean maintainer-clean:
rm -f initdb$(X) $(OBJS) encnames.c pqsignal.c rm -f initdb$(X) $(OBJS) encnames.c pqsignal.c localtime.c
# ensure that changes in datadir propagate into object file # ensure that changes in datadir propagate into object file

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,9 @@
#include "getopt_long.h" #include "getopt_long.h"
#include "miscadmin.h" #include "miscadmin.h"
/* Ideally this would be in a .h file, but it hardly seems worth the trouble */
extern const char *select_default_timezone(const char *share_path);
/* /*
* these values are passed in by makefile defines * these values are passed in by makefile defines
@ -947,8 +950,9 @@ static void
setup_config(void) setup_config(void)
{ {
char **conflines; char **conflines;
char repltok[100]; char repltok[TZ_STRLEN_MAX + 100];
char path[MAXPGPATH]; char path[MAXPGPATH];
const char *default_timezone;
fputs(_("creating configuration files ... "), stdout); fputs(_("creating configuration files ... "), stdout);
fflush(stdout); fflush(stdout);
@ -1011,6 +1015,17 @@ setup_config(void)
"#default_text_search_config = 'pg_catalog.simple'", "#default_text_search_config = 'pg_catalog.simple'",
repltok); repltok);
default_timezone = select_default_timezone(share_path);
if (default_timezone)
{
snprintf(repltok, sizeof(repltok), "timezone = '%s'",
escape_quotes(default_timezone));
conflines = replace_token(conflines, "#timezone = 'GMT'", repltok);
snprintf(repltok, sizeof(repltok), "log_timezone = '%s'",
escape_quotes(default_timezone));
conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok);
}
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
writefile(path, conflines); writefile(path, conflines);
@ -2796,14 +2811,6 @@ main(int argc, char *argv[])
sprintf(pgdenv, "PGDATA=%s", pg_data); sprintf(pgdenv, "PGDATA=%s", pg_data);
putenv(pgdenv); putenv(pgdenv);
/*
* Also ensure that TZ is set, so that we don't waste time identifying the
* system timezone each of the many times we start a standalone backend.
* It's okay to use a hard-wired value here because nothing done during
* initdb cares about the timezone setting.
*/
putenv("TZ=GMT");
if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR,
backend_exec)) < 0) backend_exec)) < 0)
{ {

View File

@ -40,6 +40,11 @@ struct pg_tm
typedef struct pg_tz pg_tz; typedef struct pg_tz pg_tz;
typedef struct pg_tzenum pg_tzenum; typedef struct pg_tzenum pg_tzenum;
/* Maximum length of a timezone name (not including trailing null) */
#define TZ_STRLEN_MAX 255
/* these functions are in localtime.c */
extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz); extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz);
extern struct pg_tm *pg_gmtime(const pg_time_t *timep); extern struct pg_tm *pg_gmtime(const pg_time_t *timep);
extern int pg_next_dst_boundary(const pg_time_t *timep, extern int pg_next_dst_boundary(const pg_time_t *timep,
@ -52,22 +57,20 @@ extern int pg_next_dst_boundary(const pg_time_t *timep,
extern size_t pg_strftime(char *s, size_t max, const char *format, extern size_t pg_strftime(char *s, size_t max, const char *format,
const struct pg_tm * tm); const struct pg_tm * tm);
extern void pg_timezone_pre_initialize(void);
extern void pg_timezone_initialize(void);
extern pg_tz *pg_tzset(const char *tzname);
extern bool tz_acceptable(pg_tz *tz);
extern bool pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff); extern bool pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff);
extern const char *pg_get_timezone_name(pg_tz *tz); extern const char *pg_get_timezone_name(pg_tz *tz);
extern bool pg_tz_acceptable(pg_tz *tz);
/* these functions and variables are in pgtz.c */
extern pg_tz *session_timezone;
extern pg_tz *log_timezone;
extern void pg_timezone_initialize(void);
extern pg_tz *pg_tzset(const char *tzname);
extern pg_tzenum *pg_tzenumerate_start(void); extern pg_tzenum *pg_tzenumerate_start(void);
extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir); extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir);
extern void pg_tzenumerate_end(pg_tzenum *dir); extern void pg_tzenumerate_end(pg_tzenum *dir);
extern pg_tz *session_timezone;
extern pg_tz *log_timezone;
extern pg_tz *gmt_timezone;
/* Maximum length of a timezone name (not including trailing null) */
#define TZ_STRLEN_MAX 255
#endif /* _PGTIME_H */ #endif /* _PGTIME_H */

View File

@ -333,8 +333,6 @@ extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *va
extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name); extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
extern ArrayType *GUCArrayReset(ArrayType *array); extern ArrayType *GUCArrayReset(ArrayType *array);
extern void pg_timezone_abbrev_initialize(void);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
extern void write_nondefault_variables(GucContext context); extern void write_nondefault_variables(GucContext context);
extern void read_nondefault_variables(void); extern void read_nondefault_variables(void);

View File

@ -16,6 +16,7 @@
#include <fcntl.h> #include <fcntl.h>
#include "datatype/timestamp.h"
#include "private.h" #include "private.h"
#include "pgtz.h" #include "pgtz.h"
#include "tzfile.h" #include "tzfile.h"
@ -734,6 +735,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
* can't assume pg_open_tzfile() is sane yet, and we don't care about * can't assume pg_open_tzfile() is sane yet, and we don't care about
* leap seconds anyway. * leap seconds anyway.
*/ */
sp->goback = sp->goahead = FALSE;
load_result = -1; load_result = -1;
} }
else else
@ -1476,3 +1478,29 @@ pg_get_timezone_name(pg_tz *tz)
return tz->TZname; return tz->TZname;
return NULL; return NULL;
} }
/*
* Check whether timezone is acceptable.
*
* What we are doing here is checking for leap-second-aware timekeeping.
* We need to reject such TZ settings because they'll wreak havoc with our
* date/time arithmetic.
*/
bool
pg_tz_acceptable(pg_tz *tz)
{
struct pg_tm *tt;
pg_time_t time2000;
/*
* To detect leap-second timekeeping, run pg_localtime for what should be
* GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; any
* other result has to be due to leap seconds.
*/
time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
tt = pg_localtime(&time2000, tz);
if (!tt || tt->tm_sec != 0)
return false;
return true;
}

File diff suppressed because it is too large Load Diff