Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Functions | Variables

pgtz.c File Reference

#include "postgres.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include "miscadmin.h"
#include "pgtz.h"
#include "storage/fd.h"
#include "utils/hsearch.h"
Include dependency graph for pgtz.c:

Go to the source code of this file.

Data Structures

struct  pg_tz_cache
struct  pg_tzenum

Defines

#define MAX_TZDIR_DEPTH   10

Functions

static bool scan_directory_ci (const char *dirname, const char *fname, int fnamelen, char *canonname, int canonnamelen)
static const char * pg_TZDIR (void)
int pg_open_tzfile (const char *name, char *canonname)
static bool init_timezone_hashtable (void)
pg_tzpg_tzset (const char *name)
void pg_timezone_initialize (void)
pg_tzenumpg_tzenumerate_start (void)
void pg_tzenumerate_end (pg_tzenum *dir)
pg_tzpg_tzenumerate_next (pg_tzenum *dir)

Variables

pg_tzsession_timezone = NULL
pg_tzlog_timezone = NULL
static HTABtimezone_cache = NULL

Define Documentation

#define MAX_TZDIR_DEPTH   10

Definition at line 324 of file pgtz.c.

Referenced by pg_tzenumerate_next().


Function Documentation

static bool init_timezone_hashtable ( void   )  [static]

Definition at line 184 of file pgtz.c.

References HASHCTL::entrysize, hash_create(), HASH_ELEM, HASHCTL::keysize, MemSet, and TZ_STRLEN_MAX.

Referenced by pg_tzset().

{
    HASHCTL     hash_ctl;

    MemSet(&hash_ctl, 0, sizeof(hash_ctl));

    hash_ctl.keysize = TZ_STRLEN_MAX + 1;
    hash_ctl.entrysize = sizeof(pg_tz_cache);

    timezone_cache = hash_create("Timezones",
                                 4,
                                 &hash_ctl,
                                 HASH_ELEM);
    if (!timezone_cache)
        return false;

    return true;
}

int pg_open_tzfile ( const char *  name,
char *  canonname 
)

Definition at line 75 of file pgtz.c.

References MAXPGPATH, PG_BINARY, pg_TZDIR(), scan_directory_ci(), strlcpy(), and TZ_STRLEN_MAX.

{
    const char *fname;
    char        fullname[MAXPGPATH];
    int         fullnamelen;
    int         orignamelen;

    /*
     * Loop to split the given name into directory levels; for each level,
     * search using scan_directory_ci().
     */
    strcpy(fullname, pg_TZDIR());
    orignamelen = fullnamelen = strlen(fullname);
    fname = name;
    for (;;)
    {
        const char *slashptr;
        int         fnamelen;

        slashptr = strchr(fname, '/');
        if (slashptr)
            fnamelen = slashptr - fname;
        else
            fnamelen = strlen(fname);
        if (fullnamelen + 1 + fnamelen >= MAXPGPATH)
            return -1;          /* not gonna fit */
        if (!scan_directory_ci(fullname, fname, fnamelen,
                               fullname + fullnamelen + 1,
                               MAXPGPATH - fullnamelen - 1))
            return -1;
        fullname[fullnamelen++] = '/';
        fullnamelen += strlen(fullname + fullnamelen);
        if (slashptr)
            fname = slashptr + 1;
        else
            break;
    }

    if (canonname)
        strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);

    return open(fullname, O_RDONLY | PG_BINARY, 0);
}

void pg_timezone_initialize ( void   ) 

Definition at line 302 of file pgtz.c.

References pg_tzset().

Referenced by InitializeGUCOptions().

{
    /*
     * We may not yet know where PGSHAREDIR is (in particular this is true in
     * an EXEC_BACKEND subprocess).  So use "GMT", which pg_tzset forces to be
     * interpreted without reference to the filesystem.  This corresponds to
     * the bootstrap default for these variables in guc.c, although in
     * principle it could be different.
     */
    session_timezone = pg_tzset("GMT");
    log_timezone = session_timezone;
}

static const char* pg_TZDIR ( void   )  [static]

Definition at line 42 of file pgtz.c.

References get_share_path(), MAXPGPATH, my_exec_path, and strlcpy().

Referenced by pg_open_tzfile(), and pg_tzenumerate_start().

{
#ifndef SYSTEMTZDIR
    /* normal case: timezone stuff is under our share dir */
    static bool done_tzdir = false;
    static char tzdir[MAXPGPATH];

    if (done_tzdir)
        return tzdir;

    get_share_path(my_exec_path, tzdir);
    strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));

    done_tzdir = true;
    return tzdir;
#else
    /* we're configured to use system's timezone database */
    return SYSTEMTZDIR;
#endif
}

void pg_tzenumerate_end ( pg_tzenum dir  ) 

Definition at line 355 of file pgtz.c.

References pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, FreeDir(), and pfree().

Referenced by pg_timezone_names().

{
    while (dir->depth >= 0)
    {
        FreeDir(dir->dirdesc[dir->depth]);
        pfree(dir->dirname[dir->depth]);
        dir->depth--;
    }
    pfree(dir);
}

pg_tz* pg_tzenumerate_next ( pg_tzenum dir  ) 

Definition at line 367 of file pgtz.c.

References AllocateDir(), pg_tzenum::baselen, pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, ereport, errcode_for_file_access(), errmsg(), errmsg_internal(), ERROR, FreeDir(), MAX_TZDIR_DEPTH, MAXPGPATH, pfree(), pg_tz_acceptable(), pstrdup(), ReadDir(), snprintf(), pg_tz::state, TRUE, pg_tzenum::tz, tzload(), and pg_tz::TZname.

Referenced by pg_timezone_names().

{
    while (dir->depth >= 0)
    {
        struct dirent *direntry;
        char        fullname[MAXPGPATH];
        struct stat statbuf;

        direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);

        if (!direntry)
        {
            /* End of this directory */
            FreeDir(dir->dirdesc[dir->depth]);
            pfree(dir->dirname[dir->depth]);
            dir->depth--;
            continue;
        }

        if (direntry->d_name[0] == '.')
            continue;

        snprintf(fullname, MAXPGPATH, "%s/%s",
                 dir->dirname[dir->depth], direntry->d_name);
        if (stat(fullname, &statbuf) != 0)
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not stat \"%s\": %m", fullname)));

        if (S_ISDIR(statbuf.st_mode))
        {
            /* Step into the subdirectory */
            if (dir->depth >= MAX_TZDIR_DEPTH - 1)
                ereport(ERROR,
                     (errmsg_internal("timezone directory stack overflow")));
            dir->depth++;
            dir->dirname[dir->depth] = pstrdup(fullname);
            dir->dirdesc[dir->depth] = AllocateDir(fullname);
            if (!dir->dirdesc[dir->depth])
                ereport(ERROR,
                        (errcode_for_file_access(),
                         errmsg("could not open directory \"%s\": %m",
                                fullname)));

            /* Start over reading in the new directory */
            continue;
        }

        /*
         * Load this timezone using tzload() not pg_tzset(), so we don't fill
         * the cache
         */
        if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
                   TRUE) != 0)
        {
            /* Zone could not be loaded, ignore it */
            continue;
        }

        if (!pg_tz_acceptable(&dir->tz))
        {
            /* Ignore leap-second zones */
            continue;
        }

        /* Timezone loaded OK. */
        return &dir->tz;
    }

    /* Nothing more found */
    return NULL;
}

pg_tzenum* pg_tzenumerate_start ( void   ) 

Definition at line 338 of file pgtz.c.

References AllocateDir(), pg_tzenum::baselen, pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, ereport, errcode_for_file_access(), errmsg(), ERROR, palloc0(), pg_TZDIR(), and pstrdup().

Referenced by pg_timezone_names().

{
    pg_tzenum  *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
    char       *startdir = pstrdup(pg_TZDIR());

    ret->baselen = strlen(startdir) + 1;
    ret->depth = 0;
    ret->dirname[0] = startdir;
    ret->dirdesc[0] = AllocateDir(startdir);
    if (!ret->dirdesc[0])
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not open directory \"%s\": %m", startdir)));
    return ret;
}

pg_tz* pg_tzset ( const char *  name  ) 

Definition at line 218 of file pgtz.c.

References elog, ERROR, FALSE, HASH_ENTER, HASH_FIND, hash_search(), init_timezone_hashtable(), NULL, pg_toupper(), pg_tz::state, TRUE, pg_tz_cache::tz, TZ_STRLEN_MAX, tzload(), pg_tz::TZname, and tzparse().

Referenced by check_log_timezone(), check_timezone(), DecodeDateTime(), DecodeTimeOnly(), pg_timezone_initialize(), timestamp_zone(), timestamptz_zone(), and timetz_zone().

{
    pg_tz_cache *tzp;
    struct state tzstate;
    char        uppername[TZ_STRLEN_MAX + 1];
    char        canonname[TZ_STRLEN_MAX + 1];
    char       *p;

    if (strlen(name) > TZ_STRLEN_MAX)
        return NULL;            /* not going to fit */

    if (!timezone_cache)
        if (!init_timezone_hashtable())
            return NULL;

    /*
     * Upcase the given name to perform a case-insensitive hashtable search.
     * (We could alternatively downcase it, but we prefer upcase so that we
     * can get consistently upcased results from tzparse() in case the name is
     * a POSIX-style timezone spec.)
     */
    p = uppername;
    while (*name)
        *p++ = pg_toupper((unsigned char) *name++);
    *p = '\0';

    tzp = (pg_tz_cache *) hash_search(timezone_cache,
                                      uppername,
                                      HASH_FIND,
                                      NULL);
    if (tzp)
    {
        /* Timezone found in cache, nothing more to do */
        return &tzp->tz;
    }

    /*
     * "GMT" is always sent to tzparse(), as per discussion above.
     */
    if (strcmp(uppername, "GMT") == 0)
    {
        if (tzparse(uppername, &tzstate, TRUE) != 0)
        {
            /* This really, really should not happen ... */
            elog(ERROR, "could not initialize GMT time zone");
        }
        /* Use uppercase name as canonical */
        strcpy(canonname, uppername);
    }
    else if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
    {
        if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
        {
            /* Unknown timezone. Fail our call instead of loading GMT! */
            return NULL;
        }
        /* For POSIX timezone specs, use uppercase name as canonical */
        strcpy(canonname, uppername);
    }

    /* Save timezone in the cache */
    tzp = (pg_tz_cache *) hash_search(timezone_cache,
                                      uppername,
                                      HASH_ENTER,
                                      NULL);

    /* hash_search already copied uppername into the hash key */
    strcpy(tzp->tz.TZname, canonname);
    memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));

    return &tzp->tz;
}

static bool scan_directory_ci ( const char *  dirname,
const char *  fname,
int  fnamelen,
char *  canonname,
int  canonnamelen 
) [static]

Definition at line 126 of file pgtz.c.

References AllocateDir(), dirent::d_name, ereport, errcode_for_file_access(), errmsg(), FreeDir(), LOG, NULL, pg_strncasecmp(), ReadDir(), and strlcpy().

Referenced by pg_open_tzfile().

{
    bool        found = false;
    DIR        *dirdesc;
    struct dirent *direntry;

    dirdesc = AllocateDir(dirname);
    if (!dirdesc)
    {
        ereport(LOG,
                (errcode_for_file_access(),
                 errmsg("could not open directory \"%s\": %m", dirname)));
        return false;
    }

    while ((direntry = ReadDir(dirdesc, dirname)) != NULL)
    {
        /*
         * Ignore . and .., plus any other "hidden" files.  This is a security
         * measure to prevent access to files outside the timezone directory.
         */
        if (direntry->d_name[0] == '.')
            continue;

        if (strlen(direntry->d_name) == fnamelen &&
            pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
        {
            /* Found our match */
            strlcpy(canonname, direntry->d_name, canonnamelen);
            found = true;
            break;
        }
    }

    FreeDir(dirdesc);

    return found;
}


Variable Documentation

HTAB* timezone_cache = NULL [static]

Definition at line 180 of file pgtz.c.