mirror of
https://github.com/postgres/postgres.git
synced 2025-06-25 01:02:05 +03:00
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
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user