diff options
| author | Bruce Momjian <bruce@momjian.us> | 2005-04-19 03:13:59 +0000 |
|---|---|---|
| committer | Bruce Momjian <bruce@momjian.us> | 2005-04-19 03:13:59 +0000 |
| commit | aa8bdab272b6def93864797a31ebe34814d0f4bb (patch) | |
| tree | 4395ef4ac32929e0cea131cb956372eff140144a /src/timezone | |
| parent | dd39dd232f608fdbaa029992eb6696a04f27459d (diff) | |
| download | postgresql-aa8bdab272b6def93864797a31ebe34814d0f4bb.tar.gz | |
Attached patch gets rid of the global timezone in the following steps:
* Changes the APIs to the timezone functions to take a pg_tz pointer as
an argument, representing the timezone to use for the selected
operation.
* Adds a global_timezone variable that represents the current timezone
in the backend as set by SET TIMEZONE (or guc, or env, etc).
* Implements a hash-table cache of loaded tables, so we don't have to
read and parse the TZ file everytime we change a timezone. While not
necesasry now (we don't change timezones very often), I beleive this
will be necessary (or at least good) when "multiple timezones in the
same query" is eventually implemented. And code-wise, this was the time
to do it.
There are no user-visible changes at this time. Implementing the
"multiple zones in one query" is a later step...
This also gets rid of some of the cruft needed to "back out a timezone
change", since we previously couldn't check a timezone unless it was
activated first.
Passes regression tests on win32, linux (slackware 10) and solaris x86.
Magnus Hagander
Diffstat (limited to 'src/timezone')
| -rw-r--r-- | src/timezone/localtime.c | 99 | ||||
| -rw-r--r-- | src/timezone/pgtz.c | 127 | ||||
| -rw-r--r-- | src/timezone/pgtz.h | 44 | ||||
| -rw-r--r-- | src/timezone/strftime.c | 4 | ||||
| -rw-r--r-- | src/timezone/zic.c | 4 |
5 files changed, 185 insertions, 93 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 8e64f065b4..28b8f289a2 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -3,7 +3,7 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.9 2004/11/01 21:34:44 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.10 2005/04/19 03:13:59 momjian Exp $ */ /* @@ -16,8 +16,8 @@ #include <fcntl.h> -#include "pgtz.h" #include "private.h" +#include "pgtz.h" #include "tzfile.h" @@ -58,37 +58,6 @@ static const char gmt[] = "GMT"; */ #define TZDEFRULESTRING ",M4.1.0,M10.5.0" -struct ttinfo -{ /* time type information */ - long tt_gmtoff; /* UTC offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UTC */ -}; - -struct lsinfo -{ /* leap second information */ - pg_time_t ls_trans; /* transition time */ - long ls_corr; /* correction to apply */ -}; - -#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) - -struct state -{ - int leapcnt; - int timecnt; - int typecnt; - int charcnt; - pg_time_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; - struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), - (2 * (TZ_STRLEN_MAX + 1)))]; - struct lsinfo lsis[TZ_MAX_LEAPS]; -}; - struct rule { int r_type; /* type of rule--see below */ @@ -115,22 +84,19 @@ static const char *getoffset(const char *strp, long *offsetp); static const char *getrule(const char *strp, struct rule * rulep); static void gmtload(struct state * sp); static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp); -static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp); +static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz); static void timesub(const pg_time_t *timep, long offset, const struct state * sp, struct pg_tm * tmp); static pg_time_t transtime(pg_time_t janfirst, int year, - const struct rule * rulep, long offset); -static int tzload(const char *name, struct state * sp); -static int tzparse(const char *name, struct state * sp, int lastditch); + const struct rule * rulep, long offset); +int tzparse(const char *name, struct state * sp, int lastditch); -static struct state lclmem; +/* GMT timezone */ static struct state gmtmem; -#define lclptr (&lclmem) #define gmtptr (&gmtmem) -static char lcl_TZname[TZ_STRLEN_MAX + 1]; -static int lcl_is_set = 0; + static int gmt_is_set = 0; /* @@ -156,7 +122,7 @@ detzcode(const char *codep) return result; } -static int +int tzload(register const char *name, register struct state * sp) { register const char *p; @@ -589,7 +555,7 @@ transtime(const pg_time_t janfirst, const int year, * appropriate. */ -static int +int tzparse(const char *name, register struct state * sp, const int lastditch) { const char *stdname; @@ -839,30 +805,6 @@ gmtload(struct state * sp) } -bool -pg_tzset(const char *name) -{ - if (lcl_is_set && strcmp(lcl_TZname, name) == 0) - return true; /* no change */ - - if (strlen(name) >= sizeof(lcl_TZname)) - return false; /* not gonna fit */ - - if (tzload(name, lclptr) != 0) - { - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - { - /* Unknown timezone. Fail our call instead of loading GMT! */ - return false; - } - } - - strcpy(lcl_TZname, name); - lcl_is_set = true; - - return true; -} - /* * The easy way to behave "as if no library function calls" localtime * is to not call it--so we drop its guts into "localsub", which can be @@ -872,14 +814,14 @@ pg_tzset(const char *name) * The unused offset argument is for the benefit of mktime variants. */ static void -localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) +localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp, const pg_tz *tz) { - register struct state *sp; + register const struct state *sp; register const struct ttinfo *ttisp; register int i; const pg_time_t t = *timep; - sp = lclptr; + sp = &tz->state; if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; @@ -906,9 +848,9 @@ localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) struct pg_tm * -pg_localtime(const pg_time_t *timep) +pg_localtime(const pg_time_t *timep, const pg_tz *tz) { - localsub(timep, 0L, &tm); + localsub(timep, 0L, &tm, tz); return &tm; } @@ -1084,15 +1026,16 @@ pg_next_dst_boundary(const pg_time_t *timep, int *before_isdst, pg_time_t *boundary, long int *after_gmtoff, - int *after_isdst) + int *after_isdst, + const pg_tz *tz) { - register struct state *sp; + register const struct state *sp; register const struct ttinfo *ttisp; int i; int j; const pg_time_t t = *timep; - sp = lclptr; + sp = &tz->state; if (sp->timecnt == 0) { /* non-DST zone, use lowest-numbered standard type */ @@ -1158,9 +1101,9 @@ pg_next_dst_boundary(const pg_time_t *timep, * Return the name of the current timezone */ const char * -pg_get_current_timezone(void) +pg_get_timezone_name(pg_tz *tz) { - if (lcl_is_set) - return lcl_TZname; + if (tz) + return tz->TZname; return NULL; } diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 599aaac4ef..f228ed5bac 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.29 2004/12/31 22:03:59 pgsql Exp $ + * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.30 2005/04/19 03:13:59 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,12 +26,18 @@ #include "utils/datetime.h" #include "utils/elog.h" #include "utils/guc.h" +#include "utils/hsearch.h" + +/* Current global timezone */ +pg_tz *global_timezone = NULL; static char tzdir[MAXPGPATH]; static int done_tzdir = 0; static const char *identify_system_timezone(void); +static const char *select_default_timezone(void); +static bool set_global_timezone(const char *tzname); /* @@ -156,12 +162,14 @@ score_timezone(const char *tzname, struct tztry * tt) struct tm *systm; struct pg_tm *pgtm; char cbuf[TZ_STRLEN_MAX + 1]; + pg_tz *tz; - if (!pg_tzset(tzname)) + tz = pg_tzset(tzname); + if (!tz) return -1; /* can't handle the TZ name at all */ /* Reject if leap seconds involved */ - if (!tz_acceptable()) + if (!tz_acceptable(tz)) { elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname); return -1; @@ -171,7 +179,7 @@ score_timezone(const char *tzname, struct tztry * tt) for (i = 0; i < tt->n_test_times; i++) { pgtt = (pg_time_t) (tt->test_times[i]); - pgtm = pg_localtime(&pgtt); + pgtm = pg_localtime(&pgtt, tz); if (!pgtm) return -1; /* probably shouldn't happen */ systm = localtime(&(tt->test_times[i])); @@ -957,6 +965,82 @@ identify_system_timezone(void) #endif /* WIN32 */ + +/* + * We keep loaded timezones in a hashtable so we don't have to + * load and parse the TZ definition file every time it is selected. + */ +static HTAB *timezone_cache = NULL; +static bool +init_timezone_hashtable(void) +{ + HASHCTL hash_ctl; + + MemSet(&hash_ctl, 0, sizeof(hash_ctl)); + + hash_ctl.keysize = TZ_STRLEN_MAX; + hash_ctl.entrysize = sizeof(pg_tz); + + timezone_cache = hash_create("Timezones", + 31, + &hash_ctl, + HASH_ELEM); + if (!timezone_cache) + return false; + + return true; +} + +/* + * Load a timezone from file or from cache. + * Does not verify that the timezone is acceptable! + */ +struct pg_tz * +pg_tzset(const char *name) +{ + pg_tz *tzp; + pg_tz tz; + + if (strlen(name) >= TZ_STRLEN_MAX) + return NULL; /* not going to fit */ + + if (!timezone_cache) + if (!init_timezone_hashtable()) + return NULL; + + tzp = (pg_tz *)hash_search(timezone_cache, + name, + HASH_FIND, + NULL); + if (tzp) + /* Timezone found in cache, nothing more to do */ + return tzp; + + if (tzload(name, &tz.state) != 0) + { + if (name[0] == ':' || tzparse(name, &tz.state, FALSE) != 0) + /* Unknown timezone. Fail our call instead of loading GMT! */ + return NULL; + } + + strcpy(tz.TZname, name); + + /* Save timezone in the cache */ + tzp = hash_search(timezone_cache, + name, + HASH_ENTER, + NULL); + + if (!tzp) + return NULL; + + strcpy(tzp->TZname, tz.TZname); + memcpy(&tzp->state, &tz.state, sizeof(tz.state)); + + return tzp; +} + + /* * Check whether timezone is acceptable. * @@ -968,7 +1052,7 @@ identify_system_timezone(void) * it can restore the old value of TZ if we don't like the new one. */ bool -tz_acceptable(void) +tz_acceptable(pg_tz *tz) { struct pg_tm *tt; pg_time_t time2000; @@ -979,13 +1063,36 @@ tz_acceptable(void) * any other result has to be due to leap seconds. */ time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400; - tt = pg_localtime(&time2000); + tt = pg_localtime(&time2000, tz); if (!tt || tt->tm_sec != 0) return false; return true; } + +/* + * Set the global timezone. Verify that it's acceptable first. + */ +static bool +set_global_timezone(const char *tzname) +{ + pg_tz *tznew; + + if (!tzname || !tzname[0]) + return false; + + tznew = pg_tzset(tzname); + if (!tznew) + return false; + + if (!tz_acceptable(tznew)) + return false; + + global_timezone = tznew; + return true; +} + /* * Identify a suitable default timezone setting based on the environment, * and make it active. @@ -995,20 +1102,20 @@ tz_acceptable(void) * from the behavior of the system timezone library. When all else fails, * fall back to GMT. */ -const char * +static const char * select_default_timezone(void) { const char *def_tz; def_tz = getenv("TZ"); - if (def_tz && pg_tzset(def_tz) && tz_acceptable()) + if (set_global_timezone(def_tz)) return def_tz; def_tz = identify_system_timezone(); - if (def_tz && pg_tzset(def_tz) && tz_acceptable()) + if (set_global_timezone(def_tz)) return def_tz; - if (pg_tzset("GMT") && tz_acceptable()) + if (set_global_timezone("GMT")) return "GMT"; ereport(FATAL, diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h index 6c05198aed..e325dec483 100644 --- a/src/timezone/pgtz.h +++ b/src/timezone/pgtz.h @@ -9,15 +9,57 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.10 2004/12/31 22:03:59 pgsql Exp $ + * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.11 2005/04/19 03:13:59 momjian Exp $ * *------------------------------------------------------------------------- */ #ifndef _PGTZ_H #define _PGTZ_H +#include "tzfile.h" + #define TZ_STRLEN_MAX 255 extern char *pg_TZDIR(void); +#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) + +struct ttinfo +{ /* time type information */ + long tt_gmtoff; /* UTC offset in seconds */ + int tt_isdst; /* used to set tm_isdst */ + int tt_abbrind; /* abbreviation list index */ + int tt_ttisstd; /* TRUE if transition is std time */ + int tt_ttisgmt; /* TRUE if transition is UTC */ +}; + +struct lsinfo +{ /* leap second information */ + pg_time_t ls_trans; /* transition time */ + long ls_corr; /* correction to apply */ +}; + +struct state +{ + int leapcnt; + int timecnt; + int typecnt; + int charcnt; + pg_time_t ats[TZ_MAX_TIMES]; + unsigned char types[TZ_MAX_TIMES]; + struct ttinfo ttis[TZ_MAX_TYPES]; + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */), + (2 * (TZ_STRLEN_MAX + 1)))]; + struct lsinfo lsis[TZ_MAX_LEAPS]; +}; + + +struct pg_tz { + char TZname[TZ_STRLEN_MAX + 1]; + struct state state; +}; + +int tzload(const char *name, struct state * sp); +int tzparse(const char *name, struct state * sp, int lastditch); + #endif /* _PGTZ_H */ diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index c23ae7cb6f..cc45527fe4 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -15,7 +15,7 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.5 2004/08/29 05:07:02 momjian Exp $ + * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.6 2005/04/19 03:13:59 momjian Exp $ */ #include "postgres.h" @@ -23,8 +23,8 @@ #include <fcntl.h> #include <locale.h> -#include "pgtz.h" #include "private.h" +#include "pgtz.h" #include "tzfile.h" diff --git a/src/timezone/zic.c b/src/timezone/zic.c index 5cc2432998..1d3695b659 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -3,7 +3,7 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.13 2004/09/27 19:16:03 momjian Exp $ + * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.14 2005/04/19 03:13:59 momjian Exp $ */ #include "postgres.h" @@ -14,8 +14,8 @@ #include <limits.h> #include <locale.h> -#include "pgtz.h" #include "private.h" +#include "pgtz.h" #include "tzfile.h" #ifdef HAVE_SYS_STAT_H |
