LCOV - code coverage report
Current view: top level - src/backend/utils/misc - tzparser.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 67.1 % 222 149
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.0 % 108 68

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * tzparser.c
       4                 :             :  *        Functions for parsing timezone offset files
       5                 :             :  *
       6                 :             :  * Note: this code is invoked from the check_hook for the GUC variable
       7                 :             :  * timezone_abbreviations.  Therefore, it should report problems using
       8                 :             :  * GUC_check_errmsg() and related functions, and try to avoid throwing
       9                 :             :  * elog(ERROR).  This is not completely bulletproof at present --- in
      10                 :             :  * particular out-of-memory will throw an error.  Could probably fix with
      11                 :             :  * PG_TRY if necessary.
      12                 :             :  *
      13                 :             :  *
      14                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      15                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      16                 :             :  *
      17                 :             :  * IDENTIFICATION
      18                 :             :  *        src/backend/utils/misc/tzparser.c
      19                 :             :  *
      20                 :             :  *-------------------------------------------------------------------------
      21                 :             :  */
      22                 :             : 
      23                 :             : #include "postgres.h"
      24                 :             : 
      25                 :             : #include <ctype.h>
      26                 :             : 
      27                 :             : #include "miscadmin.h"
      28                 :             : #include "storage/fd.h"
      29                 :             : #include "utils/datetime.h"
      30                 :             : #include "utils/guc.h"
      31                 :             : #include "utils/memutils.h"
      32                 :             : #include "utils/tzparser.h"
      33                 :             : 
      34                 :             : 
      35                 :             : #define WHITESPACE " \t\n\r"
      36                 :             : 
      37                 :             : static bool validateTzEntry(tzEntry *tzentry);
      38                 :             : static bool splitTzLine(const char *filename, int lineno,
      39                 :             :                                                 char *line, tzEntry *tzentry);
      40                 :             : static int      addToArray(tzEntry **base, int *arraysize, int n,
      41                 :             :                                            tzEntry *entry, bool override);
      42                 :             : static int      ParseTzFile(const char *filename, int depth,
      43                 :             :                                                 tzEntry **base, int *arraysize, int n);
      44                 :             : 
      45                 :             : 
      46                 :             : /*
      47                 :             :  * Apply additional validation checks to a tzEntry
      48                 :             :  *
      49                 :             :  * Returns true if OK, else false
      50                 :             :  */
      51                 :             : static bool
      52                 :      244537 : validateTzEntry(tzEntry *tzentry)
      53                 :             : {
      54                 :      244537 :         unsigned char *p;
      55                 :             : 
      56                 :             :         /*
      57                 :             :          * Check restrictions imposed by datetktbl storage format (see datetime.c)
      58                 :             :          */
      59         [ -  + ]:      244537 :         if (strlen(tzentry->abbrev) > TOKMAXLEN)
      60                 :             :         {
      61                 :           0 :                 GUC_check_errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d",
      62                 :           0 :                                                  tzentry->abbrev, TOKMAXLEN,
      63                 :           0 :                                                  tzentry->filename, tzentry->lineno);
      64                 :           0 :                 return false;
      65                 :             :         }
      66                 :             : 
      67                 :             :         /*
      68                 :             :          * Sanity-check the offset: shouldn't exceed 14 hours
      69                 :             :          */
      70   [ +  -  -  + ]:      244537 :         if (tzentry->offset > 14 * SECS_PER_HOUR ||
      71                 :      244537 :                 tzentry->offset < -14 * SECS_PER_HOUR)
      72                 :             :         {
      73                 :           0 :                 GUC_check_errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d",
      74                 :           0 :                                                  tzentry->offset,
      75                 :           0 :                                                  tzentry->filename, tzentry->lineno);
      76                 :           0 :                 return false;
      77                 :             :         }
      78                 :             : 
      79                 :             :         /*
      80                 :             :          * Convert abbrev to lowercase (must match datetime.c's conversion)
      81                 :             :          */
      82         [ +  + ]:     1133646 :         for (p = (unsigned char *) tzentry->abbrev; *p; p++)
      83                 :      889109 :                 *p = pg_tolower(*p);
      84                 :             : 
      85                 :      244537 :         return true;
      86                 :      244537 : }
      87                 :             : 
      88                 :             : /*
      89                 :             :  * Attempt to parse the line as a timezone abbrev spec
      90                 :             :  *
      91                 :             :  * Valid formats are:
      92                 :             :  *      name  zone
      93                 :             :  *      name  offset  dst
      94                 :             :  *
      95                 :             :  * Returns true if OK, else false; data is stored in *tzentry
      96                 :             :  */
      97                 :             : static bool
      98                 :      244537 : splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
      99                 :             : {
     100                 :      244537 :         char       *brkl;
     101                 :      244537 :         char       *abbrev;
     102                 :      244537 :         char       *offset;
     103                 :      244537 :         char       *offset_endptr;
     104                 :      244537 :         char       *remain;
     105                 :      244537 :         char       *is_dst;
     106                 :             : 
     107                 :      244537 :         tzentry->lineno = lineno;
     108                 :      244537 :         tzentry->filename = filename;
     109                 :             : 
     110                 :      244537 :         abbrev = strtok_r(line, WHITESPACE, &brkl);
     111         [ +  - ]:      244537 :         if (!abbrev)
     112                 :             :         {
     113                 :           0 :                 GUC_check_errmsg("missing time zone abbreviation in time zone file \"%s\", line %d",
     114                 :           0 :                                                  filename, lineno);
     115                 :           0 :                 return false;
     116                 :             :         }
     117                 :      244537 :         tzentry->abbrev = pstrdup(abbrev);
     118                 :             : 
     119                 :      244537 :         offset = strtok_r(NULL, WHITESPACE, &brkl);
     120         [ +  - ]:      244537 :         if (!offset)
     121                 :             :         {
     122                 :           0 :                 GUC_check_errmsg("missing time zone offset in time zone file \"%s\", line %d",
     123                 :           0 :                                                  filename, lineno);
     124                 :           0 :                 return false;
     125                 :             :         }
     126                 :             : 
     127                 :             :         /* We assume zone names don't begin with a digit or sign */
     128   [ +  +  +  -  :      244537 :         if (isdigit((unsigned char) *offset) || *offset == '+' || *offset == '-')
                   +  + ]
     129                 :             :         {
     130                 :      181837 :                 tzentry->zone = NULL;
     131                 :      181837 :                 tzentry->offset = strtol(offset, &offset_endptr, 10);
     132   [ +  -  -  + ]:      181837 :                 if (offset_endptr == offset || *offset_endptr != '\0')
     133                 :             :                 {
     134                 :           0 :                         GUC_check_errmsg("invalid number for time zone offset in time zone file \"%s\", line %d",
     135                 :           0 :                                                          filename, lineno);
     136                 :           0 :                         return false;
     137                 :             :                 }
     138                 :             : 
     139                 :      181837 :                 is_dst = strtok_r(NULL, WHITESPACE, &brkl);
     140   [ +  -  +  + ]:      181837 :                 if (is_dst && pg_strcasecmp(is_dst, "D") == 0)
     141                 :             :                 {
     142                 :       60192 :                         tzentry->is_dst = true;
     143                 :       60192 :                         remain = strtok_r(NULL, WHITESPACE, &brkl);
     144                 :       60192 :                 }
     145                 :             :                 else
     146                 :             :                 {
     147                 :             :                         /* there was no 'D' dst specifier */
     148                 :      121645 :                         tzentry->is_dst = false;
     149                 :      121645 :                         remain = is_dst;
     150                 :             :                 }
     151                 :      181837 :         }
     152                 :             :         else
     153                 :             :         {
     154                 :             :                 /*
     155                 :             :                  * Assume entry is a zone name.  We do not try to validate it by
     156                 :             :                  * looking up the zone, because that would force loading of a lot of
     157                 :             :                  * zones that probably will never be used in the current session.
     158                 :             :                  */
     159                 :       62700 :                 tzentry->zone = pstrdup(offset);
     160                 :       62700 :                 tzentry->offset = 0 * SECS_PER_HOUR;
     161                 :       62700 :                 tzentry->is_dst = false;
     162                 :       62700 :                 remain = strtok_r(NULL, WHITESPACE, &brkl);
     163                 :             :         }
     164                 :             : 
     165         [ +  - ]:      244537 :         if (!remain)                            /* no more non-whitespace chars */
     166                 :           0 :                 return true;
     167                 :             : 
     168         [ -  + ]:      244537 :         if (remain[0] != '#')           /* must be a comment */
     169                 :             :         {
     170                 :           0 :                 GUC_check_errmsg("invalid syntax in time zone file \"%s\", line %d",
     171                 :           0 :                                                  filename, lineno);
     172                 :           0 :                 return false;
     173                 :             :         }
     174                 :      244537 :         return true;
     175                 :      244537 : }
     176                 :             : 
     177                 :             : /*
     178                 :             :  * Insert entry into sorted array
     179                 :             :  *
     180                 :             :  * *base: base address of array (changeable if must enlarge array)
     181                 :             :  * *arraysize: allocated length of array (changeable if must enlarge array)
     182                 :             :  * n: current number of valid elements in array
     183                 :             :  * entry: new data to insert
     184                 :             :  * override: true if OK to override
     185                 :             :  *
     186                 :             :  * Returns the new array length (new value for n), or -1 if error
     187                 :             :  */
     188                 :             : static int
     189                 :      244537 : addToArray(tzEntry **base, int *arraysize, int n,
     190                 :             :                    tzEntry *entry, bool override)
     191                 :             : {
     192                 :      244537 :         tzEntry    *arrayptr;
     193                 :      244537 :         int                     low;
     194                 :      244537 :         int                     high;
     195                 :             : 
     196                 :             :         /*
     197                 :             :          * Search the array for a duplicate; as a useful side effect, the array is
     198                 :             :          * maintained in sorted order.  We use strcmp() to ensure we match the
     199                 :             :          * sort order datetime.c expects.
     200                 :             :          */
     201                 :      244537 :         arrayptr = *base;
     202                 :      244537 :         low = 0;
     203                 :      244537 :         high = n - 1;
     204         [ +  + ]:     1789508 :         while (low <= high)
     205                 :             :         {
     206                 :     1544976 :                 int                     mid = (low + high) >> 1;
     207                 :     1544976 :                 tzEntry    *midptr = arrayptr + mid;
     208                 :     1544976 :                 int                     cmp;
     209                 :             : 
     210                 :     1544976 :                 cmp = strcmp(entry->abbrev, midptr->abbrev);
     211         [ +  + ]:     1544976 :                 if (cmp < 0)
     212                 :      614481 :                         high = mid - 1;
     213         [ +  + ]:      930495 :                 else if (cmp > 0)
     214                 :      930490 :                         low = mid + 1;
     215                 :             :                 else
     216                 :             :                 {
     217                 :             :                         /*
     218                 :             :                          * Found a duplicate entry; complain unless it's the same.
     219                 :             :                          */
     220   [ +  +  +  - ]:           5 :                         if ((midptr->zone == NULL && entry->zone == NULL &&
     221         [ -  + ]:           4 :                                  midptr->offset == entry->offset &&
     222         [ #  # ]:           0 :                                  midptr->is_dst == entry->is_dst) ||
     223   [ +  +  -  + ]:           5 :                                 (midptr->zone != NULL && entry->zone != NULL &&
     224                 :           0 :                                  strcmp(midptr->zone, entry->zone) == 0))
     225                 :             :                         {
     226                 :             :                                 /* return unchanged array */
     227                 :           0 :                                 return n;
     228                 :             :                         }
     229         [ +  - ]:           5 :                         if (override)
     230                 :             :                         {
     231                 :             :                                 /* same abbrev but something is different, override */
     232                 :           5 :                                 midptr->zone = entry->zone;
     233                 :           5 :                                 midptr->offset = entry->offset;
     234                 :           5 :                                 midptr->is_dst = entry->is_dst;
     235                 :           5 :                                 return n;
     236                 :             :                         }
     237                 :             :                         /* same abbrev but something is different, complain */
     238                 :           0 :                         GUC_check_errmsg("time zone abbreviation \"%s\" is multiply defined",
     239                 :           0 :                                                          entry->abbrev);
     240                 :           0 :                         GUC_check_errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.",
     241                 :           0 :                                                                 midptr->filename, midptr->lineno,
     242                 :           0 :                                                                 entry->filename, entry->lineno);
     243                 :           0 :                         return -1;
     244                 :             :                 }
     245         [ +  + ]:     1544976 :         }
     246                 :             : 
     247                 :             :         /*
     248                 :             :          * No match, insert at position "low".
     249                 :             :          */
     250         [ +  + ]:      244532 :         if (n >= *arraysize)
     251                 :             :         {
     252                 :        1254 :                 *arraysize *= 2;
     253                 :        1254 :                 *base = (tzEntry *) repalloc(*base, *arraysize * sizeof(tzEntry));
     254                 :        1254 :         }
     255                 :             : 
     256                 :      244532 :         arrayptr = *base + low;
     257                 :             : 
     258                 :      244532 :         memmove(arrayptr + 1, arrayptr, (n - low) * sizeof(tzEntry));
     259                 :             : 
     260                 :      244532 :         memcpy(arrayptr, entry, sizeof(tzEntry));
     261                 :             : 
     262                 :      244532 :         return n + 1;
     263                 :      244537 : }
     264                 :             : 
     265                 :             : /*
     266                 :             :  * Parse a single timezone abbrev file --- can recurse to handle @INCLUDE
     267                 :             :  *
     268                 :             :  * filename: user-specified file name (does not include path)
     269                 :             :  * depth: current recursion depth
     270                 :             :  * *base: array for results (changeable if must enlarge array)
     271                 :             :  * *arraysize: allocated length of array (changeable if must enlarge array)
     272                 :             :  * n: current number of valid elements in array
     273                 :             :  *
     274                 :             :  * Returns the new array length (new value for n), or -1 if error
     275                 :             :  */
     276                 :             : static int
     277                 :        1256 : ParseTzFile(const char *filename, int depth,
     278                 :             :                         tzEntry **base, int *arraysize, int n)
     279                 :             : {
     280                 :        1256 :         char            share_path[MAXPGPATH];
     281                 :        1256 :         char            file_path[MAXPGPATH];
     282                 :        1256 :         FILE       *tzFile;
     283                 :        1256 :         char            tzbuf[1024];
     284                 :        1256 :         char       *line;
     285                 :        1256 :         tzEntry         tzentry;
     286                 :        1256 :         int                     lineno = 0;
     287                 :        1256 :         bool            override = false;
     288                 :        1256 :         const char *p;
     289                 :             : 
     290                 :             :         /*
     291                 :             :          * We enforce that the filename is all alpha characters.  This may be
     292                 :             :          * overly restrictive, but we don't want to allow access to anything
     293                 :             :          * outside the timezonesets directory, so for instance '/' *must* be
     294                 :             :          * rejected.
     295                 :             :          */
     296         [ +  + ]:       10048 :         for (p = filename; *p; p++)
     297                 :             :         {
     298         [ -  + ]:        8792 :                 if (!isalpha((unsigned char) *p))
     299                 :             :                 {
     300                 :             :                         /* at level 0, just use guc.c's regular "invalid value" message */
     301         [ #  # ]:           0 :                         if (depth > 0)
     302                 :           0 :                                 GUC_check_errmsg("invalid time zone file name \"%s\"",
     303                 :           0 :                                                                  filename);
     304                 :           0 :                         return -1;
     305                 :             :                 }
     306                 :        8792 :         }
     307                 :             : 
     308                 :             :         /*
     309                 :             :          * The maximal recursion depth is a pretty arbitrary setting. It is hard
     310                 :             :          * to imagine that someone needs more than 3 levels so stick with this
     311                 :             :          * conservative setting until someone complains.
     312                 :             :          */
     313         [ -  + ]:        1256 :         if (depth > 3)
     314                 :             :         {
     315                 :           0 :                 GUC_check_errmsg("time zone file recursion limit exceeded in file \"%s\"",
     316                 :           0 :                                                  filename);
     317                 :           0 :                 return -1;
     318                 :             :         }
     319                 :             : 
     320                 :        1256 :         get_share_path(my_exec_path, share_path);
     321                 :        2512 :         snprintf(file_path, sizeof(file_path), "%s/timezonesets/%s",
     322                 :        1256 :                          share_path, filename);
     323                 :        1256 :         tzFile = AllocateFile(file_path, "r");
     324         [ +  - ]:        1256 :         if (!tzFile)
     325                 :             :         {
     326                 :             :                 /*
     327                 :             :                  * Check to see if the problem is not the filename but the directory.
     328                 :             :                  * This is worth troubling over because if the installation share/
     329                 :             :                  * directory is missing or unreadable, this is likely to be the first
     330                 :             :                  * place we notice a problem during postmaster startup.
     331                 :             :                  */
     332                 :           0 :                 int                     save_errno = errno;
     333                 :           0 :                 DIR                *tzdir;
     334                 :             : 
     335                 :           0 :                 snprintf(file_path, sizeof(file_path), "%s/timezonesets",
     336                 :           0 :                                  share_path);
     337                 :           0 :                 tzdir = AllocateDir(file_path);
     338         [ #  # ]:           0 :                 if (tzdir == NULL)
     339                 :             :                 {
     340                 :           0 :                         GUC_check_errmsg("could not open directory \"%s\": %m",
     341                 :           0 :                                                          file_path);
     342                 :           0 :                         GUC_check_errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.",
     343                 :             :                                                           my_exec_path);
     344                 :           0 :                         return -1;
     345                 :             :                 }
     346                 :           0 :                 FreeDir(tzdir);
     347                 :           0 :                 errno = save_errno;
     348                 :             : 
     349                 :             :                 /*
     350                 :             :                  * otherwise, if file doesn't exist and it's level 0, guc.c's
     351                 :             :                  * complaint is enough
     352                 :             :                  */
     353   [ #  #  #  # ]:           0 :                 if (errno != ENOENT || depth > 0)
     354                 :           0 :                         GUC_check_errmsg("could not read time zone file \"%s\": %m",
     355                 :           0 :                                                          filename);
     356                 :             : 
     357                 :           0 :                 return -1;
     358                 :           0 :         }
     359                 :             : 
     360         [ -  + ]:      790068 :         while (!feof(tzFile))
     361                 :             :         {
     362                 :      790068 :                 lineno++;
     363         [ +  + ]:      790068 :                 if (fgets(tzbuf, sizeof(tzbuf), tzFile) == NULL)
     364                 :             :                 {
     365         [ -  + ]:        1256 :                         if (ferror(tzFile))
     366                 :             :                         {
     367                 :           0 :                                 GUC_check_errmsg("could not read time zone file \"%s\": %m",
     368                 :           0 :                                                                  filename);
     369                 :           0 :                                 n = -1;
     370                 :           0 :                                 break;
     371                 :             :                         }
     372                 :             :                         /* else we're at EOF after all */
     373                 :        1256 :                         break;
     374                 :             :                 }
     375         [ -  + ]:      788812 :                 if (strlen(tzbuf) == sizeof(tzbuf) - 1)
     376                 :             :                 {
     377                 :             :                         /* the line is too long for tzbuf */
     378                 :           0 :                         GUC_check_errmsg("line is too long in time zone file \"%s\", line %d",
     379                 :           0 :                                                          filename, lineno);
     380                 :           0 :                         n = -1;
     381                 :           0 :                         break;
     382                 :             :                 }
     383                 :             : 
     384                 :             :                 /* skip over whitespace */
     385                 :      788812 :                 line = tzbuf;
     386   [ +  +  +  + ]:     8363000 :                 while (*line && isspace((unsigned char) *line))
     387                 :     7574188 :                         line++;
     388                 :             : 
     389         [ +  + ]:      788812 :                 if (*line == '\0')              /* empty line */
     390                 :       27599 :                         continue;
     391         [ +  + ]:      761213 :                 if (*line == '#')               /* comment line */
     392                 :      516672 :                         continue;
     393                 :             : 
     394         [ +  + ]:      244541 :                 if (pg_strncasecmp(line, "@INCLUDE", strlen("@INCLUDE")) == 0)
     395                 :             :                 {
     396                 :             :                         /* pstrdup so we can use filename in result data structure */
     397                 :           2 :                         char       *includeFile = pstrdup(line + strlen("@INCLUDE"));
     398                 :           2 :                         char       *brki;
     399                 :             : 
     400                 :           2 :                         includeFile = strtok_r(includeFile, WHITESPACE, &brki);
     401   [ +  -  -  + ]:           2 :                         if (!includeFile || !*includeFile)
     402                 :             :                         {
     403                 :           0 :                                 GUC_check_errmsg("@INCLUDE without file name in time zone file \"%s\", line %d",
     404                 :           0 :                                                                  filename, lineno);
     405                 :           0 :                                 n = -1;
     406                 :           0 :                                 break;
     407                 :             :                         }
     408                 :           4 :                         n = ParseTzFile(includeFile, depth + 1,
     409                 :           2 :                                                         base, arraysize, n);
     410         [ +  - ]:           2 :                         if (n < 0)
     411                 :           0 :                                 break;
     412                 :           2 :                         continue;
     413         [ -  + ]:           2 :                 }
     414                 :             : 
     415         [ +  + ]:      244539 :                 if (pg_strncasecmp(line, "@OVERRIDE", strlen("@OVERRIDE")) == 0)
     416                 :             :                 {
     417                 :           2 :                         override = true;
     418                 :           2 :                         continue;
     419                 :             :                 }
     420                 :             : 
     421         [ +  - ]:      244537 :                 if (!splitTzLine(filename, lineno, line, &tzentry))
     422                 :             :                 {
     423                 :           0 :                         n = -1;
     424                 :           0 :                         break;
     425                 :             :                 }
     426         [ -  + ]:      244537 :                 if (!validateTzEntry(&tzentry))
     427                 :             :                 {
     428                 :           0 :                         n = -1;
     429                 :           0 :                         break;
     430                 :             :                 }
     431                 :      244537 :                 n = addToArray(base, arraysize, n, &tzentry, override);
     432         [ -  + ]:      244537 :                 if (n < 0)
     433                 :           0 :                         break;
     434                 :             :         }
     435                 :             : 
     436                 :        1256 :         FreeFile(tzFile);
     437                 :             : 
     438                 :        1256 :         return n;
     439                 :        1256 : }
     440                 :             : 
     441                 :             : /*
     442                 :             :  * load_tzoffsets --- read and parse the specified timezone offset file
     443                 :             :  *
     444                 :             :  * On success, return a filled-in TimeZoneAbbrevTable, which must have been
     445                 :             :  * guc_malloc'd not palloc'd.  On failure, return NULL, using GUC_check_errmsg
     446                 :             :  * and friends to give details of the problem.
     447                 :             :  */
     448                 :             : TimeZoneAbbrevTable *
     449                 :        1254 : load_tzoffsets(const char *filename)
     450                 :             : {
     451                 :        1254 :         TimeZoneAbbrevTable *result = NULL;
     452                 :        1254 :         MemoryContext tmpContext;
     453                 :        1254 :         MemoryContext oldContext;
     454                 :        1254 :         tzEntry    *array;
     455                 :        1254 :         int                     arraysize;
     456                 :        1254 :         int                     n;
     457                 :             : 
     458                 :             :         /*
     459                 :             :          * Create a temp memory context to work in.  This makes it easy to clean
     460                 :             :          * up afterwards.
     461                 :             :          */
     462                 :        1254 :         tmpContext = AllocSetContextCreate(CurrentMemoryContext,
     463                 :             :                                                                            "TZParserMemory",
     464                 :             :                                                                            ALLOCSET_SMALL_SIZES);
     465                 :        1254 :         oldContext = MemoryContextSwitchTo(tmpContext);
     466                 :             : 
     467                 :             :         /* Initialize array at a reasonable size */
     468                 :        1254 :         arraysize = 128;
     469                 :        1254 :         array = palloc_array(tzEntry, arraysize);
     470                 :             : 
     471                 :             :         /* Parse the file(s) */
     472                 :        1254 :         n = ParseTzFile(filename, 0, &array, &arraysize, 0);
     473                 :             : 
     474                 :             :         /* If no errors so far, let datetime.c allocate memory & convert format */
     475         [ -  + ]:        1254 :         if (n >= 0)
     476                 :             :         {
     477                 :        1254 :                 result = ConvertTimeZoneAbbrevs(array, n);
     478         [ +  - ]:        1254 :                 if (!result)
     479                 :           0 :                         GUC_check_errmsg("out of memory");
     480                 :        1254 :         }
     481                 :             : 
     482                 :             :         /* Clean up */
     483                 :        1254 :         MemoryContextSwitchTo(oldContext);
     484                 :        1254 :         MemoryContextDelete(tmpContext);
     485                 :             : 
     486                 :        2508 :         return result;
     487                 :        1254 : }
        

Generated by: LCOV version 2.3.2-1